Płynna zmiana przezroczystości obrazka

0

Witam ! Jestem tu nowy i chcę się przedstawić. Komputery to oprócz fotografii moje hobby. Mój pierwszy komputer złożyłem w 1992 a był to b. nowoczesny na tamte lata PC AT z procesorem 286 25Mhz. W latach dziewięćdziesiątych pisalem dużo w Pascalu a także trochę w Delphi. Od roku 2000 zainteresowałem się technologią WEBową i od tej pory piszę w językach skryptowych. Niedawno kupiłem projektor cyfrowy i zapragnąłem zrobić coś w rodzaju diaporamy. Nie znalazłem żadnego programu który by mi pasował więc napisałem w Javascript program który umożliwia przenikanie jednego zdjęcia w drugie. Program działa ale wymaga dość szybkiego komputera prawdopodobnie przez pośrednictwo przeglądarki a i to że jest to język interpretowany. Pomyślałem więc że taki program w Delphi jako kompilowany a nie interpretowany będzie szybszy ale okazało się ze niewiele pamiętam. Znalazłem na 4programers Funkcja Canvas.Draw ze stopniem przeźroczystości bitmapy i obsługą przeźroczystego koloru kod który umożliwia zmianę przezroczystości. Kod działa znakomicie jesli chcemy ustawić przezroczystość jeden raz, a ja chcę zmieniać przezroczystość wiele razy w cyklu zmiany zdjęcia na ekranie. Efekt jest taki że jeśli ustawimy parametr transparency np. na 99 i wywołujemy go na przykład za pomocą Timera to obrazek robi się coraz b. przezroczysty ale w pewnym momencie przezroczystośc zatrzymuje się i już nie rośnie. Dopiero ustawiając transparency na mniejsze od 70 zdjęcie znika ale to nie daje efektu płynności . Analizując kod wydaje mi sie że za to odpowiada funkcja Trunc która przy małych różnicach daje stale ten sam wynik. Drugim problemem jest to że nie można zmieniać przezroczystości od przezroczystego do nieprzezroczystego ale to ostatecznie nie problem. Próbowałem zmienić kod wprowadzając zmienną Kopia ktora zawiera kopię obrazka ktorego przezroczystość jest zmieniana jako daną wejściową. To działa ale niezupełnie. Obrazek znika ale jego tło pozostaje nieprzezroczyste i drugiego nie widać. Od kilku tygodni przeszukuję internet, douczam się i eksperymentuję ale wynik jest żaden. Jeśli ktoś mógłby pomóc byłoby super.

1

Nie wiem jaki dokładnie efekt chcesz osiągnąć, ale przezroczystość w najprostszej postaci, to nic innego jak nakładanie na siebie co najmniej dwóch (czasem więcej) obrazów (warstw), w taki sposób, że "zlewają się" one w jeden obraz finalny. W jakim stopniu każdy z nich "złoży się" na ten efekt - to już zależy od Ciebie.

Załóżmy najprostszą sytuację, w której masz tylko dwa obrazki - tło, oraz jakiś rysunek. Musisz rozbić obie te warstwy na elementy składowe, czyli piksele. Pobierasz więc kolejno każdy piksel z tła, a także odpowiadający mu (a więc posiadający te same współrzędne) piksel z rysunku i odczytujesz ich wartości składowe RGB. Powiedźmy, że masz coś takiego:

Tło [10, 14] = R: 100, G: 150, B: 200
Rysunek[10, 14] = R: 127, G: 14, B: 231

Na tej podstawie musisz stworzyć piksel końcowy, który będzie wynikiem połączenia dwóch powyższych. Oczywiście nie możesz ich tak po prostu zsumować. Musisz - jak napisałem we wstępie - określić, w jakiej proporcji złożą się one na ten piksel końcowy.

