Unit Images

DescriptionusesClasses, Interfaces, Objects and RecordsFunctions and ProceduresTypesConstantsVariables

Description

This unit allows operating on 2D images. Keeping image in memory, loading and saving from/to a files in various formats, processing (e.g. resizing, converting to grayscale, and various other operations) — it's all here.

The most important class here is TImage, with 3 descendants TRGBImage, TAlphaImage, TRGBEImage. These three descendants describe three possible ways to encode color format in memory. But you are free to create more descendants of TImage in your own units if you want to encode in pixel something different. That's one of advantages of using classes – you can freely derive new classes.

We also handle various types of image files.

All image file formats are enumerated by TImageFormat type. Of course the set of such formats also may be extended with time.

Historia: Kiedys ten modul zawieral zaledwie definicje strukurki TRGBImage i kilka podstawowych operacji na niej, na potrzeby ladowania tekstur dla OpenGL'a. Nie moglem do tego uzywac klas z modulu Graphics Delphi bo chcialem tez dzialac pod FPC ktore nie ma jeszcze gotowego GUI a modul Graphics musi byc przeciez z takim GUI zintegrowany, poprzez klase TCanvas. Z czasem zobaczylem ze zasadniczy modul Graphics Borlanda i tak nie oferowal mi zbyt wiele, w zasadzie zaczynajac i konczac swoje mozliwosci na formacie bmp, ktory zreszta byl zapisywany i odczytywany poprzez funkcje WinAPI (pod VCLem) lub Qt (po CLXem) wiec w zasadzie i tak implementacja formatow plikow graficznych byla gdzie indziej. Tymczasem ja nie potrzebowalem zadnego GUI - na poczatek, na potrzeby OpenGL'a, wystarczala mi prosta procedurka ktora zaladowalaby obrazek z pliku w dowolnym formacie (no, przynajmniej BMP, PNG i JPG, a wiec cos prostego, cos skompresowanego i cos skompresowanego stratnie) do tablicy 2 wymiarowej ktorej kazdy element bylby 3-ka bajtow RGB. Funkcja ktora to robi nazywa sie teraz LoadRGBImage.

Znalazlem libpng i libjpeg ktore pozwolily mi latwo opanowac formaty png i jpg. Implementacja formatu bmp samemu okazala sie dziecinnie prosta.

W ktoryms momencie zorientowalem sie ze ten modul powinien byc uniezalezniony od OpenGLa. Duzo pozniej, uzaleznilem ten modul od VectorMath jako ze trojka RGB lub czworka RGBA czy RGBE to w koncu tez wektor i moze korzystac z rozlicznych operacji zdefiniowanych juz w VectorMath.

Dodalem formaty plikow PPM (przy okazji PGK na uniwerku), PCX (ze starego DOSowego kodu w TP), IPL (aby zaladowac obrazki z cornell boxa na RGK na uniwerku) i w chwili gdy to pisze dodaje format RGBE (aby moc zapisywac precyzyjne rysunki ktore dostaje od raytracera).

Formaty w pamieci tez sie rozszerzyly : do TRGBImage doszedl TAlphaImage (abym mogl miec kanal alpha dla obrazkow) i TImage (jako pojemnik ktory moze zawierac w srodku TRGBImage lub TAlphaImage, w tym momencie dodaje tez format RGBE (ktory prawdopodobnie nie bedzie mial odpowiednika w postaci TRGBEImage, bedzie zawsze opakowany w TImage)).

OpenGL independency: While this unit is obviously useful when you're using OpenGL (to load OpenGL textures etc.), it is not dependent on OpenGL in any way. This has many obvious advantages, you can use this unit without using OpenGL and without having any OpenGL context prepared.

O przydatnosci formatu RGBE i generalnie formatow ktore umozliwiaja reprezentowanie koloru RGB jako 3xfloat zamiast 3xbajt, a wiec z duzo wieksza precyzja: Np. zalozmy ze mamy bardzo bardzo ciemny obrazek : dajmy na to, kazdy pixel = 3 wartosci, rgb, i maksymalna skladowa kazdego koloru jest < 1/512. Taki obrazek wyswietlony na ekranie bedzie wygladal jak czarny, jasne. Ale jezeli taki obrazek w rzeczywistosci zawiera jakies niezerowe kolory to rozjasnienie tego obrazka przez przeskalowanie wszystkich skladowych powinno ujawnic zawartosc obrazka. Lecz niestety - jezeli obrazek zostal zapisany w postaci w rodzaju TRGBImage z modulu Images to skladowe < 1/512 zostaly z pewnoscia zapisane jako 0 (zera!), a wiec rozjasnianie przez mnozenie skladowych nie ma sensu - - taki obrazek jest juz do niczego, stracilismy cala informacje przy zaokraglaniu floatow z zakresu 0..1 do zakresu 0..255.

Mozna zastanawiac sie kiedy taka precyzja jest potrzebna. Odpowiedz brzmi : przy generowaniu realistycznych obrazkow, np. w VRMLRayTracer, obrazki moga czesto wyjsc bardzo ciemne a mimo to zawierajace istotne informacje w kontrastach miedzy ciemnymi kolorami. Ponadto chcielibysmy w raytracerach swobodnie reprezentowac tez liczby powyzej 1 bo w obrazkach jakie wychodza z raytracera wazne sa nie tyle ale proporcje miedzy nimi. Scinanie wartosci do zakresu 0..1 nie jest najlepszym pomyslem, potencjalnie tracimy wtedy informacje jakie zawarte byly w bardzo jasnych kolorach (trzeba bylo dopiero sciemnic obrazek zeby je zobaczyc).

Rozwiazaniem jest wiec aby przechowywac w pamieci i zapisywac obrazek nie robiac tak brutalnego zaokraglania wartosci kolorow. Swietnym rozwiazaniem, ktore nie tworzy zbyt duzych obrazkow jest format rgbe (to samo co picture Radiance'a) Grega Warda, i to wlasnie jest format ikRGBE obrazka w pamieci i ifRGBE obrazka w pliku (ifRGBE to jest zasadniczo zrzut danych ikRGBE z naglowkiem i spakowany prosta kompresja RLE). Przy okazji do Radiance'a jest dolaczonych wiele programow ktore potrafia wykorzystac ta precyzje o ktorej mowilem wyzej i wykonywac jakies przetwarzanie obrazkow, m.in. pfilt i ximage.

Some notes about png and zlib: Since 2004-01-16 situation is clear and simple:

Every time you try to load/save from/to a PNG file, SavePNG or LoadPNG or other functions may raise exception ELibPngNotAvailable if libpng is not installed (i.e. PngLibraryName not found).

This means that you don't have to install libpng+zlib on every system that must run programs that use this unit. You will only need libpng+zlib present at runtime if you want to load/save from/to PNG file at runtime.

If you write a program that will never ever load/save a PNG file, just don't worry: even if your program uses this unit, Images, (that uses KambiPng and KambiZlib units), your program will *not* require libpng or zlib to be installed.

uses

Overview

Classes, Interfaces, Objects and Records

Name Description
Class EImagePosOutOfRange Raised by TImage.MakeExtracted when coordinates on image are wrong.
Class TImage TImage is an abstract class representing image as a simple array of pixels.
Class TRGBImage Here pixel is represented as TVector3Byte (red, green, blue)
Class TAlphaImage  
Class TRGBEImage Color is encoded as 3 mantisas + 1 exponent, this is Greg Ward's format described in"Graphic Gems" gem II.5.
Class TGrayscaleImage Color is a simple Byte value.
Class EImageLoadError LoadXxx : load image from Stream.
Class EInvalidImageFormat  
Class EInvalidBMP  
Class EInvalidPNG  
Class EInvalidJPEG  
Class EInvalidPCX  
Class EInvalidPPM  
Class EInvalidIPL  
Class EInvalidRGBE  
Class EUnableToLoadImage  
record TImageFormatInfo  
Class ENoExistingImageExt  
Class EImageFormatNotSupported LoadRGBImage : zgadnij format strumienia na podstawie rozszerzenia pliku i zaladuj.
Class EUnableToSaveImage ——————————————————————————

Functions and Procedures

function EqualRGB(const Color1, Color2: TVector3Byte; Tolerance: Byte): boolean;
function InImageClasses(ImageClass: TImageClass; const ImageClasses: array of TImageClass): boolean; overload;
function InImageClasses(Image: TImage; const ImageClasses: array of TImageClass): boolean; overload;
function ImageClassesEqual(const Ar1, Ar2: array of TImageClass): boolean;
procedure ImageClassesAssign(var Variable: TDynArrayImageClasses; const NewValue: array of TImageClass);
function Vector3ToRGBE(const v: TVector3Single): TVector4Byte;
function VectorRGBETo3Single(const v: TVector4Byte): TVector3Single;
function LoadRGBE(Stream: TStream; RoundToByteRGB: boolean): TImage;
procedure SaveRGBE(const Img: TImage; Stream: TStream);
function LoadRGBEToByteRGB(Stream: TStream): TRGBImage;
procedure SaveRGBEFromByteRGB(const Img: TRGBImage; Stream: TStream);
function LoadBMP(Stream: TStream): TRGBImage;
function LoadPNG(Stream: TStream): TRGBImage;
function LoadJPEG(Stream: TStream): TRGBImage;
function LoadPCX(Stream: TStream): TRGBImage;
function LoadPPM(Stream: TStream): TRGBImage;
function LoadIPL(Stream: TStream): TRGBImage;
function LoadGIF(Stream: TStream): TRGBImage;
function LoadTGA(Stream: TStream): TRGBImage;
procedure SaveBMP(const img: TRGBImage; Stream: TStream);
procedure SavePNG(const img: TRGBImage; Stream: TStream; interlaced: boolean); overload;
procedure SavePNG(const img: TRGBImage; Stream: TStream); overload;
procedure SaveJPEG(const img: TRGBImage; Stream: TStream; quality: integer); overload;
procedure SaveJPEG(const img: TRGBImage; Stream: TStream); overload;
procedure SavePPM(const img: TRGBImage; Stream: TStream; binary: boolean); overload;
procedure SavePPM(const img: TRGBImage; Stream: TStream); overload;
function LoadAnyPNG(Stream: TStream; FormatRequired: TImageFormatRequirements; ConvertToRequired: boolean): TImage;
procedure SaveAnyPNG(const Img: TImage; Stream: TStream; Interlaced: boolean);
function LoadAnyGIF(Stream: TStream; FormatRequired: TImageFormatRequirements; ConvertToRequired: boolean): TImage;
function LoadAnyTGA(Stream: TStream; FormatRequired: TImageFormatRequirements; ConvertToRequired: boolean): TImage;
function FileExtToImageFormat(fileext: string; OnlyLoadable, OnlySaveable: boolean; out ImgFormat: TImageFormat): boolean;
function FileExtToImageFormatDef(const fileext: string; OnlyLoadable, OnlySaveable: boolean; DefFormat: TImageFormat): TImageFormat;
function IsFileExtToImageFormat(const fileext: string; OnlyLoadable, OnlySaveable: boolean): boolean;
function IsFileExtLoadableImage(const fileext: string): boolean;
function FindExistingImageExt(const fname: string; OnlyLoadable: boolean): string;
function TryFindExistingImageExt(const fname: string; OnlyLoadable: boolean): string;
function ListImageExtsLong(OnlyLoadable, OnlySaveable: boolean; const LinePrefix: string): string;
function ListImageExtsShort(OnlyLoadable, OnlySaveable: boolean): string;
function LoadRGBImage(Stream: TStream; const typeext: string): TRGBImage; overload;
function LoadRGBImage(const fname: string): TRGBImage; overload;
function LoadRGBImage(const fname: string; resizeToX: Cardinal; resizeToY: Cardinal; UnscaledImageProc: TProcedureRGBImage =nil): TRGBImage; overload;
function LoadImage(Stream: TStream; const StreamFormat: TImageFormat; const AllowedImageClasses: array of TImageClass; const ForbiddenConvs: TImageLoadConversions) :TImage; overload;
function LoadImage(Stream: TStream; const typeext: string; const AllowedImageClasses: array of TImageClass; const ForbiddenConvs: TImageLoadConversions) :TImage; overload;
function LoadImage(const filename: string; const AllowedImageClasses: array of TImageClass; const ForbiddenConvs: TImageLoadConversions) :TImage; overload;
function LoadImage(const filename: string; const AllowedImageClasses: array of TImageClass; const ForbiddenConvs: TImageLoadConversions; const ResizeToX, ResizeToY: Cardinal): TImage; overload;
procedure SaveImage(const img: TImage; const Format: TImageFormat; Stream: TStream); overload;
procedure SaveImage(const img: TImage; const typeext: string; Stream: TStream); overload;
procedure SaveImage(const Img: TImage; const fname: string); overload;
procedure ImageAlphaConstTo1st(var img: TImage; AlphaConst: byte);
function ImageClassBestForSavingToFormat(ImgFormat: TImageFormat): TImageClass; overload;
function ImageClassBestForSavingToFormat(const FileName: string): TImageClass; overload;

Types

TImageClass = class of TImage;
TDynArrayImageClasses = array of TImageClass;
TImageFormatRequirements = (...);
TImageFormat = (...);
TImageFormats = set of TImageFormat;
TRGBImageLoadFunc = function (Stream: TStream): TRGBImage;
TRGBImageSaveFunc = procedure (const Img: TRGBImage; Stream: TStream);
TImageLoadFunc = function (Stream: TStream; FormatRequired: TImageFormatRequirements; ConvertToRequired: boolean): TImage;
TImageFormatInfoExtsCount = 1..3;
TProcedureRGBImage = procedure(var Image: TRGBImage);
TImageLoadConversion = (...);
TImageLoadConversions = set of TImageLoadConversion;

Constants

ImageFormatInfos :array[TImageFormat]of TImageFormatInfo = ( ( FormatName: 'BMP, Windows Bitmap'; ExtsCount: 1; Exts: ('bmp','',''); LoadRGB: LoadBMP; SaveRGB: SaveBMP; Load: nil), ( FormatName: 'PNG, Portable Network Graphic'; ExtsCount: 1; Exts: ('png','',''); LoadRGB: LoadPNG; SaveRGB: SavePNG; Load: LoadAnyPNG), ( FormatName: 'JFIF, JPEG File Interchange Format'; ExtsCount: 2; Exts: ('jpg','jpeg',''); LoadRGB: LoadJPEG; SaveRGB: SaveJPEG; Load: nil), ( FormatName: 'PCX Image'; ExtsCount: 1; Exts: ('pcx','',''); LoadRGB: LoadPCX; SaveRGB: nil; Load: nil), ( FormatName: 'PPM, Portable Pixel Map'; ExtsCount: 1; Exts: ('ppm','',''); LoadRGB: LoadPPM; SaveRGB: SavePPM; Load: nil), ( FormatName: 'IPLab Image'; ExtsCount: 1; Exts: ('ipl','',''); LoadRGB: LoadIPL; SaveRGB: nil; Load: nil), ( FormatName: 'RGBE (RGB+Exponent) Image'; ExtsCount: 2; Exts: ('rgbe', 'pic', ''); LoadRGB: LoadRGBEToByteRGB; SaveRGB: SaveRGBEFromByteRGB; Load: nil), ( FormatName: 'GIF, Graphics Interchange Format'; ExtsCount: 1; Exts: ('gif','',''); LoadRGB: LoadGIF; SaveRGB: nil; Load: LoadAnyGIF), ( FormatName: 'TGA, TARGA File Format'; ExtsCount: 1; Exts: ('tga','',''); LoadRGB: LoadTGA; SaveRGB: nil; Load: LoadAnyTGA) );
DefaultSaveImageFormat: TImageFormat = ifBMP;
AllImageLoadConversions: TImageLoadConversions = [Low(TImageLoadConversion) .. High(TImageLoadConversion)];

Description

Functions and Procedures

function EqualRGB(const Color1, Color2: TVector3Byte; Tolerance: Byte): boolean;

Returns if two RGB colors are be considered equal, i.e. each component in Color1 is different than corresponding component in Color2 by Tolerance or less.

function InImageClasses(ImageClass: TImageClass; const ImageClasses: array of TImageClass): boolean; overload;

True if ImageClass is one of the items ImageClasses array or inherits from (i.e. is descendant of) one of them.

function InImageClasses(Image: TImage; const ImageClasses: array of TImageClass): boolean; overload;

This is a shortcut for InImageClasses(Image.ClassType, ImageClasses)

function ImageClassesEqual(const Ar1, Ar2: array of TImageClass): boolean;

True if both arrays contain exactly the same classes in the same order.

May be extended in the future to do better checks and return true also if both array contain the same classes but in different order, and one array may contain the same classes duplicated any times. So the intention is that you should treat both arrays as sets (i.e. order of elements is ignored).

The problem is that this function should be lighting fast (as the main purpose of it is to use it in constructions like setting property values, e.g. if ImageClassesArraysEqual(Value, SomeProperty) then begin SomeProperty := Value; ... do some lengthy operations to update new value of SomeProperty ... end; ), and doing smarter checks may cost us a little time.

So for now this function returns - true if for sure both arrays contain the same classes and - false if *possibly* they don't contain the same classes.

procedure ImageClassesAssign(var Variable: TDynArrayImageClasses; const NewValue: array of TImageClass);
 
function Vector3ToRGBE(const v: TVector3Single): TVector4Byte;

Encode RGB color as Greg Ward's Red + Green + Blue + Exponent format, thus allowing you to encode 12 bytes (3 * SizeOf(Single) = 3 * 4 = 12) in 4 bytes (4 * SizeOf(Byte) = 4), usually without any significant loss.

Each component of V (red, green, blue) is from range [0, +infinity), not merely from [0, 1]. I.e. V must have only nonnegative values.

function VectorRGBETo3Single(const v: TVector4Byte): TVector3Single;

Decode Red + Green + Blue + Exponent back into RGB 3 x Single.

function LoadRGBE(Stream: TStream; RoundToByteRGB: boolean): TImage;

jesli RoundToByteRGB to zwroci result.Kind = ikRGB (patrz komentarze przy LoadRGBEToByteRGB). Wpp. result.Kind = ikRGBE.

procedure SaveRGBE(const Img: TImage; Stream: TStream);

Img.Kind musi byc in ikRGB, ikRGBE (checked even in RELEASE). W tym pierwszym przypadku - patrz komentarze przy SaveRGBEFromByteRGB.

function LoadRGBEToByteRGB(Stream: TStream): TRGBImage;

rownowazne ImageRecToRGB(LoadRGBE(Stream, true)). Nazwa jest taka pokrecona ("ToByteRGB") bo naprawde rzadko powinienes tego uzywac i zawsze musisz zdawac sobie sprawe z ograniczen : zaokraglanie do bajtu w zasadzie zabija caly sens formatu RGBE. Odczytaj plik RGBE to RGB (3xByte) i potem go zapisz (chocby nawet z powrotem w RGBE) a stracisz cala precyzje zawarta w formacie RGBE.

procedure SaveRGBEFromByteRGB(const Img: TRGBImage; Stream: TStream);

rownowazne SaveRGBE(ImageRecFromRGB(Img), Stream). Podobnie jak przy ImageRGBToRGBE, konwersja RGB na RGBE jest bezstratna ale moze byc bezsensowna (po co ci precyzja skoro dane juz sa pozbawione precyzji ?)

function LoadBMP(Stream: TStream): TRGBImage;
 
function LoadPNG(Stream: TStream): TRGBImage;
 
function LoadJPEG(Stream: TStream): TRGBImage;
 
function LoadPCX(Stream: TStream): TRGBImage;

Only 256-color PCX can be handled. This will not probably be ever improved (al least by me, Kambi), since I don't use PCX images anymore. Use PNG if you want lossless compression.

function LoadPPM(Stream: TStream): TRGBImage;

Loads only the first image in .ppm file.

function LoadIPL(Stream: TStream): TRGBImage;
 
function LoadGIF(Stream: TStream): TRGBImage;
 
function LoadTGA(Stream: TStream): TRGBImage;
 
procedure SaveBMP(const img: TRGBImage; Stream: TStream);

——————————————————————————

SaveXxx. Each file format has specialized SaveXxx that allows you to give some parameters special for given format.

Each format must also have procedure with two parameters (const Img: TRGBImage; Stream: TStream), this will be used with ImageFormatsInfo[]. This means that below we must use overloading instead of default parameters, since pointers to given procedures must be compatible with TRGBImageSaveFunc.

procedure SavePNG(const img: TRGBImage; Stream: TStream; interlaced: boolean); overload;
 
procedure SavePNG(const img: TRGBImage; Stream: TStream); overload;
 
procedure SaveJPEG(const img: TRGBImage; Stream: TStream; quality: integer); overload;

interlaced = false

procedure SaveJPEG(const img: TRGBImage; Stream: TStream); overload;
 
procedure SavePPM(const img: TRGBImage; Stream: TStream; binary: boolean); overload;

quality = 90

procedure SavePPM(const img: TRGBImage; Stream: TStream); overload;
 
function LoadAnyPNG(Stream: TStream; FormatRequired: TImageFormatRequirements; ConvertToRequired: boolean): TImage;

LoadAnyPNG : zaladuj png. Jesli frAny to zwroci ikRGB lub ikAlpha, w zaleznosci od tego co jest zapisane w obrazku. Jesli frWithoutAlpha to zwroci ikRGB - jesli obrazek mial zapisane alpha to albo je usunie (jesli ConvertToRequired = true) lub rzuci wyjatek EUnableToLoadImage (jesli ConvertToRequired = false). Podobnie jesli frWithAlpha to zwroci obrazek ikAlpha - jesli obrazek nie mial zapisanego alpha to doda alpha (jesli ConvertToRequired = true) rowne wszedzie 1.0 (czyli High(Byte)) lub rzuci wyjatek EUnableToLoadImage (jesli ConvertToRequired = false).

LoadPNG mozna teraz zrealizowac jako proste ImageRecToRGB(LoadAnyPNG(Stream, frWithoutAlpha, true));

procedure SaveAnyPNG(const Img: TImage; Stream: TStream; Interlaced: boolean);

SaveAnyPNG: Img moze miec Kind = ikRGB (wtedy zadziala jak SavePNG) lub ikAlpha (wtedy zapamieta alpha obrazka w pliku)

function LoadAnyGIF(Stream: TStream; FormatRequired: TImageFormatRequirements; ConvertToRequired: boolean): TImage;
 
function LoadAnyTGA(Stream: TStream; FormatRequired: TImageFormatRequirements; ConvertToRequired: boolean): TImage;
 
function FileExtToImageFormat(fileext: string; OnlyLoadable, OnlySaveable: boolean; out ImgFormat: TImageFormat): boolean;

znajdz TImageFormat ktore ma podane dane fileext. fileext moze byc z poczatkowa kropka (a wiec tak jak zwraca je ExtractFileExt) lub bez. Zwraca false i nie zmienia ImgFormat jesli nie ma formatu o danym fileext.

function FileExtToImageFormatDef(const fileext: string; OnlyLoadable, OnlySaveable: boolean; DefFormat: TImageFormat): TImageFormat;

jak wyzej, ale jesli nie ma formatu o danym fileext to zwraca DefFormat.

function IsFileExtToImageFormat(const fileext: string; OnlyLoadable, OnlySaveable: boolean): boolean;

jak FileExtToImageFormat ale tutaj interesuje nas tylko czy MOZESZ taki TImageFormat znalezc, a nie : jaki on jest.

function IsFileExtLoadableImage(const fileext: string): boolean;

j.w. z OnlyLoadable = true, OnlySaveable = false.

function FindExistingImageExt(const fname: string; OnlyLoadable: boolean): string;

zadana jest nazwa pliku s, bez koncowego rozszerzenia (takze bez koncowej kropki przed rozszerzeniem). Probuje doklejac rozszerzenia sposrod ImageFormatInfos[].exts az znajdzie takie ze po doklejeniu plik istnieje (NormalFileExists).

Jesli nie znajdzie - wersja Try zwroci '', wersja bez Try rzuci wyjatek ENoExistingImageExt. Jesli znajdzie - zwraca nazwe pliku z doklejonym rozszerzeniem. Jesli OnlyLoadable, bedzie szukal tylko wsrod formatow loadable.

function TryFindExistingImageExt(const fname: string; OnlyLoadable: boolean): string;
 
function ListImageExtsLong(OnlyLoadable, OnlySaveable: boolean; const LinePrefix: string): string;

te funkcje moga byc czasem wygodne do konstruowania zapytan w rodzaju SaveFileAs, przyklad - patrz glViewImage i RaytraceToWindow. ListImageExtsLong wypisuje rozszerzenia kazdego formatu w osobnej linii, wypisuje tez FormatName. Linie sa rozdzielane przez nl, jak zwykle ostatnia linia NIE jest zakonczona nl. Kazda linia zaczyna sie od LinePrefix, potem od razu jakies nie-biale znaki np. 'ppm - PPM, Portable Pixel Map' ListImageExtsShort wypisuje wszystkie dozwolone rozszerzenia oddzielone ', ' (przecinek + spacja).

function ListImageExtsShort(OnlyLoadable, OnlySaveable: boolean): string;
 
function LoadRGBImage(Stream: TStream; const typeext: string): TRGBImage; overload;
 
function LoadRGBImage(const fname: string): TRGBImage; overload;
 
function LoadRGBImage(const fname: string; resizeToX: Cardinal; resizeToY: Cardinal; UnscaledImageProc: TProcedureRGBImage =nil): TRGBImage; overload;

Jesli resizeTo[] <> 0 to dany wymiar bedzie resizowany.

ImageProc, jesli <> nil, jest wywolywane dla zaladowanego image'a PRZED wykonaniem ewentualnego skalowania. Ma to zastosowanie np. gdy chcesz zaladowac stosunkowo maly obrazek z pliku, zamienic go np. na czarno-bialy i potem przeskalowac na bardzo duzy rozmiar. W takiej sytuacji duzo bardziej ekonomiczne jest wywolanie konwersji na black&white jeszcze PRZED skalowaniem, a wiec najlepiej przekaz MakeBlackAndWhite jako ImageProc. Acha, jesli chcesz to mozesz w ImageProc zmienic rozmiary obrazka. (chociaz dla typowego resizu pewnie wygodniej bedzie uzyc parametrow resizeToX, resizeToY)

function LoadImage(Stream: TStream; const StreamFormat: TImageFormat; const AllowedImageClasses: array of TImageClass; const ForbiddenConvs: TImageLoadConversions) :TImage; overload;
 
function LoadImage(Stream: TStream; const typeext: string; const AllowedImageClasses: array of TImageClass; const ForbiddenConvs: TImageLoadConversions) :TImage; overload;
 
function LoadImage(const filename: string; const AllowedImageClasses: array of TImageClass; const ForbiddenConvs: TImageLoadConversions) :TImage; overload;
 
function LoadImage(const filename: string; const AllowedImageClasses: array of TImageClass; const ForbiddenConvs: TImageLoadConversions; const ResizeToX, ResizeToY: Cardinal): TImage; overload;
 
procedure SaveImage(const img: TImage; const Format: TImageFormat; Stream: TStream); overload;

SaveImage ktore pobiera jako parametr TImage. W zaleznosci od klasy Img :

TRGBImage - wybiera odpowiednie SaveXxx na podstawie Format, TypeExt lub FileName (jezeli podales FileName to juz nie podajesz Stream, naturalnie). Jesli rozszerzenie nie odpowiada niczemu co umiemy zapisywac - sejwuje do formatu DefaultSaveImageFormat.

TRGBEImage - jezeli image format (zapisany implicite w FileName lub TypeExt) = ifRGBE to zapisze obrazek uzywajac SaveRGBE(Img, fname), a wiec precyzja zawarta w wewnetrznym formacie ikRGBE bedzie zapisana w formacie pliku ifRGBE.

Jezeli image format <> ifRGBE to skonwertuje obrazek do RGB (uzywajac TRGBEImage.ToRGBImage) i zapisze uzywajac SaveImage(TRGBImage,..). Wiec precyzja zawarta w wewnetrznym formacie ikRGBE nie bedzie zapisana w pliku, bo formaty inne niz ifRGBE nie pozwalaja na to.

TODO: zrobic jakas ladniejsza forme, uwzgledniajaca fakt ze byc moze kiedys zrobie jeszcze jakis format pliku (if) / lub obrazka w pamieci (ik) ktore uznawalbym za precyzyjne na poziomie float.

TAlphaImage: Format must support saving alpha images then (for now this means only ifPNG), zapisze wtedy obrazek z alpha. Wpp. rzuci wyjatek Exception. TODO: do it nicer, z podobnymi parametrami jak przy LoadImage.

procedure SaveImage(const img: TImage; const typeext: string; Stream: TStream); overload;
 
procedure SaveImage(const Img: TImage; const fname: string); overload;
 
procedure ImageAlphaConstTo1st(var img: TImage; AlphaConst: byte);

ImageAlphaConst : - najpierw, jezeli obrazek jest pozbawiony alpha to je dodaj. - potem ustaw wszedzie alpha = alphaConst

function ImageClassBestForSavingToFormat(ImgFormat: TImageFormat): TImageClass; overload;

Na podstawie filename zgaduje format pliku ktory mozemy zapisac do takiego filename (czyli robi FileExtToImageFormatDef( ExtractFileExt(ImageFilename), false, true, DefaultSaveImageFormat)) (wersja z ImgFormat juz ma to w ImgFormat).

Potem wybiera TImageKind ktory ma najwiekszy sens dla zadanego formatu - tzn. taki TImageKind ze przy sejwowaniu obrazka o takim Kind do pliku o takiej nazwie (a wiec i takim formacie) nie bedzie robiona zadna strata, nie bedzie zadnego odrzucania kanalu alpha ani precyzji i zakresu floatow. W tym momencie oznacza to ze dla ifRGBE zwroci ikRGBE a dla wszystkich pozostalych ikRGB. W tej chwili zadne format nie ma dac kind z alpha - bede tu musial kiedys dodac jakis arg aby pozwolic tej funkcji na zwracanie kanalu alpha np. dla png.

function ImageClassBestForSavingToFormat(const FileName: string): TImageClass; overload;
 

Types

TImageClass = class of TImage;

TImageClass and arrays of TImageClasses —————————–

TDynArrayImageClasses = array of TImageClass;

Note: Don't name it TDynImageClassesArray, as such naming convention is reserved for TDynArray_Base descendants.

TImageFormatRequirements = (...);
 
Values
  • frWithoutAlpha:
  • frWithAlpha:
  • frAny:
TImageFormat = (...);

File formats managing —————————————————–

Values
  • ifBMP:
  • ifPNG:
  • ifJPEG:
  • ifPCX:
  • ifPPM:
  • ifIPL:
  • ifRGBE:
  • ifGIF:
  • ifTGA:
TImageFormats = set of TImageFormat;
 
TRGBImageLoadFunc = function (Stream: TStream): TRGBImage;
 
TRGBImageSaveFunc = procedure (const Img: TRGBImage; Stream: TStream);
 
TImageLoadFunc = function (Stream: TStream; FormatRequired: TImageFormatRequirements; ConvertToRequired: boolean): TImage;
 
TImageFormatInfoExtsCount = 1..3;

As you can see, each file format must have at least one (default) file extension.

TProcedureRGBImage = procedure(var Image: TRGBImage);
 
TImageLoadConversion = (...);

LoadImage is the ultimate procedure to load image from file.

Two simple example use cases: Image := LoadImage('filename.png', [], []); (when you don't care what TImage descendant you get) or ImageRGB := LoadImage('filename.png', [TRGBImage], []) as TRGBImage; (when you insist to get TRGBImage, not e.g. TAlphaImage in case png image in file has some alpha channel).

AllowedImageClasses says what image classes are allowed. As a special case, AllowedImageClasses = [] is equivalent to AllowedImageClasses = [TImage] which says that all TImage descendants are allowed. Then this function will do everything it can to load any image into the best subclass of TImage losing as little image information it can.

Example: consider you're loading a PNG file. Let's suppose you're loading it with AllowedImageClasses = []. Then if PNG file will have alpha channel, LoadImage will return TAlphaImage descendant. Else LoadImage will return TRGBImage descendant. Now let's suppose you specified AllowedImageClasses = [TRGBImage]. If PNG file will not have alpha channel, LoadImage will return TRGBImage descendant, as before. But if PNG fill *will* have alpha channel then 1. if ForbiddenConvs does not contain [ilcAlphaDelete], LoadImage will simply ignore alpha channel and return you TRGBImage 2. if ForbiddenConvs does contain [ilcAlphaDelete], LoadImage will exit with exception EUnableToLoadImage. This is somewhat safer, since you can't accidentaly ignore alpha channel that was present in file.

All images have somehow specified RGB colors. Some of them may have alpha channel, some of them may have float precision (for now, this is only for RGBE images). This means that we may have to drop (ignore) these two things while loading image. So you can firbid ignoring them by adding to ForbiddenConvs ilcAlphaDelete and/or ilcFloatPrecDelete values.

There can also happen reverse situation: you e.g. insist that AllowedImageClasses = [TAlphaImage] but given PNG image does not have alpha channel. In this case LoadImage may add "dummy" alpha channel (everywhere equal to 1.0 or High(Byte)). Similar thing when you e.g. gave AllowedImageClasses = [TRGBEImage] but you're loading from PNG image. In this case you want float precision, but image file cannot offer it. So LoadImage can simply convert discreet values to appropriating floating point values. This is usually harmless, but sometimes it may be unwanted, since you're getting something in different format than was in file. So you can add to ForbiddenConvs ilcAlphaAdd and/or ilcFloatPrecAdd to prevent that.

If at any point LoadImage will find that it's unable to satisfy AllowedImageClasses without doing any forbidden convertions in ForbiddenConvs, it will raise EUnableToLoadImage.

Again some example: specify AllowedImageClasses = [TAlphaImage] and ForbiddenConvs = [ilcAlphaAdd] to be sure that LoadImage will return TAlphaImage with alpha channel loaded from file. If file wlil not have alpha channel, EUnableToLoadImage will be raised.

Pl: Jedna nie-1-znaczna dotad sytuacja : jezeli mamy obrazek RGB (tzn. bez alpha, bez precyzji float) a musimy dodac alpha _lub_ precyzje (to znaczy AllowedImageKinds = [ikAlpha, ikRGBE]) i mozemy dodac obie (tzn. nie ma ilcAlphaAdd ani ilcFloatPrecAdd w ForbiddenConvs) to dodamy alpha. (tak czy siak, kombinacja [ikAlpha, ikRGBE] jest malo sensowna, w kazdym realnym zastosowaniu jesli umiesz obsluzyc i ikAlpha i ikRGBE to chyba ikRGB tez umiesz; — jesli kiedys skorzystam z tej zaleznosci to skresle ta linie i dodam tutaj komentarz gdzie to sie moze przydac ——)

Values
  • ilcAlphaDelete:
  • ilcFloatPrecDelete:
  • ilcAlphaAdd:
  • ilcFloatPrecAdd:
  • ilcRGBFlattenToGrayscale:
TImageLoadConversions = set of TImageLoadConversion;
 

Constants

ImageFormatInfos :array[TImageFormat]of TImageFormatInfo = ( ( FormatName: 'BMP, Windows Bitmap'; ExtsCount: 1; Exts: ('bmp','',''); LoadRGB: LoadBMP; SaveRGB: SaveBMP; Load: nil), ( FormatName: 'PNG, Portable Network Graphic'; ExtsCount: 1; Exts: ('png','',''); LoadRGB: LoadPNG; SaveRGB: SavePNG; Load: LoadAnyPNG), ( FormatName: 'JFIF, JPEG File Interchange Format'; ExtsCount: 2; Exts: ('jpg','jpeg',''); LoadRGB: LoadJPEG; SaveRGB: SaveJPEG; Load: nil), ( FormatName: 'PCX Image'; ExtsCount: 1; Exts: ('pcx','',''); LoadRGB: LoadPCX; SaveRGB: nil; Load: nil), ( FormatName: 'PPM, Portable Pixel Map'; ExtsCount: 1; Exts: ('ppm','',''); LoadRGB: LoadPPM; SaveRGB: SavePPM; Load: nil), ( FormatName: 'IPLab Image'; ExtsCount: 1; Exts: ('ipl','',''); LoadRGB: LoadIPL; SaveRGB: nil; Load: nil), ( FormatName: 'RGBE (RGB+Exponent) Image'; ExtsCount: 2; Exts: ('rgbe', 'pic', ''); LoadRGB: LoadRGBEToByteRGB; SaveRGB: SaveRGBEFromByteRGB; Load: nil), ( FormatName: 'GIF, Graphics Interchange Format'; ExtsCount: 1; Exts: ('gif','',''); LoadRGB: LoadGIF; SaveRGB: nil; Load: LoadAnyGIF), ( FormatName: 'TGA, TARGA File Format'; ExtsCount: 1; Exts: ('tga','',''); LoadRGB: LoadTGA; SaveRGB: nil; Load: LoadAnyTGA) );

= nil if can't load it with alpha channel

DefaultSaveImageFormat: TImageFormat = ifBMP;
 
AllImageLoadConversions: TImageLoadConversions = [Low(TImageLoadConversion) .. High(TImageLoadConversion)];
 

Generated by PasDoc 0.10.0 on 2008-02-25 00:00:31