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 <canvas/debug.hxx>
32 #include <tools/diagnose_ex.h>
33 
34 #include <com/sun/star/util/Endianness.hpp>
35 
36 #include <rtl/logfile.hxx>
37 #include <rtl/math.hxx>
38 
39 #include <tools/poly.hxx>
40 #include <vcl/window.hxx>
41 #include <vcl/bitmapex.hxx>
42 #include <vcl/bmpacc.hxx>
43 #include <vcl/canvastools.hxx>
44 
45 #include <basegfx/matrix/b2dhommatrix.hxx>
46 #include <basegfx/point/b2dpoint.hxx>
47 #include <basegfx/tools/canvastools.hxx>
48 #include <basegfx/numeric/ftools.hxx>
49 
50 #include <canvas/canvastools.hxx>
51 
52 #include "canvasbitmap.hxx"
53 #include "canvasbitmaphelper.hxx"
54 
55 
56 using namespace ::com::sun::star;
57 
58 namespace vclcanvas
59 {
60     CanvasBitmapHelper::CanvasBitmapHelper() :
61         mpBackBuffer(),
62         mpOutDevReference()
63     {
64     }
65 
66     void CanvasBitmapHelper::setBitmap( const BitmapEx& rBitmap )
67     {
68         ENSURE_OR_THROW( mpOutDev,
69                          "Invalid reference device" );
70 
71         mpBackBuffer.reset( new BitmapBackBuffer( rBitmap,
72                                                   mpOutDev->getOutDev() ) );
73 
74         // tell canvas helper about the new target OutDev (don't
75         // protect state, it's our own VirDev, anyways)
76         setOutDev( mpBackBuffer, false );
77     }
78 
79     void CanvasBitmapHelper::init( const BitmapEx&                rBitmap,
80                                    rendering::XGraphicDevice&     rDevice,
81                                    const OutDevProviderSharedPtr& rOutDevReference )
82     {
83         mpOutDevReference = rOutDevReference;
84         mpBackBuffer.reset( new BitmapBackBuffer( rBitmap, rOutDevReference->getOutDev() ));
85 
86         // forward new settings to base class (ref device, output
87         // surface, no protection (own backbuffer), alpha depends on
88         // whether BmpEx is transparent or not)
89         CanvasHelper::init( rDevice,
90                             mpBackBuffer,
91                             false,
92                             rBitmap.IsTransparent() );
93     }
94 
95     void CanvasBitmapHelper::disposing()
96     {
97         mpBackBuffer.reset();
98         mpOutDevReference.reset();
99 
100         // forward to base class
101         CanvasHelper::disposing();
102     }
103 
104     geometry::IntegerSize2D CanvasBitmapHelper::getSize()
105     {
106         if( !mpBackBuffer )
107             return geometry::IntegerSize2D();
108 
109         return ::vcl::unotools::integerSize2DFromSize( mpBackBuffer->getBitmapSizePixel() );
110     }
111 
112     void CanvasBitmapHelper::clear()
113     {
114         // are we disposed?
115         if( mpBackBuffer )
116             mpBackBuffer->clear(); // alpha vdev needs special treatment
117     }
118 
119     uno::Reference< rendering::XBitmap > CanvasBitmapHelper::getScaledBitmap( const geometry::RealSize2D& 	newSize,
120                                                                               sal_Bool 						beFast )
121     {
122         ENSURE_OR_THROW( mpDevice,
123                           "disposed CanvasHelper" );
124 
125         RTL_LOGFILE_CONTEXT( aLog, "::vclcanvas::CanvasBitmapHelper::getScaledBitmap()" );
126 
127         if( !mpBackBuffer || mpDevice )
128             return uno::Reference< rendering::XBitmap >(); // we're disposed
129 
130         BitmapEx aRes( mpBackBuffer->getBitmapReference() );
131 
132         aRes.Scale( ::vcl::unotools::sizeFromRealSize2D(newSize),
133                      beFast ? BMP_SCALE_FAST : BMP_SCALE_INTERPOLATE );
134 
135         return uno::Reference< rendering::XBitmap >(
136             new CanvasBitmap( aRes, *mpDevice, mpOutDevReference ) );
137     }
138 
139     uno::Sequence< sal_Int8 > CanvasBitmapHelper::getData( rendering::IntegerBitmapLayout& 		rLayout,
140                                                            const geometry::IntegerRectangle2D&	rect )
141     {
142         RTL_LOGFILE_CONTEXT( aLog, "::vclcanvas::CanvasBitmapHelper::getData()" );
143 
144         if( !mpBackBuffer )
145             return uno::Sequence< sal_Int8 >(); // we're disposed
146 
147         rLayout = getMemoryLayout();
148         Bitmap aBitmap( mpBackBuffer->getBitmapReference().GetBitmap() );
149         Bitmap aAlpha( mpBackBuffer->getBitmapReference().GetAlpha().GetBitmap() );
150 
151         ScopedBitmapReadAccess pReadAccess( aBitmap.AcquireReadAccess(),
152                                             aBitmap );
153         ScopedBitmapReadAccess pAlphaReadAccess( aAlpha.IsEmpty() ?
154                                                  (BitmapReadAccess*)NULL : aAlpha.AcquireReadAccess(),
155                                                  aAlpha );
156 
157         ENSURE_OR_THROW( pReadAccess.get() != NULL,
158                          "Could not acquire read access to bitmap" );
159 
160         // TODO(F1): Support more formats.
161         const Size aBmpSize( aBitmap.GetSizePixel() );
162 
163         rLayout.ScanLines = aBmpSize.Height();
164         rLayout.ScanLineBytes = aBmpSize.Width()*4;
165         rLayout.ScanLineStride = rLayout.ScanLineBytes;
166 
167         // for the time being, always return as BGRA
168         uno::Sequence< sal_Int8 > aRes( 4*aBmpSize.Width()*aBmpSize.Height() );
169         sal_Int8* pRes = aRes.getArray();
170 
171         int nCurrPos(0);
172         for( int y=rect.Y1;
173              y<aBmpSize.Height() && y<rect.Y2;
174              ++y )
175         {
176             if( pAlphaReadAccess.get() != NULL )
177             {
178                 for( int x=rect.X1;
179                      x<aBmpSize.Width() && x<rect.X2;
180                      ++x )
181                 {
182                     pRes[ nCurrPos++ ] = pReadAccess->GetColor( y, x ).GetRed();
183                     pRes[ nCurrPos++ ] = pReadAccess->GetColor( y, x ).GetGreen();
184                     pRes[ nCurrPos++ ] = pReadAccess->GetColor( y, x ).GetBlue();
185                     pRes[ nCurrPos++ ] = pAlphaReadAccess->GetPixel( y, x ).GetIndex();
186                 }
187             }
188             else
189             {
190                 for( int x=rect.X1;
191                      x<aBmpSize.Width() && x<rect.X2;
192                      ++x )
193                 {
194                     pRes[ nCurrPos++ ] = pReadAccess->GetColor( y, x ).GetRed();
195                     pRes[ nCurrPos++ ] = pReadAccess->GetColor( y, x ).GetGreen();
196                     pRes[ nCurrPos++ ] = pReadAccess->GetColor( y, x ).GetBlue();
197                     pRes[ nCurrPos++ ] = sal_uInt8(255);
198                 }
199             }
200         }
201 
202         return aRes;
203     }
204 
205     void CanvasBitmapHelper::setData( const uno::Sequence< sal_Int8 >& 		data,
206                                       const rendering::IntegerBitmapLayout& rLayout,
207                                       const geometry::IntegerRectangle2D&	rect )
208     {
209         RTL_LOGFILE_CONTEXT( aLog, "::vclcanvas::CanvasBitmapHelper::setData()" );
210 
211         if( !mpBackBuffer )
212             return; // we're disposed
213 
214         const rendering::IntegerBitmapLayout aRefLayout( getMemoryLayout() );
215         ENSURE_ARG_OR_THROW( aRefLayout.PlaneStride != rLayout.PlaneStride ||
216                              aRefLayout.ColorSpace  != rLayout.ColorSpace ||
217                              aRefLayout.Palette     != rLayout.Palette ||
218                              aRefLayout.IsMsbFirst  != rLayout.IsMsbFirst,
219                              "Mismatching memory layout" );
220 
221         // retrieve local copies from the BitmapEx, which are later
222         // stored back. Unfortunately, the BitmapEx does not permit
223         // in-place modifications, as they are necessary here.
224         Bitmap aBitmap( mpBackBuffer->getBitmapReference().GetBitmap() );
225         Bitmap aAlpha( mpBackBuffer->getBitmapReference().GetAlpha().GetBitmap() );
226 
227         bool bCopyBack( false ); // only copy something back, if we
228                                  // actually changed a pixel
229 
230         {
231             ScopedBitmapWriteAccess pWriteAccess( aBitmap.AcquireWriteAccess(),
232                                                   aBitmap );
233             ScopedBitmapWriteAccess pAlphaWriteAccess( aAlpha.IsEmpty() ?
234                                                        (BitmapWriteAccess*)NULL : aAlpha.AcquireWriteAccess(),
235                                                        aAlpha );
236 
237             if( pAlphaWriteAccess.get() )
238             {
239                 DBG_ASSERT( pAlphaWriteAccess->GetScanlineFormat() == BMP_FORMAT_8BIT_PAL ||
240                             pAlphaWriteAccess->GetScanlineFormat() == BMP_FORMAT_8BIT_TC_MASK,
241                             "non-8bit alpha not supported!" );
242             }
243 
244             ENSURE_OR_THROW( pWriteAccess.get() != NULL,
245                              "Could not acquire write access to bitmap" );
246 
247             // TODO(F1): Support more formats.
248             const Size aBmpSize( aBitmap.GetSizePixel() );
249 
250             // for the time being, always read as BGRA
251             int x, y, nCurrPos(0);
252             for( y=rect.Y1;
253                  y<aBmpSize.Height() && y<rect.Y2;
254                  ++y )
255             {
256                 if( pAlphaWriteAccess.get() != NULL )
257                 {
258                     switch( pWriteAccess->GetScanlineFormat() )
259                     {
260                         case BMP_FORMAT_8BIT_PAL:
261                         {
262                             Scanline pScan  = pWriteAccess->GetScanline( y );
263                             Scanline pAScan = pAlphaWriteAccess->GetScanline( y );
264 
265                             for( x=rect.X1;
266                                  x<aBmpSize.Width() && x<rect.X2;
267                                  ++x )
268                             {
269                                 *pScan++ = (sal_uInt8)pWriteAccess->GetBestPaletteIndex(
270                                     BitmapColor( data[ nCurrPos   ],
271                                                  data[ nCurrPos+1 ],
272                                                  data[ nCurrPos+2 ] ) );
273 
274                                 nCurrPos += 3;
275 
276                                 // cast to unsigned byte, for correct subtraction result
277                                 *pAScan++ = static_cast<sal_uInt8>(255 -
278                                                               static_cast<sal_uInt8>(data[ nCurrPos++ ]));
279                             }
280                         }
281                         break;
282 
283                         case BMP_FORMAT_24BIT_TC_BGR:
284                         {
285                             Scanline pScan  = pWriteAccess->GetScanline( y );
286                             Scanline pAScan = pAlphaWriteAccess->GetScanline( y );
287 
288                             for( x=rect.X1;
289                                  x<aBmpSize.Width() && x<rect.X2;
290                                  ++x )
291                             {
292                                 *pScan++ = data[ nCurrPos+2 ];
293                                 *pScan++ = data[ nCurrPos+1 ];
294                                 *pScan++ = data[ nCurrPos   ];
295 
296                                 nCurrPos += 3;
297 
298                                 // cast to unsigned byte, for correct subtraction result
299                                 *pAScan++ = static_cast<sal_uInt8>(255 -
300                                                               static_cast<sal_uInt8>(data[ nCurrPos++ ]));
301                             }
302                         }
303                         break;
304 
305                         case BMP_FORMAT_24BIT_TC_RGB:
306                         {
307                             Scanline pScan  = pWriteAccess->GetScanline( y );
308                             Scanline pAScan = pAlphaWriteAccess->GetScanline( y );
309 
310                             for( x=rect.X1;
311                                  x<aBmpSize.Width() && x<rect.X2;
312                                  ++x )
313                             {
314                                 *pScan++ = data[ nCurrPos   ];
315                                 *pScan++ = data[ nCurrPos+1 ];
316                                 *pScan++ = data[ nCurrPos+2 ];
317 
318                                 nCurrPos += 3;
319 
320                                 // cast to unsigned byte, for correct subtraction result
321                                 *pAScan++ = static_cast<sal_uInt8>(255 -
322                                                               static_cast<sal_uInt8>(data[ nCurrPos++ ]));
323                             }
324                         }
325                         break;
326 
327                         default:
328                         {
329                             for( x=rect.X1;
330                                  x<aBmpSize.Width() && x<rect.X2;
331                                  ++x )
332                             {
333                                 pWriteAccess->SetPixel( y, x, BitmapColor( data[ nCurrPos   ],
334                                                                            data[ nCurrPos+1 ],
335                                                                            data[ nCurrPos+2 ] ) );
336                                 nCurrPos += 3;
337 
338                                 // cast to unsigned byte, for correct subtraction result
339                                 pAlphaWriteAccess->SetPixel( y, x,
340                                                              BitmapColor(
341                                                                  static_cast<sal_uInt8>(255 -
342                                                                                    static_cast<sal_uInt8>(data[ nCurrPos++ ])) ) );
343                             }
344                         }
345                         break;
346                     }
347                 }
348                 else
349                 {
350                     // TODO(Q3): This is copy'n'pasted from
351                     // canvashelper.cxx, unify!
352                     switch( pWriteAccess->GetScanlineFormat() )
353                     {
354                         case BMP_FORMAT_8BIT_PAL:
355                         {
356                             Scanline pScan = pWriteAccess->GetScanline( y );
357 
358                             for( x=rect.X1;
359                                  x<aBmpSize.Width() && x<rect.X2;
360                                  ++x )
361                             {
362                                 *pScan++ = (sal_uInt8)pWriteAccess->GetBestPaletteIndex(
363                                     BitmapColor( data[ nCurrPos   ],
364                                                  data[ nCurrPos+1 ],
365                                                  data[ nCurrPos+2 ] ) );
366 
367                                 nCurrPos += 4; // skip three colors, _plus_ alpha
368                             }
369                         }
370                         break;
371 
372                         case BMP_FORMAT_24BIT_TC_BGR:
373                         {
374                             Scanline pScan = pWriteAccess->GetScanline( y );
375 
376                             for( x=rect.X1;
377                                  x<aBmpSize.Width() && x<rect.X2;
378                                  ++x )
379                             {
380                                 *pScan++ = data[ nCurrPos+2 ];
381                                 *pScan++ = data[ nCurrPos+1 ];
382                                 *pScan++ = data[ nCurrPos   ];
383 
384                                 nCurrPos += 4; // skip three colors, _plus_ alpha
385                             }
386                         }
387                         break;
388 
389                         case BMP_FORMAT_24BIT_TC_RGB:
390                         {
391                             Scanline pScan = pWriteAccess->GetScanline( y );
392 
393                             for( x=rect.X1;
394                                  x<aBmpSize.Width() && x<rect.X2;
395                                  ++x )
396                             {
397                                 *pScan++ = data[ nCurrPos   ];
398                                 *pScan++ = data[ nCurrPos+1 ];
399                                 *pScan++ = data[ nCurrPos+2 ];
400 
401                                 nCurrPos += 4; // skip three colors, _plus_ alpha
402                             }
403                         }
404                         break;
405 
406                         default:
407                         {
408                             for( x=rect.X1;
409                                  x<aBmpSize.Width() && x<rect.X2;
410                                  ++x )
411                             {
412                                 pWriteAccess->SetPixel( y, x, BitmapColor( data[ nCurrPos   ],
413                                                                            data[ nCurrPos+1 ],
414                                                                            data[ nCurrPos+2 ] ) );
415                                 nCurrPos += 4; // skip three colors, _plus_ alpha
416                             }
417                         }
418                         break;
419                     }
420                 }
421 
422                 bCopyBack = true;
423             }
424         }
425 
426         // copy back only here, since the BitmapAccessors must be
427         // destroyed beforehand
428         if( bCopyBack )
429         {
430             if( aAlpha.IsEmpty() )
431                 setBitmap( BitmapEx( aBitmap ) );
432             else
433                 setBitmap( BitmapEx( aBitmap,
434                                      AlphaMask( aAlpha ) ) );
435         }
436     }
437 
438     void CanvasBitmapHelper::setPixel( const uno::Sequence< sal_Int8 >& 	 color,
439                                        const rendering::IntegerBitmapLayout& rLayout,
440                                        const geometry::IntegerPoint2D& 		 pos )
441     {
442         RTL_LOGFILE_CONTEXT( aLog, "::vclcanvas::CanvasBitmapHelper::setPixel()" );
443 
444         if( !mpBackBuffer )
445             return; // we're disposed
446 
447         const Size aBmpSize( mpBackBuffer->getBitmapReference().GetSizePixel() );
448 
449         ENSURE_ARG_OR_THROW( pos.X >= 0 && pos.X < aBmpSize.Width(),
450                          "X coordinate out of bounds" );
451         ENSURE_ARG_OR_THROW( pos.Y >= 0 && pos.Y < aBmpSize.Height(),
452                          "Y coordinate out of bounds" );
453         ENSURE_ARG_OR_THROW( color.getLength() > 3,
454                          "not enough color components" );
455 
456         const rendering::IntegerBitmapLayout aRefLayout( getMemoryLayout() );
457         ENSURE_ARG_OR_THROW( aRefLayout.PlaneStride != rLayout.PlaneStride ||
458                              aRefLayout.ColorSpace  != rLayout.ColorSpace ||
459                              aRefLayout.Palette     != rLayout.Palette ||
460                              aRefLayout.IsMsbFirst  != rLayout.IsMsbFirst,
461                              "Mismatching memory layout" );
462 
463         // retrieve local copies from the BitmapEx, which are later
464         // stored back. Unfortunately, the BitmapEx does not permit
465         // in-place modifications, as they are necessary here.
466         Bitmap aBitmap( mpBackBuffer->getBitmapReference().GetBitmap() );
467         Bitmap aAlpha( mpBackBuffer->getBitmapReference().GetAlpha().GetBitmap() );
468 
469         bool bCopyBack( false ); // only copy something back, if we
470                                  // actually changed a pixel
471 
472         {
473             ScopedBitmapWriteAccess pWriteAccess( aBitmap.AcquireWriteAccess(),
474                                                   aBitmap );
475             ScopedBitmapWriteAccess pAlphaWriteAccess( aAlpha.IsEmpty() ?
476                                                        (BitmapWriteAccess*)NULL : aAlpha.AcquireWriteAccess(),
477                                                        aAlpha );
478 
479             ENSURE_OR_THROW( pWriteAccess.get() != NULL,
480                              "Could not acquire write access to bitmap" );
481 
482             pWriteAccess->SetPixel( pos.Y, pos.X, BitmapColor( color[ 0 ],
483                                                                color[ 1 ],
484                                                                color[ 2 ] ) );
485 
486             if( pAlphaWriteAccess.get() != NULL )
487                 pAlphaWriteAccess->SetPixel( pos.Y, pos.X, BitmapColor( 255 - color[ 3 ] ) );
488 
489             bCopyBack = true;
490         }
491 
492         // copy back only here, since the BitmapAccessors must be
493         // destroyed beforehand
494         if( bCopyBack )
495         {
496             if( aAlpha.IsEmpty() )
497                 setBitmap( BitmapEx( aBitmap ) );
498             else
499                 setBitmap( BitmapEx( aBitmap,
500                                      AlphaMask( aAlpha ) ) );
501         }
502     }
503 
504     uno::Sequence< sal_Int8 > CanvasBitmapHelper::getPixel( rendering::IntegerBitmapLayout&	rLayout,
505                                                             const geometry::IntegerPoint2D& pos )
506     {
507         RTL_LOGFILE_CONTEXT( aLog, "::vclcanvas::CanvasBitmapHelper::getPixel()" );
508 
509         if( !mpBackBuffer )
510             return uno::Sequence< sal_Int8 >(); // we're disposed
511 
512         rLayout = getMemoryLayout();
513         rLayout.ScanLines = 1;
514         rLayout.ScanLineBytes = 4;
515         rLayout.ScanLineStride = rLayout.ScanLineBytes;
516 
517         const Size aBmpSize( mpBackBuffer->getBitmapReference().GetSizePixel() );
518 
519         ENSURE_ARG_OR_THROW( pos.X >= 0 && pos.X < aBmpSize.Width(),
520                          "X coordinate out of bounds" );
521         ENSURE_ARG_OR_THROW( pos.Y >= 0 && pos.Y < aBmpSize.Height(),
522                          "Y coordinate out of bounds" );
523 
524         Bitmap aBitmap( mpBackBuffer->getBitmapReference().GetBitmap() );
525         Bitmap aAlpha( mpBackBuffer->getBitmapReference().GetAlpha().GetBitmap() );
526 
527         ScopedBitmapReadAccess pReadAccess( aBitmap.AcquireReadAccess(),
528                                             aBitmap );
529         ScopedBitmapReadAccess pAlphaReadAccess( aAlpha.IsEmpty() ?
530                                                  (BitmapReadAccess*)NULL : aAlpha.AcquireReadAccess(),
531                                                  aAlpha );
532         ENSURE_OR_THROW( pReadAccess.get() != NULL,
533                          "Could not acquire read access to bitmap" );
534 
535         uno::Sequence< sal_Int8 > aRes( 4 );
536         sal_Int8* pRes = aRes.getArray();
537 
538         const BitmapColor aColor( pReadAccess->GetColor( pos.Y, pos.X ) );
539         pRes[ 0 ] = aColor.GetRed();
540         pRes[ 1 ] = aColor.GetGreen();
541         pRes[ 2 ] = aColor.GetBlue();
542 
543         if( pAlphaReadAccess.get() != NULL )
544             pRes[ 3 ] = pAlphaReadAccess->GetPixel( pos.Y, pos.X ).GetIndex();
545         else
546             pRes[ 3 ] = sal_uInt8(255);
547 
548         return aRes;
549     }
550 
551     rendering::IntegerBitmapLayout CanvasBitmapHelper::getMemoryLayout()
552     {
553         if( !mpOutDev.get() )
554             return rendering::IntegerBitmapLayout(); // we're disposed
555 
556         return ::canvas::tools::getStdMemoryLayout(getSize());
557     }
558 
559     BitmapEx CanvasBitmapHelper::getBitmap() const
560     {
561         if( !mpBackBuffer )
562             return BitmapEx(); // we're disposed
563         else
564             return mpBackBuffer->getBitmapReference();
565     }
566 
567 }
568