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  // -----------------------------------------------------------------------------