diff options
Diffstat (limited to 'src/imageplugins/lensdistortion/pixelaccess.cpp')
-rw-r--r-- | src/imageplugins/lensdistortion/pixelaccess.cpp | 314 |
1 files changed, 314 insertions, 0 deletions
diff --git a/src/imageplugins/lensdistortion/pixelaccess.cpp b/src/imageplugins/lensdistortion/pixelaccess.cpp new file mode 100644 index 00000000..a6041f94 --- /dev/null +++ b/src/imageplugins/lensdistortion/pixelaccess.cpp @@ -0,0 +1,314 @@ +/* ============================================================ + * + * This file is a part of digiKam project + * http://www.digikam.org + * + * Date : 2004-12-27 + * Description : acess pixels method for lens distortion algorithm. + * + * Copyright (C) 2004-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> + * + * 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. + * + * ============================================================ */ + +// C++ includes. + +#include <cstring> +#include <cmath> +#include <cstdlib> + +// Local includes. + +#include "ddebug.h" +#include "pixelaccess.h" + +namespace DigikamLensDistortionImagesPlugin +{ + +PixelAccess::PixelAccess(Digikam::DImg *srcImage) +{ + m_image = srcImage; + + m_width = PixelAccessWidth; + m_height = PixelAccessHeight; + + m_depth = m_image->bytesDepth(); + m_imageWidth = m_image->width(); + m_imageHeight = m_image->height(); + m_sixteenBit = m_image->sixteenBit(); + + for ( int i = 0 ; i < PixelAccessRegions ; i++ ) + { + m_buffer[i] = new Digikam::DImg(m_image->copy(0, 0, m_width, m_height)); + + m_tileMinX[i] = 1; + m_tileMaxX[i] = m_width - 2; + m_tileMinY[i] = 1; + m_tileMaxY[i] = m_height - 2; + } +} + +PixelAccess::~PixelAccess() +{ + for( int i = 0 ; i < PixelAccessRegions ; i++ ) + delete m_buffer[i]; +} + +uchar* PixelAccess::pixelAccessAddress(int i, int j) +{ + return m_buffer[0]->bits() + m_depth * (m_width * (j + 1 - m_tileMinY[0]) + (i + 1 - m_tileMinX[0])); +} + +// Swap region[n] with region[0]. +void PixelAccess::pixelAccessSelectRegion(int n) +{ + Digikam::DImg *temp; + int a, b, c, d; + int i; + + temp = m_buffer[n]; + a = m_tileMinX[n]; + b = m_tileMaxX[n]; + c = m_tileMinY[n]; + d = m_tileMaxY[n]; + + for( i = n ; i > 0 ; i--) + { + m_buffer[i] = m_buffer[i-1]; + m_tileMinX[i] = m_tileMinX[i-1]; + m_tileMaxX[i] = m_tileMaxX[i-1]; + m_tileMinY[i] = m_tileMinY[i-1]; + m_tileMaxY[i] = m_tileMaxY[i-1]; + } + + m_buffer[0] = temp; + m_tileMinX[0] = a; + m_tileMaxX[0] = b; + m_tileMinY[0] = c; + m_tileMaxY[0] = d; +} + +// Buffer[0] is cleared, should start at [i, j], fill rows that overlap image. +void PixelAccess::pixelAccessDoEdge(int i, int j) +{ + int lineStart, lineEnd; + int rowStart, rowEnd; + int lineWidth; + uchar* line; + + lineStart = i; + if (lineStart < 0) lineStart = 0; + lineEnd = i + m_width; + if (lineEnd > m_imageWidth) lineEnd = m_imageWidth; + lineWidth = lineEnd - lineStart; + + if( lineStart >= lineEnd ) + return; + + rowStart = j; + if (rowStart < 0) rowStart = 0; + rowEnd = j + m_height; + if (rowEnd > m_imageHeight) rowEnd = m_imageHeight; + + for( int y = rowStart ; y < rowEnd ; y++ ) + { + line = pixelAccessAddress(lineStart, y); + memcpy(line, m_image->scanLine(y) + lineStart * m_depth, lineWidth * m_depth); + } +} + +// Moves buffer[0] so that [x, y] is inside it. +void PixelAccess::pixelAccessReposition(int xInt, int yInt) +{ + int newStartX = xInt - PixelAccessXOffset; + int newStartY = yInt - PixelAccessYOffset; + + m_tileMinX[0] = newStartX + 1; + m_tileMaxX[0] = newStartX + m_width - 2; + m_tileMinY[0] = newStartY + 1; + m_tileMaxY[0] = newStartY + m_height - 2; + + + if ( (newStartX < 0) || ((newStartX + m_width) >= m_imageWidth) || + (newStartY < 0) || ((newStartY + m_height) >= m_imageHeight) ) + { + // some data is off edge of image + + m_buffer[0]->fill(Digikam::DColor(0,0,0,0, m_sixteenBit)); + + // This could probably be done by bitBltImage but I did not figure out how, + // so leave the working code here. And no, it is not this: + //m_buffer[0]->bitBltImage(m_image, newStartX, newStartY, m_width, m_height, 0, 0); + + if ( ((newStartX + m_width) < 0) || (newStartX >= m_imageWidth) || + ((newStartY + m_height) < 0) || (newStartY >= m_imageHeight) ) + { + // totally outside, just leave it. + } + else + { + pixelAccessDoEdge(newStartX, newStartY); + } + } + else + { + m_buffer[0]->bitBltImage(m_image, newStartX, newStartY, m_width, m_height, 0, 0); + } +} + +void PixelAccess::pixelAccessGetCubic(double srcX, double srcY, double brighten, uchar* dst) +{ + int xInt, yInt; + double dx, dy; + uchar *corner; + + xInt = (int)floor(srcX); + dx = srcX - xInt; + yInt = (int)floor(srcY); + dy = srcY - yInt; + + // We need 4x4 pixels, xInt-1 to xInt+2 horz, yInt-1 to yInt+2 vert + // they're probably in the last place we looked... + + if ((xInt >= m_tileMinX[0]) && (xInt < m_tileMaxX[0]) && + (yInt >= m_tileMinY[0]) && (yInt < m_tileMaxY[0]) ) + { + corner = pixelAccessAddress(xInt - 1, yInt - 1); + cubicInterpolate(corner, m_depth * m_width, dst, m_sixteenBit, dx, dy, brighten); + return; + } + + // Or maybe it was a while back... + + for ( int i = 1 ; i < PixelAccessRegions ; i++) + { + if ((xInt >= m_tileMinX[i]) && (xInt < m_tileMaxX[i]) && + (yInt >= m_tileMinY[i]) && (yInt < m_tileMaxY[i]) ) + { + // Check here first next time + + pixelAccessSelectRegion(i); + corner = pixelAccessAddress(xInt - 1, yInt - 1); + cubicInterpolate(corner, m_depth * m_width, dst, m_sixteenBit, dx, dy, brighten); + return; + } + } + + // Nope, recycle an old region. + + pixelAccessSelectRegion(PixelAccessRegions - 1); + pixelAccessReposition(xInt, yInt); + + corner = pixelAccessAddress(xInt - 1, yInt - 1); + cubicInterpolate(corner, m_depth * m_width, dst, m_sixteenBit, dx, dy, brighten); +} + +/* + * Catmull-Rom cubic interpolation + * + * equally spaced points p0, p1, p2, p3 + * interpolate 0 <= u < 1 between p1 and p2 + * + * (1 u u^2 u^3) ( 0.0 1.0 0.0 0.0 ) (p0) + * ( -0.5 0.0 0.5 0.0 ) (p1) + * ( 1.0 -2.5 2.0 -0.5 ) (p2) + * ( -0.5 1.5 -1.5 0.5 ) (p3) + * + */ +void PixelAccess::cubicInterpolate(uchar* src, int rowStride, uchar* dst, + bool sixteenBit, double dx, double dy, double brighten) +{ + float um1, u, up1, up2; + float vm1, v, vp1, vp2; + int c; + const int numberOfComponents = 4; + float verts[4 * numberOfComponents]; + + um1 = ((-0.5 * dx + 1.0) * dx - 0.5) * dx; + u = (1.5 * dx - 2.5) * dx * dx + 1.0; + up1 = ((-1.5 * dx + 2.0) * dx + 0.5) * dx; + up2 = (0.5 * dx - 0.5) * dx * dx; + + vm1 = ((-0.5 * dy + 1.0) * dy - 0.5) * dy; + v = (1.5 * dy - 2.5) * dy * dy + 1.0; + vp1 = ((-1.5 * dy + 2.0) * dy + 0.5) * dy; + vp2 = (0.5 * dy - 0.5) * dy * dy; + + if (sixteenBit) + { + unsigned short *src16 = (unsigned short *)src; + unsigned short *dst16 = (unsigned short *)dst; + + // for each component, read the values of 4 pixels into array + + for (c = 0 ; c < 4 * numberOfComponents ; c++) + { + verts[c] = vm1 * src16[c] + v * src16[c+rowStride] + vp1 * src16[c+rowStride*2] + vp2 * src16[c+rowStride*3]; + } + + // for each component, compute resulting value from array + + for (c = 0 ; c < numberOfComponents ; c++) + { + float result; + result = um1 * verts[c] + u * verts[c+numberOfComponents] + + up1 * verts[c+numberOfComponents*2] + up2 * verts[c+numberOfComponents*3]; + result *= brighten; + + if (result < 0.0) + { + dst16[c] = 0; + } + else if (result > 65535.0) + { + dst16[c] = 65535; + } + else + { + dst16[c] = (uint)result; + } + } + } + else + { + for (c = 0 ; c < 4 * numberOfComponents ; c++) + { + verts[c] = vm1 * src[c] + v * src[c+rowStride] + vp1 * src[c+rowStride*2] + vp2 * src[c+rowStride*3]; + } + + for (c = 0 ; c < numberOfComponents ; c++) + { + float result; + result = um1 * verts[c] + u * verts[c+numberOfComponents] + + up1 * verts[c+numberOfComponents*2] + up2 * verts[c+numberOfComponents*3]; + result *= brighten; + + if (result < 0.0) + { + dst[c] = 0; + } + else if (result > 255.0) + { + dst[c] = 255; + } + else + { + dst[c] = (uint)result; + } + } + } +} + +} // NameSpace DigikamLensDistortionImagesPlugin + |