Computing: Free Pascal Programming

Determination of the width and height of an image from file.

There are situations, where we have some JPEG, PNG or other image files, and we want to determine the effective width and height of the pictures from within a Free Pascal program. How can we do that? This tutorial shows

Determination of the image size using FCL-Image.

FCL-Image is a powerful Lazarus/Free Pascal library, intended for image manipulation. It allows not only to read, convert, rescale, rotate, etc images, but also to create images in different file formats. The library is installed by default with Lazarus, so no need to download or install supplementary packages. FCL-Image is described, including several examples, on the Free Pascal documentation website.

The main units of FCL-Image are fpimage and fpcanvas, the latter one being used when creating an image, thus not needed here. In order to be able to read a given file format, we also have to include the corresponding file format depending "reader" unit, in particular: fpreadbmp, fpreadgif, fpreadjpeg, fpreadpng, fpreadtiff, and fpreadpcx.

To read an image file, we'll have to

  1. create a TFPCustomImage object: a small TFPMemoryImage object is all we need;
  2. create a TFPCustomImageReader object: this object has to be specific for the file format used, thus will be one of TFPReaderBMP, TFPReaderGIF, TFPReaderJPEG, TFPReaderPNG, TFPReaderTIFF, TFPReaderPCX;
  3. loading the file, using the TFPMemoryImage.LoadFromFile method.

The sample program "imagesize" asks the user for filenames (until an empty filename is entered) and tries to determine the width and height of the image stored in these files. The format specific reader is chosen according to the file extension. Here is the code:

    program imagesize;
    uses
        SysUtils, fpimage, fpreadbmp, fpreadgif, fpreadjpeg, fpreadpng, fpreadtiff, fpreadpcx;
    var
        FileName, FileExt, Mess: string;
        Img: TFPCustomImage;
        Reader: TFPCustomImageReader;

    begin
        Writeln; Writeln('Image size determination using LCL.'); Writeln;
        Img := TFPMemoryImage.Create(10, 10);
        repeat
            Mess := '';
            Write('Filename (ENTER to terminate)? '); Readln(FileName);
            if FileName <> '' then begin
                if FileExists(FileName) then begin
                    FileExt := ExtractFileExt(FileName);
                    if FileExt = '.bmp' then
                        Reader := TFPReaderBMP.Create
                    else if FileExt = '.gif' then
                        Reader := TFPReaderGIF.Create
                    else if (FileExt = '.jpg') or (FileExt = '.jpeg') then
                        Reader := TFPReaderJPEG.Create
                    else if FileExt = '.png' then
                        Reader := TFPReaderPNG.Create
                    else if (FileExt = '.tif') or (FileExt = '.tiff') then
                        Reader := TFPReaderTIFF.Create
                    else if FileExt = '.pcx' then
                        Reader := TFPReaderPCX.Create
                    else
                        Mess := 'Unsupported file format!';
                    if Mess = '' then begin
                        Img.LoadFromFile(FileName, Reader);
                        Writeln('Image width = ', Img.Width);
                        Writeln('Image height = ', Img.Height);
                    end;
                end
                else
                    Mess := 'File not found!';
            end;
            if Mess <> '' then
                Writeln('Error: ', Mess);
            Writeln;
        until FileName = '';
    end.

The screenshot below shows the execution of the first version of my sample program. That version also included the code for portable bitmaps (.pbm) and portable pixmaps (.ppm) files. However, for both of these formats, I got a runtime error (so I removed this feature from the program). Also, I was not able to determine the image size of a big PCX file; in this case, the program hung (?).

Free Pascal programming: Image size determination using FCL-Image

Determination of the image size using the "picslib" unit.

The "picslib" unit is not part of Lazarus/Free Pascal (as far as I know); it's also possible that its development is abandoned for several years. Anyway, it works well for BMP, GIF, JPEG, PNG and TIFF files (PCX files are not supported) and determining the width and height of a picture from its file is nothing more than a call to the function GetImageSize(). Click the following link to download "picslib" and dependencies. Be sure to download the following files:

