From 32999a71bc5dcec8790eb0dce0626a9cb0f1b950 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20R=C3=BCdiger?= Date: Sun, 8 Aug 2021 12:46:55 +0200 Subject: [PATCH 1/8] Fix color switching on empty palette space click --- src/app/ui/palette_view.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/app/ui/palette_view.cpp b/src/app/ui/palette_view.cpp index 1c3f6946f..a049dcf3e 100644 --- a/src/app/ui/palette_view.cpp +++ b/src/app/ui/palette_view.cpp @@ -333,6 +333,11 @@ bool PaletteView::onProcessMessage(Message* msg) break; } + if(m_hot.color >= currentPalette()->size()){ + deselect(); + break; + } + captureMouse(); // Continue... From 0a3644dfbb0edb46a81dc45e39c8ae395d9c86f0 Mon Sep 17 00:00:00 2001 From: David Capello Date: Fri, 13 May 2022 11:41:44 -0300 Subject: [PATCH 2/8] Update laf module with initial GPU support (#960) Anyway this is incomplete and Aseprite is not yet ready to support GPU acceleration (as the rendering phase is CPU intensive). --- laf | 2 +- src/app/commands/cmd_options.cpp | 9 ++++++--- src/app/modules/gui.cpp | 5 ++--- src/ui/manager.cpp | 2 ++ 4 files changed, 11 insertions(+), 7 deletions(-) diff --git a/laf b/laf index e600f3be1..af7c239ac 160000 --- a/laf +++ b/laf @@ -1 +1 @@ -Subproject commit e600f3be164c6975ed048f7043b779b11c6aa876 +Subproject commit af7c239ac6629f7bd102b3e648c0dd86c2054407 diff --git a/src/app/commands/cmd_options.cpp b/src/app/commands/cmd_options.cpp index 4ce87bd19..661dfa819 100644 --- a/src/app/commands/cmd_options.cpp +++ b/src/app/commands/cmd_options.cpp @@ -434,11 +434,14 @@ public: selectScalingItems(); - if ((int(os::instance()->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/gui.cpp b/src/app/modules/gui.cpp index 53cfb8a62..f7216a14e 100644 --- a/src/app/modules/gui.cpp +++ b/src/app/modules/gui.cpp @@ -1,5 +1,5 @@ // Aseprite -// Copyright (C) 2018-2021 Igara Studio S.A. +// Copyright (C) 2018-2022 Igara Studio S.A. // Copyright (C) 2001-2018 David Capello // // This program is distributed under the terms of @@ -181,8 +181,7 @@ int init_module_gui() // If we've created the display with hardware acceleration, // now we try to do it without hardware acceleration. if (gpuAccel && - (int(os::instance()->capabilities()) & - 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/ui/manager.cpp b/src/ui/manager.cpp index ebf47438c..744de6198 100644 --- a/src/ui/manager.cpp +++ b/src/ui/manager.cpp @@ -275,6 +275,8 @@ void Manager::flipDisplay() m_dirtyRegion.clear(); } + + m_display->swapBuffers(); } void Manager::updateAllDisplaysWithNewScale(int scale) From 38fca8ed61e14f95c21704b7efac0fb6668f5e20 Mon Sep 17 00:00:00 2001 From: David Capello Date: Mon, 16 May 2022 11:50:12 -0300 Subject: [PATCH 3/8] Don't use std::iterator<> as it's deprecated in C++17 --- laf | 2 +- src/doc/image_iterator.h | 56 +++++++++++++++------------------------ src/doc/selected_frames.h | 18 ++++++++++--- 3 files changed, 37 insertions(+), 39 deletions(-) diff --git a/laf b/laf index af7c239ac..f2110aef2 160000 --- a/laf +++ b/laf @@ -1 +1 @@ -Subproject commit af7c239ac6629f7bd102b3e648c0dd86c2054407 +Subproject commit f2110aef250cfdcd8ee5cb14fd28c3cbdfffa578 diff --git a/src/doc/image_iterator.h b/src/doc/image_iterator.h index 6d3a0fb8a..fdeaf8ff8 100644 --- a/src/doc/image_iterator.h +++ b/src/doc/image_iterator.h @@ -1,4 +1,5 @@ // Aseprite Document Library +// Copyright (c) 2022 Igara Studio S.A. // Copyright (c) 2001-2015 David Capello // // This file is released under the terms of the MIT license. @@ -14,7 +15,6 @@ #include "gfx/rect.h" #include -#include #include @@ -25,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) : @@ -141,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) { } }; @@ -160,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) { } }; @@ -180,7 +169,7 @@ namespace doc { class BitPixelAccess { public: BitPixelAccess() : - m_ptr(NULL), + m_ptr(nullptr), m_bit(0) { } @@ -262,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) { } From feece489fe1395b7abae392230fb2e21d96fde06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Capello?= Date: Mon, 16 May 2022 17:27:09 -0300 Subject: [PATCH 4/8] Fix "Expand menu bar items on mouseover" tooltip (fix #3284) --- data/widgets/options.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/widgets/options.xml b/data/widgets/options.xml index 894badfaf..c8824f3a9 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" /> Date: Mon, 16 May 2022 18:32:41 -0300 Subject: [PATCH 5/8] Use Graphics::drawSurface() with explicit Sampling/Paint options --- laf | 2 +- src/app/ui/dithering_selector.cpp | 10 +++++++-- src/app/ui/editor/editor.cpp | 3 ++- src/app/ui/file_list.cpp | 37 ++++++++++++++++++++++++++----- src/ui/graphics.cpp | 30 ------------------------- src/ui/graphics.h | 8 +------ 6 files changed, 43 insertions(+), 47 deletions(-) diff --git a/laf b/laf index f2110aef2..34c67cd6f 160000 --- a/laf +++ b/laf @@ -1 +1 @@ -Subproject commit f2110aef250cfdcd8ee5cb14fd28c3cbdfffa578 +Subproject commit 34c67cd6f2be9dbb14f559b7fb16160318b07086 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 7c9f97cf1..823d50d16 100644 --- a/src/app/ui/editor/editor.cpp +++ b/src/app/ui/editor/editor.cpp @@ -748,7 +748,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/ui/graphics.cpp b/src/ui/graphics.cpp index 7775de8c2..9c75d28e7 100644 --- a/src/ui/graphics.cpp +++ b/src/ui/graphics.cpp @@ -246,21 +246,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, @@ -298,21 +283,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 6ef87d690..c924ac50f 100644 --- a/src/ui/graphics.h +++ b/src/ui/graphics.h @@ -87,19 +87,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, From 4b99d3022ad32acab941b8b37430fd62de2af4ee Mon Sep 17 00:00:00 2001 From: David Capello Date: Mon, 16 May 2022 18:35:33 -0300 Subject: [PATCH 6/8] Minor change in ColorSelector to resize the previous rendered canvas --- src/app/ui/color_selector.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/app/ui/color_selector.cpp b/src/app/ui/color_selector.cpp index f25053d1f..95a33dd5d 100644 --- a/src/app/ui/color_selector.cpp +++ b/src/app/ui/color_selector.cpp @@ -117,8 +117,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(); } From aca96c6d36c60ff68ee0f560a4549899adbf22ae Mon Sep 17 00:00:00 2001 From: David Capello Date: Tue, 17 May 2022 13:24:53 -0300 Subject: [PATCH 7/8] Use shaders for ColorTintShadeTone/ColorSpectrum selectors (#960) This is the first step to optimize the painting code of ColorSelectors which were using a background thread to paint their surface. ColorWheel is still using the old method (without shaders). This impl is already a lot faster on CPU, and it's ready for GPU-acceleration in a future. --- laf | 2 +- src/app/modules/gfx.cpp | 44 ++++---- src/app/modules/gfx.h | 5 +- src/app/ui/color_selector.cpp | 150 ++++++++++++++++++++++++++- src/app/ui/color_selector.h | 24 ++++- src/app/ui/color_spectrum.cpp | 49 ++++++++- src/app/ui/color_spectrum.h | 7 ++ src/app/ui/color_tint_shade_tone.cpp | 49 ++++++++- src/app/ui/color_tint_shade_tone.h | 7 ++ src/app/util/shader_helpers.h | 67 ++++++++++++ 10 files changed, 373 insertions(+), 31 deletions(-) create mode 100644 src/app/util/shader_helpers.h diff --git a/laf b/laf index 34c67cd6f..8b0422877 160000 --- a/laf +++ b/laf @@ -1 +1 @@ -Subproject commit 34c67cd6f2be9dbb14f559b7fb16160318b07086 +Subproject commit 8b0422877a39d655bf6a405eb2e2985763b150da diff --git a/src/app/modules/gfx.cpp b/src/app/modules/gfx.cpp index 5359e6094..281b253f5 100644 --- a/src/app/modules/gfx.cpp +++ b/src/app/modules/gfx.cpp @@ -40,22 +40,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, @@ -90,11 +74,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, @@ -102,7 +102,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, @@ -208,8 +208,8 @@ void draw_alpha_slider(ui::Graphics* g, for (int x=0; xdrawVLine( @@ -236,8 +236,8 @@ void draw_alpha_slider(os::Surface* s, os::Paint paint; for (int x=0; x #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; @@ -224,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) @@ -421,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); @@ -537,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..9be0dba9d 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,52 @@ 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; + 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 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/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 From 4f9fe90304a0f9076dfa87a14c528aecba49152b Mon Sep 17 00:00:00 2001 From: David Capello Date: Tue, 17 May 2022 13:43:22 -0300 Subject: [PATCH 8/8] Display the Hue bar in ColorTintShadeTone with saturation/value=1.0 This is the previous behavior, but might change in the future. --- src/app/ui/color_tint_shade_tone.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/app/ui/color_tint_shade_tone.cpp b/src/app/ui/color_tint_shade_tone.cpp index 9be0dba9d..84f9305a7 100644 --- a/src/app/ui/color_tint_shade_tone.cpp +++ b/src/app/ui/color_tint_shade_tone.cpp @@ -61,11 +61,13 @@ const char* ColorTintShadeTone::getBottomBarShader() "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; + // 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; } )"; }