/*****************************************************************************
* Copyright (C) The Apache Software Foundation. All rights reserved. *
* ------------------------------------------------------------------------- *
* This software is published under the terms of the Apache Software License *
* version 1.1, a copy of which has been included with this distribution in *
* the LICENSE file. *
*****************************************************************************/
package org.apache.batik.ext.awt.image.rendered;
import org.apache.batik.ext.awt.image.GraphicsUtil;
import java.util.Map;
import java.util.List;
import java.awt.Rectangle;
import java.awt.Point;
import java.awt.image.DataBufferInt;
import java.awt.image.Raster;
import java.awt.image.WritableRaster;
import java.awt.image.SampleModel;
import java.awt.image.ColorModel;
/**
* This is an abstract base class that takes care of most of the
* normal issues surrounding the implementation of the CachableRed
* (RenderedImage) interface. It tries to make no assumptions about
* the subclass implementation.
*
* @author <a href="mailto:Thomas.DeWeeese@Kodak.com">Thomas DeWeese</a>
* @version $Id: AbstractTiledRed.java,v 1.1 2003/04/11 07:56:53 tom Exp $
*/
public abstract class AbstractTiledRed
extends AbstractRed
implements TileGenerator {
private TileStore tiles;
private static int defaultTileSize = 128;
public static int getDefaultTileSize() { return defaultTileSize; }
/**
* void constructor. The subclass must call one of the
* flavors of init before the object becomes usable.
* This is useful when the proper parameters to the init
* method need to be computed in the subclasses constructor.
*/
protected AbstractTiledRed() { }
/**
* Construct an Abstract RenderedImage from a bounds rect and props
* (may be null). The srcs Vector will be empty.
* @param bounds this defines the extent of the rable in the
* user coordinate system.
* @param props this initializes the props Map (may be null)
*/
protected AbstractTiledRed(Rectangle bounds, Map props) {
super(bounds, props);
}
/**
* Construct an Abstract RenderedImage from a source image and
* props (may be null).
* @param src will be the first (and only) member of the srcs
* Vector. Src is also used to set the bounds, ColorModel,
* SampleModel, and tile grid offsets.
* @param props this initializes the props Map. */
protected AbstractTiledRed(CachableRed src, Map props) {
super(src, props);
}
/**
* Construct an Abstract RenderedImage from a source image, bounds
* rect and props (may be null).
* @param src will be the first (and only) member of the srcs
* Vector. Src is also used to set the ColorModel, SampleModel,
* and tile grid offsets.
* @param bounds The bounds of this image.
* @param props this initializes the props Map. */
protected AbstractTiledRed(CachableRed src, Rectangle bounds, Map props) {
super(src, bounds, props);
}
/**
* Construct an Abstract RenderedImage from a source image, bounds
* rect and props (may be null).
* @param src will be the first (and only) member of the srcs
* Vector. Src is also used to set the ColorModel, SampleModel,
* and tile grid offsets.
* @param bounds The bounds of this image.
* @param cm The ColorModel to use. If null it will default to
* ComponentColorModel.
* @param sm The sample model to use. If null it will construct
* a sample model the matches the given/generated ColorModel and is
* the size of bounds.
* @param props this initializes the props Map. */
protected AbstractTiledRed(CachableRed src, Rectangle bounds,
ColorModel cm, SampleModel sm,
Map props) {
super(src, bounds, cm, sm, props);
}
/**
* Construct an Abstract Rable from a bounds rect and props
* (may be null). The srcs Vector will be empty.
* @param srcs This is used to initialize the srcs Vector. All
* the members of srcs must be Filter otherwise an error
* will be thrown.
* @param bounds this defines the extent of the rable in the
* user coordinate system.
* @param cm The ColorModel to use. If null it will default to
* ComponentColorModel.
* @param sm The sample model to use. If null it will construct
* a sample model the matches the given/generated ColorModel and is
* the size of bounds.
* @param tileGridXOff The x location of tile 0,0.
* @param tileGridYOff The y location of tile 0,0.
* @param props this initializes the props Map.
*/
protected AbstractTiledRed(CachableRed src, Rectangle bounds,
ColorModel cm, SampleModel sm,
int tileGridXOff, int tileGridYOff,
Map props) {
super(src, bounds, cm, sm, tileGridXOff, tileGridYOff, props);
}
/**
* This is one of two basic init function (this is for single
* source rendereds).
* It is provided so subclasses can compute various values
* before initializing all the state in the base class.
* You really should call this method before returning from
* your subclass constructor.
*
* @param src The source for the filter
* @param bounds The bounds of the image
* @param cm The ColorModel to use. If null it defaults to
* ComponentColorModel/ src's ColorModel.
* @param sm The Sample modle to use. If this is null it will
* use the src's sample model if that is null it will
* construct a sample model that matches the ColorModel
* and is the size of the whole image.
* @param tileGridXOff The x location of tile 0,0.
* @param tileGridYOff The y location of tile 0,0.
* @param props Any properties you want to associate with the image.
*/
protected void init(CachableRed src, Rectangle bounds,
ColorModel cm, SampleModel sm,
int tileGridXOff, int tileGridYOff,
Map props) {
init(src, bounds, cm, sm, tileGridXOff, tileGridYOff, null, props);
}
/**
* This is one of two basic init function (this is for single
* source rendereds).
* It is provided so subclasses can compute various values
* before initializing all the state in the base class.
* You really should call this method before returning from
* your subclass constructor.
*
* @param src The source for the filter
* @param bounds The bounds of the image
* @param cm The ColorModel to use. If null it defaults to
* ComponentColorModel/ src's ColorModel.
* @param sm The Sample modle to use. If this is null it will
* use the src's sample model if that is null it will
* construct a sample model that matches the ColorModel
* and is the size of the whole image.
* @param tileGridXOff The x location of tile 0,0.
* @param tileGridYOff The y location of tile 0,0.
* @param tileStore The tileStore to use (or null).
* @param props Any properties you want to associate with the image.
*/
protected void init(CachableRed src, Rectangle bounds,
ColorModel cm, SampleModel sm,
int tileGridXOff, int tileGridYOff,
TileStore tiles,
Map props) {
super.init(src, bounds, cm, sm, tileGridXOff, tileGridYOff, props);
this.tiles = tiles;
if (this.tiles == null)
this.tiles = createTileStore();
}
/**
* Construct an Abstract Rable from a List of sources a bounds rect
* and props (may be null).
* @param srcs This is used to initialize the srcs Vector. All
* the members of srcs must be CachableRed otherwise an error
* will be thrown.
* @param bounds this defines the extent of the rendered in pixels
* @param props this initializes the props Map.
*/
protected AbstractTiledRed(List srcs, Rectangle bounds, Map props) {
super(srcs, bounds, props);
}
/**
* Construct an Abstract RenderedImage from a bounds rect,
* ColorModel (may be null), SampleModel (may be null) and props
* (may be null). The srcs Vector will be empty.
* @param srcs This is used to initialize the srcs Vector. All
* the members of srcs must be CachableRed otherwise an error
* will be thrown.
* @param bounds this defines the extent of the rendered in pixels
* @param cm The ColorModel to use. If null it will default to
* ComponentColorModel.
* @param sm The sample model to use. If null it will construct
* a sample model the matches the given/generated ColorModel and is
* the size of bounds.
* @param props this initializes the props Map.
*/
protected AbstractTiledRed(List srcs, Rectangle bounds,
ColorModel cm, SampleModel sm,
Map props) {
super(srcs, bounds, cm, sm, props);
}
/**
* Construct an Abstract RenderedImage from a bounds rect,
* ColorModel (may be null), SampleModel (may be null), tile grid
* offsets and props (may be null). The srcs Vector will be
* empty.
* @param srcs This is used to initialize the srcs Vector. All
* the members of srcs must be CachableRed otherwise an error
* will be thrown.
* @param bounds this defines the extent of the rable in the
* user coordinate system.
* @param cm The ColorModel to use. If null it will default to
* ComponentColorModel.
* @param sm The sample model to use. If null it will construct
* a sample model the matches the given/generated ColorModel and is
* the size of bounds.
* @param tileGridXOff The x location of tile 0,0.
* @param tileGridYOff The y location of tile 0,0.
* @param props this initializes the props Map.
*/
protected AbstractTiledRed(List srcs, Rectangle bounds,
ColorModel cm, SampleModel sm,
int tileGridXOff, int tileGridYOff,
Map props) {
super(srcs, bounds, cm, sm, tileGridXOff, tileGridYOff, props);
}
/**
* This is the basic init function.
* It is provided so subclasses can compute various values
* before initializing all the state in the base class.
* You really should call this method before returning from
* your subclass constructor.
*
* @param srcs The list of sources
* @param bounds The bounds of the image
* @param cm The ColorModel to use. If null it defaults to
* ComponentColorModel.
* @param sm The Sample modle to use. If this is null it will
* construct a sample model that matches the ColorModel
* and is the size of the whole image.
* @param tileGridXOff The x location of tile 0,0.
* @param tileGridYOff The y location of tile 0,0.
* @param props Any properties you want to associate with the image.
*/
protected void init(List srcs, Rectangle bounds,
ColorModel cm, SampleModel sm,
int tileGridXOff, int tileGridYOff,
Map props) {
super.init(srcs, bounds, cm, sm, tileGridXOff, tileGridYOff, props);
tiles = createTileStore();
}
public TileStore getTileStore() {
return tiles;
}
protected void setTileStore(TileStore tiles) {
this.tiles = tiles;
}
protected TileStore createTileStore() {
return TileCache.getTileMap(this);
}
public WritableRaster copyData(WritableRaster wr) {
copyToRasterByBlocks(wr);
return wr;
}
public Raster getData(Rectangle rect) {
int xt0 = getXTile(rect.x);
int xt1 = getXTile(rect.x+rect.width-1);
int yt0 = getYTile(rect.y);
int yt1 = getYTile(rect.y+rect.height-1);
if ((xt0 == xt1) && (yt0 == yt1)) {
Raster r = getTile(xt0, yt0);
return r.createChild(rect.x, rect.y, rect.width, rect.height,
rect.x, rect.y, null);
}
// rect crosses tile boundries...
return super.getData(rect);
}
public Raster getTile(int x, int y) {
return tiles.getTile(x, y);
}
public Raster genTile(int x, int y) {
WritableRaster wr = makeTile(x, y);
genRect(wr);
return wr;
}
public abstract void genRect(WritableRaster wr);
// { copyToRaster(wr); }
public void setTile(int x, int y, Raster ras) {
tiles.setTile(x, y, ras);
}
public void copyToRasterByBlocks(WritableRaster wr) {
final boolean is_INT_PACK =
GraphicsUtil.is_INT_PACK_Data(getSampleModel(), false);
Rectangle bounds = getBounds();
Rectangle wrR = wr.getBounds();
int tx0 = getXTile(wrR.x);
int ty0 = getYTile(wrR.y);
int tx1 = getXTile(wrR.x+wrR.width -1);
int ty1 = getYTile(wrR.y+wrR.height-1);
if (tx0 < minTileX) tx0 = minTileX;
if (ty0 < minTileY) ty0 = minTileY;
if (tx1 >= minTileX+numXTiles) tx1 = minTileX+numXTiles-1;
if (ty1 >= minTileY+numYTiles) ty1 = minTileY+numYTiles-1;
if ((tx1 < tx0) || (ty1 < ty0))
return;
// System.out.println("WR: " + wrR);
// System.out.println("ME: " + bounds);
int insideTx0 = tx0;
int insideTx1 = tx1;
int insideTy0 = ty0;
int insideTy1 = ty1;
// Now figure out what tiles lie completely inside wr...
int tx, ty;
tx = tx0*tileWidth+tileGridXOff;
if ((tx < wrR.x) && (bounds.x != wrR.x))
// Partial tile off the left.
insideTx0++;
ty= ty0*tileHeight+tileGridYOff;
if ((ty < wrR.y) && (bounds.y != wrR.y))
// Partial tile off the top.
insideTy0++;
tx= (tx1+1)*tileWidth+tileGridXOff-1;
if ((tx >= (wrR.x+wrR.width)) &&
((bounds.x+bounds.width) != (wrR.x+wrR.width)))
// Partial tile off right
insideTx1--;
ty= (ty1+1)*tileHeight+tileGridYOff-1;
if ((ty >= (wrR.y+wrR.height)) &&
((bounds.y+bounds.height) != (wrR.y+wrR.height)))
// Partial tile off bottom
insideTy1--;
int xtiles = insideTx1-insideTx0+1;
int ytiles = insideTy1-insideTy0+1;
boolean [] occupied = null;
if ((xtiles > 0) && (ytiles > 0))
occupied = new boolean[xtiles*ytiles];
boolean [] got = new boolean[2*(tx1-tx0+1) + 2*(ty1-ty0+1)];
int idx = 0;
int numFound = 0;
// Collect all the tiles that we currently have in cache...
for (int y=ty0; y<=ty1; y++) {
for (int x=tx0; x<=tx1; x++) {
Raster ras = tiles.getTileNoCompute(x, y);
boolean found = (ras != null);
if ((y>=insideTy0) && (y<=insideTy1) &&
(x>=insideTx0) && (x<=insideTx1))
occupied[(x-insideTx0)+(y-insideTy0)*xtiles] = found;
else
got[idx++] = found;
if (!found) continue;
numFound++;
if (is_INT_PACK)
GraphicsUtil.copyData_INT_PACK(ras, wr);
else
GraphicsUtil.copyData_FALLBACK(ras, wr);
}
}
// System.out.println("Found: " + numFound + " out of " +
// ((tx1-tx0+1)*(ty1-ty0+1)));
// Compute the stuff from the middle in the largest possible Chunks.
if ((xtiles > 0) && (ytiles > 0)) {
TileBlock block = new TileBlock
(insideTx0, insideTy0, xtiles, ytiles, occupied,
0, 0, xtiles, ytiles);
// System.out.println("Starting Splits");
drawBlock(block, wr);
// Exception e= new Exception("Foo");
// e.printStackTrace();
}
idx = 0;
// Fill in the ones that weren't in the cache.
for (ty=ty0; ty<=ty1; ty++) {
for (tx=tx0; tx<=tx1; tx++) {
// At least touch the tile...
Raster ras = tiles.getTileNoCompute(tx, ty);
if ((ty>=insideTy0) && (ty<=insideTy1) &&
(tx>=insideTx0) && (tx<=insideTx1)) {
if (ras != null) continue;
// Fill the tile from wr (since wr is full now
// at least in the middle).
WritableRaster tile = makeTile(tx, ty);
if (is_INT_PACK)
GraphicsUtil.copyData_INT_PACK(wr, tile);
else
GraphicsUtil.copyData_FALLBACK(wr, tile);
tiles.setTile(tx, ty, tile);
}
else {
if (got[idx++]) continue;
// System.out.println("Computing : " + x + "," + y);
ras = getTile(tx, ty);// Compute the tile..
if (Thread.currentThread().isInterrupted())
return;
if (is_INT_PACK)
GraphicsUtil.copyData_INT_PACK(ras, wr);
else
GraphicsUtil.copyData_FALLBACK(ras, wr);
}
}
}
// System.out.println("Ending Computation: " + this);
}
/**
* Copies data from this images tile grid into wr. wr may
* extend outside the bounds of this image in which case the
* data in wr outside the bounds will not be touched.
* @param wr Raster to fill with image data.
*/
public void copyToRaster(WritableRaster wr) {
Rectangle wrR = wr.getBounds();
int tx0 = getXTile(wrR.x);
int ty0 = getYTile(wrR.y);
int tx1 = getXTile(wrR.x+wrR.width -1);
int ty1 = getYTile(wrR.y+wrR.height-1);
if (tx0 < minTileX) tx0 = minTileX;
if (ty0 < minTileY) ty0 = minTileY;
if (tx1 >= minTileX+numXTiles) tx1 = minTileX+numXTiles-1;
if (ty1 >= minTileY+numYTiles) ty1 = minTileY+numYTiles-1;
final boolean is_INT_PACK =
GraphicsUtil.is_INT_PACK_Data(getSampleModel(), false);
int xtiles = (tx1-tx0+1);
boolean [] got = new boolean[xtiles*(ty1-ty0+1)];
// Run through and get the tiles that are just sitting in the
// cache...
for (int y=ty0; y<=ty1; y++)
for (int x=tx0; x<=tx1; x++) {
Raster r = tiles.getTileNoCompute(x, y);
if (r == null) continue; // Not there.
got[x-tx0 + (y-ty0)*xtiles] = true;
if (is_INT_PACK)
GraphicsUtil.copyData_INT_PACK(r, wr);
else
GraphicsUtil.copyData_FALLBACK(r, wr);
}
// Run through and pick up the ones we need to compute...
for (int y=ty0; y<=ty1; y++)
for (int x=tx0; x<=tx1; x++) {
if (got[x-tx0 + (y-ty0)*xtiles]) continue; // already have.
Raster r = getTile(x, y);
if (is_INT_PACK)
GraphicsUtil.copyData_INT_PACK(r, wr);
else
GraphicsUtil.copyData_FALLBACK(r, wr);
}
}
protected void drawBlock(TileBlock block, WritableRaster wr) {
TileBlock [] blocks = block.getBestSplit();
if (blocks == null)
return;
drawBlockInPlace(blocks, wr);
}
protected void drawBlockAndCopy(TileBlock []blocks, WritableRaster wr) {
if (blocks.length == 1) {
TileBlock curr = blocks[0];
int xloc = curr.getXLoc()*tileWidth +tileGridXOff;
int yloc = curr.getYLoc()*tileHeight+tileGridYOff;
if ((xloc == wr.getMinX()) &&
(yloc == wr.getMinY())) {
// Safe to draw in place...
drawBlockInPlace(blocks, wr);
return;
}
}
int maxSz=0;
for (int i=0; i<blocks.length; i++) {
int sz = ((blocks[i].getWidth() *tileWidth)*
(blocks[i].getHeight()*tileHeight));
if (sz > maxSz) maxSz=sz;
}
DataBufferInt dbi = new DataBufferInt(maxSz);
int [] masks = { 0x00FF0000, 0x0000FF00, 0x000000FF, 0xFF000000 };
boolean use_INT_PACK = GraphicsUtil.is_INT_PACK_Data
(wr.getSampleModel(), false);
for (int i=0; i<blocks.length; i++) {
TileBlock curr = blocks[i];
int xloc = curr.getXLoc()*tileWidth +tileGridXOff;
int yloc = curr.getYLoc()*tileHeight+tileGridYOff;
Rectangle tb = new Rectangle(xloc, yloc,
curr.getWidth()*tileWidth,
curr.getHeight()*tileHeight);
tb = tb.intersection(bounds);
Point loc = new Point(tb.x, tb.y);
WritableRaster child = Raster.createPackedRaster
(dbi, tb.width, tb.height, tb.width, masks, loc);
genRect(child);
if (use_INT_PACK) GraphicsUtil.copyData_INT_PACK(child, wr);
else GraphicsUtil.copyData_FALLBACK(child, wr);
if (Thread.currentThread().isInterrupted())
return;
}
}
protected void drawBlockInPlace(TileBlock [] blocks, WritableRaster wr) {
// System.out.println("Ending Splits: " + blocks.length);
for (int i=0; i<blocks.length; i++) {
TileBlock curr = blocks[i];
// System.out.println("Block " + i + ":\n" + curr);
int xloc = curr.getXLoc()*tileWidth +tileGridXOff;
int yloc = curr.getYLoc()*tileHeight+tileGridYOff;
Rectangle tb = new Rectangle(xloc, yloc,
curr.getWidth()*tileWidth,
curr.getHeight()*tileHeight);
tb = tb.intersection(bounds);
WritableRaster child =
wr.createWritableChild(tb.x, tb.y, tb.width, tb.height,
tb.x, tb.y, null);
// System.out.println("Computing : " + child);
genRect(child);
if (Thread.currentThread().isInterrupted())
return;
}
}
}