Branch libreoffice-5-0-4
[LibreOffice.git] / vcl / source / filter / jpeg / JpegReader.cxx
blob5785224a7602e59bce42cb2df73d5fe837f956c0
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include <sal/config.h>
22 #include "stdio.h"
23 #include "jpeg.h"
24 #include <jpeglib.h>
25 #include <jerror.h>
27 #include "JpegReader.hxx"
28 #include <vcl/bmpacc.hxx>
29 #include <vcl/FilterConfigItem.hxx>
30 #include <vcl/graphicfilter.hxx>
31 #include <tools/fract.hxx>
32 #include <boost/scoped_array.hpp>
34 #define JPEG_MIN_READ 512
35 #define BUFFER_SIZE 4096
36 namespace {
37 // Arbitrary maximal size (512M) of a bitmap after it has been decoded.
38 // It is used to prevent excessive swapping due to large buffers in
39 // virtual memory.
40 // May have to be tuned if it turns out to be too large or too small.
41 static const sal_uInt64 MAX_BITMAP_BYTE_SIZE = sal_uInt64(512 * 1024 * 1024);
44 /* Expanded data source object for stdio input */
46 struct SourceManagerStruct {
47 jpeg_source_mgr pub; /* public fields */
48 SvStream* stream; /* source stream */
49 JOCTET* buffer; /* start of buffer */
50 boolean start_of_file; /* have we gotten any data yet? */
54 * Initialize source --- called by jpeg_read_header
55 * before any data is actually read.
57 extern "C" void init_source (j_decompress_ptr cinfo)
59 SourceManagerStruct * source = reinterpret_cast<SourceManagerStruct *>(cinfo->src);
61 /* We reset the empty-input-file flag for each image,
62 * but we don't clear the input buffer.
63 * This is correct behavior for reading a series of images from one source.
65 source->start_of_file = TRUE;
68 long StreamRead( SvStream* pStream, void* pBuffer, long nBufferSize )
70 long nRead = 0;
72 if( pStream->GetError() != ERRCODE_IO_PENDING )
74 long nInitialPosition = pStream->Tell();
76 nRead = (long) pStream->Read( pBuffer, nBufferSize );
78 if( pStream->GetError() == ERRCODE_IO_PENDING )
80 // in order to search from the old position
81 // we temporarily reset the error
82 pStream->ResetError();
83 pStream->Seek( nInitialPosition );
84 pStream->SetError( ERRCODE_IO_PENDING );
88 return nRead;
91 extern "C" boolean fill_input_buffer (j_decompress_ptr cinfo)
93 SourceManagerStruct * source = reinterpret_cast<SourceManagerStruct *>(cinfo->src);
94 size_t nbytes;
96 nbytes = StreamRead(source->stream, source->buffer, BUFFER_SIZE);
98 if (!nbytes)
100 if (source->start_of_file) /* Treat empty input file as fatal error */
102 ERREXIT(cinfo, JERR_INPUT_EMPTY);
104 WARNMS(cinfo, JWRN_JPEG_EOF);
105 /* Insert a fake EOI marker */
106 source->buffer[0] = (JOCTET) 0xFF;
107 source->buffer[1] = (JOCTET) JPEG_EOI;
108 nbytes = 2;
111 source->pub.next_input_byte = source->buffer;
112 source->pub.bytes_in_buffer = nbytes;
113 source->start_of_file = FALSE;
115 return TRUE;
118 extern "C" void skip_input_data (j_decompress_ptr cinfo, long numberOfBytes)
120 SourceManagerStruct * source = reinterpret_cast<SourceManagerStruct *>(cinfo->src);
122 /* Just a dumb implementation for now. Could use fseek() except
123 * it doesn't work on pipes. Not clear that being smart is worth
124 * any trouble anyway --- large skips are infrequent.
126 if (numberOfBytes > 0)
128 while (numberOfBytes > (long) source->pub.bytes_in_buffer)
130 numberOfBytes -= (long) source->pub.bytes_in_buffer;
131 (void) fill_input_buffer(cinfo);
133 /* note we assume that fill_input_buffer will never return false,
134 * so suspension need not be handled.
137 source->pub.next_input_byte += (size_t) numberOfBytes;
138 source->pub.bytes_in_buffer -= (size_t) numberOfBytes;
142 extern "C" void term_source (j_decompress_ptr)
144 /* no work necessary here */
147 void jpeg_svstream_src (j_decompress_ptr cinfo, void* input)
149 SourceManagerStruct * source;
150 SvStream* stream = static_cast<SvStream*>(input);
152 /* The source object and input buffer are made permanent so that a series
153 * of JPEG images can be read from the same file by calling jpeg_stdio_src
154 * only before the first one. (If we discarded the buffer at the end of
155 * one image, we'd likely lose the start of the next one.)
156 * This makes it unsafe to use this manager and a different source
157 * manager serially with the same JPEG object. Caveat programmer.
160 if (cinfo->src == NULL)
161 { /* first time for this JPEG object? */
162 cinfo->src = static_cast<jpeg_source_mgr *>(
163 (*cinfo->mem->alloc_small) (reinterpret_cast<j_common_ptr>(cinfo), JPOOL_PERMANENT, sizeof(SourceManagerStruct)));
164 source = reinterpret_cast<SourceManagerStruct *>(cinfo->src);
165 source->buffer = static_cast<JOCTET *>(
166 (*cinfo->mem->alloc_small) (reinterpret_cast<j_common_ptr>(cinfo), JPOOL_PERMANENT, BUFFER_SIZE * sizeof(JOCTET)));
169 source = reinterpret_cast<SourceManagerStruct *>(cinfo->src);
170 source->pub.init_source = init_source;
171 source->pub.fill_input_buffer = fill_input_buffer;
172 source->pub.skip_input_data = skip_input_data;
173 source->pub.resync_to_restart = jpeg_resync_to_restart; /* use default method */
174 source->pub.term_source = term_source;
175 source->stream = stream;
176 source->pub.bytes_in_buffer = 0; /* forces fill_input_buffer on first read */
177 source->pub.next_input_byte = NULL; /* until buffer loaded */
180 JPEGReader::JPEGReader( SvStream& rStream, void* /*pCallData*/, bool bSetLogSize ) :
181 mrStream ( rStream ),
182 mpAcc ( NULL ),
183 mpAcc1 ( NULL ),
184 mpBuffer ( NULL ),
185 mnLastPos ( rStream.Tell() ),
186 mnLastLines ( 0 ),
187 mbSetLogSize ( bSetLogSize )
189 maUpperName = "SVIJPEG";
190 mnFormerPos = mnLastPos;
193 JPEGReader::~JPEGReader()
195 if( mpBuffer )
196 delete[] mpBuffer;
198 if( mpAcc )
199 Bitmap::ReleaseAccess( mpAcc );
201 if( mpAcc1 )
202 Bitmap::ReleaseAccess( mpAcc1 );
205 unsigned char * JPEGReader::CreateBitmap( JPEGCreateBitmapParam * pParam )
207 if (pParam->nWidth > SAL_MAX_INT32 / 8 || pParam->nHeight > SAL_MAX_INT32 / 8)
208 return NULL; // avoid overflows later
210 if (pParam->nWidth == 0 || pParam->nHeight == 0)
211 return NULL;
213 Size aSize( pParam->nWidth, pParam->nHeight );
214 bool bGray = pParam->bGray != 0;
216 unsigned char * pBmpBuf = NULL;
218 if( mpAcc )
220 Bitmap::ReleaseAccess( mpAcc );
221 maBmp = Bitmap();
222 mpAcc = NULL;
225 sal_uInt64 nSize = aSize.Width();
226 nSize *= aSize.Height();
227 if (nSize > SAL_MAX_INT32 / (bGray?1:3))
228 return NULL;
230 // Check if the bitmap is untypically large.
231 if (nSize*(bGray?1:3) > MAX_BITMAP_BYTE_SIZE)
233 // Do not try to acquire resources for the large bitmap or to
234 // read the bitmap into memory.
235 return NULL;
238 if( bGray )
240 BitmapPalette aGrayPal( 256 );
242 for( sal_uInt16 n = 0; n < 256; n++ )
244 const sal_uInt8 cGray = (sal_uInt8) n;
245 aGrayPal[ n ] = BitmapColor( cGray, cGray, cGray );
248 maBmp = Bitmap( aSize, 8, &aGrayPal );
250 else
252 maBmp = Bitmap( aSize, 24 );
255 if ( mbSetLogSize )
257 unsigned long nUnit = pParam->density_unit;
259 if( ( ( 1 == nUnit ) || ( 2 == nUnit ) ) && pParam->X_density && pParam->Y_density )
261 Point aEmptyPoint;
262 Fraction aFractX( 1, pParam->X_density );
263 Fraction aFractY( 1, pParam->Y_density );
264 MapMode aMapMode( nUnit == 1 ? MAP_INCH : MAP_CM, aEmptyPoint, aFractX, aFractY );
265 Size aPrefSize = OutputDevice::LogicToLogic( aSize, aMapMode, MAP_100TH_MM );
267 maBmp.SetPrefSize( aPrefSize );
268 maBmp.SetPrefMapMode( MapMode( MAP_100TH_MM ) );
272 mpAcc = maBmp.AcquireWriteAccess();
274 if( mpAcc )
276 const sal_uLong nFormat = mpAcc->GetScanlineFormat();
279 ( bGray && ( BMP_FORMAT_8BIT_PAL == nFormat ) ) ||
280 ( !bGray && ( BMP_FORMAT_24BIT_TC_RGB == nFormat ) )
283 pBmpBuf = mpAcc->GetBuffer();
284 pParam->nAlignedWidth = mpAcc->GetScanlineSize();
285 pParam->bTopDown = mpAcc->IsTopDown();
287 else
289 pParam->nAlignedWidth = AlignedWidth4Bytes( aSize.Width() * ( bGray ? 8 : 24 ) );
290 pParam->bTopDown = true;
291 pBmpBuf = mpBuffer = new unsigned char[pParam->nAlignedWidth * aSize.Height()];
295 // clean up, if no Bitmap buffer can be provided.
296 if ( !pBmpBuf )
298 Bitmap::ReleaseAccess( mpAcc );
299 maBmp = Bitmap();
300 mpAcc = NULL;
303 return pBmpBuf;
306 void JPEGReader::FillBitmap()
308 if( mpBuffer && mpAcc )
310 unsigned char * pTmp;
311 BitmapColor aColor;
312 long nAlignedWidth;
313 long nWidth = mpAcc->Width();
314 long nHeight = mpAcc->Height();
316 if( mpAcc->GetBitCount() == 8 )
318 boost::scoped_array<BitmapColor> pCols(new BitmapColor[ 256 ]);
320 for( sal_uInt16 n = 0; n < 256; n++ )
322 const sal_uInt8 cGray = (sal_uInt8) n;
323 pCols[ n ] = mpAcc->GetBestMatchingColor( BitmapColor( cGray, cGray, cGray ) );
326 nAlignedWidth = AlignedWidth4Bytes( mpAcc->Width() * 8L );
328 for( long nY = 0L; nY < nHeight; nY++ )
330 pTmp = (sal_uInt8*) mpBuffer + nY * nAlignedWidth;
332 for( long nX = 0L; nX < nWidth; nX++ )
334 mpAcc->SetPixel( nY, nX, pCols[ *pTmp++ ] );
338 else
340 nAlignedWidth = AlignedWidth4Bytes( mpAcc->Width() * 24L );
342 for( long nY = 0L; nY < nHeight; nY++ )
344 // #i122985# Added fast-lane implementations using CopyScanline with direct supported mem formats
345 static bool bCheckOwnReader(true);
347 if(bCheckOwnReader)
349 // #i122985# Trying to copy the RGB data from jpeg import to make things faster. Unfortunately
350 // it has no GBR format, so RGB three-byte groups need to be 'flipped' to GBR first,
351 // then CopyScanline can use a memcpy to do the data transport. CopyScanline can also
352 // do the needed conversion from BMP_FORMAT_24BIT_TC_RGB (and it works well), but this
353 // is not faster that the old loop below using SetPixel.
354 sal_uInt8* aSource((sal_uInt8*)mpBuffer + nY * nAlignedWidth);
355 sal_uInt8* aEnd(aSource + (nWidth * 3));
357 for(sal_uInt8* aTmp(aSource); aTmp < aEnd; aTmp += 3)
359 ::std::swap(*aTmp, *(aTmp + 2));
362 mpAcc->CopyScanline(nY, aSource, BMP_FORMAT_24BIT_TC_BGR, nWidth * 3);
364 else
366 // old version: WritePixel
367 pTmp = (sal_uInt8*) mpBuffer + nY * nAlignedWidth;
369 for( long nX = 0L; nX < nWidth; nX++ )
371 aColor.SetRed( *pTmp++ );
372 aColor.SetGreen( *pTmp++ );
373 aColor.SetBlue( *pTmp++ );
374 mpAcc->SetPixel( nY, nX, aColor );
382 Graphic JPEGReader::CreateIntermediateGraphic( const Bitmap& rBitmap, long nLines )
384 Graphic aGraphic;
385 const Size aSizePixel( rBitmap.GetSizePixel() );
387 if( !mnLastLines )
389 if( mpAcc1 )
391 Bitmap::ReleaseAccess( mpAcc1 );
394 maBmp1 = Bitmap( rBitmap.GetSizePixel(), 1 );
395 maBmp1.Erase( Color( COL_WHITE ) );
396 mpAcc1 = maBmp1.AcquireWriteAccess();
399 if( nLines && ( nLines < aSizePixel.Height() ) )
401 if( mpAcc1 )
403 const long nNewLines = nLines - mnLastLines;
405 if( nNewLines )
407 mpAcc1->SetFillColor( Color( COL_BLACK ) );
408 mpAcc1->FillRect( Rectangle( Point( 0, mnLastLines ), Size( mpAcc1->Width(), nNewLines ) ) );
411 Bitmap::ReleaseAccess( mpAcc1 );
412 aGraphic = BitmapEx( rBitmap, maBmp1 );
413 mpAcc1 = maBmp1.AcquireWriteAccess();
415 else
417 aGraphic = rBitmap;
420 else
421 aGraphic = rBitmap;
423 mnLastLines = nLines;
425 return aGraphic;
428 ReadState JPEGReader::Read( Graphic& rGraphic )
430 long nEndPosition;
431 long nLines;
432 ReadState eReadState;
433 bool bRet = false;
434 sal_uInt8 cDummy;
436 // TODO: is it possible to get rid of this seek to the end?
437 // check if the stream's end is already available
438 mrStream.Seek( STREAM_SEEK_TO_END );
439 mrStream.ReadUChar( cDummy );
440 nEndPosition = mrStream.Tell();
442 // else check if at least JPEG_MIN_READ bytes can be read
443 if( mrStream.GetError() == ERRCODE_IO_PENDING )
445 mrStream.ResetError();
446 if( ( nEndPosition - mnFormerPos ) < JPEG_MIN_READ )
448 mrStream.Seek( mnLastPos );
449 return JPEGREAD_NEED_MORE;
453 // seek back to the original position
454 mrStream.Seek( mnLastPos );
456 // read the (partial) image
457 ReadJPEG( this, &mrStream, &nLines, GetPreviewSize() );
459 if( mpAcc )
461 if( mpBuffer )
463 FillBitmap();
464 delete[] mpBuffer;
465 mpBuffer = NULL;
468 Bitmap::ReleaseAccess( mpAcc );
469 mpAcc = NULL;
471 if( mrStream.GetError() == ERRCODE_IO_PENDING )
473 rGraphic = CreateIntermediateGraphic( maBmp, nLines );
475 else
477 rGraphic = maBmp;
480 bRet = true;
482 else if( mrStream.GetError() == ERRCODE_IO_PENDING )
484 bRet = true;
487 // Set status ( Pending has priority )
488 if( mrStream.GetError() == ERRCODE_IO_PENDING )
490 eReadState = JPEGREAD_NEED_MORE;
491 mrStream.ResetError();
492 mnFormerPos = mrStream.Tell();
494 else
496 eReadState = bRet ? JPEGREAD_OK : JPEGREAD_ERROR;
499 return eReadState;
502 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */