www.pudn.com > RotateByShear_demo.zip > RotateByShear.h


#ifndef _ROTATE_BY_SHEAR_H_ 
#define _ROTATE_BY_SHEAR_H_ 
 
#include  
 
#ifndef _PROGRESS_AND_ABORT_CALL_BACK 
#define _PROGRESS_AND_ABORT_CALL_BACK 
 
typedef BOOL (*ProgressAnbAbortCallBack)(BYTE bPercentComplete); 
 
#endif  // _PROGRESS_AND_ABORT_CALL_BACK 
 
template  
class CRotateByShear 
{ 
public: 
 
    CRotateByShear (ProgressAnbAbortCallBack callback = NULL) :  
        m_Callback (callback) {} 
 
    virtual ~CRotateByShear() {} 
 
    CPxl * AllocAndRotate (   
        CPxl   *pSrc,           // Pointer to source image 
        SIZE        sSrc,       // Size of source image 
        double      dAngle,     // Rotation angle 
        SIZE       *psDst,      // Pointer to destination image size 
        COLORREF    clrBack);   // Background color 
 
protected: 
 
    // 
    // You must override these 5 function in the derived class and implement  
    // specific pixel access functions according to the bitmap type you use. 
    // 
 
        // Get RGB value at given pixel coordinates 
    virtual COLORREF GetRGB (CPxl *pImage,      // Pointer to image 
                             SIZE  sImage,      // Size of image 
                             UINT  x,           // X coordinate 
                             UINT  y            // Y coordinate 
                            ) = 0; 
 
        // Set RGB value at given pixel coordinates 
    virtual void     SetRGB (CPxl *pImage,   // Pointer to image 
                             SIZE  sImage,   // Size of image 
                             UINT  x,        // X coordinate 
                             UINT  y,        // Y coordinate 
                             CPxl  clr       // New color to set 
                            ) = 0; 
 
        // Create a new bitmap, given a bitmap dimensions 
    virtual CPxl *CreateNewBitmap (SIZE  sImage // Size of image 
                            ) = 0; 
 
        // Create a new bitmap which is an identical copy of the source bitmap 
    virtual CPxl *CopyBitmap (CPxl *pImage,     // Pointer to source image 
                              SIZE  sImage      // Size of source (and destination) image 
                             ) = 0; 
 
        // Destroy a bitmap previously created in call to CreateNewBitmap(..) 
        // or to CopyBitmap (...) 
    virtual void DestroyBitmap (CPxl *pImage,   // Pointer to image 
                                SIZE  sImage    // Size of image 
                               ) = 0; 
 
private: 
 
    ProgressAnbAbortCallBack    m_Callback; 
 
    void HorizSkew (CPxl *pSrc,  
                    SIZE sSrc, 
                    CPxl *pDst,  
                    SIZE sDst, 
                    UINT uRow,  
                    int iOffset,  
                    double dWeight, 
                    COLORREF clrBack); 
 
    void VertSkew ( CPxl *pSrc,  
                    SIZE sSrc, 
                    CPxl *pDst,  
                    SIZE sDst, 
                    UINT uCol, 
                    int iOffset, 
                    double dWeight, 
                    COLORREF clrBack); 
 
    CPxl * Rotate90  (CPxl *pSrc, SIZE sSrc, SIZE *psDst); 
    CPxl * Rotate180 (CPxl *pSrc, SIZE sSrc, SIZE *psDst); 
    CPxl * Rotate270 (CPxl *pSrc, SIZE sSrc, SIZE *psDst); 
 
    CPxl * Rotate45 ( 
        CPxl *pSrc,  
        SIZE sSrc, 
        SIZE *psDst, 
        double dAngle, 
        COLORREF clrBack, 
        BOOL bMidImage); 
};  // CRotateByShear 
 
 
#ifdef ROTATE_PI 
#undef ROTATE_PI 
#endif // ROTATE_PI 
 
#define ROTATE_PI  double (3.1415926535897932384626433832795) 
 
 
template  
void  
CRotateByShear::HorizSkew ( 
    CPxl *pSrc,  
    SIZE sSrc, 
    CPxl *pDst,  
    SIZE sDst, 
    UINT uRow,  
    int iOffset,  
    double dWeight, 
    COLORREF clrBack) 
/*------------------------------------------------------------------------------ 
 
  Function: HorizSkew 
 
  Purpose:  Skews a row horizontally (with filtered weights) 
 
  Input:    Image to skew (+ dimensions) 
            Destination of skewed image (+ dimensions) 
            Row index 
            Skew offset 
            Relative weight of right pixel 
            Background color 
 
  Output:   None. 
 
  Remarks:  Limited to 45 degree skewing only. Filters two adjacent pixels. 
 
------------------------------------------------------------------------------*/ 
{ 
    for (int i = 0; i < iOffset; i++) 
    { 
        // Fill gap left of skew with background 
        SetRGB (pDst, sDst, i, uRow, clrBack); 
    } 
 
    COLORREF pxlOldLeft = clrBack; 
    for (i = 0; i < sSrc.cx; i++)  
    { 
        // Loop through row pixels 
        COLORREF pxlSrc = GetRGB(pSrc, sSrc, i, uRow); 
        // Calculate weights 
        COLORREF pxlLeft = RGB (BYTE ( double (GetRValue (pxlSrc)) * dWeight ), 
                                BYTE ( double (GetGValue (pxlSrc)) * dWeight ), 
                                BYTE ( double (GetBValue (pxlSrc)) * dWeight )); 
        // Update left over on source 
        pxlSrc = RGB ( GetRValue (pxlSrc) - ( GetRValue (pxlLeft) - GetRValue (pxlOldLeft) ), 
                       GetGValue (pxlSrc) - ( GetGValue (pxlLeft) - GetGValue (pxlOldLeft) ), 
                       GetBValue (pxlSrc) - ( GetBValue (pxlLeft) - GetBValue (pxlOldLeft) )); 
        // Check boundries  
        if ((i + iOffset >= 0) && (i + iOffset < sDst.cx)) 
        { 
            SetRGB (pDst, sDst, i + iOffset, uRow, pxlSrc); 
        } 
        // Save leftover for next pixel in scan 
        pxlOldLeft = pxlLeft; 
    } 
    // Go to rightmost point of skew 
    i += iOffset;   
    if (i < sDst.cx) 
    { 
        // If still in image bounds, put leftovers there 
        SetRGB (pDst, sDst, i, uRow, pxlOldLeft); 
    } 
    while (++i < sDst.cx) 
    {   // Clear to the right of the skewed line with background 
        SetRGB (pDst, sDst, i, uRow, clrBack); 
    } 
}   // CRotateByShear::HorizSkew 
 
 
template  
void  
CRotateByShear::VertSkew ( 
    CPxl *pSrc,  
    SIZE sSrc, 
    CPxl *pDst,  
    SIZE sDst, 
    UINT uCol, 
    int iOffset, 
    double dWeight, 
    COLORREF clrBack) 
