File indexing completed on 2024-04-28 04:20:22

0001 
0002 /*
0003    Copyright (c) 2003-2007 Clarence Dang <dang@kde.org>
0004    All rights reserved.
0005 
0006    Redistribution and use in source and binary forms, with or without
0007    modification, are permitted provided that the following conditions
0008    are met:
0009 
0010    1. Redistributions of source code must retain the above copyright
0011       notice, this list of conditions and the following disclaimer.
0012    2. Redistributions in binary form must reproduce the above copyright
0013       notice, this list of conditions and the following disclaimer in the
0014       documentation and/or other materials provided with the distribution.
0015 
0016    THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
0017    IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
0018    OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
0019    IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
0020    INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
0021    NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
0022    DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
0023    THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
0024    (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
0025    THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
0026 */
0027 
0028 
0029 #define DEBUG_KP_PIXMAP_FX 0
0030 
0031 
0032 #include "kpPixmapFX.h"
0033 
0034 
0035 #include <QPainter>
0036 #include <QImage>
0037 #include <QPolygon>
0038 
0039 #include "kpLogCategories.h"
0040 
0041 #include "layers/selections/kpAbstractSelection.h"
0042 #include "imagelib/kpColor.h"
0043 #include "kpDefs.h"
0044 
0045 //---------------------------------------------------------------------
0046 
0047 // Returns whether there is only 1 distinct point in <points>.
0048 bool kpPixmapFX::Only1PixelInPointArray (const QPolygon &points)
0049 {
0050     if (points.count () == 0) {
0051         return false;
0052     }
0053 
0054     for (int i = 1; i < static_cast<int> (points.count ()); i++)
0055     {
0056         if (points [i] != points [0]) {
0057             return false;
0058         }
0059     }
0060 
0061     return true;
0062 }
0063 
0064 //---------------------------------------------------------------------
0065 
0066 // Warp the given <width> from 1 to 0.
0067 // This is not always done (specifically if <drawingEllipse>) because
0068 // width 0 sometimes looks worse.
0069 //
0070 // Qt lines of width 1 look like they have a width between 1-2 i.e.:
0071 //
0072 // #
0073 //  ##
0074 //   #
0075 //    #
0076 //
0077 // compared to Qt's special "width 0" which just means a "proper" width 1:
0078 //
0079 // #
0080 //  #
0081 //   #
0082 //    #
0083 //
0084 static int WidthToQPenWidth (int width, bool drawingEllipse = false)
0085 {
0086     if (width == 1)
0087     {
0088         // 3x10 ellipse with Qt width 0 looks like rectangle.
0089         // Therefore, do not apply this 1 -> 0 transformations for ellipses.
0090         if (!drawingEllipse)
0091         {
0092             // Closer to looking width 1, for lines at least.
0093             return 0;
0094         }
0095     }
0096 
0097     return width;
0098 }
0099 
0100 //---------------------------------------------------------------------
0101 
0102 static void QPainterSetPenWithStipple (QPainter *p,
0103         const kpColor &fColor,
0104         int penWidth,
0105         const kpColor &fStippleColor = kpColor::Invalid,
0106         bool isEllipseLike = false)
0107 {
0108     if (!fStippleColor.isValid ())
0109     {
0110         p->setPen (
0111            kpPixmapFX::QPainterDrawLinePen (
0112                 fColor.toQColor(),
0113                 ::WidthToQPenWidth (penWidth, isEllipseLike)));
0114     }
0115     else
0116     {
0117         QPen usePen = kpPixmapFX::QPainterDrawLinePen (
0118             fColor.toQColor(),
0119             ::WidthToQPenWidth (penWidth, isEllipseLike));
0120         usePen.setStyle (Qt::DashLine);
0121         p->setPen (usePen);
0122 
0123         p->setBackground (fStippleColor.toQColor());
0124         p->setBackgroundMode (Qt::OpaqueMode);
0125     }
0126 }
0127 
0128 //---------------------------------------------------------------------
0129 
0130 // public static
0131 QPen kpPixmapFX::QPainterDrawRectPen (const QColor &color, int qtWidth)
0132 {
0133     return QPen (color, qtWidth, Qt::SolidLine, Qt::SquareCap, Qt::MiterJoin);
0134 }
0135 
0136 //---------------------------------------------------------------------
0137 
0138 // public static
0139 QPen kpPixmapFX::QPainterDrawLinePen (const QColor &color, int qtWidth)
0140 {
0141     return QPen (color, qtWidth, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin);
0142 }
0143 
0144 //---------------------------------------------------------------------
0145 //
0146 // drawPolyline() / drawLine()
0147 //
0148 
0149 // public static
0150 void kpPixmapFX::drawPolyline (QImage *image,
0151         const QPolygon &points,
0152         const kpColor &color, int penWidth,
0153         const kpColor &stippleColor)
0154 {
0155     QPainter painter(image);
0156 
0157     ::QPainterSetPenWithStipple(&painter,
0158         color, penWidth,
0159         stippleColor);
0160     
0161     // Qt bug: single point doesn't show up depending on penWidth.
0162     if (Only1PixelInPointArray(points))
0163     {
0164     #if DEBUG_KP_PIXMAP_FX
0165         qCDebug(kpLogPixmapfx) << "\tinvoking single point hack";
0166     #endif
0167         painter.drawPoint(points[0]);
0168         return;
0169     }
0170     
0171     painter.drawPolyline(points);
0172 }
0173 
0174 //---------------------------------------------------------------------
0175 //
0176 // drawPolygon()
0177 //
0178 
0179 // public static
0180 void kpPixmapFX::drawPolygon (QImage *image,
0181         const QPolygon &points,
0182         const kpColor &fcolor, int penWidth,
0183         const kpColor &bcolor,
0184         bool isFinal,
0185         const kpColor &fStippleColor)
0186 {
0187     QPainter p(image);
0188 
0189     ::QPainterSetPenWithStipple (&p,
0190         fcolor, penWidth,
0191         fStippleColor);
0192 
0193     if (bcolor.isValid ()) {
0194         p.setBrush (QBrush (bcolor.toQColor()));
0195     }
0196     // HACK: seems to be needed if set_Pen_(Qt::color0) else fills with Qt::color0.
0197     else {
0198         p.setBrush (Qt::NoBrush);
0199     }
0200 
0201     // Qt bug: single point doesn't show up depending on penWidth.
0202     if (Only1PixelInPointArray (points))
0203     {
0204     #if DEBUG_KP_PIXMAP_FX
0205         qCDebug(kpLogPixmapfx) << "\tinvoking single point hack";
0206     #endif
0207         p.drawPoint(points [0]);
0208         return;
0209     }
0210 
0211     // TODO: why aren't the ends rounded?
0212     p.drawPolygon(points, Qt::OddEvenFill);
0213 
0214     if ( isFinal ) {
0215       return;
0216     }
0217 
0218     if ( points.count() <= 2 ) {
0219       return;
0220     }
0221 
0222     p.setCompositionMode(QPainter::RasterOp_SourceXorDestination);
0223     p.setPen(QPen(Qt::white));
0224     p.drawLine(points[0], points[points.count() - 1]);
0225 }
0226 
0227 //---------------------------------------------------------------------
0228 // public static
0229 
0230 void kpPixmapFX::fillRect (QImage *image,
0231         int x, int y, int width, int height,
0232         const kpColor &color,
0233         const kpColor &stippleColor)
0234 {
0235     QPainter painter(image);
0236 
0237     if (!stippleColor.isValid ())
0238     {
0239         painter.fillRect (x, y, width, height, color.toQColor());
0240     }
0241     else
0242     {
0243         const int StippleSize = 4;
0244 
0245         painter.setClipRect (x, y, width, height);
0246 
0247         for (int dy = 0; dy < height; dy += StippleSize)
0248         {
0249             for (int dx = 0; dx < width; dx += StippleSize)
0250             {
0251                 const bool parity = ((dy + dx) / StippleSize) % 2;
0252 
0253                 kpColor useColor;
0254                 if (!parity) {
0255                     useColor = color;
0256                 }
0257                 else {
0258                     useColor = stippleColor;
0259                 }
0260                     
0261                 painter.fillRect (x + dx, y + dy, StippleSize, StippleSize, useColor.toQColor());
0262             }
0263         }
0264 
0265     }
0266 }
0267 
0268 //---------------------------------------------------------------------
0269 
0270 void kpPixmapFX::drawStippleRect(QImage *image,
0271         int x, int y, int width, int height,
0272         const kpColor &fColor,
0273         const kpColor &fStippleColor)
0274 {
0275   QPainter painter(image);
0276 
0277   painter.setPen(QPen(fColor.toQColor(), 1, Qt::DashLine));
0278   painter.setBackground(fStippleColor.toQColor());
0279   painter.setBackgroundMode(Qt::OpaqueMode);
0280   painter.drawRect(x, y, width - 1, height - 1);
0281 }
0282 
0283 //---------------------------------------------------------------------