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