I currently have a Graphics2D object which is acting as a perpendicular coordinate system(regular x-y system). I want to implement zooming which acts on a mouse listener. I have thought of getting a BufferedImage with Robot Class and then using PixelGrabber to zoom in, but wondered if I can directly work with Graphics2D without using Image objects.
Yes. You can use an AffineTransform (AffineTransform.getScaleInstance in particular) to scale all aspects of a graphics context. You could use that to either zoom in (create a larger scale) or zoom out (use a scale less than 1). I have a blog post that shows how to use AffineTransforms for things like rotation and translation, and not so much scaling, but it's a similar principle. You'll have to be mindful of how scaling the graphics context affects things like selection, listeners, etc. (Your coordinate system for the panel will need to be converted into the coordinate system for the graphics context in order to correctly select/click on objects in the scaled graphics context.)
Related
I'm searching for a method to properly scale (almost) all of a JFrame's content. All the solutions I have tried so far had a huge lack in rendering speed. What I am looking for is a speed similar to what you have when scaling content on your smartphone.
The JFrame's content should be rescalable quickly and stay scaled even if you overdraw the JFrame with new content. It should also be flexible enough so it let's you choose which BufferedImage's (which is essentially the only type I'm drawing, I don't draw any other "shapes") to redraw. I'm drawing using an ordinary Graphics, resp. Graphics2D object.
What I've tried before is the Graphic2D's scale-method and using an AffineTransformat object to scale each BufferedImage individually:
g.scale(scalingFactorX, scalingFactorY);
or alternatively:
BufferedImage img = someImageToScale();
AffineTransform scaleTransform = AffineTransform.getScaleInstance(scalingFactorX, scalingFactorY);
AffineTransformOp bilinearScaleOp = new AffineTransformOp(scaleTransform,
AffineTransformOp.TYPE_NEAREST_NEIGHBOR);
return bilinearScaleOp.filter(img, new BufferedImage(targetWidth, targetHeight,
BufferedImage.TYPE_INT_ARGB));
where scalingFactorX/Y are the factors the content should be scaled by and targetWidth, resp. targetHeight denote the resulting (scaled) dimensions of the BufferedImage.
Both approaches are rather slow which seems to be because in both cases, each frame, the scaled version of the contents have to be recalculated. I feel like I'm missing something very obvious here.
While drawImage() can resample an image, it must must do so each time paintComponent() is called. As suggested here, AffineTransformOp lets you choose the interpolation type; use the fastest one that meets your image quality requirements. Once you've satisfactorily resampled an image, ensure that you do not inadvertently resample the image a second time when rendering in a particular Graphics context. Override getPreferredSize() to make the destination component the same size as the resampled image, as shown in these examples.
My images are only 32x32, but there are a large amount of them (~500).
Consider using the flyweight pattern to render only visible images. JTable and JList are examples, as outlined here.
Are there performance benefits or other advantages to using separate components?
You may need to prototype and profile to be sure, perhaps by comparing representative examples:
Typical component-based examples include the button-based game cited here, or the chess games examined here, here and here.
The tile-based game cited here illustrates a single tile panel and multiple accessory panels that all listen to a common game model.
I'm using GraphicsContext.drawImage(img, x, y, w, h) to draw an image scaled to fit a region on my canvas. I'm noticing some aliasing artifacts due to the scaling. Does JavaFX have an equivalent to Swing's RenderingHints.VALUE_INTERPOLATION_BICUBIC?
There are two ways to scale an image. One is to do the scaling via the drawing operator as you have done it and the other one is to scale the image itself when you load it. Some constructors of the Image class have parameters to specified the desired bounds of the image and in addition they have a parameter called "smooth" which, if set to true, should do what you want.
Ok, I'm not even sure if that is the right question to ask.
I've been confused by what is A Graphics object for a while now, I used to think that it is simplly a tool to use to change colors and draw to specific container(ie JFrame, JPanel).
However, I've been studying buffering(triple, double, flipping...etc) and how it works for 3 days now, and my confusion has only increased. for instance, why when we need to draw to the buffer(ie BufferStrategy, BufferedImage) we get its own graphics object to draw to it and then we project it to the screen? does the Graphics Object represent the drawing surface (ie the JPanel it self if we're using one to draw custom painting via JPanel#paintComponent(Graphics g)) ?
and so when we're getting the graphics object of a buffer, do we actually get its drawing surface to paint on?
Please somebody explain, any help is appreciated
Thanks
From the API:
A Graphics object encapsulates state information needed for the basic
rendering operations that Java supports. This state information
includes the following properties:
The Component object on which to draw.
A translation origin for rendering and clipping coordinates.
The current clip.
The current color.
The current font.
The current logical pixel operation function (XOR or Paint).
The current XOR alternation color (see setXORMode(java.awt.Color)).
So a Graphics instance contains information about WHERE to draw (a component) as well as HOW to draw it (a color, font, etc). It then gives you methods so you can tell it WHAT to draw (a rectangle, circle, text, etc).
I have a java.awt.canvas object and I draw stuff with the Graphics2D (which I get from the bufferStrategy) and I'd like to "zoom" in and out.
So if I zoom in (scaling it up by a factor of 1) such that a line I draw from (0,0) to (10,10) Would be in reality drawn from (0,0) to (20,20)
Is this possible, or do I have to implement this myself?
Take a look at Graphics2D: http://docs.oracle.com/javase/7/docs/api/java/awt/Graphics2D.html
You apply a suitable transformation to the graphics to achieve many transformations, rotate, scale (aka zoom) and translation. Simplest way to zoom would probably be
graphics2d.scale(2.0, 2.0); // draw everything twice the original size
When I apply canvas.getContext2d().scale(1.5, 1.5), then my objects in the canvas gets bigger as expected, but are somehow blurred.
What do I have to do to make the canvas draw my objects as sharp as it is when not scaled?
Use the antialiasing rendering hint:
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
So when you scale, it will look really better.
The images of the right use the RenderingHints.VALUE_ANTIALIAS_ON
I discovered that there are ctx.transform() and ctx.scale()methods for a Canvas. Which work fine, but as they behave like just scaling an image up, the result is not sharp. Guess due to (anti)alasing and stuff like that.
So I decided to rewrite all my ctx.draw() methods to respect a GLOBAL_OFFSET, which changes value when user zooms in and out. This way, the canvas objects can keep their original coordiante point values, but respecting the zoom level and offset it is possible to draw them bigger or thinner, which kind of "simmulates" the zooming and panning.