/** This method apply grain adjustment on a pixel color channel from YCrCb color space. NRand is the lead uniform noise set from matrix used to scan whole image step by step. Additionally noise is applied on pixel using Poisson or Gaussian distribution. */ void FilmGrainFilter::adjustYCbCr(DColor& col, double range, double nRand, int channel) { double y, cb, cr, n2; col.getYCbCr(&y, &cb, &cr); if (d->settings.photoDistribution) { n2 = randomizePoisson((d->settings.grainSize / 2.0) * (range / 1.414)); } else { n2 = randomizeGauss((d->settings.grainSize / 2.0) * (range / 1.414)); } switch (channel) { case Private::Luma: y = CLAMP(y + (nRand + n2) / d->div, 0.0, 1.0); break; case Private::ChromaBlue: cb = CLAMP(cb + (nRand + n2) / d->div, 0.0, 1.0); break; default: // ChromaRed cr = CLAMP(cr + (nRand + n2) / d->div, 0.0, 1.0); break; } col.setYCbCr(y, cb, cr, col.sixteenBit()); }
/* This code is shared by six methods. Write value of pixel w|h in data to pixel nw|nh in pResBits. Antialias if requested. */ void DistortionFXFilter::setPixelFromOther(int Width, int Height, bool sixteenBit, int bytesDepth, uchar* data, uchar* pResBits, int w, int h, double nw, double nh, bool AntiAlias) { DColor color; int offset = getOffset(Width, w, h, bytesDepth); if (AntiAlias) { uchar* const ptr = pResBits + offset; if (sixteenBit) { unsigned short* ptr16 = reinterpret_cast<unsigned short*>(ptr); PixelsAliasFilter().pixelAntiAliasing16(reinterpret_cast<unsigned short*>(data), Width, Height, nw, nh, ptr16 + 3, ptr16 + 2, ptr16 + 1, ptr16); } else { PixelsAliasFilter().pixelAntiAliasing(data, Width, Height, nw, nh, ptr + 3, ptr + 2, ptr + 1, ptr); } } else { // we get the position adjusted int offsetOther = getOffsetAdjusted(Width, Height, (int)nw, (int)nh, bytesDepth); // read color color.setColor(data + offsetOther, sixteenBit); // write color to destination color.setPixel(pResBits + offset); } }
void EditorTool::slotUpdateSpotInfo(const DColor& col, const QPoint& point) { DColor color = col; setToolInfoMessage(i18n("(%1,%2) RGBA:%3,%4,%5,%6", point.x(), point.y(), color.red(), color.green(), color.blue(), color.alpha())); }
D3DCOLOR D3D9Translator::getD3DColor( DColor c ) { DColor clamped = c; clamped.clamp(); return D3DCOLOR_ARGB( static_cast<uint8>(clamped.a * 255), static_cast<uint8>(clamped.r * 255), static_cast<uint8>(clamped.g * 255), static_cast<uint8>(clamped.b * 255)); }
void DistortionFXFilter::blockWavesMultithreaded(const Args& prm) { int Width = prm.orgImage->width(); int Height = prm.orgImage->height(); uchar* data = prm.orgImage->bits(); bool sixteenBit = prm.orgImage->sixteenBit(); int bytesDepth = prm.orgImage->bytesDepth(); uchar* pResBits = prm.destImage->bits(); int nw, nh; DColor color; int offset, offsetOther; int nHalfW = Width / 2; int nHalfH = Height / 2; for (int h = prm.start; runningFlag() && (h < prm.stop); ++h) { nw = nHalfW - prm.w; nh = nHalfH - h; if (prm.Mode) { nw = (int)(prm.w + prm.Amplitude * qSin(prm.Frequency * nw * (M_PI / 180))); nh = (int)(h + prm.Amplitude * qCos(prm.Frequency * nh * (M_PI / 180))); } else { nw = (int)(prm.w + prm.Amplitude * qSin(prm.Frequency * prm.w * (M_PI / 180))); nh = (int)(h + prm.Amplitude * qCos(prm.Frequency * h * (M_PI / 180))); } offset = getOffset(Width, prm.w, h, bytesDepth); offsetOther = getOffsetAdjusted(Width, Height, (int)nw, (int)nh, bytesDepth); // read color color.setColor(data + offsetOther, sixteenBit); // write color to destination color.setPixel(pResBits + offset); } }
void SharpenFilter::convolveImageMultithreaded(const Args& prm) { double maxClamp = m_destImage.sixteenBit() ? 16777215.0 : 65535.0; double* k = 0; double red, green, blue, alpha; int mx, my, sx, sy, mcx, mcy; DColor color; for (uint x = prm.start ; runningFlag() && (x < prm.stop) ; ++x) { k = prm.normal_kernel; red = green = blue = alpha = 0; sy = prm.y - prm.halfKernelWidth; for (mcy = 0 ; runningFlag() && (mcy < prm.kernelWidth) ; ++mcy, ++sy) { my = sy < 0 ? 0 : sy > (int)m_destImage.height() - 1 ? m_destImage.height() - 1 : sy; sx = x + (-prm.halfKernelWidth); for (mcx = 0 ; runningFlag() && (mcx < prm.kernelWidth) ; ++mcx, ++sx) { mx = sx < 0 ? 0 : sx > (int)m_destImage.width() - 1 ? m_destImage.width() - 1 : sx; color = m_orgImage.getPixelColor(mx, my); red += (*k) * (color.red() * 257.0); green += (*k) * (color.green() * 257.0); blue += (*k) * (color.blue() * 257.0); alpha += (*k) * (color.alpha() * 257.0); ++k; } } red = red < 0.0 ? 0.0 : red > maxClamp ? maxClamp : red + 0.5; green = green < 0.0 ? 0.0 : green > maxClamp ? maxClamp : green + 0.5; blue = blue < 0.0 ? 0.0 : blue > maxClamp ? maxClamp : blue + 0.5; alpha = alpha < 0.0 ? 0.0 : alpha > maxClamp ? maxClamp : alpha + 0.5; m_destImage.setPixelColor(x, prm.y, DColor((int)(red / 257UL), (int)(green / 257UL), (int)(blue / 257UL), (int)(alpha / 257UL), m_destImage.sixteenBit())); } }
void WhiteBalanceTool::slotColorSelectedFromOriginal(const DColor& color) { if ( d->settingsView->pickTemperatureIsOn() ) { WBContainer settings = d->settingsView->settings(); DColor dc = color; QColor tc = dc.getQColor(); WBFilter::autoWBAdjustementFromColor(tc, settings.temperature, settings.green); d->settingsView->setSettings(settings); d->settingsView->setOnPickTemperature(false); } else { return; } d->previewWidget->setCapturePointMode(false); slotTimer(); }
void DImageFilterActionTest::testActions() { QStringList files = QDir(imagePath()).entryList(QDir::Files); files.removeOne(originalImage()); DImg original(imagePath() + originalImage()); QVERIFY(!original.isNull()); foreach (const QString& fileName, files) { DImg ref(imagePath() + fileName); QVERIFY(!ref.isNull()); DImageHistory history = ref.getImageHistory(); FilterActionFilter filter; filter.setFilterActions(history.allActions()); QVERIFY(filter.isReproducible() || filter.isComplexAction()); filter.setupFilter(original.copy()); filter.startFilterDirectly(); qDebug() << filter.filterActions().size(); DImg img = filter.getTargetImage(); QVERIFY(ref.size() == img.size()); bool isEqual = true; DImg diff(ref.width(), ref.height(), ref.sixteenBit()); diff.fill(DColor(Qt::black)); for (uint x=0; x<ref.width(); x++) { for (uint y=0; y<ref.height(); y++) { DColor cref = ref.getPixelColor(x,y); DColor cres = img.getPixelColor(x,y); if (cref.red() != cres.red() || cref.green() != cres.green() || cref.blue() != cres.blue()) { //qDebug() << x << y; diff.setPixelColor(x,y, DColor(Qt::white)); isEqual = false; } } } if (!isEqual) { showDiff(original, ref, img, diff); } QVERIFY(isEqual); }
/** This method interpolate gain adjustments to apply grain on shadows, midtones and highlights colors. The output value is a coefficient computed between 0.0 and 1.0. */ double FilmGrainFilter::interpolate(int shadows, int midtones, int highlights, const DColor& col) { double s = (shadows + 100.0) / 200.0; double m = (midtones + 100.0) / 200.0; double h = (highlights + 100.0) / 200.0; double y, cb, cr; col.getYCbCr(&y, &cb, &cr); if (y >= 0.0 && y <= 0.5) { return (s + 2 * (m - s) * y); } else if (y >= 0.5 && y <= 1.0) { return (2 * (h - m) * y + 2 * m - h); } else { return 1.0; } }
void HSLFilter::applyHSL(DImg& image) { if (image.isNull()) { return; } bool sixteenBit = image.sixteenBit(); uint numberOfPixels = image.numPixels(); int progress; int hue, sat, lig; double vib = d->settings.vibrance; DColor color; if (sixteenBit) // 16 bits image. { unsigned short* data = (unsigned short*) image.bits(); for (uint i=0; runningFlag() && (i<numberOfPixels); ++i) { color = DColor(data[2], data[1], data[0], 0, sixteenBit); // convert RGB to HSL color.getHSL(&hue, &sat, &lig); // convert HSL to RGB color.setHSL(d->htransfer16[hue], vibranceBias(d->stransfer16[sat], hue, vib, sixteenBit), d->ltransfer16[lig], sixteenBit); data[2] = color.red(); data[1] = color.green(); data[0] = color.blue(); data += 4; progress = (int)(((double)i * 100.0) / numberOfPixels); if ( progress%5 == 0 ) { postProgress( progress ); } } } else // 8 bits image. { uchar* data = image.bits(); for (uint i=0; runningFlag() && (i<numberOfPixels); ++i) { color = DColor(data[2], data[1], data[0], 0, sixteenBit); // convert RGB to HSL color.getHSL(&hue, &sat, &lig); // convert HSL to RGB color.setHSL(d->htransfer[hue], vibranceBias(d->stransfer[sat],hue,vib,sixteenBit), d->ltransfer[lig], sixteenBit); data[2] = color.red(); data[1] = color.green(); data[0] = color.blue(); data += 4; progress = (int)(((double)i * 100.0) / numberOfPixels); if ( progress%5 == 0 ) { postProgress( progress ); } } } }
void CurvesSettings::slotSpotColorChanged(const DColor& color) { DColor sc = color; switch (d->curvesBox->picker()) { case CurvesBox::BlackTonal: { // Black tonal curves point. d->curvesBox->curves()->setCurvePoint(LuminosityChannel, 1, QPoint(qMax(qMax(sc.red(), sc.green()), sc.blue()), 42 * d->histoSegments / 256)); d->curvesBox->curves()->setCurvePoint(RedChannel, 1, QPoint(sc.red(), 42 * d->histoSegments / 256)); d->curvesBox->curves()->setCurvePoint(GreenChannel, 1, QPoint(sc.green(), 42 * d->histoSegments / 256)); d->curvesBox->curves()->setCurvePoint(BlueChannel, 1, QPoint(sc.blue(), 42 * d->histoSegments / 256)); d->curvesBox->resetPickers(); break; } case CurvesBox::GrayTonal: { // Gray tonal curves point. d->curvesBox->curves()->setCurvePoint(LuminosityChannel, 8, QPoint(qMax(qMax(sc.red(), sc.green()), sc.blue()), 128 * d->histoSegments / 256)); d->curvesBox->curves()->setCurvePoint(RedChannel, 8, QPoint(sc.red(), 128 * d->histoSegments / 256)); d->curvesBox->curves()->setCurvePoint(GreenChannel, 8, QPoint(sc.green(), 128 * d->histoSegments / 256)); d->curvesBox->curves()->setCurvePoint(BlueChannel, 8, QPoint(sc.blue(), 128 * d->histoSegments / 256)); d->curvesBox->resetPickers(); break; } case CurvesBox::WhiteTonal: { // White tonal curves point. d->curvesBox->curves()->setCurvePoint(LuminosityChannel, 15, QPoint(qMax(qMax(sc.red(), sc.green()), sc.blue()), 213 * d->histoSegments / 256)); d->curvesBox->curves()->setCurvePoint(RedChannel, 15, QPoint(sc.red(), 213 * d->histoSegments / 256)); d->curvesBox->curves()->setCurvePoint(GreenChannel, 15, QPoint(sc.green(), 213 * d->histoSegments / 256)); d->curvesBox->curves()->setCurvePoint(BlueChannel, 15, QPoint(sc.blue(), 213 * d->histoSegments / 256)); d->curvesBox->resetPickers(); break; } default: { d->curvesBox->setCurveGuide(color); return; } } // Calculate Red, green, blue curves. for (int i = LuminosityChannel ; i <= BlueChannel ; ++i) { d->curvesBox->curves()->curvesCalculateCurve(i); } d->curvesBox->repaint(); d->curvesBox->resetPickers(); emit signalSpotColorChanged(); }
void DistortionFXFilter::twirlMultithreaded(const Args& prm) { int Width = prm.orgImage->width(); int Height = prm.orgImage->height(); uchar* data = prm.orgImage->bits(); bool sixteenBit = prm.orgImage->sixteenBit(); int bytesDepth = prm.orgImage->bytesDepth(); uchar* pResBits = prm.destImage->bits(); DColor color; int offset; int nHalfW = Width / 2; int nHalfH = Height / 2; double lfXScale = 1.0; double lfYScale = 1.0; double lfAngle, lfNewAngle, lfAngleSum, lfCurrentRadius; double tw, nh, nw; if (Width > Height) { lfYScale = (double)Width / (double)Height; } else if (Height > Width) { lfXScale = (double)Height / (double)Width; } // the angle step is dist divided by 10000 double lfAngleStep = prm.dist / 10000.0; // now, we get the minimum radius double lfRadMax = (double)qMax(Width, Height) / 2.0; double th = lfYScale * (double)(prm.h - nHalfH); for (int w = prm.start; runningFlag() && (w < prm.stop); ++w) { tw = lfXScale * (double)(w - nHalfW); // now, we get the distance lfCurrentRadius = qSqrt(th * th + tw * tw); // if distance is less than maximum radius... if (lfCurrentRadius < lfRadMax) { // we find the angle from the center lfAngle = qAtan2(th, tw); // we get the accumuled angle lfAngleSum = lfAngleStep * (-1.0 * (lfCurrentRadius - lfRadMax)); // ok, we sum angle with accumuled to find a new angle lfNewAngle = lfAngle + lfAngleSum; // now we find the exact position's x and y nw = (double)nHalfW + qCos(lfNewAngle) * (lfCurrentRadius / lfXScale); nh = (double)nHalfH + qSin(lfNewAngle) * (lfCurrentRadius / lfYScale); setPixelFromOther(Width, Height, sixteenBit, bytesDepth, data, pResBits, w, prm.h, nw, nh, prm.AntiAlias); } else { // copy pixel offset = getOffset(Width, w, prm.h, bytesDepth); color.setColor(data + offset, sixteenBit); color.setPixel(pResBits + offset); } } }
void DistortionFXFilter::fisheyeMultithreaded(const Args& prm) { int Width = prm.orgImage->width(); int Height = prm.orgImage->height(); uchar* data = prm.orgImage->bits(); bool sixteenBit = prm.orgImage->sixteenBit(); int bytesDepth = prm.orgImage->bytesDepth(); uchar* pResBits = prm.destImage->bits(); double nh, nw, tw; DColor color; int offset; int nHalfW = Width / 2; int nHalfH = Height / 2; double lfXScale = 1.0; double lfYScale = 1.0; double lfCoeffStep = prm.Coeff / 1000.0; double lfRadius, lfAngle; if (Width > Height) { lfYScale = (double)Width / (double)Height; } else if (Height > Width) { lfXScale = (double)Height / (double)Width; } double lfRadMax = (double)qMax(Height, Width) / 2.0; double lfCoeff = lfRadMax / qLn(qFabs(lfCoeffStep) * lfRadMax + 1.0); double th = lfYScale * (double)(prm.h - nHalfH); for (int w = prm.start; runningFlag() && (w < prm.stop); ++w) { tw = lfXScale * (double)(w - nHalfW); // we find the distance from the center lfRadius = qSqrt(th * th + tw * tw); if (lfRadius < lfRadMax) { lfAngle = qAtan2(th, tw); if (prm.Coeff > 0.0) { lfRadius = (qExp(lfRadius / lfCoeff) - 1.0) / lfCoeffStep; } else { lfRadius = lfCoeff * qLn(1.0 + (-1.0 * lfCoeffStep) * lfRadius); } nw = (double)nHalfW + (lfRadius / lfXScale) * qCos(lfAngle); nh = (double)nHalfH + (lfRadius / lfYScale) * qSin(lfAngle); setPixelFromOther(Width, Height, sixteenBit, bytesDepth, data, pResBits, w, prm.h, nw, nh, prm.AntiAlias); } else { // copy pixel offset = getOffset(Width, w, prm.h, bytesDepth); color.setColor(data + offset, sixteenBit); color.setPixel(pResBits + offset); } } }
void NRFilter::filterImage() { DColor col; int progress; int width = m_orgImage.width(); int height = m_orgImage.height(); float clip = m_orgImage.sixteenBit() ? 65535.0 : 255.0; // Allocate buffers. for (int c = 0; c < 3; ++c) { d->fimg[c] = new float[width * height]; } d->buffer[1] = new float[width * height]; d->buffer[2] = new float[width * height]; // Read the full image and convert pixel values to float [0,1]. int j = 0; for (int y = 0; runningFlag() && (y < height); ++y) { for (int x = 0; runningFlag() && (x < width); ++x) { col = m_orgImage.getPixelColor(x, y); d->fimg[0][j] = col.red() / clip; d->fimg[1][j] = col.green() / clip; d->fimg[2][j] = col.blue() / clip; ++j; } } postProgress(10); // do colour model conversion sRGB[0,1] -> YCrCb. if (runningFlag()) { srgb2ycbcr(d->fimg, width * height); } postProgress(20); // denoise the channels individually for (int c = 0; runningFlag() && (c < 3); ++c) { d->buffer[0] = d->fimg[c]; if (d->settings.thresholds[c] > 0.0) { waveletDenoise(d->buffer, width, height, d->settings.thresholds[c], d->settings.softness[c]); progress = (int)(30.0 + ((double)c * 60.0) / 4); if (progress % 5 == 0) { postProgress(progress); } } } // Retransform the image data to sRGB[0,1]. if (runningFlag()) { ycbcr2srgb(d->fimg, width * height); } postProgress(80); // clip the values for (int c = 0; runningFlag() && (c < 3); ++c) { for (int i = 0; i < width * height; ++i) { d->fimg[c][i] = qBound(0.0F, d->fimg[c][i] * clip, clip); } } postProgress(90); // Write back the full image and convert pixel values from float [0,1]. j = 0; for (int y = 0; runningFlag() && (y < height); ++y) { for (int x = 0; x < width; ++x) { col.setRed((int)(d->fimg[0][j] + 0.5)); col.setGreen((int)(d->fimg[1][j] + 0.5)); col.setBlue((int)(d->fimg[2][j] + 0.5)); col.setAlpha(m_orgImage.getPixelColor(x, y).alpha()); ++j; m_destImage.setPixelColor(x, y, col); } } postProgress(100); // Free buffers. for (int c = 0; c < 3; ++c) { delete [] d->fimg[c]; } delete [] d->buffer[1]; delete [] d->buffer[2]; }