JAI and masking operations

Posted on Thursday 26 July 2007

I just spent a few hours tearing my hair out at an peculiar behavior with JAI, and thought to briefly document it in case anybody ran into a similar situation.

I started off from the code on this relatively old article, adapting it to my needs on Groovy. The intention was to:

  1. Get an image
  2. Crop it to a square
  3. Generate a thumbnail
  4. Add a mask to the thumbnail

The original test code we wrote work perfectly. The mask function was (with control output added):


public applyMask()
{
println("ON APPLY MASK")
println("Mask ${mask.getWidth()} x ${mask.getHeight()}")
println("Alpha ${alpha.getWidth()} x ${alpha.getHeight()}")
println("Image ${image.width} x ${image.height}")

ParameterBlock params = new ParameterBlock();
params.addSource(mask);
params.addSource(image);
params.add(alpha);
params.add(null);
params.add(new Boolean(false));
result = JAI.create("composite", params, null);

println("Result ${result.width} x ${result.height}")
}

Thumbnails were generated with:


def d = new ImageUtils()
d.load("cropped.jpg")

[178, 133, 69].each {
d.thumbnail(it)
println "Created thumbnail"
d.writeResult("Thumb_${it}.jpg", "JPEG")
}

And having generated them, we iterated through them with:


def e = new ImageUtils()

[178, 133, 69].each {
e.load("Thumb_${it}.jpg")
e.loadMask("Mask_${it}.jpg")
e.loadAlpha("Alpha_${it}.jpg")
e.applyMask()
println "Created Masked thumbnail"
e.writeResult("Masked_${it}.jpg", "JPEG")
}

to apply the corresponding masks.

Turns out that when the code was optimized to just handle everything in memory, without an intermediate save, the resulting image ended up missing a few pixels in all sizes. Executing


[178, 133, 69].each {
c.thumbnail(it)
println "Created thumbnail"
c.swapSource() // Assigns the result RenderedOp to copy to work from
c.loadMask("Mask_${it}.jpg")
c.loadAlpha("Alpha_${it}.jpg")
c.applyMask()
println "Created Masked thumbnail"
c.writeResult("Masked_${it}.jpg", "JPEG")
c.restoreOriginal()
}

gave us results like:

Mask 133 x 133
Alpha 133 x 133
Image 133 x 133
Result 133 x 128

This seems to be a JAI bug when working completely from memory RenderedOp sources. Saving a temporary thumbnail and loading it again before applying the mask works just fine.


[178, 133, 69].each {
c.thumbnail(it)
println "Created thumbnail"
c.writeResult("Thumb_${it}.jpg", "JPEG")
c.load("Thumb_${it}.jpg")
c.loadMask("Mask_${it}.jpg")
c.loadAlpha("Alpha_${it}.jpg")
c.applyMask()
}


1 Comment for 'JAI and masking operations'

  1.  
    March 11, 2008 | 11:26 pm
     

    […] of the documentation will remain at the Grails wiki. You probably should also read my post on JAI and masking operations if you intend to use it to mask […]

Leave a comment

(required)

(required)


Information for comment users
Line and paragraph breaks are implemented automatically. Your e-mail address is never displayed. Please consider what you're posting.

Use the buttons below to customise your comment.


RSS feed for comments on this post | TrackBack URI