diff --git a/src/main/java/labs/introtoprogramming/lab5/graphics/ArrayRaster.java b/src/main/java/labs/introtoprogramming/lab5/graphics/ArrayRaster.java new file mode 100644 index 0000000..8e5cad1 --- /dev/null +++ b/src/main/java/labs/introtoprogramming/lab5/graphics/ArrayRaster.java @@ -0,0 +1,61 @@ +package labs.introtoprogramming.lab5.graphics; + +import java.awt.Color; +import java.awt.Image; +import java.awt.image.BufferedImage; + +public class ArrayRaster implements Raster { + + private final int width; + private final int height; + + private byte[][] red; + private byte[][] green; + private byte[][] blue; + + public ArrayRaster(int width, int height) { + this.width = width; + this.height = height; + this.red = new byte[width][height]; + this.green = new byte[width][height]; + this.blue = new byte[width][height]; + } + + public Image toImage() { + BufferedImage image = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_INT_RGB); + for (int x = 0; x < width; x++) { + for (int y = 0; y < height; y++) { + image.setRGB(x, y, getPixelRGB(x, y)); + } + } + return image; + } + + public void setPixel(int x, int y, int rgb) { + this.red[x][y] = (byte) ((rgb >> 16) & 0xFF); + this.green[x][y] = (byte) ((rgb >> 8) & 0xFF); + this.blue[x][y] = (byte) (rgb & 0xFF); + } + + public void setPixel(int x, int y, byte red, byte green, byte blue) { + this.red[x][y] = red; + this.green[x][y] = green; + this.blue[x][y] = blue; + } + + public Color getPixelColor(int x, int y) { + return new Color(red[x][y] & 255, green[x][y] & 255, blue[x][y] & 255); + } + + public int getPixelRGB(int x, int y) { + return 0xFF << 24 | ((red[x][y] & 0xFF) << 16) | ((green[x][y] & 0xFF) << 8) | (blue[x][y] & 0xFF); + } + + public int getWidth() { + return width; + } + + public int getHeight() { + return height; + } +} diff --git a/src/main/java/labs/introtoprogramming/lab5/graphics/BufferedImageRaster.java b/src/main/java/labs/introtoprogramming/lab5/graphics/BufferedImageRaster.java new file mode 100644 index 0000000..874b74e --- /dev/null +++ b/src/main/java/labs/introtoprogramming/lab5/graphics/BufferedImageRaster.java @@ -0,0 +1,57 @@ +package labs.introtoprogramming.lab5.graphics; + +import java.awt.Color; +import java.awt.GraphicsEnvironment; +import java.awt.Image; +import java.awt.image.BufferedImage; + +public class BufferedImageRaster implements Raster { + + private final BufferedImage image; + + public BufferedImageRaster(int width, int height) { + // Image Data Model compatible with default screen device to avoid any conversions while rendering + if (!GraphicsEnvironment.isHeadless()) { + image = GraphicsEnvironment.getLocalGraphicsEnvironment() + .getDefaultScreenDevice() + .getDefaultConfiguration().createCompatibleImage(width, height); + } else { + image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); + } + } + + @Override + public Image toImage() { + return image; + } + + @Override + public void setPixel(int x, int y, int rgb) { + image.setRGB(x, y, rgb); + } + + @Override + public void setPixel(int x, int y, byte red, byte green, byte blue) { + image.setRGB(x, y, 0xFF << 24 | ((red & 0xFF) << 16) | ((green & 0xFF) << 8) | (blue & 0xFF)); + } + + @Override + public Color getPixelColor(int x, int y) { + return new Color(image.getRGB(x, y)); + } + + @Override + public int getPixelRGB(int x, int y) { + return image.getRGB(x, y); + } + + @Override + public int getWidth() { + return image.getWidth(); + } + + @Override + public int getHeight() { + return image.getHeight(); + } +} diff --git a/src/main/java/labs/introtoprogramming/lab5/graphics/Raster.java b/src/main/java/labs/introtoprogramming/lab5/graphics/Raster.java index 6470a2b..be115ce 100644 --- a/src/main/java/labs/introtoprogramming/lab5/graphics/Raster.java +++ b/src/main/java/labs/introtoprogramming/lab5/graphics/Raster.java @@ -1,25 +1,19 @@ package labs.introtoprogramming.lab5.graphics; -public class Raster { +import java.awt.Color; +import java.awt.Image; - private final int width; - private final int height; +public interface Raster { - private byte[][] red; - private byte[][] green; - private byte[][] blue; + Image toImage(); - public Raster(int width, int height) { - this.width = width; - this.height = height; - this.red = new byte[width][height]; - this.green = new byte[width][height]; - this.blue = new byte[width][height]; - } + void setPixel(int x, int y, int rgb); + void setPixel(int x, int y, byte red, byte green, byte blue); - public void setPixel(int x, int y, byte red, byte green, byte blue) { - this.red[x][y] = red; - this.green[x][y] = green; - this.blue[x][y] = blue; - } -} + Color getPixelColor(int x, int y); + int getPixelRGB(int x, int y); + + int getWidth(); + int getHeight(); + +} \ No newline at end of file diff --git a/src/main/java/labs/introtoprogramming/lab5/gui/RasterRendererComponent.java b/src/main/java/labs/introtoprogramming/lab5/gui/RasterRendererComponent.java new file mode 100644 index 0000000..e12f7bd --- /dev/null +++ b/src/main/java/labs/introtoprogramming/lab5/gui/RasterRendererComponent.java @@ -0,0 +1,30 @@ +package labs.introtoprogramming.lab5.gui; + +import java.awt.Canvas; +import java.awt.Graphics; +import labs.introtoprogramming.lab5.graphics.Raster; + +public class RasterRendererComponent extends Canvas { + + private Raster raster; + + private int framesRendered = 0; + + @Override + public void update(Graphics g) { + g.drawImage(raster.toImage(), 0, 0, this); + framesRendered++; + } + + void setRaster(Raster raster) { + this.raster = raster; + } + + int getFramesRendered() { + return framesRendered; + } + + void resetFramesRendered() { + this.framesRendered = 0; + } +} diff --git a/src/main/java/labs/introtoprogramming/lab5/gui/Window.java b/src/main/java/labs/introtoprogramming/lab5/gui/Window.java new file mode 100644 index 0000000..2759e57 --- /dev/null +++ b/src/main/java/labs/introtoprogramming/lab5/gui/Window.java @@ -0,0 +1,83 @@ +package labs.introtoprogramming.lab5.gui; + +import java.awt.Toolkit; +import javax.swing.JFrame; +import labs.introtoprogramming.lab5.graphics.Raster; + +public class Window { + + private static final int FPS_UPDATE_RATE = 100; + + private JFrame frame; + private RasterRendererComponent renderer; + private Toolkit toolkit; + private String title; + + private boolean displayFPS = false; + private long prevFPSTimestamp = -1; + + public Window() { + System.setProperty("sun.java2d.opengl", "true"); + + this.toolkit = Toolkit.getDefaultToolkit(); + + this.frame = new JFrame(); + this.frame.setLayout(null); + this.frame.setResizable(false); + + this.renderer = new RasterRendererComponent(); + this.frame.add(renderer); + + this.frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + } + + private void updateFPSCounter() { + if (displayFPS && renderer.getFramesRendered() >= FPS_UPDATE_RATE) { + long currentTime = System.currentTimeMillis(); + if (prevFPSTimestamp != -1) { + double fps = Math.round(1000.0 / ((double) (currentTime - prevFPSTimestamp) / renderer.getFramesRendered())); + frame.setTitle(title + " " + ((int) fps) + "FPS"); + } + prevFPSTimestamp = currentTime; + renderer.resetFramesRendered(); + } + } + + public void update() { + renderer.repaint(); + toolkit.sync(); + + updateFPSCounter(); + } + + public void show() { + frame.setVisible(true); + } + + public void setDimensions(int width, int height) { + // Centered on the screen + frame.setBounds(getScreenWidth() / 2 - width / 2, getScreenHeight() / 2 - height / 2, width, height); + renderer.setBounds(0, 0, width, height); + } + + private int getScreenWidth() { + return toolkit.getScreenSize().width; + } + + private int getScreenHeight() { + return toolkit.getScreenSize().height; + } + + public void setTitle(String text) { + this.title = text; + frame.setTitle(text); + } + + public void setRaster(Raster raster) { + this.renderer.setRaster(raster); + } + + public void setDisplayFPS(boolean displayFPS) { + this.displayFPS = displayFPS; + } +} diff --git a/src/test/java/labs/introtoprogramming/lab5/graphics/ArrayRasterTests.java b/src/test/java/labs/introtoprogramming/lab5/graphics/ArrayRasterTests.java new file mode 100644 index 0000000..abdc292 --- /dev/null +++ b/src/test/java/labs/introtoprogramming/lab5/graphics/ArrayRasterTests.java @@ -0,0 +1,38 @@ +package labs.introtoprogramming.lab5.graphics; + +import static org.junit.Assert.assertEquals; + +import java.awt.Color; +import java.awt.image.BufferedImage; +import org.junit.Test; + +public class ArrayRasterTests { + + @Test + public void testSetPixelRGB() { + Raster raster = new ArrayRaster(100, 100); + raster.setPixel(0, 0, 42); + assertEquals(42, raster.getPixelRGB(0, 0) & 0xFFFFFF); + } + + @Test + public void testSetPixel() { + Raster raster = new ArrayRaster(100, 100); + raster.setPixel(1, 2, (byte) 1, (byte) 2, (byte) 3); + assertEquals(new Color(1, 2, 3).getRGB(), raster.getPixelRGB(1, 2)); + } + + @Test + public void testGetPixelColor() { + Raster raster = new ArrayRaster(100, 100); + raster.setPixel(1, 2, (byte) 255, (byte) 0, (byte) 0); + assertEquals(Color.RED, raster.getPixelColor(1, 2)); + } + + @Test + public void testToImage() { + Raster raster = new ArrayRaster(2, 2); + raster.setPixel(1, 1, Color.RED.getRGB()); + assertEquals(Color.RED.getRGB(), ((BufferedImage) raster.toImage()).getRGB(1, 1)); + } +} diff --git a/src/test/java/labs/introtoprogramming/lab5/graphics/BufferedImageRasterTests.java b/src/test/java/labs/introtoprogramming/lab5/graphics/BufferedImageRasterTests.java new file mode 100644 index 0000000..3d24293 --- /dev/null +++ b/src/test/java/labs/introtoprogramming/lab5/graphics/BufferedImageRasterTests.java @@ -0,0 +1,45 @@ +package labs.introtoprogramming.lab5.graphics; + +import static org.junit.Assert.assertEquals; + +import java.awt.Color; +import java.awt.image.BufferedImage; +import org.junit.Test; + +public class BufferedImageRasterTests { + + @Test + public void testGetDimensions() { + Raster raster = new BufferedImageRaster(101, 102); + assertEquals(101, raster.getWidth()); + assertEquals(102, raster.getHeight()); + } + + @Test + public void testSetPixelRGB() { + Raster raster = new BufferedImageRaster(100, 100); + raster.setPixel(0, 0, 42); + assertEquals(42, raster.getPixelRGB(0, 0) & 0xFFFFFF); + } + + @Test + public void testSetPixel() { + Raster raster = new BufferedImageRaster(100, 100); + raster.setPixel(1, 2, (byte) 1, (byte) 2, (byte) 3); + assertEquals(new Color(1, 2, 3).getRGB(), raster.getPixelRGB(1, 2)); + } + + @Test + public void testGetPixelColor() { + Raster raster = new BufferedImageRaster(100, 100); + raster.setPixel(1, 2, (byte) 255, (byte) 0, (byte) 0); + assertEquals(Color.RED, raster.getPixelColor(1, 2)); + } + + @Test + public void testToImage() { + Raster raster = new BufferedImageRaster(2, 2); + raster.setPixel(1, 1, Color.RED.getRGB()); + assertEquals(Color.RED.getRGB(), ((BufferedImage) raster.toImage()).getRGB(1, 1)); + } +}