Ośla łączka w Hollywood, cz. 2 – bazgrolnik podejście drugie

W pierwszym odcinku pokazałem prosty szkicownik. Białe linie na czarnym tle i gumka. Wystarczy żeby szkicować. Minimalizm formy i kodu (po oczyszczeniu z komentarzy to ledwie 17 linii). Wpis skończył się pomysłami jak rozwijać to dalej. W tym odcinku miało być coś innego, ale ponieważ ktoś podjął temat w komentarzach – jest kontynuacja.

Niemniej bezpośrednim impulsem do pracy nad kodem było nieprawidłowe działanie programiku pod AmigaOS 4. Nie dało się tam rysować swobodnie z ręki, jedynie długimi pociągnięciami z przerwami, w zasadzie prostymi odcinkami. W takiej formie było to nieakceptowalne do użytkowania.

Poprawki szkicownika

Brałem pod uwagę kilka możliwości. Pierwszą jaką wykluczyłem to wydajność sprzętu. Nie wnikając w szczegóły, dyplomatycznie napiszę: ten system wymaga, żeby programista pilnował wszystkiego sam.

Dołożyłem buforowanie i czyszczenie ekranu, żeby nie musieć restartować programu chcąc rysować od nowa. Czyszczenie ze względu na buforowanie działało w szczególny sposób, to znaczy oprócz wciśnięcia spacji konieczna była jednoczesna akcja myszą, nawet drobny ruch. Ta dziwaczność wynikała z tego, że do odświeżenia ekranu Hollywood wymaga zdarzenia – dopóki bufor był pusty nie odświeżał. Normalnie taki „ficzer” byłby niezauważalny w programie z menu. Sięgnięcie myszą po File –> New i ściągnięcie wskaźnika myszy z powrotem to naturalne czynności, które maskowałyby fakt, że w takim kodzie ruch myszą do odświeżania był konieczny.

Poprawki coś dały, pod AmigaOS 4 dało się normalnie rysować, ale pojawił się inny błąd – po puszczeniu przycisku myszy i ruchu dalej powstawała linia. Zanim to opiszę, wyjaśnię buforowanie.

Istnieje świetny program graficzny napisany w Hollywood o nazwie Polar Paint, który nie ma takich problemów (prędkość rysowania pod AmigaOS 4). Jest tak dlatego, ponieważ rysuje na brushu (bitmapie) w pamięci. Mój program rysuje bezpośrednio na ekranie, co wymaga częstszego odświeżania i więcej interakcji z kartą graficzną. Nie chciałem używać brusha – w tamtym programie to ma sens ponieważ opcje „undo” (cofania) wymagają tworzenia kopii w postaci kolejnych brushy. Nawet jest możliwość zapisywania ich na dysku gdy jest mało pamięci RAM. Mój programik nie ma „undo” i wystarczyłoby mi proste rozwiązanie. W podwójnym buforowaniu bufor przedni przechowuje obraz aktualnie widoczny na ekranie, a bufor tylny służy do rysowania; Flip() kopiuje bufor tylny do przedniego, aby zaktualizować ekran jednym ruchem, co eliminuje migotanie. Czyli rysuje się „na zapleczu”, a bufor przedni istnieje, by oddzielić rysowanie od wyświetlania.

Ciągnąca się linia – ten problem rozwiązano, resetując współrzędne myszy po puszczeniu przycisku, co zapobiega rysowaniu linii przy dalszym ruchu. Czyli kolejna wersja (druga poprawka) zawierała:
– czyszczenie ekranu spacją (Cls) już bez konieczności ruszania myszą
– brak ciągnącej się linii – współrzędne resetują się po puszczeniu przycisku myszy i po wciśnięciu spacji.

No ale to nie koniec. W oczekiwaniu na odpowiedź zwrotną (działanie poprawionej wersji pod AmigaOS 4), zacząłem się przyglądać i udało się wejść w stan całkowitego zatopienia się w temacie. Wydarzyło się coś, co można porównać do poniższego mema.

Rozwój programu



@DISPLAY {Title = "Program do bazgrania 2", Width = 800, Height = 600}

; Włączamy podwójne buforowanie dla płynniejszego rysowania
BeginDoubleBuffer()

