aseprite/src/app/transformation.cpp
Gaspar Capello f7b6674680 Fix Marqueed selection gets some rows duplicated when moved left and right over left canvas edge with Ctrl still pressed #2891
Prior to this fix, moving a marquee selection while holding down the Ctrl key outside the left/top area of the canvas results in a 1px distortion of the marquee image. This distortion is due to a false transformation when it is required to get the corner bounds, due to rounding the float coordinates towards 0 instead of the nearest left/top integer. The discrepancy occurs within the `getDraggedImageCopy`, `redrawExtraImage`, `drawImage` and `drawMask` functions.
2022-07-14 14:41:01 -03:00

96 lines
2.3 KiB
C++

// Aseprite
// Copyright (C) 2020-2022 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "app/transformation.h"
#include "gfx/point.h"
#include "gfx/size.h"
#include <cmath>
namespace app {
using namespace gfx;
Transformation::Transformation()
{
}
Transformation::Transformation(const RectF& bounds, double cornerThick)
: m_bounds(bounds)
, m_cornerThick(cornerThick)
{
m_pivot.x = bounds.x + bounds.w/2;
m_pivot.y = bounds.y + bounds.h/2;
}
Transformation::Corners Transformation::transformedCorners() const
{
Corners corners(m_bounds);
// TODO We could create a composed 4x4 matrix with all
// transformation and apply the same matrix to avoid calling
// rotatePoint/cos/sin functions 4 times, anyway, it's not
// critical at this point.
for (std::size_t c=0; c<corners.size(); ++c)
corners[c] = Transformation::rotatePoint(corners[c], m_pivot,
m_angle, m_skew);
return corners;
}
void Transformation::displacePivotTo(const PointF& newPivot)
{
// Calculate the rotated corners
Corners corners = transformedCorners();
// Rotate-back the position of the rotated origin (corners[0]) using
// the new pivot.
PointF pt = corners.leftTop();
pt = rotatePoint(pt, newPivot, -m_angle, 0.0);
pt = rotatePoint(pt, newPivot, 0.0, -m_skew);
// Change the new pivot.
m_pivot = newPivot;
m_bounds = RectF(pt, m_bounds.size());
}
PointF Transformation::rotatePoint(
const PointF& point,
const PointF& pivot,
const double angle,
const double skew)
{
double cos = std::cos(-angle);
double sin = std::sin(-angle);
double tan = std::tan(skew);
double dx = point.x - pivot.x;
double dy = point.y - pivot.y;
dx += dy*tan;
return PointF(pivot.x + dx*cos - dy*sin,
pivot.y + dx*sin + dy*cos);
}
RectF Transformation::transformedBounds() const
{
// Get transformed corners
Corners corners = transformedCorners();
// Create a union of all corners
RectF bounds;
for (int i=0; i<Corners::NUM_OF_CORNERS; ++i)
bounds = bounds.createUnion(RectF(corners[i].x, corners[i].y, m_cornerThick, m_cornerThick));
return bounds.floor();
}
} // namespace app