Solved

Set file resolution DPI when saving a TImage

Posted on 2011-09-19
11
774 Views
Last Modified: 2012-05-12
Hi,

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?

Regards
Eimund Smestad
0
Comment
Question by:TheEimund
  • 7
  • 4
11 Comments
 
LVL 16

Expert Comment

by:George Tokas
ID: 36566278
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->Assign(Image1->Picture->Graphic);
        //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
        SBitmap->SaveToFile("test.bmp");
        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.
0
 
LVL 16

Expert Comment

by:George Tokas
ID: 36566287
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.
0
 

Author Comment

by:TheEimund
ID: 36571892
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.

Eimund
0
Industry Leaders: We Want Your Opinion!

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

 
LVL 16

Accepted Solution

by:
George Tokas earned 500 total points
ID: 36574327
Hi Eimund,
Well in this case check this out:
http://delphicikk.atw.hu/listaz.php?id=788&oldal=30
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...
MemoryStream->Write(buffer,2);//write
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...
BY THE WAY.
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.
0
 
LVL 16

Expert Comment

by:George Tokas
ID: 36580697
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.
0
 

Author Comment

by:TheEimund
ID: 36591214
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 ;-)

Eimund
0
 
LVL 16

Expert Comment

by:George Tokas
ID: 36591897
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 www.gtokas.com (FREE) for use with C++ Builder...
By the way the link for C++ Builder topic area is:
http://www.experts-exchange.com/Programming/Editors_IDEs/C_CPP_CS/CPP_Builder/
There are MANY useful answers there and I suggest to explore the area.

George Tokas.
0
 

Author Closing Comment

by:TheEimund
ID: 36597913
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;
ChartImage->Picture->SaveToFile(*(UnicodeString*)Sender);
TFileStream* file = new TFileStream(*(UnicodeString*) Sender, fmOpenReadWrite );
file->Position = 0x26;
buf[0] = 1000*mm;
buf[1] = 1000*mm;
file->Write(buf, 8);
file->Free();

Thanks
Eimund Smestad
0
 
LVL 16

Expert Comment

by:George Tokas
ID: 36597994
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:
>>ChartImage->Picture->SaveToFile(*(UnicodeString*)Sender);
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...
>>file->Free();
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.

0
 

Author Comment

by:TheEimund
ID: 36598864
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();
	ChartImage->Picture->Bitmap->SaveToStream(stream);
	stream->Position = 0x26;
	buf[0] = 1000*mm;
	buf[1] = 1000*mm;
	stream->Write(buf, 8);
	stream->SaveToFile(*(UnicodeString*) Sender);
	stream->Free();
}

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
0
 
LVL 16

Expert Comment

by:George Tokas
ID: 36599057
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);
to:
                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.
0

Featured Post

Secure Your Active Directory - April 20, 2017

Active Directory plays a critical role in your company’s IT infrastructure and keeping it secure in today’s hacker-infested world is a must.
Microsoft published 300+ pages of guidance, but who has the time, money, and resources to implement? Register now to find an easier way.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

This article will show you some of the more useful Standard Template Library (STL) algorithms through the use of working examples.  You will learn about how these algorithms fit into the STL architecture, how they work with STL containers, and why t…
Container Orchestration platforms empower organizations to scale their apps at an exceptional rate. This is the reason numerous innovation-driven companies are moving apps to an appropriated datacenter wide platform that empowers them to scale at a …
The goal of the tutorial is to teach the user how to use functions in C++. The video will cover how to define functions, how to call functions and how to create functions prototypes. Microsoft Visual C++ 2010 Express will be used as a text editor an…
The viewer will learn how to clear a vector as well as how to detect empty vectors in C++.

679 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question