Add layer blending modes (fix #318)

- Added doc::BlendMode enum and doc::BlendFunc type
- Renamed LayerImage::getBlendMode() -> blendMode()
- BLEND_MODE_COPY is BlendMode::SRC now
- BLEND_MODE_NORMAL is BlendMode::NORMAL now
- Added app::cmd::SetLayerBlendMode
This commit is contained in:
David Capello 2015-06-13 21:29:16 -03:00
parent 8da36f990b
commit 21ca87862c
49 changed files with 1067 additions and 564 deletions

View File

@ -6,6 +6,9 @@
<grid columns="2">
<label text="Name:" />
<entry text="" id="name" magnet="true" maxsize="256" minwidth="64" cell_align="horizontal" />
<label text="Mode:" />
<combobox id="mode" />
</grid>
<separator horizontal="true" />
<hbox>

View File

@ -150,26 +150,23 @@ Layer Chunk (0x2004)
WORD Layer child level (see NOTE.1)
WORD Default layer width in pixels (ignored)
WORD Default layer height in pixels (ignored)
WORD DEPRECATED This field is not used anymore.
Blend mode (always 0 for layer set)
BLEND_MODE_NORMAL = 0
BLEND_MODE_DISSOLVE = 1
BLEND_MODE_MULTIPLY = 2
BLEND_MODE_SCREEN = 3
BLEND_MODE_OVERLAY = 4
BLEND_MODE_HARD_LIGHT = 5
BLEND_MODE_DODGE = 6
BLEND_MODE_BURN = 7
BLEND_MODE_DARKEN = 8
BLEND_MODE_LIGHTEN = 9
BLEND_MODE_ADDITION = 10
BLEND_MODE_SUBTRACT = 11
BLEND_MODE_DIFFERENCE = 12
BLEND_MODE_HUE = 13
BLEND_MODE_SATURATION = 14
BLEND_MODE_COLOR = 15
BLEND_MODE_LUMINOSITY = 16
BLEND_MODE_COPY = 17
WORD Blend mode (always 0 for layer set)
Normal = 0
Multiply = 1
Screen = 2
Overlay = 3
Darken = 4
Lighten = 5
Color Dodge = 6
Color Burn = 7
Hard Light = 8
Soft Light = 9
Difference = 10
Exclusion = 11
Hue = 12
Saturation = 13
Color = 14
Luminosity = 15
BYTE[4] For future (set to zero)
STRING Layer name

View File

@ -113,6 +113,7 @@ add_library(app-lib
cmd/set_frame_tag_color.cpp
cmd/set_frame_tag_name.cpp
cmd/set_frame_tag_range.cpp
cmd/set_layer_blend_mode.cpp
cmd/set_layer_flags.cpp
cmd/set_layer_name.cpp
cmd/set_mask.cpp

View File

@ -61,7 +61,7 @@ void BackgroundFromLayer::onExecute()
render::composite_image(bg_image.get(), cel_image,
cel->x(), cel->y(),
MID(0, cel->opacity(), 255),
static_cast<LayerImage*>(layer)->getBlendMode());
static_cast<LayerImage*>(layer)->blendMode());
// now we have to copy the new image (bg_image) to the cel...
executeAndAdd(new cmd::SetCelPosition(cel, 0, 0));

View File

@ -92,12 +92,13 @@ void CopyCel::onExecute()
executeAndAdd(new cmd::SetCelData(dstCel, srcCel->dataRef()));
}
else {
int blend = (srcLayer->isBackground() ?
BLEND_MODE_COPY: BLEND_MODE_NORMAL);
BlendMode blend = (srcLayer->isBackground() ?
BlendMode::SRC:
BlendMode::NORMAL);
ImageRef tmp(Image::createCopy(dstImage.get()));
render::composite_image(tmp.get(), srcImage,
srcCel->x(), srcCel->y(), 255, blend);
srcCel->x(), srcCel->y(), 255, blend);
executeAndAdd(new cmd::CopyRect(dstImage.get(), tmp.get(), gfx::Clip(tmp->bounds())));
}
}

View File

@ -95,8 +95,9 @@ void MoveCel::onExecute()
executeAndAdd(new cmd::UnlinkCel(srcCel));
}
else {
int blend = (srcLayer->isBackground() ?
BLEND_MODE_COPY: BLEND_MODE_NORMAL);
BlendMode blend = (srcLayer->isBackground() ?
BlendMode::SRC:
BlendMode::NORMAL);
ImageRef tmp(Image::createCopy(dstImage.get()));
render::composite_image(tmp.get(), srcImage,

View File

@ -0,0 +1,39 @@
// Aseprite
// Copyright (C) 2001-2015 David Capello
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2 as
// published by the Free Software Foundation.
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "app/cmd/set_layer_blend_mode.h"
#include "doc/layer.h"
namespace app {
namespace cmd {
SetLayerBlendMode::SetLayerBlendMode(LayerImage* layer, BlendMode blendMode)
: WithLayer(layer)
, m_oldBlendMode(layer->blendMode())
, m_newBlendMode(blendMode)
{
}
void SetLayerBlendMode::onExecute()
{
static_cast<LayerImage*>(layer())->setBlendMode(m_newBlendMode);
layer()->incrementVersion();
}
void SetLayerBlendMode::onUndo()
{
static_cast<LayerImage*>(layer())->setBlendMode(m_oldBlendMode);
layer()->incrementVersion();
}
} // namespace cmd
} // namespace app

View File

@ -0,0 +1,44 @@
// Aseprite
// Copyright (C) 2001-2015 David Capello
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2 as
// published by the Free Software Foundation.
#ifndef APP_CMD_SET_LAYER_BLEND_MODE_H_INCLUDED
#define APP_CMD_SET_LAYER_BLEND_MODE_H_INCLUDED
#pragma once
#include "app/cmd.h"
#include "app/cmd/with_layer.h"
#include "doc/blend_mode.h"
namespace doc {
class LayerImage;
}
namespace app {
namespace cmd {
using namespace doc;
class SetLayerBlendMode : public Cmd
, public WithLayer {
public:
SetLayerBlendMode(LayerImage* layer, BlendMode blendMode);
protected:
void onExecute() override;
void onUndo() override;
size_t onMemSize() const override {
return sizeof(*this);
}
private:
BlendMode m_oldBlendMode;
BlendMode m_newBlendMode;
};
} // namespace cmd
} // namespace app
#endif

View File

@ -209,23 +209,23 @@ protected:
switch (m_tiled) {
case TiledMode::NONE:
render.renderImage(m_doublebuf, m_render, m_pal, x, y,
m_zoom, 255, BLEND_MODE_NORMAL);
m_zoom, 255, BlendMode::NORMAL);
break;
case TiledMode::X_AXIS:
for (u=x-w; u<ui::display_w()+w; u+=w)
render.renderImage(m_doublebuf, m_render, m_pal, u, y,
m_zoom, 255, BLEND_MODE_NORMAL);
m_zoom, 255, BlendMode::NORMAL);
break;
case TiledMode::Y_AXIS:
for (v=y-h; v<ui::display_h()+h; v+=h)
render.renderImage(m_doublebuf, m_render, m_pal, x, v,
m_zoom, 255, BLEND_MODE_NORMAL);
m_zoom, 255, BlendMode::NORMAL);
break;
case TiledMode::BOTH:
for (v=y-h; v<ui::display_h()+h; v+=h)
for (u=x-w; u<ui::display_w()+w; u+=w)
render.renderImage(m_doublebuf, m_render, m_pal, u, v,
m_zoom, 255, BLEND_MODE_NORMAL);
m_zoom, 255, BlendMode::NORMAL);
break;
}

View File

@ -13,9 +13,12 @@
#include "ui/ui.h"
#include "app/app.h"
#include "app/cmd/set_layer_blend_mode.h"
#include "app/cmd/set_layer_name.h"
#include "app/commands/command.h"
#include "app/context_access.h"
#include "app/modules/gui.h"
#include "app/transaction.h"
#include "doc/image.h"
#include "doc/layer.h"
@ -51,18 +54,55 @@ bool LayerPropertiesCommand::onEnabled(Context* context)
void LayerPropertiesCommand::onExecute(Context* context)
{
const ContextReader reader(context);
const Layer* layer = reader.layer();
const LayerImage* layer = static_cast<const LayerImage*>(reader.layer());
app::gen::LayerProperties window;
window.name()->setText(layer->name().c_str());
window.name()->setMinSize(gfx::Size(128, 0));
window.name()->setExpansive(true);
window.mode()->addItem("Normal");
window.mode()->addItem("Multiply");
window.mode()->addItem("Screen");
window.mode()->addItem("Overlay");
window.mode()->addItem("Darken");
window.mode()->addItem("Lighten");
window.mode()->addItem("Color Dodge");
window.mode()->addItem("Color Burn");
window.mode()->addItem("Hard Light");
window.mode()->addItem("Soft Light");
window.mode()->addItem("Difference");
window.mode()->addItem("Exclusion");
window.mode()->addItem("Hue");
window.mode()->addItem("Saturation");
window.mode()->addItem("Color");
window.mode()->addItem("Luminosity");
window.mode()->setSelectedItemIndex((int)layer->blendMode());
window.mode()->setEnabled(!layer->isBackground());
window.openWindowInForeground();
if (window.getKiller() == window.ok()) {
ContextWriter writer(reader);
writer.layer()->setName(window.name()->getText());
update_screen_for_document(writer.document());
std::string newName = window.name()->getText();
BlendMode newBlendMode = (BlendMode)window.mode()->getSelectedItemIndex();
if (newName != layer->name() ||
newBlendMode != layer->blendMode()) {
ContextWriter writer(reader);
{
Transaction transaction(writer.context(), "Set Layer Properties");
if (newName != layer->name())
transaction.execute(new cmd::SetLayerName(writer.layer(), newName));
if (newBlendMode != layer->blendMode())
transaction.execute(new cmd::SetLayerBlendMode(static_cast<LayerImage*>(writer.layer()), newBlendMode));
transaction.commit();
}
update_screen_for_document(writer.document());
}
}
}

View File

@ -133,7 +133,7 @@ void MergeDownLayerCommand::onExecute(Context* context)
src_cel->x()-bounds.x,
src_cel->y()-bounds.y,
src_cel->opacity(),
static_cast<LayerImage*>(src_layer)->getBlendMode());
static_cast<LayerImage*>(src_layer)->blendMode());
transaction.execute(new cmd::SetCelPosition(dst_cel,
bounds.x, bounds.y));

View File

@ -53,7 +53,7 @@ Document::Document(Sprite* sprite)
// Extra cel
, m_extraCel(NULL)
, m_extraImage(NULL)
, m_extraCelBlendMode(BLEND_MODE_NORMAL)
, m_extraCelBlendMode(BlendMode::NORMAL)
, m_extraCelType(render::ExtraType::NONE)
// Mask
, m_mask(new Mask())

View File

