Pomoc - Szukaj - Użytkownicy - Kalendarz
Pełna wersja: [klasa] gSIP - gSIP Sophisticated Image Parser
Forum PHP.pl > Inne > Oceny
Grze_chu
Witam, chciałbym poddać pod ocenę wielmożnej społeczności PHP.pl swoje prawie 5-cio godzinne wypociny w kwestii manipulowania obrazkami w PHP.

Geneza powstania
Pewnej nocy podczas rozmyśleń spadłem na pomysł napisania klasy do operacji na obrazkach, za pomocą warstw.
Doszedłem do wniosku, że taki twór ma sens, i może się nadać na coś. Sam planuję ko używać zamiast tych image*() z PHP'owej semantyki.

Zalety
- czytelność (względna)
- łatwość obsługi (względna)

Wady
- niezbadany
- może być ciężki do zrozumienia

Plany na przyszłość
- filtry na poszczególne warstwy
- obracanie warstw
- tablica z warstwą zastąpiona obiektem (co by było bardziej OOP)

Znane bugi
- podczas skalowania obrazka z transparentnym tłem, całą wspomnianą przeźroczystość szlag trafia

Oooo tutaj jest klasa do wglądu/testu


Przykład użycia

Aby wygenerować coś takiego:



Trzeba nakodzić:

  1. $gSIP = new gSIP();
  2.  
  3. header('Content-Type: image/png');
  4.  
  5. $gSIP->createLayer('background', 300, 300)
  6. ->makeTransparent(255, 0, 255)
  7.  
  8. ->createLayer('meme_1')
  9. ->import(__APP_PATH.'/1.jpg')
  10. ->scaleLayer(100, 100)
  11. ->moveLayer(25, 25)
  12.  
  13. ->createLayer('meme_2')
  14. ->import(__APP_PATH.'/2.jpg')
  15. ->scaleLayer(100, 100)
  16. ->moveLayer(175, 25)
  17.  
  18. ->createLayer('meme_3')
  19. ->import(__APP_PATH.'/3.jpg')
  20. ->scaleLayer(100, 100)
  21. ->moveLayer(25, 175)
  22.  
  23. ->createLayer('meme_4')
  24. ->import(__APP_PATH.'/4.jpg')
  25. ->scaleLayer(100, 100)
  26. ->moveLayer(175, 175)
  27.  
  28. ->createLayer('tekst', 200, 50)
  29. ->drawRectangle(0, 0, 200, 50, true, 100, 100, 100, 30)
  30. ->moveLayer(50, 125)
  31. ->putText('gMVC -> gSIP Test', 30, 15, 5, 255, 255, 255)
  32.  
  33. ->export(gSIP::PNG);


Podobieństwo to wszelkich innych projektów jest przypadkowe, klasa pisana w oparciu o wiedzę + dokumentację na temat biblioteki GD.

Z racji że jest to moja pierwsza klasa publikowana publicznie (no... kiedyś trzeba zacząć konfrontować swoją wiedzę z innymi) to jestem otwarty na wszelką konstruktywną krytykę, przyjmę wszystko na klatę.
Klasa jest integralną częścią autorskiego frameworka gMVC, ale nic nie stoi na przeszkodzie by działać bez niego smile.gif

Skoro doczytałeś/łaś do tego punktu i właśnie marnujesz swój czas na czytanie mojego wywodu, to dziękuję za zainteresowanie tematem i pozdrawiam!
Crozin
Z OOP to to nie ma wiele wspólnego. Nie mniej jednak jestem pełen podziwu dla tego jak udało Ci się upchać to wszystko w jedną klasę.

- kompletny brak elastyczności,
- brak jakiejkolwiek możliwości rozbudowy,
- korzystanie z biblioteki GD,
- wymuszenie wykorzystania biblioteki GD,
- brak możliwości normalnego skorzystania z "biblioteki" w swoim projekcie (m.in. niedostosowanie się do PSR-0),
- brak jakiejkolwiek obsługi błędów,
- korzystanie z tablic jako struktur danych,
- jakim niby cudem ktoś miałby nauczyć się obsługi metody o takiej sygnaturze:
  1. public drawRectangle(int, int, int, int, boolean, int, int, int, int)


Generalnie: bardzo słabo
Fifi209
Destruktor nie ma tutaj sensu, przecież i tak obiekt jest niszczony.

Grze_chu
Dzięki za słowa krytyki i zainteresowaniem tematem, teraz żeby nie wyjść na obrażonego buca, pasowało by wyrazić swoje zdanie biggrin.gif

Cytat
- kompletny brak elastyczności,
- brak jakiejkolwiek możliwości rozbudowy,
- korzystanie z biblioteki GD,
- wymuszenie wykorzystania biblioteki GD,

Ciężko od "obiektowej" nakładki na GD wymagać elastyczności... Fakt faktem, nie zaznaczyłem tego w temacie. A dlaczego tak? Z tego co mi wiadomo GD to najpowszechniejszy mechanizm na publicznych serwerach PHP. A moją ideą jest tworzyć narzędzia które działają "PHP only".


