Fix ImageBundleBuilder so that it de-deduplicates identical images.
Adds the gwt.imageResource.maxBundleSize system property if an end-user needs to override the maximum sprite size.
Ensures that the ImageResourceTest is run without data inlining on systems that inline by default.

Patch by: bobv
Review by: rjrjr


git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@5097 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/user/src/com/google/gwt/resources/rg/ImageBundleBuilder.java b/user/src/com/google/gwt/resources/rg/ImageBundleBuilder.java
index 46ce8c2..941fc68 100644
--- a/user/src/com/google/gwt/resources/rg/ImageBundleBuilder.java
+++ b/user/src/com/google/gwt/resources/rg/ImageBundleBuilder.java
@@ -17,6 +17,7 @@
 
 import com.google.gwt.core.ext.TreeLogger;
 import com.google.gwt.core.ext.UnableToCompleteException;
+import com.google.gwt.dev.util.Util;
 import com.google.gwt.resources.ext.ResourceContext;
 
 import java.awt.Graphics2D;
@@ -429,7 +430,8 @@
    */
   private static final String BUNDLE_FILE_TYPE = "png";
   private static final String BUNDLE_MIME_TYPE = "image/png";
-  private static final int IMAGE_MAX_SIZE = 256;
+  private static final int IMAGE_MAX_SIZE = Integer.getInteger(
+      "gwt.imageResource.maxBundleSize", 256);
 
   public static byte[] toPng(TreeLogger logger, HasRect rect)
       throws UnableToCompleteException {
@@ -479,6 +481,11 @@
 
   private final Map<String, ImageRect> imageNameToImageRectMap = new HashMap<String, ImageRect>();
 
+  /**
+   * This map is used to de-duplicate images in generated image strips.
+   */
+  private final Map<String, String> strongNametoCanonicalImageNameMap = new HashMap<String, String>();
+
   public ImageBundleBuilder() {
   }
 
@@ -514,11 +521,21 @@
      * aren't computed until the composite is written.
      */
     ImageRect rect = getMapping(imageName);
-    if (rect == null) {
-      // Assimilate the image into the composite.
-      rect = addImage(logger, imageName, resource);
 
-      imageNameToImageRectMap.put(imageName, rect);
+    if (rect == null) {
+      String strongName = Util.computeStrongName(Util.readURLAsBytes(resource));
+      if (strongNametoCanonicalImageNameMap.containsKey(strongName)) {
+        String previousImageName = strongNametoCanonicalImageNameMap.get(strongName);
+        rect = getMapping(previousImageName);
+        assert rect != null;
+        imageNameToImageRectMap.put(imageName, rect);
+      } else {
+        // Assimilate the image into the composite.
+        rect = addImage(logger, imageName, resource);
+
+        imageNameToImageRectMap.put(imageName, rect);
+        strongNametoCanonicalImageNameMap.put(strongName, imageName);
+      }
     }
     return rect;
   }
@@ -531,7 +548,11 @@
     return imageNameToImageRectMap.get(imageName);
   }
 
+  /**
+   * Remove an image from the builder.
+   */
   public ImageRect removeMapping(String imageName) {
+    strongNametoCanonicalImageNameMap.values().remove(imageName);
     return imageNameToImageRectMap.remove(imageName);
   }
 
diff --git a/user/test/com/google/gwt/resources/ResourcesNoInlining.gwt.xml b/user/test/com/google/gwt/resources/ResourcesNoInlining.gwt.xml
new file mode 100644
index 0000000..60cdede
--- /dev/null
+++ b/user/test/com/google/gwt/resources/ResourcesNoInlining.gwt.xml
@@ -0,0 +1,17 @@
+<!--                                                                        -->
+<!-- Copyright 2009 Google Inc.                                             -->
+<!-- Licensed under the Apache License, Version 2.0 (the "License"); you    -->
+<!-- may not use this file except in compliance with the License. You may   -->
+<!-- may obtain a copy of the License at                                    -->
+<!--                                                                        -->
+<!-- http://www.apache.org/licenses/LICENSE-2.0                             -->
+<!--                                                                        -->
+<!-- Unless required by applicable law or agreed to in writing, software    -->
+<!-- distributed under the License is distributed on an "AS IS" BASIS,      -->
+<!-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or        -->
+<!-- implied. License for the specific language governing permissions and   -->
+<!-- limitations under the License.                                         -->
+<module>
+  <inherits name="com.google.gwt.resources.Resources" />
+  <set-property name="ClientBundle.enableInlining" value="false" />
+</module>
diff --git a/user/test/com/google/gwt/resources/ResourcesSuite.java b/user/test/com/google/gwt/resources/ResourcesSuite.java
index 642a812..2a754cb 100644
--- a/user/test/com/google/gwt/resources/ResourcesSuite.java
+++ b/user/test/com/google/gwt/resources/ResourcesSuite.java
@@ -17,6 +17,7 @@
 
 import com.google.gwt.junit.tools.GWTTestSuite;
 import com.google.gwt.resources.client.CSSResourceTest;
+import com.google.gwt.resources.client.ImageResourceNoInliningTest;
 import com.google.gwt.resources.client.ImageResourceTest;
 import com.google.gwt.resources.client.NestedBundleTest;
 import com.google.gwt.resources.client.TextResourceTest;
@@ -37,8 +38,8 @@
     suite.addTestSuite(CssReorderTest.class);
     suite.addTestSuite(CssRtlTest.class);
     suite.addTestSuite(CssNodeClonerTest.class);
-    // TODO(bobv) Re-enable after fixing this case in non-inlining mode
-    // suite.addTestSuite(ImageResourceTest.class);
+    suite.addTestSuite(ImageResourceTest.class);
+    suite.addTestSuite(ImageResourceNoInliningTest.class);
     suite.addTestSuite(NestedBundleTest.class);
     suite.addTestSuite(TextResourceTest.class);
 
diff --git a/user/test/com/google/gwt/resources/client/ImageResourceNoInliningTest.java b/user/test/com/google/gwt/resources/client/ImageResourceNoInliningTest.java
new file mode 100644
index 0000000..b0641a8
--- /dev/null
+++ b/user/test/com/google/gwt/resources/client/ImageResourceNoInliningTest.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2009 Google Inc.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.resources.client;
+
+/**
+ * Runs the ImageResource tests with inling disabled.
+ */
+public class ImageResourceNoInliningTest extends ImageResourceTest {
+  @Override
+  public String getModuleName() {
+    return "com.google.gwt.resources.ResourcesNoInlining";
+  }
+}
diff --git a/user/test/com/google/gwt/resources/client/ImageResourceTest.java b/user/test/com/google/gwt/resources/client/ImageResourceTest.java
index 982af95..cf8aa4c 100644
--- a/user/test/com/google/gwt/resources/client/ImageResourceTest.java
+++ b/user/test/com/google/gwt/resources/client/ImageResourceTest.java
@@ -113,9 +113,9 @@
     ImageResource lossy = r.largeLossy();
     ImageResource lossless = r.largeLossless();
 
-    // The large, lossless image should be bundled
+    // The large, lossless image should not be bundled
     if (!i64.getURL().startsWith("data:")) {
-      assertEquals(i64.getURL(), lossless.getURL());
+      assertFalse(i64.getURL().equals(lossless.getURL()));
     }
 
     // Make sure that the large, lossy image isn't bundled with the rest