The most direct way to define an image in MNG is simply to incorporate one.
There are two possibilities for this in the current draft specification: a
PNG image without the PNG signature, or the corresponding PNG-like JPEG
format, JNG (JPEG Network Graphics).
Just as with standalone PNGs, an embedded PNG must contain at least IHDR,
IDAT, and IEND chunks. It may also include PLTE, tRNS, bKGD, gAMA, cHRM, sRGB,
tEXt, iTXt, and any of the other PNG chunks we've described. The PLTE chunk
is allowed to be empty in an embedded PNG, which indicates that the global MNG
PLTE data is to be used instead.
An embedded JNG stream is exactly analogous to the PNG stream: it begins
with a JHDR chunk, includes one or more JDAT chunks containing the actual JPEG
image data, and ends with an IEND chunk. Standalone JNGs are also allowed;
they must include an 8-byte JNG signature before JHDR, with the format
that's shown in Table 12-2.
JNG Signature Bytes
||A byte with its most significant bit set (``8-bit character'')
||Carriage-return (CR) character, a.k.a. CTRL-M or ^M
||Line-feed (LF) character, a.k.a. CTRL-J or ^J
||CTRL-Z or ^Z
||Line-feed (LF) character, a.k.a. CTRL-J or ^J
JDATs simply contain JFIF-compatible JPEG data, which can be either baseline,
extended sequential, or progressive--i.e., the same format used in
practically every web site and commonly (but imprecisely) referred to as
JPEG files. The requirements on the allowed JPEG types eliminate the
less-common arithmetic and lossless JPEG variants, though the 12-bit grayscale
and 36-bit color flavors are still allowed.
To decode the JPEG image,
simply concatenate all of the JDAT data together and treat the whole as a
normal JFIF-format file stream--typically, this involves feeding the data
to the Independent JPEG Group's free libjpeg library.
In order to accommodate an alpha channel, a JNG stream may also include one
or more grayscale IDAT chunks. The JHDR chunk defines whether the image has
an alpha channel or not, and if so, what its bit depth is. Unlike PNG, which
restricts alpha channels to either 8 bits or 16 bits, a JNG alpha channel may
be any legal PNG grayscale depth: 1, 2, 4, 8, or 16 bits.
The IDATs composing the alpha channel may come before or after or be interleaved
with the JDATs to allow progressive display of an alpha-JPEG image, but no
other chunk types are allowed within the block of IDATs and JDATs.
Although incorporating complete JNGs or PNGs is conceptually the simplest
approach to defining images in a MNG stream, it is generally not the most
efficient way. MNG provides two basic alternatives that can be much better
in many cases; the first of these is delta images.
A delta image is simply a difference image; combining it with its parent
re-creates the original image, in much the same way that combining an
``up''-filtered row of pixels with the previous row results in the original,
unfiltered row. (Recall the discussion of compression filters in "Compression and Filtering".)
The difference of two arbitrary images is likely to be at least as large as
either parent image, but certain types of images may respond quite well
to differencing. For example, consider a pair of prototype images for a
web page, both containing the same background graphics and much of the
same text, but differing in small, scattered regions. Since 90% of
the image area is identical, the difference of the two will be 90% zeros,
and therefore will compress much better than either of the original images
Currently, MNG allows delta images to be encoded only in PNG format, and it
delimits them with the DHDR and IEND chunks. In addition to the delta
options for pixels given in DHDR--whether the delta applies to the main
image pixels or to the alpha channel, and whether applying the delta involves
pixel addition or merely replacement of an entire block--MNG
defines several chunks for modifying the parent image at a higher level.
Among these are the PROM chunk, for promoting the bit depth or color type
of an image, including adding an alpha channel to it; the DROP and DBYK chunks,
for dropping certain chunks, either by name alone or by both name and keyword;
and the PPLT chunk, for modifying the parent's palette (either PLTE or tRNS, or
both). The latter could be used to animate the palette of an image, for
example; cycling the colors is a popular option in some fractal programs.
PPLT could also be used to fade out an image by adding an opaque tRNS chunk
and then progressively changing the values of all entries until the image
is fully transparent.
The second and more powerful alternative to defining an image by including
its complete pixel data is object manipulation. In this mode, MNG
basically treats images as little pieces of paper that can be copied and
pasted at will. For example, a polka-dot image could be created from a single
bitmap of a circle with a transparent background, which could be copied and
pasted multiple times to create the complete, composite image. Alternatively,
tileable images of a few basic pipe fittings and elbow joints could be pasted
together in various orientations to create an image of a maze. The three
chunks used for creating or destroying images in the object sense are CLON
(``clone''), PAST (``paste''), and DISC (``discard'').
The CLON chunk is the only one necessary for the first example; it not only
copies an image object in the abstract sense, but also gives it a position
in the current frame--either as an absolute location or as an offset from
the object that was copied. In order to change the orientation of objects,
as in the maze example, the PAST chunk is required; as currently
defined, it only supports 180° rotations and mirror operations around
the x and y axes.
(90° rotations were ruled out since they
are rarely supported in hardware, and abstract images are intended to map
to hardware and platform-specific APIs as closely as possible.) PAST also
includes options to tile an object, and not only to replace the underlying
pixel data but also to composite either over or under it, assuming either
the object or underlying image includes transparency information.
Once component objects are no longer needed--for example, in the maze image
when the maze is completely drawn--the decoder can be instructed to discard
them via the DISC chunk.