xref: /aosp_15_r20/external/pdfium/core/fxcodec/png/png_decoder.cpp (revision 3ac0a46f773bac49fa9476ec2b1cf3f8da5ec3a4)
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/png/png_decoder.h"
8 
9 #include <setjmp.h>
10 #include <string.h>
11 
12 #include "core/fxcodec/cfx_codec_memory.h"
13 #include "core/fxcodec/fx_codec.h"
14 #include "core/fxcodec/fx_codec_def.h"
15 #include "core/fxcrt/unowned_ptr.h"
16 
17 #ifdef USE_SYSTEM_LIBPNG
18 #include <png.h>
19 #else
20 #include "third_party/libpng/png.h"
21 #endif
22 
23 #define PNG_ERROR_SIZE 256
24 
25 class CPngContext final : public ProgressiveDecoderIface::Context {
26  public:
27   explicit CPngContext(PngDecoder::Delegate* pDelegate);
28   ~CPngContext() override;
29 
30   png_structp m_pPng = nullptr;
31   png_infop m_pInfo = nullptr;
32   UnownedPtr<PngDecoder::Delegate> const m_pDelegate;
33   char m_szLastError[PNG_ERROR_SIZE] = {};
34 };
35 
36 extern "C" {
37 
_png_error_data(png_structp png_ptr,png_const_charp error_msg)38 void _png_error_data(png_structp png_ptr, png_const_charp error_msg) {
39   if (png_get_error_ptr(png_ptr)) {
40     strncpy(static_cast<char*>(png_get_error_ptr(png_ptr)), error_msg,
41             PNG_ERROR_SIZE - 1);
42   }
43 
44   longjmp(png_jmpbuf(png_ptr), 1);
45 }
46 
_png_warning_data(png_structp png_ptr,png_const_charp error_msg)47 void _png_warning_data(png_structp png_ptr, png_const_charp error_msg) {}
48 
_png_load_bmp_attribute(png_structp png_ptr,png_infop info_ptr,CFX_DIBAttribute * pAttribute)49 void _png_load_bmp_attribute(png_structp png_ptr,
50                              png_infop info_ptr,
51                              CFX_DIBAttribute* pAttribute) {
52   if (pAttribute) {
53 #if defined(PNG_pHYs_SUPPORTED)
54     pAttribute->m_nXDPI = png_get_x_pixels_per_meter(png_ptr, info_ptr);
55     pAttribute->m_nYDPI = png_get_y_pixels_per_meter(png_ptr, info_ptr);
56     png_uint_32 res_x, res_y;
57     int unit_type;
58     png_get_pHYs(png_ptr, info_ptr, &res_x, &res_y, &unit_type);
59     switch (unit_type) {
60       case PNG_RESOLUTION_METER:
61         pAttribute->m_wDPIUnit = CFX_DIBAttribute::kResUnitMeter;
62         break;
63       default:
64         pAttribute->m_wDPIUnit = CFX_DIBAttribute::kResUnitNone;
65     }
66 #endif
67 #if defined(PNG_iCCP_SUPPORTED)
68     png_charp icc_name;
69     png_bytep icc_profile;
70     png_uint_32 icc_proflen;
71     int compress_type;
72     png_get_iCCP(png_ptr, info_ptr, &icc_name, &compress_type, &icc_profile,
73                  &icc_proflen);
74 #endif
75 #if defined(PNG_TEXT_SUPPORTED)
76     int num_text;
77     png_textp text = nullptr;
78     png_get_text(png_ptr, info_ptr, &text, &num_text);
79 #endif
80   }
81 }
82 
_png_get_header_func(png_structp png_ptr,png_infop info_ptr)83 void _png_get_header_func(png_structp png_ptr, png_infop info_ptr) {
84   auto* pContext =
85       reinterpret_cast<CPngContext*>(png_get_progressive_ptr(png_ptr));
86   if (!pContext)
87     return;
88 
89   png_uint_32 width = 0;
90   png_uint_32 height = 0;
91   int bpc = 0;
92   int color_type = 0;
93   png_get_IHDR(png_ptr, info_ptr, &width, &height, &bpc, &color_type, nullptr,
94                nullptr, nullptr);
95   int color_type1 = color_type;
96   if (bpc > 8)
97     png_set_strip_16(png_ptr);
98   else if (bpc < 8)
99     png_set_expand_gray_1_2_4_to_8(png_ptr);
100 
101   bpc = 8;
102   if (color_type == PNG_COLOR_TYPE_PALETTE)
103     png_set_palette_to_rgb(png_ptr);
104 
105   int pass = png_set_interlace_handling(png_ptr);
106   double gamma = 1.0;
107   if (!pContext->m_pDelegate->PngReadHeader(width, height, bpc, pass,
108                                             &color_type, &gamma)) {
109     png_error(pContext->m_pPng, "Read Header Callback Error");
110   }
111   int intent;
112   if (png_get_sRGB(png_ptr, info_ptr, &intent)) {
113     png_set_gamma(png_ptr, gamma, 0.45455);
114   } else {
115     double image_gamma;
116     if (png_get_gAMA(png_ptr, info_ptr, &image_gamma))
117       png_set_gamma(png_ptr, gamma, image_gamma);
118     else
119       png_set_gamma(png_ptr, gamma, 0.45455);
120   }
121   switch (color_type) {
122     case PNG_COLOR_TYPE_GRAY:
123     case PNG_COLOR_TYPE_GRAY_ALPHA: {
124       if (color_type1 & PNG_COLOR_MASK_COLOR) {
125         png_set_rgb_to_gray(png_ptr, 1, 0.299, 0.587);
126       }
127     } break;
128     case PNG_COLOR_TYPE_PALETTE:
129       if (color_type1 != PNG_COLOR_TYPE_PALETTE) {
130         png_error(pContext->m_pPng, "Not Support Output Palette Now");
131       }
132       [[fallthrough]];
133     case PNG_COLOR_TYPE_RGB:
134     case PNG_COLOR_TYPE_RGB_ALPHA:
135       if (!(color_type1 & PNG_COLOR_MASK_COLOR)) {
136         png_set_gray_to_rgb(png_ptr);
137       }
138       png_set_bgr(png_ptr);
139       break;
140   }
141   if (!(color_type & PNG_COLOR_MASK_ALPHA))
142     png_set_strip_alpha(png_ptr);
143 
144   if (color_type & PNG_COLOR_MASK_ALPHA &&
145       !(color_type1 & PNG_COLOR_MASK_ALPHA)) {
146     png_set_filler(png_ptr, 0xff, PNG_FILLER_AFTER);
147   }
148   png_read_update_info(png_ptr, info_ptr);
149 }
150 
_png_get_end_func(png_structp png_ptr,png_infop info_ptr)151 void _png_get_end_func(png_structp png_ptr, png_infop info_ptr) {}
152 
_png_get_row_func(png_structp png_ptr,png_bytep new_row,png_uint_32 row_num,int pass)153 void _png_get_row_func(png_structp png_ptr,
154                        png_bytep new_row,
155                        png_uint_32 row_num,
156                        int pass) {
157   auto* pContext =
158       reinterpret_cast<CPngContext*>(png_get_progressive_ptr(png_ptr));
159   if (!pContext)
160     return;
161 
162   uint8_t* src_buf;
163   if (!pContext->m_pDelegate->PngAskScanlineBuf(row_num, &src_buf))
164     png_error(png_ptr, "Ask Scanline buffer Callback Error");
165 
166   if (src_buf)
167     png_progressive_combine_row(png_ptr, src_buf, new_row);
168 
169   pContext->m_pDelegate->PngFillScanlineBufCompleted(pass, row_num);
170 }
171 
172 }  // extern "C"
173 
CPngContext(PngDecoder::Delegate * pDelegate)174 CPngContext::CPngContext(PngDecoder::Delegate* pDelegate)
175     : m_pDelegate(pDelegate) {}
176 
~CPngContext()177 CPngContext::~CPngContext() {
178   png_destroy_read_struct(m_pPng ? &m_pPng : nullptr,
179                           m_pInfo ? &m_pInfo : nullptr, nullptr);
180 }
181 
182 namespace fxcodec {
183 
184 // static
StartDecode(Delegate * pDelegate)185 std::unique_ptr<ProgressiveDecoderIface::Context> PngDecoder::StartDecode(
186     Delegate* pDelegate) {
187   auto p = std::make_unique<CPngContext>(pDelegate);
188   p->m_pPng =
189       png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr);
190   if (!p->m_pPng)
191     return nullptr;
192 
193   p->m_pInfo = png_create_info_struct(p->m_pPng);
194   if (!p->m_pInfo)
195     return nullptr;
196 
197   if (setjmp(png_jmpbuf(p->m_pPng)))
198     return nullptr;
199 
200   png_set_progressive_read_fn(p->m_pPng, p.get(), _png_get_header_func,
201                               _png_get_row_func, _png_get_end_func);
202   png_set_error_fn(p->m_pPng, p->m_szLastError, _png_error_data,
203                    _png_warning_data);
204   return p;
205 }
206 
207 // static
ContinueDecode(ProgressiveDecoderIface::Context * pContext,RetainPtr<CFX_CodecMemory> codec_memory,CFX_DIBAttribute * pAttribute)208 bool PngDecoder::ContinueDecode(ProgressiveDecoderIface::Context* pContext,
209                                 RetainPtr<CFX_CodecMemory> codec_memory,
210                                 CFX_DIBAttribute* pAttribute) {
211   auto* ctx = static_cast<CPngContext*>(pContext);
212   if (setjmp(png_jmpbuf(ctx->m_pPng))) {
213     if (pAttribute &&
214         strcmp(ctx->m_szLastError, "Read Header Callback Error") == 0) {
215       _png_load_bmp_attribute(ctx->m_pPng, ctx->m_pInfo, pAttribute);
216     }
217     return false;
218   }
219   pdfium::span<uint8_t> src_buf = codec_memory->GetUnconsumedSpan();
220   png_process_data(ctx->m_pPng, ctx->m_pInfo, src_buf.data(), src_buf.size());
221   return true;
222 }
223 
224 }  // namespace fxcodec
225