/****************************************************************************\ * * * ENGINE8.CPP - Source Code Module * * * * (c)Copyright 2001 Indotek, LLC. All Rights Reserved. * * * \****************************************************************************/ #include "internal.h" // Clamp the input to the specified range #define _R3D_CLAMP(v,l,h) ((v)<(l) ? (l) : (v) > (h) ? (h) : v) typedef unsigned char _r3d_Pixel; typedef struct { int Width; // horizontal size of the image in Pixels int Height; // vertical size of the image in Pixels _r3d_Pixel *data; // pointer to first scanline of image int pitch; // byte offset between two scanlines } _r3d_Image; _r3d_Image _r3d_Src, _r3d_DstR, _r3d_DstG, _r3d_DstB, _r3d_DstA; #define _R3D_WHITE_PIXEL (255) #define _R3D_BLACK_PIXEL (0) typedef struct { int pixel; float weight; } _R3D_CONTRIB; typedef struct { int n; // number of contributors _R3D_CONTRIB *p; // pointer to list of contributions } _R3D_CLIST; _R3D_CLIST *_r3d_contrib; // array of contribution lists #define _R3D_FILTER_SUPPORT (1.0F) #define _R3D_BOX_SUPPORT (0.5F) #define _R3D_TRIANGLE_SUPPORT (1.0F) #define _R3D_BELL_SUPPORT (1.5F) #define _R3D_B_SPLINE_SUPPORT (2.0F) #define _R3D_LANCZOS3_SUPPORT (3.0F) #define _R3D_MITCHELL_SUPPORT (2.0F) inline _r3d_Pixel get_pixel(_r3d_Image *image, int x, int y) { static _r3d_Image *im = NULL; static int yy = -1; static _r3d_Pixel *p = NULL; if ((x < 0) || (x >= image->Width) || (y < 0) || (y >= image->Height)) { return (0); } if ((im != image) || (yy != y)) { im = image; yy = y; p = image->data + (y * image->pitch); } return (p[x]); } inline void get_row(_r3d_Pixel *row, _r3d_Image *image, int y) { if ((y < 0) || (y >= image->Height)) { return; } memcpy(row, image->data + (y * image->pitch), (sizeof(_r3d_Pixel) * image->Width)); } inline void get_column(_r3d_Pixel *column, _r3d_Image *image, int x) { int i, d; _r3d_Pixel *p; if ((x < 0) || (x >= image->Width)) { return; } d = image->pitch; for (i = image->Height, p = image->data + x;i-- > 0;p += d) { *column++ = *p; } } inline _r3d_Pixel put_pixel(_r3d_Image *image, int x, int y, _r3d_Pixel data) { static _r3d_Image *im = NULL; static int yy = -1; static _r3d_Pixel *p = NULL; if ((x < 0) || (x >= image->Width) || (y < 0) || (y >= image->Height)) { return (0); } if ((im != image) || (yy != y)) { im = image; yy = y; p = image->data + (y * image->pitch); } return (p[x] = data); } inline float filter(float t) { if (t < 0.0F) { t = -t; } if (t < 1.0F) { return ((2.0F * t - 3.0F) * t * t + 1.0F); } return (0.0F); } inline float box_filter(float t) { if ((t > -0.5F) && (t <= 0.5F)) { return (1.0F); } return (0.0F); } inline float triangle_filter(float t) { if (t < 0.0F) { t = -t; } if (t < 1.0F) { return (1.0F - t); } return (0.0F); } inline float bell_filter(float t) { if (t < 0.0F) { t = -t; } if (t < 0.5F) { return (0.75F - (t * t)); } if (t < 1.5F) { t = (t - 1.5F); return (0.5F * (t * t)); } return (0.0F); } inline float B_spline_filter(float t) { float tt; if (t < 0.0F) { t = -t; } if (t < 1.0F) { tt = t * t; return ((0.5F * tt * t) - tt + (2.0F / 3.0F)); } else { if (t < 2.0F) { t = 2.0F - t; return ((1.0F / 6.0F) * (t * t * t)); } } return (0.0F); } inline float sinc(float x) { x *= 3.1415926535897932384626433832795F; if (x != 0.0F) { return ((float)sin((double)x) / x); } return (1.0F); } inline float Lanczos3_filter(float t) { if (t < 0.0F) { t = -t; } if (t < 3.0F) { return (sinc(t) * sinc(t / 3.0F)); } return (0.0F); } inline float Mitchell_filter(float t) { if (t < 0.0F) { t = -t; } if (t < 1.0F) { t = (((7.0F) * (t * t * t)) + ((-12.0F) * t * t) + (5.333333333333333333333333333333333F)); return (t / 6.0F); } else { if (t < 2.0F) { t = (((-2.333333333333333333333333333333333F) * (t * t * t)) + ((12.0F) * t * t) + ((-20.0F) * t) + (10.666666666666666666666666666666666F)); return (t / 6.0F); } } return (0.0F); } BOOL _r3d_Zoom(_r3d_Image *dst, _r3d_Image *src) { float fwidth; _r3d_Image tmp; // intermediate image float xscale, yscale; // zoom scale factors int i, j, k; // loop variables int n; // pixel number float center, left, right; // filter calculation variables float width, fscale, weight; // filter calculation variables _r3d_Pixel *raster; // a row or column of pixels int LoopVar; // create intermediate image to hold horizontal zoom tmp.Width = tmp.pitch = dst->Width; tmp.Height = src->Height; if (NULL == (tmp.data = new unsigned char[tmp.Width * tmp.Height])) { return FALSE; } xscale = (float)dst->Width / (float)src->Width; yscale = (float)dst->Height / (float)src->Height; // pre-calculate filter contributions for a row if (NULL == (_r3d_contrib = new _R3D_CLIST[dst->Width])) { _R3D_DELETE(tmp.data); return FALSE; } if (xscale < 1.0F) { // _R3D_LANCZOS3_SUPPORT, _R3D_MITCHELL_SUPPORT fwidth = _R3D_LANCZOS3_SUPPORT; width = fwidth / xscale; fscale = 1.0F / xscale; for (i = 0;i < dst->Width;++i) { _r3d_contrib[i].n = 0; if (NULL == (_r3d_contrib[i].p = new _R3D_CONTRIB[(int)(width * 2+1)])) { for (LoopVar = i - 1;LoopVar >= 0;LoopVar--) _R3D_DELETE(_r3d_contrib[LoopVar].p); _R3D_DELETE(_r3d_contrib); _R3D_DELETE(tmp.data); return FALSE; } center = (float)i / xscale; left = (float)ceil(center - width); right = (float)floor(center + width); for (j = (int)left;j <= (int)right;++j) { weight = center - (float)j; // Scaling up, use Mitchell_filter(). Scaling down, use Lanczos3_filter() weight = Lanczos3_filter(weight / fscale) / fscale; if (j < 0) { n = -j; } else { if (j >= src->Width) { n = (src->Width - j) + src->Width - 1; } else { n = j; } } k = _r3d_contrib[i].n++; _r3d_contrib[i].p[k].pixel = n; _r3d_contrib[i].p[k].weight = weight; } } } else { // _R3D_LANCZOS3_SUPPORT, _R3D_MITCHELL_SUPPORT fwidth = _R3D_MITCHELL_SUPPORT; for (i = 0;i < dst->Width;++i) { _r3d_contrib[i].n = 0; //_r3d_contrib[i].p = (_R3D_CONTRIB *)calloc((int)(fwidth * 2 + 1),sizeof(_R3D_CONTRIB)); if (NULL == (_r3d_contrib[i].p = new _R3D_CONTRIB[(int)(fwidth * 2+1)])) { for (LoopVar = i - 1;LoopVar >= 0;LoopVar--) _R3D_DELETE(_r3d_contrib[LoopVar].p); _R3D_DELETE(_r3d_contrib); _R3D_DELETE(tmp.data); return FALSE; } center = (float)i / xscale; left = (float)ceil(center - fwidth); right = (float)floor(center + fwidth); for (j = (int)left;j <= (int)right;++j) { weight = center - (float)j; // Scaling up, use Mitchell_filter(). Scaling down, use Lanczos3_filter() weight = Mitchell_filter(weight); if (j < 0) { n = -j; } else { if (j >= src->Width) { n = (src->Width - j) + src->Width - 1; } else { n = j; } } k = _r3d_contrib[i].n++; _r3d_contrib[i].p[k].pixel = n; _r3d_contrib[i].p[k].weight = weight; } } } // apply filter to zoom horizontally from src to tmp if (NULL == (raster = new _r3d_Pixel[src->Width])) { for (i = 0;i < tmp.Width;++i) _R3D_DELETE(_r3d_contrib[i].p); _R3D_DELETE(_r3d_contrib); _R3D_DELETE(tmp.data); return FALSE; } for (k = 0;k < tmp.Height;++k) { get_row(raster, src, k); for (i = 0;i < tmp.Width;++i) { weight = 0.0; for (j = 0;j < _r3d_contrib[i].n;++j) { weight += raster[_r3d_contrib[i].p[j].pixel] * _r3d_contrib[i].p[j].weight; } put_pixel(&tmp, i, k, (_r3d_Pixel)_R3D_CLAMP(weight, _R3D_BLACK_PIXEL, _R3D_WHITE_PIXEL)); } } _R3D_DELETE(raster); // free the memory allocated for horizontal filter weights for (i = 0;i < dst->Width;++i) { _R3D_DELETE(_r3d_contrib[i].p); } _R3D_DELETE(_r3d_contrib); // pre-calculate filter contributions for a column if (NULL == (_r3d_contrib = new _R3D_CLIST[dst->Height])) { _R3D_DELETE(tmp.data); return FALSE; } if (yscale < 1.0) { // _R3D_LANCZOS3_SUPPORT, _R3D_MITCHELL_SUPPORT fwidth = _R3D_LANCZOS3_SUPPORT; width = fwidth / yscale; fscale = 1.0F / yscale; for (i = 0;i < dst->Height;++i) { _r3d_contrib[i].n = 0; // _r3d_contrib[i].p = (_R3D_CONTRIB *)calloc((int)(width * 2 + 1),sizeof(_R3D_CONTRIB)); if (NULL == (_r3d_contrib[i].p = new _R3D_CONTRIB[(int)(width * 2+1)])) { for (LoopVar = i - 1;LoopVar >= 0;LoopVar--) _R3D_DELETE(_r3d_contrib[LoopVar].p); _R3D_DELETE(_r3d_contrib); _R3D_DELETE(tmp.data); return FALSE; } center = (float)i / yscale; left = (float)ceil(center - width); right = (float)floor(center + width); for (j = (int)left;j <= (int)right;++j) { weight = center - (float)j; // Scaling up, use Mitchell_filter(). Scaling down, use Lanczos3_filter() weight = Lanczos3_filter(weight / fscale) / fscale; if (j < 0) { n = -j; } else { if (j >= tmp.Height) { n = (tmp.Height - j) + tmp.Height - 1; } else { n = j; } } k = _r3d_contrib[i].n++; _r3d_contrib[i].p[k].pixel = n; _r3d_contrib[i].p[k].weight = weight; } } } else { // _R3D_LANCZOS3_SUPPORT, _R3D_MITCHELL_SUPPORT fwidth = _R3D_MITCHELL_SUPPORT; for (i = 0;i < dst->Height;++i) { _r3d_contrib[i].n = 0; if (NULL == (_r3d_contrib[i].p = new _R3D_CONTRIB[(int)(fwidth * 2+1)])) { for (LoopVar = i - 1;LoopVar >= 0;LoopVar--) _R3D_DELETE(_r3d_contrib[LoopVar].p); _R3D_DELETE(_r3d_contrib); _R3D_DELETE(tmp.data); return FALSE; } center = (float)i / yscale; left = (float)ceil(center - fwidth); right = (float)floor(center + fwidth); for (j = (int)left;j <= (int)right;++j) { weight = center - (float)j; // Scaling up, use Mitchell_filter(). Scaling down, use Lanczos3_filter() weight = Mitchell_filter(weight); if (j < 0) { n = -j; } else { if (j >= tmp.Height) { n = (tmp.Height - j) + tmp.Height - 1; } else { n = j; } } k = _r3d_contrib[i].n++; _r3d_contrib[i].p[k].pixel = n; _r3d_contrib[i].p[k].weight = weight; } } } // apply filter to zoom vertically from tmp to dst if (NULL == (raster = new _r3d_Pixel[tmp.Height * sizeof(_r3d_Pixel)])) { for (i = 0;i < dst->Height;++i) _R3D_DELETE(_r3d_contrib[i].p); _R3D_DELETE(_r3d_contrib); _R3D_DELETE(tmp.data); return FALSE; } for (k = 0;k < dst->Width;++k) { get_column(raster, &tmp, k); for (i = 0;i < dst->Height;++i) { weight = 0.0; for (j = 0;j < _r3d_contrib[i].n;++j) { weight += raster[_r3d_contrib[i].p[j].pixel] * _r3d_contrib[i].p[j].weight; } put_pixel(dst, k, i, (_r3d_Pixel)_R3D_CLAMP(weight, _R3D_BLACK_PIXEL, _R3D_WHITE_PIXEL)); } } _R3D_DELETE(raster); // free the memory allocated for vertical filter weights for (i = 0;i < dst->Height;++i) { _R3D_DELETE(_r3d_contrib[i].p); } _R3D_DELETE(_r3d_contrib); _R3D_DELETE(tmp.data); return TRUE; } BOOL R3D_STDCALL r3d_SurfaceStretchSmooth(int hSrc, int hDst) { unsigned long dwColor; unsigned long *SrcRGBA, *DstRGBA; int LoopVar; // Allocate 8 new unsigned char arrays if (NULL == (_r3d_Src.data = new unsigned char[_r3d_Surface[hSrc]->dwWidth * _r3d_Surface[hSrc]->dwHeight])) { _r3d_FormulateErrorString("_r3d_SmoothStretch", "Can not allocate memory", DD_OK); return FALSE; } if (NULL == (_r3d_DstR.data = new unsigned char[_r3d_Surface[hDst]->dwWidth * _r3d_Surface[hDst]->dwHeight])) { _R3D_DELETE(_r3d_Src.data); _r3d_FormulateErrorString("_r3d_SmoothStretch", "Can not allocate memory", DD_OK); return FALSE; } if (NULL == (_r3d_DstG.data = new unsigned char[_r3d_Surface[hDst]->dwWidth * _r3d_Surface[hDst]->dwHeight])) { _R3D_DELETE(_r3d_Src.data); _R3D_DELETE(_r3d_DstR.data); _r3d_FormulateErrorString("_r3d_SmoothStretch", "Can not allocate memory", DD_OK); return FALSE; } if (NULL == (_r3d_DstB.data = new unsigned char[_r3d_Surface[hDst]->dwWidth * _r3d_Surface[hDst]->dwHeight])) { _R3D_DELETE(_r3d_Src.data); _R3D_DELETE(_r3d_DstR.data); _R3D_DELETE(_r3d_DstG.data); _r3d_FormulateErrorString("_r3d_SmoothStretch", "Can not allocate memory", DD_OK); return FALSE; } if (NULL == (_r3d_DstA.data = new unsigned char[_r3d_Surface[hDst]->dwWidth * _r3d_Surface[hDst]->dwHeight])) { _R3D_DELETE(_r3d_Src.data); _R3D_DELETE(_r3d_DstR.data); _R3D_DELETE(_r3d_DstG.data); _R3D_DELETE(_r3d_DstB.data); _r3d_FormulateErrorString("_r3d_SmoothStretch", "Can not allocate memory", DD_OK); return FALSE; } // Assign the information to the Image structures // 1. Assign Width, Height and pitch _r3d_Src.Width = _r3d_Src.pitch = _r3d_Surface[hSrc]->dwWidth; _r3d_Src.Height = _r3d_Surface[hSrc]->dwHeight; _r3d_DstR.Width = _r3d_DstR.pitch = _r3d_Surface[hDst]->dwWidth; _r3d_DstR.Height = _r3d_Surface[hDst]->dwHeight; _r3d_DstG.Width = _r3d_DstG.pitch = _r3d_Surface[hDst]->dwWidth; _r3d_DstG.Height = _r3d_Surface[hDst]->dwHeight; _r3d_DstB.Width = _r3d_DstB.pitch = _r3d_Surface[hDst]->dwWidth; _r3d_DstB.Height = _r3d_Surface[hDst]->dwHeight; _r3d_DstA.Width = _r3d_DstA.pitch = _r3d_Surface[hDst]->dwWidth; _r3d_DstA.Height = _r3d_Surface[hDst]->dwHeight; // 2. Assign Src color components to Image data SrcRGBA = (unsigned long *)r3d_SurfaceGet(hSrc); // Scale Image with filter for Red for (LoopVar = 0;LoopVar < (int)(_r3d_Surface[hSrc]->dwWidth * _r3d_Surface[hSrc]->dwHeight);LoopVar++) { dwColor = SrcRGBA[LoopVar]; _r3d_Src.data[LoopVar] = r3d_SurfaceColorGetR(dwColor); } if (!_r3d_Zoom(&_r3d_DstR, &_r3d_Src)) { _R3D_DELETE(_r3d_Src.data); _R3D_DELETE(_r3d_DstR.data); _R3D_DELETE(_r3d_DstG.data); _R3D_DELETE(_r3d_DstB.data); _R3D_DELETE(_r3d_DstA.data); _r3d_FormulateErrorString("_r3d_SmoothStretch", "_r3d_Zoom", DD_OK); return FALSE; } // Scale Image with filter for Green for (LoopVar = 0;LoopVar < (int)(_r3d_Surface[hSrc]->dwWidth * _r3d_Surface[hSrc]->dwHeight);LoopVar++) { dwColor = SrcRGBA[LoopVar]; _r3d_Src.data[LoopVar] = r3d_SurfaceColorGetG(dwColor); } if (!_r3d_Zoom(&_r3d_DstG, &_r3d_Src)) { _R3D_DELETE(_r3d_Src.data); _R3D_DELETE(_r3d_DstR.data); _R3D_DELETE(_r3d_DstG.data); _R3D_DELETE(_r3d_DstB.data); _R3D_DELETE(_r3d_DstA.data); _r3d_FormulateErrorString("_r3d_SmoothStretch", "_r3d_Zoom", DD_OK); return FALSE; } // Scale Image with filter for Blue for (LoopVar = 0;LoopVar < (int)(_r3d_Surface[hSrc]->dwWidth * _r3d_Surface[hSrc]->dwHeight);LoopVar++) { dwColor = SrcRGBA[LoopVar]; _r3d_Src.data[LoopVar] = r3d_SurfaceColorGetB(dwColor); } if (!_r3d_Zoom(&_r3d_DstB, &_r3d_Src)) { _R3D_DELETE(_r3d_Src.data); _R3D_DELETE(_r3d_DstR.data); _R3D_DELETE(_r3d_DstG.data); _R3D_DELETE(_r3d_DstB.data); _R3D_DELETE(_r3d_DstA.data); _r3d_FormulateErrorString("_r3d_SmoothStretch", "_r3d_Zoom", DD_OK); return FALSE; } // Scale Image with filter for Alpha for (LoopVar = 0;LoopVar < (int)(_r3d_Surface[hSrc]->dwWidth * _r3d_Surface[hSrc]->dwHeight);LoopVar++) { dwColor = SrcRGBA[LoopVar]; _r3d_Src.data[LoopVar] = r3d_SurfaceColorGetA(dwColor); } if (!_r3d_Zoom(&_r3d_DstA, &_r3d_Src)) { _R3D_DELETE(_r3d_Src.data); _R3D_DELETE(_r3d_DstR.data); _R3D_DELETE(_r3d_DstG.data); _R3D_DELETE(_r3d_DstB.data); _R3D_DELETE(_r3d_DstA.data); _r3d_FormulateErrorString("_r3d_SmoothStretch", "_r3d_Zoom", DD_OK); return FALSE; } // Now reassign it to hDst DstRGBA = (unsigned long *)r3d_SurfaceGet(hDst); for (LoopVar = 0;LoopVar < (int)(_r3d_Surface[hDst]->dwWidth * _r3d_Surface[hDst]->dwHeight);LoopVar++) { dwColor = r3d_SurfaceColorMakeRGBA(_r3d_DstR.data[LoopVar], _r3d_DstG.data[LoopVar], _r3d_DstB.data[LoopVar], _r3d_DstA.data[LoopVar]); DstRGBA[LoopVar] = dwColor; } // Free Resources _R3D_DELETE(_r3d_Src.data); _R3D_DELETE(_r3d_DstR.data); _R3D_DELETE(_r3d_DstG.data); _R3D_DELETE(_r3d_DstB.data); _R3D_DELETE(_r3d_DstA.data); return TRUE; }