aseprite/src/app/ui/dithering_selector.cpp

243 lines
6.2 KiB
C++
Raw Normal View History

// Aseprite
// Copyright (C) 2019 Igara Studio S.A.
// 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"
#include "app/modules/palettes.h"
#include "app/ui/skin/skin_theme.h"
#include "base/bind.h"
2018-08-09 23:58:43 +08:00
#include "doc/conversion_to_surface.h"
#include "doc/image.h"
#include "doc/image_ref.h"
#include "doc/primitives.h"
2018-08-09 23:58:43 +08:00
#include "os/surface.h"
#include "os/system.h"
#include "render/dithering.h"
#include "render/gradient.h"
#include "render/quantization.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:
DitherItem(render::DitheringAlgorithm algo,
const render::DitheringMatrix& matrix,
const std::string& text)
: ListItem(text)
, m_matrixOnly(false)
, m_dithering(algo, matrix)
, m_preview(nullptr)
, m_palId(0)
, m_palMods(0)
{
}
DitherItem(const render::DitheringMatrix& matrix,
const std::string& text)
: ListItem(text)
, m_matrixOnly(true)
, m_dithering(render::DitheringAlgorithm::None, matrix)
, m_preview(nullptr)
, m_palId(0)
, m_palMods(0)
{
}
render::DitheringAlgorithm algo() const {
return m_dithering.algorithm();
}
render::DitheringMatrix matrix() const {
return m_dithering.matrix();
}
private:
2018-08-09 23:58:43 +08:00
os::Surface* preview() {
const doc::Palette* palette = get_current_palette();
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;
}
const int w = 128, h = 16;
doc::ImageRef image1(doc::Image::create(doc::IMAGE_RGB, w, h));
render_rgba_linear_gradient(
image1.get(),
gfx::Point(0, 0),
2017-11-10 23:19:44 +08:00
gfx::Point(0, 0),
gfx::Point(w-1, 0),
doc::rgba(0, 0, 0, 255),
doc::rgba(255, 255, 255, 255),
(m_matrixOnly ? m_dithering.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_dithering, nullptr, palette, true, -1, nullptr);
}
2018-08-09 23:58:43 +08:00
m_preview = os::instance()->createRgbaSurface(w, h);
doc::convert_image_to_surface(image2.get(), palette, m_preview,
0, 0, 0, 0, w, h);
m_palId = palette->id();
m_palMods = palette->getModifications();
return m_preview;
}
void onSizeHint(SizeHintEvent& ev) override {
gfx::Size sz = textSize();
sz.w = MAX(sz.w, preview()->width()) + 4*guiscale();
sz.h += 6*guiscale() + preview()->height();
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(
preview(),
rc.x+2*guiscale(),
rc.y+4*guiscale()+textsz.h);
}
bool m_matrixOnly;
render::Dithering m_dithering;
2018-08-09 23:58:43 +08:00
os::Surface* m_preview;
doc::ObjectId m_palId;
int m_palMods;
};
} // anonymous namespace
DitheringSelector::DitheringSelector(Type type)
2017-06-15 03:34:09 +08:00
: m_type(type)
{
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));
setUseCustomWidget(true);
2017-06-15 03:34:09 +08:00
regenerate();
}
2017-06-15 03:34:09 +08:00
void DitheringSelector::regenerate()
{
removeAllItems();
Extensions& extensions = App::instance()->extensions();
auto ditheringMatrices = extensions.ditheringMatrices();
switch (m_type) {
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()));
}
addItem(
new DitherItem(
render::DitheringAlgorithm::ErrorDiffusion,
render::DitheringMatrix(),
"Floyd-Steinberg Error Diffusion Dithering"));
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()));
break;
}
setSelectedItemIndex(0);
setSizeHint(getItem(0)->sizeHint());
}
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