xref: /AOO41X/main/svtools/source/filter/jpeg/jpeg.cxx (revision 8e8ee8fefdac26d905672cc573c35fd0ae1f9356)
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 explicitely 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