/*
 *  Copyright (c) 2002 Patrick Julien <freak@codepimps.org>
 *  Copyright (c) 2004 Clarence Dang <dang@kde.org>
 *  Copyright (c) 2008-2010 Lukáš Tvrdý <lukast.dev@gmail.com>
 *  Copyright (c) 2010 José Luis Vergara Toloza <pentalis@gmail.com>
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 */

#ifndef KIS_PAINTER_H_
#define KIS_PAINTER_H_

#include <math.h>

#include <QVector>

#include <KoColorSpaceConstants.h>
#include <KoColorConversionTransformation.h>

#include "kis_distance_information.h"
#include "kis_global.h"
#include "kis_types.h"
#include <krita_export.h>

class QPen;
class KUndo2Command;
class QRect;
class QRectF;
class QStringList;
class QBitArray;
class QPainterPath;

class KoAbstractGradient;
class KoUpdater;
class KoColor;
class KoCompositeOp;

class KisUndoAdapter;
class KisPostExecutionUndoAdapter;
class KisTransaction;
class KisPattern;
class KisFilterConfiguration;
class KisPaintInformation;
class KisPaintOp;
/**
 * KisPainter contains the graphics primitives necessary to draw on a
 * KisPaintDevice. This is the same kind of abstraction as used in Qt
 * itself, where you have QPainter and QPaintDevice.
 *
 * However, KisPainter works on a tiled image and supports different
 * color models, and that's a lot more complicated.
 *
 * KisPainter supports transactions that can group various paint operations
 * in one undoable step.
 *
 * For more complex operations, you might want to have a look at the subclasses
 * of KisPainter: KisConvolutionPainter, KisFillPainter and KisGradientPainter
 *
 * KisPainter sets a number of default values, like COMPOSITE_OVER for compositeop,
 * OPACITY_OPAQUE for opacity and no selection for selection.
 */
class KRITAIMAGE_EXPORT KisPainter
{


public:
    /// Construct painter without a device
    KisPainter();

    /// Construct a painter, and begin painting on the device
    KisPainter(KisPaintDeviceSP device);

    /// Construct a painter, and begin painting on the device. All actions will be masked by the given selection.
    KisPainter(KisPaintDeviceSP device, KisSelectionSP selection);
    virtual ~KisPainter();

public:
    /**
     * Start painting on the specified device. Not undoable.
     */
    void begin(KisPaintDeviceSP device);

    /**
     * Start painting on the specified paint device. All actions will be masked by the given selection.
     */
    void begin(KisPaintDeviceSP device, KisSelectionSP selection);

    /**
     * Finish painting on the current device
     */
    void end();

    /**
     * If set, the painter action is cancelable, if the action supports that.
     */
    void setProgress(KoUpdater * progressUpdater);

    /// Begin an undoable paint operation
    void beginTransaction(const QString& transactionName = "");

    /// Return the transaction's text message
    QString transactionText();

    /// Cancel all the changes made by the painter
    void revertTransaction();

    /// Finish the undoable paint operation
    void endTransaction(KisUndoAdapter *undoAdapter);

    /**
     * Finish transaction and load it to a special adapter for strokes
     */
    void endTransaction(KisPostExecutionUndoAdapter *undoAdapter);

    /**
     * Finishes a transaction and returns a pointer to its undo command
     */
    KUndo2Command* endAndTakeTransaction();

    /**
     * Finish the transaction and delete it's undo information.
     * NOTE: Be careful, because all the previous transactions
     * will become non-undoable after execution of this method.
     */
    void deleteTransaction();

    /// continue a transaction started somewhere else
    void putTransaction(KisTransaction* transaction);

    /// take transaction out of the reach of KisPainter
    KisTransaction* takeTransaction();

    /// Returns the current paint device.
    const KisPaintDeviceSP device() const;
    KisPaintDeviceSP device();



    /**
     * Blast a region of srcWidth @param srcWidth and srcHeight @param srcHeight from @param
     * srcDev onto the current paint device. @param srcX and @param srcY set the x and y
     * positions of the origin top-left corner, @param dstX and @param dstY those of
     * the destination.
     * Any pixel read outside the limits of @param srcDev will return the
     * default pixel, this is a property of \ref KisPaintDevice.
     *
     * @param dstX the destination x-coordinate
     * @param dstY the destination y-coordinate
     * @param srcDev the source device
     * @param srcX the source x-coordinate
     * @param srcY the source y-coordinate
     * @param srcWidth the width of the region to be manipulated
     * @param srcHeight the height of the region to be manipulated
     */
    void bitBlt(qint32 dstX, qint32 dstY,
                const KisPaintDeviceSP srcDev,
                qint32 srcX, qint32 srcY,
                qint32 srcWidth, qint32 srcHeight);

    /**
     * Convenience method that uses QPoint and QRect.
     *
     * @param pos the destination coordinate, it replaces @param dstX and @param dstY.
     * @param srcDev the source device.
     * @param srcRect the rectangle describing the area to blast from @param srcDev into the current paint device.
     * @param srcRect replaces @param srcX, @param srcY, @param srcWidth and @param srcHeight.
     *
     */
    void bitBlt(const QPoint & pos, const KisPaintDeviceSP srcDev, const QRect & srcRect);

    /**
     * The same as @ref bitBlt() but reads data from oldData() part of the device
     *
     * @param dstX the destination x-coordinate
     * @param dstY the destination y-coordinate
     * @param srcDev the source device
     * @param srcX the source x-coordinate
     * @param srcY the source y-coordinate
     * @param srcWidth the width of the region to be manipulated
     * @param srcHeight the height of the region to be manipulated
     */
    void bitBltOldData(qint32 dstX, qint32 dstY,
                       const KisPaintDeviceSP srcDev,
                       qint32 srcX, qint32 srcY,
                       qint32 srcWidth, qint32 srcHeight);

    /**
     * Convenience method that uses QPoint and QRect.
     *
     * @param pos the destination coordinate, it replaces @param dstX and @param dstY.
     * @param srcDev the source device.
     * @param srcRect the rectangle describing the area to blast from @param srcDev into the current paint device.
     * @param srcRect replaces @param srcX, @param srcY, @param srcWidth and @param srcHeight.
     *
     */
    void bitBltOldData(const QPoint & pos, const KisPaintDeviceSP srcDev, const QRect & srcRect);

    /**
     * Blasts a @param selection of srcWidth @param srcWidth and srcHeight @param srcHeight
     * of @param srcDev on the current paint device. There is parameters
     * to control where the area begins in each distinct device, explained below.
     * @param selection can be used as a mask to shape @param srcDev to
     * something interesting in the same step it is rendered to the current
     * paint device. @param selection 's colorspace must be alpha8 (the
     * colorspace for selections/transparency), the rectangle formed by
     * @param selX, @param selY, @param srcWidth and @param srcHeight must not go
     * beyond its limits, and they must be different from zero.
     * @param selection and KisPainter's selection (the user selection) are
     * fused together through the composite operation COMPOSITE_MULT.
     * Any pixel read outside the limits of @param srcDev will return the
     * default pixel, this is a property of \ref KisPaintDevice.
     *
     * @param dstX the destination x-coordinate
     * @param dstY the destination y-coordinate
     * @param srcDev the source device
     * @param selection the custom selection to apply on the source device
     * @param selX the selection x-coordinate
     * @param selY the selection y-coordinate
     * @param srcX the source x-coordinate
     * @param srcY the source y-coordinate
     * @param srcWidth the width of the region to be manipulated
     * @param srcHeight the height of the region to be manipulated
     *
     */
    void bitBltWithFixedSelection(qint32 dstX, qint32 dstY,
                                  const KisPaintDeviceSP srcDev,
                                  const KisFixedPaintDeviceSP selection,
                                  qint32 selX, qint32 selY,
                                  qint32 srcX, qint32 srcY,
                                  quint32 srcWidth, quint32 srcHeight);

    /**
     * Convenience method that assumes @param selX, @param selY, @param srcX and @param srcY are
     * equal to 0. Best used when @param selection and the desired area of @param srcDev have exactly
     * the same dimensions and are specially made for each other.
     *
     * @param dstX the destination x-coordinate
     * @param dstY the destination y-coordinate
     * @param srcDev the source device
     * @param selection the custom selection to apply on the source device
     * @param srcWidth the width of the region to be manipulated
     * @param srcHeight the height of the region to be manipulated
     */
    void bitBltWithFixedSelection(qint32 dstX, qint32 dstY,
                                  const KisPaintDeviceSP srcDev,
                                  const KisFixedPaintDeviceSP selection,
                                  quint32 srcWidth, quint32 srcHeight);