To use the functions of the unit, place all downloaded files in your project directory (i.e. together with your main source) and add uses SysUtils, PicsLib; to your program source.

The sample program "imagesize2" asks the user for filenames (until an empty filename is entered) and tries to determine the width and height of the image stored in these files. No need, to load the picture in your program, no need to specify the image file format (that is determined by the "picslib" functions according to the file extension). Here is the code:

    program imagesize2;
    uses
        SysUtils, PicsLib;
    var
        FileName, Mess: string;
        Width, Height: LongWord;

    begin
        Writeln; Writeln('Image size determination using "picslib" unit.'); Writeln;
        repeat
            Mess := '';
            Write('Filename (ENTER to terminate)? '); Readln(FileName);
            if FileName <> '' then begin
                if FileExists(FileName) then begin
                    if GetImageSize(FileName, Width, Height) then begin
                        Writeln('Image width = ', Width);
                        Writeln('Image height = ', Height);
                    end
                    else
                        Mess := 'Cannot determine image size!'
                end
                else
                    Mess := 'Cannot find file specified!';
            end;
            if Mess <> '' then
                Writeln('Error: ', Mess);
            Writeln;
        until FileName = '';
    end.

The screenshot shows the execution of the sample program.

Free Pascal programming: Image size determination using the 'picslib' unit

Extracting the image size from a PNG file.

I have written a sample program that reads the beginning of a PNG as binary file, and from the data read, first, concludes if the file effectively is a PNG, and if so, transforms the hexadecimal width and height information into decimal numbers. Obvious advantage: There are no dependencies (no supplementary units needed).

You can find a very good description of the PNG format in the Internet article PNG structure for beginner. For what is needed here, remember the following:

So, determining the size of a PNG image by reading its binary content, requires the following steps:

  1. Reading its 8 first bytes and checking if they correspond to the PNG signature.
  2. Continue reading the file, byte by byte, until we find the chunk identifier "IHDR".
  3. Being positioned at the beginning at the chunk data (4 bytes after the identifier), reading the 4-bytes width at offset +0 and the 4-bytes height at offset +4, and transforming the two hexadecimal values into decimal numbers.

