7#include "core/fxcodec/jpeg/jpegmodule.h"
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"
28 pdfium::span<
const uint8_t> src_span) {
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);
41 longjmp(*(jmp_buf*)cinfo->client_data, -1);
45 if (num > (
long)cinfo->src->bytes_in_buffer) {
46 error_fatal((j_common_ptr)cinfo);
50 cinfo->src->bytes_in_buffer -= num;
54static void dest_do_nothing(j_compress_ptr cinfo) {}
56static boolean dest_empty(j_compress_ptr cinfo) {
65 src_span = JpegScanSOI(src_span);
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)) {
80 jpeg_common.source_mgr.init_source = jpeg_common_src_do_nothing;
81 jpeg_common.source_mgr.term_source = jpeg_common_src_do_nothing;
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) {
92 pInfo
->width = jpeg_common.cinfo.image_width;
93 pInfo
->height = jpeg_common.cinfo.image_height;
95 pInfo->color_transform = jpeg_common.cinfo.jpeg_color_space == JCS_YCbCr ||
96 jpeg_common.cinfo.jpeg_color_space == JCS_YCCK;
106constexpr size_t kKnownBadHeaderWithInvalidHeightByteOffsetStarts[] = {94, 163};
111 ~JpegDecoder()
override;
113 bool Create(pdfium::span<
const uint8_t> src_span,
117 bool ColorTransform);
120 bool Rewind()
override;
121 pdfium::span<uint8_t> GetNextLine()
override;
122 uint32_t GetSrcOffset()
override;
124 bool InitDecode(
bool bAcceptKnownBadHeader);
128 void InitDecompressSrc();
131 bool HasKnownBadHeaderWithInvalidHeight(size_t dimension_offset)
const;
134 bool IsSofSegment(size_t marker_offset)
const;
137 void PatchUpKnownBadHeaderWithInvalidHeight(size_t dimension_offset);
140 void PatchUpTrailer();
142 pdfium::span<uint8_t> GetWritableSrcData();
147 static constexpr size_t kSofMarkerByteOffset = 5;
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;
158JpegDecoder::JpegDecoder() =
default;
160JpegDecoder::~JpegDecoder() {
162 jpeg_common_destroy_decompress(&m_Common);
166 m_pLastScanline = pdfium::span<uint8_t>();
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)) {
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;
189 jpeg_common_destroy_decompress(&m_Common);
190 if (!known_bad_header_offset.has_value()) {
194 PatchUpKnownBadHeaderWithInvalidHeight(known_bad_header_offset.value());
195 if (!jpeg_common_create_decompress(&m_Common)) {
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);
206 if (m_Common.cinfo.saw_Adobe_marker) {
207 m_bJpegTransform =
true;
210 if (m_Common.cinfo.num_components == 3 && !m_bJpegTransform) {
211 m_Common.cinfo.out_color_space = m_Common.cinfo.jpeg_color_space;
214 m_OrigWidth = m_Common.cinfo.image_width;
215 m_OrigHeight = m_Common.cinfo.image_height;
218 m_nDefaultScaleDenom = m_Common.cinfo.scale_denom;
222bool JpegDecoder::Create(pdfium::span<
const uint8_t> src_span,
226 bool ColorTransform) {
227 m_SrcSpan = JpegScanSOI(src_span);
228 if (m_SrcSpan.size() < 2)
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;
246 if (!InitDecode(
true))
249 if (m_Common.cinfo.num_components < nComps) {
253 if (m_Common.cinfo.image_width < width) {
258 m_ScanlineBuf = DataVector<uint8_t>(m_Pitch);
259 m_nComps = m_Common.cinfo.num_components;
265bool JpegDecoder::Rewind() {
267 jpeg_common_destroy_decompress(&m_Common);
268 if (!InitDecode(
false)) {
272 m_Common.cinfo.scale_denom = m_nDefaultScaleDenom;
275 if (!jpeg_common_start_decompress(&m_Common)) {
276 jpeg_common_destroy_decompress(&m_Common);
279 CHECK_LE(
static_cast<
int>(m_Common.cinfo.output_width), m_OrigWidth);
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);
288 return pdfium::span<uint8_t>();
290 return m_ScanlineBuf;
293uint32_t JpegDecoder::GetSrcOffset() {
294 return static_cast<uint32_t>(m_SrcSpan.size() -
295 m_Common.source_mgr.bytes_in_buffer);
298void JpegDecoder::CalcPitch() {
299 m_Pitch =
static_cast<uint32_t>(m_Common.cinfo.image_width) *
300 m_Common.cinfo.num_components;
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();
312bool JpegDecoder::HasKnownBadHeaderWithInvalidHeight(
313 size_t dimension_offset)
const {
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)
325 if (m_SrcSpan.size() <= dimension_offset + 3u)
328 if (!IsSofSegment(dimension_offset - kSofMarkerByteOffset))
331 const auto pHeaderDimensions = m_SrcSpan.subspan(dimension_offset);
332 uint8_t nExpectedWidthByte1 = (
m_OrigWidth >> 8) & 0xff;
335 return pHeaderDimensions[0] == 0xff && pHeaderDimensions[1] == 0xff &&
336 pHeaderDimensions[2] == nExpectedWidthByte1 &&
337 pHeaderDimensions[3] == nExpectedWidthByte2;
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;
346void JpegDecoder::PatchUpKnownBadHeaderWithInvalidHeight(
347 size_t dimension_offset) {
348 DCHECK(m_SrcSpan.size() > dimension_offset + 1u);
349 auto pData = GetWritableSrcData().subspan(dimension_offset);
354void JpegDecoder::PatchUpTrailer() {
355 auto pData = GetWritableSrcData();
356 pData[m_SrcSpan.size() - 2] = 0xff;
357 pData[m_SrcSpan.size() - 1] = 0xd9;
360pdfium::span<uint8_t> JpegDecoder::GetWritableSrcData() {
363 const_cast<uint8_t*>(m_SrcSpan.data()), m_SrcSpan.size()));
370 pdfium::span<
const uint8_t> src_span,
374 bool ColorTransform) {
375 DCHECK(!src_span.empty());
377 auto pDecoder =
std::make_unique<JpegDecoder>();
378 if (!pDecoder->Create(src_span, width, height, nComps, ColorTransform))
386 pdfium::span<
const uint8_t> src_span) {
388 if (!JpegLoadInfo(src_span, &info))
#define UNSAFE_BUFFERS(...)
void jpeg_common_destroy_decompress(JpegCommon *jpeg_common)
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