Solved

Set file resolution DPI when saving a TImage

Posted on 2011-09-19
11
738 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
 
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
What Is Threat Intelligence?

Threat intelligence is often discussed, but rarely understood. Starting with a precise definition, along with clear business goals, is essential.

 

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

How to run any project with ease

Manage projects of all sizes how you want. Great for personal to-do lists, project milestones, team priorities and launch plans.
- Combine task lists, docs, spreadsheets, and chat in one
- View and edit from mobile/offline
- Cut down on emails

Join & Write a Comment

Article by: SunnyDark
This article's goal is to present you with an easy to use XML wrapper for C++ and also present some interesting techniques that you might use with MS C++. The reason I built this class is to ease the pain of using XML files with C++, since there is…
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…
The goal of the video will be to teach the user the concept of local variables and scope. An example of a locally defined variable will be given as well as an explanation of what scope is in C++. The local variable and concept of scope will be relat…
The viewer will learn how to use the return statement in functions in C++. The video will also teach the user how to pass data to a function and have the function return data back for further processing.

744 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

Need Help in Real-Time?

Connect with top rated Experts

13 Experts available now in Live!

Get 1:1 Help Now