chromium/ui/gfx/image/image_util_ios.mm

// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#import <UIKit/UIKit.h>

#include "build/blink_buildflags.h"
#include "ui/gfx/image/image.h"
#include "ui/gfx/image/image_util.h"
#include "ui/gfx/image/resize_image_dimensions.h"

#if BUILDFLAG(USE_BLINK)
#include "third_party/skia/include/core/SkBitmap.h"
#include "ui/gfx/codec/jpeg_codec.h"
#endif  // BUILDFLAG(USE_BLINK)

namespace {
// Copied from GTMUIImage+Resize in //third_party/google_toolbox_for_mac to
// avoid depending on other GTM* classes unnecessarily.
UIImage* ResizeUIImage(UIImage* image,
                       CGSize target_size,
                       BOOL preserve_aspect_ratio,
                       BOOL trim_to_fit) {
  CGSize imageSize = image.size;
  if (imageSize.height < 1 || imageSize.width < 1) {
    return nil;
  }
  if (target_size.height < 1 || target_size.width < 1) {
    return nil;
  }
  CGFloat aspectRatio = imageSize.width / imageSize.height;
  CGFloat targetAspectRatio = target_size.width / target_size.height;
  CGRect projectTo = CGRectZero;
  if (preserve_aspect_ratio) {
    if (trim_to_fit) {
      // Scale and clip image so that the aspect ratio is preserved and the
      // target size is filled.
      if (targetAspectRatio < aspectRatio) {
        // clip the x-axis.
        projectTo.size.width = target_size.height * aspectRatio;
        projectTo.size.height = target_size.height;
        projectTo.origin.x = (target_size.width - projectTo.size.width) / 2;
        projectTo.origin.y = 0;
      } else {
        // clip the y-axis.
        projectTo.size.width = target_size.width;
        projectTo.size.height = target_size.width / aspectRatio;
        projectTo.origin.x = 0;
        projectTo.origin.y = (target_size.height - projectTo.size.height) / 2;
      }
    } else {
      // Scale image to ensure it fits inside the specified target_size.
      if (targetAspectRatio < aspectRatio) {
        // target is less wide than the original.
        projectTo.size.width = target_size.width;
        projectTo.size.height = projectTo.size.width / aspectRatio;
        target_size = projectTo.size;
      } else {
        // target is wider than the original.
        projectTo.size.height = target_size.height;
        projectTo.size.width = projectTo.size.height * aspectRatio;
        target_size = projectTo.size;
      }
    }  // if (clip)
  } else {
    // Don't preserve the aspect ratio.
    projectTo.size = target_size;
  }

  projectTo = CGRectIntegral(projectTo);
  // There's no CGSizeIntegral, so we fake our own.
  CGRect integralRect = CGRectZero;
  integralRect.size = target_size;
  target_size = CGRectIntegral(integralRect).size;

  // Resize photo. Use UIImage drawing methods because they respect
  // UIImageOrientation as opposed to CGContextDrawImage().
  UIGraphicsBeginImageContext(target_size);
  [image drawInRect:projectTo];
  UIImage* resizedPhoto = UIGraphicsGetImageFromCurrentImageContext();
  UIGraphicsEndImageContext();
  return resizedPhoto;
}
}  // namespace

namespace gfx {

bool JPEG1xEncodedDataFromImage(const Image& image,
                                int quality,
                                std::vector<unsigned char>* dst) {
  NSData* data = UIImageJPEGRepresentation(image.ToUIImage(), quality / 100.0);

  if (data.length == 0) {
    return false;
  }

  dst->resize(data.length);
  [data getBytes:&dst->at(0) length:data.length];
  return true;
}

Image ResizedImageForSearchByImage(const Image& image) {
  if (image.IsEmpty()) {
    return image;
  }
  UIImage* ui_image = image.ToUIImage();

  if (ui_image &&
      ui_image.size.height * ui_image.size.width > kSearchByImageMaxImageArea &&
      (ui_image.size.width > kSearchByImageMaxImageWidth ||
       ui_image.size.height > kSearchByImageMaxImageHeight)) {
    CGSize new_image_size =
        CGSizeMake(kSearchByImageMaxImageWidth, kSearchByImageMaxImageHeight);
    ui_image = ResizeUIImage(ui_image, new_image_size,
                             /*preserve_aspect_ratio=*/YES, /*trim_to_fit=*/NO);
  }
  return Image(ui_image);
}

#if BUILDFLAG(USE_BLINK)
Image ImageFrom1xJPEGEncodedData(const unsigned char* input,
                                 size_t input_size) {
  std::unique_ptr<SkBitmap> bitmap(gfx::JPEGCodec::Decode(input, input_size));
  if (bitmap.get()) {
    return Image::CreateFrom1xBitmap(*bitmap);
  }

  return Image();
}
#endif  // BUILDFLAG(USE_BLINK)

Image ResizedImage(const Image& image, const gfx::Size& size) {
  UIImage* ui_image =
      ResizeUIImage(image.ToUIImage(), size.ToCGSize(),
       /*preserve_aspect_ratio=*/NO, /*trim_to_fit=*/NO);
  return Image(ui_image);
}
}  // end namespace gfx