<commit_before>/*
 * Copyright 2016 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: <EMAIL> (<NAME>)

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

#include <algorithm>
#include <memory>

extern "C" {
#ifdef USE_SYSTEM_LIBPNG
#include "png.h"  // NOLINT
#else
#include "third_party/libpng/src/png.h"
#endif
}  // extern "C"

#include "base/logging.h"
#include "pagespeed/kernel/base/message_handler.h"
#include "pagespeed/kernel/base/string.h"
#include "pagespeed/kernel/base/timer.h"
#include "pagespeed/kernel/image/image_analysis.h"
#include "pagespeed/kernel/image/image_converter.h"
#include "pagespeed/kernel/image/image_frame_interface.h"
#include "pagespeed/kernel/image/image_resizer.h"
#include "pagespeed/kernel/image/image_util.h"
#include "pagespeed/kernel/image/jpeg_optimizer.h"
#include "pagespeed/kernel/image/pixel_format_optimizer.h"
#include "pagespeed/kernel/image/png_optimizer.h"
#include "pagespeed/kernel/image/read_image.h"
#include "pagespeed/kernel/image/scanline_interface.h"
#include "pagespeed/kernel/image/scanline_status.h"
#include "pagespeed/kernel/image/webp_optimizer.h"

using net_instaweb::MessageHandler;
using net_instaweb::Timer;
using pagespeed::image_compression::ImageDimensions;
using pagespeed::image_compression::WebpConfiguration;
using pagespeed::image_compression::ConversionTimeoutHandler;
using pagespeed::image_compression::JpegCompressionOptions;
using pagespeed::image_compression::PngCompressParams;
using pagespeed::image_compression::ScanlineWriterConfig;
using pagespeed::image_compression::ImageFormat;
using pagespeed::image_compression::MultipleFrameReader;
using pagespeed::image_compression::MultipleFrameWriter;
using pagespeed::image_compression::ScanlineStatus;
using pagespeed::image_compression::ImageOptions;
using pagespeed::image_compression::ShouldConvertToProgressive;
using pagespeed::image_compression::PixelFormatOptimizer;
using pagespeed::image_compression::ScanlineReaderInterface;
using pagespeed::image_compression::ScanlineResizer;
using pagespeed::image_compression::ScanlineWriterInterface;
using pagespeed::image_compression::ImageConverter;
using pagespeed::image_compression::ComputeImageType;
using pagespeed::image_compression::IMAGE_GIF;
using pagespeed::image_compression::IMAGE_PNG;
using pagespeed::image_compression::IMAGE_JPEG;
using pagespeed::image_compression::IMAGE_WEBP;
using pagespeed::image_compression::IMAGE_UNKNOWN;

namespace pagespeed {

namespace image_compression {

bool ImageOptimizer::ComputeDesiredFormat() {
  desired_lossless_ = false;
  optimized_format_ = IMAGE_UNKNOWN;
  if (is_animated_) {
    if (options_.allow_webp_animated()) {
      optimized_format_ = IMAGE_WEBP;
      desired_lossless_ = true;
    }
  } else if (is_transparent_) {
    if (options_.allow_webp_lossless_or_alpha()) {
      optimized_format_ = IMAGE_WEBP;
      desired_lossless_ = true;
    } else if (options_.allow_png()) {
      optimized_format_ = IMAGE_PNG;
    }
  } else {
    // single frame and opaque
    if (is_photo_ &&
        (original_format_ == IMAGE_JPEG ||
         options_.allow_convert_lossless_to_lossy())) {
      // We can use lossy format.
      if (options_.allow_webp_lossy()) {
        optimized_format_ = IMAGE_WEBP;
      } else if (options_.allow_jpeg()) {
        optimized_format_ = IMAGE_JPEG;
      }
    } else if (options_.allow_webp_lossless_or_alpha()) {
      optimized_format_ = IMAGE_WEBP;
      desired_lossless_ = true;
    } else if (options_.allow_png()) {
      optimized_format_ = IMAGE_PNG;
    }
  }

  return optimized_format_ != IMAGE_UNKNOWN;
}

// Computes the dimension for the resized image. In the requested dimensions
// you can specify the width, height, or both. This method will compute
// the unspecified dimension. If the input dimensions are valid and the
// output dimensions are smaller than the input, this method returns TRUE.
bool ImageOptimizer::ComputeResizedDimension() {
  if (original_width_ < 1 || original_height_ < 1 ||
      (requested_dim_.has_width() && requested_dim_.width() < 1) ||
      (requested_dim_.has_height() && requested_dim_.height() < 1)) {
    return false;
  }

  // Do not resize the image in these cases:
  //  - input is an animated image
  //  - requested dimension is larger than the original in either way.
  // By returning TRUE, the image can still have other optimizations.
  if (is_animated_ ||
      (requested_dim_.has_width() &&
       requested_dim_.width() > original_width_) ||
      (requested_dim_.has_height() &&
       requested_dim_.height() > original_height_)) {
    optimized_width_ = original_width_;
    optimized_height_ = original_height_;
    return true;
  }

  if (!requested_dim_.has_width() && !requested_dim_.has_height()) {
    optimized_width_ = original_width_;
    optimized_height_ = original_height_;
  } else if (!requested_dim_.has_width()) {
    optimized_height_ = requested_dim_.height();
    optimized_width_ =
        (optimized_height_ * original_width_ + original_height_ / 2) /
        original_height_;
  } else if (!requested_dim_.has_height()) {
    optimized_width_ = requested_dim_.width();
    optimized_height_ =
        (optimized_width_ * original_height_ + original_width_ / 2) /
        original_width_;
  } else {
    optimized_width_ = requested_dim_.width();
    optimized_height_ = requested_dim_.height();
  }

  return true;
}

bool ImageOptimizer::ComputeDesiredQualityProgressive() {
  // Determines quality level and whether to use progressive format.
  desired_progressive_ = false;
  int quality = original_quality_;
  if (quality == -1) {
    quality = 100;
  }
  if (optimized_format_ == IMAGE_JPEG) {
    quality = std::min(quality, options_.max_jpeg_quality());
    const int kMinJpegProgressiveBytes = 10240;
    desired_progressive_ =
        ShouldConvertToProgressive(
            quality, kMinJpegProgressiveBytes,
            original_contents_.length(), optimized_width_, optimized_height_);

  } else if (is_animated_) {
    quality = std::min(quality, options_.max_webp_animated_quality());
  } else {
    quality = std::min(quality, options_.max_webp_quality());
  }

  if (quality >= 0 && quality <= 100) {
    desired_quality_ = quality;
    return true;
  }
  return false;
}

// TODO(huibao): Unify ImageFormat and ImageType.
ImageFormat ImageOptimizer::ImageTypeToImageFormat(
    net_instaweb::ImageType image_type) {
  ImageFormat image_format;
  switch (image_type) {
    case net_instaweb::IMAGE_UNKNOWN:
      image_format = IMAGE_UNKNOWN;
      break;
    case net_instaweb::IMAGE_JPEG:
      image_format = IMAGE_JPEG;
      break;
    case net_instaweb::IMAGE_PNG:
      image_format = IMAGE_PNG;
      break;<commit_msg>Fix build with -O2 on gcc-4.8; it was complaining about potentially unitialized image_format.<commit_after>/*
 * Copyright 2016 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: <EMAIL> (<NAME>)

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

#include <algorithm>
#include <memory>

extern "C" {
#ifdef USE_SYSTEM_LIBPNG
#include "png.h"  // NOLINT
#else
#include "third_party/libpng/src/png.h"
#endif
}  // extern "C"

#include "base/logging.h"
#include "pagespeed/kernel/base/message_handler.h"
#include "pagespeed/kernel/base/string.h"
#include "pagespeed/kernel/base/timer.h"
#include "pagespeed/kernel/image/image_analysis.h"
#include "pagespeed/kernel/image/image_converter.h"
#include "pagespeed/kernel/image/image_frame_interface.h"
#include "pagespeed/kernel/image/image_resizer.h"
#include "pagespeed/kernel/image/image_util.h"
#include "pagespeed/kernel/image/jpeg_optimizer.h"
#include "pagespeed/kernel/image/pixel_format_optimizer.h"
#include "pagespeed/kernel/image/png_optimizer.h"
#include "pagespeed/kernel/image/read_image.h"
#include "pagespeed/kernel/image/scanline_interface.h"
#include "pagespeed/kernel/image/scanline_status.h"
#include "pagespeed/kernel/image/webp_optimizer.h"

using net_instaweb::MessageHandler;
using net_instaweb::Timer;
using pagespeed::image_compression::ImageDimensions;
using pagespeed::image_compression::WebpConfiguration;
using pagespeed::image_compression::ConversionTimeoutHandler;
using pagespeed::image_compression::JpegCompressionOptions;
using pagespeed::image_compression::PngCompressParams;
using pagespeed::image_compression::ScanlineWriterConfig;
using pagespeed::image_compression::ImageFormat;
using pagespeed::image_compression::MultipleFrameReader;
using pagespeed::image_compression::MultipleFrameWriter;
using pagespeed::image_compression::ScanlineStatus;
using pagespeed::image_compression::ImageOptions;
using pagespeed::image_compression::ShouldConvertToProgressive;
using pagespeed::image_compression::PixelFormatOptimizer;
using pagespeed::image_compression::ScanlineReaderInterface;
using pagespeed::image_compression::ScanlineResizer;
using pagespeed::image_compression::ScanlineWriterInterface;
using pagespeed::image_compression::ImageConverter;
using pagespeed::image_compression::ComputeImageType;
using pagespeed::image_compression::IMAGE_GIF;
using pagespeed::image_compression::IMAGE_PNG;
using pagespeed::image_compression::IMAGE_JPEG;
using pagespeed::image_compression::IMAGE_WEBP;
using pagespeed::image_compression::IMAGE_UNKNOWN;

namespace pagespeed {

namespace image_compression {

bool ImageOptimizer::ComputeDesiredFormat() {
  desired_lossless_ = false;
  optimized_format_ = IMAGE_UNKNOWN;
  if (is_animated_) {
    if (options_.allow_webp_animated()) {
      optimized_format_ = IMAGE_WEBP;
      desired_lossless_ = true;
    }
  } else if (is_transparent_) {
    if (options_.allow_webp_lossless_or_alpha()) {
      optimized_format_ = IMAGE_WEBP;
      desired_lossless_ = true;
    } else if (options_.allow_png()) {
      optimized_format_ = IMAGE_PNG;
    }
  } else {
    // single frame and opaque
    if (is_photo_ &&
        (original_format_ == IMAGE_JPEG ||
         options_.allow_convert_lossless_to_lossy())) {
      // We can use lossy format.
      if (options_.allow_webp_lossy()) {
        optimized_format_ = IMAGE_WEBP;
      } else if (options_.allow_jpeg()) {
        optimized_format_ = IMAGE_JPEG;
      }
    } else if (options_.allow_webp_lossless_or_alpha()) {
      optimized_format_ = IMAGE_WEBP;
      desired_lossless_ = true;
    } else if (options_.allow_png()) {
      optimized_format_ = IMAGE_PNG;
    }
  }

  return optimized_format_ != IMAGE_UNKNOWN;
}

// Computes the dimension for the resized image. In the requested dimensions
// you can specify the width, height, or both. This method will compute
// the unspecified dimension. If the input dimensions are valid and the
// output dimensions are smaller than the input, this method returns TRUE.
bool ImageOptimizer::ComputeResizedDimension() {
  if (original_width_ < 1 || original_height_ < 1 ||
      (requested_dim_.has_width() && requested_dim_.width() < 1) ||
      (requested_dim_.has_height() && requested_dim_.height() < 1)) {
    return false;
  }

  // Do not resize the image in these cases:
  //  - input is an animated image
  //  - requested dimension is larger than the original in either way.
  // By returning TRUE, the image can still have other optimizations.
  if (is_animated_ ||
      (requested_dim_.has_width() &&
       requested_dim_.width() > original_width_) ||
      (requested_dim_.has_height() &&
       requested_dim_.height() > original_height_)) {
    optimized_width_ = original_width_;
    optimized_height_ = original_height_;
    return true;
  }

  if (!requested_dim_.has_width() && !requested_dim_.has_height()) {
    optimized_width_ = original_width_;
    optimized_height_ = original_height_;
  } else if (!requested_dim_.has_width()) {
    optimized_height_ = requested_dim_.height();
    optimized_width_ =
        (optimized_height_ * original_width_ + original_height_ / 2) /
        original_height_;
  } else if (!requested_dim_.has_height()) {
    optimized_width_ = requested_dim_.width();
    optimized_height_ =
        (optimized_width_ * original_height_ + original_width_ / 2) /
        original_width_;
  } else {
    optimized_width_ = requested_dim_.width();
    optimized_height_ = requested_dim_.height();
  }

  return true;
}

bool ImageOptimizer::ComputeDesiredQualityProgressive() {
  // Determines quality level and whether to use progressive format.
  desired_progressive_ = false;
  int quality = original_quality_;
  if (quality == -1) {
    quality = 100;
  }
  if (optimized_format_ == IMAGE_JPEG) {
    quality = std::min(quality, options_.max_jpeg_quality());
    const int kMinJpegProgressiveBytes = 10240;
    desired_progressive_ =
        ShouldConvertToProgressive(
            quality, kMinJpegProgressiveBytes,
            original_contents_.length(), optimized_width_, optimized_height_);

  } else if (is_animated_) {
    quality = std::min(quality, options_.max_webp_animated_quality());
  } else {
    quality = std::min(quality, options_.max_webp_quality());
  }

  if (quality >= 0 && quality <= 100) {
    desired_quality_ = quality;
    return true;
  }
  return false;
}

// TODO(huibao): Unify ImageFormat and ImageType.
ImageFormat ImageOptimizer::ImageTypeToImageFormat(
    net_instaweb::ImageType image_type) {
  ImageFormat image_format = IMAGE_UNKNOWN;
  switch (image_type) {
    case net_instaweb::IMAGE_UNKNOWN:
      image_format = IMAGE_UNKNOWN;
      break;
    case net_instaweb::IMAGE_JPEG:
      image_format = IMAGE_JPEG;
      break;
    case net_instaweb::IMAGE_PNG:
      image_format = IMAGE_PNG;
      break;
{'fraction_non_alphanumeric': 0.10168331736690844, 'fraction_numerical': 0.003900369508690297, 'mean_word_length': 4.618992695117263, 'pattern_counts': {'":': 0, '<': 21, '<?xml version=': 0, '>': 17, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3212948', 'n_tokens_mistral': 4559, 'n_tokens_neox': 4592, 'n_words': 988}