    /**
     * Blast a region of srcWidth @param srcWidth and srcHeight @param srcHeight from @param srcDev onto the current
     * paint device. @param srcX and @param srcY set the x and y positions of the
     * origin top-left corner, @param dstX and @param dstY those of the destination.
     * @param srcDev is a \ref KisFixedPaintDevice: this means that @param srcDev must have the same
     * colorspace as the destination device.
     *
     * @param dstX the destination x-coordinate
     * @param dstY the destination y-coordinate
     * @param srcDev the source device
     * @param srcX the source x-coordinate
     * @param srcY the source y-coordinate
     * @param srcWidth the width of the region to be manipulated
     * @param srcHeight the height of the region to be manipulated
     */
    void bltFixed(qint32 dstX, qint32 dstY,
                  const KisFixedPaintDeviceSP srcDev,
                  qint32 srcX, qint32 srcY,
                  qint32 srcWidth, qint32 srcHeight);

    /**
     * Convenience method that uses QPoint and QRect.
     *
     * @param pos the destination coordinate, it replaces @param dstX and @param dstY.
     * @param srcDev the source device.
     * @param srcRect the rectangle describing the area to blast from @param srcDev into the current paint device.
     * @param srcRect replaces @param srcX, @param srcY, @param srcWidth and @param srcHeight.
     *
     */
    void bltFixed(const QPoint & pos, const KisFixedPaintDeviceSP srcDev, const QRect & srcRect);

    /**
     * Blasts a @param selection of srcWidth @param srcWidth and srcHeight @param srcHeight
     * of @param srcDev on the current paint device. There is parameters to control
     * the top-left corner of the area in each respective paint device (@param dstX,
     * @param dstY, @param srcX, @param srcY).
     * @param selection can be used as a mask to shape @param srcDev to something
     * interesting in the same step it is rendered to the current paint device.
     * @param srcDev is a \ref KisFixedPaintDevice: this means that @param srcDev
     * must have the same colorspace as the destination device.
     * @param selection 's colorspace must be alpha8 (the colorspace for
     * selections/transparency).
     * The rectangle formed by the respective top-left coordinates of each device
     * and @param srcWidth and @param srcHeight must not go beyond their limits, and
     * they must be different from zero.
     * @param selection and KisPainter's selection (the user selection) are
     * fused together through the composite operation COMPOSITE_MULT.
     *
     * @param dstX the destination x-coordinate
     * @param dstY the destination y-coordinate
     * @param srcDev the source device
     * @param selection the selection stored in fixed device
     * @param selX the selection x-coordinate
     * @param selY the selection y-coordinate
     * @param srcX the source x-coordinate
     * @param srcY the source y-coordinate
     * @param srcWidth the width of the region to be manipulated
     * @param srcHeight the height of the region to be manipulated
     */
    void bltFixedWithFixedSelection(qint32 dstX, qint32 dstY,
                                    const KisFixedPaintDeviceSP srcDev,
                                    const KisFixedPaintDeviceSP selection,
                                    qint32 selX, qint32 selY,
                                    qint32 srcX, qint32 srcY,
                                    quint32 srcWidth, quint32 srcHeight);

    /**
     * Convenience method that assumes @param selX, @param selY, @param srcX and @param srcY are
     * equal to 0. Best used when @param selection and @param srcDev have exactly the same
     * dimensions and are specially made for each other.
     *
     * @param dstX the destination x-coordinate
     * @param dstY the destination y-coordinate
     * @param srcDev the source device
     * @param selection the custom selection to apply on the source device
     * @param srcWidth the width of the region to be manipulated
     * @param srcHeight the height of the region to be manipulated
     */
    void bltFixedWithFixedSelection(qint32 dstX, qint32 dstY,
                                    const KisFixedPaintDeviceSP srcDev,
                                    const KisFixedPaintDeviceSP selection,
                                    quint32 srcWidth, quint32 srcHeight);

    /**
     * fills a region of width @param width and height @param height of the current
     * paint device with the color @param color. @param x and @param y set the x and y positions of the
     * origin top-left corner.
     *
     * @param x the destination x-coordinate
     * @param y the destination y-coordinate
     * @param width the width of the region to be manipulated
     * @param height the height of the region to be manipulated
     * @param color the color the area is filled with
     */
    void fill(qint32 x, qint32 y, qint32 width, qint32 height, const KoColor& color);

