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 "imgprod.hxx"
22 :
23 : #include <vcl/bmpacc.hxx>
24 : #include <vcl/cvtgrf.hxx>
25 : #include <vcl/svapp.hxx>
26 : #include <unotools/ucbstreamhelper.hxx>
27 : #include <vcl/graphicfilter.hxx>
28 : #include <com/sun/star/io/XInputStream.hpp>
29 :
30 : #include "svtools/imageresourceaccess.hxx"
31 : #include <comphelper/processfactory.hxx>
32 :
33 : // --------------------
34 : // - ImgProdLockBytes -
35 : // --------------------
36 :
37 : class ImgProdLockBytes : public SvLockBytes
38 : {
39 : ::com::sun::star::uno::Reference< ::com::sun::star::io::XInputStream > xStmRef;
40 : ::com::sun::star::uno::Sequence<sal_Int8> maSeq;
41 :
42 : ImgProdLockBytes() {};
43 :
44 : public:
45 :
46 : ImgProdLockBytes( SvStream* pStm, sal_Bool bOwner );
47 : ImgProdLockBytes( ::com::sun::star::uno::Reference< ::com::sun::star::io::XInputStream > & rStreamRef );
48 : virtual ~ImgProdLockBytes();
49 :
50 : virtual ErrCode ReadAt( sal_Size nPos, void* pBuffer, sal_Size nCount, sal_Size* pRead ) const;
51 : virtual ErrCode WriteAt( sal_Size nPos, const void* pBuffer, sal_Size nCount, sal_Size* pWritten );
52 : virtual ErrCode Flush() const;
53 : virtual ErrCode SetSize( sal_Size nSize );
54 : virtual ErrCode Stat( SvLockBytesStat*, SvLockBytesStatFlag ) const;
55 : };
56 :
57 : // ------------------------------------------------------------------------
58 :
59 0 : ImgProdLockBytes::ImgProdLockBytes( SvStream* pStm, sal_Bool bOwner ) :
60 0 : SvLockBytes( pStm, bOwner )
61 : {
62 0 : }
63 :
64 : // ------------------------------------------------------------------------
65 :
66 1 : ImgProdLockBytes::ImgProdLockBytes( ::com::sun::star::uno::Reference< ::com::sun::star::io::XInputStream > & rStmRef ) :
67 1 : xStmRef( rStmRef )
68 : {
69 1 : if( xStmRef.is() )
70 : {
71 1 : const sal_uInt32 nBytesToRead = 65535;
72 : sal_uInt32 nRead;
73 :
74 1 : do
75 : {
76 1 : ::com::sun::star::uno::Sequence< sal_Int8 > aReadSeq;
77 :
78 1 : nRead = xStmRef->readSomeBytes( aReadSeq, nBytesToRead );
79 :
80 1 : if( nRead )
81 : {
82 1 : const sal_uInt32 nOldLength = maSeq.getLength();
83 1 : maSeq.realloc( nOldLength + nRead );
84 1 : memcpy( maSeq.getArray() + nOldLength, aReadSeq.getConstArray(), aReadSeq.getLength() );
85 1 : }
86 : }
87 : while( nBytesToRead == nRead );
88 : }
89 1 : }
90 :
91 : // ------------------------------------------------------------------------
92 :
93 3 : ImgProdLockBytes::~ImgProdLockBytes()
94 : {
95 3 : }
96 :
97 : // ------------------------------------------------------------------------
98 :
99 4 : ErrCode ImgProdLockBytes::ReadAt( sal_Size nPos, void* pBuffer, sal_Size nCount, sal_Size* pRead ) const
100 : {
101 4 : if( GetStream() )
102 : {
103 0 : ( (SvStream*) GetStream() )->ResetError();
104 0 : const ErrCode nErr = SvLockBytes::ReadAt( nPos, pBuffer, nCount, pRead );
105 0 : ( (SvStream*) GetStream() )->ResetError();
106 0 : return nErr;
107 : }
108 : else
109 : {
110 4 : const sal_Size nSeqLen = maSeq.getLength();
111 4 : ErrCode nErr = ERRCODE_NONE;
112 :
113 4 : if( nPos < nSeqLen )
114 : {
115 4 : if( ( nPos + nCount ) > nSeqLen )
116 1 : nCount = nSeqLen - nPos;
117 :
118 4 : memcpy( pBuffer, maSeq.getConstArray() + nPos, nCount );
119 4 : *pRead = nCount;
120 : }
121 : else
122 0 : *pRead = 0UL;
123 :
124 4 : return nErr;
125 : }
126 : }
127 :
128 : // ------------------------------------------------------------------------
129 :
130 0 : ErrCode ImgProdLockBytes::WriteAt( sal_Size nPos, const void* pBuffer, sal_Size nCount, sal_Size* pWritten )
131 : {
132 0 : if( GetStream() )
133 0 : return SvLockBytes::WriteAt( nPos, pBuffer, nCount, pWritten );
134 : else
135 : {
136 : DBG_ASSERT( xStmRef.is(), "ImgProdLockBytes::WriteAt: xInputStream has no reference..." );
137 0 : return ERRCODE_IO_CANTWRITE;
138 : }
139 : }
140 :
141 : // ------------------------------------------------------------------------
142 :
143 1 : ErrCode ImgProdLockBytes::Flush() const
144 : {
145 1 : return ERRCODE_NONE;
146 : }
147 :
148 : // ------------------------------------------------------------------------
149 :
150 0 : ErrCode ImgProdLockBytes::SetSize( sal_Size nSize )
151 : {
152 0 : if( GetStream() )
153 0 : return SvLockBytes::SetSize( nSize );
154 : else
155 : {
156 : OSL_FAIL( "ImgProdLockBytes::SetSize not supported for xInputStream..." );
157 0 : return ERRCODE_IO_CANTWRITE;
158 : }
159 : }
160 :
161 : // ------------------------------------------------------------------------
162 :
163 1 : ErrCode ImgProdLockBytes::Stat( SvLockBytesStat* pStat, SvLockBytesStatFlag eFlag ) const
164 : {
165 1 : if( GetStream() )
166 0 : return SvLockBytes::Stat( pStat, eFlag );
167 : else
168 : {
169 : DBG_ASSERT( xStmRef.is(), "ImgProdLockBytes::Stat: xInputStream has no reference..." );
170 1 : pStat->nSize = maSeq.getLength();
171 1 : return ERRCODE_NONE;
172 : }
173 : }
174 :
175 : // -----------------
176 : // - ImageProducer -
177 : // -----------------
178 :
179 70 : ImageProducer::ImageProducer() :
180 : mpStm ( NULL ),
181 70 : mbConsInit ( sal_False )
182 : {
183 70 : mpGraphic = new Graphic;
184 70 : }
185 :
186 : // ------------------------------------------------------------
187 :
188 192 : ImageProducer::~ImageProducer()
189 : {
190 64 : delete mpGraphic;
191 64 : mpGraphic = NULL;
192 :
193 64 : delete mpStm;
194 64 : mpStm = NULL;
195 128 : }
196 :
197 : // ------------------------------------------------------------
198 :
199 : // ::com::sun::star::uno::XInterface
200 1 : ::com::sun::star::uno::Any ImageProducer::queryInterface( const ::com::sun::star::uno::Type & rType ) throw(::com::sun::star::uno::RuntimeException)
201 : {
202 : ::com::sun::star::uno::Any aRet = ::cppu::queryInterface( rType,
203 : (static_cast< ::com::sun::star::lang::XInitialization* >(this)),
204 1 : (static_cast< ::com::sun::star::awt::XImageProducer* >(this)) );
205 1 : return (aRet.hasValue() ? aRet : OWeakObject::queryInterface( rType ));
206 : }
207 :
208 : // ------------------------------------------------------------
209 :
210 0 : void ImageProducer::addConsumer( const ::com::sun::star::uno::Reference< ::com::sun::star::awt::XImageConsumer >& rxConsumer ) throw(::com::sun::star::uno::RuntimeException)
211 : {
212 : DBG_ASSERT( rxConsumer.is(), "::AddConsumer(...): No consumer referenced!" );
213 0 : if( rxConsumer.is() )
214 0 : maConsList.push_back( new ::com::sun::star::uno::Reference< ::com::sun::star::awt::XImageConsumer > ( rxConsumer ));
215 0 : }
216 :
217 : // ------------------------------------------------------------
218 :
219 0 : void ImageProducer::removeConsumer( const ::com::sun::star::uno::Reference< ::com::sun::star::awt::XImageConsumer >& rxConsumer ) throw(::com::sun::star::uno::RuntimeException)
220 : {
221 0 : ConsumerList_t::reverse_iterator riter = std::find(maConsList.rbegin(),maConsList.rend(),rxConsumer);
222 :
223 0 : if (riter != maConsList.rend())
224 0 : maConsList.erase(riter.base()-1);
225 0 : }
226 :
227 : // ------------------------------------------------------------
228 :
229 1 : void ImageProducer::SetImage( const OUString& rPath )
230 : {
231 1 : maURL = rPath;
232 1 : mpGraphic->Clear();
233 1 : mbConsInit = sal_False;
234 1 : delete mpStm;
235 :
236 1 : if ( ::svt::GraphicAccess::isSupportedURL( maURL ) )
237 : {
238 0 : mpStm = ::svt::GraphicAccess::getImageStream( ::comphelper::getProcessComponentContext(), maURL );
239 : }
240 1 : else if( !maURL.isEmpty() )
241 : {
242 0 : SvStream* pIStm = ::utl::UcbStreamHelper::CreateStream( maURL, STREAM_STD_READ );
243 0 : mpStm = pIStm ? new SvStream( new ImgProdLockBytes( pIStm, sal_True ) ) : NULL;
244 : }
245 : else
246 1 : mpStm = NULL;
247 1 : }
248 :
249 : // ------------------------------------------------------------
250 :
251 0 : void ImageProducer::SetImage( SvStream& rStm )
252 : {
253 0 : maURL = OUString();
254 0 : mpGraphic->Clear();
255 0 : mbConsInit = sal_False;
256 :
257 0 : delete mpStm;
258 0 : mpStm = new SvStream( new ImgProdLockBytes( &rStm, sal_False ) );
259 0 : }
260 :
261 : // ------------------------------------------------------------
262 :
263 6 : void ImageProducer::setImage( ::com::sun::star::uno::Reference< ::com::sun::star::io::XInputStream > & rInputStmRef )
264 : {
265 6 : maURL = OUString();
266 6 : mpGraphic->Clear();
267 6 : mbConsInit = sal_False;
268 6 : delete mpStm;
269 :
270 6 : if( rInputStmRef.is() )
271 1 : mpStm = new SvStream( new ImgProdLockBytes( rInputStmRef ) );
272 : else
273 5 : mpStm = NULL;
274 6 : }
275 :
276 : // ------------------------------------------------------------
277 :
278 0 : void ImageProducer::NewDataAvailable()
279 : {
280 0 : if( ( GRAPHIC_NONE == mpGraphic->GetType() ) || mpGraphic->GetContext() )
281 0 : startProduction();
282 0 : }
283 :
284 : // ------------------------------------------------------------
285 :
286 6 : void ImageProducer::startProduction() throw(::com::sun::star::uno::RuntimeException)
287 : {
288 6 : if( !maConsList.empty() || maDoneHdl.IsSet() )
289 : {
290 6 : bool bNotifyEmptyGraphics = false;
291 :
292 : // valid stream or filled graphic? => update consumers
293 6 : if( mpStm || ( mpGraphic->GetType() != GRAPHIC_NONE ) )
294 : {
295 : // if we already have a graphic, we don't have to import again;
296 : // graphic is cleared if a new Stream is set
297 1 : if( ( mpGraphic->GetType() == GRAPHIC_NONE ) || mpGraphic->GetContext() )
298 : {
299 1 : if ( ImplImportGraphic( *mpGraphic ) && maDoneHdl.IsSet() )
300 1 : maDoneHdl.Call( mpGraphic );
301 : }
302 :
303 1 : if( mpGraphic->GetType() != GRAPHIC_NONE )
304 1 : ImplUpdateData( *mpGraphic );
305 : else
306 0 : bNotifyEmptyGraphics = true;
307 : }
308 : else
309 5 : bNotifyEmptyGraphics = true;
310 :
311 6 : if ( bNotifyEmptyGraphics )
312 : {
313 : // reset image
314 : // create temporary list to hold interfaces
315 5 : ConsumerList_t aTmp = maConsList;
316 :
317 : // iterate through interfaces
318 5 : for( ConsumerList_t::iterator iter = aTmp.begin(); iter != aTmp.end(); ++iter )
319 : {
320 0 : (*iter)->init( 0, 0 );
321 0 : (*iter)->complete( ::com::sun::star::awt::ImageStatus::IMAGESTATUS_STATICIMAGEDONE, this );
322 : }
323 :
324 5 : if ( maDoneHdl.IsSet() )
325 5 : maDoneHdl.Call( NULL );
326 : }
327 : }
328 6 : }
329 :
330 : // ------------------------------------------------------------
331 :
332 1 : sal_Bool ImageProducer::ImplImportGraphic( Graphic& rGraphic )
333 : {
334 1 : if( ERRCODE_IO_PENDING == mpStm->GetError() )
335 0 : mpStm->ResetError();
336 :
337 1 : mpStm->Seek( 0UL );
338 :
339 1 : sal_Bool bRet = GraphicConverter::Import( *mpStm, rGraphic ) == ERRCODE_NONE;
340 :
341 1 : if( ERRCODE_IO_PENDING == mpStm->GetError() )
342 0 : mpStm->ResetError();
343 :
344 1 : return bRet;
345 : }
346 :
347 : // ------------------------------------------------------------
348 :
349 1 : void ImageProducer::ImplUpdateData( const Graphic& rGraphic )
350 : {
351 1 : ImplInitConsumer( rGraphic );
352 :
353 1 : if( mbConsInit && !maConsList.empty() )
354 : {
355 : // create temporary list to hold interfaces
356 0 : ConsumerList_t aTmp = maConsList;
357 :
358 0 : ImplUpdateConsumer( rGraphic );
359 0 : mbConsInit = sal_False;
360 :
361 : // iterate through interfaces
362 0 : for( ConsumerList_t::iterator iter = aTmp.begin(); iter != aTmp.end(); ++iter )
363 0 : (*iter)->complete( ::com::sun::star::awt::ImageStatus::IMAGESTATUS_STATICIMAGEDONE, this );
364 : }
365 1 : }
366 :
367 : // ------------------------------------------------------------
368 :
369 1 : void ImageProducer::ImplInitConsumer( const Graphic& rGraphic )
370 : {
371 1 : Bitmap aBmp( rGraphic.GetBitmapEx().GetBitmap() );
372 1 : BitmapReadAccess* pBmpAcc = aBmp.AcquireReadAccess();
373 :
374 1 : if( pBmpAcc )
375 : {
376 1 : sal_uInt16 nPalCount = 0;
377 1 : sal_uInt32 nRMask = 0;
378 1 : sal_uInt32 nGMask = 0;
379 1 : sal_uInt32 nBMask = 0;
380 1 : sal_uInt32 nAMask = 0;
381 1 : ::com::sun::star::uno::Sequence< sal_Int32 > aRGBPal;
382 :
383 1 : if( pBmpAcc->HasPalette() )
384 : {
385 1 : nPalCount = pBmpAcc->GetPaletteEntryCount();
386 :
387 1 : if( nPalCount )
388 : {
389 1 : aRGBPal = ::com::sun::star::uno::Sequence< sal_Int32 >( nPalCount + 1 );
390 :
391 1 : sal_Int32* pTmp = aRGBPal.getArray();
392 :
393 257 : for( sal_uInt32 i = 0; i < nPalCount; i++, pTmp++ )
394 : {
395 256 : const BitmapColor& rCol = pBmpAcc->GetPaletteColor( (sal_uInt16) i );
396 :
397 256 : *pTmp = ( (sal_Int32) rCol.GetRed() ) << (sal_Int32)(24L);
398 256 : *pTmp |= ( (sal_Int32) rCol.GetGreen() ) << (sal_Int32)(16L);
399 256 : *pTmp |= ( (sal_Int32) rCol.GetBlue() ) << (sal_Int32)(8L);
400 256 : *pTmp |= (sal_Int32)(0x000000ffL);
401 : }
402 :
403 1 : if( rGraphic.IsTransparent() )
404 : {
405 : // append transparent entry
406 1 : *pTmp = (sal_Int32)(0xffffff00L);
407 1 : mnTransIndex = nPalCount;
408 1 : nPalCount++;
409 : }
410 : else
411 0 : mnTransIndex = 0;
412 :
413 : }
414 : }
415 : else
416 : {
417 0 : nRMask = 0xff000000UL;
418 0 : nGMask = 0x00ff0000UL;
419 0 : nBMask = 0x0000ff00UL;
420 0 : nAMask = 0x000000ffUL;
421 : }
422 :
423 : // create temporary list to hold interfaces
424 2 : ConsumerList_t aTmp = maConsList;
425 :
426 : // iterate through interfaces
427 1 : for( ConsumerList_t::iterator iter = aTmp.begin(); iter != aTmp.end(); ++iter)
428 : {
429 0 : (*iter)->init( pBmpAcc->Width(), pBmpAcc->Height() );
430 0 : (*iter)->setColorModel( pBmpAcc->GetBitCount(),aRGBPal, nRMask, nGMask, nBMask, nAMask );
431 : }
432 :
433 1 : aBmp.ReleaseAccess( pBmpAcc );
434 2 : mbConsInit = sal_True;
435 1 : }
436 1 : }
437 :
438 : // ------------------------------------------------------------
439 :
440 0 : void ImageProducer::ImplUpdateConsumer( const Graphic& rGraphic )
441 : {
442 0 : BitmapEx aBmpEx( rGraphic.GetBitmapEx() );
443 0 : Bitmap aBmp( aBmpEx.GetBitmap() );
444 0 : BitmapReadAccess* pBmpAcc = aBmp.AcquireReadAccess();
445 :
446 0 : if( pBmpAcc )
447 : {
448 0 : Bitmap aMask( aBmpEx.GetMask() );
449 0 : BitmapReadAccess* pMskAcc = !!aMask ? aMask.AcquireReadAccess() : NULL;
450 0 : const long nWidth = pBmpAcc->Width();
451 0 : const long nHeight = pBmpAcc->Height();
452 0 : const long nStartX = 0L;
453 0 : const long nEndX = nWidth - 1L;
454 0 : const long nStartY = 0L;
455 0 : const long nEndY = nHeight - 1L;
456 0 : const long nPartWidth = nEndX - nStartX + 1;
457 0 : const long nPartHeight = nEndY - nStartY + 1;
458 :
459 0 : if( !pMskAcc )
460 : {
461 0 : aMask = Bitmap( aBmp.GetSizePixel(), 1 );
462 0 : aMask.Erase( COL_BLACK );
463 0 : pMskAcc = aMask.AcquireReadAccess();
464 : }
465 :
466 : // create temporary list to hold interfaces
467 0 : ConsumerList_t aTmp = maConsList;
468 :
469 0 : if( pBmpAcc->HasPalette() )
470 : {
471 0 : const BitmapColor aWhite( pMskAcc->GetBestMatchingColor( Color( COL_WHITE ) ) );
472 :
473 0 : if( mnTransIndex < 256 )
474 : {
475 0 : ::com::sun::star::uno::Sequence<sal_Int8> aData( nPartWidth * nPartHeight );
476 0 : sal_Int8* pTmp = aData.getArray();
477 :
478 0 : for( long nY = nStartY; nY <= nEndY; nY++ )
479 : {
480 0 : for( long nX = nStartX; nX <= nEndX; nX++ )
481 : {
482 0 : if( pMskAcc->GetPixel( nY, nX ) == aWhite )
483 : *pTmp++ = sal::static_int_cast< sal_Int8 >(
484 0 : mnTransIndex );
485 : else
486 0 : *pTmp++ = pBmpAcc->GetPixel( nY, nX ).GetIndex();
487 : }
488 : }
489 :
490 : // iterate through interfaces
491 0 : for (ConsumerList_t::iterator iter = aTmp.begin(); iter != aTmp.end(); ++iter)
492 0 : (*iter)->setPixelsByBytes( nStartX, nStartY, nPartWidth, nPartHeight, aData, 0UL, nPartWidth );
493 : }
494 : else
495 : {
496 0 : ::com::sun::star::uno::Sequence<sal_Int32> aData( nPartWidth * nPartHeight );
497 0 : sal_Int32* pTmp = aData.getArray();
498 :
499 0 : for( long nY = nStartY; nY <= nEndY; nY++ )
500 : {
501 0 : for( long nX = nStartX; nX <= nEndX; nX++ )
502 : {
503 0 : if( pMskAcc->GetPixel( nY, nX ) == aWhite )
504 0 : *pTmp++ = mnTransIndex;
505 : else
506 0 : *pTmp++ = pBmpAcc->GetPixel( nY, nX ).GetIndex();
507 : }
508 : }
509 :
510 : // iterate through interfaces
511 0 : for (ConsumerList_t::iterator iter = aTmp.begin(); iter != aTmp.end(); ++iter)
512 0 : (*iter)->setPixelsByLongs( nStartX, nStartY, nPartWidth, nPartHeight, aData, 0UL, nPartWidth );
513 0 : }
514 : }
515 : else
516 : {
517 0 : ::com::sun::star::uno::Sequence<sal_Int32> aData( nPartWidth * nPartHeight );
518 0 : const BitmapColor aWhite( pMskAcc->GetBestMatchingColor( Color( COL_WHITE ) ) );
519 0 : sal_Int32* pTmp = aData.getArray();
520 :
521 0 : for( long nY = nStartY; nY <= nEndY; nY++ )
522 : {
523 0 : for( long nX = nStartX; nX <= nEndX; nX++, pTmp++ )
524 : {
525 0 : const BitmapColor aCol( pBmpAcc->GetPixel( nY, nX ) );
526 :
527 0 : *pTmp = ( (sal_Int32) aCol.GetRed() ) << (sal_Int32)(24L);
528 0 : *pTmp |= ( (sal_Int32) aCol.GetGreen() ) << (sal_Int32)(16L);
529 0 : *pTmp |= ( (sal_Int32) aCol.GetBlue() ) << (sal_Int32)(8L);
530 :
531 0 : if( pMskAcc->GetPixel( nY, nX ) != aWhite )
532 0 : *pTmp |= 0x000000ffUL;
533 0 : }
534 : }
535 :
536 : // iterate through interfaces
537 0 : for (ConsumerList_t::iterator iter = aTmp.begin(); iter != aTmp.end(); ++iter)
538 0 : (*iter)->setPixelsByLongs( nStartX, nStartY, nPartWidth, nPartHeight, aData, 0UL, nPartWidth );
539 : }
540 :
541 0 : aBmp.ReleaseAccess( pBmpAcc );
542 0 : aMask.ReleaseAccess( pMskAcc );
543 0 : }
544 0 : }
545 :
546 0 : void ImageProducer::initialize( const ::com::sun::star::uno::Sequence< ::com::sun::star::uno::Any >& aArguments ) throw (::com::sun::star::uno::Exception, ::com::sun::star::uno::RuntimeException)
547 : {
548 0 : if ( aArguments.getLength() == 1 )
549 : {
550 0 : ::com::sun::star::uno::Any aArg = aArguments.getConstArray()[0];
551 0 : OUString aURL;
552 0 : if ( aArg >>= aURL )
553 : {
554 0 : SetImage( aURL );
555 0 : }
556 : }
557 0 : }
558 :
559 : namespace frm
560 : {
561 : ::com::sun::star::uno::Reference< ::com::sun::star::uno::XInterface >
562 0 : SAL_CALL ImageProducer_CreateInstance(
563 : const ::com::sun::star::uno::Reference< ::com::sun::star::lang::XMultiServiceFactory >& )
564 : {
565 : return ::com::sun::star::uno::Reference < ::com::sun::star::uno::XInterface >(
566 0 : ( ::cppu::OWeakObject* ) new ImageProducer );
567 : }
568 72 : } // namespace frm
569 :
570 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|