/* * Copyright (c) 2002 Patrick Julien * Copyright (c) 2004 Boudewijn Rempt * Copyright (c) 2004 Clarence Dang * Copyright (c) 2004 Adrian Page * Copyright (c) 2004 Cyrille Berger * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include #include #include #include "tqbrush.h" #include "tqfontinfo.h" #include "tqfontmetrics.h" #include "tqpen.h" #include "tqregion.h" #include "tqwmatrix.h" #include #include #include #include #include #include #include #include #include #include #include "kis_brush.h" #include "kis_debug_areas.h" #include "kis_image.h" #include "kis_layer.h" #include "kis_paint_device.h" #include "kis_painter.h" #include "kis_pattern.h" #include "kis_rect.h" #include "kis_colorspace.h" #include "kis_transaction.h" #include "kis_types.h" #include "kis_vec.h" #include "kis_iterators_pixel.h" #include "kis_paintop.h" #include "kis_selection.h" #include "kis_fill_painter.h" #include "kis_color.h" // Maximum distance from a Bezier control point to the line through the start // and end points for the curve to be considered flat. #define BEZIER_FLATNESS_THRESHOLD 0.5 KisPainter::KisPainter() { init(); } KisPainter::KisPainter(KisPaintDeviceSP device) { init(); Q_ASSERT(device); begin(device); } void KisPainter::init() { m_transaction = 0; m_paintOp = 0; m_filter = 0; m_brush = 0; m_pattern= 0; m_opacity = OPACITY_OPAQUE; m_compositeOp = COMPOSITE_OVER; m_dab = 0; m_fillStyle = FillStyleNone; m_strokeStyle = StrokeStyleBrush; m_pressure = PRESSURE_MIN; m_duplicateHealing = false; m_duplicateHealingRadius = 10; m_duplicatePerspectiveCorrection = false; m_varyBrushSpacingWithPressureWhenDrawingALine = true; } KisPainter::~KisPainter() { m_brush = 0; delete m_paintOp; end(); } void KisPainter::begin(KisPaintDeviceSP device) { if (!device) return; if (m_transaction) delete m_transaction; m_device = device; m_colorSpace = device->colorSpace(); m_pixelSize = device->pixelSize(); } KCommand *KisPainter::end() { return endTransaction(); } void KisPainter::beginTransaction(const TQString& customName) { if (m_transaction) delete m_transaction; m_transaction = new KisTransaction(customName, m_device); TQ_CHECK_PTR(m_transaction); } void KisPainter::beginTransaction( KisTransaction* command) { if (m_transaction) delete m_transaction; m_transaction = command; } KCommand *KisPainter::endTransaction() { KCommand *command = m_transaction; m_transaction = 0; return command; } TQRect KisPainter::dirtyRect() { TQRect r = m_dirtyRect; m_dirtyRect = TQRect(); return r; } void KisPainter::bitBlt(TQ_INT32 dx, TQ_INT32 dy, const KisCompositeOp& op, KisPaintDeviceSP srcdev, TQ_UINT8 opacity, TQ_INT32 sx, TQ_INT32 sy, TQ_INT32 sw, TQ_INT32 sh) { if (srcdev == 0) { return; } TQRect srcRect = TQRect(sx, sy, sw, sh); if (srcdev->extentIsValid() && op != COMPOSITE_COPY) { srcRect &= srcdev->extent(); } if (srcRect.isEmpty()) { return; } dx += srcRect.x() - sx; dy += srcRect.y() - sy; sx = srcRect.x(); sy = srcRect.y(); sw = srcRect.width(); sh = srcRect.height(); addDirtyRect(TQRect(dx, dy, sw, sh)); KisColorSpace * srcCs = srcdev->colorSpace(); TQ_INT32 dstY = dy; TQ_INT32 srcY = sy; TQ_INT32 rowsRemaining = sh; while (rowsRemaining > 0) { TQ_INT32 dstX = dx; TQ_INT32 srcX = sx; TQ_INT32 columnsRemaining = sw; TQ_INT32 numContiguousDstRows = m_device->numContiguousRows(dstY, dstX, dstX + sw - 1); TQ_INT32 numContiguousSrcRows = srcdev->numContiguousRows(srcY, srcX, srcX + sw - 1); TQ_INT32 rows = TQMIN(numContiguousDstRows, numContiguousSrcRows); rows = TQMIN(rows, rowsRemaining); while (columnsRemaining > 0) { TQ_INT32 numContiguousDstColumns = m_device->numContiguousColumns(dstX, dstY, dstY + rows - 1); TQ_INT32 numContiguousSrcColumns = srcdev->numContiguousColumns(srcX, srcY, srcY + rows - 1); TQ_INT32 columns = TQMIN(numContiguousDstColumns, numContiguousSrcColumns); columns = TQMIN(columns, columnsRemaining); TQ_INT32 srcRowStride = srcdev->rowStride(srcX, srcY); //const TQ_UINT8 *srcData = srcdev->pixel(srcX, srcY); KisHLineIteratorPixel srcIt = srcdev->createHLineIterator(srcX, srcY, columns, false); const TQ_UINT8 *srcData = srcIt.rawData(); //TQ_UINT8 *dstData = m_device->writablePixel(dstX, dstY); TQ_INT32 dstRowStride = m_device->rowStride(dstX, dstY); KisHLineIteratorPixel dstIt = m_device->createHLineIterator(dstX, dstY, columns, true); TQ_UINT8 *dstData = dstIt.rawData(); m_colorSpace->bitBlt(dstData, dstRowStride, srcCs, srcData, srcRowStride, 0, 0, opacity, rows, columns, op); srcX += columns; dstX += columns; columnsRemaining -= columns; } srcY += rows; dstY += rows; rowsRemaining -= rows; } } void KisPainter::bltSelection(TQ_INT32 dx, TQ_INT32 dy, const KisCompositeOp &op, KisPaintDeviceSP srcdev, KisSelectionSP seldev, TQ_UINT8 opacity, TQ_INT32 sx, TQ_INT32 sy, TQ_INT32 sw, TQ_INT32 sh) { // Better use a probablistic method than a too slow one if (seldev->isProbablyTotallyUnselected(TQRect(dx, dy, sw, sh))) { /* kdDebug() << "Blitting outside selection rect\n"; kdDebug() << "srcdev: " << srcdev << " (" << srcdev->name() << ")" << ", seldev: " << seldev << " (" << seldev->name() << ")" << ". dx, dy " << dx << "," << dy << ". sx, sy : sw, sy " << sx << "," << sy << " : " << sw << "," << sh << endl; */ return; } bltMask(dx,dy,op,srcdev,seldev.data(),opacity,sx,sy,sw,sh); } void KisPainter::bltMask(TQ_INT32 dx, TQ_INT32 dy, const KisCompositeOp &op, KisPaintDeviceSP srcdev, KisPaintDeviceSP seldev, TQ_UINT8 opacity, TQ_INT32 sx, TQ_INT32 sy, TQ_INT32 sw, TQ_INT32 sh) { if (srcdev == 0) return; if (seldev == 0) return; if (m_device == 0) return; TQRect srcRect = TQRect(sx, sy, sw, sh); if (srcdev->extentIsValid() && op != COMPOSITE_COPY) { srcRect &= srcdev->extent(); } if (srcRect.isEmpty()) { return; } dx += srcRect.x() - sx; dy += srcRect.y() - sy; sx = srcRect.x(); sy = srcRect.y(); sw = srcRect.width(); sh = srcRect.height(); addDirtyRect(TQRect(dx, dy, sw, sh)); KisColorSpace * srcCs = srcdev->colorSpace(); TQ_INT32 dstY = dy; TQ_INT32 srcY = sy; TQ_INT32 rowsRemaining = sh; while (rowsRemaining > 0) { TQ_INT32 dstX = dx; TQ_INT32 srcX = sx; TQ_INT32 columnsRemaining = sw; TQ_INT32 numContiguousDstRows = m_device->numContiguousRows(dstY, dstX, dstX + sw - 1); TQ_INT32 numContiguousSrcRows = srcdev->numContiguousRows(srcY, srcX, srcX + sw - 1); TQ_INT32 numContiguousSelRows = seldev->numContiguousRows(dstY, dstX, dstX + sw - 1); TQ_INT32 rows = TQMIN(numContiguousDstRows, numContiguousSrcRows); rows = TQMIN(numContiguousSelRows, rows); rows = TQMIN(rows, rowsRemaining); while (columnsRemaining > 0) { TQ_INT32 numContiguousDstColumns = m_device->numContiguousColumns(dstX, dstY, dstY + rows - 1); TQ_INT32 numContiguousSrcColumns = srcdev->numContiguousColumns(srcX, srcY, srcY + rows - 1); TQ_INT32 numContiguousSelColumns = seldev->numContiguousColumns(dstX, dstY, dstY + rows - 1); TQ_INT32 columns = TQMIN(numContiguousDstColumns, numContiguousSrcColumns); columns = TQMIN(numContiguousSelColumns, columns); columns = TQMIN(columns, columnsRemaining); //TQ_UINT8 *dstData = m_device->writablePixel(dstX, dstY); TQ_INT32 dstRowStride = m_device->rowStride(dstX, dstY); KisHLineIteratorPixel dstIt = m_device->createHLineIterator(dstX, dstY, columns, true); TQ_UINT8 *dstData = dstIt.rawData(); //const TQ_UINT8 *srcData = srcdev->pixel(srcX, srcY); TQ_INT32 srcRowStride = srcdev->rowStride(srcX, srcY); KisHLineIteratorPixel srcIt = srcdev->createHLineIterator(srcX, srcY, columns, false); const TQ_UINT8 *srcData = srcIt.rawData(); //const TQ_UINT8 *selData = seldev->pixel(dstX, dstY); TQ_INT32 selRowStride = seldev->rowStride(dstX, dstY); KisHLineIteratorPixel selIt = seldev->createHLineIterator(dstX, dstY, columns, false); const TQ_UINT8 *selData = selIt.rawData(); m_colorSpace->bitBlt(dstData, dstRowStride, srcCs, srcData, srcRowStride, selData, selRowStride, opacity, rows, columns, op); srcX += columns; dstX += columns; columnsRemaining -= columns; } srcY += rows; dstY += rows; rowsRemaining -= rows; } } void KisPainter::bltSelection(TQ_INT32 dx, TQ_INT32 dy, const KisCompositeOp& op, KisPaintDeviceSP srcdev, TQ_UINT8 opacity, TQ_INT32 sx, TQ_INT32 sy, TQ_INT32 sw, TQ_INT32 sh) { if (m_device == 0) return; if (!m_device->hasSelection()) { bitBlt(dx, dy, op, srcdev, opacity, sx, sy, sw, sh); } else bltSelection(dx,dy,op,srcdev, m_device->selection(),opacity,sx,sy,sw,sh); } double KisPainter::paintLine(const KisPoint & pos1, const double pressure1, const double xTilt1, const double yTilt1, const KisPoint & pos2, const double pressure2, const double xTilt2, const double yTilt2, const double inSavedDist) { if (!m_device) return 0; if (!m_paintOp) return 0; if (!m_brush) return 0; double savedDist = inSavedDist; KisVector2D end(pos2); KisVector2D start(pos1); KisVector2D dragVec = end - start; KisVector2D movement = dragVec; if (savedDist < 0) { m_paintOp->paintAt(pos1, KisPaintInformation(pressure1, xTilt1, yTilt1, movement)); savedDist = 0; } double xSpacing = 0; double ySpacing = 0; if ( m_varyBrushSpacingWithPressureWhenDrawingALine ) { // XXX: The spacing should vary as the pressure changes along the // line. // This is a quick simplification. xSpacing = m_brush->xSpacing((pressure1 + pressure2) / 2); ySpacing = m_brush->ySpacing((pressure1 + pressure2) / 2); } else { xSpacing = m_brush->xSpacing( PRESSURE_DEFAULT ); ySpacing = m_brush->ySpacing( PRESSURE_DEFAULT ); } if (xSpacing < 0.5) { xSpacing = 0.5; } if (ySpacing < 0.5) { ySpacing = 0.5; } double xScale = 1; double yScale = 1; double spacing; // Scale x or y so that we effectively have a square brush // and calculate distance in that coordinate space. We reverse this scaling // before drawing the brush. This produces the correct spacing in both // x and y directions, even if the brush's aspect ratio is not 1:1. if (xSpacing > ySpacing) { yScale = xSpacing / ySpacing; spacing = xSpacing; } else { xScale = ySpacing / xSpacing; spacing = ySpacing; } dragVec.setX(dragVec.x() * xScale); dragVec.setY(dragVec.y() * yScale); double newDist = dragVec.length(); double dist = savedDist + newDist; double l_savedDist = savedDist; if (dist < spacing) { return dist; } dragVec.normalize(); KisVector2D step(0, 0); while (dist >= spacing) { if (l_savedDist > 0) { step += dragVec * (spacing - l_savedDist); l_savedDist -= spacing; } else { step += dragVec * spacing; } KisPoint p(start.x() + (step.x() / xScale), start.y() + (step.y() / yScale)); double distanceMoved = step.length(); double t = 0; if (newDist > DBL_EPSILON) { t = distanceMoved / newDist; } double pressure = (1 - t) * pressure1 + t * pressure2; double xTilt = (1 - t) * xTilt1 + t * xTilt2; double yTilt = (1 - t) * yTilt1 + t * yTilt2; m_paintOp->paintAt(p, KisPaintInformation(pressure, xTilt, yTilt, movement)); dist -= spacing; } if (dist > 0) return dist; else return 0; } void KisPainter::paintPolyline (const vKisPoint &points, int index, int numPoints) { if (index >= (int) points.count ()) return; if (numPoints < 0) numPoints = points.count (); if (index + numPoints > (int) points.count ()) numPoints = points.count () - index; for (int i = index; i < index + numPoints - 1; i++) { paintLine (points [index], 0/*pressure*/, 0, 0, points [index + 1], 0/*pressure*/, 0, 0); } } void KisPainter::getBezierCurvePoints(const KisPoint &pos1, const KisPoint &control1, const KisPoint &control2, const KisPoint &pos2, vKisPoint& points) { double d1 = pointToLineDistance(control1, pos1, pos2); double d2 = pointToLineDistance(control2, pos1, pos2); if (d1 < BEZIER_FLATNESS_THRESHOLD && d2 < BEZIER_FLATNESS_THRESHOLD) { points.push_back(pos1); } else { // Midpoint subdivision. See Foley & Van Dam Computer Graphics P.508 KisVector2D p1 = pos1; KisVector2D p2 = control1; KisVector2D p3 = control2; KisVector2D p4 = pos2; KisVector2D l2 = (p1 + p2) / 2; KisVector2D h = (p2 + p3) / 2; KisVector2D l3 = (l2 + h) / 2; KisVector2D r3 = (p3 + p4) / 2; KisVector2D r2 = (h + r3) / 2; KisVector2D l4 = (l3 + r2) / 2; KisVector2D r1 = l4; KisVector2D l1 = p1; KisVector2D r4 = p4; getBezierCurvePoints(l1.toKisPoint(), l2.toKisPoint(), l3.toKisPoint(), l4.toKisPoint(), points); getBezierCurvePoints(r1.toKisPoint(), r2.toKisPoint(), r3.toKisPoint(), r4.toKisPoint(), points); } } double KisPainter::paintBezierCurve(const KisPoint &pos1, const double pressure1, const double xTilt1, const double yTilt1, const KisPoint &control1, const KisPoint &control2, const KisPoint &pos2, const double pressure2, const double xTilt2, const double yTilt2, const double savedDist) { double newDistance; double d1 = pointToLineDistance(control1, pos1, pos2); double d2 = pointToLineDistance(control2, pos1, pos2); if (d1 < BEZIER_FLATNESS_THRESHOLD && d2 < BEZIER_FLATNESS_THRESHOLD) { newDistance = paintLine(pos1, pressure1, xTilt1, yTilt1, pos2, pressure2, xTilt2, yTilt2, savedDist); } else { // Midpoint subdivision. See Foley & Van Dam Computer Graphics P.508 KisVector2D p1 = pos1; KisVector2D p2 = control1; KisVector2D p3 = control2; KisVector2D p4 = pos2; KisVector2D l2 = (p1 + p2) / 2; KisVector2D h = (p2 + p3) / 2; KisVector2D l3 = (l2 + h) / 2; KisVector2D r3 = (p3 + p4) / 2; KisVector2D r2 = (h + r3) / 2; KisVector2D l4 = (l3 + r2) / 2; KisVector2D r1 = l4; KisVector2D l1 = p1; KisVector2D r4 = p4; double midPressure = (pressure1 + pressure2) / 2; double midXTilt = (xTilt1 + xTilt2) / 2; double midYTilt = (yTilt1 + yTilt2) / 2; newDistance = paintBezierCurve(l1.toKisPoint(), pressure1, xTilt1, yTilt1, l2.toKisPoint(), l3.toKisPoint(), l4.toKisPoint(), midPressure, midXTilt, midYTilt, savedDist); newDistance = paintBezierCurve(r1.toKisPoint(), midPressure, midXTilt, midYTilt, r2.toKisPoint(), r3.toKisPoint(), r4.toKisPoint(), pressure2, xTilt2, yTilt2, newDistance); } return newDistance; } void KisPainter::paintRect (const KisPoint &startPoint, const KisPoint &endPoint, const double /*pressure*/, const double /*xTilt*/, const double /*yTilt*/) { KoRect normalizedRect = KisRect (startPoint, endPoint).normalize (); vKisPoint points; points.push_back(normalizedRect.topLeft()); points.push_back(normalizedRect.bottomLeft()); points.push_back(normalizedRect.bottomRight()); points.push_back(normalizedRect.topRight()); paintPolygon(points); } void KisPainter::paintEllipse (const KisPoint &startPoint, const KisPoint &endPoint, const double /*pressure*/, const double /*xTilt*/, const double /*yTilt*/) { KisRect r = KisRect(startPoint, endPoint).normalize(); // See http://www.whizkidtech.redprince.net/bezier/circle/ for explanation. // kappa = (4/3*(sqrt(2)-1)) const double kappa = 0.5522847498; const double lx = (r.width() / 2) * kappa; const double ly = (r.height() / 2) * kappa; KisPoint center = r.center(); KisPoint p0(r.left(), center.y()); KisPoint p1(r.left(), center.y() - ly); KisPoint p2(center.x() - lx, r.top()); KisPoint p3(center.x(), r.top()); vKisPoint points; getBezierCurvePoints(p0, p1, p2, p3, points); KisPoint p4(center.x() + lx, r.top()); KisPoint p5(r.right(), center.y() - ly); KisPoint p6(r.right(), center.y()); getBezierCurvePoints(p3, p4, p5, p6, points); KisPoint p7(r.right(), center.y() + ly); KisPoint p8(center.x() + lx, r.bottom()); KisPoint p9(center.x(), r.bottom()); getBezierCurvePoints(p6, p7, p8, p9, points); KisPoint p10(center.x() - lx, r.bottom()); KisPoint p11(r.left(), center.y() + ly); getBezierCurvePoints(p9, p10, p11, p0, points); paintPolygon(points); } void KisPainter::paintAt(const KisPoint & pos, const double pressure, const double xTilt, const double yTilt) { if (!m_paintOp) return; m_paintOp->paintAt(pos, KisPaintInformation(pressure, xTilt, yTilt, KisVector2D())); } double KisPainter::pointToLineDistance(const KisPoint& p, const KisPoint& l0, const KisPoint& l1) { double lineLength = sqrt((l1.x() - l0.x()) * (l1.x() - l0.x()) + (l1.y() - l0.y()) * (l1.y() - l0.y())); double distance = 0; if (lineLength > DBL_EPSILON) { distance = ((l0.y() - l1.y()) * p.x() + (l1.x() - l0.x()) * p.y() + l0.x() * l1.y() - l1.x() * l0.y()) / lineLength; distance = fabs(distance); } return distance; } /* * Concave Polygon Scan Conversion * by Paul Heckbert * from "Graphics Gems", Academic Press, 1990 */ /* * concave: scan convert nvert-sided concave non-simple polygon with vertices at * (point[i].x, point[i].y) for i in [0..nvert-1] within the window win by * calling spanproc for each visible span of pixels. * Polygon can be clockwise or counterclockwise. * Algorithm does uniform point sampling at pixel centers. * Inside-outside test done by Jordan's rule: a point is considered inside if * an emanating ray intersects the polygon an odd number of times. * drawproc should fill in pixels from xl to xr inclusive on scanline y, * e.g: * drawproc(y, xl, xr) * int y, xl, xr; * { * int x; * for (x=xl; x<=xr; x++) * pixel_write(x, y, pixelvalue); * } * * Paul Heckbert 30 June 81, 18 Dec 89 */ typedef struct { /* a polygon edge */ double x; /* x coordinate of edge's intersection with current scanline */ double dx; /* change in x with respect to y */ int i; /* edge number: edge i goes from pt[i] to pt[i+1] */ } Edge; static int n; /* number of vertices */ static const KisPoint *pt; /* vertices */ static int nact; /* number of active edges */ static Edge *active; /* active edge list:edges crossing scanline y */ /* comparison routines for qsort */ static int compare_ind(const void *pu, const void *pv) { const int *u = static_cast(pu); const int *v = static_cast(pv); return pt[*u].y() <= pt[*v].y() ? -1 : 1; } static int compare_active(const void *pu, const void *pv) { const Edge *u = static_cast(pu); const Edge *v = static_cast(pv); return u->x <= v->x ? -1 : 1; } static void cdelete(int i) /* remove edge i from active list */ { int j; for (j=0; j=nact) return; /* edge not in active list; happens at win->y0*/ nact--; bcopy(&active[j+1], &active[j], (nact-j)*sizeof active[0]); } static void cinsert(int i, int y) /* append edge i to end of active list */ { int j; double dx; const KisPoint *p, *q; j = ix()-p->x())/(q->y()-p->y()); active[nact].x = dx*(y+.5-p->y())+p->x(); active[nact].i = i; nact++; } void KisPainter::fillPolygon(const vKisPoint& points, FillStyle fillStyle) { int nvert = points.count(); int k, y0, y1, y, i, j, xl, xr; int *ind; /* list of vertex indices, sorted by pt[ind[j]].y */ n = nvert; pt = &(points[0]); if (n<3) return; if (fillStyle == FillStyleNone) { return; } ind = new int[n]; TQ_CHECK_PTR(ind); active = new Edge[n]; TQ_CHECK_PTR(active); /* create y-sorted array of indices ind[k] into vertex list */ for (k=0; k(ceil(pt[ind[0]].y()-.5)); /* ymin of polygon */ y1 = static_cast(floor(pt[ind[n-1]].y()-.5)); /* ymax of polygon */ int x0 = INT_MAX; int x1 = INT_MIN; for (int i = 0; i < nvert; i++) { int pointHighX = static_cast(ceil(points[i].x() - 0.5)); int pointLowX = static_cast(floor(points[i].x() - 0.5)); if (pointLowX < x0) { x0 = pointLowX; } if (pointHighX > x1) { x1 = pointHighX; } } // Fill the polygon bounding rectangle with the required contents then we'll // create a mask for the actual polygon coverage. KisPaintDeviceSP polygon = new KisPaintDevice(m_device->colorSpace(), "polygon"); TQ_CHECK_PTR(polygon); KisFillPainter fillPainter(polygon); TQRect boundingRectangle(x0, y0, x1 - x0 + 1, y1 - y0 + 1); // Clip to the image bounds. if (m_device->image()) { boundingRectangle &= m_device->image()->bounds(); } switch (fillStyle) { default: // Fall through case FillStyleGradient: // Currently unsupported, fall through case FillStyleStrokes: // Currently unsupported, fall through kdWarning(DBG_AREA_CORE) << "Unknown or unsupported fill style in fillPolygon\n"; case FillStyleForegroundColor: fillPainter.fillRect(boundingRectangle, paintColor(), OPACITY_OPAQUE); break; case FillStyleBackgroundColor: fillPainter.fillRect(boundingRectangle, backgroundColor(), OPACITY_OPAQUE); break; case FillStylePattern: Q_ASSERT(m_pattern != 0); fillPainter.fillRect(boundingRectangle, m_pattern); break; } KisSelectionSP polygonMask = new KisSelection(polygon); for (y=y0; y<=y1; y++) { /* step through scanlines */ /* scanline y is at y+.5 in continuous coordinates */ /* check vertices between previous scanline and current one, if any */ for (; k0 ? i-1 : n-1; /* vertex previous to i */ if (pt[j].y() <= y-.5) /* old edge, remove from active list */ cdelete(j); else if (pt[j].y() > y+.5) /* new edge, add to active list */ cinsert(j, y); j = i y+.5) /* new edge, add to active list */ cinsert(i, y); } /* sort active edge list by active[j].x */ qsort(active, nact, sizeof active[0], compare_active); /* draw horizontal segments for scanline y */ for (j=0; j(ceil(active[j].x-.5)); /* left end of span */ xr = static_cast(floor(active[j+1].x-.5)); /* right end of span */ if (xl<=xr) { KisHLineIterator it = polygonMask->createHLineIterator(xl, y, xr - xl + 1, true); while (!it.isDone()) { // We're using a selection here, that means alpha colorspace, that means one byte. it.rawData()[0] = MAX_SELECTED; ++it; } } active[j].x += active[j].dx; /* increment edge coords */ active[j+1].x += active[j+1].dx; } } delete [] ind; delete [] active; polygon->applySelectionMask(polygonMask); TQRect r = polygon->extent(); // The strokes for the outline may have already added updated the dirtyrect, but it can't hurt, // and if we're painting without outlines, then there will be no dirty rect. Let's do it ourselves... // addDirtyRect( r ); // XXX the bltSelection will add to the dirtyrect bltSelection(r.x(), r.y(), compositeOp(), polygon, opacity(), r.x(), r.y(), r.width(), r.height()); } void KisPainter::paintPolygon(const vKisPoint& points) { if (m_fillStyle != FillStyleNone) { fillPolygon(points, m_fillStyle); } if (m_strokeStyle != StrokeStyleNone) { if (points.count() > 1) { double distance = -1; for (uint i = 0; i < points.count() - 1; i++) { distance = paintLine(points[i], PRESSURE_DEFAULT, 0, 0, points[i + 1], PRESSURE_DEFAULT, 0, 0, distance); } paintLine(points[points.count() - 1], PRESSURE_DEFAULT, 0, 0, points[0], PRESSURE_DEFAULT, 0, 0, distance); } } }