Files
libreoffice/vcl/opengl/salbmp.cxx
Chris Sherlock da64d198ec tools: svstream.hxx needs only errcode.hxx & not errinf.hxx
Change-Id: Ia28e35ae5af4f601e9a586a3deffbcd61702b0ca
Reviewed-on: https://gerrit.libreoffice.org/36896
Tested-by: Jenkins <ci@libreoffice.org>
Reviewed-by: Chris Sherlock <chris.sherlock79@gmail.com>
2017-04-25 09:31:31 +02:00

940 lines
27 KiB
C++

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* This file is part of the LibreOffice project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* This file incorporates work covered by the following license notice:
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed
* with this work for additional information regarding copyright
* ownership. The ASF licenses this file to you under the Apache
* License, Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.apache.org/licenses/LICENSE-2.0 .
*/
#include <memory>
#include <sal/config.h>
#include <vcl/opengl/OpenGLHelper.hxx>
#include <vcl/bitmap.hxx>
#include <vcl/checksum.hxx>
#include <vcl/outdev.hxx>
#include <vcl/salbtype.hxx>
#include "svdata.hxx"
#include "salgdi.hxx"
#include "vcleventlisteners.hxx"
#include <vcl/lazydelete.hxx>
#include <o3tl/make_unique.hxx>
#include <o3tl/make_shared.hxx>
#include "opengl/zone.hxx"
#include "opengl/program.hxx"
#include "opengl/salbmp.hxx"
#include "opengl/RenderState.hxx"
#include "opengl/FixedTextureAtlas.hxx"
#if OSL_DEBUG_LEVEL > 0
# define CANARY "tex-canary"
#endif
namespace
{
inline bool determineTextureFormat(sal_uInt16 nBits, GLenum& nFormat, GLenum& nType)
{
switch(nBits)
{
case 8:
nFormat = GL_LUMINANCE;
nType = GL_UNSIGNED_BYTE;
return true;
case 16:
nFormat = GL_RGB;
nType = GL_UNSIGNED_SHORT_5_6_5;
return true;
case 24:
nFormat = GL_RGB;
nType = GL_UNSIGNED_BYTE;
return true;
case 32:
nFormat = GL_RGBA;
nType = GL_UNSIGNED_BYTE;
return true;
default:
break;
}
SAL_WARN("vcl.opengl", "Could not determine the appropriate texture format for input bits '" << nBits << "'");
return false;
}
inline bool isValidBitCount( sal_uInt16 nBitCount )
{
return (nBitCount == 1) || (nBitCount == 4) || (nBitCount == 8) || (nBitCount == 16) || (nBitCount == 24) || (nBitCount == 32);
}
sal_uInt16 lclBytesPerRow(sal_uInt16 nBits, int nWidth)
{
switch(nBits)
{
case 1: return (nWidth + 7) >> 3;
case 4: return (nWidth + 1) >> 1;
case 8: return nWidth;
case 16: return nWidth * 2;
case 24: return nWidth * 3;
case 32: return nWidth * 4;
default:
OSL_FAIL("vcl::OpenGLSalBitmap::AllocateUserData(), illegal bitcount!");
}
return 0;
}
typedef std::vector<std::unique_ptr< FixedTextureAtlasManager > > TextureAtlasVector;
static vcl::DeleteOnDeinit< TextureAtlasVector > gTextureAtlases(new TextureAtlasVector);
}
OpenGLSalBitmap::OpenGLSalBitmap()
: mbDirtyTexture(true)
, mnBits(0)
, mnBytesPerRow(0)
, mnWidth(0)
, mnHeight(0)
{
}
OpenGLSalBitmap::~OpenGLSalBitmap()
{
Destroy();
VCL_GL_INFO( "~OpenGLSalBitmap" );
}
bool OpenGLSalBitmap::Create( const OpenGLTexture& rTex, long nX, long nY, long nWidth, long nHeight )
{
static const BitmapPalette aEmptyPalette;
OpenGLVCLContextZone aContextZone;
Destroy();
VCL_GL_INFO( "OpenGLSalBitmap::Create from FBO: ["
<< nX << ", " << nY << "] " << nWidth << "x" << nHeight );
mnWidth = nWidth;
mnHeight = nHeight;
// TODO Check the framebuffer configuration
mnBits = 32;
maPalette = aEmptyPalette;
if( rTex )
maTexture = OpenGLTexture( rTex, nX, nY, nWidth, nHeight );
else
maTexture = OpenGLTexture( nX, nY, nWidth, nHeight );
mbDirtyTexture = false;
VCL_GL_INFO( "Created texture " << maTexture.Id() );
assert(mnWidth == maTexture.GetWidth() &&
mnHeight == maTexture.GetHeight());
return true;
}
bool OpenGLSalBitmap::Create( const Size& rSize, sal_uInt16 nBits, const BitmapPalette& rBitmapPalette )
{
OpenGLVCLContextZone aContextZone;
Destroy();
VCL_GL_INFO( "OpenGLSalBitmap::Create with size: " << rSize );
if( !isValidBitCount( nBits ) )
return false;
maPalette = rBitmapPalette;
mnBits = nBits;
mnWidth = rSize.Width();
mnHeight = rSize.Height();
return false;
}
bool OpenGLSalBitmap::Create( const SalBitmap& rSalBmp )
{
return Create( rSalBmp, rSalBmp.GetBitCount() );
}
bool OpenGLSalBitmap::Create( const SalBitmap& rSalBmp, SalGraphics* pGraphics )
{
return Create( rSalBmp, pGraphics ? pGraphics->GetBitCount() : rSalBmp.GetBitCount() );
}
bool OpenGLSalBitmap::Create( const SalBitmap& rSalBmp, sal_uInt16 nNewBitCount )
{
OpenGLZone aZone;
// check that carefully only in the debug mode
assert(dynamic_cast<const OpenGLSalBitmap*>(&rSalBmp));
const OpenGLSalBitmap& rSourceBitmap = static_cast<const OpenGLSalBitmap&>(rSalBmp);
VCL_GL_INFO("OpenGLSalBitmap::Create from BMP: "
<< rSourceBitmap.mnWidth << "x" << rSourceBitmap.mnHeight
<< " Bits old: " << mnBits << " new:" << nNewBitCount );
if( isValidBitCount( nNewBitCount ) )
{
// TODO: lfrb: What about the pending operations?!
mnBits = nNewBitCount;
mnBytesPerRow = rSourceBitmap.mnBytesPerRow;
mnWidth = rSourceBitmap.mnWidth;
mnHeight = rSourceBitmap.mnHeight;
maPalette = rSourceBitmap.maPalette;
// execute any pending operations on the source bitmap
maTexture = rSourceBitmap.GetTexture();
mbDirtyTexture = false;
// be careful here, we are share & reference-count the
// mpUserBuffer, BUT this Create() is called from
// Bitmap::ImplMakeUnique().
// Consequently, there might be cases when this needs to be made
// unique later (when we don't do that right away here), like when
// using the BitmapWriteAccess.
mpUserBuffer = rSourceBitmap.mpUserBuffer;
return true;
}
return false;
}
bool OpenGLSalBitmap::Create( const css::uno::Reference< css::rendering::XBitmapCanvas >& /*xBitmapCanvas*/, Size& /*rSize*/, bool /*bMask*/ )
{
// TODO Is this method needed?
return false;
}
OpenGLTexture& OpenGLSalBitmap::GetTexture() const
{
OpenGLSalBitmap* pThis = const_cast<OpenGLSalBitmap*>(this);
if( !maTexture || mbDirtyTexture )
pThis->CreateTexture();
VCL_GL_INFO( "Got texture " << maTexture.Id() );
return pThis->maTexture;
}
void OpenGLSalBitmap::Destroy()
{
OpenGLZone aZone;
VCL_GL_INFO("Destroy OpenGLSalBitmap texture:" << maTexture.Id());
maTexture = OpenGLTexture();
mpUserBuffer.reset();
}
bool OpenGLSalBitmap::AllocateUserData()
{
VCL_GL_INFO( "OpenGLSalBitmap::AllocateUserData" );
if( mnWidth && mnHeight )
{
mnBytesPerRow = lclBytesPerRow(mnBits, mnWidth);
}
bool alloc = false;
if (mnBytesPerRow != 0 && mnHeight &&
mnBytesPerRow <= std::numeric_limits<sal_uInt32>::max() / mnHeight)
{
try
{
size_t nToAllocate = static_cast<sal_uInt32>(mnBytesPerRow) * mnHeight;
#if OSL_DEBUG_LEVEL > 0
nToAllocate += sizeof(CANARY);
#endif
mpUserBuffer = o3tl::make_shared_array<sal_uInt8>(nToAllocate);
#if OSL_DEBUG_LEVEL > 0
memcpy(mpUserBuffer.get() + nToAllocate - sizeof(CANARY),
CANARY, sizeof(CANARY));
#endif
alloc = true;
}
catch (const std::bad_alloc &) {}
}
if (!alloc)
{
SAL_WARN("vcl.opengl", "bad alloc " << mnBytesPerRow << "x" << mnHeight);
mpUserBuffer.reset();
mnBytesPerRow = 0;
}
#ifdef DBG_UTIL
else
{
for (size_t i = 0; i < size_t(mnBytesPerRow * mnHeight); i++)
mpUserBuffer.get()[i] = (i & 0xFF);
}
#endif
return mpUserBuffer.get() != nullptr;
}
namespace {
class ImplPixelFormat
{
protected:
sal_uInt8* mpData;
public:
static ImplPixelFormat* GetFormat( sal_uInt16 nBits, const BitmapPalette& rPalette );
virtual void StartLine( sal_uInt8* pLine ) { mpData = pLine; }
virtual const BitmapColor& ReadPixel() = 0;
virtual ~ImplPixelFormat() { }
};
class ImplPixelFormat8 : public ImplPixelFormat
{
private:
const BitmapPalette& mrPalette;
public:
explicit ImplPixelFormat8( const BitmapPalette& rPalette )
: mrPalette( rPalette )
{
}
virtual const BitmapColor& ReadPixel() override
{
assert( mrPalette.GetEntryCount() > *mpData );
return mrPalette[ *mpData++ ];
}
};
class ImplPixelFormat4 : public ImplPixelFormat
{
private:
const BitmapPalette& mrPalette;
sal_uInt32 mnX;
sal_uInt32 mnShift;
public:
explicit ImplPixelFormat4( const BitmapPalette& rPalette )
: mrPalette( rPalette )
, mnX(0)
, mnShift(4)
{
}
virtual void StartLine( sal_uInt8* pLine ) override
{
mpData = pLine;
mnX = 0;
mnShift = 4;
}
virtual const BitmapColor& ReadPixel() override
{
sal_uInt32 nIdx = ( mpData[mnX >> 1] >> mnShift) & 0x0f;
assert( mrPalette.GetEntryCount() > nIdx );
const BitmapColor& rColor = mrPalette[nIdx];
mnX++;
mnShift ^= 4;
return rColor;
}
};
class ImplPixelFormat1 : public ImplPixelFormat
{
private:
const BitmapPalette& mrPalette;
sal_uInt32 mnX;
public:
explicit ImplPixelFormat1( const BitmapPalette& rPalette )
: mrPalette(rPalette)
, mnX(0)
{
}
virtual void StartLine( sal_uInt8* pLine ) override
{
mpData = pLine;
mnX = 0;
}
virtual const BitmapColor& ReadPixel() override
{
const BitmapColor& rColor = mrPalette[ (mpData[mnX >> 3 ] >> ( 7 - ( mnX & 7 ) )) & 1];
mnX++;
return rColor;
}
};
ImplPixelFormat* ImplPixelFormat::GetFormat( sal_uInt16 nBits, const BitmapPalette& rPalette )
{
switch( nBits )
{
case 1: return new ImplPixelFormat1( rPalette );
case 4: return new ImplPixelFormat4( rPalette );
case 8: return new ImplPixelFormat8( rPalette );
}
return nullptr;
}
void lclInstantiateTexture(OpenGLTexture& rTexture, const int nWidth, const int nHeight,
const GLenum nFormat, const GLenum nType, sal_uInt8* pData)
{
if (nWidth == nHeight)
{
TextureAtlasVector &sTextureAtlases = *gTextureAtlases.get();
if (sTextureAtlases.empty())
{
sTextureAtlases.push_back(o3tl::make_unique<FixedTextureAtlasManager>(8, 8, 16));
sTextureAtlases.push_back(o3tl::make_unique<FixedTextureAtlasManager>(8, 8, 24));
sTextureAtlases.push_back(o3tl::make_unique<FixedTextureAtlasManager>(8, 8, 32));
sTextureAtlases.push_back(o3tl::make_unique<FixedTextureAtlasManager>(8, 8, 48));
sTextureAtlases.push_back(o3tl::make_unique<FixedTextureAtlasManager>(8, 8, 64));
}
for (std::unique_ptr<FixedTextureAtlasManager> & pTextureAtlas : sTextureAtlases)
{
if (nWidth == pTextureAtlas->GetSubtextureSize())
{
rTexture = pTextureAtlas->InsertBuffer(nWidth, nHeight, nFormat, nType, pData);
return;
}
}
}
rTexture = OpenGLTexture (nWidth, nHeight, nFormat, nType, pData);
}
// Write color information for 1 and 4 bit palette bitmap scanlines.
class ScanlineWriter
{
BitmapPalette& maPalette;
sal_uInt8 mnColorsPerByte; // number of colors that are stored in one byte
sal_uInt8 mnColorBitSize; // number of bits a color takes
sal_uInt8 mnColorBitMask; // bit mask used to isolate the color
sal_uInt8* mpCurrentScanline;
long mnX;
public:
ScanlineWriter(BitmapPalette& aPalette, sal_Int8 nColorsPerByte)
: maPalette(aPalette)
, mnColorsPerByte(nColorsPerByte)
, mnColorBitSize(8 / mnColorsPerByte) // bit size is number of bit in a byte divided by number of colors per byte (8 / 2 = 4 for 4-bit)
, mnColorBitMask((1 << mnColorBitSize) - 1) // calculate the bit mask from the bit size
, mpCurrentScanline(nullptr)
, mnX(0)
{}
void writeRGB(sal_uInt8 nR, sal_uInt8 nG, sal_uInt8 nB)
{
// calculate to which index we will write
long nScanlineIndex = mnX / mnColorsPerByte;
// calculate the number of shifts to get the color information to the right place
long nShift = (8 - mnColorBitSize) - ((mnX % mnColorsPerByte) * mnColorBitSize);
sal_uInt16 nColorIndex = maPalette.GetBestIndex(BitmapColor(nR, nG, nB));
mpCurrentScanline[nScanlineIndex] &= ~(mnColorBitMask << nShift); // clear
mpCurrentScanline[nScanlineIndex] |= (nColorIndex & mnColorBitMask) << nShift; // set
mnX++;
}
void nextLine(sal_uInt8* pScanline)
{
mnX = 0;
mpCurrentScanline = pScanline;
}
};
} // end anonymous namespace
Size OpenGLSalBitmap::GetSize() const
{
return Size(mnWidth, mnHeight);
}
GLuint OpenGLSalBitmap::CreateTexture()
{
VCL_GL_INFO( "::CreateTexture bits: " << mnBits);
GLenum nFormat = GL_RGBA;
GLenum nType = GL_UNSIGNED_BYTE;
sal_uInt8* pData( nullptr );
bool bAllocated( false );
if (mpUserBuffer.get() != nullptr)
{
if( mnBits == 16 || mnBits == 24 || mnBits == 32 )
{
// no conversion needed for truecolor
pData = mpUserBuffer.get();
determineTextureFormat(mnBits, nFormat, nType);
}
else if( mnBits == 8 && maPalette.IsGreyPalette() )
{
// no conversion needed for grayscale
pData = mpUserBuffer.get();
nFormat = GL_LUMINANCE;
nType = GL_UNSIGNED_BYTE;
}
else
{
VCL_GL_INFO( "::CreateTexture - convert from " << mnBits << " to 24 bits" );
// convert to 24 bits RGB using palette
pData = new sal_uInt8[mnHeight * mnWidth * 3];
bAllocated = true;
determineTextureFormat(24, nFormat, nType);
std::unique_ptr<ImplPixelFormat> pSrcFormat(ImplPixelFormat::GetFormat(mnBits, maPalette));
sal_uInt8* pSrcData = mpUserBuffer.get();
sal_uInt8* pDstData = pData;
sal_uInt32 nY = mnHeight;
while( nY-- )
{
pSrcFormat->StartLine( pSrcData );
sal_uInt32 nX = mnWidth;
if (nFormat == GL_BGR)
{
while( nX-- )
{
const BitmapColor& c = pSrcFormat->ReadPixel();
*pDstData++ = c.GetBlue();
*pDstData++ = c.GetGreen();
*pDstData++ = c.GetRed();
}
}
else // RGB
{
while( nX-- )
{
const BitmapColor& c = pSrcFormat->ReadPixel();
*pDstData++ = c.GetRed();
*pDstData++ = c.GetGreen();
*pDstData++ = c.GetBlue();
}
}
pSrcData += mnBytesPerRow;
}
}
}
OpenGLVCLContextZone aContextZone;
lclInstantiateTexture(maTexture, mnWidth, mnHeight, nFormat, nType, pData);
VCL_GL_INFO("Created texture " << maTexture.Id() << " bits: " << mnBits);
if( bAllocated )
delete[] pData;
mbDirtyTexture = false;
CHECK_GL_ERROR();
return maTexture.Id();
}
bool OpenGLSalBitmap::ReadTexture()
{
sal_uInt8* pData = mpUserBuffer.get();
GLenum nFormat = GL_RGBA;
GLenum nType = GL_UNSIGNED_BYTE;
VCL_GL_INFO( "::ReadTexture " << mnWidth << "x" << mnHeight << " bits: " << mnBits);
if( pData == nullptr )
return false;
OpenGLVCLContextZone aContextZone;
rtl::Reference<OpenGLContext> xContext = OpenGLContext::getVCLContext();
xContext->state().scissor().disable();
xContext->state().stencil().disable();
if (mnBits == 8 || mnBits == 16 || mnBits == 24 || mnBits == 32)
{
determineTextureFormat(mnBits, nFormat, nType);
#if OSL_DEBUG_LEVEL > 0
// help valgrind & drmemory rescue us - touch last and first bits.
pData[0] = 0;
pData[mnBits/8*mnWidth*mnHeight-1] = 0;
// if this fails we can read too much into pData
assert(mnWidth == maTexture.GetWidth() &&
mnHeight == maTexture.GetHeight());
#endif
maTexture.Read(nFormat, nType, pData);
#if OSL_DEBUG_LEVEL > 0
// If we read over the end of pData we have a real hidden memory
// corruption problem !
size_t nCanary = static_cast<sal_uInt32>(mnBytesPerRow) * mnHeight;
assert(!memcmp(pData + nCanary, CANARY, sizeof (CANARY)));
#endif
return true;
}
else if (mnBits == 1 || mnBits == 4)
{ // convert buffers from 24-bit RGB to 1 or 4-bit buffer
std::vector<sal_uInt8> aBuffer(mnWidth * mnHeight * 3);
sal_uInt8* pBuffer = aBuffer.data();
determineTextureFormat(24, nFormat, nType);
maTexture.Read(nFormat, nType, pBuffer);
sal_uInt16 nSourceBytesPerRow = lclBytesPerRow(24, mnWidth);
std::unique_ptr<ScanlineWriter> pWriter;
switch(mnBits)
{
case 1:
pWriter.reset(new ScanlineWriter(maPalette, 8));
break;
case 4:
default:
pWriter.reset(new ScanlineWriter(maPalette, 2));
break;
}
for (int y = 0; y < mnHeight; ++y)
{
sal_uInt8* pSource = &pBuffer[y * nSourceBytesPerRow];
sal_uInt8* pDestination = &pData[y * mnBytesPerRow];
pWriter->nextLine(pDestination);
for (int x = 0; x < mnWidth; ++x)
{
// read source
sal_uInt8 nR = *pSource++;
sal_uInt8 nG = *pSource++;
sal_uInt8 nB = *pSource++;
pWriter->writeRGB(nR, nG, nB);
}
}
return true;
}
SAL_WARN("vcl.opengl", "::ReadTexture - tx:" << maTexture.Id() << " @ "
<< mnWidth << "x" << mnHeight << "- unimplemented bit depth: "
<< mnBits);
return false;
}
sal_uInt16 OpenGLSalBitmap::GetBitCount() const
{
return mnBits;
}
bool OpenGLSalBitmap::calcChecksumGL(OpenGLTexture& rInputTexture, ChecksumType& rChecksum) const
{
OUString FragShader("areaHashCRC64TFragmentShader");
rtl::Reference< OpenGLContext > xContext = OpenGLContext::getVCLContext();
xContext->state().scissor().disable();
xContext->state().stencil().disable();
static vcl::DeleteOnDeinit<OpenGLTexture> gCRCTableTexture(
new OpenGLTexture(512, 1, GL_RGBA, GL_UNSIGNED_BYTE,
vcl_get_crc64_table()));
OpenGLTexture &rCRCTableTexture = *gCRCTableTexture.get();
// First Pass
int nWidth = rInputTexture.GetWidth();
int nHeight = rInputTexture.GetHeight();
OpenGLProgram* pProgram = xContext->UseProgram("textureVertexShader", FragShader);
if (pProgram == nullptr)
return false;
int nNewWidth = ceil( nWidth / 4.0 );
int nNewHeight = ceil( nHeight / 4.0 );
OpenGLTexture aFirstPassTexture = OpenGLTexture(nNewWidth, nNewHeight);
OpenGLFramebuffer* pFramebuffer = xContext->AcquireFramebuffer(aFirstPassTexture);
pProgram->SetUniform1f( "xstep", 1.0 / mnWidth );
pProgram->SetUniform1f( "ystep", 1.0 / mnHeight );
pProgram->SetTexture("crc_table", rCRCTableTexture);
pProgram->SetTexture("sampler", rInputTexture);
pProgram->DrawTexture(rInputTexture);
pProgram->Clean();
OpenGLContext::ReleaseFramebuffer(pFramebuffer);
CHECK_GL_ERROR();
// Second Pass
nWidth = aFirstPassTexture.GetWidth();
nHeight = aFirstPassTexture.GetHeight();
pProgram = xContext->UseProgram("textureVertexShader", FragShader);
if (pProgram == nullptr)
return false;
nNewWidth = ceil( nWidth / 4.0 );
nNewHeight = ceil( nHeight / 4.0 );
OpenGLTexture aSecondPassTexture = OpenGLTexture(nNewWidth, nNewHeight);
pFramebuffer = xContext->AcquireFramebuffer(aSecondPassTexture);
pProgram->SetUniform1f( "xstep", 1.0 / mnWidth );
pProgram->SetUniform1f( "ystep", 1.0 / mnHeight );
pProgram->SetTexture("crc_table", rCRCTableTexture);
pProgram->SetTexture("sampler", aFirstPassTexture);
pProgram->DrawTexture(aFirstPassTexture);
pProgram->Clean();
OpenGLContext::ReleaseFramebuffer(pFramebuffer);
CHECK_GL_ERROR();
// Final CRC on CPU
OpenGLTexture& aFinalTexture = aSecondPassTexture;
std::vector<sal_uInt8> aBuf( aFinalTexture.GetWidth() * aFinalTexture.GetHeight() * 4 );
aFinalTexture.Read(GL_RGBA, GL_UNSIGNED_BYTE, aBuf.data());
ChecksumType nCrc = vcl_get_checksum(0, aBuf.data(), aBuf.size());
rChecksum = nCrc;
return true;
}
void OpenGLSalBitmap::updateChecksum() const
{
if (mbChecksumValid)
return;
if( (mnWidth * mnHeight) < (1024*768) || mnWidth < 128 || mnHeight < 128 )
{
SalBitmap::updateChecksum();
return;
}
OpenGLSalBitmap* pThis = const_cast<OpenGLSalBitmap*>(this);
OpenGLVCLContextZone aContextZone;
OpenGLTexture& rInputTexture = GetTexture();
pThis->mbChecksumValid = calcChecksumGL(rInputTexture, pThis->mnChecksum);
if (!pThis->mbChecksumValid)
SalBitmap::updateChecksum();
}
BitmapBuffer* OpenGLSalBitmap::AcquireBuffer( BitmapAccessMode nMode )
{
OpenGLVCLContextZone aContextZone;
if( nMode != BitmapAccessMode::Info )
{
if (!mpUserBuffer.get())
{
if( !AllocateUserData() )
return nullptr;
if( maTexture && !ReadTexture() )
return nullptr;
}
}
// mpUserBuffer must be unique when we are doing the write access
if (nMode == BitmapAccessMode::Write && mpUserBuffer && !mpUserBuffer.unique())
{
std::shared_ptr<sal_uInt8> aBuffer(mpUserBuffer);
mpUserBuffer.reset();
AllocateUserData();
memcpy(mpUserBuffer.get(), aBuffer.get(), static_cast<sal_uInt32>(mnBytesPerRow) * mnHeight);
}
BitmapBuffer* pBuffer = new BitmapBuffer;
pBuffer->mnWidth = mnWidth;
pBuffer->mnHeight = mnHeight;
pBuffer->maPalette = maPalette;
pBuffer->mnScanlineSize = mnBytesPerRow;
pBuffer->mpBits = mpUserBuffer.get();
pBuffer->mnBitCount = mnBits;
switch (mnBits)
{
case 1:
pBuffer->mnFormat = ScanlineFormat::N1BitMsbPal;
break;
case 4:
pBuffer->mnFormat = ScanlineFormat::N4BitMsnPal;
break;
case 8:
pBuffer->mnFormat = ScanlineFormat::N8BitPal;
break;
case 16:
{
pBuffer->mnFormat = ScanlineFormat::N16BitTcMsbMask;
ColorMaskElement aRedMask(0x0000f800);
aRedMask.CalcMaskShift();
ColorMaskElement aGreenMask(0x000007e0);
aGreenMask.CalcMaskShift();
ColorMaskElement aBlueMask(0x0000001f);
aBlueMask.CalcMaskShift();
pBuffer->maColorMask = ColorMask(aRedMask, aGreenMask, aBlueMask);
break;
}
case 24:
{
pBuffer->mnFormat = ScanlineFormat::N24BitTcRgb;
break;
}
case 32:
{
pBuffer->mnFormat = ScanlineFormat::N32BitTcRgba;
ColorMaskElement aRedMask(0xff000000);
aRedMask.CalcMaskShift();
ColorMaskElement aGreenMask(0x00ff0000);
aGreenMask.CalcMaskShift();
ColorMaskElement aBlueMask(0x0000ff00);
aBlueMask.CalcMaskShift();
pBuffer->maColorMask = ColorMask(aRedMask, aGreenMask, aBlueMask);
break;
}
}
return pBuffer;
}
void OpenGLSalBitmap::ReleaseBuffer( BitmapBuffer* pBuffer, BitmapAccessMode nMode )
{
OpenGLVCLContextZone aContextZone;
if( nMode == BitmapAccessMode::Write )
{
maTexture = OpenGLTexture();
mbDirtyTexture = true;
mbChecksumValid = false;
}
// The palette is modified on read during the BitmapWriteAccess,
// but of course, often it is not modified; interesting.
maPalette = pBuffer->maPalette;
// Are there any more ground movements underneath us ?
assert( pBuffer->mnWidth == mnWidth );
assert( pBuffer->mnHeight == mnHeight );
assert( pBuffer->mnBitCount == mnBits );
delete pBuffer;
}
bool OpenGLSalBitmap::GetSystemData( BitmapSystemData& /*rData*/ )
{
SAL_WARN( "vcl.opengl", "*** NOT IMPLEMENTED *** GetSystemData" );
#if 0
// TODO Implement for ANDROID/OSX/IOS/WIN32
X11SalBitmap rBitmap;
BitmapBuffer* pBuffer;
rBitmap.Create( GetSize(), mnBits, maPalette );
pBuffer = rBitmap.AcquireBuffer( false );
if( pBuffer == NULL )
return false;
if (!mpUserBuffer.get())
{
if( !AllocateUserData() || !ReadTexture() )
{
rBitmap.ReleaseBuffer( pBuffer, false );
return false;
}
}
// TODO Might be more efficient to add a static method to SalBitmap
// to get system data from a buffer
memcpy( pBuffer->mpBits, mpUserBuffer.get(), mnBytesPerRow * mnHeight );
rBitmap.ReleaseBuffer( pBuffer, false );
return rBitmap.GetSystemData( rData );
#else
return false;
#endif
}
bool OpenGLSalBitmap::Replace( const Color& rSearchColor, const Color& rReplaceColor, sal_uLong nTol )
{
VCL_GL_INFO("::Replace");
OpenGLZone aZone;
rtl::Reference<OpenGLContext> xContext = OpenGLContext::getVCLContext();
xContext->state().scissor().disable();
xContext->state().stencil().disable();
OpenGLFramebuffer* pFramebuffer;
OpenGLProgram* pProgram;
GetTexture();
pProgram = xContext->UseProgram( "textureVertexShader",
"replaceColorFragmentShader" );
if( !pProgram )
return false;
OpenGLTexture aNewTex = OpenGLTexture( mnWidth, mnHeight );
pFramebuffer = xContext->AcquireFramebuffer( aNewTex );
pProgram->SetTexture( "sampler", maTexture );
pProgram->SetColor( "search_color", rSearchColor );
pProgram->SetColor( "replace_color", rReplaceColor );
pProgram->SetUniform1f( "epsilon", nTol / 255.0f );
pProgram->DrawTexture( maTexture );
pProgram->Clean();
OpenGLContext::ReleaseFramebuffer( pFramebuffer );
maTexture = aNewTex;
CHECK_GL_ERROR();
return true;
}
// Convert texture to greyscale and adjust bitmap metadata
bool OpenGLSalBitmap::ConvertToGreyscale()
{
VCL_GL_INFO("::ConvertToGreyscale");
// avoid re-converting to 8bits.
if ( mnBits == 8 && maPalette == Bitmap::GetGreyPalette(256) )
return false;
OpenGLZone aZone;
rtl::Reference<OpenGLContext> xContext = OpenGLContext::getVCLContext();
xContext->state().scissor().disable();
xContext->state().stencil().disable();
OpenGLFramebuffer* pFramebuffer;
OpenGLProgram* pProgram;
GetTexture();
pProgram = xContext->UseProgram("textureVertexShader", "greyscaleFragmentShader");
if (!pProgram)
return false;
OpenGLTexture aNewTex(mnWidth, mnHeight);
pFramebuffer = xContext->AcquireFramebuffer(aNewTex);
pProgram->SetTexture("sampler", maTexture);
pProgram->DrawTexture(maTexture);
pProgram->Clean();
OpenGLContext::ReleaseFramebuffer( pFramebuffer );
maTexture = aNewTex;
mnBits = 8;
maPalette = Bitmap::GetGreyPalette(256);
// AllocateUserData will handle the rest.
mpUserBuffer.reset();
mbDirtyTexture = false;
CHECK_GL_ERROR();
return true;
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */