2017-05-23 02:43:53 +08:00
|
|
|
// Aseprite
|
|
|
|
|
// Copyright (C) 2017 David Capello
|
|
|
|
|
//
|
|
|
|
|
// This program is distributed under the terms of
|
|
|
|
|
// the End-User License Agreement for Aseprite.
|
|
|
|
|
|
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
|
|
|
#include "config.h"
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#include "app/ui/dithering_selector.h"
|
|
|
|
|
|
2017-06-15 03:34:09 +08:00
|
|
|
#include "app/app.h"
|
|
|
|
|
#include "app/extensions.h"
|
2017-05-23 02:43:53 +08:00
|
|
|
#include "app/modules/palettes.h"
|
|
|
|
|
#include "app/ui/skin/skin_theme.h"
|
|
|
|
|
#include "base/bind.h"
|
|
|
|
|
#include "doc/conversion_she.h"
|
|
|
|
|
#include "doc/image.h"
|
|
|
|
|
#include "doc/image_ref.h"
|
|
|
|
|
#include "doc/primitives.h"
|
2017-06-10 04:31:41 +08:00
|
|
|
#include "render/gradient.h"
|
2017-05-23 02:43:53 +08:00
|
|
|
#include "render/quantization.h"
|
|
|
|
|
#include "she/surface.h"
|
|
|
|
|
#include "she/system.h"
|
|
|
|
|
#include "ui/graphics.h"
|
|
|
|
|
#include "ui/listitem.h"
|
|
|
|
|
#include "ui/paint_event.h"
|
|
|
|
|
#include "ui/size_hint_event.h"
|
|
|
|
|
|
|
|
|
|
namespace app {
|
|
|
|
|
|
|
|
|
|
using namespace ui;
|
|
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
|
|
|
|
|
class DitherItem : public ListItem {
|
|
|
|
|
public:
|
2017-06-10 04:31:41 +08:00
|
|
|
|
2017-05-23 02:43:53 +08:00
|
|
|
DitherItem(render::DitheringAlgorithm algo,
|
|
|
|
|
const render::DitheringMatrix& matrix,
|
|
|
|
|
const std::string& text)
|
|
|
|
|
: ListItem(text)
|
2017-06-10 04:31:41 +08:00
|
|
|
, m_matrixOnly(false)
|
2017-05-23 02:43:53 +08:00
|
|
|
, m_algo(algo)
|
2017-06-02 05:18:44 +08:00
|
|
|
, m_matrix(matrix)
|
|
|
|
|
, m_preview(nullptr)
|
|
|
|
|
, m_palId(0)
|
|
|
|
|
, m_palMods(0)
|
|
|
|
|
{
|
2017-05-23 02:43:53 +08:00
|
|
|
}
|
|
|
|
|
|
2017-06-10 04:31:41 +08:00
|
|
|
DitherItem(const render::DitheringMatrix& matrix,
|
|
|
|
|
const std::string& text)
|
|
|
|
|
: ListItem(text)
|
|
|
|
|
, m_matrixOnly(true)
|
|
|
|
|
, m_algo(render::DitheringAlgorithm::None)
|
|
|
|
|
, m_matrix(matrix)
|
|
|
|
|
, m_preview(nullptr)
|
|
|
|
|
, m_palId(0)
|
|
|
|
|
, m_palMods(0)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
2017-05-23 02:43:53 +08:00
|
|
|
render::DitheringAlgorithm algo() const { return m_algo; }
|
|
|
|
|
render::DitheringMatrix matrix() const { return m_matrix; }
|
|
|
|
|
|
|
|
|
|
private:
|
2017-06-02 05:18:44 +08:00
|
|
|
she::Surface* preview() {
|
2017-05-23 02:43:53 +08:00
|
|
|
const doc::Palette* palette = get_current_palette();
|
2017-06-02 05:18:44 +08:00
|
|
|
ASSERT(palette);
|
|
|
|
|
|
|
|
|
|
if (m_preview) {
|
|
|
|
|
// Reuse the preview in case that the palette is exactly the same
|
|
|
|
|
if (palette->id() == m_palId &&
|
|
|
|
|
palette->getModifications() == m_palMods)
|
|
|
|
|
return m_preview;
|
|
|
|
|
|
|
|
|
|
// In other case regenerate the preview for the current palette
|
|
|
|
|
m_preview->dispose();
|
|
|
|
|
m_preview = nullptr;
|
|
|
|
|
}
|
2017-05-23 02:43:53 +08:00
|
|
|
|
|
|
|
|
const int w = 128, h = 16;
|
|
|
|
|
doc::ImageRef image1(doc::Image::create(doc::IMAGE_RGB, w, h));
|
2017-06-10 04:31:41 +08:00
|
|
|
render_rgba_linear_gradient(
|
|
|
|
|
image1.get(),
|
|
|
|
|
gfx::Point(0, 0),
|
|
|
|
|
gfx::Point(w-1, 0),
|
|
|
|
|
doc::rgba(0, 0, 0, 255),
|
|
|
|
|
doc::rgba(255, 255, 255, 255),
|
|
|
|
|
m_matrixOnly ? m_matrix: render::DitheringMatrix());
|
|
|
|
|
|
|
|
|
|
doc::ImageRef image2;
|
|
|
|
|
if (m_matrixOnly) {
|
|
|
|
|
image2 = image1;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
image2.reset(doc::Image::create(doc::IMAGE_INDEXED, w, h));
|
|
|
|
|
doc::clear_image(image2.get(), 0);
|
|
|
|
|
render::convert_pixel_format(
|
|
|
|
|
image1.get(), image2.get(), IMAGE_INDEXED,
|
|
|
|
|
m_algo, m_matrix, nullptr, palette, true, -1, nullptr);
|
|
|
|
|
}
|
2017-05-23 02:43:53 +08:00
|
|
|
|
|
|
|
|
m_preview = she::instance()->createRgbaSurface(w, h);
|
|
|
|
|
doc::convert_image_to_surface(image2.get(), palette, m_preview,
|
|
|
|
|
0, 0, 0, 0, w, h);
|
2017-06-02 05:18:44 +08:00
|
|
|
|
|
|
|
|
m_palId = palette->id();
|
|
|
|
|
m_palMods = palette->getModifications();
|
|
|
|
|
return m_preview;
|
2017-05-23 02:43:53 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void onSizeHint(SizeHintEvent& ev) override {
|
|
|
|
|
gfx::Size sz = textSize();
|
|
|
|
|
|
2017-06-02 05:18:44 +08:00
|
|
|
sz.w = MAX(sz.w, preview()->width()) + 4*guiscale();
|
|
|
|
|
sz.h += 6*guiscale() + preview()->height();
|
2017-05-23 02:43:53 +08:00
|
|
|
|
|
|
|
|
ev.setSizeHint(sz);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void onPaint(PaintEvent& ev) override {
|
|
|
|
|
Graphics* g = ev.graphics();
|
|
|
|
|
skin::SkinTheme* theme = static_cast<skin::SkinTheme*>(this->theme());
|
|
|
|
|
|
|
|
|
|
gfx::Color fg, bg;
|
|
|
|
|
if (isSelected()) {
|
|
|
|
|
fg = theme->colors.listitemSelectedText();
|
|
|
|
|
bg = theme->colors.listitemSelectedFace();
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
fg = theme->colors.listitemNormalText();
|
|
|
|
|
bg = theme->colors.listitemNormalFace();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
gfx::Rect rc = clientBounds();
|
|
|
|
|
g->fillRect(bg, rc);
|
|
|
|
|
|
|
|
|
|
gfx::Size textsz = textSize();
|
|
|
|
|
g->drawText(text(), fg, bg,
|
|
|
|
|
gfx::Point(rc.x+2*guiscale(),
|
|
|
|
|
rc.y+2*guiscale()));
|
|
|
|
|
g->drawRgbaSurface(
|
2017-06-02 05:18:44 +08:00
|
|
|
preview(),
|
2017-05-23 02:43:53 +08:00
|
|
|
rc.x+2*guiscale(),
|
|
|
|
|
rc.y+4*guiscale()+textsz.h);
|
|
|
|
|
}
|
|
|
|
|
|
2017-06-10 04:31:41 +08:00
|
|
|
bool m_matrixOnly;
|
2017-05-23 02:43:53 +08:00
|
|
|
render::DitheringAlgorithm m_algo;
|
|
|
|
|
render::DitheringMatrix m_matrix;
|
|
|
|
|
she::Surface* m_preview;
|
2017-06-02 05:18:44 +08:00
|
|
|
doc::ObjectId m_palId;
|
|
|
|
|
int m_palMods;
|
2017-05-23 02:43:53 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
} // anonymous namespace
|
|
|
|
|
|
2017-06-10 04:31:41 +08:00
|
|
|
DitheringSelector::DitheringSelector(Type type)
|
2017-06-15 03:34:09 +08:00
|
|
|
: m_type(type)
|
2017-05-23 02:43:53 +08:00
|
|
|
{
|
2017-06-15 03:34:09 +08:00
|
|
|
Extensions& extensions = App::instance()->extensions();
|
|
|
|
|
|
|
|
|
|
// If an extension with "ditheringMatrices" is disable/enable, we
|
|
|
|
|
// regenerate this DitheringSelector
|
|
|
|
|
m_extChanges =
|
|
|
|
|
extensions.DitheringMatricesChange.connect(
|
|
|
|
|
base::Bind<void>(&DitheringSelector::regenerate, this));
|
|
|
|
|
|
2017-05-24 00:41:11 +08:00
|
|
|
setUseCustomWidget(true);
|
2017-06-15 03:34:09 +08:00
|
|
|
regenerate();
|
|
|
|
|
}
|
2017-05-24 00:41:11 +08:00
|
|
|
|
2017-06-15 03:34:09 +08:00
|
|
|
void DitheringSelector::regenerate()
|
|
|
|
|
{
|
|
|
|
|
removeAllItems();
|
|
|
|
|
|
|
|
|
|
Extensions& extensions = App::instance()->extensions();
|
|
|
|
|
auto ditheringMatrices = extensions.ditheringMatrices();
|
|
|
|
|
|
|
|
|
|
switch (m_type) {
|
2017-06-10 04:31:41 +08:00
|
|
|
case SelectBoth:
|
|
|
|
|
addItem(new DitherItem(render::DitheringAlgorithm::None,
|
|
|
|
|
render::DitheringMatrix(), "No Dithering"));
|
2017-06-15 03:34:09 +08:00
|
|
|
for (const auto& it : ditheringMatrices) {
|
|
|
|
|
addItem(
|
|
|
|
|
new DitherItem(
|
|
|
|
|
render::DitheringAlgorithm::Ordered,
|
|
|
|
|
it.matrix(),
|
|
|
|
|
"Ordered Dithering+" + it.name()));
|
|
|
|
|
}
|
|
|
|
|
for (const auto& it : ditheringMatrices) {
|
|
|
|
|
addItem(
|
|
|
|
|
new DitherItem(
|
|
|
|
|
render::DitheringAlgorithm::Old,
|
|
|
|
|
it.matrix(),
|
|
|
|
|
"Old Dithering+" + it.name()));
|
|
|
|
|
}
|
2017-06-10 04:31:41 +08:00
|
|
|
break;
|
|
|
|
|
case SelectMatrix:
|
|
|
|
|
addItem(new DitherItem(render::DitheringMatrix(), "No Dithering"));
|
2017-06-15 03:34:09 +08:00
|
|
|
for (auto& it : ditheringMatrices)
|
|
|
|
|
addItem(new DitherItem(it.matrix(), it.name()));
|
2017-06-10 04:31:41 +08:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2017-05-23 02:43:53 +08:00
|
|
|
setSelectedItemIndex(0);
|
2017-05-24 07:41:30 +08:00
|
|
|
setSizeHint(getItem(0)->sizeHint());
|
2017-05-23 02:43:53 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
render::DitheringAlgorithm DitheringSelector::ditheringAlgorithm()
|
|
|
|
|
{
|
|
|
|
|
auto item = static_cast<DitherItem*>(getSelectedItem());
|
|
|
|
|
if (item)
|
|
|
|
|
return item->algo();
|
|
|
|
|
else
|
|
|
|
|
return render::DitheringAlgorithm::None;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
render::DitheringMatrix DitheringSelector::ditheringMatrix()
|
|
|
|
|
{
|
|
|
|
|
auto item = static_cast<DitherItem*>(getSelectedItem());
|
|
|
|
|
if (item)
|
|
|
|
|
return item->matrix();
|
|
|
|
|
else
|
|
|
|
|
return render::DitheringMatrix();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} // namespace app
|