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