Add simple color selection for tag/user data colors

master
David Capello 2017-04-04 20:02:29 -03:00
parent 855be8f05f
commit 1671411465
19 changed files with 273 additions and 34 deletions

11
data/palettes/tags.gpl Normal file
View File

@ -0,0 +1,11 @@
GIMP Palette
Channels: RGBA
#
0 0 0 0 Transparent
254 91 89 255 Red
247 165 71 255 Orange
243 206 82 255 Yellow
106 205 91 255 Green
87 185 242 255 Blue
209 134 223 255 Purple
165 165 167 255 Gray

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 14 KiB

View File

@ -391,6 +391,8 @@
<part id="tool_configuration" x="144" y="128" w="16" h="16" />
<part id="tool_minieditor" x="160" y="128" w="16" h="16" />
<part id="tool_move_slice" x="224" y="48" w="16" h="16" />
<part id="simple_color_border" x="16" y="32" w1="3" w2="6" w3="3" h1="3" h2="6" h3="3" />
<part id="simple_color_selected" x="32" y="32" w1="3" w2="6" w3="3" h1="3" h2="6" h3="3" />
</parts>
<styles>
<style id="box" />
@ -409,7 +411,7 @@
</style>
<style id="window_button">
<background color="window_titlebar_face" />
<newlayer />
<newlayer />
<background part="window_button_normal" align="center middle" />
<background part="window_button_hot" state="mouse" align="center middle" />
<background part="window_button_selected" state="selected" align="center middle" />
@ -884,5 +886,11 @@
<background part="colorbar_selection" />
<background part="colorbar_selection_hot" state="mouse" />
</style>
<style id="simple_color">
<background color="face" />
<background color="menuitem_hot_face" state="mouse" />
<border part="simple_color_border" />
<border part="simple_color_selected" state="selected" />
</style>
</styles>
</theme>

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Aseprite -->
<!-- Copyright (C) 2015-2016 by David Capello -->
<!-- Copyright (C) 2015-2017 by David Capello -->
<gui>
<window id="frame_tag_properties" text="@.title">
<grid columns="2">
@ -14,7 +14,7 @@
<entry maxsize="10" id="to" />
<label text="@.color" />
<colorpicker id="color" />
<colorpicker id="color" simple="true" />
<label text="@.ani_dir" />
<combobox id="anidir" />

View File

@ -7,7 +7,7 @@
<entry id="text" magnet="true" maxsize="65535" minwidth="128" expansive="true" />
<hbox>
<label text="@.color" style="tooltip_text" />
<colorpicker id="color" expansive="true" />
<colorpicker id="color" simple="true" expansive="true" />
</hbox>
</vbox>
</tipwindow>

View File

@ -0,0 +1,26 @@
GIMP Palette File Format Extension
====================================================
Aseprite can load/save GIMP Palettes (`.gpl` files) extended with
alpha information using the following format:
```
GIMP Palette
Channels: RGBA
#
0 0 0 0 Transparent
254 91 89 255 Red
247 165 71 255 Orange
243 206 82 255 Yellow
106 205 91 255 Green
87 185 242 255 Blue
209 134 223 255 Purple
165 165 167 255 Gray
```
You must specify `Channels: RGBA` in the header and then each entry
must contain an extra alpha value. There are no plans to provide a
different value for `Channels` properties.
Note that this is an Aseprite extension, GIMP does not support
palettes with alpha values.

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2001-2016 David Capello
// Copyright (C) 2001-2017 David Capello
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
@ -104,7 +104,7 @@ void MaskByColorCommand::onExecute(Context* context)
(get_config_color("MaskColor", "Color",
ColorBar::instance()->getFgColor()),
sprite->pixelFormat(),
false);
false, false);
label_tolerance = new Label("Tolerance:");
m_sliderTolerance = new Slider(0, 255, get_config_int("MaskColor", "Tolerance", 0));
m_checkPreview = new CheckBox("&Preview");

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2001-2016 David Capello
// Copyright (C) 2001-2017 David Capello
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
@ -107,7 +107,7 @@ void SpritePropertiesCommand::onExecute(Context* context)
if (sprite->pixelFormat() == IMAGE_INDEXED) {
color_button = new ColorButton(app::Color::fromIndex(sprite->transparentColor()),
IMAGE_INDEXED,
false);
false, false);
window.transparentColorPlaceholder()->addChild(color_button);
}

