blob: ac2eb0e03de35685a551e33facf06fd16b86488f [file] [log] [blame]
epoger@google.comec3ed6a2011-07-28 14:26:00 +00001
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
reed@android.com0767e472008-12-23 16:06:51 +00009
reed@android.com0767e472008-12-23 16:06:51 +000010#include "SkImageDecoder.h"
reed@android.comb08eb2b2009-01-06 20:16:26 +000011#include "SkImageEncoder.h"
reed@android.com0767e472008-12-23 16:06:51 +000012#include "SkMovie.h"
13#include "SkStream.h"
14#include "SkTemplates.h"
reed@android.comad789ee2011-01-03 19:52:17 +000015#include "SkCGUtils.h"
reed@android.com0767e472008-12-23 16:06:51 +000016
yangsu@google.com900d8772011-06-24 18:56:00 +000017#ifdef SK_BUILD_FOR_MAC
18#include <ApplicationServices/ApplicationServices.h>
19#endif
20
21#ifdef SK_BUILD_FOR_IOS
22#include <CoreGraphics/CoreGraphics.h>
caryclark@google.com35f5ac92012-09-18 15:41:18 +000023#include <ImageIO/ImageIO.h>
24//#include <UTCoreTypes.h> // FIXME: hack -- can't figure out how to include this
25extern const CFStringRef kUTTypeJPEG __OSX_AVAILABLE_STARTING(__MAC_10_4,__IPHONE_3_0);
26extern const CFStringRef kUTTypePNG __OSX_AVAILABLE_STARTING(__MAC_10_4,__IPHONE_3_0);
yangsu@google.com900d8772011-06-24 18:56:00 +000027#endif
yangsu@google.comc134f392011-06-23 22:27:30 +000028
reed@android.com0767e472008-12-23 16:06:51 +000029static void malloc_release_proc(void* info, const void* data, size_t size) {
30 sk_free(info);
31}
32
33static CGDataProviderRef SkStreamToDataProvider(SkStream* stream) {
34 // TODO: use callbacks, so we don't have to load all the data into RAM
35 size_t len = stream->getLength();
36 void* data = sk_malloc_throw(len);
37 stream->read(data, len);
rmistry@google.comd6176b02012-08-23 18:14:13 +000038
reed@android.com0767e472008-12-23 16:06:51 +000039 return CGDataProviderCreateWithData(data, data, len, malloc_release_proc);
40}
41
42static CGImageSourceRef SkStreamToCGImageSource(SkStream* stream) {
43 CGDataProviderRef data = SkStreamToDataProvider(stream);
44 CGImageSourceRef imageSrc = CGImageSourceCreateWithDataProvider(data, 0);
45 CGDataProviderRelease(data);
46 return imageSrc;
47}
48
49class SkImageDecoder_CG : public SkImageDecoder {
50protected:
reed@android.com3f1f06a2010-03-03 21:04:12 +000051 virtual bool onDecode(SkStream* stream, SkBitmap* bm, Mode);
reed@android.com0767e472008-12-23 16:06:51 +000052};
53
54#define BITMAP_INFO (kCGBitmapByteOrder32Big | kCGImageAlphaPremultipliedLast)
55
reed@android.com3f1f06a2010-03-03 21:04:12 +000056bool SkImageDecoder_CG::onDecode(SkStream* stream, SkBitmap* bm, Mode mode) {
reed@android.com0767e472008-12-23 16:06:51 +000057 CGImageSourceRef imageSrc = SkStreamToCGImageSource(stream);
58
59 if (NULL == imageSrc) {
60 return false;
61 }
reed@android.com0767e472008-12-23 16:06:51 +000062 SkAutoTCallVProc<const void, CFRelease> arsrc(imageSrc);
rmistry@google.comd6176b02012-08-23 18:14:13 +000063
reed@android.com0767e472008-12-23 16:06:51 +000064 CGImageRef image = CGImageSourceCreateImageAtIndex(imageSrc, 0, NULL);
65 if (NULL == image) {
66 return false;
67 }
reed@android.com0767e472008-12-23 16:06:51 +000068 SkAutoTCallVProc<CGImage, CGImageRelease> arimage(image);
rmistry@google.comd6176b02012-08-23 18:14:13 +000069
reed@android.com0767e472008-12-23 16:06:51 +000070 const int width = CGImageGetWidth(image);
71 const int height = CGImageGetHeight(image);
72 bm->setConfig(SkBitmap::kARGB_8888_Config, width, height);
73 if (SkImageDecoder::kDecodeBounds_Mode == mode) {
74 return true;
75 }
rmistry@google.comd6176b02012-08-23 18:14:13 +000076
reed@android.com0767e472008-12-23 16:06:51 +000077 if (!this->allocPixelRef(bm, NULL)) {
78 return false;
79 }
rmistry@google.comd6176b02012-08-23 18:14:13 +000080
reed@android.com0767e472008-12-23 16:06:51 +000081 bm->lockPixels();
82 bm->eraseColor(0);
83
reed@google.comaf9d9c22011-06-15 17:33:29 +000084 // use the same colorspace, so we don't change the pixels at all
85 CGColorSpaceRef cs = CGImageGetColorSpace(image);
reed@android.com0767e472008-12-23 16:06:51 +000086 CGContextRef cg = CGBitmapContextCreate(bm->getPixels(), width, height,
87 8, bm->rowBytes(), cs, BITMAP_INFO);
88 CGContextDrawImage(cg, CGRectMake(0, 0, width, height), image);
89 CGContextRelease(cg);
reed@android.com0767e472008-12-23 16:06:51 +000090
91 bm->unlockPixels();
92 return true;
93}
94
95///////////////////////////////////////////////////////////////////////////////
96
97SkImageDecoder* SkImageDecoder::Factory(SkStream* stream) {
98 return SkNEW(SkImageDecoder_CG);
99}
100
reed@android.com0767e472008-12-23 16:06:51 +0000101/////////////////////////////////////////////////////////////////////////
102
103SkMovie* SkMovie::DecodeStream(SkStream* stream) {
104 return NULL;
105}
106
107/////////////////////////////////////////////////////////////////////////
108
reed@android.com0ae6b242008-12-23 16:49:54 +0000109static size_t consumer_put(void* info, const void* buffer, size_t count) {
110 SkWStream* stream = reinterpret_cast<SkWStream*>(info);
111 return stream->write(buffer, count) ? count : 0;
112}
113
114static void consumer_release(void* info) {
115 // we do nothing, since by design we don't "own" the stream (i.e. info)
116}
117
118static CGDataConsumerRef SkStreamToCGDataConsumer(SkWStream* stream) {
119 CGDataConsumerCallbacks procs;
120 procs.putBytes = consumer_put;
121 procs.releaseConsumer = consumer_release;
122 // we don't own/reference the stream, so it our consumer must not live
123 // longer that our caller's ownership of the stream
124 return CGDataConsumerCreate(stream, &procs);
125}
126
127static CGImageDestinationRef SkStreamToImageDestination(SkWStream* stream,
128 CFStringRef type) {
129 CGDataConsumerRef consumer = SkStreamToCGDataConsumer(stream);
130 if (NULL == consumer) {
131 return NULL;
132 }
133 SkAutoTCallVProc<const void, CFRelease> arconsumer(consumer);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000134
reed@android.com0ae6b242008-12-23 16:49:54 +0000135 return CGImageDestinationCreateWithDataConsumer(consumer, type, 1, NULL);
136}
137
138class SkImageEncoder_CG : public SkImageEncoder {
139public:
140 SkImageEncoder_CG(Type t) : fType(t) {}
141
142protected:
143 virtual bool onEncode(SkWStream* stream, const SkBitmap& bm, int quality);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000144
reed@android.com0ae6b242008-12-23 16:49:54 +0000145private:
146 Type fType;
147};
148
reed@android.com0ae6b242008-12-23 16:49:54 +0000149/* Encode bitmaps via CGImageDestination. We setup a DataConsumer which writes
150 to our SkWStream. Since we don't reference/own the SkWStream, our consumer
151 must only live for the duration of the onEncode() method.
152 */
153bool SkImageEncoder_CG::onEncode(SkWStream* stream, const SkBitmap& bm,
154 int quality) {
155 CFStringRef type;
156 switch (fType) {
157 case kJPEG_Type:
158 type = kUTTypeJPEG;
159 break;
160 case kPNG_Type:
161 type = kUTTypePNG;
162 break;
163 default:
164 return false;
165 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000166
reed@android.com0ae6b242008-12-23 16:49:54 +0000167 CGImageDestinationRef dst = SkStreamToImageDestination(stream, type);
168 if (NULL == dst) {
169 return false;
170 }
171 SkAutoTCallVProc<const void, CFRelease> ardst(dst);
172
173 CGImageRef image = SkCreateCGImageRef(bm);
174 if (NULL == image) {
175 return false;
176 }
177 SkAutoTCallVProc<CGImage, CGImageRelease> agimage(image);
epoger@google.com0928c4a2012-01-31 15:14:08 +0000178
179 CGImageDestinationAddImage(dst, image, NULL);
180 return CGImageDestinationFinalize(dst);
reed@android.com0ae6b242008-12-23 16:49:54 +0000181}
182
reed@android.com0767e472008-12-23 16:06:51 +0000183SkImageEncoder* SkImageEncoder::Create(Type t) {
reed@android.com0767e472008-12-23 16:06:51 +0000184 switch (t) {
185 case kJPEG_Type:
reed@android.com0767e472008-12-23 16:06:51 +0000186 case kPNG_Type:
reed@android.com0ae6b242008-12-23 16:49:54 +0000187 break;
reed@android.com0767e472008-12-23 16:06:51 +0000188 default:
189 return NULL;
190 }
reed@android.com0ae6b242008-12-23 16:49:54 +0000191 return SkNEW_ARGS(SkImageEncoder_CG, (t));
reed@android.com0767e472008-12-23 16:06:51 +0000192}
193