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>
26 #include "JpegReader.hxx"
27 #include <vcl/bitmapaccess.hxx>
28 #include <vcl/FilterConfigItem.hxx>
29 #include <vcl/graphicfilter.hxx>
30 #include <tools/fract.hxx>
33 #define JPEG_MIN_READ 512
34 #define BUFFER_SIZE 4096
36 /* Expanded data source object for stdio input */
38 struct SourceManagerStruct
{
39 jpeg_source_mgr pub
; /* public fields */
40 SvStream
* stream
; /* source stream */
41 JOCTET
* buffer
; /* start of buffer */
42 boolean start_of_file
; /* have we gotten any data yet? */
46 * Initialize source --- called by jpeg_read_header
47 * before any data is actually read.
49 extern "C" void init_source (j_decompress_ptr cinfo
)
51 SourceManagerStruct
* source
= reinterpret_cast<SourceManagerStruct
*>(cinfo
->src
);
53 /* We reset the empty-input-file flag for each image,
54 * but we don't clear the input buffer.
55 * This is correct behavior for reading a series of images from one source.
57 source
->start_of_file
= TRUE
;
60 long StreamRead( SvStream
* pStream
, void* pBuffer
, long nBufferSize
)
64 if( pStream
->GetError() != ERRCODE_IO_PENDING
)
66 long nInitialPosition
= pStream
->Tell();
68 nRead
= static_cast<long>(pStream
->ReadBytes(pBuffer
, nBufferSize
));
70 if( pStream
->GetError() == ERRCODE_IO_PENDING
)
72 // in order to search from the old position
73 // we temporarily reset the error
74 pStream
->ResetError();
75 pStream
->Seek( nInitialPosition
);
76 pStream
->SetError( ERRCODE_IO_PENDING
);
83 extern "C" boolean
fill_input_buffer (j_decompress_ptr cinfo
)
85 SourceManagerStruct
* source
= reinterpret_cast<SourceManagerStruct
*>(cinfo
->src
);
88 nbytes
= StreamRead(source
->stream
, source
->buffer
, BUFFER_SIZE
);
92 if (source
->start_of_file
) /* Treat empty input file as fatal error */
94 ERREXIT(cinfo
, JERR_INPUT_EMPTY
);
96 WARNMS(cinfo
, JWRN_JPEG_EOF
);
97 /* Insert a fake EOI marker */
98 source
->buffer
[0] = (JOCTET
) 0xFF;
99 source
->buffer
[1] = (JOCTET
) JPEG_EOI
;
103 source
->pub
.next_input_byte
= source
->buffer
;
104 source
->pub
.bytes_in_buffer
= nbytes
;
105 source
->start_of_file
= FALSE
;
110 extern "C" void skip_input_data (j_decompress_ptr cinfo
, long numberOfBytes
)
112 SourceManagerStruct
* source
= reinterpret_cast<SourceManagerStruct
*>(cinfo
->src
);
114 /* Just a dumb implementation for now. Could use fseek() except
115 * it doesn't work on pipes. Not clear that being smart is worth
116 * any trouble anyway --- large skips are infrequent.
118 if (numberOfBytes
> 0)
120 while (numberOfBytes
> (long) source
->pub
.bytes_in_buffer
)
122 numberOfBytes
-= (long) source
->pub
.bytes_in_buffer
;
123 (void) fill_input_buffer(cinfo
);
125 /* note we assume that fill_input_buffer will never return false,
126 * so suspension need not be handled.
129 source
->pub
.next_input_byte
+= (size_t) numberOfBytes
;
130 source
->pub
.bytes_in_buffer
-= (size_t) numberOfBytes
;
134 extern "C" void term_source (j_decompress_ptr
)
136 /* no work necessary here */
139 void jpeg_svstream_src (j_decompress_ptr cinfo
, void* input
)
141 SourceManagerStruct
* source
;
142 SvStream
* stream
= static_cast<SvStream
*>(input
);
144 /* The source object and input buffer are made permanent so that a series
145 * of JPEG images can be read from the same file by calling jpeg_stdio_src
146 * only before the first one. (If we discarded the buffer at the end of
147 * one image, we'd likely lose the start of the next one.)
148 * This makes it unsafe to use this manager and a different source
149 * manager serially with the same JPEG object. Caveat programmer.
152 if (cinfo
->src
== nullptr)
153 { /* first time for this JPEG object? */
154 cinfo
->src
= static_cast<jpeg_source_mgr
*>(
155 (*cinfo
->mem
->alloc_small
) (reinterpret_cast<j_common_ptr
>(cinfo
), JPOOL_PERMANENT
, sizeof(SourceManagerStruct
)));
156 source
= reinterpret_cast<SourceManagerStruct
*>(cinfo
->src
);
157 source
->buffer
= static_cast<JOCTET
*>(
158 (*cinfo
->mem
->alloc_small
) (reinterpret_cast<j_common_ptr
>(cinfo
), JPOOL_PERMANENT
, BUFFER_SIZE
* sizeof(JOCTET
)));
161 source
= reinterpret_cast<SourceManagerStruct
*>(cinfo
->src
);
162 source
->pub
.init_source
= init_source
;
163 source
->pub
.fill_input_buffer
= fill_input_buffer
;
164 source
->pub
.skip_input_data
= skip_input_data
;
165 source
->pub
.resync_to_restart
= jpeg_resync_to_restart
; /* use default method */
166 source
->pub
.term_source
= term_source
;
167 source
->stream
= stream
;
168 source
->pub
.bytes_in_buffer
= 0; /* forces fill_input_buffer on first read */
169 source
->pub
.next_input_byte
= nullptr; /* until buffer loaded */
172 JPEGReader::JPEGReader( SvStream
& rStream
, void* /*pCallData*/, bool bSetLogSize
) :
173 mrStream ( rStream
),
174 mnLastPos ( rStream
.Tell() ),
176 mbSetLogSize ( bSetLogSize
)
178 maUpperName
= "SVIJPEG";
179 mnFormerPos
= mnLastPos
;
182 JPEGReader::~JPEGReader()
186 bool JPEGReader::CreateBitmap(JPEGCreateBitmapParam
& rParam
)
188 if (rParam
.nWidth
> SAL_MAX_INT32
/ 8 || rParam
.nHeight
> SAL_MAX_INT32
/ 8)
189 return false; // avoid overflows later
191 if (rParam
.nWidth
== 0 || rParam
.nHeight
== 0)
194 Size
aSize(rParam
.nWidth
, rParam
.nHeight
);
195 bool bGray
= rParam
.bGray
;
199 sal_uInt64 nSize
= aSize
.Width() * aSize
.Height();
201 if (nSize
> SAL_MAX_INT32
/ (bGray
?1:3))
206 BitmapPalette
aGrayPal( 256 );
208 for( sal_uInt16 n
= 0; n
< 256; n
++ )
210 const sal_uInt8 cGray
= (sal_uInt8
) n
;
211 aGrayPal
[ n
] = BitmapColor( cGray
, cGray
, cGray
);
214 maBitmap
= Bitmap(aSize
, 8, &aGrayPal
);
218 maBitmap
= Bitmap(aSize
, 24);
223 unsigned long nUnit
= rParam
.density_unit
;
225 if (((1 == nUnit
) || (2 == nUnit
)) && rParam
.X_density
&& rParam
.Y_density
)
228 Fraction
aFractX( 1, rParam
.X_density
);
229 Fraction
aFractY( 1, rParam
.Y_density
);
230 MapMode
aMapMode( nUnit
== 1 ? MapUnit::MapInch
: MapUnit::MapCM
, aEmptyPoint
, aFractX
, aFractY
);
231 Size aPrefSize
= OutputDevice::LogicToLogic( aSize
, aMapMode
, MapUnit::Map100thMM
);
233 maBitmap
.SetPrefSize(aPrefSize
);
234 maBitmap
.SetPrefMapMode(MapMode(MapUnit::Map100thMM
));
241 Graphic
JPEGReader::CreateIntermediateGraphic(long nLines
)
244 const Size
aSizePixel(maBitmap
.GetSizePixel());
248 maIncompleteAlpha
= Bitmap(aSizePixel
, 1);
249 maIncompleteAlpha
.Erase(Color(COL_WHITE
));
252 if (nLines
&& (nLines
< aSizePixel
.Height()))
254 const long nNewLines
= nLines
- mnLastLines
;
259 Bitmap::ScopedWriteAccess
pAccess(maIncompleteAlpha
);
260 pAccess
->SetFillColor(Color(COL_BLACK
));
261 pAccess
->FillRect(Rectangle(Point(0, mnLastLines
), Size(pAccess
->Width(), nNewLines
)));
264 aGraphic
= BitmapEx(maBitmap
, maIncompleteAlpha
);
276 mnLastLines
= nLines
;
281 ReadState
JPEGReader::Read( Graphic
& rGraphic
)
285 ReadState eReadState
;
289 // TODO: is it possible to get rid of this seek to the end?
290 // check if the stream's end is already available
291 mrStream
.Seek( STREAM_SEEK_TO_END
);
292 mrStream
.ReadUChar( cDummy
);
293 nEndPosition
= mrStream
.Tell();
295 // else check if at least JPEG_MIN_READ bytes can be read
296 if( mrStream
.GetError() == ERRCODE_IO_PENDING
)
298 mrStream
.ResetError();
299 if( ( nEndPosition
- mnFormerPos
) < JPEG_MIN_READ
)
301 mrStream
.Seek( mnLastPos
);
302 return JPEGREAD_NEED_MORE
;
306 // seek back to the original position
307 mrStream
.Seek( mnLastPos
);
309 // read the (partial) image
310 ReadJPEG( this, &mrStream
, &nLines
, GetPreviewSize() );
312 if (!maBitmap
.IsEmpty())
314 if( mrStream
.GetError() == ERRCODE_IO_PENDING
)
316 rGraphic
= CreateIntermediateGraphic(nLines
);
325 else if( mrStream
.GetError() == ERRCODE_IO_PENDING
)
330 // Set status ( Pending has priority )
331 if( mrStream
.GetError() == ERRCODE_IO_PENDING
)
333 eReadState
= JPEGREAD_NEED_MORE
;
334 mrStream
.ResetError();
335 mnFormerPos
= mrStream
.Tell();
339 eReadState
= bRet
? JPEGREAD_OK
: JPEGREAD_ERROR
;
345 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */