summaryrefslogtreecommitdiffstats
path: root/src/imageplugins/freerotation/freerotation.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/imageplugins/freerotation/freerotation.cpp')
-rw-r--r--src/imageplugins/freerotation/freerotation.cpp263
1 files changed, 263 insertions, 0 deletions
diff --git a/src/imageplugins/freerotation/freerotation.cpp b/src/imageplugins/freerotation/freerotation.cpp
new file mode 100644
index 00000000..3f8803d5
--- /dev/null
+++ b/src/imageplugins/freerotation/freerotation.cpp
@@ -0,0 +1,263 @@
+/* ============================================================
+ *
+ * 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