According to Jon on the call, he said he was putting together just such a lift 
module (for image stuff), so I figured I'd toss this over in case it was of use 
to him in that module.

If anyone else wants to use it independently, consider it a contribution to 
lift, and licensed the same way.

-Ross

On Jan 15, 2010, at 9:51 AM, Peter Robinett wrote:

> Ross, this looks nice. Imagine resizing code is something that I've
> personally had to do many times and is always annoying, so perhaps
> this would make a good Lift module? Anyway, this is probably best
> discussed on the main list...
> 
> Peter
> 
> On Jan 14, 3:01 am, Ross Mellgren <dri...@gmail.com> wrote:
>> Oh I nearly forgot I said on the conference call the other day that I'd send 
>> the image resize code we built at work, in case it would be helpful. Here it 
>> is:
>> 
>> import java.awt.{Graphics, RenderingHints, Transparency}
>> import java.awt.geom.AffineTransform
>> import java.awt.image.{AffineTransformOp, BufferedImage, ColorModel, 
>> IndexColorModel}
>> 
>> /**
>>  * Helpers for manipulating images
>>  */
>> object ImageHelpers
>> {
>> // Some code here omitted -- Ed.
>> 
>>     /** Rendering hints set up for the highest quality rendering */
>>     val highQualityHints = {
>>         val h = new RenderingHints(null)
>>         h.put(RenderingHints.KEY_ALPHA_INTERPOLATION, 
>> RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY)
>>         h.put(RenderingHints.KEY_COLOR_RENDERING, 
>> RenderingHints.VALUE_COLOR_RENDER_QUALITY)
>>         h.put(RenderingHints.KEY_INTERPOLATION, 
>> RenderingHints.VALUE_INTERPOLATION_BICUBIC)
>>         h.put(RenderingHints.KEY_ANTIALIASING, 
>> RenderingHints.VALUE_ANTIALIAS_ON)
>>         h.put(RenderingHints.KEY_RENDERING, 
>> RenderingHints.VALUE_RENDER_QUALITY)
>>         h
>>     }
>> 
>> // Some code here omitted -- Ed.
>> 
>>     /**
>>      * Resize an image of the given source type by the given ratios, 
>> properly handling GIF transparency, giving back the resized
>>      * image and the new image format type that should be used.
>>      *
>>      * The image type might change if the input type is an indexed color 
>> model, because it is a hard problem to choose an optimized
>>      * palette, and currently we don't. This function will return "png" as 
>> the new type in this case.
>>      *
>>      * If the input image is not using an indexed color model with 
>> transparency, then the target format and color model will be
>>      * identical to the source.
>>      */
>>     def resize(source: BufferedImage, inputFormat: String, dx: Double, dy: 
>> Double): (BufferedImage, String) = {
>>         var sourceColorModel = source.getColorModel
>>         val targetColorModel = source.getColorModel
>>         val standardColorModel = ColorModel.getRGBdefault
>> 
>>         val (targetWidth, targetHeight) = (((source.getWidth: Double) * 
>> dx).asInstanceOf[Int], ((source.getHeight: Double) * dy).asInstanceOf[Int])
>> 
>>         def resize(src: BufferedImage, dst: BufferedImage) {
>>             val g = dst.createGraphics
>>             try {
>>                 g.setRenderingHints(highQualityHints)
>>                 g.drawImage(src, new 
>> AffineTransformOp(AffineTransform.getScaleInstance(dx, dy), 
>> AffineTransformOp.TYPE_BICUBIC), 0, 0)
>>             } finally {
>>                 g.dispose
>>             }
>>         }
>> 
>>         // GIF support in Java is very ornery. For GIFs we have to manually 
>> do the masking on input, and then just punt on outputting GIFs and instead 
>> output PNGs.
>>         if (sourceColorModel.isInstanceOf[IndexColorModel] &&
>>             sourceColorModel.hasAlpha &&
>>             sourceColorModel.getTransparency == Transparency.BITMASK &&
>>             
>> sourceColorModel.asInstanceOf[IndexColorModel].getTransparentPixel >= 0) {
>> 
>>             val indexColorModel = 
>> sourceColorModel.asInstanceOf[IndexColorModel]
>>             val transparent = 
>> indexColorModel.getRGB(indexColorModel.getTransparentPixel)
>> 
>>             val masked = new BufferedImage(standardColorModel, 
>> standardColorModel.createCompatibleWritableRaster(source.getWidth, 
>> source.getHeight), standardColorModel.isAlphaPremultiplied, null)
>>             var w = masked.getWidth
>>             var h = masked.getHeight
>> 
>>             val buf  = new Array[Int](w)
>> 
>>             var y = 0
>>             while (y < h) {
>>                 source.getRGB(0, y, w, 1, buf,  0, 1)
>> 
>>                 var x = 0
>>                 while (x < w) {
>>                     val c = buf(x)
>>                     if (c == transparent) {
>>                         buf(x) = 0
>>                     }
>>                     x += 1
>>                 }
>> 
>>                 masked.setRGB(0, y, w, 1, buf, 0, 1)
>>                 y += 1
>>             }
>> 
>>             val resized = new BufferedImage(standardColorModel, 
>> standardColorModel.createCompatibleWritableRaster(targetWidth, 
>> targetHeight), standardColorModel.isAlphaPremultiplied, null)
>>             resize(masked, resized)
>>             (resized, "png")
>>         } else if (sourceColorModel.isInstanceOf[IndexColorModel]) {
>>             // The input color model is indexed, and we know we won't be 
>> able to generate a tolerable palette to make another indexed color model, so 
>> use sRGB and upgrade to PNG.
>>             val resized = new BufferedImage(standardColorModel, 
>> standardColorModel.createCompatibleWritableRaster(targetWidth, 
>> targetHeight), standardColorModel.isAlphaPremultiplied, null)
>>             resize(source, resized)
>>             (resized, "png")
>>         } else {
>>             val resized = new BufferedImage(targetColorModel, 
>> targetColorModel.createCompatibleWritableRaster(targetWidth, targetHeight), 
>> targetColorModel.isAlphaPremultiplied, null)
>>             resize(source, resized)
>>             (resized, inputFormat)
>>         }
>>     }
>> 
>> }
>> 
>> It isn't perhaps the ideal implementation, as mentioned in some of the 
>> comments, but perhaps Jon or others might find it useful in whole or part.
>> 
>> -Ross
> -- 
> You received this message because you are subscribed to the Google Groups 
> "Lift-committers" group.
> To post to this group, send email to lift-committ...@googlegroups.com.
> To unsubscribe from this group, send email to 
> lift-committers+unsubscr...@googlegroups.com.
> For more options, visit this group at 
> http://groups.google.com/group/lift-committers?hl=en.
> 
> 

-- 
You received this message because you are subscribed to the Google Groups 
"Lift" group.
To post to this group, send email to lift...@googlegroups.com.
To unsubscribe from this group, send email to 
liftweb+unsubscr...@googlegroups.com.
For more options, visit this group at 
http://groups.google.com/group/liftweb?hl=en.


Reply via email to