HighTechTalks DotNet Forums  

Scale Transform - Why do I need to account for verticalResolution?

Dotnet Framework (Drawing) microsoft.public.dotnet.framework.drawing


Discuss Scale Transform - Why do I need to account for verticalResolution? in the Dotnet Framework (Drawing) forum.



Reply
 
Thread Tools Search this Thread Display Modes
  #1  
Old   
Mash
 
Posts: n/a

Default Scale Transform - Why do I need to account for verticalResolution? - 12-31-2007 , 02:23 PM






My code calculates the size required to maintain the aspect ratio of
an image when displaying it. I use a scale transformation to scale the
image to the proper size. For some reason my scaling is always off
unless I account for the vertical and horizontal resolution of the
image being drawn, as well as the DpiX and DpiY of the graphics
object.

Why is this?

My control consists of a main control which can be resized by the
user, an inner control which is resized as the main control is
resized, and an image which is drawn in the inner control. The inner
control is sized to maintain the aspect ratio of the image when
displayed in the main control.

Here are the steps, I've include relevant code below

1. Calculate Scaling Ratio
2. Resize my control to be the exact size of the image
3. Perform a transformation on my image so it is drawn within the
inner control

In the function SetupTransform, if I take out the code to regarding
the resolution of the Image and the Dpi of the graphics object my
scaling is off, if I leave it in the scaling is perfect. I don't
understand why? I've tried changing the PageUnits of my graphics
object but that doesn't seem to make a difference.

1.Calculate Scaling Ratio
public float CalculateScalingRatio(float width, float height)
{
float largestRatio=0;
if (Image != null)
{
largestRatio = (Math.Max((float)Image.Width / width,
(float)Image.Height / height));
}
return largestRatio;
}

2.Resize inner control so that aspect ratio will be maintained
private void MarkedImageContainer_Paint(object sender, PaintEventArgs
e)
{
double largestRatio;
float posX, posY;

if (m_markedImage != null)
{

//get the graphics object
Graphics g = e.Graphics;

if (m_maintainAspectRatio)
{
//calculate the largest scaling ratio required for
the image to maintain
//its' aspect ratio
largestRatio =
m_markedImage.CalculateScalingRatio(Parent.Width, Parent.Height);
posX = (float)((Parent.Width -
(m_markedImage.Image.Width / largestRatio)) / 2);
posY = (float)((Parent.Height -
(m_markedImage.Image.Height / largestRatio)) / 2);

Width = (int)(m_markedImage.Image.Width /
largestRatio);
Height = (int)(m_markedImage.Image.Height /
largestRatio);
}
Left = Convert.ToInt32(posX);
Top = Convert.ToInt32(posY) + m_topPadding;
//make sure the marked image is drawn entirely within
this control
m_markedImage.Limits = new Rectangle(0, 0, this.Width,
this.Height);
//render

m_markedImage.Draw(g);
}
else
{

}
}

}

3.Tranform and draw image
protected override void SetupTransform(Graphics g)
{

//Why do I need to do this??
float xDpiScale = (m_Image.VerticalResolution / g.DpiX);
float yDpiScale = (m_Image.HorizontalResolution / g.DpiY);

float trueLimitX = Limits.Width*xDpiScale;
float trueLimitY = Limits.Height*yDpiScale;

//calculate scaling ratio necessary to draw the image
inside of its limits
float largestRatio = CalculateScalingRatio(Limits.Width,
Limits.Height);

ScaleFactor = largestRatio;

//create the matrix we'll use to perform the scaling
Matrix mx = new Matrix();
mx.Scale(1.0f / largestRatio, 1.0f / largestRatio);

g.Transform = mx;
mx.Dispose();

}

//now draw the image
protected override void RenderObject(Graphics g)
{

//draw the ourTemplate on the surface that was scaled in
SetupTransform
g.DrawImageUnscaled(m_Image, 0, 0);

//reset the graphics object
}

Reply With Quote
  #2  
Old   
Bob Powell [MVP]
 
Posts: n/a

Default Re: Scale Transform - Why do I need to account for verticalResolution? - 12-31-2007 , 05:18 PM






The DrawImage variants that do not explicitly specify the source and
destination rectangle and the Pixel GraphicsUnit will automatically make the
size translation on the drawing surface.

To control the output on the pixel level you need to either be aware of the
exact image resolution of the drawing surface and the image or specify the
three factors mentioned above.

--
--
Bob Powell [MVP]
Visual C#, System.Drawing

Ramuseco Limited .NET consulting
http://www.ramuseco.com

Find great Windows Forms articles in Windows Forms Tips and Tricks
http://www.bobpowell.net/tipstricks.htm

Answer those GDI+ questions with the GDI+ FAQ
http://www.bobpowell.net/faqmain.htm

All new articles provide code in C# and VB.NET.
Subscribe to the RSS feeds provided and never miss a new article.


"Mash" <mashman74 (AT) hotmail (DOT) com> wrote

Quote:
My code calculates the size required to maintain the aspect ratio of
an image when displaying it. I use a scale transformation to scale the
image to the proper size. For some reason my scaling is always off
unless I account for the vertical and horizontal resolution of the
image being drawn, as well as the DpiX and DpiY of the graphics
object.

Why is this?

My control consists of a main control which can be resized by the
user, an inner control which is resized as the main control is
resized, and an image which is drawn in the inner control. The inner
control is sized to maintain the aspect ratio of the image when
displayed in the main control.

Here are the steps, I've include relevant code below

1. Calculate Scaling Ratio
2. Resize my control to be the exact size of the image
3. Perform a transformation on my image so it is drawn within the
inner control

In the function SetupTransform, if I take out the code to regarding
the resolution of the Image and the Dpi of the graphics object my
scaling is off, if I leave it in the scaling is perfect. I don't
understand why? I've tried changing the PageUnits of my graphics
object but that doesn't seem to make a difference.

1.Calculate Scaling Ratio
public float CalculateScalingRatio(float width, float height)
{
float largestRatio=0;
if (Image != null)
{
largestRatio = (Math.Max((float)Image.Width / width,
(float)Image.Height / height));
}
return largestRatio;
}

2.Resize inner control so that aspect ratio will be maintained
private void MarkedImageContainer_Paint(object sender, PaintEventArgs
e)
{
double largestRatio;
float posX, posY;

if (m_markedImage != null)
{

//get the graphics object
Graphics g = e.Graphics;

if (m_maintainAspectRatio)
{
//calculate the largest scaling ratio required for
the image to maintain
//its' aspect ratio
largestRatio =
m_markedImage.CalculateScalingRatio(Parent.Width, Parent.Height);
posX = (float)((Parent.Width -
(m_markedImage.Image.Width / largestRatio)) / 2);
posY = (float)((Parent.Height -
(m_markedImage.Image.Height / largestRatio)) / 2);

Width = (int)(m_markedImage.Image.Width /
largestRatio);
Height = (int)(m_markedImage.Image.Height /
largestRatio);
}
Left = Convert.ToInt32(posX);
Top = Convert.ToInt32(posY) + m_topPadding;
//make sure the marked image is drawn entirely within
this control
m_markedImage.Limits = new Rectangle(0, 0, this.Width,
this.Height);
//render

m_markedImage.Draw(g);
}
else
{

}
}

}

3.Tranform and draw image
protected override void SetupTransform(Graphics g)
{

//Why do I need to do this??
float xDpiScale = (m_Image.VerticalResolution / g.DpiX);
float yDpiScale = (m_Image.HorizontalResolution / g.DpiY);

float trueLimitX = Limits.Width*xDpiScale;
float trueLimitY = Limits.Height*yDpiScale;

//calculate scaling ratio necessary to draw the image
inside of its limits
float largestRatio = CalculateScalingRatio(Limits.Width,
Limits.Height);

ScaleFactor = largestRatio;

//create the matrix we'll use to perform the scaling
Matrix mx = new Matrix();
mx.Scale(1.0f / largestRatio, 1.0f / largestRatio);

g.Transform = mx;
mx.Dispose();

}

//now draw the image
protected override void RenderObject(Graphics g)
{

//draw the ourTemplate on the surface that was scaled in
SetupTransform
g.DrawImageUnscaled(m_Image, 0, 0);

//reset the graphics object
}


Reply With Quote
  #3  
Old   
Mash
 
Posts: n/a

Default Re: Scale Transform - Why do I need to account forverticalResolution? - 12-31-2007 , 09:45 PM



On Dec 31, 5:18*pm, "Bob Powell [MVP]" <b... (AT) spamkillerbobpowell (DOT) net>
wrote:
Quote:
The DrawImage variants that do not explicitly specify the source and
destination rectangle and the Pixel GraphicsUnit will automatically make the
size translation on the drawing surface.

To control the output on the pixel level you need to either be aware of the
exact image resolution of the drawing surface and the image or specify the
three factors mentioned above.

Thanks for the response Bob,
Bear with me as I'm a bit of a novice with GDI, but I thought if I did
the desired transform on the graphics object then I would do a 1:1
draw of the image?
So if I wanted to scale the image by 1/2, I would apply a scale
transform to the Graphics object and then just drawImageUnscaled with
that graphics object.
Shouldn't the transformation handle all of the scaling for me
regardless of the surface?


Thanks


Reply With Quote
  #4  
Old   
Bob Powell [MVP]
 
Posts: n/a

Default Re: Scale Transform - Why do I need to account for verticalResolution? - 01-01-2008 , 08:34 AM



DrawImageUnscaled draws the image _physically_ unscaled taking the actual
size of the rectangle calculated by the resolution and the pixel dimensions
of the picture.

It uses the world coordinates and ties them to the resolution of the output
device, most likely 96 DPI.

If you just want to output the image according to its pixel dimensions and
ignore the actual physical size you must use the DrawImage variant that
specifies the source, destination and Pixel GraphicsUnit.

--
--
Bob Powell [MVP]
Visual C#, System.Drawing

Ramuseco Limited .NET consulting
http://www.ramuseco.com

Find great Windows Forms articles in Windows Forms Tips and Tricks
http://www.bobpowell.net/tipstricks.htm

Answer those GDI+ questions with the GDI+ FAQ
http://www.bobpowell.net/faqmain.htm

All new articles provide code in C# and VB.NET.
Subscribe to the RSS feeds provided and never miss a new article.


"Mash" <mashman74 (AT) hotmail (DOT) com> wrote

On Dec 31, 5:18 pm, "Bob Powell [MVP]" <b... (AT) spamkillerbobpowell (DOT) net>
wrote:
Quote:
The DrawImage variants that do not explicitly specify the source and
destination rectangle and the Pixel GraphicsUnit will automatically make
the
size translation on the drawing surface.

To control the output on the pixel level you need to either be aware of
the
exact image resolution of the drawing surface and the image or specify the
three factors mentioned above.

Thanks for the response Bob,
Bear with me as I'm a bit of a novice with GDI, but I thought if I did
the desired transform on the graphics object then I would do a 1:1
draw of the image?
So if I wanted to scale the image by 1/2, I would apply a scale
transform to the Graphics object and then just drawImageUnscaled with
that graphics object.
Shouldn't the transformation handle all of the scaling for me
regardless of the surface?


Thanks



Reply With Quote
  #5  
Old   
Mash
 
Posts: n/a

Default Re: Scale Transform - Why do I need to account forverticalResolution? - 01-01-2008 , 07:14 PM



On Jan 1, 8:34*am, "Bob Powell [MVP]" <b... (AT) spamkillerbobpowell (DOT) net>
wrote:
Quote:
DrawImageUnscaled draws the image _physically_ unscaled taking the actual
size of the rectangle calculated by the resolution and the pixel dimensions
of the picture.

It uses the world coordinates and ties them to the resolution of the output
device, most likely 96 DPI.

If you just want to output the image according to its pixel dimensions and
ignore the actual physical size you must use the DrawImage variant that
specifies the source, destination and Pixel GraphicsUnit.
Thanks again Bob, that helps alot.
So does that mean I really don't need a transformation to scale my
image? I believe that using DrawImage with a source and dest rectangle
will scale my image as required. I'll play around with it when I get
back to work.
I have Another question on the same topic if that's ok...
In the same application I add shapes to point to certain locations on
the image. As the image is scaled, the shapes have to point to the
same location on the image.
I don't actually draw on the image itself because I don't want the
shapes (and text) scaled as the image is scaled. So I do the
following:
-Get the graphics object and perform a scale transformation after
calculating the scale ratio for the image (the code is in a previous
post)
-Draw the image
-restore the state of the graphics object
-perform a translation and draw my shapes and text so that they point
to the proper locations on the image (I use the same factor to
translate the shapes as I do to scale the image, see code below).

This works great and as I scale the image the shapes always point to
the proper location on the image (I also allow the user to move the
shapes using the backtrack method I learned on your site...Thanks)

So I have to take the resolutions of the image and drawing surface
into account when placing the shapes in the proper location with
respect to the image. Will that change if I use the DrawImage with src/
dest rectangles? I've been learning GDI+ and transformations as I
write this app but I'm definitely missing something.

So to calculate the scaling for the image I do (which might change
based on the comments you've made)
protected override void SetupTransform(Graphics g)
{

float xDpiScale = (m_Image.VerticalResolution / g.DpiX);
float yDpiScale = (m_Image.HorizontalResolution /
g.DpiY);

float trueLimitX = Limits.Width*xDpiScale;
float trueLimitY = Limits.Height*yDpiScale;

//calculate scaling ratio necessary to draw the image
inside of its limits
float largestRatio = CalculateScalingRatio(Limits.Width,
Limits.Height);

ScaleFactor = largestRatio;


//create the matrix we'll use to perform the scaling
Matrix mx = new Matrix();
mx.Scale(1.0f / largestRatio, 1.0f / largestRatio);

g.Transform = mx;
mx.Dispose();

}

//Do a DrawImageUnscaled,etc...


I then pass the ScaleFactor to the shapes which perform a translation
as follows
protected override void SetupTransform(Graphics g)
{
Matrix mx = new Matrix();

//This Scale Factor is the same one as calculated when scaling the
image
mx.Translate(Position.X/ScaleFactor, Position.Y/ScaleFactor);

g.Transform = mx;

}

//draw the shapes

I chose this method because I couldn't think of another way to always
draw the shapes to point to the same location in the image as the
image is scaled. If you have a better method I would appreciate any
advice. Thanks again
Mike




Reply With Quote
  #6  
Old   
Mash
 
Posts: n/a

Default Re: Scale Transform - Why do I need to account forverticalResolution? - 01-01-2008 , 09:39 PM



On Jan 1, 8:34*am, "Bob Powell [MVP]" <b... (AT) spamkillerbobpowell (DOT) net>
wrote:
Quote:
DrawImageUnscaled draws the image _physically_ unscaled taking the actual
size of the rectangle calculated by the resolution and the pixel dimensions
of the picture.

It uses the world coordinates and ties them to the resolution of the output
device, most likely 96 DPI.

If you just want to output the image according to its pixel dimensions and
ignore the actual physical size you must use the DrawImage variant that
specifies the source, destination and Pixel GraphicsUnit.

For anybody who wants to know more about this I found this MS article
on this topic

http://support.microsoft.com/kb/317174

PRB: Drawing Dimensions Are Inconsistent with Image.Size Property When
You Use DrawImageUnscaled() Method




Reply With Quote
Reply




Thread Tools Search this Thread
Search this Thread:

Advanced Search
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

vB code is On
Smilies are On
[IMG] code is On
HTML code is Off



Powered by vBulletin Version 3.5.4
Copyright ©2000 - 2008, Jelsoft Enterprises Ltd.