| Magnifying Glass | Lab Report | |||
|
Chinese Translation by Hector Xiang |
||||
Purpose
The purpose of the Magnifier program is to
demonstrate how to create a digital magnifying glass to enlarge a small selected area of
an image.
An alternate and better solution using TPicture/TGraphic polymorphism by Anders Melander will also be discussed.
Materials and Equipment
Software Requirements
Windows 95/98
Delphi 3 or 4 (to recompile)
Magnifier.EXE
GIF Support: Requires Anders Melander's TGIFImage (from www.melander.dk/delphi/gifimage) and a "GIF" conditional to be set before compilation.Hardware Requirements
Super VGA display with 800-by-600 screen in high/true color display mode
Procedure (see discussion comments below before using Anders Melander's Magnifier)
Discussion
Anders Melander suggested a better approach to my original program. I'm
showing both his solution and mine since I think both can be educational.
In my program, GIF support is added during FormCreate to the OpenPictureDialog Filter with the following:
| {$IFDEF GIF} s := OpenPictureDialog.Filter + '|GIFs (*.gif)|*.gif'; Insert('*.gif;',s, POS('(',s)+1); // Put GIF in "All" selection Insert('*.gif;',s, POS('|',s)+1); OpenPictureDialog.Filter := s; {$ENDIF} |
Anders' program has a better approach in its FormCreate:
| OpenPictureDialog.Filter := GraphicFilter(TGraphic) |
The Load button in the program is used to read various file graphics file formats. The ButtonLoadImageClick method is used to read any of the supported file formats by calling a function LoadGraphicsFile, which is defined in the GraphicsConversionLibrary unit.
Unfortuantely, the in-memory TBitmap does not directly support any file format except BMP. This means that to manipulate pixels, most file formats must be first converted to a TBitmap. In my version of LoadGraphicsFile, I used a brute force approach to perform all these conversions. Anders' approach is much shorter using the polymorphism of TGraphic/TPicture:
| // Create TBitmap from BMP, JPG, WMF, EMF, GIF or any other // format supported by TGraphic. // [anme] Completely rewritten to use TPicture/TGraphic polymorphism FUNCTION LoadGraphicsFile(CONST Filename: STRING; CONST ForcePf24bit: BOOLEAN): TBitmap; VAR Image: TPicture; BEGIN Result := nil; if FileExists(Filename) then begin Result := TBitmap.Create; try Image := TPicture.Create; try // Load image - let TPicture worry about the image type Image.LoadFromFile(Filename); // Try converting image to bitmap try Result.Assign(Image.Graphic); except // Image didn't support conversion to TBitmap // Draw image on bitmap instead Result.Width := Image.Graphic.Width; Result.Height := Image.Graphic.Height; Result.PixelFormat := pf24bit; Result.Canvas.Draw(0, 0, Image.Graphic); end; finally Image.Free; end; except Result.Free; raise; end; // The following appears to be needed for the circular magnifier for some // (not all) GIF files and for pf8bit TBitmaps. Some GIF files // do not appear correctly without this, and pf8bit bitmaps do not // have "real time" updates without forcing this. This may not be // desirable in other programs. // This is an experimental "fix" -- I wish I knew why this is necessary. // efg, 21 Feb 99 IF ForcePf24bit AND (RESULT.PixelFormat <> pf24bit) THEN Result.PixelFormat := pf24bit end END {LoadGraphicFile}; |
I added the ForcePf24bit parameter to Anders' routine -- this will be discussed below.
Look at the TMagnifier.ShowMagnifier method for details of how the "magnification process" works. A brief outline appears here.
The diameter of the magnified area times the magnification factor is a constant. For example, if the area of interest had a diameter of 30 pixels at a 2X magnification (30 *2 = 60), only 15 pixels would be selected at a 4X magnification (15 * 4 = 60). With no magnification (1X) this would be a 60-pixel diameter area (60 * 1 = 60). The calculation of a diameter is limited by the integer arithmetic of pixel sizes.
An in-memory Bitmap is read as part of the OpenPictureDialog.Execute processing in the ButtonLoadImageClick method. To avoid working with a variety of pixel formats and palettes, all the files read by LoadGraphicsFile (in the GraphicsConversionsLibrary.PAS unit) are converted to a pf24bit pixel format. This means that correct colors are only displayed when using a high color or true color display mode.
For the square magnifier, the original in-memory bitmap is first copied to a new ModifiedBitmap. A small square area of the original in-memory Bitmap, around the point (X,Y) from the MouseDown or MouseMove event, is stretched onto a larger area of the ModifiedBitmap. The ModifiedBitmap is then displayed in the ImageOnForm TImage.
Extra work is needed for the circular magnifier. A new ModifiedBitmap is first copied from the in-memory Bitmap as described above. A CircularMask bitmap is created with a solid black background and a filled-in white circle of appropriate size. CopyRect with CopyMode := cmSrcAnd converts the CircularMask to have an enlarged circular area from the original image with a black area around this circle.
An "And" of a black = 0 (for R, G, and B) area from the mask results in a black area. An "And" of a white=255=$FF (for R, G, and B) area from the mask selects a circular area of interest from the in-memory Bitmap. Setting CircularMask.Transparent := TRUE allows a Draw method to overlay the circular area transparently on the ModifiedBitmap. As with the square magnifier, the ModifiedBitmap is displayed in the ImageOnForm TImage.
When the Stretch checkbox is checked, the border line around the magnified area may be missing, or partially missing.
The "Square" or "Circle" magnifying glass will usually become a "Rectangle" or "Ellipse" when the Stretch checkbox is checked.
The circular magnifying glass has a peculiar behavior when operated in 256-color mode -- as noted above the program is only recommended for high or true color display modes. When using the 8-bits/pixel Deer test picture, many of the pixels are white. In all cases, the colors are not "correct" in 256-color mode since palettes are ignored.
An unexpected change was needed in Delphi 4 in ImageOnFormMouseMove to avoid a Range Check Error at runtime:
| TargetColor := Bitmap.Canvas.Pixels[xActual, YActual]; IF TargetColor = -1 THEN ... |
This check is needed in Delphi 4 to avoid a Range Check Error in GetRValue, etc. These functions (GetRValue, etc.) take a DWORD argument, but DWORD has a different definition in D3 and D4. In D3 a DWORD is an Integer, but in D4 a DWORD is a LongWord. A value of -1 now creates a Range Check Error in D4, which is returned by Pixels above when the property is undefined like when a TBitmap is created without any assignment to Width or Height.
Overall, Anders' approach was much better than mine, but in an initial test I observed two problems. In one case the circular magnifier didn't work correctly for certain GIFs (like the KSFlag.GIF that can be downloaded below) -- but not all GIFs. For certain images, especially pf8bit images (like the Deer image that can be downloaded below), Anders' magnifier was sluggish. Both of these probems were solved by the ForcePf24bit flag described above. I'm not sure why this "solves" the problem since I won't claim to understand the "cause" of the problems.
Here's what the problem looked liked with certain GIF files:
| Notes: 1. "Force pf24bit" not checked when KSFlag.GIF was loaded 2. "Circle" shape checked 3. "Stretch" Checked 4. Radius = 10 pixels 5. Click (or press and drag) near (X,Y) = (21,20). 6. Instead of circular magnifier, a sluggish, black rectangle is seen. |
|
![]() |
The "Force pf24bit" option solved the problem:
| Notes: 1. "Force pf24bit" was checked when KSFlag.GIF was loaded 2. "Circle" shape checked 3. "Stretch" Checked 4. Radius = 10 pixels 5. Click (or press and drag) near (X,Y) = (21,20). 6. The stretched circular magnifier is very snappy. |
![]() |
Similar Project
Magnify by Fei Hongbin
Conclusions
Creating the circular magnifier is a good exercise in using a bitmap mask
to copy a selected area of a bitmap and then drawing it on another bitmap using
transparency.
Related
Non-Linear Magnification Home Page
www.cs.indiana.edu/hyplan/tkeahey/research/nlm/nlm.html
Dr. Chris Rorden's Kylix magnifier example
www.psychology.nottingham.ac.uk/staff/cr1/kylix.html
Keywords
CopyMode, cmSrcCopy, cmSrcAnd, Bitmap Mask, CopyRect, Transparent, Draw, Ellipse,
Rectangle, Invalidate, GetPixelFormatString, IsPaletteDevice, LoadGraphicsFile,
Magnification Factor, BMP, JPG, WMF, EMF, GIF, WmEraseBkgnd, TIniFile, MulDiv,
OpenPictureDialog, TPicture/TGraphic polymorphism
Download
Delphi 3/4 Source and EXE (229 KB): Magnifier.ZIP
Delphi 3/4 Source Code with Anders Melanders' better solution (9 KB): AndersMelanderMagnifier.ZIP
Mandrill test image: Mandrill.ZIP (pf24bit
bitmap)
KS Flag test image: KSFlag.GIF
Deer test image: Deer.ZIP (pf8bit bitmap)
Compiles without any changes in D4.
Updated 10 Jun 2003
since 2 Jan 1999