Add image buffers and mask to apply symmetry to image brushes

This commit is contained in:
Gaspar Capello 2024-11-01 11:11:38 -03:00
parent 9e396227c2
commit 053a538272
2 changed files with 150 additions and 0 deletions

View File

@ -13,6 +13,7 @@
#include "base/pi.h"
#include "doc/algo.h"
#include "doc/algorithm/flip_image.h"
#include "doc/algorithm/polygon.h"
#include "doc/blend_internals.h"
#include "doc/image.h"
@ -286,6 +287,7 @@ void Brush::setImageColor(const ImageColor imageColor,
(m_bgColor ? true: false), (m_bgColor ? *m_bgColor: 0));
break;
}
resetSymmetries();
}
void Brush::resetImageColors()
@ -319,6 +321,133 @@ static void algo_hline(int x1, int y, int x2, void *data)
draw_hline(reinterpret_cast<Image*>(data), x1, y, x2, BitmapTraits::max_value);
}
void Brush::resetSymmetries()
{
m_symmetryImages.resize(0);
m_symmetryMasks.resize(0);
}
void Brush::reserveSymmetries()
{
m_symmetryImages.resize(int(SymmetryIndex::ELEMENTS));
m_symmetryMasks.resize(int(SymmetryIndex::ELEMENTS));
}
Image* Brush::getSymmetryImage(const SymmetryIndex index)
{
if (index <= SymmetryIndex::ORIGINAL ||
index >= SymmetryIndex::ELEMENTS)
return m_image.get();
if (m_symmetryImages.empty())
reserveSymmetries();
const int i = int(index);
if (!m_symmetryImages[i]) {
switch (index) {
case SymmetryIndex::FLIPPED_X:
case SymmetryIndex::FLIPPED_Y: {
const doc::algorithm::FlipType flip =
(index == SymmetryIndex::FLIPPED_X ? doc::algorithm::FlipType::FlipHorizontal:
doc::algorithm::FlipType::FlipVertical);
if (m_image) {
std::unique_ptr<Image> tempImage(Image::createCopy(m_image.get()));
doc::algorithm::flip_image(tempImage.get(), tempImage->bounds(), flip);
m_symmetryImages[i].reset(tempImage.release());
}
if (m_maskBitmap && !m_symmetryMasks[i]) {
std::unique_ptr<Image> tempImage(Image::createCopy(m_maskBitmap.get()));
doc::algorithm::flip_image(tempImage.get(), tempImage->bounds(), flip);
m_symmetryMasks[i].reset(tempImage.release());
}
break;
}
case SymmetryIndex::FLIPPED_XY: {
if (m_image) {
std::unique_ptr<Image> tempImage(Image::createCopy(m_image.get()));
doc::algorithm::flip_image(tempImage.get(),
tempImage->bounds(),
doc::algorithm::FlipType::FlipVertical);
doc::algorithm::flip_image(tempImage.get(),
tempImage->bounds(),
doc::algorithm::FlipType::FlipHorizontal);
m_symmetryImages[i].reset(tempImage.release());
}
if (m_maskBitmap && !m_symmetryMasks[i]) {
std::unique_ptr<Image> tempImage(Image::createCopy(m_maskBitmap.get()));
doc::algorithm::flip_image(tempImage.get(),
tempImage->bounds(),
doc::algorithm::FlipType::FlipVertical);
doc::algorithm::flip_image(tempImage.get(),
tempImage->bounds(),
doc::algorithm::FlipType::FlipHorizontal);
m_symmetryMasks[i].reset(tempImage.release());
}
break;
}
case SymmetryIndex::ROT_FLIP_90:
case SymmetryIndex::ROT_FLIP_270: {
const double angle = (index == SymmetryIndex::ROT_FLIP_90? 90.0 : -90.0);
if (m_image) {
std::unique_ptr<Image> tempImage(
Image::create(m_image->pixelFormat(),
m_image->height(),
m_image->width()));
rotate_image(m_image.get(), tempImage.get(), angle);
doc::algorithm::flip_image(tempImage.get(),
tempImage->bounds(),
doc::algorithm::FlipType::FlipHorizontal);
m_symmetryImages[i].reset(tempImage.release());
}
if (m_maskBitmap && !m_symmetryMasks[i]) {
std::unique_ptr<Image> tempImage(
Image::create(m_maskBitmap->pixelFormat(),
m_maskBitmap->height(),
m_maskBitmap->width()));
rotate_image(m_maskBitmap.get(), tempImage.get(), angle);
doc::algorithm::flip_image(tempImage.get(),
tempImage->bounds(),
doc::algorithm::FlipType::FlipHorizontal);
m_symmetryMasks[i].reset(tempImage.release());
}
break;
}
case SymmetryIndex::ROTATED_90:
case SymmetryIndex::ROTATED_270: {
const double angle = (index == SymmetryIndex::ROTATED_90? 90.0 : -90.0);
if (m_image) {
std::unique_ptr<Image> tempImage(
Image::create(m_image.get()->pixelFormat(),
m_image.get()->height(),
m_image.get()->width()));
rotate_image(m_image.get(), tempImage.get(), angle);
m_symmetryImages[i].reset(tempImage.release());
}
if (m_maskBitmap && !m_symmetryMasks[i]) {
std::unique_ptr<Image> tempImage(
Image::create(m_maskBitmap->pixelFormat(),
m_maskBitmap->height(),
m_maskBitmap->width()));
rotate_image(m_maskBitmap.get(), tempImage.get(), angle);
m_symmetryMasks[i].reset(tempImage.release());
}
}
}
}
return m_symmetryImages[i].get();
}
Image* Brush::getSymmetryMask(const SymmetryIndex index)
{
if (index <= SymmetryIndex::ORIGINAL ||
index >= SymmetryIndex::ELEMENTS)
return m_maskBitmap.get();
getSymmetryImage(index); // Update Image and Mask symmetry buffers
return m_symmetryMasks[int(index)].get();
}
// Regenerates the brush bitmap and its rectangle's region.
void Brush::regenerate()
{

View File

@ -22,6 +22,18 @@
namespace doc {
enum class SymmetryIndex {
ORIGINAL = 0,
FLIPPED_X = 1,
FLIPPED_Y = 2,
FLIPPED_XY = 3,
ROTATED_270 = 4,
ROT_FLIP_90 = 5,
ROTATED_90 = 6,
ROT_FLIP_270 = 7,
ELEMENTS = 8
};
class Brush;
using BrushRef = std::shared_ptr<Brush>;
@ -93,6 +105,11 @@ namespace doc {
return m_image.get();
}
void resetSymmetries();
void reserveSymmetries();
Image* getSymmetryImage(const SymmetryIndex index);
Image* getSymmetryMask(const SymmetryIndex index);
private:
void clean();
void regenerate();
@ -112,6 +129,10 @@ namespace doc {
ImageRef m_patternImage;
int m_gen;
//Symmetry image/mask buffers
std::vector<ImageRef> m_symmetryImages;
std::vector<ImageRef> m_symmetryMasks;
// Extra data used for setImageColor()
ImageRef m_backupImage; // Backup image to avoid losing original brush colors/pattern
std::optional<color_t> m_mainColor; // Main image brush color