Improve performance drawing color selectors (fix #1516, fix #1695)

This commit is contained in:
David Capello 2018-03-28 23:39:07 -03:00
parent 5a91325750
commit 0fe5edb7fe
10 changed files with 521 additions and 110 deletions

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2001-2017 David Capello
// Copyright (C) 2001-2018 David Capello
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
@ -8,15 +8,11 @@
#include "config.h"
#endif
#include "gfx/color.h"
#include "ui/intern.h"
#include "ui/system.h"
#include "ui/theme.h"
#include "app/modules/gfx.h"
#include "app/app.h"
#include "app/color_utils.h"
#include "app/console.h"
#include "app/modules/gfx.h"
#include "app/modules/gui.h"
#include "app/modules/palettes.h"
#include "app/ui/editor/editor.h"
@ -24,8 +20,13 @@
#include "doc/blend_funcs.h"
#include "doc/image.h"
#include "doc/palette.h"
#include "gfx/color.h"
#include "gfx/point.h"
#include "gfx/rect.h"
#include "she/surface.h"
#include "ui/intern.h"
#include "ui/system.h"
#include "ui/theme.h"
namespace app {
@ -181,4 +182,31 @@ void draw_alpha_slider(ui::Graphics* g,
}
}
// TODO this code is exactly the same as draw_alpha_slider() with a ui::Graphics
void draw_alpha_slider(she::Surface* s,
const gfx::Rect& rc,
const app::Color& color)
{
const int xmax = MAX(1, rc.w-1);
const doc::color_t c =
(color.getType() != app::Color::MaskType ?
doc::rgba(color.getRed(),
color.getGreen(),
color.getBlue(), 255): 0);
for (int x=0; x<rc.w; ++x) {
const int a = (255 * x / xmax);
const doc::color_t c1 = doc::rgba_blender_normal(gridColor1, c, a);
const doc::color_t c2 = doc::rgba_blender_normal(gridColor2, c, a);
const int mid = rc.h/2;
const int odd = (x / rc.h) & 1;
s->drawVLine(
app::color_utils::color_for_ui(app::Color::fromImage(IMAGE_RGB, odd ? c2: c1)),
rc.x+x, rc.y, mid);
s->drawVLine(
app::color_utils::color_for_ui(app::Color::fromImage(IMAGE_RGB, odd ? c1: c2)),
rc.x+x, rc.y+mid, rc.h-mid);
}
}
} // namespace app

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2001-2017 David Capello
// Copyright (C) 2001-2018 David Capello
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
@ -13,7 +13,14 @@
#include "gfx/color.h"
#include "gfx/rect.h"
#include "ui/base.h"
#include "ui/graphics.h"
namespace she {
class Surface;
}
namespace ui {
class Graphics;
}
namespace app {
using namespace doc;
@ -34,6 +41,10 @@ namespace app {
const gfx::Rect& rc,
const app::Color& color);
void draw_alpha_slider(she::Surface* s,
const gfx::Rect& rc,
const app::Color& color);
} // namespace app
#endif

View File

