mirror of https://github.com/aseprite/aseprite.git
				
				
				
			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,9 +304,9 @@ void Doc::generateMaskBoundaries(const Mask* mask) | |||
|   ASSERT(mask); | ||||
| 
 | ||||
|   if (!mask->isEmpty()) { | ||||
|     m_maskBoundaries.reset(new MaskBoundaries(mask->bitmap())); | ||||
|     m_maskBoundaries->offset(mask->bounds().x, | ||||
|                              mask->bounds().y); | ||||
|     m_maskBoundaries.regen(mask->bitmap()); | ||||
|     m_maskBoundaries.offset(mask->bounds().x, | ||||
|                             mask->bounds().y); | ||||
|   } | ||||
| 
 | ||||
|   notifySelectionBoundariesChanged(); | ||||
|  |  | |||
|  | @ -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,12 +398,10 @@ 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, | ||||
|                               -brush->center().y); | ||||
|     m_brushBoundaries.offset(-brush->center().x, | ||||
|                              -brush->center().y); | ||||
| 
 | ||||
|   if (deleteMask) | ||||
|     delete mask; | ||||
|  | @ -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()) { | ||||
|     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()); | ||||
|   // Create the mask boundaries path
 | ||||
|   auto& segs = m_document->maskBoundaries(); | ||||
|   segs.createPathIfNeeeded(); | ||||
| 
 | ||||
|     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); | ||||
|   } | ||||
|   CheckedDrawMode checked(g, m_antsOffset, | ||||
|                           gfx::rgba(0, 0, 0, 255), | ||||
|                           gfx::rgba(255, 255, 255, 255)); | ||||
|   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,11 +105,11 @@ 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, | ||||
|                          boundariesDelta.y); | ||||
|       boundaries.offset(boundariesDelta.x, | ||||
|                         boundariesDelta.y); | ||||
|     } | ||||
|     else { | ||||
|       ASSERT(false); | ||||
|  |  | |||
|  | @ -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…
	
		Reference in New Issue