Tworzenie przezroczystego obrazka png w Delphi 7

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

PNG.Canvas.Pixels

operuje tylko na RGB

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.

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:

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):

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

I to tyle.

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. ;)

1

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

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).

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.

0

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

0

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

0

Bo używam komponentów PNGimagelist oraz CoolTrayIcon.

zrzut.png

1 użytkowników online, w tym zalogowanych: 0, gości: 1