Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

JVM Languages

Java Q&A


October 1996: Java Q & A

How Do I Display Media Formats that Netscape Does Not Support?

Cliff Berg

Cliff, vice president of technology of Digital Focus, can be contacted at [email protected]. To submit questions, check out the Java Developer FAQ Web site at http://www.digitalfocus.com/faq/.


GIF and JPEG images are found everywhere on the World Wide Web. Unfortunately, images are often in other formats, and conversion between formats is always risky, unless you're practiced at it. It would be nice if there were a way to display other kinds of images. It would be nicer still if there were a general way to employ all sorts of media on your web site. Netscape and Microsoft provide this in their respective plug-in and ActiveX technologies, but these are proprietary and require users to download and install these components. It would be better if there were a more-general, nonproprietary way. There is-using Java.

To enable a web-site visitor to use media formats of your choice, all you need to do is either write or obtain viewer classes for those media, and write a Java applet to instantiate and use the classes. Any Java-capable web browser will know how to download and install the Java classes.

With Sun's HotJava browser, these media classes can be used in a content handler. HotJava knows how to find the classes it needs automatically and install the right viewer for a given media type-you do not have to write an applet "wrapper." With HotJava, you can write HTML pages that contain hyperlinks to new kinds of media. HotJava will make the association between the media and the content handler that knows how to view the media. Netscape won't do this because it does not support Java content handlers. However, this is not a great disadvantage: A simple workaround is to instantiate a viewer on the HTML page in the form of a Java applet. The applet acts as a "wrapper" for the media-viewer classes.

This works nicely because the HTML applet tag has a nearly identical syntax to the image tag, so you can resize it and position it in much the same way you would an image. For example, if the applet is a viewer for a Windows bitmap image, then you can, in effect, place .BMP images on the web page. The actual file name of the .BMP image can be specified as an applet parameter.

You can extend this technique to any kind of media. An associate of mine has written an MPEG viewer in Java, which can be placed anywhere on a web page (or in a separate Java frame, so that it occupies its own resizable window). Since lots of people have images in the .BMP format, I will create a Windows 3.x bitmap image .BMP viewer.

BMP Image Files

There are many different kinds of bitmap file formats. These include X Windows bitmap files, as well as several versions of the .BMP format. Here I'll consider the Windows 3.x version. Note that a web-site visitor with Windows 95 or NT (or a Sun workstation, Mac, or any brand of computer for that matter) will be able to view a Windows 3.x bitmap file in any Java-capable browser-even though that particular bitmap format may be incompatible with their operating system.

The Windows 3 bitmap file contains four sections: file header, bitmap header, color palette (optional), and an array of actual image data. The bitmap header contains most of the information about the image format and parameters; see Figure 1.

For more information on the .BMP format, see "The BMP File Format, Parts 1 and 2," by David Charlap (DDJ, March and April 1995), or Encyclopedia of Graphics File Formats, Second Edition, by James Murray and William vanRyper (O'Reilly & Associates, 1996).

Big-Endian versus Little-Endian

The Java virtual machine (JVM) defines an instruction set for a virtual computer, as well as a format for the primitive data types used by that computer: int, short, byte, and so on. Since a short is made up of two bytes, the machine must define which of those two bytes occupies a higher address in memory: the byte containing the low-order bits, or the one with the high-order bits, when the entire short is viewed as a number. Since the JVM stores high-order bits first, it is Big-endian; Intel machines are Little-endian.

Since .BMP is a Windows format, the data it contains is also Little-endian. This means that you have to invert the ordering of bytes in multibyte numeric quantities in order for the Java client to be able to interpret them.

The SwappedDataInputStream class provides this service (see Listing One). SwappedDataInputStream extends the InputStream abstract class and acts as a filter on data coming from any other InputStream. The constructor takes an InputStream as a parameter-this can be a FileInputStream or an input stream obtained via URL.openStream(). SwappedDataInputStream adds the methods readShort() and readInt() to InputStream. There are methods of the same name in the AWT DataInputStream class, but they have no relationship to the ones in DataInputStream other than their name and the fact that they return the same data type. These are the filtering methods that perform the byte-order reversal that we require. SwappedDataInputStream also adds a file-position-counter capability to the InputStream.

Data Packing

The actual image data in a .BMP image is contained in an array of scan lines, ordered from the bottom to the top of the image. Thus, the last line of the image is read first. (Haven't you ever read a book from back to front?) In addition, each line is padded with extra zero bytes, as needed, to make the scan line a multiple of four bytes.

Images can be either palette based or direct RGB encoded. A palette-based image uses a table of colors referred to as a color palette: Each pixel in the image is represented by a value that is an index into the table. This technique allows the image information to be more compact, since the number of table entries is typically fewer than the number of possible colors in the color space; hence, each pixel is represented by fewer bits. In the case of a .BMP image, a palette may contain 2, 16, or 256 colors. This translates into pixel depths of 1, 4, and 8 bits, respectively. A .BMP requiring more than 256 colors cannot use a palette, so each pixel is then represented by a 32-bit value that contains red, green, blue, and alpha components (although the alpha is undefined and not used). Contrary to convention, the RGB values are actually in the sequence blue, green, and red (BGR).

To summarize, palette-based bitmaps pack 1, 4, or 8 bits per pixel into each scan line, padded at the end with as many zero-filled bytes as are necessary to make the scan line width a multiple of 4. (In addition, if the width of the image is not divisible by the number of pixels per byte, the last byte is padded with zero bits as well.) Nonpalette-based bitmaps-those with more than 256 colors-contain one 4-byte int per pixel, for a total of 16 million colors (256256256, since the last byte is unused).

Creating the ImageProducer

The Win3BMPApplet class instantiates a Win3BMPFile object and then calls the object's read() method, supplying an input stream. The input stream may be any InputStream or derived object, including the input stream of a URLConnection, or a FileInputStream. The Win3BMPFile.read() method performs the entire reading, parsing, and unpacking of the .BMP file.

Once you have read the file and constructed a memory representation of it, you can display it by calling the makeImageSource() method, which constructs a MemoryImageSource object. The Applet method createImage() can then be used to create an image from this image source, which can be displayed with the Graphics .drawImage() method.

makeImageSource() has to do two things: select a color model, and construct a MemoryImageSource object that uses that color model for the image data. The Java AWT provides two color models that are useful in this case: IndexColorModel for palette-based images and the default RGB color model used by the AWT components themselves for direct RGB encoded images. The color model provides methods that allow components to retrieve color values for pixels and map them to the display. For example, getRed(int pixel) returns the red value of pixel; the object calling this method does not have to know if the color model looks up the value in a table or extracts it directly from the pixel data.

The MemoryImageSource class has several constructors, and the correct choice depends on the bit depth of the image data, among other things. There are only two choices for packing the data however: one byte per pixel, or one int per pixel. Thus, while you are unpacking pixel data from the .BMP file, you also must construct the byte array or int array data structure that will be needed by the MemoryImageSource constructor you intend to use. That is why Win3BMPFile has two unpack methods: one for creating a byte array, and one for creating an int array.

Instantiating the Applet

Once created, the applet can be used like an HTML image. In Example 1, the URL of the actual .BMP file is provided as a parameter, allowing a web-page designer to use the applet over and over again for different images. Netscape will fetch the Java classes once and store them in its cache, and its Java class loader won't attempt to fetch the same classes multiple times on the same page.

You could extend the applet by creating other kinds of image viewers to handle arbitrary kinds of images or by catching the exception thrown when the image format is not recognized. The applet could try one viewer, and if an exception is thrown, try another, until it finds one that can display the image. If no viewer can recognize the format, then no image would be displayed, and an exception message would be printed on the Java console.

A further enhancement could provide visual feedback to the user on the progress of the image loading and construction, or even provide a rolodex of images. The applet thus becomes a window into the object's visual behavior, which can be as dynamic as you like.

Figure 2 shows two instances of the applet displaying different images on a Netscape page. The complete source code is available electronically and at http://www.digitalfocus.com/ddj/code).

Summary

Java brings openness and extensibility a browser without the need for plug-ins or built-in capability to handle every kind of object that users might want to view. Objects of any kind can be placed on a web site with appropriate wrappers to provide the behavior of those objects for viewing or manipulating. This model can be extended not just to media objects, but to data objects of all kinds. An applet that displays a .BMP image in Netscape is a small demonstration of a powerful capability: the ability to distribute native objects on the Web along with a nonproprietary and platform-independent way of viewing and manipulating those objects, no matter where or how they were created.

Example 1: An Applet can be used like an HTML image.

<applet code="Win3BMPApplet.class"
    width=...the width of the image...
    height=...the height of the image... >
 <param name="href" value="...the URL of the image...">
</applet>

Figure 1: Basic structure of a .BMP.

Figure 2: The Applet.

Listing One

/* SwappedDataInputStream.java */

import java.io.*;

/** Provide byte-order-reversed input of bytes, int's, and short's. Allows for
 * proper reading of those numeric values from a "little-endian" source. */

public class SwappedDataInputStream extends InputStream
{
    InputStream is;
    long pos = 0;

    public SwappedDataInputStream(InputStream is)
    {
        this.is = is;
    }
    public long curPos()
    {
        return pos;
    }
    public int read()
    throws
        IOException
    {
        int n = is.read();
        if (n < 0) return -1;
        pos++;
        return n;
    }
    public int read(byte[] b)
    throws
        IOException
    {
        return read(b, 0, b.length);
    }
    public int read(byte[] b, int offset, int len)
    throws
        IOException
    {
        int n = is.read(b, offset, len);
        if (n < 0) return -1;
        pos += n;
        return n;
    }
    public short readShort()
    throws
        IOException
    {
        byte[] b = {0, 0};
        short mask = 0xff;
        short s = 0;

        int n = read(b, 0, 2);
        if (n < 2) throw new IOException();

        // Swap the bytes
        s |= ((short)(b[1])) << 8;
        s |= (((short)(b[0])) & mask);

        return s;
    }
    public int readInt()
    throws
        IOException
    {
        byte[] b = {0, 0, 0, 0};
        short mask = 0xff;
        int i = 0;

        int n = read(b, 0, 4);
        if (n < 4) throw new IOException();

        // Swap the bytes
        i |= ((((short)(b[3])) & mask) << 24);
        i |= ((((short)(b[2])) & mask) << 16);
        i |= ((((short)(b[1])) & mask) << 8);
        i |= (((short)(b[0])) & mask);

        return i;
    }
    // We don't use these, but they must be overridden to complete class.
    // The reason is this class is merely a wrapper for the InputStream
    // object that it uses.

    public int available()
    throws
        IOException
    {
        return is.available();
    }
    public void close()
    throws
        IOException
    {
        is.close();
    }
    public void mark(int readlimit)
    {
        is.mark(readlimit);
    }
    public boolean markSupported()
    {
        return is.markSupported();
    }
    public void reset()
    throws
        IOException
    {
        is.reset();
        pos = 0;
    }
    public long skip(long n)
    throws
        IOException
    {
        long m = is.skip(n);
        pos += m;
        return m;
    }
}


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.