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