1// Copyright (c) 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 <AppKit/AppKit.h>
8
9#include "base/logging.h"
10#include "base/mac/scoped_nsobject.h"
11#include "ui/gfx/image/image_png_rep.h"
12#include "ui/gfx/size.h"
13
14namespace gfx {
15namespace internal {
16
17namespace {
18
19// Returns a 16x16 red NSImage to visually show when a NSImage cannot be
20// created from PNG data.
21// Caller takes ownership of the returned NSImage.
22NSImage* GetErrorNSImage() {
23  NSRect rect = NSMakeRect(0, 0, 16, 16);
24  NSImage* image = [[NSImage alloc] initWithSize:rect.size];
25  [image lockFocus];
26  [[NSColor colorWithDeviceRed:1.0 green:0.0 blue:0.0 alpha:1.0] set];
27  NSRectFill(rect);
28  [image unlockFocus];
29  return image;
30}
31
32}  // namespace
33
34scoped_refptr<base::RefCountedMemory> Get1xPNGBytesFromNSImage(
35    NSImage* nsimage) {
36  CGImageRef cg_image = [nsimage CGImageForProposedRect:NULL
37                                                context:nil
38                                                  hints:nil];
39  base::scoped_nsobject<NSBitmapImageRep> ns_bitmap(
40      [[NSBitmapImageRep alloc] initWithCGImage:cg_image]);
41  NSData* ns_data = [ns_bitmap representationUsingType:NSPNGFileType
42                                            properties:nil];
43  const unsigned char* bytes =
44      static_cast<const unsigned char*>([ns_data bytes]);
45  scoped_refptr<base::RefCountedBytes> refcounted_bytes(
46      new base::RefCountedBytes());
47  refcounted_bytes->data().assign(bytes, bytes + [ns_data length]);
48  return refcounted_bytes;
49}
50
51NSImage* NSImageFromPNG(const std::vector<gfx::ImagePNGRep>& image_png_reps,
52                        CGColorSpaceRef color_space) {
53  if (image_png_reps.empty()) {
54    LOG(ERROR) << "Unable to decode PNG.";
55    return GetErrorNSImage();
56  }
57
58  base::scoped_nsobject<NSImage> image;
59  for (size_t i = 0; i < image_png_reps.size(); ++i) {
60    scoped_refptr<base::RefCountedMemory> png = image_png_reps[i].raw_data;
61    CHECK(png.get());
62    base::scoped_nsobject<NSData> ns_data(
63        [[NSData alloc] initWithBytes:png->front() length:png->size()]);
64    base::scoped_nsobject<NSBitmapImageRep> ns_image_rep(
65        [[NSBitmapImageRep alloc] initWithData:ns_data]);
66    if (!ns_image_rep) {
67      LOG(ERROR) << "Unable to decode PNG at "
68                 << image_png_reps[i].scale
69                 << ".";
70      return GetErrorNSImage();
71    }
72
73    // PNGCodec ignores colorspace related ancillary chunks (sRGB, iCCP). Ignore
74    // colorspace information when decoding directly from PNG to an NSImage so
75    // that the conversions: PNG -> SkBitmap -> NSImage and PNG -> NSImage
76    // produce visually similar results.
77    CGColorSpaceModel decoded_color_space_model = CGColorSpaceGetModel(
78        [[ns_image_rep colorSpace] CGColorSpace]);
79    CGColorSpaceModel color_space_model = CGColorSpaceGetModel(color_space);
80    if (decoded_color_space_model == color_space_model) {
81      base::scoped_nsobject<NSColorSpace> ns_color_space(
82          [[NSColorSpace alloc] initWithCGColorSpace:color_space]);
83      NSBitmapImageRep* ns_retagged_image_rep =
84          [ns_image_rep
85              bitmapImageRepByRetaggingWithColorSpace:ns_color_space];
86      if (ns_retagged_image_rep && ns_retagged_image_rep != ns_image_rep)
87        ns_image_rep.reset([ns_retagged_image_rep retain]);
88    }
89
90    if (!image.get()) {
91      float scale = image_png_reps[i].scale;
92      NSSize image_size = NSMakeSize([ns_image_rep pixelsWide] / scale,
93                                     [ns_image_rep pixelsHigh] / scale);
94      image.reset([[NSImage alloc] initWithSize:image_size]);
95    }
96    [image addRepresentation:ns_image_rep];
97  }
98
99  return image.release();
100}
101
102gfx::Size NSImageSize(NSImage* image) {
103  NSSize size = [image size];
104  int width = static_cast<int>(size.width);
105  int height = static_cast<int>(size.height);
106  return gfx::Size(width, height);
107}
108
109} // namespace internal
110} // namespace gfx
111
112