1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 
23 
24 // MARKER(update_precomp.py): autogen include statement, do not remove
25 #include "precompiled_canvas.hxx"
26 
27 #include <vcl/canvastools.hxx>
28 
29 #include <vcl/bitmap.hxx>
30 #include <vcl/bitmapex.hxx>
31 #include <vcl/bmpacc.hxx>
32 #include <tools/diagnose_ex.h>
33 
34 #include "dx_impltools.hxx"
35 #include <basegfx/numeric/ftools.hxx>
36 
37 #include <canvas/debug.hxx>
38 #include <canvas/verbosetrace.hxx>
39 
40 #include <com/sun/star/lang/XServiceInfo.hpp>
41 #include <com/sun/star/rendering/XIntegerBitmap.hpp>
42 
43 #include <boost/scoped_array.hpp>
44 
45 #include "dx_vcltools.hxx"
46 
47 using namespace ::com::sun::star;
48 
49 namespace dxcanvas
50 {
51     namespace tools
52     {
53         namespace
54         {
55             /// Calc number of colors in given BitmapInfoHeader
56 			sal_Int32 calcDIBColorCount( const BITMAPINFOHEADER& rBIH )
57             {
58                 if( rBIH.biSize != sizeof( BITMAPCOREHEADER ) )
59                 {
60                     if( rBIH.biBitCount <= 8 )
61                     {
62                         if( rBIH.biClrUsed )
63                             return rBIH.biClrUsed;
64                         else
65                             return 1L << rBIH.biBitCount;
66                     }
67                 }
68                 else
69                 {
70                     BITMAPCOREHEADER* pCoreHeader = (BITMAPCOREHEADER*)&rBIH;
71 
72                     if( pCoreHeader->bcBitCount <= 8 )
73                         return 1L << pCoreHeader->bcBitCount;
74                 }
75 
76                 return 0; // nothing known
77             }
78 
79             /// Draw DI bits to given Graphics
80             bool drawDIBits( const ::boost::shared_ptr< Gdiplus::Graphics >& rGraphics,
81                              const void* 									 hDIB )
82             {
83                 bool 			bRet( false );
84                 BitmapSharedPtr pBitmap;
85 
86                 const BITMAPINFO* pBI = (BITMAPINFO*)GlobalLock( (HGLOBAL)hDIB );
87 
88                 if( pBI )
89                 {
90                     const BITMAPINFOHEADER*	pBIH = (BITMAPINFOHEADER*)pBI;
91                     const BYTE*				pBits = (BYTE*) pBI + *(DWORD*)pBI +
92                         calcDIBColorCount( *pBIH ) * sizeof( RGBQUAD );
93 
94                     // forward to outsourced GDI+ rendering method
95                     // (header clashes)
96                     bRet = tools::drawDIBits( rGraphics, *pBI, (void*)pBits );
97 
98                     GlobalUnlock( (HGLOBAL)hDIB );
99                 }
100 
101                 return bRet;
102             }
103 
104             /** Draw VCL bitmap to given Graphics
105 
106             	@param rBmp
107                 Reference to bitmap. Might get modified, in such a way
108                 that it will hold a DIB after a successful function call.
109              */
110             bool drawVCLBitmap( const ::boost::shared_ptr< Gdiplus::Graphics >&	rGraphics,
111                                 ::Bitmap& 										rBmp )
112             {
113                 BitmapSystemData aBmpSysData;
114 
115                 if( !rBmp.GetSystemData( aBmpSysData ) ||
116                     !aBmpSysData.pDIB )
117                 {
118                     // first of all, ensure that Bitmap contains a DIB, by
119                     // acquiring a read access
120                     BitmapReadAccess* pReadAcc = rBmp.AcquireReadAccess();
121 
122                     // TODO(P2): Acquiring a read access can actually
123                     // force a read from VRAM, thus, avoiding this
124                     // step somehow will increase performance
125                     // here.
126                     if( pReadAcc )
127                     {
128                         // try again: now, WinSalBitmap must have
129                         // generated a DIB
130                         if( rBmp.GetSystemData( aBmpSysData ) &&
131                             aBmpSysData.pDIB )
132                         {
133                             return drawDIBits( rGraphics,
134                                                aBmpSysData.pDIB );
135                         }
136 
137                         rBmp.ReleaseAccess( pReadAcc );
138                     }
139                 }
140                 else
141                 {
142                     return drawDIBits( rGraphics,
143                                        aBmpSysData.pDIB );
144                 }
145 
146                 // failed to generate DIBits from vcl bitmap
147                 return false;
148             }
149 
150             /** Create a chunk of raw RGBA data GDI+ Bitmap from VCL BbitmapEX
151              */
152             RawRGBABitmap bitmapFromVCLBitmapEx( const ::BitmapEx& rBmpEx )
153             {
154                 // TODO(P2): Avoid temporary bitmap generation, maybe
155                 // even ensure that created DIBs are copied back to
156                 // BmpEx (currently, every AcquireReadAccess() will
157                 // make the local bitmap copy unique, effectively
158                 // duplicating the memory used)
159 
160                 ENSURE_OR_THROW( rBmpEx.IsTransparent(),
161                                   "::dxcanvas::tools::bitmapFromVCLBitmapEx(): "
162                                   "BmpEx not transparent" );
163 
164                 // convert transparent bitmap to 32bit RGBA
165                 // ========================================
166 
167                 const ::Size aBmpSize( rBmpEx.GetSizePixel() );
168 
169                 RawRGBABitmap aBmpData;
170                 aBmpData.mnWidth	 = aBmpSize.Width();
171                 aBmpData.mnHeight	 = aBmpSize.Height();
172                 aBmpData.mpBitmapData.reset( new sal_uInt8[ 4*aBmpData.mnWidth*aBmpData.mnHeight ] );
173 
174                 Bitmap aBitmap( rBmpEx.GetBitmap() );
175 
176                 ScopedBitmapReadAccess pReadAccess( aBitmap.AcquireReadAccess(),
177                                                     aBitmap );
178 
179                 const sal_Int32 nWidth( aBmpSize.Width() );
180                 const sal_Int32 nHeight( aBmpSize.Height() );
181 
182                 ENSURE_OR_THROW( pReadAccess.get() != NULL,
183                                   "::dxcanvas::tools::bitmapFromVCLBitmapEx(): "
184                                   "Unable to acquire read acces to bitmap" );
185 
186                 if( rBmpEx.IsAlpha() )
187                 {
188                     Bitmap aAlpha( rBmpEx.GetAlpha().GetBitmap() );
189 
190                     ScopedBitmapReadAccess pAlphaReadAccess( aAlpha.AcquireReadAccess(),
191                                                              aAlpha );
192 
193                     // By convention, the access buffer always has
194                     // one of the following formats:
195                     //
196                     //    BMP_FORMAT_1BIT_MSB_PAL
197                     //	  BMP_FORMAT_4BIT_MSN_PAL
198                     //	  BMP_FORMAT_8BIT_PAL
199                     //	  BMP_FORMAT_16BIT_TC_LSB_MASK
200                     //	  BMP_FORMAT_24BIT_TC_BGR
201                     //	  BMP_FORMAT_32BIT_TC_MASK
202                     //
203                     // and is always BMP_FORMAT_BOTTOM_UP
204                     //
205                     // This is the way
206                     // WinSalBitmap::AcquireBuffer() sets up the
207                     // buffer
208 
209                     ENSURE_OR_THROW( pAlphaReadAccess.get() != NULL,
210                                       "::dxcanvas::tools::bitmapFromVCLBitmapEx(): "
211                                       "Unable to acquire read acces to alpha" );
212 
213                     ENSURE_OR_THROW( pAlphaReadAccess->GetScanlineFormat() == BMP_FORMAT_8BIT_PAL ||
214                                       pAlphaReadAccess->GetScanlineFormat() == BMP_FORMAT_8BIT_TC_MASK,
215                                       "::dxcanvas::tools::bitmapFromVCLBitmapEx(): "
216                                       "Unsupported alpha scanline format" );
217 
218                     BitmapColor		aCol;
219                     const sal_Int32 nWidth( aBmpSize.Width() );
220                     const sal_Int32 nHeight( aBmpSize.Height() );
221                     sal_uInt8* 		pCurrOutput( aBmpData.mpBitmapData.get() );
222                     int 			x, y;
223 
224                     for( y=0; y<nHeight; ++y )
225                     {
226                         switch( pReadAccess->GetScanlineFormat() )
227                         {
228                             case BMP_FORMAT_8BIT_PAL:
229                             {
230                                 Scanline pScan  = pReadAccess->GetScanline( y );
231                                 Scanline pAScan = pAlphaReadAccess->GetScanline( y );
232 
233                                 for( x=0; x<nWidth; ++x )
234                                 {
235                                     aCol = pReadAccess->GetPaletteColor( *pScan++ );
236 
237                                     *pCurrOutput++ = aCol.GetBlue();
238                                     *pCurrOutput++ = aCol.GetGreen();
239                                     *pCurrOutput++ = aCol.GetRed();
240 
241                                     // out notion of alpha is
242                                     // different from the rest
243                                     // of the world's
244                                     *pCurrOutput++ = 255 - (BYTE)*pAScan++;
245                                 }
246                             }
247                             break;
248 
249                             case BMP_FORMAT_24BIT_TC_BGR:
250                             {
251                                 Scanline pScan  = pReadAccess->GetScanline( y );
252                                 Scanline pAScan = pAlphaReadAccess->GetScanline( y );
253 
254                                 for( x=0; x<nWidth; ++x )
255                                 {
256                                     // store as RGBA
257                                     *pCurrOutput++ = *pScan++;
258                                     *pCurrOutput++ = *pScan++;
259                                     *pCurrOutput++ = *pScan++;
260 
261                                     // out notion of alpha is
262                                     // different from the rest
263                                     // of the world's
264                                     *pCurrOutput++ = 255 - (BYTE)*pAScan++;
265                                 }
266                             }
267                             break;
268 
269                             // TODO(P2): Might be advantageous
270                             // to hand-formulate the following
271                             // formats, too.
272                             case BMP_FORMAT_1BIT_MSB_PAL:
273                                 // FALLTHROUGH intended
274                             case BMP_FORMAT_4BIT_MSN_PAL:
275                                 // FALLTHROUGH intended
276                             case BMP_FORMAT_16BIT_TC_LSB_MASK:
277                                 // FALLTHROUGH intended
278                             case BMP_FORMAT_32BIT_TC_MASK:
279                             {
280                                 Scanline pAScan = pAlphaReadAccess->GetScanline( y );
281 
282                                 // using fallback for those
283                                 // seldom formats
284                                 for( x=0; x<nWidth; ++x )
285                                 {
286                                     // yes. x and y are swapped on Get/SetPixel
287                                     aCol = pReadAccess->GetColor(y,x);
288 
289                                     *pCurrOutput++ = aCol.GetBlue();
290                                     *pCurrOutput++ = aCol.GetGreen();
291                                     *pCurrOutput++ = aCol.GetRed();
292 
293                                     // out notion of alpha is
294                                     // different from the rest
295                                     // of the world's
296                                     *pCurrOutput++ = 255 - (BYTE)*pAScan++;
297                                 }
298                             }
299                             break;
300 
301                             case BMP_FORMAT_1BIT_LSB_PAL:
302                                 // FALLTHROUGH intended
303                             case BMP_FORMAT_4BIT_LSN_PAL:
304                                 // FALLTHROUGH intended
305                             case BMP_FORMAT_8BIT_TC_MASK:
306                                 // FALLTHROUGH intended
307                             case BMP_FORMAT_24BIT_TC_RGB:
308                                 // FALLTHROUGH intended
309                             case BMP_FORMAT_24BIT_TC_MASK:
310                                 // FALLTHROUGH intended
311                             case BMP_FORMAT_16BIT_TC_MSB_MASK:
312                                 // FALLTHROUGH intended
313                             case BMP_FORMAT_32BIT_TC_ABGR:
314                                 // FALLTHROUGH intended
315                             case BMP_FORMAT_32BIT_TC_ARGB:
316                                 // FALLTHROUGH intended
317                             case BMP_FORMAT_32BIT_TC_BGRA:
318                                 // FALLTHROUGH intended
319                             case BMP_FORMAT_32BIT_TC_RGBA:
320                                 // FALLTHROUGH intended
321                             default:
322                                 ENSURE_OR_THROW( false,
323                                                   "::dxcanvas::tools::bitmapFromVCLBitmapEx(): "
324                                                   "Unexpected scanline format - has "
325                                                   "WinSalBitmap::AcquireBuffer() changed?" );
326                         }
327                     }
328                 }
329                 else
330                 {
331                     Bitmap aMask( rBmpEx.GetMask() );
332 
333                     ScopedBitmapReadAccess pMaskReadAccess( aMask.AcquireReadAccess(),
334                                                             aMask );
335 
336                     // By convention, the access buffer always has
337                     // one of the following formats:
338                     //
339                     //    BMP_FORMAT_1BIT_MSB_PAL
340                     //	  BMP_FORMAT_4BIT_MSN_PAL
341                     //	  BMP_FORMAT_8BIT_PAL
342                     //	  BMP_FORMAT_16BIT_TC_LSB_MASK
343                     //	  BMP_FORMAT_24BIT_TC_BGR
344                     //	  BMP_FORMAT_32BIT_TC_MASK
345                     //
346                     // and is always BMP_FORMAT_BOTTOM_UP
347                     //
348                     // This is the way
349                     // WinSalBitmap::AcquireBuffer() sets up the
350                     // buffer
351 
352                     ENSURE_OR_THROW( pMaskReadAccess.get() != NULL,
353                                       "::dxcanvas::tools::bitmapFromVCLBitmapEx(): "
354                                       "Unable to acquire read acces to mask" );
355 
356                     ENSURE_OR_THROW( pMaskReadAccess->GetScanlineFormat() == BMP_FORMAT_1BIT_MSB_PAL,
357                                       "::dxcanvas::tools::bitmapFromVCLBitmapEx(): "
358                                       "Unsupported mask scanline format" );
359 
360                     BitmapColor		aCol;
361                     int 			nCurrBit;
362                     const int		nMask( 1L );
363                     const int 		nInitialBit(7);
364                     sal_uInt8* 		pCurrOutput( aBmpData.mpBitmapData.get() );
365                     int 			x, y;
366 
367                     // mapping table, to get from mask index color to
368                     // alpha value (which depends on the mask's palette)
369                     sal_uInt8 aColorMap[2];
370 
371                     const BitmapColor& rCol0( pMaskReadAccess->GetPaletteColor( 0 ) );
372                     const BitmapColor& rCol1( pMaskReadAccess->GetPaletteColor( 1 ) );
373 
374                     // shortcut for true luminance calculation
375                     // (assumes that palette is grey-level). Note the
376                     // swapped the indices here, to account for the
377                     // fact that VCL's notion of alpha is inverted to
378                     // the rest of the world's.
379                     aColorMap[0] = rCol1.GetRed();
380                     aColorMap[1] = rCol0.GetRed();
381 
382                     for( y=0; y<nHeight; ++y )
383                     {
384                         switch( pReadAccess->GetScanlineFormat() )
385                         {
386                             case BMP_FORMAT_8BIT_PAL:
387                             {
388                                 Scanline pScan  = pReadAccess->GetScanline( y );
389                                 Scanline pMScan = pMaskReadAccess->GetScanline( y );
390 
391                                 for( x=0, nCurrBit=nInitialBit; x<nWidth; ++x )
392                                 {
393                                     aCol = pReadAccess->GetPaletteColor( *pScan++ );
394 
395                                     *pCurrOutput++ = aCol.GetBlue();
396                                     *pCurrOutput++ = aCol.GetGreen();
397                                     *pCurrOutput++ = aCol.GetRed();
398 
399                                     *pCurrOutput++ = aColorMap[ (pMScan[ (x & ~7L) >> 3L ] >> nCurrBit ) & nMask ];
400                                     nCurrBit = ((nCurrBit - 1) % 8L) & 7L;
401                                 }
402                             }
403                             break;
404 
405                             case BMP_FORMAT_24BIT_TC_BGR:
406                             {
407                                 Scanline pScan  = pReadAccess->GetScanline( y );
408                                 Scanline pMScan = pMaskReadAccess->GetScanline( y );
409 
410                                 for( x=0, nCurrBit=nInitialBit; x<nWidth; ++x )
411                                 {
412                                     // store as RGBA
413                                     *pCurrOutput++ = *pScan++;
414                                     *pCurrOutput++ = *pScan++;
415                                     *pCurrOutput++ = *pScan++;
416 
417                                     *pCurrOutput++ = aColorMap[ (pMScan[ (x & ~7L) >> 3L ] >> nCurrBit ) & nMask ];
418                                     nCurrBit = ((nCurrBit - 1) % 8L) & 7L;
419                                 }
420                             }
421                             break;
422 
423                             // TODO(P2): Might be advantageous
424                             // to hand-formulate the following
425                             // formats, too.
426                             case BMP_FORMAT_1BIT_MSB_PAL:
427                                 // FALLTHROUGH intended
428                             case BMP_FORMAT_4BIT_MSN_PAL:
429                                 // FALLTHROUGH intended
430                             case BMP_FORMAT_16BIT_TC_LSB_MASK:
431                                 // FALLTHROUGH intended
432                             case BMP_FORMAT_32BIT_TC_MASK:
433                             {
434                                 Scanline pMScan = pMaskReadAccess->GetScanline( y );
435 
436                                 // using fallback for those
437                                 // seldom formats
438                                 for( x=0, nCurrBit=nInitialBit; x<nWidth; ++x )
439                                 {
440                                     // yes. x and y are swapped on Get/SetPixel
441                                     aCol = pReadAccess->GetColor(y,x);
442 
443                                     // store as RGBA
444                                     *pCurrOutput++ = aCol.GetBlue();
445                                     *pCurrOutput++ = aCol.GetGreen();
446                                     *pCurrOutput++ = aCol.GetRed();
447 
448                                     *pCurrOutput++ = aColorMap[ (pMScan[ (x & ~7L) >> 3L ] >> nCurrBit ) & nMask ];
449                                     nCurrBit = ((nCurrBit - 1) % 8L) & 7L;
450                                 }
451                             }
452                             break;
453 
454                             case BMP_FORMAT_1BIT_LSB_PAL:
455                                 // FALLTHROUGH intended
456                             case BMP_FORMAT_4BIT_LSN_PAL:
457                                 // FALLTHROUGH intended
458                             case BMP_FORMAT_8BIT_TC_MASK:
459                                 // FALLTHROUGH intended
460                             case BMP_FORMAT_24BIT_TC_RGB:
461                                 // FALLTHROUGH intended
462                             case BMP_FORMAT_24BIT_TC_MASK:
463                                 // FALLTHROUGH intended
464                             case BMP_FORMAT_16BIT_TC_MSB_MASK:
465                                 // FALLTHROUGH intended
466                             case BMP_FORMAT_32BIT_TC_ABGR:
467                                 // FALLTHROUGH intended
468                             case BMP_FORMAT_32BIT_TC_ARGB:
469                                 // FALLTHROUGH intended
470                             case BMP_FORMAT_32BIT_TC_BGRA:
471                                 // FALLTHROUGH intended
472                             case BMP_FORMAT_32BIT_TC_RGBA:
473                                 // FALLTHROUGH intended
474                             default:
475                                 ENSURE_OR_THROW( false,
476                                                   "::dxcanvas::tools::bitmapFromVCLBitmapEx(): "
477                                                   "Unexpected scanline format - has "
478                                                   "WinSalBitmap::AcquireBuffer() changed?" );
479                         }
480                     }
481                 }
482 
483                 return aBmpData;
484             }
485 
486             bool drawVCLBitmapEx( const ::boost::shared_ptr< Gdiplus::Graphics >& rGraphics,
487                                   const ::BitmapEx& 							  rBmpEx )
488             {
489                 if( !rBmpEx.IsTransparent() )
490                 {
491                     Bitmap aBmp( rBmpEx.GetBitmap() );
492                     return drawVCLBitmap( rGraphics, aBmp );
493                 }
494                 else
495                 {
496                     return drawRGBABits( rGraphics,
497                                          bitmapFromVCLBitmapEx( rBmpEx ) );
498                 }
499             }
500         }
501 
502         bool drawVCLBitmapFromXBitmap( const ::boost::shared_ptr< Gdiplus::Graphics >& rGraphics,
503                                        const uno::Reference< rendering::XBitmap >&	   xBitmap )
504         {
505             // TODO(F2): add support for floating point bitmap formats
506             uno::Reference< rendering::XIntegerReadOnlyBitmap > xIntBmp(
507                 xBitmap, uno::UNO_QUERY );
508 
509             if( !xIntBmp.is() )
510                 return false;
511 
512             ::BitmapEx aBmpEx = ::vcl::unotools::bitmapExFromXBitmap( xIntBmp );
513             if( !aBmpEx )
514                 return false;
515 
516             return drawVCLBitmapEx( rGraphics, aBmpEx );
517         }
518     }
519 }
520