View File

@ -864,6 +864,7 @@ static void ase_file_write_palette_chunk(FILE* f, ASE_FrameHeader* frame_header,
for (int c=from; c<=to; ++c) {
color_t color = pal->getEntry(c);
// TODO add support to save palette entry name
fputw(0, f); // Entry flags (without name)
fputc(rgba_getr(color), f);
fputc(rgba_getg(color), f);

View File

@ -118,8 +118,8 @@ ColorBar::ColorBar(int align)
, m_tintShadeTone(nullptr)
, m_spectrum(nullptr)
, m_wheel(nullptr)
, m_fgColor(app::Color::fromRgb(255, 255, 255), IMAGE_RGB, true)
, m_bgColor(app::Color::fromRgb(0, 0, 0), IMAGE_RGB, true)
, m_fgColor(app::Color::fromRgb(255, 255, 255), IMAGE_RGB, true, false)
, m_bgColor(app::Color::fromRgb(0, 0, 0), IMAGE_RGB, true, false)
, m_fgWarningIcon(new WarningIcon)
, m_bgWarningIcon(new WarningIcon)
, m_lock(false)

View File

@ -42,14 +42,16 @@ static WidgetType colorbutton_type()
}
ColorButton::ColorButton(const app::Color& color,
PixelFormat pixelFormat,
bool canPinSelector)
const PixelFormat pixelFormat,
const bool canPinSelector,
const bool showSimpleColors)
: ButtonBase("", colorbutton_type(), kButtonWidget, kButtonWidget)
, m_color(color)
, m_pixelFormat(pixelFormat)
, m_window(NULL)
, m_dependOnLayer(false)
, m_canPinSelector(canPinSelector)
, m_showSimpleColors(showSimpleColors)
{
setFocusStop(true);
initTheme();
@ -274,7 +276,7 @@ void ColorButton::openSelectorDialog()
bool pinned = (!m_windowDefaultBounds.isEmpty());
if (m_window == NULL) {
m_window = new ColorPopup(m_canPinSelector);
m_window = new ColorPopup(m_canPinSelector, m_showSimpleColors);
m_window->ColorChange.connect(&ColorButton::onWindowColorChange, this);
}

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2001-2016 David Capello
// Copyright (C) 2001-2017 David Capello
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
@ -27,8 +27,9 @@ namespace app {
, public IColorSource {
public:
ColorButton(const app::Color& color,
PixelFormat pixelFormat,
bool canPinSelector);
const PixelFormat pixelFormat,
const bool canPinSelector,
const bool showSimpleColors);
~ColorButton();
PixelFormat pixelFormat() const;
@ -65,6 +66,7 @@ namespace app {
gfx::Rect m_windowDefaultBounds;
bool m_dependOnLayer;
bool m_canPinSelector;
bool m_showSimpleColors;
};
} // namespace app

View File

@ -17,15 +17,18 @@
#include "app/context.h"
#include "app/context_access.h"
#include "app/document.h"
#include "app/file/palette_file.h"
#include "app/modules/gfx.h"
#include "app/modules/gui.h"
#include "app/modules/palettes.h"
#include "app/resource_finder.h"
#include "app/transaction.h"
#include "app/ui/palette_view.h"
#include "app/ui/skin/skin_theme.h"
#include "app/ui_context.h"
#include "base/bind.h"
#include "base/scoped_value.h"
#include "base/unique_ptr.h"
#include "doc/image_impl.h"
#include "doc/palette.h"
#include "doc/sprite.h"
@ -46,7 +49,73 @@ enum {
MASK_MODE
};
ColorPopup::ColorPopup(bool canPin)
static base::UniquePtr<doc::Palette> g_simplePal(nullptr);
class ColorPopup::SimpleColors : public HBox {
public:
class Item : public Button {
public:
Item(ColorPopup* colorPopup, const app::Color& color)
: Button("")
, m_colorPopup(colorPopup)
, m_color(color) {
}
private:
void onClick(Event& ev) override {
m_colorPopup->setColorWithSignal(m_color);
}
void onPaint(PaintEvent& ev) override {
Graphics* g = ev.graphics();
skin::SkinTheme* theme = skin::SkinTheme::instance();
gfx::Rect rc = clientBounds();
Button::onPaint(ev);
rc.shrink(theme->calcBorder(this, style()));
draw_color(g, rc, m_color, doc::ColorMode::RGB);
}
ColorPopup* m_colorPopup;
app::Color m_color;
};
SimpleColors(ColorPopup* colorPopup, TooltipManager* tooltips) {
for (int i=0; i<g_simplePal->size(); ++i) {
doc::color_t c = g_simplePal->getEntry(i);
app::Color color =
app::Color::fromRgb(doc::rgba_getr(c),
doc::rgba_getg(c),
doc::rgba_getb(c),
doc::rgba_geta(c));
Item* item = new Item(colorPopup, color);
item->setSizeHint(gfx::Size(16, 16)*ui::guiscale());
item->setStyle(skin::SkinTheme::instance()->styles.simpleColor());
addChild(item);
tooltips->addTooltipFor(
item, g_simplePal->getEntryName(i), BOTTOM);
}
}
void selectColor(int index) {
for (int i=0; i<g_simplePal->size(); ++i) {
children()[i]->setSelected(i == index);
}
}
void deselect() {
for (int i=0; i<g_simplePal->size(); ++i) {
children()[i]->setSelected(false);
}
}
};
ColorPopup::ColorPopup(const bool canPin,
bool showSimpleColors)
: PopupWindowPin(" ", // Non-empty to create title-bar and close button
ClickBehavior::CloseOnClickInOtherWindow,
canPin)
@ -54,11 +123,26 @@ ColorPopup::ColorPopup(bool canPin)
, m_topBox(HORIZONTAL)
, m_color(app::Color::fromMask())
, m_colorPalette(false, PaletteView::SelectOneColor, this, 7*guiscale())
, m_simpleColors(nullptr)
, m_colorType(5)
, m_maskLabel("Transparent Color Selected")
, m_canPin(canPin)
, m_disableHexUpdate(false)
{
if (showSimpleColors) {
if (!g_simplePal) {
ResourceFinder rf;
rf.includeDataDir("palettes/tags.gpl");
if (rf.findFirst())
g_simplePal.reset(load_palette(rf.filename().c_str()));
}
if (g_simplePal)
m_simpleColors = new SimpleColors(this, &m_tooltips);
else
showSimpleColors = false;
}
m_colorType.addItem("Index")->setFocusStop(false);
m_colorType.addItem("RGB")->setFocusStop(false);
m_colorType.addItem("HSB")->setFocusStop(false);
@ -69,7 +153,6 @@ ColorPopup::ColorPopup(bool canPin)
m_topBox.setChildSpacing(0);
m_colorPaletteContainer.attachToView(&m_colorPalette);
m_colorPaletteContainer.setExpansive(true);
m_rgbSliders.setExpansive(true);
m_hsvSliders.setExpansive(true);
@ -100,6 +183,9 @@ ColorPopup::ColorPopup(bool canPin)
}
setText(""); // To remove title
m_vbox.addChild(&m_tooltips);
if (m_simpleColors)
m_vbox.addChild(m_simpleColors);
m_vbox.addChild(&m_topBox);
m_vbox.addChild(&m_colorPaletteContainer);
m_vbox.addChild(&m_rgbSliders);
@ -115,8 +201,11 @@ ColorPopup::ColorPopup(bool canPin)
m_graySlider.ColorChange.connect(&ColorPopup::onColorSlidersChange, this);
m_hexColorEntry.ColorChange.connect(&ColorPopup::onColorHexEntryChange, this);
// Set RGB just for the sizeHint(), and then deselect the color type
// (the first setColor() call will setup it correctly.)
selectColorType(app::Color::RgbType);
setSizeHint(gfx::Size(300*guiscale(), sizeHint().h));
m_colorType.deselectItems();
m_onPaletteChangeConn =
App::instance()->PaletteChange.connect(&ColorPopup::onPaletteChange, this);
@ -132,6 +221,18 @@ void ColorPopup::setColor(const app::Color& color, SetColorOptions options)
{
m_color = color;
if (m_simpleColors) {
int r = color.getRed();
int g = color.getGreen();
int b = color.getBlue();
int a = color.getAlpha();
int i = g_simplePal->findExactMatch(r, g, b, a, -1);
if (i >= 0)
m_simpleColors->selectColor(i);
else
m_simpleColors->deselect();
}
if (color.getType() == app::Color::IndexType) {
m_colorPalette.deselect();
m_colorPalette.selectColor(color.getIndex());
@ -195,8 +296,38 @@ void ColorPopup::onColorHexEntryChange(const app::Color& color)
m_disableHexUpdate = false;
}
void ColorPopup::onSimpleColorClick()
{
m_colorType.deselectItems();
if (!g_simplePal)
return;
app::Color color = getColor();
// Find bestfit palette entry
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 = g_simplePal->findBestfit(r, g, b, a, 0);
if (i >= 0) {
color_t c = g_simplePal->getEntry(i);
color = app::Color::fromRgb(doc::rgba_getr(c),
doc::rgba_getg(c),
doc::rgba_getb(c),
doc::rgba_geta(c));
}
setColorWithSignal(color);
}
void ColorPopup::onColorTypeClick()
{
if (m_simpleColors)
m_simpleColors->deselect();
app::Color newColor = getColor();
switch (m_colorType.selectedItem()) {

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2001-2016 David Capello
// Copyright (C) 2001-2017 David Capello
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
@ -18,6 +18,7 @@
#include "obs/signal.h"
#include "ui/grid.h"
#include "ui/label.h"
#include "ui/tooltips.h"
#include "ui/view.h"
namespace app {
@ -31,7 +32,7 @@ namespace app {
DoNotChangeType
};
ColorPopup(bool canPin);
ColorPopup(const bool canPin, bool showSimpleColors);
~ColorPopup();
void setColor(const app::Color& color, SetColorOptions options);
@ -45,6 +46,7 @@ namespace app {
void onMakeFixed() override;
void onColorSlidersChange(ColorSlidersChangeEvent& ev);
void onColorHexEntryChange(const app::Color& color);
void onSimpleColorClick();
void onColorTypeClick();
void onPaletteChange();
@ -53,14 +55,19 @@ namespace app {
private:
void selectColorType(app::Color::Type type);
// void selectSimpleColor(const app::Color& color);
void setColorWithSignal(const app::Color& color);
void findBestfitIndex(const app::Color& color);
class SimpleColors;
ui::Box m_vbox;
ui::TooltipManager m_tooltips;
ui::Box m_topBox;
app::Color m_color;
ui::View m_colorPaletteContainer;
PaletteView m_colorPalette;
SimpleColors* m_simpleColors;
ButtonSet m_colorType;
HexColorEntry m_hexColorEntry;
RgbSliders m_rgbSliders;

View File

@ -926,7 +926,7 @@ class ContextBar::TransparentColorField : public HBox {
public:
TransparentColorField(ContextBar* owner)
: m_icon(1)
, m_maskColor(app::Color::fromMask(), IMAGE_RGB, false)
, m_maskColor(app::Color::fromMask(), IMAGE_RGB, false, false)
, m_owner(owner) {
SkinTheme* theme = SkinTheme::instance();

View File

@ -403,12 +403,14 @@ Widget* WidgetLoader::convertXmlElementToWidget(const TiXmlElement* elem, Widget
}
}
else if (elem_name == "colorpicker") {
bool rgba = bool_attr_is_true(elem, "rgba");
const bool rgba = bool_attr_is_true(elem, "rgba");
const bool simple = bool_attr_is_true(elem, "simple");
if (!widget)
widget = new ColorButton(Color::fromMask(),
rgba ? IMAGE_RGB:
app_get_current_pixel_format(), false);
app_get_current_pixel_format(), false,
simple);
}
else if (elem_name == "dropdownbutton") {
if (!widget) {

View File

@ -1,5 +1,5 @@
// Aseprite Document Library
// Copyright (c) 2001-2016 David Capello
// Copyright (c) 2001-2017 David Capello
//
// This file is released under the terms of the MIT license.
// Read LICENSE.txt for more information.
@ -38,6 +38,7 @@ Palette* load_gpl_file(const char *filename)
base::UniquePtr<Palette> pal(new Palette(frame_t(0), 0));
std::string comment;
bool hasAlpha = false;
while (std::getline(f, line)) {
// Trim line.
@ -57,18 +58,37 @@ Palette* load_gpl_file(const char *filename)
}
// Remove properties (TODO add these properties in the palette)
if (!std::isdigit(line[0]))
if (!std::isdigit(line[0])) {
std::vector<std::string> parts;
base::split_string(line, parts, ":");
// Aseprite extension for palettes with alpha channel.
if (parts.size() == 2 &&
parts[0] == "Channels") {
base::trim_string(parts[1], parts[1]);
if (parts[1] == "RGBA")
hasAlpha = true;
}
continue;
}
int r, g, b;
int r, g, b, a = 255;
std::string entryName;
std::istringstream lineIn(line);
// TODO add support to read the color name
lineIn >> r >> g >> b;
if (hasAlpha) {
lineIn >> a;
}
lineIn >> entryName;
if (lineIn.fail())
continue;
pal->addEntry(rgba(r, g, b, 255));
pal->addEntry(rgba(r, g, b, a));
if (!entryName.empty()) {
base::trim_string(entryName, entryName);
if (!entryName.empty())
pal->setEntryName(pal->size()-1, entryName);
}
}
base::trim_string(comment, comment);
@ -80,19 +100,26 @@ Palette* load_gpl_file(const char *filename)
return pal.release();
}
bool save_gpl_file(const Palette *pal, const char *filename)
bool save_gpl_file(const Palette* pal, const char* filename)
{
std::ofstream f(FSTREAM_PATH(filename));
if (f.bad()) return false;
f << "GIMP Palette\n"
<< "#\n";
const bool hasAlpha = pal->hasAlpha();
f << "GIMP Palette\n";
if (hasAlpha)
f << "Channels: RGBA\n";
f << "#\n";
for (int i=0; i<pal->size(); ++i) {
uint32_t col = pal->getEntry(i);
f << std::setfill(' ') << std::setw(3) << ((int)rgba_getr(col)) << " "
<< std::setfill(' ') << std::setw(3) << ((int)rgba_getg(col)) << " "
<< std::setfill(' ') << std::setw(3) << ((int)rgba_getb(col)) << "\tUntitled\n";
<< std::setfill(' ') << std::setw(3) << ((int)rgba_getb(col));
if (hasAlpha)
f << " " << std::setfill(' ') << std::setw(3) << ((int)rgba_geta(col));
f << "\tUntitled\n"; // TODO add support for color name entries
}
return true;

View File

@ -1,5 +1,5 @@
// Aseprite Document Library
// Copyright (c) 2001-2016 David Capello
// Copyright (c) 2001-2017 David Capello
//
// This file is released under the terms of the MIT license.
// Read LICENSE.txt for more information.
@ -278,4 +278,21 @@ void Palette::applyRemap(const Remap& remap)
setEntry(remap[i], original.getEntry(i));
}
void Palette::setEntryName(const int i, const std::string& name)
{
if (i >= m_names.size())
m_names.resize(i+1);
m_names[i] = name;
}
const std::string& Palette::getEntryName(const int i) const
{
if (i >= 0 && i < int(m_names.size()))
return m_names[i];
else {
static std::string emptyString;
return emptyString;
}
}
} // namespace doc

View File

@ -1,5 +1,5 @@
// Aseprite Document Library
// Copyright (c) 2001-2016 David Capello
// Copyright (c) 2001-2017 David Capello
//
// This file is released under the terms of the MIT license.
// Read LICENSE.txt for more information.
@ -92,9 +92,14 @@ namespace doc {
void applyRemap(const Remap& remap);
// TODO add undo/redo support of entry names
void setEntryName(const int i, const std::string& name);
const std::string& getEntryName(const int i) const;
private:
frame_t m_frame;
std::vector<color_t> m_colors;
std::vector<std::string> m_names;
int m_modifications;
std::string m_filename; // If the palette is associated with a file.
std::string m_comment; // Some extra comment from the .gpl file (author, website, etc.).