summaryrefslogtreecommitdiff
path: root/include/mbgl/util/image.hpp
blob: c019bb949c0985d5221a33c6da0fe121f095195e (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
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
#pragma once

#include <mbgl/util/noncopyable.hpp>
#include <mbgl/util/geometry.hpp>
#include <mbgl/util/size.hpp>

#include <string>
#include <memory>
#include <algorithm>

namespace mbgl {

enum class ImageAlphaMode {
    Unassociated,
    Premultiplied,
    Exclusive, // Alpha-channel only
};

template <ImageAlphaMode Mode>
class Image : private util::noncopyable {
public:
    Image() = default;

    Image(Size size_)
        : size(std::move(size_)),
          data(std::make_unique<uint8_t[]>(bytes())) {}

    Image(Size size_, const uint8_t* srcData, std::size_t srcLength)
        : size(std::move(size_)) {
        if (srcLength != bytes()) {
            throw std::invalid_argument("mismatched image size");
        }
        data = std::make_unique<uint8_t[]>(bytes());
        std::copy(srcData, srcData + srcLength, data.get());
    }

    Image(Size size_, std::unique_ptr<uint8_t[]> data_)
        : size(std::move(size_)),
          data(std::move(data_)) {}

    Image(Image&& o)
        : size(o.size),
          data(std::move(o.data)) {}

    Image& operator=(Image&& o) {
        size = o.size;
        data = std::move(o.data);
        return *this;
    }

    friend bool operator==(const Image& lhs, const Image& rhs) {
        return std::equal(lhs.data.get(), lhs.data.get() + lhs.bytes(),
                          rhs.data.get(), rhs.data.get() + rhs.bytes());
    }

    friend bool operator!=(const Image& lhs, const Image& rhs) {
        return !(lhs == rhs);
    }

    bool valid() const {
        return !size.isEmpty() && data.get() != nullptr;
    }

    template <typename T = Image>
    T clone() const {
        T copy(size);
        std::copy(data.get(), data.get() + bytes(), copy.data.get());
        return copy;
    }

    size_t stride() const { return channels * size.width; }
    size_t bytes() const { return stride() * size.height; }

    void fill(uint8_t value) {
        std::fill(data.get(), data.get() + bytes(), value);
    }

    // Copy image data within `rect` from `src` to the rectangle of the same size at `pt`
    // in `dst`. If the specified bounds exceed the bounds of the source or destination,
    // throw `std::out_of_range`. Must not be used to move data within a single Image.
    static void copy(const Image& srcImg, Image& dstImg, const Point<uint32_t>& srcPt, const Point<uint32_t>& dstPt, const Size& size) {
        if (!srcImg.valid()) {
            throw std::invalid_argument("invalid source for image copy");
        }

        if (!dstImg.valid()) {
            throw std::invalid_argument("invalid destination for image copy");
        }

        if (size.width > srcImg.size.width ||
            size.height > srcImg.size.height ||
            srcPt.x > srcImg.size.width - size.width ||
            srcPt.y > srcImg.size.height - size.height) {
            throw std::out_of_range("out of range source coordinates for image copy");
        }

        if (size.width > dstImg.size.width ||
            size.height > dstImg.size.height ||
            dstPt.x > dstImg.size.width - size.width ||
            dstPt.y > dstImg.size.height - size.height) {
            throw std::out_of_range("out of range destination coordinates for image copy");
        }

        const uint8_t* srcData = srcImg.data.get();
              uint8_t* dstData = dstImg.data.get();

        assert(srcData != dstData);

        for (uint32_t y = 0; y < size.height; y++) {
            const std::size_t srcOffset = (srcPt.y + y) * srcImg.stride() + srcPt.x * channels;
            const std::size_t dstOffset = (dstPt.y + y) * dstImg.stride() + dstPt.x * channels;
            std::copy(srcData + srcOffset,
                      srcData + srcOffset + size.width * channels,
                      dstData + dstOffset);
        }
    }

    Size size;
    static constexpr size_t channels = Mode == ImageAlphaMode::Exclusive ? 1 : 4;
    std::unique_ptr<uint8_t[]> data;
};

using UnassociatedImage = Image<ImageAlphaMode::Unassociated>;
using PremultipliedImage = Image<ImageAlphaMode::Premultiplied>;
using AlphaImage = Image<ImageAlphaMode::Exclusive>;

// TODO: don't use std::string for binary data.
PremultipliedImage decodeImage(const std::string&);
std::string encodePNG(const PremultipliedImage&);

} // namespace mbgl