    /**
     * First you need to setup the painter with setMirrorInformation,
     * then these set of methods provide way to render the devices mirrored
     * according the axisCenter vertically or horizontally or both.
     *
     * @param rc rectangle area covered by dab
     * @param dab this device will be mirrored in-place, it means that it will be changed
     */
    void renderMirrorMask(QRect rc, KisFixedPaintDeviceSP dab);
    void renderMirrorMask(QRect rc, KisFixedPaintDeviceSP dab, KisFixedPaintDeviceSP mask);
    void renderMirrorMask(QRect rc, KisPaintDeviceSP dab);
    void renderMirrorMask(QRect rc, KisPaintDeviceSP dab, int sx, int sy, KisFixedPaintDeviceSP mask);

    /**
     * Convenience method for renderMirrorMask(), allows to choose whether
     * we need to preserve out dab or do the transformations in-place.
     *
     * @param rc rectangle area covered by dab
     * @param dab the device to render
     * @param preserveDab states whether a temporary device should be
     *                    created to do the transformations
     */
    void renderMirrorMaskSafe(QRect rc, KisFixedPaintDeviceSP dab, bool preserveDab);

    /**
     * Convenience method for renderMirrorMask(), allows to choose whether
     * we need to preserve our fixed mask or do the transformations in-place.
     *
     * @param rc rectangle area covered by dab
     * @param dab the device to render
     * @param mask mask to use for rendering
     * @param preserveMask states whether a temporary device should be
     *                    created to do the transformations
     */
    void renderMirrorMaskSafe(QRect rc, KisPaintDeviceSP dab, int sx, int sy, KisFixedPaintDeviceSP mask, bool preserveMask);

    /**
     * A complex method that re-renders a dab on an \p rc area.
     * The \p rc  area and all the dedicated mirroring areas are cleared
     * before the painting, so this method should be used by paintops
     * which do not update the canvas incrementally, but instead
     * regenerate some internal cache \p dab with the COMPOSITE_COPY op.
     *
     * \see KisExperimentPaintOp
     */
    void renderDabWithMirroringNonIncremental(QRect rc, KisPaintDeviceSP dab);

    /**
      * The methods in this class do not tell the paintdevice to update, but they calculate the
      * dirty area. This method returns this dirty area and resets it.
      */
    QVector<QRect> takeDirtyRegion();

    /**
     * Paint a line that connects the dots in points
     */
    void paintPolyline(const QVector <QPointF> &points,
                       int index = 0, int numPoints = -1);

    /**
     * Draw a line between pos1 and pos2 using the currently set brush and color.
     * If savedDist is less than zero, the brush is painted at pos1 before being
     * painted along the line using the spacing setting.
     * @return the drag distance, that is the remains of the distance between p1 and p2 not covered
     * because the currenlty set brush has a spacing greater than that distance.
     */
    KisDistanceInformation paintLine(const KisPaintInformation &pi1,
                     const KisPaintInformation &pi2,
                     const KisDistanceInformation& savedDist = KisDistanceInformation());

    /**
     * Draw a Bezier curve between pos1 and pos2 using control points 1 and 2.
     * If savedDist is less than zero, the brush is painted at pos1 before being
     * painted along the curve using the spacing setting.
     * @return the drag distance, that is the remains of the distance between p1 and p2 not covered
     * because the currenlty set brush has a spacing greater than that distance.
     */
    KisDistanceInformation paintBezierCurve(const KisPaintInformation &pi1,
                            const QPointF &control1,
                            const QPointF &control2,
                            const KisPaintInformation &pi2,
                            const KisDistanceInformation& savedDist = KisDistanceInformation());
    /**
     * Fill the given vector points with the points needed to draw the Bezier curve between
     * pos1 and pos2 using control points 1 and 2, excluding the final pos2.
     */
    void getBezierCurvePoints(const QPointF &pos1,
                              const QPointF &control1,
                              const QPointF &control2,
                              const QPointF &pos2,
                              vQPointF& points) const;

    /**
     * Paint a rectangle.
     * @param rect the rectangle to paint.
     */
    void paintRect(const QRectF &rect);

    /**
     * Paint a rectangle.
     *
     * @param x x coordinate of the top-left corner
     * @param y y coordinate of the top-left corner
     * @param w the rectangle width
     * @param h the rectangle height
     */
    void paintRect(const qreal x,
                   const qreal y,
                   const qreal w,
                   const qreal h);

    /**
     * Paint the ellipse that fills the given rectangle.
     *
     * @param rect the rectangle containing the ellipse to paint.
     */
    void paintEllipse(const QRectF &rect);

