github.com/cellofellow/gopkg@v0.0.0-20140722061823-eec0544a62ad/image/webp/libwebp/examples/pngdec.c (about) 1 // Copyright 2012 Google Inc. All Rights Reserved. 2 // 3 // Use of this source code is governed by a BSD-style license 4 // that can be found in the COPYING file in the root of the source 5 // tree. An additional intellectual property rights grant can be found 6 // in the file PATENTS. All contributing project authors may 7 // be found in the AUTHORS file in the root of the source tree. 8 // ----------------------------------------------------------------------------- 9 // 10 // PNG decode. 11 12 #include "./pngdec.h" 13 14 #ifdef HAVE_CONFIG_H 15 #include "config.h" 16 #endif 17 18 #include <stdio.h> 19 20 #ifdef WEBP_HAVE_PNG 21 #include <png.h> 22 #include <setjmp.h> // note: this must be included *after* png.h 23 #include <stdlib.h> 24 #include <string.h> 25 26 #include "webp/encode.h" 27 #include "./metadata.h" 28 29 static void PNGAPI error_function(png_structp png, png_const_charp error) { 30 if (error != NULL) fprintf(stderr, "libpng error: %s\n", error); 31 longjmp(png_jmpbuf(png), 1); 32 } 33 34 // Converts the NULL terminated 'hexstring' which contains 2-byte character 35 // representations of hex values to raw data. 36 // 'hexstring' may contain values consisting of [A-F][a-f][0-9] in pairs, 37 // e.g., 7af2..., separated by any number of newlines. 38 // 'expected_length' is the anticipated processed size. 39 // On success the raw buffer is returned with its length equivalent to 40 // 'expected_length'. NULL is returned if the processed length is less than 41 // 'expected_length' or any character aside from those above is encountered. 42 // The returned buffer must be freed by the caller. 43 static uint8_t* HexStringToBytes(const char* hexstring, 44 size_t expected_length) { 45 const char* src = hexstring; 46 size_t actual_length = 0; 47 uint8_t* const raw_data = (uint8_t*)malloc(expected_length); 48 uint8_t* dst; 49 50 if (raw_data == NULL) return NULL; 51 52 for (dst = raw_data; actual_length < expected_length && *src != '\0'; ++src) { 53 char* end; 54 char val[3]; 55 if (*src == '\n') continue; 56 val[0] = *src++; 57 val[1] = *src; 58 val[2] = '\0'; 59 *dst++ = (uint8_t)strtol(val, &end, 16); 60 if (end != val + 2) break; 61 ++actual_length; 62 } 63 64 if (actual_length != expected_length) { 65 free(raw_data); 66 return NULL; 67 } 68 return raw_data; 69 } 70 71 static int ProcessRawProfile(const char* profile, size_t profile_len, 72 MetadataPayload* const payload) { 73 const char* src = profile; 74 char* end; 75 int expected_length; 76 77 if (profile == NULL || profile_len == 0) return 0; 78 79 // ImageMagick formats 'raw profiles' as 80 // '\n<name>\n<length>(%8lu)\n<hex payload>\n'. 81 if (*src != '\n') { 82 fprintf(stderr, "Malformed raw profile, expected '\\n' got '\\x%.2X'\n", 83 *src); 84 return 0; 85 } 86 ++src; 87 // skip the profile name and extract the length. 88 while (*src != '\0' && *src++ != '\n') {} 89 expected_length = (int)strtol(src, &end, 10); 90 if (*end != '\n') { 91 fprintf(stderr, "Malformed raw profile, expected '\\n' got '\\x%.2X'\n", 92 *end); 93 return 0; 94 } 95 ++end; 96 97 // 'end' now points to the profile payload. 98 payload->bytes = HexStringToBytes(end, expected_length); 99 if (payload->bytes == NULL) return 0; 100 payload->size = expected_length; 101 return 1; 102 } 103 104 static const struct { 105 const char* name; 106 int (*process)(const char* profile, size_t profile_len, 107 MetadataPayload* const payload); 108 size_t storage_offset; 109 } kPNGMetadataMap[] = { 110 // http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/PNG.html#TextualData 111 // See also: ExifTool on CPAN. 112 { "Raw profile type exif", ProcessRawProfile, METADATA_OFFSET(exif) }, 113 { "Raw profile type xmp", ProcessRawProfile, METADATA_OFFSET(xmp) }, 114 // Exiftool puts exif data in APP1 chunk, too. 115 { "Raw profile type APP1", ProcessRawProfile, METADATA_OFFSET(exif) }, 116 // XMP Specification Part 3, Section 3 #PNG 117 { "XML:com.adobe.xmp", MetadataCopy, METADATA_OFFSET(xmp) }, 118 { NULL, NULL, 0 }, 119 }; 120 121 // Looks for metadata at both the beginning and end of the PNG file, giving 122 // preference to the head. 123 // Returns true on success. The caller must use MetadataFree() on 'metadata' in 124 // all cases. 125 static int ExtractMetadataFromPNG(png_structp png, 126 png_infop const head_info, 127 png_infop const end_info, 128 Metadata* const metadata) { 129 int p; 130 131 for (p = 0; p < 2; ++p) { 132 png_infop const info = (p == 0) ? head_info : end_info; 133 png_textp text = NULL; 134 const int num = png_get_text(png, info, &text, NULL); 135 int i; 136 // Look for EXIF / XMP metadata. 137 for (i = 0; i < num; ++i, ++text) { 138 int j; 139 for (j = 0; kPNGMetadataMap[j].name != NULL; ++j) { 140 if (!strcmp(text->key, kPNGMetadataMap[j].name)) { 141 MetadataPayload* const payload = 142 (MetadataPayload*)((uint8_t*)metadata + 143 kPNGMetadataMap[j].storage_offset); 144 png_size_t text_length; 145 switch (text->compression) { 146 #ifdef PNG_iTXt_SUPPORTED 147 case PNG_ITXT_COMPRESSION_NONE: 148 case PNG_ITXT_COMPRESSION_zTXt: 149 text_length = text->itxt_length; 150 break; 151 #endif 152 case PNG_TEXT_COMPRESSION_NONE: 153 case PNG_TEXT_COMPRESSION_zTXt: 154 default: 155 text_length = text->text_length; 156 break; 157 } 158 if (payload->bytes != NULL) { 159 fprintf(stderr, "Ignoring additional '%s'\n", text->key); 160 } else if (!kPNGMetadataMap[j].process(text->text, text_length, 161 payload)) { 162 fprintf(stderr, "Failed to process: '%s'\n", text->key); 163 return 0; 164 } 165 break; 166 } 167 } 168 } 169 // Look for an ICC profile. 170 { 171 png_charp name; 172 int comp_type; 173 #if ((PNG_LIBPNG_VER_MAJOR << 8) | PNG_LIBPNG_VER_MINOR << 0) < \ 174 ((1 << 8) | (5 << 0)) 175 png_charp profile; 176 #else // >= libpng 1.5.0 177 png_bytep profile; 178 #endif 179 png_uint_32 len; 180 181 if (png_get_iCCP(png, info, 182 &name, &comp_type, &profile, &len) == PNG_INFO_iCCP) { 183 if (!MetadataCopy((const char*)profile, len, &metadata->iccp)) return 0; 184 } 185 } 186 } 187 188 return 1; 189 } 190 191 int ReadPNG(FILE* in_file, WebPPicture* const pic, int keep_alpha, 192 Metadata* const metadata) { 193 png_structp png; 194 png_infop info = NULL; 195 png_infop end_info = NULL; 196 int color_type, bit_depth, interlaced; 197 int has_alpha; 198 int num_passes; 199 int p; 200 int ok = 0; 201 png_uint_32 width, height, y; 202 int stride; 203 uint8_t* rgb = NULL; 204 205 png = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0); 206 if (png == NULL) { 207 goto End; 208 } 209 210 png_set_error_fn(png, 0, error_function, NULL); 211 if (setjmp(png_jmpbuf(png))) { 212 Error: 213 MetadataFree(metadata); 214 png_destroy_read_struct(&png, &info, &end_info); 215 goto End; 216 } 217 218 info = png_create_info_struct(png); 219 if (info == NULL) goto Error; 220 end_info = png_create_info_struct(png); 221 if (end_info == NULL) goto Error; 222 223 png_init_io(png, in_file); 224 png_read_info(png, info); 225 if (!png_get_IHDR(png, info, 226 &width, &height, &bit_depth, &color_type, &interlaced, 227 NULL, NULL)) goto Error; 228 229 png_set_strip_16(png); 230 png_set_packing(png); 231 if (color_type == PNG_COLOR_TYPE_PALETTE) png_set_palette_to_rgb(png); 232 if (color_type == PNG_COLOR_TYPE_GRAY || 233 color_type == PNG_COLOR_TYPE_GRAY_ALPHA) { 234 if (bit_depth < 8) { 235 png_set_expand_gray_1_2_4_to_8(png); 236 } 237 png_set_gray_to_rgb(png); 238 } 239 if (png_get_valid(png, info, PNG_INFO_tRNS)) { 240 png_set_tRNS_to_alpha(png); 241 has_alpha = 1; 242 } else { 243 has_alpha = !!(color_type & PNG_COLOR_MASK_ALPHA); 244 } 245 246 if (!keep_alpha) { 247 png_set_strip_alpha(png); 248 has_alpha = 0; 249 } 250 251 num_passes = png_set_interlace_handling(png); 252 png_read_update_info(png, info); 253 stride = (has_alpha ? 4 : 3) * width * sizeof(*rgb); 254 rgb = (uint8_t*)malloc(stride * height); 255 if (rgb == NULL) goto Error; 256 for (p = 0; p < num_passes; ++p) { 257 for (y = 0; y < height; ++y) { 258 png_bytep row = rgb + y * stride; 259 png_read_rows(png, &row, NULL, 1); 260 } 261 } 262 png_read_end(png, end_info); 263 264 if (metadata != NULL && 265 !ExtractMetadataFromPNG(png, info, end_info, metadata)) { 266 fprintf(stderr, "Error extracting PNG metadata!\n"); 267 goto Error; 268 } 269 270 png_destroy_read_struct(&png, &info, &end_info); 271 272 pic->width = width; 273 pic->height = height; 274 pic->use_argb = 1; 275 ok = has_alpha ? WebPPictureImportRGBA(pic, rgb, stride) 276 : WebPPictureImportRGB(pic, rgb, stride); 277 278 if (!ok) { 279 goto Error; 280 } 281 282 End: 283 free(rgb); 284 return ok; 285 } 286 #else // !WEBP_HAVE_PNG 287 int ReadPNG(FILE* in_file, struct WebPPicture* const pic, int keep_alpha, 288 struct Metadata* const metadata) { 289 (void)in_file; 290 (void)pic; 291 (void)keep_alpha; 292 (void)metadata; 293 fprintf(stderr, "PNG support not compiled. Please install the libpng " 294 "development package before building.\n"); 295 return 0; 296 } 297 #endif // WEBP_HAVE_PNG 298 299 // -----------------------------------------------------------------------------