 |
BorlandTalk.com Borland discussion newsgroups
|
| View previous topic :: View next topic |
| Author |
Message |
Enquiring Mind Guest
|
Posted: Wed May 16, 2007 8:52 pm Post subject: Handling palettes in copying pictures to and from clipboard |
|
|
Hi,
Can anyone indicate how to avoid the following problem?
I load the Factory.bmp bitmap from Borland..Images into a TPicture object
called InputPicture, save the picture to a memory stream called
InputGraphicStream, and inspect the size of the stream. I find it's 43.24
kB.
I then create an output bitmap object OutputPicture, draw the input picture
on its canvas using
OutputPicture.Canvas.StretchDraw(Rect(0,0,InputPicture.Width-1,
InputPicture.Height-1), InputPicture.Graphic)
then save OutputPicture to a memory stream OutputGraphicStream. The size of
data in the output stream is 168.803 kB, or about 4 times the size of the
input stream.
I presume this is due to the replacement of 1-byte colour in the input image
by 4-byte colour in the output image.
What is the recommended way of making the colour depth of the output image
the same as that of the input image? The on-line help suggests that it's
incorrect to assign the Palette property like so:
OutputBitmap.Palette:= InputPicture.Graphic.Palette;
Any help would be appreciated.
Enquiring Mind |
|
| Back to top |
|
 |
Reiner Guest
|
Posted: Thu May 17, 2007 7:41 am Post subject: Re: Handling palettes in copying pictures to and from clipbo |
|
|
"Enquiring Mind" <Enquiring.Mind (AT) nospam (DOT) btopenworld.com> wrote in message
news:464b28d1$1 (AT) newsgroups (DOT) borland.com...
| Quote: | OutputBitmap.Palette:= InputPicture.Graphic.Palette;
|
try
OutputBitmap.PixelFormat:= InputPicture.Picture.Bitmap.PixelFormat;
OutputBitmap.Palette:= InputPicture.Picture.Bitmap.Palette; |
|
| Back to top |
|
 |
Enquiring Mind Guest
|
Posted: Thu May 17, 2007 4:07 pm Post subject: Re: Handling palettes in copying pictures to and from clipbo |
|
|
"Reiner" <slugu (AT) yahoo (DOT) com> wrote in message
news:464bc0bf (AT) newsgroups (DOT) borland.com...
| Quote: |
try
OutputBitmap.PixelFormat:= InputPicture.Picture.Bitmap.PixelFormat;
OutputBitmap.Palette:= InputPicture.Picture.Bitmap.Palette;
Thanks for the suggestion. However I think that I can see two problems with |
the code:
1) I have a feeling (correct me if I'm wrong) that the colour range is
defined *either* by a Windows-specific device dependent palette (a look-up
table of 256 colours), *or* by the PixelFormat property, but not both. My
guess is that if a Palette property is defined, then the pixel content is a
byte indicating the position of the colour in the palette and the colour of
each pixel must be looked up; whereas if a PixelFormat property is defined
that indicates colour depth in bits, each pixel contains the actual colour
data. Therefore only one or the other property should be copied, depending
on whether or not the Palette property is zero. To my mind, this level of
hidden interdependence between properties is an example of poor OO code
design. To avoid all possible confusion, it would have been better to
provide a Boolean property to indicate whether the bitmap's pixel values are
in the pixel array or in a look-up array. Then one could write
if InputPicture.Graphic is TBitmap then
begin
if InputPicture.UsesPalette then {Clearer than InputPicture.Palette<>0}
OutputBitmap.Palette:= InputPicture.Bitmap.Palette {On-line help
warns against doing this}
else
OutputBitmap.PixelFormat:= InputPicture.Bitmap.PixelFormat;
end
else if InputPicture.Graphic is TJpegImage then
...;
High-level application developers do not want to have to manipulate
low-level Windows handles like the Palette property. The Windows Palette
handle could easily have been encapsulated in a property having a higher
level Pascal data type.
2) The TPicture.Bitmap property is a *very* misleading property. Intuitively
one would think that it returns the picture's data as a bitmap. After all,
every picture of whatever type must eventually be rendered as a bitmap, and
therefore the concept of a bitmap property is relevant and applicable to any
type of graphic. But in reality the Bitmap property only returns a bitmap if
the TPicture's Graphic property is a TBitmap. If the picture graphic is say
a TJpegImage the property returns nil, and the above code fails. This lack
of applicability across all possible TPicture instances is another example,
IMHO, of poor OO code design. It would have been better to omit this
property from the TPicture class definition, and make programmers use
'Picture.Graphic as TBitmap' instead. A lot of confusion would have thereby
been avoided.
Regards,
Enquiring Mind |
|
| Back to top |
|
 |
Jens Gruschel Guest
|
Posted: Fri May 18, 2007 8:11 am Post subject: Re: Handling palettes in copying pictures to and from clipbo |
|
|
| Quote: | 1) I have a feeling (correct me if I'm wrong) that the colour range is
defined *either* by a Windows-specific device dependent palette (a look-up
table of 256 colours), *or* by the PixelFormat property, but not both.
|
No, DIBs with less than 16 bit also have a color palette. See BITMAPINFO
in the WinAPI help for more details.
I don't know whether this will help you, but in case a CF_BITMAP is
available in the clipboard there's *always* a CF_DIB available, which I
prefer for pasting (however it might depend what exactly you need). With
following code you should be able to paste *any* bitmap (with or without
color palette) to *any* canvas, possibly with better quality compared to
using a device dependent bitmap:
function CopyDIBFromClipboard(Canvas: TCanvas; X, Y: Integer): Boolean;
var
DataHandle: THandle;
BitmapInfo: PBitmapInfo;
Origin: Pointer;
W, H: Integer;
begin
Result := False;
if OpenClipboard(0) then try
if IsClipboardFormatAvailable(CF_DIB) then begin
DataHandle := GetClipboardData(CF_DIB);
if DataHandle <> 0 then begin
BitmapInfo := GlobalLock(DataHandle);
if Assigned(BitmapInfo) then try
W := BitmapInfo^.bmiHeader.biWidth;
H := Abs(BitmapInfo^.bmiHeader.biHeight));
Origin := Pointer(Integer(BitmapInfo)
+ Integer(BitmapInfo^.bmiHeader.biSize));
if BitmapInfo^.bmiHeader.biClrUsed > 0 then begin
Origin := Pointer(Integer(Origin)
+ BitmapInfo^.bmiHeader.biClrUsed * SizeOf(TRGBQuad));
end
else if BitmapInfo^.bmiHeader.biBitCount < 16 then begin
Origin := Pointer(Integer(Origin)
+ (1 shl BitmapInfo^.bmiHeader.biBitCount)
* SizeOf(TRGBQuad));
end;
StretchDIBits(Canvas.Handle,
X, Y, W, H,
0, 0,
BitmapInfo^.bmiHeader.biWidth,
BitmapInfo^.bmiHeader.biHeight,
Origin, BitmapInfo^, DIB_RGB_COLORS, SRCCOPY);
Result := True;
finally
GlobalUnlock(DataHandle)
end;
end;
end;
finally
CloseClipboard;
end;
end;
(I modified this function after pasting it here, I hope it's ok)
| Quote: | 2) The TPicture.Bitmap property is a *very* misleading property. Intuitively
one would think that it returns the picture's data as a bitmap. After all,
every picture of whatever type must eventually be rendered as a bitmap
|
No, metafiles can be drawn using lines and other vector routines. There
is no need for a bitmap to render certain types of graphics.
--
Jens Gruschel
http://www.pegtop.net |
|
| Back to top |
|
 |
