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 .
21 #include <tools/solar.h>
32 #include <vcl/bmpacc.hxx>
34 #include <vcl/FilterConfigItem.hxx>
35 #include <vcl/graphicfilter.hxx>
41 using namespace ::com::sun::star
;
43 #define JPEGMINREAD 512
49 // ------------------------------------------------------------------------
51 extern "C" void* CreateBitmap( void* pJPEGReader
, void* pJPEGCreateBitmapParam
)
53 return ( (JPEGReader
*) pJPEGReader
)->CreateBitmap( pJPEGCreateBitmapParam
);
56 // ------------------------------------------------------------------------
58 extern "C" void* GetScanline( void* pJPEGWriter
, long nY
)
60 return ( (JPEGWriter
*) pJPEGWriter
)->GetScanline( nY
);
63 // ------------------------------------------------------------------------
65 struct JPEGCallbackStruct
67 uno::Reference
< task::XStatusIndicator
> xStatusIndicator
;
70 extern "C" long JPEGCallback( void* pCallbackData
, long nPercent
)
72 JPEGCallbackStruct
* pS
= (JPEGCallbackStruct
*)pCallbackData
;
73 if ( pS
&& pS
->xStatusIndicator
.is() )
75 pS
->xStatusIndicator
->setValue( nPercent
);
84 struct jpeg_destination_mgr pub
; /* public fields */
86 SvStream
* outfile
; /* target stream */
87 JOCTET
* buffer
; /* start of buffer */
90 typedef my_destination_mgr
* my_dest_ptr
;
92 extern "C" void init_destination (j_compress_ptr cinfo
)
94 my_dest_ptr dest
= (my_dest_ptr
) cinfo
->dest
;
96 /* Allocate the output buffer --- it will be released when done with image */
97 dest
->buffer
= (JOCTET
*)
98 (*cinfo
->mem
->alloc_small
) ((j_common_ptr
) cinfo
, JPOOL_IMAGE
,
99 BUF_SIZE
* sizeof(JOCTET
));
101 dest
->pub
.next_output_byte
= dest
->buffer
;
102 dest
->pub
.free_in_buffer
= BUF_SIZE
;
105 extern "C" boolean
empty_output_buffer (j_compress_ptr cinfo
)
107 my_dest_ptr dest
= (my_dest_ptr
) cinfo
->dest
;
109 if (dest
->outfile
->Write(dest
->buffer
, BUF_SIZE
) !=
111 ERREXIT(cinfo
, JERR_FILE_WRITE
);
113 dest
->pub
.next_output_byte
= dest
->buffer
;
114 dest
->pub
.free_in_buffer
= BUF_SIZE
;
119 extern "C" void term_destination (j_compress_ptr cinfo
)
121 my_dest_ptr dest
= (my_dest_ptr
) cinfo
->dest
;
122 size_t datacount
= BUF_SIZE
- dest
->pub
.free_in_buffer
;
124 /* Write any data remaining in the buffer */
126 if (dest
->outfile
->Write(dest
->buffer
, datacount
) != datacount
)
127 ERREXIT(cinfo
, JERR_FILE_WRITE
);
131 extern "C" void jpeg_svstream_dest (j_compress_ptr cinfo
, void* out
)
133 SvStream
* outfile
= (SvStream
*)out
;
136 /* The destination object is made permanent so that multiple JPEG images
137 * can be written to the same file without re-executing jpeg_svstream_dest.
138 * This makes it dangerous to use this manager and a different destination
139 * manager serially with the same JPEG object, because their private object
140 * sizes may be different. Caveat programmer.
142 if (cinfo
->dest
== NULL
) { /* first time for this JPEG object? */
143 cinfo
->dest
= (struct jpeg_destination_mgr
*)
144 (*cinfo
->mem
->alloc_small
) ((j_common_ptr
) cinfo
, JPOOL_PERMANENT
,
145 sizeof(my_destination_mgr
));
148 dest
= (my_dest_ptr
) cinfo
->dest
;
149 dest
->pub
.init_destination
= init_destination
;
150 dest
->pub
.empty_output_buffer
= empty_output_buffer
;
151 dest
->pub
.term_destination
= term_destination
;
152 dest
->outfile
= outfile
;
155 /* Expanded data source object for stdio input */
158 struct jpeg_source_mgr pub
; /* public fields */
160 SvStream
* infile
; /* source stream */
161 JOCTET
* buffer
; /* start of buffer */
162 boolean start_of_file
; /* have we gotten any data yet? */
165 typedef my_source_mgr
* my_src_ptr
;
168 * Initialize source --- called by jpeg_read_header
169 * before any data is actually read.
172 extern "C" void init_source (j_decompress_ptr cinfo
)
174 my_src_ptr src
= (my_src_ptr
) cinfo
->src
;
176 /* We reset the empty-input-file flag for each image,
177 * but we don't clear the input buffer.
178 * This is correct behavior for reading a series of images from one source.
180 src
->start_of_file
= sal_True
;
183 long StreamRead( SvStream
* pSvStm
, void* pBuffer
, long nBufferSize
)
187 if( pSvStm
->GetError() != ERRCODE_IO_PENDING
)
189 long nActPos
= pSvStm
->Tell();
191 nRead
= (long) pSvStm
->Read( pBuffer
, nBufferSize
);
193 if( pSvStm
->GetError() == ERRCODE_IO_PENDING
)
195 // Damit wir wieder an die alte Position
196 // seeken koennen, setzen wir den Error temp.zurueck
197 pSvStm
->ResetError();
198 pSvStm
->Seek( nActPos
);
199 pSvStm
->SetError( ERRCODE_IO_PENDING
);
206 extern "C" boolean
fill_input_buffer (j_decompress_ptr cinfo
)
208 my_src_ptr src
= (my_src_ptr
) cinfo
->src
;
211 nbytes
= StreamRead(src
->infile
, src
->buffer
, BUF_SIZE
);
214 if (src
->start_of_file
) /* Treat empty input file as fatal error */
215 ERREXIT(cinfo
, JERR_INPUT_EMPTY
);
216 WARNMS(cinfo
, JWRN_JPEG_EOF
);
217 /* Insert a fake EOI marker */
218 src
->buffer
[0] = (JOCTET
) 0xFF;
219 src
->buffer
[1] = (JOCTET
) JPEG_EOI
;
223 src
->pub
.next_input_byte
= src
->buffer
;
224 src
->pub
.bytes_in_buffer
= nbytes
;
225 src
->start_of_file
= sal_False
;
230 extern "C" void skip_input_data (j_decompress_ptr cinfo
, long num_bytes
)
232 my_src_ptr src
= (my_src_ptr
) cinfo
->src
;
234 /* Just a dumb implementation for now. Could use fseek() except
235 * it doesn't work on pipes. Not clear that being smart is worth
236 * any trouble anyway --- large skips are infrequent.
239 while (num_bytes
> (long) src
->pub
.bytes_in_buffer
) {
240 num_bytes
-= (long) src
->pub
.bytes_in_buffer
;
241 (void) fill_input_buffer(cinfo
);
242 /* note we assume that fill_input_buffer will never return sal_False,
243 * so suspension need not be handled.
246 src
->pub
.next_input_byte
+= (size_t) num_bytes
;
247 src
->pub
.bytes_in_buffer
-= (size_t) num_bytes
;
251 extern "C" void term_source (j_decompress_ptr
)
253 /* no work necessary here */
256 extern "C" void jpeg_svstream_src (j_decompress_ptr cinfo
, void * in
)
259 SvStream
* infile
= (SvStream
*)in
;
261 /* The source object and input buffer are made permanent so that a series
262 * of JPEG images can be read from the same file by calling jpeg_stdio_src
263 * only before the first one. (If we discarded the buffer at the end of
264 * one image, we'd likely lose the start of the next one.)
265 * This makes it unsafe to use this manager and a different source
266 * manager serially with the same JPEG object. Caveat programmer.
268 if (cinfo
->src
== NULL
) { /* first time for this JPEG object? */
269 cinfo
->src
= (struct jpeg_source_mgr
*)
270 (*cinfo
->mem
->alloc_small
) ((j_common_ptr
) cinfo
, JPOOL_PERMANENT
,
271 sizeof(my_source_mgr
));
272 src
= (my_src_ptr
) cinfo
->src
;
273 src
->buffer
= (JOCTET
*)
274 (*cinfo
->mem
->alloc_small
) ((j_common_ptr
) cinfo
, JPOOL_PERMANENT
,
275 BUF_SIZE
* sizeof(JOCTET
));
278 src
= (my_src_ptr
) cinfo
->src
;
279 src
->pub
.init_source
= init_source
;
280 src
->pub
.fill_input_buffer
= fill_input_buffer
;
281 src
->pub
.skip_input_data
= skip_input_data
;
282 src
->pub
.resync_to_restart
= jpeg_resync_to_restart
; /* use default method */
283 src
->pub
.term_source
= term_source
;
284 src
->infile
= infile
;
285 src
->pub
.bytes_in_buffer
= 0; /* forces fill_input_buffer on first read */
286 src
->pub
.next_input_byte
= NULL
; /* until buffer loaded */
293 JPEGReader::JPEGReader( SvStream
& rStm
, void* /*pCallData*/, sal_Bool bSetLS
) :
298 nLastPos ( rStm
.Tell() ),
300 bSetLogSize ( bSetLS
)
302 maUpperName
= rtl::OUString("SVIJPEG");
303 nFormerPos
= nLastPos
;
306 // ------------------------------------------------------------------------
308 JPEGReader::~JPEGReader()
311 rtl_freeMemory( pBuffer
);
314 aBmp
.ReleaseAccess( pAcc
);
317 aBmp1
.ReleaseAccess( pAcc1
);
320 // ------------------------------------------------------------------------
322 void* JPEGReader::CreateBitmap( void* _pParam
)
324 JPEGCreateBitmapParam
*pParam
= (JPEGCreateBitmapParam
*) _pParam
;
326 if (pParam
->nWidth
> SAL_MAX_INT32
/8 || pParam
->nHeight
> SAL_MAX_INT32
/8)
327 return NULL
; // avoid overflows later
329 Size
aSize( pParam
->nWidth
, pParam
->nHeight
);
330 sal_Bool bGray
= pParam
->bGray
!= 0;
332 void* pBmpBuf
= NULL
;
335 aBmp
.ReleaseAccess( pAcc
);
337 sal_uInt64 nSize
= aSize
.Width();
338 nSize
*= aSize
.Height();
339 if (nSize
> SAL_MAX_INT32
/ 24)
344 BitmapPalette
aGrayPal( 256 );
346 for( sal_uInt16 n
= 0; n
< 256; n
++ )
348 const sal_uInt8 cGray
= (sal_uInt8
) n
;
349 aGrayPal
[ n
] = BitmapColor( cGray
, cGray
, cGray
);
352 aBmp
= Bitmap( aSize
, 8, &aGrayPal
);
355 aBmp
= Bitmap( aSize
, 24 );
359 unsigned long nUnit
= ((JPEGCreateBitmapParam
*)pParam
)->density_unit
;
361 if( ( ( 1 == nUnit
) || ( 2 == nUnit
) ) &&
362 pParam
->X_density
&& pParam
->Y_density
)
365 Fraction
aFractX( 1, pParam
->X_density
);
366 Fraction
aFractY( 1, pParam
->Y_density
);
367 MapMode
aMapMode( nUnit
== 1 ? MAP_INCH
: MAP_CM
, aEmptyPoint
, aFractX
, aFractY
);
368 Size aPrefSize
= OutputDevice::LogicToLogic( aSize
, aMapMode
, MAP_100TH_MM
);
370 aBmp
.SetPrefSize( aPrefSize
);
371 aBmp
.SetPrefMapMode( MapMode( MAP_100TH_MM
) );
375 pAcc
= aBmp
.AcquireWriteAccess();
379 const sal_uLong nFormat
= pAcc
->GetScanlineFormat();
382 ( bGray
&& ( BMP_FORMAT_8BIT_PAL
== nFormat
) ) ||
383 ( !bGray
&& ( BMP_FORMAT_24BIT_TC_RGB
== nFormat
) )
386 pBmpBuf
= pAcc
->GetBuffer();
387 pParam
->nAlignedWidth
= pAcc
->GetScanlineSize();
388 pParam
->bTopDown
= pAcc
->IsTopDown();
392 pParam
->nAlignedWidth
= AlignedWidth4Bytes( aSize
.Width() * ( bGray
? 8 : 24 ) );
393 pParam
->bTopDown
= sal_True
;
394 pBmpBuf
= pBuffer
= rtl_allocateMemory( pParam
->nAlignedWidth
* aSize
.Height() );
398 // clean up, if no Bitmap buffer can be provided.
401 aBmp
.ReleaseAccess( pAcc
);
408 // ------------------------------------------------------------------------
410 void JPEGReader::FillBitmap()
412 if( pBuffer
&& pAcc
)
417 long nWidth
= pAcc
->Width();
418 long nHeight
= pAcc
->Height();
420 if( pAcc
->GetBitCount() == 8 )
422 BitmapColor
* pCols
= new BitmapColor
[ 256 ];
424 for( sal_uInt16 n
= 0; n
< 256; n
++ )
426 const sal_uInt8 cGray
= (sal_uInt8
) n
;
427 pCols
[ n
] = pAcc
->GetBestMatchingColor( BitmapColor( cGray
, cGray
, cGray
) );
430 nAlignedWidth
= AlignedWidth4Bytes( pAcc
->Width() * 8L );
432 for( long nY
= 0L; nY
< nHeight
; nY
++ )
434 pTmp
= (sal_uInt8
*) pBuffer
+ nY
* nAlignedWidth
;
436 for( long nX
= 0L; nX
< nWidth
; nX
++ )
437 pAcc
->SetPixel( nY
, nX
, pCols
[ *pTmp
++ ] );
444 nAlignedWidth
= AlignedWidth4Bytes( pAcc
->Width() * 24L );
446 for( long nY
= 0L; nY
< nHeight
; nY
++ )
448 pTmp
= (sal_uInt8
*) pBuffer
+ nY
* nAlignedWidth
;
450 for( long nX
= 0L; nX
< nWidth
; nX
++ )
452 aColor
.SetRed( *pTmp
++ );
453 aColor
.SetGreen( *pTmp
++ );
454 aColor
.SetBlue( *pTmp
++ );
455 pAcc
->SetPixel( nY
, nX
, aColor
);
462 // ------------------------------------------------------------------------
464 Graphic
JPEGReader::CreateIntermediateGraphic( const Bitmap
& rBitmap
, long nLines
)
467 const Size
aSizePix( rBitmap
.GetSizePixel() );
472 aBmp1
.ReleaseAccess( pAcc1
);
474 aBmp1
= Bitmap( rBitmap
.GetSizePixel(), 1 );
475 aBmp1
.Erase( Color( COL_WHITE
) );
476 pAcc1
= aBmp1
.AcquireWriteAccess();
479 if( nLines
&& ( nLines
< aSizePix
.Height() ) )
483 const long nNewLines
= nLines
- nLastLines
;
487 pAcc1
->SetFillColor( Color( COL_BLACK
) );
488 pAcc1
->FillRect( Rectangle( Point( 0, nLastLines
),
489 Size( pAcc1
->Width(), nNewLines
) ) );
492 aBmp1
.ReleaseAccess( pAcc1
);
493 aGraphic
= BitmapEx( rBitmap
, aBmp1
);
494 pAcc1
= aBmp1
.AcquireWriteAccess();
507 // ------------------------------------------------------------------------
509 ReadState
JPEGReader::Read( Graphic
& rGraphic
)
513 ReadState eReadState
;
514 sal_Bool bRet
= sal_False
;
517 #if 1 // TODO: is it possible to get rid of this seek to the end?
518 // check if the stream's end is already available
519 rIStm
.Seek( STREAM_SEEK_TO_END
);
521 nEndPos
= rIStm
.Tell();
523 // else check if at least JPEGMINREAD bytes can be read
524 if( rIStm
.GetError() == ERRCODE_IO_PENDING
)
527 if( ( nEndPos
- nFormerPos
) < JPEGMINREAD
)
529 rIStm
.Seek( nLastPos
);
530 return JPEGREAD_NEED_MORE
;
534 // seek back to the original position
535 rIStm
.Seek( nLastPos
);
538 Size aPreviewSize
= GetPreviewSize();
539 SetJpegPreviewSizeHint( aPreviewSize
.Width(), aPreviewSize
.Height() );
541 // read the (partial) image
542 ReadJPEG( this, &rIStm
, &nLines
);
549 rtl_freeMemory( pBuffer
);
553 aBmp
.ReleaseAccess( pAcc
);
556 if( rIStm
.GetError() == ERRCODE_IO_PENDING
)
557 rGraphic
= CreateIntermediateGraphic( aBmp
, nLines
);
563 else if( rIStm
.GetError() == ERRCODE_IO_PENDING
)
566 // Status setzen ( Pending hat immer Vorrang )
567 if( rIStm
.GetError() == ERRCODE_IO_PENDING
)
569 eReadState
= JPEGREAD_NEED_MORE
;
571 nFormerPos
= rIStm
.Tell();
576 eReadState
= JPEGREAD_OK
;
578 eReadState
= JPEGREAD_ERROR
;
589 JPEGWriter::JPEGWriter( SvStream
& rStm
, const uno::Sequence
< beans::PropertyValue
>* pFilterData
, bool* pExportWasGrey
) :
593 pExpWasGrey ( pExportWasGrey
)
595 FilterConfigItem
aConfigItem( (uno::Sequence
< beans::PropertyValue
>*)pFilterData
);
596 bGreys
= aConfigItem
.ReadInt32( String( RTL_CONSTASCII_USTRINGPARAM( "ColorMode" ) ), 0 ) != 0;
597 nQuality
= aConfigItem
.ReadInt32( String( RTL_CONSTASCII_USTRINGPARAM( "Quality" ) ), 75 );
601 int nArgs
= pFilterData
->getLength();
602 const beans::PropertyValue
* pValues
= pFilterData
->getConstArray();
605 if ( pValues
->Name
== "StatusIndicator" )
607 pValues
->Value
>>= xStatusIndicator
;
614 // ------------------------------------------------------------------------
616 void* JPEGWriter::GetScanline( long nY
)
618 void* pScanline
= NULL
;
623 pScanline
= pAcc
->GetScanline( nY
);
627 long nWidth
= pAcc
->Width();
628 sal_uInt8
* pTmp
= pBuffer
;
630 if( pAcc
->HasPalette() )
632 for( long nX
= 0L; nX
< nWidth
; nX
++ )
634 aColor
= pAcc
->GetPaletteColor( (sal_uInt8
) pAcc
->GetPixel( nY
, nX
) );
635 *pTmp
++ = aColor
.GetRed();
638 *pTmp
++ = aColor
.GetGreen();
639 *pTmp
++ = aColor
.GetBlue();
644 for( long nX
= 0L; nX
< nWidth
; nX
++ )
646 aColor
= pAcc
->GetPixel( nY
, nX
);
647 *pTmp
++ = aColor
.GetRed();
650 *pTmp
++ = aColor
.GetGreen();
651 *pTmp
++ = aColor
.GetBlue();
662 // ------------------------------------------------------------------------
664 sal_Bool
JPEGWriter::Write( const Graphic
& rGraphic
)
666 sal_Bool bRet
= sal_False
;
668 if ( xStatusIndicator
.is() )
671 xStatusIndicator
->start( aMsg
, 100 );
674 Bitmap
aGraphicBmp( rGraphic
.GetBitmap() );
678 if ( !aGraphicBmp
.Convert( BMP_CONVERSION_8BIT_GREYS
) )
679 aGraphicBmp
= rGraphic
.GetBitmap();
682 pAcc
= aGraphicBmp
.AcquireReadAccess();
684 if ( !bGreys
) // bitmap was not explicitly converted into greyscale,
685 { // check if source is greyscale only
687 sal_Bool bIsGrey
= sal_True
;
689 long nWidth
= pAcc
->Width();
690 for ( long nY
= 0; bIsGrey
&& ( nY
< pAcc
->Height() ); nY
++ )
693 for( long nX
= 0L; bIsGrey
&& ( nX
< nWidth
); nX
++ )
695 aColor
= pAcc
->HasPalette() ? pAcc
->GetPaletteColor( (sal_uInt8
) pAcc
->GetPixel( nY
, nX
) )
696 : pAcc
->GetPixel( nY
, nX
);
697 bIsGrey
= ( aColor
.GetRed() == aColor
.GetGreen() ) && ( aColor
.GetRed() == aColor
.GetBlue() );
705 *pExpWasGrey
= bGreys
;
709 bNative
= ( pAcc
->GetScanlineFormat() == BMP_FORMAT_24BIT_TC_RGB
);
712 pBuffer
= new sal_uInt8
[ AlignedWidth4Bytes( bGreys
? pAcc
->Width() * 8L : pAcc
->Width() * 24L ) ];
714 JPEGCallbackStruct aCallbackData
;
715 aCallbackData
.xStatusIndicator
= xStatusIndicator
;
716 bRet
= (sal_Bool
) WriteJPEG( this, &rOStm
, pAcc
->Width(), pAcc
->Height(), bGreys
, nQuality
, &aCallbackData
);
721 aGraphicBmp
.ReleaseAccess( pAcc
);
724 if ( xStatusIndicator
.is() )
725 xStatusIndicator
->end();
734 sal_Bool
ImportJPEG( SvStream
& rStm
, Graphic
& rGraphic
, void* pCallerData
, sal_Int32 nImportFlags
)
736 JPEGReader
* pJPEGReader
= (JPEGReader
*) rGraphic
.GetContext();
737 ReadState eReadState
;
738 sal_Bool bRet
= sal_True
;
741 pJPEGReader
= new JPEGReader( rStm
, pCallerData
, ( nImportFlags
& GRFILTER_I_FLAGS_SET_LOGSIZE_FOR_JPEG
) != 0 );
743 if( nImportFlags
& GRFILTER_I_FLAGS_FOR_PREVIEW
)
744 pJPEGReader
->SetPreviewSize( Size(128,128) );
746 pJPEGReader
->DisablePreviewMode();
748 rGraphic
.SetContext( NULL
);
749 eReadState
= pJPEGReader
->Read( rGraphic
);
751 if( eReadState
== JPEGREAD_ERROR
)
756 else if( eReadState
== JPEGREAD_OK
)
759 rGraphic
.SetContext( pJPEGReader
);
768 sal_Bool
ExportJPEG( SvStream
& rOStm
, const Graphic
& rGraphic
,
769 const ::com::sun::star::uno::Sequence
< ::com::sun::star::beans::PropertyValue
>* pFilterData
,
773 JPEGWriter
aJPEGWriter( rOStm
, pFilterData
, pExportWasGrey
);
774 return aJPEGWriter
.Write( rGraphic
);
777 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */