<commit_before>// Copyright 2012 Google Inc. All Rights Reserved.
//
// Use of this source code is governed by a BSD-style license
// that can be found in the COPYING file in the root of the source
// tree. An additional intellectual property rights grant can be found
// in the file PATENTS. All contributing project authors may
// be found in the AUTHORS file in the root of the source tree.
// -----------------------------------------------------------------------------
//
//  simple tool to convert animated GIFs to WebP
//
// Authors: Skal (<EMAIL>)
//          Urvang (<EMAIL>)

#include <assert.h>
#include <stdio.h>
#include <string.h>

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#ifdef WEBP_HAVE_GIF

#include <gif_lib.h>
#include "webp/encode.h"
#include "webp/mux.h"
#include "./example_util.h"
#include "./gif2webp_util.h"

#define GIF_TRANSPARENT_MASK 0x01
#define GIF_DISPOSE_MASK     0x07
#define GIF_DISPOSE_SHIFT    2
#define WHITE_COLOR          0xffffffff
#define MAX_CACHE_SIZE       30

//------------------------------------------------------------------------------

static int transparent_index = -1;  // Index of transparent color in the map.

static void SanitizeKeyFrameIntervals(size_t* const kmin_ptr,
                                      size_t* const kmax_ptr) {
  size_t kmin = *kmin_ptr;
  size_t kmax = *kmax_ptr;
  int print_warning = 1;

  if (kmin == 0) {  // Disable keyframe insertion.
    kmax = ~0;
    kmin = kmax - 1;
    print_warning = 0;
  }
  if (kmax == 0) {
    kmax = ~0;
    print_warning = 0;
  }

  if (kmin >= kmax) {
    kmin = kmax - 1;
    if (print_warning) {
      fprintf(stderr,
              "WARNING: Setting kmin = %zu, so that kmin < kmax.\n", kmin);
    }
  } else if (kmin < (kmax / 2 + 1)) {
    // This ensures that cache.keyframe + kmin >= kmax is always true. So, we
    // can flush all the frames in the ‘count_since_key_frame == kmax’ case.
    kmin = (kmax / 2 + 1);
    if (print_warning) {
      fprintf(stderr,
              "WARNING: Setting kmin = %zu, so that kmin >= kmax / 2 + 1.\n",
              kmin);
    }
  }
  // Limit the max number of frames that are allocated.
  if (kmax - kmin > MAX_CACHE_SIZE) {
    kmin = kmax - MAX_CACHE_SIZE;
    if (print_warning) {
      fprintf(stderr,
              "WARNING: Setting kmin = %zu, so that kmax - kmin <= 30.\n",
              kmin);
    }
  }
  *kmin_ptr = kmin;
  *kmax_ptr = kmax;
}

static void Remap(const uint8_t* const src, const GifFileType* const gif,
                  uint32_t* dst, int len) {
  int i;
  const GifColorType* colors;
  const ColorMapObject* const cmap =
      gif->Image.ColorMap ? gif->Image.ColorMap : gif->SColorMap;
  if (cmap == NULL) return;
  colors = cmap->Colors;

  for (i = 0; i < len; ++i) {
    const GifColorType c = colors[src[i]];
    dst[i] = (src[i] == transparent_index) ? WEBP_UTIL_TRANSPARENT_COLOR
           : c.Blue | (c.Green << 8) | (c.Red << 16) | (0xff << 24);
  }
}

