Package org.stathissideris.ascii2image.graphics

Source Code of org.stathissideris.ascii2image.graphics.BitmapRenderer$TextCanvas

/**
* ditaa - Diagrams Through Ascii Art
*
* Copyright (C) 2004-2011 Efstathios Sideris
*
* ditaa 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 3 of
* the License, or (at your option) any later version.
*
* ditaa 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 ditaa.  If not, see <http://www.gnu.org/licenses/>.
*  
*/
package org.stathissideris.ascii2image.graphics;

import java.awt.BasicStroke;
import java.awt.Canvas;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.Stroke;
import java.awt.geom.AffineTransform;
import java.awt.geom.GeneralPath;
import java.awt.image.BufferedImage;
import java.awt.image.ConvolveOp;
import java.awt.image.Kernel;
import java.awt.image.RenderedImage;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;

import javax.imageio.ImageIO;

import org.stathissideris.ascii2image.core.ConversionOptions;
import org.stathissideris.ascii2image.core.RenderingOptions;
import org.stathissideris.ascii2image.core.Shape3DOrderingComparator;
import org.stathissideris.ascii2image.core.ShapeAreaComparator;
import org.stathissideris.ascii2image.text.TextGrid;

/**
*
* @author Efstathios Sideris
*/
public class BitmapRenderer {

  private static final boolean DEBUG = true;
  private static final boolean DEBUG_LINES = false;

  private static final String IDREGEX = "^.+_vfill$";
 
  Stroke normalStroke;
  Stroke dashStroke;
 
  public static void main(String[] args) throws Exception {
   
   
    long startTime = System.currentTimeMillis();
   
    ConversionOptions options = new ConversionOptions();
   
    TextGrid grid = new TextGrid();
   
    String filename = "bug18.txt";
   
    grid.loadFrom("tests/text/"+filename);
   
    Diagram diagram = new Diagram(grid, options);
    new BitmapRenderer().renderToPNG(diagram, "tests/images/"+filename+".png", options.renderingOptions);
    long endTime = System.currentTimeMillis();
    long totalTime  = (endTime - startTime) / 1000;
    System.out.println("Done in "+totalTime+"sec");
   
    File workDir = new File("tests/images");
    //Process p = Runtime.getRuntime().exec("display "+filename+".png", null, workDir);
  }

  private boolean renderToPNG(Diagram diagram, String filename, RenderingOptions options){ 
    RenderedImage image = renderToImage(diagram, options);
   
    try {
      File file = new File(filename);
      ImageIO.write(image, "png", file);
    } catch (IOException e) {
      //e.printStackTrace();
      System.err.println("Error: Cannot write to file "+filename);
      return false;
    }
    return true;
  }
 
  public RenderedImage renderToImage(Diagram diagram, RenderingOptions options){
    BufferedImage image;
    if(options.needsTransparency()) {
      image = new BufferedImage(
          diagram.getWidth(),
          diagram.getHeight(),
          BufferedImage.TYPE_INT_ARGB);
    } else {
      image = new BufferedImage(
          diagram.getWidth(),
          diagram.getHeight(),
          BufferedImage.TYPE_INT_RGB);
    }
   
    return render(diagram, image, options);
  }
 
  public RenderedImage render(Diagram diagram, BufferedImage image,  RenderingOptions options){
    RenderedImage renderedImage = image;
    Graphics2D g2 = image.createGraphics();

    Object antialiasSetting = RenderingHints.VALUE_ANTIALIAS_OFF;
    if(options.performAntialias())
      antialiasSetting = RenderingHints.VALUE_ANTIALIAS_ON;
   
    g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, antialiasSetting);

    g2.setColor(options.getBackgroundColor());
    //TODO: find out why the next line does not work
    g2.fillRect(0, 0, image.getWidth()+10, image.getHeight()+10);
    /*for(int y = 0; y < diagram.getHeight(); y ++)
      g2.drawLine(0, y, diagram.getWidth(), y);*/
   
    g2.setStroke(new BasicStroke(1, BasicStroke.CAP_SQUARE, BasicStroke.JOIN_ROUND));

    ArrayList<DiagramShape> shapes = diagram.getAllDiagramShapes();

    if(DEBUG) System.out.println("Rendering "+shapes.size()+" shapes (groups flattened)");

