Page 2 of 4

Re: Image Rotation routines for 24/32 bit with optional AA

Posted: Fri Sep 11, 2009 4:27 pm
by djes
Badly, it's not my own routine, even if I optimised it, and I really have no time by now... But yours "does the job", and is clean :)

Re: Image Rotation routines for 24/32 bit with optional AA

Posted: Sun Sep 13, 2009 12:08 am
by luis
Optimized a little for speed.

1) Splitted the code and macros for 24 / 32 bit to remove the select/case branch (2% gain)

2) Removed the clipping test (RGB_Clamp macro) using a little trick (the - 0.000005 in the code) to eliminate the rounding problems who caused sometimes the overflow/underflow in the 0/255 range. This way a lot of if were removed (around 1% gain probably less).

3) Eliminated the copy of the data from the image to the structure in the main loop. It wasn't needed. My bad. Now I simply move a pointer in memory and read the data directly without copy it to the temp structure until the moment I need to write it to the destination (changed the ReadPixel?? macro). This is where the gain is (big error, big gain I guess). 10% maybe a little more.

So, at the end should be around 15% faster for the heavier scenario (32 bit and antialias enabled).

Hope I don't broke something in the process, but it seem working !

Bye!

Re: Image Rotation routines for 24/32 bit with optional AA

Posted: Sun Sep 13, 2009 1:34 am
by djes
Nice code, clean and nice to read! Just for info, to optimise (not useful in my opinion as it's not designed to be realtime), you could use lines routines as x and y have a linear progression. Then you only have to compute the four source corners (not the dest). The second optimisation (harder), is to have a linear progression along the memory, as you could somehow add the x and y components of the source image. The third optimisation is to transform the source image to something more manageable by the processor. All of this could benefit from assembler, but will be not as portable.
However, as dest is memory and fully linear, you can speed up things a bit by removing some computations in the main loop, as in this topic : http://www.purebasic.fr/english/viewtop ... 13&t=39036

But honestly I prefer this code like it is now, as it is convenient ;)

Re: Image Rotation routines for 24/32 bit with optional AA

Posted: Sun Sep 13, 2009 10:44 am
by luis
Your points are very interesting djes! I'm not quite sure how to implement them, especially the first one.
djes wrote:you could use lines routines as x and y have a linear progression. Then you only have to compute the four source corners (not the dest).

Probably I don't have the concept very clear in my mind and should I give it some thought... a little over my head I believe :)

If you have time and the will, could you explain a little more in detail the first suggestion you make ?

In any case thank you and thank you for the link too, I'll read it this afternoon!


EDIT: I lied, I din't resist and read it now!
AHHH! I see what you mean, yes I thought about that but I skipped it because it would have meant to loose my nice ReadPixel WritePixel macros and have them diluted along the main loop. It will be faster (very little I think) but I will be less easy to read and to derive maybe other routines from the same code. Well it's often that way when optimize for speed, you loose something else. But I understand your point. Maybe I'll try it just for fun!

Re: Image Rotation routines for 24/32 bit with optional AA

Posted: Sun Sep 13, 2009 12:22 pm
by djes
In any case, keep your original code :)

Re: Image Rotation routines for 24/32 bit with optional AA

Posted: Sun Sep 20, 2009 9:03 pm
by Le Soldat Inconnu
hi,

i do code to rotate image with AA

i just adapt for alpha channel

so let go :)

Code: Select all

