1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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>
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
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
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
)
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
);
91 extern "C" boolean
fill_input_buffer (j_decompress_ptr cinfo
)
93 SourceManagerStruct
* source
= reinterpret_cast<SourceManagerStruct
*>(cinfo
->src
);
96 nbytes
= StreamRead(source
->stream
, source
->buffer
, BUFFER_SIZE
);
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
;
111 source
->pub
.next_input_byte
= source
->buffer
;
112 source
->pub
.bytes_in_buffer
= nbytes
;
113 source
->start_of_file
= FALSE
;
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
),
185 mnLastPos ( rStream
.Tell() ),
187 mbSetLogSize ( bSetLogSize
)
189 maUpperName
= "SVIJPEG";
190 mnFormerPos
= mnLastPos
;
193 JPEGReader::~JPEGReader()
199 Bitmap::ReleaseAccess( mpAcc
);
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)
213 Size
aSize( pParam
->nWidth
, pParam
->nHeight
);
214 bool bGray
= pParam
->bGray
!= 0;
216 unsigned char * pBmpBuf
= NULL
;
220 Bitmap::ReleaseAccess( mpAcc
);
225 sal_uInt64 nSize
= aSize
.Width();
226 nSize
*= aSize
.Height();
227 if (nSize
> SAL_MAX_INT32
/ (bGray
?1:3))
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.
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
);
252 maBmp
= Bitmap( aSize
, 24 );
257 unsigned long nUnit
= pParam
->density_unit
;
259 if( ( ( 1 == nUnit
) || ( 2 == nUnit
) ) && pParam
->X_density
&& pParam
->Y_density
)
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();
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();
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.
298 Bitmap::ReleaseAccess( mpAcc
);
306 void JPEGReader::FillBitmap()
308 if( mpBuffer
&& mpAcc
)
310 unsigned char * pTmp
;
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
++ ] );
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);
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);
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
)
385 const Size
aSizePixel( rBitmap
.GetSizePixel() );
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() ) )
403 const long nNewLines
= nLines
- mnLastLines
;
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();
423 mnLastLines
= nLines
;
428 ReadState
JPEGReader::Read( Graphic
& rGraphic
)
432 ReadState eReadState
;
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() );
468 Bitmap::ReleaseAccess( mpAcc
);
471 if( mrStream
.GetError() == ERRCODE_IO_PENDING
)
473 rGraphic
= CreateIntermediateGraphic( maBmp
, nLines
);
482 else if( mrStream
.GetError() == ERRCODE_IO_PENDING
)
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();
496 eReadState
= bRet
? JPEGREAD_OK
: JPEGREAD_ERROR
;
502 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */