Image1.Picture.LoadFromFile('c:\windows\help.ico'); Image1.Picture.LoadFromFile('c:\windows\Bubbles.bmp'); Image1.Picture.LoadFromFile('c:\QUAKE2\q2.ico'); // this failsIt also won't display some metafiles.
Getting Rid of Flicker
procedure TForm1.FormCreate(Sender: TObject); begin Form1.DoubleBuffered := true; end;For other solutions, see GDI Graphics In Delphi - Four Ways To Kill Flicker and Picture Motion--Avoid The Flicker .
The Clipboard
Instead, you should use TClipboard.Assign and TPicture.Assign which do use the TPicture clipboard commands.
Clipboard.Assign(Image1.Picture); Clipboard.Assign(SpeedButton1.Glyph); // Glyph is a TBitmap Image1.Picture.Assign(Clipboard);For these to work, be sure to add clipbrd to the uses clause.
For more info, see the Clipboard section of efg's Delphi Graphics: Algorithms.
Problems
Icons
Image1.Picture.LoadFromFile('c:\windows\help.ico'); Clipboard.Assign(Image1.Picture); // This fails Clipboard.Assign(Image1.Picture.Graphic); // and this fails
Monochrome Bitmaps
Image1.Picture.LoadFromFile('c:\windows\Bubbles.bmp'); Clipboard.Assign(Image1.Picture); // This works fine Image1.Picture.LoadFromFile('c:\windows\Circles.bmp'); Clipboard.Assign(Image1.Picture); // This crashes the systemI was able to track this to TBitmap.SaveToClipboardFormat. It also "appears" to only be a problem with bmp files which have a 2-color (monochrome) palette, those with
On a separate system (running Windows 95), I got "Invalid clipboard format" the first time this code executed. Afterwards, it simply copied a null graphic to the clipboard. (Note: Windows 98 crashes, Windows 95 simply reports an error.)
On the Windows 98 system, Circles.bmp also causes the Microsoft Paint program to crash if you select Edit / Select All.
Using Windows XP, it sort of works. Circles.bmp is monochrome - Dark Blue and Light Blue - but when you paste it, it is black and white. (Well, at least it doesn't crash.) Of course, this means that Delphi is ok, it is just another Windows design problem that users will blame on you and your code.
Fix
Image1.Picture.LoadFromFile('c:\windows\Circles.bmp'); if Image1.Picture.Graphic is TBitmap then if not Image1.Picture.Bitmap.Monochrome then Clipboard.Assign(Image1.Picture.Graphic) else Clipboard.Clear else if Image1.Picture.Graphic is TIcon then Clipboard.Clear else // Metafile, jpeg, ... Clipboard.Assign(Image1.Picture.Graphic); if Clipboard.HasFormat(CF_PICTURE) then Image2.Picture.Assign(Clipboard); end;
JPEG
Modifying and/or creating JPEG images is a bit more complex and is covered in the separate JPEG Image page.
PNG
Locating, installing, and using this code is covered in the separate PNG Image page.
GIF
Uses ..., axctrls; procedure TForm1.Open1Click(Sender: TObject); var f : TFileStream; graphic : TOleGraphic; begin if OpenDialog1.Execute then begin graphic := TOleGraphic.Create; f := TFileStream.Create (OpenDialog1.FileName, fmOpenRead or fmShareDenyNone); try graphic.LoadFromStream(f); Image1.Picture.Assign(graphic); finally f.Free end end end;Also see Torry's Delphi GIF Pages. TGIFImage (By Anders Melander) Integrates with TPicture to add GIF support to the TImage. (Full source code is provided.)
TIFF
To create a tiff file, just copy Bmp2tiff.pas to your program directory and call it via something like this
// Save Image as TIFF in the same path with extension '.TIF' WriteTiffToFile( ChangeFileExt(OpenDialog1.FileName, '.TIF'), Image1.Picture.Bitmap );
Related Commands
SavePictureDialog1.DefaultExt := GraphicExtension(TBitmap); SavePictureDialog1.Filter := GraphicFilter(TBitmap); if SavePictureDialog1.Execute thenThese work on colors
ColorToRGB ColorToString StringToColorThere are 42 "named" colors.
Reading Images
I have an application that displays MRI images that are stored as simple data files (2 bytes per pixel).
The following code requires about 3 seconds to load each image
var // F: File; // Defined as a global variable S: string; i, j: integer; buff: array [0..131072] of char; tc:TColor; begin AssignFile(F, 'downloads\bruce_June5.txt'); Reset(F, 2); BlockRead(F, ImageArray[0, 0], 256*256); Image1.Picture.Bitmap.Height := 256; Image1.Picture.Bitmap.Width := 256; for i := 0 to 255 do for j := 0 to 255 do begin tc := ImageArray[i, j]; Image1.Picture.Bitmap.Canvas.Pixels[i, j] := tc; end; Image1.Canvas.Refresh; // CloseFile(F); end;By first writing to a non-displayed bitmap, this code requires only 1 second per bitmap.
var // F: File; S: string; i, j, k: integer; buff: array [0..131072] of char; tc:TColor; Bitmap: TBitmap; begin AssignFile(F, 'downloads\bruce_June5.txt'); Reset(F, 2); ImageList1.CreateSize(256, 256); for k := 0 to 155 do begin // 155 Bitmap := TBitmap.Create; Bitmap.Height := 256; Bitmap.Width := 256; BlockRead(F, ImageArray[0, 0], 256*256); for i := 0 to 255 do for j := 0 to 255 do begin tc := ImageArray[i, j]; Bitmap.Canvas.Pixels[i, j] := tc; end; ImageList1.Add(Bitmap, nil); end; Image1.Picture.Bitmap := Bitmap; // This just indicates that the load is done end;
Reading an *.bmp file is very fast (about 6 seconds for 155 images),
however, the images are arranged
as
var hb: HBITMAP; Stream: TFileStream; begin Stream := TFileStream.Create('xxyy.bmp',fmOpenRead ); Imagelist1.Height := 256; Imagelist1.Width := 256; Imagelist1.Clear; bm.LoadFromStream(Stream); Imagelist1.Add(bm, nil); // bm.ReleaseHandle; Stream.Destroy; Image1.Picture.Bitmap := bm; // This just indicates that the load is done end;
The problem is with how the *.bmp file was created - I loaded the 156 images into an imagelist (but only the first 74 are available using Windows 98, they are all read using Windows XP - same *.exe) and then saved them to the *.bmp file using this code.
var hb: HBITMAP; Stream: TFileStream; bm: TBitmap; begin Stream := TFileStream.Create('xxyy.bmp',fmCreate ); bm := TBitmap.Create; hb := Imagelist1.GetImageBitmap ; bm.Width := 256*100; // This is part of the debug bm.Handle := hb; bm.SaveToStream (Stream); bm.ReleaseHandle; Stream.Destroy; end;The Delphi 5 help says
All images in an image list are contained in a single, wide bitmap in screen device format.but, apparently, that is not true.
I tried setting the width of the bitmap object before assigning the handle - no effect. Either way, after
Presumably, this is controlled by the AllocBy parameter.
Changing Images via a TMemoryStream
It only takes a second to read the entire 20M file into an array. Reformatting it into a second array with RGB planes takes 20 seconds. I assume that the Trunc command is taking too long. I have experimented with several commands that convert an integer to a byte (trunc, dividing by 256, and shr which is the fastest).
Tbitmap writes to a stream as 3 bytes (RGB) per pixel.
This code replaces one image with another (it is actually fairly fast).
If you don't set PixelFormat to
var bm2: TBitmap; st: TMemoryStream; begin bm2:= TBitmap.Create; bm2.PixelFormat := pf24bit; bm2.Width := 256 ; bm2.Height := 256 ; st := TMemoryStream.Create; bm2.SaveToStream (st); st.Seek( - (256*256*3), soFromEnd); st.WriteBuffer( ColorArray[ScrollPos,0,0,1] , 256*256*3); st.Seek(0, soFromBeginning ); bm2.LoadFromStream(st); Image1.Picture.Bitmap := bm2; bm2.Destroy ; st.Destroy; end;Notice how the bits are arranged
ColorArray[ScrollPos,0,0,1] | | | | | - color plane - 1-blue 2-green 3-red | --- x 0 is left of bitmap, Increases toward right ----- y 0 is bottom of bitmap, Increases toward top
Using Scanlines[] to Edit a Canvas
Keeping the Same Aspect Ratio
Reference: efg's Computer Lab
Undocumented Data
PRGBTriple = ^TRGBTriple; {$EXTERNALSYM tagRGBTRIPLE} tagRGBTRIPLE = packed record rgbtBlue: Byte; rgbtGreen: Byte; rgbtRed: Byte; end; TRGBTriple = tagRGBTRIPLE; {$EXTERNALSYM RGBTRIPLE} RGBTRIPLE = tagRGBTRIPLE; PRGBQuad = ^TRGBQuad; {$EXTERNALSYM tagRGBQUAD} tagRGBQUAD = packed record rgbBlue: Byte; rgbGreen: Byte; rgbRed: Byte; rgbReserved: Byte; end; TRGBQuad = tagRGBQUAD; {$EXTERNALSYM RGBQUAD} RGBQUAD = tagRGBQUAD;If a bitmap's height is positive, then 0,0 is at the bottom left corner of the image; if it is negative, then it is at the top left corner of the image. (or vise versa - its not documented in the help, I found this in TBitmap.GetScanLine in graphics.pas)
Also useful and undocumented in the Delphi 5 help file