Enquiring Mind Guest
|
Posted: Fri May 18, 2007 5:03 pm Post subject: Re: Handling palettes in copying pictures to and from clipbo |
|
|
"Jens Gruschel" <nospam (AT) nospam (DOT) nospam> wrote in message
news:464d48a0$1 (AT) newsgroups (DOT) borland.com...
| Quote: | I don't know whether this will help you, but in case a CF_BITMAP is
available in the clipboard there's *always* a CF_DIB available, which I
prefer for pasting (however it might depend what exactly you need). With
following code you should be able to paste *any* bitmap (with or without
color palette) to *any* canvas, possibly with better quality compared to
using a device dependent bitmap:
(I modified this function after pasting it here, I hope it's ok)
Many thanks for the code. Very useful! I was able to compile it after |
removing a surplus right bracket in the line
H := Abs(BitmapInfo^.bmiHeader.biHeight));
It makes extensive use of low level Windows API functions. I am endeavouring
to use higher level Delphi classes where possible. A TPicture picture can be
copied to the clipboard using the method
procedure TPicture.SaveToClipboardFormat(var AFormat: Word; var AData:
THandle; var APalette: HPALETTE);
I presume the APalette output parameter is the Windows handle of the
picture's palette, if it has one. It seems unnecessary, because the palette
of the TPicture object can be retrieved from the object itself.
A picture once on the clipboard can be pasted using the method
procedure TPicture.LoadFromClipboardFormat(AFormat: Word; AData: THandle;
APalette: HPALETTE);
In this case I am not sure what value should be assigned to the APalette
input parameter, because I would have thought that the palette data should
have been copied to the clipboard in the SaveToClipboardFormat, and should
therefore be an output of the method, not an input (i.e. a var parameter). I
would also have imagined that SaveToClipboardFormat should have copied the
palette data of the original picture to the clipboard (rather than a
handle), so that LoadFromClipboardFormat would need to create a new Palette
object, load its data from the clipboard, and finally return a reference to
it.
If an APalette value is input to LoadFromClipboardFormat that is different
to that of the bitmap that was copied to the clipboard, what is the result?
Conversely, if LoadFromClipboardFormat is called with APalette equal to
zero, what palette does the pasted bitmap use? In a test case, I inspected
the value of a pasted bitmap's palette and found it to be negative. I
presume this means that it hasn't been assigned, and that Delphi/Windows
uses the Windows default palette.
Regards,
Enquiring Mind |
|
| Back to top |
|
 |
Jens Gruschel Guest
|
Posted: Sat May 19, 2007 5:17 pm Post subject: Re: Handling palettes in copying pictures to and from clipbo |
|
|
| Quote: | Many thanks for the code. Very useful! I was able to compile it after
removing a surplus right bracket in the line
H := Abs(BitmapInfo^.bmiHeader.biHeight));
|
Right. I forgot to remove it. My original code does not set W and H but
passes both values to a method (I'm using this code within my own bitmap
class).
| Quote: | procedure TPicture.SaveToClipboardFormat(var AFormat: Word; var AData:
THandle; var APalette: HPALETTE);
I presume the APalette output parameter is the Windows handle of the
picture's palette, if it has one. It seems unnecessary, because the palette
of the TPicture object can be retrieved from the object itself.
|
I've looked at the source code and don't really understand why the
palette is returned. TGraphic.Palette should return the same (at least
for TBitmap).
| Quote: | procedure TPicture.LoadFromClipboardFormat(AFormat: Word; AData: THandle;
APalette: HPALETTE);
In this case I am not sure what value should be assigned to the APalette
input parameter, because I would have thought that the palette data should
have been copied to the clipboard in the SaveToClipboardFormat, and should
therefore be an output of the method, not an input (i.e. a var parameter).
|
Looks like you can force the bitmap to use a new palette when loading it
from the clipboard. I guess that was useful in the days where 256 color
mode was used and different applications were using different color
palettes. Maybe someone else knows better...?
At http://msdn2.microsoft.com/en-us/library/ms649013.aspx you can find
some more information. Microsoft recommends using CF_DIB instead of
CF_BITMAP (at the very bottom of the page). AFAIK Windows 3.11 did not
support CF_DIB, so maybe Delphi is using CF_BITMAP for compatibility
reasons (I guess parts of the Graphics.pas code were written before
Windows 95 was released).
--
Jens Gruschel
http://www.pegtop.net |
|
| Back to top |
|
 |
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
|