90, 180, 270 Grad RotateImage

Hier könnt Ihr gute, von Euch geschriebene Codes posten. Sie müssen auf jeden Fall funktionieren und sollten möglichst effizient, elegant und beispielhaft oder einfach nur cool sein.
Christian+
Beiträge: 213
Registriert: 13.07.2008 10:05
Computerausstattung: Windows 8.1 Pro
AMD Phenom II X4 955 @ 3.2 GHz
4GB RAM
NVIDIA GeForce GTX 660

90, 180, 270 Grad RotateImage

Beitrag von Christian+ »

Ich wollte heute mal ein Image in 90 Grad schritten drehen.
Da ich aber dafür nur sehr langsame Funktionen oder welche die API Funktionen verwenden fand habe ich mich mal selbst dran versucht.
Hier nun das Ergebnis vielleicht kann es ja noch jemand brauchen bzw. vielleicht hat ja jemand sogar eine Idee wie man es noch schneller lösen kann.

Update:
Code ist überarbeitet sollte im Gegensatz zu dem davor nur fehlerfrei sein und ist auf jeden Fall einiges schneller ist aber nur für 32 Bit Images da für 24 Bit Images das ganze erheblich langsamer wäre.

Update 2:
Code erneut überarbeitet ist jetzt nochmal etwas schneller vor allem bei quadratischen Bildern.

Code: Alles auswählen

EnableExplicit

