Line data Source code
1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /*
3 : * This file is part of the LibreOffice project.
4 : *
5 : * This Source Code Form is subject to the terms of the Mozilla Public
6 : * License, v. 2.0. If a copy of the MPL was not distributed with this
7 : * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 : *
9 : * This file incorporates work covered by the following license notice:
10 : *
11 : * Licensed to the Apache Software Foundation (ASF) under one or more
12 : * contributor license agreements. See the NOTICE file distributed
13 : * with this work for additional information regarding copyright
14 : * ownership. The ASF licenses this file to you under the Apache
15 : * License, Version 2.0 (the "License"); you may not use this file
16 : * except in compliance with the License. You may obtain a copy of
17 : * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18 : */
19 :
20 :
21 : #include <tools/solar.h>
22 :
23 : extern "C"
24 : {
25 : #include "stdio.h"
26 : #include "jpeg.h"
27 : #include <jpeglib.h>
28 : #include <jerror.h>
29 : }
30 :
31 : #include "JpegReader.hxx"
32 : #include <vcl/bmpacc.hxx>
33 : #include <vcl/FilterConfigItem.hxx>
34 : #include <vcl/graphicfilter.hxx>
35 :
36 : #define JPEG_MIN_READ 512
37 : #define BUFFER_SIZE 4096
38 : namespace {
39 : // Arbitrary maximal size (256M) of bitmaps after they have been decoded.
40 : // It is used to prevent excessive swapping due to large buffers in
41 : // virtual memory.
42 : // May have to be tuned if it turns out to be too large or too small.
43 : static const sal_uInt64 MAX_BITMAP_BYTE_SIZE = sal_uInt64(256 * 1024 * 1024);
44 : }
45 :
46 25 : extern "C" void* CreateBitmapFromJPEGReader( void* pJPEGReader, void* pJPEGCreateBitmapParam )
47 : {
48 25 : return ( (JPEGReader*) pJPEGReader )->CreateBitmap( pJPEGCreateBitmapParam );
49 : }
50 :
51 : /* Expanded data source object for stdio input */
52 :
53 : typedef struct {
54 : struct jpeg_source_mgr pub; /* public fields */
55 : SvStream* stream; /* source stream */
56 : JOCTET* buffer; /* start of buffer */
57 : boolean start_of_file; /* have we gotten any data yet? */
58 : } SourceManagerStruct;
59 :
60 : typedef SourceManagerStruct* SourceManagerStructPointer;
61 :
62 : /*
63 : * Initialize source --- called by jpeg_read_header
64 : * before any data is actually read.
65 : */
66 30 : extern "C" void init_source (j_decompress_ptr cinfo)
67 : {
68 30 : SourceManagerStructPointer source = (SourceManagerStructPointer) cinfo->src;
69 :
70 : /* We reset the empty-input-file flag for each image,
71 : * but we don't clear the input buffer.
72 : * This is correct behavior for reading a series of images from one source.
73 : */
74 30 : source->start_of_file = sal_True;
75 30 : }
76 :
77 148 : long StreamRead( SvStream* pStream, void* pBuffer, long nBufferSize )
78 : {
79 148 : long nRead = 0;
80 :
81 148 : if( pStream->GetError() != ERRCODE_IO_PENDING )
82 : {
83 148 : long nInitialPosition = pStream->Tell();
84 :
85 148 : nRead = (long) pStream->Read( pBuffer, nBufferSize );
86 :
87 148 : if( pStream->GetError() == ERRCODE_IO_PENDING )
88 : {
89 : // Damit wir wieder an die alte Position
90 : // seeken koennen, setzen wir den Error temp.zurueck
91 0 : pStream->ResetError();
92 0 : pStream->Seek( nInitialPosition );
93 0 : pStream->SetError( ERRCODE_IO_PENDING );
94 : }
95 : }
96 :
97 148 : return nRead;
98 : }
99 :
100 148 : extern "C" boolean fill_input_buffer (j_decompress_ptr cinfo)
101 : {
102 148 : SourceManagerStructPointer source = (SourceManagerStructPointer) cinfo->src;
103 : size_t nbytes;
104 :
105 148 : nbytes = StreamRead(source->stream, source->buffer, BUFFER_SIZE);
106 :
107 148 : if (!nbytes)
108 : {
109 0 : if (source->start_of_file) /* Treat empty input file as fatal error */
110 : {
111 0 : ERREXIT(cinfo, JERR_INPUT_EMPTY);
112 : }
113 0 : WARNMS(cinfo, JWRN_JPEG_EOF);
114 : /* Insert a fake EOI marker */
115 0 : source->buffer[0] = (JOCTET) 0xFF;
116 0 : source->buffer[1] = (JOCTET) JPEG_EOI;
117 0 : nbytes = 2;
118 : }
119 :
120 148 : source->pub.next_input_byte = source->buffer;
121 148 : source->pub.bytes_in_buffer = nbytes;
122 148 : source->start_of_file = sal_False;
123 :
124 148 : return sal_True;
125 : }
126 :
127 36 : extern "C" void skip_input_data (j_decompress_ptr cinfo, long numberOfBytes)
128 : {
129 36 : SourceManagerStructPointer source = (SourceManagerStructPointer) cinfo->src;
130 :
131 : /* Just a dumb implementation for now. Could use fseek() except
132 : * it doesn't work on pipes. Not clear that being smart is worth
133 : * any trouble anyway --- large skips are infrequent.
134 : */
135 36 : if (numberOfBytes > 0)
136 : {
137 86 : while (numberOfBytes > (long) source->pub.bytes_in_buffer)
138 : {
139 14 : numberOfBytes -= (long) source->pub.bytes_in_buffer;
140 14 : (void) fill_input_buffer(cinfo);
141 :
142 : /* note we assume that fill_input_buffer will never return sal_False,
143 : * so suspension need not be handled.
144 : */
145 : }
146 36 : source->pub.next_input_byte += (size_t) numberOfBytes;
147 36 : source->pub.bytes_in_buffer -= (size_t) numberOfBytes;
148 : }
149 36 : }
150 :
151 24 : extern "C" void term_source (j_decompress_ptr)
152 : {
153 : /* no work necessary here */
154 24 : }
155 :
156 30 : extern "C" void jpeg_svstream_src (j_decompress_ptr cinfo, void* input)
157 : {
158 : SourceManagerStructPointer source;
159 30 : SvStream* stream = (SvStream*)input;
160 :
161 : /* The source object and input buffer are made permanent so that a series
162 : * of JPEG images can be read from the same file by calling jpeg_stdio_src
163 : * only before the first one. (If we discarded the buffer at the end of
164 : * one image, we'd likely lose the start of the next one.)
165 : * This makes it unsafe to use this manager and a different source
166 : * manager serially with the same JPEG object. Caveat programmer.
167 : */
168 :
169 30 : if (cinfo->src == NULL)
170 : { /* first time for this JPEG object? */
171 : cinfo->src = (struct jpeg_source_mgr *)
172 30 : (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, sizeof(SourceManagerStruct));
173 30 : source = (SourceManagerStructPointer) cinfo->src;
174 : source->buffer = (JOCTET *)
175 30 : (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, BUFFER_SIZE * sizeof(JOCTET));
176 : }
177 :
178 30 : source = (SourceManagerStructPointer) cinfo->src;
179 30 : source->pub.init_source = init_source;
180 30 : source->pub.fill_input_buffer = fill_input_buffer;
181 30 : source->pub.skip_input_data = skip_input_data;
182 30 : source->pub.resync_to_restart = jpeg_resync_to_restart; /* use default method */
183 30 : source->pub.term_source = term_source;
184 30 : source->stream = stream;
185 30 : source->pub.bytes_in_buffer = 0; /* forces fill_input_buffer on first read */
186 30 : source->pub.next_input_byte = NULL; /* until buffer loaded */
187 30 : }
188 :
189 30 : JPEGReader::JPEGReader( SvStream& rStream, void* /*pCallData*/, bool bSetLogSize ) :
190 : mrStream ( rStream ),
191 : mpAcc ( NULL ),
192 : mpAcc1 ( NULL ),
193 : mpBuffer ( NULL ),
194 30 : mnLastPos ( rStream.Tell() ),
195 : mnLastLines ( 0 ),
196 60 : mbSetLogSize ( bSetLogSize )
197 : {
198 30 : maUpperName = OUString("SVIJPEG");
199 30 : mnFormerPos = mnLastPos;
200 30 : }
201 :
202 90 : JPEGReader::~JPEGReader()
203 : {
204 30 : if( mpBuffer )
205 0 : rtl_freeMemory( mpBuffer );
206 :
207 30 : if( mpAcc )
208 0 : maBmp.ReleaseAccess( mpAcc );
209 :
210 30 : if( mpAcc1 )
211 0 : maBmp1.ReleaseAccess( mpAcc1 );
212 60 : }
213 :
214 25 : void* JPEGReader::CreateBitmap( void* _pParam )
215 : {
216 25 : JPEGCreateBitmapParam *pParam = (JPEGCreateBitmapParam *) _pParam;
217 :
218 25 : if (pParam->nWidth > SAL_MAX_INT32 / 8 || pParam->nHeight > SAL_MAX_INT32 / 8)
219 0 : return NULL; // avoid overflows later
220 :
221 25 : if (pParam->nWidth <= 0 || pParam->nHeight <=0)
222 0 : return NULL;
223 :
224 25 : Size aSize( pParam->nWidth, pParam->nHeight );
225 25 : bool bGray = pParam->bGray != 0;
226 :
227 25 : void* pBmpBuf = NULL;
228 :
229 25 : if( mpAcc )
230 : {
231 0 : maBmp.ReleaseAccess( mpAcc );
232 0 : maBmp = Bitmap();
233 0 : mpAcc = NULL;
234 : }
235 :
236 25 : sal_uInt64 nSize = aSize.Width();
237 25 : nSize *= aSize.Height();
238 25 : if (nSize > SAL_MAX_INT32 / 24)
239 0 : return NULL;
240 :
241 : // Check if the bitmap is untypically large.
242 25 : if (nSize*(bGray?1:3) > MAX_BITMAP_BYTE_SIZE)
243 : {
244 : // Do not try to acquire resources for the large bitmap or to
245 : // read the bitmap into memory.
246 0 : return NULL;
247 : }
248 :
249 25 : if( bGray )
250 : {
251 1 : BitmapPalette aGrayPal( 256 );
252 :
253 257 : for( sal_uInt16 n = 0; n < 256; n++ )
254 : {
255 256 : const sal_uInt8 cGray = (sal_uInt8) n;
256 256 : aGrayPal[ n ] = BitmapColor( cGray, cGray, cGray );
257 : }
258 :
259 1 : maBmp = Bitmap( aSize, 8, &aGrayPal );
260 : }
261 : else
262 : {
263 24 : maBmp = Bitmap( aSize, 24 );
264 : }
265 :
266 25 : if ( mbSetLogSize )
267 : {
268 25 : unsigned long nUnit = ((JPEGCreateBitmapParam*)pParam)->density_unit;
269 :
270 25 : if( ( ( 1 == nUnit ) || ( 2 == nUnit ) ) && pParam->X_density && pParam->Y_density )
271 : {
272 21 : Point aEmptyPoint;
273 21 : Fraction aFractX( 1, pParam->X_density );
274 21 : Fraction aFractY( 1, pParam->Y_density );
275 21 : MapMode aMapMode( nUnit == 1 ? MAP_INCH : MAP_CM, aEmptyPoint, aFractX, aFractY );
276 21 : Size aPrefSize = OutputDevice::LogicToLogic( aSize, aMapMode, MAP_100TH_MM );
277 :
278 21 : maBmp.SetPrefSize( aPrefSize );
279 21 : maBmp.SetPrefMapMode( MapMode( MAP_100TH_MM ) );
280 : }
281 : }
282 :
283 25 : mpAcc = maBmp.AcquireWriteAccess();
284 :
285 25 : if( mpAcc )
286 : {
287 25 : const sal_uLong nFormat = mpAcc->GetScanlineFormat();
288 :
289 25 : if(
290 25 : ( bGray && ( BMP_FORMAT_8BIT_PAL == nFormat ) ) ||
291 48 : ( !bGray && ( BMP_FORMAT_24BIT_TC_RGB == nFormat ) )
292 : )
293 : {
294 1 : pBmpBuf = mpAcc->GetBuffer();
295 1 : pParam->nAlignedWidth = mpAcc->GetScanlineSize();
296 1 : pParam->bTopDown = mpAcc->IsTopDown();
297 : }
298 : else
299 : {
300 24 : pParam->nAlignedWidth = AlignedWidth4Bytes( aSize.Width() * ( bGray ? 8 : 24 ) );
301 24 : pParam->bTopDown = sal_True;
302 24 : pBmpBuf = mpBuffer = rtl_allocateMemory( pParam->nAlignedWidth * aSize.Height() );
303 : }
304 : }
305 :
306 : // clean up, if no Bitmap buffer can be provided.
307 25 : if ( !pBmpBuf )
308 : {
309 0 : maBmp.ReleaseAccess( mpAcc );
310 0 : maBmp = Bitmap();
311 0 : mpAcc = NULL;
312 : }
313 :
314 25 : return pBmpBuf;
315 : }
316 :
317 24 : void JPEGReader::FillBitmap()
318 : {
319 24 : if( mpBuffer && mpAcc )
320 : {
321 : HPBYTE pTmp;
322 24 : BitmapColor aColor;
323 : long nAlignedWidth;
324 24 : long nWidth = mpAcc->Width();
325 24 : long nHeight = mpAcc->Height();
326 :
327 24 : if( mpAcc->GetBitCount() == 8 )
328 : {
329 0 : BitmapColor* pCols = new BitmapColor[ 256 ];
330 :
331 0 : for( sal_uInt16 n = 0; n < 256; n++ )
332 : {
333 0 : const sal_uInt8 cGray = (sal_uInt8) n;
334 0 : pCols[ n ] = mpAcc->GetBestMatchingColor( BitmapColor( cGray, cGray, cGray ) );
335 : }
336 :
337 0 : nAlignedWidth = AlignedWidth4Bytes( mpAcc->Width() * 8L );
338 :
339 0 : for( long nY = 0L; nY < nHeight; nY++ )
340 : {
341 0 : pTmp = (sal_uInt8*) mpBuffer + nY * nAlignedWidth;
342 :
343 0 : for( long nX = 0L; nX < nWidth; nX++ )
344 : {
345 0 : mpAcc->SetPixel( nY, nX, pCols[ *pTmp++ ] );
346 : }
347 : }
348 :
349 0 : delete[] pCols;
350 : }
351 : else
352 : {
353 24 : nAlignedWidth = AlignedWidth4Bytes( mpAcc->Width() * 24L );
354 :
355 4992 : for( long nY = 0L; nY < nHeight; nY++ )
356 : {
357 4968 : pTmp = (sal_uInt8*) mpBuffer + nY * nAlignedWidth;
358 :
359 2400544 : for( long nX = 0L; nX < nWidth; nX++ )
360 : {
361 2395576 : aColor.SetRed( *pTmp++ );
362 2395576 : aColor.SetGreen( *pTmp++ );
363 2395576 : aColor.SetBlue( *pTmp++ );
364 2395576 : mpAcc->SetPixel( nY, nX, aColor );
365 : }
366 : }
367 24 : }
368 : }
369 24 : }
370 :
371 0 : Graphic JPEGReader::CreateIntermediateGraphic( const Bitmap& rBitmap, long nLines )
372 : {
373 0 : Graphic aGraphic;
374 0 : const Size aSizePixel( rBitmap.GetSizePixel() );
375 :
376 0 : if( !mnLastLines )
377 : {
378 0 : if( mpAcc1 )
379 : {
380 0 : maBmp1.ReleaseAccess( mpAcc1 );
381 : }
382 :
383 0 : maBmp1 = Bitmap( rBitmap.GetSizePixel(), 1 );
384 0 : maBmp1.Erase( Color( COL_WHITE ) );
385 0 : mpAcc1 = maBmp1.AcquireWriteAccess();
386 : }
387 :
388 0 : if( nLines && ( nLines < aSizePixel.Height() ) )
389 : {
390 0 : if( mpAcc1 )
391 : {
392 0 : const long nNewLines = nLines - mnLastLines;
393 :
394 0 : if( nNewLines )
395 : {
396 0 : mpAcc1->SetFillColor( Color( COL_BLACK ) );
397 0 : mpAcc1->FillRect( Rectangle( Point( 0, mnLastLines ), Size( mpAcc1->Width(), nNewLines ) ) );
398 : }
399 :
400 0 : maBmp1.ReleaseAccess( mpAcc1 );
401 0 : aGraphic = BitmapEx( rBitmap, maBmp1 );
402 0 : mpAcc1 = maBmp1.AcquireWriteAccess();
403 : }
404 : else
405 : {
406 0 : aGraphic = rBitmap;
407 : }
408 : }
409 : else
410 0 : aGraphic = rBitmap;
411 :
412 0 : mnLastLines = nLines;
413 :
414 0 : return aGraphic;
415 : }
416 :
417 30 : ReadState JPEGReader::Read( Graphic& rGraphic )
418 : {
419 : long nEndPosition;
420 : long nLines;
421 : ReadState eReadState;
422 30 : bool bRet = false;
423 : sal_uInt8 cDummy;
424 :
425 : // TODO: is it possible to get rid of this seek to the end?
426 : // check if the stream's end is already available
427 30 : mrStream.Seek( STREAM_SEEK_TO_END );
428 30 : mrStream >> cDummy;
429 30 : nEndPosition = mrStream.Tell();
430 :
431 : // else check if at least JPEG_MIN_READ bytes can be read
432 30 : if( mrStream.GetError() == ERRCODE_IO_PENDING )
433 : {
434 0 : mrStream.ResetError();
435 0 : if( ( nEndPosition - mnFormerPos ) < JPEG_MIN_READ )
436 : {
437 0 : mrStream.Seek( mnLastPos );
438 0 : return JPEGREAD_NEED_MORE;
439 : }
440 : }
441 :
442 : // seek back to the original position
443 30 : mrStream.Seek( mnLastPos );
444 :
445 30 : Size aPreviewSize = GetPreviewSize();
446 30 : SetJpegPreviewSizeHint( aPreviewSize.Width(), aPreviewSize.Height() );
447 :
448 : // read the (partial) image
449 30 : ReadJPEG( this, &mrStream, &nLines );
450 :
451 30 : if( mpAcc )
452 : {
453 25 : if( mpBuffer )
454 : {
455 24 : FillBitmap();
456 24 : rtl_freeMemory( mpBuffer );
457 24 : mpBuffer = NULL;
458 : }
459 :
460 25 : maBmp.ReleaseAccess( mpAcc );
461 25 : mpAcc = NULL;
462 :
463 25 : if( mrStream.GetError() == ERRCODE_IO_PENDING )
464 : {
465 0 : rGraphic = CreateIntermediateGraphic( maBmp, nLines );
466 : }
467 : else
468 : {
469 25 : rGraphic = maBmp;
470 : }
471 :
472 25 : bRet = true;
473 : }
474 5 : else if( mrStream.GetError() == ERRCODE_IO_PENDING )
475 : {
476 0 : bRet = true;
477 : }
478 :
479 : // Set status ( Pending has priority )
480 30 : if( mrStream.GetError() == ERRCODE_IO_PENDING )
481 : {
482 0 : eReadState = JPEGREAD_NEED_MORE;
483 0 : mrStream.ResetError();
484 0 : mnFormerPos = mrStream.Tell();
485 : }
486 : else
487 : {
488 30 : eReadState = bRet ? JPEGREAD_OK : JPEGREAD_ERROR;
489 : }
490 :
491 30 : return eReadState;
492 465 : }
493 :
494 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|