@ -1,9 +1,11 @@
// Aseprite
// Copyright (C) 2016-2017 David Capello
// Copyright (C) 2016-2018 David Capello
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
#define COLSEL_TRACE(...)
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
@ -14,29 +16,193 @@
#include "app/modules/gfx.h"
#include "app/ui/skin/skin_theme.h"
#include "app/ui/status_bar.h"
#include "base/concurrent_queue.h"
#include "base/scoped_value.h"
#include "base/thread.h"
#include "she/surface.h"
#include "she/system.h"
#include "ui/manager.h"
#include "ui/message.h"
#include "ui/paint_event.h"
#include "ui/register_message.h"
#include "ui/scale.h"
#include "ui/size_hint_event.h"
#include "ui/system.h"
#include <cmath>
#include <condition_variable>
#include <thread>
namespace app {
using namespace app::skin;
using namespace ui;
// TODO This logic could be used to redraw any widget:
// 1. We send a onPaintSurfaceInBgThread() to paint the widget on a
// offscreen buffer
// 2. When the painting is done, we flip the buffer onto the screen
// 3. If we receive another onPaint() we can cancel the background
// painting and start another onPaintSurfaceInBgThread()
class ColorSelector::Painter {
public:
Painter()
: m_canvas(nullptr)
, m_paintingThread([this]{ paintingProc(); })
{
}
~Painter() {
if (m_canvas)
m_canvas->dispose();
}
void addRef() {
++m_ref;
}
void releaseRef() {
--m_ref;
if (m_ref == 0) {
{
std::unique_lock<std::mutex> lock(m_mutex);
stopCurrentPainting(lock);
m_killing = true;
m_paintingCV.notify_one();
}
m_paintingThread.join();
}
}
she::Surface* getCanvas(int w, int h, gfx::Color bgColor) {
if (!m_canvas ||
m_canvas->width() != w ||
m_canvas->height() != h) {
std::unique_lock<std::mutex> lock(m_mutex);
stopCurrentPainting(lock);
auto oldCanvas = m_canvas;
m_canvas = she::instance()->createSurface(w, h);
m_canvas->fillRect(bgColor, gfx::Rect(0, 0, w, h));
if (oldCanvas) {
m_canvas->drawSurface(oldCanvas, 0, 0);
oldCanvas->dispose();
}
}
return m_canvas;
}
void startBgPainting(ColorSelector* colorSelector,
const gfx::Rect& mainBounds,
const gfx::Rect& bottomBarBounds,
const gfx::Rect& alphaBarBounds) {
COLSEL_TRACE("COLSEL: startBgPainting for %p\n", colorSelector);
std::unique_lock<std::mutex> lock(m_mutex);
stopCurrentPainting(lock);
m_colorSelector = colorSelector;
m_manager = colorSelector->manager();
m_mainBounds = mainBounds;
m_bottomBarBounds = bottomBarBounds;
m_alphaBarBounds = alphaBarBounds;
m_stopPainting = false;
m_paintingCV.notify_one();
}
private:
void stopCurrentPainting(std::unique_lock<std::mutex>& lock) {
// Stop current
if (m_colorSelector) {
COLSEL_TRACE("COLSEL: stoppping painting of %p\n", m_colorSelector);
// TODO use another condition variable here
m_stopPainting = true;
m_waitStopCV.wait(lock);
}
ASSERT(m_colorSelector == nullptr);
}
void paintingProc() {
COLSEL_TRACE("COLSEL: paintingProc starts\n");
std::unique_lock<std::mutex> lock(m_mutex);
while (true) {
m_paintingCV.wait(lock);
if (m_killing)
break;
auto colorSel = m_colorSelector;
if (!colorSel)
break;
COLSEL_TRACE("COLSEL: starting painting in bg for %p\n", colorSel);
// Do the intesive painting in the background thread
{
lock.unlock();
colorSel->onPaintSurfaceInBgThread(
m_canvas,
m_mainBounds,
m_bottomBarBounds,
m_alphaBarBounds,
m_stopPainting);
lock.lock();
}
m_colorSelector = nullptr;
if (m_stopPainting) {
COLSEL_TRACE("COLSEL: painting for %p stopped\n");
m_waitStopCV.notify_one();
}
else {
COLSEL_TRACE("COLSEL: painting for %p done and sending message\n");
colorSel->m_paintFlags |= DoneFlag;
}
}
COLSEL_TRACE("COLSEL: paintingProc ends\n");
}
int m_ref = 0;
bool m_killing = false;
bool m_stopPainting = false;
std::mutex m_mutex;
std::condition_variable m_paintingCV;
std::condition_variable m_waitStopCV;
she::Surface* m_canvas;
ColorSelector* m_colorSelector;
ui::Manager* m_manager;
gfx::Rect m_mainBounds;
gfx::Rect m_bottomBarBounds;
gfx::Rect m_alphaBarBounds;
std::thread m_paintingThread;
};
static ColorSelector::Painter painter;
ColorSelector::ColorSelector()
: Widget(kGenericWidget)
, m_paintFlags(AllAreasFlag)
, m_lockColor(false)
, m_capturedInBottom(false)
, m_capturedInAlpha(false)
, m_timer(100, this)
{
initTheme();
painter.addRef();
}
ColorSelector::~ColorSelector()
{
painter.releaseRef();
}
void ColorSelector::selectColor(const app::Color& color)
@ -44,6 +210,9 @@ void ColorSelector::selectColor(const app::Color& color)
if (m_lockColor)
return;
if (m_color != color)
m_paintFlags |= onNeedsSurfaceRepaint(color);
m_color = color;
invalidate();
}
@ -166,6 +335,14 @@ bool ColorSelector::onProcessMessage(ui::Message* msg)
}
break;
case kTimerMessage:
if (m_paintFlags & DoneFlag) {
m_timer.stop();
invalidate();
return true;
}
break;
}
return Widget::onProcessMessage(msg);
@ -177,6 +354,15 @@ void ColorSelector::onInitTheme(ui::InitThemeEvent& ev)
setBorder(gfx::Border(3*ui::guiscale()));
}
void ColorSelector::onResize(ui::ResizeEvent& ev)
{
Widget::onResize(ev);
// We'll need to redraw the whole surface again with the new widget
// size.
m_paintFlags = AllAreasFlag;
}
void ColorSelector::onPaint(ui::PaintEvent& ev)
{
ui::Graphics* g = ev.graphics();
@ -190,6 +376,10 @@ void ColorSelector::onPaint(ui::PaintEvent& ev)
if (rc.isEmpty())
return;
g->drawSurface(
painter.getCanvas(rc.w, rc.h, theme->colors.workspace()),
rc.x, rc.y);
gfx::Rect bottomBarBounds = this->bottomBarBounds();
gfx::Rect alphaBarBounds = this->alphaBarBounds();
rc.h -= bottomBarBounds.h + alphaBarBounds.h;
@ -204,12 +394,21 @@ void ColorSelector::onPaint(ui::PaintEvent& ev)
alphaBarBounds.offset(-bounds().origin());
onPaintAlphaBar(g, alphaBarBounds);
}
if ((m_paintFlags & AllAreasFlag) != 0) {
m_paintFlags &= ~DoneFlag;
m_timer.start();
gfx::Point d = -rc.origin();
rc.offset(d);
if (!bottomBarBounds.isEmpty()) bottomBarBounds.offset(d);
if (!alphaBarBounds.isEmpty()) alphaBarBounds.offset(d);
painter.startBgPainting(this, rc, bottomBarBounds, alphaBarBounds);
}
}
void ColorSelector::onPaintAlphaBar(ui::Graphics* g, const gfx::Rect& rc)
{
draw_alpha_slider(g, rc, m_color);
const double lit = m_color.getHslLightness();
const int alpha = m_color.getAlpha();
const gfx::Point pos(rc.x + int(rc.w * alpha / 255),
@ -217,6 +416,28 @@ void ColorSelector::onPaintAlphaBar(ui::Graphics* g, const gfx::Rect& rc)
paintColorIndicator(g, pos, lit < 0.5);
}
void ColorSelector::onPaintSurfaceInBgThread(
she::Surface* s,
const gfx::Rect& main,
const gfx::Rect& bottom,
const gfx::Rect& alpha,
bool& stop)
{
if ((m_paintFlags & AlphaBarFlag) && !alpha.isEmpty()) {
draw_alpha_slider(s, alpha, m_color);
if (stop)
return;
m_paintFlags ^= AlphaBarFlag;
}
}
int ColorSelector::onNeedsSurfaceRepaint(const app::Color& newColor)
{
return (m_color.getRed() != newColor.getRed() ||
m_color.getGreen() != newColor.getGreen() ||
m_color.getBlue() != newColor.getBlue() ? AlphaBarFlag: 0);
}
void ColorSelector::paintColorIndicator(ui::Graphics* g,
const gfx::Point& pos,
const bool white)

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2016-2017 David Capello
// Copyright (C) 2016-2018 David Capello
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
@ -11,15 +11,27 @@
#include "app/color.h"
#include "app/ui/color_source.h"
#include "obs/signal.h"
#include "she/surface.h"
#include "ui/mouse_buttons.h"
#include "ui/timer.h"
#include "ui/widget.h"
#include <cmath>
// TODO move this to laf::base
inline bool cs_double_diff(double a, double b) {
return std::fabs((a)-(b)) > 0.001;
}
namespace app {
class ColorSelector : public ui::Widget
, public IColorSource {
public:
class Painter;
ColorSelector();
~ColorSelector();
void selectColor(const app::Color& color);
@ -30,11 +42,33 @@ namespace app {
obs::signal<void(const app::Color&, ui::MouseButtons)> ColorChange;
protected:
// paintFlags for onPaintSurfaceInBgThread and return value of
// onNeedsSurfaceRepaint().
enum {
MainAreaFlag = 1,
BottomBarFlag = 2,
AlphaBarFlag = 4,
AllAreasFlag = MainAreaFlag | BottomBarFlag | AlphaBarFlag,
DoneFlag = 8,
};
void onSizeHint(ui::SizeHintEvent& ev) override;
bool onProcessMessage(ui::Message* msg) override;
void onInitTheme(ui::InitThemeEvent& ev) override;
void onResize(ui::ResizeEvent& ev) override;
void onPaint(ui::PaintEvent& ev) override;
virtual app::Color getMainAreaColor(const int u, const int umax,
const int v, const int vmax) = 0;
virtual app::Color getBottomBarColor(const int u, const int umax) = 0;
virtual void onPaintMainArea(ui::Graphics* g, const gfx::Rect& rc) = 0;
virtual void onPaintBottomBar(ui::Graphics* g, const gfx::Rect& rc) = 0;
virtual void onPaintSurfaceInBgThread(she::Surface* s,
const gfx::Rect& main,
const gfx::Rect& bottom,
const gfx::Rect& alpha,
bool& stop);
virtual int onNeedsSurfaceRepaint(const app::Color& newColor);
virtual bool subColorPicked() { return false; }
void paintColorIndicator(ui::Graphics* g,
@ -43,12 +77,12 @@ namespace app {
app::Color m_color;
private:
void onSizeHint(ui::SizeHintEvent& ev) override;
bool onProcessMessage(ui::Message* msg) override;
void onInitTheme(ui::InitThemeEvent& ev) override;
void onPaint(ui::PaintEvent& ev) override;
// These flags indicate which areas must be redrawed in the
// background thread. Equal to DoneFlag when the surface is
// already painted in the background thread surface.
int m_paintFlags;
private:
app::Color getAlphaBarColor(const int u, const int umax);
void onPaintAlphaBar(ui::Graphics* g, const gfx::Rect& rc);
@ -66,6 +100,8 @@ namespace app {
// widget.
bool m_capturedInBottom;
bool m_capturedInAlpha;
ui::Timer m_timer;
};
} // namespace app

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2001-2017 David Capello
// Copyright (C) 2001-2018 David Capello
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
@ -55,25 +55,6 @@ app::Color ColorSpectrum::getBottomBarColor(const int u, const int umax)
void ColorSpectrum::onPaintMainArea(ui::Graphics* g, const gfx::Rect& rc)
{
double sat = m_color.getHslSaturation();
int umax = MAX(1, rc.w-1);
int vmax = MAX(1, rc.h-1);
for (int y=0; y<rc.h; ++y) {
for (int x=0; x<rc.w; ++x) {
double hue = 360.0 * double(x) / double(umax);
double lit = 1.0 - double(y) / double(vmax);
gfx::Color color = color_utils::color_for_ui(
app::Color::fromHsl(
MID(0.0, hue, 360.0),
sat,
MID(0.0, lit, 1.0)));
g->putPixel(color, rc.x+x, rc.y+y);
}
}
if (m_color.getType() != app::Color::MaskType) {
double hue = m_color.getHslHue();
double lit = m_color.getHslLightness();
@ -86,16 +67,8 @@ void ColorSpectrum::onPaintMainArea(ui::Graphics* g, const gfx::Rect& rc)
void ColorSpectrum::onPaintBottomBar(ui::Graphics* g, const gfx::Rect& rc)
{
double hue = m_color.getHslHue();
double lit = m_color.getHslLightness();
for (int x=0; x<rc.w; ++x) {
gfx::Color color = color_utils::color_for_ui(
app::Color::fromHsl(hue, double(x) / double(rc.w), lit));
g->drawVLine(color, rc.x+x, rc.y, rc.h);
}
if (m_color.getType() != app::Color::MaskType) {
double sat = m_color.getHslSaturation();
gfx::Point pos(rc.x + int(double(rc.w) * sat),
@ -104,4 +77,63 @@ void ColorSpectrum::onPaintBottomBar(ui::Graphics* g, const gfx::Rect& rc)
}
}
void ColorSpectrum::onPaintSurfaceInBgThread(
she::Surface* s,
const gfx::Rect& main,
const gfx::Rect& bottom,
const gfx::Rect& alpha,
bool& stop)
{
if (m_paintFlags & MainAreaFlag) {
double sat = m_color.getHslSaturation();
int umax = MAX(1, main.w-1);
int vmax = MAX(1, main.h-1);
for (int y=0; y<main.h && !stop; ++y) {
for (int x=0; x<main.w && !stop; ++x) {
double hue = 360.0 * double(x) / double(umax);
double lit = 1.0 - double(y) / double(vmax);
gfx::Color color = color_utils::color_for_ui(
app::Color::fromHsl(
MID(0.0, hue, 360.0),
sat,
MID(0.0, lit, 1.0)));
s->putPixel(color, main.x+x, main.y+y);
}
}
if (stop)
return;
m_paintFlags ^= MainAreaFlag;
}
if (m_paintFlags & BottomBarFlag) {
double lit = m_color.getHslLightness();
double hue = m_color.getHslHue();
for (int x=0; x<bottom.w && !stop; ++x) {
gfx::Color color = color_utils::color_for_ui(
app::Color::fromHsl(hue, double(x) / double(bottom.w), lit));
s->drawVLine(color, bottom.x+x, bottom.y, bottom.h);
}
if (stop)
return;
m_paintFlags ^= BottomBarFlag;
}
// Paint alpha bar
ColorSelector::onPaintSurfaceInBgThread(s, main, bottom, alpha, stop);
}
int ColorSpectrum::onNeedsSurfaceRepaint(const app::Color& newColor)
{
return
// Only if the saturation changes we have to redraw the main surface.
(cs_double_diff(m_color.getHslSaturation(), newColor.getHslSaturation()) ? MainAreaFlag: 0) |
(cs_double_diff(m_color.getHslHue(), newColor.getHslHue()) ||
cs_double_diff(m_color.getHslLightness(), newColor.getHslLightness()) ? BottomBarFlag: 0) |
ColorSelector::onNeedsSurfaceRepaint(newColor);
}
} // namespace app

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2001-2017 David Capello
// Copyright (C) 2001-2018 David Capello
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
@ -22,6 +22,12 @@ namespace app {
app::Color getBottomBarColor(const int u, const int umax) override;
void onPaintMainArea(ui::Graphics* g, const gfx::Rect& rc) override;
void onPaintBottomBar(ui::Graphics* g, const gfx::Rect& rc) override;
void onPaintSurfaceInBgThread(she::Surface* s,
const gfx::Rect& main,
const gfx::Rect& bottom,
const gfx::Rect& alpha,
bool& stop) override;
int onNeedsSurfaceRepaint(const app::Color& newColor) override;
};
} // namespace app

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2016-2017 David Capello
// Copyright (C) 2016-2018 David Capello
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
@ -48,25 +48,6 @@ app::Color ColorTintShadeTone::getBottomBarColor(const int u, const int umax)
void ColorTintShadeTone::onPaintMainArea(ui::Graphics* g, const gfx::Rect& rc)
{
double hue = m_color.getHsvHue();
int umax = MAX(1, rc.w-1);
int vmax = MAX(1, rc.h-1);
for (int y=0; y<rc.h; ++y) {
for (int x=0; x<rc.w; ++x) {
double sat = double(x) / double(umax);
double val = 1.0 - double(y) / double(vmax);
gfx::Color color = color_utils::color_for_ui(
app::Color::fromHsv(
hue,
MID(0.0, sat, 1.0),
MID(0.0, val, 1.0)));
g->putPixel(color, rc.x+x, rc.y+y);
}
}
if (m_color.getType() != app::Color::MaskType) {
double sat = m_color.getHsvSaturation();
double val = m_color.getHsvValue();
@ -79,14 +60,6 @@ void ColorTintShadeTone::onPaintMainArea(ui::Graphics* g, const gfx::Rect& rc)
void ColorTintShadeTone::onPaintBottomBar(ui::Graphics* g, const gfx::Rect& rc)
{
for (int x=0; x<rc.w; ++x) {
gfx::Color color = color_utils::color_for_ui(
app::Color::fromHsv(
(360.0 * x / rc.w), 1.0, 1.0));
g->drawVLine(color, rc.x+x, rc.y, rc.h);
}
if (m_color.getType() != app::Color::MaskType) {
double hue = m_color.getHsvHue();
gfx::Point pos(rc.x + int(rc.w * hue / 360.0),
@ -95,4 +68,60 @@ void ColorTintShadeTone::onPaintBottomBar(ui::Graphics* g, const gfx::Rect& rc)
}
}
void ColorTintShadeTone::onPaintSurfaceInBgThread(
she::Surface* s,
const gfx::Rect& main,
const gfx::Rect& bottom,
const gfx::Rect& alpha,
bool& stop)
{
double hue = m_color.getHsvHue();
int umax = MAX(1, main.w-1);
int vmax = MAX(1, main.h-1);
if (m_paintFlags & MainAreaFlag) {
for (int y=0; y<main.h && !stop; ++y) {
for (int x=0; x<main.w && !stop; ++x) {
double sat = double(x) / double(umax);
double val = 1.0 - double(y) / double(vmax);
gfx::Color color = color_utils::color_for_ui(
app::Color::fromHsv(
hue,
MID(0.0, sat, 1.0),
MID(0.0, val, 1.0)));
s->putPixel(color, main.x+x, main.y+y);
}
}
if (stop)
return;
m_paintFlags ^= MainAreaFlag;
}
if (m_paintFlags & BottomBarFlag) {
for (int x=0; x<bottom.w && !stop; ++x) {
gfx::Color color = color_utils::color_for_ui(
app::Color::fromHsv(
(360.0 * x / bottom.w), 1.0, 1.0));
s->drawVLine(color, bottom.x+x, bottom.y, bottom.h);
}
if (stop)
return;
m_paintFlags ^= BottomBarFlag;
}
// Paint alpha bar
ColorSelector::onPaintSurfaceInBgThread(s, main, bottom, alpha, stop);
}
int ColorTintShadeTone::onNeedsSurfaceRepaint(const app::Color& newColor)
{
return
// Only if the hue changes we have to redraw the main surface.
(cs_double_diff(m_color.getHsvHue(), newColor.getHsvHue()) ? MainAreaFlag: 0) |
ColorSelector::onNeedsSurfaceRepaint(newColor);
}
} // namespace app

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2016-2017 David Capello
// Copyright (C) 2016-2018 David Capello
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
@ -11,6 +11,7 @@
#include "app/ui/color_selector.h"
namespace app {
class Color;
class ColorTintShadeTone : public ColorSelector {
public:
@ -22,6 +23,12 @@ namespace app {
app::Color getBottomBarColor(const int u, const int umax) override;
void onPaintMainArea(ui::Graphics* g, const gfx::Rect& rc) override;
void onPaintBottomBar(ui::Graphics* g, const gfx::Rect& rc) override;
void onPaintSurfaceInBgThread(she::Surface* s,
const gfx::Rect& main,
const gfx::Rect& bottom,
const gfx::Rect& alpha,
bool& stop) override;
int onNeedsSurfaceRepaint(const app::Color& newColor) override;
};
} // namespace app

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2001-2017 David Capello
// Copyright (C) 2001-2018 David Capello
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
@ -61,6 +61,7 @@ ColorWheel::ColorWheel()
[this]{
SkinTheme* theme = SkinTheme::instance();
m_options.setStyle(theme->styles.colorWheelOptions());
m_bgColor = theme->colors.editorFace();
});
initTheme();
}
@ -175,7 +176,6 @@ app::Color ColorWheel::getBottomBarColor(const int u, const int umax)
void ColorWheel::onPaintMainArea(ui::Graphics* g, const gfx::Rect& rc)
{
bool oldHarmonyPicked = m_harmonyPicked;
SkinTheme* theme = static_cast<SkinTheme*>(this->theme());
int r = MIN(rc.w/2, rc.h/2);
m_wheelRadius = r;
@ -183,28 +183,6 @@ void ColorWheel::onPaintMainArea(ui::Graphics* g, const gfx::Rect& rc)
rc.y+rc.h/2-r,
r*2, r*2);
int umax = MAX(1, rc.w-1);
int vmax = MAX(1, rc.h-1);
for (int y=0; y<rc.h; ++y) {
for (int x=0; x<rc.w; ++x) {
app::Color appColor =
getMainAreaColor(x, umax,
y, vmax);
gfx::Color color;
if (appColor.getType() != app::Color::MaskType) {
appColor.setAlpha(255);
color = color_utils::color_for_ui(appColor);
}
else {
color = theme->colors.editorFace();
}
g->putPixel(color, rc.x+x, rc.y+y);
}
}
if (m_color.getAlpha() > 0) {
if (m_colorModel == ColorModel::NORMAL_MAP) {
double angle = std::atan2(m_color.getGreen()-128,
@ -253,16 +231,6 @@ void ColorWheel::onPaintMainArea(ui::Graphics* g, const gfx::Rect& rc)
void ColorWheel::onPaintBottomBar(ui::Graphics* g, const gfx::Rect& rc)
{
double hue = m_color.getHsvHue();
double sat = m_color.getHsvSaturation();
for (int x=0; x<rc.w; ++x) {
gfx::Color color = color_utils::color_for_ui(
app::Color::fromHsv(hue, sat, double(x) / double(rc.w)));
g->drawVLine(color, rc.x+x, rc.y, rc.h);
}
if (m_color.getType() != app::Color::MaskType) {
double val = m_color.getHsvValue();
gfx::Point pos(rc.x + int(double(rc.w) * val),
@ -271,8 +239,74 @@ void ColorWheel::onPaintBottomBar(ui::Graphics* g, const gfx::Rect& rc)
}
}
void ColorWheel::onPaintSurfaceInBgThread(she::Surface* s,
const gfx::Rect& main,
const gfx::Rect& bottom,
const gfx::Rect& alpha,
bool& stop)
{
if (m_paintFlags & MainAreaFlag) {
int umax = MAX(1, main.w-1);
int vmax = MAX(1, main.h-1);
for (int y=0; y<main.h && !stop; ++y) {
for (int x=0; x<main.w && !stop; ++x) {
app::Color appColor =
getMainAreaColor(x, umax,
y, vmax);
gfx::Color color;
if (appColor.getType() != app::Color::MaskType) {
appColor.setAlpha(255);
color = color_utils::color_for_ui(appColor);
}
else {
color = m_bgColor;
}
s->putPixel(color, main.x+x, main.y+y);
}
}
if (stop)
return;
m_paintFlags ^= MainAreaFlag;
}
if (m_paintFlags & BottomBarFlag) {
double hue = m_color.getHsvHue();
double sat = m_color.getHsvSaturation();
for (int x=0; x<bottom.w && !stop; ++x) {
gfx::Color color = color_utils::color_for_ui(
app::Color::fromHsv(hue, sat, double(x) / double(bottom.w)));
s->drawVLine(color, bottom.x+x, bottom.y, bottom.h);
}
if (stop)
return;
m_paintFlags ^= BottomBarFlag;
}
// Paint alpha bar
ColorSelector::onPaintSurfaceInBgThread(s, main, bottom, alpha, stop);
}
int ColorWheel::onNeedsSurfaceRepaint(const app::Color& newColor)
{
return
// Only if the saturation changes we have to redraw the main surface.
(m_colorModel != ColorModel::NORMAL_MAP &&
cs_double_diff(m_color.getHsvValue(), newColor.getHsvValue()) ? MainAreaFlag: 0) |
(cs_double_diff(m_color.getHsvHue(), newColor.getHsvHue()) ||
cs_double_diff(m_color.getHsvSaturation(), newColor.getHsvSaturation()) ? BottomBarFlag: 0) |
ColorSelector::onNeedsSurfaceRepaint(newColor);
}
void ColorWheel::setDiscrete(bool state)
{
if (m_discrete != state)
m_paintFlags = AllAreasFlag;
m_discrete = state;
Preferences::instance().colorBar.discreteWheel(m_discrete);

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2001-2017 David Capello
// Copyright (C) 2001-2018 David Capello
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
@ -47,6 +47,12 @@ namespace app {
app::Color getBottomBarColor(const int u, const int umax) override;
void onPaintMainArea(ui::Graphics* g, const gfx::Rect& rc) override;
void onPaintBottomBar(ui::Graphics* g, const gfx::Rect& rc) override;
void onPaintSurfaceInBgThread(she::Surface* s,
const gfx::Rect& main,
const gfx::Rect& bottom,
const gfx::Rect& alpha,
bool& stop) override;
int onNeedsSurfaceRepaint(const app::Color& newColor) override;
bool subColorPicked() override { return m_harmonyPicked; }
private:
@ -61,6 +67,7 @@ namespace app {
int convertHueAngle(int angle, int dir) const;
gfx::Rect m_wheelBounds;
gfx::Color m_bgColor;
int m_wheelRadius;
bool m_discrete;
ColorModel m_colorModel;