Tutaj następuje wyrysowanie fraktala na płaszczyźnie formularza Form1.
Można podstawić dowolny inny obiekt posiadający klasę TCanvas, np. Image1.
Mandelbrot(Image1->Canvas, Image1);

Biblioteka niezbędna do obsługi funkcji std::auto_ptr.
Sprawdzanie czy grafika wczytana do Image1 jest BITMAPĄ.
Sprawdzanie czy grafika wczytana do Image1 jest w formacie JPEG.
Jeżeli do Image1 została wczytana grafika w formacie JPEG to konieczne jest dokonanie jej konwersji na format JPEG.
Można by tutaj zrezygnować w konwersji na obiekt klasy TJPEGImage i posłużyć się takim samym sposobem jak w przypadku botmapy, czyli:
 x = Image1->Picture->Bitmap->PixelFormat;
Zmienna x otrzymałaby prawidłową wartość, jednak w wyniku tej operacji z obiektu Image1 zniknęłaby grafika.
To tylko etykieta (nazwa dowolna).
Korzysta z niej funkcja goto
w celu powrócenia do początku kodu.
Powrót do etykiety Start
Programowanie w Borland C++ Builder
Fraktal Mandelbrota
Pierwsze komputerowe fraktale stworzył Benoit Mandelbrot. Fraktal Mandelbrota bada ciąg licz zespolonych, a każda z nich ma część rzeczywistą  oraz część urojoną. Przed tą ostatnią stoi liczba oznaczana jako j, która ma to do siebie, że podniesiona do kwadratu daje -1. Wszystkim na pewno jest wiadomo iż nie istnieje pierwiastek z -1, ale tylko w zbiorze liczb rzeczywistych, na płaszczyźnie liczb zespolonych pierwiastek z liczby -1 jest właśnie liczbą j, czyli inaczej jednostką urojoną. Czym są liczby zespolone nie będę tutaj wyjaśniał, bo to nie wykład, lecz zwykła porada mająca na celu stworzenie fraktala Mandelbrota. Jak powstaje fraktal Mandelbrota? W skrócie polega to na tym, że badamy każdy punkt na płaszczyźnie zespolonej i dla każdego z nich testujemy zbieżność szeregu. Zaprezentuję teraz funkcję rysującą wspomniany fraktal na płaszczyźnie Canvas, w zależności od tego jaki obiekt podstawimy jako argumenty tej funkcji:
// Plik nagłówkowy np. Unit1.cpp
//---------------------------------------------------------------------------

TColor kpFractal(double cX, double cY)
{
 double x = 0, y = 0;
 byte n = 0;
 for(; n < 255; n++)
 {
  double xNext = x * x - y * y + cX;
  double yNext = 2 * x * y + cY;
  if(xNext * xNext + yNext * yNext > 4)
   break;
  else
  {
   x = xNext;
   y = yNext;
  }
 }
 TColor kolor;
 if(n == 255) kolor = clBlack;
 else kolor = TColor(255, 255 - n, n);

 return kolor;
}
//---------------------------------------------------------------------------
void __fastcall Mandelbrot(TCanvas *Canvas, TObject *Temp)
{
 int kMax = reinterpret_cast<TControl *>(Temp)->ClientWidth;
 int lMax = reinterpret_cast<TControl *>(Temp)->ClientHeight;

 double cXmin = 0.20, cYmin = 0.54;
 double cXmax = 0.22, cYmax = 0.55;
 for(int l = 0; l < lMax; l++)
 {
  for(int k = 0; k < kMax; k++)
  {
   double cX, cY;
   cX = cXmin + k * (cXmax - cXmin) / kMax;
   cY = cYmin + l * (cYmax - cYmin) / lMax;
   Canvas->Pixels[k][l] = kpFractal(cX, cY);
  }
 }
}
//---------------------------------------------------------------------------
void __fastcall TForm1::FormDblClick(TObject *Sender)
{
 Mandelbrot(Canvas, this);
}
//---------------------------------------------------------------------------

...powrót do menu. 

 

Sprawdzanie głębi bitowej plików graficznych.

Przedstawiony niżej kod bazuje na właściwości PixelFormat obiektu typu TBitmap i służy do sprawdzania w jakiej głębi bitowej zostały zapisane pliki graficzne w formacie BMP i JPEG. Ponieważ te dwa formaty zasadniczo się między sobą różnią niezbędne jest rozróżnienie między nimi. W przykładzie głębia bitowa będzie odczytywana dla plików graficznych załadowanych do obiektu Image1:
// Plik nagłówkowy np. Unit1.cpp
//---------------------------------------------------------------------------

#include <memory>
void __fastcall TForm1::Button3Click(TObject *Sender)
{
 int x;
 String pf;
 if(Image1->Picture->Graphic->GetNamePath() == "TBitmap")
 {
  x = Image1->Picture->Bitmap->PixelFormat;
  switch(x)
  {
   case 1: pf = "1 bit"; break;
   case 2: pf = "4 bity"; break;
   case 3: pf = "8 bitów"; break;
   case 4: pf = "15 bitów"; break;
   case 5: pf = "16 bitów"; break;
   case 6: pf = "24 bity"; break;
   case 7: pf = "32 bity"; break;
   case 8: pf = "Inne (nierozpoznane)"; break;
  }
 }
 if(Image1->Picture->Graphic->GetNamePath() == "TJPEGImage")
 {
  std::auto_ptr<TJPEGImage> jpg(new TJPEGImage());
  jpg->Assign(Image1->Picture);
  x = jpg->PixelFormat;
  switch(x)
  {
   case 0: pf = "24 bit"; break;
   case 1: pf = "8 bity"; break;
  }
 }
 Label1->Caption = "Głębia kolorów grafiki: " + pf;
}
//---------------------------------------------------------------------------

...powrót do menu. 

Konwersja bitmapy na szarą i monochromatyczną.

Można by sobie pomyśleć, że do zamiany grafiki z kolorowej na szarą lub monochromatyczną wystarczy zmienić jej głębię bitową za pomocą funkcji PixelFormat, np. bitmapa monochromatyczna ma głębię bitową równą 1 bitowi, z szarą bitmapą nie jest tak prosto, bo ma ona co prawda 8 bitową głębie kolorów, ale bitmapa kolorowa też może mieć taką głębię. Tak czy inaczej zmiana głębi bitowej na 8 czy 1 bitów nie zmieni jej na szarą lub monochromatyczną, wymaga to zastosowania bardziej złożonej operacji konwersji.
Zamianę na grafikę szarą dokonamy poprzez stworzenie specjalnej 8 bitowej palety szarych kolorów, a potem zmienimy paletę barw grafiki na taką właśnie szarą paletę. Poniższy przykład przedstawia gotową funkcję GreyScale pobierającą jako argument obiekt typu TBitmap zawierający bitmapę i zwracającą również obiekt typu TBitmap:
 

//--------------------------------
Graphics::TBitmap *GreyScale(Graphics::TBitmap *pBitmap)
{
 LOGPALETTE *pal;
 HPALETTE hpal;
 int i;
 int PaletteSize;
 pBitmap->PixelFormat = pf8bit;
 pal = NULL;
 PaletteSize = sizeof(TLogPalette) + (sizeof(TPaletteEntry) * 255);
 pal = (LOGPALETTE *) malloc(PaletteSize);
 pal->palVersion = 0x300;
 pal->palNumEntries = 256;
 for (i = 0; i <= 255; i++)
 {
  pal->palPalEntry[i].peRed = i;
  pal->palPalEntry[i].peGreen = i;
  pal->palPalEntry[i].peBlue = i;
  pal->palPalEntry[i].peFlags = PC_NOCOLLAPSE;
 }

 hpal = CreatePalette(pal);
 if(hpal != 0) pBitmap->Palette = hpal;
 free(pal);
 return pBitmap;
}
//--------------------------------

Kluczem do zmiany kolorów na szare jest tutaj struktura LOGPALETTE. Element palVersion jest to liczba reprezentując paletę kolorów systemowych i dla Windows wynosi ona 0x300, element palNumEntries precyzuje liczbę kolorów wejściowych w logicznej palecie, dla palety 8 bitowej będzie to zawsze wartość 256 (liczy się od 0 do 255, co daje 256), element palPalEntry definiuje kolor dla każdego elementu palety logicznej. Ten element składa się z trzech kolorów składowych RGB i w przypadku palety 8 bitowej każdy kolor składowy posiada 255 elementów, czyli mówiąc prościej każda ze składowych RGB może przyjąć wartość od 0 do 255. Jak wiadomo jeżeli wartości składowej RGB będą identyczne, czyli np. R = 10; G = 10; B = 10; to takie składowe dadzą w sumie kolor szary i w przykładzie wewnątrz pętli każdej składowej jest przypisywana w każdym obiegu pętli taka sama wartość, dlatego poszczególne elementy palety przyjmują kolory szare. W dalaszej części kodu tak zmodyfikowaną paletą, zastępujemy oryginalną paletę kolorów bitmapy.
Na szczególną uwagę zasługuje parametr palPalEntry[x].peFlag, ten parametr decyduje o tym jak paleta zostanie użyta, w przykładzie jeżeli w systemowej palecie nie ma żadnych nieużywanych wejść, kolor jest dopasowywany normalnie, jeżeli natomiast występuje w palecie systemowej to kolor w logicznej palecie może być dopasowany do tego koloru, co zapewnia pełną zgodność z paletą systemową, jednak niekoniecznie możemy otrzymać takie wartości jakie sobie zaplanowaliśmy. Można tutaj przekazać równie dobrze parametr
PC_EXPLICIT, który zamienia mniej znaczące wartości logiczne wejścia palety i dopasowuje je do indeksów palety sprzęty komputerowego, co zapewnia większą zgodność ze sprzętem. Możemy również przekazać tutaj wartość NULL, lub w ogóle zrezygnować z ustawianie tej flagi, ale w tym ostatnim przypadku, paleta kolorów może się w pewnych szczególnych przypadkach w ogóle nie zmienić.
Niże przedstawiam sposób wywołania tej funkcji poprzez wczytanie grafiki do Obiektu typu TBitmap, a potem przepisaniu jej do obiektu Image:
 

//--------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
 Graphics::TBitmap *Bmp = new Graphics::TBitmap;
 Bmp->LoadFromFile("E:\\Grafika\\Archer.bmp");
 Image1->Picture->Bitmap->Assign(GreyScale(Bmp));
 Bmp->Free();
}
//--------------------------------

Można też zrezygnować z obiektu pośredniczącego jakim jest TBitmap i wczytać bitmapę do obiektu Image, a następnie ją zmodyfikować:
 

//--------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
 Image1->Picture->LoadFromFile("E:\\Grafika\\Archer.bmp");
 Image1->Picture->Bitmap->Assign(GreyScale(Image1->Picture->Bitmap));
}
//--------------------------------

Teraz przykład przerobienia bitmapy na monochromatyczną, czyli składającą się z dwóch kolorów białego i czarnego. działanie przedstawionej tutaj funkcji polega na przerobieniu każdego koloru składowego RGB na czarny lub biały, jeżeli wartość składowej mieści się w przedziale od 0 do 127 to przyjmuje ona wartość 0, w przeciwnym razie wartość 255, czyli albo kolor czarny, albo biały. Ja w przykładzie ustawiłem sobie ten przedział po środku, czyli od 0 do 127 i od 127 do 255, ale można go sobie dowolnie modyfikować, jeżeli ustawimy np. ten przedział od 0 do 49 i od 50 do 255 to otrzymamy ciemniejszą bitmapę monochromatyczną
 

//--------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
 Graphics::TBitmap *bmp = new Graphics::TBitmap;
 bmp->LoadFromFile("E:\\Grafika\\Archer.bmp");
 bmp->PixelFormat = pf8bit;

 for(int x = 0; x < bmp->Height; x++)
 {
  RGBQUAD *piksel = (RGBQUAD *) bmp->ScanLine[x];
  for(int y = 0; y < bmp->Width; y++, piksel++)
  {
   int r = piksel->rgbRed;
   int g = piksel->rgbGreen;
   int b = piksel->rgbBlue;
   int z = piksel->rgbReserved;

   if(r > 128) r = 255; else r = 0;
   if(g > 128) g = 255; else g = 0;
   if(b > 128) b = 255; else b = 0;
   if(z > 128) z = 255; else z = 0;

   piksel->rgbRed   = (BYTE)r;
   piksel->rgbGreen = (BYTE)g;
   piksel->rgbBlue  = (BYTE)b;
   piksel->rgbReserved = (BYTE)z;
  }
 }

 Image1->Picture->Assign(bmp);
 Image1->Picture->Bitmap->PixelFormat = pf1bit;
 Image1->Picture->SaveToFile(ExtractFilePath(ParamStr(0)) + "grafika.bmp");
}
//--------------------------------

Jeżeli chodzi o parametr rgbReserved, to w plikach pomocy do Borland Developer Studio 2006 jest napisane, że powinien mieć wartość 0, lecz przy wartość równej 0 nie otrzymałem prawidłowej bitmapy, więc jest tak jak na przykładzie. Być może jest to zależne od systemu operacyjne lub od wersji środowiska. Jeżeli występują problemy z tworzeniem bitmapy to proponuję zrezygnować z tego parametru.

 

...powrót do menu. 

 

Wyświetlanie grafiki z wykorzystaniem biblioteki gdiplus.dll GDI+.

Podrozdziały:

Jak wszystkim wiadomo do obsługi plików graficznych w środowisku BCB służy komponent TImage bazujący głównie na klasie TCanvas, która obsługuje bitmapy, żeby skorzystać z plików w formacie JPEG trzeba włączać do projektu plik jpeg.hpp, ale o obsłudze innych formatów graficznych bez dodatkowych bibliotek nie ma co marzyć. Problem ten można częściowo rozwiązać poprzez wykorzystanie biblioteki gdiplus.dll GDI+. Co to jest GDI?

 

(ang. Graphic Device Interface - graficzny interfejs urządzenia) - wewnętrzny język graficzny systemu operacyjnego Windows wykorzystywany do prezentowania grafiki na ekranie monitora i drukowanie na drukarce. Zapewnia to wierne oddanie zawartości ekranu na drukarce oraz znaczne poprawienie szybkości druku w porównaniu z drukarkami, w których wydruki muszą być przetwarzane przez procesor drukarki. Wraz z pojawieniem się Windows XP GDI jest zastępowany GDI+.

Obsługiwane formaty plików to: BMP, GIF, JPEG, PNG, TIFF, and EMF.
Korzystanie z tej biblioteki wymaga uprzedniego przygotowania projektu, trzeba przede wszystkim włączyć do niego plik gdiplus.h i bibliotekę gdiplus.lib. Niestety nie wiem jak jest z dostępnością  tych bibliotek w darmowych środowiskach BCB, tzw. Personal, w wersjach komercyjnych począwszy od wersji 6.0 powinny być.
    Tworzymy nowy projekt, zapisujemy, a następnie w pliku nagłówkowym (np. Unit1.h) w sekcji include włączmy plik #include "gdiplus.h", potem z menu Project | Add to project... włączmy do projektu bibliotekę gdiplus.lib, powinna się znajdować w katalogu z zainstalowanym środowiskiem BCB, np. ...\Borland\CBuilder6\Lib\psdk. Następnie w pliku nagłówkowym w sekcji private lub public deklarujemy obiekt klasy Gdiplus oraz zmienną typu unsigned LONG_PTR.

W starszych wersjach środowiska BCB może zajść konieczność włączenia dodatkowych plików w sekcji include:

#define STRICT
#include <windows.h>
#include <algorithm>
using std::min;
using std::max;
#include <gdiplus.h>

//--------------------------------
#include "gdiplus.h"
private:
        Gdiplus::GdiplusStartupInput gdiplusStartupInput;
        ULONG_PTR gdiplusToken;

//--------------------------------

Następnie w pliku źródłowym w konstruktorze klasy definiujemy obiekt, trzeba jeszcze umieścić na samym początku pliku definicję typu STRICT, tworzymy również zdarzenie OnFormDestroy dla formularza i umieszczamy w nim instrukcje usuwające obiekt z pamięci wraz z zakończeniem działania programu.

 

//---------------------------------------------------------------------------
#define STRICT

#include <vcl.h>
#pragma hdrstop

#include "Unit1.h"

//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"

TForm1 *Form1;
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
        : TForm(Owner)
{
 GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
}
//---------------------------------------------------------------------------
void __fastcall TForm1::FormDestroy(TObject *Sender)
{
 Gdiplus::GdiplusShutdown(gdiplusToken);
}
//---------------------------------------------------------------------------

Po tych wszystkich zabiegach program jest już gotowy do wyświetlania zawartości plików graficznych. Możliwości GDI+ są bardzo duże i występuje tutaj duża mnogość funkcji, wszystkich nie będę prezentował w tej poradzie, gdyż ma ona na celu pokazanie jak należy korzystać  z tej biblioteki, wszystkie niezbędne informacje na temat wszystkich funkcji zostały obszernie opisane w plikach pomocy.
Na początek dwa przykłady pokazujące jak wyświetlić plik graficzny w formacie PNG bezpośrednio na formularzu, oraz na obiekcie Image1. Przy wyświetlaniu obiektu na formularzu kod odrysowujący grafikę należy umieścić w zdarzeniu OnPaint formularza, po to żeby po każdym odświeżeniu był on na nowo odrysowywany. W przypadku obiektu Image wystarczy odrysować w nim raz grafikę, gdyż odrysowywaniem po każdym odświeżeniu zajmie się wewnętrzny mechanizm tego obiektu. Tutaj istnieją dwie możliwości, można grafikę wczytać wraz z uruchomieniem programu i odrysowywać ją w dogodnym momencie, lub też wczytywać grafikę z pliku przed każdym jej odrysowaniem, inna możliwość to wczytanie grafiki do pamięci, a potem jej zmiana.
Przedstawię teraz trzy przykłady. W pierwszym przykładzie grafika będzie wczytywana bezpośrednio przed odrysowywaniem, jest to metoda wielce nieefektywna, gdyż program musi przed każdym odrysowaniem grafiki wczytać ją z pliku, co w przypadku odrysowywania na formularzu będzie stanowić duże obciążenie.

 

//---------------------------------------------------------------------------
#define STRICT

#include <vcl.h>
#pragma hdrstop

#include "Unit1.h"

//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"

TForm1 *Form1;
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
        : TForm(Owner)
{
 GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
}
//---------------------------------------------------------------------------
void __fastcall TForm1::FormDestroy(TObject *Sender)
{
 Gdiplus::GdiplusShutdown(gdiplusToken);
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender) // wczytanie grafiki do obiektu Image
{
 Gdiplus::Graphics grphx(Image1->Canvas->Handle);
 String path = ExtractFilePath(ParamStr(0)) + "plik.png";
 Gdiplus::Image image(WideString(path).c_bstr() );
 grphx.DrawImage(&image, 0, 0, image.GetWidth(), image.GetHeight());
}
//---------------------------------------------------------------------------
void __fastcall TForm1::FormPaint(TObject *Sender) // odrysowywanie grafiki na formularzu
{
 Gdiplus::Graphics grphx(this->Canvas->Handle);
 String path = ExtractFilePath(ParamStr(0)) + "plik.png";
 Gdiplus::Image image(WideString(path).c_bstr() );
 grphx.DrawImage(&image, 0, 0, image.GetWidth(), image.GetHeight());
}
//---------------------------------------------------------------------------

Krótkie wyjaśnienie zanim przejdę do kolejnych przykładów:

Gdiplus::Graphics grphx(this->Canvas->Handle)                - obiekt klasy Gdiplus::Graphics, w nawiasach następuje podłączenie obiektu pod formularz, lub kontrolkę na której ma być wyświetlana. Obowiązuje tutaj ta sama zasada, która dotyczy wyświetlania grafiki z wykorzystanie Canvas, a więc obiekt musi obsługiwać klasę TCanvas, czyli np. obiekt typu TPanel się teoretycznie nie nadaje, chociaż można to obejść.

String path = ExtractFilePath(ParamStr(0)) + "plik.png";  - ścieżka dostępu do pliku graficznego.

Gdiplus::Image image(WideString(path).c_bstr());            - obiekt typu Gdiplus::Image, w nawiasach następuje przypisanie  obiektowi pliku zawierającego grafikę, którą chcemy wyświetlić.

grphx.DrawImage(&image, 0, 0, image.GetWidth(), image.GetHeight());    - obiekt typu Gdiplus::Graphics odrysowuje grafikę za pomocą funkcji DrawImage na podłączonej kontrolce. Pierwszy argument to wskaźnik do obiektu typu Gdiplus::Image zawierajacego grafikę, drugi i trzeci argument to współrzędne X i Y położenia grafiki, czwarty i piąty określają wymiary długość i szerokość grafiki, w przykładzie wymiary zostały pobrane z obiektu Gdiplus::Image, ale można je określać jawnie.

 

W drugim przykładzie pokażę jak utworzyć globalnie obiekt typu Gdiplus::Image, tak by mógł on przechowywać grafikę, w ten sposób nie zachodzi potrzeba każdorazowego jej wczytywania z pliku, tuż przed odrysowaniem. Trzeba w pliku nagłówkowym w sekcji private lub public zadeklarować nowy obiekt typu Gdiplus::Image, a następnie zdefiniować go w konstruktorze klasy, no i oczywiście usunąć w przy zamknięciu programu:

Plik nagłówkowy np. Unit1.h

//--------------------------------
#include "gdiplus.h"
private:
        Gdiplus::GdiplusStartupInput gdiplusStartupInput;
        ULONG_PTR gdiplusToken;

        Gdiplus::Image *imagePNG;

//--------------------------------

 

Plik źródłowy np. Unit1.cpp

//---------------------------------------------------------------------------
#define STRICT

#include <vcl.h>
#pragma hdrstop

#include "Unit1.h"

//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"

TForm1 *Form1;
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
        : TForm(Owner)
{
 GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
 pathPNG = ExtractFilePath(ParamStr(0)) + "plik.png";
 imagePNG = new Gdiplus::Image(WideString(pPNG).c_bstr());
}
//---------------------------------------------------------------------------
void __fastcall TForm1::FormDestroy(TObject *Sender)
{
 delete imagePNG;
 Gdiplus::GdiplusShutdown(gdiplusToken);
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender) // wczytanie grafiki do obiektu Image
{
 Gdiplus::Graphics grphxPNG(Image1->Canvas->Handle);
 grphxPNG.DrawImage(imagePNG, 0, 0, imagePNG->GetWidth(), imagePNG->GetHeight());
}
//---------------------------------------------------------------------------
void __fastcall TForm1::FormPaint(TObject *Sender) // odrysowywanie grafiki na formularzu
{
 Gdiplus::Graphics grphxPNG(this->Canvas->Handle);
 grphxPNG.DrawImage(imagePNG, 0, 0, imagePNG->GetWidth(), imagePNG->GetHeight());
}
//---------------------------------------------------------------------------

 

To nie wymaga dalszych wyjaśnień. Trzeci przykład pokarze jak zmieniać pliki graficzne, a konkretnie jak wczytać nowy plik graficzny, przykład bazuje na poprzednim:

 

//---------------------------------------------------------------------------
#define STRICT

#include <vcl.h>
#pragma hdrstop

#include "Unit1.h"

//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"

TForm1 *Form1;
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
        : TForm(Owner)
{
 GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
 pathPNG = ExtractFilePath(ParamStr(0)) + "plik.png";
 imagePNG = new Gdiplus::Image(WideString(pPNG).c_bstr());
}
//---------------------------------------------------------------------------
void __fastcall TForm1::FormDestroy(TObject *Sender)
{
 delete imagePNG;
 Gdiplus::GdiplusShutdown(gdiplusToken);
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender) // wczytanie grafiki do obiektu Image
{
 pathPNG = "c:\\moje pliki\\nowy.png";
 imagePNG = imagePNG->FromFile(WideString(pathPNG).c_bstr(),
false);

 Gdiplus::Graphics grphxPNG(Image1->Canvas->Handle);
 grphxPNG.DrawImage(imagePNG, 0, 0, imagePNG->GetWidth(), imagePNG->GetHeight());
 Repaint(); // to w celu odrysowania nowej grafiki na formularzu, powiązane ze zdarzeniem OnPaint;
}
//---------------------------------------------------------------------------
void __fastcall TForm1::FormPaint(TObject *Sender) // odrysowywanie grafiki na formularzu
{
 Gdiplus::Graphics grphxPNG(this->Canvas->Handle);
 grphxPNG.DrawImage(imagePNG, 0, 0, imagePNG->GetWidth(), imagePNG->GetHeight());
}
//---------------------------------------------------------------------------

 

We ostatnich dwóch przykładach zawsze przy uruchomieniu programu była wczytywana grafika do obiektu typu Gdiplus::Image, nie jest to jednak konieczne, można utworzyć wszystkie potrzebne obiekty, a grafikę wczytywać dopiero gdy jej potrzebujemy. Trzeba jednak uważać, żeby nie wywołać funkcji wyświetlającej grafikę, przed wczytaniem grafiki, gdyż zaowocuje to błędem. Niżej kolejny przykład, w którym na początku program tworzy globalny obiekt typu Gdiplus::Image, ale nie wczytuje do niego żadnej grafiki. Plik jest wczytywany dopiero przed jego odrysowaniem w obiekcie Image1. Istotne jest tutaj, że deklarujemy tak jak poprzednio w pliku nagłówkowym obiekt typu Gdiplus::Image, ale do jego definicji w pliku źródłowym nie używamy operatora new, lecz definiujemy go jako obiekt pusty. Jest to dla mnie trochę niezrozumiałe, gdyż wygląda to jak definicja obiektu wirtualnego, więc wydaje się, że nie powinno się stosować operatora delete do usuwania obiektu, jednak użycie tego operatora nie wywołuje błędów.

Plik nagłówkowy np. Unit1.h

//--------------------------------
#include "gdiplus.h"
private:
        Gdiplus::GdiplusStartupInput gdiplusStartupInput;
        ULONG_PTR gdiplusToken;

        Gdiplus::Image *imagePNG;

//--------------------------------

 

Plik źródłowy np. Unit1.cpp

//---------------------------------------------------------------------------
#define STRICT

#include <vcl.h>
#pragma hdrstop

#include "Unit1.h"

//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"

TForm1 *Form1;
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
        : TForm(Owner)
{
 GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
 imagePNG = NULL;
}
//---------------------------------------------------------------------------
void __fastcall TForm1::FormDestroy(TObject *Sender)
{
 delete imagePNG;
 Gdiplus::GdiplusShutdown(gdiplusToken);
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender) // wczytanie grafiki do obiektu Image
{
 String pathPNG = "c:\\moje pliki\\nowy.png";
 imagePNG = imagePNG->FromFile(L"c:\\moje pliki\\nowy.png", false); // lub jak wcześniej: WideString(pathPNG).c_bstr()

 Gdiplus::Graphics grphxPNG(Image1->Canvas->Handle);
 grphxPNG.DrawImage(imagePNG, 0, 0, imagePNG->GetWidth(), imagePNG->GetHeight());
}
//---------------------------------------------------------------------------

 

Funkcja DrawImage pozwala na wiele więcej, można np. z jej pomocą narysować pochylony plik graficzny:
 

void __fastcall TForm1::Button1Click(TObject *Sender)
{
 imagePNG = imagePNG->FromFile(L"c:\\moje pliki\\nowy.png", false);

 Gdiplus::Point destPoints[3] = {
                                 Gdiplus::Point(0, 0),
                                 Gdiplus::Point(250, 50),
                                 Gdiplus::Point(175, 120)
                                };
 Gdiplus::Graphics grphxPNG(Image1->Canvas->Handle);
 grphxPNG.DrawImage(imagePNG, destPoints, 3);
}
//-----------------------------------------------------------------

 

Tablica Gdiplus::Point musi w takim przypadku zawsze zawierać trzy elementy tego typu, pierwsze dwie współrzędne to początek odrysowywanie grafiki czyli lewy górny róg, drugi dwie współrzędne to oś X czyli są położone w prawym górnym rogu, trzecie dwie współrzędne to oś Y, czyli lewy dolny róg. Czwarta współrzędna jest ustalana automatycznie i są to wektor pierwszego i drugiego koordynata oraz wektor pierwszego i trzeciego koordynata.
W bibliotece GDI+ znajdują się predefiniowane funkcje. które umożliwiają obrót grafiki o 90, 280 270 stopni:

 

void __fastcall TForm1::Button1Click(TObject *Sender)
{
 imagePNG = imagePNG->FromFile(L"nowy.png", false);
 Gdiplus::Graphics grphxPNG(Image1->Canvas->Handle);


 imagePNG->RotateFlip(Gdiplus::Rotate90FlipNone);
 grphxPNG.DrawImage(imagePNG, 0, 0, imagePNG->GetWidth(), imagePNG->GetHeight());
}
//-----------------------------------------------------------------

 

Typ wyliczeniowy RotateFilpType:

 

typedef enum {
              RotateNoneFlipNone = 0,
              Rotate90FlipNone   = 1,
              Rotate180FlipNone  = 2,
              Rotate270FlipNone  = 3,
              RotateNoneFlipX    = 4,
              Rotate90FlipX      = 5,
              Rotate180FlipX     = 6,
              Rotate270FlipX     = 7,
              RotateNoneFlipY    = Rotate180FlipX,
              Rotate90FlipY      = Rotate270FlipX,
              Rotate180FlipY     = RotateNoneFlipX,
              Rotate270FlipY     = Rotate90FlipX,
              RotateNoneFlipXY   = Rotate180FlipNone,
              Rotate90FlipXY     = Rotate270FlipNone,
              Rotate180FlipXY    = RotateNoneFlipNone,
              Rotate270FlipXY    = Rotate90FlipNone
            } RotateFlipType;

Można podawać tylko numer typu np: imagePNG->RotateFlip(1);

Chcąc obrócić grafikę o dowolny kąt trzeba go zdefiniować w funkcji RotateTransform. Funkcja akceptuje wartości z zakresu od 0 do 360. Pewnym problemem jest tutaj wyśrodkowanie obróconego obrazu, gdyż nie mam pojęcia gdzie znajduje się punkt obrotu, z testów wynika, że się przemieszcza w zależności od kąta obrotu:

void __fastcall TForm1::Button1Click(TObject *Sender)
{
 Gdiplus::Graphics grphxPNG(Image1->Canvas->Handle);
 Gdiplus::Image imageFile(L"c:\\plik.png", false);

 int angle = 45;
 grphxPNG.RotateTransform(angle);

 int X = imageFile.GetWidth();
 int Y = imageFile.GetHeight();

 grphxPNG.DrawImage(&imageFile, X/2, -Y/2);
}
//-----------------------------------------------------------------

Wczytywanie plików zawierających piramidę obrazków - klatki.


Kolejna rzecz to wczytywanie plików zawierających piramidę obrazków, czyli takich jak np. animowane GIF'y, można wczytać każdą klatkę oddzielnie, działa to również w przypadku plików TIF, ale ja próbowałem stworzyć taki plik w Photoshop'ie i co prawda zapisywał mi on pliki TIF zarówno z warstwami jak i ramkami, jednak nie udało mi się pobrać z nich pojedynczych klatek. Pliki TIF zawierające tylko jedną warstwę wczytywały się prawidłowo, ale tych z warstwami, nie dało się w ogóle wczytać, być może takie pliki tworzy się w jakiś szczególny sposób. W przykładzie wczytany zostanie animowany plik GIF i po każdym kliknięciu w przycisk Button1 będzie wyświetlana jego kolejna klatka, aż do osiągnięcia ostatniej, gdy licznik przekroczy liczbę klatek w pliku zostanie wyświetlony komunikat. W przykładzie plik graficzny jest wczytywany za każdym kliknięciem przycisku, ale to oczywiście nie jest wcale tutaj konieczne, można go wczytać razem z uruchamianym programem, to o czym pisałem wyżej.

