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