michael@0: /* michael@0: * Copyright 2008 The Android Open Source Project michael@0: * michael@0: * Use of this source code is governed by a BSD-style license that can be michael@0: * found in the LICENSE file. michael@0: */ michael@0: michael@0: #include "SkCGUtils.h" michael@0: #include "SkColorPriv.h" michael@0: #include "SkImageDecoder.h" michael@0: #include "SkImageEncoder.h" michael@0: #include "SkMovie.h" michael@0: #include "SkStream.h" michael@0: #include "SkStreamHelpers.h" michael@0: #include "SkTemplates.h" michael@0: #include "SkUnPreMultiply.h" michael@0: michael@0: #ifdef SK_BUILD_FOR_MAC michael@0: #include michael@0: #endif michael@0: michael@0: #ifdef SK_BUILD_FOR_IOS michael@0: #include michael@0: #include michael@0: #include michael@0: #endif michael@0: michael@0: static void malloc_release_proc(void* info, const void* data, size_t size) { michael@0: sk_free(info); michael@0: } michael@0: michael@0: static CGDataProviderRef SkStreamToDataProvider(SkStream* stream) { michael@0: // TODO: use callbacks, so we don't have to load all the data into RAM michael@0: SkAutoMalloc storage; michael@0: const size_t len = CopyStreamToStorage(&storage, stream); michael@0: void* data = storage.detach(); michael@0: michael@0: return CGDataProviderCreateWithData(data, data, len, malloc_release_proc); michael@0: } michael@0: michael@0: static CGImageSourceRef SkStreamToCGImageSource(SkStream* stream) { michael@0: CGDataProviderRef data = SkStreamToDataProvider(stream); michael@0: CGImageSourceRef imageSrc = CGImageSourceCreateWithDataProvider(data, 0); michael@0: CGDataProviderRelease(data); michael@0: return imageSrc; michael@0: } michael@0: michael@0: class SkImageDecoder_CG : public SkImageDecoder { michael@0: protected: michael@0: virtual bool onDecode(SkStream* stream, SkBitmap* bm, Mode); michael@0: }; michael@0: michael@0: #define BITMAP_INFO (kCGBitmapByteOrder32Big | kCGImageAlphaPremultipliedLast) michael@0: michael@0: bool SkImageDecoder_CG::onDecode(SkStream* stream, SkBitmap* bm, Mode mode) { michael@0: CGImageSourceRef imageSrc = SkStreamToCGImageSource(stream); michael@0: michael@0: if (NULL == imageSrc) { michael@0: return false; michael@0: } michael@0: SkAutoTCallVProc arsrc(imageSrc); michael@0: michael@0: CGImageRef image = CGImageSourceCreateImageAtIndex(imageSrc, 0, NULL); michael@0: if (NULL == image) { michael@0: return false; michael@0: } michael@0: SkAutoTCallVProc arimage(image); michael@0: michael@0: const int width = SkToInt(CGImageGetWidth(image)); michael@0: const int height = SkToInt(CGImageGetHeight(image)); michael@0: bm->setConfig(SkBitmap::kARGB_8888_Config, width, height); michael@0: if (SkImageDecoder::kDecodeBounds_Mode == mode) { michael@0: return true; michael@0: } michael@0: michael@0: if (!this->allocPixelRef(bm, NULL)) { michael@0: return false; michael@0: } michael@0: michael@0: bm->lockPixels(); michael@0: bm->eraseColor(SK_ColorTRANSPARENT); michael@0: michael@0: CGColorSpaceRef cs = CGColorSpaceCreateDeviceRGB(); michael@0: CGContextRef cg = CGBitmapContextCreate(bm->getPixels(), width, height, 8, bm->rowBytes(), cs, BITMAP_INFO); michael@0: CFRelease(cs); michael@0: michael@0: CGContextDrawImage(cg, CGRectMake(0, 0, width, height), image); michael@0: CGContextRelease(cg); michael@0: michael@0: CGImageAlphaInfo info = CGImageGetAlphaInfo(image); michael@0: switch (info) { michael@0: case kCGImageAlphaNone: michael@0: case kCGImageAlphaNoneSkipLast: michael@0: case kCGImageAlphaNoneSkipFirst: michael@0: SkASSERT(SkBitmap::ComputeIsOpaque(*bm)); michael@0: bm->setAlphaType(kOpaque_SkAlphaType); michael@0: break; michael@0: default: michael@0: // we don't know if we're opaque or not, so compute it. michael@0: if (SkBitmap::ComputeIsOpaque(*bm)) { michael@0: bm->setAlphaType(kOpaque_SkAlphaType); michael@0: } michael@0: } michael@0: if (!bm->isOpaque() && this->getRequireUnpremultipliedColors()) { michael@0: // CGBitmapContext does not support unpremultiplied, so the image has been premultiplied. michael@0: // Convert to unpremultiplied. michael@0: for (int i = 0; i < width; ++i) { michael@0: for (int j = 0; j < height; ++j) { michael@0: uint32_t* addr = bm->getAddr32(i, j); michael@0: *addr = SkUnPreMultiply::UnPreMultiplyPreservingByteOrder(*addr); michael@0: } michael@0: } michael@0: bm->setAlphaType(kUnpremul_SkAlphaType); michael@0: } michael@0: bm->unlockPixels(); michael@0: return true; michael@0: } michael@0: michael@0: /////////////////////////////////////////////////////////////////////////////// michael@0: michael@0: extern SkImageDecoder* image_decoder_from_stream(SkStreamRewindable*); michael@0: michael@0: SkImageDecoder* SkImageDecoder::Factory(SkStreamRewindable* stream) { michael@0: SkImageDecoder* decoder = image_decoder_from_stream(stream); michael@0: if (NULL == decoder) { michael@0: // If no image decoder specific to the stream exists, use SkImageDecoder_CG. michael@0: return SkNEW(SkImageDecoder_CG); michael@0: } else { michael@0: return decoder; michael@0: } michael@0: } michael@0: michael@0: ///////////////////////////////////////////////////////////////////////// michael@0: michael@0: SkMovie* SkMovie::DecodeStream(SkStreamRewindable* stream) { michael@0: return NULL; michael@0: } michael@0: michael@0: ///////////////////////////////////////////////////////////////////////// michael@0: michael@0: static size_t consumer_put(void* info, const void* buffer, size_t count) { michael@0: SkWStream* stream = reinterpret_cast(info); michael@0: return stream->write(buffer, count) ? count : 0; michael@0: } michael@0: michael@0: static void consumer_release(void* info) { michael@0: // we do nothing, since by design we don't "own" the stream (i.e. info) michael@0: } michael@0: michael@0: static CGDataConsumerRef SkStreamToCGDataConsumer(SkWStream* stream) { michael@0: CGDataConsumerCallbacks procs; michael@0: procs.putBytes = consumer_put; michael@0: procs.releaseConsumer = consumer_release; michael@0: // we don't own/reference the stream, so it our consumer must not live michael@0: // longer that our caller's ownership of the stream michael@0: return CGDataConsumerCreate(stream, &procs); michael@0: } michael@0: michael@0: static CGImageDestinationRef SkStreamToImageDestination(SkWStream* stream, michael@0: CFStringRef type) { michael@0: CGDataConsumerRef consumer = SkStreamToCGDataConsumer(stream); michael@0: if (NULL == consumer) { michael@0: return NULL; michael@0: } michael@0: SkAutoTCallVProc arconsumer(consumer); michael@0: michael@0: return CGImageDestinationCreateWithDataConsumer(consumer, type, 1, NULL); michael@0: } michael@0: michael@0: class SkImageEncoder_CG : public SkImageEncoder { michael@0: public: michael@0: SkImageEncoder_CG(Type t) : fType(t) {} michael@0: michael@0: protected: michael@0: virtual bool onEncode(SkWStream* stream, const SkBitmap& bm, int quality); michael@0: michael@0: private: michael@0: Type fType; michael@0: }; michael@0: michael@0: /* Encode bitmaps via CGImageDestination. We setup a DataConsumer which writes michael@0: to our SkWStream. Since we don't reference/own the SkWStream, our consumer michael@0: must only live for the duration of the onEncode() method. michael@0: */ michael@0: bool SkImageEncoder_CG::onEncode(SkWStream* stream, const SkBitmap& bm, michael@0: int quality) { michael@0: // Used for converting a bitmap to 8888. michael@0: const SkBitmap* bmPtr = &bm; michael@0: SkBitmap bitmap8888; michael@0: michael@0: CFStringRef type; michael@0: switch (fType) { michael@0: case kICO_Type: michael@0: type = kUTTypeICO; michael@0: break; michael@0: case kBMP_Type: michael@0: type = kUTTypeBMP; michael@0: break; michael@0: case kGIF_Type: michael@0: type = kUTTypeGIF; michael@0: break; michael@0: case kJPEG_Type: michael@0: type = kUTTypeJPEG; michael@0: break; michael@0: case kPNG_Type: michael@0: // PNG encoding an ARGB_4444 bitmap gives the following errors in GM: michael@0: // : CGImageDestinationAddImage image could not be converted to destination michael@0: // format. michael@0: // : CGImageDestinationFinalize image destination does not have enough images michael@0: // So instead we copy to 8888. michael@0: if (bm.colorType() == kARGB_4444_SkColorType) { michael@0: bm.copyTo(&bitmap8888, kPMColor_SkColorType); michael@0: bmPtr = &bitmap8888; michael@0: } michael@0: type = kUTTypePNG; michael@0: break; michael@0: default: michael@0: return false; michael@0: } michael@0: michael@0: CGImageDestinationRef dst = SkStreamToImageDestination(stream, type); michael@0: if (NULL == dst) { michael@0: return false; michael@0: } michael@0: SkAutoTCallVProc ardst(dst); michael@0: michael@0: CGImageRef image = SkCreateCGImageRef(*bmPtr); michael@0: if (NULL == image) { michael@0: return false; michael@0: } michael@0: SkAutoTCallVProc agimage(image); michael@0: michael@0: CGImageDestinationAddImage(dst, image, NULL); michael@0: return CGImageDestinationFinalize(dst); michael@0: } michael@0: michael@0: /////////////////////////////////////////////////////////////////////////////// michael@0: michael@0: static SkImageEncoder* sk_imageencoder_cg_factory(SkImageEncoder::Type t) { michael@0: switch (t) { michael@0: case SkImageEncoder::kICO_Type: michael@0: case SkImageEncoder::kBMP_Type: michael@0: case SkImageEncoder::kGIF_Type: michael@0: case SkImageEncoder::kJPEG_Type: michael@0: case SkImageEncoder::kPNG_Type: michael@0: break; michael@0: default: michael@0: return NULL; michael@0: } michael@0: return SkNEW_ARGS(SkImageEncoder_CG, (t)); michael@0: } michael@0: michael@0: static SkImageEncoder_EncodeReg gEReg(sk_imageencoder_cg_factory); michael@0: michael@0: struct FormatConversion { michael@0: CFStringRef fUTType; michael@0: SkImageDecoder::Format fFormat; michael@0: }; michael@0: michael@0: // Array of the types supported by the decoder. michael@0: static const FormatConversion gFormatConversions[] = { michael@0: { kUTTypeBMP, SkImageDecoder::kBMP_Format }, michael@0: { kUTTypeGIF, SkImageDecoder::kGIF_Format }, michael@0: { kUTTypeICO, SkImageDecoder::kICO_Format }, michael@0: { kUTTypeJPEG, SkImageDecoder::kJPEG_Format }, michael@0: // Also include JPEG2000 michael@0: { kUTTypeJPEG2000, SkImageDecoder::kJPEG_Format }, michael@0: { kUTTypePNG, SkImageDecoder::kPNG_Format }, michael@0: }; michael@0: michael@0: static SkImageDecoder::Format UTType_to_Format(const CFStringRef uttype) { michael@0: for (size_t i = 0; i < SK_ARRAY_COUNT(gFormatConversions); i++) { michael@0: if (CFStringCompare(uttype, gFormatConversions[i].fUTType, 0) == kCFCompareEqualTo) { michael@0: return gFormatConversions[i].fFormat; michael@0: } michael@0: } michael@0: return SkImageDecoder::kUnknown_Format; michael@0: } michael@0: michael@0: static SkImageDecoder::Format get_format_cg(SkStreamRewindable* stream) { michael@0: CGImageSourceRef imageSrc = SkStreamToCGImageSource(stream); michael@0: michael@0: if (NULL == imageSrc) { michael@0: return SkImageDecoder::kUnknown_Format; michael@0: } michael@0: michael@0: SkAutoTCallVProc arsrc(imageSrc); michael@0: const CFStringRef name = CGImageSourceGetType(imageSrc); michael@0: if (NULL == name) { michael@0: return SkImageDecoder::kUnknown_Format; michael@0: } michael@0: return UTType_to_Format(name); michael@0: } michael@0: michael@0: static SkImageDecoder_FormatReg gFormatReg(get_format_cg);