// Read the GIF image frame.
static int ReadFrame(GifFileType* const gif, WebPFrameRect* const gif_rect,
                     WebPPicture* const webp_frame) {
  WebPPicture sub_image;
  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(webp_frame, 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(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:
  if (!ok) webp_frame->error_code = sub_image.error_code;
  WebPPictureFree(&sub_image);
  free(tmp);
  return ok;
}

static int GetBackgroundColor(const ColorMapObject* const color_map,
                              GifWord bgcolor_idx, uint32_t* const bgcolor) {
  if (transparent_index != -1 && bgcolor_idx == transparent_index) {
    *bgcolor = WEBP_UTIL_TRANSPARENT_COLOR;  // Special case.
    return 1;
  } else if (color_map == NULL || color_map->Colors == NULL
             || bgcolor_idx >= color_map->ColorCount) {
    return 0;  // Invalid color map or index.
  } else {
    const GifColorType color = color_map->Colors[bgcolor_idx];
    *bgcolor = (0xff        << 24)
             | (color.Red   << 16)
             | (color.Green <<  8)
             | (color.Blue  <<  0);
    return 1;
  }
}

static void DisplayGifError(const GifFileType* const gif, int gif_error) {
  // GIFLIB_MAJOR is only defined in libgif >= 4.2.0.
  // libgif 4.2.0 has retired PrintGifError() and added GifErrorString().
#if defined(GIFLIB_MAJOR) && defined(GIFLIB_MINOR) && \
        ((GIFLIB_MAJOR == 4 && GIFLIB_MINOR >= 2) || GIFLIB_MAJOR > 4)
#if GIFLIB_MAJOR >= 5<commit_msg>gif2webp: Backward compatibility for giflib version <= 4.1.3<commit_after>// Copyright 2012 Google Inc. All Rights Reserved.
//
// Use of this source code is governed by a BSD-style license
// that can be found in the COPYING file in the root of the source
// tree. An additional intellectual property rights grant can be found
// in the file PATENTS. All contributing project authors may
// be found in the AUTHORS file in the root of the source tree.
// -----------------------------------------------------------------------------
//
//  simple tool to convert animated GIFs to WebP
//
// Authors: Skal (<EMAIL>)
//          Urvang (urvang<EMAIL>)

#include <assert.h>
#include <stdio.h>
#include <string.h>

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#ifdef WEBP_HAVE_GIF

#include <gif_lib.h>
#include "webp/encode.h"
#include "webp/mux.h"
#include "./example_util.h"
#include "./gif2webp_util.h"

#define GIF_TRANSPARENT_MASK 0x01
#define GIF_DISPOSE_MASK     0x07
#define GIF_DISPOSE_SHIFT    2
#define WHITE_COLOR          0xffffffff
#define MAX_CACHE_SIZE       30

//------------------------------------------------------------------------------

static int transparent_index = -1;  // Index of transparent color in the map.

static void SanitizeKeyFrameIntervals(size_t* const kmin_ptr,
                                      size_t* const kmax_ptr) {
  size_t kmin = *kmin_ptr;
  size_t kmax = *kmax_ptr;
  int print_warning = 1;

  if (kmin == 0) {  // Disable keyframe insertion.
    kmax = ~0;
    kmin = kmax - 1;
    print_warning = 0;
  }
  if (kmax == 0) {
    kmax = ~0;
    print_warning = 0;
  }

  if (kmin >= kmax) {
    kmin = kmax - 1;
    if (print_warning) {
      fprintf(stderr,
              "WARNING: Setting kmin = %zu, so that kmin < kmax.\n", kmin);
    }
  } else if (kmin < (kmax / 2 + 1)) {
    // This ensures that cache.keyframe + kmin >= kmax is always true. So, we
    // can flush all the frames in the ‘count_since_key_frame == kmax’ case.
    kmin = (kmax / 2 + 1);
    if (print_warning) {
      fprintf(stderr,
              "WARNING: Setting kmin = %zu, so that kmin >= kmax / 2 + 1.\n",
              kmin);
    }
  }
  // Limit the max number of frames that are allocated.
  if (kmax - kmin > MAX_CACHE_SIZE) {
    kmin = kmax - MAX_CACHE_SIZE;
    if (print_warning) {
      fprintf(stderr,
              "WARNING: Setting kmin = %zu, so that kmax - kmin <= 30.\n",
              kmin);
    }
  }
  *kmin_ptr = kmin;
  *kmax_ptr = kmax;
}

static void Remap(const uint8_t* const src, const GifFileType* const gif,
                  uint32_t* dst, int len) {
  int i;
  const GifColorType* colors;
  const ColorMapObject* const cmap =
      gif->Image.ColorMap ? gif->Image.ColorMap : gif->SColorMap;
  if (cmap == NULL) return;
  colors = cmap->Colors;

  for (i = 0; i < len; ++i) {
    const GifColorType c = colors[src[i]];
    dst[i] = (src[i] == transparent_index) ? WEBP_UTIL_TRANSPARENT_COLOR
           : c.Blue | (c.Green << 8) | (c.Red << 16) | (0xff << 24);
  }
}

// Read the GIF image frame.
static int ReadFrame(GifFileType* const gif, WebPFrameRect* const gif_rect,
                     WebPPicture* const webp_frame) {
  WebPPicture sub_image;
  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(webp_frame, 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(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:
  if (!ok) webp_frame->error_code = sub_image.error_code;
  WebPPictureFree(&sub_image);
  free(tmp);
  return ok;
}

static int GetBackgroundColor(const ColorMapObject* const color_map,
                              int bgcolor_idx, uint32_t* const bgcolor) {
  if (transparent_index != -1 && bgcolor_idx == transparent_index) {
    *bgcolor = WEBP_UTIL_TRANSPARENT_COLOR;  // Special case.
    return 1;
  } else if (color_map == NULL || color_map->Colors == NULL
             || bgcolor_idx >= color_map->ColorCount) {
    return 0;  // Invalid color map or index.
  } else {
    const GifColorType color = color_map->Colors[bgcolor_idx];
    *bgcolor = (0xff        << 24)
             | (color.Red   << 16)
             | (color.Green <<  8)
             | (color.Blue  <<  0);
    return 1;
  }
}

static void DisplayGifError(const GifFileType* const gif, int gif_error) {
  // GIFLIB_MAJOR is only defined in libgif >= 4.2.0.
  // libgif 4.2.0 has retired PrintGifError() and added GifErrorString().
#if defined(GIFLIB_MAJOR) && defined(GIFLIB_MINOR) && \
        ((GIFLIB_MAJOR == 4 && GIFLIB_MINOR >= 2) || GIFLIB_MAJOR > 4)
#if GIFLIB_MAJOR >= 5
{'fraction_non_alphanumeric': 0.14928848641655887, 'fraction_numerical': 0.01500646830530401, 'mean_word_length': 2.29619101762365, 'pattern_counts': {'":': 0, '<': 58, '<?xml version=': 0, '>': 51, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 2, 'word_list_counts': {'cursed_substrings.json': 3, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1635131', 'n_tokens_mistral': 4219, 'n_tokens_neox': 4036, 'n_words': 1145}