mirror of
https://github.com/aseprite/aseprite.git
synced 2025-03-31 16:20:22 +00:00
Fix image brush paintbrush mode is broken (issue 1261)
This commit is contained in:
parent
a77543eb2b
commit
b32db39aa2
@ -1102,6 +1102,7 @@ public:
|
||||
m_height = m_brush->bounds().h;
|
||||
m_u = (m_brush->patternOrigin().x - loop->getCelOrigin().x) % m_width;
|
||||
m_v = (m_brush->patternOrigin().y - loop->getCelOrigin().y) % m_height;
|
||||
m_transparentColor = loop->sprite()->transparentColor();
|
||||
}
|
||||
|
||||
void prepareForPointShape(ToolLoop* loop, bool firstPoint, int x, int y) override {
|
||||
@ -1132,6 +1133,11 @@ private:
|
||||
const Image* m_brushMask;
|
||||
int m_opacity;
|
||||
int m_u, m_v, m_width, m_height;
|
||||
|
||||
// When we have a image brush from an INDEXED sprite, we need to know
|
||||
// which is the background color in order to translate to transparent color
|
||||
// in a RGBA sprite.
|
||||
color_t m_transparentColor;
|
||||
};
|
||||
|
||||
template<>
|
||||
@ -1144,20 +1150,36 @@ void BrushInkProcessing<RgbTraits>::processPixel(int x, int y) {
|
||||
switch (m_brushImage->pixelFormat()) {
|
||||
case IMAGE_RGB: {
|
||||
c = get_pixel_fast<RgbTraits>(m_brushImage, x, y);
|
||||
|
||||
// We blend the previous image brush pixel with a pixel from the
|
||||
// image preview (*m_dstAddress). Yes, dstImage, in that way we
|
||||
// can overlap image brush self printed areas (auto compose
|
||||
// colors). Doing this, we avoid eraser action of the pixels
|
||||
// with alpha <255 in the image brush.
|
||||
c = rgba_blender_normal(*m_dstAddress, c, m_opacity);
|
||||
break;
|
||||
}
|
||||
case IMAGE_INDEXED: {
|
||||
// TODO m_palette->getEntry(c) does not work because the m_palette member is
|
||||
// loaded the Graya Palette, NOT the original Indexed Palette from where m_brushImage belongs.
|
||||
// This conversion can be possible if we load the palette pointer in m_brush when
|
||||
// is created the custom brush in the Indexed Sprite.
|
||||
c = get_pixel_fast<IndexedTraits>(m_brushImage, x, y);
|
||||
c = m_palette->getEntry(c);
|
||||
if (m_transparentColor == c)
|
||||
c = 0;
|
||||
else
|
||||
c = m_palette->getEntry(c);
|
||||
c = rgba_blender_normal(*m_dstAddress, c, m_opacity);
|
||||
break;
|
||||
}
|
||||
case IMAGE_GRAYSCALE: {
|
||||
c = get_pixel_fast<GrayscaleTraits>(m_brushImage, x, y);
|
||||
// TODO review this line
|
||||
c = graya(m_palette->getEntry(c), graya_geta(c));
|
||||
c = rgba(graya_getv(c), graya_getv(c), graya_getv(c), graya_geta(c));
|
||||
c = rgba_blender_normal(*m_dstAddress, c, m_opacity);
|
||||
break;
|
||||
}
|
||||
case IMAGE_BITMAP: {
|
||||
// TODO In which circuntance is possible this case?
|
||||
c = get_pixel_fast<BitmapTraits>(m_brushImage, x, y);
|
||||
c = c ? m_fgColor: m_bgColor;
|
||||
break;
|
||||
@ -1166,8 +1188,7 @@ void BrushInkProcessing<RgbTraits>::processPixel(int x, int y) {
|
||||
ASSERT(false);
|
||||
return;
|
||||
}
|
||||
|
||||
*m_dstAddress = rgba_blender_normal(*m_srcAddress, c, m_opacity);
|
||||
*m_dstAddress = c;
|
||||
}
|
||||
|
||||
template<>
|
||||
@ -1180,22 +1201,33 @@ void BrushInkProcessing<GrayscaleTraits>::processPixel(int x, int y) {
|
||||
switch (m_brushImage->pixelFormat()) {
|
||||
case IMAGE_RGB: {
|
||||
c = get_pixel_fast<RgbTraits>(m_brushImage, x, y);
|
||||
c = graya(int(rgba_getr(c)) + int(rgba_getg(c)) + int(rgba_getb(c)) / 3,
|
||||
rgba_geta(c));
|
||||
c = graya(rgba_luma(c), rgba_geta(c));
|
||||
c = graya_blender_normal(*m_dstAddress, c, m_opacity);
|
||||
break;
|
||||
}
|
||||
case IMAGE_INDEXED: {
|
||||
// TODO m_palette->getEntry(c) does not work because the
|
||||
// m_palette member is loaded the Graya Palette, NOT the
|
||||
// original Indexed Palette from where m_brushImage belongs.
|
||||
// This conversion can be possible if we load the palette
|
||||
// pointer in m_brush when is created the custom brush in the
|
||||
// Indexed Sprite.
|
||||
c = get_pixel_fast<IndexedTraits>(m_brushImage, x, y);
|
||||
c = m_palette->getEntry(c);
|
||||
c = graya(int(rgba_getr(c)) + int(rgba_getg(c)) + int(rgba_getb(c)) / 3,
|
||||
rgba_geta(c));
|
||||
if (m_transparentColor == c)
|
||||
c = 0;
|
||||
else
|
||||
c = m_palette->getEntry(c);
|
||||
c = graya(rgba_luma(c), rgba_geta(c));
|
||||
c = graya_blender_normal(*m_dstAddress, c, m_opacity);
|
||||
break;
|
||||
}
|
||||
case IMAGE_GRAYSCALE: {
|
||||
c = get_pixel_fast<GrayscaleTraits>(m_brushImage, x, y);
|
||||
c = graya_blender_normal(*m_dstAddress, c, m_opacity);
|
||||
break;
|
||||
}
|
||||
case IMAGE_BITMAP: {
|
||||
// TODO In which circuntance is possible this case?
|
||||
c = get_pixel_fast<BitmapTraits>(m_brushImage, x, y);
|
||||
c = c ? m_fgColor: m_bgColor;
|
||||
break;
|
||||
@ -1204,8 +1236,7 @@ void BrushInkProcessing<GrayscaleTraits>::processPixel(int x, int y) {
|
||||
ASSERT(false);
|
||||
return;
|
||||
}
|
||||
|
||||
*m_dstAddress = graya_blender_normal(*m_srcAddress, c, m_opacity);
|
||||
*m_dstAddress = c;
|
||||
}
|
||||
|
||||
template<>
|
||||
@ -1234,6 +1265,7 @@ void BrushInkProcessing<IndexedTraits>::processPixel(int x, int y) {
|
||||
break;
|
||||
}
|
||||
case IMAGE_BITMAP: {
|
||||
// TODO In which circuntance is possible this case?
|
||||
c = get_pixel_fast<BitmapTraits>(m_brushImage, x, y);
|
||||
c = c ? m_fgColor: m_bgColor;
|
||||
break;
|
||||
@ -1242,8 +1274,8 @@ void BrushInkProcessing<IndexedTraits>::processPixel(int x, int y) {
|
||||
ASSERT(false);
|
||||
return;
|
||||
}
|
||||
|
||||
*m_dstAddress = c;
|
||||
if (c != m_transparentColor)
|
||||
*m_dstAddress = c;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
@ -59,6 +59,15 @@ public:
|
||||
};
|
||||
|
||||
class IntertwineAsLines : public Intertwine {
|
||||
static void Line(int x, int y, Stroke* stroke) {
|
||||
gfx::Point newPoint(x, y);
|
||||
if (stroke->empty() ||
|
||||
stroke->lastPoint() != newPoint) {
|
||||
stroke->addPoint(newPoint);
|
||||
}
|
||||
}
|
||||
|
||||
Stroke m_pts;
|
||||
public:
|
||||
bool snapByAngle() override { return true; }
|
||||
|
||||
@ -71,14 +80,23 @@ public:
|
||||
doPointshapePoint(stroke[0].x, stroke[0].y, loop);
|
||||
}
|
||||
else if (stroke.size() >= 2) {
|
||||
for (int c=0; c+1<stroke.size(); ++c) {
|
||||
int x1 = stroke[c].x;
|
||||
int y1 = stroke[c].y;
|
||||
int x2 = stroke[c+1].x;
|
||||
int y2 = stroke[c+1].y;
|
||||
if (stroke.size() == 2 && stroke[0] == stroke[1])
|
||||
return;
|
||||
else {
|
||||
for (int c=0; c+1<stroke.size(); ++c) {
|
||||
algo_line_continuous(
|
||||
stroke[c].x,
|
||||
stroke[c].y,
|
||||
stroke[c+1].x,
|
||||
stroke[c+1].y,
|
||||
(void*)&m_pts,
|
||||
(AlgoPixel)&IntertwineAsLines::Line);
|
||||
}
|
||||
|
||||
doPointshapeLine(x1, y1, x2, y2, loop);
|
||||
for (int c=1; c<m_pts.size(); ++c)
|
||||
doPointshapePoint(m_pts[c].x, m_pts[c].y, loop);
|
||||
}
|
||||
m_pts.reset();
|
||||
}
|
||||
|
||||
// Closed shape (polygon outline)
|
||||
@ -96,8 +114,11 @@ public:
|
||||
return;
|
||||
}
|
||||
|
||||
// Contour
|
||||
joinStroke(loop, stroke);
|
||||
// Don't draw the contour to avoid double drawing the filled
|
||||
// polygon and the contour when we use a custom brush and we use
|
||||
// the alpha compositing ink with opacity < 255 or the custom
|
||||
// brush has semi-transparent pixels.
|
||||
//joinStroke(loop, stroke);
|
||||
|
||||
// Fill content
|
||||
doc::algorithm::polygon(stroke.size(), (const int*)&stroke[0], loop, (AlgoHLine)doPointshapeHline);
|
||||
@ -385,8 +406,11 @@ public:
|
||||
|
||||
if (stroke.size() == 0)
|
||||
return;
|
||||
else if (m_pts.empty() && stroke.size() == 1) {
|
||||
m_pts = stroke;
|
||||
else if (stroke.size() == 1) {
|
||||
if (m_pts.empty())
|
||||
m_pts = stroke;
|
||||
doPointshapePoint(stroke[0].x, stroke[0].y, loop);
|
||||
return;
|
||||
}
|
||||
else {
|
||||
for (int c=0; c+1<stroke.size(); ++c) {
|
||||
@ -421,8 +445,11 @@ public:
|
||||
return;
|
||||
}
|
||||
|
||||
// Contour
|
||||
joinStroke(loop, stroke);
|
||||
// Don't draw the contour to avoid double drawing the filled
|
||||
// polygon and the contour when we use a custom brush and we use
|
||||
// the alpha compositing ink with opacity < 255 or the custom
|
||||
// brush has semi-transparent pixels.
|
||||
//joinStroke(loop, stroke);
|
||||
|
||||
// Fill content
|
||||
doc::algorithm::polygon(stroke.size(), (const int*)&stroke[0], loop, (AlgoHLine)doPointshapeHline);
|
||||
|
@ -206,7 +206,8 @@ void ToolLoopManager::doLoopStep(bool lastStep)
|
||||
// freehand algorithm needs this trace policy to redraw only the
|
||||
// last dirty area, which can vary in one pixel from the previous
|
||||
// tool loop cycle).
|
||||
m_toolLoop->invalidateDstImage(m_dirtyArea);
|
||||
m_toolLoop->invalidateDstImage();
|
||||
m_toolLoop->validateDstImage(gfx::Region(m_toolLoop->getDstImage()->bounds()));
|
||||
}
|
||||
|
||||
m_toolLoop->validateDstImage(m_dirtyArea);
|
||||
@ -214,8 +215,18 @@ void ToolLoopManager::doLoopStep(bool lastStep)
|
||||
// Join or fill user points
|
||||
if (!m_toolLoop->getFilled() || (!lastStep && !m_toolLoop->getPreviewFilled()))
|
||||
m_toolLoop->getIntertwine()->joinStroke(m_toolLoop, main_stroke);
|
||||
else
|
||||
else {
|
||||
// Filled + Freehand Controller = Contour Tool
|
||||
if (m_toolLoop->getController()->isFreehand()) {
|
||||
// With this we avoid over-drawing the edge of the contour,
|
||||
// appreciated when we use the Contour Tool combined with Image
|
||||
// Brush with alpha content.
|
||||
m_toolLoop->invalidateDstImage();
|
||||
m_toolLoop->validateDstImage(gfx::Region(m_toolLoop->getDstImage()->bounds()));
|
||||
}
|
||||
|
||||
m_toolLoop->getIntertwine()->fillStroke(m_toolLoop, main_stroke);
|
||||
}
|
||||
|
||||
if (m_toolLoop->getTracePolicy() == TracePolicy::Overlap) {
|
||||
// Copy destination to source (yes, destination to source). In
|
||||
|
Loading…
x
Reference in New Issue
Block a user