Image Rotation routines for 24/32 bit with optional AA
Image Rotation routines for 24/32 bit with optional AA
The following posts contain the code and three samples.
Potentially cross platform. Tested on Win32/64 and Linux32.
Tested with PB 5.30
- 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.
EDIT: v1.10 Changed error handling, tested code for PB 5.73.
EDIT: v1.11 Hopefully fixed an IMA and tested code with PB 5.73.
Potentially cross platform. Tested on Win32/64 and Linux32.
Tested with PB 5.30
- 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.
EDIT: v1.10 Changed error handling, tested code for PB 5.73.
EDIT: v1.11 Hopefully fixed an IMA and tested code with PB 5.73.
Last edited by luis on Wed Apr 20, 2022 8:12 pm, edited 17 times in total.
MAIN CODE - "RotateImage.pb"
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
Last edited by luis on Wed Apr 20, 2022 8:09 pm, edited 13 times in total.
TEST 1
Code: Select all
; TEST - RotateImageFree
EnableExplicit
IncludeFile "..\RotateImage.pb"
Procedure.i CreateCheckers (Width, Height)
Protected x, y
Protected nImage, Size = 12
nImage = CreateImage(#PB_Any, Width, Height, 24)
If nImage
StartDrawing(ImageOutput(nImage))
Box(0, 0, Width, Height, $FFFFFF)
While y < Height + Size
While x < Width + Size
Box(x, y, Size, Size, $C0C0C0)
Box(x + Size, y + Size, Size, Size, $C0C0C0)
x + Size * 2
Wend
x = 0
y + Size * 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 Event, Time1, k
Define nImgSrc, nImgOut, nFont
Define nImgCheck, nImgDisp
; ****************************************************************
; try to change these vars to check out the various combinations *
; ****************************************************************
Define BitPlanes = 32 ; try 24 or 32 bpp image
Define flgAntiAlias = 1 ; try antialias (1) or simpler rotation (0)
Define fRot = 33 ; angle used for rotation
Define FillColor = 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, BitPlanes)
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(BitPlanes) + " BPP IMAGE - TEXT", RGB(k/3, k/2, k/2))
Next
If BitPlanes = 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
FillColor = 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()
If OpenWindow(#WIN, 0, 0, 800, 800, "", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
Time1 = ElapsedMilliseconds()
nImgOut = RotateImageFree(nImgSrc, fRot, flgAntiAlias, FillColor)
Time1 = ElapsedMilliseconds() - Time1
SetWindowTitle(#WIN, "Test " + Str(BitPlanes) + " bit, AA = " + Str(flgAntiAlias) + ", msec = " + Str(Time1) + ", 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))
FreeImage(nImgDisp)
While Event <> #PB_Event_CloseWindow
Event = WaitWindowEvent()
Wend
FreeImage(nImgSrc)
FreeFont(nFont)
EndIf
Last edited by luis on Fri Aug 29, 2014 11:53 pm, edited 1 time in total.
TEST 2
Code: Select all
; TEST - RotateImage
EnableExplicit
IncludeFile "..\RotateImage.pb"
Procedure.i CreateCheckers (Width, Height)
Protected x, y
Protected nImage, Size = 12
nImage = CreateImage(#PB_Any, Width, Height, 24)
If nImage
StartDrawing(ImageOutput(nImage))
Box(0, 0, Width, Height, $FFFFFF)
While y < Height + Size
While x < Width + Size
Box(x, y, Size, Size, $C0C0C0)
Box(x + Size, y + Size, Size, Size, $C0C0C0)
x + Size * 2
Wend
x = 0
y + Size * 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 Event, Time1, k
Define nImgSrc, nImgOut, nFont
Define nImgCheck, nImgDisp
; ****************************************************************
; try to change these vars to check out the various combinations *
; ****************************************************************
Define BitPlanes = 32 ; test 24 or 32 bpp image
Define Rot = 90 ; angle used for rotation (+/-) 90, 180, 270
nFont = LoadFont(#PB_Any, "Arial", 14, #PB_Font_Bold)
nImgSrc = CreateImage(#PB_Any, #IW, #IH, BitPlanes)
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(BitPlanes) + " BPP image - Text", RGB(k/3, k/2, k/2))
Next
If BitPlanes = 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()
; UsePNGImageDecoder()
; nImgSrc = LoadImage(#PB_Any, "test32.png")
If OpenWindow(#WIN, 0, 0, 600, 700, "", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
Time1 = ElapsedMilliseconds()
nImgOut = RotateImage (nImgSrc, Rot)
Time1 = ElapsedMilliseconds() - Time1
If nImgOut = 0 : MessageRequester("Test", "Invalid angle ? (" + Str(Rot) + ")") :End: EndIf
SetWindowTitle(#WIN, "Test " + Str(BitPlanes) + " bit, msec = " + Str(Time1) + ", deg = " + Str(Rot))
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 Event <> #PB_Event_CloseWindow
Event = WaitWindowEvent()
Wend
FreeImage(nImgDisp)
FreeImage(nImgSrc)
FreeFont(nFont)
EndIf
Last edited by luis on Fri Aug 29, 2014 11:54 pm, edited 1 time in total.
TEST 3
Code: Select all
; TEST - Flip and Mirror
EnableExplicit
IncludeFile "..\RotateImage.pb"
Procedure.i CreateCheckers (Width, Height)
Protected x, y
Protected nImage, Size = 12
nImage = CreateImage(#PB_Any, Width, Height, 24)
If nImage
StartDrawing(ImageOutput(nImage))
Box(0, 0, Width, Height, $FFFFFF)
While y < Height + Size
While x < Width + Size
Box(x, y, Size, Size, $C0C0C0)
Box(x + Size, y + Size, Size, Size, $C0C0C0)
x + Size*2
Wend
x = 0
y + Size*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 = 360
Define Event, Time1, k, Title$
Define nImgSrc, nImgOut, nFont
Define nImgCheck, nImgDisp1, nImgDisp2
; ****************************************************************
; try to change these vars to check out the various combinations *
; ****************************************************************
Define BitPlanes = 32 ; test 24 or 32 bpp image
Define flgFlipOrMirror = 0 ; 1 = FlipImage(), 0 = MirrorImage()
nFont = LoadFont(#PB_Any, "Arial", 12, #PB_Font_Bold)
nImgSrc = CreateImage(#PB_Any, #IW, #IH, BitPlanes)
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(BitPlanes) + " BPP image - Text", RGB(k/3, k/2, k/2))
Next
If BitPlanes = 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)
Time1 = ElapsedMilliseconds()
If flgFlipOrMirror = 1
nImgOut = FlipImage(nImgSrc)
Else
nImgOut = MirrorImage(nImgSrc)
EndIf
Time1 = ElapsedMilliseconds() - Time1
If flgFlipOrMirror = 1
Title$ = "Test FlipImage(), "
Else
Title$ = "Test MirrorImage(), "
EndIf
SetWindowTitle(#WIN, Title$ + Str(BitPlanes) + " bit, msec = " + Str(Time1))
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 Event <> #PB_Event_CloseWindow
Event = WaitWindowEvent()
Wend
FreeImage(nImgDisp1)
FreeImage(nImgDisp2)
FreeImage(nImgSrc)
FreeFont(nFont)
EndIf
Last edited by luis on Fri Aug 29, 2014 11:55 pm, edited 2 times in total.
-
- Addict
- Posts: 1264
- Joined: Wed Feb 28, 2007 9:13 am
- Location: London
Re: Image Rotation routines for 24/32 bit with optional AA
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:to
to "cancel out" any rotation, because I just want the antialiasing. However, the image comes out exactly the same as it went in!
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: Select all
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
Code: Select all
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
Re: Image Rotation routines for 24/32 bit with optional AA
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.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!
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

Re: Image Rotation routines for 24/32 bit with optional AA
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).
Re: Image Rotation routines for 24/32 bit with optional AA
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).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?
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: Select all
; 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
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 !
Last edited by luis on Fri Sep 11, 2009 2:32 pm, edited 1 time in total.
"Have you tried turning it off and on again ?"
A little PureBasic review
A little PureBasic review
Re: Image Rotation routines for 24/32 bit with optional AA
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 ?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).
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!

"Have you tried turning it off and on again ?"
A little PureBasic review
A little PureBasic review
Re: Image Rotation routines for 24/32 bit with optional AA
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!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!
Once again, great code and surely useful! Many thanks

-
- Addict
- Posts: 1264
- Joined: Wed Feb 28, 2007 9:13 am
- Location: London
Re: Image Rotation routines for 24/32 bit with optional AA
Right. I'll see if I can implement it with Einander's image distortion (corner pin) code.luis wrote:This type of AA is really useful in a otherwise distorted rotated images but not on perfectly fine images.
Maybe not, but you got the job done!BTW I'm not very expert in this field !
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.
Last edited by Seymour Clufley on Fri Sep 11, 2009 3:56 pm, edited 1 time in total.
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."
Re: Image Rotation routines for 24/32 bit with optional AA
We did a rotation some times ago. Maybe this code can help...
Here's the code:
Here's the code:
Code: Select all
;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
Re: Image Rotation routines for 24/32 bit with optional AA
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!
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!
"Have you tried turning it off and on again ?"
A little PureBasic review
A little PureBasic review