ImageBrushPaint.java :  » XML » java-axp » javaaxp » swingviewer » service » impl » rendering » Java Open Source

Java Open Source » XML » java axp 
java axp » javaaxp » swingviewer » service » impl » rendering » ImageBrushPaint.java
/*
 * java-axp XPS utility
 * Copyright (C) 2007-2008 Chris Pope
 * 
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 */

package javaaxp.swingviewer.service.impl.rendering;

import java.awt.Graphics2D;
import java.awt.Paint;
import java.awt.PaintContext;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.geom.AffineTransform;
import java.awt.geom.NoninvertibleTransformException;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
import java.awt.image.Raster;

import javaaxp.core.service.LRUCache;
import javaaxp.core.service.LRUCache.LRUCostFunction;
import javaaxp.core.service.model.document.page.STTileMode;

public class ImageBrushPaint implements Paint {

  
  
  private static LRUCache<ImageBrushDescriptor, ImageBrushPaint> fImageBrushPaintCache = new LRUCache<ImageBrushDescriptor, ImageBrushPaint>(5*1024*1024, new LRUCostFunction<ImageBrushPaint>() {
    public int storageCost(ImageBrushPaint value) {
      return value.fImage.getHeight() * value.fImage.getWidth() * value.fImage.getColorModel().getNumComponents();
    }
  });

  private BufferedImage fImage;
  private AffineTransform fTransform;
  private Rectangle2D fLocationOfFirstTileToRender;
  private STTileMode fTileMode;
  private double fOpacity;
  private AffineTransform fLastDeviceTransform;
  private Rectangle2D fLastUserBounds;
  private CustomPaintContext fContext;

  
  private static class ImageBrushDescriptor {
    public AffineTransform fTransform;
    public Rectangle2D fPortionOfSourceImageToBeRendered;
    public Rectangle2D fLocationOfFirstTileToRender;
    public STTileMode fTileMode;
    public double fOpacity;
    public String fImageSource;
    public ImageBrushDescriptor(AffineTransform transform, Rectangle2D portionOfSourceImageToBeRendered, Rectangle2D locationOfFirstTileToRender, STTileMode tileMode, double opacity, String imageSource) {
      super();
      fTransform = transform;
      fPortionOfSourceImageToBeRendered = portionOfSourceImageToBeRendered;
      fLocationOfFirstTileToRender = locationOfFirstTileToRender;
      fTileMode = tileMode;
      fOpacity = opacity;
      fImageSource = imageSource;
    }
    @Override
    public int hashCode() {
      final int PRIME = 31;
      int result = 1;
      result = PRIME * result + ((fImageSource == null) ? 0 : fImageSource.hashCode());
      result = PRIME * result + ((fLocationOfFirstTileToRender == null) ? 0 : fLocationOfFirstTileToRender.hashCode());
      long temp;
      temp = Double.doubleToLongBits(fOpacity);
      result = PRIME * result + (int) (temp ^ (temp >>> 32));
      result = PRIME * result + ((fPortionOfSourceImageToBeRendered == null) ? 0 : fPortionOfSourceImageToBeRendered.hashCode());
      result = PRIME * result + ((fTileMode == null) ? 0 : fTileMode.hashCode());
      result = PRIME * result + ((fTransform == null) ? 0 : fTransform.hashCode());
      return result;
    }
    @Override
    public boolean equals(Object obj) {
      if (this == obj)
        return true;
      if (obj == null)
        return false;
      if (getClass() != obj.getClass())
        return false;
      final ImageBrushDescriptor other = (ImageBrushDescriptor) obj;
      if (fImageSource == null) {
        if (other.fImageSource != null)
          return false;
      } else if (!fImageSource.equals(other.fImageSource))
        return false;
      if (fLocationOfFirstTileToRender == null) {
        if (other.fLocationOfFirstTileToRender != null)
          return false;
      } else if (!fLocationOfFirstTileToRender.equals(other.fLocationOfFirstTileToRender))
        return false;
      if (Double.doubleToLongBits(fOpacity) != Double.doubleToLongBits(other.fOpacity))
        return false;
      if (fPortionOfSourceImageToBeRendered == null) {
        if (other.fPortionOfSourceImageToBeRendered != null)
          return false;
      } else if (!fPortionOfSourceImageToBeRendered.equals(other.fPortionOfSourceImageToBeRendered))
        return false;
      
      if (fTileMode != other.fTileMode)
        return false;
      
      if (fTransform == null) {
        if (other.fTransform != null)
          return false;
      } else if (!fTransform.equals(other.fTransform))
        return false;
      return true;
    }
  }
  
  
  
  public static ImageBrushPaint getImageBrushPaint(BufferedImage bi, AffineTransform at, Rectangle2D portionOfSourceImageToBeRendered, Rectangle2D locationOfFirstTileToRender, STTileMode tileMode, double opacity, String imageSource){
    ImageBrushDescriptor d = new ImageBrushDescriptor(at, portionOfSourceImageToBeRendered, locationOfFirstTileToRender,tileMode, opacity, imageSource);
    ImageBrushPaint p = null;
    p = fImageBrushPaintCache.get(d);
    if(p == null){
      p = new ImageBrushPaint(bi, at, portionOfSourceImageToBeRendered, locationOfFirstTileToRender,tileMode, opacity);
      fImageBrushPaintCache.put(d, p);
    }
    return p;
  }
  

  public ImageBrushPaint(BufferedImage bi, AffineTransform at, Rectangle2D portionOfSourceImageToBeRendered, Rectangle2D locationOfFirstTileToRender, STTileMode tileMode, double opacity) {
    try {
      if((int)(portionOfSourceImageToBeRendered.getX() + portionOfSourceImageToBeRendered.getWidth()) < bi.getWidth() || 
          (int)(portionOfSourceImageToBeRendered.getY() + portionOfSourceImageToBeRendered.getHeight()) < bi.getHeight()){
        //must make a new buffered image
        fImage = bi.getSubimage((int)portionOfSourceImageToBeRendered.getX(), (int)portionOfSourceImageToBeRendered.getY(), (int)portionOfSourceImageToBeRendered.getWidth(), (int)portionOfSourceImageToBeRendered.getHeight());
      } else {
        fImage = bi;
      }
    } catch (Throwable t){
      System.out.println(t.getMessage());
    }
    fTransform = at;
    fLocationOfFirstTileToRender  = locationOfFirstTileToRender;
    fTileMode = tileMode;
    fOpacity = opacity;
  }

  public PaintContext createContext(ColorModel cm, Rectangle deviceBounds,
      Rectangle2D userBounds, AffineTransform xform, RenderingHints hints) {
    
    if(fLastDeviceTransform == null || fLastUserBounds == null){
      fLastDeviceTransform = xform;
      fLastUserBounds = userBounds;
      fContext = new CustomPaintContext( deviceBounds, xform);
    } else if(!fLastDeviceTransform.equals(xform) || !fLastUserBounds.equals(userBounds)){
      fLastDeviceTransform = xform;
      fLastUserBounds = userBounds;
      fContext = new CustomPaintContext( deviceBounds, xform);
    }
    return fContext;
  }

  public int getTransparency() {
    // TODO Auto-generated method stub
    return 0;
  }
  
  public class CustomPaintContext implements PaintContext {
    private BufferedImage fSourceBufferedImage;
    private double fDeviceX;
    private double fDeviceY;

