Advertisement
kiwiwings

PathGradientPaintExample

Jan 31st, 2015
256
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Java 5 11.88 KB | None | 0 0
  1. import java.awt.MultipleGradientPaint.*;
  2. import java.awt.*;
  3. import java.awt.font.*;
  4. import java.awt.geom.*;
  5. import java.awt.image.*;
  6. import java.io.FileOutputStream;
  7.  
  8. import javax.imageio.ImageIO;
  9.  
  10. /**
  11.  * Example class which implement a custom Paint object
  12.  * This will be part of Apache POI slide rendering for shapes with shape/path style gradient
  13.  * Originally I thought this is the same as PathGradientBrush in MFC, but actually this
  14.  * method is much trivial, as the mfc class cycles around and give each point a different
  15.  * linear gradient to the center, whereas for powerpoint the shape outline and color gradually
  16.  * changes to the center.
  17.  */
  18. public class PathGradientPaintExample {
  19.     public static void main(String[] args) throws Exception {
  20.         int width = 500;
  21.         int height = 500;
  22.        
  23.         Color greenTrans = new Color(0, 255, 0, 200);
  24.  
  25.         Color colors[] = { Color.blue, greenTrans, Color.magenta };
  26.         float fractions[] = { 0f, 0.1f, 1f };
  27.         PathGradientPaint pgpSquare = new PathGradientPaint(colors, fractions, BasicStroke.CAP_SQUARE, BasicStroke.JOIN_MITER);
  28.         PathGradientPaint pgpRound = new PathGradientPaint(colors, fractions);
  29.        
  30.         BufferedImage img = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
  31.         Graphics2D graphics = img.createGraphics();
  32.         graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
  33.         graphics.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
  34.         graphics.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
  35.         graphics.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);
  36.        
  37.         // the easy case, a convex shape
  38.         Shape shape1 = getShape1(width, height, graphics);
  39.         graphics.setPaint(pgpSquare);
  40.         graphics.setRenderingHint(PathGradientPaint.GRADIENT_SHAPE, shape1);
  41.         graphics.fill(shape1);
  42.  
  43.         // a club, which needs rounded lines, otherwise the lines overshoot
  44.         Shape shape2 = getShape2(width, height, graphics);
  45.         Rectangle rect2 = shape2.getBounds();
  46.         graphics.setRenderingHint(PathGradientPaint.GRADIENT_SHAPE, shape2);
  47.         graphics.setPaint(pgpRound);
  48.         graphics.translate(10, (int)(height/2+rect2.getHeight()/2));
  49.         graphics.translate(rect2.getCenterX(), rect2.getCenterY());
  50.         graphics.rotate(Math.PI/2);
  51.         graphics.scale(0.7, 1);
  52.         graphics.translate(-rect2.getCenterX(), -rect2.getCenterY());
  53.         graphics.fill(shape2);
  54.  
  55.         // a H figure, to show the diameter calculation error
  56.         graphics.setTransform(new AffineTransform());
  57.         Shape shape3 = getShape3(width, height, graphics);
  58.         graphics.setPaint(pgpSquare);
  59.         graphics.setRenderingHint(PathGradientPaint.GRADIENT_SHAPE, shape3);
  60.         graphics.translate(330, 350);
  61.         graphics.fill(shape3);
  62.        
  63.         // a smaller H, to check fewer gradient steps
  64.         Shape shape4 = getShape3(width/3, height/3, graphics);
  65.         graphics.setPaint(pgpSquare);
  66.         graphics.setRenderingHint(PathGradientPaint.GRADIENT_SHAPE, shape4);
  67.         graphics.translate(65, 0);
  68.         graphics.fill(shape4);
  69.  
  70.        
  71.        
  72.         FileOutputStream out = new FileOutputStream("test.png");
  73.         ImageIO.write(img, "png", out);
  74.         out.close();
  75.  
  76.     }
  77.  
  78.     public static class PathGradientPaint implements Paint {
  79.  
  80.         private static class PathGradientPaintKey extends RenderingHints.Key {
  81.             PathGradientPaintKey(int id) { super(id); }
  82.             public boolean isCompatibleValue(Object val) { return true; }
  83.         }
  84.        
  85.         /**
  86.          * PathGradientPaint needs the shape to be set.
  87.          * It will be achieved through setting it in the rendering hints
  88.          */
  89.         public static RenderingHints.Key GRADIENT_SHAPE = new PathGradientPaintKey(1);
  90.        
  91.         // http://asserttrue.blogspot.de/2010/01/how-to-iimplement-custom-paint-in-50.html
  92.         protected final Color colors[];
  93.         protected final float fractions[];
  94.         protected final int capStyle;
  95.         protected final int joinStyle;
  96.         protected final int transparency;
  97.  
  98.        
  99.         public PathGradientPaint(Color colors[], float fractions[]) {
  100.             this(colors,fractions,BasicStroke.CAP_ROUND,BasicStroke.JOIN_ROUND);
  101.         }
  102.        
  103.         public PathGradientPaint(Color colors[], float fractions[], int capStyle, int joinStyle) {
  104.             this.colors = colors;
  105.             this.fractions = fractions;
  106.             this.capStyle = capStyle;
  107.             this.joinStyle = joinStyle;
  108.  
  109.             // determine transparency
  110.             boolean opaque = true;
  111.             for (int i = 0; i < colors.length; i++){
  112.                 opaque = opaque && (colors[i].getAlpha() == 0xff);
  113.             }
  114.             this.transparency = opaque ? OPAQUE : TRANSLUCENT;
  115.         }
  116.        
  117.         public PaintContext createContext(ColorModel cm,
  118.             Rectangle deviceBounds,
  119.             Rectangle2D userBounds,
  120.             AffineTransform transform,
  121.             RenderingHints hints) {
  122.             return new PathGradientContext(cm, deviceBounds, userBounds, transform, hints);
  123.         }
  124.        
  125.         public int getTransparency() {
  126.             return transparency;
  127.         }
  128.  
  129.         class PathGradientContext implements PaintContext {
  130.             protected final Rectangle deviceBounds;
  131.             protected final Rectangle2D userBounds;
  132.             protected final AffineTransform xform;
  133.             protected final RenderingHints hints;
  134.  
  135.             /**
  136.              * for POI: the shape will be only known when the subclasses determines the concrete implementation
  137.              * in the draw/-content method, so we need to postpone the setting/creation as long as possible
  138.              **/
  139.             protected final Shape shape;
  140.             protected final PaintContext pCtx;
  141.             protected final int gradientSteps;
  142.             WritableRaster raster;
  143.  
  144.             public PathGradientContext(
  145.                   ColorModel cm
  146.                 , Rectangle deviceBounds
  147.                 , Rectangle2D userBounds
  148.                 , AffineTransform xform
  149.                 , RenderingHints hints
  150.             ) {
  151.                 shape = (Shape)hints.get(GRADIENT_SHAPE);
  152.                 if (shape == null) {
  153.                     throw new IllegalPathStateException("PathGradientPaint needs a shape to be set via the rendering hint PathGradientPaint.GRADIANT_SHAPE.");
  154.                 }
  155.  
  156.                 this.deviceBounds = deviceBounds;
  157.                 this.userBounds = userBounds;
  158.                 this.xform = xform;
  159.                 this.hints = hints;
  160.  
  161.                 gradientSteps = getGradientSteps(shape);
  162.  
  163.                 Point2D start = new Point2D.Double(0, 0);
  164.                 Point2D end = new Point2D.Double(gradientSteps, 0);
  165.                 LinearGradientPaint gradientPaint = new LinearGradientPaint(start, end, fractions, colors, CycleMethod.NO_CYCLE, ColorSpaceType.SRGB, new AffineTransform());
  166.                
  167.                 Rectangle bounds = new Rectangle(0, 0, gradientSteps, 1);
  168.                 pCtx = gradientPaint.createContext(cm, bounds, bounds, new AffineTransform(), hints);
  169.             }
  170.  
  171.             public void dispose() {}
  172.  
  173.             public ColorModel getColorModel() {
  174.                 return pCtx.getColorModel();
  175.             }
  176.  
  177.             public Raster getRaster(int xOffset, int yOffset, int w, int h) {
  178.                 ColorModel cm = getColorModel();
  179.                 if (raster == null) createRaster();
  180.  
  181.                 // TODO: eventually use caching here
  182.                 WritableRaster childRaster = cm.createCompatibleWritableRaster(w, h);
  183.                 Rectangle2D childRect = new Rectangle2D.Double(xOffset, yOffset, w, h);
  184.                 if (!childRect.intersects(deviceBounds)) {
  185.                     // usually doesn't happen ...
  186.                     return childRaster;
  187.                 }
  188.                
  189.                 Rectangle2D destRect = new Rectangle2D.Double();
  190.                 Rectangle2D.intersect(childRect, deviceBounds, destRect);
  191.                 int dx = (int)(destRect.getX()-deviceBounds.getX());
  192.                 int dy = (int)(destRect.getY()-deviceBounds.getY());
  193.                 int dw = (int)destRect.getWidth();
  194.                 int dh = (int)destRect.getHeight();
  195.                 Object data = raster.getDataElements(dx, dy, dw, dh, null);
  196.                 dx = (int)(destRect.getX()-childRect.getX());
  197.                 dy = (int)(destRect.getY()-childRect.getY());
  198.                 childRaster.setDataElements(dx, dy, dw, dh, data);
  199.                
  200.                 return childRaster;
  201.             }
  202.  
  203.             protected int getGradientSteps(Shape shape) {
  204.                 Rectangle rect = shape.getBounds();
  205.                 int lower = 1;
  206.                 int upper = (int)(Math.max(rect.getWidth(),rect.getHeight())/2.0);
  207.                 while (lower < upper-1) {
  208.                     int mid = lower + (upper - lower) / 2;
  209.                     BasicStroke bs = new BasicStroke(mid, capStyle, joinStyle);
  210.                     Area area = new Area(bs.createStrokedShape(shape));
  211.                     if (area.isSingular()) {
  212.                         upper = mid;
  213.                     } else {
  214.                         lower = mid;
  215.                     }
  216.                 }
  217.                 return upper;
  218.             }
  219.            
  220.            
  221.            
  222.             protected void createRaster() {
  223.                 ColorModel cm = getColorModel();
  224.                 raster = cm.createCompatibleWritableRaster((int)deviceBounds.getWidth(), (int)deviceBounds.getHeight());
  225.                 BufferedImage img = new BufferedImage(cm, raster, false, null);
  226.                 Graphics2D graphics = img.createGraphics();
  227.                 graphics.setRenderingHints(hints);
  228.                 graphics.translate(-deviceBounds.getX(), -deviceBounds.getY());
  229.                 graphics.transform(xform);
  230.  
  231.                 Raster img2 = pCtx.getRaster(0, 0, gradientSteps, 1);
  232.                 int rgb[] = new int[cm.getNumComponents()];
  233.  
  234.                 for (int i = gradientSteps-1; i>=0; i--) {
  235.                     img2.getPixel(i, 0, rgb);
  236.                     Color c = new Color(rgb[0],rgb[1],rgb[2]);
  237.                     if (rgb.length == 4) {
  238.                         // it doesn't work to use just a color with transparency ...
  239.                         graphics.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC, rgb[3]/255.0f));                          
  240.                     }
  241.                     graphics.setStroke(new BasicStroke(i+1, capStyle, joinStyle));
  242.                     graphics.setColor(c);
  243.                     graphics.draw(shape);
  244.                 }
  245.                
  246.                 graphics.dispose();
  247.             }
  248.         }
  249.     }
  250.  
  251.    
  252.    
  253.     static Shape getShape1(int width, int height, Graphics2D graphics) {
  254.         int x1Points[] = {0, width, 0, width};
  255.         int y1Points[] = {0, height, height, 0};
  256.         GeneralPath polygon = new GeneralPath(GeneralPath.WIND_EVEN_ODD, x1Points.length);
  257.         polygon.moveTo(x1Points[0], y1Points[0]);
  258.  
  259.         for (int index = 1; index < x1Points.length; index++) {
  260.             polygon.lineTo(x1Points[index], y1Points[index]);
  261.         };
  262.  
  263.         polygon.closePath();
  264.         return polygon;
  265.     }
  266.    
  267.     static Shape getShape2(int width, int height, Graphics2D graphics) {
  268.         Font f = new Font("Lucida Sans", Font.BOLD, height/2);
  269.         FontRenderContext fontRenderContext = graphics.getFontRenderContext();
  270.         GlyphVector glyphVector = f.createGlyphVector(fontRenderContext, "\u2663"); //  club
  271.         return glyphVector.getOutline();
  272.     }
  273.  
  274.     static Shape getShape3(int width, int height, Graphics2D graphics) {
  275.         Font f = new Font("Lucida Sans", Font.BOLD, height/2);
  276.         FontRenderContext fontRenderContext = graphics.getFontRenderContext();
  277.         GlyphVector glyphVector = f.createGlyphVector(fontRenderContext, "H");
  278.         return glyphVector.getOutline();
  279.     }
  280. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement