libopenraw  0.3.7
jfifcontainer.cpp
1 /*
2  * libopenraw - jfifcontainer.cpp
3  *
4  * Copyright (C) 2006-2020 Hubert Figuière
5  *
6  * This library is free software: you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public License
8  * as published by the Free Software Foundation, either version 3 of
9  * the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library. If not, see
18  * <http://www.gnu.org/licenses/>.
19  */
20 
21 
22 #include <setjmp.h>
23 #include <fcntl.h>
24 #include <stddef.h>
25 #include <string.h>
26 #include <memory>
27 
28 /*
29  * The extern "C" below is REQUIRED for libjpeg-mmx-dev
30  * as found on debian because some people have this installed.
31  */
32 extern "C" {
33 #include <jpeglib.h>
34 }
35 
36 #include <libopenraw/debug.h>
37 
38 #include "bitmapdata.hpp"
39 #include "io/stream.hpp"
40 #include "io/streamclone.hpp"
41 #include "trace.hpp"
42 #include "jfifcontainer.hpp"
43 #include "ifdfilecontainer.hpp"
44 
45 namespace OpenRaw {
46 
47 using namespace Debug;
48 
49 namespace Internals {
50 
51 namespace {
52 
53 /* libjpeg callbacks j_ is the prefix for these callbacks */
54 void j_init_source(::j_decompress_ptr cinfo);
55 ::boolean j_fill_input_buffer(::j_decompress_ptr cinfo);
56 void j_skip_input_data(::j_decompress_ptr cinfo,
57  long num_bytes);
58 void j_term_source(::j_decompress_ptr cinfo);
59 void j_error_exit(::j_common_ptr cinfo);
60 
61 }
62 
65 #define BUF_SIZE 1024
66 
67 typedef struct {
68  struct jpeg_source_mgr pub;
69  JfifContainer * self;
70  off_t offset;
71  JOCTET* buf;
72 } jpeg_src_t;
73 
74 JfifContainer::JfifContainer(const IO::Stream::Ptr &_file, off_t _offset)
75  : RawContainer(_file, _offset),
76  m_cinfo(), m_jerr(),
77  m_headerLoaded(false), m_exif_offset(0)
78 {
79  setEndian(ENDIAN_BIG);
80  /* this is a hack because jpeg_create_decompress is
81  * implemented as a Macro
82  */
83 
84  m_cinfo.err = jpeg_std_error(&m_jerr);
85  m_jerr.error_exit = &j_error_exit;
86  jpeg_create_decompress(&m_cinfo);
87 
88  /* inspired by jdatasrc.c */
89 
90  jpeg_src_t *src = (jpeg_src_t *)
91  (*m_cinfo.mem->alloc_small)((j_common_ptr)&m_cinfo,
92  JPOOL_PERMANENT,
93  sizeof(jpeg_src_t));
94  m_cinfo.src = (jpeg_source_mgr*)src;
95  src->pub.init_source = j_init_source;
96  src->pub.fill_input_buffer = j_fill_input_buffer;
97  src->pub.skip_input_data = j_skip_input_data;
98  src->pub.resync_to_restart = jpeg_resync_to_restart;
99  src->pub.term_source = j_term_source;
100  src->self = this;
101  src->pub.bytes_in_buffer = 0;
102  src->pub.next_input_byte = nullptr;
103  src->buf = (JOCTET*)(*m_cinfo.mem->alloc_small)
104  ((j_common_ptr)&m_cinfo,
105  JPOOL_PERMANENT,
106  BUF_SIZE * sizeof(JOCTET));
107 }
108 
110 {
111  jpeg_destroy_decompress(&m_cinfo);
112 }
113 
114 
115 bool JfifContainer::getDimensions(uint32_t &x, uint32_t &y)
116 {
117  if(!m_headerLoaded) {
118  if (_loadHeader() == 0) {
119  LOGDBG1("load header failed\n");
120  return false;
121  }
122  }
123  x = m_cinfo.output_width;
124  y = m_cinfo.output_height;
125  return true;
126 }
127 
128 
129 bool JfifContainer::getDecompressedData(BitmapData &data)
130 {
131  if(!m_headerLoaded) {
132  if (_loadHeader() == 0) {
133  LOGDBG1("load header failed\n");
134  return false;
135  }
136  }
137  if (::setjmp(m_jpegjmp) != 0) {
138  return false;
139  }
140  jpeg_start_decompress(&m_cinfo);
141  int row_size = m_cinfo.output_width * m_cinfo.output_components;
142  char *dataPtr
143  = (char*)data.allocData(row_size * m_cinfo.output_height);
144  char *currentPtr = dataPtr;
145  JSAMPARRAY buffer
146  = (*m_cinfo.mem->alloc_sarray)((j_common_ptr)&m_cinfo,
147  JPOOL_IMAGE, row_size,
148  1);
149  while (m_cinfo.output_scanline < m_cinfo.output_height) {
150  jpeg_read_scanlines(&m_cinfo, buffer, 1);
151  memcpy(currentPtr, buffer, row_size);
152  currentPtr += row_size;
153  }
154  data.setDimensions(m_cinfo.output_width, m_cinfo.output_height);
155 
156  jpeg_finish_decompress(&m_cinfo);
157  return true;
158 }
159 
160 
161 int JfifContainer::_loadHeader()
162 {
163 
164  m_file->seek(0, SEEK_SET);
165 
166  if (::setjmp(m_jpegjmp) == 0) {
167  int ret = jpeg_read_header(&m_cinfo, TRUE);
168 
169  jpeg_calc_output_dimensions(&m_cinfo);
170  m_headerLoaded = (ret == 1);
171  return ret;
172  }
173  return 0;
174 }
175 
176 namespace {
177 
178 __attribute__((noreturn))
179 void j_error_exit(j_common_ptr cinfo)
180 {
181  (*cinfo->err->output_message) (cinfo);
182  JfifContainer *self = ((jpeg_src_t *)(((j_decompress_ptr)cinfo)->src))->self;
183  ::longjmp(self->jpegjmp(), 1);
184 }
185 
186 void j_init_source(j_decompress_ptr)
187 {
188 }
189 
190 
191 boolean j_fill_input_buffer(j_decompress_ptr cinfo)
192 {
193  jpeg_src_t *src = (jpeg_src_t*)cinfo->src;
194  JfifContainer *self = src->self;
195  int n = self->file()->read(src->buf, BUF_SIZE * sizeof(*src->buf));
196  if (n >= 0) {
197  src->pub.next_input_byte = src->buf;
198  src->pub.bytes_in_buffer = n;
199  }
200  else {
201  src->pub.next_input_byte = nullptr;
202  src->pub.bytes_in_buffer = 0;
203  }
204  return TRUE;
205 }
206 
207 
208 void j_skip_input_data(j_decompress_ptr cinfo,
209  long num_bytes)
210 {
211  jpeg_src_t *src = (jpeg_src_t*)cinfo->src;
212  if (num_bytes > 0) {
213  while ((size_t)num_bytes > src->pub.bytes_in_buffer) {
214  num_bytes -= src->pub.bytes_in_buffer;
215  j_fill_input_buffer(cinfo);
216  }
217  src->pub.next_input_byte += (size_t) num_bytes;
218  src->pub.bytes_in_buffer -= (size_t) num_bytes;
219  }
220 }
221 
222 
223 void j_term_source(j_decompress_ptr)
224 {
225 }
226 
227 }
228 
229 std::unique_ptr<IfdFileContainer> & JfifContainer::ifdContainer()
230 {
231  if(!m_ifd) {
232  m_file->seek(0, SEEK_SET);
233 
234  // XXX check results and bail.
235  auto result = readUInt16(m_file, m_endian); // SOI
236  result = readUInt16(m_file, m_endian); // APP0
237  result = readUInt16(m_file, m_endian); // ignore
238 
239  char delim[7];
240  delim[6] = 0;
241  m_file->read(delim, 6);
242  if(memcmp(delim, "Exif\0\0", 6) == 0) {
243  m_exif_offset = m_file->seek(0, SEEK_CUR);
244  m_ifd.reset(new IfdFileContainer(
246  std::make_shared<IO::StreamClone>(m_file, m_exif_offset)), 0));
247  }
248  }
249  return m_ifd;
250 }
251 
253 {
254  if(ifdContainer()) {
255  return m_ifd->setDirectory(0);
256  }
257  return IfdDir::Ref();
258 }
259 
261 {
262  if(ifdContainer()) {
263  return m_ifd->setDirectory(idx);
264  }
265  return IfdDir::Ref();
266 }
267 
268 
270 {
271  IfdDir::Ref main = mainIfd();
272  return main->getExifIFD();
273 }
274 
275 }
276 }
277 /*
278  Local Variables:
279  mode:c++
280  c-file-style:"stroustrup"
281  c-file-offsets:((innamespace . 0))
282  tab-width:2
283  c-basic-offset:2
284  indent-tabs-mode:nil
285  fill-column:80
286  End:
287 */
std::shared_ptr< Stream > Ptr
Definition: stream.hpp:47
std::shared_ptr< IfdDir > Ref
Shared ptr of an IfdDir.
Definition: ifddir.hpp:56
std::unique_ptr< IfdFileContainer > & ifdContainer()
Generic interface for the RAW file container.
Option< uint16_t > readUInt16(const IO::Stream::Ptr &f, EndianType endian) const
Read an uint16 following the m_endian set.
IO::Stream::Ptr m_file
The file handle.
Global namespace for libopenraw.
Definition: arwfile.cpp:29
struct jpeg_source_mgr pub