compile
[kdegraphics.git] / kolourpaint / pixmapfx / kpPixmapFX_DrawShapes.cpp
blob14e69bd1dac32254f1bd3f4e8d83a595f413e2e4
2 /*
3 Copyright (c) 2003-2007 Clarence Dang <dang@kde.org>
4 All rights reserved.
6 Redistribution and use in source and binary forms, with or without
7 modification, are permitted provided that the following conditions
8 are met:
10 1. Redistributions of source code must retain the above copyright
11 notice, this list of conditions and the following disclaimer.
12 2. Redistributions in binary form must reproduce the above copyright
13 notice, this list of conditions and the following disclaimer in the
14 documentation and/or other materials provided with the distribution.
16 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18 OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19 IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25 THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 #define DEBUG_KP_PIXMAP_FX 0
32 #include <kpPixmapFX.h>
34 #include <math.h>
36 #include <qapplication.h>
37 #include <qbitmap.h>
38 #include <qdatetime.h>
39 #include <qimage.h>
40 #include <qpainter.h>
41 #include <qpainterpath.h>
42 #include <qpixmap.h>
43 #include <qpoint.h>
44 #include <qpolygon.h>
45 #include <qrect.h>
47 #include <kconfig.h>
48 #include <kconfiggroup.h>
49 #include <kdebug.h>
50 #include <kglobal.h>
51 #include <klocale.h>
52 #include <kmessagebox.h>
54 #include <kpAbstractSelection.h>
55 #include <kpColor.h>
56 #include <kpDefs.h>
57 #include <kpTool.h>
60 // Returns whether there is only 1 distinct point in <points>.
61 static bool Only1PixelInPointArray (const QPolygon &points)
63 if (points.count () == 0)
64 return false;
66 for (int i = 1; i < (int) points.count (); i++)
68 if (points [i] != points [0])
69 return false;
72 return true;
76 // Warp the given <width> from 1 to 0.
77 // This is not always done (specifically if <drawingEllipse>) because
78 // width 0 sometimes looks worse.
80 // Qt lines of width 1 look like they have a width between 1-2 i.e.:
82 // #
83 // ##
84 // #
85 // #
87 // compared to Qt's special "width 0" which just means a "proper" width 1:
89 // #
90 // #
91 // #
92 // #
94 static int WidthToQPenWidth (int width, bool drawingEllipse = false)
96 if (width == 1)
98 // 3x10 ellipse with Qt width 0 looks like rectangle.
99 // Therefore, do not apply this 1 -> 0 transformations for ellipses.
100 if (!drawingEllipse)
102 // Closer to looking width 1, for lines at least.
103 return 0;
107 return width;
111 static void QPainterSetPenWithStipple (QPainter *p,
112 bool drawingOnRGBLayer,
113 const kpColor &fColor,
114 int penWidth,
115 const kpColor &fStippleColor = kpColor::Invalid,
116 bool isEllipseLike = false)
118 if (!fStippleColor.isValid ())
120 p->setPen (
121 kpPixmapFX::QPainterDrawLinePen (
122 kpPixmapFX::draw_ToQColor (fColor, drawingOnRGBLayer),
123 ::WidthToQPenWidth (penWidth, isEllipseLike)));
125 else
127 QPen usePen = kpPixmapFX::QPainterDrawLinePen (
128 kpPixmapFX::draw_ToQColor (fColor, drawingOnRGBLayer),
129 ::WidthToQPenWidth (penWidth, isEllipseLike));
130 usePen.setStyle (Qt::DashLine);
131 p->setPen (usePen);
133 p->setBackground (kpPixmapFX::draw_ToQColor (fStippleColor, drawingOnRGBLayer));
134 p->setBackgroundMode (Qt::OpaqueMode);
139 // public static
140 QPen kpPixmapFX::QPainterDrawRectPen (const QColor &color, int qtWidth)
142 return QPen (color, qtWidth, Qt::SolidLine, Qt::SquareCap, Qt::MiterJoin);
145 // public static
146 QPen kpPixmapFX::QPainterDrawLinePen (const QColor &color, int qtWidth)
148 return QPen (color, qtWidth, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin);
153 // drawPolyline() / drawLine()
156 struct DrawPolylinePackage
158 QPolygon points;
159 kpColor color;
160 int penWidth;
161 kpColor stippleColor;
164 static void DrawPolylineHelper (QPainter *p,
165 bool drawingOnRGBLayer,
166 void *data)
168 DrawPolylinePackage *pack = static_cast <DrawPolylinePackage *> (data);
170 #if DEBUG_KP_PIXMAP_FX
171 kDebug () << "DrawPolylineHelper() points=" << pack->points.toList ()
172 << " color=" << (int *) pack->color.toQRgb ()
173 << " penWidth=" << pack->penWidth
174 << endl;
175 #endif
177 ::QPainterSetPenWithStipple (p, drawingOnRGBLayer,
178 pack->color, pack->penWidth,
179 pack->stippleColor);
181 // Qt bug: single point doesn't show up depending on penWidth.
182 if (Only1PixelInPointArray (pack->points))
184 #if DEBUG_KP_PIXMAP_FX
185 kDebug () << "\tinvoking single point hack";
186 #endif
187 p->drawPoint (pack->points [0]);
188 return;
191 p->drawPolyline (pack->points);
194 // public static
195 void kpPixmapFX::drawPolyline (QPixmap *image,
196 const QPolygon &points,
197 const kpColor &color, int penWidth,
198 const kpColor &stippleColor)
200 DrawPolylinePackage pack;
201 pack.points = points;
202 pack.color = color;
203 pack.penWidth = penWidth;
204 pack.stippleColor = stippleColor;
206 kpPixmapFX::draw (image, &::DrawPolylineHelper,
207 color.isOpaque () || (stippleColor.isValid () && stippleColor.isOpaque ()),
208 color.isTransparent () || (stippleColor.isValid () && stippleColor.isTransparent ()),
209 &pack);
212 // public static
213 void kpPixmapFX::drawLine (QPixmap *image,
214 int x1, int y1, int x2, int y2,
215 const kpColor &color, int penWidth,
216 const kpColor &stippleColor)
218 QPolygon points;
219 points.append (QPoint (x1, y1));
220 points.append (QPoint (x2, y2));
222 drawPolyline (image,
223 points,
224 color, penWidth,
225 stippleColor);
230 // drawPolygon()
233 struct DrawPolygonPackage
235 QPolygon points;
236 kpColor fcolor;
237 int penWidth;
238 kpColor bcolor;
239 bool isFinal;
240 kpColor fStippleColor;
243 static void DrawPolygonHelper (QPainter *p,
244 bool drawingOnRGBLayer,
245 void *data)
247 DrawPolygonPackage *pack = static_cast <DrawPolygonPackage *> (data);
249 #if DEBUG_KP_PIXMAP_FX
250 kDebug () << "DrawPolygonHelper() points=" << pack->points.toList ()
251 << " fcolor=" << (int *) pack->fcolor.toQRgb ()
252 << " penWidth=" << pack->penWidth
253 << " bcolor="
254 << (int *) (pack->bcolor.isValid () ?
255 pack->bcolor.toQRgb () :
256 0xabadcafe)
257 << " isFinal=" << pack->isFinal
258 << endl;
259 #endif
261 ::QPainterSetPenWithStipple (p, drawingOnRGBLayer,
262 pack->fcolor, pack->penWidth,
263 pack->fStippleColor);
265 if (pack->bcolor.isValid ())
266 p->setBrush (QBrush (kpPixmapFX::draw_ToQColor (pack->bcolor, drawingOnRGBLayer)));
267 // HACK: seems to be needed if set_Pen_(Qt::color0) else fills with Qt::color0.
268 else
269 p->setBrush (Qt::NoBrush);
271 // Qt bug: single point doesn't show up depending on penWidth.
272 if (Only1PixelInPointArray (pack->points))
274 #if DEBUG_KP_PIXMAP_FX
275 kDebug () << "\tinvoking single point hack";
276 #endif
277 p->drawPoint (pack->points [0]);
278 return;
281 // TODO: why aren't the ends rounded?
282 p->drawPolygon (pack->points, Qt::OddEvenFill);
284 // TODO
285 #if 0
286 if (pack->isFinal)
287 return;
289 int count = pointsInRect.count ();
290 if (count <= 2)
291 return;
293 if (painter.isActive ())
295 QPen XORpen = painter.pen ();
296 XORpen.setColor (Qt::white);
298 painter.setPen (XORpen);
299 painter.setRasterOp (Qt::XorROP);
302 if (maskPainter.isActive ())
304 QPen XORpen = maskPainter.pen ();
306 // TODO???
307 #if 0
308 if (kpTool::isColorTransparent (foregroundColor))
309 XORpen.setColor (Qt::color1/*opaque*/);
310 else
311 XORpen.setColor (Qt::color0/*transparent*/);
312 #endif
314 maskPainter.setPen (XORpen);
317 PAINTER_CALL (drawLine (pointsInRect [0], pointsInRect [count - 1]));
318 #endif
321 // public static
322 void kpPixmapFX::drawPolygon (QPixmap *image,
323 const QPolygon &points,
324 const kpColor &fcolor, int penWidth,
325 const kpColor &bcolor,
326 bool isFinal,
327 const kpColor &fStippleColor)
329 DrawPolygonPackage pack;
330 pack.points = points;
331 pack.fcolor = fcolor;
332 pack.penWidth = penWidth;
333 pack.bcolor = bcolor;
334 pack.isFinal = isFinal;
335 pack.fStippleColor = fStippleColor,
337 kpPixmapFX::draw (image, &::DrawPolygonHelper,
338 fcolor.isOpaque () ||
339 (bcolor.isValid () && bcolor.isOpaque ()) ||
340 (fStippleColor.isValid () && fStippleColor.isOpaque ()),
341 fcolor.isTransparent () ||
342 (bcolor.isValid () && bcolor.isTransparent ()) ||
343 (fStippleColor.isValid () && fStippleColor.isTransparent ()),
344 &pack);
349 // drawCurve()
352 struct DrawCurvePackage
354 QPoint startPoint,
355 controlPointP, controlPointQ,
356 endPoint;
357 kpColor color;
358 int penWidth;
361 static void DrawCurveHelper (QPainter *p,
362 bool drawingOnRGBLayer,
363 void *data)
365 DrawCurvePackage *pack = static_cast <DrawCurvePackage *> (data);
367 #if DEBUG_KP_PIXMAP_FX
368 kDebug () << "DrawCurveHelper() pack: startPoint=" << pack->startPoint
369 << " controlPointP=" << pack->controlPointP
370 << " controlPointQ=" << pack->controlPointQ
371 << " endPoint=" << pack->endPoint
372 << " color=" << (int *) pack->color.toQRgb ()
373 << " penWidth=" << pack->penWidth
374 << endl;
375 #endif
377 ::QPainterSetPenWithStipple (p, drawingOnRGBLayer,
378 pack->color, pack->penWidth);
380 // SYNC: Qt bug: single point doesn't show up depending on penWidth.
381 if (pack->startPoint == pack->controlPointP &&
382 pack->controlPointP == pack->controlPointQ &&
383 pack->controlPointQ == pack->endPoint)
385 #if DEBUG_KP_PIXMAP_FX
386 kDebug () << "\tinvoking single point hack";
387 #endif
388 p->drawPoint (pack->startPoint);
389 return;
392 QPainterPath curvePath;
393 curvePath.moveTo (pack->startPoint);
394 curvePath.cubicTo (pack->controlPointP,
395 pack->controlPointQ,
396 pack->endPoint);
398 p->strokePath (curvePath, p->pen ());
401 // public static
402 void kpPixmapFX::drawCurve (QPixmap *image,
403 const QPoint &startPoint,
404 const QPoint &controlPointP, const QPoint &controlPointQ,
405 const QPoint &endPoint,
406 const kpColor &color, int penWidth)
408 DrawCurvePackage pack;
409 pack.startPoint = startPoint;
410 pack.controlPointP = controlPointP;
411 pack.controlPointQ = controlPointQ;
412 pack.endPoint = endPoint;
413 pack.color = color;
414 pack.penWidth = penWidth;
416 kpPixmapFX::draw (image, &::DrawCurveHelper,
417 color.isOpaque (), color.isTransparent (),
418 &pack);
423 // fillRect()
426 struct FillRectPackage
428 int x, y, width, height;
429 kpColor color;
430 kpColor stippleColor;
433 static void FillRectHelper (QPainter *p,
434 bool drawingOnRGBLayer,
435 void *data)
437 FillRectPackage *pack = static_cast <FillRectPackage *> (data);
439 if (!pack->stippleColor.isValid ())
441 p->fillRect (pack->x, pack->y, pack->width, pack->height,
442 kpPixmapFX::draw_ToQColor (pack->color, drawingOnRGBLayer));
444 else
446 const int StippleSize = 4;
448 p->save ();
449 p->setClipRect (pack->x, pack->y, pack->width, pack->height);
451 for (int dy = 0; dy < pack->height; dy += StippleSize)
453 for (int dx = 0; dx < pack->width; dx += StippleSize)
455 const bool parity = ((dy + dx) / StippleSize) % 2;
457 kpColor useColor;
458 if (!parity)
459 useColor = pack->color;
460 else
461 useColor = pack->stippleColor;
463 p->fillRect (pack->x + dx, pack->y + dy, StippleSize, StippleSize,
464 kpPixmapFX::draw_ToQColor (useColor, drawingOnRGBLayer));
468 p->restore ();
472 // public static
473 void kpPixmapFX::fillRect (QPixmap *image,
474 int x, int y, int width, int height,
475 const kpColor &color,
476 const kpColor &stippleColor)
478 FillRectPackage pack;
479 pack.x = x, pack.y = y, pack.width = width, pack.height = height;
480 pack.color = color;
481 pack.stippleColor = stippleColor;
483 kpPixmapFX::draw (image, &::FillRectHelper,
484 color.isOpaque () || (stippleColor.isValid () && stippleColor.isOpaque ()),
485 color.isTransparent () || (stippleColor.isValid () && stippleColor.isTransparent ()),
486 &pack);
491 // DrawGenericRect()
494 struct DrawGenericRectPackage
496 int x, y, width, height;
497 void (*func) (QPainter * /*p*/,
498 int /*x*/, int /*y*/,
499 int /*width*/, int/*height*/);
500 kpColor fcolor;
501 int penWidth;
502 kpColor bcolor;
503 kpColor fStippleColor;
504 bool isEllipseLike;
507 static void DrawGenericRectHelper (QPainter *p,
508 bool drawingOnRGBLayer,
509 void *data)
511 DrawGenericRectPackage *pack = static_cast <DrawGenericRectPackage *> (data);
513 #if DEBUG_KP_PIXMAP_FX
514 kDebug () << "\tkppixmapfx.cpp:DrawGenericRectHelper(drawingOnRGBLayer="
515 << drawingOnRGBLayer << ") pack: "
516 << pack->x << "," << pack->y << ","
517 << pack->width << "," << pack->height << ",func=" << pack->func << ")"
518 << " pen.color=" << (int *) pack->fcolor.toQRgb ()
519 << " penWidth=" << pack->penWidth
520 << " bcolor="
521 << (int *) (pack->bcolor.isValid () ?
522 pack->bcolor.toQRgb () :
523 0xabadcafe)
524 << " isEllipseLike=" << pack->isEllipseLike
525 << endl;
526 #endif
528 ::QPainterSetPenWithStipple (p, drawingOnRGBLayer,
529 pack->fcolor, pack->penWidth,
530 pack->fStippleColor,
531 pack->isEllipseLike);
533 if (pack->bcolor.isValid ())
534 p->setBrush (QBrush (kpPixmapFX::draw_ToQColor (pack->bcolor, drawingOnRGBLayer)));
535 // HACK: seems to be needed if set_Pen_(Qt::color0) else fills with Qt::color0.
536 else
537 p->setBrush (Qt::NoBrush);
539 // Fight Qt behaviour of painting width = fill width + pen width
540 // and height = fill height + pen height. Get rid of pen width.
541 (*pack->func) (p,
542 pack->x + pack->penWidth / 2,
543 pack->y + pack->penWidth / 2,
544 pack->width - pack->penWidth,
545 pack->height - pack->penWidth);
548 // Calls to drawRect(), drawRoundedRect() and drawEllipse() are
549 // forwarded here. <func> is the respective QPainter function and
550 // may or may not be called.
551 static void DrawGenericRect (QPixmap *image,
552 int x, int y, int width, int height,
553 void (*func) (QPainter * /*p*/, int /*x*/, int /*y*/,
554 int /*width*/, int/*height*/),
555 const kpColor &fcolor, int penWidth,
556 const kpColor &bcolor,
557 const kpColor &fStippleColor,
558 bool isEllipseLike)
560 #if DEBUG_KP_PIXMAP_FX
561 kDebug () << "kppixmapfx.cpp:DrawGenericRect(" << x << "," << y << ","
562 << width << "," << height << ",func=" << func << ")"
563 << " pen.color=" << (int *) fcolor.toQRgb ()
564 << " penWidth=" << penWidth
565 << " bcolor="
566 << (int *) (bcolor.isValid () ?
567 bcolor.toQRgb () :
568 0xabadcafe)
569 << " isEllipseLike=" << isEllipseLike
570 << endl;
571 #endif
574 Q_ASSERT (width > 0);
575 Q_ASSERT (height > 0);
577 Q_ASSERT (func);
579 // Check foreground colour valid.
580 // Background is allowed to be invalid (no fill).
581 Q_ASSERT (fcolor.isValid ());
584 if (width == 1 || height == 1)
586 #if DEBUG_KP_PIXMAP_FX
587 kDebug () << "\twidth=1 or height=1 - draw line";
588 #endif
590 kpPixmapFX::drawLine (image,
591 x, y, x + width - 1, y + height - 1,
592 fcolor, 1/*force pen width to 1*/,
593 fStippleColor);
594 return;
598 DrawGenericRectPackage pack;
599 pack.x = x, pack.y = y, pack.width = width, pack.height = height;
600 pack.func = func;
601 pack.fcolor = fcolor;
603 // Outline is so big that fill won't be seen?
604 if (penWidth * 2 >= width || penWidth * 2 >= height)
606 #if DEBUG_KP_PIXMAP_FX
607 kDebug () << "\toutline dominates fill - fill with outline";
608 #endif
610 // Fill with outline.
611 // TODO: doesn't emulate non-Qt::SolidLine pens
612 // TODO: Transition from this hack to normal drawing makes the
613 // ellipse look like it moves 1 pixel to the right due to
614 // Qt missing a pixel on the left of some sizes of ellipses.
615 pack.penWidth = 1;
616 pack.bcolor = fcolor; // Outline colour.
618 else
620 pack.penWidth = penWidth;
621 pack.bcolor = bcolor;
624 pack.fStippleColor = fStippleColor;
626 pack.isEllipseLike = isEllipseLike;
629 kpPixmapFX::draw (image, &::DrawGenericRectHelper,
630 fcolor.isOpaque () ||
631 (bcolor.isValid () && bcolor.isOpaque ()) ||
632 (fStippleColor.isValid () && fStippleColor.isOpaque ()),
633 fcolor.isTransparent () ||
634 (bcolor.isValid () && bcolor.isTransparent ()) ||
635 (fStippleColor.isValid () && fStippleColor.isTransparent ()),
636 &pack);
640 static void DrawRectHelper (QPainter *p,
641 int x, int y, int width, int height)
643 p->drawRect (x, y, width, height);
646 // public static
647 void kpPixmapFX::drawRect (QPixmap *image,
648 int x, int y, int width, int height,
649 const kpColor &fcolor, int penWidth,
650 const kpColor &bcolor,
651 const kpColor &fStippleColor)
653 ::DrawGenericRect (image,
654 x, y, width, height,
655 &::DrawRectHelper,
656 fcolor, penWidth,
657 bcolor,
658 fStippleColor,
659 false/*not ellipse-like*/);
663 static void DrawRoundedRectHelper (QPainter *p,
664 int x, int y, int width, int height)
666 // (has default arguments for the roundness i.e. different prototype
667 // to QPainter::draw{Rect,Ellipse}(), therefore need pointer to these
668 // helpers instead of directly to a QPainter member function)
669 p->drawRoundRect (x, y, width, height);
672 // public static
673 void kpPixmapFX::drawRoundedRect (QPixmap *image,
674 int x, int y, int width, int height,
675 const kpColor &fcolor, int penWidth,
676 const kpColor &bcolor,
677 const kpColor &fStippleColor)
679 ::DrawGenericRect (image,
680 x, y, width, height,
681 &::DrawRoundedRectHelper,
682 fcolor, penWidth,
683 bcolor,
684 fStippleColor,
685 true/*ellipse like*/);
689 static void DrawEllipseHelper (QPainter *p,
690 int x, int y, int width, int height)
692 p->drawEllipse (x, y, width, height);
695 // public static
696 void kpPixmapFX::drawEllipse (QPixmap *image,
697 int x, int y, int width, int height,
698 const kpColor &fcolor, int penWidth,
699 const kpColor &bcolor,
700 const kpColor &fStippleColor)
702 ::DrawGenericRect (image,
703 x, y, width, height,
704 &::DrawEllipseHelper,
705 fcolor, penWidth,
706 bcolor,
707 fStippleColor,
708 true/*ellipse like*/);