Jeanette wrote:
> The rasters aren't actually interleaved since Denis is only using 1 band.

Yes, but the sample model is actually a degenerate instance of
PixelInterleavedSampleModel. Is it true to say that the sample offset has to
be between 0 and nSamples-1, or will it not bother to check?

>  Using the factory method is a good idea and is better than
> writing your own SampleModel if there is no real need for one since
> this will cause the image to fall into a custom case and that will
> render slower.

Yes. In order to make up for my misleading commute-addled outpourings
earlier, I've written a concrete implementation of the thing that Denis was
trying to achieve, slightly generalized for the number of child rasters.
It's in the attached file.

>  SampleModels don't have an offset but Rasters do.

But only via the DataBuffer's offset? I don't see any concept of offset
directly on Raster itself.

> Note the offsets variable below is the offset into the DataBuffer.

Yes, and unfortunately, the factory methods for creating *packed pixel*
rasters (e.g. true color) don't give you access to the offset. This means
the effect Denis is trying to achieve wouldn't be possible using quite the
same technique for packed pixel sample models. To show how it might be done,
I've included another example in the attachment which uses a 5-6-5 packed
sample model, and has a routine to create an offset buffer based on the
parent's one.

> Denis could call that factory method multiple times to create the
> multiple images, giving a different offset each time.

See the attached code.

Cheers,
Pete

---
Pete Cockerell
California, USA
http://www.best.com/~petec


//Title:        Raster Test
//Version:
//Copyright:    Copyright (c) 1998
//Author:       Pete Cockerell
//Company:      PeteSoft
//Description:

package RasterTest;

import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;

import com.sun.java.swing.JFrame;

public class Main extends JFrame {

  public Main() {
  }

  static final int BORDER_WIDTH = 10;
  public void paint(Graphics g) {
    Insets insets = getInsets();
    // Do the drawing in a rectangle inset BORDER_WIDTH from the edge of the window

    Rectangle bounds = new Rectangle(
      insets.left + BORDER_WIDTH,
      insets.top + BORDER_WIDTH,
      getWidth() - insets.left - insets.right - 2*BORDER_WIDTH,
      getHeight() - insets.top - insets.bottom - 2*BORDER_WIDTH);

    // Clear the background. Sorry about the flickering. Fill free
    // to use double buffering to avoid it...
    g.setColor(getBackground());
    g.fillRect(bounds.x, bounds.y, bounds.width, bounds.height);

    // Create and paint the rasters
    //paintRasters((Graphics2D)g, bounds, 8);
    paintColorRasters((Graphics2D)g, bounds, 8);
  }

  // Create a grayscale interleaved raster with an interleaving
  // level of numChildRasters. Draw it and the child rasters obtained
  // by uninterleaving the rows all within the rectangle bounds and
  // using the given Graphics2D
  void paintRasters(Graphics2D g, Rectangle bounds, int numChildRasters) {

    // Calculate the size of a single child raster. Because of the way
    // we arrange the children and the parent raster within the bounds,
    // we divide by n+1 instead of n.
    int width = bounds.width / (numChildRasters+1);
    int height = bounds.height / (numChildRasters+1);

    // The total height of the parent is the height of a child
    // times the number of children.
    int parentHeight = height*numChildRasters;

    // Create a ushort grayscale buffered image of the parent size
    BufferedImage parentImage =
      new BufferedImage(width, parentHeight, BufferedImage.TYPE_USHORT_GRAY);
    Graphics2D parentGraphics = parentImage.createGraphics();
    // Fill it with gray lines. The i'th line modulo nChildRasters
    // has a brightness proportional to i. The result is that all
    // rows of a given child have the same gray level.
    for (int y = 0; y < parentHeight; y++) {
      int childRaster = y % numChildRasters;
      int level = 255* childRaster / (numChildRasters-1);
      parentGraphics.setColor(new Color(level, level, level));
      parentGraphics.drawLine(0, y, width, y);
    }

    // Draw the parent at the left of the given rect, leaving space
    // for the children at the top
    g.drawImage(parentImage, bounds.x, bounds.y+height, this);

    // Now create an array of child images using the same databuffer
    // as the parent. We use the fact that we the sample model is
    // a degenerate PixelInterleavedSampleModel (only one band) to
    // create rasters that use the same sample model but with an
    // offset into the data buffer
    BufferedImage childImages[] = new BufferedImage[numChildRasters];
    Point origin = new Point(0,0);
    for (int i = 0; i < numChildRasters; i++) {
      WritableRaster r = Raster.createInterleavedRaster(parentImage.getData().getDataBuffer(),
                                  width,
                                  height,
                                  // The scaline stride
                                  width*numChildRasters,
                                  1,
                                  // The databuffer offset for this child
                                  new int[] {i*width},
                                  origin);

      // Create the new image from the raster we created
      // (We could just draw it here, but store it anyway.)
      childImages[i] = new BufferedImage(
                              parentImage.getColorModel(),
                              r,
                              parentImage.isAlphaPremultiplied(),
                              null);
    }

    // Now go through the child images, drawing them
    // across the top of the screen. Note that each one is
    // a solid gray level.
    for (int i = 0; i < numChildRasters; i++) {
      Graphics2D childGraphics = childImages[i].createGraphics();

      // Print the child num in the top left
      childGraphics.setColor(Color.black);
      childGraphics.drawString(i+"",1,11);
      childGraphics.setColor(Color.white);
      childGraphics.drawString(i+"",0,10);

      g.drawImage(childImages[i], bounds.x+width*(i+1), bounds.y, this);
    }
  }

