views:

394

answers:

2

All,

I'm doing some image manipulation in Scala by making use of BufferedImages and Raster objects. I am attempting to get all the pixels in the buffered image with the following code.

val raster = f.getRaster()

// Preallocating the array causes ArrayIndexOutOfBoundsException .. http://forums.sun.com/thread.jspa?threadID=5297789
// RGB channels;
val pixelBuffer = new Array[Int](width*height*3)
val pixels = raster.getPixels(0,0,width,height,pixelBuffer)

Now, when I read in relatively large files, this works fine. When I read in 20x20 PNG files, I get an ArrayIndexOutOfBoundsException:

java.lang.ArrayIndexOutOfBoundsException: 1200
at sun.awt.image.ByteInterleavedRaster.getPixels(ByteInterleavedRaster.java:1050)

I've read online that the way around this problem is to NOT preallocate the pixelBuffer, but instead to pass in a null value and use the one returned by the Raster.getPixels method.

Here's my problem. When I do the naive approach and just pass Nil as the last argument:

val pixels = raster.getPixels(0,0,width,height,Nil)

I get the error

error: overloaded method value getPixels with alternatives (Int,Int,Int,Int,Array[Double])Array[Double] <and> (Int,Int,Int,Int,Array[Float])Array[Float] <and> (Int,Int,Int,Int,Array[Int])Array[Int] cannot be applied to (Int,Int,Int,Int,Nil.type)
val pixels = raster.getPixels(0,0,width,height,Nil)

So obviously the compiler cannot determine which of the two methods I'm trying to call; it's ambiguous. If I were using Java, I would cast the null to make my intent explicit. I can't quite figure out how to get the same effect in Scala. Things I've tried:

 val pixelBuffer:Array[Int] = Nil // Cannot instantiate an Array to Nil for some reason
 Nil.asInstanceOf(Array[Int]) // asInstanceOf is not a member of Nil

Any idea how to tell the compiler explicitly that I want the method with the Int array as last parameter rather than a Float array?

EDIT: As an answer points out, I was getting Nil mixed up with null. Nil is an empty list. See the following blog post

Also, I should point out that the array out of bounds exception was my fault (as these things often are). The problem was I was assuming that the raster had 3 channels, but my image had 4 channels, since I had created it that way. I instead preallocate the array as follows:

val numChannels = raster.getNumBands() 

val pixelBuffer = new Array[Int](width*height*numChannels)
val pixels = raster.getPixels(minX,minY,width,height,pixelBuffer)

Thanks for the help

+5  A: 

You confused Nil with null.

Dimitris Andreou
Yes I did. - Good point. As I mention in my edit, the following post helped sort me out : http://javaforyou.wordpress.com/2009/07/12/nothingness/
I82Much
+11  A: 

(Assuming you want the question of how to resolve overloads when you need to pass a null):

Just as you would in Java, by ascribing the type corresponding to the overload you wish to invoke (in Java you'd cast, but it amounts to the same thing: an assertion of the static type to assign to the null):

scala> object O { def m(i: Int, s: String): String = s * i; def m(i: Int, l: List[String]): String = l.mkString(":") * i }
defined module O

scala> O.m(23, null)
<console>:7: error: ambiguous reference to overloaded definition,
both method m in object O of type (i: Int,l: List[String])String
and  method m in object O of type (i: Int,s: String)String
match argument types (Int,Null)
       O.m(23, null)
         ^

scala> O.m(23, null: String)
res4: String = nullnullnullnullnullnullnullnullnullnullnullnullnullnullnullnullnullnullnullnullnullnullnull

scala> O.m(23, null: List[String])
java.lang.NullPointerException
        at O$.m(<console>:5)
        at .<init>(<console>:7)
        at .<clinit>(<console>)
        at RequestResult$.<init>(<console>:9)
        at RequestResult$.<clinit>(<console>)
        at RequestResult$scala_repl_result(<console>)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
        at java.lang.reflect.Method.invoke(Method.java:597)
        at scala.tools.nsc.Interpreter$Request$$anonfun$loadAndRun$1$$anonfun$apply$18.apply(Interpreter.scala:981)
        at scala.tools.nsc.Interpreter$Request$$anonfun$loadAndRun$1$$anonfun$apply$18.apply(Interpreter.scala:981)
        at scala.util.control.Exception$Catch.apply(Exception.scala:7...
scala>
Randall Schulz
This is a perfect explanation. Thanks
I82Much
I'd rather you explained that ascribing means chosing which of the possible types you want a value to assume, while casting forces a value into another type, which it may or may not take.
Daniel