RawSpeed
fast raw decoding library
Loading...
Searching...
No Matches
JpegDecompressor.cpp
Go to the documentation of this file.
1/*
2 RawSpeed - RAW file decoder.
3
4 Copyright (C) 2009-2014 Klaus Post
5 Copyright (C) 2017 Roman Lebedev
6
7 This library is free software; you can redistribute it and/or
8 modify it under the terms of the GNU Lesser General Public
9 License as published by the Free Software Foundation; either
10 version 2 of the License, or (at your option) any later version.
11
12 This library is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
16
17 You should have received a copy of the GNU Lesser General Public
18 License along with this library; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20*/
21
22#include "rawspeedconfig.h" // IWYU pragma: keep
23
24#ifdef HAVE_JPEG
25
27#include "adt/Array2DRef.h"
28#include "adt/Point.h"
31#include "io/IOException.h"
32#include <algorithm>
33#include <array>
34#include <cstddef>
35#include <cstdint>
36#include <jpeglib.h>
37#include <memory>
38#include <vector>
39
40using std::min;
41using std::unique_ptr;
42
43namespace rawspeed {
44
45namespace {
46
47/* Read JPEG image from a memory segment */
48
49void init_source(j_decompress_ptr /*cinfo*/) {
50 // No action needed.
51}
52
53boolean fill_input_buffer(j_decompress_ptr cinfo) {
54 return cinfo->src->bytes_in_buffer != 0;
55}
56
57// NOLINTNEXTLINE(google-runtime-int)
58void skip_input_data(j_decompress_ptr cinfo, long num_bytes) {
59 auto* src = cinfo->src;
60
61 if (num_bytes > static_cast<int>(src->bytes_in_buffer))
62 ThrowIOE("read out of buffer");
63 if (num_bytes > 0) {
64#pragma GCC diagnostic push
65#pragma GCC diagnostic ignored "-Wpragmas"
66#pragma GCC diagnostic ignored "-Wunknown-warning-option"
67#pragma GCC diagnostic ignored "-Wunsafe-buffer-usage"
68 src->next_input_byte += static_cast<size_t>(num_bytes);
69#pragma GCC diagnostic pop
70 src->bytes_in_buffer -= static_cast<size_t>(num_bytes);
71 }
72}
73
74void term_source(j_decompress_ptr /*cinfo*/) {
75 // No action needed.
76}
77
78[[maybe_unused]] void
79jpeg_mem_src_int(j_decompress_ptr cinfo, const unsigned char* buffer,
80 long nbytes) { // NOLINT(google-runtime-int)
81 jpeg_source_mgr* src;
82
83 if (cinfo->src == nullptr) { /* first time for this JPEG object? */
84 void* buf =
85 cinfo->mem->alloc_small(reinterpret_cast<j_common_ptr>(cinfo),
86 JPOOL_PERMANENT, sizeof(jpeg_source_mgr));
87 cinfo->src = static_cast<jpeg_source_mgr*>(buf);
88 }
89
90 src = cinfo->src;
91 src->init_source = init_source;
92 src->fill_input_buffer = fill_input_buffer;
93 src->skip_input_data = skip_input_data;
94 src->resync_to_restart = jpeg_resync_to_restart; /* use default method */
95 src->term_source = term_source;
96 src->bytes_in_buffer = nbytes;
97 src->next_input_byte = buffer;
98}
99
100// NOLINTNEXTLINE(readability-static-definition-in-anonymous-namespace)
101[[noreturn]] METHODDEF(void) my_error_throw(j_common_ptr cinfo) {
102 std::array<char, JMSG_LENGTH_MAX> buf;
103 buf.fill(0);
104 cinfo->err->format_message(cinfo, buf.data());
105 ThrowRDE("JPEG decoder error: %s", buf.data());
106}
107
108} // namespace
109
110struct JpegDecompressor::JpegDecompressStruct final : jpeg_decompress_struct {
111 struct jpeg_error_mgr jerr;
112
113 JpegDecompressStruct(const JpegDecompressStruct&) = delete;
114 JpegDecompressStruct(JpegDecompressStruct&&) noexcept = delete;
115 JpegDecompressStruct&
116 operator=(const JpegDecompressStruct&) noexcept = delete;
117 JpegDecompressStruct& operator=(JpegDecompressStruct&&) noexcept = delete;
118
119 JpegDecompressStruct() {
120 jpeg_create_decompress(this);
121
122 err = jpeg_std_error(&jerr);
123 jerr.error_exit = &my_error_throw;
124 }
125 ~JpegDecompressStruct() { jpeg_destroy_decompress(this); }
126};
127
128void JpegDecompressor::decode(uint32_t offX,
129 uint32_t offY) { /* Each slice is a JPEG image */
130 JpegDecompressStruct dinfo;
131
132#ifdef HAVE_JPEG_MEM_SRC
133 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast): why, macOS?!
134 jpeg_mem_src(&dinfo, const_cast<uint8_t*>(input.begin()), input.getSize());
135#else
136 jpeg_mem_src_int(&dinfo, input.begin(), input.getSize());
137#endif
138
139 if (JPEG_HEADER_OK != jpeg_read_header(&dinfo, static_cast<boolean>(true)))
140 ThrowRDE("Unable to read JPEG header");
141
142 jpeg_start_decompress(&dinfo);
143 if (dinfo.output_components != static_cast<int>(mRaw->getCpp()))
144 ThrowRDE("Component count doesn't match");
145 int row_stride = dinfo.output_width * dinfo.output_components;
146
147 std::vector<uint8_t, AlignedAllocator<uint8_t, 16>> complete_buffer;
148 complete_buffer.resize(dinfo.output_height * row_stride);
149
150 const Array2DRef<uint8_t> tmp(&complete_buffer[0],
151 dinfo.output_components * dinfo.output_width,
152 dinfo.output_height, row_stride);
153
154 while (dinfo.output_scanline < dinfo.output_height) {
155 JSAMPROW rowOut = &tmp(dinfo.output_scanline, 0);
156 if (0 == jpeg_read_scanlines(&dinfo, &rowOut, 1))
157 ThrowRDE("JPEG Error while decompressing image.");
158 }
159 jpeg_finish_decompress(&dinfo);
160
161 // Now the image is decoded, and we copy the image data
162 int copy_w = min(mRaw->dim.x - offX, dinfo.output_width);
163 int copy_h = min(mRaw->dim.y - offY, dinfo.output_height);
164
165 const Array2DRef<uint16_t> out(mRaw->getU16DataAsUncroppedArray2DRef());
166 for (int row = 0; row < copy_h; row++) {
167 for (int col = 0; col < dinfo.output_components * copy_w; col++)
168 out(row + offY, (dinfo.output_components * offX) + col) = tmp(row, col);
169 }
170}
171
172} // namespace rawspeed
173
174#else
175
176#pragma message \
177 "JPEG is not present! Lossy JPEG compression will not be supported!"
178
179#endif
#define ThrowIOE(...)
Definition IOException.h:37
#define ThrowRDE(...)
void RAWSPEED_UNLIKELY_FUNCTION RAWSPEED_NOINLINE static char buf[bufSize]