Board index » delphi » tBitmap - need to manipulate DPI, how?

tBitmap - need to manipulate DPI, how?


2005-01-31 08:51:17 PM
delphi245
Hello all,
I have the problem that a tBitmap internally inherits the screen's
resolution (96 DPI, which you can check with GetDevicecaps).
Whenever I use a component like a tRichedit to render text on this
bitmap, it does so at the screen's resolution which is too small for
my purposes.
I need to manipulate this DPI setting, I want to make bitmaps of
various resolutions.
How to do it? Is there a function like "SetDeviceCaps"?
--
Arthur Hoornweg
(In order to reply per e-mail, please just remove the ".net"
from my e-mail address. Leave the rest of the address intact
including the "antispam" part. I had to take this measure to
counteract unsollicited mail.)
 
 

Re:tBitmap - need to manipulate DPI, how?

In article <41fe29c5$XXXX@XXXXX.COM>, Arthur Hoornweg writes:
Quote
I have the problem that a tBitmap internally inherits the screen's
resolution (96 DPI, which you can check with GetDevicecaps).

Whenever I use a component like a tRichedit to render text on this
bitmap, it does so at the screen's resolution which is too small for
my purposes.

I need to manipulate this DPI setting, I want to make bitmaps of
various resolutions.

How to do it? Is there a function like "SetDeviceCaps"?
You can define your own coordinate system for a canvas, see SetMapMode,
SetWindowExtEx, SetViewportExtEx. The following sample app uses this
mechanism to paint a richedit to a bitmap with some scaling applied.
Painting a rich edit control onto a bitmap for preview:
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms,
Dialogs,
StdCtrls, ExtCtrls, ComCtrls;
type
TForm1 = class(TForm)
RichEdit1: TRichEdit;
Image1: TImage;
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
uses printers, richedit;
{$R *.DFM}
procedure TForm1.Button1Click(Sender: TObject);
var
zoomfactor, xScale, yScale: Single;
pagerect, outputrect : TRect;
fmtRange: TFormatRange;
begin
zoomfactor := 0.5;
xScale := Screen.PixelsPerInch / GetDeviceCaps( Printer.handle,
LOGPIXELSX );
yScale := Screen.PixelsPerInch / GetDeviceCaps( Printer.handle,
LOGPIXELSY );
// Size bitmap to 50% of size of a printer page and fill it white
With image1.Picture.Bitmap Do Begin
Width := Round( Printer.Pagewidth * zoomfactor * xScale );
Height:= Round( Printer.PageHeight * zoomfactor * yScale );
With Canvas Do Begin
Brush.Color := clWhite;
Brush.Style := bsSolid;
FillRect( Cliprect );
End;
End;
// scale the bitmap canvas according to the zoomfactor
With image1.Picture.Bitmap.Canvas Do Begin
SetMapMode( handle, MM_ANISOTROPIC );
SetWindowExtEx(handle,
Screen.PixelsPerInch, Screen.PixelsPerInch,
Nil);
SetViewportExtEx(handle,
Round(Screen.PixelsPerInch * zoomfactor),
Round(Screen.PixelsPerInch * zoomfactor),
Nil);
End;
// set up a page rectangle for the rich edit control and
// an output area inside, which gives us some margins. The
// units here are twips (1/1440 inch).
With image1.Picture.Bitmap Do
pagerect := Rect( 0, 0,
Round(width * 1440 / Screen.PixelsPerInch /
xScale),
Round(height * 1440 / Screen.PixelsPerInch /
yScale) );
outputrect := pagerect;
InflateRect( outputrect, -720, -720 ); // 1/2 inch margin
// set up the parameter record for EM_FORMATRANGE
fillChar( fmtRange, sizeof(fmtrange), 0);
With fmtRange Do Begin
hDC := image1.Picture.Bitmap.Canvas.Handle;
hdcTarget := hDC;
rc := outputrect;
rcPage := pagerect;
chrg.cpMin := 0;
chrg.cpMax := richedit1.GetTextLen-1;
End;
// format the text
richedit1.Perform( EM_FORMATRANGE, 1, Longint(@fmtRange));
// Free cached information
richedit1.Perform( EM_FORMATRANGE, 0, 0);
end;
end.
Quote

Peter Below (TeamB)
Use the newsgroup archives :
www.mers.com/searchsite.html
www.tamaracka.com/search.htm
groups.google.com
www.prolix.be
 

Re:tBitmap - need to manipulate DPI, how?

Hi Peter,
I know about the SetMapMode etc, but all examples I have seen so
far using tRichedits and EM_formatrange set the hdc to
mapmode mm_text. Yours sets it to mm_anisotropic.
Now how does the richedit know how to scale, say a 10 points
font, to the resolution I want?
My own "solution" so far involved the modifying of the RTF code
itself and substituting the font sizes (\fs Macro) to scaled
versions.
Regards,
Arthur hoornweg
Peter Below (TeamB) schreef:
Quote
In article <41fe29c5$XXXX@XXXXX.COM>, Arthur Hoornweg writes:

>I have the problem that a tBitmap internally inherits the screen's
>resolution (96 DPI, which you can check with GetDevicecaps).
>
>Whenever I use a component like a tRichedit to render text on this
>bitmap, it does so at the screen's resolution which is too small for
>my purposes.
>
>I need to manipulate this DPI setting, I want to make bitmaps of
>various resolutions.
>
>How to do it? Is there a function like "SetDeviceCaps"?


You can define your own coordinate system for a canvas, see SetMapMode,
SetWindowExtEx, SetViewportExtEx. The following sample app uses this
mechanism to paint a richedit to a bitmap with some scaling applied.

Painting a rich edit control onto a bitmap for preview:

unit Unit1;

interface

uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms,
Dialogs,
StdCtrls, ExtCtrls, ComCtrls;

type
TForm1 = class(TForm)
RichEdit1: TRichEdit;
Image1: TImage;
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;

var
Form1: TForm1;

implementation

uses printers, richedit;
{$R *.DFM}


procedure TForm1.Button1Click(Sender: TObject);
var
zoomfactor, xScale, yScale: Single;
pagerect, outputrect : TRect;
fmtRange: TFormatRange;
begin
zoomfactor := 0.5;
xScale := Screen.PixelsPerInch / GetDeviceCaps( Printer.handle,
LOGPIXELSX );
yScale := Screen.PixelsPerInch / GetDeviceCaps( Printer.handle,
LOGPIXELSY );
// Size bitmap to 50% of size of a printer page and fill it white
With image1.Picture.Bitmap Do Begin
Width := Round( Printer.Pagewidth * zoomfactor * xScale );
Height:= Round( Printer.PageHeight * zoomfactor * yScale );
With Canvas Do Begin
Brush.Color := clWhite;
Brush.Style := bsSolid;
FillRect( Cliprect );
End;
End;

// scale the bitmap canvas according to the zoomfactor
With image1.Picture.Bitmap.Canvas Do Begin
SetMapMode( handle, MM_ANISOTROPIC );
SetWindowExtEx(handle,
Screen.PixelsPerInch, Screen.PixelsPerInch,
Nil);
SetViewportExtEx(handle,
Round(Screen.PixelsPerInch * zoomfactor),
Round(Screen.PixelsPerInch * zoomfactor),
Nil);
End;

// set up a page rectangle for the rich edit control and
// an output area inside, which gives us some margins. The
// units here are twips (1/1440 inch).
With image1.Picture.Bitmap Do
pagerect := Rect( 0, 0,
Round(width * 1440 / Screen.PixelsPerInch /
xScale),
Round(height * 1440 / Screen.PixelsPerInch /
yScale) );
outputrect := pagerect;
InflateRect( outputrect, -720, -720 ); // 1/2 inch margin

// set up the parameter record for EM_FORMATRANGE
fillChar( fmtRange, sizeof(fmtrange), 0);
With fmtRange Do Begin
hDC := image1.Picture.Bitmap.Canvas.Handle;
hdcTarget := hDC;
rc := outputrect;
rcPage := pagerect;
chrg.cpMin := 0;
chrg.cpMax := richedit1.GetTextLen-1;
End;
// format the text
richedit1.Perform( EM_FORMATRANGE, 1, Longint(@fmtRange));
// Free cached information
richedit1.Perform( EM_FORMATRANGE, 0, 0);
end;

end.



--
Peter Below (TeamB)
Use the newsgroup archives :
www.mers.com/searchsite.html
www.tamaracka.com/search.htm
groups.google.com
www.prolix.be


--
Arthur Hoornweg
(In order to reply per e-mail, please just remove the ".net"
from my e-mail address. Leave the rest of the address intact
including the "antispam" part. I had to take this measure to
counteract unsollicited mail.)
 

Re:tBitmap - need to manipulate DPI, how?

In article <41fe7e5c$XXXX@XXXXX.COM>, Arthur Hoornweg writes:
Quote
I know about the SetMapMode etc, but all examples I have seen so
far using tRichedits and EM_formatrange set the hdc to
mapmode mm_text. Yours sets it to mm_anisotropic.

Now how does the richedit know how to scale, say a 10 points
font, to the resolution I want?
It calculates its output size in terms of the output canvas logical units,
which is whatever you define using the mapping mode in question. For
MM_TEXT the logical units are the same as the device units (pixel), for a
custom mapping mode they are not.
Peter Below (TeamB)
Use the newsgroup archives :
www.mers.com/searchsite.html
www.tamaracka.com/search.htm
groups.google.com
www.prolix.be
 

Re:tBitmap - need to manipulate DPI, how?

Peter Below (TeamB) schreef:
Quote
It calculates its output size in terms of the output canvas logical units,
which is whatever you define using the mapping mode in question. For
MM_TEXT the logical units are the same as the device units (pixel), for a
Ah, so you don't manipulate the internal DPI characteristics
at all, merely change the coordinate system.
Too bad, though the solution is elegant, it would severely
interfere with the rest of my code that assumes that
1 pixel = 1 pixel.
Isn't there any possibility to "really" change the DPI values
of a bitmap? So that GetDeviceCaps() returns it correctly
even in mm_text mapping mode?
--
Arthur Hoornweg
(In order to reply per e-mail, please just remove the ".net"
from my e-mail address. Leave the rest of the address intact
including the "antispam" part. I had to take this measure to
counteract unsollicited mail.)
 

Re:tBitmap - need to manipulate DPI, how?

In article <420082f7$XXXX@XXXXX.COM>, Arthur Hoornweg writes:
Quote
Ah, so you don't manipulate the internal DPI characteristics
at all, merely change the coordinate system.

Too bad, though the solution is elegant, it would severely
interfere with the rest of my code that assumes that
1 pixel = 1 pixel.

Isn't there any possibility to "really" change the DPI values
of a bitmap? So that GetDeviceCaps() returns it correctly
even in mm_text mapping mode?
Nope. But i don't see your problem. You can always change the mapping
mode in mid-drawing, so draw your own stuff in MM_TEXT and only change
the mapping mode to render the richedits content.
There may be another option: richedit version 3 supports zooming, search the
SDK docs for EM_SETZOOM. I don't know if that affects text rendered via
EM_FORMATRANGE, but it should, IMO. You need a richedit wrapper that uses
riched20.dll instead of riched32.dll for this to work and your code may aquire
some platform dependency since older platforms may have version 2 of the
control in this DLL, not version 3.
If you don't have a suitable richedit control available: search the newsgroup
archives for TPBRichEdit20, or get the JEDI VCL from sourceforge.net.
Peter Below (TeamB)
Use the newsgroup archives :
www.mers.com/searchsite.html
www.tamaracka.com/search.htm
groups.google.com
www.prolix.be