; Współrzędne poprzedniej pozycji kursora myszy (-1 oznacza brak punktu startowego)
Local poprzednieX = -1, poprzednieY = -1
; Aktualna grubość linii rysowania (domyślnie 5)
Local aktualnaGrubosc = 5
; Aktualna grubość gumki (domyślnie 15)
Local aktualnaGumka = 15
; Aktualny kolor rysowania (domyślnie biały)
Local aktualnyKolor = #WHITE

; Definiujemy obsługę zdarzeń myszy i klawiatury
InstallEventHandler({
	OnMouseDown = Function()
		; Ustawiamy współrzędne startowe tylko przy pierwszym wciśnięciu myszy
		If poprzednieX = -1 And poprzednieY = -1 Then poprzednieX, poprzednieY = MouseX(), MouseY()
	EndFunction,
	
	OnMouseUp = Function()
		; Resetujemy współrzędne po puszczeniu przycisku myszy, zapobiegając ciągnącej się linii
		poprzednieX, poprzednieY = -1, -1
	EndFunction,
	
	OnRightMouseUp = Function()
		; Resetujemy współrzędne po puszczeniu PPM, zapobiegając ciągnącej się linii
		poprzednieX, poprzednieY = -1, -1
	EndFunction,
	
	OnMouseMove = Function()
		Local obecneX, obecneY = MouseX(), MouseY()
		; Rysujemy linię w wybranym kolorze i grubości lewym przyciskiem myszy, jeśli mamy punkt startowy
		If IsLeftMouse() And poprzednieX >= 0 And poprzednieY >= 0 Then Line(poprzednieX, poprzednieY, obecneX, obecneY, aktualnyKolor, {Thickness = aktualnaGrubosc})
		; Wymazujemy czarną linią prawym przyciskiem myszy, jeśli mamy punkt startowy
		If IsRightMouse() And poprzednieX >= 0 And poprzednieY >= 0 Then Line(poprzednieX, poprzednieY, obecneX, obecneY, #BLACK, {Thickness = aktualnaGumka})
		; Aktualizujemy współrzędne tylko podczas rysowania lub wymazywania
		If IsLeftMouse() Or IsRightMouse() Then poprzednieX, poprzednieY = obecneX, obecneY
		Flip()  ; Aktualizujemy ekran po rysowaniu, zapewniając płynność
	EndFunction,
	
	OnKeyDown = Function()
		; Czyścimy ekran spacją i resetujemy współrzędne (aktualizacja ekranu w pętli zdarzeń)
		If IsKeyDown("SPACE") Then Cls(#BLACK); poprzednieX, poprzednieY = -1, -1
		; Zmiana koloru rysowania pod klawiszami 1-0
		If IsKeyDown("1") Then aktualnyKolor = #WHITE           ; Biały
		If IsKeyDown("2") Then aktualnyKolor = #RED             ; Czerwony
		If IsKeyDown("3") Then aktualnyKolor = #BLUE            ; Niebieski
		If IsKeyDown("4") Then aktualnyKolor = #YELLOW          ; Żółty
		If IsKeyDown("5") Then aktualnyKolor = #GREEN           ; Zielony
		If IsKeyDown("6") Then aktualnyKolor = RGB(255, 128, 0) ; Pomarańczowy
		If IsKeyDown("7") Then aktualnyKolor = #PURPLE          ; Fioletowy
		If IsKeyDown("8") Then aktualnyKolor = RGB(139, 69, 19) ; Brązowy
		If IsKeyDown("9") Then aktualnyKolor = #GRAY            ; Szary
		If IsKeyDown("0") Then aktualnyKolor = #BLACK           ; Czarny (do detali)
		; Zmiana grubości linii pod klawiszami a-c (tylko małe litery)
		If IsKeyDown("a") Then aktualnaGrubosc = 5     ; Domyślna grubość
		If IsKeyDown("b") Then aktualnaGrubosc = 10    ; 2x grubsza
		If IsKeyDown("c") Then aktualnaGrubosc = 15    ; Jeszcze grubsza
		; Zmiana rozmiaru gumki pod klawiszami d-f (tylko małe litery)
		If IsKeyDown("d") Then aktualnaGumka = 15      ; Normalna gumka
		If IsKeyDown("f") Then aktualnaGumka = 40      ; Duża gumka
	EndFunction
})

; Główna pętla programu
Repeat
	WaitEvent  ; Czekamy na zdarzenie (myszy lub klawiatury)
	Flip()     ; Aktualizujemy ekran po każdym zdarzeniu, umożliwiając natychmiastowe czyszczenie
Forever

; Wyłączamy buforowanie na koniec programu
EndDoubleBuffer()

Właściwie większość kodu i komentarzy jest odświeżona. Oczywiście można się krzywić, że nie ma zapisu obrazka do pliku, ale to jest łatwe przez schowek. Chciałem utrzymać minimalistyczny kod i dać jak najwięcej. W sumie większość komend w kodzie jest powtarzalnych, nie ma wielu nowości. Za to funkcjonalność wzrosła znacznie.

Aktualnie:
– jest normalnie działające czyszczenie ekranu
– są kolory
– dodano różne grubości bazgrania
– dołożono grubą gumkę

Testy nowej wersji

Jak dałem do testowanie 'recedentowi’ to u niego pojawił się też „ficzer” z ciągnącą linią. Mianowicie po skończeniu mazania, gdy chciało się rysować, pojawiała się linia od ostatniego punktu gumki do punktu rozpoczęcia rysowania. Typowy „ficzer” (błąd dający nieplanowaną funkcjonalność) można było wykorzystać do rysowania idealnie prostych linii.

Żeby tego nie mieć (wystrzelonej linii po wznowieniu rysowania), przed rozpoczęciem rysowania w nowym miejscu trzeba było „na sucho” kliknąć i dopiero potem przytrzymać LPM aby rysować dalej.

Winna była moja pierdołowatość, przy dodawaniu nowości zapomniałem o resetowaniu gumki. To znaczy ustawiłem dla lewego przycisku, ale zapomniałem o prawym (OnRightMouseUp). Zapewne przez to, że zapis dla lewego to był OnMouseUp a nie OnLeftMouseUp. Ponieważ chętnie dzielę się kodem, 'recedent’ to wyłapał, co mi oszczędziło kawałek życia. Czasem człowiek patrzy i nie widzi. Przy okazji podrzucił mi obrazki z testowania.

Kolory zawierają czarny, co może budzić zdziwienie. Uznałem, że może się przydać do rysowania na innym kolorze, żeby nie używać gumki. A co złego byłoby w używaniu gumki jako czarnego koloru? Ona jest grubsza niż zwykła linia rysująca. Kolory wybrałem takie, które lepiej wyglądają na czarnym. No i z racji tego, co zazwyczaj się bazgrze, to jest proste, że brąz i zieleń bardziej przydatne niż np. róż czy cyjan. Typowy pierwszy obrazek jaki się rysuje to jest przecież domek i drzewko. Dwa kolory musiałem wprowadzić w formacie RGB, bo nie są zdefiniowane w Hollywood słownie. Uznałem za konieczność wprowadzić grubą gumkę, tak jak była gąbka do tablicy szkolnej.

Klawiszologia

Kolory
1 – biały
2 – czerwony
3 – niebieski
4 – żółty
5 – zielony
6 –pomarańczowy
7 –fioletowy
8 – brązowy
9 – szary
0 – czarny

Grubości pisaków
a – zwykły 5 pikseli
b – grubszy 10 pikseli
c – najgrubszy 15 pikseli

Gumki
f – gruba (f jak fat) 40 pikseli
n – zwykła (n jak normal) 15 pikseli

Co dalej?

Nie chcę rozwijać tego programu. To miał być – i ciągle jest – prosty program do bazgrania. Obsługiwany „z łapy”, bez menu. Dla mnie rozwijania kolejnego „Painta” jest bez sensu. W ogóle nie mam potrzeby tworzenia programów, których nie będę używał. Jeśli coś bym sobie „ulepił”, to na przykład kalkulator akwarystyczny czy program do szyfrowania starożytnymi algorytmami. Coś hobbystycznego dla siebie.

Muszę przemyśleć, czy w ogóle jest sens pokazywać kod publicznie. Bo nikt tego nie podejmuje, a narażam się tylko na krytykę. A to odbiera mi radość z hobby, Ja sie uczę i dzielę się kodem, tak to należy traktować. A że ktoś potrafi lepiej – nie wątpię.

Tak że „ośla łączka” jest otwarta, ale jakie będą dalsze tematy – to zależy od komentarzy. Musi być interakcja, a nie pamiętnik.

Ten post ma jeden komentarz

  1. Darek

    Mona Lisa wyszla calkiem zgrabnie 🙂

Dodaj komentarz