diff --git a/data/pref.xml b/data/pref.xml
index aa7fa6213..8068e00fd 100644
--- a/data/pref.xml
+++ b/data/pref.xml
@@ -36,6 +36,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -59,7 +75,6 @@
-
@@ -98,6 +113,13 @@
+
+
diff --git a/data/skins/default/sheet.png b/data/skins/default/sheet.png
index 590efd825..ebe610d1b 100644
Binary files a/data/skins/default/sheet.png and b/data/skins/default/sheet.png differ
diff --git a/data/skins/default/skin.xml b/data/skins/default/skin.xml
index 967333803..140a1467f 100644
--- a/data/skins/default/skin.xml
+++ b/data/skins/default/skin.xml
@@ -372,6 +372,9 @@
+
+
+
diff --git a/data/widgets/palette_from_sprite.xml b/data/widgets/palette_from_sprite.xml
index a39f65c12..ab3d46017 100644
--- a/data/widgets/palette_from_sprite.xml
+++ b/data/widgets/palette_from_sprite.xml
@@ -9,6 +9,8 @@
+
+
diff --git a/src/app/color.cpp b/src/app/color.cpp
index 23677cafb..92cec8c38 100644
--- a/src/app/color.cpp
+++ b/src/app/color.cpp
@@ -35,30 +35,33 @@ Color Color::fromMask()
}
// static
-Color Color::fromRgb(int r, int g, int b)
+Color Color::fromRgb(int r, int g, int b, int a)
{
Color color(Color::RgbType);
color.m_value.rgb.r = r;
color.m_value.rgb.g = g;
color.m_value.rgb.b = b;
+ color.m_value.rgb.a = a;
return color;
}
// static
-Color Color::fromHsv(int h, int s, int v)
+Color Color::fromHsv(int h, int s, int v, int a)
{
Color color(Color::HsvType);
color.m_value.hsv.h = h;
color.m_value.hsv.s = s;
color.m_value.hsv.v = v;
+ color.m_value.hsv.a = a;
return color;
}
// static
-Color Color::fromGray(int g)
+Color Color::fromGray(int g, int a)
{
Color color(Color::GrayType);
- color.m_value.gray = g;
+ color.m_value.gray.g = g;
+ color.m_value.gray.a = a;
return color;
}
@@ -83,13 +86,15 @@ Color Color::fromImage(PixelFormat pixelFormat, color_t c)
if (rgba_geta(c) > 0) {
color = Color::fromRgb(rgba_getr(c),
rgba_getg(c),
- rgba_getb(c));
+ rgba_getb(c),
+ rgba_geta(c));
}
break;
case IMAGE_GRAYSCALE:
if (graya_geta(c) > 0) {
- color = Color::fromGray(graya_getv(c));
+ color = Color::fromGray(graya_getv(c),
+ graya_geta(c));
}
break;
@@ -117,26 +122,26 @@ Color Color::fromString(const std::string& str)
if (str != "mask") {
if (str.find("rgb{") == 0 ||
- str.find("hsv{") == 0) {
- int c = 0, table[3] = { 0, 0, 0 };
+ str.find("hsv{") == 0 ||
+ str.find("gray{") == 0) {
+ int c = 0, table[4] = { 0, 0, 0, 255 };
std::string::size_type i = 4, j;
while ((j = str.find_first_of(",}", i)) != std::string::npos) {
std::string element = str.substr(i, j - i);
- if (c < 3)
+ if (c < 4)
table[c++] = std::strtol(element.c_str(), NULL, 10);
- if (c >= 3)
+ if (c >= 4)
break;
i = j+1;
}
if (str[0] == 'r')
- color = Color::fromRgb(table[0], table[1], table[2]);
- else
- color = Color::fromHsv(table[0], table[1], table[2]);
- }
- else if (str.find("gray{") == 0) {
- color = Color::fromGray(std::strtol(str.c_str()+5, NULL, 10));
+ color = Color::fromRgb(table[0], table[1], table[2], table[3]);
+ else if (str[0] == 'h')
+ color = Color::fromHsv(table[0], table[1], table[2], table[3]);
+ else if (str[0] == 'g')
+ color = Color::fromGray(table[0], c >= 2 ? table[1]: 255);
}
else if (str.find("index{") == 0) {
color = Color::fromIndex(std::strtol(str.c_str()+6, NULL, 10));
@@ -160,18 +165,22 @@ std::string Color::toString() const
result << "rgb{"
<< m_value.rgb.r << ","
<< m_value.rgb.g << ","
- << m_value.rgb.b << "}";
+ << m_value.rgb.b << ","
+ << m_value.rgb.a << "}";
break;
case Color::HsvType:
result << "hsv{"
<< m_value.hsv.h << ","
<< m_value.hsv.s << ","
- << m_value.hsv.v << "}";
+ << m_value.hsv.v << ","
+ << m_value.hsv.a << "}";
break;
case Color::GrayType:
- result << "gray{" << m_value.gray << "}";
+ result << "gray{"
+ << m_value.gray.g << ","
+ << m_value.gray.a << "}";
break;
case Color::IndexType:
@@ -226,7 +235,7 @@ std::string Color::toHumanReadableString(PixelFormat pixelFormat, HumanReadableS
break;
case Color::GrayType:
- result << "Gray " << m_value.gray;
+ result << "Gray " << m_value.gray.g;
break;
case Color::IndexType: {
@@ -288,7 +297,7 @@ std::string Color::toHumanReadableString(PixelFormat pixelFormat, HumanReadableS
break;
case Color::GrayType:
- result << "Gry-" << m_value.gray;
+ result << "Gry-" << m_value.gray.g;
break;
case Color::IndexType:
@@ -318,16 +327,20 @@ bool Color::operator==(const Color& other) const
return
m_value.rgb.r == other.m_value.rgb.r &&
m_value.rgb.g == other.m_value.rgb.g &&
- m_value.rgb.b == other.m_value.rgb.b;
+ m_value.rgb.b == other.m_value.rgb.b &&
+ m_value.rgb.a == other.m_value.rgb.a;
case Color::HsvType:
return
m_value.hsv.h == other.m_value.hsv.h &&
m_value.hsv.s == other.m_value.hsv.s &&
- m_value.hsv.v == other.m_value.hsv.v;
+ m_value.hsv.v == other.m_value.hsv.v &&
+ m_value.hsv.a == other.m_value.hsv.a;
case Color::GrayType:
- return m_value.gray == other.m_value.gray;
+ return
+ m_value.gray.g == other.m_value.gray.g &&
+ m_value.gray.a == other.m_value.gray.a;
case Color::IndexType:
return m_value.index == other.m_value.index;
@@ -370,7 +383,7 @@ int Color::getRed() const
double(m_value.hsv.v) / 100.0)).red();
case Color::GrayType:
- return m_value.gray;
+ return m_value.gray.g;
case Color::IndexType: {
int i = m_value.index;
@@ -402,7 +415,7 @@ int Color::getGreen() const
double(m_value.hsv.v) / 100.0)).green();
case Color::GrayType:
- return m_value.gray;
+ return m_value.gray.g;
case Color::IndexType: {
int i = m_value.index;
@@ -434,7 +447,7 @@ int Color::getBlue() const
double(m_value.hsv.v) / 100.0)).blue();
case Color::GrayType:
- return m_value.gray;
+ return m_value.gray.g;
case Color::IndexType: {
int i = m_value.index;
@@ -538,7 +551,7 @@ int Color::getValue() const
return m_value.hsv.v;
case Color::GrayType:
- return 100 * m_value.gray / 255;
+ return 100 * m_value.gray.g / 255;
case Color::IndexType: {
int i = m_value.index;
@@ -574,7 +587,7 @@ int Color::getGray() const
return 255 * m_value.hsv.v / 100;
case Color::GrayType:
- return m_value.gray;
+ return m_value.gray.g;
case Color::IndexType: {
int i = m_value.index;
@@ -602,13 +615,14 @@ int Color::getIndex() const
return 0;
case Color::RgbType:
- return get_current_palette()->findBestfit(getRed(), getGreen(), getBlue());
-
case Color::HsvType:
- return get_current_palette()->findBestfit(getRed(), getGreen(), getBlue());
-
- case Color::GrayType:
- return m_value.gray;
+ case Color::GrayType: {
+ int i = get_current_palette()->findExactMatch(getRed(), getGreen(), getBlue(), getAlpha());
+ if (i >= 0)
+ return i;
+ else
+ return get_current_palette()->findBestfit(getRed(), getGreen(), getBlue(), getAlpha(), 0);
+ }
case Color::IndexType:
return m_value.index;
@@ -619,4 +633,34 @@ int Color::getIndex() const
return -1;
}
+int Color::getAlpha() const
+{
+ switch (getType()) {
+
+ case Color::MaskType:
+ return 0;
+
+ case Color::RgbType:
+ return m_value.rgb.a;
+
+ case Color::HsvType:
+ return m_value.hsv.a;
+
+ case Color::GrayType:
+ return m_value.gray.a;
+
+ case Color::IndexType: {
+ int i = m_value.index;
+ if (i >= 0 && i < get_current_palette()->size())
+ return rgba_geta(get_current_palette()->getEntry(i));
+ else
+ return 0;
+ }
+
+ }
+
+ ASSERT(false);
+ return -1;
+}
+
} // namespace app
diff --git a/src/app/color.h b/src/app/color.h
index fb71695d0..711ec725b 100644
--- a/src/app/color.h
+++ b/src/app/color.h
@@ -42,9 +42,9 @@ namespace app {
Color() : m_type(MaskType) { }
static Color fromMask();
- static Color fromRgb(int r, int g, int b);
- static Color fromHsv(int h, int s, int v); // h=[0,360], s=[0,100], v=[0,100]
- static Color fromGray(int g);
+ static Color fromRgb(int r, int g, int b, int a = 255);
+ static Color fromHsv(int h, int s, int v, int a = 255); // h=[0,360], s=[0,100], v=[0,100]
+ static Color fromGray(int g, int a = 255);
static Color fromIndex(int index);
static Color fromImage(PixelFormat pixelFormat, color_t c);
@@ -74,6 +74,7 @@ namespace app {
int getValue() const;
int getGray() const;
int getIndex() const;
+ int getAlpha() const;
private:
Color(Type type) : m_type(type) { }
@@ -84,12 +85,14 @@ namespace app {
// Color value
union {
struct {
- int r, g, b;
+ int r, g, b, a;
} rgb;
struct {
- int h, s, v;
+ int h, s, v, a;
} hsv;
- int gray;
+ struct {
+ int g, a;
+ } gray;
int index;
} m_value;
};
diff --git a/src/app/color_utils.cpp b/src/app/color_utils.cpp
index 8cf1c7560..5398184c0 100644
--- a/src/app/color_utils.cpp
+++ b/src/app/color_utils.cpp
@@ -52,14 +52,16 @@ gfx::Color color_utils::color_for_ui(const app::Color& color)
c = gfx::rgba(
color.getRed(),
color.getGreen(),
- color.getBlue(), 255);
+ color.getBlue(),
+ color.getAlpha());
break;
case app::Color::GrayType:
c = gfx::rgba(
color.getGray(),
color.getGray(),
- color.getGray(), 255);
+ color.getGray(),
+ color.getAlpha());
break;
case app::Color::IndexType: {
@@ -70,7 +72,8 @@ gfx::Color color_utils::color_for_ui(const app::Color& color)
c = gfx::rgba(
rgba_getr(_c),
rgba_getg(_c),
- rgba_getb(_c), 255);
+ rgba_getb(_c),
+ color.getAlpha());
break;
}
@@ -116,10 +119,10 @@ doc::color_t color_utils::color_for_target_mask(const app::Color& color, const C
else {
switch (colorTarget.pixelFormat()) {
case IMAGE_RGB:
- c = doc::rgba(color.getRed(), color.getGreen(), color.getBlue(), 255);
+ c = doc::rgba(color.getRed(), color.getGreen(), color.getBlue(), color.getAlpha());
break;
case IMAGE_GRAYSCALE:
- c = doc::graya(color.getGray(), 255);
+ c = doc::graya(color.getGray(), color.getAlpha());
break;
case IMAGE_INDEXED:
if (color.getType() == app::Color::IndexType) {
@@ -130,6 +133,7 @@ doc::color_t color_utils::color_for_target_mask(const app::Color& color, const C
color.getRed(),
color.getGreen(),
color.getBlue(),
+ color.getAlpha(),
colorTarget.isTransparent() ?
colorTarget.maskColor(): // Don't return the mask color
-1); // Return any color, we are in a background layer.
diff --git a/src/app/commands/cmd_color_quantization.cpp b/src/app/commands/cmd_color_quantization.cpp
index 70b50fa90..ef38d16fe 100644
--- a/src/app/commands/cmd_color_quantization.cpp
+++ b/src/app/commands/cmd_color_quantization.cpp
@@ -9,12 +9,14 @@
#include "config.h"
#endif
+#include "app/app.h"
#include "app/cmd/set_palette.h"
#include "app/commands/command.h"
#include "app/console.h"
#include "app/context.h"
#include "app/context_access.h"
#include "app/modules/palettes.h"
+#include "app/pref/preferences.h"
#include "app/transaction.h"
#include "app/ui/color_bar.h"
#include "app/ui_context.h"
@@ -70,6 +72,8 @@ void ColorQuantizationCommand::onExecute(Context* context)
curPalette = sprite->palette(frame);
window.newPalette()->setSelected(true);
+ window.alphaChannel()->setSelected(
+ App::instance()->preferences().quantization.withAlpha());
window.ncolors()->setText("256");
ColorBar::instance()->getPaletteView()->getSelectedEntries(entries);
@@ -92,6 +96,9 @@ void ColorQuantizationCommand::onExecute(Context* context)
if (window.getKiller() != window.ok())
return;
+ bool withAlpha = window.alphaChannel()->isSelected();
+ App::instance()->preferences().quantization.withAlpha(withAlpha);
+
bool createPal = false;
if (window.newPalette()->isSelected()) {
int n = window.ncolors()->getTextInt();
@@ -107,7 +114,8 @@ void ColorQuantizationCommand::onExecute(Context* context)
return;
Palette tmpPalette(frame, entries.picks());
- render::create_palette_from_rgb(sprite, 0, sprite->lastFrame(), &tmpPalette);
+ render::create_palette_from_rgb(sprite, 0, sprite->lastFrame(),
+ withAlpha, &tmpPalette);
base::UniquePtr newPalette(
new Palette(createPal ? tmpPalette:
diff --git a/src/app/commands/cmd_eyedropper.cpp b/src/app/commands/cmd_eyedropper.cpp
index fbcc0c54a..207a8e9a0 100644
--- a/src/app/commands/cmd_eyedropper.cpp
+++ b/src/app/commands/cmd_eyedropper.cpp
@@ -12,7 +12,7 @@
#include "app/app.h"
#include "app/color.h"
#include "app/color_picker.h"
-#include "app/commands/command.h"
+#include "app/commands/cmd_eyedropper.h"
#include "app/commands/commands.h"
#include "app/commands/params.h"
#include "app/modules/editors.h"
@@ -32,21 +32,6 @@ namespace app {
using namespace ui;
-class EyedropperCommand : public Command {
- /**
- * True means "pick background color", false the foreground color.
- */
- bool m_background;
-
-public:
- EyedropperCommand();
- Command* clone() const override { return new EyedropperCommand(*this); }
-
-protected:
- void onLoadParams(const Params& params) override;
- void onExecute(Context* context) override;
-};
-
EyedropperCommand::EyedropperCommand()
: Command("Eyedropper",
"Eyedropper",
@@ -55,6 +40,113 @@ EyedropperCommand::EyedropperCommand()
m_background = false;
}
+void EyedropperCommand::pickSample(const doc::Site& site,
+ const gfx::Point& pixelPos,
+ app::Color& color)
+{
+ // Check if we've to grab alpha channel or the merged color.
+ Preferences& pref = Preferences::instance();
+ bool allLayers =
+ (pref.eyedropper.sample() == app::gen::EyedropperSample::ALL_LAYERS);
+
+ ColorPicker picker;
+ picker.pickColor(site,
+ pixelPos,
+ (allLayers ?
+ ColorPicker::FromComposition:
+ ColorPicker::FromActiveLayer));
+
+ app::gen::EyedropperChannel channel =
+ pref.eyedropper.channel();
+
+ app::Color picked = picker.color();
+
+ switch (channel) {
+ case app::gen::EyedropperChannel::COLOR_ALPHA:
+ color = picked;
+ break;
+ case app::gen::EyedropperChannel::COLOR:
+ if (picked.getAlpha() > 0)
+ color = app::Color::fromRgb(picked.getRed(),
+ picked.getGreen(),
+ picked.getBlue(),
+ color.getAlpha());
+ break;
+ case app::gen::EyedropperChannel::ALPHA:
+ switch (color.getType()) {
+
+ case app::Color::RgbType:
+ case app::Color::IndexType:
+ color = app::Color::fromRgb(color.getRed(),
+ color.getGreen(),
+ color.getBlue(),
+ picked.getAlpha());
+ break;
+
+ case app::Color::HsvType:
+ color = app::Color::fromHsv(color.getHue(),
+ color.getSaturation(),
+ color.getValue(),
+ picked.getAlpha());
+ break;
+
+ case app::Color::GrayType:
+ color = app::Color::fromGray(color.getGray(),
+ picked.getAlpha());
+ break;
+
+ }
+ break;
+ case app::gen::EyedropperChannel::RGBA:
+ if (picked.getType() == app::Color::RgbType)
+ color = picked;
+ else
+ color = app::Color::fromRgb(picked.getRed(),
+ picked.getGreen(),
+ picked.getBlue(),
+ picked.getAlpha());
+ break;
+ case app::gen::EyedropperChannel::RGB:
+ if (picked.getAlpha() > 0)
+ color = app::Color::fromRgb(picked.getRed(),
+ picked.getGreen(),
+ picked.getBlue(),
+ color.getAlpha());
+ break;
+ case app::gen::EyedropperChannel::HSVA:
+ if (picked.getType() == app::Color::HsvType)
+ color = picked;
+ else
+ color = app::Color::fromHsv(picked.getHue(),
+ picked.getSaturation(),
+ picked.getValue(),
+ picked.getAlpha());
+ break;
+ case app::gen::EyedropperChannel::HSV:
+ if (picked.getAlpha() > 0)
+ color = app::Color::fromHsv(picked.getHue(),
+ picked.getSaturation(),
+ picked.getValue(),
+ color.getAlpha());
+ break;
+ case app::gen::EyedropperChannel::GRAYA:
+ if (picked.getType() == app::Color::GrayType)
+ color = picked;
+ else
+ color = app::Color::fromGray(picked.getGray(),
+ picked.getAlpha());
+ break;
+ case app::gen::EyedropperChannel::GRAY:
+ if (picked.getAlpha() > 0)
+ color = app::Color::fromGray(picked.getGray(),
+ color.getAlpha());
+ break;
+ case app::gen::EyedropperChannel::INDEX:
+ color = app::Color::fromIndex(picked.getIndex());
+ break;
+ }
+}
+
void EyedropperCommand::onLoadParams(const Params& params)
{
std::string target = params.get("target");
@@ -82,27 +174,18 @@ void EyedropperCommand::onExecute(Context* context)
// Pixel position to get
gfx::Point pixelPos = editor->screenToEditor(ui::get_mouse_position());
- // Check if we've to grab alpha channel or the merged color.
+ // Start with fg/bg color
Preferences& pref = Preferences::instance();
- bool grabAlpha = pref.editor.grabAlpha();
+ app::Color color =
+ m_background ? pref.colorBar.bgColor():
+ pref.colorBar.fgColor();
- ColorPicker picker;
- picker.pickColor(editor->getSite(),
- pixelPos,
- grabAlpha ?
- ColorPicker::FromActiveLayer:
- ColorPicker::FromComposition);
-
- if (grabAlpha) {
- tools::ToolBox* toolBox = App::instance()->getToolBox();
- for (auto tool : *toolBox)
- pref.tool(tool).opacity(picker.alpha());
- }
+ pickSample(editor->getSite(), pixelPos, color);
if (m_background)
- pref.colorBar.bgColor(picker.color());
+ pref.colorBar.bgColor(color);
else
- pref.colorBar.fgColor(picker.color());
+ pref.colorBar.fgColor(color);
}
Command* CommandFactory::createEyedropperCommand()
diff --git a/src/app/commands/cmd_eyedropper.h b/src/app/commands/cmd_eyedropper.h
new file mode 100644
index 000000000..c2886b3de
--- /dev/null
+++ b/src/app/commands/cmd_eyedropper.h
@@ -0,0 +1,41 @@
+// 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_COMMANDS_CMD_EYEDROPPER_H_INCLUDED
+#define APP_COMMANDS_CMD_EYEDROPPER_H_INCLUDED
+#pragma once
+
+#include "app/color.h"
+#include "app/commands/command.h"
+
+namespace doc {
+ class Site;
+}
+
+namespace app {
+
+ class EyedropperCommand : public Command {
+ public:
+ EyedropperCommand();
+ Command* clone() const override { return new EyedropperCommand(*this); }
+
+ // Returns the color in the given sprite pos.
+ void pickSample(const doc::Site& site,
+ const gfx::Point& pixelPos,
+ app::Color& color);
+
+ protected:
+ void onLoadParams(const Params& params) override;
+ void onExecute(Context* context) override;
+
+ // True means "pick background color", false the foreground color.
+ bool m_background;
+ };
+
+} // namespace app
+
+#endif // APP_COMMANDS_CMD_EYEDROPPER_H_INCLUDED
diff --git a/src/app/commands/cmd_palette_editor.cpp b/src/app/commands/cmd_palette_editor.cpp
index 53e7afd92..8e2411ccc 100644
--- a/src/app/commands/cmd_palette_editor.cpp
+++ b/src/app/commands/cmd_palette_editor.cpp
@@ -497,7 +497,7 @@ void PaletteEntryEditor::setAbsolutePaletteEntryChannel(ColorSliders::Channel ch
int picksCount = entries.picks();
uint32_t src_color;
- int r, g, b;
+ int r, g, b, a;
Palette* palette = get_current_palette();
for (int c=0; csize(); c++) {
@@ -509,6 +509,7 @@ void PaletteEntryEditor::setAbsolutePaletteEntryChannel(ColorSliders::Channel ch
r = rgba_getr(src_color);
g = rgba_getg(src_color);
b = rgba_getb(src_color);
+ a = rgba_geta(src_color);
switch (m_type) {
@@ -518,6 +519,7 @@ void PaletteEntryEditor::setAbsolutePaletteEntryChannel(ColorSliders::Channel ch
r = color.getRed();
g = color.getGreen();
b = color.getBlue();
+ a = color.getAlpha();
}
// Modify one channel a set of entries
else {
@@ -531,6 +533,9 @@ void PaletteEntryEditor::setAbsolutePaletteEntryChannel(ColorSliders::Channel ch
case ColorSliders::Blue:
b = color.getBlue();
break;
+ case ColorSliders::Alpha:
+ a = color.getAlpha();
+ break;
}
}
break;
@@ -561,6 +566,9 @@ void PaletteEntryEditor::setAbsolutePaletteEntryChannel(ColorSliders::Channel ch
case ColorSliders::Value:
hsv.value(double(color.getValue()) / 100.0);
break;
+ case ColorSliders::Alpha:
+ a = color.getAlpha();
+ break;
}
}
@@ -573,7 +581,7 @@ void PaletteEntryEditor::setAbsolutePaletteEntryChannel(ColorSliders::Channel ch
break;
}
- palette->setEntry(c, doc::rgba(r, g, b, 255));
+ palette->setEntry(c, doc::rgba(r, g, b, a));
}
}
@@ -586,7 +594,7 @@ void PaletteEntryEditor::setRelativePaletteEntryChannel(ColorSliders::Channel ch
m_relDeltas[channel] = delta;
uint32_t src_color;
- int r, g, b;
+ int r, g, b, a;
Palette* palette = get_current_palette();
for (int c=0; csize(); c++) {
@@ -598,6 +606,7 @@ void PaletteEntryEditor::setRelativePaletteEntryChannel(ColorSliders::Channel ch
r = rgba_getr(src_color);
g = rgba_getg(src_color);
b = rgba_getb(src_color);
+ a = rgba_geta(src_color);
switch (m_type) {
@@ -605,6 +614,7 @@ void PaletteEntryEditor::setRelativePaletteEntryChannel(ColorSliders::Channel ch
r = MID(0, r+m_relDeltas[ColorSliders::Red], 255);
g = MID(0, g+m_relDeltas[ColorSliders::Green], 255);
b = MID(0, b+m_relDeltas[ColorSliders::Blue], 255);
+ a = MID(0, a+m_relDeltas[ColorSliders::Alpha], 255);
break;
case app::Color::HsvType: {
@@ -627,12 +637,13 @@ void PaletteEntryEditor::setRelativePaletteEntryChannel(ColorSliders::Channel ch
r = rgb.red();
g = rgb.green();
b = rgb.blue();
+ a = MID(0, a+m_relDeltas[ColorSliders::Alpha], 255);
break;
}
}
- palette->setEntry(c, doc::rgba(r, g, b, 255));
+ palette->setEntry(c, doc::rgba(r, g, b, a));
}
}
diff --git a/src/app/commands/cmd_sprite_size.cpp b/src/app/commands/cmd_sprite_size.cpp
index 6d27e4409..37a2f48ef 100644
--- a/src/app/commands/cmd_sprite_size.cpp
+++ b/src/app/commands/cmd_sprite_size.cpp
@@ -28,6 +28,7 @@
#include "doc/cel.h"
#include "doc/cels_range.h"
#include "doc/image.h"
+#include "doc/layer.h"
#include "doc/mask.h"
#include "doc/primitives.h"
#include "doc/sprite.h"
@@ -95,10 +96,12 @@ protected:
ImageRef new_image(Image::create(image->pixelFormat(), MAX(1, w), MAX(1, h)));
doc::algorithm::fixup_image_transparent_colors(image);
- doc::algorithm::resize_image(image, new_image.get(),
+ doc::algorithm::resize_image(
+ image, new_image.get(),
m_resize_method,
m_sprite->palette(cel->frame()),
- m_sprite->rgbMap(cel->frame()));
+ m_sprite->rgbMap(cel->frame()),
+ (cel->layer()->isBackground() ? -1: m_sprite->transparentColor()));
api.replaceImage(m_sprite, cel->imageRef(), new_image);
}
@@ -125,10 +128,12 @@ protected:
gfx::Rect(
scale_x(m_document->mask()->bounds().x-1),
scale_y(m_document->mask()->bounds().y-1), MAX(1, w), MAX(1, h)));
- algorithm::resize_image(old_bitmap.get(), new_mask->bitmap(),
- m_resize_method,
- m_sprite->palette(0), // Ignored
- m_sprite->rgbMap(0)); // Ignored
+ algorithm::resize_image(
+ old_bitmap.get(), new_mask->bitmap(),
+ m_resize_method,
+ m_sprite->palette(0), // Ignored
+ m_sprite->rgbMap(0), // Ignored
+ -1); // Ignored
// Reshrink
new_mask->intersect(new_mask->bounds());
diff --git a/src/app/commands/filters/filter_target_buttons.cpp b/src/app/commands/filters/filter_target_buttons.cpp
index 31599a216..0c2c4c633 100644
--- a/src/app/commands/filters/filter_target_buttons.cpp
+++ b/src/app/commands/filters/filter_target_buttons.cpp
@@ -61,17 +61,15 @@ FilterTargetButtons::FilterTargetButtons(int imgtype, bool withChannels)
case IMAGE_INDEXED:
r = check_button_new("R", 2, 0, 0, 0);
g = check_button_new("G", 0, 0, 0, 0);
- b = check_button_new("B", 0, (imgtype == IMAGE_RGB) ? 0: 2, 0, 0);
+ b = check_button_new("B", 0, 0, 0, 0);
+ a = check_button_new("A", 0, 2, 0, 0);
r->setId("r");
g->setId("g");
b->setId("b");
+ a->setId("a");
- if (imgtype == IMAGE_RGB) {
- a = check_button_new("A", 0, 2, 0, 0);
- a->setId("a");
- }
- else {
+ if (imgtype == IMAGE_INDEXED) {
index = check_button_new("Index", 0, 0, 0, 0);
index->setId("i");
}
diff --git a/src/app/file/ase_format.cpp b/src/app/file/ase_format.cpp
index ff4506bea..de48b91fc 100644
--- a/src/app/file/ase_format.cpp
+++ b/src/app/file/ase_format.cpp
@@ -144,7 +144,8 @@ class AseFormat : public FileFormat {
FILE_SUPPORT_FRAMES |
FILE_SUPPORT_PALETTES |
FILE_SUPPORT_FRAME_TAGS |
- FILE_SUPPORT_BIG_PALETTES;
+ FILE_SUPPORT_BIG_PALETTES |
+ FILE_SUPPORT_PALETTE_WITH_ALPHA;
}
bool onLoad(FileOp* fop) override;
@@ -666,9 +667,7 @@ static Palette* ase_file_read_palette_chunk(FILE* f, Palette* prevPal, frame_t f
int g = fgetc(f);
int b = fgetc(f);
int a = fgetc(f);
-
- // TODO don't ignore alpha
- pal->setEntry(c, rgba(r, g, b, 255));
+ pal->setEntry(c, rgba(r, g, b, a));
// Skip name
if (flags & 1) {
diff --git a/src/app/file/file.cpp b/src/app/file/file.cpp
index 3316b7df4..8436e7499 100644
--- a/src/app/file/file.cpp
+++ b/src/app/file/file.cpp
@@ -332,6 +332,22 @@ FileOp* fop_to_save_document(const Context* context, const Document* document,
}
}
+ // Palette with alpha
+ if (!fop->format->support(FILE_SUPPORT_PALETTE_WITH_ALPHA)) {
+ bool done = false;
+ for (Palette* pal : fop->document->sprite()->getPalettes()) {
+ for (int c=0; csize(); ++c) {
+ if (rgba_geta(pal->getEntry(c)) < 255) {
+ warnings += "<<- Palette with alpha channel";
+ done = true;
+ break;
+ }
+ }
+ if (done)
+ break;
+ }
+ }
+
// Show the confirmation alert
if (!warnings.empty()) {
// Interative
@@ -724,7 +740,7 @@ void fop_post_load(FileOp* fop)
sprite->palette(frame_t(0))->isBlack()) {
base::SharedPtr palette(
render::create_palette_from_rgb(
- sprite, frame_t(0), sprite->lastFrame(), nullptr));
+ sprite, frame_t(0), sprite->lastFrame(), true, nullptr));
sprite->resetPalettes();
sprite->setPalette(palette.get(), false);
@@ -740,6 +756,16 @@ void fop_sequence_set_format_options(FileOp* fop, const base::SharedPtrseq.format_options = format_options;
}
+void fop_sequence_set_ncolors(FileOp* fop, int ncolors)
+{
+ fop->seq.palette->resize(ncolors);
+}
+
+int fop_sequence_get_ncolors(FileOp* fop)
+{
+ return fop->seq.palette->size();
+}
+
void fop_sequence_set_color(FileOp *fop, int index, int r, int g, int b)
{
fop->seq.palette->setEntry(index, rgba(r, g, b, 255));
@@ -760,6 +786,25 @@ void fop_sequence_get_color(FileOp *fop, int index, int *r, int *g, int *b)
*b = rgba_getb(c);
}
+void fop_sequence_set_alpha(FileOp* fop, int index, int a)
+{
+ int c = fop->seq.palette->getEntry(index);
+ int r = rgba_getr(c);
+ int g = rgba_getg(c);
+ int b = rgba_getb(c);
+
+ fop->seq.palette->setEntry(index, rgba(r, g, b, a));
+}
+
+void fop_sequence_get_alpha(FileOp* fop, int index, int* a)
+{
+ ASSERT(index >= 0);
+ if (index >= 0 && index < fop->seq.palette->size())
+ *a = rgba_geta(fop->seq.palette->getEntry(index));
+ else
+ *a = 0;
+}
+
Image* fop_sequence_image(FileOp* fop, PixelFormat pixelFormat, int w, int h)
{
Sprite* sprite;
diff --git a/src/app/file/file.h b/src/app/file/file.h
index 7121f142f..8e5babfeb 100644
--- a/src/app/file/file.h
+++ b/src/app/file/file.h
@@ -133,8 +133,12 @@ namespace app {
void fop_post_load(FileOp* fop);
void fop_sequence_set_format_options(FileOp* fop, const base::SharedPtr& format_options);
+ void fop_sequence_set_ncolors(FileOp* fop, int ncolors);
+ int fop_sequence_get_ncolors(FileOp* fop);
void fop_sequence_set_color(FileOp* fop, int index, int r, int g, int b);
void fop_sequence_get_color(FileOp* fop, int index, int *r, int *g, int *b);
+ void fop_sequence_set_alpha(FileOp* fop, int index, int a);
+ void fop_sequence_get_alpha(FileOp* fop, int index, int* a);
Image* fop_sequence_image(FileOp* fi, PixelFormat pixelFormat, int w, int h);
void fop_error(FileOp* fop, const char *error, ...);
diff --git a/src/app/file/file_format.h b/src/app/file/file_format.h
index 9485020c5..e6f179079 100644
--- a/src/app/file/file_format.h
+++ b/src/app/file/file_format.h
@@ -27,6 +27,7 @@
#define FILE_SUPPORT_GET_FORMAT_OPTIONS 0x00000800
#define FILE_SUPPORT_FRAME_TAGS 0x00001000
#define FILE_SUPPORT_BIG_PALETTES 0x00002000 // Palettes w/more than 256 colors
+#define FILE_SUPPORT_PALETTE_WITH_ALPHA 0x00004000
namespace app {
diff --git a/src/app/file/gif_format.cpp b/src/app/file/gif_format.cpp
index b5f1df973..9f093372b 100644
--- a/src/app/file/gif_format.cpp
+++ b/src/app/file/gif_format.cpp
@@ -641,7 +641,7 @@ bool GifFormat::onSave(FileOp* fop)
for (frame_t frame_num(0); frame_numtotalFrames(); ++frame_num) {
clear_image(buffer_image, background_color);
render.renderSprite(buffer_image, sprite, frame_num);
- optimizer.feedWithImage(buffer_image);
+ optimizer.feedWithImage(buffer_image, false);
}
current_palette.makeBlack();
@@ -667,7 +667,7 @@ bool GifFormat::onSave(FileOp* fop)
std::vector imgarray(1);
imgarray[0] = buffer_image;
- render::create_palette_from_images(imgarray, ¤t_palette, has_background);
+ render::create_palette_from_images(imgarray, ¤t_palette, has_background, false);
rgbmap.regenerate(¤t_palette, transparent_index);
}
break;
diff --git a/src/app/file/png_format.cpp b/src/app/file/png_format.cpp
index 6abf5e556..2e9960817 100644
--- a/src/app/file/png_format.cpp
+++ b/src/app/file/png_format.cpp
@@ -39,7 +39,8 @@ class PngFormat : public FileFormat {
FILE_SUPPORT_GRAY |
FILE_SUPPORT_GRAYA |
FILE_SUPPORT_INDEXED |
- FILE_SUPPORT_SEQUENCES;
+ FILE_SUPPORT_SEQUENCES |
+ FILE_SUPPORT_PALETTE_WITH_ALPHA;
}
bool onLoad(FileOp* fop) override;
@@ -184,39 +185,34 @@ bool PngFormat::onLoad(FileOp* fop)
return false;
}
- // Transparent palette entries
- std::vector pal_alphas(256, 255);
- int mask_entry = -1;
-
// Read the palette
if (png_get_color_type(png_ptr, info_ptr) == PNG_COLOR_TYPE_PALETTE &&
png_get_PLTE(png_ptr, info_ptr, &palette, &num_palette)) {
- int c;
+ fop_sequence_set_ncolors(fop, num_palette);
- for (c = 0; c < num_palette; c++) {
+ for (int c=0; cseq.has_alpha = true; // Is a transparent sprite
-
- if (mask_entry < 0)
- mask_entry = i;
+ if (trans[i] == 0) {
+ if (mask_entry < 0)
+ mask_entry = i;
+ }
}
}
@@ -225,8 +221,6 @@ bool PngFormat::onLoad(FileOp* fop)
fop->document->sprite()->setTransparentColor(mask_entry);
}
- mask_entry = fop->document->sprite()->transparentColor();
-
/* Allocate the memory to hold the image using the fields of info_ptr. */
/* The easiest way to read the image: */
@@ -290,18 +284,10 @@ bool PngFormat::onLoad(FileOp* fop)
else if (png_get_color_type(png_ptr, info_ptr) == PNG_COLOR_TYPE_PALETTE) {
uint8_t* src_address = row_pointer;
uint8_t* dst_address = (uint8_t*)image->getPixelAddress(0, y);
- unsigned int x, c;
+ unsigned int x;
- for (x=0; xpixelFormat() == IMAGE_INDEXED) {
int c, r, g, b;
+ int pal_size = fop_sequence_get_ncolors(fop);
+ ASSERT(pal_size > 0 && pal_size <= PNG_MAX_PALETTE_LENGTH);
+ pal_size = MID(1, pal_size, PNG_MAX_PALETTE_LENGTH);
#if PNG_MAX_PALETTE_LENGTH != 256
#error PNG_MAX_PALETTE_LENGTH should be 256
#endif
// Save the color palette.
- palette = (png_colorp)png_malloc(png_ptr, PNG_MAX_PALETTE_LENGTH * sizeof(png_color));
- for (c = 0; c < PNG_MAX_PALETTE_LENGTH; c++) {
+ palette = (png_colorp)png_malloc(png_ptr, pal_size * sizeof(png_color));
+ for (c = 0; c < pal_size; c++) {
fop_sequence_get_color(fop, c, &r, &g, &b);
palette[c].red = r;
palette[c].green = g;
palette[c].blue = b;
}
- png_set_PLTE(png_ptr, info_ptr, palette, PNG_MAX_PALETTE_LENGTH);
+ png_set_PLTE(png_ptr, info_ptr, palette, pal_size);
// If the sprite does not have a (visible) background layer, we
- // include the alpha information of palette entries to indicate
- // which is the transparent color.
+ // put alpha=0 to the transparent color.
+ int mask_entry = -1;
if (fop->document->sprite()->backgroundLayer() == NULL ||
!fop->document->sprite()->backgroundLayer()->isVisible()) {
- int mask_entry = fop->document->sprite()->transparentColor();
- int num_trans = mask_entry+1;
- png_bytep trans = (png_bytep)png_malloc(png_ptr, num_trans);
-
- for (c = 0; c < num_trans; ++c)
- trans[c] = (c == mask_entry ? 0: 255);
-
- png_set_tRNS(png_ptr, info_ptr, trans, num_trans, NULL);
- png_free(png_ptr, trans);
+ mask_entry = fop->document->sprite()->transparentColor();
}
+
+ int num_trans = pal_size;
+ png_bytep trans = (png_bytep)png_malloc(png_ptr, num_trans);
+
+ for (c=0; c= 0 && index < get_current_palette()->size()) {
+ if (alpha > 0) {
+ if (type == app::Color::IndexType) {
+ int index = color.getIndex();
+
+ if (index >= 0 && index < get_current_palette()->size()) {
+ g->fillRect(color_utils::color_for_ui(color), rc);
+ }
+ else {
+ g->fillRect(gfx::rgba(0, 0, 0), rc);
+ g->drawLine(gfx::rgba(255, 255, 255),
+ gfx::Point(rc.x+rc.w-2, rc.y+1),
+ gfx::Point(rc.x+1, rc.y+rc.h-2));
+ }
+ }
+ else
g->fillRect(color_utils::color_for_ui(color), rc);
- }
- else {
- g->fillRect(gfx::rgba(0, 0, 0), rc);
- g->drawLine(gfx::rgba(255, 255, 255),
- gfx::Point(rc.x+rc.w-2, rc.y+1),
- gfx::Point(rc.x+1, rc.y+rc.h-2));
- }
- return;
}
-
- g->fillRect(color_utils::color_for_ui(color), rc);
}
void draw_color_button(ui::Graphics* g,
diff --git a/src/app/modules/gfx.h b/src/app/modules/gfx.h
index 43f647464..afc060d50 100644
--- a/src/app/modules/gfx.h
+++ b/src/app/modules/gfx.h
@@ -18,6 +18,9 @@
namespace app {
using namespace doc;
+ void draw_color(ui::Graphics* g,
+ const gfx::Rect& rc, const app::Color& color);
+
void draw_color_button(ui::Graphics* g,
const gfx::Rect& rc, const app::Color& color,
bool hot, bool drag);
diff --git a/src/app/tools/ink_processing.h b/src/app/tools/ink_processing.h
index 5135cb7b5..36ec7fe83 100644
--- a/src/app/tools/ink_processing.h
+++ b/src/app/tools/ink_processing.h
@@ -10,6 +10,7 @@
#include "app/tools/shading_options.h"
#include "doc/blend_funcs.h"
#include "doc/image_impl.h"
+#include "doc/layer.h"
#include "doc/palette.h"
#include "doc/rgbmap.h"
#include "doc/sprite.h"
@@ -241,14 +242,22 @@ public:
m_palette(get_current_palette()),
m_rgbmap(loop->getRgbMap()),
m_opacity(loop->getOpacity()),
- m_color(m_palette->getEntry(loop->getPrimaryColor())) {
+ m_color(m_palette->getEntry(loop->getPrimaryColor())),
+ m_maskColor(loop->getLayer()->isBackground() ? -1: loop->sprite()->transparentColor()) {
}
void processPixel(int x, int y) {
- color_t c = rgba_blender_normal(m_palette->getEntry(*m_srcAddress), m_color, m_opacity);
+ color_t c = *m_srcAddress;
+ if (c == m_maskColor)
+ c = m_palette->getEntry(c) & rgba_rgb_mask; // Alpha = 0
+ else
+ c = m_palette->getEntry(c);
+
+ c = rgba_blender_normal(c, m_color, m_opacity);
*m_dstAddress = m_rgbmap->mapColor(rgba_getr(c),
rgba_getg(c),
- rgba_getb(c));
+ rgba_getb(c),
+ rgba_geta(c));
}
private:
@@ -256,6 +265,7 @@ private:
const RgbMap* m_rgbmap;
int m_opacity;
color_t m_color;
+ color_t m_maskColor;
};
//////////////////////////////////////////////////////////////////////
@@ -387,24 +397,27 @@ public:
m_opacity(loop->getOpacity()),
m_tiledMode(loop->getTiledMode()),
m_srcImage(loop->getSrcImage()),
- m_area(get_current_palette()) {
+ m_area(get_current_palette(),
+ loop->getLayer()->isBackground() ? -1: loop->sprite()->transparentColor()) {
}
void processPixel(int x, int y) {
m_area.reset();
get_neighboring_pixels(m_srcImage, x, y, 3, 3, 1, 1, m_tiledMode, m_area);
- if (m_area.count > 0 && m_area.a/9 >= 128) {
+ if (m_area.count > 0) {
m_area.r /= m_area.count;
m_area.g /= m_area.count;
m_area.b /= m_area.count;
+ m_area.a /= 9;
uint32_t color32 = m_palette->getEntry(*m_srcAddress);
m_area.r = rgba_getr(color32) + (m_area.r-rgba_getr(color32)) * m_opacity / 255;
m_area.g = rgba_getg(color32) + (m_area.g-rgba_getg(color32)) * m_opacity / 255;
m_area.b = rgba_getb(color32) + (m_area.b-rgba_getb(color32)) * m_opacity / 255;
+ m_area.a = rgba_geta(color32) + (m_area.a-rgba_geta(color32)) * m_opacity / 255;
- *m_dstAddress = m_rgbmap->mapColor(m_area.r, m_area.g, m_area.b);
+ *m_dstAddress = m_rgbmap->mapColor(m_area.r, m_area.g, m_area.b, m_area.a);
}
else {
*m_dstAddress = *m_srcAddress;
@@ -415,20 +428,27 @@ private:
struct GetPixelsDelegate {
const Palette* pal;
int count, r, g, b, a;
+ color_t maskColor;
- GetPixelsDelegate(const Palette* pal) : pal(pal) { }
+ GetPixelsDelegate(const Palette* pal,
+ color_t maskColor)
+ : pal(pal), maskColor(maskColor) { }
void reset() { count = r = g = b = a = 0; }
void operator()(IndexedTraits::pixel_t color)
{
- a += (color == 0 ? 0: 255);
+ if (color == maskColor)
+ return;
uint32_t color32 = pal->getEntry(color);
- r += rgba_getr(color32);
- g += rgba_getg(color32);
- b += rgba_getb(color32);
- count++;
+ if (rgba_geta(color32) > 0) {
+ r += rgba_getr(color32);
+ g += rgba_getg(color32);
+ b += rgba_getb(color32);
+ a += rgba_geta(color32);
+ ++count;
+ }
}
};
@@ -510,7 +530,7 @@ public:
m_palette->getEntry(*m_srcAddress), m_color2, m_opacity);
*m_dstAddress = m_rgbmap->mapColor(
- rgba_getr(c), rgba_getg(c), rgba_getb(c));
+ rgba_getr(c), rgba_getg(c), rgba_getb(c), rgba_geta(c));
}
}
}
@@ -610,7 +630,8 @@ void JumbleInkProcessing::processPixel(int x, int y)
if (rgba_geta(c) >= 128)
*m_dstAddress = m_rgbmap->mapColor(rgba_getr(c),
rgba_getg(c),
- rgba_getb(c));
+ rgba_getb(c),
+ rgba_geta(c));
else
*m_dstAddress = 0;
}
@@ -692,7 +713,8 @@ public:
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));
+ rgba_getb(c),
+ rgba_geta(c));
}
private:
@@ -819,7 +841,7 @@ void BrushInkProcessing::processPixel(int x, int y) {
switch (m_brushImage->pixelFormat()) {
case IMAGE_RGB: {
c = get_pixel_fast(m_brushImage, x, y);
- c = m_palette->findBestfit(rgba_getr(c), rgba_getg(c), rgba_getb(c));
+ c = m_palette->findBestfit(rgba_getr(c), rgba_getg(c), rgba_getb(c), rgba_geta(c), 0);
break;
}
case IMAGE_INDEXED: {
@@ -828,8 +850,7 @@ void BrushInkProcessing::processPixel(int x, int y) {
}
case IMAGE_GRAYSCALE: {
c = get_pixel_fast(m_brushImage, x, y);
- c = graya_getv(c);
- c = m_palette->findBestfit(c, c, c);
+ c = m_palette->findBestfit(graya_getv(c), graya_getv(c), graya_getv(c), graya_geta(c), 0);
break;
}
case IMAGE_BITMAP: {
diff --git a/src/app/tools/ink_type.h b/src/app/tools/ink_type.h
index 6acadcdc9..a7934fc45 100644
--- a/src/app/tools/ink_type.h
+++ b/src/app/tools/ink_type.h
@@ -14,7 +14,8 @@ namespace tools {
enum class InkType {
DEFAULT = 0,
- SET_ALPHA = 1,
+ REPLACE_PIXEL = 0,
+ ALPHA_COMPOSITING = 1,
LOCK_ALPHA = 2,
};
diff --git a/src/app/tools/inks.h b/src/app/tools/inks.h
index 9cad96d41..8132f2f60 100644
--- a/src/app/tools/inks.h
+++ b/src/app/tools/inks.h
@@ -62,11 +62,7 @@ public:
case Opaque: m_proc = ink_processing[INK_OPAQUE][depth]; break;
case SetAlpha: m_proc = ink_processing[INK_SETALPHA][depth]; break;
case LockAlpha: m_proc = ink_processing[INK_LOCKALPHA][depth]; break;
- default:
- m_proc = (loop->getOpacity() == 255 ?
- ink_processing[INK_OPAQUE][depth]:
- ink_processing[INK_TRANSPARENT][depth]);
- break;
+ default: m_proc = ink_processing[INK_TRANSPARENT][depth]; break;
}
}
}
diff --git a/src/app/ui/color_bar.cpp b/src/app/ui/color_bar.cpp
index 0e5f9bfb5..739e4efa1 100644
--- a/src/app/ui/color_bar.cpp
+++ b/src/app/ui/color_bar.cpp
@@ -805,11 +805,13 @@ void ColorBar::onFixWarningClick(ColorButton* colorButton, ui::Button* warningIc
color_t color = doc::rgba(
appColor.getRed(),
appColor.getGreen(),
- appColor.getBlue(), 255);
+ appColor.getBlue(),
+ appColor.getAlpha());
int index = newPalette->findExactMatch(
appColor.getRed(),
appColor.getGreen(),
- appColor.getBlue());
+ appColor.getBlue(),
+ appColor.getAlpha());
// It should be -1, because the user has pressed the warning
// button that is available only when the color isn't in the
@@ -853,7 +855,8 @@ void ColorBar::updateWarningIcon(const app::Color& color, ui::Button* warningIco
int index = get_current_palette()->findExactMatch(
color.getRed(),
color.getGreen(),
- color.getBlue());
+ color.getBlue(),
+ color.getAlpha());
warningIcon->setVisible(index < 0);
warningIcon->getParent()->layout();
diff --git a/src/app/ui/color_button.cpp b/src/app/ui/color_button.cpp
index 306ebdc9b..e2204d3bd 100644
--- a/src/app/ui/color_button.cpp
+++ b/src/app/ui/color_button.cpp
@@ -95,7 +95,7 @@ bool ColorButton::onProcessMessage(Message* msg)
break;
case kMouseEnterMessage:
- StatusBar::instance()->showColor(0, "", m_color, 255);
+ StatusBar::instance()->showColor(0, "", m_color);
break;
case kMouseLeaveMessage:
diff --git a/src/app/ui/color_selector.cpp b/src/app/ui/color_selector.cpp
index 9b58eeaed..152ec92b7 100644
--- a/src/app/ui/color_selector.cpp
+++ b/src/app/ui/color_selector.cpp
@@ -159,20 +159,27 @@ void ColorSelector::onColorHexEntryChange(const app::Color& color)
void ColorSelector::onColorTypeClick()
{
- app::Color newColor;
+ app::Color newColor = getColor();
switch (m_colorType.selectedItem()) {
case INDEX_MODE:
- newColor = app::Color::fromIndex(getColor().getIndex());
+ newColor = app::Color::fromIndex(newColor.getIndex());
break;
case RGB_MODE:
- newColor = app::Color::fromRgb(getColor().getRed(), getColor().getGreen(), getColor().getBlue());
+ newColor = app::Color::fromRgb(newColor.getRed(),
+ newColor.getGreen(),
+ newColor.getBlue(),
+ newColor.getAlpha());
break;
case HSB_MODE:
- newColor = app::Color::fromHsv(getColor().getHue(), getColor().getSaturation(), getColor().getValue());
+ newColor = app::Color::fromHsv(newColor.getHue(),
+ newColor.getSaturation(),
+ newColor.getValue(),
+ newColor.getAlpha());
break;
case GRAY_MODE:
- newColor = app::Color::fromGray(getColor().getGray());
+ newColor = app::Color::fromGray(newColor.getGray(),
+ newColor.getAlpha());
break;
case MASK_MODE:
newColor = app::Color::fromMask();
@@ -194,10 +201,11 @@ void ColorSelector::findBestfitIndex(const app::Color& color)
int r = color.getRed();
int g = color.getGreen();
int b = color.getBlue();
+ int a = color.getAlpha();
// Search for the closest color to the RGB values
- int i = get_current_palette()->findBestfit(r, g, b);
- if (i >= 0 && i < 256) {
+ int i = get_current_palette()->findBestfit(r, g, b, a, 0);
+ if (i >= 0) {
m_colorPalette.deselect();
m_colorPalette.selectColor(i);
}
diff --git a/src/app/ui/color_sliders.cpp b/src/app/ui/color_sliders.cpp
index 7c9dd33b8..7302cb579 100644
--- a/src/app/ui/color_sliders.cpp
+++ b/src/app/ui/color_sliders.cpp
@@ -237,6 +237,7 @@ RgbSliders::RgbSliders()
addSlider(Red, "R", 0, 255);
addSlider(Green, "G", 0, 255);
addSlider(Blue, "B", 0, 255);
+ addSlider(Alpha, "A", 0, 255);
}
void RgbSliders::onSetColor(const app::Color& color)
@@ -244,13 +245,15 @@ void RgbSliders::onSetColor(const app::Color& color)
setAbsSliderValue(0, color.getRed());
setAbsSliderValue(1, color.getGreen());
setAbsSliderValue(2, color.getBlue());
+ setAbsSliderValue(3, color.getAlpha());
}
app::Color RgbSliders::getColorFromSliders()
{
return app::Color::fromRgb(getAbsSliderValue(0),
getAbsSliderValue(1),
- getAbsSliderValue(2));
+ getAbsSliderValue(2),
+ getAbsSliderValue(3));
}
//////////////////////////////////////////////////////////////////////
@@ -262,6 +265,7 @@ HsvSliders::HsvSliders()
addSlider(Hue, "H", 0, 360);
addSlider(Saturation, "S", 0, 100);
addSlider(Value, "B", 0, 100);
+ addSlider(Alpha, "A", 0, 255);
}
void HsvSliders::onSetColor(const app::Color& color)
@@ -269,13 +273,15 @@ void HsvSliders::onSetColor(const app::Color& color)
setAbsSliderValue(0, color.getHue());
setAbsSliderValue(1, color.getSaturation());
setAbsSliderValue(2, color.getValue());
+ setAbsSliderValue(3, color.getAlpha());
}
app::Color HsvSliders::getColorFromSliders()
{
return app::Color::fromHsv(getAbsSliderValue(0),
getAbsSliderValue(1),
- getAbsSliderValue(2));
+ getAbsSliderValue(2),
+ getAbsSliderValue(3));
}
//////////////////////////////////////////////////////////////////////
@@ -284,18 +290,20 @@ app::Color HsvSliders::getColorFromSliders()
GraySlider::GraySlider()
: ColorSliders()
{
- addSlider(Gray, "V", 0, 255);
+ addSlider(Gray, "V", 0, 255);
+ addSlider(Alpha, "A", 0, 255);
}
void GraySlider::onSetColor(const app::Color& color)
{
setAbsSliderValue(0, color.getGray());
+ setAbsSliderValue(1, color.getAlpha());
}
-
app::Color GraySlider::getColorFromSliders()
{
- return app::Color::fromGray(getAbsSliderValue(0));
+ return app::Color::fromGray(getAbsSliderValue(0),
+ getAbsSliderValue(1));
}
} // namespace app
diff --git a/src/app/ui/color_sliders.h b/src/app/ui/color_sliders.h
index c60817df8..8c0a6a398 100644
--- a/src/app/ui/color_sliders.h
+++ b/src/app/ui/color_sliders.h
@@ -31,7 +31,8 @@ namespace app {
public:
enum Channel { Red, Green, Blue,
Hue, Saturation, Value,
- Gray };
+ Gray,
+ Alpha };
enum Mode { Absolute, Relative };
ColorSliders();
diff --git a/src/app/ui/color_spectrum.cpp b/src/app/ui/color_spectrum.cpp
index 525c1187b..76e9ea9ef 100644
--- a/src/app/ui/color_spectrum.cpp
+++ b/src/app/ui/color_spectrum.cpp
@@ -135,7 +135,7 @@ bool ColorSpectrum::onProcessMessage(ui::Message* msg)
app::Color color = pickColor(mouseMsg->position());
if (color != app::Color::fromMask()) {
- StatusBar::instance()->showColor(0, "", color, 255);
+ StatusBar::instance()->showColor(0, "", color);
if (hasCapture())
ColorChange(color, mouseMsg->buttons());
}
diff --git a/src/app/ui/context_bar.cpp b/src/app/ui/context_bar.cpp
index 8459bfcec..10b34069d 100644
--- a/src/app/ui/context_bar.cpp
+++ b/src/app/ui/context_bar.cpp
@@ -42,6 +42,7 @@
#include "ui/int_entry.h"
#include "ui/label.h"
#include "ui/listitem.h"
+#include "ui/menu.h"
#include "ui/popup_window.h"
#include "ui/preferred_size_event.h"
#include "ui/theme.h"
@@ -147,6 +148,7 @@ private:
void closePopup() {
m_popupWindow.closeWindow(NULL);
+ deselectItems();
}
void onBrushChange(const BrushRef& brush) {
@@ -319,66 +321,67 @@ protected:
}
};
-class ContextBar::InkTypeField : public ComboBox
+class ContextBar::InkTypeField : public ButtonSet
{
public:
- InkTypeField() : m_lock(false) {
- // The same order as in InkType
- addItem("Default Ink");
-#if 0
- addItem("Opaque");
-#endif
- addItem("Set Alpha");
- addItem("Lock Alpha");
-#if 0
- addItem("Merge");
- addItem("Shading");
- addItem("Replace");
- addItem("Erase");
- addItem("Selection");
- addItem("Blur");
- addItem("Jumble");
-#endif
+ InkTypeField(ContextBar* owner) : ButtonSet(1)
+ , m_owner(owner) {
+ addItem(
+ static_cast(getTheme())->get_part(PART_INK_DEFAULT));
}
void setInkType(InkType inkType) {
- int index = 0;
+ int part = PART_INK_DEFAULT;
switch (inkType) {
- case InkType::DEFAULT: index = 0; break;
- case InkType::SET_ALPHA: index = 1; break;
- case InkType::LOCK_ALPHA: index = 2; break;
+ case InkType::REPLACE_PIXEL: part = PART_INK_DEFAULT; break;
+ case InkType::ALPHA_COMPOSITING: part = PART_INK_COMPOSITE; break;
+ case InkType::LOCK_ALPHA: part = PART_INK_LOCK_ALPHA; break;
}
- m_lock = true;
- setSelectedItemIndex(index);
- m_lock = false;
+ getItem(0)->setIcon(
+ static_cast(getTheme())->get_part(part));
}
protected:
- void onChange() override {
- ComboBox::onChange();
+ void onItemChange() override {
+ ButtonSet::onItemChange();
- if (m_lock)
- return;
+ gfx::Rect bounds = getBounds();
- InkType inkType = InkType::DEFAULT;
-
- switch (getSelectedItemIndex()) {
- case 0: inkType = InkType::DEFAULT; break;
- case 1: inkType = InkType::SET_ALPHA; break;
- case 2: inkType = InkType::LOCK_ALPHA; break;
- }
+ Menu menu;
+ MenuItem
+ replace("Replace Pixel"),
+ alphacompo("Alpha Compositing"),
+ lockalpha("Lock Alpha");
+ menu.addChild(&replace);
+ menu.addChild(&alphacompo);
+ menu.addChild(&lockalpha);
+ Tool* tool = App::instance()->activeTool();
+ switch (Preferences::instance().tool(tool).ink()) {
+ case tools::InkType::REPLACE_PIXEL: replace.setSelected(true); break;
+ case tools::InkType::ALPHA_COMPOSITING: alphacompo.setSelected(true); break;
+ case tools::InkType::LOCK_ALPHA: lockalpha.setSelected(true); break;
+ }
+
+ replace.Click.connect(Bind(&InkTypeField::selectInk, this, InkType::REPLACE_PIXEL));
+ alphacompo.Click.connect(Bind(&InkTypeField::selectInk, this, InkType::ALPHA_COMPOSITING));
+ lockalpha.Click.connect(Bind(&InkTypeField::selectInk, this, InkType::LOCK_ALPHA));
+
+ menu.showPopup(gfx::Point(bounds.x, bounds.y+bounds.h));
+
+ deselectItems();
+ }
+
+ void selectInk(InkType inkType) {
Tool* tool = App::instance()->activeTool();
Preferences::instance().tool(tool).ink(inkType);
+
+ m_owner->updateForCurrentTool();
}
- void onCloseListBox() override {
- releaseFocus();
- }
-
- bool m_lock;
+ ContextBar* m_owner;
};
class ContextBar::InkOpacityField : public IntEntry
@@ -742,21 +745,51 @@ protected:
}
};
-class ContextBar::GrabAlphaField : public CheckBox
+class ContextBar::EyedropperField : public HBox
{
public:
- GrabAlphaField() : CheckBox("Grab Alpha") {
- setup_mini_font(this);
+ EyedropperField() {
+ m_channel.addItem("Color+Alpha");
+ m_channel.addItem("Color");
+ m_channel.addItem("Alpha");
+ m_channel.addItem("RGB+Alpha");
+ m_channel.addItem("RGB");
+ m_channel.addItem("HSB+Alpha");
+ m_channel.addItem("HSB");
+ m_channel.addItem("Gray+Alpha");
+ m_channel.addItem("Gray");
+ m_channel.addItem("Best fit Index");
+
+ m_sample.addItem("All Layers");
+ m_sample.addItem("Current Layer");
+
+ addChild(new Label("Pick:"));
+ addChild(&m_channel);
+ addChild(new Label("Sample:"));
+ addChild(&m_sample);
+
+ m_channel.Change.connect(Bind(&EyedropperField::onChannelChange, this));
+ m_sample.Change.connect(Bind(&EyedropperField::onSampleChange, this));
}
-protected:
- void onClick(Event& ev) override {
- CheckBox::onClick(ev);
-
- Preferences::instance().editor.grabAlpha(isSelected());
-
- releaseFocus();
+ void updateFromPreferences(app::Preferences::Eyedropper& prefEyedropper) {
+ m_channel.setSelectedItemIndex((int)prefEyedropper.channel());
+ m_sample.setSelectedItemIndex((int)prefEyedropper.sample());
}
+
+private:
+ void onChannelChange() {
+ Preferences::instance().eyedropper.channel(
+ (app::gen::EyedropperChannel)m_channel.getSelectedItemIndex());
+ }
+
+ void onSampleChange() {
+ Preferences::instance().eyedropper.sample(
+ (app::gen::EyedropperSample)m_sample.getSelectedItemIndex());
+ }
+
+ ComboBox m_channel;
+ ComboBox m_sample;
};
class ContextBar::AutoSelectLayerField : public CheckBox
@@ -802,12 +835,12 @@ ContextBar::ContextBar()
addChild(m_contiguous = new ContiguousField());
addChild(m_stopAtGrid = new StopAtGridField());
- addChild(m_inkType = new InkTypeField());
+ addChild(m_inkType = new InkTypeField(this));
- addChild(m_opacityLabel = new Label("Opacity:"));
+ addChild(m_inkOpacityLabel = new Label("Opacity:"));
addChild(m_inkOpacity = new InkOpacityField());
- addChild(m_grabAlpha = new GrabAlphaField());
+ addChild(m_eyedropperField = new EyedropperField());
addChild(m_autoSelectLayer = new AutoSelectLayerField());
@@ -832,7 +865,7 @@ ContextBar::ContextBar()
m_freehandBox->addChild(m_freehandAlgo = new FreehandAlgorithmField());
setup_mini_font(m_toleranceLabel);
- setup_mini_font(m_opacityLabel);
+ setup_mini_font(m_inkOpacityLabel);
TooltipManager* tooltipManager = new TooltipManager();
addChild(tooltipManager);
@@ -840,17 +873,13 @@ ContextBar::ContextBar()
tooltipManager->addTooltipFor(m_brushType, "Brush Type", BOTTOM);
tooltipManager->addTooltipFor(m_brushSize, "Brush Size (in pixels)", BOTTOM);
tooltipManager->addTooltipFor(m_brushAngle, "Brush Angle (in degrees)", BOTTOM);
- tooltipManager->addTooltipFor(m_inkOpacity, "Opacity (Alpha value in RGBA)", BOTTOM);
+ tooltipManager->addTooltipFor(m_inkType, "Ink", BOTTOM);
+ tooltipManager->addTooltipFor(m_inkOpacity, "Opacity (paint intensity)", BOTTOM);
tooltipManager->addTooltipFor(m_sprayWidth, "Spray Width", BOTTOM);
tooltipManager->addTooltipFor(m_spraySpeed, "Spray Speed", BOTTOM);
tooltipManager->addTooltipFor(m_transparentColor, "Transparent Color", BOTTOM);
tooltipManager->addTooltipFor(m_rotAlgo, "Rotation Algorithm", BOTTOM);
tooltipManager->addTooltipFor(m_freehandAlgo, "Freehand trace algorithm", BOTTOM);
- tooltipManager->addTooltipFor(m_grabAlpha,
- "When checked the tool picks the color from the active layer, and its alpha\n"
- "component is used to setup the opacity level of all drawing tools.\n\n"
- "When unchecked -the default behavior- the color is picked\n"
- "from the composition of all sprite layers.", LEFT | TOP);
m_brushType->setupTooltips(tooltipManager);
m_selectionMode->setupTooltips(tooltipManager);
@@ -939,6 +968,25 @@ void ContextBar::updateForTool(tools::Tool* tool)
m_brushPatternField->setBrushPattern(
preferences.brush.pattern());
+ // Tool ink
+ bool isPaint = tool &&
+ (tool->getInk(0)->isPaint() ||
+ tool->getInk(1)->isPaint());
+ bool isEffect = tool &&
+ (tool->getInk(0)->isEffect() ||
+ tool->getInk(1)->isEffect());
+
+ // True if the current tool support opacity slider
+ bool supportOpacity = (isPaint || isEffect);
+
+ // True if it makes sense to change the ink property for the current
+ // tool.
+ bool hasInk = tool &&
+ ((tool->getInk(0)->isPaint() && !tool->getInk(0)->isEffect()) ||
+ (tool->getInk(1)->isPaint() && !tool->getInk(1)->isEffect()));
+
+ bool hasInkWithOpacity = false;
+
if (toolPref) {
m_tolerance->setTextf("%d", toolPref->tolerance());
m_contiguous->setSelected(toolPref->contiguous());
@@ -948,22 +996,19 @@ void ContextBar::updateForTool(tools::Tool* tool)
m_inkType->setInkType(toolPref->ink());
m_inkOpacity->setTextf("%d", toolPref->opacity());
+ hasInkWithOpacity =
+ ((isPaint && toolPref->ink() != tools::InkType::REPLACE_PIXEL) ||
+ (isEffect));
+
m_freehandAlgo->setFreehandAlgorithm(toolPref->freehandAlgorithm());
m_sprayWidth->setValue(toolPref->spray.width());
m_spraySpeed->setValue(toolPref->spray.speed());
}
- m_grabAlpha->setSelected(preferences.editor.grabAlpha());
+ m_eyedropperField->updateFromPreferences(preferences.eyedropper);
m_autoSelectLayer->setSelected(preferences.editor.autoSelectLayer());
- // True if the current tool needs opacity options
- bool hasOpacity = tool &&
- (tool->getInk(0)->isPaint() ||
- tool->getInk(0)->isEffect() ||
- tool->getInk(1)->isPaint() ||
- tool->getInk(1)->isEffect());
-
// True if we have an image as brush
bool hasImageBrush = (activeBrush()->type() == kImageBrushType);
@@ -977,10 +1022,6 @@ void ContextBar::updateForTool(tools::Tool* tool)
(tool->getInk(0)->isCelMovement() ||
tool->getInk(1)->isCelMovement());
- // True if it makes sense to change the ink property for the current
- // tool.
- bool hasInk = hasOpacity;
-
// True if the current tool is floodfill
bool isFloodfill = tool &&
(tool->getPointShape(0)->isFloodFill() ||
@@ -1005,16 +1046,16 @@ void ContextBar::updateForTool(tools::Tool* tool)
tool->getController(1)->isFreehand());
// Show/Hide fields
- m_brushType->setVisible(hasOpacity && (!isFloodfill || (isFloodfill && hasImageBrush)));
- m_brushSize->setVisible(hasOpacity && !isFloodfill && !hasImageBrush);
- m_brushAngle->setVisible(hasOpacity && !isFloodfill && !hasImageBrush);
- m_brushPatternField->setVisible(hasOpacity && hasImageBrush);
- m_opacityLabel->setVisible(hasOpacity);
+ m_brushType->setVisible(supportOpacity && (!isFloodfill || (isFloodfill && hasImageBrush)));
+ m_brushSize->setVisible(supportOpacity && !isFloodfill && !hasImageBrush);
+ m_brushAngle->setVisible(supportOpacity && !isFloodfill && !hasImageBrush);
+ m_brushPatternField->setVisible(supportOpacity && hasImageBrush);
m_inkType->setVisible(hasInk && !hasImageBrush);
- m_inkOpacity->setVisible(hasOpacity);
- m_grabAlpha->setVisible(isEyedropper);
+ m_inkOpacityLabel->setVisible(hasInkWithOpacity && supportOpacity);
+ m_inkOpacity->setVisible(hasInkWithOpacity && supportOpacity);
+ m_eyedropperField->setVisible(isEyedropper);
m_autoSelectLayer->setVisible(isMove);
- m_freehandBox->setVisible(isFreehand && hasOpacity);
+ m_freehandBox->setVisible(isFreehand && supportOpacity);
m_toleranceLabel->setVisible(hasTolerance);
m_tolerance->setVisible(hasTolerance);
m_contiguous->setVisible(hasTolerance);
diff --git a/src/app/ui/context_bar.h b/src/app/ui/context_bar.h
index de172e3ed..f30fa213c 100644
--- a/src/app/ui/context_bar.h
+++ b/src/app/ui/context_bar.h
@@ -106,7 +106,7 @@ namespace app {
class RotAlgorithmField;
class FreehandAlgorithmField;
class BrushPatternField;
- class GrabAlphaField;
+ class EyedropperField;
class DropPixelsField;
class AutoSelectLayerField;
@@ -118,9 +118,9 @@ namespace app {
ContiguousField* m_contiguous;
StopAtGridField* m_stopAtGrid;
InkTypeField* m_inkType;
- ui::Label* m_opacityLabel;
+ ui::Label* m_inkOpacityLabel;
InkOpacityField* m_inkOpacity;
- GrabAlphaField* m_grabAlpha;
+ EyedropperField* m_eyedropperField;
AutoSelectLayerField* m_autoSelectLayer;
ui::Box* m_freehandBox;
FreehandAlgorithmField* m_freehandAlgo;
diff --git a/src/app/ui/editor/editor.cpp b/src/app/ui/editor/editor.cpp
index 6f42482de..734f4fcc7 100644
--- a/src/app/ui/editor/editor.cpp
+++ b/src/app/ui/editor/editor.cpp
@@ -916,49 +916,21 @@ tools::Ink* Editor::getCurrentEditorInk()
break;
}
}
- else {
+ // Only paint tools can have different inks
+ else if (ink->isPaint() && !ink->isEffect()) {
tools::InkType inkType = Preferences::instance().tool(tool).ink();
const char* id = NULL;
switch (inkType) {
- case tools::InkType::DEFAULT:
- // Do nothing
+ case tools::InkType::REPLACE_PIXEL:
+ id = tools::WellKnownInks::PaintOpaque;
break;
- case tools::InkType::SET_ALPHA:
- id = tools::WellKnownInks::PaintSetAlpha;
+ case tools::InkType::ALPHA_COMPOSITING:
+ id = tools::WellKnownInks::Paint;
break;
case tools::InkType::LOCK_ALPHA:
id = tools::WellKnownInks::PaintLockAlpha;
break;
-#if 0
- case tools::InkType::OPAQUE:
- id = tools::WellKnownInks::PaintOpaque;
- break;
- case tools::InkType::MERGE:
- id = tools::WellKnownInks::Paint;
- break;
- case tools::InkType::SHADING:
- id = tools::WellKnownInks::Shading;
- break;
- case tools::InkType::REPLACE:
- if (!m_secondaryButton)
- id = tools::WellKnownInks::ReplaceBgWithFg;
- else
- id = tools::WellKnownInks::ReplaceFgWithBg;
- break;
- case tools::InkType::ERASER:
- id = tools::WellKnownInks::Eraser;
- break;
- case tools::InkType::SELECTION:
- id = tools::WellKnownInks::Selection;
- break;
- case tools::InkType::BLUR:
- id = tools::WellKnownInks::Blur;
- break;
- case tools::InkType::JUMBLE:
- id = tools::WellKnownInks::Jumble;
- break;
-#endif
}
if (id)
diff --git a/src/app/ui/editor/standby_state.cpp b/src/app/ui/editor/standby_state.cpp
index ba2d0e141..972fa2852 100644
--- a/src/app/ui/editor/standby_state.cpp
+++ b/src/app/ui/editor/standby_state.cpp
@@ -14,6 +14,7 @@
#include "app/app.h"
#include "app/color_picker.h"
#include "app/commands/commands.h"
+#include "app/commands/cmd_eyedropper.h"
#include "app/commands/params.h"
#include "app/ini_file.h"
#include "app/pref/preferences.h"
@@ -363,18 +364,14 @@ bool StandbyState::onUpdateStatusBar(Editor* editor)
}
// For eye-dropper
else if (ink->isEyedropper()) {
- bool grabAlpha = Preferences::instance().editor.grabAlpha();
- ColorPicker picker;
- picker.pickColor(editor->getSite(),
- spritePos,
- grabAlpha ?
- ColorPicker::FromActiveLayer:
- ColorPicker::FromComposition);
+ EyedropperCommand cmd;
+ app::Color color = Preferences::instance().colorBar.fgColor();
+ cmd.pickSample(editor->getSite(), spritePos, color);
char buf[256];
sprintf(buf, "- Pos %d %d", spritePos.x, spritePos.y);
- StatusBar::instance()->showColor(0, buf, picker.color(), picker.alpha());
+ StatusBar::instance()->showColor(0, buf, color);
}
else {
Mask* mask =
diff --git a/src/app/ui/palette_view.cpp b/src/app/ui/palette_view.cpp
index 59580ac58..4f1ab5d19 100644
--- a/src/app/ui/palette_view.cpp
+++ b/src/app/ui/palette_view.cpp
@@ -14,6 +14,7 @@
#include "app/color_utils.h"
#include "app/commands/commands.h"
#include "app/modules/editors.h"
+#include "app/modules/gfx.h"
#include "app/modules/gui.h"
#include "app/modules/palettes.h"
#include "app/ui/editor/editor.h"
@@ -311,8 +312,8 @@ bool PaletteView::onProcessMessage(Message* msg)
int idx = m_hot.color;
idx = MID(0, idx, currentPalette()->size()-1);
- StatusBar::instance()->showColor(0, "",
- app::Color::fromIndex(idx), 255);
+ StatusBar::instance()->showColor(
+ 0, "", app::Color::fromIndex(idx));
MouseButtons buttons = mouseMsg->buttons();
if (hasCapture() && ((idx != m_currentEntry) ||
@@ -422,37 +423,44 @@ void PaletteView::onPaint(ui::PaintEvent& ev)
// Draw palette entries
for (int i=0; isize(); ++i) {
gfx::Rect box = getPaletteEntryBounds(i);
- gfx::Color color = gfx::rgba(
- rgba_getr(palette->getEntry(i)),
- rgba_getg(palette->getEntry(i)),
- rgba_getb(palette->getEntry(i)));
+ doc::color_t palColor = palette->getEntry(i);
+ app::Color appColor = app::Color::fromRgb(
+ rgba_getr(palColor),
+ rgba_getg(palColor),
+ rgba_getb(palColor),
+ rgba_geta(palColor));
+ gfx::Color gfxColor = gfx::rgba(
+ rgba_getr(palColor),
+ rgba_getg(palColor),
+ rgba_getb(palColor),
+ rgba_geta(palColor));
g->drawRect(gfx::rgba(0, 0, 0), gfx::Rect(box).enlarge(guiscale()));
- g->fillRect(color, box);
+ draw_color(g, box, appColor);
switch (m_style) {
case SelectOneColor:
if (m_currentEntry == i)
- g->fillRect(color_utils::blackandwhite_neg(color),
+ g->fillRect(color_utils::blackandwhite_neg(gfxColor),
gfx::Rect(box.getCenter(), gfx::Size(1, 1)));
break;
case FgBgColors:
if (fgIndex == i) {
- gfx::Color neg = color_utils::blackandwhite_neg(color);
+ gfx::Color neg = color_utils::blackandwhite_neg(gfxColor);
for (int i=0; idrawHLine(neg, box.x, box.y+i, m_boxsize/2-i);
}
if (bgIndex == i) {
- gfx::Color neg = color_utils::blackandwhite_neg(color);
+ gfx::Color neg = color_utils::blackandwhite_neg(gfxColor);
for (int i=0; idrawHLine(neg, box.x+box.w-(i+1), box.y+box.h-m_boxsize/4+i, i+1);
}
if (transparentIndex == i)
- g->fillRect(color_utils::blackandwhite_neg(color),
+ g->fillRect(color_utils::blackandwhite_neg(gfxColor),
gfx::Rect(box.getCenter(), gfx::Size(1, 1)));
break;
}
@@ -804,7 +812,7 @@ int PaletteView::findExactIndex(const app::Color& color) const
case Color::RgbType:
case Color::HsvType:
case Color::GrayType:
- return currentPalette()->findExactMatch(color.getRed(), color.getGreen(), color.getBlue());
+ return currentPalette()->findExactMatch(color.getRed(), color.getGreen(), color.getBlue(), color.getAlpha());
case Color::IndexType:
return color.getIndex();
diff --git a/src/app/ui/skin/skin_parts.h b/src/app/ui/skin/skin_parts.h
index 7b589a884..118c337b1 100644
--- a/src/app/ui/skin/skin_parts.h
+++ b/src/app/ui/skin/skin_parts.h
@@ -176,6 +176,10 @@ namespace app {
PART_FREEHAND_ALGO_DOTS,
PART_FREEHAND_ALGO_DOTS_SELECTED,
+ PART_INK_DEFAULT,
+ PART_INK_COMPOSITE,
+ PART_INK_LOCK_ALPHA,
+
PARTS
};
diff --git a/src/app/ui/skin/skin_theme.cpp b/src/app/ui/skin/skin_theme.cpp
index c1bbd9fd0..fb3fce864 100644
--- a/src/app/ui/skin/skin_theme.cpp
+++ b/src/app/ui/skin/skin_theme.cpp
@@ -278,6 +278,9 @@ SkinTheme::SkinTheme()
sheet_mapping["freehand_algo_pixel_perfect_selected"] = PART_FREEHAND_ALGO_PIXEL_PERFECT_SELECTED;
sheet_mapping["freehand_algo_dots"] = PART_FREEHAND_ALGO_DOTS;
sheet_mapping["freehand_algo_dots_selected"] = PART_FREEHAND_ALGO_DOTS_SELECTED;
+ sheet_mapping["ink_default"] = PART_INK_DEFAULT;
+ sheet_mapping["ink_composite"] = PART_INK_COMPOSITE;
+ sheet_mapping["ink_lock_alpha"] = PART_INK_LOCK_ALPHA;
}
SkinTheme::~SkinTheme()
diff --git a/src/app/ui/status_bar.cpp b/src/app/ui/status_bar.cpp
index f37edd196..36c9a4b2d 100644
--- a/src/app/ui/status_bar.cpp
+++ b/src/app/ui/status_bar.cpp
@@ -286,12 +286,11 @@ void StatusBar::showTip(int msecs, const char *format, ...)
invalidate();
}
-void StatusBar::showColor(int msecs, const char* text, const app::Color& color, int alpha)
+void StatusBar::showColor(int msecs, const char* text, const app::Color& color)
{
if (setStatusText(msecs, text)) {
m_state = SHOW_COLOR;
m_color = color;
- m_alpha = alpha;
}
}
@@ -382,9 +381,9 @@ void StatusBar::onPaint(ui::PaintEvent& ev)
// Draw color description
std::string str = m_color.toHumanReadableString(app_get_current_pixel_format(),
app::Color::LongHumanReadableString);
- if (m_alpha < 255) {
+ if (m_color.getAlpha() < 255) {
char buf[256];
- sprintf(buf, " \xCE\xB1%d", m_alpha);
+ sprintf(buf, " \xCE\xB1%d", m_color.getAlpha());
str += buf;
}
diff --git a/src/app/ui/status_bar.h b/src/app/ui/status_bar.h
index 15d2a74c0..84c887fd6 100644
--- a/src/app/ui/status_bar.h
+++ b/src/app/ui/status_bar.h
@@ -54,7 +54,7 @@ namespace app {
bool setStatusText(int msecs, const char *format, ...);
void showTip(int msecs, const char *format, ...);
- void showColor(int msecs, const char* text, const Color& color, int alpha);
+ void showColor(int msecs, const char* text, const Color& color);
void showTool(int msecs, tools::Tool* tool);
protected:
@@ -87,7 +87,6 @@ namespace app {
// Showing a color
Color m_color;
- int m_alpha;
// Box of main commands
ui::Widget* m_commandsBox;
diff --git a/src/doc/CMakeLists.txt b/src/doc/CMakeLists.txt
index b21d72613..63eea5556 100644
--- a/src/doc/CMakeLists.txt
+++ b/src/doc/CMakeLists.txt
@@ -17,7 +17,6 @@ add_library(doc-lib
cel_data_io.cpp
cel_io.cpp
cels_range.cpp
- color_scales.cpp
compressed_image.cpp
context.cpp
conversion_she.cpp
diff --git a/src/doc/algorithm/resize_image.cpp b/src/doc/algorithm/resize_image.cpp
index c3565d6b2..16fed961e 100644
--- a/src/doc/algorithm/resize_image.cpp
+++ b/src/doc/algorithm/resize_image.cpp
@@ -18,7 +18,7 @@
namespace doc {
namespace algorithm {
-void resize_image(const Image* src, Image* dst, ResizeMethod method, const Palette* pal, const RgbMap* rgbmap)
+void resize_image(const Image* src, Image* dst, ResizeMethod method, const Palette* pal, const RgbMap* rgbmap, color_t maskColor)
{
switch (method) {
@@ -111,15 +111,23 @@ void resize_image(const Image* src, Image* dst, ResizeMethod method, const Palet
break;
}
case IMAGE_INDEXED: {
- int r = int((rgba_getr(pal->getEntry(color[0]))*u2 + rgba_getr(pal->getEntry(color[1]))*u1)*v2 +
- (rgba_getr(pal->getEntry(color[2]))*u2 + rgba_getr(pal->getEntry(color[3]))*u1)*v1);
- int g = int((rgba_getg(pal->getEntry(color[0]))*u2 + rgba_getg(pal->getEntry(color[1]))*u1)*v2 +
- (rgba_getg(pal->getEntry(color[2]))*u2 + rgba_getg(pal->getEntry(color[3]))*u1)*v1);
- int b = int((rgba_getb(pal->getEntry(color[0]))*u2 + rgba_getb(pal->getEntry(color[1]))*u1)*v2 +
- (rgba_getb(pal->getEntry(color[2]))*u2 + rgba_getb(pal->getEntry(color[3]))*u1)*v1);
- int a = int(((color[0] == 0 ? 0: 255)*u2 + (color[1] == 0 ? 0: 255)*u1)*v2 +
- ((color[2] == 0 ? 0: 255)*u2 + (color[3] == 0 ? 0: 255)*u1)*v1);
- dst_color = a > 127 ? rgbmap->mapColor(r, g, b): 0;
+ // Convert index to RGBA values
+ for (int i=0; i<4; ++i) {
+ if (color[i] == maskColor)
+ color[i] = pal->getEntry(color[i]) & rgba_rgb_mask; // Set alpha = 0
+ else
+ color[i] = pal->getEntry(color[i]);
+ }
+
+ int r = int((rgba_getr(color[0])*u2 + rgba_getr(color[1])*u1)*v2 +
+ (rgba_getr(color[2])*u2 + rgba_getr(color[3])*u1)*v1);
+ int g = int((rgba_getg(color[0])*u2 + rgba_getg(color[1])*u1)*v2 +
+ (rgba_getg(color[2])*u2 + rgba_getg(color[3])*u1)*v1);
+ int b = int((rgba_getb(color[0])*u2 + rgba_getb(color[1])*u1)*v2 +
+ (rgba_getb(color[2])*u2 + rgba_getb(color[3])*u1)*v1);
+ int a = int((rgba_geta(color[0])*u2 + rgba_geta(color[1])*u1)*v2 +
+ (rgba_geta(color[2])*u2 + rgba_geta(color[3])*u1)*v1);
+ dst_color = rgbmap->mapColor(r, g, b, a);
break;
}
}
diff --git a/src/doc/algorithm/resize_image.h b/src/doc/algorithm/resize_image.h
index c3b1ac1a9..ee7a08589 100644
--- a/src/doc/algorithm/resize_image.h
+++ b/src/doc/algorithm/resize_image.h
@@ -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,6 +8,7 @@
#define DOC_ALGORITHM_RESIZE_IMAGE_H_INCLUDED
#pragma once
+#include "doc/color.h"
#include "gfx/fwd.h"
namespace doc {
@@ -27,7 +28,8 @@ namespace doc {
// Warning: If you are using the RESIZE_METHOD_BILINEAR, it is
// recommended to use 'fixup_image_transparent_colors' function
// over the source image 'src' BEFORE using this routine.
- void resize_image(const Image* src, Image* dst, ResizeMethod method, const Palette* palette, const RgbMap* rgbmap);
+ void resize_image(const Image* src, Image* dst, ResizeMethod method, const Palette* palette, const RgbMap* rgbmap,
+ color_t maskColor);
// It does not modify the image to the human eye, but internally
// tries to fixup all colors that are completelly transparent
diff --git a/src/doc/color_scales.cpp b/src/doc/color_scales.cpp
deleted file mode 100644
index 5a5039b5c..000000000
--- a/src/doc/color_scales.cpp
+++ /dev/null
@@ -1,48 +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 "base/clamp.h"
-
-// Based on Allegro _rgb_scale_5 and _rgb_scale_6 tables
-
-namespace doc {
-
-int scale_5bits_to_8bits(int channel5bits)
-{
- static int scale[32] = {
- 0, 8, 16, 24, 33, 41, 49, 57,
- 66, 74, 82, 90, 99, 107, 115, 123,
- 132, 140, 148, 156, 165, 173, 181, 189,
- 198, 206, 214, 222, 231, 239, 247, 255
- };
-
- ASSERT(channel5bits >= 0);
- ASSERT(channel5bits < 32);
- return scale[channel5bits];
-}
-
-int scale_6bits_to_8bits(int channel6bits)
-{
- static int scale[64] = {
- 0, 4, 8, 12, 16, 20, 24, 28,
- 32, 36, 40, 44, 48, 52, 56, 60,
- 65, 69, 73, 77, 81, 85, 89, 93,
- 97, 101, 105, 109, 113, 117, 121, 125,
- 130, 134, 138, 142, 146, 150, 154, 158,
- 162, 166, 170, 174, 178, 182, 186, 190,
- 195, 199, 203, 207, 211, 215, 219, 223,
- 227, 231, 235, 239, 243, 247, 251, 255
- };
- ASSERT(channel6bits >= 0);
- ASSERT(channel6bits < 64);
- return scale[channel6bits];
-}
-
-} // namespace doc
diff --git a/src/doc/color_scales.h b/src/doc/color_scales.h
index 1f79310c9..aa52d43eb 100644
--- a/src/doc/color_scales.h
+++ b/src/doc/color_scales.h
@@ -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.
@@ -15,8 +15,64 @@
namespace doc {
- int scale_5bits_to_8bits(int channel5bits);
- int scale_6bits_to_8bits(int channel6bits);
+ inline int scale_2bits_to_8bits(int channel2bits) {
+ static int scale[4] = {
+ 0, 85, 170, 255
+ };
+ ASSERT(channel2bits >= 0);
+ ASSERT(channel2bits < 4);
+ return scale[channel2bits];
+ }
+
+ inline int scale_3bits_to_8bits(int channel3bits) {
+ static int scale[8] = {
+ 0, 36, 72, 109,
+ 145, 182, 218, 255
+ };
+ ASSERT(channel3bits >= 0);
+ ASSERT(channel3bits < 8);
+ return scale[channel3bits];
+ }
+
+ inline int scale_4bits_to_8bits(int channel4bits) {
+ static int scale[16] = {
+ 0, 16, 34, 51,
+ 68, 85, 102, 119,
+ 136, 153, 170, 187,
+ 204, 221, 238, 255
+ };
+ ASSERT(channel4bits >= 0);
+ ASSERT(channel4bits < 16);
+ return scale[channel4bits];
+ }
+
+ inline int scale_5bits_to_8bits(int channel5bits) {
+ static int scale[32] = {
+ 0, 8, 16, 24, 33, 41, 49, 57,
+ 66, 74, 82, 90, 99, 107, 115, 123,
+ 132, 140, 148, 156, 165, 173, 181, 189,
+ 198, 206, 214, 222, 231, 239, 247, 255
+ };
+ ASSERT(channel5bits >= 0);
+ ASSERT(channel5bits < 32);
+ return scale[channel5bits];
+ }
+
+ inline int scale_6bits_to_8bits(int channel6bits) {
+ static int scale[64] = {
+ 0, 4, 8, 12, 16, 20, 24, 28,
+ 32, 36, 40, 44, 48, 52, 56, 60,
+ 65, 69, 73, 77, 81, 85, 89, 93,
+ 97, 101, 105, 109, 113, 117, 121, 125,
+ 130, 134, 138, 142, 146, 150, 154, 158,
+ 162, 166, 170, 174, 178, 182, 186, 190,
+ 195, 199, 203, 207, 211, 215, 219, 223,
+ 227, 231, 235, 239, 243, 247, 251, 255
+ };
+ ASSERT(channel6bits >= 0);
+ ASSERT(channel6bits < 64);
+ return scale[channel6bits];
+ }
} // namespace doc
diff --git a/src/doc/palette.cpp b/src/doc/palette.cpp
index 6f91143ae..f92623e2e 100644
--- a/src/doc/palette.cpp
+++ b/src/doc/palette.cpp
@@ -142,9 +142,9 @@ void Palette::makeBlack()
// Creates a linear ramp in the palette.
void Palette::makeGradient(int from, int to)
{
- int r, g, b;
- int r1, g1, b1;
- int r2, g2, b2;
+ int r, g, b, a;
+ int r1, g1, b1, a1;
+ int r2, g2, b2, a2;
int i, n;
ASSERT(from >= 0 && from <= 255);
@@ -160,22 +160,27 @@ void Palette::makeGradient(int from, int to)
r1 = rgba_getr(getEntry(from));
g1 = rgba_getg(getEntry(from));
b1 = rgba_getb(getEntry(from));
+ a1 = rgba_geta(getEntry(from));
+
r2 = rgba_getr(getEntry(to));
g2 = rgba_getg(getEntry(to));
b2 = rgba_getb(getEntry(to));
+ a2 = rgba_geta(getEntry(to));
for (i=from+1; i col_diff;
-static void bestfit_init()
+static void initBestfit()
{
- int i, k;
+ col_diff.resize(4*128, 0);
- for (i=1; i<64; i++) {
- k = i * i;
- col_diff[0 +i] = col_diff[0 +128-i] = k * (59 * 59);
- col_diff[128+i] = col_diff[128+128-i] = k * (30 * 30);
- col_diff[256+i] = col_diff[256+128-i] = k * (11 * 11);
+ for (int i=1; i<64; ++i) {
+ int k = i * i;
+ col_diff[0 +i] = col_diff[0 +128-i] = k * 59 * 59;
+ col_diff[128+i] = col_diff[128+128-i] = k * 30 * 30;
+ col_diff[256+i] = col_diff[256+128-i] = k * 11 * 11;
+ col_diff[384+i] = col_diff[384+128-i] = k * 8 * 8;
}
}
-int Palette::findBestfit(int r, int g, int b, int mask_index) const
+int Palette::findBestfit(int r, int g, int b, int a, int mask_index) const
{
-#ifdef __GNUC__
- register int bestfit asm("%eax");
-#else
- register int bestfit;
-#endif
- int i, coldiff, lowest;
-
ASSERT(r >= 0 && r <= 255);
ASSERT(g >= 0 && g <= 255);
ASSERT(b >= 0 && b <= 255);
+ ASSERT(a >= 0 && a <= 255);
- if (col_diff[1] == 0)
- bestfit_init();
-
- bestfit = 0;
- lowest = std::numeric_limits::max();
+ if (col_diff.empty())
+ initBestfit();
r >>= 3;
g >>= 3;
b >>= 3;
+ a >>= 3;
- i = 0;
- while (i < size()) {
+ // Mask index is like alpha = 0, so we can use it as transparent color.
+ if (a == 0 && mask_index >= 0)
+ return mask_index;
+
+ int bestfit = 0;
+ int lowest = std::numeric_limits::max();
+ int size = MIN(256, m_colors.size());
+
+ for (int i=0; i>3) - g) & 0x7F ];
+ int coldiff = col_diff[((rgba_getg(rgb)>>3) - g) & 0x7F];
if (coldiff < lowest) {
- coldiff += (col_diff + 128) [ ((rgba_getr(rgb)>>3) - r) & 0x7F ];
+ coldiff += col_diff[128 + (((rgba_getr(rgb)>>3) - r) & 0x7F)];
if (coldiff < lowest) {
- coldiff += (col_diff + 256) [ ((rgba_getb(rgb)>>3) - b) & 0x7F ];
- if (coldiff < lowest && i != mask_index) {
- bestfit = i;
- if (coldiff == 0)
- return bestfit;
- lowest = coldiff;
+ coldiff += col_diff[256 + (((rgba_getb(rgb)>>3) - b) & 0x7F)];
+ if (coldiff < lowest) {
+ coldiff += col_diff[384 + (((rgba_geta(rgb)>>3) - a) & 0x7F)];
+ if (coldiff < lowest && i != mask_index) {
+ if (coldiff == 0)
+ return i;
+
+ bestfit = i;
+ lowest = coldiff;
+ }
}
}
}
- i++;
}
return bestfit;
diff --git a/src/doc/palette.h b/src/doc/palette.h
index f5505a029..167a9f96c 100644
--- a/src/doc/palette.h
+++ b/src/doc/palette.h
@@ -77,8 +77,8 @@ namespace doc {
void makeGradient(int from, int to);
- int findExactMatch(int r, int g, int b) const;
- int findBestfit(int r, int g, int b, int mask_index = 0) const;
+ int findExactMatch(int r, int g, int b, int a) const;
+ int findBestfit(int r, int g, int b, int a, int mask_index) const;
private:
frame_t m_frame;
diff --git a/src/doc/resize_image_tests.cpp b/src/doc/resize_image_tests.cpp
index 7c04b36cb..0e9fb426a 100644
--- a/src/doc/resize_image_tests.cpp
+++ b/src/doc/resize_image_tests.cpp
@@ -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.
@@ -56,7 +56,7 @@ color_t test_image_scaled_9x9_bilinear[81] =
0x000000, 0x000000, 0x565656, 0xa9a9a9, 0xffffff, 0xa9a9a9, 0x565656, 0x000000, 0x000000
};
-Image* create_image_from_data(PixelFormat format, color_t* data, int width, int height)
+Image* create_image_from_data(PixelFormat format, color_t* data, int width, int height)
{
Image* new_image = Image::create(format, width, height);
for (int i = 0; i < width * height; i++) {
@@ -72,7 +72,7 @@ TEST(ResizeImage, NearestNeighborInterp)
Image* dst_expected = create_image_from_data(IMAGE_RGB, test_image_scaled_9x9_nearest, 9, 9);
Image* dst = Image::create(IMAGE_RGB, 9, 9);
- algorithm::resize_image(src, dst, algorithm::RESIZE_METHOD_NEAREST_NEIGHBOR, NULL, NULL);
+ algorithm::resize_image(src, dst, algorithm::RESIZE_METHOD_NEAREST_NEIGHBOR, NULL, NULL, -1);
ASSERT_EQ(0, count_diff_between_images(dst, dst_expected));
}
diff --git a/src/doc/rgbmap.cpp b/src/doc/rgbmap.cpp
index b53158b1d..112fb8c3e 100644
--- a/src/doc/rgbmap.cpp
+++ b/src/doc/rgbmap.cpp
@@ -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.
@@ -15,7 +15,11 @@
namespace doc {
-#define MAPSIZE 32*32*32
+#define RSIZE 32
+#define GSIZE 32
+#define BSIZE 32
+#define ASIZE 8
+#define MAPSIZE (RSIZE*GSIZE*BSIZE*ASIZE)
RgbMap::RgbMap()
: Object(ObjectType::RgbMap)
@@ -36,26 +40,23 @@ void RgbMap::regenerate(const Palette* palette, int mask_index)
m_palette = palette;
m_modifications = palette->getModifications();
+ // TODO This is slow for 256 colors 32*32*32*8 findBestfit calls
+
int i = 0;
- for (int r=0; r<32; ++r) {
- for (int g=0; g<32; ++g) {
- for (int b=0; b<32; ++b) {
- m_map[i++] =
- palette->findBestfit(
- scale_5bits_to_8bits(r),
- scale_5bits_to_8bits(g),
- scale_5bits_to_8bits(b), mask_index);
+ for (int r=0; rfindBestfit(
+ scale_5bits_to_8bits(r),
+ scale_5bits_to_8bits(g),
+ scale_5bits_to_8bits(b),
+ scale_3bits_to_8bits(a), mask_index);
+ }
}
}
}
}
-int RgbMap::mapColor(int r, int g, int b) const
-{
- ASSERT(r >= 0 && r < 256);
- ASSERT(g >= 0 && g < 256);
- ASSERT(b >= 0 && b < 256);
- return m_map[((r>>3) << 10) + ((g>>3) << 5) + (b>>3)];
-}
-
} // namespace doc
diff --git a/src/doc/rgbmap.h b/src/doc/rgbmap.h
index 1bf228131..aed8b5002 100644
--- a/src/doc/rgbmap.h
+++ b/src/doc/rgbmap.h
@@ -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.
@@ -24,7 +24,14 @@ namespace doc {
bool match(const Palette* palette) const;
void regenerate(const Palette* palette, int mask_index);
- int mapColor(int r, int g, int b) const;
+ int mapColor(int r, int g, int b, int a) const {
+ ASSERT(r >= 0 && r < 256);
+ ASSERT(g >= 0 && g < 256);
+ ASSERT(b >= 0 && b < 256);
+ ASSERT(a >= 0 && a < 256);
+ // bits -> bbbbbgggggrrrrraaa
+ return m_map[(a>>5) | ((b>>3) << 3) | ((g>>3) << 8) | ((r>>3) << 13)];
+ }
private:
std::vector m_map;
diff --git a/src/filters/color_curve_filter.cpp b/src/filters/color_curve_filter.cpp
index 17dcdec27..52735d831 100644
--- a/src/filters/color_curve_filter.cpp
+++ b/src/filters/color_curve_filter.cpp
@@ -114,7 +114,7 @@ void ColorCurveFilter::applyToIndexed(FilterManager* filterMgr)
Target target = filterMgr->getTarget();
const Palette* pal = filterMgr->getIndexedData()->getPalette();
const RgbMap* rgbmap = filterMgr->getIndexedData()->getRgbMap();
- int x, c, r, g, b;
+ int x, c, r, g, b, a;
for (x=0; xskipPixel()) {
@@ -129,15 +129,18 @@ void ColorCurveFilter::applyToIndexed(FilterManager* filterMgr)
c = m_cmap[c];
}
else {
- r = rgba_getr(pal->getEntry(c));
- g = rgba_getg(pal->getEntry(c));
- b = rgba_getb(pal->getEntry(c));
+ c = pal->getEntry(c);
+ r = rgba_getr(c);
+ g = rgba_getg(c);
+ b = rgba_getb(c);
+ a = rgba_geta(c);
- if (target & TARGET_RED_CHANNEL) r = m_cmap[r];
+ if (target & TARGET_RED_CHANNEL ) r = m_cmap[r];
if (target & TARGET_GREEN_CHANNEL) g = m_cmap[g];
- if (target & TARGET_BLUE_CHANNEL) b = m_cmap[b];
+ if (target & TARGET_BLUE_CHANNEL ) b = m_cmap[b];
+ if (target & TARGET_ALPHA_CHANNEL) a = m_cmap[a];
- c = rgbmap->mapColor(r, g, b);
+ c = rgbmap->mapColor(r, g, b, a);
}
*(dst_address++) = MID(0, c, pal->size()-1);
diff --git a/src/filters/convolution_matrix_filter.cpp b/src/filters/convolution_matrix_filter.cpp
index d720ce9c6..5212828f1 100644
--- a/src/filters/convolution_matrix_filter.cpp
+++ b/src/filters/convolution_matrix_filter.cpp
@@ -87,22 +87,28 @@ namespace {
struct GetPixelsDelegateIndexed : public GetPixelsDelegate {
const Palette* pal;
- int r, g, b, index;
+ int r, g, b, a, index;
GetPixelsDelegateIndexed(const Palette* pal) : pal(pal) { }
void reset(const ConvolutionMatrix* matrix) {
GetPixelsDelegate::reset(matrix);
- r = g = b = index = 0;
+ r = g = b = a = index = 0;
}
- void operator()(GrayscaleTraits::pixel_t color)
+ void operator()(IndexedTraits::pixel_t color)
{
if (*matrixData) {
- r += rgba_getr(pal->getEntry(color)) * (*matrixData);
- g += rgba_getg(pal->getEntry(color)) * (*matrixData);
- b += rgba_getb(pal->getEntry(color)) * (*matrixData);
index += color * (*matrixData);
+ color_t rgba = pal->getEntry(color);
+ if (rgba_geta(rgba) == 0)
+ div -= *matrixData;
+ else {
+ r += rgba_getr(rgba) * (*matrixData);
+ g += rgba_getg(rgba) * (*matrixData);
+ b += rgba_getb(rgba) * (*matrixData);
+ a += rgba_geta(rgba) * (*matrixData);
+ }
}
matrixData++;
}
@@ -297,28 +303,37 @@ void ConvolutionMatrixFilter::applyToIndexed(FilterManager* filterMgr)
*(dst_address++) = delegate.index;
}
else {
+ color = pal->getEntry(color);
+
if (target & TARGET_RED_CHANNEL) {
delegate.r = delegate.r / delegate.div + m_matrix->getBias();
delegate.r = MID(0, delegate.r, 255);
}
else
- delegate.r = rgba_getr(pal->getEntry(color));
+ delegate.r = rgba_getr(color);
if (target & TARGET_GREEN_CHANNEL) {
delegate.g = delegate.g / delegate.div + m_matrix->getBias();
delegate.g = MID(0, delegate.g, 255);
}
else
- delegate.g = rgba_getg(pal->getEntry(color));
+ delegate.g = rgba_getg(color);
if (target & TARGET_BLUE_CHANNEL) {
delegate.b = delegate.b / delegate.div + m_matrix->getBias();
delegate.b = MID(0, delegate.b, 255);
}
else
- delegate.b = rgba_getb(pal->getEntry(color));
+ delegate.b = rgba_getb(color);
- *(dst_address++) = rgbmap->mapColor(delegate.r, delegate.g, delegate.b);
+ if (target & TARGET_ALPHA_CHANNEL) {
+ delegate.a = delegate.a / delegate.div + m_matrix->getBias();
+ delegate.a = MID(0, delegate.a, 255);
+ }
+ else
+ delegate.a = rgba_geta(color);
+
+ *(dst_address++) = rgbmap->mapColor(delegate.r, delegate.g, delegate.b, delegate.a);
}
}
}
diff --git a/src/filters/invert_color_filter.cpp b/src/filters/invert_color_filter.cpp
index 352652414..f55d78623 100644
--- a/src/filters/invert_color_filter.cpp
+++ b/src/filters/invert_color_filter.cpp
@@ -92,7 +92,7 @@ void InvertColorFilter::applyToIndexed(FilterManager* filterMgr)
const RgbMap* rgbmap = filterMgr->getIndexedData()->getRgbMap();
int w = filterMgr->getWidth();
Target target = filterMgr->getTarget();
- int x, c, r, g, b;
+ int x, c, r, g, b, a;
for (x=0; xskipPixel()) {
@@ -106,15 +106,18 @@ void InvertColorFilter::applyToIndexed(FilterManager* filterMgr)
if (target & TARGET_INDEX_CHANNEL)
c ^= 0xff;
else {
- r = rgba_getr(pal->getEntry(c));
- g = rgba_getg(pal->getEntry(c));
- b = rgba_getb(pal->getEntry(c));
+ c = pal->getEntry(c);
+ r = rgba_getr(c);
+ g = rgba_getg(c);
+ b = rgba_getb(c);
+ a = rgba_geta(c);
if (target & TARGET_RED_CHANNEL ) r ^= 0xff;
if (target & TARGET_GREEN_CHANNEL) g ^= 0xff;
if (target & TARGET_BLUE_CHANNEL ) b ^= 0xff;
+ if (target & TARGET_ALPHA_CHANNEL) a ^= 0xff;
- c = rgbmap->mapColor(r, g, b);
+ c = rgbmap->mapColor(r, g, b, a);
}
*(dst_address++) = c;
diff --git a/src/filters/median_filter.cpp b/src/filters/median_filter.cpp
index ae6b0dbf4..2a9b52de9 100644
--- a/src/filters/median_filter.cpp
+++ b/src/filters/median_filter.cpp
@@ -78,9 +78,11 @@ namespace {
channel[0][c] = color;
}
else {
- channel[0][c] = rgba_getr(pal->getEntry(color));
- channel[1][c] = rgba_getg(pal->getEntry(color));
- channel[2][c] = rgba_getb(pal->getEntry(color));
+ color_t rgb = pal->getEntry(color);
+ channel[0][c] = rgba_getr(rgb);
+ channel[1][c] = rgba_getg(rgb);
+ channel[2][c] = rgba_getb(rgb);
+ channel[3][c] = rgba_geta(rgb);
}
c++;
}
@@ -222,7 +224,7 @@ void MedianFilter::applyToIndexed(FilterManager* filterMgr)
const Palette* pal = filterMgr->getIndexedData()->getPalette();
const RgbMap* rgbmap = filterMgr->getIndexedData()->getRgbMap();
Target target = filterMgr->getTarget();
- int color, r, g, b;
+ int color, r, g, b, a;
GetPixelsDelegateIndexed delegate(pal, m_channel, target);
int x = filterMgr->x();
int x2 = x+filterMgr->getWidth();
@@ -245,13 +247,14 @@ void MedianFilter::applyToIndexed(FilterManager* filterMgr)
}
else {
color = get_pixel_fast(src, x, y);
+ color = pal->getEntry(color);
if (target & TARGET_RED_CHANNEL) {
std::sort(m_channel[0].begin(), m_channel[0].end());
r = m_channel[0][m_ncolors/2];
}
else
- r = rgba_getr(pal->getEntry(color));
+ r = rgba_getr(color);
if (target & TARGET_GREEN_CHANNEL) {
std::sort(m_channel[1].begin(), m_channel[1].end());
@@ -265,9 +268,16 @@ void MedianFilter::applyToIndexed(FilterManager* filterMgr)
b = m_channel[2][m_ncolors/2];
}
else
- b = rgba_getb(pal->getEntry(color));
+ b = rgba_getb(color);
- *(dst_address++) = rgbmap->mapColor(r, g, b);
+ if (target & TARGET_ALPHA_CHANNEL) {
+ std::sort(m_channel[3].begin(), m_channel[3].end());
+ a = m_channel[3][m_ncolors/2];
+ }
+ else
+ a = rgba_geta(color);
+
+ *(dst_address++) = rgbmap->mapColor(r, g, b, a);
}
}
}
diff --git a/src/render/color_histogram.h b/src/render/color_histogram.h
index ac421a914..499928844 100644
--- a/src/render/color_histogram.h
+++ b/src/render/color_histogram.h
@@ -20,34 +20,35 @@
namespace render {
using namespace doc;
- template // Number of bits for each component in the histogram
+ template
class ColorHistogram {
public:
// Number of elements in histogram for each RGB component
enum {
RElements = 1 << RBits,
GElements = 1 << GBits,
- BElements = 1 << BBits
+ BElements = 1 << BBits,
+ AElements = 1 << ABits
};
ColorHistogram()
- : m_histogram(RElements*GElements*BElements, 0)
- , m_useHighPrecision(true)
- {
+ : m_histogram(RElements*GElements*BElements*AElements, 0)
+ , m_useHighPrecision(true) {
}
// Returns the number of points in the specified histogram
- // entry. Each index (i, j, k) is in the range of the
- // histogram i=[0,RElements), etc.
- std::size_t at(int i, int j, int k) const
- {
- return m_histogram[histogramIndex(i, j, k)];
+ // entry. Each rgba-index is in the range of the histogram, e.g.
+ // r=[0,RElements), g=[0,GElements), etc.
+ std::size_t at(int r, int g, int b, int a) const {
+ return m_histogram[histogramIndex(r, g, b, a)];
}
// Add the specified "color" in the histogram as many times as the
// specified value in "count".
- void addSamples(uint32_t color, std::size_t count = 1)
- {
+ void addSamples(uint32_t color, std::size_t count = 1) {
int i = histogramIndex(color);
if (m_histogram[i] < std::numeric_limits::max()-count) // Avoid overflow
@@ -79,8 +80,7 @@ namespace render {
// with the more important colors in the histogram. Returns the
// number of used entries in the palette (maybe the range [from,to]
// is more than necessary).
- int createOptimizedPalette(Palette* palette, int from, int to)
- {
+ int createOptimizedPalette(Palette* palette, int from, int to) {
// Can we use the high-precision table?
if (m_useHighPrecision && int(m_highPrecision.size()) <= (to-from+1)) {
for (int i=0; i<(int)m_highPrecision.size(); ++i)
@@ -105,16 +105,19 @@ namespace render {
// Converts input color in a index for the histogram. It reduces
// each 8-bit component to the resolution given in the template
// parameters.
- std::size_t histogramIndex(uint32_t color) const
- {
+ std::size_t histogramIndex(uint32_t color) const {
return histogramIndex((rgba_getr(color) >> (8 - RBits)),
(rgba_getg(color) >> (8 - GBits)),
- (rgba_getb(color) >> (8 - BBits)));
+ (rgba_getb(color) >> (8 - BBits)),
+ (rgba_geta(color) >> (8 - ABits)));
}
- std::size_t histogramIndex(int i, int j, int k) const
- {
- return i | (j << RBits) | (k << (RBits+GBits));
+ std::size_t histogramIndex(int r, int g, int b, int a) const {
+ return
+ r
+ | (g << RBits)
+ | (b << (RBits+GBits))
+ | (a << (RBits+GBits+BBits));
}
// 3D histogram (the index in the histogram is calculated through histogramIndex() function).
diff --git a/src/render/median_cut.h b/src/render/median_cut.h
index f4b21258d..b63e6fc20 100644
--- a/src/render/median_cut.h
+++ b/src/render/median_cut.h
@@ -21,40 +21,46 @@ namespace render {
// These classes are used as parameters for some Box's generic
// member functions, so we can access to a different axis using
// the same generic function (i=Red channel in RAxisGetter, etc.).
- struct RAxisGetter { static std::size_t at(const Histogram& h, int i, int j, int k) { return h.at(i, j, k); } };
- struct GAxisGetter { static std::size_t at(const Histogram& h, int i, int j, int k) { return h.at(j, i, k); } };
- struct BAxisGetter { static std::size_t at(const Histogram& h, int i, int j, int k) { return h.at(j, k, i); } };
+ struct RAxisGetter { static std::size_t at(const Histogram& h, int i, int j, int k, int l) { return h.at(i, j, k, l); } };
+ struct GAxisGetter { static std::size_t at(const Histogram& h, int i, int j, int k, int l) { return h.at(j, i, k, l); } };
+ struct BAxisGetter { static std::size_t at(const Histogram& h, int i, int j, int k, int l) { return h.at(j, k, i, l); } };
+ struct AAxisGetter { static std::size_t at(const Histogram& h, int i, int j, int k, int l) { return h.at(j, k, l, i); } };
// These classes are used as template parameter to split a Box
// along an axis (see splitAlongAxis)
struct RAxisSplitter {
- static Box box1(const Box& box, int r) { return Box(box.r1, box.g1, box.b1, r, box.g2, box.b2); }
- static Box box2(const Box& box, int r) { return Box(r, box.g1, box.b1, box.r2, box.g2, box.b2); }
+ static Box box1(const Box& box, int r) { return Box(box.r1, box.g1, box.b1, box.a1, r, box.g2, box.b2, box.a2); }
+ static Box box2(const Box& box, int r) { return Box(r, box.g1, box.b1, box.a1, box.r2, box.g2, box.b2, box.a2); }
};
struct GAxisSplitter {
- static Box box1(const Box& box, int g) { return Box(box.r1, box.g1, box.b1, box.r2, g, box.b2); }
- static Box box2(const Box& box, int g) { return Box(box.r1, g, box.b1, box.r2, box.g2, box.b2); }
+ static Box box1(const Box& box, int g) { return Box(box.r1, box.g1, box.b1, box.a1, box.r2, g, box.b2, box.a2); }
+ static Box box2(const Box& box, int g) { return Box(box.r1, g, box.b1, box.a1, box.r2, box.g2, box.b2, box.a2); }
};
struct BAxisSplitter {
- static Box box1(const Box& box, int b) { return Box(box.r1, box.g1, box.b1, box.r2, box.g2, b ); }
- static Box box2(const Box& box, int b) { return Box(box.r1, box.g1, b, box.r2, box.g2, box.b2); }
+ static Box box1(const Box& box, int b) { return Box(box.r1, box.g1, box.b1, box.a1, box.r2, box.g2, b, box.a2); }
+ static Box box2(const Box& box, int b) { return Box(box.r1, box.g1, b, box.a1, box.r2, box.g2, box.b2, box.a2); }
+ };
+ struct AAxisSplitter {
+ static Box box1(const Box& box, int a) { return Box(box.r1, box.g1, box.b1, box.a1, box.r2, box.g2, box.b2, a ); }
+ static Box box2(const Box& box, int a) { return Box(box.r1, box.g1, box.b1, a, box.r2, box.g2, box.b2, box.a2); }
};
public:
- Box(int r1, int g1, int b1,
- int r2, int g2, int b2)
- : r1(r1), g1(g1), b1(b1)
- , r2(r2), g2(g2), b2(b2)
+ Box(int r1, int g1, int b1, int a1,
+ int r2, int g2, int b2, int a2)
+ : r1(r1), g1(g1), b1(b1), a1(a1)
+ , r2(r2), g2(g2), b2(b2), a2(a2)
, points(0)
- , volume(calculateVolume()) { }
+ , volume(calculateVolume()) {
+ }
// Shrinks each plane of the box to a position where there are
// points in the histogram.
- void shrink(const Histogram& histogram)
- {
- axisShrink(histogram, r1, r2, g1, g2, b1, b2);
- axisShrink(histogram, g1, g2, r1, r2, b1, b2);
- axisShrink(histogram, b1, b2, r1, r2, g1, g2);
+ void shrink(const Histogram& histogram) {
+ axisShrink(histogram, r1, r2, g1, g2, b1, b2, a1, a2);
+ axisShrink(histogram, g1, g2, r1, r2, b1, b2, a1, a2);
+ axisShrink(histogram, b1, b2, r1, r2, g1, g2, a1, a2);
+ axisShrink(histogram, a1, a2, r1, r2, g1, g2, b1, b2);
// Calculate number of points inside the box (this is done by
// first time here, because the Box ctor didn't calculate it).
@@ -64,37 +70,47 @@ namespace render {
volume = calculateVolume();
}
- bool split(const Histogram& histogram, std::priority_queue& boxes) const
- {
+ bool split(const Histogram& histogram, std::priority_queue& boxes) const {
// Split along the largest dimension of the box.
- if ((r2-r1) >= (g2-g1) && (r2-r1) >= (b2-b1)) {
- return splitAlongAxis(histogram, boxes, r1, r2, g1, g2, b1, b2);
+ if ((r2-r1) >= (g2-g1) &&
+ (r2-r1) >= (b2-b1) &&
+ (r2-r1) >= (a2-a1)) {
+ return splitAlongAxis(histogram, boxes, r1, r2, g1, g2, b1, b2, a1, a2);
}
- else if ((g2-g1) >= (r2-r1) && (g2-g1) >= (b2-b1)) {
- return splitAlongAxis(histogram, boxes, g1, g2, r1, r2, b1, b2);
+
+ if ((g2-g1) >= (r2-r1) &&
+ (g2-g1) >= (b2-b1) &&
+ (g2-g1) >= (a2-a1)) {
+ return splitAlongAxis(histogram, boxes, g1, g2, r1, r2, b1, b2, a1, a2);
}
- else {
- return splitAlongAxis(histogram, boxes, b1, b2, r1, r2, g1, g2);
+
+ if ((b2-b1) >= (r2-r1) &&
+ (b2-b1) >= (g2-g1) &&
+ (b2-b1) >= (a2-a1)) {
+ return splitAlongAxis(histogram, boxes, b1, b2, r1, r2, g1, g2, a1, a2);
}
+
+ return splitAlongAxis(histogram, boxes, a1, a2, r1, r2, g1, g2, b1, b2);
}
// Returns the color enclosed by the box calculating the mean of
// all histogram's points inside the box.
- uint32_t meanColor(const Histogram& histogram) const
- {
- std::size_t r = 0, g = 0, b = 0;
+ uint32_t meanColor(const Histogram& histogram) const {
+ std::size_t r = 0, g = 0, b = 0, a = 0;
std::size_t count = 0;
- int i, j, k;
+ int i, j, k, l;
for (i=r1; i<=r2; ++i)
for (j=g1; j<=g2; ++j)
- for (k=b1; k<=b2; ++k) {
- int c = histogram.at(i, j, k);
- r += c * i;
- g += c * j;
- b += c * k;
- count += c;
- }
+ for (k=b1; k<=b2; ++k)
+ for (l=a1; l<=a2; ++l) {
+ int c = histogram.at(i, j, k, l);
+ r += c * i;
+ g += c * j;
+ b += c * k;
+ a += c * l;
+ count += c;
+ }
// No colors in the box? This should not be possible.
ASSERT(count > 0 && "Box without histogram points, you must fill the histogram before using this function.");
@@ -104,12 +120,12 @@ namespace render {
// Returns the mean.
return doc::rgba((255 * r / (Histogram::RElements-1)) / count,
(255 * g / (Histogram::GElements-1)) / count,
- (255 * b / (Histogram::BElements-1)) / count, 255);
+ (255 * b / (Histogram::BElements-1)) / count,
+ (255 * a / (Histogram::AElements-1)) / count);
}
// The boxes will be sort in the priority_queue by volume.
- bool operator<(const Box& other) const
- {
+ bool operator<(const Box& other) const {
return volume < other.volume;
}
@@ -119,21 +135,20 @@ namespace render {
// value returned by this function is cached in the "volume"
// variable member of Box class to avoid multiplying several
// times.
- int calculateVolume() const
- {
- return (r2-r1+1) * (g2-g1+1) * (b2-b1+1);
+ int calculateVolume() const {
+ return (r2-r1+1) * (g2-g1+1) * (b2-b1+1) * (a2-a1+1);
}
// Returns the number of histogram's points inside the box bounds.
- std::size_t countPoints(const Histogram& histogram) const
- {
+ std::size_t countPoints(const Histogram& histogram) const {
std::size_t count = 0;
- int i, j, k;
+ int i, j, k, l;
for (i=r1; i<=r2; ++i)
for (j=g1; j<=g2; ++j)
for (k=b1; k<=b2; ++k)
- count += histogram.at(i, j, k);
+ for (l=a1; l<=a2; ++l)
+ count += histogram.at(i, j, k, l);
return count;
}
@@ -145,16 +160,19 @@ namespace render {
static void axisShrink(const Histogram& histogram,
int& i1, int& i2,
const int& j1, const int& j2,
- const int& k1, const int& k2)
+ const int& k1, const int& k2,
+ const int& l1, const int& l2)
{
- int j, k;
+ int j, k, l;
// Shrink i1.
for (; i1 0)
- goto doneA;
+ for (l=l1; l<=l2; ++l) {
+ if (AxisGetter::at(histogram, i1, j, k, l) > 0)
+ goto doneA;
+ }
}
}
}
@@ -164,8 +182,10 @@ namespace render {
for (; i2>i1; --i2) {
for (j=j1; j<=j2; ++j) {
for (k=k1; k<=k2; ++k) {
- if (AxisGetter::at(histogram, i2, j, k) > 0)
- goto doneB;
+ for (l=l1; l<=l2; ++l) {
+ if (AxisGetter::at(histogram, i2, j, k, l) > 0)
+ goto doneB;
+ }
}
}
}
@@ -183,13 +203,13 @@ namespace render {
std::priority_queue& boxes,
const int& i1, const int& i2,
const int& j1, const int& j2,
- const int& k1, const int& k2) const
- {
+ const int& k1, const int& k2,
+ const int& l1, const int& l2) const {
// These two variables will be used to count how many points are
// in each side of the box if we split it in "i" position.
std::size_t totalPoints1 = 0;
std::size_t totalPoints2 = this->points;
- int i, j, k;
+ int i, j, k, l;
// We will try to split the box along the "i" axis. Imagine a
// plane which its normal vector is "i" axis, so we will try to
@@ -202,7 +222,8 @@ namespace render {
// We count all points in "i" plane.
for (j=j1; j<=j2; ++j)
for (k=k1; k<=k2; ++k)
- planePoints += AxisGetter::at(histogram, i, j, k);
+ for (l=l1; l<=l2; ++l)
+ planePoints += AxisGetter::at(histogram, i, j, k, l);
// As we move the plane to split through "i" axis One side is getting more points,
totalPoints1 += planePoints;
@@ -234,8 +255,8 @@ namespace render {
return false;
}
- int r1, g1, b1; // Min point (closest to origin)
- int r2, g2, b2; // Max point
+ int r1, g1, b1, a1; // Min point (closest to origin)
+ int r2, g2, b2, a2; // Max point
std::size_t points; // Number of points in the space which enclose this box
int volume;
}; // end of class Box
@@ -250,10 +271,11 @@ namespace render {
std::priority_queue > boxes;
// First we start with one big box containing all histogram's samples.
- boxes.push(Box(0, 0, 0,
+ boxes.push(Box(0, 0, 0, 0,
Histogram::RElements-1,
Histogram::GElements-1,
- Histogram::BElements-1));
+ Histogram::BElements-1,
+ Histogram::AElements-1));
// Then we split each box until we reach the maximum specified by
// the user (maxBoxes) or until there aren't more boxes to split.
diff --git a/src/render/ordered_dither.h b/src/render/ordered_dither.h
index 965ea889a..57a9d31dc 100644
--- a/src/render/ordered_dither.h
+++ b/src/render/ordered_dither.h
@@ -60,12 +60,13 @@ namespace render {
3, 1 };
class OrderedDither {
- static int colorDistance(int r1, int g1, int b1,
- int r2, int g2, int b2) {
+ static int colorDistance(int r1, int g1, int b1, int a1,
+ int r2, int g2, int b2, int a2) {
// The factor for RGB components came from doc::rba_luma()
return int((r1-r2) * (r1-r2) * 21 + // 2126
(g1-g2) * (g1-g2) * 71 + // 7152
- (b1-b2) * (b1-b2) * 7); // 722
+ (b1-b2) * (b1-b2) * 7 + // 722
+ (a1-a2) * (a1-a2));
}
public:
@@ -80,7 +81,7 @@ namespace render {
const doc::RgbMap* rgbmap,
const doc::Palette* palette) {
// Alpha=0, output transparent color
- if (!doc::rgba_geta(color))
+ if (m_transparentIndex >= 0 && !doc::rgba_geta(color))
return m_transparentIndex;
// Get the nearest color in the palette with the given RGB
@@ -88,14 +89,16 @@ namespace render {
int r = doc::rgba_getr(color);
int g = doc::rgba_getg(color);
int b = doc::rgba_getb(color);
+ int a = doc::rgba_geta(color);
doc::color_t nearest1idx =
- (rgbmap ? rgbmap->mapColor(r, g, b):
- palette->findBestfit(r, g, b, m_transparentIndex));
+ (rgbmap ? rgbmap->mapColor(r, g, b, a):
+ palette->findBestfit(r, g, b, a, m_transparentIndex));
doc::color_t nearest1rgb = palette->getEntry(nearest1idx);
int r1 = doc::rgba_getr(nearest1rgb);
int g1 = doc::rgba_getg(nearest1rgb);
int b1 = doc::rgba_getb(nearest1rgb);
+ int a1 = doc::rgba_geta(nearest1rgb);
// Between the original color ('color' parameter) and 'nearest'
// index, we have an error (r1-r, g1-g, b1-b). Here we try to
@@ -104,12 +107,14 @@ namespace render {
int r2 = r - (r1-r);
int g2 = g - (g1-g);
int b2 = b - (b1-b);
+ int a2 = a - (a1-a);
r2 = MID(0, r2, 255);
g2 = MID(0, g2, 255);
b2 = MID(0, b2, 255);
+ a2 = MID(0, a2, 255);
doc::color_t nearest2idx =
- (rgbmap ? rgbmap->mapColor(r2, g2, b2):
- palette->findBestfit(r2, g2, b2, m_transparentIndex));
+ (rgbmap ? rgbmap->mapColor(r2, g2, b2, a2):
+ palette->findBestfit(r2, g2, b2, a2, m_transparentIndex));
// If both possible RGB colors use the same index, we cannot
// make any dither with these two colors.
@@ -120,12 +125,13 @@ namespace render {
r2 = doc::rgba_getr(nearest2rgb);
g2 = doc::rgba_getg(nearest2rgb);
b2 = doc::rgba_getb(nearest2rgb);
+ a2 = doc::rgba_geta(nearest2rgb);
// Here we calculate the distance between the original 'color'
// and 'nearest1rgb'. The maximum possible distance is given by
// the distance between 'nearest1rgb' and 'nearest2rgb'.
- int d = colorDistance(r1, g1, b1, r, g, b);
- int D = colorDistance(r1, g1, b1, r2, g2, b2);
+ int d = colorDistance(r1, g1, b1, a1, r, g, b, a);
+ int D = colorDistance(r1, g1, b1, a1, r2, g2, b2, a2);
if (D == 0)
return nearest1idx;
diff --git a/src/render/quantization.cpp b/src/render/quantization.cpp
index dbab3e3ba..ef0c19ecc 100644
--- a/src/render/quantization.cpp
+++ b/src/render/quantization.cpp
@@ -36,6 +36,7 @@ Palette* create_palette_from_rgb(
const Sprite* sprite,
frame_t fromFrame,
frame_t toFrame,
+ bool withAlpha,
Palette* palette)
{
PaletteOptimizer optimizer;
@@ -43,7 +44,7 @@ Palette* create_palette_from_rgb(
if (!palette)
palette = new Palette(fromFrame, 256);
- bool has_background_layer = (sprite->backgroundLayer() != nullptr);
+ bool hasBackgroundLayer = (sprite->backgroundLayer() != nullptr);
// Add a flat image with the current sprite's frame rendered
ImageRef flat_image(Image::create(IMAGE_RGB,
@@ -53,11 +54,11 @@ Palette* create_palette_from_rgb(
render::Render render;
for (frame_t frame=fromFrame; frame<=toFrame; ++frame) {
render.renderSprite(flat_image.get(), sprite, frame);
- optimizer.feedWithImage(flat_image.get());
+ optimizer.feedWithImage(flat_image.get(), withAlpha);
}
// Generate an optimized palette
- optimizer.calculate(palette, has_background_layer);
+ optimizer.calculate(palette, hasBackgroundLayer);
return palette;
}
@@ -85,7 +86,7 @@ Image* convert_pixel_format(
}
color_t c;
- int r, g, b;
+ int r, g, b, a;
switch (image->pixelFormat()) {
@@ -137,11 +138,12 @@ Image* convert_pixel_format(
r = rgba_getr(c);
g = rgba_getg(c);
b = rgba_getb(c);
+ a = rgba_geta(c);
- if (rgba_geta(c) == 0)
- *dst_it = 0;
+ if (a == 0)
+ *dst_it = 0; // TODO why 0 is mask color and not a param?
else
- *dst_it = rgbmap->mapColor(r, g, b);
+ *dst_it = rgbmap->mapColor(r, g, b, a);
}
ASSERT(dst_it == dst_end);
break;
@@ -192,11 +194,13 @@ Image* convert_pixel_format(
for (; src_it != src_end; ++src_it, ++dst_it) {
ASSERT(dst_it != dst_end);
c = *src_it;
+ a = graya_geta(c);
+ c = graya_getv(c);
- if (graya_geta(c) == 0)
- *dst_it = 0;
+ if (a == 0)
+ *dst_it = 0; // TODO why 0 is mask color and not a param?
else
- *dst_it = graya_getv(c);
+ *dst_it = rgbmap->mapColor(c, c, c, a);
}
ASSERT(dst_it == dst_end);
break;
@@ -226,9 +230,7 @@ Image* convert_pixel_format(
if (!is_background && c == image->maskColor())
*dst_it = 0;
else
- *dst_it = rgba(rgba_getr(palette->getEntry(c)),
- rgba_getg(palette->getEntry(c)),
- rgba_getb(palette->getEntry(c)), 255);
+ *dst_it = palette->getEntry(c);
}
ASSERT(dst_it == dst_end);
break;
@@ -249,12 +251,14 @@ Image* convert_pixel_format(
if (!is_background && c == image->maskColor())
*dst_it = 0;
else {
- r = rgba_getr(palette->getEntry(c));
- g = rgba_getg(palette->getEntry(c));
- b = rgba_getb(palette->getEntry(c));
+ c = palette->getEntry(c);
+ r = rgba_getr(c);
+ g = rgba_getg(c);
+ b = rgba_getb(c);
+ a = rgba_geta(c);
g = 255 * Hsv(Rgb(r, g, b)).valueInt() / 100;
- *dst_it = graya(g, 255);
+ *dst_it = graya(g, a);
}
}
ASSERT(dst_it == dst_end);
@@ -277,11 +281,12 @@ Image* convert_pixel_format(
if (!is_background && c == image->maskColor())
*dst_it = dstMaskColor;
else {
- r = rgba_getr(palette->getEntry(c));
- g = rgba_getg(palette->getEntry(c));
- b = rgba_getb(palette->getEntry(c));
-
- *dst_it = rgbmap->mapColor(r, g, b);
+ c = palette->getEntry(c);
+ r = rgba_getr(c);
+ g = rgba_getg(c);
+ b = rgba_getb(c);
+ a = rgba_geta(c);
+ *dst_it = rgbmap->mapColor(r, g, b, a);
}
}
ASSERT(dst_it == dst_end);
@@ -300,7 +305,7 @@ Image* convert_pixel_format(
// Creation of optimized palette for RGB images
// by David Capello
-void PaletteOptimizer::feedWithImage(Image* image)
+void PaletteOptimizer::feedWithImage(Image* image, bool withAlpha)
{
uint32_t color;
@@ -314,9 +319,10 @@ void PaletteOptimizer::feedWithImage(Image* image)
for (; it != end; ++it) {
color = *it;
-
if (rgba_geta(color) > 0) {
- color |= rgba(0, 0, 0, 255);
+ if (!withAlpha)
+ color |= rgba(0, 0, 0, 255);
+
m_histogram.addSamples(color, 1);
}
}
@@ -332,8 +338,13 @@ void PaletteOptimizer::feedWithImage(Image* image)
color = *it;
if (graya_geta(color) > 0) {
- color = graya_getv(color);
- m_histogram.addSamples(rgba(color, color, color, 255), 1);
+ if (!withAlpha)
+ color = graya(graya_getv(color), 255);
+
+ m_histogram.addSamples(rgba(graya_getv(color),
+ graya_getv(color),
+ graya_getv(color),
+ graya_geta(color)), 1);
}
}
}
@@ -346,25 +357,27 @@ void PaletteOptimizer::feedWithImage(Image* image)
}
}
-void PaletteOptimizer::calculate(Palette* palette, bool has_background_layer)
+void PaletteOptimizer::calculate(Palette* palette, bool hasBackgroundLayer)
{
// If the sprite has a background layer, the first entry can be
// used, in other case the 0 indexed will be the mask color, so it
// will not be used later in the color conversion (from RGB to
// Indexed).
- int first_usable_entry = (has_background_layer ? 0: 1);
+ int first_usable_entry = (hasBackgroundLayer ? 0: 1);
int used_colors = m_histogram.createOptimizedPalette(
palette, first_usable_entry, palette->size()-1);
palette->resize(MAX(1, first_usable_entry+used_colors));
}
-void create_palette_from_images(const std::vector& images, Palette* palette, bool has_background_layer)
+void create_palette_from_images(const std::vector& images, Palette* palette,
+ bool hasBackgroundLayer,
+ bool withAlpha)
{
PaletteOptimizer optimizer;
for (int i=0; i<(int)images.size(); ++i)
- optimizer.feedWithImage(images[i]);
+ optimizer.feedWithImage(images[i], withAlpha);
- optimizer.calculate(palette, has_background_layer);
+ optimizer.calculate(palette, hasBackgroundLayer);
}
} // namespace render
diff --git a/src/render/quantization.h b/src/render/quantization.h
index 1a53119f2..472e318ad 100644
--- a/src/render/quantization.h
+++ b/src/render/quantization.h
@@ -26,26 +26,28 @@ namespace doc {
namespace render {
using namespace doc;
- class PaletteOptimizer {
- public:
- void feedWithImage(Image* image);
- void calculate(Palette* palette, bool has_background_layer);
+ class PaletteOptimizer {
+ public:
+ void feedWithImage(Image* image, bool withAlpha);
+ void calculate(Palette* palette, bool hasBackgroundLayer);
private:
- ColorHistogram<5, 6, 5> m_histogram;
+ ColorHistogram<5, 6, 5, 5> m_histogram;
};
void create_palette_from_images(
const std::vector& images,
Palette* palette,
- bool has_background_layer);
+ bool hasBackgroundLayer,
+ bool withAlpha);
// Creates a new palette suitable to quantize the given RGB sprite to Indexed color.
Palette* create_palette_from_rgb(
const Sprite* sprite,
frame_t fromFrame,
frame_t toFrame,
- Palette* newPalette); // Can be NULL to create a new palette
+ bool withAlpha,
+ Palette* newPalette); // Can be NULL to create a new palette
// Changes the image pixel format. The dithering method is used only
// when you want to convert from RGB to Indexed.
diff --git a/src/render/render.cpp b/src/render/render.cpp
index ae54417b2..3c96c9cc2 100644
--- a/src/render/render.cpp
+++ b/src/render/render.cpp
@@ -492,10 +492,18 @@ void Render::renderSprite(
switch (m_bgType) {
case BgType::CHECKED:
- if (bgLayer && bgLayer->isVisible())
+ if (bgLayer && bgLayer->isVisible() && rgba_geta(bg_color) == 255) {
fill_rect(dstImage, area.dstBounds(), bg_color);
- else
+ }
+ else {
renderBackground(dstImage, area, zoom);
+ if (bgLayer && bgLayer->isVisible() && rgba_geta(bg_color) > 0) {
+ blend_rect(dstImage, area.dst.x, area.dst.y,
+ area.dst.x+area.size.w-1,
+ area.dst.y+area.size.h-1,
+ bg_color, 255);
+ }
+ }
break;
case BgType::TRANSPARENT: