/* * Copyright 2008 The Android Open Source Project * * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #include "SkImageEncoderPriv.h" #if defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_IOS) #include "mac/SkUniqueCFRef.h" #include "SkBitmap.h" #include "SkCGUtils.h" #include "SkColorData.h" #include "SkData.h" #include "SkStream.h" #include "SkStreamPriv.h" #include "SkTemplates.h" #include "SkUnPreMultiply.h" #ifdef SK_BUILD_FOR_MAC #include #endif #ifdef SK_BUILD_FOR_IOS #include #include #include #endif static size_t consumer_put(void* info, const void* buffer, size_t count) { SkWStream* stream = reinterpret_cast(info); return stream->write(buffer, count) ? count : 0; } static void consumer_release(void* info) { // we do nothing, since by design we don't "own" the stream (i.e. info) } static SkUniqueCFRef SkStreamToCGDataConsumer(SkWStream* stream) { CGDataConsumerCallbacks procs; procs.putBytes = consumer_put; procs.releaseConsumer = consumer_release; // we don't own/reference the stream, so it our consumer must not live // longer that our caller's ownership of the stream return SkUniqueCFRef(CGDataConsumerCreate(stream, &procs)); } static SkUniqueCFRef SkStreamToImageDestination(SkWStream* stream, CFStringRef type) { SkUniqueCFRef consumer = SkStreamToCGDataConsumer(stream); if (nullptr == consumer) { return nullptr; } return SkUniqueCFRef( CGImageDestinationCreateWithDataConsumer(consumer.get(), type, 1, nullptr)); } /* Encode bitmaps via CGImageDestination. We setup a DataConsumer which writes to our SkWStream. Since we don't reference/own the SkWStream, our consumer must only live for the duration of the onEncode() method. */ bool SkEncodeImageWithCG(SkWStream* stream, const SkPixmap& pixmap, SkEncodedImageFormat format) { SkBitmap bm; if (!bm.installPixels(pixmap)) { return false; } bm.setImmutable(); CFStringRef type; switch (format) { case SkEncodedImageFormat::kICO: type = kUTTypeICO; break; case SkEncodedImageFormat::kBMP: type = kUTTypeBMP; break; case SkEncodedImageFormat::kGIF: type = kUTTypeGIF; break; case SkEncodedImageFormat::kJPEG: type = kUTTypeJPEG; break; case SkEncodedImageFormat::kPNG: // PNG encoding an ARGB_4444 bitmap gives the following errors in GM: // : CGImageDestinationAddImage image could not be converted to destination // format. // : CGImageDestinationFinalize image destination does not have enough images // So instead we copy to 8888. if (bm.colorType() == kARGB_4444_SkColorType) { SkBitmap bitmapN32; bitmapN32.allocPixels(bm.info().makeColorType(kN32_SkColorType)); bm.readPixels(bitmapN32.info(), bitmapN32.getPixels(), bitmapN32.rowBytes(), 0, 0); bm.swap(bitmapN32); } type = kUTTypePNG; break; default: return false; } SkUniqueCFRef dst = SkStreamToImageDestination(stream, type); if (nullptr == dst) { return false; } SkUniqueCFRef image(SkCreateCGImageRef(bm)); if (nullptr == image) { return false; } CGImageDestinationAddImage(dst.get(), image.get(), nullptr); return CGImageDestinationFinalize(dst.get()); } #endif//defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_IOS)