    /**
     * Paint the ellipse that fills the given rectangle.
     *
     * @param x x coordinate of the top-left corner
     * @param y y coordinate of the top-left corner
     * @param w the rectangle width
     * @param h the rectangle height
     */
    void paintEllipse(const qreal x,
                      const qreal y,
                      const qreal w,
                      const qreal h);

    /**
     * Paint the polygon with the points given in points. It automatically closes the polygon
     * by drawing the line from the last point to the first.
     */
    void paintPolygon(const vQPointF& points);

    /** Draw a spot at pos using the currently set paint op, brush and color */
    qreal paintAt(const KisPaintInformation &pos);

    /**
     * Stroke the given QPainterPath.
     */
    void paintPainterPath(const QPainterPath& path);

    /**
     * Fills the area enclosed by the given QPainterPath
     * Convenience method for fillPainterPath(path, rect)
     */
    void fillPainterPath(const QPainterPath& path);

    /**
     * Fills the portion of an area enclosed by the given QPainterPath
     *
     * \param rect the portion of the path to fill
     */
    void fillPainterPath(const QPainterPath& path, const QRect &requestedRect);

    /**
     * Draw the path using the Pen
     */
    void drawPainterPath(const QPainterPath& path, const QPen& pen);

    /**
     * paint an unstroked one-pixel wide line from specified start position to the
     * specified end position.
     *
     */
    void drawLine(const QPointF & start, const QPointF & end);

    /**
     * paint an unstroked line with thickness from specified start position to the
     * specified end position. Scanline algorithm is used.
     */
    void drawLine(const QPointF &start, const QPointF &end, qreal width, bool antialias);


    /**
     * paints an unstroked, aliased one-pixel line using the DDA algorithm from specified start position to the
     * specified end position.
     *
     */
    void drawDDALine(const QPointF & start, const QPointF & end);

    /**
     * Paint an unstroked, wobbly one-pixel wide line from the specified start to the specified
     * end position.
     *
     */
    void drawWobblyLine(const QPointF & start, const QPointF & end);

    /**
     * Paint an unstroked, anti-aliased one-pixel wide line from the specified start to the specified
     * end position using the Wu algorithm
     */
    void drawWuLine(const QPointF & start, const QPointF & end);

    /**
     * Paint an unstroked wide line from the specified start to the specified
     * end position with width varying from @param w1 at the start to @param w2 at
     * the end.
     *
     * XXX: the width should be set in doubles, not integers.
     */
    void drawThickLine(const QPointF & start, const QPointF & end, int startWidth, int endWidth);

    /**
     * Set the channelflags: a bit array where true means that the
     * channel corresponding in position with the bit will be read
     * by the operation, and false means that it will not be affected.
     *
     * An empty channelFlags parameter means that all channels are
     * affected.
     *
     * @param the bit array that masks the source channels; only
     * the channels where the corresponding bit is true will will be
     * composited onto the destination device.
     */
    void setChannelFlags(QBitArray channelFlags);

    /// @return the channel flags
    QBitArray channelFlags();

    /**
     * Set the paintop preset to use. If @param image is given,
     * the paintop will be created using this image as parameter.
     * Some paintops really want to know about the image they work
     * for, e.g. the duplicate paintop.
     */
    void setPaintOpPreset(KisPaintOpPresetSP preset, KisImageWSP image);

    /// Return the paintop preset
    KisPaintOpPresetSP preset() const;

    /**
     * Return the active paintop (which is created based on the specified preset and
     * will be deleted as soon as the KisPainter instance dies).
     */
    KisPaintOp* paintOp() const;

    void setMirrorInformation(const QPointF &axisCenter, bool mirrorHorizontaly, bool mirrorVerticaly);

    /**
     * copy the mirror information to other painter
     */
    void copyMirrorInformation(KisPainter * painter);

    /**
     * Returns whether the mirroring methods will do any
     * work when called
     */
    bool hasMirroring() const;

    /// Set the current pattern
    void setPattern(const KisPattern * pattern);

    /// Returns the currently set pattern
    const KisPattern * pattern() const;

    /**
     * Set the color that will be used to paint with, and convert it
     * to the color space of the current paint device.
     */
    void setPaintColor(const KoColor& color);

    /// Returns the color that will be used to paint with
    const KoColor &paintColor() const;

    /**
     * Set the current background color, and convert it
     * to the color space of the current paint device.
     */
    void setBackgroundColor(const KoColor& color);

