RotateByShear.h
上传用户:weiyeyoule
上传日期:2007-01-03
资源大小:21k
文件大小:21k
源码类别:

GDI/图象编程

开发平台:

Visual C++

  1. #ifndef _ROTATE_BY_SHEAR_H_
  2. #define _ROTATE_BY_SHEAR_H_
  3. #include <math.h>
  4. #ifndef _PROGRESS_AND_ABORT_CALL_BACK
  5. #define _PROGRESS_AND_ABORT_CALL_BACK
  6. typedef BOOL (*ProgressAnbAbortCallBack)(BYTE bPercentComplete);
  7. #endif  // _PROGRESS_AND_ABORT_CALL_BACK
  8. template <class CPxl>
  9. class CRotateByShear
  10. {
  11. public:
  12.     CRotateByShear (ProgressAnbAbortCallBack callback = NULL) : 
  13.         m_Callback (callback) {}
  14.     virtual ~CRotateByShear() {}
  15.     CPxl * AllocAndRotate (  
  16.         CPxl   *pSrc,           // Pointer to source image
  17.         SIZE        sSrc,       // Size of source image
  18.         double      dAngle,     // Rotation angle
  19.         SIZE       *psDst,      // Pointer to destination image size
  20.         COLORREF    clrBack);   // Background color
  21. protected:
  22.     //
  23.     // You must override these 5 function in the derived class and implement 
  24.     // specific pixel access functions according to the bitmap type you use.
  25.     //
  26.         // Get RGB value at given pixel coordinates
  27.     virtual COLORREF GetRGB (CPxl *pImage,      // Pointer to image
  28.                              SIZE  sImage,      // Size of image
  29.                              UINT  x,           // X coordinate
  30.                              UINT  y            // Y coordinate
  31.                             ) = 0;
  32.         // Set RGB value at given pixel coordinates
  33.     virtual void     SetRGB (CPxl *pImage,   // Pointer to image
  34.                              SIZE  sImage,   // Size of image
  35.                              UINT  x,        // X coordinate
  36.                              UINT  y,        // Y coordinate
  37.                              CPxl  clr       // New color to set
  38.                             ) = 0;
  39.         // Create a new bitmap, given a bitmap dimensions
  40.     virtual CPxl *CreateNewBitmap (SIZE  sImage // Size of image
  41.                             ) = 0;
  42.         // Create a new bitmap which is an identical copy of the source bitmap
  43.     virtual CPxl *CopyBitmap (CPxl *pImage,     // Pointer to source image
  44.                               SIZE  sImage      // Size of source (and destination) image
  45.                              ) = 0;
  46.         // Destroy a bitmap previously created in call to CreateNewBitmap(..)
  47.         // or to CopyBitmap (...)
  48.     virtual void DestroyBitmap (CPxl *pImage,   // Pointer to image
  49.                                 SIZE  sImage    // Size of image
  50.                                ) = 0;
  51. private:
  52.     ProgressAnbAbortCallBack    m_Callback;
  53.     void HorizSkew (CPxl *pSrc, 
  54.                     SIZE sSrc,
  55.                     CPxl *pDst, 
  56.                     SIZE sDst,
  57.                     UINT uRow, 
  58.                     int iOffset, 
  59.                     double dWeight,
  60.                     COLORREF clrBack);
  61.     void VertSkew ( CPxl *pSrc, 
  62.                     SIZE sSrc,
  63.                     CPxl *pDst, 
  64.                     SIZE sDst,
  65.                     UINT uCol,
  66.                     int iOffset,
  67.                     double dWeight,
  68.                     COLORREF clrBack);
  69.     CPxl * Rotate90  (CPxl *pSrc, SIZE sSrc, SIZE *psDst);
  70.     CPxl * Rotate180 (CPxl *pSrc, SIZE sSrc, SIZE *psDst);
  71.     CPxl * Rotate270 (CPxl *pSrc, SIZE sSrc, SIZE *psDst);
  72.     CPxl * Rotate45 (
  73.         CPxl *pSrc, 
  74.         SIZE sSrc,
  75.         SIZE *psDst,
  76.         double dAngle,
  77.         COLORREF clrBack,
  78.         BOOL bMidImage);
  79. };  // CRotateByShear
  80. #ifdef ROTATE_PI
  81. #undef ROTATE_PI
  82. #endif // ROTATE_PI
  83. #define ROTATE_PI  double (3.1415926535897932384626433832795)
  84. template <class CPxl>
  85. void 
  86. CRotateByShear<CPxl>::HorizSkew (
  87.     CPxl *pSrc, 
  88.     SIZE sSrc,
  89.     CPxl *pDst, 
  90.     SIZE sDst,
  91.     UINT uRow, 
  92.     int iOffset, 
  93.     double dWeight,
  94.     COLORREF clrBack)
  95. /*------------------------------------------------------------------------------
  96.   Function: HorizSkew
  97.   Purpose:  Skews a row horizontally (with filtered weights)
  98.   Input:    Image to skew (+ dimensions)
  99.             Destination of skewed image (+ dimensions)
  100.             Row index
  101.             Skew offset
  102.             Relative weight of right pixel
  103.             Background color
  104.   Output:   None.
  105.   Remarks:  Limited to 45 degree skewing only. Filters two adjacent pixels.
  106. ------------------------------------------------------------------------------*/
  107. {
  108.     for (int i = 0; i < iOffset; i++)
  109.     {
  110.         // Fill gap left of skew with background
  111.         SetRGB (pDst, sDst, i, uRow, clrBack);
  112.     }
  113.     COLORREF pxlOldLeft = clrBack;
  114.     for (i = 0; i < sSrc.cx; i++) 
  115.     {
  116.         // Loop through row pixels
  117.         COLORREF pxlSrc = GetRGB(pSrc, sSrc, i, uRow);
  118.         // Calculate weights
  119.         COLORREF pxlLeft = RGB (BYTE ( double (GetRValue (pxlSrc)) * dWeight ),
  120.                                 BYTE ( double (GetGValue (pxlSrc)) * dWeight ),
  121.                                 BYTE ( double (GetBValue (pxlSrc)) * dWeight ));
  122.         // Update left over on source
  123.         pxlSrc = RGB ( GetRValue (pxlSrc) - ( GetRValue (pxlLeft) - GetRValue (pxlOldLeft) ),
  124.                        GetGValue (pxlSrc) - ( GetGValue (pxlLeft) - GetGValue (pxlOldLeft) ),
  125.                        GetBValue (pxlSrc) - ( GetBValue (pxlLeft) - GetBValue (pxlOldLeft) ));
  126.         // Check boundries 
  127.         if ((i + iOffset >= 0) && (i + iOffset < sDst.cx))
  128.         {
  129.             SetRGB (pDst, sDst, i + iOffset, uRow, pxlSrc);
  130.         }
  131.         // Save leftover for next pixel in scan
  132.         pxlOldLeft = pxlLeft;
  133.     }
  134.     // Go to rightmost point of skew
  135.     i += iOffset;  
  136.     if (i < sDst.cx)
  137.     {
  138.         // If still in image bounds, put leftovers there
  139.         SetRGB (pDst, sDst, i, uRow, pxlOldLeft);
  140.     }
  141.     while (++i < sDst.cx)
  142.     {   // Clear to the right of the skewed line with background
  143.         SetRGB (pDst, sDst, i, uRow, clrBack);
  144.     }
  145. }   // CRotateByShear::HorizSkew
  146. template <class CPxl>
  147. void 
  148. CRotateByShear<CPxl>::VertSkew (
  149.     CPxl *pSrc, 
  150.     SIZE sSrc,
  151.     CPxl *pDst, 
  152.     SIZE sDst,
  153.     UINT uCol,
  154.     int iOffset,
  155.     double dWeight,
  156.     COLORREF clrBack)
  157. /*------------------------------------------------------------------------------
  158.   Function: VertSkew
  159.   Purpose:  Skews a column vertically (with filtered weights)
  160.   Input:    Image to skew (+dimensions)
  161.             Destination of skewed image (+dimensions)
  162.             Column index
  163.             Skew offset
  164.             Relative weight of upper pixel
  165.             Background color
  166.   Output:   None.
  167.   Remarks:  Limited to 45 degree skewing only. Filters two adjacent pixels.
  168. ------------------------------------------------------------------------------*/
  169. {
  170.     for (int i = 0; i < iOffset; i++)
  171.     {
  172.         // Fill gap above skew with background
  173.         SetRGB (pDst, sDst, uCol, i, clrBack);
  174.     }
  175.     COLORREF pxlOldLeft = clrBack;
  176.     int iYPos;
  177.     for (i = 0; i < sSrc.cy; i++) 
  178.     {
  179.         // Loop through column pixels
  180.         COLORREF pxlSrc = GetRGB (pSrc, sSrc, uCol, i);
  181.         iYPos = i + iOffset;
  182.         // Calculate weights
  183.         COLORREF pxlLeft = RGB (BYTE ( double (GetRValue (pxlSrc)) * dWeight ),
  184.                                 BYTE ( double (GetGValue (pxlSrc)) * dWeight ),
  185.                                 BYTE ( double (GetBValue (pxlSrc)) * dWeight ));
  186.         // Update left over on source
  187.         pxlSrc = RGB ( GetRValue (pxlSrc) - ( GetRValue (pxlLeft) - GetRValue (pxlOldLeft) ),
  188.                        GetGValue (pxlSrc) - ( GetGValue (pxlLeft) - GetGValue (pxlOldLeft) ),
  189.                        GetBValue (pxlSrc) - ( GetBValue (pxlLeft) - GetBValue (pxlOldLeft) ));
  190.         // Check boundries
  191.         if ((iYPos >= 0) && (iYPos < sDst.cy))
  192.         {
  193.             SetRGB (pDst, sDst, uCol, iYPos, pxlSrc);
  194.         }
  195.         // Save leftover for next pixel in scan
  196.         pxlOldLeft = pxlLeft;
  197.     }
  198.     // Go to bottom point of skew
  199.     i = iYPos;  
  200.     if (i < sDst.cy)
  201.     {
  202.         // If still in image bounds, put leftovers there
  203.         SetRGB (pDst, sDst, uCol, i, pxlOldLeft);
  204.     }
  205.     while (++i < sDst.cy)
  206.     {
  207.         // Clear below skewed line with background
  208.         SetRGB (pDst, sDst, uCol, i, clrBack);
  209.     }
  210. }   // CRotateByShear::VertSkew
  211. template <class CPxl>
  212. CPxl * 
  213. CRotateByShear<CPxl>::Rotate90  (CPxl *pSrc, SIZE sSrc, SIZE *psDst)
  214. /*------------------------------------------------------------------------------
  215.   Function: Rotate90
  216.   Purpose:  Rotates an image by 90 degrees (counter clockwise)
  217.   Input:    Image to rotate (+dimensions)
  218.             Pointer to destination size
  219.   Output:   Pointer to newly allocated rotated image
  220.   Remarks:  Precise rotation, no filters required.
  221. ------------------------------------------------------------------------------*/
  222. {
  223.     (*psDst).cx = sSrc.cy;
  224.     (*psDst).cy = sSrc.cx;
  225.     CPxl *pDst = CreateNewBitmap (*psDst);
  226.     if (NULL == pDst)
  227.     {
  228.         return NULL;
  229.     }
  230.     for (UINT uY = 0; uY < UINT(sSrc.cy); uY++)
  231.     {
  232.         for (UINT uX = 0; uX < UINT(sSrc.cx); uX++)
  233.         {
  234.             SetRGB (pDst, *psDst, uY, (*psDst).cy - uX - 1, GetRGB (pSrc, sSrc, uX, uY));
  235.         }
  236.         if (m_Callback)
  237.         {
  238.             // Report progress
  239.             if (!m_Callback (BYTE(double (uY) / double(sSrc.cy) * double(50.0))))
  240.             {
  241.                 // Operation cancelled
  242.                 DestroyBitmap (pDst, *psDst);
  243.                 return NULL;
  244.             }
  245.         }
  246.     }
  247.     return pDst;
  248. }   // CRotateByShear::Rotate90
  249. template <class CPxl>
  250. CPxl * 
  251. CRotateByShear<CPxl>::Rotate180  (CPxl *pSrc, SIZE sSrc, SIZE *psDst)
  252. /*------------------------------------------------------------------------------
  253.   Function: Rotate180
  254.   Purpose:  Rotates an image by 180 degrees (counter clockwise)
  255.   Input:    Image to rotate (+dimensions)
  256.             Pointer to destination size
  257.   Output:   Pointer to newly allocated rotated image
  258.   Remarks:  Precise rotation, no filters required.
  259. ------------------------------------------------------------------------------*/
  260. {
  261.     *psDst = sSrc;
  262.     CPxl *pDst = CreateNewBitmap (*psDst);
  263.     if (NULL == pDst)
  264.     {
  265.         return NULL;
  266.     }
  267.     for (UINT uY = 0; uY < UINT(sSrc.cy); uY++)
  268.     {
  269.         for (UINT uX = 0; uX < UINT(sSrc.cx); uX++)
  270.         {
  271.             SetRGB (pDst, *psDst, (*psDst).cx - uX - 1, (*psDst).cy - uY - 1, GetRGB (pSrc, sSrc, uX, uY));
  272.         }
  273.         if (m_Callback)
  274.         {
  275.             // Report progress
  276.             if (!m_Callback (BYTE(double (uY) / double(sSrc.cy) * double(50.0))))
  277.             {
  278.                 // Operation cancelled
  279.                 DestroyBitmap (pDst, *psDst);
  280.                 return NULL;
  281.             }
  282.         }
  283.     }
  284.     return pDst;
  285. }   // CRotateByShear::Rotate180
  286. template <class CPxl>
  287. CPxl * 
  288. CRotateByShear<CPxl>::Rotate270  (CPxl *pSrc, SIZE sSrc, SIZE *psDst)
  289. /*------------------------------------------------------------------------------
  290.   Function: Rotate270
  291.   Purpose:  Rotates an image by 270 degrees (counter clockwise)
  292.   Input:    Image to rotate (+dimensions)
  293.             Pointer to destination size
  294.   Output:   Pointer to newly allocated rotated image
  295.   Remarks:  Precise rotation, no filters required.
  296. ------------------------------------------------------------------------------*/
  297. {
  298.     (*psDst).cx = sSrc.cy;
  299.     (*psDst).cy = sSrc.cx;
  300.     CPxl *pDst = CreateNewBitmap (*psDst);
  301.     if (NULL == pDst)
  302.     {
  303.         return NULL;
  304.     }
  305.     for (UINT uY = 0; uY < UINT(sSrc.cy); uY++)
  306.     {
  307.         for (UINT uX = 0; uX < UINT(sSrc.cx); uX++)
  308.         {
  309.             SetRGB (pDst, *psDst, (*psDst).cx - uY - 1, uX, GetRGB (pSrc, sSrc, uX, uY));
  310.         }
  311.         if (m_Callback)
  312.         {
  313.             // Report progress
  314.             if (!m_Callback (BYTE(double (uY) / double(sSrc.cy) * double(50.0))))
  315.             {
  316.                 // Operation cancelled
  317.                 DestroyBitmap (pDst, *psDst);
  318.                 return NULL;
  319.             }
  320.         }
  321.     }
  322.     return pDst;
  323. }   // CRotateByShear::Rotate270
  324. template <class CPxl>
  325. CPxl * 
  326. CRotateByShear<CPxl>::Rotate45  (
  327.     CPxl *pSrc, 
  328.     SIZE sSrc,
  329.     SIZE *psDst,
  330.     double dAngle,
  331.     COLORREF clrBack,
  332.     BOOL bMidImage)
  333. /*------------------------------------------------------------------------------
  334.   Function: Rotate45
  335.   Purpose:  Rotates an image by a given degree in range [-45.0 .. +45.0]
  336.             (counter clockwise)
  337.   Input:    Image to rotate (+dimensions)
  338.             Pointer to destination size
  339.             Degree of rotation
  340.             Background color
  341.             Was middle image used (for correct progress report)
  342.   Output:   Pointer to newly allocated rotated image
  343.   Remarks:  Using the 3-shear technique.
  344. ------------------------------------------------------------------------------*/
  345. {
  346.     if (0.0 == dAngle)
  347.     {
  348.         // No rotation at all
  349.         (*psDst) = sSrc;
  350.         return CopyBitmap (pSrc, sSrc);
  351.     }
  352.     double dRadAngle = dAngle * ROTATE_PI / double(180); // Angle in radians
  353.     double dSinE = sin (dRadAngle);
  354.     double dTan = tan (dRadAngle / 2.0);
  355.     // Calc first shear (horizontal) destination image dimensions 
  356.     SIZE sDst1;
  357.     sDst1.cx = sSrc.cx + int(double(sSrc.cy) * fabs(dTan));
  358.     sDst1.cy = sSrc.cy;
  359.     /******* Perform 1st shear (horizontal) ******/
  360.     // Allocate image for 1st shear
  361.     CPxl *pDst1 = CreateNewBitmap (sDst1);
  362.     if (NULL == pDst1)
  363.     {
  364.         return NULL;
  365.     }
  366.     for (UINT u = 0; u < UINT(sDst1.cy); u++) 
  367.     {  
  368.         double dShear;
  369.         if (dTan >= 0.0)
  370.         {
  371.             // Positive angle
  372.             dShear = (double(u) + 0.5) * dTan;
  373.         }
  374.         else
  375.         {
  376.             // Negative angle
  377.             dShear = (double (int(u) - sDst1.cy) + 0.5) * dTan;
  378.         }
  379.         int iShear = int (floor (dShear));
  380.         HorizSkew ( pSrc, 
  381.                     sSrc, 
  382.                     pDst1, 
  383.                     sDst1,
  384.                     u, 
  385.                     iShear, 
  386.                     dShear - double(iShear),
  387.                     clrBack);
  388.         if (m_Callback)
  389.         {
  390.             // Report progress
  391.             BYTE bProgress = bMidImage ? 50 + BYTE(double (u) / double(sDst1.cy) * double(50.0 / 3.0)) :
  392.                                          BYTE(double (u) / double(sDst1.cy) * double(33.33333));
  393.             if (!m_Callback (bProgress))
  394.             {
  395.                 // Operation cancelled
  396.                 DestroyBitmap (pDst1, sDst1);
  397.                 return NULL;
  398.             }
  399.         }
  400.     }
  401.     /******* Perform 2nd shear  (vertical) ******/
  402.     // Calc 2nd shear (vertical) destination image dimensions
  403.     SIZE sDst2;
  404.     sDst2.cx = sDst1.cx;
  405.     sDst2.cy = UINT (double (sSrc.cx) * fabs (dSinE) + double (sSrc.cy) * cos (dRadAngle)) + 1;
  406.     // Allocate image for 2nd shear
  407.     CPxl *pDst2 = CreateNewBitmap (sDst2);
  408.     if (NULL == pDst2)
  409.     {
  410.         DestroyBitmap (pDst1, sDst1);
  411.         return NULL;
  412.     }
  413.     double dOffset;     // Variable skew offset
  414.     if (dSinE > 0.0)
  415.     {   
  416.         // Positive angle
  417.         dOffset = double (sSrc.cx - 1) * dSinE;
  418.     }
  419.     else
  420.     {
  421.         // Negative angle
  422.         dOffset = -dSinE * double (sSrc.cx - sDst2.cx);
  423.     }
  424.     for (u = 0; u < UINT(sDst2.cx); u++, dOffset -= dSinE) 
  425.     {
  426.         int iShear = int (floor (dOffset));
  427.         VertSkew (  pDst1, 
  428.                     sDst1,
  429.                     pDst2,
  430.                     sDst2,
  431.                     u,
  432.                     iShear,
  433.                     dOffset - double(iShear),
  434.                     clrBack);
  435.         if (m_Callback)
  436.         {
  437.             // Report progress
  438.             BYTE bProgress = bMidImage ? 66 + BYTE(double (u) / double(sDst2.cy) * double(50.0 / 3.0)) :
  439.                                          33 + BYTE(double (u) / double(sDst2.cy) * double(33.33333));
  440.             if (!m_Callback (bProgress))
  441.             {
  442.                 // Operation cancelled
  443.                 DestroyBitmap (pDst1, sDst1);
  444.                 DestroyBitmap (pDst2, sDst2);
  445.                 return NULL;
  446.             }
  447.         }
  448.     }
  449.     /******* Perform 3rd shear (horizontal) ******/
  450.     // Free result of 1st shear
  451.     DestroyBitmap (pDst1, sDst1);
  452.     // Calc 3rd shear (horizontal) destination image dimensions
  453.     (*psDst).cx = UINT (double(sSrc.cy) * fabs (dSinE) + double(sSrc.cx) * cos (dRadAngle)) + 1;
  454.     (*psDst).cy = sDst2.cy;
  455.     // Allocate image for 3rd shear
  456.     CPxl *pDst3 = CreateNewBitmap (*psDst);
  457.     if (NULL == pDst3)
  458.     {
  459.         DestroyBitmap (pDst2, sDst2);
  460.         return NULL;
  461.     }
  462.     if (dSinE >= 0.0)
  463.     {
  464.         // Positive angle
  465.         dOffset = double(sSrc.cx - 1) * dSinE * -dTan;
  466.     }
  467.     else 
  468.     {
  469.         // Negative angle
  470.         dOffset = dTan * (double(sSrc.cx - 1) * -dSinE + double(1 - (*psDst).cy));
  471.     }
  472.     for (u = 0; u < UINT((*psDst).cy); u++, dOffset += dTan)
  473.     {
  474.         int iShear = int (floor(dOffset));
  475.         HorizSkew ( pDst2,
  476.                     sDst2,
  477.                     pDst3,
  478.                     (*psDst),
  479.                     u,
  480.                     iShear,
  481.                     dOffset - double (iShear),
  482.                     clrBack
  483.                   );
  484.         if (m_Callback)
  485.         {
  486.             // Report progress
  487.             BYTE bProgress = bMidImage ? 83 + BYTE(double (u) / double((*psDst).cy) * double(50.0 / 3.0)) :
  488.                                          66 + BYTE(double (u) / double((*psDst).cy) * double(33.33333));
  489.             if (!m_Callback (bProgress))
  490.             {
  491.                 // Operation cancelled
  492.                 DestroyBitmap (pDst2, sDst2);
  493.                 DestroyBitmap (pDst3, *psDst);
  494.                 return NULL;
  495.             }
  496.         }
  497.     }
  498.     // Free result of 2nd shear    
  499.     DestroyBitmap (pDst2, sDst2);
  500.     // Return result of 3rd shear
  501.     return pDst3;      
  502. }   // CRotateByShear::Rotate45
  503. template <class CPxl>
  504. CPxl * 
  505. CRotateByShear<CPxl>::AllocAndRotate (  
  506.     CPxl       *pSrc, 
  507.     SIZE        sSrc,
  508.     double      dAngle,
  509.     SIZE       *psDst,
  510.     COLORREF    clrBack
  511. )
  512. /*------------------------------------------------------------------------------
  513.   Function: AllocAndRotate
  514.   Purpose:  Rotates an image by a given degree
  515.   Input:    Image to rotate (+dimensions)
  516.             Angle of rotation
  517.             Pointers to dimensions of rotated image
  518.             Background color
  519.   Output:   Pointer to newly allocated rotated image
  520.   Remarks:  Angle is unlimited. 3-shears technique is used.
  521. ------------------------------------------------------------------------------*/
  522. {
  523.     CPxl *pMidImg = pSrc;
  524.     SIZE sMidImg = sSrc;
  525.     if (NULL == pSrc)
  526.     {
  527.         return NULL;
  528.     }
  529.     while (dAngle >= 360.0)
  530.     {
  531.         // Bring angle to range of (-INF .. 360.0)
  532.         dAngle -= 360.0;
  533.     }
  534.     while (dAngle < 0.0)
  535.     {
  536.         // Bring angle to range of [0.0 .. 360.0) 
  537.         dAngle += 360.0;
  538.     }
  539.     if ((dAngle > 45.0) && (dAngle <= 135.0)) 
  540.     {
  541.         // Angle in (45.0 .. 135.0] 
  542.         // Rotate image by 90 degrees into temporary image,
  543.         // so it requires only an extra rotation angle 
  544.         // of -45.0 .. +45.0 to complete rotation.
  545.         pMidImg = Rotate90 (pSrc, sSrc, &sMidImg);
  546.         dAngle -= 90.0;
  547.     }
  548.     else if ((dAngle > 135.0) && (dAngle <= 225.0)) 
  549.     { 
  550.         // Angle in (135.0 .. 225.0] 
  551.         // Rotate image by 180 degrees into temporary image,
  552.         // so it requires only an extra rotation angle 
  553.         // of -45.0 .. +45.0 to complete rotation.
  554.         pMidImg = Rotate180 (pSrc, sSrc, &sMidImg);
  555.         dAngle -= 180.0;
  556.     }
  557.     else if ((dAngle > 225.0) && (dAngle <= 315.0)) 
  558.     { 
  559.         // Angle in (225.0 .. 315.0] 
  560.         // Rotate image by 270 degrees into temporary image,
  561.         // so it requires only an extra rotation angle 
  562.         // of -45.0 .. +45.0 to complete rotation.
  563.         pMidImg = Rotate270 (pSrc, sSrc, &sMidImg);
  564.         dAngle -= 270.0;
  565.     }
  566.     // If we got here, angle is in (-45.0 .. +45.0]
  567.     if (NULL == pMidImg)
  568.     {
  569.         // Failed to allocate middle image
  570.         return NULL;
  571.     }
  572.     CPxl *pDst = Rotate45 ( pMidImg,
  573.                             sMidImg,
  574.                             psDst,
  575.                             dAngle,
  576.                             clrBack,
  577.                             pSrc != pMidImg);
  578.     if (pSrc != pMidImg)
  579.     {
  580.         // Middle image was required, free it now.
  581.         DestroyBitmap (pMidImg, sMidImg);
  582.     }
  583.     return pDst;
  584. }   // AllocAndRotate
  585. #endif