Revision: 5841 Author: [email protected] Date: Thu Jul 30 11:19:40 2009 Log: Add support for animated images to ImageResource. This specifically enables using @sprite with animated .gif files.
Patch by: bobv Review by: jgw http://code.google.com/p/google-web-toolkit/source/detail?r=5841 Added: /trunk/user/test/com/google/gwt/resources/client/animated.gif Modified: /trunk/user/src/com/google/gwt/resources/client/ImageResource.java /trunk/user/src/com/google/gwt/resources/client/impl/ImageResourcePrototype.java /trunk/user/src/com/google/gwt/resources/rg/ImageBundleBuilder.java /trunk/user/src/com/google/gwt/resources/rg/ImageResourceGenerator.java /trunk/user/test/com/google/gwt/resources/client/ImageResourceTest.java ======================================= --- /dev/null +++ /trunk/user/test/com/google/gwt/resources/client/animated.gif Thu Jul 30 11:19:40 2009 @@ -0,0 +1,69 @@ +GIF89a ô ÿÿÿ ÿððþŠŠþààþFFþzzþ ÿXXþ$$þ¬¬þ¾¾þ þœœþ þ66þhhþ !ÿ NETSCAPE2.0 !þ Created with ajaxload.info !ù + , w !å¨ DB ÇA «H +‰àȬ ³Áa° ¦D‚ æ@ ^¶ AéXø p...@ñ¸"Uƒ‚³Q# ÎáB \;ŸÍ Ã1ª oÏ:2$ v @ +$|,3 + + ‚_# +d €5 3—" s5 e! !ù + , v i...@e9Žda ÉA Œ²ŠÄñÀ /« `ph$ Ca%@ ŒÇépH© °x½F ÂuS‰ ƒ x# + + Â.¿Ý„†Y fŽ L _" +p +3BƒW ˆ]|L +\6’{|zš8 7[7! !ù + , x Ù e9Ž D E" ²Š„ƒÀ2r,« qPÄ ‡ € j´Â`ð8 ë Â@ 8b H , *Ñâ0 -¦ +ðmFW îä9½LP¤E3+ + (‚ B" + f {ˆ*BW_/‰ +...@_$‰‚~kr 7Ar7! !ù + , v Ù4e9Ž„! Hñ"Ë* ÐQ /@ƒ±ˆ- 4€ ép 4 ŒR+÷¼- Ÿèµp ȧ`ÑP(–6 ƒá ðU/ü *, „) (+/]"lO +/†*Ak‘ “Š K”ŒŠ]A~66 6! !ù + , l i e9Ž " Çñ +Ë* ‡ ½¾ -Ö80H ‚±€=N;¡ ÐÊ T„EìÐ ½ ® îq è¤í±ež êUoK2_WZòÝŒV‰´1jgW e...@tuh//w`? +‰f~#’‰–6”“#! !ù + , ~ ¹,e9Ž‚" ƒñ Ä* +†; pR³% „°#0 ¤š`¡ À 'Ãc™(¤ ”J@@ + +¿ Áµ /1Ái 4 ˆÂ`½V ‰ Bâ¾V +u}„"c aNi/ ] )) - Lel mi} +me[+! !ù + , y I e9ŽÂ" Mó6Ä*¨"7EÍ–„ @G((L&Ô...@z +§‰ùº „...@ w¬Z) „pl ( + ‡ ÔŽ qõu* R&c ` ))( s _J ˆ>_\ ' Gm7 Œ$+! !ù + , w I e9Ž *,‹ (Ä*¾(üB5[ 1 ² ¥Z²ÓIah! G—ªex z²ìj0ˆe¿6�...@v| U« ñ4º¶Dm² +%$ Í›ëp + \ G x +} @+ | =+ + 1“- Ea5 l)+! !ù + , y )œä¨ž'AœK©’¯àÚ ,¢¶ý “‰E\(lƒœ©&;5 à 5 D‰Ä €0è +³3‚a¬0- ‹ µ- + Ñ +¡À ” ŽÃƒpH4 V % +i +p[R"| Œ ‘# +™ 6 iZwcw*! !ù + , y )œä¨ž,K”*ù¶Ä ‹¨0Ÿ aš;׋а Y8 b`4én¤ + ¨Bb ‚b»x +¾ ,±ÁÔ ¾‘ ±Ë ¾Í äÑ( Ƚ° + % +> + +2* Š i* / : ™+$ v*! !ù + , u )œä¨žl[ª$á +²J q[£Âq 3™`Q[ø 5 ø: Š•ð IX!0ÀrAD8 Cv« É ÜHPfi ¾äi Q” �...@p C +%D +P Q46 Š +ici Nj0w +„)#! !ù + , y )œä¨. q¾¨ +,G ®J r(¯ +J 8 ‡Cðä* Ї†B´,™ Ž +ê& < + + Œ´Û h± W~- ¼‘`Ñ, ‡–õ¤ ,ì> ; + +8RN<, + <1T ] +˜c ‘—' +qk$ +@)#! ; ======================================= --- /trunk/user/src/com/google/gwt/resources/client/ImageResource.java Wed Mar 11 17:58:41 2009 +++ /trunk/user/src/com/google/gwt/resources/client/ImageResource.java Thu Jul 30 11:19:40 2009 @@ -34,6 +34,14 @@ @Documented @Target(ElementType.METHOD) public @interface ImageOptions { + /** + * If <code>true</code>, the image will be flipped about the y-axis when + * {...@link com.google.gwt.i18n.client.LocaleInfo#isRTL()} returns + * <code>true</code>. This is intended to be used by graphics that are + * sensitive to layout direction, such as arrows and disclosure indicators. + */ + boolean flipRtl() default false; + /** * This option affects the image bundling optimization to allow the image to * be used with the {...@link CssResource} {...@code @sprite} rule where @@ -42,14 +50,6 @@ * @see "CssResource documentation" */ RepeatStyle repeatStyle() default RepeatStyle.None; - - /** - * If <code>true</code>, the image will be flipped along the y-axis when - * {...@link com.google.gwt.i18n.client.LocaleInfo#isRTL()} returns - * <code>true</code>. This is intended to be used by graphics that are - * sensitive to layout direction, such as arrows and disclosure indicators. - */ - boolean flipRtl() default false; } /** @@ -103,4 +103,9 @@ * Returns the width of the image. */ int getWidth(); -} + + /** + * Return <code>true</code> if the image contains multiple frames. + */ + boolean isAnimated(); +} ======================================= --- /trunk/user/src/com/google/gwt/resources/client/impl/ImageResourcePrototype.java Wed Mar 11 17:58:41 2009 +++ /trunk/user/src/com/google/gwt/resources/client/impl/ImageResourcePrototype.java Thu Jul 30 11:19:40 2009 @@ -23,6 +23,7 @@ */ public class ImageResourcePrototype implements ImageResource { + private final boolean animated; private final String name; private final String url; private final int left; @@ -30,14 +31,18 @@ private final int width; private final int height; + /** + * Only called by generated code. + */ public ImageResourcePrototype(String name, String url, int left, int top, - int width, int height) { + int width, int height, boolean animated) { this.name = name; this.left = left; this.top = top; this.height = height; this.width = width; this.url = url; + this.animated = animated; } /** @@ -78,4 +83,8 @@ public int getWidth() { return width; } -} + + public boolean isAnimated() { + return animated; + } +} ======================================= --- /trunk/user/src/com/google/gwt/resources/rg/ImageBundleBuilder.java Fri Mar 27 13:23:50 2009 +++ /trunk/user/src/com/google/gwt/resources/rg/ImageBundleBuilder.java Thu Jul 30 11:19:40 2009 @@ -31,11 +31,14 @@ import java.util.Collections; import java.util.Comparator; import java.util.HashMap; +import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Map; import javax.imageio.ImageIO; +import javax.imageio.ImageReader; +import javax.imageio.stream.MemoryCacheImageInputStream; /** * Accumulates state for the bundled image. @@ -213,6 +216,8 @@ BufferedImage getImage(); + BufferedImage[] getImages(); + int getLeft(); String getName(); @@ -287,7 +292,7 @@ private boolean hasBeenPositioned; private final int height, width; - private final BufferedImage image; + private final BufferedImage[] images; private int left, top; private final String name; private final AffineTransform transform = new AffineTransform(); @@ -299,7 +304,7 @@ this.name = other.getName(); this.height = other.getHeight(); this.width = other.getWidth(); - this.image = other.getImage(); + this.images = other.getImages(); this.left = other.getLeft(); this.top = other.getTop(); setTransform(other.getTransform()); @@ -307,17 +312,28 @@ public ImageRect(String name, BufferedImage image) { this.name = name; - this.image = image; + this.images = new BufferedImage[] {image}; this.width = image.getWidth(); this.height = image.getHeight(); } + + public ImageRect(String name, BufferedImage[] images) { + this.name = name; + this.images = images; + this.width = images[0].getWidth(); + this.height = images[0].getHeight(); + } public int getHeight() { return height; } public BufferedImage getImage() { - return image; + return images[0]; + } + + public BufferedImage[] getImages() { + return images; } public int getLeft() { @@ -343,6 +359,10 @@ public boolean hasBeenPositioned() { return hasBeenPositioned; } + + public boolean isAnimated() { + return images.length > 1; + } public void setPosition(int left, int top) { hasBeenPositioned = true; @@ -603,10 +623,40 @@ logger = logger.branch(TreeLogger.TRACE, "Adding image '" + imageName + "'", null); - BufferedImage image; + BufferedImage image = null; // Load the image try { - image = ImageIO.read(imageUrl); + String path = imageUrl.getPath(); + String suffix = path.substring(path.lastIndexOf('.') + 1); + + /* + * ImageIO uses an SPI pattern API. We don't care about the particulars of + * the implementation, so just choose the first ImageReader. + */ + Iterator<ImageReader> it = ImageIO.getImageReadersBySuffix(suffix); + if (it.hasNext()) { + ImageReader reader = it.next(); + + reader.setInput(new MemoryCacheImageInputStream(imageUrl.openStream())); + + int numImages = reader.getNumImages(true); + if (numImages == 0) { + // Fall through + + } else if (numImages == 1) { + image = reader.read(0); + + } else { + // Read all contained images + BufferedImage[] images = new BufferedImage[numImages]; + for (int i = 0; i < numImages; i++) { + images[i] = reader.read(i); + } + + ImageRect rect = new ImageRect(imageName, images); + throw new UnsuitableForStripException(rect); + } + } } catch (IllegalArgumentException iex) { if (imageName.toLowerCase().endsWith("png") && iex.getMessage() != null @@ -625,7 +675,7 @@ throw iex; } } catch (IOException e) { - logger.log(TreeLogger.ERROR, "Unable to read image resource", null); + logger.log(TreeLogger.ERROR, "Unable to read image resource", e); throw new UnableToCompleteException(); } ======================================= --- /trunk/user/src/com/google/gwt/resources/rg/ImageResourceGenerator.java Thu Mar 12 11:16:43 2009 +++ /trunk/user/src/com/google/gwt/resources/rg/ImageResourceGenerator.java Thu Jul 30 11:19:40 2009 @@ -86,7 +86,7 @@ + urlExpressions[1] + " : " + urlExpressions[0] + ","); } sw.println(rect.getLeft() + ", " + rect.getTop() + ", " + rect.getWidth() - + ", " + rect.getHeight()); + + ", " + rect.getHeight() + ", " + rect.isAnimated()); sw.outdent(); sw.print(")"); @@ -224,9 +224,17 @@ } catch (UnsuitableForStripException e) { // Add the image to the output as a separate resource rect = e.getImageRect(); - byte[] imageBytes = ImageBundleBuilder.toPng(logger, rect); - String urlExpression = context.deploy(rect.getName() + ".png", - "image/png", imageBytes, false); + + String urlExpression; + if (rect.isAnimated()) { + // Can't re-encode animated images, so we emit it as-is + urlExpression = context.deploy(resource, false); + } else { + // Re-encode the image as a PNG to strip random header data + byte[] imageBytes = ImageBundleBuilder.toPng(logger, rect); + urlExpression = context.deploy(rect.getName() + ".png", "image/png", + imageBytes, false); + } urlsByExternalImageRect.put(rect, new String[] {urlExpression, null}); } ======================================= --- /trunk/user/test/com/google/gwt/resources/client/ImageResourceTest.java Tue Jun 9 12:10:22 2009 +++ /trunk/user/test/com/google/gwt/resources/client/ImageResourceTest.java Thu Jul 30 11:19:40 2009 @@ -31,6 +31,9 @@ */ public class ImageResourceTest extends GWTTestCase { static interface Resources extends ClientBundle { + @Source("animated.gif") + ImageResource animated(); + @Source("16x16.png") ImageResource i16x16(); @@ -73,6 +76,21 @@ public String getModuleName() { return "com.google.gwt.resources.Resources"; } + + public void testAnimated() { + Resources r = GWT.create(Resources.class); + + ImageResource a = r.animated(); + + assertTrue(a.isAnimated()); + assertEquals(16, a.getWidth()); + assertEquals(16, a.getHeight()); + assertEquals(0, a.getLeft()); + assertEquals(0, a.getTop()); + + // Make sure the animated image is encoded separately + assertFalse(a.getURL().equals(r.i16x16().getURL())); + } public void testDedup() { Resources r = GWT.create(Resources.class); --~--~---------~--~----~------------~-------~--~----~ http://groups.google.com/group/Google-Web-Toolkit-Contributors -~----------~----~----~----~------~----~------~--~---
