1/* 2 * Copyright 2011 Google Inc. 3 * 4 * Use of this source code is governed by a BSD-style license that can be 5 * found in the LICENSE file. 6 */ 7 8#include "include/core/SkTypes.h" 9#if defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_IOS) 10 11#include "include/core/SkBitmap.h" 12#include "include/private/SkColorData.h" 13#include "include/private/SkMacros.h" 14#include "include/private/SkTo.h" 15#include "include/utils/mac/SkCGUtils.h" 16#include "src/utils/mac/SkUniqueCFRef.h" 17 18#include <climits> 19#include <memory> 20 21static CGBitmapInfo compute_cgalpha_info_rgba(SkAlphaType at) { 22 CGBitmapInfo info = kCGBitmapByteOrder32Big; 23 switch (at) { 24 case kUnknown_SkAlphaType: break; 25 case kOpaque_SkAlphaType: info |= kCGImageAlphaNoneSkipLast; break; 26 case kPremul_SkAlphaType: info |= kCGImageAlphaPremultipliedLast; break; 27 case kUnpremul_SkAlphaType: info |= kCGImageAlphaLast; break; 28 } 29 return info; 30} 31 32static CGBitmapInfo compute_cgalpha_info_bgra(SkAlphaType at) { 33 CGBitmapInfo info = kCGBitmapByteOrder32Little; 34 switch (at) { 35 case kUnknown_SkAlphaType: break; 36 case kOpaque_SkAlphaType: info |= kCGImageAlphaNoneSkipFirst; break; 37 case kPremul_SkAlphaType: info |= kCGImageAlphaPremultipliedFirst; break; 38 case kUnpremul_SkAlphaType: info |= kCGImageAlphaFirst; break; 39 } 40 return info; 41} 42static CGBitmapInfo compute_cgalpha_info_4444(SkAlphaType at) { 43 CGBitmapInfo info = kCGBitmapByteOrder16Little; 44 switch (at) { 45 case kOpaque_SkAlphaType: info |= kCGImageAlphaNoneSkipLast; break; 46 default: info |= kCGImageAlphaPremultipliedLast; break; 47 } 48 return info; 49} 50 51static bool get_bitmap_info(SkColorType skColorType, 52 SkAlphaType skAlphaType, 53 size_t* bitsPerComponent, 54 CGBitmapInfo* info, 55 bool* upscaleTo32) { 56 if (upscaleTo32) { 57 *upscaleTo32 = false; 58 } 59 switch (skColorType) { 60 case kRGB_565_SkColorType: 61 if (upscaleTo32) { 62 *upscaleTo32 = true; 63 } 64 // now treat like RGBA 65 *bitsPerComponent = 8; 66 *info = compute_cgalpha_info_rgba(kOpaque_SkAlphaType); 67 break; 68 case kRGBA_8888_SkColorType: 69 *bitsPerComponent = 8; 70 *info = compute_cgalpha_info_rgba(skAlphaType); 71 break; 72 case kBGRA_8888_SkColorType: 73 *bitsPerComponent = 8; 74 *info = compute_cgalpha_info_bgra(skAlphaType); 75 break; 76 case kARGB_4444_SkColorType: 77 *bitsPerComponent = 4; 78 *info = compute_cgalpha_info_4444(skAlphaType); 79 break; 80 default: 81 return false; 82 } 83 return true; 84} 85 86static std::unique_ptr<SkBitmap> prepare_for_image_ref(const SkBitmap& bm, 87 size_t* bitsPerComponent, 88 CGBitmapInfo* info) { 89 bool upscaleTo32; 90 if (!get_bitmap_info(bm.colorType(), bm.alphaType(), bitsPerComponent, info, &upscaleTo32)) { 91 return nullptr; 92 } 93 if (upscaleTo32) { 94 std::unique_ptr<SkBitmap> copy(new SkBitmap); 95 // here we make a deep copy of the pixels, since CG won't take our 96 // 565 directly, so we always go to RGBA 97 copy->allocPixels(bm.info().makeColorType(kRGBA_8888_SkColorType)); 98 bm.readPixels(copy->info(), copy->getPixels(), copy->rowBytes(), 0, 0); 99 return copy; 100 } 101 return std::make_unique<SkBitmap>(bm); 102} 103 104CGImageRef SkCreateCGImageRefWithColorspace(const SkBitmap& bm, 105 CGColorSpaceRef colorSpace) { 106 if (bm.drawsNothing()) { 107 return nullptr; 108 } 109 size_t bitsPerComponent SK_INIT_TO_AVOID_WARNING; 110 CGBitmapInfo info SK_INIT_TO_AVOID_WARNING; 111 112 std::unique_ptr<SkBitmap> bitmap = prepare_for_image_ref(bm, &bitsPerComponent, &info); 113 if (nullptr == bitmap) { 114 return nullptr; 115 } 116 117 SkPixmap pm = bitmap->pixmap(); // Copy bitmap info before releasing it. 118 const size_t s = bitmap->computeByteSize(); 119 void* pixels = bitmap->getPixels(); 120 121 // our provider "owns" the bitmap*, and will take care of deleting it 122 SkUniqueCFRef<CGDataProviderRef> dataRef(CGDataProviderCreateWithData( 123 bitmap.release(), pixels, s, 124 [](void* p, const void*, size_t) { delete reinterpret_cast<SkBitmap*>(p); })); 125 126 SkUniqueCFRef<CGColorSpaceRef> rgb; 127 if (nullptr == colorSpace) { 128 rgb.reset(CGColorSpaceCreateDeviceRGB()); 129 colorSpace = rgb.get(); 130 } 131 return CGImageCreate(pm.width(), pm.height(), bitsPerComponent, 132 pm.info().bytesPerPixel() * CHAR_BIT, pm.rowBytes(), colorSpace, 133 info, dataRef.get(), nullptr, false, kCGRenderingIntentDefault); 134} 135 136void SkCGDrawBitmap(CGContextRef cg, const SkBitmap& bm, float x, float y) { 137 SkUniqueCFRef<CGImageRef> img(SkCreateCGImageRef(bm)); 138 139 if (img) { 140 CGRect r = CGRectMake(0, 0, bm.width(), bm.height()); 141 142 CGContextSaveGState(cg); 143 CGContextTranslateCTM(cg, x, r.size.height + y); 144 CGContextScaleCTM(cg, 1, -1); 145 146 CGContextDrawImage(cg, r, img.get()); 147 148 CGContextRestoreGState(cg); 149 } 150} 151 152/////////////////////////////////////////////////////////////////////////////////////////////////// 153 154CGContextRef SkCreateCGContext(const SkPixmap& pmap) { 155 CGBitmapInfo cg_bitmap_info = 0; 156 size_t bitsPerComponent = 0; 157 switch (pmap.colorType()) { 158 case kRGBA_8888_SkColorType: 159 bitsPerComponent = 8; 160 cg_bitmap_info = compute_cgalpha_info_rgba(pmap.alphaType()); 161 break; 162 case kBGRA_8888_SkColorType: 163 bitsPerComponent = 8; 164 cg_bitmap_info = compute_cgalpha_info_bgra(pmap.alphaType()); 165 break; 166 default: 167 return nullptr; // no other colortypes are supported (for now) 168 } 169 170 size_t rb = pmap.addr() ? pmap.rowBytes() : 0; 171 SkUniqueCFRef<CGColorSpaceRef> cs(CGColorSpaceCreateDeviceRGB()); 172 CGContextRef cg = CGBitmapContextCreate(pmap.writable_addr(), pmap.width(), pmap.height(), 173 bitsPerComponent, rb, cs.get(), cg_bitmap_info); 174 return cg; 175} 176 177bool SkCopyPixelsFromCGImage(const SkImageInfo& info, size_t rowBytes, void* pixels, 178 CGImageRef image) { 179 CGBitmapInfo cg_bitmap_info = 0; 180 size_t bitsPerComponent = 0; 181 switch (info.colorType()) { 182 case kRGBA_8888_SkColorType: 183 bitsPerComponent = 8; 184 cg_bitmap_info = compute_cgalpha_info_rgba(info.alphaType()); 185 break; 186 case kBGRA_8888_SkColorType: 187 bitsPerComponent = 8; 188 cg_bitmap_info = compute_cgalpha_info_bgra(info.alphaType()); 189 break; 190 default: 191 return false; // no other colortypes are supported (for now) 192 } 193 194 SkUniqueCFRef<CGColorSpaceRef> cs(CGColorSpaceCreateDeviceRGB()); 195 SkUniqueCFRef<CGContextRef> cg(CGBitmapContextCreate( 196 pixels, info.width(), info.height(), bitsPerComponent, 197 rowBytes, cs.get(), cg_bitmap_info)); 198 if (!cg) { 199 return false; 200 } 201 202 // use this blend mode, to avoid having to erase the pixels first, and to avoid CG performing 203 // any blending (which could introduce errors and be slower). 204 CGContextSetBlendMode(cg.get(), kCGBlendModeCopy); 205 206 CGContextDrawImage(cg.get(), CGRectMake(0, 0, info.width(), info.height()), image); 207 return true; 208} 209 210bool SkCreateBitmapFromCGImage(SkBitmap* dst, CGImageRef image) { 211 const int width = SkToInt(CGImageGetWidth(image)); 212 const int height = SkToInt(CGImageGetHeight(image)); 213 SkImageInfo info = SkImageInfo::MakeN32Premul(width, height); 214 215 SkBitmap tmp; 216 if (!tmp.tryAllocPixels(info)) { 217 return false; 218 } 219 220 if (!SkCopyPixelsFromCGImage(tmp.info(), tmp.rowBytes(), tmp.getPixels(), image)) { 221 return false; 222 } 223 224 CGImageAlphaInfo cgInfo = CGImageGetAlphaInfo(image); 225 switch (cgInfo) { 226 case kCGImageAlphaNone: 227 case kCGImageAlphaNoneSkipLast: 228 case kCGImageAlphaNoneSkipFirst: 229 SkASSERT(SkBitmap::ComputeIsOpaque(tmp)); 230 tmp.setAlphaType(kOpaque_SkAlphaType); 231 break; 232 default: 233 // we don't know if we're opaque or not, so compute it. 234 if (SkBitmap::ComputeIsOpaque(tmp)) { 235 tmp.setAlphaType(kOpaque_SkAlphaType); 236 } 237 } 238 239 *dst = tmp; 240 return true; 241} 242 243sk_sp<SkImage> SkMakeImageFromCGImage(CGImageRef src) { 244 SkBitmap bm; 245 if (!SkCreateBitmapFromCGImage(&bm, src)) { 246 return nullptr; 247 } 248 249 bm.setImmutable(); 250 return bm.asImage(); 251} 252 253#endif//defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_IOS) 254