Home | History | Annotate | Download | only in ports
      1 /*
      2  * Copyright 2008 The Android Open Source Project
      3  *
      4  * Use of this source code is governed by a BSD-style license that can be
      5  * found in the LICENSE file.
      6  */
      7 
      8 #include "SkCGUtils.h"
      9 #include "SkColorPriv.h"
     10 #include "SkImageDecoder.h"
     11 #include "SkImageEncoder.h"
     12 #include "SkMovie.h"
     13 #include "SkStream.h"
     14 #include "SkStreamHelpers.h"
     15 #include "SkTemplates.h"
     16 #include "SkUnPreMultiply.h"
     17 
     18 #ifdef SK_BUILD_FOR_MAC
     19 #include <ApplicationServices/ApplicationServices.h>
     20 #endif
     21 
     22 #ifdef SK_BUILD_FOR_IOS
     23 #include <CoreGraphics/CoreGraphics.h>
     24 #include <ImageIO/ImageIO.h>
     25 #include <MobileCoreServices/MobileCoreServices.h>
     26 #endif
     27 
     28 static void malloc_release_proc(void* info, const void* data, size_t size) {
     29     sk_free(info);
     30 }
     31 
     32 static CGDataProviderRef SkStreamToDataProvider(SkStream* stream) {
     33     // TODO: use callbacks, so we don't have to load all the data into RAM
     34     SkAutoMalloc storage;
     35     const size_t len = CopyStreamToStorage(&storage, stream);
     36     void* data = storage.detach();
     37 
     38     return CGDataProviderCreateWithData(data, data, len, malloc_release_proc);
     39 }
     40 
     41 static CGImageSourceRef SkStreamToCGImageSource(SkStream* stream) {
     42     CGDataProviderRef data = SkStreamToDataProvider(stream);
     43     CGImageSourceRef imageSrc = CGImageSourceCreateWithDataProvider(data, 0);
     44     CGDataProviderRelease(data);
     45     return imageSrc;
     46 }
     47 
     48 class SkImageDecoder_CG : public SkImageDecoder {
     49 protected:
     50     virtual bool onDecode(SkStream* stream, SkBitmap* bm, Mode);
     51 };
     52 
     53 // Returns an unpremultiplied version of color. It will have the same ordering and size as an
     54 // SkPMColor, but the alpha will not be premultiplied.
     55 static SkPMColor unpremultiply_pmcolor(SkPMColor color) {
     56     U8CPU a = SkGetPackedA32(color);
     57     const SkUnPreMultiply::Scale scale = SkUnPreMultiply::GetScale(a);
     58     return SkPackARGB32NoCheck(a,
     59                                SkUnPreMultiply::ApplyScale(scale, SkGetPackedR32(color)),
     60                                SkUnPreMultiply::ApplyScale(scale, SkGetPackedG32(color)),
     61                                SkUnPreMultiply::ApplyScale(scale, SkGetPackedB32(color)));
     62 }
     63 
     64 #define BITMAP_INFO (kCGBitmapByteOrder32Big | kCGImageAlphaPremultipliedLast)
     65 
     66 bool SkImageDecoder_CG::onDecode(SkStream* stream, SkBitmap* bm, Mode mode) {
     67     CGImageSourceRef imageSrc = SkStreamToCGImageSource(stream);
     68 
     69     if (NULL == imageSrc) {
     70         return false;
     71     }
     72     SkAutoTCallVProc<const void, CFRelease> arsrc(imageSrc);
     73 
     74     CGImageRef image = CGImageSourceCreateImageAtIndex(imageSrc, 0, NULL);
     75     if (NULL == image) {
     76         return false;
     77     }
     78     SkAutoTCallVProc<CGImage, CGImageRelease> arimage(image);
     79 
     80     const int width = CGImageGetWidth(image);
     81     const int height = CGImageGetHeight(image);
     82     bm->setConfig(SkBitmap::kARGB_8888_Config, width, height);
     83     if (SkImageDecoder::kDecodeBounds_Mode == mode) {
     84         return true;
     85     }
     86 
     87     if (!this->allocPixelRef(bm, NULL)) {
     88         return false;
     89     }
     90 
     91     bm->lockPixels();
     92     bm->eraseColor(SK_ColorTRANSPARENT);
     93 
     94     CGColorSpaceRef cs = CGColorSpaceCreateDeviceRGB();
     95     CGContextRef cg = CGBitmapContextCreate(bm->getPixels(), width, height, 8, bm->rowBytes(), cs, BITMAP_INFO);
     96     CFRelease(cs);
     97 
     98     CGContextDrawImage(cg, CGRectMake(0, 0, width, height), image);
     99     CGContextRelease(cg);
    100 
    101     CGImageAlphaInfo info = CGImageGetAlphaInfo(image);
    102     switch (info) {
    103         case kCGImageAlphaNone:
    104         case kCGImageAlphaNoneSkipLast:
    105         case kCGImageAlphaNoneSkipFirst:
    106             SkASSERT(SkBitmap::ComputeIsOpaque(*bm));
    107             bm->setAlphaType(kOpaque_SkAlphaType);
    108             break;
    109         default:
    110             // we don't know if we're opaque or not, so compute it.
    111             if (SkBitmap::ComputeIsOpaque(*bm)) {
    112                 bm->setAlphaType(kOpaque_SkAlphaType);
    113             }
    114     }
    115     if (!bm->isOpaque() && this->getRequireUnpremultipliedColors()) {
    116         // CGBitmapContext does not support unpremultiplied, so the image has been premultiplied.
    117         // Convert to unpremultiplied.
    118         for (int i = 0; i < width; ++i) {
    119             for (int j = 0; j < height; ++j) {
    120                 uint32_t* addr = bm->getAddr32(i, j);
    121                 *addr = unpremultiply_pmcolor(*addr);
    122             }
    123         }
    124         bm->setAlphaType(kUnpremul_SkAlphaType);
    125     }
    126     bm->unlockPixels();
    127     return true;
    128 }
    129 
    130 ///////////////////////////////////////////////////////////////////////////////
    131 
    132 extern SkImageDecoder* image_decoder_from_stream(SkStreamRewindable*);
    133 
    134 SkImageDecoder* SkImageDecoder::Factory(SkStreamRewindable* stream) {
    135     SkImageDecoder* decoder = image_decoder_from_stream(stream);
    136     if (NULL == decoder) {
    137         // If no image decoder specific to the stream exists, use SkImageDecoder_CG.
    138         return SkNEW(SkImageDecoder_CG);
    139     } else {
    140         return decoder;
    141     }
    142 }
    143 
    144 /////////////////////////////////////////////////////////////////////////
    145 
    146 SkMovie* SkMovie::DecodeStream(SkStreamRewindable* stream) {
    147     return NULL;
    148 }
    149 
    150 /////////////////////////////////////////////////////////////////////////
    151 
    152 static size_t consumer_put(void* info, const void* buffer, size_t count) {
    153     SkWStream* stream = reinterpret_cast<SkWStream*>(info);
    154     return stream->write(buffer, count) ? count : 0;
    155 }
    156 
    157 static void consumer_release(void* info) {
    158     // we do nothing, since by design we don't "own" the stream (i.e. info)
    159 }
    160 
    161 static CGDataConsumerRef SkStreamToCGDataConsumer(SkWStream* stream) {
    162     CGDataConsumerCallbacks procs;
    163     procs.putBytes = consumer_put;
    164     procs.releaseConsumer = consumer_release;
    165     // we don't own/reference the stream, so it our consumer must not live
    166     // longer that our caller's ownership of the stream
    167     return CGDataConsumerCreate(stream, &procs);
    168 }
    169 
    170 static CGImageDestinationRef SkStreamToImageDestination(SkWStream* stream,
    171                                                         CFStringRef type) {
    172     CGDataConsumerRef consumer = SkStreamToCGDataConsumer(stream);
    173     if (NULL == consumer) {
    174         return NULL;
    175     }
    176     SkAutoTCallVProc<const void, CFRelease> arconsumer(consumer);
    177 
    178     return CGImageDestinationCreateWithDataConsumer(consumer, type, 1, NULL);
    179 }
    180 
    181 class SkImageEncoder_CG : public SkImageEncoder {
    182 public:
    183     SkImageEncoder_CG(Type t) : fType(t) {}
    184 
    185 protected:
    186     virtual bool onEncode(SkWStream* stream, const SkBitmap& bm, int quality);
    187 
    188 private:
    189     Type fType;
    190 };
    191 
    192 /*  Encode bitmaps via CGImageDestination. We setup a DataConsumer which writes
    193     to our SkWStream. Since we don't reference/own the SkWStream, our consumer
    194     must only live for the duration of the onEncode() method.
    195  */
    196 bool SkImageEncoder_CG::onEncode(SkWStream* stream, const SkBitmap& bm,
    197                                  int quality) {
    198     // Used for converting a bitmap to 8888.
    199     const SkBitmap* bmPtr = &bm;
    200     SkBitmap bitmap8888;
    201 
    202     CFStringRef type;
    203     switch (fType) {
    204         case kICO_Type:
    205             type = kUTTypeICO;
    206             break;
    207         case kBMP_Type:
    208             type = kUTTypeBMP;
    209             break;
    210         case kGIF_Type:
    211             type = kUTTypeGIF;
    212             break;
    213         case kJPEG_Type:
    214             type = kUTTypeJPEG;
    215             break;
    216         case kPNG_Type:
    217             // PNG encoding an ARGB_4444 bitmap gives the following errors in GM:
    218             // <Error>: CGImageDestinationAddImage image could not be converted to destination
    219             // format.
    220             // <Error>: CGImageDestinationFinalize image destination does not have enough images
    221             // So instead we copy to 8888.
    222             if (bm.config() == SkBitmap::kARGB_4444_Config) {
    223                 bm.copyTo(&bitmap8888, SkBitmap::kARGB_8888_Config);
    224                 bmPtr = &bitmap8888;
    225             }
    226             type = kUTTypePNG;
    227             break;
    228         default:
    229             return false;
    230     }
    231 
    232     CGImageDestinationRef dst = SkStreamToImageDestination(stream, type);
    233     if (NULL == dst) {
    234         return false;
    235     }
    236     SkAutoTCallVProc<const void, CFRelease> ardst(dst);
    237 
    238     CGImageRef image = SkCreateCGImageRef(*bmPtr);
    239     if (NULL == image) {
    240         return false;
    241     }
    242     SkAutoTCallVProc<CGImage, CGImageRelease> agimage(image);
    243 
    244     CGImageDestinationAddImage(dst, image, NULL);
    245     return CGImageDestinationFinalize(dst);
    246 }
    247 
    248 ///////////////////////////////////////////////////////////////////////////////
    249 
    250 static SkImageEncoder* sk_imageencoder_cg_factory(SkImageEncoder::Type t) {
    251     switch (t) {
    252         case SkImageEncoder::kICO_Type:
    253         case SkImageEncoder::kBMP_Type:
    254         case SkImageEncoder::kGIF_Type:
    255         case SkImageEncoder::kJPEG_Type:
    256         case SkImageEncoder::kPNG_Type:
    257             break;
    258         default:
    259             return NULL;
    260     }
    261     return SkNEW_ARGS(SkImageEncoder_CG, (t));
    262 }
    263 
    264 static SkImageEncoder_EncodeReg gEReg(sk_imageencoder_cg_factory);
    265 
    266 struct FormatConversion {
    267     CFStringRef             fUTType;
    268     SkImageDecoder::Format  fFormat;
    269 };
    270 
    271 // Array of the types supported by the decoder.
    272 static const FormatConversion gFormatConversions[] = {
    273     { kUTTypeBMP, SkImageDecoder::kBMP_Format },
    274     { kUTTypeGIF, SkImageDecoder::kGIF_Format },
    275     { kUTTypeICO, SkImageDecoder::kICO_Format },
    276     { kUTTypeJPEG, SkImageDecoder::kJPEG_Format },
    277     // Also include JPEG2000
    278     { kUTTypeJPEG2000, SkImageDecoder::kJPEG_Format },
    279     { kUTTypePNG, SkImageDecoder::kPNG_Format },
    280 };
    281 
    282 static SkImageDecoder::Format UTType_to_Format(const CFStringRef uttype) {
    283     for (size_t i = 0; i < SK_ARRAY_COUNT(gFormatConversions); i++) {
    284         if (CFStringCompare(uttype, gFormatConversions[i].fUTType, 0) == kCFCompareEqualTo) {
    285             return gFormatConversions[i].fFormat;
    286         }
    287     }
    288     return SkImageDecoder::kUnknown_Format;
    289 }
    290 
    291 static SkImageDecoder::Format get_format_cg(SkStreamRewindable* stream) {
    292     CGImageSourceRef imageSrc = SkStreamToCGImageSource(stream);
    293 
    294     if (NULL == imageSrc) {
    295         return SkImageDecoder::kUnknown_Format;
    296     }
    297 
    298     SkAutoTCallVProc<const void, CFRelease> arsrc(imageSrc);
    299     const CFStringRef name = CGImageSourceGetType(imageSrc);
    300     if (NULL == name) {
    301         return SkImageDecoder::kUnknown_Format;
    302     }
    303     return UTType_to_Format(name);
    304 }
    305 
    306 static SkImageDecoder_FormatReg gFormatReg(get_format_cg);
    307