build fix: no comphelper/profilezone.hxx in this branch
[LibreOffice.git] / vcl / source / filter / jpeg / JpegReader.cxx
blobcde54c49dbde91d828275ed58b36d37dae703f97
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 "jpeg.h"
23 #include <jpeglib.h>
24 #include <jerror.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>
31 #include <memory>
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 )
62 long nRead = 0;
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 );
80 return nRead;
83 extern "C" boolean fill_input_buffer (j_decompress_ptr cinfo)
85 SourceManagerStruct * source = reinterpret_cast<SourceManagerStruct *>(cinfo->src);
86 size_t nbytes;
88 nbytes = StreamRead(source->stream, source->buffer, BUFFER_SIZE);
90 if (!nbytes)
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;
100 nbytes = 2;
103 source->pub.next_input_byte = source->buffer;
104 source->pub.bytes_in_buffer = nbytes;
105 source->start_of_file = FALSE;
107 return TRUE;
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() ),
175 mnLastLines ( 0 ),
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)
192 return false;
194 Size aSize(rParam.nWidth, rParam.nHeight);
195 bool bGray = rParam.bGray;
197 maBitmap = Bitmap();
199 sal_uInt64 nSize = aSize.Width() * aSize.Height();
201 if (nSize > SAL_MAX_INT32 / (bGray?1:3))
202 return false;
204 if( bGray )
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);
216 else
218 maBitmap = Bitmap(aSize, 24);
221 if (mbSetLogSize)
223 unsigned long nUnit = rParam.density_unit;
225 if (((1 == nUnit) || (2 == nUnit)) && rParam.X_density && rParam.Y_density )
227 Point aEmptyPoint;
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));
238 return true;
241 Graphic JPEGReader::CreateIntermediateGraphic(long nLines)
243 Graphic aGraphic;
244 const Size aSizePixel(maBitmap.GetSizePixel());
246 if (!mnLastLines)
248 maIncompleteAlpha = Bitmap(aSizePixel, 1);
249 maIncompleteAlpha.Erase(Color(COL_WHITE));
252 if (nLines && (nLines < aSizePixel.Height()))
254 const long nNewLines = nLines - mnLastLines;
256 if (nNewLines > 0)
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);
266 else
268 aGraphic = maBitmap;
271 else
273 aGraphic = maBitmap;
276 mnLastLines = nLines;
278 return aGraphic;
281 ReadState JPEGReader::Read( Graphic& rGraphic )
283 long nEndPosition;
284 long nLines;
285 ReadState eReadState;
286 bool bRet = false;
287 sal_uInt8 cDummy;
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);
318 else
320 rGraphic = maBitmap;
323 bRet = true;
325 else if( mrStream.GetError() == ERRCODE_IO_PENDING )
327 bRet = true;
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();
337 else
339 eReadState = bRet ? JPEGREAD_OK : JPEGREAD_ERROR;
342 return eReadState;
345 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */