Jak narysować przezroczystą ikonę?

0

Wyciągam z pliku ikonę za pomocą ExtractIcon, "trafia" do zmiennej HICON.
Tworzę TICON i wrzucam do niej uchwyt tej HICON.
Tworzę TBitmap i używam Draw() do narysowania ikony na tej bitmapie.
Ostatni krok to użyć na Form1.Canvas lub w TImage.Canvas, funkcji StretchDraw podając bitmapę.

Wszystko elegancko działa poza przeźroczystością.
W bitmapie ustawiałem:

bmp.Transparent:= True;
bmp.TransparentColor:= clWhite;
//+ wymiary
bmp.Canvas.Draw(0, 0, tikona);
{
...
Image1.Canvas.StretchDraw()
}

ale nic to nie dało. (w TImage transparent zaznaczone mam, próbowałem też rysować na Image1.Picture.Bitmap... itd, ale tu to nawet nie chciało narysować przeskalowanej ikony więc zostałem przy Image1.Canvas).

Proszę o jakieś porady.

0

Niestety metoda StretchDraw nie obejmuje malowania ikon; Nie wiem jakiego środowiska używasz, jednak zakładam, że to Delphi 7, w którym to w pomocy jest ładnie opisana ta metoda:

StretchDraw method (TCanvas) napisał(a)

Draws the graphic specified by the Graphic parameter in the rectangle specified by the Rect parameter.

Delphi syntax:

procedure StretchDraw(const Rect: TRect; Graphic: TGraphic);

C++ syntax:

void __fastcall StretchDraw(const TRect &Rect, TGraphic* Graphic);

Description

Call StretchDraw to draw a graphic on the canvas so that the image fits in the specified rectangle. StretchDraw calls the Draw method of the graphic. The graphic object determines how to fit into the rectangle. This may involve changing magnification and/or aspect ratio.

To render the graphic in its natural size, use the Draw method, instead.

If the graphic is a TBitmap object, the bitmap is rendered using the value of CopyMode;

Note: If the graphic is an icon, it is not stretched.

tak więc nota na końcu opisu wyjaśnia, że jeśli grafiką jest ikona - nie zostanie ona rozciągnięta.


Musisz przezroczystą ikonkę najpierw namalować na płótnie bitmapy metodą Draw, gdzie rozmiar bitmapy jest dokładnie taki sam jak ikony, a następnie namalować bitmapę na np. TImage już metodą StretchDraw;

Przykładowy kod:

procedure TMainForm.FormCreate(Sender: TObject);
var
  hAppIcon: HICON;
  icnApp: TIcon;
  bmpApp: TBitmap;
begin
  hAppIcon := ExtractIcon(hInstance, PChar(ExtractFileName(ParamStr(0))), 0);

  if hAppIcon <> 0 then
  begin
    icnApp := TIcon.Create();
    bmpApp := TBitmap.Create();

    try
      icnApp.Handle := hAppIcon;

      with imgOriginal.Canvas do
      begin
        Pen.Color := clSilver;
        Brush.Color := clSilver;
        Rectangle(ClipRect);
        Draw(ClipRect.Left, ClipRect.Top, icnApp);
      end;

      with bmpApp do
      begin
        PixelFormat := pf32bit;
        Width := icnApp.Width;
        Height := icnApp.Height;

        with Canvas do
        begin
          Pen.Color := clSilver;
          Brush.Color := clSilver;
          Rectangle(ClipRect);
          Draw(ClipRect.Left, ClipRect.Top, icnApp);
        end;
      end;

      with imgStretched.Canvas do
        StretchDraw(ClipRect, bmpApp);
    finally
      icnApp.Free();
      bmpApp.Free();
    end;
  end;
end;

Zastosowanie powyższego kodu:

StretchedIcon.png

Oryginalny rozmiar zastosowanej ikony to 48x48 pikseli (kontrolka po prawej stronie ma rozmiar 105x105 pikseli).

0

@furious programming

