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 "SkImageEncoderPriv.h" 9 10 #if defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_IOS) 11 12 #include "mac/SkUniqueCFRef.h" 13 #include "SkBitmap.h" 14 #include "SkCGUtils.h" 15 #include "SkColorData.h" 16 #include "SkData.h" 17 #include "SkStream.h" 18 #include "SkStreamPriv.h" 19 #include "SkTemplates.h" 20 #include "SkUnPreMultiply.h" 21 22 #ifdef SK_BUILD_FOR_MAC 23 #include <ApplicationServices/ApplicationServices.h> 24 #endif 25 26 #ifdef SK_BUILD_FOR_IOS 27 #include <CoreGraphics/CoreGraphics.h> 28 #include <ImageIO/ImageIO.h> 29 #include <MobileCoreServices/MobileCoreServices.h> 30 #endif 31 32 static size_t consumer_put(void* info, const void* buffer, size_t count) { 33 SkWStream* stream = reinterpret_cast<SkWStream*>(info); 34 return stream->write(buffer, count) ? count : 0; 35 } 36 37 static void consumer_release(void* info) { 38 // we do nothing, since by design we don't "own" the stream (i.e. info) 39 } 40 41 static SkUniqueCFRef<CGDataConsumerRef> SkStreamToCGDataConsumer(SkWStream* stream) { 42 CGDataConsumerCallbacks procs; 43 procs.putBytes = consumer_put; 44 procs.releaseConsumer = consumer_release; 45 // we don't own/reference the stream, so it our consumer must not live 46 // longer that our caller's ownership of the stream 47 return SkUniqueCFRef<CGDataConsumerRef>(CGDataConsumerCreate(stream, &procs)); 48 } 49 50 static SkUniqueCFRef<CGImageDestinationRef> SkStreamToImageDestination(SkWStream* stream, 51 CFStringRef type) { 52 SkUniqueCFRef<CGDataConsumerRef> consumer = SkStreamToCGDataConsumer(stream); 53 if (nullptr == consumer) { 54 return nullptr; 55 } 56 57 return SkUniqueCFRef<CGImageDestinationRef>( 58 CGImageDestinationCreateWithDataConsumer(consumer.get(), type, 1, nullptr)); 59 } 60 61 /* Encode bitmaps via CGImageDestination. We setup a DataConsumer which writes 62 to our SkWStream. Since we don't reference/own the SkWStream, our consumer 63 must only live for the duration of the onEncode() method. 64 */ 65 bool SkEncodeImageWithCG(SkWStream* stream, const SkPixmap& pixmap, SkEncodedImageFormat format) { 66 SkBitmap bm; 67 if (!bm.installPixels(pixmap)) { 68 return false; 69 } 70 bm.setImmutable(); 71 72 CFStringRef type; 73 switch (format) { 74 case SkEncodedImageFormat::kICO: 75 type = kUTTypeICO; 76 break; 77 case SkEncodedImageFormat::kBMP: 78 type = kUTTypeBMP; 79 break; 80 case SkEncodedImageFormat::kGIF: 81 type = kUTTypeGIF; 82 break; 83 case SkEncodedImageFormat::kJPEG: 84 type = kUTTypeJPEG; 85 break; 86 case SkEncodedImageFormat::kPNG: 87 // PNG encoding an ARGB_4444 bitmap gives the following errors in GM: 88 // <Error>: CGImageDestinationAddImage image could not be converted to destination 89 // format. 90 // <Error>: CGImageDestinationFinalize image destination does not have enough images 91 // So instead we copy to 8888. 92 if (bm.colorType() == kARGB_4444_SkColorType) { 93 SkBitmap bitmapN32; 94 bitmapN32.allocPixels(bm.info().makeColorType(kN32_SkColorType)); 95 bm.readPixels(bitmapN32.info(), bitmapN32.getPixels(), bitmapN32.rowBytes(), 0, 0); 96 bm.swap(bitmapN32); 97 } 98 type = kUTTypePNG; 99 break; 100 default: 101 return false; 102 } 103 104 SkUniqueCFRef<CGImageDestinationRef> dst = SkStreamToImageDestination(stream, type); 105 if (nullptr == dst) { 106 return false; 107 } 108 109 SkUniqueCFRef<CGImageRef> image(SkCreateCGImageRef(bm)); 110 if (nullptr == image) { 111 return false; 112 } 113 114 CGImageDestinationAddImage(dst.get(), image.get(), nullptr); 115 return CGImageDestinationFinalize(dst.get()); 116 } 117 118 #endif//defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_IOS) 119