Hi Ross,

That looks good.  It seems you're doing a much better job of handling
gif than I would have.  Maybe we should move this discussion to the
main list to get everyone's input on what features would be useful.

Tim, If you've already created a branch, let me know and I'll get my
stuff in there too-- My resize code uses the sanselan project to read
exif orientation information.

On Fri, Jan 15, 2010 at 10:33 AM, Ross Mellgren <[email protected]> wrote:
> Well, it has to keep the whole source (packed) in memory and the target 
> (unpacked / 32 bit RGBA) in memory, so I would assume that as long as 
> AffineTransformOp is not doing something untoward it's probably around (src 
> width * src height * src bytes/pixel) + (dest width * dest height * 4 bytes) 
> plus a little bit extra.
>
> I'm not a Java2D guru or anything, so I'm not sure how that could be improved 
> offhand.
>
> If you want to wrap it up and put it in, that'd be awesome!
>
> -Ross
>
> On Jan 15, 2010, at 10:17 AM, Timothy Perrett wrote:
>
>> Cool stuff Ross, whats the overhead like in terms of memory etc?
>>
>> I might have a bit of time to put this into a module and stuff it on review 
>> board.
>>
>> Cheers, Tim
>>
>> On 15 Jan 2010, at 15:03, Ross Mellgren wrote:
>>
>>> 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 <[email protected]> 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 [email protected].
>>>> To unsubscribe from this group, send email to 
>>>> [email protected].
>>>> 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 [email protected].
>>> To unsubscribe from this group, send email to 
>>> [email protected].
>>> For more options, visit this group at 
>>> http://groups.google.com/group/liftweb?hl=en.
>>>
>>>
>>
>> --
>> You received this message because you are subscribed to the Google Groups 
>> "Lift" group.
>> To post to this group, send email to [email protected].
>> To unsubscribe from this group, send email to 
>> [email protected].
>> For more options, visit this group at 
>> http://groups.google.com/group/liftweb?hl=en.
>>
>>
>
>
> --
> You received this message because you are subscribed to the Google Groups 
> "Lift" group.
> To post to this group, send email to [email protected].
> To unsubscribe from this group, send email to 
> [email protected].
> For more options, visit this group at 
> http://groups.google.com/group/liftweb?hl=en.
>
>
>
>
-- 
You received this message because you are subscribed to the Google Groups 
"Lift" group.
To post to this group, send email to [email protected].
To unsubscribe from this group, send email to 
[email protected].
For more options, visit this group at 
http://groups.google.com/group/liftweb?hl=en.


Reply via email to