Home | History | Annotate | Download | only in ports
      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