mirror of https://github.com/aseprite/aseprite.git
Add new kind of Image iterators line by line: Image::read/writeArea()
This commit is contained in:
parent
70c8924719
commit
8d4c4857ee
|
@ -45,6 +45,7 @@ add_library(doc-lib
|
||||||
image.cpp
|
image.cpp
|
||||||
image_impl.cpp
|
image_impl.cpp
|
||||||
image_io.cpp
|
image_io.cpp
|
||||||
|
image_iterators2.cpp
|
||||||
layer.cpp
|
layer.cpp
|
||||||
layer_io.cpp
|
layer_io.cpp
|
||||||
layer_list.cpp
|
layer_list.cpp
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
// Aseprite Document Library
|
// Aseprite Document Library
|
||||||
// Copyright (c) 2018-2020 Igara Studio S.A.
|
// Copyright (c) 2018-2024 Igara Studio S.A.
|
||||||
// Copyright (c) 2001-2016 David Capello
|
// Copyright (c) 2001-2016 David Capello
|
||||||
//
|
//
|
||||||
// This file is released under the terms of the MIT license.
|
// This file is released under the terms of the MIT license.
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
// Aseprite Document Library
|
// Aseprite Document Library
|
||||||
// Copyright (c) 2018-2023 Igara Studio S.A.
|
// Copyright (c) 2018-2024 Igara Studio S.A.
|
||||||
// Copyright (c) 2001-2016 David Capello
|
// Copyright (c) 2001-2016 David Capello
|
||||||
//
|
//
|
||||||
// This file is released under the terms of the MIT license.
|
// This file is released under the terms of the MIT license.
|
||||||
|
@ -12,6 +12,7 @@
|
||||||
#include "doc/color.h"
|
#include "doc/color.h"
|
||||||
#include "doc/color_mode.h"
|
#include "doc/color_mode.h"
|
||||||
#include "doc/image_buffer.h"
|
#include "doc/image_buffer.h"
|
||||||
|
#include "doc/image_iterators2.h"
|
||||||
#include "doc/image_spec.h"
|
#include "doc/image_spec.h"
|
||||||
#include "doc/object.h"
|
#include "doc/object.h"
|
||||||
#include "doc/pixel_format.h"
|
#include "doc/pixel_format.h"
|
||||||
|
@ -103,6 +104,21 @@ public:
|
||||||
virtual void fillRect(int x1, int y1, int x2, int y2, color_t color) = 0;
|
virtual void fillRect(int x1, int y1, int x2, int y2, color_t color) = 0;
|
||||||
virtual void blendRect(int x1, int y1, int x2, int y2, color_t color, int opacity) = 0;
|
virtual void blendRect(int x1, int y1, int x2, int y2, color_t color, int opacity) = 0;
|
||||||
|
|
||||||
|
ReadIterator readArea() const { return ReadIterator(this, this->bounds()); }
|
||||||
|
WriteIterator writeArea() { return WriteIterator(this, this->bounds()); }
|
||||||
|
|
||||||
|
ReadIterator readArea(const gfx::Rect& bounds,
|
||||||
|
const IteratorStart start = IteratorStart::TopLeft) const
|
||||||
|
{
|
||||||
|
return ReadIterator(this, bounds, start);
|
||||||
|
}
|
||||||
|
|
||||||
|
WriteIterator writeArea(const gfx::Rect& bounds,
|
||||||
|
const IteratorStart start = IteratorStart::TopLeft)
|
||||||
|
{
|
||||||
|
return WriteIterator(this, bounds, start);
|
||||||
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
Image(const ImageSpec& spec);
|
Image(const ImageSpec& spec);
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,39 @@
|
||||||
|
// Aseprite Document Library
|
||||||
|
// Copyright (c) 2024 Igara Studio S.A.
|
||||||
|
//
|
||||||
|
// This file is released under the terms of the MIT license.
|
||||||
|
// Read LICENSE.txt for more information.
|
||||||
|
|
||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
#include "config.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "doc/image.h"
|
||||||
|
|
||||||
|
namespace doc {
|
||||||
|
|
||||||
|
ReadIterator::ReadIterator(const Image* image, const gfx::Rect& bounds, const IteratorStart start)
|
||||||
|
: m_rows(bounds.h)
|
||||||
|
{
|
||||||
|
switch (start) {
|
||||||
|
case IteratorStart::TopLeft:
|
||||||
|
m_addr = image->getPixelAddress(bounds.x, bounds.y);
|
||||||
|
m_nextRow = image->rowBytes();
|
||||||
|
break;
|
||||||
|
case IteratorStart::TopRight:
|
||||||
|
m_addr = image->getPixelAddress(bounds.x2() - 1, bounds.y);
|
||||||
|
m_nextRow = image->rowBytes();
|
||||||
|
break;
|
||||||
|
case IteratorStart::BottomLeft:
|
||||||
|
m_addr = image->getPixelAddress(bounds.x, bounds.y2() - 1);
|
||||||
|
m_nextRow = -image->rowBytes();
|
||||||
|
break;
|
||||||
|
case IteratorStart::BottomRight:
|
||||||
|
m_addr = image->getPixelAddress(bounds.x2() - 1, bounds.y2() - 1);
|
||||||
|
m_nextRow = -image->rowBytes();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
m_addr -= m_nextRow; // This is canceled in the first nextLine() call.
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace doc
|
|
@ -0,0 +1,74 @@
|
||||||
|
// Aseprite Document Library
|
||||||
|
// Copyright (c) 2024 Igara Studio S.A.
|
||||||
|
//
|
||||||
|
// This file is released under the terms of the MIT license.
|
||||||
|
// Read LICENSE.txt for more information.
|
||||||
|
|
||||||
|
#ifndef DOC_IMAGE_ITERATOR2_H_INCLUDED
|
||||||
|
#define DOC_IMAGE_ITERATOR2_H_INCLUDED
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "base/ints.h"
|
||||||
|
#include "gfx/fwd.h"
|
||||||
|
|
||||||
|
namespace doc {
|
||||||
|
|
||||||
|
class Image;
|
||||||
|
|
||||||
|
// New iterators classes.
|
||||||
|
|
||||||
|
enum class IteratorStart : uint8_t { TopLeft, TopRight, BottomLeft, BottomRight };
|
||||||
|
|
||||||
|
class ReadIterator {
|
||||||
|
public:
|
||||||
|
ReadIterator(const Image* image,
|
||||||
|
const gfx::Rect& bounds,
|
||||||
|
IteratorStart start = IteratorStart::TopLeft);
|
||||||
|
|
||||||
|
const uint8_t* addr8() const { return m_addr; }
|
||||||
|
const uint16_t* addr16() const { return (uint16_t*)m_addr; }
|
||||||
|
const uint32_t* addr32() const { return (uint32_t*)m_addr; }
|
||||||
|
|
||||||
|
template<typename ImageTraits>
|
||||||
|
typename ImageTraits::const_address_t addr() const
|
||||||
|
{
|
||||||
|
return (typename ImageTraits::const_address_t)m_addr;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool nextLine()
|
||||||
|
{
|
||||||
|
m_addr += m_nextRow;
|
||||||
|
return (m_rows-- > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
uint8_t* m_addr = nullptr;
|
||||||
|
|
||||||
|
private:
|
||||||
|
int m_rows = 0;
|
||||||
|
int m_nextRow = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
class WriteIterator : public ReadIterator {
|
||||||
|
public:
|
||||||
|
WriteIterator(Image* image,
|
||||||
|
const gfx::Rect& bounds,
|
||||||
|
const IteratorStart start = IteratorStart::TopLeft)
|
||||||
|
: ReadIterator(image, bounds, start)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t* addr8() { return m_addr; }
|
||||||
|
uint16_t* addr16() { return (uint16_t*)m_addr; }
|
||||||
|
uint32_t* addr32() { return (uint32_t*)m_addr; }
|
||||||
|
|
||||||
|
template<typename ImageTraits>
|
||||||
|
uint32_t* addr()
|
||||||
|
{
|
||||||
|
return (typename ImageTraits::address_t)m_addr;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace doc
|
||||||
|
|
||||||
|
#endif
|
|
@ -11,6 +11,7 @@
|
||||||
|
|
||||||
#include <gtest/gtest.h>
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
#include "doc/algorithm/random_image.h"
|
||||||
#include "doc/image.h"
|
#include "doc/image.h"
|
||||||
#include "doc/primitives.h"
|
#include "doc/primitives.h"
|
||||||
|
|
||||||
|
@ -25,9 +26,27 @@ protected:
|
||||||
ImageAllTypes() {}
|
ImageAllTypes() {}
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef testing::Types<RgbTraits, GrayscaleTraits, IndexedTraits, BitmapTraits> ImageAllTraits;
|
using ImageAllTraits = testing::Types<RgbTraits, GrayscaleTraits, IndexedTraits, BitmapTraits>;
|
||||||
|
|
||||||
TYPED_TEST_SUITE(ImageAllTypes, ImageAllTraits);
|
TYPED_TEST_SUITE(ImageAllTypes, ImageAllTraits);
|
||||||
|
|
||||||
|
#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
|
||||||
|
|
||||||
TYPED_TEST(ImageAllTypes, PutGetAndIterators)
|
TYPED_TEST(ImageAllTypes, PutGetAndIterators)
|
||||||
{
|
{
|
||||||
using ImageTraits = TypeParam;
|
using ImageTraits = TypeParam;
|
||||||
|
@ -195,6 +214,75 @@ TYPED_TEST(ImageAllTypes, FillRect)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int main(int argc, char** argv)
|
int main(int argc, char** argv)
|
||||||
{
|
{
|
||||||
::testing::InitGoogleTest(&argc, argv);
|
::testing::InitGoogleTest(&argc, argv);
|
||||||
|
|
Loading…
Reference in New Issue