BufferedImage should take size of raster...
If I make a BufferedImage(320,240,TYPE_<ANYTHING>) then setRaster(r) where the width and the height if the raster is different from the width and height of the BufferedImage, the BufferedImage doesn't take on the width and height of the raster. It stays what it was went it was created.
For example: if the raster is created with a width of 100 and a height of 100, the bufferedImage I made above stays 320 x240 when bufferedImage.setRaster(r) s called.
This causes problems later when trying to drawImage(). To be able to draw the bufferedImage to fill the drawing area, one needs to either scale() the drawing or do complicated arithmatic to calculate the drawImage() parameters.
It would be a whole lot simpler if the BufferedImage would just take the width and height of a new raster.
Anybody have any comments? Maybe I'm just looking at my problem wrong. I want to draw a bufferedImage to fill the drawing area -- scaling the image on the fly to fit.
[994 byte] By [
kenwarnera] at [2007-11-26 17:42:53]

# 1
Couple options:
1) create your BufferedImage using the natural size of the image and use one of the overloaded Graphics.drawImage methods to automatically scale your image.
2) keep the unscaled BufferedImage in memory and regen a scaled version whenever the the size changes. This is more time-efficient than option 1 but less space-efficient.
# 2
Here's a more complete description of what I'm doing.
I'm making a viewer for large images that lets the
user pan and zoom on portions of the image.
So I take small rectangles from the image to project
onto the viewport.
Your first suggestion is what I want to avoid doing.
The aritmatic can be messy when both image and raster
are different size than the bufferedImage I use to
draw on the viewport. It would be much simpler if
there was a way to force the BufferedImage to take the
size of a new raster.
I'm doing your second suggestion more or less. I keep
the whole image in a projection class and take portions
of it for viewing. But I want to avoid making a new BufferedImage
just to fit the size of a new raster.
If there was a method that caused the bufferedImage to take the size of the raster, then I wouldn't have to create a new image each time the user zoomed.
Here's some code snippets to show what I'm doing:
In the projection method:
raster = (WritableRaster)image.getRaster();
viewRaster = (WritableRaster)image.getData(new Rectangle(viewX, viewY, viewW, viewH));
viewRaster = viewRaster.createWritableTranslatedChild(0, 0);
canvas.createNewBufferedImage(viewRaster);
canvas.setViewRaster(viewRaster);
In the Canvas class:
//If the user zooms, then I have to make a new image to the new zoomed size.
//So as the user zooms in and out, I have to make an image to fit each
//new view fit the viewport either that or go through a bunch of arithmatic to
//make sure that the image is drawn to fill the viewport
//big is not being used right now. It was being used and I haven't taken it out
//yet because I might use it in the future.
public void createNewBufferedImage(WritableRaster r)
{
bufferedImage = new BufferedImage(r.getWidth(), r.getHeight(), BufferedImage.TYPE_INT_RGB);
if(big != null)
big.dispose();
big = (Graphics2D)bufferedImage.getGraphics();
}
//I'm using BufferStrategy(2)
//If the WritableRaster r is a different size than the bufferedImage was created at,
//then there are aritmatic calculations to make the drawn image fill the viewport.
public void setViewRaster(WritableRaster r)
{
if(r != null)
{
bufferedImage.setData(r);
}
while(!bsg.drawImage(bufferedImage,0,0,thisW, thisH,this));
bs.show();
this.requestFocusInWindow();
}
# 3
> I'm making a viewer for large images that lets the
> user pan and zoom on portions of the image.
> So I take small rectangles from the image to project
> onto the viewport.
Creating a new BufferedImage has minimal overhead compared to creating a new raster. The BufferedImage is basically just a container for a ColorModel + Raster pair. If you used the method BufferedImage.getSubimage, you would get a clipped region of your original image backed by the same data as the original and using the same ColorModel reference as the original. So, like I said, minimal overhead.
> The aritmatic can be messy when both image and raster
> are different size than the bufferedImage I use to
> draw on the viewport. It would be much simpler if
> there was a way to force the BufferedImage to take
> the
> size of a new raster.
I guess I don't understand why you need to do any arithmetic at all. All image transformations and clipping can be done via the Graphics object used to paint the image. In fact, you may not need to create sub images at all for what you want to do. Example code below. You'll notice that all that you need to do to add panning/zooming is to add code somewhere outside the class that adjusts the zoom level via setZoom or the clip location via setClipLocation.
import java.awt.*;
import java.awt.image.BufferedImage;
import java.awt.geom.*;
import javax.swing.*;
public class ImagePanel extends JPanel {
private BufferedImage image;
private Point iLocation = new Point(0,0);
private double iZoom = 1;
public ImagePanel(BufferedImage image) {
this.image = image;
setOpaque(true);
}
public Point getClipLocation() {
return (Point)iLocation.clone();
}
public void setClipLocation(Point p) {
Point old = iLocation;
if (!old.equals(p)) {
iLocation = new Point(p);
firePropertyChange("clipLocation", old, iLocation);
repaint();
}
}
public double getZoom() {
return iZoom;
}
public void setZoom(double zoom) {
double old = iZoom;
if (old != zoom) {
iZoom = zoom;
firePropertyChange("zoom", old, iZoom);
repaint();
}
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (image != null) {
// create a transform in image space
AffineTransform t1 = AffineTransform.getScaleInstance(iZoom, iZoom);
AffineTransform t2 = AffineTransform.getTranslateInstance(-iLocation.x, -iLocation.y);
t1.concatenate(t2);
// transform and draw the image
Graphics2D g2 = (Graphics2D)g;
g2.drawRenderedImage(image, t1);
}
else {
String s = "No Image Available";
int sw = g.getFontMetrics().stringWidth(s);
g.drawString(s, (getWidth() - sw) / 2, getHeight() / 2);
}
}
}
# 4
Jaspre,
I tested translation and scaling. It does work but it works very slowly.
My current implementation using a child raster of the source image raster and then generating a new buffered image to contain it is 2 orders of magnitude O(2) faster. I know, hard to believe. I can update my 640X360 viewport from a larger source image in less than 10 milliseconds.
Translating and scaling take over a second -- here's why -- I think.
The source image is about 2k by 1k bytes. The scale factor is about 2.5 in most cases. I think that means that the entire image is scaled 2.5 times larger -- I think. And that take a long time. I could be wrong. And I intend my little applet to view even much larger images.
Now if their was a way to tell the graphics2d object to only scale a viewport sized recangle of the source image then translation and scaling would be a nice way to accomplish my goals. Maybe there is a way to do that but I haven't discovered it yet
So the results of my test brings me back to my original wish that there was some way to force a BufferedImage to take the size of the raster it had just received. This wouldn't have to be the only way a BufferedImage acts when getting a new raster -- but it would be handy to have it be an available tool.
But thanks for the response Jaspre. Working through the test you suggested added to my knowledge.