Cytat
- brak możliwości normalnego skorzystania z "biblioteki" w swoim projekcie (m.in. niedostosowanie się do PSR-0),

Wynika to z tego, że osobiście nie mam nic napisane w zgodzie z PSR-0 dlatego nie widziałem takiej potrzeby, ale dziś poczytałem trochę o przestrzeniach nazw i idei PSR-0, i ma to sens... niebawem przerobię własny FW na PSR-0 i wtedy pojawi się również update do obecnego skryptu.


Cytat
- brak jakiejkolwiek obsługi błędów,

"Moja wina, moja wina, moja bardzo wielka wina" - Biję się w pierś... zapomniałem biggrin.gif


Cytat
- korzystanie z tablic jako struktur danych,

Hmm, czemu nie? Jeśli dobrze zrozumiałem, to w obecnej logice stworzenie obiektu Layer i używanie go jako kontener to takie OOP na siłę... moje zdanie.

Cytat
- korzystanie z tablic jako struktur danych,

Zamysł był prosty, miało być szybko, a napisanie we wspomnianym przez Ciebie przypadku drawRectangle($X, $Y, $width, $height, [$fill, [$r, [$g, [$b, [$a]]]]]) wydawało mi się łatwe do ogarnięcia, choć ... przespałem się z tym problemem, i doszedłem do wniosku że lepiej będzie zaimplementować generator, powiedzmy taki:

  1. gSIP::colorHEX('#FFFFFF'); lub gSIP::colorHEX('#FFF');
  2. gSIP::colorRGB(255, 255, 255);
  3. gSIP::colorRGBA(255, 255, 255, 0)
  4.  
  5. //owe "metody" w obiekcie zwracały by zasób z imagecollorallocate() a wtedy wspomniana metoda drawRectangle miała by taką postać:
  6.  
  7. drawRectangle($X, $Y, $width, $height, $fill, gSIP::colorHEX('#FFF))


Takie rozwiązanie ma sens?
Osobiście sądzę że tak, powinno ułatwić "debugowanie" i wprowadzanie zmian... trochę czytelniej w tym makaronie kodu się staje biggrin.gif


Cytat
Destruktor nie ma tutaj sensu, przecież i tak obiekt jest niszczony.

Nazwijmy to inaczej... formalność biggrin.gif Zawsze czytałem że bałagan w pamięci powinno się po sobie sprzątać.


Cytat
Z OOP to to nie ma wiele wspólnego

Na koniec rodzynek biggrin.gif Bardzo zależało mi na interfejsie fluid dla tego "wymysłu", stąd takie kompromisy... wszystko wyglądało by ładniej, gdyby gSIP::createLayer() zwracało obiekt Layer, a ten dopiero dawał możliwość operacji na samej warstwie... w sumie to nie głupie biggrin.gif

Pozdrawiam, i liczę na więcej tak konstruktywnych wypowiedzi smile.gif
Fifi209
Cytat(Grze_chu @ 19.08.2011, 00:23:38 ) *
Nazwijmy to inaczej... formalność biggrin.gif Zawsze czytałem że bałagan w pamięci powinno się po sobie sprzątać.

Skoro obiekt jest niszczony to i tak pamięć się zwolni, tak?
Grze_chu
W domyśle miałem pamięć po zasobach zalokowanych przez funkcje imagecreate*()

EDIT:
Chyba że może źle zinterpretowałem kontekst Twych wypowiedzi biggrin.gif
Crozin
Cytat
Ciężko od "obiektowej" nakładki na GD wymagać elastyczności... Fakt faktem, nie zaznaczyłem tego w temacie. A dlaczego tak? Z tego co mi wiadomo GD to najpowszechniejszy mechanizm na publicznych serwerach PHP. A moją ideą jest tworzyć narzędzia które działają "PHP only".
W temacie wątku można przeczytać o wyrafinowanej bibliotece to obróbki grafiki, więc nie oczekuj, że będę w ogóle brać pod uwagę "serwerek za 50 zł i stronkę w PHP". wink.gif
Zresztą nic nie stoi na przeszkodzie by stworzyć kilka sterowników (od GD, ImageMagicka czy nawet Gmagicka) implementujących wspólny podstawowy interfejs. A taki sterownik Imagicka mógłby dodatkowo implementować jakiś bardziej zaawansowany interfejs.
Ot, banalny sposób na zapewnienie sobie normalnej biblioteki do normalnych projektów i jakieś podstawowej (opartej o GD) dla tych uboższych.

Cytat
Hmm, czemu nie? Jeśli dobrze zrozumiałem, to w obecnej logice stworzenie obiektu Layer i używanie go jako kontener to takie OOP na siłę... moje zdanie.
Po pierwsze Twoja obecna struktura jest trochę zła. Na chwilę obecną Twój obiekt reprezentuje nic innego jak pojedynczą warstwę (co sugerują nawet metody w tym obiekcie), która ma jakieś dziwne zdolności do zmieniania się w coś innego czy łączenia innych warstw. Na dobrą sprawę powinieneś mieć obiekt Image, który jest niczym innym jak kontenerem, a dokładniej listą dwukierunkową umożliwiającą zmianę kolejności elementów (przesuwanie warstwy w górę / w dół, a więc PHP-owskie tablice nie nadają się do tego). Warstwy muszą być osobnym obiektem ponieważ mogą one wydostać się poza obiekt obrazu, chociażby po to by móc na nich zastosować filtr (rozmycie, nasycenie kolorów, odbicie lustrzane, obrót itd.). Znacznie wygodniej jest operować na obiekcie, który wymusza prawidłową strukturę, rzuci wyjątkiem jeśli zajdzie taka potrzeba, da się sklonować czy rozszerzyć oraz co najważniejsze umożliwia ukrycie danych czy jakieś wewnętrzne operacje na nich.

Cytat
Zamysł był prosty, miało być szybko, a napisanie we wspomnianym przez Ciebie przypadku drawRectangle($X, $Y, $width, $height, [$fill, [$r, [$g, [$b, [$a]]]]]) wydawało mi się łatwe do ogarnięcia, choć ... przespałem się z tym problemem, i doszedłem do wniosku że lepiej będzie zaimplementować generator, powiedzmy taki: [...] Takie rozwiązanie ma sens?
Osobiście sądzę że tak, powinno ułatwić "debugowanie" i wprowadzanie zmian... trochę czytelniej w tym makaronie kodu się staje
Kolor sam w sobie jest obiektem. W jego przypadku rzeczywiście powinno być kilka możliwości utworzenia go bo przecież kolor można zdefiniować na wiele sposobów (podstawowe RGB(A) i HSB(A), w dodatku ten pierwszy wprowadza się albo jako r, g, b, (a) albo rgb(a)). Na dobrą sprawę mógłbyś mieć interfejs definiujący metody getR(), getG(), getB(), getAlpha(), getRGBA oraz dwie podstawowe klasy implementujące go ColorRGB, ColorHSB. Bo przecież poza zdefiniowaniem koloru można na nim wykonywać sporo operacji (rozjaśnianie, przycieminie, regulowanie poziomu nasycenia, taki obiekt musi być możliwy do sklonowania, w przypadku HSB można łatwo manipulować również samym kolorem bazowym itd.).
Swoją drogą podobna jest sytuacja w przypadku punktu czy wymiarów.

Mając tak zdefiniowane obiekty nagle interfejs staje się znacznie prostszy i czytelniejszy:
  1. drawRectangle(Point $location, Dimension $dimension, Color $color);
  2. fillRectangle(Point $location, Dimension $dimension, Color $color);


Cytat
Nazwijmy to inaczej... formalność Zawsze czytałem że bałagan w pamięci powinno się po sobie sprzątać.
Masz rację, ale w PHP masz coś takiego jak Garbage Collector, który się tym zajmuje. W dodatku z samej natury PHP (cała pamięć jest zwalniania po zakończeniu działania skryptu) jest dosyć ciężko doprowadzić do poważnego wycieku pamięci. Jawne zamykanie zasobów (szczególnie tych niewspółdzielonych jak np. obraz w pamięci wygenerowany przez GD) jest konieczne wtedy gdy wiesz, że GC nie zdąży posprzątać po Tobie (np. w przypadku gdy operujesz na 5-ciu obrazach). Przy czym nic złego nie dzieje się przy jawnym zamknięciu zasobu, więc możesz śmiało korzystać z tego - sprawia to, że kod jest nieco bardziej "verbose".

Cytat
Na koniec rodzynek Bardzo zależało mi na interfejsie fluid dla tego "wymysłu", stąd takie kompromisy... wszystko wyglądało by ładniej, gdyby gSIP::createLayer() zwracało obiekt Layer, a ten dopiero dawał możliwość operacji na samej warstwie... w sumie to nie głupie
Fluid interface uda Ci się zachować i przy normalnym kodzie (co najwyżej będziesz musiał obejść ułomność PHP jaką jest brak obsługi new ClassName()->doSth() przez statyczną metodę). Zresztą dobry interfejs to nie taki, gdzie musisz wpisać jak najmniej znaczków, a taki który Cię nie ogranicza i jest logiczny. Na chwilę obecną Twój (w normalnym przypadku użycia, gdzie taki kod będzie przeplatany z innym) z logicznością nie za bardzo się lubi. wink.gif

EDIT:
Swoją drogą taka biblioteka do manipulacji obrazami to świetne ćwiczenie z OOP, bo na prawdę występuje to masa przeróżnych konstrukcji, których w PHP gdzie indziej ciężko szukać.
To jest wersja lo-fi głównej zawartości. Aby zobaczyć pełną wersję z większą zawartością, obrazkami i formatowaniem proszę kliknij tutaj.
Invision Power Board © 2001-2025 Invision Power Services, Inc.