ImageTools plugin

  • Tags: image
  • Latest: 0.1
  • Last Updated: 31 December 2009
  • Grails version: *
  • Authors: null
6 votes
Dependency:
compile ":image-tools:0.1"

Summary

Installation

grails install-plugin http://www.arquetipos.co.cr/blog/files/grails-image-tools-1.0.4.zip

Description

Installation and usage

This is a very basic plugin, that so far encapsulates JAI calls for operations such as image loading, saving, cropping, masking and thumbnail creation.

To get the latest version, you must install the source from GIT and build the plugin yourself

Download here:

https://github.com/ricardojmendez/grails-imagetools/archives/master

Un-(zip/tar) into new directory. Execute the following commands:

cd (new directory)
grails upgrade
grails create-plugin ImageTools
grails package-plugin
dir *.zip (to get the full name of the created plugin)
cd (to your project using the plugin)
grails install-plugin (path to created plugin)

All plugin dependencies - two JAI libraries - will be included. You can also find the plugin repository at its new home on Github.

Once the plugin has been installed in your project you'll have a new class at your disposal, called ImageTool. You can use an instance of this class to load an image, operate on it, and save the result.

import org.grails.plugins.imagetools.*

// To load an image from a buffer def imageTool = new ImageTool() imageTool.load(f.getBytes())

// To load an image from a path imageTool.load("/path/to/image.jpg")

You can save an in-memory snapshot of the image before applying any operations.

imageTool.saveOriginal()

Once loaded, the image can be resized by calling:

imageTool.thumbnail(640)  // With the desired thumbnail size

And then the resulting image can be saved to a file calling

imageTool.writeResult("smaller.640.jpg", "JPEG")

Starting on 1.0.4, it can also be saved as a byte array, courtesy of code sent in by Paul Bowler:

var arr = imageTool.getBytes("JPEG")

An important note is that the loaded image is never explicitly replaced after an operation - instead, a resulting image is created in the object. If you wish to apply more operations to the result, you should swap it with the originally loaded image.

imageTool.swapSource()

There are three functions that you use in order to apply an alpha mask to an image: first you load the alpha and the mask, and then apply them.

imageTool.loadAlpha("alpha.jpg")
imageTool.loadMask("mask.jpg")
imageTool.applyMask()

Easy enough, isn't it? Let's go for a more complex example. Suppose you want to load an image, reduce it to a maximum size of 640, crop it to a square, then create thumbnails in several sizes and apply masks that have the name "Alpha_XYZ.jpg" and "Mask_XYZ.jpg", where XYZ is the thumbnail size. This is how you would do it:

def imageTool = new ImageTool()
imageTool.load("/path/to/image.jpg")
// Reduces the image size
imageTool.thumbnail(640)
// Saves the result
imageTool.writeResult("image_smaller.jpg", "JPEG")
// Crops it to a square
imageTool.square()
/* 
 * Swaps the resulting image with the main one, so that we continue to
 * operate on the square crop.
 */
imageTool.swapSource()
// Creates a copy of the original for later restore
imageTool.saveOriginal()
/* 
 * Iterate through the thumbnail sizes that we want
 *
 */
[178, 133, 69].each {
    def tempName = "image.${it}.jpg"
    /*
     * Creates and saves the thumbnail. It needs to be temporarily saved
     * and re-loaded before applying the masking operation, because of 
     * something that seems to be a JAI glitch.
     * See the following link for details:
     * http://ricardo.strangevistas.net/jai-and-masking-operations.html
     */
    imageTool.thumbnail(it)
    imageTool.writeResult(tempName, "JPEG")
    imageTool.load(tempName)
    // Loads the alpha and mask, and applies them
    imageTool.loadAlpha("Alpha_${it}.jpg")
    imageTool.loadMask("Mask_${it}.jpg")

imageTool.applyMask() // Finally, save it and restore the original so that we can continue // to operate on the unmodified image imageTool.writeResult(tempname, "JPEG") imageTool.restoreOriginal() }

That's all for now, folks. Grab me on the Grails list or at ricardo at arquetipos dot co dot cr if you need any more pointers. Good luck!

FAQs

mediaLib accelerator

ImageTools uses JAI for its image-handling operations. JAI can use an optional native library for extra performance, or can work in pure Java mode, but if the native library is not present you might get an exception like the following:.

Error: Could not find mediaLib accelerator wrapper classes. Continuing in pure Java mode
Occurs in: com.sun.media.jai.mlib.MediaLibAccessor
java.lang.NoClassDefFoundError: com/sun/medialib/mlib/Image
at com.sun.media.jai.mlib.MediaLibAccessor$1.run(MediaLibAccessor.java:248)
This only means that the native libraries weren't found, and you can still use the ImageTools plugin like you would expect. To see how to get rid of this error, or learn more about JAI's native support, read this thread.

Retrieving image from domain class property

To save an image as a property on a domain class and return the image or a thumbnail to your gsp

// domain
class LogFile {
	String name
	String description
	Date createdDate
	Date lastUpdated
	byte[] myImage
    static constraints = {
    }
}

// controller

def getImageThumbnail = { def logFile = LogFile.get(params.id) assert logFile != null println "Retrieved image size ${logFile.myImage.length}" def imageTool = new org.grails.plugins.imagetools.ImageTool() imageTool.load(logFile.myImage) imageTool.thumbnail(125) def thumbImage = imageTool.getBytes("JPEG") println "Returning thumb size ${thumbImage.length}" response.contentType = "image/jpeg" response.contentLength = thumbImage.length response.outputStream.write(thumbImage) }

def getImage = { def logFile = LogFile.get(params.id) println "Displaying image size ${logFile.myImage.length}" response.contentType = "image/jpeg" response.contentLength = logFile?.myImage.length response.outputStream.write(logFile?.myImage) }

// gsp
<tr class="prop">
  <td valign="top" class="name"><g:message code="logFile.myImage.label" default="Image" /></td>
    <td>
      <img src="${createLink(action:'getImage', id:logFileInstance?.id)}" />
  </td> 	                           
</tr>

// create some instances in BootStrap with images in grails-app/conf/resources

import org.apache.commons.io.IOUtils import org.codehaus.groovy.grails.commons.ConfigurationHolder import grails.util.GrailsUtil import org.ums.LogFile

def init = { servletContext -> println "**** BootStrap; GrailsUtil.environment: ${GrailsUtil.environment}" if (!LogFile.count()) { def newLog = new LogFile(name:"Sample1",description:"a desc",createdDate:new Date(), lastUpdated:new Date()) newLog.myImage = getFile("console") if( !newLog.save(flush:true) ) { newLog.errors.each { println it } } new LogFile(name:"New log 1",description:"a desc",createdDate:new Date(), lastUpdated:new Date(), myImage:getFile("grails")).save(flush:true) new LogFile(name:"New log 2",description:"a desc",createdDate:new Date(), lastUpdated:new Date(), myImage:getFile("groovy")).save(flush:true) new LogFile(name:"New log 3",description:"a desc",createdDate:new Date(), lastUpdated:new Date(), myImage:getFile("logging")).save(flush:true) }

def getFile(String fileName) { def filePath = "resources/${fileName}.png" println "Importing ${filePath}" InputStream is = this.getClass().getResourceAsStream(filePath) byte[] image = IOUtils.toByteArray(is) println "File size ${image.size()}" return image }