/*------------------------------------------------------------------------------ 
 
  Function: VertSkew 
 
  Purpose:  Skews a column vertically (with filtered weights) 
 
  Input:    Image to skew (+dimensions) 
            Destination of skewed image (+dimensions) 
            Column index 
            Skew offset 
            Relative weight of upper pixel 
            Background color 
 
  Output:   None. 
 
  Remarks:  Limited to 45 degree skewing only. Filters two adjacent pixels. 
 
------------------------------------------------------------------------------*/ 
{ 
    for (int i = 0; i < iOffset; i++) 
    { 
        // Fill gap above skew with background 
        SetRGB (pDst, sDst, uCol, i, clrBack); 
    } 
 
    COLORREF pxlOldLeft = clrBack; 
 
    int iYPos; 
    for (i = 0; i < sSrc.cy; i++)  
    { 
        // Loop through column pixels 
        COLORREF pxlSrc = GetRGB (pSrc, sSrc, uCol, i); 
        iYPos = i + iOffset; 
        // Calculate weights 
        COLORREF pxlLeft = RGB (BYTE ( double (GetRValue (pxlSrc)) * dWeight ), 
                                BYTE ( double (GetGValue (pxlSrc)) * dWeight ), 
                                BYTE ( double (GetBValue (pxlSrc)) * dWeight )); 
        // Update left over on source 
        pxlSrc = RGB ( GetRValue (pxlSrc) - ( GetRValue (pxlLeft) - GetRValue (pxlOldLeft) ), 
                       GetGValue (pxlSrc) - ( GetGValue (pxlLeft) - GetGValue (pxlOldLeft) ), 
                       GetBValue (pxlSrc) - ( GetBValue (pxlLeft) - GetBValue (pxlOldLeft) )); 
        // Check boundries 
        if ((iYPos >= 0) && (iYPos < sDst.cy)) 
        { 
            SetRGB (pDst, sDst, uCol, iYPos, pxlSrc); 
        } 
        // Save leftover for next pixel in scan 
        pxlOldLeft = pxlLeft; 
    } 
    // Go to bottom point of skew 
    i = iYPos;   
    if (i < sDst.cy) 
    { 
        // If still in image bounds, put leftovers there 
        SetRGB (pDst, sDst, uCol, i, pxlOldLeft); 
    } 
    while (++i < sDst.cy) 
    { 
        // Clear below skewed line with background 
        SetRGB (pDst, sDst, uCol, i, clrBack); 
    } 
}   // CRotateByShear::VertSkew 
 
 
template  
CPxl *  
CRotateByShear::Rotate90  (CPxl *pSrc, SIZE sSrc, SIZE *psDst) 
/*------------------------------------------------------------------------------ 
 
  Function: Rotate90 
 
  Purpose:  Rotates an image by 90 degrees (counter clockwise) 
 
  Input:    Image to rotate (+dimensions) 
            Pointer to destination size 
 
  Output:   Pointer to newly allocated rotated image 
 
  Remarks:  Precise rotation, no filters required. 
 
------------------------------------------------------------------------------*/ 
{ 
    (*psDst).cx = sSrc.cy; 
    (*psDst).cy = sSrc.cx; 
 
    CPxl *pDst = CreateNewBitmap (*psDst); 
    if (NULL == pDst) 
    { 
        return NULL; 
    } 
    for (UINT uY = 0; uY < UINT(sSrc.cy); uY++) 
    { 
        for (UINT uX = 0; uX < UINT(sSrc.cx); uX++) 
        { 
            SetRGB (pDst, *psDst, uY, (*psDst).cy - uX - 1, GetRGB (pSrc, sSrc, uX, uY)); 
        } 
        if (m_Callback) 
        { 
            // Report progress 
            if (!m_Callback (BYTE(double (uY) / double(sSrc.cy) * double(50.0)))) 
            { 
                // Operation cancelled 
                DestroyBitmap (pDst, *psDst); 
                return NULL; 
            } 
        } 
    } 
    return pDst; 
}   // CRotateByShear::Rotate90 
 
template  
CPxl *  
CRotateByShear::Rotate180  (CPxl *pSrc, SIZE sSrc, SIZE *psDst) 
/*------------------------------------------------------------------------------ 
 
  Function: Rotate180 
 
  Purpose:  Rotates an image by 180 degrees (counter clockwise) 
 
  Input:    Image to rotate (+dimensions) 
            Pointer to destination size 
 
  Output:   Pointer to newly allocated rotated image 
 
  Remarks:  Precise rotation, no filters required. 
 
------------------------------------------------------------------------------*/ 
{ 
    *psDst = sSrc; 
 
    CPxl *pDst = CreateNewBitmap (*psDst); 
    if (NULL == pDst) 
    { 
        return NULL; 
    } 
 
    for (UINT uY = 0; uY < UINT(sSrc.cy); uY++) 
    { 
        for (UINT uX = 0; uX < UINT(sSrc.cx); uX++) 
        { 
            SetRGB (pDst, *psDst, (*psDst).cx - uX - 1, (*psDst).cy - uY - 1, GetRGB (pSrc, sSrc, uX, uY)); 
        } 
        if (m_Callback) 
        { 
            // Report progress 
            if (!m_Callback (BYTE(double (uY) / double(sSrc.cy) * double(50.0)))) 
            { 
                // Operation cancelled 
                DestroyBitmap (pDst, *psDst); 
                return NULL; 
            } 
        } 
    } 
    return pDst; 
}   // CRotateByShear::Rotate180 
 
