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 */
32extern "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
45namespace OpenRaw {
46
47using namespace Debug;
48
49namespace Internals {
50
51namespace {
52
53/* libjpeg callbacks j_ is the prefix for these callbacks */
54void j_init_source(::j_decompress_ptr cinfo);
55::boolean j_fill_input_buffer(::j_decompress_ptr cinfo);
56void j_skip_input_data(::j_decompress_ptr cinfo,
57 long num_bytes);
58void j_term_source(::j_decompress_ptr cinfo);
59void j_error_exit(::j_common_ptr cinfo);
60
61}
62
65#define BUF_SIZE 1024
66
67typedef struct {
68 struct jpeg_source_mgr pub;
70 off_t offset;
71 JOCTET* buf;
73
74JfifContainer::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
115bool 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
129bool 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
161int 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
176namespace {
177
178void 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
185void j_init_source(j_decompress_ptr)
186{
187}
188
189
190boolean 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
207void 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
222void j_term_source(j_decompress_ptr)
223{
224}
225
226}
227
228std::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
259IfdDir::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*/
virtual void setDimensions(uint32_t x, uint32_t y)
std::unique_ptr< IfdFileContainer > & ifdContainer()
Option< uint16_t > readUInt16(const IO::Stream::Ptr &f)
CIFF is the container for CRW files. It is an attempt from Canon to make this a standard....
Definition arwfile.cpp:30
struct jpeg_source_mgr pub