2014-10-21 09:21:31 +08:00
|
|
|
// Aseprite Document Library
|
2025-08-22 21:22:35 +08:00
|
|
|
// Copyright (C) 2019-2025 Igara Studio S.A.
|
2019-09-12 05:19:29 +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.
|
2007-09-19 07:57:02 +08:00
|
|
|
|
2013-08-06 08:20:19 +08:00
|
|
|
#ifdef HAVE_CONFIG_H
|
2007-09-19 07:57:02 +08:00
|
|
|
#include "config.h"
|
2013-08-06 08:20:19 +08:00
|
|
|
#endif
|
2007-09-19 07:57:02 +08:00
|
|
|
|
2024-12-19 20:36:27 +08:00
|
|
|
#include "doc/image.h"
|
2014-10-21 09:21:31 +08:00
|
|
|
#include "doc/mask.h"
|
2024-07-05 22:23:13 +08:00
|
|
|
#include "gfx/point.h"
|
2012-01-09 09:34:36 +08:00
|
|
|
|
|
|
|
#include <cstdlib>
|
|
|
|
#include <cstring>
|
2007-09-19 07:57:02 +08:00
|
|
|
|
2014-10-21 09:21:31 +08:00
|
|
|
namespace doc {
|
2008-10-01 09:27:51 +08:00
|
|
|
|
2018-03-16 03:05:56 +08:00
|
|
|
namespace {
|
|
|
|
|
|
|
|
template<typename Func>
|
|
|
|
void for_each_mask_pixel(Mask& a, const Mask& b, Func f)
|
|
|
|
{
|
|
|
|
a.reserve(b.bounds());
|
2024-12-17 01:52:19 +08:00
|
|
|
|
2018-03-16 03:05:56 +08:00
|
|
|
{
|
|
|
|
LockImageBits<BitmapTraits> aBits(a.bitmap());
|
|
|
|
auto aIt = aBits.begin();
|
2024-12-17 01:52:19 +08:00
|
|
|
|
2018-03-16 03:05:56 +08:00
|
|
|
auto bounds = a.bounds();
|
|
|
|
for (int y = 0; y < bounds.h; ++y) {
|
|
|
|
for (int x = 0; x < bounds.w; ++x, ++aIt) {
|
|
|
|
color_t aColor = *aIt;
|
|
|
|
color_t bColor = (b.containsPoint(bounds.x + x, bounds.y + y) ? 1 : 0);
|
|
|
|
*aIt = f(aColor, bColor);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
a.shrink();
|
2024-12-17 01:52:19 +08:00
|
|
|
}
|
|
|
|
|
2018-03-16 03:05:56 +08:00
|
|
|
} // namespace
|
|
|
|
|
2014-10-21 09:21:31 +08:00
|
|
|
Mask::Mask() : Object(ObjectType::Mask)
|
2007-09-19 07:57:02 +08:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2008-10-01 09:27:51 +08:00
|
|
|
Mask::Mask(const Mask& mask) : Object(mask)
|
2007-09-19 07:57:02 +08:00
|
|
|
{
|
2012-01-09 09:34:36 +08:00
|
|
|
copyFrom(&mask);
|
|
|
|
}
|
|
|
|
|
2008-10-01 09:27:51 +08:00
|
|
|
Mask::~Mask()
|
|
|
|
{
|
2025-08-22 21:22:35 +08:00
|
|
|
ASSERT(m_freezes == 0);
|
2008-10-01 09:27:51 +08:00
|
|
|
}
|
2007-09-19 07:57:02 +08:00
|
|
|
|
2010-10-04 02:51:03 +08:00
|
|
|
int Mask::getMemSize() const
|
|
|
|
{
|
2012-01-09 09:34:36 +08:00
|
|
|
return sizeof(Mask) + (m_bitmap ? m_bitmap->getMemSize() : 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Mask::setName(const char* name)
|
|
|
|
{
|
|
|
|
m_name = name;
|
2010-10-04 02:51:03 +08:00
|
|
|
}
|
|
|
|
|
2010-01-28 05:16:49 +08:00
|
|
|
void Mask::freeze()
|
|
|
|
{
|
2025-08-22 21:22:35 +08:00
|
|
|
ASSERT(m_freezes >= 0);
|
|
|
|
m_freezes++;
|
2010-01-28 05:16:49 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void Mask::unfreeze()
|
|
|
|
{
|
2025-08-22 21:22:35 +08:00
|
|
|
ASSERT(m_freezes > 0);
|
|
|
|
m_freezes--;
|
2010-01-28 05:16:49 +08:00
|
|
|
|
|
|
|
// Shrink just in case
|
2025-08-22 21:22:35 +08:00
|
|
|
if (m_freezes == 0)
|
2010-01-28 05:16:49 +08:00
|
|
|
shrink();
|
|
|
|
}
|
|
|
|
|
2012-02-21 06:12:06 +08:00
|
|
|
bool Mask::isRectangular() const
|
|
|
|
{
|
|
|
|
if (!m_bitmap)
|
|
|
|
return false;
|
|
|
|
|
2015-04-03 07:42:43 +08:00
|
|
|
LockImageBits<BitmapTraits> bits(m_bitmap.get());
|
2013-11-10 06:59:05 +08:00
|
|
|
LockImageBits<BitmapTraits>::iterator it = bits.begin(), end = bits.end();
|
2012-02-21 06:12:06 +08:00
|
|
|
|
2013-11-10 06:59:05 +08:00
|
|
|
for (; it != end; ++it) {
|
|
|
|
if (*it == 0)
|
|
|
|
return false;
|
2012-02-21 06:12:06 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2012-01-09 09:34:36 +08:00
|
|
|
void Mask::copyFrom(const Mask* sourceMask)
|
2007-09-19 07:57:02 +08:00
|
|
|
{
|
2025-08-22 21:22:35 +08:00
|
|
|
ASSERT(m_freezes == 0);
|
2019-09-12 05:19:29 +08:00
|
|
|
|
2012-01-09 09:34:36 +08:00
|
|
|
clear();
|
2014-07-30 12:28:15 +08:00
|
|
|
setName(sourceMask->name().c_str());
|
2007-09-19 07:57:02 +08:00
|
|
|
|
2014-07-30 12:28:15 +08:00
|
|
|
if (sourceMask->bitmap()) {
|
2012-01-09 09:34:36 +08:00
|
|
|
// Add all the area of "mask"
|
2014-07-30 12:28:15 +08:00
|
|
|
add(sourceMask->bounds());
|
2007-09-19 07:57:02 +08:00
|
|
|
|
2019-09-12 05:19:29 +08:00
|
|
|
// And copy the "mask" bitmap (m_bitmap can be nullptr if this is
|
|
|
|
// frozen, so add() doesn't created the bitmap)
|
|
|
|
if (m_bitmap)
|
|
|
|
copy_image(m_bitmap.get(), sourceMask->m_bitmap.get());
|
2007-09-19 07:57:02 +08:00
|
|
|
}
|
2024-07-05 22:23:13 +08:00
|
|
|
}
|
|
|
|
|
2024-08-14 20:49:18 +08:00
|
|
|
void Mask::fromImage(const Image* image, const gfx::Point& maskOrigin, uint8_t alphaThreshold)
|
2024-07-05 22:23:13 +08:00
|
|
|
{
|
|
|
|
if (image) {
|
|
|
|
replace(image->bounds().setOrigin(maskOrigin));
|
|
|
|
freeze();
|
|
|
|
{
|
|
|
|
LockImageBits<BitmapTraits> maskBits(bitmap());
|
|
|
|
auto maskIt = maskBits.begin();
|
|
|
|
auto maskEnd = maskBits.end();
|
|
|
|
|
|
|
|
switch (image->pixelFormat()) {
|
|
|
|
case IMAGE_RGB: {
|
|
|
|
LockImageBits<RgbTraits> rgbBits(image);
|
|
|
|
auto rgbIt = rgbBits.begin();
|
|
|
|
#if _DEBUG
|
|
|
|
auto rgbEnd = rgbBits.end();
|
|
|
|
#endif
|
|
|
|
for (; maskIt != maskEnd; ++maskIt, ++rgbIt) {
|
|
|
|
ASSERT(rgbIt != rgbEnd);
|
|
|
|
color_t c = *rgbIt;
|
2024-08-14 20:49:18 +08:00
|
|
|
*maskIt = (rgba_geta(c) > alphaThreshold);
|
2024-07-05 22:23:13 +08:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case IMAGE_GRAYSCALE: {
|
|
|
|
LockImageBits<GrayscaleTraits> grayBits(image);
|
|
|
|
auto grayIt = grayBits.begin();
|
|
|
|
#if _DEBUG
|
|
|
|
auto grayEnd = grayBits.end();
|
|
|
|
#endif
|
|
|
|
for (; maskIt != maskEnd; ++maskIt, ++grayIt) {
|
|
|
|
ASSERT(grayIt != grayEnd);
|
|
|
|
color_t c = *grayIt;
|
2024-08-14 20:49:18 +08:00
|
|
|
*maskIt = (graya_geta(c) > alphaThreshold);
|
2024-07-05 22:23:13 +08:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case IMAGE_INDEXED: {
|
|
|
|
const doc::color_t maskColor = image->maskColor();
|
|
|
|
LockImageBits<IndexedTraits> idxBits(image);
|
|
|
|
auto idxIt = idxBits.begin();
|
|
|
|
#if _DEBUG
|
|
|
|
auto idxEnd = idxBits.end();
|
|
|
|
#endif
|
|
|
|
for (; maskIt != maskEnd; ++maskIt, ++idxIt) {
|
|
|
|
ASSERT(idxIt != idxEnd);
|
|
|
|
color_t c = *idxIt;
|
|
|
|
*maskIt = (c != maskColor);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
unfreeze();
|
|
|
|
}
|
2007-09-19 07:57:02 +08:00
|
|
|
}
|
|
|
|
|
2012-01-09 09:34:36 +08:00
|
|
|
void Mask::offsetOrigin(int dx, int dy)
|
2007-09-19 07:57:02 +08:00
|
|
|
{
|
2012-01-09 09:34:36 +08:00
|
|
|
m_bounds.offset(dx, dy);
|
2007-09-19 07:57:02 +08:00
|
|
|
}
|
|
|
|
|
2012-01-09 09:34:36 +08:00
|
|
|
void Mask::clear()
|
2007-09-19 07:57:02 +08:00
|
|
|
{
|
2015-04-03 07:42:43 +08:00
|
|
|
m_bitmap.reset();
|
2012-01-09 09:34:36 +08:00
|
|
|
m_bounds = gfx::Rect(0, 0, 0, 0);
|
2007-09-19 07:57:02 +08:00
|
|
|
}
|
|
|
|
|
2012-01-09 09:34:36 +08:00
|
|
|
void Mask::invert()
|
2007-09-19 07:57:02 +08:00
|
|
|
{
|
2015-01-19 09:05:33 +08:00
|
|
|
if (!m_bitmap)
|
|
|
|
return;
|
|
|
|
|
2015-04-03 07:42:43 +08:00
|
|
|
LockImageBits<BitmapTraits> bits(m_bitmap.get());
|
2015-01-19 09:05:33 +08:00
|
|
|
LockImageBits<BitmapTraits>::iterator it = bits.begin(), end = bits.end();
|
2013-11-10 06:59:05 +08:00
|
|
|
|
2015-01-19 09:05:33 +08:00
|
|
|
for (; it != end; ++it)
|
|
|
|
*it = (*it ? 0 : 1);
|
2007-09-19 07:57:02 +08:00
|
|
|
|
2015-01-19 09:05:33 +08:00
|
|
|
shrink();
|
2007-09-19 07:57:02 +08:00
|
|
|
}
|
|
|
|
|
2014-12-14 06:10:54 +08:00
|
|
|
void Mask::replace(const gfx::Rect& bounds)
|
2007-09-19 07:57:02 +08:00
|
|
|
{
|
2020-09-19 06:29:43 +08:00
|
|
|
if (bounds.isEmpty()) {
|
|
|
|
clear();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-12-14 06:10:54 +08:00
|
|
|
m_bounds = bounds;
|
2007-09-19 07:57:02 +08:00
|
|
|
|
2015-01-19 09:05:33 +08:00
|
|
|
m_bitmap.reset(Image::create(IMAGE_BITMAP, bounds.w, bounds.h, m_buffer));
|
2015-04-03 07:42:43 +08:00
|
|
|
clear_image(m_bitmap.get(), 1);
|
2007-09-19 07:57:02 +08:00
|
|
|
}
|
|
|
|
|
2018-03-16 03:05:56 +08:00
|
|
|
void Mask::add(const doc::Mask& mask)
|
|
|
|
{
|
|
|
|
for_each_mask_pixel(*this, mask, [](color_t a, color_t b) -> color_t { return a | b; });
|
|
|
|
}
|
|
|
|
|
|
|
|
void Mask::subtract(const doc::Mask& mask)
|
|
|
|
{
|
|
|
|
for_each_mask_pixel(*this, mask, [](color_t a, color_t b) -> color_t {
|
|
|
|
if (a)
|
|
|
|
return a - b;
|
|
|
|
else
|
|
|
|
return 0;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
void Mask::intersect(const doc::Mask& mask)
|
|
|
|
{
|
|
|
|
for_each_mask_pixel(*this, mask, [](color_t a, color_t b) -> color_t { return a & b; });
|
|
|
|
}
|
|
|
|
|
2014-12-14 06:10:54 +08:00
|
|
|
void Mask::add(const gfx::Rect& bounds)
|
2010-01-28 05:16:49 +08:00
|
|
|
{
|
2025-08-22 21:22:35 +08:00
|
|
|
if (m_freezes == 0)
|
2014-12-14 06:10:54 +08:00
|
|
|
reserve(bounds);
|
2007-09-19 07:57:02 +08:00
|
|
|
|
2025-08-22 21:22:35 +08:00
|
|
|
// m_bitmap can be nullptr if we have m_freezes > 0
|
2019-09-12 05:19:29 +08:00
|
|
|
if (!m_bitmap)
|
|
|
|
return;
|
|
|
|
|
2015-04-03 07:42:43 +08:00
|
|
|
fill_rect(m_bitmap.get(),
|
2019-09-12 05:19:29 +08:00
|
|
|
bounds.x - m_bounds.x,
|
|
|
|
bounds.y - m_bounds.y,
|
|
|
|
bounds.x - m_bounds.x + bounds.w - 1,
|
|
|
|
bounds.y - m_bounds.y + bounds.h - 1,
|
|
|
|
1);
|
2007-09-19 07:57:02 +08:00
|
|
|
}
|
|
|
|
|
2014-12-14 06:10:54 +08:00
|
|
|
void Mask::subtract(const gfx::Rect& bounds)
|
2012-01-09 09:34:36 +08:00
|
|
|
{
|
2015-01-19 09:05:33 +08:00
|
|
|
if (!m_bitmap)
|
|
|
|
return;
|
2014-12-14 06:10:54 +08:00
|
|
|
|
2015-04-03 07:42:43 +08:00
|
|
|
fill_rect(m_bitmap.get(),
|
2015-01-19 09:05:33 +08:00
|
|
|
bounds.x - m_bounds.x,
|
|
|
|
bounds.y - m_bounds.y,
|
|
|
|
bounds.x - m_bounds.x + bounds.w - 1,
|
|
|
|
bounds.y - m_bounds.y + bounds.h - 1,
|
|
|
|
0);
|
|
|
|
|
|
|
|
shrink();
|
2007-09-19 07:57:02 +08:00
|
|
|
}
|
|
|
|
|
2014-12-14 06:10:54 +08:00
|
|
|
void Mask::intersect(const gfx::Rect& bounds)
|
2007-09-19 07:57:02 +08:00
|
|
|
{
|
2015-01-19 09:05:33 +08:00
|
|
|
if (!m_bitmap)
|
|
|
|
return;
|
2007-09-19 07:57:02 +08:00
|
|
|
|
2015-06-10 03:22:47 +08:00
|
|
|
gfx::Rect newBounds = m_bounds.createIntersection(bounds);
|
2014-12-14 06:10:54 +08:00
|
|
|
|
2015-01-19 09:05:33 +08:00
|
|
|
Image* image = NULL;
|
2007-09-19 07:57:02 +08:00
|
|
|
|
2015-01-19 09:05:33 +08:00
|
|
|
if (!newBounds.isEmpty()) {
|
2020-04-23 07:20:04 +08:00
|
|
|
image = crop_image(m_bitmap.get(),
|
2015-01-19 09:05:33 +08:00
|
|
|
newBounds.x - m_bounds.x,
|
|
|
|
newBounds.y - m_bounds.y,
|
|
|
|
newBounds.w,
|
|
|
|
newBounds.h,
|
|
|
|
0);
|
2007-09-19 07:57:02 +08:00
|
|
|
}
|
2015-01-19 09:05:33 +08:00
|
|
|
|
|
|
|
m_bitmap.reset(image);
|
|
|
|
m_bounds = newBounds;
|
|
|
|
|
|
|
|
shrink();
|
2007-09-19 07:57:02 +08:00
|
|
|
}
|
|
|
|
|
2012-01-09 09:34:36 +08:00
|
|
|
void Mask::byColor(const Image* src, int color, int fuzziness)
|
2007-09-19 07:57:02 +08:00
|
|
|
{
|
2014-12-14 06:10:54 +08:00
|
|
|
replace(src->bounds());
|
2007-09-19 07:57:02 +08:00
|
|
|
|
2015-04-03 07:42:43 +08:00
|
|
|
Image* dst = m_bitmap.get();
|
2007-09-19 07:57:02 +08:00
|
|
|
|
2014-07-30 12:28:15 +08:00
|
|
|
switch (src->pixelFormat()) {
|
2007-09-19 07:57:02 +08:00
|
|
|
case IMAGE_RGB: {
|
2013-11-10 06:59:05 +08:00
|
|
|
const LockImageBits<RgbTraits> srcBits(src);
|
|
|
|
LockImageBits<BitmapTraits> dstBits(dst, Image::WriteLock);
|
|
|
|
LockImageBits<RgbTraits>::const_iterator src_it = srcBits.begin(), src_end = srcBits.end();
|
2014-08-18 07:33:32 +08:00
|
|
|
LockImageBits<BitmapTraits>::iterator dst_it = dstBits.begin();
|
|
|
|
#ifdef _DEBUG
|
|
|
|
LockImageBits<BitmapTraits>::iterator dst_end = dstBits.end();
|
|
|
|
#endif
|
2007-09-19 07:57:02 +08:00
|
|
|
int src_r, src_g, src_b, src_a;
|
|
|
|
int dst_r, dst_g, dst_b, dst_a;
|
2013-11-10 06:59:05 +08:00
|
|
|
color_t c;
|
|
|
|
|
|
|
|
dst_r = rgba_getr(color);
|
|
|
|
dst_g = rgba_getg(color);
|
|
|
|
dst_b = rgba_getb(color);
|
|
|
|
dst_a = rgba_geta(color);
|
|
|
|
|
|
|
|
for (; src_it != src_end; ++src_it, ++dst_it) {
|
|
|
|
ASSERT(dst_it != dst_end);
|
|
|
|
c = *src_it;
|
|
|
|
|
|
|
|
src_r = rgba_getr(c);
|
|
|
|
src_g = rgba_getg(c);
|
|
|
|
src_b = rgba_getb(c);
|
|
|
|
src_a = rgba_geta(c);
|
|
|
|
|
|
|
|
if (!((src_r >= dst_r - fuzziness) && (src_r <= dst_r + fuzziness) &&
|
|
|
|
(src_g >= dst_g - fuzziness) && (src_g <= dst_g + fuzziness) &&
|
|
|
|
(src_b >= dst_b - fuzziness) && (src_b <= dst_b + fuzziness) &&
|
|
|
|
(src_a >= dst_a - fuzziness) && (src_a <= dst_a + fuzziness)))
|
2013-11-11 05:42:34 +08:00
|
|
|
*dst_it = 0;
|
2007-09-19 07:57:02 +08:00
|
|
|
}
|
2013-11-10 06:59:05 +08:00
|
|
|
ASSERT(dst_it == dst_end);
|
|
|
|
break;
|
|
|
|
}
|
2007-09-19 07:57:02 +08:00
|
|
|
|
|
|
|
case IMAGE_GRAYSCALE: {
|
2013-11-10 06:59:05 +08:00
|
|
|
const LockImageBits<GrayscaleTraits> srcBits(src);
|
|
|
|
LockImageBits<BitmapTraits> dstBits(dst, Image::WriteLock);
|
|
|
|
LockImageBits<GrayscaleTraits>::const_iterator src_it = srcBits.begin(),
|
|
|
|
src_end = srcBits.end();
|
2014-08-18 07:33:32 +08:00
|
|
|
LockImageBits<BitmapTraits>::iterator dst_it = dstBits.begin();
|
|
|
|
#ifdef _DEBUG
|
|
|
|
LockImageBits<BitmapTraits>::iterator dst_end = dstBits.end();
|
|
|
|
#endif
|
2007-09-19 07:57:02 +08:00
|
|
|
int src_k, src_a;
|
|
|
|
int dst_k, dst_a;
|
2013-11-10 06:59:05 +08:00
|
|
|
color_t c;
|
2007-09-19 07:57:02 +08:00
|
|
|
|
2013-11-10 06:59:05 +08:00
|
|
|
dst_k = graya_getv(color);
|
|
|
|
dst_a = graya_geta(color);
|
2007-09-19 07:57:02 +08:00
|
|
|
|
2013-11-10 06:59:05 +08:00
|
|
|
for (; src_it != src_end; ++src_it, ++dst_it) {
|
|
|
|
ASSERT(dst_it != dst_end);
|
|
|
|
c = *src_it;
|
2007-09-19 07:57:02 +08:00
|
|
|
|
2013-11-10 06:59:05 +08:00
|
|
|
src_k = graya_getv(c);
|
|
|
|
src_a = graya_geta(c);
|
2007-09-19 07:57:02 +08:00
|
|
|
|
2013-11-10 06:59:05 +08:00
|
|
|
if (!((src_k >= dst_k - fuzziness) && (src_k <= dst_k + fuzziness) &&
|
|
|
|
(src_a >= dst_a - fuzziness) && (src_a <= dst_a + fuzziness)))
|
2013-11-11 05:42:34 +08:00
|
|
|
*dst_it = 0;
|
2007-09-19 07:57:02 +08:00
|
|
|
}
|
2013-11-10 06:59:05 +08:00
|
|
|
ASSERT(dst_it == dst_end);
|
|
|
|
break;
|
|
|
|
}
|
2007-09-19 07:57:02 +08:00
|
|
|
|
|
|
|
case IMAGE_INDEXED: {
|
2013-11-10 06:59:05 +08:00
|
|
|
const LockImageBits<IndexedTraits> srcBits(src);
|
|
|
|
LockImageBits<BitmapTraits> dstBits(dst, Image::WriteLock);
|
|
|
|
LockImageBits<IndexedTraits>::const_iterator src_it = srcBits.begin(),
|
|
|
|
src_end = srcBits.end();
|
2014-08-18 07:33:32 +08:00
|
|
|
LockImageBits<BitmapTraits>::iterator dst_it = dstBits.begin();
|
|
|
|
#ifdef _DEBUG
|
|
|
|
LockImageBits<BitmapTraits>::iterator dst_end = dstBits.end();
|
|
|
|
#endif
|
2013-11-10 06:59:05 +08:00
|
|
|
color_t c, min, max;
|
|
|
|
|
|
|
|
for (; src_it != src_end; ++src_it, ++dst_it) {
|
|
|
|
ASSERT(dst_it != dst_end);
|
|
|
|
c = *src_it;
|
|
|
|
|
|
|
|
if (color > fuzziness)
|
|
|
|
min = color - fuzziness;
|
|
|
|
else
|
|
|
|
min = 0;
|
|
|
|
max = color + fuzziness;
|
|
|
|
|
|
|
|
if (!((c >= min) && (c <= max)))
|
2013-11-11 05:42:34 +08:00
|
|
|
*dst_it = 0;
|
2007-09-19 07:57:02 +08:00
|
|
|
}
|
2013-11-10 06:59:05 +08:00
|
|
|
ASSERT(dst_it == dst_end);
|
|
|
|
break;
|
|
|
|
}
|
2007-09-19 07:57:02 +08:00
|
|
|
}
|
|
|
|
|
2012-01-09 09:34:36 +08:00
|
|
|
shrink();
|
2007-09-19 07:57:02 +08:00
|
|
|
}
|
|
|
|
|
2012-01-09 09:34:36 +08:00
|
|
|
void Mask::crop(const Image* image)
|
2007-09-19 07:57:02 +08:00
|
|
|
{
|
2012-01-06 06:45:03 +08:00
|
|
|
#define ADVANCE(beg, end, o_end, cmp, op, getpixel1, getpixel) \
|
|
|
|
{ \
|
|
|
|
done = true; \
|
|
|
|
for (beg = beg_##beg; beg cmp beg_##end; beg op) { \
|
|
|
|
old_color = getpixel1; \
|
|
|
|
done = true; \
|
|
|
|
for (c++; c <= beg_##o_end; c++) { \
|
|
|
|
if (getpixel != old_color) { \
|
|
|
|
done = false; \
|
|
|
|
break; \
|
|
|
|
} \
|
|
|
|
} \
|
|
|
|
if (!done) \
|
|
|
|
break; \
|
|
|
|
} \
|
|
|
|
if (done) \
|
|
|
|
done_count++; \
|
2007-09-19 07:57:02 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
int beg_x1, beg_y1, beg_x2, beg_y2;
|
2015-03-06 02:19:00 +08:00
|
|
|
int c, x1, y1, x2, y2;
|
2007-09-19 07:57:02 +08:00
|
|
|
int done_count = 0;
|
|
|
|
int done;
|
2015-03-06 02:19:00 +08:00
|
|
|
color_t old_color;
|
2007-09-19 07:57:02 +08:00
|
|
|
|
2012-01-09 09:34:36 +08:00
|
|
|
if (!m_bitmap)
|
2007-09-19 07:57:02 +08:00
|
|
|
return;
|
|
|
|
|
2012-01-09 09:34:36 +08:00
|
|
|
beg_x1 = m_bounds.x;
|
|
|
|
beg_y1 = m_bounds.y;
|
|
|
|
beg_x2 = beg_x1 + m_bounds.w - 1;
|
|
|
|
beg_y2 = beg_y1 + m_bounds.h - 1;
|
2007-09-19 07:57:02 +08:00
|
|
|
|
2022-06-10 05:28:06 +08:00
|
|
|
beg_x1 = std::clamp(beg_x1, 0, m_bounds.w - 1);
|
|
|
|
beg_y1 = std::clamp(beg_y1, 0, m_bounds.h - 1);
|
|
|
|
beg_x2 = std::clamp(beg_x2, beg_x1, m_bounds.w - 1);
|
|
|
|
beg_y2 = std::clamp(beg_y2, beg_y1, m_bounds.h - 1);
|
2007-09-19 07:57:02 +08:00
|
|
|
|
|
|
|
/* left */
|
2013-11-10 06:59:05 +08:00
|
|
|
ADVANCE(x1, x2, y2, <=, ++, get_pixel(image, x1, c = beg_y1), get_pixel(image, x1, c));
|
2007-09-19 07:57:02 +08:00
|
|
|
/* right */
|
2013-11-10 06:59:05 +08:00
|
|
|
ADVANCE(x2, x1, y2, >=, --, get_pixel(image, x2, c = beg_y1), get_pixel(image, x2, c));
|
2007-09-19 07:57:02 +08:00
|
|
|
/* top */
|
2013-11-10 06:59:05 +08:00
|
|
|
ADVANCE(y1, y2, x2, <=, ++, get_pixel(image, c = beg_x1, y1), get_pixel(image, c, y1));
|
2007-09-19 07:57:02 +08:00
|
|
|
/* bottom */
|
2013-11-10 06:59:05 +08:00
|
|
|
ADVANCE(y2, y1, x2, >=, --, get_pixel(image, c = beg_x1, y2), get_pixel(image, c, y2));
|
2007-09-19 07:57:02 +08:00
|
|
|
|
|
|
|
if (done_count < 4)
|
2014-12-14 06:10:54 +08:00
|
|
|
intersect(gfx::Rect(x1, y1, x2 - x1 + 1, y2 - y1 + 1));
|
2007-09-19 07:57:02 +08:00
|
|
|
else
|
2012-01-09 09:34:36 +08:00
|
|
|
clear();
|
2007-09-19 07:57:02 +08:00
|
|
|
|
|
|
|
#undef ADVANCE
|
|
|
|
}
|
|
|
|
|
2014-12-14 06:10:54 +08:00
|
|
|
void Mask::reserve(const gfx::Rect& bounds)
|
2010-01-28 05:16:49 +08:00
|
|
|
{
|
2014-12-14 06:10:54 +08:00
|
|
|
ASSERT(!bounds.isEmpty());
|
2010-01-28 05:16:49 +08:00
|
|
|
|
2012-01-09 09:34:36 +08:00
|
|
|
if (!m_bitmap) {
|
2014-12-14 06:10:54 +08:00
|
|
|
m_bounds = bounds;
|
2015-01-19 09:05:33 +08:00
|
|
|
m_bitmap.reset(Image::create(IMAGE_BITMAP, bounds.w, bounds.h, m_buffer));
|
2015-04-03 07:42:43 +08:00
|
|
|
clear_image(m_bitmap.get(), 0);
|
2010-01-28 05:16:49 +08:00
|
|
|
}
|
|
|
|
else {
|
2014-12-14 06:10:54 +08:00
|
|
|
gfx::Rect newBounds = m_bounds.createUnion(bounds);
|
|
|
|
|
|
|
|
if (m_bounds != newBounds) {
|
2020-04-23 07:20:04 +08:00
|
|
|
Image* image = crop_image(m_bitmap.get(),
|
2014-12-14 06:10:54 +08:00
|
|
|
newBounds.x - m_bounds.x,
|
|
|
|
newBounds.y - m_bounds.y,
|
|
|
|
newBounds.w,
|
|
|
|
newBounds.h,
|
|
|
|
0);
|
2015-01-19 09:05:33 +08:00
|
|
|
m_bitmap.reset(image);
|
2014-12-14 06:10:54 +08:00
|
|
|
m_bounds = newBounds;
|
2010-01-28 05:16:49 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Mask::shrink()
|
2007-09-19 07:57:02 +08:00
|
|
|
{
|
2010-01-28 05:16:49 +08:00
|
|
|
// If the mask is frozen we avoid the shrinking
|
2025-08-22 21:22:35 +08:00
|
|
|
if (m_freezes > 0)
|
2010-01-28 05:16:49 +08:00
|
|
|
return;
|
2012-01-06 06:45:03 +08:00
|
|
|
|
|
|
|
#define SHRINK_SIDE(u_begin, u_op, u_final, u_add, v_begin, v_op, v_final, v_add, U, V, var) \
|
|
|
|
{ \
|
|
|
|
for (u = u_begin; u u_op u_final; u u_add) { \
|
|
|
|
for (v = v_begin; v v_op v_final; v v_add) { \
|
2015-06-26 04:53:11 +08:00
|
|
|
if (get_pixel_fast<BitmapTraits>(m_bitmap.get(), U, V)) \
|
2012-01-06 06:45:03 +08:00
|
|
|
break; \
|
|
|
|
} \
|
|
|
|
if (v == v_final) \
|
|
|
|
var; \
|
|
|
|
else \
|
|
|
|
break; \
|
|
|
|
} \
|
2007-09-19 07:57:02 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
int u, v, x1, y1, x2, y2;
|
|
|
|
|
2012-01-09 09:34:36 +08:00
|
|
|
x1 = m_bounds.x;
|
|
|
|
y1 = m_bounds.y;
|
|
|
|
x2 = m_bounds.x + m_bounds.w - 1;
|
|
|
|
y2 = m_bounds.y + m_bounds.h - 1;
|
2007-09-19 07:57:02 +08:00
|
|
|
|
2012-01-09 09:34:36 +08:00
|
|
|
SHRINK_SIDE(0, <, m_bounds.w, ++, 0, <, m_bounds.h, ++, u, v, x1++);
|
2007-09-19 07:57:02 +08:00
|
|
|
|
2012-01-09 09:34:36 +08:00
|
|
|
SHRINK_SIDE(0, <, m_bounds.h, ++, 0, <, m_bounds.w, ++, v, u, y1++);
|
2007-09-19 07:57:02 +08:00
|
|
|
|
2012-01-09 09:34:36 +08:00
|
|
|
SHRINK_SIDE(m_bounds.w - 1, >, 0, --, 0, <, m_bounds.h, ++, u, v, x2--);
|
2007-09-19 07:57:02 +08:00
|
|
|
|
2012-01-09 09:34:36 +08:00
|
|
|
SHRINK_SIDE(m_bounds.h - 1, >, 0, --, 0, <, m_bounds.w, ++, v, u, y2--);
|
2007-09-19 07:57:02 +08:00
|
|
|
|
2008-10-10 10:14:53 +08:00
|
|
|
if ((x1 > x2) || (y1 > y2)) {
|
2012-01-09 09:34:36 +08:00
|
|
|
clear();
|
2007-09-19 07:57:02 +08:00
|
|
|
}
|
2012-01-09 09:34:36 +08:00
|
|
|
else if ((x1 != m_bounds.x) || (x2 != m_bounds.x + m_bounds.w - 1) || (y1 != m_bounds.y) ||
|
|
|
|
(y2 != m_bounds.y + m_bounds.h - 1)) {
|
|
|
|
u = m_bounds.x;
|
|
|
|
v = m_bounds.y;
|
|
|
|
|
|
|
|
m_bounds.x = x1;
|
|
|
|
m_bounds.y = y1;
|
|
|
|
m_bounds.w = x2 - x1 + 1;
|
|
|
|
m_bounds.h = y2 - y1 + 1;
|
|
|
|
|
2020-04-23 07:20:04 +08:00
|
|
|
Image* image =
|
|
|
|
crop_image(m_bitmap.get(), m_bounds.x - u, m_bounds.y - v, m_bounds.w, m_bounds.h, 0);
|
2015-01-19 09:05:33 +08:00
|
|
|
m_bitmap.reset(image);
|
2007-09-19 07:57:02 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
#undef SHRINK_SIDE
|
|
|
|
}
|
2013-08-06 08:20:19 +08:00
|
|
|
|
2014-10-21 09:21:31 +08:00
|
|
|
} // namespace doc
|