From 108f87d4c733b4a99e4e1b166e95e044727a7a0e Mon Sep 17 00:00:00 2001 From: Michael Bayne Date: Wed, 5 Sep 2018 16:06:27 -0700 Subject: [PATCH] Simplify Color & Rectangle handling, fix bug. We can just keep Color in its ARGB format and have a single static brightness() helper. Reactangle grew a union method which allows us to avoid AWT's Rectangle entirely. stringsToList() needed to return null for null array input, and I went ahead and used Arrays.asList while I was in there because we know the provenance of the input array, no one will mutate it. --- .../threerings/getdown/data/Application.java | 36 +++---- .../com/threerings/getdown/util/Color.java | 74 ++------------ .../com/threerings/getdown/util/Config.java | 16 +-- .../threerings/getdown/util/Rectangle.java | 11 ++- .../threerings/getdown/util/ColorTest.java | 98 +------------------ .../threerings/getdown/launcher/Getdown.java | 6 +- .../getdown/launcher/GetdownApp.java | 2 +- .../getdown/launcher/RotatingBackgrounds.java | 8 +- .../getdown/launcher/StatusPanel.java | 18 ++-- 9 files changed, 63 insertions(+), 206 deletions(-) diff --git a/core/src/main/java/com/threerings/getdown/data/Application.java b/core/src/main/java/com/threerings/getdown/data/Application.java index cdc7bbf..ef282d6 100644 --- a/core/src/main/java/com/threerings/getdown/data/Application.java +++ b/core/src/main/java/com/threerings/getdown/data/Application.java @@ -86,7 +86,7 @@ public class Application public final String name; /** A background color, just in case. */ - public final Color background; + public final int background; /** Background image specifiers for `RotatingBackgrounds`. */ public final List rotatingBackgrounds; @@ -107,19 +107,19 @@ public class Application public final Rectangle progress; /** The color of the progress text. */ - public final Color progressText; + public final int progressText; /** The color of the progress bar. */ - public final Color progressBar; + public final int progressBar; /** The dimensions of the status display. */ public final Rectangle status; /** The color of the status text. */ - public final Color statusText; + public final int statusText; /** The color of the text shadow. */ - public final Color textShadow; + public final int textShadow; /** Where to point the user for help with install errors. */ public final String installError; @@ -164,21 +164,19 @@ public class Application this.progressText = config.getColor("ui.progress_text", Color.BLACK); this.hideProgressText = config.getBoolean("ui.hide_progress_text"); this.minShowSeconds = config.getInt("ui.min_show_seconds", 5); - this.progressBar = config.getColor("ui.progress_bar", new Color(0x66, 0x99, 0xCC)); + this.progressBar = config.getColor("ui.progress_bar", 0x6699CC); this.status = config.getRect("ui.status", new Rectangle(5, 25, 500, 100)); this.statusText = config.getColor("ui.status_text", Color.BLACK); - this.textShadow = config.getColor("ui.text_shadow", null); + this.textShadow = config.getColor("ui.text_shadow", Color.CLEAR); this.hideDecorations = config.getBoolean("ui.hide_decorations"); - this.backgroundImage = config.getString("ui.background_image", - config.getString("ui.background")); // support legacy format - // and now ui.background can refer to the background color, but fall back to black - // or white, depending on the brightness of the progressText - Color defaultBackground = (.5f < this.progressText.brightness()) - ? Color.BLACK - : Color.WHITE; + this.backgroundImage = config.getString("ui.background_image"); + // default to black or white bg color, depending on the brightness of the progressText + int defaultBackground = (0.5f < Color.brightness(this.progressText)) ? + Color.BLACK : Color.WHITE; this.background = config.getColor("ui.background", defaultBackground); this.progressImage = config.getString("ui.progress_image"); - this.rotatingBackgrounds = stringsToList(config.getMultiValue("ui.rotating_background")); + this.rotatingBackgrounds = stringsToList( + config.getMultiValue("ui.rotating_background")); this.iconImages = stringsToList(config.getMultiValue("ui.icon")); this.errorBackground = config.getString("ui.error_background"); @@ -192,7 +190,7 @@ public class Application this.patchNotesUrl = config.getUrl("ui.patch_notes_url", null); // step progress percentage (defaults and then customized values) - EnumMap> stepPercentages = new EnumMap>(Step.class); + EnumMap> stepPercentages = new EnumMap<>(Step.class); for (Step step : Step.values()) { stepPercentages.put(step, step.defaultPercents); } @@ -1640,11 +1638,7 @@ public class Application */ public static List stringsToList (String[] values) { - List list = new ArrayList<>(values.length); - for (String val : values) { - list.add(val); - } - return Collections.unmodifiableList(list); + return values == null ? null : Collections.unmodifiableList(Arrays.asList(values)); } /** Used to parse resources with the specified name. */ diff --git a/core/src/main/java/com/threerings/getdown/util/Color.java b/core/src/main/java/com/threerings/getdown/util/Color.java index 3c33594..6586cc7 100644 --- a/core/src/main/java/com/threerings/getdown/util/Color.java +++ b/core/src/main/java/com/threerings/getdown/util/Color.java @@ -6,76 +6,22 @@ package com.threerings.getdown.util; /** - * An immutable color. + * Utilities for handling ARGB colors. */ public class Color { - public final static Color WHITE = new Color(255, 255, 255); - public final static Color BLACK = new Color(0, 0, 0); + public final static int CLEAR = 0x00000000; + public final static int WHITE = 0xFFFFFFFF; + public final static int BLACK = 0xFF000000; - public final int red; - public final int green; - public final int blue; - public final int alpha; - - public Color (int r, int g, int b) - { - this.red = Math.min(Math.max(r, 0), 255); - this.green = Math.min(Math.max(g, 0), 255); - this.blue = Math.min(Math.max(b, 0), 255); - this.alpha = 255; - } - - public Color (int rgba, boolean hasAlpha) - { - this.red = (rgba >> 16) & 0xFF; - this.green = (rgba >> 8) & 0xFF; - this.blue = (rgba >> 0) & 0xFF; - - if (hasAlpha) { - this.alpha = (rgba >> 24) & 0xFF; - } else { - this.alpha = 255; - } - } - - /** - * Returns the combined RBGA value for this color. - */ - public int rgba () - { - return ((alpha & 0xFF) << 24) | - ((red & 0xFF) << 16) | - ((green & 0xFF) << 8) | - ((blue & 0xFF) << 0); - } - - /** - * Returns the brightness of this color, per the standard HSB model. - */ - public float brightness () - { + public static float brightness (int argb) { + // TODO: we're ignoring alpha here... + int red = (argb >> 16) & 0xFF; + int green = (argb >> 8) & 0xFF; + int blue = (argb >> 0) & 0xFF; int max = Math.max(Math.max(red, green), blue); return ((float) max) / 255.0f; } - /** {@inheritDoc} */ - @Override - public boolean equals(Object other) - { - return other instanceof Color && ((Color)other).rgba() == this.rgba(); - } - - /** {@inheritDoc} */ - @Override - public int hashCode() - { - return rgba(); - } - - /** {@inheritDoc} */ - public String toString () - { - return getClass().getName() + "[r=" + red + ", g=" + green + ", b=" + blue + ", a=" + alpha + "]"; - } + private Color () {} } diff --git a/core/src/main/java/com/threerings/getdown/util/Config.java b/core/src/main/java/com/threerings/getdown/util/Config.java index 1a98974..ed7303b 100644 --- a/core/src/main/java/com/threerings/getdown/util/Config.java +++ b/core/src/main/java/com/threerings/getdown/util/Config.java @@ -148,16 +148,16 @@ public class Config } /** - * Parses the given hex color value (e.g. FFFFF) and returns a Color object with that value. - * If the given value is null of not a valid hexadecimal number, this will return null. + * Parses the given hex color value (e.g. FFCC99) and returns an {@code Integer} with that + * value. If the given value is null or not a valid hexadecimal number, this will return null. */ - public static Color parseColor (String hexValue) + public static Integer parseColor (String hexValue) { if (!StringUtil.isBlank(hexValue)) { try { - int rgba = Integer.parseInt(hexValue, 16); - boolean hasAlpha = hexValue.length() > 6; - return new Color(rgba, hasAlpha); + // if no alpha channel is specified, use 255 (full alpha) + int alpha = hexValue.length() > 6 ? 0 : 0xFF000000; + return Integer.parseInt(hexValue, 16) | alpha; } catch (NumberFormatException e) { log.warning("Ignoring invalid color", "hexValue", hexValue, "exception", e); } @@ -300,10 +300,10 @@ public class Config } /** Used to parse color specifications from the config file. */ - public Color getColor (String name, Color def) + public int getColor (String name, int def) { String value = getString(name); - Color color = parseColor(value); + Integer color = parseColor(value); return (color == null) ? def : color; } diff --git a/core/src/main/java/com/threerings/getdown/util/Rectangle.java b/core/src/main/java/com/threerings/getdown/util/Rectangle.java index 19d5398..92527a1 100644 --- a/core/src/main/java/com/threerings/getdown/util/Rectangle.java +++ b/core/src/main/java/com/threerings/getdown/util/Rectangle.java @@ -23,9 +23,18 @@ public class Rectangle this.height = height; } + public Rectangle union (Rectangle other) { + int x1 = Math.min(x, other.x); + int x2 = Math.max(x + width, other.x + other.width); + int y1 = Math.min(y, other.y); + int y2 = Math.max(y + height, other.y + other.height); + return new Rectangle(x1, y1, x2 - x1, y2 - y1); + } + /** {@inheritDoc} */ public String toString () { - return getClass().getName() + "[x=" + x + ", y=" + y + ", width=" + width + ", height=" + height + "]"; + return getClass().getName() + "[x=" + x + ", y=" + y + + ", width=" + width + ", height=" + height + "]"; } } diff --git a/core/src/test/java/com/threerings/getdown/util/ColorTest.java b/core/src/test/java/com/threerings/getdown/util/ColorTest.java index 4d6a664..6b52b67 100644 --- a/core/src/test/java/com/threerings/getdown/util/ColorTest.java +++ b/core/src/test/java/com/threerings/getdown/util/ColorTest.java @@ -6,9 +6,7 @@ package com.threerings.getdown.util; import org.junit.Test; - import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; /** * Tests {@link Color}. @@ -16,96 +14,10 @@ import static org.junit.Assert.assertFalse; public class ColorTest { @Test - public void testRgbConstructor () - { - Color color1 = new Color(0, 0, 0); - assertEquals(0, color1.red); - assertEquals(0, color1.green); - assertEquals(0, color1.blue); - assertEquals(255, color1.alpha); - assertEquals(0xFF000000, color1.rgba()); - assertEquals(0, color1.brightness(), 0.0000001); - assertEquals(Color.BLACK, color1); - - Color color2 = new Color(255, 255, 255); - assertEquals(255, color2.red); - assertEquals(255, color2.green); - assertEquals(255, color2.blue); - assertEquals(255, color2.alpha); - assertEquals(0xFFFFFFFF, color2.rgba()); - assertEquals(1, color2.brightness(), 0.0000001); - assertEquals(Color.WHITE, color2); - - Color color3 = new Color(1, 2, 3); - assertEquals(1, color3.red); - assertEquals(2, color3.green); - assertEquals(3, color3.blue); - assertEquals(255, color3.alpha); - assertEquals(0xFF010203, color3.rgba()); - assertEquals(0.0117647, color3.brightness(), 0.0000001); - - Color color4 = new Color(-100, 300, 200); - assertEquals(0, color4.red); - assertEquals(255, color4.green); - assertEquals(200, color4.blue); - assertEquals(255, color4.alpha); - assertEquals(0xFF00FFC8, color4.rgba()); - assertEquals(1, color4.brightness(), 0.0000001); - } - - @Test - public void testRgbaConstructor () - { - Color color1 = new Color(0, false); - assertEquals(0, color1.red); - assertEquals(0, color1.green); - assertEquals(0, color1.blue); - assertEquals(255, color1.alpha); - assertEquals(0xFF000000, color1.rgba()); - assertEquals(0, color1.brightness(), 0.0000001); - assertEquals(Color.BLACK, color1); - - Color color2 = new Color(0, true); - assertEquals(0, color2.red); - assertEquals(0, color2.green); - assertEquals(0, color2.blue); - assertEquals(0, color2.alpha); - assertEquals(0, color2.rgba()); - assertEquals(0, color2.brightness(), 0.0000001); - assertFalse(Color.BLACK.equals(color2)); - - Color color3 = new Color(0x00FFFFFF, false); - assertEquals(255, color3.red); - assertEquals(255, color3.green); - assertEquals(255, color3.blue); - assertEquals(255, color3.alpha); - assertEquals(0xFFFFFFFF, color3.rgba()); - assertEquals(1, color3.brightness(), 0.0000001); - assertEquals(Color.WHITE, color3); - - Color color4 = new Color(0x00FFFFFF, true); - assertEquals(255, color4.red); - assertEquals(255, color4.green); - assertEquals(255, color4.blue); - assertEquals(0, color4.alpha); - assertEquals(0x00FFFFFF, color4.rgba()); - assertEquals(1, color4.brightness(), 0.0000001); - assertFalse(Color.WHITE.equals(color4)); - - Color color5 = new Color(0x00010203, false); - assertEquals(1, color5.red); - assertEquals(2, color5.green); - assertEquals(3, color5.blue); - assertEquals(255, color5.alpha); - assertEquals(0xFF010203, color5.rgba()); - assertEquals(0.0117647, color5.brightness(), 0.0000001); - - Color color6 = new Color(0x00010203, true); - assertEquals(1, color6.red); - assertEquals(2, color6.green); - assertEquals(3, color6.blue); - assertEquals(0, color6.alpha); - assertEquals(0x00010203, color6.rgba()); - assertEquals(0.0117647, color6.brightness(), 0.0000001); + public void testBrightness() { + assertEquals(0, Color.brightness(0xFF000000), 0.0000001); + assertEquals(1, Color.brightness(0xFFFFFFFF), 0.0000001); + assertEquals(0.0117647, Color.brightness(0xFF010203), 0.0000001); + assertEquals(1, Color.brightness(0xFF00FFC8), 0.0000001); } } diff --git a/launcher/src/main/java/com/threerings/getdown/launcher/Getdown.java b/launcher/src/main/java/com/threerings/getdown/launcher/Getdown.java index c78a912..37ea675 100644 --- a/launcher/src/main/java/com/threerings/getdown/launcher/Getdown.java +++ b/launcher/src/main/java/com/threerings/getdown/launcher/Getdown.java @@ -10,7 +10,6 @@ import java.awt.Container; import java.awt.Dimension; import java.awt.EventQueue; import java.awt.Image; -import java.awt.Rectangle; import java.awt.event.ActionEvent; import java.awt.image.BufferedImage; @@ -888,9 +887,8 @@ public abstract class Getdown extends Thread _status.setSize(size); _layers.setPreferredSize(size); - Rectangle patchNotes = new Rectangle(_ifc.patchNotes.x, _ifc.patchNotes.y, - _ifc.patchNotes.width, _ifc.patchNotes.height); - _patchNotes.setBounds(patchNotes); + _patchNotes.setBounds(_ifc.patchNotes.x, _ifc.patchNotes.y, + _ifc.patchNotes.width, _ifc.patchNotes.height); _patchNotes.setVisible(false); // we were displaying progress while the UI wasn't up. Now that it is, whatever progress diff --git a/launcher/src/main/java/com/threerings/getdown/launcher/GetdownApp.java b/launcher/src/main/java/com/threerings/getdown/launcher/GetdownApp.java index 75590c2..babb48d 100644 --- a/launcher/src/main/java/com/threerings/getdown/launcher/GetdownApp.java +++ b/launcher/src/main/java/com/threerings/getdown/launcher/GetdownApp.java @@ -142,7 +142,7 @@ public class GetdownApp }); _frame.setUndecorated(_ifc.hideDecorations); try { - _frame.setBackground(new Color(_ifc.background.rgba(), true)); + _frame.setBackground(new Color(_ifc.background, true)); } catch (UnsupportedOperationException e) { log.warning("Failed to set background", e); } catch (IllegalComponentStateException e) { diff --git a/launcher/src/main/java/com/threerings/getdown/launcher/RotatingBackgrounds.java b/launcher/src/main/java/com/threerings/getdown/launcher/RotatingBackgrounds.java index 55a142f..854b39c 100644 --- a/launcher/src/main/java/com/threerings/getdown/launcher/RotatingBackgrounds.java +++ b/launcher/src/main/java/com/threerings/getdown/launcher/RotatingBackgrounds.java @@ -49,9 +49,10 @@ public class RotatingBackgrounds minDisplayTime = new int[backgrounds.size()]; images = new Image[backgrounds.size()]; for (int ii = 0; ii < backgrounds.size(); ii++) { - String[] pieces = backgrounds.get(ii).split(";"); + String background = backgrounds.get(ii); + String[] pieces = background.split(";"); if (pieces.length != 2) { - log.warning("Unable to parse background image '" + backgrounds.get(ii) + "'"); + log.warning("Unable to parse background image '" + background + "'"); makeEmpty(); return; } @@ -59,8 +60,7 @@ public class RotatingBackgrounds try { minDisplayTime[ii] = Integer.parseInt(pieces[1]); } catch (NumberFormatException e) { - log.warning("Unable to parse background image display time '" + - backgrounds.get(ii) + "'"); + log.warning("Unable to parse background image display time '" + background + "'"); makeEmpty(); return; } diff --git a/launcher/src/main/java/com/threerings/getdown/launcher/StatusPanel.java b/launcher/src/main/java/com/threerings/getdown/launcher/StatusPanel.java index 2350ac6..e3574a2 100644 --- a/launcher/src/main/java/com/threerings/getdown/launcher/StatusPanel.java +++ b/launcher/src/main/java/com/threerings/getdown/launcher/StatusPanel.java @@ -11,7 +11,6 @@ import java.awt.Font; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Image; -import java.awt.Rectangle; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.image.ImageObserver; @@ -30,6 +29,7 @@ import com.samskivert.util.StringUtil; import com.samskivert.util.Throttle; import com.threerings.getdown.data.Application.UpdateInterface; +import com.threerings.getdown.util.Rectangle; import static com.threerings.getdown.Log.log; @@ -64,9 +64,7 @@ public class StatusPanel extends JComponent int width = img == null ? -1 : img.getWidth(this); int height = img == null ? -1 : img.getHeight(this); if (width == -1 || height == -1) { - Rectangle progress = new Rectangle(ifc.progress.x, ifc.progress.y, ifc.progress.width, ifc.progress.height); - Rectangle status = new Rectangle(ifc.status.x, ifc.status.y, ifc.status.width, ifc.status.height); - Rectangle bounds = progress.union(status); + Rectangle bounds = ifc.progress.union(ifc.status); // assume the x inset defines the frame padding; add it on the left, right, and bottom _psize = new Dimension(bounds.x + bounds.width + bounds.x, bounds.y + bounds.height + bounds.x); @@ -109,7 +107,7 @@ public class StatusPanel extends JComponent _progress = percent; if (!_ifc.hideProgressText) { String msg = MessageFormat.format(get("m.complete"), percent); - _newplab = createLabel(msg, new Color(_ifc.progressText.rgba(), true)); + _newplab = createLabel(msg, new Color(_ifc.progressText, true)); } needsRepaint = true; } @@ -134,7 +132,7 @@ public class StatusPanel extends JComponent int minutes = (int)(remaining / 60), seconds = (int)(remaining % 60); String remstr = minutes + ":" + ((seconds < 10) ? "0" : "") + seconds; String msg = MessageFormat.format(get("m.remain"), remstr); - _newrlab = createLabel(msg, new Color(_ifc.statusText.rgba(), true)); + _newrlab = createLabel(msg, new Color(_ifc.statusText, true)); } needsRepaint = true; @@ -228,7 +226,7 @@ public class StatusPanel extends JComponent gfx.drawImage(_barimg, _ifc.progress.x, _ifc.progress.y, null); gfx.setClip(null); } else { - gfx.setColor(new Color(_ifc.progressBar.rgba(), true)); + gfx.setColor(new Color(_ifc.progressBar, true)); gfx.fillRect(_ifc.progress.x, _ifc.progress.y, _progress * _ifc.progress.width / 100, _ifc.progress.height); @@ -272,7 +270,7 @@ public class StatusPanel extends JComponent status += " ."; } } - _newlab = createLabel(status, new Color(_ifc.statusText.rgba(), true)); + _newlab = createLabel(status, new Color(_ifc.statusText, true)); // set the width of the label to the width specified int width = _ifc.status.width; if (width == 0) { @@ -306,8 +304,8 @@ public class StatusPanel extends JComponent protected Label createLabel (String text, Color color) { Label label = new Label(text, color, FONT); - if (_ifc.textShadow != null) { - label.setAlternateColor(new Color(_ifc.textShadow.rgba(), true)); + if (_ifc.textShadow != 0) { + label.setAlternateColor(new Color(_ifc.textShadow, true)); label.setStyle(LabelStyleConstants.SHADOW); } return label;