Fix non-normal blend modes when the backdrop is transparent (fix #1096)

This commit is contained in:
Gaspar Capello 2019-02-25 18:02:58 -03:00 committed by David Capello
parent cc62e9ed59
commit f53544842c
41 changed files with 382 additions and 155 deletions

View File

@ -166,6 +166,7 @@
</section>
<section id="experimental" text="Experimental">
<option id="new_render_engine" type="bool" default="true" />
<option id="new_blend" type="bool" default="true" />
<option id="use_native_clipboard" type="bool" default="true" />
<option id="use_native_file_dialog" type="bool" default="false" />
<option id="one_finger_as_mouse_movement" type="bool" default="true" />

View File

@ -1082,6 +1082,7 @@ disable_extension = &Disable
uninstall_extension = &Uninstall
open_extension_folder = Open &Folder
user_interface = User Interface
new_blend = New layer blending method
new_render_engine = New render engine for sprite editor
native_clipboard = Use native clipboard
native_file_dialog = Use native file dialog

View File

@ -1,5 +1,5 @@
<!-- Aseprite -->
<!-- Copyright (C) 2018 Igara Studio S.A. -->
<!-- Copyright (C) 2018-2019 Igara Studio S.A. -->
<!-- Copyright (C) 2001-2018 David Capello -->
<gui>
<window id="options" text="@.title">
@ -362,8 +362,8 @@
<check id="gif_options_alert" text="@.gif_options_alert"
pref="gif.show_alert" />
<check id="jpeg_options_alert" text="@.jpeg_options_alert"
pref="jpeg.show_alert" />
<check id="svg_options_alert" text="@.svg_options_alert"
pref="jpeg.show_alert" />
<check id="svg_options_alert" text="@.svg_options_alert"
pref="svg.show_alert" />
<check id="advanced_mode_alert" text="@.advanced_mode_alert"
pref="advanced_mode.show_alert" />
@ -414,6 +414,11 @@
pref="experimental.new_render_engine" />
<link text="(#1671)" url="https://github.com/aseprite/aseprite/issues/1671" />
</hbox>
<hbox>
<check text="@.new_blend"
pref="experimental.new_blend" />
<link text="(#1096)" url="https://github.com/aseprite/aseprite/issues/1096" />
</hbox>
<check id="native_clipboard" text="@.native_clipboard" />
<check id="native_file_dialog" text="@.native_file_dialog" />
<check id="one_finger_as_mouse_movement"

View File

@ -1,4 +1,5 @@
// Aseprite
// Copyright (C) 2019 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello
//
// This program is distributed under the terms of
@ -31,9 +32,11 @@ namespace app {
namespace cmd {
FlattenLayers::FlattenLayers(doc::Sprite* sprite,
const doc::SelectedLayers& layers0)
const doc::SelectedLayers& layers0,
const bool newBlend)
: WithSprite(sprite)
{
m_newBlendMethod = newBlend;
doc::SelectedLayers layers(layers0);
layers.removeChildrenIfParentIsSelected();
@ -87,6 +90,7 @@ void FlattenLayers::onExecute()
}
render::Render render;
render.setNewBlend(m_newBlendMethod);
render.setBgType(render::BgType::NONE);
{

View File

@ -1,4 +1,5 @@
// Aseprite
// Copyright (C) 2019 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello
//
// This program is distributed under the terms of
@ -20,13 +21,15 @@ namespace cmd {
, public WithSprite {
public:
FlattenLayers(doc::Sprite* sprite,
const doc::SelectedLayers& layers);
const doc::SelectedLayers& layers,
const bool newBlendMethod);
protected:
void onExecute() override;
private:
ObjectIds m_layerIds;
bool m_newBlendMethod;
};
} // namespace cmd

View File

@ -1,4 +1,5 @@
// Aseprite
// Copyright (C) 2019 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello
//
// This program is distributed under the terms of
@ -88,7 +89,8 @@ void ColorPicker::pickColor(const Site& site,
m_color = app::Color::fromImage(
sprite->pixelFormat(),
render::get_sprite_pixel(sprite, pos.x, pos.y,
site.frame(), proj));
site.frame(), proj,
Preferences::instance().experimental.newBlend()));
doc::CelList cels;
sprite->pickCels(pos.x, pos.y, site.frame(), 128,

View File

@ -1,4 +1,5 @@
// Aseprite
// Copyright (C) 2019 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello
//
// This program is distributed under the terms of
@ -78,7 +79,8 @@ public:
const doc::PixelFormat pixelFormat,
const render::DitheringAlgorithm ditheringAlgorithm,
const render::DitheringMatrix& ditheringMatrix,
const gfx::Point& pos)
const gfx::Point& pos,
const bool newBlend)
: m_image(dstImage)
, m_pos(pos)
, m_running(true)
@ -89,11 +91,13 @@ public:
sprite, frame,
pixelFormat,
ditheringAlgorithm,
ditheringMatrix]() { // Copy the matrix
ditheringMatrix,
newBlend]() { // Copy the matrix
run(sprite, frame,
pixelFormat,
ditheringAlgorithm,
ditheringMatrix);
ditheringMatrix,
newBlend);
})
{
}
@ -116,13 +120,15 @@ private:
const doc::frame_t frame,
const doc::PixelFormat pixelFormat,
const render::DitheringAlgorithm ditheringAlgorithm,
const render::DitheringMatrix& ditheringMatrix) {
const render::DitheringMatrix& ditheringMatrix,
const bool newBlend) {
doc::ImageRef tmp(
Image::create(sprite->pixelFormat(),
m_image->width(),
m_image->height()));
render::Render render;
render.setNewBlend(newBlend);
render.renderSprite(
tmp.get(), sprite, frame,
gfx::Clip(0, 0,
@ -291,7 +297,8 @@ private:
dstPixelFormat,
ditheringAlgorithm(),
ditheringMatrix(),
visibleBounds.origin()));
visibleBounds.origin(),
Preferences::instance().experimental.newBlend()));
m_timer.start();
}
@ -467,15 +474,16 @@ void ChangePixelFormatCommand::onExecute(Context* context)
{
const ContextReader reader(context);
SpriteJob job(reader, "Color Mode Change");
const bool newBlend = Preferences::instance().experimental.newBlend();
job.startJobWithCallback(
[this, &job, flatten] {
[this, &job, flatten, newBlend] {
Sprite* sprite(job.sprite());
if (flatten) {
SelectedLayers selLayers;
for (auto layer : sprite->root()->layers())
selLayers.insert(layer);
job.tx()(new cmd::FlattenLayers(sprite, selLayers));
job.tx()(new cmd::FlattenLayers(sprite, selLayers, newBlend));
}
job.tx()(

View File

@ -1,4 +1,5 @@
// Aseprite
// Copyright (C) 2019 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello
//
// This program is distributed under the terms of
@ -111,12 +112,14 @@ void ColorQuantizationCommand::onExecute(Context* context)
ContextReader reader(context);
SpriteJob job(reader, "Color Quantization");
const bool newBlend = Preferences::instance().experimental.newBlend();
job.startJobWithCallback(
[sprite, withAlpha, &tmpPalette, &job]{
[sprite, withAlpha, &tmpPalette, &job, newBlend]{
render::create_palette_from_sprite(
sprite, 0, sprite->lastFrame(),
withAlpha, &tmpPalette,
&job); // SpriteJob is a render::TaskDelegate
&job,
newBlend); // SpriteJob is a render::TaskDelegate
});
job.waitJob();
if (job.isCanceled())

View File

@ -1,4 +1,5 @@
// Aseprite
// Copyright (C) 2019 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello
//
// This program is distributed under the terms of
@ -82,8 +83,10 @@ void FlattenLayersCommand::onExecute(Context* context)
range.selectLayer(layer);
}
}
tx(new cmd::FlattenLayers(sprite, range.selectedLayers()));
const bool newBlend = Preferences::instance().experimental.newBlend();
tx(new cmd::FlattenLayers(sprite,
range.selectedLayers(),
newBlend));
tx.commit();
}

View File

@ -367,6 +367,7 @@ void ImportSpriteSheetCommand::onExecute(Context* context)
Sprite* sprite = document->sprite();
frame_t currentFrame = context->activeSite().frame();
render::Render render;
render.setNewBlend(Preferences::instance().experimental.newBlend());
// Each sprite in the sheet
std::vector<gfx::Rect> tileRects;

View File

@ -1,4 +1,5 @@
// Aseprite
// Copyright (C) 2019 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello
//
// This program is distributed under the terms of
@ -137,7 +138,8 @@ void NewBrushCommand::onQuickboxCancel(Editor* editor)
void NewBrushCommand::createBrush(const Site& site, const Mask* mask)
{
doc::ImageRef image(new_image_from_mask(site, mask));
doc::ImageRef image(new_image_from_mask(site, mask,
Preferences::instance().experimental.newBlend()));
if (!image)
return;

View File

@ -1,4 +1,5 @@
// Aseprite
// Copyright (C) 2019 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello
//
// This program is distributed under the terms of
@ -254,6 +255,7 @@ void NewLayerCommand::onExecute(Context* context)
if (pasteDoc && layer->isImage()) {
Sprite* pasteSpr = pasteDoc->sprite();
render::Render render;
render.setNewBlend(true);
render.setBgType(render::BgType::NONE);
// Add more frames at the end

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2018 Igara Studio S.A.
// Copyright (C) 2018-2019 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello
//
// This program is distributed under the terms of
@ -55,7 +55,7 @@ void NewSpriteFromSelectionCommand::onExecute(Context* context)
const Sprite* sprite = site.sprite();
const Mask* mask = doc->mask();
ImageRef image(
new_image_from_mask(site, mask));
new_image_from_mask(site, mask, true));
if (!image)
return;

View File

@ -451,7 +451,8 @@ Doc* Doc::duplicate(DuplicateType type) const
(spriteCopy,
sourceSprite->root(),
gfx::Rect(0, 0, sourceSprite->width(), sourceSprite->height()),
frame_t(0), sourceSprite->lastFrame());
frame_t(0), sourceSprite->lastFrame(),
Preferences::instance().experimental.newBlend());
// Add and select the new flat layer
spriteCopy->root()->addLayer(flatLayer);

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2018 Igara Studio S.A.
// Copyright (C) 2018-2019 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello
//
// This program is distributed under the terms of
@ -1018,6 +1018,8 @@ void DocExporter::renderSample(const Sample& sample, doc::Image* dst, int x, int
*sample.selectedLayers());
render::Render render;
render.setNewBlend(Preferences::instance().experimental.newBlend());
if (extrude) {
const gfx::Rect& trim = sample.trimmedBounds();

View File

@ -730,6 +730,8 @@ void FileOp::operate(IFileOpProgress* progress)
// For each frame in the sprite.
render::Render render;
render.setNewBlend(Preferences::instance().experimental.newBlend());
frame_t outputFrame = 0;
for (frame_t frame : m_roi.selectedFrames()) {
// Draw the "frame" in "m_seq.image"
@ -891,7 +893,7 @@ void FileOp::postLoad()
base::SharedPtr<Palette> palette(
render::create_palette_from_sprite(
sprite, frame_t(0), sprite->lastFrame(), true,
nullptr, nullptr));
nullptr, nullptr, Preferences::instance().experimental.newBlend()));
sprite->resetPalettes();
sprite->setPalette(palette.get(), false);

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2018 Igara Studio S.A.
// Copyright (C) 2018-2019 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello
//
// This program is distributed under the terms of
@ -14,6 +14,7 @@
#include "app/file/file_format.h"
#include "app/file/format_options.h"
#include "app/modules/palettes.h"
#include "app/pref/preferences.h"
#include "base/file_handle.h"
#include "doc/doc.h"
#include "flic/flic.h"
@ -217,6 +218,7 @@ bool FliFormat::onSave(FileOp* fop)
// Create the bitmaps
ImageRef bmp(Image::create(IMAGE_INDEXED, sprite->width(), sprite->height()));
render::Render render;
render.setNewBlend(Preferences::instance().experimental.newBlend());
// Write frame by frame
flic::Frame fliFrame;

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2018 Igara Studio S.A.
// Copyright (C) 2018-2019 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello
//
// This program is distributed under the terms of
@ -1333,6 +1333,8 @@ private:
void renderFrame(frame_t frame, Image* dst) {
render::Render render;
render.setNewBlend(Preferences::instance().experimental.newBlend());
render.setBgType(render::BgType::NONE);
clear_image(dst, m_clearColor);
render.renderSprite(dst, m_sprite, frame);

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2018 Igara Studio S.A.
// Copyright (C) 2018-2019 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello
//
// This program is distributed under the terms of
@ -15,6 +15,7 @@
#include "app/file/file.h"
#include "app/file/file_format.h"
#include "app/file/format_options.h"
#include "app/pref/preferences.h"
#include "base/cfile.h"
#include "base/file_handle.h"
#include "doc/doc.h"
@ -284,6 +285,8 @@ bool IcoFormat::onSave(FileOp* fop)
sprite->height()));
render::Render render;
render.setNewBlend(Preferences::instance().experimental.newBlend());
for (n=frame_t(0); n<num; ++n) {
render.renderSprite(image.get(), sprite, n);

View File

@ -1,4 +1,5 @@
// Aseprite
// Copyright (C) 2019 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello
//
// This program is distributed under the terms of
@ -26,10 +27,12 @@ static bool has_cels(const Layer* layer, frame_t frame);
LayerImage* create_flatten_layer_copy(Sprite* dstSprite, const Layer* srcLayer,
const gfx::Rect& bounds,
frame_t frmin, frame_t frmax)
frame_t frmin, frame_t frmax,
const bool newBlend)
{
std::unique_ptr<LayerImage> flatLayer(new LayerImage(dstSprite));
render::Render render;
render.setNewBlend(newBlend);
for (frame_t frame=frmin; frame<=frmax; ++frame) {
// Does this frame have cels to render?

View File

@ -1,4 +1,5 @@
// Aseprite
// Copyright (C) 2019 Igara Studio S.A.
// Copyright (C) 2001-2015 David Capello
//
// This program is distributed under the terms of
@ -28,7 +29,8 @@ namespace app {
// sprite.
LayerImage* create_flatten_layer_copy(Sprite* dstSprite, const Layer* srcLayer,
const gfx::Rect& bounds,
frame_t frmin, frame_t frmax);
frame_t frmin, frame_t frmax,
const bool newBlend);
} // namespace app

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2018 Igara Studio S.A.
// Copyright (C) 2018-2019 Igara Studio S.A.
// Copyright (C) 2015-2018 David Capello
//
// This program is distributed under the terms of
@ -177,6 +177,7 @@ int Image_drawSprite(lua_State* L)
// the source image without undo information.
if (obj->cel(L) == nullptr) {
render::Render render;
render.setNewBlend(true);
render.renderSprite(
dst, sprite, frame,
gfx::Clip(pos.x, pos.y,
@ -189,6 +190,7 @@ int Image_drawSprite(lua_State* L)
ImageRef tmp(Image::createCopy(dst));
render::Render render;
render.setNewBlend(true);
render.renderSprite(
tmp.get(), sprite, frame,
gfx::Clip(pos.x, pos.y,

View File

@ -589,6 +589,7 @@ void Editor::drawOneSpriteUnclippedRect(ui::Graphics* g, const gfx::Rect& sprite
rendered.reset(Image::create(IMAGE_RGB, rc2.w, rc2.h,
m_renderEngine->getRenderImageBuffer()));
m_renderEngine->setNewBlendMethod(Preferences::instance().experimental.newBlend());
m_renderEngine->setRefLayersVisiblity(true);
m_renderEngine->setSelectedLayer(m_layer);
if (m_flags & Editor::kUseNonactiveLayersOpacityWhenEnabled)

View File

@ -1,4 +1,5 @@
// Aseprite
// Copyright (C) 2019 Igara Studio S.A.
// Copyright (C) 2018 David Capello
//
// This program is distributed under the terms of
@ -21,6 +22,7 @@ static doc::ImageBufferPtr g_renderBuffer;
EditorRender::EditorRender()
: m_render(new render::Render)
{
m_render->setNewBlend(Preferences::instance().experimental.newBlend());
}
EditorRender::~EditorRender()
@ -38,6 +40,11 @@ void EditorRender::setNonactiveLayersOpacity(const int opacity)
m_render->setNonactiveLayersOpacity(opacity);
}
void EditorRender::setNewBlendMethod(const bool newBlend)
{
m_render->setNewBlend(newBlend);
}
void EditorRender::setProjection(const render::Projection& projection)
{
m_render->setProjection(projection);

View File

@ -1,4 +1,5 @@
// Aseprite
// Copyright (C) 2019 Igara Studio S.A.
// Copyright (C) 2018 David Capello
//
// This program is distributed under the terms of
@ -41,6 +42,7 @@ namespace app {
void setRefLayersVisiblity(const bool visible);
void setNonactiveLayersOpacity(const int opacity);
void setNewBlendMethod(const bool newBlend);
void setProjection(const render::Projection& projection);

View File

@ -685,11 +685,13 @@ void PixelsMovement::drawImage(doc::Image* dst, const gfx::Point& pt, bool rende
dst->setMaskColor(m_sprite->transparentColor());
dst->clear(dst->maskColor());
if (renderOriginalLayer)
render::Render().renderLayer(
if (renderOriginalLayer) {
render::Render render;
render.renderLayer(
dst, m_layer, m_site.frame(),
gfx::Clip(bounds.x-pt.x, bounds.y-pt.y, bounds),
BlendMode::SRC);
}
color_t maskColor = m_maskColor;

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2018 Igara Studio S.A.
// Copyright (C) 2018-2019 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello
//
// This program is distributed under the terms of
@ -720,7 +720,8 @@ void StandbyState::transformSelection(Editor* editor, MouseMessage* msg, HandleT
editor->brushPreview().hide();
EditorCustomizationDelegate* customization = editor->getCustomizationDelegate();
std::unique_ptr<Image> tmpImage(new_image_from_mask(editor->getSite()));
std::unique_ptr<Image> tmpImage(new_image_from_mask(editor->getSite(),
Preferences::instance().experimental.newBlend()));
PixelsMovementPtr pixelsMovement(
new PixelsMovement(UIContext::instance(),

View File

@ -372,7 +372,9 @@ public:
m_floodfillSrcImage->clear(m_sprite->transparentColor());
render::Render().renderSprite(
render::Render render;
render.setNewBlend(Preferences::instance().experimental.newBlend());
render.renderSprite(
m_floodfillSrcImage,
m_sprite,
m_frame,

View File

@ -1,4 +1,5 @@
// Aseprite
// Copyright (C) 2019 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello
//
// This program is distributed under the terms of
@ -192,7 +193,9 @@ static bool copy_from_document(const Site& site, bool merged = false)
ASSERT(document);
const Mask* mask = document->mask();
Image* image = new_image_from_mask(site, mask, merged);
Image* image = new_image_from_mask(site, mask,
Preferences::instance().experimental.newBlend(),
merged);
if (!image)
return false;

View File

@ -1,4 +1,5 @@
// Aseprite
// Copyright (C) 2019 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello
//
// This program is distributed under the terms of
@ -21,14 +22,15 @@ namespace app {
using namespace doc;
Image* new_image_from_mask(const Site& site)
Image* new_image_from_mask(const Site& site, const bool newBlend)
{
const Mask* srcMask = site.document()->mask();
return new_image_from_mask(site, srcMask);
return new_image_from_mask(site, srcMask, newBlend);
}
doc::Image* new_image_from_mask(const Site& site,
const doc::Mask* srcMask,
const bool newBlend,
bool merged)
{
const Sprite* srcSprite = site.sprite();
@ -53,6 +55,7 @@ doc::Image* new_image_from_mask(const Site& site,
int x = 0, y = 0;
if (merged) {
render::Render render;
render.setNewBlend(newBlend);
render.renderSprite(dst.get(), srcSprite, site.frame(),
gfx::Clip(0, 0, srcBounds));

View File

@ -1,4 +1,5 @@
// Aseprite
// Copyright (C) 2019 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello
//
// This program is distributed under the terms of
@ -16,9 +17,11 @@ namespace doc {
namespace app {
class Site;
doc::Image* new_image_from_mask(const Site& site);
doc::Image* new_image_from_mask(const Site& site,
const bool newBlend);
doc::Image* new_image_from_mask(const Site& site,
const doc::Mask* mask,
const bool newBlend,
bool merged = false);
} // namespace app

View File

@ -1,4 +1,5 @@
// Aseprite Document Library
// Copyright (c) 2019 Igara Studio S.A.
// Copyright (c) 2001-2017 David Capello
//
// This file is released under the terms of the MIT license.
@ -37,6 +38,39 @@ namespace {
#define blend_difference(b, s) (ABS((b) - (s)))
#define blend_exclusion(b, s, t) ((t) = MUL_UN8((b), (s), (t)), ((b) + (s) - 2*(t)))
// New Blender Method macros
#define RGBA_BLENDER_N(name) \
color_t rgba_blender_##name##_n(color_t backdrop, color_t src, int opacity) { \
if (backdrop & rgba_a_mask) { \
color_t normal = rgba_blender_normal(backdrop, src, opacity); \
color_t blend = rgba_blender_##name(backdrop, src, opacity); \
int Ba = rgba_geta(backdrop); \
color_t normalToBlendMerge = rgba_blender_merge(normal, blend, Ba); \
int t; \
int srcTotalAlpha = MUL_UN8(rgba_geta(src), opacity, t); \
int compositeAlpha = MUL_UN8(Ba, srcTotalAlpha, t); \
return rgba_blender_merge(normalToBlendMerge, blend, compositeAlpha); \
} \
else \
return rgba_blender_normal(backdrop, src, opacity); \
}
#define GRAYA_BLENDER_N(name) \
color_t graya_blender_##name##_n(color_t backdrop, color_t src, int opacity) { \
if (backdrop & graya_a_mask) { \
color_t normal = graya_blender_normal(backdrop, src, opacity); \
color_t blend = graya_blender_##name(backdrop, src, opacity); \
int Ba = graya_geta(backdrop); \
color_t normalToBlendMerge = graya_blender_merge(normal, blend, Ba); \
int t; \
int srcTotalAlpha = MUL_UN8(graya_geta(src), opacity, t); \
int compositeAlpha = MUL_UN8(Ba, srcTotalAlpha, t); \
return graya_blender_merge(normalToBlendMerge, blend, compositeAlpha); \
} \
else \
return graya_blender_normal(backdrop, src, opacity); \
}
inline uint32_t blend_divide(uint32_t b, uint32_t s)
{
if (b == 0)
@ -169,13 +203,13 @@ color_t rgba_blender_normal(color_t backdrop, color_t src, int opacity)
{
int t;
if ((backdrop & rgba_a_mask) == 0) {
if (!(backdrop & rgba_a_mask)) {
int a = rgba_geta(src);
a = MUL_UN8(a, opacity, t);
a <<= rgba_a_shift;
return (src & rgba_rgb_mask) | a;
}
else if ((src & rgba_a_mask) == 0) {
else if (!(src & rgba_a_mask)) {
return backdrop;
}
@ -209,6 +243,14 @@ color_t rgba_blender_normal(color_t backdrop, color_t src, int opacity)
return rgba(Rr, Rg, Rb, Ra);
}
color_t rgba_blender_normal_dst_over(color_t backdrop, color_t src, int opacity)
{
int t;
int Sa = MUL_UN8(rgba_geta(src), opacity, t);
src = (src & rgba_rgb_mask) | (Sa << rgba_a_shift);
return rgba_blender_normal(src, backdrop);
}
color_t rgba_blender_multiply(color_t backdrop, color_t src, int opacity)
{
int t;
@ -469,6 +511,26 @@ color_t rgba_blender_divide(color_t backdrop, color_t src, int opacity)
return rgba_blender_normal(backdrop, src, opacity);
}
// New Blender Methods:
RGBA_BLENDER_N(multiply)
RGBA_BLENDER_N(screen)
RGBA_BLENDER_N(overlay)
RGBA_BLENDER_N(darken)
RGBA_BLENDER_N(lighten)
RGBA_BLENDER_N(color_dodge)
RGBA_BLENDER_N(color_burn)
RGBA_BLENDER_N(hard_light)
RGBA_BLENDER_N(soft_light)
RGBA_BLENDER_N(difference)
RGBA_BLENDER_N(exclusion)
RGBA_BLENDER_N(hsl_color)
RGBA_BLENDER_N(hsl_hue)
RGBA_BLENDER_N(hsl_saturation)
RGBA_BLENDER_N(hsl_luminosity)
RGBA_BLENDER_N(addition)
RGBA_BLENDER_N(subtract)
RGBA_BLENDER_N(divide)
//////////////////////////////////////////////////////////////////////
// GRAY blenders
@ -520,13 +582,13 @@ color_t graya_blender_normal(color_t backdrop, color_t src, int opacity)
{
int t;
if ((backdrop & graya_a_mask) == 0) {
if (!(backdrop & graya_a_mask)) {
int a = graya_geta(src);
a = MUL_UN8(a, opacity, t);
a <<= graya_a_shift;
return (src & 0xff) | a;
}
else if ((src & graya_a_mask) == 0)
else if (!(src & graya_a_mask))
return backdrop;
int Bg, Ba;
@ -649,6 +711,21 @@ color_t graya_blender_divide(color_t backdrop, color_t src, int opacity)
return graya_blender_normal(backdrop, src, opacity);
}
GRAYA_BLENDER_N(multiply)
GRAYA_BLENDER_N(screen)
GRAYA_BLENDER_N(overlay)
GRAYA_BLENDER_N(darken)
GRAYA_BLENDER_N(lighten)
GRAYA_BLENDER_N(color_dodge)
GRAYA_BLENDER_N(color_burn)
GRAYA_BLENDER_N(hard_light)
GRAYA_BLENDER_N(soft_light)
GRAYA_BLENDER_N(difference)
GRAYA_BLENDER_N(exclusion)
GRAYA_BLENDER_N(addition)
GRAYA_BLENDER_N(subtract)
GRAYA_BLENDER_N(divide)
//////////////////////////////////////////////////////////////////////
// indexed
@ -660,7 +737,7 @@ color_t indexed_blender_src(color_t dst, color_t src, int opacity)
//////////////////////////////////////////////////////////////////////
// getters
BlendFunc get_rgba_blender(BlendMode blendmode)
BlendFunc get_rgba_blender(BlendMode blendmode, const bool newBlend)
{
switch (blendmode) {
case BlendMode::SRC: return rgba_blender_src;
@ -668,32 +745,33 @@ BlendFunc get_rgba_blender(BlendMode blendmode)
case BlendMode::NEG_BW: return rgba_blender_neg_bw;
case BlendMode::RED_TINT: return rgba_blender_red_tint;
case BlendMode::BLUE_TINT: return rgba_blender_blue_tint;
case BlendMode::DST_OVER: return rgba_blender_normal_dst_over;
case BlendMode::NORMAL: return rgba_blender_normal;
case BlendMode::MULTIPLY: return rgba_blender_multiply;
case BlendMode::SCREEN: return rgba_blender_screen;
case BlendMode::OVERLAY: return rgba_blender_overlay;
case BlendMode::DARKEN: return rgba_blender_darken;
case BlendMode::LIGHTEN: return rgba_blender_lighten;
case BlendMode::COLOR_DODGE: return rgba_blender_color_dodge;
case BlendMode::COLOR_BURN: return rgba_blender_color_burn;
case BlendMode::HARD_LIGHT: return rgba_blender_hard_light;
case BlendMode::SOFT_LIGHT: return rgba_blender_soft_light;
case BlendMode::DIFFERENCE: return rgba_blender_difference;
case BlendMode::EXCLUSION: return rgba_blender_exclusion;
case BlendMode::HSL_HUE: return rgba_blender_hsl_hue;
case BlendMode::HSL_SATURATION: return rgba_blender_hsl_saturation;
case BlendMode::HSL_COLOR: return rgba_blender_hsl_color;
case BlendMode::HSL_LUMINOSITY: return rgba_blender_hsl_luminosity;
case BlendMode::ADDITION: return rgba_blender_addition;
case BlendMode::SUBTRACT: return rgba_blender_subtract;
case BlendMode::DIVIDE: return rgba_blender_divide;
case BlendMode::MULTIPLY: return newBlend? rgba_blender_multiply_n: rgba_blender_multiply;
case BlendMode::SCREEN: return newBlend? rgba_blender_screen_n: rgba_blender_screen;
case BlendMode::OVERLAY: return newBlend? rgba_blender_overlay_n: rgba_blender_overlay;
case BlendMode::DARKEN: return newBlend? rgba_blender_darken_n: rgba_blender_darken;
case BlendMode::LIGHTEN: return newBlend? rgba_blender_lighten_n: rgba_blender_lighten;
case BlendMode::COLOR_DODGE: return newBlend? rgba_blender_color_dodge_n: rgba_blender_color_dodge;
case BlendMode::COLOR_BURN: return newBlend? rgba_blender_color_burn_n: rgba_blender_color_burn;
case BlendMode::HARD_LIGHT: return newBlend? rgba_blender_hard_light_n: rgba_blender_hard_light;
case BlendMode::SOFT_LIGHT: return newBlend? rgba_blender_soft_light_n: rgba_blender_soft_light;
case BlendMode::DIFFERENCE: return newBlend? rgba_blender_difference_n: rgba_blender_difference;
case BlendMode::EXCLUSION: return newBlend? rgba_blender_exclusion_n: rgba_blender_exclusion;
case BlendMode::HSL_HUE: return newBlend? rgba_blender_hsl_hue_n: rgba_blender_hsl_hue;
case BlendMode::HSL_SATURATION: return newBlend? rgba_blender_hsl_saturation_n: rgba_blender_hsl_saturation;
case BlendMode::HSL_COLOR: return newBlend? rgba_blender_hsl_color_n: rgba_blender_hsl_color;
case BlendMode::HSL_LUMINOSITY: return newBlend? rgba_blender_hsl_luminosity_n: rgba_blender_hsl_luminosity;
case BlendMode::ADDITION: return newBlend? rgba_blender_addition_n: rgba_blender_addition;
case BlendMode::SUBTRACT: return newBlend? rgba_blender_subtract_n: rgba_blender_subtract;
case BlendMode::DIVIDE: return newBlend? rgba_blender_divide_n: rgba_blender_divide;
}
ASSERT(false);
return rgba_blender_src;
}
BlendFunc get_graya_blender(BlendMode blendmode)
BlendFunc get_graya_blender(BlendMode blendmode, const bool newBlend)
{
switch (blendmode) {
case BlendMode::SRC: return graya_blender_src;
@ -703,30 +781,30 @@ BlendFunc get_graya_blender(BlendMode blendmode)
case BlendMode::BLUE_TINT: return graya_blender_normal;
case BlendMode::NORMAL: return graya_blender_normal;
case BlendMode::MULTIPLY: return graya_blender_multiply;
case BlendMode::SCREEN: return graya_blender_screen;
case BlendMode::OVERLAY: return graya_blender_overlay;
case BlendMode::DARKEN: return graya_blender_darken;
case BlendMode::LIGHTEN: return graya_blender_lighten;
case BlendMode::COLOR_DODGE: return graya_blender_color_dodge;
case BlendMode::COLOR_BURN: return graya_blender_color_burn;
case BlendMode::HARD_LIGHT: return graya_blender_hard_light;
case BlendMode::SOFT_LIGHT: return graya_blender_soft_light;
case BlendMode::DIFFERENCE: return graya_blender_difference;
case BlendMode::EXCLUSION: return graya_blender_exclusion;
case BlendMode::MULTIPLY: return newBlend? graya_blender_multiply_n: graya_blender_multiply;
case BlendMode::SCREEN: return newBlend? graya_blender_screen_n: graya_blender_screen;
case BlendMode::OVERLAY: return newBlend? graya_blender_overlay_n: graya_blender_overlay;
case BlendMode::DARKEN: return newBlend? graya_blender_darken_n: graya_blender_darken;
case BlendMode::LIGHTEN: return newBlend? graya_blender_lighten_n: graya_blender_lighten;
case BlendMode::COLOR_DODGE: return newBlend? graya_blender_color_dodge_n: graya_blender_color_dodge;
case BlendMode::COLOR_BURN: return newBlend? graya_blender_color_burn_n: graya_blender_color_burn;
case BlendMode::HARD_LIGHT: return newBlend? graya_blender_hard_light_n: graya_blender_hard_light;
case BlendMode::SOFT_LIGHT: return newBlend? graya_blender_soft_light_n: graya_blender_soft_light;
case BlendMode::DIFFERENCE: return newBlend? graya_blender_difference_n: graya_blender_difference;
case BlendMode::EXCLUSION: return newBlend? graya_blender_exclusion_n: graya_blender_exclusion;
case BlendMode::HSL_HUE: return graya_blender_normal;
case BlendMode::HSL_SATURATION: return graya_blender_normal;
case BlendMode::HSL_COLOR: return graya_blender_normal;
case BlendMode::HSL_LUMINOSITY: return graya_blender_normal;
case BlendMode::ADDITION: return graya_blender_addition;
case BlendMode::SUBTRACT: return graya_blender_subtract;
case BlendMode::DIVIDE: return graya_blender_divide;
case BlendMode::ADDITION: return newBlend? graya_blender_exclusion_n: graya_blender_addition;
case BlendMode::SUBTRACT: return newBlend? graya_blender_subtract_n: graya_blender_subtract;
case BlendMode::DIVIDE: return newBlend? graya_blender_divide_n: graya_blender_divide;
}
ASSERT(false);
return graya_blender_src;
}
BlendFunc get_indexed_blender(BlendMode blendmode)
BlendFunc get_indexed_blender(BlendMode blendmode, const bool newBlend)
{
return indexed_blender_src;
}

View File

@ -1,4 +1,5 @@
// Aseprite Document Library
// Copyright (C) 2019 Igara Studio S.A.
// Copyright (c) 2001-2017 David Capello
//
// This file is released under the terms of the MIT license.
@ -21,6 +22,7 @@ namespace doc {
color_t rgba_blender_red_tint(color_t backdrop, color_t src, int opacity);
color_t rgba_blender_blue_tint(color_t backdrop, color_t src, int opacity);
color_t rgba_blender_normal(color_t backdrop, color_t src, int opacity = 255);
color_t rgba_blender_normal_dst_over(color_t backdrop, color_t src, int opacity);
color_t rgba_blender_multiply(color_t backdrop, color_t src, int opacity);
color_t rgba_blender_screen(color_t backdrop, color_t src, int opacity);
color_t rgba_blender_overlay(color_t backdrop, color_t src, int opacity);
@ -55,12 +57,15 @@ namespace doc {
color_t graya_blender_soft_light(color_t backdrop, color_t src, int opacity);
color_t graya_blender_difference(color_t backdrop, color_t src, int opacity);
color_t graya_blender_exclusion(color_t backdrop, color_t src, int opacity);
color_t graya_blender_addition(color_t backdrop, color_t src, int opacity);
color_t graya_blender_subtract(color_t backdrop, color_t src, int opacity);
color_t graya_blender_divide(color_t backdrop, color_t src, int opacity);
color_t indexed_blender_src(color_t dst, color_t src, int opacity);
BlendFunc get_rgba_blender(BlendMode blendmode);
BlendFunc get_graya_blender(BlendMode blendmode);
BlendFunc get_indexed_blender(BlendMode blendmode);
BlendFunc get_rgba_blender(BlendMode blendmode, const bool newBlend);
BlendFunc get_graya_blender(BlendMode blendmode, const bool newBlend);
BlendFunc get_indexed_blender(BlendMode blendmode, const bool newBlend);
} // namespace doc

View File

@ -20,6 +20,7 @@ namespace doc {
NEG_BW = -4, // Negative Black & White
RED_TINT = -5,
BLUE_TINT = -6,
DST_OVER = -7,
// Aseprite (.ase files) blend modes
NORMAL = 0,

View File

@ -1,5 +1,5 @@
// Aseprite Document Library
// Copyright (c) 2018 Igara Studio S.A.
// Copyright (c) 2018-2019 Igara Studio S.A.
// Copyright (c) 2001-2015 David Capello
//
// This file is released under the terms of the MIT license.
@ -39,8 +39,8 @@ namespace doc {
return bytes_per_pixel * pixels_per_row;
}
static inline BlendFunc get_blender(BlendMode blend_mode) {
return get_rgba_blender(blend_mode);
static inline BlendFunc get_blender(BlendMode blend_mode, bool newBlend) {
return get_rgba_blender(blend_mode, newBlend);
}
static inline bool same_color(const pixel_t a, const pixel_t b) {
@ -80,8 +80,8 @@ namespace doc {
return bytes_per_pixel * pixels_per_row;
}
static inline BlendFunc get_blender(BlendMode blend_mode) {
return get_graya_blender(blend_mode);
static inline BlendFunc get_blender(BlendMode blend_mode, bool newBlend) {
return get_graya_blender(blend_mode, newBlend);
}
static inline bool same_color(const pixel_t a, const pixel_t b) {
@ -121,8 +121,8 @@ namespace doc {
return bytes_per_pixel * pixels_per_row;
}
static inline BlendFunc get_blender(BlendMode blend_mode) {
return get_indexed_blender(blend_mode);
static inline BlendFunc get_blender(BlendMode blend_mode, bool newBlend) {
return get_indexed_blender(blend_mode, newBlend);
}
static inline bool same_color(const pixel_t a, const pixel_t b) {

View File

@ -1,4 +1,5 @@
// Aseprite Render Library
// Copyright (c) 2019 Igara Studio S.A.
// Copyright (c) 2001-2018 David Capello
//
// This file is released under the terms of the MIT license.
@ -20,7 +21,8 @@ color_t get_sprite_pixel(const Sprite* sprite,
const double x,
const double y,
const frame_t frame,
const Projection& proj)
const Projection& proj,
const bool newBlend)
{
color_t color = 0;
@ -29,6 +31,7 @@ color_t get_sprite_pixel(const Sprite* sprite,
std::unique_ptr<Image> image(Image::create(sprite->pixelFormat(), 1, 1));
render::Render render;
render.setNewBlend(newBlend);
render.setRefLayersVisiblity(true);
render.setProjection(proj);
render.renderSprite(

View File

@ -1,4 +1,5 @@
// Aseprite Render Library
// Copyright (c) 2019 Igara Studio S.A.
// Copyright (c) 2001-2016 David Capello
//
// This file is released under the terms of the MIT license.
@ -26,7 +27,8 @@ namespace render {
const double x,
const double y,
const frame_t frame,
const Projection& proj);
const Projection& proj,
const bool newBlend);
} // namespace render

View File

@ -1,4 +1,5 @@
// Aseprite Render Library
// Copyright (C) 2019 Igara Studio S.A.
// Copyright (c) 2001-2018 David Capello
//
// This file is released under the terms of the MIT license.
@ -40,7 +41,8 @@ Palette* create_palette_from_sprite(
const frame_t toFrame,
const bool withAlpha,
Palette* palette,
TaskDelegate* delegate)
TaskDelegate* delegate,
const bool newBlend)
{
PaletteOptimizer optimizer;
@ -53,6 +55,7 @@ Palette* create_palette_from_sprite(
// Feed the optimizer with all rendered frames
render::Render render;
render.setNewBlend(newBlend);
for (frame_t frame=fromFrame; frame<=toFrame; ++frame) {
render.renderSprite(flat_image.get(), sprite, frame);
optimizer.feedWithImage(flat_image.get(), withAlpha);

View File

@ -1,4 +1,5 @@
// Aseprite Rener Library
// Copyright (C) 2019 Igara Studio S.A.
// Copyright (c) 2001-2015, 2017 David Capello
//
// This file is released under the terms of the MIT license.
@ -43,7 +44,8 @@ namespace render {
const doc::frame_t toFrame,
const bool withAlpha,
doc::Palette* newPalette, // Can be NULL to create a new palette
TaskDelegate* delegate);
TaskDelegate* delegate,
const bool newBlend);
// Changes the image pixel format. The dithering method is used only
// when you want to convert from RGB to Indexed.

View File

@ -1,4 +1,5 @@
// Aseprite Render Library
// Copyright (c) 2019 Igara Studio S.A.
// Copyright (c) 2001-2018 David Capello
//
// This file is released under the terms of the MIT license.
@ -33,9 +34,9 @@ class BlenderHelper {
BlendFunc m_blendFunc;
color_t m_mask_color;
public:
BlenderHelper(const Image* src, const Palette* pal, BlendMode blendMode)
BlenderHelper(const Image* src, const Palette* pal, BlendMode blendMode, const bool newBlend)
{
m_blendFunc = SrcTraits::get_blender(blendMode);
m_blendFunc = SrcTraits::get_blender(blendMode, newBlend);
m_mask_color = src->maskColor();
}
inline typename DstTraits::pixel_t
@ -55,9 +56,9 @@ class BlenderHelper<RgbTraits, GrayscaleTraits> {
BlendFunc m_blendFunc;
color_t m_mask_color;
public:
BlenderHelper(const Image* src, const Palette* pal, BlendMode blendMode)
BlenderHelper(const Image* src, const Palette* pal, BlendMode blendMode, const bool newBlend)
{
m_blendFunc = RgbTraits::get_blender(blendMode);
m_blendFunc = RgbTraits::get_blender(blendMode, newBlend);
m_mask_color = src->maskColor();
}
inline RgbTraits::pixel_t
@ -81,10 +82,10 @@ class BlenderHelper<RgbTraits, IndexedTraits> {
BlendFunc m_blendFunc;
color_t m_mask_color;
public:
BlenderHelper(const Image* src, const Palette* pal, BlendMode blendMode)
BlenderHelper(const Image* src, const Palette* pal, BlendMode blendMode, const bool newBlend)
{
m_blendMode = blendMode;
m_blendFunc = RgbTraits::get_blender(blendMode);
m_blendFunc = RgbTraits::get_blender(blendMode, newBlend);
m_mask_color = src->maskColor();
m_pal = pal;
}
@ -111,7 +112,7 @@ class BlenderHelper<IndexedTraits, IndexedTraits> {
BlendMode m_blendMode;
color_t m_mask_color;
public:
BlenderHelper(const Image* src, const Palette* pal, BlendMode blendMode)
BlenderHelper(const Image* src, const Palette* pal, BlendMode blendMode, const bool newBlend)
{
m_blendMode = blendMode;
m_mask_color = src->maskColor();
@ -140,14 +141,15 @@ void composite_image_without_scale(
const int opacity,
const BlendMode blendMode,
const double sx,
const double sy)
const double sy,
const bool newBlend)
{
ASSERT(dst);
ASSERT(src);
ASSERT(DstTraits::pixel_format == dst->pixelFormat());
ASSERT(SrcTraits::pixel_format == src->pixelFormat());
BlenderHelper<DstTraits, SrcTraits> blender(src, pal, blendMode);
BlenderHelper<DstTraits, SrcTraits> blender(src, pal, blendMode, newBlend);
gfx::Clip area(areaF);
if (!area.clip(dst->width(), dst->height(),
@ -195,7 +197,8 @@ void composite_image_scale_up(
const int opacity,
const BlendMode blendMode,
const double sx,
const double sy)
const double sy,
const bool newBlend)
{
ASSERT(dst);
ASSERT(src);
@ -208,7 +211,7 @@ void composite_image_scale_up(
int(sy*double(src->height()))))
return;
BlenderHelper<DstTraits, SrcTraits> blender(src, pal, blendMode);
BlenderHelper<DstTraits, SrcTraits> blender(src, pal, blendMode, newBlend);
int px_x, px_y;
int px_w = int(sx);
int px_h = int(sy);
@ -334,7 +337,8 @@ void composite_image_scale_down(
const int opacity,
const BlendMode blendMode,
const double sx,
const double sy)
const double sy,
const bool newBlend)
{
ASSERT(dst);
ASSERT(src);
@ -347,7 +351,7 @@ void composite_image_scale_down(
int(sy*double(src->height()))))
return;
BlenderHelper<DstTraits, SrcTraits> blender(src, pal, blendMode);
BlenderHelper<DstTraits, SrcTraits> blender(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)
@ -401,7 +405,8 @@ void composite_image_general(
const int opacity,
const BlendMode blendMode,
const double sx,
const double sy)
const double sy,
const bool newBlend)
{
ASSERT(dst);
ASSERT(src);
@ -413,7 +418,7 @@ void composite_image_general(
sx*src->width(), sy*src->height()))
return;
BlenderHelper<DstTraits, SrcTraits> blender(src, pal, blendMode);
BlenderHelper<DstTraits, SrcTraits> blender(src, pal, blendMode, newBlend);
gfx::Rect dstBounds(
area.dstBounds().x, area.dstBounds().y,
@ -514,6 +519,7 @@ Render::Render()
, m_extraType(ExtraType::NONE)
, m_extraCel(NULL)
, m_extraImage(NULL)
, m_newBlendMethod(true)
, m_bgType(BgType::TRANSPARENT)
, m_bgCheckedSize(16, 16)
, m_globalOpacity(255)
@ -539,6 +545,11 @@ void Render::setNonactiveLayersOpacity(const int opacity)
m_nonactiveLayersOpacity = opacity;
}
void Render::setNewBlend(const bool newBlend)
{
m_newBlendMethod = newBlend;
}
void Render::setProjection(const Projection& projection)
{
m_proj = projection;
@ -694,53 +705,26 @@ void Render::renderSprite(
}
}
// Draw checked background
switch (m_bgType) {
case BgType::CHECKED:
if (bgLayer && bgLayer->isVisible() && rgba_geta(bg_color) == 255) {
fill_rect(dstImage, area.dstBounds(), bg_color);
}
else {
renderBackground(dstImage, area);
if (bgLayer && bgLayer->isVisible() && rgba_geta(bg_color) > 0) {
blend_rect(dstImage,
int(area.dst.x),
int(area.dst.y),
int(area.dst.x+area.size.w-1),
int(area.dst.y+area.size.h-1),
bg_color, 255);
}
}
break;
case BgType::TRANSPARENT:
fill_rect(dstImage, area.dstBounds(), bg_color);
break;
if (m_newBlendMethod) {
// New Blending Method:
// Generate temporal background image. It will merge with dstImage.
if (!m_tmpBuf)
m_tmpBuf.reset(new doc::ImageBuffer);
ImageRef tmpBackground(Image::create(dstImage->spec(), m_tmpBuf));
renderBg(tmpBackground.get(), bgLayer, bg_color, area);
// Clear dstImage
fill_rect(dstImage, area.dstBounds(), bg_color);
// Draw Background - Onion skin behind the sprite - Transparent Layers
renderSpriteLayers(dstImage, area, frame, compositeImage);
// Draw the background on each pixel which opacity is different to 255
composite_image(dstImage, tmpBackground.get(), sprite->palette(frame),
0, 0, 255, BlendMode::DST_OVER);
}
else {
// Old Blending Method:
renderBg(dstImage, bgLayer, bg_color, area);
renderSpriteLayers(dstImage, area, frame, compositeImage);
}
// Draw the background layer.
m_globalOpacity = 255;
renderLayer(
m_sprite->root(), dstImage,
area, frame, compositeImage,
true,
false,
BlendMode::UNSPECIFIED,
false);
// Draw onion skin behind the sprite.
if (m_onionskin.position() == OnionskinPosition::BEHIND)
renderOnionskin(dstImage, area, frame, compositeImage);
// Draw the transparent layers.
m_globalOpacity = 255;
renderLayer(
m_sprite->root(), dstImage,
area, frame, compositeImage,
false,
true,
BlendMode::UNSPECIFIED, false);
// Draw onion skin in front of the sprite.
if (m_onionskin.position() == OnionskinPosition::INFRONT)
@ -766,6 +750,57 @@ void Render::renderSprite(
}
}
void Render::renderSpriteLayers(Image* dstImage,
const gfx::ClipF& area,
frame_t frame,
CompositeImageFunc compositeImage) {
// Draw the background layer.
m_globalOpacity = 255;
renderLayer(m_sprite->root(), dstImage,
area, frame, compositeImage,
true,
false,
BlendMode::UNSPECIFIED,
false);
// Draw onion skin behind the sprite.
if (m_onionskin.position() == OnionskinPosition::BEHIND)
renderOnionskin(dstImage, area, frame, compositeImage);
// Draw the transparent layers.
m_globalOpacity = 255;
renderLayer(m_sprite->root(), dstImage,
area, frame, compositeImage,
false,
true,
BlendMode::UNSPECIFIED, false);
}
void Render::renderBg(Image* image, const Layer* bgLayer, color_t bg_color, const gfx::ClipF& area) {
switch (m_bgType) {
case BgType::CHECKED:
if (bgLayer && bgLayer->isVisible() && rgba_geta(bg_color) == 255) {
fill_rect(image, area.dstBounds(), bg_color);
}
else {
renderBackground(image, area);
if (bgLayer && bgLayer->isVisible() && rgba_geta(bg_color) > 0) {
blend_rect(image,
int(area.dst.x),
int(area.dst.y),
int(area.dst.x+area.size.w-1),
int(area.dst.y+area.size.h-1),
bg_color, 255);
}
}
break;
case BgType::TRANSPARENT:
fill_rect(image, area.dstBounds(), bg_color);
break;
}
}
void Render::renderOnionskin(
Image* dstImage,
const gfx::Clip& area,
@ -906,7 +941,8 @@ void Render::renderImage(
m_proj.applyY(src_image->height())),
opacity, blendMode,
m_proj.scaleX(),
m_proj.scaleY());
m_proj.scaleY(),
m_newBlendMethod);
}
void Render::renderLayer(
@ -1148,7 +1184,8 @@ void Render::renderImage(
opacity,
blendMode,
m_proj.scaleX() * celBounds.w / double(cel_image->width()),
m_proj.scaleY() * celBounds.h / double(cel_image->height()));
m_proj.scaleY() * celBounds.h / double(cel_image->height()),
m_newBlendMethod);
}
CompositeImageFunc Render::getImageComposition(

View File

@ -1,4 +1,5 @@
// Aseprite Render Library
// Copyright (C) 2019 Igara Studio S.A.
// Copyright (c) 2001-2018 David Capello
//
// This file is released under the terms of the MIT license.
@ -8,6 +9,7 @@
#define RENDER_RENDER_H_INCLUDED
#pragma once
#include "doc/doc.h"
#include "doc/anidir.h"
#include "doc/blend_mode.h"
#include "doc/color.h"
@ -41,7 +43,8 @@ namespace render {
const int opacity,
const BlendMode blendMode,
const double sx,
const double sy);
const double sy,
const bool newBlend);
class Render {
enum Flags {
@ -53,6 +56,7 @@ namespace render {
void setRefLayersVisiblity(const bool visible);
void setNonactiveLayersOpacity(const int opacity);
void setNewBlend(const bool newBlend);
// Viewport configuration
void setProjection(const Projection& projection);
@ -127,6 +131,18 @@ namespace render {
const int opacity,
const BlendMode blendMode);
void renderSpriteLayers(
Image* dstImage,
const gfx::ClipF& area,
frame_t frame,
CompositeImageFunc compositeImage);
void renderBg(
Image* image,
const Layer* bgLayer,
color_t bg_color,
const gfx::ClipF& area);
private:
void renderOnionskin(
Image* image,
@ -180,6 +196,7 @@ namespace render {
const Cel* m_extraCel;
const Image* m_extraImage;
BlendMode m_extraBlendMode;
bool m_newBlendMethod;
BgType m_bgType;
bool m_bgZoom;
color_t m_bgColor1;
@ -193,6 +210,7 @@ namespace render {
gfx::Point m_previewPos;
BlendMode m_previewBlendMode;
OnionskinOptions m_onionskin;
ImageBufferPtr m_tmpBuf;
};
void composite_image(Image* dst,