Riven
|
 |
«
Posted
2009-10-26 14:47:18 » |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108
| public static Dimension getImageDimension(File file) throws IOException { RandomAccessFile raf = new RandomAccessFile(file, "r"); try { int header = raf.readUnsignedShort();
if (header == 0x8950) { raf.seek(8 + 4 + 4);
return new Dimension(raf.readInt(), raf.readInt()); }
if (header == 0xffd8) {
} else if (header == 0x424D) { raf.seek(0x0012);
int w = raf.read() | (raf.read() << 8) | (raf.read() << 16) | (raf.read() << 24); int h = raf.read() | (raf.read() << 8) | (raf.read() << 16) | (raf.read() << 24); return new Dimension(w, h); } else if (header == (('G' << 8) | ('I' << 0))) { raf.seek(0x0006); int w = raf.read() | (raf.read() << 8); int h = raf.read() | (raf.read() << 8); return new Dimension(w, h); } else { throw new IllegalStateException("unexpected header: " + Integer.toHexString(header)); }
while (true) { int marker = raf.readUnsignedShort();
switch (marker) { case 0xffd8: case 0xffd0: case 0xffd1: case 0xffd2: case 0xffd3: case 0xffd4: case 0xffd5: case 0xffd6: case 0xffd7: case 0xffd9: break;
case 0xffdd: raf.readUnsignedShort(); break;
case 0xffe0: case 0xffe1: case 0xffe2: case 0xffe3: case 0xffe4: case 0xffe5: case 0xffe6: case 0xffe7: case 0xffe8: case 0xffe9: case 0xffea: case 0xffeb: case 0xffec: case 0xffed: case 0xffee: case 0xffef: case 0xfffe: case 0xffdb: case 0xffc4: case 0xffda: raf.readFully(new byte[raf.readUnsignedShort() - 2]); break;
case 0xffc0: case 0xffc2: raf.readUnsignedShort(); raf.readByte(); int height = raf.readUnsignedShort(); int width = raf.readUnsignedShort(); return new Dimension(width, height);
default: throw new IllegalStateException("invalid jpg marker: " + Integer.toHexString(marker)); } } } finally { raf.close(); } } } |
|
Hi, appreciate more people! Σ ♥ = ¾ Learn how to award medals... and work your way up the social rankings!
|
|
|
Riven
|
 |
«
Reply #1 - Posted
2009-10-26 15:33:40 » |
|
This version works on InputStreams too, which is required for loading your images through a classloader. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112
| public static Dimension getImageDimension(File file) throws IOException { return ImageUtil.getImageDimension(new FileInputStream(file)); }
public static Dimension getImageDimension(InputStream in) throws IOException { DataInputStream dis = new DataInputStream(in);
try { int header = dis.readUnsignedShort();
if (header == 0x8950) { dis.readFully(new byte[(8 - 2) + 4 + 4]);
return new Dimension(dis.readInt(), dis.readInt()); }
if (header == 0xffd8) {
} else if (header == 0x424D) { dis.readFully(new byte[16]);
int w = dis.read() | (dis.read() << 8) | (dis.read() << 16) | (dis.read() << 24); int h = dis.read() | (dis.read() << 8) | (dis.read() << 16) | (dis.read() << 24); return new Dimension(w, h); } else if (header == (('G' << 8) | ('I' << 0))) { dis.readFully(new byte[4]); int w = dis.read() | (dis.read() << 8); int h = dis.read() | (dis.read() << 8); return new Dimension(w, h); } else { throw new IllegalStateException("unexpected header: " + Integer.toHexString(header)); }
while (true) { int marker = dis.readUnsignedShort();
switch (marker) { case 0xffd8: case 0xffd0: case 0xffd1: case 0xffd2: case 0xffd3: case 0xffd4: case 0xffd5: case 0xffd6: case 0xffd7: case 0xffd9: break;
case 0xffdd: dis.readUnsignedShort(); break;
case 0xffe0: case 0xffe1: case 0xffe2: case 0xffe3: case 0xffe4: case 0xffe5: case 0xffe6: case 0xffe7: case 0xffe8: case 0xffe9: case 0xffea: case 0xffeb: case 0xffec: case 0xffed: case 0xffee: case 0xffef: case 0xfffe: case 0xffdb: case 0xffc4: case 0xffda: dis.readFully(new byte[dis.readUnsignedShort() - 2]); break;
case 0xffc0: case 0xffc2: dis.readUnsignedShort(); dis.readByte(); int height = dis.readUnsignedShort(); int width = dis.readUnsignedShort(); return new Dimension(width, height);
default: throw new IllegalStateException("invalid jpg marker: " + Integer.toHexString(marker)); } } } finally { dis.close(); } } |
|
Hi, appreciate more people! Σ ♥ = ¾ Learn how to award medals... and work your way up the social rankings!
|
|
|
Abuse
|
 |
