package net.sourceforge.fidocadj.circuit;
import java.awt.*;
import java.awt.image.*;
import java.io.*;
import javax.imageio.*;
import net.sourceforge.fidocadj.geom.*;
/** Employs a bitmap image as a canvas to trace on it.
<pre>
This file is part of FidoCadJ.
FidoCadJ is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
FidoCadJ 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with FidoCadJ. If not,
@see <a href=http://www.gnu.org/licenses/>http://www.gnu.org/licenses/</a>.
Copyright 2017 by Davide Bucci
</pre>
*/
public class ImageAsCanvas
{
private BufferedImage img;
private String filename;
private BufferedImage resizedImg;
private double resolution=200;
private double xcorner=0;
private double ycorner=0;
private int MAX_RESIZED_WIDTH;
private int MAX_RESIZED_HEIGHT;
/** Constructor.
*/
public ImageAsCanvas()
{
img=null;
try {
Dimension screensize=Toolkit.getDefaultToolkit().getScreenSize();
MAX_RESIZED_WIDTH=screensize.width*3;
MAX_RESIZED_HEIGHT=screensize.height*3;
} catch (java.awt.HeadlessException E) {
MAX_RESIZED_WIDTH=3000;
MAX_RESIZED_HEIGHT=3000;
}
}
/** Specify an image to attach to the current drawing.
@param f the path and the filename of the image file to
load and display.
@throws IOException if the file is not found or can not be loaded.
*/
public void loadImage(String f)
throws IOException
{
img=ImageIO.read(new File(f));
filename=f;
}
/** Specify an image to attach to the current drawing.
@param f the path and the filename of the image file to
load and display.
@param i the image to be loaded.
*/
public void loadImage(String f, BufferedImage i)
{
img=i;
filename=f;
}
/** Specify the resolution of the image in dots per inch.
This is employed for the coordinate mapping so that the image size
is correctly matched with the FidoCadJ coordinate systems.
@param res image resolution in dots per inch (dpi).
*/
public void setResolution(double res)
{
resolution=res;
}
/** Get the current resolution in dpi.
@return the current resolution in dots per inch.
*/
public double getResolution()
{
return resolution;
}
/** Remove the attached image.
*/
public void removeImage()
{
img=null;
}
/** Get the current file name.
@return the current file name
*/
public String getFilename()
{
return filename;
}
/** Set the coordinates of the origin corner (left topmost one).
@param x the x coordinate.
@param y the y coordinate.
*/
public void setCorner(double x, double y)
{
xcorner=x;
ycorner=y;
}
/** Get the x coordinate of the left topmost point of the image (use
FidoCadJ coordinates).
@return the x coordinate.
*/
public double getCornerX()
{
return xcorner;
}
/** Track the extreme points of the image in the given coordinate systems.
@param mc the coordinate systems.
*/
public void trackExtremePoints(MapCoordinates mc)
{
if(img==null)
return;
int ox=mc.mapXi(xcorner, ycorner,false);
int oy=mc.mapYi(xcorner, ycorner,false);
// The FidoCadJ resolution is 200dpi.
int w=(int)(200.0*img.getWidth()/resolution*mc.getXMagnitude()+0.5);
int h=(int)(200.0*img.getHeight()/resolution*mc.getYMagnitude()+0.5);
mc.trackPoint(ox, oy);
mc.trackPoint(ox+w, oy+h);
}
/** Get the y coordinate of the left topmost point of the image (use
FidoCadJ coordinates).
@return the y coordinate.
*/
public double getCornerY()
{
return ycorner;
}
private int oldw=0;
private int oldh=0;
private int shiftx=0;
private int shifty=0;
/** Draw the current image in the given graphic context.
@param g the Graphic2D object where the image has to be drawn.
@param mc the current coordinate mapping.
*/
public void drawCanvasImage(Graphics2D g, MapCoordinates mc)
{
if(img==null)
return;
// The image is drawn only in the "dirty" region of the drawing area
// so to greatly improve redrawing speed.
Rectangle clip=g.getClipBounds();
int regionx=clip.x;
int regiony=clip.y;
int regionwidth=clip.width;
int regionheight=clip.height;
// The FidoCadJ resolution is 200dpi.
int w=(int)(200.0*img.getWidth()/resolution*mc.getXMagnitude()+0.5);
int h=(int)(200.0*img.getHeight()/resolution*mc.getYMagnitude()+0.5);
int ox=mc.mapXi(xcorner, ycorner,false);
int oy=mc.mapYi(xcorner, ycorner,false);
// This code is needed to avoid exceeding the boundaries of the
// images (this produces a tiled effect).
regionwidth=Math.max(0,Math.min(regionwidth,w-regionx+ox));
regionheight=Math.max(0,Math.min(regionheight,h-regiony+ox));
regionx=Math.max(ox,regionx);
regiony=Math.max(oy,regiony);
// Resizing an image is pretty time-consuming. Therefore, this is done
// only when it is absolutely needed. This happens when the zoom is
// changed, or when the chunk of the image which has been resized
// should be changed.
if(oldw!=w || oldh!=h || regionx<shiftx || regiony<shifty||
regionx+regionwidth>shiftx+MAX_RESIZED_WIDTH ||
regiony+regionheight>shifty+MAX_RESIZED_HEIGHT)
{
/*System.out.println("\nPartial image calculation");
System.out.println("MAX_RESIZED_WIDTH/3="+(MAX_RESIZED_WIDTH/3));
System.out.println("MAX_RESIZED_HEIGHT/3="+(MAX_RESIZED_HEIGHT/3));
System.out.println("shiftx="+shiftx+" shifty="+shifty);
System.out.println("regionx="+regionx+" regiony="+regiony);
System.out.println("regionx+regionwidth="+(regionx+regionwidth)+
" regiony+regionheight="+(regiony+regionheight));
System.out.println("---------------------------");
System.out.println("oldw!=w "+(oldw!=w));
System.out.println("oldh!=h "+ (oldh!=h));
System.out.println("regionx<shiftx "+ (regionx<shiftx));
System.out.println("regiony<shifty "+ (regiony<shifty));
System.out.println(
"regionx+regionwidth>shiftx+MAX_RESIZED_WIDTH " +
(regionx+regionwidth>shiftx+MAX_RESIZED_WIDTH));
System.out.println(
"regiony+regionheight>shifty+MAX_RESIZED_HEIGHT " +
(regiony+regionheight>shifty+MAX_RESIZED_HEIGHT));*/
GraphicsEnvironment env =
GraphicsEnvironment.getLocalGraphicsEnvironment();
GraphicsDevice device = env.getDefaultScreenDevice();
GraphicsConfiguration config = device.getDefaultConfiguration();
oldw=w;
oldh=h;
if(w<MAX_RESIZED_WIDTH && h<MAX_RESIZED_HEIGHT) {
// Here the image can be resized all together.
shiftx=ox;
shifty=oy;
resizedImg = config.createCompatibleImage(
w, h, Transparency.TRANSLUCENT);
// If the resulting image is very small, implement a multi-step
// resize to improve the rendering quality.
if(img.getWidth()/w>5) {
System.out.print("Multistep reduction");
BufferedImage rs=img;
BufferedImage rs1=img;
BufferedImage rs2;
int nw=img.getWidth();
int nh=img.getHeight();
while (nw>w*2) {
System.out.print(".");
rs= config.createCompatibleImage(
nw/2, nh/2, Transparency.TRANSLUCENT);
Graphics2D graphics2D = rs.createGraphics();
graphics2D.setRenderingHint(
RenderingHints.KEY_INTERPOLATION,
RenderingHints.VALUE_INTERPOLATION_BILINEAR);
graphics2D.setRenderingHint(
RenderingHints.KEY_RENDERING,
RenderingHints.VALUE_RENDER_QUALITY);
graphics2D.setRenderingHint(
RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
graphics2D.drawImage(rs1,0,0,nw/2,nh/2,null);
nw=nw/2;
nh=nh/2;
rs2=rs;
rs=rs1;
rs1=rs2;
}
System.out.print("\n");
resizedImg.getGraphics().drawImage(rs,0,0,w,h,null);
} else {
resizedImg.getGraphics().drawImage(img,0,0,w,h,null);
}
} else {
// Here, resizing the image would produce an image too big.
// Therefore, the image is resized by chunks.
resizedImg = config.createCompatibleImage(
MAX_RESIZED_WIDTH, MAX_RESIZED_HEIGHT,
Transparency.TRANSLUCENT);
shiftx=Math.max(regionx-MAX_RESIZED_WIDTH/3,0)+ox;
shifty=Math.max(regiony-MAX_RESIZED_HEIGHT/3,0)+oy;
/*System.out.println("---------------------------");
System.out.println("New shiftx = "+shiftx+ " shifty = "+shifty);
*/
resizedImg.getGraphics().drawImage(img, 0, 0,
MAX_RESIZED_WIDTH,
MAX_RESIZED_HEIGHT,
(int)(shiftx/mc.getXMagnitude()*resolution/200.0+0.5),
(int)(shifty/mc.getXMagnitude()*resolution/200.0+0.5),
(int)((shiftx+MAX_RESIZED_WIDTH)
/mc.getXMagnitude()*resolution/200.0
+0.5),
(int)((shifty+MAX_RESIZED_HEIGHT)/
mc.getYMagnitude()*resolution/200.0+0.5),
null);
}
}
// Draw the resized image at the right place.
g.drawImage(resizedImg, regionx, regiony,
regionx+regionwidth,
regiony+regionheight,
regionx-shiftx,
regiony-shifty,
regionx+regionwidth-shiftx,
regiony+regionheight-shifty,
null);
}
}