| /* |
| |
| Copyright 2001-2003 The Apache Software Foundation |
| |
| Licensed under the Apache License, Version 2.0 (the "License"); |
| you may not use this file except in compliance with the License. |
| You may obtain a copy of the License at |
| |
| http://www.apache.org/licenses/LICENSE-2.0 |
| |
| Unless required by applicable law or agreed to in writing, software |
| distributed under the License is distributed on an "AS IS" BASIS, |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| See the License for the specific language governing permissions and |
| limitations under the License. |
| |
| */ |
| package org.apache.batik.ext.awt.image.rendered; |
| |
| import java.awt.Rectangle; |
| import java.awt.RenderingHints; |
| import java.awt.image.DataBufferInt; |
| import java.awt.image.SampleModel; |
| import java.awt.image.SinglePixelPackedSampleModel; |
| import java.awt.image.WritableRaster; |
| |
| import org.apache.batik.ext.awt.image.GraphicsUtil; |
| import org.apache.batik.ext.awt.image.PadMode; |
| |
| |
| /** |
| * This is an implementation of a Pad operation as a RenderedImage. |
| * |
| * @author <a href="mailto:Thomas.DeWeeese@Kodak.com">Thomas DeWeese</a> |
| * @version $Id$ */ |
| 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); |
| } |
| } |