Set file resolution DPI when saving a TImage


I am trying to control the size (physical dimension like inches og centimeters) of the picture that I save, and I wish to set the resolution (pixels per meter) when saveing the file. I use C++ Builder and have my picture in a the TImage component. I use TImage::TPicture::SaveToFile to save the picture, but I can't find any options for setting the file resolition. Do anyone have any clue to how to achieve this?

Eimund Smestad
Who is Participating?
George TokasConnect With a Mentor Commented:
Hi Eimund,
Well in this case check this out:
It is for Delphi but can translated easily.
In a few words:
Load the bitmap to a TMemoryStream using Bitmap->SaveToStream() method.
Declare a buffer for 2 bytes.
char buffer[2];
Set memory position to the index you want. usually both intexes 26h and 2ah have the same value using
MemoryStream->Position = 0x26h;
Make the read or write depending on the function (set, get) using:
MemoryStream->Read(buffer,2);//read maybe &buffer is needed depending on your declaration for the buffer...
This way you get/set DPI...
Unfortunately even though we have access to Picture->Bitmap we don't have option to read/write to the header of the file...
Having in mind and the Assign functions I mentioned before you have option to modify DPI and in other graphic formats...
The reason we don't have access to the header is because windows when they have to use a graphic object are using DIBs (Device Indepentent Bitmap) and so DPI is not used. Had hell till understand that but thankfuly found out quickly because of DirectX...

George Tokas.
George TokasCommented:
Hello there,
The following works with all versions after BCB5.
TImage supports - now - multiple formats and this way "converts" all supported formats to bitmaps.

        int Width,Height,SWidth,Sheight;
//creating 2 TBitmaps
        Graphics::TBitmap *Bitmap = new Graphics::TBitmap;
        Graphics::TBitmap *SBitmap = new Graphics::TBitmap;
//Assigning the Graphic (can be bmp,jpeg or whatever) and converts if needed to a bitmap
//The size (height,width) is the size of the ORIGNAL image - example 400x400.
        //Bitmap->SaveToFile("test.bmp");//testing intermediate, uncomment and you will see the graphic saved as bitmap
//BUT the size of TImage may be other when using the Stretch property to true. Seting the properties to the new size
        SBitmap->Width = Image1->Width;
        SBitmap->Height = Image1->Height;
//Making a StretchBlt
        StretchBlt(     SBitmap->Canvas->Handle, 0, 0, SBitmap->Width, SBitmap->Height,
                        Bitmap->Canvas->Handle, 0, 0, Bitmap->Width, Bitmap->Height, SRCCOPY);
//And saving
        delete Bitmap;
        delete SBitmap;

Tested it and working with a jpeg with original size 1280x800 to a resized 1680x1050.
I mentioned AFTER BCB5 because version5 need to add a component for supporting other file types than bmp.

George Tokas.
George TokasCommented:
One more thing:
There is a specific topic area about C++ Builder here and this question would be better is asked there....
If I didn't had mail notification I wouldn't see this Q.

George Tokas.
Free Tool: Subnet Calculator

The subnet calculator helps you design networks by taking an IP address and network mask and returning information such as network, broadcast address, and host range.

One of a set of tools we're offering as a way of saying thank you for being a part of the community.

TheEimundAuthor Commented:
Hi George,

thank you for your answer, I was looking for the C++Builder zone, but was not able to find it...

I'm able to control number of pixels that a picture file has, but my question is how I can control the pixel size in inches for instance. I'm talking about the horizontal and vertical resolution in the bitmap header file, offset 26h and 2Ah.

George TokasCommented:
I forgot something:
When testing the code I posted before and checked for the final re-sized bitmap in hex plane both indexes are zero and I mean no DPI is set there for that image...
Maybe it is because of the conversion from jpeg to bmp.

George Tokas.
TheEimundAuthor Commented:
Thank you George, it looks like the solution I was looking for. I been out of the office this past two days, so haven't been able to test the code yet. I will do it after this weekend and award you then ;-)

George TokasCommented:
You are welcome.
>>it looks like the solution I was looking for
Which one?
The TMemoryStream method - last proposal - do the trick but you have to consider and the rest of the graphics formats when working with graphics.
Anyway DPI is valid mostly for printing.
I am not sure, because never tested that, if there is a way to access all those (DPI, palletes, size, etc) when working with DirectX...
I have a set of wrapper classes around Direct3D9 at (FREE) for use with C++ Builder...
By the way the link for C++ Builder topic area is:
There are MANY useful answers there and I suggest to explore the area.

George Tokas.
TheEimundAuthor Commented:
It worked to write to the memory location of  0x26h and 0x2Ah. I also experienced that this location originally was set to zero when the C++ Builder program saved the image to a bitmap file. Maybe zero in these loactions means that Windows will use a default resolution. However, when I wrote the desired resolution to these locations I got the correct size of the picture when I opened it in Paint and viewed the Attributes of the picture. And more important when I imported the picture to a Word document it was displayed in correct size in cm. Here is the code I used

__int32 buf[2];
double mm = Screen->PixelsPerInch/25.4;
TFileStream* file = new TFileStream(*(UnicodeString*) Sender, fmOpenReadWrite );
file->Position = 0x26;
buf[0] = 1000*mm;
buf[1] = 1000*mm;
file->Write(buf, 8);

Eimund Smestad
George TokasCommented:
You are welcome Eimund,
But something is puzzling me...
>>(*(UnicodeString*) Sender
Looks like you are using a version of BCB >= 2006...
Try making a loop for 1000 times with the code. I am suspecting leaks...
Maybe also I am mistaken...
The file stream can be proved tricky some times, that was why I proposed TMemoryStream especially here where you are accessing the file 3 times and I can't see a closing command and I mean:
Create and save to a file.
>>TFileStream* file = new TFileStream(*(UnicodeString*) Sender, fmOpenReadWrite );
Opening again for read and write, 2nd access
>>file->Write(buf, 8);
3rd access for writing and that has the danger to fail...
The handler to the file MAYBE doesn't released for sure...
That is why I propose to make a loop for 1000 times and check the handlers used from the executable in the task manager....

George Tokas.

TheEimundAuthor Commented:
I see your point regarding TMemoryStream, therefore I have rewritten the code to

void __fastcall MemoryPlotSaveFile(TObject* Sender)
	__int32 buf[2];
	TMemoryStream* stream = new TMemoryStream();
	stream->Position = 0x26;
	buf[0] = 1000*mm;
	buf[1] = 1000*mm;
	stream->Write(buf, 8);
	stream->SaveToFile(*(UnicodeString*) Sender);

Open in new window

I couldn't detect any memory leakage from this code.

>> *(UnicodeString*) Sender

is because the String containing the filepath is sent as a TObject*, so I just typecast it back to UnicodeString. I use BCB 2009.

Eimund Smestad
George TokasCommented:
Hi Eimund,
You can use delete instead of free(), I believe it is safer because in many cases when releasing VCL objects that is done asynchronously by the RTL and in case of an exception MAYBE not released properly.
But seeing your code I KNOW that you have experience, so I am just "pointing" potential hazards...
For some "old dogs" like me (registered since BCB V1) the UnicodeString rings a bell because there was limited support in versions before 2006.
One more thing if I may.
Consider changing the code from:
                stream->Position = 0x26;
      buf[0] = 1000*mm;
      buf[1] = 1000*mm;
      stream->Write(buf, 8);
                stream->Position = 0x26;
      buf[0] = 1000*mm;
                stream->Write(buf[0], 4);
      buf[1] = 1000*mm;
                stream->Position = 0x2a;
      stream->Write(buf[1], 4);

The reason for the proposal is that there are times the buffer declared not be continuous say: 0x00000000 to 0x00000007 but fragmented. Happened more than once to me (in fact multiple times) when dealing with DirectX.
In all other ways your code is wisely writen from the beggining.

George Tokas.
Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.

All Courses

From novice to tech pro — start learning today.