Dowód, że działa skalowanie (XE): http://images.tinypic.pl/i/00361/ayhz6e8ck15r.jpg
user image

Prowizoryczny kod (bez with... i pisane byle jak):

procedure TForm1.Button1Click(Sender: TObject);
var
  sciezka: String;
  hikona: HICON;
  tikona: TIcon;
  bmp: TBitmap;
begin
  sciezka:= 'C:\test.exe';

  try
    hikona:= ExtractIcon(Handle, PChar(sciezka), 0);

    tikona:= TIcon.Create;
    tikona.Handle:= hikona;

    bmp:= TBitmap.Create;
    bmp.Width:= tikona.Width;
    bmp.Height:= tikona.Height;

    bmp.Canvas.Draw(0, 0, tikona);

    Image1.Width:= 24;
    Image1.Height:= 24;
    Image1.Canvas.StretchDraw(Rect(0, 0, 24, 24), bmp);

  finally
    tikona.Free;
    bmp.Free;
  end;
end;

z kodu wywaliłem kombinacje z Transparent i inne, bo nie wywaliły tła białego :(

0

@jakjak - świetnie, że przedstawiłeś przykład dla Delphi XE; Ja niestety nie posiadam tego środowiska, więc mogłem przetestować jedynie pod Delphi 7 (nie wspominam o Lazarusie, bo zarówno w treści wątku jak i w tagach nie zawarto takich informacji);

Z tego co widać - metodę StretchDraw unowoczęsniono w Delphi XE o obsługę ikon, jednak w Delphi 7 ikony nie są obsługiwane (dokładnie tak, jak napisane jest w pomocy środowiska);

Niestety pytacz nie napisał na jakim środowisku pracuje, więc podałem przykład dla najpopularniejszego wśród początkujących.

Więc trzeba było od razu napisać jaką masz wersję środowiska i najpierw przetestować zanim zada się pytanie.

0

@furious programming
doceniam kod, który wkleiłeś i dziękuję. Mój problem polega tylko i wyłącznie na tym, że nie mam jak wywalić tło z tej ikony. To białe tło sprawia, że brzydko wszystko wygląda (u ciebie jest srebrne tło). Czy wiesz jak je usunąć (to tło)?

0

Nie ma sprawy - jeśli Tobie on nie pomoże to być może kiedyś w przyszłości ktoś programujący w Delphi 7 będzie miał podobny problem i trafi na ten wątek - będzie miał przedstawione rozwiązanie;

Jeśli chodzi o tło pod ikoną - z tego co możesz zauważyć w moim kodzie przed rysowaniem ikony ustalam kolor dla pędzla (Pen) oraz dla szczotki (Brush) i maluję tło (w przykładzie wykorzystałem kolor clSilver); Jeśli interesuje Cię inny kolor, ale jednolity to musisz najpierw namalować tło w kontrolce, a potem samą ikonę; Jeśli to nie pomoże to wykorzystaj pomocniczą bitmapę;

EDIT: a tło tej ikony (w oryginale) jest białe/innego koloru czy przezroczyste?

0

przeźroczysta jest ikona w pliku
(co do kodu, zaraz będę dalej kombinował)

0

po krótkiej zabawie stwierdzam, że raczej bez sensu jest ładować ikony do bitmapy, bo tracą wtedy przeźroczystość i mam tu ma myśli niektóre ikony, czyli takie ikony, w których twórca narysował np. globus i krawędzie Ziemi zrobił półprzeźroczyste (lub prawie całkiem przeźroczyste), wtedy program wywali białe tło, ale ciut ciemniejsze tło zostawi i wtedy będzie ikona przeźroczysta, ale na globusie spostrzeżemy pixele białe lub innego koloru psujące wygląd ikony. (można np. plik ico otworzyć w mspaint i zapisać do bmp i wczytać na TImage z Transparent:= True, wtedy właśnie ujrzymy na niektórych ikonach brzydkie pixele, a ikony bez półprzeźroczystych rzeczy będą w porządku).

Pozostaje mi zabawa z formatami obsługującymi przeźroczystość? ico, png... z tym że np. ikony (ico) nie da rady przeskalować funkcją StretchDraw (wg google) więc ciężko mi będzie i nie wiem co zrobię - poszukam jutro w google informacji i popróbuję coś zrobić, albo znajdę komponent jakiś, albo zrezygnuję ze skalowania ikon, albo...

0

btw. dziwacznym rozwiązaniem może być wypełnianie bitmapy kolorem takim samym jak kolor fromy. Zakładając, że ktoś używa gotowego stylu z listy, to wtedy można taki kolor wpisać do wypełnienia bitmapy. Można też jako kolor dać pixel formy Form1.Canvas.Pixels[1,1], bo w przypadku gotowych styli nie podamy Form1.Color, bo styl np. będzie czarny, a Color w programie domyślny, szarawy.

user image

procedure TForm1.Button1Click(Sender: TObject);
var
  sciezka: String;
  tikona: TIcon;
  bmp: TBitmap;
  szer, wys: Byte; //szerokosc, wys. ikony
  x: Byte; //zmienna do rozmiaru ikony (jedna wystarczy bo i tak nikt nie robi ikon nieproporcjonalnych)
begin
  sciezka:= 'C:\program.exe';

  try
    tikona:= TIcon.Create;
    tikona.Handle:= ExtractIcon(Handle, PChar(sciezka), 0);
    szer:= tikona.Width;
    wys:= tikona.Height;

    bmp:= TBitmap.Create;
    bmp.Width:= szer;
    bmp.Height:= wys;
    bmp.Canvas.Brush.Color:= clBlack; //kolor tła formy (by nie było pozostałości pixeli na ikonie). Można też Form1.Canvas.Pixels[1,1];
    bmp.Canvas.FillRect(Rect(0, 0, szer, wys));

    bmp.Canvas.Draw(0, 0, tikona); //rysowanie ikony na bitmapie

    Image1.Transparent:= True;
    x:= 32; //wymiary ikony (wys i szer)
    Image1.Width:= x;
    Image1.Height:= x;
    Image1.Canvas.StretchDraw(Rect(0, 0, x, x), bmp);

  finally
    tikona.Free;
    bmp.Free;
  end;
end;

Później jak znajdę chwilę, to poszukam w google coś o ikonach lub o png (przeźroczystość mają więc kombinowania z tłem nie będzie raczej)...

0

Ja już raczej nie będę przedstawiał Ci nowych rozwiązań, bo pomiędzy Delphi 7 a XE jak widać jest spora różnica w supporcie plików graficznych; Jednak dalej zastanawiam się nad tym co Ty w ogóle chcesz osiągnąć i jaki efekt by Cię zadowolił; Przedstawiłem Ci w pierwszym swoim poście kod, który rozciąga dowolny plik ikon na dowolnej kontrolce, a na dodatek wykorzystujący dowolny kolor tła, a nie standardowo w takim przypadku biały;

Zrzut z powyższego posta sugeruje, że nieźle namieszałeś z ustaleniem tła pod ikoną, skoro cały formularz został przemalowany na czarno (chyba, że takie jest założenie?); Jeżeli to nie miało tak wyglądać, to pomieszałeś z tym malowaniem; Także jeśli chodzi o samą ikonę - rozciągnięta przez metodę StretchDraw będzie zawsze przejawiać "pikselozę" i nie unikniesz tego, jeśli nie wykorzystasz lepszych algorytmów niż te zaimplementowane w klasie TCanvas; Nadmienić także należy, że ikona zostałą poprawnie rozciągnięta, bo przezroczystość nie została utracona; To co podałem w pierwszym poście także zostało poprawnie rozciągnięte, a na krawędziach ikony widać piksele "półprzezroczyste" elegancko łączące się z kolorem tła - dlatego ikona została namalowana na pomocniczej bitmapie, a dopiero potem na kanwie docelowej (w przykładzie w kontrolce klasy TImage);

Kolor pod samą ikoną także możesz pobierać dynamicznie, tak by pod różnymi schematami kolorów aplikacji tło pod ikoną zawsze było takie same, jak kontrolki-rodzica;


Nic więcej nie mogę Ci doradzić, a z racji tej, że dalej nie znam powodu Twojego grymasu do posta dołączam przykłądową aplikację do "torturowania" ikon:

IconStretching.png
Program skanowany na VirusTotal - brak zagrożeń

W tym małym programiku możesz załadować ikonę z pliku *.ico lub jeśli potrzebujesz - możesz wyciągnąć ikonę z (mam nadzieję) dowolnego pliku *.exe, który posiada jakąkolwiek ikonę inną, niż standardowa przydzielana przez system; Możesz też zmienić kolor tła formularza, na którym są kontrolki z namalowanymi ikonami; Po zmianie koloru tła kontrolki są przemalowywane, więc przezroczystość pozostanie; Możesz także rozciągać ikonę aby zobaczyć efekty działania metody StretchDraw; Na zrzucie widoczna jest ramka wokół obydwu ikon - to zwykły DrawFocusRect i służy do tego, by widzieć dokładne rozmiary kontrolek; Ramki te można wyłączyć - opcja Widok - Pokaż ramki lub kombinacja Ctrl+R;

Aplikacja jest napisana pod Delphi 7, więc jeśli nic nie stanie na przeszkodzie to będziesz mógł otworzyć projekt w nowszej wersji środowiska; Niestety nie mam czasu bawić się dłużej tym programikiem, więc zaimplementowane jest wyciąganie ikony z aplikacji jedynie w rozmiarze 32x32 piksele - po więcej informacji na ten temat odsyłam do Google; Pobaw się tym programikiem i przeanalizuj kod by dowiedzieć się w jaki sposób odbywa się rysowanie ikony na tle takim samym, jak tło kontrolki-rodzica (tu: formularz).

0

@furious programming
dziękuję za aplikację, chętnie przejrzę kod, przynajmniej czegoś się nauczę i miło, że chciało ci się to pisać. A swoją drogą, to ta czarna forma to styl w XE o nazwie Metro Black, są też inne style Metro Blue, Green i później wiele styli czerwonych i innych także jedne style mają buttony żółte, zielone, czerwone i inne i tła czarne, białe, niebieskawe, zielonawe i dowolne innego (z możliwością eksportowania takiego stylu do np. Photoshopa by ustawić koloru po swojemu dla przycisków i załadować do programu, zapisać styl na stałe na listę, fajna rzecz). A skoro już cię interesuje po co mi było to skalowanie ikon, to mam śmietnik na pulpicie, a nie lubię używać gotowych programów do grupowania ikon, instalować tego i tak dalej, dlatego chcę dla siebie zrobić wysuwany pasek z ikonami małymi (do tego skalowanie mi potrzebne) + ewentualnie rozszerzanie po nacelowaniu kursorem na ikonę (i do tego użytecznego dla mnie programiku chcę dorzucić google translate i kilka wyszukiwarek z pewnych serwisów, które nie mają zaadaptowanego dodania wyszukiwarki w firefoxie [ten mini pasek bok paska adresu]). Właściwie to wszystko.
Podsumowując, dziękuję za czas, który poświęciłeś dla mnie, cenię to bardzo.

0

Nie ma sprawy - miałem luźne dwie godzinki to tak na szybko napisałem taki mini-previewer; Jeszcze ciekawszy efekt można uzyskać ładując wszystkie ikony z danego pliku oraz wyciągając różne rozmiary z każdej z nich; Niestety na takie manewry brakło mi czasu i wiedzy, bo nigdy czegoś takiego nie robiłem i musiałbym dłuższą chilę posiedzieć na MSDNie;

Pamiętaj także, że nie wszystkie ikony jakie będziesz chciał załadować będą obsługiwały przezroczystość; Jest dużo programów (choćby większość modułów pakietu Glary Utilities), które posiadają ikonę z 24-bitową głębią kolorów, więc kanał alfa w nich nie występuje;

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