| /* |
| |
| 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.CompositeContext; |
| import java.awt.Graphics2D; |
| import java.awt.Point; |
| import java.awt.Rectangle; |
| import java.awt.color.ColorSpace; |
| import java.awt.image.BufferedImage; |
| import java.awt.image.ColorModel; |
| import java.awt.image.DataBuffer; |
| import java.awt.image.DirectColorModel; |
| import java.awt.image.Raster; |
| import java.awt.image.SampleModel; |
| import java.awt.image.WritableRaster; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.ArrayList; |
| |
| import org.apache.batik.ext.awt.image.CompositeRule; |
| import org.apache.batik.ext.awt.image.GraphicsUtil; |
| import org.apache.batik.ext.awt.image.PadMode; |
| import org.apache.batik.ext.awt.image.SVGComposite; |
| |
| /** |
| * This is an implementation of an affine operation as a RenderedImage. |
| * Right now the implementation makes use of the AffineBufferedImageOp |
| * to do the work. Eventually this may move to be more tiled in nature. |
| * |
| * @author <a href="mailto:Thomas.DeWeeese@Kodak.com">Thomas DeWeese</a> |
| * @version $Id$ |
| */ |
| public class CompositeRed extends AbstractRed { |
| |
| CompositeRule rule; |
| CompositeContext [] contexts; |
| |
| public CompositeRed(List srcs, CompositeRule rule) { |
| super(); // We _must_ call init... |
| |
| CachableRed src = (CachableRed)srcs.get(0); |
| |
| ColorModel cm = fixColorModel (src); |
| |
| this.rule = rule; |
| |
| SVGComposite comp = new SVGComposite(rule); |
| contexts = new CompositeContext[srcs.size()]; |
| |
| int idx = 0; |
| Iterator i = srcs.iterator(); |
| Rectangle myBounds = null; |
| while (i.hasNext()) { |
| CachableRed cr = (CachableRed)i.next(); |
| |
| contexts[idx++] = comp.createContext(cr.getColorModel(), cm, null); |
| |
| Rectangle newBound = cr.getBounds(); |
| if (myBounds == null) { |
| myBounds = newBound; |
| continue; |
| } |
| |
| switch (rule.getRule()) { |
| case CompositeRule.RULE_IN: |
| if (myBounds.intersects(newBound)) |
| myBounds = myBounds.intersection(newBound); |
| else { |
| myBounds.width = 0; |
| myBounds.height = 0; |
| } |
| break; |
| case CompositeRule.RULE_OUT: |
| // Last node determines bounds... |
| myBounds = newBound; |
| break; |
| default: |
| // myBounds= myBounds.union(newBound); |
| myBounds.add( newBound ); |
| } |
| } |
| |
| if (myBounds == null) |
| throw new IllegalArgumentException |
| ("Composite Operation Must have some source!"); |
| |
| if (rule.getRule() == CompositeRule.RULE_ARITHMETIC) { |
| List vec = new ArrayList( srcs.size() ); |
| i = srcs.iterator(); |
| while (i.hasNext()) { |
| CachableRed cr = (CachableRed)i.next(); |
| Rectangle r = cr.getBounds(); |
| // For arithmetic make sure they are all the same size... |
| if ((r.x != myBounds.x) || |
| (r.y != myBounds.y) || |
| (r.width != myBounds.width) || |
| (r.height != myBounds.height)) |
| cr = new PadRed(cr, myBounds, PadMode.ZERO_PAD, null); |
| vec.add(cr); |
| } |
| srcs = vec; |
| } |
| |
| // fix my sample model so it makes sense given my size. |
| SampleModel sm = fixSampleModel(src, cm, myBounds); |
| |
| // System.out.println("Comp: " + myBounds); |
| // System.out.println(" SM: " + sm.getWidth()+"x"+sm.getHeight()); |
| |
| int defSz = AbstractTiledRed.getDefaultTileSize(); |
| |
| // Make tile(0,0) fall on the closest intersection of defaultSz. |
| int tgX = defSz*(int)Math.floor(myBounds.x/defSz); |
| int tgY = defSz*(int)Math.floor(myBounds.y/defSz); |
| |
| // Finish initializing our base class... |
| init(srcs, myBounds, cm, sm, tgX, tgY, null); |
| } |
| |
| public WritableRaster copyData(WritableRaster wr) { |
| // copyToRaster(wr); |
| genRect(wr); |
| return wr; |
| } |
| |
| public Raster getTile(int x, int y) { |
| int tx = tileGridXOff+x*tileWidth; |
| int ty = tileGridYOff+y*tileHeight; |
| Point pt = new Point(tx, ty); |
| WritableRaster wr = Raster.createWritableRaster(sm, pt); |
| genRect(wr); |
| |
| return wr; |
| } |
| |
| public void emptyRect(WritableRaster wr) { |
| PadRed.ZeroRecter zr = PadRed.ZeroRecter.getZeroRecter(wr); |
| zr.zeroRect(new Rectangle(wr.getMinX(), wr.getMinY(), |
| wr.getWidth(), wr.getHeight())); |
| } |
| |
| public void genRect(WritableRaster wr) { |
| // long startTime = System.currentTimeMillis(); |
| // System.out.println("Comp GenR: " + wr); |
| Rectangle r = wr.getBounds(); |
| |
| int idx = 0; |
| Iterator i = srcs.iterator(); |
| boolean first = true; |
| while (i.hasNext()) { |
| CachableRed cr = (CachableRed)i.next(); |
| if (first) { |
| Rectangle crR = cr.getBounds(); |
| if ((r.x < crR.x) || |
| (r.y < crR.y) || |
| (r.x+r.width > crR.x+crR.width) || |
| (r.y+r.height > crR.y+crR.height)) |
| // Portions outside my bounds, zero them... |
| emptyRect(wr); |
| |
| // Fill in initial image... |
| cr.copyData(wr); |
| |
| if ( ! cr.getColorModel().isAlphaPremultiplied() ) |
| GraphicsUtil.coerceData(wr, cr.getColorModel(), true); |
| first = false; |
| } else { |
| Rectangle crR = cr.getBounds(); |
| if (crR.intersects(r)) { |
| Rectangle smR = crR.intersection(r); |
| Raster ras = cr.getData(smR); |
| WritableRaster smWR = wr.createWritableChild |
| (smR.x, smR.y, smR.width, smR.height, |
| smR.x, smR.y, null); |
| |
| contexts[idx].compose(ras, smWR, smWR); |
| } |
| } |
| |
| idx++; |
| } |
| // long endTime = System.currentTimeMillis(); |
| // System.out.println("Other: " + (endTime-startTime)); |
| } |
| |
| // This is an alternate Implementation that uses drawImage. |
| // In testing this was not significantly faster and it had some |
| // problems with alpha premultiplied. |
| public void genRect_OVER(WritableRaster wr) { |
| // long startTime = System.currentTimeMillis(); |
| // System.out.println("Comp GenR: " + wr); |
| Rectangle r = wr.getBounds(); |
| |
| ColorModel cm = getColorModel(); |
| |
| BufferedImage bi = new BufferedImage |
| (cm, wr.createWritableTranslatedChild(0,0), |
| cm.isAlphaPremultiplied(), null); |
| |
| Graphics2D g2d = GraphicsUtil.createGraphics(bi); |
| g2d.translate(-r.x, -r.y); |
| |
| Iterator i = srcs.iterator(); |
| boolean first = true; |
| while (i.hasNext()) { |
| CachableRed cr = (CachableRed)i.next(); |
| if (first) { |
| Rectangle crR = cr.getBounds(); |
| if ((r.x < crR.x) || |
| (r.y < crR.y) || |
| (r.x+r.width > crR.x+crR.width) || |
| (r.y+r.height > crR.y+crR.height)) |
| // Portions outside my bounds, zero them... |
| emptyRect(wr); |
| |
| // Fill in initial image... |
| cr.copyData(wr); |
| |
| GraphicsUtil.coerceData(wr, cr.getColorModel(), |
| cm.isAlphaPremultiplied()); |
| first = false; |
| } else { |
| GraphicsUtil.drawImage(g2d, cr); |
| } |
| } |
| // long endTime = System.currentTimeMillis(); |
| // System.out.println("OVER: " + (endTime-startTime)); |
| } |
| |
| /** |
| * 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, |
| ColorModel cm, |
| Rectangle bounds) { |
| int defSz = AbstractTiledRed.getDefaultTileSize(); |
| |
| // Make tile(0,0) fall on the closest intersection of defaultSz. |
| int tgX = defSz*(int)Math.floor(bounds.x/defSz); |
| int tgY = defSz*(int)Math.floor(bounds.y/defSz); |
| |
| int tw = (bounds.x+bounds.width)-tgX; |
| int th = (bounds.y+bounds.height)-tgY; |
| |
| SampleModel sm = src.getSampleModel(); |
| |
| int w = sm.getWidth(); |
| if (w < defSz) w = defSz; |
| if (w > tw) w = tw; |
| |
| int h = sm.getHeight(); |
| if (h < defSz) h = defSz; |
| if (h > th) h = th; |
| |
| if ((w <= 0) || (h <= 0)) { |
| w = 1; |
| h = 1; |
| } |
| |
| // System.out.println("tg: " + tgX + "x" + tgY); |
| // System.out.println("t: " + tw + "x" + th); |
| // System.out.println("sz: " + w + "x" + h); |
| |
| return cm.createCompatibleSampleModel(w, h); |
| } |
| |
| protected static ColorModel fixColorModel(CachableRed src) { |
| ColorModel cm = src.getColorModel(); |
| |
| if (cm.hasAlpha()) { |
| if (!cm.isAlphaPremultiplied()) |
| cm = GraphicsUtil.coerceColorModel(cm, true); |
| return cm; |
| } |
| |
| int b = src.getSampleModel().getNumBands()+1; |
| if (b > 4) |
| throw new IllegalArgumentException |
| ("CompositeRed can only handle up to three band images"); |
| |
| int [] masks = new int[4]; |
| for (int i=0; i < b-1; i++) |
| masks[i] = 0xFF0000 >> (8*i); |
| masks[3] = 0xFF << (8*(b-1)); |
| ColorSpace cs = cm.getColorSpace(); |
| |
| return new DirectColorModel(cs, 8*b, masks[0], masks[1], |
| masks[2], masks[3], |
| true, DataBuffer.TYPE_INT); |
| } |
| } |