krita

kis_painter.cc

00001 /*
00002  *  Copyright (c) 2002 Patrick Julien <freak@codepimps.org>
00003  *  Copyright (c) 2004 Boudewijn Rempt <boud@valdyas.org>
00004  *  Copyright (c) 2004 Clarence Dang <dang@kde.org>
00005  *  Copyright (c) 2004 Adrian Page <adrian@pagenet.plus.com>
00006  *  Copyright (c) 2004 Cyrille Berger <cberger@cberger.net>
00007  *
00008  *  This program is free software; you can redistribute it and/or modify
00009  *  it under the terms of the GNU General Public License as published by
00010  *  the Free Software Foundation; either version 2 of the License, or
00011  *  (at your option) any later version.
00012  *
00013  *  This program is distributed in the hope that it will be useful,
00014  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00015  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00016  *  GNU General Public License for more details.
00017  *
00018  *  You should have received a copy of the GNU General Public License
00019  *  along with this program; if not, write to the Free Software
00020  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00021  */
00022 #include <stdlib.h>
00023 #include <string.h>
00024 #include <cfloat>
00025 #include <cmath>
00026 #include <climits>
00027 #include <strings.h>
00028 
00029 #include "qbrush.h"
00030 #include "qfontinfo.h"
00031 #include "qfontmetrics.h"
00032 #include "qpen.h"
00033 #include "qregion.h"
00034 #include "qwmatrix.h"
00035 #include <qimage.h>
00036 #include <qmap.h>
00037 #include <qpainter.h>
00038 #include <qpixmap.h>
00039 #include <qpointarray.h>
00040 #include <qrect.h>
00041 #include <qstring.h>
00042 
00043 #include <kdebug.h>
00044 #include <kcommand.h>
00045 #include <klocale.h>
00046 
00047 #include "kis_brush.h"
00048 #include "kis_debug_areas.h"
00049 #include "kis_image.h"
00050 #include "kis_layer.h"
00051 #include "kis_paint_device.h"
00052 #include "kis_painter.h"
00053 #include "kis_pattern.h"
00054 #include "kis_rect.h"
00055 #include "kis_colorspace.h"
00056 #include "kis_transaction.h"
00057 #include "kis_types.h"
00058 #include "kis_vec.h"
00059 #include "kis_iterators_pixel.h"
00060 #include "kis_paintop.h"
00061 #include "kis_selection.h"
00062 #include "kis_fill_painter.h"
00063 #include "kis_color.h"
00064 
00065 // Maximum distance from a Bezier control point to the line through the start
00066 // and end points for the curve to be considered flat.
00067 #define BEZIER_FLATNESS_THRESHOLD 0.5
00068 
00069 KisPainter::KisPainter()
00070 {
00071     init();
00072 }
00073 
00074 KisPainter::KisPainter(KisPaintDeviceSP device)
00075 {
00076     init();
00077     Q_ASSERT(device);
00078         begin(device);
00079 }
00080 
00081 void KisPainter::init()
00082 {
00083     m_transaction = 0;
00084     m_paintOp = 0;
00085     m_filter = 0;
00086     m_brush = 0;
00087     m_pattern= 0;
00088     m_opacity = OPACITY_OPAQUE;
00089     m_compositeOp = COMPOSITE_OVER;
00090     m_dab = 0;
00091     m_fillStyle = FillStyleNone;
00092     m_strokeStyle = StrokeStyleBrush;
00093     m_pressure = PRESSURE_MIN;
00094     m_duplicateHealing = false;
00095     m_duplicateHealingRadius = 10;
00096     m_duplicatePerspectiveCorrection = false;
00097     m_varyBrushSpacingWithPressureWhenDrawingALine = true;
00098 }
00099 
00100 KisPainter::~KisPainter()
00101 {
00102     m_brush = 0;
00103     delete m_paintOp;
00104     end();
00105 }
00106 
00107 void KisPainter::begin(KisPaintDeviceSP device)
00108 {
00109     if (!device) return;
00110 
00111     if (m_transaction)
00112         delete m_transaction;
00113 
00114     m_device = device;
00115     m_colorSpace = device->colorSpace();
00116     m_pixelSize = device->pixelSize();
00117 }
00118 
00119 KCommand *KisPainter::end()
00120 {
00121     return endTransaction();
00122 }
00123 
00124 void KisPainter::beginTransaction(const QString& customName)
00125 {
00126     if (m_transaction)
00127         delete m_transaction;
00128     m_transaction = new KisTransaction(customName, m_device);
00129     Q_CHECK_PTR(m_transaction);
00130 }
00131 
00132 void KisPainter::beginTransaction( KisTransaction* command)
00133 {
00134     if (m_transaction)
00135         delete m_transaction;
00136     m_transaction = command;
00137 }
00138 
00139 
00140 KCommand *KisPainter::endTransaction()
00141 {
00142     KCommand *command = m_transaction;
00143         m_transaction = 0;
00144         return command;
00145 }
00146 
00147 
00148 QRect KisPainter::dirtyRect() {
00149     QRect r = m_dirtyRect;
00150     m_dirtyRect = QRect();
00151     return r;
00152 }
00153 
00154 void KisPainter::bitBlt(Q_INT32 dx, Q_INT32 dy,
00155                         const KisCompositeOp& op,
00156                         KisPaintDeviceSP srcdev,
00157                         Q_UINT8 opacity,
00158                         Q_INT32 sx, Q_INT32 sy,
00159                         Q_INT32 sw, Q_INT32 sh)
00160 {
00161     if (srcdev == 0) {
00162         return;
00163     }
00164 
00165     QRect srcRect = QRect(sx, sy, sw, sh);
00166 
00167     if (srcdev->extentIsValid() && op != COMPOSITE_COPY) {
00168         srcRect &= srcdev->extent();
00169     }
00170 
00171     if (srcRect.isEmpty()) {
00172         return;
00173     }
00174 
00175     dx += srcRect.x() - sx;
00176     dy += srcRect.y() - sy;
00177 
00178     sx = srcRect.x();
00179     sy = srcRect.y();
00180     sw = srcRect.width();
00181     sh = srcRect.height();
00182 
00183     addDirtyRect(QRect(dx, dy, sw, sh));
00184 
00185     KisColorSpace * srcCs = srcdev->colorSpace();
00186 
00187     Q_INT32 dstY = dy;
00188     Q_INT32 srcY = sy;
00189     Q_INT32 rowsRemaining = sh;
00190 
00191     while (rowsRemaining > 0) {
00192 
00193         Q_INT32 dstX = dx;
00194         Q_INT32 srcX = sx;
00195         Q_INT32 columnsRemaining = sw;
00196         Q_INT32 numContiguousDstRows = m_device->numContiguousRows(dstY, dstX, dstX + sw - 1);
00197         Q_INT32 numContiguousSrcRows = srcdev->numContiguousRows(srcY, srcX, srcX + sw - 1);
00198 
00199         Q_INT32 rows = QMIN(numContiguousDstRows, numContiguousSrcRows);
00200         rows = QMIN(rows, rowsRemaining);
00201 
00202         while (columnsRemaining > 0) {
00203 
00204             Q_INT32 numContiguousDstColumns = m_device->numContiguousColumns(dstX, dstY, dstY + rows - 1);
00205             Q_INT32 numContiguousSrcColumns = srcdev->numContiguousColumns(srcX, srcY, srcY + rows - 1);
00206 
00207             Q_INT32 columns = QMIN(numContiguousDstColumns, numContiguousSrcColumns);
00208             columns = QMIN(columns, columnsRemaining);
00209 
00210             Q_INT32 srcRowStride = srcdev->rowStride(srcX, srcY);
00211             //const Q_UINT8 *srcData = srcdev->pixel(srcX, srcY);
00212             KisHLineIteratorPixel srcIt = srcdev->createHLineIterator(srcX, srcY, columns, false);
00213             const Q_UINT8 *srcData = srcIt.rawData();
00214 
00215             //Q_UINT8 *dstData = m_device->writablePixel(dstX, dstY);
00216             Q_INT32 dstRowStride = m_device->rowStride(dstX, dstY);
00217             KisHLineIteratorPixel dstIt = m_device->createHLineIterator(dstX, dstY, columns, true);
00218             Q_UINT8 *dstData = dstIt.rawData();
00219 
00220 
00221             m_colorSpace->bitBlt(dstData,
00222                           dstRowStride,
00223                           srcCs,
00224                           srcData,
00225                           srcRowStride,
00226                           0,
00227                           0,
00228                           opacity,
00229                           rows,
00230                           columns,
00231                           op);
00232 
00233             srcX += columns;
00234             dstX += columns;
00235             columnsRemaining -= columns;
00236         }
00237 
00238         srcY += rows;
00239         dstY += rows;
00240         rowsRemaining -= rows;
00241     }
00242 }
00243 
00244 void KisPainter::bltSelection(Q_INT32 dx, Q_INT32 dy,
00245                   const KisCompositeOp &op,
00246                   KisPaintDeviceSP srcdev,
00247                   KisSelectionSP seldev,
00248                   Q_UINT8 opacity,
00249                   Q_INT32 sx, Q_INT32 sy,
00250                   Q_INT32 sw, Q_INT32 sh)
00251 {
00252     // Better use a probablistic method than a too slow one
00253     if (seldev->isProbablyTotallyUnselected(QRect(dx, dy, sw, sh))) {
00254 /*
00255         kdDebug() << "Blitting outside selection rect\n";
00256 
00257         kdDebug() << "srcdev: " << srcdev << " (" << srcdev->name() << ")"
00258         << ", seldev: " << seldev << " (" << seldev->name() << ")"
00259         << ". dx, dy " << dx << "," << dy
00260         << ". sx, sy : sw, sy " << sx << "," << sy << " : " << sw << "," << sh << endl;
00261 */
00262         return;
00263     }
00264     bltMask(dx,dy,op,srcdev,seldev.data(),opacity,sx,sy,sw,sh);
00265 }
00266 
00267 void KisPainter::bltMask(Q_INT32 dx, Q_INT32 dy,
00268                      const KisCompositeOp &op,
00269                      KisPaintDeviceSP srcdev,
00270                      KisPaintDeviceSP seldev,
00271                      Q_UINT8 opacity,
00272                      Q_INT32 sx, Q_INT32 sy,
00273                      Q_INT32 sw, Q_INT32 sh)
00274 
00275 {
00276     if (srcdev == 0) return;
00277 
00278     if (seldev == 0) return;
00279 
00280     if (m_device == 0) return;
00281 
00282 
00283     QRect srcRect = QRect(sx, sy, sw, sh);
00284 
00285     if (srcdev->extentIsValid() && op != COMPOSITE_COPY) {
00286         srcRect &= srcdev->extent();
00287     }
00288 
00289     if (srcRect.isEmpty()) {
00290         return;
00291     }
00292 
00293     dx += srcRect.x() - sx;
00294     dy += srcRect.y() - sy;
00295 
00296     sx = srcRect.x();
00297     sy = srcRect.y();
00298     sw = srcRect.width();
00299     sh = srcRect.height();
00300 
00301     addDirtyRect(QRect(dx, dy, sw, sh));
00302 
00303     KisColorSpace * srcCs = srcdev->colorSpace();
00304 
00305     Q_INT32 dstY = dy;
00306     Q_INT32 srcY = sy;
00307     Q_INT32 rowsRemaining = sh;
00308 
00309     while (rowsRemaining > 0) {
00310 
00311         Q_INT32 dstX = dx;
00312         Q_INT32 srcX = sx;
00313         Q_INT32 columnsRemaining = sw;
00314         Q_INT32 numContiguousDstRows = m_device->numContiguousRows(dstY, dstX, dstX + sw - 1);
00315         Q_INT32 numContiguousSrcRows = srcdev->numContiguousRows(srcY, srcX, srcX + sw - 1);
00316         Q_INT32 numContiguousSelRows = seldev->numContiguousRows(dstY, dstX, dstX + sw - 1);
00317 
00318         Q_INT32 rows = QMIN(numContiguousDstRows, numContiguousSrcRows);
00319         rows = QMIN(numContiguousSelRows, rows);
00320         rows = QMIN(rows, rowsRemaining);
00321 
00322         while (columnsRemaining > 0) {
00323 
00324             Q_INT32 numContiguousDstColumns = m_device->numContiguousColumns(dstX, dstY, dstY + rows - 1);
00325             Q_INT32 numContiguousSrcColumns = srcdev->numContiguousColumns(srcX, srcY, srcY + rows - 1);
00326             Q_INT32 numContiguousSelColumns = seldev->numContiguousColumns(dstX, dstY, dstY + rows - 1);
00327 
00328             Q_INT32 columns = QMIN(numContiguousDstColumns, numContiguousSrcColumns);
00329             columns = QMIN(numContiguousSelColumns, columns);
00330             columns = QMIN(columns, columnsRemaining);
00331 
00332             //Q_UINT8 *dstData = m_device->writablePixel(dstX, dstY);
00333             Q_INT32 dstRowStride = m_device->rowStride(dstX, dstY);
00334             KisHLineIteratorPixel dstIt = m_device->createHLineIterator(dstX, dstY, columns, true);
00335             Q_UINT8 *dstData = dstIt.rawData();
00336 
00337             //const Q_UINT8 *srcData = srcdev->pixel(srcX, srcY);
00338             Q_INT32 srcRowStride = srcdev->rowStride(srcX, srcY);
00339             KisHLineIteratorPixel srcIt = srcdev->createHLineIterator(srcX, srcY, columns, false);
00340             const Q_UINT8 *srcData = srcIt.rawData();
00341 
00342             //const Q_UINT8 *selData = seldev->pixel(dstX, dstY);
00343             Q_INT32 selRowStride = seldev->rowStride(dstX, dstY);
00344             KisHLineIteratorPixel selIt = seldev->createHLineIterator(dstX, dstY, columns, false);
00345             const Q_UINT8 *selData = selIt.rawData();
00346 
00347             m_colorSpace->bitBlt(dstData,
00348                                    dstRowStride,
00349                                    srcCs,
00350                                    srcData,
00351                                    srcRowStride,
00352                                    selData,
00353                                    selRowStride,
00354                                    opacity,
00355                                    rows,
00356                                    columns,
00357                                    op);
00358 
00359             srcX += columns;
00360             dstX += columns;
00361             columnsRemaining -= columns;
00362         }
00363 
00364         srcY += rows;
00365         dstY += rows;
00366         rowsRemaining -= rows;
00367     }
00368 }
00369 
00370 
00371 void KisPainter::bltSelection(Q_INT32 dx, Q_INT32 dy,
00372                   const KisCompositeOp& op,
00373                   KisPaintDeviceSP srcdev,
00374                   Q_UINT8 opacity,
00375                   Q_INT32 sx, Q_INT32 sy,
00376                   Q_INT32 sw, Q_INT32 sh)
00377 {
00378     if (m_device == 0) return;
00379     if (!m_device->hasSelection()) {
00380         bitBlt(dx, dy, op, srcdev, opacity, sx, sy, sw, sh);
00381     }
00382     else
00383         bltSelection(dx,dy,op,srcdev, m_device->selection(),opacity,sx,sy,sw,sh);
00384 }
00385 
00386 double KisPainter::paintLine(const KisPoint & pos1,
00387                  const double pressure1,
00388                  const double xTilt1,
00389                  const double yTilt1,
00390                  const KisPoint & pos2,
00391                  const double pressure2,
00392                  const double xTilt2,
00393                  const double yTilt2,
00394                  const double inSavedDist)
00395 {
00396     if (!m_device) return 0;
00397     if (!m_paintOp) return 0;
00398     if (!m_brush) return 0;
00399 
00400     double savedDist = inSavedDist;
00401     KisVector2D end(pos2);
00402     KisVector2D start(pos1);
00403 
00404     KisVector2D dragVec = end - start;
00405     KisVector2D movement = dragVec;
00406 
00407     if (savedDist < 0) {
00408         m_paintOp->paintAt(pos1, KisPaintInformation(pressure1, xTilt1, yTilt1, movement));
00409         savedDist = 0;
00410     }
00411 
00412     double xSpacing = 0;
00413     double ySpacing = 0;
00414 
00415      if ( m_varyBrushSpacingWithPressureWhenDrawingALine ) {
00416         // XXX: The spacing should vary as the pressure changes along the
00417         // line.
00418         // This is a quick simplification.
00419         xSpacing = m_brush->xSpacing((pressure1 + pressure2) / 2);
00420         ySpacing = m_brush->ySpacing((pressure1 + pressure2) / 2);
00421      }
00422      else {
00423          xSpacing = m_brush->xSpacing( PRESSURE_DEFAULT );
00424          ySpacing = m_brush->ySpacing( PRESSURE_DEFAULT );
00425      }
00426 
00427     if (xSpacing < 0.5) {
00428         xSpacing = 0.5;
00429     }
00430     if (ySpacing < 0.5) {
00431         ySpacing = 0.5;
00432     }
00433 
00434     double xScale = 1;
00435     double yScale = 1;
00436     double spacing;
00437     // Scale x or y so that we effectively have a square brush
00438     // and calculate distance in that coordinate space. We reverse this scaling
00439     // before drawing the brush. This produces the correct spacing in both
00440     // x and y directions, even if the brush's aspect ratio is not 1:1.
00441     if (xSpacing > ySpacing) {
00442         yScale = xSpacing / ySpacing;
00443         spacing = xSpacing;
00444     }
00445     else {
00446         xScale = ySpacing / xSpacing;
00447         spacing = ySpacing;
00448     }
00449 
00450     dragVec.setX(dragVec.x() * xScale);
00451     dragVec.setY(dragVec.y() * yScale);
00452 
00453     double newDist = dragVec.length();
00454     double dist = savedDist + newDist;
00455     double l_savedDist = savedDist;
00456 
00457     if (dist < spacing) {
00458         return dist;
00459     }
00460 
00461     dragVec.normalize();
00462     KisVector2D step(0, 0);
00463 
00464     while (dist >= spacing) {
00465         if (l_savedDist > 0) {
00466             step += dragVec * (spacing - l_savedDist);
00467             l_savedDist -= spacing;
00468         }
00469         else {
00470             step += dragVec * spacing;
00471         }
00472 
00473         KisPoint p(start.x() + (step.x() / xScale), start.y() + (step.y() / yScale));
00474 
00475         double distanceMoved = step.length();
00476         double t = 0;
00477 
00478         if (newDist > DBL_EPSILON) {
00479             t = distanceMoved / newDist;
00480         }
00481 
00482         double pressure = (1 - t) * pressure1 + t * pressure2;
00483         double xTilt = (1 - t) * xTilt1 + t * xTilt2;
00484         double yTilt = (1 - t) * yTilt1 + t * yTilt2;
00485 
00486         m_paintOp->paintAt(p, KisPaintInformation(pressure, xTilt, yTilt, movement));
00487         dist -= spacing;
00488     }
00489 
00490     if (dist > 0)
00491         return dist;
00492     else
00493         return 0;
00494 }
00495 
00496 void KisPainter::paintPolyline (const vKisPoint &points,
00497                                 int index, int numPoints)
00498 {
00499     if (index >= (int) points.count ())
00500         return;
00501 
00502     if (numPoints < 0)
00503         numPoints = points.count ();
00504 
00505     if (index + numPoints > (int) points.count ())
00506         numPoints = points.count () - index;
00507 
00508 
00509     for (int i = index; i < index + numPoints - 1; i++)
00510     {
00511         paintLine (points [index], 0/*pressure*/, 0, 0, points [index + 1],
00512                0/*pressure*/, 0, 0);
00513     }
00514 }
00515 
00516 void KisPainter::getBezierCurvePoints(const KisPoint &pos1,
00517                       const KisPoint &control1,
00518                       const KisPoint &control2,
00519                       const KisPoint &pos2,
00520                       vKisPoint& points)
00521 {
00522     double d1 = pointToLineDistance(control1, pos1, pos2);
00523     double d2 = pointToLineDistance(control2, pos1, pos2);
00524 
00525     if (d1 < BEZIER_FLATNESS_THRESHOLD && d2 < BEZIER_FLATNESS_THRESHOLD) {
00526         points.push_back(pos1);
00527     } else {
00528         // Midpoint subdivision. See Foley & Van Dam Computer Graphics P.508
00529         KisVector2D p1 = pos1;
00530         KisVector2D p2 = control1;
00531         KisVector2D p3 = control2;
00532         KisVector2D p4 = pos2;
00533 
00534         KisVector2D l2 = (p1 + p2) / 2;
00535         KisVector2D h = (p2 + p3) / 2;
00536         KisVector2D l3 = (l2 + h) / 2;
00537         KisVector2D r3 = (p3 + p4) / 2;
00538         KisVector2D r2 = (h + r3) / 2;
00539         KisVector2D l4 = (l3 + r2) / 2;
00540         KisVector2D r1 = l4;
00541         KisVector2D l1 = p1;
00542         KisVector2D r4 = p4;
00543 
00544         getBezierCurvePoints(l1.toKisPoint(), l2.toKisPoint(), l3.toKisPoint(), l4.toKisPoint(), points);
00545         getBezierCurvePoints(r1.toKisPoint(), r2.toKisPoint(), r3.toKisPoint(), r4.toKisPoint(), points);
00546     }
00547 }
00548 
00549 double KisPainter::paintBezierCurve(const KisPoint &pos1,
00550                     const double pressure1,
00551                     const double xTilt1,
00552                     const double yTilt1,
00553                     const KisPoint &control1,
00554                     const KisPoint &control2,
00555                     const KisPoint &pos2,
00556                     const double pressure2,
00557                     const double xTilt2,
00558                     const double yTilt2,
00559                     const double savedDist)
00560 {
00561     double newDistance;
00562     double d1 = pointToLineDistance(control1, pos1, pos2);
00563     double d2 = pointToLineDistance(control2, pos1, pos2);
00564 
00565     if (d1 < BEZIER_FLATNESS_THRESHOLD && d2 < BEZIER_FLATNESS_THRESHOLD) {
00566         newDistance = paintLine(pos1, pressure1, xTilt1, yTilt1, pos2, pressure2, xTilt2, yTilt2, savedDist);
00567     } else {
00568         // Midpoint subdivision. See Foley & Van Dam Computer Graphics P.508
00569         KisVector2D p1 = pos1;
00570         KisVector2D p2 = control1;
00571         KisVector2D p3 = control2;
00572         KisVector2D p4 = pos2;
00573 
00574         KisVector2D l2 = (p1 + p2) / 2;
00575         KisVector2D h = (p2 + p3) / 2;
00576         KisVector2D l3 = (l2 + h) / 2;
00577         KisVector2D r3 = (p3 + p4) / 2;
00578         KisVector2D r2 = (h + r3) / 2;
00579         KisVector2D l4 = (l3 + r2) / 2;
00580         KisVector2D r1 = l4;
00581         KisVector2D l1 = p1;
00582         KisVector2D r4 = p4;
00583 
00584         double midPressure = (pressure1 + pressure2) / 2;
00585         double midXTilt = (xTilt1 + xTilt2) / 2;
00586         double midYTilt = (yTilt1 + yTilt2) / 2;
00587 
00588         newDistance = paintBezierCurve(l1.toKisPoint(), pressure1, xTilt1, yTilt1,
00589                            l2.toKisPoint(), l3.toKisPoint(),
00590                            l4.toKisPoint(), midPressure, midXTilt, midYTilt,
00591                            savedDist);
00592         newDistance = paintBezierCurve(r1.toKisPoint(), midPressure, midXTilt, midYTilt,
00593                            r2.toKisPoint(),
00594                            r3.toKisPoint(),
00595                            r4.toKisPoint(), pressure2, xTilt2, yTilt2, newDistance);
00596     }
00597 
00598     return newDistance;
00599 }
00600 
00601 void KisPainter::paintRect (const KisPoint &startPoint,
00602                             const KisPoint &endPoint,
00603                             const double /*pressure*/,
00604                 const double /*xTilt*/,
00605                 const double /*yTilt*/)
00606 {
00607     KoRect normalizedRect = KisRect (startPoint, endPoint).normalize ();
00608 
00609     vKisPoint points;
00610 
00611     points.push_back(normalizedRect.topLeft());
00612     points.push_back(normalizedRect.bottomLeft());
00613     points.push_back(normalizedRect.bottomRight());
00614     points.push_back(normalizedRect.topRight());
00615 
00616     paintPolygon(points);
00617 }
00618 
00619 void KisPainter::paintEllipse (const KisPoint &startPoint,
00620                                const KisPoint &endPoint,
00621                                const double /*pressure*/,
00622                    const double /*xTilt*/,
00623                    const double /*yTilt*/)
00624 {
00625     KisRect r = KisRect(startPoint, endPoint).normalize();
00626 
00627     // See http://www.whizkidtech.redprince.net/bezier/circle/ for explanation.
00628     // kappa = (4/3*(sqrt(2)-1))
00629     const double kappa = 0.5522847498;
00630     const double lx = (r.width() / 2) * kappa;
00631     const double ly = (r.height() / 2) * kappa;
00632 
00633     KisPoint center = r.center();
00634 
00635     KisPoint p0(r.left(), center.y());
00636     KisPoint p1(r.left(), center.y() - ly);
00637     KisPoint p2(center.x() - lx, r.top());
00638     KisPoint p3(center.x(), r.top());
00639 
00640     vKisPoint points;
00641 
00642     getBezierCurvePoints(p0, p1, p2, p3, points);
00643 
00644     KisPoint p4(center.x() + lx, r.top());
00645     KisPoint p5(r.right(), center.y() - ly);
00646     KisPoint p6(r.right(), center.y());
00647 
00648     getBezierCurvePoints(p3, p4, p5, p6, points);
00649 
00650     KisPoint p7(r.right(), center.y() + ly);
00651     KisPoint p8(center.x() + lx, r.bottom());
00652     KisPoint p9(center.x(), r.bottom());
00653 
00654     getBezierCurvePoints(p6, p7, p8, p9, points);
00655 
00656     KisPoint p10(center.x() - lx, r.bottom());
00657     KisPoint p11(r.left(), center.y() + ly);
00658 
00659     getBezierCurvePoints(p9, p10, p11, p0, points);
00660 
00661     paintPolygon(points);
00662 }
00663 
00664 void KisPainter::paintAt(const KisPoint & pos,
00665                          const double pressure,
00666                          const double xTilt,
00667                          const double yTilt)
00668 {
00669     if (!m_paintOp) return;
00670     m_paintOp->paintAt(pos, KisPaintInformation(pressure, xTilt, yTilt, KisVector2D()));
00671 }
00672 
00673 double KisPainter::pointToLineDistance(const KisPoint& p, const KisPoint& l0, const KisPoint& l1)
00674 {
00675     double lineLength = sqrt((l1.x() - l0.x()) * (l1.x() - l0.x()) + (l1.y() - l0.y()) * (l1.y() - l0.y()));
00676     double distance = 0;
00677 
00678     if (lineLength > DBL_EPSILON) {
00679         distance = ((l0.y() - l1.y()) * p.x() + (l1.x() - l0.x()) * p.y() + l0.x() * l1.y() - l1.x() * l0.y()) / lineLength;
00680         distance = fabs(distance);
00681     }
00682 
00683     return distance;
00684 }
00685 
00686 /*
00687  * Concave Polygon Scan Conversion
00688  * by Paul Heckbert
00689  * from "Graphics Gems", Academic Press, 1990
00690  */
00691 
00692 /*
00693  * concave: scan convert nvert-sided concave non-simple polygon with vertices at
00694  * (point[i].x, point[i].y) for i in [0..nvert-1] within the window win by
00695  * calling spanproc for each visible span of pixels.
00696  * Polygon can be clockwise or counterclockwise.
00697  * Algorithm does uniform point sampling at pixel centers.
00698  * Inside-outside test done by Jordan's rule: a point is considered inside if
00699  * an emanating ray intersects the polygon an odd number of times.
00700  * drawproc should fill in pixels from xl to xr inclusive on scanline y,
00701  * e.g:
00702  *    drawproc(y, xl, xr)
00703  *    int y, xl, xr;
00704  *    {
00705  *        int x;
00706  *        for (x=xl; x<=xr; x++)
00707  *        pixel_write(x, y, pixelvalue);
00708  *    }
00709  *
00710  *  Paul Heckbert    30 June 81, 18 Dec 89
00711  */
00712 
00713 typedef struct {    /* a polygon edge */
00714     double x;       /* x coordinate of edge's intersection with current scanline */
00715     double dx;      /* change in x with respect to y */
00716     int i;            /* edge number: edge i goes from pt[i] to pt[i+1] */
00717 } Edge;
00718 
00719 static int n;            /* number of vertices */
00720 static const KisPoint *pt;    /* vertices */
00721 
00722 static int nact;        /* number of active edges */
00723 static Edge *active;        /* active edge list:edges crossing scanline y */
00724 
00725 /* comparison routines for qsort */
00726 static int compare_ind(const void *pu, const void *pv)
00727 {
00728     const int *u = static_cast<const int *>(pu);
00729     const int *v = static_cast<const int *>(pv);
00730 
00731     return pt[*u].y() <= pt[*v].y() ? -1 : 1;
00732 }
00733 
00734 static int compare_active(const void *pu, const void *pv)
00735 {
00736     const Edge *u = static_cast<const Edge *>(pu);
00737     const Edge *v = static_cast<const Edge *>(pv);
00738 
00739     return u->x <= v->x ? -1 : 1;
00740 }
00741 
00742 static void cdelete(int i)        /* remove edge i from active list */
00743 {
00744     int j;
00745 
00746     for (j=0; j<nact && active[j].i!=i; j++);
00747     if (j>=nact) return;        /* edge not in active list; happens at win->y0*/
00748     nact--;
00749     bcopy(&active[j+1], &active[j], (nact-j)*sizeof active[0]);
00750 }
00751 
00752 static void cinsert(int i, int y)        /* append edge i to end of active list */
00753 {
00754     int j;
00755     double dx;
00756     const KisPoint *p, *q;
00757 
00758     j = i<n-1 ? i+1 : 0;
00759     if (pt[i].y() < pt[j].y()) {
00760         p = &pt[i]; q = &pt[j];
00761     } else {
00762         p = &pt[j]; q = &pt[i];
00763     }
00764     /* initialize x position at intersection of edge with scanline y */
00765     active[nact].dx = dx = (q->x()-p->x())/(q->y()-p->y());
00766     active[nact].x = dx*(y+.5-p->y())+p->x();
00767     active[nact].i = i;
00768     nact++;
00769 }
00770 
00771 void KisPainter::fillPolygon(const vKisPoint& points, FillStyle fillStyle)
00772 {
00773     int nvert = points.count();
00774     int k, y0, y1, y, i, j, xl, xr;
00775     int *ind;        /* list of vertex indices, sorted by pt[ind[j]].y */
00776 
00777     n = nvert;
00778     pt = &(points[0]);
00779     if (n<3) return;
00780     if (fillStyle == FillStyleNone) {
00781         return;
00782     }
00783 
00784     ind = new int[n];
00785     Q_CHECK_PTR(ind);
00786     active = new Edge[n];
00787     Q_CHECK_PTR(active);
00788 
00789     /* create y-sorted array of indices ind[k] into vertex list */
00790     for (k=0; k<n; k++)
00791         ind[k] = k;
00792     qsort(ind, n, sizeof ind[0], compare_ind);  /* sort ind by pt[ind[k]].y */
00793 
00794     nact = 0;                /* start with empty active list */
00795     k = 0;                    /* ind[k] is next vertex to process */
00796     y0 = static_cast<int>(ceil(pt[ind[0]].y()-.5));            /* ymin of polygon */
00797     y1 = static_cast<int>(floor(pt[ind[n-1]].y()-.5));        /* ymax of polygon */
00798 
00799     int x0 = INT_MAX;
00800     int x1 = INT_MIN;
00801 
00802     for (int i = 0; i < nvert; i++) {
00803         int pointHighX = static_cast<int>(ceil(points[i].x() - 0.5));
00804         int pointLowX = static_cast<int>(floor(points[i].x() - 0.5));
00805 
00806         if (pointLowX < x0) {
00807             x0 = pointLowX;
00808         }
00809         if (pointHighX > x1) {
00810             x1 = pointHighX;
00811         }
00812     }
00813 
00814     // Fill the polygon bounding rectangle with the required contents then we'll
00815     // create a mask for the actual polygon coverage.
00816 
00817     KisPaintDeviceSP polygon = new KisPaintDevice(m_device->colorSpace(), "polygon");
00818     Q_CHECK_PTR(polygon);
00819 
00820     KisFillPainter fillPainter(polygon);
00821     QRect boundingRectangle(x0, y0, x1 - x0 + 1, y1 - y0 + 1);
00822 
00823     // Clip to the image bounds.
00824     if (m_device->image()) {
00825         boundingRectangle &= m_device->image()->bounds();
00826     }
00827 
00828     switch (fillStyle) {
00829     default:
00830         // Fall through
00831     case FillStyleGradient:
00832         // Currently unsupported, fall through
00833     case FillStyleStrokes:
00834         // Currently unsupported, fall through
00835         kdWarning(DBG_AREA_CORE) << "Unknown or unsupported fill style in fillPolygon\n";
00836     case FillStyleForegroundColor:
00837         fillPainter.fillRect(boundingRectangle, paintColor(), OPACITY_OPAQUE);
00838         break;
00839     case FillStyleBackgroundColor:
00840         fillPainter.fillRect(boundingRectangle, backgroundColor(), OPACITY_OPAQUE);
00841         break;
00842     case FillStylePattern:
00843         Q_ASSERT(m_pattern != 0);
00844         fillPainter.fillRect(boundingRectangle, m_pattern);
00845         break;
00846     }
00847 
00848     KisSelectionSP polygonMask = new KisSelection(polygon);
00849 
00850     for (y=y0; y<=y1; y++) {        /* step through scanlines */
00851         /* scanline y is at y+.5 in continuous coordinates */
00852 
00853         /* check vertices between previous scanline and current one, if any */
00854         for (; k<n && pt[ind[k]].y()<=y+.5; k++) {
00855             /* to simplify, if pt.y=y+.5, pretend it's above */
00856             /* invariant: y-.5 < pt[i].y <= y+.5 */
00857             i = ind[k];
00858             /*
00859              * insert or delete edges before and after vertex i (i-1 to i,
00860              * and i to i+1) from active list if they cross scanline y
00861              */
00862             j = i>0 ? i-1 : n-1;        /* vertex previous to i */
00863             if (pt[j].y() <= y-.5)        /* old edge, remove from active list */
00864                 cdelete(j);
00865             else if (pt[j].y() > y+.5)    /* new edge, add to active list */
00866                 cinsert(j, y);
00867             j = i<n-1 ? i+1 : 0;        /* vertex next after i */
00868             if (pt[j].y() <= y-.5)        /* old edge, remove from active list */
00869                 cdelete(i);
00870             else if (pt[j].y() > y+.5)    /* new edge, add to active list */
00871                 cinsert(i, y);
00872         }
00873 
00874         /* sort active edge list by active[j].x */
00875         qsort(active, nact, sizeof active[0], compare_active);
00876 
00877         /* draw horizontal segments for scanline y */
00878         for (j=0; j<nact; j+=2) {    /* draw horizontal segments */
00879             /* span 'tween j & j+1 is inside, span tween j+1 & j+2 is outside */
00880             xl = static_cast<int>(ceil(active[j].x-.5));        /* left end of span */
00881             xr = static_cast<int>(floor(active[j+1].x-.5));        /* right end of span */
00882 
00883             if (xl<=xr) {
00884                 KisHLineIterator it = polygonMask->createHLineIterator(xl, y, xr - xl + 1, true);
00885 
00886                 while (!it.isDone()) {
00887                     // We're using a selection here, that means alpha colorspace, that means one byte.
00888                     it.rawData()[0] = MAX_SELECTED;
00889                     ++it;
00890                 }
00891             }
00892 
00893             active[j].x += active[j].dx;        /* increment edge coords */
00894             active[j+1].x += active[j+1].dx;
00895         }
00896     }
00897     delete [] ind;
00898     delete [] active;
00899 
00900     polygon->applySelectionMask(polygonMask);
00901 
00902     QRect r = polygon->extent();
00903 
00904     // The strokes for the outline may have already added updated the dirtyrect, but it can't hurt,
00905     // and if we're painting without outlines, then there will be no dirty rect. Let's do it ourselves...
00906     // addDirtyRect( r ); // XXX the bltSelection will add to the dirtyrect
00907 
00908     bltSelection(r.x(), r.y(), compositeOp(), polygon, opacity(), r.x(), r.y(), r.width(), r.height());
00909 }
00910 
00911 void KisPainter::paintPolygon(const vKisPoint& points)
00912 {
00913     if (m_fillStyle != FillStyleNone) {
00914         fillPolygon(points, m_fillStyle);
00915     }
00916 
00917     if (m_strokeStyle != StrokeStyleNone) {
00918         if (points.count() > 1) {
00919             double distance = -1;
00920 
00921             for (uint i = 0; i < points.count() - 1; i++) {
00922                 distance = paintLine(points[i], PRESSURE_DEFAULT, 0, 0, points[i + 1], PRESSURE_DEFAULT, 0, 0, distance);
00923             }
00924             paintLine(points[points.count() - 1], PRESSURE_DEFAULT, 0, 0, points[0], PRESSURE_DEFAULT, 0, 0, distance);
00925         }
00926     }
00927 }
00928 
KDE Home | KDE Accessibility Home | Description of Access Keys