mirror of
https://github.com/aseprite/aseprite.git
synced 2025-02-06 12:39:57 +00:00
Draw mask boundaries with a gfx::Path
The path is also cached so on each re-paint we can re-use it while it's still valid.
This commit is contained in:
parent
9801010a2b
commit
3d2013b33c
2
laf
2
laf
@ -1 +1 @@
|
||||
Subproject commit 207de6662911e9471c01681fb16912b756646f07
|
||||
Subproject commit 8032d186a751326d0fc6436d69570ad4a3c4aaf1
|
@ -304,8 +304,8 @@ void Doc::generateMaskBoundaries(const Mask* mask)
|
||||
ASSERT(mask);
|
||||
|
||||
if (!mask->isEmpty()) {
|
||||
m_maskBoundaries.reset(new MaskBoundaries(mask->bitmap()));
|
||||
m_maskBoundaries->offset(mask->bounds().x,
|
||||
m_maskBoundaries.regen(mask->bitmap());
|
||||
m_maskBoundaries.offset(mask->bounds().x,
|
||||
mask->bounds().y);
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2018-2019 Igara Studio S.A.
|
||||
// Copyright (C) 2018-2020 Igara Studio S.A.
|
||||
// Copyright (C) 2001-2018 David Capello
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
@ -20,6 +20,7 @@
|
||||
#include "doc/color.h"
|
||||
#include "doc/document.h"
|
||||
#include "doc/frame.h"
|
||||
#include "doc/mask_boundaries.h"
|
||||
#include "doc/pixel_format.h"
|
||||
#include "gfx/rect.h"
|
||||
#include "obs/observable.h"
|
||||
@ -32,7 +33,6 @@ namespace doc {
|
||||
class Cel;
|
||||
class Layer;
|
||||
class Mask;
|
||||
class MaskBoundaries;
|
||||
class Sprite;
|
||||
}
|
||||
|
||||
@ -143,8 +143,16 @@ namespace app {
|
||||
void destroyMaskBoundaries();
|
||||
void generateMaskBoundaries(const Mask* mask = nullptr);
|
||||
|
||||
const MaskBoundaries* getMaskBoundaries() const {
|
||||
return m_maskBoundaries.get();
|
||||
const MaskBoundaries& maskBoundaries() const {
|
||||
return m_maskBoundaries;
|
||||
}
|
||||
|
||||
MaskBoundaries& maskBoundaries() {
|
||||
return m_maskBoundaries;
|
||||
}
|
||||
|
||||
bool hasMaskBoundaries() const {
|
||||
return !m_maskBoundaries.isEmpty();
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
@ -217,7 +225,7 @@ namespace app {
|
||||
Transaction* m_transaction;
|
||||
|
||||
// Selected mask region boundaries
|
||||
std::unique_ptr<doc::MaskBoundaries> m_maskBoundaries;
|
||||
doc::MaskBoundaries m_maskBoundaries;
|
||||
|
||||
// Data to save the file in the same format that it was loaded
|
||||
FormatOptionsPtr m_format_options;
|
||||
|
@ -371,7 +371,7 @@ void BrushPreview::generateBoundaries()
|
||||
{
|
||||
BrushRef brush = getCurrentBrush();
|
||||
|
||||
if (m_brushBoundaries &&
|
||||
if (!m_brushBoundaries.isEmpty() &&
|
||||
m_brushGen == brush->gen())
|
||||
return;
|
||||
|
||||
@ -398,11 +398,9 @@ void BrushPreview::generateBoundaries()
|
||||
mask = brush->maskBitmap();
|
||||
}
|
||||
|
||||
m_brushBoundaries.reset(
|
||||
new MaskBoundaries(mask ? mask: brushImage));
|
||||
|
||||
m_brushBoundaries.regen(mask ? mask: brushImage);
|
||||
if (!isOnePixel)
|
||||
m_brushBoundaries->offset(-brush->center().x,
|
||||
m_brushBoundaries.offset(-brush->center().x,
|
||||
-brush->center().y);
|
||||
|
||||
if (deleteMask)
|
||||
@ -512,7 +510,7 @@ void BrushPreview::traceBrushBoundaries(ui::Graphics* g,
|
||||
gfx::Color color,
|
||||
PixelDelegate pixelDelegate)
|
||||
{
|
||||
for (const auto& seg : *m_brushBoundaries) {
|
||||
for (const auto& seg : m_brushBoundaries) {
|
||||
gfx::Rect bounds = seg.bounds();
|
||||
bounds.offset(pos);
|
||||
bounds = m_editor->editorToScreen(bounds);
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2019 Igara Studio S.A.
|
||||
// Copyright (C) 2019-2020 Igara Studio S.A.
|
||||
// Copyright (C) 2001-2016 David Capello
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
@ -93,7 +93,7 @@ namespace app {
|
||||
gfx::Point m_editorPosition; // Position in the editor (model)
|
||||
|
||||
// Information about current brush
|
||||
std::shared_ptr<doc::MaskBoundaries> m_brushBoundaries;
|
||||
doc::MaskBoundaries m_brushBoundaries;
|
||||
int m_brushGen;
|
||||
int m_brushWidth;
|
||||
int m_brushHeight;
|
||||
|
@ -894,7 +894,7 @@ void Editor::drawSpriteUnclippedRect(ui::Graphics* g, const gfx::Rect& _rc)
|
||||
}
|
||||
|
||||
// Draw the mask
|
||||
if (m_document->getMaskBoundaries())
|
||||
if (m_document->hasMaskBoundaries())
|
||||
drawMask(g);
|
||||
|
||||
// Post-render decorator.
|
||||
@ -934,34 +934,26 @@ void Editor::drawMask(Graphics* g)
|
||||
!m_docPref.show.selectionEdges())
|
||||
return;
|
||||
|
||||
ASSERT(m_document->getMaskBoundaries());
|
||||
ASSERT(m_document->hasMaskBoundaries());
|
||||
|
||||
gfx::Point pt = mainTilePosition();
|
||||
pt.x = m_padding.x + m_proj.applyX(pt.x);
|
||||
pt.y = m_padding.y + m_proj.applyY(pt.y);
|
||||
|
||||
for (const auto& seg : *m_document->getMaskBoundaries()) {
|
||||
// Create the mask boundaries path
|
||||
auto& segs = m_document->maskBoundaries();
|
||||
segs.createPathIfNeeeded();
|
||||
|
||||
CheckedDrawMode checked(g, m_antsOffset,
|
||||
gfx::rgba(0, 0, 0, 255),
|
||||
gfx::rgba(255, 255, 255, 255));
|
||||
gfx::Rect bounds = m_proj.apply(seg.bounds());
|
||||
|
||||
if (m_proj.scaleX() >= 1.0) {
|
||||
if (!seg.open() && seg.vertical())
|
||||
--bounds.x;
|
||||
}
|
||||
|
||||
if (m_proj.scaleY() >= 1.0) {
|
||||
if (!seg.open() && !seg.vertical())
|
||||
--bounds.y;
|
||||
}
|
||||
|
||||
// The color doesn't matter, we are using CheckedDrawMode
|
||||
if (seg.vertical())
|
||||
g->drawVLine(gfx::rgba(0, 0, 0), pt.x+bounds.x, pt.y+bounds.y, bounds.h);
|
||||
else
|
||||
g->drawHLine(gfx::rgba(0, 0, 0), pt.x+bounds.x, pt.y+bounds.y, bounds.w);
|
||||
}
|
||||
os::Paint paint;
|
||||
paint.style(os::Paint::Stroke);
|
||||
paint.color(gfx::rgba(0, 0, 0));
|
||||
g->setMatrix(Matrix::MakeTrans(pt.x, pt.y));
|
||||
g->concat(m_proj.scaleMatrix());
|
||||
g->drawPath(segs.path(), paint);
|
||||
g->resetMatrix();
|
||||
}
|
||||
|
||||
void Editor::drawMaskSafe()
|
||||
@ -971,7 +963,7 @@ void Editor::drawMaskSafe()
|
||||
|
||||
if (isVisible() &&
|
||||
m_document &&
|
||||
m_document->getMaskBoundaries()) {
|
||||
m_document->hasMaskBoundaries()) {
|
||||
Region region;
|
||||
getDrawableRegion(region, kCutTopWindows);
|
||||
region.offset(-bounds().origin());
|
||||
@ -2047,7 +2039,7 @@ void Editor::onPaint(ui::PaintEvent& ev)
|
||||
#endif // ENABLE_DEVMODE
|
||||
|
||||
// Draw the mask boundaries
|
||||
if (m_document->getMaskBoundaries()) {
|
||||
if (m_document->hasMaskBoundaries()) {
|
||||
drawMask(g);
|
||||
m_antsTimer.start();
|
||||
}
|
||||
|
@ -105,10 +105,10 @@ bool MovingSelectionState::onMouseMove(Editor* editor, MouseMessage* msg)
|
||||
editor->document()->mask()->setOrigin(newMaskOrigin.x,
|
||||
newMaskOrigin.y);
|
||||
|
||||
if (MaskBoundaries* boundaries =
|
||||
const_cast<MaskBoundaries*>(editor->document()->getMaskBoundaries())) {
|
||||
if (editor->document()->hasMaskBoundaries()) {
|
||||
MaskBoundaries& boundaries = editor->document()->maskBoundaries();
|
||||
const gfx::Point boundariesDelta = newMaskOrigin - oldMaskOrigin;
|
||||
boundaries->offset(boundariesDelta.x,
|
||||
boundaries.offset(boundariesDelta.x,
|
||||
boundariesDelta.y);
|
||||
}
|
||||
else {
|
||||
|
@ -832,13 +832,13 @@ bool StandbyState::overSelectionEdges(Editor* editor,
|
||||
if (Preferences::instance().selection.moveEdges() &&
|
||||
editor->isActive() &&
|
||||
editor->document()->isMaskVisible() &&
|
||||
editor->document()->getMaskBoundaries() &&
|
||||
editor->document()->hasMaskBoundaries() &&
|
||||
// TODO improve this check, how we can know that we aren't in the MovingPixelsState
|
||||
!dynamic_cast<MovingPixelsState*>(editor->getState().get())) {
|
||||
gfx::Point mainOffset(editor->mainTilePosition());
|
||||
|
||||
// For each selection edge
|
||||
for (const auto& seg : *editor->document()->getMaskBoundaries()) {
|
||||
for (const auto& seg : editor->document()->maskBoundaries()) {
|
||||
gfx::Rect segBounds = seg.bounds();
|
||||
segBounds.offset(mainOffset);
|
||||
segBounds = editor->editorToScreen(segBounds);
|
||||
|
@ -14,8 +14,17 @@
|
||||
|
||||
namespace doc {
|
||||
|
||||
MaskBoundaries::MaskBoundaries(const Image* bitmap)
|
||||
void MaskBoundaries::reset()
|
||||
{
|
||||
m_segs.clear();
|
||||
if (!m_path.isEmpty())
|
||||
m_path.rewind();
|
||||
}
|
||||
|
||||
void MaskBoundaries::regen(const Image* bitmap)
|
||||
{
|
||||
reset();
|
||||
|
||||
int x, y, w = bitmap->width(), h = bitmap->height();
|
||||
|
||||
const LockImageBits<BitmapTraits> bits(bitmap);
|
||||
@ -334,6 +343,24 @@ void MaskBoundaries::offset(int x, int y)
|
||||
{
|
||||
for (Segment& seg : m_segs)
|
||||
seg.offset(x, y);
|
||||
|
||||
m_path.offset(x, y);
|
||||
}
|
||||
|
||||
void MaskBoundaries::createPathIfNeeeded()
|
||||
{
|
||||
if (!m_path.isEmpty())
|
||||
return;
|
||||
|
||||
for (const auto& seg : m_segs) {
|
||||
gfx::Rect rc = seg.bounds();
|
||||
m_path.moveTo(rc.x, rc.y);
|
||||
|
||||
if (seg.vertical())
|
||||
m_path.lineTo(rc.x, rc.y2());
|
||||
else
|
||||
m_path.lineTo(rc.x2(), rc.y);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace doc
|
||||
|
@ -1,4 +1,5 @@
|
||||
// Aseprite Document Library
|
||||
// Copyright (c) 2020 Igara Studio S.A.
|
||||
// Copyright (c) 2001-2015 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
@ -8,6 +9,7 @@
|
||||
#define DOC_MASK_BOUNDARIES_H_INCLUDED
|
||||
#pragma once
|
||||
|
||||
#include "gfx/path.h"
|
||||
#include "gfx/rect.h"
|
||||
|
||||
#include <vector>
|
||||
@ -41,7 +43,9 @@ namespace doc {
|
||||
typedef list_type::iterator iterator;
|
||||
typedef list_type::const_iterator const_iterator;
|
||||
|
||||
MaskBoundaries(const Image* bitmap);
|
||||
bool isEmpty() const { return m_segs.empty(); }
|
||||
void reset();
|
||||
void regen(const Image* bitmap);
|
||||
|
||||
const_iterator begin() const { return m_segs.begin(); }
|
||||
const_iterator end() const { return m_segs.end(); }
|
||||
@ -49,9 +53,13 @@ namespace doc {
|
||||
iterator end() { return m_segs.end(); }
|
||||
|
||||
void offset(int x, int y);
|
||||
gfx::Path& path() { return m_path; }
|
||||
|
||||
void createPathIfNeeeded();
|
||||
|
||||
private:
|
||||
list_type m_segs;
|
||||
gfx::Path m_path;
|
||||
};
|
||||
|
||||
} // namespace doc
|
||||
|
@ -1,4 +1,5 @@
|
||||
// Aseprite Render Library
|
||||
// Copyright (c) 2020 Igara Studio S.A.
|
||||
// Copyright (c) 2016 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
@ -9,6 +10,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "doc/pixel_ratio.h"
|
||||
#include "gfx/matrix.h"
|
||||
#include "render/zoom.h"
|
||||
|
||||
namespace render {
|
||||
@ -79,6 +81,10 @@ namespace render {
|
||||
removeY(r.y+r.h) - v);
|
||||
}
|
||||
|
||||
gfx::Matrix scaleMatrix() const {
|
||||
return gfx::Matrix::MakeScale(scaleX(), scaleY());
|
||||
}
|
||||
|
||||
private:
|
||||
doc::PixelRatio m_pixelRatio;
|
||||
Zoom m_zoom;
|
||||
|
@ -13,6 +13,8 @@
|
||||
|
||||
#include "base/string.h"
|
||||
#include "gfx/clip.h"
|
||||
#include "gfx/matrix.h"
|
||||
#include "gfx/path.h"
|
||||
#include "gfx/point.h"
|
||||
#include "gfx/rect.h"
|
||||
#include "gfx/region.h"
|
||||
@ -81,6 +83,36 @@ bool Graphics::clipRect(const gfx::Rect& rc)
|
||||
return m_surface->clipRect(gfx::Rect(rc).offset(m_dx, m_dy));
|
||||
}
|
||||
|
||||
void Graphics::save()
|
||||
{
|
||||
m_surface->save();
|
||||
}
|
||||
|
||||
void Graphics::concat(const gfx::Matrix& matrix)
|
||||
{
|
||||
m_surface->concat(matrix);
|
||||
}
|
||||
|
||||
void Graphics::setMatrix(const gfx::Matrix& matrix)
|
||||
{
|
||||
m_surface->setMatrix(matrix);
|
||||
}
|
||||
|
||||
void Graphics::resetMatrix()
|
||||
{
|
||||
m_surface->resetMatrix();
|
||||
}
|
||||
|
||||
void Graphics::restore()
|
||||
{
|
||||
m_surface->restore();
|
||||
}
|
||||
|
||||
gfx::Matrix Graphics::matrix() const
|
||||
{
|
||||
return m_surface->matrix();
|
||||
}
|
||||
|
||||
void Graphics::setDrawMode(DrawMode mode, int param,
|
||||
const gfx::Color a,
|
||||
const gfx::Color b)
|
||||
@ -144,6 +176,21 @@ void Graphics::drawLine(gfx::Color color, const gfx::Point& _a, const gfx::Point
|
||||
m_surface->drawLine(a, b, paint);
|
||||
}
|
||||
|
||||
void Graphics::drawPath(gfx::Path& path, const Paint& paint)
|
||||
{
|
||||
os::SurfaceLock lock(m_surface);
|
||||
|
||||
auto m = matrix();
|
||||
save();
|
||||
setMatrix(gfx::Matrix::MakeTrans(m_dx, m_dy));
|
||||
concat(m);
|
||||
|
||||
m_surface->drawPath(path, paint);
|
||||
|
||||
dirty(matrix().mapRect(path.bounds()).inflate(1, 1));
|
||||
restore();
|
||||
}
|
||||
|
||||
void Graphics::drawRect(gfx::Color color, const gfx::Rect& rcOrig)
|
||||
{
|
||||
gfx::Rect rc(rcOrig);
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Aseprite UI Library
|
||||
// Copyright (C) 2019 Igara Studio S.A.
|
||||
// Copyright (C) 2019-2020 Igara Studio S.A.
|
||||
// Copyright (C) 2001-2018 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
@ -21,6 +21,8 @@
|
||||
#include <string>
|
||||
|
||||
namespace gfx {
|
||||
class Matrix;
|
||||
class Path;
|
||||
class Region;
|
||||
}
|
||||
|
||||
@ -58,6 +60,13 @@ namespace ui {
|
||||
void restoreClip();
|
||||
bool clipRect(const gfx::Rect& rc);
|
||||
|
||||
void save();
|
||||
void concat(const gfx::Matrix& matrix);
|
||||
void setMatrix(const gfx::Matrix& matrix);
|
||||
void resetMatrix();
|
||||
void restore();
|
||||
gfx::Matrix matrix() const;
|
||||
|
||||
void setDrawMode(DrawMode mode, int param = 0,
|
||||
const gfx::Color a = gfx::ColorNone,
|
||||
const gfx::Color b = gfx::ColorNone);
|
||||
@ -68,6 +77,7 @@ namespace ui {
|
||||
void drawHLine(gfx::Color color, int x, int y, int w);
|
||||
void drawVLine(gfx::Color color, int x, int y, int h);
|
||||
void drawLine(gfx::Color color, const gfx::Point& a, const gfx::Point& b);
|
||||
void drawPath(gfx::Path& path, const Paint& paint);
|
||||
|
||||
void drawRect(gfx::Color color, const gfx::Rect& rc);
|
||||
void fillRect(gfx::Color color, const gfx::Rect& rc);
|
||||
|
Loading…
x
Reference in New Issue
Block a user