Tworzenie przezroczystego obrazka png w Delphi 7

Tworzenie przezroczystego obrazka png w Delphi 7
AS
  • Rejestracja:około 8 lat
  • Ostatnio:9 miesięcy
  • Postów:48
0

Co chcę osiągnać...

  1. Chcę stworzyć obrazek o rozmiarach 16x16.
  2. W pierwszej fazie muszę wylać biały kolor
  3. W drugiej fazie muszę ustawić przezroczystość tego białego koloru na 127 (50%)
  4. W ostatniej fazie muszę nałożyć biały pasek nieprzezroczysty.

Obrazek dla łatwiejszego zrozumienia o co mi chodzi:

obrazek.png

Do tego celu mam zamiar użyć PNGImage ( http://proger.i-forge.net/%D0%9A%D0%BE%D0%BC%D0%BF%D1%8C%D1%8E%D1%82%D0%B5%D1%80/Delphi/[20120225]%20Useful%20Delphi%20packages/ )

Problem z tym że nie widzę możliwości pracowania na kanale Alfa. Taki

Kopiuj
PNG.Canvas.Pixels

operuje tylko na RGB

edytowany 5x, ostatnio: flowCRANE
flowCRANE
Moderator Delphi/Pascal
  • Rejestracja:ponad 13 lat
  • Ostatnio:18 minut
  • Lokalizacja:Tuchów
  • Postów:12171
2

TPNGImage nie jest konieczne, tak samo jak kanał alpha. Wszystko co potrzebujesz można wykonać za pomocą standardowej klasy TBitmap, przechowującej składowe pikseli w formacie BGR.


Twój przypadek jest dość prosty w implementacji, dlatego że potrzebujesz ”wylać” kolor biały na istniejący obraz, a to nic innego jak rozjaśnienie obrazu. Dlatego zamiast robić protezę prezroczystości białego kwadratu, powinieneś się skupić na algorytmie rozjaśniającym piksele (a ten jest krótki i bardzo prosty). Natomiast namalowanie finalnej białej linii to nic innego jak wywołanie Canvas.MoveTo i Canvas.LineTo (AFAIR w Delphi 7 nie ma Canvas.Line, więc dwie metody zamiast jednej).

Druga sprawa – Canvas.Pixels jest powolny. Właściwość ta używa gettera, który najpierw pozyskuje wskaźnik na konkretny piksel w całym bloku danych obrazu, następnie łączy wszystkie składowe w 32-bitową liczbę i ją zwraca. Ty musiałbyś znów tę liczbę podzielić na składowe, zmodyfikować je, złączyć w jedną liczbę i za pomocą Canvas.Pixels wpisać do pamięci obrazu. A setter tej właściwości znów musiałby szukać miejsca, rozdzielać wartość na składowe i wpisywać do pamięci. To dość powolne, choć w przypadku tak małego obrazu, efektywność nie ma większego znaczenia.


Sugeruję więc skorzystanie z właściwości TBitmap.ScanLine do pozyskania wskaźników na pierwszy piksel każdego rzędu oraz z prostej arytmetyki do rozjaśniania składowych.

Poniżej podaję deklaracje potrzebnych struktur (te mogą istnieć w bibliotece standardowej), a także definicje funkcji rozjaśniającej składową oraz tej głównej, rozjaśniającej cały obraz. Kod pisany z palca, więc wymaga sprawdzenia. Tym bardziej, że Delphi 7 nie widziałem na oczy od wielu lat.

Kopiuj
type
  TRGBTriple = packed record
    B, G, R: Byte;
  end;

type
  PRGBTripleArr = ^TRGBTripleArr;
  TRGBTripleArr = packed array [0 .. MaxInt div SizeOf(TRGBTriple) - 1] of TRGBTriple;
  
  function LightenComponent(AComponent, APercent: Byte): Byte;
  begin
    APercent := 100 - APercent;
    Result := Trunc(AComponent * APercent / 100) + Round(255 - APercent / 100 * 255);
  end;
  
  procedure LightenBitmap(ABitmap: TBitmap; APercent: Byte);
  var
    Line: PRGBTripleArr;
    LineIndex, PixelIndex: Integer;
  begin
    ABitmap.BeginUpdate();
    try
      for LineIndex := 0 to ABitmap.Height - 1 do
      begin
        Line := ABitmap.ScanLine[LineIndex];
        
        for PixelIndex := 0 to ABitmap.Width - 1 do
          with Line^[PixelIndex] do
          begin
            B := LightenComponent(B, APercent);
            G := LightenComponent(G, APercent);
            R := LightenComponent(R, APercent);
          end;
      end;
    finally
      ABitmap.EndUpdate();
    end;
  end;

W razie czego możesz sobie stworzyć osobny typ dla procentażu:

Kopiuj
type
  TPercent = 0 .. 100;

Taki typ możesz wykorzystać do deklaracji parametrów obu funkcji (dla APercent).

Na koniec, jedyne co musisz zrobić, to wywołać funkcję LightenBitmap – w pierwszym parametrze podać bitmapę źródłową, a w drugim procentowy stopień rozjaśnienia (im wyższy, tym obraz zostanie bardziej rozjaśniony; jeśli podasz 0 to kolory nie zmienią się, a jeśli 100 to obraz wyjdzie biały):

Kopiuj
LightenBitmap(Bitmap, 50);  // rozjaśnienie o połowę skali

I to tyle.


Pracuję nad własną, arcade'ową, docelowo komercyjną grą z gatunku action/adventure w stylu retro (pixel art), programując silnik i powłokę gry od zupełnych podstaw, przy użyciu Free Pascala i SDL3. Więcej informacji znajdziesz na moim mikroblogu.
edytowany 15x, ostatnio: flowCRANE
0
flowCRANE
Moderator Delphi/Pascal
  • Rejestracja:ponad 13 lat
  • Ostatnio:18 minut
  • Lokalizacja:Tuchów
  • Postów:12171
0

Owszem, można dociągnąć sobie bibliotekę i skorzystać z gotowego rozwiązania. Mimo wszystko warto też wiedzieć jak wyglądają dane obrazu, jak działa ScanLine i co można z tym wszystkim zrobić. A zrobić można bardzo dużo, z reguły kilkoma linijkami kodu. ;)