void __fastcall TForm1::Button1Click(TObject *Sender)
{
 String fileGIF = "c:\\my file\\plik.gif"; // animowany plik GIF
 imagePNG = imagePNG->FromFile(WideString(fileGIF).c_bstr(), false);
 static UINT licznik = 0; // licznik kliknięć

 Start:

 Gdiplus::Graphics grphxPNG(Image1->Canvas->Handle);

 UINT count = imagePNG->GetFrameDimensionsCount();
 GUID* pDimensionIDs = (GUID*)malloc(sizeof(GUID)*count);

 imagePNG->GetFrameDimensionsList(pDimensionIDs, count);

 __int8 m_nFrameCount = imagePNG->GetFrameCount(&pDimensionIDs[0]);

 if(licznik < m_nFrameCount)
 {
  imagePNG->SelectActiveFrame(pDimensionIDs, licznik++);
  Image1->Canvas->FillRect(TRect(0, 0, imagePNG->GetWidth(), imagePNG->GetHeight()));
  grphxPNG.DrawImage(imagePNG, 0, 0, imagePNG->GetWidth(), imagePNG->GetHeight());

  Image1->Width = imagePNG->GetWidth();
  Image1->Height = imagePNG->GetHeight();
 }
 else
 {
  ShowMessage("Nie ma takiej ramki");
  licznik = 0;
  goto Start;
 }
}
//-----------------------------------------------------------------

Krótkie wyjaśnienie:

GetFrameDimensionsCount()                                                              - pobiera liczbę wymiarów klatek w obiekcie Gdiplus::Image
GetFrameDimensionList(GUID *dimensionIDs, UINT count)            - pobiera identyfikatory wymiarów klatek i zapisuje je do tablicy dimensionIDs, count określa liczbę elementów w tablicy
GetFrameCount(const GUID *dimensionID)                                      - pobiera liczbę klatek w Gdiplus::Image
SelectActiveFrame(const GUID *dimensionID, UINT frameIndex)  -określa która klatka ma ba być aktywna, czyli którą klatkę odrysuje funkcja DrawImage, gdzie dimensionID to wskaźnik do tablicy przechowującej poszczególne klatki wraz z ich wymiarami, a frameIndex to numer klatki, która ma być aktywna. Liczenie klatek zaczyna się od 0;

Jak widać w przykładzie wszystkie klatki z pliku graficznego są przechowywane w tablicy pDimensionIDs, zmienna m_nFrameCount przechowuje informacje o całkowitej liczbie klatek, natomiast funkcja SelectActiveFrame określa która klatka ma być w danym momencie aktywna, czyli widoczna. W kodzie znalazła się funkcja klasy TCanvas FillRect, jej zadaniem jest zamazywanie wcześniej wyświetlanej klatki przed nową, dodatkowo jeżeli ładujemy klatkę zawierającą przezroczystość to dzięki tej funkcji zostanie ona zachowana o ile ustawimy w Image1 właściwość Transparent na true.

Animowanie plików GIF.


Od takiego kodu tylko krok do wyświetlenia animowanego GIF'a, i tutaj niestety przyznaję ze smutkiem nie wiem jak pobrać z pliku GIF czas wyświetlania poszczególnych klatek. Znalazłem co prawda funkcję Gdiplus::FrameDimensionTime, ale użycie jej w programie kończy się błędem, dlatego w przykładzie czas pracy animacji zostanie określony ręcznie. Umieszczamy na formularzu komponent Timer1, gdyż to w nim będzie się odbywała animacja. obiekt Gdiplus::Image zadeklaruje tutaj jako globalny w sekcji public pliku nagłówkowego podobnie jak wszystkie inne obiekty, które można zdefiniować przed użyciem.

private:
        Gdiplus::GdiplusStartupInput gdiplusStartupInput;
        ULONG_PTR gdiplusToken;

        GUID* pDimensionIDs;
        int m_nFrameCount;

        Gdiplus::Image *imageGIF;

//-----------------------------------------------------------------

 

__fastcall TForm1::TForm1(TComponent* Owner)
        : TForm(Owner)
{
 DoubleBuffered = true; // ustawienie podwójnego buforowania dla formularza wyeliminuje migotanie Image1
 GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);

 String pathGIF = ExtractFilePath(ParamStr(0)) + "amor.gif";

 imageGIF = imageGIF->FromFile(WideString(pathGIF).c_bstr(), false);

 UINT count = imageGIF->GetFrameDimensionsCount();
 pDimensionIDs = (GUID*)malloc(sizeof(GUID)*count);

 imageGIF->GetFrameDimensionsList(pDimensionIDs, count);

 m_nFrameCount = imageGIF->GetFrameCount(&pDimensionIDs[0]);

 Image1->Width = imageGIF->GetWidth();
 Image1->Height = imageGIF->GetHeight();
}
//-----------------------------------------------------------------
void __fastcall TForm1::Timer1Timer(TObject *Sender)
{
 static int i = -1;
 Start:
 Gdiplus::Graphics grphxPNG(Image1->Canvas->Handle);

 if(i < m_nFrameCount)
 {
  if(i == 1) Timer1->Interval = 600; // wstrzymanie na dłużej na drugiej klatce
  else Timer1->Interval = 300;
 
  imagePNG->SelectActiveFrame(pDimensionIDs, i++);

  Image1->Canvas->FillRect(TRect(0, 0, imagePNG->GetWidth(), imagePNG->GetHeight()));
  grphxPNG.DrawImage(imagePNG, 0, 0, imagePNG->GetWidth(), imagePNG->GetHeight());

 }
 else
 {
  i = 0;
  goto Start;
 }
}
//-----------------------------------------------------------------

Tyle wystarczy, żeby animacja GIF działała, trzeba jednak pamiętać, że takie rozwiązanie jest mniej efektywne niż obsługa plików GIF z wykorzystaniem stworzonych specjalnie do tego celu bibliotek.

Wczytywanie plików z zasobów.

Teraz nadszedł czas na wczytywanie plików z zasobów. Na początek prosta operacja wczytywania plików w formacie GIF i PNG z zasobu. Format pliku nie ma tutaj większego znaczenia, istotne jest tylko. żeby w zasobach umieścić plik w formacie obsługiwanym przez GDI+.
Plik zasobu tworzy się w prosty sposób, jak w każdym innym przypadku. Wszystkie pliki graficzne niezależnie od formatu są umieszczane jako typ RCDATA. W tym celu uruchamiamy notatnik i umieszczamy w nim taki wpisy dla każdego z plików:

ID_GIF RCDATA "plik.gif"
ID_PNG RCDATA "plik.png"

