2014-10-21 09:21:31 +08:00
|
|
|
// Aseprite Document Library
|
2025-04-10 20:56:46 +08:00
|
|
|
// Copyright (c) 2018-2025 Igara Studio S.A.
|
2018-08-09 04:27:26 +08:00
|
|
|
// Copyright (c) 2001-2018 David Capello
|
2014-10-21 09:21:31 +08:00
|
|
|
//
|
|
|
|
// This file is released under the terms of the MIT license.
|
|
|
|
// Read LICENSE.txt for more information.
|
2013-11-10 06:59:05 +08:00
|
|
|
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
|
|
#include "config.h"
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include <gtest/gtest.h>
|
|
|
|
|
2024-12-24 02:55:43 +08:00
|
|
|
#include "doc/algorithm/random_image.h"
|
2024-12-19 20:36:27 +08:00
|
|
|
#include "doc/image.h"
|
2014-10-21 09:21:31 +08:00
|
|
|
#include "doc/primitives.h"
|
2013-11-10 06:59:05 +08:00
|
|
|
|
2018-08-09 04:27:26 +08:00
|
|
|
#include <memory>
|
|
|
|
|
2013-11-10 06:59:05 +08:00
|
|
|
using namespace base;
|
2014-10-21 09:21:31 +08:00
|
|
|
using namespace doc;
|
2013-11-10 06:59:05 +08:00
|
|
|
|
|
|
|
template<typename T>
|
|
|
|
class ImageAllTypes : public testing::Test {
|
|
|
|
protected:
|
|
|
|
ImageAllTypes() {}
|
|
|
|
};
|
|
|
|
|
2024-12-24 02:55:43 +08:00
|
|
|
using ImageAllTraits = testing::Types<RgbTraits, GrayscaleTraits, IndexedTraits, BitmapTraits>;
|
|
|
|
|
2022-08-18 08:46:33 +08:00
|
|
|
TYPED_TEST_SUITE(ImageAllTypes, ImageAllTraits);
|
2013-11-10 06:59:05 +08:00
|
|
|
|
2024-12-24 02:55:43 +08:00
|
|
|
#if DOC_USE_BITMAP_AS_1BPP
|
|
|
|
|
|
|
|
template<typename T>
|
|
|
|
class ImageAllTypesNoBitmap : public testing::Test {
|
|
|
|
protected:
|
|
|
|
ImageAllTypesNoBitmap() {}
|
|
|
|
};
|
|
|
|
|
|
|
|
using ImageAllTraitsNoBitmap = testing::Types<RgbTraits, GrayscaleTraits, IndexedTraits>;
|
|
|
|
TYPED_TEST_SUITE(ImageAllTypesNoBitmap, ImageAllTraitsNoBitmap);
|
|
|
|
|
|
|
|
#else // !DOC_USE_BITMAP_AS_1BPP
|
|
|
|
|
|
|
|
#define ImageAllTypesNoBitmap ImageAllTypes
|
|
|
|
|
|
|
|
#endif // !DOC_USE_BITMAP_AS_1BPP
|
|
|
|
|
2013-11-10 06:59:05 +08:00
|
|
|
TYPED_TEST(ImageAllTypes, PutGetAndIterators)
|
|
|
|
{
|
2025-04-10 20:56:46 +08:00
|
|
|
using ImageTraits = TypeParam;
|
|
|
|
|
|
|
|
std::vector<int> lengths = { 1, 4, 7, 8, 9, 15, 33 };
|
|
|
|
std::vector<gfx::Size> sizes(lengths.size() * lengths.size());
|
|
|
|
std::size_t k = 0;
|
2015-03-05 08:35:11 +08:00
|
|
|
for (std::size_t i = 0; i < lengths.size(); ++i)
|
|
|
|
for (std::size_t j = 0; j < lengths.size(); ++j)
|
2025-04-10 20:56:46 +08:00
|
|
|
sizes[k++] = gfx::Size(lengths[j], lengths[i]);
|
2013-11-10 06:59:05 +08:00
|
|
|
|
2025-04-10 20:56:46 +08:00
|
|
|
for (const auto& size : sizes) {
|
|
|
|
const int w = size.w;
|
|
|
|
const int h = size.h;
|
2018-08-09 04:27:26 +08:00
|
|
|
std::unique_ptr<Image> image(Image::create(ImageTraits::pixel_format, w, h));
|
2013-11-10 06:59:05 +08:00
|
|
|
std::vector<int> data(w * h);
|
|
|
|
|
|
|
|
for (int y = 0; y < h; ++y)
|
|
|
|
for (int x = 0; x < w; ++x)
|
|
|
|
data[y * w + x] = (std::rand() % ImageTraits::max_value);
|
|
|
|
|
|
|
|
for (int i = 0; i < w * h; ++i)
|
2018-08-09 04:27:26 +08:00
|
|
|
put_pixel(image.get(), i % w, i / w, data[i]);
|
2013-11-10 06:59:05 +08:00
|
|
|
|
|
|
|
for (int i = 0; i < w * h; ++i)
|
2018-08-09 04:27:26 +08:00
|
|
|
ASSERT_EQ(data[i], get_pixel(image.get(), i % w, i / w));
|
2013-11-10 06:59:05 +08:00
|
|
|
|
|
|
|
std::vector<gfx::Rect> areas;
|
|
|
|
|
|
|
|
// Read-only iterator (whole image)
|
|
|
|
{
|
2018-08-09 04:27:26 +08:00
|
|
|
const LockImageBits<ImageTraits> bits((const Image*)image.get());
|
2025-04-10 20:56:46 +08:00
|
|
|
auto begin = bits.begin(), it = begin, end = bits.end();
|
2013-11-10 06:59:05 +08:00
|
|
|
|
|
|
|
for (int i = 0; it != end; ++it, ++i) {
|
|
|
|
assert(data[i] == *it);
|
|
|
|
ASSERT_EQ(data[i], *it);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Read-only iterator (areas)
|
|
|
|
for (int i = 0;; ++i) {
|
|
|
|
gfx::Rect bounds(i, i, w - i * 2, h - i * 2);
|
|
|
|
if (bounds.w <= 0 || bounds.h <= 0)
|
|
|
|
break;
|
|
|
|
|
2018-08-09 04:27:26 +08:00
|
|
|
const LockImageBits<ImageTraits> bits((const Image*)image.get(), bounds);
|
2025-04-10 20:56:46 +08:00
|
|
|
auto begin = bits.begin(), it = begin, end = bits.end();
|
2013-11-10 06:59:05 +08:00
|
|
|
|
|
|
|
for (int y = bounds.y; y < bounds.y + bounds.h; ++y) {
|
|
|
|
for (int x = bounds.x; x < bounds.x + bounds.w; ++x, ++it) {
|
|
|
|
SCOPED_TRACE(x);
|
|
|
|
SCOPED_TRACE(y);
|
|
|
|
|
|
|
|
ASSERT_TRUE(it != end);
|
|
|
|
EXPECT_EQ(data[y * w + x], *it);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
EXPECT_TRUE(it == end);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Write iterator (whole image)
|
|
|
|
{
|
2018-08-09 04:27:26 +08:00
|
|
|
LockImageBits<ImageTraits> bits(image.get(), Image::WriteLock);
|
2025-04-10 20:56:46 +08:00
|
|
|
auto begin = bits.begin(), it = begin, end = bits.end();
|
2013-11-10 06:59:05 +08:00
|
|
|
|
|
|
|
for (int i = 0; it != end; ++it, ++i) {
|
|
|
|
*it = 1;
|
|
|
|
EXPECT_EQ(1, *it);
|
|
|
|
}
|
|
|
|
|
|
|
|
it = begin;
|
|
|
|
for (int i = 0; it != end; ++it, ++i) {
|
|
|
|
EXPECT_EQ(1, *it);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(Image, DiffRgbImages)
|
|
|
|
{
|
2018-08-09 04:27:26 +08:00
|
|
|
std::unique_ptr<Image> a(Image::create(IMAGE_RGB, 32, 32));
|
|
|
|
std::unique_ptr<Image> b(Image::create(IMAGE_RGB, 32, 32));
|
2013-11-10 06:59:05 +08:00
|
|
|
|
2018-08-09 04:27:26 +08:00
|
|
|
clear_image(a.get(), rgba(0, 0, 0, 0));
|
|
|
|
clear_image(b.get(), rgba(0, 0, 0, 0));
|
2013-11-10 06:59:05 +08:00
|
|
|
|
2018-08-09 04:27:26 +08:00
|
|
|
ASSERT_EQ(0, count_diff_between_images(a.get(), b.get()));
|
2018-11-14 02:32:38 +08:00
|
|
|
ASSERT_TRUE(is_same_image(a.get(), b.get()));
|
2013-11-10 06:59:05 +08:00
|
|
|
|
2018-11-14 02:32:38 +08:00
|
|
|
// No difference because alpha=0
|
2018-08-09 04:27:26 +08:00
|
|
|
put_pixel(a.get(), 0, 0, rgba(255, 0, 0, 0));
|
2018-11-14 02:32:38 +08:00
|
|
|
ASSERT_EQ(0, count_diff_between_images(a.get(), b.get()));
|
|
|
|
ASSERT_TRUE(is_same_image(a.get(), b.get()));
|
|
|
|
|
|
|
|
put_pixel(a.get(), 0, 0, rgba(255, 0, 0, 255));
|
2018-08-09 04:27:26 +08:00
|
|
|
ASSERT_EQ(1, count_diff_between_images(a.get(), b.get()));
|
2018-11-14 02:32:38 +08:00
|
|
|
ASSERT_FALSE(is_same_image(a.get(), b.get()));
|
2013-11-10 06:59:05 +08:00
|
|
|
|
2018-11-14 02:32:38 +08:00
|
|
|
put_pixel(a.get(), 1, 1, rgba(0, 0, 255, 128));
|
2018-08-09 04:27:26 +08:00
|
|
|
ASSERT_EQ(2, count_diff_between_images(a.get(), b.get()));
|
2018-11-14 02:32:38 +08:00
|
|
|
ASSERT_FALSE(is_same_image(a.get(), b.get()));
|
2013-11-10 06:59:05 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
TYPED_TEST(ImageAllTypes, DrawHLine)
|
|
|
|
{
|
2025-04-10 20:56:46 +08:00
|
|
|
using ImageTraits = TypeParam;
|
|
|
|
|
|
|
|
std::vector<int> lengths = { 7, 8, 9, 15, 16, 17, 31, 32, 33 };
|
|
|
|
std::vector<gfx::Size> sizes(lengths.size() * lengths.size());
|
|
|
|
std::size_t k = 0;
|
2015-03-05 08:35:11 +08:00
|
|
|
for (std::size_t i = 0; i < lengths.size(); ++i)
|
|
|
|
for (std::size_t j = 0; j < lengths.size(); ++j)
|
2025-04-10 20:56:46 +08:00
|
|
|
sizes[k++] = gfx::Size(lengths[j], lengths[i]);
|
2013-11-10 06:59:05 +08:00
|
|
|
|
2025-04-10 20:56:46 +08:00
|
|
|
for (const auto& size : sizes) {
|
|
|
|
const int w = size.w;
|
|
|
|
const int h = size.h;
|
2018-08-09 04:27:26 +08:00
|
|
|
std::unique_ptr<Image> image(Image::create(ImageTraits::pixel_format, w, h));
|
2013-11-10 06:59:05 +08:00
|
|
|
image->clear(0);
|
2015-03-05 08:35:11 +08:00
|
|
|
|
2013-11-10 06:59:05 +08:00
|
|
|
for (int c = 0; c < 100; ++c) {
|
|
|
|
int x = rand() % w;
|
|
|
|
int y = rand() % h;
|
|
|
|
int x2 = x + (rand() % (w - x));
|
|
|
|
image->drawHLine(x, y, x2, rand() % ImageTraits::max_value);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-08-03 23:43:58 +08:00
|
|
|
TYPED_TEST(ImageAllTypes, FillRect)
|
|
|
|
{
|
2025-04-10 20:56:46 +08:00
|
|
|
using ImageTraits = TypeParam;
|
2016-08-03 23:43:58 +08:00
|
|
|
|
2025-04-10 20:56:46 +08:00
|
|
|
for (int i = 0; i <= 110; i += 11) {
|
|
|
|
const int w = 1 + i;
|
|
|
|
const int h = 1 + i;
|
2016-08-03 23:43:58 +08:00
|
|
|
|
2018-08-09 04:27:26 +08:00
|
|
|
std::unique_ptr<Image> image(Image::create(ImageTraits::pixel_format, w, h));
|
2016-08-03 23:43:58 +08:00
|
|
|
color_t color = (rand() % ImageTraits::max_value);
|
|
|
|
if (!color)
|
|
|
|
color = 1;
|
|
|
|
|
2025-04-10 20:56:46 +08:00
|
|
|
for (int j = 0; j <= 1100; j += 11) {
|
|
|
|
const int x1 = rand() % w;
|
|
|
|
const int y1 = rand() % h;
|
|
|
|
const int x2 = x1 + (rand() % (w - x1));
|
|
|
|
const int y2 = y1 + (rand() % (h - y1));
|
2016-08-03 23:43:58 +08:00
|
|
|
|
|
|
|
image->clear(0);
|
2018-08-09 04:27:26 +08:00
|
|
|
fill_rect(image.get(), x1, y1, x2, y2, color);
|
2016-08-03 23:43:58 +08:00
|
|
|
|
|
|
|
// Check
|
|
|
|
for (int v = 0; v < h; ++v) {
|
|
|
|
for (int u = 0; u < w; ++u) {
|
2018-08-09 04:27:26 +08:00
|
|
|
color_t pixel = get_pixel_fast<ImageTraits>(image.get(), u, v);
|
2016-08-03 23:43:58 +08:00
|
|
|
if (u >= x1 && v >= y1 && u <= x2 && v <= y2)
|
|
|
|
EXPECT_EQ(color, pixel);
|
|
|
|
else
|
|
|
|
EXPECT_EQ(0, pixel);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-12-24 02:55:43 +08:00
|
|
|
TYPED_TEST(ImageAllTypesNoBitmap, NewIterators)
|
|
|
|
{
|
|
|
|
using ImageTraits = TypeParam;
|
|
|
|
|
|
|
|
for (int i = 0; i < 100; ++i) {
|
|
|
|
const int w = 1 + i;
|
|
|
|
const int h = 1 + i;
|
|
|
|
|
|
|
|
std::unique_ptr<Image> image(Image::create(ImageTraits::pixel_format, w, h));
|
|
|
|
doc::algorithm::random_image(image.get());
|
|
|
|
|
|
|
|
// TopLeft
|
|
|
|
{
|
|
|
|
int v = 0;
|
|
|
|
auto it = image->readArea(image->bounds(), IteratorStart::TopLeft);
|
|
|
|
while (it.nextLine()) {
|
|
|
|
auto* addr = (typename ImageTraits::address_t)it.addr8();
|
|
|
|
for (int u = 0; u < w; ++u, ++addr) {
|
|
|
|
auto expected = get_pixel_fast<ImageTraits>(image.get(), u, v);
|
|
|
|
ASSERT_EQ(expected, *addr);
|
|
|
|
}
|
|
|
|
++v;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// TopRight
|
|
|
|
{
|
|
|
|
int v = 0;
|
|
|
|
auto it = image->readArea(image->bounds(), IteratorStart::TopRight);
|
|
|
|
while (it.nextLine()) {
|
|
|
|
auto* addr = (typename ImageTraits::address_t)it.addr8();
|
|
|
|
for (int u = w - 1; u >= 0; --u, --addr) {
|
|
|
|
auto expected = get_pixel_fast<ImageTraits>(image.get(), u, v);
|
|
|
|
ASSERT_EQ(expected, *addr);
|
|
|
|
}
|
|
|
|
++v;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// BottomLeft
|
|
|
|
{
|
|
|
|
int v = h - 1;
|
|
|
|
auto it = image->readArea(image->bounds(), IteratorStart::BottomLeft);
|
|
|
|
while (it.nextLine()) {
|
|
|
|
auto* addr = (typename ImageTraits::address_t)it.addr8();
|
|
|
|
for (int u = 0; u < w; ++u, ++addr) {
|
|
|
|
auto expected = get_pixel_fast<ImageTraits>(image.get(), u, v);
|
|
|
|
ASSERT_EQ(expected, *addr);
|
|
|
|
}
|
|
|
|
--v;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// BottomRight
|
|
|
|
{
|
|
|
|
int v = h - 1;
|
|
|
|
auto it = image->readArea(image->bounds(), IteratorStart::BottomRight);
|
|
|
|
while (it.nextLine()) {
|
|
|
|
auto* addr = (typename ImageTraits::address_t)it.addr8();
|
|
|
|
for (int u = w - 1; u >= 0; --u, --addr) {
|
|
|
|
auto expected = get_pixel_fast<ImageTraits>(image.get(), u, v);
|
|
|
|
ASSERT_EQ(expected, *addr);
|
|
|
|
}
|
|
|
|
--v;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-11-10 06:59:05 +08:00
|
|
|
int main(int argc, char** argv)
|
|
|
|
{
|
|
|
|
::testing::InitGoogleTest(&argc, argv);
|
|
|
|
return RUN_ALL_TESTS();
|
|
|
|
}
|