Version 6.1.4.1, tag libreoffice-6.1.4.1
[LibreOffice.git] / vcl / opengl / salbmp.cxx
blob6e497ff71d74673c14fcf97cbe1a5fbae37da17b
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <memory>
21 #include <sal/config.h>
23 #include <vcl/opengl/OpenGLHelper.hxx>
25 #include <vcl/bitmap.hxx>
26 #include <vcl/checksum.hxx>
27 #include <vcl/outdev.hxx>
28 #include <vcl/salbtype.hxx>
29 #include <svdata.hxx>
30 #include <salgdi.hxx>
31 #include <vcleventlisteners.hxx>
32 #include <vcl/lazydelete.hxx>
34 #include <o3tl/make_unique.hxx>
35 #include <o3tl/make_shared.hxx>
37 #include <opengl/zone.hxx>
38 #include <opengl/program.hxx>
39 #include <opengl/salbmp.hxx>
40 #include <opengl/RenderState.hxx>
41 #include <opengl/FixedTextureAtlas.hxx>
43 #if OSL_DEBUG_LEVEL > 0
44 # define CANARY "tex-canary"
45 #endif
47 namespace
50 inline bool determineTextureFormat(sal_uInt16 nBits, GLenum& nFormat, GLenum& nType)
52 switch(nBits)
54 case 8:
55 nFormat = GL_LUMINANCE;
56 nType = GL_UNSIGNED_BYTE;
57 return true;
58 case 16:
59 nFormat = GL_RGB;
60 nType = GL_UNSIGNED_SHORT_5_6_5;
61 return true;
62 case 24:
63 nFormat = GL_RGB;
64 nType = GL_UNSIGNED_BYTE;
65 return true;
66 case 32:
67 nFormat = GL_RGBA;
68 nType = GL_UNSIGNED_BYTE;
69 return true;
70 default:
71 break;
73 SAL_WARN("vcl.opengl", "Could not determine the appropriate texture format for input bits '" << nBits << "'");
74 return false;
77 inline bool isValidBitCount( sal_uInt16 nBitCount )
79 return (nBitCount == 1) || (nBitCount == 4) || (nBitCount == 8) || (nBitCount == 16) || (nBitCount == 24) || (nBitCount == 32);
82 sal_uInt32 lclBytesPerRow(sal_uInt16 nBits, int nWidth)
84 switch(nBits)
86 case 1: return (nWidth + 7) >> 3;
87 case 4: return (nWidth + 1) >> 1;
88 case 8: return nWidth;
89 case 16: return nWidth * 2;
90 case 24: return nWidth * 3;
91 case 32: return nWidth * 4;
92 default:
93 OSL_FAIL("vcl::OpenGLSalBitmap::AllocateUserData(), illegal bitcount!");
95 return 0;
98 typedef std::vector<std::unique_ptr< FixedTextureAtlasManager > > TextureAtlasVector;
99 static vcl::DeleteOnDeinit< TextureAtlasVector > gTextureAtlases(new TextureAtlasVector);
103 OpenGLSalBitmap::OpenGLSalBitmap()
104 : mbDirtyTexture(true)
105 , mnBits(0)
106 , mnBytesPerRow(0)
107 , mnWidth(0)
108 , mnHeight(0)
112 OpenGLSalBitmap::~OpenGLSalBitmap()
114 Destroy();
115 VCL_GL_INFO( "~OpenGLSalBitmap" );
118 bool OpenGLSalBitmap::Create( const OpenGLTexture& rTex, long nX, long nY, long nWidth, long nHeight )
120 DBG_TESTSOLARMUTEX();
121 static const BitmapPalette aEmptyPalette;
122 OpenGLVCLContextZone aContextZone;
124 Destroy();
125 VCL_GL_INFO( "OpenGLSalBitmap::Create from FBO: ["
126 << nX << ", " << nY << "] " << nWidth << "x" << nHeight );
128 GLint nMaxTextureSize;
129 glGetIntegerv( GL_MAX_TEXTURE_SIZE, &nMaxTextureSize );
130 if ( nWidth > nMaxTextureSize )
132 nWidth = nMaxTextureSize;
133 VCL_GL_INFO( "Width limited to " << nMaxTextureSize );
136 if ( nHeight > nMaxTextureSize )
138 nHeight = nMaxTextureSize;
139 VCL_GL_INFO( "Height limited to " << nMaxTextureSize );
142 mnWidth = nWidth;
143 mnHeight = nHeight;
145 // TODO Check the framebuffer configuration
146 mnBits = 32;
147 maPalette = aEmptyPalette;
149 if( rTex )
150 maTexture = OpenGLTexture( rTex, nX, nY, nWidth, nHeight );
151 else
152 maTexture = OpenGLTexture( nX, nY, nWidth, nHeight );
153 mbDirtyTexture = false;
154 VCL_GL_INFO( "Created texture " << maTexture.Id() );
156 assert(mnWidth == maTexture.GetWidth() &&
157 mnHeight == maTexture.GetHeight());
159 return true;
162 bool OpenGLSalBitmap::Create( const Size& rSize, sal_uInt16 nBits, const BitmapPalette& rBitmapPalette )
164 DBG_TESTSOLARMUTEX();
165 OpenGLVCLContextZone aContextZone;
167 Destroy();
168 VCL_GL_INFO( "OpenGLSalBitmap::Create with size: " << rSize );
170 if( !isValidBitCount( nBits ) )
171 return false;
172 maPalette = rBitmapPalette;
173 mnBits = nBits;
174 mnWidth = rSize.Width();
175 mnHeight = rSize.Height();
177 // Limit size to what GL allows, so later glTexImage2D() won't fail.
178 GLint nMaxTextureSize;
179 glGetIntegerv(GL_MAX_TEXTURE_SIZE, &nMaxTextureSize);
180 if (mnWidth > nMaxTextureSize)
181 mnWidth = nMaxTextureSize;
182 if (mnHeight > nMaxTextureSize)
183 mnHeight = nMaxTextureSize;
185 return false;
188 bool OpenGLSalBitmap::Create( const SalBitmap& rSalBmp )
190 DBG_TESTSOLARMUTEX();
191 return Create( rSalBmp, rSalBmp.GetBitCount() );
194 bool OpenGLSalBitmap::Create( const SalBitmap& rSalBmp, SalGraphics* pGraphics )
196 DBG_TESTSOLARMUTEX();
197 return Create( rSalBmp, pGraphics ? pGraphics->GetBitCount() : rSalBmp.GetBitCount() );
200 bool OpenGLSalBitmap::Create( const SalBitmap& rSalBmp, sal_uInt16 nNewBitCount )
202 DBG_TESTSOLARMUTEX();
203 OpenGLZone aZone;
205 // check that carefully only in the debug mode
206 assert(dynamic_cast<const OpenGLSalBitmap*>(&rSalBmp));
208 const OpenGLSalBitmap& rSourceBitmap = static_cast<const OpenGLSalBitmap&>(rSalBmp);
210 VCL_GL_INFO("OpenGLSalBitmap::Create from BMP: "
211 << rSourceBitmap.mnWidth << "x" << rSourceBitmap.mnHeight
212 << " Bits old: " << mnBits << " new:" << nNewBitCount );
214 if( isValidBitCount( nNewBitCount ) )
216 // TODO: lfrb: What about the pending operations?!
217 mnBits = nNewBitCount;
218 mnBytesPerRow = rSourceBitmap.mnBytesPerRow;
219 mnWidth = rSourceBitmap.mnWidth;
220 mnHeight = rSourceBitmap.mnHeight;
221 maPalette = rSourceBitmap.maPalette;
222 // execute any pending operations on the source bitmap
223 maTexture = rSourceBitmap.GetTexture();
224 mbDirtyTexture = false;
226 // be careful here, we are share & reference-count the
227 // mpUserBuffer, BUT this Create() is called from
228 // Bitmap::ImplMakeUnique().
229 // Consequently, there might be cases when this needs to be made
230 // unique later (when we don't do that right away here), like when
231 // using the BitmapWriteAccess.
232 mpUserBuffer = rSourceBitmap.mpUserBuffer;
234 return true;
236 return false;
239 bool OpenGLSalBitmap::Create( const css::uno::Reference< css::rendering::XBitmapCanvas >& /*xBitmapCanvas*/, Size& /*rSize*/, bool /*bMask*/ )
241 DBG_TESTSOLARMUTEX();
242 // TODO Is this method needed?
243 return false;
246 OpenGLTexture& OpenGLSalBitmap::GetTexture() const
248 OpenGLSalBitmap* pThis = const_cast<OpenGLSalBitmap*>(this);
249 if( !maTexture || mbDirtyTexture )
250 pThis->CreateTexture();
251 VCL_GL_INFO( "Got texture " << maTexture.Id() );
252 return pThis->maTexture;
255 void OpenGLSalBitmap::Destroy()
257 OpenGLZone aZone;
259 VCL_GL_INFO("Destroy OpenGLSalBitmap texture:" << maTexture.Id());
260 maTexture = OpenGLTexture();
261 mpUserBuffer.reset();
264 bool OpenGLSalBitmap::AllocateUserData()
266 VCL_GL_INFO( "OpenGLSalBitmap::AllocateUserData" );
268 if( mnWidth && mnHeight )
270 mnBytesPerRow = lclBytesPerRow(mnBits, mnWidth);
273 bool alloc = false;
274 if (mnBytesPerRow != 0 && mnHeight &&
275 mnBytesPerRow <= std::numeric_limits<sal_uInt32>::max() / mnHeight)
279 size_t nToAllocate = mnBytesPerRow * mnHeight;
280 #if OSL_DEBUG_LEVEL > 0
281 nToAllocate += sizeof(CANARY);
282 #endif
283 mpUserBuffer = o3tl::make_shared_array<sal_uInt8>(nToAllocate);
284 #if OSL_DEBUG_LEVEL > 0
285 memcpy(mpUserBuffer.get() + nToAllocate - sizeof(CANARY),
286 CANARY, sizeof(CANARY));
287 #endif
288 alloc = true;
290 catch (const std::bad_alloc &) {}
292 if (!alloc)
294 SAL_WARN("vcl.opengl", "bad alloc " << mnBytesPerRow << "x" << mnHeight);
295 mpUserBuffer.reset();
296 mnBytesPerRow = 0;
298 #ifdef DBG_UTIL
299 else
301 for (size_t i = 0; i < size_t(mnBytesPerRow * mnHeight); i++)
302 mpUserBuffer.get()[i] = (i & 0xFF);
304 #endif
306 return mpUserBuffer.get() != nullptr;
309 namespace {
311 class ImplPixelFormat
313 protected:
314 sal_uInt8* mpData;
315 public:
316 static ImplPixelFormat* GetFormat( sal_uInt16 nBits, const BitmapPalette& rPalette );
318 virtual void StartLine( sal_uInt8* pLine ) { mpData = pLine; }
319 virtual const BitmapColor& ReadPixel() = 0;
320 virtual ~ImplPixelFormat() { }
323 class ImplPixelFormat8 : public ImplPixelFormat
325 private:
326 const BitmapPalette& mrPalette;
328 public:
329 explicit ImplPixelFormat8( const BitmapPalette& rPalette )
330 : mrPalette( rPalette )
333 virtual const BitmapColor& ReadPixel() override
335 assert( mrPalette.GetEntryCount() > *mpData );
336 return mrPalette[ *mpData++ ];
340 class ImplPixelFormat4 : public ImplPixelFormat
342 private:
343 const BitmapPalette& mrPalette;
344 sal_uInt32 mnX;
345 sal_uInt32 mnShift;
347 public:
348 explicit ImplPixelFormat4( const BitmapPalette& rPalette )
349 : mrPalette( rPalette )
350 , mnX(0)
351 , mnShift(4)
354 virtual void StartLine( sal_uInt8* pLine ) override
356 mpData = pLine;
357 mnX = 0;
358 mnShift = 4;
360 virtual const BitmapColor& ReadPixel() override
362 sal_uInt32 nIdx = ( mpData[mnX >> 1] >> mnShift) & 0x0f;
363 assert( mrPalette.GetEntryCount() > nIdx );
364 const BitmapColor& rColor = mrPalette[nIdx];
365 mnX++;
366 mnShift ^= 4;
367 return rColor;
371 class ImplPixelFormat1 : public ImplPixelFormat
373 private:
374 const BitmapPalette& mrPalette;
375 sal_uInt32 mnX;
377 public:
378 explicit ImplPixelFormat1( const BitmapPalette& rPalette )
379 : mrPalette(rPalette)
380 , mnX(0)
383 virtual void StartLine( sal_uInt8* pLine ) override
385 mpData = pLine;
386 mnX = 0;
388 virtual const BitmapColor& ReadPixel() override
390 const BitmapColor& rColor = mrPalette[ (mpData[mnX >> 3 ] >> ( 7 - ( mnX & 7 ) )) & 1];
391 mnX++;
392 return rColor;
396 ImplPixelFormat* ImplPixelFormat::GetFormat( sal_uInt16 nBits, const BitmapPalette& rPalette )
398 switch( nBits )
400 case 1: return new ImplPixelFormat1( rPalette );
401 case 4: return new ImplPixelFormat4( rPalette );
402 case 8: return new ImplPixelFormat8( rPalette );
405 return nullptr;
408 void lclInstantiateTexture(OpenGLTexture& rTexture, const int nWidth, const int nHeight,
409 const GLenum nFormat, const GLenum nType, sal_uInt8 const * pData)
411 if (nWidth == nHeight)
413 TextureAtlasVector &sTextureAtlases = *gTextureAtlases.get();
414 if (sTextureAtlases.empty())
416 sTextureAtlases.push_back(o3tl::make_unique<FixedTextureAtlasManager>(8, 8, 16));
417 sTextureAtlases.push_back(o3tl::make_unique<FixedTextureAtlasManager>(8, 8, 24));
418 sTextureAtlases.push_back(o3tl::make_unique<FixedTextureAtlasManager>(8, 8, 32));
419 sTextureAtlases.push_back(o3tl::make_unique<FixedTextureAtlasManager>(8, 8, 48));
420 sTextureAtlases.push_back(o3tl::make_unique<FixedTextureAtlasManager>(8, 8, 64));
422 for (std::unique_ptr<FixedTextureAtlasManager> & pTextureAtlas : sTextureAtlases)
424 if (nWidth == pTextureAtlas->GetSubtextureSize())
426 rTexture = pTextureAtlas->InsertBuffer(nWidth, nHeight, nFormat, nType, pData);
427 return;
431 rTexture = OpenGLTexture (nWidth, nHeight, nFormat, nType, pData);
434 // Write color information for 1 and 4 bit palette bitmap scanlines.
435 class ScanlineWriter
437 BitmapPalette& maPalette;
438 sal_uInt8 mnColorsPerByte; // number of colors that are stored in one byte
439 sal_uInt8 mnColorBitSize; // number of bits a color takes
440 sal_uInt8 mnColorBitMask; // bit mask used to isolate the color
441 sal_uInt8* mpCurrentScanline;
442 long mnX;
444 public:
445 ScanlineWriter(BitmapPalette& aPalette, sal_Int8 nColorsPerByte)
446 : maPalette(aPalette)
447 , mnColorsPerByte(nColorsPerByte)
448 , 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)
449 , mnColorBitMask((1 << mnColorBitSize) - 1) // calculate the bit mask from the bit size
450 , mpCurrentScanline(nullptr)
451 , mnX(0)
454 void writeRGB(sal_uInt8 nR, sal_uInt8 nG, sal_uInt8 nB)
456 // calculate to which index we will write
457 long nScanlineIndex = mnX / mnColorsPerByte;
459 // calculate the number of shifts to get the color information to the right place
460 long nShift = (8 - mnColorBitSize) - ((mnX % mnColorsPerByte) * mnColorBitSize);
462 sal_uInt16 nColorIndex = maPalette.GetBestIndex(BitmapColor(nR, nG, nB));
463 mpCurrentScanline[nScanlineIndex] &= ~(mnColorBitMask << nShift); // clear
464 mpCurrentScanline[nScanlineIndex] |= (nColorIndex & mnColorBitMask) << nShift; // set
465 mnX++;
468 void nextLine(sal_uInt8* pScanline)
470 mnX = 0;
471 mpCurrentScanline = pScanline;
475 } // end anonymous namespace
477 Size OpenGLSalBitmap::GetSize() const
479 return Size(mnWidth, mnHeight);
482 GLuint OpenGLSalBitmap::CreateTexture()
484 VCL_GL_INFO( "::CreateTexture bits: " << mnBits);
485 GLenum nFormat = GL_RGBA;
486 GLenum nType = GL_UNSIGNED_BYTE;
487 sal_uInt8* pData( nullptr );
488 bool bAllocated( false );
490 if (mpUserBuffer.get() != nullptr)
492 if( mnBits == 16 || mnBits == 24 || mnBits == 32 )
494 // no conversion needed for truecolor
495 pData = mpUserBuffer.get();
497 determineTextureFormat(mnBits, nFormat, nType);
499 else if( mnBits == 8 && maPalette.IsGreyPalette() )
501 // no conversion needed for grayscale
502 pData = mpUserBuffer.get();
503 nFormat = GL_LUMINANCE;
504 nType = GL_UNSIGNED_BYTE;
506 else
508 VCL_GL_INFO( "::CreateTexture - convert from " << mnBits << " to 24 bits" );
510 // convert to 24 bits RGB using palette
511 pData = new sal_uInt8[mnHeight * mnWidth * 3];
512 bAllocated = true;
513 determineTextureFormat(24, nFormat, nType);
515 std::unique_ptr<ImplPixelFormat> pSrcFormat(ImplPixelFormat::GetFormat(mnBits, maPalette));
517 sal_uInt8* pSrcData = mpUserBuffer.get();
518 sal_uInt8* pDstData = pData;
520 sal_uInt32 nY = mnHeight;
521 while( nY-- )
523 pSrcFormat->StartLine( pSrcData );
525 sal_uInt32 nX = mnWidth;
526 if (nFormat == GL_BGR)
528 while( nX-- )
530 const BitmapColor& c = pSrcFormat->ReadPixel();
531 *pDstData++ = c.GetBlue();
532 *pDstData++ = c.GetGreen();
533 *pDstData++ = c.GetRed();
536 else // RGB
538 while( nX-- )
540 const BitmapColor& c = pSrcFormat->ReadPixel();
541 *pDstData++ = c.GetRed();
542 *pDstData++ = c.GetGreen();
543 *pDstData++ = c.GetBlue();
547 pSrcData += mnBytesPerRow;
552 OpenGLVCLContextZone aContextZone;
554 lclInstantiateTexture(maTexture, mnWidth, mnHeight, nFormat, nType, pData);
556 VCL_GL_INFO("Created texture " << maTexture.Id() << " bits: " << mnBits);
558 if( bAllocated )
559 delete[] pData;
561 mbDirtyTexture = false;
563 CHECK_GL_ERROR();
564 return maTexture.Id();
567 bool OpenGLSalBitmap::ReadTexture()
569 sal_uInt8* pData = mpUserBuffer.get();
571 GLenum nFormat = GL_RGBA;
572 GLenum nType = GL_UNSIGNED_BYTE;
574 VCL_GL_INFO( "::ReadTexture " << mnWidth << "x" << mnHeight << " bits: " << mnBits);
576 if( pData == nullptr )
577 return false;
579 OpenGLVCLContextZone aContextZone;
581 rtl::Reference<OpenGLContext> xContext = OpenGLContext::getVCLContext();
582 xContext->state().scissor().disable();
583 xContext->state().stencil().disable();
585 if ((mnBits == 8 && maPalette.IsGreyPalette()) || mnBits == 16 || mnBits == 24 || mnBits == 32)
587 determineTextureFormat(mnBits, nFormat, nType);
589 #if OSL_DEBUG_LEVEL > 0
590 // help valgrind & drmemory rescue us - touch last and first bits.
591 pData[0] = 0;
592 pData[mnBits/8*mnWidth*mnHeight-1] = 0;
593 // if this fails we can read too much into pData
594 assert(mnWidth == maTexture.GetWidth() &&
595 mnHeight == maTexture.GetHeight());
596 #endif
598 maTexture.Read(nFormat, nType, pData);
600 #if OSL_DEBUG_LEVEL > 0
601 // If we read over the end of pData we have a real hidden memory
602 // corruption problem !
603 size_t nCanary = mnBytesPerRow * mnHeight;
604 assert(!memcmp(pData + nCanary, CANARY, sizeof (CANARY)));
605 #endif
606 return true;
608 else if (mnBits == 1 || mnBits == 4)
609 { // convert buffers from 24-bit RGB to 1 or 4-bit buffer
610 std::vector<sal_uInt8> aBuffer(mnWidth * mnHeight * 3);
612 sal_uInt8* pBuffer = aBuffer.data();
613 determineTextureFormat(24, nFormat, nType);
614 maTexture.Read(nFormat, nType, pBuffer);
615 sal_uInt32 nSourceBytesPerRow = lclBytesPerRow(24, mnWidth);
617 std::unique_ptr<ScanlineWriter> pWriter;
618 switch(mnBits)
620 case 1:
621 pWriter.reset(new ScanlineWriter(maPalette, 8));
622 break;
623 case 4:
624 default:
625 pWriter.reset(new ScanlineWriter(maPalette, 2));
626 break;
629 for (int y = 0; y < mnHeight; ++y)
631 sal_uInt8* pSource = &pBuffer[y * nSourceBytesPerRow];
632 sal_uInt8* pDestination = &pData[y * mnBytesPerRow];
634 pWriter->nextLine(pDestination);
636 for (int x = 0; x < mnWidth; ++x)
638 // read source
639 sal_uInt8 nR = *pSource++;
640 sal_uInt8 nG = *pSource++;
641 sal_uInt8 nB = *pSource++;
643 pWriter->writeRGB(nR, nG, nB);
646 return true;
649 SAL_WARN("vcl.opengl", "::ReadTexture - tx:" << maTexture.Id() << " @ "
650 << mnWidth << "x" << mnHeight << "- unimplemented bit depth: "
651 << mnBits);
652 return false;
655 sal_uInt16 OpenGLSalBitmap::GetBitCount() const
657 return mnBits;
660 bool OpenGLSalBitmap::calcChecksumGL(OpenGLTexture& rInputTexture, BitmapChecksum& rChecksum) const
662 OUString FragShader("areaHashCRC64TFragmentShader");
664 rtl::Reference< OpenGLContext > xContext = OpenGLContext::getVCLContext();
665 xContext->state().scissor().disable();
666 xContext->state().stencil().disable();
668 static vcl::DeleteOnDeinit<OpenGLTexture> gCRCTableTexture(
669 new OpenGLTexture(512, 1, GL_RGBA, GL_UNSIGNED_BYTE,
670 vcl_get_crc64_table()));
671 OpenGLTexture &rCRCTableTexture = *gCRCTableTexture.get();
673 // First Pass
675 int nWidth = rInputTexture.GetWidth();
676 int nHeight = rInputTexture.GetHeight();
678 OpenGLProgram* pProgram = xContext->UseProgram("textureVertexShader", FragShader);
679 if (pProgram == nullptr)
680 return false;
682 int nNewWidth = ceil( nWidth / 4.0 );
683 int nNewHeight = ceil( nHeight / 4.0 );
685 OpenGLTexture aFirstPassTexture = OpenGLTexture(nNewWidth, nNewHeight);
686 OpenGLFramebuffer* pFramebuffer = xContext->AcquireFramebuffer(aFirstPassTexture);
688 pProgram->SetUniform1f( "xstep", 1.0 / mnWidth );
689 pProgram->SetUniform1f( "ystep", 1.0 / mnHeight );
691 pProgram->SetTexture("crc_table", rCRCTableTexture);
692 pProgram->SetTexture("sampler", rInputTexture);
693 pProgram->DrawTexture(rInputTexture);
694 pProgram->Clean();
696 OpenGLContext::ReleaseFramebuffer(pFramebuffer);
698 CHECK_GL_ERROR();
700 // Second Pass
702 nWidth = aFirstPassTexture.GetWidth();
703 nHeight = aFirstPassTexture.GetHeight();
705 pProgram = xContext->UseProgram("textureVertexShader", FragShader);
706 if (pProgram == nullptr)
707 return false;
709 nNewWidth = ceil( nWidth / 4.0 );
710 nNewHeight = ceil( nHeight / 4.0 );
712 OpenGLTexture aSecondPassTexture = OpenGLTexture(nNewWidth, nNewHeight);
713 pFramebuffer = xContext->AcquireFramebuffer(aSecondPassTexture);
715 pProgram->SetUniform1f( "xstep", 1.0 / mnWidth );
716 pProgram->SetUniform1f( "ystep", 1.0 / mnHeight );
718 pProgram->SetTexture("crc_table", rCRCTableTexture);
719 pProgram->SetTexture("sampler", aFirstPassTexture);
720 pProgram->DrawTexture(aFirstPassTexture);
721 pProgram->Clean();
723 OpenGLContext::ReleaseFramebuffer(pFramebuffer);
725 CHECK_GL_ERROR();
727 // Final CRC on CPU
728 OpenGLTexture& aFinalTexture = aSecondPassTexture;
729 std::vector<sal_uInt8> aBuf( aFinalTexture.GetWidth() * aFinalTexture.GetHeight() * 4 );
730 aFinalTexture.Read(GL_RGBA, GL_UNSIGNED_BYTE, aBuf.data());
732 BitmapChecksum nCrc = vcl_get_checksum(0, aBuf.data(), aBuf.size());
734 rChecksum = nCrc;
735 return true;
738 void OpenGLSalBitmap::updateChecksum() const
740 if (mbChecksumValid)
741 return;
743 if( (mnWidth * mnHeight) < (1024*768) || mnWidth < 128 || mnHeight < 128 )
745 SalBitmap::updateChecksum();
746 return;
749 OpenGLSalBitmap* pThis = const_cast<OpenGLSalBitmap*>(this);
751 OpenGLVCLContextZone aContextZone;
752 OpenGLTexture& rInputTexture = GetTexture();
753 pThis->mbChecksumValid = calcChecksumGL(rInputTexture, pThis->mnChecksum);
754 if (!pThis->mbChecksumValid)
755 SalBitmap::updateChecksum();
758 BitmapBuffer* OpenGLSalBitmap::AcquireBuffer( BitmapAccessMode nMode )
760 OpenGLVCLContextZone aContextZone;
762 if( nMode != BitmapAccessMode::Info )
764 if (!mpUserBuffer.get())
766 if( !AllocateUserData() )
767 return nullptr;
768 if( maTexture && !ReadTexture() )
769 return nullptr;
773 // mpUserBuffer must be unique when we are doing the write access
774 if (nMode == BitmapAccessMode::Write && mpUserBuffer && mpUserBuffer.use_count() > 1)
776 std::shared_ptr<sal_uInt8> aBuffer(mpUserBuffer);
778 mpUserBuffer.reset();
779 AllocateUserData();
780 memcpy(mpUserBuffer.get(), aBuffer.get(), mnBytesPerRow * mnHeight);
783 BitmapBuffer* pBuffer = new BitmapBuffer;
784 pBuffer->mnWidth = mnWidth;
785 pBuffer->mnHeight = mnHeight;
786 pBuffer->maPalette = maPalette;
787 pBuffer->mnScanlineSize = mnBytesPerRow;
788 pBuffer->mpBits = mpUserBuffer.get();
789 pBuffer->mnBitCount = mnBits;
791 switch (mnBits)
793 case 1:
794 pBuffer->mnFormat = ScanlineFormat::N1BitMsbPal;
795 break;
796 case 4:
797 pBuffer->mnFormat = ScanlineFormat::N4BitMsnPal;
798 break;
799 case 8:
800 pBuffer->mnFormat = ScanlineFormat::N8BitPal;
801 break;
802 case 16:
804 pBuffer->mnFormat = ScanlineFormat::N16BitTcMsbMask;
805 ColorMaskElement aRedMask(0x0000f800);
806 aRedMask.CalcMaskShift();
807 ColorMaskElement aGreenMask(0x000007e0);
808 aGreenMask.CalcMaskShift();
809 ColorMaskElement aBlueMask(0x0000001f);
810 aBlueMask.CalcMaskShift();
811 pBuffer->maColorMask = ColorMask(aRedMask, aGreenMask, aBlueMask);
812 break;
814 case 24:
816 pBuffer->mnFormat = ScanlineFormat::N24BitTcRgb;
817 break;
819 case 32:
821 pBuffer->mnFormat = ScanlineFormat::N32BitTcRgba;
822 ColorMaskElement aRedMask(0xff000000);
823 aRedMask.CalcMaskShift();
824 ColorMaskElement aGreenMask(0x00ff0000);
825 aGreenMask.CalcMaskShift();
826 ColorMaskElement aBlueMask(0x0000ff00);
827 aBlueMask.CalcMaskShift();
828 pBuffer->maColorMask = ColorMask(aRedMask, aGreenMask, aBlueMask);
829 break;
833 return pBuffer;
836 void OpenGLSalBitmap::ReleaseBuffer( BitmapBuffer* pBuffer, BitmapAccessMode nMode )
838 OpenGLVCLContextZone aContextZone;
840 if( nMode == BitmapAccessMode::Write )
842 maTexture = OpenGLTexture();
843 mbDirtyTexture = true;
844 mbChecksumValid = false;
846 // The palette is modified on read during the BitmapWriteAccess,
847 // but of course, often it is not modified; interesting.
848 maPalette = pBuffer->maPalette;
850 // Are there any more ground movements underneath us ?
851 assert( pBuffer->mnWidth == mnWidth );
852 assert( pBuffer->mnHeight == mnHeight );
853 assert( pBuffer->mnBitCount == mnBits );
855 delete pBuffer;
858 bool OpenGLSalBitmap::GetSystemData( BitmapSystemData& /*rData*/ )
860 SAL_WARN( "vcl.opengl", "*** NOT IMPLEMENTED *** GetSystemData" );
861 #if 0
862 // TODO Implement for ANDROID/OSX/IOS/WIN32
863 X11SalBitmap rBitmap;
864 BitmapBuffer* pBuffer;
866 rBitmap.Create( GetSize(), mnBits, maPalette );
867 pBuffer = rBitmap.AcquireBuffer( false );
868 if( pBuffer == NULL )
869 return false;
871 if (!mpUserBuffer.get())
873 if( !AllocateUserData() || !ReadTexture() )
875 rBitmap.ReleaseBuffer( pBuffer, false );
876 return false;
880 // TODO Might be more efficient to add a static method to SalBitmap
881 // to get system data from a buffer
882 memcpy( pBuffer->mpBits, mpUserBuffer.get(), mnBytesPerRow * mnHeight );
884 rBitmap.ReleaseBuffer( pBuffer, false );
885 return rBitmap.GetSystemData( rData );
886 #else
887 return false;
888 #endif
891 bool OpenGLSalBitmap::Replace( const Color& rSearchColor, const Color& rReplaceColor, sal_uInt8 nTol )
893 VCL_GL_INFO("::Replace");
895 OpenGLZone aZone;
896 rtl::Reference<OpenGLContext> xContext = OpenGLContext::getVCLContext();
897 xContext->state().scissor().disable();
898 xContext->state().stencil().disable();
900 OpenGLFramebuffer* pFramebuffer;
901 OpenGLProgram* pProgram;
903 GetTexture();
904 pProgram = xContext->UseProgram( "textureVertexShader",
905 "replaceColorFragmentShader" );
906 if( !pProgram )
907 return false;
909 OpenGLTexture aNewTex = OpenGLTexture( mnWidth, mnHeight );
910 pFramebuffer = xContext->AcquireFramebuffer( aNewTex );
912 pProgram->SetTexture( "sampler", maTexture );
913 pProgram->SetColor( "search_color", rSearchColor );
914 pProgram->SetColor( "replace_color", rReplaceColor );
915 pProgram->SetUniform1f( "epsilon", nTol / 255.0f );
916 pProgram->DrawTexture( maTexture );
917 pProgram->Clean();
919 OpenGLContext::ReleaseFramebuffer( pFramebuffer );
920 maTexture = aNewTex;
922 CHECK_GL_ERROR();
923 return true;
926 // Convert texture to greyscale and adjust bitmap metadata
927 bool OpenGLSalBitmap::ConvertToGreyscale()
929 VCL_GL_INFO("::ConvertToGreyscale");
931 // avoid re-converting to 8bits.
932 if ( mnBits == 8 && maPalette == Bitmap::GetGreyPalette(256) )
933 return false;
935 OpenGLZone aZone;
936 rtl::Reference<OpenGLContext> xContext = OpenGLContext::getVCLContext();
937 xContext->state().scissor().disable();
938 xContext->state().stencil().disable();
940 OpenGLFramebuffer* pFramebuffer;
941 OpenGLProgram* pProgram;
943 GetTexture();
944 pProgram = xContext->UseProgram("textureVertexShader", "greyscaleFragmentShader");
946 if (!pProgram)
947 return false;
949 OpenGLTexture aNewTex(mnWidth, mnHeight);
950 pFramebuffer = xContext->AcquireFramebuffer(aNewTex);
951 pProgram->SetTexture("sampler", maTexture);
952 pProgram->DrawTexture(maTexture);
953 pProgram->Clean();
955 OpenGLContext::ReleaseFramebuffer( pFramebuffer );
956 maTexture = aNewTex;
957 mnBits = 8;
958 maPalette = Bitmap::GetGreyPalette(256);
960 // AllocateUserData will handle the rest.
961 mpUserBuffer.reset();
962 mbDirtyTexture = false;
964 CHECK_GL_ERROR();
965 return true;
968 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */