<commit_before>/*
 * Copyright 2009 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

// Author: <NAME>

#include "pagespeed/kernel/image/webp_optimizer.h"

#include "base/logging.h"
#include "pagespeed/kernel/base/message_handler.h"
#include "pagespeed/kernel/image/scanline_utils.h"

extern "C" {
#ifdef USE_SYSTEM_LIBWEBP
#include "webp/decode.h"
#else
#include "third_party/libwebp/src/webp/decode.h"
#endif
}

namespace pagespeed {

namespace image_compression {

using image_compression::GetNumChannelsFromPixelFormat;
using net_instaweb::MessageHandler;

// Copied from libwebp/v0_2/examples/cwebp.c
static const char* const kWebPErrorMessages[] = {
  "OK",
  "OUT_OF_MEMORY: Out of memory allocating objects",
  "BITSTREAM_OUT_OF_MEMORY: Out of memory re-allocating byte buffer",
  "NULL_PARAMETER: NULL parameter passed to function",
  "INVALID_CONFIGURATION: configuration is invalid",
  "BAD_DIMENSION: Bad picture dimension. Maximum width and height "
  "allowed is 16383 pixels.",
  "PARTITION0_OVERFLOW: Partition #0 is too big to fit 512k.\n"
  "To reduce the size of this partition, try using less segments "
  "with the -segments option, and eventually reduce the number of "
  "header bits using -partition_limit. More details are available "
  "in the manual (`man cwebp`)",
  "PARTITION_OVERFLOW: Partition is too big to fit 16M",
  "BAD_WRITE: Picture writer returned an I/O error",
  "FILE_TOO_BIG: File would be too big to fit in 4G",
  "USER_ABORT: encoding abort requested by user"
};

// The libwebp error code returned in case of timeouts.
static const int kWebPErrorTimeout = VP8_ENC_ERROR_USER_ABORT;

void WebpConfiguration::CopyTo(WebPConfig* webp_config) const {
  webp_config->lossless = lossless;
  webp_config->quality = quality;
  webp_config->method = method;
  webp_config->target_size = target_size;
  webp_config->alpha_compression = alpha_compression;
  webp_config->alpha_filtering = alpha_filtering;
  webp_config->alpha_quality = alpha_quality;
}

WebpFrameWriter::WebpFrameWriter(MessageHandler* handler) :
    MultipleFrameWriter(handler), image_spec_(NULL), next_frame_(0),
    next_scanline_(0), empty_frame_(false), frame_stride_px_(0),
    frame_position_px_(NULL), frame_bytes_per_pixel_(0), webp_image_(NULL),
    webp_frame_cache_(NULL), webp_mux_(NULL), output_image_(NULL),
    has_alpha_(false), image_prepared_(false), progress_hook_(NULL),
    progress_hook_data_(NULL) {
}

WebpFrameWriter::~WebpFrameWriter() {
  FreeWebpStructs();
}

void WebpFrameWriter::FreeWebpStructs() {
  // Shortcut the initial case, which will happen every time this
  // class is used.
  if ((webp_frame_cache_ == NULL) &&
      (webp_image_ == NULL) &&
      (webp_mux_ == NULL)) {
    return;
  }

  WebPFrameCacheDelete(webp_frame_cache_);
  webp_frame_cache_ = NULL;

  WebPPictureFree(webp_image_);
  delete webp_image_;
  webp_image_ = NULL;

  WebPMuxDelete(webp_mux_);
  webp_mux_ = NULL;
}


ScanlineStatus WebpFrameWriter::Initialize(const void* config,
                                           GoogleString* out) {
  FreeWebpStructs();
  webp_mux_ = WebPMuxNew();
  if (webp_mux_ == NULL) {
    return PS_LOGGED_STATUS(PS_LOG_ERROR, message_handler(),
                            SCANLINE_STATUS_INTERNAL_ERROR,
                            FRAME_WEBPWRITER,
                            "WebPMuxNew() failure");
  }

  if (config == NULL) {
    return PS_LOGGED_STATUS(PS_LOG_DFATAL, message_handler(),
                            SCANLINE_STATUS_INVOCATION_ERROR,
                            FRAME_WEBPWRITER,
                            "missing WebpConfiguration*");
  }

  const WebpConfiguration* webp_config =
      static_cast<const WebpConfiguration*>(config);

  if (!WebPConfigInit(&webp_config_)) {
    return PS_LOGGED_STATUS(PS_LOG_ERROR, message_handler(),
                            SCANLINE_STATUS_INTERNAL_ERROR,
                            FRAME_WEBPWRITER, "WebPConfigInit()");
  }

  webp_config->CopyTo(&webp_config_);

  if (!WebPValidateConfig(&webp_config_)) {
    return PS_LOGGED_STATUS(PS_LOG_ERROR, message_handler(),
                            SCANLINE_STATUS_INTERNAL_ERROR,
                            FRAME_WEBPWRITER, "WebPValidateConfig()");
  }

  if (webp_config->progress_hook) {
    progress_hook_ = webp_config->progress_hook;
    progress_hook_data_ = webp_config->user_data;
  }

  output_image_ = out;

  return ScanlineStatus(SCANLINE_STATUS_SUCCESS);
}

int WebpFrameWriter::ProgressHook(int percent, const WebPPicture* picture) {
  const WebpFrameWriter* webp_writer =
      static_cast<WebpFrameWriter*>(picture->user_data);
  return webp_writer->progress_hook_(percent, webp_writer->progress_hook_data_);
}

ScanlineStatus WebpFrameWriter::PrepareImage(const ImageSpec* image_spec) {
  DVLOG(1) << image_spec->ToString();
  if (image_prepared_) {
    return PS_LOGGED_STATUS(PS_LOG_DFATAL, message_handler(),
                            SCANLINE_STATUS_INVOCATION_ERROR,
                            FRAME_WEBPWRITER, "image already prepared");
  }

  DVLOG(1) << "PrepareImage: num_frames: " << image_spec->num_frames;

  if ((image_spec->height > WEBP_MAX_DIMENSION) ||
      (image_spec->width > WEBP_MAX_DIMENSION)) {
    return PS_LOGGED_STATUS(PS_LOG_ERROR, message_handler(),
                            SCANLINE_STATUS_INVOCATION_ERROR,
                            FRAME_WEBPWRITER,
                            "image dimensions larger than the maximum of %d",
                            WEBP_MAX_DIMENSION);
  }

  if ((image_spec->height < 1) || (image_spec->width < 1)) {
    return PS_LOGGED_STATUS(PS_LOG_ERROR, message_handler(),
                            SCANLINE_STATUS_INVOCATION_ERROR,
                            FRAME_WEBPWRITER,
                            "each image dimension must be at least 1");
  }

  webp_image_ = new WebPPicture();

  if (!WebPPictureInit(webp_image_)) {
    return PS_LOGGED_STATUS(PS_LOG_ERROR, message_handler(),
                            SCANLINE_STATUS_INTERNAL_ERROR,
                            FRAME_WEBPWRITER, "WebPPictureInit()");
  }

  webp_image_->width = image_spec->width;
  webp_image_->height = image_spec->height;
  webp_image_->use_argb = true;
#ifndef NDEBUG
  webp_image_->stats = &stats_;
#endif

  if (!WebPPictureAlloc(webp_image_)) {
    return PS_LOGGED_STATUS(PS_LOG_ERROR, message_handler(),
                            SCANLINE_STATUS_INTERNAL_ERROR,
                            FRAME_WEBPWRITER, "WebPPictureAlloc()");
  }
  WebPUtilClearPic(webp_image_, NULL);

  webp_image_->user_data = this;<commit_msg>Log UNSUPPORTED_FEATURE for webp dimensions > 16383. (from jpfeiff)<commit_after>/*
 * Copyright 2009 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

// Author: <NAME>

#include "pagespeed/kernel/image/webp_optimizer.h"

#include "base/logging.h"
#include "pagespeed/kernel/base/message_handler.h"
#include "pagespeed/kernel/image/scanline_utils.h"

extern "C" {
#ifdef USE_SYSTEM_LIBWEBP
#include "webp/decode.h"
#else
#include "third_party/libwebp/src/webp/decode.h"
#endif
}

namespace pagespeed {

namespace image_compression {

using image_compression::GetNumChannelsFromPixelFormat;
using net_instaweb::MessageHandler;

// Copied from libwebp/v0_2/examples/cwebp.c
static const char* const kWebPErrorMessages[] = {
  "OK",
  "OUT_OF_MEMORY: Out of memory allocating objects",
  "BITSTREAM_OUT_OF_MEMORY: Out of memory re-allocating byte buffer",
  "NULL_PARAMETER: NULL parameter passed to function",
  "INVALID_CONFIGURATION: configuration is invalid",
  "BAD_DIMENSION: Bad picture dimension. Maximum width and height "
  "allowed is 16383 pixels.",
  "PARTITION0_OVERFLOW: Partition #0 is too big to fit 512k.\n"
  "To reduce the size of this partition, try using less segments "
  "with the -segments option, and eventually reduce the number of "
  "header bits using -partition_limit. More details are available "
  "in the manual (`man cwebp`)",
  "PARTITION_OVERFLOW: Partition is too big to fit 16M",
  "BAD_WRITE: Picture writer returned an I/O error",
  "FILE_TOO_BIG: File would be too big to fit in 4G",
  "USER_ABORT: encoding abort requested by user"
};

// The libwebp error code returned in case of timeouts.
static const int kWebPErrorTimeout = VP8_ENC_ERROR_USER_ABORT;

void WebpConfiguration::CopyTo(WebPConfig* webp_config) const {
  webp_config->lossless = lossless;
  webp_config->quality = quality;
  webp_config->method = method;
  webp_config->target_size = target_size;
  webp_config->alpha_compression = alpha_compression;
  webp_config->alpha_filtering = alpha_filtering;
  webp_config->alpha_quality = alpha_quality;
}

WebpFrameWriter::WebpFrameWriter(MessageHandler* handler) :
    MultipleFrameWriter(handler), image_spec_(NULL), next_frame_(0),
    next_scanline_(0), empty_frame_(false), frame_stride_px_(0),
    frame_position_px_(NULL), frame_bytes_per_pixel_(0), webp_image_(NULL),
    webp_frame_cache_(NULL), webp_mux_(NULL), output_image_(NULL),
    has_alpha_(false), image_prepared_(false), progress_hook_(NULL),
    progress_hook_data_(NULL) {
}

WebpFrameWriter::~WebpFrameWriter() {
  FreeWebpStructs();
}

void WebpFrameWriter::FreeWebpStructs() {
  // Shortcut the initial case, which will happen every time this
  // class is used.
  if ((webp_frame_cache_ == NULL) &&
      (webp_image_ == NULL) &&
      (webp_mux_ == NULL)) {
    return;
  }

  WebPFrameCacheDelete(webp_frame_cache_);
  webp_frame_cache_ = NULL;

  WebPPictureFree(webp_image_);
  delete webp_image_;
  webp_image_ = NULL;

  WebPMuxDelete(webp_mux_);
  webp_mux_ = NULL;
}


ScanlineStatus WebpFrameWriter::Initialize(const void* config,
                                           GoogleString* out) {
  FreeWebpStructs();
  webp_mux_ = WebPMuxNew();
  if (webp_mux_ == NULL) {
    return PS_LOGGED_STATUS(PS_LOG_ERROR, message_handler(),
                            SCANLINE_STATUS_INTERNAL_ERROR,
                            FRAME_WEBPWRITER,
                            "WebPMuxNew() failure");
  }

  if (config == NULL) {
    return PS_LOGGED_STATUS(PS_LOG_DFATAL, message_handler(),
                            SCANLINE_STATUS_INVOCATION_ERROR,
                            FRAME_WEBPWRITER,
                            "missing WebpConfiguration*");
  }

  const WebpConfiguration* webp_config =
      static_cast<const WebpConfiguration*>(config);

  if (!WebPConfigInit(&webp_config_)) {
    return PS_LOGGED_STATUS(PS_LOG_ERROR, message_handler(),
                            SCANLINE_STATUS_INTERNAL_ERROR,
                            FRAME_WEBPWRITER, "WebPConfigInit()");
  }

  webp_config->CopyTo(&webp_config_);

  if (!WebPValidateConfig(&webp_config_)) {
    return PS_LOGGED_STATUS(PS_LOG_ERROR, message_handler(),
                            SCANLINE_STATUS_INTERNAL_ERROR,
                            FRAME_WEBPWRITER, "WebPValidateConfig()");
  }

  if (webp_config->progress_hook) {
    progress_hook_ = webp_config->progress_hook;
    progress_hook_data_ = webp_config->user_data;
  }

  output_image_ = out;

  return ScanlineStatus(SCANLINE_STATUS_SUCCESS);
}

int WebpFrameWriter::ProgressHook(int percent, const WebPPicture* picture) {
  const WebpFrameWriter* webp_writer =
      static_cast<WebpFrameWriter*>(picture->user_data);
  return webp_writer->progress_hook_(percent, webp_writer->progress_hook_data_);
}

ScanlineStatus WebpFrameWriter::PrepareImage(const ImageSpec* image_spec) {
  DVLOG(1) << image_spec->ToString();
  if (image_prepared_) {
    return PS_LOGGED_STATUS(PS_LOG_DFATAL, message_handler(),
                            SCANLINE_STATUS_INVOCATION_ERROR,
                            FRAME_WEBPWRITER, "image already prepared");
  }

  DVLOG(1) << "PrepareImage: num_frames: " << image_spec->num_frames;

  if ((image_spec->height > WEBP_MAX_DIMENSION) ||
      (image_spec->width > WEBP_MAX_DIMENSION)) {
    return PS_LOGGED_STATUS(PS_LOG_ERROR, message_handler(),
                            SCANLINE_STATUS_UNSUPPORTED_FEATURE,
                            FRAME_WEBPWRITER,
                            "each image dimension must be at most %d",
                            WEBP_MAX_DIMENSION);
  }

  if ((image_spec->height < 1) || (image_spec->width < 1)) {
    return PS_LOGGED_STATUS(PS_LOG_ERROR, message_handler(),
                            SCANLINE_STATUS_UNSUPPORTED_FEATURE,
                            FRAME_WEBPWRITER,
                            "each image dimension must be at least 1");
  }

  webp_image_ = new WebPPicture();

  if (!WebPPictureInit(webp_image_)) {
    return PS_LOGGED_STATUS(PS_LOG_ERROR, message_handler(),
                            SCANLINE_STATUS_INTERNAL_ERROR,
                            FRAME_WEBPWRITER, "WebPPictureInit()");
  }

  webp_image_->width = image_spec->width;
  webp_image_->height = image_spec->height;
  webp_image_->use_argb = true;
#ifndef NDEBUG
  webp_image_->stats = &stats_;
#endif

  if (!WebPPictureAlloc(webp_image_)) {
    return PS_LOGGED_STATUS(PS_LOG_ERROR, message_handler(),
                            SCANLINE_STATUS_INTERNAL_ERROR,
                            FRAME_WEBPWRITER, "WebPPictureAlloc()");
  }
  WebPUtilClearPic(webp_image_, NULL);

  webp_image_->user_data = this;
{'fraction_non_alphanumeric': 0.09370404089922263, 'fraction_numerical': 0.004972336998389243, 'mean_word_length': 3.2857142857142856, 'pattern_counts': {'":': 0, '<': 25, '<?xml version=': 0, '>': 68, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 4, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5441486', 'n_tokens_mistral': 4595, 'n_tokens_neox': 4473, 'n_words': 966}