Browse Source

Text: flush glyph cache image including padding.

Otherwise the texture may retain garbage data from previous GPU
allocations, causing random artifacts in rendered output.
pull/638/head
Vladimír Vondruš 2 years ago
parent
commit
57ca69bc29
  1. 13
      src/Magnum/Text/AbstractGlyphCache.cpp
  2. 13
      src/Magnum/Text/AbstractGlyphCache.h
  3. 18
      src/Magnum/Text/DistanceFieldGlyphCache.cpp
  4. 292
      src/Magnum/Text/Test/AbstractGlyphCacheTest.cpp
  5. 36
      src/Magnum/Text/Test/GlyphCacheGLTest.cpp

13
src/Magnum/Text/AbstractGlyphCache.cpp

@ -380,18 +380,25 @@ void AbstractGlyphCache::flushImage(const Range3Di& range) {
CORRADE_ASSERT((rangeu.min() <= rangeu.max()).all() && (rangeu.max() <= Vector3ui{state.image.size()}).all(),
"Text::AbstractGlyphCache::flushImage():" << Debug::packed << range << "out of range for size" << Debug::packed << state.image.size(), );
/* Set the image including padding, to make sure the sampled glyph area
doesn't contain potentially uninitialized GPU memory */
const Vector3i paddedMin = Math::max(Vector3i{0},
range.min() - Vector3i{padding(), 0});
const Vector3i paddedMax = Math::min(size(),
range.max() + Vector3i{padding(), 0});
/** @todo ugh have slicing on images directly already */
PixelStorage storage;
storage.setRowLength(state.image.size().x())
.setSkip(range.min());
.setSkip(paddedMin);
/* Set image height only if it's an array glyph cache, as otherwise it'd
cause errors on ES2 that doesn't support this pixel storage state */
if(state.image.size().z() != 1)
storage.setImageHeight(state.image.size().y());
doSetImage(range.min(), ImageView3D{
doSetImage(paddedMin, ImageView3D{
storage,
state.image.format(),
range.size(),
paddedMax - paddedMin,
state.image.data()});
}

13
src/Magnum/Text/AbstractGlyphCache.h

@ -648,10 +648,15 @@ class MAGNUM_TEXT_EXPORT AbstractGlyphCache {
* @m_since_latest
*
* Call after copying glyph data to @ref image() in order to reflect
* the updates to the GPU-side data. The @p layer and @p range is
* expected to be in bounds for @ref size(). You can use
* @ref Math::join() on rectangles passed to @ref addGlyph() to
* calculate the area that spans all glyphs that were added.
* the updates to the GPU-side data. The @p range is expected to be in
* bounds for @ref size(). You can use @ref Math::join() on rectangles
* passed to @ref addGlyph() to calculate the area that spans all
* glyphs that were added.
*
* The function assumes the @p range excludes @ref padding(). The image
* data get copied to the GPU including the padding to make sure the
* padded glyph area doesn't contain leftovers of uninitialized GPU
* memory.
*/
void flushImage(const Range3Di& range);

18
src/Magnum/Text/DistanceFieldGlyphCache.cpp

@ -112,24 +112,14 @@ void DistanceFieldGlyphCache::doSetImage(const Vector2i& offset, const ImageView
#endif
#if !(defined(MAGNUM_TARGET_GLES2) && defined(MAGNUM_TARGET_WEBGL))
{
/* Create an image view that includes the distance field radius as
well, to be sure the edges are processed appropriately as well */
/** @todo this feels more like a hack than a real solution, any better
idea? Tried to make TextureTools::DistanceField pad the processing
area on its own, but eventually gave up as it would (a) made the
processing do more work than necessary in many cases, with no easy
way to opt out of that, (b) wasn't really working if the input
didn't have a black strip around the edge on platforms that don't
support border clamp, (c) required the input texture to have
certain wrapping mode set */
/* The image range was already expanded to include the padding in
flushImage() */
CORRADE_INTERNAL_ASSERT(image.storage().skip().xy() == offset);
#ifdef CORRADE_NO_ASSERT
static_cast<void>(offset);
#endif
const Vector2i paddedMin = Math::max(Vector2i{0},
image.storage().skip().xy() - padding());
const Vector2i paddedMax = Math::min(size().xy(),
image.size() + image.storage().skip().xy() + padding());
const Vector2i paddedMin = image.storage().skip().xy();
const Vector2i paddedMax = image.size() + image.storage().skip().xy();
/* TextureTools::DistanceField expects the input size and output
rectangle size ratio to be a multiple of 2 in order for the shader

292
src/Magnum/Text/Test/AbstractGlyphCacheTest.cpp

@ -140,6 +140,14 @@ struct AbstractGlyphCacheTest: TestSuite::Tester {
#endif
};
const struct {
const char* name;
Vector2i padding;
} FlushImageData[]{
{"", {}},
{"with padding", {2, 3}},
};
const struct {
const char* name;
GlyphCacheFeatures features;
@ -205,25 +213,33 @@ AbstractGlyphCacheTest::AbstractGlyphCacheTest() {
&AbstractGlyphCacheTest::insertNot2D,
&AbstractGlyphCacheTest::insertMultiFont,
#endif
});
&AbstractGlyphCacheTest::flushImage,
&AbstractGlyphCacheTest::flushImageWholeArea,
&AbstractGlyphCacheTest::flushImageLayer,
&AbstractGlyphCacheTest::flushImage2D,
&AbstractGlyphCacheTest::flushImage2DPassthrough2D,
&AbstractGlyphCacheTest::flushImageNotImplemented,
&AbstractGlyphCacheTest::flushImagePassthrough2DNotImplemented,
&AbstractGlyphCacheTest::flushImageOutOfRange,
&AbstractGlyphCacheTest::flushImage2DNot2D,
addInstancedTests({&AbstractGlyphCacheTest::flushImage,
&AbstractGlyphCacheTest::flushImageWholeArea,
&AbstractGlyphCacheTest::flushImageLayer,
&AbstractGlyphCacheTest::flushImage2D,
&AbstractGlyphCacheTest::flushImage2DPassthrough2D},
Containers::arraySize(FlushImageData));
#ifdef MAGNUM_BUILD_DEPRECATED
&AbstractGlyphCacheTest::setImage,
&AbstractGlyphCacheTest::setImageOutOfRange,
addTests({&AbstractGlyphCacheTest::flushImageNotImplemented,
&AbstractGlyphCacheTest::flushImagePassthrough2DNotImplemented});
addInstancedTests({&AbstractGlyphCacheTest::flushImageOutOfRange},
Containers::arraySize(FlushImageData));
addTests({&AbstractGlyphCacheTest::flushImage2DNot2D});
#ifdef MAGNUM_BUILD_DEPRECATED
addInstancedTests({&AbstractGlyphCacheTest::setImage},
Containers::arraySize(FlushImageData));
addTests({&AbstractGlyphCacheTest::setImageOutOfRange,
&AbstractGlyphCacheTest::setImageInvalidFormat,
&AbstractGlyphCacheTest::setImageNot2D,
#endif
&AbstractGlyphCacheTest::setImageNot2D});
#endif
&AbstractGlyphCacheTest::processedImage});
addTests({&AbstractGlyphCacheTest::processedImage});
addInstancedTests({&AbstractGlyphCacheTest::processedImageNotSupported},
Containers::arraySize(ProcessedImageNotSupportedData));
@ -1014,6 +1030,9 @@ void AbstractGlyphCacheTest::insertMultiFont() {
#endif
void AbstractGlyphCacheTest::flushImage() {
auto&& data = FlushImageData[testCaseInstanceId()];
setTestCaseDescription(data.name);
struct: AbstractGlyphCache {
using AbstractGlyphCache::AbstractGlyphCache;
@ -1021,27 +1040,56 @@ void AbstractGlyphCacheTest::flushImage() {
void doSetImage(const Vector3i& offset, const ImageView3D& image) override {
called = true;
char pixels0[]{
'a', 'b', 'c', 0,
'd', 'e', 'f', 0,
};
char pixels1[]{
'0', '1', '2', 0,
'3', '4', '5', 0,
};
CORRADE_COMPARE(offset, (Vector3i{15, 30, 3}));
CORRADE_COMPARE(image.size(), (Vector3i{3, 2, 2}));
CORRADE_COMPARE_AS(image.pixels<Byte>()[0],
(ImageView2D{PixelFormat::R8Snorm, {3, 2}, pixels0}),
DebugTools::CompareImage);
CORRADE_COMPARE_AS(image.pixels<Byte>()[1],
(ImageView2D{PixelFormat::R8Snorm, {3, 2}, pixels1}),
DebugTools::CompareImage);
CORRADE_COMPARE(offset, (Vector3i{15, 30, 3} - Vector3i{padding(), 0}));
CORRADE_COMPARE(image.size(), (Vector3i{3, 2, 2} + Vector3i{2*padding(), 0}));
if(padding().isZero()) {
char pixels0[]{
'a', 'b', 'c', 0,
'd', 'e', 'f', 0,
};
char pixels1[]{
'0', '1', '2', 0,
'3', '4', '5', 0,
};
CORRADE_COMPARE_AS(image.pixels<Byte>()[0],
(ImageView2D{PixelFormat::R8Snorm, {3, 2}, pixels0}),
DebugTools::CompareImage);
CORRADE_COMPARE_AS(image.pixels<Byte>()[1],
(ImageView2D{PixelFormat::R8Snorm, {3, 2}, pixels1}),
DebugTools::CompareImage);
} else {
char pixels0[]{
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 'a', 'b', 'c', 0, 0, 0,
0, 0, 'd', 'e', 'f', 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0
};
char pixels1[]{
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, '0', '1', '2', 0, 0, 0,
0, 0, '3', '4', '5', 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0
};
CORRADE_COMPARE_AS(image.pixels<Byte>()[0],
(ImageView2D{PixelFormat::R8Snorm, {7, 8}, pixels0}),
DebugTools::CompareImage);
CORRADE_COMPARE_AS(image.pixels<Byte>()[1],
(ImageView2D{PixelFormat::R8Snorm, {7, 8}, pixels1}),
DebugTools::CompareImage);
}
}
bool called = false;
/* Padding should have no effect on any of this */
} cache{PixelFormat::R8Snorm, {45, 35, 5}, {2, 3}};
} cache{PixelFormat::R8Snorm, {45, 35, 5}, data.padding};
/* Capture correct function name */
CORRADE_VERIFY(true);
@ -1062,8 +1110,12 @@ void AbstractGlyphCacheTest::flushImage() {
}
void AbstractGlyphCacheTest::flushImageWholeArea() {
auto&& data = FlushImageData[testCaseInstanceId()];
setTestCaseDescription(data.name);
/* Like above, but calling flushImage() with the whole size to test bounds
checking */
checking. The padding doesn't affect the call in this case -- the actual
range is always the whole image. */
struct: AbstractGlyphCache {
using AbstractGlyphCache::AbstractGlyphCache;
@ -1091,8 +1143,7 @@ void AbstractGlyphCacheTest::flushImageWholeArea() {
}
bool called = false;
/* Padding should have no effect on any of this */
} cache{PixelFormat::R8Snorm, {3, 2, 2}, {2, 3}};
} cache{PixelFormat::R8Snorm, {3, 2, 2}, data.padding};
/* Capture correct function name */
CORRADE_VERIFY(true);
@ -1113,6 +1164,9 @@ void AbstractGlyphCacheTest::flushImageWholeArea() {
}
void AbstractGlyphCacheTest::flushImageLayer() {
auto&& data = FlushImageData[testCaseInstanceId()];
setTestCaseDescription(data.name);
/* Single slice subset of flushImage() */
struct: AbstractGlyphCache {
@ -1122,20 +1176,36 @@ void AbstractGlyphCacheTest::flushImageLayer() {
void doSetImage(const Vector3i& offset, const ImageView3D& image) override {
called = true;
char pixels[]{
'a', 'b', 'c', 0,
'd', 'e', 'f', 0,
};
CORRADE_COMPARE(offset, (Vector3i{15, 30, 3}));
CORRADE_COMPARE(image.size(), (Vector3i{3, 2, 1}));
CORRADE_COMPARE_AS(image.pixels<Byte>()[0],
(ImageView2D{PixelFormat::R8Snorm, {3, 2}, pixels}),
DebugTools::CompareImage);
CORRADE_COMPARE(offset, (Vector3i{15, 30, 3} - Vector3i{padding(), 0}));
CORRADE_COMPARE(image.size(), (Vector3i{3, 2, 1} + Vector3i{2*padding(), 0}));
if(padding().isZero()) {
char pixels[]{
'a', 'b', 'c', 0,
'd', 'e', 'f', 0,
};
CORRADE_COMPARE_AS(image.pixels<Byte>()[0],
(ImageView2D{PixelFormat::R8Snorm, {3, 2}, pixels}),
DebugTools::CompareImage);
} else {
char pixels[]{
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 'a', 'b', 'c', 0, 0, 0,
0, 0, 'd', 'e', 'f', 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0
};
CORRADE_COMPARE_AS(image.pixels<Byte>()[0],
(ImageView2D{PixelFormat::R8Snorm, {7, 8}, pixels}),
DebugTools::CompareImage);
}
}
bool called = false;
/* Padding should have no effect on any of this */
} cache{PixelFormat::R8Snorm, {45, 35, 5}, {2, 3}};
} cache{PixelFormat::R8Snorm, {45, 35, 5}, data.padding};
/* Capture correct function name */
CORRADE_VERIFY(true);
@ -1153,6 +1223,9 @@ void AbstractGlyphCacheTest::flushImageLayer() {
}
void AbstractGlyphCacheTest::flushImage2D() {
auto&& data = FlushImageData[testCaseInstanceId()];
setTestCaseDescription(data.name);
/* Like flushImageLayer() but reduced to two dimensions */
struct: AbstractGlyphCache {
@ -1162,20 +1235,36 @@ void AbstractGlyphCacheTest::flushImage2D() {
void doSetImage(const Vector3i& offset, const ImageView3D& image) override {
called = true;
char pixels[]{
'a', 'b', 'c', 0,
'd', 'e', 'f', 0,
};
CORRADE_COMPARE(offset, (Vector3i{15, 30, 0}));
CORRADE_COMPARE(image.size(), (Vector3i{3, 2, 1}));
CORRADE_COMPARE_AS(image.pixels<Byte>()[0],
(ImageView2D{PixelFormat::R8Snorm, {3, 2}, pixels}),
DebugTools::CompareImage);
CORRADE_COMPARE(offset, (Vector3i{15, 30, 0} - Vector3i{padding(), 0}));
CORRADE_COMPARE(image.size(), (Vector3i{3, 2, 1} + Vector3i{2*padding(), 0}));
if(padding().isZero()) {
char pixels[]{
'a', 'b', 'c', 0,
'd', 'e', 'f', 0,
};
CORRADE_COMPARE_AS(image.pixels<Byte>()[0],
(ImageView2D{PixelFormat::R8Snorm, {3, 2}, pixels}),
DebugTools::CompareImage);
} else {
char pixels[]{
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 'a', 'b', 'c', 0, 0, 0,
0, 0, 'd', 'e', 'f', 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0
};
CORRADE_COMPARE_AS(image.pixels<Byte>()[0],
(ImageView2D{PixelFormat::R8Snorm, {7, 8}, pixels}),
DebugTools::CompareImage);
}
}
bool called = false;
/* Padding should have no effect on any of this */
} cache{PixelFormat::R8Snorm, {45, 35}, {2, 3}};
} cache{PixelFormat::R8Snorm, {45, 35}, data.padding};
/* Capture correct function name */
CORRADE_VERIFY(true);
@ -1193,6 +1282,9 @@ void AbstractGlyphCacheTest::flushImage2D() {
}
void AbstractGlyphCacheTest::flushImage2DPassthrough2D() {
auto&& data = FlushImageData[testCaseInstanceId()];
setTestCaseDescription(data.name);
/* Like flushImage2D() but with 2D doSetImage() */
struct: AbstractGlyphCache {
@ -1202,20 +1294,36 @@ void AbstractGlyphCacheTest::flushImage2DPassthrough2D() {
void doSetImage(const Vector2i& offset, const ImageView2D& image) override {
called = true;
char pixels[]{
'a', 'b', 'c', 0,
'd', 'e', 'f', 0,
};
CORRADE_COMPARE(offset, (Vector2i{15, 30}));
CORRADE_COMPARE(image.size(), (Vector2i{3, 2}));
CORRADE_COMPARE_AS(image.pixels<Byte>(),
(ImageView2D{PixelFormat::R8Snorm, {3, 2}, pixels}),
DebugTools::CompareImage);
CORRADE_COMPARE(offset, (Vector2i{15, 30}) - padding());
CORRADE_COMPARE(image.size(), (Vector2i{3, 2}) + 2*padding());
if(padding().isZero()) {
char pixels[]{
'a', 'b', 'c', 0,
'd', 'e', 'f', 0,
};
CORRADE_COMPARE_AS(image.pixels<Byte>(),
(ImageView2D{PixelFormat::R8Snorm, {3, 2}, pixels}),
DebugTools::CompareImage);
} else {
char pixels[]{
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 'a', 'b', 'c', 0, 0, 0,
0, 0, 'd', 'e', 'f', 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0
};
CORRADE_COMPARE_AS(image.pixels<Byte>(),
(ImageView2D{PixelFormat::R8Snorm, {7, 8}, pixels}),
DebugTools::CompareImage);
}
}
bool called = false;
/* Padding should have no effect on any of this */
} cache{PixelFormat::R8Snorm, {45, 35}, {2, 3}};
} cache{PixelFormat::R8Snorm, {45, 35}, data.padding};
/* Capture correct function name */
CORRADE_VERIFY(true);
@ -1271,10 +1379,13 @@ void AbstractGlyphCacheTest::flushImagePassthrough2DNotImplemented() {
}
void AbstractGlyphCacheTest::flushImageOutOfRange() {
auto&& data = FlushImageData[testCaseInstanceId()];
setTestCaseDescription(data.name);
CORRADE_SKIP_IF_NO_ASSERT();
/* The padding should have no effect on this */
DummyGlyphCache cache{PixelFormat::R32F, {1024, 512, 8}, {2, 3}};
/* The padding should not have any effect on the check */
DummyGlyphCache cache{PixelFormat::R32F, {1024, 512, 8}, data.padding};
std::ostringstream out;
Error redirectError{&out};
@ -1322,6 +1433,9 @@ void AbstractGlyphCacheTest::flushImage2DNot2D() {
#ifdef MAGNUM_BUILD_DEPRECATED
void AbstractGlyphCacheTest::setImage() {
auto&& data = FlushImageData[testCaseInstanceId()];
setTestCaseDescription(data.name);
CORRADE_IGNORE_DEPRECATED_PUSH
struct: AbstractGlyphCache {
using AbstractGlyphCache::AbstractGlyphCache;
@ -1330,20 +1444,38 @@ void AbstractGlyphCacheTest::setImage() {
void doSetImage(const Vector2i& offset, const ImageView2D& image) override {
called = true;
char pixels[]{
'a', 'b', 'c', 0,
'd', 'e', 'f', 0,
};
CORRADE_COMPARE(offset, (Vector2i{15, 30}));
CORRADE_COMPARE_AS(image,
(ImageView2D{PixelFormat::R8Unorm, {3, 2}, pixels}),
DebugTools::CompareImage);
CORRADE_COMPARE(offset, (Vector2i{15, 30}) - padding());
CORRADE_COMPARE(image.size(), (Vector2i{3, 2}) + 2*padding());
if(padding().isZero()) {
char pixels[]{
'a', 'b', 'c', 0,
'd', 'e', 'f', 0,
};
CORRADE_COMPARE_AS(image.pixels<Byte>(),
(ImageView2D{PixelFormat::R8Snorm, {3, 2}, pixels}),
DebugTools::CompareImage);
} else {
char pixels[]{
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 'a', 'b', 'c', 0, 0, 0,
0, 0, 'd', 'e', 'f', 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0
};
CORRADE_COMPARE_AS(image.pixels<Byte>(),
(ImageView2D{PixelFormat::R8Snorm, {7, 8}, pixels}),
DebugTools::CompareImage);
}
}
bool called = false;
/* Deliberately using the deprecated PixelFormat-less constructor to verify
that passing a R8Unorm image "just works" */
} cache{{45, 35}};
} cache{{45, 35}, data.padding};
CORRADE_IGNORE_DEPRECATED_POP
/* Capture correct function name */

36
src/Magnum/Text/Test/GlyphCacheGLTest.cpp

@ -132,6 +132,11 @@ const UnsignedByte ExpectedData[]{
void GlyphCacheGLTest::setImage() {
GlyphCache cache{{16, 8}};
/* Fill the texture with non-zero data to verify the padding gets uploaded
as well */
cache.texture().setSubImage(0, {}, Image2D{PixelFormat::R8Unorm, {16, 8}, Containers::Array<char>{DirectInit, 16*8, '\xcd'}});
MAGNUM_VERIFY_NO_GL_ERROR();
Utility::copy(
Containers::StridedArrayView2D<const UnsignedByte>{InputData, {4, 8}},
cache.image().pixels<UnsignedByte>()[0].sliceSize({4, 8}, {4, 8}));
@ -143,24 +148,45 @@ void GlyphCacheGLTest::setImage() {
MutableImageView2D actual{actual3.format(), actual3.size().xy(), actual3.data()};
MAGNUM_VERIFY_NO_GL_ERROR();
ImageView2D expected{PixelFormat::R8Unorm, {16, 8}, ExpectedData};
/* The CPU-side image is zero-initialized, what was set above in the
texture isn't present there */
CORRADE_COMPARE_AS(actual,
expected,
(ImageView2D{PixelFormat::R8Unorm, {16, 8}, ExpectedData}),
DebugTools::CompareImage);
#ifdef MAGNUM_TARGET_GLES2
CORRADE_SKIP("Luminance format used on GLES2 isn't usable for framebuffer reading, can't verify texture contents.");
#else
/* Verify the actual texture. It should be the same as above. On GLES we
cannot really verify that the size matches, but at least something. */
/* The actual texture has just the slice updated, the rest stays. On GLES
we cannot really verify that the size matches, but at least
something. */
#ifndef MAGNUM_TARGET_GLES
Image2D image = cache.texture().image(0, {PixelFormat::R8Unorm});
#else
Image2D image = DebugTools::textureSubImage(cache.texture(), 0, {{}, {16, 8}}, {PixelFormat::R8Unorm});
#endif
MAGNUM_VERIFY_NO_GL_ERROR();
const UnsignedByte expectedTextureData[]{
0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0,
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0,
0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff,
0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0,
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0,
0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff,
};
CORRADE_COMPARE_AS(image,
expected,
(ImageView2D{PixelFormat::R8Unorm, {16, 8}, expectedTextureData}),
DebugTools::CompareImage);
#endif
}

Loading…
Cancel
Save