That's what the sample program "pngsize" does. It asks the user for filenames (until an empty filename is entered) and, if the file has a PNG signature, determines the width and height of the image. Filenames may be specified with or without the .png extension (if the file as entered is not found, the program tries with the same name, with .png added); file extensions other than .png are accepted (all that counts for the program is the PNG signature). The only unit required is SysUtils; I added LazUTF8 in order to be able to access files with UTF8 filenames (this allows to read files with names containing non-ASCII characters on the recent Windows releases). Note, that filenames containing spaces are rejected. In fact, using such names seems not to be possible with the Free Pascal Open procedure (?). Here is the code:

    program pngsize;
    uses
        SysUtils, LazUTF8;
    type
        TChars = array of Char;
    const
        // Signature at the beginning of PNG files
        PNGSignature: TChars = (
            #$89, #$50, #$4E, #$47, #$0D, #$0A, #$1A, #$0A
        );
        // PNG file IHDR chunk type indicator
        // Image width and height may be found in IHDR chunk
        IHDR: TChars = (
            'I', 'H', 'D', 'R'
        );
    var
        I, P: Integer;
        Offset, W, H, M: Int64;
        FileName, Mess: string;
        OK: Boolean;
        Signature, ChunkType, Width, Height: TChars;
        PNGFile: file of Char;

    function EqualArrays(A1, A2: TChars): Boolean;
    var
        I: Integer;
        AreEqual: Boolean;
    begin
        AreEqual := True;
        if Length(A1) <> Length(A2) then
            AreEqual := False
        else begin
            for I := 0 to Length(A1) - 1 do begin
                if A1[I] <> A2[I] then
                    AreEqual := False;
            end;
        end;
        Result := AreEqual;
    end;

    begin
        Writeln; Writeln('PNG size determination.'); Writeln;
        repeat
            Mess := '';
            Write('FileName (ENTER to terminate)? '); Readln(FileName);
            if FileName <> '' then begin
                OK := True; P := UTF8Pos(' ', FileName);
                if P <> 0 then begin
                    // File names including spaces are not supported
                    // This seems to be a limitation of FPC (?)
                    OK := False;
                    Mess := 'Invalid filename!'
                end
                else begin
                    // Check if file exists (consider file name input with or without .png extension)
                    OK := FileExists(FileName);
                    if not OK then begin
                        FileName += '.png';
                        OK := FileExists(FileName);
                    end;
                    if OK then begin
                        Assign(PNGFile, Filename); Reset(PNGFile);
                        if FileSize(PNGFile) < 25 then
                            Mess := 'File is not a PNG file!'
                        else begin
                            // Read the 8 first bytes of the file
                            SetLength(Signature, 8);
                            for I := 0 to 7 do
                                Read(PNGFile, Signature[I]);
                            if EqualArrays(PNGSignature, Signature) then begin
                                // Proceed if the 8 first bytes of the file are a PNG signature
                                // Find the IHDR chunk type indicator
                                SetLength(ChunkType, 4);
                                Offset := 8;
                                Seek(PNGFile, Offset);
                                for I := 0 to 3 do
                                    Read(PNGFile, ChunkType[I]);
                                while (not EqualArrays(ChunkType, IHDR)) and (not EoF(PNGFile)) do begin
                                    // As long as the 4 bytes read aren't equal to the IHDR chunk type indicator,
                                    // read another 4 bytes, using an offset of +1 relative to the previous read operation
                                    Inc(Offset);
                                    Seek(PNGFile, Offset);
                                    for I := 0 to 3 do
                                        Read(PNGFile, ChunkType[I]);
                                end;
                                if EoF(PNGFile) then
                                    Mess := 'IHDR chunk not found!'
                                else begin
                                    // IHDR chunk found: Read the 4 bytes width and 4 bytes height
                                    // The width is stored immediately after the chunk indicator,
                                    // the height is stored immediately after the width
                                    SetLength(Width, 4); SetLength(Height, 4);
                                    Offset += 4;
                                    Seek(PNGFile, Offset);
                                    for I := 0 to 3 do
                                        Read(PNGFile, Width[I]);
                                    for I := 0 to 3 do
                                        Read(PNGFile, Height[I]);
                                    // Calculate the width and height from the binary values
                                    W := 0; H := 0; M := 1;
                                    for I := 3 downto 0 do begin
                                        W += M * Ord(Width[I]);
                                        H += M * Ord(Height[I]);
                                        M *= 256;
                                    end;
                                    // Display the PNG image width and height
                                    Writeln('PNG image width = ', W);
                                    Writeln('PNG image height = ', H);
                                end;
                            end
                            else begin
                                Mess := 'File is not a PNG file!';
                            end;
                        end;
                        Close(PNGFile);
                    end
                    else begin
                        Mess := 'File not found!';
                    end;
                end;
            end;
            if Mess <> '' then
                Writeln('Error: ', Mess);
            Writeln;
        until FileName = '';
    end.

Note: As you probably noticed, I decided to read the PNG as a file of characters. This is not really logical, but it simplifies the search operations for strings (like "IHDR"). For the rest, it's essentially the same as reading it as a file of bytes...

The screenshot shows the determination of the image size of two PNG files, first using the sample program "imgsize" (FCL-Image package), then "pngsize" (binary read). The values found are, of course, the same.

Free Pascal programming: PNG size determination first using FCL-Image, then reading the values from the binary files

Click the following link to download the sources of the 3 program samples. Please, note, that the files related to the "picslib" unit are not included in the download archive.


If you find this text helpful, please, support me and this website by signing my guestbook.