    /// Returns the current background color
    const KoColor &backgroundColor() const;

    /// Set the current generator (a generator can be used to fill an area
    void setGenerator(const KisFilterConfiguration * generator);

    /// @return the current generator configuration
    const KisFilterConfiguration * generator() const;

    /// This enum contains the styles with which we can fill things like polygons and ellipses
    enum FillStyle {
        FillStyleNone,
        FillStyleForegroundColor,
        FillStyleBackgroundColor,
        FillStylePattern,
        FillStyleGradient,
        FillStyleStrokes,
        FillStyleGenerator
    };

    /// Set the current style with which to fill
    void setFillStyle(FillStyle fillStyle);

    /// Returns the current fill style
    FillStyle fillStyle() const;

    /// Set whether a polygon's filled area should be anti-aliased or not. The default is true.
    void setAntiAliasPolygonFill(bool antiAliasPolygonFill);

    /// Return whether a polygon's filled area should be anti-aliased or not
    bool antiAliasPolygonFill();

    /// The style of the brush stroke around polygons and so
    enum StrokeStyle {
        StrokeStyleNone,
        StrokeStyleBrush
    };

    /// Set the current brush stroke style
    void setStrokeStyle(StrokeStyle strokeStyle);

    /// Returns the current brush stroke style
    StrokeStyle strokeStyle() const;

    void setFlow(quint8 flow);

    quint8 flow() const;

    /// Set the opacity which is used in painting (like filling polygons)
    void setOpacity(quint8 opacity);

    /// Returns the opacity that is used in painting
    quint8 opacity() const;

    /// Sets the bounds of the painter area; if not set, the painter
    /// will happily paint where you ask it, making the paint device
    /// larger as it goes
    void setBounds(const QRect & bounds);
    QRect bounds();

    /// Set the composite op for this painter
    void setCompositeOp(const KoCompositeOp * op);
    const KoCompositeOp * compositeOp();

    /// Set the composite op for this painter by string.
    /// Note: the colorspace must be set previously!
    void setCompositeOp(const QString& op);

    /**
     * Add the r to the current dirty rect.
     */
    void addDirtyRect(const QRect & r);

    /**
     * Reset the selection to the given selection. All painter actions will be
     * masked by the specified selection.
     */
    void setSelection(KisSelectionSP selection);

    /**
     * @return the selection set on this painter.
     */
    KisSelectionSP selection();

    void setGradient(const KoAbstractGradient* gradient);
    const KoAbstractGradient* gradient() const;

    /**
    * Set the size of the tile in fillPainterPath, useful when optimizing the use of fillPainterPath
    * e.g. Spray paintop uses more small tiles, although selections uses bigger tiles. QImage::fill
    * is quite expensive so with smaller images you can save instructions
    * Default and maximum size is 256x256 image
    */
    void setMaskImageSize(qint32 width, qint32 height);

    /**
     * If the alpha channel is locked, the alpha values of the paint device we are painting on
     * will not change.
     */
    void setLockAlpha(bool protect);
    bool alphaLocked() const;


    /**
     * set the rendering intent in case pixels need to be converted before painting
     */
    void setRenderingIntent(KoColorConversionTransformation::Intent intent);

    /**
     * set the conversion flags in case pixels need to be converted before painting
     */
    void setColorConversionFlags(KoColorConversionTransformation::ConversionFlags conversionFlags);

protected:
    /// Initialize, set everything to '0' or defaults
    void init();

    /// Fill the polygon defined by points with the fillStyle
    void fillPolygon(const vQPointF& points, FillStyle fillStyle);

private:

    KisPainter(const KisPainter&);
    KisPainter& operator=(const KisPainter&);

    float frac(float value) {
        float tmp = 0;
        return modff(value , &tmp);
    }

    float invertFrac(float value) {
        float tmp = 0;
        return 1.0f - modff(value , &tmp);
    }

protected:
    KoUpdater * progressUpdater();

private:
    template <bool useOldSrcData>
        void bitBltImpl(qint32 dstX, qint32 dstY,
                        const KisPaintDeviceSP srcDev,
                        qint32 srcX, qint32 srcY,
                        qint32 srcWidth, qint32 srcHeight);

    inline void compositeOnePixel(quint8 *dst, const KoColor &color);

private:

    struct Private;
    Private* const d;
};


#endif // KIS_PAINTER_H_

