I want to develop a Java-Plugin for ImageJ that flips an image horizontally.
But my code flips only half of the picture. Maybe, there is something wrong with the construction or the output of the image copy?
public class flipHorizontal implements PlugInFilter {
public int setup (String arg, ImagePlus imp)
{
return DOES_ALL;
}
public void run (ImageProcessor ip)
{
int height=ip.getHeight();
int width=ip.getWidth();
ImageProcessor copy = ip;
for (int x=0; x<width; x++) {
for (int y=0; y<height; y++) {
int p=ip.getPixel(width-x-1,y);
copy.putPixel(x,y,p);
}
}
}
}
Your logic is wrong. What you get is normal: you're not processing half of your image but flipping horizontally once half of your image and twice the other half (if I'm not mistaken).
Anyway, if you want to flip horizontally by manipulating pixels yourself directly, as in your code sample, then instead of going to width, you need to go to half the width (width/2).
You then need to actually invert the two pixels from "left" and "right"
Here's an horizontal flip that works:
for (int x = 0; x < w / 2; x++) {
for (int y = 0; y < h; y++) {
final int l = tmp.getRGB( w - (x + 1), y);
final int r = tmp.getRGB( x, y);
tmp.setRGB( x, y, l );
tmp.setRGB( w - (x + 1), y, r );
}
}
There may be "off-by-one" errors in the code above but you should get the idea.
TacticalCoder is correct that you should only be iterating to half way across the image, and you need to save the value from the other side before overwriting it.
There are two additional points that might be worth making, however - one is the the ImageProcessor class already has a method called flipHorizontal, so you can simplify your code to:
public class flipHorizontal implements PlugInFilter {
public int setup (String arg, ImagePlus imp) {
return DOES_ALL;
}
public void run (ImageProcessor ip) {
ip.flipHorizontal();
}
}
The other point that would be worth making is that it seems that you misunderstand what this line means:
ImageProcessor copy = ip;
That's just creating another reference to the same object as ip, so:
copy.putPixel(x,y,p);
... and:
ip.putPixel(x,y,p);
... have exactly the same effect. If you want to create a new ImageProcessor representing the same pixel data, you could do:
ImageProcessor copy = ip.duplicate();
However, that's not necessary in this case.
Related
I'm making a game similar to agar.io where a blob goes around and eats dots. I'm making it on my phone and you control the blob with your finger to collect the dots. I noticed that when I collect a single dot some random other dots dissappearing as well. I debugged some and found out that unless you collected the dots in the order that they were added to the array, any dot with a lower array order would be destroyed. Example: if you collected the dot added to the array 7th then dots 0-6 would dissappear, bit if you collected 1 then 2 and so on then no other dots would dissappear randomly. I created another simpler example to explore this problem. Now it's a simple screen with 5 circles. You can pick up and drag and drop any circle. I noticed the same problem where you drag a circle and other circles randomly dissappear even though there is no code to make them dissappear. My code is as follows:
// Drag n' Drop //
Objects[] box;
int objCount = 5;
void setup() {
box = new Objects[objCount];
for (int i = 0; i < objCount; i++){
box[i] = new Objects(random(displayWidth),random(displayHeight),200);
}
}
void draw() {
background(170);
for (Objects boxes : box) {
boxes.display();
boxes.dragging();
}
}
class Objects {
float x, y;
float s;
Objects(float tempX, float tempY, int tempS) {
x = tempX;
y = tempY;
s = tempS;
}
void display() {
ellipse(x, y, s, s);
}
void dragging() {
if (dist(x, y, mouseX, mouseY) < 500) {
x = mouseX;
y = mouseY;
s = 300;
}
}
}
I believe my problem may lie in the loop I use to call the display function of the box object, but I cannot find out any other way to make it work. Any help us very much appreciated. Thank you for your time. PS Im using processing to run this code.
Kelton
Firstly, I'd like to thank you for I have never played with Processing before and you inspired me to download it!
There are quite a few things wrong that I'd like to point out and maybe steer you in the right direction. The main issue lies within your dragging() method you are not actually removing the objects you are just moving them to your mouse position, giving you the illusion they are being removed!
Anyway, as you said you were creating the game Agar.io, I would assume that you yourself should have your own Blob. For the sake of my Java brain I have switched what you called Objects to Blobs.
First off, the setup.
import java.util.*;
public static final int BLOB_COUNT = 10;
List<Blob> blobs = new ArrayList<Blob>();
// this is our blob, the one that displays in the middle of the screen
Blob myBlob = new Blob(mouseX, mouseY, 50);
void setup() {
size(1000, 500);
for (int i = 0; i < BLOB_COUNT; i++){
blobs.add(new Blob(random(displayWidth/2),random(displayHeight/2),50));
}
}
Notice how I am using ArrayLists rather than an array, this will make it easier for you to add and remove from the List.
Next, the draw() so this happens each frame.
void draw() {
background(170);
// refreshes the players blob wherever the cursor is!
myBlob.setX(mouseX);
myBlob.setY(mouseY);
myBlob.display();
// display the other blobs on the screen
for (Blob boxes : blobs) {
boxes.display();
boxes.dragging();
}
}
Notice, we want to update our blob to the current position of the mouse!
Lastly, the Blob class!
class Blob {
float x, y;
float size;
Blob(float tempX, float tempY, int size) {
this.x = tempX;
this.y = tempY;
this.size = size;
}
void display() {
ellipse(x, y, size, size);
}
void dragging() {
if (dist(x, y, mouseX, mouseY) < myBlob.getSize()/2) {
myBlob.setBlobSize(25);
this.x = random(displayWidth/2);
this.y = random(displayHeight/2);
}
}
void setX(float x){
this.x = x;
}
void setY(float y) {
this.y = y;
}
void setBlobSize(float size) {
this.size += size;
}
float getSize() {
return this.size;
}
}
So now, we check in the dragging() method whether the blob is close to our blob, and if it is we want to consume that blob (which increases our mass) and then we want that blob to re-spawn to another location, well that's how most Agar.io games work, but of course this is entirely up to you. There is also much more accurate ways to calculate the area of the blob and determine whether two blobs are within touching distance, but I'll leave the maths to you.
Hi I am in need of some help. I need to write a convolution method from scratch that takes in the following inputs: int[][] and BufferedImage inputImage. I can assume that the kernel has size 3x3.
My approach is to do the follow:
convolve inner pixels
convolve corner pixels
convolve outer pixels
In the program that I will post below I believe I convolve the inner pixels but I am a bit lost at how to convolve the corner and outer pixels. I am aware that corner pixels are at (0,0), (width-1,0), (0, height-1) and (width-1,height-1). I think I know to how approach the problem but not sure how to execute that in writing though. Please to aware that I am very new to programming :/ Any assistance will be very helpful to me.
import java.awt.*;
import java.awt.image.BufferedImage;
import com.programwithjava.basic.DrawingKit;
import java.util.Scanner;
public class Problem28 {
// maximum value of a sample
private static final int MAX_VALUE = 255;
//minimum value of a sample
private static final int MIN_VALUE = 0;
public BufferedImage convolve(int[][] kernel, BufferedImage inputImage) {
}
public BufferedImage convolveInner(double center, BufferedImage inputImage) {
int width = inputImage.getWidth();
int height = inputImage.getHeight();
BufferedImage inputImage1 = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
//inner pixels
for (int x = 1; x < width - 1; x++) {
for (int y = 1; y < height - 1; y ++) {
//get pixels at x, y
int colorValue = inputImage.getRGB(x, y);
Color pixelColor = new Color(colorValue);
int red = pixelColor.getRed() ;
int green = pixelColor.getGreen() ;
int blue = pixelColor.getBlue();
int innerred = (int) center*red;
int innergreen = (int) center*green;
int innerblue = (int) center*blue;
Color newPixelColor = new Color(innerred, innergreen, innerblue);
int newRgbvalue = newPixelColor.getRGB();
inputImage1.setRGB(x, y, newRgbvalue);
}
}
return inputImage1;
}
public BufferedImage convolveEdge(double edge, BufferedImage inputImage) {
int width = inputImage.getWidth();
int height = inputImage.getHeight();
BufferedImage inputImage2 = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
//inner pixels
for (int x = 0; x < width - 1; x++) {
for (int y = 0; y < height - 1; y ++) {
//get pixels at x, y
int colorValue = inputImage.getRGB(x, y);
Color pixelColor = new Color(colorValue);
int red = pixelColor.getRed() ;
int green = pixelColor.getGreen() ;
int blue = pixelColor.getBlue();
int innerred = (int) edge*red;
int innergreen = (int) edge*green;
int innerblue = (int) edge*blue;
Color newPixelColor = new Color(innerred, innergreen, innerblue);
int newRgbvalue = newPixelColor.getRGB();
inputImage2.setRGB(x, y, newRgbvalue);
}
}
return inputImage2;
}
public BufferedImage convolveCorner(double corner, BufferedImage inputImage) {
int width = inputImage.getWidth();
int height = inputImage.getHeight();
BufferedImage inputImage3 = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
//inner pixels
for (int x = 0; x < width - 1; x++) {
for (int y = 0; y < height - 1; y ++) {
//get pixels at x, y
int colorValue = inputImage.getRGB(x, y);
Color pixelColor = new Color(colorValue);
int red = pixelColor.getRed() ;
int green = pixelColor.getGreen() ;
int blue = pixelColor.getBlue();
int innerred = (int) corner*red;
int innergreen = (int) corner*green;
int innerblue = (int) corner*blue;
Color newPixelColor = new Color(innerred, innergreen, innerblue);
int newRgbvalue = newPixelColor.getRGB();
inputImage3.setRGB(x, y, newRgbvalue);
}
}
return inputImage3;
}
public static void main(String[] args) {
DrawingKit dk = new DrawingKit("Compositor", 1000, 1000);
BufferedImage p1 = dk.loadPicture("image/pattern1.jpg");
Problem28 c = new Problem28();
BufferedImage p5 = c.convolve();
dk.drawPicture(p5, 0, 100);
}
}
I changed the code a bit but the output comes out as black. What did I do wrong:
import java.awt.*;
import java.awt.image.BufferedImage;
import com.programwithjava.basic.DrawingKit;
import java.util.Scanner;
public class Problem28 {
// maximum value of a sample
private static final int MAX_VALUE = 255;
//minimum value of a sample
private static final int MIN_VALUE = 0;
public BufferedImage convolve(int[][] kernel, BufferedImage inputImage) {
int width = inputImage.getWidth();
int height = inputImage.getHeight();
BufferedImage inputImage1 = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
//for every pixel
for (int x = 0; x < width; x ++) {
for (int y = 0; y < height; y ++) {
int colorValue = inputImage.getRGB(x,y);
Color pixelColor = new Color(colorValue);
int red = pixelColor.getRed();
int green = pixelColor.getGreen();
int blue = pixelColor.getBlue();
double gray = 0;
//multiply every value of kernel with corresponding image pixel
for (int i = 0; i < 3; i ++) {
for (int j = 0; j < 3; j ++) {
int imageX = (x - 3/2 + i + width) % width;
int imageY = (x -3/2 + j + height) % height;
int RGB = inputImage.getRGB(imageX, imageY);
int GRAY = (RGB) & 0xff;
gray += (GRAY*kernel[i][j]);
}
}
int out;
out = (int) Math.min(Math.max(gray * 1, 0), 255);
inputImage1.setRGB(x, y, new Color(out,out,out).getRGB());
}
}
return inputImage1;
}
public static void main(String[] args) {
int[][] newArray = {{1/9, 1/9, 1/9}, {1/9, 1/9, 1/9}, {1/9, 1/9, 1/9}};
DrawingKit dk = new DrawingKit("Problem28", 1000, 1000);
BufferedImage p1 = dk.loadPicture("image/pattern1.jpg");
Problem28 c = new Problem28();
BufferedImage p2 = c.convolve(newArray, p1);
dk.drawPicture(p2, 0, 100);
}
}
Welcome ewuzz! I wrote a convolution using CUDA about a week ago, and the majority of my experience is with Java, so I feel qualified to provide advice for this problem.
Rather than writing all of the code for you, the best way to solve this large program is to discuss individual elements. You mentioned you are very new to programming. As the programs you write become more complex, it's essential to write small working snippets before combining them into a large successful program (or iteratively add snippets). With this being said, it's already apparent you're trying to debug a ~100 line program, and this approach will cost you time in most cases.
The first point to discuss is the general approach you mentioned. If you think about the program, what is the simplest and most repeated step? Obviously this is the kernel/mask step, so we can start from here. When you convolute each pixel, you are performing a similar option, regardless of the position (corner, edge, inside). While there are special steps necessary for these edge cases, they share similar underlying steps. If you try to write code for each of these cases separately, you will have to update the code in multiple (three) places with each adjustment and it will make the whole program more difficult to grasp.
To support my point above, here's what happened when I pasted your code into IntelliJ. This illustrates the (yellow) red flag of using the same code in multiple places:
The concrete way to fix this problem is to combine the three convolve methods into a single one and use if statements for edge-cases as necessary.
Our pseudocode with this change:
convolve(kernel, inputImage)
for each pixel in the image
convolve the single pixel and check edge cases
endfor
end
That seems pretty basic right? If we are able to successfully check edge cases, then this extremely simple logic will work. The reason I left it so general above to show how convolve the single pixel and check edge cases is logically grouped. This means it's a good candidate for extracting a method, which could look like:
private void convolvePixel(int x, int y, int[][] kernel, BufferedImage input, BufferedImage output)
Now to implement our method above, we will need to break it into a few steps, which we may then break into more steps if necessary. We'll need to look at the input image, if possible for each pixel accumulate the values using the kernel, and then set this in the output image. For brevity I will only write pseudocode from here.
convolvePixel(x, y, kernel, input, output)
accumulation = 0
for each row of kernel applicable pixels
for each column of kernel applicable pixels
if this neighboring pixel location is within the image boundaries then
input color = get the color at this neighboring pixel
adjusted value = input color * relative kernel mask value
accumulation += adjusted value
else
//handle this somehow, mentioned below
endif
endfor
endfor
set output pixel as accumulation, assuming this convolution method does not require normalization
end
The pseudocode above is already relatively long. When implementing you could write methods for the if and the else cases, but it you should be fine with this structure.
There are a few ways to handle the edge case of the else above. Your assignment probably specifies a requirement, but the fancy way is to tile around, and pretend like there's another instance of the same image next to this input image. Wikipedia explains three possibilities:
Extend - The nearest border pixels are conceptually extended as far as necessary to provide values for the convolution. Corner pixels are extended in 90° wedges. Other edge pixels are extended in lines.
Wrap - (The method I mentioned) The image is conceptually wrapped (or tiled) and values are taken from the opposite edge or corner.
Crop - Any pixel in the output image which would require values from beyond the edge is skipped. This method can result in the output image being slightly smaller, with the edges having been cropped.
A huge part of becoming a successful programmer is researching on your own. If you read about these methods, work through them on paper, run your convolvePixel method on single pixels, and compare the output to your results by hand, you will find success.
Summary:
Start by cleaning-up your code before anything.
Group the same code into one place.
Hammer out a small chunk (convolving a single pixel). Print out the result and the input values and verify they are correct.
Draw out edge/corner cases.
Read about ways to solve edge cases and decide what fits your needs.
Try implementing the else case through the same form of testing.
Call your convolveImage method with the loop, using the convolvePixel method you know works. Done!
You can look up pseudocode and even specific code to solve the exact problem, so I focused on providing general insight and strategies I have developed through my degree and personal experience. Good luck and please let me know if you want to discuss anything else in the comments below.
Java code for multiple blurs via convolution.
So, as the title reads I am trying to add offsets to my java game. I was given a tip by a friend that I need to minus the offset from where I render the tiles onto my screen.
So I created a random world generator and did the offset thing, but I ran into a problem.
My Code:
public void generateMap(Graphics g) {
block = seed.nextInt(2);
//Render Dirt
if(block == 0) {
g.drawImage(Assets.dirt, x - GameState.xOffset, y - GameState.yOffset, null);
x += 32;
}
//Render Grass
if(block == 1) {
g.drawImage(Assets.grass, x - GameState.xOffset, y - GameState.yOffset, null);
x += 32;
}
//Check Where the X is
if(x > xFinish) {
if(y < yFinish) {
x = xStart;
y += 32;
}
}
}
looks simple enough right? after I do that I create code to add one to the offset every time I loop around:
public void tick() {
xOffset += 1;
}
So after that is done I run it but it does this:
is there any simple way I can fix this so that it appears that the screen "scrolls" to the left?
Is there any simple way I can fix this...
Probably not. Games are complicated. Don't let that dissuade you.
You are generating your game world and drawing in the same methods - you don't want to do this. Separation of responsibility is very important - you don't want a whole bunch of code in one spot doing the same thing. In this case, the functionality to generate the world and the drawing code need to be split.
For the world generation, generate the game world once, and persist it to storage using whatever format you like. Keep this away from the drawing code - it has no place there.
For representing blocks in your world, consider something like this:
class Block {
public BlockType getType() {
return type;
}
public int getxPosition() {
return xPosition;
}
public int getyPosition() {
return yPosition;
}
// hashCode(), equals(), etc omitted, they should be implemented
public static enum BlockType {
Dirt(Assets.dirt),
Grass(Assets.grass);
private final BufferedImage image;
BlockType(BufferedImage image) {
this.image = image;
}
public BufferedImage getImage() {
return image;
}
}
private final BlockType type;
private final int xPosition;
private final int yPosition;
private Block(BlockType type, int xPosition, int yPosition) {
this.type = type;
this.xPosition = xPosition;
this.yPosition = yPosition;
}
public static Block getInstance(BlockType type, int xPosition, int yPosition) {
return new Block(type, xPosition, yPosition);
}
}
You can then use Block.getInstance() to generate a map once, like this:
class GameState {
private final int WORLD_SIZE = 1024;
private Block[][] _world = new Block[WORLD_SIZE][WORLD_SIZE];
private static Random seed = new Random();
public void generateMap() {
int blockTypeLength = Block.BlockType.values().length;
for (int x = 0; x < WORLD_SIZE; x++) {
for (int y = 0; y < WORLD_SIZE; y++) {
int blockType = seed.nextInt(blockTypeLength);
_world[x][y] = Block.getInstance(Block.BlockType.values()[blockType], x, y);
}
}
}
public Block[][] getMap() {
return _world; // not thread safe, shares internal state, all the usual warnings
}
This obviously isn't the only way to generate a world - you would probably generate a world and save, then load from disk in later games (unless it was a short lived game - I don't know, that's your call).
Once you've got the world sorted out, you'd move on to a different module that would handle drawing. Assume GameState has two fields playerX and playerY that represent the player's coordinates in the game world (note: direct fields like this are bad practice, but used to simplify this example):
public void paintComponent(Graphics g) {
super.paintComponent(g);
Block[][] screen = new Block[16][16]; // declare a screen buffer to draw
// Assumes player is in the center of the screen
int screenRadiusX = GameFrame.Assets.SCREENBOUNDS_X / 2 / blockSize;
int screenRadiusY = GameFrame.Assets.SCREENBOUNDS_Y / 2 / blockSize;
for (int x = state.playerX - 8, xS = 0; x < state.playerX + 8; x++, xS++) {
for (int y = state.playerY - 8, yS = 0; y < state.playerY + 8; y++, yS++) {
screen[xS][yS] = world[x][y];
}
}
for (int x = 0; x < screen.length; x++) {
for (int y = 0; y < screen.length; y++) {
Block current = screen[x][y];
g.drawImage(current.getType().getImage(),
x * blockSize, // blockSize is the pixel dimension of
y * blockSize,
null
);
}
}
}
If this helps, then great! I'm glad I was able to help. If not, or if some ideas are still unclear, then I would consider perhaps running through a tutorial or book that walks you through making a game. Don't forget to learn the platform you're coding on during such a process.
I'm making a game in java, is a rpg, however, only with the map the game is slow.
The map is made in TiledMap Editor, therefore, an XML that is read and loaded into an ArrayList. My PC is a dual-core 3.0, 4GB RAM, 1GB Video.
The do the rendering is done as follows:
//method of tileset class
public void loadTileset(){
positions = new int[1 + tilesX * tilesY][2];
int yy = 0;
int xx = 0;
int index = 0;
// save the initial x and y point of each tile in an array named positions
// positions[tileNumber] [0] - X position
// positions[tileNumber] [1] - Y position
for(int i = 1 ; i < positions.length; i++){
if(index == tilesX ){
yy += tileHeight;
xx = 0;
index = 0;
}
positions[i][0] = xx;
positions[i][1] = yy;
xx += tileWidth;
index++;
}
}
//method of map class
public void draw(Graphics2D screen){
//x and y position of each tile on the screen
int x = 0; int y = 0;
for(int j = 0; j < 20 ; j++){
for(int i = initialTile ; i < initialTile + quantTiles ; i++){
int tile = map[j][i];
if(tile != 0){
screen.drawImage(tileSet.getTileImage().getSubimage(tileSet.getTileX(tile), tileSet.getTileY(tile),tileSet.getTileWidth(), tileSet.getTileHeight()),x,y,null);
}
x += tileSet.getTileWidth();
}
x = 0;
y += tileSet.getTileHeight();
}
}
Am I doing something wrong?
Note: I'm new to the forum and to make matters worse I do not understand very much English, so excuse any mistake.
First of all, you should not create the subimages for the tiles during each call. Strictly speaking, you should not call getSubimage at all for images that you want to paint: It will make the image "unmanaged", and this can degrade rendering performance by an order of magnitude. You should only call getSubimage for images that you do not want to render - for example, when you are initially creating individual images for the tiles.
You obviously already have a TileSet class. You could add a bit of functionality to this class so that you can directly access images for the tiles.
Your current code looks like this:
screen.drawImage(
tileSet.getTileImage().getSubimage(
tileSet.getTileX(tile),
tileSet.getTileY(tile),
tileSet.getTileWidth(),
tileSet.getTileHeight()),
x,y,null);
You could change it to look like this:
screen.drawImage(tileSet.getTileImage(tile), x,y,null);
The getTileImage(int tile) method suggested here could then obtain tiles that have been stored internally.
I'll sketch a few lines of code from the tip of my head, you'll probably be able to transfer this into your TileSet class:
class TileSet
{
private Map<Integer, BufferedImage> tileImages;
TileSet()
{
....
prepareTileImages();
}
private void prepareTileImages()
{
tileImages = new HashMap<Integer, BufferedImage>();
for (int tile : allPossibleTileValuesThatMayBeInTheMap)
{
// These are the tiles that you originally rendered
// in your "draw"-Method
BufferedImage image =
getTileImage().getSubimage(
getTileX(tile),
getTileY(tile),
getTileWidth(),
getTileHeight());
// Create a new, managed copy of the image,
// and store it in the map
BufferedImage managedImage = convertToARGB(image);
tileImages.put(tile, managedImage);
}
}
private static BufferedImage convertToARGB(BufferedImage image)
{
BufferedImage newImage = new BufferedImage(
image.getWidth(), image.getHeight(),
BufferedImage.TYPE_INT_ARGB);
Graphics2D g = newImage.createGraphics();
g.drawImage(image, 0, 0, null);
g.dispose();
return newImage;
}
// This is the new method: For a given "tile" value
// that you found at map[x][y], this returns the
// appropriate tile:
public BufferedImage getTileImage(int tile)
{
return tileImages.get(tile);
}
}
I'm following a textbook and have become stuck at a particular point.
This is a console application.
I have the following class with a rotate image method:
public class Rotate {
public ColorImage rotateImage(ColorImage theImage) {
int height = theImage.getHeight();
int width = theImage.getWidth();
//having to create new obj instance to aid with rotation
ColorImage rotImage = new ColorImage(height, width);
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
Color pix = theImage.getPixel(x, y);
rotImage.setPixel(height - y - 1, x, pix);
}
}
//I want this to return theImage ideally so I can keep its state
return rotImage;
}
}
The rotation works, but I have to create a new ColorImage (class below) and this means I am creating a new object instance (rotImage) and losing the state of the object I pass in (theImage). Presently, it's not a big deal as ColorImage does not house much, but if I wanted it to house the state of, say, number of rotations it has had applied or a List of something I'm losing all that.
The class below is from the textbook.
public class ColorImage extends BufferedImage {
public ColorImage(BufferedImage image) {
super(image.getWidth(), image.getHeight(), TYPE_INT_RGB);
int width = image.getWidth();
int height = image.getHeight();
for (int y = 0; y < height; y++)
for (int x = 0; x < width; x++)
setRGB(x, y, image.getRGB(x, y));
}
public ColorImage(int width, int height) {
super(width, height, TYPE_INT_RGB);
}
public void setPixel(int x, int y, Color col) {
int pixel = col.getRGB();
setRGB(x, y, pixel);
}
public Color getPixel(int x, int y) {
int pixel = getRGB(x, y);
return new Color(pixel);
}
}
My question is, how can I rotate the image I pass in so I can preserve its state?
Unless you limit yourself to square images or to 180° rotations, you need a new object, as the dimensions would have changed. The dimensions of a BufferedImage object, once created, are constant.
If I wanted it to house the state of, say, number of rotations it has had applied or a List of something I'm losing all that
You can create another class to hold that other information along with the ColorImage/BufferedImage, then limit the ColorImage/BufferedImage class itself to holding only the pixels. An example:
class ImageWithInfo {
Map<String, Object> properties; // meta information
File file; // on-disk file that we loaded this image from
ColorImage image; // pixels
}
Then you can replace the pixels object freely, while preserving the other state. It's often helpful to favor composition over inheritance. In brief that means, instead of extending a class, create a separate class that contains the original class as a field.
Also note that the rotation implementation from your book seems to be mainly for learning purposes. It's fine for that, but will show its performance limitations if you manipulate very big images or for continuous graphics rotation at animation speeds.