Unit CastleImages

DescriptionUsesClasses, Interfaces, Objects and RecordsFunctions and ProceduresTypesConstantsVariables

Description

Loading, saving, and processing of images (TCastleImage and friends). Storing images in the memory, loading and saving them from/to files in various formats, resizing, converting to grayscale, copying and merging, many other image operations — it's all here. We include here special image types useful with modern GPUs: image data compressed for GPU (TGPUCompressedImage) and 3D image data (every image has Depth in addition to Width and Height).

The most important class here is TCastleImage. It represents an image as a simple uncompressed array of pixels. Descendants of TCastleImage define what exactly is a "pixel". We have 8-bit color images (TRGBAlphaImage, TRGBImage, TGrayscaleAlphaImage and TGrayscaleImage). We also have an image with floating-point precision and range: TRGBFloatImage.

There is also a more abstract image class TEncodedImage, representing either uncompressed image (TCastleImage) or an image with data compressed for GPU (TGPUCompressedImage).

When reading and writing image files, we understand various image formats. (See TImageFormat in castleimages_file_formats.inc documentation for a current list of supported formats, with comments specific to particular formats.) The basic loading and saving procedures and LoadImage and SaveImage.

Example usage of this unit:

  var
    Image: TCastleImage;
  begin
    Image := LoadImage('image.png');
    { scale the image to be 2x smaller }
    Image.Resize(Image.Width div 2, Image.Height div 2);
    SaveImage(Image, 'newimage.png');
  end;

This unit is not dependent on OpenGL or any other rendering library. See CastleGLImages for OpenGL image operations (for textures and others).

Uses

Overview

Classes, Interfaces, Objects and Records

Name Description
Class EImagePosOutOfRange Raised by TCastleImage.MakeExtracted when coordinates on image are wrong.
Class EImageLerpError  
Class EImageLerpInvalidClasses  
Class EImageLerpDifferentSizes  
Class EImageAssignmentError  
Class EImageCannotConvertFpImage  
Class TEncodedImage Abstract class for an image with unspecified, possibly compressed, memory format.
Class TCastleImage An abstract class representing image as a simple array of pixels.
Class ECannotFlipCompressedImage  
Class TGPUCompressedImage Image compressed using one of the GPU texture compression algorithms.
Class ECannotDecompressTexture  
Class TRGBImage Image with pixel represented as a TVector3Byte (red, green, blue).
Class TRGBAlphaImage  
Class TRGBFloatImage Image with high-precision RGB colors encoded as 3 floats.
Class TGrayscaleImage Grayscale image.
Class TGrayscaleAlphaImage Grayscale image with an alpha channel.
Class EImageLoadError  
Class EInvalidImageFormat  
Class EUnableToLoadImage  
Class EImageFormatNotSupported  
Class EImageSaveError  
Object TGPUCompressionInfo  

Functions and Procedures

function EqualRGB(const Color1, Color2: TVector3Byte; Tolerance: Byte): boolean;
function InImageClasses(ImageClass: TEncodedImageClass; const ImageClasses: array of TEncodedImageClass): boolean; overload;
function InImageClasses(Image: TEncodedImage; const ImageClasses: array of TEncodedImageClass): boolean; overload;
function ImageClassesEqual(const Ar1, Ar2: array of TEncodedImageClass): boolean;
function Vector3ToRGBE(const v: TVector3Single): TVector4Byte;
function VectorRGBETo3Single(const v: TVector4Byte): TVector3Single;
function IsImageMimeType(const MimeType: string; const OnlyLoadable, OnlySaveable: boolean): boolean;
function ListImageExtsLong(OnlyLoadable, OnlySaveable: boolean; const LinePrefix: string): string;
function ListImageExtsShort(OnlyLoadable, OnlySaveable: boolean): string;
function ImageExtToMimeType(Ext: string): string;
function LoadImage(Stream: TStream; const MimeType: string; const AllowedImageClasses: array of TEncodedImageClass) :TCastleImage; overload;
function LoadImage(const URL: string): TCastleImage; overload;
function LoadImage(const URL: string; const AllowedImageClasses: array of TEncodedImageClass) :TCastleImage; overload;
function LoadImage(const URL: string; const AllowedImageClasses: array of TEncodedImageClass; const ResizeWidth, ResizeHeight: Cardinal; const Interpolation: TResizeInterpolation = riNearest): TCastleImage; overload;
function LoadEncodedImage(Stream: TStream; const MimeType: string; const AllowedImageClasses: array of TEncodedImageClass) :TEncodedImage; overload;
function LoadEncodedImage(const URL: string): TEncodedImage; overload;
function LoadEncodedImage(URL: string; const AllowedImageClasses: array of TEncodedImageClass) :TEncodedImage; overload;
procedure SaveImage(const img: TEncodedImage; const MimeType: string; Stream: TStream); overload;
procedure SaveImage(const Img: TEncodedImage; const URL: string); overload;
function ImageClassBestForSavingToFormat(const URL: string): TCastleImageClass;
procedure AlphaMaxTo1st(var A: TAlphaChannel; const B: TAlphaChannel);
function StringToAlpha(S: string; var WarningDone: boolean): TAutoAlphaChannel;

Types

TAutoAlphaChannel = (...);
TAlphaChannel = acNone .. acFullRange;
TResizeInterpolation = (...);
TResizeNiceInterpolation = (...);
TCastleImageList = specialize TFPGObjectList<TCastleImage>;
TEncodedImageList = specialize TFPGObjectList<TEncodedImage>;
TGPUCompression = (...);
TGPUCompressions = set of TGPUCompression;
TS3TCImage = TGPUCompressedImage deprecated;
TDecompressTextureFunction = function (Image: TGPUCompressedImage): TCastleImage;
TCastleImageClass = class of TCastleImage;
TEncodedImageClass = class of TEncodedImage;
TLoadImagePreprocessEvent = procedure (var ImageUrl: string);

Constants

DefaultAlphaTolerance = 5;
AlphaToString: array [TAutoAlphaChannel] of string = ('AUTO', 'NONE', 'SIMPLE_YES_NO', 'FULL_RANGE');
GPUCompressionInfo: array [TGPUCompression] of TGPUCompressionInfo = ( (Name: 'DXT1 (no alpha)' ; RequiresPowerOf2: true ; AlphaChannel: acNone), (Name: 'DXT1' ; RequiresPowerOf2: true ; AlphaChannel: acSimpleYesNo), (Name: 'DXT3' ; RequiresPowerOf2: true ; AlphaChannel: acFullRange), (Name: 'DXT5' ; RequiresPowerOf2: true ; AlphaChannel: acFullRange), (Name: 'PVRTC1_4bpp_RGB' ; RequiresPowerOf2: true ; AlphaChannel: acNone), (Name: 'PVRTC1_2bpp_RGB' ; RequiresPowerOf2: true ; AlphaChannel: acNone), (Name: 'PVRTC1_4bpp_RGBA' ; RequiresPowerOf2: true ; AlphaChannel: acFullRange), (Name: 'PVRTC1_2bpp_RGBA' ; RequiresPowerOf2: true ; AlphaChannel: acFullRange), (Name: 'PVRTC2_4bpp' ; RequiresPowerOf2: false; AlphaChannel: acFullRange), (Name: 'PVRTC2_2bpp' ; RequiresPowerOf2: false; AlphaChannel: acFullRange), (Name: 'ATITC_RGB' ; RequiresPowerOf2: false; AlphaChannel: acNone), (Name: 'ATITC_RGBA_ExplicitAlpha' ; RequiresPowerOf2: false; AlphaChannel: acFullRange), (Name: 'ATITC_RGBA_InterpolatedAlpha'; RequiresPowerOf2: false; AlphaChannel: acFullRange), (Name: 'ETC1' ; RequiresPowerOf2: true ; AlphaChannel: acNone) );

Variables

DecompressTexture: TDecompressTextureFunction;
LoadImage_FileFilters: TFileFilterList;
SaveImage_FileFilters: TFileFilterList;
LoadImagePreprocess: TLoadImagePreprocessEvent;

Description

Functions and Procedures

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

Check if the two RGB colors are equal, ignoring small differences. All three color components may differ by at most Tolerance. When Tolerance is 0, this is a normal (exact) comparison.

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

Check is ImageClass one of the items in the ImageClasses array, or a descendant of one of them.

function InImageClasses(Image: TEncodedImage; const ImageClasses: array of TEncodedImageClass): boolean; overload;

Check is Image class one of the items in the ImageClasses array, or a descendant of one of them. This is a shortcut for InImageClasses(Image.ClassType, ImageClasses).

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

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

function Vector3ToRGBE(const v: TVector3Single): TVector4Byte;

Encode RGB color as Red + Green + Blue + Exponent format. This allows you to encode high-precision colors in 4 bytes, see ifRGBE image format for pointers why this is useful.

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

function VectorRGBETo3Single(const v: TVector4Byte): TVector3Single;

Decode Red + Green + Blue + Exponent back into RGB (3 floats).

function IsImageMimeType(const MimeType: string; const OnlyLoadable, OnlySaveable: boolean): boolean;

Does this MIME type correspond to image.

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

List available image file formats.

This is basically for debug/info purposes, you can show this to user to let him know which formats are supported (and by which extensions they are recognized). Although almost always a better way to show this to user is just to use SaveImage_FileFilters with a save dialog like TCastleWindowCustom.FileDialog, this shows file types in the open/save dialog, so it's most natural and convenient to user.

ListImageExtsLong produces a multiline info (separated by NL, last line not terminated by NL), shows all extensions and FormatName for each file format. Each line starts with LinePrefix.

ListImageExtsShort writes all recognized extensions separated by comma (', ').

function ListImageExtsShort(OnlyLoadable, OnlySaveable: boolean): string;
 
function ImageExtToMimeType(Ext: string): string;

Guess MIME type from image extension. Empty string if cannot guess.

function LoadImage(Stream: TStream; const MimeType: string; const AllowedImageClasses: array of TEncodedImageClass) :TCastleImage; overload;

The ultimate procedure to load an image from a file or URL.

URL is downloaded using CastleDownload unit. As always, if you all you care about is loading normal files, then just pass a normal filename (absolute or relative to the current directory) as the URL parameter.