Wprowadzasz więc sobie zmienną - nazwijmy ją Blend - mieszczącą się w przedziale 0-100. Blend = 0 oznacza, że rysunek w ogóle nie jest przezroczysty, a Blend = 100 oznacza jego całkowite zniknięcie. Najłatwiej jest to sobie wyobrazić tak, że Blend decyduje o tym, jak dużo od siebie wniesie tło. Im większy Blend, tym tło bardziej zdominuje piksel końcowy (przy 100 zdominuje go całkowicie).

OK, no to dla przykładowej wartości Blend = 80:

Bierzesz pierwszą z brzegu składową koloru, czyli R. Wiesz, że wkład R tła w piksel końcowy będzie wynosił 80% (bo Blend = 80), a więc wkład rysunku ograniczy się do pozostałych 20% (nie możesz przecież przekroczyć 100%). Liczysz więc:

80% z R: 100 to 80;
20% z R: 127 to 25,4

Razem 105,4. Zaokrąglasz i zostaje 105. Całość powtarzasz dla pozostałych składowych piksela (G i B) i masz piksel końcowy. Zapisujesz go i powtarzasz cały proces dla pozostałych pikseli. Ot cała filozofia.

Jeżeli masz więcej warstw, to najlepiej rozbić to sobie na etapy. Najpierw łączysz te dwie "najgłębsze", potem dodajesz do wyniku połączenia kolejną i kolejną - aż do skutku.

O ile Twój problem nie jest bardziej złożony, możesz zostawić ten sprawiający Ci problemy komponent i na szybko skrobnąć coś swojego. Jak widzisz, nie jest to zbyt trudne.

Kodzik na szybko. Prymitywny - wiem (warto przerobić na ScanLine), ale to tylko dla przykładu ;].

procedure TForm1.Button1Click(Sender: TObject);
var
  X, Y: Integer;
  R1, R2, R3,
  G1, G2, G3,
  B1, B2, B3: Byte;

const
  Blend = 80;

begin
  for Y:= 0 to ObrazKoncowy.Height - 1 do
  for X:= 0 to ObrazKoncowy.Width - 1 do
    begin
      R1:= GetRValue(Tlo.Canvas.Pixels[X, Y]);
      R2:= GetRValue(Rysunek.Canvas.Pixels[X, Y]);

      G1:= GetGValue(Tlo.Canvas.Pixels[X, Y]);
      G2:= GetGValue(Rysunek.Canvas.Pixels[X, Y]);

      B1:= GetBValue(Tlo.Canvas.Pixels[X, Y]);
      B2:= GetBValue(Rysunek.Canvas.Pixels[X, Y]);

      R3:= Round(((R1 / 100) * Blend) + ((R2 / 100) * (100 - Blend)));
      G3:= Round(((G1 / 100) * Blend) + ((G2 / 100) * (100 - Blend)));
      B3:= Round(((B1 / 100) * Blend) + ((B2 / 100) * (100 - Blend)));

      ObrazKoncowy.Canvas.Pixels[X, Y]:= RGB(R3, G3, B3);
    end;
end; 
0

Naprawdę wielkie dzięki Crow. Muszę sobie to co napisałeś dokładnie przeanalizować i spróbuję coś napisać. Dam znać jak coś wydumam. Pozdrawiam.

1

Nie używałbym właściwości Pixels w przypadku, gdy zależy nam na efektywności kodu; Właściwość ta jest bardzo powolna, co przy obróbce większych obrazków sprawi, że kod wykonywać się będzie pół dnia i kawałek nocy;

Skorzystanie z właściwości ScanLine to jedyne rozsądne rozwiązanie.

0

Jeszcze raz wielkie dzięki Crow. Po Twoim wyjaśnieniu wszystko stało sie jasne wręcz banalne. Napisanie włąsciwego kodu zajęło mi dosłownie kilkadziesiąt minut razem z poprawkami. To jest prawdziwa pomoc a nie wymądrzanie się żeby nie używać Pixels a ScanLine bo o tym to wszędzie w internecie piszą. Gdyby nie Twoje wyjaśnienie to bym pewnie jeszcze długo się mordował. Pozdrawiam.

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