chalk

kis_painter.cpp
1/*
2 * Copyright (c) 2002 Patrick Julien <freak@codepimps.org>
3 * Copyright (c) 2004 Boudewijn Rempt <boud@valdyas.org>
4 * Copyright (c) 2004 Clarence Dang <dang@kde.org>
5 * Copyright (c) 2004 Adrian Page <adrian@pagenet.plus.com>
6 * Copyright (c) 2004 Cyrille Berger <cberger@cberger.net>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
21 */
22#include <stdlib.h>
23#include <string.h>
24#include <cfloat>
25#include <cmath>
26#include <climits>
27#include <strings.h>
28
29#include "tqbrush.h"
30#include "tqfontinfo.h"
31#include "tqfontmetrics.h"
32#include "tqpen.h"
33#include "tqregion.h"
34#include "tqwmatrix.h"
35#include <tqimage.h>
36#include <tqmap.h>
37#include <tqpainter.h>
38#include <tqpixmap.h>
39#include <tqpointarray.h>
40#include <tqrect.h>
41#include <tqstring.h>
42
43#include <kdebug.h>
44#include <kcommand.h>
45#include <tdelocale.h>
46
47#include "kis_brush.h"
48#include "kis_debug_areas.h"
49#include "kis_image.h"
50#include "kis_layer.h"
51#include "kis_paint_device.h"
52#include "kis_painter.h"
53#include "kis_pattern.h"
54#include "kis_rect.h"
55#include "kis_colorspace.h"
56#include "kis_transaction.h"
57#include "kis_types.h"
58#include "kis_vec.h"
59#include "kis_iterators_pixel.h"
60#include "kis_paintop.h"
61#include "kis_selection.h"
62#include "kis_fill_painter.h"
63#include "kis_color.h"
64
65// Maximum distance from a Bezier control point to the line through the start
66// and end points for the curve to be considered flat.
67#define BEZIER_FLATNESS_THRESHOLD 0.5
68
73
75{
76 init();
77 Q_ASSERT(device);
79}
80
82{
83 m_transaction = 0;
84 m_paintOp = 0;
85 m_filter = 0;
86 m_brush = 0;
87 m_pattern= 0;
88 m_opacity = OPACITY_OPAQUE;
89 m_compositeOp = COMPOSITE_OVER;
90 m_dab = 0;
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;
98}
99
100KisPainter::~KisPainter()
101{
102 m_brush = 0;
103 delete m_paintOp;
104 end();
105}
106
107void KisPainter::begin(KisPaintDeviceSP device)
108{
109 if (!device) return;
110
111 if (m_transaction)
112 delete m_transaction;
113
114 m_device = device;
115 m_colorSpace = device->colorSpace();
116 m_pixelSize = device->pixelSize();
117}
118
120{
121 return endTransaction();
122}
123
124void KisPainter::beginTransaction(const TQString& customName)
125{
126 if (m_transaction)
127 delete m_transaction;
128 m_transaction = new KisTransaction(customName, m_device);
129 TQ_CHECK_PTR(m_transaction);
130}
131
132void KisPainter::beginTransaction( KisTransaction* command)
133{
134 if (m_transaction)
135 delete m_transaction;
136 m_transaction = command;
137}
138
139
141{
142 KCommand *command = m_transaction;
143 m_transaction = 0;
144 return command;
145}
146
147
149 TQRect r = m_dirtyRect;
150 m_dirtyRect = TQRect();
151 return r;
152}
153
154void KisPainter::bitBlt(TQ_INT32 dx, TQ_INT32 dy,
155 const KisCompositeOp& op,
156 KisPaintDeviceSP srcdev,
157 TQ_UINT8 opacity,
158 TQ_INT32 sx, TQ_INT32 sy,
159 TQ_INT32 sw, TQ_INT32 sh)
160{
161 if (srcdev == 0) {
162 return;
163 }
164
165 TQRect srcRect = TQRect(sx, sy, sw, sh);
166
167 if (srcdev->extentIsValid() && op != COMPOSITE_COPY) {
168 srcRect &= srcdev->extent();
169 }
170
171 if (srcRect.isEmpty()) {
172 return;
173 }
174
175 dx += srcRect.x() - sx;
176 dy += srcRect.y() - sy;
177
178 sx = srcRect.x();
179 sy = srcRect.y();
180 sw = srcRect.width();
181 sh = srcRect.height();
182
183 addDirtyRect(TQRect(dx, dy, sw, sh));
184
185 KisColorSpace * srcCs = srcdev->colorSpace();
186
187 TQ_INT32 dstY = dy;
188 TQ_INT32 srcY = sy;
189 TQ_INT32 rowsRemaining = sh;
190
191 while (rowsRemaining > 0) {
192
193 TQ_INT32 dstX = dx;
194 TQ_INT32 srcX = sx;
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);
198
199 TQ_INT32 rows = TQMIN(numContiguousDstRows, numContiguousSrcRows);
200 rows = TQMIN(rows, rowsRemaining);
201
202 while (columnsRemaining > 0) {
203
204 TQ_INT32 numContiguousDstColumns = m_device->numContiguousColumns(dstX, dstY, dstY + rows - 1);
205 TQ_INT32 numContiguousSrcColumns = srcdev->numContiguousColumns(srcX, srcY, srcY + rows - 1);
206
207 TQ_INT32 columns = TQMIN(numContiguousDstColumns, numContiguousSrcColumns);
208 columns = TQMIN(columns, columnsRemaining);
209
210 TQ_INT32 srcRowStride = srcdev->rowStride(srcX, srcY);
211 //const TQ_UINT8 *srcData = srcdev->pixel(srcX, srcY);
212 KisHLineIteratorPixel srcIt = srcdev->createHLineIterator(srcX, srcY, columns, false);
213 const TQ_UINT8 *srcData = srcIt.rawData();
214
215 //TQ_UINT8 *dstData = m_device->writablePixel(dstX, dstY);
216 TQ_INT32 dstRowStride = m_device->rowStride(dstX, dstY);
217 KisHLineIteratorPixel dstIt = m_device->createHLineIterator(dstX, dstY, columns, true);
218 TQ_UINT8 *dstData = dstIt.rawData();
219
220
221 m_colorSpace->bitBlt(dstData,
222 dstRowStride,
223 srcCs,
224 srcData,
225 srcRowStride,
226 0,
227 0,
228 opacity,
229 rows,
230 columns,
231 op);
232
233 srcX += columns;
234 dstX += columns;
235 columnsRemaining -= columns;
236 }
237
238 srcY += rows;
239 dstY += rows;
240 rowsRemaining -= rows;
241 }
242}
243
244void KisPainter::bltSelection(TQ_INT32 dx, TQ_INT32 dy,
245 const KisCompositeOp &op,
246 KisPaintDeviceSP srcdev,
247 KisSelectionSP seldev,
248 TQ_UINT8 opacity,
249 TQ_INT32 sx, TQ_INT32 sy,
250 TQ_INT32 sw, TQ_INT32 sh)
251{
252 // Better use a probablistic method than a too slow one
253 if (seldev->isProbablyTotallyUnselected(TQRect(dx, dy, sw, sh))) {
254/*
255 kdDebug() << "Blitting outside selection rect\n";
256
257 kdDebug() << "srcdev: " << srcdev << " (" << srcdev->name() << ")"
258 << ", seldev: " << seldev << " (" << seldev->name() << ")"
259 << ". dx, dy " << dx << "," << dy
260 << ". sx, sy : sw, sy " << sx << "," << sy << " : " << sw << "," << sh << endl;
261*/
262 return;
263 }
264 bltMask(dx,dy,op,srcdev,seldev.data(),opacity,sx,sy,sw,sh);
265}
266
267void KisPainter::bltMask(TQ_INT32 dx, TQ_INT32 dy,
268 const KisCompositeOp &op,
269 KisPaintDeviceSP srcdev,
270 KisPaintDeviceSP seldev,
271 TQ_UINT8 opacity,
272 TQ_INT32 sx, TQ_INT32 sy,
273 TQ_INT32 sw, TQ_INT32 sh)
274
275{
276 if (srcdev == 0) return;
277
278 if (seldev == 0) return;
279
280 if (m_device == 0) return;
281
282
283 TQRect srcRect = TQRect(sx, sy, sw, sh);
284
285 if (srcdev->extentIsValid() && op != COMPOSITE_COPY) {
286 srcRect &= srcdev->extent();
287 }
288
289 if (srcRect.isEmpty()) {
290 return;
291 }
292
293 dx += srcRect.x() - sx;
294 dy += srcRect.y() - sy;
295
296 sx = srcRect.x();
297 sy = srcRect.y();
298 sw = srcRect.width();
299 sh = srcRect.height();
300
301 addDirtyRect(TQRect(dx, dy, sw, sh));
302
303 KisColorSpace * srcCs = srcdev->colorSpace();
304
305 TQ_INT32 dstY = dy;
306 TQ_INT32 srcY = sy;
307 TQ_INT32 rowsRemaining = sh;
308
309 while (rowsRemaining > 0) {
310
311 TQ_INT32 dstX = dx;
312 TQ_INT32 srcX = sx;
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);
317
318 TQ_INT32 rows = TQMIN(numContiguousDstRows, numContiguousSrcRows);
319 rows = TQMIN(numContiguousSelRows, rows);
320 rows = TQMIN(rows, rowsRemaining);
321
322 while (columnsRemaining > 0) {
323
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);
327
328 TQ_INT32 columns = TQMIN(numContiguousDstColumns, numContiguousSrcColumns);
329 columns = TQMIN(numContiguousSelColumns, columns);
330 columns = TQMIN(columns, columnsRemaining);
331
332 //TQ_UINT8 *dstData = m_device->writablePixel(dstX, dstY);
333 TQ_INT32 dstRowStride = m_device->rowStride(dstX, dstY);
334 KisHLineIteratorPixel dstIt = m_device->createHLineIterator(dstX, dstY, columns, true);
335 TQ_UINT8 *dstData = dstIt.rawData();
336
337 //const TQ_UINT8 *srcData = srcdev->pixel(srcX, srcY);
338 TQ_INT32 srcRowStride = srcdev->rowStride(srcX, srcY);
339 KisHLineIteratorPixel srcIt = srcdev->createHLineIterator(srcX, srcY, columns, false);
340 const TQ_UINT8 *srcData = srcIt.rawData();
341
342 //const TQ_UINT8 *selData = seldev->pixel(dstX, dstY);
343 TQ_INT32 selRowStride = seldev->rowStride(dstX, dstY);
344 KisHLineIteratorPixel selIt = seldev->createHLineIterator(dstX, dstY, columns, false);
345 const TQ_UINT8 *selData = selIt.rawData();
346
347 m_colorSpace->bitBlt(dstData,
348 dstRowStride,
349 srcCs,
350 srcData,
351 srcRowStride,
352 selData,
353 selRowStride,
354 opacity,
355 rows,
356 columns,
357 op);
358
359 srcX += columns;
360 dstX += columns;
361 columnsRemaining -= columns;
362 }
363
364 srcY += rows;
365 dstY += rows;
366 rowsRemaining -= rows;
367 }
368}
369
370
371void KisPainter::bltSelection(TQ_INT32 dx, TQ_INT32 dy,
372 const KisCompositeOp& op,
373 KisPaintDeviceSP srcdev,
374 TQ_UINT8 opacity,
375 TQ_INT32 sx, TQ_INT32 sy,
376 TQ_INT32 sw, TQ_INT32 sh)
377{
378 if (m_device == 0) return;
379 if (!m_device->hasSelection()) {
380 bitBlt(dx, dy, op, srcdev, opacity, sx, sy, sw, sh);
381 }
382 else
383 bltSelection(dx,dy,op,srcdev, m_device->selection(),opacity,sx,sy,sw,sh);
384}
385
386double KisPainter::paintLine(const KisPoint & pos1,
387 const double pressure1,
388 const double xTilt1,
389 const double yTilt1,
390 const KisPoint & pos2,
391 const double pressure2,
392 const double xTilt2,
393 const double yTilt2,
394 const double inSavedDist)
395{
396 if (!m_device) return 0;
397 if (!m_paintOp) return 0;
398 if (!m_brush) return 0;
399
400 double savedDist = inSavedDist;
401 KisVector2D end(pos2);
402 KisVector2D start(pos1);
403
404 KisVector2D dragVec = end - start;
405 KisVector2D movement = dragVec;
406
407 if (savedDist < 0) {
408 m_paintOp->paintAt(pos1, KisPaintInformation(pressure1, xTilt1, yTilt1, movement));
409 savedDist = 0;
410 }
411
412 double xSpacing = 0;
413 double ySpacing = 0;
414
415 if ( m_varyBrushSpacingWithPressureWhenDrawingALine ) {
416 // XXX: The spacing should vary as the pressure changes along the
417 // line.
418 // This is a quick simplification.
419 xSpacing = m_brush->xSpacing((pressure1 + pressure2) / 2);
420 ySpacing = m_brush->ySpacing((pressure1 + pressure2) / 2);
421 }
422 else {
423 xSpacing = m_brush->xSpacing( PRESSURE_DEFAULT );
424 ySpacing = m_brush->ySpacing( PRESSURE_DEFAULT );
425 }
426
427 if (xSpacing < 0.5) {
428 xSpacing = 0.5;
429 }
430 if (ySpacing < 0.5) {
431 ySpacing = 0.5;
432 }
433
434 double xScale = 1;
435 double yScale = 1;
436 double spacing;
437 // Scale x or y so that we effectively have a square brush
438 // and calculate distance in that coordinate space. We reverse this scaling
439 // before drawing the brush. This produces the correct spacing in both
440 // x and y directions, even if the brush's aspect ratio is not 1:1.
441 if (xSpacing > ySpacing) {
442 yScale = xSpacing / ySpacing;
443 spacing = xSpacing;
444 }
445 else {
446 xScale = ySpacing / xSpacing;
447 spacing = ySpacing;
448 }
449
450 dragVec.setX(dragVec.x() * xScale);
451 dragVec.setY(dragVec.y() * yScale);
452
453 double newDist = dragVec.length();
454 double dist = savedDist + newDist;
455 double l_savedDist = savedDist;
456
457 if (dist < spacing) {
458 return dist;
459 }
460
461 dragVec.normalize();
462 KisVector2D step(0, 0);
463
464 while (dist >= spacing) {
465 if (l_savedDist > 0) {
466 step += dragVec * (spacing - l_savedDist);
467 l_savedDist -= spacing;
468 }
469 else {
470 step += dragVec * spacing;
471 }
472
473 KisPoint p(start.x() + (step.x() / xScale), start.y() + (step.y() / yScale));
474
475 double distanceMoved = step.length();
476 double t = 0;
477
478 if (newDist > DBL_EPSILON) {
479 t = distanceMoved / newDist;
480 }
481
482 double pressure = (1 - t) * pressure1 + t * pressure2;
483 double xTilt = (1 - t) * xTilt1 + t * xTilt2;
484 double yTilt = (1 - t) * yTilt1 + t * yTilt2;
485
486 m_paintOp->paintAt(p, KisPaintInformation(pressure, xTilt, yTilt, movement));
487 dist -= spacing;
488 }
489
490 if (dist > 0)
491 return dist;
492 else
493 return 0;
494}
495
496void KisPainter::paintPolyline (const vKisPoint &points,
497 int index, int numPoints)
498{
499 if (index >= (int) points.count ())
500 return;
501
502 if (numPoints < 0)
503 numPoints = points.count ();
504
505 if (index + numPoints > (int) points.count ())
506 numPoints = points.count () - index;
507
508
509 for (int i = index; i < index + numPoints - 1; i++)
510 {
511 paintLine (points [index], 0/*pressure*/, 0, 0, points [index + 1],
512 0/*pressure*/, 0, 0);
513 }
514}
515
517 const KisPoint &control1,
518 const KisPoint &control2,
519 const KisPoint &pos2,
520 vKisPoint& points)
521{
522 double d1 = pointToLineDistance(control1, pos1, pos2);
523 double d2 = pointToLineDistance(control2, pos1, pos2);
524
525 if (d1 < BEZIER_FLATNESS_THRESHOLD && d2 < BEZIER_FLATNESS_THRESHOLD) {
526 points.push_back(pos1);
527 } else {
528 // Midpoint subdivision. See Foley & Van Dam Computer Graphics P.508
529 KisVector2D p1 = pos1;
530 KisVector2D p2 = control1;
531 KisVector2D p3 = control2;
532 KisVector2D p4 = pos2;
533
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;
540 KisVector2D r1 = l4;
541 KisVector2D l1 = p1;
542 KisVector2D r4 = p4;
543
544 getBezierCurvePoints(l1.toKisPoint(), l2.toKisPoint(), l3.toKisPoint(), l4.toKisPoint(), points);
545 getBezierCurvePoints(r1.toKisPoint(), r2.toKisPoint(), r3.toKisPoint(), r4.toKisPoint(), points);
546 }
547}
548
550 const double pressure1,
551 const double xTilt1,
552 const double yTilt1,
553 const KisPoint &control1,
554 const KisPoint &control2,
555 const KisPoint &pos2,
556 const double pressure2,
557 const double xTilt2,
558 const double yTilt2,
559 const double savedDist)
560{
561 double newDistance;
562 double d1 = pointToLineDistance(control1, pos1, pos2);
563 double d2 = pointToLineDistance(control2, pos1, pos2);
564
565 if (d1 < BEZIER_FLATNESS_THRESHOLD && d2 < BEZIER_FLATNESS_THRESHOLD) {
566 newDistance = paintLine(pos1, pressure1, xTilt1, yTilt1, pos2, pressure2, xTilt2, yTilt2, savedDist);
567 } else {
568 // Midpoint subdivision. See Foley & Van Dam Computer Graphics P.508
569 KisVector2D p1 = pos1;
570 KisVector2D p2 = control1;
571 KisVector2D p3 = control2;
572 KisVector2D p4 = pos2;
573
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;
580 KisVector2D r1 = l4;
581 KisVector2D l1 = p1;
582 KisVector2D r4 = p4;
583
584 double midPressure = (pressure1 + pressure2) / 2;
585 double midXTilt = (xTilt1 + xTilt2) / 2;
586 double midYTilt = (yTilt1 + yTilt2) / 2;
587
588 newDistance = paintBezierCurve(l1.toKisPoint(), pressure1, xTilt1, yTilt1,
589 l2.toKisPoint(), l3.toKisPoint(),
590 l4.toKisPoint(), midPressure, midXTilt, midYTilt,
591 savedDist);
592 newDistance = paintBezierCurve(r1.toKisPoint(), midPressure, midXTilt, midYTilt,
593 r2.toKisPoint(),
594 r3.toKisPoint(),
595 r4.toKisPoint(), pressure2, xTilt2, yTilt2, newDistance);
596 }
597
598 return newDistance;
599}
600
601void KisPainter::paintRect (const KisPoint &startPoint,
602 const KisPoint &endPoint,
603 const double /*pressure*/,
604 const double /*xTilt*/,
605 const double /*yTilt*/)
606{
607 KoRect normalizedRect = KisRect (startPoint, endPoint).normalize ();
608
609 vKisPoint points;
610
611 points.push_back(normalizedRect.topLeft());
612 points.push_back(normalizedRect.bottomLeft());
613 points.push_back(normalizedRect.bottomRight());
614 points.push_back(normalizedRect.topRight());
615
616 paintPolygon(points);
617}
618
619void KisPainter::paintEllipse (const KisPoint &startPoint,
620 const KisPoint &endPoint,
621 const double /*pressure*/,
622 const double /*xTilt*/,
623 const double /*yTilt*/)
624{
625 KisRect r = KisRect(startPoint, endPoint).normalize();
626
627 // See http://www.whizkidtech.redprince.net/bezier/circle/ for explanation.
628 // kappa = (4/3*(sqrt(2)-1))
629 const double kappa = 0.5522847498;
630 const double lx = (r.width() / 2) * kappa;
631 const double ly = (r.height() / 2) * kappa;
632
633 KisPoint center = r.center();
634
635 KisPoint p0(r.left(), center.y());
636 KisPoint p1(r.left(), center.y() - ly);
637 KisPoint p2(center.x() - lx, r.top());
638 KisPoint p3(center.x(), r.top());
639
640 vKisPoint points;
641
642 getBezierCurvePoints(p0, p1, p2, p3, points);
643
644 KisPoint p4(center.x() + lx, r.top());
645 KisPoint p5(r.right(), center.y() - ly);
646 KisPoint p6(r.right(), center.y());
647
648 getBezierCurvePoints(p3, p4, p5, p6, points);
649
650 KisPoint p7(r.right(), center.y() + ly);
651 KisPoint p8(center.x() + lx, r.bottom());
652 KisPoint p9(center.x(), r.bottom());
653
654 getBezierCurvePoints(p6, p7, p8, p9, points);
655
656 KisPoint p10(center.x() - lx, r.bottom());
657 KisPoint p11(r.left(), center.y() + ly);
658
659 getBezierCurvePoints(p9, p10, p11, p0, points);
660
661 paintPolygon(points);
662}
663
665 const double pressure,
666 const double xTilt,
667 const double yTilt)
668{
669 if (!m_paintOp) return;
670 m_paintOp->paintAt(pos, KisPaintInformation(pressure, xTilt, yTilt, KisVector2D()));
671}
672
673double KisPainter::pointToLineDistance(const KisPoint& p, const KisPoint& l0, const KisPoint& l1)
674{
675 double lineLength = sqrt((l1.x() - l0.x()) * (l1.x() - l0.x()) + (l1.y() - l0.y()) * (l1.y() - l0.y()));
676 double distance = 0;
677
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);
681 }
682
683 return distance;
684}
685
686/*
687 * Concave Polygon Scan Conversion
688 * by Paul Heckbert
689 * from "Graphics Gems", Academic Press, 1990
690 */
691
692/*
693 * concave: scan convert nvert-sided concave non-simple polygon with vertices at
694 * (point[i].x, point[i].y) for i in [0..nvert-1] within the window win by
695 * calling spanproc for each visible span of pixels.
696 * Polygon can be clockwise or counterclockwise.
697 * Algorithm does uniform point sampling at pixel centers.
698 * Inside-outside test done by Jordan's rule: a point is considered inside if
699 * an emanating ray intersects the polygon an odd number of times.
700 * drawproc should fill in pixels from xl to xr inclusive on scanline y,
701 * e.g:
702 * drawproc(y, xl, xr)
703 * int y, xl, xr;
704 * {
705 * int x;
706 * for (x=xl; x<=xr; x++)
707 * pixel_write(x, y, pixelvalue);
708 * }
709 *
710 * Paul Heckbert 30 June 81, 18 Dec 89
711 */
712
713typedef struct { /* a polygon edge */
714 double x; /* x coordinate of edge's intersection with current scanline */
715 double dx; /* change in x with respect to y */
716 int i; /* edge number: edge i goes from pt[i] to pt[i+1] */
717} Edge;
718
719static int n; /* number of vertices */
720static const KisPoint *pt; /* vertices */
721
722static int nact; /* number of active edges */
723static Edge *active; /* active edge list:edges crossing scanline y */
724
725/* comparison routines for qsort */
726static int compare_ind(const void *pu, const void *pv)
727{
728 const int *u = static_cast<const int *>(pu);
729 const int *v = static_cast<const int *>(pv);
730
731 return pt[*u].y() <= pt[*v].y() ? -1 : 1;
732}
733
734static int compare_active(const void *pu, const void *pv)
735{
736 const Edge *u = static_cast<const Edge *>(pu);
737 const Edge *v = static_cast<const Edge *>(pv);
738
739 return u->x <= v->x ? -1 : 1;
740}
741
742static void cdelete(int i) /* remove edge i from active list */
743{
744 int j;
745
746 for (j=0; j<nact && active[j].i!=i; j++);
747 if (j>=nact) return; /* edge not in active list; happens at win->y0*/
748 nact--;
749 bcopy(&active[j+1], &active[j], (nact-j)*sizeof active[0]);
750}
751
752static void cinsert(int i, int y) /* append edge i to end of active list */
753{
754 int j;
755 double dx;
756 const KisPoint *p, *q;
757
758 j = i<n-1 ? i+1 : 0;
759 if (pt[i].y() < pt[j].y()) {
760 p = &pt[i]; q = &pt[j];
761 } else {
762 p = &pt[j]; q = &pt[i];
763 }
764 /* initialize x position at intersection of edge with scanline y */
765 active[nact].dx = dx = (q->x()-p->x())/(q->y()-p->y());
766 active[nact].x = dx*(y+.5-p->y())+p->x();
767 active[nact].i = i;
768 nact++;
769}
770
771void KisPainter::fillPolygon(const vKisPoint& points, FillStyle fillStyle)
772{
773 int nvert = points.count();
774 int k, y0, y1, y, i, j, xl, xr;
775 int *ind; /* list of vertex indices, sorted by pt[ind[j]].y */
776
777 n = nvert;
778 pt = &(points[0]);
779 if (n<3) return;
780 if (fillStyle == FillStyleNone) {
781 return;
782 }
783
784 ind = new int[n];
785 TQ_CHECK_PTR(ind);
786 active = new Edge[n];
787 TQ_CHECK_PTR(active);
788
789 /* create y-sorted array of indices ind[k] into vertex list */
790 for (k=0; k<n; k++)
791 ind[k] = k;
792 qsort(ind, n, sizeof ind[0], compare_ind); /* sort ind by pt[ind[k]].y */
793
794 nact = 0; /* start with empty active list */
795 k = 0; /* ind[k] is next vertex to process */
796 y0 = static_cast<int>(ceil(pt[ind[0]].y()-.5)); /* ymin of polygon */
797 y1 = static_cast<int>(floor(pt[ind[n-1]].y()-.5)); /* ymax of polygon */
798
799 int x0 = INT_MAX;
800 int x1 = INT_MIN;
801
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));
805
806 if (pointLowX < x0) {
807 x0 = pointLowX;
808 }
809 if (pointHighX > x1) {
810 x1 = pointHighX;
811 }
812 }
813
814 // Fill the polygon bounding rectangle with the required contents then we'll
815 // create a mask for the actual polygon coverage.
816
817 KisPaintDeviceSP polygon = new KisPaintDevice(m_device->colorSpace(), "polygon");
818 TQ_CHECK_PTR(polygon);
819
820 KisFillPainter fillPainter(polygon);
821 TQRect boundingRectangle(x0, y0, x1 - x0 + 1, y1 - y0 + 1);
822
823 // Clip to the image bounds.
824 if (m_device->image()) {
825 boundingRectangle &= m_device->image()->bounds();
826 }
827
828 switch (fillStyle) {
829 default:
830 // Fall through
831 case FillStyleGradient:
832 // Currently unsupported, fall through
833 case FillStyleStrokes:
834 // Currently unsupported, fall through
835 kdWarning(DBG_AREA_CORE) << "Unknown or unsupported fill style in fillPolygon\n";
836 case FillStyleForegroundColor:
837 fillPainter.fillRect(boundingRectangle, paintColor(), OPACITY_OPAQUE);
838 break;
839 case FillStyleBackgroundColor:
840 fillPainter.fillRect(boundingRectangle, backgroundColor(), OPACITY_OPAQUE);
841 break;
842 case FillStylePattern:
843 Q_ASSERT(m_pattern != 0);
844 fillPainter.fillRect(boundingRectangle, m_pattern);
845 break;
846 }
847
848 KisSelectionSP polygonMask = new KisSelection(polygon);
849
850 for (y=y0; y<=y1; y++) { /* step through scanlines */
851 /* scanline y is at y+.5 in continuous coordinates */
852
853 /* check vertices between previous scanline and current one, if any */
854 for (; k<n && pt[ind[k]].y()<=y+.5; k++) {
855 /* to simplify, if pt.y=y+.5, pretend it's above */
856 /* invariant: y-.5 < pt[i].y <= y+.5 */
857 i = ind[k];
858 /*
859 * insert or delete edges before and after vertex i (i-1 to i,
860 * and i to i+1) from active list if they cross scanline y
861 */
862 j = i>0 ? i-1 : n-1; /* vertex previous to i */
863 if (pt[j].y() <= y-.5) /* old edge, remove from active list */
864 cdelete(j);
865 else if (pt[j].y() > y+.5) /* new edge, add to active list */
866 cinsert(j, y);
867 j = i<n-1 ? i+1 : 0; /* vertex next after i */
868 if (pt[j].y() <= y-.5) /* old edge, remove from active list */
869 cdelete(i);
870 else if (pt[j].y() > y+.5) /* new edge, add to active list */
871 cinsert(i, y);
872 }
873
874 /* sort active edge list by active[j].x */
875 qsort(active, nact, sizeof active[0], compare_active);
876
877 /* draw horizontal segments for scanline y */
878 for (j=0; j<nact; j+=2) { /* draw horizontal segments */
879 /* span 'tween j & j+1 is inside, span tween j+1 & j+2 is outside */
880 xl = static_cast<int>(ceil(active[j].x-.5)); /* left end of span */
881 xr = static_cast<int>(floor(active[j+1].x-.5)); /* right end of span */
882
883 if (xl<=xr) {
884 KisHLineIterator it = polygonMask->createHLineIterator(xl, y, xr - xl + 1, true);
885
886 while (!it.isDone()) {
887 // We're using a selection here, that means alpha colorspace, that means one byte.
888 it.rawData()[0] = MAX_SELECTED;
889 ++it;
890 }
891 }
892
893 active[j].x += active[j].dx; /* increment edge coords */
894 active[j+1].x += active[j+1].dx;
895 }
896 }
897 delete [] ind;
898 delete [] active;
899
900 polygon->applySelectionMask(polygonMask);
901
902 TQRect r = polygon->extent();
903
904 // The strokes for the outline may have already added updated the dirtyrect, but it can't hurt,
905 // and if we're painting without outlines, then there will be no dirty rect. Let's do it ourselves...
906 // addDirtyRect( r ); // XXX the bltSelection will add to the dirtyrect
907
908 bltSelection(r.x(), r.y(), compositeOp(), polygon, opacity(), r.x(), r.y(), r.width(), r.height());
909}
910
911void KisPainter::paintPolygon(const vKisPoint& points)
912{
913 if (m_fillStyle != FillStyleNone) {
914 fillPolygon(points, m_fillStyle);
915 }
916
917 if (m_strokeStyle != StrokeStyleNone) {
918 if (points.count() > 1) {
919 double distance = -1;
920
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);
923 }
924 paintLine(points[points.count() - 1], PRESSURE_DEFAULT, 0, 0, points[0], PRESSURE_DEFAULT, 0, 0, distance);
925 }
926 }
927}
928
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.
This class keeps information that can be used in the painting process, for example by brushes.
Definition kis_paintop.h:50
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.
Definition kis_painter.h:95
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.
Definition kis_point.h:28
A double-based rect class that can return a TQRect that encloses the KisRect.
Definition kis_rect.h:29
KisSelection contains a byte-map representation of a layer, where the value of a byte signifies wheth...