1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/gfx/skia/trunk/src/ports/SkImageDecoder_CG.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,295 @@ 1.4 +/* 1.5 + * Copyright 2008 The Android Open Source Project 1.6 + * 1.7 + * Use of this source code is governed by a BSD-style license that can be 1.8 + * found in the LICENSE file. 1.9 + */ 1.10 + 1.11 +#include "SkCGUtils.h" 1.12 +#include "SkColorPriv.h" 1.13 +#include "SkImageDecoder.h" 1.14 +#include "SkImageEncoder.h" 1.15 +#include "SkMovie.h" 1.16 +#include "SkStream.h" 1.17 +#include "SkStreamHelpers.h" 1.18 +#include "SkTemplates.h" 1.19 +#include "SkUnPreMultiply.h" 1.20 + 1.21 +#ifdef SK_BUILD_FOR_MAC 1.22 +#include <ApplicationServices/ApplicationServices.h> 1.23 +#endif 1.24 + 1.25 +#ifdef SK_BUILD_FOR_IOS 1.26 +#include <CoreGraphics/CoreGraphics.h> 1.27 +#include <ImageIO/ImageIO.h> 1.28 +#include <MobileCoreServices/MobileCoreServices.h> 1.29 +#endif 1.30 + 1.31 +static void malloc_release_proc(void* info, const void* data, size_t size) { 1.32 + sk_free(info); 1.33 +} 1.34 + 1.35 +static CGDataProviderRef SkStreamToDataProvider(SkStream* stream) { 1.36 + // TODO: use callbacks, so we don't have to load all the data into RAM 1.37 + SkAutoMalloc storage; 1.38 + const size_t len = CopyStreamToStorage(&storage, stream); 1.39 + void* data = storage.detach(); 1.40 + 1.41 + return CGDataProviderCreateWithData(data, data, len, malloc_release_proc); 1.42 +} 1.43 + 1.44 +static CGImageSourceRef SkStreamToCGImageSource(SkStream* stream) { 1.45 + CGDataProviderRef data = SkStreamToDataProvider(stream); 1.46 + CGImageSourceRef imageSrc = CGImageSourceCreateWithDataProvider(data, 0); 1.47 + CGDataProviderRelease(data); 1.48 + return imageSrc; 1.49 +} 1.50 + 1.51 +class SkImageDecoder_CG : public SkImageDecoder { 1.52 +protected: 1.53 + virtual bool onDecode(SkStream* stream, SkBitmap* bm, Mode); 1.54 +}; 1.55 + 1.56 +#define BITMAP_INFO (kCGBitmapByteOrder32Big | kCGImageAlphaPremultipliedLast) 1.57 + 1.58 +bool SkImageDecoder_CG::onDecode(SkStream* stream, SkBitmap* bm, Mode mode) { 1.59 + CGImageSourceRef imageSrc = SkStreamToCGImageSource(stream); 1.60 + 1.61 + if (NULL == imageSrc) { 1.62 + return false; 1.63 + } 1.64 + SkAutoTCallVProc<const void, CFRelease> arsrc(imageSrc); 1.65 + 1.66 + CGImageRef image = CGImageSourceCreateImageAtIndex(imageSrc, 0, NULL); 1.67 + if (NULL == image) { 1.68 + return false; 1.69 + } 1.70 + SkAutoTCallVProc<CGImage, CGImageRelease> arimage(image); 1.71 + 1.72 + const int width = SkToInt(CGImageGetWidth(image)); 1.73 + const int height = SkToInt(CGImageGetHeight(image)); 1.74 + bm->setConfig(SkBitmap::kARGB_8888_Config, width, height); 1.75 + if (SkImageDecoder::kDecodeBounds_Mode == mode) { 1.76 + return true; 1.77 + } 1.78 + 1.79 + if (!this->allocPixelRef(bm, NULL)) { 1.80 + return false; 1.81 + } 1.82 + 1.83 + bm->lockPixels(); 1.84 + bm->eraseColor(SK_ColorTRANSPARENT); 1.85 + 1.86 + CGColorSpaceRef cs = CGColorSpaceCreateDeviceRGB(); 1.87 + CGContextRef cg = CGBitmapContextCreate(bm->getPixels(), width, height, 8, bm->rowBytes(), cs, BITMAP_INFO); 1.88 + CFRelease(cs); 1.89 + 1.90 + CGContextDrawImage(cg, CGRectMake(0, 0, width, height), image); 1.91 + CGContextRelease(cg); 1.92 + 1.93 + CGImageAlphaInfo info = CGImageGetAlphaInfo(image); 1.94 + switch (info) { 1.95 + case kCGImageAlphaNone: 1.96 + case kCGImageAlphaNoneSkipLast: 1.97 + case kCGImageAlphaNoneSkipFirst: 1.98 + SkASSERT(SkBitmap::ComputeIsOpaque(*bm)); 1.99 + bm->setAlphaType(kOpaque_SkAlphaType); 1.100 + break; 1.101 + default: 1.102 + // we don't know if we're opaque or not, so compute it. 1.103 + if (SkBitmap::ComputeIsOpaque(*bm)) { 1.104 + bm->setAlphaType(kOpaque_SkAlphaType); 1.105 + } 1.106 + } 1.107 + if (!bm->isOpaque() && this->getRequireUnpremultipliedColors()) { 1.108 + // CGBitmapContext does not support unpremultiplied, so the image has been premultiplied. 1.109 + // Convert to unpremultiplied. 1.110 + for (int i = 0; i < width; ++i) { 1.111 + for (int j = 0; j < height; ++j) { 1.112 + uint32_t* addr = bm->getAddr32(i, j); 1.113 + *addr = SkUnPreMultiply::UnPreMultiplyPreservingByteOrder(*addr); 1.114 + } 1.115 + } 1.116 + bm->setAlphaType(kUnpremul_SkAlphaType); 1.117 + } 1.118 + bm->unlockPixels(); 1.119 + return true; 1.120 +} 1.121 + 1.122 +/////////////////////////////////////////////////////////////////////////////// 1.123 + 1.124 +extern SkImageDecoder* image_decoder_from_stream(SkStreamRewindable*); 1.125 + 1.126 +SkImageDecoder* SkImageDecoder::Factory(SkStreamRewindable* stream) { 1.127 + SkImageDecoder* decoder = image_decoder_from_stream(stream); 1.128 + if (NULL == decoder) { 1.129 + // If no image decoder specific to the stream exists, use SkImageDecoder_CG. 1.130 + return SkNEW(SkImageDecoder_CG); 1.131 + } else { 1.132 + return decoder; 1.133 + } 1.134 +} 1.135 + 1.136 +///////////////////////////////////////////////////////////////////////// 1.137 + 1.138 +SkMovie* SkMovie::DecodeStream(SkStreamRewindable* stream) { 1.139 + return NULL; 1.140 +} 1.141 + 1.142 +///////////////////////////////////////////////////////////////////////// 1.143 + 1.144 +static size_t consumer_put(void* info, const void* buffer, size_t count) { 1.145 + SkWStream* stream = reinterpret_cast<SkWStream*>(info); 1.146 + return stream->write(buffer, count) ? count : 0; 1.147 +} 1.148 + 1.149 +static void consumer_release(void* info) { 1.150 + // we do nothing, since by design we don't "own" the stream (i.e. info) 1.151 +} 1.152 + 1.153 +static CGDataConsumerRef SkStreamToCGDataConsumer(SkWStream* stream) { 1.154 + CGDataConsumerCallbacks procs; 1.155 + procs.putBytes = consumer_put; 1.156 + procs.releaseConsumer = consumer_release; 1.157 + // we don't own/reference the stream, so it our consumer must not live 1.158 + // longer that our caller's ownership of the stream 1.159 + return CGDataConsumerCreate(stream, &procs); 1.160 +} 1.161 + 1.162 +static CGImageDestinationRef SkStreamToImageDestination(SkWStream* stream, 1.163 + CFStringRef type) { 1.164 + CGDataConsumerRef consumer = SkStreamToCGDataConsumer(stream); 1.165 + if (NULL == consumer) { 1.166 + return NULL; 1.167 + } 1.168 + SkAutoTCallVProc<const void, CFRelease> arconsumer(consumer); 1.169 + 1.170 + return CGImageDestinationCreateWithDataConsumer(consumer, type, 1, NULL); 1.171 +} 1.172 + 1.173 +class SkImageEncoder_CG : public SkImageEncoder { 1.174 +public: 1.175 + SkImageEncoder_CG(Type t) : fType(t) {} 1.176 + 1.177 +protected: 1.178 + virtual bool onEncode(SkWStream* stream, const SkBitmap& bm, int quality); 1.179 + 1.180 +private: 1.181 + Type fType; 1.182 +}; 1.183 + 1.184 +/* Encode bitmaps via CGImageDestination. We setup a DataConsumer which writes 1.185 + to our SkWStream. Since we don't reference/own the SkWStream, our consumer 1.186 + must only live for the duration of the onEncode() method. 1.187 + */ 1.188 +bool SkImageEncoder_CG::onEncode(SkWStream* stream, const SkBitmap& bm, 1.189 + int quality) { 1.190 + // Used for converting a bitmap to 8888. 1.191 + const SkBitmap* bmPtr = &bm; 1.192 + SkBitmap bitmap8888; 1.193 + 1.194 + CFStringRef type; 1.195 + switch (fType) { 1.196 + case kICO_Type: 1.197 + type = kUTTypeICO; 1.198 + break; 1.199 + case kBMP_Type: 1.200 + type = kUTTypeBMP; 1.201 + break; 1.202 + case kGIF_Type: 1.203 + type = kUTTypeGIF; 1.204 + break; 1.205 + case kJPEG_Type: 1.206 + type = kUTTypeJPEG; 1.207 + break; 1.208 + case kPNG_Type: 1.209 + // PNG encoding an ARGB_4444 bitmap gives the following errors in GM: 1.210 + // <Error>: CGImageDestinationAddImage image could not be converted to destination 1.211 + // format. 1.212 + // <Error>: CGImageDestinationFinalize image destination does not have enough images 1.213 + // So instead we copy to 8888. 1.214 + if (bm.colorType() == kARGB_4444_SkColorType) { 1.215 + bm.copyTo(&bitmap8888, kPMColor_SkColorType); 1.216 + bmPtr = &bitmap8888; 1.217 + } 1.218 + type = kUTTypePNG; 1.219 + break; 1.220 + default: 1.221 + return false; 1.222 + } 1.223 + 1.224 + CGImageDestinationRef dst = SkStreamToImageDestination(stream, type); 1.225 + if (NULL == dst) { 1.226 + return false; 1.227 + } 1.228 + SkAutoTCallVProc<const void, CFRelease> ardst(dst); 1.229 + 1.230 + CGImageRef image = SkCreateCGImageRef(*bmPtr); 1.231 + if (NULL == image) { 1.232 + return false; 1.233 + } 1.234 + SkAutoTCallVProc<CGImage, CGImageRelease> agimage(image); 1.235 + 1.236 + CGImageDestinationAddImage(dst, image, NULL); 1.237 + return CGImageDestinationFinalize(dst); 1.238 +} 1.239 + 1.240 +/////////////////////////////////////////////////////////////////////////////// 1.241 + 1.242 +static SkImageEncoder* sk_imageencoder_cg_factory(SkImageEncoder::Type t) { 1.243 + switch (t) { 1.244 + case SkImageEncoder::kICO_Type: 1.245 + case SkImageEncoder::kBMP_Type: 1.246 + case SkImageEncoder::kGIF_Type: 1.247 + case SkImageEncoder::kJPEG_Type: 1.248 + case SkImageEncoder::kPNG_Type: 1.249 + break; 1.250 + default: 1.251 + return NULL; 1.252 + } 1.253 + return SkNEW_ARGS(SkImageEncoder_CG, (t)); 1.254 +} 1.255 + 1.256 +static SkImageEncoder_EncodeReg gEReg(sk_imageencoder_cg_factory); 1.257 + 1.258 +struct FormatConversion { 1.259 + CFStringRef fUTType; 1.260 + SkImageDecoder::Format fFormat; 1.261 +}; 1.262 + 1.263 +// Array of the types supported by the decoder. 1.264 +static const FormatConversion gFormatConversions[] = { 1.265 + { kUTTypeBMP, SkImageDecoder::kBMP_Format }, 1.266 + { kUTTypeGIF, SkImageDecoder::kGIF_Format }, 1.267 + { kUTTypeICO, SkImageDecoder::kICO_Format }, 1.268 + { kUTTypeJPEG, SkImageDecoder::kJPEG_Format }, 1.269 + // Also include JPEG2000 1.270 + { kUTTypeJPEG2000, SkImageDecoder::kJPEG_Format }, 1.271 + { kUTTypePNG, SkImageDecoder::kPNG_Format }, 1.272 +}; 1.273 + 1.274 +static SkImageDecoder::Format UTType_to_Format(const CFStringRef uttype) { 1.275 + for (size_t i = 0; i < SK_ARRAY_COUNT(gFormatConversions); i++) { 1.276 + if (CFStringCompare(uttype, gFormatConversions[i].fUTType, 0) == kCFCompareEqualTo) { 1.277 + return gFormatConversions[i].fFormat; 1.278 + } 1.279 + } 1.280 + return SkImageDecoder::kUnknown_Format; 1.281 +} 1.282 + 1.283 +static SkImageDecoder::Format get_format_cg(SkStreamRewindable* stream) { 1.284 + CGImageSourceRef imageSrc = SkStreamToCGImageSource(stream); 1.285 + 1.286 + if (NULL == imageSrc) { 1.287 + return SkImageDecoder::kUnknown_Format; 1.288 + } 1.289 + 1.290 + SkAutoTCallVProc<const void, CFRelease> arsrc(imageSrc); 1.291 + const CFStringRef name = CGImageSourceGetType(imageSrc); 1.292 + if (NULL == name) { 1.293 + return SkImageDecoder::kUnknown_Format; 1.294 + } 1.295 + return UTType_to_Format(name); 1.296 +} 1.297 + 1.298 +static SkImageDecoder_FormatReg gFormatReg(get_format_cg);