Java-Gaming.org    
Featured games (91)
games approved by the League of Dukes
Games in Showcase (581)
games submitted by our members
Games in WIP (500)
games currently in development
News: Read the Java Gaming Resources, or peek at the official Java tutorials
 
    Home     Help   Search   Login   Register   
Pages: [1]
  ignore  |  Print  
  Image Rotator  (Read 3380 times)
0 Members and 1 Guest are viewing this topic.
Offline philfrei
« Posted 2012-08-21 03:29:43 »

I wanted to crop an image after rotating it, and couldn't figure out a way to do this using the Java2D AffineTransform (and not have it wreak havok with the rest of what was on the screen.

Maybe there is a way to do this and I just reinvented a wheel for nothing. But at least I finally learned something about basic 2D Transforms (and the matrix & vector math underlying).

So, below is some code that can rotate an image. First a demo graphic or two:

Graphic for Apo!



Code is here:
http://pastebin.java-gaming.org/55c3f863a26

The code: first get it working, now it needs improvement. The biggest question I have is the way I interpolate the new pixel values. As I generate the new locations for the original pixels, I store the distance to the surrounding four points (or less, if one is on the edge). Then, when determining the color of the new pixels, I take the closest three and use a weighted average based on their distances. This seems a little dubious as the the triangles are not equilateral, so the angles should also be taken into consideration. However, they are right triangles and thus the corners are pretty well spread out, and visually it seems to work pretty well.

I tried using just the closest point, and by calculating the weighted average using the square of the distance, but both shortcuts were considerably more jaggy. I am assuming someone has come up with a better way to do the interpolation of the new values, and would love to learn about it.

To use the code, create an ImageRotator object, then call the rotate() method, with three parameters (1) image to be rotated (2) radians of rotation (3) a boolean that is false if the graphic has no alpha (is a normal jpeg, for example) or true if there is alpha data that needs to be part of the interpolation. The method returns a BufferedImage of the rotated graphic.

"Greetings my friends! We are all interested in the future, for that is where you and I are going to spend the rest of our lives!" -- The Amazing Criswell
Offline philfrei
« Reply #1 - Posted 2012-08-21 03:36:53 »

this rotation dedicated to Nate:


"Greetings my friends! We are all interested in the future, for that is where you and I are going to spend the rest of our lives!" -- The Amazing Criswell
Offline philfrei
« Reply #2 - Posted 2012-08-21 20:29:09 »

Am reading that a more accurate lerp would make use of barycentric math. Am working to acquire this. Kind of nice: getting a grip on barycentric math should help with understanding the Simplex noise generation algorithm.

I guess some efficiency might be achieved by packing the color into a long, and only unpacking when it becomes time to lerp. Also, perhaps just store all the points that are candidates for "closest" and only pick the closest three when it comes time to lerp. Huh Will have to do more experiments.

Maybe don't need to compute the new image x,y sizes directly, but can get this from the four corners (which are needed regardless in order to get the translation required to keep the new coordinates positive).

"Greetings my friends! We are all interested in the future, for that is where you and I are going to spend the rest of our lives!" -- The Amazing Criswell
Games published by our own members! Check 'em out!
Legends of Yore - The Casual Retro Roguelike
Offline davedes
« Reply #3 - Posted 2012-08-21 21:04:21 »

Why don't you just use g.rotate and g.drawImage on a new BufferedImage? It will (should) take advantage of hardware if possible.

Offline philfrei
« Reply #4 - Posted 2012-08-21 22:13:23 »

Why don't you just use g.rotate and g.drawImage on a new BufferedImage? It will (should) take advantage of hardware if possible.
Thanks for the reply, davedes. I tried using g.rotate, but it doesn't return an image. I wish to clip the resulting rotated image before displaying it, and would like the flexibility of performing further operations on it as well. Is there is a way to clip an image after it has been rotated using g.rotate that leaves the rest of the display space intact?

One could edit an image prior to rotating so that the rotated result will be clipped correctly, but that seems overly complicated.

Is there a way to get at data (extract a BufferedImage) from a Graphics object?

