You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
digikam/digikam/imageplugins/freerotation/freerotation.cpp

264 lines
9.0 KiB

/* ============================================================
*
* This file is a part of digiKam project
* http://www.digikam.org
*
* Date : 2005-07-18
* Description : Free rotation threaded image filter.
*
* Copyright (C) 2004-2007 by Gilles Caulier <caulier dot gilles 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.
*
* ============================================================ */
// Degrees to radian convertion coeff (PI/180). To optimize computation.
#define DEG2RAD 0.017453292519943
// C++ includes.
#include <cmath>
#include <cstdlib>
// Local includes.
#include "dimg.h"
#include "dimgimagefilters.h"
#include "freerotation.h"
namespace DigikamFreeRotationImagesPlugin
{
FreeRotation::FreeRotation(Digikam::DImg *orgImage, TQObject *parent, double angle, bool antialiasing,
int autoCrop, TQColor backgroundColor, int orgW, int orgH)
: Digikam::DImgThreadedFilter(orgImage, parent, "FreeRotation")
{
m_angle = angle;
m_orgW = orgW;
m_orgH = orgH;
m_antiAlias = antialiasing;
m_autoCrop = autoCrop;
m_backgroundColor = backgroundColor;
initFilter();
}
void FreeRotation::filterImage(void)
{
int progress;
int w, h, nw, nh, j, i = 0;
int nNewHeight, nNewWidth;
int nhdx, nhdy, nhsx, nhsy;
double lfSin, lfCos, lfx, lfy;
int nWidth = m_orgImage.width();
int nHeight = m_orgImage.height();
uchar *pBits = m_orgImage.bits();
unsigned short *pBits16 = (unsigned short*)m_orgImage.bits();
// first of all, we need to calcule the sin and cos of the given angle
lfSin = sin (m_angle * -DEG2RAD);
lfCos = cos (m_angle * -DEG2RAD);
// now, we have to calc the new size for the destination image
if ((lfSin * lfCos) < 0)
{
nNewWidth = ROUND (fabs (nWidth * lfCos - nHeight * lfSin));
nNewHeight = ROUND (fabs (nWidth * lfSin - nHeight * lfCos));
}
else
{
nNewWidth = ROUND (fabs (nWidth * lfCos + nHeight * lfSin));
nNewHeight = ROUND (fabs (nWidth * lfSin + nHeight * lfCos));
}
// getting the destination's center position
nhdx = nNewWidth / 2;
nhdy = nNewHeight / 2;
// getting the source's center position
nhsx = nWidth / 2;
nhsy = nHeight / 2;
// now, we have to alloc a new image
bool sixteenBit = m_orgImage.sixteenBit();
m_destImage = Digikam::DImg(nNewWidth, nNewHeight, sixteenBit, m_orgImage.hasAlpha());
m_destImage.fill( Digikam::DColor(m_backgroundColor.rgb(), sixteenBit) );
uchar *pResBits = m_destImage.bits();
unsigned short *pResBits16 = (unsigned short *)m_destImage.bits();
Digikam::DImgImageFilters filters;
// main loop
for (h = 0; !m_cancel && (h < nNewHeight); h++)
{
nh = h - nhdy;
for (w = 0; !m_cancel && (w < nNewWidth); w++)
{
nw = w - nhdx;
i = setPosition (nNewWidth, w, h);
lfx = (double)nw * lfCos - (double)nh * lfSin + nhsx;
lfy = (double)nw * lfSin + (double)nh * lfCos + nhsy;
if (isInside (nWidth, nHeight, (int)lfx, (int)lfy))
{
if (m_antiAlias)
{
if (!sixteenBit)
filters.pixelAntiAliasing(pBits, nWidth, nHeight, lfx, lfy,
&pResBits[i+3], &pResBits[i+2],
&pResBits[i+1], &pResBits[i]);
else
filters.pixelAntiAliasing16(pBits16, nWidth, nHeight, lfx, lfy,
&pResBits16[i+3], &pResBits16[i+2],
&pResBits16[i+1], &pResBits16[i]);
}
else
{
j = setPosition (nWidth, (int)lfx, (int)lfy);
for (int p = 0 ; p < 4 ; p++)
{
if (!sixteenBit)
pResBits[i] = pBits[j];
else
pResBits16[i] = pBits16[j];
i++;
j++;
}
}
}
}
// Update the progress bar in dialog.
progress = (int)(((double)h * 100.0) / nNewHeight);
if (progress%5 == 0)
postProgress( progress );
}
// Compute the rotated destination image size using original image dimensions.
int W, H;
double absAngle = fabs(m_angle);
if (absAngle < 90.0)
{
W = (int)(m_orgW * cos(absAngle * DEG2RAD) + m_orgH * sin(absAngle * DEG2RAD));
H = (int)(m_orgH * cos(absAngle * DEG2RAD) + m_orgW * sin(absAngle * DEG2RAD));
}
else
{
H = (int)(m_orgW * cos((absAngle-90.0) * DEG2RAD) + m_orgH * sin((absAngle-90.0) * DEG2RAD));
W = (int)(m_orgH * cos((absAngle-90.0) * DEG2RAD) + m_orgW * sin((absAngle-90.0) * DEG2RAD));
}
// Auto-cropping destination image without black holes around.
TQRect autoCrop;
switch(m_autoCrop)
{
case WidestArea:
{
// 'Widest Area' method (by Renchi Raju).
autoCrop.setX( (int)(nHeight * sin(absAngle * DEG2RAD)) );
autoCrop.setY( (int)(nWidth * sin(absAngle * DEG2RAD)) );
autoCrop.setWidth( (int)(nNewWidth - 2*nHeight * sin(absAngle * DEG2RAD)) );
autoCrop.setHeight( (int)(nNewHeight - 2*nWidth * sin(absAngle * DEG2RAD)) );
if (!autoCrop.isValid())
{
m_destImage = Digikam::DImg(m_orgImage.width(), m_orgImage.height(),
m_orgImage.sixteenBit(), m_orgImage.hasAlpha());
m_destImage.fill( Digikam::DColor(m_backgroundColor.rgb(), sixteenBit) );
m_newSize = TQSize();
}
else
{
m_destImage = m_destImage.copy(autoCrop);
m_newSize.setWidth( (int)(W - 2*m_orgH * sin(absAngle * DEG2RAD)) );
m_newSize.setHeight( (int)(H - 2*m_orgW * sin(absAngle * DEG2RAD)) );
}
break;
}
case LargestArea:
{
// 'Largest Area' method (by Gerhard Kulzer).
float gamma = atan((float)nHeight / (float)nWidth);
if (absAngle < 90.0)
{
autoCrop.setWidth( (int)((float)nHeight / cos(absAngle*DEG2RAD) /
( tan(gamma) + tan(absAngle*DEG2RAD) )) );
autoCrop.setHeight( (int)((float)autoCrop.width() * tan(gamma)) );
}
else
{
autoCrop.setHeight( (int)((float)nHeight / cos((absAngle-90.0)*DEG2RAD) /
( tan(gamma) + tan((absAngle-90.0)*DEG2RAD) )) );
autoCrop.setWidth( (int)((float)autoCrop.height() * tan(gamma)) );
}
autoCrop.moveCenter( TQPoint(nNewWidth/2, nNewHeight/2));
if (!autoCrop.isValid())
{
m_destImage = Digikam::DImg(m_orgImage.width(), m_orgImage.height(),
m_orgImage.sixteenBit(), m_orgImage.hasAlpha());
m_destImage.fill( Digikam::DColor(m_backgroundColor.rgb(), sixteenBit) );
m_newSize = TQSize();
}
else
{
m_destImage = m_destImage.copy(autoCrop);
gamma = atan((float)m_orgH / (float)m_orgW);
if (absAngle < 90.0)
{
m_newSize.setWidth( (int)((float)m_orgH / cos(absAngle*DEG2RAD) /
( tan(gamma) + tan(absAngle*DEG2RAD) )) );
m_newSize.setHeight( (int)((float)m_newSize.width() * tan(gamma)) );
}
else
{
m_newSize.setHeight( (int)((float)m_orgH / cos((absAngle-90.0)*DEG2RAD) /
( tan(gamma) + tan((absAngle-90.0)*DEG2RAD) )) );
m_newSize.setWidth( (int)((float)m_newSize.height() * tan(gamma)) );
}
}
break;
}
default: // No auto croping.
{
m_newSize.setWidth( W );
m_newSize.setHeight( H );
break;
}
}
}
} // NameSpace DigikamFreeRotationImagesPlugin