Add BrushPreview class to wrap all code related to brush preview/editor cursor

This commit is contained in:
David Capello 2015-06-25 12:44:47 -03:00
parent ac82dcdd49
commit f571e4ceb2
25 changed files with 727 additions and 765 deletions

View File

@ -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

View File

@ -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");

View File

@ -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<gfx::Color> 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<LayerImage*>(layer)->opacity(), t);
document->prepareExtraCel(brushBounds, opacity);
document->setExtraCelType(render::ExtraType::NONE);
document->setExtraCelBlendMode(
(layer ? static_cast<LayerImage*>(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<BitmapTraits> bits(mask.get());
auto pos = bits.begin();
for (int v=0; v<h; ++v) {
for (int u=0; u<w; ++u) {
*pos = get_pixel(brushImage, u, v);
++pos;
}
}
}
m_brushBoundaries.reset(
new MaskBoundaries(
(mask ? mask.get(): brushImage)));
}
void BrushPreview::forEachBrushPixel(
ui::Graphics* g,
const gfx::Point& screenPos,
const gfx::Point& spritePos,
gfx::Color color,
PixelDelegate pixelDelegate)
{
m_savedPixelsIterator = 0;
if (m_type & CROSS)
traceCrossPixels(g, screenPos, color, pixelDelegate);
if (m_type & SELECTION_CROSS)
traceSelectionCrossPixels(g, spritePos, color, 1, pixelDelegate);
if (m_type & BRUSH_BOUNDARIES)
traceBrushBoundaries(g, spritePos, color, pixelDelegate);
// Depending on the editor zoom, maybe wee need subpixel movement (a
// little dot inside the active pixel)
if (m_editor->zoom().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<bounds.y+bounds.h; ++pt.y)
(this->*pixelDelegate)(g, pt, color);
}
else {
for (; pt.x<bounds.x+bounds.w; ++pt.x)
(this->*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

View File

@ -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 <vector>
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<doc::MaskBoundaries> m_brushBoundaries;
int m_brushGen;
int m_brushWidth;
int m_brushHeight;
static std::vector<gfx::Color> 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

View File

@ -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 <algorithm>
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<gfx::Color> 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<void>(&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<LayerImage*>(m_layer)->opacity(), t);
m_document->prepareExtraCel(brushBounds, opacity);
m_document->setExtraCelType(render::ExtraType::NONE);
m_document->setExtraCelBlendMode(
(m_layer ? static_cast<LayerImage*>(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<BitmapTraits> bits(mask.get());
auto pos = bits.begin();
for (int v=0; v<h; ++v) {
for (int u=0; u<w; ++u) {
*pos = get_pixel(brush_image, u, v);
++pos;
}
}
}
delete cursor_bound.boundaries;
cursor_bound.boundaries = new MaskBoundaries(
(mask ? mask.get(): brush_image));
}
void Editor::forEachBrushPixel(
ui::Graphics* g,
const gfx::Point& screenPos,
const gfx::Point& spritePos,
gfx::Color color,
Editor::PixelDelegate pixelDelegate)
{
saved_pixel_n = 0;
if (cursor_type & CURSOR_THINCROSS)
trace_thincross_pixels(g, this, screenPos, color, pixelDelegate);
if (cursor_type & CURSOR_THICKCROSS)
trace_thickcross_pixels(g, this, spritePos, color, 1, pixelDelegate);
if (cursor_type & CURSOR_BRUSHBOUNDS)
trace_brush_bounds(g, this, spritePos, color, pixelDelegate);
if (IS_SUBPIXEL(this)) {
pixelDelegate(g, screenPos, color);
}
}
//////////////////////////////////////////////////////////////////////
// New Thin Cross
static void trace_thincross_pixels(ui::Graphics* g, Editor* editor,
const gfx::Point& pt, gfx::Color color, Editor::PixelDelegate pixelDelegate)
{
static int cursor_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 (cursor_cross[v*7+u]) {
out.x = pt.x-3+u;
out.y = pt.y-3+v;
pixelDelegate(g, out, color);
}
}
}
}
//////////////////////////////////////////////////////////////////////
// Old Thick Cross
static void trace_thickcross_pixels(ui::Graphics* g, Editor* editor,
const gfx::Point& pt, gfx::Color color, int thickness, Editor::PixelDelegate pixelDelegate)
{
static int cursor_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 = editor->editorToScreen(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

View File

@ -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;
}

View File

@ -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;

View File

@ -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<MouseMessage*>(msg));
MouseMessage* mouseMsg = static_cast<MouseMessage*>(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<MouseMessage*>(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<SkinTheme*>(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()
{

View File

@ -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).

View File

@ -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; }

View File

@ -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)

View File

@ -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;

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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

View File

@ -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;
}

View File

@ -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;

View File

@ -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)

View File

@ -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;

View File

@ -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;
}

View File

@ -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;

View File

@ -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;
}

View File

@ -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 {

View File

@ -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;
}

View File

@ -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;