@elode - It’s probably possible, but I don’t know how to do it at the moment. I’ll look at it as a third additional mode. I just need to figure out how to get access to the background color from the texturing function.
@dkazakov - I figured out the performance issue… or rather I located it, though I don’t know why it’s an issue. I convert each pixel into a QColor so I could easily access the Value, then convert it back after I adjust the value based on the texture. The weird thing is, when I do this conversion using the already existing KoColorSpace::toQColor() and KoColorSpace::fromQColor() methods, the call to each of them causes extreme lag (I commented lines of code to single out which lines were causing the slowdown, and if I had either one in, the brush was really slow, but nothing else would slow it down). So I changed the code to what I have below, which basically does the exact same thing in almost the same way, and there’s no performance issue… I can play around with it more to see if those functions can be optimized, or if it’s just an issue with passing parameters around, but it’s weird… Here’s my current code for KisTextureProperties::apply():
void KisTextureProperties::apply(KisFixedPaintDeviceSP dab, const QPoint &offset, const KisPaintInformation & info)
{
if (!m_enabled) return;
KisPaintDeviceSP fillDevice = new KisPaintDevice(KoColorSpaceRegistry::instance()->alpha8());
QRect rect = dab->bounds();
KisPaintDeviceSP mask = m_maskInfo->mask();
const QRect maskBounds = m_maskInfo->maskBounds();
KIS_SAFE_ASSERT_RECOVER_RETURN(mask);
int x = offset.x() % maskBounds.width() - m_offsetX;
int y = offset.y() % maskBounds.height() - m_offsetY;
KisFillPainter fillPainter(fillDevice);
fillPainter.fillRect(x - 1, y - 1, rect.width() + 2, rect.height() + 2, mask, maskBounds);
fillPainter.end();
qreal pressure = m_strengthOption.apply(info);
quint8 *dabData = dab->data();
KisHLineIteratorSP iter = fillDevice->createHLineIteratorNG(x, y, rect.width());
for (int row = 0; row < rect.height(); ++row) {
for (int col = 0; col < rect.width(); ++col) {
if (m_texturingMode == MULTIPLY) {
dab->colorSpace()->multiplyAlpha(dabData, quint8(*iter->oldRawData() * pressure), 1);
}
else if (m_texturingMode == SUBTRACT) {
int pressureOffset = (1.0 - pressure) * 255;
qint16 maskA = *iter->oldRawData() + pressureOffset;
quint8 dabA = dab->colorSpace()->opacityU8(dabData);
dabA = qMax(0, (qint16)dabA - maskA);
dab->colorSpace()->setOpacity(dabData, dabA, 1);
}
else if (m_texturingMode == VALUE) {
int pressureOffset = (1.0 - pressure) * 255;
qint16 maskValue = *iter->oldRawData() + pressureOffset;
qreal maskValueF = qMax(qint16(0), qMin(qint16(255), maskValue)) / 255.0f;
int channelnumber = dab->colorSpace()->channelCount();
QVector <float> channelValuesF(channelnumber);
QColor dabQColor;
//dab->colorSpace()->toQColor(dabData, &dabQColor); // - slow!!!!
dab->colorSpace()->normalisedChannelsValue(dabData, channelValuesF);
dabQColor.setRgbF(channelValuesF[2], channelValuesF[1], channelValuesF[0], channelValuesF[3]);
qreal dabValue = dabQColor.valueF();
qreal dabHue = dabQColor.hueF();
qreal dabSaturation = dabQColor.saturationF();
qreal finalValue = maskValueF * dabValue;
dabQColor.setHsvF(dabHue, dabSaturation, finalValue, dabQColor.alphaF());
//dab->colorSpace()->fromQColor(dabQColor, dabData); // -slow
channelValuesF[0] = dabQColor.blueF();
channelValuesF[1] = dabQColor.greenF();
channelValuesF[2] = dabQColor.redF();
channelValuesF[3] = dabQColor.alphaF();
dab->colorSpace()->fromNormalisedChannelsValue(dabData, channelValuesF);
}
else {
}
iter->nextPixel();
dabData += dab->pixelSize();
}
iter->nextRow();
}
}