fix sprites for pixel ratios that are not 1 and 2
- OpenGL ES 2 doesn't allow NPOT textures with wrap-around - The Sprite object reported the map's pixelRatio, even though it loaded @2x assets - Copying icons from the sprite into the atlas now uses bilinear scaling to scale up to the actual size
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -15,6 +15,7 @@
|
||||
/ios/app/build
|
||||
/test/build
|
||||
/test/node_modules
|
||||
/test/fixtures/*/*_actual.*
|
||||
/include/mbgl/shader/shaders.hpp
|
||||
/src/shader/shaders_gl.cpp
|
||||
/src/shader/shaders_gles2.cpp
|
||||
|
||||
@@ -10,12 +10,11 @@
|
||||
'../platform/default/sqlite_cache.cpp',
|
||||
'../platform/default/sqlite3.hpp',
|
||||
'../platform/default/sqlite3.cpp',
|
||||
'../platform/default/compression.hpp',
|
||||
'../platform/default/compression.cpp',
|
||||
],
|
||||
|
||||
'include_dirs': [
|
||||
'../include',
|
||||
'../src',
|
||||
],
|
||||
|
||||
'variables': {
|
||||
|
||||
@@ -24,10 +24,10 @@ const std::string &applicationRoot();
|
||||
const std::string &assetRoot();
|
||||
|
||||
// Shows an alpha image with the specified dimensions in a named window.
|
||||
void show_debug_image(std::string name, const char *data, size_t width, size_t height);
|
||||
void showDebugImage(std::string name, const char *data, size_t width, size_t height);
|
||||
|
||||
// Shows an alpha image with the specified dimensions in a named window.
|
||||
void show_color_debug_image(std::string name, const char *data, size_t logical_width, size_t logical_height, size_t width, size_t height);
|
||||
void showColorDebugImage(std::string name, const char *data, size_t logical_width, size_t logical_height, size_t width, size_t height);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,10 +5,10 @@
|
||||
#include <mbgl/util/util.hpp>
|
||||
#include <mbgl/util/async_queue.hpp>
|
||||
#include <mbgl/util/variant.hpp>
|
||||
#include <mbgl/util/compression.hpp>
|
||||
#include <mbgl/platform/log.hpp>
|
||||
|
||||
#include "sqlite3.hpp"
|
||||
#include "compression.hpp"
|
||||
|
||||
#include <uv.h>
|
||||
|
||||
|
||||
@@ -31,6 +31,9 @@ if [[ ${TRAVIS_OS_NAME} == "linux" ]]; then
|
||||
x11proto-xf86vidmode-dev libxxf86vm-dev \
|
||||
libxcursor-dev libxinerama-dev \
|
||||
llvm-3.4 # required for mesa
|
||||
|
||||
mapbox_time "install_mesa" \
|
||||
mason install mesa 10.4.3
|
||||
fi
|
||||
|
||||
mapbox_time "install_awscli" \
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <mbgl/util/math.hpp>
|
||||
#include <mbgl/util/std.hpp>
|
||||
#include <mbgl/util/constants.hpp>
|
||||
#include <mbgl/util/scaling.hpp>
|
||||
|
||||
#include <mbgl/map/sprite.hpp>
|
||||
|
||||
@@ -66,34 +67,6 @@ bool SpriteAtlas::resize(const float newRatio) {
|
||||
return dirty;
|
||||
}
|
||||
|
||||
void copy_bitmap(const uint32_t *src, const int src_stride, const int src_x, const int src_y,
|
||||
uint32_t *dst, const int dst_stride, const int dst_height, const int dst_x, const int dst_y,
|
||||
const int width, const int height, const bool wrap) {
|
||||
if (wrap) {
|
||||
|
||||
for (int y = -1; y <= height; y++) {
|
||||
int dst_y_wrapped = (y + dst_y + dst_height) % dst_height;
|
||||
int src_y_wrapped = ((y + height) % height) + src_y;
|
||||
int srcI = src_y_wrapped * src_stride + src_x;
|
||||
int dstI = dst_y_wrapped * dst_stride;
|
||||
for (int x = -1; x <= width; x++) {
|
||||
int dst_x_wrapped = (x + dst_x + dst_stride) % dst_stride;
|
||||
int src_x_wrapped = (x + width) % width;
|
||||
dst[dstI + dst_x_wrapped] = src[srcI + src_x_wrapped];
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
dst += dst_y * dst_stride + dst_x;
|
||||
src += src_y * src_stride + src_x;
|
||||
for (int y = 0; y < height; y++, src += src_stride, dst += dst_stride) {
|
||||
for (int x = 0; x < width; x++) {
|
||||
dst[x] = src[x];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rect<SpriteAtlas::dimension> SpriteAtlas::allocateImage(const size_t pixel_width, const size_t pixel_height) {
|
||||
// Increase to next number divisible by 4, but at least 1.
|
||||
// This is so we can scale down the texture coordinates and pack them
|
||||
@@ -176,25 +149,53 @@ void SpriteAtlas::allocate() {
|
||||
|
||||
void SpriteAtlas::copy(const Rect<dimension>& dst, const SpritePosition& src, const bool wrap) {
|
||||
if (!sprite->raster) return;
|
||||
const uint32_t *src_img = reinterpret_cast<const uint32_t *>(sprite->raster->getData());
|
||||
if (!src_img) return;
|
||||
allocate();
|
||||
uint32_t *dst_img = reinterpret_cast<uint32_t *>(data);
|
||||
|
||||
copy_bitmap(
|
||||
/* source buffer */ src_img,
|
||||
/* source stride */ sprite->raster->getWidth(),
|
||||
/* source x */ src.x,
|
||||
/* source y */ src.y,
|
||||
/* dest buffer */ dst_img,
|
||||
/* dest stride */ width * pixelRatio,
|
||||
/* dest height */ height * pixelRatio,
|
||||
/* dest x */ dst.x * pixelRatio,
|
||||
/* dest y */ dst.y * pixelRatio,
|
||||
/* icon dimension */ src.width,
|
||||
/* icon dimension */ src.height,
|
||||
/* wrap padding */ wrap
|
||||
);
|
||||
const uint32_t *srcData = reinterpret_cast<const uint32_t *>(sprite->raster->getData());
|
||||
if (!srcData) return;
|
||||
const vec2<uint32_t> srcSize { sprite->raster->getWidth(), sprite->raster->getHeight() };
|
||||
const Rect<uint32_t> srcPos { src.x, src.y, src.width, src.height };
|
||||
|
||||
allocate();
|
||||
uint32_t *dstData = reinterpret_cast<uint32_t *>(data);
|
||||
const vec2<uint32_t> dstSize { static_cast<unsigned int>(width * pixelRatio),
|
||||
static_cast<unsigned int>(height * pixelRatio) };
|
||||
const Rect<uint32_t> dstPos { static_cast<uint32_t>(dst.x * pixelRatio),
|
||||
static_cast<uint32_t>(dst.y * pixelRatio),
|
||||
static_cast<uint32_t>(dst.originalW * pixelRatio),
|
||||
static_cast<uint32_t>(dst.originalH * pixelRatio) };
|
||||
|
||||
util::bilinearScale(srcData, srcSize, srcPos, dstData, dstSize, dstPos);
|
||||
|
||||
// Add borders around the copied image if required.
|
||||
if (wrap) {
|
||||
// We're copying from the same image so we don't have to scale again.
|
||||
const uint32_t border = 1;
|
||||
// Left border
|
||||
if (dstPos.x >= border) {
|
||||
util::nearestNeighborScale(
|
||||
dstData, dstSize, { dstPos.x + dstPos.w - border - 1, dstPos.y, border, dstPos.h },
|
||||
dstData, dstSize, { dstPos.x - border, dstPos.y, border, dstPos.h });
|
||||
}
|
||||
// Right border
|
||||
util::nearestNeighborScale(dstData, dstSize, { dstPos.x, dstPos.y, border, dstPos.h },
|
||||
dstData, dstSize,
|
||||
{ dstPos.x + dstPos.w, dstPos.y, border, dstPos.h });
|
||||
|
||||
// Top border
|
||||
if (dstPos.y >= border) {
|
||||
util::nearestNeighborScale(
|
||||
dstData, dstSize, { dstPos.x - border, dstPos.y + dstPos.h - border - 1,
|
||||
dstPos.w + 2 * border, border },
|
||||
dstData, dstSize,
|
||||
{ dstPos.x - border, dstPos.y - border, dstPos.w + 2 * border, border });
|
||||
}
|
||||
|
||||
// Bottom border
|
||||
util::nearestNeighborScale(
|
||||
dstData, dstSize, { dstPos.x - border, dstPos.y, dstPos.w + 2 * border, border },
|
||||
dstData, dstSize,
|
||||
{ dstPos.x - border, dstPos.y + dstPos.h, dstPos.w + 2 * border, border });
|
||||
}
|
||||
|
||||
dirty = true;
|
||||
}
|
||||
@@ -236,8 +237,10 @@ void SpriteAtlas::bind(bool linear) {
|
||||
#ifndef GL_ES_VERSION_2_0
|
||||
MBGL_CHECK_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0));
|
||||
#endif
|
||||
MBGL_CHECK_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT));
|
||||
MBGL_CHECK_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT));
|
||||
// We are using clamp to edge here since OpenGL ES doesn't allow GL_REPEAT on NPOT textures.
|
||||
// We use those when the pixelRatio isn't a power of two, e.g. on iPhone 6 Plus.
|
||||
MBGL_CHECK_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE));
|
||||
MBGL_CHECK_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE));
|
||||
first = true;
|
||||
} else {
|
||||
MBGL_CHECK_ERROR(glBindTexture(GL_TEXTURE_2D, texture));
|
||||
@@ -255,7 +258,7 @@ void SpriteAtlas::bind(bool linear) {
|
||||
allocate();
|
||||
|
||||
if (first) {
|
||||
glTexImage2D(
|
||||
MBGL_CHECK_ERROR(glTexImage2D(
|
||||
GL_TEXTURE_2D, // GLenum target
|
||||
0, // GLint level
|
||||
GL_RGBA, // GLint internalformat
|
||||
@@ -265,9 +268,9 @@ void SpriteAtlas::bind(bool linear) {
|
||||
GL_RGBA, // GLenum format
|
||||
GL_UNSIGNED_BYTE, // GLenum type
|
||||
data // const GLvoid * data
|
||||
);
|
||||
));
|
||||
} else {
|
||||
glTexSubImage2D(
|
||||
MBGL_CHECK_ERROR(glTexSubImage2D(
|
||||
GL_TEXTURE_2D, // GLenum target
|
||||
0, // GLint level
|
||||
0, // GLint xoffset
|
||||
@@ -277,12 +280,14 @@ void SpriteAtlas::bind(bool linear) {
|
||||
GL_RGBA, // GLenum format
|
||||
GL_UNSIGNED_BYTE, // GLenum type
|
||||
data // const GLvoid *pixels
|
||||
);
|
||||
));
|
||||
}
|
||||
|
||||
dirty = false;
|
||||
|
||||
// platform::show_color_debug_image("Sprite Atlas", reinterpret_cast<const char *>(data), width, height, width * pixelRatio, height * pixelRatio);
|
||||
#ifndef GL_ES_VERSION_2_0
|
||||
// platform::showColorDebugImage("Sprite Atlas", reinterpret_cast<const char *>(data), width * pixelRatio, height * pixelRatio, width * pixelRatio, height * pixelRatio);
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -374,7 +374,7 @@ std::string Map::getStyleJSON() const {
|
||||
util::ptr<Sprite> Map::getSprite() {
|
||||
const float pixelRatio = state.getPixelRatio();
|
||||
const std::string &sprite_url = style->getSpriteURL();
|
||||
if (!sprite || sprite->pixelRatio != pixelRatio) {
|
||||
if (!sprite || !sprite->hasPixelRatio(pixelRatio)) {
|
||||
sprite = Sprite::Create(sprite_url, pixelRatio, *env);
|
||||
}
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ util::ptr<Sprite> Sprite::Create(const std::string &base_url, float pixelRatio,
|
||||
|
||||
Sprite::Sprite(const Key &, const std::string& base_url, float pixelRatio_)
|
||||
: valid(base_url.length() > 0),
|
||||
pixelRatio(pixelRatio_),
|
||||
pixelRatio(pixelRatio_ > 1 ? 2 : 1),
|
||||
spriteURL(base_url + (pixelRatio_ > 1 ? "@2x" : "") + ".png"),
|
||||
jsonURL(base_url + (pixelRatio_ > 1 ? "@2x" : "") + ".json"),
|
||||
raster(),
|
||||
@@ -41,6 +41,11 @@ Sprite::Sprite(const Key &, const std::string& base_url, float pixelRatio_)
|
||||
future(promise.get_future()) {
|
||||
}
|
||||
|
||||
bool Sprite::hasPixelRatio(float ratio) const {
|
||||
return pixelRatio == (ratio > 1 ? 2 : 1);
|
||||
}
|
||||
|
||||
|
||||
void Sprite::waitUntilLoaded() const {
|
||||
future.wait();
|
||||
}
|
||||
|
||||
@@ -43,6 +43,8 @@ public:
|
||||
|
||||
const SpritePosition &getSpritePosition(const std::string& name) const;
|
||||
|
||||
bool hasPixelRatio(float ratio) const;
|
||||
|
||||
void waitUntilLoaded() const;
|
||||
bool isLoaded() const;
|
||||
|
||||
|
||||
@@ -87,7 +87,7 @@ std::string decompress(const std::string &raw) {
|
||||
inflateEnd(&inflate_stream);
|
||||
|
||||
if (code != Z_STREAM_END) {
|
||||
throw std::runtime_error(inflate_stream.msg);
|
||||
throw std::runtime_error(inflate_stream.msg ? inflate_stream.msg : "decompression error");
|
||||
}
|
||||
|
||||
return result;
|
||||
111
src/mbgl/util/scaling.cpp
Normal file
111
src/mbgl/util/scaling.cpp
Normal file
@@ -0,0 +1,111 @@
|
||||
#include "scaling.hpp"
|
||||
|
||||
namespace {
|
||||
|
||||
using namespace mbgl;
|
||||
|
||||
inline uint8_t bilinearInterpolate(uint8_t tl, uint8_t tr, uint8_t bl, uint8_t br, double dx, double dy) {
|
||||
const double t = dx * (tr - tl) + tl;
|
||||
const double b = dx * (br - bl) + bl;
|
||||
return t + dy * (b - t);
|
||||
}
|
||||
|
||||
template <size_t i>
|
||||
inline const uint8_t& b(const uint32_t& w) {
|
||||
return reinterpret_cast<const uint8_t*>(&w)[i];
|
||||
}
|
||||
|
||||
template <size_t i>
|
||||
inline uint8_t& b(uint32_t& w) {
|
||||
return reinterpret_cast<uint8_t*>(&w)[i];
|
||||
}
|
||||
|
||||
vec2<double> getFactor(const Rect<uint32_t>& srcPos, const Rect<uint32_t>& dstPos) {
|
||||
return {
|
||||
double(srcPos.w) / dstPos.w,
|
||||
double(srcPos.h) / dstPos.h
|
||||
};
|
||||
}
|
||||
|
||||
vec2<uint32_t> getBounds(const vec2<uint32_t>& srcSize, const Rect<uint32_t>& srcPos,
|
||||
const vec2<uint32_t>& dstSize, const Rect<uint32_t>& dstPos,
|
||||
const vec2<double>& factor) {
|
||||
if (srcPos.x > srcSize.x || srcPos.y > srcSize.y ||
|
||||
dstPos.x > dstSize.x || dstPos.y > dstSize.y) {
|
||||
// Source or destination position is out of range.
|
||||
return { 0, 0 };
|
||||
}
|
||||
|
||||
// Make sure we don't read/write values out of range.
|
||||
return { std::min(uint32_t(double(srcSize.x - srcPos.x) / factor.x),
|
||||
std::min(dstSize.x - dstPos.x, dstPos.w)),
|
||||
std::min(uint32_t(double(srcSize.y - srcPos.y) / factor.y),
|
||||
std::min(dstSize.y - dstPos.y, dstPos.h)) };
|
||||
}
|
||||
}
|
||||
|
||||
namespace mbgl {
|
||||
namespace util {
|
||||
|
||||
void bilinearScale(const uint32_t* srcData, const vec2<uint32_t>& srcSize,
|
||||
const Rect<uint32_t>& srcPos, uint32_t* dstData, const vec2<uint32_t>& dstSize,
|
||||
const Rect<uint32_t>& dstPos) {
|
||||
const auto factor = getFactor(srcPos, dstPos);
|
||||
const auto bounds = getBounds(srcSize, srcPos, dstSize, dstPos, factor);
|
||||
|
||||
double fractSrcY = srcPos.y;
|
||||
double fractSrcX;
|
||||
uint32_t x, y;
|
||||
size_t i = dstSize.x * dstPos.y + dstPos.x;
|
||||
for (y = 0; y < bounds.y; y++) {
|
||||
fractSrcX = srcPos.x;
|
||||
const uint32_t srcY0 = fractSrcY;
|
||||
const uint32_t srcY1 = std::min(srcY0 + 1, srcSize.y - 1);
|
||||
for (x = 0; x < bounds.x; x++) {
|
||||
const uint32_t srcX0 = fractSrcX;
|
||||
const uint32_t srcX1 = std::min(srcX0 + 1, srcSize.x - 1);
|
||||
|
||||
const uint32_t tl = srcData[srcSize.x * srcY0 + srcX0];
|
||||
const uint32_t tr = srcData[srcSize.x * srcY0 + srcX1];
|
||||
const uint32_t bl = srcData[srcSize.x * srcY1 + srcX0];
|
||||
const uint32_t br = srcData[srcSize.x * srcY1 + srcX1];
|
||||
|
||||
const double dx = fractSrcX - srcX0;
|
||||
const double dy = fractSrcY - srcY0;
|
||||
uint32_t& dst = dstData[i + x];
|
||||
b<0>(dst) = bilinearInterpolate(b<0>(tl), b<0>(tr), b<0>(bl), b<0>(br), dx, dy);
|
||||
b<1>(dst) = bilinearInterpolate(b<1>(tl), b<1>(tr), b<1>(bl), b<1>(br), dx, dy);
|
||||
b<2>(dst) = bilinearInterpolate(b<2>(tl), b<2>(tr), b<2>(bl), b<2>(br), dx, dy);
|
||||
b<3>(dst) = bilinearInterpolate(b<3>(tl), b<3>(tr), b<3>(bl), b<3>(br), dx, dy);
|
||||
fractSrcX += factor.x;
|
||||
}
|
||||
i += dstSize.x;
|
||||
fractSrcY += factor.y;
|
||||
}
|
||||
}
|
||||
|
||||
void nearestNeighborScale(const uint32_t* srcData, const vec2<uint32_t>& srcSize,
|
||||
const Rect<uint32_t>& srcPos, uint32_t* dstData,
|
||||
const vec2<uint32_t>& dstSize, const Rect<uint32_t>& dstPos) {
|
||||
const auto factor = getFactor(srcPos, dstPos);
|
||||
const auto bounds = getBounds(srcSize, srcPos, dstSize, dstPos, factor);
|
||||
|
||||
double fractSrcY = srcPos.y;
|
||||
double fractSrcX;
|
||||
size_t i = dstSize.x * dstPos.y + dstPos.x;
|
||||
uint32_t srcY;
|
||||
uint32_t x, y;
|
||||
for (y = 0; y < bounds.y; y++) {
|
||||
fractSrcX = srcPos.x;
|
||||
srcY = srcSize.x * uint32_t(fractSrcY);
|
||||
for (x = 0; x < bounds.x; x++) {
|
||||
dstData[i + x] = srcData[srcY + uint32_t(fractSrcX)];
|
||||
fractSrcX += factor.x;
|
||||
}
|
||||
i += dstSize.x;
|
||||
fractSrcY += factor.y;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
23
src/mbgl/util/scaling.hpp
Normal file
23
src/mbgl/util/scaling.hpp
Normal file
@@ -0,0 +1,23 @@
|
||||
#ifndef MBGL_UTIL_SCALING
|
||||
#define MBGL_UTIL_SCALING
|
||||
|
||||
|
||||
#include <mbgl/util/vec.hpp>
|
||||
#include <mbgl/util/rect.hpp>
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace mbgl {
|
||||
namespace util {
|
||||
|
||||
void bilinearScale(const uint32_t* srcData, const vec2<uint32_t>& srcSize,
|
||||
const Rect<uint32_t>& srcPos, uint32_t* dstData, const vec2<uint32_t>& dstSize,
|
||||
const Rect<uint32_t>& dstPos);
|
||||
|
||||
void nearestNeighborScale(const uint32_t* srcData, const vec2<uint32_t>& srcSize,
|
||||
const Rect<uint32_t>& srcPos, uint32_t* dstData,
|
||||
const vec2<uint32_t>& dstSize, const Rect<uint32_t>& dstPos);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
BIN
test/fixtures/sprites/atlas_reference.bin
vendored
Normal file
BIN
test/fixtures/sprites/atlas_reference.bin
vendored
Normal file
Binary file not shown.
BIN
test/fixtures/sprites/atlas_reference.png
vendored
Normal file
BIN
test/fixtures/sprites/atlas_reference.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 8.9 KiB |
BIN
test/fixtures/sprites/bright.bin
vendored
Normal file
BIN
test/fixtures/sprites/bright.bin
vendored
Normal file
Binary file not shown.
19
test/fixtures/sprites/convert_sprite.js
vendored
Normal file
19
test/fixtures/sprites/convert_sprite.js
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
// Converts a PNG image to a custom "image format" that has a uint32_t width/height prefix and then
|
||||
// raw RGBA data. We can't use the built-in PNG reading routines because they are reading
|
||||
// premultiplied images by default.
|
||||
|
||||
var fs = require('fs');
|
||||
var zlib = require('zlib');
|
||||
var PNG = require('png-js');
|
||||
var png = PNG.load('styles/sprites/bright.png');
|
||||
png.decodePixels(function(data) {
|
||||
var result = new Buffer(8 + data.length);
|
||||
result.writeUInt32BE(png.width, 0);
|
||||
result.writeUInt32BE(png.height, 4);
|
||||
data.copy(result, 8);
|
||||
|
||||
zlib.deflate(result, function(err, data) {
|
||||
if (err) throw err;
|
||||
fs.writeFileSync('test/fixtures/sprites/bright.bin', data);
|
||||
});
|
||||
});
|
||||
53
test/miscellaneous/bilinear.cpp
Normal file
53
test/miscellaneous/bilinear.cpp
Normal file
@@ -0,0 +1,53 @@
|
||||
#include "../fixtures/util.hpp"
|
||||
#include <mbgl/util/compression.hpp>
|
||||
#include <mbgl/util/scaling.hpp>
|
||||
#include <mbgl/util/image.hpp>
|
||||
#include <mbgl/util/io.hpp>
|
||||
#include <mbgl/util/std.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
|
||||
using namespace mbgl;
|
||||
|
||||
TEST(Bilinear, Scaling) {
|
||||
// We're reading from a custom "image format" that has a uint32_t width/height prefix and then
|
||||
// raw RGBA data. We can't use the built-in PNG reading routines because they are reading
|
||||
// premultiplied images by default.
|
||||
const std::string sprite = util::decompress(util::read_file("test/fixtures/sprites/bright.bin"));
|
||||
const uint8_t *src = reinterpret_cast<const uint8_t *>(sprite.data());
|
||||
ASSERT_GT(sprite.length(), 8u);
|
||||
const uint32_t width = src[0] << 24 | src[1] << 16 | src[2] << 8 | src[3];
|
||||
const uint32_t height = src[4] << 24 | src[5] << 16 | src[6] << 8 | src[7];
|
||||
ASSERT_EQ(sprite.length(), 2 * sizeof(uint32_t) + width * height * sizeof(uint32_t));
|
||||
|
||||
const uint32_t *srcData = reinterpret_cast<const uint32_t *>(src + 8);
|
||||
const vec2<uint32_t> srcSize { width, height };
|
||||
const vec2<uint32_t> dstSize { 128, 128 };
|
||||
auto dst = util::make_unique<uint32_t[]>(dstSize.x * dstSize.y);
|
||||
uint32_t *dstData = dst.get();
|
||||
std::fill(dstData, dstData + dstSize.x * dstSize.y, 0xFFFF00FF);
|
||||
|
||||
util::bilinearScale(srcData, srcSize, { 0, 0, 24, 24 }, dstData, dstSize, { 8, 8, 24, 24 });
|
||||
util::bilinearScale(srcData, srcSize, { 26, 0, 24, 24 }, dstData, dstSize, { 0, 40, 48, 48 });
|
||||
util::bilinearScale(srcData, srcSize, { 26, 26, 24, 24 }, dstData, dstSize, { 52, 40, 36, 36 });
|
||||
util::bilinearScale(srcData, srcSize, { 26, 26, 24, 24 }, dstData, dstSize, { 52, 40, 36, 36 });
|
||||
util::bilinearScale(srcData, srcSize, { 104, 0, 24, 24 }, dstData, dstSize, { 96, 0, 48, 48 });
|
||||
util::bilinearScale(srcData, srcSize, { 52, 260, 24, 24 }, dstData, dstSize, { 108, 108, 38, 38 });
|
||||
util::bilinearScale(srcData, srcSize, { 380, 0, 24, 24 }, dstData, dstSize, { 36, 0, 24, 24 });
|
||||
util::bilinearScale(srcData, srcSize, { 396, 396, 24, 24 }, dstData, dstSize, { 0, 0, 50, 50 });
|
||||
util::bilinearScale(srcData, srcSize, { 380, 182, 12, 12 }, dstData, dstSize, { 52, 80, 24, 24 });
|
||||
|
||||
// From the bottom
|
||||
util::bilinearScale(srcData, srcSize, { 252, 380, 12, 12 }, dstData, dstSize, { 0, 90, 12, 12 });
|
||||
util::bilinearScale(srcData, srcSize, { 252, 380, 12, 12 }, dstData, dstSize, { 18, 90, 24, 24 });
|
||||
|
||||
const std::string data { reinterpret_cast<char *>(dstData), dstSize.x * dstSize.y * sizeof(uint32_t) };
|
||||
util::write_file("test/fixtures/sprites/atlas_actual.png", util::compress_png(dstSize.x, dstSize.y, dstData));
|
||||
util::write_file("test/fixtures/sprites/atlas_actual.bin", util::compress(data));
|
||||
|
||||
const std::string reference = util::decompress(util::read_file("test/fixtures/sprites/atlas_reference.bin"));
|
||||
|
||||
EXPECT_EQ(reference.size(), data.size());
|
||||
EXPECT_TRUE(0 == std::memcmp(data.data(), reference.data(), data.size()));
|
||||
}
|
||||
@@ -38,6 +38,7 @@
|
||||
'headless/headless.cpp',
|
||||
|
||||
'miscellaneous/clip_ids.cpp',
|
||||
'miscellaneous/bilinear.cpp',
|
||||
'miscellaneous/comparisons.cpp',
|
||||
'miscellaneous/enums.cpp',
|
||||
'miscellaneous/functions.cpp',
|
||||
|
||||
Reference in New Issue
Block a user