Procedure.i RotateImage32Bit(SourceImage32Bit.i, Rotate.i = 90, InPlace.i = #False)
  Protected Width.i, Height.i, ResultImage32Bit.i, ImageByteSize.i, SourceBuffer.i, ResultBuffer.i, Pos1.i, Pos2.i, Row.i
  Protected *SourcePixel.Long, *ResultPixel.Long
  
  If Not IsImage(SourceImage32Bit) Or Not ImageDepth(SourceImage32Bit, #PB_Image_InternalDepth) = 32 : ProcedureReturn #False : EndIf
  
  Width = ImageWidth(SourceImage32Bit)
  Height = ImageHeight(SourceImage32Bit)
  ImageByteSize = Width * Height * 4
  
  Rotate = Rotate % 360
  If Rotate < 0 : Rotate + 360 : EndIf

  If Rotate = 0
    If InPlace
      ProcedureReturn SourceImage32Bit
    Else
      ProcedureReturn CopyImage(SourceImage32Bit, #PB_Any)
    EndIf
  EndIf
  
  If Width = Height
    If InPlace
      ResultImage32Bit = SourceImage32Bit
    Else
      ResultImage32Bit = CopyImage(SourceImage32Bit, #PB_Any)
    EndIf
    StartDrawing(ImageOutput(ResultImage32Bit))
    SourceBuffer  = DrawingBuffer()
    StopDrawing()
    Select Rotate
      Case 90
        Row = Width * 4 - 4
        ImageByteSize / 4
        While ImageByteSize
          *SourcePixel = SourceBuffer + Pos1         + Pos2         * Width
          *ResultPixel = SourceBuffer + Pos2         + (Row - Pos1) * Width
          Swap *ResultPixel\l, *SourcePixel\l
          *ResultPixel = SourceBuffer + (Row - Pos1) + (Row - Pos2) * Width
          Swap *ResultPixel\l, *SourcePixel\l
          *ResultPixel = SourceBuffer + (Row - Pos2) + Pos1         * Width
          Swap *ResultPixel\l, *SourcePixel\l
          Pos1 + 4
          If Pos1 > Width << 1
            Pos2 + 4
            Pos1 = 0
          EndIf
          ImageByteSize - 4
        Wend
      Case 180
        *SourcePixel = SourceBuffer
        *ResultPixel = SourceBuffer + ImageByteSize - 4
        ImageByteSize / 2
        While ImageByteSize
          Swap *ResultPixel\l, *SourcePixel\l
          *SourcePixel + 4
          *ResultPixel - 4
          ImageByteSize - 4
        Wend
      Case 270
        Row = Width * 4 - 4
        ImageByteSize / 4
        While ImageByteSize
          *SourcePixel = SourceBuffer + Pos1         + Pos2         * Width
          *ResultPixel = SourceBuffer + (Row - Pos2) + Pos1         * Width
          Swap *ResultPixel\l, *SourcePixel\l
          *ResultPixel = SourceBuffer + (Row - Pos1) + (Row - Pos2) * Width
          Swap *ResultPixel\l, *SourcePixel\l
          *ResultPixel = SourceBuffer + Pos2         + (Row - Pos1) * Width
          Swap *ResultPixel\l, *SourcePixel\l
          Pos1 + 4
          If Pos1 > Width << 1
            Pos2 + 4
            Pos1 = 0
          EndIf
          ImageByteSize - 4
        Wend
    EndSelect
  Else
    If Rotate <> 180 : Swap Width, Height : EndIf
    StartDrawing(ImageOutput(SourceImage32Bit))
    SourceBuffer = DrawingBuffer()
    StopDrawing()
    ResultImage32Bit = CreateImage(#PB_Any, Width,  Height, 32)
    StartDrawing(ImageOutput(ResultImage32Bit))
    ResultBuffer = DrawingBuffer()
    StopDrawing()
    Select Rotate
      Case 90
        Pos2 = ImageByteSize - 4 * Width
        While Pos1 < ImageByteSize
          *SourcePixel = SourceBuffer + Pos1
          *ResultPixel = ResultBuffer + Pos2
          *ResultPixel\l = *SourcePixel\l
          Pos1 + 4
          Pos2 - 4 * Width
          If Pos2 < 0
            Pos2 = ImageByteSize - 4 * Width
            ResultBuffer + 4
          EndIf
        Wend
      Case  180
        *SourcePixel = SourceBuffer
        *ResultPixel = ResultBuffer + ImageByteSize - 4
        While ImageByteSize
          *ResultPixel\l = *SourcePixel\l
          *SourcePixel + 4
          *ResultPixel - 4
          ImageByteSize - 4
        Wend
      Case 270
        ResultBuffer + 4 * Width - 4
        While Pos1 < ImageByteSize
          *SourcePixel = SourceBuffer + Pos1
          *ResultPixel = ResultBuffer + Pos2
          *ResultPixel\l = *SourcePixel\l
          Pos1 + 4
          Pos2 + 4 * Width
          If Pos2 > ImageByteSize-4
            Pos2 = 0
            ResultBuffer - 4
          EndIf
        Wend
    EndSelect
  EndIf
  
  ProcedureReturn ResultImage32Bit
EndProcedure

; Beispiel

Define Image.i, Font.i, RotatedImage.i

Image = CreateImage(#PB_Any, 400, 300, 32)
Font = LoadFont(#PB_Any, "Arial", 100)

StartDrawing(ImageOutput(Image))
DrawingFont(FontID(Font))
DrawText(200 - TextWidth("Test") / 2, 150 - TextHeight("Test") / 2, "Test", RGB(255,255,255), RGB(0,0,255))
StopDrawing()

RotatedImage = RotateImage32Bit(Image)

OpenWindow(0, 0, 0, 800, 400, "RotateImage Beispiel", #PB_Window_ScreenCentered|#PB_Window_SystemMenu)

ImageGadget(0, 0, 0, 400, 400, ImageID(Image))
ImageGadget(1, 400, 0, 40, 40, ImageID(RotatedImage))

Repeat : Until WaitWindowEvent() = #PB_Event_CloseWindow
Zuletzt geändert von Christian+ am 20.08.2013 08:56, insgesamt 11-mal geändert.
Windows 8.1 Pro 64Bit | AMD Phenom II X4 955 @ 3.2 GHz | 4GB RAM | NVIDIA GeForce GTX 660
Benutzeravatar
Thorium
Beiträge: 1722
Registriert: 12.06.2005 11:15
Wohnort: Germany
Kontaktdaten:

Re: 90, 180, 270 Grad RotateImage

Beitrag von Thorium »

Du kannst es noch stark beschleunigen, wenn du anstatt CopyMemory mit Pointern arbeitest. Allerdings musst du dann die verschiedenen Farbtiefen separat abhandeln.
Zu mir kommen behinderte Delphine um mit mir zu schwimmen.

Wir fordern mehr Aufmerksamkeit für umfallende Reissäcke! Bild
Benutzeravatar
AND51
Beiträge: 5220
Registriert: 01.10.2005 13:15

Re: 90, 180, 270 Grad RotateImage

Beitrag von AND51 »

Ich hatte da ma was geschrieben. Für Sprites. Allerdings muss Höhe = Breite sein, da das Drehen bei quadratischen Sprites besonders effizient ist.
Schau mal hier:

http://www.purebasic.fr/german/viewtopi ... tatesprite

Lässt sich bestimmt auch für Images zurecht biegen! :allright:
PB 4.30

Code: Alles auswählen

Macro Happy
 ;-)
EndMacro

Happy End
Christian+
Beiträge: 213
Registriert: 13.07.2008 10:05
Computerausstattung: Windows 8.1 Pro
AMD Phenom II X4 955 @ 3.2 GHz
4GB RAM
NVIDIA GeForce GTX 660

Re: 90, 180, 270 Grad RotateImage

Beitrag von Christian+ »

@Thorium
CopyMemory ist echt langsam für 32 Bit Images wird jetzt drauf verzichtet weshalb diese nun einiges schneller gedreht werden wie 24 Bit Images habe den Code oben aktualisiert.

@AND51
Werde mir mal deine Sprite Lösung anschauen vielleicht kann damit auch das drehen quadratischen Images beschleunigt werden.

Edit:
Jetzt werden 24 Bit und 32 Bit Images schneller gedreht wobei 24 Bit Images mit den 3 Byte pro Pixel immer noch langsamer sind kann man das noch irgendwie schneller lösen?
Außerdem habe ich die Winkelübergabe jetzt so ähnlich wie bei der Sprite Lösung gemacht denke das ist praktischer.
Windows 8.1 Pro 64Bit | AMD Phenom II X4 955 @ 3.2 GHz | 4GB RAM | NVIDIA GeForce GTX 660
Christian+
Beiträge: 213
Registriert: 13.07.2008 10:05
Computerausstattung: Windows 8.1 Pro
AMD Phenom II X4 955 @ 3.2 GHz
4GB RAM
NVIDIA GeForce GTX 660

Re: 90, 180, 270 Grad RotateImage

Beitrag von Christian+ »

So das Ganze ist nochmal aktualisiert sollte nochmal ein Stück schneller sein auf die Unterstützung von 24 Bit Images habe ich dieses Mal allerdings verzichtet da diese nur erheblich langsamer gedreht werden können zumindest mit meinen Code und ich das sowieso nicht brauche da ich nur 32 Bit Images drehen muss.
Vor allem beim quadratische Images um 180 Grad drehen sollte der Speed unterschied sehr deutlich sein da dort wie bei AND51 seinem RotateSprite mit Swap gearbeitet wird.
Windows 8.1 Pro 64Bit | AMD Phenom II X4 955 @ 3.2 GHz | 4GB RAM | NVIDIA GeForce GTX 660
fabulouspaul
Beiträge: 120
Registriert: 01.04.2011 21:59

Re: 90, 180, 270 Grad RotateImage

Beitrag von fabulouspaul »

Supersache Christian, genau sowas hae ich gesucht!
Ich hatte vorher mit PlgBlt_ experimentiert, allerdings funktioniert das nur für Images bis zu einer gewissen Größe (ich hantiere mit sehr grossen gescannten TIFFs und JPEGs).
Da TIFFs von PB mit LoadImage als 32-Bit-Images eingeladen werden, klappt Deine Routine super. :allright:

Für JPEGs oder andere 24-Bit-Images habe ich eine kleine Routine zum konvertieren gebastelt.

Code: Alles auswählen

Structure pixel24   ;RGB-Struktur ohne Alpha-Kanal
  pixel_r.a
  pixel_g.a
  pixel_b.a
EndStructure
  
Structure pixel32   ;RGB-Struktur mit Alpha-Kanal
  pix_r.a
  pix_g.a
  pix_b.a
  pix_a.a
EndStructure

Procedure.l konvertiere24nach32(bildnummer)
  
  Protected org_output
  Protected *org_adr.pixel24
  Protected org_zeilenlaenge
  Protected org_hoehe
  Protected org_breite
  Protected kopie
  Protected kopie_output
  Protected *kopie_adr.pixel32
  Protected kopie_zeilenlaenge
  Protected i
  Protected j
  Protected k
  Protected l
  
  kopie = CreateImage(#PB_Any, ImageWidth(bildnummer), ImageHeight(bildnummer), 32)
  If kopie
    org_output = ImageOutput(bildnummer)
    If StartDrawing(org_output)
        *org_adr.pixel24 = DrawingBuffer()
        org_zeilenlaenge = DrawingBufferPitch()
        org_hoehe = ImageHeight(bildnummer)
        org_breite = ImageWidth(bildnummer)
      StopDrawing()
    Else
      ProcedureReturn -1      
    EndIf
    
    kopie_output = ImageOutput(kopie)
    If StartDrawing(kopie_output)
        *kopie_adr.pixel32 = DrawingBuffer()
        kopie_zeilenlaenge = DrawingBufferPitch()
      StopDrawing()
    Else
      ProcedureReturn -1       
    EndIf
    
    For i = 1 To org_hoehe
      k = org_zeilenlaenge
      l = kopie_zeilenlaenge
      For j = 1 To org_breite
        *kopie_adr\pix_r = *org_adr\pixel_r
        *kopie_adr\pix_g = *org_adr\pixel_g
        *kopie_adr\pix_b = *org_adr\pixel_b
        *kopie_adr\pix_a = 255
        *kopie_adr + 4
        *org_adr + 3
        k - 3
        l - 4
      Next
      *org_adr + k
      *kopie_adr + l
    Next
    
    ProcedureReturn kopie 
  Else
    ProcedureReturn -1
  EndIf 
EndProcedure
In Verbindung mit Deinem RotateImage klappt es so auch für 24-Bit Images.
Christian+
Beiträge: 213
Registriert: 13.07.2008 10:05
Computerausstattung: Windows 8.1 Pro
AMD Phenom II X4 955 @ 3.2 GHz
4GB RAM
NVIDIA GeForce GTX 660

Re: 90, 180, 270 Grad RotateImage

Beitrag von Christian+ »

So jetzt habe ich auch mal 24 Bit Images die ich rotieren will. Ich habe deswegen mal geschaut ob man das Konvertieren noch schneller hinbekommt da es für meine Anforderungen etwas zu langsam ist und dazu geschaut ob sich deine Funktion noch optimieren lässt. Ich habe sie auch noch schneller bekommen wie sie vorher war allerdings ist sie dennoch zumindest auf meinem PC langsamer wie wenn ich einfach das 24 Bit Image in ein 32 Bit Image zeichne.
Hier mal die zwei Proceduren:

Code: Alles auswählen

Procedure.l konvertiere24nach32(bildnummer)
 
  Protected org_output
  Protected *org_adr.Long
  Protected org_zeilenlaenge
  Protected org_hoehe
  Protected org_breite
  Protected kopie
  Protected kopie_output
  Protected *kopie_adr.Long
  Protected kopie_zeilenlaenge
  Protected i
  Protected j
  Protected k
  Protected l
  Protected maske = 255 << (8 * 3)
 
  kopie = CreateImage(#PB_Any, ImageWidth(bildnummer), ImageHeight(bildnummer), 32)
  If kopie
    org_output = ImageOutput(bildnummer)
    If StartDrawing(org_output)
        *org_adr = DrawingBuffer()
        org_zeilenlaenge = DrawingBufferPitch()
        org_hoehe = ImageHeight(bildnummer)
        org_breite = ImageWidth(bildnummer)
      StopDrawing()
    Else
      ProcedureReturn -1     
    EndIf
   
    kopie_output = ImageOutput(kopie)
    If StartDrawing(kopie_output)
        *kopie_adr = DrawingBuffer()
        kopie_zeilenlaenge = DrawingBufferPitch()
      StopDrawing()
    Else
      ProcedureReturn -1       
    EndIf
   
    For i = 1 To org_hoehe
      k = org_zeilenlaenge
      l = kopie_zeilenlaenge
      For j = 1 To org_breite
        *kopie_adr\l = *org_adr\l | maske
        *kopie_adr + 4
        *org_adr + 3
        k - 3
        l - 4
      Next
      *org_adr + k
      *kopie_adr + l
    Next
   
    ProcedureReturn kopie
  Else
    ProcedureReturn -1
  EndIf
EndProcedure

Code: Alles auswählen

Procedure Convert24BitTo32Bit(SourceImage24Bit.i)
  Protected Width.i, Height.i, ResultImage.i
  If Not IsImage(SourceImage24Bit)
    ProcedureReturn #False
  EndIf
  If ImageDepth(SourceImage24Bit, #PB_Image_InternalDepth) = 32
    ProcedureReturn CopyImage(SourceImage24Bit, #PB_Any)
  EndIf
  Width = ImageWidth(SourceImage24Bit)
  Height = ImageHeight(SourceImage24Bit)
  ResultImage = CreateImage(#PB_Any, Width, Height, 32)
  StartDrawing(ImageOutput(ResultImage))
  DrawImage(ImageID(SourceImage24Bit), 0, 0)
  StopDrawing()
  ProcedureReturn ResultImage
EndProcedure
Windows 8.1 Pro 64Bit | AMD Phenom II X4 955 @ 3.2 GHz | 4GB RAM | NVIDIA GeForce GTX 660
Christian+
Beiträge: 213
Registriert: 13.07.2008 10:05
Computerausstattung: Windows 8.1 Pro
AMD Phenom II X4 955 @ 3.2 GHz
4GB RAM
NVIDIA GeForce GTX 660

Re: 90, 180, 270 Grad RotateImage

Beitrag von Christian+ »

Ich habe jetzt mal die alte Version mit einer neuen besseren und schnelleren RotateImage32Bit Version ersetzt.
Windows 8.1 Pro 64Bit | AMD Phenom II X4 955 @ 3.2 GHz | 4GB RAM | NVIDIA GeForce GTX 660
Antworten