Trzeba sobie oczywiście przygotować odpowiednio pliki plik.gif i plik.png i zapisać je w katalogu z programem. Plik zasobów zapisujemy również w katalogu z programem pod nazwą zasob.rc, przy czym nazwa jest dowolna (jednowyrazowa) lecz rozszerzenie jest tutaj istotne. Następnie poprzez menu Project | Add to project... włączamy do projektu plik zasoby.rc. Podczas kompilacji programu zostanie on przetworzony i przerobiony na plik zasoby.res i ten plik zostanie automatycznie włączony w zasoby programu.
Dla potrzeb programu stworzyłem funkcję, która będzie wczytywała zasób w oparciu o nazwę identyfikator i będzie zwracała wartość typu IStream, bezpośrednio do funkcji FromStream będącej składnikiem obiektu typu Gdiplus::Image.

IStream* LoadFromStream(char *resType)
{
 HRSRC hResource = FindResource(HInstance, resType, RT_RCDATA);
 if(!hResource) return NULL;

 DWORD Size = SizeofResource(HInstance, hResource);

 const void* pResourceData = LockResource(LoadResource(HInstance, hResource));

 if(!pResourceData) return NULL;

 HGLOBAL m_hBuffer = GlobalAlloc(GMEM_MOVEABLE, Size);

 if(m_hBuffer)
 {
  void* pBuffer = GlobalLock(m_hBuffer);
  if(pBuffer)
  {
   CopyMemory(pBuffer, pResourceData, Size);

   IStream* pStream = NULL;

   if(CreateStreamOnHGlobal(m_hBuffer, FALSE, &pStream) == S_OK)
   {
    return pStream;
   }
   else
   {
    pStream->Release();

    GlobalUnlock(m_hBuffer);
    GlobalFree(m_hBuffer);
    m_hBuffer = NULL;
   }
  }
 }
 return NULL;
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
 Gdiplus::Graphics grphxPNG(Image1->Canvas->Handle);
 Gdiplus::Image *imageRes;

 static bool lswitch = false;

 if(!lswitch) imageRes = imageRes->FromStream(LoadFromStream("ID_GIF"));
 else imageRes = imageRes->FromStream(LoadFromStream("ID_PNG"));

 if(lswitch) lswitch = false;
 else lswitch = true;

 Image1->Canvas->FillRect(TRect(0, 0, imageRes->GetWidth(), imageRes->GetHeight()));
 grphxPNG.DrawImage(imageRes, 0, 0, imageRes->GetWidth(), imageRes->GetHeight());

 Image1->Width = imageRes->GetWidth();
 Image1->Height = imageRes->GetHeight();
}
//-----------------------------------------------------------------

Umieściłem w kodzie przełącznik lswitch tylko w celu pokazania jak można przemiennie wczytywać pliki z zasobów.
Od tego kodu pozostaje już tylko krok do wczytania z pliku animowanego GIF'a, w zasadzie wystarczy połączyć ze sobą kod na wczytanie grafiki z zasobu z kodem na animowanie:

private:
        Gdiplus::GdiplusStartupInput gdiplusStartupInput;
        ULONG_PTR gdiplusToken;

        GUID* pDimensionIDs;
        int m_nFrameCount;

        Gdiplus::Image *imageGIF;

//-----------------------------------------------------------------

 

__fastcall TForm1::TForm1(TComponent* Owner)
        : TForm(Owner)
{
 DoubleBuffered = true; // ustawienie podwójnego buforowania dla formularza wyeliminuje migotanie Image1
 GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
 
 Timer1->Enabled = false;
}
//-----------------------------------------------------------------
IStream* LoadFromStream(char *resType)
{
 HRSRC hResource = FindResource(HInstance, resType, RT_RCDATA);
 if(!hResource) return NULL;

 DWORD Size = SizeofResource(HInstance, hResource);

 const void* pResourceData = LockResource(LoadResource(HInstance, hResource));

 if(!pResourceData) return NULL;

 HGLOBAL m_hBuffer = GlobalAlloc(GMEM_MOVEABLE, Size);

 if(m_hBuffer)
 {
  void* pBuffer = GlobalLock(m_hBuffer);
  if(pBuffer)
  {
   CopyMemory(pBuffer, pResourceData, Size);

   IStream* pStream = NULL;

   if(CreateStreamOnHGlobal(m_hBuffer, FALSE, &pStream) == S_OK)
   {
    return pStream;
   }
   else
   {
    pStream->Release();

    GlobalUnlock(m_hBuffer);
    GlobalFree(m_hBuffer);
    m_hBuffer = NULL;
   }
  }
 }
 return NULL;
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
 imageGIF = imageGIF->FromStream(LoadFromStream("ID_GIF"));

 UINT count = imageGIF->GetFrameDimensionsCount();
 pDimensionIDs = (GUID*)malloc(sizeof(GUID)*count);

 imageGIF->GetFrameDimensionsList(pDimensionIDs, count);

 m_nFrameCount = imageGIF->GetFrameCount(&pDimensionIDs[0]);

 Image1->Width  = imageGIF->GetWidth();
 Image1->Height = imageGIF->GetHeight();
}
//-----------------------------------------------------------------

void __fastcall TForm1::Timer1Timer(TObject *Sender)
{
 static int i = -1;
 Start:
 Gdiplus::Graphics grphxPNG(Image1->Canvas->Handle);

 if(i < m_nFrameCount)
 {
  if(i == 1) Timer1->Interval = 600; // wstrzymanie na dłużej na drugiej klatce
  else Timer1->Interval = 300;
 
  imagePNG->SelectActiveFrame(pDimensionIDs, i++);

  Image1->Canvas->FillRect(TRect(0, 0, imagePNG->GetWidth(), imagePNG->GetHeight()));
  grphxPNG.DrawImage(imagePNG, 0, 0, imagePNG->GetWidth(), imagePNG->GetHeight());

 }
 else
 {
  i = 0;
  goto Start;
 }
}
//-----------------------------------------------------------------

Wszystkie przedstawione przykłady nie wyczerpują możliwości.

            Konwersja plików z jednego formatu na inny.

Na zakończenie przykład konwersji pliku z formatu TIF na format PNG. Nie będę omawiał tutaj poszczególnych elementów kodu:

int GetEncoderClsid(const WCHAR* format, CLSID* pClsid)
{
 unsigned int num = 0;
 unsigned int size = 0;

 Gdiplus::GetImageEncodersSize(&num, &size);
 if(size == 0)return -1;

 Gdiplus::ImageCodecInfo* imageCodecInfo = new Gdiplus::ImageCodecInfo[size];
 Gdiplus::GetImageEncoders(num, size, imageCodecInfo);

 for(unsigned int i = 0; i < num; ++i)
 {
  if(wcscmp(imageCodecInfo[i].MimeType, format) == 0)
  {
   *pClsid = imageCodecInfo[i].Clsid;
   delete[] imageCodecInfo;
   return i;
  }
 }
 delete[] imageCodecInfo;
 return -1;
}
//-----------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
 Gdiplus::Graphics graphics(this->Handle);
 String oldFile = "c:\\plik.tif";
 String newFile = "c:\\nowy.png";
 Gdiplus::Image imageFile(WideString(oldFile).c_bstr(), false);

 CLSID pngClsid;
 GetEncoderClsid(L"image/png", &pngClsid);
 imageFile.Save(WideString(newFile).c_bstr(), &pngClsid, NULL);
}
//-----------------------------------------------------------------