Pracuję nad własną, arcade'ową, docelowo komercyjną grą z gatunku action/adventure w stylu retro (pixel art), programując silnik i powłokę gry od zupełnych podstaw, przy użyciu Free Pascala i SDL3. Więcej informacji znajdziesz na moim mikroblogu.
AS
  • Rejestracja:około 8 lat
  • Ostatnio:9 miesięcy
  • Postów:48
1

Dzieki za odpowiedź! Tak czy siak udało mi się to zrobić w TPNGObject

Kopiuj
PNG := TPNGObject.Create;
PNG.CreateBlank(COLOR_RGBALPHA, 8, 16, 16);
PNG.Canvas.Brush.Style := bsSolid;
PNG.Canvas.Brush.Color := RGB(255, 255, 255);
PNG.Canvas.FillRect(Rect(0, 0, 16, 16));

for x := 0 to 15 do
  for y := 0 to 15 do
    if y = 8 then
      PNG.AlphaScanline[x][y] := 255
    else
      PNG.AlphaScanline[x][y] := 127;

Dodam że ten PNG będzie użyty jako ikonka w trayu (wykres).

edytowany 4x, ostatnio: flowCRANE
flowCRANE
Można i tak – w końcu przede wszystkim liczy się poprawność działania. Ale pamiętaj, że alternatywy istnieją i nie wymagają dodatkowych pakietów czy bibliotek. ;)
flowCRANE
Moderator Delphi/Pascal
  • Rejestracja:ponad 13 lat
  • Ostatnio:18 minut
  • Lokalizacja:Tuchów
  • Postów:12171
0

Jak tak teraz patrzę na ten wątek, to wydaje mi się, że nie zrozumiałem co chcesz osiągnąć. Ty potrzebujesz półprzezroczystej kalki, a nie obrazka z nałożoną kalką – w takim przypadku moje rozwiązanie jest z tyłka, a Twoje jest jak najbardziej poprawne. Choć jeśli ta kalka ma stały rozmiar i zawsze taką samą zawartość, to równie dobrze mogłaby być osobnym plikiem PNG zapisanym na dysku lub w zasobach.

Ale swój poprzedni post zostawię – w razie gdyby ktoś kiedyś potrzebował rozjaśnić obraz to będzie miał gotowca.


Pracuję nad własną, arcade'ową, docelowo komercyjną grą z gatunku action/adventure w stylu retro (pixel art), programując silnik i powłokę gry od zupełnych podstaw, przy użyciu Free Pascala i SDL3. Więcej informacji znajdziesz na moim mikroblogu.
AS
  • Rejestracja:około 8 lat
  • Ostatnio:9 miesięcy
  • Postów:48
0

Ten PNG musi być generowalny w locie bo to jest wykres użycia CPU
title

flowCRANE
No i wszystko jasne. ;)
Azarien
  • Rejestracja:ponad 21 lat
  • Ostatnio:około 2 godziny
0

Ale skąd założenie że to musi być PNG?

AS
  • Rejestracja:około 8 lat
  • Ostatnio:9 miesięcy
  • Postów:48
0

Bo używam komponentów PNGimagelist oraz CoolTrayIcon.

zrzut.png

edytowany 2x, ostatnio: flowCRANE
flowCRANE
Mała uwaga – obrazki dodajemy do załączników, dzięki temu nigdy nie przepadną.
woolfik
  • Rejestracja:ponad 17 lat
  • Ostatnio:około 17 godzin
  • Postów:1597

Zarejestruj się i dołącz do największej społeczności programistów w Polsce.

Otrzymaj wsparcie, dziel się wiedzą i rozwijaj swoje umiejętności z najlepszymi.