| /* ==================================================================== |
| 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.poi.sl.draw; |
| |
| import java.awt.*; |
| import java.awt.MultipleGradientPaint.ColorSpaceType; |
| import java.awt.MultipleGradientPaint.CycleMethod; |
| import java.awt.geom.*; |
| import java.awt.image.*; |
| |
| class PathGradientPaint implements Paint { |
| |
| // http://asserttrue.blogspot.de/2010/01/how-to-iimplement-custom-paint-in-50.html |
| protected final Color colors[]; |
| protected final float fractions[]; |
| protected final int capStyle; |
| protected final int joinStyle; |
| protected final int transparency; |
| |
| |
| public PathGradientPaint(Color colors[], float fractions[]) { |
| this(colors,fractions,BasicStroke.CAP_ROUND,BasicStroke.JOIN_ROUND); |
| } |
| |
| public PathGradientPaint(Color colors[], float fractions[], int capStyle, int joinStyle) { |
| this.colors = colors.clone(); |
| this.fractions = fractions.clone(); |
| this.capStyle = capStyle; |
| this.joinStyle = joinStyle; |
| |
| // determine transparency |
| boolean opaque = true; |
| for (Color c : colors) { |
| if (c != null) { |
| opaque = opaque && (c.getAlpha() == 0xff); |
| } |
| } |
| this.transparency = opaque ? OPAQUE : TRANSLUCENT; |
| } |
| |
| public PaintContext createContext(ColorModel cm, |
| Rectangle deviceBounds, |
| Rectangle2D userBounds, |
| AffineTransform transform, |
| RenderingHints hints) { |
| return new PathGradientContext(cm, deviceBounds, userBounds, transform, hints); |
| } |
| |
| public int getTransparency() { |
| return transparency; |
| } |
| |
| class PathGradientContext implements PaintContext { |
| protected final Rectangle deviceBounds; |
| protected final Rectangle2D userBounds; |
| protected final AffineTransform xform; |
| protected final RenderingHints hints; |
| |
| /** |
| * for POI: the shape will be only known when the subclasses determines the concrete implementation |
| * in the draw/-content method, so we need to postpone the setting/creation as long as possible |
| **/ |
| protected final Shape shape; |
| protected final PaintContext pCtx; |
| protected final int gradientSteps; |
| WritableRaster raster; |
| |
| public PathGradientContext( |
| ColorModel cm |
| , Rectangle deviceBounds |
| , Rectangle2D userBounds |
| , AffineTransform xform |
| , RenderingHints hints |
| ) { |
| shape = (Shape)hints.get(Drawable.GRADIENT_SHAPE); |
| if (shape == null) { |
| throw new IllegalPathStateException("PathGradientPaint needs a shape to be set via the rendering hint Drawable.GRADIANT_SHAPE."); |
| } |
| |
| this.deviceBounds = deviceBounds; |
| this.userBounds = userBounds; |
| this.xform = xform; |
| this.hints = hints; |
| |
| gradientSteps = getGradientSteps(shape); |
| |
| Point2D start = new Point2D.Double(0, 0); |
| Point2D end = new Point2D.Double(gradientSteps, 0); |
| LinearGradientPaint gradientPaint = new LinearGradientPaint(start, end, fractions, colors, CycleMethod.NO_CYCLE, ColorSpaceType.SRGB, new AffineTransform()); |
| |
| Rectangle bounds = new Rectangle(0, 0, gradientSteps, 1); |
| pCtx = gradientPaint.createContext(cm, bounds, bounds, new AffineTransform(), hints); |
| } |
| |
| public void dispose() {} |
| |
| public ColorModel getColorModel() { |
| return pCtx.getColorModel(); |
| } |
| |
| public Raster getRaster(int xOffset, int yOffset, int w, int h) { |
| ColorModel cm = getColorModel(); |
| if (raster == null) createRaster(); |
| |
| // TODO: eventually use caching here |
| WritableRaster childRaster = cm.createCompatibleWritableRaster(w, h); |
| Rectangle2D childRect = new Rectangle2D.Double(xOffset, yOffset, w, h); |
| if (!childRect.intersects(deviceBounds)) { |
| // usually doesn't happen ... |
| return childRaster; |
| } |
| |
| Rectangle2D destRect = new Rectangle2D.Double(); |
| Rectangle2D.intersect(childRect, deviceBounds, destRect); |
| int dx = (int)(destRect.getX()-deviceBounds.getX()); |
| int dy = (int)(destRect.getY()-deviceBounds.getY()); |
| int dw = (int)destRect.getWidth(); |
| int dh = (int)destRect.getHeight(); |
| Object data = raster.getDataElements(dx, dy, dw, dh, null); |
| dx = (int)(destRect.getX()-childRect.getX()); |
| dy = (int)(destRect.getY()-childRect.getY()); |
| childRaster.setDataElements(dx, dy, dw, dh, data); |
| |
| return childRaster; |
| } |
| |
| protected int getGradientSteps(Shape gradientShape) { |
| Rectangle rect = gradientShape.getBounds(); |
| int lower = 1; |
| int upper = (int)(Math.max(rect.getWidth(),rect.getHeight())/2.0); |
| while (lower < upper-1) { |
| int mid = lower + (upper - lower) / 2; |
| BasicStroke bs = new BasicStroke(mid, capStyle, joinStyle); |
| Area area = new Area(bs.createStrokedShape(gradientShape)); |
| if (area.isSingular()) { |
| upper = mid; |
| } else { |
| lower = mid; |
| } |
| } |
| return upper; |
| } |
| |
| |
| |
| protected void createRaster() { |
| ColorModel cm = getColorModel(); |
| raster = cm.createCompatibleWritableRaster((int)deviceBounds.getWidth(), (int)deviceBounds.getHeight()); |
| BufferedImage img = new BufferedImage(cm, raster, false, null); |
| Graphics2D graphics = img.createGraphics(); |
| graphics.setRenderingHints(hints); |
| graphics.translate(-deviceBounds.getX(), -deviceBounds.getY()); |
| graphics.transform(xform); |
| |
| Raster img2 = pCtx.getRaster(0, 0, gradientSteps, 1); |
| int rgb[] = new int[cm.getNumComponents()]; |
| |
| for (int i = gradientSteps-1; i>=0; i--) { |
| img2.getPixel(i, 0, rgb); |
| Color c = new Color(rgb[0],rgb[1],rgb[2]); |
| if (rgb.length == 4) { |
| // it doesn't work to use just a color with transparency ... |
| graphics.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC, rgb[3]/255.0f)); |
| } |
| graphics.setStroke(new BasicStroke(i+1, capStyle, joinStyle)); |
| graphics.setColor(c); |
| graphics.draw(shape); |
| } |
| |
| graphics.dispose(); |
| } |
| } |
| } |