libopenraw
jfifcontainer.cpp
1 /*
2  * libopenraw - jfifcontainer.cpp
3  *
4  * Copyright (C) 2006-2017 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)
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 void j_error_exit(j_common_ptr cinfo)
179 {
180  (*cinfo->err->output_message) (cinfo);
181  JfifContainer *self = ((jpeg_src_t *)(((j_decompress_ptr)cinfo)->src))->self;
182  ::longjmp(self->jpegjmp(), 1);
183 }
184 
185 void j_init_source(j_decompress_ptr)
186 {
187 }
188 
189 
190 boolean j_fill_input_buffer(j_decompress_ptr cinfo)
191 {
192  jpeg_src_t *src = (jpeg_src_t*)cinfo->src;
193  JfifContainer *self = src->self;
194  int n = self->file()->read(src->buf, BUF_SIZE * sizeof(*src->buf));
195  if (n >= 0) {
196  src->pub.next_input_byte = src->buf;
197  src->pub.bytes_in_buffer = n;
198  }
199  else {
200  src->pub.next_input_byte = nullptr;
201  src->pub.bytes_in_buffer = 0;
202  }
203  return TRUE;
204 }
205 
206 
207 void j_skip_input_data(j_decompress_ptr cinfo,
208  long num_bytes)
209 {
210  jpeg_src_t *src = (jpeg_src_t*)cinfo->src;
211  if (num_bytes > 0) {
212  while ((size_t)num_bytes > src->pub.bytes_in_buffer) {
213  num_bytes -= src->pub.bytes_in_buffer;
214  j_fill_input_buffer(cinfo);
215  }
216  src->pub.next_input_byte += (size_t) num_bytes;
217  src->pub.bytes_in_buffer -= (size_t) num_bytes;
218  }
219 }
220 
221 
222 void j_term_source(j_decompress_ptr)
223 {
224 }
225 
226 }
227 
228 std::unique_ptr<IfdFileContainer> & JfifContainer::ifdContainer()
229 {
230  if(!m_ifd) {
231  m_file->seek(0, SEEK_SET);
232 
233  // XXX check results and bail.
234  auto result = readUInt16(m_file); // SOI
235  result = readUInt16(m_file); // APP0
236  result = readUInt16(m_file); // ignore
237 
238  char delim[7];
239  delim[6] = 0;
240  m_file->read(delim, 6);
241  if(memcmp(delim, "Exif\0\0", 6) == 0) {
242  size_t exif_offset = m_file->seek(0, SEEK_CUR);
243  m_ifd.reset(new IfdFileContainer(
244  IO::Stream::Ptr(
245  std::make_shared<IO::StreamClone>(m_file, exif_offset)), 0));
246  }
247  }
248  return m_ifd;
249 }
250 
252 {
253  if(ifdContainer()) {
254  return m_ifd->setDirectory(0);
255  }
256  return IfdDir::Ref();
257 }
258 
259 IfdDir::Ref JfifContainer::getIfdDirAt(int idx)
260 {
261  if(ifdContainer()) {
262  return m_ifd->setDirectory(idx);
263  }
264  return IfdDir::Ref();
265 }
266 
267 
269 {
270  IfdDir::Ref main = mainIfd();
271  return main->getExifIFD();
272 }
273 
274 }
275 }
276 /*
277  Local Variables:
278  mode:c++
279  c-file-style:"stroustrup"
280  c-file-offsets:((innamespace . 0))
281  tab-width:2
282  c-basic-offset:2
283  indent-tabs-mode:nil
284  fill-column:80
285  End:
286 */
CIFF is the container for CRW files. It is an attempt from Canon to make this a standard. I guess it failed.
Definition: arwfile.cpp:30
std::unique_ptr< IfdFileContainer > & ifdContainer()
struct jpeg_source_mgr pub
virtual void setDimensions(uint32_t x, uint32_t y)
Definition: bitmapdata.cpp:169
Definition: trace.cpp:30