|
|
|
/* ============================================================
|
|
|
|
*
|
|
|
|
* This file is a part of digiKam project
|
|
|
|
* http://www.digikam.org
|
|
|
|
*
|
|
|
|
* Date : 2005-07-18
|
|
|
|
* Description : Distortion FX threaded image filter.
|
|
|
|
*
|
|
|
|
* Copyright (C) 2005-2007 by Gilles Caulier <caulier dot gilles at gmail dot com>
|
|
|
|
* Copyright (C) 2006-2007 by Marcel Wiesweg <marcel dot wiesweg at gmx dot de>
|
|
|
|
*
|
|
|
|
* Original Distortion algorithms copyrighted 2004-2005 by
|
|
|
|
* Pieter Z. Voloshyn <pieter dot voloshyn at gmail dot 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, 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.
|
|
|
|
*
|
|
|
|
* ============================================================ */
|
|
|
|
|
|
|
|
// Represents 1
|
|
|
|
#define ANGLE_RATIO 0.017453292519943295769236907685
|
|
|
|
|
|
|
|
// C++ includes.
|
|
|
|
|
|
|
|
#include <cmath>
|
|
|
|
#include <cstdlib>
|
|
|
|
|
|
|
|
// TQt includes.
|
|
|
|
|
|
|
|
#include <tqdatetime.h>
|
|
|
|
|
|
|
|
// Local includes.
|
|
|
|
|
|
|
|
#include "dimg.h"
|
|
|
|
#include "dimgimagefilters.h"
|
|
|
|
#include "distortionfx.h"
|
|
|
|
|
|
|
|
namespace DigikamDistortionFXImagesPlugin
|
|
|
|
{
|
|
|
|
|
|
|
|
DistortionFX::DistortionFX(Digikam::DImg *orgImage, TQObject *parent, int effectType,
|
|
|
|
int level, int iteration, bool antialiasing)
|
|
|
|
: Digikam::DImgThreadedFilter(orgImage, parent, "DistortionFX")
|
|
|
|
{
|
|
|
|
m_effectType = effectType;
|
|
|
|
m_level = level;
|
|
|
|
m_iteration = iteration;
|
|
|
|
m_antiAlias = antialiasing;
|
|
|
|
|
|
|
|
initFilter();
|
|
|
|
}
|
|
|
|
|
|
|
|
void DistortionFX::filterImage(void)
|
|
|
|
{
|
|
|
|
int w = m_orgImage.width();
|
|
|
|
int h = m_orgImage.height();
|
|
|
|
int l = m_level;
|
|
|
|
int f = m_iteration;
|
|
|
|
|
|
|
|
switch (m_effectType)
|
|
|
|
{
|
|
|
|
case FishEye:
|
|
|
|
fisheye(&m_orgImage, &m_destImage, (double)(l/5.0), m_antiAlias);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case Twirl:
|
|
|
|
twirl(&m_orgImage, &m_destImage, l, m_antiAlias);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case CilindricalHor:
|
|
|
|
cilindrical(&m_orgImage, &m_destImage, (double)l, true, false, m_antiAlias);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case CilindricalVert:
|
|
|
|
cilindrical(&m_orgImage, &m_destImage, (double)l, false, true, m_antiAlias);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case CilindricalHV:
|
|
|
|
cilindrical(&m_orgImage, &m_destImage, (double)l, true, true, m_antiAlias);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case Caricature:
|
|
|
|
fisheye(&m_orgImage, &m_destImage, (double)(-l/5.0), m_antiAlias);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case MultipleCorners:
|
|
|
|
multipleCorners(&m_orgImage, &m_destImage, l, m_antiAlias);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case WavesHorizontal:
|
|
|
|
waves(&m_orgImage, &m_destImage, l, f, true, true);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case WavesVertical:
|
|
|
|
waves(&m_orgImage, &m_destImage, l, f, true, false);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case BlockWaves1:
|
|
|
|
blockWaves(&m_orgImage, &m_destImage, l, f, false);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case BlockWaves2:
|
|
|
|
blockWaves(&m_orgImage, &m_destImage, l, f, true);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case CircularWaves1:
|
|
|
|
circularWaves(&m_orgImage, &m_destImage, w/2, h/2, (double)l, (double)f, 0.0, false, m_antiAlias);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case CircularWaves2:
|
|
|
|
circularWaves(&m_orgImage, &m_destImage, w/2, h/2, (double)l, (double)f, 25.0, true, m_antiAlias);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case PolarCoordinates:
|
|
|
|
polarCoordinates(&m_orgImage, &m_destImage, true, m_antiAlias);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case UnpolarCoordinates:
|
|
|
|
polarCoordinates(&m_orgImage, &m_destImage, false, m_antiAlias);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case Tile:
|
|
|
|
tile(&m_orgImage, &m_destImage, 200-f, 200-f, l);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
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 DistortionFX::setPixelFromOther(int Width, int Height, bool sixteenBit, int bytesDepth,
|
|
|
|
uchar *data, uchar *pResBits,
|
|
|
|
int w, int h, double nw, double nh, bool AntiAlias)
|
|
|
|
{
|
|
|
|
Digikam::DColor color;
|
|
|
|
int offset, offsetOther;
|
|
|
|
|
|
|
|
offset = getOffset(Width, w, h, bytesDepth);
|
|
|
|
|
|
|
|
if (AntiAlias)
|
|
|
|
{
|
|
|
|
uchar *ptr = pResBits + offset;
|
|
|
|
if (sixteenBit)
|
|
|
|
{
|
|
|
|
unsigned short *ptr16 = (unsigned short *)ptr;
|
|
|
|
Digikam::DImgImageFilters().pixelAntiAliasing16((unsigned short *)data, Width, Height, nw, nh,
|
|
|
|
ptr16+3, ptr16+2, ptr16+1, ptr16);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
Digikam::DImgImageFilters().pixelAntiAliasing(data, Width, Height, nw, nh,
|
|
|
|
ptr+3, ptr+2, ptr+1, ptr);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// we get the position adjusted
|
|
|
|
offsetOther = getOffsetAdjusted(Width, Height, (int)nw, (int)nh, bytesDepth);
|
|
|
|
// read color
|
|
|
|
color.setColor(data + offsetOther, sixteenBit);
|
|
|
|
// write color to destination
|
|
|
|
color.setPixel(pResBits + offset);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Function to apply the fisheye effect backported from ImageProcessing version 2
|
|
|
|
*
|
|
|
|
* data => The image data in RGBA mode.
|
|
|
|
* Width => Width of image.
|
|
|
|
* Height => Height of image.
|
|
|
|
* Coeff => Distortion effect coeff. Positive value render 'Fish Eyes' effect,
|
|
|
|
* and negative values render 'Caricature' effect.
|
|
|
|
* Antialias => Smart bluring result.
|
|
|
|
*
|
|
|
|
* Theory => This is a great effect if you take employee photos
|
|
|
|
* Its pure trigonometry. I think if you study hard the code you
|
|
|
|
* understand very well.
|
|
|
|
*/
|
|
|
|
void DistortionFX::fisheye(Digikam::DImg *orgImage, Digikam::DImg *destImage, double Coeff, bool AntiAlias)
|
|
|
|
{
|
|
|
|
if (Coeff == 0.0) return;
|
|
|
|
|
|
|
|
int Width = orgImage->width();
|
|
|
|
int Height = orgImage->height();
|
|
|
|
uchar* data = orgImage->bits();
|
|
|
|
bool sixteenBit = orgImage->sixteenBit();
|
|
|
|
int bytesDepth = orgImage->bytesDepth();
|
|
|
|
uchar* pResBits = destImage->bits();
|
|
|
|
|
|
|
|
int h, w;
|
|
|
|
double nh, nw, th, tw;
|
|
|
|
|
|
|
|
int progress;
|
|
|
|
int nHalfW = Width / 2, nHalfH = Height / 2;
|
|
|
|
|
|
|
|
Digikam::DColor color;
|
|
|
|
int offset;
|
|
|
|
|
|
|
|
double lfXScale = 1.0, lfYScale = 1.0;
|
|
|
|
double lfRadius, lfRadMax, lfAngle, lfCoeff, lfCoeffStep = Coeff / 1000.0;
|
|
|
|
|
|
|
|
if (Width > Height)
|
|
|
|
lfYScale = (double)Width / (double)Height;
|
|
|
|
else if (Height > Width)
|
|
|
|
lfXScale = (double)Height / (double)Width;
|
|
|
|
|
|
|
|
lfRadMax = (double)TQMAX(Height, Width) / 2.0;
|
|
|
|
lfCoeff = lfRadMax / log (fabs (lfCoeffStep) * lfRadMax + 1.0);
|
|
|
|
|
|
|
|
// main loop
|
|
|
|
|
|
|
|
for (h = 0; !m_cancel && (h < Height); h++)
|
|
|
|
{
|
|
|
|
th = lfYScale * (double)(h - nHalfH);
|
|
|
|
|
|
|
|
for (w = 0; !m_cancel && (w < Width); w++)
|
|
|
|
{
|
|
|
|
tw = lfXScale * (double)(w - nHalfW);
|
|
|
|
|
|
|
|
// we find the distance from the center
|
|
|
|
lfRadius = sqrt (th * th + tw * tw);
|
|
|
|
|
|
|
|
if (lfRadius < lfRadMax)
|
|
|
|
{
|
|
|
|
lfAngle = atan2 (th, tw);
|
|
|
|
|
|
|
|
if (Coeff > 0.0)
|
|
|
|
lfRadius = (exp (lfRadius / lfCoeff) - 1.0) / lfCoeffStep;
|
|
|
|
else
|
|
|
|
lfRadius = lfCoeff * log (1.0 + (-1.0 * lfCoeffStep) * lfRadius);
|
|
|
|
|
|
|
|
nw = (double)nHalfW + (lfRadius / lfXScale) * cos (lfAngle);
|
|
|
|
nh = (double)nHalfH + (lfRadius / lfYScale) * sin (lfAngle);
|
|
|
|
|
|
|
|
setPixelFromOther(Width, Height, sixteenBit, bytesDepth, data, pResBits, w, h, nw, nh, AntiAlias);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// copy pixel
|
|
|
|
offset = getOffset(Width, w, h, bytesDepth);
|
|
|
|
color.setColor(data + offset, sixteenBit);
|
|
|
|
color.setPixel(pResBits + offset);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Update the progress bar in dialog.
|
|
|
|
progress = (int) (((double)(h) * 100.0) / Height);
|
|
|
|
|
|
|
|
if (progress%5 == 0)
|
|
|
|
postProgress(progress);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Function to apply the twirl effect backported from ImageProcessing version 2
|
|
|
|
*
|
|
|
|
* data => The image data in RGBA mode.
|
|
|
|
* Width => Width of image.
|
|
|
|
* Height => Height of image.
|
|
|
|
* Twirl => Distance value.
|
|
|
|
* Antialias => Smart bluring result.
|
|
|
|
*
|
|
|
|
* Theory => Take spiral studies, you will understand better, I'm studying
|
|
|
|
* hard on this effect, because it is not too fast.
|
|
|
|
*/
|
|
|
|
void DistortionFX::twirl(Digikam::DImg *orgImage, Digikam::DImg *destImage, int Twirl, bool AntiAlias)
|
|
|
|
{
|
|
|
|
// if twirl value is zero, we do nothing
|
|
|
|
|
|
|
|
if (Twirl == 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
int Width = orgImage->width();
|
|
|
|
int Height = orgImage->height();
|
|
|
|
uchar* data = orgImage->bits();
|
|
|
|
bool sixteenBit = orgImage->sixteenBit();
|
|
|
|
int bytesDepth = orgImage->bytesDepth();
|
|
|
|
uchar* pResBits = destImage->bits();
|
|
|
|
|
|
|
|
int h, w;
|
|
|
|
double tw, th, nh, nw;
|
|
|
|
|
|
|
|
Digikam::DColor color;
|
|
|
|
int offset;
|
|
|
|
|
|
|
|
int progress;
|
|
|
|
int nHalfW = Width / 2, nHalfH = Height / 2;
|
|
|
|
|
|
|
|
double lfXScale = 1.0, lfYScale = 1.0;
|
|
|
|
double lfAngle, lfNewAngle, lfAngleStep, lfAngleSum, lfCurrentRadius, lfRadMax;
|
|
|
|
|
|
|
|
if (Width > Height)
|
|
|
|
lfYScale = (double)Width / (double)Height;
|
|
|
|
else if (Height > Width)
|
|
|
|
lfXScale = (double)Height / (double)Width;
|
|
|
|
|
|
|
|
// the angle step is twirl divided by 10000
|
|
|
|
lfAngleStep = Twirl / 10000.0;
|
|
|
|
// now, we get the minimum radius
|
|
|
|
lfRadMax = (double)TQMAX(Width, Height) / 2.0;
|
|
|
|
|
|
|
|
// main loop
|
|
|
|
|
|
|
|
for (h = 0; !m_cancel && (h < Height); h++)
|
|
|
|
{
|
|
|
|
th = lfYScale * (double)(h - nHalfH);
|
|
|
|
|
|
|
|
for (w = 0; !m_cancel && (w < Width); w++)
|
|
|
|
{
|
|
|
|
tw = lfXScale * (double)(w - nHalfW);
|
|
|
|
|
|
|
|
// now, we get the distance
|
|
|
|
lfCurrentRadius = sqrt (th * th + tw * tw);
|
|
|
|
|
|
|
|
// if distance is less than maximum radius...
|
|
|
|
if (lfCurrentRadius < lfRadMax)
|
|
|
|
{
|
|
|
|
// we find the angle from the center
|
|
|
|
lfAngle = atan2 (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 + cos (lfNewAngle) * (lfCurrentRadius / lfXScale);
|
|
|
|
nh = (double)nHalfH + sin (lfNewAngle) * (lfCurrentRadius / lfYScale);
|
|
|
|
|
|
|
|
setPixelFromOther(Width, Height, sixteenBit, bytesDepth, data, pResBits, w, h, nw, nh, AntiAlias);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// copy pixel
|
|
|
|
offset = getOffset(Width, w, h, bytesDepth);
|
|
|
|
color.setColor(data + offset, sixteenBit);
|
|
|
|
color.setPixel(pResBits + offset);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Update the progress bar in dialog.
|
|
|
|
progress = (int) (((double)h * 100.0) / Height);
|
|
|
|
|
|
|
|
if (progress%5 == 0)
|
|
|
|
postProgress(progress);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Function to apply the Cilindrical effect backported from ImageProcessing version 2
|
|
|
|
*
|
|
|
|
* data => The image data in RGBA mode.
|
|
|
|
* Width => Width of image.
|
|
|
|
* Height => Height of image.
|
|
|
|
* Coeff => Cilindrical value.
|
|
|
|
*Qt::Horizontal => Apply horizontally.
|
|
|
|
*Qt::Vertical => Apply vertically.
|
|
|
|
* Antialias => Smart bluring result.
|
|
|
|
*
|
|
|
|
* Theory => This is a great effect, similar to Spherize (Photoshop).
|
|
|
|
* If you understand FishEye, you will understand Cilindrical
|
|
|
|
* FishEye apply a logarithm function using a sphere radius,
|
|
|
|
* Spherize use the same function but in a rectangular
|
|
|
|
* environment.
|
|
|
|
*/
|
|
|
|
void DistortionFX::cilindrical(Digikam::DImg *orgImage, Digikam::DImg *destImage, double Coeff,
|
|
|
|
boolQt::Horizontal, boolQt::Vertical, bool AntiAlias)
|
|
|
|
|
|
|
|
{
|
|
|
|
if ((Coeff == 0.0) || (! Qt::Horizontal ||Qt::Vertical)))
|
|
|
|
return;
|
|
|
|
|
|
|
|
int Width = orgImage->width();
|
|
|
|
int Height = orgImage->height();
|
|
|
|
uchar* data = orgImage->bits();
|
|
|
|
bool sixteenBit = orgImage->sixteenBit();
|
|
|
|
int bytesDepth = orgImage->bytesDepth();
|
|
|
|
uchar* pResBits = destImage->bits();
|
|
|
|
|
|
|
|
int progress;
|
|
|
|
|
|
|
|
int h, w;
|
|
|
|
double nh, nw;
|
|
|
|
|
|
|
|
int nHalfW = Width / 2, nHalfH = Height / 2;
|
|
|
|
double lfCoeffX = 1.0, lfCoeffY = 1.0, lfCoeffStep = Coeff / 1000.0;
|
|
|
|
|
|
|
|
if Qt::Horizontal)
|
|
|
|
lfCoeffX = (double)nHalfW / log (fabs (lfCoeffStep) * nHalfW + 1.0);
|
|
|
|
if Qt::Vertical)
|
|
|
|
lfCoeffY = (double)nHalfH / log (fabs (lfCoeffStep) * nHalfH + 1.0);
|
|
|
|
|
|
|
|
// initial copy
|
|
|
|
memcpy (pResBits, data, orgImage->numBytes());
|
|
|
|
|
|
|
|
// main loop
|
|
|
|
|
|
|
|
for (h = 0; !m_cancel && (h < Height); h++)
|
|
|
|
{
|
|
|
|
for (w = 0; !m_cancel && (w < Width); w++)
|
|
|
|
{
|
|
|
|
// we find the distance from the center
|
|
|
|
nh = fabs ((double)(h - nHalfH));
|
|
|
|
nw = fabs ((double)(w - nHalfW));
|
|
|
|
|
|
|
|
if Qt::Horizontal)
|
|
|
|
{
|
|
|
|
if (Coeff > 0.0)
|
|
|
|
nw = (exp (nw / lfCoeffX) - 1.0) / lfCoeffStep;
|
|
|
|
else
|
|
|
|
nw = lfCoeffX * log (1.0 + (-1.0 * lfCoeffStep) * nw);
|
|
|
|
}
|
|
|
|
|
|
|
|
if Qt::Vertical)
|
|
|
|
{
|
|
|
|
if (Coeff > 0.0)
|
|
|
|
nh = (exp (nh / lfCoeffY) - 1.0) / lfCoeffStep;
|
|
|
|
else
|
|
|
|
nh = lfCoeffY * log (1.0 + (-1.0 * lfCoeffStep) * nh);
|
|
|
|
}
|
|
|
|
|
|
|
|
nw = (double)nHalfW + ((w >= nHalfW) ? nw : -nw);
|
|
|
|
nh = (double)nHalfH + ((h >= nHalfH) ? nh : -nh);
|
|
|
|
|
|
|
|
setPixelFromOther(Width, Height, sixteenBit, bytesDepth, data, pResBits, w, h, nw, nh, AntiAlias);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Update the progress bar in dialog.
|
|
|
|
progress = (int) (((double)h * 100.0) / Height);
|
|
|
|
|
|
|
|
if (progress%5 == 0)
|
|
|
|
postProgress(progress);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Function to apply the Multiple Corners effect backported from ImageProcessing version 2
|
|
|
|
*
|
|
|
|
* data => The image data in RGBA mode.
|
|
|
|
* Width => Width of image.
|
|
|
|
* Height => Height of image.
|
|
|
|
* Factor => nb corners.
|
|
|
|
* Antialias => Smart bluring result.
|
|
|
|
*
|
|
|
|
* Theory => This is an amazing function, you've never seen this before.
|
|
|
|
* I was testing some trigonometric functions, and I saw that if
|
|
|
|
* I multiply the angle by 2, the result is an image like this
|
|
|
|
* If we multiply by 3, we can create the SixCorners effect.
|
|
|
|
*/
|
|
|
|
void DistortionFX::multipleCorners(Digikam::DImg *orgImage, Digikam::DImg *destImage, int Factor, bool AntiAlias)
|
|
|
|
{
|
|
|
|
if (Factor == 0) return;
|
|
|
|
|
|
|
|
int Width = orgImage->width();
|
|
|
|
int Height = orgImage->height();
|
|
|
|
uchar* data = orgImage->bits();
|
|
|
|
bool sixteenBit = orgImage->sixteenBit();
|
|
|
|
int bytesDepth = orgImage->bytesDepth();
|
|
|
|
uchar* pResBits = destImage->bits();
|
|
|
|
|
|
|
|
int h, w;
|
|
|
|
double nh, nw;
|
|
|
|
int progress;
|
|
|
|
|
|
|
|
int nHalfW = Width / 2, nHalfH = Height / 2;
|
|
|
|
double lfAngle, lfNewRadius, lfCurrentRadius, lfRadMax;
|
|
|
|
|
|
|
|
lfRadMax = sqrt (Height * Height + Width * Width) / 2.0;
|
|
|
|
|
|
|
|
// main loop
|
|
|
|
|
|
|
|
for (h = 0; !m_cancel && (h < Height); h++)
|
|
|
|
{
|
|
|
|
for (w = 0; !m_cancel && (w < Width); w++)
|
|
|
|
{
|
|
|
|
// we find the distance from the center
|
|
|
|
nh = nHalfH - h;
|
|
|
|
nw = nHalfW - w;
|
|
|
|
|
|
|
|
// now, we get the distance
|
|
|
|
lfCurrentRadius = sqrt (nh * nh + nw * nw);
|
|
|
|
// we find the angle from the center
|
|
|
|
lfAngle = atan2 (nh, nw) * (double)Factor;
|
|
|
|
|
|
|
|
// ok, we sum angle with accumuled to find a new angle
|
|
|
|
lfNewRadius = lfCurrentRadius * lfCurrentRadius / lfRadMax;
|
|
|
|
|
|
|
|
// now we find the exact position's x and y
|
|
|
|
nw = (double)nHalfW - (cos (lfAngle) * lfNewRadius);
|
|
|
|
nh = (double)nHalfH - (sin (lfAngle) * lfNewRadius);
|
|
|
|
|
|
|
|
setPixelFromOther(Width, Height, sixteenBit, bytesDepth, data, pResBits, w, h, nw, nh, AntiAlias);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Update the progress bar in dialog.
|
|
|
|
progress = (int) (((double)h * 100.0) / Height);
|
|
|
|
|
|
|
|
if (progress%5 == 0)
|
|
|
|
postProgress(progress);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Function to apply the Polar Coordinates effect backported from ImageProcessing version 2
|
|
|
|
*
|
|
|
|
* data => The image data in RGBA mode.
|
|
|
|
* Width => Width of image.
|
|
|
|
* Height => Height of image.
|
|
|
|
* Type => if true Polar Coordinate to Polar else inverse.
|
|
|
|
* Antialias => Smart bluring result.
|
|
|
|
*
|
|
|
|
* Theory => Similar to PolarCoordinates from Photoshop. We apply the polar
|
|
|
|
* transformation in a proportional (Height and Width) radius.
|
|
|
|
*/
|
|
|
|
void DistortionFX::polarCoordinates(Digikam::DImg *orgImage, Digikam::DImg *destImage, bool Type, bool AntiAlias)
|
|
|
|
{
|
|
|
|
int Width = orgImage->width();
|
|
|
|
int Height = orgImage->height();
|
|
|
|
uchar* data = orgImage->bits();
|
|
|
|
bool sixteenBit = orgImage->sixteenBit();
|
|
|
|
int bytesDepth = orgImage->bytesDepth();
|
|
|
|
uchar* pResBits = destImage->bits();
|
|
|
|
|
|
|
|
int h, w;
|
|
|
|
double nh, nw, th, tw;
|
|
|
|
int progress;
|
|
|
|
|
|
|
|
int nHalfW = Width / 2, nHalfH = Height / 2;
|
|
|
|
double lfXScale = 1.0, lfYScale = 1.0;
|
|
|
|
double lfAngle, lfRadius, lfRadMax;
|
|
|
|
|
|
|
|
if (Width > Height)
|
|
|
|
lfYScale = (double)Width / (double)Height;
|
|
|
|
else if (Height > Width)
|
|
|
|
lfXScale = (double)Height / (double)Width;
|
|
|
|
|
|
|
|
lfRadMax = (double)TQMAX(Height, Width) / 2.0;
|
|
|
|
|
|
|
|
// main loop
|
|
|
|
|
|
|
|
for (h = 0; !m_cancel && (h < Height); h++)
|
|
|
|
{
|
|
|
|
th = lfYScale * (double)(h - nHalfH);
|
|
|
|
|
|
|
|
for (w = 0; !m_cancel && (w < Width); w++)
|
|
|
|
{
|
|
|
|
tw = lfXScale * (double)(w - nHalfW);
|
|
|
|
|
|
|
|
if (Type)
|
|
|
|
{
|
|
|
|
// now, we get the distance
|
|
|
|
lfRadius = sqrt (th * th + tw * tw);
|
|
|
|
// we find the angle from the center
|
|
|
|
lfAngle = atan2 (tw, th);
|
|
|
|
|
|
|
|
// now we find the exact position's x and y
|
|
|
|
nh = lfRadius * (double) Height / lfRadMax;
|
|
|
|
nw = lfAngle * (double) Width / (2 * M_PI);
|
|
|
|
|
|
|
|
nw = (double)nHalfW + nw;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
lfRadius = (double)(h) * lfRadMax / (double)Height;
|
|
|
|
lfAngle = (double)(w) * (2 * M_PI) / (double) Width;
|
|
|
|
|
|
|
|
nw = (double)nHalfW - (lfRadius / lfXScale) * sin (lfAngle);
|
|
|
|
nh = (double)nHalfH - (lfRadius / lfYScale) * cos (lfAngle);
|
|
|
|
}
|
|
|
|
|
|
|
|
setPixelFromOther(Width, Height, sixteenBit, bytesDepth, data, pResBits, w, h, nw, nh, AntiAlias);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Update the progress bar in dialog.
|
|
|
|
progress = (int) (((double)h * 100.0) / Height);
|
|
|
|
|
|
|
|
if (progress%5 == 0)
|
|
|
|
postProgress(progress);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Function to apply the circular waves effect backported from ImageProcessing version 2
|
|
|
|
*
|
|
|
|
* data => The image data in RGBA mode.
|
|
|
|
* Width => Width of image.
|
|
|
|
* Height => Height of image.
|
|
|
|
* X, Y => Position of circle center on the image.
|
|
|
|
* Amplitude => Sinoidal maximum height
|
|
|
|
* Frequency => Frequency value.
|
|
|
|
* Phase => Phase value.
|
|
|
|
* WavesType => If true the amplitude is proportional to radius.
|
|
|
|
* Antialias => Smart bluring result.
|
|
|
|
*
|
|
|
|
* Theory => Similar to Waves effect, but here I apply a senoidal function
|
|
|
|
* with the angle point.
|
|
|
|
*/
|
|
|
|
void DistortionFX::circularWaves(Digikam::DImg *orgImage, Digikam::DImg *destImage, int X, int Y, double Amplitude,
|
|
|
|
double Frequency, double Phase, bool WavesType, bool AntiAlias)
|
|
|
|
{
|
|
|
|
if (Amplitude < 0.0) Amplitude = 0.0;
|
|
|
|
if (Frequency < 0.0) Frequency = 0.0;
|
|
|
|
|
|
|
|
int Width = orgImage->width();
|
|
|
|
int Height = orgImage->height();
|
|
|
|
uchar* data = orgImage->bits();
|
|
|
|
bool sixteenBit = orgImage->sixteenBit();
|
|
|
|
int bytesDepth = orgImage->bytesDepth();
|
|
|
|
uchar* pResBits = destImage->bits();
|
|
|
|
|
|
|
|
int h, w;
|
|
|
|
double nh, nw;
|
|
|
|
int progress;
|
|
|
|
|
|
|
|
double lfRadius, lfRadMax, lfNewAmp = Amplitude;
|
|
|
|
double lfFreqAngle = Frequency * ANGLE_RATIO;
|
|
|
|
|
|
|
|
Phase *= ANGLE_RATIO;
|
|
|
|
|
|
|
|
lfRadMax = sqrt (Height * Height + Width * Width);
|
|
|
|
|
|
|
|
for (h = 0; !m_cancel && (h < Height); h++)
|
|
|
|
{
|
|
|
|
for (w = 0; !m_cancel && (w < Width); w++)
|
|
|
|
{
|
|
|
|
nw = X - w;
|
|
|
|
nh = Y - h;
|
|
|
|
|
|
|
|
lfRadius = sqrt (nw * nw + nh * nh);
|
|
|
|
|
|
|
|
if (WavesType)
|
|
|
|
lfNewAmp = Amplitude * lfRadius / lfRadMax;
|
|
|
|
|
|
|
|
nw = (double)w + lfNewAmp * sin(lfFreqAngle * lfRadius + Phase);
|
|
|
|
nh = (double)h + lfNewAmp * cos(lfFreqAngle * lfRadius + Phase);
|
|
|
|
|
|
|
|
setPixelFromOther(Width, Height, sixteenBit, bytesDepth, data, pResBits, w, h, nw, nh, AntiAlias);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Update the progress bar in dialog.
|
|
|
|
progress = (int) (((double)h * 100.0) / Height);
|
|
|
|
|
|
|
|
if (progress%5 == 0)
|
|
|
|
postProgress(progress);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Function to apply the waves effect
|
|
|
|
*
|
|
|
|
* data => The image data in RGBA mode.
|
|
|
|
* Width => Width of image.
|
|
|
|
* Height => Height of image.
|
|
|
|
* Amplitude => Sinoidal maximum height.
|
|
|
|
* Frequency => Frequency value.
|
|
|
|
* FillSides => Like a boolean variable.
|
|
|
|
* Direction =>Qt::Vertical or horizontal flag.
|
|
|
|
*
|
|
|
|
* Theory => This is an amazing effect, very funny, and very simple to
|
|
|
|
* understand. You just need understand how sin and cos works.
|
|
|
|
*/
|
|
|
|
void DistortionFX::waves(Digikam::DImg *orgImage, Digikam::DImg *destImage,
|
|
|
|
int Amplitude, int Frequency,
|
|
|
|
bool FillSides, bool Direction)
|
|
|
|
{
|
|
|
|
if (Amplitude < 0) Amplitude = 0;
|
|
|
|
if (Frequency < 0) Frequency = 0;
|
|
|
|
|
|
|
|
int Width = orgImage->width();
|
|
|
|
int Height = orgImage->height();
|
|
|
|
|
|
|
|
int progress;
|
|
|
|
int h, w;
|
|
|
|
|
|
|
|
if (Direction) //Qt::Horizontal
|
|
|
|
{
|
|
|
|
int tx;
|
|
|
|
|
|
|
|
for (h = 0; !m_cancel && (h < Height); h++)
|
|
|
|
{
|
|
|
|
tx = lround(Amplitude * sin ((Frequency * 2) * h * (M_PI / 180)));
|
|
|
|
destImage->bitBltImage(orgImage, 0, h, Width, 1, tx, h);
|
|
|
|
|
|
|
|
if (FillSides)
|
|
|
|
{
|
|
|
|
destImage->bitBltImage(orgImage, Width - tx, h, tx, 1, 0, h);
|
|
|
|
destImage->bitBltImage(orgImage, 0, h, Width - (Width - 2 * Amplitude + tx), 1, Width + tx, h);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Update the progress bar in dialog.
|
|
|
|
progress = (int) (((double)h * 100.0) / Height);
|
|
|
|
|
|
|
|
if (progress%5 == 0)
|
|
|
|
postProgress(progress);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
int ty;
|
|
|
|
|
|
|
|
for (w = 0; !m_cancel && (w < Width); w++)
|
|
|
|
{
|
|
|
|
ty = lround(Amplitude * sin ((Frequency * 2) * w * (M_PI / 180)));
|
|
|
|
destImage->bitBltImage(orgImage, w, 0, 1, Height, w, ty);
|
|
|
|
|
|
|
|
if (FillSides)
|
|
|
|
{
|
|
|
|
destImage->bitBltImage(orgImage, w, Height - ty, 1, ty, w, 0);
|
|
|
|
destImage->bitBltImage(orgImage, w, 0, 1, Height - (Height - 2 * Amplitude + ty), w, Height + ty);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Update the progress bar in dialog.
|
|
|
|
progress = (int) (((double)w * 100.0) / Width);
|
|
|
|
|
|
|
|
if (progress%5 == 0)
|
|
|
|
postProgress(progress);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Function to apply the block waves effect
|
|
|
|
*
|
|
|
|
* data => The image data in RGBA mode.
|
|
|
|
* Width => Width of image.
|
|
|
|
* Height => Height of image.
|
|
|
|
* Amplitude => Sinoidal maximum height
|
|
|
|
* Frequency => Frequency value
|
|
|
|
* Mode => The mode to be applied.
|
|
|
|
*
|
|
|
|
* Theory => This is an amazing effect, very funny when amplitude and
|
|
|
|
* frequency are small values.
|
|
|
|
*/
|
|
|
|
void DistortionFX::blockWaves(Digikam::DImg *orgImage, Digikam::DImg *destImage,
|
|
|
|
int Amplitude, int Frequency, bool Mode)
|
|
|
|
{
|
|
|
|
if (Amplitude < 0) Amplitude = 0;
|
|
|
|
if (Frequency < 0) Frequency = 0;
|
|
|
|
|
|
|
|
int Width = orgImage->width();
|
|
|
|
int Height = orgImage->height();
|
|
|
|
uchar* data = orgImage->bits();
|
|
|
|
bool sixteenBit = orgImage->sixteenBit();
|
|
|
|
int bytesDepth = orgImage->bytesDepth();
|
|
|
|
uchar* pResBits = destImage->bits();
|
|
|
|
|
|
|
|
int nw, nh, progress;
|
|
|
|
double Radius;
|
|
|
|
|
|
|
|
Digikam::DColor color;
|
|
|
|
int offset, offsetOther;
|
|
|
|
|
|
|
|
int nHalfW = Width / 2, nHalfH = Height / 2;
|
|
|
|
|
|
|
|
for (int w = 0; !m_cancel && (w < Width); w++)
|
|
|
|
{
|
|
|
|
for (int h = 0; !m_cancel && (h < Height); h++)
|
|
|
|
{
|
|
|
|
nw = nHalfW - w;
|
|
|
|
nh = nHalfH - h;
|
|
|
|
|
|
|
|
Radius = sqrt (nw * nw + nh * nh);
|
|
|
|
|
|
|
|
if (Mode)
|
|
|
|
{
|
|
|
|
nw = (int)(w + Amplitude * sin (Frequency * nw * (M_PI / 180)));
|
|
|
|
nh = (int)(h + Amplitude * cos (Frequency * nh * (M_PI / 180)));
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
nw = (int)(w + Amplitude * sin (Frequency * w * (M_PI / 180)));
|
|
|
|
nh = (int)(h + Amplitude * cos (Frequency * h * (M_PI / 180)));
|
|
|
|
}
|
|
|
|
|
|
|
|
offset = getOffset(Width, 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);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Update the progress bar in dialog.
|
|
|
|
progress = (int) (((double)w * 100.0) / Width);
|
|
|
|
|
|
|
|
if (progress%5 == 0)
|
|
|
|
postProgress(progress);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Function to apply the tile effect
|
|
|
|
*
|
|
|
|
* data => The image data in RGBA mode.
|
|
|
|
* Width => Width of image.
|
|
|
|
* Height => Height of image.
|
|
|
|
* WSize => Tile Width
|
|
|
|
* HSize => Tile Height
|
|
|
|
* Random => Maximum random value
|
|
|
|
*
|
|
|
|
* Theory => Similar to Tile effect from Photoshop and very easy to
|
|
|
|
* understand. We get a rectangular area using WSize and HSize and
|
|
|
|
* replace in a position with a random distance from the original
|
|
|
|
* position.
|
|
|
|
*/
|
|
|
|
void DistortionFX::tile(Digikam::DImg *orgImage, Digikam::DImg *destImage,
|
|
|
|
int WSize, int HSize, int Random)
|
|
|
|
{
|
|
|
|
if (WSize < 1) WSize = 1;
|
|
|
|
if (HSize < 1) HSize = 1;
|
|
|
|
if (Random < 1) Random = 1;
|
|
|
|
|
|
|
|
int Width = orgImage->width();
|
|
|
|
int Height = orgImage->height();
|
|
|
|
|
|
|
|
TQDateTime dt = TQDateTime::tqcurrentDateTime();
|
|
|
|
TQDateTime Y2000( TQDate(2000, 1, 1), TQTime(0, 0, 0) );
|
|
|
|
uint seed = dt.secsTo(Y2000);
|
|
|
|
|
|
|
|
int tx, ty, h, w, progress;
|
|
|
|
|
|
|
|
for (h = 0; !m_cancel && (h < Height); h += HSize)
|
|
|
|
{
|
|
|
|
for (w = 0; !m_cancel && (w < Width); w += WSize)
|
|
|
|
{
|
|
|
|
tx = (int)(rand_r(&seed) % Random) - (Random / 2);
|
|
|
|
ty = (int)(rand_r(&seed) % Random) - (Random / 2);
|
|
|
|
destImage->bitBltImage(orgImage, w, h, WSize, HSize, w + tx, h + ty);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Update the progress bar in dialog.
|
|
|
|
progress = (int)(((double)h * 100.0) / Height);
|
|
|
|
|
|
|
|
if (progress%5 == 0)
|
|
|
|
postProgress(progress);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// UNUSED
|
|
|
|
/* Function to return the maximum radius with a determined angle
|
|
|
|
*
|
|
|
|
* Height => Height of the image
|
|
|
|
* Width => Width of the image
|
|
|
|
* Angle => Angle to analize the maximum radius
|
|
|
|
*
|
|
|
|
* Theory => This function calcule the maximum radius to that angle
|
|
|
|
* so, we can build an oval circunference
|
|
|
|
*/
|
|
|
|
/*
|
|
|
|
double DistortionFX::maximumRadius(int Height, int Width, double Angle)
|
|
|
|
{
|
|
|
|
double MaxRad, MinRad;
|
|
|
|
double Radius, DegAngle = fabs (Angle * 57.295); // Rads -> Degrees
|
|
|
|
|
|
|
|
MinRad = TQMIN (Height, Width) / 2.0; // Gets the minor radius
|
|
|
|
MaxRad = TQMAX (Height, Width) / 2.0; // Gets the major radius
|
|
|
|
|
|
|
|
// Find the quadrant between -PI/2 and PI/2
|
|
|
|
if (DegAngle > 90.0)
|
|
|
|
Radius = proportionalValue (MinRad, MaxRad, (DegAngle * (255.0 / 90.0)));
|
|
|
|
else
|
|
|
|
Radius = proportionalValue (MaxRad, MinRad, ((DegAngle - 90.0) * (255.0 / 90.0)));
|
|
|
|
return (Radius);
|
|
|
|
}
|
|
|
|
*/
|
|
|
|
|
|
|
|
} // NameSpace DigikamDistortionFXImagesPlugin
|