Blame imageio/webpdec.c

Packit 9c6abc
// Copyright 2014 Google Inc. All Rights Reserved.
Packit 9c6abc
//
Packit 9c6abc
// Use of this source code is governed by a BSD-style license
Packit 9c6abc
// that can be found in the COPYING file in the root of the source
Packit 9c6abc
// tree. An additional intellectual property rights grant can be found
Packit 9c6abc
// in the file PATENTS. All contributing project authors may
Packit 9c6abc
// be found in the AUTHORS file in the root of the source tree.
Packit 9c6abc
// -----------------------------------------------------------------------------
Packit 9c6abc
//
Packit 9c6abc
// WebP decode.
Packit 9c6abc
Packit 9c6abc
#ifdef HAVE_CONFIG_H
Packit 9c6abc
#include "webp/config.h"
Packit 9c6abc
#endif
Packit 9c6abc
Packit 9c6abc
#include "./webpdec.h"
Packit 9c6abc
Packit 9c6abc
#include <assert.h>
Packit 9c6abc
#include <stdio.h>
Packit 9c6abc
#include <stdlib.h>
Packit 9c6abc
Packit 9c6abc
#include "webp/decode.h"
Packit 9c6abc
#include "webp/demux.h"
Packit 9c6abc
#include "webp/encode.h"
Packit 9c6abc
#include "./imageio_util.h"
Packit 9c6abc
#include "./metadata.h"
Packit 9c6abc
Packit 9c6abc
//------------------------------------------------------------------------------
Packit 9c6abc
// WebP decoding
Packit 9c6abc
Packit 9c6abc
static const char* const kStatusMessages[VP8_STATUS_NOT_ENOUGH_DATA + 1] = {
Packit 9c6abc
  "OK", "OUT_OF_MEMORY", "INVALID_PARAM", "BITSTREAM_ERROR",
Packit 9c6abc
  "UNSUPPORTED_FEATURE", "SUSPENDED", "USER_ABORT", "NOT_ENOUGH_DATA"
Packit 9c6abc
};
Packit 9c6abc
Packit 9c6abc
static void PrintAnimationWarning(const WebPDecoderConfig* const config) {
Packit 9c6abc
  if (config->input.has_animation) {
Packit 9c6abc
    fprintf(stderr,
Packit 9c6abc
            "Error! Decoding of an animated WebP file is not supported.\n"
Packit 9c6abc
            "       Use webpmux to extract the individual frames or\n"
Packit 9c6abc
            "       vwebp to view this image.\n");
Packit 9c6abc
  }
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
void PrintWebPError(const char* const in_file, int status) {
Packit 9c6abc
  fprintf(stderr, "Decoding of %s failed.\n", in_file);
Packit 9c6abc
  fprintf(stderr, "Status: %d", status);
Packit 9c6abc
  if (status >= VP8_STATUS_OK && status <= VP8_STATUS_NOT_ENOUGH_DATA) {
Packit 9c6abc
    fprintf(stderr, "(%s)", kStatusMessages[status]);
Packit 9c6abc
  }
Packit 9c6abc
  fprintf(stderr, "\n");
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
int LoadWebP(const char* const in_file,
Packit 9c6abc
             const uint8_t** data, size_t* data_size,
Packit 9c6abc
             WebPBitstreamFeatures* bitstream) {
Packit 9c6abc
  VP8StatusCode status;
Packit 9c6abc
  WebPBitstreamFeatures local_features;
Packit 9c6abc
  if (!ImgIoUtilReadFile(in_file, data, data_size)) return 0;
Packit 9c6abc
Packit 9c6abc
  if (bitstream == NULL) {
Packit 9c6abc
    bitstream = &local_features;
Packit 9c6abc
  }
Packit 9c6abc
Packit 9c6abc
  status = WebPGetFeatures(*data, *data_size, bitstream);
Packit 9c6abc
  if (status != VP8_STATUS_OK) {
Packit 9c6abc
    free((void*)*data);
Packit 9c6abc
    *data = NULL;
Packit 9c6abc
    *data_size = 0;
Packit 9c6abc
    PrintWebPError(in_file, status);
Packit 9c6abc
    return 0;
Packit 9c6abc
  }
Packit 9c6abc
  return 1;
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
//------------------------------------------------------------------------------
Packit 9c6abc
Packit 9c6abc
VP8StatusCode DecodeWebP(const uint8_t* const data, size_t data_size,
Packit 9c6abc
                         WebPDecoderConfig* const config) {
Packit 9c6abc
  if (config == NULL) return VP8_STATUS_INVALID_PARAM;
Packit 9c6abc
  PrintAnimationWarning(config);
Packit 9c6abc
  return WebPDecode(data, data_size, config);
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
VP8StatusCode DecodeWebPIncremental(
Packit 9c6abc
    const uint8_t* const data, size_t data_size,
Packit 9c6abc
    WebPDecoderConfig* const config) {
Packit 9c6abc
  VP8StatusCode status = VP8_STATUS_OK;
Packit 9c6abc
  if (config == NULL) return VP8_STATUS_INVALID_PARAM;
Packit 9c6abc
Packit 9c6abc
  PrintAnimationWarning(config);
Packit 9c6abc
Packit 9c6abc
  // Decoding call.
Packit 9c6abc
  {
Packit 9c6abc
    WebPIDecoder* const idec = WebPIDecode(data, data_size, config);
Packit 9c6abc
    if (idec == NULL) {
Packit 9c6abc
      fprintf(stderr, "Failed during WebPINewDecoder().\n");
Packit 9c6abc
      return VP8_STATUS_OUT_OF_MEMORY;
Packit 9c6abc
    } else {
Packit 9c6abc
      status = WebPIUpdate(idec, data, data_size);
Packit 9c6abc
      WebPIDelete(idec);
Packit 9c6abc
    }
Packit 9c6abc
  }
Packit 9c6abc
  return status;
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
// -----------------------------------------------------------------------------
Packit 9c6abc
// Metadata
Packit 9c6abc
Packit 9c6abc
static int ExtractMetadata(const uint8_t* const data, size_t data_size,
Packit 9c6abc
                           Metadata* const metadata) {
Packit 9c6abc
  WebPData webp_data = { data, data_size };
Packit 9c6abc
  WebPDemuxer* const demux = WebPDemux(&webp_data);
Packit 9c6abc
  WebPChunkIterator chunk_iter;
Packit 9c6abc
  uint32_t flags;
Packit 9c6abc
Packit 9c6abc
  if (demux == NULL) return 0;
Packit 9c6abc
  assert(metadata != NULL);
Packit 9c6abc
Packit 9c6abc
  flags = WebPDemuxGetI(demux, WEBP_FF_FORMAT_FLAGS);
Packit 9c6abc
Packit 9c6abc
  if ((flags & ICCP_FLAG) && WebPDemuxGetChunk(demux, "ICCP", 1, &chunk_iter)) {
Packit 9c6abc
    MetadataCopy((const char*)chunk_iter.chunk.bytes, chunk_iter.chunk.size,
Packit 9c6abc
                 &metadata->iccp);
Packit 9c6abc
    WebPDemuxReleaseChunkIterator(&chunk_iter);
Packit 9c6abc
  }
Packit 9c6abc
  if ((flags & EXIF_FLAG) && WebPDemuxGetChunk(demux, "EXIF", 1, &chunk_iter)) {
Packit 9c6abc
    MetadataCopy((const char*)chunk_iter.chunk.bytes, chunk_iter.chunk.size,
Packit 9c6abc
                 &metadata->exif);
Packit 9c6abc
    WebPDemuxReleaseChunkIterator(&chunk_iter);
Packit 9c6abc
  }
Packit 9c6abc
  if ((flags & XMP_FLAG) && WebPDemuxGetChunk(demux, "XMP ", 1, &chunk_iter)) {
Packit 9c6abc
    MetadataCopy((const char*)chunk_iter.chunk.bytes, chunk_iter.chunk.size,
Packit 9c6abc
                 &metadata->xmp);
Packit 9c6abc
    WebPDemuxReleaseChunkIterator(&chunk_iter);
Packit 9c6abc
  }
Packit 9c6abc
  WebPDemuxDelete(demux);
Packit 9c6abc
  return 1;
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
// -----------------------------------------------------------------------------
Packit 9c6abc
Packit 9c6abc
int ReadWebP(const uint8_t* const data, size_t data_size,
Packit 9c6abc
             WebPPicture* const pic,
Packit 9c6abc
             int keep_alpha, Metadata* const metadata) {
Packit 9c6abc
  int ok = 0;
Packit 9c6abc
  VP8StatusCode status = VP8_STATUS_OK;
Packit 9c6abc
  WebPDecoderConfig config;
Packit 9c6abc
  WebPDecBuffer* const output_buffer = &config.output;
Packit 9c6abc
  WebPBitstreamFeatures* const bitstream = &config.input;
Packit 9c6abc
Packit 9c6abc
  if (data == NULL || data_size == 0 || pic == NULL) return 0;
Packit 9c6abc
Packit 9c6abc
  if (!WebPInitDecoderConfig(&config)) {
Packit 9c6abc
    fprintf(stderr, "Library version mismatch!\n");
Packit 9c6abc
    return 0;
Packit 9c6abc
  }
Packit 9c6abc
Packit 9c6abc
  status = WebPGetFeatures(data, data_size, bitstream);
Packit 9c6abc
  if (status != VP8_STATUS_OK) {
Packit 9c6abc
    PrintWebPError("input data", status);
Packit 9c6abc
    return 0;
Packit 9c6abc
  }
Packit 9c6abc
Packit 9c6abc
  do {
Packit 9c6abc
    const int has_alpha = keep_alpha && bitstream->has_alpha;
Packit 9c6abc
    uint64_t stride;
Packit 9c6abc
    pic->width = bitstream->width;
Packit 9c6abc
    pic->height = bitstream->height;
Packit 9c6abc
    if (pic->use_argb) {
Packit 9c6abc
      stride = (uint64_t)bitstream->width * 4;
Packit 9c6abc
    } else {
Packit 9c6abc
      stride = (uint64_t)bitstream->width * (has_alpha ? 5 : 3) / 2;
Packit 9c6abc
      pic->colorspace = has_alpha ? WEBP_YUV420A : WEBP_YUV420;
Packit 9c6abc
    }
Packit 9c6abc
Packit 9c6abc
    if (!ImgIoUtilCheckSizeArgumentsOverflow(stride, bitstream->height)) {
Packit 9c6abc
      status = VP8_STATUS_OUT_OF_MEMORY;
Packit 9c6abc
      break;
Packit 9c6abc
    }
Packit 9c6abc
Packit 9c6abc
    ok = WebPPictureAlloc(pic);
Packit 9c6abc
    if (!ok) {
Packit 9c6abc
      status = VP8_STATUS_OUT_OF_MEMORY;
Packit 9c6abc
      break;
Packit 9c6abc
    }
Packit 9c6abc
    if (pic->use_argb) {
Packit 9c6abc
#ifdef WORDS_BIGENDIAN
Packit 9c6abc
      output_buffer->colorspace = MODE_ARGB;
Packit 9c6abc
#else
Packit 9c6abc
      output_buffer->colorspace = MODE_BGRA;
Packit 9c6abc
#endif
Packit 9c6abc
      output_buffer->u.RGBA.rgba = (uint8_t*)pic->argb;
Packit 9c6abc
      output_buffer->u.RGBA.stride = pic->argb_stride * sizeof(uint32_t);
Packit 9c6abc
      output_buffer->u.RGBA.size = output_buffer->u.RGBA.stride * pic->height;
Packit 9c6abc
    } else {
Packit 9c6abc
      output_buffer->colorspace = has_alpha ? MODE_YUVA : MODE_YUV;
Packit 9c6abc
      output_buffer->u.YUVA.y = pic->y;
Packit 9c6abc
      output_buffer->u.YUVA.u = pic->u;
Packit 9c6abc
      output_buffer->u.YUVA.v = pic->v;
Packit 9c6abc
      output_buffer->u.YUVA.a = has_alpha ? pic->a : NULL;
Packit 9c6abc
      output_buffer->u.YUVA.y_stride = pic->y_stride;
Packit 9c6abc
      output_buffer->u.YUVA.u_stride = pic->uv_stride;
Packit 9c6abc
      output_buffer->u.YUVA.v_stride = pic->uv_stride;
Packit 9c6abc
      output_buffer->u.YUVA.a_stride = has_alpha ? pic->a_stride : 0;
Packit 9c6abc
      output_buffer->u.YUVA.y_size = pic->height * pic->y_stride;
Packit 9c6abc
      output_buffer->u.YUVA.u_size = (pic->height + 1) / 2 * pic->uv_stride;
Packit 9c6abc
      output_buffer->u.YUVA.v_size = (pic->height + 1) / 2 * pic->uv_stride;
Packit 9c6abc
      output_buffer->u.YUVA.a_size = pic->height * pic->a_stride;
Packit 9c6abc
    }
Packit 9c6abc
    output_buffer->is_external_memory = 1;
Packit 9c6abc
Packit 9c6abc
    status = DecodeWebP(data, data_size, &config);
Packit 9c6abc
    ok = (status == VP8_STATUS_OK);
Packit 9c6abc
    if (ok && !keep_alpha && pic->use_argb) {
Packit 9c6abc
      // Need to wipe out the alpha value, as requested.
Packit 9c6abc
      int x, y;
Packit 9c6abc
      uint32_t* argb = pic->argb;
Packit 9c6abc
      for (y = 0; y < pic->height; ++y) {
Packit 9c6abc
        for (x = 0; x < pic->width; ++x) argb[x] |= 0xff000000u;
Packit 9c6abc
        argb += pic->argb_stride;
Packit 9c6abc
      }
Packit 9c6abc
    }
Packit 9c6abc
  } while (0);   // <- so we can 'break' out of the loop
Packit 9c6abc
Packit 9c6abc
  if (status != VP8_STATUS_OK) {
Packit 9c6abc
    PrintWebPError("input data", status);
Packit 9c6abc
    ok = 0;
Packit 9c6abc
  }
Packit 9c6abc
Packit 9c6abc
  WebPFreeDecBuffer(output_buffer);
Packit 9c6abc
Packit 9c6abc
  if (ok && metadata != NULL) {
Packit 9c6abc
    ok = ExtractMetadata(data, data_size, metadata);
Packit 9c6abc
    if (!ok) {
Packit 9c6abc
      PrintWebPError("metadata", VP8_STATUS_BITSTREAM_ERROR);
Packit 9c6abc
    }
Packit 9c6abc
  }
Packit 9c6abc
  if (!ok) WebPPictureFree(pic);
Packit 9c6abc
  return ok;
Packit 9c6abc
}
Packit 9c6abc
Packit 9c6abc
// -----------------------------------------------------------------------------