summaryrefslogtreecommitdiffstats
path: root/src/runtimerender/Qt3DSRenderImageScaler.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/runtimerender/Qt3DSRenderImageScaler.cpp')
-rw-r--r--src/runtimerender/Qt3DSRenderImageScaler.cpp883
1 files changed, 883 insertions, 0 deletions
diff --git a/src/runtimerender/Qt3DSRenderImageScaler.cpp b/src/runtimerender/Qt3DSRenderImageScaler.cpp
new file mode 100644
index 0000000..d699179
--- /dev/null
+++ b/src/runtimerender/Qt3DSRenderImageScaler.cpp
@@ -0,0 +1,883 @@
+/****************************************************************************
+**
+** Copyright (C) 1999-2001 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "Qt3DSRender.h"
+#include "foundation/Qt3DSMath.h"
+#include "foundation/Qt3DSAllocatorCallback.h"
+#include "Qt3DSRenderImageScaler.h"
+#include "foundation/Qt3DSMemoryBuffer.h"
+
+using namespace qt3ds::render;
+//==============================================================================
+// Namespace
+//==============================================================================
+CImageScaler::CImageScaler(NVAllocatorCallback &inAlloc)
+ : m_Allocator(inAlloc)
+{
+}
+//==============================================================================
+/**
+ * Scales the given image by the given scale factor.
+ *
+ * This method creates a new image based on the parameters given.
+ *
+ * @param inScaleMethod type of scaling operation
+ * @param inOldBuffer points to the old picture
+ * @param inOldWidth width of the old picture
+ * @param inOldHeight height of the old picture
+ * @param inNewBuffer will point to the scaled picture
+ * @param inNewWidth width of the new picture
+ * @param inNewHeight height of the new picture
+ * @param inPlanes number of planes (1 for greyscale, 3 for rgb, etc)
+ * also equivalent to the return value of the CTextureType::PixelSize method.
+ */
+void CImageScaler::Scale(EScaleMethod inScaleMethod, unsigned char *inOldBuffer,
+ unsigned long inOldWidth, unsigned long inOldHeight,
+ unsigned char *&outNewBuffer, unsigned long inNewWidth,
+ unsigned long inNewHeight, unsigned long inPlanes)
+{
+ switch (inScaleMethod) {
+ case SCALEMETHOD_CROP:
+ CImageScaler::Crop(inOldBuffer, inOldWidth, inOldHeight, outNewBuffer, inNewWidth,
+ inNewHeight, inPlanes);
+ break;
+
+ case SCALEMETHOD_BILINEAR:
+ CImageScaler::Bilinear(inOldBuffer, inOldWidth, inOldHeight, outNewBuffer, inNewWidth,
+ inNewHeight, inPlanes);
+ break;
+
+ default:
+ QT3DS_ASSERT(false);
+ break;
+ }
+}
+
+//==============================================================================
+/**
+ * Scales the given image by the given scale factor.
+ *
+ * This method creates a new image based on the parameters given.
+ *
+ * @param inScaleMethod type of scaling operation
+ * @param inOldBuffer points to the old picture
+ * @param inOldWidth width of the old picture
+ * @param inOldHeight height of the old picture
+ * @param inNewBuffer will point to the scaled picture
+ * @param inNewWidth width of the new picture
+ * @param inNewHeight height of the new picture
+ * @param inPlanes number of planes (1 for greyscale, 3 for rgb, etc)
+ * also equivalent to the return value of the CTextureType::PixelSize method.
+ */
+void CImageScaler::FastScale(EScaleMethod inScaleMethod, unsigned char *inOldBuffer,
+ unsigned long inOldWidth, unsigned long inOldHeight,
+ unsigned char *&outNewBuffer, unsigned long inNewWidth,
+ unsigned long inNewHeight, unsigned long inPlanes)
+{
+ switch (inScaleMethod) {
+ case SCALEMETHOD_CROP:
+ CImageScaler::Crop(inOldBuffer, inOldWidth, inOldHeight, outNewBuffer, inNewWidth,
+ inNewHeight, inPlanes);
+ break;
+
+ case SCALEMETHOD_POINTSAMPLE:
+ CImageScaler::FastPointSample(inOldBuffer, inOldWidth, inOldHeight, outNewBuffer,
+ inNewWidth, inNewHeight, inPlanes);
+ break;
+
+ default:
+ QT3DS_ASSERT(false);
+ break;
+ }
+}
+
+//==============================================================================
+/**
+ * Debug method that simply crops the picture instead of scaling.
+ *
+ * Not for use in production. This is a test method to exercise the framework.
+ *
+ * @param inOldBuffer points to the old picture
+ * @param inOldWidth width of the old picture
+ * @param inOldHeight height of the old picture
+ * @param inNewBuffer will point to the scaled picture
+ * @param inNewWidth width of the new picture
+ * @param inNewHeight height of the new picture
+ * @param inPlanes number of planes (1 for greyscale, 3 for rgb, etc)
+ * also equivalent to the return value of the CTextureType::PixelSize method.
+*/
+void CImageScaler::Crop(unsigned char *inOldBuffer, unsigned long inOldWidth,
+ unsigned long inOldHeight, unsigned char *&outNewBuffer,
+ unsigned long inNewWidth, unsigned long inNewHeight, unsigned long inPlanes)
+{
+ Q_UNUSED(inOldHeight);
+
+ QT3DS_ASSERT(inNewWidth <= inOldWidth);
+ QT3DS_ASSERT(inNewHeight <= inOldHeight);
+
+ long theMinWidth = NVMin(inOldWidth, inNewWidth);
+
+ outNewBuffer = new unsigned char[inNewWidth * inNewHeight * inPlanes];
+ ::memset(outNewBuffer, 0, inNewWidth * inNewHeight * inPlanes);
+
+ for (unsigned long theRow = 0; theRow < inNewHeight; ++theRow) {
+ ::memcpy(outNewBuffer + theRow * inNewWidth * inPlanes,
+ inOldBuffer + theRow * inOldWidth * inPlanes, theMinWidth * inPlanes);
+ }
+}
+
+//==============================================================================
+/**
+ * Plain scaling.
+ *
+ * Code adopted from http://www.codeguru.com/bitmap/SmoothBitmapResizing.html
+ * Preliminary formatting completed but still needs work.
+ *
+ * @param inOldBuffer points to the old picture
+ * @param inOldWidth width of the old picture
+ * @param inOldHeight height of the old picture
+ * @param inNewBuffer will point to the scaled picture
+ * @param inNewWidth width of the new picture
+ * @param inNewHeight height of the new picture
+ * @param inPlanes number of planes (1 for greyscale, 3 for rgb, etc)
+ * also equivalent to the return value of the CTextureType::PixelSize method.
+*/
+void CImageScaler::Bilinear(unsigned char *inOldBuffer, unsigned long inOldWidth,
+ unsigned long inOldHeight, unsigned char *&outNewBuffer,
+ unsigned long inNewWidth, unsigned long inNewHeight,
+ unsigned long inPlanes)
+{
+ QT3DS_ASSERT(inPlanes > 0);
+
+ outNewBuffer = new unsigned char[inNewWidth * inNewHeight * inPlanes];
+ CImageScaler::Resize(inOldBuffer, inOldWidth, inOldHeight, outNewBuffer, inNewWidth,
+ inNewHeight, inPlanes);
+}
+
+//==============================================================================
+/**
+ * Fast removal of selected pixels.
+ *
+ * Really fast scanning of every n-th pixel. This algorithm works basically by
+ * adding a fraction to the source pointer for each pixel destination, using
+ * fixed point arithmetic.
+ *
+ * @param inOldBuffer points to the old picture
+ * @param inOldWidth width of the old picture
+ * @param inOldHeight height of the old picture
+ * @param inNewBuffer will point to the scaled picture
+ * @param inNewWidth width of the new picture
+ * @param inNewHeight height of the new picture
+ * @param inPlanes number of planes (1 for greyscale, 3 for rgb, etc)
+ * also equivalent to the return value of the CTextureType::PixelSize method.
+*/
+void CImageScaler::FastPointSample(unsigned char *inOldBuffer, unsigned long inOldWidth,
+ unsigned long inOldHeight, unsigned char *&outNewBuffer,
+ unsigned long inNewWidth, unsigned long inNewHeight,
+ unsigned long inPlanes)
+{
+ unsigned long theXAccum;
+ unsigned long theYAccum;
+ unsigned long theXConst;
+ unsigned long theYConst;
+ unsigned long theRow;
+ unsigned long theColumn;
+ unsigned long theAdd;
+ unsigned long theSrcIndex;
+ outNewBuffer = new unsigned char[inNewWidth * inNewHeight * inPlanes];
+
+ // *** Debug ***
+ // char dMessage[100];
+ //::sprintf( dMessage, "PointSample: %ldx%ld to %ldx%ld\n",inOldInfo.m_Width, inOldInfo.m_Height, outNewInfo.m_Width, outNewInfo.m_Height );
+ //::OutputDebugString( dMessage );
+
+ switch (inPlanes) {
+ case 4: {
+ long *theSrc;
+ long *theDst;
+ theSrc = reinterpret_cast<long *>(inOldBuffer);
+ theDst = reinterpret_cast<long *>(outNewBuffer);
+ theYAccum = 0;
+ theXConst = (inOldWidth << 16) / inNewWidth;
+ theYConst = (inOldHeight << 16) / inNewHeight;
+ unsigned long theAdd;
+ for (theRow = 0; theRow < inNewHeight; theRow++) {
+ theXAccum = 0;
+ theSrcIndex = 0;
+ for (theColumn = 0; theColumn < inNewWidth; theColumn++) {
+
+ theXAccum += theXConst;
+ theAdd = theXAccum >> 16;
+ *theDst = theSrc[theSrcIndex];
+ theDst++;
+ theSrcIndex += theAdd;
+ // Clear out the integer portion of the accumulator.
+ theXAccum = theXAccum & 0xFFFF;
+ }
+
+ theYAccum += theYConst;
+ theAdd = (theYAccum) >> 16;
+ theSrc += theAdd * inOldWidth;
+ // Clear out the integer portion of the accumulator.
+ theYAccum = theYAccum & 0xFFFF;
+ }
+ } break;
+
+ case 3: {
+ unsigned char *theDest;
+ unsigned char *theSource;
+ theDest = reinterpret_cast<unsigned char *>(outNewBuffer);
+ theSource = reinterpret_cast<unsigned char *>(inOldBuffer);
+ theYAccum = 0;
+ theXConst = (inOldWidth << 16) / inNewWidth;
+ theYConst = (inOldHeight << 16) / inNewHeight;
+ for (theRow = 0; theRow < inNewHeight; ++theRow) {
+ theXAccum = 0;
+ theSrcIndex = 0;
+ for (theColumn = 0; theColumn < inNewWidth; ++theColumn) {
+ theDest[0] = theSource[0];
+ theDest[1] = theSource[1];
+ theDest[2] = theSource[2];
+ theDest += 3;
+ theSrcIndex += 3 * (theXAccum) >> 16;
+ theXAccum = theXAccum & 0xFFFF;
+ }
+ theYAccum += theYConst;
+ theAdd = (theYAccum) >> 16;
+ theSource += theAdd * inOldWidth * 3;
+ theYAccum = theYAccum & 0xFFFF;
+ }
+ } break;
+
+ case 2: {
+ short *theDest;
+ short *theSource;
+ theDest = reinterpret_cast<short *>(outNewBuffer);
+ theSource = reinterpret_cast<short *>(inOldBuffer);
+ theYAccum = 0;
+ theXConst = (inOldWidth << 16) / inNewWidth;
+ theYConst = (inOldHeight << 16) / inNewHeight;
+ for (unsigned long theY = 0; theY < inNewHeight; ++theY) {
+ theXAccum = 0;
+ theSrcIndex = 0;
+ for (unsigned long theX = 0; theX < inNewWidth; ++theX) {
+ *theDest = *theSource;
+ ++theDest;
+ theXAccum += theXConst;
+ theSrcIndex += (theXAccum) >> 16;
+ theXAccum = theXAccum & 0xFFFF;
+ }
+ theYAccum += theYConst;
+ theAdd = (theYAccum) >> 16;
+ theSource += theAdd * inOldWidth;
+ theYAccum = theYAccum & 0xFFFF;
+ }
+ } break;
+
+ case 1: {
+ unsigned char *theDest;
+ unsigned char *theSource;
+ theDest = reinterpret_cast<unsigned char *>(outNewBuffer);
+ theSource = reinterpret_cast<unsigned char *>(inOldBuffer);
+ theYAccum = 0;
+ theXConst = (inOldWidth << 16) / inNewWidth;
+ theYConst = (inOldHeight << 16) / inNewHeight;
+ for (unsigned long theY = 0; theY < inNewHeight; ++theY) {
+ theXAccum = 0;
+ theSrcIndex = 0;
+ for (unsigned long theX = 0; theX < inNewWidth; ++theX) {
+ *theDest = *theSource;
+ ++theDest;
+ theXAccum += theXConst;
+ theSrcIndex += (theXAccum) >> 16;
+ theXAccum = theXAccum & 0xFFFF;
+ }
+ theYAccum += theYConst;
+ theAdd = (theYAccum) >> 16;
+ theSource += theAdd * inOldWidth;
+ theYAccum = theYAccum & 0xFFFF;
+ }
+ } break;
+
+ default:
+ QT3DS_ASSERT(false);
+ break;
+ }
+}
+
+//==============================================================================
+/**
+ * @param inWidth
+ * @param inHeight
+ * @return unsigned char*
+ */
+unsigned char *CImageScaler::AllocateBuffer(long inWidth, long inHeight)
+{
+ unsigned char *theBuffer = new unsigned char[inHeight * inWidth * 4];
+ return theBuffer;
+}
+
+//==============================================================================
+/**
+ * @param ioBuffer the buffer to release
+ */
+void CImageScaler::ReleaseBuffer(unsigned char *&ioBuffer)
+{
+ delete[] ioBuffer;
+ ioBuffer = NULL;
+}
+
+//==============================================================================
+/**
+ * @param inOldBuffer points to the old picture
+ * @param inOldWidth width of the old picture
+ * @param inOldHeight height of the old picture
+ * @param inNewBuffer will point to the scaled picture
+ * @param inNewWidth width of the new picture
+ * @param inNewHeight height of the new picture
+ * @param inPlanes number of planes (1 for greyscale, 3 for rgb, etc)
+ * also equivalent to the return value of the CTextureType::PixelSize method.
+ */
+void CImageScaler::Resize(unsigned char *inOldBuffer, unsigned long inOldWidth,
+ unsigned long inOldHeight, unsigned char *&outNewBuffer,
+ unsigned long inNewWidth, unsigned long inNewHeight,
+ unsigned long inPlanes)
+{
+ QT3DS_ASSERT(inPlanes == 4);
+
+ // only do the temporary allocation if necessary
+ if (inOldWidth < inNewWidth || inOldHeight < inNewHeight) {
+ CImageScaler::ExpandRowsAndColumns(inOldBuffer, inOldWidth, inOldHeight, outNewBuffer,
+ inNewWidth, inNewHeight, inPlanes);
+ return;
+ } else {
+ // The downsampling algorithms *do* assume four planes.
+ if (inOldWidth > inNewWidth && inOldHeight > inNewHeight) {
+ MemoryBuffer<> theBuffer(ForwardingAllocator(m_Allocator, "ImageScaler::TempBuffer"));
+ theBuffer.reserve(inNewWidth * inOldHeight * 4);
+ unsigned char *theTempBuffer = theBuffer.begin();
+ CImageScaler::ReduceCols(inOldBuffer, inOldWidth, inOldHeight, theTempBuffer,
+ inNewWidth);
+ CImageScaler::ReduceRows(theTempBuffer, inNewWidth, inOldHeight, outNewBuffer,
+ inNewHeight);
+ } else if (inOldWidth > inNewWidth) {
+ CImageScaler::ReduceCols(inOldBuffer, inOldWidth, inOldHeight, outNewBuffer,
+ inNewWidth);
+ } else if (inOldHeight > inNewHeight) {
+ CImageScaler::ReduceRows(inOldBuffer, inNewWidth, inOldHeight, outNewBuffer,
+ inNewHeight);
+ }
+ }
+}
+
+void CImageScaler::ExpandRowsAndColumns(unsigned char *inBuffer, unsigned long inWidth,
+ unsigned long inHeight, unsigned char *outBuffer,
+ unsigned long inDstWidth, unsigned long inDstHeight,
+ unsigned long inPlanes)
+{
+ if (inDstWidth < inWidth || inDstHeight < inHeight) {
+ return;
+ }
+ /*if( inPlanes == 4 )
+ {
+ FastExpandRowsAndColumns( inBuffer, inWidth, inHeight,
+ outBuffer, inDstWidth, inDstHeight );
+ return;
+ }*/
+ unsigned long theYPosition;
+ unsigned short theYRatio;
+ unsigned short theYInvRatio;
+ unsigned long theXPosition;
+ unsigned short theXRatio;
+ unsigned short theXInvRatio;
+
+ unsigned long theRow;
+ unsigned long theColumn;
+ unsigned long theSrcIndex;
+ unsigned long theDstIndex;
+ unsigned long theSrcLineLength;
+ unsigned long theTemp;
+ unsigned long thePixel;
+
+ theDstIndex = 0;
+ theSrcIndex = 0;
+ theSrcLineLength = inWidth * inPlanes;
+ theYPosition = inDstHeight;
+ theYRatio = 1 << 8;
+ theYInvRatio = 0;
+ theXInvRatio = 0;
+ // Here we go....
+ // This algorithm will be quite a bit hairy, if you want
+ // to understand it, then look at the two expand alogorithms above
+ // and realize the this is just the logical combination of the two
+ for (theRow = 0; theRow < inDstHeight; theRow++) {
+ // Run through all the rows, multiplying if necessary the two ratio's together
+ theXPosition = inDstWidth;
+ if (theYPosition < inHeight) {
+ // We have crossed a row boundary
+ theYRatio = (unsigned short)((theYPosition << 8) / inHeight);
+ theYInvRatio = (unsigned short)((1 << 8) - theYRatio);
+
+ for (theColumn = 0; theColumn < inDstWidth; theColumn++) {
+ if (theXPosition < inWidth) {
+ theXRatio = (unsigned short)((theXPosition << 8) / inWidth);
+ theXInvRatio = (unsigned short)((1 << 8) - theXRatio);
+
+ // The combination of both the x and y ratio's
+ unsigned long theLeftRatio = (theXRatio * theYRatio) >> 8;
+ unsigned long theRightRatio = (theXInvRatio * theYRatio) >> 8;
+ unsigned long theLowLeftRatio = (theXRatio * theYInvRatio) >> 8;
+ unsigned long theLowRightRatio = (theXInvRatio * theYInvRatio) >> 8;
+ // We are on a row and column boundary, thus each pixel here is the
+ // combination of four pixels (left right, low left, low right)
+ for (thePixel = 0; thePixel < inPlanes; thePixel++) {
+ // Left side first
+ theTemp = (theLeftRatio * inBuffer[theSrcIndex]);
+ theTemp += (theRightRatio * inBuffer[theSrcIndex + inPlanes]);
+ theTemp += (theLowLeftRatio * inBuffer[theSrcIndex + theSrcLineLength]);
+ theTemp += (theLowRightRatio
+ * inBuffer[theSrcIndex + theSrcLineLength + inPlanes]);
+ outBuffer[theDstIndex] = (unsigned char)(theTemp >> 8);
+ theDstIndex++;
+ theSrcIndex++;
+ }
+ // Reset our position calculation
+ theXPosition = inDstWidth - inWidth + theXPosition;
+ } else {
+ for (thePixel = 0; thePixel < inPlanes; thePixel++) {
+ theTemp = theYRatio * inBuffer[theSrcIndex + thePixel];
+ theTemp +=
+ theYInvRatio * inBuffer[theSrcIndex + theSrcLineLength + thePixel];
+ outBuffer[theDstIndex] = (unsigned char)(theTemp >> 8);
+ theDstIndex++;
+ }
+ // Reset our position calculation
+ theXPosition -= inWidth;
+ }
+ }
+ // Reset our position calculation
+ theYPosition = inDstHeight - inHeight + theYPosition;
+ // Make the src index point to the next line
+ theSrcIndex += inPlanes;
+ } // Ends the if to check if we are crossing a row boundary
+ // Else we are not crossing a row boundary
+ else {
+ for (theColumn = 0; theColumn < inDstWidth; theColumn++) {
+ // If we are crossing a column boundary
+ if (theXPosition < inWidth) {
+ theXRatio = (unsigned short)((theXPosition << 8) / inWidth);
+ theXInvRatio = (unsigned short)((1 << 8) - theXRatio);
+ for (thePixel = 0; thePixel < inPlanes; thePixel++) {
+ theTemp = theXRatio * inBuffer[theSrcIndex];
+ theTemp += theXInvRatio * inBuffer[theSrcIndex + inPlanes];
+ outBuffer[theDstIndex] = (unsigned char)(theTemp >> 8);
+ theSrcIndex++;
+ theDstIndex++;
+ }
+
+ theXPosition = inDstWidth - inWidth + theXPosition;
+ }
+ // Else we are not crossing a column boundary
+ else {
+ for (thePixel = 0; thePixel < inPlanes; thePixel++) {
+ outBuffer[theDstIndex] = inBuffer[theSrcIndex + thePixel];
+ theDstIndex++;
+ }
+ theXPosition -= inWidth;
+ }
+ }
+ // reset our y position indicator
+ theYPosition -= inHeight;
+ // reset the src index to the beginning the next line
+ theSrcIndex += inPlanes;
+ // reset src index to the beginning of this line
+ theSrcIndex -= theSrcLineLength;
+ } // End of else for row boundary
+ } // End of for loop for iterating through all rows
+}
+
+// Assuming the number of planes is four
+
+void CImageScaler::FastExpandRowsAndColumns(unsigned char *inBuffer, unsigned long inWidth,
+ unsigned long inHeight, unsigned char *outBuffer,
+ unsigned long inDstWidth, unsigned long inDstHeight)
+{
+
+ if (inDstWidth < inWidth || inDstHeight < inHeight) {
+ return;
+ }
+ unsigned long theYPosition;
+ unsigned short theYRatio;
+ unsigned short theYInvRatio;
+ unsigned long theXPosition;
+ unsigned short theXRatio;
+ unsigned short theXInvRatio;
+ // The combination of both the x and y ratio's
+ unsigned long theLeftRatio;
+ unsigned long theRightRatio;
+ unsigned long theLowLeftRatio;
+ unsigned long theLowRightRatio;
+
+ unsigned long theRow;
+ unsigned long theColumn;
+ unsigned long theSrcIndex;
+ unsigned long theDstIndex;
+ unsigned long theSrcLineLength;
+ unsigned long theTemp;
+
+ theDstIndex = 0;
+ theSrcIndex = 0;
+ theSrcLineLength = inWidth * 4;
+ theYPosition = inDstHeight;
+ theYInvRatio = 0;
+ theXInvRatio = 0;
+ // Here we go....
+ // This algorithm will be quite a bit hairy, if you want
+ // to understand it, then look at the two expand alogorithms above
+ // and realize the this is just the logical combination of the two
+ for (theRow = 0; theRow < inDstHeight; theRow++) {
+ // Run through all the rows, multiplying if necessary the two ratio's together
+ theXPosition = inDstWidth;
+ if (theYPosition < inHeight) {
+ // We have crossed a row boundary
+ theYRatio = (unsigned short)((theYPosition << 8) / inHeight);
+ theYInvRatio = (unsigned short)((1 << 8) - theYRatio);
+
+ for (theColumn = 0; theColumn < inDstWidth; theColumn++) {
+ if (theXPosition < inWidth) {
+ theXRatio = (unsigned short)((theXPosition << 8) / inWidth);
+ theXInvRatio = (unsigned short)((1 << 8) - theXRatio);
+ theLeftRatio = (theXRatio * theYRatio) >> 8;
+ theRightRatio = (theXInvRatio * theYRatio) >> 8;
+ theLowLeftRatio = (theXRatio * theYInvRatio) >> 8;
+ theLowRightRatio = (theXInvRatio * theYInvRatio) >> 8;
+ // We are on a row and column boundary, thus each pixel here is the
+ // combination of four pixels (left right, low left, low right)
+
+ // Left side first
+ theTemp = (inBuffer[theSrcIndex]);
+ theTemp += (inBuffer[theSrcIndex + 4]);
+ theTemp += (inBuffer[theSrcIndex + theSrcLineLength]);
+ theTemp += (inBuffer[theSrcIndex + theSrcLineLength + 4]);
+ outBuffer[theDstIndex] = (unsigned char)(theTemp >> 2);
+ theDstIndex++;
+ theSrcIndex++;
+ // Left side first
+ theTemp = (inBuffer[theSrcIndex]);
+ theTemp += (inBuffer[theSrcIndex + 4]);
+ theTemp += (inBuffer[theSrcIndex + theSrcLineLength]);
+ theTemp += (inBuffer[theSrcIndex + theSrcLineLength + 4]);
+ outBuffer[theDstIndex] = (unsigned char)(theTemp >> 2);
+ theDstIndex++;
+ theSrcIndex++;
+ // Left side first
+ theTemp = (inBuffer[theSrcIndex]);
+ theTemp += (inBuffer[theSrcIndex + 4]);
+ theTemp += (inBuffer[theSrcIndex + theSrcLineLength]);
+ theTemp += (inBuffer[theSrcIndex + theSrcLineLength + 4]);
+ outBuffer[theDstIndex] = (unsigned char)(theTemp >> 2);
+ theDstIndex++;
+ theSrcIndex++;
+ // Left side first
+ theTemp = (inBuffer[theSrcIndex]);
+ theTemp += (inBuffer[theSrcIndex + 4]);
+ theTemp += (inBuffer[theSrcIndex + theSrcLineLength]);
+ theTemp += (inBuffer[theSrcIndex + theSrcLineLength + 4]);
+ outBuffer[theDstIndex] = (unsigned char)(theTemp >> 2);
+ theDstIndex++;
+ theSrcIndex++;
+ // Reset our position calculation
+ theXPosition = inDstWidth - inWidth + theXPosition;
+ } else {
+
+ theTemp = inBuffer[theSrcIndex];
+ theTemp += inBuffer[theSrcIndex + theSrcLineLength];
+ outBuffer[theDstIndex] = (unsigned char)(theTemp >> 1);
+ theDstIndex++;
+ theTemp = inBuffer[theSrcIndex + 1];
+ theTemp += inBuffer[theSrcIndex + theSrcLineLength + 1];
+ outBuffer[theDstIndex] = (unsigned char)(theTemp >> 1);
+ theDstIndex++;
+ theTemp = inBuffer[theSrcIndex + 2];
+ theTemp += inBuffer[theSrcIndex + theSrcLineLength + 2];
+ outBuffer[theDstIndex] = (unsigned char)(theTemp >> 1);
+ theDstIndex++;
+ theTemp = inBuffer[theSrcIndex + 3];
+ theTemp += inBuffer[theSrcIndex + theSrcLineLength + 3];
+ outBuffer[theDstIndex] = (unsigned char)(theTemp >> 1);
+ theDstIndex++;
+ // Reset our position calculation
+ theXPosition -= inWidth;
+ }
+ }
+ // Reset our position calculation
+ theYPosition = inDstHeight - inHeight + theYPosition;
+ // Make the src index point to the next line
+ theSrcIndex += 4;
+ } // Ends the if to check if we are crossing a row boundary
+ // Else we are not crossing a row boundary
+ else {
+ for (theColumn = 0; theColumn < inDstWidth; theColumn++) {
+ // If we are crossing a column boundary
+ if (theXPosition < inWidth) {
+ theXRatio = (unsigned short)((theXPosition << 8) / inWidth);
+ theXInvRatio = (unsigned short)((1 << 8) - theXRatio);
+
+ theTemp = inBuffer[theSrcIndex];
+ theTemp += inBuffer[theSrcIndex + 4];
+ outBuffer[theDstIndex] = (unsigned char)(theTemp >> 1);
+ theSrcIndex++;
+ theDstIndex++;
+ theTemp = inBuffer[theSrcIndex];
+ theTemp += inBuffer[theSrcIndex + 4];
+ outBuffer[theDstIndex] = (unsigned char)(theTemp >> 1);
+ theSrcIndex++;
+ theDstIndex++;
+ theTemp = inBuffer[theSrcIndex];
+ theTemp += inBuffer[theSrcIndex + 4];
+ outBuffer[theDstIndex] = (unsigned char)(theTemp >> 1);
+ theSrcIndex++;
+ theDstIndex++;
+ theTemp = inBuffer[theSrcIndex];
+ theTemp += inBuffer[theSrcIndex + 4];
+ outBuffer[theDstIndex] = (unsigned char)(theTemp >> 1);
+ theSrcIndex++;
+ theDstIndex++;
+
+ theXPosition = inDstWidth - inWidth + theXPosition;
+ }
+ // Else we are not crossing a column boundary
+ else {
+ *((long *)(outBuffer + theDstIndex)) = *((long *)(inBuffer + theSrcIndex));
+ theDstIndex += 4;
+ theXPosition -= inWidth;
+ }
+ }
+ // reset our y position indicator
+ theYPosition -= inHeight;
+ // reset the src index to the beginning the next line
+ theSrcIndex += 4;
+ // reset src index to the beginning of this line
+ theSrcIndex -= theSrcLineLength;
+ } // End of else for row boundary
+ } // End of for loop for iterating through all rows
+}
+
+//==============================================================================
+/**
+ * @param inSrcBuffer
+ */
+void CImageScaler::ReduceCols(unsigned char *inSrcBuffer, long inSrcWidth, long inSrcHeight,
+ unsigned char *&outDstBuffer, long inDstWidth)
+{
+ long theDDAConst = static_cast<long>(1024.0 * inDstWidth / inSrcWidth);
+ long theDDAAccum = 0L;
+ long thePixelCount;
+
+ long theSrcRow;
+ long theSrcCol;
+ long theDstCol;
+
+ long theRedAccum;
+ long theGreenAccum;
+ long theBlueAccum;
+ long theAlphaAccum;
+
+ unsigned char *theDstPointer = outDstBuffer;
+ unsigned char *theSrcPointer = inSrcBuffer;
+ unsigned char *theSrcRowPointer;
+ unsigned char *theDstRowPointer;
+
+ long theSrcStepSize = 4;
+ long theDstStepSize = 4;
+
+ for (theSrcRow = 0; theSrcRow < inSrcHeight; ++theSrcRow) {
+
+ theSrcRowPointer = theSrcPointer + (theSrcRow * inSrcWidth * theSrcStepSize);
+ theDstRowPointer = theDstPointer + (theSrcRow * inDstWidth * theDstStepSize);
+
+ theSrcCol = 0L;
+ theDstCol = 0L;
+ theRedAccum = 0L;
+ theGreenAccum = 0L;
+ theBlueAccum = 0L;
+ theAlphaAccum = 0L;
+ thePixelCount = 0L;
+ theDDAAccum = 0L;
+
+ while (theSrcCol < inSrcWidth) {
+ while ((theDDAAccum < 1024L) && (theSrcCol < inSrcWidth)) {
+ theRedAccum += 1024L * theSrcRowPointer[(theSrcCol * theSrcStepSize) + 0];
+ theGreenAccum += 1024L * theSrcRowPointer[(theSrcCol * theSrcStepSize) + 1];
+ theBlueAccum += 1024L * theSrcRowPointer[(theSrcCol * theSrcStepSize) + 2];
+ theAlphaAccum += 1024L * theSrcRowPointer[(theSrcCol * theSrcStepSize) + 3];
+
+ theDDAAccum += theDDAConst;
+ thePixelCount += 1024L;
+ ++theSrcCol;
+ }
+
+ theDDAAccum = (theSrcCol < inSrcWidth) ? (theDDAAccum - 1024L) : (0L);
+ thePixelCount -= theDDAAccum;
+
+ theRedAccum -=
+ theDDAAccum * (long)theSrcRowPointer[((theSrcCol - 1) * theSrcStepSize) + 0];
+ theGreenAccum -=
+ theDDAAccum * (long)theSrcRowPointer[((theSrcCol - 1) * theSrcStepSize) + 1];
+ theBlueAccum -=
+ theDDAAccum * (long)theSrcRowPointer[((theSrcCol - 1) * theSrcStepSize) + 2];
+ theAlphaAccum -=
+ theDDAAccum * (long)theSrcRowPointer[((theSrcCol - 1) * theSrcStepSize) + 3];
+
+ theDstRowPointer[(theDstCol * theDstStepSize) + 0] =
+ (unsigned char)(theRedAccum / thePixelCount);
+ theDstRowPointer[(theDstCol * theDstStepSize) + 1] =
+ (unsigned char)(theGreenAccum / thePixelCount);
+ theDstRowPointer[(theDstCol * theDstStepSize) + 2] =
+ (unsigned char)(theBlueAccum / thePixelCount);
+ theDstRowPointer[(theDstCol * theDstStepSize) + 3] =
+ (unsigned char)(theAlphaAccum / thePixelCount);
+
+ thePixelCount = 1024L - theDDAAccum;
+ ++theDstCol;
+
+ if (theDstCol >= inDstWidth) {
+ break;
+ }
+
+ theRedAccum =
+ thePixelCount * (long)theSrcRowPointer[((theSrcCol - 1) * theSrcStepSize) + 0];
+ theGreenAccum =
+ thePixelCount * (long)theSrcRowPointer[((theSrcCol - 1) * theSrcStepSize) + 1];
+ theBlueAccum =
+ thePixelCount * (long)theSrcRowPointer[((theSrcCol - 1) * theSrcStepSize) + 2];
+ theAlphaAccum =
+ thePixelCount * (long)theSrcRowPointer[((theSrcCol - 1) * theSrcStepSize) + 3];
+ }
+ }
+}
+
+//==============================================================================
+/**
+ * @param inSrcBuffer
+ */
+void CImageScaler::ReduceRows(unsigned char *inSrcBuffer, long inSrcWidth, long inSrcHeight,
+ unsigned char *&outDstBuffer, long inDstHeight)
+{
+ long theDDAConst = static_cast<long>(1024.0 * inDstHeight / inSrcHeight);
+ long theDDAAccum = 0;
+ long thePixelCount;
+
+ long theSrcRow;
+ long theSrcCol;
+ long theDstRow;
+
+ long theRedAccum;
+ long theGreenAccum;
+ long theBlueAccum;
+ long theAlphaAccum;
+
+ unsigned char *theDstPointer = outDstBuffer;
+ unsigned char *theSrcPointer = inSrcBuffer;
+ unsigned char *theSrcColPointer = NULL;
+ unsigned char *theDstColPointer = NULL;
+
+ long theStepSize = 4;
+ long theSrcStride = 4 * inSrcWidth;
+ long theDstStride = 4 * inSrcWidth;
+
+ for (theSrcCol = 0; theSrcCol < inSrcWidth; ++theSrcCol) {
+ theSrcColPointer = theSrcPointer + (theSrcCol * theStepSize);
+ theDstColPointer = theDstPointer + (theSrcCol * theStepSize);
+
+ theSrcRow = 0L;
+ theDstRow = 0L;
+ theRedAccum = 0L;
+ theGreenAccum = 0L;
+ theBlueAccum = 0L;
+ theAlphaAccum = 0L;
+ thePixelCount = 0L;
+
+ theDDAAccum = 0L;
+
+ while (theSrcRow < inSrcHeight) {
+ while ((theDDAAccum < 1024L) && (theSrcRow < inSrcHeight)) {
+ theRedAccum += 1024L * theSrcColPointer[(theSrcRow * theSrcStride) + 0];
+ theGreenAccum += 1024L * theSrcColPointer[(theSrcRow * theSrcStride) + 1];
+ theBlueAccum += 1024L * theSrcColPointer[(theSrcRow * theSrcStride) + 2];
+ theAlphaAccum += 1024L * theSrcColPointer[(theSrcRow * theSrcStride) + 3];
+
+ theDDAAccum += theDDAConst;
+ thePixelCount += 1024L;
+ ++theSrcRow;
+ }
+
+ theDDAAccum = (theSrcRow < inSrcHeight) ? (theDDAAccum - 1024L) : (0L);
+ thePixelCount -= theDDAAccum;
+
+ theRedAccum -=
+ theDDAAccum * (long)theSrcColPointer[((theSrcRow - 1) * theSrcStride) + 0];
+ theGreenAccum -=
+ theDDAAccum * (long)theSrcColPointer[((theSrcRow - 1) * theSrcStride) + 1];
+ theBlueAccum -=
+ theDDAAccum * (long)theSrcColPointer[((theSrcRow - 1) * theSrcStride) + 2];
+ theAlphaAccum -=
+ theDDAAccum * (long)theSrcColPointer[((theSrcRow - 1) * theSrcStride) + 3];
+
+ theDstColPointer[(theDstRow * theDstStride) + 0] =
+ (unsigned char)(theRedAccum / thePixelCount);
+ theDstColPointer[(theDstRow * theDstStride) + 1] =
+ (unsigned char)(theGreenAccum / thePixelCount);
+ theDstColPointer[(theDstRow * theDstStride) + 2] =
+ (unsigned char)(theBlueAccum / thePixelCount);
+ theDstColPointer[(theDstRow * theDstStride) + 3] =
+ (unsigned char)(theAlphaAccum / thePixelCount);
+
+ thePixelCount = 1024L - theDDAAccum;
+ ++theDstRow;
+
+ if (theDstRow >= inDstHeight) {
+ break;
+ }
+
+ theRedAccum =
+ thePixelCount * (long)theSrcColPointer[((theSrcRow - 1) * theSrcStride) + 0];
+ theGreenAccum =
+ thePixelCount * (long)theSrcColPointer[((theSrcRow - 1) * theSrcStride) + 1];
+ theBlueAccum =
+ thePixelCount * (long)theSrcColPointer[((theSrcRow - 1) * theSrcStride) + 2];
+ theAlphaAccum =
+ thePixelCount * (long)theSrcColPointer[((theSrcRow - 1) * theSrcStride) + 3];
+ }
+ }
+}