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 25 static void malloc_release_proc(void* info, const void* data, size_t size) { 26 sk_free(info); 27 } 28 29 static 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 38 static CGImageSourceRef SkStreamToCGImageSource(SkStream* stream) { 39 CGDataProviderRef data = SkStreamToDataProvider(stream); 40 CGImageSourceRef imageSrc = CGImageSourceCreateWithDataProvider(data, 0); 41 CGDataProviderRelease(data); 42 return imageSrc; 43 } 44 45 class SkImageDecoder_CG : public SkImageDecoder { 46 protected: 47 virtual bool onDecode(SkStream* stream, SkBitmap* bm, Mode); 48 }; 49 50 #define BITMAP_INFO (kCGBitmapByteOrder32Big | kCGImageAlphaPremultipliedLast) 51 52 bool 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 93 SkImageDecoder* SkImageDecoder::Factory(SkStream* stream) { 94 return SkNEW(SkImageDecoder_CG); 95 } 96 97 ///////////////////////////////////////////////////////////////////////// 98 99 SkMovie* SkMovie::DecodeStream(SkStream* stream) { 100 return NULL; 101 } 102 103 ///////////////////////////////////////////////////////////////////////// 104 105 static 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 110 static void consumer_release(void* info) { 111 // we do nothing, since by design we don't "own" the stream (i.e. info) 112 } 113 114 static 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 123 static 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 134 class SkImageEncoder_CG : public SkImageEncoder { 135 public: 136 SkImageEncoder_CG(Type t) : fType(t) {} 137 138 protected: 139 virtual bool onEncode(SkWStream* stream, const SkBitmap& bm, int quality); 140 141 private: 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 */ 149 bool 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 179 SkImageEncoder* 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