    Iterator<DiagramShape> shapesIt;
    if(options.dropShadows()){
      //render shadows
      shapesIt = shapes.iterator();
      while(shapesIt.hasNext()){
        DiagramShape shape = shapesIt.next();

        if(shape.getPoints().isEmpty()) continue;

        //GeneralPath path = shape.makeIntoPath();
        GeneralPath path;
        path = shape.makeIntoRenderPath(diagram, options);     
             
        float offset = diagram.getMinimumOfCellDimension() / 3.333f;
     
        if(path != null
            && shape.dropsShadow()
            && shape.getType() != DiagramShape.TYPE_CUSTOM){
          GeneralPath shadow = new GeneralPath(path);
          AffineTransform translate = new AffineTransform();
          translate.setToTranslation(offset, offset);
          shadow.transform(translate);
          g2.setColor(new Color(150,150,150));
          g2.fill(shadow);
       
        }
      }

   
      //blur shadows
   
      if(true) {
        int blurRadius = 6;
        int blurRadius2 = blurRadius * blurRadius;
        float blurRadius2F = blurRadius2;
        float weight = 1.0f / blurRadius2F;
        float[] elements = new float[blurRadius2];
        for (int k = 0; k < blurRadius2; k++)
          elements[k] = weight;
        Kernel myKernel = new Kernel(blurRadius, blurRadius, elements);

        //if EDGE_NO_OP is not selected, EDGE_ZERO_FILL is the default which creates a black border
        ConvolveOp simpleBlur =
          new ConvolveOp(myKernel, ConvolveOp.EDGE_NO_OP, null);
               
        BufferedImage destination =
          new BufferedImage(
            image.getWidth(),
            image.getHeight(),
            image.getType());

        simpleBlur.filter(image, (BufferedImage) destination);

        //destination = destination.getSubimage(blurRadius/2, blurRadius/2, image.getWidth(), image.getHeight());
        g2 = (Graphics2D) destination.getGraphics();
        g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, antialiasSetting);
        renderedImage = (RenderedImage) destination;
      }
    }

   
    //fill and stroke
   
    float dashInterval = Math.min(diagram.getCellWidth(), diagram.getCellHeight()) / 2;
    //Stroke normalStroke = g2.getStroke();
   
    float strokeWeight = diagram.getMinimumOfCellDimension() / 10;
   
    normalStroke =
      new BasicStroke(
      strokeWeight,
      //10,
      BasicStroke.CAP_ROUND,
      BasicStroke.JOIN_ROUND
      );

    dashStroke =
      new BasicStroke(
      strokeWeight,
      BasicStroke.CAP_BUTT,
      BasicStroke.JOIN_ROUND,
      0,
      new float[] {dashInterval},
      0
      );
   
    //TODO: at this stage we should draw the open shapes first in order to make sure they are at the bottom (this is useful for the {mo} shape)
   
   
    //find storage shapes
    ArrayList<DiagramShape> storageShapes = new ArrayList<DiagramShape>();
    shapesIt = shapes.iterator();
    while(shapesIt.hasNext()){
      DiagramShape shape = (DiagramShape) shapesIt.next();
      if(shape.getType() == DiagramShape.TYPE_STORAGE) {
        storageShapes.add(shape);
        continue;
      }
    }

    //render storage shapes
    //special case since they are '3d' and should be
    //rendered bottom to top
    //TODO: known bug: if a storage object is within a bigger normal box, it will be overwritten in the main drawing loop
    //(BUT this is not possible since tags are applied to all shapes overlaping shapes)

   
    Collections.sort(storageShapes, new Shape3DOrderingComparator());
   
    g2.setStroke(normalStroke);
    shapesIt = storageShapes.iterator();
    while(shapesIt.hasNext()){
      DiagramShape shape = (DiagramShape) shapesIt.next();

      GeneralPath path;
      path = shape.makeIntoRenderPath(diagram, options);
     
      if(!shape.isStrokeDashed()) {
        if(shape.getFillColor() != null)
          g2.setColor(shape.getFillColor());
        else
          g2.setColor(Color.white);
        g2.fill(path);
      }

      if(shape.isStrokeDashed())
        g2.setStroke(dashStroke);
      else
        g2.setStroke(normalStroke);
      g2.setColor(shape.getStrokeColor());
      g2.draw(path);
    }

    //sort so that the largest shapes are rendered first
    Collections.sort(shapes, new ShapeAreaComparator());
   
    //render the rest of the shapes
    ArrayList<DiagramShape> pointMarkers = new ArrayList<DiagramShape>();
    shapesIt = shapes.iterator();
    while(shapesIt.hasNext()){
      DiagramShape shape = (DiagramShape) shapesIt.next();
      if(shape.getType() == DiagramShape.TYPE_POINT_MARKER) {
        pointMarkers.add(shape);
        continue;
      }
      if(shape.getType() == DiagramShape.TYPE_STORAGE) {
        continue;
      }
      if(shape.getType() == DiagramShape.TYPE_CUSTOM){
        renderCustomShape(shape, g2);
        continue;
      }

      if(shape.getPoints().isEmpty()) continue;

      int size = shape.getPoints().size();
     
      GeneralPath path;
      path = shape.makeIntoRenderPath(diagram, options);
     
      //fill
      if(path != null && shape.isClosed() && !shape.isStrokeDashed()){
        if(shape.getFillColor() != null)
          g2.setColor(shape.getFillColor());
        else
          g2.setColor(Color.white);
        g2.fill(path);
      }
     
      //draw
      if(shape.getType() != DiagramShape.TYPE_ARROWHEAD){
        g2.setColor(shape.getStrokeColor());
        if(shape.isStrokeDashed())
          g2.setStroke(dashStroke);
        else
          g2.setStroke(normalStroke);
        g2.draw(path);
      }
    }
   
    //render point markers
   
    g2.setStroke(normalStroke);
    shapesIt = pointMarkers.iterator();
    while(shapesIt.hasNext()){
      DiagramShape shape = (DiagramShape) shapesIt.next();
      //if(shape.getType() != DiagramShape.TYPE_POINT_MARKER) continue;

      GeneralPath path;
      path = shape.makeIntoRenderPath(diagram, options);
     
      g2.setColor(Color.white);
      g2.fill(path);
      g2.setColor(shape.getStrokeColor());
      g2.draw(path);
    }   
   
    //handle text
    //g2.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
    //renderTextLayer(diagram.getTextObjects().iterator());
   
    Iterator<DiagramText> textIt = diagram.getTextObjects().iterator();
    while(textIt.hasNext()){
      DiagramText text = textIt.next();
      g2.setFont(text.getFont());
      if(text.hasOutline()){
        g2.setColor(text.getOutlineColor());
        g2.drawString(text.getText(), text.getXPos() + 1, text.getYPos());
        g2.drawString(text.getText(), text.getXPos() - 1, text.getYPos());
        g2.drawString(text.getText(), text.getXPos(), text.getYPos() + 1);
        g2.drawString(text.getText(), text.getXPos(), text.getYPos() - 1);
      }
      g2.setColor(text.getColor());
      g2.drawString(text.getText(), text.getXPos(), text.getYPos());
    }
   
    if(options.renderDebugLines() || DEBUG_LINES){
      Stroke debugStroke =
        new BasicStroke(
        1,
        BasicStroke.CAP_ROUND,
        BasicStroke.JOIN_ROUND
        );
      g2.setStroke(debugStroke);
      g2.setColor(new Color(170, 170, 170));
      g2.setXORMode(Color.white);
      for(int x = 0; x < diagram.getWidth(); x += diagram.getCellWidth())
        g2.drawLine(x, 0, x, diagram.getHeight());
      for(int y = 0; y < diagram.getHeight(); y += diagram.getCellHeight())
        g2.drawLine(0, y, diagram.getWidth(), y);
    }
   

    g2.dispose();
   
    return renderedImage;
  }
 
  private RenderedImage renderTextLayer(ArrayList<DiagramText> textObjects, int width, int height){
    TextCanvas canvas = new TextCanvas(textObjects);
    Image image = canvas.createImage(width, height);
    Graphics g = image.getGraphics();
    canvas.paint(g);
    return (RenderedImage) image;
  }
 
  private class TextCanvas extends Canvas {
    ArrayList<DiagramText> textObjects;
   
    public TextCanvas(ArrayList<DiagramText> textObjects){
      this.textObjects = textObjects;
    }
   
    public void paint(Graphics g){
      Graphics g2 = (Graphics2D) g;
      Iterator<DiagramText> textIt = textObjects.iterator();
      while(textIt.hasNext()){
        DiagramText text = (DiagramText) textIt.next();
        g2.setFont(text.getFont());
        if(text.hasOutline()){
          g2.setColor(text.getOutlineColor());
          g2.drawString(text.getText(), text.getXPos() + 1, text.getYPos());
          g2.drawString(text.getText(), text.getXPos() - 1, text.getYPos());
          g2.drawString(text.getText(), text.getXPos(), text.getYPos() + 1);
          g2.drawString(text.getText(), text.getXPos(), text.getYPos() - 1);
        }
        g2.setColor(text.getColor());
        g2.drawString(text.getText(), text.getXPos(), text.getYPos());
      }
    }
  }
 
  private void renderCustomShape(DiagramShape shape, Graphics2D g2){
    CustomShapeDefinition definition = shape.getDefinition();
   
    Rectangle bounds = shape.getBounds();
   
    if(definition.hasBorder()){
      g2.setColor(shape.getStrokeColor());
      if(shape.isStrokeDashed())
        g2.setStroke(dashStroke);
      else
        g2.setStroke(normalStroke);
      g2.drawLine(bounds.x, bounds.y, bounds.x + bounds.width, bounds.y);
      g2.drawLine(bounds.x + bounds.width, bounds.y, bounds.x + bounds.width, bounds.y + bounds.height);
      g2.drawLine(bounds.x, bounds.y + bounds.height, bounds.x + bounds.width, bounds.y + bounds.height);
      g2.drawLine(bounds.x, bounds.y, bounds.x, bounds.y + bounds.height);
     
//      g2.drawRect(bounds.x, bounds.y, bounds.width, bounds.height); //looks different!     
    }
   
    //TODO: custom shape distintion relies on filename extension. Make this more intelligent
    if(definition.getFilename().endsWith(".png")){
      renderCustomPNGShape(shape, g2);
    } else if(definition.getFilename().endsWith(".svg")){
      renderCustomSVGShape(shape, g2);
    }
  }
 
  private void renderCustomSVGShape(DiagramShape shape, Graphics2D g2){
    CustomShapeDefinition definition = shape.getDefinition();
    Rectangle bounds = shape.getBounds();
    Image graphic;
    try {
      if(shape.getFillColor() == null) {
        graphic = ImageHandler.instance().renderSVG(
            definition.getFilename(), bounds.width, bounds.height, definition.stretches());
      } else {
        graphic = ImageHandler.instance().renderSVG(
            definition.getFilename(), bounds.width, bounds.height, definition.stretches(), IDREGEX, shape.getFillColor());       
      }
      g2.drawImage(graphic, bounds.x, bounds.y, null);
    } catch (IOException e) {
      e.printStackTrace();
    }
  }
 
  private void renderCustomPNGShape(DiagramShape shape, Graphics2D g2){
    CustomShapeDefinition definition = shape.getDefinition();
    Rectangle bounds = shape.getBounds();
    Image graphic = ImageHandler.instance().loadImage(definition.getFilename());
   
    int xPos, yPos, width, height;
   
    if(definition.stretches()){ //occupy all available space
      xPos = bounds.x; yPos = bounds.y;
      width = bounds.width; height = bounds.height;
    } else { //decide how to fit
      int newHeight = bounds.width * graphic.getHeight(null) / graphic.getWidth(null);
      if(newHeight < bounds.height){ //expand to fit width
        height = newHeight;
        width = bounds.width;
        xPos = bounds.x;
        yPos = bounds.y + bounds.height / 2 - graphic.getHeight(null) / 2;
      } else { //expand to fit height
        width = graphic.getWidth(null) * bounds.height / graphic.getHeight(null);
        height = bounds.height;
        xPos = bounds.x + bounds.width / 2 - graphic.getWidth(null) / 2;
        yPos = bounds.y;
      }
    }
   
    g2.drawImage(graphic, xPos, yPos, width, height, null);   
  }
 
  public static boolean isColorDark(Color color){
    int brightness = Math.max(color.getRed(), color.getGreen());
    brightness = Math.max(color.getBlue(), brightness);
    if(brightness < 200) {
      if(DEBUG) System.out.println("Color "+color+" is dark");
      return true;
    }
    if(DEBUG) System.out.println("Color "+color+" is not dark");
    return false;
  }
}
TOP

Related Classes of org.stathissideris.ascii2image.graphics.BitmapRenderer$TextCanvas

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.