/*******************************************************************************
* Copyright (c) 2016 Alex Shapiro - github.com/shpralex
* This program and the accompanying materials
* are made available under the terms of the The MIT License (MIT)
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*******************************************************************************/
package com.sproutlife.panel.gamepanel;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.geom.AffineTransform;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.OutputStream;
import java.util.Vector;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import com.sproutlife.panel.PanelController;
import com.sproutlife.renderer.RendererUtils;
public class ImageManager {
public interface PaintImageListener {
public void paintImage();
}
public enum LogoStyle {
None, Small, Large;
}
private static Color BACK_COLOR = Color.white;
private static int SAVE_IMAGE_PAD = 0;
public static void setBackgroundColor(Color color) {
BACK_COLOR = color;
}
public static void setImagePad(int pad) {
SAVE_IMAGE_PAD = pad;
}
private PanelController panelController;
private LogoStyle logoStyle;
public static final Image applicationLogoImage = null;
public static Image smallLogoImage = null;
public static Image largeLogoImage = null;
private Image drawImage;
private boolean imageIsDirty;
private String message = null;
private boolean enablePaint = true; //Set enable paint to false to prevent updating a dirty image
protected Vector<PaintImageListener> paintImageListeners;
public ImageManager(PanelController panelController, LogoStyle logoStyle) {
this.panelController = panelController;
this.logoStyle = logoStyle;
paintImageListeners = new Vector<PaintImageListener>();
try {
smallLogoImage = new ImageIcon(ImageManager.class.getResource("/images/SproutLife_Logo.png")).getImage();
}
catch (Exception ex) {
//This exception probably means that /resources/ should be added to the classpath
ex.printStackTrace();
}
largeLogoImage = smallLogoImage;
}
private boolean testAndClearImageDirtyFlag() {
// Image Dirty Flag must be cleared immediately after testing to avoid
// potential race conditions when repainting from multiple threads at once.
boolean imageWasDirty = imageIsDirty && enablePaint;
if (enablePaint) {
imageIsDirty = false;
}
return imageWasDirty;
}
public void setEnablePaint(boolean enablePaint) {
this.enablePaint = enablePaint;
}
private void flagImageAsDirty() {
imageIsDirty = true;
}
protected synchronized void paint(Graphics2D g2) {
if ( drawImage == null ) {
createImage();
}
if ( testAndClearImageDirtyFlag() ) {
Image logoImage = getLogoImage();
paintImage(drawImage, logoImage, panelController.getBoardRenderer().getTransform());
firePaintImage();
}
g2.drawImage(drawImage, 0, 0, null);
}
protected Image getLogoImage() {
if ( logoStyle == LogoStyle.Large ) {
return largeLogoImage;
}
else if ( logoStyle == LogoStyle.Small ) {
return applicationLogoImage;
}
return null;
}
public Image getLogoImage(double w, double h) {
Image logoImage = smallLogoImage;
if ( w > 1024 &&
h > 768 ) {
logoImage = largeLogoImage;
}
return logoImage;
}
public void repaintNewImage() {
panelController.getBoardRenderer().updateVisibleRenderers(panelController.getScrollController().getViewportRendererBounds());
flagImageAsDirty();
panelController.getScrollPanel().repaint();
}
protected void paintImage(Image image, Image logoImage, AffineTransform transform) {
if ( panelController.getBoardRenderer() != null ) {
paintBackground(image);
Graphics2D g2 = (Graphics2D)image.getGraphics();
g2.setTransform(transform);
panelController.getBoardRenderer().paint(g2);
paintMessage(image);
g2.setTransform(new AffineTransform());
if ( logoImage != null ) {
// draw the logo in the bottom right corner
g2.setTransform(new AffineTransform());
g2.drawImage(logoImage,
image.getWidth(null)-logoImage.getWidth(null),
image.getHeight(null)-logoImage.getHeight(null),
null);
}
}
}
protected ByteArrayOutputStream paintImagePdf(Image logoImage, int width, int height, AffineTransform transform) {
try {
Class<?> documentClass = Class.forName("com.lowagie.text.Document");
Class<?> pageSizeClass = Class.forName("com.lowagie.text.PageSize");
Class<?> defaultFontMapperClass = Class.forName("com.lowagie.text.pdf.DefaultFontMapper");
Class<?> fontMapperClass = Class.forName("com.lowagie.text.pdf.FontMapper");
Class<?> pdfContentByteClass = Class.forName("com.lowagie.text.pdf.PdfContentByte");
Class<?> pdfTemplateClass = Class.forName("com.lowagie.text.pdf.PdfTemplate");
Class<?> pdfWriterClass = Class.forName("com.lowagie.text.pdf.PdfWriter");
Class<?> rectangleClass = Class.forName("com.lowagie.text.Rectangle");
/*
Class<?> documentClass = Class.forName("com.itextpdf.text.Document");
Class<?> pageSizeClass = Class.forName("com.itextpdf.text.PageSize");
Class<?> defaultFontMapperClass = Class.forName("com.itextpdf.text.pdf.DefaultFontMapper");
Class<?> fontMapperClass = Class.forName("com.itextpdf.text.pdf.FontMapper");
Class<?> pdfContentByteClass = Class.forName("com.itextpdf.text.pdf.PdfContentByte");
Class<?> pdfTemplateClass = Class.forName("com.itextpdf.text.pdf.PdfTemplate");
Class<?> pdfWriterClass = Class.forName("com.itextpdf.text.pdf.PdfWriter");
Class<?> rectangleClass = Class.forName("com.itextpdf.text.Rectangle");
*/
if ( panelController.getBoardRenderer() != null ) {
Object doc = documentClass.getConstructor().newInstance();
documentClass.getMethod("addProducer").invoke(doc);
documentClass.getMethod("addTitle",String.class).invoke(doc,"Navigator");
Object rectangle = pageSizeClass.getField("_11X17").get(pageSizeClass);
//doc.setPageSize(new com.lowagie.text.Rectangle(PageSize.LETTER.getHeight()*2,PageSize.LETTER.getWidth()*2));
documentClass.getMethod("setPageSize", rectangleClass).invoke(doc,rectangleClass.getMethod("rotate").invoke(rectangle));
ByteArrayOutputStream baosPDF = new ByteArrayOutputStream();
Object docWriter = null;
Object dc = null;
try {
//docWriter = PdfWriter.getInstance((Document) doc, baosPDF);
docWriter = pdfWriterClass.getMethod("getInstance",documentClass,OutputStream.class).invoke(pdfWriterClass, doc, baosPDF);
//doc.open();
documentClass.getMethod("open").invoke(doc);
//dc = docWriter.getDirectContent();
dc = pdfWriterClass.getMethod("getDirectContent").invoke(docWriter);
}
catch (Exception ex) { ex.printStackTrace();}
// get a pdf template from the direct content
//PdfTemplate tp = ((PdfContentByte) dc).createTemplate(width, height);
Object tp = pdfContentByteClass.getMethod("createTemplate",float.class, float.class).invoke(dc,width,height);
// create an AWT renderer from the pdf template
//Graphics2D g2 = tp.createGraphics(image.getWidth(null), image.getHeight(null), new DefaultFontMapper() );
Graphics2D g2 = (Graphics2D) pdfTemplateClass.getMethod("createGraphics",float.class,float.class,fontMapperClass).invoke(
tp,width, height,defaultFontMapperClass.getConstructor().newInstance());
paintBackground(g2,width,height);
//Graphics2D g2 = (Graphics2D)image.getGraphics();
g2.setTransform(transform);
AffineTransform oldTransform = new AffineTransform();
oldTransform.setTransform(panelController.getBoardRenderer().getTransform());
panelController.getBoardRenderer().getTransform().setToScale(1,1);
//panelController.getBoardRenderer().updateLabels();
panelController.getBoardRenderer().paint(g2);
panelController.getBoardRenderer().getTransform().setTransform(oldTransform);
if ( logoImage != null ) {
// draw the logo in the bottom right corner
g2.setTransform(new AffineTransform());
g2.drawImage(logoImage,
width-logoImage.getWidth(null),
height-logoImage.getHeight(null),
null);
}
//((PdfContentByte) dc).addTemplate(tp, (1224-image.getWidth(null))/2 ,(792-image.getHeight(null))/2);
pdfContentByteClass.getMethod("addTemplate", pdfTemplateClass,float.class,float.class).invoke(
dc, tp,(1224-width)/2 ,(792-height)/2);
g2.dispose();
try {
if (doc != null && (Boolean) documentClass.getMethod("isOpen").invoke(doc)) {
documentClass.getMethod("close").invoke(doc);
//doc.close();
}
if (docWriter != null) {
pdfWriterClass.getMethod("close").invoke(docWriter);
//docWriter.close();
}
}
catch (Exception ex) { ex.printStackTrace();}
return baosPDF;
}
}
catch (Exception ex) {
ex.printStackTrace();
}
return null;
}
public BufferedImage generateScaledImage(int maxImageWidth, int maxImageHeight,
Image logoImage, int pad) {
Rectangle2D.Double imageRectangle = panelController.getScrollController().getRendererRectangle();
Rectangle newImageDimension = RendererUtils.getAspectScaledRectangle(imageRectangle, maxImageWidth, maxImageHeight);
return generateImage(newImageDimension.width, newImageDimension.height,
logoImage, pad);
}
public BufferedImage generateImage(int imageWidth, int imageHeight,
Image logoImage, int pad) {
try {
panelController.getInteractionLock().readLock().lock();
if ( panelController.getScrollController().getVisibleRectangle().width == 0 ||
panelController.getScrollController().getVisibleRectangle().height == 0 ) {
return null;
}
BufferedImage image = new BufferedImage(imageWidth, imageHeight,
BufferedImage.TYPE_INT_RGB);
int actualImageWidth = imageWidth-2*pad;
int actualImageHeight = imageHeight-2*pad;
Rectangle2D.Double imageRectangle = panelController.getScrollController().getRendererRectangle();
Rectangle newImageDimension = RendererUtils.getAspectScaledRectangle(imageRectangle, actualImageWidth, actualImageHeight);
double xZoomFactor = (newImageDimension.width / imageRectangle.width);
double yZoomFactor = (newImageDimension.height / imageRectangle.height);
int xOffset = 0, yOffset = 0;
if ( newImageDimension.width < actualImageWidth ) {
xOffset = (actualImageWidth - newImageDimension.width) / 2;
}
if ( newImageDimension.height < actualImageHeight ) {
yOffset = (actualImageHeight - newImageDimension.height) / 2;
}
AffineTransform transform = new AffineTransform();
transform.translate(pad+xOffset, pad+yOffset);
transform.scale(xZoomFactor*panelController.getScrollController().getScalingZoomFactor(), yZoomFactor*panelController.getScrollController().getScalingZoomFactor());
transform.translate(-imageRectangle.getMinX()/panelController.getScrollController().getScalingZoomFactor(),
-imageRectangle.getMinY()/panelController.getScrollController().getScalingZoomFactor());
paintImage(image, logoImage, transform);
return image;
}
finally {
panelController.getInteractionLock().readLock().unlock();
}
}
public ByteArrayOutputStream generatePdf() {
Rectangle2D.Double imageRectangle1 = getPaddedExportImageSize();
Rectangle imageRectangle2 = RendererUtils.getAspectScaledRectangle(imageRectangle1, 1224, 792);
Image logoImage = smallLogoImage;
int imageWidth = imageRectangle2.width;
int imageHeight = imageRectangle2.height;
int pad = SAVE_IMAGE_PAD;
try {
panelController.getInteractionLock().readLock().lock();
if ( panelController.getScrollController().getVisibleRectangle().width == 0 ||
panelController.getScrollController().getVisibleRectangle().height == 0 ) {
return null;
}
//BufferedImage image = new BufferedImage(imageWidth, imageHeight,
// BufferedImage.TYPE_INT_RGB);
int actualImageWidth = imageWidth-2*pad;
int actualImageHeight = imageHeight-2*pad;
Rectangle2D.Double imageRectangle = panelController.getScrollController().getRendererRectangle();
Rectangle newImageDimension = RendererUtils.getAspectScaledRectangle(imageRectangle, actualImageWidth, actualImageHeight);
double xZoomFactor = (newImageDimension.width / imageRectangle.width);
double yZoomFactor = (newImageDimension.height / imageRectangle.height);
int xOffset = 0, yOffset = 0;
if ( newImageDimension.width < actualImageWidth ) {
xOffset = (actualImageWidth - newImageDimension.width) / 2;
}
if ( newImageDimension.height < actualImageHeight ) {
yOffset = (actualImageHeight - newImageDimension.height) / 2;
}
AffineTransform transform = new AffineTransform();
transform.translate(pad+xOffset, pad+yOffset);
transform.scale(xZoomFactor*panelController.getScrollController().getScalingZoomFactor(), yZoomFactor*panelController.getScrollController().getScalingZoomFactor());
transform.translate(-imageRectangle.getMinX()/panelController.getScrollController().getScalingZoomFactor(),
-imageRectangle.getMinY()/panelController.getScrollController().getScalingZoomFactor());
return paintImagePdf(logoImage, imageWidth, imageHeight, transform);
}
finally {
panelController.getInteractionLock().readLock().unlock();
}
}
public Rectangle2D.Double getRendererBounds(int imageWidth, int imageHeight,
Point2D.Double centerLocation) {
double actualWidth = imageWidth / panelController.getBoardRenderer().getZoom();
double actualHeight = imageHeight / panelController.getBoardRenderer().getZoom();
return new Rectangle2D.Double(centerLocation.x - actualWidth/2,
centerLocation.y - actualHeight/2,
actualWidth, actualHeight);
}
public BufferedImage generateCenteredImage(int imageWidth, int imageHeight,
Point2D.Double centerLocation,
Image logoImage) {
try {
panelController.getInteractionLock().readLock().lock();
if ( panelController.getScrollController().getVisibleRectangle().width == 0 ||
panelController.getScrollController().getVisibleRectangle().height == 0 ) {
return null;
}
BufferedImage image = new BufferedImage(imageWidth, imageHeight,
BufferedImage.TYPE_INT_RGB);
Rectangle2D.Double bounds = getRendererBounds(imageWidth, imageHeight, centerLocation);
AffineTransform transform = new AffineTransform();
transform.scale(panelController.getScrollController().getScalingZoomFactor(), panelController.getScrollController().getScalingZoomFactor());
transform.translate(-bounds.getMinX(),-bounds.getMinY());
paintImage(image, logoImage, transform);
return image;
}
finally {
panelController.getInteractionLock().readLock().unlock();
}
}
public BufferedImage generateCenteredImage(Rectangle2D.Double bounds) {
Image logoImage = getLogoImage(bounds.height, bounds.width);
return generateCenteredImage(bounds, logoImage);
}
public BufferedImage generateCenteredImage(Rectangle2D.Double bounds, Image logoImage) {
try {
panelController.getInteractionLock().readLock().lock();
if ( panelController.getScrollController().getVisibleRectangle().width == 0 ||
panelController.getScrollController().getVisibleRectangle().height == 0 ) {
return null;
}
double scalingZoomFactor = panelController.getScrollController().getScalingZoomFactor();
BufferedImage image = new BufferedImage(
(int)Math.ceil(bounds.width * scalingZoomFactor),
(int)Math.ceil(bounds.height * scalingZoomFactor),
BufferedImage.TYPE_INT_RGB);
AffineTransform transform = new AffineTransform();
transform.scale(scalingZoomFactor, scalingZoomFactor);
transform.translate(-bounds.getMinX(),-bounds.getMinY());
paintImage(image, logoImage, transform);
return image;
}
finally {
panelController.getInteractionLock().readLock().unlock();
}
}
public Rectangle2D.Double getPaddedExportImageSize() {
return panelController.getScrollController().getRendererRectangle(SAVE_IMAGE_PAD);
}
public BufferedImage getExportImage() {
Rectangle2D.Double imageRectangle = getPaddedExportImageSize();
Image logoImage = getLogoImage(imageRectangle.width, imageRectangle.height);
BufferedImage image = generateImage((int)imageRectangle.width,
(int)imageRectangle.height,
logoImage,
SAVE_IMAGE_PAD);
return image;
}
public BufferedImage getScaledExportImage(int imageWidth, int imageHeight) {
Image logoImage = getLogoImage(imageWidth, imageHeight);
BufferedImage scaledImage = generateImage(imageWidth,imageHeight,
logoImage,
SAVE_IMAGE_PAD);
return scaledImage;
}
public BufferedImage getCroppedExportImage(int imageWidth, int imageHeight) {
Image logoImage = getLogoImage(imageWidth, imageHeight);
BufferedImage image = generateCenteredImage(imageWidth, imageHeight,
panelController.getScrollController().getCenterDrawPosition(), logoImage);
return image;
}
public void saveImage(File saveFile) throws Exception {
BufferedImage image = getExportImage();
if( image != null ) {
try {
// save the drawing to a PNG file
ImageIO.write(image, "png", saveFile);
System.gc();
}
catch (Exception ex) { ex.printStackTrace(); }
}
}
public void saveCroppedImage(File saveFile, int imageWidth, int imageHeight) throws Exception {
BufferedImage image = getCroppedExportImage(imageWidth, imageHeight);
if( image != null ) {
try {
// save the drawing to a PNG file
ImageIO.write(image, "png", saveFile);
}
catch (Exception ex) { ex.printStackTrace(); }
}
}
public void saveScaledImage(File saveFile, int imageWidth, int imageHeight) throws Exception {
BufferedImage scaledImage = getScaledExportImage(imageWidth, imageHeight);
if( scaledImage != null ) {
try {
// save the drawing to a PNG file
ImageIO.write(scaledImage, "png", saveFile);
System.gc();
}
catch (Exception ex) { ex.printStackTrace(); }
}
}
protected synchronized void createImage() {
drawImage = new BufferedImage(panelController.getScrollController().getVisibleRectangle().width,
panelController.getScrollController().getVisibleRectangle().height,
BufferedImage.TYPE_INT_ARGB);
//Does this need to be here?
paintBackground(drawImage);
}
protected void paintBackground(Image image) {
paintBackground((Graphics2D)image.getGraphics(),image.getWidth(null),image.getHeight(null));
}
protected void paintBackground(Graphics2D g2, int width, int height) {
g2.setColor(BACK_COLOR);
g2.fillRect(0,0,width,height);
}
public void addPaintImageListener(PaintImageListener paintImageListener) {
paintImageListeners.add(paintImageListener);
}
public void removePaintImageListener(PaintImageListener paintImageListener) {
paintImageListeners.remove(paintImageListener);
}
private void firePaintImage() {
for ( PaintImageListener paintImageListener : paintImageListeners ) {
paintImageListener.paintImage();
}
}
public void paintMessage(Image image) {
if (getMessage()==null) return;
Graphics2D g2 = (Graphics2D)image.getGraphics();
g2.setColor(new Color(255,0,0,100));
RenderingHints rh = new RenderingHints(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
rh.put(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
g2.setRenderingHints(rh);
g2.setFont(new Font("Arial",Font.PLAIN,40));
int len = getMessage().length()/2;
g2.drawString(getMessage(),image.getWidth(null)/2-len*18,image.getHeight(null)/2-20);
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}