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.