template  
CPxl *  
CRotateByShear::Rotate270  (CPxl *pSrc, SIZE sSrc, SIZE *psDst) 
/*------------------------------------------------------------------------------ 
 
  Function: Rotate270 
 
  Purpose:  Rotates an image by 270 degrees (counter clockwise) 
 
  Input:    Image to rotate (+dimensions) 
            Pointer to destination size 
 
  Output:   Pointer to newly allocated rotated image 
 
  Remarks:  Precise rotation, no filters required. 
 
------------------------------------------------------------------------------*/ 
{ 
    (*psDst).cx = sSrc.cy; 
    (*psDst).cy = sSrc.cx; 
 
    CPxl *pDst = CreateNewBitmap (*psDst); 
    if (NULL == pDst) 
    { 
        return NULL; 
    } 
 
    for (UINT uY = 0; uY < UINT(sSrc.cy); uY++) 
    { 
        for (UINT uX = 0; uX < UINT(sSrc.cx); uX++) 
        { 
            SetRGB (pDst, *psDst, (*psDst).cx - uY - 1, uX, GetRGB (pSrc, sSrc, uX, uY)); 
        } 
        if (m_Callback) 
        { 
            // Report progress 
            if (!m_Callback (BYTE(double (uY) / double(sSrc.cy) * double(50.0)))) 
            { 
                // Operation cancelled 
                DestroyBitmap (pDst, *psDst); 
                return NULL; 
            } 
        } 
    } 
    return pDst; 
}   // CRotateByShear::Rotate270 
 
template  
CPxl *  
CRotateByShear::Rotate45  ( 
    CPxl *pSrc,  
    SIZE sSrc, 
    SIZE *psDst, 
    double dAngle, 
    COLORREF clrBack, 
    BOOL bMidImage) 
