1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: jpeg.cxx,v $
12 * This file is part of OpenOffice.org.
14 * OpenOffice.org is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU Lesser General Public License version 3
16 * only, as published by the Free Software Foundation.
18 * OpenOffice.org is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License version 3 for more details
22 * (a copy is included in the LICENSE file that accompanied this code).
24 * You should have received a copy of the GNU Lesser General Public License
25 * version 3 along with OpenOffice.org. If not, see
26 * <http://www.openoffice.org/license.html>
27 * for a copy of the LGPLv3 License.
29 ************************************************************************/
31 // MARKER(update_precomp.py): autogen include statement, do not remove
32 #include "precompiled_svtools.hxx"
34 #include <tools/solar.h>
37 #define INT32 JPEG_INT32
53 #include <vcl/bmpacc.hxx>
55 #include <svtools/FilterConfigItem.hxx>
56 #include <svtools/filter.hxx>
62 using namespace ::com::sun::star
;
64 #define JPEGMINREAD 512
70 // ------------------------------------------------------------------------
72 extern "C" void* CreateBitmap( void* pJPEGReader
, void* pJPEGCreateBitmapParam
)
74 return ( (JPEGReader
*) pJPEGReader
)->CreateBitmap( pJPEGCreateBitmapParam
);
77 // ------------------------------------------------------------------------
79 extern "C" void* GetScanline( void* pJPEGWriter
, long nY
)
81 return ( (JPEGWriter
*) pJPEGWriter
)->GetScanline( nY
);
84 // ------------------------------------------------------------------------
86 struct JPEGCallbackStruct
88 uno::Reference
< task::XStatusIndicator
> xStatusIndicator
;
91 extern "C" long JPEGCallback( void* pCallbackData
, long nPercent
)
93 JPEGCallbackStruct
* pS
= (JPEGCallbackStruct
*)pCallbackData
;
94 if ( pS
&& pS
->xStatusIndicator
.is() )
96 pS
->xStatusIndicator
->setValue( nPercent
);
101 #define BUF_SIZE 4096
105 struct jpeg_destination_mgr pub
; /* public fields */
107 SvStream
* outfile
; /* target stream */
108 JOCTET
* buffer
; /* start of buffer */
109 } my_destination_mgr
;
111 typedef my_destination_mgr
* my_dest_ptr
;
113 extern "C" void init_destination (j_compress_ptr cinfo
)
115 my_dest_ptr dest
= (my_dest_ptr
) cinfo
->dest
;
117 /* Allocate the output buffer --- it will be released when done with image */
118 dest
->buffer
= (JOCTET
*)
119 (*cinfo
->mem
->alloc_small
) ((j_common_ptr
) cinfo
, JPOOL_IMAGE
,
120 BUF_SIZE
* sizeof(JOCTET
));
122 dest
->pub
.next_output_byte
= dest
->buffer
;
123 dest
->pub
.free_in_buffer
= BUF_SIZE
;
126 extern "C" int empty_output_buffer (j_compress_ptr cinfo
)
128 my_dest_ptr dest
= (my_dest_ptr
) cinfo
->dest
;
130 if (dest
->outfile
->Write(dest
->buffer
, BUF_SIZE
) !=
132 ERREXIT(cinfo
, JERR_FILE_WRITE
);
134 dest
->pub
.next_output_byte
= dest
->buffer
;
135 dest
->pub
.free_in_buffer
= BUF_SIZE
;
140 extern "C" void term_destination (j_compress_ptr cinfo
)
142 my_dest_ptr dest
= (my_dest_ptr
) cinfo
->dest
;
143 size_t datacount
= BUF_SIZE
- dest
->pub
.free_in_buffer
;
145 /* Write any data remaining in the buffer */
147 if (dest
->outfile
->Write(dest
->buffer
, datacount
) != datacount
)
148 ERREXIT(cinfo
, JERR_FILE_WRITE
);
152 extern "C" void jpeg_svstream_dest (j_compress_ptr cinfo
, void* out
)
154 SvStream
* outfile
= (SvStream
*)out
;
157 /* The destination object is made permanent so that multiple JPEG images
158 * can be written to the same file without re-executing jpeg_svstream_dest.
159 * This makes it dangerous to use this manager and a different destination
160 * manager serially with the same JPEG object, because their private object
161 * sizes may be different. Caveat programmer.
163 if (cinfo
->dest
== NULL
) { /* first time for this JPEG object? */
164 cinfo
->dest
= (struct jpeg_destination_mgr
*)
165 (*cinfo
->mem
->alloc_small
) ((j_common_ptr
) cinfo
, JPOOL_PERMANENT
,
166 sizeof(my_destination_mgr
));
169 dest
= (my_dest_ptr
) cinfo
->dest
;
170 dest
->pub
.init_destination
= init_destination
;
171 dest
->pub
.empty_output_buffer
= empty_output_buffer
;
172 dest
->pub
.term_destination
= term_destination
;
173 dest
->outfile
= outfile
;
176 /* Expanded data source object for stdio input */
179 struct jpeg_source_mgr pub
; /* public fields */
181 SvStream
* infile
; /* source stream */
182 JOCTET
* buffer
; /* start of buffer */
183 boolean start_of_file
; /* have we gotten any data yet? */
186 typedef my_source_mgr
* my_src_ptr
;
189 * Initialize source --- called by jpeg_read_header
190 * before any data is actually read.
193 extern "C" void init_source (j_decompress_ptr cinfo
)
195 my_src_ptr src
= (my_src_ptr
) cinfo
->src
;
197 /* We reset the empty-input-file flag for each image,
198 * but we don't clear the input buffer.
199 * This is correct behavior for reading a series of images from one source.
201 src
->start_of_file
= TRUE
;
204 long StreamRead( SvStream
* pSvStm
, void* pBuffer
, long nBufferSize
)
208 if( pSvStm
->GetError() != ERRCODE_IO_PENDING
)
210 long nActPos
= pSvStm
->Tell();
212 nRead
= (long) pSvStm
->Read( pBuffer
, nBufferSize
);
214 if( pSvStm
->GetError() == ERRCODE_IO_PENDING
)
218 // Damit wir wieder an die alte Position
219 // seeken koennen, setzen wir den Error temp.zurueck
220 pSvStm
->ResetError();
221 pSvStm
->Seek( nActPos
);
222 pSvStm
->SetError( ERRCODE_IO_PENDING
);
231 extern "C" int fill_input_buffer (j_decompress_ptr cinfo
)
233 my_src_ptr src
= (my_src_ptr
) cinfo
->src
;
236 nbytes
= StreamRead(src
->infile
, src
->buffer
, BUF_SIZE
);
239 if (src
->start_of_file
) /* Treat empty input file as fatal error */
240 ERREXIT(cinfo
, JERR_INPUT_EMPTY
);
241 WARNMS(cinfo
, JWRN_JPEG_EOF
);
242 /* Insert a fake EOI marker */
243 src
->buffer
[0] = (JOCTET
) 0xFF;
244 src
->buffer
[1] = (JOCTET
) JPEG_EOI
;
248 src
->pub
.next_input_byte
= src
->buffer
;
249 src
->pub
.bytes_in_buffer
= nbytes
;
250 src
->start_of_file
= FALSE
;
255 extern "C" void skip_input_data (j_decompress_ptr cinfo
, long num_bytes
)
257 my_src_ptr src
= (my_src_ptr
) cinfo
->src
;
259 /* Just a dumb implementation for now. Could use fseek() except
260 * it doesn't work on pipes. Not clear that being smart is worth
261 * any trouble anyway --- large skips are infrequent.
264 while (num_bytes
> (long) src
->pub
.bytes_in_buffer
) {
265 num_bytes
-= (long) src
->pub
.bytes_in_buffer
;
266 (void) fill_input_buffer(cinfo
);
267 /* note we assume that fill_input_buffer will never return FALSE,
268 * so suspension need not be handled.
271 src
->pub
.next_input_byte
+= (size_t) num_bytes
;
272 src
->pub
.bytes_in_buffer
-= (size_t) num_bytes
;
276 extern "C" void term_source (j_decompress_ptr
)
278 /* no work necessary here */
281 extern "C" void jpeg_svstream_src (j_decompress_ptr cinfo
, void * in
)
284 SvStream
* infile
= (SvStream
*)in
;
286 /* The source object and input buffer are made permanent so that a series
287 * of JPEG images can be read from the same file by calling jpeg_stdio_src
288 * only before the first one. (If we discarded the buffer at the end of
289 * one image, we'd likely lose the start of the next one.)
290 * This makes it unsafe to use this manager and a different source
291 * manager serially with the same JPEG object. Caveat programmer.
293 if (cinfo
->src
== NULL
) { /* first time for this JPEG object? */
294 cinfo
->src
= (struct jpeg_source_mgr
*)
295 (*cinfo
->mem
->alloc_small
) ((j_common_ptr
) cinfo
, JPOOL_PERMANENT
,
296 sizeof(my_source_mgr
));
297 src
= (my_src_ptr
) cinfo
->src
;
298 src
->buffer
= (JOCTET
*)
299 (*cinfo
->mem
->alloc_small
) ((j_common_ptr
) cinfo
, JPOOL_PERMANENT
,
300 BUF_SIZE
* sizeof(JOCTET
));
303 src
= (my_src_ptr
) cinfo
->src
;
304 src
->pub
.init_source
= init_source
;
305 src
->pub
.fill_input_buffer
= fill_input_buffer
;
306 src
->pub
.skip_input_data
= skip_input_data
;
307 src
->pub
.resync_to_restart
= jpeg_resync_to_restart
; /* use default method */
308 src
->pub
.term_source
= term_source
;
309 src
->infile
= infile
;
310 src
->pub
.bytes_in_buffer
= 0; /* forces fill_input_buffer on first read */
311 src
->pub
.next_input_byte
= NULL
; /* until buffer loaded */
318 JPEGReader::JPEGReader( SvStream
& rStm
, void* /*pCallData*/, sal_Bool bSetLS
) :
323 nLastPos ( rStm
.Tell() ),
325 bSetLogSize ( bSetLS
)
327 maUpperName
= String::CreateFromAscii( "SVIJPEG", 7 );
328 nFormerPos
= nLastPos
;
331 // ------------------------------------------------------------------------
333 JPEGReader::~JPEGReader()
336 rtl_freeMemory( pBuffer
);
339 aBmp
.ReleaseAccess( pAcc
);
342 aBmp1
.ReleaseAccess( pAcc1
);
345 // ------------------------------------------------------------------------
347 void* JPEGReader::CreateBitmap( void* pParam
)
349 Size
aSize( ((JPEGCreateBitmapParam
*)pParam
)->nWidth
,
350 ((JPEGCreateBitmapParam
*)pParam
)->nHeight
);
351 sal_Bool bGray
= ((JPEGCreateBitmapParam
*)pParam
)->bGray
!= 0;
353 void* pBmpBuf
= NULL
;
356 aBmp
.ReleaseAccess( pAcc
);
360 BitmapPalette
aGrayPal( 256 );
362 for( USHORT n
= 0; n
< 256; n
++ )
364 const BYTE cGray
= (BYTE
) n
;
365 aGrayPal
[ n
] = BitmapColor( cGray
, cGray
, cGray
);
368 aBmp
= Bitmap( aSize
, 8, &aGrayPal
);
371 aBmp
= Bitmap( aSize
, 24 );
375 unsigned long nUnit
= ((JPEGCreateBitmapParam
*)pParam
)->density_unit
;
377 if( ( ( 1 == nUnit
) || ( 2 == nUnit
) ) &&
378 ( (JPEGCreateBitmapParam
*) pParam
)->X_density
&&
379 ( (JPEGCreateBitmapParam
*) pParam
)->Y_density
)
382 Fraction
aFractX( 1, ((JPEGCreateBitmapParam
*)pParam
)->X_density
);
383 Fraction
aFractY( 1, ((JPEGCreateBitmapParam
*)pParam
)->Y_density
);
384 MapMode
aMapMode( nUnit
== 1 ? MAP_INCH
: MAP_CM
, aEmptyPoint
, aFractX
, aFractY
);
385 Size aPrefSize
= OutputDevice::LogicToLogic( aSize
, aMapMode
, MAP_100TH_MM
);
387 aBmp
.SetPrefSize( aPrefSize
);
388 aBmp
.SetPrefMapMode( MapMode( MAP_100TH_MM
) );
392 pAcc
= aBmp
.AcquireWriteAccess();
398 const ULONG nFormat
= pAcc
->GetScanlineFormat();
401 ( bGray
&& ( BMP_FORMAT_8BIT_PAL
== nFormat
) ) ||
403 ( !bGray
&& ( BMP_FORMAT_24BIT_TC_BGR
== nFormat
) )
405 ( !bGray
&& ( BMP_FORMAT_24BIT_TC_RGB
== nFormat
) )
409 pBmpBuf
= pAcc
->GetBuffer();
410 nAlignedWidth
= pAcc
->GetScanlineSize();
411 ((JPEGCreateBitmapParam
*)pParam
)->bTopDown
= pAcc
->IsTopDown();
415 nAlignedWidth
= AlignedWidth4Bytes( aSize
.Width() * ( bGray
? 8 : 24 ) );
416 ((JPEGCreateBitmapParam
*)pParam
)->bTopDown
= TRUE
;
417 pBmpBuf
= pBuffer
= rtl_allocateMemory( nAlignedWidth
* aSize
.Height() );
419 ((JPEGCreateBitmapParam
*)pParam
)->nAlignedWidth
= nAlignedWidth
;
425 // ------------------------------------------------------------------------
427 void JPEGReader::FillBitmap()
429 if( pBuffer
&& pAcc
)
434 long nWidth
= pAcc
->Width();
435 long nHeight
= pAcc
->Height();
437 if( pAcc
->GetBitCount() == 8 )
439 BitmapColor
* pCols
= new BitmapColor
[ 256 ];
441 for( USHORT n
= 0; n
< 256; n
++ )
443 const BYTE cGray
= (BYTE
) n
;
444 pCols
[ n
] = pAcc
->GetBestMatchingColor( BitmapColor( cGray
, cGray
, cGray
) );
447 nAlignedWidth
= AlignedWidth4Bytes( pAcc
->Width() * 8L );
449 for( long nY
= 0L; nY
< nHeight
; nY
++ )
451 pTmp
= (BYTE
*) pBuffer
+ nY
* nAlignedWidth
;
453 for( long nX
= 0L; nX
< nWidth
; nX
++ )
454 pAcc
->SetPixel( nY
, nX
, pCols
[ *pTmp
++ ] );
461 nAlignedWidth
= AlignedWidth4Bytes( pAcc
->Width() * 24L );
463 for( long nY
= 0L; nY
< nHeight
; nY
++ )
465 pTmp
= (BYTE
*) pBuffer
+ nY
* nAlignedWidth
;
467 for( long nX
= 0L; nX
< nWidth
; nX
++ )
470 aColor
.SetBlue( *pTmp
++ );
471 aColor
.SetGreen( *pTmp
++ );
472 aColor
.SetRed( *pTmp
++ );
474 aColor
.SetRed( *pTmp
++ );
475 aColor
.SetGreen( *pTmp
++ );
476 aColor
.SetBlue( *pTmp
++ );
478 pAcc
->SetPixel( nY
, nX
, aColor
);
485 // ------------------------------------------------------------------------
487 Graphic
JPEGReader::CreateIntermediateGraphic( const Bitmap
& rBitmap
, long nLines
)
490 const Size
aSizePix( rBitmap
.GetSizePixel() );
495 aBmp1
.ReleaseAccess( pAcc1
);
497 aBmp1
= Bitmap( rBitmap
.GetSizePixel(), 1 );
498 aBmp1
.Erase( Color( COL_WHITE
) );
499 pAcc1
= aBmp1
.AcquireWriteAccess();
502 if( nLines
&& ( nLines
< aSizePix
.Height() ) )
506 const long nNewLines
= nLines
- nLastLines
;
510 pAcc1
->SetFillColor( Color( COL_BLACK
) );
511 pAcc1
->FillRect( Rectangle( Point( 0, nLastLines
),
512 Size( pAcc1
->Width(), nNewLines
) ) );
515 aBmp1
.ReleaseAccess( pAcc1
);
516 aGraphic
= BitmapEx( rBitmap
, aBmp1
);
517 pAcc1
= aBmp1
.AcquireWriteAccess();
530 // ------------------------------------------------------------------------
532 ReadState
JPEGReader::Read( Graphic
& rGraphic
)
536 ReadState eReadState
;
540 #if 1 // TODO: is it possible to get rid of this seek to the end?
541 // check if the stream's end is already available
542 rIStm
.Seek( STREAM_SEEK_TO_END
);
544 nEndPos
= rIStm
.Tell();
546 // else check if at least JPEGMINREAD bytes can be read
547 if( rIStm
.GetError() == ERRCODE_IO_PENDING
)
550 if( ( nEndPos
- nFormerPos
) < JPEGMINREAD
)
552 rIStm
.Seek( nLastPos
);
553 return JPEGREAD_NEED_MORE
;
557 // seek back to the original position
558 rIStm
.Seek( nLastPos
);
561 Size aPreviewSize
= GetPreviewSize();
562 SetJpegPreviewSizeHint( aPreviewSize
.Width(), aPreviewSize
.Height() );
564 // read the (partial) image
565 ReadJPEG( this, &rIStm
, &nLines
);
572 rtl_freeMemory( pBuffer
);
576 aBmp
.ReleaseAccess( pAcc
);
579 if( rIStm
.GetError() == ERRCODE_IO_PENDING
)
580 rGraphic
= CreateIntermediateGraphic( aBmp
, nLines
);
586 else if( rIStm
.GetError() == ERRCODE_IO_PENDING
)
589 // Status setzen ( Pending hat immer Vorrang )
590 if( rIStm
.GetError() == ERRCODE_IO_PENDING
)
592 eReadState
= JPEGREAD_NEED_MORE
;
594 nFormerPos
= rIStm
.Tell();
599 eReadState
= JPEGREAD_OK
;
601 eReadState
= JPEGREAD_ERROR
;
612 JPEGWriter::JPEGWriter( SvStream
& rStm
, const uno::Sequence
< beans::PropertyValue
>* pFilterData
) :
617 FilterConfigItem
aConfigItem( (uno::Sequence
< beans::PropertyValue
>*)pFilterData
);
618 bGreys
= aConfigItem
.ReadInt32( String( RTL_CONSTASCII_USTRINGPARAM( "ColorMode" ) ), 0 ) != 0;
619 nQuality
= aConfigItem
.ReadInt32( String( RTL_CONSTASCII_USTRINGPARAM( "Quality" ) ), 75 );
623 int nArgs
= pFilterData
->getLength();
624 const beans::PropertyValue
* pValues
= pFilterData
->getConstArray();
627 if( pValues
->Name
.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "StatusIndicator" ) ) )
629 pValues
->Value
>>= xStatusIndicator
;
636 // ------------------------------------------------------------------------
638 void* JPEGWriter::GetScanline( long nY
)
640 void* pScanline
= NULL
;
645 pScanline
= pAcc
->GetScanline( nY
);
649 long nWidth
= pAcc
->Width();
650 BYTE
* pTmp
= pBuffer
;
652 if( pAcc
->HasPalette() )
654 for( long nX
= 0L; nX
< nWidth
; nX
++ )
656 aColor
= pAcc
->GetPaletteColor( (BYTE
) pAcc
->GetPixel( nY
, nX
) );
658 *pTmp
++ = aColor
.GetBlue();
659 *pTmp
++ = aColor
.GetGreen();
660 *pTmp
++ = aColor
.GetRed();
662 *pTmp
++ = aColor
.GetRed();
663 *pTmp
++ = aColor
.GetGreen();
664 *pTmp
++ = aColor
.GetBlue();
670 for( long nX
= 0L; nX
< nWidth
; nX
++ )
672 aColor
= pAcc
->GetPixel( nY
, nX
);
674 *pTmp
++ = aColor
.GetBlue();
675 *pTmp
++ = aColor
.GetGreen();
676 *pTmp
++ = aColor
.GetRed();
678 *pTmp
++ = aColor
.GetRed();
679 *pTmp
++ = aColor
.GetGreen();
680 *pTmp
++ = aColor
.GetBlue();
692 // ------------------------------------------------------------------------
694 BOOL
JPEGWriter::Write( const Graphic
& rGraphic
)
698 if ( xStatusIndicator
.is() )
701 xStatusIndicator
->start( aMsg
, 100 );
704 Bitmap
aGraphicBmp( rGraphic
.GetBitmap() );
708 if ( !aGraphicBmp
.Convert( BMP_CONVERSION_8BIT_GREYS
) )
709 aGraphicBmp
= rGraphic
.GetBitmap();
712 pAcc
= aGraphicBmp
.AcquireReadAccess();
717 bNative
= ( pAcc
->GetScanlineFormat() == BMP_FORMAT_24BIT_TC_BGR
);
719 bNative
= ( pAcc
->GetScanlineFormat() == BMP_FORMAT_24BIT_TC_RGB
);
723 pBuffer
= new BYTE
[ AlignedWidth4Bytes( pAcc
->Width() * 24L ) ];
725 JPEGCallbackStruct aCallbackData
;
726 aCallbackData
.xStatusIndicator
= xStatusIndicator
;
727 bRet
= (BOOL
) WriteJPEG( this, &rOStm
, pAcc
->Width(), pAcc
->Height(), nQuality
, &aCallbackData
);
732 aGraphicBmp
.ReleaseAccess( pAcc
);
735 if ( xStatusIndicator
.is() )
736 xStatusIndicator
->end();
745 BOOL
ImportJPEG( SvStream
& rStm
, Graphic
& rGraphic
, void* pCallerData
, sal_Int32 nImportFlags
)
747 JPEGReader
* pJPEGReader
= (JPEGReader
*) rGraphic
.GetContext();
748 ReadState eReadState
;
752 pJPEGReader
= new JPEGReader( rStm
, pCallerData
, ( nImportFlags
& GRFILTER_I_FLAGS_SET_LOGSIZE_FOR_JPEG
) != 0 );
754 if( nImportFlags
& GRFILTER_I_FLAGS_FOR_PREVIEW
)
755 pJPEGReader
->SetPreviewSize( Size(128,128) );
757 pJPEGReader
->DisablePreviewMode();
759 rGraphic
.SetContext( NULL
);
760 eReadState
= pJPEGReader
->Read( rGraphic
);
762 if( eReadState
== JPEGREAD_ERROR
)
767 else if( eReadState
== JPEGREAD_OK
)
770 rGraphic
.SetContext( pJPEGReader
);
779 BOOL
ExportJPEG( SvStream
& rOStm
, const Graphic
& rGraphic
, const ::com::sun::star::uno::Sequence
< ::com::sun::star::beans::PropertyValue
>* pFilterData
)
781 JPEGWriter
aJPEGWriter( rOStm
, pFilterData
);
782 return aJPEGWriter
.Write( rGraphic
);