xref: /aosp_15_r20/external/pdfium/core/fxcodec/jpeg/jpeg_progressive_decoder.cpp (revision 3ac0a46f773bac49fa9476ec2b1cf3f8da5ec3a4)
1 // Copyright 2020 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/jpeg_progressive_decoder.h"
8 
9 #include <utility>
10 
11 #include "core/fxcodec/cfx_codec_memory.h"
12 #include "core/fxcodec/fx_codec.h"
13 #include "core/fxcodec/jpeg/jpeg_common.h"
14 #include "core/fxcodec/scanlinedecoder.h"
15 #include "core/fxcrt/fx_safe_types.h"
16 #include "core/fxge/dib/cfx_dibbase.h"
17 #include "core/fxge/dib/fx_dib.h"
18 #include "third_party/abseil-cpp/absl/types/optional.h"
19 #include "third_party/base/check.h"
20 #include "third_party/base/memory/ptr_util.h"
21 
22 class CJpegContext final : public ProgressiveDecoderIface::Context {
23  public:
24   CJpegContext();
25   ~CJpegContext() override;
26 
GetJumpMark()27   jmp_buf& GetJumpMark() { return m_JumpMark; }
28 
29   jmp_buf m_JumpMark;
30   jpeg_decompress_struct m_Info = {};
31   jpeg_error_mgr m_ErrMgr = {};
32   jpeg_source_mgr m_SrcMgr = {};
33   unsigned int m_SkipSize = 0;
34 };
35 
36 extern "C" {
37 
error_fatal(j_common_ptr cinfo)38 static void error_fatal(j_common_ptr cinfo) {
39   auto* pContext = reinterpret_cast<CJpegContext*>(cinfo->client_data);
40   longjmp(pContext->m_JumpMark, -1);
41 }
42 
src_skip_data(jpeg_decompress_struct * cinfo,long num)43 static void src_skip_data(jpeg_decompress_struct* cinfo, long num) {
44   if (cinfo->src->bytes_in_buffer < static_cast<size_t>(num)) {
45     auto* pContext = reinterpret_cast<CJpegContext*>(cinfo->client_data);
46     pContext->m_SkipSize = (unsigned int)(num - cinfo->src->bytes_in_buffer);
47     cinfo->src->bytes_in_buffer = 0;
48   } else {
49     cinfo->src->next_input_byte += num;
50     cinfo->src->bytes_in_buffer -= num;
51   }
52 }
53 
54 }  // extern "C"
55 
JpegLoadAttribute(const jpeg_decompress_struct & info,CFX_DIBAttribute * pAttribute)56 static void JpegLoadAttribute(const jpeg_decompress_struct& info,
57                               CFX_DIBAttribute* pAttribute) {
58   pAttribute->m_nXDPI = info.X_density;
59   pAttribute->m_nYDPI = info.Y_density;
60   pAttribute->m_wDPIUnit =
61       static_cast<CFX_DIBAttribute::ResUnit>(info.density_unit);
62 }
63 
CJpegContext()64 CJpegContext::CJpegContext() {
65   m_Info.client_data = this;
66   m_Info.err = &m_ErrMgr;
67 
68   m_ErrMgr.error_exit = error_fatal;
69   m_ErrMgr.emit_message = error_do_nothing_int;
70   m_ErrMgr.output_message = error_do_nothing;
71   m_ErrMgr.format_message = error_do_nothing_char;
72   m_ErrMgr.reset_error_mgr = error_do_nothing;
73 
74   m_SrcMgr.init_source = src_do_nothing;
75   m_SrcMgr.term_source = src_do_nothing;
76   m_SrcMgr.skip_input_data = src_skip_data;
77   m_SrcMgr.fill_input_buffer = src_fill_buffer;
78   m_SrcMgr.resync_to_restart = src_resync;
79 }
80 
~CJpegContext()81 CJpegContext::~CJpegContext() {
82   jpeg_destroy_decompress(&m_Info);
83 }
84 
85 namespace fxcodec {
86 
87 // static
GetInstance()88 JpegProgressiveDecoder* JpegProgressiveDecoder::GetInstance() {
89   static pdfium::base::NoDestructor<JpegProgressiveDecoder> s;
90   return s.get();
91 }
92 
93 // static
94 std::unique_ptr<ProgressiveDecoderIface::Context>
Start()95 JpegProgressiveDecoder::Start() {
96   // Use ordinary pointer until past the possibility of a longjump.
97   auto* pContext = new CJpegContext();
98   if (setjmp(pContext->m_JumpMark) == -1) {
99     delete pContext;
100     return nullptr;
101   }
102 
103   jpeg_create_decompress(&pContext->m_Info);
104   pContext->m_Info.src = &pContext->m_SrcMgr;
105   pContext->m_SkipSize = 0;
106   return pdfium::WrapUnique(pContext);
107 }
108 
109 // static
GetJumpMark(Context * pContext)110 jmp_buf& JpegProgressiveDecoder::GetJumpMark(Context* pContext) {
111   return static_cast<CJpegContext*>(pContext)->GetJumpMark();
112 }
113 
114 // static
ReadHeader(Context * pContext,int * width,int * height,int * nComps,CFX_DIBAttribute * pAttribute)115 int JpegProgressiveDecoder::ReadHeader(Context* pContext,
116                                        int* width,
117                                        int* height,
118                                        int* nComps,
119                                        CFX_DIBAttribute* pAttribute) {
120   DCHECK(pAttribute);
121 
122   auto* ctx = static_cast<CJpegContext*>(pContext);
123   int ret = jpeg_read_header(&ctx->m_Info, TRUE);
124   if (ret == JPEG_SUSPENDED)
125     return 2;
126   if (ret != JPEG_HEADER_OK)
127     return 1;
128 
129   *width = ctx->m_Info.image_width;
130   *height = ctx->m_Info.image_height;
131   *nComps = ctx->m_Info.num_components;
132   JpegLoadAttribute(ctx->m_Info, pAttribute);
133   return 0;
134 }
135 
136 // static
StartScanline(Context * pContext,int down_scale)137 bool JpegProgressiveDecoder::StartScanline(Context* pContext, int down_scale) {
138   auto* ctx = static_cast<CJpegContext*>(pContext);
139   ctx->m_Info.scale_denom = static_cast<unsigned int>(down_scale);
140   return !!jpeg_start_decompress(&ctx->m_Info);
141 }
142 
143 // static
ReadScanline(Context * pContext,unsigned char * dest_buf)144 bool JpegProgressiveDecoder::ReadScanline(Context* pContext,
145                                           unsigned char* dest_buf) {
146   auto* ctx = static_cast<CJpegContext*>(pContext);
147   unsigned int nlines = jpeg_read_scanlines(&ctx->m_Info, &dest_buf, 1);
148   return nlines == 1;
149 }
150 
GetAvailInput(Context * pContext) const151 FX_FILESIZE JpegProgressiveDecoder::GetAvailInput(Context* pContext) const {
152   auto* ctx = static_cast<CJpegContext*>(pContext);
153   return static_cast<FX_FILESIZE>(ctx->m_SrcMgr.bytes_in_buffer);
154 }
155 
Input(Context * pContext,RetainPtr<CFX_CodecMemory> codec_memory)156 bool JpegProgressiveDecoder::Input(Context* pContext,
157                                    RetainPtr<CFX_CodecMemory> codec_memory) {
158   pdfium::span<uint8_t> src_buf = codec_memory->GetUnconsumedSpan();
159   auto* ctx = static_cast<CJpegContext*>(pContext);
160   if (ctx->m_SkipSize) {
161     if (ctx->m_SkipSize > src_buf.size()) {
162       ctx->m_SrcMgr.bytes_in_buffer = 0;
163       ctx->m_SkipSize -= src_buf.size();
164       return true;
165     }
166     src_buf = src_buf.subspan(ctx->m_SkipSize);
167     ctx->m_SkipSize = 0;
168   }
169   ctx->m_SrcMgr.next_input_byte = src_buf.data();
170   ctx->m_SrcMgr.bytes_in_buffer = src_buf.size();
171   return true;
172 }
173 
174 JpegProgressiveDecoder::JpegProgressiveDecoder() = default;
175 
176 JpegProgressiveDecoder::~JpegProgressiveDecoder() = default;
177 
178 }  // namespace fxcodec
179