Kluczem do określenia formatu na który konwertujemy grafikę jest tutaj funkcja GetEncodeClsid, która pobiera jako pierwszy argument wskaźnik określający na jaki format chcemy skonwertować grafikę, drugi argument zwraca do funkcja Save typ konwertowanego pliku i funkcja Save zapisuje nowy plik pod tym formatem. Wskaźniki mogą przyjmować następujące wartości: image/bmp; image/jpeg; image/gif; image/emf; image/png; image/tiff. Należy przy tym pamiętać, żeby funkcji Save podać właściwe rozszerzenie dla pliku.

...powrót do początku porady.     ...powrót do menu. 

Prosta konwersja kolorów WEB, RGB, TColor.
W środowisku Borland Developer Studio 2006znajduje się kilka funkcji funkcji umożliwiających prostą konwersję między różnymi formatami kolorów. Nie wiem czy występują w środowisku BCB 6.
// Plik nagłówkowy np. Unit1.cpp
//---------------------------------------------------------------------------

void __fastcall TForm1::Button1Click(TObject *Sender)
{
 Form1->Color = WebColorStrToColor("FF0000"); // kolor WEB do TColor
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Button2Click(TObject *Sender) // kolor WEB do RGB kolor
{
 TColor kolor = WebColorStrToColor("FF0000"); // kolor WEB to TColor
 TColorRef rgbVal = ColorToRGB(kolor); // TColor to RGB

 Form1->Color = (TColor)rgbVal;
 Label1->Caption = Format("Kolor RGB: r - %d; g - %d; b - %d", OPENARRAY(TVarRec, (GetRValue(rgbVal), GetGValue(rgbVal), GetBValue(rgbVal))));

}
//---------------------------------------------------------------------------
void __fastcall TForm1::Button3Click(TObject *Sender)
{
 Label1->Caption = ColorToWebColorStr(clRed); // kolor TColor do kolor WEB
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Button4Click(TObject *Sender)
{
 Label1->Caption = RGBToWebColorStr(RGB(255, 0, 0)); // kolor RGB do kolor WEB
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Button4Click(TObject *Sender)
{
 int kolorRGB = ColorToRGB(clYellow); // kolor TColor do RGB kolor
 int r = GetRValue(kolorRGB);
 int g = GetGValue(kolorRGB);
 int b = GetBValue(kolorRGB);
 TVarRec vr[] = {r, g, b};
 Label1->Caption = Format("Składowe RGB: R=%d; G=%d; B=%d", vr, 3);
 Form1->Color = (TColor)kolorRGB;
}
//---------------------------------------------------------------------------

Osoby, które korzystają ze starszych wersji środowiska muszą stworzyć sobie funkcje do konwersji kolorów.

 

Podaję przykład z wykorzystaniem komponentu typu TColorDialog. W przykładzie konwersja jest prowadzona dwukierunkowo. Najpierw program pobiera wartość typu TColor z obiektu typu TColorDialog, następnie wartość ta jest konwertowana do postaci koloru WEB przechowywanego w zmiennej typu String. Kolor WEB to zapis trzech wartości typu heksadecymalnego stanowiących szesnastkowy zapis wartości typu int na które składają się składowe RGB, czyli każda składowa RGB ma swój odpowiednik w postaci wartości heksadecymalnej. Potem program dokonuje rozbicia koloru RGB przechowywanego w zmiennej typu String na pojedyncze wartości typu heksadecymalnego, by potem dokonać konwersji pojedynczych heksów do wartości typu int, a te z kolei są konwertowane na wartość typu TColor.

// Plik nagłówkowy np. Unit1.cpp
//---------------------------------------------------------------------------

#include <tchar.h>

int WebToRGB(const TCHAR *value)
{
 struct CHexMap
 {
  TCHAR chr;
  int value;
 };
 const int HexMapL = 16;
 CHexMap HexMap[HexMapL] =
 {
  {'0', 0}, {'1', 1},
  {'2', 2}, {'3', 3},
  {'4', 4}, {'5', 5},
  {'6', 6}, {'7', 7},
  {'8', 8}, {'9', 9},
  {'A', 10}, {'B', 11},
  {'C', 12}, {'D', 13},
  {'E', 14}, {'F', 15}
 };

 TCHAR *mstr = _tcsupr(_tcsdup(value));

 TCHAR *s = mstr;
 int result = 0;
 if(*s == '0' && *(s + 1) == 'X') s += 2;
 bool firsttime = true;
 while (*s != '\0')
 {
  bool found = false;
  for(int i = 0; i < HexMapL; i++)
  {
   if(*s == HexMap[i].chr)
   {
    if(!firsttime) result <<= 4;
    result |= HexMap[i].value;
    found = true;
    break;
   }
  }
  if(!found) break;
  s++;
  firsttime = false;
 }
 free(mstr);
 return result;
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
 if(KolorDialog1->Execute())
 {
  TColor kolor = KolorDialog1->Color; // KolorDialog1 - obiekt typu TColorDialog

  /* konwersja wartości typy TColor do wartości RGB */
  byte r = GetRValue(kolor);
  byte g = GetGValue(kolor);
  byte b = GetBValue(kolor);

  /* konwersja wartosci RGB na wartości hexadecymalne odpowiednik koloru WEB */
  String webColor = (String)(IntToHex(r, 2) + IntToHex(g, 2) + IntToHex(b, 2));

  Label1->Caption = webColor;


  /* konwersja w drugą stronę z wartości hexadecymalnej (kolor WEB)
     na wartość TColor */


  String wk = Label1->Caption;

  /* najpierw następuje rozbicie wartości typu String zawierającej zapis
     hexadecymalny na pojedyncze wartości hexów stanowiące składowe RGB
     następnie funkcja WebToRGB konwertuje te wartości na wartości typu int
     po czym są one konwertowane na wartość typu TColor */


  TColor rgbColor = TColor(RGB( WebToRGB(wk.SubString(1, 2).c_str()),
                                WebToRGB(wk.SubString(3, 2).c_str()),
                                WebToRGB(wk.SubString(5, 2).c_str()) ));
  Panel1->Color = rgbColor;
 }
}
//---------------------------------------------------------------------------

...powrót do menu.