| /* |
| |
| Licensed to the Apache Software Foundation (ASF) under one or more |
| contributor license agreements. See the NOTICE file distributed with |
| this work for additional information regarding copyright ownership. |
| The ASF licenses this file to You 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.AlphaComposite; |
| import java.awt.Color; |
| import java.awt.Graphics2D; |
| import java.awt.Point; |
| import java.awt.Rectangle; |
| import java.awt.RenderingHints; |
| import java.awt.geom.AffineTransform; |
| import java.awt.image.BufferedImage; |
| import java.awt.image.ColorModel; |
| import java.awt.image.DataBufferInt; |
| import java.awt.image.Raster; |
| import java.awt.image.RenderedImage; |
| 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.util.HaltingThread; |
| |
| /** |
| * This filter simply tiles its tile starting from the upper |
| * left corner of the tiled region. |
| * |
| * @author <a href="mailto:vincent.hardy@eng.sun.com">Vincent Hardy</a> |
| * @version $Id$ |
| */ |
| public class TileRed extends AbstractRed implements TileGenerator { |
| static final AffineTransform IDENTITY = new AffineTransform(); |
| |
| /** |
| * Area tiled by this filter. |
| */ |
| Rectangle tiledRegion; |
| |
| int xStep; |
| int yStep; |
| |
| TileStore tiles; |
| |
| private RenderingHints hints; |
| |
| final boolean is_INT_PACK; |
| |
| /** |
| * Tile |
| */ |
| RenderedImage tile = null; |
| WritableRaster raster = null; |
| |
| |
| public TileRed(RenderedImage tile, |
| Rectangle tiledRegion) { |
| this(tile, tiledRegion, tile.getWidth(), tile.getHeight(), null); |
| } |
| |
| public TileRed(RenderedImage tile, |
| Rectangle tiledRegion, |
| RenderingHints hints) { |
| this(tile, tiledRegion, tile.getWidth(), tile.getHeight(), hints); |
| } |
| |
| public TileRed(RenderedImage tile, |
| Rectangle tiledRegion, |
| int xStep, int yStep) { |
| this(tile, tiledRegion, xStep, yStep, null); |
| } |
| |
| public TileRed(RenderedImage tile, |
| Rectangle tiledRegion, |
| int xStep, int yStep, |
| RenderingHints hints) { |
| if(tiledRegion == null){ |
| throw new IllegalArgumentException(); |
| } |
| |
| if(tile == null){ |
| throw new IllegalArgumentException(); |
| } |
| |
| // org.apache.batik.test.gvt.ImageDisplay.showImage("Tile: ", tile); |
| this.tiledRegion = tiledRegion; |
| this.xStep = xStep; |
| this.yStep = yStep; |
| this.hints = hints; |
| |
| SampleModel sm = fixSampleModel(tile, xStep, yStep, |
| tiledRegion.width, |
| tiledRegion.height); |
| ColorModel cm = tile.getColorModel(); |
| |
| double smSz = AbstractTiledRed.getDefaultTileSize(); |
| smSz = smSz*smSz; |
| |
| double stepSz = (xStep*(double)yStep); |
| // be prepaired to grow the default tile size quite a bit if |
| // it means the image tile will fit in it... |
| if (16.1*smSz > stepSz) { |
| int xSz = xStep; |
| int ySz = yStep; |
| |
| // If the pattern size is small then have multiple copies |
| // in our tile. |
| if (4*stepSz <= smSz) { |
| int mult = (int)Math.ceil(Math.sqrt(smSz/stepSz)); |
| xSz *= mult; |
| ySz *= mult; |
| } |
| // System.out.println("Using Raster for pattern"); |
| sm = sm.createCompatibleSampleModel(xSz, ySz); |
| raster = Raster.createWritableRaster |
| (sm, new Point(tile.getMinX(), tile.getMinY())); |
| } |
| |
| is_INT_PACK = GraphicsUtil.is_INT_PACK_Data(sm, false); |
| // System.out.println("Is INT PACK: " + is_INT_PACK); |
| |
| // Initialize our base class We set our bounds be we will |
| // respond with data for any area we cover. This is needed |
| // because the userRegion passed into PatterPaintContext |
| // doesn't account for stroke So we use that as a basis but |
| // when the context asks us for stuff outside that region we |
| // complie. |
| init((CachableRed)null, tiledRegion, cm, sm, |
| tile.getMinX(), tile.getMinY(), null); |
| |
| if (raster != null) { |
| WritableRaster fromRaster = raster.createWritableChild |
| (tile.getMinX(), tile.getMinY(), |
| xStep, yStep, tile.getMinX(), tile.getMinY(), null); |
| |
| // Fill one 'tile' of the input.... |
| fillRasterFrom(fromRaster, tile); |
| fillOutRaster(raster); |
| } |
| else { |
| this.tile = new TileCacheRed(GraphicsUtil.wrap(tile)); |
| } |
| } |
| |
| public WritableRaster copyData(WritableRaster wr) { |
| int xOff = ((int)Math.floor(wr.getMinX()/xStep))*xStep; |
| int yOff = ((int)Math.floor(wr.getMinY()/yStep))*yStep; |
| int x0 = wr.getMinX()-xOff; |
| int y0 = wr.getMinY()-yOff; |
| int tx0 = getXTile(x0); |
| int ty0 = getYTile(y0); |
| int tx1 = getXTile(x0+wr.getWidth() -1); |
| int ty1 = getYTile(y0+wr.getHeight()-1); |
| |
| for (int y=ty0; y<=ty1; y++) |
| for (int x=tx0; x<=tx1; x++) { |
| Raster r = getTile(x, y); |
| r = r.createChild(r.getMinX(), r.getMinY(), |
| r.getWidth(), r.getHeight(), |
| r.getMinX()+xOff, r.getMinY()+yOff, null); |
| if (is_INT_PACK) |
| GraphicsUtil.copyData_INT_PACK(r, wr); |
| else |
| GraphicsUtil.copyData_FALLBACK(r, wr); |
| } |
| return wr; |
| } |
| |
| |
| public Raster getTile(int x, int y) { |
| |
| if (raster!=null) { |
| // We have a Single raster that we translate where needed |
| // position. So just offest appropriately. |
| int tx = tileGridXOff+x*tileWidth; |
| int ty = tileGridYOff+y*tileHeight; |
| return raster.createTranslatedChild(tx, ty); |
| } |
| |
| // System.out.println("Checking Cache [" + x + "," + y + "]"); |
| return genTile(x,y); |
| } |
| |
| public Raster genTile(int x, int y) { |
| // System.out.println("Cache Miss [" + x + "," + y + "]"); |
| int tx = tileGridXOff+x*tileWidth; |
| int ty = tileGridYOff+y*tileHeight; |
| |
| if (raster!=null) { |
| // We have a Single raster that we translate where needed |
| // position. So just offest appropriately. |
| return raster.createTranslatedChild(tx, ty); |
| } |
| |
| Point pt = new Point(tx, ty); |
| WritableRaster wr = Raster.createWritableRaster(sm, pt); |
| fillRasterFrom(wr, tile); |
| return wr; |
| } |
| |
| public WritableRaster fillRasterFrom(WritableRaster wr, RenderedImage src){ |
| // System.out.println("Getting Raster : " + count + " " + wr.getMinX() + "/" + wr.getMinY() + "/" + wr.getWidth() + "/" + wr.getHeight()); |
| // System.out.println("Tile : " + tile.getMinX() + "/" + tile.getMinY() + "/" + tile.getWidth() + "/" + tile.getHeight()); |
| |
| ColorModel cm = getColorModel(); |
| BufferedImage bi |
| = new BufferedImage(cm, |
| wr.createWritableTranslatedChild(0, 0), |
| cm.isAlphaPremultiplied(), null); |
| |
| Graphics2D g = GraphicsUtil.createGraphics(bi, hints); |
| |
| int minX = wr.getMinX(); |
| int minY = wr.getMinY(); |
| int maxX = wr.getWidth(); |
| int maxY = wr.getHeight(); |
| |
| |
| g.setComposite(AlphaComposite.Clear); |
| g.setColor(new Color(0, 0, 0, 0)); |
| g.fillRect(0, 0, maxX, maxY); |
| g.setComposite(AlphaComposite.SrcOver); |
| |
| g.translate(-minX, -minY); |
| |
| // Process initial translate so that tile is |
| // painted to the left of the raster top-left |
| // corner on the first drawRenderedImage |
| int x1 = src.getMinX()+src.getWidth()-1; |
| int y1 = src.getMinY()+src.getHeight()-1; |
| |
| int tileTx = (int)Math.ceil(((minX-x1)/xStep))*xStep; |
| int tileTy = (int)Math.ceil(((minY-y1)/yStep))*yStep; |
| |
| g.translate(tileTx, tileTy); |
| |
| int curX = tileTx - wr.getMinX() + src.getMinX(); |
| int curY = tileTy - wr.getMinY() + src.getMinY(); |
| |
| // System.out.println("Wr: " + wr.getBounds()); |
| // System.out.println("Src : [" + src.getMinX() + ", " + |
| // src.getMinY() + ", " + |
| // src.getWidth() + ", " + |
| // src.getHeight() + "]"); |
| // System.out.println("tileTx/tileTy : " + tileTx + " / " + tileTy); |
| minX = curX; |
| while(curY < maxY) { |
| if (HaltingThread.hasBeenHalted()) |
| return wr; |
| |
| while (curX < maxX) { |
| // System.out.println("curX/curY : " + curX + " / " + curY); |
| // System.out.println("transform : " + |
| // g.getTransform().getTranslateX() + |
| // " / " + |
| // g.getTransform().getTranslateY()); |
| GraphicsUtil.drawImage(g, src); |
| curX += xStep; |
| g.translate(xStep, 0); |
| } |
| curY += yStep; |
| g.translate(minX-curX, yStep); |
| curX = minX; |
| } |
| |
| /*g.setTransform(new AffineTransform()); |
| g.setPaint(colors[count++]); |
| count %= colors.length; |
| |
| g.fillRect(0, 0, maxX, maxY);*/ |
| |
| // Don't coerceData since it will be in the proper alpha state |
| // due to the drawing. |
| // GraphicsUtil.coerceData(wr, src.getColorModel(), alphaPremult); |
| return wr; |
| } |
| |
| protected void fillOutRaster(WritableRaster wr) { |
| if (is_INT_PACK) |
| fillOutRaster_INT_PACK(wr); |
| else |
| fillOutRaster_FALLBACK(wr); |
| |
| } |
| |
| protected void fillOutRaster_INT_PACK(WritableRaster wr) { |
| // System.out.println("Fast copyData"); |
| int x0 = wr.getMinX(); |
| int y0 = wr.getMinY(); |
| int width = wr.getWidth(); |
| int height = wr.getHeight(); |
| |
| SinglePixelPackedSampleModel sppsm; |
| sppsm = (SinglePixelPackedSampleModel)wr.getSampleModel(); |
| |
| final int scanStride = sppsm.getScanlineStride(); |
| DataBufferInt db = (DataBufferInt)wr.getDataBuffer(); |
| final int [] pixels = db.getBankData()[0]; |
| final int base = |
| (db.getOffset() + |
| sppsm.getOffset(x0-wr.getSampleModelTranslateX(), |
| y0-wr.getSampleModelTranslateY())); |
| int step = xStep; |
| for (int x=xStep; x<width; x+=step, step*=2) { |
| int w = step; |
| if (x+w > width) w = width-x; |
| if (w >= 128) { |
| int srcSP = base; |
| int dstSP = base+x; |
| for(int y=0; y<yStep; y++) { |
| System.arraycopy(pixels, srcSP, pixels, dstSP, w); |
| srcSP += scanStride; |
| dstSP += scanStride; |
| } |
| } else { |
| int srcSP = base; |
| int dstSP = base+x; |
| for(int y=0; y<yStep; y++) { |
| int end = srcSP; |
| srcSP += w-1; |
| dstSP += w-1; |
| while(srcSP>=end) |
| pixels[dstSP--] = pixels[srcSP--]; |
| srcSP+=scanStride+1; |
| dstSP+=scanStride+1; |
| } |
| } |
| } |
| |
| step = yStep; |
| for (int y=yStep; y<height; y+=step, step*=2) { |
| int h = step; |
| if (y+h > height) h = height-y; |
| int dstSP = base+y*scanStride; |
| System.arraycopy(pixels, base, pixels, dstSP, h*scanStride); |
| } |
| } |
| |
| protected void fillOutRaster_FALLBACK(WritableRaster wr) { |
| // System.out.println("Fast copyData"); |
| int width = wr.getWidth(); |
| int height = wr.getHeight(); |
| |
| Object data = null; |
| |
| int step = xStep; |
| for (int x=xStep; x<width; x+=step, step*=4) { |
| int w = step; |
| if (x+w > width) w = width-x; |
| data = wr.getDataElements(0, 0, w, yStep, data); |
| wr.setDataElements(x, 0, w, yStep, data); |
| x+=w; |
| |
| if (x >= width) break; |
| if (x+w > width) w = width-x; |
| wr.setDataElements(x, 0, w, yStep, data); |
| x+=w; |
| |
| if (x >= width) break; |
| if (x+w > width) w = width-x; |
| wr.setDataElements(x, 0, w, yStep, data); |
| } |
| |
| step = yStep; |
| for (int y=yStep; y<height; y+=step, step*=4) { |
| int h = step; |
| if (y+h > height) h = height-y; |
| data = wr.getDataElements(0, 0, width, h, data); |
| wr.setDataElements(0, y, width, h, data); |
| y+=h; |
| |
| if (h >= height) break; |
| if (y+h > height) h = height-y; |
| wr.setDataElements(0, y, width, h, data); |
| y+=h; |
| |
| if (h >= height) break; |
| if (y+h > height) h = height-y; |
| wr.setDataElements(0, y, width, h, data); |
| y+=h; |
| } |
| } |
| |
| /** |
| * 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(RenderedImage src, |
| int stepX, int stepY, |
| int width, int height) { |
| int defSz = AbstractTiledRed.getDefaultTileSize(); |
| SampleModel sm = src.getSampleModel(); |
| int w = sm.getWidth(); |
| if (w < defSz) w = defSz; |
| if (w > stepX) w = stepX; |
| // if (w > width) w = width; |
| int h = sm.getHeight(); |
| if (h < defSz) h = defSz; |
| if (h > stepY) h = stepY; |
| // if (h > height) h = height; |
| return sm.createCompatibleSampleModel(w, h); |
| } |
| } |