Ejemplo n.º 1
0
static int ReadSubImage(GifFileType* gif, WebPPicture* pic, WebPPicture* view) {
  const GifImageDesc image_desc = gif->Image;
  const int offset_x = image_desc.Left;
  const int offset_y = image_desc.Top;
  const int sub_w = image_desc.Width;
  const int sub_h = image_desc.Height;
  uint32_t* dst = NULL;
  uint8_t* tmp = NULL;
  int ok = 0;

  // Use a view for the sub-picture:
  if (!WebPPictureView(pic, offset_x, offset_y, sub_w, sub_h, view)) {
    fprintf(stderr, "Sub-image %dx%d at position %d,%d is invalid!\n",
            sub_w, sub_h, offset_x, offset_y);
    goto End;
  }
  dst = view->argb;

  tmp = (uint8_t*)malloc(sub_w * sizeof(*tmp));
  if (tmp == NULL) goto End;

  if (image_desc.Interlace) {  // Interlaced image.
    // We need 4 passes, with the following offsets and jumps.
    const int interlace_offsets[] = { 0, 4, 2, 1 };
    const int interlace_jumps[]   = { 8, 8, 4, 2 };
    int pass;
    for (pass = 0; pass < 4; ++pass) {
      int y;
      for (y = interlace_offsets[pass]; y < sub_h; y += interlace_jumps[pass]) {
        if (DGifGetLine(gif, tmp, sub_w) == GIF_ERROR) goto End;
        Remap(tmp, gif, dst + y * view->argb_stride, sub_w);
      }
    }
  } else {  // Non-interlaced image.
    int y;
    for (y = 0; y < sub_h; ++y) {
      if (DGifGetLine(gif, tmp, sub_w) == GIF_ERROR) goto End;
      Remap(tmp, gif, dst + y * view->argb_stride, sub_w);
    }
  }
  // re-align the view with even offset (and adjust dimensions if needed).
  WebPPictureView(pic, offset_x & ~1, offset_y & ~1,
                  sub_w + (offset_x & 1), sub_h + (offset_y & 1), view);
  ok = 1;

 End:
  free(tmp);
  return ok;
}
Ejemplo n.º 2
0
// Given previous and current canvas, picks the optimal rectangle for the
// current frame. The initial guess for 'rect' will be the full canvas.
static int GetSubRect(const WebPPicture* const prev_canvas,
                      const WebPPicture* const curr_canvas, int is_key_frame,
                      int is_first_frame, int empty_rect_allowed,
                      FrameRect* const rect, WebPPicture* const sub_frame) {
  rect->x_offset_ = 0;
  rect->y_offset_ = 0;
  rect->width_ = curr_canvas->width;
  rect->height_ = curr_canvas->height;
  if (!is_key_frame || is_first_frame) {  // Optimize frame rectangle.
    // Note: This behaves as expected for first frame, as 'prev_canvas' is
    // initialized to a fully transparent canvas in the beginning.
    MinimizeChangeRectangle(prev_canvas, curr_canvas, rect);
  }

  if (IsEmptyRect(rect)) {
    if (empty_rect_allowed) {  // No need to get 'sub_frame'.
      return 1;
    } else {                   // Force a 1x1 rectangle.
      rect->width_ = 1;
      rect->height_ = 1;
      assert(rect->x_offset_ == 0);
      assert(rect->y_offset_ == 0);
    }
  }

  SnapToEvenOffsets(rect);
  return WebPPictureView(curr_canvas, rect->x_offset_, rect->y_offset_,
                         rect->width_, rect->height_, sub_frame);
}
Ejemplo n.º 3
0
int WebPPictureDistortion(const WebPPicture* src, const WebPPicture* ref,
                          int type, float results[5]) {
  int w, h, c;
  int ok = 0;
  WebPPicture p0, p1;
  double total_size = 0., total_distortion = 0.;
  if (src == NULL || ref == NULL ||
      src->width != ref->width || src->height != ref->height ||
      results == NULL) {
    return 0;
  }

  VP8SSIMDspInit();
  if (!WebPPictureInit(&p0) || !WebPPictureInit(&p1)) return 0;
  w = src->width;
  h = src->height;
  if (!WebPPictureView(src, 0, 0, w, h, &p0)) goto Error;
  if (!WebPPictureView(ref, 0, 0, w, h, &p1)) goto Error;

  // We always measure distortion in ARGB space.
  if (p0.use_argb == 0 && !WebPPictureYUVAToARGB(&p0)) goto Error;
  if (p1.use_argb == 0 && !WebPPictureYUVAToARGB(&p1)) goto Error;
  for (c = 0; c < 4; ++c) {
    float distortion;
    const size_t stride0 = 4 * (size_t)p0.argb_stride;
    const size_t stride1 = 4 * (size_t)p1.argb_stride;
    if (!WebPPlaneDistortion((const uint8_t*)p0.argb + c, stride0,
                             (const uint8_t*)p1.argb + c, stride1,
                             w, h, 4, type, &distortion, results + c)) {
      goto Error;
    }
    total_distortion += distortion;
    total_size += w * h;
  }

  results[4] = (type == 1) ? (float)GetLogSSIM(total_distortion, total_size)
                           : (float)GetPSNR(total_distortion, total_size);
  ok = 1;

 Error:
  WebPPictureFree(&p0);
  WebPPictureFree(&p1);
  return ok;
}
Ejemplo n.º 4
0
int GIFReadFrame(GifFileType* const gif, int transparent_index,
                 GIFFrameRect* const gif_rect, WebPPicture* const picture) {
  WebPPicture sub_image;
  const GifImageDesc* const image_desc = &gif->Image;
  uint32_t* dst = NULL;
  uint8_t* tmp = NULL;
  int ok = 0;
  GIFFrameRect rect = {
      image_desc->Left, image_desc->Top, image_desc->Width, image_desc->Height
  };
  *gif_rect = rect;

  // Use a view for the sub-picture:
  if (!WebPPictureView(picture, rect.x_offset, rect.y_offset,
                       rect.width, rect.height, &sub_image)) {
    fprintf(stderr, "Sub-image %dx%d at position %d,%d is invalid!\n",
            rect.width, rect.height, rect.x_offset, rect.y_offset);
    return 0;
  }
  dst = sub_image.argb;

  tmp = (uint8_t*)malloc(rect.width * sizeof(*tmp));
  if (tmp == NULL) goto End;

  if (image_desc->Interlace) {  // Interlaced image.
    // We need 4 passes, with the following offsets and jumps.
    const int interlace_offsets[] = { 0, 4, 2, 1 };
    const int interlace_jumps[]   = { 8, 8, 4, 2 };
    int pass;
    for (pass = 0; pass < 4; ++pass) {
      int y;
      for (y = interlace_offsets[pass]; y < rect.height;
           y += interlace_jumps[pass]) {
        if (DGifGetLine(gif, tmp, rect.width) == GIF_ERROR) goto End;
        Remap(gif, tmp, rect.width, transparent_index,
              dst + y * sub_image.argb_stride);
      }
    }
  } else {  // Non-interlaced image.
    int y;
    for (y = 0; y < rect.height; ++y) {
      if (DGifGetLine(gif, tmp, rect.width) == GIF_ERROR) goto End;
      Remap(gif, tmp, rect.width, transparent_index,
            dst + y * sub_image.argb_stride);
    }
  }
  ok = 1;

 End:
  if (!ok) picture->error_code = sub_image.error_code;
  WebPPictureFree(&sub_image);
  free(tmp);
  return ok;
}
Ejemplo n.º 5
0
// Read the GIF image frame.
static int ReadFrame(GifFileType* const gif, WebPFrameRect* const gif_rect,
                     WebPPicture* const sub_image, WebPPicture* const curr) {
  const GifImageDesc image_desc = gif->Image;
  uint32_t* dst = NULL;
  uint8_t* tmp = NULL;
  int ok = 0;
  WebPFrameRect rect = {
      image_desc.Left, image_desc.Top, image_desc.Width, image_desc.Height
  };
  *gif_rect = rect;

  // Use a view for the sub-picture:
  if (!WebPPictureView(curr, rect.x_offset, rect.y_offset,
                       rect.width, rect.height, sub_image)) {
    fprintf(stderr, "Sub-image %dx%d at position %d,%d is invalid!\n",
            rect.width, rect.height, rect.x_offset, rect.y_offset);
    goto End;
  }
  dst = sub_image->argb;

  tmp = (uint8_t*)malloc(rect.width * sizeof(*tmp));
  if (tmp == NULL) goto End;

  if (image_desc.Interlace) {  // Interlaced image.
    // We need 4 passes, with the following offsets and jumps.
    const int interlace_offsets[] = { 0, 4, 2, 1 };
    const int interlace_jumps[]   = { 8, 8, 4, 2 };
    int pass;
    for (pass = 0; pass < 4; ++pass) {
      int y;
      for (y = interlace_offsets[pass]; y < rect.height;
           y += interlace_jumps[pass]) {
        if (DGifGetLine(gif, tmp, rect.width) == GIF_ERROR) goto End;
        Remap(tmp, gif, dst + y * sub_image->argb_stride, rect.width);
      }
    }
  } else {  // Non-interlaced image.
    int y;
    for (y = 0; y < rect.height; ++y) {
      if (DGifGetLine(gif, tmp, rect.width) == GIF_ERROR) goto End;
      Remap(tmp, gif, dst + y * sub_image->argb_stride, rect.width);
    }
  }
  ok = 1;

 End:
  free(tmp);
  return ok;
}
Ejemplo n.º 6
0
// Given previous and current canvas, picks the optimal rectangle for the
// current frame. The initial guess for 'rect' will be the full canvas.
static int GetSubRect(const WebPPicture* const prev_canvas,
                      const WebPPicture* const curr_canvas, int is_key_frame,
                      int is_first_frame, FrameRect* const rect,
                      WebPPicture* const sub_frame) {
  rect->x_offset_ = 0;
  rect->y_offset_ = 0;
  rect->width_ = curr_canvas->width;
  rect->height_ = curr_canvas->height;
  if (!is_key_frame || is_first_frame) {  // Optimize frame rectangle.
    // Note: This behaves as expected for first frame, as 'prev_canvas' is
    // initialized to a fully transparent canvas in the beginning.
    MinimizeChangeRectangle(prev_canvas, curr_canvas, rect);
  }
  SnapToEvenOffsets(rect);

  return WebPPictureView(curr_canvas, rect->x_offset_, rect->y_offset_,
                         rect->width_, rect->height_, sub_frame);
}
Ejemplo n.º 7
0
int webp_encode(const char *in_file, const char *out_file, const FfiWebpEncodeConfig *encode_config) {
  int return_value = -1;
  FILE *out = NULL;
  int keep_alpha = 1;
  WebPPicture picture;
  WebPConfig config;
  // OPTIONS BEGIN
  if (encode_config->lossless == 0 || encode_config->lossless == 1){
    config.lossless = encode_config->lossless;
  }
  if (encode_config->quality >= 0 && encode_config->quality <= 100){
    config.quality = encode_config->quality;
  }
  if (encode_config->method >= 0 && encode_config->method <= 6){
    config.method = encode_config->method;
  }
  if (encode_config->target_size > 0){
    config.target_size = encode_config->target_size;
  }
  if (encode_config->target_PSNR > 0){
    config.target_PSNR = encode_config->target_PSNR;
  }
  if (encode_config->segments >= 0 && encode_config->segments <= 4){
    config.segments = encode_config->segments;
  }
  if (encode_config->sns_strength >= 0 && encode_config->sns_strength <= 100){
    config.sns_strength = encode_config->sns_strength;
  }
  if (encode_config->filter_strength >= 0 && encode_config->filter_strength <= 100){
    config.filter_strength = encode_config->filter_strength;
  }
  if (encode_config->filter_sharpness >= 0 && encode_config->filter_sharpness <= 7){
    config.filter_sharpness = encode_config->filter_sharpness;
  }
  if (encode_config->filter_type == 0 || encode_config->filter_type == 1){
    config.filter_type = encode_config->filter_type;
  }
  if (encode_config->autofilter == 0 || encode_config->autofilter == 1){
    config.autofilter = encode_config->autofilter;
  }
  if (encode_config->alpha_compression == 0 || encode_config->alpha_compression == 1){
    config.alpha_compression = encode_config->alpha_compression;
  }
  if (encode_config->alpha_filtering >= 0 && encode_config->alpha_filtering <= 2){
    config.alpha_filtering = encode_config->alpha_filtering;
  }
  if (encode_config->alpha_quality >= 0 && encode_config->alpha_quality <= 100){
    config.alpha_quality = encode_config->alpha_quality;
  }
  if (encode_config->pass >= 0 && encode_config->pass <= 10){
    config.pass = encode_config->pass;
  }
  if (encode_config->show_compressed >= 0){
    config.show_compressed = encode_config->show_compressed;
  }
  if (encode_config->preprocessing == 0 || encode_config->preprocessing == 1){
    config.preprocessing = encode_config->preprocessing;
  }
  if (encode_config->partitions >= 0 && encode_config->partitions <= 3){
    config.partitions = encode_config->partitions;
  }
  if (encode_config->partition_limit >= 0 && encode_config->partition_limit <= 100){
    config.partition_limit = encode_config->partition_limit;
  }
  if ((encode_config->width | encode_config->height) > 0){
    picture.width = encode_config->width;
    picture.height = encode_config->height;
  }
  // OPTIONS END
  
  if (!WebPPictureInit(&picture) ||
      !WebPConfigInit(&config)) {
    //fprintf(stderr, "Error! Version mismatch!\n");
    return 1;
  }
  
  if (!WebPValidateConfig(&config)) {
    //fprintf(stderr, "Error! Invalid configuration.\n");
    return_value = 2;
    goto Error;
  }
  
  if (!UtilReadPicture(in_file, &picture, keep_alpha)) {
    //fprintf(stderr, "Error! Cannot read input picture file '%s'\n", in_file);
    return_value = 3;
    goto Error;
  }
  
  out = fopen(out_file, "wb");
  if (out == NULL) {
    //fprintf(stderr, "Error! Cannot open output file '%s'\n", out_file);
    return_value = 4;
    goto Error;
  }
  picture.writer = EncodeWriter;
  picture.custom_ptr = (void*)out;
  
  if ((encode_config->crop_w | encode_config->crop_h) > 0){
    if (!WebPPictureView(&picture, encode_config->crop_x, encode_config->crop_y, encode_config->crop_w, encode_config->crop_h, &picture)) {
      //fprintf(stderr, "Error! Cannot crop picture\n");
      return_value = 5;
      goto Error;
    }
  }
  
  if ((encode_config->resize_w | encode_config->resize_h) > 0) {
    if (!WebPPictureRescale(&picture, encode_config->resize_w, encode_config->resize_h)) {
      //fprintf(stderr, "Error! Cannot resize picture\n");
      return_value = 6;
      goto Error;
    }
  }
  
  if (picture.extra_info_type > 0) {
    AllocExtraInfo(&picture);
  }
  
  if (!WebPEncode(&config, &picture)) {
    //fprintf(stderr, "Error! Cannot encode picture as WebP\n");
    return_value = 7;
    goto Error;
  }
  return_value = 0;
  
Error:
  free(picture.extra_info);
  WebPPictureFree(&picture);
  if (out != NULL) {
    fclose(out);
  }

  return return_value;
}
Ejemplo n.º 8
0
// Optimize the image frame for WebP and encode it.
static int OptimizeAndEncodeFrame(
    const WebPConfig* const config, const WebPFrameRect* const gif_rect,
    WebPPicture* const curr, WebPPicture* const prev_canvas,
    WebPPicture* const curr_canvas, WebPPicture* const sub_image,
    WebPMuxFrameInfo* const info, WebPFrameCache* const cache) {
  WebPFrameRect rect = *gif_rect;

  // Snap to even offsets (and adjust dimensions if needed).
  rect.width += (rect.x_offset & 1);
  rect.height += (rect.y_offset & 1);
  rect.x_offset &= ~1;
  rect.y_offset &= ~1;

  if (!WebPPictureView(curr, rect.x_offset, rect.y_offset,
                       rect.width, rect.height, sub_image)) {
    return 0;
  }
  info->x_offset = rect.x_offset;
  info->y_offset = rect.y_offset;

  if (is_first_frame || WebPUtilIsKeyFrame(curr, &rect, prev_canvas)) {
    // Add this as a key frame.
    if (!WebPFrameCacheAddFrame(cache, config, NULL, NULL, info, sub_image)) {
      return 0;
    }
    // Update prev_canvas by simply copying from 'curr'.
    WebPUtilCopyPixels(curr, prev_canvas);
  } else {
    if (!config->lossless) {
      // For lossy compression, it's better to replace transparent pixels of
      // 'curr' with actual RGB values, whenever possible.
      WebPUtilReduceTransparency(prev_canvas, &rect, curr);
      WebPUtilFlattenSimilarBlocks(prev_canvas, &rect, curr);
    }
    if (!WebPFrameCacheShouldTryKeyFrame(cache)) {
      // Add this as a frame rectangle.
      if (!WebPFrameCacheAddFrame(cache, config, info, sub_image, NULL, NULL)) {
        return 0;
      }
      // Update prev_canvas by blending 'curr' into it.
      WebPUtilBlendPixels(curr, gif_rect, prev_canvas);
    } else {
      WebPPicture full_image;
      WebPMuxFrameInfo full_image_info;
      int ok;

      // Convert to a key frame.
      WebPUtilCopyPixels(curr, curr_canvas);
      WebPUtilConvertToKeyFrame(prev_canvas, &rect, curr_canvas);
      if (!WebPPictureView(curr_canvas, rect.x_offset, rect.y_offset,
                           rect.width, rect.height, &full_image)) {
        return 0;
      }
      full_image_info = *info;
      full_image_info.x_offset = rect.x_offset;
      full_image_info.y_offset = rect.y_offset;

      // Add both variants to cache: frame rectangle and key frame.
      ok = WebPFrameCacheAddFrame(cache, config, info, sub_image,
                                  &full_image_info, &full_image);
      WebPPictureFree(&full_image);
      if (!ok) return 0;

      // Update prev_canvas by simply copying from 'curr_canvas'.
      WebPUtilCopyPixels(curr_canvas, prev_canvas);
    }
  }
  return 1;
}