|
Hello All,
I'm about to convert/modify the code to support 16-bit images as I am working on smartphones and smaller image sizes are important and most of the displays are only capable of 16 or 15-bit images anyway.
Anyone done it already?
Thanks,
Kenny.
|
|
|
|
|
This really is an excellent class. I just love the way you simplified the ability to add filters.
Now I wonder if you could help me. I am scaling a bitmap image before running through the pixels to create a window region. Ie, I am using a bitmap with a mask color to template my window. Now I want to add the ability for a window to be resized, the only way to do this is to resize my bitmap and create a new window region.
Trouble is, when I use this class, the output bits all have a slight color shift. To the naked eye this is nothing, but when scanning for a certain pixel color, it cannot be found.
I want to add an extra paramater, a COLOREF value, to the Scale() member function. Which in turn would leave this color untouched when found. But the source code is not very well commented, so I am have problems trying to determine where I could place the color check routine.
Hope you can help
|
|
|
|
|
I am sorry I am not that interested in the article itself, but I want to talk about your problem.
Usually rotation/scaling will all involve interpolation. So that is why you are having the problem where a certain color shifts. However, you can solve most of your problem by choosing the nearest neighbour method to pick the destination value instead of 1st 2nd degree interpolations. This will probably apply somewhere in this particular article.
Try it out in Photoshop. create a test file rotate it by setting the preferences cubic, quad. and nearest neigh. See what happens.
If there are large blocks of the certain color, you will see that in those areas what I am talking about will be void and you will get your desired color regardless.
yalcin
|
|
|
|
|
Wow, it's been a long time since I worked on this project.
I did try out the scaling and rotations in Photoimpact and noticed the 'undesired' behaviour also. When I worked on this project it was a neccessity to scan for an exact coloref and not a range. As it happens, I modified the class slightly to leave that color untouched. In most cases this would cause anti-aliasing problems and leave jagged edges, but for what I was working on it was not a problem.
Waldermort
|
|
|
|
|
There is an interesting line in 2PassScale.h:
if (iLeft < (int(uSrcSize) - 1 / 2))
wouldn't it same as
if (iLeft < (int(uSrcSize) - 0)) ?
Or maybe I don't get it...
|
|
|
|
|
Below is the code for a modified version of C2PassScale that uses integer arithmetic to achieve a 3x-4x speed improvement.
It also handles images with more than 3 colors (change PS_MAX_DEPTH if you plan on using more than 4 colors).
It does _not_ currently handles images with other than 8 bits per channel.
There were also some bug fixes that I have included.
It also now takes the size of a full scan line in bytes. By using this intelligently, it is possible to scale a sub-rectangle of an image into a full image, or into another sub rectangle.
Unfortunately, I had already made some windows specific modifications for my own use, and I am too busy to take them out. So if you are not running on a windows platform, you will need to take out the ASSERT lines, and change some of the data types.
[3-30-05] Added comments to Scale() and AllocAndScale() to clarify the parameters.
Hope this is useful,
Jake Montgomery
=============== CODE BELOW ==================
#pragma once
#include <math.h>
#define BOUND(x,a,b) \
(((x) <= (a)) ? (a) : (((x) > (b)) ? (b) : (x)))
#define PS_MAX_DEPTH 4
// Based on Eran Yariv's TwoPassScale, modified for speed, bug fixes, and variable depths.
// Does 2 pass scaling of bitmaps
// Template based, can use various methods for interpolation.
// Could certainly be improved.
// to use:
// C2PassScale <CBilinearFilter> ScaleEngine;
// ScaleEngine.Scale ((UCHAR *)dibSrc.GetBits(), // A pointer to the source bitmap bits
// depth, // The size of a single pixel in bytes (both source and scaled image)
// dibSrc.Width(), // The width of a line of the source image to scale in pixels
// dibSrc.WidthBytes(), // The width of a single line of the source image in bytes (to allow for padding, etc.)
// dibSrc.Height(), // The height of the source image to scale in pixels.
// (UCHAR *)GetBits(), // A pointer to a buffer to hold the ecaled image
// Width(), // The desired width of a line of the scaled image in pixels
// WidthBytes(), // The width of a single line of the scaled image in bytes (to allow for padding, etc.)
// Height()); // The desired height of the scaled image in pixels.
// or AllocAndScale()
// Modified 1-17-05 to use more integer math -- much faater. [JRM]
class CGenericFilter
{
public:
CGenericFilter (double dWidth) : m_dWidth (dWidth) {}
virtual ~CGenericFilter() {}
double GetWidth() { return m_dWidth; }
void SetWidth (double dWidth) { m_dWidth = dWidth; }
virtual double Filter (double dVal) = 0;
protected:
#define FILTER_PI double (3.1415926535897932384626433832795)
#define FILTER_2PI double (2.0 * 3.1415926535897932384626433832795)
#define FILTER_4PI double (4.0 * 3.1415926535897932384626433832795)
double m_dWidth;
};
class CBoxFilter : public CGenericFilter
{
public:
CBoxFilter (double dWidth = double(0.5)) : CGenericFilter(dWidth) {}
virtual ~CBoxFilter() {}
double Filter (double dVal) { return (fabs(dVal) <= m_dWidth ? 1.0 : 0.0); }
};
class CBilinearFilter : public CGenericFilter
{
public:
CBilinearFilter (double dWidth = double(1.0)) : CGenericFilter(dWidth) {}
virtual ~CBilinearFilter() {}
double Filter (double dVal)
{
dVal = fabs(dVal);
return (dVal < m_dWidth ? m_dWidth - dVal : 0.0);
}
};
class CGaussianFilter : public CGenericFilter
{
public:
CGaussianFilter (double dWidth = double(3.0)) : CGenericFilter(dWidth) {}
virtual ~CGaussianFilter() {}
double Filter (double dVal)
{
if (fabs (dVal) > m_dWidth)
{
return 0.0;
}
return exp (-dVal * dVal / 2.0) / sqrt (FILTER_2PI);
}
};
class CHammingFilter : public CGenericFilter
{
public:
CHammingFilter (double dWidth = double(0.5)) : CGenericFilter(dWidth) {}
virtual ~CHammingFilter() {}
double Filter (double dVal)
{
if (fabs (dVal) > m_dWidth)
{
return 0.0;
}
double dWindow = 0.54 + 0.46 * cos (FILTER_2PI * dVal);
double dSinc = (dVal == 0) ? 1.0 : sin (FILTER_PI * dVal) / (FILTER_PI * dVal);
return dWindow * dSinc;
}
};
class CBlackmanFilter : public CGenericFilter
{
public:
CBlackmanFilter (double dWidth = double(0.5)) : CGenericFilter(dWidth) {}
virtual ~CBlackmanFilter() {}
double Filter (double dVal)
{
if (fabs (dVal) > m_dWidth)
{
return 0.0;
}
double dN = 2.0 * m_dWidth + 1.0;
return 0.42 + 0.5 * cos (FILTER_2PI * dVal / ( dN - 1.0 )) +
0.08 * cos (FILTER_4PI * dVal / ( dN - 1.0 ));
}
};
typedef struct
{
unsigned int*Weights; // Normalized weights of neighboring pixels
int Left,Right; // Bounds of source pixels window
} ContributionType; // Contirbution information for a single pixel
typedef struct
{
ContributionType *ContribRow; // Row (or column) of contribution weights
UINT WindowSize, // Filter window size (of affecting source pixels)
LineLength; // Length of line (no. or rows / cols)
} LineContribType; // Contribution information for an entire line (row or column)
typedef BOOL (*ProgressAnbAbortCallBack)(BYTE bPercentComplete);
template <class FilterClass>
class C2PassScale
{
public:
C2PassScale (ProgressAnbAbortCallBack callback = NULL) :
m_Callback (callback) {m_byteDepth = 3;}
virtual ~C2PassScale() {}
UCHAR * AllocAndScale (
UCHAR *pOrigImage,
UINT pixelBytes,
UINT uOrigWidth,
UINT uOrigWidthBytes,
UINT uOrigHeight,
UINT uNewWidth,
UINT uNewWidthBytes,
UINT uNewHeight);
UCHAR * Scale (
UCHAR *pOrigImage,
UINT pixelBytes,
UINT uOrigWidth,
UINT uOrigWidthBytes,
UINT uOrigHeight,
UCHAR *pDstImage,
UINT uNewWidth,
UINT uNewWidthBytes,
UINT uNewHeight);
int m_byteDepth;
private:
ProgressAnbAbortCallBack m_Callback;
BOOL m_bCanceled;
LineContribType *AllocContributions ( UINT uLineLength,
UINT uWindowSize);
void FreeContributions (LineContribType * p);
LineContribType *CalcContributions ( UINT uLineSize,
UINT uSrcSize,
double dScale);
void ScaleRow ( UCHAR *pSrc,
UINT uSrcWidth,
UINT uSrcWidthBytes,
UCHAR *pRes,
UINT uResWidth,
UINT uDstWidthBytes,
UINT uRow,
LineContribType *Contrib);
void HorizScale ( UCHAR *pSrc,
UINT uSrcWidth,
UINT uSrcWidthBytes,
UINT uSrcHeight,
UCHAR *pDst,
UINT uResWidth,
UINT uResWidthBytes);
void ScaleCol ( UCHAR *pSrc,
UINT uSrcWidth,
UINT uSrcWidthBytes,
UCHAR *pRes,
UINT uResWidth,
UINT uResWidthBytes,
UINT uResHeight,
UINT uCol,
LineContribType *Contrib);
void VertScale ( UCHAR *pSrc,
UINT uSrcWidth,
UINT uSrcWidthBytes,
UINT uSrcHeight,
UCHAR *pDst,
UINT uResHeight);
};
template <class FilterClass>
LineContribType *
C2PassScale<FilterClass>::
AllocContributions (UINT uLineLength, UINT uWindowSize)
{
LineContribType *res = new LineContribType;
// Init structure header
res->WindowSize = uWindowSize;
res->LineLength = uLineLength;
// Allocate list of contributions
res->ContribRow = new ContributionType[uLineLength];
for (UINT u = 0 ; u < uLineLength ; u++)
{
// Allocate contributions for every pixel
res->ContribRow[u].Weights = new unsigned int[uWindowSize];
}
return res;
}
template <class FilterClass>
void
C2PassScale<FilterClass>::
FreeContributions (LineContribType * p)
{
for (UINT u = 0; u < p->LineLength; u++)
{
// Free contribs for every pixel
delete [] p->ContribRow[u].Weights;
}
delete [] p->ContribRow; // Free list of pixels contribs
delete p; // Free contribs header
}
template <class FilterClass>
LineContribType *
C2PassScale<FilterClass>::
CalcContributions (UINT uLineSize, UINT uSrcSize, double dScale)
{
FilterClass CurFilter;
double dWidth;
double dFScale = 1.0;
double dFilterWidth = CurFilter.GetWidth();
if (dScale < 1.0)
{ // Minification
dWidth = dFilterWidth / dScale;
dFScale = dScale;
}
else
{ // Magnification
dWidth= dFilterWidth;
}
// Window size is the number of sampled pixels
// int iWindowSize = 2 * (int)ceil(dWidth) + 1;
int iWindowSize = 2 * ((int)ceil(dWidth) + 1); // changed ... causing crash with bi-linear filiter?? [JRM]
// Allocate a new line contributions strucutre
LineContribType *res = AllocContributions (uLineSize, iWindowSize);
double *dWeights = new double[iWindowSize];
for (UINT u = 0; u < uLineSize; u++)
{ // Scan through line of contributions
double dCenter = (double)u / dScale; // Reverse mapping
// Find the significant edge points that affect the pixel
int iLeft = max (0, (int)floor (dCenter - dWidth));
int iRight = min ((int)ceil (dCenter + dWidth), int(uSrcSize) - 1);
// Cut edge points to fit in filter window in case of spill-off
if (iRight - iLeft + 1 > iWindowSize)
{
if (iLeft < (int(uSrcSize) - 1 / 2))
{
iLeft++;
}
else
{
iRight--;
}
}
int nFallback = iLeft;
BOOL bNonZeroFound = false;
double dTotalWeight = 0.0; // Zero sum of weights
double dVal;
for (int iSrc = iLeft; iSrc <= iRight; iSrc++)
{ // Calculate weights
dVal = CurFilter.Filter (dFScale * (dCenter - (double)iSrc));
if (dVal > 0.0)
dVal *= dFScale;
else
{
dVal = 0.0;
// zero conribution, trim
if (!bNonZeroFound)
{
// we are on the left side, trim left
iLeft = iSrc+1;
continue;
}
else
{
// we are on the right side, trim right
iRight = iSrc-1;
break;
}
}
bNonZeroFound = true;
dTotalWeight += dVal;
dWeights[iSrc-iLeft] = dVal;
}
if (iLeft > iRight)
{
ASSERT(FALSE);
iLeft = iRight = nFallback;
dWeights[0] = 0.0;
}
res->ContribRow[u].Left = iLeft;
res->ContribRow[u].Right = iRight;
ASSERT (dTotalWeight >= 0.0); // An error in the filter function can cause this
if (dTotalWeight > 0.0)
{ // Normalize weight of neighbouring points
for (iSrc = iLeft; iSrc <= iRight; iSrc++)
{ // Normalize point
dWeights[iSrc-iLeft] /= dTotalWeight;
}
}
// scale weights to integers weights for effeciency
for (iSrc = iLeft; iSrc <= iRight; iSrc++)
res->ContribRow[u].Weights[iSrc-iLeft] = (unsigned int)(dWeights[iSrc-iLeft] * 0xffff);
}
delete [] dWeights;
return res;
}
template <class FilterClass>
void
C2PassScale<FilterClass>::
ScaleRow ( UCHAR *pSrc,
UINT uSrcWidth,
UINT uSrcWidthBytes,
UCHAR *pRes,
UINT uResWidth,
UINT uResWidthBytes,
UINT uRow,
LineContribType *Contrib)
{
UCHAR * const pSrcRow = &(pSrc[uRow * uSrcWidthBytes]);
UCHAR * const pDstRow = &(pRes[uRow * uResWidthBytes]);
UCHAR *pSrcLoc;
UCHAR *pDstLoc;
unsigned int vals[PS_MAX_DEPTH];
for (UINT x = 0; x < uResWidth; x++)
{ // Loop through row
int v, i;
for (v= 0; v < m_byteDepth; v++)
vals[v] = 0;
int iLeft = Contrib->ContribRow[x].Left; // Retrieve left boundries
int iRight = Contrib->ContribRow[x].Right; // Retrieve right boundries
pSrcLoc = &pSrcRow[iLeft*m_byteDepth];
for (i = iLeft; i <= iRight; i++)
{ // Scan between boundries
#ifdef _DEBUG
ASSERT(i-iLeft < (int)Contrib->WindowSize);
#endif
// Accumulate weighted effect of each neighboring pixel
for (v= 0; v < m_byteDepth; v++)
vals[v] += Contrib->ContribRow[x].Weights[i-iLeft] * *pSrcLoc++;
}
pDstLoc = &pDstRow[x*m_byteDepth];
for (v= 0; v < m_byteDepth; v++)
{
// copy to destination, and scale back down by BYTE
*pDstLoc++ = BOUND(vals[v] >> 16, 0, 0xff); // Place result in destination pixel
}
}
}
template <class FilterClass>
void
C2PassScale<FilterClass>::
HorizScale ( UCHAR *pSrc,
UINT uSrcWidth,
UINT uSrcWidthBytes,
UINT uSrcHeight,
UCHAR *pDst,
UINT uResWidth,
UINT uResWidthBytes)
{
// Assumes heights are the same
// TRACE ("Performing horizontal scaling...\n");
if (uResWidth == uSrcWidth)
{ // No scaling required, just copy
if(uSrcHeight <= 0) return;
if (uResWidthBytes == uSrcWidthBytes)
{
int copy = ((uSrcHeight -1) * uSrcWidthBytes) + uSrcWidth*m_byteDepth; // avoids overrun if starting in middle of image.
memcpy (pDst, pSrc, copy);
return;
}
else
{
for (UINT y = 0; y < uSrcHeight; y++)
memcpy(pDst+uResWidthBytes*y, pSrc+uSrcWidthBytes*y, uSrcWidth*m_byteDepth);
return;
}
}
// Allocate and calculate the contributions
LineContribType * Contrib = CalcContributions (uResWidth, uSrcWidth, double(uResWidth) / double(uSrcWidth));
for (UINT u = 0; u < uSrcHeight; u++)
{ // Step through rows
if (NULL != m_Callback)
{
//
// Progress and report callback supplied
//
if (!m_Callback (BYTE(double(u) / double (uSrcHeight) * 50.0)))
{
//
// User wished to abort now
//
m_bCanceled = TRUE;
FreeContributions (Contrib); // Free contributions structure
return;
}
}
ScaleRow ( pSrc,
uSrcWidth,
uSrcWidthBytes,
pDst,
uResWidth,
uResWidthBytes,
u,
Contrib); // Scale each row
}
FreeContributions (Contrib); // Free contributions structure
}
template <class FilterClass>
void
C2PassScale<FilterClass>::
ScaleCol ( UCHAR *pSrc,
UINT uSrcWidth,
UINT uSrcWidthBytes,
UCHAR *pRes,
UINT uResWidth,
UINT uResWidthBytes,
UINT uResHeight,
UINT uCol,
LineContribType *Contrib)
{
UCHAR *pSrcLoc;
UCHAR *pDstLoc;
unsigned int vals[PS_MAX_DEPTH];
// assumes same height
for (UINT y = 0; y < uResHeight; y++)
{ // Loop through column
int v, i;
for (v= 0; v < m_byteDepth; v++)
vals[v] = 0;
int iLeft = Contrib->ContribRow[y].Left; // Retrieve left boundries
int iRight = Contrib->ContribRow[y].Right; // Retrieve right boundries
pSrcLoc = pSrc + uSrcWidthBytes*iLeft + uCol* m_byteDepth;
for (i = iLeft; i <= iRight; i++)
{ // Scan between boundries
// Accumulate weighted effect of each neighboring pixel
UCHAR *pCurSrc = pSrc + uSrcWidthBytes*i + uCol* m_byteDepth;
#ifdef _DEBUG
ASSERT(i-iLeft < (int)Contrib->WindowSize);
#endif
for (v= 0; v < m_byteDepth; v++)
vals[v] += Contrib->ContribRow[y].Weights[i-iLeft] * pSrcLoc[v];
pSrcLoc += uSrcWidthBytes;
}
pDstLoc = pRes + (y * uResWidthBytes) + uCol*m_byteDepth;
for (v= 0; v < m_byteDepth; v++)
{
// scale back
*pDstLoc++ = BOUND( vals[v] >> 16, 0, 0xff); // Place result in destination pixel
}
}
}
template <class FilterClass>
void
C2PassScale<FilterClass>::
VertScale ( UCHAR *pSrc,
UINT uSrcWidth,
UINT uSrcWidthBytes,
UINT uSrcHeight,
UCHAR *pDst,
UINT uResHeight)
{
// TRACE ("Performing vertical scaling...");
// assumes widths are the same!
if (uSrcHeight == uResHeight)
{ // No scaling required, just copy
if (uSrcHeight <= 0) return;
int copy = ((uSrcHeight -1) * uSrcWidthBytes) + uSrcWidth*m_byteDepth; // avoids overrun if starting in middle of image.
memcpy (pDst, pSrc, copy);
return;
}
// Allocate and calculate the contributions
LineContribType * Contrib = CalcContributions (uResHeight, uSrcHeight, double(uResHeight) / double(uSrcHeight));
for (UINT u = 0; u < uSrcWidth; u++)
{ // Step through columns
if (NULL != m_Callback)
{
//
// Progress and report callback supplied
//
if (!m_Callback (BYTE(double(u) / double (uSrcWidth) * 50.0) + 50))
{
//
// User wished to abort now
//
m_bCanceled = TRUE;
FreeContributions (Contrib); // Free contributions structure
return;
}
}
ScaleCol ( pSrc,
uSrcWidth,
uSrcWidthBytes,
pDst,
uSrcWidth,
uSrcWidthBytes,
uResHeight,
u,
Contrib); // Scale each column
}
FreeContributions (Contrib); // Free contributions structure
}
template <class FilterClass>
UCHAR *
C2PassScale<FilterClass>::
AllocAndScale (
UCHAR *pOrigImage, // A pointer to the source bitmap bits
UINT pixelBytes, // The size of a single pixel in bytes (both source and scaled image)
UINT uOrigWidth, // The width of a line of the source image to scale in pixels
UINT uOrigWidthBytes, // The width of a single line of the source image in bytes (to allow for padding, etc.)
UINT uOrigHeight, // The height of the source image to scale in pixels.
UINT uNewWidth, // The desired width of a line of the scaled image in pixels
UINT uNewWidthBytes, // The width of a single line of the scaled image in bytes (to allow for padding, etc.)
UINT uNewHeight) // The desired height of the scaled image in pixels.
{
// Scale source image horizontally into temporary image
m_byteDepth = pixelBytes;
m_bCanceled = FALSE;
UCHAR *pTemp = new UCHAR [uNewWidthBytes * uOrigHeight];
HorizScale (pOrigImage,
uOrigWidth,
uOrigWidthBytes,
uOrigHeight,
pTemp,
uNewWidth,
uNewWidthBytes);
if (m_bCanceled)
{
delete [] pTemp;
return NULL;
}
// Scale temporary image vertically into result image
UCHAR *pRes = new UCHAR [uNewWidth * uNewHeight *m_byteDepth];
VertScale ( pTemp,
uNewWidth,
uNewWidthBytes,
uOrigHeight,
pRes,
uNewHeight);
if (m_bCanceled)
{
delete [] pTemp;
delete [] pRes;
return NULL;
}
delete [] pTemp;
return pRes;
}
template <class FilterClass>
UCHAR *
C2PassScale<FilterClass>::
Scale (
UCHAR *pOrigImage, // A pointer to the source bitmap bits
UINT pixelBytes, // The size of a single pixel in bytes (both source and scaled image)
UINT uOrigWidth, // The width of a line of the source image to scale in pixels
UINT uOrigWidthBytes, // The width of a single line of the source image in bytes (to allow for padding, etc.)
UINT uOrigHeight, // The height of the source image to scale in pixels.
UCHAR *pDstImage, // A pointer to a buffer to hold the ecaled image
UINT uNewWidth, // The desired width of a line of the scaled image in pixels
UINT uNewWidthBytes, // The width of a single line of the scaled image in bytes (to allow for padding, etc.)
UINT uNewHeight) // The desired height of the scaled image in pixels.
{
// Scale source image horizontally into temporary image
ASSERT(PS_MAX_DEPTH >= pixelBytes);
m_byteDepth = pixelBytes;
m_bCanceled = FALSE;
UCHAR *pTemp = new UCHAR [ uOrigHeight *uNewWidthBytes];
HorizScale (pOrigImage,
uOrigWidth,
uOrigWidthBytes,
uOrigHeight,
pTemp,
uNewWidth,
uNewWidthBytes);
if (m_bCanceled)
{
delete [] pTemp;
return NULL;
}
// Scale temporary image vertically into result image
VertScale ( pTemp,
uNewWidth,
uNewWidthBytes,
uOrigHeight,
pDstImage,
uNewHeight);
delete [] pTemp;
if (m_bCanceled)
{
return NULL;
}
return pDstImage;
}
|
|
|
|
|
Where is the "macros.h" header file?
It is impossible to compile your file under MSVC.NET 2003 because BOUND isn't defined and so on.
|
|
|
|
|
I have fixed the original post.
The only item you need from macros.h is
#define BOUND(x,a,b) \
(((x) <= (a)) ? (a) : (((x) > (b)) ? (b) : (x)))
|
|
|
|
|
Thanks
|
|
|
|
|
In the below code you are using dibSrc object. Which class is this? I don't see it in this project. Can you give the URL from where I can download the dibSrc class?
Because I don't know to calculate WidthBytes that is passed to AllocAndScale function
Thanks.
<br />
-- modified at 13:35 Saturday 13th May, 2006
|
|
|
|
|
I believe that dibSrc is only used in the example code in the comments, therefore it should be understood to be incomplete. dibSrc in this case is not a class, but an instance of an image class that is not defined in this project. There is a comment on the WidthBytes that you removed: "The width of a single line of the source image in bytes (to allow for padding, etc.)". If this is not enough explanation, then I will try again. "WidthBytes" (uOrigWidthBytes and uNewWidthBytes in the code) is the number of bytes between lines of the image, so if line 0 starts at address 0x1000, and line 1 starts at 0x1321, then "WidthBytes" would be 0x0321. If your image is tightly packed, with no memory gap between lines, the WidthBytes is simply the "width in pixels" * "size of a single pixel in bytes". Hope this helps.
|
|
|
|
|
I am having a problem while scaling an image 800 x 600 to a size of 75 x 75.
It works fine for 76 x 76. It throws access violation error in following method.
template <class FilterClass><br />
void<br />
C2PassScale<FilterClass>::<br />
ScaleCol
Can you elaborate why this is happening?
|
|
|
|
|
In my experience working with images and whatnot, image dimensions that are a multiple of 4 seem to be much more agreeable. Try making sure that your dimensions are multiples of 4.
|
|
|
|
|
Yes, I tried out what u suggested. The width of the image must be multiple of 4 in order for the algorithm to work.
|
|
|
|
|
I do strongly advise not to use this code for 24bit bitmaps – there are bugs all over.
The destination bitmap is VERY much smoothed, it’s is shifted 1 pixel upwards and 1 pixel to the right, it produces wrong colours (for example a grey bitmap (128) is turned to 126; very bright values are turned to about 2 values too low).
I didn’t test on other than 24bit bitmaps, but wouldn’t be surprised if it’s the same there. As the code contains some strange lines (for example int ... – 1 / 2, or „caused crash ...“).
To me this is typical example how openly disposed code should not be: Only very bad tested, not maintained, ...
So, if you want my opinion: Don’t use this junk.
If you need 24bit resampling, StretchDIBits (in Mode HALFTONE) does a good job. For example: www.christian-etter.de/?p=283
|
|
|
|
|
I use the code like this,but can't work,may some good guy give me an anwser?
void CDib::ScaleTo (DWORD dwNewWidth, DWORD dwNewHeight)
{
LPBYTE lpDIB = (LPBYTE)GlobalLock(m_hDib);
if (! lpDIB)
{
GlobalUnlock(m_hDib);
return;
}
unsigned long *lpData = (unsigned long*)FindDIBBits(lpDIB);
C2PassScale <cbilinearfilter> ScaleEngine;
COLORREF *pOldBitmap = lpData;
lpData = ScaleEngine.AllocAndScale(lpData,
(LONG)DIBWidth(lpDIB),
(LONG)DIBHeight(lpDIB),
dwNewWidth,
dwNewHeight
);
if (NULL == lpData)
{
AfxMessageBox("缩放错误");
}
GlobalUnlock(m_hDib);
UpdateInternal();
delete pOldBitmap;
}
|
|
|
|
|
thank u for ur filters.
i use those filters in my project, now i need write a particular report , but i dont know about those arithmetics clearly. can u send some information about that to me , thank u
|
|
|
|
|
This is something that others can definitely build on, ... in a variety of ways, starting by adding your own customized derived filter class!
Thanks for sharing this valuable piece of work with the community.
It easily got my '5'.
William
Fortes in fide et opere!
|
|
|
|
|
You great work "two pass filter on image rotation" is very excellent.
I not only want to use your lib, but also want to know the algorithm behind it.
May you write the white paper about the algorithm and upload it to here.
Thanks!
|
|
|
|
|
Here is a link to start:
http://www.worldserver.com/turk/computergraphics/ResamplingFilters.pdf
|
|
|
|
|
There is also more theoritical information available at this link:
http://www.dspguide.com/ch16.htm
|
|
|
|
|
I've tried this, and it visually it works great!
Sadly, however it is too slow for my needs. I'd like a scaling procedure that is better visually than StretchBlt or DrawDIBDraw, but faster than 2PassScale. I does not to be as good visually as 2PassScale.
Anyone know of / got any such code please? Perhaps just some pointers?
Thanks
Jeremy Davis
http://www.astad.org
|
|
|
|
|
I think there's much room for speed improvement:
- using float arithmetics.
- building the row sub / column sum as floats, and do the conversion to BYTE after the loop
- we can multiply the entire COLORREF with the weight (roundoff might cause some overflow in the next byte, though, should be checked)
- alternately, we can use SIMD instructions to speed it up
"Vierteile den, der sie Hure schimpft mit einem türkischen Säbel."
mlog || Agile Programming | doxygen
|
|
|
|
|
How to use this function with gray scale bitmap
BYTE *m_pBits; //m_pBits point to color data in Picture
//in this article use COLORREF *m_pBits
|
|
|
|
|
can you give me a sample code?
|
|
|
|
|