@ -15,6 +15,7 @@
#include "base/observable.h"
#include "base/shared_ptr.h"
#include "base/unique_ptr.h"
#include "doc/blend_mode.h"
#include "doc/color.h"
#include "doc/document.h"
#include "doc/frame.h"
@ -127,8 +128,8 @@ namespace app {
Cel* getExtraCel() const;
Image* getExtraCelImage() const;
render::ExtraType getExtraCelType() const { return m_extraCelType; }
int getExtraCelBlendMode() const { return m_extraCelBlendMode; }
void setExtraCelBlendMode(int mode) { m_extraCelBlendMode = mode; }
BlendMode getExtraCelBlendMode() const { return m_extraCelBlendMode; }
void setExtraCelBlendMode(BlendMode mode) { m_extraCelBlendMode = mode; }
//////////////////////////////////////////////////////////////////////
// Mask
@ -212,7 +213,7 @@ namespace app {
// Image of the extra cel.
ImageRef m_extraImage;
int m_extraCelBlendMode;
BlendMode m_extraCelBlendMode;
render::ExtraType m_extraCelType;
// Current mask.

View File

@ -59,7 +59,6 @@
#include <set>
namespace app {
DocumentApi::DocumentApi(Document* document, Transaction& transaction)

View File

@ -663,16 +663,17 @@ static Layer* ase_file_read_layer_chunk(FILE* f, Sprite* sprite, Layer** previou
child_level = fgetw(f);
fgetw(f); // default width
fgetw(f); // default height
fgetw(f); // blend mode
int blendmode = fgetw(f); // blend mode
ase_file_read_padding(f, 4);
name = ase_file_read_string(f);
/* image layer */
// Image layer
if (layer_type == 0) {
layer = new LayerImage(sprite);
static_cast<LayerImage*>(layer)->setBlendMode((BlendMode)blendmode);
}
/* layer set */
// Layer set
else if (layer_type == 1) {
layer = new LayerFolder(sprite);
}
@ -684,7 +685,7 @@ static Layer* ase_file_read_layer_chunk(FILE* f, Sprite* sprite, Layer** previou
// name
layer->setName(name.c_str());
// child level...
// Child level
if (child_level == *current_level)
(*previous_layer)->parent()->addLayer(layer);
else if (child_level > *current_level)
@ -718,10 +719,10 @@ static void ase_file_write_layer_chunk(FILE* f, ASE_FrameHeader* frame_header, L
}
fputw(child_level, f);
/* default width & height, and blend mode */
// Default width & height, and blend mode
fputw(0, f);
fputw(0, f);
fputw(layer->isImage() ? static_cast<LayerImage*>(layer)->getBlendMode(): 0, f);
fputw(layer->isImage() ? (int)static_cast<LayerImage*>(layer)->blendMode(): 0, f);
/* padding */
ase_file_write_padding(f, 4);

View File

@ -75,10 +75,10 @@ TEST_F(GifFormat, OpaqueIndexed)
doc->setFilename(fn);
Palette* pal = sprite->palette(frame_t(0));
pal->setEntry(0, rgb(255, 255, 255));
pal->setEntry(1, rgb(255, 13, 254));
pal->setEntry(2, rgb(129, 255, 32));
pal->setEntry(3, rgb(0, 0, 255));
pal->setEntry(0, rgba(255, 255, 255, 255));
pal->setEntry(1, rgba(255, 13, 254, 255));
pal->setEntry(2, rgba(129, 255, 32, 255));
pal->setEntry(3, rgba(0, 0, 255, 255));
LayerImage* layer = dynamic_cast<LayerImage*>(sprite->folder()->getFirstLayer());
layer->setBackground(true);
@ -105,10 +105,10 @@ TEST_F(GifFormat, OpaqueIndexed)
EXPECT_TRUE(layer->isBackground());
Palette* pal = sprite->palette(frame_t(0));
EXPECT_EQ(rgb(255, 255, 255), pal->getEntry(0));
EXPECT_EQ(rgb(255, 13, 254), pal->getEntry(1));
EXPECT_EQ(rgb(129, 255, 32), pal->getEntry(2));
EXPECT_EQ(rgb(0, 0, 255), pal->getEntry(3));
EXPECT_EQ(rgba(255, 255, 255, 255), pal->getEntry(0));
EXPECT_EQ(rgba(255, 13, 254, 255), pal->getEntry(1));
EXPECT_EQ(rgba(129, 255, 32, 255), pal->getEntry(2));
EXPECT_EQ(rgba(0, 0, 255, 255), pal->getEntry(3));
Image* image = layer->cel(frame_t(0))->image();
EXPECT_EQ(0, sprite->transparentColor());
@ -132,10 +132,10 @@ TEST_F(GifFormat, TransparentIndexed)
doc->setFilename(fn);
Palette* pal = sprite->palette(frame_t(0));
pal->setEntry(0, rgb(255, 255, 255));
pal->setEntry(1, rgb(255, 13, 254));
pal->setEntry(2, rgb(129, 255, 32));
pal->setEntry(3, rgb(0, 0, 255));
pal->setEntry(0, rgba(255, 255, 255, 255));
pal->setEntry(1, rgba(255, 13, 254, 255));
pal->setEntry(2, rgba(129, 255, 32, 255));
pal->setEntry(3, rgba(0, 0, 255, 255));
LayerImage* layer = dynamic_cast<LayerImage*>(sprite->folder()->getFirstLayer());
ASSERT_NE((LayerImage*)NULL, layer);
@ -161,10 +161,10 @@ TEST_F(GifFormat, TransparentIndexed)
EXPECT_FALSE(layer->isBackground());
Palette* pal = sprite->palette(frame_t(0));
EXPECT_EQ(rgb(255, 255, 255), pal->getEntry(0));
EXPECT_EQ(rgb(255, 13, 254), pal->getEntry(1));
EXPECT_EQ(rgb(129, 255, 32), pal->getEntry(2));
EXPECT_EQ(rgb(0, 0, 255), pal->getEntry(3));
EXPECT_EQ(rgba(255, 255, 255, 255), pal->getEntry(0));
EXPECT_EQ(rgba(255, 13, 254, 255), pal->getEntry(1));
EXPECT_EQ(rgba(129, 255, 32, 255), pal->getEntry(2));
EXPECT_EQ(rgba(0, 0, 255, 255), pal->getEntry(3));
Image* image = layer->cel(frame_t(0))->image();
EXPECT_EQ(0, sprite->transparentColor());
@ -192,9 +192,9 @@ TEST_F(GifFormat, TransparentRgbQuantization)
Image* image = layer->cel(frame_t(0))->image();
image->putPixel(0, 0, rgba(0, 0, 0, 0));
image->putPixel(0, 1, rgb(255, 0, 0));
image->putPixel(1, 0, rgb(0, 255, 0));
image->putPixel(1, 1, rgb(0, 0, 255));
image->putPixel(0, 1, rgba(255, 0, 0, 255));
image->putPixel(1, 0, rgba(0, 255, 0, 255));
image->putPixel(1, 1, rgba(0, 0, 255, 255));
save_document(&m_ctx, doc);
@ -213,9 +213,9 @@ TEST_F(GifFormat, TransparentRgbQuantization)
Image* image = layer->cel(frame_t(0))->image();
EXPECT_EQ(0, sprite->transparentColor());
EXPECT_EQ(0, image->getPixel(0, 0));
EXPECT_EQ(rgb(255, 0, 0), pal->getEntry(image->getPixel(0, 1)));
EXPECT_EQ(rgb(0, 255, 0), pal->getEntry(image->getPixel(1, 0)));
EXPECT_EQ(rgb(0, 0, 255), pal->getEntry(image->getPixel(1, 1)));
EXPECT_EQ(rgba(255, 0, 0, 255), pal->getEntry(image->getPixel(0, 1)));
EXPECT_EQ(rgba(0, 255, 0, 255), pal->getEntry(image->getPixel(1, 0)));
EXPECT_EQ(rgba(0, 0, 255, 255), pal->getEntry(image->getPixel(1, 1)));
doc->close();
delete doc;
@ -237,10 +237,10 @@ TEST_F(GifFormat, OpaqueRgbQuantization)
EXPECT_NE((LayerImage*)NULL, sprite->backgroundLayer());
Image* image = layer->cel(frame_t(0))->image();
image->putPixel(0, 0, rgb(0, 0, 0));
image->putPixel(0, 1, rgb(255, 0, 0));
image->putPixel(1, 0, rgb(0, 255, 0));
image->putPixel(1, 1, rgb(0, 0, 255));
image->putPixel(0, 0, rgba(0, 0, 0, 255));
image->putPixel(0, 1, rgba(255, 0, 0, 255));
image->putPixel(1, 0, rgba(0, 255, 0, 255));
image->putPixel(1, 1, rgba(0, 0, 255, 255));
save_document(&m_ctx, doc);
@ -260,10 +260,10 @@ TEST_F(GifFormat, OpaqueRgbQuantization)
Palette* pal = sprite->palette(frame_t(0));
Image* image = layer->cel(frame_t(0))->image();
EXPECT_EQ(0, sprite->transparentColor());
EXPECT_EQ(rgb(0, 0, 0), pal->getEntry(image->getPixel(0, 0)));
EXPECT_EQ(rgb(255, 0, 0), pal->getEntry(image->getPixel(0, 1)));
EXPECT_EQ(rgb(0, 255, 0), pal->getEntry(image->getPixel(1, 0)));
EXPECT_EQ(rgb(0, 0, 255), pal->getEntry(image->getPixel(1, 1)));
EXPECT_EQ(rgba(0, 0, 0, 255), pal->getEntry(image->getPixel(0, 0)));
EXPECT_EQ(rgba(255, 0, 0, 255), pal->getEntry(image->getPixel(0, 1)));
EXPECT_EQ(rgba(0, 255, 0, 255), pal->getEntry(image->getPixel(1, 0)));
EXPECT_EQ(rgba(0, 0, 255, 255), pal->getEntry(image->getPixel(1, 1)));
doc->close();
delete doc;

View File

@ -24,7 +24,6 @@
#include "app/ui/skin/skin_theme.h"
#include "gfx/point.h"
#include "gfx/rect.h"
#include "doc/blend.h"
#include "doc/image.h"
#include "doc/palette.h"

View File

@ -15,7 +15,6 @@
#include "app/resource_finder.h"
#include "base/fs.h"
#include "base/path.h"
#include "doc/blend.h"
#include "doc/image.h"
#include "doc/palette.h"
#include "doc/sprite.h"

View File

@ -8,6 +8,7 @@
#include "app/modules/palettes.h"
#include "app/tools/shade_table.h"
#include "app/tools/shading_options.h"
#include "doc/blend_funcs.h"
#include "doc/image_impl.h"
#include "doc/palette.h"
#include "doc/rgbmap.h"
@ -181,7 +182,7 @@ private:
template<>
void LockAlphaInkProcessing<RgbTraits>::processPixel(int x, int y) {
color_t result = rgba_blend_normal(*m_srcAddress, m_color, m_opacity);
color_t result = rgba_blender_normal(*m_srcAddress, m_color, m_opacity);
*m_dstAddress = rgba(
rgba_getr(result),
rgba_getg(result),
@ -191,7 +192,7 @@ void LockAlphaInkProcessing<RgbTraits>::processPixel(int x, int y) {
template<>
void LockAlphaInkProcessing<GrayscaleTraits>::processPixel(int x, int y) {
color_t result = graya_blend_normal(*m_srcAddress, m_color, m_opacity);
color_t result = graya_blender_normal(*m_srcAddress, m_color, m_opacity);
*m_dstAddress = graya(
graya_getv(result),
graya_geta(*m_srcAddress));
@ -225,12 +226,12 @@ private:
template<>
void TransparentInkProcessing<RgbTraits>::processPixel(int x, int y) {
*m_dstAddress = rgba_blend_normal(*m_srcAddress, m_color, m_opacity);
*m_dstAddress = rgba_blender_normal(*m_srcAddress, m_color, m_opacity);
}
template<>
void TransparentInkProcessing<GrayscaleTraits>::processPixel(int x, int y) {
*m_dstAddress = graya_blend_normal(*m_srcAddress, m_color, m_opacity);
*m_dstAddress = graya_blender_normal(*m_srcAddress, m_color, m_opacity);
}
template<>
@ -244,7 +245,7 @@ public:
}
void processPixel(int x, int y) {
color_t c = rgba_blend_normal(m_palette->getEntry(*m_srcAddress), m_color, m_opacity);
color_t c = rgba_blender_normal(m_palette->getEntry(*m_srcAddress), m_color, m_opacity);
*m_dstAddress = m_rgbmap->mapColor(rgba_getr(c),
rgba_getg(c),
rgba_getb(c));
@ -472,7 +473,7 @@ void ReplaceInkProcessing<RgbTraits>::processPixel(int x, int y) {
if ((rgba_geta(src) == 0 && rgba_geta(m_color1) == 0) ||
(rgba_geta(src) > 0 && rgba_geta(m_color1) > 0 &&
((src & rgba_rgb_mask) == (m_color1 & rgba_rgb_mask)))) {
*m_dstAddress = rgba_blend_merge(src, m_color2, m_opacity);
*m_dstAddress = rgba_blender_merge(src, m_color2, m_opacity);
}
}
@ -483,7 +484,7 @@ void ReplaceInkProcessing<GrayscaleTraits>::processPixel(int x, int y) {
if ((graya_geta(src) == 0 && graya_geta(m_color1) == 0) ||
(graya_geta(src) > 0 && graya_geta(m_color1) > 0 &&
((src & graya_v_mask) == (m_color1 & graya_v_mask)))) {
*m_dstAddress = graya_blend_merge(src, m_color2, m_opacity);
*m_dstAddress = graya_blender_merge(src, m_color2, m_opacity);
}
}
@ -505,7 +506,7 @@ public:
if (m_opacity == 255)
*m_dstAddress = m_color2;
else {
color_t c = rgba_blend_normal(
color_t c = rgba_blender_normal(
m_palette->getEntry(*m_srcAddress), m_color2, m_opacity);
*m_dstAddress = m_rgbmap->mapColor(
@ -586,14 +587,14 @@ template<>
void JumbleInkProcessing<RgbTraits>::processPixel(int x, int y)
{
pickColorFromArea(x, y);
*m_dstAddress = rgba_blend_merge(*m_srcAddress, m_color, m_opacity);
*m_dstAddress = rgba_blender_merge(*m_srcAddress, m_color, m_opacity);
}
template<>
void JumbleInkProcessing<GrayscaleTraits>::processPixel(int x, int y)
{
pickColorFromArea(x, y);
*m_dstAddress = graya_blend_merge(*m_srcAddress, m_color, m_opacity);
*m_dstAddress = graya_blender_merge(*m_srcAddress, m_color, m_opacity);
}
template<>
@ -602,9 +603,9 @@ void JumbleInkProcessing<IndexedTraits>::processPixel(int x, int y)
pickColorFromArea(x, y);
color_t tc = (m_color != 0 ? m_palette->getEntry(m_color): 0);
color_t c = rgba_blend_merge(*m_srcAddress != 0 ?
m_palette->getEntry(*m_srcAddress): 0,
tc, m_opacity);
color_t c = rgba_blender_merge(*m_srcAddress != 0 ?
m_palette->getEntry(*m_srcAddress): 0,
tc, m_opacity);
if (rgba_geta(c) >= 128)
*m_dstAddress = m_rgbmap->mapColor(rgba_getr(c),
@ -670,12 +671,12 @@ private:
template<>
void XorInkProcessing<RgbTraits>::processPixel(int x, int y) {
*m_dstAddress = rgba_blend_blackandwhite(*m_srcAddress, m_color, 255);
*m_dstAddress = rgba_blender_neg_bw(*m_srcAddress, m_color, 255);
}
template<>
void XorInkProcessing<GrayscaleTraits>::processPixel(int x, int y) {
*m_dstAddress = graya_blend_blackandwhite(*m_srcAddress, m_color, 255);
*m_dstAddress = graya_blender_neg_bw(*m_srcAddress, m_color, 255);
}
template<>
@ -688,7 +689,7 @@ public:
}
void processPixel(int x, int y) {
color_t c = rgba_blend_blackandwhite(m_palette->getEntry(*m_srcAddress), m_color, 255);
color_t c = rgba_blender_neg_bw(m_palette->getEntry(*m_srcAddress), m_color, 255);
*m_dstAddress = m_rgbmap->mapColor(rgba_getr(c),
rgba_getg(c),
rgba_getb(c));
@ -771,7 +772,7 @@ void BrushInkProcessing<RgbTraits>::processPixel(int x, int y) {
return;
}
*m_dstAddress = rgba_blend_normal(*m_srcAddress, c, m_opacity);
*m_dstAddress = rgba_blender_normal(*m_srcAddress, c, m_opacity);
}
template<>
@ -807,7 +808,7 @@ void BrushInkProcessing<GrayscaleTraits>::processPixel(int x, int y) {
return;
}
*m_dstAddress = graya_blend_normal(*m_srcAddress, c, m_opacity);
*m_dstAddress = graya_blender_normal(*m_srcAddress, c, m_opacity);
}
template<>

View File

@ -187,7 +187,8 @@ void Editor::drawBrushPreview(const gfx::Point& pos)
cel ? cel->opacity(): 255);
m_document->setExtraCelType(render::ExtraType::NONE);
m_document->setExtraCelBlendMode(
m_layer ? static_cast<LayerImage*>(m_layer)->getBlendMode(): BLEND_MODE_NORMAL);
(m_layer ? static_cast<LayerImage*>(m_layer)->blendMode():
BlendMode::NORMAL));
// In 'indexed' images, if the current color is 0, we have to use
// a different mask color (different from 0) to draw the extra layer
@ -202,7 +203,7 @@ void Editor::drawBrushPreview(const gfx::Point& pos)
render::Render().renderLayer(
extraImage, m_layer, m_frame,
gfx::Clip(0, 0, brushBounds),
BLEND_MODE_COPY);
BlendMode::SRC);
// This extra cel is a patch for the current layer/frame
m_document->setExtraCelType(render::ExtraType::PATCH);

View File

@ -23,7 +23,6 @@
#include "app/ui/editor/scoped_cursor.h"
#include "app/ui/keyboard_shortcuts.h"
#include "app/ui_context.h"
#include "doc/blend.h"
#include "ui/message.h"
#include "ui/system.h"

View File

@ -785,13 +785,13 @@ void Editor::flashCurrentLayer()
clear_image(flash_image, flash_image->maskColor());
copy_image(flash_image, src_image, x, y);
m_document->setExtraCelBlendMode(BLEND_MODE_BLACKANDWHITE);
m_document->setExtraCelBlendMode(BlendMode::NEG_BW);
drawSpriteClipped(gfx::Region(
gfx::Rect(0, 0, m_sprite->width(), m_sprite->height())));
gui_feedback();
m_document->setExtraCelBlendMode(BLEND_MODE_NORMAL);
m_document->setExtraCelBlendMode(BlendMode::NORMAL);
m_document->destroyExtraCel();
invalidate();
}

View File

@ -74,7 +74,7 @@ PixelsMovement::PixelsMovement(Context* context,
m_document->prepareExtraCel(m_sprite->bounds(), opacity);
m_document->setExtraCelType(render::ExtraType::COMPOSITE);
m_document->setExtraCelBlendMode(
static_cast<LayerImage*>(m_layer)->getBlendMode());
static_cast<LayerImage*>(m_layer)->blendMode());
redrawExtraImage();
@ -465,7 +465,7 @@ void PixelsMovement::stampImage()
expand.getDestCanvas(), image,
-expand.getCel()->x(),
-expand.getCel()->y(),
cel->opacity(), BLEND_MODE_NORMAL);
cel->opacity(), BlendMode::NORMAL);
expand.commit();
}

View File

@ -22,7 +22,6 @@
#include "app/ui/skin/style.h"
#include "app/ui/status_bar.h"
#include "app/util/clipboard.h"
#include "doc/blend.h"
#include "doc/image.h"
#include "doc/palette.h"
#include "doc/remap.h"

View File

@ -10,7 +10,7 @@ add_library(doc-lib
algorithm/rotate.cpp
algorithm/rotsprite.cpp
algorithm/shrink_bounds.cpp
blend.cpp
blend_funcs.cpp
brush.cpp
cel.cpp
cel_data.cpp

View File

@ -12,7 +12,7 @@
#endif
#include "fixmath/fixmath.h"
#include "doc/blend.h"
#include "doc/blend_funcs.h"
#include "doc/image_impl.h"
#include "doc/primitives.h"
#include "doc/primitives_fast.h"
@ -49,11 +49,11 @@ static void image_scale_tpl(Image* dst, const Image* src, int x, int y, int w, i
}
static color_t rgba_blender(color_t back, color_t front) {
return rgba_blenders[BLEND_MODE_NORMAL](back, front, 255);
return rgba_blender_normal(back, front, 255);
}
static color_t grayscale_blender(color_t back, color_t front) {
return graya_blenders[BLEND_MODE_NORMAL](back, front, 255);
return graya_blender_normal(back, front, 255);
}
class if_blender {
@ -182,7 +182,6 @@ protected:
class RgbDelegate : public GenericDelegate<RgbTraits> {
public:
RgbDelegate(color_t mask_color) {
m_blender = rgba_blenders[BLEND_MODE_NORMAL];
m_mask_color = mask_color;
}
@ -191,20 +190,18 @@ public:
int c = spr->getPixel(spr_x, spr_y);
if ((rgba_geta(m_mask_color) == 0) || ((c & rgba_rgb_mask) != (m_mask_color & rgba_rgb_mask)))
*m_it = m_blender(*m_it, c, 255);
*m_it = rgba_blender_normal(*m_it, c, 255);
++m_it;
}
private:
BLEND_COLOR m_blender;
color_t m_mask_color;
};
class GrayscaleDelegate : public GenericDelegate<GrayscaleTraits> {
public:
GrayscaleDelegate(color_t mask_color) {
m_blender = graya_blenders[BLEND_MODE_NORMAL];
m_mask_color = mask_color;
}
@ -213,13 +210,12 @@ public:
int c = spr->getPixel(spr_x, spr_y);
if ((graya_geta(m_mask_color) == 0) || ((c & graya_v_mask) != (m_mask_color & graya_v_mask)))
*m_it = m_blender(*m_it, c, 255);
*m_it = graya_blender_normal(*m_it, c, 255);
++m_it;
}
private:
BLEND_COLOR m_blender;
color_t m_mask_color;
};

View File

@ -10,7 +10,6 @@
#include "base/unique_ptr.h"
#include "doc/algorithm/rotate.h"
#include "doc/blend.h"
#include "doc/image_impl.h"
#include "doc/primitives.h"
@ -177,7 +176,7 @@ void rotsprite_image(Image* bmp, Image* spr,
spr_copy->copy(spr, gfx::Clip(spr->bounds()));
for (int i=0; i<3; ++i) {
tmp_copy->clear(maskColor);
clear_image(tmp_copy, maskColor);
image_scale2x(tmp_copy, spr_copy, spr->width()*(1<<i), spr->height()*(1<<i));
spr_copy->copy(tmp_copy, gfx::Clip(tmp_copy->bounds()));
}

View File

@ -1,317 +0,0 @@
// Aseprite Document Library
// Copyright (c) 2001-2014 David Capello
//
// 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.h"
#include "doc/image.h"
namespace doc {
BLEND_COLOR rgba_blenders[] =
{
rgba_blend_normal,
rgba_blend_copy,
rgba_blend_merge,
rgba_blend_red_tint,
rgba_blend_blue_tint,
rgba_blend_blackandwhite,
};
BLEND_COLOR graya_blenders[] =
{
graya_blend_normal,
graya_blend_copy,
graya_blend_copy,
graya_blend_copy,
graya_blend_copy,
graya_blend_blackandwhite,
};
//////////////////////////////////////////////////////////////////////
// RGB blenders
int rgba_blend_normal(int back, int front, int opacity)
{
int t;
if ((back & 0xff000000) == 0) {
return
(front & 0xffffff) |
(INT_MULT(rgba_geta(front), opacity, t) << rgba_a_shift);
}
else if ((front & 0xff000000) == 0) {
return back;
}
else {
int B_r, B_g, B_b, B_a;
int F_r, F_g, F_b, F_a;
int D_r, D_g, D_b, D_a;
B_r = rgba_getr(back);
B_g = rgba_getg(back);
B_b = rgba_getb(back);
B_a = rgba_geta(back);
F_r = rgba_getr(front);
F_g = rgba_getg(front);
F_b = rgba_getb(front);
F_a = rgba_geta(front);
F_a = INT_MULT(F_a, opacity, t);
D_a = B_a + F_a - INT_MULT(B_a, F_a, t);
D_r = B_r + (F_r-B_r) * F_a / D_a;
D_g = B_g + (F_g-B_g) * F_a / D_a;
D_b = B_b + (F_b-B_b) * F_a / D_a;
return rgba(D_r, D_g, D_b, D_a);
}
}
int rgba_blend_copy(int back, int front, int opacity)
{
return front;
}
int rgba_blend_forpath(int back, int front, int opacity)
{
int t, F_r, F_g, F_b, F_a;
F_r = rgba_getr(front);
F_g = rgba_getg(front);
F_b = rgba_getb(front);
F_a = rgba_geta(front);
F_a = INT_MULT(F_a, opacity, t);
return rgba(F_r, F_g, F_b, F_a);
}
int rgba_blend_merge(int back, int front, int opacity)
{
int B_r, B_g, B_b, B_a;
int F_r, F_g, F_b, F_a;
int D_r, D_g, D_b, D_a;
int t;
B_r = rgba_getr(back);
B_g = rgba_getg(back);
B_b = rgba_getb(back);
B_a = rgba_geta(back);
F_r = rgba_getr(front);
F_g = rgba_getg(front);
F_b = rgba_getb(front);
F_a = rgba_geta(front);
if (B_a == 0) {
D_r = F_r;
D_g = F_g;
D_b = F_b;
}
else if (F_a == 0) {
D_r = B_r;
D_g = B_g;
D_b = B_b;
}
else {
D_r = B_r + INT_MULT((F_r - B_r), opacity, t);
D_g = B_g + INT_MULT((F_g - B_g), opacity, t);
D_b = B_b + INT_MULT((F_b - B_b), opacity, t);
}
D_a = B_a + INT_MULT((F_a - B_a), opacity, t);
if (D_a == 0)
D_r = D_g = D_b = 0;
return rgba(D_r, D_g, D_b, D_a);
}
// Part of this code comes from pixman library
// Copyright (C) 2000 Keith Packard, member of The XFree86 Project, Inc.
// 2005 Lars Knoll & Zack Rusin, Trolltech
#define ONE_HALF 0x80
#define G_SHIFT 8
#define DIV_ONE_UN8(x) \
(((x) + ONE_HALF + (((x) + ONE_HALF) >> G_SHIFT)) >> G_SHIFT)
static inline uint32_t
blend_overlay(uint32_t d, uint32_t ad, uint32_t s, uint32_t as)
{
uint32_t r;
if (2 * d < ad)
r = 2 * s * d;
else
r = as * ad - 2 * (ad - d) * (as - s);
return DIV_ONE_UN8(r);
}
int rgba_blend_color_tint(int back, int front, int opacity, int color)
{
int F_r, F_g, F_b, F_a;
int B_r, B_g, B_b, B_a;
B_r = rgba_getr(front);
B_g = rgba_getg(front);
B_b = rgba_getb(front);
B_a = rgba_geta(front);
F_r = rgba_getr(color);
F_g = rgba_getg(color);
F_b = rgba_getb(color);
F_a = rgba_geta(color);
F_r = blend_overlay(B_r, B_a, F_r, F_a);
F_g = blend_overlay(B_g, B_a, F_g, F_a);
F_b = blend_overlay(B_b, B_a, F_b, F_a);
F_a = (B_a * (~B_a) + F_a * (~F_a)) / 255;
F_a += DIV_ONE_UN8(F_a * B_a);
return rgba_blend_normal(back, rgba(F_r, F_g, F_b, F_a), opacity);
}
int rgba_blend_red_tint(int back, int front, int opacity)
{
return rgba_blend_color_tint(back, front, opacity, rgba(255, 0, 0, 128));
}
int rgba_blend_blue_tint(int back, int front, int opacity)
{
return rgba_blend_color_tint(back, front, opacity, rgba(0, 0, 255, 128));
}
int rgba_blend_blackandwhite(int back, int front, int opacity)
{
int B_r, B_g, B_b, B_a;
int D_v;
B_a = rgba_geta(back);
if (B_a == 0)
return front;
B_r = rgba_getr(back);
B_g = rgba_getg(back);
B_b = rgba_getb(back);
if ((B_r*30 + B_g*59 + B_b*11)/100 < 128)
D_v = 255;
else
D_v = 0;
return rgba(D_v, D_v, D_v, 255);
}
//////////////////////////////////////////////////////////////////////
// Grayscale blenders
int graya_blend_normal(int back, int front, int opacity)
{
int t;
if ((back & 0xff00) == 0) {
return
(front & 0xff) |
(INT_MULT(graya_geta (front), opacity, t) << graya_a_shift);
}
else if ((front & 0xff00) == 0) {
return back;
}
else {
int B_g, B_a;
int F_g, F_a;
int D_g, D_a;
B_g = graya_getv(back);
B_a = graya_geta(back);
F_g = graya_getv(front);
F_a = graya_geta(front);
F_a = INT_MULT(F_a, opacity, t);
D_a = B_a + F_a - INT_MULT(B_a, F_a, t);
D_g = B_g + (F_g-B_g) * F_a / D_a;
return graya(D_g, D_a);
}
}
int graya_blend_copy(int back, int front, int opacity)
{
return front;
}
int graya_blend_forpath(int back, int front, int opacity)
{
int t, F_k, F_a;
F_k = graya_getv(front);
F_a = graya_geta(front);
F_a = INT_MULT(F_a, opacity, t);
return graya(F_k, F_a);
}
int graya_blend_merge(int back, int front, int opacity)
{
int B_k, B_a;
int F_k, F_a;
int D_k, D_a;
int t;
B_k = graya_getv(back);
B_a = graya_geta(back);
F_k = graya_getv(front);
F_a = graya_geta(front);
if (B_a == 0) {
D_k = F_k;
}
else if (F_a == 0) {
D_k = B_k;
}
else {
D_k = B_k + INT_MULT((F_k-B_k), opacity, t);
}
D_a = B_a + INT_MULT((F_a-B_a), opacity, t);
if (D_a == 0)
D_k = 0;
return graya(D_k, D_a);
}
int graya_blend_blackandwhite(int back, int front, int opacity)
{
int B_k, B_a;
int D_k;
B_a = graya_geta(back);
if (B_a == 0)
return front;
B_k = graya_getv(back);
if (B_k < 128)
D_k = 255;
else
D_k = 0;
return graya(D_k, 255);
}
//////////////////////////////////////////////////////////////////////
// Indexed blenders
int indexed_blend_direct(int back, int front, int opacity)
{
return front;
}
} // namespace doc

View File

@ -1,49 +0,0 @@
// Aseprite Document Library
// Copyright (c) 2001-2014 David Capello
//
// This file is released under the terms of the MIT license.
// Read LICENSE.txt for more information.
#ifndef DOC_BLEND_H_INCLUDED
#define DOC_BLEND_H_INCLUDED
#pragma once
#define INT_MULT(a, b, t) \
((t) = (a) * (b) + 0x80, ((((t) >> 8) + (t)) >> 8))
namespace doc {
enum {
BLEND_MODE_NORMAL,
BLEND_MODE_COPY,
BLEND_MODE_MERGE,
BLEND_MODE_RED_TINT,
BLEND_MODE_BLUE_TINT,
BLEND_MODE_BLACKANDWHITE,
BLEND_MODE_MAX,
};
typedef int (*BLEND_COLOR)(int back, int front, int opacity);
extern BLEND_COLOR rgba_blenders[];
extern BLEND_COLOR graya_blenders[];
int rgba_blend_normal(int back, int front, int opacity);
int rgba_blend_copy(int back, int front, int opacity);
int rgba_blend_forpath(int back, int front, int opacity);
int rgba_blend_merge(int back, int front, int opacity);
int rgba_blend_red_tint(int back, int front, int opacity);
int rgba_blend_blue_tint(int back, int front, int opacity);
int rgba_blend_blackandwhite(int back, int front, int opacity);
int graya_blend_normal(int back, int front, int opacity);
int graya_blend_copy(int back, int front, int opacity);
int graya_blend_forpath(int back, int front, int opacity);
int graya_blend_merge(int back, int front, int opacity);
int graya_blend_blackandwhite(int back, int front, int opacity);
int indexed_blend_direct(int back, int front, int opacity);
} // namespace doc
#endif

661
src/doc/blend_funcs.cpp Normal file
View File

@ -0,0 +1,661 @@
// Aseprite Document Library
// Copyright (c) 2001-2015 David Capello
//
// This file is released under the terms of the MIT license.
// Read LICENSE.txt for more information.
//
// --
//
// Some references about alpha compositing and blend modes:
//
// http://dev.w3.org/fxtf/compositing-1/
// http://www.adobe.com/devnet/pdf/pdf_reference.html
//
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "doc/blend_funcs.h"
#include "doc/blend_internals.h"
#include <cmath>
namespace {
#define blend_multiply(b, s, t) (MUL_UN8((b), (s), (t)))
#define blend_screen(b, s, t) ((b) + (s) - MUL_UN8((b), (s), (t)))
#define blend_overlay(b, s, t) (blend_hard_light(s, b, t))
#define blend_darken(b, s) (MIN((b), (s)))
#define blend_lighten(b, s) (MAX((b), (s)))
#define blend_hard_light(b, s, t) ((s) < 128 ? \
blend_multiply((b), (s)<<1, (t)): \
blend_screen((b), ((s)<<1)-255, (t)))
#define blend_difference(b, s) (ABS((b) - (s)))
#define blend_exclusion(b, s, t) ((t) = MUL_UN8((b), (s), (t)), ((b) + (s) - 2*(t)))
inline uint32_t blend_color_dodge(uint32_t b, uint32_t s)
{
if (b == 0)
return 0;
s = (255 - s);
if (b >= s)
return 255;
else
return DIV_UN8(b, s); // return b / (1-s)
}
inline uint32_t blend_color_burn(uint32_t b, uint32_t s)
{
if (b == 255)
return 255;
b = (255 - b);
if (b >= s)
return 0;
else
return 255 - DIV_UN8(b, s); // return 1 - ((1-b)/s)
}
inline uint32_t blend_soft_light(uint32_t _b, uint32_t _s)
{
double b = _b / 255.0;
double s = _s / 255.0;
double r, d;
if (b <= 0.25)
d = ((16*b-12)*b+4)*b;
else
d = std::sqrt(b);
if (s <= 0.5)
r = b - (1.0 - 2.0*s) * b * (1.0 - b);
else
r = b - (1.0 - 2.0*s) * b * (1.0 - b);
return (uint32_t)(r * 255 + 0.5);
}
} // annonymous namespace
namespace doc {
//////////////////////////////////////////////////////////////////////
// RGB blenders
color_t rgba_blender_src(color_t backdrop, color_t src, int opacity)
{
return src;
}
color_t rgba_blender_merge(color_t backdrop, color_t src, int opacity)
{
int Br, Bg, Bb, Ba;
int Sr, Sg, Sb, Sa;
int Rr, Rg, Rb, Ra;
int t;
Br = rgba_getr(backdrop);
Bg = rgba_getg(backdrop);
Bb = rgba_getb(backdrop);
Ba = rgba_geta(backdrop);
Sr = rgba_getr(src);
Sg = rgba_getg(src);
Sb = rgba_getb(src);
Sa = rgba_geta(src);
if (Ba == 0) {
Rr = Sr;
Rg = Sg;
Rb = Sb;
}
else if (Sa == 0) {
Rr = Br;
Rg = Bg;
Rb = Bb;
}
else {
Rr = Br + MUL_UN8((Sr - Br), opacity, t);
Rg = Bg + MUL_UN8((Sg - Bg), opacity, t);
Rb = Bb + MUL_UN8((Sb - Bb), opacity, t);
}
Ra = Ba + MUL_UN8((Sa - Ba), opacity, t);
if (Ra == 0)
Rr = Rg = Rb = 0;
return rgba(Rr, Rg, Rb, Ra);
}
color_t rgba_blender_neg_bw(color_t backdrop, color_t src, int opacity)
{
if (!(backdrop & rgba_a_mask))
return src;
else if (rgba_luma(backdrop) < 128)
return rgba(255, 255, 255, 255);
else
return rgba(0, 0, 0, 255);
}
color_t rgba_blender_red_tint(color_t backdrop, color_t src, int opacity)
{
int v = rgba_luma(src);
src = rgba(v, 0, 0, rgba_geta(src));
return rgba_blender_normal(backdrop, src, opacity);
}
color_t rgba_blender_blue_tint(color_t backdrop, color_t src, int opacity)
{
int v = rgba_luma(src);
src = rgba(0, 0, v, rgba_geta(src));
return rgba_blender_normal(backdrop, src, opacity);
}
color_t rgba_blender_normal(color_t backdrop, color_t src, int opacity)
{
int t;
if ((backdrop & rgba_a_mask) == 0) {
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) {
return backdrop;
}
int Br, Bg, Bb, Ba;
int Sr, Sg, Sb, Sa;
int Rr, Rg, Rb, Ra;
Br = rgba_getr(backdrop);
Bg = rgba_getg(backdrop);
Bb = rgba_getb(backdrop);
Ba = rgba_geta(backdrop);
Sr = rgba_getr(src);
Sg = rgba_getg(src);
Sb = rgba_getb(src);
Sa = rgba_geta(src);
Sa = MUL_UN8(Sa, opacity, t);
Ra = Ba + Sa - MUL_UN8(Ba, Sa, t);
Rr = Br + (Sr-Br) * Sa / Ra;
Rg = Bg + (Sg-Bg) * Sa / Ra;
Rb = Bb + (Sb-Bb) * Sa / Ra;
return rgba(Rr, Rg, Rb, Ra);
}
color_t rgba_blender_multiply(color_t backdrop, color_t src, int opacity)
{
int t;
int r = blend_multiply(rgba_getr(backdrop), rgba_getr(src), t);
int g = blend_multiply(rgba_getg(backdrop), rgba_getg(src), t);
int b = blend_multiply(rgba_getb(backdrop), rgba_getb(src), t);
src = rgba(r, g, b, 0) | (src & rgba_a_mask);
return rgba_blender_normal(backdrop, src, opacity);
}
color_t rgba_blender_screen(color_t backdrop, color_t src, int opacity)
{
int t;
int r = blend_screen(rgba_getr(backdrop), rgba_getr(src), t);
int g = blend_screen(rgba_getg(backdrop), rgba_getg(src), t);
int b = blend_screen(rgba_getb(backdrop), rgba_getb(src), t);
src = rgba(r, g, b, 0) | (src & rgba_a_mask);
return rgba_blender_normal(backdrop, src, opacity);
}
color_t rgba_blender_overlay(color_t backdrop, color_t src, int opacity)
{
int t;
int r = blend_overlay(rgba_getr(backdrop), rgba_getr(src), t);
int g = blend_overlay(rgba_getg(backdrop), rgba_getg(src), t);
int b = blend_overlay(rgba_getb(backdrop), rgba_getb(src), t);
src = rgba(r, g, b, 0) | (src & rgba_a_mask);
return rgba_blender_normal(backdrop, src, opacity);
}
color_t rgba_blender_darken(color_t backdrop, color_t src, int opacity)
{
int r = blend_darken(rgba_getr(backdrop), rgba_getr(src));
int g = blend_darken(rgba_getg(backdrop), rgba_getg(src));
int b = blend_darken(rgba_getb(backdrop), rgba_getb(src));
src = rgba(r, g, b, 0) | (src & rgba_a_mask);
return rgba_blender_normal(backdrop, src, opacity);
}
color_t rgba_blender_lighten(color_t backdrop, color_t src, int opacity)
{
int r = blend_lighten(rgba_getr(backdrop), rgba_getr(src));
int g = blend_lighten(rgba_getg(backdrop), rgba_getg(src));
int b = blend_lighten(rgba_getb(backdrop), rgba_getb(src));
src = rgba(r, g, b, 0) | (src & rgba_a_mask);
return rgba_blender_normal(backdrop, src, opacity);
}
color_t rgba_blender_color_dodge(color_t backdrop, color_t src, int opacity)
{
int r = blend_color_dodge(rgba_getr(backdrop), rgba_getr(src));
int g = blend_color_dodge(rgba_getg(backdrop), rgba_getg(src));
int b = blend_color_dodge(rgba_getb(backdrop), rgba_getb(src));
src = rgba(r, g, b, 0) | (src & rgba_a_mask);
return rgba_blender_normal(backdrop, src, opacity);
}
color_t rgba_blender_color_burn(color_t backdrop, color_t src, int opacity)
{
int r = blend_color_burn(rgba_getr(backdrop), rgba_getr(src));
int g = blend_color_burn(rgba_getg(backdrop), rgba_getg(src));
int b = blend_color_burn(rgba_getb(backdrop), rgba_getb(src));
src = rgba(r, g, b, 0) | (src & rgba_a_mask);
return rgba_blender_normal(backdrop, src, opacity);
}
color_t rgba_blender_hard_light(color_t backdrop, color_t src, int opacity)
{
int t;
int r = blend_hard_light(rgba_getr(backdrop), rgba_getr(src), t);
int g = blend_hard_light(rgba_getg(backdrop), rgba_getg(src), t);
int b = blend_hard_light(rgba_getb(backdrop), rgba_getb(src), t);
src = rgba(r, g, b, 0) | (src & rgba_a_mask);
return rgba_blender_normal(backdrop, src, opacity);
}
color_t rgba_blender_soft_light(color_t backdrop, color_t src, int opacity)
{
int r = blend_soft_light(rgba_getr(backdrop), rgba_getr(src));
int g = blend_soft_light(rgba_getg(backdrop), rgba_getg(src));
int b = blend_soft_light(rgba_getb(backdrop), rgba_getb(src));
src = rgba(r, g, b, 0) | (src & rgba_a_mask);
return rgba_blender_normal(backdrop, src, opacity);
}
color_t rgba_blender_difference(color_t backdrop, color_t src, int opacity)
{
int r = blend_difference(rgba_getr(backdrop), rgba_getr(src));
int g = blend_difference(rgba_getg(backdrop), rgba_getg(src));
int b = blend_difference(rgba_getb(backdrop), rgba_getb(src));
src = rgba(r, g, b, 0) | (src & rgba_a_mask);
return rgba_blender_normal(backdrop, src, opacity);
}
color_t rgba_blender_exclusion(color_t backdrop, color_t src, int opacity)
{
int t;
int r = blend_exclusion(rgba_getr(backdrop), rgba_getr(src), t);
int g = blend_exclusion(rgba_getg(backdrop), rgba_getg(src), t);
int b = blend_exclusion(rgba_getb(backdrop), rgba_getb(src), t);
src = rgba(r, g, b, 0) | (src & rgba_a_mask);
return rgba_blender_normal(backdrop, src, opacity);
}
//////////////////////////////////////////////////////////////////////
// HSV blenders
static double lum(double r, double g, double b)
{
return 0.3*r + 0.59*g + 0.11*b;
}
static double sat(double r, double g, double b)
{
return MAX(r, MAX(g, b)) - MIN(r, MIN(g, b));
}
static void clip_color(double& r, double& g, double& b)
{
double l = lum(r, g, b);
double n = MIN(r, MIN(g, b));
double x = MAX(r, MAX(g, b));
if (n < 0) {
r = l + (((r - l) * l) / (l - n));
g = l + (((g - l) * l) / (l - n));
b = l + (((b - l) * l) / (l - n));
}
if (x > 1) {
r = l + (((r - l) * (1 - l)) / (x - l));
g = l + (((g - l) * (1 - l)) / (x - l));
b = l + (((b - l) * (1 - l)) / (x - l));
}
}
static void set_lum(double& r, double& g, double& b, double l)
{
double d = l - lum(r, g, b);
r += d;
g += d;
b += d;
clip_color(r, g, b);
}
static void set_sat(double& r, double& g, double& b, double s)
{
double& min = MIN(r, MIN(g, b));
double& mid = MID(r, g, b);
double& max = MAX(r, MAX(g, b));
if (max > min) {
mid = ((mid - min)*s) / (max - min);
max = s;
}
else
mid = max = 0;
min = 0;
}
color_t rgba_blender_hsl_hue(color_t backdrop, color_t src, int opacity)
{
double r = rgba_getr(backdrop)/255.0;
double g = rgba_getg(backdrop)/255.0;
double b = rgba_getb(backdrop)/255.0;
double s = sat(r, g, b);
double l = lum(r, g, b);
r = rgba_getr(src)/255.0;
g = rgba_getg(src)/255.0;
b = rgba_getb(src)/255.0;
set_sat(r, g, b, s);
set_lum(r, g, b, l);
src = rgba(int(255.0*r), int(255.0*g), int(255.0*b), 0) | (src & rgba_a_mask);
return rgba_blender_normal(backdrop, src, opacity);
}
color_t rgba_blender_hsl_saturation(color_t backdrop, color_t src, int opacity)
{
double r = rgba_getr(src)/255.0;
double g = rgba_getg(src)/255.0;
double b = rgba_getb(src)/255.0;
double s = sat(r, g, b);
r = rgba_getr(backdrop)/255.0;
g = rgba_getg(backdrop)/255.0;
b = rgba_getb(backdrop)/255.0;
double l = lum(r, g, b);
set_sat(r, g, b, s);
set_lum(r, g, b, l);
src = rgba(int(255.0*r), int(255.0*g), int(255.0*b), 0) | (src & rgba_a_mask);
return rgba_blender_normal(backdrop, src, opacity);
}
color_t rgba_blender_hsl_color(color_t backdrop, color_t src, int opacity)
{
double r = rgba_getr(backdrop)/255.0;
double g = rgba_getg(backdrop)/255.0;
double b = rgba_getb(backdrop)/255.0;
double l = lum(r, g, b);
r = rgba_getr(src)/255.0;
g = rgba_getg(src)/255.0;
b = rgba_getb(src)/255.0;
set_lum(r, g, b, l);
src = rgba(int(255.0*r), int(255.0*g), int(255.0*b), 0) | (src & rgba_a_mask);
return rgba_blender_normal(backdrop, src, opacity);
}
color_t rgba_blender_hsl_luminosity(color_t backdrop, color_t src, int opacity)
{
double r = rgba_getr(src)/255.0;
double g = rgba_getg(src)/255.0;
double b = rgba_getb(src)/255.0;
double l = lum(r, g, b);
r = rgba_getr(backdrop)/255.0;
g = rgba_getg(backdrop)/255.0;
b = rgba_getb(backdrop)/255.0;
set_lum(r, g, b, l);
src = rgba(int(255.0*r), int(255.0*g), int(255.0*b), 0) | (src & rgba_a_mask);
return rgba_blender_normal(backdrop, src, opacity);
}
//////////////////////////////////////////////////////////////////////
// GRAY blenders
color_t graya_blender_src(color_t backdrop, color_t src, int opacity)
{
return src;
}
color_t graya_blender_merge(color_t backdrop, color_t src, int opacity)
{
int Bk, Ba;
int Sk, Sa;
int Rk, Ra;
int t;
Bk = graya_getv(backdrop);
Ba = graya_geta(backdrop);
Sk = graya_getv(src);
Sa = graya_geta(src);
if (Ba == 0) {
Rk = Sk;
}
else if (Sa == 0) {
Rk = Bk;
}
else {
Rk = Bk + MUL_UN8((Sk-Bk), opacity, t);
}
Ra = Ba + MUL_UN8((Sa-Ba), opacity, t);
if (Ra == 0)
Rk = 0;
return graya(Rk, Ra);
}
color_t graya_blender_neg_bw(color_t backdrop, color_t src, int opacity)
{
if ((backdrop & graya_a_mask) == 0)
return src;
else if (graya_getv(backdrop) < 128)
return graya(255, 255);
else
return graya(0, 255);
}
color_t graya_blender_normal(color_t backdrop, color_t src, int opacity)
{
int t;
if ((backdrop & graya_a_mask) == 0) {
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)
return backdrop;
int Bg, Ba;
int Sg, Sa;
int Rg, Ra;
Bg = graya_getv(backdrop);
Ba = graya_geta(backdrop);
Sg = graya_getv(src);
Sa = graya_geta(src);
Sa = MUL_UN8(Sa, opacity, t);
Ra = Ba + Sa - MUL_UN8(Ba, Sa, t);
Rg = Bg + (Sg-Bg) * Sa / Ra;
return graya(Rg, Ra);
}
color_t graya_blender_multiply(color_t backdrop, color_t src, int opacity)
{
int t;
int v = blend_multiply(graya_getv(backdrop), graya_getv(src), t);
src = graya(v, 0) | (src & graya_a_mask);
return graya_blender_normal(backdrop, src, opacity);
}
color_t graya_blender_screen(color_t backdrop, color_t src, int opacity)
{
int t;
int v = blend_screen(graya_getv(backdrop), graya_getv(src), t);
src = graya(v, 0) | (src & graya_a_mask);
return graya_blender_normal(backdrop, src, opacity);
}
color_t graya_blender_overlay(color_t backdrop, color_t src, int opacity)
{
int t;
int v = blend_overlay(graya_getv(backdrop), graya_getv(src), t);
src = graya(v, 0) | (src & graya_a_mask);
return graya_blender_normal(backdrop, src, opacity);
}
color_t graya_blender_darken(color_t backdrop, color_t src, int opacity)
{
int v = blend_darken(graya_getv(backdrop), graya_getv(src));
src = graya(v, 0) | (src & graya_a_mask);
return graya_blender_normal(backdrop, src, opacity);
}
color_t graya_blender_lighten(color_t backdrop, color_t src, int opacity)
{
int v = blend_lighten(graya_getv(backdrop), graya_getv(src));
src = graya(v, 0) | (src & graya_a_mask);
return graya_blender_normal(backdrop, src, opacity);
}
color_t graya_blender_color_dodge(color_t backdrop, color_t src, int opacity)
{
int v = blend_color_dodge(graya_getv(backdrop), graya_getv(src));
src = graya(v, 0) | (src & graya_a_mask);
return graya_blender_normal(backdrop, src, opacity);
}
color_t graya_blender_color_burn(color_t backdrop, color_t src, int opacity)
{
int v = blend_color_burn(graya_getv(backdrop), graya_getv(src));
src = graya(v, 0) | (src & graya_a_mask);
return graya_blender_normal(backdrop, src, opacity);
}
color_t graya_blender_hard_light(color_t backdrop, color_t src, int opacity)
{
int t;
int v = blend_hard_light(graya_getv(backdrop), graya_getv(src), t);
src = graya(v, 0) | (src & graya_a_mask);
return graya_blender_normal(backdrop, src, opacity);
}
color_t graya_blender_soft_light(color_t backdrop, color_t src, int opacity)
{
int v = blend_soft_light(graya_getv(backdrop), graya_getv(src));
src = graya(v, 0) | (src & graya_a_mask);
return graya_blender_normal(backdrop, src, opacity);
}
color_t graya_blender_difference(color_t backdrop, color_t src, int opacity)
{
int v = blend_difference(graya_getv(backdrop), graya_getv(src));
src = graya(v, 0) | (src & graya_a_mask);
return graya_blender_normal(backdrop, src, opacity);
}
color_t graya_blender_exclusion(color_t backdrop, color_t src, int opacity)
{
int t;
int v = blend_exclusion(graya_getv(backdrop), graya_getv(src), t);
src = graya(v, 0) | (src & graya_a_mask);
return graya_blender_normal(backdrop, src, opacity);
}
//////////////////////////////////////////////////////////////////////
// indexed
color_t indexed_blender_src(color_t dst, color_t src, int opacity)
{
return src;
}
//////////////////////////////////////////////////////////////////////
// getters
BlendFunc get_rgba_blender(BlendMode blendmode)
{
switch (blendmode) {
case BlendMode::SRC: return rgba_blender_src;
case BlendMode::MERGE: return rgba_blender_merge;
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::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;
}
ASSERT(false);
return rgba_blender_src;
}
BlendFunc get_graya_blender(BlendMode blendmode)
{
switch (blendmode) {
case BlendMode::SRC: return graya_blender_src;
case BlendMode::MERGE: return graya_blender_merge;
case BlendMode::NEG_BW: return graya_blender_neg_bw;
case BlendMode::RED_TINT: return graya_blender_normal;
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::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;
}
ASSERT(false);
return graya_blender_src;
}
BlendFunc get_indexed_blender(BlendMode blendmode)
{
return indexed_blender_src;
}
} // namespace doc

32
src/doc/blend_funcs.h Normal file
View File

@ -0,0 +1,32 @@
// Aseprite Document Library
// Copyright (c) 2001-2015 David Capello
//
// This file is released under the terms of the MIT license.
// Read LICENSE.txt for more information.
#ifndef DOC_BLEND_FUNCS_H_INCLUDED
#define DOC_BLEND_FUNCS_H_INCLUDED
#pragma once
#include "doc/blend_mode.h"
#include "doc/color.h"
namespace doc {
typedef color_t (*BlendFunc)(color_t backdrop, color_t src, int opacity);
color_t rgba_blender_normal(color_t backdrop, color_t src, int opacity);
color_t rgba_blender_merge(color_t backdrop, color_t src, int opacity);
color_t rgba_blender_neg_bw(color_t backdrop, color_t src, int opacity);
color_t graya_blender_normal(color_t backdrop, color_t src, int opacity);
color_t graya_blender_merge(color_t backdrop, color_t src, int opacity);
color_t graya_blender_neg_bw(color_t backdrop, color_t src, int opacity);
BlendFunc get_rgba_blender(BlendMode blendmode);
BlendFunc get_graya_blender(BlendMode blendmode);
BlendFunc get_indexed_blender(BlendMode blendmode);
} // namespace doc
#endif

17
src/doc/blend_internals.h Normal file
View File

@ -0,0 +1,17 @@
// Aseprite Document Library
// Copyright (c) 2001-2015 David Capello
//
// This file is released under the terms of the MIT license.
// Read LICENSE.txt for more information.
#ifndef DOC_BLEND_INTERNALS_H_INCLUDED
#define DOC_BLEND_INTERNALS_H_INCLUDED
#pragma once
#include "../../third_party/pixman/pixman/pixman-combine32.h"
#if !defined(MUL_UN8) || !defined(DIV_UN8)
#error Invalid Pixman library
#endif
#endif

43
src/doc/blend_mode.h Normal file
View File

@ -0,0 +1,43 @@
// Aseprite Document Library
// Copyright (c) 2001-2015 David Capello
//
// This file is released under the terms of the MIT license.
// Read LICENSE.txt for more information.
#ifndef DOC_BLEND_MODE_H_INCLUDED
#define DOC_BLEND_MODE_H_INCLUDED
#pragma once
namespace doc {
enum class BlendMode {
// Special internal/undocumented alpha compositing and blend modes
UNSPECIFIED = -1,
SRC = -2,
MERGE = -3,
NEG_BW = -4, // Negative Black & White
RED_TINT = -5,
BLUE_TINT = -6,
// Aseprite (.ase files) blend modes
NORMAL = 0,
MULTIPLY = 1,
SCREEN = 2,
OVERLAY = 3,
DARKEN = 4,
LIGHTEN = 5,
COLOR_DODGE = 6,
COLOR_BURN = 7,
HARD_LIGHT = 8,
SOFT_LIGHT = 9,
DIFFERENCE = 10,
EXCLUSION = 11,
HSL_HUE = 12,
HSL_SATURATION = 13,
HSL_COLOR = 14,
HSL_LUMINOSITY = 15
};
} // namespace doc
#endif

View File

@ -1,5 +1,5 @@
// Aseprite Document Library
// Copyright (c) 2001-2014 David Capello
// Copyright (c) 2001-2015 David Capello
//
// This file is released under the terms of the MIT license.
// Read LICENSE.txt for more information.
@ -51,8 +51,12 @@ namespace doc {
(a << rgba_a_shift));
}
inline uint32_t rgb(uint8_t r, uint8_t g, uint8_t b) {
return rgba(0, 0, 0, 255);
inline int rgb_luma(int r, int g, int b) {
return (r*2126 + g*7152 + b*722) / 10000;
}
inline uint8_t rgba_luma(uint32_t c) {
return rgb_luma(rgba_getr(c), rgba_getg(c), rgba_getb(c));
}
//////////////////////////////////////////////////////////////////////

View File

@ -12,7 +12,6 @@
#include "base/24bits.h"
#include "doc/algo.h"
#include "doc/blend.h"
#include "doc/color_scales.h"
#include "doc/image_impl.h"
#include "doc/palette.h"

View File

@ -8,7 +8,8 @@
#define DOC_DOC_H_INCLUDED
#pragma once
#include "doc/blend.h"
#include "doc/blend_funcs.h"
#include "doc/blend_mode.h"
#include "doc/brush.h"
#include "doc/cel.h"
#include "doc/color.h"

View File

@ -1,5 +1,5 @@
// Aseprite Document Library
// Copyright (c) 2001-2014 David Capello
// Copyright (c) 2001-2015 David Capello
//
// This file is released under the terms of the MIT license.
// Read LICENSE.txt for more information.
@ -11,7 +11,6 @@
#include "doc/image.h"
#include "doc/algo.h"
#include "doc/blend.h"
#include "doc/brush.h"
#include "doc/image_impl.h"
#include "doc/palette.h"

View File

@ -1,5 +1,5 @@
// Aseprite Document Library
// Copyright (c) 2001-2014 David Capello
// Copyright (c) 2001-2015 David Capello
//
// This file is released under the terms of the MIT license.
// Read LICENSE.txt for more information.
@ -8,7 +8,6 @@
#define DOC_IMAGE_H_INCLUDED
#pragma once
#include "doc/blend.h"
#include "doc/color.h"
#include "doc/image_buffer.h"
#include "doc/object.h"

View File

@ -11,7 +11,7 @@
#include <cstdlib>
#include <cstring>
#include "doc/blend.h"
#include "doc/blend_funcs.h"
#include "doc/image.h"
#include "doc/image_bits.h"
#include "doc/image_iterator.h"
@ -248,7 +248,7 @@ namespace doc {
for (y=y1; y<=y2; ++y) {
addr = (address_t)getPixelAddress(x1, y);
for (x=x1; x<=x2; ++x) {
*addr = rgba_blend_normal(*addr, color, opacity);
*addr = rgba_blender_normal(*addr, color, opacity);
++addr;
}
}

View File

@ -8,6 +8,7 @@
#define DOC_IMAGE_TRAITS_H_INCLUDED
#pragma once
#include "doc/blend_funcs.h"
#include "doc/pixel_format.h"
namespace doc {
@ -30,15 +31,12 @@ namespace doc {
static const pixel_t min_value = 0x00000000l;
static const pixel_t max_value = 0xffffffffl;
static inline int getRowStrideBytes(int pixels_per_row)
{
static inline int getRowStrideBytes(int pixels_per_row) {
return bytes_per_pixel * pixels_per_row;
}
static inline BLEND_COLOR get_blender(int blend_mode)
{
ASSERT(blend_mode >= 0 && blend_mode < BLEND_MODE_MAX);
return rgba_blenders[blend_mode];
static inline BlendFunc get_blender(BlendMode blend_mode) {
return get_rgba_blender(blend_mode);
}
};
@ -60,15 +58,12 @@ namespace doc {
static const pixel_t min_value = 0x0000;
static const pixel_t max_value = 0xffff;
static inline int getRowStrideBytes(int pixels_per_row)
{
static inline int getRowStrideBytes(int pixels_per_row) {
return bytes_per_pixel * pixels_per_row;
}
static inline BLEND_COLOR get_blender(int blend_mode)
{
ASSERT(blend_mode >= 0 && blend_mode < BLEND_MODE_MAX);
return graya_blenders[blend_mode];
static inline BlendFunc get_blender(BlendMode blend_mode) {
return get_graya_blender(blend_mode);
}
};
@ -90,14 +85,12 @@ namespace doc {
static const pixel_t min_value = 0x00;
static const pixel_t max_value = 0xff;
static inline int getRowStrideBytes(int pixels_per_row)
{
static inline int getRowStrideBytes(int pixels_per_row) {
return bytes_per_pixel * pixels_per_row;
}
static inline BLEND_COLOR get_blender(int blend_mode)
{
return indexed_blend_direct;
static inline BlendFunc get_blender(BlendMode blend_mode) {
return get_indexed_blender(blend_mode);
}
};
@ -119,8 +112,7 @@ namespace doc {
static const pixel_t min_value = 0;
static const pixel_t max_value = 1;
static inline int getRowStrideBytes(int pixels_per_row)
{
static inline int getRowStrideBytes(int pixels_per_row) {
return ((pixels_per_row+7) / 8);
}
};

View File

@ -10,7 +10,6 @@
#include "doc/layer.h"
#include "doc/blend.h"
#include "doc/cel.h"
#include "doc/image.h"
#include "doc/primitives.h"
@ -85,6 +84,7 @@ Cel* Layer::cel(frame_t frame) const
LayerImage::LayerImage(Sprite* sprite)
: Layer(ObjectType::LayerImage, sprite)
, m_blendmode(BlendMode::NORMAL)
{
}

View File

@ -8,7 +8,7 @@
#define DOC_LAYER_H_INCLUDED
#pragma once
#include "doc/blend.h"
#include "doc/blend_mode.h"
#include "doc/cel_list.h"
#include "doc/frame.h"
#include "doc/layer_list.h"
@ -116,7 +116,8 @@ namespace doc {
virtual int getMemSize() const override;
int getBlendMode() const { return BLEND_MODE_NORMAL; }
BlendMode blendMode() const { return m_blendmode; }
void setBlendMode(BlendMode blendmode) { m_blendmode = blendmode; }
void addCel(Cel *cel);
void removeCel(Cel *cel);
@ -137,6 +138,7 @@ namespace doc {
private:
void destroyAllCels();
BlendMode m_blendmode;
CelList m_cels; // List of all cels inside this layer used by frames.
};

View File

@ -10,7 +10,6 @@
#include "config.h"
#endif
#include "doc/blend.h"
#include "doc/image.h"
#include "doc/path.h"

View File

@ -11,7 +11,6 @@
#include "doc/primitives.h"
#include "doc/algo.h"
#include "doc/blend.h"
#include "doc/brush.h"
#include "doc/image_impl.h"
#include "doc/palette.h"

View File

@ -100,8 +100,8 @@ struct PalEntryWithIndexPredicate {
}
case SortPaletteBy::LUMA: {
value1 = (rgba_getr(c1)*299 + rgba_getg(c1)*587 + rgba_getb(c1)*114); // do not /1000 (so we get more precission)
value2 = (rgba_getr(c2)*299 + rgba_getg(c2)*587 + rgba_getb(c2)*114);
value1 = rgb_luma(rgba_getr(c1), rgba_getg(c1), rgba_getb(c1));
value2 = rgb_luma(rgba_getr(c2), rgba_getg(c2), rgba_getb(c2));
break;
}

View File

@ -10,7 +10,6 @@
#include "render/quantization.h"
#include "doc/blend.h"
#include "doc/image_impl.h"
#include "doc/images_collector.h"
#include "doc/layer.h"

View File

@ -10,6 +10,8 @@
#include "render/render.h"
#include "doc/blend_internals.h"
#include "doc/blend_mode.h"
#include "doc/doc.h"
#include "doc/handle_anidir.h"
#include "doc/image_impl.h"
@ -23,12 +25,12 @@ namespace render {
template<class DstTraits, class SrcTraits>
class BlenderHelper {
BLEND_COLOR m_blend_color;
BlendFunc m_blend_func;
color_t m_mask_color;
public:
BlenderHelper(const Image* src, const Palette* pal, int blend_mode)
BlenderHelper(const Image* src, const Palette* pal, BlendMode blend_mode)
{
m_blend_color = SrcTraits::get_blender(blend_mode);
m_blend_func = SrcTraits::get_blender(blend_mode);
m_mask_color = src->maskColor();
}
inline void operator()(typename DstTraits::pixel_t& scanline,
@ -37,7 +39,7 @@ public:
int opacity)
{
if (src != m_mask_color)
scanline = (*m_blend_color)(dst, src, opacity);
scanline = (*m_blend_func)(dst, src, opacity);
else
scanline = dst;
}
@ -45,12 +47,12 @@ public:
template<>
class BlenderHelper<RgbTraits, GrayscaleTraits> {
BLEND_COLOR m_blend_color;
BlendFunc m_blend_func;
color_t m_mask_color;
public:
BlenderHelper(const Image* src, const Palette* pal, int blend_mode)
BlenderHelper(const Image* src, const Palette* pal, BlendMode blend_mode)
{
m_blend_color = RgbTraits::get_blender(blend_mode);
m_blend_func = RgbTraits::get_blender(blend_mode);
m_mask_color = src->maskColor();
}
inline void operator()(RgbTraits::pixel_t& scanline,
@ -60,7 +62,7 @@ public:
{
if (src != m_mask_color) {
int v = graya_getv(src);
scanline = (*m_blend_color)(dst, rgba(v, v, v, graya_geta(src)), opacity);
scanline = (*m_blend_func)(dst, rgba(v, v, v, graya_geta(src)), opacity);
}
else
scanline = dst;
@ -70,14 +72,14 @@ public:
template<>
class BlenderHelper<RgbTraits, IndexedTraits> {
const Palette* m_pal;
int m_blend_mode;
BLEND_COLOR m_blend_color;
BlendMode m_blend_mode;
BlendFunc m_blend_func;
color_t m_mask_color;
public:
BlenderHelper(const Image* src, const Palette* pal, int blend_mode)
BlenderHelper(const Image* src, const Palette* pal, BlendMode blend_mode)
{
m_blend_mode = blend_mode;
m_blend_color = RgbTraits::get_blender(blend_mode);
m_blend_func = RgbTraits::get_blender(blend_mode);
m_mask_color = src->maskColor();
m_pal = pal;
}
@ -86,12 +88,12 @@ public:
const IndexedTraits::pixel_t& src,
int opacity)
{
if (m_blend_mode == BLEND_MODE_COPY) {
if (m_blend_mode == BlendMode::SRC) {
scanline = m_pal->getEntry(src);
}
else {
if (src != m_mask_color) {
scanline = (*m_blend_color)(dst, m_pal->getEntry(src), opacity);
scanline = (*m_blend_func)(dst, m_pal->getEntry(src), opacity);
}
else
scanline = dst;
@ -101,10 +103,10 @@ public:
template<>
class BlenderHelper<IndexedTraits, IndexedTraits> {
int m_blend_mode;
BlendMode m_blend_mode;
color_t m_mask_color;
public:
BlenderHelper(const Image* src, const Palette* pal, int blend_mode)
BlenderHelper(const Image* src, const Palette* pal, BlendMode blend_mode)
{
m_blend_mode = blend_mode;
m_mask_color = src->maskColor();
@ -114,7 +116,7 @@ public:
const IndexedTraits::pixel_t& src,
int opacity)
{
if (m_blend_mode == BLEND_MODE_COPY) {
if (m_blend_mode == BlendMode::SRC) {
scanline = src;
}
else {
@ -130,7 +132,7 @@ template<class DstTraits, class SrcTraits>
static void compose_scaled_image_scale_up(
Image* dst, const Image* src, const Palette* pal,
gfx::Clip area,
int opacity, int blend_mode, Zoom zoom)
int opacity, BlendMode blend_mode, Zoom zoom)
{
BlenderHelper<DstTraits, SrcTraits> blender(src, pal, blend_mode);
int px_x, px_y;
@ -257,7 +259,7 @@ template<class DstTraits, class SrcTraits>
static void compose_scaled_image_scale_down(
Image* dst, const Image* src, const Palette* pal,
gfx::Clip area,
int opacity, int blend_mode, Zoom zoom)
int opacity, BlendMode blend_mode, Zoom zoom)
{
BlenderHelper<DstTraits, SrcTraits> blender(src, pal, blend_mode);
int unbox_w = zoom.remove(1);
@ -314,7 +316,7 @@ template<class DstTraits, class SrcTraits>
static void compose_scaled_image(
Image* dst, const Image* src, const Palette* pal,
const gfx::Clip& area,
int opacity, int blend_mode, Zoom zoom)
int opacity, BlendMode blend_mode, Zoom zoom)
{
if (zoom.scale() >= 1.0)
compose_scaled_image_scale_up<DstTraits, SrcTraits>(dst, src, pal, area, opacity, blend_mode, zoom);
@ -373,7 +375,7 @@ void Render::setPreviewImage(const Layer* layer, frame_t frame, Image* image)
void Render::setExtraImage(
ExtraType type,
const Cel* cel, const Image* image, int blendMode,
const Cel* cel, const Image* image, BlendMode blendMode,
const Layer* currentLayer,
frame_t currentFrame)
{
@ -438,7 +440,7 @@ void Render::renderLayer(
const Layer* layer,
frame_t frame,
const gfx::Clip& area,
int blend_mode)
BlendMode blend_mode)
{
m_sprite = layer->sprite();
@ -506,7 +508,7 @@ void Render::renderSprite(
renderLayer(
m_sprite->folder(), dstImage,
area, frame, zoom, scaled_func,
true, true, -1);
true, true, BlendMode::UNSPECIFIED);
// Onion-skin feature: Draw previous/next frames with different
// opacity (<255)
@ -544,11 +546,11 @@ void Render::renderSprite(
if (m_globalOpacity > 0) {
m_globalOpacity = MID(0, m_globalOpacity, 255);
int blend_mode = -1;
BlendMode blend_mode = BlendMode::UNSPECIFIED;
if (m_onionskin.type() == OnionskinType::MERGE)
blend_mode = BLEND_MODE_NORMAL;
blend_mode = BlendMode::NORMAL;
else if (m_onionskin.type() == OnionskinType::RED_BLUE_TINT)
blend_mode = (frameOut < frame ? BLEND_MODE_RED_TINT: BLEND_MODE_BLUE_TINT);
blend_mode = (frameOut < frame ? BlendMode::RED_TINT: BlendMode::BLUE_TINT);
renderLayer(m_sprite->folder(), dstImage,
area, frameIn, zoom, scaled_func,
@ -605,7 +607,7 @@ void Render::renderBackground(Image* image,
}
void Render::renderImage(Image* dst_image, const Image* src_image,
const Palette* pal, int x, int y, Zoom zoom, int opacity, int blend_mode)
const Palette* pal, int x, int y, Zoom zoom, int opacity, BlendMode blend_mode)
{
RenderScaledImage scaled_func = getRenderScaledImageFunc(
dst_image->pixelFormat(),
@ -628,7 +630,7 @@ void Render::renderLayer(
RenderScaledImage scaled_func,
bool render_background,
bool render_transparent,
int blend_mode)
BlendMode blend_mode)
{
// we can't read from this layer
if (!layer->isVisible())
@ -681,14 +683,14 @@ void Render::renderLayer(
int t, output_opacity;
output_opacity = MID(0, cel->opacity(), 255);
output_opacity = INT_MULT(output_opacity, m_globalOpacity, t);
output_opacity = MUL_UN8(output_opacity, m_globalOpacity, t);
ASSERT(src_image->maskColor() == m_sprite->transparentColor());
int layer_blend_mode =
(blend_mode < 0 ?
static_cast<const LayerImage*>(layer)->getBlendMode():
blend_mode);
BlendMode layer_blend_mode =
(blend_mode == BlendMode::UNSPECIFIED ?
static_cast<const LayerImage*>(layer)->blendMode():
blend_mode);
// Draw parts outside the "m_extraCel" area
if (drawExtra && m_extraType == ExtraType::PATCH) {
@ -758,7 +760,7 @@ void Render::renderCel(
const Cel* cel,
const gfx::Clip& area,
RenderScaledImage scaled_func,
int opacity, int blend_mode, Zoom zoom)
int opacity, BlendMode blend_mode, Zoom zoom)
{
int cel_x = zoom.apply(cel->x());
int cel_y = zoom.apply(cel->y());
@ -820,7 +822,7 @@ Render::RenderScaledImage Render::getRenderScaledImageFunc(
}
void composite_image(Image* dst, const Image* src,
int x, int y, int opacity, int blend_mode)
int x, int y, int opacity, BlendMode blend_mode)
{
// As the background is not rendered in renderImage(), we don't need
// to configure the Render instance's BgType.

View File

@ -9,6 +9,7 @@
#pragma once
#include "doc/anidir.h"
#include "doc/blend_mode.h"
#include "doc/color.h"
#include "doc/frame.h"
#include "doc/pixel_format.h"
@ -99,7 +100,7 @@ namespace render {
// layer/frame.
void setExtraImage(
ExtraType type,
const Cel* cel, const Image* image, int blendMode,
const Cel* cel, const Image* image, BlendMode blendMode,
const Layer* currentLayer,
frame_t currentFrame);
void removeExtraImage();
@ -128,7 +129,7 @@ namespace render {
const Layer* layer,
frame_t frame,
const gfx::Clip& area,
int blend_mode = -1);
BlendMode blend_mode = BlendMode::UNSPECIFIED);
// Main function used to render the sprite. Draws the given sprite
// frame in a new image and return it. Note: zoomedRect must have
@ -147,13 +148,13 @@ namespace render {
void renderImage(Image* dst_image, const Image* src_image,
const Palette* pal, int x, int y, Zoom zoom,
int opacity, int blend_mode);
int opacity, BlendMode blend_mode);
private:
typedef void (*RenderScaledImage)(
Image* dst, const Image* src, const Palette* pal,
const gfx::Clip& area,
int opacity, int blend_mode, Zoom zoom);
int opacity, BlendMode blend_mode, Zoom zoom);
void renderLayer(
const Layer* layer,
@ -163,7 +164,7 @@ namespace render {
RenderScaledImage renderScaledImage,
bool render_background,
bool render_transparent,
int blend_mode);
BlendMode blend_mode);
void renderCel(
Image* dst_image,
@ -172,7 +173,7 @@ namespace render {
const Cel* cel,
const gfx::Clip& area,
RenderScaledImage scaled_func,
int opacity, int blend_mode, Zoom zoom);
int opacity, BlendMode blend_mode, Zoom zoom);
static RenderScaledImage getRenderScaledImageFunc(
PixelFormat dstFormat,
@ -184,7 +185,7 @@ namespace render {
ExtraType m_extraType;
const Cel* m_extraCel;
const Image* m_extraImage;
int m_extraBlendMode;
BlendMode m_extraBlendMode;
BgType m_bgType;
bool m_bgZoom;
@ -199,7 +200,7 @@ namespace render {
};
void composite_image(Image* dst, const Image* src,
int x, int y, int opacity, int blend_mode);
int x, int y, int opacity, BlendMode blend_mode);
} // namespace render

View File

@ -562,7 +562,7 @@ void ComboBox::onButtonClick(Event& ev)
void ComboBox::openListBox()
{
if (m_window)
if (!isEnabled() || m_window)
return;
m_window = new Window(Window::WithoutTitleBar);