From f571e4ceb2ea96afe8936ae3cae3c3973a1ebcba Mon Sep 17 00:00:00 2001 From: David Capello Date: Thu, 25 Jun 2015 12:44:47 -0300 Subject: [PATCH] Add BrushPreview class to wrap all code related to brush preview/editor cursor --- src/app/CMakeLists.txt | 2 +- src/app/app.cpp | 3 - src/app/ui/editor/brush_preview.cpp | 456 ++++++++++++++++ src/app/ui/editor/brush_preview.h | 129 +++++ src/app/ui/editor/cursor.cpp | 513 ------------------ src/app/ui/editor/drawing_state.cpp | 25 +- src/app/ui/editor/drawing_state.h | 2 +- src/app/ui/editor/editor.cpp | 174 +++--- src/app/ui/editor/editor.h | 38 +- src/app/ui/editor/editor_state.h | 3 +- src/app/ui/editor/moving_pixels_state.cpp | 7 +- src/app/ui/editor/moving_pixels_state.h | 2 +- src/app/ui/editor/navigate_state.cpp | 3 +- src/app/ui/editor/play_state.cpp | 2 +- src/app/ui/editor/scoped_cursor.h | 34 -- src/app/ui/editor/scrolling_state.cpp | 5 +- src/app/ui/editor/scrolling_state.h | 2 +- src/app/ui/editor/select_box_state.cpp | 25 +- src/app/ui/editor/select_box_state.h | 2 +- src/app/ui/editor/standby_state.cpp | 49 +- src/app/ui/editor/standby_state.h | 4 +- .../ui/editor/state_with_wheel_behavior.cpp | 3 - src/app/ui/editor/tool_loop_impl.cpp | 2 - src/app/ui/editor/zooming_state.cpp | 5 +- src/app/ui/editor/zooming_state.h | 2 +- 25 files changed, 727 insertions(+), 765 deletions(-) create mode 100644 src/app/ui/editor/brush_preview.cpp create mode 100644 src/app/ui/editor/brush_preview.h delete mode 100644 src/app/ui/editor/cursor.cpp delete mode 100644 src/app/ui/editor/scoped_cursor.h diff --git a/src/app/CMakeLists.txt b/src/app/CMakeLists.txt index 908f667b4..ff23f5c44 100644 --- a/src/app/CMakeLists.txt +++ b/src/app/CMakeLists.txt @@ -311,7 +311,7 @@ add_library(app-lib ui/devconsole_view.cpp ui/document_view.cpp ui/drop_down_button.cpp - ui/editor/cursor.cpp + ui/editor/brush_preview.cpp ui/editor/drawing_state.cpp ui/editor/editor.cpp ui/editor/editor_observers.cpp diff --git a/src/app/app.cpp b/src/app/app.cpp index 11aa3f8b9..d85618f08 100644 --- a/src/app/app.cpp +++ b/src/app/app.cpp @@ -175,9 +175,6 @@ void App::initialize(const AppOptions& options) // Register well-known image file types. FileFormatsManager::instance()->registerAllFormats(); - // init editor cursor - Editor::initEditorCursor(); - if (isPortable()) PRINTF("Running in portable mode\n"); diff --git a/src/app/ui/editor/brush_preview.cpp b/src/app/ui/editor/brush_preview.cpp new file mode 100644 index 000000000..c999a2e52 --- /dev/null +++ b/src/app/ui/editor/brush_preview.cpp @@ -0,0 +1,456 @@ +// Aseprite +// Copyright (C) 2001-2015 David Capello +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License version 2 as +// published by the Free Software Foundation. + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "app/ui/editor/brush_preview.h" + +#include "app/app.h" +#include "app/color.h" +#include "app/color_utils.h" +#include "app/document.h" +#include "app/tools/controller.h" +#include "app/tools/ink.h" +#include "app/tools/intertwine.h" +#include "app/tools/point_shape.h" +#include "app/tools/tool_loop.h" +#include "app/ui/context_bar.h" +#include "app/ui/editor/editor.h" +#include "app/ui/editor/tool_loop_impl.h" +#include "app/ui/main_window.h" +#include "app/ui_context.h" +#include "doc/algo.h" +#include "doc/blend_internals.h" +#include "doc/brush.h" +#include "doc/cel.h" +#include "doc/image_impl.h" +#include "doc/layer.h" +#include "doc/primitives.h" +#include "doc/site.h" + +namespace app { + +using namespace doc; + +std::vector BrushPreview::m_savedPixels; +int BrushPreview::m_savedPixelsIterator; + +BrushPreview::BrushPreview(Editor* editor) + : m_editor(editor) + , m_type(CROSS) + , m_onScreen(false) + , m_screenPosition(0, 0) + , m_editorPosition(0, 0) +{ +} + +BrushPreview::~BrushPreview() +{ +} + +// static +Brush* BrushPreview::getCurrentBrush() +{ + return App::instance()->getMainWindow()->getContextBar()->activeBrush().get(); +} + +// static +color_t BrushPreview::getBrushColor(Sprite* sprite, Layer* layer) +{ + app::Color c = Preferences::instance().colorBar.fgColor(); + ASSERT(sprite != NULL); + + // Avoid using invalid colors + if (!c.isValid()) + return 0; + + if (layer != NULL) + return color_utils::color_for_layer(c, layer); + else + return color_utils::color_for_image(c, sprite->pixelFormat()); +} + +// Draws the brush cursor in the specified absolute mouse position +// given in 'pos' param. Warning: You should clean the cursor before +// to use this routine with other editor. +void BrushPreview::show(const gfx::Point& screenPos) +{ + if (m_onScreen) + hide(); + + app::Document* document = m_editor->document(); + Sprite* sprite = m_editor->sprite(); + Layer* layer = m_editor->layer(); + ASSERT(sprite); + + // Get drawable region + m_editor->getDrawableRegion(m_clippingRegion, ui::Widget::kCutTopWindows); + + // Get cursor color + app::Color app_cursor_color = Preferences::instance().editor.cursorColor(); + gfx::Color ui_cursor_color = color_utils::color_for_ui(app_cursor_color); + m_blackAndWhiteNegative = (app_cursor_color.getType() == app::Color::MaskType); + + // Cursor in the screen (view) + m_screenPosition = screenPos; + + // Get cursor position in the editor + gfx::Point spritePos = m_editor->screenToEditor(screenPos); + + // Get the current tool + tools::Ink* ink = m_editor->getCurrentEditorInk(); + + // Setup the cursor type debrushding of several factors (current tool, + // foreground color, and layer transparency). + color_t brush_color = getBrushColor(sprite, layer); + color_t mask_color = sprite->transparentColor(); + + if (ink->isSelection() || ink->isSlice()) { + m_type = SELECTION_CROSS; + } + else if ( + // Use cursor bounds for inks that are effects (eraser, blur, etc.) + (ink->isEffect()) || + // or when the brush color is transparent and we are not in the background layer + (layer && !layer->isBackground() && + brush_color == mask_color)) { + m_type = BRUSH_BOUNDARIES; + } + else { + m_type = CROSS; + } + + // For cursor type 'bounds' we have to generate cursor boundaries + if (m_type & BRUSH_BOUNDARIES) + generateBoundaries(); + + // Draw pixel/brush preview + if ((m_type & CROSS) && m_editor->getState()->requireBrushPreview()) { + Brush* brush = getCurrentBrush(); + gfx::Rect brushBounds = brush->bounds(); + brushBounds.offset(spritePos); + + // Create the extra cel to show the brush preview + Site site = m_editor->getSite(); + Cel* cel = site.cel(); + + int t, opacity = 255; + if (cel) opacity = MUL_UN8(opacity, cel->opacity(), t); + if (layer) opacity = MUL_UN8(opacity, static_cast(layer)->opacity(), t); + + document->prepareExtraCel(brushBounds, opacity); + document->setExtraCelType(render::ExtraType::NONE); + document->setExtraCelBlendMode( + (layer ? static_cast(layer)->blendMode(): + BlendMode::NORMAL)); + + Image* extraImage = document->getExtraCelImage(); + extraImage->setMaskColor(mask_color); + clear_image(extraImage, mask_color); + + if (layer) { + render::Render().renderLayer( + extraImage, layer, m_editor->frame(), + gfx::Clip(0, 0, brushBounds), + BlendMode::SRC); + + // This extra cel is a patch for the current layer/frame + document->setExtraCelType(render::ExtraType::PATCH); + } + + tools::ToolLoop* loop = create_tool_loop_preview( + m_editor, UIContext::instance(), extraImage, + -gfx::Point(brushBounds.x, + brushBounds.y)); + + if (loop) { + loop->getInk()->prepareInk(loop); + loop->getIntertwine()->prepareIntertwine(); + loop->getController()->prepareController(); + loop->getPointShape()->preparePointShape(loop); + loop->getPointShape()->transformPoint( + loop, -brush->bounds().x, -brush->bounds().y); + delete loop; + } + + document->notifySpritePixelsModified( + sprite, gfx::Region(m_lastBounds = brushBounds)); + } + + // Save area and draw the cursor + { + ui::ScreenGraphics g; + ui::SetClip clip(&g, gfx::Rect(0, 0, g.width(), g.height())); + + forEachBrushPixel(&g, m_screenPosition, spritePos, ui_cursor_color, &BrushPreview::savePixelDelegate); + forEachBrushPixel(&g, m_screenPosition, spritePos, ui_cursor_color, &BrushPreview::drawPixelDelegate); + } + + // Cursor in the editor (model) + m_onScreen = true; + m_editorPosition = spritePos; + + // Save the clipping-region to know where to clean the pixels + m_oldClippingRegion = m_clippingRegion; +} + +// Cleans the brush cursor from the specified editor. +// +// The mouse position is got from the last call to drawBrushPreview() +// (m_cursorEditor). So you must to use this routine only if you +// called drawBrushPreview() before. +void BrushPreview::hide() +{ + if (!m_onScreen) + return; + + app::Document* document = m_editor->document(); + Sprite* sprite = m_editor->sprite(); + ASSERT(sprite); + + m_editor->getDrawableRegion(m_clippingRegion, ui::Widget::kCutTopWindows); + + gfx::Point pos = m_editorPosition; + + { + // Restore pixels + ui::ScreenGraphics g; + ui::SetClip clip(&g, gfx::Rect(0, 0, g.width(), g.height())); + + forEachBrushPixel(&g, m_screenPosition, pos, gfx::ColorNone, + &BrushPreview::clearPixelDelegate); + } + + // Clean pixel/brush preview + if ((m_type & CROSS) && + m_editor->getState()->requireBrushPreview()) { + document->destroyExtraCel(); + document->notifySpritePixelsModified( + sprite, gfx::Region(m_lastBounds)); + } + + m_onScreen = false; + m_clippingRegion.clear(); + m_oldClippingRegion.clear(); +} + +void BrushPreview::redraw() +{ + if (m_onScreen) { + gfx::Point screenPos = m_screenPosition; + hide(); + show(screenPos); + } +} + +void BrushPreview::invalidateRegion(const gfx::Region& region) +{ + m_clippingRegion.createSubtraction(m_clippingRegion, region); +} + +void BrushPreview::generateBoundaries() +{ + Brush* brush = getCurrentBrush(); + + if (m_brushBoundaries && + m_brushGen == brush->gen()) + return; + + Image* brushImage = brush->image(); + int w = brushImage->width(); + int h = brushImage->height(); + + m_brushGen = brush->gen(); + m_brushWidth = w; + m_brushHeight = h; + + ImageRef mask; + if (brushImage->pixelFormat() != IMAGE_BITMAP) { + mask.reset(Image::create(IMAGE_BITMAP, w, h)); + + LockImageBits bits(mask.get()); + auto pos = bits.begin(); + for (int v=0; vzoom().scale() >= 4.0) + (this->*pixelDelegate)(g, screenPos, color); +} + +void BrushPreview::traceCrossPixels( + ui::Graphics* g, + const gfx::Point& pt, gfx::Color color, + PixelDelegate pixelDelegate) +{ + static int cross[7*7] = { + 0, 0, 0, 1, 0, 0, 0, + 0, 0, 0, 1, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, + 1, 1, 0, 0, 0, 1, 1, + 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1, 0, 0, 0, + 0, 0, 0, 1, 0, 0, 0, + }; + gfx::Point out; + int u, v; + + for (v=0; v<7; v++) { + for (u=0; u<7; u++) { + if (cross[v*7+u]) { + out.x = pt.x-3+u; + out.y = pt.y-3+v; + (this->*pixelDelegate)(g, out, color); + } + } + } +} + +////////////////////////////////////////////////////////////////////// +// Old Thick Cross + +void BrushPreview::traceSelectionCrossPixels( + ui::Graphics* g, + const gfx::Point& pt, gfx::Color color, + int thickness, PixelDelegate pixelDelegate) +{ + static int cross[6*6] = { + 0, 0, 1, 1, 0, 0, + 0, 0, 1, 1, 0, 0, + 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, + 0, 0, 1, 1, 0, 0, + 0, 0, 1, 1, 0, 0, + }; + gfx::Point out, outpt = m_editor->editorToScreen(pt); + int u, v; + int size = m_editor->zoom().apply(thickness/2); + int size2 = m_editor->zoom().apply(thickness); + if (size2 == 0) size2 = 1; + + for (v=0; v<6; v++) { + for (u=0; u<6; u++) { + if (!cross[v*6+u]) + continue; + + out = outpt; + out.x += ((u<3) ? u-size-3: u-size-3+size2); + out.y += ((v<3) ? v-size-3: v-size-3+size2); + + (this->*pixelDelegate)(g, out, color); + } + } +} + +////////////////////////////////////////////////////////////////////// +// Current Brush Bounds + +void BrushPreview::traceBrushBoundaries(ui::Graphics* g, + gfx::Point pos, + gfx::Color color, + PixelDelegate pixelDelegate) +{ + pos.x -= m_brushWidth/2; + pos.y -= m_brushHeight/2; + + for (const auto& seg : *m_brushBoundaries) { + gfx::Rect bounds = seg.bounds(); + bounds.offset(pos); + bounds = m_editor->editorToScreen(bounds); + + if (seg.open()) { + if (seg.vertical()) --bounds.x; + else --bounds.y; + } + + gfx::Point pt(bounds.x, bounds.y); + if (seg.vertical()) { + for (; pt.y*pixelDelegate)(g, pt, color); + } + else { + for (; pt.x*pixelDelegate)(g, pt, color); + } + } +} + +void BrushPreview::savePixelDelegate(ui::Graphics* g, const gfx::Point& pt, gfx::Color color) +{ + if (m_clippingRegion.contains(pt)) { + color_t c = g->getPixel(pt.x, pt.y); + + if (m_savedPixelsIterator < (int)m_savedPixels.size()) + m_savedPixels[m_savedPixelsIterator] = c; + else + m_savedPixels.push_back(c); + + ++m_savedPixelsIterator; + } +} + +void BrushPreview::drawPixelDelegate(ui::Graphics* gfx, const gfx::Point& pt, gfx::Color color) +{ + if (m_savedPixelsIterator < (int)m_savedPixels.size() && + m_clippingRegion.contains(pt)) { + if (m_blackAndWhiteNegative) { + int c = m_savedPixels[m_savedPixelsIterator++]; + int r = gfx::getr(c); + int g = gfx::getg(c); + int b = gfx::getb(c); + + gfx->putPixel(color_utils::blackandwhite_neg(gfx::rgba(r, g, b)), pt.x, pt.y); + } + else { + gfx->putPixel(color, pt.x, pt.y); + } + } +} + +void BrushPreview::clearPixelDelegate(ui::Graphics* g, const gfx::Point& pt, gfx::Color color) +{ + if (m_savedPixelsIterator < (int)m_savedPixels.size()) { + if (m_clippingRegion.contains(pt)) + g->putPixel(m_savedPixels[m_savedPixelsIterator++], pt.x, pt.y); + else if (!m_oldClippingRegion.isEmpty() && + m_oldClippingRegion.contains(pt)) + m_savedPixelsIterator++; + } +} + +} // namespace app diff --git a/src/app/ui/editor/brush_preview.h b/src/app/ui/editor/brush_preview.h new file mode 100644 index 000000000..ee33d8545 --- /dev/null +++ b/src/app/ui/editor/brush_preview.h @@ -0,0 +1,129 @@ +// Aseprite +// Copyright (C) 2001-2015 David Capello +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License version 2 as +// published by the Free Software Foundation. + +#ifndef APP_UI_EDITOR_BRUSH_PREVIEW_H_INCLUDED +#define APP_UI_EDITOR_BRUSH_PREVIEW_H_INCLUDED +#pragma once + +#include "base/shared_ptr.h" +#include "doc/color.h" +#include "doc/mask_boundaries.h" +#include "gfx/color.h" +#include "gfx/point.h" +#include "gfx/rect.h" +#include "gfx/region.h" + +#include + +namespace doc { + class Brush; + class Layer; + class Sprite; +} + +namespace ui { + class Graphics; +} + +namespace app { + class Editor; + + class BrushPreview { + public: + // Brush type + enum { + CROSS = 1, + SELECTION_CROSS = 2, + BRUSH_BOUNDARIES = 4, + }; + + BrushPreview(Editor* editor); + ~BrushPreview(); + + bool onScreen() const { return m_onScreen; } + const gfx::Point& screenPosition() const { return m_screenPosition; } + + void show(const gfx::Point& screenPos); + void move(const gfx::Point& screenPos); + void hide(); + void redraw(); + + void invalidateRegion(const gfx::Region& region); + + private: + typedef void (BrushPreview::*PixelDelegate)(ui::Graphics*, const gfx::Point&, gfx::Color); + + static doc::Brush* getCurrentBrush(); + static doc::color_t getBrushColor(doc::Sprite* sprite, doc::Layer* layer); + + void generateBoundaries(); + void forEachBrushPixel( + ui::Graphics* g, + const gfx::Point& screenPos, + const gfx::Point& spritePos, + gfx::Color color, + PixelDelegate pixelDelegate); + + void traceCrossPixels(ui::Graphics* g, const gfx::Point& pt, gfx::Color color, PixelDelegate pixel); + void traceSelectionCrossPixels(ui::Graphics* g, const gfx::Point& pt, gfx::Color color, int thickness, PixelDelegate pixel); + void traceBrushBoundaries(ui::Graphics* g, gfx::Point pos, gfx::Color color, PixelDelegate pixel); + + void savePixelDelegate(ui::Graphics* g, const gfx::Point& pt, gfx::Color color); + void drawPixelDelegate(ui::Graphics* g, const gfx::Point& pt, gfx::Color color); + void clearPixelDelegate(ui::Graphics* g, const gfx::Point& pt, gfx::Color color); + + Editor* m_editor; + int m_type; + + // The brush preview shows the cross or brush boundaries as black + // & white negative. + bool m_blackAndWhiteNegative; + + // The brush preview is on the screen. + bool m_onScreen; + gfx::Point m_screenPosition; // Position in the screen (view) + gfx::Point m_editorPosition; // Position in the editor (model) + + // Information about current brush + base::SharedPtr m_brushBoundaries; + int m_brushGen; + int m_brushWidth; + int m_brushHeight; + + static std::vector m_savedPixels; + static int m_savedPixelsIterator; + + gfx::Region m_clippingRegion; + gfx::Region m_oldClippingRegion; + + gfx::Rect m_lastBounds; + }; + + class HideBrushPreview { + public: + HideBrushPreview(BrushPreview& brushPreview) + : m_brushPreview(brushPreview) + , m_oldScreenPosition(brushPreview.screenPosition()) + , m_onScreen(brushPreview.onScreen()) { + if (m_onScreen) + m_brushPreview.hide(); + } + + ~HideBrushPreview() { + if (m_onScreen) + m_brushPreview.show(m_oldScreenPosition); + } + + private: + BrushPreview& m_brushPreview; + gfx::Point m_oldScreenPosition; + bool m_onScreen; + }; + +} // namespace app + +#endif diff --git a/src/app/ui/editor/cursor.cpp b/src/app/ui/editor/cursor.cpp deleted file mode 100644 index 6e9ba6187..000000000 --- a/src/app/ui/editor/cursor.cpp +++ /dev/null @@ -1,513 +0,0 @@ -// Aseprite -// Copyright (C) 2001-2015 David Capello -// -// This program is free software; you can redistribute it and/or modify -// it under the terms of the GNU General Public License version 2 as -// published by the Free Software Foundation. - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include "app/ui/editor/editor.h" - -#include "app/app.h" -#include "app/color.h" -#include "app/color_utils.h" -#include "app/ini_file.h" -#include "app/modules/editors.h" -#include "app/tools/controller.h" -#include "app/tools/ink.h" -#include "app/tools/intertwine.h" -#include "app/tools/point_shape.h" -#include "app/tools/tool.h" -#include "app/tools/tool_loop.h" -#include "app/ui/context_bar.h" -#include "app/ui/editor/tool_loop_impl.h" -#include "app/ui/main_window.h" -#include "app/ui_context.h" -#include "base/bind.h" -#include "base/memory.h" -#include "doc/algo.h" -#include "doc/blend_internals.h" -#include "doc/brush.h" -#include "doc/cel.h" -#include "doc/image_impl.h" -#include "doc/layer.h" -#include "doc/mask_boundaries.h" -#include "doc/primitives.h" -#include "doc/site.h" -#include "doc/sprite.h" -#include "render/render.h" -#include "ui/base.h" -#include "ui/system.h" -#include "ui/widget.h" - -#include - -namespace app { - -using namespace ui; - -// Returns true if the cursor of the editor needs subpixel movement. -#define IS_SUBPIXEL(editor) ((editor)->m_zoom.scale() >= 4.0) - -static struct { - MaskBoundaries* boundaries; - int brush_gen; - int brush_width; - int brush_height; -} cursor_bound = { nullptr, 0, 0, 0 }; - -enum { - CURSOR_THINCROSS = 1, - CURSOR_THICKCROSS = 2, - CURSOR_BRUSHBOUNDS = 4 -}; - -static int cursor_type = CURSOR_THINCROSS; -static bool cursor_negative; - -static std::vector saved_pixel; -static int saved_pixel_n; - -// These clipping regions are shared between all editors, so we cannot -// make assumptions about their old state -static gfx::Region clipping_region; -static gfx::Region old_clipping_region; - -static gfx::Rect lastBrushBounds; - -static void generate_cursor_boundaries(); - -static void trace_thincross_pixels(ui::Graphics* g, Editor* editor, const gfx::Point& pt, gfx::Color color, Editor::PixelDelegate pixel); -static void trace_thickcross_pixels(ui::Graphics* g, Editor* editor, const gfx::Point& pt, gfx::Color color, int thickness, Editor::PixelDelegate pixel); -static void trace_brush_bounds(ui::Graphics* g, Editor* editor, gfx::Point pos, gfx::Color color, Editor::PixelDelegate pixel); - -static void savepixel(ui::Graphics* g, const gfx::Point& pt, gfx::Color color); -static void drawpixel(ui::Graphics* g, const gfx::Point& pt, gfx::Color color); -static void clearpixel(ui::Graphics* g, const gfx::Point& pt, gfx::Color color); - -static color_t get_brush_color(Sprite* sprite, Layer* layer); - -////////////////////////////////////////////////////////////////////// -// CURSOR COLOR -////////////////////////////////////////////////////////////////////// - -static gfx::Color ui_cursor_color; -static bool is_cursor_mask; - -static void update_cursor_color() -{ - app::Color color = Preferences::instance().editor.cursorColor(); - - ui_cursor_color = color_utils::color_for_ui(color); - is_cursor_mask = (color.getType() == app::Color::MaskType); -} - -static Brush* get_current_brush() -{ - return App::instance()->getMainWindow()->getContextBar()->activeBrush().get(); -} - -void Editor::initEditorCursor() -{ - update_cursor_color(); - - Preferences::instance().editor.cursorColor.AfterChange.connect( - Bind(&update_cursor_color)); - - App::instance()->PaletteChange.connect(&update_cursor_color); -} - -void Editor::exitEditorCursor() -{ - delete cursor_bound.boundaries; - cursor_bound.boundaries = nullptr; -} - -// Draws the brush cursor in the specified absolute mouse position -// given in 'pos' param. Warning: You should clean the cursor before -// to use this routine with other editor. -void Editor::drawBrushPreview(const gfx::Point& pos) -{ - ASSERT(!m_cursorOnScreen); - ASSERT(m_sprite); - - // Get drawable region - getDrawableRegion(clipping_region, kCutTopWindows); - - // Get cursor color - cursor_negative = is_cursor_mask; - - // Cursor in the screen (view) - m_cursorScreen = pos; - - // Get cursor position in the editor - gfx::Point spritePos = screenToEditor(pos); - - // Get the current tool - tools::Ink* ink = getCurrentEditorInk(); - - // Setup the cursor type debrushding of several factors (current tool, - // foreground color, and layer transparency). - color_t brush_color = get_brush_color(m_sprite, m_layer); - color_t mask_color = m_sprite->transparentColor(); - - if (ink->isSelection() || ink->isSlice()) { - cursor_type = CURSOR_THICKCROSS; - } - else if ( - // Use cursor bounds for inks that are effects (eraser, blur, etc.) - (ink->isEffect()) || - // or when the brush color is transparent and we are not in the background layer - (m_layer && !m_layer->isBackground() && - brush_color == mask_color)) { - cursor_type = CURSOR_BRUSHBOUNDS; - } - else { - cursor_type = CURSOR_THINCROSS; - } - - // For cursor type 'bounds' we have to generate cursor boundaries - if (cursor_type & CURSOR_BRUSHBOUNDS) - generate_cursor_boundaries(); - - // Draw pixel/brush preview - if (cursor_type & CURSOR_THINCROSS && m_state->requireBrushPreview()) { - Brush* brush = get_current_brush(); - gfx::Rect brushBounds = brush->bounds(); - brushBounds.offset(spritePos); - - // Create the extra cel to show the brush preview - Site site = getSite(); - Cel* cel = site.cel(); - - int t, opacity = 255; - if (cel) opacity = MUL_UN8(opacity, cel->opacity(), t); - if (m_layer) opacity = MUL_UN8(opacity, static_cast(m_layer)->opacity(), t); - - m_document->prepareExtraCel(brushBounds, opacity); - m_document->setExtraCelType(render::ExtraType::NONE); - m_document->setExtraCelBlendMode( - (m_layer ? static_cast(m_layer)->blendMode(): - BlendMode::NORMAL)); - - Image* extraImage = m_document->getExtraCelImage(); - extraImage->setMaskColor(mask_color); - clear_image(extraImage, mask_color); - - if (m_layer) { - render::Render().renderLayer( - extraImage, m_layer, m_frame, - gfx::Clip(0, 0, brushBounds), - BlendMode::SRC); - - // This extra cel is a patch for the current layer/frame - m_document->setExtraCelType(render::ExtraType::PATCH); - } - - tools::ToolLoop* loop = create_tool_loop_preview( - this, UIContext::instance(), extraImage, - -gfx::Point(brushBounds.x, - brushBounds.y)); - - if (loop) { - loop->getInk()->prepareInk(loop); - loop->getIntertwine()->prepareIntertwine(); - loop->getController()->prepareController(); - loop->getPointShape()->preparePointShape(loop); - loop->getPointShape()->transformPoint( - loop, -brush->bounds().x, -brush->bounds().y); - delete loop; - } - - m_document->notifySpritePixelsModified( - m_sprite, - gfx::Region(lastBrushBounds = brushBounds)); - } - - // Save area and draw the cursor - { - ScreenGraphics g; - SetClip clip(&g, gfx::Rect(0, 0, g.width(), g.height())); - - forEachBrushPixel(&g, m_cursorScreen, spritePos, ui_cursor_color, savepixel); - forEachBrushPixel(&g, m_cursorScreen, spritePos, ui_cursor_color, drawpixel); - } - - // Cursor in the editor (model) - m_cursorOnScreen = true; - m_cursorEditor = spritePos; - - // Save the clipping-region to know where to clean the pixels - old_clipping_region = clipping_region; -} - -// Cleans the brush cursor from the specified editor. -// -// The mouse position is got from the last call to drawBrushPreview() -// (m_cursorEditor). So you must to use this routine only if you -// called drawBrushPreview() before. -void Editor::clearBrushPreview() -{ - ASSERT(m_cursorOnScreen); - ASSERT(m_sprite); - - getDrawableRegion(clipping_region, kCutTopWindows); - - gfx::Point pos = m_cursorEditor; - - { - // Restore pixels - ScreenGraphics g; - SetClip clip(&g, gfx::Rect(0, 0, g.width(), g.height())); - - forEachBrushPixel(&g, m_cursorScreen, pos, gfx::ColorNone, clearpixel); - } - - // Clean pixel/brush preview - if (cursor_type & CURSOR_THINCROSS && m_state->requireBrushPreview()) { - m_document->destroyExtraCel(); - m_document->notifySpritePixelsModified( - m_sprite, gfx::Region(lastBrushBounds)); - } - - m_cursorOnScreen = false; - - clipping_region.clear(); - old_clipping_region.clear(); -} - -// Returns true if the cursor to draw in the editor has subpixel -// movement (a little pixel of the screen that indicates where is the -// mouse inside the pixel of the sprite). -bool Editor::doesBrushPreviewNeedSubpixel() -{ - return IS_SUBPIXEL(this); -} - -////////////////////////////////////////////////////////////////////// - -static void generate_cursor_boundaries() -{ - Brush* brush = get_current_brush(); - - if (cursor_bound.boundaries && - cursor_bound.brush_gen == brush->gen()) - return; - - Image* brush_image = brush->image(); - int w = brush_image->width(); - int h = brush_image->height(); - - cursor_bound.brush_gen = brush->gen(); - cursor_bound.brush_width = w; - cursor_bound.brush_height = h; - - ImageRef mask; - if (brush_image->pixelFormat() != IMAGE_BITMAP) { - mask.reset(Image::create(IMAGE_BITMAP, w, h)); - - LockImageBits bits(mask.get()); - auto pos = bits.begin(); - for (int v=0; veditorToScreen(pt); - int u, v; - int size = editor->zoom().apply(thickness/2); - int size2 = editor->zoom().apply(thickness); - if (size2 == 0) size2 = 1; - - for (v=0; v<6; v++) { - for (u=0; u<6; u++) { - if (!cursor_cross[v*6+u]) - continue; - - out = outpt; - out.x += ((u<3) ? u-size-3: u-size-3+size2); - out.y += ((v<3) ? v-size-3: v-size-3+size2); - - pixelDelegate(g, out, color); - } - } -} - -////////////////////////////////////////////////////////////////////// -// Current Brush Bounds - -struct Data { - ui::Graphics* g; - gfx::Color color; - Editor::PixelDelegate pixelDelegate; -}; - -static void algo_line_proxy(int x, int y, void* _data) -{ - Data* data = (Data*)_data; - data->pixelDelegate(data->g, gfx::Point(x, y), data->color); -} - -static void trace_brush_bounds(ui::Graphics* g, Editor* editor, - gfx::Point pos, gfx::Color color, Editor::PixelDelegate pixelDelegate) -{ - Data data = { g, color, pixelDelegate }; - - pos.x -= cursor_bound.brush_width/2; - pos.y -= cursor_bound.brush_height/2; - - for (const auto& seg : *cursor_bound.boundaries) { - gfx::Rect bounds = seg.bounds(); - bounds.offset(pos); - bounds = editor->editorToScreen(bounds); - - if (seg.open()) { - if (seg.vertical()) --bounds.x; - else --bounds.y; - } - - if (seg.vertical()) - doc::algo_line(bounds.x, bounds.y, bounds.x, bounds.y+bounds.h-1, (void*)&data, algo_line_proxy); - else - doc::algo_line(bounds.x, bounds.y, bounds.x+bounds.w-1, bounds.y, (void*)&data, algo_line_proxy); - } -} - -////////////////////////////////////////////////////////////////////// -// Helpers - -static void savepixel(ui::Graphics* g, const gfx::Point& pt, gfx::Color color) -{ - if (clipping_region.contains(pt)) { - color_t c = g->getPixel(pt.x, pt.y); - - if (saved_pixel_n < (int)saved_pixel.size()) - saved_pixel[saved_pixel_n] = c; - else - saved_pixel.push_back(c); - - ++saved_pixel_n; - } -} - -static void drawpixel(ui::Graphics* graphics, const gfx::Point& pt, gfx::Color color) -{ - if (saved_pixel_n < (int)saved_pixel.size() && clipping_region.contains(pt)) { - if (cursor_negative) { - int c = saved_pixel[saved_pixel_n++]; - int r = gfx::getr(c); - int g = gfx::getg(c); - int b = gfx::getb(c); - - graphics->putPixel(color_utils::blackandwhite_neg(gfx::rgba(r, g, b)), pt.x, pt.y); - } - else { - graphics->putPixel(color, pt.x, pt.y); - } - } -} - -static void clearpixel(ui::Graphics* g, const gfx::Point& pt, gfx::Color color) -{ - if (saved_pixel_n < (int)saved_pixel.size()) { - if (clipping_region.contains(pt)) - g->putPixel(saved_pixel[saved_pixel_n++], pt.x, pt.y); - else if (!old_clipping_region.isEmpty() && - old_clipping_region.contains(pt)) - saved_pixel_n++; - } -} - -static color_t get_brush_color(Sprite* sprite, Layer* layer) -{ - app::Color c = Preferences::instance().colorBar.fgColor(); - ASSERT(sprite != NULL); - - // Avoid using invalid colors - if (!c.isValid()) - return 0; - - if (layer != NULL) - return color_utils::color_for_layer(c, layer); - else - return color_utils::color_for_image(c, sprite->pixelFormat()); -} - -} // namespace app diff --git a/src/app/ui/editor/drawing_state.cpp b/src/app/ui/editor/drawing_state.cpp index ba5acf550..e2048d82a 100644 --- a/src/app/ui/editor/drawing_state.cpp +++ b/src/app/ui/editor/drawing_state.cpp @@ -20,7 +20,6 @@ #include "app/tools/tool_loop.h" #include "app/tools/tool_loop_manager.h" #include "app/ui/editor/editor.h" -#include "app/ui/editor/scoped_cursor.h" #include "app/ui/keyboard_shortcuts.h" #include "app/ui_context.h" #include "ui/message.h" @@ -65,7 +64,9 @@ DrawingState::~DrawingState() void DrawingState::initToolLoop(Editor* editor, MouseMessage* msg) { - HideShowDrawingCursor hideShow(editor); + // It's needed to clear and redraw the brush boundaries after the + // first mouse pressed/point shape if drawn. + HideBrushPreview hide(editor->brushPreview()); m_toolLoopManager->prepareLoop(pointer_from_msg(msg)); m_toolLoopManager->pressButton(pointer_from_msg(msg)); @@ -129,16 +130,16 @@ bool DrawingState::onMouseMove(Editor* editor, MouseMessage* msg) m_mouseMoveReceived = true; - // Hide the drawing cursor - HideShowDrawingCursor hideShow(editor); + // It's needed to avoid some glitches with brush boundaries. + // + // TODO we should be able to avoid this if we correctly invalidate + // the BrushPreview::m_clippingRegion + HideBrushPreview hide(editor->brushPreview()); // Infinite scroll gfx::Point mousePos = editor->autoScroll(msg, AutoScroll::MouseDir, true); - // Hide the cursor again - editor->hideDrawingCursor(); - - // notify mouse movement to the tool + // Notify mouse movement to the tool ASSERT(m_toolLoopManager != NULL); m_toolLoopManager ->movement(tools::ToolLoopManager::Pointer(mousePos.x, mousePos.y, @@ -147,15 +148,13 @@ bool DrawingState::onMouseMove(Editor* editor, MouseMessage* msg) return true; } -bool DrawingState::onSetCursor(Editor* editor) +bool DrawingState::onSetCursor(Editor* editor, const gfx::Point& mouseScreenPos) { if (m_toolLoop->getInk()->isEyedropper()) { - editor->hideDrawingCursor(); - ui::set_mouse_cursor(kEyedropperCursor); + editor->showMouseCursor(kEyedropperCursor); } else { - ui::set_mouse_cursor(kNoCursor); - editor->showDrawingCursor(); + editor->showBrushPreview(mouseScreenPos); } return true; } diff --git a/src/app/ui/editor/drawing_state.h b/src/app/ui/editor/drawing_state.h index 8d3079e64..1ce994316 100644 --- a/src/app/ui/editor/drawing_state.h +++ b/src/app/ui/editor/drawing_state.h @@ -24,7 +24,7 @@ namespace app { virtual bool onMouseDown(Editor* editor, ui::MouseMessage* msg) override; virtual bool onMouseUp(Editor* editor, ui::MouseMessage* msg) override; virtual bool onMouseMove(Editor* editor, ui::MouseMessage* msg) override; - virtual bool onSetCursor(Editor* editor) override; + virtual bool onSetCursor(Editor* editor, const gfx::Point& mouseScreenPos) override; virtual bool onKeyDown(Editor* editor, ui::KeyMessage* msg) override; virtual bool onKeyUp(Editor* editor, ui::KeyMessage* msg) override; virtual bool onUpdateStatusBar(Editor* editor) override; diff --git a/src/app/ui/editor/editor.cpp b/src/app/ui/editor/editor.cpp index 5ae25f69d..bbf0207fc 100644 --- a/src/app/ui/editor/editor.cpp +++ b/src/app/ui/editor/editor.cpp @@ -33,7 +33,6 @@ #include "app/ui/editor/moving_pixels_state.h" #include "app/ui/editor/pixels_movement.h" #include "app/ui/editor/play_state.h" -#include "app/ui/editor/scoped_cursor.h" #include "app/ui/editor/standby_state.h" #include "app/ui/main_window.h" #include "app/ui/skin/skin_theme.h" @@ -151,9 +150,7 @@ Editor::Editor(Document* document, EditorFlags flags) , m_layer(m_sprite->folder()->getFirstLayer()) , m_frame(frame_t(0)) , m_zoom(1, 1) - , m_cursorOnScreen(false) - , m_cursorScreen(0, 0) - , m_cursorEditor(0, 0) + , m_brushPreview(this) , m_quicktool(NULL) , m_selectionMode(tools::SelectionMode::DEFAULT) , m_offset_x(0) @@ -219,7 +216,6 @@ Editor::~Editor() void Editor::destroyEditorSharedInternals() { m_renderBuffer.reset(); - exitEditorCursor(); } bool Editor::isActive() const @@ -237,7 +233,7 @@ WidgetType editor_type() void Editor::setStateInternal(const EditorStatePtr& newState) { - HideShowDrawingCursor hideShow(this); + m_brushPreview.hide(); // Fire before change state event, set the state, and fire after // change state event. @@ -269,7 +265,7 @@ void Editor::setStateInternal(const EditorStatePtr& newState) m_observers.notifyStateChanged(this); // Setup the new mouse cursor - setCursor(); + setCursor(ui::get_mouse_position()); updateStatusBar(); } @@ -343,13 +339,10 @@ void Editor::setDefaultScroll() // Sets the scroll position of the editor void Editor::setEditorScroll(const gfx::Point& scroll, bool blit_valid_rgn) { + HideBrushPreview hide(m_brushPreview); View* view = View::getView(this); Point oldScroll; Region region; - bool onScreen = m_cursorOnScreen; - - if (onScreen) - clearBrushPreview(); if (blit_valid_rgn) { getDrawableRegion(region, kCutTopWindows); @@ -363,9 +356,6 @@ void Editor::setEditorScroll(const gfx::Point& scroll, bool blit_valid_rgn) // Move screen with blits scrollRegion(region, oldScroll - newScroll); } - - if (onScreen) - drawBrushPreview(m_cursorScreen); } void Editor::setEditorZoom(Zoom zoom) @@ -512,6 +502,10 @@ void Editor::drawOneSpriteUnclippedRect(ui::Graphics* g, const gfx::Rect& sprite tmp, 0, 0, 0, 0, rc.w, rc.h); g->blit(tmp, 0, 0, dest_x, dest_y, rc.w, rc.h); + + m_brushPreview.invalidateRegion( + gfx::Region( + gfx::Rect(dest_x, dest_y, rc.w, rc.h))); } } } @@ -698,17 +692,11 @@ void Editor::drawMaskSafe() if (isVisible() && m_document && m_document->getMaskBoundaries()) { - bool onScreen = m_cursorOnScreen; - Region region; getDrawableRegion(region, kCutTopWindows); region.offset(-getBounds().getOrigin()); - if (onScreen) - clearBrushPreview(); - else - ui::hide_mouse_cursor(); - + HideBrushPreview hide(m_brushPreview); GraphicsPtr g = getGraphics(getClientBounds()); for (const gfx::Rect& rc : region) { @@ -716,12 +704,6 @@ void Editor::drawMaskSafe() if (clip) drawMask(g.get()); } - - // Draw the cursor - if (onScreen) - drawBrushPreview(m_cursorScreen); - else - ui::show_mouse_cursor(); } } @@ -814,6 +796,8 @@ void Editor::flashCurrentLayer() gfx::Point Editor::autoScroll(MouseMessage* msg, AutoScroll dir, bool blit_valid_rgn) { + // // Hide the brush preview + // HideBrushPreview hide(editor->brushPreview()); View* view = View::getView(this); gfx::Rect vp = view->getViewportBounds(); gfx::Point mousePos = msg->position(); @@ -857,6 +841,16 @@ gfx::Point Editor::autoScroll(MouseMessage* msg, AutoScroll dir, bool blit_valid return mousePos; } +void Editor::drawBrushPreview(const gfx::Point& pos) +{ + m_brushPreview.show(pos); +} + +void Editor::clearBrushPreview() +{ + m_brushPreview.hide(); +} + bool Editor::isCurrentToolAffectedByRightClickMode() { tools::Tool* tool = App::instance()->activeTool(); @@ -1010,43 +1004,6 @@ Rect Editor::editorToScreen(const Rect& rc) editorToScreen(rc.getPoint2())); } -void Editor::showDrawingCursor() -{ - ASSERT(m_sprite != NULL); - - if (!m_cursorOnScreen && canDraw()) { - ui::hide_mouse_cursor(); - drawBrushPreview(ui::get_mouse_position()); - ui::show_mouse_cursor(); - } -} - -void Editor::hideDrawingCursor() -{ - if (m_cursorOnScreen) { - ui::hide_mouse_cursor(); - clearBrushPreview(); - ui::show_mouse_cursor(); - } -} - -void Editor::moveDrawingCursor() -{ - // Draw cursor - if (m_cursorOnScreen) { - gfx::Point mousePos = ui::get_mouse_position(); - - // Redraw it only when the mouse change to other pixel (not when - // the mouse just moves). - if (m_cursorScreen != mousePos) { - ui::hide_mouse_cursor(); - clearBrushPreview(); - drawBrushPreview(mousePos); - ui::show_mouse_cursor(); - } - } -} - void Editor::addObserver(EditorObserver* observer) { m_observers.addObserver(observer); @@ -1081,19 +1038,16 @@ Rect Editor::getVisibleSpriteBounds() // Changes the scroll to see the given point as the center of the editor. void Editor::centerInSpritePoint(const gfx::Point& spritePos) { + HideBrushPreview hide(m_brushPreview); View* view = View::getView(this); Rect vp = view->getViewportBounds(); - hideDrawingCursor(); - gfx::Point scroll( m_offset_x - (vp.w/2) + m_zoom.apply(1)/2 + m_zoom.apply(spritePos.x), m_offset_y - (vp.h/2) + m_zoom.apply(1)/2 + m_zoom.apply(spritePos.y)); updateEditor(); setEditorScroll(scroll, false); - - showDrawingCursor(); invalidate(); } @@ -1133,21 +1087,18 @@ void Editor::updateQuicktool() // // TODO We could add a quick tool observer for this if (old_quicktool != new_quicktool) { - // Hide the drawing cursor with the current tool brush size before - // we change the quicktool. In this way we avoid using the - // quicktool brush size to clean the current tool cursor. + // Hide the brush preview with the current tool brush size + // before we change the quicktool. In this way we avoid using + // the quicktool brush size to clean the current tool cursor. // // TODO Create a new Document concept of multiple extra cels: we // need an extra cel for the drawing cursor, other for the moving // pixels, etc. In this way we'll not have conflicts between // different uses of the same extra cel. - if (m_state->requireBrushPreview()) - hideDrawingCursor(); - - m_quicktool = new_quicktool; - - if (m_state->requireBrushPreview()) - showDrawingCursor(); + { + HideBrushPreview hide(m_brushPreview); + m_quicktool = new_quicktool; + } m_state->onQuickToolChange(this); @@ -1226,7 +1177,7 @@ bool Editor::onProcessMessage(Message* msg) break; case kMouseLeaveMessage: - hideDrawingCursor(); + m_brushPreview.hide(); StatusBar::instance()->clearText(); break; @@ -1240,7 +1191,7 @@ bool Editor::onProcessMessage(Message* msg) updateQuicktool(); updateContextBarFromModifiers(); - setCursor(); + setCursor(mouseMsg->position()); } EditorStatePtr holdState(m_state); @@ -1258,14 +1209,15 @@ bool Editor::onProcessMessage(Message* msg) case kMouseUpMessage: if (m_sprite) { EditorStatePtr holdState(m_state); - bool result = m_state->onMouseUp(this, static_cast(msg)); + MouseMessage* mouseMsg = static_cast(msg); + bool result = m_state->onMouseUp(this, mouseMsg); if (!hasCapture()) { m_secondaryButton = false; updateQuicktool(); updateContextBarFromModifiers(); - setCursor(); + setCursor(mouseMsg->position()); } if (result) @@ -1281,7 +1233,7 @@ bool Editor::onProcessMessage(Message* msg) if (hasMouse()) { updateQuicktool(); updateContextBarFromModifiers(); - setCursor(); + setCursor(ui::get_mouse_position()); } if (used) @@ -1297,7 +1249,7 @@ bool Editor::onProcessMessage(Message* msg) if (hasMouse()) { updateQuicktool(); updateContextBarFromModifiers(); - setCursor(); + setCursor(ui::get_mouse_position()); } if (used) @@ -1320,7 +1272,7 @@ bool Editor::onProcessMessage(Message* msg) break; case kSetCursorMessage: - setCursor(); + setCursor(static_cast(msg)->position()); return true; } @@ -1361,14 +1313,11 @@ void Editor::onResize(ui::ResizeEvent& ev) void Editor::onPaint(ui::PaintEvent& ev) { + HideBrushPreview hide(m_brushPreview); Graphics* g = ev.getGraphics(); gfx::Rect rc = getClientBounds(); SkinTheme* theme = static_cast(this->getTheme()); - bool onScreen = m_cursorOnScreen; - if (onScreen) - clearBrushPreview(); - // Editor without sprite if (!m_sprite) { g->fillRect(theme->colors.editorFace(), rc); @@ -1390,11 +1339,6 @@ void Editor::onPaint(ui::PaintEvent& ev) else { m_mask_timer.stop(); } - - // Draw the cursor again - if (onScreen) { - drawBrushPreview(ui::get_mouse_position()); - } } catch (const LockedDocumentException&) { // The sprite is locked to be read, so we can draw an opaque @@ -1405,6 +1349,12 @@ void Editor::onPaint(ui::PaintEvent& ev) } } +void Editor::onInvalidateRegion(const gfx::Region& region) +{ + Widget::onInvalidateRegion(region); + m_brushPreview.invalidateRegion(region); +} + // When the current tool is changed void Editor::onCurrentToolChange() { @@ -1419,18 +1369,12 @@ void Editor::onCurrentToolChange() void Editor::onFgColorChange() { - if (m_cursorOnScreen) { - hideDrawingCursor(); - showDrawingCursor(); - } + m_brushPreview.redraw(); } void Editor::onBrushSizeOrAngleChange() { - if (m_cursorOnScreen) { - hideDrawingCursor(); - showDrawingCursor(); - } + m_brushPreview.redraw(); } void Editor::onExposeSpritePixels(doc::DocumentEvent& ev) @@ -1439,16 +1383,14 @@ void Editor::onExposeSpritePixels(doc::DocumentEvent& ev) m_state->onExposeSpritePixels(ev.region()); } -void Editor::setCursor() +void Editor::setCursor(const gfx::Point& mouseScreenPos) { bool used = false; if (m_sprite) - used = m_state->onSetCursor(this); + used = m_state->onSetCursor(this, mouseScreenPos); - if (!used) { - hideDrawingCursor(); - ui::set_mouse_cursor(kArrowCursor); - } + if (!used) + showMouseCursor(kArrowCursor); } bool Editor::canDraw() @@ -1474,7 +1416,7 @@ void Editor::setZoomAndCenterInMouse(Zoom zoom, { View* view = View::getView(this); Rect vp = view->getViewportBounds(); - HideShowDrawingCursor hideShow(this); + HideBrushPreview hide(m_brushPreview); gfx::Point screenPos; gfx::Point spritePos; @@ -1547,7 +1489,7 @@ void Editor::pasteImage(const Image* image, const gfx::Point& pos) // Clear brush preview, as the extra cel will be replaced with the // pasted image. - hideDrawingCursor(); + m_brushPreview.hide(); PixelsMovementPtr pixelsMovement( new PixelsMovement(UIContext::instance(), @@ -1637,6 +1579,18 @@ void Editor::setAnimationSpeedMultiplier(double speed) m_aniSpeed = speed; } +void Editor::showMouseCursor(CursorType cursorType) +{ + m_brushPreview.hide(); + ui::set_mouse_cursor(cursorType); +} + +void Editor::showBrushPreview(const gfx::Point& screenPos) +{ + ui::set_mouse_cursor(kNoCursor); + m_brushPreview.show(screenPos); +} + // static ImageBufferPtr Editor::getRenderImageBuffer() { diff --git a/src/app/ui/editor/editor.h b/src/app/ui/editor/editor.h index 6df7d8e80..eb76e2c0b 100644 --- a/src/app/ui/editor/editor.h +++ b/src/app/ui/editor/editor.h @@ -13,6 +13,7 @@ #include "app/color.h" #include "app/document.h" #include "app/tools/selection_mode.h" +#include "app/ui/editor/brush_preview.h" #include "app/ui/editor/editor_observers.h" #include "app/ui/editor/editor_state.h" #include "app/ui/editor/editor_states_history.h" @@ -24,6 +25,7 @@ #include "gfx/fwd.h" #include "render/zoom.h" #include "ui/base.h" +#include "ui/cursor_type.h" #include "ui/timer.h" #include "ui/widget.h" @@ -59,8 +61,6 @@ namespace app { class Editor : public ui::Widget, public doc::DocumentObserver { public: - typedef void (*PixelDelegate)(ui::Graphics*, const gfx::Point&, gfx::Color); - enum EditorFlags { kNoneFlag = 0, kShowGrid = 1, @@ -118,7 +118,6 @@ namespace app { const render::Zoom& zoom() const { return m_zoom; } int offsetX() const { return m_offset_x; } int offsetY() const { return m_offset_y; } - bool cursorOnScreen() const { return m_cursorOnScreen; } void setZoom(render::Zoom zoom) { m_zoom = zoom; } void setOffsetX(int x) { m_offset_x = x; } @@ -142,10 +141,6 @@ namespace app { gfx::Rect screenToEditor(const gfx::Rect& rc); gfx::Rect editorToScreen(const gfx::Rect& rc); - void showDrawingCursor(); - void hideDrawingCursor(); - void moveDrawingCursor(); - void addObserver(EditorObserver* observer); void removeObserver(EditorObserver* observer); @@ -201,34 +196,36 @@ namespace app { double getAnimationSpeedMultiplier() const; void setAnimationSpeedMultiplier(double speed); + // Functions to be used in EditorState::onSetCursor() + void showMouseCursor(ui::CursorType cursorType); + void showBrushPreview(const gfx::Point& pos); + + // Gets the brush preview controller. + BrushPreview& brushPreview() { return m_brushPreview; } + // Returns the buffer used to render editor viewports. // E.g. It can be re-used by PreviewCommand static ImageBufferPtr getRenderImageBuffer(); static AppRender& renderEngine() { return m_renderEngine; } - // in cursor.cpp - - static void initEditorCursor(); - protected: bool onProcessMessage(ui::Message* msg) override; void onPreferredSize(ui::PreferredSizeEvent& ev) override; void onResize(ui::ResizeEvent& ev) override; void onPaint(ui::PaintEvent& ev) override; + void onInvalidateRegion(const gfx::Region& region) override; void onCurrentToolChange(); void onFgColorChange(); void onBrushSizeOrAngleChange(); void onExposeSpritePixels(doc::DocumentEvent& ev); private: - static void exitEditorCursor(); void setStateInternal(const EditorStatePtr& newState); void updateQuicktool(); void updateContextBarFromModifiers(); void drawBrushPreview(const gfx::Point& pos); void clearBrushPreview(); - bool doesBrushPreviewNeedSubpixel(); bool isCurrentToolAffectedByRightClickMode(); void drawMaskSafe(); @@ -236,14 +233,7 @@ namespace app { void drawGrid(ui::Graphics* g, const gfx::Rect& spriteBounds, const gfx::Rect& gridBounds, const app::Color& color, int alpha); - void setCursor(); - - void forEachBrushPixel( - ui::Graphics* g, - const gfx::Point& screenPos, - const gfx::Point& spritePos, - gfx::Color color, - PixelDelegate pixelDelegate); + void setCursor(const gfx::Point& mouseScreenPos); // Draws the specified portion of sprite in the editor. Warning: // You should setup the clip of the screen before calling this @@ -266,10 +256,8 @@ namespace app { frame_t m_frame; // Active frame in the editor render::Zoom m_zoom; // Zoom in the editor - // Drawing cursor - bool m_cursorOnScreen; - gfx::Point m_cursorScreen; // Position in the screen (view) - gfx::Point m_cursorEditor; // Position in the editor (model) + // Brush preview + BrushPreview m_brushPreview; // Current selected quicktool (this genererally should be NULL if // the user is not pressing any keyboard key). diff --git a/src/app/ui/editor/editor_state.h b/src/app/ui/editor/editor_state.h index 6e4a400e2..6754dab6a 100644 --- a/src/app/ui/editor/editor_state.h +++ b/src/app/ui/editor/editor_state.h @@ -11,6 +11,7 @@ #include "base/disable_copying.h" #include "base/shared_ptr.h" +#include "gfx/point.h" namespace gfx { class Region; @@ -87,7 +88,7 @@ namespace app { // Called each time the mouse changes its position so we can set an // appropiated cursor depending on the new coordinates of the mouse // pointer. - virtual bool onSetCursor(Editor* editor) { return false; } + virtual bool onSetCursor(Editor* editor, const gfx::Point& mouseScreenPos) { return false; } // Called when a key is pressed over the current editor. virtual bool onKeyDown(Editor* editor, ui::KeyMessage* msg) { return false; } diff --git a/src/app/ui/editor/moving_pixels_state.cpp b/src/app/ui/editor/moving_pixels_state.cpp index 42f86c661..cffdb482d 100644 --- a/src/app/ui/editor/moving_pixels_state.cpp +++ b/src/app/ui/editor/moving_pixels_state.cpp @@ -319,20 +319,19 @@ bool MovingPixelsState::onMouseMove(Editor* editor, MouseMessage* msg) return StandbyState::onMouseMove(editor, msg); } -bool MovingPixelsState::onSetCursor(Editor* editor) +bool MovingPixelsState::onSetCursor(Editor* editor, const gfx::Point& mouseScreenPos) { ASSERT(m_pixelsMovement); ASSERT(editor == m_editor); // Move selection if (m_pixelsMovement->isDragging()) { - editor->hideDrawingCursor(); - ui::set_mouse_cursor(kMoveCursor); + editor->showMouseCursor(kMoveCursor); return true; } // Use StandbyState implementation - return StandbyState::onSetCursor(editor); + return StandbyState::onSetCursor(editor, mouseScreenPos); } bool MovingPixelsState::onKeyDown(Editor* editor, KeyMessage* msg) diff --git a/src/app/ui/editor/moving_pixels_state.h b/src/app/ui/editor/moving_pixels_state.h index d45f08423..da5dc968e 100644 --- a/src/app/ui/editor/moving_pixels_state.h +++ b/src/app/ui/editor/moving_pixels_state.h @@ -41,7 +41,7 @@ namespace app { virtual bool onMouseDown(Editor* editor, ui::MouseMessage* msg) override; virtual bool onMouseUp(Editor* editor, ui::MouseMessage* msg) override; virtual bool onMouseMove(Editor* editor, ui::MouseMessage* msg) override; - virtual bool onSetCursor(Editor* editor) override; + virtual bool onSetCursor(Editor* editor, const gfx::Point& mouseScreenPos) override; virtual bool onKeyDown(Editor* editor, ui::KeyMessage* msg) override; virtual bool onKeyUp(Editor* editor, ui::KeyMessage* msg) override; virtual bool onUpdateStatusBar(Editor* editor) override; diff --git a/src/app/ui/editor/navigate_state.cpp b/src/app/ui/editor/navigate_state.cpp index 379d515ec..0d9511466 100644 --- a/src/app/ui/editor/navigate_state.cpp +++ b/src/app/ui/editor/navigate_state.cpp @@ -13,6 +13,7 @@ #include "app/ui/editor/editor.h" #include "app/ui/editor/scrolling_state.h" +#include "ui/message.h" namespace app { @@ -46,7 +47,7 @@ bool NavigateState::onMouseUp(Editor* editor, MouseMessage* msg) bool NavigateState::onMouseMove(Editor* editor, MouseMessage* msg) { - editor->moveDrawingCursor(); + editor->showBrushPreview(msg->position()); editor->updateStatusBar(); return true; } diff --git a/src/app/ui/editor/play_state.cpp b/src/app/ui/editor/play_state.cpp index cf2700c6d..2828af191 100644 --- a/src/app/ui/editor/play_state.cpp +++ b/src/app/ui/editor/play_state.cpp @@ -109,7 +109,7 @@ bool PlayState::onMouseUp(Editor* editor, MouseMessage* msg) bool PlayState::onMouseMove(Editor* editor, MouseMessage* msg) { - editor->moveDrawingCursor(); + editor->showBrushPreview(msg->position()); editor->updateStatusBar(); return true; } diff --git a/src/app/ui/editor/scoped_cursor.h b/src/app/ui/editor/scoped_cursor.h deleted file mode 100644 index bf264f0dc..000000000 --- a/src/app/ui/editor/scoped_cursor.h +++ /dev/null @@ -1,34 +0,0 @@ -// Aseprite -// Copyright (C) 2001-2015 David Capello -// -// This program is free software; you can redistribute it and/or modify -// it under the terms of the GNU General Public License version 2 as -// published by the Free Software Foundation. - -#ifndef APP_UI_EDITOR_SCOPED_CURSOR_H_INCLUDED -#define APP_UI_EDITOR_SCOPED_CURSOR_H_INCLUDED -#pragma once - -namespace app { - - class HideShowDrawingCursor { - public: - HideShowDrawingCursor(Editor* editor) - : m_editor(editor) - , m_cursorOnScreen(m_editor->cursorOnScreen()) - { - if (m_cursorOnScreen) - m_editor->hideDrawingCursor(); - } - ~HideShowDrawingCursor() { - if (m_cursorOnScreen) - m_editor->showDrawingCursor(); - } - private: - Editor* m_editor; - bool m_cursorOnScreen; - }; - -} // namespace app - -#endif diff --git a/src/app/ui/editor/scrolling_state.cpp b/src/app/ui/editor/scrolling_state.cpp index 99d434174..a0465cf59 100644 --- a/src/app/ui/editor/scrolling_state.cpp +++ b/src/app/ui/editor/scrolling_state.cpp @@ -67,10 +67,9 @@ bool ScrollingState::onMouseMove(Editor* editor, MouseMessage* msg) return true; } -bool ScrollingState::onSetCursor(Editor* editor) +bool ScrollingState::onSetCursor(Editor* editor, const gfx::Point& mouseScreenPos) { - editor->hideDrawingCursor(); - ui::set_mouse_cursor(kScrollCursor); + editor->showMouseCursor(kScrollCursor); return true; } diff --git a/src/app/ui/editor/scrolling_state.h b/src/app/ui/editor/scrolling_state.h index aeb2ea342..4970bb317 100644 --- a/src/app/ui/editor/scrolling_state.h +++ b/src/app/ui/editor/scrolling_state.h @@ -22,7 +22,7 @@ namespace app { virtual bool onMouseDown(Editor* editor, ui::MouseMessage* msg) override; virtual bool onMouseUp(Editor* editor, ui::MouseMessage* msg) override; virtual bool onMouseMove(Editor* editor, ui::MouseMessage* msg) override; - virtual bool onSetCursor(Editor* editor) override; + virtual bool onSetCursor(Editor* editor, const gfx::Point& mouseScreenPos) override; virtual bool onKeyDown(Editor* editor, ui::KeyMessage* msg) override; virtual bool onKeyUp(Editor* editor, ui::KeyMessage* msg) override; virtual bool onUpdateStatusBar(Editor* editor) override; diff --git a/src/app/ui/editor/select_box_state.cpp b/src/app/ui/editor/select_box_state.cpp index f523f031d..96ad8486f 100644 --- a/src/app/ui/editor/select_box_state.cpp +++ b/src/app/ui/editor/select_box_state.cpp @@ -165,27 +165,30 @@ bool SelectBoxState::onMouseMove(Editor* editor, MouseMessage* msg) return StandbyState::onMouseMove(editor, msg); } -bool SelectBoxState::onSetCursor(Editor* editor) +bool SelectBoxState::onSetCursor(Editor* editor, const gfx::Point& mouseScreenPos) { if (hasFlag(RULERS)) { if (m_movingRuler >= 0) { switch (m_rulers[m_movingRuler].getOrientation()) { - case Ruler::Horizontal: ui::set_mouse_cursor(kSizeNSCursor); return true; - case Ruler::Vertical: ui::set_mouse_cursor(kSizeWECursor); return true; + + case Ruler::Horizontal: + editor->showMouseCursor(kSizeNSCursor); + return true; + + case Ruler::Vertical: + editor->showMouseCursor(kSizeWECursor); + return true; } } - int x = ui::get_mouse_position().x; - int y = ui::get_mouse_position().y; - for (Rulers::iterator it = m_rulers.begin(), end = m_rulers.end(); it != end; ++it) { - if (touchRuler(editor, *it, x, y)) { + if (touchRuler(editor, *it, mouseScreenPos.x, mouseScreenPos.y)) { switch (it->getOrientation()) { case Ruler::Horizontal: - ui::set_mouse_cursor(kSizeNSCursor); + editor->showMouseCursor(kSizeNSCursor); return true; case Ruler::Vertical: - ui::set_mouse_cursor(kSizeWECursor); + editor->showMouseCursor(kSizeWECursor); return true; } } @@ -193,11 +196,11 @@ bool SelectBoxState::onSetCursor(Editor* editor) } if (!requireBrushPreview()) { - ui::set_mouse_cursor(kArrowCursor); + editor->showMouseCursor(kArrowCursor); return true; } - return StandbyState::onSetCursor(editor); + return StandbyState::onSetCursor(editor, mouseScreenPos); } bool SelectBoxState::acceptQuickTool(tools::Tool* tool) diff --git a/src/app/ui/editor/select_box_state.h b/src/app/ui/editor/select_box_state.h index bcb5de186..a7a27f39b 100644 --- a/src/app/ui/editor/select_box_state.h +++ b/src/app/ui/editor/select_box_state.h @@ -62,7 +62,7 @@ namespace app { virtual bool onMouseDown(Editor* editor, ui::MouseMessage* msg) override; virtual bool onMouseUp(Editor* editor, ui::MouseMessage* msg) override; virtual bool onMouseMove(Editor* editor, ui::MouseMessage* msg) override; - virtual bool onSetCursor(Editor* editor) override; + virtual bool onSetCursor(Editor* editor, const gfx::Point& mouseScreenPos) override; virtual bool acceptQuickTool(tools::Tool* tool) override; virtual bool requireBrushPreview() override; virtual tools::Ink* getStateInk() override; diff --git a/src/app/ui/editor/standby_state.cpp b/src/app/ui/editor/standby_state.cpp index bbe30ccf9..1b3cc8483 100644 --- a/src/app/ui/editor/standby_state.cpp +++ b/src/app/ui/editor/standby_state.cpp @@ -281,71 +281,62 @@ bool StandbyState::onMouseMove(Editor* editor, MouseMessage* msg) callEyedropper(editor); } - editor->moveDrawingCursor(); + editor->showBrushPreview(msg->position()); editor->updateStatusBar(); return true; } -bool StandbyState::onSetCursor(Editor* editor) +bool StandbyState::onSetCursor(Editor* editor, const gfx::Point& mouseScreenPos) { tools::Ink* ink = editor->getCurrentEditorInk(); if (ink) { // If the current tool change selection (e.g. rectangular marquee, etc.) if (ink->isSelection()) { // See if the cursor is in some selection handle. - if (m_decorator->onSetCursor(editor)) + if (m_decorator->onSetCursor(editor, mouseScreenPos)) return true; // Move pixels if (editor->isInsideSelection()) { EditorCustomizationDelegate* customization = editor->getCustomizationDelegate(); - editor->hideDrawingCursor(); - if (customization && customization->isCopySelectionKeyPressed()) - ui::set_mouse_cursor(kArrowPlusCursor); + editor->showMouseCursor(kArrowPlusCursor); else - ui::set_mouse_cursor(kMoveCursor); + editor->showMouseCursor(kMoveCursor); return true; } } else if (ink->isEyedropper()) { - editor->hideDrawingCursor(); - ui::set_mouse_cursor(kEyedropperCursor); + editor->showMouseCursor(kEyedropperCursor); return true; } else if (ink->isZoom()) { - editor->hideDrawingCursor(); - ui::set_mouse_cursor(kMagnifierCursor); + editor->showMouseCursor(kMagnifierCursor); return true; } else if (ink->isScrollMovement()) { - editor->hideDrawingCursor(); - ui::set_mouse_cursor(kScrollCursor); + editor->showMouseCursor(kScrollCursor); return true; } else if (ink->isCelMovement()) { - editor->hideDrawingCursor(); - ui::set_mouse_cursor(kMoveCursor); + editor->showMouseCursor(kMoveCursor); return true; } else if (ink->isSlice()) { - ui::set_mouse_cursor(kNoCursor); - editor->showDrawingCursor(); + editor->showBrushPreview(mouseScreenPos); return true; } } // Draw if (editor->canDraw()) { - ui::set_mouse_cursor(kNoCursor); - editor->showDrawingCursor(); + editor->showBrushPreview(mouseScreenPos); } // Forbidden else { - editor->hideDrawingCursor(); - ui::set_mouse_cursor(kForbiddenCursor); + editor->showMouseCursor(kForbiddenCursor); } return true; @@ -420,7 +411,7 @@ void StandbyState::transformSelection(Editor* editor, MouseMessage* msg, HandleT try { // Clear brush preview, as the extra cel will be replaced with the // transformed image. - editor->hideDrawingCursor(); + editor->brushPreview().hide(); EditorCustomizationDelegate* customization = editor->getCustomizationDelegate(); Document* document = editor->document(); @@ -446,11 +437,11 @@ void StandbyState::transformSelection(Editor* editor, MouseMessage* msg, HandleT // TODO steal the PixelsMovement of the other editor and use it for this one. StatusBar::instance()->showTip(1000, "The sprite is locked in other editor"); - ui::set_mouse_cursor(kForbiddenCursor); + editor->showMouseCursor(kForbiddenCursor); } catch (const std::bad_alloc&) { StatusBar::instance()->showTip(1000, "Not enough memory to transform the selection"); - ui::set_mouse_cursor(kForbiddenCursor); + editor->showMouseCursor(kForbiddenCursor); } } @@ -492,7 +483,7 @@ TransformHandles* StandbyState::Decorator::getTransformHandles(Editor* editor) return m_transfHandles; } -bool StandbyState::Decorator::onSetCursor(Editor* editor) +bool StandbyState::Decorator::onSetCursor(Editor* editor, const gfx::Point& mouseScreenPos) { if (!editor->isActive() || !editor->document()->isMaskVisible()) @@ -500,8 +491,8 @@ bool StandbyState::Decorator::onSetCursor(Editor* editor) const gfx::Transformation transformation(m_standbyState->getTransformation(editor)); TransformHandles* tr = getTransformHandles(editor); - HandleType handle = tr->getHandleAtPoint(editor, - ui::get_mouse_position(), transformation); + HandleType handle = tr->getHandleAtPoint( + editor, mouseScreenPos, transformation); CursorType newCursor = kArrowCursor; @@ -553,9 +544,7 @@ bool StandbyState::Decorator::onSetCursor(Editor* editor) newCursor = rotated_rotate_cursors[(c+angle) % num]; } - // Hide the drawing cursor (just in case) and show the new system cursor. - editor->hideDrawingCursor(); - ui::set_mouse_cursor(newCursor); + editor->showMouseCursor(newCursor); return true; } diff --git a/src/app/ui/editor/standby_state.h b/src/app/ui/editor/standby_state.h index 0ca3bfb71..d621c0234 100644 --- a/src/app/ui/editor/standby_state.h +++ b/src/app/ui/editor/standby_state.h @@ -27,7 +27,7 @@ namespace app { virtual bool onMouseDown(Editor* editor, ui::MouseMessage* msg) override; virtual bool onMouseUp(Editor* editor, ui::MouseMessage* msg) override; virtual bool onMouseMove(Editor* editor, ui::MouseMessage* msg) override; - virtual bool onSetCursor(Editor* editor) override; + virtual bool onSetCursor(Editor* editor, const gfx::Point& mouseScreenPos) override; virtual bool onKeyDown(Editor* editor, ui::KeyMessage* msg) override; virtual bool onKeyUp(Editor* editor, ui::KeyMessage* msg) override; virtual bool onUpdateStatusBar(Editor* editor) override; @@ -54,7 +54,7 @@ namespace app { TransformHandles* getTransformHandles(Editor* editor); - bool onSetCursor(Editor* editor); + bool onSetCursor(Editor* editor, const gfx::Point& mouseScreenPos); // EditorDecorator overrides void preRenderDecorator(EditorPreRender* render) override; diff --git a/src/app/ui/editor/state_with_wheel_behavior.cpp b/src/app/ui/editor/state_with_wheel_behavior.cpp index 6b89fed2e..c9966dba5 100644 --- a/src/app/ui/editor/state_with_wheel_behavior.cpp +++ b/src/app/ui/editor/state_with_wheel_behavior.cpp @@ -152,10 +152,7 @@ bool StateWithWheelBehavior::onMouseWheel(Editor* editor, MouseMessage* msg) } gfx::Point scroll = view->getViewScroll(); - - editor->hideDrawingCursor(); editor->setEditorScroll(scroll+delta, true); - editor->showDrawingCursor(); break; } diff --git a/src/app/ui/editor/tool_loop_impl.cpp b/src/app/ui/editor/tool_loop_impl.cpp index 5610faa55..15d1f6db7 100644 --- a/src/app/ui/editor/tool_loop_impl.cpp +++ b/src/app/ui/editor/tool_loop_impl.cpp @@ -190,9 +190,7 @@ public: } void updateDirtyArea() override { - m_editor->hideDrawingCursor(); m_document->notifySpritePixelsModified(m_sprite, m_dirtyArea); - m_editor->showDrawingCursor(); } void updateStatusBar(const char* text) override { diff --git a/src/app/ui/editor/zooming_state.cpp b/src/app/ui/editor/zooming_state.cpp index 300fb6386..1a09852c3 100644 --- a/src/app/ui/editor/zooming_state.cpp +++ b/src/app/ui/editor/zooming_state.cpp @@ -78,10 +78,9 @@ bool ZoomingState::onMouseMove(Editor* editor, MouseMessage* msg) return true; } -bool ZoomingState::onSetCursor(Editor* editor) +bool ZoomingState::onSetCursor(Editor* editor, const gfx::Point& mouseScreenPos) { - editor->hideDrawingCursor(); - ui::set_mouse_cursor(kMagnifierCursor); + editor->showMouseCursor(kMagnifierCursor); return true; } diff --git a/src/app/ui/editor/zooming_state.h b/src/app/ui/editor/zooming_state.h index c44d7ca98..8de90fe69 100644 --- a/src/app/ui/editor/zooming_state.h +++ b/src/app/ui/editor/zooming_state.h @@ -23,7 +23,7 @@ namespace app { virtual bool onMouseDown(Editor* editor, ui::MouseMessage* msg) override; virtual bool onMouseUp(Editor* editor, ui::MouseMessage* msg) override; virtual bool onMouseMove(Editor* editor, ui::MouseMessage* msg) override; - virtual bool onSetCursor(Editor* editor) override; + virtual bool onSetCursor(Editor* editor, const gfx::Point& mouseScreenPos) override; virtual bool onKeyDown(Editor* editor, ui::KeyMessage* msg) override; virtual bool onKeyUp(Editor* editor, ui::KeyMessage* msg) override; virtual bool onUpdateStatusBar(Editor* editor) override;