summaryrefslogtreecommitdiffstats
path: root/vcl/opengl/salbmp.cxx
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-27 16:51:28 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-27 16:51:28 +0000
commit940b4d1848e8c70ab7642901a68594e8016caffc (patch)
treeeb72f344ee6c3d9b80a7ecc079ea79e9fba8676d /vcl/opengl/salbmp.cxx
parentInitial commit. (diff)
downloadlibreoffice-940b4d1848e8c70ab7642901a68594e8016caffc.tar.xz
libreoffice-940b4d1848e8c70ab7642901a68594e8016caffc.zip
Adding upstream version 1:7.0.4.upstream/1%7.0.4upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'vcl/opengl/salbmp.cxx')
-rw-r--r--vcl/opengl/salbmp.cxx783
1 files changed, 783 insertions, 0 deletions
diff --git a/vcl/opengl/salbmp.cxx b/vcl/opengl/salbmp.cxx
new file mode 100644
index 000000000..e9b1bca73
--- /dev/null
+++ b/vcl/opengl/salbmp.cxx
@@ -0,0 +1,783 @@
+/* -*- 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 <sal/log.hxx>
+#include <osl/diagnose.h>
+#include <tools/debug.hxx>
+
+#include <vcl/opengl/OpenGLHelper.hxx>
+
+#include <vcl/bitmap.hxx>
+#include <vcl/checksum.hxx>
+#include <vcl/outdev.hxx>
+#include <svdata.hxx>
+#include <salgdi.hxx>
+#include <vcleventlisteners.hxx>
+#include <vcl/lazydelete.hxx>
+#include <scanlinewriter.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
+{
+
+bool determineTextureFormat(sal_uInt16 nBits, GLenum& nFormat, GLenum& nType)
+{
+ switch(nBits)
+ {
+ case 8:
+ nFormat = GL_LUMINANCE;
+ nType = GL_UNSIGNED_BYTE;
+ 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;
+}
+
+bool isValidBitCount( sal_uInt16 nBitCount )
+{
+ return (nBitCount == 1) || (nBitCount == 4) || (nBitCount == 8) || (nBitCount == 24) || (nBitCount == 32);
+}
+
+sal_uInt32 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 24: return nWidth * 3;
+ case 32: return nWidth * 4;
+ default:
+ OSL_FAIL("vcl::OpenGLSalBitmap::AllocateUserData(), illegal bitcount!");
+ }
+ return 0;
+}
+}
+
+OpenGLSalBitmap::OpenGLSalBitmap()
+: mbDirtyTexture(true)
+, mnBits(0)
+, mnBytesPerRow(0)
+, mnWidth(0)
+, mnHeight(0)
+{
+}
+
+OpenGLSalBitmap::~OpenGLSalBitmap()
+{
+ Destroy();
+ VCL_GL_INFO( "~OpenGLSalBitmap" );
+}
+
+void OpenGLSalBitmap::Create( const OpenGLTexture& rTex, long nX, long nY, long nWidth, long nHeight )
+{
+ DBG_TESTSOLARMUTEX();
+ static const BitmapPalette aEmptyPalette;
+ OpenGLVCLContextZone aContextZone;
+
+ Destroy();
+ VCL_GL_INFO( "OpenGLSalBitmap::Create from FBO: ["
+ << nX << ", " << nY << "] " << nWidth << "x" << nHeight );
+
+ GLint nMaxTextureSize;
+ glGetIntegerv( GL_MAX_TEXTURE_SIZE, &nMaxTextureSize );
+ if ( nWidth > nMaxTextureSize )
+ {
+ nWidth = nMaxTextureSize;
+ VCL_GL_INFO( "Width limited to " << nMaxTextureSize );
+ }
+
+ if ( nHeight > nMaxTextureSize )
+ {
+ nHeight = nMaxTextureSize;
+ VCL_GL_INFO( "Height limited to " << nMaxTextureSize );
+ }
+
+ 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());
+}
+
+bool OpenGLSalBitmap::Create( const Size& rSize, sal_uInt16 nBits, const BitmapPalette& rBitmapPalette )
+{
+ DBG_TESTSOLARMUTEX();
+ 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();
+
+ // Limit size to what GL allows, so later glTexImage2D() won't fail.
+ GLint nMaxTextureSize;
+ glGetIntegerv(GL_MAX_TEXTURE_SIZE, &nMaxTextureSize);
+ if (mnWidth > nMaxTextureSize)
+ mnWidth = nMaxTextureSize;
+ if (mnHeight > nMaxTextureSize)
+ mnHeight = nMaxTextureSize;
+
+ return false;
+}
+
+bool OpenGLSalBitmap::Create( const SalBitmap& rSalBmp )
+{
+ DBG_TESTSOLARMUTEX();
+ return Create( rSalBmp, rSalBmp.GetBitCount() );
+}
+
+bool OpenGLSalBitmap::Create( const SalBitmap& rSalBmp, SalGraphics* pGraphics )
+{
+ DBG_TESTSOLARMUTEX();
+ return Create( rSalBmp, pGraphics ? pGraphics->GetBitCount() : rSalBmp.GetBitCount() );
+}
+
+bool OpenGLSalBitmap::Create( const SalBitmap& rSalBmp, sal_uInt16 nNewBitCount )
+{
+ DBG_TESTSOLARMUTEX();
+ 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*/ )
+{
+ DBG_TESTSOLARMUTEX();
+ // 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();
+ DeallocateUserData();
+}
+
+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 = 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);
+ DeallocateUserData();
+ }
+#ifdef DBG_UTIL
+ else
+ {
+ for (size_t i = 0; i < size_t(mnBytesPerRow * mnHeight); i++)
+ mpUserBuffer.get()[i] = (i & 0xFF);
+ }
+#endif
+
+ return mpUserBuffer != nullptr;
+}
+
+void OpenGLSalBitmap::DeallocateUserData()
+{
+ mpUserBuffer.reset();
+ mnBytesPerRow = 0;
+}
+
+namespace {
+
+void lclInstantiateTexture(OpenGLTexture& rTexture, const int nWidth, const int nHeight,
+ const GLenum nFormat, const GLenum nType, sal_uInt8 const * pData)
+{
+ if (nWidth == nHeight)
+ {
+ typedef std::vector<std::unique_ptr<FixedTextureAtlasManager>> TextureAtlasVector;
+ static vcl::DeleteOnDeinit<TextureAtlasVector> aTextureAtlases([]() {
+ TextureAtlasVector* p = new TextureAtlasVector;
+ p->reserve(5);
+ p->push_back(std::make_unique<FixedTextureAtlasManager>(8, 8, 16));
+ p->push_back(std::make_unique<FixedTextureAtlasManager>(8, 8, 24));
+ p->push_back(std::make_unique<FixedTextureAtlasManager>(8, 8, 32));
+ p->push_back(std::make_unique<FixedTextureAtlasManager>(8, 8, 48));
+ p->push_back(std::make_unique<FixedTextureAtlasManager>(8, 8, 64));
+ return p;
+ }());
+ for (std::unique_ptr<FixedTextureAtlasManager>& pTextureAtlas : *aTextureAtlases.get())
+ {
+ if (nWidth == pTextureAtlas->GetSubtextureSize())
+ {
+ rTexture = pTextureAtlas->InsertBuffer(nWidth, nHeight, nFormat, nType, pData);
+ return;
+ }
+ }
+ }
+ rTexture = OpenGLTexture (nWidth, nHeight, nFormat, nType, pData);
+}
+
+} // 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 != nullptr)
+ {
+ if( mnBits == 24 || mnBits == 32 )
+ {
+ // no conversion needed for truecolor
+ pData = mpUserBuffer.get();
+
+ determineTextureFormat(mnBits, nFormat, nType);
+ }
+ else if( mnBits == 8 && maPalette.IsGreyPalette8Bit() )
+ {
+ // no conversion needed for 8bit 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
+ determineTextureFormat(24, nFormat, nType);
+ pData = convertDataBitCount( mpUserBuffer.get(), mnWidth, mnHeight,
+ mnBits, mnBytesPerRow, maPalette,
+ nFormat == GL_BGR ? BitConvert::BGR : BitConvert::RGB ).release();
+ bAllocated = true;
+ }
+ }
+
+ 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 && maPalette.IsGreyPalette8Bit()) || 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 && !defined NDEBUG
+ // If we read over the end of pData we have a real hidden memory
+ // corruption problem !
+ size_t nCanary = mnBytesPerRow * mnHeight;
+ assert(!memcmp(pData + nCanary, CANARY, sizeof (CANARY)));
+#endif
+ return true;
+ }
+ else if (mnBits == 1 || mnBits == 4 || mnBits == 8)
+ { // convert buffers from 24-bit RGB to 1,4 or 8-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_uInt32 nSourceBytesPerRow = lclBytesPerRow(24, mnWidth);
+
+ std::unique_ptr<vcl::ScanlineWriter> pWriter = vcl::ScanlineWriter::Create(mnBits, maPalette);
+ 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, BitmapChecksum& 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(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(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());
+
+ BitmapChecksum 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)
+ {
+ if( !AllocateUserData() )
+ return nullptr;
+ if( maTexture && !ReadTexture() )
+ {
+ DeallocateUserData();
+ return nullptr;
+ }
+ }
+ }
+
+ // mpUserBuffer must be unique when we are doing the write access
+ if (nMode == BitmapAccessMode::Write && mpUserBuffer && mpUserBuffer.use_count() > 1)
+ {
+ std::shared_ptr<sal_uInt8> aBuffer(mpUserBuffer);
+
+ mpUserBuffer.reset();
+ AllocateUserData();
+ memcpy(mpUserBuffer.get(), aBuffer.get(), 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 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;
+ }
+ default: assert(false);
+ }
+
+ 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 );
+ DeallocateUserData();
+ 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_uInt8 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( 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.IsGreyPalette8Bit())
+ return true;
+
+ 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.
+ DeallocateUserData();
+ mbDirtyTexture = false;
+
+ CHECK_GL_ERROR();
+ return true;
+}
+
+// This is needed to just make the bitmap usable as an alpha channel.
+// Converting to 8bit grey will do.
+bool OpenGLSalBitmap::InterpretAs8Bit()
+{
+ return ConvertToGreyscale();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */