mirror of
https://github.com/aseprite/aseprite.git
synced 2025-01-27 06:35:16 +00:00
The code was refactored moving the BlenderHelper class from "render" to "doc", and now doc::blend_image() supports blending different color modes. Some work is still needed to work with grayscale images correctly.
This commit is contained in:
parent
31f3c79566
commit
8f7bf09263
2
laf
2
laf
@ -1 +1 @@
|
||||
Subproject commit c23da3b0516540036ee501e6a35de9afc0356ab4
|
||||
Subproject commit 26994fe6c1210e0989eaddd4b2bdc00422e1ac8a
|
@ -1,5 +1,5 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2022 Igara Studio S.A.
|
||||
// Copyright (C) 2022-2024 Igara Studio S.A.
|
||||
// Copyright (C) 2001-2018 David Capello
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
@ -12,9 +12,11 @@
|
||||
#include "app/modules/palettes.h"
|
||||
|
||||
#include "app/app.h"
|
||||
#include "app/context.h"
|
||||
#include "app/extensions.h"
|
||||
#include "app/file/palette_file.h"
|
||||
#include "app/resource_finder.h"
|
||||
#include "app/site.h"
|
||||
#include "base/fs.h"
|
||||
#include "doc/image.h"
|
||||
#include "doc/palette.h"
|
||||
@ -125,6 +127,15 @@ void load_default_palette()
|
||||
// function and use the active Site palette.
|
||||
Palette* get_current_palette()
|
||||
{
|
||||
#if !ENABLE_UI
|
||||
if (auto* app = App::instance()) {
|
||||
if (auto* ctx = app->context()) {
|
||||
Site site = ctx->activeSite();
|
||||
if (site.sprite())
|
||||
return site.palette();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return ase_current_palette;
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2018-2023 Igara Studio S.A.
|
||||
// Copyright (C) 2018-2024 Igara Studio S.A.
|
||||
// Copyright (C) 2015-2018 David Capello
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
@ -16,6 +16,7 @@
|
||||
#include "app/context.h"
|
||||
#include "app/doc.h"
|
||||
#include "app/file/file.h"
|
||||
#include "app/modules/palettes.h"
|
||||
#include "app/script/blend_mode.h"
|
||||
#include "app/script/docobj.h"
|
||||
#include "app/script/engine.h"
|
||||
@ -29,6 +30,7 @@
|
||||
#include "doc/algorithm/flip_image.h"
|
||||
#include "doc/algorithm/flip_type.h"
|
||||
#include "doc/algorithm/shrink_bounds.h"
|
||||
#include "doc/blend_image.h"
|
||||
#include "doc/cel.h"
|
||||
#include "doc/image.h"
|
||||
#include "doc/image_ref.h"
|
||||
@ -312,13 +314,18 @@ int Image_drawImage(lua_State* L)
|
||||
const Image* src = sprite->image(L);
|
||||
|
||||
if (auto cel = obj->cel(L)) {
|
||||
gfx::Rect bounds(0, 0, src->size().w, src->size().h);
|
||||
buf.reset(new doc::ImageBuffer);
|
||||
ImageRef tmp_src(
|
||||
doc::crop_image(dst,
|
||||
gfx::Rect(pos.x, pos.y, src->size().w, src->size().h),
|
||||
0, buf));
|
||||
doc::blend_image(tmp_src.get(), src, 0, 0, opacity, blendMode);
|
||||
gfx::Rect bounds(src->size());
|
||||
|
||||
// Create the ImageBuffer only when it doesn't exist so we can
|
||||
// cache the allocated buffer.
|
||||
if (!buf)
|
||||
buf = std::make_shared<doc::ImageBuffer>();
|
||||
|
||||
ImageRef tmp_src(doc::crop_image(dst, gfx::Rect(pos, src->size()), 0, buf));
|
||||
doc::blend_image(tmp_src.get(), src,
|
||||
gfx::Clip(src->size()),
|
||||
cel->sprite()->palette(0),
|
||||
opacity, blendMode);
|
||||
// TODO Use something similar to doc::algorithm::shrink_bounds2()
|
||||
// but we need something that does the render and compares
|
||||
// the minimal modified area.
|
||||
@ -332,7 +339,8 @@ int Image_drawImage(lua_State* L)
|
||||
// the source image without undo information.
|
||||
else {
|
||||
doc::blend_image(dst, src,
|
||||
pos.x, pos.y,
|
||||
gfx::Clip(pos, src->bounds()),
|
||||
get_current_palette(),
|
||||
opacity, blendMode);
|
||||
}
|
||||
return 0;
|
||||
|
@ -1,5 +1,5 @@
|
||||
# Aseprite Document Library
|
||||
# Copyright (C) 2019-2023 Igara Studio S.A.
|
||||
# Copyright (C) 2019-2024 Igara Studio S.A.
|
||||
# Copyright (C) 2001-2018 David Capello
|
||||
|
||||
if(WIN32)
|
||||
@ -22,6 +22,7 @@ add_library(doc-lib
|
||||
algorithm/stroke_selection.cpp
|
||||
anidir.cpp
|
||||
blend_funcs.cpp
|
||||
blend_image.cpp
|
||||
blend_mode.cpp
|
||||
brush.cpp
|
||||
brush_type.cpp
|
||||
|
84
src/doc/blend_image.cpp
Normal file
84
src/doc/blend_image.cpp
Normal file
@ -0,0 +1,84 @@
|
||||
// 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/blend_image.h"
|
||||
|
||||
#include "doc/blend_internals.h"
|
||||
#include "doc/image_impl.h"
|
||||
|
||||
namespace doc {
|
||||
|
||||
template<typename DstTraits,
|
||||
typename SrcTraits>
|
||||
void blend_image_templ(Image* dst,
|
||||
const Image* src,
|
||||
const gfx::Clip& area,
|
||||
const Palette* pal,
|
||||
const int opacity,
|
||||
const BlendMode blendMode)
|
||||
{
|
||||
if constexpr (DstTraits::color_mode == ColorMode::INDEXED ||
|
||||
SrcTraits::color_mode == ColorMode::INDEXED) {
|
||||
ASSERT(pal != nullptr);
|
||||
if (pal == nullptr)
|
||||
return;
|
||||
}
|
||||
BlenderHelper<DstTraits, SrcTraits> blender(dst, src, pal, blendMode, true);
|
||||
LockImageBits<DstTraits> dstBits(dst);
|
||||
const LockImageBits<SrcTraits> srcBits(src);
|
||||
auto dstIt = dstBits.begin_area(area.dstBounds());
|
||||
auto srcIt = srcBits.begin_area(area.srcBounds());
|
||||
auto dstEnd = dstBits.end_area(area.dstBounds());
|
||||
for (; dstIt < dstEnd; ++dstIt, ++srcIt)
|
||||
*dstIt = blender(*dstIt, *srcIt, opacity);
|
||||
}
|
||||
|
||||
void blend_image(Image* dst,
|
||||
const Image* src,
|
||||
gfx::Clip area,
|
||||
const Palette* pal,
|
||||
const int opacity,
|
||||
const BlendMode blendMode)
|
||||
{
|
||||
if (!area.clip(dst->width(), dst->height(), src->width(), src->height()))
|
||||
return;
|
||||
|
||||
switch (dst->pixelFormat()) {
|
||||
|
||||
case IMAGE_RGB:
|
||||
switch (src->pixelFormat()) {
|
||||
case IMAGE_RGB: return blend_image_templ<RgbTraits, RgbTraits >(dst, src, area, pal, opacity, blendMode);
|
||||
case IMAGE_GRAYSCALE: return blend_image_templ<RgbTraits, GrayscaleTraits>(dst, src, area, pal, opacity, blendMode);
|
||||
case IMAGE_INDEXED: return blend_image_templ<RgbTraits, IndexedTraits >(dst, src, area, pal, opacity, blendMode);
|
||||
}
|
||||
break;
|
||||
|
||||
case IMAGE_GRAYSCALE:
|
||||
switch (src->pixelFormat()) {
|
||||
case IMAGE_RGB: return blend_image_templ<GrayscaleTraits, RgbTraits >(dst, src, area, pal, opacity, blendMode);
|
||||
case IMAGE_GRAYSCALE: return blend_image_templ<GrayscaleTraits, GrayscaleTraits>(dst, src, area, pal, opacity, blendMode);
|
||||
case IMAGE_INDEXED: return blend_image_templ<GrayscaleTraits, IndexedTraits >(dst, src, area, pal, opacity, blendMode);
|
||||
}
|
||||
break;
|
||||
|
||||
case IMAGE_INDEXED:
|
||||
switch (src->pixelFormat()) {
|
||||
case IMAGE_RGB: return blend_image_templ<IndexedTraits, RgbTraits >(dst, src, area, pal, opacity, blendMode);
|
||||
case IMAGE_GRAYSCALE: return blend_image_templ<IndexedTraits, GrayscaleTraits>(dst, src, area, pal, opacity, blendMode);
|
||||
case IMAGE_INDEXED: return blend_image_templ<IndexedTraits, IndexedTraits >(dst, src, area, pal, opacity, blendMode);
|
||||
}
|
||||
break;
|
||||
|
||||
case IMAGE_TILEMAP:
|
||||
return dst->copy(src, area);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace doc
|
30
src/doc/blend_image.h
Normal file
30
src/doc/blend_image.h
Normal file
@ -0,0 +1,30 @@
|
||||
// Aseprite Document Library
|
||||
// Copyright (C) 2019-2024 Igara Studio S.A.
|
||||
// Copyright (C) 2001-2018 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
|
||||
#ifndef DOC_BLEND_IMAGE_H_INCLUDED
|
||||
#define DOC_BLEND_IMAGE_H_INCLUDED
|
||||
#pragma once
|
||||
|
||||
#include "doc/blend_mode.h"
|
||||
#include "gfx/fwd.h"
|
||||
|
||||
namespace doc {
|
||||
class Image;
|
||||
class Palette;
|
||||
|
||||
void blend_image(Image* dst,
|
||||
const Image* src,
|
||||
gfx::Clip area,
|
||||
// For indexed color mode
|
||||
const Palette* pal,
|
||||
// For grayscale/RGB color modes
|
||||
const int opacity,
|
||||
const doc::BlendMode blendMode);
|
||||
|
||||
} // namespace doc
|
||||
|
||||
#endif
|
@ -1,4 +1,5 @@
|
||||
// Aseprite Document Library
|
||||
// Copyright (c) 2024 Igara Studio S.A.
|
||||
// Copyright (c) 2001-2015 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
@ -14,4 +15,201 @@
|
||||
#error Invalid Pixman library
|
||||
#endif
|
||||
|
||||
#include "doc/blend_funcs.h"
|
||||
#include "doc/blend_mode.h"
|
||||
#include "doc/color.h"
|
||||
#include "doc/image.h"
|
||||
#include "doc/image_traits.h"
|
||||
#include "doc/palette.h"
|
||||
|
||||
namespace doc {
|
||||
|
||||
template<class DstTraits, class SrcTraits>
|
||||
class BlenderHelper {
|
||||
BlendFunc m_blendFunc;
|
||||
color_t m_maskColor;
|
||||
public:
|
||||
BlenderHelper(Image* dst, const Image* src, const Palette* pal,
|
||||
const BlendMode blendMode, const bool newBlend)
|
||||
{
|
||||
m_blendFunc = SrcTraits::get_blender(blendMode, newBlend);
|
||||
m_maskColor = src->maskColor();
|
||||
}
|
||||
|
||||
inline typename DstTraits::pixel_t
|
||||
operator()(typename DstTraits::pixel_t dst,
|
||||
typename SrcTraits::pixel_t src,
|
||||
int opacity)
|
||||
{
|
||||
if (src != m_maskColor)
|
||||
return (*m_blendFunc)(dst, src, opacity);
|
||||
else
|
||||
return dst;
|
||||
}
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// X -> Rgb
|
||||
|
||||
template<>
|
||||
class BlenderHelper<RgbTraits, GrayscaleTraits> {
|
||||
BlendFunc m_blendFunc;
|
||||
color_t m_maskColor;
|
||||
public:
|
||||
BlenderHelper(Image* dst, const Image* src, const Palette* pal,
|
||||
const BlendMode blendMode, const bool newBlend)
|
||||
{
|
||||
m_blendFunc = RgbTraits::get_blender(blendMode, newBlend);
|
||||
m_maskColor = src->maskColor();
|
||||
}
|
||||
|
||||
inline RgbTraits::pixel_t
|
||||
operator()(RgbTraits::pixel_t dst,
|
||||
GrayscaleTraits::pixel_t src,
|
||||
int opacity)
|
||||
{
|
||||
if (src != m_maskColor) {
|
||||
int v = graya_getv(src);
|
||||
return (*m_blendFunc)(dst, rgba(v, v, v, graya_geta(src)), opacity);
|
||||
}
|
||||
else
|
||||
return dst;
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
class BlenderHelper<RgbTraits, IndexedTraits> {
|
||||
const Palette* m_pal;
|
||||
BlendMode m_blendMode;
|
||||
BlendFunc m_blendFunc;
|
||||
color_t m_maskColor;
|
||||
public:
|
||||
BlenderHelper(Image* dst, const Image* src, const Palette* pal,
|
||||
const BlendMode blendMode, const bool newBlend)
|
||||
{
|
||||
m_blendMode = blendMode;
|
||||
m_blendFunc = RgbTraits::get_blender(blendMode, newBlend);
|
||||
m_maskColor = src->maskColor();
|
||||
m_pal = pal;
|
||||
}
|
||||
|
||||
inline RgbTraits::pixel_t
|
||||
operator()(RgbTraits::pixel_t dst,
|
||||
IndexedTraits::pixel_t src,
|
||||
int opacity)
|
||||
{
|
||||
if (m_blendMode == BlendMode::SRC) {
|
||||
return m_pal->getEntry(src);
|
||||
}
|
||||
else {
|
||||
if (src != m_maskColor) {
|
||||
return (*m_blendFunc)(dst, m_pal->getEntry(src), opacity);
|
||||
}
|
||||
else
|
||||
return dst;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// X -> Grayscale
|
||||
|
||||
template<>
|
||||
class BlenderHelper<GrayscaleTraits, RgbTraits> {
|
||||
BlendFunc m_blendFunc;
|
||||
public:
|
||||
BlenderHelper(Image* dst, const Image* src, const Palette* pal,
|
||||
const BlendMode blendMode, const bool newBlend)
|
||||
{
|
||||
m_blendFunc = RgbTraits::get_blender(blendMode, newBlend);
|
||||
}
|
||||
|
||||
inline GrayscaleTraits::pixel_t
|
||||
operator()(GrayscaleTraits::pixel_t dst,
|
||||
RgbTraits::pixel_t src,
|
||||
int opacity)
|
||||
{
|
||||
// TODO we should be able to configure this function
|
||||
return rgba_to_graya_using_luma(src);
|
||||
}
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// X -> Indexed
|
||||
|
||||
template<>
|
||||
class BlenderHelper<IndexedTraits, RgbTraits> {
|
||||
const Palette* m_pal;
|
||||
BlendMode m_blendMode;
|
||||
BlendFunc m_blendFunc;
|
||||
color_t m_maskColor;
|
||||
public:
|
||||
BlenderHelper(Image* dst, const Image* src, const Palette* pal,
|
||||
const BlendMode blendMode, const bool newBlend)
|
||||
{
|
||||
m_blendMode = blendMode;
|
||||
m_blendFunc = RgbTraits::get_blender(blendMode, newBlend);
|
||||
m_pal = pal;
|
||||
|
||||
if (m_blendMode == BlendMode::SRC)
|
||||
m_maskColor = -1;
|
||||
else
|
||||
m_maskColor = dst->maskColor();
|
||||
}
|
||||
|
||||
inline IndexedTraits::pixel_t
|
||||
operator()(IndexedTraits::pixel_t dst,
|
||||
RgbTraits::pixel_t src,
|
||||
int opacity)
|
||||
{
|
||||
if (dst != m_maskColor) {
|
||||
src = (*m_blendFunc)(m_pal->getEntry(dst), src, opacity);
|
||||
}
|
||||
return m_pal->findBestfit(rgba_getr(src),
|
||||
rgba_getg(src),
|
||||
rgba_getb(src),
|
||||
rgba_geta(src),
|
||||
m_maskColor);
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
class BlenderHelper<IndexedTraits, IndexedTraits> {
|
||||
BlendMode m_blendMode;
|
||||
color_t m_maskColor;
|
||||
int m_paletteSize;
|
||||
public:
|
||||
BlenderHelper(Image* dst, const Image* src, const Palette* pal,
|
||||
const BlendMode blendMode, const bool newBlend)
|
||||
{
|
||||
m_blendMode = blendMode;
|
||||
m_maskColor = src->maskColor();
|
||||
m_paletteSize = pal->size();
|
||||
}
|
||||
|
||||
inline IndexedTraits::pixel_t
|
||||
operator()(IndexedTraits::pixel_t dst,
|
||||
IndexedTraits::pixel_t src,
|
||||
int opacity)
|
||||
{
|
||||
if (m_blendMode == BlendMode::SRC) {
|
||||
return src;
|
||||
}
|
||||
else if (m_blendMode == BlendMode::DST_OVER) {
|
||||
if (dst != m_maskColor)
|
||||
return dst;
|
||||
else
|
||||
return src;
|
||||
}
|
||||
else {
|
||||
if (src != m_maskColor && src < m_paletteSize)
|
||||
return src;
|
||||
else
|
||||
return dst;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace doc
|
||||
|
||||
#endif
|
||||
|
@ -72,46 +72,6 @@ void copy_image(Image* dst, const Image* src, int x, int y)
|
||||
dst->copy(src, gfx::Clip(x, y, 0, 0, src->width(), src->height()));
|
||||
}
|
||||
|
||||
template<typename ImageTraits>
|
||||
void blend_image_templ(Image* dst,
|
||||
const Image* src,
|
||||
const int x, const int y,
|
||||
const int opacity,
|
||||
BlendFunc& blender)
|
||||
{
|
||||
gfx::Clip area = gfx::Clip(x, y, 0, 0, src->width(), src->height());
|
||||
if (!area.clip(dst->width(), dst->height(), src->width(), src->height()))
|
||||
return;
|
||||
LockImageBits<ImageTraits> dstBits(dst);
|
||||
const LockImageBits<ImageTraits> srcBits(src);
|
||||
auto dstIt = dstBits.begin_area(area.dstBounds());
|
||||
auto srcIt = srcBits.begin_area(area.srcBounds());
|
||||
auto dstEnd = dstBits.end_area(area.dstBounds());
|
||||
for (; dstIt < dstEnd; ++dstIt, ++srcIt)
|
||||
*dstIt = blender(*dstIt, *srcIt, opacity);
|
||||
}
|
||||
|
||||
void blend_image(Image* dst, const Image* src, const int x, const int y,
|
||||
const int opacity,
|
||||
const doc::BlendMode blendMode)
|
||||
{
|
||||
ASSERT(dst->pixelFormat() == src->pixelFormat());
|
||||
BlendFunc blender;
|
||||
switch (src->pixelFormat()) {
|
||||
case IMAGE_RGB:
|
||||
blender = get_rgba_blender(blendMode, true);
|
||||
return blend_image_templ<RgbTraits>(dst, src, x, y, opacity, blender);
|
||||
case IMAGE_GRAYSCALE:
|
||||
blender = get_graya_blender(blendMode, true);
|
||||
return blend_image_templ<GrayscaleTraits>(dst, src, x, y, opacity, blender);
|
||||
case IMAGE_INDEXED:
|
||||
blender = get_indexed_blender(blendMode, true);
|
||||
return blend_image_templ<IndexedTraits>(dst, src, x, y, opacity, blender);
|
||||
case IMAGE_TILEMAP:
|
||||
return copy_image(dst, src, x, y);
|
||||
}
|
||||
}
|
||||
|
||||
void copy_image(Image* dst, const Image* src, const gfx::Region& rgn)
|
||||
{
|
||||
for (const gfx::Rect& rc : rgn)
|
||||
|
@ -10,7 +10,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "base/ints.h"
|
||||
#include "doc/blend_mode.h"
|
||||
#include "doc/color.h"
|
||||
#include "doc/image_buffer.h"
|
||||
#include "gfx/fwd.h"
|
||||
@ -18,7 +17,6 @@
|
||||
namespace doc {
|
||||
class Brush;
|
||||
class Image;
|
||||
class Palette;
|
||||
class Remap;
|
||||
|
||||
color_t get_pixel(const Image* image, int x, int y);
|
||||
@ -28,8 +26,6 @@ namespace doc {
|
||||
|
||||
void copy_image(Image* dst, const Image* src);
|
||||
void copy_image(Image* dst, const Image* src, int x, int y);
|
||||
void blend_image(Image* dst, const Image* src, int x, int y,
|
||||
const int opacity, const doc::BlendMode blendMode);
|
||||
void copy_image(Image* dst, const Image* src, const gfx::Region& rgn);
|
||||
Image* crop_image(const Image* image, int x, int y, int w, int h, color_t bg, const ImageBufferPtr& buffer = ImageBufferPtr());
|
||||
Image* crop_image(const Image* image, const gfx::Rect& bounds, color_t bg, const ImageBufferPtr& buffer = ImageBufferPtr());
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Aseprite Render Library
|
||||
// Copyright (C) 2019-2023 Igara Studio S.A.
|
||||
// Copyright (C) 2019-2024 Igara Studio S.A.
|
||||
// Copyright (C) 2001-2018 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
@ -34,119 +34,6 @@ namespace {
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// Scaled composite
|
||||
|
||||
template<class DstTraits, class SrcTraits>
|
||||
class BlenderHelper {
|
||||
BlendFunc m_blendFunc;
|
||||
color_t m_mask_color;
|
||||
public:
|
||||
BlenderHelper(const Image* src, const Palette* pal, BlendMode blendMode, const bool newBlend)
|
||||
{
|
||||
m_blendFunc = SrcTraits::get_blender(blendMode, newBlend);
|
||||
m_mask_color = src->maskColor();
|
||||
}
|
||||
inline typename DstTraits::pixel_t
|
||||
operator()(const typename DstTraits::pixel_t& dst,
|
||||
const typename SrcTraits::pixel_t& src,
|
||||
const int opacity)
|
||||
{
|
||||
if (src != m_mask_color)
|
||||
return (*m_blendFunc)(dst, src, opacity);
|
||||
else
|
||||
return dst;
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
class BlenderHelper<RgbTraits, GrayscaleTraits> {
|
||||
BlendFunc m_blendFunc;
|
||||
color_t m_mask_color;
|
||||
public:
|
||||
BlenderHelper(const Image* src, const Palette* pal, BlendMode blendMode, const bool newBlend)
|
||||
{
|
||||
m_blendFunc = RgbTraits::get_blender(blendMode, newBlend);
|
||||
m_mask_color = src->maskColor();
|
||||
}
|
||||
inline RgbTraits::pixel_t
|
||||
operator()(const RgbTraits::pixel_t& dst,
|
||||
const GrayscaleTraits::pixel_t& src,
|
||||
const int opacity)
|
||||
{
|
||||
if (src != m_mask_color) {
|
||||
int v = graya_getv(src);
|
||||
return (*m_blendFunc)(dst, rgba(v, v, v, graya_geta(src)), opacity);
|
||||
}
|
||||
else
|
||||
return dst;
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
class BlenderHelper<RgbTraits, IndexedTraits> {
|
||||
const Palette* m_pal;
|
||||
BlendMode m_blendMode;
|
||||
BlendFunc m_blendFunc;
|
||||
color_t m_mask_color;
|
||||
public:
|
||||
BlenderHelper(const Image* src, const Palette* pal, BlendMode blendMode, const bool newBlend)
|
||||
{
|
||||
m_blendMode = blendMode;
|
||||
m_blendFunc = RgbTraits::get_blender(blendMode, newBlend);
|
||||
m_mask_color = src->maskColor();
|
||||
m_pal = pal;
|
||||
}
|
||||
inline RgbTraits::pixel_t
|
||||
operator()(const RgbTraits::pixel_t& dst,
|
||||
const IndexedTraits::pixel_t& src,
|
||||
const int opacity)
|
||||
{
|
||||
if (m_blendMode == BlendMode::SRC) {
|
||||
return m_pal->getEntry(src);
|
||||
}
|
||||
else {
|
||||
if (src != m_mask_color) {
|
||||
return (*m_blendFunc)(dst, m_pal->getEntry(src), opacity);
|
||||
}
|
||||
else
|
||||
return dst;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
class BlenderHelper<IndexedTraits, IndexedTraits> {
|
||||
BlendMode m_blendMode;
|
||||
color_t m_maskColor;
|
||||
int m_paletteSize;
|
||||
public:
|
||||
BlenderHelper(const Image* src, const Palette* pal, BlendMode blendMode, const bool newBlend)
|
||||
{
|
||||
m_blendMode = blendMode;
|
||||
m_maskColor = src->maskColor();
|
||||
m_paletteSize = pal->size();
|
||||
}
|
||||
inline IndexedTraits::pixel_t
|
||||
operator()(const IndexedTraits::pixel_t& dst,
|
||||
const IndexedTraits::pixel_t& src,
|
||||
const int opacity)
|
||||
{
|
||||
if (m_blendMode == BlendMode::SRC) {
|
||||
return src;
|
||||
}
|
||||
else if (m_blendMode == BlendMode::DST_OVER) {
|
||||
if (dst != m_maskColor)
|
||||
return dst;
|
||||
else
|
||||
return src;
|
||||
}
|
||||
else {
|
||||
if (src != m_maskColor && src < m_paletteSize)
|
||||
return src;
|
||||
else
|
||||
return dst;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template<class DstTraits, class SrcTraits>
|
||||
void composite_image_without_scale(
|
||||
Image* dst, const Image* src, const Palette* pal,
|
||||
@ -163,7 +50,7 @@ void composite_image_without_scale(
|
||||
ASSERT(DstTraits::pixel_format == dst->pixelFormat());
|
||||
ASSERT(SrcTraits::pixel_format == src->pixelFormat());
|
||||
|
||||
BlenderHelper<DstTraits, SrcTraits> blender(src, pal, blendMode, newBlend);
|
||||
BlenderHelper<DstTraits, SrcTraits> blender(dst, src, pal, blendMode, newBlend);
|
||||
|
||||
gfx::Clip area(areaF);
|
||||
if (!area.clip(dst->width(), dst->height(),
|
||||
@ -226,7 +113,7 @@ void composite_image_scale_up(
|
||||
int(sy*double(src->height()))))
|
||||
return;
|
||||
|
||||
BlenderHelper<DstTraits, SrcTraits> blender(src, pal, blendMode, newBlend);
|
||||
BlenderHelper<DstTraits, SrcTraits> blender(dst, src, pal, blendMode, newBlend);
|
||||
int px_x, px_y;
|
||||
int px_w = int(sx);
|
||||
int px_h = int(sy);
|
||||
@ -379,7 +266,7 @@ void composite_image_scale_down(
|
||||
int(sy*double(src->height()))))
|
||||
return;
|
||||
|
||||
BlenderHelper<DstTraits, SrcTraits> blender(src, pal, blendMode, newBlend);
|
||||
BlenderHelper<DstTraits, SrcTraits> blender(dst, src, pal, blendMode, newBlend);
|
||||
int step_w = int(1.0 / sx);
|
||||
int step_h = int(1.0 / sy);
|
||||
if (step_w < 1 || step_h < 1)
|
||||
@ -447,7 +334,7 @@ void composite_image_general(
|
||||
sx*src->width(), sy*src->height()))
|
||||
return;
|
||||
|
||||
BlenderHelper<DstTraits, SrcTraits> blender(src, pal, blendMode, newBlend);
|
||||
BlenderHelper<DstTraits, SrcTraits> blender(dst, src, pal, blendMode, newBlend);
|
||||
|
||||
gfx::Rect dstBounds(
|
||||
area.dstBounds().x, area.dstBounds().y,
|
||||
@ -522,7 +409,7 @@ void composite_image_general_with_tile_flags(
|
||||
sx*src->width(), sy*src->height()))
|
||||
return;
|
||||
|
||||
BlenderHelper<DstTraits, SrcTraits> blender(src, pal, blendMode, newBlend);
|
||||
BlenderHelper<DstTraits, SrcTraits> blender(dst, src, pal, blendMode, newBlend);
|
||||
|
||||
gfx::Rect dstBounds(
|
||||
area.dstBounds().x, area.dstBounds().y,
|
||||
|
@ -1,4 +1,4 @@
|
||||
-- Copyright (C) 2019-2023 Igara Studio S.A.
|
||||
-- Copyright (C) 2019-2024 Igara Studio S.A.
|
||||
-- Copyright (C) 2018 David Capello
|
||||
--
|
||||
-- This file is released under the terms of the MIT license.
|
||||
@ -330,13 +330,14 @@ do
|
||||
1, 2, 4, 3, 4,
|
||||
3, 4, 0, 0, 0 })
|
||||
|
||||
-- BlendMode.NORMAL by default, so mask color (color=0) is skipped
|
||||
b:drawImage(a, Point(0, 3))
|
||||
expect_img(b, { 0, 0, 0, 0, 0,
|
||||
0, 1, 2, 1, 2,
|
||||
1, 2, 4, 3, 4,
|
||||
0, 1, 2, 0, 0 })
|
||||
3, 1, 2, 0, 0 })
|
||||
|
||||
b:drawImage(a, Point(0, 3)) -- Do nothing
|
||||
b:drawImage(a, Point(0, 3), 255, BlendMode.SRC)
|
||||
expect_img(b, { 0, 0, 0, 0, 0,
|
||||
0, 1, 2, 1, 2,
|
||||
1, 2, 4, 3, 4,
|
||||
@ -476,3 +477,60 @@ local spr = Sprite(3, 3) -- Test with sprite (with transactions & undo/redo)
|
||||
test_image_flip(app.image)
|
||||
app.sprite = nil -- Test without sprite (without transactions)
|
||||
test_image_flip(Image(3, 3))
|
||||
|
||||
----------------------------------------------------------------------
|
||||
-- Test crash using Image:drawImage() with different color modes
|
||||
|
||||
do
|
||||
local tmp = Sprite(3, 3)
|
||||
local pal = Palette(4)
|
||||
pal:setColor(0, Color(0, 0, 0))
|
||||
pal:setColor(1, Color(255, 0, 0))
|
||||
pal:setColor(2, Color(0, 255, 0))
|
||||
pal:setColor(3, Color(0, 0, 255))
|
||||
tmp:setPalette(pal)
|
||||
|
||||
local rgb = Image{ width=2, height=2, colorMode=ColorMode.RGB }
|
||||
local idx = Image{ width=2, height=2, colorMode=ColorMode.INDEXED }
|
||||
|
||||
-- Draw INDEXED -> RGB
|
||||
|
||||
array_to_pixels({ 0, 1,
|
||||
2, 3 }, idx)
|
||||
rgb:drawImage(idx)
|
||||
|
||||
local k = pal:getColor(0).rgbaPixel
|
||||
local r = pal:getColor(1).rgbaPixel
|
||||
local g = pal:getColor(2).rgbaPixel
|
||||
local b = pal:getColor(3).rgbaPixel
|
||||
expect_img(rgb, { 0, r,
|
||||
g, b })
|
||||
|
||||
rgb:drawImage(idx, 0, 0, 255, BlendMode.SRC)
|
||||
expect_img(rgb, { k, r,
|
||||
g, b })
|
||||
|
||||
rgb:drawImage(idx, 1, 0)
|
||||
expect_img(rgb, { k, r,
|
||||
g, g })
|
||||
|
||||
rgb:drawImage(idx, 1, 0, 255, BlendMode.SRC)
|
||||
expect_img(rgb, { k, k,
|
||||
g, g })
|
||||
|
||||
-- Draw RGB -> INDEXED
|
||||
|
||||
array_to_pixels({ 0, r,
|
||||
g, b }, rgb)
|
||||
|
||||
idx:clear(1)
|
||||
idx:drawImage(rgb, 0, 0, 255, BlendMode.SRC)
|
||||
expect_img(idx, { 0, 1,
|
||||
2, 3 })
|
||||
|
||||
idx:clear(1)
|
||||
idx:drawImage(rgb)
|
||||
expect_img(idx, { 1, 1,
|
||||
2, 3 })
|
||||
|
||||
end
|
||||
|
@ -20,7 +20,22 @@ local function dump_img(image)
|
||||
for v=0,h-1 do
|
||||
local lineStr = ' '
|
||||
for u=0,w-1 do
|
||||
lineStr = lineStr .. image:getPixel(u, v) .. ','
|
||||
local pix = image:getPixel(u, v)
|
||||
local pixStr
|
||||
if image.colorMode == ColorMode.RGB then
|
||||
pixStr = string.format('rgba(%d,%d,%d,%d)',
|
||||
app.pixelColor.rgbaR(pix),
|
||||
app.pixelColor.rgbaG(pix),
|
||||
app.pixelColor.rgbaB(pix),
|
||||
app.pixelColor.rgbaA(pix))
|
||||
elseif image.colorMode == ColorMode.GRAY then
|
||||
pixStr = string.format('gray(%d,%d)',
|
||||
app.pixelColor.grayaV(pix),
|
||||
app.pixelColor.grayaA(pix))
|
||||
else
|
||||
pixStr = tostring(pix)
|
||||
end
|
||||
lineStr = lineStr .. pixStr .. ','
|
||||
end
|
||||
print(lineStr)
|
||||
end
|
||||
|
Loading…
x
Reference in New Issue
Block a user