I've added the code here:

http://github.com/dpp/liftweb/tree/wip_tim_285

Cheers, Tim

On Jan 15, 8:52 pm, Ross Mellgren <[email protected]> wrote:
> This is already on the main list, I moved it there after Peter mentioned it. 
> Sorry, I have a bad habit of starting conversations on lift-committers I'm 
> trying to break before David breaks it for me ;-)
>
> GIF resizing was a huge sticking point for the project here at Paytronix that 
> this code was originally written for. I'd prefer it if all resize transforms 
> could resize into the same format as the source (right now it's GIF -> PNG, 
> and most everything else is source type -> dest type), but:
>   - Shrunk GIFs are ugly due to the limited color palette and single-bit 
> transparency.
>   - Calculating an ideal palette and doing any dithering is not trivial, and 
> for the application I had it was fine to change the format.
>
> -Ross
>
> On Jan 15, 2010, at 3:47 PM, Jonathan Hoffman wrote:
>
>
>
> > 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 
> >>>>> athttp://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 
> >>>> athttp://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 
> >>> athttp://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 
> >> athttp://groups.google.com/group/liftweb?hl=en.
>
> > --
> > You received this message because you are subscribed to the
>
> ...
>
> read more »
-- 
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