gfx/skia/trunk/src/ports/SkImageDecoder_CG.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/gfx/skia/trunk/src/ports/SkImageDecoder_CG.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,295 @@
     1.4 +/*
     1.5 + * Copyright 2008 The Android Open Source Project
     1.6 + *
     1.7 + * Use of this source code is governed by a BSD-style license that can be
     1.8 + * found in the LICENSE file.
     1.9 + */
    1.10 +
    1.11 +#include "SkCGUtils.h"
    1.12 +#include "SkColorPriv.h"
    1.13 +#include "SkImageDecoder.h"
    1.14 +#include "SkImageEncoder.h"
    1.15 +#include "SkMovie.h"
    1.16 +#include "SkStream.h"
    1.17 +#include "SkStreamHelpers.h"
    1.18 +#include "SkTemplates.h"
    1.19 +#include "SkUnPreMultiply.h"
    1.20 +
    1.21 +#ifdef SK_BUILD_FOR_MAC
    1.22 +#include <ApplicationServices/ApplicationServices.h>
    1.23 +#endif
    1.24 +
    1.25 +#ifdef SK_BUILD_FOR_IOS
    1.26 +#include <CoreGraphics/CoreGraphics.h>
    1.27 +#include <ImageIO/ImageIO.h>
    1.28 +#include <MobileCoreServices/MobileCoreServices.h>
    1.29 +#endif
    1.30 +
    1.31 +static void malloc_release_proc(void* info, const void* data, size_t size) {
    1.32 +    sk_free(info);
    1.33 +}
    1.34 +
    1.35 +static CGDataProviderRef SkStreamToDataProvider(SkStream* stream) {
    1.36 +    // TODO: use callbacks, so we don't have to load all the data into RAM
    1.37 +    SkAutoMalloc storage;
    1.38 +    const size_t len = CopyStreamToStorage(&storage, stream);
    1.39 +    void* data = storage.detach();
    1.40 +
    1.41 +    return CGDataProviderCreateWithData(data, data, len, malloc_release_proc);
    1.42 +}
    1.43 +
    1.44 +static CGImageSourceRef SkStreamToCGImageSource(SkStream* stream) {
    1.45 +    CGDataProviderRef data = SkStreamToDataProvider(stream);
    1.46 +    CGImageSourceRef imageSrc = CGImageSourceCreateWithDataProvider(data, 0);
    1.47 +    CGDataProviderRelease(data);
    1.48 +    return imageSrc;
    1.49 +}
    1.50 +
    1.51 +class SkImageDecoder_CG : public SkImageDecoder {
    1.52 +protected:
    1.53 +    virtual bool onDecode(SkStream* stream, SkBitmap* bm, Mode);
    1.54 +};
    1.55 +
    1.56 +#define BITMAP_INFO (kCGBitmapByteOrder32Big | kCGImageAlphaPremultipliedLast)
    1.57 +
    1.58 +bool SkImageDecoder_CG::onDecode(SkStream* stream, SkBitmap* bm, Mode mode) {
    1.59 +    CGImageSourceRef imageSrc = SkStreamToCGImageSource(stream);
    1.60 +
    1.61 +    if (NULL == imageSrc) {
    1.62 +        return false;
    1.63 +    }
    1.64 +    SkAutoTCallVProc<const void, CFRelease> arsrc(imageSrc);
    1.65 +
    1.66 +    CGImageRef image = CGImageSourceCreateImageAtIndex(imageSrc, 0, NULL);
    1.67 +    if (NULL == image) {
    1.68 +        return false;
    1.69 +    }
    1.70 +    SkAutoTCallVProc<CGImage, CGImageRelease> arimage(image);
    1.71 +
    1.72 +    const int width = SkToInt(CGImageGetWidth(image));
    1.73 +    const int height = SkToInt(CGImageGetHeight(image));
    1.74 +    bm->setConfig(SkBitmap::kARGB_8888_Config, width, height);
    1.75 +    if (SkImageDecoder::kDecodeBounds_Mode == mode) {
    1.76 +        return true;
    1.77 +    }
    1.78 +
    1.79 +    if (!this->allocPixelRef(bm, NULL)) {
    1.80 +        return false;
    1.81 +    }
    1.82 +
    1.83 +    bm->lockPixels();
    1.84 +    bm->eraseColor(SK_ColorTRANSPARENT);
    1.85 +
    1.86 +    CGColorSpaceRef cs = CGColorSpaceCreateDeviceRGB();
    1.87 +    CGContextRef cg = CGBitmapContextCreate(bm->getPixels(), width, height, 8, bm->rowBytes(), cs, BITMAP_INFO);
    1.88 +    CFRelease(cs);
    1.89 +
    1.90 +    CGContextDrawImage(cg, CGRectMake(0, 0, width, height), image);
    1.91 +    CGContextRelease(cg);
    1.92 +
    1.93 +    CGImageAlphaInfo info = CGImageGetAlphaInfo(image);
    1.94 +    switch (info) {
    1.95 +        case kCGImageAlphaNone:
    1.96 +        case kCGImageAlphaNoneSkipLast:
    1.97 +        case kCGImageAlphaNoneSkipFirst:
    1.98 +            SkASSERT(SkBitmap::ComputeIsOpaque(*bm));
    1.99 +            bm->setAlphaType(kOpaque_SkAlphaType);
   1.100 +            break;
   1.101 +        default:
   1.102 +            // we don't know if we're opaque or not, so compute it.
   1.103 +            if (SkBitmap::ComputeIsOpaque(*bm)) {
   1.104 +                bm->setAlphaType(kOpaque_SkAlphaType);
   1.105 +            }
   1.106 +    }
   1.107 +    if (!bm->isOpaque() && this->getRequireUnpremultipliedColors()) {
   1.108 +        // CGBitmapContext does not support unpremultiplied, so the image has been premultiplied.
   1.109 +        // Convert to unpremultiplied.
   1.110 +        for (int i = 0; i < width; ++i) {
   1.111 +            for (int j = 0; j < height; ++j) {
   1.112 +                uint32_t* addr = bm->getAddr32(i, j);
   1.113 +                *addr = SkUnPreMultiply::UnPreMultiplyPreservingByteOrder(*addr);
   1.114 +            }
   1.115 +        }
   1.116 +        bm->setAlphaType(kUnpremul_SkAlphaType);
   1.117 +    }
   1.118 +    bm->unlockPixels();
   1.119 +    return true;
   1.120 +}
   1.121 +
   1.122 +///////////////////////////////////////////////////////////////////////////////
   1.123 +
   1.124 +extern SkImageDecoder* image_decoder_from_stream(SkStreamRewindable*);
   1.125 +
   1.126 +SkImageDecoder* SkImageDecoder::Factory(SkStreamRewindable* stream) {
   1.127 +    SkImageDecoder* decoder = image_decoder_from_stream(stream);
   1.128 +    if (NULL == decoder) {
   1.129 +        // If no image decoder specific to the stream exists, use SkImageDecoder_CG.
   1.130 +        return SkNEW(SkImageDecoder_CG);
   1.131 +    } else {
   1.132 +        return decoder;
   1.133 +    }
   1.134 +}
   1.135 +
   1.136 +/////////////////////////////////////////////////////////////////////////
   1.137 +
   1.138 +SkMovie* SkMovie::DecodeStream(SkStreamRewindable* stream) {
   1.139 +    return NULL;
   1.140 +}
   1.141 +
   1.142 +/////////////////////////////////////////////////////////////////////////
   1.143 +
   1.144 +static size_t consumer_put(void* info, const void* buffer, size_t count) {
   1.145 +    SkWStream* stream = reinterpret_cast<SkWStream*>(info);
   1.146 +    return stream->write(buffer, count) ? count : 0;
   1.147 +}
   1.148 +
   1.149 +static void consumer_release(void* info) {
   1.150 +    // we do nothing, since by design we don't "own" the stream (i.e. info)
   1.151 +}
   1.152 +
   1.153 +static CGDataConsumerRef SkStreamToCGDataConsumer(SkWStream* stream) {
   1.154 +    CGDataConsumerCallbacks procs;
   1.155 +    procs.putBytes = consumer_put;
   1.156 +    procs.releaseConsumer = consumer_release;
   1.157 +    // we don't own/reference the stream, so it our consumer must not live
   1.158 +    // longer that our caller's ownership of the stream
   1.159 +    return CGDataConsumerCreate(stream, &procs);
   1.160 +}
   1.161 +
   1.162 +static CGImageDestinationRef SkStreamToImageDestination(SkWStream* stream,
   1.163 +                                                        CFStringRef type) {
   1.164 +    CGDataConsumerRef consumer = SkStreamToCGDataConsumer(stream);
   1.165 +    if (NULL == consumer) {
   1.166 +        return NULL;
   1.167 +    }
   1.168 +    SkAutoTCallVProc<const void, CFRelease> arconsumer(consumer);
   1.169 +
   1.170 +    return CGImageDestinationCreateWithDataConsumer(consumer, type, 1, NULL);
   1.171 +}
   1.172 +
   1.173 +class SkImageEncoder_CG : public SkImageEncoder {
   1.174 +public:
   1.175 +    SkImageEncoder_CG(Type t) : fType(t) {}
   1.176 +
   1.177 +protected:
   1.178 +    virtual bool onEncode(SkWStream* stream, const SkBitmap& bm, int quality);
   1.179 +
   1.180 +private:
   1.181 +    Type fType;
   1.182 +};
   1.183 +
   1.184 +/*  Encode bitmaps via CGImageDestination. We setup a DataConsumer which writes
   1.185 +    to our SkWStream. Since we don't reference/own the SkWStream, our consumer
   1.186 +    must only live for the duration of the onEncode() method.
   1.187 + */
   1.188 +bool SkImageEncoder_CG::onEncode(SkWStream* stream, const SkBitmap& bm,
   1.189 +                                 int quality) {
   1.190 +    // Used for converting a bitmap to 8888.
   1.191 +    const SkBitmap* bmPtr = &bm;
   1.192 +    SkBitmap bitmap8888;
   1.193 +
   1.194 +    CFStringRef type;
   1.195 +    switch (fType) {
   1.196 +        case kICO_Type:
   1.197 +            type = kUTTypeICO;
   1.198 +            break;
   1.199 +        case kBMP_Type:
   1.200 +            type = kUTTypeBMP;
   1.201 +            break;
   1.202 +        case kGIF_Type:
   1.203 +            type = kUTTypeGIF;
   1.204 +            break;
   1.205 +        case kJPEG_Type:
   1.206 +            type = kUTTypeJPEG;
   1.207 +            break;
   1.208 +        case kPNG_Type:
   1.209 +            // PNG encoding an ARGB_4444 bitmap gives the following errors in GM:
   1.210 +            // <Error>: CGImageDestinationAddImage image could not be converted to destination
   1.211 +            // format.
   1.212 +            // <Error>: CGImageDestinationFinalize image destination does not have enough images
   1.213 +            // So instead we copy to 8888.
   1.214 +            if (bm.colorType() == kARGB_4444_SkColorType) {
   1.215 +                bm.copyTo(&bitmap8888, kPMColor_SkColorType);
   1.216 +                bmPtr = &bitmap8888;
   1.217 +            }
   1.218 +            type = kUTTypePNG;
   1.219 +            break;
   1.220 +        default:
   1.221 +            return false;
   1.222 +    }
   1.223 +
   1.224 +    CGImageDestinationRef dst = SkStreamToImageDestination(stream, type);
   1.225 +    if (NULL == dst) {
   1.226 +        return false;
   1.227 +    }
   1.228 +    SkAutoTCallVProc<const void, CFRelease> ardst(dst);
   1.229 +
   1.230 +    CGImageRef image = SkCreateCGImageRef(*bmPtr);
   1.231 +    if (NULL == image) {
   1.232 +        return false;
   1.233 +    }
   1.234 +    SkAutoTCallVProc<CGImage, CGImageRelease> agimage(image);
   1.235 +
   1.236 +    CGImageDestinationAddImage(dst, image, NULL);
   1.237 +    return CGImageDestinationFinalize(dst);
   1.238 +}
   1.239 +
   1.240 +///////////////////////////////////////////////////////////////////////////////
   1.241 +
   1.242 +static SkImageEncoder* sk_imageencoder_cg_factory(SkImageEncoder::Type t) {
   1.243 +    switch (t) {
   1.244 +        case SkImageEncoder::kICO_Type:
   1.245 +        case SkImageEncoder::kBMP_Type:
   1.246 +        case SkImageEncoder::kGIF_Type:
   1.247 +        case SkImageEncoder::kJPEG_Type:
   1.248 +        case SkImageEncoder::kPNG_Type:
   1.249 +            break;
   1.250 +        default:
   1.251 +            return NULL;
   1.252 +    }
   1.253 +    return SkNEW_ARGS(SkImageEncoder_CG, (t));
   1.254 +}
   1.255 +
   1.256 +static SkImageEncoder_EncodeReg gEReg(sk_imageencoder_cg_factory);
   1.257 +
   1.258 +struct FormatConversion {
   1.259 +    CFStringRef             fUTType;
   1.260 +    SkImageDecoder::Format  fFormat;
   1.261 +};
   1.262 +
   1.263 +// Array of the types supported by the decoder.
   1.264 +static const FormatConversion gFormatConversions[] = {
   1.265 +    { kUTTypeBMP, SkImageDecoder::kBMP_Format },
   1.266 +    { kUTTypeGIF, SkImageDecoder::kGIF_Format },
   1.267 +    { kUTTypeICO, SkImageDecoder::kICO_Format },
   1.268 +    { kUTTypeJPEG, SkImageDecoder::kJPEG_Format },
   1.269 +    // Also include JPEG2000
   1.270 +    { kUTTypeJPEG2000, SkImageDecoder::kJPEG_Format },
   1.271 +    { kUTTypePNG, SkImageDecoder::kPNG_Format },
   1.272 +};
   1.273 +
   1.274 +static SkImageDecoder::Format UTType_to_Format(const CFStringRef uttype) {
   1.275 +    for (size_t i = 0; i < SK_ARRAY_COUNT(gFormatConversions); i++) {
   1.276 +        if (CFStringCompare(uttype, gFormatConversions[i].fUTType, 0) == kCFCompareEqualTo) {
   1.277 +            return gFormatConversions[i].fFormat;
   1.278 +        }
   1.279 +    }
   1.280 +    return SkImageDecoder::kUnknown_Format;
   1.281 +}
   1.282 +
   1.283 +static SkImageDecoder::Format get_format_cg(SkStreamRewindable* stream) {
   1.284 +    CGImageSourceRef imageSrc = SkStreamToCGImageSource(stream);
   1.285 +
   1.286 +    if (NULL == imageSrc) {
   1.287 +        return SkImageDecoder::kUnknown_Format;
   1.288 +    }
   1.289 +
   1.290 +    SkAutoTCallVProc<const void, CFRelease> arsrc(imageSrc);
   1.291 +    const CFStringRef name = CGImageSourceGetType(imageSrc);
   1.292 +    if (NULL == name) {
   1.293 +        return SkImageDecoder::kUnknown_Format;
   1.294 +    }
   1.295 +    return UTType_to_Format(name);
   1.296 +}
   1.297 +
   1.298 +static SkImageDecoder_FormatReg gFormatReg(get_format_cg);

mercurial