LibreOffice Module canvas (master)  1
dx_vcltools.cxx
Go to the documentation of this file.
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
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/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
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 .
18  */
19 
20 #include <sal/config.h>
21 
23 #include <com/sun/star/lang/XServiceInfo.hpp>
24 #include <com/sun/star/rendering/XIntegerBitmap.hpp>
25 #include <tools/diagnose_ex.h>
26 #include <vcl/bitmap.hxx>
27 #include <vcl/bitmapex.hxx>
28 #include <vcl/BitmapReadAccess.hxx>
29 #include <vcl/canvastools.hxx>
30 
31 #include "dx_impltools.hxx"
32 #include "dx_vcltools.hxx"
33 
34 using namespace ::com::sun::star;
35 
36 namespace dxcanvas::tools
37 {
38  namespace
39  {
41  sal_Int32 calcDIBColorCount( const BITMAPINFOHEADER& rBIH )
42  {
43  if( rBIH.biSize != sizeof( BITMAPCOREHEADER ) )
44  {
45  if( rBIH.biBitCount <= 8 )
46  {
47  if( rBIH.biClrUsed )
48  return rBIH.biClrUsed;
49  else
50  return 1 << rBIH.biBitCount;
51  }
52  }
53  else
54  {
55  BITMAPCOREHEADER const * pCoreHeader = reinterpret_cast<BITMAPCOREHEADER const *>(&rBIH);
56 
57  if( pCoreHeader->bcBitCount <= 8 )
58  return 1 << pCoreHeader->bcBitCount;
59  }
60 
61  return 0; // nothing known
62  }
63 
65  bool drawDIBits( const std::shared_ptr< Gdiplus::Graphics >& rGraphics,
66  const void* hDIB )
67  {
68  bool bRet( false );
69  BitmapSharedPtr pBitmap;
70 
71  const BITMAPINFO* pBI = static_cast<BITMAPINFO*>(GlobalLock( const_cast<void *>(hDIB) ));
72 
73  if( pBI )
74  {
75  const BYTE* pBits = reinterpret_cast<BYTE const *>(pBI) + pBI->bmiHeader.biSize +
76  calcDIBColorCount( pBI->bmiHeader ) * sizeof( RGBQUAD );
77 
78  // forward to outsourced GDI+ rendering method
79  // (header clashes)
80  bRet = tools::drawDIBits( rGraphics, *pBI, pBits );
81 
82  GlobalUnlock( const_cast<void *>(hDIB) );
83  }
84 
85  return bRet;
86  }
87 
94  bool drawVCLBitmap( const std::shared_ptr< Gdiplus::Graphics >& rGraphics,
95  ::Bitmap& rBmp )
96  {
97  BitmapSystemData aBmpSysData;
98 
99  if( !rBmp.GetSystemData( aBmpSysData ) ||
100  !aBmpSysData.pDIB )
101  {
102  // first of all, ensure that Bitmap contains a DIB, by
103  // acquiring a read access
104  BitmapReadAccess* pReadAcc = rBmp.AcquireReadAccess();
105 
106  // TODO(P2): Acquiring a read access can actually
107  // force a read from VRAM, thus, avoiding this
108  // step somehow will increase performance
109  // here.
110  if( pReadAcc )
111  {
112  // try again: now, WinSalBitmap must have
113  // generated a DIB
114  if( rBmp.GetSystemData( aBmpSysData ) &&
115  aBmpSysData.pDIB )
116  {
117  return drawDIBits( rGraphics,
118  aBmpSysData.pDIB );
119  }
120 
121  Bitmap::ReleaseAccess( pReadAcc );
122  }
123  }
124  else
125  {
126  return drawDIBits( rGraphics,
127  aBmpSysData.pDIB );
128  }
129 
130  // failed to generate DIBits from vcl bitmap
131  return false;
132  }
133 
136  RawRGBABitmap bitmapFromVCLBitmapEx( const ::BitmapEx& rBmpEx )
137  {
138  // TODO(P2): Avoid temporary bitmap generation, maybe
139  // even ensure that created DIBs are copied back to
140  // BmpEx (currently, every AcquireReadAccess() will
141  // make the local bitmap copy unique, effectively
142  // duplicating the memory used)
143 
144  ENSURE_OR_THROW( rBmpEx.IsTransparent(),
145  "::dxcanvas::tools::bitmapFromVCLBitmapEx(): "
146  "BmpEx not transparent" );
147 
148  // convert transparent bitmap to 32bit RGBA
149  // ========================================
150 
151  const ::Size aBmpSize( rBmpEx.GetSizePixel() );
152 
153  RawRGBABitmap aBmpData;
154  aBmpData.mnWidth = aBmpSize.Width();
155  aBmpData.mnHeight = aBmpSize.Height();
156  aBmpData.maBitmapData.resize(4*aBmpData.mnWidth*aBmpData.mnHeight);
157 
158  Bitmap aBitmap( rBmpEx.GetBitmap() );
159 
160  Bitmap::ScopedReadAccess pReadAccess( aBitmap );
161 
162  const sal_Int32 nWidth( aBmpSize.Width() );
163  const sal_Int32 nHeight( aBmpSize.Height() );
164 
165  ENSURE_OR_THROW( pReadAccess.get() != nullptr,
166  "::dxcanvas::tools::bitmapFromVCLBitmapEx(): "
167  "Unable to acquire read access to bitmap" );
168 
169  if( rBmpEx.IsAlpha() || rBmpEx.GetMask().GetBitCount() == 8 )
170  {
171  Bitmap aAlpha( rBmpEx.IsAlpha() ? rBmpEx.GetAlpha().GetBitmap() : rBmpEx.GetMask());
172 
173  Bitmap::ScopedReadAccess pAlphaReadAccess( aAlpha );
174 
175  // By convention, the access buffer always has
176  // one of the following formats:
177 
178  // ScanlineFormat::N1BitMsbPal
179  // ScanlineFormat::N4BitMsnPal
180  // ScanlineFormat::N8BitPal
181  // ScanlineFormat::N24BitTcBgr
182  // ScanlineFormat::N32BitTcMask
183 
184  // and is always ScanlineFormat::BottomUp
185 
186  // This is the way
187  // WinSalBitmap::AcquireBuffer() sets up the
188  // buffer
189 
190  ENSURE_OR_THROW( pAlphaReadAccess.get() != nullptr,
191  "::dxcanvas::tools::bitmapFromVCLBitmapEx(): "
192  "Unable to acquire read access to alpha" );
193 
194  ENSURE_OR_THROW( pAlphaReadAccess->GetScanlineFormat() == ScanlineFormat::N8BitPal,
195  "::dxcanvas::tools::bitmapFromVCLBitmapEx(): "
196  "Unsupported alpha scanline format" );
197 
198  BitmapColor aCol;
199  sal_uInt8* pCurrOutput(aBmpData.maBitmapData.data());
200  int x, y;
201 
202  for( y=0; y<nHeight; ++y )
203  {
204  switch( pReadAccess->GetScanlineFormat() )
205  {
206  case ScanlineFormat::N8BitPal:
207  {
208  Scanline pScan = pReadAccess->GetScanline( y );
209  Scanline pAScan = pAlphaReadAccess->GetScanline( y );
210 
211  for( x=0; x<nWidth; ++x )
212  {
213  aCol = pReadAccess->GetPaletteColor( *pScan++ );
214 
215  *pCurrOutput++ = aCol.GetBlue();
216  *pCurrOutput++ = aCol.GetGreen();
217  *pCurrOutput++ = aCol.GetRed();
218 
219  // out notion of alpha is
220  // different from the rest
221  // of the world's
222  *pCurrOutput++ = 255 - static_cast<BYTE>(*pAScan++);
223  }
224  }
225  break;
226 
227  case ScanlineFormat::N24BitTcBgr:
228  {
229  Scanline pScan = pReadAccess->GetScanline( y );
230  Scanline pAScan = pAlphaReadAccess->GetScanline( y );
231 
232  for( x=0; x<nWidth; ++x )
233  {
234  // store as RGBA
235  *pCurrOutput++ = *pScan++;
236  *pCurrOutput++ = *pScan++;
237  *pCurrOutput++ = *pScan++;
238 
239  // out notion of alpha is
240  // different from the rest
241  // of the world's
242  *pCurrOutput++ = 255 - static_cast<BYTE>(*pAScan++);
243  }
244  }
245  break;
246 
247  // TODO(P2): Might be advantageous
248  // to hand-formulate the following
249  // formats, too.
250  case ScanlineFormat::N1BitMsbPal:
251  case ScanlineFormat::N4BitMsnPal:
252  case ScanlineFormat::N32BitTcMask:
253  {
254  Scanline pAScan = pAlphaReadAccess->GetScanline( y );
255 
256  // using fallback for those
257  // seldom formats
258  for( x=0; x<nWidth; ++x )
259  {
260  // yes. x and y are swapped on Get/SetPixel
261  aCol = pReadAccess->GetColor(y,x);
262 
263  *pCurrOutput++ = aCol.GetBlue();
264  *pCurrOutput++ = aCol.GetGreen();
265  *pCurrOutput++ = aCol.GetRed();
266 
267  // out notion of alpha is
268  // different from the rest
269  // of the world's
270  *pCurrOutput++ = 255 - static_cast<BYTE>(*pAScan++);
271  }
272  }
273  break;
274 
275  case ScanlineFormat::N1BitLsbPal:
276  case ScanlineFormat::N4BitLsnPal:
277  case ScanlineFormat::N24BitTcRgb:
278  case ScanlineFormat::N32BitTcAbgr:
279  case ScanlineFormat::N32BitTcArgb:
280  case ScanlineFormat::N32BitTcBgra:
281  case ScanlineFormat::N32BitTcRgba:
282  default:
283  ENSURE_OR_THROW( false,
284  "::dxcanvas::tools::bitmapFromVCLBitmapEx(): "
285  "Unexpected scanline format - has "
286  "WinSalBitmap::AcquireBuffer() changed?" );
287  }
288  }
289  }
290  else
291  {
292  Bitmap aMask( rBmpEx.GetMask() );
293 
294  Bitmap::ScopedReadAccess pMaskReadAccess( aMask );
295 
296  // By convention, the access buffer always has
297  // one of the following formats:
298 
299  // ScanlineFormat::N1BitMsbPal
300  // ScanlineFormat::N4BitMsnPal
301  // ScanlineFormat::N8BitPal
302  // ScanlineFormat::N24BitTcBgr
303  // ScanlineFormat::N32BitTcMask
304 
305  // and is always ScanlineFormat::BottomUp
306 
307  // This is the way
308  // WinSalBitmap::AcquireBuffer() sets up the
309  // buffer
310 
311  ENSURE_OR_THROW( pMaskReadAccess.get() != nullptr,
312  "::dxcanvas::tools::bitmapFromVCLBitmapEx(): "
313  "Unable to acquire read access to mask" );
314 
315  ENSURE_OR_THROW( pMaskReadAccess->GetScanlineFormat() == ScanlineFormat::N1BitMsbPal,
316  "::dxcanvas::tools::bitmapFromVCLBitmapEx(): "
317  "Unsupported mask scanline format" );
318 
319  BitmapColor aCol;
320  int nCurrBit;
321  const int nMask( 1 );
322  const int nInitialBit(7);
323  sal_uInt8* pCurrOutput(aBmpData.maBitmapData.data());
324  int x, y;
325 
326  // mapping table, to get from mask index color to
327  // alpha value (which depends on the mask's palette)
328  sal_uInt8 aColorMap[2];
329 
330  const BitmapColor& rCol0( pMaskReadAccess->GetPaletteColor( 0 ) );
331  const BitmapColor& rCol1( pMaskReadAccess->GetPaletteColor( 1 ) );
332 
333  // shortcut for true luminance calculation
334  // (assumes that palette is grey-level). Note the
335  // swapped the indices here, to account for the
336  // fact that VCL's notion of alpha is inverted to
337  // the rest of the world's.
338  aColorMap[0] = rCol1.GetRed();
339  aColorMap[1] = rCol0.GetRed();
340 
341  for( y=0; y<nHeight; ++y )
342  {
343  switch( pReadAccess->GetScanlineFormat() )
344  {
345  case ScanlineFormat::N8BitPal:
346  {
347  Scanline pScan = pReadAccess->GetScanline( y );
348  Scanline pMScan = pMaskReadAccess->GetScanline( y );
349 
350  for( x=0, nCurrBit=nInitialBit; x<nWidth; ++x )
351  {
352  aCol = pReadAccess->GetPaletteColor( *pScan++ );
353 
354  *pCurrOutput++ = aCol.GetBlue();
355  *pCurrOutput++ = aCol.GetGreen();
356  *pCurrOutput++ = aCol.GetRed();
357 
358  *pCurrOutput++ = aColorMap[ (pMScan[ (x & ~7) >> 3 ] >> nCurrBit ) & nMask ];
359  nCurrBit = ((nCurrBit - 1) % 8) & 7;
360  }
361  }
362  break;
363 
364  case ScanlineFormat::N24BitTcBgr:
365  {
366  Scanline pScan = pReadAccess->GetScanline( y );
367  Scanline pMScan = pMaskReadAccess->GetScanline( y );
368 
369  for( x=0, nCurrBit=nInitialBit; x<nWidth; ++x )
370  {
371  // store as RGBA
372  *pCurrOutput++ = *pScan++;
373  *pCurrOutput++ = *pScan++;
374  *pCurrOutput++ = *pScan++;
375 
376  *pCurrOutput++ = aColorMap[ (pMScan[ (x & ~7) >> 3 ] >> nCurrBit ) & nMask ];
377  nCurrBit = ((nCurrBit - 1) % 8) & 7;
378  }
379  }
380  break;
381 
382  // TODO(P2): Might be advantageous
383  // to hand-formulate the following
384  // formats, too.
385  case ScanlineFormat::N1BitMsbPal:
386  case ScanlineFormat::N4BitMsnPal:
387  case ScanlineFormat::N32BitTcMask:
388  {
389  Scanline pMScan = pMaskReadAccess->GetScanline( y );
390 
391  // using fallback for those
392  // seldom formats
393  for( x=0, nCurrBit=nInitialBit; x<nWidth; ++x )
394  {
395  // yes. x and y are swapped on Get/SetPixel
396  aCol = pReadAccess->GetColor(y,x);
397 
398  // store as RGBA
399  *pCurrOutput++ = aCol.GetBlue();
400  *pCurrOutput++ = aCol.GetGreen();
401  *pCurrOutput++ = aCol.GetRed();
402 
403  *pCurrOutput++ = aColorMap[ (pMScan[ (x & ~7) >> 3 ] >> nCurrBit ) & nMask ];
404  nCurrBit = ((nCurrBit - 1) % 8) & 7;
405  }
406  }
407  break;
408 
409  case ScanlineFormat::N1BitLsbPal:
410  case ScanlineFormat::N4BitLsnPal:
411  case ScanlineFormat::N24BitTcRgb:
412  case ScanlineFormat::N32BitTcAbgr:
413  case ScanlineFormat::N32BitTcArgb:
414  case ScanlineFormat::N32BitTcBgra:
415  case ScanlineFormat::N32BitTcRgba:
416  default:
417  ENSURE_OR_THROW( false,
418  "::dxcanvas::tools::bitmapFromVCLBitmapEx(): "
419  "Unexpected scanline format - has "
420  "WinSalBitmap::AcquireBuffer() changed?" );
421  }
422  }
423  }
424 
425  return aBmpData;
426  }
427 
428  bool drawVCLBitmapEx( const std::shared_ptr< Gdiplus::Graphics >& rGraphics,
429  const ::BitmapEx& rBmpEx )
430  {
431  if( !rBmpEx.IsTransparent() )
432  {
433  Bitmap aBmp( rBmpEx.GetBitmap() );
434  return drawVCLBitmap( rGraphics, aBmp );
435  }
436  else
437  {
438  return drawRGBABits( rGraphics,
439  bitmapFromVCLBitmapEx( rBmpEx ) );
440  }
441  }
442  }
443 
444  bool drawVCLBitmapFromXBitmap( const std::shared_ptr< Gdiplus::Graphics >& rGraphics,
445  const uno::Reference< rendering::XBitmap >& xBitmap )
446  {
447  // TODO(F2): add support for floating point bitmap formats
448  uno::Reference< rendering::XIntegerReadOnlyBitmap > xIntBmp(
449  xBitmap, uno::UNO_QUERY );
450 
451  if( !xIntBmp.is() )
452  return false;
453 
455  if( !aBmpEx )
456  return false;
457 
458  return drawVCLBitmapEx( rGraphics, aBmpEx );
459  }
460 }
461 
462 
463 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
unsigned char BYTE
BitmapReadAccess * AcquireReadAccess()
bool drawDIBits(const std::shared_ptr< Gdiplus::Graphics > &rGraphics, const BITMAPINFO &rBI, const void *pBits)
::BitmapEx bitmapExFromXBitmap(const uno::Reference< rendering::XIntegerReadOnlyBitmap > &xInputBitmap)
float x
Definition: dx_9rm.cxx:190
float y
Definition: dx_9rm.cxx:190
bool drawRGBABits(const std::shared_ptr< Gdiplus::Graphics > &rGraphics, const RawRGBABitmap &rRawRGBAData)
sal_uInt8 * Scanline
std::shared_ptr< ::cppcanvas::Bitmap > BitmapSharedPtr
bool GetSystemData(BitmapSystemData &rData) const
static void ReleaseAccess(BitmapInfoAccess *pAccess)
Gdiplus::BitmapData aBmpData
#define ENSURE_OR_THROW(c, m)
unsigned char sal_uInt8
bool drawVCLBitmapFromXBitmap(const std::shared_ptr< Gdiplus::Graphics > &rGraphics, const uno::Reference< rendering::XBitmap > &xBitmap)