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