30#include "tqfontinfo.h"
31#include "tqfontmetrics.h"
39#include <tqpointarray.h>
48#include "kis_debug_areas.h"
51#include "kis_paint_device.h"
52#include "kis_painter.h"
53#include "kis_pattern.h"
55#include "kis_colorspace.h"
56#include "kis_transaction.h"
59#include "kis_iterators_pixel.h"
60#include "kis_paintop.h"
61#include "kis_selection.h"
62#include "kis_fill_painter.h"
67#define BEZIER_FLATNESS_THRESHOLD 0.5
88 m_opacity = OPACITY_OPAQUE;
89 m_compositeOp = COMPOSITE_OVER;
91 m_fillStyle = FillStyleNone;
92 m_strokeStyle = StrokeStyleBrush;
93 m_pressure = PRESSURE_MIN;
94 m_duplicateHealing = false;
95 m_duplicateHealingRadius = 10;
96 m_duplicatePerspectiveCorrection = false;
97 m_varyBrushSpacingWithPressureWhenDrawingALine = true;
100KisPainter::~KisPainter()
112 delete m_transaction;
115 m_colorSpace = device->colorSpace();
116 m_pixelSize = device->pixelSize();
127 delete m_transaction;
128 m_transaction = new KisTransaction(customName, m_device);
129 TQ_CHECK_PTR(m_transaction);
135 delete m_transaction;
136 m_transaction = command;
142 KCommand *command = m_transaction;
149 TQRect r = m_dirtyRect;
150 m_dirtyRect = TQRect();
155 const KisCompositeOp& op,
156 KisPaintDeviceSP srcdev,
158 TQ_INT32 sx, TQ_INT32 sy,
159 TQ_INT32 sw, TQ_INT32 sh)
165 TQRect srcRect = TQRect(sx, sy, sw, sh);
167 if (srcdev->extentIsValid() && op != COMPOSITE_COPY) {
168 srcRect &= srcdev->extent();
171 if (srcRect.isEmpty()) {
175 dx += srcRect.x() - sx;
176 dy += srcRect.y() - sy;
180 sw = srcRect.width();
181 sh = srcRect.height();
189 TQ_INT32 rowsRemaining = sh;
191 while (rowsRemaining > 0) {
195 TQ_INT32 columnsRemaining = sw;
196 TQ_INT32 numContiguousDstRows = m_device->numContiguousRows(dstY, dstX, dstX + sw - 1);
197 TQ_INT32 numContiguousSrcRows = srcdev->numContiguousRows(srcY, srcX, srcX + sw - 1);
199 TQ_INT32 rows = TQMIN(numContiguousDstRows, numContiguousSrcRows);
200 rows = TQMIN(rows, rowsRemaining);
202 while (columnsRemaining > 0) {
204 TQ_INT32 numContiguousDstColumns = m_device->numContiguousColumns(dstX, dstY, dstY + rows - 1);
205 TQ_INT32 numContiguousSrcColumns = srcdev->numContiguousColumns(srcX, srcY, srcY + rows - 1);
207 TQ_INT32 columns = TQMIN(numContiguousDstColumns, numContiguousSrcColumns);
208 columns = TQMIN(columns, columnsRemaining);
210 TQ_INT32 srcRowStride = srcdev->rowStride(srcX, srcY);
213 const TQ_UINT8 *srcData = srcIt.rawData();
216 TQ_INT32 dstRowStride = m_device->rowStride(dstX, dstY);
218 TQ_UINT8 *dstData = dstIt.rawData();
221 m_colorSpace->bitBlt(dstData,
235 columnsRemaining -= columns;
240 rowsRemaining -= rows;
245 const KisCompositeOp &op,
246 KisPaintDeviceSP srcdev,
247 KisSelectionSP seldev,
249 TQ_INT32 sx, TQ_INT32 sy,
250 TQ_INT32 sw, TQ_INT32 sh)
253 if (seldev->isProbablyTotallyUnselected(TQRect(dx, dy, sw, sh))) {
268 const KisCompositeOp &op,
269 KisPaintDeviceSP srcdev,
270 KisPaintDeviceSP seldev,
272 TQ_INT32 sx, TQ_INT32 sy,
273 TQ_INT32 sw, TQ_INT32 sh)
276 if (srcdev == 0) return;
278 if (seldev == 0) return;
280 if (m_device == 0) return;
283 TQRect srcRect = TQRect(sx, sy, sw, sh);
285 if (srcdev->extentIsValid() && op != COMPOSITE_COPY) {
286 srcRect &= srcdev->extent();
289 if (srcRect.isEmpty()) {
293 dx += srcRect.x() - sx;
294 dy += srcRect.y() - sy;
298 sw = srcRect.width();
299 sh = srcRect.height();
307 TQ_INT32 rowsRemaining = sh;
309 while (rowsRemaining > 0) {
313 TQ_INT32 columnsRemaining = sw;
314 TQ_INT32 numContiguousDstRows = m_device->numContiguousRows(dstY, dstX, dstX + sw - 1);
315 TQ_INT32 numContiguousSrcRows = srcdev->numContiguousRows(srcY, srcX, srcX + sw - 1);
316 TQ_INT32 numContiguousSelRows = seldev->numContiguousRows(dstY, dstX, dstX + sw - 1);
318 TQ_INT32 rows = TQMIN(numContiguousDstRows, numContiguousSrcRows);
319 rows = TQMIN(numContiguousSelRows, rows);
320 rows = TQMIN(rows, rowsRemaining);
322 while (columnsRemaining > 0) {
324 TQ_INT32 numContiguousDstColumns = m_device->numContiguousColumns(dstX, dstY, dstY + rows - 1);
325 TQ_INT32 numContiguousSrcColumns = srcdev->numContiguousColumns(srcX, srcY, srcY + rows - 1);
326 TQ_INT32 numContiguousSelColumns = seldev->numContiguousColumns(dstX, dstY, dstY + rows - 1);
328 TQ_INT32 columns = TQMIN(numContiguousDstColumns, numContiguousSrcColumns);
329 columns = TQMIN(numContiguousSelColumns, columns);
330 columns = TQMIN(columns, columnsRemaining);
333 TQ_INT32 dstRowStride = m_device->rowStride(dstX, dstY);
335 TQ_UINT8 *dstData = dstIt.rawData();
338 TQ_INT32 srcRowStride = srcdev->rowStride(srcX, srcY);
340 const TQ_UINT8 *srcData = srcIt.rawData();
343 TQ_INT32 selRowStride = seldev->rowStride(dstX, dstY);
345 const TQ_UINT8 *selData = selIt.rawData();
347 m_colorSpace->bitBlt(dstData,
361 columnsRemaining -= columns;
366 rowsRemaining -= rows;
372 const KisCompositeOp& op,
373 KisPaintDeviceSP srcdev,
375 TQ_INT32 sx, TQ_INT32 sy,
376 TQ_INT32 sw, TQ_INT32 sh)
378 if (m_device == 0) return;
379 if (!m_device->hasSelection()) {
387 const double pressure1,
391 const double pressure2,
394 const double inSavedDist)
396 if (!m_device) return 0;
397 if (!m_paintOp) return 0;
398 if (!m_brush) return 0;
400 double savedDist = inSavedDist;
401 KisVector2D end(pos2);
402 KisVector2D start(pos1);
404 KisVector2D dragVec = end - start;
405 KisVector2D movement = dragVec;
415 if ( m_varyBrushSpacingWithPressureWhenDrawingALine ) {
419 xSpacing = m_brush->xSpacing((pressure1 + pressure2) / 2);
420 ySpacing = m_brush->ySpacing((pressure1 + pressure2) / 2);
423 xSpacing = m_brush->xSpacing( PRESSURE_DEFAULT );
424 ySpacing = m_brush->ySpacing( PRESSURE_DEFAULT );
427 if (xSpacing < 0.5) {
430 if (ySpacing < 0.5) {
441 if (xSpacing > ySpacing) {
442 yScale = xSpacing / ySpacing;
446 xScale = ySpacing / xSpacing;
450 dragVec.setX(dragVec.x() * xScale);
451 dragVec.setY(dragVec.y() * yScale);
453 double newDist = dragVec.length();
454 double dist = savedDist + newDist;
455 double l_savedDist = savedDist;
457 if (dist < spacing) {
462 KisVector2D step(0, 0);
464 while (dist >= spacing) {
465 if (l_savedDist > 0) {
466 step += dragVec * (spacing - l_savedDist);
467 l_savedDist -= spacing;
470 step += dragVec * spacing;
473 KisPoint p(start.x() + (step.x() / xScale), start.y() + (step.y() / yScale));
475 double distanceMoved = step.length();
478 if (newDist > DBL_EPSILON) {
479 t = distanceMoved / newDist;
482 double pressure = (1 - t) * pressure1 + t * pressure2;
483 double xTilt = (1 - t) * xTilt1 + t * xTilt2;
484 double yTilt = (1 - t) * yTilt1 + t * yTilt2;
497 int index, int numPoints)
499 if (index >= ( int) points.count ())
503 numPoints = points.count ();
505 if (index + numPoints > ( int) points.count ())
506 numPoints = points.count () - index;
509 for ( int i = index; i < index + numPoints - 1; i++)
511 paintLine (points [index], 0, 0, 0, points [index + 1],
525 if (d1 < BEZIER_FLATNESS_THRESHOLD && d2 < BEZIER_FLATNESS_THRESHOLD) {
526 points.push_back(pos1);
529 KisVector2D p1 = pos1;
530 KisVector2D p2 = control1;
531 KisVector2D p3 = control2;
532 KisVector2D p4 = pos2;
534 KisVector2D l2 = (p1 + p2) / 2;
535 KisVector2D h = (p2 + p3) / 2;
536 KisVector2D l3 = (l2 + h) / 2;
537 KisVector2D r3 = (p3 + p4) / 2;
538 KisVector2D r2 = (h + r3) / 2;
539 KisVector2D l4 = (l3 + r2) / 2;
544 getBezierCurvePoints(l1.toKisPoint(), l2.toKisPoint(), l3.toKisPoint(), l4.toKisPoint(), points);
545 getBezierCurvePoints(r1.toKisPoint(), r2.toKisPoint(), r3.toKisPoint(), r4.toKisPoint(), points);
550 const double pressure1,
556 const double pressure2,
559 const double savedDist)
565 if (d1 < BEZIER_FLATNESS_THRESHOLD && d2 < BEZIER_FLATNESS_THRESHOLD) {
566 newDistance = paintLine(pos1, pressure1, xTilt1, yTilt1, pos2, pressure2, xTilt2, yTilt2, savedDist);
569 KisVector2D p1 = pos1;
570 KisVector2D p2 = control1;
571 KisVector2D p3 = control2;
572 KisVector2D p4 = pos2;
574 KisVector2D l2 = (p1 + p2) / 2;
575 KisVector2D h = (p2 + p3) / 2;
576 KisVector2D l3 = (l2 + h) / 2;
577 KisVector2D r3 = (p3 + p4) / 2;
578 KisVector2D r2 = (h + r3) / 2;
579 KisVector2D l4 = (l3 + r2) / 2;
584 double midPressure = (pressure1 + pressure2) / 2;
585 double midXTilt = (xTilt1 + xTilt2) / 2;
586 double midYTilt = (yTilt1 + yTilt2) / 2;
589 l2.toKisPoint(), l3.toKisPoint(),
590 l4.toKisPoint(), midPressure, midXTilt, midYTilt,
592 newDistance = paintBezierCurve(r1.toKisPoint(), midPressure, midXTilt, midYTilt,
595 r4.toKisPoint(), pressure2, xTilt2, yTilt2, newDistance);
607 KoRect normalizedRect = KisRect (startPoint, endPoint).normalize ();
611 points.push_back(normalizedRect.topLeft());
612 points.push_back(normalizedRect.bottomLeft());
613 points.push_back(normalizedRect.bottomRight());
614 points.push_back(normalizedRect.topRight());
629 const double kappa = 0.5522847498;
630 const double lx = (r.width() / 2) * kappa;
631 const double ly = (r.height() / 2) * kappa;
636 KisPoint p1(r.left(), center.y() - ly);
637 KisPoint p2(center.x() - lx, r.top());
644 KisPoint p4(center.x() + lx, r.top());
645 KisPoint p5(r.right(), center.y() - ly);
650 KisPoint p7(r.right(), center.y() + ly);
651 KisPoint p8(center.x() + lx, r.bottom());
652 KisPoint p9(center.x(), r.bottom());
656 KisPoint p10(center.x() - lx, r.bottom());
657 KisPoint p11(r.left(), center.y() + ly);
669 if (!m_paintOp) return;
675 double lineLength = sqrt((l1.x() - l0.x()) * (l1.x() - l0.x()) + (l1.y() - l0.y()) * (l1.y() - l0.y()));
678 if (lineLength > DBL_EPSILON) {
679 distance = ((l0.y() - l1.y()) * p.x() + (l1.x() - l0.x()) * p.y() + l0.x() * l1.y() - l1.x() * l0.y()) / lineLength;
680 distance = fabs(distance);
726static int compare_ind( const void *pu, const void *pv)
728 const int *u = static_cast<const int * >(pu);
729 const int *v = static_cast<const int * >(pv);
731 return pt[*u].y() <= pt[*v].y() ? -1 : 1;
734static int compare_active( const void *pu, const void *pv)
736 const Edge *u = static_cast<const Edge * >(pu);
737 const Edge *v = static_cast<const Edge * >(pv);
739 return u->x <= v->x ? -1 : 1;
742static void cdelete( int i)
746 for (j=0; j<nact && active[j].i!=i; j++);
749 bcopy(&active[j+1], &active[j], (nact-j)* sizeof active[0]);
752static void cinsert( int i, int y)
759 if (pt[i].y() < pt[j].y()) {
760 p = &pt[i]; q = &pt[j];
762 p = &pt[j]; q = &pt[i];
765 active[nact].dx = dx = (q->x()-p->x())/(q->y()-p->y());
766 active[nact].x = dx*(y+.5-p->y())+p->x();
773 int nvert = points.count();
774 int k, y0, y1, y, i, j, xl, xr;
786 active = new Edge[n];
787 TQ_CHECK_PTR(active);
792 qsort(ind, n, sizeof ind[0], compare_ind);
796 y0 = static_cast<int>(ceil(pt[ind[0]].y()-.5));
797 y1 = static_cast<int>(floor(pt[ind[n-1]].y()-.5));
802 for ( int i = 0; i < nvert; i++) {
803 int pointHighX = static_cast<int>(ceil(points[i].x() - 0.5));
804 int pointLowX = static_cast<int>(floor(points[i].x() - 0.5));
806 if (pointLowX < x0) {
809 if (pointHighX > x1) {
817 KisPaintDeviceSP polygon = new KisPaintDevice(m_device->colorSpace(), "polygon");
818 TQ_CHECK_PTR(polygon);
821 TQRect boundingRectangle(x0, y0, x1 - x0 + 1, y1 - y0 + 1);
824 if (m_device->image()) {
825 boundingRectangle &= m_device->image()->bounds();
831 case FillStyleGradient:
833 case FillStyleStrokes:
835 kdWarning(DBG_AREA_CORE) << "Unknown or unsupported fill style in fillPolygon\n";
836 case FillStyleForegroundColor:
839 case FillStyleBackgroundColor:
842 case FillStylePattern:
843 Q_ASSERT(m_pattern != 0);
844 fillPainter. fillRect(boundingRectangle, m_pattern);
850 for (y=y0; y<=y1; y++) {
854 for (; k<n && pt[ind[k]].y()<=y+.5; k++) {
863 if (pt[j].y() <= y-.5)
865 else if (pt[j].y() > y+.5)
868 if (pt[j].y() <= y-.5)
870 else if (pt[j].y() > y+.5)
875 qsort(active, nact, sizeof active[0], compare_active);
878 for (j=0; j<nact; j+=2) {
880 xl = static_cast<int>(ceil(active[j].x-.5));
881 xr = static_cast<int>(floor(active[j+1].x-.5));
884 KisHLineIterator it = polygonMask->createHLineIterator(xl, y, xr - xl + 1, true);
886 while (!it.isDone()) {
888 it.rawData()[0] = MAX_SELECTED;
893 active[j].x += active[j].dx;
894 active[j+1].x += active[j+1].dx;
900 polygon->applySelectionMask(polygonMask);
902 TQRect r = polygon->extent();
913 if (m_fillStyle != FillStyleNone) {
917 if (m_strokeStyle != StrokeStyleNone) {
918 if (points.count() > 1) {
919 double distance = -1;
921 for (uint i = 0; i < points.count() - 1; i++) {
922 distance = paintLine(points[i], PRESSURE_DEFAULT, 0, 0, points[i + 1], PRESSURE_DEFAULT, 0, 0, distance);
924 paintLine(points[points.count() - 1], PRESSURE_DEFAULT, 0, 0, points[0], PRESSURE_DEFAULT, 0, 0, distance);
A colorspace is the definition of a certain color model in Chalk.
This painter can be used to fill paint devices in different ways.
void fillRect(TQ_INT32 x, TQ_INT32 y, TQ_INT32 w, TQ_INT32 h, const KisColor &c) Fill a rectangle with a certain color.
The pixel iterators are high level iterarators.
A paint device contains the actual pixel data and offers methods to read and write pixels.
void init() Initialize, set everything to '0' or defaults.
TQRect addDirtyRect(TQRect r) Add the r to the current dirty rect, and return the dirtyRect after adding r to it.
static double pointToLineDistance(const KisPoint &p, const KisPoint &l0, const KisPoint &l1) Calculate the distance that point p is from the line made by connecting l0 and l1.
FillStyle This enum contains the styles with which we can fill things like polygons and ellipses.
KisColor paintColor() const Returns the color that will be used to paint with.
KisColor backgroundColor() const Returns the current background color.
void paintRect(const KisPoint &startPoint, const KisPoint &endPoint, const double pressure, const double xTilt, const double yTilt) Paint the rectangle with given begin and end points.
KCommand * end() Finish painting on the current device.
KisCompositeOp compositeOp() const Returns the current composite operation.
KisPaintDeviceSP device() const Returns the current paint device.
void fillPolygon(const vKisPoint &points, FillStyle fillStyle) Fill the polygon defined by points with the fillStyle.
void bltMask(TQ_INT32 dx, TQ_INT32 dy, const KisCompositeOp &op, KisPaintDeviceSP src, KisPaintDeviceSP selMask, TQ_UINT8 opacity, TQ_INT32 sx, TQ_INT32 sy, TQ_INT32 sw, TQ_INT32 sh) A version of bitBlt that renders using an external mask, ignoring the src device's own selection,...
TQ_UINT8 opacity() const Returns the opacity that is used in painting.
void paintPolygon(const vKisPoint &points) Paint the polygon with the points given in points.
void getBezierCurvePoints(const KisPoint &pos1, const KisPoint &control1, const KisPoint &control2, const KisPoint &pos2, vKisPoint &points) Fill the given vector points with the points needed to draw the Bezier curve between pos1 and pos2 us...
FillStyle fillStyle() const Returns the current fill style.
double pressure() Returns the current pressure.
void bitBlt(TQ_INT32 dx, TQ_INT32 dy, const KisCompositeOp &op, KisPaintDeviceSP src, TQ_INT32 sx, TQ_INT32 sy, TQ_INT32 sw, TQ_INT32 sh) Blast the specified region from src onto the current paint device.
void paintPolyline(const TQValueVector< KisPoint > &points, int index=0, int numPoints=-1) Paint a line that connects the dots in points.
TQRect dirtyRect() The methods below are 'higher' level than the above methods.
void beginTransaction(const TQString &customName=TQString()) Begin an undoable paint operation.
void begin(KisPaintDeviceSP device) Start painting on the specified device.
void bltSelection(TQ_INT32 dx, TQ_INT32 dy, const KisCompositeOp &op, KisPaintDeviceSP src, KisSelectionSP selMask, TQ_UINT8 opacity, TQ_INT32 sx, TQ_INT32 sy, TQ_INT32 sw, TQ_INT32 sh) A version of bitBlt that renders using an external selection mask, ignoring the src device's own sele...
void paintAt(const KisPoint &pos, const double pressure, const double, const double) Draw a spot at pos using the currently set paint op, brush and color.
KisPainter() Construct painter without a device.
void paintEllipse(const KisPoint &startPoint, const KisPoint &endPoint, const double pressure, const double, const double) Paint the ellipse with given begin and end points.
KCommand * endTransaction() Finish the undoable paint operation.
double 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=-1) Draw a Bezier curve between pos1 and pos2 using control points 1 and 2.
double 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 savedDist=-1) Draw a line between pos1 and pos2 using the currently set brush and color.
A double-based point class that can return it's coordinates approximated to integers.
A double-based rect class that can return a TQRect that encloses the KisRect.
KisSelection contains a byte-map representation of a layer, where the value of a byte signifies wheth...
|