summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMikhail Pozdnyakov <mikhail.pozdnyakov@mapbox.com>2018-10-12 14:40:18 +0300
committerMikhail Pozdnyakov <mikhail.pozdnyakov@mapbox.com>2018-11-22 09:16:36 +0200
commita22ed80ea31838a8aafdcf095b3418db1a41002f (patch)
tree950f2b68b7b09ea6819b98b3e6af221f2327f3b9
parent6bb1e977eafc61af39d47df083bb3de13205fae9 (diff)
downloadqtlocation-mapboxgl-a22ed80ea31838a8aafdcf095b3418db1a41002f.tar.gz
Refactor mbgl::Image class
- Used `thin template idiom` to avoid size bloating - Exceptions free - No public data members, other improvements
-rw-r--r--cmake/core-files.txt1
-rw-r--r--include/mbgl/util/image.hpp188
-rw-r--r--platform/android/src/bitmap.cpp10
-rw-r--r--platform/android/src/map/image.cpp2
-rwxr-xr-xplatform/android/src/native_map_view.cpp2
-rw-r--r--platform/android/src/text/local_glyph_rasterizer.cpp5
-rw-r--r--platform/default/jpeg_reader.cpp2
-rw-r--r--platform/default/png_reader.cpp2
-rw-r--r--platform/default/png_writer.cpp8
-rw-r--r--platform/glfw/glfw_view.cpp2
-rw-r--r--platform/node/src/node_map.cpp14
-rw-r--r--platform/qt/src/qt_image.cpp2
-rw-r--r--src/mbgl/annotation/annotation_manager.cpp2
-rw-r--r--src/mbgl/geometry/dem_data.cpp11
-rw-r--r--src/mbgl/geometry/dem_data.hpp4
-rw-r--r--src/mbgl/geometry/line_atlas.cpp18
-rw-r--r--src/mbgl/gl/context.hpp8
-rw-r--r--src/mbgl/renderer/image_atlas.cpp10
-rw-r--r--src/mbgl/renderer/image_manager.cpp11
-rw-r--r--src/mbgl/renderer/layers/render_heatmap_layer.cpp9
-rw-r--r--src/mbgl/renderer/layers/render_line_layer.cpp9
-rw-r--r--src/mbgl/renderer/renderer_impl.cpp2
-rw-r--r--src/mbgl/sprite/sprite_parser.cpp6
-rw-r--r--src/mbgl/text/glyph_atlas.cpp6
-rw-r--r--src/mbgl/text/glyph_pbf.cpp7
-rw-r--r--src/mbgl/util/image.cpp110
-rw-r--r--src/mbgl/util/premultiply.cpp18
-rw-r--r--src/mbgl/util/tiny_sdf.cpp14
-rw-r--r--test/geometry/dem_data.test.cpp2
-rw-r--r--test/renderer/image_manager.test.cpp6
-rw-r--r--test/sprite/sprite_parser.test.cpp32
-rw-r--r--test/src/mbgl/test/util.cpp16
-rw-r--r--test/style/source.test.cpp8
-rw-r--r--test/style/style_image.test.cpp8
-rw-r--r--test/text/glyph_manager.test.cpp11
-rw-r--r--test/text/glyph_pbf.test.cpp2
-rw-r--r--test/util/image.test.cpp170
37 files changed, 383 insertions, 355 deletions
diff --git a/cmake/core-files.txt b/cmake/core-files.txt
index 2d55807ee8..84535614b9 100644
--- a/cmake/core-files.txt
+++ b/cmake/core-files.txt
@@ -734,6 +734,7 @@ src/mbgl/util/http_timeout.cpp
src/mbgl/util/http_timeout.hpp
src/mbgl/util/i18n.cpp
src/mbgl/util/i18n.hpp
+src/mbgl/util/image.cpp
src/mbgl/util/interpolate.cpp
src/mbgl/util/intersection_tests.cpp
src/mbgl/util/intersection_tests.hpp
diff --git a/include/mbgl/util/image.hpp b/include/mbgl/util/image.hpp
index 4887058f79..e38a283d70 100644
--- a/include/mbgl/util/image.hpp
+++ b/include/mbgl/util/image.hpp
@@ -1,6 +1,5 @@
#pragma once
-#include <mbgl/util/noncopyable.hpp>
#include <mbgl/util/geometry.hpp>
#include <mbgl/util/size.hpp>
@@ -11,6 +10,35 @@
namespace mbgl {
+class ImageBase {
+public:
+ // Returns true, if has data and non-zero size; returns false otherwise.
+ bool valid() const;
+ uint8_t* data() { return data_.get(); }
+ const uint8_t* data() const { return data_.get(); }
+ const Size& size() const { return size_; }
+ // Takes data from this image, leaving it in invalid state and with empty size.
+ std::unique_ptr<uint8_t[]> takeData();
+
+protected:
+ ImageBase();
+ ImageBase(const Size& s, size_t channels);
+ ImageBase(const Size& s, std::unique_ptr<uint8_t[]> d);
+ ImageBase(ImageBase&& o);
+ ~ImageBase();
+ ImageBase& operator=(ImageBase&& o);
+
+ size_t stride(size_t channels) const;
+ size_t bytes(size_t channels) const;
+ void resize(const Size& s, size_t channels);
+ void clear(const Point<uint32_t>& pt, const Size& size, size_t channels);
+
+ static void copy(const ImageBase& srcImg, ImageBase& dstImg, const Point<uint32_t>& srcPt, const Point<uint32_t>& dstPt, const Size& size, size_t channels);
+
+ Size size_;
+ std::unique_ptr<uint8_t[]> data_;
+};
+
enum class ImageAlphaMode {
Unassociated,
Premultiplied,
@@ -18,152 +46,46 @@ enum class ImageAlphaMode {
};
template <ImageAlphaMode Mode>
-class Image : private util::noncopyable {
+class Image : public ImageBase {
public:
+ static constexpr size_t channels = Mode == ImageAlphaMode::Exclusive ? 1 : 4;
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)) {
- o.size.width = o.size.height = 0;
+ Image(const Size& s) : ImageBase(s, channels) {}
+ Image(const Size& s, std::unique_ptr<uint8_t[]> d) : ImageBase(s, std::move(d)) {}
+ Image(Image&& o) = default;
+ ~Image() = default;
+ Image& operator=(Image&& o) = default;
+ size_t stride() const { return ImageBase::stride(channels); }
+ size_t bytes() const { return ImageBase::bytes(channels); }
+ // Resizes this image. If the given size is more than the current one,
+ // extra space is filled with '0'.
+ void resize(const Size& s) { ImageBase::resize(s, channels); }
+ // Clears the rect area specified by `pt` and `size` from this image.
+ // The image must be valid.
+ void clear(const Point<uint32_t>& pt, const Size& s) {
+ ImageBase::clear(pt, s, channels);
}
-
- Image& operator=(Image&& o) {
- size = o.size;
- data = std::move(o.data);
- o.size.width = o.size.height = 0;
- return *this;
+ bool operator==(const Image& o) const {
+ return std::equal(data(), data() + bytes(),
+ o.data(), o.data() + o.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 {
- return !size.isEmpty() && data.get() != nullptr;
+ bool operator!=(const Image& o) const {
+ return !(operator==(o));
}
template <typename T = Image>
T clone() const {
- T copy_(size);
- std::copy(data.get(), data.get() + bytes(), copy_.data.get());
+ T copy_(size());
+ std::copy(data(), data() + bytes(), copy_.data());
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);
- }
-
- void resize(Size size_) {
- if (size == size_) {
- return;
- }
- Image newImage(size_);
- newImage.fill(0);
- copy(*this, newImage, {0, 0}, {0, 0}, {
- std::min(size.width, size_.width),
- std::min(size.height, size_.height)
- });
- operator=(std::move(newImage));
- }
-
- // Clears the rect area specified by `pt` and `size` from `dstImage`.
- static void clear(Image& dstImg, const Point<uint32_t>& pt, const Size& size) {
- if (size.isEmpty()) {
- return;
- }
-
- if (!dstImg.valid()) {
- throw std::invalid_argument("invalid destination for image clear");
- }
-
- if (size.width > dstImg.size.width ||
- size.height > dstImg.size.height ||
- pt.x > dstImg.size.width - size.width ||
- pt.y > dstImg.size.height - size.height) {
- throw std::out_of_range("out of range destination coordinates for image clear");
- }
-
- uint8_t* dstData = dstImg.data.get();
-
- for (uint32_t y = 0; y < size.height; y++) {
- const std::size_t dstOffset = (pt.y + y) * dstImg.stride() + pt.x * channels;
- std::memset(dstData + dstOffset, 0, size.width * channels);
- }
- }
-
// 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.
+ // in `dst`. The specified bounds must not exceed the bounds of the source or destination.
+ // 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 (size.isEmpty()) {
- return;
- }
-
- 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);
- }
+ return ImageBase::copy(srcImg, dstImg, srcPt, dstPt, size, channels);
}
-
- Size size;
- static constexpr size_t channels = Mode == ImageAlphaMode::Exclusive ? 1 : 4;
- std::unique_ptr<uint8_t[]> data;
};
using UnassociatedImage = Image<ImageAlphaMode::Unassociated>;
diff --git a/platform/android/src/bitmap.cpp b/platform/android/src/bitmap.cpp
index eb7c676b12..e664a2d406 100644
--- a/platform/android/src/bitmap.cpp
+++ b/platform/android/src/bitmap.cpp
@@ -89,7 +89,7 @@ PremultipliedImage Bitmap::GetImage(jni::JNIEnv& env, const jni::Object<Bitmap>&
}
jni::Local<jni::Object<Bitmap>> Bitmap::CreateBitmap(jni::JNIEnv& env, const PremultipliedImage& image) {
- auto bitmap = CreateBitmap(env, image.size.width, image.size.height, Config::ARGB_8888);
+ auto bitmap = CreateBitmap(env, image.size().width, image.size().height, Config::ARGB_8888);
AndroidBitmapInfo info;
const int result = AndroidBitmap_getInfo(&env, jni::Unwrap(*bitmap), &info);
@@ -98,15 +98,15 @@ jni::Local<jni::Object<Bitmap>> Bitmap::CreateBitmap(jni::JNIEnv& env, const Pre
throw std::runtime_error("bitmap creation: couldn't get bitmap info");
}
- assert(info.width == image.size.width);
- assert(info.height == image.size.height);
+ assert(info.width == image.size().width);
+ assert(info.height == image.size().height);
assert(info.format == ANDROID_BITMAP_FORMAT_RGBA_8888);
PixelGuard guard(env, bitmap);
// Copy the PremultipliedImage into the Android Bitmap
- for (uint32_t y = 0; y < image.size.height; y++) {
- auto begin = image.data.get() + y * image.stride();
+ for (uint32_t y = 0; y < image.size().height; y++) {
+ auto begin = image.data() + y * image.stride();
std::copy(begin, begin + image.stride(), guard.get() + y * info.stride);
}
diff --git a/platform/android/src/map/image.cpp b/platform/android/src/map/image.cpp
index a91cc938ed..76b7a02a9b 100644
--- a/platform/android/src/map/image.cpp
+++ b/platform/android/src/map/image.cpp
@@ -29,7 +29,7 @@ mbgl::style::Image Image::getImage(jni::JNIEnv& env, const jni::Object<Image>& i
throw mbgl::util::SpriteImageException("Sprite image pixel count mismatch");
}
- jni::GetArrayRegion(env, *pixels, 0, size, reinterpret_cast<jbyte*>(premultipliedImage.data.get()));
+ jni::GetArrayRegion(env, *pixels, 0, size, reinterpret_cast<jbyte*>(premultipliedImage.data()));
return mbgl::style::Image {name, std::move(premultipliedImage), pixelRatio, sdf};
}
diff --git a/platform/android/src/native_map_view.cpp b/platform/android/src/native_map_view.cpp
index 1c744a6b57..dfe330dab4 100755
--- a/platform/android/src/native_map_view.cpp
+++ b/platform/android/src/native_map_view.cpp
@@ -601,7 +601,7 @@ void NativeMapView::addAnnotationIcon(JNIEnv& env, const jni::String& symbol, ji
throw mbgl::util::SpriteImageException("Sprite image pixel count mismatch");
}
- jni::GetArrayRegion(env, *jpixels, 0, size, reinterpret_cast<jbyte*>(premultipliedImage.data.get()));
+ jni::GetArrayRegion(env, *jpixels, 0, size, reinterpret_cast<jbyte*>(premultipliedImage.data()));
map->addAnnotationImage(std::make_unique<mbgl::style::Image>(
symbolName, std::move(premultipliedImage), float(scale)));
}
diff --git a/platform/android/src/text/local_glyph_rasterizer.cpp b/platform/android/src/text/local_glyph_rasterizer.cpp
index 8892ee3f37..534f323172 100644
--- a/platform/android/src/text/local_glyph_rasterizer.cpp
+++ b/platform/android/src/text/local_glyph_rasterizer.cpp
@@ -120,8 +120,9 @@ Glyph LocalGlyphRasterizer::rasterizeGlyph(const FontStack& fontStack, GlyphID g
// Copy alpha values from RGBA bitmap into the AlphaImage output
fixedMetrics.bitmap = AlphaImage(size);
- for (uint32_t i = 0; i < size.width * size.height; i++) {
- fixedMetrics.bitmap.data[i] = rgbaBitmap.data[4 * i + 3];
+ uint32_t sizeArea = size.area();
+ for (uint32_t i = 0; i < sizeArea; ++i) {
+ fixedMetrics.bitmap.data()[i] = rgbaBitmap.data()[4 * i + 3];
}
return fixedMetrics;
diff --git a/platform/default/jpeg_reader.cpp b/platform/default/jpeg_reader.cpp
index 5f613f9423..93740649f0 100644
--- a/platform/default/jpeg_reader.cpp
+++ b/platform/default/jpeg_reader.cpp
@@ -120,7 +120,7 @@ PremultipliedImage decodeJPEG(const uint8_t* data, size_t size) {
size_t rowStride = components * width;
PremultipliedImage image({ static_cast<uint32_t>(width), static_cast<uint32_t>(height) });
- uint8_t* dst = image.data.get();
+ uint8_t* dst = image.data();
JSAMPARRAY buffer = (*cinfo.mem->alloc_sarray)((j_common_ptr) &cinfo, JPOOL_IMAGE, rowStride, 1);
diff --git a/platform/default/png_reader.cpp b/platform/default/png_reader.cpp
index 4d4ee29d1f..8046db7449 100644
--- a/platform/default/png_reader.cpp
+++ b/platform/default/png_reader.cpp
@@ -131,7 +131,7 @@ PremultipliedImage decodePNG(const uint8_t* data, size_t size) {
// alloc row pointers
const std::unique_ptr<png_bytep[]> rows(new png_bytep[height]);
for (unsigned row = 0; row < height; ++row)
- rows[row] = image.data.get() + row * width * 4;
+ rows[row] = image.data() + row * width * 4;
png_read_image(png_ptr, rows.get());
png_read_end(png_ptr, nullptr);
diff --git a/platform/default/png_writer.cpp b/platform/default/png_writer.cpp
index b89e253f85..ffc02126a9 100644
--- a/platform/default/png_writer.cpp
+++ b/platform/default/png_writer.cpp
@@ -44,8 +44,8 @@ std::string encodePNG(const PremultipliedImage& pre) {
// IHDR chunk for our RGBA image.
const char ihdr[13] = {
- NETWORK_BYTE_UINT32(src.size.width), // width
- NETWORK_BYTE_UINT32(src.size.height), // height
+ NETWORK_BYTE_UINT32(src.size().width), // width
+ NETWORK_BYTE_UINT32(src.size().height), // height
8, // bit depth == 8 bits
6, // color type == RGBA
0, // compression method == deflate
@@ -56,10 +56,10 @@ std::string encodePNG(const PremultipliedImage& pre) {
// Prepare the (compressed) data chunk.
const auto stride = src.stride();
std::string idat;
- for (uint32_t y = 0; y < src.size.height; y++) {
+ for (uint32_t y = 0; y < src.size().height; y++) {
// Every scanline needs to be prefixed with one byte that indicates the filter type.
idat.append(1, 0); // filter type 0
- idat.append((const char*)(src.data.get() + y * stride), stride);
+ idat.append((const char*)(src.data() + y * stride), stride);
}
idat = util::compress(idat);
diff --git a/platform/glfw/glfw_view.cpp b/platform/glfw/glfw_view.cpp
index 9179113139..101f18212f 100644
--- a/platform/glfw/glfw_view.cpp
+++ b/platform/glfw/glfw_view.cpp
@@ -324,7 +324,7 @@ GLFWView::makeImage(const std::string& id, int width, int height, float pixelRat
const int h = std::ceil(pixelRatio * height);
mbgl::PremultipliedImage image({ static_cast<uint32_t>(w), static_cast<uint32_t>(h) });
- auto data = reinterpret_cast<uint32_t*>(image.data.get());
+ auto* data = reinterpret_cast<uint32_t*>(image.data());
const int dist = (w / 2) * (w / 2);
for (int y = 0; y < h; y++) {
for (int x = 0; x < w; x++) {
diff --git a/platform/node/src/node_map.cpp b/platform/node/src/node_map.cpp
index 6dccdf5292..bb7a5121f9 100644
--- a/platform/node/src/node_map.cpp
+++ b/platform/node/src/node_map.cpp
@@ -421,7 +421,7 @@ void NodeMap::Render(const Nan::FunctionCallbackInfo<v8::Value>& info) {
try {
auto options = ParseOptions(Nan::To<v8::Object>(info[0]).ToLocalChecked());
assert(!nodeMap->req);
- assert(!nodeMap->image.data);
+ assert(!nodeMap->image.valid());
nodeMap->req = std::make_unique<RenderRequest>(Nan::To<v8::Function>(info[1]).ToLocalChecked());
nodeMap->startRender(std::move(options));
@@ -463,7 +463,7 @@ void NodeMap::startRender(NodeMap::RenderOptions options) {
error = std::move(eptr);
uv_async_send(async);
} else {
- assert(!image.data);
+ assert(!image.valid());
image = frontend->readStillImage();
uv_async_send(async);
}
@@ -498,7 +498,7 @@ void NodeMap::renderFinished() {
// These have to be empty to be prepared for the next render call.
assert(!req);
- assert(!image.data);
+ assert(!image.valid());
v8::Local<v8::Function> callback = Nan::New(request->callback);
v8::Local<v8::Object> target = Nan::New<v8::Object>();
@@ -524,16 +524,16 @@ void NodeMap::renderFinished() {
assert(!error);
request->runInAsyncScope(target, callback, 1, argv);
- } else if (img.data) {
+ } else if (img.valid()) {
v8::Local<v8::Object> pixels = Nan::NewBuffer(
- reinterpret_cast<char *>(img.data.get()), img.bytes(),
+ reinterpret_cast<char *>(img.data()), img.bytes(),
// Retain the data until the buffer is deleted.
[](char *, void * hint) {
delete [] reinterpret_cast<uint8_t*>(hint);
},
- img.data.get()
+ img.data()
).ToLocalChecked();
- img.data.release();
+ img.takeData().release();
v8::Local<v8::Value> argv[] = {
Nan::Null(),
diff --git a/platform/qt/src/qt_image.cpp b/platform/qt/src/qt_image.cpp
index 302d398739..2a4dd23f40 100644
--- a/platform/qt/src/qt_image.cpp
+++ b/platform/qt/src/qt_image.cpp
@@ -7,7 +7,7 @@
namespace mbgl {
std::string encodePNG(const PremultipliedImage& pre) {
- QImage image(pre.data.get(), pre.size.width, pre.size.height,
+ QImage image(pre.data(), pre.size().width, pre.size().height,
QImage::Format_ARGB32_Premultiplied);
QByteArray array;
diff --git a/src/mbgl/annotation/annotation_manager.cpp b/src/mbgl/annotation/annotation_manager.cpp
index 1baf83179e..ccd3290b0b 100644
--- a/src/mbgl/annotation/annotation_manager.cpp
+++ b/src/mbgl/annotation/annotation_manager.cpp
@@ -240,7 +240,7 @@ double AnnotationManager::getTopOffsetPixelsForImage(const std::string& id_) {
std::lock_guard<std::mutex> lock(mutex);
const std::string id = prefixedImageID(id_);
auto it = images.find(id);
- return it != images.end() ? -(it->second.getImage().size.height / it->second.getPixelRatio()) / 2 : 0;
+ return it != images.end() ? -(it->second.getImage().size().height / it->second.getPixelRatio()) / 2 : 0;
}
} // namespace mbgl
diff --git a/src/mbgl/geometry/dem_data.cpp b/src/mbgl/geometry/dem_data.cpp
index 7fa98950ea..0c7d9ee29b 100644
--- a/src/mbgl/geometry/dem_data.cpp
+++ b/src/mbgl/geometry/dem_data.cpp
@@ -4,12 +4,12 @@
namespace mbgl {
DEMData::DEMData(const PremultipliedImage& _image, Tileset::DEMEncoding encoding):
- dim(_image.size.height),
- border(std::max<int32_t>(std::ceil(_image.size.height / 2), 1)),
+ dim(_image.size().height),
+ border(std::max<int32_t>(std::ceil(_image.size().height / 2), 1)),
stride(dim + 2 * border),
image({ static_cast<uint32_t>(stride), static_cast<uint32_t>(stride) }) {
- if (_image.size.height != _image.size.width){
+ if (_image.size().height != _image.size().width){
throw std::runtime_error("raster-dem tiles must be square.");
}
@@ -25,13 +25,14 @@ DEMData::DEMData(const PremultipliedImage& _image, Tileset::DEMEncoding encoding
auto decodeRGB = encoding == Tileset::DEMEncoding::Terrarium ? decodeTerrarium : decodeMapbox;
- std::memset(image.data.get(), 0, image.bytes());
+ std::memset(image.data(), 0, image.bytes());
for (int32_t y = 0; y < dim; y++) {
for (int32_t x = 0; x < dim; x++) {
const int32_t i = y * dim + x;
const int32_t j = i * 4;
- set(x, y, decodeRGB(_image.data[j], _image.data[j+1], _image.data[j+2]));
+ const uint8_t* imageData = _image.data();
+ set(x, y, decodeRGB(imageData[j], imageData[j+1], imageData[j+2]));
}
}
diff --git a/src/mbgl/geometry/dem_data.hpp b/src/mbgl/geometry/dem_data.hpp
index 817d3cc9c9..16bfb4f308 100644
--- a/src/mbgl/geometry/dem_data.hpp
+++ b/src/mbgl/geometry/dem_data.hpp
@@ -17,11 +17,11 @@ public:
void backfillBorder(const DEMData& borderTileData, int8_t dx, int8_t dy);
void set(const int32_t x, const int32_t y, const int32_t value) {
- reinterpret_cast<int32_t*>(image.data.get())[idx(x, y)] = value + 65536;
+ reinterpret_cast<int32_t*>(image.data())[idx(x, y)] = value + 65536;
}
int32_t get(const int32_t x, const int32_t y) const {
- return reinterpret_cast<const int32_t*>(image.data.get())[idx(x, y)] - 65536;
+ return reinterpret_cast<const int32_t*>(image.data())[idx(x, y)] - 65536;
}
const PremultipliedImage* getImage() const {
diff --git a/src/mbgl/geometry/line_atlas.cpp b/src/mbgl/geometry/line_atlas.cpp
index 1bd6f987e1..6b9a6601ac 100644
--- a/src/mbgl/geometry/line_atlas.cpp
+++ b/src/mbgl/geometry/line_atlas.cpp
@@ -43,7 +43,7 @@ LinePatternPos LineAtlas::addDash(const std::vector<float>& dasharray, LinePatte
return LinePatternPos();
}
- if (nextRow + dashheight > image.size.height) {
+ if (nextRow + dashheight > image.size().height) {
Log::Warning(Event::OpenGL, "line atlas bitmap overflow");
return LinePatternPos();
}
@@ -53,7 +53,7 @@ LinePatternPos LineAtlas::addDash(const std::vector<float>& dasharray, LinePatte
length += part;
}
- float stretch = image.size.width / length;
+ float stretch = image.size().width / length;
float halfWidth = stretch * 0.5;
// If dasharray has an odd length, both the first and last parts
// are dashes and should be joined seamlessly.
@@ -61,7 +61,7 @@ LinePatternPos LineAtlas::addDash(const std::vector<float>& dasharray, LinePatte
for (int y = -n; y <= n; y++) {
int row = nextRow + n + y;
- int index = image.size.width * row;
+ int index = image.size().width * row;
float left = 0;
float right = dasharray[0];
@@ -71,7 +71,7 @@ LinePatternPos LineAtlas::addDash(const std::vector<float>& dasharray, LinePatte
left -= dasharray.back();
}
- for (uint32_t x = 0; x < image.size.width; x++) {
+ for (uint32_t x = 0; x < image.size().width; x++) {
while (right < x / stretch) {
left = right;
@@ -105,14 +105,14 @@ LinePatternPos LineAtlas::addDash(const std::vector<float>& dasharray, LinePatte
} else {
signedDistance = int((inside ? 1 : -1) * dist);
}
-
- image.data[index + x] = fmax(0, fmin(255, signedDistance + offset));
+ uint8_t* imageData = const_cast<uint8_t*>(image.data());
+ imageData[index + x] = fmax(0, fmin(255, signedDistance + offset));
}
}
LinePatternPos position;
- position.y = (0.5 + nextRow + n) / image.size.height;
- position.height = (2.0 * n) / image.size.height;
+ position.y = (0.5 + nextRow + n) / image.size().height;
+ position.height = (2.0 * n) / image.size().height;
position.width = length;
nextRow += dashheight;
@@ -123,7 +123,7 @@ LinePatternPos LineAtlas::addDash(const std::vector<float>& dasharray, LinePatte
}
Size LineAtlas::getSize() const {
- return image.size;
+ return image.size();
}
void LineAtlas::upload(gl::Context& context, gl::TextureUnit unit) {
diff --git a/src/mbgl/gl/context.hpp b/src/mbgl/gl/context.hpp
index bd682f44da..9eaec2d2c1 100644
--- a/src/mbgl/gl/context.hpp
+++ b/src/mbgl/gl/context.hpp
@@ -121,7 +121,7 @@ public:
template <typename Image>
void drawPixels(const Image& image) {
auto format = image.channels == 4 ? TextureFormat::RGBA : TextureFormat::Alpha;
- drawPixels(image.size, image.data.get(), format);
+ drawPixels(image.size(), image.data(), format);
}
#endif // MBGL_USE_GLES2
@@ -131,7 +131,7 @@ public:
TextureUnit unit = 0,
TextureType type = TextureType::UnsignedByte) {
auto format = image.channels == 4 ? TextureFormat::RGBA : TextureFormat::Alpha;
- return { image.size, createTexture(image.size, image.data.get(), format, unit, type) };
+ return { image.size(), createTexture(image.size(), image.data(), format, unit, type) };
}
template <typename Image>
@@ -140,8 +140,8 @@ public:
TextureUnit unit = 0,
TextureType type = TextureType::UnsignedByte) {
auto format = image.channels == 4 ? TextureFormat::RGBA : TextureFormat::Alpha;
- updateTexture(obj.texture.get(), image.size, image.data.get(), format, unit, type);
- obj.size = image.size;
+ updateTexture(obj.texture.get(), image.size(), image.data(), format, unit, type);
+ obj.size = image.size();
}
// Creates an empty texture with the specified dimensions.
diff --git a/src/mbgl/renderer/image_atlas.cpp b/src/mbgl/renderer/image_atlas.cpp
index b39c788ced..a97d913616 100644
--- a/src/mbgl/renderer/image_atlas.cpp
+++ b/src/mbgl/renderer/image_atlas.cpp
@@ -18,8 +18,8 @@ ImagePosition::ImagePosition(const mapbox::Bin& bin, const style::Image::Impl& i
const mapbox::Bin& _packImage(mapbox::ShelfPack& pack, const style::Image::Impl& image, ImageAtlas& resultImage, ImageType imageType) {
const mapbox::Bin& bin = *pack.packOne(-1,
- image.image.size.width + 2 * padding,
- image.image.size.height + 2 * padding);
+ image.image.size().width + 2 * padding,
+ image.image.size().height + 2 * padding);
resultImage.image.resize({
static_cast<uint32_t>(pack.width()),
@@ -33,11 +33,11 @@ const mapbox::Bin& _packImage(mapbox::ShelfPack& pack, const style::Image::Impl&
bin.x + padding,
bin.y + padding
},
- image.image.size);
+ image.image.size());
uint32_t x = bin.x + padding,
y = bin.y + padding,
- w = image.image.size.width,
- h = image.image.size.height;
+ w = image.image.size().width,
+ h = image.image.size().height;
if (imageType == ImageType::Pattern) {
// Add 1 pixel wrapped padding on each side of the image.
diff --git a/src/mbgl/renderer/image_manager.cpp b/src/mbgl/renderer/image_manager.cpp
index fc1f5bb167..107f9cea29 100644
--- a/src/mbgl/renderer/image_manager.cpp
+++ b/src/mbgl/renderer/image_manager.cpp
@@ -44,7 +44,7 @@ void ImageManager::removeImage(const std::string& id) {
const uint32_t y = it->second.bin->y;
const uint32_t w = it->second.bin->w;
const uint32_t h = it->second.bin->h;
- PremultipliedImage::clear(atlasImage, { x, y }, { w, h });
+ atlasImage.clear({ x, y }, { w, h });
shelfPack.unref(*it->second.bin);
patterns.erase(it);
@@ -130,8 +130,8 @@ optional<ImagePosition> ImageManager::getPattern(const std::string& id) {
return {};
}
- const uint16_t width = image->image.size.width + padding * 2;
- const uint16_t height = image->image.size.height + padding * 2;
+ const uint16_t width = image->image.size().width + padding * 2;
+ const uint16_t height = image->image.size().height + padding * 2;
mapbox::Bin* bin = shelfPack.packOne(-1, width, height);
if (!bin) {
@@ -139,13 +139,14 @@ optional<ImagePosition> ImageManager::getPattern(const std::string& id) {
}
atlasImage.resize(getPixelSize());
+ assert(atlasImage.valid());
const PremultipliedImage& src = image->image;
const uint32_t x = bin->x + padding;
const uint32_t y = bin->y + padding;
- const uint32_t w = src.size.width;
- const uint32_t h = src.size.height;
+ const uint32_t w = src.size().width;
+ const uint32_t h = src.size().height;
PremultipliedImage::copy(src, atlasImage, { 0, 0 }, { x, y }, { w, h });
diff --git a/src/mbgl/renderer/layers/render_heatmap_layer.cpp b/src/mbgl/renderer/layers/render_heatmap_layer.cpp
index 4e5e890358..14f54cf639 100644
--- a/src/mbgl/renderer/layers/render_heatmap_layer.cpp
+++ b/src/mbgl/renderer/layers/render_heatmap_layer.cpp
@@ -196,13 +196,14 @@ void RenderHeatmapLayer::updateColorRamp() {
}
const auto length = colorRamp.bytes();
+ auto* colorData = colorRamp.data();
for (uint32_t i = 0; i < length; i += 4) {
const auto color = colorValue.evaluate(static_cast<double>(i) / length);
- colorRamp.data[i + 0] = std::floor(color.r * 255);
- colorRamp.data[i + 1] = std::floor(color.g * 255);
- colorRamp.data[i + 2] = std::floor(color.b * 255);
- colorRamp.data[i + 3] = std::floor(color.a * 255);
+ colorData[i + 0] = std::floor(color.r * 255);
+ colorData[i + 1] = std::floor(color.g * 255);
+ colorData[i + 2] = std::floor(color.b * 255);
+ colorData[i + 3] = std::floor(color.a * 255);
}
if (colorRampTexture) {
diff --git a/src/mbgl/renderer/layers/render_line_layer.cpp b/src/mbgl/renderer/layers/render_line_layer.cpp
index 94081b5f09..a610ab7711 100644
--- a/src/mbgl/renderer/layers/render_line_layer.cpp
+++ b/src/mbgl/renderer/layers/render_line_layer.cpp
@@ -250,13 +250,14 @@ void RenderLineLayer::updateColorRamp() {
}
const auto length = colorRamp.bytes();
+ auto* colorData = colorRamp.data();
for (uint32_t i = 0; i < length; i += 4) {
const auto color = colorValue.evaluate(static_cast<double>(i) / length);
- colorRamp.data[i] = std::floor(color.r * 255);
- colorRamp.data[i + 1] = std::floor(color.g * 255);
- colorRamp.data[i + 2] = std::floor(color.b * 255);
- colorRamp.data[i + 3] = std::floor(color.a * 255);
+ colorData[i] = std::floor(color.r * 255);
+ colorData[i + 1] = std::floor(color.g * 255);
+ colorData[i + 2] = std::floor(color.b * 255);
+ colorData[i + 3] = std::floor(color.a * 255);
}
if (colorRampTexture) {
diff --git a/src/mbgl/renderer/renderer_impl.cpp b/src/mbgl/renderer/renderer_impl.cpp
index 32fcd57332..f9e00eb623 100644
--- a/src/mbgl/renderer/renderer_impl.cpp
+++ b/src/mbgl/renderer/renderer_impl.cpp
@@ -488,7 +488,7 @@ void Renderer::Impl::render(const UpdateParameters& updateParameters) {
auto image = parameters.context.readFramebuffer<AlphaImage, gl::TextureFormat::Stencil>(viewport.size, false);
// Scale the Stencil buffer to cover the entire color space.
- auto it = image.data.get();
+ auto it = image.data();
auto end = it + viewport.size.width * viewport.size.height;
const auto factor = 255.0f / *std::max_element(it, end);
for (; it != end; ++it) {
diff --git a/src/mbgl/sprite/sprite_parser.cpp b/src/mbgl/sprite/sprite_parser.cpp
index 99e2b0c8ca..116673cb4f 100644
--- a/src/mbgl/sprite/sprite_parser.cpp
+++ b/src/mbgl/sprite/sprite_parser.cpp
@@ -23,11 +23,11 @@ std::unique_ptr<style::Image> createStyleImage(const std::string& id,
// Disallow invalid parameter configurations.
if (width <= 0 || height <= 0 || width > 1024 || height > 1024 ||
ratio <= 0 || ratio > 10 ||
- srcX >= image.size.width || srcY >= image.size.height ||
- srcX + width > image.size.width || srcY + height > image.size.height) {
+ srcX >= image.size().width || srcY >= image.size().height ||
+ srcX + width > image.size().width || srcY + height > image.size().height) {
Log::Error(Event::Sprite, "Can't create sprite with invalid metrics: %ux%u@%u,%u in %ux%u@%sx sprite",
width, height, srcX, srcY,
- image.size.width, image.size.height,
+ image.size().width, image.size().height,
util::toString(ratio).c_str());
return nullptr;
}
diff --git a/src/mbgl/text/glyph_atlas.cpp b/src/mbgl/text/glyph_atlas.cpp
index da65aea8a9..ca85e474af 100644
--- a/src/mbgl/text/glyph_atlas.cpp
+++ b/src/mbgl/text/glyph_atlas.cpp
@@ -22,8 +22,8 @@ GlyphAtlas makeGlyphAtlas(const GlyphMap& glyphs) {
const Glyph& glyph = **entry.second;
const mapbox::Bin& bin = *pack.packOne(-1,
- glyph.bitmap.size.width + 2 * padding,
- glyph.bitmap.size.height + 2 * padding);
+ glyph.bitmap.size().width + 2 * padding,
+ glyph.bitmap.size().height + 2 * padding);
result.image.resize({
static_cast<uint32_t>(pack.width()),
@@ -37,7 +37,7 @@ GlyphAtlas makeGlyphAtlas(const GlyphMap& glyphs) {
bin.x + padding,
bin.y + padding
},
- glyph.bitmap.size);
+ glyph.bitmap.size());
positions.emplace(glyph.id,
GlyphPosition {
diff --git a/src/mbgl/text/glyph_pbf.cpp b/src/mbgl/text/glyph_pbf.cpp
index cfaf803f75..afc57a13af 100644
--- a/src/mbgl/text/glyph_pbf.cpp
+++ b/src/mbgl/text/glyph_pbf.cpp
@@ -80,8 +80,11 @@ std::vector<Glyph> parseGlyphPBF(const GlyphRange& glyphRange, const std::string
if (size.area() != glyphData.size()) {
continue;
}
-
- glyph.bitmap = AlphaImage(size, reinterpret_cast<const uint8_t*>(glyphData.data()), glyphData.size());
+ std::unique_ptr<uint8_t[]> imageData{std::make_unique<uint8_t[]>(glyphData.size())};
+ const uint8_t* glyphDataAsUint8 = reinterpret_cast<const uint8_t*>(glyphData.data());
+ std::copy(glyphDataAsUint8, glyphDataAsUint8 + glyphData.size(), imageData.get());
+ glyph.bitmap = AlphaImage(size, std::move(imageData));
+ assert(glyphData.size() == glyph.bitmap.bytes());
}
result.push_back(std::move(glyph));
diff --git a/src/mbgl/util/image.cpp b/src/mbgl/util/image.cpp
new file mode 100644
index 0000000000..ee27a6e7d1
--- /dev/null
+++ b/src/mbgl/util/image.cpp
@@ -0,0 +1,110 @@
+#include <mbgl/util/image.hpp>
+
+namespace mbgl {
+
+ImageBase::ImageBase() = default;
+
+ImageBase::ImageBase(const Size& s, size_t channels)
+ : size_(std::move(s)),
+ data_(std::make_unique<uint8_t[]>(bytes(channels))) {
+}
+
+ImageBase::ImageBase(const Size& s, std::unique_ptr<uint8_t[]> d)
+ : size_(std::move(s)),
+ data_(std::move(d)) {}
+
+ImageBase::ImageBase(ImageBase&& o)
+ : size_(std::move(o.size_)),
+ data_(o.takeData()) {}
+
+ImageBase& ImageBase::operator=(ImageBase&& o) {
+ size_ = o.size_;
+ data_ = o.takeData();
+ return *this;
+}
+
+ImageBase::~ImageBase() {}
+
+bool ImageBase::valid() const {
+ return !size_.isEmpty() && data_ != nullptr;
+}
+
+size_t ImageBase::stride(size_t channels) const {
+ return channels * size_.width;
+}
+
+size_t ImageBase::bytes(size_t channels) const {
+ return channels * size_.area();
+}
+
+std::unique_ptr<uint8_t[]> ImageBase::takeData() {
+ size_ = Size{};
+ return std::move(data_);
+}
+
+void ImageBase::resize(const Size& s, size_t channels) {
+ if (s == size_) {
+ return;
+ }
+ const size_t bytesCount = channels * s.area();
+ if (bytesCount == 0u) {
+ takeData().reset();
+ return;
+ }
+ std::unique_ptr<uint8_t[]> newData{std::make_unique<uint8_t[]>(bytesCount)};
+ std::memset(newData.get(), 0, bytesCount);
+ ImageBase newImage(s, std::move(newData));
+ if (valid() && newImage.valid()) {
+ copy(*this, newImage, {0u, 0u}, {0u, 0u}, {
+ std::min(s.width, size_.width),
+ std::min(s.height, size_.height)
+ }, channels);
+ }
+ operator=(std::move(newImage));
+}
+
+void ImageBase::clear(const Point<uint32_t>& pt, const Size& clearedSize, size_t channels) {
+ assert(valid());
+ if (clearedSize.isEmpty()) {
+ return;
+ }
+ assert(clearedSize.width <= size_.width);
+ assert(clearedSize.height <= size_.height);
+ assert(pt.x <= size_.width - clearedSize.width);
+ assert(pt.y <= size_.height - clearedSize.height);
+
+ for (uint32_t y = 0u; y < clearedSize.height; ++y) {
+ const std::size_t dstOffset = (pt.y + y) * stride(channels) + pt.x * channels;
+ std::memset(data() + dstOffset, 0u, clearedSize.width * channels);
+ }
+}
+// static
+void ImageBase::copy(const ImageBase& srcImg, ImageBase& dstImg, const Point<uint32_t>& srcPt, const Point<uint32_t>& dstPt, const Size& size, size_t channels) {
+ assert(srcImg.valid());
+ assert(dstImg.valid());
+
+ assert(size.width <= srcImg.size().width);
+ assert(size.height <= srcImg.size().height);
+ assert(srcPt.x <= srcImg.size().width - size.width);
+ assert(srcPt.y <= srcImg.size().height - size.height);
+
+ assert(size.width <= dstImg.size().width);
+ assert(size.height <= dstImg.size().height);
+ assert(dstPt.x <= dstImg.size().width - size.width);
+ assert(dstPt.y <= dstImg.size().height - size.height);
+
+ const uint8_t* srcData = srcImg.data();
+ uint8_t* dstData = dstImg.data();
+
+ assert(srcData != dstData);
+
+ for (uint32_t y = 0u; y < size.height; ++y) {
+ const std::size_t srcOffset = (srcPt.y + y) * srcImg.stride(channels) + srcPt.x * channels;
+ const std::size_t dstOffset = (dstPt.y + y) * dstImg.stride(channels) + dstPt.x * channels;
+ std::copy(srcData + srcOffset,
+ srcData + srcOffset + size.width * channels,
+ dstData + dstOffset);
+ }
+}
+
+} // namespace mbgl
diff --git a/src/mbgl/util/premultiply.cpp b/src/mbgl/util/premultiply.cpp
index d9fb2480de..c03ac84f4c 100644
--- a/src/mbgl/util/premultiply.cpp
+++ b/src/mbgl/util/premultiply.cpp
@@ -6,13 +6,10 @@ namespace mbgl {
namespace util {
PremultipliedImage premultiply(UnassociatedImage&& src) {
- PremultipliedImage dst;
+ Size size = src.size();
+ PremultipliedImage dst(size, src.takeData());
- dst.size = src.size;
- src.size = { 0, 0 };
- dst.data = std::move(src.data);
-
- uint8_t* data = dst.data.get();
+ uint8_t* data = dst.data();
for (size_t i = 0; i < dst.bytes(); i += 4) {
uint8_t& r = data[i + 0];
uint8_t& g = data[i + 1];
@@ -27,13 +24,10 @@ PremultipliedImage premultiply(UnassociatedImage&& src) {
}
UnassociatedImage unpremultiply(PremultipliedImage&& src) {
- UnassociatedImage dst;
-
- dst.size = src.size;
- src.size = { 0, 0 };
- dst.data = std::move(src.data);
+ Size size = src.size();
+ UnassociatedImage dst(size, src.takeData());
- uint8_t* data = dst.data.get();
+ uint8_t* data = dst.data();
for (size_t i = 0; i < dst.bytes(); i += 4) {
uint8_t& r = data[i + 0];
uint8_t& g = data[i + 1];
diff --git a/src/mbgl/util/tiny_sdf.cpp b/src/mbgl/util/tiny_sdf.cpp
index 6edcd83bc2..926da6ee3f 100644
--- a/src/mbgl/util/tiny_sdf.cpp
+++ b/src/mbgl/util/tiny_sdf.cpp
@@ -71,10 +71,10 @@ void edt(std::vector<double>& data,
} // namespace tinysdf
AlphaImage transformRasterToSDF(const AlphaImage& rasterInput, double radius, double cutoff) {
- uint32_t size = rasterInput.size.width * rasterInput.size.height;
- uint32_t maxDimension = std::max(rasterInput.size.width, rasterInput.size.height);
+ uint32_t size = rasterInput.size().area();
+ uint32_t maxDimension = std::max(rasterInput.size().width, rasterInput.size().height);
- AlphaImage sdf(rasterInput.size);
+ AlphaImage sdf(rasterInput.size());
// temporary arrays for the distance transform
std::vector<double> gridOuter(size);
@@ -85,17 +85,17 @@ AlphaImage transformRasterToSDF(const AlphaImage& rasterInput, double radius, do
std::vector<int16_t> v(maxDimension);
for (uint32_t i = 0; i < size; i++) {
- double a = double(rasterInput.data[i]) / 255; // alpha value
+ double a = double(rasterInput.data()[i]) / 255; // alpha value
gridOuter[i] = a == 1.0 ? 0.0 : a == 0.0 ? tinysdf::INF : std::pow(std::max(0.0, 0.5 - a), 2.0);
gridInner[i] = a == 1.0 ? tinysdf::INF : a == 0.0 ? 0.0 : std::pow(std::max(0.0, a - 0.5), 2.0);
}
- tinysdf::edt(gridOuter, rasterInput.size.width, rasterInput.size.height, f, d, v, z);
- tinysdf::edt(gridInner, rasterInput.size.width, rasterInput.size.height, f, d, v, z);
+ tinysdf::edt(gridOuter, rasterInput.size().width, rasterInput.size().height, f, d, v, z);
+ tinysdf::edt(gridInner, rasterInput.size().width, rasterInput.size().height, f, d, v, z);
for (uint32_t i = 0; i < size; i++) {
double distance = gridOuter[i] - gridInner[i];
- sdf.data[i] = std::max(0l, std::min(255l, ::lround(255.0 - 255.0 * (distance / radius + cutoff))));
+ sdf.data()[i] = std::max(0l, std::min(255l, ::lround(255.0 - 255.0 * (distance / radius + cutoff))));
}
return sdf;
diff --git a/test/geometry/dem_data.test.cpp b/test/geometry/dem_data.test.cpp
index 3848f028f1..8c1455cd7a 100644
--- a/test/geometry/dem_data.test.cpp
+++ b/test/geometry/dem_data.test.cpp
@@ -10,7 +10,7 @@ auto fakeImage = [](Size s) {
PremultipliedImage img = PremultipliedImage(s);
for (size_t i = 0; i < img.bytes(); i ++) {
- img.data[i] = (i+1) % 4 == 0 ? 1 : std::rand() % 255;
+ img.data()[i] = (i+1) % 4 == 0 ? 1 : std::rand() % 255;
}
return img;
};
diff --git a/test/renderer/image_manager.test.cpp b/test/renderer/image_manager.test.cpp
index 4a838d0f9c..c6a1d39bcd 100644
--- a/test/renderer/image_manager.test.cpp
+++ b/test/renderer/image_manager.test.cpp
@@ -39,7 +39,7 @@ TEST(ImageManager, Basic) {
EXPECT_EQ(18, metro.displaySize()[0]);
EXPECT_EQ(18, metro.displaySize()[1]);
EXPECT_EQ(1.0f, metro.pixelRatio);
- EXPECT_EQ(imageManager.getPixelSize(), imageManager.getAtlasImage().size);
+ EXPECT_EQ(imageManager.getPixelSize(), imageManager.getAtlasImage().size());
test::checkImage("test/fixtures/image_manager/basic", imageManager.getAtlasImage());
}
@@ -48,7 +48,7 @@ TEST(ImageManager, Updates) {
ImageManager imageManager;
PremultipliedImage imageA({ 16, 12 });
- imageA.fill(255);
+ std::fill(imageA.data(), imageA.data() + imageA.bytes(), 255);
imageManager.addImage(makeMutable<style::Image::Impl>("one", std::move(imageA), 1));
auto a = *imageManager.getPattern("one");
@@ -62,7 +62,7 @@ TEST(ImageManager, Updates) {
test::checkImage("test/fixtures/image_manager/updates_before", imageManager.getAtlasImage());
PremultipliedImage imageB({ 5, 5 });
- imageA.fill(200);
+ std::fill(imageA.data(), imageA.data() + imageA.bytes(), 200);
imageManager.updateImage(makeMutable<style::Image::Impl>("one", std::move(imageB), 1));
auto b = *imageManager.getPattern("one");
diff --git a/test/sprite/sprite_parser.test.cpp b/test/sprite/sprite_parser.test.cpp
index 529e4c75e8..544c7c41d8 100644
--- a/test/sprite/sprite_parser.test.cpp
+++ b/test/sprite/sprite_parser.test.cpp
@@ -24,8 +24,8 @@ TEST(Sprite, SpriteImageCreationInvalid) {
const PremultipliedImage image_1x = decodeImage(util::read_file("test/fixtures/annotations/emerald.png"));
- ASSERT_EQ(200u, image_1x.size.width);
- ASSERT_EQ(299u, image_1x.size.height);
+ ASSERT_EQ(200u, image_1x.size().width);
+ ASSERT_EQ(299u, image_1x.size().height);
ASSERT_EQ(nullptr, createStyleImage("test", image_1x, 0, 0, 0, 16, 1, false)); // width == 0
ASSERT_EQ(nullptr, createStyleImage("test", image_1x, 0, 0, 16, 0, 1, false)); // height == 0
@@ -38,8 +38,8 @@ TEST(Sprite, SpriteImageCreationInvalid) {
ASSERT_EQ(nullptr, createStyleImage("test", image_1x, 0, 0, 16, 1025, 1, false)); // too tall
ASSERT_EQ(nullptr, createStyleImage("test", image_1x, -1, 0, 16, 16, 1, false)); // srcX < 0
ASSERT_EQ(nullptr, createStyleImage("test", image_1x, 0, -1, 16, 16, 1, false)); // srcY < 0
- ASSERT_EQ(nullptr, createStyleImage("test", image_1x, 0, 0, image_1x.size.width + 1, 16, 1, false)); // right edge out of bounds
- ASSERT_EQ(nullptr, createStyleImage("test", image_1x, 0, 0, 16, image_1x.size.height + 1, 1, false)); // bottom edge out of bounds
+ ASSERT_EQ(nullptr, createStyleImage("test", image_1x, 0, 0, image_1x.size().width + 1, 16, 1, false)); // right edge out of bounds
+ ASSERT_EQ(nullptr, createStyleImage("test", image_1x, 0, 0, 16, image_1x.size().height + 1, 1, false)); // bottom edge out of bounds
EXPECT_EQ(1u, log.count({
EventSeverity::Error,
@@ -137,14 +137,14 @@ TEST(Sprite, SpriteImageCreationInvalid) {
TEST(Sprite, SpriteImageCreation1x) {
const PremultipliedImage image_1x = decodeImage(util::read_file("test/fixtures/annotations/emerald.png"));
- ASSERT_EQ(200u, image_1x.size.width);
- ASSERT_EQ(299u, image_1x.size.height);
+ ASSERT_EQ(200u, image_1x.size().width);
+ ASSERT_EQ(299u, image_1x.size().height);
{ // "museum_icon":{"x":177,"y":187,"width":18,"height":18,"pixelRatio":1,"sdf":false}
const auto sprite = createStyleImage("test", image_1x, 177, 187, 18, 18, 1, false);
ASSERT_TRUE(sprite.get());
- EXPECT_EQ(18u, sprite->getImage().size.width);
- EXPECT_EQ(18u, sprite->getImage().size.height);
+ EXPECT_EQ(18u, sprite->getImage().size().width);
+ EXPECT_EQ(18u, sprite->getImage().size().height);
EXPECT_EQ(1, sprite->getPixelRatio());
EXPECT_EQ(readImage("test/fixtures/annotations/result-spriteimagecreation1x-museum.png"),
sprite->getImage());
@@ -157,8 +157,8 @@ TEST(Sprite, SpriteImageCreation2x) {
// "museum_icon":{"x":354,"y":374,"width":36,"height":36,"pixelRatio":2,"sdf":false}
const auto sprite = createStyleImage("test", image_2x, 354, 374, 36, 36, 2, false);
ASSERT_TRUE(sprite.get());
- EXPECT_EQ(36u, sprite->getImage().size.width);
- EXPECT_EQ(36u, sprite->getImage().size.height);
+ EXPECT_EQ(36u, sprite->getImage().size().width);
+ EXPECT_EQ(36u, sprite->getImage().size().height);
EXPECT_EQ(2, sprite->getPixelRatio());
EXPECT_EQ(readImage("test/fixtures/annotations/result-spriteimagecreation2x.png"),
sprite->getImage());
@@ -170,8 +170,8 @@ TEST(Sprite, SpriteImageCreation1_5x) {
// "museum_icon":{"x":354,"y":374,"width":36,"height":36,"pixelRatio":2,"sdf":false}
const auto sprite = createStyleImage("test", image_2x, 354, 374, 36, 36, 1.5, false);
ASSERT_TRUE(sprite.get());
- EXPECT_EQ(36u, sprite->getImage().size.width);
- EXPECT_EQ(36u, sprite->getImage().size.height);
+ EXPECT_EQ(36u, sprite->getImage().size().width);
+ EXPECT_EQ(36u, sprite->getImage().size().height);
EXPECT_EQ(1.5, sprite->getPixelRatio());
EXPECT_EQ(readImage("test/fixtures/annotations/result-spriteimagecreation1_5x-museum.png"),
sprite->getImage());
@@ -179,8 +179,8 @@ TEST(Sprite, SpriteImageCreation1_5x) {
// "hospital_icon":{"x":314,"y":518,"width":36,"height":36,"pixelRatio":2,"sdf":false}
const auto sprite2 = createStyleImage("test", image_2x, 314, 518, 35, 35, 1.5, false);
ASSERT_TRUE(sprite2.get());
- EXPECT_EQ(35u, sprite2->getImage().size.width);
- EXPECT_EQ(35u, sprite2->getImage().size.height);
+ EXPECT_EQ(35u, sprite2->getImage().size().width);
+ EXPECT_EQ(35u, sprite2->getImage().size().height);
EXPECT_EQ(1.5, sprite2->getPixelRatio());
EXPECT_EQ(readImage("test/fixtures/annotations/result-spriteimagecreation1_5x-hospital.png"),
sprite2->getImage());
@@ -273,8 +273,8 @@ TEST(Sprite, SpriteParsing) {
{
auto& sprite = *std::find_if(images.begin(), images.end(), [] (const auto& image) { return image->getID() == "generic-metro"; });
- EXPECT_EQ(18u, sprite->getImage().size.width);
- EXPECT_EQ(18u, sprite->getImage().size.height);
+ EXPECT_EQ(18u, sprite->getImage().size().width);
+ EXPECT_EQ(18u, sprite->getImage().size().height);
EXPECT_EQ(1, sprite->getPixelRatio());
EXPECT_EQ(readImage("test/fixtures/annotations/result-spriteparsing.png"), sprite->getImage());
}
diff --git a/test/src/mbgl/test/util.cpp b/test/src/mbgl/test/util.cpp
index 30eea19bdf..5d3cccb464 100644
--- a/test/src/mbgl/test/util.cpp
+++ b/test/src/mbgl/test/util.cpp
@@ -109,23 +109,23 @@ void checkImage(const std::string& base,
}
PremultipliedImage expected = decodeImage(expected_image);
- PremultipliedImage diff { expected.size };
+ PremultipliedImage diff { expected.size() };
#if !TEST_READ_ONLY
util::write_file(base + "/actual.png", encodePNG(actual));
#endif
- ASSERT_EQ(expected.size, actual.size);
+ ASSERT_EQ(expected.size(), actual.size());
- double pixels = mapbox::pixelmatch(actual.data.get(),
- expected.data.get(),
- expected.size.width,
- expected.size.height,
- diff.data.get(),
+ double pixels = mapbox::pixelmatch(actual.data(),
+ expected.data(),
+ expected.size().width,
+ expected.size().height,
+ diff.data(),
pixelThreshold);
- EXPECT_LE(pixels / (expected.size.width * expected.size.height), imageThreshold);
+ EXPECT_LE(pixels / expected.size().area(), imageThreshold);
#if !TEST_READ_ONLY
util::write_file(base + "/diff.png", encodePNG(diff));
diff --git a/test/style/source.test.cpp b/test/style/source.test.cpp
index abd4350231..a8059198d3 100644
--- a/test/style/source.test.cpp
+++ b/test/style/source.test.cpp
@@ -730,10 +730,10 @@ TEST(Source, ImageSourceImageUpdate) {
// Load initial, so the source state will be loaded=true
source.loadDescription(test.fileSource);
PremultipliedImage rgba({ 1, 1 });
- rgba.data[0] = 255;
- rgba.data[1] = 254;
- rgba.data[2] = 253;
- rgba.data[3] = 0;
+ rgba.data()[0] = 255;
+ rgba.data()[1] = 254;
+ rgba.data()[2] = 253;
+ rgba.data()[3] = 0;
// Schedule an update
test.loop.invoke([&] () {
diff --git a/test/style/style_image.test.cpp b/test/style/style_image.test.cpp
index e49bf37582..d703570e18 100644
--- a/test/style/style_image.test.cpp
+++ b/test/style/style_image.test.cpp
@@ -35,14 +35,14 @@ TEST(StyleImage, ZeroRatio) {
TEST(StyleImage, Retina) {
style::Image image("test", PremultipliedImage({ 32, 24 }), 2.0);
- EXPECT_EQ(32u, image.getImage().size.width);
- EXPECT_EQ(24u, image.getImage().size.height);
+ EXPECT_EQ(32u, image.getImage().size().width);
+ EXPECT_EQ(24u, image.getImage().size().height);
EXPECT_EQ(2, image.getPixelRatio());
}
TEST(StyleImage, FractionalRatio) {
style::Image image("test", PremultipliedImage({ 20, 12 }), 1.5);
- EXPECT_EQ(20u, image.getImage().size.width);
- EXPECT_EQ(12u, image.getImage().size.height);
+ EXPECT_EQ(20u, image.getImage().size().width);
+ EXPECT_EQ(12u, image.getImage().size().height);
EXPECT_EQ(1.5, image.getPixelRatio());
}
diff --git a/test/text/glyph_manager.test.cpp b/test/text/glyph_manager.test.cpp
index 3d7a220dc5..6cec11b80f 100644
--- a/test/text/glyph_manager.test.cpp
+++ b/test/text/glyph_manager.test.cpp
@@ -32,8 +32,9 @@ public:
stub.metrics.left = 0;
stub.metrics.top = -8;
stub.metrics.advance = 24;
-
- stub.bitmap = AlphaImage(Size(30, 30), stubBitmap, stubBitmapLength);
+ std::unique_ptr<uint8_t[]> imageData{std::make_unique<uint8_t[]>(stubBitmapLength)};
+ std::copy(stubBitmap, stubBitmap + stubBitmapLength, imageData.get());
+ stub.bitmap = AlphaImage(Size(30, 30), std::move(imageData));
return stub;
}
@@ -236,11 +237,11 @@ TEST(GlyphManager, LoadLocalCJKGlyph) {
EXPECT_EQ(glyph->metrics.left, 0);
EXPECT_EQ(glyph->metrics.top, -8);
EXPECT_EQ(glyph->metrics.advance, 24ul);
- EXPECT_EQ(glyph->bitmap.size, Size(30, 30));
+ EXPECT_EQ(glyph->bitmap.size(), Size(30, 30));
- size_t pixelCount = glyph->bitmap.size.width * glyph->bitmap.size.height;
+ size_t pixelCount = glyph->bitmap.size().area();
for (size_t i = 0; i < pixelCount; i++) {
- EXPECT_EQ(glyph->bitmap.data[i], sdfBitmap[i]);
+ EXPECT_EQ(glyph->bitmap.data()[i], sdfBitmap[i]);
}
test.end();
diff --git a/test/text/glyph_pbf.test.cpp b/test/text/glyph_pbf.test.cpp
index c222ec1dd9..770ed1f949 100644
--- a/test/text/glyph_pbf.test.cpp
+++ b/test/text/glyph_pbf.test.cpp
@@ -13,7 +13,7 @@ TEST(GlyphPBF, Parsing) {
auto& sdf = sdfs[0];
EXPECT_EQ(69u, sdf.id);
AlphaImage expected({7, 7});
- expected.fill('x');
+ std::fill(expected.data(), expected.data() + expected.bytes(), 'x');
EXPECT_EQ(expected, sdf.bitmap);
EXPECT_EQ(1u, sdf.metrics.width);
EXPECT_EQ(1u, sdf.metrics.height);
diff --git a/test/util/image.test.cpp b/test/util/image.test.cpp
index c2922415cb..016ea9c905 100644
--- a/test/util/image.test.cpp
+++ b/test/util/image.test.cpp
@@ -8,150 +8,142 @@ using namespace mbgl;
TEST(Image, PNGRoundTrip) {
PremultipliedImage rgba({ 1, 1 });
- rgba.data[0] = 128;
- rgba.data[1] = 0;
- rgba.data[2] = 0;
- rgba.data[3] = 255;
+ rgba.data()[0] = 128;
+ rgba.data()[1] = 0;
+ rgba.data()[2] = 0;
+ rgba.data()[3] = 255;
PremultipliedImage image = decodeImage(encodePNG(rgba));
- EXPECT_EQ(128, image.data[0]);
- EXPECT_EQ(0, image.data[1]);
- EXPECT_EQ(0, image.data[2]);
- EXPECT_EQ(255, image.data[3]);
+ EXPECT_EQ(128, image.data()[0]);
+ EXPECT_EQ(0, image.data()[1]);
+ EXPECT_EQ(0, image.data()[2]);
+ EXPECT_EQ(255, image.data()[3]);
}
TEST(Image, PNGRoundTripAlpha) {
PremultipliedImage rgba({ 1, 1 });
- rgba.data[0] = 128;
- rgba.data[1] = 0;
- rgba.data[2] = 0;
- rgba.data[3] = 128;
+ rgba.data()[0] = 128;
+ rgba.data()[1] = 0;
+ rgba.data()[2] = 0;
+ rgba.data()[3] = 128;
PremultipliedImage image = decodeImage(encodePNG(rgba));
- EXPECT_EQ(128, image.data[0]);
- EXPECT_EQ(0, image.data[1]);
- EXPECT_EQ(0, image.data[2]);
- EXPECT_EQ(128, image.data[3]);
+ EXPECT_EQ(128, image.data()[0]);
+ EXPECT_EQ(0, image.data()[1]);
+ EXPECT_EQ(0, image.data()[2]);
+ EXPECT_EQ(128, image.data()[3]);
}
TEST(Image, PNGReadNoProfile) {
PremultipliedImage image = decodeImage(util::read_file("test/fixtures/image/no_profile.png"));
- EXPECT_EQ(128, image.data[0]);
- EXPECT_EQ(0, image.data[1]);
- EXPECT_EQ(0, image.data[2]);
- EXPECT_EQ(255, image.data[3]);
+ EXPECT_EQ(128, image.data()[0]);
+ EXPECT_EQ(0, image.data()[1]);
+ EXPECT_EQ(0, image.data()[2]);
+ EXPECT_EQ(255, image.data()[3]);
}
TEST(Image, PNGReadNoProfileAlpha) {
PremultipliedImage image = decodeImage(util::read_file("test/fixtures/image/no_profile_alpha.png"));
- EXPECT_EQ(64, image.data[0]);
- EXPECT_EQ(0, image.data[1]);
- EXPECT_EQ(0, image.data[2]);
- EXPECT_EQ(128, image.data[3]);
+ EXPECT_EQ(64, image.data()[0]);
+ EXPECT_EQ(0, image.data()[1]);
+ EXPECT_EQ(0, image.data()[2]);
+ EXPECT_EQ(128, image.data()[3]);
}
TEST(Image, PNGReadProfile) {
PremultipliedImage image = decodeImage(util::read_file("test/fixtures/image/profile.png"));
- EXPECT_EQ(128, image.data[0]);
- EXPECT_EQ(0, image.data[1]);
- EXPECT_EQ(0, image.data[2]);
- EXPECT_EQ(255, image.data[3]);
+ EXPECT_EQ(128, image.data()[0]);
+ EXPECT_EQ(0, image.data()[1]);
+ EXPECT_EQ(0, image.data()[2]);
+ EXPECT_EQ(255, image.data()[3]);
}
TEST(Image, PNGReadProfileAlpha) {
PremultipliedImage image = decodeImage(util::read_file("test/fixtures/image/profile_alpha.png"));
- EXPECT_EQ(64, image.data[0]);
- EXPECT_EQ(0, image.data[1]);
- EXPECT_EQ(0, image.data[2]);
- EXPECT_EQ(128, image.data[3]);
+ EXPECT_EQ(64, image.data()[0]);
+ EXPECT_EQ(0, image.data()[1]);
+ EXPECT_EQ(0, image.data()[2]);
+ EXPECT_EQ(128, image.data()[3]);
}
TEST(Image, PNGTile) {
PremultipliedImage image = decodeImage(util::read_file("test/fixtures/image/tile.png"));
- EXPECT_EQ(256u, image.size.width);
- EXPECT_EQ(256u, image.size.height);
+ EXPECT_EQ(256u, image.size().width);
+ EXPECT_EQ(256u, image.size().height);
}
TEST(Image, JPEGTile) {
PremultipliedImage image = decodeImage(util::read_file("test/fixtures/image/tile.jpeg"));
- EXPECT_EQ(256u, image.size.width);
- EXPECT_EQ(256u, image.size.height);
+ EXPECT_EQ(256u, image.size().width);
+ EXPECT_EQ(256u, image.size().height);
}
TEST(Image, Resize) {
AlphaImage image({0, 0});
image.resize({1, 1});
- EXPECT_EQ(image.size, Size({1, 1}));
+ EXPECT_EQ(image.size(), Size({1, 1}));
- image.fill(100);
+ std::fill(image.data(), image.data() + image.bytes(), 100);
image.resize({2, 1});
- EXPECT_EQ(image.size, Size({2, 1}));
- EXPECT_EQ(image.data[0], 100);
- EXPECT_EQ(image.data[1], 0);
+ EXPECT_EQ(image.size(), Size({2, 1}));
+ EXPECT_EQ(image.data()[0], 100);
+ EXPECT_EQ(image.data()[1], 0);
image.resize({0, 0});
- EXPECT_EQ(image.size, Size({0, 0}));
+ EXPECT_EQ(image.size(), Size({0, 0}));
+ EXPECT_EQ(image.data(), nullptr);
+ EXPECT_FALSE(image.valid());
}
-TEST(Image, Copy) {
- PremultipliedImage src5({5, 5});
- PremultipliedImage dst5({5, 5});
- PremultipliedImage src10({10, 10});
- PremultipliedImage dst10({10, 10});
-
- EXPECT_THROW(PremultipliedImage::copy(src5, dst10, {0, 0}, {0, 0}, {6, 1}), std::out_of_range);
- EXPECT_THROW(PremultipliedImage::copy(src5, dst10, {0, 0}, {0, 0}, {1, 6}), std::out_of_range);
- EXPECT_THROW(PremultipliedImage::copy(src5, dst10, {1, 1}, {0, 0}, {5, 1}), std::out_of_range);
- EXPECT_THROW(PremultipliedImage::copy(src5, dst10, {1, 1}, {0, 0}, {1, 5}), std::out_of_range);
-
- EXPECT_THROW(PremultipliedImage::copy(src10, dst5, {0, 0}, {0, 0}, {6, 1}), std::out_of_range);
- EXPECT_THROW(PremultipliedImage::copy(src10, dst5, {0, 0}, {0, 0}, {1, 6}), std::out_of_range);
- EXPECT_THROW(PremultipliedImage::copy(src10, dst5, {0, 0}, {1, 1}, {5, 1}), std::out_of_range);
- EXPECT_THROW(PremultipliedImage::copy(src10, dst5, {0, 0}, {1, 1}, {1, 5}), std::out_of_range);
-
- const uint32_t max = std::numeric_limits<uint32_t>::max();
-
- EXPECT_THROW(PremultipliedImage::copy(src10, dst10, {max, 0}, {0, 0}, {1, 1}), std::out_of_range);
- EXPECT_THROW(PremultipliedImage::copy(src10, dst10, {0, max}, {0, 0}, {1, 1}), std::out_of_range);
- EXPECT_THROW(PremultipliedImage::copy(src10, dst10, {0, 0}, {max, 0}, {1, 1}), std::out_of_range);
- EXPECT_THROW(PremultipliedImage::copy(src10, dst10, {0, 0}, {0, max}, {1, 1}), std::out_of_range);
-
- EXPECT_THROW(PremultipliedImage::copy(src10, dst10, {1, 0}, {0, 0}, {max, 1}), std::out_of_range);
- EXPECT_THROW(PremultipliedImage::copy(src10, dst10, {0, 1}, {0, 0}, {1, max}), std::out_of_range);
- EXPECT_THROW(PremultipliedImage::copy(src10, dst10, {0, 0}, {1, 0}, {max, 1}), std::out_of_range);
- EXPECT_THROW(PremultipliedImage::copy(src10, dst10, {0, 0}, {0, 1}, {1, max}), std::out_of_range);
+TEST(Image, TakeData) {
+ UnassociatedImage rgba({ 1, 1 });
+ rgba.data()[0] = 255;
+ rgba.data()[1] = 254;
+ rgba.data()[2] = 253;
+ rgba.data()[3] = 128;
+
+ auto data = rgba.takeData();
+
+ EXPECT_EQ(255, data[0]);
+ EXPECT_EQ(254, data[1]);
+ EXPECT_EQ(253, data[2]);
+ EXPECT_EQ(128, data[3]);
+
+ EXPECT_EQ(Size(), rgba.size());
+ EXPECT_EQ(nullptr, rgba.data());
+ EXPECT_FALSE(rgba.valid());
}
TEST(Image, Move) {
UnassociatedImage rgba({ 1, 1 });
- rgba.data[0] = 255;
- rgba.data[1] = 254;
- rgba.data[2] = 253;
- rgba.data[3] = 128;
+ rgba.data()[0] = 255;
+ rgba.data()[1] = 254;
+ rgba.data()[2] = 253;
+ rgba.data()[3] = 128;
auto moved = std::move(rgba);
- EXPECT_EQ(0u, rgba.size.width);
- EXPECT_EQ(nullptr, rgba.data.get());
- EXPECT_EQ(254, moved.data[1]);
- EXPECT_EQ(1u, moved.size.width);
+ EXPECT_EQ(0u, rgba.size().width);
+ EXPECT_EQ(nullptr, rgba.data());
+ EXPECT_EQ(254, moved.data()[1]);
+ EXPECT_EQ(1u, moved.size().width);
}
TEST(Image, Premultiply) {
UnassociatedImage rgba({ 1, 1 });
- rgba.data[0] = 255;
- rgba.data[1] = 254;
- rgba.data[2] = 253;
- rgba.data[3] = 128;
+ rgba.data()[0] = 255;
+ rgba.data()[1] = 254;
+ rgba.data()[2] = 253;
+ rgba.data()[3] = 128;
PremultipliedImage image = util::premultiply(std::move(rgba));
- EXPECT_EQ(128, image.data[0]);
- EXPECT_EQ(127, image.data[1]);
- EXPECT_EQ(127, image.data[2]);
- EXPECT_EQ(128, image.data[3]);
- EXPECT_EQ(1u, image.size.width);
- EXPECT_EQ(1u, image.size.height);
- EXPECT_EQ(0u, rgba.size.width);
- EXPECT_EQ(0u, rgba.size.height);
+ EXPECT_EQ(128, image.data()[0]);
+ EXPECT_EQ(127, image.data()[1]);
+ EXPECT_EQ(127, image.data()[2]);
+ EXPECT_EQ(128, image.data()[3]);
+ EXPECT_EQ(1u, image.size().width);
+ EXPECT_EQ(1u, image.size().height);
+ EXPECT_EQ(0u, rgba.size().width);
+ EXPECT_EQ(0u, rgba.size().height);
}