9

I'm trying to get a BufferedImage from raw samples, but I get exceptions about trying to read past the available data range which I just don't understand. What I'm trying to do is:

val datasize = image.width * image.height
val imgbytes = image.data.getIntArray(0, datasize)
val datamodel = new SinglePixelPackedSampleModel(DataBuffer.TYPE_INT, image.width, image.height, Array(image.red_mask.intValue, image.green_mask.intValue, image.blue_mask.intValue))
val buffer = datamodel.createDataBuffer
val raster = Raster.createRaster(datamodel, buffer, new Point(0,0))
datamodel.setPixels(0, 0, image.width, image.height, imgbytes, buffer)
val newimage = new BufferedImage(image.width, image.height, BufferedImage.TYPE_INT_RGB)
newimage.setData(raster)

Unfortunately I get:

Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 32784
    at java.awt.image.SinglePixelPackedSampleModel.setPixels(SinglePixelPackedSampleModel.java:689)
    at screenplayer.Main$.ximage_to_swt(Main.scala:40)
    at screenplayer.Main$.main(Main.scala:31)
    at screenplayer.Main.main(Main.scala)

The data is standard RGB with 1 byte padding (so that 1 pixel == 4 bytes) and the image size is 1366x24 px.


I finally got the code to run with the suggestion below. The final code is:

val datasize = image.width * image.height
val imgbytes = image.data.getIntArray(0, datasize)

val raster = Raster.createPackedRaster(DataBuffer.TYPE_INT, image.width, image.height, 3, 8, null)
raster.setDataElements(0, 0, image.width, image.height, imgbytes)

val newimage = new BufferedImage(image.width, image.height, BufferedImage.TYPE_INT_RGB)
newimage.setData(raster)

If it can be improved, I'm open to suggestions of course, but in general it works as expected.

viraptor
  • 33,322
  • 10
  • 107
  • 191

1 Answers1

10

setPixels assumes that the image data is not packed. So it's looking for an input of length image.width*image.height*3, and running off the end of the array.

Here are three options for how to fix the problem.

(1) Unpack imgbytes so it is 3x longer, and do it the same way as above.

(2) Manually load the buffer from imgbytes instead of using setPixels:

var i=0
while (i < imgbytes.length) {
  buffer.setElem(i, imgbytes(i))
  i += 1
}

(3) Don't use createDataBuffer; if you already know that your data has the proper formatting you can create the appropriate buffer yourself (in this case, a DataBufferInt):

val buffer = new DataBufferInt(imgbytes, imgbytes.length)

(you may need to do imgbytes.clone if your original copy could get mutated by something else).

Rex Kerr
  • 166,841
  • 26
  • 322
  • 407
  • I tried those solutions, but either I get unrelated garbage, or a black screen. The data is correct if I look at the hex dump of it. Could you provide a short fragment that actually goes from imgbytes to BufferedImage? It would be really appreciated :) – viraptor Nov 26 '10 at 19:21
  • If you can provide code that creates `image` in your example, sure. Or if you don't care how the data comes in, I'll create an example that isn't from an image. – Rex Kerr Nov 26 '10 at 20:22
  • That's a custom object I get straight from Xorg. If you can do it with known `height`, `width` and an array of either 4 byte components (RGB order + 1byte padding), or single int (same format) per each pixel, I can figure out the rest. The part where I fail is probably the buffer<->raster interaction (I'm not even sure if both are needed). Thanks a lot. – viraptor Nov 26 '10 at 20:31
  • Ok - I got it to work (code included in the question). Unless this can be made simpler somehow - feel free to correct :) – viraptor Nov 26 '10 at 22:28
  • After two weeks of trying to find some sort of information like this, I'm very very happy to find this question. Thanks for the question and answer! – OzBarry Sep 20 '12 at 18:51