LibreOffice Module vcl (master)  1
JpegReader.cxx
Go to the documentation of this file.
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/graphicfilter.hxx>
28 #include <vcl/outdev.hxx>
29 #include <tools/fract.hxx>
30 #include <tools/stream.hxx>
31 #include <memory>
32 
33 #define BUFFER_SIZE 4096
34 
35 extern "C" {
36 
37 /*
38  * Initialize source --- called by jpeg_read_header
39  * before any data is actually read.
40  */
41 static void init_source (j_decompress_ptr cinfo)
42 {
43  SourceManagerStruct * source = reinterpret_cast<SourceManagerStruct *>(cinfo->src);
44 
45  /* We reset the empty-input-file flag for each image,
46  * but we don't clear the input buffer.
47  * This is correct behavior for reading a series of images from one source.
48  */
49  source->start_of_file = TRUE;
50  source->no_data_available = FALSE;
51 }
52 
53 }
54 
55 static long StreamRead( SvStream* pStream, void* pBuffer, long nBufferSize )
56 {
57  long nRead = 0;
58 
59  if( pStream->GetError() != ERRCODE_IO_PENDING )
60  {
61  long nInitialPosition = pStream->Tell();
62 
63  nRead = static_cast<long>(pStream->ReadBytes(pBuffer, nBufferSize));
64 
65  if( pStream->GetError() == ERRCODE_IO_PENDING )
66  {
67  // in order to search from the old position
68  // we temporarily reset the error
69  pStream->ResetError();
70  pStream->Seek( nInitialPosition );
71  pStream->SetError( ERRCODE_IO_PENDING );
72  }
73  }
74 
75  return nRead;
76 }
77 
78 extern "C" {
79 
80 static boolean fill_input_buffer (j_decompress_ptr cinfo)
81 {
82  SourceManagerStruct * source = reinterpret_cast<SourceManagerStruct *>(cinfo->src);
83  size_t nbytes;
84 
85  nbytes = StreamRead(source->stream, source->buffer, BUFFER_SIZE);
86 
87  if (!nbytes)
88  {
89  source->no_data_available = TRUE;
90  if (source->start_of_file) /* Treat empty input file as fatal error */
91  {
92  ERREXIT(cinfo, JERR_INPUT_EMPTY);
93  }
94  WARNMS(cinfo, JWRN_JPEG_EOF);
95  /* Insert a fake EOI marker */
96  source->buffer[0] = JOCTET(0xFF);
97  source->buffer[1] = JOCTET(JPEG_EOI);
98  nbytes = 2;
99  }
100 
101  source->pub.next_input_byte = source->buffer;
102  source->pub.bytes_in_buffer = nbytes;
103  source->start_of_file = FALSE;
104 
105  return TRUE;
106 }
107 
108 static void skip_input_data (j_decompress_ptr cinfo, long numberOfBytes)
109 {
110  SourceManagerStruct * source = reinterpret_cast<SourceManagerStruct *>(cinfo->src);
111 
112  /* Just a dumb implementation for now. Could use fseek() except
113  * it doesn't work on pipes. Not clear that being smart is worth
114  * any trouble anyway --- large skips are infrequent.
115  */
116  if (numberOfBytes > 0)
117  {
118  while (numberOfBytes > static_cast<long>(source->pub.bytes_in_buffer))
119  {
120  numberOfBytes -= static_cast<long>(source->pub.bytes_in_buffer);
121  (void) fill_input_buffer(cinfo);
122 
123  /* note we assume that fill_input_buffer will never return false,
124  * so suspension need not be handled.
125  */
126  }
127  source->pub.next_input_byte += static_cast<size_t>(numberOfBytes);
128  source->pub.bytes_in_buffer -= static_cast<size_t>(numberOfBytes);
129  }
130 }
131 
132 static void term_source (j_decompress_ptr)
133 {
134  /* no work necessary here */
135 }
136 
137 }
138 
139 void jpeg_svstream_src (j_decompress_ptr cinfo, void* input)
140 {
141  SourceManagerStruct * source;
142  SvStream* stream = static_cast<SvStream*>(input);
143 
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.
150  */
151 
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)));
159  }
160 
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 */
170 }
171 
173  mrStream ( rStream ),
174  mnLastPos ( rStream.Tell() ),
175  mnLastLines ( 0 ),
176  mbSetLogSize ( nImportFlags & GraphicFilterImportFlags::SetLogsizeForJpeg )
177 {
178  maUpperName = "SVIJPEG";
179 
180  if (!(nImportFlags & GraphicFilterImportFlags::UseExistingBitmap))
181  {
182  mpBitmap.reset(new Bitmap());
183  mpIncompleteAlpha.reset(new Bitmap());
184  }
185 }
186 
188 {
189 }
190 
192 {
193  if (rParam.nWidth > SAL_MAX_INT32 / 8 || rParam.nHeight > SAL_MAX_INT32 / 8)
194  return false; // avoid overflows later
195 
196  if (rParam.nWidth == 0 || rParam.nHeight == 0)
197  return false;
198 
199  Size aSize(rParam.nWidth, rParam.nHeight);
200  bool bGray = rParam.bGray;
201 
202  mpBitmap.reset(new Bitmap());
203 
204  sal_uInt64 nSize = aSize.Width() * aSize.Height();
205 
206  if (nSize > SAL_MAX_INT32 / (bGray?1:3))
207  return false;
208 
209  if( bGray )
210  {
211  BitmapPalette aGrayPal( 256 );
212 
213  for( sal_uInt16 n = 0; n < 256; n++ )
214  {
215  const sal_uInt8 cGray = static_cast<sal_uInt8>(n);
216  aGrayPal[ n ] = BitmapColor( cGray, cGray, cGray );
217  }
218 
219  mpBitmap.reset(new Bitmap(aSize, 8, &aGrayPal));
220  }
221  else
222  {
223  mpBitmap.reset(new Bitmap(aSize, 24));
224  }
225 
226  if (mbSetLogSize)
227  {
228  unsigned long nUnit = rParam.density_unit;
229 
230  if (((1 == nUnit) || (2 == nUnit)) && rParam.X_density && rParam.Y_density )
231  {
232  Fraction aFractX( 1, rParam.X_density );
233  Fraction aFractY( 1, rParam.Y_density );
234  MapMode aMapMode( nUnit == 1 ? MapUnit::MapInch : MapUnit::MapCM, Point(), aFractX, aFractY );
235  Size aPrefSize = OutputDevice::LogicToLogic(aSize, aMapMode, MapMode(MapUnit::Map100thMM));
236 
237  mpBitmap->SetPrefSize(aPrefSize);
238  mpBitmap->SetPrefMapMode(MapMode(MapUnit::Map100thMM));
239  }
240  }
241 
242  return true;
243 }
244 
246 {
247  Graphic aGraphic;
248  const Size aSizePixel(mpBitmap->GetSizePixel());
249 
250  if (!mnLastLines)
251  {
252  mpIncompleteAlpha.reset(new Bitmap(aSizePixel, 1));
254  }
255 
256  if (nLines && (nLines < aSizePixel.Height()))
257  {
258  const long nNewLines = nLines - mnLastLines;
259 
260  if (nNewLines > 0)
261  {
262  {
264  pAccess->SetFillColor(COL_BLACK);
265  pAccess->FillRect(tools::Rectangle(Point(0, mnLastLines), Size(pAccess->Width(), nNewLines)));
266  }
267 
268  aGraphic = BitmapEx(*mpBitmap, *mpIncompleteAlpha);
269  }
270  else
271  {
272  aGraphic = *mpBitmap;
273  }
274  }
275  else
276  {
277  aGraphic = *mpBitmap;
278  }
279 
280  mnLastLines = nLines;
281 
282  return aGraphic;
283 }
284 
286 {
287  ReadState eReadState;
288  bool bRet = false;
289 
290  // seek back to the original position
292 
293  // read the (partial) image
294  long nLines;
295  ReadJPEG( this, &mrStream, &nLines, GetPreviewSize(), nImportFlags, ppAccess );
296 
297  auto bUseExistingBitmap = static_cast<bool>(nImportFlags & GraphicFilterImportFlags::UseExistingBitmap);
298  if (bUseExistingBitmap || !mpBitmap->IsEmpty())
299  {
301  {
302  rGraphic = CreateIntermediateGraphic(nLines);
303  }
304  else
305  {
306  if (!bUseExistingBitmap)
307  rGraphic = *mpBitmap;
308  }
309 
310  bRet = true;
311  }
312  else if( mrStream.GetError() == ERRCODE_IO_PENDING )
313  {
314  bRet = true;
315  }
316 
317  // Set status ( Pending has priority )
319  {
320  eReadState = JPEGREAD_NEED_MORE;
322  }
323  else
324  {
325  eReadState = bRet ? JPEGREAD_OK : JPEGREAD_ERROR;
326  }
327 
328  return eReadState;
329 }
330 
331 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
long Width() const
std::unique_ptr< Bitmap > mpBitmap
Definition: JpegReader.hxx:50
Read pixel data into an existing bitmap.
virtual ~JPEGReader() override
Definition: JpegReader.cxx:187
bool CreateBitmap(JPEGCreateBitmapParam const &param)
Definition: JpegReader.cxx:191
ReadState Read(Graphic &rGraphic, GraphicFilterImportFlags nImportFlags, BitmapScopedWriteAccess *ppAccess)
Definition: JpegReader.cxx:285
constexpr::Color COL_BLACK(0x00, 0x00, 0x00)
JOCTET * buffer
Definition: jpeg.h:60
long mnLastLines
Definition: JpegReader.hxx:54
Point LogicToLogic(const Point &rPtSource, const MapMode *pMapModeSource, const MapMode *pMapModeDest) const
Definition: map.cxx:1674
long Height() const
static boolean fill_input_buffer(j_decompress_ptr cinfo)
Definition: JpegReader.cxx:80
unsigned long X_density
Definition: JpegReader.hxx:41
unsigned long density_unit
Definition: JpegReader.hxx:40
sal_uInt64 Seek(sal_uInt64 nPos)
ReadState
Definition: gifread.cxx:41
static long StreamRead(SvStream *pStream, void *pBuffer, long nBufferSize)
Definition: JpegReader.cxx:55
long Width() const
OUString maUpperName
Definition: graph.hxx:68
static void init_source(j_decompress_ptr cinfo)
Definition: JpegReader.cxx:41
boolean start_of_file
Definition: jpeg.h:61
JPEGReader(SvStream &rStream, GraphicFilterImportFlags nImportFlags)
Definition: JpegReader.cxx:172
tools::SvRef< SotStorageStream > stream
std::unique_ptr< Bitmap > mpIncompleteAlpha
Definition: JpegReader.hxx:51
unsigned long nWidth
Definition: JpegReader.hxx:38
void FillRect(const tools::Rectangle &rRect)
Definition: bmpacc3.cxx:236
ErrCode GetError() const
static void term_source(j_decompress_ptr)
Definition: JpegReader.cxx:132
void ReadJPEG(JPEGReader *pJPEGReader, void *pInputStream, long *pLines, Size const &previewSize, GraphicFilterImportFlags nImportFlags, BitmapScopedWriteAccess *ppAccess)
Definition: jpegc.cxx:350
SvStream & mrStream
Definition: JpegReader.hxx:49
unsigned long Y_density
Definition: JpegReader.hxx:42
Size GetPreviewSize() const
Definition: impgraph.cxx:99
#define BUFFER_SIZE
Definition: JpegReader.cxx:33
SvStream * stream
Definition: jpeg.h:59
#define SAL_MAX_INT32
#define TRUE
Graphic CreateIntermediateGraphic(long nLines)
Definition: JpegReader.cxx:245
void SetError(ErrCode nErrorCode)
long const mnLastPos
Definition: JpegReader.hxx:53
unsigned long nHeight
Definition: JpegReader.hxx:39
void jpeg_svstream_src(j_decompress_ptr cinfo, void *input)
Definition: JpegReader.cxx:139
std::size_t ReadBytes(void *pData, std::size_t nSize)
static void skip_input_data(j_decompress_ptr cinfo, long numberOfBytes)
Definition: JpegReader.cxx:108
unsigned char sal_uInt8
bool const mbSetLogSize
Definition: JpegReader.hxx:55
jpeg_source_mgr pub
Definition: jpeg.h:58
sal_uInt64 Tell() const
constexpr::Color COL_WHITE(0xFF, 0xFF, 0xFF)
#define FALSE
void SetFillColor()
Definition: bmpacc3.cxx:44
#define ERRCODE_IO_PENDING
Definition: errcode.hxx:227
virtual void ResetError()
boolean no_data_available
Definition: jpeg.h:62
GraphicFilterImportFlags
typedef void(CALLTYPE *GetFuncDataPtr)(sal_uInt16 &nNo