Version 6.4.0.0.beta1, tag libreoffice-6.4.0.0.beta1
[LibreOffice.git] / vcl / opengl / salbmp.cxx
blob36c34a2b3b926a6e5b64e984a283dfd63918404d
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>
22 #include <sal/log.hxx>
23 #include <osl/diagnose.h>
24 #include <tools/debug.hxx>
26 #include <vcl/opengl/OpenGLHelper.hxx>
28 #include <vcl/bitmap.hxx>
29 #include <vcl/checksum.hxx>
30 #include <vcl/outdev.hxx>
31 #include <svdata.hxx>
32 #include <salgdi.hxx>
33 #include <vcleventlisteners.hxx>
34 #include <vcl/lazydelete.hxx>
36 #include <o3tl/make_shared.hxx>
38 #include <opengl/zone.hxx>
39 #include <opengl/program.hxx>
40 #include <opengl/salbmp.hxx>
41 #include <opengl/RenderState.hxx>
42 #include <opengl/FixedTextureAtlas.hxx>
44 #if OSL_DEBUG_LEVEL > 0
45 # define CANARY "tex-canary"
46 #endif
48 namespace
51 bool determineTextureFormat(sal_uInt16 nBits, GLenum& nFormat, GLenum& nType)
53 switch(nBits)
55 case 8:
56 nFormat = GL_LUMINANCE;
57 nType = GL_UNSIGNED_BYTE;
58 return true;
59 case 24:
60 nFormat = GL_RGB;
61 nType = GL_UNSIGNED_BYTE;
62 return true;
63 case 32:
64 nFormat = GL_RGBA;
65 nType = GL_UNSIGNED_BYTE;
66 return true;
67 default:
68 break;
70 SAL_WARN("vcl.opengl", "Could not determine the appropriate texture format for input bits '" << nBits << "'");
71 return false;
74 bool isValidBitCount( sal_uInt16 nBitCount )
76 return (nBitCount == 1) || (nBitCount == 4) || (nBitCount == 8) || (nBitCount == 24) || (nBitCount == 32);
79 sal_uInt32 lclBytesPerRow(sal_uInt16 nBits, int nWidth)
81 switch(nBits)
83 case 1: return (nWidth + 7) >> 3;
84 case 4: return (nWidth + 1) >> 1;
85 case 8: return nWidth;
86 case 24: return nWidth * 3;
87 case 32: return nWidth * 4;
88 default:
89 OSL_FAIL("vcl::OpenGLSalBitmap::AllocateUserData(), illegal bitcount!");
91 return 0;
94 typedef std::vector<std::unique_ptr< FixedTextureAtlasManager > > TextureAtlasVector;
95 static vcl::DeleteOnDeinit< TextureAtlasVector > gTextureAtlases(new TextureAtlasVector);
99 OpenGLSalBitmap::OpenGLSalBitmap()
100 : mbDirtyTexture(true)
101 , mnBits(0)
102 , mnBytesPerRow(0)
103 , mnWidth(0)
104 , mnHeight(0)
108 OpenGLSalBitmap::~OpenGLSalBitmap()
110 Destroy();
111 VCL_GL_INFO( "~OpenGLSalBitmap" );
114 void OpenGLSalBitmap::Create( const OpenGLTexture& rTex, long nX, long nY, long nWidth, long nHeight )
116 DBG_TESTSOLARMUTEX();
117 static const BitmapPalette aEmptyPalette;
118 OpenGLVCLContextZone aContextZone;
120 Destroy();
121 VCL_GL_INFO( "OpenGLSalBitmap::Create from FBO: ["
122 << nX << ", " << nY << "] " << nWidth << "x" << nHeight );
124 GLint nMaxTextureSize;
125 glGetIntegerv( GL_MAX_TEXTURE_SIZE, &nMaxTextureSize );
126 if ( nWidth > nMaxTextureSize )
128 nWidth = nMaxTextureSize;
129 VCL_GL_INFO( "Width limited to " << nMaxTextureSize );
132 if ( nHeight > nMaxTextureSize )
134 nHeight = nMaxTextureSize;
135 VCL_GL_INFO( "Height limited to " << nMaxTextureSize );
138 mnWidth = nWidth;
139 mnHeight = nHeight;
141 // TODO Check the framebuffer configuration
142 mnBits = 32;
143 maPalette = aEmptyPalette;
145 if( rTex )
146 maTexture = OpenGLTexture( rTex, nX, nY, nWidth, nHeight );
147 else
148 maTexture = OpenGLTexture( nX, nY, nWidth, nHeight );
149 mbDirtyTexture = false;
150 VCL_GL_INFO( "Created texture " << maTexture.Id() );
152 assert(mnWidth == maTexture.GetWidth() &&
153 mnHeight == maTexture.GetHeight());
156 bool OpenGLSalBitmap::Create( const Size& rSize, sal_uInt16 nBits, const BitmapPalette& rBitmapPalette )
158 DBG_TESTSOLARMUTEX();
159 OpenGLVCLContextZone aContextZone;
161 Destroy();
162 VCL_GL_INFO( "OpenGLSalBitmap::Create with size: " << rSize );
164 if( !isValidBitCount( nBits ) )
165 return false;
166 maPalette = rBitmapPalette;
167 mnBits = nBits;
168 mnWidth = rSize.Width();
169 mnHeight = rSize.Height();
171 // Limit size to what GL allows, so later glTexImage2D() won't fail.
172 GLint nMaxTextureSize;
173 glGetIntegerv(GL_MAX_TEXTURE_SIZE, &nMaxTextureSize);
174 if (mnWidth > nMaxTextureSize)
175 mnWidth = nMaxTextureSize;
176 if (mnHeight > nMaxTextureSize)
177 mnHeight = nMaxTextureSize;
179 return false;
182 bool OpenGLSalBitmap::Create( const SalBitmap& rSalBmp )
184 DBG_TESTSOLARMUTEX();
185 return Create( rSalBmp, rSalBmp.GetBitCount() );
188 bool OpenGLSalBitmap::Create( const SalBitmap& rSalBmp, SalGraphics* pGraphics )
190 DBG_TESTSOLARMUTEX();
191 return Create( rSalBmp, pGraphics ? pGraphics->GetBitCount() : rSalBmp.GetBitCount() );
194 bool OpenGLSalBitmap::Create( const SalBitmap& rSalBmp, sal_uInt16 nNewBitCount )
196 DBG_TESTSOLARMUTEX();
197 OpenGLZone aZone;
199 // check that carefully only in the debug mode
200 assert(dynamic_cast<const OpenGLSalBitmap*>(&rSalBmp));
202 const OpenGLSalBitmap& rSourceBitmap = static_cast<const OpenGLSalBitmap&>(rSalBmp);
204 VCL_GL_INFO("OpenGLSalBitmap::Create from BMP: "
205 << rSourceBitmap.mnWidth << "x" << rSourceBitmap.mnHeight
206 << " Bits old: " << mnBits << " new:" << nNewBitCount );
208 if( isValidBitCount( nNewBitCount ) )
210 // TODO: lfrb: What about the pending operations?!
211 mnBits = nNewBitCount;
212 mnBytesPerRow = rSourceBitmap.mnBytesPerRow;
213 mnWidth = rSourceBitmap.mnWidth;
214 mnHeight = rSourceBitmap.mnHeight;
215 maPalette = rSourceBitmap.maPalette;
216 // execute any pending operations on the source bitmap
217 maTexture = rSourceBitmap.GetTexture();
218 mbDirtyTexture = false;
220 // be careful here, we are share & reference-count the
221 // mpUserBuffer, BUT this Create() is called from
222 // Bitmap::ImplMakeUnique().
223 // Consequently, there might be cases when this needs to be made
224 // unique later (when we don't do that right away here), like when
225 // using the BitmapWriteAccess.
226 mpUserBuffer = rSourceBitmap.mpUserBuffer;
228 return true;
230 return false;
233 bool OpenGLSalBitmap::Create( const css::uno::Reference< css::rendering::XBitmapCanvas >& /*xBitmapCanvas*/, Size& /*rSize*/, bool /*bMask*/ )
235 DBG_TESTSOLARMUTEX();
236 // TODO Is this method needed?
237 return false;
240 OpenGLTexture& OpenGLSalBitmap::GetTexture() const
242 OpenGLSalBitmap* pThis = const_cast<OpenGLSalBitmap*>(this);
243 if( !maTexture || mbDirtyTexture )
244 pThis->CreateTexture();
245 VCL_GL_INFO( "Got texture " << maTexture.Id() );
246 return pThis->maTexture;
249 void OpenGLSalBitmap::Destroy()
251 OpenGLZone aZone;
253 VCL_GL_INFO("Destroy OpenGLSalBitmap texture:" << maTexture.Id());
254 maTexture = OpenGLTexture();
255 DeallocateUserData();
258 bool OpenGLSalBitmap::AllocateUserData()
260 VCL_GL_INFO( "OpenGLSalBitmap::AllocateUserData" );
262 if( mnWidth && mnHeight )
264 mnBytesPerRow = lclBytesPerRow(mnBits, mnWidth);
267 bool alloc = false;
268 if (mnBytesPerRow != 0 && mnHeight &&
269 mnBytesPerRow <= std::numeric_limits<sal_uInt32>::max() / mnHeight)
273 size_t nToAllocate = mnBytesPerRow * mnHeight;
274 #if OSL_DEBUG_LEVEL > 0
275 nToAllocate += sizeof(CANARY);
276 #endif
277 mpUserBuffer = o3tl::make_shared_array<sal_uInt8>(nToAllocate);
278 #if OSL_DEBUG_LEVEL > 0
279 memcpy(mpUserBuffer.get() + nToAllocate - sizeof(CANARY),
280 CANARY, sizeof(CANARY));
281 #endif
282 alloc = true;
284 catch (const std::bad_alloc &) {}
286 if (!alloc)
288 SAL_WARN("vcl.opengl", "bad alloc " << mnBytesPerRow << "x" << mnHeight);
289 DeallocateUserData();
291 #ifdef DBG_UTIL
292 else
294 for (size_t i = 0; i < size_t(mnBytesPerRow * mnHeight); i++)
295 mpUserBuffer.get()[i] = (i & 0xFF);
297 #endif
299 return mpUserBuffer != nullptr;
302 void OpenGLSalBitmap::DeallocateUserData()
304 mpUserBuffer.reset();
305 mnBytesPerRow = 0;
308 namespace {
310 class ImplPixelFormat
312 protected:
313 sal_uInt8* mpData;
314 public:
315 static ImplPixelFormat* GetFormat( sal_uInt16 nBits, const BitmapPalette& rPalette );
317 virtual void StartLine( sal_uInt8* pLine ) { mpData = pLine; }
318 virtual const BitmapColor& ReadPixel() = 0;
319 virtual ~ImplPixelFormat() { }
322 class ImplPixelFormat8 : public ImplPixelFormat
324 private:
325 const BitmapPalette& mrPalette;
327 public:
328 explicit ImplPixelFormat8( const BitmapPalette& rPalette )
329 : mrPalette( rPalette )
332 virtual const BitmapColor& ReadPixel() override
334 assert( mrPalette.GetEntryCount() > *mpData );
335 return mrPalette[ *mpData++ ];
339 class ImplPixelFormat4 : public ImplPixelFormat
341 private:
342 const BitmapPalette& mrPalette;
343 sal_uInt32 mnX;
344 sal_uInt32 mnShift;
346 public:
347 explicit ImplPixelFormat4( const BitmapPalette& rPalette )
348 : mrPalette( rPalette )
349 , mnX(0)
350 , mnShift(4)
353 virtual void StartLine( sal_uInt8* pLine ) override
355 mpData = pLine;
356 mnX = 0;
357 mnShift = 4;
359 virtual const BitmapColor& ReadPixel() override
361 sal_uInt32 nIdx = ( mpData[mnX >> 1] >> mnShift) & 0x0f;
362 assert( mrPalette.GetEntryCount() > nIdx );
363 const BitmapColor& rColor = mrPalette[nIdx];
364 mnX++;
365 mnShift ^= 4;
366 return rColor;
370 class ImplPixelFormat1 : public ImplPixelFormat
372 private:
373 const BitmapPalette& mrPalette;
374 sal_uInt32 mnX;
376 public:
377 explicit ImplPixelFormat1( const BitmapPalette& rPalette )
378 : mrPalette(rPalette)
379 , mnX(0)
382 virtual void StartLine( sal_uInt8* pLine ) override
384 mpData = pLine;
385 mnX = 0;
387 virtual const BitmapColor& ReadPixel() override
389 const BitmapColor& rColor = mrPalette[ (mpData[mnX >> 3 ] >> ( 7 - ( mnX & 7 ) )) & 1];
390 mnX++;
391 return rColor;
395 ImplPixelFormat* ImplPixelFormat::GetFormat( sal_uInt16 nBits, const BitmapPalette& rPalette )
397 switch( nBits )
399 case 1: return new ImplPixelFormat1( rPalette );
400 case 4: return new ImplPixelFormat4( rPalette );
401 case 8: return new ImplPixelFormat8( rPalette );
404 return nullptr;
407 void lclInstantiateTexture(OpenGLTexture& rTexture, const int nWidth, const int nHeight,
408 const GLenum nFormat, const GLenum nType, sal_uInt8 const * pData)
410 if (nWidth == nHeight)
412 TextureAtlasVector &sTextureAtlases = *gTextureAtlases.get();
413 if (sTextureAtlases.empty())
415 sTextureAtlases.push_back(std::make_unique<FixedTextureAtlasManager>(8, 8, 16));
416 sTextureAtlases.push_back(std::make_unique<FixedTextureAtlasManager>(8, 8, 24));
417 sTextureAtlases.push_back(std::make_unique<FixedTextureAtlasManager>(8, 8, 32));
418 sTextureAtlases.push_back(std::make_unique<FixedTextureAtlasManager>(8, 8, 48));
419 sTextureAtlases.push_back(std::make_unique<FixedTextureAtlasManager>(8, 8, 64));
421 for (std::unique_ptr<FixedTextureAtlasManager> & pTextureAtlas : sTextureAtlases)
423 if (nWidth == pTextureAtlas->GetSubtextureSize())
425 rTexture = pTextureAtlas->InsertBuffer(nWidth, nHeight, nFormat, nType, pData);
426 return;
430 rTexture = OpenGLTexture (nWidth, nHeight, nFormat, nType, pData);
433 // Write color information for 1 and 4 bit palette bitmap scanlines.
434 class ScanlineWriter
436 BitmapPalette& maPalette;
437 sal_uInt8 const mnColorsPerByte; // number of colors that are stored in one byte
438 sal_uInt8 const mnColorBitSize; // number of bits a color takes
439 sal_uInt8 const mnColorBitMask; // bit mask used to isolate the color
440 sal_uInt8* mpCurrentScanline;
441 long mnX;
443 public:
444 ScanlineWriter(BitmapPalette& aPalette, sal_Int8 nColorsPerByte)
445 : maPalette(aPalette)
446 , mnColorsPerByte(nColorsPerByte)
447 , 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)
448 , mnColorBitMask((1 << mnColorBitSize) - 1) // calculate the bit mask from the bit size
449 , mpCurrentScanline(nullptr)
450 , mnX(0)
453 void writeRGB(sal_uInt8 nR, sal_uInt8 nG, sal_uInt8 nB)
455 // calculate to which index we will write
456 long nScanlineIndex = mnX / mnColorsPerByte;
458 // calculate the number of shifts to get the color information to the right place
459 long nShift = (8 - mnColorBitSize) - ((mnX % mnColorsPerByte) * mnColorBitSize);
461 sal_uInt16 nColorIndex = maPalette.GetBestIndex(BitmapColor(nR, nG, nB));
462 mpCurrentScanline[nScanlineIndex] &= ~(mnColorBitMask << nShift); // clear
463 mpCurrentScanline[nScanlineIndex] |= (nColorIndex & mnColorBitMask) << nShift; // set
464 mnX++;
467 void nextLine(sal_uInt8* pScanline)
469 mnX = 0;
470 mpCurrentScanline = pScanline;
474 } // end anonymous namespace
476 Size OpenGLSalBitmap::GetSize() const
478 return Size(mnWidth, mnHeight);
481 GLuint OpenGLSalBitmap::CreateTexture()
483 VCL_GL_INFO( "::CreateTexture bits: " << mnBits);
484 GLenum nFormat = GL_RGBA;
485 GLenum nType = GL_UNSIGNED_BYTE;
486 sal_uInt8* pData( nullptr );
487 bool bAllocated( false );
489 if (mpUserBuffer != nullptr)
491 if( mnBits == 24 || mnBits == 32 )
493 // no conversion needed for truecolor
494 pData = mpUserBuffer.get();
496 determineTextureFormat(mnBits, nFormat, nType);
498 else
500 VCL_GL_INFO( "::CreateTexture - convert from " << mnBits << " to 24 bits" );
502 // convert to 24 bits RGB using palette
503 pData = new sal_uInt8[mnHeight * mnWidth * 3];
504 bAllocated = true;
505 determineTextureFormat(24, nFormat, nType);
507 std::unique_ptr<ImplPixelFormat> pSrcFormat(ImplPixelFormat::GetFormat(mnBits, maPalette));
509 sal_uInt8* pSrcData = mpUserBuffer.get();
510 sal_uInt8* pDstData = pData;
512 sal_uInt32 nY = mnHeight;
513 while( nY-- )
515 pSrcFormat->StartLine( pSrcData );
517 sal_uInt32 nX = mnWidth;
518 if (nFormat == GL_BGR)
520 while( nX-- )
522 const BitmapColor& c = pSrcFormat->ReadPixel();
523 *pDstData++ = c.GetBlue();
524 *pDstData++ = c.GetGreen();
525 *pDstData++ = c.GetRed();
528 else // RGB
530 while( nX-- )
532 const BitmapColor& c = pSrcFormat->ReadPixel();
533 *pDstData++ = c.GetRed();
534 *pDstData++ = c.GetGreen();
535 *pDstData++ = c.GetBlue();
539 pSrcData += mnBytesPerRow;
544 OpenGLVCLContextZone aContextZone;
546 lclInstantiateTexture(maTexture, mnWidth, mnHeight, nFormat, nType, pData);
548 VCL_GL_INFO("Created texture " << maTexture.Id() << " bits: " << mnBits);
550 if( bAllocated )
551 delete[] pData;
553 mbDirtyTexture = false;
555 CHECK_GL_ERROR();
556 return maTexture.Id();
559 bool OpenGLSalBitmap::ReadTexture()
561 sal_uInt8* pData = mpUserBuffer.get();
563 GLenum nFormat = GL_RGBA;
564 GLenum nType = GL_UNSIGNED_BYTE;
566 VCL_GL_INFO( "::ReadTexture " << mnWidth << "x" << mnHeight << " bits: " << mnBits);
568 if( pData == nullptr )
569 return false;
571 OpenGLVCLContextZone aContextZone;
573 rtl::Reference<OpenGLContext> xContext = OpenGLContext::getVCLContext();
574 xContext->state().scissor().disable();
575 xContext->state().stencil().disable();
577 if ((mnBits == 8 && maPalette.IsGreyPalette()) || mnBits == 24 || mnBits == 32)
579 determineTextureFormat(mnBits, nFormat, nType);
581 #if OSL_DEBUG_LEVEL > 0
582 // help valgrind & drmemory rescue us - touch last and first bits.
583 pData[0] = 0;
584 pData[mnBits/8*mnWidth*mnHeight-1] = 0;
585 // if this fails we can read too much into pData
586 assert(mnWidth == maTexture.GetWidth() &&
587 mnHeight == maTexture.GetHeight());
588 #endif
590 maTexture.Read(nFormat, nType, pData);
592 #if OSL_DEBUG_LEVEL > 0 && !defined NDEBUG
593 // If we read over the end of pData we have a real hidden memory
594 // corruption problem !
595 size_t nCanary = mnBytesPerRow * mnHeight;
596 assert(!memcmp(pData + nCanary, CANARY, sizeof (CANARY)));
597 #endif
598 return true;
600 else if (mnBits == 1 || mnBits == 4 || mnBits == 8)
601 { // convert buffers from 24-bit RGB to 1,4 or 8-bit buffer
602 std::vector<sal_uInt8> aBuffer(mnWidth * mnHeight * 3);
604 sal_uInt8* pBuffer = aBuffer.data();
605 determineTextureFormat(24, nFormat, nType);
606 maTexture.Read(nFormat, nType, pBuffer);
607 sal_uInt32 nSourceBytesPerRow = lclBytesPerRow(24, mnWidth);
609 std::unique_ptr<ScanlineWriter> pWriter;
610 switch(mnBits)
612 case 1:
613 pWriter.reset(new ScanlineWriter(maPalette, 8));
614 break;
615 case 4:
616 pWriter.reset(new ScanlineWriter(maPalette, 2));
617 break;
618 case 8:
619 pWriter.reset(new ScanlineWriter(maPalette, 1));
620 break;
621 default:
622 abort();
625 for (int y = 0; y < mnHeight; ++y)
627 sal_uInt8* pSource = &pBuffer[y * nSourceBytesPerRow];
628 sal_uInt8* pDestination = &pData[y * mnBytesPerRow];
630 pWriter->nextLine(pDestination);
632 for (int x = 0; x < mnWidth; ++x)
634 // read source
635 sal_uInt8 nR = *pSource++;
636 sal_uInt8 nG = *pSource++;
637 sal_uInt8 nB = *pSource++;
639 pWriter->writeRGB(nR, nG, nB);
642 return true;
645 SAL_WARN("vcl.opengl", "::ReadTexture - tx:" << maTexture.Id() << " @ "
646 << mnWidth << "x" << mnHeight << "- unimplemented bit depth: "
647 << mnBits);
648 return false;
651 sal_uInt16 OpenGLSalBitmap::GetBitCount() const
653 return mnBits;
656 bool OpenGLSalBitmap::calcChecksumGL(OpenGLTexture& rInputTexture, BitmapChecksum& rChecksum) const
658 OUString FragShader("areaHashCRC64TFragmentShader");
660 rtl::Reference< OpenGLContext > xContext = OpenGLContext::getVCLContext();
661 xContext->state().scissor().disable();
662 xContext->state().stencil().disable();
664 static vcl::DeleteOnDeinit<OpenGLTexture> gCRCTableTexture(
665 new OpenGLTexture(512, 1, GL_RGBA, GL_UNSIGNED_BYTE,
666 vcl_get_crc64_table()));
667 OpenGLTexture &rCRCTableTexture = *gCRCTableTexture.get();
669 // First Pass
671 int nWidth = rInputTexture.GetWidth();
672 int nHeight = rInputTexture.GetHeight();
674 OpenGLProgram* pProgram = xContext->UseProgram("textureVertexShader", FragShader);
675 if (pProgram == nullptr)
676 return false;
678 int nNewWidth = ceil( nWidth / 4.0 );
679 int nNewHeight = ceil( nHeight / 4.0 );
681 OpenGLTexture aFirstPassTexture(nNewWidth, nNewHeight);
682 OpenGLFramebuffer* pFramebuffer = xContext->AcquireFramebuffer(aFirstPassTexture);
684 pProgram->SetUniform1f( "xstep", 1.0 / mnWidth );
685 pProgram->SetUniform1f( "ystep", 1.0 / mnHeight );
687 pProgram->SetTexture("crc_table", rCRCTableTexture);
688 pProgram->SetTexture("sampler", rInputTexture);
689 pProgram->DrawTexture(rInputTexture);
690 pProgram->Clean();
692 OpenGLContext::ReleaseFramebuffer(pFramebuffer);
694 CHECK_GL_ERROR();
696 // Second Pass
698 nWidth = aFirstPassTexture.GetWidth();
699 nHeight = aFirstPassTexture.GetHeight();
701 pProgram = xContext->UseProgram("textureVertexShader", FragShader);
702 if (pProgram == nullptr)
703 return false;
705 nNewWidth = ceil( nWidth / 4.0 );
706 nNewHeight = ceil( nHeight / 4.0 );
708 OpenGLTexture aSecondPassTexture(nNewWidth, nNewHeight);
709 pFramebuffer = xContext->AcquireFramebuffer(aSecondPassTexture);
711 pProgram->SetUniform1f( "xstep", 1.0 / mnWidth );
712 pProgram->SetUniform1f( "ystep", 1.0 / mnHeight );
714 pProgram->SetTexture("crc_table", rCRCTableTexture);
715 pProgram->SetTexture("sampler", aFirstPassTexture);
716 pProgram->DrawTexture(aFirstPassTexture);
717 pProgram->Clean();
719 OpenGLContext::ReleaseFramebuffer(pFramebuffer);
721 CHECK_GL_ERROR();
723 // Final CRC on CPU
724 OpenGLTexture& aFinalTexture = aSecondPassTexture;
725 std::vector<sal_uInt8> aBuf( aFinalTexture.GetWidth() * aFinalTexture.GetHeight() * 4 );
726 aFinalTexture.Read(GL_RGBA, GL_UNSIGNED_BYTE, aBuf.data());
728 BitmapChecksum nCrc = vcl_get_checksum(0, aBuf.data(), aBuf.size());
730 rChecksum = nCrc;
731 return true;
734 void OpenGLSalBitmap::updateChecksum() const
736 if (mbChecksumValid)
737 return;
739 if( (mnWidth * mnHeight) < (1024*768) || mnWidth < 128 || mnHeight < 128 )
741 SalBitmap::updateChecksum();
742 return;
745 OpenGLSalBitmap* pThis = const_cast<OpenGLSalBitmap*>(this);
747 OpenGLVCLContextZone aContextZone;
748 OpenGLTexture& rInputTexture = GetTexture();
749 pThis->mbChecksumValid = calcChecksumGL(rInputTexture, pThis->mnChecksum);
750 if (!pThis->mbChecksumValid)
751 SalBitmap::updateChecksum();
754 BitmapBuffer* OpenGLSalBitmap::AcquireBuffer( BitmapAccessMode nMode )
756 OpenGLVCLContextZone aContextZone;
758 if( nMode != BitmapAccessMode::Info )
760 if (!mpUserBuffer.get())
762 if( !AllocateUserData() )
763 return nullptr;
764 if( maTexture && !ReadTexture() )
766 DeallocateUserData();
767 return nullptr;
772 // mpUserBuffer must be unique when we are doing the write access
773 if (nMode == BitmapAccessMode::Write && mpUserBuffer && mpUserBuffer.use_count() > 1)
775 std::shared_ptr<sal_uInt8> aBuffer(mpUserBuffer);
777 mpUserBuffer.reset();
778 AllocateUserData();
779 memcpy(mpUserBuffer.get(), aBuffer.get(), mnBytesPerRow * mnHeight);
782 BitmapBuffer* pBuffer = new BitmapBuffer;
783 pBuffer->mnWidth = mnWidth;
784 pBuffer->mnHeight = mnHeight;
785 pBuffer->maPalette = maPalette;
786 pBuffer->mnScanlineSize = mnBytesPerRow;
787 pBuffer->mpBits = mpUserBuffer.get();
788 pBuffer->mnBitCount = mnBits;
790 switch (mnBits)
792 case 1:
793 pBuffer->mnFormat = ScanlineFormat::N1BitMsbPal;
794 break;
795 case 4:
796 pBuffer->mnFormat = ScanlineFormat::N4BitMsnPal;
797 break;
798 case 8:
799 pBuffer->mnFormat = ScanlineFormat::N8BitPal;
800 break;
801 case 24:
803 pBuffer->mnFormat = ScanlineFormat::N24BitTcRgb;
804 break;
806 case 32:
808 pBuffer->mnFormat = ScanlineFormat::N32BitTcRgba;
809 ColorMaskElement aRedMask(0xff000000);
810 aRedMask.CalcMaskShift();
811 ColorMaskElement aGreenMask(0x00ff0000);
812 aGreenMask.CalcMaskShift();
813 ColorMaskElement aBlueMask(0x0000ff00);
814 aBlueMask.CalcMaskShift();
815 pBuffer->maColorMask = ColorMask(aRedMask, aGreenMask, aBlueMask);
816 break;
818 default: assert(false);
821 return pBuffer;
824 void OpenGLSalBitmap::ReleaseBuffer( BitmapBuffer* pBuffer, BitmapAccessMode nMode )
826 OpenGLVCLContextZone aContextZone;
828 if( nMode == BitmapAccessMode::Write )
830 maTexture = OpenGLTexture();
831 mbDirtyTexture = true;
832 mbChecksumValid = false;
834 // The palette is modified on read during the BitmapWriteAccess,
835 // but of course, often it is not modified; interesting.
836 maPalette = pBuffer->maPalette;
838 // Are there any more ground movements underneath us ?
839 assert( pBuffer->mnWidth == mnWidth );
840 assert( pBuffer->mnHeight == mnHeight );
841 assert( pBuffer->mnBitCount == mnBits );
843 delete pBuffer;
846 bool OpenGLSalBitmap::GetSystemData( BitmapSystemData& /*rData*/ )
848 SAL_WARN( "vcl.opengl", "*** NOT IMPLEMENTED *** GetSystemData" );
849 #if 0
850 // TODO Implement for ANDROID/OSX/IOS/WIN32
851 X11SalBitmap rBitmap;
852 BitmapBuffer* pBuffer;
854 rBitmap.Create( GetSize(), mnBits, maPalette );
855 pBuffer = rBitmap.AcquireBuffer( false );
856 if( pBuffer == NULL )
857 return false;
859 if (!mpUserBuffer.get())
861 if( !AllocateUserData() || !ReadTexture() )
863 rBitmap.ReleaseBuffer( pBuffer, false );
864 DeallocateUserData();
865 return false;
869 // TODO Might be more efficient to add a static method to SalBitmap
870 // to get system data from a buffer
871 memcpy( pBuffer->mpBits, mpUserBuffer.get(), mnBytesPerRow * mnHeight );
873 rBitmap.ReleaseBuffer( pBuffer, false );
874 return rBitmap.GetSystemData( rData );
875 #else
876 return false;
877 #endif
880 bool OpenGLSalBitmap::Replace( const Color& rSearchColor, const Color& rReplaceColor, sal_uInt8 nTol )
882 VCL_GL_INFO("::Replace");
884 OpenGLZone aZone;
885 rtl::Reference<OpenGLContext> xContext = OpenGLContext::getVCLContext();
886 xContext->state().scissor().disable();
887 xContext->state().stencil().disable();
889 OpenGLFramebuffer* pFramebuffer;
890 OpenGLProgram* pProgram;
892 GetTexture();
893 pProgram = xContext->UseProgram( "textureVertexShader",
894 "replaceColorFragmentShader" );
895 if( !pProgram )
896 return false;
898 OpenGLTexture aNewTex( mnWidth, mnHeight );
899 pFramebuffer = xContext->AcquireFramebuffer( aNewTex );
901 pProgram->SetTexture( "sampler", maTexture );
902 pProgram->SetColor( "search_color", rSearchColor );
903 pProgram->SetColor( "replace_color", rReplaceColor );
904 pProgram->SetUniform1f( "epsilon", nTol / 255.0f );
905 pProgram->DrawTexture( maTexture );
906 pProgram->Clean();
908 OpenGLContext::ReleaseFramebuffer( pFramebuffer );
909 maTexture = aNewTex;
911 CHECK_GL_ERROR();
912 return true;
915 // Convert texture to greyscale and adjust bitmap metadata
916 bool OpenGLSalBitmap::ConvertToGreyscale()
918 VCL_GL_INFO("::ConvertToGreyscale");
920 // avoid re-converting to 8bits.
921 if ( mnBits == 8 && maPalette == Bitmap::GetGreyPalette(256) )
922 return true;
924 OpenGLZone aZone;
925 rtl::Reference<OpenGLContext> xContext = OpenGLContext::getVCLContext();
926 xContext->state().scissor().disable();
927 xContext->state().stencil().disable();
929 OpenGLFramebuffer* pFramebuffer;
930 OpenGLProgram* pProgram;
932 GetTexture();
933 pProgram = xContext->UseProgram("textureVertexShader", "greyscaleFragmentShader");
935 if (!pProgram)
936 return false;
938 OpenGLTexture aNewTex(mnWidth, mnHeight);
939 pFramebuffer = xContext->AcquireFramebuffer(aNewTex);
940 pProgram->SetTexture("sampler", maTexture);
941 pProgram->DrawTexture(maTexture);
942 pProgram->Clean();
944 OpenGLContext::ReleaseFramebuffer( pFramebuffer );
945 maTexture = aNewTex;
946 mnBits = 8;
947 maPalette = Bitmap::GetGreyPalette(256);
949 // AllocateUserData will handle the rest.
950 DeallocateUserData();
951 mbDirtyTexture = false;
953 CHECK_GL_ERROR();
954 return true;
957 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */