It is currently Fri May 24, 2013 12:39 am

All times are UTC + 1 hour




Post new topic Reply to topic  [ 45 posts ]  Go to page 1, 2, 3  Next
Author Message
 Post subject: Image Rotation routines for 24/32 bit with optional AA
PostPosted: Mon Sep 07, 2009 11:22 pm 
Offline
Addict
Addict
User avatar

Joined: Wed Aug 31, 2005 11:09 pm
Posts: 2240
Location: Italy
The following posts contain the code (1,2) and samples (3,4,5)

Potentially cross platform. Tested on Win32/64 and Linux32.

Requires PB 4.40 (written with beta 2).

- RotateImageFree (nSrcImage, fDegRot.f, flgAntiAliasing, iFillColor)

- RotateImage (nSrcImage, iDegRot) optimized for 90/180/270 degrees rotations

- FlipImage (nSrcImage) vertical flip

- MirrorImage (nSrcImage) horizontal flip (mirror)

These routines preserve the alpha channel information (see image with partially transparent circles).


EDIT: v1.01 - Streamlined a little

EDIT: v1.02 - Optimized a little for speed (15% gain for AA/32bit)

EDIT: v1.03 (let's forget about it)

EDIT: v1.04 Better, added some tweaking to make it hopefully work better with the "orthogonal" angles too.

EDIT: v1.05 Fixed dimensioning of the destination image with arbitrary angles (1 pixel off sometimes...).

EDIT: v1.06 Now 0,90,180,270 degrees angles passed to RotateImageFree() are processed by RotateImage() instead. Should have been this way from the start.


Sample image:

Image


Last edited by luis on Tue Mar 27, 2012 8:09 pm, edited 13 times in total.

Top
 Profile  
 
 Post subject:
PostPosted: Mon Sep 07, 2009 11:23 pm 
Offline
Addict
Addict
User avatar

Joined: Wed Aug 31, 2005 11:09 pm
Posts: 2240
Location: Italy
MAIN CODE - "RotateImage.pb"

Code:
;******************************************************************************
;*
;* Image Rotation routines for 24/32 bit with optional AA
;* by Luis, http://luis.no-ip.net
;* v1.06 for PB 4.60
;*
;* 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.
;*
;* ----------------------------------------------------------------------------
;*
;* RotateImageFree (nSrcImage, fDegRot.f, flgAntiAliasing, iFillColor)
;*
;*  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
;*  iFillColor          Used to fill the new areas of the resulting image
;*
;*  Return              a 24/32 bit image rotated by fDegRot
;*
;* NOTES :
;*  iFillColor is not used with 32 bit images, the new areas are always transparent.
;*
;*  iFillColor 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, iDegRot)
;*
;*  nSrcImage           The 24/32 bit PureBasic's image to rotate
;*  iDegRot             Integer angle in degrees (+/-) 90/180/270
;*
;*  Return              a 24/32 bit image rotated by iDegRot
;*
;* 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
;*
;******************************************************************************

XIncludeFile #PB_Compiler_FilePath + "RotateImage.pbi"


; support procedures

Macro JMP_IF_ZERO (var, label)
; If 'var' equals zero, then execute 'exec' and jump to 'label'
; Yes, I like GOTOs for error handling.
If var = 0: Goto label : EndIf
EndMacro


Procedure.i AllocateImageData (nImage, *iBufferPitch.Integer, iFillColor = -1)

Protected *ImageMem, *AllocMem, iBufferPitch

StartDrawing(ImageOutput(nImage))
   
  *ImageMem = DrawingBuffer()
  iBufferPitch = DrawingBufferPitch()

  If iFillColor <> -1
    Select ImageDepth(nImage)
        Case 24
            Box(0, 0, ImageWidth(nImage), ImageHeight(nImage), iFillColor)
        Case 32
            DrawingMode(#PB_2DDrawing_AlphaChannel)   
            Box(0, 0, ImageWidth(nImage), ImageHeight(nImage), $00) ; full transparent
    EndSelect
  EndIf

  *AllocMem = AllocateMemory(iBufferPitch * ImageHeight(nImage))

  If *AllocMem
    CopyMemory(*ImageMem, *AllocMem, MemorySize(*AllocMem))
    *iBufferPitch\i = iBufferPitch
  Else
    *iBufferPitch\i = 0
  EndIf

StopDrawing()

ProcedureReturn *AllocMem
EndProcedure

Procedure CopyImageData (nImage, *DestMem)
StartDrawing(ImageOutput(nImage))
 CopyMemory(*DestMem, DrawingBuffer(), MemorySize(*DestMem))
StopDrawing()
EndProcedure

; user procedures

Procedure.i RotateImage (nSrcImage, iDegRot)

; Rotate 24 bit images at (+/-) 90/180/270 degrees

; Rotate 32 bit images at (+/-) 90/180/270 degrees preserving the alpha-channel

; This one uses the DrawingBuffer() ability to work with images available in PB 4.40

Protected *tRGBAs.T_RGBA, *tRGBAd.T_RGBA, tPixel.T_RGBA, iType
Protected *SrcMem, *DestMem, iBufferPitchSrc, iBufferPitchDest
Protected iSrcWidth, iSrcHeight, iDestWidth, iDestHeight, nDestImage
Protected iX, iY, iXs, iYs
Protected iBitPlanes

; sanity checks
If IsImage(nSrcImage) = 0
    ProcedureReturn 0
EndIf

iBitPlanes = ImageDepth(nSrcImage)

If iBitPlanes <> 24 And iBitPlanes <> 32
    ProcedureReturn 0
EndIf

; sanity checks
If iDegRot % 90
    ProcedureReturn 0
EndIf

iDegRot % 360

If iDegRot = 0
    ProcedureReturn CopyImage(nSrcImage, #PB_Any)
EndIf

CompilerIf (#PB_Compiler_OS = #PB_OS_Linux)
 iDegRot = -iDegRot
CompilerEndIf

iSrcWidth = ImageWidth(nSrcImage)
iSrcHeight = ImageHeight(nSrcImage)

Select iDegRot           
    Case 90, -270
        iDestWidth = iSrcHeight
        iDestHeight = iSrcWidth
        iType = 1
    Case 180, -180
        iType = 2
        iDestWidth = iSrcWidth
        iDestHeight = iSrcHeight
    Case 270, -90
        iType = 3
        iDestWidth = iSrcHeight
        iDestHeight = iSrcWidth
EndSelect

; create 24/32 bit destination image
nDestImage = CreateImage(#PB_Any, iDestWidth, iDestHeight, iBitPlanes)
JMP_IF_ZERO (nDestImage, lbl_RotateImage_ERR)

; copy src image to allocated memory
*SrcMem = AllocateImageData(nSrcImage, @iBufferPitchSrc)
JMP_IF_ZERO (*SrcMem, lbl_RotateImage_Alloc_ERR)

; copy dest image to allocated memory
*DestMem = AllocateImageData(nDestImage, @iBufferPitchDest)
JMP_IF_ZERO (*DestMem, lbl_RotateImage_Alloc_ERR)

Select iBitPlanes
    Case 24
        For iY = 0 To iDestHeight - 1
            For iX = 0 To iDestWidth - 1
           
                Select iType
                    Case 1
                        iYs = iSrcHeight - iX - 1
                        iXs = iY
                    Case 2
                        iYs = iSrcHeight - iY - 1
                        iXs = iSrcWidth - iX - 1   
                    Case 3
                        iYs = iX
                        iXs = iSrcWidth - iY - 1
                EndSelect

                CopyPixel24 (iXs, iYs, iX, iY, iBufferPitchSrc, iBufferPitchDest, *tRGBAs, *tRGBAd, *SrcMem, *DestMem)
                           
            Next
         Next   
    Case 32
        For iY = 0 To iDestHeight - 1
            For iX = 0 To iDestWidth - 1
           
                Select iType
                    Case 1
                        iYs = iSrcHeight - iX - 1
                        iXs = iY
                    Case 2
                        iYs = iSrcHeight - iY - 1
                        iXs = iSrcWidth - iX - 1   
                    Case 3
                        iYs = iX
                        iXs = iSrcWidth - iY - 1
                EndSelect

                CopyPixel32 (iXs, iYs, iX, iY, iBufferPitchSrc, iBufferPitchDest, *tRGBAs, *tRGBAd, *SrcMem, *DestMem)                   
                           
            Next
         Next   
EndSelect

CopyImageData(nDestImage, *DestMem)
   
FreeMemory(*SrcMem)
FreeMemory(*DestMem)

ProcedureReturn nDestImage

       
lbl_RotateImage_Alloc_ERR:
; check if one was successfull and free it
If *SrcMem <> 0 : FreeMemory(*SrcMem) : EndIf
If *DestMem <> 0 : FreeMemory(*DestMem) : EndIf

; image was already created, free it
FreeImage(nDestImage)

lbl_RotateImage_ERR:
ProcedureReturn 0

EndProcedure


Procedure.i RotateImageFree (nSrcImage, fDegRot.f, flgAntiAliasing, iFillColor = $ffffff)
; 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

; Rotate 24 bit images at any angle optionally with anti-aliasing filling the new area
; of the resulting image with the specified color

; Rotate 32 bit images at any angle optionally with anti-aliasing preserving the alpha-channel

; This one uses the DrawingBuffer() ability to work with images available in PB 4.40

Protected *tRGBAs.T_RGBA, *tRGBAd.T_RGBA, tPixel.T_RGBA
Protected *SrcMem, *DestMem, iBufferPitchSrc, iBufferPitchDest
Protected fzCos.f, fzSin.f
Protected iSrcWidth, iSrcHeight, iDestWidth, iDestHeight, nDestImage
Protected iX, iY, iXs, iYs, iXc1, iYc1, iXc2, iYc2, iColor
Protected iBitPlanes
Protected XRoundFix, YRoundFix

; sanity checks
If IsImage(nSrcImage) = 0
    ProcedureReturn 0
EndIf

iBitPlanes = ImageDepth(nSrcImage)

If iBitPlanes <> 24 And iBitPlanes <> 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

If fDegRot > 270.0
    XRoundFix = -1
    YRoundFix = 0
ElseIf fDegRot > 180.0
    XRoundFix = -1
    YRoundFix = -1
ElseIf fDegRot > 90.0
    XRoundFix = 0
    YRoundFix = -1
EndIf


CompilerIf (#PB_Compiler_OS = #PB_OS_Linux)
 fDegRot = -fDegRot
CompilerEndIf

fzCos = Cos(Radian(fDegRot))
fzSin = Sin(Radian(fDegRot))

iSrcWidth = ImageWidth(nSrcImage)
iSrcHeight = ImageHeight(nSrcImage)

iDestWidth = Int(iSrcWidth * Abs(fzCos) + iSrcHeight * Abs(fzSin) + 0.9)
iDestHeight = Int(iSrcHeight * Abs(fzCos) + iSrcWidth * Abs(fzSin) + 0.9)

iXc1 = iSrcWidth / 2
iYc1 = iSrcHeight / 2
iXc2 = iDestWidth / 2
iYc2 = iDestHeight / 2

; create 24/32 bit destination image
nDestImage = CreateImage(#PB_Any, iDestWidth, iDestHeight, iBitPlanes)
JMP_IF_ZERO (nDestImage, lbl_RotateImageFree_ERR)

; copy src image to allocated memory
*SrcMem = AllocateImageData (nSrcImage, @iBufferPitchSrc)
JMP_IF_ZERO (*SrcMem, lbl_RotateImageFree_Alloc_ERR)

; copy dest image to allocated memory and fill with backcolor
*DestMem = AllocateImageData(nDestImage, @iBufferPitchDest, iFillColor)
JMP_IF_ZERO (*DestMem, lbl_RotateImageFree_Alloc_ERR)

     
Select flgAntiAliasing

    Case #False   
   
        Select iBitPlanes
            Case 24
     
                For iY = 0 To iDestHeight - 1
                    For iX = 0 To iDestWidth - 1
                   
                        ; For each nDestImage point find rotated nSrcImage source point
                        iXs = iXc1 + (iX - iXc2) * fzCos + (iY - iYc2) * fzSin + XRoundFix
                        iYs = iYc1 + (iY - iYc2) * fzCos - (iX - iXc2) * fzSin + YRoundFix
                                   
                        If iXs >= 0 And iXs < iSrcWidth  And iYs >= 0 And iYs < iSrcHeight
                            ; Move valid rotated nSrcImage source points to nDestImage                   
                            CopyPixel24 (iXs, iYs, iX, iY, iBufferPitchSrc, iBufferPitchDest, *tRGBAs, *tRGBAd, *SrcMem, *DestMem)
                        EndIf
                                   
                    Next
                Next   
           
            Case 32
           
                For iY = 0 To iDestHeight - 1
                    For iX = 0 To iDestWidth - 1
                        ; For each nDestImage point find rotated nSrcImage source point
                        iXs = iXc1 + (iX - iXc2) * fzCos + (iY - iYc2) * fzSin + XRoundFix
                        iYs = iYc1 + (iY - iYc2) * fzCos - (iX - iXc2) * fzSin + YRoundFix
                                   
                        If iXs >= 0 And iXs < iSrcWidth  And iYs >= 0 And iYs < iSrcHeight
                            ; Move valid rotated nSrcImage source points to nDestImage                   
                            CopyPixel32 (iXs, iYs, iX, iY, iBufferPitchSrc, iBufferPitchDest, *tRGBAs, *tRGBAd, *SrcMem, *DestMem)
                        EndIf
                                   
                    Next
                Next   
                                     
        EndSelect
         
    Case #True
        Protected iXs0, iYs0, icr, icg, icb, icr0, icg0, icb0, icr1, icg1, icb1
        Protected fXs.f, fYs.f, fXfs1.f, fYfs1.f
        Protected fXfs1less.f, fYfs1less.f

        Select iBitPlanes
           
            Case 24
           
                For iY = 0 To iDestHeight - 1
                    For iX = 0 To iDestWidth - 1
           
                        ; For each nDestImage point find rotated nSrcImage source point
                        fXs = iXc1 + (iX - iXc2) * fzCos + (iY - iYc2) * fzSin + XRoundFix
                        fYs = iYc1 + (iY - iYc2) * fzCos - (iX - iXc2) * fzSin + YRoundFix
             
                        ; Bottom left coords of bounding floating point rectangle on nSrcImage
                        iXs0 = Int(fXs)   
                        iYs0 = Int(fYs)
                           
                        If iXs0 >= 0 And iXs0 <= iSrcWidth -1 And iYs0 >= 0 And iYs0 <= iSrcHeight - 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 (iXs0, iYs0, iBufferPitchSrc, *tRGBAs, *SrcMem)                                                                                                                               
                            icr = *tRGBAs\R * fXfs1less
                            icg = *tRGBAs\G * fXfs1less
                            icb = *tRGBAs\B * fXfs1less
                               
                            ReadPixel24 (iXs0 + 1, iYs0, iBufferPitchSrc, *tRGBAs, *SrcMem)
                            icr0 = *tRGBAs\R * fXfs1 + icr
                            icg0 = *tRGBAs\G * fXfs1 + icg
                            icb0 = *tRGBAs\B * fXfs1 + icb
                                 
                            ReadPixel24 (iXs0, iYs0 + 1, iBufferPitchSrc, *tRGBAs, *SrcMem)
                            icr = *tRGBAs\R * fXfs1less
                            icg = *tRGBAs\G * fXfs1less
                            icb = *tRGBAs\B * fXfs1less
                                                                       
                            ReadPixel24 (iXs0 + 1, iYs0 + 1, iBufferPitchSrc, *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, iX, iY, iBufferPitchDest, *tRGBAd, *DestMem)                               
                        EndIf           
                    Next
                Next           
           
            Case 32

                Protected ica, ica0, ica1
               
                For iY = 0 To iDestHeight - 1
                    For iX = 0 To iDestWidth - 1
           
                        ; For each nDestImage point find rotated nSrcImage source point
                        fXs = iXc1 + (iX - iXc2) * fzCos + (iY - iYc2) * fzSin + XRoundFix
                        fYs = iYc1 + (iY - iYc2) * fzCos - (iX - iXc2) * fzSin + YRoundFix
             
                        ; Bottom left coords of bounding floating point rectangle on nSrcImage
                        iXs0 = Int(fXs)   
                        iYs0 = Int(fYs)                       

                        If iXs0 >= 0 And iXs0 <= iSrcWidth - 1 And iYs0 >= 0 And iYs0 < iSrcHeight - 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 (iXs0, iYs0, iBufferPitchSrc, *tRGBAs, *SrcMem)                                                                                                                                                       
                            icr = *tRGBAs\R * fXfs1less
                            icg = *tRGBAs\G * fXfs1less
                            icb = *tRGBAs\B * fXfs1less
                            ica = *tRGBAs\A * fXfs1less
                                                           
                            ReadPixel32 (iXs0 + 1, iYs0, iBufferPitchSrc, *tRGBAs, *SrcMem)
                            icr0 = *tRGBAs\R * fXfs1 + icr
                            icg0 = *tRGBAs\G * fXfs1 + icg
                            icb0 = *tRGBAs\B * fXfs1 + icb
                            ica0 = *tRGBAs\A * fXfs1 + ica
                                                             
                            ReadPixel32 (iXs0, iYs0 + 1, iBufferPitchSrc, *tRGBAs, *SrcMem)
                            icr = *tRGBAs\R * fXfs1less
                            icg = *tRGBAs\G * fXfs1less
                            icb = *tRGBAs\B * fXfs1less
                            ica = *tRGBAs\A * fXfs1less
                                                                                                   
                            ReadPixel32 (iXs0 + 1, iYs0 + 1, iBufferPitchSrc, *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, iX, iY, iBufferPitchDest, *tRGBAd, *DestMem)
                        EndIf           
                    Next
                Next           

        EndSelect
EndSelect

CopyImageData (nDestImage, *DestMem)
   
FreeMemory(*SrcMem)
FreeMemory(*DestMem)

ProcedureReturn nDestImage

       
lbl_RotateImageFree_Alloc_ERR:
; check if one was successfull and free it
If *SrcMem <> 0 : FreeMemory(*SrcMem) : EndIf
If *DestMem <> 0 : FreeMemory(*DestMem) : EndIf

; image was already created, free it
FreeImage(nDestImage)

lbl_RotateImageFree_ERR:
ProcedureReturn 0

EndProcedure


Procedure.i FlipImage (nSrcImage)

; Flip vertically a 24/32 bit image preserving the alpha-channel

; This one uses the DrawingBuffer() ability to work with images available in PB 4.40

Protected *tRGBAs.T_RGBA, *tRGBAd.T_RGBA, tPixel.T_RGBA, iType
Protected *SrcMem, *DestMem, iBufferPitchSrc, iBufferPitchDest
Protected iSrcWidth, iSrcHeight, iDestWidth, iDestHeight, nDestImage
Protected iX, iY
Protected iBitPlanes
   
; sanity checks
If IsImage(nSrcImage) = 0
    ProcedureReturn 0
EndIf

iBitPlanes = ImageDepth(nSrcImage)

If iBitPlanes <> 24 And iBitPlanes <> 32
    ProcedureReturn 0
EndIf

iSrcWidth = ImageWidth(nSrcImage)
iSrcHeight = ImageHeight(nSrcImage)

iDestWidth = iSrcWidth
iDestHeight = iSrcHeight


; create 24/32 bit destination image
nDestImage = CreateImage(#PB_Any, iDestWidth, iDestHeight, iBitPlanes)
JMP_IF_ZERO (nDestImage, lbl_FlipImage_ERR)

; copy src image to allocated memory
*SrcMem = AllocateImageData(nSrcImage, @iBufferPitchSrc)
JMP_IF_ZERO (*SrcMem, lbl_FlipImage_Alloc_ERR)

; copy dest image to allocated memory
*DestMem = AllocateImageData(nDestImage, @iBufferPitchDest)
JMP_IF_ZERO (*DestMem, lbl_FlipImage_Alloc_ERR)

Select iBitPlanes
    Case 24
        For iY = 0 To iDestHeight - 1
            For iX = 0 To iDestWidth - 1           
                CopyPixel24 (iX, iSrcHeight - iY - 1, iX, iY, iBufferPitchSrc, iBufferPitchDest, *tRGBAs, *tRGBAd, *SrcMem, *DestMem)
            Next
         Next       
    Case 32
        For iY = 0 To iDestHeight - 1
            For iX = 0 To iDestWidth - 1           
                CopyPixel32 (iX, iSrcHeight - iY - 1, iX, iY, iBufferPitchSrc, iBufferPitchDest, *tRGBAs, *tRGBAd, *SrcMem, *DestMem)
            Next
         Next   
EndSelect

CopyImageData(nDestImage, *DestMem)

FreeMemory(*SrcMem)
FreeMemory(*DestMem)

ProcedureReturn nDestImage

       
lbl_FlipImage_Alloc_ERR:
; check if one was successfull and free it
If *SrcMem <> 0 : FreeMemory(*SrcMem) : EndIf
If *DestMem <> 0 : FreeMemory(*DestMem) : EndIf

; image was already created, free it
FreeImage(nDestImage)

lbl_FlipImage_ERR:
ProcedureReturn 0

EndProcedure

Procedure.i MirrorImage (nSrcImage)

; Mirror horizontally a 24/32 bit image preserving the alpha-channel

; This one uses the DrawingBuffer() ability to work with images available in PB 4.40

Protected *tRGBAs.T_RGBA, *tRGBAd.T_RGBA, tPixel.T_RGBA, iType
Protected *SrcMem, *DestMem, iBufferPitchSrc, iBufferPitchDest
Protected iSrcWidth, iSrcHeight, iDestWidth, iDestHeight, nDestImage
Protected iX, iY
Protected iBitPlanes

; sanity checks
If IsImage(nSrcImage) = 0
    ProcedureReturn 0
EndIf

iBitPlanes = ImageDepth(nSrcImage)

If iBitPlanes <> 24 And iBitPlanes <> 32
    ProcedureReturn 0
EndIf

iSrcWidth = ImageWidth(nSrcImage)
iSrcHeight = ImageHeight(nSrcImage)

iDestWidth = iSrcWidth
iDestHeight = iSrcHeight


; create 24/32 bit destination image
nDestImage = CreateImage(#PB_Any, iDestWidth, iDestHeight, iBitPlanes)
JMP_IF_ZERO (nDestImage, lbl_MirrorImage_ERR)

; copy src image to allocated memory
*SrcMem = AllocateImageData(nSrcImage, @iBufferPitchSrc)
JMP_IF_ZERO (*SrcMem, lbl_MirrorImage_Alloc_ERR)

; copy dest image to allocated memory
*DestMem = AllocateImageData(nDestImage, @iBufferPitchDest)
JMP_IF_ZERO (*DestMem,lbl_MirrorImage_Alloc_ERR)

Select iBitPlanes
    Case 24
        For iY = 0 To iDestHeight - 1
            For iX = 0 To iDestWidth - 1
                CopyPixel24 (iSrcWidth - iX - 1, iY, iX, iY, iBufferPitchSrc, iBufferPitchDest, *tRGBAs, *tRGBAd, *SrcMem, *DestMem)     
            Next
        Next   
    Case 32
        For iY = 0 To iDestHeight - 1
            For iX = 0 To iDestWidth - 1
                CopyPixel32 (iSrcWidth - iX - 1, iY, iX, iY, iBufferPitchSrc, iBufferPitchDest, *tRGBAs, *tRGBAd, *SrcMem, *DestMem)     
            Next
        Next   
EndSelect

CopyImageData(nDestImage, *DestMem)

FreeMemory(*SrcMem)
FreeMemory(*DestMem)

ProcedureReturn nDestImage

       
lbl_MirrorImage_Alloc_ERR:
; check if one was successfull and free it
If *SrcMem <> 0 : FreeMemory(*SrcMem) : EndIf
If *DestMem <> 0 : FreeMemory(*DestMem) : EndIf

; image was already created, free it
FreeImage(nDestImage)

lbl_MirrorImage_ERR:
ProcedureReturn 0

EndProcedure


Last edited by luis on Tue Mar 27, 2012 8:01 pm, edited 10 times in total.

Top
 Profile  
 
 Post subject:
PostPosted: Mon Sep 07, 2009 11:24 pm 
Offline
Addict
Addict
User avatar

Joined: Wed Aug 31, 2005 11:09 pm
Posts: 2240
Location: Italy
INCLUDE FOR THE CODE ABOVE - "RotateImage.pbi"

Code:
; RotateImage.pbi

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 (iXs, iYs, iXd, iYd, iBufferPitchSrc, iBufferPitchDest, ptRGBAs, ptRGBAd, pMemSrc, pMemDest)
 ptRGBAs = pMemSrc  + (iYs) * iBufferPitchSrc + (iXs) << 2
 ptRGBAd = pMemDest + (iYd) * iBufferPitchDest + (iXd) << 2       
 ptRGBAd\R = ptRGBAs\R
 ptRGBAd\G = ptRGBAs\G
 ptRGBAd\B = ptRGBAs\B       
 ptRGBAd\A = ptRGBAs\A
EndMacro

Macro CopyPixel24 (iXs, iYs, iXd, iYd, iBufferPitchSrc, iBufferPitchDest, ptRGBAs, ptRGBAd, pMemSrc, pMemDest)
 ptRGBAs = pMemSrc  + (iYs) * iBufferPitchSrc + (iXs) * 3
 ptRGBAd = pMemDest + (iYd) * iBufferPitchDest + (iXd) * 3       
 ptRGBAd\R = ptRGBAs\R
 ptRGBAd\G = ptRGBAs\G
 ptRGBAd\B = ptRGBAs\B       
EndMacro

Macro ReadPixel32 (iX, iY, iBufferPitchSrc, ptRGBA, pMemSrc)
 ptRGBA = pMemSrc + (iY) * iBufferPitchSrc + (iX) << 2
EndMacro

Macro ReadPixel24 (iX, iY, iBufferPitchSrc, ptRGBA, pMemSrc)
 ptRGBA = pMemSrc + (iY) * iBufferPitchSrc + (iX) * 3
EndMacro

Macro WritePixel32 (tPixel, iX, iY, iBufferPitchDest, ptRGBA, pMemDest)
 ptRGBA = pMemDest + (iY) * iBufferPitchDest + (iX) << 2                       
 ptRGBA\R = tPixel\R
 ptRGBA\G = tPixel\G
 ptRGBA\B = tPixel\B
 ptRGBA\A = tPixel\A
EndMacro

Macro WritePixel24 (tPixel, iX, iY, iBufferPitchDest, ptRGBA, pMemDest)
 ptRGBA = pMemDest + (iY) * iBufferPitchDest + (iX) * 3
 ptRGBA\R = tPixel\R
 ptRGBA\G = tPixel\G
 ptRGBA\B = tPixel\B
EndMacro


Last edited by luis on Tue Mar 27, 2012 4:40 pm, edited 4 times in total.

Top
 Profile  
 
 Post subject:
PostPosted: Mon Sep 07, 2009 11:25 pm 
Offline
Addict
Addict
User avatar

Joined: Wed Aug 31, 2005 11:09 pm
Posts: 2240
Location: Italy
TEST 1

Code:
; TEST - RotateImageFree

EnableExplicit

IncludeFile "RotateImage.pb"

Procedure.i CreateCheckers (iWidth, iHeight)
 Protected x, y
 Protected nImage, iSize = 12
 
 nImage = CreateImage(#PB_Any, iWidth, iHeight, 24)
 
 If nImage
    StartDrawing(ImageOutput(nImage))
   
     Box(0, 0, iWidth, iHeight, $FFFFFF)
     
     While y < iHeight + iSize
        While x < iWidth + iSize
            Box(x, y, iSize, iSize, $C0C0C0)
            Box(x + iSize, y + iSize, iSize, iSize, $C0C0C0)
            x + iSize*2           
        Wend
        x = 0
        y + iSize*2
     Wend
     
    StopDrawing()
 EndIf
 
 ProcedureReturn nImage
EndProcedure

Procedure.i MergeImages (nImgCheck, nImgOut)
 Protected nImage
 
 nImage = CreateImage(#PB_Any, ImageWidth(nImgCheck), ImageHeight(nImgCheck), 24)

 If nImage
    StartDrawing(ImageOutput(nImage))
   
     DrawImage(ImageID(nImgCheck), 0, 0)
     DrawAlphaImage(ImageID(nImgOut), 0, 0)
         
    StopDrawing()
 EndIf
 
 ProcedureReturn nImage

EndProcedure


#WIN = 1
#IMG = 1

#IW = 640
#IH = 480

Define iEvent, iTime1, k, nFont
Define nImgSrc, nImgOut
Define nImgCheck, nImgDisp


; ****************************************************************
; try to change these vars to check out the various combinations *
; ****************************************************************

Define iBitPlanes = 32                  ; test 24 or 32 bpp image
Define fRot = 33.0                      ; angle used for rotation
Define flgAntiAlias = 1                 ; Antialias (1) or simpler rotation (0)
Define iFillColor = RGB(255,255,255)    ; useful for transparent masking using GDI or simply to fill the background


nFont = LoadFont(#PB_Any, "Arial", 12, #PB_Font_Bold)

nImgSrc = CreateImage(#PB_Any, #IW, #IH, iBitPlanes)
       
StartDrawing(ImageOutput(nImgSrc))
 
 For k = 0 To 100 Step 20
    Box(0 + k, 0 + k, #IW - k * 2, #IH - k * 2, RGB(k + 50, k + 100, k + 150))
 Next

 DrawingMode(#PB_2DDrawing_Transparent)
 DrawingFont(FontID(nFont))
 
 For k = 0 To #IH Step #IH / 10
    DrawText(20, k + 10, "TEXT - " + Str(iBitPlanes) + " BPP IMAGE - TEXT", RGB(k/3, k/2, k/2))
 Next

 If iBitPlanes = 24
    DrawingMode(#PB_2DDrawing_Default)               
    Circle(#IW - 200, 100, 50, RGB(128,0,0))
    Circle(#IW - 200, #IH - 100, 50, RGB(0,128,0))   
    Circle(#IW - 80, #IH/2, 50, RGB(0,0,128))       
 Else
    iFillColor = 0
   
    ; make 3 holes
    DrawingMode(#PB_2DDrawing_AlphaChannel)       
    Circle(#IW - 200, 100, 50,  $00)
    Circle(#IW - 200, #IH - 100, 50, $00)
    Circle(#IW - 80, #IH/2, 50, $00)
   
    ; fill them with semi-transparent circles
    DrawingMode(#PB_2DDrawing_AlphaBlend)
    Circle(#IW - 200, 100, 50, RGBA(128,0,0,100))
    Circle(#IW - 200, #IH - 100, 50, RGBA(0,128,0,100))
    Circle(#IW - 80, #IH/2, 50, RGBA(0,0,128,100))       
 EndIf
   
StopDrawing()


;;UsePNGImageDecoder()   
;;nImgSrc = LoadImage(#PB_Any, "test24.bmp")
;;nImgSrc = LoadImage(#PB_Any, "test32.png")


If OpenWindow(#WIN, 0, 0, 800, 800, "", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
           
    iTime1 = ElapsedMilliseconds()         
    nImgOut = RotateImageFree (nImgSrc, fRot, flgAntiAlias, iFillColor)
    iTime1 = ElapsedMilliseconds() - iTime1
   
    SetWindowTitle(#WIN, "Test " + Str(iBitPlanes) + " bit, AA = " + Str(flgAntiAlias) + ", msec = " + Str(iTime1) + ", deg = " + StrF(fRot,1))

    nImgCheck = CreateCheckers(ImageWidth(nImgOut), ImageHeight(nImgOut))
    nImgDisp = MergeImages(nImgCheck, nImgOut)
       
    FreeImage(nImgOut)
    FreeImage(nImgCheck)
       
    ImageGadget(#IMG, 0, 0, 0, 0, ImageID(nImgDisp), #PB_Image_Border)
   
    While iEvent <> #PB_Event_CloseWindow           
        iEvent = WaitWindowEvent()
    Wend
               
    FreeImage(nImgDisp)
    FreeImage(nImgSrc)
    FreeFont(nFont)
EndIf



Top
 Profile  
 
 Post subject:
PostPosted: Mon Sep 07, 2009 11:26 pm 
Offline
Addict
Addict
User avatar

Joined: Wed Aug 31, 2005 11:09 pm
Posts: 2240
Location: Italy
TEST 2

Code:
; TEST - RotateImage

EnableExplicit

IncludeFile "RotateImage.pb"

Procedure.i CreateCheckers (iWidth, iHeight)
 Protected x, y
 Protected nImage, iSize = 12
 
 nImage = CreateImage(#PB_Any, iWidth, iHeight, 24)
 
 If nImage
    StartDrawing(ImageOutput(nImage))
   
     Box(0, 0, iWidth, iHeight, $FFFFFF)
     
     While y < iHeight + iSize
        While x < iWidth + iSize
            Box(x, y, iSize, iSize, $C0C0C0)
            Box(x + iSize, y + iSize, iSize, iSize, $C0C0C0)
            x + iSize*2           
        Wend
        x = 0
        y + iSize*2
     Wend
     
    StopDrawing()
 EndIf
 
 ProcedureReturn nImage
EndProcedure

Procedure.i MergeImages (nImgCheck, nImgOut)
 Protected nImage
 
 nImage = CreateImage(#PB_Any, ImageWidth(nImgCheck), ImageHeight(nImgCheck), 24)

 If nImage
    StartDrawing(ImageOutput(nImage))
   
     DrawImage(ImageID(nImgCheck), 0, 0)
     DrawAlphaImage(ImageID(nImgOut), 0, 0)
         
    StopDrawing()
 EndIf
 
 ProcedureReturn nImage

EndProcedure


#WIN = 1
#IMG = 1

#IW = 640
#IH = 480

Define iEvent, iTime1, k, nFont
Define nImgSrc, nImgOut
Define nImgCheck, nImgDisp


; ****************************************************************
; try to change these vars to check out the various combinations *
; ****************************************************************

Define iBitPlanes = 32          ; test 24 or 32 bpp image
Define iRot = 90                ; angle used for rotation (+/-) 90, 180, 270


nFont = LoadFont(#PB_Any, "Arial", 14, #PB_Font_Bold)

nImgSrc = CreateImage(#PB_Any, #IW, #IH, iBitPlanes)
       
StartDrawing(ImageOutput(nImgSrc))
 
 For k = 0 To 100 Step 20
    Box(0 + k, 0 + k, #IW - k * 2, #IH - k * 2, RGB(k + 50, k + 100, k + 150))
 Next

 DrawingMode(#PB_2DDrawing_Transparent)
 DrawingFont(FontID(nFont))
 
 For k = 0 To #IH Step #IH / 10
    DrawText(20, k + 8, "Text - " + Str(iBitPlanes) + " BPP image - Text", RGB(k/3, k/2, k/2))
 Next

 If iBitPlanes = 24       
    DrawingMode(#PB_2DDrawing_Default)               
    Circle(#IW - 200, 100, 50, RGB(128,0,0))
    Circle(#IW - 200, #IH - 100, 50, RGB(0,128,0))   
    Circle(#IW - 80, #IH/2, 50, RGB(0,0,128))       
 Else
    ; make 3 holes
    DrawingMode(#PB_2DDrawing_AlphaChannel)   
    Circle(#IW - 80, #IH/2, 50, $00)
    Circle(#IW - 200, 100, 50,  $00)
    Circle(#IW - 200, #IH - 100, 50, $00)
   
    ; fill them with semi-transparent circles
    DrawingMode(#PB_2DDrawing_AlphaBlend)
    Circle(#IW - 200, 100, 50, RGBA(128,0,0,100))
    Circle(#IW - 200, #IH - 100, 50, RGBA(0,128,0,100))
    Circle(#IW - 80, #IH/2, 50, RGBA(0,0,128,100))       
 EndIf
 
StopDrawing()

   
;;nImgSrc = LoadImag;;UsePNGImageDecoder()   
;;nImgSrc = LoadImage(#PB_Any, "test24.bmp")
;;nImgSrc = LoadImage(#PB_Any, "test32.png")


If OpenWindow(#WIN, 0, 0, 800, 800, "", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
           
    iTime1 = ElapsedMilliseconds()         
    nImgOut = RotateImage (nImgSrc, iRot)
    iTime1 = ElapsedMilliseconds() - iTime1
   
    If nImgOut = 0 : MessageRequester("Test", "Invalid angle ? (" + Str(iRot) + ")") :End: EndIf
   
    SetWindowTitle(#WIN, "Test " + Str(iBitPlanes) + " bit, msec = " + Str(iTime1) + ", deg = " + Str(iRot))

    nImgCheck = CreateCheckers(ImageWidth(nImgOut), ImageHeight(nImgOut))
    nImgDisp = MergeImages(nImgCheck, nImgOut)
       
    FreeImage(nImgOut)
    FreeImage(nImgCheck)
       
    ImageGadget(#IMG, 0, 0, 0, 0, ImageID(nImgDisp), #PB_Image_Border)
   
    While iEvent <> #PB_Event_CloseWindow           
        iEvent = WaitWindowEvent()
    Wend
               
    FreeImage(nImgDisp)
    FreeImage(nImgSrc)
    FreeFont(nFont)
EndIf



Top
 Profile  
 
 Post subject:
PostPosted: Mon Sep 07, 2009 11:26 pm 
Offline
Addict
Addict
User avatar

Joined: Wed Aug 31, 2005 11:09 pm
Posts: 2240
Location: Italy
TEST 3

Code:
; TEST - Flip and Mirror

EnableExplicit

IncludeFile "RotateImage.pb"

Procedure.i CreateCheckers (iWidth, iHeight)
 Protected x, y
 Protected nImage, iSize = 12
 
 nImage = CreateImage(#PB_Any, iWidth, iHeight, 24)
 
 If nImage
    StartDrawing(ImageOutput(nImage))
   
     Box(0, 0, iWidth, iHeight, $FFFFFF)
     
     While y < iHeight + iSize
        While x < iWidth + iSize
            Box(x, y, iSize, iSize, $C0C0C0)
            Box(x + iSize, y + iSize, iSize, iSize, $C0C0C0)
            x + iSize*2           
        Wend
        x = 0
        y + iSize*2
     Wend
     
    StopDrawing()
 EndIf
 
 ProcedureReturn nImage
EndProcedure

Procedure.i MergeImages (nImgCheck, nImgOut)
 Protected nImage
 
 nImage = CreateImage(#PB_Any, ImageWidth(nImgCheck), ImageHeight(nImgCheck), 24)

 If nImage
    StartDrawing(ImageOutput(nImage))
   
     DrawImage(ImageID(nImgCheck), 0, 0)
     DrawAlphaImage(ImageID(nImgOut), 0, 0)
         
    StopDrawing()
 EndIf
 
 ProcedureReturn nImage

EndProcedure


#WIN   = 1
#IMG_1 = 1
#IMG_2 = 2

#IW = 512
#IH = 384

Define iEvent, iTime1, k, nFont, sTitle.s
Define nImgSrc, nImgOut
Define nImgCheck, nImgDisp1, nImgDisp2


; ****************************************************************
; try to change these vars to check out the various combinations *
; ****************************************************************

Define iBitPlanes = 32          ; test 24 or 32 bpp image
Define flgFlipOrMirror = 1      ; 1 = FlipImage() everything else MirrorImage()


nFont = LoadFont(#PB_Any, "Arial", 12, #PB_Font_Bold)

nImgSrc = CreateImage(#PB_Any, #IW, #IH, iBitPlanes)
       
StartDrawing(ImageOutput(nImgSrc))
 
 For k = 0 To 100 Step 20
    Box(0 + k, 0 + k, #IW - k * 2, #IH - k * 2, RGB(k + 50, k + 100, k + 150))
 Next

 DrawingMode(#PB_2DDrawing_Transparent)
 DrawingFont(FontID(nFont))
 
 For k = 0 To #IH Step #IH / 10
    DrawText(20, k + 8, "Text - " + Str(iBitPlanes) + " BPP image - Text", RGB(k/3, k/2, k/2))
 Next

 If iBitPlanes = 24       
    DrawingMode(#PB_2DDrawing_Default)               
    Circle(#IW - 180, 100, 50, RGB(128,0,0))
    Circle(#IW - 180, #IH - 100, 50, RGB(0,128,0))   
    Circle(#IW - 80, #IH/2, 50, RGB(0,0,128))       
 Else
    ; make 3 holes
    DrawingMode(#PB_2DDrawing_AlphaChannel)       
    Circle(#IW - 180, 100, 50,  $00)
    Circle(#IW - 180, #IH - 100, 50, $00)
    Circle(#IW - 80, #IH/2, 50, $00)
   
    ; fill them with semi-transparent circles
    DrawingMode(#PB_2DDrawing_AlphaBlend)
    Circle(#IW - 180, 100, 50, RGBA(128,0,0,100))
    Circle(#IW - 180, #IH - 100, 50, RGBA(0,128,0,100))
    Circle(#IW - 80, #IH/2, 50, RGBA(0,0,128,100))       
 EndIf
 
StopDrawing()

   
;;UsePNGImageDecoder()   
;;nImgSrc = LoadImage(#PB_Any, "test24.bmp")
;;nImgSrc = LoadImage(#PB_Any, "test32.png")


If OpenWindow(#WIN, 0, 0, 600, 800, "", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
           
    iTime1 = ElapsedMilliseconds()         

    If flgFlipOrMirror = 1       
        nImgOut = FlipImage(nImgSrc)
    Else
        nImgOut = MirrorImage(nImgSrc)
    EndIf   
   
    iTime1 = ElapsedMilliseconds() - iTime1
   
    If flgFlipOrMirror = 1
        sTitle = "Test FlipImage(), "
    Else
        sTitle = "Test MirrorImage(), "
    EndIf   
                       
    SetWindowTitle(#WIN, sTitle + Str(iBitPlanes) + " bit, msec = " + Str(iTime1))

    nImgCheck = CreateCheckers(ImageWidth(nImgOut), ImageHeight(nImgOut))
   
    nImgDisp1 = MergeImages(nImgCheck, nImgSrc)
    nImgDisp2 = MergeImages(nImgCheck, nImgOut)
       
    FreeImage(nImgOut)
    FreeImage(nImgCheck)
   
    ImageGadget(#IMG_1, 0, 0, 0, 0, ImageID(nImgDisp1), #PB_Image_Border)   
    ImageGadget(#IMG_2, 0, 400, 0, 0, ImageID(nImgDisp2), #PB_Image_Border)
   
    While iEvent <> #PB_Event_CloseWindow           
        iEvent = WaitWindowEvent()
    Wend
   
    FreeImage(nImgDisp1)           
    FreeImage(nImgDisp2)
    FreeImage(nImgSrc)
    FreeFont(nFont)
EndIf



Last edited by luis on Sat Sep 12, 2009 11:54 pm, edited 1 time in total.

Top
 Profile  
 
 Post subject: Re: Image Rotation routines for 24/32 bit with optional AA
PostPosted: Fri Sep 11, 2009 3:55 am 
Offline
Addict
Addict
User avatar

Joined: Wed Feb 28, 2007 9:13 am
Posts: 923
Location: Edinburgh
Thanks for this, Luis. The results are very impressive!

I've been trying to make the AA code work on its own, but no success so far. Is it dependent on the image being rotated?

In the AA code, I changed these lines:
Code:
For iY = 0 To iDestHeight - 1
            For iX = 0 To iDestWidth - 1
                ; For each nDestImage point find rotated nSrcImage source point
                fXs = iXc1 + (iX - iXc2) * fzCos + (iY - iYc2) * fzSin
                fYs = iYc1 + (iY - iYc2) * fzCos - (iX - iXc2) * fzSin
to
Code:
For iY = 0 To iDestHeight - 1
            For iX = 0 To iDestWidth - 1
                ; For each nDestImage point find rotated nSrcImage source point
                fXs = iX
                fYs = iY
to "cancel out" any rotation, because I just want the antialiasing. However, the image comes out exactly the same as it went in!


Top
 Profile  
 
 Post subject: Re: Image Rotation routines for 24/32 bit with optional AA
PostPosted: Fri Sep 11, 2009 10:32 am 
Offline
Enthusiast
Enthusiast
User avatar

Joined: Fri Apr 27, 2007 12:38 pm
Posts: 528
Location: Finland
Seymour Clufley wrote:
to "cancel out" any rotation, because I just want the antialiasing. However, the image comes out exactly the same as it went in!

That's because anti-aliasing (sort of) depends on blending adjacent pixels together. Your code blends pixels with themselves only, which results in the same output as the original. What you need to do is check the color values of the surrounding pixels of each pixel in your image and manipulate the central pixel based on those values. One way is to use a weighted average of the pixels. Or you can use PB's own AlphaBlend() function to blend the pixels.

This isn't real anti-aliasing though, it's more of a softening or smoothing filter. It would be nice to have PureBasic support matrix filters for images natively :)


Top
 Profile  
 
 Post subject: Re: Image Rotation routines for 24/32 bit with optional AA
PostPosted: Fri Sep 11, 2009 10:43 am 
Offline
Enthusiast
Enthusiast
User avatar

Joined: Fri Apr 27, 2007 12:38 pm
Posts: 528
Location: Finland
Oh and thanks to luis for the code! One note though, I think it could be optimized a lot by moving all of the select/case-blocks out of the loops, and making own loops 24/32 bits (so that you don't have to check the bits for each pixel, which is what slows it down).


Top
 Profile  
 
 Post subject: Re: Image Rotation routines for 24/32 bit with optional AA
PostPosted: Fri Sep 11, 2009 2:20 pm 
Offline
Addict
Addict
User avatar

Joined: Wed Aug 31, 2005 11:09 pm
Posts: 2240
Location: Italy
Seymour Clufley wrote:
I've been trying to make the AA code work on its own, but no success so far. Is it dependent on the image being rotated?


In a sense yes, because destination and source point must slightly differ (their position is calculated not using integers like normal pixel coordinates but using floating point accuracy).

If you modify the code as you did the iXs0 = Int(fXs) will be exactly like iXs0 = fXs, so fXfs1 will be always zero and fXfs1less always 1

Code:
                ; Bottom left coords of bounding floating point rectangle on nSrcImage
                iXs0 = Int(fXs)   
                iYs0 = Int(fYs)
                   
                If iXs0 > 0 And iXs0 < iSrcWidth -1 And iYs0 > 0 And iYs0 < iSrcHeight - 1
               
                    fXfs1 = fXs - Int(fXs)
                    fYfs1 = fYs - Int(fYs)

                    fXfs1less = 1 - fXfs1
                    fYfs1less = 1 - fYfs1


so in the code that follow you can see how the RGB components are not altered and they are exactly as the input source.

The algorithm need to be slightly changed as suggested by eesau, but I don't think the result will be very pleasing. This type of AA is really useful in a otherwise distorted rotated images but not on perfectly fine images.

BTW I'm not very expert in this field !

_________________
[ Home ] [ My PC ] [ New to PB ? ]


Last edited by luis on Fri Sep 11, 2009 2:32 pm, edited 1 time in total.

Top
 Profile  
 
 Post subject: Re: Image Rotation routines for 24/32 bit with optional AA
PostPosted: Fri Sep 11, 2009 2:31 pm 
Offline
Addict
Addict
User avatar

Joined: Wed Aug 31, 2005 11:09 pm
Posts: 2240
Location: Italy
eesau wrote:
Oh and thanks to luis for the code! One note though, I think it could be optimized a lot by moving all of the select/case-blocks out of the loops, and making own loops 24/32 bits (so that you don't have to check the bits for each pixel, which is what slows it down).


Do you mean separating the code for 24/32 bit in two branches and removing the select-case inside the loop and in the macros ?

If you mean that the impact would be negligible I'm afraid, yes it's true we could have a lot of CMP and JUMP or something like that removed from inside the loop, but the bulk of the time I believe is spent inside the floating point calculations. I don't know, maybe you could have a 1% gain that way. Try if you like! :)

_________________
[ Home ] [ My PC ] [ New to PB ? ]


Top
 Profile  
 
 Post subject: Re: Image Rotation routines for 24/32 bit with optional AA
PostPosted: Fri Sep 11, 2009 2:38 pm 
Offline
Enthusiast
Enthusiast
User avatar

Joined: Fri Apr 27, 2007 12:38 pm
Posts: 528
Location: Finland
luis wrote:
Do you mean separating the code for 24/32 bit in two branches and removing the select-case inside the loop and in the macros ?

If you mean that the impact would be negligible I'm afraid, yes it's true we could have a lot of CMP and JUMP or something like that removed from inside the loop, but the bulk of the time I believe is spent inside the floating point calculations. I don't know, maybe you could have a 1% gain that way. Try if you like! :)

Yes, that's what I meant. I think it would make a bigger than 1% difference, but the code is already quite fast. Maybe I'll do a speed test when I have the time!

Once again, great code and surely useful! Many thanks 8)


Top
 Profile  
 
 Post subject: Re: Image Rotation routines for 24/32 bit with optional AA
PostPosted: Fri Sep 11, 2009 3:28 pm 
Offline
Addict
Addict
User avatar

Joined: Wed Feb 28, 2007 9:13 am
Posts: 923
Location: Edinburgh
luis wrote:
This type of AA is really useful in a otherwise distorted rotated images but not on perfectly fine images.
Right. I'll see if I can implement it with Einander's image distortion (corner pin) code.

Quote:
BTW I'm not very expert in this field !
Maybe not, but you got the job done!

I'm surprised that normal antialiasing has not been achieved in the PB community. (I've attempted it myself but the maths involved confuse me.)

Thanks again for the code,
Seymour.

_________________
JACK WEBB: "Coding in C is like sculpting a statue using only sandpaper. You can do it, but the result wouldn't be any better. So why bother? Just use the right tools and get the job done."


Last edited by Seymour Clufley on Fri Sep 11, 2009 3:56 pm, edited 1 time in total.

Top
 Profile  
 
 Post subject: Re: Image Rotation routines for 24/32 bit with optional AA
PostPosted: Fri Sep 11, 2009 3:32 pm 
Offline
Addict
Addict
User avatar

Joined: Sat Feb 19, 2005 2:46 pm
Posts: 1334
Location: Pas-de-Calais, France
We did a rotation some times ago. Maybe this code can help...

Here's the code:
Code:
;Fast and clean rotation
;Original Code : LSI (le soldat inconnu) for his effects library (http://www.purearea.net/pb/download/userlibs/Effect.zip)
;Optimisation (not fully optimised) by djes (djes@free.fr)
;Not needing assembly inline by psychopanta

;#PI.f=3.14159265

;******************************************************************************************************
; RotateImageEx2
;>= Angle (in degrees :()
ProcedureDLL.l RotateImageEx2(ImageID, Angle.f)
  Protected bmi.BITMAPINFO, bmi2.BITMAPINFO, Hdc.l, NewImageID, Mem, n, nn, bm.BITMAP
 
  ;radian conversion
  Debug "Angle : "+Str(angle)
  Angle*#PI/180
 
  Protected Cos.f = Cos(Angle)
  Protected Sin.f = Sin(Angle)
 
  Protected CouleurFond = 0
 
  GetObject_(ImageID, SizeOf(BITMAP), @bm.BITMAP)
 
  bmi\bmiHeader\biSize = SizeOf(BITMAPINFOHEADER)
  bmi\bmiHeader\biWidth = bm\bmWidth
  bmi\bmiHeader\biHeight = bm\bmHeight
  bmi\bmiHeader\biPlanes = 1
  bmi\bmiHeader\biBitCount = 32
  bmi\bmiHeader\biCompression = #BI_RGB
 
  bmi2\bmiHeader\biSize = SizeOf(BITMAPINFOHEADER)
  bmi2\bmiHeader\biWidth = bm\bmWidth * Abs(Cos) + bm\bmHeight * Abs(Sin)
  bmi2\bmiHeader\biHeight = bm\bmHeight * Abs(Cos) + bm\bmWidth * Abs(Sin)
  bmi2\bmiHeader\biPlanes = 1
  bmi2\bmiHeader\biBitCount = 32
  bmi2\bmiHeader\biCompression = #BI_RGB
 
  Mem = AllocateMemory(bm\bmWidth * bm\bmHeight * 4)
  If Mem
    Protected Mem2 = AllocateMemory(bmi2\bmiHeader\biWidth * bmi2\bmiHeader\biHeight * 4)
    If Mem2
     
      ;retrieves the bits of the specified bitmap and copies them into a buffer
      Hdc = CreateCompatibleDC_(GetDC_(ImageID))
      If Hdc
        GetDIBits_(Hdc, ImageID, 0, bm\bmHeight, Mem, @bmi, #DIB_RGB_COLORS)
        ReleaseDC_(0, Hdc)
      EndIf
     
      Protected CX1 = bm\bmWidth - 1
      Protected CY1 = bm\bmHeight - 1
      Protected CX2 = bmi2\bmiHeader\biWidth - 1
      Protected CY2 = bmi2\bmiHeader\biHeight - 1
     
      Protected Mem01 = Mem + bm\bmWidth * 4
      Protected Mem10 = Mem + 4
      Protected Mem11 = Mem01 + 4
     
      Protected Mem2Temp = Mem2
      Protected deb=-CX2/2
      Protected fin=deb+CX2;= Round(CX2/2,1) but <> (CX2*2-CX2)/2
         
      For nn = 0 To CY2

        Protected x1b.l
        Protected y1b.l = (nn * 2) - CY2

        Protected Temp1.f = (CX1 - (y1b * Sin))/2
        Protected Temp2.f = (CY1 + (y1b * Cos))/2       

        Protected x1.f = Temp1 + (deb * Cos)
        Protected y1.f = Temp2 + (deb * Sin)

        For x1b = deb To fin
         
          ;could be faster with arrays
         
          Protected x2.l = x1
          Protected y2.l = y1
         
          If x1 < x2
            !dec dword[p.v_x2]
          EndIf
          If y1 < y2
            !dec dword[p.v_y2]
          EndIf
         
          Protected x2b.l = x2 + 1
          Protected y2b.l = y2 + 1
         
          ;test boundaries
          If x2b >= 0 And x2 <= CX1 And y2b >= 0 And y2 <= CY1 
         
            Protected fx.f = x1 - x2
            Protected fy.f = y1 - y2
            Protected f00.f = 1 - fx
            Protected f10.f = 1 - fy
            Protected f01.f = f00 * fy
            f00 * f10
            f10 * fx
            Protected f11.f = fx * fy
         
            Protected MemTemp = (x2 + y2 * bm\bmWidth) * 4
            Protected c00.l, c01.l, c11.l, c10.l
           
            If x2 >= 0 And x2 <= CX1
              If y2 >= 0 And y2 <= CY1
                !mov eax,dword[p.v_Mem]
                !add eax,dword[p.v_MemTemp]
                !mov eax,dword[eax]
                !mov dword[p.v_c00],eax
                ;c00 = PeekL(Mem + MemTemp)
              Else
                c00 = 0
              EndIf
              If y2b >= 0 And y2b <= CY1
                !mov eax,dword[p.v_Mem01]
                !add eax,dword[p.v_MemTemp]
                !mov eax,dword[eax]
                !mov dword[p.v_c01],eax
                ;c01 = PeekL(Mem01 + MemTemp)
              Else
                c01 = 0
              EndIf
            Else
              c00 = 0
              c01 = 0
            EndIf
            If x2b >= 0 And x2b <= CX1
              If y2 >= 0 And y2 <= CY1
                !mov eax,dword[p.v_Mem10]
                !add eax,dword[p.v_MemTemp]
                !mov eax,dword[eax]
                !mov dword[p.v_c10],eax
                ;c10 = PeekL(Mem10 + MemTemp)
              Else
                c10 = 0
              EndIf
              If  y2b >= 0 And y2b <= CY1
                !mov eax,dword[p.v_Mem11]
                !add eax,dword[p.v_MemTemp]
                !mov eax,dword[eax]
                !mov dword[p.v_c11],eax
                ;c11 = PeekL(Mem11 + MemTemp)
              Else
                c11 = 0
              EndIf
            Else
              c10 = 0
              c11 = 0
            EndIf
   
            Protected r1.l,r2.l,r3.l,r4.l,g1.l,g2.l,g3.l,g4.l,b1.l,b2.l,b3.l,b4.l

            !mov eax,dword[p.v_c00]
            !mov ebx,eax
            !mov ecx,eax

            !and eax,$FF
            !mov dword[p.v_r1],eax
            !and ebx,$FF00
            !mov dword[p.v_g1],ebx
            !and ecx,$FF0000
            !mov dword[p.v_b1],ecx

            !mov eax,dword[p.v_c10]
            !mov ebx,eax
            !mov ecx,eax

            !and eax,$FF
            !mov dword[p.v_r2],eax
            !and ebx,$FF00
            !mov dword[p.v_g2],ebx
            !and ecx,$FF0000
            !mov dword[p.v_b2],ecx

            !mov eax,dword[p.v_c01]
            !mov ebx,eax
            !mov ecx,eax

            !and eax,$FF
            !mov dword[p.v_r3],eax
            !and ebx,$FF00
            !mov dword[p.v_g3],ebx
            !and ecx,$FF0000
            !mov dword[p.v_b3],ecx

            !mov eax,dword[p.v_c11]
            !mov ebx,eax
            !mov ecx,eax

            !and eax,$FF
            !mov dword[p.v_r4],eax
            !and ebx,$FF00
            !mov dword[p.v_g4],ebx
            !and ecx,$FF0000
            !mov dword[p.v_b4],ecx
     
           ;pure knows well how to do this
            Protected r.l = r1 * f00 + r2 * f10 + r3 * f01 + r4 * f11
            Protected g.l = g1 * f00 + g2 * f10 + g3 * f01 + g4 * f11
            Protected b.l = b1 * f00 + b2 * f10 + b3 * f01 + b4 * f11
         
            !mov eax,dword[p.v_r]
            !mov ebx,dword[p.v_g]
            !mov ecx,dword[p.v_b]
            ;one trick is to let triplets at the same place to avoid shifting
            !and eax,$FF                                     
            !and ebx,$FF00
            !and ecx,$FF0000
            !or eax,ebx
            !or eax,ecx

            !mov ebx,dword[p.v_Mem2Temp]
            !mov dword[ebx],eax
         
          Else

            !mov ebx,dword[p.v_Mem2Temp]
            !xor eax,eax
            !mov dword[ebx],eax

          EndIf
         
          Mem2Temp + 4
          x1 + cos
          y1 + sin
         
        Next
      Next
     
      ; On crée la nouvelle image
      NewImageID = CreateImage(#PB_Any, bmi2\bmiHeader\biWidth, bmi2\bmiHeader\biHeight)
      Hdc = CreateCompatibleDC_(GetDC_(ImageID(NewImageID)))
      If Hdc
        SetDIBits_(Hdc, ImageID(NewImageID), 0, bmi2\bmiHeader\biHeight, Mem2, @bmi2, #DIB_RGB_COLORS) ; on envoie la liste dans l'image
        ReleaseDC_(0, Hdc)
      EndIf
     
      FreeMemory(Mem2)
    EndIf
    FreeMemory(Mem)
  EndIf
 
  ProcedureReturn NewImageID
EndProcedure

;******************************************************************************************************
;- Speed Test

SetPriorityClass_(GetCurrentProcess_(),#REALTIME_PRIORITY_CLASS)

#NbTest = 4

MessageRequester("Speed test",Str(#NbTest)+" rotations of a 1000x1000 image...")

BigImage = CreateImage(#PB_Any, 1000, 1000)
If BigImage<>0
  StartDrawing(ImageOutput(BigImage))
    For n = 0 To 999
      For nn = 0 To 999
        Plot(n, nn, Random($FFFFFF))
      Next
    Next
  StopDrawing()
 
  Dim Liste(ImageWidth(BigImage) - 1, ImageHeight(BigImage) - 1)
 
  Time1 = ElapsedMilliseconds()
 
  For n = 1 To #NbTest
    DestImage = RotateImageEx2(ImageID(BigImage), 30)
  Next
 
  Time2 = ElapsedMilliseconds()
 
  Speed = (Time2 - Time1) / #NbTest
  NbPixels = ImageHeight(BigImage) * ImageWidth(BigImage)
 
  MessageRequester("Results", "Average speed of "+Str(Speed) + " ms" + Chr(10) + "Image of " + Str(ImageWidth(BigImage)) + " * " + Str(ImageHeight(BigImage)) + " pixels" + Chr(10) + "Pixels nb = " + Str(NbPixels), 0)
EndIf

;******************************************************************************************************
;- Drawing test

SrcImage = CreateImage(#PB_Any, 80, 100)

If SrcImage<>0
  StartDrawing(ImageOutput(SrcImage))
    Box(0, 0, 80, 100, $6F6F6F)
    Box(5, 5, 35, 45, $FF)
    Box(40, 5, 35, 45, $FF00)
    Box(5, 50, 35, 45, $FF0000)
    Box(40, 50, 35, 45, $FFFFFF)
  StopDrawing()
 
  RotImage = RotateImageEx2(ImageID(SrcImage), u) 

EndIf

SetPriorityClass_(GetCurrentProcess_(),#NORMAL_PRIORITY_CLASS)

; Création de la fenêtre et de la GadgetList
If OpenWindow(0, 0, 0, 250, 300, "Effect - Image rotation", #PB_Window_SystemMenu | #PB_Window_ScreenCentered | #PB_Window_MinimizeGadget) = 0 Or CreateGadgetList(WindowID(0)) = 0
  End
EndIf

TextGadget(#PB_Any, 10, 10, 100, 15, "Src Image")
ImageGadget(#PB_Any, 10, 25, 0, 0, ImageID(SrcImage))

TextGadget(1, 10, 135, 100, 15, "Rotation")
ImageGadget(2, 10, 150, 0, 0, ImageID(RotImage))

angle = 1

Repeat

  Event = WindowEvent()
  Delay(20)

  RotImage1 = ImageID(RotateImageEx2(ImageID(SrcImage), angle))

  SetGadgetText(1, "Rotation : " + Str(angle))
  SetGadgetState(2, RotImage1)

  If IsImage(RotImage2) : FreeImage(RotImage2) : Delay(20) : EndIf

  angle + 1

  RotImage2 = ImageID(RotateImageEx2(ImageID(SrcImage), angle))

  SetGadgetText(1, "Rotation : " + Str(angle))
  SetGadgetState(2, RotImage2)

  If IsImage(RotImage1) : FreeImage(RotImage1) : Delay(20) : EndIf

  angle + 1
 

Until Event = #PB_Event_CloseWindow

End

_________________
The Shooting Crew ~> http://www.shootingcrew.com/
Bobble Puzzle, Purebreaker 3 ~> http://djes.free.fr


Top
 Profile  
 
 Post subject: Re: Image Rotation routines for 24/32 bit with optional AA
PostPosted: Fri Sep 11, 2009 3:59 pm 
Offline
Addict
Addict
User avatar

Joined: Wed Aug 31, 2005 11:09 pm
Posts: 2240
Location: Italy
Yes Djes, I remember this exceptionally fast routine from the forum, it's really great.

But I wrote my own because I wanted to not use api if possible, to be usanble under 32/64 bits and to support the background color (24 bit) and transparency (32 bit).

Would be very very nice if you can add all of the above to this routine to make it plug-and-play compatible with my own.

In any case is a very good piece of software if the above points are not important.

Cheers!

_________________
[ Home ] [ My PC ] [ New to PB ? ]


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 45 posts ]  Go to page 1, 2, 3  Next

All times are UTC + 1 hour


Who is online

Users browsing this forum: No registered users and 0 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
Jump to:  

 


Powered by phpBB © 2008 phpBB Group
subSilver+ theme by Canver Software, sponsor Sanal Modifiye