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