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