I'm working on a texture editor, and rotation might be useful there. The obvious scaling controls only work directly on the vertical and horizontal, they don't create skews (or I'm not "getting" how to do this).

"Greetings my friends! We are all interested in the future, for that is where you and I are going to spend the rest of our lives!" -- The Amazing Criswell
Offline StumpyStrust
« Reply #5 - Posted 2012-08-22 01:46:36 »

If you use a volatile image as a back buffer you can call getSnapShot() which will return a BufferedImage. I do not know how slow this though...

Offline ra4king

JGO Kernel


Medals: 322
Projects: 2
Exp: 4 years


I'm the King!


« Reply #6 - Posted 2012-08-22 04:36:34 »

You could set a clip on the Graphics object itself?

Offline davedes
« Reply #7 - Posted 2012-08-22 04:55:35 »

Your method doesn't seem to work for the following image:
http://i.imgur.com/4wfDs.png

I'd recommend modifying the int array directly, instead of calling setPixel, if you know the image type.

Another way to do this (if I understand your goal correctly) is to create a new BufferedImage, then use getGraphics and drawImage with the affine transform.

Here is a simple code example


Offline philfrei
« Reply #8 - Posted 2012-08-23 07:58:57 »

@davedes!

First off, thanks for pointing out that one can get a Graphics2D object from a BufferedImage. That makes SO much more possible! I'll have to play with it a bit, in terms of learning the in's and out's, especially with the aspects like the various RenderingHints and when it is appropriate to use them.

Quote
Your method doesn't seem to work for the following image:
http://i.imgur.com/4wfDs.png

You are right! I'm seeing now that one can do a getType() method to determine the type of a graphic. I've always just used TYPE_INT_ARGB for my dabbling. It looks like I was reading alpha channel data where I thought I was dealing with red, etc.

Quote
I'd recommend modifying the int array directly, instead of calling setPixel, if you know the image type.

By that, do you mean using setRGB() method? OK, that makes sense. Bit shifts and adds to assemble the int should execute quickly.

I hadn't thought much about the edge issue, beyond noticing the jaggies on my demo examples. The main place I was going to use this was with a graphic that has alpha = 0 on the edges. Computationally adding anti-aliasing--that would take some thought. And, am less likely to do so, now that using getGraphics() has been pointed out.

I did work out a mathematically correct algorithm for a linear interpolation of the color values, earlier this evening. It is a little involved, first one determines Barycentric origin and basis vectors from the color values of the translated points, then plug (x, y) values from them into an equation to solve for the value of the new point. I'm tempted to try implementing it and seeing how much better it looks (and slower it performs) than the shortcut of weighting via the distances. Am also curious how the Java writers have handled all this--maybe the source code is available. Priorities, though...

"Greetings my friends! We are all interested in the future, for that is where you and I are going to spend the rest of our lives!" -- The Amazing Criswell
Offline davedes
« Reply #9 - Posted 2012-08-24 04:40:36 »

Quote
By that, do you mean using setRGB() method? OK, that makes sense. Bit shifts and adds to assemble the int should execute quickly.
Nope, like this:

1  
int[] pixels = ((DataBufferInt)img.getRaster().getDataBuffer()).getData();


Now you can modify the pixel array directly. For byte[] type images, it would be DataBufferByte.

You can also use the getGraphics method to conveniently "convert" an image to the expected type.
1  
2  
3  
4  
5  
if (image.getType()!=BufferedImage.TYPE_INT_ARGB) {
    BufferedImage newImage = new BufferedImage(image.getWidth(), image.getHeight(), BufferedImage.TYPE_INT_ARGB);
    newImage.getGraphics().drawImage(image, 0, 0, null);
    image = newImage;
}

Games published by our own members! Check 'em out!
Legends of Yore - The Casual Retro Roguelike
Offline Riven
« League of Dukes »

JGO Overlord


Medals: 605
Projects: 4
Exp: 16 years


Hand over your head.


« Reply #10 - Posted 2012-08-27 12:48:04 »

Note that that last code  sample renders  the translucent image on a black 'background'. Use a Comppsite to make the target image fully transparant prior to drawing your image  on it.

Hi, appreciate more people! Σ ♥ = ¾
Learn how to award medals... and work your way up the social rankings
Offline nsigma
« Reply #11 - Posted 2012-08-27 14:02:02 »

Note that that last code  sample renders  the translucent image on a black 'background'. Use a Comppsite to make the target image fully transparant prior to drawing your image  on it.

A new BufferedImage of type ARGB (or any transparent type) should be initialized as fully transparent already, no?

Praxis LIVE - open-source intermedia toolkit and live interactive visual editor
Digital Prisoners - interactive spaces and projections
Offline Riven
« League of Dukes »

JGO Overlord


Medals: 605
Projects: 4
Exp: 16 years


Hand over your head.


« Reply #12 - Posted 2012-08-27 14:06:13 »

Note that that last code  sample renders  the translucent image on a black 'background'. Use a Comppsite to make the target image fully transparant prior to drawing your image  on it.

A new BufferedImage of type ARGB (or any transparent type) should be initialized as fully transparent already, no?
I just described how that assumption is false.

Hi, appreciate more people! Σ ♥ = ¾
Learn how to award medals... and work your way up the social rankings
Offline nsigma
« Reply #13 - Posted 2012-08-27 14:22:43 »

I just described how that assumption is false.

I was questioning whether you had a concrete example of when that assumption doesn't hold up?  The underlying DataBufferInt has its arrays initialized with all values = 0 (ie. full transparent), and in all uses I've made that's the result.  However, I can't find anything in the JavaDoc that states this should always be the case.

Praxis LIVE - open-source intermedia toolkit and live interactive visual editor
Digital Prisoners - interactive spaces and projections
Offline Riven
« League of Dukes »

JGO Overlord


Medals: 605
Projects: 4
Exp: 16 years


Hand over your head.


« Reply #14 - Posted 2012-08-27 14:48:07 »

You are right.

I have to add though that I was required to add that code somewhere in 2004, because otherwise the result simply didn't contain any translucency:
1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
   public static BufferedImage copy(BufferedImage src, int newType) {
      if (newType == BufferedImage.TYPE_CUSTOM)
         throw new IllegalStateException();

      BufferedImage dst = new BufferedImage(src.getWidth(), src.getHeight(), newType);
     if (dst.getColorModel().hasAlpha()) {
        ImageUtil.makeTransparent(dst);
     }
      dst.getGraphics().drawImage(src, 0, 0, null);
      return dst;
   }

   public static void makeTransparent(BufferedImage img) {
      Graphics2D g = img.createGraphics();
      g.setComposite(AlphaComposite.getInstance(AlphaComposite.CLEAR, 0.0f));
      g.fillRect(0, 0, img.getWidth(), img.getHeight());
      g.dispose();
   }


Seems like this was a bug, and has been solved (probably ages ago). It may have been related to an ancient nVidia bug that make it impossible to use glClearColor(0,0,0,0) to glClear(...) the framebuffer, that was solved in 2006, IIRC.

Hi, appreciate more people! Σ ♥ = ¾
Learn how to award medals... and work your way up the social rankings
Pages: [1]
  ignore  |  Print  
 
 
You cannot reply to this message, because it is very, very old.

 

Add your game by posting it in the WIP section,
or publish it in Showcase.

The first screenshot will be displayed as a thumbnail.

xsi3rr4x (57 views)
2014-04-15 18:08:23

BurntPizza (55 views)
2014-04-15 03:46:01

UprightPath (68 views)
2014-04-14 17:39:50

UprightPath (51 views)
2014-04-14 17:35:47

Porlus (68 views)
2014-04-14 15:48:38

tom_mai78101 (94 views)
2014-04-10 04:04:31

BurntPizza (154 views)
2014-04-08 23:06:04

tom_mai78101 (249 views)
2014-04-05 13:34:39

trollwarrior1 (205 views)
2014-04-04 12:06:45

CJLetsGame (213 views)
2014-04-01 02:16:10
List of Learning Resources
by SHC
2014-04-18 03:17:39

List of Learning Resources
by Longarmx
2014-04-08 03:14:44

Good Examples
by matheus23
2014-04-05 13:51:37

Good Examples
by Grunnt
2014-04-03 15:48:46

Good Examples
by Grunnt
2014-04-03 15:48:37

Good Examples
by matheus23
2014-04-01 18:40:51

Good Examples
by matheus23
2014-04-01 18:40:34

Anonymous/Local/Inner class gotchas
by Roquen
2014-03-11 15:22:30
java-gaming.org is not responsible for the content posted by its members, including references to external websites, and other references that may or may not have a relation with our primarily gaming and game production oriented community. inquiries and complaints can be sent via email to the info‑account of the company managing the website of java‑gaming.org
Powered by MySQL Powered by PHP Powered by SMF 1.1.18 | SMF © 2013, Simple Machines | Managed by Enhanced Four Valid XHTML 1.0! Valid CSS!