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