  // This is very similar to the grayscale version, except we
  // can't use the same trick with the raster offset with the
  // pixel packed color model. What we do instead is create a
  // copy of the parent's databuffer, but with an appropriate
  // offset for each child.
  void paintColorRasters(Graphics2D g, Rectangle bounds, int numChildRasters) {
    int width = bounds.width / (numChildRasters+1);
    int height = bounds.height / (numChildRasters+1);
    int parentHeight = height*numChildRasters;
    BufferedImage parentImage =
      new BufferedImage(width, parentHeight, BufferedImage.TYPE_USHORT_565_RGB);
   Graphics2D parentGraphics = parentImage.createGraphics();

   // This time the hue of each child row is the same, and
   // the saturation increases from top to bottom.
   for (int y = 0; y < parentHeight; y++) {
      int childRaster = y % numChildRasters;
      float saturation = 1.0f*y / parentHeight;
      float hue = 1.0f*childRaster / numChildRasters;
      Color c = Color.getHSBColor(hue, saturation, 1.0f);
      parentGraphics.setColor(c);
      parentGraphics.drawLine(0, y, width, y);
    }
    g.drawImage(parentImage, bounds.x, bounds.y+height, this);
    BufferedImage childImages[] = new BufferedImage[numChildRasters];
    Point origin = new Point(0,0);

    // Get the parent databuffer that we'll clone.
    DataBuffer parentDataBuffer = parentImage.getData().getDataBuffer();
    Raster parentRaster = parentImage.getRaster();
    // Note that we have to assume the sample model class here, so this would
    // have to be changed if the parent sample model changed
    int masks[] = ((SinglePixelPackedSampleModel)parentRaster.
                      getSampleModel()).
                        getBitMasks();

    for (int i = 0; i < numChildRasters; i++) {
      // Derived the offset databuffer according to the child index
      DataBuffer db = createOffsetDataBuffer(parentDataBuffer, i*width);
      // Create a new packed pixel raster with the offset databuffer
      WritableRaster r = Raster.createPackedRaster(
          db,
          width,
          height,
          width*numChildRasters,
          masks,
          origin);

      // The rest is the same as the grayscale version
      childImages[i] = new BufferedImage(
                              parentImage.getColorModel(),
                              r,
                              parentImage.isAlphaPremultiplied(),
                              null);
    }
    for (int i = 0; i < numChildRasters; i++) {
      Graphics2D childGraphics = childImages[i].createGraphics();
      childGraphics.setColor(Color.black);
      childGraphics.drawString(i+"",1,11);
      childGraphics.setColor(Color.white);
      childGraphics.drawString(i+"",0,10);

      g.drawImage(childImages[i], bounds.x+width*(i+1), bounds.y, this);
    }
  }

  // Return a clone of the given databuffer but with the given
  // offset. Note that the switch statement should be completed
  // of the databuffer class could vary
  DataBuffer createOffsetDataBuffer(DataBuffer db, int offset) {
    switch (db.getDataType()) {
      case DataBuffer.TYPE_USHORT:
        return new DataBufferUShort(((DataBufferUShort)db).getData(), db.getSize(), offset);
      //And the rest...
    }
    return null;
 }

  public static void main(String[] args) {
    Main main1 = new Main();
    main1.setSize(200, 200);
    main1.addWindowListener(new WindowAdapter() {
      public void windowClosing(WindowEvent e) {
        System.exit(0);
      }
    });
    main1.show();
  }
}

Reply via email to