3 Copyright (c) 2003-2007 Clarence Dang <dang@kde.org>
6 Redistribution and use in source and binary forms, with or without
7 modification, are permitted provided that the following conditions
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>
36 #include <qapplication.h>
38 #include <qdatetime.h>
41 #include <qpainterpath.h>
48 #include <kconfiggroup.h>
52 #include <kmessagebox.h>
54 #include <kpAbstractSelection.h>
60 // Returns whether there is only 1 distinct point in <points>.
61 static bool Only1PixelInPointArray (const QPolygon
&points
)
63 if (points
.count () == 0)
66 for (int i
= 1; i
< (int) points
.count (); i
++)
68 if (points
[i
] != points
[0])
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.:
87 // compared to Qt's special "width 0" which just means a "proper" width 1:
94 static int WidthToQPenWidth (int width
, bool drawingEllipse
= false)
98 // 3x10 ellipse with Qt width 0 looks like rectangle.
99 // Therefore, do not apply this 1 -> 0 transformations for ellipses.
102 // Closer to looking width 1, for lines at least.
111 static void QPainterSetPenWithStipple (QPainter
*p
,
112 bool drawingOnRGBLayer
,
113 const kpColor
&fColor
,
115 const kpColor
&fStippleColor
= kpColor::Invalid
,
116 bool isEllipseLike
= false)
118 if (!fStippleColor
.isValid ())
121 kpPixmapFX::QPainterDrawLinePen (
122 kpPixmapFX::draw_ToQColor (fColor
, drawingOnRGBLayer
),
123 ::WidthToQPenWidth (penWidth
, isEllipseLike
)));
127 QPen usePen
= kpPixmapFX::QPainterDrawLinePen (
128 kpPixmapFX::draw_ToQColor (fColor
, drawingOnRGBLayer
),
129 ::WidthToQPenWidth (penWidth
, isEllipseLike
));
130 usePen
.setStyle (Qt::DashLine
);
133 p
->setBackground (kpPixmapFX::draw_ToQColor (fStippleColor
, drawingOnRGBLayer
));
134 p
->setBackgroundMode (Qt::OpaqueMode
);
140 QPen
kpPixmapFX::QPainterDrawRectPen (const QColor
&color
, int qtWidth
)
142 return QPen (color
, qtWidth
, Qt::SolidLine
, Qt::SquareCap
, Qt::MiterJoin
);
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
161 kpColor stippleColor
;
164 static void DrawPolylineHelper (QPainter
*p
,
165 bool drawingOnRGBLayer
,
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
177 ::QPainterSetPenWithStipple (p
, drawingOnRGBLayer
,
178 pack
->color
, pack
->penWidth
,
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";
187 p
->drawPoint (pack
->points
[0]);
191 p
->drawPolyline (pack
->points
);
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
;
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 ()),
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
)
219 points
.append (QPoint (x1
, y1
));
220 points
.append (QPoint (x2
, y2
));
233 struct DrawPolygonPackage
240 kpColor fStippleColor
;
243 static void DrawPolygonHelper (QPainter
*p
,
244 bool drawingOnRGBLayer
,
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
254 << (int *) (pack
->bcolor
.isValid () ?
255 pack
->bcolor
.toQRgb () :
257 << " isFinal=" << pack
->isFinal
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.
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";
277 p
->drawPoint (pack
->points
[0]);
281 // TODO: why aren't the ends rounded?
282 p
->drawPolygon (pack
->points
, Qt::OddEvenFill
);
289 int count
= pointsInRect
.count ();
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 ();
308 if (kpTool::isColorTransparent (foregroundColor
))
309 XORpen
.setColor (Qt::color1
/*opaque*/);
311 XORpen
.setColor (Qt::color0
/*transparent*/);
314 maskPainter
.setPen (XORpen
);
317 PAINTER_CALL (drawLine (pointsInRect
[0], pointsInRect
[count
- 1]));
322 void kpPixmapFX::drawPolygon (QPixmap
*image
,
323 const QPolygon
&points
,
324 const kpColor
&fcolor
, int penWidth
,
325 const kpColor
&bcolor
,
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 ()),
352 struct DrawCurvePackage
355 controlPointP
, controlPointQ
,
361 static void DrawCurveHelper (QPainter
*p
,
362 bool drawingOnRGBLayer
,
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
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";
388 p
->drawPoint (pack
->startPoint
);
392 QPainterPath curvePath
;
393 curvePath
.moveTo (pack
->startPoint
);
394 curvePath
.cubicTo (pack
->controlPointP
,
398 p
->strokePath (curvePath
, p
->pen ());
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
;
414 pack
.penWidth
= penWidth
;
416 kpPixmapFX::draw (image
, &::DrawCurveHelper
,
417 color
.isOpaque (), color
.isTransparent (),
426 struct FillRectPackage
428 int x
, y
, width
, height
;
430 kpColor stippleColor
;
433 static void FillRectHelper (QPainter
*p
,
434 bool drawingOnRGBLayer
,
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
));
446 const int StippleSize
= 4;
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;
459 useColor
= pack
->color
;
461 useColor
= pack
->stippleColor
;
463 p
->fillRect (pack
->x
+ dx
, pack
->y
+ dy
, StippleSize
, StippleSize
,
464 kpPixmapFX::draw_ToQColor (useColor
, drawingOnRGBLayer
));
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
;
481 pack
.stippleColor
= stippleColor
;
483 kpPixmapFX::draw (image
, &::FillRectHelper
,
484 color
.isOpaque () || (stippleColor
.isValid () && stippleColor
.isOpaque ()),
485 color
.isTransparent () || (stippleColor
.isValid () && stippleColor
.isTransparent ()),
494 struct DrawGenericRectPackage
496 int x
, y
, width
, height
;
497 void (*func
) (QPainter
* /*p*/,
498 int /*x*/, int /*y*/,
499 int /*width*/, int/*height*/);
503 kpColor fStippleColor
;
507 static void DrawGenericRectHelper (QPainter
*p
,
508 bool drawingOnRGBLayer
,
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
521 << (int *) (pack
->bcolor
.isValid () ?
522 pack
->bcolor
.toQRgb () :
524 << " isEllipseLike=" << pack
->isEllipseLike
528 ::QPainterSetPenWithStipple (p
, drawingOnRGBLayer
,
529 pack
->fcolor
, pack
->penWidth
,
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.
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.
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
,
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
566 << (int *) (bcolor
.isValid () ?
569 << " isEllipseLike=" << isEllipseLike
574 Q_ASSERT (width
> 0);
575 Q_ASSERT (height
> 0);
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";
590 kpPixmapFX::drawLine (image
,
591 x
, y
, x
+ width
- 1, y
+ height
- 1,
592 fcolor
, 1/*force pen width to 1*/,
598 DrawGenericRectPackage pack
;
599 pack
.x
= x
, pack
.y
= y
, pack
.width
= width
, pack
.height
= height
;
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";
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.
616 pack
.bcolor
= fcolor
; // Outline colour.
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 ()),
640 static void DrawRectHelper (QPainter
*p
,
641 int x
, int y
, int width
, int height
)
643 p
->drawRect (x
, y
, width
, height
);
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
,
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
);
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
,
681 &::DrawRoundedRectHelper
,
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
);
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
,
704 &::DrawEllipseHelper
,
708 true/*ellipse like*/);