1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include "imgprod.hxx"
22 #include <osl/diagnose.h>
23 #include <tools/debug.hxx>
24 #include <vcl/bitmapaccess.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>
32 #include <svtools/imageresourceaccess.hxx>
33 #include <comphelper/processfactory.hxx>
37 class ImgProdLockBytes
: public SvLockBytes
39 css::uno::Reference
< css::io::XInputStream
> xStmRef
;
40 css::uno::Sequence
<sal_Int8
> maSeq
;
44 ImgProdLockBytes( SvStream
* pStm
, bool bOwner
);
45 explicit ImgProdLockBytes( css::uno::Reference
< css::io::XInputStream
> const & rStreamRef
);
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
;
56 ImgProdLockBytes::ImgProdLockBytes( SvStream
* pStm
, bool bOwner
) :
57 SvLockBytes( pStm
, bOwner
)
62 ImgProdLockBytes::ImgProdLockBytes( css::uno::Reference
< css::io::XInputStream
> const & rStmRef
) :
68 const sal_uInt32 nBytesToRead
= 65535;
73 css::uno::Sequence
< sal_Int8
> aReadSeq
;
75 nRead
= xStmRef
->readSomeBytes( aReadSeq
, nBytesToRead
);
79 const sal_uInt32 nOldLength
= maSeq
.getLength();
80 maSeq
.realloc( nOldLength
+ nRead
);
81 memcpy( maSeq
.getArray() + nOldLength
, aReadSeq
.getConstArray(), aReadSeq
.getLength() );
84 while( nBytesToRead
== nRead
);
87 ErrCode
ImgProdLockBytes::ReadAt(sal_uInt64
const nPos
,
88 void* pBuffer
, std::size_t nCount
, std::size_t * pRead
) const
92 const_cast<SvStream
*>(GetStream())->ResetError();
93 const ErrCode nErr
= SvLockBytes::ReadAt( nPos
, pBuffer
, nCount
, pRead
);
94 const_cast<SvStream
*>(GetStream())->ResetError();
99 const std::size_t nSeqLen
= maSeq
.getLength();
103 if( ( nPos
+ nCount
) > nSeqLen
)
104 nCount
= nSeqLen
- nPos
;
106 memcpy( pBuffer
, maSeq
.getConstArray() + nPos
, nCount
);
117 ErrCode
ImgProdLockBytes::WriteAt(sal_uInt64
const nPos
,
118 const void* pBuffer
, std::size_t nCount
, std::size_t * pWritten
)
121 return SvLockBytes::WriteAt( nPos
, pBuffer
, nCount
, pWritten
);
124 DBG_ASSERT( xStmRef
.is(), "ImgProdLockBytes::WriteAt: xInputStream has no reference..." );
125 return ERRCODE_IO_CANTWRITE
;
130 ErrCode
ImgProdLockBytes::Flush() const
136 ErrCode
ImgProdLockBytes::SetSize(sal_uInt64
const nSize
)
139 return SvLockBytes::SetSize( nSize
);
142 OSL_FAIL( "ImgProdLockBytes::SetSize not supported for xInputStream..." );
143 return ERRCODE_IO_CANTWRITE
;
148 ErrCode
ImgProdLockBytes::Stat( SvLockBytesStat
* pStat
) const
151 return SvLockBytes::Stat( pStat
);
154 DBG_ASSERT( xStmRef
.is(), "ImgProdLockBytes::Stat: xInputStream has no reference..." );
155 pStat
->nSize
= maSeq
.getLength();
161 ImageProducer::ImageProducer()
165 mpGraphic
.reset( new Graphic
);
168 ImageProducer::~ImageProducer()
174 css::uno::Any
ImageProducer::queryInterface( const css::uno::Type
& rType
)
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
));
183 void ImageProducer::addConsumer( const css::uno::Reference
< css::awt::XImageConsumer
>& rxConsumer
)
185 DBG_ASSERT( rxConsumer
.is(), "::AddConsumer(...): No consumer referenced!" );
186 if( rxConsumer
.is() )
187 maConsList
.push_back( rxConsumer
);
191 void ImageProducer::removeConsumer( const css::uno::Reference
< css::awt::XImageConsumer
>& rxConsumer
)
193 ConsumerList_t::reverse_iterator riter
= std::find(maConsList
.rbegin(),maConsList
.rend(),rxConsumer
);
195 if (riter
!= maConsList
.rend())
196 maConsList
.erase(riter
.base()-1);
200 void ImageProducer::SetImage( const OUString
& rPath
)
207 if ( ::svt::GraphicAccess::isSupportedURL( maURL
) )
209 mpStm
= ::svt::GraphicAccess::getImageStream( ::comphelper::getProcessComponentContext(), maURL
);
211 else if( !maURL
.isEmpty() )
213 std::unique_ptr
<SvStream
> pIStm
= ::utl::UcbStreamHelper::CreateStream( maURL
, StreamMode::STD_READ
);
215 mpStm
.reset( new SvStream( new ImgProdLockBytes( pIStm
.release(), true ) ) );
220 void ImageProducer::SetImage( SvStream
& rStm
)
226 mpStm
.reset( new SvStream( new ImgProdLockBytes( &rStm
, false ) ) );
230 void ImageProducer::setImage( css::uno::Reference
< css::io::XInputStream
> const & rInputStmRef
)
237 if( rInputStmRef
.is() )
238 mpStm
.reset( new SvStream( new ImgProdLockBytes( rInputStmRef
) ) );
242 void ImageProducer::NewDataAvailable()
244 if( ( GraphicType::NONE
== mpGraphic
->GetType() ) || mpGraphic
->GetReaderContext() )
249 void ImageProducer::startProduction()
251 if( maConsList
.empty() && !maDoneHdl
.IsSet() )
254 bool bNotifyEmptyGraphics
= false;
256 // valid stream or filled graphic? => update consumers
257 if( mpStm
|| ( mpGraphic
->GetType() != GraphicType::NONE
) )
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() )
263 if ( ImplImportGraphic( *mpGraphic
) )
264 maDoneHdl
.Call( mpGraphic
.get() );
267 if( mpGraphic
->GetType() != GraphicType::NONE
)
268 ImplUpdateData( *mpGraphic
);
270 bNotifyEmptyGraphics
= true;
273 bNotifyEmptyGraphics
= true;
275 if ( !bNotifyEmptyGraphics
)
279 // create temporary list to hold interfaces
280 ConsumerList_t aTmp
= maConsList
;
282 // iterate through interfaces
283 for (auto const& elem
: aTmp
)
286 elem
->complete( css::awt::ImageStatus::IMAGESTATUS_STATICIMAGEDONE
, this );
289 maDoneHdl
.Call( nullptr );
293 bool ImageProducer::ImplImportGraphic( Graphic
& rGraphic
)
298 if( ERRCODE_IO_PENDING
== mpStm
->GetError() )
303 bool bRet
= GraphicConverter::Import( *mpStm
, rGraphic
) == ERRCODE_NONE
;
305 if( ERRCODE_IO_PENDING
== mpStm
->GetError() )
312 void ImageProducer::ImplUpdateData( const Graphic
& rGraphic
)
314 ImplInitConsumer( rGraphic
);
316 if( mbConsInit
&& !maConsList
.empty() )
318 // create temporary list to hold interfaces
319 ConsumerList_t aTmp
= maConsList
;
321 ImplUpdateConsumer( rGraphic
);
324 // iterate through interfaces
325 for (auto const& elem
: aTmp
)
326 elem
->complete( css::awt::ImageStatus::IMAGESTATUS_STATICIMAGEDONE
, this );
331 void ImageProducer::ImplInitConsumer( const Graphic
& rGraphic
)
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
);
343 // create temporary list to hold interfaces
344 ConsumerList_t aTmp
= maConsList
;
346 // iterate through interfaces
347 for (auto const& elem
: aTmp
)
349 elem
->init( nWidth
, nHeight
);
350 elem
->setColorModel( nBitCount
,aRGBPal
, nRMask
, nGMask
, nBMask
, nAMask
);
357 void ImageProducer::ImplUpdateConsumer( const Graphic
& rGraphic
)
359 BitmapEx
aBmpEx( rGraphic
.GetBitmapEx() );
360 Bitmap
aBmp( aBmpEx
.GetBitmap() );
361 BitmapReadAccess
* pBmpAcc
= aBmp
.AcquireReadAccess();
366 Bitmap
aMask( aBmpEx
.GetMask() );
367 BitmapReadAccess
* pMskAcc
= !!aMask
? 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;
379 aMask
= Bitmap( aBmp
.GetSizePixel(), 1 );
380 aMask
.Erase( COL_BLACK
);
381 pMskAcc
= aMask
.AcquireReadAccess();
384 // create temporary list to hold interfaces
385 ConsumerList_t aTmp
= maConsList
;
387 if( pBmpAcc
->HasPalette() )
389 const BitmapColor
aWhite( pMskAcc
->GetBestMatchingColor( COL_WHITE
) );
391 if( mnTransIndex
< 256 )
393 css::uno::Sequence
<sal_Int8
> aData( nPartWidth
* nPartHeight
);
394 sal_Int8
* pTmp
= aData
.getArray();
396 for( tools::Long nY
= nStartY
; nY
<= nEndY
; nY
++ )
398 Scanline pScanlineMask
= pMskAcc
->GetScanline( nY
);
399 Scanline pScanline
= pBmpAcc
->GetScanline( nY
);
400 for( tools::Long nX
= nStartX
; nX
<= nEndX
; nX
++ )
402 if( pMskAcc
->GetPixelFromData( pScanlineMask
, nX
) == aWhite
)
403 *pTmp
++ = sal::static_int_cast
< sal_Int8
>(
406 *pTmp
++ = pBmpAcc
->GetPixelFromData( pScanline
, nX
).GetIndex();
410 // iterate through interfaces
411 for (auto const& elem
: aTmp
)
412 elem
->setPixelsByBytes( nStartX
, nStartY
, nPartWidth
, nPartHeight
, aData
, 0UL, nPartWidth
);
416 css::uno::Sequence
<sal_Int32
> aData( nPartWidth
* nPartHeight
);
417 sal_Int32
* pTmp
= aData
.getArray();
419 for( tools::Long nY
= nStartY
; nY
<= nEndY
; nY
++ )
421 Scanline pScanlineMask
= pMskAcc
->GetScanline( nY
);
422 Scanline pScanline
= pBmpAcc
->GetScanline( nY
);
423 for( tools::Long nX
= nStartX
; nX
<= nEndX
; nX
++ )
425 if( pMskAcc
->GetPixelFromData( pScanlineMask
, nX
) == aWhite
)
426 *pTmp
++ = mnTransIndex
;
428 *pTmp
++ = pBmpAcc
->GetPixelFromData( pScanline
, nX
).GetIndex();
432 // iterate through interfaces
433 for (auto const& elem
: aTmp
)
434 elem
->setPixelsByLongs( nStartX
, nStartY
, nPartWidth
, nPartHeight
, aData
, 0UL, nPartWidth
);
439 css::uno::Sequence
<sal_Int32
> aData( nPartWidth
* nPartHeight
);
440 const BitmapColor
aWhite( pMskAcc
->GetBestMatchingColor( COL_WHITE
) );
441 sal_Int32
* pTmp
= aData
.getArray();
443 for( tools::Long nY
= nStartY
; nY
<= nEndY
; nY
++ )
445 Scanline pScanlineMask
= pMskAcc
->GetScanline( nY
);
446 Scanline pScanline
= pBmpAcc
->GetScanline( nY
);
447 for( tools::Long nX
= nStartX
; nX
<= nEndX
; nX
++, pTmp
++ )
449 const BitmapColor
aCol( pBmpAcc
->GetPixelFromData( pScanline
, nX
) );
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;
455 if( pMskAcc
->GetPixelFromData( pScanlineMask
, nX
) != aWhite
)
456 *pTmp
|= 0x000000ffUL
;
460 // iterate through interfaces
461 for (auto const& elem
: aTmp
)
462 elem
->setPixelsByLongs( nStartX
, nStartY
, nPartWidth
, nPartHeight
, aData
, 0UL, nPartWidth
);
465 Bitmap::ReleaseAccess( pBmpAcc
);
466 Bitmap::ReleaseAccess( pMskAcc
);
470 void ImageProducer::initialize( const css::uno::Sequence
< css::uno::Any
>& aArguments
)
472 if ( aArguments
.getLength() == 1 )
474 css::uno::Any aArg
= aArguments
.getConstArray()[0];
484 extern "C" SAL_DLLPUBLIC_EXPORT
css::uno::XInterface
*
485 com_sun_star_form_ImageProducer_get_implementation(css::uno::XComponentContext
*,
486 css::uno::Sequence
<css::uno::Any
> const &)
488 return cppu::acquire(new ImageProducer());
491 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */