<* PRAGMA LL *>An
Image.T is a screen-independent specification of an {\it
   image}.  An image is a pixmap that includes specifications for
   both color and resolution. It is rendered consistently across
   screen types in terms of its colors and size. 
INTERFACEAnImage ; IMPORT Pixmap, Rd, ScrnPixmap, Thread, TrestleComm, VBT, Word, Wr; TYPE T = Pixmap.T; EXCEPTION Error;
Image.Raw is an array of pixels, with both resolution and
   color information.  It is like a Pixmap.Raw, with the
   addition of resolution and color information. An Image.T is
   built from an Image.Raw pixmap using procedures in this interface.
   There are three types of Image.Raw pixmaps: The RawBitmap
   represents bitmaps (1-bit deep pixmaps); the RawPixmap
   represents pixmaps that do not have a color table; and the
   RawPixmapCMap represents pixmaps that use a color table. 
TYPE
  Pixel = Word.T;
  Mode = {Stable, Normal, Accurate};
  RGB = RECORD r, g, b: REAL END;
TYPE
  Raw = OBJECT
      width, height: INTEGER;
      xres, yres: REAL := 75.0; (* res at which raw image was *constructed*, in pixels per inch (dpi) *)
    METHODS
      get (h, v: INTEGER): Pixel;
      set (h, v: INTEGER; pixel: Pixel);
    END;
  RawBitmap = Raw BRANDED OBJECT END;
  RawPixmap = Raw OBJECT
      needsGamma := TRUE;
      colorMode  := Mode.Normal;
    END;
  RawPixmapCMap = RawPixmap OBJECT
      colors: REF ARRAY OF RGB;
    END;
 If pm is a Raw pixmap, then pm contains pm.height
   rows, and each row contains pm.width elements.  These fields
   are read-only after they are initialized.  The pixels are
   accessed with (0,0) in the northwest corner and
   (width-1,height-1) in the southeast corner.  The pm.xres
   and pm.yres fields specify the resolution at which pm was
   designed. The get and set methods retrieve and store
   individual elements of the pixmap.
   Each subtype of Raw can interpret a ``pixel'' in whatever way it
   chooses. The three subtypes defined here do the following:
\begin{itemize}
   \item If pm is a RawBitmap pixmap, then it is guaranteed that
   the method pm.get will return a 0 or 1.  In the call
   pm.set(h,v,pixel), only the least significant bit of pixel
   is used.
   \item
   If pm is a RawPixmap, the pixels in pm encode an RGB
   value each of whose components is 8 bits.  An (r,g,b) triple
   is stored as
       r * 256 * 256 + g * 256 + b
   and each of r, g, and b is between 0 and 255.  The field
   pm.needsGamma indicates whether to let Trestle gamma-correct
   the colors.  The pm.colorMode field determines how each RGB
   value in the pixmap should be displayed on color-mapped
   display.
   \item
   If pm is a RawPixmapCMap, the pixels in pm are used as
   an index into the color table stored in the field
   pm.colors.
\end{itemize}
   The colors used to display a colored pixmap pm depends
   on a number of factors. The pm.colorMode field is
   used to match colors in
   the pixmap with colors in the color table, as described in the
   ScrnPixmap interface.  The matching
   depends on other applications running, on other pixmaps
   being displayed, and on the depth of the screen.
The current implementation does not perform any dithering, except on monochrome screens. On monochrome screen, a very crude ``thresholding'' is performed: if the brightness of the color is more than 50\% of the maximum brightness, the screen's foreground color is used. Otherwise, the screen's background color is used.
\subsubsection{Retrieving and storing ``raw'' pixmaps}
   An Image.Raw can be built from a reader containing an image in
   Jef Poskanzer's ``portable anymap file'' (``pnm'') format, and a
   ``pnm'' description of an Image.Raw can be stored into a writer.
There are many tools available in the public domain for manipulating images in ``pnm'' format and for converting between that format and other formats (e.g., GIF, X11, Macintosh PICT, HP PaintJet, and so on).
There are three types of ``pnm'' files: \begin{itemize}
\item ``pbm'' -- portable bitmap file
\item ``pgm'' -- portable graymap file
\item ``ppm'' -- portable pixmap file
\end{itemize} Each of these format has two variants: ``raw'' and ``ASCII.'' In the ``ASCII'' version, pixel values are stored as ASCII decimal numbers. In the ``raw'' version, pixel values must be less than 256 and are stored as plain bytes.
PROCEDURE FromRd (rd: Rd.T): Raw
  RAISES {Thread.Alerted, Rd.Failure, Error};
<* LL = arbitrary *>
Returns anImage.Rawfrom the readerrdcontaining an image in ``pnm'' format. Pixels in ``ppm'' files are normalized to 8 bits per channel and intensity values of ``pgm'' files are normalized to 8 bits.
PROCEDURE ToWr (raw: Raw; wr: Wr.T)
  RAISES {Thread.Alerted, Wr.Failure};
<* LL = arbitrary *>
Store an ASCII description ofrawinto the writerwrusing ``pnm'' format.
Procedures
FromRd and ToWr are not guaranteed to be
   idempotent because pixel values are normalized by FromRd to
   be 8 bits.  Also, the ``pnm'' format produced by ToWr is
   either ASCII ``pbm'' for subtypes of RawBitmap or ASCII
   ``ppm'' for subtypes of RawPixmap, whereas procedure
   FromRd can accept these formats as well as the ``raw''
   variants and grayscale formats (``pgm'').
   The more serious limitation of using ``pnm'' format is that
   ``pnm'' does include any information about the pixmap
   resolution or color matching.  FromRd will use the default
   resolution of a Raw and the default color parameters of a
   RawPixmap; ToWr simply ignores the resolution and color
   fields. 
\subsubsection{Creating ``raw'' pixmaps from a VBT}
   FromVBT captures the information in an arbitrary VBT into an
   Image.Raw of particular dimensions: 
PROCEDURE FromVBT(v : VBT.T; width, height: REAL): Raw
   RAISES {TrestleComm.Failure};
<* LL = VBT.mu *>
Return a screen-independentRawthat describesvwhenvis scaled to bewidthbyheightmillimeters.
The current implementation of
FromVBT will cause v to be
   redisplayed multiple times: First v is detached from its parent
   pm (unless pm = NIL). Next, v is installed in an offscreen
   Trestle window, with an appropriate ScaleFilter inserted to make
   v the correct size. A call to VBT.Capture creates a
   screen-dependent version of the offscreen window. At this point,
   v is detached from the offscreen window, and reattached to pm
   (unless pm = NIL).  Each time that v changes its parent,
   various VBT methods (reshape, rescreen, redisplay, and so on) are
   called.
   The following procedure converts a
   screen-dependent pixmap (such as that returned
   by VBT.Capture), into one that is screen-independent: 
PROCEDURE FromScrnPixmap (
    spm: ScrnPixmap.T;
    st: VBT.ScreenType): Raw RAISES {TrestleComm.Failure};
<* LL.sup <= VBT.mu *>
Returns a screen-independentRawthat describes the pixmapspmwhen displayed onst. Any field ofRawthat cannot be computed fromspmandstis given its default value. For example, theneedsGammaand thecolorModefields of pixmaps that are deeper than 1-bit.
\subsubsection{Building an image from ``raw'' pixmaps}
   The remaining procedures in this interface create an Image.T
   from an Image.Raw pixmap: 
PROCEDURE Unscaled (raw: Raw): T; <* LL.sup <= VBT.mu *>
Returns a pixmap that will display asraw. The pixels inrawwill not be scaled regardless of the screen's resolution.
For example, consider a pixmap
pm whose dimensions are 150 wide
   by 50 high.  On a 75dpi screen (a typical 1993-vintage monitor),
   the pixmap pm would appear 2 inches wide and 2/3 inches high.  On
   a high-resolution monitor of 300 dpi, pm would appear 1/2 inch
   wide and 1/6 inch high.  The pm.xres and pm.yres fields are
   ignored.
   If you want pm always to appear as 2 inches by 2/3 inches,
   regardless of the pixel density of the monitor, you'd use
   Scaled instead: 
PROCEDURE Scaled (raw: Raw): T; <* LL.sup <= VBT.mu *>
 Return a pixmap that will display as raw, scaled for the
   screen's resolution.  The horizontal and vertical dimensions
   are scaled independently. The current implementation scales pixmaps by non-negative integer amounts: horizontally by
ROUND(dpiX/pm.xres) and
   vertically by ROUND(dpiY/pm.yres), where dpiX and dpiY
   are the horizontal and vertical resolution of the screen,
   respectively, expressed in dots-per-inch.
   In the example above, suppose that pm.xres and pm.yres were
   both 75.  On a 300 dpi screen, pm would appear 2 inches wide and
   2/3 inches high.  Each pixel in pm would appear as a block of 4x4
   screen pixels.  If the screen were 250 dpi horizontally and 175 dpi
   vertically, then pm would appear $1\frac{1}{2}$ inches wide and
   $1\frac{1}{3}$ inches high.  Each pixel in pm would appear as a
   block of 3x2 screen pixels.
   Procedure ScaledN allows you to provide a collection of
   pixmaps, each at a different resolution, and scales the most
   appropriate pixmap: 
PROCEDURE ScaledN (READONLY raws: ARRAY OF Raw;
                   tolerance: REAL     := 0.25;
                   maxScale : CARDINAL := 4     ): T;
<* LL.sup <= VBT.mu *>
Return a pixmap which will scale and display pixmapraws[i], whereiis chosen so thatraws[i]has the ``most appropriate'' resolution.
Specifically,
i is chosen such to minimize the {\em scale factor}
   (the amount that a ``raw'' pixmap must be scaled) while remaining
   within the given error tolerance.
   The scale factor of pixmap pm is
       MAX (dpiX/pm.xres, dpiY/pm.yres)
   where dpiX and dpiY are the horizontal and vertical
   resolutions of the screen, respectively, expressed in
   dots-per-inch.
   For a given scale factor s, the error is
      ABS (MAX ((dpiX - MAX(s, maxScale) * pm.xres) / dpiX,
                (dpiY - MAX(s, maxScale) * pm.yres) / dpiY))
   If none of the pixmaps in the raws array satisfies the
   tolerance, then the pixmap giving the smallest error is
   chosen.
   The purpose of tolerance and maxScale is to allow the user
   control over the interpretation of ``most appropriate'' when
   chosing the pixmap.
\begin{itemize}
   \item A small tolerance ensures a small error, which can
   mean a larger scale factor.
   For example, suppose the screen has a resolution of 300 dpi and
   pixmaps that are 150 and 250 dpi.  When tolerance \verb|<| 1/6,
   then ScaledN chooses the 150 dpi pixmap with a scale factor equal
   to 2, rather than the 250 dpi pixmap with a scale factor equal to 1.
   \item A small maxScale makes it less likely that a very
   low-resolution pixmap (which happens to give very small error)
   is chosen over a higher-resolution pixmap (which gives a
   larger error).
   For example, suppose the screen has a resolution of 300 dpi and
   pixmaps that are 50 and 200 dpi.  If tolerance \verb|>| 1/3, then
   ScaledN always chooses the 200 dpi pixmap, because the error,
   (300 - 200)/300=1/3, is within the tolerance and the scale factor
   for 200 dpi is less than the scale factor for the 50 dpi pixmap.
   However, when tolerance \verb|<| 1/3, the 50 dpi pixmap is chosen
   unless maxScale \verb|<=| 4.
\end{itemize}
PROCEDURE ForceUnScaledBehavior (useUnscaled: BOOLEAN);
If useUnscaled=TRUE, subsequent calls to Scaled() will instead operate as Unscaled(). Calling this procedure again with useUnscaled=FALSE will restore the defined behavior for subsequent calls to Scaled(). The reason for adding this procedure is that the implementation of most of FormsVBT uses Scaled(). By calling this procedure the programmer can override the Scaled() behavior to produce Unscaled() behavior, even for library components, without having to modify the source code.
END Image.