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 "skia/ext/skia_utils_ios.h" 6 7#import <ImageIO/ImageIO.h> 8#include <stddef.h> 9#include <stdint.h> 10#import <UIKit/UIKit.h> 11 12#include "base/ios/ios_util.h" 13#include "base/logging.h" 14#include "base/mac/scoped_cftyperef.h" 15#include "base/stl_util.h" 16#include "third_party/skia/include/utils/mac/SkCGUtils.h" 17 18namespace { 19 20const uint8_t kICOHeaderMagic[4] = {0x00, 0x00, 0x01, 0x00}; 21 22// Returns whether the data encodes an ico image. 23bool EncodesIcoImage(NSData* image_data) { 24 if (image_data.length < base::size(kICOHeaderMagic)) 25 return false; 26 return memcmp(kICOHeaderMagic, image_data.bytes, 27 base::size(kICOHeaderMagic)) == 0; 28} 29 30} // namespace 31 32namespace skia { 33 34SkBitmap CGImageToSkBitmap(CGImageRef image, CGSize size, bool is_opaque) { 35 SkBitmap bitmap; 36 if (!image) 37 return bitmap; 38 39 if (!bitmap.tryAllocN32Pixels(size.width, size.height, is_opaque)) 40 return bitmap; 41 42 void* data = bitmap.getPixels(); 43 44 // Allocate a bitmap context with 4 components per pixel (BGRA). Apple 45 // recommends these flags for improved CG performance. 46#define HAS_ARGB_SHIFTS(a, r, g, b) \ 47 (SK_A32_SHIFT == (a) && SK_R32_SHIFT == (r) \ 48 && SK_G32_SHIFT == (g) && SK_B32_SHIFT == (b)) 49#if defined(SK_CPU_LENDIAN) && HAS_ARGB_SHIFTS(24, 16, 8, 0) 50 base::ScopedCFTypeRef<CGColorSpaceRef> color_space( 51 CGColorSpaceCreateDeviceRGB()); 52 base::ScopedCFTypeRef<CGContextRef> context(CGBitmapContextCreate( 53 data, 54 size.width, 55 size.height, 56 8, 57 size.width * 4, 58 color_space, 59 kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host)); 60#else 61#error We require that Skia's and CoreGraphics's recommended \ 62 image memory layout match. 63#endif 64#undef HAS_ARGB_SHIFTS 65 66 DCHECK(context); 67 if (!context) 68 return bitmap; 69 70 CGRect imageRect = CGRectMake(0.0, 0.0, size.width, size.height); 71 CGContextSetBlendMode(context, kCGBlendModeCopy); 72 CGContextDrawImage(context, imageRect, image); 73 74 return bitmap; 75} 76 77UIImage* SkBitmapToUIImageWithColorSpace(const SkBitmap& skia_bitmap, 78 CGFloat scale, 79 CGColorSpaceRef color_space) { 80 if (skia_bitmap.isNull()) 81 return nil; 82 83 // First convert SkBitmap to CGImageRef. 84 base::ScopedCFTypeRef<CGImageRef> cg_image( 85 SkCreateCGImageRefWithColorspace(skia_bitmap, color_space)); 86 87 // Now convert to UIImage. 88 return [UIImage imageWithCGImage:cg_image.get() 89 scale:scale 90 orientation:UIImageOrientationUp]; 91} 92 93std::vector<SkBitmap> ImageDataToSkBitmaps(NSData* image_data) { 94 DCHECK(image_data); 95 96 // On iOS 8.1.1 |CGContextDrawImage| crashes when processing images included 97 // in .ico files that are 88x88 pixels or larger (http://crbug.com/435068). 98 bool skip_images_88x88_or_larger = 99 base::ios::IsRunningOnOrLater(8, 1, 1) && EncodesIcoImage(image_data); 100 101 base::ScopedCFTypeRef<CFDictionaryRef> empty_dictionary( 102 CFDictionaryCreate(NULL, NULL, NULL, 0, NULL, NULL)); 103 std::vector<SkBitmap> frames; 104 105 base::ScopedCFTypeRef<CGImageSourceRef> source( 106 CGImageSourceCreateWithData((CFDataRef)image_data, empty_dictionary)); 107 108 size_t count = CGImageSourceGetCount(source); 109 for (size_t index = 0; index < count; ++index) { 110 base::ScopedCFTypeRef<CGImageRef> cg_image( 111 CGImageSourceCreateImageAtIndex(source, index, empty_dictionary)); 112 113 CGSize size = CGSizeMake(CGImageGetWidth(cg_image), 114 CGImageGetHeight(cg_image)); 115 if (size.width >= 88 && size.height >= 88 && skip_images_88x88_or_larger) 116 continue; 117 118 const SkBitmap bitmap = CGImageToSkBitmap(cg_image, size, false); 119 if (!bitmap.empty()) 120 frames.push_back(bitmap); 121 } 122 123 DLOG_IF(WARNING, frames.size() != count) << "Only decoded " << frames.size() 124 << " frames for " << count << " expected."; 125 return frames; 126} 127 128UIColor* UIColorFromSkColor(SkColor color) { 129 return [UIColor colorWithRed:SkColorGetR(color) / 255.0f 130 green:SkColorGetG(color) / 255.0f 131 blue:SkColorGetB(color) / 255.0f 132 alpha:SkColorGetA(color) / 255.0f]; 133} 134 135} // namespace skia 136