Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
jpegmodule.cpp
Go to the documentation of this file.
1// Copyright 2014 The PDFium Authors
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
6
7#include "core/fxcodec/jpeg/jpegmodule.h"
8
9#include <memory>
10#include <optional>
11#include <type_traits>
12#include <utility>
13
14#include "build/build_config.h"
15#include "core/fxcodec/cfx_codec_memory.h"
16#include "core/fxcodec/jpeg/jpeg_common.h"
17#include "core/fxcodec/scanlinedecoder.h"
18#include "core/fxcrt/check.h"
19#include "core/fxcrt/check_op.h"
20#include "core/fxcrt/compiler_specific.h"
21#include "core/fxcrt/data_vector.h"
22#include "core/fxcrt/fx_safe_types.h"
23#include "core/fxcrt/raw_span.h"
24#include "core/fxge/dib/cfx_dibbase.h"
25#include "core/fxge/dib/fx_dib.h"
26
28 pdfium::span<const uint8_t> src_span) {
29 DCHECK(!src_span.empty());
30
31 for (size_t offset = 0; offset + 1 < src_span.size(); ++offset) {
32 if (src_span[offset] == 0xff && src_span[offset + 1] == 0xd8)
33 return src_span.subspan(offset);
34 }
35 return src_span;
36}
37
38extern "C" {
39
40static void error_fatal(j_common_ptr cinfo) {
41 longjmp(*(jmp_buf*)cinfo->client_data, -1);
42}
43
44static void src_skip_data(jpeg_decompress_struct* cinfo, long num) {
45 if (num > (long)cinfo->src->bytes_in_buffer) {
46 error_fatal((j_common_ptr)cinfo);
47 }
48 // SAFETY: required from library API as checked above.
49 UNSAFE_BUFFERS(cinfo->src->next_input_byte += num);
50 cinfo->src->bytes_in_buffer -= num;
51}
52
53#if BUILDFLAG(IS_WIN)
54static void dest_do_nothing(j_compress_ptr cinfo) {}
55
56static boolean dest_empty(j_compress_ptr cinfo) {
57 return false;
58}
59#endif // BUILDFLAG(IS_WIN)
60
61} // extern "C"
62
63static bool JpegLoadInfo(pdfium::span<const uint8_t> src_span,
64 JpegModule::ImageInfo* pInfo) {
65 src_span = JpegScanSOI(src_span);
66
67 JpegCommon jpeg_common = {};
68 jpeg_common.error_mgr.error_exit = error_fatal;
69 jpeg_common.error_mgr.emit_message = jpeg_common_error_do_nothing_int;
70 jpeg_common.error_mgr.output_message = jpeg_common_error_do_nothing;
71 jpeg_common.error_mgr.format_message = jpeg_common_error_do_nothing_char;
72 jpeg_common.error_mgr.reset_error_mgr = jpeg_common_error_do_nothing;
73 jpeg_common.error_mgr.trace_level = 0;
74 jpeg_common.cinfo.err = &jpeg_common.error_mgr;
75 jpeg_common.cinfo.client_data = &jpeg_common.jmpbuf;
76 if (!jpeg_common_create_decompress(&jpeg_common)) {
77 return false;
78 }
79
80 jpeg_common.source_mgr.init_source = jpeg_common_src_do_nothing;
81 jpeg_common.source_mgr.term_source = jpeg_common_src_do_nothing;
82 jpeg_common.source_mgr.skip_input_data = src_skip_data;
83 jpeg_common.source_mgr.fill_input_buffer = jpeg_common_src_fill_buffer;
84 jpeg_common.source_mgr.resync_to_restart = jpeg_common_src_resync;
85 jpeg_common.source_mgr.bytes_in_buffer = src_span.size();
86 jpeg_common.source_mgr.next_input_byte = src_span.data();
87 jpeg_common.cinfo.src = &jpeg_common.source_mgr;
88 if (jpeg_common_read_header(&jpeg_common, TRUE) != JPEG_HEADER_OK) {
90 return false;
91 }
92 pInfo->width = jpeg_common.cinfo.image_width;
93 pInfo->height = jpeg_common.cinfo.image_height;
94 pInfo->num_components = jpeg_common.cinfo.num_components;
95 pInfo->color_transform = jpeg_common.cinfo.jpeg_color_space == JCS_YCbCr ||
96 jpeg_common.cinfo.jpeg_color_space == JCS_YCCK;
97 pInfo->bits_per_components = jpeg_common.cinfo.data_precision;
99 return true;
100}
101
102namespace fxcodec {
103
104namespace {
105
106constexpr size_t kKnownBadHeaderWithInvalidHeightByteOffsetStarts[] = {94, 163};
107
108class JpegDecoder final : public ScanlineDecoder {
109 public:
110 JpegDecoder();
111 ~JpegDecoder() override;
112
113 bool Create(pdfium::span<const uint8_t> src_span,
114 uint32_t width,
115 uint32_t height,
116 int nComps,
117 bool ColorTransform);
118
119 // ScanlineDecoder:
120 bool Rewind() override;
121 pdfium::span<uint8_t> GetNextLine() override;
122 uint32_t GetSrcOffset() override;
123
124 bool InitDecode(bool bAcceptKnownBadHeader);
125
126 private:
127 void CalcPitch();
128 void InitDecompressSrc();
129
130 // Only called when initial jpeg_read_header() fails.
131 bool HasKnownBadHeaderWithInvalidHeight(size_t dimension_offset) const;
132
133 // Is a JPEG SOFn marker, which is defined as 0xff, 0xc[0-9a-f].
134 bool IsSofSegment(size_t marker_offset) const;
135
136 // Patch up the in-memory JPEG header for known bad JPEGs.
137 void PatchUpKnownBadHeaderWithInvalidHeight(size_t dimension_offset);
138
139 // Patch up the JPEG trailer, even if it is correct.
140 void PatchUpTrailer();
141
142 pdfium::span<uint8_t> GetWritableSrcData();
143
144 // For a given invalid height byte offset in
145 // |kKnownBadHeaderWithInvalidHeightByteOffsetStarts|, the SOFn marker should
146 // be this many bytes before that.
147 static constexpr size_t kSofMarkerByteOffset = 5;
148
149 JpegCommon m_Common = {};
150 pdfium::raw_span<const uint8_t> m_SrcSpan;
151 DataVector<uint8_t> m_ScanlineBuf;
152 bool m_bInited = false;
153 bool m_bStarted = false;
154 bool m_bJpegTransform = false;
155 uint32_t m_nDefaultScaleDenom = 1;
156};
157
158JpegDecoder::JpegDecoder() = default;
159
160JpegDecoder::~JpegDecoder() {
161 if (m_bInited) {
162 jpeg_common_destroy_decompress(&m_Common);
163 }
164
165 // Span in superclass can't outlive our buffer.
166 m_pLastScanline = pdfium::span<uint8_t>();
167}
168
169bool JpegDecoder::InitDecode(bool bAcceptKnownBadHeader) {
170 m_Common.cinfo.err = &m_Common.error_mgr;
171 m_Common.cinfo.client_data = &m_Common.jmpbuf;
172 if (!jpeg_common_create_decompress(&m_Common)) {
173 return false;
174 }
175 InitDecompressSrc();
176 m_bInited = true;
177 m_Common.cinfo.image_width = m_OrigWidth;
178 m_Common.cinfo.image_height = m_OrigHeight;
179 if (jpeg_common_read_header(&m_Common, TRUE) != JPEG_HEADER_OK) {
180 std::optional<size_t> known_bad_header_offset;
181 if (bAcceptKnownBadHeader) {
182 for (size_t offset : kKnownBadHeaderWithInvalidHeightByteOffsetStarts) {
183 if (HasKnownBadHeaderWithInvalidHeight(offset)) {
184 known_bad_header_offset = offset;
185 break;
186 }
187 }
188 }
189 jpeg_common_destroy_decompress(&m_Common);
190 if (!known_bad_header_offset.has_value()) {
191 m_bInited = false;
192 return false;
193 }
194 PatchUpKnownBadHeaderWithInvalidHeight(known_bad_header_offset.value());
195 if (!jpeg_common_create_decompress(&m_Common)) {
196 return false;
197 }
198 InitDecompressSrc();
199 m_Common.cinfo.image_width = m_OrigWidth;
200 m_Common.cinfo.image_height = m_OrigHeight;
201 if (jpeg_common_read_header(&m_Common, TRUE) != JPEG_HEADER_OK) {
202 jpeg_common_destroy_decompress(&m_Common);
203 return false;
204 }
205 }
206 if (m_Common.cinfo.saw_Adobe_marker) {
207 m_bJpegTransform = true;
208 }
209
210 if (m_Common.cinfo.num_components == 3 && !m_bJpegTransform) {
211 m_Common.cinfo.out_color_space = m_Common.cinfo.jpeg_color_space;
212 }
213
214 m_OrigWidth = m_Common.cinfo.image_width;
215 m_OrigHeight = m_Common.cinfo.image_height;
218 m_nDefaultScaleDenom = m_Common.cinfo.scale_denom;
219 return true;
220}
221
222bool JpegDecoder::Create(pdfium::span<const uint8_t> src_span,
223 uint32_t width,
224 uint32_t height,
225 int nComps,
226 bool ColorTransform) {
227 m_SrcSpan = JpegScanSOI(src_span);
228 if (m_SrcSpan.size() < 2)
229 return false;
230
231 PatchUpTrailer();
232
233 m_Common.error_mgr.error_exit = error_fatal;
234 m_Common.error_mgr.emit_message = jpeg_common_error_do_nothing_int;
235 m_Common.error_mgr.output_message = jpeg_common_error_do_nothing;
236 m_Common.error_mgr.format_message = jpeg_common_error_do_nothing_char;
237 m_Common.error_mgr.reset_error_mgr = jpeg_common_error_do_nothing;
238 m_Common.source_mgr.init_source = jpeg_common_src_do_nothing;
239 m_Common.source_mgr.term_source = jpeg_common_src_do_nothing;
240 m_Common.source_mgr.skip_input_data = src_skip_data;
241 m_Common.source_mgr.fill_input_buffer = jpeg_common_src_fill_buffer;
242 m_Common.source_mgr.resync_to_restart = jpeg_common_src_resync;
243 m_bJpegTransform = ColorTransform;
244 m_OutputWidth = m_OrigWidth = width;
245 m_OutputHeight = m_OrigHeight = height;
246 if (!InitDecode(/*bAcceptKnownBadHeader=*/true))
247 return false;
248
249 if (m_Common.cinfo.num_components < nComps) {
250 return false;
251 }
252
253 if (m_Common.cinfo.image_width < width) {
254 return false;
255 }
256
257 CalcPitch();
258 m_ScanlineBuf = DataVector<uint8_t>(m_Pitch);
259 m_nComps = m_Common.cinfo.num_components;
260 m_bpc = 8;
261 m_bStarted = false;
262 return true;
263}
264
265bool JpegDecoder::Rewind() {
266 if (m_bStarted) {
267 jpeg_common_destroy_decompress(&m_Common);
268 if (!InitDecode(/*bAcceptKnownBadHeader=*/false)) {
269 return false;
270 }
271 }
272 m_Common.cinfo.scale_denom = m_nDefaultScaleDenom;
275 if (!jpeg_common_start_decompress(&m_Common)) {
276 jpeg_common_destroy_decompress(&m_Common);
277 return false;
278 }
279 CHECK_LE(static_cast<int>(m_Common.cinfo.output_width), m_OrigWidth);
280 m_bStarted = true;
281 return true;
282}
283
284pdfium::span<uint8_t> JpegDecoder::GetNextLine() {
285 uint8_t* row_array[] = {m_ScanlineBuf.data()};
286 int nlines = jpeg_common_read_scanlines(&m_Common, row_array, 1u);
287 if (nlines <= 0) {
288 return pdfium::span<uint8_t>();
289 }
290 return m_ScanlineBuf;
291}
292
293uint32_t JpegDecoder::GetSrcOffset() {
294 return static_cast<uint32_t>(m_SrcSpan.size() -
295 m_Common.source_mgr.bytes_in_buffer);
296}
297
298void JpegDecoder::CalcPitch() {
299 m_Pitch = static_cast<uint32_t>(m_Common.cinfo.image_width) *
300 m_Common.cinfo.num_components;
301 m_Pitch += 3;
302 m_Pitch /= 4;
303 m_Pitch *= 4;
304}
305
306void JpegDecoder::InitDecompressSrc() {
307 m_Common.cinfo.src = &m_Common.source_mgr;
308 m_Common.source_mgr.bytes_in_buffer = m_SrcSpan.size();
309 m_Common.source_mgr.next_input_byte = m_SrcSpan.data();
310}
311
312bool JpegDecoder::HasKnownBadHeaderWithInvalidHeight(
313 size_t dimension_offset) const {
314 // Perform lots of possibly redundant checks to make sure this has no false
315 // positives.
316 bool bDimensionChecks =
317 m_Common.cinfo.err->msg_code == JERR_IMAGE_TOO_BIG &&
318 m_Common.cinfo.image_width < JPEG_MAX_DIMENSION &&
319 m_Common.cinfo.image_height == 0xffff && m_OrigWidth > 0 &&
320 m_OrigWidth <= JPEG_MAX_DIMENSION && m_OrigHeight > 0 &&
321 m_OrigHeight <= JPEG_MAX_DIMENSION;
322 if (!bDimensionChecks)
323 return false;
324
325 if (m_SrcSpan.size() <= dimension_offset + 3u)
326 return false;
327
328 if (!IsSofSegment(dimension_offset - kSofMarkerByteOffset))
329 return false;
330
331 const auto pHeaderDimensions = m_SrcSpan.subspan(dimension_offset);
332 uint8_t nExpectedWidthByte1 = (m_OrigWidth >> 8) & 0xff;
333 uint8_t nExpectedWidthByte2 = m_OrigWidth & 0xff;
334 // Height high byte, height low byte, width high byte, width low byte.
335 return pHeaderDimensions[0] == 0xff && pHeaderDimensions[1] == 0xff &&
336 pHeaderDimensions[2] == nExpectedWidthByte1 &&
337 pHeaderDimensions[3] == nExpectedWidthByte2;
338}
339
340bool JpegDecoder::IsSofSegment(size_t marker_offset) const {
341 const auto pHeaderMarker = m_SrcSpan.subspan(marker_offset);
342 return pHeaderMarker[0] == 0xff && pHeaderMarker[1] >= 0xc0 &&
343 pHeaderMarker[1] <= 0xcf;
344}
345
346void JpegDecoder::PatchUpKnownBadHeaderWithInvalidHeight(
347 size_t dimension_offset) {
348 DCHECK(m_SrcSpan.size() > dimension_offset + 1u);
349 auto pData = GetWritableSrcData().subspan(dimension_offset);
350 pData[0] = (m_OrigHeight >> 8) & 0xff;
351 pData[1] = m_OrigHeight & 0xff;
352}
353
354void JpegDecoder::PatchUpTrailer() {
355 auto pData = GetWritableSrcData();
356 pData[m_SrcSpan.size() - 2] = 0xff;
357 pData[m_SrcSpan.size() - 1] = 0xd9;
358}
359
360pdfium::span<uint8_t> JpegDecoder::GetWritableSrcData() {
361 // SAFETY: const_cast<> doesn't change size.
362 return UNSAFE_BUFFERS(pdfium::make_span(
363 const_cast<uint8_t*>(m_SrcSpan.data()), m_SrcSpan.size()));
364}
365
366} // namespace
367
368// static
369std::unique_ptr<ScanlineDecoder> JpegModule::CreateDecoder(
370 pdfium::span<const uint8_t> src_span,
371 uint32_t width,
372 uint32_t height,
373 int nComps,
374 bool ColorTransform) {
375 DCHECK(!src_span.empty());
376
377 auto pDecoder = std::make_unique<JpegDecoder>();
378 if (!pDecoder->Create(src_span, width, height, nComps, ColorTransform))
379 return nullptr;
380
381 return pDecoder;
382}
383
384// static
385std::optional<JpegModule::ImageInfo> JpegModule::LoadInfo(
386 pdfium::span<const uint8_t> src_span) {
387 ImageInfo info;
388 if (!JpegLoadInfo(src_span, &info))
389 return std::nullopt;
390
391 return info;
392}
393
394#if BUILDFLAG(IS_WIN)
397 size_t* dest_size) {
404
405 jpeg_compress_struct cinfo = {}; // Aggregate initialization.
406 static_assert(std::is_aggregate_v<decltype(cinfo)>);
407 cinfo.err = &jerr;
409 const int bytes_per_pixel = pSource->GetBPP() / 8;
410 uint32_t nComponents = bytes_per_pixel >= 3 ? 3 : 1;
417 safe_buf_len += 1024;
418 if (!safe_buf_len.IsValid())
419 return false;
420
423 const int MIN_TRY_BUF_LEN = 1024;
424 while (!(*dest_buf) && dest_buf_length > MIN_TRY_BUF_LEN) {
425 dest_buf_length >>= 1;
427 }
428 if (!(*dest_buf))
429 return false;
430
437 cinfo.dest = &dest;
441 if (nComponents == 1) {
443 } else if (nComponents == 3) {
445 } else {
447 }
448 uint8_t* line_buf = nullptr;
449 if (nComponents > 1)
451
457 pdfium::span<const uint8_t> src_scan =
459 if (nComponents > 1) {
461 if (nComponents == 3) {
463 for (uint32_t i = 0; i < width; i++) {
465 dest_scan += 3;
467 }
468 });
469 } else {
471 for (uint32_t i = 0; i < pitch; i++) {
472 *dest_scan++ = ~src_scan.front();
474 }
475 });
476 }
478 } else {
479 row_pointer[0] = const_cast<uint8_t*>(src_scan.data());
480 }
484 if (cinfo.next_scanline == row) {
485 constexpr size_t kJpegBlockSize = 1048576;
486 *dest_buf =
492 }
493 });
494 }
499
500 return true;
501}
502#endif // BUILDFLAG(IS_WIN)
503
504} // namespace fxcodec
#define DCHECK
Definition check.h:33
#define CHECK_LE(x, y)
Definition check_op.h:14
#define UNSAFE_BUFFERS(...)
void jpeg_common_destroy_decompress(JpegCommon *jpeg_common)
Definition jpeg_common.c:20
static void src_skip_data(jpeg_decompress_struct *cinfo, long num)
static pdfium::span< const uint8_t > JpegScanSOI(pdfium::span< const uint8_t > src_span)
static bool JpegLoadInfo(pdfium::span< const uint8_t > src_span, JpegModule::ImageInfo *pInfo)
static void error_fatal(j_common_ptr cinfo)
fxcodec::JpegModule JpegModule
Definition jpegmodule.h:64
jmp_buf jmpbuf
Definition jpeg_common.h:40