Simple examples:

  { When you don't care what TCastleImage descendant you get: }
  Image := LoadImage('image.png');

  { When you insist on getting TRGBImage, that is 8-bit color image
    without an alpha channel. }
  ImageRGB := LoadImage('image.png', [TRGBImage]) as TRGBImage;

Image file format may be given explicitly (overloaded version with Format parameter), or guessed based on URL extension (which can be given explicitly by TypeExt, or automatically calculated from full URL). For now, we cannot guess the file format based on file contents or MIME type (the latter case would be sensible for http URLs).

AllowedImageClasses says what image classes are allowed. As a special case, AllowedImageClasses = [] is equivalent to AllowedImageClasses = [TCastleImage] which says that all TCastleImage descendants are allowed. Then this function will do everything it can to load any image into the best subclass of TCastleImage, 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 you can get TGrayscaleImage, TGrayscaleAlphaImage, TRGBImage, TRGBAlphaImage, depending on whether PNG file is grayscale or not and has alpha or not. Now let's suppose you specified AllowedImageClasses = [TRGBImage]. If PNG file will not be grayscale and not have alpha channel, LoadImage will return TRGBImage descendant, as before. But if PNG fill *will* have alpha channel then LoadImage will simply ignore (strip) alpha channel and return you TRGBImage.

Similar thing for grayscale: if image file was grayscale but you requested only TRGBImage, then grayscale may be "expanded" into full three-channel RGB.

There can also happen reverse situation: you e.g. insist that AllowedImageClasses = [TRGBAlphaImage] 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 = [TRGBFloatImage] 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.

If at any point LoadImage will find that it's unable to satisfy AllowedImageClasses, it will raise EUnableToLoadImage.

Exceptions raised
EUnableToLoadImage
If Image cannot be loaded into allowed AllowedImageClasses.
EImageFormatNotSupported
If image file format cannot be loaded at all. This can happen if format is totally unknown (not recognized MIME type, derived from file extension in case of local files) or if this image format cannot be loaded at all.
See also
LoadEncodedImage
function LoadImage(const URL: string): TCastleImage; overload;
 
function LoadImage(const URL: string; const AllowedImageClasses: array of TEncodedImageClass) :TCastleImage; overload;
 
function LoadImage(const URL: string; const AllowedImageClasses: array of TEncodedImageClass; const ResizeWidth, ResizeHeight: Cardinal; const Interpolation: TResizeInterpolation = riNearest): TCastleImage; overload;
 
function LoadEncodedImage(Stream: TStream; const MimeType: string; const AllowedImageClasses: array of TEncodedImageClass) :TEncodedImage; overload;

Load image to TEncodedImage format. This allows loading image compressed with GPU, which is good for optimally loading it to GPU. However, the operations on GPU-compressed image are very limited, we generally cannot do much with GPU-compressed date except rendering it.

See also
LoadImage
function LoadEncodedImage(const URL: string): TEncodedImage; overload;
 
function LoadEncodedImage(URL: string; const AllowedImageClasses: array of TEncodedImageClass) :TEncodedImage; overload;
 
procedure SaveImage(const img: TEncodedImage; const MimeType: string; Stream: TStream); overload;

Save image to a file. Takes URL as parameter, you can give file URL or just a normal filename.

File format is determined by looking at URL (guessing MIME type using URIMimeType), or given explicitly as MimeType, or just given explicitly as Format parameter.

Image class does not affect the created image file format, on the assumption that the "memory format" of the image (what TCastleImage descendant is used) can be orthogonal to the actual "file format" used to save this file.

Tries to write the image preserving it as closely as possible in this image format. When it's not possible, according conversions may be done: floating point precision of TRGBFloatImage may be lost (if saving to any file format besides RGBE file, although saving to OpenEXR may also preserve it once implemented), alpha channel may be lost, grayscale may be expanded and such.

Although not absolutely all conversions are implemented for now. You can be sure that all image formats (that allow any saving at all) can be saved from TRGBImage. Also TRGBFloatImage can be saved to RGBE file. Also PNG format supports full collection (grayscale/rgb, alpha/no alpha are all perfectly possible in PNG file; and TRGBFloatImage will be just converted to 8-bit RGB before saving to PNG).

Exceptions raised
EImageSaveError
When it's not possible to save image, because of Img class (memory format) and/or image file format.
procedure SaveImage(const Img: TEncodedImage; const URL: string); overload;
 
function ImageClassBestForSavingToFormat(const URL: string): TCastleImageClass;

Choose TCastleImage descendant best matching for this image file format. The only purpose of this for now is to pick TRGBFloatImage for RGBE files, chooses TRGBImage for anything else.

For the overloaded version with URL, file format is determined by guessing based on file extension.

procedure AlphaMaxTo1st(var A: TAlphaChannel; const B: TAlphaChannel);

Maximum alpha channel type. Chooses "full range" if anything is "full range", otherwise choose "simple yes/no" if anything is "simple yes/no", otherwise returns "no alpha channel".

function StringToAlpha(S: string; var WarningDone: boolean): TAutoAlphaChannel;
 

Types

TAutoAlphaChannel = (...);
 
Values
  • acAuto:  
  • acNone:  
  • acSimpleYesNo:  
  • acFullRange:  
TAlphaChannel = acNone .. acFullRange;

See TCastleImage.AlphaChannel.

TResizeInterpolation = (...);

Basic resize interpolation modes, fast and available for all image types.

Values
  • riNearest:  
  • riBilinear:  
TResizeNiceInterpolation = (...);

Resize interpolation modes for MakeResized with TResizeNiceInterpolation parameters. These are much slower than our TResizeInterpolation, as they are implemented by conversion to FpImage. However, they offer some extra quality.

Values
  • rniNearest:  
  • rniBilinear:  
  • rniMitchel:  
  • rniBlackman:  
  • rniBlackmanSinc:  
  • rniBlackmanBessel:  
  • rniGaussian:  
  • rniHermite:  
  • rniLanczos:  
  • rniQuadratic:  
  • rniCubic:  
  • rniCatrom:  
  • rniHanning:  
  • rniHamming:  
TCastleImageList = specialize TFPGObjectList<TCastleImage>;
 
TEncodedImageList = specialize TFPGObjectList<TEncodedImage>;
 
TGPUCompression = (...);

Possible compression of textures for GPU.

Values
  • tcDxt1_RGB: S3TC DXT1 compression, for RGB images with no alpha or simple yes/no alpha. This compression format is often supported by desktop OpenGL implementations. See http://en.wikipedia.org/wiki/S3_Texture_Compression about S3TC. It is also supported by a small number of Android devices.

    tcDxt1_RGB and tcDxt1_RGBA are the same compression method, except in tcDxt1_RGB the alpha information is ignored while rendering, while in tcDxt1_RGBA the rendering assumes we have simple yes/no alpha.

    The difference is equivalent to OpenGL differences in treating

    • GL_COMPRESSED_RGB_S3TC_DXT1_EXT and

    • GL_COMPRESSED_RGBA_S3TC_DXT1_EXT.

  • tcDxt1_RGBA: S3TC DXT1 compression, for RGB images with no alpha or simple yes/no alpha. See above tcDxt1_RGB description for details.
  • tcDxt3: S3TC DXT3 compression, for RGBA images with full alpha channel, best for images with sharp alpha transitions. This compression format is often supported by desktop OpenGL implementations. See http://en.wikipedia.org/wiki/S3_Texture_Compression about S3TC.
  • tcDxt5: S3TC DXT3 compression, for RGBA images with full alpha channel, best for images with smooth alpha transitions. This compression format is often supported by desktop OpenGL implementations. See http://en.wikipedia.org/wiki/S3_Texture_Compression about S3TC.
  • tcPvrtc1_4bpp_RGB: PowerVR texture compression (PVRTC) format. Supported by some Android and iOS devices, using PowerVR GPU by Imagination Technologies. See http://en.wikipedia.org/wiki/PVRTC .

    To generate such textures, PowerVR provides a nice tool PVRTexTool, see http://community.imgtec.com/developers/powervr/tools/pvrtextool/ .

  • tcPvrtc1_2bpp_RGB:  
  • tcPvrtc1_4bpp_RGBA:  
  • tcPvrtc1_2bpp_RGBA:  
  • tcPvrtc2_4bpp:  
  • tcPvrtc2_2bpp:  
  • tcATITC_RGB: ATI texture compression format, without alpha. Supported by some Android devices (Adreno GPU from Qualcomm).

    There is no perfect program to generate such texture, unfortunately. The only sensible choice is to use ATI compressonator from http://developer.amd.com/tools-and-sdks/archive/legacy-cpu-gpu-tools/the-compressonator/ . Unfortunately, it's installation may fail on some Windows versions and wine (Linux). We've had most success installing it on 32-bit Windows, and them copying to wine. ATI deprecated this program.

    Adreno SDK contains library to compress to ATITC formats, but no useful program to actually convert files to this format (wrapped in ktx or dds).

  • tcATITC_RGBA_ExplicitAlpha: ATI texture compression format, with sharp alpha. Supported by some Android devices (Adreno GPU from Qualcomm).
  • tcATITC_RGBA_InterpolatedAlpha: ATI texture compression format, with smooth alpha. Supported by some Android devices (Adreno GPU from Qualcomm).
  • tcETC1: ETC texture compression, without alpha. See http://en.wikipedia.org/wiki/Ericsson_Texture_Compression . Available on almost all Android OpenGLES 2.0 devices, unfortunately it doesn't support alpha channel.

    It can be generated using various tools — dedicated etcpack, also PVRTexTool and ATI compressonator.

TGPUCompressions = set of TGPUCompression;
 
TS3TCImage = TGPUCompressedImage deprecated;

Warning: this symbol is deprecated.

Deprecated alias for TGPUCompressedImage

TDecompressTextureFunction = function (Image: TGPUCompressedImage): TCastleImage;
 
TCastleImageClass = class of TCastleImage;
 
TEncodedImageClass = class of TEncodedImage;
 
TLoadImagePreprocessEvent = procedure (var ImageUrl: string);
 

Constants

DefaultAlphaTolerance = 5;

Default parameters for TEncodedImage.AlphaChannel, decide how to detect textures alpha channel.

AlphaToString: array [TAutoAlphaChannel] of string = ('AUTO', 'NONE', 'SIMPLE_YES_NO', 'FULL_RANGE');
 
GPUCompressionInfo: array [TGPUCompression] of TGPUCompressionInfo = ( (Name: 'DXT1 (no alpha)' ; RequiresPowerOf2: true ; AlphaChannel: acNone), (Name: 'DXT1' ; RequiresPowerOf2: true ; AlphaChannel: acSimpleYesNo), (Name: 'DXT3' ; RequiresPowerOf2: true ; AlphaChannel: acFullRange), (Name: 'DXT5' ; RequiresPowerOf2: true ; AlphaChannel: acFullRange), (Name: 'PVRTC1_4bpp_RGB' ; RequiresPowerOf2: true ; AlphaChannel: acNone), (Name: 'PVRTC1_2bpp_RGB' ; RequiresPowerOf2: true ; AlphaChannel: acNone), (Name: 'PVRTC1_4bpp_RGBA' ; RequiresPowerOf2: true ; AlphaChannel: acFullRange), (Name: 'PVRTC1_2bpp_RGBA' ; RequiresPowerOf2: true ; AlphaChannel: acFullRange), (Name: 'PVRTC2_4bpp' ; RequiresPowerOf2: false; AlphaChannel: acFullRange), (Name: 'PVRTC2_2bpp' ; RequiresPowerOf2: false; AlphaChannel: acFullRange), (Name: 'ATITC_RGB' ; RequiresPowerOf2: false; AlphaChannel: acNone), (Name: 'ATITC_RGBA_ExplicitAlpha' ; RequiresPowerOf2: false; AlphaChannel: acFullRange), (Name: 'ATITC_RGBA_InterpolatedAlpha'; RequiresPowerOf2: false; AlphaChannel: acFullRange), (Name: 'ETC1' ; RequiresPowerOf2: true ; AlphaChannel: acNone) );
 

Variables

DecompressTexture: TDecompressTextureFunction;

Assign here texture decompression function that is available. This way the "decompressor" is pluggable, which means that you can even use OpenGL to decompress textures, if you're going to load images while some OpenGL context is active.

LoadImage_FileFilters: TFileFilterList;

File filters if you want to choose a file that can be loaded/saved by appropriate functions from Images unit.

These objects should be treated as read-only outside this unit. Initialization / finalization of this unit automatically take care of them.

SaveImage_FileFilters: TFileFilterList;
 
LoadImagePreprocess: TLoadImagePreprocessEvent;

If assigned, all URLs loaded by LoadImage and LoadEncodedImage are processed by this event. This allows to globally modify / observe your images paths, e.g. to use GPU compressed alternative versions.


Generated by PasDoc 0.14.0.