Home | History | Annotate | Download | only in image
      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 
     19 namespace gfx {
     20 namespace internal {
     21 
     22 namespace {
     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.
     27 UIImage* 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.
     49 UIImage* 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 
     60 scoped_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 
     74 UIImage* 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 
     96 scoped_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 
    111 ImageSkia* 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 
    127 gfx::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