diff --git a/data/palettes/tags.gpl b/data/palettes/tags.gpl
new file mode 100644
index 000000000..222849003
--- /dev/null
+++ b/data/palettes/tags.gpl
@@ -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
diff --git a/data/themes/default/sheet.png b/data/themes/default/sheet.png
index 2d9d162fc..05ff8f5eb 100644
Binary files a/data/themes/default/sheet.png and b/data/themes/default/sheet.png differ
diff --git a/data/themes/default/theme.xml b/data/themes/default/theme.xml
index 3dd8ee7a5..7d1f06adb 100644
--- a/data/themes/default/theme.xml
+++ b/data/themes/default/theme.xml
@@ -391,6 +391,8 @@
+
+
@@ -409,7 +411,7 @@
+
diff --git a/data/widgets/frame_tag_properties.xml b/data/widgets/frame_tag_properties.xml
index 59bd9ab1b..d57066bb1 100644
--- a/data/widgets/frame_tag_properties.xml
+++ b/data/widgets/frame_tag_properties.xml
@@ -1,6 +1,6 @@
-
+
@@ -14,7 +14,7 @@
-
+
diff --git a/data/widgets/user_data.xml b/data/widgets/user_data.xml
index 29207505d..5e3ac1948 100644
--- a/data/widgets/user_data.xml
+++ b/data/widgets/user_data.xml
@@ -7,7 +7,7 @@
-
+
diff --git a/docs/gpl-palette-extension.md b/docs/gpl-palette-extension.md
new file mode 100644
index 000000000..3324c425b
--- /dev/null
+++ b/docs/gpl-palette-extension.md
@@ -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.
diff --git a/src/app/commands/cmd_mask_by_color.cpp b/src/app/commands/cmd_mask_by_color.cpp
index b30b7adaf..72ba2136a 100644
--- a/src/app/commands/cmd_mask_by_color.cpp
+++ b/src/app/commands/cmd_mask_by_color.cpp
@@ -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");
diff --git a/src/app/commands/cmd_sprite_properties.cpp b/src/app/commands/cmd_sprite_properties.cpp
index 12907c58c..75584c428 100644
--- a/src/app/commands/cmd_sprite_properties.cpp
+++ b/src/app/commands/cmd_sprite_properties.cpp
@@ -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);
}
diff --git a/src/app/file/ase_format.cpp b/src/app/file/ase_format.cpp
index 8a738e489..021b70e99 100644
--- a/src/app/file/ase_format.cpp
+++ b/src/app/file/ase_format.cpp
@@ -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);
diff --git a/src/app/ui/color_bar.cpp b/src/app/ui/color_bar.cpp
index b938e7c01..6a5868262 100644
--- a/src/app/ui/color_bar.cpp
+++ b/src/app/ui/color_bar.cpp
@@ -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)
diff --git a/src/app/ui/color_button.cpp b/src/app/ui/color_button.cpp
index 7a84720b5..4d215b9f5 100644
--- a/src/app/ui/color_button.cpp
+++ b/src/app/ui/color_button.cpp
@@ -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);
}
diff --git a/src/app/ui/color_button.h b/src/app/ui/color_button.h
index 3c38e7679..d6737b7c4 100644
--- a/src/app/ui/color_button.h
+++ b/src/app/ui/color_button.h
@@ -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
diff --git a/src/app/ui/color_popup.cpp b/src/app/ui/color_popup.cpp
index 7a8a7bb1f..7ccacbba9 100644
--- a/src/app/ui/color_popup.cpp
+++ b/src/app/ui/color_popup.cpp
@@ -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 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; isize(); ++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; isize(); ++i) {
+ children()[i]->setSelected(i == index);
+ }
+ }
+
+ void deselect() {
+ for (int i=0; isize(); ++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()) {
diff --git a/src/app/ui/color_popup.h b/src/app/ui/color_popup.h
index 43e30ee61..af5fad714 100644
--- a/src/app/ui/color_popup.h
+++ b/src/app/ui/color_popup.h
@@ -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;
diff --git a/src/app/ui/context_bar.cpp b/src/app/ui/context_bar.cpp
index d6a838f1c..e0c5b1df5 100644
--- a/src/app/ui/context_bar.cpp
+++ b/src/app/ui/context_bar.cpp
@@ -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();
diff --git a/src/app/widget_loader.cpp b/src/app/widget_loader.cpp
index 30093c47d..26d3d6b78 100644
--- a/src/app/widget_loader.cpp
+++ b/src/app/widget_loader.cpp
@@ -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) {
diff --git a/src/doc/file/gpl_file.cpp b/src/doc/file/gpl_file.cpp
index 231dc7ba3..b9e016d74 100644
--- a/src/doc/file/gpl_file.cpp
+++ b/src/doc/file/gpl_file.cpp
@@ -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 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 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; isize(); ++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;
diff --git a/src/doc/palette.cpp b/src/doc/palette.cpp
index c1342ecb6..0c678fb5c 100644
--- a/src/doc/palette.cpp
+++ b/src/doc/palette.cpp
@@ -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
diff --git a/src/doc/palette.h b/src/doc/palette.h
index 9e109efcc..4fbac42c1 100644
--- a/src/doc/palette.h
+++ b/src/doc/palette.h
@@ -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 m_colors;
+ std::vector 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.).