<* 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 + band 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.Raw
from the readerrd
containing 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 ofraw
into the writerwr
using ``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-independentRaw
that describesv
whenv
is scaled to bewidth
byheight
millimeters.
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-independentRaw
that describes the pixmapspm
when displayed onst
. Any field ofRaw
that cannot be computed fromspm
andst
is given its default value. For example, theneedsGamma
and thecolorMode
fields 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 inraw
will 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]
, wherei
is 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.