AlainD
AlainD

Reputation: 6587

TPicture Width and Height reported incorrectly for .ico files (Delphi 7)

Using Delphi 7. I have a simple routine successfully loading .bmp, .emf, .wmf, .ico and .jpg files (code given below). My problem is that every .ico (Icon) file always reports TImage.TPicture.Width and TImage.TPicture.Height as "32". All icons are 32-bit with a single page inside. It doesn't matter what the actual size is (I have tried 16x16, 32x32, 64x64 and 128x128).

If I manually set TImage.Width and TImage.Width to what I know the icon size is, the image displays nicely. All the other file types report the size correctly.

Why is there a problem with .ico files and how do I correct or workaround the problem.

procedure TfrmImageLoader.btnBrowseClick(Sender: TObject);
var
    openPictureDlg: TOpenPictureDialog;
    jpgImage: TJPEGImage;
    testWidth, testHeight: Integer;
begin
    // Browse for the image file
    openPictureDlg := TOpenPictureDialog.Create(Self);
    if (openPictureDlg.Execute) then
        begin
        // Check if file exists
        if (FileExists(openPictureDlg.FileName)) then
            begin
            // Load the image into out image component
            imgLoaded.Visible := False;
            if (IsJPEG(openPictureDlg.FileName)) then
                begin
                jpgImage := TJPEGImage.Create();
                jpgImage.LoadFromFile(openPictureDlg.FileName);
                imgLoaded.Picture.Assign(jpgImage);
                jpgImage.Free();
                end
            else
                begin
                imgLoaded.Picture.LoadFromFile(openPictureDlg.FileName);
                end;
                
            // Test width...here's the problem. Icons always report "32".
            testWidth := m_imgLoaded.Picture.Width;
            testHeight := m_imgLoaded.Picture.Height;
            m_imgLoaded.Visible := True;
            end
        else
            begin
            // File does not exist
            MessageDlg('File does not exist', mtWarning, [mbOK], 0);
            end;
        end;

    // Clean up
    openPictureDlg.Free();
end;

Update 1

As a test, I loaded the file as a TIcon, but the results are the same.

ico: TIcon;
// ...
ico := TIcon.Create();
ico.LoadFromFile(openPictureDlg.FileName);
testWidth := ico.Width;   // Still 32, regardless of the actual size
testHeight := ico.Height;
ico.Free();

Update 2

See the accepted answer. There are basically two ways to get the correct size (a) load the icon, assign to a TBitmap, and read the bitmap size or (b) read the icon header, bytes 7 & 8 are the width/height. The latter is ~20 times faster in my tests and the code is given below:

procedure GetTrueIconSize2(const cszIcon: String; var trueW: Integer; var trueH: Integer);
var
    fs: TFileStream;
    firstBytes: AnsiString;
begin
    // The size of image/vnd.microsoft.icon MIME files (Windows icon) is in the header
    // at bytes 7 & 8. A value of "0" means "256" (the largest icon size supported).
    fs := TFileStream.Create(cszIcon, fmOpenRead);
    try
        SetLength(firstBytes, 8);
        fs.Read(firstBytes[1], 8);
        trueW := Integer(firstBytes[7]);
        if (trueW = 0) then
            trueW := 256;

        trueH := Integer(firstBytes[8]);
        if (trueH  = 0) then
            trueH := 256;
    finally
        fs.Free();
    end;
end;

Upvotes: 2

Views: 259

Answers (1)

AmigoJack
AmigoJack

Reputation: 6099

A workaround would be to parse ICO files yourself, which is rather trivial: https://en.wikipedia.org/wiki/ICO_(file_format) - that way you easily know the dimensions for each entry. In the most simple case (only one picture) the first 6 bytes of the file must be #0#0#1#0#1#0 and byte 7 and 8 are width and height.

Upvotes: 2

Related Questions