Merge XFA to PDFium master at 4dc95e7 on 10/28/2014
[pdfium.git] / core / src / fxcodec / codec / fx_codec_png.cpp
1 // Copyright 2014 PDFium Authors. All rights reserved.\r
2 // Use of this source code is governed by a BSD-style license that can be\r
3 // found in the LICENSE file.\r
4 \r
5 // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com\r
6 \r
7 #include "../../../include/fxcodec/fx_codec.h"\r
8 #include "../../../include/fxge/fx_dib.h"\r
9 #include "codec_int.h"\r
10 extern "C" {\r
11 #undef FAR\r
12 #include "../fx_lpng/include/fx_png.h"\r
13 }\r
14 static void _png_error_data(png_structp png_ptr, png_const_charp error_msg)\r
15 {\r
16     if(png_get_error_ptr(png_ptr)) {\r
17         FXSYS_strncpy((char*)png_get_error_ptr(png_ptr), error_msg, PNG_ERROR_SIZE - 1);\r
18     }\r
19     longjmp(png_jmpbuf(png_ptr), 1);\r
20 }\r
21 static void _png_warning_data(png_structp png_ptr, png_const_charp error_msg)\r
22 {\r
23 }\r
24 static void _png_load_bmp_attribute(png_structp png_ptr, png_infop info_ptr, CFX_DIBAttribute* pAttribute)\r
25 {\r
26     if (pAttribute) {\r
27 #if defined(PNG_pHYs_SUPPORTED)\r
28         pAttribute->m_nXDPI = png_get_x_pixels_per_meter(png_ptr, info_ptr);\r
29         pAttribute->m_nYDPI = png_get_y_pixels_per_meter(png_ptr, info_ptr);\r
30         png_uint_32 res_x, res_y;\r
31         int unit_type;\r
32         png_get_pHYs(png_ptr, info_ptr, &res_x, &res_y, &unit_type);\r
33         switch (unit_type) {\r
34             case PNG_RESOLUTION_METER:\r
35                 pAttribute->m_wDPIUnit = FXCODEC_RESUNIT_METER;\r
36                 break;\r
37             default:\r
38                 pAttribute->m_wDPIUnit = FXCODEC_RESUNIT_NONE;\r
39         }\r
40 #endif\r
41 #if defined(PNG_iCCP_SUPPORTED)\r
42         png_charp icc_name;\r
43         png_bytep icc_profile;\r
44         png_uint_32 icc_proflen;\r
45         int compress_type;\r
46         png_get_iCCP(png_ptr, info_ptr, &icc_name, &compress_type, &icc_profile, &icc_proflen);\r
47 #endif\r
48         int bTime = 0;\r
49 #if defined(PNG_tIME_SUPPORTED)\r
50         png_timep t = NULL;\r
51         png_get_tIME(png_ptr, info_ptr, &t);\r
52         if (t) {\r
53             FXSYS_memset32(pAttribute->m_strTime, 0, 20);\r
54             FXSYS_sprintf((FX_LPSTR)pAttribute->m_strTime, "%4d:%2d:%2d %2d:%2d:%2d", t->year, t->month, t->day, t->hour, t->minute, t->second);\r
55             bTime = 1;\r
56         }\r
57 #endif\r
58 #if defined(PNG_TEXT_SUPPORTED)\r
59         int i;\r
60         FX_DWORD len;\r
61         FX_LPCSTR buf;\r
62         int num_text;\r
63         png_textp text = NULL;\r
64         png_get_text(png_ptr, info_ptr, &text, &num_text);\r
65         for (i = 0; i < num_text; i++) {\r
66             len = (FX_DWORD)FXSYS_strlen(text[i].key);\r
67             buf = "Time";\r
68             if (!FXSYS_memcmp32(buf, text[i].key, FX_MIN(len, FXSYS_strlen(buf)))) {\r
69                 if (!bTime) {\r
70                     FXSYS_memset32(pAttribute->m_strTime, 0, 20);\r
71                     FXSYS_memcpy32(pAttribute->m_strTime, text[i].text, text[i].text_length);\r
72                 }\r
73             } else {\r
74                 buf = "Author";\r
75                 if (!FXSYS_memcmp32(buf, text[i].key, FX_MIN(len, FXSYS_strlen(buf)))) {\r
76                     pAttribute->m_strAuthor.Empty();\r
77                     pAttribute->m_strAuthor.Load((FX_LPBYTE)text[i].text, (FX_STRSIZE)text[i].text_length);\r
78                 }\r
79             }\r
80         }\r
81 #endif\r
82     }\r
83 }\r
84 struct FXPNG_Context {\r
85     png_structp png_ptr;\r
86     png_infop   info_ptr;\r
87     void*               parent_ptr;\r
88     void*               child_ptr;\r
89 \r
90     void*               (*m_AllocFunc)(unsigned int);\r
91     void                (*m_FreeFunc)(void*);\r
92 };\r
93 extern "C" {\r
94     static void* _png_alloc_func(unsigned int size)\r
95     {\r
96         return FX_Alloc(char, size);\r
97     }\r
98     static void  _png_free_func(void* p)\r
99     {\r
100         if(p != NULL) {\r
101             FX_Free(p);\r
102         }\r
103     }\r
104 };\r
105 static void _png_get_header_func(png_structp png_ptr, png_infop info_ptr)\r
106 {\r
107     FXPNG_Context* p = (FXPNG_Context*)png_get_progressive_ptr(png_ptr);\r
108     if(p == NULL) {\r
109         return;\r
110     }\r
111     CCodec_PngModule* pModule = (CCodec_PngModule*)p->parent_ptr;\r
112     if(pModule == NULL) {\r
113         return;\r
114     }\r
115     png_uint_32 width = 0, height = 0;\r
116     int bpc = 0, color_type = 0 , color_type1 = 0, pass = 0;\r
117     double gamma = 1.0;\r
118     png_get_IHDR(png_ptr, info_ptr, &width, &height, &bpc, &color_type, NULL, NULL, NULL);\r
119     color_type1 = color_type;\r
120     if(bpc > 8) {\r
121         png_set_strip_16(png_ptr);\r
122     } else if(bpc < 8) {\r
123         png_set_expand_gray_1_2_4_to_8(png_ptr);\r
124     }\r
125     bpc = 8;\r
126     if(color_type == PNG_COLOR_TYPE_PALETTE) {\r
127         png_set_palette_to_rgb(png_ptr);\r
128     }\r
129     pass = png_set_interlace_handling(png_ptr);\r
130     if(!pModule->ReadHeaderCallback(p->child_ptr, width, height, bpc, pass, &color_type, &gamma)) {\r
131         png_error(p->png_ptr, "Read Header Callback Error");\r
132     }\r
133     int intent;\r
134     if (png_get_sRGB(png_ptr, info_ptr, &intent)) {\r
135         png_set_gamma(png_ptr, gamma, 0.45455);\r
136     } else {\r
137         double image_gamma;\r
138         if(png_get_gAMA(png_ptr, info_ptr, &image_gamma)) {\r
139             png_set_gamma(png_ptr, gamma, image_gamma);\r
140         } else {\r
141             png_set_gamma(png_ptr, gamma, 0.45455);\r
142         }\r
143     }\r
144     switch(color_type) {\r
145         case PNG_COLOR_TYPE_GRAY:\r
146         case PNG_COLOR_TYPE_GRAY_ALPHA: {\r
147                 if(color_type1 & PNG_COLOR_MASK_COLOR) {\r
148                     png_set_rgb_to_gray(png_ptr, 1, 0.299, 0.587);\r
149                 }\r
150             }\r
151             break;\r
152         case PNG_COLOR_TYPE_PALETTE:\r
153             if(color_type1 != PNG_COLOR_TYPE_PALETTE) {\r
154                 png_error(p->png_ptr, "Not Support Output Palette Now");\r
155             }\r
156         case PNG_COLOR_TYPE_RGB:\r
157         case PNG_COLOR_TYPE_RGB_ALPHA:\r
158             if(!(color_type1 & PNG_COLOR_MASK_COLOR)) {\r
159                 png_set_gray_to_rgb(png_ptr);\r
160             }\r
161             png_set_bgr(png_ptr);\r
162             break;\r
163     }\r
164     if(!(color_type & PNG_COLOR_MASK_ALPHA)) {\r
165         png_set_strip_alpha(png_ptr);\r
166     }\r
167     if(color_type & PNG_COLOR_MASK_ALPHA && !(color_type1 & PNG_COLOR_MASK_ALPHA)) {\r
168         png_set_filler(png_ptr, 0xff, PNG_FILLER_AFTER);\r
169     }\r
170     png_read_update_info(png_ptr, info_ptr);\r
171 }\r
172 static void _png_get_end_func(png_structp png_ptr, png_infop info_ptr) {}\r
173 static void _png_get_row_func(png_structp png_ptr, png_bytep new_row, png_uint_32 row_num, int pass)\r
174 {\r
175     FXPNG_Context* p = (FXPNG_Context*)png_get_progressive_ptr(png_ptr);\r
176     if(p == NULL) {\r
177         return;\r
178     }\r
179     CCodec_PngModule* pModule = (CCodec_PngModule*)p->parent_ptr;\r
180     FX_LPBYTE src_buf = NULL;\r
181     if(!pModule->AskScanlineBufCallback(p->child_ptr, row_num, src_buf)) {\r
182         png_error(png_ptr, "Ask Scanline buffer Callback Error");\r
183     }\r
184     if(src_buf != NULL) {\r
185         png_progressive_combine_row(png_ptr, src_buf, new_row);\r
186     }\r
187     pModule->FillScanlineBufCompletedCallback(p->child_ptr, pass, row_num);\r
188 }\r
189 void* CCodec_PngModule::Start(void* pModule)\r
190 {\r
191     FXPNG_Context* p = (FXPNG_Context*)FX_Alloc(FX_BYTE, sizeof(FXPNG_Context));\r
192     if(p == NULL) {\r
193         return NULL;\r
194     }\r
195     p->m_AllocFunc = _png_alloc_func;\r
196     p->m_FreeFunc = _png_free_func;\r
197     p->png_ptr = NULL;\r
198     p->info_ptr = NULL;\r
199     p->parent_ptr = (void*)this;\r
200     p->child_ptr = pModule;\r
201     p->png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);\r
202     if (p->png_ptr == NULL) {\r
203         FX_Free(p);\r
204         return NULL;\r
205     }\r
206     p->info_ptr = png_create_info_struct(p->png_ptr);\r
207     if (p->info_ptr == NULL) {\r
208         png_destroy_read_struct(&(p->png_ptr), (png_infopp)NULL, (png_infopp)NULL);\r
209         FX_Free(p);\r
210         return NULL;\r
211     }\r
212     if (setjmp(png_jmpbuf(p->png_ptr))) {\r
213         if(p != NULL) {\r
214             png_destroy_read_struct(&(p->png_ptr), &(p->info_ptr), (png_infopp)NULL);\r
215             FX_Free(p);\r
216         }\r
217         return NULL;\r
218     }\r
219     png_set_progressive_read_fn(p->png_ptr, p,\r
220                                 _png_get_header_func,\r
221                                 _png_get_row_func,\r
222                                 _png_get_end_func);\r
223     png_set_error_fn(p->png_ptr, m_szLastError, (png_error_ptr)_png_error_data, (png_error_ptr)_png_warning_data);\r
224     return p;\r
225 }\r
226 void CCodec_PngModule::Finish(void* pContext)\r
227 {\r
228     FXPNG_Context* p = (FXPNG_Context*)pContext;\r
229     if(p != NULL) {\r
230         png_destroy_read_struct(&(p->png_ptr), &(p->info_ptr), (png_infopp)NULL);\r
231         p->m_FreeFunc(p);\r
232     }\r
233 }\r
234 FX_BOOL CCodec_PngModule::Input(void* pContext, FX_LPCBYTE src_buf, FX_DWORD src_size, CFX_DIBAttribute* pAttribute)\r
235 {\r
236     FXPNG_Context* p = (FXPNG_Context*)pContext;\r
237     if(setjmp(png_jmpbuf(p->png_ptr))) {\r
238         if (pAttribute && 0 == FXSYS_strcmp(m_szLastError, "Read Header Callback Error")) {\r
239             _png_load_bmp_attribute(p->png_ptr, p->info_ptr, pAttribute);\r
240         }\r
241         return FALSE;\r
242     }\r
243     png_process_data(p->png_ptr, p->info_ptr, (FX_LPBYTE)src_buf, src_size);\r
244     return TRUE;\r
245 }\r