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