first commit
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
#BlueJ class context
|
||||
comment0.params=name
|
||||
comment0.target=DarkerFilter(java.lang.String)
|
||||
comment0.text=\n\ Constructor\ for\ objects\ of\ class\ DarkerFilter.\n\ @param\ name\ The\ name\ of\ the\ filter.\n
|
||||
numComments=1
|
@@ -0,0 +1,33 @@
|
||||
/**
|
||||
* An image filter to make the image a bit darker.
|
||||
*
|
||||
* @author Michael Kölling and David J. Barnes.
|
||||
* @version 1.0
|
||||
*/
|
||||
public class DarkerFilter extends Filter
|
||||
{
|
||||
/**
|
||||
* Constructor for objects of class DarkerFilter.
|
||||
* @param name The name of the filter.
|
||||
*/
|
||||
public DarkerFilter(String name)
|
||||
{
|
||||
super(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply this filter to an image.
|
||||
*
|
||||
* @param image The image to be changed by this filter.
|
||||
*/
|
||||
public void apply(OFImage image)
|
||||
{
|
||||
int height = image.getHeight();
|
||||
int width = image.getWidth();
|
||||
for(int y = 0; y < height; y++) {
|
||||
for(int x = 0; x < width; x++) {
|
||||
image.setPixel(x, y, image.getPixel(x, y).darker());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,20 @@
|
||||
#BlueJ class context
|
||||
comment0.params=name
|
||||
comment0.target=EdgeFilter(java.lang.String)
|
||||
comment0.text=\n\ Constructor\ for\ objects\ of\ class\ EdgeFilter.\n\ @param\ name\ The\ name\ of\ the\ filter.\n
|
||||
comment1.params=image
|
||||
comment1.target=void\ apply(OFImage)
|
||||
comment1.text=\n\ Apply\ this\ filter\ to\ an\ image.\n\ \n\ @param\ \ image\ \ The\ image\ to\ be\ changed\ by\ this\ filter.\n
|
||||
comment2.params=xpos\ ypos
|
||||
comment2.target=java.awt.Color\ edge(int,\ int)
|
||||
comment2.text=\n\ Return\ a\ new\ color\ that\ is\ the\ smoothed\ color\ of\ a\ given\n\ position.\ The\ "smoothed\ color"\ is\ the\ color\ value\ that\ is\ the\n\ average\ of\ this\ pixel\ and\ all\ the\ adjacent\ pixels.\n\ @param\ xpos\ The\ x\ position\ of\ the\ pixel.\n\ @param\ ypos\ The\ y\ position\ of\ the\ pixel.\n\ @return\ The\ smoothed\ color.\n
|
||||
comment3.params=pixels
|
||||
comment3.target=int\ diffRed(java.util.List)
|
||||
comment3.text=\n\ @param\ pixels\ The\ list\ of\ pixels\ to\ be\ averaged.\n\ @return\ The\ average\ of\ all\ the\ red\ values\ in\ the\ given\ list\ of\ pixels.\n
|
||||
comment4.params=pixels
|
||||
comment4.target=int\ diffGreen(java.util.List)
|
||||
comment4.text=\n\ @param\ pixels\ The\ list\ of\ pixels\ to\ be\ averaged.\n\ @return\ The\ average\ of\ all\ the\ green\ values\ in\ the\ given\ list\ of\ pixels.\n
|
||||
comment5.params=pixels
|
||||
comment5.target=int\ diffBlue(java.util.List)
|
||||
comment5.text=\n\ @param\ pixels\ The\ list\ of\ pixels\ to\ be\ averaged.\n\ @return\ The\ average\ of\ all\ the\ blue\ values\ in\ the\ given\ list\ of\ pixels.\n
|
||||
numComments=6
|
@@ -0,0 +1,143 @@
|
||||
import java.awt.Color;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* An image filter to detect edges and highlight them, a bit like
|
||||
* a colored pencil drawing.
|
||||
*
|
||||
* @author Michael Kölling and David J. Barnes.
|
||||
* @version 1.0
|
||||
*/
|
||||
public class EdgeFilter extends Filter
|
||||
{
|
||||
private static final int TOLERANCE = 20;
|
||||
|
||||
private OFImage original;
|
||||
private int width;
|
||||
private int height;
|
||||
|
||||
/**
|
||||
* Constructor for objects of class EdgeFilter.
|
||||
* @param name The name of the filter.
|
||||
*/
|
||||
public EdgeFilter(String name)
|
||||
{
|
||||
super(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply this filter to an image.
|
||||
*
|
||||
* @param image The image to be changed by this filter.
|
||||
*/
|
||||
public void apply(OFImage image)
|
||||
{
|
||||
original = new OFImage(image);
|
||||
width = original.getWidth();
|
||||
height = original.getHeight();
|
||||
|
||||
for(int y = 0; y < height; y++) {
|
||||
for(int x = 0; x < width; x++) {
|
||||
image.setPixel(x, y, edge(x, y));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a new color that is the smoothed color of a given
|
||||
* position. The "smoothed color" is the color value that is the
|
||||
* average of this pixel and all the adjacent pixels.
|
||||
* @param xpos The x position of the pixel.
|
||||
* @param ypos The y position of the pixel.
|
||||
* @return The smoothed color.
|
||||
*/
|
||||
private Color edge(int xpos, int ypos)
|
||||
{
|
||||
List<Color> pixels = new ArrayList<>(9);
|
||||
|
||||
for(int y = ypos-1; y <= ypos+1; y++) {
|
||||
for(int x = xpos-1; x <= xpos+1; x++) {
|
||||
if( x >= 0 && x < width && y >= 0 && y < height ) {
|
||||
pixels.add(original.getPixel(x, y));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new Color(255 - diffRed(pixels), 255 - diffGreen(pixels), 255 - diffBlue(pixels));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param pixels The list of pixels to be averaged.
|
||||
* @return The average of all the red values in the given list of pixels.
|
||||
*/
|
||||
private int diffRed(List<Color> pixels)
|
||||
{
|
||||
int max = 0;
|
||||
int min = 255;
|
||||
for(Color color : pixels) {
|
||||
int val = color.getRed();
|
||||
if(val > max) {
|
||||
max = val;
|
||||
}
|
||||
if(val < min) {
|
||||
min = val;
|
||||
}
|
||||
}
|
||||
int difference = max - min - TOLERANCE;
|
||||
if(difference < 0) {
|
||||
difference = 0;
|
||||
}
|
||||
return difference;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param pixels The list of pixels to be averaged.
|
||||
* @return The average of all the green values in the given list of pixels.
|
||||
*/
|
||||
private int diffGreen(List<Color> pixels)
|
||||
{
|
||||
int max = 0;
|
||||
int min = 255;
|
||||
for(Color color : pixels) {
|
||||
int val = color.getGreen();
|
||||
if(val > max) {
|
||||
max = val;
|
||||
}
|
||||
if(val < min) {
|
||||
min = val;
|
||||
}
|
||||
}
|
||||
int difference = max - min - TOLERANCE;
|
||||
if(difference < 0) {
|
||||
difference = 0;
|
||||
}
|
||||
return difference;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param pixels The list of pixels to be averaged.
|
||||
* @return The average of all the blue values in the given list of pixels.
|
||||
*/
|
||||
private int diffBlue(List<Color> pixels)
|
||||
{
|
||||
int max = 0;
|
||||
int min = 255;
|
||||
for(Color color : pixels) {
|
||||
int val = color.getBlue();
|
||||
if(val > max) {
|
||||
max = val;
|
||||
}
|
||||
if(val < min) {
|
||||
min = val;
|
||||
}
|
||||
}
|
||||
int difference = max - min - TOLERANCE;
|
||||
if(difference < 0) {
|
||||
difference = 0;
|
||||
}
|
||||
return difference;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,8 @@
|
||||
#BlueJ class context
|
||||
comment0.params=name
|
||||
comment0.target=Filter(java.lang.String)
|
||||
comment0.text=\n\ Create\ a\ new\ filter\ with\ a\ given\ name.\n\ @param\ name\ The\ name\ of\ the\ filter.\n
|
||||
comment1.params=
|
||||
comment1.target=java.lang.String\ getName()
|
||||
comment1.text=\n\ Return\ the\ name\ of\ this\ filter.\n\ \n\ @return\ \ The\ name\ of\ this\ filter.\n
|
||||
numComments=2
|
@@ -0,0 +1,38 @@
|
||||
/**
|
||||
* Filter is an abstract superclass for all image filters in this
|
||||
* application. Filters can be applied to OFImages by invoking the apply
|
||||
* method.
|
||||
*
|
||||
* @author Michael Kölling and David J. Barnes.
|
||||
* @version 1.0
|
||||
*/
|
||||
public abstract class Filter
|
||||
{
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* Create a new filter with a given name.
|
||||
* @param name The name of the filter.
|
||||
*/
|
||||
public Filter(String name)
|
||||
{
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the name of this filter.
|
||||
*
|
||||
* @return The name of this filter.
|
||||
*/
|
||||
public String getName()
|
||||
{
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply this filter to an image.
|
||||
*
|
||||
* @param image The image to be changed by this filter.
|
||||
*/
|
||||
public abstract void apply(OFImage image);
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
#BlueJ class context
|
||||
comment0.params=name
|
||||
comment0.target=FishEyeFilter(java.lang.String)
|
||||
comment0.text=\n\ Constructor\ for\ objects\ of\ class\ LensFilter.\n\ @param\ name\ The\ name\ of\ the\ filter.\n
|
||||
comment1.params=width
|
||||
comment1.target=int[]\ computeXArray(int)
|
||||
comment1.text=\n\ Compute\ and\ return\ an\ array\ of\ horizontal\ offsets\ for\ each\ pixel\ column.\n\ These\ can\ then\ be\ applied\ as\ the\ horizontal\ offset\ for\ each\ pixel.\n
|
||||
comment2.params=height
|
||||
comment2.target=int[]\ computeYArray(int)
|
||||
comment2.text=\n\ Compute\ and\ return\ an\ array\ of\ vertical\ offsets\ for\ each\ pixel\ row.\n\ These\ can\ then\ be\ applied\ as\ the\ vertical\ offset\ for\ each\ pixel.\n
|
||||
numComments=3
|
@@ -0,0 +1,73 @@
|
||||
import java.awt.Color;
|
||||
|
||||
/**
|
||||
* An image filter to create an effect similar to a fisheye camera lens.
|
||||
* (Works especially well on portraits.)
|
||||
*
|
||||
* @author Michael Kölling and David J. Barnes.
|
||||
* @version 1.0
|
||||
*/
|
||||
public class FishEyeFilter extends Filter
|
||||
{
|
||||
// constants:
|
||||
private final static int SCALE = 20; // this defines the strenght of the filter
|
||||
private final static double TWO_PI = 2 * Math.PI;
|
||||
|
||||
/**
|
||||
* Constructor for objects of class LensFilter.
|
||||
* @param name The name of the filter.
|
||||
*/
|
||||
public FishEyeFilter(String name)
|
||||
{
|
||||
super(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply this filter to an image.
|
||||
*
|
||||
* @param image The image to be changed by this filter.
|
||||
*/
|
||||
public void apply(OFImage image)
|
||||
{
|
||||
int height = image.getHeight();
|
||||
int width = image.getWidth();
|
||||
OFImage original = new OFImage(image);
|
||||
|
||||
int[] xa = computeXArray(width);
|
||||
int[] ya = computeYArray(height);
|
||||
|
||||
for(int y = 0; y < height; y++) {
|
||||
for(int x = 0; x < width; x++) {
|
||||
image.setPixel(x, y, original.getPixel(x + xa[x], y + ya[y]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute and return an array of horizontal offsets for each pixel column.
|
||||
* These can then be applied as the horizontal offset for each pixel.
|
||||
*/
|
||||
private int[] computeXArray(int width)
|
||||
{
|
||||
int[] xArray = new int[width];
|
||||
|
||||
for(int i=0; i < width; i++) {
|
||||
xArray[i] = (int)(Math.sin( ((double)i / width) * TWO_PI) * SCALE);
|
||||
}
|
||||
return xArray;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute and return an array of vertical offsets for each pixel row.
|
||||
* These can then be applied as the vertical offset for each pixel.
|
||||
*/
|
||||
private int[] computeYArray(int height)
|
||||
{
|
||||
int[] yArray = new int[height];
|
||||
|
||||
for(int i=0; i < height; i++) {
|
||||
yArray[i] = (int)(Math.sin( ((double)i / height) * TWO_PI) * SCALE);
|
||||
}
|
||||
return yArray;
|
||||
}
|
||||
}
|
@@ -0,0 +1,5 @@
|
||||
#BlueJ class context
|
||||
comment0.params=name
|
||||
comment0.target=GrayScaleFilter(java.lang.String)
|
||||
comment0.text=\n\ Constructor\ for\ objects\ of\ class\ GrayScaleFilter.\n\ @param\ name\ The\ name\ of\ the\ filter.\n
|
||||
numComments=1
|
@@ -0,0 +1,37 @@
|
||||
import java.awt.Color;
|
||||
|
||||
/**
|
||||
* An image filter to remove color from an image.
|
||||
*
|
||||
* @author Michael Kölling and David J. Barnes.
|
||||
* @version 1.0
|
||||
*/
|
||||
public class GrayScaleFilter extends Filter
|
||||
{
|
||||
/**
|
||||
* Constructor for objects of class GrayScaleFilter.
|
||||
* @param name The name of the filter.
|
||||
*/
|
||||
public GrayScaleFilter(String name)
|
||||
{
|
||||
super(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply this filter to an image.
|
||||
*
|
||||
* @param image The image to be changed by this filter.
|
||||
*/
|
||||
public void apply(OFImage image)
|
||||
{
|
||||
int height = image.getHeight();
|
||||
int width = image.getWidth();
|
||||
for(int y = 0; y < height; y++) {
|
||||
for(int x = 0; x < width; x++) {
|
||||
Color pix = image.getPixel(x, y);
|
||||
int avg = (pix.getRed() + pix.getGreen() + pix.getBlue()) / 3;
|
||||
image.setPixel(x, y, new Color(avg, avg, avg));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,8 @@
|
||||
#BlueJ class context
|
||||
comment0.params=imageFile
|
||||
comment0.target=OFImage\ loadImage(java.io.File)
|
||||
comment0.text=\n\ Read\ an\ image\ file\ from\ disk\ and\ return\ it\ as\ an\ image.\ This\ method\n\ can\ read\ JPG\ and\ PNG\ file\ formats.\ In\ case\ of\ any\ problem\ (e.g\ the\ file\ \n\ does\ not\ exist,\ is\ in\ an\ undecodable\ format,\ or\ any\ other\ read\ error)\ \n\ this\ method\ returns\ null.\n\ \n\ @param\ imageFile\ \ The\ image\ file\ to\ be\ loaded.\n\ @return\ \ \ \ \ \ \ \ \ \ \ The\ image\ object\ or\ null\ if\ it\ could\ not\ be\ read.\n
|
||||
comment1.params=image\ file
|
||||
comment1.target=void\ saveImage(OFImage,\ java.io.File)
|
||||
comment1.text=\n\ Write\ an\ image\ file\ to\ disk.\ The\ file\ format\ is\ JPG.\ In\ case\ of\ any\ \n\ problem\ the\ method\ just\ silently\ returns.\n\ \n\ @param\ image\ \ The\ image\ to\ be\ saved.\n\ @param\ file\ \ \ The\ file\ to\ save\ to.\n
|
||||
numComments=2
|
@@ -0,0 +1,61 @@
|
||||
import java.awt.image.*;
|
||||
import javax.imageio.*;
|
||||
import java.io.*;
|
||||
|
||||
/**
|
||||
* ImageFileManager is a small utility class with static methods to load
|
||||
* and save images.
|
||||
*
|
||||
* The files on disk can be in JPG or PNG image format. For files written
|
||||
* by this class, the format is determined by the constant IMAGE_FORMAT.
|
||||
*
|
||||
* @author Michael Kölling and David J. Barnes.
|
||||
* @version 2.0
|
||||
*/
|
||||
public class ImageFileManager
|
||||
{
|
||||
// A constant for the image format that this writer uses for writing.
|
||||
// Available formats are "jpg" and "png".
|
||||
private static final String IMAGE_FORMAT = "jpg";
|
||||
|
||||
/**
|
||||
* Read an image file from disk and return it as an image. This method
|
||||
* can read JPG and PNG file formats. In case of any problem (e.g the file
|
||||
* does not exist, is in an undecodable format, or any other read error)
|
||||
* this method returns null.
|
||||
*
|
||||
* @param imageFile The image file to be loaded.
|
||||
* @return The image object or null if it could not be read.
|
||||
*/
|
||||
public static OFImage loadImage(File imageFile)
|
||||
{
|
||||
try {
|
||||
BufferedImage image = ImageIO.read(imageFile);
|
||||
if(image == null || (image.getWidth(null) < 0)) {
|
||||
// we could not load the image - probably invalid file format
|
||||
return null;
|
||||
}
|
||||
return new OFImage(image);
|
||||
}
|
||||
catch(IOException exc) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Write an image file to disk. The file format is JPG. In case of any
|
||||
* problem the method just silently returns.
|
||||
*
|
||||
* @param image The image to be saved.
|
||||
* @param file The file to save to.
|
||||
*/
|
||||
public static void saveImage(OFImage image, File file)
|
||||
{
|
||||
try {
|
||||
ImageIO.write(image, IMAGE_FORMAT, file);
|
||||
}
|
||||
catch(IOException exc) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,14 @@
|
||||
#BlueJ class context
|
||||
comment0.params=
|
||||
comment0.target=ImagePanel()
|
||||
comment0.text=\n\ Create\ a\ new,\ empty\ ImagePanel.\n
|
||||
comment1.params=
|
||||
comment1.target=void\ clearImage()
|
||||
comment1.text=\n\ Clear\ the\ image\ on\ this\ panel.\n
|
||||
comment2.params=
|
||||
comment2.target=java.awt.Dimension\ getPreferredSize()
|
||||
comment2.text=\n\ Tell\ the\ layout\ manager\ how\ big\ we\ would\ like\ to\ be.\n\ (This\ method\ gets\ called\ by\ layout\ managers\ for\ placing\n\ the\ components.)\n\ \n\ @return\ The\ preferred\ dimension\ for\ this\ component.\n
|
||||
comment3.params=g
|
||||
comment3.target=void\ paintComponent(java.awt.Graphics)
|
||||
comment3.text=\n\ This\ component\ needs\ to\ be\ redisplayed.\ Copy\ the\ internal\ image\ \n\ to\ screen.\ (This\ method\ gets\ called\ by\ the\ Swing\ screen\ painter\ \n\ every\ time\ it\ want\ this\ component\ displayed.)\n\ \n\ @param\ g\ The\ graphics\ context\ that\ can\ be\ used\ to\ draw\ on\ this\ component.\n
|
||||
numComments=4
|
@@ -0,0 +1,89 @@
|
||||
import java.awt.*;
|
||||
import javax.swing.*;
|
||||
import java.awt.image.*;
|
||||
|
||||
/**
|
||||
* An ImagePanel is a Swing component that can display an OFImage.
|
||||
* It is constructed as a subclass of JComponent with the added functionality
|
||||
* of setting an OFImage that will be displayed on the surface of this
|
||||
* component.
|
||||
*
|
||||
* @author Michael Kölling and David J. Barnes.
|
||||
* @version 1.0
|
||||
*/
|
||||
public class ImagePanel extends JComponent
|
||||
{
|
||||
// The current width and height of this panel
|
||||
private int width, height;
|
||||
|
||||
// An internal image buffer that is used for painting. For
|
||||
// actual display, this image buffer is then copied to screen.
|
||||
private OFImage panelImage;
|
||||
|
||||
/**
|
||||
* Create a new, empty ImagePanel.
|
||||
*/
|
||||
public ImagePanel()
|
||||
{
|
||||
width = 360; // arbitrary size for empty panel
|
||||
height = 240;
|
||||
panelImage = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the image that this panel should show.
|
||||
*
|
||||
* @param image The image to be displayed.
|
||||
*/
|
||||
public void setImage(OFImage image)
|
||||
{
|
||||
if(image != null) {
|
||||
width = image.getWidth();
|
||||
height = image.getHeight();
|
||||
panelImage = image;
|
||||
repaint();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the image on this panel.
|
||||
*/
|
||||
public void clearImage()
|
||||
{
|
||||
Graphics imageGraphics = panelImage.getGraphics();
|
||||
imageGraphics.setColor(Color.LIGHT_GRAY);
|
||||
imageGraphics.fillRect(0, 0, width, height);
|
||||
repaint();
|
||||
}
|
||||
|
||||
// The following methods are redefinitions of methods
|
||||
// inherited from superclasses.
|
||||
|
||||
/**
|
||||
* Tell the layout manager how big we would like to be.
|
||||
* (This method gets called by layout managers for placing
|
||||
* the components.)
|
||||
*
|
||||
* @return The preferred dimension for this component.
|
||||
*/
|
||||
public Dimension getPreferredSize()
|
||||
{
|
||||
return new Dimension(width, height);
|
||||
}
|
||||
|
||||
/**
|
||||
* This component needs to be redisplayed. Copy the internal image
|
||||
* to screen. (This method gets called by the Swing screen painter
|
||||
* every time it want this component displayed.)
|
||||
*
|
||||
* @param g The graphics context that can be used to draw on this component.
|
||||
*/
|
||||
public void paintComponent(Graphics g)
|
||||
{
|
||||
Dimension size = getSize();
|
||||
g.clearRect(0, 0, size.width, size.height);
|
||||
if(panelImage != null) {
|
||||
g.drawImage(panelImage, 0, 0, null);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,47 @@
|
||||
#BlueJ class context
|
||||
comment0.params=
|
||||
comment0.target=ImageViewer()
|
||||
comment0.text=\n\ Create\ an\ ImageViewer\ and\ display\ its\ GUI\ on\ screen.\n
|
||||
comment1.params=
|
||||
comment1.target=void\ openFile()
|
||||
comment1.text=\n\ Open\ function\:\ open\ a\ file\ chooser\ to\ select\ a\ new\ image\ file,\n\ and\ then\ display\ the\ chosen\ image.\n
|
||||
comment10.params=text
|
||||
comment10.target=void\ showStatus(java.lang.String)
|
||||
comment10.text=\n\ Show\ a\ message\ in\ the\ status\ bar\ at\ the\ bottom\ of\ the\ screen.\n\ @param\ text\ The\ status\ message.\n
|
||||
comment11.params=status
|
||||
comment11.target=void\ setButtonsEnabled(boolean)
|
||||
comment11.text=\n\ Enable\ or\ disable\ all\ toolbar\ buttons.\n\ \n\ @param\ status\ \ 'true'\ to\ enable\ the\ buttons,\ 'false'\ to\ disable.\n
|
||||
comment12.params=
|
||||
comment12.target=java.util.List\ createFilters()
|
||||
comment12.text=\n\ Create\ a\ list\ with\ all\ the\ known\ filters.\n\ @return\ The\ list\ of\ filters.\n
|
||||
comment13.params=
|
||||
comment13.target=void\ makeFrame()
|
||||
comment13.text=\n\ Create\ the\ Swing\ frame\ and\ its\ content.\n
|
||||
comment14.params=frame
|
||||
comment14.target=void\ makeMenuBar(javax.swing.JFrame)
|
||||
comment14.text=\n\ Create\ the\ main\ frame's\ menu\ bar.\n\ \n\ @param\ frame\ \ \ The\ frame\ that\ the\ menu\ bar\ should\ be\ added\ to.\n
|
||||
comment2.params=
|
||||
comment2.target=void\ close()
|
||||
comment2.text=\n\ Close\ function\:\ close\ the\ current\ image.\n
|
||||
comment3.params=
|
||||
comment3.target=void\ saveAs()
|
||||
comment3.text=\n\ Save\ As\ function\:\ save\ the\ current\ image\ to\ a\ file.\n
|
||||
comment4.params=
|
||||
comment4.target=void\ quit()
|
||||
comment4.text=\n\ Quit\ function\:\ quit\ the\ application.\n
|
||||
comment5.params=filter
|
||||
comment5.target=void\ applyFilter(Filter)
|
||||
comment5.text=\n\ Apply\ a\ given\ filter\ to\ the\ current\ image.\n\ \n\ @param\ filter\ \ \ The\ filter\ object\ to\ be\ applied.\n
|
||||
comment6.params=
|
||||
comment6.target=void\ showAbout()
|
||||
comment6.text=\n\ 'About'\ function\:\ show\ the\ 'about'\ box.\n
|
||||
comment7.params=
|
||||
comment7.target=void\ makeLarger()
|
||||
comment7.text=\n\ Make\ the\ current\ picture\ larger.\n
|
||||
comment8.params=
|
||||
comment8.target=void\ makeSmaller()
|
||||
comment8.text=\n\ Make\ the\ current\ picture\ smaller.\n
|
||||
comment9.params=filename
|
||||
comment9.target=void\ showFilename(java.lang.String)
|
||||
comment9.text=\n\ Show\ the\ file\ name\ of\ the\ current\ image\ in\ the\ fils\ display\ label.\n\ 'null'\ may\ be\ used\ as\ a\ parameter\ if\ no\ file\ is\ currently\ loaded.\n\ \n\ @param\ filename\ \ The\ file\ name\ to\ be\ displayed,\ or\ null\ for\ 'no\ file'.\n
|
||||
numComments=15
|
@@ -0,0 +1,381 @@
|
||||
import java.awt.*;
|
||||
import java.awt.event.*;
|
||||
import java.awt.image.*;
|
||||
import javax.swing.*;
|
||||
import javax.swing.border.*;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
|
||||
/**
|
||||
* ImageViewer is the main class of the image viewer application. It builds and
|
||||
* displays the application GUI and initialises all other components.
|
||||
*
|
||||
* To start the application, create an object of this class.
|
||||
*
|
||||
* @author Michael Kölling and David J. Barnes.
|
||||
* @version 3.1
|
||||
*/
|
||||
public class ImageViewer
|
||||
{
|
||||
// static fields:
|
||||
private static final String VERSION = "Version 3.1";
|
||||
private static JFileChooser fileChooser = new JFileChooser(System.getProperty("user.dir"));
|
||||
|
||||
// fields:
|
||||
private JFrame frame;
|
||||
private ImagePanel imagePanel;
|
||||
private JLabel filenameLabel;
|
||||
private JLabel statusLabel;
|
||||
private JButton smallerButton;
|
||||
private JButton largerButton;
|
||||
private OFImage currentImage;
|
||||
|
||||
private List<Filter> filters;
|
||||
|
||||
/**
|
||||
* Create an ImageViewer and display its GUI on screen.
|
||||
*/
|
||||
public ImageViewer()
|
||||
{
|
||||
currentImage = null;
|
||||
filters = createFilters();
|
||||
makeFrame();
|
||||
}
|
||||
|
||||
|
||||
// ---- implementation of menu functions ----
|
||||
|
||||
/**
|
||||
* Open function: open a file chooser to select a new image file,
|
||||
* and then display the chosen image.
|
||||
*/
|
||||
private void openFile()
|
||||
{
|
||||
int returnVal = fileChooser.showOpenDialog(frame);
|
||||
|
||||
if(returnVal != JFileChooser.APPROVE_OPTION) {
|
||||
return; // cancelled
|
||||
}
|
||||
File selectedFile = fileChooser.getSelectedFile();
|
||||
currentImage = ImageFileManager.loadImage(selectedFile);
|
||||
|
||||
if(currentImage == null) { // image file was not a valid image
|
||||
JOptionPane.showMessageDialog(frame,
|
||||
"The file was not in a recognized image file format.",
|
||||
"Image Load Error",
|
||||
JOptionPane.ERROR_MESSAGE);
|
||||
return;
|
||||
}
|
||||
|
||||
imagePanel.setImage(currentImage);
|
||||
setButtonsEnabled(true);
|
||||
showFilename(selectedFile.getPath());
|
||||
showStatus("File loaded.");
|
||||
frame.pack();
|
||||
}
|
||||
|
||||
/**
|
||||
* Close function: close the current image.
|
||||
*/
|
||||
private void close()
|
||||
{
|
||||
currentImage = null;
|
||||
imagePanel.clearImage();
|
||||
showFilename(null);
|
||||
setButtonsEnabled(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Save As function: save the current image to a file.
|
||||
*/
|
||||
private void saveAs()
|
||||
{
|
||||
if(currentImage != null) {
|
||||
int returnVal = fileChooser.showSaveDialog(frame);
|
||||
|
||||
if(returnVal != JFileChooser.APPROVE_OPTION) {
|
||||
return; // cancelled
|
||||
}
|
||||
File selectedFile = fileChooser.getSelectedFile();
|
||||
ImageFileManager.saveImage(currentImage, selectedFile);
|
||||
|
||||
showFilename(selectedFile.getPath());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Quit function: quit the application.
|
||||
*/
|
||||
private void quit()
|
||||
{
|
||||
System.exit(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply a given filter to the current image.
|
||||
*
|
||||
* @param filter The filter object to be applied.
|
||||
*/
|
||||
private void applyFilter(Filter filter)
|
||||
{
|
||||
if(currentImage != null) {
|
||||
filter.apply(currentImage);
|
||||
frame.repaint();
|
||||
showStatus("Applied: " + filter.getName());
|
||||
}
|
||||
else {
|
||||
showStatus("No image loaded.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 'About' function: show the 'about' box.
|
||||
*/
|
||||
private void showAbout()
|
||||
{
|
||||
JOptionPane.showMessageDialog(frame,
|
||||
"ImageViewer\n" + VERSION,
|
||||
"About ImageViewer",
|
||||
JOptionPane.INFORMATION_MESSAGE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Make the current picture larger.
|
||||
*/
|
||||
private void makeLarger()
|
||||
{
|
||||
if(currentImage != null) {
|
||||
// create new image with double size
|
||||
int width = currentImage.getWidth();
|
||||
int height = currentImage.getHeight();
|
||||
OFImage newImage = new OFImage(width * 2, height * 2);
|
||||
|
||||
// copy pixel data into new image
|
||||
for(int y = 0; y < height; y++) {
|
||||
for(int x = 0; x < width; x++) {
|
||||
Color col = currentImage.getPixel(x, y);
|
||||
newImage.setPixel(x * 2, y * 2, col);
|
||||
newImage.setPixel(x * 2 + 1, y * 2, col);
|
||||
newImage.setPixel(x * 2, y * 2 + 1, col);
|
||||
newImage.setPixel(x * 2+1, y * 2 + 1, col);
|
||||
}
|
||||
}
|
||||
|
||||
currentImage = newImage;
|
||||
imagePanel.setImage(currentImage);
|
||||
frame.pack();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Make the current picture smaller.
|
||||
*/
|
||||
private void makeSmaller()
|
||||
{
|
||||
if(currentImage != null) {
|
||||
// create new image with double size
|
||||
int width = currentImage.getWidth() / 2;
|
||||
int height = currentImage.getHeight() / 2;
|
||||
OFImage newImage = new OFImage(width, height);
|
||||
|
||||
// copy pixel data into new image
|
||||
for(int y = 0; y < height; y++) {
|
||||
for(int x = 0; x < width; x++) {
|
||||
newImage.setPixel(x, y, currentImage.getPixel(x * 2, y * 2));
|
||||
}
|
||||
}
|
||||
|
||||
currentImage = newImage;
|
||||
imagePanel.setImage(currentImage);
|
||||
frame.pack();
|
||||
}
|
||||
}
|
||||
|
||||
// ---- support methods ----
|
||||
|
||||
/**
|
||||
* Show the file name of the current image in the fils display label.
|
||||
* 'null' may be used as a parameter if no file is currently loaded.
|
||||
*
|
||||
* @param filename The file name to be displayed, or null for 'no file'.
|
||||
*/
|
||||
private void showFilename(String filename)
|
||||
{
|
||||
if(filename == null) {
|
||||
filenameLabel.setText("No file displayed.");
|
||||
}
|
||||
else {
|
||||
filenameLabel.setText("File: " + filename);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Show a message in the status bar at the bottom of the screen.
|
||||
* @param text The status message.
|
||||
*/
|
||||
private void showStatus(String text)
|
||||
{
|
||||
statusLabel.setText(text);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Enable or disable all toolbar buttons.
|
||||
*
|
||||
* @param status 'true' to enable the buttons, 'false' to disable.
|
||||
*/
|
||||
private void setButtonsEnabled(boolean status)
|
||||
{
|
||||
smallerButton.setEnabled(status);
|
||||
largerButton.setEnabled(status);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create a list with all the known filters.
|
||||
* @return The list of filters.
|
||||
*/
|
||||
private List<Filter> createFilters()
|
||||
{
|
||||
List<Filter> filterList = new ArrayList<>();
|
||||
filterList.add(new DarkerFilter("Darker"));
|
||||
filterList.add(new LighterFilter("Lighter"));
|
||||
filterList.add(new ThresholdFilter("Threshold"));
|
||||
filterList.add(new InvertFilter("Invert"));
|
||||
filterList.add(new SolarizeFilter("Solarize"));
|
||||
filterList.add(new SmoothFilter("Smooth"));
|
||||
filterList.add(new PixelizeFilter("Pixelize"));
|
||||
filterList.add(new MirrorFilter("Mirror"));
|
||||
filterList.add(new GrayScaleFilter("Grayscale"));
|
||||
filterList.add(new EdgeFilter("Edge Detection"));
|
||||
filterList.add(new FishEyeFilter("Fish Eye"));
|
||||
|
||||
return filterList;
|
||||
}
|
||||
|
||||
// ---- Swing stuff to build the frame and all its components and menus ----
|
||||
|
||||
/**
|
||||
* Create the Swing frame and its content.
|
||||
*/
|
||||
private void makeFrame()
|
||||
{
|
||||
frame = new JFrame("ImageViewer");
|
||||
JPanel contentPane = (JPanel)frame.getContentPane();
|
||||
contentPane.setBorder(new EmptyBorder(12, 12, 12, 12));
|
||||
|
||||
makeMenuBar(frame);
|
||||
|
||||
// Specify the layout manager with nice spacing
|
||||
contentPane.setLayout(new BorderLayout(6, 6));
|
||||
|
||||
// Create the image pane in the center
|
||||
imagePanel = new ImagePanel();
|
||||
imagePanel.setBorder(new EtchedBorder());
|
||||
contentPane.add(imagePanel, BorderLayout.CENTER);
|
||||
|
||||
// Create two labels at top and bottom for the file name and status messages
|
||||
filenameLabel = new JLabel();
|
||||
contentPane.add(filenameLabel, BorderLayout.NORTH);
|
||||
|
||||
statusLabel = new JLabel(VERSION);
|
||||
contentPane.add(statusLabel, BorderLayout.SOUTH);
|
||||
|
||||
// Create the toolbar with the buttons
|
||||
JPanel toolbar = new JPanel();
|
||||
toolbar.setLayout(new GridLayout(0, 1));
|
||||
|
||||
smallerButton = new JButton("Smaller");
|
||||
smallerButton.addActionListener(e -> makeSmaller());
|
||||
toolbar.add(smallerButton);
|
||||
|
||||
largerButton = new JButton("Larger");
|
||||
largerButton.addActionListener(e -> makeLarger());
|
||||
toolbar.add(largerButton);
|
||||
|
||||
// Add toolbar into panel with flow layout for spacing
|
||||
JPanel flow = new JPanel();
|
||||
flow.add(toolbar);
|
||||
|
||||
contentPane.add(flow, BorderLayout.WEST);
|
||||
|
||||
// building is done - arrange the components
|
||||
showFilename(null);
|
||||
setButtonsEnabled(false);
|
||||
frame.pack();
|
||||
|
||||
// place the frame at the center of the screen and show
|
||||
Dimension d = Toolkit.getDefaultToolkit().getScreenSize();
|
||||
frame.setLocation(d.width/2 - frame.getWidth()/2, d.height/2 - frame.getHeight()/2);
|
||||
frame.setVisible(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the main frame's menu bar.
|
||||
*
|
||||
* @param frame The frame that the menu bar should be added to.
|
||||
*/
|
||||
private void makeMenuBar(JFrame frame)
|
||||
{
|
||||
final int SHORTCUT_MASK =
|
||||
Toolkit.getDefaultToolkit().getMenuShortcutKeyMask();
|
||||
|
||||
JMenuBar menubar = new JMenuBar();
|
||||
frame.setJMenuBar(menubar);
|
||||
|
||||
JMenu menu;
|
||||
JMenuItem item;
|
||||
|
||||
// create the File menu
|
||||
menu = new JMenu("File");
|
||||
menubar.add(menu);
|
||||
|
||||
item = new JMenuItem("Open...");
|
||||
item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_O, SHORTCUT_MASK));
|
||||
item.addActionListener(e -> openFile());
|
||||
menu.add(item);
|
||||
|
||||
item = new JMenuItem("Close");
|
||||
item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_W, SHORTCUT_MASK));
|
||||
item.addActionListener(e -> close());
|
||||
menu.add(item);
|
||||
menu.addSeparator();
|
||||
|
||||
item = new JMenuItem("Save As...");
|
||||
item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_S, SHORTCUT_MASK));
|
||||
item.addActionListener(e -> saveAs());
|
||||
menu.add(item);
|
||||
menu.addSeparator();
|
||||
|
||||
item = new JMenuItem("Quit");
|
||||
item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_Q, SHORTCUT_MASK));
|
||||
item.addActionListener(e -> quit());
|
||||
menu.add(item);
|
||||
|
||||
|
||||
// create the Filter menu
|
||||
menu = new JMenu("Filter");
|
||||
menubar.add(menu);
|
||||
|
||||
for(Filter filter : filters) {
|
||||
item = new JMenuItem(filter.getName());
|
||||
item.addActionListener(e -> applyFilter(filter));
|
||||
menu.add(item);
|
||||
}
|
||||
|
||||
// create the Help menu
|
||||
menu = new JMenu("Help");
|
||||
menubar.add(menu);
|
||||
|
||||
item = new JMenuItem("About ImageViewer...");
|
||||
item.addActionListener(e -> showAbout());
|
||||
menu.add(item);
|
||||
|
||||
}
|
||||
}
|
@@ -0,0 +1,5 @@
|
||||
#BlueJ class context
|
||||
comment0.params=name
|
||||
comment0.target=InvertFilter(java.lang.String)
|
||||
comment0.text=\n\ Constructor\ for\ objects\ of\ class\ InvertFilter.\n\ @param\ name\ The\ name\ of\ the\ filter.\n
|
||||
numComments=1
|
@@ -0,0 +1,38 @@
|
||||
import java.awt.Color;
|
||||
|
||||
/**
|
||||
* An image filter to invert colors.
|
||||
*
|
||||
* @author Michael Kölling and David J. Barnes.
|
||||
* @version 1.0
|
||||
*/
|
||||
public class InvertFilter extends Filter
|
||||
{
|
||||
/**
|
||||
* Constructor for objects of class InvertFilter.
|
||||
* @param name The name of the filter.
|
||||
*/
|
||||
public InvertFilter(String name)
|
||||
{
|
||||
super(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply this filter to an image.
|
||||
*
|
||||
* @param image The image to be changed by this filter.
|
||||
*/
|
||||
public void apply(OFImage image)
|
||||
{
|
||||
int height = image.getHeight();
|
||||
int width = image.getWidth();
|
||||
for(int y = 0; y < height; y++) {
|
||||
for(int x = 0; x < width; x++) {
|
||||
Color pix = image.getPixel(x, y);
|
||||
image.setPixel(x, y, new Color(255 - pix.getRed(),
|
||||
255 - pix.getGreen(),
|
||||
255 - pix.getBlue()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,5 @@
|
||||
#BlueJ class context
|
||||
comment0.params=name
|
||||
comment0.target=LighterFilter(java.lang.String)
|
||||
comment0.text=\n\ Constructor\ for\ objects\ of\ class\ LighterFilter.\n\ @param\ name\ The\ name\ of\ the\ filter.\n
|
||||
numComments=1
|
@@ -0,0 +1,34 @@
|
||||
/**
|
||||
* An image filter to make the image a bit lighter.
|
||||
*
|
||||
* @author Michael Kölling and David J. Barnes.
|
||||
* @version 1.0
|
||||
*/
|
||||
public class LighterFilter extends Filter
|
||||
{
|
||||
/**
|
||||
* Constructor for objects of class LighterFilter.
|
||||
* @param name The name of the filter.
|
||||
*/
|
||||
public LighterFilter(String name)
|
||||
{
|
||||
super(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply this filter to an image.
|
||||
*
|
||||
* @param image The image to be changed by this filter.
|
||||
*/
|
||||
public void apply(OFImage image)
|
||||
{
|
||||
int height = image.getHeight();
|
||||
int width = image.getWidth();
|
||||
for(int y = 0; y < height; y++) {
|
||||
for(int x = 0; x < width; x++) {
|
||||
image.setPixel(x, y, image.getPixel(x, y).brighter());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,5 @@
|
||||
#BlueJ class context
|
||||
comment0.params=name
|
||||
comment0.target=MirrorFilter(java.lang.String)
|
||||
comment0.text=\n\ Constructor\ for\ objects\ of\ class\ MirrorFilter.\n\ @param\ name\ The\ name\ of\ the\ filter.\n
|
||||
numComments=1
|
@@ -0,0 +1,37 @@
|
||||
import java.awt.Color;
|
||||
|
||||
/**
|
||||
* An image filter to mirror (flip) the image horizontally.
|
||||
*
|
||||
* @author Michael Kölling and David J. Barnes.
|
||||
* @version 1.0
|
||||
*/
|
||||
public class MirrorFilter extends Filter
|
||||
{
|
||||
/**
|
||||
* Constructor for objects of class MirrorFilter.
|
||||
* @param name The name of the filter.
|
||||
*/
|
||||
public MirrorFilter(String name)
|
||||
{
|
||||
super(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply this filter to an image.
|
||||
*
|
||||
* @param image The image to be changed by this filter.
|
||||
*/
|
||||
public void apply(OFImage image)
|
||||
{
|
||||
int height = image.getHeight();
|
||||
int width = image.getWidth();
|
||||
for(int y = 0; y < height; y++) {
|
||||
for(int x = 0; x < width / 2; x++) {
|
||||
Color left = image.getPixel(x, y);
|
||||
image.setPixel(x, y, image.getPixel(width - 1 - x, y));
|
||||
image.setPixel(width - 1 - x, y, left);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,14 @@
|
||||
#BlueJ class context
|
||||
comment0.params=image
|
||||
comment0.target=OFImage(java.awt.image.BufferedImage)
|
||||
comment0.text=\n\ Create\ an\ OFImage\ copied\ from\ a\ BufferedImage.\n\ @param\ image\ The\ image\ to\ copy.\n
|
||||
comment1.params=width\ height
|
||||
comment1.target=OFImage(int,\ int)
|
||||
comment1.text=\n\ Create\ an\ OFImage\ with\ specified\ size\ and\ unspecified\ content.\n\ @param\ width\ The\ width\ of\ the\ image.\n\ @param\ height\ The\ height\ of\ the\ image.\n
|
||||
comment2.params=x\ y\ col
|
||||
comment2.target=void\ setPixel(int,\ int,\ java.awt.Color)
|
||||
comment2.text=\n\ Set\ a\ given\ pixel\ of\ this\ image\ to\ a\ specified\ color.\ The\n\ color\ is\ represented\ as\ an\ (r,g,b)\ value.\n\ @param\ x\ The\ x\ position\ of\ the\ pixel.\n\ @param\ y\ The\ y\ position\ of\ the\ pixel.\n\ @param\ col\ The\ color\ of\ the\ pixel.\n
|
||||
comment3.params=x\ y
|
||||
comment3.target=java.awt.Color\ getPixel(int,\ int)
|
||||
comment3.text=\n\ Get\ the\ color\ value\ at\ a\ specified\ pixel\ position.\n\ @param\ x\ The\ x\ position\ of\ the\ pixel.\n\ @param\ y\ The\ y\ position\ of\ the\ pixel.\n\ @return\ The\ color\ of\ the\ pixel\ at\ the\ given\ position.\n
|
||||
numComments=4
|
@@ -0,0 +1,57 @@
|
||||
import java.awt.*;
|
||||
import java.awt.image.*;
|
||||
import javax.swing.*;
|
||||
|
||||
/**
|
||||
* OFImage is a class that defines an image in OF (Objects First) format.
|
||||
*
|
||||
* @author Michael Kölling and David J. Barnes.
|
||||
* @version 2.0
|
||||
*/
|
||||
public class OFImage extends BufferedImage
|
||||
{
|
||||
/**
|
||||
* Create an OFImage copied from a BufferedImage.
|
||||
* @param image The image to copy.
|
||||
*/
|
||||
public OFImage(BufferedImage image)
|
||||
{
|
||||
super(image.getColorModel(), image.copyData(null),
|
||||
image.isAlphaPremultiplied(), null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an OFImage with specified size and unspecified content.
|
||||
* @param width The width of the image.
|
||||
* @param height The height of the image.
|
||||
*/
|
||||
public OFImage(int width, int height)
|
||||
{
|
||||
super(width, height, TYPE_INT_RGB);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a given pixel of this image to a specified color. The
|
||||
* color is represented as an (r,g,b) value.
|
||||
* @param x The x position of the pixel.
|
||||
* @param y The y position of the pixel.
|
||||
* @param col The color of the pixel.
|
||||
*/
|
||||
public void setPixel(int x, int y, Color col)
|
||||
{
|
||||
int pixel = col.getRGB();
|
||||
setRGB(x, y, pixel);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the color value at a specified pixel position.
|
||||
* @param x The x position of the pixel.
|
||||
* @param y The y position of the pixel.
|
||||
* @return The color of the pixel at the given position.
|
||||
*/
|
||||
public Color getPixel(int x, int y)
|
||||
{
|
||||
int pixel = getRGB(x, y);
|
||||
return new Color(pixel);
|
||||
}
|
||||
}
|
@@ -0,0 +1,5 @@
|
||||
#BlueJ class context
|
||||
comment0.params=name
|
||||
comment0.target=PixelizeFilter(java.lang.String)
|
||||
comment0.text=\n\ Constructor\ for\ objects\ of\ class\ PixelizeFilter.\n\ @param\ name\ The\ name\ of\ the\ filter.\n
|
||||
numComments=1
|
@@ -0,0 +1,44 @@
|
||||
import java.awt.Color;
|
||||
|
||||
/**
|
||||
* An image filter to create a pixelization effect, like an enlarged
|
||||
* low-resolution digital image.
|
||||
*
|
||||
* @author Michael Kölling and David J. Barnes.
|
||||
* @version 1.0
|
||||
*/
|
||||
public class PixelizeFilter extends Filter
|
||||
{
|
||||
/**
|
||||
* Constructor for objects of class PixelizeFilter.
|
||||
* @param name The name of the filter.
|
||||
*/
|
||||
public PixelizeFilter(String name)
|
||||
{
|
||||
super(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply this filter to an image.
|
||||
*
|
||||
* @param image The image to be changed by this filter.
|
||||
*/
|
||||
public void apply(OFImage image)
|
||||
{
|
||||
final int PIXEL_SIZE = 5;
|
||||
int width = image.getWidth();
|
||||
int height = image.getHeight();
|
||||
|
||||
for(int y = 0; y < height; y += PIXEL_SIZE) {
|
||||
for(int x = 0; x < width; x += PIXEL_SIZE) {
|
||||
Color pix = image.getPixel(x, y);
|
||||
for(int dy = y; dy < y + PIXEL_SIZE; dy++) {
|
||||
for(int dx = x; dx < x + PIXEL_SIZE; dx++) {
|
||||
if( dx < width && dy < height )
|
||||
image.setPixel(dx, dy, pix);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,25 @@
|
||||
Project "imageviewer-final" (version 3.1)
|
||||
Authors: David Barnes and Michael Kölling
|
||||
|
||||
This project is part of the material for the book
|
||||
|
||||
Objects First with Java - A Practical Introduction using BlueJ
|
||||
Sixth edition
|
||||
David J. Barnes and Michael Kölling
|
||||
Pearson Education, 2016
|
||||
|
||||
It is discussed in chapter 13.
|
||||
|
||||
This project demonstrates the building of a simple GUI (including labels,
|
||||
buttons, layout managers, event handling, menus, dialogs) as well as a bit of
|
||||
image processing.
|
||||
|
||||
This project is started by creating an object of class ImageViewer. This will
|
||||
automatically display the application's GUI - no further interaction with the
|
||||
object on the object bench is necessary.
|
||||
|
||||
WARNING:
|
||||
|
||||
It sometimes happens (unfortunately) that the image viewer window opens BEHIND the
|
||||
BlueJ window. This can be confusing, since it initially appears as if nothing
|
||||
is happening.
|
@@ -0,0 +1,20 @@
|
||||
#BlueJ class context
|
||||
comment0.params=name
|
||||
comment0.target=SmoothFilter(java.lang.String)
|
||||
comment0.text=\n\ Constructor\ for\ objects\ of\ class\ SmoothFilter.\n\ @param\ name\ The\ name\ of\ the\ filter.\n
|
||||
comment1.params=image
|
||||
comment1.target=void\ apply(OFImage)
|
||||
comment1.text=\n\ Apply\ this\ filter\ to\ an\ image.\n\ \n\ @param\ \ image\ \ The\ image\ to\ be\ changed\ by\ this\ filter.\n
|
||||
comment2.params=xpos\ ypos
|
||||
comment2.target=java.awt.Color\ smooth(int,\ int)
|
||||
comment2.text=\n\ Return\ a\ new\ color\ that\ is\ the\ smoothed\ color\ of\ a\ given\n\ position.\ The\ "smoothed\ color"\ is\ the\ color\ value\ that\ is\ the\n\ average\ of\ this\ pixel\ and\ all\ the\ adjacent\ pixels.\n\ @param\ xpos\ The\ xposition\ of\ the\ pixel.\n\ @param\ ypos\ The\ yposition\ of\ the\ pixel.\n\ @return\ The\ smoothed\ color.\n
|
||||
comment3.params=pixels
|
||||
comment3.target=int\ avgRed(java.util.List)
|
||||
comment3.text=\n\ @param\ pixels\ The\ list\ of\ pixels.\n\ @return\ The\ average\ of\ all\ the\ red\ values\ in\ the\ given\ list\ of\ pixels.\n
|
||||
comment4.params=pixels
|
||||
comment4.target=int\ avgGreen(java.util.List)
|
||||
comment4.text=\n\ @param\ pixels\ The\ list\ of\ pixels.\n\ @return\ The\ average\ of\ all\ the\ green\ values\ in\ the\ given\ list\ of\ pixels.\n
|
||||
comment5.params=pixels
|
||||
comment5.target=int\ avgBlue(java.util.List)
|
||||
comment5.text=\n\ @param\ pixels\ The\ list\ of\ pixels.\n\ @return\ The\ average\ of\ all\ the\ blue\ values\ in\ the\ given\ list\ of\ pixels.\n
|
||||
numComments=6
|
@@ -0,0 +1,105 @@
|
||||
import java.awt.Color;
|
||||
import java.util.List;
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* An image filter to reduce sharp edges and pixelization. A bit like
|
||||
* a soft lens.
|
||||
*
|
||||
* @author Michael Kölling and David J. Barnes.
|
||||
* @version 1.0
|
||||
*/
|
||||
public class SmoothFilter extends Filter
|
||||
{
|
||||
private OFImage original;
|
||||
private int width;
|
||||
private int height;
|
||||
|
||||
/**
|
||||
* Constructor for objects of class SmoothFilter.
|
||||
* @param name The name of the filter.
|
||||
*/
|
||||
public SmoothFilter(String name)
|
||||
{
|
||||
super(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply this filter to an image.
|
||||
*
|
||||
* @param image The image to be changed by this filter.
|
||||
*/
|
||||
public void apply(OFImage image)
|
||||
{
|
||||
original = new OFImage(image);
|
||||
width = original.getWidth();
|
||||
height = original.getHeight();
|
||||
|
||||
for(int y = 0; y < height; y++) {
|
||||
for(int x = 0; x < width; x++) {
|
||||
image.setPixel(x, y, smooth(x, y));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a new color that is the smoothed color of a given
|
||||
* position. The "smoothed color" is the color value that is the
|
||||
* average of this pixel and all the adjacent pixels.
|
||||
* @param xpos The xposition of the pixel.
|
||||
* @param ypos The yposition of the pixel.
|
||||
* @return The smoothed color.
|
||||
*/
|
||||
private Color smooth(int xpos, int ypos)
|
||||
{
|
||||
List<Color> pixels = new ArrayList<>(9);
|
||||
|
||||
for(int y = ypos - 1; y <= ypos + 1; y++) {
|
||||
for(int x = xpos - 1; x <= xpos + 1; x++) {
|
||||
if( x >= 0 && x < width && y >= 0 && y < height )
|
||||
pixels.add(original.getPixel(x, y));
|
||||
}
|
||||
}
|
||||
|
||||
return new Color(avgRed(pixels), avgGreen(pixels), avgBlue(pixels));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param pixels The list of pixels.
|
||||
* @return The average of all the red values in the given list of pixels.
|
||||
*/
|
||||
private int avgRed(List<Color> pixels)
|
||||
{
|
||||
int total = 0;
|
||||
for(Color color : pixels) {
|
||||
total += color.getRed();
|
||||
}
|
||||
return total / pixels.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param pixels The list of pixels.
|
||||
* @return The average of all the green values in the given list of pixels.
|
||||
*/
|
||||
private int avgGreen(List<Color> pixels)
|
||||
{
|
||||
int total = 0;
|
||||
for(Color color : pixels) {
|
||||
total += color.getGreen();
|
||||
}
|
||||
return total / pixels.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param pixels The list of pixels.
|
||||
* @return The average of all the blue values in the given list of pixels.
|
||||
*/
|
||||
private int avgBlue(List<Color> pixels)
|
||||
{
|
||||
int total = 0;
|
||||
for(Color color : pixels) {
|
||||
total += color.getBlue();
|
||||
}
|
||||
return total / pixels.size();
|
||||
}
|
||||
}
|
@@ -0,0 +1,5 @@
|
||||
#BlueJ class context
|
||||
comment0.params=name
|
||||
comment0.target=SolarizeFilter(java.lang.String)
|
||||
comment0.text=\n\ Constructor\ for\ objects\ of\ class\ Solarize.\n\ @param\ name\ The\ name\ of\ the\ filter.\n
|
||||
numComments=1
|
@@ -0,0 +1,49 @@
|
||||
import java.awt.Color;
|
||||
|
||||
/**
|
||||
* An image filter to create a solarization effect.
|
||||
*
|
||||
* @author Michael Kölling and David J. Barnes.
|
||||
* @version 1.0
|
||||
*/
|
||||
public class SolarizeFilter extends Filter
|
||||
{
|
||||
/**
|
||||
* Constructor for objects of class Solarize.
|
||||
* @param name The name of the filter.
|
||||
*/
|
||||
public SolarizeFilter(String name)
|
||||
{
|
||||
super(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply this filter to an image.
|
||||
*
|
||||
* @param image The image to be changed by this filter.
|
||||
*/
|
||||
public void apply(OFImage image)
|
||||
{
|
||||
int height = image.getHeight();
|
||||
int width = image.getWidth();
|
||||
for(int y = 0; y < height; y++) {
|
||||
for(int x = 0; x < width; x++) {
|
||||
Color pix = image.getPixel(x, y);
|
||||
int red = pix.getRed();
|
||||
if(red <= 127) {
|
||||
red = 255 - red;
|
||||
}
|
||||
int green = pix.getGreen();
|
||||
if(green <= 127) {
|
||||
green = 255 - green;
|
||||
}
|
||||
int blue = pix.getBlue();
|
||||
if(blue <= 127) {
|
||||
blue = 255 - blue;
|
||||
}
|
||||
image.setPixel(x, y, new Color(red, green, blue));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,5 @@
|
||||
#BlueJ class context
|
||||
comment0.params=name
|
||||
comment0.target=ThresholdFilter(java.lang.String)
|
||||
comment0.text=\n\ Constructor\ for\ objects\ of\ class\ ThresholdFilter.\n\ @param\ name\ The\ name\ of\ the\ filter.\n
|
||||
numComments=1
|
@@ -0,0 +1,45 @@
|
||||
import java.awt.Color;
|
||||
|
||||
/**
|
||||
* An three-level gray-based threshold filter.
|
||||
*
|
||||
* @author Michael Kölling and David J. Barnes.
|
||||
* @version 1.0
|
||||
*/
|
||||
public class ThresholdFilter extends Filter
|
||||
{
|
||||
/**
|
||||
* Constructor for objects of class ThresholdFilter.
|
||||
* @param name The name of the filter.
|
||||
*/
|
||||
public ThresholdFilter(String name)
|
||||
{
|
||||
super(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply this filter to an image.
|
||||
*
|
||||
* @param image The image to be changed by this filter.
|
||||
*/
|
||||
public void apply(OFImage image)
|
||||
{
|
||||
int height = image.getHeight();
|
||||
int width = image.getWidth();
|
||||
for(int y = 0; y < height; y++) {
|
||||
for(int x = 0; x < width; x++) {
|
||||
Color pixel = image.getPixel(x, y);
|
||||
int brightness = (pixel.getRed() + pixel.getBlue() + pixel.getGreen()) / 3;
|
||||
if(brightness <= 85) {
|
||||
image.setPixel(x, y, Color.BLACK);
|
||||
}
|
||||
else if(brightness <= 170) {
|
||||
image.setPixel(x, y, Color.GRAY);
|
||||
}
|
||||
else {
|
||||
image.setPixel(x, y, Color.WHITE);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,311 @@
|
||||
#BlueJ package file
|
||||
dependency1.from=EdgeFilter
|
||||
dependency1.to=OFImage
|
||||
dependency1.type=UsesDependency
|
||||
dependency10.from=ImageViewer
|
||||
dependency10.to=LighterFilter
|
||||
dependency10.type=UsesDependency
|
||||
dependency11.from=ImageViewer
|
||||
dependency11.to=SolarizeFilter
|
||||
dependency11.type=UsesDependency
|
||||
dependency12.from=ImageViewer
|
||||
dependency12.to=MirrorFilter
|
||||
dependency12.type=UsesDependency
|
||||
dependency13.from=ImageViewer
|
||||
dependency13.to=ThresholdFilter
|
||||
dependency13.type=UsesDependency
|
||||
dependency14.from=ImageViewer
|
||||
dependency14.to=FishEyeFilter
|
||||
dependency14.type=UsesDependency
|
||||
dependency15.from=ImageViewer
|
||||
dependency15.to=Filter
|
||||
dependency15.type=UsesDependency
|
||||
dependency16.from=ImageViewer
|
||||
dependency16.to=InvertFilter
|
||||
dependency16.type=UsesDependency
|
||||
dependency17.from=SolarizeFilter
|
||||
dependency17.to=OFImage
|
||||
dependency17.type=UsesDependency
|
||||
dependency18.from=LighterFilter
|
||||
dependency18.to=OFImage
|
||||
dependency18.type=UsesDependency
|
||||
dependency19.from=DarkerFilter
|
||||
dependency19.to=OFImage
|
||||
dependency19.type=UsesDependency
|
||||
dependency2.from=SmoothFilter
|
||||
dependency2.to=OFImage
|
||||
dependency2.type=UsesDependency
|
||||
dependency20.from=PixelizeFilter
|
||||
dependency20.to=OFImage
|
||||
dependency20.type=UsesDependency
|
||||
dependency21.from=MirrorFilter
|
||||
dependency21.to=OFImage
|
||||
dependency21.type=UsesDependency
|
||||
dependency22.from=ThresholdFilter
|
||||
dependency22.to=OFImage
|
||||
dependency22.type=UsesDependency
|
||||
dependency23.from=ImagePanel
|
||||
dependency23.to=OFImage
|
||||
dependency23.type=UsesDependency
|
||||
dependency24.from=FishEyeFilter
|
||||
dependency24.to=OFImage
|
||||
dependency24.type=UsesDependency
|
||||
dependency25.from=Filter
|
||||
dependency25.to=OFImage
|
||||
dependency25.type=UsesDependency
|
||||
dependency26.from=InvertFilter
|
||||
dependency26.to=OFImage
|
||||
dependency26.type=UsesDependency
|
||||
dependency27.from=GrayScaleFilter
|
||||
dependency27.to=OFImage
|
||||
dependency27.type=UsesDependency
|
||||
dependency28.from=ImageFileManager
|
||||
dependency28.to=OFImage
|
||||
dependency28.type=UsesDependency
|
||||
dependency29.from=ImageViewer
|
||||
dependency29.to=ImageFileManager
|
||||
dependency29.type=UsesDependency
|
||||
dependency3.from=ImageViewer
|
||||
dependency3.to=SmoothFilter
|
||||
dependency3.type=UsesDependency
|
||||
dependency4.from=ImageViewer
|
||||
dependency4.to=DarkerFilter
|
||||
dependency4.type=UsesDependency
|
||||
dependency5.from=ImageViewer
|
||||
dependency5.to=PixelizeFilter
|
||||
dependency5.type=UsesDependency
|
||||
dependency6.from=ImageViewer
|
||||
dependency6.to=OFImage
|
||||
dependency6.type=UsesDependency
|
||||
dependency7.from=ImageViewer
|
||||
dependency7.to=ImagePanel
|
||||
dependency7.type=UsesDependency
|
||||
dependency8.from=ImageViewer
|
||||
dependency8.to=GrayScaleFilter
|
||||
dependency8.type=UsesDependency
|
||||
dependency9.from=ImageViewer
|
||||
dependency9.to=EdgeFilter
|
||||
dependency9.type=UsesDependency
|
||||
objectbench.height=76
|
||||
objectbench.width=1074
|
||||
package.editor.height=636
|
||||
package.editor.width=966
|
||||
package.editor.x=70
|
||||
package.editor.y=80
|
||||
package.numDependencies=29
|
||||
package.numTargets=16
|
||||
package.showExtends=true
|
||||
package.showUses=true
|
||||
project.charset=UTF-8
|
||||
readme.editor.height=642
|
||||
readme.editor.width=871
|
||||
readme.editor.x=41
|
||||
readme.editor.y=67
|
||||
target1.editor.height=731
|
||||
target1.editor.width=850
|
||||
target1.editor.x=50
|
||||
target1.editor.y=60
|
||||
target1.height=60
|
||||
target1.name=SolarizeFilter
|
||||
target1.naviview.expanded=true
|
||||
target1.showInterface=false
|
||||
target1.type=ClassTarget
|
||||
target1.typeParameters=
|
||||
target1.width=110
|
||||
target1.x=780
|
||||
target1.y=190
|
||||
target10.editor.height=734
|
||||
target10.editor.width=889
|
||||
target10.editor.x=50
|
||||
target10.editor.y=60
|
||||
target10.height=60
|
||||
target10.name=Filter
|
||||
target10.naviview.expanded=true
|
||||
target10.showInterface=false
|
||||
target10.type=AbstractTarget
|
||||
target10.typeParameters=
|
||||
target10.width=100
|
||||
target10.x=600
|
||||
target10.y=260
|
||||
target11.editor.height=730
|
||||
target11.editor.width=976
|
||||
target11.editor.x=50
|
||||
target11.editor.y=60
|
||||
target11.height=60
|
||||
target11.name=EdgeFilter
|
||||
target11.naviview.expanded=true
|
||||
target11.showInterface=false
|
||||
target11.type=ClassTarget
|
||||
target11.typeParameters=
|
||||
target11.width=90
|
||||
target11.x=450
|
||||
target11.y=120
|
||||
target12.editor.height=731
|
||||
target12.editor.width=890
|
||||
target12.editor.x=50
|
||||
target12.editor.y=60
|
||||
target12.height=60
|
||||
target12.name=FishEyeFilter
|
||||
target12.naviview.expanded=true
|
||||
target12.showInterface=false
|
||||
target12.type=ClassTarget
|
||||
target12.typeParameters=
|
||||
target12.width=100
|
||||
target12.x=550
|
||||
target12.y=120
|
||||
target13.editor.height=730
|
||||
target13.editor.width=869
|
||||
target13.editor.x=50
|
||||
target13.editor.y=60
|
||||
target13.height=60
|
||||
target13.name=MirrorFilter
|
||||
target13.naviview.expanded=true
|
||||
target13.showInterface=false
|
||||
target13.type=ClassTarget
|
||||
target13.typeParameters=
|
||||
target13.width=110
|
||||
target13.x=780
|
||||
target13.y=260
|
||||
target14.editor.height=699
|
||||
target14.editor.width=1004
|
||||
target14.editor.x=41
|
||||
target14.editor.y=23
|
||||
target14.height=60
|
||||
target14.name=ImageViewer
|
||||
target14.naviview.expanded=true
|
||||
target14.showInterface=false
|
||||
target14.type=ClassTarget
|
||||
target14.typeParameters=
|
||||
target14.width=130
|
||||
target14.x=230
|
||||
target14.y=30
|
||||
target15.editor.height=724
|
||||
target15.editor.width=877
|
||||
target15.editor.x=50
|
||||
target15.editor.y=60
|
||||
target15.height=60
|
||||
target15.name=SmoothFilter
|
||||
target15.naviview.expanded=true
|
||||
target15.showInterface=false
|
||||
target15.type=ClassTarget
|
||||
target15.typeParameters=
|
||||
target15.width=110
|
||||
target15.x=780
|
||||
target15.y=330
|
||||
target16.editor.height=728
|
||||
target16.editor.width=885
|
||||
target16.editor.x=50
|
||||
target16.editor.y=60
|
||||
target16.height=60
|
||||
target16.name=ImagePanel
|
||||
target16.naviview.expanded=true
|
||||
target16.showInterface=false
|
||||
target16.type=ClassTarget
|
||||
target16.typeParameters=
|
||||
target16.width=140
|
||||
target16.x=60
|
||||
target16.y=270
|
||||
target2.editor.height=720
|
||||
target2.editor.width=946
|
||||
target2.editor.x=50
|
||||
target2.editor.y=60
|
||||
target2.height=60
|
||||
target2.name=ThresholdFilter
|
||||
target2.naviview.expanded=true
|
||||
target2.showInterface=false
|
||||
target2.type=ClassTarget
|
||||
target2.typeParameters=
|
||||
target2.width=120
|
||||
target2.x=650
|
||||
target2.y=400
|
||||
target3.editor.height=728
|
||||
target3.editor.width=858
|
||||
target3.editor.x=50
|
||||
target3.editor.y=60
|
||||
target3.height=60
|
||||
target3.name=InvertFilter
|
||||
target3.naviview.expanded=true
|
||||
target3.showInterface=false
|
||||
target3.type=ClassTarget
|
||||
target3.typeParameters=
|
||||
target3.width=110
|
||||
target3.x=780
|
||||
target3.y=400
|
||||
target4.editor.height=732
|
||||
target4.editor.width=895
|
||||
target4.editor.x=50
|
||||
target4.editor.y=60
|
||||
target4.height=60
|
||||
target4.name=GrayScaleFilter
|
||||
target4.naviview.expanded=true
|
||||
target4.showInterface=false
|
||||
target4.type=ClassTarget
|
||||
target4.typeParameters=
|
||||
target4.width=110
|
||||
target4.x=660
|
||||
target4.y=120
|
||||
target5.editor.height=731
|
||||
target5.editor.width=858
|
||||
target5.editor.x=50
|
||||
target5.editor.y=60
|
||||
target5.height=60
|
||||
target5.name=DarkerFilter
|
||||
target5.naviview.expanded=true
|
||||
target5.showInterface=false
|
||||
target5.type=ClassTarget
|
||||
target5.typeParameters=
|
||||
target5.width=100
|
||||
target5.x=430
|
||||
target5.y=400
|
||||
target6.editor.height=729
|
||||
target6.editor.width=878
|
||||
target6.editor.x=50
|
||||
target6.editor.y=60
|
||||
target6.height=60
|
||||
target6.name=PixelizeFilter
|
||||
target6.naviview.expanded=true
|
||||
target6.showInterface=false
|
||||
target6.type=ClassTarget
|
||||
target6.typeParameters=
|
||||
target6.width=110
|
||||
target6.x=780
|
||||
target6.y=120
|
||||
target7.editor.height=737
|
||||
target7.editor.width=846
|
||||
target7.editor.x=50
|
||||
target7.editor.y=60
|
||||
target7.height=60
|
||||
target7.name=ImageFileManager
|
||||
target7.naviview.expanded=true
|
||||
target7.showInterface=false
|
||||
target7.type=ClassTarget
|
||||
target7.typeParameters=
|
||||
target7.width=140
|
||||
target7.x=60
|
||||
target7.y=190
|
||||
target8.editor.height=718
|
||||
target8.editor.width=872
|
||||
target8.editor.x=50
|
||||
target8.editor.y=60
|
||||
target8.height=60
|
||||
target8.name=LighterFilter
|
||||
target8.naviview.expanded=true
|
||||
target8.showInterface=false
|
||||
target8.type=ClassTarget
|
||||
target8.typeParameters=
|
||||
target8.width=100
|
||||
target8.x=540
|
||||
target8.y=400
|
||||
target9.editor.height=700
|
||||
target9.editor.width=863
|
||||
target9.editor.x=0
|
||||
target9.editor.y=22
|
||||
target9.height=90
|
||||
target9.name=OFImage
|
||||
target9.naviview.expanded=true
|
||||
target9.showInterface=false
|
||||
target9.type=ClassTarget
|
||||
target9.typeParameters=
|
||||
target9.width=110
|
||||
target9.x=90
|
||||
target9.y=510
|
Reference in New Issue
Block a user