1 2/* 3 * Copyright 2008 The Android Open Source Project 4 * 5 * Use of this source code is governed by a BSD-style license that can be 6 * found in the LICENSE file. 7 */ 8 9 10#include "SkImageDecoder.h" 11#include "SkImageEncoder.h" 12#include "SkMovie.h" 13#include "SkStream.h" 14#include "SkTemplates.h" 15#include "SkCGUtils.h" 16 17#ifdef SK_BUILD_FOR_MAC 18#include <ApplicationServices/ApplicationServices.h> 19#endif 20 21#ifdef SK_BUILD_FOR_IOS 22#include <CoreGraphics/CoreGraphics.h> 23#endif 24 25static void malloc_release_proc(void* info, const void* data, size_t size) { 26 sk_free(info); 27} 28 29static CGDataProviderRef SkStreamToDataProvider(SkStream* stream) { 30 // TODO: use callbacks, so we don't have to load all the data into RAM 31 size_t len = stream->getLength(); 32 void* data = sk_malloc_throw(len); 33 stream->read(data, len); 34 35 return CGDataProviderCreateWithData(data, data, len, malloc_release_proc); 36} 37 38static CGImageSourceRef SkStreamToCGImageSource(SkStream* stream) { 39 CGDataProviderRef data = SkStreamToDataProvider(stream); 40 CGImageSourceRef imageSrc = CGImageSourceCreateWithDataProvider(data, 0); 41 CGDataProviderRelease(data); 42 return imageSrc; 43} 44 45class SkImageDecoder_CG : public SkImageDecoder { 46protected: 47 virtual bool onDecode(SkStream* stream, SkBitmap* bm, Mode); 48}; 49 50#define BITMAP_INFO (kCGBitmapByteOrder32Big | kCGImageAlphaPremultipliedLast) 51 52bool SkImageDecoder_CG::onDecode(SkStream* stream, SkBitmap* bm, Mode mode) { 53 CGImageSourceRef imageSrc = SkStreamToCGImageSource(stream); 54 55 if (NULL == imageSrc) { 56 return false; 57 } 58 SkAutoTCallVProc<const void, CFRelease> arsrc(imageSrc); 59 60 CGImageRef image = CGImageSourceCreateImageAtIndex(imageSrc, 0, NULL); 61 if (NULL == image) { 62 return false; 63 } 64 SkAutoTCallVProc<CGImage, CGImageRelease> arimage(image); 65 66 const int width = CGImageGetWidth(image); 67 const int height = CGImageGetHeight(image); 68 bm->setConfig(SkBitmap::kARGB_8888_Config, width, height); 69 if (SkImageDecoder::kDecodeBounds_Mode == mode) { 70 return true; 71 } 72 73 if (!this->allocPixelRef(bm, NULL)) { 74 return false; 75 } 76 77 bm->lockPixels(); 78 bm->eraseColor(0); 79 80 // use the same colorspace, so we don't change the pixels at all 81 CGColorSpaceRef cs = CGImageGetColorSpace(image); 82 CGContextRef cg = CGBitmapContextCreate(bm->getPixels(), width, height, 83 8, bm->rowBytes(), cs, BITMAP_INFO); 84 CGContextDrawImage(cg, CGRectMake(0, 0, width, height), image); 85 CGContextRelease(cg); 86 87 bm->unlockPixels(); 88 return true; 89} 90 91/////////////////////////////////////////////////////////////////////////////// 92 93SkImageDecoder* SkImageDecoder::Factory(SkStream* stream) { 94 return SkNEW(SkImageDecoder_CG); 95} 96 97///////////////////////////////////////////////////////////////////////// 98 99SkMovie* SkMovie::DecodeStream(SkStream* stream) { 100 return NULL; 101} 102 103///////////////////////////////////////////////////////////////////////// 104 105static size_t consumer_put(void* info, const void* buffer, size_t count) { 106 SkWStream* stream = reinterpret_cast<SkWStream*>(info); 107 return stream->write(buffer, count) ? count : 0; 108} 109 110static void consumer_release(void* info) { 111 // we do nothing, since by design we don't "own" the stream (i.e. info) 112} 113 114static CGDataConsumerRef SkStreamToCGDataConsumer(SkWStream* stream) { 115 CGDataConsumerCallbacks procs; 116 procs.putBytes = consumer_put; 117 procs.releaseConsumer = consumer_release; 118 // we don't own/reference the stream, so it our consumer must not live 119 // longer that our caller's ownership of the stream 120 return CGDataConsumerCreate(stream, &procs); 121} 122 123static CGImageDestinationRef SkStreamToImageDestination(SkWStream* stream, 124 CFStringRef type) { 125 CGDataConsumerRef consumer = SkStreamToCGDataConsumer(stream); 126 if (NULL == consumer) { 127 return NULL; 128 } 129 SkAutoTCallVProc<const void, CFRelease> arconsumer(consumer); 130 131 return CGImageDestinationCreateWithDataConsumer(consumer, type, 1, NULL); 132} 133 134class SkImageEncoder_CG : public SkImageEncoder { 135public: 136 SkImageEncoder_CG(Type t) : fType(t) {} 137 138protected: 139 virtual bool onEncode(SkWStream* stream, const SkBitmap& bm, int quality); 140 141private: 142 Type fType; 143}; 144 145/* Encode bitmaps via CGImageDestination. We setup a DataConsumer which writes 146 to our SkWStream. Since we don't reference/own the SkWStream, our consumer 147 must only live for the duration of the onEncode() method. 148 */ 149bool SkImageEncoder_CG::onEncode(SkWStream* stream, const SkBitmap& bm, 150 int quality) { 151 CFStringRef type; 152 switch (fType) { 153 case kJPEG_Type: 154 type = kUTTypeJPEG; 155 break; 156 case kPNG_Type: 157 type = kUTTypePNG; 158 break; 159 default: 160 return false; 161 } 162 163 CGImageDestinationRef dst = SkStreamToImageDestination(stream, type); 164 if (NULL == dst) { 165 return false; 166 } 167 SkAutoTCallVProc<const void, CFRelease> ardst(dst); 168 169 CGImageRef image = SkCreateCGImageRef(bm); 170 if (NULL == image) { 171 return false; 172 } 173 SkAutoTCallVProc<CGImage, CGImageRelease> agimage(image); 174 175 CGImageDestinationAddImage(dst, image, NULL); 176 return CGImageDestinationFinalize(dst); 177} 178 179SkImageEncoder* SkImageEncoder::Create(Type t) { 180 switch (t) { 181 case kJPEG_Type: 182 case kPNG_Type: 183 break; 184 default: 185 return NULL; 186 } 187 return SkNEW_ARGS(SkImageEncoder_CG, (t)); 188} 189 190