mirror of
https://github.com/aseprite/aseprite.git
synced 2025-01-02 11:59:58 +00:00
Add gradient tool (fix #418)
This commit is contained in:
parent
212c3e5126
commit
97bfaa21ec
@ -449,6 +449,7 @@
|
||||
<key tool="zoom" shortcut="Z" />
|
||||
|
||||
<key tool="paint_bucket" shortcut="G" />
|
||||
<key tool="gradient" shortcut="G" />
|
||||
|
||||
<key tool="line" shortcut="L" />
|
||||
<key tool="curve" shortcut="Shift+L" />
|
||||
@ -1144,6 +1145,14 @@
|
||||
pointshape="floodfill"
|
||||
tracepolicy="accumulate"
|
||||
/>
|
||||
<tool id="gradient"
|
||||
text="Gradient Tool"
|
||||
ink="gradient"
|
||||
controller="two_points"
|
||||
pointshape="floodfill"
|
||||
intertwine="first_point"
|
||||
tracepolicy="last"
|
||||
/>
|
||||
</group>
|
||||
|
||||
<group id="perfect_traces" text="Perfect Traces">
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 14 KiB |
@ -376,6 +376,7 @@
|
||||
<part id="tool_zoom" x="208" y="32" w="16" h="16" />
|
||||
<part id="tool_slice" x="224" y="32" w="16" h="16" />
|
||||
<part id="tool_paint_bucket" x="144" y="48" w="16" h="16" />
|
||||
<part id="tool_gradient" x="160" y="48" w="16" h="16" />
|
||||
<part id="tool_line" x="144" y="64" w="16" h="16" />
|
||||
<part id="tool_curve" x="160" y="64" w="16" h="16" />
|
||||
<part id="tool_rectangle" x="144" y="80" w="16" h="16" />
|
||||
|
@ -8,6 +8,8 @@
|
||||
#define APP_TOOLS_INK_H_INCLUDED
|
||||
#pragma once
|
||||
|
||||
#include "app/tools/stroke.h"
|
||||
|
||||
namespace gfx {
|
||||
class Region;
|
||||
}
|
||||
@ -22,9 +24,6 @@ namespace app {
|
||||
// The main task of this class is to draw scanlines through its
|
||||
// inkHline function member.
|
||||
class Ink {
|
||||
// selection, paint, paint_fg, paint_bg, eraser,
|
||||
// replace_fg_with_bg, replace_bg_with_fg, pick_fg, pick_bg, scroll,
|
||||
// move, shade, blur, jumble
|
||||
public:
|
||||
virtual ~Ink() { }
|
||||
|
||||
@ -89,6 +88,12 @@ namespace app {
|
||||
// to a shape (e.g. pen shape) with various scanlines.
|
||||
virtual void inkHline(int x1, int y, int x2, ToolLoop* loop) = 0;
|
||||
|
||||
// Returns true in case that the ink needs to update something
|
||||
// depending on the specific stroke points (e.g. for color
|
||||
// gradients)
|
||||
virtual bool dependsOnStroke() const { return false; }
|
||||
virtual void updateInk(ToolLoop* loop, Strokes& strokes) { }
|
||||
|
||||
};
|
||||
|
||||
} // namespace tools
|
||||
|
@ -33,6 +33,7 @@ class BaseInkProcessing {
|
||||
public:
|
||||
virtual ~BaseInkProcessing() { }
|
||||
virtual void hline(int x1, int y, int x2, ToolLoop* loop) = 0;
|
||||
virtual void updateInk(ToolLoop* loop, Strokes& strokes) { }
|
||||
};
|
||||
|
||||
template<typename Derived>
|
||||
@ -193,6 +194,7 @@ public:
|
||||
c = m_palette->getEntry(c);
|
||||
|
||||
color_t result = rgba_blender_normal(c, m_color, m_opacity);
|
||||
// TODO should we use m_rgbmap->mapColor instead?
|
||||
*m_dstAddress = m_palette->findBestfit(
|
||||
rgba_getr(result),
|
||||
rgba_getg(result),
|
||||
@ -865,6 +867,272 @@ private:
|
||||
bool m_left;
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// Gradient Ink
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
static ImageBufferPtr tmpGradientBuffer; // TODO non-thread safe
|
||||
|
||||
class TemporalPixmanGradient {
|
||||
public:
|
||||
TemporalPixmanGradient(ToolLoop* loop) {
|
||||
if (!tmpGradientBuffer)
|
||||
tmpGradientBuffer.reset(new ImageBuffer(1));
|
||||
|
||||
m_tmpImage.reset(
|
||||
Image::create(IMAGE_RGB,
|
||||
loop->getDstImage()->width(),
|
||||
loop->getDstImage()->height(),
|
||||
tmpGradientBuffer));
|
||||
m_tmpImage->clear(0);
|
||||
}
|
||||
|
||||
void renderRgbaGradient(ToolLoop* loop, Strokes& strokes,
|
||||
// RGBA colors
|
||||
color_t c0, color_t c1) {
|
||||
if (strokes.empty() || strokes[0].size() < 2) {
|
||||
m_tmpImage->clear(0);
|
||||
return;
|
||||
}
|
||||
|
||||
pixman_point_fixed_t u, v;
|
||||
pixman_gradient_stop_t stops[2];
|
||||
|
||||
u.x = pixman_int_to_fixed(strokes[0].firstPoint().x);
|
||||
u.y = pixman_int_to_fixed(strokes[0].firstPoint().y);
|
||||
v.x = pixman_int_to_fixed(strokes[0].lastPoint().x);
|
||||
v.y = pixman_int_to_fixed(strokes[0].lastPoint().y);
|
||||
|
||||
// As we use non-premultiplied RGB values, we need correct RGB
|
||||
// values on each stop. So in case that one color has alpha=0
|
||||
// (complete transparent), use the RGB values of the
|
||||
// non-transparent color in the other stop point.
|
||||
if (doc::rgba_geta(c0) == 0 &&
|
||||
doc::rgba_geta(c1) != 0) {
|
||||
c0 = (c1 & rgba_rgb_mask);
|
||||
}
|
||||
else if (doc::rgba_geta(c0) != 0 &&
|
||||
doc::rgba_geta(c1) == 0) {
|
||||
c1 = (c0 & rgba_rgb_mask);
|
||||
}
|
||||
|
||||
stops[0].x = pixman_int_to_fixed(0);
|
||||
stops[0].color.red = int(doc::rgba_getr(c0)) << 8;
|
||||
stops[0].color.green = int(doc::rgba_getg(c0)) << 8;
|
||||
stops[0].color.blue = int(doc::rgba_getb(c0)) << 8;
|
||||
stops[0].color.alpha = int(doc::rgba_geta(c0)) << 8;
|
||||
|
||||
stops[1].x = pixman_int_to_fixed(1);
|
||||
stops[1].color.red = int(doc::rgba_getr(c1)) << 8;
|
||||
stops[1].color.green = int(doc::rgba_getg(c1)) << 8;
|
||||
stops[1].color.blue = int(doc::rgba_getb(c1)) << 8;
|
||||
stops[1].color.alpha = int(doc::rgba_geta(c1)) << 8;
|
||||
|
||||
pixman_image_t* gradientImg =
|
||||
pixman_image_create_linear_gradient(
|
||||
&u, &v, stops, 2);
|
||||
pixman_image_set_repeat(gradientImg, PIXMAN_REPEAT_PAD);
|
||||
|
||||
pixman_image_t* rasterImg =
|
||||
pixman_image_create_bits(PIXMAN_a8b8g8r8,
|
||||
m_tmpImage->width(),
|
||||
m_tmpImage->height(),
|
||||
(uint32_t*)m_tmpImage->getPixelAddress(0, 0),
|
||||
m_tmpImage->getRowStrideSize());
|
||||
|
||||
pixman_image_composite(PIXMAN_OP_SRC, // Copy the gradient (no alpha compositing)
|
||||
gradientImg, nullptr,
|
||||
rasterImg,
|
||||
0, 0, 0, 0, 0, 0,
|
||||
m_tmpImage->width(),
|
||||
m_tmpImage->height());
|
||||
|
||||
pixman_image_unref(gradientImg);
|
||||
pixman_image_unref(rasterImg);
|
||||
}
|
||||
|
||||
protected:
|
||||
ImageRef m_tmpImage;
|
||||
RgbTraits::address_t m_tmpAddress;
|
||||
};
|
||||
|
||||
template<typename ImageTraits>
|
||||
class GradientInkProcessing : public DoubleInkProcessing<GradientInkProcessing<ImageTraits>, ImageTraits> {
|
||||
public:
|
||||
GradientInkProcessing(ToolLoop* loop) {
|
||||
}
|
||||
|
||||
void processPixel(int x, int y) {
|
||||
// Do nothing (it's specialized for each case)
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
class GradientInkProcessing<RgbTraits> : public TemporalPixmanGradient,
|
||||
public DoubleInkProcessing<GradientInkProcessing<RgbTraits>, RgbTraits> {
|
||||
public:
|
||||
typedef DoubleInkProcessing<GradientInkProcessing<RgbTraits>, RgbTraits> base;
|
||||
|
||||
GradientInkProcessing(ToolLoop* loop)
|
||||
: TemporalPixmanGradient(loop)
|
||||
, m_opacity(loop->getOpacity()) {
|
||||
}
|
||||
|
||||
void updateInk(ToolLoop* loop, Strokes& strokes) override {
|
||||
color_t c0 = loop->getPrimaryColor();
|
||||
color_t c1 = loop->getSecondaryColor();
|
||||
|
||||
renderRgbaGradient(loop, strokes, c0, c1);
|
||||
}
|
||||
|
||||
void hline(int x1, int y, int x2, ToolLoop* loop) override {
|
||||
m_tmpAddress = (RgbTraits::address_t)m_tmpImage->getPixelAddress(x1, y);
|
||||
base::hline(x1, y, x2, loop);
|
||||
}
|
||||
|
||||
void processPixel(int x, int y) {
|
||||
// As m_tmpAddress is the rendered gradient from pixman, its RGB
|
||||
// values are premultiplied, here we can divide them by the alpha
|
||||
// value to get the non-premultiplied values.
|
||||
doc::color_t c = *m_tmpAddress;
|
||||
int a = doc::rgba_geta(c);
|
||||
int r, g, b;
|
||||
if (a > 0) {
|
||||
r = doc::rgba_getr(c) * 255 / a;
|
||||
g = doc::rgba_getg(c) * 255 / a;
|
||||
b = doc::rgba_getb(c) * 255 / a;
|
||||
}
|
||||
else
|
||||
r = g = b = 0;
|
||||
|
||||
*m_dstAddress = rgba_blender_normal(*m_srcAddress,
|
||||
doc::rgba(r, g, b, a),
|
||||
m_opacity);
|
||||
++m_tmpAddress;
|
||||
}
|
||||
|
||||
private:
|
||||
const int m_opacity;
|
||||
};
|
||||
|
||||
|
||||
template<>
|
||||
class GradientInkProcessing<GrayscaleTraits> : public TemporalPixmanGradient,
|
||||
public DoubleInkProcessing<GradientInkProcessing<GrayscaleTraits>, GrayscaleTraits> {
|
||||
public:
|
||||
typedef DoubleInkProcessing<GradientInkProcessing<GrayscaleTraits>, GrayscaleTraits> base;
|
||||
|
||||
GradientInkProcessing(ToolLoop* loop)
|
||||
: TemporalPixmanGradient(loop)
|
||||
, m_opacity(loop->getOpacity()) {
|
||||
}
|
||||
|
||||
void updateInk(ToolLoop* loop, Strokes& strokes) override {
|
||||
color_t c0 = loop->getPrimaryColor();
|
||||
color_t c1 = loop->getSecondaryColor();
|
||||
int v0 = int(doc::graya_getv(c0));
|
||||
int a0 = int(doc::graya_geta(c0));
|
||||
int v1 = int(doc::graya_getv(c1));
|
||||
int a1 = int(doc::graya_geta(c1));
|
||||
c0 = doc::rgba(v0, v0, v0, a0);
|
||||
c1 = doc::rgba(v1, v1, v1, a1);
|
||||
|
||||
renderRgbaGradient(loop, strokes, c0, c1);
|
||||
}
|
||||
|
||||
void hline(int x1, int y, int x2, ToolLoop* loop) override {
|
||||
m_tmpAddress = (RgbTraits::address_t)m_tmpImage->getPixelAddress(x1, y);
|
||||
base::hline(x1, y, x2, loop);
|
||||
}
|
||||
|
||||
void processPixel(int x, int y) {
|
||||
// As m_tmpAddress is the rendered gradient from pixman, its RGB
|
||||
// values are premultiplied, here we can divide them by the alpha
|
||||
// value to get the non-premultiplied values.
|
||||
doc::color_t c = *m_tmpAddress;
|
||||
int a = doc::rgba_geta(c);
|
||||
int v;
|
||||
if (a > 0) {
|
||||
// Here we could get R, G, or B because this is a grayscale gradient anyway.
|
||||
v = doc::rgba_getr(c) * 255 / a;
|
||||
}
|
||||
else
|
||||
v = 0;
|
||||
|
||||
*m_dstAddress = graya_blender_normal(*m_srcAddress,
|
||||
doc::graya(v, a),
|
||||
m_opacity);
|
||||
++m_tmpAddress;
|
||||
}
|
||||
|
||||
private:
|
||||
const int m_opacity;
|
||||
};
|
||||
|
||||
|
||||
template<>
|
||||
class GradientInkProcessing<IndexedTraits> : public TemporalPixmanGradient,
|
||||
public DoubleInkProcessing<GradientInkProcessing<IndexedTraits>, IndexedTraits> {
|
||||
public:
|
||||
typedef DoubleInkProcessing<GradientInkProcessing<IndexedTraits>, IndexedTraits> base;
|
||||
|
||||
GradientInkProcessing(ToolLoop* loop)
|
||||
: TemporalPixmanGradient(loop)
|
||||
, m_opacity(loop->getOpacity())
|
||||
, m_palette(get_current_palette())
|
||||
, m_rgbmap(loop->getRgbMap())
|
||||
, m_maskIndex(loop->getLayer()->isBackground() ? -1: loop->sprite()->transparentColor()) {
|
||||
}
|
||||
|
||||
void updateInk(ToolLoop* loop, Strokes& strokes) override {
|
||||
color_t c0 = m_palette->getEntry(loop->getPrimaryColor());
|
||||
color_t c1 = m_palette->getEntry(loop->getSecondaryColor());
|
||||
|
||||
renderRgbaGradient(loop, strokes, c0, c1);
|
||||
}
|
||||
|
||||
void hline(int x1, int y, int x2, ToolLoop* loop) override {
|
||||
m_tmpAddress = (RgbTraits::address_t)m_tmpImage->getPixelAddress(x1, y);
|
||||
base::hline(x1, y, x2, loop);
|
||||
}
|
||||
|
||||
void processPixel(int x, int y) {
|
||||
// As m_tmpAddress is the rendered gradient from pixman, its RGB
|
||||
// values are premultiplied, here we can divide them by the alpha
|
||||
// value to get the non-premultiplied values.
|
||||
doc::color_t c = *m_tmpAddress;
|
||||
int a = doc::rgba_geta(c);
|
||||
int r, g, b;
|
||||
if (a > 0) {
|
||||
r = doc::rgba_getr(c) * 255 / a;
|
||||
g = doc::rgba_getg(c) * 255 / a;
|
||||
b = doc::rgba_getb(c) * 255 / a;
|
||||
}
|
||||
else
|
||||
r = g = b = 0;
|
||||
|
||||
doc::color_t c0 = *m_srcAddress;
|
||||
if (int(c0) == m_maskIndex)
|
||||
c0 = m_palette->getEntry(c0) & rgba_rgb_mask; // Alpha = 0
|
||||
else
|
||||
c0 = m_palette->getEntry(c0);
|
||||
c = rgba_blender_normal(c0, c, m_opacity);
|
||||
|
||||
*m_dstAddress = m_rgbmap->mapColor(rgba_getr(c),
|
||||
rgba_getg(c),
|
||||
rgba_getb(c),
|
||||
rgba_geta(c));
|
||||
++m_tmpAddress;
|
||||
}
|
||||
|
||||
private:
|
||||
const int m_opacity;
|
||||
const Palette* m_palette;
|
||||
const RgbMap* m_rgbmap;
|
||||
const int m_maskIndex;
|
||||
};
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// Xor Ink
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
@ -34,6 +34,10 @@ protected:
|
||||
m_proc.reset(proc);
|
||||
}
|
||||
|
||||
BaseInkProcessing* proc() {
|
||||
return m_proc;
|
||||
}
|
||||
|
||||
private:
|
||||
InkProcessingPtr m_proc;
|
||||
};
|
||||
@ -133,6 +137,25 @@ public:
|
||||
};
|
||||
|
||||
|
||||
class GradientInk : public BaseInk {
|
||||
public:
|
||||
Ink* clone() override { return new GradientInk(*this); }
|
||||
|
||||
bool isPaint() const override { return true; }
|
||||
bool isEffect() const override { return true; }
|
||||
bool dependsOnStroke() const override { return true; }
|
||||
|
||||
void prepareInk(ToolLoop* loop) override {
|
||||
setProc(get_ink_proc<GradientInkProcessing>(loop));
|
||||
}
|
||||
|
||||
void updateInk(ToolLoop* loop, Strokes& strokes) override {
|
||||
proc()->updateInk(loop, strokes);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
class ScrollInk : public Ink {
|
||||
public:
|
||||
Ink* clone() override { return new ScrollInk(*this); }
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2001-2015 David Capello
|
||||
// Copyright (C) 2001-2017 David Capello
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
// the End-User License Agreement for Aseprite.
|
||||
@ -20,6 +20,19 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
class IntertwineFirstPoint : public Intertwine {
|
||||
public:
|
||||
|
||||
void joinStroke(ToolLoop* loop, const Stroke& stroke) override {
|
||||
if (!stroke.empty())
|
||||
doPointshapePoint(stroke[0].x, stroke[0].y, loop);
|
||||
}
|
||||
|
||||
void fillStroke(ToolLoop* loop, const Stroke& stroke) override {
|
||||
joinStroke(loop, stroke);
|
||||
}
|
||||
};
|
||||
|
||||
class IntertwineAsLines : public Intertwine {
|
||||
public:
|
||||
bool snapByAngle() override { return true; }
|
||||
|
@ -8,6 +8,8 @@
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <pixman.h> // Needed for GradientInk
|
||||
|
||||
#include "app/tools/tool_box.h"
|
||||
|
||||
#include "app/gui_xml.h"
|
||||
@ -55,6 +57,7 @@ const char* WellKnownInks::PaintBg = "paint_bg";
|
||||
const char* WellKnownInks::PaintCopy = "paint_copy";
|
||||
const char* WellKnownInks::PaintLockAlpha = "paint_lock_alpha";
|
||||
const char* WellKnownInks::Shading = "shading";
|
||||
const char* WellKnownInks::Gradient = "gradient";
|
||||
const char* WellKnownInks::Eraser = "eraser";
|
||||
const char* WellKnownInks::ReplaceFgWithBg = "replace_fg_with_bg";
|
||||
const char* WellKnownInks::ReplaceBgWithFg = "replace_bg_with_fg";
|
||||
@ -69,6 +72,7 @@ const char* WellKnownInks::Blur = "blur";
|
||||
const char* WellKnownInks::Jumble = "jumble";
|
||||
|
||||
const char* WellKnownIntertwiners::None = "none";
|
||||
const char* WellKnownIntertwiners::FirstPoint = "first_point";
|
||||
const char* WellKnownIntertwiners::AsLines = "as_lines";
|
||||
const char* WellKnownIntertwiners::AsRectangles = "as_rectangles";
|
||||
const char* WellKnownIntertwiners::AsEllipses = "as_ellipses";
|
||||
@ -89,6 +93,7 @@ ToolBox::ToolBox()
|
||||
m_inks[WellKnownInks::PaintBg] = new PaintInk(PaintInk::WithBg);
|
||||
m_inks[WellKnownInks::PaintCopy] = new PaintInk(PaintInk::Copy);
|
||||
m_inks[WellKnownInks::PaintLockAlpha] = new PaintInk(PaintInk::LockAlpha);
|
||||
m_inks[WellKnownInks::Gradient] = new GradientInk();
|
||||
m_inks[WellKnownInks::Shading] = new ShadingInk();
|
||||
m_inks[WellKnownInks::Eraser] = new EraserInk(EraserInk::Eraser);
|
||||
m_inks[WellKnownInks::ReplaceFgWithBg] = new EraserInk(EraserInk::ReplaceFgWithBg);
|
||||
@ -115,6 +120,7 @@ ToolBox::ToolBox()
|
||||
m_pointshapers[WellKnownPointShapes::Spray] = new SprayPointShape();
|
||||
|
||||
m_intertwiners[WellKnownIntertwiners::None] = new IntertwineNone();
|
||||
m_intertwiners[WellKnownIntertwiners::FirstPoint] = new IntertwineFirstPoint();
|
||||
m_intertwiners[WellKnownIntertwiners::AsLines] = new IntertwineAsLines();
|
||||
m_intertwiners[WellKnownIntertwiners::AsRectangles] = new IntertwineAsRectangles();
|
||||
m_intertwiners[WellKnownIntertwiners::AsEllipses] = new IntertwineAsEllipses();
|
||||
|
@ -37,6 +37,7 @@ namespace app {
|
||||
extern const char* PaintCopy;
|
||||
extern const char* PaintLockAlpha;
|
||||
extern const char* Shading;
|
||||
extern const char* Gradient;
|
||||
extern const char* Eraser;
|
||||
extern const char* ReplaceFgWithBg;
|
||||
extern const char* ReplaceBgWithFg;
|
||||
@ -53,6 +54,7 @@ namespace app {
|
||||
|
||||
namespace WellKnownIntertwiners {
|
||||
extern const char* None;
|
||||
extern const char* FirstPoint;
|
||||
extern const char* AsLines;
|
||||
extern const char* AsRectangles;
|
||||
extern const char* AsEllipses;
|
||||
|
@ -173,6 +173,9 @@ void ToolLoopManager::doLoopStep(bool last_step)
|
||||
m_toolLoop->validateSrcImage(m_dirtyArea);
|
||||
}
|
||||
|
||||
if (m_toolLoop->getInk()->dependsOnStroke())
|
||||
m_toolLoop->getInk()->updateInk(m_toolLoop, strokes);
|
||||
|
||||
// Invalidate destionation image areas.
|
||||
if (m_toolLoop->getTracePolicy() == TracePolicy::Last) {
|
||||
// Copy source to destination (reset the previous trace). Useful
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2001-2016 David Capello
|
||||
// Copyright (C) 2001-2017 David Capello
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
// the End-User License Agreement for Aseprite.
|
||||
@ -130,7 +130,7 @@ void BrushPreview::show(const gfx::Point& screenPos)
|
||||
}
|
||||
else if (
|
||||
(brush->type() == kImageBrushType ||
|
||||
brush->size() > 1.0 / m_editor->zoom().scale()) &&
|
||||
((isFloodfill ? 1: brush->size()) > (1.0 / m_editor->zoom().scale()))) &&
|
||||
(// 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
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Aseprite Gfx Library
|
||||
// Copyright (C) 2001-2016 David Capello
|
||||
// Copyright (C) 2001-2017 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
// Read LICENSE.txt for more information.
|
||||
@ -26,10 +26,6 @@ namespace gfx {
|
||||
#else
|
||||
struct Box {
|
||||
int32_t x1, y1, x2, y2;
|
||||
|
||||
operator Rect() const {
|
||||
return Rect(x1, y1, x2-x1, y2-y1);
|
||||
}
|
||||
};
|
||||
struct Region {
|
||||
Box extents;
|
||||
@ -51,7 +47,13 @@ namespace gfx {
|
||||
RegionIterator operator++(int) { RegionIterator o(*this); ++m_ptr; return o; }
|
||||
bool operator==(const RegionIterator& o) const { return m_ptr == o.m_ptr; }
|
||||
bool operator!=(const RegionIterator& o) const { return m_ptr != o.m_ptr; }
|
||||
reference operator*() { m_rect = *m_ptr; return m_rect; }
|
||||
reference operator*() {
|
||||
m_rect.x = m_ptr->x1;
|
||||
m_rect.y = m_ptr->y1;
|
||||
m_rect.w = m_ptr->x2 - m_ptr->x1;
|
||||
m_rect.h = m_ptr->y2 - m_ptr->y1;
|
||||
return m_rect;
|
||||
}
|
||||
private:
|
||||
Box* m_ptr;
|
||||
mutable Rect m_rect;
|
||||
|
Loading…
Reference in New Issue
Block a user