maybe someone can give hand of help and tell how to create and print form
like this:
using java.
Also, it should be filled with needed information.
java.awt.print - Java 2D printing, since JDK 1.2
javax.print - aka the Java Print Service (JPS) API, since JDK 1.4
from http://java.sun.com/javase/technologies/desktop/printing/
I think you need a bit of googling - it looks like a very trivial task.
If you are using Swing, the follow the procedure below:
For A4 setting:
Use a JFrame of approx. 750 px. X 960 px.
In the Window use JLabels, JTextFields and JTextAreas to Design the template.
Also do add a print button anywhere on the window (to initiate the print command).
Now when all designing is complete, in the code window of the button action event, simply
add:
<Button Name>.setVisible(false);
<PanelName>.print();
First one will hide the Button, second will actually present you with a print dialog.
Additionally, use Netbeans IDE to save time in designing. It is a great time saver in the designing, compiling and testing grounds.
Please revert back for any doubts, Hope the information is helpful.
If you need to do it in a web application, the printing should be done in javascript. But you may render the page using Java. http://shyarmal.blogspot.com/2011/08/printing-example-with-java-ee.html
If you are doing it using swing: http://shyarmal.blogspot.com/2011/08/printing-with-jeditorpane.html
A little late, but I'll leave this here for reference:
//pertinent code only
import java.awt.print
public void FilePrintClicked(){
PrinterJob job = PrinterJob.getPrinterJob();
PageFormat format = job.defaultPage();
format.setOrientation(PageFormat.LANDSCAPE);
job.setPrintable(this, format);
try{
if(job.printDialog()) job.print();
}
catch(Exception e){e.printStackTrace();}
}
public int print(Graphics g, PageFormat format, int pagenum) {
if (pagenum > 0){
return Printable.NO_SUCH_PAGE;
}
g.translate((int)format.getImageableX(), (int)format.getImageableY());
float pageWidth = (float)format.getImageableWidth();
float pageHeight = (float)format.getImageableHeight();
float imageHeight = (float)this.getHeight();
float imageWidth = (float)this.getWidth();
float scaleFactor = Math.min((float)pageWidth/(float)imageWidth, (float)pageHeight/(float)imageHeight);
int scaledWidth = (int)(((float)imageWidth)*scaleFactor);
int scaledHeight = (int)(((float)imageHeight)*scaleFactor);
BufferedImage canvas = new BufferedImage( this.getWidth(), this.getHeight(), BufferedImage.TYPE_INT_RGB);
Graphics2D gg = canvas.createGraphics();
this.paint( gg );
Image img = canvas ;
g.drawImage(img, 0, 0, scaledWidth, scaledHeight, null );
return Printable.PAGE_EXISTS;
}
Note: Your class needs to implement Printable
It's a little dirty, but it's rather old code from when I was learning Java and I didn't double-check it as I posted it here, but it's working in my application so.....
Related
I have looked at these post How to resize an image without loading into memory? and JMACKIG https://github.com/justinedelson/jmagick only runs on linux and im on a windows currently or has not been tested on onther operating systems besided linux is notes in the README.md Also have looked at this post Java image scaling without loading the whole image into memory But in this case he get the image from a File in mine case I get the image from a Image object.
The use case in mine case is that mine program is in a while loop and then takes screenshots. Then the program looks at the image to detect color and determain where ever it has to do some action based on the color it finds. But the problem right now is that after about 60 iterations of the loop I get a memory leak with this error java get out of memory heap space
So I also tried to clear up mine java cache after every iterations with this bat file: Clear Java Cache but thats seems to do about nothing
So is there anyway that its possible to clear the cache after every iteration of the loop or in the resize function clear the cache of make that the image doesnt go into the cache? Also have seen post where you can increase the max cache size of the program but that doesnt really fix the problem in this case cause The program runs doubtly longer but the error will still occurr and I want to find a way to make it possible that I dont get that memory leak at all.
the code for the image resizing
public Image SCapture(int w, int h) throws Exception
{
Robot robot = new Robot();
BufferedImage screenCapture = robot.createScreenCapture(new Rectangle(Toolkit.getDefaultToolkit().getScreenSize()));
Image image = getScaledImage(screenCapture, w, h);
return image;
}
//Gets screen width and height from main
private static Image getScaledImage(Image srcImg, int w, int h){
BufferedImage resizedImg = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2 = resizedImg.createGraphics();
g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
g2.drawImage(srcImg, 0, 0, w, h, null);
g2.dispose();
return resizedImg;
}
Also I dont really care about image quality it just has to be done fast and clean
Update: After looking in the code more it was something with mine ArrayList checking all the color of the pixels. But this little trick at the end of mine while loop solved the problem
How to force garbage collection in Java?
public static void gc() {
Object obj = new Object();
WeakReference ref = new WeakReference<Object>(obj);
obj = null;
while(ref.get() != null) {
System.gc();
}
}
I need to resize PNG, JPEG and GIF files. How can I do this using Java?
FWIW I just released (Apache 2, hosted on GitHub) a simple image-scaling library for Java called imgscalr (available on Maven central).
The library implements a few different approaches to image-scaling (including Chris Campbell's incremental approach with a few minor enhancements) and will either pick the most optimal approach for you if you ask it to, or give you the fastest or best looking (if you ask for that).
Usage is dead-simple, just a bunch of static methods. The simplest use-case is:
BufferedImage scaledImage = Scalr.resize(myImage, 200);
All operations maintain the image's original proportions, so in this case you are asking imgscalr to resize your image within a bounds of 200 pixels wide and 200 pixels tall and by default it will automatically select the best-looking and fastest approach for that since it wasn't specified.
I realize on the outset this looks like self-promotion (it is), but I spent my fair share of time googling this exact same subject and kept coming up with different results/approaches/thoughts/suggestions and decided to sit down and write a simple implementation that would address that 80-85% use-cases where you have an image and probably want a thumbnail for it -- either as fast as possible or as good-looking as possible (for those that have tried, you'll notice doing a Graphics.drawImage even with BICUBIC interpolation to a small enough image, it still looks like garbage).
After loading the image you can try:
BufferedImage createResizedCopy(Image originalImage,
int scaledWidth, int scaledHeight,
boolean preserveAlpha)
{
System.out.println("resizing...");
int imageType = preserveAlpha ? BufferedImage.TYPE_INT_RGB : BufferedImage.TYPE_INT_ARGB;
BufferedImage scaledBI = new BufferedImage(scaledWidth, scaledHeight, imageType);
Graphics2D g = scaledBI.createGraphics();
if (preserveAlpha) {
g.setComposite(AlphaComposite.Src);
}
g.drawImage(originalImage, 0, 0, scaledWidth, scaledHeight, null);
g.dispose();
return scaledBI;
}
Thumbnailator is an open-source image resizing library for Java with a fluent interface, distributed under the MIT license.
I wrote this library because making high-quality thumbnails in Java can be surprisingly difficult, and the resulting code could be pretty messy. With Thumbnailator, it's possible to express fairly complicated tasks using a simple fluent API.
A simple example
For a simple example, taking a image and resizing it to 100 x 100 (preserving the aspect ratio of the original image), and saving it to an file can achieved in a single statement:
Thumbnails.of("path/to/image")
.size(100, 100)
.toFile("path/to/thumbnail");
An advanced example
Performing complex resizing tasks is simplified with Thumbnailator's fluent interface.
Let's suppose we want to do the following:
take the images in a directory and,
resize them to 100 x 100, with the aspect ratio of the original image,
save them all to JPEGs with quality settings of 0.85,
where the file names are taken from the original with thumbnail. appended to the beginning
Translated to Thumbnailator, we'd be able to perform the above with the following:
Thumbnails.of(new File("path/to/directory").listFiles())
.size(100, 100)
.outputFormat("JPEG")
.outputQuality(0.85)
.toFiles(Rename.PREFIX_DOT_THUMBNAIL);
A note about image quality and speed
This library also uses the progressive bilinear scaling method highlighted in Filthy Rich Clients by Chet Haase and Romain Guy in order to generate high-quality thumbnails while ensuring acceptable runtime performance.
You don't need a library to do this. You can do it with Java itself.
Chris Campbell has an excellent and detailed write-up on scaling images - see this article.
Chet Haase and Romain Guy also have a detailed and very informative write-up of image scaling in their book, Filthy Rich Clients.
Java Advanced Imaging is now open source, and provides the operations you need.
If you are dealing with large images or want a nice looking result it's not a trivial task in java. Simply doing it via a rescale op via Graphics2D will not create a high quality thumbnail. You can do it using JAI, but it requires more work than you would imagine to get something that looks good and JAI has a nasty habit of blowing our your JVM with OutOfMemory errors.
I suggest using ImageMagick as an external executable if you can get away with it. Its simple to use and it does the job right so that you don't have to.
If, having imagemagick installed on your maschine is an option, I recommend im4java. It is a very thin abstraction layer upon the command line interface, but does its job very well.
The Java API does not provide a standard scaling feature for images and downgrading image quality.
Because of this I tried to use cvResize from JavaCV but it seems to cause problems.
I found a good library for image scaling: simply add the dependency for "java-image-scaling" in your pom.xml.
<dependency>
<groupId>com.mortennobel</groupId>
<artifactId>java-image-scaling</artifactId>
<version>0.8.6</version>
</dependency>
In the maven repository you will get the recent version for this.
Ex. In your java program
ResampleOp resamOp = new ResampleOp(50, 40);
BufferedImage modifiedImage = resamOp.filter(originalBufferedImage, null);
You could try to use GraphicsMagick Image Processing System with im4java as a comand-line interface for Java.
There are a lot of advantages of GraphicsMagick, but one for all:
GM is used to process billions of
files at the world's largest photo
sites (e.g. Flickr and Etsy).
Image Magick has been mentioned. There is a JNI front end project called JMagick. It's not a particularly stable project (and Image Magick itself has been known to change a lot and even break compatibility). That said, we've had good experience using JMagick and a compatible version of Image Magick in a production environment to perform scaling at a high throughput, low latency rate. Speed was substantially better then with an all Java graphics library that we previously tried.
http://www.jmagick.org/index.html
Simply use Burkhard's answer but add this line after creating the graphics:
g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
You could also set the value to BICUBIC, it will produce a better quality image but is a more expensive operation. There are other rendering hints you can set but I have found that interpolation produces the most notable effect.
Keep in mind if you want to zoom in in a lot, java code most likely will be very slow. I find larger images start to produce lag around 300% zoom even with all rendering hints set to optimize for speed over quality.
You can use Marvin (pure Java image processing framework) for this kind of operation:
http://marvinproject.sourceforge.net
Scale plug-in:
http://marvinproject.sourceforge.net/en/plugins/scale.html
It turns out that writing a performant scaler is not trivial. I did it once for an open source project: ImageScaler.
In principle 'java.awt.Image#getScaledInstance(int, int, int)' would do the job as well, but there is a nasty bug with this - refer to my link for details.
I have developed a solution with the freely available classes ( AnimatedGifEncoder, GifDecoder, and LWZEncoder) available for handling GIF Animation.
You can download the jgifcode jar and run the GifImageUtil class.
Link: http://www.jgifcode.com
you can use following popular product: thumbnailator
If you dont want to import imgScalr like #Riyad Kalla answer above which i tested too works fine, you can do this
taken from Peter Walser answer #Peter Walser on another issue though:
/**
* utility method to get an icon from the resources of this class
* #param name the name of the icon
* #return the icon, or null if the icon wasn't found.
*/
public Icon getIcon(String name) {
Icon icon = null;
URL url = null;
ImageIcon imgicon = null;
BufferedImage scaledImage = null;
try {
url = getClass().getResource(name);
icon = new ImageIcon(url);
if (icon == null) {
System.out.println("Couldn't find " + url);
}
BufferedImage bi = new BufferedImage(
icon.getIconWidth(),
icon.getIconHeight(),
BufferedImage.TYPE_INT_RGB);
Graphics g = bi.createGraphics();
// paint the Icon to the BufferedImage.
icon.paintIcon(null, g, 0,0);
g.dispose();
bi = resizeImage(bi,30,30);
scaledImage = bi;// or replace with this line Scalr.resize(bi, 30,30);
imgicon = new ImageIcon(scaledImage);
} catch (Exception e) {
System.out.println("Couldn't find " + getClass().getName() + "/" + name);
e.printStackTrace();
}
return imgicon;
}
public static BufferedImage resizeImage (BufferedImage image, int areaWidth, int areaHeight) {
float scaleX = (float) areaWidth / image.getWidth();
float scaleY = (float) areaHeight / image.getHeight();
float scale = Math.min(scaleX, scaleY);
int w = Math.round(image.getWidth() * scale);
int h = Math.round(image.getHeight() * scale);
int type = image.getTransparency() == Transparency.OPAQUE ? BufferedImage.TYPE_INT_RGB : BufferedImage.TYPE_INT_ARGB;
boolean scaleDown = scale < 1;
if (scaleDown) {
// multi-pass bilinear div 2
int currentW = image.getWidth();
int currentH = image.getHeight();
BufferedImage resized = image;
while (currentW > w || currentH > h) {
currentW = Math.max(w, currentW / 2);
currentH = Math.max(h, currentH / 2);
BufferedImage temp = new BufferedImage(currentW, currentH, type);
Graphics2D g2 = temp.createGraphics();
g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
g2.drawImage(resized, 0, 0, currentW, currentH, null);
g2.dispose();
resized = temp;
}
return resized;
} else {
Object hint = scale > 2 ? RenderingHints.VALUE_INTERPOLATION_BICUBIC : RenderingHints.VALUE_INTERPOLATION_BILINEAR;
BufferedImage resized = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2 = resized.createGraphics();
g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, hint);
g2.drawImage(image, 0, 0, w, h, null);
g2.dispose();
return resized;
}
}
Try this folowing method :
ImageIcon icon = new ImageIcon("image.png");
Image img = icon.getImage();
Image newImg = img.getScaledInstance(350, 350, java.evt.Image.SCALE_SMOOTH);
icon = new ImageIcon(img);
JOptionPane.showMessageDialog(null, "image on The frame", "Display Image", JOptionPane.INFORMATION_MESSAGE, icon);
you can also use
Process p = Runtime.getRuntime().exec("convert " + origPath + " -resize 75% -quality 70 " + largePath + "");
p.waitFor();
Design jLabel first:
JLabel label1 = new JLabel("");
label1.setHorizontalAlignment(SwingConstants.CENTER);
label1.setBounds(628, 28, 169, 125);
frame1.getContentPane().add(label1); //frame1 = "Jframe name"
Then you can code below code(add your own height and width):
ImageIcon imageIcon1 = new ImageIcon(new ImageIcon("add location url").getImage().getScaledInstance(100, 100, Image.SCALE_DEFAULT)); //100, 100 add your own size
label1.setIcon(imageIcon1);
I am a relative newbie in game programming. I know how to draw pixels to a BufferedImage using setPixel(). It is horribly slow on larger formats so I moved on and found VolatileImage (took me a week or so). It is fairly easy to draw lines, strings, rects, etc but I can't draw individual pixels. I already tried using drawLine(x,y,x,y) but I get 3-4 FPS on an 800x600 image.
The fact that java didn't include setPixel() or setRGB() in the VolatileImage makes me pretty angry and confused.
I have 4 questions:
Is there a way to draw individual pixels on a VolatileImage? (on 1440x900 formats with FPS > 40)
Can I draw pixels in a BufferedImage with a faster method? (same 1440x900, FPS > 40)
Is there any other way to draw pixels fast enough for 3D games?
Can I make my BufferedImage hardware accelerated( tried using setAccelerationPriority(1F) but it doesn't work)
Please if you have any idea tell me. I can't continue making my game wihout this information. I already made 3D rendering algorithms but i need to be able to draw fast pixels. I have got a good feeling about this game.
Here's the code if it can help you help me:
public static void drawImageRendered (int x, int y, int w, int h) { // This is just a method to test the performance
int a[] = new int[3]; // The array containing R, G and B value for each pixel
bImg = Launcher.contObj.getGraphicsConfiguration().createCompatibleImage(800, 600); // Creates a compatible image for the JPanel object i am working with (800x600)
bImg.setAccelerationPriority(1F); // I am trying to get this image accelerated
WritableRaster wr = bImg.getRaster(); // The image's writable raster
for (int i = 0; i < bImg.getWidth(); i++) {
for (int j = 0; j < bImg.getHeight(); j++) {
a[0] = i % 256;
a[2] = j % 256;
a[1] = (j * i) % 256;
wr.setPixel(i, j, a); // Sets the pixels (You get a nice pattern)
}
}
g.drawImage(bImg, x, y, w, h, null);
}
I would much prefer not using OpenGL or any other external libraries, just plain Java.
Well you're basically drawing one pixel after the other using the CPU. There's no way that this can be accelerated, thus such a method does simply not make any sense for a VolatileImage. The low FPS you get suggest that this even causes a significant overhead, as each pixel drawing operation is sent to the graphics card (with information such as location & colour), which takes longer than to modify 3 or 4 bytes of RAM.
I suggest to either stop drawing each pixel separately or to figure out a way to make your drawing algorithm run directly on the graphics card (which most likely requires another language than Java).
It's been over 4 years since this post got an answer. I was looking for an answer to this question as well and stumbled on this post. After some more searching, I got it to work. Below I'll post the source to rendering pixels with a VolatileImage.
It seems Java hides our ability to plot pixels directly to a VolatileImage, but we can draw buffered images to it. For good reason. Using the software to plot a pixel doesn't really help with acceleration(in Java it seems). If you can plot pixels to a BufferedImage, and then render it on a VolatileImage, you may get a speed bonus since it's hardware accelerated from that point.
The source down below is a self-contained example. You can copy-pasta practically all of it to your project and run it.
https://github.com/Miekpeeps/JavaSnippets-repo/blob/master/src/graphics_rendering/pixels_03/PlottingVolatile.java
In the constructor I save the Graphics environment of the app/game.
private GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
private GraphicsConfiguration gc = ge.getDefaultScreenDevice().getDefaultConfiguration();
Then, when I call a method to enable hardware we create a buffer. I set the transparency to Opaque. In my little engine, I deal with transparency/alpha blending on another thread in the pipeline.
public void setHardwareAcceleration(Boolean hw)
{
useHW = hw;
if (hw)
{
vbuffer = gc.createCompatibleVolatileImage(width, height, Transparency.OPAQUE);
System.setProperty("sun.java2d.opengl", hw.toString()); // may not be needed.
}
}
For each frame I update, I get the Graphics from the VolatileImage and render my buffer there. Nothing gets rendered if I dont flush().
#Override
public void paintComponent(Graphics g)
{
if(useHW)
{
g = vbuffer.getGraphics();
g.drawImage(buffer, 0, 0, null);
vbuffer.flush();
}
else
{
g.drawImage(buffer, 0, 0, null);
buffer.flush();
}
}
There is still a little bit of overhead when calling to plot a pixel on the BufferedImage writable raster. But when we update the screen, we get a speed boost when using the Volatile image instead of using the Buffered image.
Hope this helps some folks out. Cheers.
I've got a Java desktop app that works, amongst other, on OS X.
Now the new MacBook Pro has a retina display and I'm concerned: how is it going to work regarding Swing?
What about when a Java app uses both Swing components and some bitmap graphics (like custom icons / ImageIcon)?
Shall all desktop Java apps be automatically resized (for example by quadrupling every pixel) or am I going to need to create two versions of my icons set (for example one with 24x24 icons and the other with 96x96 icons) and somehow determine that the app is running on a retina display?
Use IconLoader library. It supports HiDPI images http://bulenkov.com/iconloader/ It also provides a way to work with HiDPI images (drawing, etc)
On Apple's Java 6 you can provide multiple versions of the same image. Depending on the screen (retina or not), one or the other image is picked and drawn.
However, those images have to loaded in a special way:
Toolkit.getDefaultToolkit().getImage("NSImage://your_image_name_without_extension");
For example, if your (regular resolution) image is called: "scissor.png", you have to create a high resolution version "scissor#2x.png" (following the Apple naming conventions) and place both images in the Resources directory of your app bundle (yes, you need to bundle your app).
Then call:
Image img = Toolkit.getDefaultToolkit().getImage("NSImage://scissor");
You can use the resulting image in your buttons and it will be drawn with the right resolution magically.
There are two other "tricks" you can use:
Using an AffineTransform of (0.5, 0.5) on your Graphics2D object before drawing an Image. Also see this java-dev message
Creating a high dpi version of your image programmatically using this hack
The first "trick" (0.5 scaling) by now also works on Oracle's Java 7/8.
I.e. if you draw an image with 0.5 scaling directly to the component's Graphics object, it will be rendered in high resolution on Retina displays (and also with half its original size).
Update
Starting with Java 9, there is better built-in support for images with different resolutions via the MultiResolutionImage interface. For more details, please see this answer.
I can confirm that the scaling your images works with on Oracle Java 1.8. I cannot get the NSImage hack to work on java 1.7 or 1.8. I think this only works with Java 6 from Mac...
Unless someone else has a better solution, what I do is the following:
Create two sets of icons.
If you have a 48pixel width icon create one 48px #normal DPI and another at 96px with 2x DPI. Rename the 2xDPI image as #2x.png to conform with apple naming standards.
Subclass ImageIcon and call it RetinaIcon or whatever.
You can test for a Retina display as follows:
public static boolean isRetina() {
boolean isRetina = false;
GraphicsDevice graphicsDevice = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice();
try {
Field field = graphicsDevice.getClass().getDeclaredField("scale");
if (field != null) {
field.setAccessible(true);
Object scale = field.get(graphicsDevice);
if(scale instanceof Integer && ((Integer) scale).intValue() == 2) {
isRetina = true;
}
}
}
catch (Exception e) {
e.printStackTrace();
}
return isRetina;
}
Make sure to #Override the width and height of the new ImageIcon class as follows:
#Override
public int getIconWidth()
{
if(isRetina())
{
return super.getIconWidth()/2;
}
return super.getIconWidth();
}
#Override
public int getIconHeight()
{
if(isRetina())
{
return super.getIconHeight()/2;
}
return super.getIconHeight();
}
Once you have a test for the retina screen and your custom width/height methods overridden you can customise the painIcon method as follows:
#Override
public synchronized void paintIcon(Component c, Graphics g, int x, int y)
{
ImageObserver observer = getImageObserver();
if (observer == null)
{
observer = c;
}
Image image = getImage();
int width = image.getWidth(observer);
int height = image.getHeight(observer);
final Graphics2D g2d = (Graphics2D)g.create(x, y, width, height);
if(isRetina())
{
g2d.scale(0.5, 0.5);
}
else
{
}
g2d.drawImage(image, 0, 0, observer);
g2d.scale(1, 1);
g2d.dispose();
}
I do not know how this will work with multiple screens though- is there anyone else that can help out with that???
Hope this code helps out anyway!
Jason Barraclough.
Here is an example of using the scaling as mentioned above:
RetinaIcon is on the left. ImageIcon is on the right
Here is a solution, that works also when the icons are used in the apple menu. There the icon is automatically greyed. So I have implemented a class DenseIcon which paints densely:
public synchronized void paintIcon(Component c, Graphics g, int x, int y) {
if(getImageObserver() == null) {
g.drawImage(getImage0(), x, y, getIconWidth(), getIconHeight(), c);
} else {
g.drawImage(getImage0(), x, y, getIconWidth(), getIconHeight(), getImageObserver());
}
}
How to hook into the greying I have not yet figured out. So as a kludge we return a low res image so that the menu can do its modifications:
public Image getImage() {
Image image = getImage0().getScaledInstance(
getIconWidth(),
getIconHeight(),
Image.SCALE_SMOOTH);
ImageIcon icon = new ImageIcon(image, getDescription());
return icon.getImage();
}
You find the code of the full class here on gist. You need to instantiate the icon class with an URL to an image that is twice the size. Works for 2K displays.
This how icons look like on my retina macbook '12:
On the left side icons in IntelliJ IDEA 11 (swing app) and on the right side IDEA 12 which is claimed to be retinized. As you can see automatically resized icons (on the left) looks pretty ugly.
As far as I know, they, just like the guys from Chrome team, made it by providing double sized icons.
I'm having an issue using bilinear interpolation for 16 bit data. I have two images, origImage and displayImage. I want to use AffineTransformOp to filter origImage through an AffineTransform into displayImage which is the size of the display area. origImage is of type BufferedImage.TYPE_USHORT_GRAY and has a raster of type sun.awt.image.ShortInterleavedRaster. Here is the code I have right now
displayImage = new BufferedImage(getWidth(), getHeight(), origImage.getType());
try {
op = new AffineTransformOp(atx, AffineTransformOp.TYPE_BILINEAR);
op.filter(origImage, displayImage);
}
catch (Exception e) {
e.printStackTrace();
}
In order to show the error I have created 2 gradient images. One has values in the 15 bit range (max of 32767) and one in the 16 bit range (max of 65535). Below are the two images
15 bit image
16 bit image
These two images were created in identical fashions and should look identical, but notice the line across the middle of the 16 bit image. At first I thought that this was an overflow problem however, it is weird that it's manifesting itself in the center of the gradient instead of at the end where the pixel values are higher. Also, if it was an overflow issue than I would suspect that the 15 bit image would have been affected as well.
Any help on this would be greatly appreciated.
I was just wondering why no one is answering, did I provide enough information? Is more info needed?
Below is the code I use to generate the AffineTransform. All of the referenced variables are calculated based off of user input (mouse movement) and should be correct (it's been tested by a lot of people including myself). Hopefully this can help with the error.
AffineTransform panTranslate = new AffineTransform();
panTranslate.translate(imagePanOffset.x, imagePanOffset.y);
AffineTransform rotateCenterTranslate = new AffineTransform();
rotateCenterTranslate.translate(imageRotateCTR.x, imageRotateCTR.y);
AffineTransform rotateTransform = new AffineTransform();
rotateTransform.rotate(Math.toRadians(rotateValue));
AffineTransform rotateAntiCenterTranslate = new AffineTransform();
rotateAntiCenterTranslate.translate(-imageRotateCTR.x, -imageRotateCTR.y);
AffineTransform translateTransform = new AffineTransform();
translateTransform.translate(imageMagOffset.x, imageMagOffset.y);
AffineTransform flipMatrixTransform = new AffineTransform();
switch (flipState) {
case ENV.FLIP_NORMAL: // NORMAL
break;
case ENV.FLIP_TOP_BOTTOM: // FLIP
flipMatrixTransform.scale(1.0, -1.0);
flipMatrixTransform.translate(0.0, -h);
break;
case ENV.FLIP_LEFT_RIGHT: // MIRROR
flipMatrixTransform.scale(-1.0, 1.0);
flipMatrixTransform.translate(-w, 0.0);
break;
case ENV.FLIP_TOP_BOTTOM_LEFT_RIGHT: // FLIP+MIRROR
flipMatrixTransform.scale(-1.0, -1.0);
flipMatrixTransform.translate(-w, -h);
break;
}
scaleTransform = new AffineTransform();
scaleTransform.scale(magFactor, magFactor);
AffineTransform atx = new AffineTransform();
atx.concatenate(panTranslate);
atx.concatenate(rotateCenterTranslate);
atx.concatenate(rotateTransform);
atx.concatenate(rotateAntiCenterTranslate);
atx.concatenate(translateTransform);
atx.concatenate(flipMatrixTransform);
atx.concatenate(scaleTransform);
I still have no idea what's going on here. I'd really appreciate any help that can be provided. I've also attached an example of the bug happening in a real image that I encounter for more reference.
Here is the bug happening in an X-ray of the hand
Here is a zoomed up version focused on the area between the thumb and first finger.
Note again how the bug doesn't occur on the extremely white areas, but on the values in the middle of the dynamic range, just like in the gradient image.
I've discovered more information. I was adjusting some of the transforms and found that the bug does not occur if I just filter through an identity matrix. It also doesn't occur if I translate by an integer amount. It does occur if I translate by a non integer amount. It also occurs if I zoom by any amount other than 1 (integer or not). Hopefully this helps.
After more experimenting, the bug definitely manifests itself at the boundary pixels between half the max intensity (65535/2 = 32767.5). It also ONLY occurs at this value. I hope this might help diagnosis!!
At the request of AlBlue here is code that is completely independent of my application that can generate the bug. Note that in the original post I included an image gradient generated with the below code however I zoomed in on one of the gradients to better show the effect. You should see the effect four times on the 0.5 translated image and not on either of the other two images. Also note that this bug appears while scaling by any amount other than 1. Just replace AffineTransform.getTranslateInstance() with AffineTransform.getScaleInstance(0.9, 0.9) to see the bug also.
private static class MyJPanel extends JPanel {
BufferedImage displayImage = null;
public MyJPanel(double translateValue) {
super();
BufferedImage bi = new BufferedImage(1024, 1024, BufferedImage.TYPE_USHORT_GRAY);
int dataRange = (int)Math.pow(2, 16);
double step = dataRange/(bi.getRaster().getDataBuffer().getSize()/4.0);
double value = 0;
for (int i=0; i<bi.getRaster().getDataBuffer().getSize(); i++) {
bi.getRaster().getDataBuffer().setElem(i, (int)value);
if (value >= dataRange)
value = 0;
else
value += step;
}
displayImage = new BufferedImage(bi.getWidth(), bi.getHeight(), bi.getType());
AffineTransform tx = AffineTransform.getTranslateInstance(translateValue, translateValue);
AffineTransformOp op = new AffineTransformOp(tx, AffineTransformOp.TYPE_BILINEAR);
op.filter(bi, displayImage);
}
public void paint(Graphics g) {
super.paint(g);
g.drawImage(displayImage, 0, 0, this);
}
}
private static void showDisplayError() {
JDialog dialog1 = new JDialog();
dialog1.setTitle("No Translation");
MyJPanel panel1 = new MyJPanel(0);
dialog1.getContentPane().add(panel1);
dialog1.setSize(1024, 1024);
dialog1.setVisible(true);
JDialog dialog2 = new JDialog();
dialog2.setTitle("Translation of 0.5");
MyJPanel panel2 = new MyJPanel(0.5);
dialog2.getContentPane().add(panel2);
dialog2.setSize(1024, 1024);
dialog2.setVisible(true);
JDialog dialog3 = new JDialog();
dialog3.setTitle("Translation of 1.0");
MyJPanel panel3 = new MyJPanel(1.0);
dialog3.getContentPane().add(panel3);
dialog3.setSize(1024, 1024);
dialog3.setVisible(true);
}
As another update, I just tried this on Fedora 10 and saw the bug is still present.
What version of java (java -version) and OS are you using? It might be a bug in the transform (which has since been fixed) or it might be an error in the rendering to PNG.
Have you tried using a NEAREST_NEIGHBOR filter instead of the BILINEAR one?
You can work around it by applying the transform in a Graphics2D instead of an AffineTransformOp:
if (useG2D) {
Graphics2D g = displayImage.createGraphics();
g.transform(tx);
g.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
RenderingHints.VALUE_INTERPOLATION_BILINEAR);
g.drawImage(bi, null, 0, 0);
} else {
AffineTransformOp op = new AffineTransformOp(tx, AffineTransformOp.TYPE_BILINEAR);
op.filter(bi, displayImage);
}
I don't know why this would give different output, but it does.
Note: useG2D could be a constant or it could be set based on the result of tx.getType(). The bug does not occur with TYPE_QUADRANT_ROTATION, TYPE_FLIP or TYPE_IDENTITY transforms.
Did you solve this? It is likely a being caused by not using the AffineTransformOp correctly. How did you create the AffineTransform atx ? If I have that I should be able to replicate to help debug.
You may wish to have a look at this site too. It contains lots of useful information about AffineTransformOp