Menu ▾ ▴

#20 BufferToImage improvements

open
nobody
None
5
2007-10-05
2007-10-05
Ken Larson
No

Suggestions from Jeremy Wood:

How would you like to proceed with the BufferToImage class?

You can leave it the way it is -- which should be functional.

I would suggest beefing it up to the following signatures:

class BufferToImage {
public BufferToImage(VideoFormat format);

/** Mainly for backwards compatibility.
* This just calls createBufferedImage().
* This is guaranteed to return a BufferedImage.
*/
public Image createImage(Buffer buffer);

/** Returns the BufferedImage type that is optimized for the
* default GraphicsConfiguration in use. This is going to be
* most useful for video playback on a particular computer.
* For example, Macs will probably return RGB or ARGB, and
* Windows will probably return BGR. Using this format
* should minimize the overhead it takes in rendering this image
* to the monitor.
*/
public static int getPreferredImageType();

/** Returns the closest BufferedImage type that matches the
* VideoFormat this object was constructed with. If no reasonable
* match can be found, this will return getPreferredImageType().
* Using this format should minimize the overhead it takes to
* transcode the image data from a Buffer to a BufferedImage.
*/
public int getVideoFormatImageType();

/** Creates a BufferedImage of type getPreferredImageType(),
* and calls loadImage() on that image.
*/
public BufferedImage createBufferedImage(Buffer buffer);

/** This copies the contents from buffer into dest.
* This is the recommended method to use, because it will
* recycle the destination image.
* dest can be of any image type, but it is recommended it be
* either getPreferredImageType() or getVideoFormatImageType().
*/
public void loadImage(Buffer buffer,BufferedImage dest);
}

Discussion

  • Ken Larson

    Ken Larson - 2007-10-05

    Logged In: YES
    user_id=911347
    Originator: YES

    My response:
    I added a basic createBufferedImage, but I'm not quite sure how to proceed with mine, as mine does not create standard BufferedImage types (they end up being CUSTOM). This is due to my use of databuffers (which you recommended against but I still haven't investigated.

     
  • Ken Larson

    Ken Larson - 2007-10-05

    Logged In: YES
    user_id=911347
    Originator: YES

    Earlier comments from Jeremy:

    It is wasteful to create a new image every time. If the goal is, for example, playback, then you only need to really create 1 image, and you can recycle that over and over. Also in one of our desktop apps here at work ( see http://www.tech4learning.com/frames/ ) we create animations and play them back for the user: we always cache images that aren't part of the leading selection to the disk, so we still try to keep very few images in memory. So... this delves into the question of how much are you willing to change the signatures. (For that matter, is it worth changing the signature to indicate it's returning a BufferedImage instead of a java.awt.Image image? Notes in the JMF code suggest the only reason the signature is for an awt.Image is because this was written pre-Java1.1.8.)

    The cost of creating an image isn't that bad, but churning through so many images eventually will result in a nasty garbage collection burp -- that's always hard to explain to end users.

    2. Also I noticed you use DataBuffers. I don't recommend this:
    http://javagraphics.blogspot.com/2007/04/managed-images.html

    I haven't tried constructing an image from a DataBuffer, but I suspect it's just as unsafe as touching the DataBuffer from an existing image.

    3. Also both at work and in my personal codebase I use a mechanism for recycling primitive arrays. For example, I can call:
    int[] myIntArray = ArrayFactory.getIntArray(w*h);

    And in the finally clause of a method I'll call:
    ArrayFactory.put(myIntArray);

    When you're dealing with hundreds of frames of data, or hundreds of image filters, etc., this really adds up to some good savings. At work we have a really complicated model that also includes timers, and it will regularly remove objects that sit around too long. My personal projects are smaller and aren't as clever.

    4. What you're doing looks like it will work, but it won't be as optimized as it could be. Here's a tough design decision:

    Macs like ARGB images. They perform notably better with them. Last I recall, Windows preferred BGR images. If you want I can run some benchmarks to get some proof of this. (Also see GraphicsConfiguration.createCompatibleImage() ).

    So in your implementation of BufferToImage you're going to make the image whatever-the-source buffer was. It might be a good idea to convert it to the platform's preferred format. But this might also depend on the intended usage of the image you're creating. If you plan on creating once and rendering a thousand times, this will be worth your trouble. If you plan on transcoding without ever rendering to the screen... well... why bother optimizing it.

    So recommendations for performance:
    1. Check to see if constructing DataBuffers resulted in unmanaged images.
    2. Provide some way for the caller to assign a destination BufferedImage to use.
    3. Provide a static call to get the preferred BufferedImage type for the default GraphicsConfiguration
    4. Provide a call in BufferToImage to get the preferred BufferedImage type of that RGBFormat
    5. Possibly consider recycling int arrays. (At the very least, would it hurt to keep one int array as a field in the BufferToImage object, and synchronize around that?)

    Also, if you want to design really robust, long-term solutions: I noticed buffer.getLength() and buffer.getOffset() aren't being consulted. That should probably change... it may not always be safe to assume those are data.length and 0.

    If we can agree on the method signatures of this class I'd be happy to work on the implementation details. And I can throw it through a rigamarole of tests to see what performs better.