ProcedureDLL.l RotateImageEx2(ImageID, Angle.f, Mode.l) ; Rotation d'une image d'un angle en °
	Protected bmi.BITMAPINFO, bmi2.BITMAPINFO, hdc.l, NewImageID, Mem, n, nn, bm.BITMAP
	
	Angle = Angle * #PI / 180 ; On convertit en radian
	
	Cos.f = Cos(Angle)
	Sin.f = Sin(Angle)
	
	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
	
	bmi2\bmiHeader\biSize = SizeOf(BITMAPINFOHEADER)
	Select Mode
		Case 1
			bmi2\bmiHeader\biWidth = bm\bmWidth
			bmi2\bmiHeader\biHeight = bm\bmHeight
		Case 2
			bmi2\bmiHeader\biWidth = Round(Sqr(bm\bmWidth * bm\bmWidth + bm\bmHeight * bm\bmHeight), 1)
			bmi2\bmiHeader\biHeight = bmi2\bmiHeader\biWidth
		Default
			bmi2\bmiHeader\biWidth = Round(bm\bmWidth * Abs(Cos) + bm\bmHeight * Abs(Sin), 1)
			bmi2\bmiHeader\biHeight = Round(bm\bmHeight * Abs(Cos) + bm\bmWidth * Abs(Sin), 1)
	EndSelect
	bmi2\bmiHeader\biPlanes = 1
	bmi2\bmiHeader\biBitCount = 32
	
	Mem = AllocateMemory(bm\bmWidth * bm\bmHeight * 4)
	If Mem
		Mem2 = AllocateMemory(bmi2\bmiHeader\biWidth * bmi2\bmiHeader\biHeight * 4)
		If Mem2
			
			hdc = CreateCompatibleDC_(GetDC_(ImageID))
			If hdc
				GetDIBits_(hdc, ImageID, 0, bm\bmHeight, Mem, @bmi, #DIB_RGB_COLORS) ; on envoie la liste dans l'image
				DeleteDC_(hdc)
			EndIf
			
			CX1 = bm\bmWidth - 1
			CY1 = bm\bmHeight - 1
			CX2 = bmi2\bmiHeader\biWidth - 1
			CY2 = bmi2\bmiHeader\biHeight - 1
			
			Mem01 = Mem + bm\bmWidth * 4
			Mem10 = Mem + 4
			Mem11 = Mem01 + 4
			
			Mem2Temp = Mem2
			
			For nn = 0 To CY2
				y1b.l = nn * 2 - CY2
				Temp1.f = CX1 - y1b * Sin
				Temp2.f = CY1 + y1b * Cos
				For n = 0 To CX2
					x1b.l = n * 2 - CX2
					
					x1.f = (Temp1 + x1b * Cos) / 2
					y1.f = (Temp2 + x1b * Sin) / 2
					
					x2.l = x1
					y2.l = y1
					
					If x1 < x2
						DEC x2
					EndIf
					If y1 < y2
						DEC y2
					EndIf
					
					x2b = x2 + 1
					y2b = y2 + 1
					
					If x2b >= 0 And x2 <= CX1 And y2b >= 0 And y2 <= CY1 ; On filtre si on est completement en dehors de l'image
						
						fx.f = x1 - x2
						fy.f = y1 - y2
						f00.f = (1 - fx) * (1 - fy)
						f01.f = (1 - fx) * fy
						f10.f = fx * (1 - fy)
						f11.f = fx * fy
						
						MemTemp = (x2 + y2 * bm\bmWidth) * 4
						
						If x2 >= 0 And x2 <= CX1
							If y2 >= 0 And y2 <= CY1
								c00 = PeekL(Mem + MemTemp)
							Else
								c00 = 0
							EndIf
							If y2b >= 0 And y2b <= CY1
								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
								c10 = PeekL(Mem10 + MemTemp)
							Else
								c10 = 0
							EndIf
							If y2b >= 0 And y2b <= CY1
								c11 = PeekL(Mem11 + MemTemp)
							Else
								c11 = 0
							EndIf
						Else
							c10 = 0
							c11 = 0
						EndIf
						
						Channel00 = c00 >> 24 & $FF
						Channel01 = c01 >> 24 & $FF
						Channel10 = c10 >> 24 & $FF
						Channel11 = c11 >> 24 & $FF
						Alpha = Channel00 * f00 + Channel01 * f01 + Channel10 * f10 + Channel11 * f11
						Channel00 = c00 >> 16 & $FF
						Channel01 = c01 >> 16 & $FF
						Channel10 = c10 >> 16 & $FF
						Channel11 = c11 >> 16 & $FF
						Rouge = Channel00 * f00 + Channel01 * f01 + Channel10 * f10 + Channel11 * f11
						Channel00 = c00 >> 8 & $FF
						Channel01 = c01 >> 8 & $FF
						Channel10 = c10 >> 8 & $FF
						Channel11 = c11 >> 8 & $FF
						Vert = Channel00 * f00 + Channel01 * f01 + Channel10 * f10 + Channel11 * f11
						Channel00 = c00 & $FF
						Channel01 = c01 & $FF
						Channel10 = c10 & $FF
						Channel11 = c11 & $FF
						Bleu = Channel00 * f00 + Channel01 * f01 + Channel10 * f10 + Channel11 * f11
						
						PokeL(Mem2Temp, Rouge | Vert << 8 | Bleu << 16 | Alpha << 24)
						
					Else
						PokeL(Mem2Temp, 0)
					EndIf
					
					Mem2Temp + 4
					
				Next
			Next
			
			; On crée la nouvelle image
			NewImageID = CreateImage(#PB_Any, bmi2\bmiHeader\biWidth, bmi2\bmiHeader\biHeight, 32)
			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
				DeleteDC_(hdc)
			EndIf
			
			FreeMemory(Mem2)
		EndIf
		FreeMemory(Mem)
	EndIf
	
	ProcedureReturn NewImageID
EndProcedure


;- ----------------------------------------
;- Test  d'affichage d'image avec rotation
; On dessine une image

ImageNormale = CreateImage(#PB_Any, 80, 100, 32)
StartDrawing(ImageOutput(ImageNormale))
	DrawingMode(#PB_2DDrawing_AlphaChannel)
	Box(0, 0, 80, 100, $00000000)
	DrawingMode(#PB_2DDrawing_Gradient | #PB_2DDrawing_AlphaBlend)
	LinearGradient(0, 0, 80, 100)
	GradientColor(0.0, $00000000)
	GradientColor(1.0, $FF000000)
	Box(0, 0, 80, 100)
	DrawingMode(#PB_2DDrawing_AlphaBlend)
	Box(5, 5, 35, 45, $B00000FF)
	Box(40, 5, 35, 45, $FF00FF00)
	Box(5, 50, 35, 45, $FFFF0000)
	Box(40, 50, 35, 45, $80FFFFFF)
StopDrawing()

; Création de la fenêtre et de la GadgetList
If OpenWindow(0, 0, 0, 350, 400, "Effect - Rotation d'image", #PB_Window_SystemMenu | #PB_Window_ScreenCentered | #PB_Window_MinimizeGadget) = 0
	End
EndIf

Angle.f = 30
Mode = 0
ImageRotation = RotateImageEx2(ImageID(ImageNormale), Angle, Mode)

TextGadget(#PB_Any, 10, 10, 100, 15, "Image normale")
ImageGadget(#PB_Any, 10, 25, 0, 0, ImageID(ImageNormale))

RotationTexte = TextGadget(#PB_Any, 10, 200, 100, 15, "Rotation de 30°")
Rotation = ImageGadget(#PB_Any, 10, 215, 0, 0, ImageID(ImageRotation))
RotationTemps = TextGadget(#PB_Any, 180, 270, 170, 15, "")

TextGadget(#PB_Any, 180, 200, 100, 15, "Mode :")
OptionGadget(0, 180, 215, 170, 15, "normal")
OptionGadget(1, 180, 230, 170, 15, "sans redimensionnement")
OptionGadget(2, 180, 245, 170, 15, "avec redimensionnement fixe")
SetGadgetState(0, 1)

AddWindowTimer(0, 1, 200)

Repeat
	Event = WaitWindowEvent()
	
	If Event = #PB_Event_Timer
		If EventTimer() = 1
			Angle + 1
			If Angle = 360
				Angle = 0
			EndIf
			Temps1 = ElapsedMilliseconds()
			For n = 1 To 100
				FreeImage(ImageRotation)
				ImageRotation = RotateImageEx2(ImageID(ImageNormale), Angle, Mode)
			Next
			Temps2 = ElapsedMilliseconds()
			SetGadgetState(Rotation, ImageID(ImageRotation))
			SetGadgetText(RotationTexte, "Rotation de " + Str(Angle) + "°")
			SetGadgetText(RotationTemps, StrF((Temps2 - Temps1) / 100, 2) + "ms")
			
		EndIf
	ElseIf Event = #PB_Event_Gadget
		Select EventGadget()
			Case 0
				Mode = 0
			Case 1
				Mode = 1
			Case 2
				Mode = 2
		EndSelect
	EndIf
	
Until Event = #PB_Event_CloseWindow

End

Full librairie is here
http://www.lsi-dev.com/index.php?mod=do ... erid&id=40

Image symetry, rotation, and some effect about color

Re: Image Rotation routines for 24/32 bit with optional AA

Posted: Fri Feb 05, 2010 7:35 pm
by c4s
Thanks luis, great code!

The only thing I dislike is the heavy line count. The code I'm using right now has about 100 lines and fits into one procedure.
But I think I have to accept this for AA, alpha support and non rectangle rotation. ;)

Re: Image Rotation routines for 24/32 bit with optional AA

Posted: Fri Feb 05, 2010 8:27 pm
by luis
c4s wrote:The code I'm using right now has about 100 lines and fits into one procedure.
But I think I have to accept this for AA, alpha support and non rectangle rotation. ;)
Well, if with "non rectangle rotation" you mean "free angle rotation" (I'm not sure) you can see there are a couple of routines included to do 90 degrees rotations and flipping/mirroring too. They are also obviously faster than the generic one. If you need only a subset of what this one does you can trim it down (that's the good thing of having the source !).

Thanks for the thanks, they are always appreciated!

Re: Image Rotation routines for 24/32 bit with optional AA

Posted: Fri Feb 05, 2010 8:38 pm
by c4s
luis wrote:Well, if with "non rectangle rotation" you mean "free angle rotation" (I'm not sure) you can see there are a couple of routines included to do 90 degrees rotations and flipping/mirroring too. They are also obviously faster than the generic one. If you need only a subset of what this one does you can trim it down (that's the good thing of having the source !).

Thanks for the thanks, they are always appreciated!
Sorry, I meant the rectangular rotation (90, 180, 270).
You are right. I can pick out the procedures I need so when I would remove the comments etc. the same line count will come up and it's then even cross-platform and supports alpha and retains the depth (in contrast to the code I used).

Now when I realized this your code is even better!

Maybe you should add a small note in the first post about having this only-rectangular-rotation-procedure thing?

Re: Image Rotation routines for 24/32 bit with optional AA

Posted: Sat Feb 06, 2010 6:00 pm
by c4s
After playing a bit with the code, taking out the things I need etc. I found out that I could need handling of images with lower depth like 16 bit.
Is it possible to just modifiy the CopyPixelXX(), ReadPixelXX(), WritePixelXX() procedures to support this or are there higher restrictions?

Re: Image Rotation routines for 24/32 bit with optional AA

Posted: Sat Feb 06, 2010 7:07 pm
by luis
It should work without modifications if I'm not mistaken... the real difference it's between 32 bit (rgb + alpha) and 24 bit (rgb without alpha).

The 24 bit routines should work for 16 bits also. I should try but I'm reasonably sure.

Re: Image Rotation routines for 24/32 bit with optional AA

Posted: Sat Feb 06, 2010 9:59 pm
by c4s
luis wrote:The 24 bit routines should work for 16 bits also. I should try but I'm reasonably sure.
Yes, you are right. Seems to work. But I forgot that PureBasic can't save in the "correct" bitrate, right?

Note, I just found a nasty memory leak or at least this behavior wasn't clear for me: The source image still exists after the modification!
To fix it I just added FreeImage(nSrcImage) after AllocateImageData(nSrcImage, @iBufferPitchSrc).


Anyway, thanks again for your great code and help. :)

Re: Image Rotation routines for 24/32 bit with optional AA

Posted: Sat Feb 06, 2010 10:25 pm
by luis
c4s wrote: Yes, you are right. Seems to work. But I forgot that PureBasic can't save in the "correct" bitrate, right?
Bitdepths for loading, saving and catchimage are not particulary clear, at least to me.
The best thing is to try and check the results I'm afraid.

See this bugreport for example:

http://www.purebasic.fr/english/viewtop ... 13#p303913
c4s wrote: Note, I just found a nasty memory leak or at least this behavior wasn't clear for me: The source image still exists after the modification!
It's by design. You get a new image rotated, but the original is not deleted. You could have still some very good use for it for what I know.

That's why in the examples I wrote there is a FreeImage() for that. The intention was to make that clear, but maybe I should have wrote it explicitly. Well, I'm saying that here and now!
c4s wrote: Anyway, thanks again for your great code and help. :)
No problem :)

Re: Image Rotation routines for 24/32 bit with optional AA

Posted: Tue Oct 12, 2010 3:27 pm
by oryaaaaa
Thank you very much. :D

Re: Image Rotation routines for 24/32 bit with optional AA

Posted: Mon Aug 15, 2011 12:21 pm
by ozzie
Many thanks, luis - just what I need.