xref: /trunk/main/svtools/source/filter/jpeg/jpeg.cxx (revision 56b8eddc)
1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 
23 
24 // MARKER(update_precomp.py): autogen include statement, do not remove
25 #include "precompiled_svtools.hxx"
26 
27 #include <tools/solar.h>
28 
29 extern "C"
30 {
31 	#include "stdio.h"
32 	#include "jpeg.h"
33 	#include "jpeglib.h"
34 	#include "jerror.h"
35 }
36 
37 #define _JPEGPRIVATE
38 #include <vcl/bmpacc.hxx>
39 #include "jpeg.hxx"
40 #include <svtools/FilterConfigItem.hxx>
41 #include <svtools/filter.hxx>
42 
43 // -----------
44 // - Defines -
45 // -----------
46 
47 using namespace ::com::sun::star;
48 
49 #define JPEGMINREAD 512
50 
51 namespace {
52     // Arbitrary maximal size (256M) of bitmaps after they have been decoded.
53     // It is used to prevent excessive swapping due to large buffers in
54     // virtual memory.
55     // May have to be tuned if it turns out to be too large or too small.
56     static const sal_uInt64 MAX_BITMAP_BYTE_SIZE = sal_uInt64(256 * 1024 * 1024);
57 }
58 
59 // -------------
60 // - (C-Calls) -
61 // -------------
62 
63 // ------------------------------------------------------------------------
64 
CreateBitmap(void * pJPEGReader,void * pJPEGCreateBitmapParam)65 extern "C" void* CreateBitmap( void* pJPEGReader, void* pJPEGCreateBitmapParam )
66 {
67 	return ( (JPEGReader*) pJPEGReader )->CreateBitmap( pJPEGCreateBitmapParam );
68 }
69 
70 // ------------------------------------------------------------------------
71 
GetScanline(void * pJPEGWriter,long nY)72 extern "C" void* GetScanline( void* pJPEGWriter, long nY )
73 {
74 	return ( (JPEGWriter*) pJPEGWriter )->GetScanline( nY );
75 }
76 
77 // ------------------------------------------------------------------------
78 
79 struct JPEGCallbackStruct
80 {
81 	uno::Reference< task::XStatusIndicator > xStatusIndicator;
82 };
83 
JPEGCallback(void * pCallbackData,long nPercent)84 extern "C" long JPEGCallback( void* pCallbackData, long nPercent )
85 {
86 	JPEGCallbackStruct* pS = (JPEGCallbackStruct*)pCallbackData;
87 	if ( pS && pS->xStatusIndicator.is() )
88 	{
89 		pS->xStatusIndicator->setValue( nPercent );
90 	}
91 	return 0L;
92 }
93 
94 #define BUF_SIZE  4096
95 
96 typedef struct
97 {
98   struct jpeg_destination_mgr pub;  /* public fields */
99 
100   SvStream* outfile;                /* target stream */
101   JOCTET * buffer;                  /* start of buffer */
102 } my_destination_mgr;
103 
104 typedef my_destination_mgr * my_dest_ptr;
105 
init_destination(j_compress_ptr cinfo)106 extern "C" void init_destination (j_compress_ptr cinfo)
107 {
108   my_dest_ptr dest = (my_dest_ptr) cinfo->dest;
109 
110   /* Allocate the output buffer --- it will be released when done with image */
111   dest->buffer = (JOCTET *)
112       (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE,
113                                   BUF_SIZE * sizeof(JOCTET));
114 
115   dest->pub.next_output_byte = dest->buffer;
116   dest->pub.free_in_buffer = BUF_SIZE;
117 }
118 
empty_output_buffer(j_compress_ptr cinfo)119 extern "C" boolean empty_output_buffer (j_compress_ptr cinfo)
120 {
121   my_dest_ptr dest = (my_dest_ptr) cinfo->dest;
122 
123   if (dest->outfile->Write(dest->buffer, BUF_SIZE) !=
124       (size_t) BUF_SIZE)
125     ERREXIT(cinfo, JERR_FILE_WRITE);
126 
127   dest->pub.next_output_byte = dest->buffer;
128   dest->pub.free_in_buffer = BUF_SIZE;
129 
130   return sal_True;
131 }
132 
term_destination(j_compress_ptr cinfo)133 extern "C" void term_destination (j_compress_ptr cinfo)
134 {
135   my_dest_ptr dest = (my_dest_ptr) cinfo->dest;
136   size_t datacount = BUF_SIZE - dest->pub.free_in_buffer;
137 
138   /* Write any data remaining in the buffer */
139   if (datacount > 0) {
140     if (dest->outfile->Write(dest->buffer, datacount) != datacount)
141       ERREXIT(cinfo, JERR_FILE_WRITE);
142   }
143 }
144 
jpeg_svstream_dest(j_compress_ptr cinfo,void * out)145 extern "C" void jpeg_svstream_dest (j_compress_ptr cinfo, void* out)
146 {
147   SvStream * outfile = (SvStream*)out;
148   my_dest_ptr dest;
149 
150   /* The destination object is made permanent so that multiple JPEG images
151    * can be written to the same file without re-executing jpeg_svstream_dest.
152    * This makes it dangerous to use this manager and a different destination
153    * manager serially with the same JPEG object, because their private object
154    * sizes may be different.  Caveat programmer.
155    */
156   if (cinfo->dest == NULL) {    /* first time for this JPEG object? */
157     cinfo->dest = (struct jpeg_destination_mgr *)
158       (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT,
159                                   sizeof(my_destination_mgr));
160   }
161 
162   dest = (my_dest_ptr) cinfo->dest;
163   dest->pub.init_destination = init_destination;
164   dest->pub.empty_output_buffer = empty_output_buffer;
165   dest->pub.term_destination = term_destination;
166   dest->outfile = outfile;
167 }
168 
169 /* Expanded data source object for stdio input */
170 
171 typedef struct {
172   struct jpeg_source_mgr pub;   /* public fields */
173 
174   SvStream * infile;            /* source stream */
175   JOCTET * buffer;              /* start of buffer */
176   boolean start_of_file;        /* have we gotten any data yet? */
177 } my_source_mgr;
178 
179 typedef my_source_mgr * my_src_ptr;
180 
181 /*
182  * Initialize source --- called by jpeg_read_header
183  * before any data is actually read.
184  */
185 
init_source(j_decompress_ptr cinfo)186 extern "C" void init_source (j_decompress_ptr cinfo)
187 {
188   my_src_ptr src = (my_src_ptr) cinfo->src;
189 
190   /* We reset the empty-input-file flag for each image,
191    * but we don't clear the input buffer.
192    * This is correct behavior for reading a series of images from one source.
193    */
194   src->start_of_file = sal_True;
195 }
196 
StreamRead(SvStream * pSvStm,void * pBuffer,long nBufferSize)197 long StreamRead( SvStream* pSvStm, void* pBuffer, long nBufferSize )
198 {
199         long            nRead;
200 
201         if( pSvStm->GetError() != ERRCODE_IO_PENDING )
202         {
203                 long nActPos = pSvStm->Tell();
204 
205                 nRead = (long) pSvStm->Read( pBuffer, nBufferSize );
206 
207                 if( pSvStm->GetError() == ERRCODE_IO_PENDING )
208                 {
209                         nRead = 0;
210 
211                         // Damit wir wieder an die alte Position
212                         // seeken koennen, setzen wir den Error temp.zurueck
213                         pSvStm->ResetError();
214                         pSvStm->Seek( nActPos );
215                         pSvStm->SetError( ERRCODE_IO_PENDING );
216                 }
217         }
218         else
219                 nRead = 0;
220 
221         return nRead;
222 }
223 
fill_input_buffer(j_decompress_ptr cinfo)224 extern "C" boolean fill_input_buffer (j_decompress_ptr cinfo)
225 {
226   my_src_ptr src = (my_src_ptr) cinfo->src;
227   size_t nbytes;
228 
229   nbytes = StreamRead(src->infile, src->buffer, BUF_SIZE);
230 
231   if (nbytes <= 0) {
232     if (src->start_of_file)     /* Treat empty input file as fatal error */
233       ERREXIT(cinfo, JERR_INPUT_EMPTY);
234     WARNMS(cinfo, JWRN_JPEG_EOF);
235     /* Insert a fake EOI marker */
236     src->buffer[0] = (JOCTET) 0xFF;
237     src->buffer[1] = (JOCTET) JPEG_EOI;
238     nbytes = 2;
239   }
240 
241   src->pub.next_input_byte = src->buffer;
242   src->pub.bytes_in_buffer = nbytes;
243   src->start_of_file = sal_False;
244 
245   return sal_True;
246 }
247 
skip_input_data(j_decompress_ptr cinfo,long num_bytes)248 extern "C" void skip_input_data (j_decompress_ptr cinfo, long num_bytes)
249 {
250   my_src_ptr src = (my_src_ptr) cinfo->src;
251 
252   /* Just a dumb implementation for now.  Could use fseek() except
253    * it doesn't work on pipes.  Not clear that being smart is worth
254    * any trouble anyway --- large skips are infrequent.
255    */
256   if (num_bytes > 0) {
257     while (num_bytes > (long) src->pub.bytes_in_buffer) {
258       num_bytes -= (long) src->pub.bytes_in_buffer;
259       (void) fill_input_buffer(cinfo);
260       /* note we assume that fill_input_buffer will never return sal_False,
261        * so suspension need not be handled.
262        */
263     }
264     src->pub.next_input_byte += (size_t) num_bytes;
265     src->pub.bytes_in_buffer -= (size_t) num_bytes;
266   }
267 }
268 
term_source(j_decompress_ptr)269 extern "C" void term_source (j_decompress_ptr)
270 {
271   /* no work necessary here */
272 }
273 
jpeg_svstream_src(j_decompress_ptr cinfo,void * in)274 extern "C" void jpeg_svstream_src (j_decompress_ptr cinfo, void * in)
275 {
276   my_src_ptr src;
277   SvStream * infile = (SvStream*)in;
278 
279   /* The source object and input buffer are made permanent so that a series
280    * of JPEG images can be read from the same file by calling jpeg_stdio_src
281    * only before the first one.  (If we discarded the buffer at the end of
282    * one image, we'd likely lose the start of the next one.)
283    * This makes it unsafe to use this manager and a different source
284    * manager serially with the same JPEG object.  Caveat programmer.
285    */
286   if (cinfo->src == NULL) {     /* first time for this JPEG object? */
287     cinfo->src = (struct jpeg_source_mgr *)
288       (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT,
289                                   sizeof(my_source_mgr));
290     src = (my_src_ptr) cinfo->src;
291     src->buffer = (JOCTET *)
292       (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT,
293                                   BUF_SIZE * sizeof(JOCTET));
294   }
295 
296   src = (my_src_ptr) cinfo->src;
297   src->pub.init_source = init_source;
298   src->pub.fill_input_buffer = fill_input_buffer;
299   src->pub.skip_input_data = skip_input_data;
300   src->pub.resync_to_restart = jpeg_resync_to_restart; /* use default method */
301   src->pub.term_source = term_source;
302   src->infile = infile;
303   src->pub.bytes_in_buffer = 0; /* forces fill_input_buffer on first read */
304   src->pub.next_input_byte = NULL; /* until buffer loaded */
305 }
306 
307 // --------------
308 // - JPEGReader -
309 // --------------
310 
JPEGReader(SvStream & rStm,void *,sal_Bool bSetLS)311 JPEGReader::JPEGReader( SvStream& rStm, void* /*pCallData*/, sal_Bool bSetLS ) :
312 		rIStm			( rStm ),
313 		pAcc			( NULL ),
314 		pAcc1			( NULL ),
315 		pBuffer			( NULL ),
316 		nLastPos		( rStm.Tell() ),
317 		nLastLines		( 0 ),
318         bSetLogSize     ( bSetLS )
319 {
320 	maUpperName = String::CreateFromAscii( "SVIJPEG", 7 );
321 	nFormerPos = nLastPos;
322 }
323 
324 // ------------------------------------------------------------------------
325 
~JPEGReader()326 JPEGReader::~JPEGReader()
327 {
328 	if( pBuffer )
329 		rtl_freeMemory( pBuffer );
330 
331 	if( pAcc )
332 		aBmp.ReleaseAccess( pAcc );
333 
334 	if( pAcc1 )
335 		aBmp1.ReleaseAccess( pAcc1 );
336 }
337 
338 // ------------------------------------------------------------------------
339 
CreateBitmap(void * pParam)340 void* JPEGReader::CreateBitmap( void* pParam )
341 {
342     Size        aSize( ((JPEGCreateBitmapParam*)pParam)->nWidth,
343                         ((JPEGCreateBitmapParam*)pParam)->nHeight );
344     sal_Bool    bGray = ((JPEGCreateBitmapParam*)pParam)->bGray != 0;
345 
346     void* pBmpBuf = NULL;
347 
348     if( pAcc )
349     {
350         aBmp.ReleaseAccess( pAcc );
351         aBmp = Bitmap();
352         pAcc = NULL;
353     }
354 
355     // Check if the bitmap is untypically large.
356     if (aSize.Width()<=0
357         || aSize.Height()<=0
358         || sal_uInt64(aSize.Width())*sal_uInt64(aSize.Height())*(bGray?1:3) > MAX_BITMAP_BYTE_SIZE)
359     {
360         // Do not try to acquire resources for the large bitmap or to
361         // read the bitmap into memory.
362         return NULL;
363     }
364 
365 	if( bGray )
366 	{
367 		BitmapPalette aGrayPal( 256 );
368 
369 		for( sal_uInt16 n = 0; n < 256; n++ )
370 		{
371 			const sal_uInt8 cGray = (sal_uInt8) n;
372 			aGrayPal[ n ] = BitmapColor( cGray, cGray, cGray );
373 		}
374 
375 		aBmp = Bitmap( aSize, 8, &aGrayPal );
376 	}
377 	else
378 		aBmp = Bitmap( aSize, 24 );
379 
380     if ( bSetLogSize )
381     {
382         unsigned long nUnit = ((JPEGCreateBitmapParam*)pParam)->density_unit;
383 
384         if( ( ( 1 == nUnit ) || ( 2 == nUnit ) ) &&
385             ( (JPEGCreateBitmapParam*) pParam )->X_density &&
386             ( (JPEGCreateBitmapParam*) pParam )->Y_density )
387         {
388             Point       aEmptyPoint;
389 	        Fraction	aFractX( 1, ((JPEGCreateBitmapParam*)pParam)->X_density );
390 	        Fraction	aFractY( 1, ((JPEGCreateBitmapParam*)pParam)->Y_density );
391 	        MapMode		aMapMode( nUnit == 1 ? MAP_INCH : MAP_CM, aEmptyPoint, aFractX, aFractY );
392 	        Size		aPrefSize = OutputDevice::LogicToLogic( aSize, aMapMode, MAP_100TH_MM );
393 
394             aBmp.SetPrefSize( aPrefSize );
395             aBmp.SetPrefMapMode( MapMode( MAP_100TH_MM ) );
396         }
397     }
398 
399     pAcc = aBmp.AcquireWriteAccess();
400 
401     if( pAcc )
402     {
403         long nAlignedWidth;
404 
405 		const sal_uLong nFormat = pAcc->GetScanlineFormat();
406 
407 		if(
408 			( bGray && ( BMP_FORMAT_8BIT_PAL == nFormat ) ) ||
409 			( !bGray && ( BMP_FORMAT_24BIT_TC_RGB == nFormat ) )
410 		  )
411 		{
412 			pBmpBuf = pAcc->GetBuffer();
413 			nAlignedWidth = pAcc->GetScanlineSize();
414 			((JPEGCreateBitmapParam*)pParam)->bTopDown = pAcc->IsTopDown();
415 		}
416 		else
417 		{
418 			nAlignedWidth = AlignedWidth4Bytes( aSize.Width() * ( bGray ? 8 : 24 ) );
419 			((JPEGCreateBitmapParam*)pParam)->bTopDown = sal_True;
420 			pBmpBuf = pBuffer = rtl_allocateMemory( nAlignedWidth * aSize.Height() );
421 		}
422 
423         // clean up, if no Bitmap buffer can be provided.
424         if ( pBmpBuf == 0 )
425         {
426             aBmp.ReleaseAccess( pAcc );
427             aBmp = Bitmap();
428             pAcc = NULL;
429         }
430 
431         ((JPEGCreateBitmapParam*)pParam)->nAlignedWidth = nAlignedWidth;
432     }
433 
434     return pBmpBuf;
435 }
436 
437 // ------------------------------------------------------------------------
438 
FillBitmap()439 void JPEGReader::FillBitmap()
440 {
441 	if( pBuffer && pAcc )
442 	{
443 		HPBYTE		pTmp;
444 		BitmapColor	aColor;
445 		long		nAlignedWidth;
446 		long		nWidth = pAcc->Width();
447 		long		nHeight = pAcc->Height();
448 
449 		if( pAcc->GetBitCount() == 8 )
450 		{
451 			BitmapColor* pCols = new BitmapColor[ 256 ];
452 
453 			for( sal_uInt16 n = 0; n < 256; n++ )
454 			{
455 				const sal_uInt8 cGray = (sal_uInt8) n;
456 				pCols[ n ] = pAcc->GetBestMatchingColor( BitmapColor( cGray, cGray, cGray ) );
457 			}
458 
459 			nAlignedWidth = AlignedWidth4Bytes( pAcc->Width() * 8L );
460 
461 			for( long nY = 0L; nY < nHeight; nY++ )
462 			{
463 				pTmp = (sal_uInt8*) pBuffer + nY * nAlignedWidth;
464 
465 				for( long nX = 0L; nX < nWidth; nX++ )
466 					pAcc->SetPixel( nY, nX, pCols[ *pTmp++ ] );
467 			}
468 
469 			delete[] pCols;
470 		}
471 		else
472 		{
473 			nAlignedWidth = AlignedWidth4Bytes( pAcc->Width() * 24L );
474 
475 			for( long nY = 0L; nY < nHeight; nY++ )
476 			{
477                 // #122985# Added fast-lane implementations using CopyScanline with direct supported mem formats
478                 static bool bCheckOwnReader(true);
479 
480                 if(bCheckOwnReader)
481                 {
482                     // #122985# Trying to copy the RGB data from jpeg import to make things faster. Unfortunately
483                     // it has no GBR format, so RGB three-byte groups need to be 'flipped' to GBR first,
484                     // then CopyScanline can use a memcpy to do the data transport. CopyScanline can also
485                     // do the needed conversion from BMP_FORMAT_24BIT_TC_RGB (and it works well), but this
486                     // is not faster that the old loop below using SetPixel.
487                     sal_uInt8* aSource((sal_uInt8*)pBuffer + nY * nAlignedWidth);
488                     sal_uInt8* aEnd(aSource + (nWidth * 3));
489 
490                     for(sal_uInt8* aTmp(aSource); aTmp < aEnd; aTmp += 3)
491                     {
492                         ::std::swap(*aTmp, *(aTmp + 2));
493                     }
494 
495                     pAcc->CopyScanline(nY, aSource, BMP_FORMAT_24BIT_TC_BGR, nWidth * 3);
496                 }
497                 else
498                 {
499                     // old version: WritePixel
500                     pTmp = (sal_uInt8*) pBuffer + nY * nAlignedWidth;
501 
502                     for( long nX = 0L; nX < nWidth; nX++ )
503                     {
504                         aColor.SetRed( *pTmp++ );
505                         aColor.SetGreen( *pTmp++ );
506                         aColor.SetBlue( *pTmp++ );
507                         pAcc->SetPixel( nY, nX, aColor );
508                     }
509                 }
510 			}
511 		}
512 	}
513 }
514 
515 // ------------------------------------------------------------------------
516 
CreateIntermediateGraphic(const Bitmap & rBitmap,long nLines)517 Graphic JPEGReader::CreateIntermediateGraphic( const Bitmap& rBitmap, long nLines )
518 {
519 	Graphic		aGraphic;
520 	const Size	aSizePix( rBitmap.GetSizePixel() );
521 
522 	if( !nLastLines )
523 	{
524 		if( pAcc1 )
525 			aBmp1.ReleaseAccess( pAcc1 );
526 
527 		aBmp1 = Bitmap( rBitmap.GetSizePixel(), 1 );
528 		aBmp1.Erase( Color( COL_WHITE ) );
529 		pAcc1 = aBmp1.AcquireWriteAccess();
530 	}
531 
532 	if( nLines && ( nLines < aSizePix.Height() ) )
533 	{
534 		if( pAcc1 )
535 		{
536 			const long nNewLines = nLines - nLastLines;
537 
538 			if( nNewLines )
539 			{
540 				pAcc1->SetFillColor( Color( COL_BLACK ) );
541 				pAcc1->FillRect( Rectangle( Point( 0, nLastLines ),
542 											Size( pAcc1->Width(), nNewLines ) ) );
543 			}
544 
545 			aBmp1.ReleaseAccess( pAcc1 );
546 			aGraphic = BitmapEx( rBitmap, aBmp1 );
547 			pAcc1 = aBmp1.AcquireWriteAccess();
548 		}
549 		else
550 			aGraphic = rBitmap;
551 	}
552 	else
553 		aGraphic = rBitmap;
554 
555 	nLastLines = nLines;
556 
557 	return aGraphic;
558 }
559 
560 // ------------------------------------------------------------------------
561 
Read(Graphic & rGraphic)562 ReadState JPEGReader::Read( Graphic& rGraphic )
563 {
564 	long		nEndPos;
565 	long		nLines;
566 	ReadState	eReadState;
567 	sal_Bool		bRet = sal_False;
568 	sal_uInt8		cDummy;
569 
570 #if 1 // TODO: is it possible to get rid of this seek to the end?
571 	// check if the stream's end is already available
572 	rIStm.Seek( STREAM_SEEK_TO_END );
573 	rIStm >> cDummy;
574 	nEndPos = rIStm.Tell();
575 
576 	// else check if at least JPEGMINREAD bytes can be read
577 	if( rIStm.GetError() == ERRCODE_IO_PENDING )
578 	{
579 		rIStm.ResetError();
580 		if( ( nEndPos  - nFormerPos ) < JPEGMINREAD )
581 		{
582 			rIStm.Seek( nLastPos );
583 			return JPEGREAD_NEED_MORE;
584 		}
585 	}
586 
587 	// seek back to the original position
588 	rIStm.Seek( nLastPos );
589 #endif
590 
591     Size aPreviewSize = GetPreviewSize();
592     SetJpegPreviewSizeHint( aPreviewSize.Width(), aPreviewSize.Height() );
593 
594 	// read the (partial) image
595 	ReadJPEG( this, &rIStm, &nLines );
596 
597 	if( pAcc )
598 	{
599 		if( pBuffer )
600 		{
601 			FillBitmap();
602 			rtl_freeMemory( pBuffer );
603 			pBuffer = NULL;
604 		}
605 
606 		aBmp.ReleaseAccess( pAcc );
607 		pAcc = NULL;
608 
609 		if( rIStm.GetError() == ERRCODE_IO_PENDING )
610 			rGraphic = CreateIntermediateGraphic( aBmp, nLines );
611 		else
612 			rGraphic = aBmp;
613 
614 		bRet = sal_True;
615 	}
616 	else if( rIStm.GetError() == ERRCODE_IO_PENDING )
617 		bRet = sal_True;
618 
619 	// Status setzen ( Pending hat immer Vorrang )
620 	if( rIStm.GetError() == ERRCODE_IO_PENDING )
621 	{
622 		eReadState = JPEGREAD_NEED_MORE;
623 		rIStm.ResetError();
624 		nFormerPos = rIStm.Tell();
625 	}
626 	else
627 	{
628 		if( bRet )
629 			eReadState = JPEGREAD_OK;
630 		else
631 			eReadState = JPEGREAD_ERROR;
632 	}
633 
634 	return eReadState;
635 }
636 
637 
638 // --------------
639 // - JPEGWriter -
640 // --------------
641 
JPEGWriter(SvStream & rStm,const uno::Sequence<beans::PropertyValue> * pFilterData,bool * pExportWasGrey)642 JPEGWriter::JPEGWriter( SvStream& rStm, const uno::Sequence< beans::PropertyValue >* pFilterData, bool* pExportWasGrey ) :
643 		rOStm		( rStm ),
644 		pAcc		( NULL ),
645 		pBuffer		( NULL ),
646 		pExpWasGrey ( pExportWasGrey )
647 {
648 	FilterConfigItem aConfigItem( (uno::Sequence< beans::PropertyValue >*)pFilterData );
649 	bGreys = aConfigItem.ReadInt32( String( RTL_CONSTASCII_USTRINGPARAM( "ColorMode" ) ), 0 ) != 0;
650 	nQuality = aConfigItem.ReadInt32( String( RTL_CONSTASCII_USTRINGPARAM( "Quality" ) ), 75 );
651 
652 	if ( pFilterData )
653 	{
654 		int nArgs = pFilterData->getLength();
655 		const beans::PropertyValue* pValues = pFilterData->getConstArray();
656 		while( nArgs-- )
657 		{
658 			if( pValues->Name.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "StatusIndicator" ) ) )
659 			{
660 				pValues->Value >>= xStatusIndicator;
661 			}
662 			pValues++;
663 		}
664 	}
665 }
666 
667 // ------------------------------------------------------------------------
668 
GetScanline(long nY)669 void* JPEGWriter::GetScanline( long nY )
670 {
671 	void* pScanline = NULL;
672 
673 	if( pAcc )
674 	{
675 		if( bNative )
676 			pScanline = pAcc->GetScanline( nY );
677 		else if( pBuffer )
678 		{
679 			BitmapColor aColor;
680 			long		nWidth = pAcc->Width();
681 			sal_uInt8*		pTmp = pBuffer;
682 
683 			if( pAcc->HasPalette() )
684 			{
685 				for( long nX = 0L; nX < nWidth; nX++ )
686 				{
687 					aColor = pAcc->GetPaletteColor( pAcc->GetPixelIndex( nY, nX ) );
688 					*pTmp++ = aColor.GetRed();
689 					if ( bGreys )
690 						continue;
691 					*pTmp++ = aColor.GetGreen();
692 					*pTmp++ = aColor.GetBlue();
693 				}
694 			}
695 			else
696 			{
697 				for( long nX = 0L; nX < nWidth; nX++ )
698 				{
699 					aColor = pAcc->GetPixel( nY, nX );
700 					*pTmp++ = aColor.GetRed();
701 					if ( bGreys )
702 						continue;
703 					*pTmp++ = aColor.GetGreen();
704 					*pTmp++ = aColor.GetBlue();
705 				}
706 			}
707 
708 			pScanline = pBuffer;
709 		}
710 	}
711 
712 	return pScanline;
713 }
714 
715 // ------------------------------------------------------------------------
716 
Write(const Graphic & rGraphic)717 sal_Bool JPEGWriter::Write( const Graphic& rGraphic )
718 {
719 	sal_Bool bRet = sal_False;
720 
721 	if ( xStatusIndicator.is() )
722 	{
723 		rtl::OUString aMsg;
724 		xStatusIndicator->start( aMsg, 100 );
725 	}
726 
727 	Bitmap aGraphicBmp( rGraphic.GetBitmap() );
728 
729 	if ( bGreys )
730 	{
731 		if ( !aGraphicBmp.Convert( BMP_CONVERSION_8BIT_GREYS ) )
732 			aGraphicBmp = rGraphic.GetBitmap();
733 	}
734 
735 	pAcc = aGraphicBmp.AcquireReadAccess();
736 
737 	if ( !bGreys )	// bitmap was not explicitly converted into greyscale,
738 	{				// check if source is greyscale only
739 
740 		sal_Bool bIsGrey = sal_True;
741 
742 		long nWidth = pAcc->Width();
743 		for ( long nY = 0; bIsGrey && ( nY < pAcc->Height() ); nY++ )
744 		{
745 			BitmapColor aColor;
746 			for( long nX = 0L; bIsGrey && ( nX < nWidth ); nX++ )
747 			{
748 				aColor = pAcc->HasPalette() ? pAcc->GetPaletteColor( pAcc->GetPixelIndex( nY, nX ) )
749 											: pAcc->GetPixel( nY, nX );
750 				bIsGrey = ( aColor.GetRed() == aColor.GetGreen() ) && ( aColor.GetRed() == aColor.GetBlue() );
751 			}
752 		}
753 		if ( bIsGrey )
754 			bGreys = sal_True;
755 	}
756 
757 	if( pExpWasGrey )
758 	    *pExpWasGrey = bGreys;
759 
760 	if( pAcc )
761 	{
762 		bNative = ( pAcc->GetScanlineFormat() == BMP_FORMAT_24BIT_TC_RGB );
763 
764 		if( !bNative )
765 			pBuffer = new sal_uInt8[ AlignedWidth4Bytes( bGreys ? pAcc->Width() * 8L : pAcc->Width() * 24L ) ];
766 
767 		JPEGCallbackStruct aCallbackData;
768 		aCallbackData.xStatusIndicator = xStatusIndicator;
769 		bRet = (sal_Bool) WriteJPEG( this, &rOStm, pAcc->Width(), pAcc->Height(), bGreys, nQuality, &aCallbackData );
770 
771 		delete[] pBuffer;
772 		pBuffer = NULL;
773 
774 		aGraphicBmp.ReleaseAccess( pAcc );
775 		pAcc = NULL;
776 	}
777 	if ( xStatusIndicator.is() )
778 		xStatusIndicator->end();
779 
780 	return bRet;
781 }
782 
783 // --------------
784 // - ImportJPEG -
785 // --------------
786 
ImportJPEG(SvStream & rStm,Graphic & rGraphic,void * pCallerData,sal_Int32 nImportFlags)787 sal_Bool ImportJPEG( SvStream& rStm, Graphic& rGraphic, void* pCallerData, sal_Int32 nImportFlags )
788 {
789 	JPEGReader*	pJPEGReader = (JPEGReader*) rGraphic.GetContext();
790 	ReadState	eReadState;
791 	sal_Bool		bRet = sal_True;
792 
793 	if( !pJPEGReader )
794         pJPEGReader = new JPEGReader( rStm, pCallerData, ( nImportFlags & GRFILTER_I_FLAGS_SET_LOGSIZE_FOR_JPEG ) != 0 );
795 
796     if( nImportFlags & GRFILTER_I_FLAGS_FOR_PREVIEW )
797         pJPEGReader->SetPreviewSize( Size(128,128) );
798     else
799         pJPEGReader->DisablePreviewMode();
800 
801 	rGraphic.SetContext( NULL );
802 	eReadState = pJPEGReader->Read( rGraphic );
803 
804 	if( eReadState == JPEGREAD_ERROR )
805 	{
806 		bRet = sal_False;
807 		delete pJPEGReader;
808 	}
809 	else if( eReadState == JPEGREAD_OK )
810 		delete pJPEGReader;
811 	else
812 		rGraphic.SetContext( pJPEGReader );
813 
814 	return bRet;
815 }
816 
817 //  --------------
818 //  - ExportJPEG -
819 //  --------------
820 
ExportJPEG(SvStream & rOStm,const Graphic & rGraphic,const::com::sun::star::uno::Sequence<::com::sun::star::beans::PropertyValue> * pFilterData,bool * pExportWasGrey)821 sal_Bool ExportJPEG( SvStream& rOStm, const Graphic& rGraphic,
822                  const ::com::sun::star::uno::Sequence< ::com::sun::star::beans::PropertyValue >* pFilterData,
823                  bool* pExportWasGrey
824                 )
825 {
826 	JPEGWriter aJPEGWriter( rOStm, pFilterData, pExportWasGrey );
827 	return aJPEGWriter.Write( rGraphic );
828 }
829