Version 5.4.3.2, tag libreoffice-5.4.3.2
[LibreOffice.git] / vcl / opengl / salbmp.cxx
blobaed6adfef9637be8605b81da8ba7c2fb877d5780
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_uInt16 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 static const BitmapPalette aEmptyPalette;
121 OpenGLVCLContextZone aContextZone;
123 Destroy();
124 VCL_GL_INFO( "OpenGLSalBitmap::Create from FBO: ["
125 << nX << ", " << nY << "] " << nWidth << "x" << nHeight );
127 GLint nMaxTextureSize;
128 glGetIntegerv( GL_MAX_TEXTURE_SIZE, &nMaxTextureSize );
129 if ( nWidth > nMaxTextureSize )
131 nWidth = nMaxTextureSize;
132 VCL_GL_INFO( "Width limited to " << nMaxTextureSize );
135 if ( nHeight > nMaxTextureSize )
137 nHeight = nMaxTextureSize;
138 VCL_GL_INFO( "Height limited to " << nMaxTextureSize );
141 mnWidth = nWidth;
142 mnHeight = nHeight;
144 // TODO Check the framebuffer configuration
145 mnBits = 32;
146 maPalette = aEmptyPalette;
148 if( rTex )
149 maTexture = OpenGLTexture( rTex, nX, nY, nWidth, nHeight );
150 else
151 maTexture = OpenGLTexture( nX, nY, nWidth, nHeight );
152 mbDirtyTexture = false;
153 VCL_GL_INFO( "Created texture " << maTexture.Id() );
155 assert(mnWidth == maTexture.GetWidth() &&
156 mnHeight == maTexture.GetHeight());
158 return true;
161 bool OpenGLSalBitmap::Create( const Size& rSize, sal_uInt16 nBits, const BitmapPalette& rBitmapPalette )
163 OpenGLVCLContextZone aContextZone;
165 Destroy();
166 VCL_GL_INFO( "OpenGLSalBitmap::Create with size: " << rSize );
168 if( !isValidBitCount( nBits ) )
169 return false;
170 maPalette = rBitmapPalette;
171 mnBits = nBits;
172 mnWidth = rSize.Width();
173 mnHeight = rSize.Height();
174 return false;
177 bool OpenGLSalBitmap::Create( const SalBitmap& rSalBmp )
179 return Create( rSalBmp, rSalBmp.GetBitCount() );
182 bool OpenGLSalBitmap::Create( const SalBitmap& rSalBmp, SalGraphics* pGraphics )
184 return Create( rSalBmp, pGraphics ? pGraphics->GetBitCount() : rSalBmp.GetBitCount() );
187 bool OpenGLSalBitmap::Create( const SalBitmap& rSalBmp, sal_uInt16 nNewBitCount )
189 OpenGLZone aZone;
191 // check that carefully only in the debug mode
192 assert(dynamic_cast<const OpenGLSalBitmap*>(&rSalBmp));
194 const OpenGLSalBitmap& rSourceBitmap = static_cast<const OpenGLSalBitmap&>(rSalBmp);
196 VCL_GL_INFO("OpenGLSalBitmap::Create from BMP: "
197 << rSourceBitmap.mnWidth << "x" << rSourceBitmap.mnHeight
198 << " Bits old: " << mnBits << " new:" << nNewBitCount );
200 if( isValidBitCount( nNewBitCount ) )
202 // TODO: lfrb: What about the pending operations?!
203 mnBits = nNewBitCount;
204 mnBytesPerRow = rSourceBitmap.mnBytesPerRow;
205 mnWidth = rSourceBitmap.mnWidth;
206 mnHeight = rSourceBitmap.mnHeight;
207 maPalette = rSourceBitmap.maPalette;
208 // execute any pending operations on the source bitmap
209 maTexture = rSourceBitmap.GetTexture();
210 mbDirtyTexture = false;
212 // be careful here, we are share & reference-count the
213 // mpUserBuffer, BUT this Create() is called from
214 // Bitmap::ImplMakeUnique().
215 // Consequently, there might be cases when this needs to be made
216 // unique later (when we don't do that right away here), like when
217 // using the BitmapWriteAccess.
218 mpUserBuffer = rSourceBitmap.mpUserBuffer;
220 return true;
222 return false;
225 bool OpenGLSalBitmap::Create( const css::uno::Reference< css::rendering::XBitmapCanvas >& /*xBitmapCanvas*/, Size& /*rSize*/, bool /*bMask*/ )
227 // TODO Is this method needed?
228 return false;
231 OpenGLTexture& OpenGLSalBitmap::GetTexture() const
233 OpenGLSalBitmap* pThis = const_cast<OpenGLSalBitmap*>(this);
234 if( !maTexture || mbDirtyTexture )
235 pThis->CreateTexture();
236 VCL_GL_INFO( "Got texture " << maTexture.Id() );
237 return pThis->maTexture;
240 void OpenGLSalBitmap::Destroy()
242 OpenGLZone aZone;
244 VCL_GL_INFO("Destroy OpenGLSalBitmap texture:" << maTexture.Id());
245 maTexture = OpenGLTexture();
246 mpUserBuffer.reset();
249 bool OpenGLSalBitmap::AllocateUserData()
251 VCL_GL_INFO( "OpenGLSalBitmap::AllocateUserData" );
253 if( mnWidth && mnHeight )
255 mnBytesPerRow = lclBytesPerRow(mnBits, mnWidth);
258 bool alloc = false;
259 if (mnBytesPerRow != 0 && mnHeight &&
260 mnBytesPerRow <= std::numeric_limits<sal_uInt32>::max() / mnHeight)
264 size_t nToAllocate = static_cast<sal_uInt32>(mnBytesPerRow) * mnHeight;
265 #if OSL_DEBUG_LEVEL > 0
266 nToAllocate += sizeof(CANARY);
267 #endif
268 mpUserBuffer = o3tl::make_shared_array<sal_uInt8>(nToAllocate);
269 #if OSL_DEBUG_LEVEL > 0
270 memcpy(mpUserBuffer.get() + nToAllocate - sizeof(CANARY),
271 CANARY, sizeof(CANARY));
272 #endif
273 alloc = true;
275 catch (const std::bad_alloc &) {}
277 if (!alloc)
279 SAL_WARN("vcl.opengl", "bad alloc " << mnBytesPerRow << "x" << mnHeight);
280 mpUserBuffer.reset();
281 mnBytesPerRow = 0;
283 #ifdef DBG_UTIL
284 else
286 for (size_t i = 0; i < size_t(mnBytesPerRow * mnHeight); i++)
287 mpUserBuffer.get()[i] = (i & 0xFF);
289 #endif
291 return mpUserBuffer.get() != nullptr;
294 namespace {
296 class ImplPixelFormat
298 protected:
299 sal_uInt8* mpData;
300 public:
301 static ImplPixelFormat* GetFormat( sal_uInt16 nBits, const BitmapPalette& rPalette );
303 virtual void StartLine( sal_uInt8* pLine ) { mpData = pLine; }
304 virtual const BitmapColor& ReadPixel() = 0;
305 virtual ~ImplPixelFormat() { }
308 class ImplPixelFormat8 : public ImplPixelFormat
310 private:
311 const BitmapPalette& mrPalette;
313 public:
314 explicit ImplPixelFormat8( const BitmapPalette& rPalette )
315 : mrPalette( rPalette )
318 virtual const BitmapColor& ReadPixel() override
320 assert( mrPalette.GetEntryCount() > *mpData );
321 return mrPalette[ *mpData++ ];
325 class ImplPixelFormat4 : public ImplPixelFormat
327 private:
328 const BitmapPalette& mrPalette;
329 sal_uInt32 mnX;
330 sal_uInt32 mnShift;
332 public:
333 explicit ImplPixelFormat4( const BitmapPalette& rPalette )
334 : mrPalette( rPalette )
335 , mnX(0)
336 , mnShift(4)
339 virtual void StartLine( sal_uInt8* pLine ) override
341 mpData = pLine;
342 mnX = 0;
343 mnShift = 4;
345 virtual const BitmapColor& ReadPixel() override
347 sal_uInt32 nIdx = ( mpData[mnX >> 1] >> mnShift) & 0x0f;
348 assert( mrPalette.GetEntryCount() > nIdx );
349 const BitmapColor& rColor = mrPalette[nIdx];
350 mnX++;
351 mnShift ^= 4;
352 return rColor;
356 class ImplPixelFormat1 : public ImplPixelFormat
358 private:
359 const BitmapPalette& mrPalette;
360 sal_uInt32 mnX;
362 public:
363 explicit ImplPixelFormat1( const BitmapPalette& rPalette )
364 : mrPalette(rPalette)
365 , mnX(0)
368 virtual void StartLine( sal_uInt8* pLine ) override
370 mpData = pLine;
371 mnX = 0;
373 virtual const BitmapColor& ReadPixel() override
375 const BitmapColor& rColor = mrPalette[ (mpData[mnX >> 3 ] >> ( 7 - ( mnX & 7 ) )) & 1];
376 mnX++;
377 return rColor;
381 ImplPixelFormat* ImplPixelFormat::GetFormat( sal_uInt16 nBits, const BitmapPalette& rPalette )
383 switch( nBits )
385 case 1: return new ImplPixelFormat1( rPalette );
386 case 4: return new ImplPixelFormat4( rPalette );
387 case 8: return new ImplPixelFormat8( rPalette );
390 return nullptr;
393 void lclInstantiateTexture(OpenGLTexture& rTexture, const int nWidth, const int nHeight,
394 const GLenum nFormat, const GLenum nType, sal_uInt8* pData)
396 if (nWidth == nHeight)
398 TextureAtlasVector &sTextureAtlases = *gTextureAtlases.get();
399 if (sTextureAtlases.empty())
401 sTextureAtlases.push_back(o3tl::make_unique<FixedTextureAtlasManager>(8, 8, 16));
402 sTextureAtlases.push_back(o3tl::make_unique<FixedTextureAtlasManager>(8, 8, 24));
403 sTextureAtlases.push_back(o3tl::make_unique<FixedTextureAtlasManager>(8, 8, 32));
404 sTextureAtlases.push_back(o3tl::make_unique<FixedTextureAtlasManager>(8, 8, 48));
405 sTextureAtlases.push_back(o3tl::make_unique<FixedTextureAtlasManager>(8, 8, 64));
407 for (std::unique_ptr<FixedTextureAtlasManager> & pTextureAtlas : sTextureAtlases)
409 if (nWidth == pTextureAtlas->GetSubtextureSize())
411 rTexture = pTextureAtlas->InsertBuffer(nWidth, nHeight, nFormat, nType, pData);
412 return;
416 rTexture = OpenGLTexture (nWidth, nHeight, nFormat, nType, pData);
419 // Write color information for 1 and 4 bit palette bitmap scanlines.
420 class ScanlineWriter
422 BitmapPalette& maPalette;
423 sal_uInt8 mnColorsPerByte; // number of colors that are stored in one byte
424 sal_uInt8 mnColorBitSize; // number of bits a color takes
425 sal_uInt8 mnColorBitMask; // bit mask used to isolate the color
426 sal_uInt8* mpCurrentScanline;
427 long mnX;
429 public:
430 ScanlineWriter(BitmapPalette& aPalette, sal_Int8 nColorsPerByte)
431 : maPalette(aPalette)
432 , mnColorsPerByte(nColorsPerByte)
433 , 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)
434 , mnColorBitMask((1 << mnColorBitSize) - 1) // calculate the bit mask from the bit size
435 , mpCurrentScanline(nullptr)
436 , mnX(0)
439 void writeRGB(sal_uInt8 nR, sal_uInt8 nG, sal_uInt8 nB)
441 // calculate to which index we will write
442 long nScanlineIndex = mnX / mnColorsPerByte;
444 // calculate the number of shifts to get the color information to the right place
445 long nShift = (8 - mnColorBitSize) - ((mnX % mnColorsPerByte) * mnColorBitSize);
447 sal_uInt16 nColorIndex = maPalette.GetBestIndex(BitmapColor(nR, nG, nB));
448 mpCurrentScanline[nScanlineIndex] &= ~(mnColorBitMask << nShift); // clear
449 mpCurrentScanline[nScanlineIndex] |= (nColorIndex & mnColorBitMask) << nShift; // set
450 mnX++;
453 void nextLine(sal_uInt8* pScanline)
455 mnX = 0;
456 mpCurrentScanline = pScanline;
460 } // end anonymous namespace
462 Size OpenGLSalBitmap::GetSize() const
464 return Size(mnWidth, mnHeight);
467 GLuint OpenGLSalBitmap::CreateTexture()
469 VCL_GL_INFO( "::CreateTexture bits: " << mnBits);
470 GLenum nFormat = GL_RGBA;
471 GLenum nType = GL_UNSIGNED_BYTE;
472 sal_uInt8* pData( nullptr );
473 bool bAllocated( false );
475 if (mpUserBuffer.get() != nullptr)
477 if( mnBits == 16 || mnBits == 24 || mnBits == 32 )
479 // no conversion needed for truecolor
480 pData = mpUserBuffer.get();
482 determineTextureFormat(mnBits, nFormat, nType);
484 else if( mnBits == 8 && maPalette.IsGreyPalette() )
486 // no conversion needed for grayscale
487 pData = mpUserBuffer.get();
488 nFormat = GL_LUMINANCE;
489 nType = GL_UNSIGNED_BYTE;
491 else
493 VCL_GL_INFO( "::CreateTexture - convert from " << mnBits << " to 24 bits" );
495 // convert to 24 bits RGB using palette
496 pData = new sal_uInt8[mnHeight * mnWidth * 3];
497 bAllocated = true;
498 determineTextureFormat(24, nFormat, nType);
500 std::unique_ptr<ImplPixelFormat> pSrcFormat(ImplPixelFormat::GetFormat(mnBits, maPalette));
502 sal_uInt8* pSrcData = mpUserBuffer.get();
503 sal_uInt8* pDstData = pData;
505 sal_uInt32 nY = mnHeight;
506 while( nY-- )
508 pSrcFormat->StartLine( pSrcData );
510 sal_uInt32 nX = mnWidth;
511 if (nFormat == GL_BGR)
513 while( nX-- )
515 const BitmapColor& c = pSrcFormat->ReadPixel();
516 *pDstData++ = c.GetBlue();
517 *pDstData++ = c.GetGreen();
518 *pDstData++ = c.GetRed();
521 else // RGB
523 while( nX-- )
525 const BitmapColor& c = pSrcFormat->ReadPixel();
526 *pDstData++ = c.GetRed();
527 *pDstData++ = c.GetGreen();
528 *pDstData++ = c.GetBlue();
532 pSrcData += mnBytesPerRow;
537 OpenGLVCLContextZone aContextZone;
539 lclInstantiateTexture(maTexture, mnWidth, mnHeight, nFormat, nType, pData);
541 VCL_GL_INFO("Created texture " << maTexture.Id() << " bits: " << mnBits);
543 if( bAllocated )
544 delete[] pData;
546 mbDirtyTexture = false;
548 CHECK_GL_ERROR();
549 return maTexture.Id();
552 bool OpenGLSalBitmap::ReadTexture()
554 sal_uInt8* pData = mpUserBuffer.get();
556 GLenum nFormat = GL_RGBA;
557 GLenum nType = GL_UNSIGNED_BYTE;
559 VCL_GL_INFO( "::ReadTexture " << mnWidth << "x" << mnHeight << " bits: " << mnBits);
561 if( pData == nullptr )
562 return false;
564 OpenGLVCLContextZone aContextZone;
566 rtl::Reference<OpenGLContext> xContext = OpenGLContext::getVCLContext();
567 xContext->state().scissor().disable();
568 xContext->state().stencil().disable();
570 if (mnBits == 8 || mnBits == 16 || mnBits == 24 || mnBits == 32)
572 determineTextureFormat(mnBits, nFormat, nType);
574 #if OSL_DEBUG_LEVEL > 0
575 // help valgrind & drmemory rescue us - touch last and first bits.
576 pData[0] = 0;
577 pData[mnBits/8*mnWidth*mnHeight-1] = 0;
578 // if this fails we can read too much into pData
579 assert(mnWidth == maTexture.GetWidth() &&
580 mnHeight == maTexture.GetHeight());
581 #endif
583 maTexture.Read(nFormat, nType, pData);
585 #if OSL_DEBUG_LEVEL > 0
586 // If we read over the end of pData we have a real hidden memory
587 // corruption problem !
588 size_t nCanary = static_cast<sal_uInt32>(mnBytesPerRow) * mnHeight;
589 assert(!memcmp(pData + nCanary, CANARY, sizeof (CANARY)));
590 #endif
591 return true;
593 else if (mnBits == 1 || mnBits == 4)
594 { // convert buffers from 24-bit RGB to 1 or 4-bit buffer
595 std::vector<sal_uInt8> aBuffer(mnWidth * mnHeight * 3);
597 sal_uInt8* pBuffer = aBuffer.data();
598 determineTextureFormat(24, nFormat, nType);
599 maTexture.Read(nFormat, nType, pBuffer);
600 sal_uInt16 nSourceBytesPerRow = lclBytesPerRow(24, mnWidth);
602 std::unique_ptr<ScanlineWriter> pWriter;
603 switch(mnBits)
605 case 1:
606 pWriter.reset(new ScanlineWriter(maPalette, 8));
607 break;
608 case 4:
609 default:
610 pWriter.reset(new ScanlineWriter(maPalette, 2));
611 break;
614 for (int y = 0; y < mnHeight; ++y)
616 sal_uInt8* pSource = &pBuffer[y * nSourceBytesPerRow];
617 sal_uInt8* pDestination = &pData[y * mnBytesPerRow];
619 pWriter->nextLine(pDestination);
621 for (int x = 0; x < mnWidth; ++x)
623 // read source
624 sal_uInt8 nR = *pSource++;
625 sal_uInt8 nG = *pSource++;
626 sal_uInt8 nB = *pSource++;
628 pWriter->writeRGB(nR, nG, nB);
631 return true;
634 SAL_WARN("vcl.opengl", "::ReadTexture - tx:" << maTexture.Id() << " @ "
635 << mnWidth << "x" << mnHeight << "- unimplemented bit depth: "
636 << mnBits);
637 return false;
640 sal_uInt16 OpenGLSalBitmap::GetBitCount() const
642 return mnBits;
645 bool OpenGLSalBitmap::calcChecksumGL(OpenGLTexture& rInputTexture, ChecksumType& rChecksum) const
647 OUString FragShader("areaHashCRC64TFragmentShader");
649 rtl::Reference< OpenGLContext > xContext = OpenGLContext::getVCLContext();
650 xContext->state().scissor().disable();
651 xContext->state().stencil().disable();
653 static vcl::DeleteOnDeinit<OpenGLTexture> gCRCTableTexture(
654 new OpenGLTexture(512, 1, GL_RGBA, GL_UNSIGNED_BYTE,
655 vcl_get_crc64_table()));
656 OpenGLTexture &rCRCTableTexture = *gCRCTableTexture.get();
658 // First Pass
660 int nWidth = rInputTexture.GetWidth();
661 int nHeight = rInputTexture.GetHeight();
663 OpenGLProgram* pProgram = xContext->UseProgram("textureVertexShader", FragShader);
664 if (pProgram == nullptr)
665 return false;
667 int nNewWidth = ceil( nWidth / 4.0 );
668 int nNewHeight = ceil( nHeight / 4.0 );
670 OpenGLTexture aFirstPassTexture = OpenGLTexture(nNewWidth, nNewHeight);
671 OpenGLFramebuffer* pFramebuffer = xContext->AcquireFramebuffer(aFirstPassTexture);
673 pProgram->SetUniform1f( "xstep", 1.0 / mnWidth );
674 pProgram->SetUniform1f( "ystep", 1.0 / mnHeight );
676 pProgram->SetTexture("crc_table", rCRCTableTexture);
677 pProgram->SetTexture("sampler", rInputTexture);
678 pProgram->DrawTexture(rInputTexture);
679 pProgram->Clean();
681 OpenGLContext::ReleaseFramebuffer(pFramebuffer);
683 CHECK_GL_ERROR();
685 // Second Pass
687 nWidth = aFirstPassTexture.GetWidth();
688 nHeight = aFirstPassTexture.GetHeight();
690 pProgram = xContext->UseProgram("textureVertexShader", FragShader);
691 if (pProgram == nullptr)
692 return false;
694 nNewWidth = ceil( nWidth / 4.0 );
695 nNewHeight = ceil( nHeight / 4.0 );
697 OpenGLTexture aSecondPassTexture = OpenGLTexture(nNewWidth, nNewHeight);
698 pFramebuffer = xContext->AcquireFramebuffer(aSecondPassTexture);
700 pProgram->SetUniform1f( "xstep", 1.0 / mnWidth );
701 pProgram->SetUniform1f( "ystep", 1.0 / mnHeight );
703 pProgram->SetTexture("crc_table", rCRCTableTexture);
704 pProgram->SetTexture("sampler", aFirstPassTexture);
705 pProgram->DrawTexture(aFirstPassTexture);
706 pProgram->Clean();
708 OpenGLContext::ReleaseFramebuffer(pFramebuffer);
710 CHECK_GL_ERROR();
712 // Final CRC on CPU
713 OpenGLTexture& aFinalTexture = aSecondPassTexture;
714 std::vector<sal_uInt8> aBuf( aFinalTexture.GetWidth() * aFinalTexture.GetHeight() * 4 );
715 aFinalTexture.Read(GL_RGBA, GL_UNSIGNED_BYTE, aBuf.data());
717 ChecksumType nCrc = vcl_get_checksum(0, aBuf.data(), aBuf.size());
719 rChecksum = nCrc;
720 return true;
723 void OpenGLSalBitmap::updateChecksum() const
725 if (mbChecksumValid)
726 return;
728 if( (mnWidth * mnHeight) < (1024*768) || mnWidth < 128 || mnHeight < 128 )
730 SalBitmap::updateChecksum();
731 return;
734 OpenGLSalBitmap* pThis = const_cast<OpenGLSalBitmap*>(this);
736 OpenGLVCLContextZone aContextZone;
737 OpenGLTexture& rInputTexture = GetTexture();
738 pThis->mbChecksumValid = calcChecksumGL(rInputTexture, pThis->mnChecksum);
739 if (!pThis->mbChecksumValid)
740 SalBitmap::updateChecksum();
743 BitmapBuffer* OpenGLSalBitmap::AcquireBuffer( BitmapAccessMode nMode )
745 OpenGLVCLContextZone aContextZone;
747 if( nMode != BitmapAccessMode::Info )
749 if (!mpUserBuffer.get())
751 if( !AllocateUserData() )
752 return nullptr;
753 if( maTexture && !ReadTexture() )
754 return nullptr;
758 // mpUserBuffer must be unique when we are doing the write access
759 if (nMode == BitmapAccessMode::Write && mpUserBuffer && !mpUserBuffer.unique())
761 std::shared_ptr<sal_uInt8> aBuffer(mpUserBuffer);
763 mpUserBuffer.reset();
764 AllocateUserData();
765 memcpy(mpUserBuffer.get(), aBuffer.get(), static_cast<sal_uInt32>(mnBytesPerRow) * mnHeight);
768 BitmapBuffer* pBuffer = new BitmapBuffer;
769 pBuffer->mnWidth = mnWidth;
770 pBuffer->mnHeight = mnHeight;
771 pBuffer->maPalette = maPalette;
772 pBuffer->mnScanlineSize = mnBytesPerRow;
773 pBuffer->mpBits = mpUserBuffer.get();
774 pBuffer->mnBitCount = mnBits;
776 switch (mnBits)
778 case 1:
779 pBuffer->mnFormat = ScanlineFormat::N1BitMsbPal;
780 break;
781 case 4:
782 pBuffer->mnFormat = ScanlineFormat::N4BitMsnPal;
783 break;
784 case 8:
785 pBuffer->mnFormat = ScanlineFormat::N8BitPal;
786 break;
787 case 16:
789 pBuffer->mnFormat = ScanlineFormat::N16BitTcMsbMask;
790 ColorMaskElement aRedMask(0x0000f800);
791 aRedMask.CalcMaskShift();
792 ColorMaskElement aGreenMask(0x000007e0);
793 aGreenMask.CalcMaskShift();
794 ColorMaskElement aBlueMask(0x0000001f);
795 aBlueMask.CalcMaskShift();
796 pBuffer->maColorMask = ColorMask(aRedMask, aGreenMask, aBlueMask);
797 break;
799 case 24:
801 pBuffer->mnFormat = ScanlineFormat::N24BitTcRgb;
802 break;
804 case 32:
806 pBuffer->mnFormat = ScanlineFormat::N32BitTcRgba;
807 ColorMaskElement aRedMask(0xff000000);
808 aRedMask.CalcMaskShift();
809 ColorMaskElement aGreenMask(0x00ff0000);
810 aGreenMask.CalcMaskShift();
811 ColorMaskElement aBlueMask(0x0000ff00);
812 aBlueMask.CalcMaskShift();
813 pBuffer->maColorMask = ColorMask(aRedMask, aGreenMask, aBlueMask);
814 break;
818 return pBuffer;
821 void OpenGLSalBitmap::ReleaseBuffer( BitmapBuffer* pBuffer, BitmapAccessMode nMode )
823 OpenGLVCLContextZone aContextZone;
825 if( nMode == BitmapAccessMode::Write )
827 maTexture = OpenGLTexture();
828 mbDirtyTexture = true;
829 mbChecksumValid = false;
831 // The palette is modified on read during the BitmapWriteAccess,
832 // but of course, often it is not modified; interesting.
833 maPalette = pBuffer->maPalette;
835 // Are there any more ground movements underneath us ?
836 assert( pBuffer->mnWidth == mnWidth );
837 assert( pBuffer->mnHeight == mnHeight );
838 assert( pBuffer->mnBitCount == mnBits );
840 delete pBuffer;
843 bool OpenGLSalBitmap::GetSystemData( BitmapSystemData& /*rData*/ )
845 SAL_WARN( "vcl.opengl", "*** NOT IMPLEMENTED *** GetSystemData" );
846 #if 0
847 // TODO Implement for ANDROID/OSX/IOS/WIN32
848 X11SalBitmap rBitmap;
849 BitmapBuffer* pBuffer;
851 rBitmap.Create( GetSize(), mnBits, maPalette );
852 pBuffer = rBitmap.AcquireBuffer( false );
853 if( pBuffer == NULL )
854 return false;
856 if (!mpUserBuffer.get())
858 if( !AllocateUserData() || !ReadTexture() )
860 rBitmap.ReleaseBuffer( pBuffer, false );
861 return false;
865 // TODO Might be more efficient to add a static method to SalBitmap
866 // to get system data from a buffer
867 memcpy( pBuffer->mpBits, mpUserBuffer.get(), mnBytesPerRow * mnHeight );
869 rBitmap.ReleaseBuffer( pBuffer, false );
870 return rBitmap.GetSystemData( rData );
871 #else
872 return false;
873 #endif
876 bool OpenGLSalBitmap::Replace( const Color& rSearchColor, const Color& rReplaceColor, sal_uLong nTol )
878 VCL_GL_INFO("::Replace");
880 OpenGLZone aZone;
881 rtl::Reference<OpenGLContext> xContext = OpenGLContext::getVCLContext();
882 xContext->state().scissor().disable();
883 xContext->state().stencil().disable();
885 OpenGLFramebuffer* pFramebuffer;
886 OpenGLProgram* pProgram;
888 GetTexture();
889 pProgram = xContext->UseProgram( "textureVertexShader",
890 "replaceColorFragmentShader" );
891 if( !pProgram )
892 return false;
894 OpenGLTexture aNewTex = OpenGLTexture( mnWidth, mnHeight );
895 pFramebuffer = xContext->AcquireFramebuffer( aNewTex );
897 pProgram->SetTexture( "sampler", maTexture );
898 pProgram->SetColor( "search_color", rSearchColor );
899 pProgram->SetColor( "replace_color", rReplaceColor );
900 pProgram->SetUniform1f( "epsilon", nTol / 255.0f );
901 pProgram->DrawTexture( maTexture );
902 pProgram->Clean();
904 OpenGLContext::ReleaseFramebuffer( pFramebuffer );
905 maTexture = aNewTex;
907 CHECK_GL_ERROR();
908 return true;
911 // Convert texture to greyscale and adjust bitmap metadata
912 bool OpenGLSalBitmap::ConvertToGreyscale()
914 VCL_GL_INFO("::ConvertToGreyscale");
916 // avoid re-converting to 8bits.
917 if ( mnBits == 8 && maPalette == Bitmap::GetGreyPalette(256) )
918 return false;
920 OpenGLZone aZone;
921 rtl::Reference<OpenGLContext> xContext = OpenGLContext::getVCLContext();
922 xContext->state().scissor().disable();
923 xContext->state().stencil().disable();
925 OpenGLFramebuffer* pFramebuffer;
926 OpenGLProgram* pProgram;
928 GetTexture();
929 pProgram = xContext->UseProgram("textureVertexShader", "greyscaleFragmentShader");
931 if (!pProgram)
932 return false;
934 OpenGLTexture aNewTex(mnWidth, mnHeight);
935 pFramebuffer = xContext->AcquireFramebuffer(aNewTex);
936 pProgram->SetTexture("sampler", maTexture);
937 pProgram->DrawTexture(maTexture);
938 pProgram->Clean();
940 OpenGLContext::ReleaseFramebuffer( pFramebuffer );
941 maTexture = aNewTex;
942 mnBits = 8;
943 maPalette = Bitmap::GetGreyPalette(256);
945 // AllocateUserData will handle the rest.
946 mpUserBuffer.reset();
947 mbDirtyTexture = false;
949 CHECK_GL_ERROR();
950 return true;
953 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */