mirror of
https://github.com/aseprite/aseprite.git
synced 2025-03-14 04:19:12 +00:00
Merge branch 'palette-with-alpha'
This commit is contained in:
commit
48541af433
@ -36,6 +36,22 @@
|
||||
<value id="IF_VISIBLE" value="1" />
|
||||
<value id="ALWAYS" value="2" />
|
||||
</enum>
|
||||
<enum id="EyedropperChannel">
|
||||
<value id="COLOR_ALPHA" value="0" />
|
||||
<value id="COLOR" value="1" />
|
||||
<value id="ALPHA" value="2" />
|
||||
<value id="RGBA" value="3" />
|
||||
<value id="RGB" value="4" />
|
||||
<value id="HSVA" value="5" />
|
||||
<value id="HSV" value="6" />
|
||||
<value id="GRAYA" value="7" />
|
||||
<value id="GRAY" value="8" />
|
||||
<value id="INDEX" value="9" />
|
||||
</enum>
|
||||
<enum id="EyedropperSample">
|
||||
<value id="ALL_LAYERS" value="0" />
|
||||
<value id="CURRENT_LAYER" value="1" />
|
||||
</enum>
|
||||
</types>
|
||||
|
||||
<global>
|
||||
@ -59,7 +75,6 @@
|
||||
<option id="zoom_from_center_with_keys" type="bool" default="false" />
|
||||
<option id="show_scrollbars" type="bool" default="true" migrate="Options.ShowScrollbars" />
|
||||
<option id="right_click_mode" type="RightClickMode" default="RightClickMode::PAINT_BGCOLOR" migrate="Options.RightClickMode" />
|
||||
<option id="grab_alpha" type="bool" default="false" migrate="Options.GrabAlpha" />
|
||||
<option id="auto_select_layer" type="bool" default="false" migrate="Options.AutoSelectLayer" />
|
||||
<option id="cursor_color" type="app::Color" default="app::Color::fromMask()" migrate="Tools.CursorColor" />
|
||||
</section>
|
||||
@ -98,6 +113,13 @@
|
||||
<option id="transparent_color" type="app::Color" />
|
||||
<option id="rotation_algorithm" type="app::tools::RotationAlgorithm" default="app::tools::RotationAlgorithm::DEFAULT" />
|
||||
</section>
|
||||
<section id="quantization">
|
||||
<option id="with_alpha" type="bool" default="true" />
|
||||
</section>
|
||||
<section id="eyedropper" text="Editor">
|
||||
<option id="channel" type="EyedropperChannel" default="EyedropperChannel::COLOR_ALPHA" />
|
||||
<option id="sample" type="EyedropperSample" default="EyedropperSample::ALL_LAYERS" />
|
||||
</section>
|
||||
</global>
|
||||
|
||||
<tool>
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
@ -372,6 +372,9 @@
|
||||
<part id="canvas_s" x="112" y="128" w="16" h="16" />
|
||||
<part id="canvas_se" x="128" y="128" w="16" h="16" />
|
||||
<part id="canvas_empty" x="96" y="96" w="1" h="1" />
|
||||
<part id="ink_default" x="144" y="144" w="16" h="16" />
|
||||
<part id="ink_composite" x="160" y="144" w="16" h="16" />
|
||||
<part id="ink_lock_alpha" x="176" y="144" w="16" h="16" />
|
||||
</parts>
|
||||
|
||||
<stylesheet>
|
||||
|
@ -9,6 +9,8 @@
|
||||
<radio id="current_palette" text="Replace current palette" group="1" cell_hspan="3" />
|
||||
<radio id="current_range" text="Replace current range" group="1" cell_hspan="3" />
|
||||
|
||||
<check id="alpha_channel" text="Create entries with alpha component" cell_hspan="3" />
|
||||
|
||||
<separator horizontal="true" cell_hspan="3" />
|
||||
|
||||
<box horizontal="true" homogeneous="true" cell_hspan="3" cell_align="right">
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -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.
|
||||
|
@ -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<Palette> newPalette(
|
||||
new Palette(createPal ? tmpPalette:
|
||||
|
@ -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()
|
||||
|
41
src/app/commands/cmd_eyedropper.h
Normal file
41
src/app/commands/cmd_eyedropper.h
Normal file
@ -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
|
@ -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; c<palette->size(); 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; c<palette->size(); 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));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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());
|
||||
|
@ -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");
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -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; c<pal->size(); ++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> 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::SharedPtr<FormatOp
|
||||
fop->seq.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;
|
||||
|
@ -133,8 +133,12 @@ namespace app {
|
||||
void fop_post_load(FileOp* fop);
|
||||
|
||||
void fop_sequence_set_format_options(FileOp* fop, const base::SharedPtr<FormatOptions>& 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, ...);
|
||||
|
@ -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 {
|
||||
|
||||
|
@ -641,7 +641,7 @@ bool GifFormat::onSave(FileOp* fop)
|
||||
for (frame_t frame_num(0); frame_num<sprite->totalFrames(); ++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<Image*> 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;
|
||||
|
@ -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<uint8_t> 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; c<num_palette; ++c) {
|
||||
fop_sequence_set_color(fop, c,
|
||||
palette[c].red,
|
||||
palette[c].green,
|
||||
palette[c].blue);
|
||||
}
|
||||
for (; c < 256; c++) {
|
||||
fop_sequence_set_color(fop, c, 0, 0, 0);
|
||||
}
|
||||
|
||||
// Read alpha values for palette entries
|
||||
png_bytep trans = NULL; // Transparent palette entries
|
||||
int num_trans = 0;
|
||||
int mask_entry = -1;
|
||||
|
||||
png_get_tRNS(png_ptr, info_ptr, &trans, &num_trans, NULL);
|
||||
|
||||
for (int i = 0; i < num_trans; ++i) {
|
||||
pal_alphas[i] = trans[i];
|
||||
fop_sequence_set_alpha(fop, i, trans[i]);
|
||||
|
||||
if (pal_alphas[i] < 128) {
|
||||
if (trans[i] < 255) {
|
||||
fop->seq.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; x<width; x++) {
|
||||
c = *(src_address++);
|
||||
|
||||
if (pal_alphas[c] < 128) {
|
||||
*(dst_address++) = mask_entry;
|
||||
}
|
||||
else {
|
||||
*(dst_address++) = c;
|
||||
}
|
||||
}
|
||||
for (x=0; x<width; x++)
|
||||
*(dst_address++) = *(src_address++);
|
||||
}
|
||||
|
||||
fop_progress(fop,
|
||||
@ -398,37 +384,44 @@ bool PngFormat::onSave(FileOp* fop)
|
||||
|
||||
if (image->pixelFormat() == 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<num_trans; ++c) {
|
||||
int alpha = 255;
|
||||
fop_sequence_get_alpha(fop, c, &alpha);
|
||||
trans[c] = (c == mask_entry ? 0: alpha);
|
||||
}
|
||||
|
||||
png_set_tRNS(png_ptr, info_ptr, trans, num_trans, NULL);
|
||||
png_free(png_ptr, trans);
|
||||
}
|
||||
|
||||
/* Write the file header information. */
|
||||
|
@ -62,33 +62,38 @@ static void rectgrid(ui::Graphics* g, const gfx::Rect& rc, const gfx::Size& tile
|
||||
}
|
||||
}
|
||||
|
||||
static void draw_color(ui::Graphics* g, const Rect& rc, const app::Color& color)
|
||||
void draw_color(ui::Graphics* g, const Rect& rc, const app::Color& color)
|
||||
{
|
||||
if (rc.w < 1 || rc.h < 1)
|
||||
return;
|
||||
|
||||
app::Color::Type type = color.getType();
|
||||
int alpha = color.getAlpha();
|
||||
|
||||
if (type == app::Color::MaskType) {
|
||||
rectgrid(g, rc, gfx::Size(rc.w/4, rc.h/2));
|
||||
return;
|
||||
if (alpha < 255) {
|
||||
if (rc.w == rc.h)
|
||||
rectgrid(g, rc, gfx::Size(rc.w/2, rc.h/2));
|
||||
else
|
||||
rectgrid(g, rc, gfx::Size(rc.w/4, rc.h/2));
|
||||
}
|
||||
else if (type == app::Color::IndexType) {
|
||||
int index = color.getIndex();
|
||||
|
||||
if (index >= 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,
|
||||
|
@ -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);
|
||||
|
@ -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<IndexedTraits>(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<IndexedTraits>::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<IndexedTraits>::processPixel(int x, int y) {
|
||||
switch (m_brushImage->pixelFormat()) {
|
||||
case IMAGE_RGB: {
|
||||
c = get_pixel_fast<RgbTraits>(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<IndexedTraits>::processPixel(int x, int y) {
|
||||
}
|
||||
case IMAGE_GRAYSCALE: {
|
||||
c = get_pixel_fast<GrayscaleTraits>(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: {
|
||||
|
@ -14,7 +14,8 @@ namespace tools {
|
||||
|
||||
enum class InkType {
|
||||
DEFAULT = 0,
|
||||
SET_ALPHA = 1,
|
||||
REPLACE_PIXEL = 0,
|
||||
ALPHA_COMPOSITING = 1,
|
||||
LOCK_ALPHA = 2,
|
||||
};
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -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:
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -31,7 +31,8 @@ namespace app {
|
||||
public:
|
||||
enum Channel { Red, Green, Blue,
|
||||
Hue, Saturation, Value,
|
||||
Gray };
|
||||
Gray,
|
||||
Alpha };
|
||||
enum Mode { Absolute, Relative };
|
||||
|
||||
ColorSliders();
|
||||
|
@ -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());
|
||||
}
|
||||
|
@ -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<SkinTheme*>(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<SkinTheme*>(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<void>(&InkTypeField::selectInk, this, InkType::REPLACE_PIXEL));
|
||||
alphacompo.Click.connect(Bind<void>(&InkTypeField::selectInk, this, InkType::ALPHA_COMPOSITING));
|
||||
lockalpha.Click.connect(Bind<void>(&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<void>(&EyedropperField::onChannelChange, this));
|
||||
m_sample.Change.connect(Bind<void>(&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);
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
|
@ -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 =
|
||||
|
@ -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; i<palette->size(); ++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; i<m_boxsize/2; ++i)
|
||||
g->drawHLine(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; i<m_boxsize/4; ++i)
|
||||
g->drawHLine(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();
|
||||
|
@ -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
|
||||
};
|
||||
|
||||
|
@ -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()
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
@ -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
|
||||
|
||||
|
@ -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<to; ++i) {
|
||||
r = r1 + (r2-r1) * (i-from) / n;
|
||||
g = g1 + (g2-g1) * (i-from) / n;
|
||||
b = b1 + (b2-b1) * (i-from) / n;
|
||||
setEntry(i, rgba(r, g, b, 255));
|
||||
a = a1 + (a2-a1) * (i-from) / n;
|
||||
|
||||
setEntry(i, rgba(r, g, b, a));
|
||||
}
|
||||
}
|
||||
|
||||
int Palette::findExactMatch(int r, int g, int b) const
|
||||
int Palette::findExactMatch(int r, int g, int b, int a) const
|
||||
{
|
||||
for (int i=0; i<(int)m_colors.size(); ++i)
|
||||
if (getEntry(i) == rgba(r, g, b, 255))
|
||||
if (getEntry(i) == rgba(r, g, b, a))
|
||||
return i;
|
||||
|
||||
return -1;
|
||||
@ -184,61 +189,64 @@ int Palette::findExactMatch(int r, int g, int b) const
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// Based on Allegro's bestfit_color
|
||||
|
||||
static unsigned int col_diff[3*128];
|
||||
static std::vector<unsigned int> 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<int>::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<int>::max();
|
||||
int size = MIN(256, m_colors.size());
|
||||
|
||||
for (int i=0; i<size; ++i) {
|
||||
color_t rgb = m_colors[i];
|
||||
|
||||
coldiff = (col_diff + 0) [ ((rgba_getg(rgb)>>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;
|
||||
|
@ -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;
|
||||
|
@ -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));
|
||||
}
|
||||
|
@ -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; r<RSIZE; ++r) {
|
||||
for (int g=0; g<GSIZE; ++g) {
|
||||
for (int b=0; b<BSIZE; ++b) {
|
||||
for (int a=0; a<ASIZE; ++a) {
|
||||
m_map[i++] =
|
||||
palette->findBestfit(
|
||||
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
|
||||
|
@ -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<uint8_t> m_map;
|
||||
|
@ -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; x<w; x++) {
|
||||
if (filterMgr->skipPixel()) {
|
||||
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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; x<w; x++) {
|
||||
if (filterMgr->skipPixel()) {
|
||||
@ -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;
|
||||
|
@ -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<IndexedTraits>(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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -20,34 +20,35 @@
|
||||
namespace render {
|
||||
using namespace doc;
|
||||
|
||||
template<int RBits, int GBits, int BBits> // Number of bits for each component in the histogram
|
||||
template<int RBits, // Number of bits for each component in the histogram
|
||||
int GBits,
|
||||
int BBits,
|
||||
int ABits>
|
||||
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<std::size_t>::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).
|
||||
|
@ -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<RAxisGetter>(histogram, r1, r2, g1, g2, b1, b2);
|
||||
axisShrink<GAxisGetter>(histogram, g1, g2, r1, r2, b1, b2);
|
||||
axisShrink<BAxisGetter>(histogram, b1, b2, r1, r2, g1, g2);
|
||||
void shrink(const Histogram& histogram) {
|
||||
axisShrink<RAxisGetter>(histogram, r1, r2, g1, g2, b1, b2, a1, a2);
|
||||
axisShrink<GAxisGetter>(histogram, g1, g2, r1, r2, b1, b2, a1, a2);
|
||||
axisShrink<BAxisGetter>(histogram, b1, b2, r1, r2, g1, g2, a1, a2);
|
||||
axisShrink<AAxisGetter>(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<Box>& boxes) const
|
||||
{
|
||||
bool split(const Histogram& histogram, std::priority_queue<Box>& boxes) const {
|
||||
// Split along the largest dimension of the box.
|
||||
if ((r2-r1) >= (g2-g1) && (r2-r1) >= (b2-b1)) {
|
||||
return splitAlongAxis<RAxisGetter, RAxisSplitter>(histogram, boxes, r1, r2, g1, g2, b1, b2);
|
||||
if ((r2-r1) >= (g2-g1) &&
|
||||
(r2-r1) >= (b2-b1) &&
|
||||
(r2-r1) >= (a2-a1)) {
|
||||
return splitAlongAxis<RAxisGetter, RAxisSplitter>(histogram, boxes, r1, r2, g1, g2, b1, b2, a1, a2);
|
||||
}
|
||||
else if ((g2-g1) >= (r2-r1) && (g2-g1) >= (b2-b1)) {
|
||||
return splitAlongAxis<GAxisGetter, GAxisSplitter>(histogram, boxes, g1, g2, r1, r2, b1, b2);
|
||||
|
||||
if ((g2-g1) >= (r2-r1) &&
|
||||
(g2-g1) >= (b2-b1) &&
|
||||
(g2-g1) >= (a2-a1)) {
|
||||
return splitAlongAxis<GAxisGetter, GAxisSplitter>(histogram, boxes, g1, g2, r1, r2, b1, b2, a1, a2);
|
||||
}
|
||||
else {
|
||||
return splitAlongAxis<BAxisGetter, BAxisSplitter>(histogram, boxes, b1, b2, r1, r2, g1, g2);
|
||||
|
||||
if ((b2-b1) >= (r2-r1) &&
|
||||
(b2-b1) >= (g2-g1) &&
|
||||
(b2-b1) >= (a2-a1)) {
|
||||
return splitAlongAxis<BAxisGetter, BAxisSplitter>(histogram, boxes, b1, b2, r1, r2, g1, g2, a1, a2);
|
||||
}
|
||||
|
||||
return splitAlongAxis<AAxisGetter, AAxisSplitter>(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<i2; ++i1) {
|
||||
for (j=j1; j<=j2; ++j) {
|
||||
for (k=k1; k<=k2; ++k) {
|
||||
if (AxisGetter::at(histogram, i1, j, k) > 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<Box>& 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<Box<Histogram> > boxes;
|
||||
|
||||
// First we start with one big box containing all histogram's samples.
|
||||
boxes.push(Box<Histogram>(0, 0, 0,
|
||||
boxes.push(Box<Histogram>(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.
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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<Image*>& images, Palette* palette, bool has_background_layer)
|
||||
void create_palette_from_images(const std::vector<Image*>& 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
|
||||
|
@ -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<Image*>& 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.
|
||||
|
@ -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:
|
||||
|
Loading…
x
Reference in New Issue
Block a user