예제 #1
0
파일: spray_brush.cpp 프로젝트: KDE/krita
qreal SprayBrush::rotationAngle(KisRandomSourceSP randomSource)
{
    qreal rotation = 0.0;

    if (m_shapeDynamicsProperties->fixedRotation) {
        rotation = deg2rad(m_shapeDynamicsProperties->fixedAngle);
    }

    if (m_shapeDynamicsProperties->randomRotation) {

        qreal randomValue = 0.0;

        if (m_properties->gaussian) {
            randomValue = qBound<qreal>(0.0, randomSource->generateGaussian(0.0, 0.5), 1.0);
        } else {
            randomValue = randomSource->generateNormalized();
        }

        rotation =
            linearInterpolation(rotation ,
                                M_PI * 2.0 * randomValue,
                                m_shapeDynamicsProperties->randomRotationWeight);
    }

    return rotation;
}
예제 #2
0
void HairyBrush::paintLine(KisPaintDeviceSP dab, KisPaintDeviceSP layer, const KisPaintInformation &pi1, const KisPaintInformation &pi2, qreal scale, qreal rotation)
{
    m_counter++;

    qreal x1 = pi1.pos().x();
    qreal y1 = pi1.pos().y();

    qreal x2 = pi2.pos().x();
    qreal y2 = pi2.pos().y();

    qreal dx = x2 - x1;
    qreal dy = y2 - y1;

    // TODO:this angle is different from the drawing angle in sensor (info.angle()). The bug is caused probably due to
    // not computing the drag vector properly in paintBezierLine when smoothing is used
    //qreal angle = atan2(dy, dx);
    qreal angle = rotation;

    qreal mousePressure = 1.0;
    if (m_properties->useMousePressure) { // want pressure from mouse movement
        qreal distance = sqrt(dx * dx + dy * dy);
        mousePressure = (1.0 - computeMousePressure(distance));
        scale *= mousePressure;
    }
    // this pressure controls shear and ink depletion
    qreal pressure = mousePressure * (pi2.pressure() * 2);

    Bristle *bristle = 0;
    KoColor bristleColor(dab->colorSpace());

    m_dabAccessor = dab->createRandomAccessorNG((int)x1, (int)y1);

    m_dab = dab;

    // initialization block
    if (firstStroke()) {
        initAndCache();
    }

    // if this is first time the brush touches the canvas and we use soak the ink from canvas
    if (firstStroke() && m_properties->useSoakInk) {
        if (layer) {
            colorifyBristles(layer, pi1.pos());
        }
        else {
            dbgKrita << "Can't soak the ink from the layer";
        }
    }

    KisRandomSourceSP randomSource = pi2.randomSource();

    qreal fx1, fy1, fx2, fy2;
    qreal randomX, randomY;
    qreal shear;

    float inkDeplation = 0.0;
    int inkDepletionSize = m_properties->inkDepletionCurve.size();
    int bristleCount = m_bristles.size();
    int bristlePathSize;
    qreal treshold = 1.0 - pi2.pressure();
    for (int i = 0; i < bristleCount; i++) {

        if (!m_bristles.at(i)->enabled()) continue;
        bristle = m_bristles[i];

        randomX = (randomSource->generateNormalized() * 2 - 1.0) * m_properties->randomFactor;
        randomY = (randomSource->generateNormalized() * 2 - 1.0) * m_properties->randomFactor;

        shear = pressure * m_properties->shearFactor;

        m_transform.reset();
        m_transform.rotateRadians(-angle);
        m_transform.scale(scale, scale);
        m_transform.translate(randomX, randomY);
        m_transform.shear(shear, shear);

        if (firstStroke() || (!m_properties->connectedPath)) {
            // transform start dab
            m_transform.map(bristle->x(), bristle->y(), &fx1, &fy1);
            // transform end dab
            m_transform.map(bristle->x(), bristle->y(), &fx2, &fy2);
        }
        else {
            // continue the path of the bristle from the previous position
            fx1 = bristle->prevX();
            fy1 = bristle->prevY();
            m_transform.map(bristle->x(), bristle->y(), &fx2, &fy2);
        }
        // remember the end point
        bristle->setPrevX(fx2);
        bristle->setPrevY(fy2);

        // all coords relative to device position
        fx1 += x1;
        fy1 += y1;

        fx2 += x2;
        fy2 += y2;

        if (m_properties->threshold && (bristle->length() < treshold)) continue;
        // paint between first and last dab
        const QVector<QPointF> bristlePath = m_trajectory.getLinearTrajectory(QPointF(fx1, fy1), QPointF(fx2, fy2), 1.0);
        bristlePathSize = m_trajectory.size();

        memcpy(bristleColor.data(), bristle->color().data() , m_pixelSize);
        for (int i = 0; i < bristlePathSize ; i++) {

            if (m_properties->inkDepletionEnabled) {
                inkDeplation = fetchInkDepletion(bristle, inkDepletionSize);

                if (m_properties->useSaturation && m_transfo != 0) {
                    saturationDepletion(bristle, bristleColor, pressure, inkDeplation);
                }

                if (m_properties->useOpacity) {
                    opacityDepletion(bristle, bristleColor, pressure, inkDeplation);
                }

            }
            else {
                if (bristleColor.opacityU8() != 0) {
                    bristleColor.setOpacity(bristle->length());
                }
            }

            addBristleInk(bristle, bristlePath.at(i), bristleColor);
            bristle->setInkAmount(1.0 - inkDeplation);
            bristle->upIncrement();
        }

    }
    m_dab = 0;
    m_dabAccessor = 0;
}
예제 #3
0
파일: spray_brush.cpp 프로젝트: KDE/krita
void SprayBrush::paint(KisPaintDeviceSP dab, KisPaintDeviceSP source,
                       const KisPaintInformation& info,
                       qreal rotation, qreal scale,
                       qreal additionalScale,
                       const KoColor &color, const KoColor &bgColor)
{
    KisRandomSourceSP randomSource = info.randomSource();

    // initializing painter
    if (!m_painter) {
        m_painter = new KisPainter(dab);
        m_painter->setFillStyle(KisPainter::FillStyleForegroundColor);
        m_painter->setMaskImageSize(m_shapeProperties->width, m_shapeProperties->height);
        m_dabPixelSize = dab->colorSpace()->pixelSize();
        if (m_colorProperties->useRandomHSV) {
            m_transfo = dab->colorSpace()->createColorTransformation("hsv_adjustment", QHash<QString, QVariant>());
        }

        m_brushQImage = m_shapeProperties->image;
        if (!m_brushQImage.isNull()) {
            m_brushQImage = m_brushQImage.scaled(m_shapeProperties->width, m_shapeProperties->height);
        }
        m_imageDevice = new KisPaintDevice(dab->colorSpace());
    }


    qreal x = info.pos().x();
    qreal y = info.pos().y();
    KisRandomAccessorSP accessor = dab->createRandomAccessorNG(qRound(x), qRound(y));

    Q_ASSERT(color.colorSpace()->pixelSize() == dab->pixelSize());
    m_inkColor = color;
    KisCrossDeviceColorPicker colorPicker(source, m_inkColor);

    // apply size sensor
    m_radius = m_properties->radius() * scale * additionalScale;

    // jitter movement
    if (m_properties->jitterMovement) {
        x = x + ((2 * m_radius * randomSource->generateNormalized()) - m_radius) * m_properties->amount;
        y = y + ((2 * m_radius * randomSource->generateNormalized()) - m_radius) * m_properties->amount;
    }

    // this is wrong for every shape except pixel and anti-aliased pixel


    if (m_properties->useDensity) {
        m_particlesCount = (m_properties->coverage * (M_PI * pow2(m_radius)) / pow2(additionalScale));
    }
    else {
        m_particlesCount = m_properties->particleCount;
    }

    QHash<QString, QVariant> params;
    qreal nx, ny;
    int ix, iy;

    qreal angle;
    qreal length;
    qreal rotationZ = 0.0;
    qreal particleScale = 1.0;

    bool shouldColor = true;
    if (m_colorProperties->fillBackground) {
        m_painter->setPaintColor(bgColor);
        paintCircle(m_painter, x, y, m_radius);
    }

    QTransform m;
    m.reset();
    m.rotateRadians(-rotation + deg2rad(m_properties->brushRotation));
    m.scale(m_properties->scale, m_properties->scale);

    for (quint32 i = 0; i < m_particlesCount; i++) {
        // generate random angle
        angle = randomSource->generateNormalized() * M_PI * 2;

        // generate random length
        if (m_properties->gaussian) {
            length = randomSource->generateGaussian(0.0, 0.5);
        }
        else {
            length = randomSource->generateNormalized();
        }

        if (m_shapeDynamicsProperties->enabled) {
            // rotation
            rotationZ = rotationAngle(randomSource);

            if (m_shapeDynamicsProperties->followCursor) {

                rotationZ = linearInterpolation(rotationZ, angle, m_shapeDynamicsProperties->followCursorWeigth);
            }


            if (m_shapeDynamicsProperties->followDrawingAngle) {

                rotationZ = linearInterpolation(rotationZ, info.drawingAngle(), m_shapeDynamicsProperties->followDrawingAngleWeight);
            }

            // random size - scale
            if (m_shapeDynamicsProperties->randomSize) {
                particleScale = randomSource->generateNormalized();
            }
        }
        // generate polar coordinate
        nx = (m_radius * cos(angle)  * length);
        ny = (m_radius * sin(angle)  * length);

        // compute the height of the ellipse
        ny *= m_properties->aspect;

        // transform
        m.map(nx, ny, &nx, &ny);

        // color transformation

        if (shouldColor) {
            if (m_colorProperties->sampleInputColor) {
                colorPicker.pickOldColor(nx + x, ny + y, m_inkColor.data());
            }

            // mix the color with background color
            if (m_colorProperties->mixBgColor) {
                KoMixColorsOp * mixOp = dab->colorSpace()->mixColorsOp();

                const quint8 *colors[2];
                colors[0] = m_inkColor.data();
                colors[1] = bgColor.data();

                qint16 colorWeights[2];
                int MAX_16BIT = 255;
                qreal blend = info.pressure();

                colorWeights[0] = static_cast<quint16>(blend * MAX_16BIT);
                colorWeights[1] = static_cast<quint16>((1.0 - blend) * MAX_16BIT);
                mixOp->mixColors(colors, colorWeights, 2, m_inkColor.data());
            }

            if (m_colorProperties->useRandomHSV && m_transfo) {
                params["h"] = (m_colorProperties->hue / 180.0) * randomSource->generateNormalized();
                params["s"] = (m_colorProperties->saturation / 100.0) * randomSource->generateNormalized();
                params["v"] = (m_colorProperties->value / 100.0) * randomSource->generateNormalized();
                m_transfo->setParameters(params);
                m_transfo->setParameter(3, 1);//sets the type to HSV. For some reason 0 is not an option.
                m_transfo->setParameter(4, false);//sets the colorize to false.
                m_transfo->transform(m_inkColor.data(), m_inkColor.data() , 1);
            }

            if (m_colorProperties->useRandomOpacity) {
                quint8 alpha = qRound(randomSource->generateNormalized() * OPACITY_OPAQUE_U8);
                m_inkColor.setOpacity(alpha);
                m_painter->setOpacity(alpha);
            }

            if (!m_colorProperties->colorPerParticle) {
                shouldColor = false;
            }

            m_painter->setPaintColor(m_inkColor);
        }

        qreal jitteredWidth = qMax(1.0 * additionalScale, m_shapeProperties->width * particleScale * additionalScale);
        qreal jitteredHeight = qMax(1.0 * additionalScale, m_shapeProperties->height * particleScale * additionalScale);

        if (m_shapeProperties->enabled) {
            switch (m_shapeProperties->shape) {
            // ellipse
            case 0:
            {
                if (m_shapeProperties->width == m_shapeProperties->height) {
                    paintCircle(m_painter, nx + x, ny + y, jitteredWidth * 0.5);
                }
                else {
                    paintEllipse(m_painter, nx + x, ny + y, jitteredWidth * 0.5 , jitteredHeight * 0.5, rotationZ);
                }
                break;
            }
            // rectangle
            case 1:
            {
                paintRectangle(m_painter, nx + x, ny + y, qRound(jitteredWidth) , qRound(jitteredHeight), rotationZ);
                break;
            }
            // wu-particle
            case 2: {
                paintParticle(accessor, m_inkColor, nx + x, ny + y);
                break;
            }
            // pixel
            case 3: {
                ix = qRound(nx + x);
                iy = qRound(ny + y);
                accessor->moveTo(ix, iy);
                memcpy(accessor->rawData(), m_inkColor.data(), m_dabPixelSize);
                break;
            }
            case 4: {
                if (!m_brushQImage.isNull()) {

                    QTransform m;
                    m.rotate(rad2deg(rotationZ));
                    m.scale(additionalScale, additionalScale);

                    if (m_shapeDynamicsProperties->randomSize) {
                        m.scale(particleScale, particleScale);
                    }
                    m_transformed = m_brushQImage.transformed(m, Qt::SmoothTransformation);
                    m_imageDevice->convertFromQImage(m_transformed, 0);
                    KisRandomAccessorSP ac = m_imageDevice->createRandomAccessorNG(0, 0);
                    QRect rc = m_transformed.rect();

                    if (m_colorProperties->useRandomHSV && m_transfo) {

                        for (int y = rc.y(); y < rc.y() + rc.height(); y++) {
                            for (int x = rc.x(); x < rc.x() + rc.width(); x++) {
                                ac->moveTo(x, y);
                                m_transfo->transform(ac->rawData(), ac->rawData() , 1);
                            }
                        }
                    }

                    ix = qRound(nx + x - rc.width() * 0.5);
                    iy = qRound(ny + y - rc.height() * 0.5);
                    m_painter->bitBlt(QPoint(ix, iy), m_imageDevice, rc);
                    m_imageDevice->clear();
                    break;
                }
            }
            }
            // Auto-brush
        }
        else {
            KisDabShape shape(particleScale * additionalScale, 1.0, -rotationZ);
            QPointF hotSpot = m_brush->hotSpot(shape, info);
            QPointF pos(nx + x, ny + y);
            QPointF pt = pos - hotSpot;

            qint32 ix;
            qreal xFraction;
            qint32 iy;
            qreal yFraction;

            KisPaintOp::splitCoordinate(pt.x(), &ix, &xFraction);
            KisPaintOp::splitCoordinate(pt.y(), &iy, &yFraction);

            //KisFixedPaintDeviceSP dab;
            if (m_brush->brushType() == IMAGE ||
                    m_brush->brushType() == PIPE_IMAGE) {
                m_fixedDab = m_brush->paintDevice(m_fixedDab->colorSpace(),
                                                  shape, info, xFraction, yFraction);

                if (m_colorProperties->useRandomHSV && m_transfo) {
                    quint8 * dabPointer = m_fixedDab->data();
                    int pixelCount = m_fixedDab->bounds().width() * m_fixedDab->bounds().height();
                    m_transfo->transform(dabPointer, dabPointer, pixelCount);
                }

            }
            else {
                m_brush->mask(m_fixedDab, m_inkColor, shape,
                              info, xFraction, yFraction);
            }
            m_painter->bltFixed(QPoint(ix, iy), m_fixedDab, m_fixedDab->bounds());
        }
        if (m_colorProperties->colorPerParticle) {
            m_inkColor=color;//reset color//
        }
    }
    // recover from jittering of color,
    // m_inkColor.opacity is recovered with every paint
}
예제 #4
0
void KisSketchPaintOp::paintLine(const KisPaintInformation &pi1, const KisPaintInformation &pi2, KisDistanceInformation *currentDistance)
{
    Q_UNUSED(currentDistance);

    if (!m_brush || !painter()) return;

    if (!m_dab) {
        m_dab = source()->createCompositionSourceDevice();
        m_painter = new KisPainter(m_dab);
        m_painter->setPaintColor(painter()->paintColor());
    }
    else {
        m_dab->clear();
    }

    QPointF prevMouse = pi1.pos();
    QPointF mousePosition = pi2.pos();
    m_points.append(mousePosition);


    const qreal lodAdditionalScale = KisLodTransform::lodToScale(painter()->device());
    const qreal scale = lodAdditionalScale * m_sizeOption.apply(pi2);
    if ((scale * m_brush->width()) <= 0.01 || (scale * m_brush->height()) <= 0.01) return;

    const qreal currentLineWidth = qMax(0.9, lodAdditionalScale * m_lineWidthOption.apply(pi2, m_sketchProperties.lineWidth));

    const qreal currentOffsetScale = m_offsetScaleOption.apply(pi2, m_sketchProperties.offset);
    const double rotation = m_rotationOption.apply(pi2);
    const double currentProbability = m_densityOption.apply(pi2, m_sketchProperties.probability);

    // shaded: does not draw this line, chrome does, fur does
    if (m_sketchProperties.makeConnection) {
        drawConnection(prevMouse, mousePosition, currentLineWidth);
    }

    setCurrentScale(scale);
    setCurrentRotation(rotation);

    qreal thresholdDistance = 0.0;

    // update the mask for simple mode only once
    // determine the radius
    if (m_count == 0 && m_sketchProperties.simpleMode) {
        updateBrushMask(pi2, 1.0, 0.0);
        //m_radius = qMax(m_maskDab->bounds().width(),m_maskDab->bounds().height()) * 0.5;
        m_radius = 0.5 * qMax(m_brush->width(), m_brush->height());
    }

    if (!m_sketchProperties.simpleMode) {
        updateBrushMask(pi2, scale, rotation);
        m_radius = qMax(m_maskDab->bounds().width(), m_maskDab->bounds().height()) * 0.5;
        thresholdDistance = pow(m_radius, 2);
    }

    if (m_sketchProperties.simpleMode) {
        // update the radius according scale in simple mode
        thresholdDistance = pow(m_radius * scale, 2);
    }

    // determine density
    const qreal density = thresholdDistance * currentProbability;

    // probability behaviour
    qreal probability = 1.0 - currentProbability;

    QColor painterColor = painter()->paintColor().toQColor();
    QColor randomColor;
    KoColor color(m_dab->colorSpace());

    int w = m_maskDab->bounds().width();
    quint8 opacityU8 = 0;
    quint8 * pixel;
    qreal distance;
    QPoint  positionInMask;
    QPointF diff;

    int size = m_points.size();
    // MAIN LOOP
    for (int i = 0; i < size; i++) {
        diff = m_points.at(i) - mousePosition;
        distance = diff.x() * diff.x() + diff.y() * diff.y();

        // circle test
        bool makeConnection = false;
        if (m_sketchProperties.simpleMode) {
            if (distance < thresholdDistance) {
                makeConnection = true;
            }
            // mask test
        }
        else {
            if (m_brushBoundingBox.contains(m_points.at(i))) {
                positionInMask = (diff + m_hotSpot).toPoint();
                uint pos = ((positionInMask.y() * w + positionInMask.x()) * m_maskDab->pixelSize());
                if (pos < m_maskDab->allocatedPixels() * m_maskDab->pixelSize()) {
                    pixel = m_maskDab->data() + pos;
                    opacityU8 = m_maskDab->colorSpace()->opacityU8(pixel);
                    if (opacityU8 != 0) {
                        makeConnection = true;
                    }
                }
            }

        }

        if (!makeConnection) {
            // check next point
            continue;
        }

        if (m_sketchProperties.distanceDensity) {
            probability =  distance / density;
        }

        KisRandomSourceSP randomSource = pi2.randomSource();

        // density check
        if (randomSource->generateNormalized() >= probability) {
            QPointF offsetPt = diff * currentOffsetScale;

            if (m_sketchProperties.randomRGB) {
                /**
                 * Since the order of calculation of function
                 * parameters is not defined by C++ standard, we
                 * should generate values in an external code snippet
                 * which has a definite order of execution.
                 */
                qreal r1 = randomSource->generateNormalized();
                qreal r2 = randomSource->generateNormalized();
                qreal r3 = randomSource->generateNormalized();

                // some color transformation per line goes here
                randomColor.setRgbF(r1 * painterColor.redF(),
                                    r2 * painterColor.greenF(),
                                    r3 * painterColor.blueF());
                color.fromQColor(randomColor);
                m_painter->setPaintColor(color);
            }

            // distance based opacity
            quint8 opacity = OPACITY_OPAQUE_U8;
            if (m_sketchProperties.distanceOpacity) {
                opacity *= qRound((1.0 - (distance / thresholdDistance)));
            }

            if (m_sketchProperties.randomOpacity) {
                opacity *= randomSource->generateNormalized();
            }

            m_painter->setOpacity(opacity);

            if (m_sketchProperties.magnetify) {
                drawConnection(mousePosition + offsetPt, m_points.at(i) - offsetPt, currentLineWidth);
            }
            else {
                drawConnection(mousePosition + offsetPt, mousePosition - offsetPt, currentLineWidth);
            }



        }
    }// end of MAIN LOOP

    m_count++;

    QRect rc = m_dab->extent();
    quint8 origOpacity = m_opacityOption.apply(painter(), pi2);

    painter()->bitBlt(rc.x(), rc.y(), m_dab, rc.x(), rc.y(), rc.width(), rc.height());
    painter()->renderMirrorMask(rc, m_dab);
    painter()->setOpacity(origOpacity);
}