    public CustomPaintContext(Rectangle deviceBounds, AffineTransform xform) {
      fDeviceX = deviceBounds.getX();
      fDeviceY = deviceBounds.getY();
      fSourceBufferedImage = new BufferedImage((int)Math.ceil(deviceBounds.getWidth()) , (int)Math.ceil(deviceBounds.getHeight()), BufferedImage.TYPE_4BYTE_ABGR);
//      fSourceBufferedImage = new BufferedImage((int)(deviceBounds.getX() + deviceBounds.getWidth()) + 1, (int)(deviceBounds.getHeight()   + deviceBounds.getY()) + 1, BufferedImage.TYPE_4BYTE_ABGR);
      
      
      //The bounding device rectangle, one we account for the space-saving transformation
      Rectangle2D deviceBounds2D = new Rectangle2D.Double(0, 0, deviceBounds.getWidth(), deviceBounds.getHeight());
      
      //translate to new origin
      AffineTransform brushTransform = new AffineTransform() /*AffineTransform.getTranslateInstance(-deviceBounds.getX(), -deviceBounds.getY())*/;
      brushTransform.concatenate(xform);
      brushTransform.concatenate(fTransform);
      double matrix[] = new double[6];
      brushTransform.getMatrix(matrix);
      matrix[4] = 0;
      matrix[5] = 0;
      brushTransform = new AffineTransform(matrix);
      
      Graphics2D g2 = fSourceBufferedImage.createGraphics();
      g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
      g2.setTransform(brushTransform);
      tileImage(brushTransform, deviceBounds2D, g2);
    }
    private void tileImage(AffineTransform brushTransform, Rectangle2D deviceBounds2D, Graphics2D g2) {
      if(fTileMode == STTileMode.NONE){
        g2.translate(fLocationOfFirstTileToRender.getX(), fLocationOfFirstTileToRender.getY());
        g2.drawImage(fImage, 0,0, (int)fLocationOfFirstTileToRender.getWidth(),(int)fLocationOfFirstTileToRender.getHeight(),null);
        
      } else {
        //TODO: Test
        try {
          AffineTransform reverseBrushTransform = brushTransform.createInverse();
          Shape deviceBoundInViewboxSpace = reverseBrushTransform.createTransformedShape(deviceBounds2D);
          Rectangle2D deviceBoundInViewboxSpaceBoundingBox = deviceBoundInViewboxSpace.getBounds2D();
          
          //given fLocationOfFirstTileToRender's position, calc how many can fit above, 
          int numTilesLeft = (int)(fLocationOfFirstTileToRender.getX() / fLocationOfFirstTileToRender.getWidth()) + 1;
          int numTilesAbove = (int)(fLocationOfFirstTileToRender.getY() / fLocationOfFirstTileToRender.getHeight()) + 1;
          int startingX = (int)(deviceBoundInViewboxSpaceBoundingBox.getX() - numTilesLeft * fLocationOfFirstTileToRender.getWidth());
          int startingY = (int)(deviceBoundInViewboxSpaceBoundingBox.getY() - numTilesAbove * fLocationOfFirstTileToRender.getHeight()) + 1;
          int numTilesPerRow = (int)(deviceBoundInViewboxSpaceBoundingBox.getWidth() / fLocationOfFirstTileToRender.getWidth()) + 1;
          int numTilesPerColumn = (int)(deviceBoundInViewboxSpaceBoundingBox.getHeight() / fLocationOfFirstTileToRender.getHeight()) + 1;
          
          for(int row = 0; row <= numTilesPerColumn; row++){
            for(int column = 0; column <= numTilesPerRow; column++){
              Rectangle2D currTileLocation = new Rectangle2D.Double(startingX + column * fLocationOfFirstTileToRender.getWidth(), startingY + row * fLocationOfFirstTileToRender.getHeight(), fLocationOfFirstTileToRender.getWidth(),fLocationOfFirstTileToRender.getHeight());
              if(deviceBoundInViewboxSpace.intersects(currTileLocation)){
                boolean sameVertOrientation = ((row - numTilesLeft) & 1) == 0;
                boolean sameHorizOrientation = ((column -  numTilesAbove) & 1) == 0;

                STTileMode mode = fTileMode;
                //mode = STTileMode.FLIP_XY;
                AffineTransform imageTransform = new AffineTransform();
                
                imageTransform.translate((int)currTileLocation.getX(), (int)currTileLocation.getY());
                if(!sameVertOrientation && (mode == STTileMode.FLIP_Y || mode == STTileMode.FLIP_XY)){
                  imageTransform.concatenate(AffineTransform.getScaleInstance(1, -1));
                  imageTransform.concatenate(AffineTransform.getTranslateInstance(0, -fImage.getHeight()));
                }
                
                if(!sameHorizOrientation&& (mode == STTileMode.FLIP_X || mode == STTileMode.FLIP_XY)){
                  imageTransform.concatenate(AffineTransform.getScaleInstance(-1, 1));
                  imageTransform.concatenate(AffineTransform.getTranslateInstance(-fImage.getWidth(), 0));
                }
                
                //TODO: Should the image scaled first? In non-tiled mode we specific the size of the image in 
                //Viewport space, while implicity defines the scaled.
                //Can/Should this be done with imageTransform?
                g2.drawImage(fImage, imageTransform, null);


              }
            }
          }
        } catch (NoninvertibleTransformException e) {
          // TODO Auto-generated catch block
          e.printStackTrace();
        }
      }
      
    }
    public void dispose() {
    }
    public ColorModel getColorModel() {
      return fSourceBufferedImage.getColorModel();
    }

    public Raster getRaster(int x, int y, int w, int h) {
//      return fSourceBufferedImage.getSubimage(0, 0, w, h).getRaster();
      try {
        return fSourceBufferedImage.getSubimage((int)(x - fDeviceX), (int)(y - fDeviceY), w, h).getRaster();
//        return fSourceBufferedImage.getSubimage((int)(x ), (int)(y ), w, h).getRaster();
      } catch (Throwable t){
        System.out.println("error");
        return fSourceBufferedImage.getSubimage(0, 0, w, w).getRaster();
      }
    }

  }

}
java2s.com  | Contact Us | Privacy Policy
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.