How to scale a pixel-drawing function in Java - java

In Java, I have a function like this:
public void setPixel(int x, int y, boolean on);
It sets a virtual black and white pixel, given whether it is on or not.
How can I call this function so the resulting display will be four times larger?
I tried this:
int x = 3;
int y = 3;
setPixel(x, y, true);
setPixel(x+1, y+1, true);
setPixel(x+2, y+2, true);
setPixel(x+3, y+3, true);
But naturally, it overlapped when I tried to draw something. How should I call the method?
While I'm tagging this Java, the concept could apply to any language.

Answering on these assumptions: setPixel sets a single pixel to white or black (if on is true, to black, else to white). You want to use this function to get a B&W image and make it four times larger. The code you provided is wrong and just makes a diagonal instad of a 4x4 block. Is this correct? If so:
A way to draw a 4 times larger image would then be, for example, to have a "getPixel(x,y)" which gets you whether the pixel at (x,y) is on in the original image and then start painting somewhere else in 4x4 blocks. Whenever you move by one pixel in either X or Y direction when getting the values of your original image, you move by 4 in your new image to scale. So then what you intended to do maybe was something like this?
void setBlock(int x, int y, boolean on, int scale)
for(int i=0; i < scale; i++){
for(int j=0; j < scale; j++){
setPixel(scale*x + i, scale*y + j, on);
And then iterate over your original image's coordinates doing something like this?
setBlock(x, y, getPixel(x, y), 4);

Related

Java - Get all pixels inside of a circle on a Buffered Image

I wasn't able to find the solution to this problem online, sorry if this question is already asked before, also I think my question's title wasn't specific enough, so, I will explain in more details.
So, what I did is created a BufferedImage with a specific size and type, let's say for example:
BufferedImage img1 = new BufferedImage(500, 500, BufferedImage.TYPE_INT_ARGB);
And what I am trying to do, is to use a "for" loop and loop thru every single image's pixel inside of an imaginary circle, lets say that circle starts on (X, Y)(0, 0), and ends on (X, Y)(500, 500).
Now, what I am trying to do, is to loop thru every single pixel inside of that imaginary circle, and then later, do something with that pixel (changing it's color for example).
Can anyone please help me do it? Thanks!
I came up with an idea that could solve this problem not only for circles, but also for any other shapes.
So, my idea is to create another Buffered Image, make it the same size as the one i want to edit (work on), make sure that all of it's pixels are blank (transparent), and draw a shape that you want on that new image (a circle for example) also, we keep the track of the shape's color, and the shape needs to be filled.
After that, we loop thru each pixel on the image that we want to edit using the for loops and the x, y integer variables, each time the loop repeats, we check on the shape's image to see if the shape is drawn there, and to do that we check if the pixel color is same as the shape's color on the shape's image. And if it is, the loop has detected the coordinates to the pixel inside of the shape, and then we can do something with it. (The for loops use the X and Y coordinate integers)
Here is an example code:
public static BufferedImage yourMethodName(BufferedImage inputImage, Point circlePosition, Dimension circleSize)
{
BufferedImage outputImage = inputImage;
BufferedImage circleImage = new BufferedImage(inputImage.getWidth(), inputImage.getHeight(), BufferedImage.TYPE_INT_ARGB);
Graphics drawOnCircleImg = circleImage.getGraphics();
Color circleColor = new Color(0, 255, 0);
drawOnCircleImg.setColor(circleColor);
drawOnCircleImg.fillOval(circlePosition.x, circlePosition.y, circleSize.width, circleSize.height);
for(int y = 0; y < outputImage.getHeight(); y++)
{
for(int x = 0; x < outputImage.getWidth(); x++)
{
if(circleImage.getRGB(x, y) == circleColor.getRGB())
{
//PIXEL IS INSIDE OF THE SHAPE, IT IS DETECTED, DO SOMETHING NOW
//VARIABLES FOR THE PIXEL POSITION ARE: X, Y
}
}
}
return outputImage;
}

Render lots of individual pixels efficient?

I have made a map on an image which I load in the game like this:
List<Block> tempBlocks = new ArrayList<Block>();
BufferedImage level = levels.get(currentLevel);
for (int x = 0; x < level.getWidth(); x++) {
for (int y = 0; y < level.getHeight(); y++) {
tempBlocks.add(new Block(x, y, new Color(level.getRGB(x, y))));
}
}
blocks = tempBlocks;
isLoading = false;
The array blocks is an array with all 1-pixel-blocks. Every single block renders like this:
g.setColor(color);
g.drawLine(x, y, x, y);
I call them like this:
for (int i = 0; i < getCurrentScene().blocks.size(); i++)
getCurrentScene().blocks.get(i).render(g);
It's 1280x720 blocks... Is it a better way to render this insane amount of blocks? Because I want it to load from an image, which is 1280x720. I get like 3 FPS now...
For what you're doing now, if you do that for every frame, you're creating new objects all over the place for every single frame, and only using them once before discarding and allowing the garbage collector to pick them up.
Create enough objects to represent how the screen is now.
Create enough objects to represent how the screen should be next.
Don't lose those. Don't create any more.
For the Color(int int int) thing you're doing? You can just do Color(int); new color(level.getRGB(x, y)) should work just fine; the java.awt.Color class can take a single int instead of three.
That said, you'd still be initializing Color a million times. If you have a limited number of Colors, it may be beneficial to call them directly or cache them in some way, instead of continually recreating new Color objects on the heap.
Shepard solved it in the comments. What I did was to render the image as g.drawImage(image, x, y); instead of rendering all of the pixels. I still use the blocks for collision detection, but that is way more efficient.

Slick is getting very slow but only drawin rectangles

I am using slick for java since a few days and got a serious problem.
If i run a completely empty apllication (it just shows the fps) with a solution of 800x600 i get a fps count between 700 and 800.
If I now draw an array with 13300 entries as a grid of green and white rectangles, the fps drop to something around 70.
With more entries in the array it becomes really slow.
For example in a solution of 1024x768 and an array with 21760 entries the fps drop to 40.
How i draw a single entry:
public void draw(Graphics graphics){
graphics.setColor(new Color(getColor().getRed(), getColor().getGreen(), getColor().getBlue(), getColor().getAlpha()));
graphics.fillRect(getPosition().x, getPosition().y, getSize().x, getSize().y);
Color_ARGB white = new Color_ARGB(Color_ARGB.ColorNames.WHITE);
graphics.setColor(new Color(white.getRed(), white.getGreen(), white.getBlue(), white.getAlpha()));
}
And this is how I draw the complete array:
public void draw(Graphics graphics) {
for (int ix = 0; ix < getWidth(); ix++) {
for (int iy = 0; iy < getHeight(); iy++) {
getGameGridAt(ix, iy).draw(graphics);
}
}
}
In my opinion 21760 is not that much.
Is there anything wrong with my code or is slick just too slow to draw so much rectangles?
You only want to draw rectangles that are on the screen. If your screen bounds go from 0 to 1024 in the x direction and from 0 to 768 in the y direction, then you only want to loop through rectangles that are inside those bounds and then only draw those rectangles. I can't imagine you are trying to draw 21760 rectangles inside those bounds.
If you are, then try creating one static rectangle and then just try drawing that ONE in all of the different positions you need to draw it at rather than creating a new one every time. For example, in a game I am making, I might have 1000 tiles that are "grass" tiles, but all 1000 of those share the same static texture. So I only need to reference one image rather than each tile creating its own.
Each rectangle can still have a unique state. Just make your own rectangle class and have a static final Image that holds a 5*5 image. Each rectangle will use this image when it needs to be drawn. You can still have unique properties for each rectangle. For example, private Vector2f position, private boolean isAlive, etc
You're probably not going to be able to draw individual rectangles any faster than that.
Games that render millions of polygons per second do so using vertex buffer objects (VBO). For that, you'll probably need to code against the OpenGL API (LWJGL) itself, not a wrapper.
Not sure if Slick will allow it, but if this thing looks anything like a chessboard grid... you could draw just 4 rectangles, grab them and use the resulting image as a texture for your whole image. I'm not even a java programmer just trying to come up with a solution.
Since you're only repeatedly using just a few colors creating a new Color object for every single one is bound to be slow... use new only once for each different color used and store the re-usable colors somewhere in your class, than call the functions with those, constantly allocating and freeing memory is very slow.
And while this might not be as much a benefit as not using new each time but have you considered caching the results of all those function calls and rewriting code as
public void draw(Graphics graphics) {
int ixmax = getWidth();
int iymax = getHeight();
for (int ix = 0; ix < ixmax; ix++) {
for (int iy = 0; iy < iymax; iy++) {
getGameGridAt(ix, iy).draw(graphics);
}
}
}
Or if you'd prefer not to declare new variables
public void draw(Graphics graphics) {
for (int ix = getWidth() - 1; ix >= 0; ix--) {
for (int iy = getHeight() - 1; iy >= 0; iy--) {
getGameGridAt(ix, iy).draw(graphics);
}
}
}
Just noticed in another answer you have an integral size grid (5x5) ... in this case the fastest way to go about this would seem to be to draw each item a single pixel (you can do this directly in memory using a 2-dimensional array) and scale it to 500% or use it as a texture and draw a single rectangle with it the final size you desire ... should be quite fast. Sorry for all the confusion caused by previous answers, you should have said what you're doing more clearly from the start.
If scaling and textures are not available you can still draw in memory using something like this (written in c++, please translate it to java yourself)
for( int x = 0; x < grid.width(); x++ ) {
for( int y = 0; y < grid.height(); y++ ) {
image[x*5][y*5] = grid.color[x][y];
image[x*5][y*5 + 1] = grid.color[x][y];
image[x*5][y*5 + 2] = grid.color[x][y];
image[x*5][y*5 + 3] = grid.color[x][y];
image[x*5][y*5 + 4] = grid.color[x][y];
}
memcpy(image[x*5+1], image[x*5], grid.height() * sizeof(image[0][0]) );
memcpy(image[x*5+2], image[x*5], grid.height() * sizeof(image[0][0]) );
memcpy(image[x*5+3], image[x*5], grid.height() * sizeof(image[0][0]) );
memcpy(image[x*5+4], image[x*5], grid.height() * sizeof(image[0][0]) );
}
I'm not sure, but perhaps for graphics the x and y might be represented in the reversed order than used here, so change the code accordingly if it that's the case (you'll figure that out as soon as a few iterations run), also your data is probably structured a bit differently but I think the idea should be clear.

Extracting a rectangular pixel region from an image

I need to extract a pixel region described by (2n+1) x (2m+1) centred on leftimage (xl,yl). n and m are user input parameters and xl and yl are already defined. Thus far I have this code:
for(int xl = n; xl < picOneGreyScale.getWidth() - n; xl++) {
for(int yl = m; yl < picOneGreyScale.getHeight() - m; yl++) {
//extract (2n+1) x (2m+1) pixel region centred on leftimage (xl,yl);
for(int nArea = xl-n; nArea < xl+n+1; nArea++) {
for(int mArea = yl-m; mArea < yl+m+1; mArea++) {
*code here*
}
}
I'm uncertain as to how to continue. I have defined a BufferedImage called leftRegion:
BufferedImage leftRegion = new BufferedImage((2*n+1),(2*m+1),BufferedImage.TYPE_BYTE_GRAY);
which I intend to use to "extract" my pixel region into. My thoughts thus far are, for where it says code here to extract the pixel at the current location (using getRGB?) and then nesting another for loop to place this pixel within the correct x, y coordinates for leftRegion. I'm not sure how to do this however or if I'm thinking too complex. Alternatively it may be possible to use getRGB with extended arguments:
getRGB(int startX, int startY, int w, int h, int[] rgbArray, int offset, int scansize)
instead of the two inner for loops but again I'm not so hot on how to implement this. Finally there is a method for BufferedImage called copyData which looks like it might be relevant but I'm not sure how to use it. What's the best way to implement this? Many thanks as always.
Additional Information:
Okay so I'm trying to use the getSubImage method of the BufferedImage class:
leftRegion = picOneGreyScale.getSubimage(xl, yl, (2*n+1), (2*m+1));
only I'm getting an error "(y + height) is outside of Raster". How does getSubImage work exactly? Will the image be centered around xl, yl with the width and height being extended equally either side, or does it work differently? Am I even following the right path?
I figured it out. I simply replaced the two inner for loops with this:
leftRegion = picOneGreyScale.getSubimage(xl-n, yl-n, (2*n+1), (2*m+1));

Invert bitmap colors

I have the following problem. I have a charting program, and it's design is black, but the charts (that I get from the server as images) are light (it actually uses only 5 colors: red, green, white, black and gray).
To fit with the design inversion does a good job, the only problem is that red and green are inverted also (green -> pink, red -> green).
Is there a way to invert everything except those 2 colors, or a way to repaint those colors after inversion?
And how costly are those operations (since I get the chart updates pretty often)?
Thanks in advance :)
UPDATE
I tried replacing colors with setPixel method in a loop
for(int x = 0 ;x < chart.getWidth();x++) {
for(int y = 0;y < chart.getHeight();y++) {
final int replacement = getColorReplacement(chart.getPixel(x, y));
if(replacement != 0) {
chart.setPixel(x, y, replacement);
}
}
}
Unfortunetely, the method takes too long (~650ms), is there a faster way to do it, and will setPixels() method work faster?
Manipulating a bitmap is much faster if you copy the image data into an int array by calling getPixels only once, and don't call any function inside the loop. Just manipulate the array, then call setPixels at the end.
Something like that:
int length = bitmap.getWidth()*bitmap.getHeight();
int[] array = new int[length];
bitmap.getPixels(array,0,bitmap.getWidth(),0,0,bitmap.getWidth(),bitmap.getHeight());
for (int i=0;i<length;i++){
// If the bitmap is in ARGB_8888 format
if (array[i] == 0xff000000){
array[i] = 0xffffffff;
} else if ...
}
}
bitmap.setPixels(array,0,bitmap.getWidth(),0,0,bitmap.getWidth(),bitmap.getHeight());
If you have it available as BufferedImage, you can access its raster and edit it as you please.
WritableRaster raster = my_image.getRaster();
// Edit all the pixels you wanna change in the raster (green -> red, pink -> green)
// for (x,y) in ...
// raster.setPixel(x, y, ...)
my_image.setData(raster);
OK seen that you're really only using 5 colors it's quite easy.
Regarding performances, I don't know about Android but I can tell you that in Java using setRGB is amazingly slower than getting back the data buffer and writing directly in the int[].
When I write "amazingly slower", to give you an idea, on OS X 10.4 the following code:
for ( int x = 0; x < width; x++ ) {
for ( int y = 0; y < height; y++ ) {
img.setRGB(x,y,0xFFFFFFFF);
}
}
can be 100 times (!) slower than:
for ( int x = 0; x < width; x++ ) {
for ( int y = 0; y < height; y++ ) {
array[y*width+x] = 0xFFFFFFFF;
}
}
You read correctly: one hundred time. Measured on a Core 2 Duo / Mac Mini / OS X 10.4.
(of course you need to first get access to the underlying int[] array but hopefully this shouldn't be difficult)
I cannot stress enough that the problem ain't the two for loops: in both cases it's the same unoptimized for loops. So it's really setRGB that is the issue here.
I don't know it works on Android, but you probably should get rid of setRGB if you want something that performs well.
A quick way would be to use AvoidXfermode to repaint just those colors you want changed - you could then switch between any colors you want. You just need to do something like this:
// will change red to green
Paint change1 = new Paint();
change1.setColor(Color.GREEN);
change1.setXfermode(new AvoidXfermode(Color.RED, 245, AvoidXfermode.Mode.TARGET));
Canvas c = new Canvas();
c.setBitmap(chart);
c.drawRect(0, 0, width, height, change1);
// rinse, repeat for other colors
You may need to play with the tolerance for the AvoidXfermode, but that should do what you want a lot faster than a per-pixel calculation. Also, make sure your chart image is in ARGB8888 mode. By default, Android tends to work with images in RGB565 mode, which tends to mess up color calculations like you want to use - to be sure, you can make sure your image is both in ARGB8888 mode and mutable by calling Bitmap chart = chartFromServer.copy(Config.ARGB_8888, true); before you setup the Xfermode.
Clarification: to change other colors, you wouldn't have to re-load the images all over again, you would just have to create other Paints with the appropriate colors you want changed like so:
// changes green to red
Paint change1 = new Paint();
change1.setColor(Color.GREEN);
change1.setXfermode(new AvoidXfermode(Color.RED, 245, AvoidXfermode.Mode.TARGET));
// changes white to blue
Paint change2 = new Paint();
change2.setColor(Color.BLUE);
change2.setXfermode(new AvoidXfermode(Color.WHITE, 245, AvoidXfermode.Mode.TARGET));
// ... other Paints with other changes you want to apply to this image
Canvas c = new Canvas();
c.setBitmap(chart);
c.drawRect(0, 0, width, height, change1);
c.drawRect(0, 0, width, height, change2);
//...
c.drawRect(0, 0, width, height, changeN);

Categories