You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@flex.apache.org by cf...@apache.org on 2012/10/25 21:01:49 UTC
svn commit: r1402274 [27/31] - in
/incubator/flex/sdk/branches/develop/modules/thirdparty/batik/sources/org/apache/flex/forks/batik/ext:
./ awt/ awt/color/ awt/font/ awt/g2d/ awt/geom/ awt/image/ awt/image/codec/
awt/image/codec/jpeg/ awt/image/codec/p...
Added: incubator/flex/sdk/branches/develop/modules/thirdparty/batik/sources/org/apache/flex/forks/batik/ext/awt/image/rendered/FilterAlphaRed.java
URL: http://svn.apache.org/viewvc/incubator/flex/sdk/branches/develop/modules/thirdparty/batik/sources/org/apache/flex/forks/batik/ext/awt/image/rendered/FilterAlphaRed.java?rev=1402274&view=auto
==============================================================================
--- incubator/flex/sdk/branches/develop/modules/thirdparty/batik/sources/org/apache/flex/forks/batik/ext/awt/image/rendered/FilterAlphaRed.java (added)
+++ incubator/flex/sdk/branches/develop/modules/thirdparty/batik/sources/org/apache/flex/forks/batik/ext/awt/image/rendered/FilterAlphaRed.java Thu Oct 25 19:01:43 2012
@@ -0,0 +1,69 @@
+/*
+
+ 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.flex.forks.batik.ext.awt.image.rendered;
+
+import java.awt.image.Raster;
+import java.awt.image.SampleModel;
+import java.awt.image.WritableRaster;
+
+import org.apache.flex.forks.batik.ext.awt.ColorSpaceHintKey;
+
+/**
+ * This strips out the source alpha channel into a one band image.
+ *
+ * @author <a href="mailto:Thomas.DeWeeese@Kodak.com">Thomas DeWeese</a>
+ * @version $Id: FilterAlphaRed.java 475477 2006-11-15 22:44:28Z cam $ */
+public class FilterAlphaRed extends AbstractRed {
+
+ /**
+ * Construct an alpah channel from the given src, according to
+ * the SVG masking rules.
+ *
+ * @param src The image to convert to an alpha channel (mask image)
+ */
+ public FilterAlphaRed(CachableRed src) {
+ super(src, src.getBounds(),
+ src.getColorModel(),
+ src.getSampleModel(),
+ src.getTileGridXOffset(),
+ src.getTileGridYOffset(),
+ null);
+
+ props.put(ColorSpaceHintKey.PROPERTY_COLORSPACE,
+ ColorSpaceHintKey.VALUE_COLORSPACE_ALPHA);
+ }
+
+ public WritableRaster copyData(WritableRaster wr) {
+ // new Exception("FilterAlphaRed: ").printStackTrace();
+ // Get my source.
+ CachableRed srcRed = (CachableRed)getSources().get(0);
+
+ SampleModel sm = srcRed.getSampleModel();
+ if (sm.getNumBands() == 1)
+ // Already one band of data so we just use it...
+ return srcRed.copyData(wr);
+
+ PadRed.ZeroRecter.zeroRect(wr);
+ Raster srcRas = srcRed.getData(wr.getBounds());
+ AbstractRed.copyBand(srcRas, srcRas.getNumBands()-1, wr,
+ wr.getNumBands()-1);
+ return wr;
+ }
+
+}
Propchange: incubator/flex/sdk/branches/develop/modules/thirdparty/batik/sources/org/apache/flex/forks/batik/ext/awt/image/rendered/FilterAlphaRed.java
------------------------------------------------------------------------------
svn:eol-style = native
Added: incubator/flex/sdk/branches/develop/modules/thirdparty/batik/sources/org/apache/flex/forks/batik/ext/awt/image/rendered/FilterAsAlphaRed.java
URL: http://svn.apache.org/viewvc/incubator/flex/sdk/branches/develop/modules/thirdparty/batik/sources/org/apache/flex/forks/batik/ext/awt/image/rendered/FilterAsAlphaRed.java?rev=1402274&view=auto
==============================================================================
--- incubator/flex/sdk/branches/develop/modules/thirdparty/batik/sources/org/apache/flex/forks/batik/ext/awt/image/rendered/FilterAsAlphaRed.java (added)
+++ incubator/flex/sdk/branches/develop/modules/thirdparty/batik/sources/org/apache/flex/forks/batik/ext/awt/image/rendered/FilterAsAlphaRed.java Thu Oct 25 19:01:43 2012
@@ -0,0 +1,153 @@
+/*
+
+ 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.flex.forks.batik.ext.awt.image.rendered;
+
+
+
+import java.awt.Transparency;
+import java.awt.color.ColorSpace;
+import java.awt.image.ComponentColorModel;
+import java.awt.image.DataBuffer;
+import java.awt.image.DataBufferByte;
+import java.awt.image.PixelInterleavedSampleModel;
+import java.awt.image.Raster;
+import java.awt.image.SampleModel;
+import java.awt.image.WritableRaster;
+
+import org.apache.flex.forks.batik.ext.awt.ColorSpaceHintKey;
+
+/**
+ * This converts any source into a mask according to the SVG masking rules.
+ *
+ * @author <a href="mailto:Thomas.DeWeeese@Kodak.com">Thomas DeWeese</a>
+ * @version $Id: FilterAsAlphaRed.java 475477 2006-11-15 22:44:28Z cam $ */
+public class FilterAsAlphaRed extends AbstractRed {
+
+ /**
+ * Construct an alpah channel from the given src, according to
+ * the SVG masking rules.
+ *
+ * @param src The image to convert to an alpha channel (mask image)
+ */
+ public FilterAsAlphaRed(CachableRed src) {
+ super(new Any2LumRed(src),src.getBounds(),
+ new ComponentColorModel
+ (ColorSpace.getInstance(ColorSpace.CS_GRAY),
+ new int [] {8}, false, false,
+ Transparency.OPAQUE,
+ DataBuffer.TYPE_BYTE),
+ new PixelInterleavedSampleModel
+ (DataBuffer.TYPE_BYTE,
+ src.getSampleModel().getWidth(),
+ src.getSampleModel().getHeight(),
+ 1, src.getSampleModel().getWidth(),
+ new int [] { 0 }),
+ src.getTileGridXOffset(),
+ src.getTileGridYOffset(),
+ null);
+
+ props.put(ColorSpaceHintKey.PROPERTY_COLORSPACE,
+ ColorSpaceHintKey.VALUE_COLORSPACE_ALPHA);
+ }
+
+ public WritableRaster copyData(WritableRaster wr) {
+ // Get my source.
+ CachableRed srcRed = (CachableRed)getSources().get(0);
+
+ SampleModel sm = srcRed.getSampleModel();
+ if (sm.getNumBands() == 1)
+ // Already one band of data so we just use it...
+ return srcRed.copyData(wr);
+
+ // Two band case so we need to multiply them...
+ // Note: Our source will always have either one or two bands
+ // since we insert an Any2Lum transform before ourself in the
+ // rendering chain.
+
+ Raster srcRas = srcRed.getData(wr.getBounds());
+ PixelInterleavedSampleModel srcSM;
+ srcSM = (PixelInterleavedSampleModel)srcRas.getSampleModel();
+
+ DataBufferByte srcDB = (DataBufferByte)srcRas.getDataBuffer();
+ byte [] src = srcDB.getData();
+
+ PixelInterleavedSampleModel dstSM;
+ dstSM = (PixelInterleavedSampleModel)wr.getSampleModel();
+
+ DataBufferByte dstDB = (DataBufferByte)wr.getDataBuffer();
+ byte [] dst = dstDB.getData();
+
+ int srcX0 = srcRas.getMinX()-srcRas.getSampleModelTranslateX();
+ int srcY0 = srcRas.getMinY()-srcRas.getSampleModelTranslateY();
+
+ int dstX0 = wr.getMinX()-wr.getSampleModelTranslateX();
+ int dstX1 = dstX0+wr.getWidth()-1;
+ int dstY0 = wr.getMinY()-wr.getSampleModelTranslateY();
+
+ int srcStep = srcSM.getPixelStride();
+ int [] offsets = srcSM.getBandOffsets();
+ int srcLOff = offsets[0];
+ int srcAOff = offsets[1];
+
+ if (srcRed.getColorModel().isAlphaPremultiplied()) {
+ // Lum is already multiplied by alpha so we just copy lum channel.
+ for (int y=0; y<srcRas.getHeight(); y++) {
+ int srcI = srcDB.getOffset() + srcSM.getOffset(srcX0, srcY0);
+ int dstI = dstDB.getOffset() + dstSM.getOffset(dstX0, dstY0);
+ int dstE = dstDB.getOffset() + dstSM.getOffset(dstX1+1,dstY0);
+
+ srcI += srcLOff; // Go to Lum Channel (already mult by alpha).
+
+ while (dstI < dstE) {
+ dst[dstI++] = src[srcI];
+ srcI += srcStep; // Go to next pixel
+ }
+ srcY0++;
+ dstY0++;
+ }
+ }
+ else {
+ // This allows me to pre-adjust my index by srcLOff
+ // Then only add the offset for srcAOff
+ srcAOff = srcAOff-srcLOff;
+
+ for (int y=0; y<srcRas.getHeight(); y++) {
+ int srcI = srcDB.getOffset() + srcSM.getOffset(srcX0, srcY0);
+ int dstI = dstDB.getOffset() + dstSM.getOffset(dstX0, dstY0);
+ int dstE = dstDB.getOffset() + dstSM.getOffset(dstX1+1,dstY0);
+
+ srcI += srcLOff;
+
+ while (dstI < dstE) {
+ int sl = (src[srcI])&0xFF; // LOff already included
+ int sa = (src[srcI+srcAOff])&0xFF;
+ // the + 0x80 forces proper rounding.
+ dst[dstI++] = (byte)((sl*sa+0x80)>>8);
+
+ srcI+= srcStep; // next pixel
+ }
+ srcY0++;
+ dstY0++;
+ }
+ }
+
+ return wr;
+ }
+
+}
Propchange: incubator/flex/sdk/branches/develop/modules/thirdparty/batik/sources/org/apache/flex/forks/batik/ext/awt/image/rendered/FilterAsAlphaRed.java
------------------------------------------------------------------------------
svn:eol-style = native
Added: incubator/flex/sdk/branches/develop/modules/thirdparty/batik/sources/org/apache/flex/forks/batik/ext/awt/image/rendered/FloodRed.java
URL: http://svn.apache.org/viewvc/incubator/flex/sdk/branches/develop/modules/thirdparty/batik/sources/org/apache/flex/forks/batik/ext/awt/image/rendered/FloodRed.java?rev=1402274&view=auto
==============================================================================
--- incubator/flex/sdk/branches/develop/modules/thirdparty/batik/sources/org/apache/flex/forks/batik/ext/awt/image/rendered/FloodRed.java (added)
+++ incubator/flex/sdk/branches/develop/modules/thirdparty/batik/sources/org/apache/flex/forks/batik/ext/awt/image/rendered/FloodRed.java Thu Oct 25 19:01:43 2012
@@ -0,0 +1,127 @@
+/*
+
+ 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.flex.forks.batik.ext.awt.image.rendered;
+
+import java.awt.Color;
+import java.awt.Graphics2D;
+import java.awt.Paint;
+import java.awt.Point;
+import java.awt.Rectangle;
+import java.awt.image.BufferedImage;
+import java.awt.image.ColorModel;
+import java.awt.image.Raster;
+import java.awt.image.SampleModel;
+import java.awt.image.WritableRaster;
+
+import org.apache.flex.forks.batik.ext.awt.image.GraphicsUtil;
+
+/**
+ * This implementation of RenderedImage will generate an infinate
+ * field of a single color. It reports bounds but will in fact render
+ * out to infinity.
+ *
+ * @author <a href="mailto:vincent.hardy@eng.sun.com">Vincent Hardy</a>
+ * @version $Id: FloodRed.java 475477 2006-11-15 22:44:28Z cam $
+ */
+public class FloodRed extends AbstractRed {
+
+ /**
+ * A single tile that we move around as needed...
+ */
+ private WritableRaster raster;
+
+ /**
+ * Construct a fully transparent black image <tt>bounds</tt> size.
+ * @param bounds the bounds of the image (in fact will respond with
+ * any request).
+ */
+ public FloodRed(Rectangle bounds) {
+ this(bounds, new Color(0, 0, 0, 0));
+ }
+
+ /**
+ * Construct a fully transparent image <tt>bounds</tt> size, will
+ * paint one tile with paint. Thus paint should not be a pattered
+ * paint or gradient but should be a solid color.
+ * @param bounds the bounds of the image (in fact will respond with
+ * any request).
+ */
+ public FloodRed(Rectangle bounds,
+ Paint paint) {
+ super(); // We _must_ call init...
+
+ ColorModel cm = GraphicsUtil.sRGB_Unpre;
+
+ int defSz = AbstractTiledRed.getDefaultTileSize();
+
+ int tw = bounds.width;
+ if (tw > defSz) tw = defSz;
+ int th = bounds.height;
+ if (th > defSz) th = defSz;
+
+ // fix my sample model so it makes sense given my size.
+ SampleModel sm = cm.createCompatibleSampleModel(tw, th);
+
+ // Finish initializing our base class...
+ init((CachableRed)null, bounds, cm, sm, 0, 0, null);
+
+ raster = Raster.createWritableRaster(sm, new Point(0, 0));
+ BufferedImage offScreen = new BufferedImage(cm, raster,
+ cm.isAlphaPremultiplied(),
+ null);
+
+ Graphics2D g = GraphicsUtil.createGraphics(offScreen);
+ g.setPaint(paint);
+ g.fillRect(0, 0, bounds.width, bounds.height);
+ g.dispose();
+ }
+
+ public Raster getTile(int x, int y) {
+ // 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);
+ }
+
+ public WritableRaster copyData(WritableRaster wr) {
+ int tx0 = getXTile(wr.getMinX());
+ int ty0 = getYTile(wr.getMinY());
+ int tx1 = getXTile(wr.getMinX()+wr.getWidth() -1);
+ int ty1 = getYTile(wr.getMinY()+wr.getHeight()-1);
+
+ final boolean is_INT_PACK =
+ GraphicsUtil.is_INT_PACK_Data(getSampleModel(), false);
+
+ for (int y=ty0; y<=ty1; y++)
+ for (int x=tx0; x<=tx1; x++) {
+ Raster r = getTile(x, y);
+ if (is_INT_PACK)
+ GraphicsUtil.copyData_INT_PACK(r, wr);
+ else
+ GraphicsUtil.copyData_FALLBACK(r, wr);
+ }
+
+ return wr;
+ }
+}
+
+
+
+
Propchange: incubator/flex/sdk/branches/develop/modules/thirdparty/batik/sources/org/apache/flex/forks/batik/ext/awt/image/rendered/FloodRed.java
------------------------------------------------------------------------------
svn:eol-style = native
Added: incubator/flex/sdk/branches/develop/modules/thirdparty/batik/sources/org/apache/flex/forks/batik/ext/awt/image/rendered/FormatRed.java
URL: http://svn.apache.org/viewvc/incubator/flex/sdk/branches/develop/modules/thirdparty/batik/sources/org/apache/flex/forks/batik/ext/awt/image/rendered/FormatRed.java?rev=1402274&view=auto
==============================================================================
--- incubator/flex/sdk/branches/develop/modules/thirdparty/batik/sources/org/apache/flex/forks/batik/ext/awt/image/rendered/FormatRed.java (added)
+++ incubator/flex/sdk/branches/develop/modules/thirdparty/batik/sources/org/apache/flex/forks/batik/ext/awt/image/rendered/FormatRed.java Thu Oct 25 19:01:43 2012
@@ -0,0 +1,204 @@
+/*
+
+ 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.flex.forks.batik.ext.awt.image.rendered;
+
+
+import java.awt.Point;
+import java.awt.Transparency;
+import java.awt.color.ColorSpace;
+import java.awt.image.BufferedImage;
+import java.awt.image.ColorModel;
+import java.awt.image.ComponentColorModel;
+import java.awt.image.ComponentSampleModel;
+import java.awt.image.DataBuffer;
+import java.awt.image.DirectColorModel;
+import java.awt.image.Raster;
+import java.awt.image.SampleModel;
+import java.awt.image.SinglePixelPackedSampleModel;
+import java.awt.image.WritableRaster;
+
+import org.apache.flex.forks.batik.ext.awt.image.GraphicsUtil;
+
+
+/**
+ * This allows you to specify the ColorModel, Alpha premult and/or
+ * SampleModel to be used for output. If the input image lacks
+ * Alpha and alpha is included in output then it is filled with
+ * alpha=1. In all other cases bands are simply copied.
+ *
+ * @author <a href="mailto:Thomas.DeWeeese@Kodak.com">Thomas DeWeese</a>
+ * @version $Id: FormatRed.java 478363 2006-11-22 23:01:13Z dvholten $
+ */
+public class FormatRed extends AbstractRed {
+
+ public static CachableRed construct(CachableRed src, ColorModel cm) {
+ ColorModel srcCM = src.getColorModel();
+ if ((cm.hasAlpha() != srcCM.hasAlpha()) ||
+ (cm.isAlphaPremultiplied() != srcCM.isAlphaPremultiplied()))
+ return new FormatRed(src, cm);
+
+ if (cm.getNumComponents() != srcCM.getNumComponents())
+ throw new IllegalArgumentException
+ ("Incompatible ColorModel given");
+
+
+ if ((srcCM instanceof ComponentColorModel) &&
+ (cm instanceof ComponentColorModel))
+ return src;
+
+ if ((srcCM instanceof DirectColorModel) &&
+ (cm instanceof DirectColorModel))
+ return src;
+
+ return new FormatRed(src, cm);
+ }
+
+ /**
+ * Construct an instance of CachableRed around a BufferedImage.
+ */
+ public FormatRed(CachableRed cr, SampleModel sm) {
+ super(cr, cr.getBounds(),
+ makeColorModel(cr, sm), sm,
+ cr.getTileGridXOffset(),
+ cr.getTileGridYOffset(),
+ null);
+ }
+
+ public FormatRed(CachableRed cr, ColorModel cm) {
+ super(cr, cr.getBounds(),
+ cm, makeSampleModel(cr, cm),
+ cr.getTileGridXOffset(),
+ cr.getTileGridYOffset(),
+ null);
+ }
+
+ /**
+ * fetch the source image for this node.
+ */
+ public CachableRed getSource() {
+ return (CachableRed)getSources().get(0);
+ }
+
+ public Object getProperty(String name) {
+ return getSource().getProperty(name);
+ }
+
+ public String [] getPropertyNames() {
+ return getSource().getPropertyNames();
+ }
+
+ public WritableRaster copyData(WritableRaster wr) {
+ ColorModel cm = getColorModel();
+ CachableRed cr = getSource();
+ ColorModel srcCM = cr.getColorModel();
+ SampleModel srcSM = cr.getSampleModel();
+ srcSM = srcSM.createCompatibleSampleModel(wr.getWidth(),
+ wr.getHeight());
+ WritableRaster srcWR;
+ srcWR = Raster.createWritableRaster(srcSM, new Point(wr.getMinX(),
+ wr.getMinY()));
+ getSource().copyData(srcWR);
+
+ BufferedImage srcBI = new BufferedImage
+ (srcCM, srcWR.createWritableTranslatedChild(0,0),
+ srcCM.isAlphaPremultiplied(), null);
+ BufferedImage dstBI = new BufferedImage
+ (cm, wr.createWritableTranslatedChild(0,0),
+ cm.isAlphaPremultiplied(), null);
+
+ GraphicsUtil.copyData(srcBI, dstBI);
+
+ return wr;
+ }
+
+ public static SampleModel makeSampleModel(CachableRed cr, ColorModel cm) {
+ SampleModel srcSM = cr.getSampleModel();
+ return cm.createCompatibleSampleModel(srcSM.getWidth(),
+ srcSM.getHeight());
+ }
+
+ public static ColorModel makeColorModel(CachableRed cr, SampleModel sm) {
+ ColorModel srcCM = cr.getColorModel();
+ ColorSpace cs = srcCM.getColorSpace();
+
+ int bands = sm.getNumBands();
+
+ int bits;
+ int dt = sm.getDataType();
+ switch (dt) {
+ case DataBuffer.TYPE_BYTE: bits=8; break;
+ case DataBuffer.TYPE_SHORT: bits=16; break;
+ case DataBuffer.TYPE_USHORT: bits=16; break;
+ case DataBuffer.TYPE_INT: bits=32; break;
+ default:
+ throw new IllegalArgumentException
+ ("Unsupported DataBuffer type: " + dt);
+ }
+
+ boolean hasAlpha = srcCM.hasAlpha();
+ if (hasAlpha){
+ // if Src has Alpha then our out bands must
+ // either be one less than the source (no out alpha)
+ // or equal (still has alpha)
+ if (bands == srcCM.getNumComponents()-1)
+ hasAlpha = false;
+ else if (bands != srcCM.getNumComponents())
+ throw new IllegalArgumentException
+ ("Incompatible number of bands in and out");
+ } else {
+ if (bands == srcCM.getNumComponents()+1)
+ hasAlpha = true;
+ else if (bands != srcCM.getNumComponents())
+ throw new IllegalArgumentException
+ ("Incompatible number of bands in and out");
+ }
+
+ boolean preMult = srcCM.isAlphaPremultiplied();
+ if (!hasAlpha)
+ preMult = false;
+
+ if (sm instanceof ComponentSampleModel) {
+ int [] bitsPer = new int[bands];
+ for (int i=0; i<bands; i++)
+ bitsPer[i] = bits;
+
+ return new ComponentColorModel
+ (cs, bitsPer, hasAlpha, preMult,
+ hasAlpha ? Transparency.TRANSLUCENT : Transparency.OPAQUE,
+ dt);
+ } else if (sm instanceof SinglePixelPackedSampleModel) {
+ SinglePixelPackedSampleModel sppsm;
+ sppsm = (SinglePixelPackedSampleModel)sm;
+ int[] masks = sppsm.getBitMasks();
+ if (bands == 4)
+ return new DirectColorModel
+ (cs, bits, masks[0], masks[1], masks[2], masks[3],
+ preMult, dt);
+ else if (bands == 3)
+ return new DirectColorModel
+ (cs, bits, masks[0], masks[1], masks[2], 0x0,
+ preMult, dt);
+ else
+ throw new IllegalArgumentException
+ ("Incompatible number of bands out for ColorModel");
+ }
+ throw new IllegalArgumentException
+ ("Unsupported SampleModel Type");
+ }
+}
Propchange: incubator/flex/sdk/branches/develop/modules/thirdparty/batik/sources/org/apache/flex/forks/batik/ext/awt/image/rendered/FormatRed.java
------------------------------------------------------------------------------
svn:eol-style = native
Added: incubator/flex/sdk/branches/develop/modules/thirdparty/batik/sources/org/apache/flex/forks/batik/ext/awt/image/rendered/GaussianBlurRed8Bit.java
URL: http://svn.apache.org/viewvc/incubator/flex/sdk/branches/develop/modules/thirdparty/batik/sources/org/apache/flex/forks/batik/ext/awt/image/rendered/GaussianBlurRed8Bit.java?rev=1402274&view=auto
==============================================================================
--- incubator/flex/sdk/branches/develop/modules/thirdparty/batik/sources/org/apache/flex/forks/batik/ext/awt/image/rendered/GaussianBlurRed8Bit.java (added)
+++ incubator/flex/sdk/branches/develop/modules/thirdparty/batik/sources/org/apache/flex/forks/batik/ext/awt/image/rendered/GaussianBlurRed8Bit.java Thu Oct 25 19:01:43 2012
@@ -0,0 +1,578 @@
+/*
+
+ 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.flex.forks.batik.ext.awt.image.rendered;
+
+import java.awt.Rectangle;
+import java.awt.RenderingHints;
+import java.awt.color.ColorSpace;
+import java.awt.image.ColorModel;
+import java.awt.image.ConvolveOp;
+import java.awt.image.DataBuffer;
+import java.awt.image.DataBufferInt;
+import java.awt.image.DirectColorModel;
+import java.awt.image.Kernel;
+import java.awt.image.Raster;
+import java.awt.image.SampleModel;
+import java.awt.image.SinglePixelPackedSampleModel;
+import java.awt.image.WritableRaster;
+
+import org.apache.flex.forks.batik.ext.awt.image.GraphicsUtil;
+
+/**
+ * This implementation of RenderableImage will render its input
+ * GraphicsNode on demand for tiles.
+ *
+ * @author <a href="mailto:vincent.hardy@eng.sun.com">Vincent Hardy</a>
+ * @version $Id: GaussianBlurRed8Bit.java 478276 2006-11-22 18:33:37Z dvholten $
+ */
+public class GaussianBlurRed8Bit extends AbstractRed {
+
+ int xinset, yinset;
+ double stdDevX, stdDevY;
+ RenderingHints hints;
+ ConvolveOp [] convOp = new ConvolveOp [2];
+ int dX, dY;
+
+ /**
+ * Construct a blurred version of <tt>src</tt>, by blurring with a
+ * gaussian kernel with standard Deviation of <tt>stdDev</tt> pixels.
+ * @param src The source image to blur
+ * @param stdDev The Standard Deviation of the Gaussian kernel.
+ * @param rh Rendering hints.
+ */
+ public GaussianBlurRed8Bit(CachableRed src,
+ double stdDev,
+ RenderingHints rh) {
+ this(src, stdDev, stdDev, rh);
+ }
+
+ /**
+ * Construct a blurred version of <tt>src</tt>, by blurring with a
+ * gaussian kernel with standard Deviation of <tt>stdDev</tt> pixels.
+ * @param src The source image to blur
+ * @param stdDevX The Standard Deviation of the Gaussian kernel in X
+ * @param stdDevY The Standard Deviation of the Gaussian kernel in Y
+ * @param rh Rendering hints.
+ */
+ public GaussianBlurRed8Bit(CachableRed src,
+ double stdDevX, double stdDevY,
+ RenderingHints rh) {
+ super(); // Remember to call super.init()
+
+ this.stdDevX = stdDevX;
+ this.stdDevY = stdDevY;
+ this.hints = rh;
+
+ xinset = surroundPixels(stdDevX, rh);
+ yinset = surroundPixels(stdDevY, rh);
+
+ Rectangle myBounds = src.getBounds();
+ myBounds.x += xinset;
+ myBounds.y += yinset;
+ myBounds.width -= 2*xinset;
+ myBounds.height -= 2*yinset;
+ if ((myBounds.width <= 0) ||
+ (myBounds.height <= 0)) {
+ myBounds.width=0;
+ myBounds.height=0;
+ }
+
+ ColorModel cm = fixColorModel(src);
+ SampleModel sm = src.getSampleModel();
+ int tw = sm.getWidth();
+ int th = sm.getHeight();
+ if (tw > myBounds.width) tw = myBounds.width;
+ if (th > myBounds.height) th = myBounds.height;
+ sm = cm.createCompatibleSampleModel(tw, th);
+
+ init(src, myBounds, cm, sm,
+ src.getTileGridXOffset()+xinset,
+ src.getTileGridYOffset()+yinset, null);
+
+ boolean highQuality = ((hints != null) &&
+ RenderingHints.VALUE_RENDER_QUALITY.equals
+ (hints.get(RenderingHints.KEY_RENDERING)));
+
+ // System.out.println("StdDev: " + stdDevX + "x" + stdDevY);
+ if ((xinset != 0) && ((stdDevX < 2) || highQuality))
+ convOp[0] = new ConvolveOp(makeQualityKernelX(xinset*2+1));
+ else
+ dX = (int)Math.floor(DSQRT2PI*stdDevX+0.5f);
+
+ if ((yinset != 0) && ((stdDevY < 2) || highQuality))
+ convOp[1] = new ConvolveOp(makeQualityKernelY(yinset*2+1));
+ else
+ dY = (int)Math.floor(DSQRT2PI*stdDevY+0.5f);
+ }
+
+ /**
+ * Constant: sqrt(2*PI)
+ */
+ static final float SQRT2PI = (float)Math.sqrt(2*Math.PI);
+
+ /**
+ * Constant: 3*sqrt(2*PI)/4
+ */
+ static final float DSQRT2PI = SQRT2PI*3f/4f;
+
+ /**
+ * Constant: precision used in computation of the Kernel radius
+ */
+ static final float precision = 0.499f;
+
+ /**
+ * Calculate the number of surround pixels required for a given
+ * standard Deviation.
+ */
+ public static int surroundPixels(double stdDev) {
+ return surroundPixels(stdDev, null);
+ }
+
+ /**
+ * Calculate the number of surround pixels required for a given
+ * standard Deviation. Also takes into account rendering quality
+ * hint.
+ */
+ public static int surroundPixels(double stdDev, RenderingHints hints) {
+ boolean highQuality = ((hints != null) &&
+ RenderingHints.VALUE_RENDER_QUALITY.equals
+ (hints.get(RenderingHints.KEY_RENDERING)));
+
+ if ((stdDev < 2) || highQuality) {
+ // Start with 1/2 the zero box enery.
+ float areaSum = (float)(0.5/(stdDev*SQRT2PI));
+ int i=0;
+ while (areaSum < precision) {
+ areaSum += (float)(Math.pow(Math.E, -i*i/(2*stdDev*stdDev)) /
+ (stdDev*SQRT2PI));
+ i++;
+ }
+
+ return i;
+ }
+
+ //compute d
+ int diam = (int)Math.floor(DSQRT2PI*stdDev+0.5f);
+ if (diam%2 == 0)
+ return diam-1 + diam/2; // even case
+ else
+ return diam-2 + diam/2; // Odd case
+ }
+
+ /*
+ * Here we compute the data for the one-dimensional kernel of
+ * length '2*(radius-1) + 1'
+ *
+ * @param radius stdDeviationX or stdDeviationY.
+ * @see #makeQualityKernels */
+ private float [] computeQualityKernelData(int len, double stdDev){
+ final float[] kernelData = new float [len];
+
+ int mid = len/2;
+ float sum = 0; // Used to normalise the kernel
+ for(int i=0; i<len; i++){
+ kernelData[i] = (float)(Math.pow(Math.E, -(i-mid)*(i-mid)/
+ (2*stdDev*stdDev)) /
+ (SQRT2PI*stdDev));
+ sum += kernelData[i];
+ }
+
+ // Normalise: make elements sum to 1
+ for (int i=0; i<len; i++)
+ kernelData[i] /= sum;
+
+ return kernelData;
+ }
+
+ private Kernel makeQualityKernelX(int len) {
+ return new Kernel(len, 1, computeQualityKernelData(len, stdDevX));
+ }
+
+ private Kernel makeQualityKernelY(int len) {
+ return new Kernel(1, len, computeQualityKernelData(len, stdDevY));
+ }
+
+ public WritableRaster copyData(WritableRaster wr) {
+ // Get my source.
+ CachableRed src = (CachableRed)getSources().get(0);
+
+ Rectangle r = wr.getBounds();
+ r.x -= xinset;
+ r.y -= yinset;
+ r.width += 2*xinset;
+ r.height += 2*yinset;
+
+ // System.out.println("Gaussian GenR: " + wr);
+ // System.out.println("SrcReq: " + r);
+
+ ColorModel srcCM = src.getColorModel();
+
+ WritableRaster tmpR1=null, tmpR2=null;
+
+ tmpR1 = srcCM.createCompatibleWritableRaster(r.width, r.height);
+ {
+ WritableRaster fill;
+ fill = tmpR1.createWritableTranslatedChild(r.x, r.y);
+ src.copyData(fill);
+ }
+ if (srcCM.hasAlpha() && !srcCM.isAlphaPremultiplied())
+ GraphicsUtil.coerceData(tmpR1, srcCM, true);
+
+ // For the blur box approx we can use dest as our intermediate
+ // otherwise we let it default to null which means we create a new
+ // one...
+
+ // this lets the Vertical conv know how much is junk, so it
+ // doesn't bother to convolve the top and bottom edges
+ int skipX;
+ // long t1 = System.currentTimeMillis();
+ if (xinset == 0) {
+ skipX = 0;
+ } else if (convOp[0] != null) {
+ tmpR2 = getColorModel().createCompatibleWritableRaster
+ (r.width, r.height);
+ tmpR2 = convOp[0].filter(tmpR1, tmpR2);
+ skipX = convOp[0].getKernel().getXOrigin();
+
+ // Swap them...
+ WritableRaster tmp = tmpR1;
+ tmpR1 = tmpR2;
+ tmpR2 = tmp;
+ } else {
+ if ((dX&0x01) == 0){
+ tmpR1 = boxFilterH(tmpR1, tmpR1, 0, 0, dX, dX/2);
+ tmpR1 = boxFilterH(tmpR1, tmpR1, dX/2, 0, dX, dX/2-1);
+ tmpR1 = boxFilterH(tmpR1, tmpR1, dX-1, 0, dX+1, dX/2);
+ skipX = dX-1 + dX/2;
+ } else {
+ tmpR1 = boxFilterH(tmpR1, tmpR1, 0, 0, dX, dX/2);
+ tmpR1 = boxFilterH(tmpR1, tmpR1, dX/2, 0, dX, dX/2);
+ tmpR1 = boxFilterH(tmpR1, tmpR1, dX-2, 0, dX, dX/2);
+ skipX = dX-2 + dX/2;
+ }
+ }
+
+ if (yinset == 0) {
+ tmpR2 = tmpR1;
+ } else if (convOp[1] != null) {
+ if (tmpR2 == null) {
+ tmpR2 = getColorModel().createCompatibleWritableRaster
+ (r.width, r.height);
+ }
+ tmpR2 = convOp[1].filter(tmpR1, tmpR2);
+ } else {
+ if ((dY&0x01) == 0){
+ tmpR1 = boxFilterV(tmpR1, tmpR1, skipX, 0, dY, dY/2);
+ tmpR1 = boxFilterV(tmpR1, tmpR1, skipX, dY/2, dY, dY/2-1);
+ tmpR1 = boxFilterV(tmpR1, tmpR1, skipX, dY-1, dY+1, dY/2);
+ }
+ else {
+ tmpR1 = boxFilterV(tmpR1, tmpR1, skipX, 0, dY, dY/2);
+ tmpR1 = boxFilterV(tmpR1, tmpR1, skipX, dY/2, dY, dY/2);
+ tmpR1 = boxFilterV(tmpR1, tmpR1, skipX, dY-2, dY, dY/2);
+ }
+ tmpR2 = tmpR1;
+ }
+ // long t2 = System.currentTimeMillis();
+ // System.out.println("Time: " + (t2-t1) +
+ // (((convOp[0] != null) || (convOp[1] != null))?
+ // " ConvOp":""));
+ // System.out.println("Rasters WR :" + wr.getBounds());
+ // System.out.println(" tmp:" + tmpR2.getBounds());
+ // System.out.println(" bounds:" + getBounds());
+ // System.out.println(" skipX:" + skipX +
+ // " dx:" + dX + " Dy: " + dY);
+ tmpR2 = tmpR2.createWritableTranslatedChild(r.x, r.y);
+ GraphicsUtil.copyData(tmpR2, wr);
+
+ return wr;
+ }
+
+ private WritableRaster boxFilterH(Raster src, WritableRaster dest,
+ int skipX, int skipY,
+ int boxSz, int loc) {
+
+ final int w = src.getWidth();
+ final int h = src.getHeight();
+
+ // Check if the raster is wide enough to do _any_ work
+ if (w < (2*skipX)+boxSz) return dest;
+ if (h < (2*skipY)) return dest;
+
+ final SinglePixelPackedSampleModel srcSPPSM =
+ (SinglePixelPackedSampleModel)src.getSampleModel();
+
+ final SinglePixelPackedSampleModel dstSPPSM =
+ (SinglePixelPackedSampleModel)dest.getSampleModel();
+
+ // Stride is the distance between two consecutive column elements,
+ // in the one-dimention dataBuffer
+ final int srcScanStride = srcSPPSM.getScanlineStride();
+ final int dstScanStride = dstSPPSM.getScanlineStride();
+
+ // Access the integer buffer for each image.
+ DataBufferInt srcDB = (DataBufferInt)src.getDataBuffer();
+ DataBufferInt dstDB = (DataBufferInt)dest.getDataBuffer();
+
+ // Offset defines where in the stack the real data begin
+ final int srcOff
+ = (srcDB.getOffset() +
+ srcSPPSM.getOffset
+ (src.getMinX()-src.getSampleModelTranslateX(),
+ src.getMinY()-src.getSampleModelTranslateY()));
+ final int dstOff
+ = (dstDB.getOffset() +
+ dstSPPSM.getOffset
+ (dest.getMinX()-dest.getSampleModelTranslateX(),
+ dest.getMinY()-dest.getSampleModelTranslateY()));
+
+ // Access the pixel value array
+ final int[] srcPixels = srcDB.getBankData()[0];
+ final int[] destPixels = dstDB.getBankData()[0];
+
+ final int [] buffer = new int [boxSz];
+ int curr, prev;
+
+ // Fixed point normalization factor (8.24)
+ int scale = (1<<24)/boxSz;
+
+ /*
+ * System.out.println("Info: srcOff: " + srcOff +
+ * " x: " + skipX +
+ * " y: " + skipY +
+ * " w: " + w +
+ * " h: " + h +
+ * " boxSz " + boxSz +
+ * " srcStride: " + srcScanStride);
+ */
+
+ for (int y=skipY; y<(h-skipY); y++) {
+ int sp = srcOff + y*srcScanStride;
+ int dp = dstOff + y*dstScanStride;
+ int rowEnd = sp + (w-skipX);
+
+ int k = 0;
+ int sumA = 0;
+ int sumR = 0;
+ int sumG = 0;
+ int sumB = 0;
+
+ sp += skipX;
+ int end = sp+boxSz;
+
+ while (sp < end) {
+ curr = buffer[k] = srcPixels[sp];
+ sumA += (curr>>> 24);
+ sumR += (curr >> 16)&0xFF;
+ sumG += (curr >> 8)&0xFF;
+ sumB += (curr )&0xFF;
+ k++;
+ sp++;
+ }
+
+ dp += skipX + loc;
+ prev = destPixels[dp] = (( (sumA*scale)&0xFF000000) |
+ (((sumR*scale)&0xFF000000)>>>8) |
+ (((sumG*scale)&0xFF000000)>>>16) |
+ (((sumB*scale)&0xFF000000)>>>24));
+ dp++;
+ k=0;
+ while (sp < rowEnd) {
+ curr = buffer[k];
+ if (curr == srcPixels[sp]) {
+ destPixels[dp] = prev;
+ } else {
+ sumA -= (curr>>> 24);
+ sumR -= (curr >> 16)&0xFF;
+ sumG -= (curr >> 8)&0xFF;
+ sumB -= (curr )&0xFF;
+
+ curr = buffer[k] = srcPixels[sp];
+
+ sumA += (curr>>> 24);
+ sumR += (curr >> 16)&0xFF;
+ sumG += (curr >> 8)&0xFF;
+ sumB += (curr )&0xFF;
+ prev = destPixels[dp] = (( (sumA*scale)&0xFF000000) |
+ (((sumR*scale)&0xFF000000)>>>8) |
+ (((sumG*scale)&0xFF000000)>>>16) |
+ (((sumB*scale)&0xFF000000)>>>24));
+ }
+ k = (k+1)%boxSz;
+ sp++;
+ dp++;
+ }
+ }
+ return dest;
+ }
+
+ private WritableRaster boxFilterV(Raster src, WritableRaster dest,
+ int skipX, int skipY,
+ int boxSz, int loc) {
+
+ final int w = src.getWidth();
+ final int h = src.getHeight();
+
+ // Check if the raster is wide enough to do _any_ work
+ if (w < (2*skipX)) return dest;
+ if (h < (2*skipY)+boxSz) return dest;
+
+ final SinglePixelPackedSampleModel srcSPPSM =
+ (SinglePixelPackedSampleModel)src.getSampleModel();
+
+ final SinglePixelPackedSampleModel dstSPPSM =
+ (SinglePixelPackedSampleModel)dest.getSampleModel();
+
+ // Stride is the distance between two consecutive column elements,
+ // in the one-dimention dataBuffer
+ final int srcScanStride = srcSPPSM.getScanlineStride();
+ final int dstScanStride = dstSPPSM.getScanlineStride();
+
+ // Access the integer buffer for each image.
+ DataBufferInt srcDB = (DataBufferInt)src.getDataBuffer();
+ DataBufferInt dstDB = (DataBufferInt)dest.getDataBuffer();
+
+ // Offset defines where in the stack the real data begin
+ final int srcOff
+ = (srcDB.getOffset() +
+ srcSPPSM.getOffset
+ (src.getMinX()-src.getSampleModelTranslateX(),
+ src.getMinY()-src.getSampleModelTranslateY()));
+ final int dstOff
+ = (dstDB.getOffset() +
+ dstSPPSM.getOffset
+ (dest.getMinX()-dest.getSampleModelTranslateX(),
+ dest.getMinY()-dest.getSampleModelTranslateY()));
+
+
+ // Access the pixel value array
+ final int[] srcPixels = srcDB.getBankData()[0];
+ final int[] destPixels = dstDB.getBankData()[0];
+
+ final int [] buffer = new int [boxSz];
+ int curr, prev;
+
+ // Fixed point normalization factor (8.24)
+ final int scale = (1<<24)/boxSz;
+
+ /*
+ * System.out.println("Info: srcOff: " + srcOff +
+ * " x: " + skipX +
+ * " y: " + skipY +
+ * " w: " + w +
+ * " h: " + h +
+ * " boxSz " + boxSz +
+ * " srcStride: " + srcScanStride);
+ */
+
+ for (int x=skipX; x<(w-skipX); x++) {
+ int sp = srcOff + x;
+ int dp = dstOff + x;
+ int colEnd = sp + (h-skipY)*srcScanStride;
+
+ int k=0;
+ int sumA = 0;
+ int sumR = 0;
+ int sumG = 0;
+ int sumB = 0;
+
+ sp += skipY*srcScanStride;
+ int end = sp+(boxSz*srcScanStride);
+
+ while (sp < end) {
+ curr = buffer[k] = srcPixels[sp];
+ sumA += (curr>>> 24);
+ sumR += (curr >> 16)&0xFF;
+ sumG += (curr >> 8)&0xFF;
+ sumB += (curr )&0xFF;
+ k++;
+ sp+=srcScanStride;
+ }
+
+
+ dp += (skipY + loc)*dstScanStride;
+ prev = destPixels[dp] = (( (sumA*scale)&0xFF000000) |
+ (((sumR*scale)&0xFF000000)>>>8) |
+ (((sumG*scale)&0xFF000000)>>>16) |
+ (((sumB*scale)&0xFF000000)>>>24));
+ dp+=dstScanStride;
+ k=0;
+ while (sp < colEnd) {
+ curr = buffer[k];
+ if (curr == srcPixels[sp]) {
+ destPixels[dp] = prev;
+ } else {
+ sumA -= (curr>>> 24);
+ sumR -= (curr >> 16)&0xFF;
+ sumG -= (curr >> 8)&0xFF;
+ sumB -= (curr )&0xFF;
+
+ curr = buffer[k] = srcPixels[sp];
+
+ sumA += (curr>>> 24);
+ sumR += (curr >> 16)&0xFF;
+ sumG += (curr >> 8)&0xFF;
+ sumB += (curr )&0xFF;
+ prev = destPixels[dp] = (( (sumA*scale)&0xFF000000) |
+ (((sumR*scale)&0xFF000000)>>>8) |
+ (((sumG*scale)&0xFF000000)>>>16) |
+ (((sumB*scale)&0xFF000000)>>>24));
+ }
+ k = (k+1)%boxSz;
+ sp+=srcScanStride;
+ dp+=dstScanStride;
+ }
+ }
+ return dest;
+ }
+
+ protected static ColorModel fixColorModel(CachableRed src) {
+ ColorModel cm = src.getColorModel();
+
+ int b = src.getSampleModel().getNumBands();
+ int [] masks = new int[4];
+ switch (b) {
+ case 1:
+ masks[0] = 0xFF;
+ break;
+ case 2:
+ masks[0] = 0x00FF;
+ masks[3] = 0xFF00;
+ break;
+ case 3:
+ masks[0] = 0xFF0000;
+ masks[1] = 0x00FF00;
+ masks[2] = 0x0000FF;
+ break;
+ case 4:
+ masks[0] = 0x00FF0000;
+ masks[1] = 0x0000FF00;
+ masks[2] = 0x000000FF;
+ masks[3] = 0xFF000000;
+ break;
+ default:
+ throw new IllegalArgumentException
+ ("GaussianBlurRed8Bit only supports one to four band images");
+ }
+ ColorSpace cs = cm.getColorSpace();
+ return new DirectColorModel(cs, 8*b, masks[0], masks[1],
+ masks[2], masks[3],
+ true, DataBuffer.TYPE_INT);
+ }
+}
Propchange: incubator/flex/sdk/branches/develop/modules/thirdparty/batik/sources/org/apache/flex/forks/batik/ext/awt/image/rendered/GaussianBlurRed8Bit.java
------------------------------------------------------------------------------
svn:eol-style = native
Added: incubator/flex/sdk/branches/develop/modules/thirdparty/batik/sources/org/apache/flex/forks/batik/ext/awt/image/rendered/IndexImage.java
URL: http://svn.apache.org/viewvc/incubator/flex/sdk/branches/develop/modules/thirdparty/batik/sources/org/apache/flex/forks/batik/ext/awt/image/rendered/IndexImage.java?rev=1402274&view=auto
==============================================================================
--- incubator/flex/sdk/branches/develop/modules/thirdparty/batik/sources/org/apache/flex/forks/batik/ext/awt/image/rendered/IndexImage.java (added)
+++ incubator/flex/sdk/branches/develop/modules/thirdparty/batik/sources/org/apache/flex/forks/batik/ext/awt/image/rendered/IndexImage.java Thu Oct 25 19:01:43 2012
@@ -0,0 +1,800 @@
+/*
+
+ 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.flex.forks.batik.ext.awt.image.rendered;
+
+import java.awt.Graphics2D;
+import java.awt.Point;
+import java.awt.RenderingHints;
+import java.awt.image.BufferedImage;
+import java.awt.image.ColorModel;
+import java.awt.image.DataBuffer;
+import java.awt.image.IndexColorModel;
+import java.awt.image.MultiPixelPackedSampleModel;
+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.flex.forks.batik.ext.awt.image.GraphicsUtil;
+
+/**
+ * This class implements an adaptive palette generator to reduce images to a
+ * specified number of colors.
+ *
+ * Ideally this would also support a better dither option than just
+ * the JDK's pattern dither.
+ *
+ * The algorithm used is the 'Median Cut Algorithm' published by
+ * Paul Heckbert in early '80s.
+ *
+ * @author <a href="mailto:deweese@apache.org">Thomas DeWeese</a>
+ * @author <a href="mailto:jun@oop-reserch.com">Jun Inamori</a>
+ * @version $Id: IndexImage.java 489226 2006-12-21 00:05:36Z cam $
+ */
+
+public class IndexImage{
+
+ /**
+ * Used to track a color and the number of pixels of that colors
+ */
+ private static class Counter {
+
+ /**
+ * contains the 'packed' rgb-color for this point.
+ * Must not change after construction!
+ */
+ final int val;
+
+ /**
+ * the number of image-pixels with this color.
+ */
+ int count=1;
+
+ Counter(int val) { this.val = val; }
+
+ boolean add(int val) {
+ // See if the value matches us...
+ if (this.val != val)
+ return false;
+ count++;
+ return true;
+ }
+
+ /**
+ * convert the color-point of this counter to an rgb-array.
+ * To avoid creating lots of arrays, the caller passes the
+ * array to store the result.
+ *
+ * @param rgb an int[ 3 ] to store the result.
+ * @return an int-array with rgb-color-values (same as rgb-parameter)
+ */
+ int[] getRgb( int[] rgb ){
+ rgb[ Cube.RED ] = (val&0xFF0000)>>16;
+ rgb[ Cube.GRN ] = (val&0x00FF00)>>8;
+ rgb[ Cube.BLU ] = (val&0x0000FF);
+ return rgb;
+ }
+ }
+
+ /**
+ * Used to define a cube of the colorspace. The cube can be split
+ * approximagely in half to generate two cubes.
+ */
+ private static class Cube {
+ static final byte[] RGB_BLACK= new byte[]{ 0, 0, 0 };
+
+ int[] min = {0, 0, 0}, max={255,255,255};
+
+ boolean done = false;
+
+
+ /**
+ * the colors-array is not modified - in fact, all cubes use
+ * the same colors-array. The Counter contains the
+ * rgb-color-code and the count of pixels with this color.
+ */
+ final Counter[][] colors;
+
+ /**
+ * the number of color-points in this cube.
+ */
+ int count=0;
+
+ static final int RED = 0;
+ static final int GRN = 1;
+ static final int BLU = 2;
+
+ /**
+ * Define a new cube.
+ * @param colors contains the 3D color histogram to be subdivided
+ * @param count the total number of pixels in the 3D histogram.
+ */
+ Cube( Counter[][] colors, int count) {
+ this.colors = colors;
+ this.count = count;
+ }
+
+ /**
+ * If this returns true then the cube can not be subdivided any
+ * further
+ */
+ public boolean isDone() { return done; }
+
+ /**
+ * check, if the color defined by val[] is inside this cube.
+ *
+ * @param val int[ 3 ] containing r,g,b-values
+ * @return true when color is inside this cube
+ */
+ private boolean contains( int[] val ){
+
+ int vRed = val[ RED ]; // just save some array-accesses
+ int vGrn = val[ GRN ];
+ int vBlu = val[ BLU ];
+
+ return (
+ ( ( min[ RED ] <= vRed ) && ( vRed <= max[ RED ]))&&
+ ( ( min[ GRN ] <= vGrn ) && ( vGrn <= max[ GRN ]))&&
+ ( ( min[ BLU ] <= vBlu ) && ( vBlu <= max[ BLU ])));
+ }
+
+ /**
+ * Splits the cube into two parts. This cube is
+ * changed to be one half and the returned cube is the other half.
+ * This tries to pick the right channel to split on.
+ */
+ Cube split() {
+ int dr = max[ RED ]-min[ RED ]+1;
+ int dg = max[ GRN ]-min[ GRN ]+1;
+ int db = max[ BLU ]-min[ BLU ]+1;
+ int c0, c1, splitChannel;
+
+ // Figure out which axis is the longest and split along
+ // that axis (this tries to keep cubes square-ish).
+ if (dr >= dg) {
+ if (dr >= db) { splitChannel = RED; c0=GRN; c1=BLU; }
+ else { splitChannel = BLU; c0=RED; c1=GRN; }
+ } else if (dg >= db) {
+ splitChannel = GRN;
+ c0=RED;
+ c1=BLU;
+ } else {
+ splitChannel = BLU;
+ c0=GRN;
+ c1=RED;
+ }
+
+// System.out.println("Red:" + dr
+// + " Grn:" + dg
+// + " Blu:" + db
+// + " Split:" + splitChannel
+// + " c0:" + c0
+// + " c1:" + c1 );
+
+ Cube ret;
+
+ // try to split the longest axis
+ ret = splitChannel(splitChannel, c0, c1);
+ if (ret != null ) return ret;
+
+ // try to split along the 2nd longest axis
+ ret = splitChannel(c0, splitChannel, c1);
+ if (ret != null ) return ret;
+
+ // only one left
+ ret = splitChannel(c1, splitChannel, c0);
+ if (ret != null) return ret;
+
+ // so far, no split was possible trying all 3 colors: this
+ // cube can't be split further
+ done = true;
+ return null;
+ }
+
+ /**
+ * Adjust (normalize) min/max of this cube so that they span
+ * the actual content. This method is called on the two cubes
+ * resulting from a split. <br> We search the counts[] from
+ * min to max for the leftmost non-null entry. That is the
+ * new min. Then we search counts[] from max to min for the
+ * rightmost non-null entry. That is the new max. <br>This
+ * requires, that {@link #computeCounts } really computes
+ * <i>all</i> counts-values (and does not stop after the
+ * necessary number of points for a split is found, as it was
+ * done in the previous version of this class).
+ *
+ * @param splitChannel the color used for the last split
+ * @param counts contains the number of points along the splitChannel
+ * - only counts[ min .. max ] is valid.
+ */
+ private void normalize( int splitChannel, int[] counts ){
+
+ if ( count == 0 ){
+ // empty cube: nothing to normalize
+ return;
+ }
+
+ int iMin = min[ splitChannel ];
+ int iMax = max[ splitChannel ];
+ int loBound = -1;
+ int hiBound = -1;
+
+ // we search from left to right for the first non-null
+ // entry in counts[]
+ for( int i = iMin; i <= iMax; i++ ){
+ if ( counts[ i ] == 0 ){
+ // this entry is 0: search more
+ continue;
+ }
+
+ // we reached a non-null entry: stop looking further
+ loBound = i;
+ break;
+ }
+
+ // we search from right to left for the first non-null
+ // entry in counts[]
+ for( int i= iMax; i >= iMin; i-- ){
+ if ( counts[ i ] == 0 ){
+ // this entry is 0: search more
+ continue;
+ }
+ // we reached a non-null entry: stop looking further
+ hiBound = i;
+ break;
+ }
+
+ boolean flagChangedLo = (loBound != -1 ) && ( iMin != loBound );
+ boolean flagChangedHi = (hiBound != -1 ) && ( iMax != hiBound );
+// if ( flagChangedLo || flagChangedHi ){
+// System.out.println("old min:" + min[ splitChannel ] + "/max:" + max[ splitChannel ]
+// + " new: " + loBound + "/" + hiBound );
+// StringBuffer buff = new StringBuffer( 100 );
+// for( int i= min[ splitChannel ]; i <= max[ splitChannel]; i++ ){
+// buff.append( counts[ i ] );
+// buff.append( ',' );
+// }
+// System.out.println("Counts:" + buff );
+// }
+
+ if ( flagChangedLo ){
+ min[ splitChannel ]= loBound;
+ }
+ if ( flagChangedHi ){
+ max[ splitChannel ]= hiBound;
+ }
+ }
+
+
+ /**
+ * Splits the image according to the parameters. It tries
+ * to find a location where half the pixels are on one side
+ * and half the pixels are on the other.
+ */
+ Cube splitChannel(int splitChannel, int c0, int c1) {
+
+ if (min[splitChannel] == max[splitChannel]) {
+ // thickness along the splitChannel is only one point: cannot split
+ return null;
+ }
+
+ if ( count == 0 ){
+ // this Cube has no points: cannot split
+ return null;
+ }
+
+ // System.out.println( toString() );
+
+ int half = count/2;
+ // Each entry is the number of pixels that have that value
+ // in the split channel within the cube (so pixels
+ // that have that value in the split channel aren't counted
+ // if they are outside the cube in the other color channels.
+ int[] counts = computeCounts( splitChannel, c0, c1 );
+
+ int tcount=0;
+ int lastAdd=-1;
+ // These indicate what the top value for the low cube and
+ // the low value of the high cube should be in the split channel
+ // (they may not be one off if there are 'dead' spots in the
+ // counts array.
+ int splitLo=min[splitChannel];
+ int splitHi=max[splitChannel];
+ for (int i=min[splitChannel]; i<=max[splitChannel]; i++) {
+ int c = counts[i];
+ if (c == 0) {
+ // No counts below this so move up bottom of cube.
+ if ((tcount == 0) && (i < max[splitChannel]))
+ min[splitChannel] = i+1;
+ continue;
+ }
+
+ if (tcount+c < half) {
+ lastAdd = i;
+ tcount+=c;
+ continue;
+ }
+ if ((half-tcount) <= ((tcount+c)-half)) {
+ // Then lastAdd is a better top idx for this then i.
+ if (lastAdd == -1) {
+ // No lower place to break.
+ if (c == count) {
+ // All pixels are at this value so make min/max
+ // reflect that.
+ max[splitChannel] = i;
+ return null; // no split to make.
+ } else {
+ // There are values about this one so
+ // split above.
+ splitLo = i;
+ splitHi = i+1;
+ tcount += c; // fix 35683
+ break;
+ }
+ }
+ splitLo = lastAdd;
+ splitHi = i;
+ } else {
+ if (i == max[splitChannel]) {
+ if ( c == count) {
+ // would move min up but that should
+ // have happened already.
+ return null; // no split to make.
+ } else {
+ // Would like to break between i and i+1
+ // but no i+1 so use lastAdd and i;
+ splitLo = lastAdd;
+ splitHi = i;
+ break;
+ }
+ }
+ // Include c in counts
+ tcount += c;
+ splitLo = i;
+ splitHi = i+1;
+ }
+ break;
+ }
+
+ // System.out.println("Split: " + splitChannel + "@"
+ // + splitLo + "-"+splitHi +
+ // " Count: " + tcount + " of " + count +
+ // " LA: " + lastAdd);
+
+ // Create the new cube and update everyone's bounds & counts.
+ Cube ret = new Cube(colors, tcount);
+ count = count-tcount;
+ ret.min[splitChannel] = min[splitChannel];
+ ret.max[splitChannel] = splitLo;
+ min[splitChannel] = splitHi;
+
+ // the cube was split along splitChannel, the other
+ // dimensions dont change
+ ret.min[c0] = min[c0];
+ ret.max[c0] = max[c0];
+ ret.min[c1] = min[c1];
+ ret.max[c1] = max[c1];
+
+// if ( count <= 0 ){
+// System.out.println("This cube has no points after split:" + toString() );
+// }
+// if ( ret.count <= 0 ){
+// System.out.println("That cube has no points after split:" + ret.toString() + " this:" + toString() );
+// System.out.println("SplitLo:" + splitLo + " SplitHi:" + splitHi );
+// }
+
+ // after a split we 'normalize' both cubes, so that their
+ // min/max reflect the actual bounds of the cube. comment
+ // the next two lines when you want to see the impact of
+ // using non-normalized cubes
+ normalize( splitChannel, counts );
+ ret.normalize( splitChannel, counts );
+
+ return ret;
+ }
+
+ /**
+ * create an array, which contains the number of pixels for
+ * each point along the splitChannel (between min and max of
+ * this cube).
+ *
+ * @param splitChannel one of RED | GRN | BLU
+ * @param c0 one of the other channels
+ * @param c1 the third channel
+ * @return an int[ 255 ] where only int[ min .. max ] contain
+ * valid counts.
+ */
+ private int[] computeCounts( int splitChannel, int c0, int c1) {
+
+ int splitSh4 = (2-splitChannel)*4;
+ int c0Sh4 = (2-c0)*4;
+ int c1Sh4 = (2-c1)*4;
+
+ // after split, each half should have half of the cube's points
+ int half = count/2;
+
+ // Each entry is the number of pixels that have that value
+ // in the split channel within the cube (so pixels
+ // that have that value in the split channel aren't counted
+ // if they are outside the cube in the other color channels.
+ int[] counts = new int[256];
+ int tcount = 0;
+
+ int minR=min[0], minG=min[1], minB=min[2];
+ int maxR=max[0], maxG=max[1], maxB=max[2];
+
+ int[] minIdx = { minR >> 4, minG >> 4, minB >> 4 };
+ int[] maxIdx = { maxR >> 4, maxG >> 4, maxB >> 4 };
+
+ int [] vals = {0, 0, 0};
+ for (int i=minIdx[splitChannel]; i<=maxIdx[splitChannel]; i++) {
+ int idx1 = i<<splitSh4;
+ for (int j=minIdx[c0]; j <=maxIdx[c0]; j++) {
+ int idx2 = idx1 | (j<<c0Sh4);
+ for (int k=minIdx[c1]; k<=maxIdx[c1]; k++) {
+ int idx = idx2 | (k<<c1Sh4);
+ Counter[] v = colors[idx];
+ for( int iColor = 0; iColor < v.length; iColor++ ){
+ Counter c = v[ iColor ];
+ vals = c.getRgb( vals );
+ if ( contains( vals )){
+ // The vals[] lies completly within
+ // this cube so count it.
+ counts[ vals[splitChannel] ] += c.count;
+ tcount += c.count;
+ }
+ }
+ }
+ }
+ // the next statement-line stops the loop after we
+ // found the split-point. however, we continue to
+ // fill the counts[] because that is needed for
+ // normalization
+// // We've found the half way point. Note that the
+// // rest of counts is not filled out.
+// if (( tcount > 0 ) && (tcount >= half)) break; // fix 35683
+ }
+
+ // the result so far is the filled counts[]
+ return counts;
+ }
+
+
+ /**
+ * convert the cube-content to String-representation for logging.
+ * @return the min/max-boundarys of the rgb-channels and
+ * pixel-count of this Cube.
+ */
+ public String toString() {
+ return "Cube: [" +
+ min[ RED ] + '-' + max[ RED ] + "] [" +
+ min[ GRN ] + '-' + max[ GRN ] + "] [" +
+ min[ BLU ] + '-' + max[ BLU ] + "] n:" + count;
+ }
+
+
+ /**
+ * Returns the average color for this cube (no alpha).
+ */
+ public int averageColor() {
+ if (count == 0) {
+ // cube is empty: return black
+ return 0;
+ }
+
+ byte[] rgb = averageColorRGB( null );
+
+ return (( rgb[ RED ] << 16 ) & 0x00FF0000)
+ | (( rgb[ GRN ] << 8 ) & 0x0000FF00)
+ | (( rgb[ BLU ] ) & 0x000000FF);
+ }
+
+ /**
+ * Returns the average color for this cube
+ */
+ public byte[] averageColorRGB( byte[] rgb ) {
+
+ if (count == 0) return RGB_BLACK;
+
+ float red=0, grn=0, blu=0;
+
+ // the boundarys of this cube
+ int minR=min[0], minG=min[1], minB=min[2];
+ int maxR=max[0], maxG=max[1], maxB=max[2];
+ int [] minIdx = {minR>>4, minG>>4, minB>>4};
+ int [] maxIdx = {maxR>>4, maxG>>4, maxB>>4};
+ int[] vals = new int[3];
+
+ for (int i=minIdx[0]; i<=maxIdx[0]; i++) {
+ int idx1 = i<<8;
+ for (int j=minIdx[1]; j<=maxIdx[1]; j++) {
+ int idx2 = idx1 | (j<<4);
+ for (int k=minIdx[2]; k<=maxIdx[2]; k++) {
+ int idx = idx2 | k;
+ Counter[] v = colors[idx];
+ for( int iColor = 0; iColor < v.length; iColor++ ){
+ Counter c = v[ iColor ];
+ vals = c.getRgb( vals );
+ if ( contains( vals ) ) {
+ float weight = (c.count/(float)count);
+ red += (vals[0]*weight);
+ grn += (vals[1]*weight);
+ blu += (vals[2]*weight);
+ }
+ }
+ }
+ }
+ }
+ byte[] result = (rgb == null) ? new byte[3] : rgb;
+ result[ RED ] = (byte)(red + 0.5f);
+ result[ GRN ] = (byte)(grn + 0.5f);
+ result[ BLU ] = (byte)(blu + 0.5f);
+
+ return result;
+ }
+
+ }
+
+ /**
+ * create an array of rgb-colors from the cubes-array.
+ * The color of each cube is computed as the sum of all colors in the cube,
+ * where each pixel is weighted according to it's count.
+ *
+ * @param nCubes number of entries to use in cubes
+ * @param cubes contains the Cubes resulting from running the split-algorithm.
+ * @return a byte[][] which is arranged as [ r|g|b ][ 0..nCubes-1 ]
+ */
+ static byte[][] computeRGB( int nCubes, Cube[] cubes ){
+
+ byte[] r = new byte[nCubes];
+ byte[] g = new byte[nCubes];
+ byte[] b = new byte[nCubes];
+
+ byte[] rgb = new byte[3];
+ for (int i=0; i<nCubes; i++) {
+ rgb = cubes[i].averageColorRGB( rgb );
+ r[i] = rgb[ Cube.RED ];
+ g[i] = rgb[ Cube.GRN ];
+ b[i] = rgb[ Cube.BLU ];
+ }
+
+ byte[][] result = new byte[3][];
+ result[ Cube.RED ] = r;
+ result[ Cube.GRN ] = g;
+ result[ Cube.BLU ] = b;
+
+// logRGB( r, g, b );
+
+ return result;
+ }
+
+ /**
+ * helper-method to print the complete rgb-arrays.
+ * @param r
+ * @param g
+ * @param b
+ */
+ static void logRGB( byte[] r, byte[] g, byte[] b ){
+
+ StringBuffer buff = new StringBuffer( 100 );
+ int nColors = r.length;
+ for( int i= 0; i < nColors; i++ ) {
+ String rgbStr= "(" + (r[i]+128) + ',' + (g[i] +128 ) + ',' + (b[i] + 128) + ")," ;
+ buff.append( rgbStr );
+ }
+ System.out.println("RGB:" + nColors + buff );
+ }
+
+
+ /**
+ * step 1: fill a data-structure with the count of each color in the image.
+ * @param bi input-image
+ * @return a List[] where each slot is a List of Counters (or null)
+ */
+ static List[] createColorList( BufferedImage bi ){
+
+ int w= bi.getWidth();
+ int h= bi.getHeight();
+
+ // Using 4 bits from RG & B.
+ List[] colors = new ArrayList[1<<12];
+
+ for(int i_w=0; i_w<w; i_w++){
+ for(int i_h=0; i_h<h; i_h++){
+ int rgb=(bi.getRGB(i_w,i_h) & 0x00FFFFFF); // mask away alpha
+ // Get index from high four bits of each component.
+ int idx = (((rgb&0xF00000)>>> 12) |
+ ((rgb&0x00F000)>>> 8) |
+ ((rgb&0x0000F0)>>> 4));
+
+ // Get the 'hash vector' for that key.
+ List v = colors[idx];
+ if (v == null) {
+ // No colors in this bin yet so create list and
+ // add color.
+ v = new ArrayList();
+ v.add(new Counter(rgb));
+ colors[idx] = v;
+ } else {
+ // find our color in the bin or create a counter for it.
+ Iterator i = v.iterator();
+ while (true) {
+ if (i.hasNext()) {
+ // try adding our color to each counter...
+ if (((Counter)i.next()).add(rgb)) break;
+ } else {
+ v.add(new Counter(rgb));
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ return colors;
+ }
+
+
+ /**
+ * step 2: convert the result of step 1 to an Cube[][] which is
+ * more efficient in the following iterations. All slots in the
+ * result are filled with at least an empty array - thus we avoid
+ * tests for null. <br>Note: the converted slots in colors are no
+ * longer needed and removed.
+ *
+ * @param colors the data-structure to convert. Note that it is
+ * empty after conversion!
+ * @return same data as in colors, but Lists are converted to arrays.
+ */
+ static Counter[][] convertColorList( List[] colors ){
+
+ // used to fill empty slots
+ final Counter[] EMPTY_COUNTER = new Counter[0];
+
+ Counter[][] colorTbl= new Counter[ 1<< 12 ][];
+ for( int i= 0; i < colors.length; i++ ){
+ List cl = colors[ i ];
+ if ( cl == null ){
+ colorTbl[ i ] = EMPTY_COUNTER;
+ continue;
+ }
+ int nSlots = cl.size();
+ colorTbl[i] = (Counter[])cl.toArray( new Counter[ nSlots ] );
+
+ // the colors[ i ] - data is no longer needed: discard
+ colors[ i ] = null;
+ }
+
+ return colorTbl;
+ }
+
+ /**
+ * Converts the input image (must be TYPE_INT_RGB or
+ * TYPE_INT_ARGB) to an indexed image. Generating an adaptive
+ * palette with number of colors specified.
+ * @param bi the image to be processed.
+ * @param nColors number of colors in the palette
+ */
+ public static BufferedImage getIndexedImage( BufferedImage bi, int nColors) {
+ int w=bi.getWidth();
+ int h=bi.getHeight();
+
+ // Using 4 bits from RG & B.
+ List[] colors = createColorList( bi );
+
+ // now we have initialized the colors[] with lists of Counters.
+ // from now on, this data-structure is just read, not modified.
+ // convert it to Counter[][] for faster iteration
+ Counter[][] colorTbl = convertColorList( colors );
+
+ // this is no longer needed: discard
+ colors = null;
+
+ int nCubes=1;
+ int fCube=0;
+ Cube [] cubes = new Cube[nColors];
+ cubes[0] = new Cube(colorTbl, w*h);
+
+ while (nCubes < nColors) {
+ while (cubes[fCube].isDone()) {
+ fCube++;
+ if (fCube == nCubes) break;
+ }
+ if (fCube == nCubes) {
+ // System.out.println("fCube == nCubes" + fCube );
+ break;
+ }
+ Cube c = cubes[fCube];
+ Cube nc = c.split();
+ if (nc != null) {
+ // store the cube with less points towards the end of
+ // the array, so that fat cubes get more splits
+ if (nc.count > c.count) {
+ // new cube has more points: swap
+ Cube tmp = c; c= nc; nc = tmp;
+ }
+ int j = fCube;
+ int cnt = c.count;
+ for (int i=fCube+1; i<nCubes; i++) {
+ if (cubes[i].count < cnt)
+ break;
+ cubes[j++] = cubes[i];
+ }
+ cubes[j++] = c;
+
+ cnt = nc.count;
+ while (j<nCubes) {
+ if (cubes[j].count < cnt)
+ break;
+ j++;
+ }
+ for (int i=nCubes; i>j; i--)
+ cubes[i] = cubes[i-1];
+ cubes[j++] = nc;
+ nCubes++;
+ }
+ }
+
+ // convert the remaining cubes to the colors they represent
+ byte[][] rgbTbl = computeRGB( nCubes, cubes );
+
+ // The JDK doesn't seem to dither the image correctly if I go
+ // below 8bits per pixel. So I dither to an 8bit palette
+ // image that only has nCubes colors. Then I copy the data to
+ // a lower bit depth image that I return.
+ IndexColorModel icm= new IndexColorModel( 8, nCubes, rgbTbl[0], rgbTbl[1], rgbTbl[2] );
+
+ BufferedImage indexed =new BufferedImage
+ (w, h, BufferedImage.TYPE_BYTE_INDEXED, icm);
+ Graphics2D g2d=indexed.createGraphics();
+ g2d.setRenderingHint
+ (RenderingHints.KEY_DITHERING,
+ RenderingHints.VALUE_DITHER_ENABLE);
+ g2d.drawImage(bi, 0, 0, null);
+ g2d.dispose();
+
+
+ int bits;
+ for (bits=1; bits <=8; bits++) {
+ if ((1<<bits) >= nCubes) break;
+ }
+// System.out.println("Bits: " + bits + " Cubes: " + nCubes);
+
+ if (bits > 4) {
+ // 8 bit image we are done...
+ return indexed;
+ }
+
+ // Create our low bit depth image...
+ if (bits ==3) bits = 4;
+ ColorModel cm = new IndexColorModel(bits,nCubes,
+ rgbTbl[0], rgbTbl[1], rgbTbl[2] );
+ SampleModel sm;
+ sm = new MultiPixelPackedSampleModel(DataBuffer.TYPE_BYTE, w, h, bits);
+ WritableRaster ras = Raster.createWritableRaster( sm, new Point(0,0));
+
+ // Copy the data to the low bitdepth image.
+ bi = indexed;
+ indexed = new BufferedImage(cm, ras, bi.isAlphaPremultiplied(), null);
+ GraphicsUtil.copyData(bi, indexed);
+ return indexed;
+ }
+}
Propchange: incubator/flex/sdk/branches/develop/modules/thirdparty/batik/sources/org/apache/flex/forks/batik/ext/awt/image/rendered/IndexImage.java
------------------------------------------------------------------------------
svn:eol-style = native
Added: incubator/flex/sdk/branches/develop/modules/thirdparty/batik/sources/org/apache/flex/forks/batik/ext/awt/image/rendered/LRUCache.java
URL: http://svn.apache.org/viewvc/incubator/flex/sdk/branches/develop/modules/thirdparty/batik/sources/org/apache/flex/forks/batik/ext/awt/image/rendered/LRUCache.java?rev=1402274&view=auto
==============================================================================
--- incubator/flex/sdk/branches/develop/modules/thirdparty/batik/sources/org/apache/flex/forks/batik/ext/awt/image/rendered/LRUCache.java (added)
+++ incubator/flex/sdk/branches/develop/modules/thirdparty/batik/sources/org/apache/flex/forks/batik/ext/awt/image/rendered/LRUCache.java Thu Oct 25 19:01:43 2012
@@ -0,0 +1,161 @@
+/*
+
+ 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.flex.forks.batik.ext.awt.image.rendered;
+
+import org.apache.flex.forks.batik.util.DoublyLinkedList;
+
+/**
+ *
+ * @version $Id: LRUCache.java 498740 2007-01-22 18:35:57Z dvholten $
+ */
+public class LRUCache {
+
+ /**
+ * Interface for object participating in the LRU Cache. These
+ * inform the object of key events in the status of the object in
+ * the LRU cache.
+ */
+ public interface LRUObj {
+ /**
+ * Called when the object first becomes active in the LRU cache.
+ * @param nde The LRU cache node associated with this object.
+ * should be remembered so it can be returned by
+ * <tt>lruGet</tt>.
+ */
+ void lruSet(LRUNode nde);
+ /**
+ * Called to get the LRU node for this object. Should return the
+ * node passed in to lruSet.
+ */
+ LRUNode lruGet();
+ /**
+ * Called to inform the object that it is no longer in the cache.
+ */
+ void lruRemove();
+ }
+
+ /**
+ * Interface for nodes in the LRU cache, basicly nodes in a doubly
+ * linked list.
+ */
+ public class LRUNode extends DoublyLinkedList.Node {
+ private LRUObj obj = null;
+ public LRUObj getObj () { return obj; }
+ protected void setObj (LRUObj newObj) {
+ if (obj != null) obj.lruRemove();
+
+ obj = newObj;
+ if (obj != null) obj.lruSet(this);
+ }
+ }
+
+ private DoublyLinkedList free = null;
+ private DoublyLinkedList used = null;
+ private int maxSize = 0;
+
+ public LRUCache(int size) {
+ if (size <= 0) size=1;
+ maxSize = size;
+
+ free = new DoublyLinkedList();
+ used = new DoublyLinkedList();
+
+ while (size > 0) {
+ free.add(new LRUNode());
+ size--;
+ }
+ }
+
+ public int getUsed() {
+ return used.getSize();
+ }
+
+ public synchronized void setSize(int newSz) {
+
+ if (maxSize < newSz) { // list grew...
+
+ for (int i=maxSize; i<newSz; i++)
+ free.add(new LRUNode());
+
+ } else if (maxSize > newSz) {
+
+ for (int i=used.getSize(); i>newSz; i--) {
+ LRUNode nde = (LRUNode)used.getTail();
+ used.remove(nde);
+ nde.setObj(null);
+ }
+ }
+
+ maxSize = newSz;
+ }
+
+ public synchronized void flush() {
+ while (used.getSize() > 0) {
+ LRUNode nde = (LRUNode)used.pop();
+ nde.setObj(null);
+ free.add(nde);
+ }
+ }
+
+ public synchronized void remove(LRUObj obj) {
+ LRUNode nde = obj.lruGet();
+ if (nde == null) return;
+ used.remove(nde);
+ nde.setObj(null);
+ free.add(nde);
+ }
+
+ public synchronized void touch(LRUObj obj) {
+ LRUNode nde = obj.lruGet();
+ if (nde == null) return;
+ used.touch(nde);
+ }
+
+ public synchronized void add(LRUObj obj) {
+ LRUNode nde = obj.lruGet();
+
+ // already linked in...
+ if (nde != null) {
+ used.touch(nde);
+ return;
+ }
+
+ if (free.getSize() > 0) {
+ nde = (LRUNode)free.pop();
+ nde.setObj(obj);
+ used.add(nde);
+ } else {
+ nde = (LRUNode)used.getTail();
+ nde.setObj(obj);
+ used.touch(nde);
+ }
+ }
+
+ protected synchronized void print() {
+ System.out.println("In Use: " + used.getSize() +
+ " Free: " + free.getSize());
+ LRUNode nde = (LRUNode)used.getHead();
+ if (nde == null) return;
+ do {
+ System.out.println(nde.getObj());
+ nde = (LRUNode)nde.getNext();
+ } while (nde != used.getHead());
+ }
+
+}
Propchange: incubator/flex/sdk/branches/develop/modules/thirdparty/batik/sources/org/apache/flex/forks/batik/ext/awt/image/rendered/LRUCache.java
------------------------------------------------------------------------------
svn:eol-style = native