«
Reply #2 - Posted
2009-10-26 17:11:10 » |
|
Wouldn't it be nice if DataInput defined methods for reading primitives in LSB order as well as Java's chosen convention of MSB.
It seems such a simple yet incredibly useful thing to have missed off; and given the choice of zip for code package distribution it must have been an I/O necessity from the very beginning! (DEFLATE stores block lengths in LSB order)
|
|
|
|
Games published by our own members! Check 'em out!
|
|
Riven
|
 |
«
Reply #3 - Posted
2009-10-26 21:31:12 » |
|
Yup. Judging by how the naming convetions at Sun are (LayoutManager => LayoutManager2, NIO => NIO2) we could expect DataInput2 any day now.
|
Hi, appreciate more people! Σ ♥ = ¾ Learn how to award medals... and work your way up the social rankings!
|
|
|
CaptainJester
|
 |
«
Reply #4 - Posted
2009-10-27 12:17:33 » |
|
Wouldn't it be nice if DataInput defined methods for reading primitives in LSB order as well as Java's chosen convention of MSB.
It seems such a simple yet incredibly useful thing to have missed off; and given the choice of zip for code package distribution it must have been an I/O necessity from the very beginning! (DEFLATE stores block lengths in LSB order)
You could use a FileChannel. Then when you create your ByteBuffer for reading from the channel you set the order to ByteOrder.LITTLE_ENDIAN or ByteOrder.BIG_ENDIAN. Then from the ByteBuffer getFloat,getInt,getLong etc...
|
|
|
|
Riven
|
 |
«
Reply #5 - Posted
2009-10-27 12:35:44 » |
|
Yeah... but I'm working with InputStreams...
|
Hi, appreciate more people! Σ ♥ = ¾ Learn how to award medals... and work your way up the social rankings!
|
|
|
CommanderKeith
|
 |
«
Reply #6 - Posted
2009-10-28 12:24:49 » |
|
This is very useful, thanks. May I ask, what are you using it for?
|
|
|
|
Riven
|
 |
«
Reply #7 - Posted
2009-10-28 13:47:25 » |
|
I have this webservice, that must scale huge images (8K * 4K pixels) to thumbnails. I use this code to determine whether the webservice (which has a small heap to keep FullGC down to a minimum) can handle it, or it should launch a new JVM to do it -- which might run out of memory, without taking down the webservice.
|
Hi, appreciate more people! Σ ♥ = ¾ Learn how to award medals... and work your way up the social rankings!
|
|
|
Abuse
|
 |
«
Reply #8 - Posted
2009-10-28 19:54:05 » |
|
I have this webservice, that must scale huge images (8K * 4K pixels) to thumbnails. I use this code to determine whether the webservice (which has a small heap to keep FullGC down to a minimum) can handle it, or it should launch a new JVM to do it -- which might run out of memory, without taking down the webservice.
Sounds like what you really want is a progressive decoder for all of said image formats 
|
|
|
|
Riven
|
 |
