Blitzbasic Tutorial |
AppTitle "Bitmap to Rectangle Konverter by bob Jan. 2006" Const ScreenX =1024 Const ScreenY =768 Const ScreenC =16 Const ScreenM =2 Graphics ScreenX,ScreenY,ScreenC,ScreenM mask = loadimage("inventar_mask.bmp") final= loadimage("inventar.bmp") Type rec Field x,y,b,h End TypeFinde_Bereiche mask SetBuffer BackBuffer() While Not KeyHit(1) Color 255,0,0 MX =Mousex() MY = MouseY() DrawBlock final,0,0 bCount =0 For r.rec = Each rec bCount = bcount+1 If RECTSOVERLAP (mx, my, 1,1, r\x, r\y,r\b,r\h) Then Rect r\x, r\y, r\b, r\h, 1 Color 255,255,255 If mousedown(1) then Text r\x+5 ,r\y+3,"CLICK!" Else Color 255,0,0 Rect r\x, r\y, r\b, r\h, 0 Text r\x+5 ,r\y+3,bCount EndIf EndIf Next Flip WendEnd Function Finde_Bereiche(img) SetBuffer ImageBuffer(img) LockBuffer ImageBuffer(img) endeX = ImageWidth(img) endeY = Imageheight(img) For y = 0 To endeY-1 For x = 0 To endeX rgb = ReadPixelFast(x,y) And $FFFFFF If rgb <> 0 And FoundStart=False Then DebugLog rgb r.rec = New rec r\x = x r\y = y FoundStart = True DebugLog "Found x,y at " + x +"|"+y EndIf If Rgb = 0 And Foundstart = True Then ;Horizontales End gefunden r\b = x - r\x Goto Weiter EndIf Next Next If foundstart = False Then Return EndIf .weiterx=x-1 For y = r\y To endeY rgb = ReadPixelFast(x,y) And $FFFFFF If rgb = 0 Then r\h = y-r\y Exit EndIf Next If r\h =0 then r\h = y-r\y UnlockBuffer ImageBuffer(img) Color 0,0,0 Rect r\x,r\y,r\b,r\h Finde_Bereiche img End Function |
Funktionsweise: Anhand eines Schwarz weis Bitmaps (1 Bit Farbtiefe, also rein schwarz weis) werden alle weisen Stellen der Grafik identifiziert und in Rectangle Typen abgelegt. Das Programm arbeitet dabei sämtliche Pixel durch. Da dieses Programm ein Tool ist, und nicht Teil irgendeiner Engine werden soll, ist der Zeitaufwand den das Programm dabei benötigt unerheblich. Ich war dann zum Schluss dennoch überrascht dass es so schnell ging. Erläuterungen zum Code. |
Const ScreenX =1024 Const ScreenY =768 Const ScreenC =16 Const ScreenM =2 Graphics ScreenX,ScreenY,ScreenC,ScreenM mask = loadimage("inventar_mask.bmp") final= loadimage("inventar.bmp") kopie = CopyImage(mask) Type rec Field x,y,b,h End Type |
Es werden Konstante für Die Auflösung , Farbtiefe und Windows Mode Festgelegt.
Danach wird der Screen mittels Graphics Initialisiert und
2 Bitmaps geladen. Einmal das Fertige Bild (final) (Siehe oben) und einmal eine rein schwarz weis Entsprechung (mask).
Das bedarf wohl keiner weiteren Erklärung. Der Type "rec" dient zur Aufnahme
der Eigenschaften eines Rechtecks. Die Startkoordinate X,Y die der oberen linken Ecke eines Rechtecks Entsprechen sowie
die breite (B) und höhe (H) des Rechtecks. Verwechselt Angaben B und H nicht mit Koordinaten.
Es sind nämlich keine. Es folgt der Aufruf der Funktion die das Kernstück ist. Finde_Bereiche mask Hier wird als Parameter der Funktion das Image "mask" übergeben. (Habe das Image so genant weil es mich an alte Projekte erinnerte wo Schwarzweisbilder dazu dienten einen bestimmten Bereich einer Grafik als Transparent zu deklarieren.) |
Function Finde_Bereiche(img) SetBuffer ImageBuffer(img) LockBuffer ImageBuffer(img) endeX = ImageWidth(img) endeY = Imageheight(img) |
Die Funktion verlangt als einzigen Parameter ein geladenes Bild. Der Imagebuffer des Bildes wird anschließend gesperrt
und breite und höhe des Bildes ermittelt. |
For y = 0 To endeY-1 For x = 0 To endeX rgb = ReadPixelFast(x,y) AND $FFFFFF |
Es wird eine Doppelschleife (for Y, for X) eingeleitet. Wir stellen fest das die Schleifen sich über die gesamte Breite * Höhe des Bittmaps erstreckt. Bei der Auflösung des Bildes (736 * 736) sind dies immerhin 541696 Durchläufe. Die Variable rgb nimmt den Farbwert der Function ReadPixelFast auf. X und Y sind, na klar, die aktuelle x,y, position der Schleife die zusätzliche Angabe "And $FFFFFF" sorgt dafür das in der Variable rgb eine "Anständige" Zahl gespeichert wird (0 für schwarz und 16777215 für weis). |
If rgb <> 0 And FoundStart=False Then r.rec = New rec r\x = x r\y = y FoundStart = True 'DebugLog "Found x,y at " + x +"|"+y EndIf |
Eine erste Entscheidung. Hier wird überprüft ob auf der Bitmap an aktueller Schleifenposition (x,y) eine andere Farbe als schwarz gefunden wurde UND ob die Variable FoundStart noch den Wert "False" besitzt. (Die Variable wird gleichzeitig mit ihrem ersten auftauschen erstellt und ist somit, da sie keinen Wert hat, in jedem Fall = False.) Das heißt. Sobald das erste weise Pixel auftaucht, wird ein erster Type rec erstellt. Die aktuellen Koordinaten werden in den eigenschaften des Typs abgelegt. Foundstart wird nun auf True gesetzt somit kann diese If Abfrage in keinem Fall mehr erneut ausgeführt werden. |
If rgb = 0 And Foundstart = True Then ;Horizontales End gefunden r\b = x - r\x Goto Weiter EndIf Next Next |
Eine zweite If Bedingung überprüft nun im weitern Verlauf der Doppelschleife ob ein schwarzes Pixel gefunden wurde UND ob FoundStart=True ist. Das heißt dass diese Bedingung nur dann erfüllt sein kann wen die vorherige Bedingung irgendwann mal erfüllt wurde. Gehen wir mal davon aus dass zuvor ein Weises Pixel gefunden wurde. (Foundstart ist dann = True) . Da wir wissen das wir die Farbe der Pixel erst auf Horizontaler Achse überprüfen, am ende der Bitmap ein Zeile tiefer Springen und dann wieder in horizontaler Richtung prüfen (Schreibmaschinen mässig), haben wir nun eine weise grade Linie abgearbeitet und sind nun an deren Ende auf ein schwarzes Pixel gestoßen. Hier haben wir dan den rechten Rand eines Bereichs gefunden. Wir notieren als die feststehend Breite. r\b =x -r\x (Breite = aktuelle Position - der bereits gefundenen Startposition des Rechtecks. Wir erinnern uns B ist KEINE Koordinate.) Sobald wir diesen Punkt erreicht haben können wir die Doppelschleife komplett verlassen. Wir springen einfach ab mittels "Goto Weiter". .weiter |
If foundstart = False Then Return |
Die erneute Abfrage ob foundstart = False ist dient dazu die Funktion an dieser Stelle mittels Return zu verlassen. Dies wäre z.B. dann der Fall wen die gesamte Bitmap schwarz wäre. Da die Sprungmark ".weiter" allerdings hinter der If abfrage liegt, wird diese nicht ausgeführt (Wenn wir davon ausgehen das wir vorher schon etwas "weises" gefunden haben. Neue Schleife neues Glück. |
X = x-1 For y = r\y To endeY rgb = ReadPixelFast(x,y) AND $FFFFFF If rgb = 0 Then r\h = y-r\y Exit EndIf Next |
Wir erinnern uns. Wir haben für das erste zu erstellende Rechteck Bereits die X,Y Koordinaten sowie die Breite herausgefunden. Um die Angaben mit der Höhe des Rechtecks zu komplettieren brauchen wir nur eine Schleife. Sie beginnt bei der aktuellen y Position und endet, wenn nicht vorher die Höhe gefunden wurde, am unteren Rand der Bitmap. Vorsicht aber. Wir haben die Doppelschleife auf einem schwarzen Pixel verlassen. Wir müssen erstmal zurück in den weisen Bereich. Deshalb: x=x-1. Wir erfassen nun mit ReadPixelfast erneut die Farbe. Sobald diese wieder schwarz ist, haben wir auch die Höhe des Rechtecks ermittelt. "r\h = y-r\y". Wir verlassen nun mit "Exit" auch diese Schleife. Zur Vorsicht überprüfen wir noch mal ob auch wirklich etwas als Breite eingetragen wurde. Dies Könnte notwendig sein wen der weise Bereich bis zur letzten Y Position geht. Ein schwarzer Pixel würde dann nicht gefunden und die Breite wäre 0. Das wäre nicht so Toll. (Man könnte beim Finden eines schwarzen Pixels auch nur die Schleife einfach nur verlassen und die Breite anhand des aktuellen Stands von Y ermitteln. Aber was soll's.) Das Ende Naht. Oder? |
UnlockBuffer ImageBuffer(img) Color 0,0,0 Rect r\x,r\y,r\b,r\h Finde_Bereiche img |
Wir sind kurz vor dem Ende der Funktion. Einige mögen sich schon gedanken gemacht haben was den mit den anderen Rectangels passiert. Einige merken es aber auch erst jetzt, da ich es erwähne. Jetzt kommt der Trick bei der ganzen Sache. Wir hebn mittels UnlockBuffer erstmal wieder die Sperrung des "Mask" Bitmaps auf um mit normalen Zeichenfunktionen auf dem Buffer zu malen. Da wir ja schon unser erstes Rectangle erfolgreich gefunden haben, testen wir es direkt mal aus. Rect r\x, r\y ,r\br r\h. Wichtig ist das wir unbedingt die aktuelle Farbe für Zeichnungsaktionen auf schwarz setzen. Color 0,0,0. Somit haben wir grade unser kompletten weisen ersten Bereich mit schwarz übermalt. Anschließend rufen wir die Funktion einfach erneut auf. (Rekursion. U.a. eine Funktion die sich selbst aufruft.) Das ganze Spiel beginnt somit von neuem. Allerdings….. Wir wissen ja nun das ein weiser Bereich nicht erneut gefunden werden kann. Wir haben ihn grade komplett übermalt. D.h. Früher oder später ist das ganze Image schwarz. Dann tritt folgendes ein. |
If foundstart = False Then Return |
Erinnert Ihr euch? Foundstart liefert bei komplett schwarzen Bildern allein schon deshalb weil die Variable dann nie erstellt wird.
Ist die Funktion nun endlich mittels Return verlassen, brauchen wir nur noch das Ergebnis unserer Bemühungen anzuzeigen.
Auf eine großartige Kommentierung verzichte ich hier weil jeder der soweit durchgehalten hat dies locker selber enträtseln kann. |
SetBuffer BackBuffer() While Not KeyHit(1) Color 255,0,0 MX =Mousex() MY = MouseY() DrawBlock final,0,0 bCount =0 For r.rec = Each rec bCount = bcount+1 If RECTSOVERLAP (mx, my, 1,1, r\x, r\y,r\b,r\h) Then Rect r\x, r\y, r\b, r\h, 1 Color 255,255,255 If mousedown(1) then Text r\x+5 ,r\y+3,"CLICK!" Else Color 255,0,0 Rect r\x, r\y, r\b, r\h, 0 Text r\x+5 ,r\y+3,bCount EndIf EndIf Next Flip WendEnd |
Eine simple Endlosschleife wie sie in den meisten aller bb Programme zu finden ist. Wir zeigen die Originalgrafik und legen blenden zu Testzwecken die Rectangles ein. Anmerkungen: Der "Trick" einfach das zuletzt erstellte Rechteck dazu zu verwenden sein weises "Elternteil" einfach zu killen und die Funktion wieder mit der Doppelschleife ab 0,0 zu initialisieren, mag dem ein oder andern Programmiere als unsauber erscheinen. Es gibt eine reihe Ansätze Zeit bei der Funktion zu sparen. Zeit die mir allerdings dann für andere Projekte fehlen würde und die ich dazu verendete diese Tutoral zu schreiben. Seid also bitte nicht zu streng mit mir. Ich würde mich im übrigens sehr darüber freuen wenn jemand aus der "GUI Liga" für diese kleine Tool eine Benutzerfreundlich, kleine Oberfläche erstellen könnt. Eine Dateiauswahl für Bitmaps wäre sehr schön. Weiterhin nützt das ganze Programm natürlich auch nicht viel wen die erzeugten Rectangle Typen nicht in eine Datei gespeichert werden. Sie sollen ja später in einem ganz anderen Programm Verwendung finden. Der Type Rec könnte auch noch ein Zusätzliches Field gebrauchen um z.B. ein feste Verbindung zu der Bitmap zu gewährleisten. Danke und viel Spaß beim tüfteln. |
rpg@iomagic.de |