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 = pContext->m_pDelegate->PngAskScanlineBuf(row_num);
163 CHECK(src_buf);
164 png_progressive_combine_row(png_ptr, src_buf, new_row);
165
166 pContext->m_pDelegate->PngFillScanlineBufCompleted(pass, row_num);
167 }
168
169 } // extern "C"
170
CPngContext(PngDecoder::Delegate * pDelegate)171 CPngContext::CPngContext(PngDecoder::Delegate* pDelegate)
172 : m_pDelegate(pDelegate) {}
173
~CPngContext()174 CPngContext::~CPngContext() {
175 png_destroy_read_struct(m_pPng ? &m_pPng : nullptr,
176 m_pInfo ? &m_pInfo : nullptr, nullptr);
177 }
178
179 namespace fxcodec {
180
181 // static
StartDecode(Delegate * pDelegate)182 std::unique_ptr<ProgressiveDecoderIface::Context> PngDecoder::StartDecode(
183 Delegate* pDelegate) {
184 auto p = std::make_unique<CPngContext>(pDelegate);
185 p->m_pPng =
186 png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr);
187 if (!p->m_pPng)
188 return nullptr;
189
190 p->m_pInfo = png_create_info_struct(p->m_pPng);
191 if (!p->m_pInfo)
192 return nullptr;
193
194 if (setjmp(png_jmpbuf(p->m_pPng)))
195 return nullptr;
196
197 png_set_progressive_read_fn(p->m_pPng, p.get(), _png_get_header_func,
198 _png_get_row_func, _png_get_end_func);
199 png_set_error_fn(p->m_pPng, p->m_szLastError, _png_error_data,
200 _png_warning_data);
201 return p;
202 }
203
204 // static
ContinueDecode(ProgressiveDecoderIface::Context * pContext,RetainPtr<CFX_CodecMemory> codec_memory,CFX_DIBAttribute * pAttribute)205 bool PngDecoder::ContinueDecode(ProgressiveDecoderIface::Context* pContext,
206 RetainPtr<CFX_CodecMemory> codec_memory,
207 CFX_DIBAttribute* pAttribute) {
208 auto* ctx = static_cast<CPngContext*>(pContext);
209 if (setjmp(png_jmpbuf(ctx->m_pPng))) {
210 if (pAttribute &&
211 strcmp(ctx->m_szLastError, "Read Header Callback Error") == 0) {
212 _png_load_bmp_attribute(ctx->m_pPng, ctx->m_pInfo, pAttribute);
213 }
214 return false;
215 }
216 pdfium::span<uint8_t> src_buf = codec_memory->GetUnconsumedSpan();
217 png_process_data(ctx->m_pPng, ctx->m_pInfo, src_buf.data(), src_buf.size());
218 return true;
219 }
220
221 } // namespace fxcodec
222