diff options
author | John Firebaugh <john.firebaugh@gmail.com> | 2017-02-10 17:18:18 -0800 |
---|---|---|
committer | John Firebaugh <john.firebaugh@gmail.com> | 2017-02-14 12:29:01 -0600 |
commit | 94f011895c8e1bde36ee2ec235dbbcf2c994ac4c (patch) | |
tree | 9a9724792f87eb3c74aa2ac3d3114ca45d91471b /include/mbgl | |
parent | c6d5eaf47941162ee6166842d5434a0e3a6c33a0 (diff) | |
download | qtlocation-mapboxgl-94f011895c8e1bde36ee2ec235dbbcf2c994ac4c.tar.gz |
[core] Make Image safer
Provide Image::copy, which handles copying rectangles from a source to a destination, with thorough bounds checking.
Also fixes an indexing error in SpriteAtlas, where the top row of pixels in a wrapped image was copied from the wrong source row.
Diffstat (limited to 'include/mbgl')
-rw-r--r-- | include/mbgl/util/image.hpp | 65 | ||||
-rw-r--r-- | include/mbgl/util/size.hpp | 4 |
2 files changed, 65 insertions, 4 deletions
diff --git a/include/mbgl/util/image.hpp b/include/mbgl/util/image.hpp index 2de7646837..5d1462e7c4 100644 --- a/include/mbgl/util/image.hpp +++ b/include/mbgl/util/image.hpp @@ -1,6 +1,7 @@ #pragma once #include <mbgl/util/noncopyable.hpp> +#include <mbgl/util/geometry.hpp> #include <mbgl/util/size.hpp> #include <string> @@ -24,6 +25,15 @@ public: : 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_)) {} @@ -38,10 +48,13 @@ public: return *this; } - bool operator==(const Image& rhs) const { - return size == rhs.size && - std::equal(data.get(), data.get() + bytes(), rhs.data.get(), - rhs.data.get() + rhs.bytes()); + 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 { @@ -58,6 +71,50 @@ public: 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; diff --git a/include/mbgl/util/size.hpp b/include/mbgl/util/size.hpp index 1af85bcff5..79679a92fb 100644 --- a/include/mbgl/util/size.hpp +++ b/include/mbgl/util/size.hpp @@ -13,6 +13,10 @@ public: constexpr Size(const uint32_t width_, const uint32_t height_) : width(width_), height(height_) { } + constexpr uint32_t area() const { + return width * height; + } + constexpr explicit operator bool() const { return width > 0 && height > 0; } |