«
Reply #9 - Posted
2009-10-28 21:32:19 » |
|
Indeed. Me lazy. BMP and PNG (RGB24/ARGB32) are trivial. JPG not so simple. JAI did not do it (dispite the promise), and I didn't want to spend more time on it, so this was a quick hack, that will probably be used for a few years. 
|
Hi, appreciate more people! Σ ♥ = ¾ Learn how to award medals... and work your way up the social rankings!
|
|
|
Games published by our own members! Check 'em out!
|
|
CaptainJester
|
 |
«
Reply #10 - Posted
2009-10-29 14:49:36 » |
|
Yeah... but I'm working with InputStreams...
Feed the bytes into a ByteBuffer yourself. Then you can still take advantage of the ByteOrder of a ByteBuffer.
|
|
|
|
CommanderKeith
|
 |
«
Reply #11 - Posted
2009-10-29 16:46:27 » |
|
I have this webservice, that must scale huge images (8K * 4K pixels) to thumbnails. I use this code to determine whether the webservice (which has a small heap to keep FullGC down to a minimum) can handle it, or it should launch a new JVM to do it -- which might run out of memory, without taking down the webservice.
Interesting. Clever solution. How annoying that JAI api can't do it.
|
|
|
|
Riven
|
 |
«
Reply #12 - Posted
2009-10-29 18:00:03 » |
|
Feed the bytes into a ByteBuffer yourself. Then you can still take advantage of the ByteOrder of a ByteBuffer.
And the resulting code would have been... better?
|
Hi, appreciate more people! Σ ♥ = ¾ Learn how to award medals... and work your way up the social rankings!
|
|
|
pjt33
|
 |
«
Reply #13 - Posted
2009-10-30 00:22:05 » |
|
Indeed. Me lazy. BMP and PNG (RGB24/ARGB32) are trivial. JPG not so simple. JAI did not do it (dispite the promise), and I didn't want to spend more time on it, so this was a quick hack, that will probably be used for a few years.  JAI does it for TIFFs - I've used it to process a 22k x 22k image without exceeding 64MB heap. Not sure how you were going about it, but what I used was 1 2 3 4 5 6 7 8 9 10 11
| ImageReader ir=ImageIO.getImageReadersByFormatName("tiff").next(); ImageInputStream iis=new FileImageInputStream(new RandomAccessFile(file,"r")); ir.setInput(iis); for (y) { for (x) { ImageReadParam irp=new ImageReadParam(); irp.setSourceRegion(new Rectangle(x,y,w,h)); BufferedImage bi=ir.read(0,irp); } } |
If Sun's JPG reader doesn't handle that then there must be a third party one which does, although finding and getting it approved for use will, of course, be much hassle.
|
|
|
|
Riven
|
 |
«
Reply #14 - Posted
2009-10-30 09:02:13 » |
|
Thanks, turned out I used JAI totally different, and appearantly that resulted in loading the whole image. Anyway, I got it to work with JPG (trivial, with your code sample), but man.. it's slow! the image is 8K*4K ImageIO.read(new File("something.jpg")); // 3secthe code below takes almost 40 (!!) seconds. Just the loading of the tiles, not even scaling! So I'm going to stick with my current (external JVM) solution. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| ImageReader ir = ImageIO.getImageReadersByFormatName("jpeg").next(); ImageInputStream iis = new FileImageInputStream(new RandomAccessFile(file, "r")); ir.setInput(iis);
int w = ir.getWidth(0); int h = ir.getHeight(0);
int dim = 512;
for (int y = 0; y < h - dim; y += dim) { for (int x = 0; x < w - dim; x += dim) { ImageReadParam irp = new ImageReadParam(); irp.setSourceRegion(new Rectangle(x, y, dim, dim)); BufferedImage bi = ir.read(0, irp); bi.flush(); } } |
|
Hi, appreciate more people! Σ ♥ = ¾ Learn how to award medals... and work your way up the social rankings!
|
|
|
|