1
0
mirror of https://github.com/aseprite/aseprite.git synced 2025-02-23 18:39:55 +00:00
aseprite/src/tools/tool_loop_manager.cpp
David Capello 5380877c4a Refactor Editor class to use a state design pattern.
+ Added EditorState and derived classes (StandByState, DrawingState, etc.)
+ Added StatusBarListener.
2011-04-10 20:15:17 -03:00

245 lines
7.5 KiB
C++

/* ASE - Allegro Sprite Editor
* Copyright (C) 2001-2011 David Capello
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "config.h"
#include "tools/tool_loop_manager.h"
#include "context.h"
#include "raster/image.h"
#include "tools/controller.h"
#include "tools/ink.h"
#include "tools/intertwine.h"
#include "tools/point_shape.h"
#include "tools/tool_loop.h"
#include "util/render.h"
using namespace gfx;
using namespace tools;
ToolLoopManager::ToolLoopManager(ToolLoop* toolLoop)
: m_toolLoop(toolLoop)
{
}
ToolLoopManager::~ToolLoopManager()
{
}
bool ToolLoopManager::isCanceled() const
{
return m_toolLoop->isCanceled();
}
void ToolLoopManager::prepareLoop(const Pointer& pointer)
{
// Start with no points at all
m_points.clear();
// Prepare the image where we will draw on
image_copy(m_toolLoop->getDstImage(),
m_toolLoop->getSrcImage(), 0, 0);
// Prepare the ink
m_toolLoop->getInk()->prepareInk(m_toolLoop);
// Prepare preview image (the destination image will be our preview
// in the tool-loop time, so we can see what we are drawing)
RenderEngine::setPreviewImage(m_toolLoop->getLayer(),
m_toolLoop->getDstImage());
}
void ToolLoopManager::releaseLoop(const Pointer& pointer)
{
// No more preview image
RenderEngine::setPreviewImage(NULL, NULL);
}
void ToolLoopManager::pressButton(const Pointer& pointer)
{
// If the user pressed the other mouse button...
if ((m_toolLoop->getMouseButton() == 0 && pointer.getButton() == Pointer::Right) ||
(m_toolLoop->getMouseButton() == 1 && pointer.getButton() == Pointer::Left)) {
// Cancel the tool-loop (the destination image should be completelly discarded)
m_toolLoop->cancel();
return;
}
// Convert the screen point to a sprite point
Point spritePoint = m_toolLoop->screenToSprite(Point(pointer.getX(), pointer.getY()));
m_toolLoop->setSpeed(Point(0, 0));
m_oldPoint = spritePoint;
snapToGrid(true, spritePoint);
m_toolLoop->getController()->pressButton(m_points, spritePoint);
std::string statusText;
m_toolLoop->getController()->getStatusBarText(m_points, statusText);
m_toolLoop->updateStatusBar(statusText.c_str());
doLoopStep(false);
}
bool ToolLoopManager::releaseButton(const Pointer& pointer)
{
Point spritePoint = m_toolLoop->screenToSprite(Point(pointer.getX(), pointer.getY()));
snapToGrid(true, spritePoint);
bool res = m_toolLoop->getController()->releaseButton(m_points, spritePoint);
if (!res && (m_toolLoop->getInk()->isSelection() || m_toolLoop->getFilled())) {
m_toolLoop->getInk()->setFinalStep(m_toolLoop, true);
doLoopStep(true);
m_toolLoop->getInk()->setFinalStep(m_toolLoop, false);
}
return res;
}
void ToolLoopManager::movement(const Pointer& pointer)
{
// Convert the screen point to a sprite point
Point spritePoint = m_toolLoop->screenToSprite(Point(pointer.getX(), pointer.getY()));
// Calculate the speed (new sprite point - old sprite point)
m_toolLoop->setSpeed(spritePoint - m_oldPoint);
m_oldPoint = spritePoint;
snapToGrid(true, spritePoint);
m_toolLoop->getController()->movement(m_toolLoop, m_points, spritePoint);
std::string statusText;
m_toolLoop->getController()->getStatusBarText(m_points, statusText);
m_toolLoop->updateStatusBar(statusText.c_str());
doLoopStep(false);
}
void ToolLoopManager::doLoopStep(bool last_step)
{
static Rect old_dirty_area; // TODO Not thread safe
Points points_to_interwine;
if (!last_step)
m_toolLoop->getController()->getPointsToInterwine(m_points, points_to_interwine);
else
points_to_interwine = m_points;
Point offset(m_toolLoop->getOffset());
for (size_t i=0; i<points_to_interwine.size(); ++i)
points_to_interwine[i] += offset;
switch (m_toolLoop->getTracePolicy()) {
case TracePolicyAccumulate:
// Do nothing. We accumulate traces in the destination image.
break;
case TracePolicyLast:
// Copy source to destination (reset the previous trace). Useful
// for tools like Line and Ellipse tools (we kept the last trace only).
image_clear(m_toolLoop->getDstImage(), 0);
image_copy(m_toolLoop->getDstImage(), m_toolLoop->getSrcImage(), 0, 0);
break;
case TracePolicyOverlap:
// Copy destination to source (yes, destination to source). In
// this way each new trace overlaps the previous one.
image_copy(m_toolLoop->getSrcImage(), m_toolLoop->getDstImage(), 0, 0);
break;
}
// Get the modified area in the sprite with this intertwined set of points
if (!m_toolLoop->getFilled() || (!last_step && !m_toolLoop->getPreviewFilled()))
m_toolLoop->getIntertwine()->joinPoints(m_toolLoop, points_to_interwine);
else
m_toolLoop->getIntertwine()->fillPoints(m_toolLoop, points_to_interwine);
// Calculat the area to be updated in the screen/editor/sprite
Rect dirty_area;
calculateDirtyArea(m_toolLoop, points_to_interwine, dirty_area);
Rect new_dirty_area;
if (m_toolLoop->getTracePolicy() == TracePolicyLast) {
new_dirty_area = old_dirty_area.createUnion(dirty_area);
old_dirty_area = dirty_area;
}
else {
new_dirty_area = dirty_area;
}
if (!new_dirty_area.isEmpty())
m_toolLoop->updateArea(new_dirty_area);
}
// Applies the grid settings to the specified sprite point, if
// "flexible" is true this function will try to snap the point
// to one of the four corners of each grid-tile, if "flexible"
// is false, only the origin of each grid-tile will be used
// to snap the point
void ToolLoopManager::snapToGrid(bool flexible, Point& point)
{
if (!m_toolLoop->getController()->canSnapToGrid() ||
!m_toolLoop->getContext()->getSettings()->getSnapToGrid())
return;
Rect grid(m_toolLoop->getContext()->getSettings()->getGridBounds());
register int w = grid.w;
register int h = grid.h;
div_t d, dx, dy;
flexible = flexible ? 1: 0;
dx = div(grid.x, w);
dy = div(grid.y, h);
d = div(point.x-dx.rem, w);
point.x = dx.rem + d.quot*w + ((d.rem > w/2)? w-flexible: 0);
d = div(point.y-dy.rem, h);
point.y = dy.rem + d.quot*h + ((d.rem > h/2)? h-flexible: 0);
}
void ToolLoopManager::calculateDirtyArea(ToolLoop* loop, const Points& points, Rect& dirty_area)
{
Point minpt, maxpt;
calculateMinMax(points, minpt, maxpt);
// Expand the dirty-area with the pen width
Rect r1, r2;
loop->getPointShape()->getModifiedArea(loop, minpt.x, minpt.y, r1);
loop->getPointShape()->getModifiedArea(loop, maxpt.x, maxpt.y, r2);
dirty_area = r1.createUnion(r2);
}
void ToolLoopManager::calculateMinMax(const Points& points, Point& minpt, Point& maxpt)
{
ASSERT(points.size() > 0);
minpt.x = points[0].x;
minpt.y = points[0].y;
maxpt.x = points[0].x;
maxpt.y = points[0].y;
for (size_t c=1; c<points.size(); ++c) {
minpt.x = MIN(minpt.x, points[c].x);
minpt.y = MIN(minpt.y, points[c].y);
maxpt.x = MAX(maxpt.x, points[c].x);
maxpt.y = MAX(maxpt.y, points[c].y);
}
}