/*------------------------------------------------------------------------------ 
 
  Function: Rotate45 
 
  Purpose:  Rotates an image by a given degree in range [-45.0 .. +45.0] 
            (counter clockwise) 
 
  Input:    Image to rotate (+dimensions) 
            Pointer to destination size 
            Degree of rotation 
            Background color 
            Was middle image used (for correct progress report) 
 
  Output:   Pointer to newly allocated rotated image 
 
  Remarks:  Using the 3-shear technique. 
 
------------------------------------------------------------------------------*/ 
{ 
    if (0.0 == dAngle) 
    { 
        // No rotation at all 
        (*psDst) = sSrc; 
        return CopyBitmap (pSrc, sSrc); 
    } 
 
    double dRadAngle = dAngle * ROTATE_PI / double(180); // Angle in radians 
    double dSinE = sin (dRadAngle); 
    double dTan = tan (dRadAngle / 2.0); 
 
    // Calc first shear (horizontal) destination image dimensions  
    SIZE sDst1; 
    sDst1.cx = sSrc.cx + int(double(sSrc.cy) * fabs(dTan)); 
    sDst1.cy = sSrc.cy; 
 
 
    /******* Perform 1st shear (horizontal) ******/ 
 
    // Allocate image for 1st shear 
    CPxl *pDst1 = CreateNewBitmap (sDst1); 
    if (NULL == pDst1) 
    { 
        return NULL; 
    } 
    for (UINT u = 0; u < UINT(sDst1.cy); u++)  
    {   
        double dShear; 
 
        if (dTan >= 0.0) 
        { 
            // Positive angle 
            dShear = (double(u) + 0.5) * dTan; 
        } 
        else 
        { 
            // Negative angle 
            dShear = (double (int(u) - sDst1.cy) + 0.5) * dTan; 
        } 
        int iShear = int (floor (dShear)); 
        HorizSkew ( pSrc,  
                    sSrc,  
                    pDst1,  
                    sDst1, 
                    u,  
                    iShear,  
                    dShear - double(iShear), 
                    clrBack); 
        if (m_Callback) 
        { 
            // Report progress 
            BYTE bProgress = bMidImage ? 50 + BYTE(double (u) / double(sDst1.cy) * double(50.0 / 3.0)) : 
                                         BYTE(double (u) / double(sDst1.cy) * double(33.33333)); 
            if (!m_Callback (bProgress)) 
            { 
                // Operation cancelled 
                DestroyBitmap (pDst1, sDst1); 
                return NULL; 
            } 
        } 
    } 
 
    /******* Perform 2nd shear  (vertical) ******/ 
 
    // Calc 2nd shear (vertical) destination image dimensions 
    SIZE sDst2; 
    sDst2.cx = sDst1.cx; 
    sDst2.cy = UINT (double (sSrc.cx) * fabs (dSinE) + double (sSrc.cy) * cos (dRadAngle)) + 1; 
    // Allocate image for 2nd shear 
    CPxl *pDst2 = CreateNewBitmap (sDst2); 
    if (NULL == pDst2) 
    { 
        DestroyBitmap (pDst1, sDst1); 
        return NULL; 
    } 
 
    double dOffset;     // Variable skew offset 
    if (dSinE > 0.0) 
    {    
        // Positive angle 
        dOffset = double (sSrc.cx - 1) * dSinE; 
    } 
    else 
    { 
        // Negative angle 
        dOffset = -dSinE * double (sSrc.cx - sDst2.cx); 
    } 
 
    for (u = 0; u < UINT(sDst2.cx); u++, dOffset -= dSinE)  
    { 
        int iShear = int (floor (dOffset)); 
        VertSkew (  pDst1,  
                    sDst1, 
                    pDst2, 
                    sDst2, 
                    u, 
                    iShear, 
                    dOffset - double(iShear), 
                    clrBack); 
        if (m_Callback) 
        { 
            // Report progress 
            BYTE bProgress = bMidImage ? 66 + BYTE(double (u) / double(sDst2.cy) * double(50.0 / 3.0)) : 
                                         33 + BYTE(double (u) / double(sDst2.cy) * double(33.33333)); 
            if (!m_Callback (bProgress)) 
            { 
                // Operation cancelled 
                DestroyBitmap (pDst1, sDst1); 
                DestroyBitmap (pDst2, sDst2); 
                return NULL; 
            } 
        } 
    } 
 
    /******* Perform 3rd shear (horizontal) ******/ 
 
    // Free result of 1st shear 
    DestroyBitmap (pDst1, sDst1); 
 
    // Calc 3rd shear (horizontal) destination image dimensions 
    (*psDst).cx = UINT (double(sSrc.cy) * fabs (dSinE) + double(sSrc.cx) * cos (dRadAngle)) + 1; 
    (*psDst).cy = sDst2.cy; 
 
    // Allocate image for 3rd shear 
    CPxl *pDst3 = CreateNewBitmap (*psDst); 
 
    if (NULL == pDst3) 
    { 
        DestroyBitmap (pDst2, sDst2); 
        return NULL; 
    } 
 
    if (dSinE >= 0.0) 
    { 
        // Positive angle 
        dOffset = double(sSrc.cx - 1) * dSinE * -dTan; 
    } 
    else  
    { 
        // Negative angle 
        dOffset = dTan * (double(sSrc.cx - 1) * -dSinE + double(1 - (*psDst).cy)); 
    } 
    for (u = 0; u < UINT((*psDst).cy); u++, dOffset += dTan) 
    { 
        int iShear = int (floor(dOffset)); 
        HorizSkew ( pDst2, 
                    sDst2, 
                    pDst3, 
                    (*psDst), 
                    u, 
                    iShear, 
                    dOffset - double (iShear), 
                    clrBack 
                  ); 
        if (m_Callback) 
        { 
            // Report progress 
            BYTE bProgress = bMidImage ? 83 + BYTE(double (u) / double((*psDst).cy) * double(50.0 / 3.0)) : 
                                         66 + BYTE(double (u) / double((*psDst).cy) * double(33.33333)); 
            if (!m_Callback (bProgress)) 
            { 
                // Operation cancelled 
                DestroyBitmap (pDst2, sDst2); 
                DestroyBitmap (pDst3, *psDst); 
                return NULL; 
            } 
        } 
    } 
    // Free result of 2nd shear     
    DestroyBitmap (pDst2, sDst2); 
    // Return result of 3rd shear 
    return pDst3;       
}   // CRotateByShear::Rotate45 
 
 
template  
CPxl *  
CRotateByShear::AllocAndRotate (   
    CPxl       *pSrc,  
    SIZE        sSrc, 
    double      dAngle, 
    SIZE       *psDst, 
    COLORREF    clrBack 
) 
/*------------------------------------------------------------------------------ 
 
  Function: AllocAndRotate 
 
  Purpose:  Rotates an image by a given degree 
 
  Input:    Image to rotate (+dimensions) 
            Angle of rotation 
            Pointers to dimensions of rotated image 
            Background color 
 
  Output:   Pointer to newly allocated rotated image 
 
  Remarks:  Angle is unlimited. 3-shears technique is used. 
 
------------------------------------------------------------------------------*/ 
{ 
    CPxl *pMidImg = pSrc; 
    SIZE sMidImg = sSrc; 
 
    if (NULL == pSrc) 
    { 
        return NULL; 
    } 
 
    while (dAngle >= 360.0) 
    { 
        // Bring angle to range of (-INF .. 360.0) 
        dAngle -= 360.0; 
    } 
    while (dAngle < 0.0) 
    { 
        // Bring angle to range of [0.0 .. 360.0)  
        dAngle += 360.0; 
    } 
    if ((dAngle > 45.0) && (dAngle <= 135.0))  
    { 
        // Angle in (45.0 .. 135.0]  
        // Rotate image by 90 degrees into temporary image, 
        // so it requires only an extra rotation angle  
        // of -45.0 .. +45.0 to complete rotation. 
        pMidImg = Rotate90 (pSrc, sSrc, &sMidImg); 
        dAngle -= 90.0; 
    } 
    else if ((dAngle > 135.0) && (dAngle <= 225.0))  
    {  
        // Angle in (135.0 .. 225.0]  
        // Rotate image by 180 degrees into temporary image, 
        // so it requires only an extra rotation angle  
        // of -45.0 .. +45.0 to complete rotation. 
        pMidImg = Rotate180 (pSrc, sSrc, &sMidImg); 
        dAngle -= 180.0; 
    } 
    else if ((dAngle > 225.0) && (dAngle <= 315.0))  
    {  
        // Angle in (225.0 .. 315.0]  
        // Rotate image by 270 degrees into temporary image, 
        // so it requires only an extra rotation angle  
        // of -45.0 .. +45.0 to complete rotation. 
        pMidImg = Rotate270 (pSrc, sSrc, &sMidImg); 
        dAngle -= 270.0; 
    } 
 
    // If we got here, angle is in (-45.0 .. +45.0] 
 
    if (NULL == pMidImg) 
    { 
        // Failed to allocate middle image 
        return NULL; 
    } 
 
    CPxl *pDst = Rotate45 ( pMidImg, 
                            sMidImg, 
                            psDst, 
                            dAngle, 
                            clrBack, 
                            pSrc != pMidImg); 
 
    if (pSrc != pMidImg) 
    { 
        // Middle image was required, free it now. 
        DestroyBitmap (pMidImg, sMidImg); 
    } 
    return pDst; 
}   // AllocAndRotate 
 
 
 
#endif