Code: Select all
;******************************************************************************
;*
;* Image Rotation routines for 24/32 bit with optional AA
;* by Luis, http://luis.no-ip.net
;* v1.11 for PB 5.73
;*
;* Tested under Windows 32/64 bit and Linux 32 bit with PB 4.40 B2
;*
;* These routines can deal with both 24/32 bit images and with the alpha channel.
;* The output of the routines will be an image with the same number of BPP
;* as the one passed in input to them. The source image is not freed.
;*
;* Inspired by a simpler Visual Basic code from Robert Rayment. Thank you.
;* http://www.planet-source-code.com/vb/scripts/ShowCode.asp?txtCodeId=23476&lngWId=1
;*
;* ----------------------------------------------------------------------------
;*
;* RotateImageFree (nSrcImage, fDegRot.f, flgAntiAliasing, FillColor)
;*
;*  nSrcImage           The 24/32 bit PureBasic's image to rotate
;*  fDegRot             Float angle in degrees (+/-) 0.0 -> 360.0
;*  flgAntiAliasing     0 for simpler rotation, 1 for antialiased rotation
;*  FillColor           Used to fill the new areas of the resulting image
;*
;*  Return              a 24/32 bit image rotated by fDegRot
;*
;* NOTES :
;*  FillColor is not used with 32 bit images, the new areas are always transparent.
;*
;*  FillColor can be set to a unique color with 24 bit images if you want to
;*  draw the resulting image with masking using GDI functions under Windows,
;*  for example. Or maybe to simply match a certain background color.
;*
;*  The anti aliasing use 4 pixels to do the AA, this is useful especially
;*  when text is present on the image to be rotated to obtain a good quality
;*  at the expense of speed. A free-angle rotation really need AA !
;*
;* ----------------------------------------------------------------------------
;*
;* RotateImage (nSrcImage, DegRot)
;*
;*  nSrcImage           The 24/32 bit PureBasic's image to rotate
;*  DegRot              Integer angle in degrees (+/-) 90/180/270
;*
;*  Return              a 24/32 bit image rotated by DegRot
;*
;* NOTES :
;*   Use this procedure to rotate by multiples of 90 degrees instead of RotateImageFree(). 
;*   It's faster and it's not subject to rounding errors.
;*
;* ----------------------------------------------------------------------------
;*
;* FlipImage (nSrcImage)
;*
;*  nSrcImage           The 24/32 bit PureBasic's image to flip
;*
;*  Return              a 24/32 bit image flipped vertically
;*
;* ----------------------------------------------------------------------------
;*
;* MirrorImage (nSrcImage)
;*
;*  nSrcImage           The 24/32 bit PureBasic's image to mirror
;*
;*  Return              a 24/32 bit image mirrored horizontally
;*
;******************************************************************************
#EXC_ALLOC_ERR = 1
CompilerIf Defined(Macro_TRY, #PB_Constant) = 0
#Macro_TRY = 1
Global EXCEPTION
Macro TRY (exp, exc = 0)
; [DESC]
; Evaluate the expression and jump to the corresponding CATCH if it's false.
;
; [INPUT]
; exp : The expression to test.
; exc : An optional numeric code to identify the exception type for the CATCH.
;
; [NOTES]
; This is inspired to real exception handling but it isn't.
; The purpose of this TRY/CATCH pair is to avoid messy, deep error checking in structured code 
; and to wrap the use of GOTO inside something more convenient to read/follow.
; You can write the main body of the code under the assumption nothing is going wrong, 
; and concentrate the cleanup / recovery in a single place, keeping single entry and exit points.
;
; Example:
;
; Procedure.i MyProc()
; 
;  TRY (ProcA(), 1)
;  ...
;  TRY (ProcB(), 2)
;  ...
;  TRY (ProcC(), 3)
;  ...
; 
;  ProcedureReturn 1 ; success
;  
;  CATCH
;  
;  Select EXCEPTION
;   Case 1 : ; specific cleanup
;   Case 2 : ; specific cleanup
;   Case 3 : ; specific cleanup
;   Default : ; fallback
;  EndSelect
; 
;  ProcedureReturn 0 ; failure
; EndProcedure
;
  
 If Not (exp)
    EXCEPTION = exc
    Goto label_catch_exception
 Else
    EXCEPTION = 0
 EndIf
EndMacro
Macro RAISE (exc = 0)
; [DESC]
; Unconditionally jump to the corresponding CATCH.
;
; [INPUT]
; exc : An optional numeric code to identify the exception type for the CATCH.
  
 EXCEPTION = exc
 Goto label_catch_exception
 EndMacro
Macro CATCH()
; [DESC]
; Receive the control from the program when the TRY expression is false.
;
; [NOTES]
; See the command TRY
 label_catch_exception:
EndMacro
CompilerEndIf
Structure T_RGBA
 B.a
 G.a
 R.a
 A.a
EndStructure
Macro RGB_B(color)
 ((color & $FF0000) >> 16) 
EndMacro
Macro RGB_G(color)
 ((color & $FF00) >> 8) 
EndMacro
Macro RGB_R(color)
 (color & $FF) 
EndMacro 
Macro RGB_Mix (r, g, b)
 ((((b) << 8 + (g)) << 8) + (r))
EndMacro
Macro CopyPixel32 (Xs, Ys, Xd, Yd, BufferPitchSrc, BufferPitchDest, ptRGBAs, ptRGBAd, pMemSrc, pMemDest)
 ptRGBAs = pMemSrc  + (Ys) * BufferPitchSrc + (Xs) << 2
 ptRGBAd = pMemDest + (Yd) * BufferPitchDest + (Xd) << 2        
 ptRGBAd\R = ptRGBAs\R
 ptRGBAd\G = ptRGBAs\G
 ptRGBAd\B = ptRGBAs\B        
 ptRGBAd\A = ptRGBAs\A
EndMacro
Macro CopyPixel24 (Xs, Ys, Xd, Yd, BufferPitchSrc, BufferPitchDest, ptRGBAs, ptRGBAd, pMemSrc, pMemDest)
 ptRGBAs = pMemSrc  + (Ys) * BufferPitchSrc + (Xs) * 3
 ptRGBAd = pMemDest + (Yd) * BufferPitchDest + (Xd) * 3
 ptRGBAd\R = ptRGBAs\R
 ptRGBAd\G = ptRGBAs\G
 ptRGBAd\B = ptRGBAs\B        
EndMacro
Macro ReadPixel32 (X, Y, BufferPitchSrc, ptRGBA, pMemSrc) 
 ptRGBA = pMemSrc + (Y) * BufferPitchSrc + (X) << 2
EndMacro
Macro ReadPixel24 (X, Y, BufferPitchSrc, ptRGBA, pMemSrc) 
 ptRGBA = pMemSrc + (Y) * BufferPitchSrc + (X) * 3
EndMacro
Macro WritePixel32 (tPixel, X, Y, BufferPitchDest, ptRGBA, pMemDest)
 ptRGBA = pMemDest + (Y) * BufferPitchDest + (X) << 2                       
 ptRGBA\R = tPixel\R
 ptRGBA\G = tPixel\G
 ptRGBA\B = tPixel\B
 ptRGBA\A = tPixel\A
EndMacro
Macro WritePixel24 (tPixel, X, Y, BufferPitchDest, ptRGBA, pMemDest)
 ptRGBA = pMemDest + (Y) * BufferPitchDest + (X) * 3
 ptRGBA\R = tPixel\R
 ptRGBA\G = tPixel\G
 ptRGBA\B = tPixel\B
EndMacro
Procedure.i AllocateImageData (nImage, *BufferPitch.Integer, FillColor = -1)
 Protected *ImageMem, *AllocMem, BufferPitch
 StartDrawing(ImageOutput(nImage))
   
  *ImageMem = DrawingBuffer()
  BufferPitch = DrawingBufferPitch()
   If FillColor <> -1
    Select ImageDepth(nImage)
        Case 24
            Box(0, 0, ImageWidth(nImage), ImageHeight(nImage), FillColor)
        Case 32
            DrawingMode(#PB_2DDrawing_AlphaChannel)   
            Box(0, 0, ImageWidth(nImage), ImageHeight(nImage), $00) ; full transparent
    EndSelect
  EndIf
  *AllocMem = AllocateMemory(BufferPitch * ImageHeight(nImage))
  If *AllocMem
    CopyMemory(*ImageMem, *AllocMem, MemorySize(*AllocMem))
    *BufferPitch\i = BufferPitch
  Else
    *BufferPitch\i = 0
  EndIf
 StopDrawing()
 ProcedureReturn *AllocMem
EndProcedure
Procedure CopyImageData (nImage, *DestMem)
 StartDrawing(ImageOutput(nImage))
  CopyMemory(*DestMem, DrawingBuffer(), MemorySize(*DestMem))
 StopDrawing()
EndProcedure
Procedure.i RotateImage (nSrcImage, DegRot)
; Rotate 24 bit images at (+/-) 90/180/270 degrees
; Rotate 32 bit images at (+/-) 90/180/270 degrees preserving the alpha-channel
 
 Protected *tRGBAs.T_RGBA, *tRGBAd.T_RGBA, tPixel.T_RGBA, RotType
 Protected *SrcMem, *DestMem, BufferPitchSrc, BufferPitchDest
 Protected SrcWidth, SrcHeight, DestWidth, DestHeight, nDestImage
 Protected X, Y, Xs, Ys
 Protected BitPlanes
 
 ; sanity checks
 
 If IsImage(nSrcImage) = 0
    ProcedureReturn 0
 EndIf
 BitPlanes = ImageDepth(nSrcImage)
 If BitPlanes <> 24 And BitPlanes <> 32
    ProcedureReturn 0
 EndIf
 If DegRot % 90
    ProcedureReturn 0
 EndIf
 DegRot % 360
 If DegRot = 0
    ProcedureReturn CopyImage(nSrcImage, #PB_Any)
 EndIf
 CompilerIf (#PB_Compiler_OS = #PB_OS_Linux)
  DegRot = -DegRot
 CompilerEndIf
 SrcWidth = ImageWidth(nSrcImage)
 SrcHeight = ImageHeight(nSrcImage)
 Select DegRot            
    Case 90, -270
        DestWidth = SrcHeight
        DestHeight = SrcWidth
        RotType = 1
    Case 180, -180
        RotType = 2
        DestWidth = SrcWidth
        DestHeight = SrcHeight
    Case 270, -90
        RotType = 3
        DestWidth = SrcHeight
        DestHeight = SrcWidth
 EndSelect
 ; create 24/32 bit destination image
 nDestImage = CreateImage(#PB_Any, DestWidth, DestHeight, BitPlanes)
 TRY (nDestImage)
 ; copy src image to allocated memory
 *SrcMem = AllocateImageData(nSrcImage, @BufferPitchSrc)
 TRY (*SrcMem, #EXC_ALLOC_ERR)
 ; copy dest image to allocated memory
 *DestMem = AllocateImageData(nDestImage, @BufferPitchDest)
 TRY (*DestMem, #EXC_ALLOC_ERR)
 Select BitPlanes
    Case 24
        For Y = 0 To DestHeight - 1
            For X = 0 To DestWidth - 1
           
                Select RotType
                    Case 1
                        Ys = SrcHeight - X - 1
                        Xs = Y
                    Case 2
                        Ys = SrcHeight - Y - 1
                        Xs = SrcWidth - X - 1   
                    Case 3
                        Ys = X
                        Xs = SrcWidth - Y - 1
                EndSelect
                CopyPixel24 (Xs, Ys, X, Y, BufferPitchSrc, BufferPitchDest, *tRGBAs, *tRGBAd, *SrcMem, *DestMem)
                           
            Next
         Next   
    Case 32
        For Y = 0 To DestHeight - 1
            For X = 0 To DestWidth - 1
           
                Select RotType
                    Case 1
                        Ys = SrcHeight - X - 1
                        Xs = Y
                    Case 2
                        Ys = SrcHeight - Y - 1
                        Xs = SrcWidth - X - 1   
                    Case 3
                        Ys = X
                        Xs = SrcWidth - Y - 1
                EndSelect
                CopyPixel32 (Xs, Ys, X, Y, BufferPitchSrc, BufferPitchDest, *tRGBAs, *tRGBAd, *SrcMem, *DestMem)                   
                           
            Next
         Next   
 EndSelect
 CopyImageData(nDestImage, *DestMem)
   
 FreeMemory(*SrcMem)
 FreeMemory(*DestMem)
 ProcedureReturn nDestImage
 CATCH()
 If EXCEPTION = #EXC_ALLOC_ERR
    If *SrcMem <> 0 : FreeMemory(*SrcMem) : EndIf
    If *DestMem <> 0 : FreeMemory(*DestMem) : EndIf    
    FreeImage(nDestImage)
 EndIf
 
 ProcedureReturn 0
EndProcedure
Procedure.i RotateImageFree (nSrcImage, fDegRot.f, flgAntiAliasing, FillColor = $ffffff)
; Rotates 24 bit images at any angle optionally with anti-aliasing filling the new area of the resulting image with the specified color.
; Rotates 32 bit images at any angle optionally with anti-aliasing preserving the alpha-channel.
 
 Protected *tRGBAs.T_RGBA, *tRGBAd.T_RGBA, tPixel.T_RGBA
 Protected *SrcMem, *DestMem, BufferPitchSrc, BufferPitchDest
 Protected fzCos.f, fzSin.f
 Protected SrcWidth, SrcHeight, DestWidth, DestHeight, BitPlanes, nDestImage
 Protected X, Y, Xs, Ys, Xc1, Yc1, Xc2, Yc2, iColor
 ; sanity checks
 If IsImage(nSrcImage) = 0
    ProcedureReturn 0
 EndIf
 BitPlanes = ImageDepth(nSrcImage)
 If BitPlanes <> 24 And BitPlanes <> 32
    ProcedureReturn 0
 EndIf
 If fDegRot >= 360.0 ; wrap it
    fDegRot = 360.0 * (fDegRot / 360.0 - Int(fDegRot / 360.0))
 EndIf
 If fDegRot = 0.0 Or fDegRot = 90.0 Or fDegRot = 180.0 Or fDegRot = 270.0
    ProcedureReturn RotateImage(nSrcImage, fDegRot)
 EndIf
 
 CompilerIf (#PB_Compiler_OS = #PB_OS_Linux)
 fDegRot = -fDegRot
 CompilerEndIf
 fzCos = Cos(Radian(fDegRot))
 fzSin = Sin(Radian(fDegRot)) 
 SrcWidth = ImageWidth(nSrcImage)
 SrcHeight = ImageHeight(nSrcImage)
 DestWidth = Round(SrcWidth * Abs(fzCos) + SrcHeight * Abs(fzSin), #PB_Round_Up)
 DestHeight = Round(SrcHeight * Abs(fzCos) + SrcWidth * Abs(fzSin), #PB_Round_Up)
 Xc1 = SrcWidth / 2
 Yc1 = SrcHeight / 2
 Xc2 = DestWidth / 2
 Yc2 = DestHeight / 2
 ; create 24/32 bit destination image
 nDestImage = CreateImage(#PB_Any, DestWidth, DestHeight, BitPlanes)
 TRY (nDestImage)
 ; copy src image to allocated memory
 *SrcMem = AllocateImageData (nSrcImage, @BufferPitchSrc)
 TRY (*SrcMem, #EXC_ALLOC_ERR)
 ; copy dest image to allocated memory and fill with backcolor
 *DestMem = AllocateImageData(nDestImage, @BufferPitchDest, FillColor)
 TRY (*DestMem, #EXC_ALLOC_ERR)
     
 Select flgAntiAliasing
    Case #False   
   
        Select BitPlanes
            Case 24
     
                For Y = 0 To DestHeight - 1
                    For X = 0 To DestWidth - 1
                   
                        ; For each nDestImage point find rotated nSrcImage source point
                        Xs = Xc1 + (X - Xc2) * fzCos + (Y - Yc2) * fzSin 
                        Ys = Yc1 + (Y - Yc2) * fzCos - (X - Xc2) * fzSin 
                                   
                        If Xs >= 0 And Xs < SrcWidth  And Ys >= 0 And Ys < SrcHeight
                            ; Move valid rotated nSrcImage source points to nDestImage                   
                            CopyPixel24 (Xs, Ys, X, Y, BufferPitchSrc, BufferPitchDest, *tRGBAs, *tRGBAd, *SrcMem, *DestMem)
                        EndIf
                                   
                    Next
                Next   
           
            Case 32
           
                For Y = 0 To DestHeight - 1
                    For X = 0 To DestWidth - 1
                        ; For each nDestImage point find rotated nSrcImage source point
                        Xs = Xc1 + (X - Xc2) * fzCos + (Y - Yc2) * fzSin 
                        Ys = Yc1 + (Y - Yc2) * fzCos - (X - Xc2) * fzSin 
                                   
                        If Xs >= 0 And Xs < SrcWidth  And Ys >= 0 And Ys < SrcHeight
                            ; Move valid rotated nSrcImage source points to nDestImage                   
                            CopyPixel32 (Xs, Ys, X, Y, BufferPitchSrc, BufferPitchDest, *tRGBAs, *tRGBAd, *SrcMem, *DestMem)
                        EndIf
                                   
                    Next
                Next   
                                     
        EndSelect
         
    Case #True
    
        Protected Xs0, Ys0, icr, icg, icb, icr0, icg0, icb0, icr1, icg1, icb1
        Protected fXs.f, fYs.f, fXfs1.f, fYfs1.f
        Protected fXfs1less.f, fYfs1less.f
        Select BitPlanes
           
            Case 24
           
                For Y = 0 To DestHeight - 1
                    For X = 0 To DestWidth - 1
           
                        ; For each nDestImage point find rotated nSrcImage source point
                        fXs = Xc1 + (X - Xc2) * fzCos + (Y - Yc2) * fzSin 
                        fYs = Yc1 + (Y - Yc2) * fzCos - (X - Xc2) * fzSin  
             
                        ; Bottom left coords of bounding floating point rectangle on nSrcImage
                        Xs0 = Int(fXs)   
                        Ys0 = Int(fYs)
                           
                        If Xs0 >= 0 And Xs0 < SrcWidth - 1 And Ys0 >= 0 And Ys0 < SrcHeight - 1
                            fXfs1 = fXs - Int(fXs)
                            fYfs1 = fYs - Int(fYs)
       
                            fXfs1less = 1 - fXfs1 - 0.000005 : If fXfs1less < 0 : fXfs1less = 0 : EndIf
                            fYfs1less = 1 - fYfs1 - 0.000005 : If fYfs1less < 0 : fYfs1less = 0 : EndIf
                                                                     
                            ReadPixel24 (Xs0, Ys0, BufferPitchSrc, *tRGBAs, *SrcMem)                                                                                                                               
                            icr = *tRGBAs\R * fXfs1less
                            icg = *tRGBAs\G * fXfs1less
                            icb = *tRGBAs\B * fXfs1less
                               
                            ReadPixel24 (Xs0 + 1, Ys0, BufferPitchSrc, *tRGBAs, *SrcMem)
                            icr0 = *tRGBAs\R * fXfs1 + icr
                            icg0 = *tRGBAs\G * fXfs1 + icg
                            icb0 = *tRGBAs\B * fXfs1 + icb
                                 
                            ReadPixel24 (Xs0, Ys0 + 1, BufferPitchSrc, *tRGBAs, *SrcMem)
                            icr = *tRGBAs\R * fXfs1less
                            icg = *tRGBAs\G * fXfs1less
                            icb = *tRGBAs\B * fXfs1less
                                                                       
                            ReadPixel24 (Xs0 + 1, Ys0 + 1, BufferPitchSrc, *tRGBAs, *SrcMem)
                            icr1 = *tRGBAs\R * fXfs1 + icr
                            icg1 = *tRGBAs\G * fXfs1 + icg
                            icb1 = *tRGBAs\B * fXfs1 + icb
                   
                            ; Weight along axis Y
                            tPixel\R = fYfs1less * icr0 + fYfs1 * icr1
                            tPixel\G = fYfs1less * icg0 + fYfs1 * icg1
                            tPixel\B = fYfs1less * icb0 + fYfs1 * icb1                           
                           
                            WritePixel24 (tPixel, X, Y, BufferPitchDest, *tRGBAd, *DestMem)                               
                        EndIf           
                    Next
                Next           
           
            Case 32
                Protected ica, ica0, ica1
               
                For Y = 0 To DestHeight - 1
                    For X = 0 To DestWidth - 1
           
                        ; For each nDestImage point find rotated nSrcImage source point
                        fXs = Xc1 + (X - Xc2) * fzCos + (Y - Yc2) * fzSin 
                        fYs = Yc1 + (Y - Yc2) * fzCos - (X - Xc2) * fzSin 
             
                        ; Bottom left coords of bounding floating point rectangle on nSrcImage
                        Xs0 = Int(fXs)   
                        Ys0 = Int(fYs)                       
                        If Xs0 >= 0 And Xs0 < SrcWidth - 1 And Ys0 >= 0 And Ys0 < SrcHeight - 1
                       
                            fXfs1 = fXs - Int(fXs)
                            fYfs1 = fYs - Int(fYs)
       
                            fXfs1less = 1 - fXfs1 - 0.000005 : If fXfs1less < 0 : fXfs1less = 0 : EndIf
                            fYfs1less = 1 - fYfs1 - 0.000005 : If fYfs1less < 0 : fYfs1less = 0 : EndIf
                                                                     
                            ReadPixel32 (Xs0, Ys0, BufferPitchSrc, *tRGBAs, *SrcMem)                                                                                                                                                       
                            icr = *tRGBAs\R * fXfs1less
                            icg = *tRGBAs\G * fXfs1less
                            icb = *tRGBAs\B * fXfs1less
                            ica = *tRGBAs\A * fXfs1less
                                                           
                            ReadPixel32 (Xs0 + 1, Ys0, BufferPitchSrc, *tRGBAs, *SrcMem)
                            icr0 = *tRGBAs\R * fXfs1 + icr
                            icg0 = *tRGBAs\G * fXfs1 + icg
                            icb0 = *tRGBAs\B * fXfs1 + icb
                            ica0 = *tRGBAs\A * fXfs1 + ica
                                                             
                            ReadPixel32 (Xs0, Ys0 + 1, BufferPitchSrc, *tRGBAs, *SrcMem)
                            icr = *tRGBAs\R * fXfs1less
                            icg = *tRGBAs\G * fXfs1less
                            icb = *tRGBAs\B * fXfs1less
                            ica = *tRGBAs\A * fXfs1less
                                                                                                   
                            ReadPixel32 (Xs0 + 1, Ys0 + 1, BufferPitchSrc, *tRGBAs, *SrcMem)
                            icr1 = *tRGBAs\R * fXfs1 + icr
                            icg1 = *tRGBAs\G * fXfs1 + icg
                            icb1 = *tRGBAs\B * fXfs1 + icb
                            ica1 = *tRGBAs\A * fXfs1 + ica
                                               
                            ; Weight along axis Y
                            tPixel\R = fYfs1less * icr0 + fYfs1 * icr1
                            tPixel\G = fYfs1less * icg0 + fYfs1 * icg1
                            tPixel\B = fYfs1less * icb0 + fYfs1 * icb1
                            tPixel\A = fYfs1less * ica0 + fYfs1 * ica1                           
                           
                            WritePixel32 (tPixel, X, Y, BufferPitchDest, *tRGBAd, *DestMem)
                        EndIf           
                    Next
                Next           
        EndSelect
 EndSelect
 CopyImageData(nDestImage, *DestMem)
   
 FreeMemory(*SrcMem)
 FreeMemory(*DestMem)
 ProcedureReturn nDestImage
 CATCH()
 If EXCEPTION = #EXC_ALLOC_ERR
    If *SrcMem <> 0 : FreeMemory(*SrcMem) : EndIf
    If *DestMem <> 0 : FreeMemory(*DestMem) : EndIf
    FreeImage(nDestImage)
 EndIf
 
 ProcedureReturn 0
EndProcedure
Procedure.i FlipImage (nSrcImage)
; Flip vertically a 24/32 bit image preserving the alpha-channel
 Protected *tRGBAs.T_RGBA, *tRGBAd.T_RGBA, tPixel.T_RGBA, RotType
 Protected *SrcMem, *DestMem, BufferPitchSrc, BufferPitchDest
 Protected SrcWidth, SrcHeight, DestWidth, DestHeight, nDestImage
 Protected X, Y
 Protected BitPlanes
   
 ; sanity checks
 If IsImage(nSrcImage) = 0
    ProcedureReturn 0
 EndIf
 BitPlanes = ImageDepth(nSrcImage)
 If BitPlanes <> 24 And BitPlanes <> 32
    ProcedureReturn 0
 EndIf
 SrcWidth = ImageWidth(nSrcImage)
 SrcHeight = ImageHeight(nSrcImage)
 DestWidth = SrcWidth
 DestHeight = SrcHeight
 ; create 24/32 bit destination image
 nDestImage = CreateImage(#PB_Any, DestWidth, DestHeight, BitPlanes)
 TRY (nDestImage)
 ; copy src image to allocated memory
 *SrcMem = AllocateImageData(nSrcImage, @BufferPitchSrc)
 TRY (*SrcMem, #EXC_ALLOC_ERR)
 ; copy dest image to allocated memory
 *DestMem = AllocateImageData(nDestImage, @BufferPitchDest)
 TRY (*DestMem, #EXC_ALLOC_ERR)
 Select BitPlanes
    Case 24
        For Y = 0 To DestHeight - 1
            For X = 0 To DestWidth - 1           
                CopyPixel24 (X, SrcHeight - Y - 1, X, Y, BufferPitchSrc, BufferPitchDest, *tRGBAs, *tRGBAd, *SrcMem, *DestMem)
            Next
         Next       
    Case 32
        For Y = 0 To DestHeight - 1
            For X = 0 To DestWidth - 1           
                CopyPixel32 (X, SrcHeight - Y - 1, X, Y, BufferPitchSrc, BufferPitchDest, *tRGBAs, *tRGBAd, *SrcMem, *DestMem)
            Next
         Next   
 EndSelect
 CopyImageData(nDestImage, *DestMem)
 FreeMemory(*SrcMem)
 FreeMemory(*DestMem)
 ProcedureReturn nDestImage
 CATCH()
 If EXCEPTION = #EXC_ALLOC_ERR
    If *SrcMem <> 0 : FreeMemory(*SrcMem) : EndIf
    If *DestMem <> 0 : FreeMemory(*DestMem) : EndIf    
    FreeImage(nDestImage) 
 EndIf
 
 ProcedureReturn 0
EndProcedure
Procedure.i MirrorImage (nSrcImage)
; Mirror horizontally a 24/32 bit image preserving the alpha-channel
 Protected *tRGBAs.T_RGBA, *tRGBAd.T_RGBA, tPixel.T_RGBA, RotType
 Protected *SrcMem, *DestMem, BufferPitchSrc, BufferPitchDest, BitPlanes
 Protected SrcWidth, SrcHeight, DestWidth, DestHeight, nDestImage
 Protected X, Y
 ; sanity checks
 If IsImage(nSrcImage) = 0
    ProcedureReturn 0
 EndIf
 BitPlanes = ImageDepth(nSrcImage)
 If BitPlanes <> 24 And BitPlanes <> 32
    ProcedureReturn 0
 EndIf
 SrcWidth = ImageWidth(nSrcImage)
 SrcHeight = ImageHeight(nSrcImage)
 DestWidth = SrcWidth
 DestHeight = SrcHeight
 ; create 24/32 bit destination image
 nDestImage = CreateImage(#PB_Any, DestWidth, DestHeight, BitPlanes)
 TRY (nDestImage)
 ; copy src image to allocated memory
 *SrcMem = AllocateImageData(nSrcImage, @BufferPitchSrc)
 TRY (*SrcMem, #EXC_ALLOC_ERR)
 ; copy dest image to allocated memory
 *DestMem = AllocateImageData(nDestImage, @BufferPitchDest)
 TRY (*DestMem, #EXC_ALLOC_ERR)
 Select BitPlanes
    Case 24
        For Y = 0 To DestHeight - 1
            For X = 0 To DestWidth - 1
                CopyPixel24 (SrcWidth - X - 1, Y, X, Y, BufferPitchSrc, BufferPitchDest, *tRGBAs, *tRGBAd, *SrcMem, *DestMem)     
            Next
        Next   
    Case 32
        For Y = 0 To DestHeight - 1
            For X = 0 To DestWidth - 1
                CopyPixel32 (SrcWidth - X - 1, Y, X, Y, BufferPitchSrc, BufferPitchDest, *tRGBAs, *tRGBAd, *SrcMem, *DestMem)     
            Next
        Next   
 EndSelect
 CopyImageData(nDestImage, *DestMem)
 FreeMemory(*SrcMem)
 FreeMemory(*DestMem)
 ProcedureReturn nDestImage
   
 CATCH()
 If EXCEPTION = #EXC_ALLOC_ERR   
    If *SrcMem <> 0 : FreeMemory(*SrcMem) : EndIf
    If *DestMem <> 0 : FreeMemory(*DestMem) : EndIf        
    FreeImage(nDestImage) 
 EndIf
 ProcedureReturn 0
EndProcedure