summaryrefslogtreecommitdiff
path: root/platform/darwin/src/image.mm
blob: 122682883e606cbf00f370ca505f67a7dad803e0 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
#include <mbgl/util/image+MGLAdditions.hpp>

#import <ImageIO/ImageIO.h>

namespace {

template <typename T, typename S, void (*Releaser)(S)>
struct CFHandle {
    CFHandle(T t_): t(t_) {}
    ~CFHandle() { Releaser(t); }
    T operator*() { return t; }
    operator bool() { return t; }
private:
    T t;
};

} // namespace

using CGImageHandle = CFHandle<CGImageRef, CGImageRef, CGImageRelease>;
using CFDataHandle = CFHandle<CFDataRef, CFTypeRef, CFRelease>;
using CGImageSourceHandle = CFHandle<CGImageSourceRef, CFTypeRef, CFRelease>;
using CGDataProviderHandle = CFHandle<CGDataProviderRef, CGDataProviderRef, CGDataProviderRelease>;
using CGColorSpaceHandle = CFHandle<CGColorSpaceRef, CGColorSpaceRef, CGColorSpaceRelease>;
using CGContextHandle = CFHandle<CGContextRef, CGContextRef, CGContextRelease>;

CGImageRef CGImageCreateWithMGLPremultipliedImage(mbgl::PremultipliedImage&& src) {
    // We're converting the PremultipliedImage's backing store to a CGDataProvider, and are taking
    // over ownership of the memory.
    CGDataProviderHandle provider(CGDataProviderCreateWithData(
        NULL, src.data.get(), src.bytes(), [](void*, const void* data, size_t) {
            delete[] reinterpret_cast<const decltype(src.data)::element_type*>(data);
        }));
    if (!provider) {
        return nil;
    }

    // If we successfully created the provider, it will take over management of the memory segment.
    src.data.release();

    CGColorSpaceHandle colorSpace(CGColorSpaceCreateDeviceRGB());
    if (!colorSpace) {
        return nil;
    }

    constexpr const size_t bitsPerComponent = 8;
    constexpr const size_t bytesPerPixel = 4;
    constexpr const size_t bitsPerPixel = bitsPerComponent * bytesPerPixel;
    const size_t bytesPerRow = bytesPerPixel * src.size.width;

    return CGImageCreate(src.size.width, src.size.height, bitsPerComponent, bitsPerPixel,
                         bytesPerRow, *colorSpace,
                         kCGBitmapByteOrderDefault | kCGImageAlphaPremultipliedLast, *provider,
                         NULL, false, kCGRenderingIntentDefault);
}

mbgl::PremultipliedImage MGLPremultipliedImageFromCGImage(CGImageRef src) {
    const size_t width = CGImageGetWidth(src);
    const size_t height = CGImageGetHeight(src);

    mbgl::PremultipliedImage image({ static_cast<uint32_t>(width), static_cast<uint32_t>(height) });

    CGColorSpaceHandle colorSpace(CGColorSpaceCreateDeviceRGB());
    if (!colorSpace) {
        throw std::runtime_error("CGColorSpaceCreateDeviceRGB failed");
    }

    constexpr const size_t bitsPerComponent = 8;
    constexpr const size_t bytesPerPixel = 4;
    const size_t bytesPerRow = bytesPerPixel * width;

    CGContextHandle context(CGBitmapContextCreate(
        image.data.get(), width, height, bitsPerComponent, bytesPerRow, *colorSpace,
        kCGBitmapByteOrderDefault | kCGImageAlphaPremultipliedLast));
    if (!context) {
        throw std::runtime_error("CGBitmapContextCreate failed");
    }

    CGContextSetBlendMode(*context, kCGBlendModeCopy);
    CGContextDrawImage(*context, CGRectMake(0, 0, width, height), src);

    return image;
}

namespace mbgl {

PremultipliedImage decodeImage(const std::string& source) {
    CFDataHandle data(CFDataCreateWithBytesNoCopy(
        kCFAllocatorDefault, reinterpret_cast<const unsigned char*>(source.data()), source.size(),
        kCFAllocatorNull));
    if (!data) {
        throw std::runtime_error("CFDataCreateWithBytesNoCopy failed");
    }

    CGImageSourceHandle imageSource(CGImageSourceCreateWithData(*data, NULL));
    if (!imageSource) {
        throw std::runtime_error("CGImageSourceCreateWithData failed");
    }

    CGImageHandle image(CGImageSourceCreateImageAtIndex(*imageSource, 0, NULL));
    if (!image) {
        throw std::runtime_error("CGImageSourceCreateImageAtIndex failed");
    }

    return MGLPremultipliedImageFromCGImage(*image);
}

} // namespace mbgl