1/*
2 * Copyright 2016 Google Inc.
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 "include/codec/SkEncodedOrigin.h"
9#include "include/ports/SkImageGeneratorCG.h"
10#include "include/private/SkTemplates.h"
11#include "include/utils/mac/SkCGUtils.h"
12#include "src/core/SkPixmapPriv.h"
13#include "src/utils/mac/SkUniqueCFRef.h"
14
15#ifdef SK_BUILD_FOR_MAC
16#include <ApplicationServices/ApplicationServices.h>
17#endif
18
19#ifdef SK_BUILD_FOR_IOS
20#include <CoreGraphics/CoreGraphics.h>
21#include <ImageIO/ImageIO.h>
22#include <MobileCoreServices/MobileCoreServices.h>
23#endif
24
25namespace {
26class ImageGeneratorCG : public SkImageGenerator {
27public:
28    ImageGeneratorCG(const SkImageInfo&, SkUniqueCFRef<CGImageSourceRef> imageSrc,
29                     sk_sp<SkData> data, SkEncodedOrigin);
30
31protected:
32    sk_sp<SkData> onRefEncodedData() override;
33
34    bool onGetPixels(const SkImageInfo&, void* pixels, size_t rowBytes, const Options&) override;
35
36private:
37    const SkUniqueCFRef<CGImageSourceRef> fImageSrc;
38    const sk_sp<SkData> fData;
39    const SkEncodedOrigin fOrigin;
40
41    using INHERITED = SkImageGenerator;
42};
43
44static SkUniqueCFRef<CGImageSourceRef> data_to_CGImageSrc(SkData* data) {
45    SkUniqueCFRef<CGDataProviderRef> cgData(
46            CGDataProviderCreateWithData(data, data->data(), data->size(), nullptr));
47    if (!cgData) {
48        return nullptr;
49    }
50    return SkUniqueCFRef<CGImageSourceRef>(
51            CGImageSourceCreateWithDataProvider(cgData.get(), nullptr));
52}
53
54}  // namespace
55
56std::unique_ptr<SkImageGenerator> SkImageGeneratorCG::MakeFromEncodedCG(sk_sp<SkData> data) {
57    SkUniqueCFRef<CGImageSourceRef> imageSrc = data_to_CGImageSrc(data.get());
58    if (!imageSrc) {
59        return nullptr;
60    }
61
62    SkUniqueCFRef<CFDictionaryRef> properties(
63            CGImageSourceCopyPropertiesAtIndex(imageSrc.get(), 0, nullptr));
64    if (!properties) {
65        return nullptr;
66    }
67
68    CFNumberRef widthRef = static_cast<CFNumberRef>(
69            CFDictionaryGetValue(properties.get(), kCGImagePropertyPixelWidth));
70    CFNumberRef heightRef = static_cast<CFNumberRef>(
71            CFDictionaryGetValue(properties.get(), kCGImagePropertyPixelHeight));
72    if (nullptr == widthRef || nullptr == heightRef) {
73        return nullptr;
74    }
75
76    int width, height;
77    if (!CFNumberGetValue(widthRef , kCFNumberIntType, &width ) ||
78        !CFNumberGetValue(heightRef, kCFNumberIntType, &height))
79    {
80        return nullptr;
81    }
82
83    bool hasAlpha = bool(CFDictionaryGetValue(properties.get(), kCGImagePropertyHasAlpha));
84    SkAlphaType alphaType = hasAlpha ? kPremul_SkAlphaType : kOpaque_SkAlphaType;
85    SkImageInfo info = SkImageInfo::MakeS32(width, height, alphaType);
86
87    SkEncodedOrigin origin = kDefault_SkEncodedOrigin;
88    CFNumberRef orientationRef = static_cast<CFNumberRef>(
89            CFDictionaryGetValue(properties.get(), kCGImagePropertyOrientation));
90    int originInt;
91    if (orientationRef && CFNumberGetValue(orientationRef, kCFNumberIntType, &originInt)) {
92        origin = (SkEncodedOrigin) originInt;
93    }
94
95    if (SkEncodedOriginSwapsWidthHeight(origin)) {
96        info = SkPixmapPriv::SwapWidthHeight(info);
97    }
98
99    // FIXME: We have the opportunity to extract color space information here,
100    //        though I think it makes sense to wait until we understand how
101    //        we want to communicate it to the generator.
102
103    return std::unique_ptr<SkImageGenerator>(new ImageGeneratorCG(info, std::move(imageSrc),
104                                                                  std::move(data), origin));
105}
106
107ImageGeneratorCG::ImageGeneratorCG(const SkImageInfo& info, SkUniqueCFRef<CGImageSourceRef> src,
108                                   sk_sp<SkData> data, SkEncodedOrigin origin)
109    : INHERITED(info)
110    , fImageSrc(std::move(src))
111    , fData(std::move(data))
112    , fOrigin(origin)
113{}
114
115sk_sp<SkData> ImageGeneratorCG::onRefEncodedData() {
116    return fData;
117}
118
119bool ImageGeneratorCG::onGetPixels(const SkImageInfo& info, void* pixels, size_t rowBytes,
120                                   const Options&)
121{
122    if (kN32_SkColorType != info.colorType()) {
123        // FIXME: Support other colorTypes.
124        return false;
125    }
126
127    switch (info.alphaType()) {
128        case kOpaque_SkAlphaType:
129            if (kOpaque_SkAlphaType != this->getInfo().alphaType()) {
130                return false;
131            }
132            break;
133        case kPremul_SkAlphaType:
134            break;
135        default:
136            return false;
137    }
138
139    SkUniqueCFRef<CGImageRef> image(CGImageSourceCreateImageAtIndex(fImageSrc.get(), 0, nullptr));
140    if (!image) {
141        return false;
142    }
143
144    SkPixmap dst(info, pixels, rowBytes);
145    auto decode = [&image](const SkPixmap& pm) {
146        // FIXME: Using SkCopyPixelsFromCGImage (as opposed to swizzling
147        // ourselves) greatly restricts the color and alpha types that we
148        // support.  If we swizzle ourselves, we can add support for:
149        //     kUnpremul_SkAlphaType
150        //     16-bit per component RGBA
151        //     kGray_8_SkColorType
152        // Additionally, it would be interesting to compare the performance
153        // of SkSwizzler with CG's built in swizzler.
154        return SkCopyPixelsFromCGImage(pm, image.get());
155    };
156    return SkPixmapPriv::Orient(dst, fOrigin, decode);
157}
158