diff --git a/data/widgets/options.xml b/data/widgets/options.xml
index 964888743..0d391a74c 100644
--- a/data/widgets/options.xml
+++ b/data/widgets/options.xml
@@ -63,7 +63,7 @@
text="@.show_home" />
+ tooltip="@.expand_menu_bar_items_on_mouseover_tooltip" />
capabilities()) &
- int(os::Capabilities::GpuAccelerationSwitch)) == int(os::Capabilities::GpuAccelerationSwitch)) {
+#ifdef _DEBUG // TODO enable this on Release when Aseprite supports
+ // GPU-acceleration properly
+ if (os::instance()->hasCapability(os::Capabilities::GpuAccelerationSwitch)) {
gpuAcceleration()->setSelected(m_pref.general.gpuAcceleration());
}
- else {
+ else
+#endif
+ {
gpuAcceleration()->setVisible(false);
}
diff --git a/src/app/modules/gfx.cpp b/src/app/modules/gfx.cpp
index b2c96f3a3..85216643a 100644
--- a/src/app/modules/gfx.cpp
+++ b/src/app/modules/gfx.cpp
@@ -43,22 +43,6 @@ using namespace gfx;
namespace {
-gfx::Color gridColor1()
-{
- if (ui::is_ui_thread() && current_editor)
- return color_utils::color_for_ui(current_editor->docPref().bg.color1());
- else
- return gfx::rgba(128, 128, 128);
-}
-
-gfx::Color gridColor2()
-{
- if (ui::is_ui_thread() && current_editor)
- return color_utils::color_for_ui(current_editor->docPref().bg.color2());
- else
- return gfx::rgba(192, 192, 192);
-}
-
void draw_checked_grid(ui::Graphics* g,
const gfx::Rect& rc,
const gfx::Size& tile,
@@ -93,11 +77,27 @@ void draw_checked_grid(ui::Graphics* g,
} // anonymous namespace
+gfx::Color grid_color1()
+{
+ if (ui::is_ui_thread() && current_editor)
+ return color_utils::color_for_ui(current_editor->docPref().bg.color1());
+ else
+ return gfx::rgba(128, 128, 128);
+}
+
+gfx::Color grid_color2()
+{
+ if (ui::is_ui_thread() && current_editor)
+ return color_utils::color_for_ui(current_editor->docPref().bg.color2());
+ else
+ return gfx::rgba(192, 192, 192);
+}
+
void draw_checked_grid(ui::Graphics* g,
const gfx::Rect& rc,
const gfx::Size& tile)
{
- draw_checked_grid(g, rc, tile, gridColor1(), gridColor2());
+ draw_checked_grid(g, rc, tile, grid_color1(), grid_color2());
}
void draw_checked_grid(ui::Graphics* g,
@@ -105,7 +105,7 @@ void draw_checked_grid(ui::Graphics* g,
const gfx::Size& tile,
DocumentPreferences& docPref)
{
- draw_checked_grid(g, rc, tile, gridColor1(), gridColor2());
+ draw_checked_grid(g, rc, tile, grid_color1(), grid_color2());
}
void draw_color(ui::Graphics* g,
@@ -228,7 +228,18 @@ void draw_tile(ui::Graphics* g,
os::SurfaceRef surface = os::instance()->makeRgbaSurface(w, h);
convert_image_to_surface(tileImage.get(), get_current_palette(),
surface.get(), 0, 0, 0, 0, w, h);
- g->drawRgbaSurface(surface.get(), gfx::Rect(0, 0, w, h), rc);
+
+ ui::Paint paint;
+ paint.blendMode(os::BlendMode::SrcOver);
+
+ os::Sampling sampling;
+ if (w > rc.w && h > rc.h) {
+ sampling = os::Sampling(os::Sampling::Filter::Linear,
+ os::Sampling::Mipmap::Nearest);
+ }
+
+ g->drawSurface(surface.get(), gfx::Rect(0, 0, w, h), rc,
+ os::Sampling(), &paint);
}
void draw_tile_button(ui::Graphics* g,
@@ -286,8 +297,8 @@ void draw_alpha_slider(ui::Graphics* g,
for (int x=0; xdrawVLine(
@@ -314,8 +325,8 @@ void draw_alpha_slider(os::Surface* s,
os::Paint paint;
for (int x=0; xcapabilities()) &
- int(os::Capabilities::GpuAccelerationSwitch)) == int(os::Capabilities::GpuAccelerationSwitch)) {
+ os::instance()->hasCapability(os::Capabilities::GpuAccelerationSwitch)) {
if (create_main_window(false, maximized, lastError)) {
// Disable hardware acceleration
pref.general.gpuAcceleration(false);
diff --git a/src/app/ui/color_selector.cpp b/src/app/ui/color_selector.cpp
index f25053d1f..ff277093c 100644
--- a/src/app/ui/color_selector.cpp
+++ b/src/app/ui/color_selector.cpp
@@ -19,6 +19,7 @@
#include "app/modules/gfx.h"
#include "app/ui/skin/skin_theme.h"
#include "app/ui/status_bar.h"
+#include "app/util/shader_helpers.h"
#include "base/clamp.h"
#include "base/concurrent_queue.h"
#include "base/scoped_value.h"
@@ -36,8 +37,16 @@
#include
#include
#include
+#include
#include
+#if SK_ENABLE_SKSL
+ #include "os/skia/skia_surface.h"
+
+ #include "include/core/SkCanvas.h"
+ #include "include/effects/SkRuntimeEffect.h"
+#endif
+
namespace app {
using namespace app::skin;
@@ -117,8 +126,14 @@ public:
paint.color(bgColor);
paint.style(os::Paint::Fill);
m_canvas->drawRect(gfx::Rect(0, 0, w, h), paint);
- if (oldCanvas)
- m_canvas->drawSurface(oldCanvas.get(), 0, 0);
+ if (oldCanvas) {
+ m_canvas->drawSurface(
+ oldCanvas.get(),
+ gfx::Rect(0, 0, oldCanvas->width(), oldCanvas->height()),
+ gfx::Rect(0, 0, w, h),
+ os::Sampling(),
+ nullptr);
+ }
}
return m_canvas.get();
}
@@ -218,6 +233,11 @@ private:
static ColorSelector::Painter painter;
+#if SK_ENABLE_SKSL
+// static
+sk_sp ColorSelector::m_alphaEffect;
+#endif
+
ColorSelector::ColorSelector()
: Widget(kGenericWidget)
, m_paintFlags(AllAreasFlag)
@@ -415,12 +435,88 @@ 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();
+
+ os::Surface* painterSurface = nullptr;
+
+#if SK_ENABLE_SKSL // Paint with shaders
+ buildEffects();
+ if (m_mainEffect && m_bottomEffect && m_alphaEffect) {
+ SkCanvas* canvas;
+ bool isSRGB;
+ // TODO compare both color spaces
+ if (get_current_color_space()->isSRGB() &&
+ g->getInternalSurface()->colorSpace()->isSRGB()) {
+ // We can render directly in the ui::Graphics surface
+ canvas = &static_cast(g->getInternalSurface())->canvas();
+ isSRGB = true;
+ }
+ else {
+ // We'll paint in the ColorSelector::Painter canvas, and so we
+ // can convert color spaces.
+ painterSurface = painter.getCanvas(rc.w, rc.h, theme->colors.workspace());
+ canvas = &static_cast(painterSurface)->canvas();
+ isSRGB = false;
+ }
+
+ canvas->save();
+ {
+ SkPaint p;
+ p.setStyle(SkPaint::kFill_Style);
+
+ // Main area
+ gfx::Rect rc2(0, 0, rc.w, std::max(1, rc.h-bottomBarBounds.h-alphaBarBounds.h));
+
+ SkRuntimeShaderBuilder builder1(m_mainEffect);
+ builder1.uniform("iRes") = SkV3{float(rc2.w), float(rc2.h), 0.0f};
+ builder1.uniform("iColor") = appColor_to_SkV4(m_color);
+ p.setShader(builder1.makeShader());
+
+ if (isSRGB)
+ canvas->translate(rc.x+g->getInternalDeltaX(),
+ rc.y+g->getInternalDeltaY());
+
+ canvas->drawRect(SkRect::MakeXYWH(0, 0, rc2.w, rc2.h), p);
+
+ // Bottom bar
+ canvas->translate(0.0, rc2.h);
+ rc2.h = bottomBarBounds.h;
+
+ SkRuntimeShaderBuilder builder2(m_bottomEffect);
+ builder2.uniform("iRes") = SkV3{float(rc2.w), float(rc2.h), 0.0f};
+ builder2.uniform("iColor") = appColor_to_SkV4(m_color);
+ p.setShader(builder2.makeShader());
+
+ canvas->drawRect(SkRect::MakeXYWH(0, 0, rc2.w, rc2.h), p);
+
+ // Alpha bar
+ canvas->translate(0.0, rc2.h);
+ rc2.h = alphaBarBounds.h;
+
+ SkRuntimeShaderBuilder builder3(m_alphaEffect);
+ builder3.uniform("iRes") = SkV3{float(rc2.w), float(rc2.h), 0.0f};
+ builder3.uniform("iColor") = appColor_to_SkV4(m_color);
+ builder3.uniform("iBg1") = gfxColor_to_SkV4(grid_color1());
+ builder3.uniform("iBg2") = gfxColor_to_SkV4(grid_color2());
+ p.setShader(builder3.makeShader());
+
+ canvas->drawRect(SkRect::MakeXYWH(0, 0, rc2.w, rc2.h), p);
+ }
+ canvas->restore();
+
+ // We already painted all areas
+ m_paintFlags = 0;
+ }
+ else
+#endif // SK_ENABLE_SKSL
+ {
+ painterSurface = painter.getCanvas(rc.w, rc.h, theme->colors.workspace());
+ }
+
+ if (painterSurface)
+ g->drawSurface(painterSurface, rc.x, rc.y);
+
rc.h -= bottomBarBounds.h + alphaBarBounds.h;
onPaintMainArea(g, rc);
@@ -531,4 +627,56 @@ void ColorSelector::updateColorSpace()
invalidate();
}
+#if SK_ENABLE_SKSL
+// static
+const char* ColorSelector::getAlphaBarShader()
+{
+ return R"(
+uniform half3 iRes;
+uniform half4 iColor;
+uniform half4 iBg1;
+uniform half4 iBg2;
+
+half4 main(vec2 fragcoord) {
+ vec2 d = (fragcoord.xy / iRes.xy);
+ half4 p = (mod((fragcoord.x / iRes.y) + floor(d.y+0.5), 2.0) > 1.0) ? iBg2: iBg1;
+ half4 q = iColor.rgb1;
+ float a = d.x;
+ return (1.0-a)*p + a*q;
+}
+)";
+}
+
+void ColorSelector::buildEffects()
+{
+ if (!m_mainEffect) {
+ if (const char* code = getMainAreaShader())
+ m_mainEffect = buildEffect(code);
+ }
+
+ if (!m_bottomEffect) {
+ if (const char* code = getBottomBarShader())
+ m_bottomEffect = buildEffect(code);
+ }
+
+ if (!m_alphaEffect) {
+ if (const char* code = getAlphaBarShader())
+ m_alphaEffect = buildEffect(code);
+ }
+}
+
+sk_sp ColorSelector::buildEffect(const char* code)
+{
+ auto result = SkRuntimeEffect::MakeForShader(SkString(code));
+ if (!result.errorText.isEmpty()) {
+ LOG(ERROR, "Shader error: %s\n", result.errorText.c_str());
+ std::printf("Shader error: %s\n", result.errorText.c_str());
+ return nullptr;
+ }
+ else {
+ return result.effect;
+ }
+}
+#endif
+
} // namespace app
diff --git a/src/app/ui/color_selector.h b/src/app/ui/color_selector.h
index 8d2d0fcf4..6317cceaf 100644
--- a/src/app/ui/color_selector.h
+++ b/src/app/ui/color_selector.h
@@ -1,5 +1,5 @@
// Aseprite
-// Copyright (C) 2018-2021 Igara Studio S.A.
+// Copyright (C) 2018-2022 Igara Studio S.A.
// Copyright (C) 2016-2018 David Capello
//
// This program is distributed under the terms of
@@ -21,6 +21,13 @@
#include
#include
+// TODO We should wrap the SkRuntimeEffect in laf-os, SkRuntimeEffect
+// and SkRuntimeShaderBuilder might change in future Skia
+// versions.
+#if SK_ENABLE_SKSL
+ #include "include/effects/SkRuntimeEffect.h"
+#endif
+
// TODO move this to laf::base
inline bool cs_double_diff(double a, double b) {
return std::fabs((a)-(b)) > 0.001;
@@ -61,6 +68,8 @@ namespace app {
void onResize(ui::ResizeEvent& ev) override;
void onPaint(ui::PaintEvent& ev) override;
+ virtual const char* getMainAreaShader() { return nullptr; }
+ virtual const char* getBottomBarShader() { return nullptr; }
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;
@@ -101,6 +110,12 @@ namespace app {
void updateColorSpace();
+#if SK_ENABLE_SKSL
+ static const char* getAlphaBarShader();
+ void buildEffects();
+ sk_sp buildEffect(const char* code);
+#endif
+
// Internal flag used to lock the modification of m_color.
// E.g. When the user picks a color harmony, we don't want to
// change the main color.
@@ -117,6 +132,13 @@ namespace app {
ui::Timer m_timer;
obs::scoped_connection m_appConn;
+
+#if SK_ENABLE_SKSL
+ // Shaders
+ sk_sp m_mainEffect;
+ sk_sp m_bottomEffect;
+ static sk_sp m_alphaEffect;
+#endif
};
} // namespace app
diff --git a/src/app/ui/color_spectrum.cpp b/src/app/ui/color_spectrum.cpp
index 79fe0a615..8d8619012 100644
--- a/src/app/ui/color_spectrum.cpp
+++ b/src/app/ui/color_spectrum.cpp
@@ -1,5 +1,5 @@
// Aseprite
-// Copyright (C) 2020 Igara Studio S.A.
+// Copyright (C) 2020-2022 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello
//
// This program is distributed under the terms of
@@ -14,6 +14,7 @@
#include "app/color_utils.h"
#include "app/ui/skin/skin_theme.h"
#include "app/ui/status_bar.h"
+#include "app/util/shader_helpers.h"
#include "base/clamp.h"
#include "os/surface.h"
#include "ui/graphics.h"
@@ -35,6 +36,52 @@ ColorSpectrum::ColorSpectrum()
{
}
+const char* ColorSpectrum::getMainAreaShader()
+{
+#if SK_ENABLE_SKSL
+ if (m_mainShader.empty()) {
+ m_mainShader += "uniform half3 iRes;"
+ "uniform half4 iColor;";
+ m_mainShader += kRGB_to_HSL_sksl;
+ m_mainShader += kHSL_to_RGB_sksl;
+ m_mainShader += R"(
+half4 main(vec2 fragcoord) {
+ vec2 d = fragcoord.xy / iRes.xy;
+ half hue = d.x;
+ half sat = rgb_to_hsl(iColor.rgb).y;
+ half lit = 1.0 - d.y;
+ return hsl_to_rgb(half3(hue, sat, lit)).rgb1;
+}
+)";
+ }
+ return m_mainShader.c_str();
+#else
+ return nullptr;
+#endif
+}
+
+const char* ColorSpectrum::getBottomBarShader()
+{
+#if SK_ENABLE_SKSL
+ if (m_bottomShader.empty()) {
+ m_bottomShader += "uniform half3 iRes;"
+ "uniform half4 iColor;";
+ m_bottomShader += kRGB_to_HSL_sksl;
+ m_bottomShader += kHSL_to_RGB_sksl;
+ m_bottomShader += R"(
+half4 main(vec2 fragcoord) {
+ half s = (fragcoord.x / iRes.x);
+ half3 hsl = rgb_to_hsl(iColor.rgb);
+ return hsl_to_rgb(half3(hsl.x, s, hsl.z)).rgb1;
+}
+)";
+ }
+ return m_bottomShader.c_str();
+#else
+ return nullptr;
+#endif
+}
+
app::Color ColorSpectrum::getMainAreaColor(const int u, const int umax,
const int v, const int vmax)
{
diff --git a/src/app/ui/color_spectrum.h b/src/app/ui/color_spectrum.h
index 254201ded..6d419660e 100644
--- a/src/app/ui/color_spectrum.h
+++ b/src/app/ui/color_spectrum.h
@@ -1,4 +1,5 @@
// Aseprite
+// Copyright (C) 2022 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello
//
// This program is distributed under the terms of
@@ -17,6 +18,8 @@ namespace app {
ColorSpectrum();
protected:
+ const char* getMainAreaShader() override;
+ const char* getBottomBarShader() override;
app::Color getMainAreaColor(const int u, const int umax,
const int v, const int vmax) override;
app::Color getBottomBarColor(const int u, const int umax) override;
@@ -28,6 +31,10 @@ namespace app {
const gfx::Rect& alpha,
bool& stop) override;
int onNeedsSurfaceRepaint(const app::Color& newColor) override;
+
+ private:
+ std::string m_mainShader;
+ std::string m_bottomShader;
};
} // namespace app
diff --git a/src/app/ui/color_tint_shade_tone.cpp b/src/app/ui/color_tint_shade_tone.cpp
index c700fe10a..84f9305a7 100644
--- a/src/app/ui/color_tint_shade_tone.cpp
+++ b/src/app/ui/color_tint_shade_tone.cpp
@@ -1,5 +1,5 @@
// Aseprite
-// Copyright (C) 2020 Igara Studio S.A.
+// Copyright (C) 2020-2022 Igara Studio S.A.
// Copyright (C) 2016-2018 David Capello
//
// This program is distributed under the terms of
@@ -13,6 +13,7 @@
#include "app/color_utils.h"
#include "app/ui/skin/skin_theme.h"
+#include "app/util/shader_helpers.h"
#include "base/clamp.h"
#include "ui/graphics.h"
@@ -28,6 +29,54 @@ ColorTintShadeTone::ColorTintShadeTone()
{
}
+const char* ColorTintShadeTone::getMainAreaShader()
+{
+#if SK_ENABLE_SKSL
+ if (m_mainShader.empty()) {
+ m_mainShader += "uniform half3 iRes;"
+ "uniform half4 iColor;";
+ m_mainShader += kRGB_to_HSV_sksl;
+ m_mainShader += kHSV_to_RGB_sksl;
+ m_mainShader += R"(
+half4 main(vec2 fragcoord) {
+ vec2 d = fragcoord.xy / iRes.xy;
+ half hue = rgb_to_hsv(iColor.rgb).x;
+ half sat = d.x;
+ half val = 1.0 - d.y;
+ return hsv_to_rgb(vec3(hue, sat, val)).rgb1;
+}
+)";
+ }
+ return m_mainShader.c_str();
+#else
+ return nullptr;
+#endif
+}
+
+const char* ColorTintShadeTone::getBottomBarShader()
+{
+#if SK_ENABLE_SKSL
+ if (m_bottomShader.empty()) {
+ m_bottomShader += "uniform half3 iRes;"
+ "uniform half4 iColor;";
+ m_bottomShader += kRGB_to_HSV_sksl;
+ m_bottomShader += kHSV_to_RGB_sksl;
+ // TODO should we display the hue bar with the current sat/value?
+ m_bottomShader += R"(
+half4 main(vec2 fragcoord) {
+ half h = (fragcoord.x / iRes.x);
+ // half3 hsv = rgb_to_hsv(iColor.rgb);
+ // return hsv_to_rgb(half3(h, hsv.y, hsv.z)).rgb1;
+ return hsv_to_rgb(half3(h, 1.0, 1.0)).rgb1;
+}
+)";
+ }
+ return m_bottomShader.c_str();
+#else
+ return nullptr;
+#endif
+}
+
app::Color ColorTintShadeTone::getMainAreaColor(const int u, const int umax,
const int v, const int vmax)
{
diff --git a/src/app/ui/color_tint_shade_tone.h b/src/app/ui/color_tint_shade_tone.h
index 68686d672..704609b48 100644
--- a/src/app/ui/color_tint_shade_tone.h
+++ b/src/app/ui/color_tint_shade_tone.h
@@ -1,4 +1,5 @@
// Aseprite
+// Copyright (C) 2022 Igara Studio S.A.
// Copyright (C) 2016-2018 David Capello
//
// This program is distributed under the terms of
@@ -18,6 +19,8 @@ namespace app {
ColorTintShadeTone();
protected:
+ const char* getMainAreaShader() override;
+ const char* getBottomBarShader() override;
app::Color getMainAreaColor(const int u, const int umax,
const int v, const int vmax) override;
app::Color getBottomBarColor(const int u, const int umax) override;
@@ -29,6 +32,10 @@ namespace app {
const gfx::Rect& alpha,
bool& stop) override;
int onNeedsSurfaceRepaint(const app::Color& newColor) override;
+
+ private:
+ std::string m_mainShader;
+ std::string m_bottomShader;
};
} // namespace app
diff --git a/src/app/ui/dithering_selector.cpp b/src/app/ui/dithering_selector.cpp
index 11656ff58..21de48680 100644
--- a/src/app/ui/dithering_selector.cpp
+++ b/src/app/ui/dithering_selector.cpp
@@ -149,14 +149,20 @@ private:
g->drawText(text(), fg, bg,
gfx::Point(rc.x+2*guiscale(),
rc.y+2*guiscale()));
- g->drawRgbaSurface(
+
+ ui::Paint paint;
+ paint.blendMode(os::BlendMode::SrcOver);
+
+ g->drawSurface(
preview(),
preview()->bounds(),
gfx::Rect(
rc.x+2*guiscale(),
rc.y+4*guiscale()+textsz.h,
preview()->width()*guiscale(),
- preview()->height()*guiscale()));
+ preview()->height()*guiscale()),
+ os::Sampling(),
+ &paint);
}
bool m_matrixOnly;
diff --git a/src/app/ui/editor/editor.cpp b/src/app/ui/editor/editor.cpp
index aacc63c75..d506ce9bb 100644
--- a/src/app/ui/editor/editor.cpp
+++ b/src/app/ui/editor/editor.cpp
@@ -793,7 +793,8 @@ void Editor::drawOneSpriteUnclippedRect(ui::Graphics* g, const gfx::Rect& sprite
g->drawSurface(tmp.get(),
gfx::Rect(0, 0, rc2.w, rc2.h),
dest,
- sampling);
+ sampling,
+ nullptr);
}
else {
g->blit(tmp.get(), 0, 0, dest.x, dest.y, dest.w, dest.h);
diff --git a/src/app/ui/file_list.cpp b/src/app/ui/file_list.cpp
index 162a3594c..ec2bdac66 100644
--- a/src/app/ui/file_list.cpp
+++ b/src/app/ui/file_list.cpp
@@ -465,9 +465,22 @@ void FileList::onPaint(ui::PaintEvent& ev)
tbounds.shrink(1);
os::SurfaceRef thumbnail = m_selected->getThumbnail();
- g->drawRgbaSurface(thumbnail.get(),
- gfx::Rect(0, 0, thumbnail->width(), thumbnail->height()),
- tbounds);
+
+ ui::Paint paint;
+ paint.blendMode(os::BlendMode::SrcOver);
+
+ os::Sampling sampling;
+ if (thumbnail->width() > tbounds.w &&
+ thumbnail->height() > tbounds.h) {
+ sampling = os::Sampling(os::Sampling::Filter::Linear,
+ os::Sampling::Mipmap::Nearest);
+ }
+
+ g->drawSurface(thumbnail.get(),
+ gfx::Rect(0, 0, thumbnail->width(), thumbnail->height()),
+ tbounds,
+ sampling,
+ &paint);
}
}
@@ -561,9 +574,21 @@ void FileList::paintItem(ui::Graphics* g, IFileItem* fi, const int i)
tbounds.shrink(1);
}
- g->drawRgbaSurface(thumbnail.get(),
- gfx::Rect(0, 0, thumbnail->width(), thumbnail->height()),
- tbounds);
+ ui::Paint paint;
+ paint.blendMode(os::BlendMode::SrcOver);
+
+ os::Sampling sampling;
+ if (thumbnail->width() > tbounds.w &&
+ thumbnail->height() > tbounds.h) {
+ sampling = os::Sampling(os::Sampling::Filter::Linear,
+ os::Sampling::Mipmap::Nearest);
+ }
+
+ g->drawSurface(thumbnail.get(),
+ gfx::Rect(0, 0, thumbnail->width(), thumbnail->height()),
+ tbounds,
+ sampling,
+ &paint);
}
else {
tbounds = gfx::Rect(0, 0, 20*guiscale(), 2+4*(8.0-m_zoom)/8.0*guiscale())
diff --git a/src/app/ui/palette_view.cpp b/src/app/ui/palette_view.cpp
index 0a4f54eb8..e19443e20 100644
--- a/src/app/ui/palette_view.cpp
+++ b/src/app/ui/palette_view.cpp
@@ -316,7 +316,18 @@ public:
os::SurfaceRef surface = os::instance()->makeRgbaSurface(w, h);
convert_image_to_surface(tileImage.get(), get_current_palette(),
surface.get(), 0, 0, 0, 0, w, h);
- g->drawRgbaSurface(surface.get(), gfx::Rect(0, 0, w, h), box);
+
+ ui::Paint paint;
+ paint.blendMode(os::BlendMode::SrcOver);
+
+ os::Sampling sampling;
+ if (w > box.w && h > box.h) {
+ sampling = os::Sampling(os::Sampling::Filter::Linear,
+ os::Sampling::Mipmap::Nearest);
+ }
+
+ g->drawSurface(surface.get(), gfx::Rect(0, 0, w, h), box,
+ sampling, &paint);
}
negColor = gfx::rgba(255, 255, 255);
}
@@ -626,6 +637,12 @@ bool PaletteView::onProcessMessage(Message* msg)
switch (m_hot.part) {
case Hit::COLOR:
+ // Clicking outside the palette range will deselect
+ if (m_hot.color >= currentPalette()->size()) {
+ deselect();
+ break;
+ }
+
m_state = State::SELECTING_COLOR;
// As we can ctrl+click color bar + timeline, now we have to
diff --git a/src/app/util/shader_helpers.h b/src/app/util/shader_helpers.h
new file mode 100644
index 000000000..eeedb4f34
--- /dev/null
+++ b/src/app/util/shader_helpers.h
@@ -0,0 +1,67 @@
+// Aseprite
+// Copyright (C) 2022 Igara Studio S.A.
+//
+// This program is distributed under the terms of
+// the End-User License Agreement for Aseprite.
+
+#ifndef APP_UTIL_SHADER_HELPERS_H_INCLUDED
+#define APP_UTIL_SHADER_HELPERS_H_INCLUDED
+#pragma once
+
+#if SK_ENABLE_SKSL
+
+#include "app/color.h"
+#include "gfx/color.h"
+
+#include "include/core/SkM44.h"
+
+// To include kRGB_to_HSL_sksl and kHSL_to_RGB_sksl
+#include "src/core/SkRuntimeEffectPriv.h"
+
+namespace app {
+
+// rgb_to_hsl() and hsv_to_hsl() functions by Sam Hocevar licensed
+// under WTFPL (https://en.wikipedia.org/wiki/WTFPL)
+// Source:
+// http://lolengine.net/blog/2013/07/27/rgb-to-hsv-in-glsl
+// https://stackoverflow.com/a/17897228/408239
+
+inline constexpr char kRGB_to_HSV_sksl[] = R"(
+half3 rgb_to_hsv(half3 c) {
+ half4 K = half4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0);
+ half4 p = mix(half4(c.bg, K.wz), half4(c.gb, K.xy), step(c.b, c.g));
+ half4 q = mix(half4(p.xyw, c.r), half4(c.r, p.yzx), step(p.x, c.r));
+
+ float d = q.x - min(q.w, q.y);
+ float e = 1.0e-10;
+ return half3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x);
+}
+)";
+
+inline constexpr char kHSV_to_RGB_sksl[] = R"(
+half3 hsv_to_rgb(half3 c) {
+ half4 K = half4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);
+ half3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www);
+ return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);
+}
+)";
+
+inline SkV4 gfxColor_to_SkV4(gfx::Color color) {
+ return SkV4{float(gfx::getr(color) / 255.0),
+ float(gfx::getg(color) / 255.0),
+ float(gfx::getb(color) / 255.0),
+ float(gfx::geta(color) / 255.0)};
+}
+
+inline SkV4 appColor_to_SkV4(const app::Color& color) {
+ return SkV4{float(color.getRed() / 255.0),
+ float(color.getGreen() / 255.0),
+ float(color.getBlue() / 255.0),
+ float(color.getAlpha() / 255.0)};
+}
+
+} // namespace app
+
+#endif
+
+#endif
diff --git a/src/doc/image_iterator.h b/src/doc/image_iterator.h
index 37b8b782f..010696e5a 100644
--- a/src/doc/image_iterator.h
+++ b/src/doc/image_iterator.h
@@ -1,5 +1,5 @@
// Aseprite Document Library
-// Copyright (c) 2019 Igara Studio S.A.
+// Copyright (c) 2019-2022 Igara Studio S.A.
// Copyright (c) 2001-2015 David Capello
//
// This file is released under the terms of the MIT license.
@@ -15,7 +15,6 @@
#include "gfx/rect.h"
#include
-#include
#include
@@ -26,18 +25,15 @@ namespace doc {
template
- class ImageIteratorT : public std::iterator {
+ class ImageIteratorT {
public:
- // GCC 4.6 needs these re-definitions here.
- typedef ptrdiff_t difference_type;
- typedef PointerType pointer;
- typedef ReferenceType reference;
+ using iterator_category = std::forward_iterator_tag;
+ using value_type = typename ImageTraits::pixel_t;
+ using difference_type = std::ptrdiff_t;
+ using pointer = PointerType;
+ using reference = ReferenceType;
- ImageIteratorT() : m_ptr(NULL) {
+ ImageIteratorT() : m_ptr(nullptr) {
}
ImageIteratorT(const ImageIteratorT& other) :
@@ -142,17 +138,13 @@ namespace doc {
typename ImageTraits::pixel_t *,
typename ImageTraits::pixel_t&> {
public:
- // GCC 4.6 needs these re-definitions here.
- typedef typename ImageTraits::pixel_t* pointer;
- typedef typename ImageTraits::pixel_t& reference;
-
ImageIterator() {
}
ImageIterator(const Image* image, const gfx::Rect& bounds, int x, int y) :
ImageIteratorT(image, bounds, x, y) {
+ typename ImageIterator::pointer,
+ typename ImageIterator::reference>(image, bounds, x, y) {
}
};
@@ -161,17 +153,13 @@ namespace doc {
typename ImageTraits::pixel_t const *,
typename ImageTraits::pixel_t const &> {
public:
- // GCC 4.6 needs these re-definitions here.
- typedef typename ImageTraits::pixel_t const* pointer;
- typedef typename ImageTraits::pixel_t const& reference;
-
ImageConstIterator() {
}
ImageConstIterator(const Image* image, const gfx::Rect& bounds, int x, int y) :
ImageIteratorT(image, bounds, x, y) {
+ typename ImageConstIterator::pointer,
+ typename ImageConstIterator::reference>(image, bounds, x, y) {
}
};
@@ -181,7 +169,7 @@ namespace doc {
class BitPixelAccess {
public:
BitPixelAccess() :
- m_ptr(NULL),
+ m_ptr(nullptr),
m_bit(0) {
}
@@ -263,20 +251,17 @@ namespace doc {
template
- class ImageIteratorT
- : public std::iterator {
+ class ImageIteratorT {
public:
- // GCC 4.6 needs these re-definitions here.
- typedef ptrdiff_t difference_type;
- typedef PointerType pointer;
- typedef ReferenceType reference;
+ using iterator_category = std::forward_iterator_tag;
+ using value_type = BitmapTraits::pixel_t;
+ using difference_type = std::ptrdiff_t;
+ using pointer = PointerType;
+ using reference = ReferenceType;
+
enum { pixels_per_byte = BitmapTraits::pixels_per_byte };
- ImageIteratorT() : m_ptr(NULL) {
+ ImageIteratorT() : m_ptr(nullptr) {
}
ImageIteratorT(const ImageIteratorT& other) :
diff --git a/src/doc/selected_frames.h b/src/doc/selected_frames.h
index ac1042937..01155705e 100644
--- a/src/doc/selected_frames.h
+++ b/src/doc/selected_frames.h
@@ -1,4 +1,5 @@
// Aseprite Document Library
+// Copyright (c) 2022 Igara Studio S.A.
// Copyright (c) 2016-2018 David Capello
//
// This file is released under the terms of the MIT license.
@@ -11,7 +12,6 @@
#include "doc/frame_range.h"
#include
-#include
#include
namespace doc {
@@ -20,9 +20,15 @@ namespace doc {
typedef std::vector Ranges;
public:
- class const_iterator : public std::iterator {
+ class const_iterator {
static const int kNullFrame = -2;
public:
+ using iterator_category = std::forward_iterator_tag;
+ using value_type = frame_t;
+ using difference_type = std::ptrdiff_t;
+ using pointer = frame_t*;
+ using reference = frame_t&;
+
const_iterator(const Ranges::const_iterator& it)
: m_it(it), m_frame(kNullFrame) {
}
@@ -70,8 +76,14 @@ namespace doc {
mutable frame_t m_frame;
};
- class const_reverse_iterator : public std::iterator {
+ class const_reverse_iterator {
public:
+ using iterator_category = std::forward_iterator_tag;
+ using value_type = frame_t;
+ using difference_type = std::ptrdiff_t;
+ using pointer = frame_t*;
+ using reference = frame_t&;
+
const_reverse_iterator(const Ranges::const_reverse_iterator& it)
: m_it(it), m_frame(-1) {
}
diff --git a/src/ui/graphics.cpp b/src/ui/graphics.cpp
index cfcccd5ba..9d6525d39 100644
--- a/src/ui/graphics.cpp
+++ b/src/ui/graphics.cpp
@@ -257,21 +257,6 @@ void Graphics::drawSurface(os::Surface* surface, int x, int y)
m_surface->drawSurface(surface, m_dx+x, m_dy+y);
}
-void Graphics::drawSurface(os::Surface* surface,
- const gfx::Rect& srcRect,
- const gfx::Rect& dstRect)
-{
- dirty(gfx::Rect(m_dx+dstRect.x, m_dy+dstRect.y,
- dstRect.w, dstRect.h));
-
- os::SurfaceLock lockSrc(surface);
- os::SurfaceLock lockDst(m_surface.get());
- m_surface->drawSurface(
- surface,
- srcRect,
- gfx::Rect(dstRect).offset(m_dx, m_dy));
-}
-
void Graphics::drawSurface(os::Surface* surface,
const gfx::Rect& srcRect,
const gfx::Rect& dstRect,
@@ -309,21 +294,6 @@ void Graphics::drawRgbaSurface(os::Surface* surface, int srcx, int srcy, int dst
m_surface->drawRgbaSurface(surface, srcx, srcy, m_dx+dstx, m_dy+dsty, w, h);
}
-void Graphics::drawRgbaSurface(os::Surface* surface,
- const gfx::Rect& srcRect,
- const gfx::Rect& dstRect)
-{
- dirty(gfx::Rect(m_dx+dstRect.x, m_dy+dstRect.y,
- dstRect.w, dstRect.h));
-
- os::SurfaceLock lockSrc(surface);
- os::SurfaceLock lockDst(m_surface.get());
- m_surface->drawRgbaSurface(
- surface,
- srcRect,
- gfx::Rect(dstRect).offset(m_dx, m_dy));
-}
-
void Graphics::drawColoredRgbaSurface(os::Surface* surface, gfx::Color color, int x, int y)
{
dirty(gfx::Rect(m_dx+x, m_dy+y, surface->width(), surface->height()));
diff --git a/src/ui/graphics.h b/src/ui/graphics.h
index 69b5fa217..5e2be7fba 100644
--- a/src/ui/graphics.h
+++ b/src/ui/graphics.h
@@ -90,19 +90,13 @@ namespace ui {
const gfx::Rect& outer, const gfx::Rect& inner);
void drawSurface(os::Surface* surface, int x, int y);
- void drawSurface(os::Surface* surface,
- const gfx::Rect& srcRect,
- const gfx::Rect& dstRect);
void drawSurface(os::Surface* surface,
const gfx::Rect& srcRect,
const gfx::Rect& dstRect,
const os::Sampling& sampling,
- const ui::Paint* paint = nullptr);
+ const ui::Paint* paint);
void drawRgbaSurface(os::Surface* surface, int x, int y);
void drawRgbaSurface(os::Surface* surface, int srcx, int srcy, int dstx, int dsty, int w, int h);
- void drawRgbaSurface(os::Surface* surface,
- const gfx::Rect& srcRect,
- const gfx::Rect& dstRect);
void drawColoredRgbaSurface(os::Surface* surface, gfx::Color color, int x, int y);
void drawColoredRgbaSurface(os::Surface* surface, gfx::Color color, int srcx, int srcy, int dstx, int dsty, int w, int h);
void drawSurfaceNine(os::Surface* surface,
diff --git a/src/ui/manager.cpp b/src/ui/manager.cpp
index ddcb91fe0..aea4d0641 100644
--- a/src/ui/manager.cpp
+++ b/src/ui/manager.cpp
@@ -309,6 +309,8 @@ void Manager::flipAllDisplays()
window->display()->flipDisplay();
}
}
+
+ m_display.nativeWindow()->swapBuffers();
}
void Manager::updateAllDisplaysWithNewScale(int scale)