/*****************************************************************************
* 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.PadMode;
import org.apache.batik.ext.awt.image.GraphicsUtil;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.image.DataBufferInt;
import java.awt.image.WritableRaster;
import java.awt.image.SampleModel;
import java.awt.image.SinglePixelPackedSampleModel;
/**
* This is an implementation of a Pad operation as a RenderedImage.
*
* @author <a href="mailto:Thomas.DeWeeese@Kodak.com">Thomas DeWeese</a>
* @version $Id: PadRed.java,v 1.1 2003/04/11 07:56:53 tom Exp $ */
public class PadRed extends AbstractRed {
static final boolean DEBUG=false;
PadMode padMode;
RenderingHints hints;
/**
* Construct A Rendered Pad operation. If the pad is smaller than
* the original image size then this devolves to a Crop.
*
* @param src The image to pad/crop
* @param bounds The bounds of the result (same coord system as src).
* @param padMode The pad mode to use (currently ignored).
* @param hints The hints to use for drawing 'pad' area.
*/
public PadRed(CachableRed src,
Rectangle bounds,
PadMode padMode,
RenderingHints hints) {
super(src,bounds,src.getColorModel(),
fixSampleModel(src, bounds),
bounds.x, bounds.y,
null);
this.padMode = padMode;
if (DEBUG) {
System.out.println("Src: " + src + " Bounds: " + bounds +
" Off: " +
src.getTileGridXOffset() + ", " +
src.getTileGridYOffset());
}
this.hints = hints;
}
public WritableRaster copyData(WritableRaster wr) {
// Get my source.
CachableRed src = (CachableRed)getSources().get(0);
Rectangle srcR = src.getBounds();
Rectangle wrR = wr.getBounds();
if (wrR.intersects(srcR)) {
Rectangle r = wrR.intersection(srcR);
// Limit the raster I send to my source to his rect.
WritableRaster srcWR;
srcWR = wr.createWritableChild(r.x, r.y, r.width, r.height,
r.x, r.y, null);
src.copyData(srcWR);
}
if (padMode == PadMode.ZERO_PAD) {
handleZero(wr);
} else if (padMode == PadMode.REPLICATE) {
handleReplicate(wr);
} else if (padMode == PadMode.WRAP) {
handleWrap(wr);
}
return wr;
}
protected static class ZeroRecter {
WritableRaster wr;
int bands;
static int [] zeros=null;
public ZeroRecter(WritableRaster wr) {
this.wr = wr;
this.bands = wr.getSampleModel().getNumBands();
}
public void zeroRect(Rectangle r) {
synchronized (this) {
if ((zeros == null) || (zeros.length <r.width*bands))
zeros = new int[r.width*bands];
}
for (int y=0; y<r.height; y++) {
wr.setPixels(r.x, r.y+y, r.width, 1, zeros);
}
}
public static ZeroRecter getZeroRecter(WritableRaster wr) {
if (GraphicsUtil.is_INT_PACK_Data(wr.getSampleModel(), false))
return new ZeroRecter_INT_PACK(wr);
else
return new ZeroRecter(wr);
}
public static void zeroRect(WritableRaster wr) {
ZeroRecter zr = getZeroRecter(wr);
zr.zeroRect(wr.getBounds());
}
}
protected static class ZeroRecter_INT_PACK extends ZeroRecter {
final int base;
final int scanStride;
final int pixels[];
final int zeros[];
final int x0, y0;
public ZeroRecter_INT_PACK(WritableRaster wr) {
super(wr);
SinglePixelPackedSampleModel sppsm;
sppsm = (SinglePixelPackedSampleModel)wr.getSampleModel();
scanStride = sppsm.getScanlineStride();
DataBufferInt db = (DataBufferInt)wr.getDataBuffer();
x0 = wr.getMinY();
y0 = wr.getMinX();
base = (db.getOffset() +
sppsm.getOffset(x0-wr.getSampleModelTranslateX(),
y0-wr.getSampleModelTranslateY()));
pixels = db.getBankData()[0];
if (wr.getWidth() > 10)
zeros = new int[wr.getWidth()];
else
zeros = null;
}
public void zeroRect(Rectangle r) {
final int rbase = base+(r.x-x0) + (r.y-y0)*scanStride;
if (r.width > 10) {
// Longer runs use arraycopy...
for (int y=0; y<r.height; y++) {
int sp = rbase + y*scanStride;
System.arraycopy(zeros, 0, pixels, sp, r.width);
}
} else {
// Small runs quicker to avoid func call.
int sp = rbase;
int end = sp +r.width;
int adj = scanStride-r.width;
for (int y=0; y<r.height; y++) {
while (sp < end)
pixels[sp++] = 0;
sp += adj;
end += scanStride;
}
}
}
}
protected void handleZero(WritableRaster wr) {
// Get my source.
CachableRed src = (CachableRed)getSources().get(0);
Rectangle srcR = src.getBounds();
Rectangle wrR = wr.getBounds();
ZeroRecter zr = ZeroRecter.getZeroRecter(wr);
// area rect (covers the area left to handle).
Rectangle ar = new Rectangle(wrR.x, wrR.y, wrR.width, wrR.height);
// draw rect (used for calls to zeroRect);
Rectangle dr = new Rectangle(wrR.x, wrR.y, wrR.width, wrR.height);
// We split the edge drawing up into four parts.
//
// +-----------------------------+
// | 1 | 2 |
// | +---------------+------|
// / / /4 /
// / / / /
// / / / /
// / / / /
// | +---------------+------|
// | | 3 |
// +-----------------------------+
//
// We update our x,y, width, height as we go along so
// we 'forget' about the parts we have already painted...
// Draw #1
if (DEBUG) {
System.out.println("WrR: " + wrR + " srcR: " + srcR);
// g2d.setColor(new Color(255,0,0,128));
}
if (ar.x < srcR.x) {
int w = srcR.x-ar.x;
if (w > ar.width) w=ar.width;
// g2d.fillRect(x, y, w, height);
dr.width = w;
zr.zeroRect(dr);
ar.x+=w;
ar.width-=w;
}
// Draw #2
if (DEBUG) {
System.out.println("WrR: [" +
ar.x + "," + ar.y + "," +
ar.width + "," + ar.height +
"] s rcR: " + srcR);
// g2d.setColor(new Color(0,0,255,128));
}
if (ar.y < srcR.y) {
int h = srcR.y-ar.y;
if (h > ar.height) h=ar.height;
// g2d.fillRect(x, y, width, h);
dr.x = ar.x;
dr.y = ar.y;
dr.width = ar.width;
dr.height = h;
zr.zeroRect(dr);
ar.y +=h;
ar.height-=h;
}
// Draw #3
if (DEBUG) {
System.out.println("WrR: [" +
ar.x + "," + ar.y + "," +
ar.width + "," + ar.height +
"] srcR: " + srcR);
// g2d.setColor(new Color(0,255,0,128));
}
if (ar.y+ar.height > srcR.y+srcR.height) {
int h = (ar.y+ar.height) - (srcR.y+srcR.height);
if (h > ar.height) h=ar.height;
int y0 = ar.y+ar.height-h; // the +/-1 cancel (?)
// g2d.fillRect(x, y0, width, h);
dr.x = ar.x;
dr.y = y0;
dr.width = ar.width;
dr.height = h;
zr.zeroRect(dr);
ar.height -= h;
}
// Draw #4
if (DEBUG) {
System.out.println("WrR: [" +
ar.x + "," + ar.y + "," +
ar.width + "," + ar.height +
"] srcR: " + srcR);
// g2d.setColor(new Color(255,255,0,128));
}
if (ar.x+ar.width > srcR.x+srcR.width) {
int w = (ar.x+ar.width) - (srcR.x+srcR.width);
if (w > ar.width) w=ar.width;
int x0 = ar.x+ar.width-w; // the +/-1 cancel (?)
// g2d.fillRect(x0, y, w, height);
dr.x = x0;
dr.y = ar.y;
dr.width = w;
dr.height = ar.height;
zr.zeroRect(dr);
ar.width-=w;
}
}
protected void handleReplicate(WritableRaster wr) {
// Get my source.
CachableRed src = (CachableRed)getSources().get(0);
Rectangle srcR = src.getBounds();
Rectangle wrR = wr.getBounds();
int x = wrR.x;
int y = wrR.y;
int width = wrR.width;
int height = wrR.height;
Rectangle r;
{
// Calculate an intersection that makes some sense
// even when the rects don't really intersect
// (The x and y ranges will be correct if they
// overlap in one dimension even if they don't
// intersect in both dimensions).
int minX = (srcR.x > x) ? srcR.x : x;
int maxX = (((srcR.x+srcR.width-1) < (x+width-1)) ?
( srcR.x+srcR.width-1) : (x+width-1));
int minY = (srcR.y > y) ? srcR.y : y;
int maxY = (((srcR.y+srcR.height-1) < (y+height-1)) ?
( srcR.y+srcR.height-1) : (y+height-1));
int x0 = minX;
int w = maxX-minX+1;
int y0 = minY;
int h = maxY-minY+1;
if (w <0 ) { x0 = 0; w = 0; }
if (h <0 ) { y0 = 0; h = 0; }
r = new Rectangle(x0, y0, w, h);
}
// We split the edge drawing up into four parts.
//
// +-----------------------------+
// | 3 | 1 | 4 |
// | +---------------+ |
// / / / /
// / / src / /
// / / / /
// / / / /
// | +---------------+ |
// | | 2 | |
// +-----------------------------+
//
// Draw #1
if (y < srcR.y) {
int repW = r.width;
int repX = r.x;
int wrX = r.x;
int wrY = y;
if (x+width-1 <= srcR.x) {
// we are off to the left of src. so set repX to the
// left most pixel...
repW = 1;
repX = srcR.x;
wrX = x+width-1;
} else if (x >= srcR.x+srcR.width) {
// we are off to the right of src, so set repX to
// the right most pixel
repW = 1;
repX = srcR.x+srcR.width-1;
wrX = x;
}
// This fills the top row of section 1 from src (we
// go to src instead of getting the data from wr because
// in some cases wr will be completely off the top of src
WritableRaster wr1 = wr.createWritableChild(wrX, wrY,
repW, 1,
repX, srcR.y, null);
src.copyData(wr1);
wrY++;
int endY = srcR.y;
if (y+height < endY) endY = y+height;
if (wrY < endY) {
int [] pixels = wr.getPixels(wrX, wrY-1,
repW, 1, (int [])null);
while (wrY < srcR.y) {
wr.setPixels(wrX, wrY, repW, 1, pixels);
wrY++;
}
}
}
// Draw #2
if ((y+height) > (srcR.y+srcR.height)) {
int repW = r.width;
int repX = r.x;
int repY = srcR.y+srcR.height-1;
int wrX = r.x;
int wrY = srcR.y+srcR.height;
if (wrY < y) wrY = y;
if (x+width <= srcR.x) {
// we are off to the left of src. so set repX to the
// left most pixel...
repW = 1;
repX = srcR.x;
wrX = x+width-1;
} else if (x >= srcR.x+srcR.width) {
// we are off to the right of src, so set repX to
// the right most pixel
repW = 1;
repX = srcR.x+srcR.width-1;
wrX = x;
}
if (DEBUG) {
System.out.println("wr: " + wr.getBounds());
System.out.println("req: [" + wrX + ", " + wrY + ", " +
repW + ", 1]");
}
// First we get the top row of pixels from src. (we
// go to src instead of getting the data from wr because
// in some cases wr will be completely off the bottom of src).
WritableRaster wr1 = wr.createWritableChild(wrX, wrY,
repW, 1,
repX, repY, null);
// This fills the top row of section 2 from src
src.copyData(wr1);
wrY++;
int endY = y+height;
if (wrY < endY) {
// This fills the rest of section 2 from the first line.
int [] pixels = wr.getPixels(wrX, wrY-1,
repW, 1, (int [])null);
while (wrY < endY) {
wr.setPixels(wrX, wrY, repW, 1, pixels);
wrY++;
}
}
}
// Draw #3
if (x < srcR.x) {
// We are garunteed that we have a column of pixels down
// the edge of 1 and src. We simply replicate this column
// out to the edges of 2.
int wrX = srcR.x;
if (x+width <= srcR.x) {
wrX = x+width-1;
}
int xLoc = x;
int [] pixels = wr.getPixels(wrX, y, 1, height, (int [])null);
while (xLoc < wrX) {
wr.setPixels(xLoc, y, 1, height, pixels);
xLoc++;
}
}
// Draw #4
if (x+width > srcR.x+srcR.width) {
// We are garunteed that we have a column of pixels down
// the edge of 1 and src. We simply replicate this column
// out to the edges of 3.
int wrX = srcR.x+srcR.width-1;
if (x >= srcR.x+srcR.width) {
wrX = x;
}
int xLoc = wrX+1;
int endX = x+width-1;
int [] pixels = wr.getPixels(wrX, y, 1, height, (int [])null);
while (xLoc < endX) {
wr.setPixels(xLoc, y, 1, height, pixels);
xLoc++;
}
}
}
protected void handleWrap(WritableRaster wr) {
handleZero(wr);
}
/**
* This function 'fixes' the source's sample model.
* right now it just ensures that the sample model isn't
* much larger than my width.
*/
protected static SampleModel fixSampleModel(CachableRed src,
Rectangle bounds) {
int defSz = AbstractTiledRed.getDefaultTileSize();
SampleModel sm = src.getSampleModel();
int w = sm.getWidth();
if (w < defSz) w = defSz;
if (w > bounds.width) w = bounds.width;
int h = sm.getHeight();
if (h < defSz) h = defSz;
if (h > bounds.height) h = bounds.height;
// System.out.println("Pad SMSz: " + w + "x" + h);
return sm.createCompatibleSampleModel(w, h);
}
}