1// Copyright 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "ui/gfx/image/image.h"
6
7#import <UIKit/UIKit.h>
8#include <cmath>
9#include <limits>
10
11#include "base/logging.h"
12#include "base/mac/scoped_cftyperef.h"
13#include "base/mac/scoped_nsobject.h"
14#include "ui/gfx/image/image_png_rep.h"
15#include "ui/gfx/image/image_skia.h"
16#include "ui/gfx/image/image_skia_util_ios.h"
17#include "ui/gfx/size.h"
18
19namespace gfx {
20namespace internal {
21
22namespace {
23
24// Returns a 16x16 red UIImage to visually show when a UIImage cannot be
25// created from PNG data. Logs error as well.
26// Caller takes ownership of returned UIImage.
27UIImage* CreateErrorUIImage(float scale) {
28  LOG(ERROR) << "Unable to decode PNG into UIImage.";
29  base::ScopedCFTypeRef<CGColorSpaceRef> color_space(
30      CGColorSpaceCreateDeviceRGB());
31  base::ScopedCFTypeRef<CGContextRef> context(CGBitmapContextCreate(
32      NULL,  // Allow CG to allocate memory.
33      16,    // width
34      16,    // height
35      8,     // bitsPerComponent
36      0,     // CG will calculate by default.
37      color_space,
38      kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host));
39  CGContextSetRGBFillColor(context, 1.0, 0.0, 0.0, 1.0);
40  CGContextFillRect(context, CGRectMake(0.0, 0.0, 16, 16));
41  base::ScopedCFTypeRef<CGImageRef> cg_image(
42      CGBitmapContextCreateImage(context));
43  return [[UIImage imageWithCGImage:cg_image.get()
44                              scale:scale
45                        orientation:UIImageOrientationUp] retain];
46}
47
48// Converts from ImagePNGRep to UIImage.
49UIImage* CreateUIImageFromImagePNGRep(const gfx::ImagePNGRep& image_png_rep) {
50  float scale = image_png_rep.scale;
51  scoped_refptr<base::RefCountedMemory> png = image_png_rep.raw_data;
52  CHECK(png.get());
53  NSData* data = [NSData dataWithBytes:png->front() length:png->size()];
54  UIImage* image = [[UIImage alloc] initWithData:data scale:scale];
55  return image ? image : CreateErrorUIImage(scale);
56}
57
58}  // namespace
59
60scoped_refptr<base::RefCountedMemory> Get1xPNGBytesFromUIImage(
61    UIImage* uiimage) {
62  NSData* data = UIImagePNGRepresentation(uiimage);
63
64  if ([data length] == 0)
65    return NULL;
66
67  scoped_refptr<base::RefCountedBytes> png_bytes(
68      new base::RefCountedBytes());
69  png_bytes->data().resize([data length]);
70  [data getBytes:&png_bytes->data().at(0) length:[data length]];
71  return png_bytes;
72}
73
74UIImage* CreateUIImageFromPNG(
75    const std::vector<gfx::ImagePNGRep>& image_png_reps) {
76  float ideal_scale = ImageSkia::GetMaxSupportedScale();
77
78  if (image_png_reps.empty())
79    return CreateErrorUIImage(ideal_scale);
80
81  // Find best match for |ideal_scale|.
82  float smallest_diff = std::numeric_limits<float>::max();
83  size_t closest_index = 0u;
84  for (size_t i = 0; i < image_png_reps.size(); ++i) {
85    float scale = image_png_reps[i].scale;
86    float diff = std::abs(ideal_scale - scale);
87    if (diff < smallest_diff) {
88      smallest_diff = diff;
89      closest_index = i;
90    }
91  }
92
93  return CreateUIImageFromImagePNGRep(image_png_reps[closest_index]);
94}
95
96scoped_refptr<base::RefCountedMemory> Get1xPNGBytesFromImageSkia(
97    const ImageSkia* skia) {
98  // iOS does not expose libpng, so conversion from ImageSkia to PNG must go
99  // through UIImage.
100  // TODO(rohitrao): Rewrite the callers of this function to save the UIImage
101  // representation in the gfx::Image.  If we're generating it, we might as well
102  // hold on to it.
103  const gfx::ImageSkiaRep& image_skia_rep = skia->GetRepresentation(1.0f);
104  if (image_skia_rep.scale() != 1.0f)
105    return NULL;
106
107  UIImage* image = UIImageFromImageSkiaRep(image_skia_rep);
108  return Get1xPNGBytesFromUIImage(image);
109}
110
111ImageSkia* ImageSkiaFromPNG(
112    const std::vector<gfx::ImagePNGRep>& image_png_reps) {
113  // iOS does not expose libpng, so conversion from PNG to ImageSkia must go
114  // through UIImage.
115  gfx::ImageSkia* image_skia = new gfx::ImageSkia();
116  for (size_t i = 0; i < image_png_reps.size(); ++i) {
117    base::scoped_nsobject<UIImage> uiimage(
118        CreateUIImageFromImagePNGRep(image_png_reps[i]));
119    gfx::ImageSkiaRep image_skia_rep = ImageSkiaRepOfScaleFromUIImage(
120        uiimage, image_png_reps[i].scale);
121    if (!image_skia_rep.is_null())
122      image_skia->AddRepresentation(image_skia_rep);
123  }
124  return image_skia;
125}
126
127gfx::Size UIImageSize(UIImage* image) {
128  int width = static_cast<int>(image.size.width);
129  int height = static_cast<int>(image.size.height);
130  return gfx::Size(width, height);
131}
132
133} // namespace internal
134} // namespace gfx
135