From d920c066998dd32dad2f5d0c8c5e438b285c3371 Mon Sep 17 00:00:00 2001 From: Cristian Maglie <c.maglie@arduino.cc> Date: Sun, 3 Jan 2016 16:14:58 +0100 Subject: [PATCH] Now the IDE use vectorial images whenever possible The caller of Theme.getThemeImage(...) now pass only the name of the needed resource and the theme folder is searche in the following order: - name.svg - name.png (if svg is not available) - name@2x.png (if none of the above are available or if 1x png is too low resolution for the current scaling factor) --- .../arduino/view/preferences/Preferences.java | 2 +- app/src/processing/app/Base.java | 4 +- app/src/processing/app/EditorHeader.java | 9 +- app/src/processing/app/EditorLineStatus.java | 2 +- app/src/processing/app/EditorToolbar.java | 4 +- app/src/processing/app/Theme.java | 104 ++++++++++++------ 6 files changed, 86 insertions(+), 39 deletions(-) diff --git a/app/src/cc/arduino/view/preferences/Preferences.java b/app/src/cc/arduino/view/preferences/Preferences.java index 4ea522e35..7b52ccee1 100644 --- a/app/src/cc/arduino/view/preferences/Preferences.java +++ b/app/src/cc/arduino/view/preferences/Preferences.java @@ -197,7 +197,7 @@ public class Preferences extends javax.swing.JDialog { additionalBoardsManagerField.setToolTipText(tr("Enter a comma separated list of urls")); - extendedAdditionalUrlFieldWindow.setIcon(new ImageIcon(Theme.getThemeImage("newwindow.png", this))); + extendedAdditionalUrlFieldWindow.setIcon(new ImageIcon(Theme.getThemeImage("newwindow", this, Theme.scale(16), Theme.scale(14)))); extendedAdditionalUrlFieldWindow.setMargin(new java.awt.Insets(1, 1, 1, 1)); extendedAdditionalUrlFieldWindow.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { diff --git a/app/src/processing/app/Base.java b/app/src/processing/app/Base.java index c018702fe..575925cb2 100644 --- a/app/src/processing/app/Base.java +++ b/app/src/processing/app/Base.java @@ -43,7 +43,6 @@ import processing.app.debug.TargetBoard; import processing.app.debug.TargetPackage; import processing.app.debug.TargetPlatform; import processing.app.helpers.*; -import processing.app.helpers.FileUtils.SplitFile; import processing.app.helpers.filefilters.OnlyDirs; import processing.app.helpers.filefilters.OnlyFilesWithExtension; import processing.app.javax.swing.filechooser.FileNameExtensionFilter; @@ -1743,7 +1742,8 @@ public class Base { */ @SuppressWarnings("serial") public void handleAbout() { - final Image image = Theme.getLibImage("about.png", activeEditor); + final Image image = Theme.getLibImage("about", activeEditor, + Theme.scale(475), Theme.scale(300)); final Window window = new Window(activeEditor) { public void paint(Graphics g) { g.drawImage(image, 0, 0, null); diff --git a/app/src/processing/app/EditorHeader.java b/app/src/processing/app/EditorHeader.java index 5d6b3d104..27d887b25 100644 --- a/app/src/processing/app/EditorHeader.java +++ b/app/src/processing/app/EditorHeader.java @@ -73,6 +73,7 @@ public class EditorHeader extends JComponent { static final int MENU = 3; static final int PIECE_WIDTH = scale(4); + static final int PIECE_HEIGHT = scale(33); // value for the size bars, buttons, etc static final int GRID_SIZE = scale(33); @@ -150,8 +151,12 @@ public class EditorHeader extends JComponent { pieces = new Image[STATUS.length][WHERE.length]; for (int i = 0; i < STATUS.length; i++) { for (int j = 0; j < WHERE.length; j++) { - String path = "tab-" + STATUS[i] + "-" + WHERE[j] + ".png"; - pieces[i][j] = Theme.getThemeImage(path, this); + String path = "tab-" + STATUS[i] + "-" + WHERE[j]; + pieces[i][j] = Theme.getThemeImage(path, this, + // TODO: Refactor this mess... + j == MENU ? PIECE_HEIGHT + : PIECE_WIDTH, + PIECE_HEIGHT); } } } diff --git a/app/src/processing/app/EditorLineStatus.java b/app/src/processing/app/EditorLineStatus.java index a4dc6af0a..1f21590ea 100644 --- a/app/src/processing/app/EditorLineStatus.java +++ b/app/src/processing/app/EditorLineStatus.java @@ -60,7 +60,7 @@ public class EditorLineStatus extends JComponent { high = scale(Theme.getInteger("linestatus.height")); if (OSUtils.isMacOS()) { - resize = Theme.getThemeImage("resize.png", this); + resize = Theme.getThemeImage("resize", this, RESIZE_IMAGE_SIZE, RESIZE_IMAGE_SIZE); } //linestatus.bgcolor = #000000 //linestatus.font = SansSerif,plain,10 diff --git a/app/src/processing/app/EditorToolbar.java b/app/src/processing/app/EditorToolbar.java index 9ca232633..add87784c 100644 --- a/app/src/processing/app/EditorToolbar.java +++ b/app/src/processing/app/EditorToolbar.java @@ -139,7 +139,9 @@ public class EditorToolbar extends JComponent implements MouseInputListener, Key } private void loadButtons() { - Image allButtons = Theme.getThemeImage("buttons.png", this); + Image allButtons = Theme.getThemeImage("buttons", this, + BUTTON_IMAGE_SIZE * BUTTON_COUNT, + BUTTON_IMAGE_SIZE * 3); buttonImages = new Image[BUTTON_COUNT][3]; for (int i = 0; i < BUTTON_COUNT; i++) { diff --git a/app/src/processing/app/Theme.java b/app/src/processing/app/Theme.java index 523c3150b..47eb7c31d 100644 --- a/app/src/processing/app/Theme.java +++ b/app/src/processing/app/Theme.java @@ -21,21 +21,35 @@ package processing.app; -import processing.app.helpers.FileUtils; -import processing.app.helpers.FileUtils.SplitFile; -import processing.app.helpers.OSUtils; -import processing.app.helpers.PreferencesHelper; -import processing.app.helpers.PreferencesMap; +import static processing.app.I18n.tr; -import javax.swing.text.StyleContext; -import java.awt.*; +import java.awt.Color; +import java.awt.Component; +import java.awt.Font; +import java.awt.Image; +import java.awt.MediaTracker; +import java.awt.RenderingHints; +import java.awt.SystemColor; +import java.awt.Toolkit; import java.awt.font.TextAttribute; +import java.io.ByteArrayOutputStream; import java.io.File; +import java.net.URL; import java.util.HashMap; import java.util.Hashtable; import java.util.Map; -import static processing.app.I18n.tr; +import javax.swing.text.StyleContext; + +import org.apache.batik.transcoder.Transcoder; +import org.apache.batik.transcoder.TranscoderException; +import org.apache.batik.transcoder.TranscoderInput; +import org.apache.batik.transcoder.TranscoderOutput; +import org.apache.batik.transcoder.image.PNGTranscoder; + +import processing.app.helpers.OSUtils; +import processing.app.helpers.PreferencesHelper; +import processing.app.helpers.PreferencesMap; /** * Storage class for theme settings. This was separated from the Preferences @@ -200,51 +214,77 @@ public class Theme { /** * Return an Image object from inside the Processing lib folder. */ - static public Image getLibImage(String filename, Component who) { - Toolkit tk = Toolkit.getDefaultToolkit(); + static public Image getLibImage(String filename, Component who, int width, + int height) { + File libFolder = BaseNoGui.getContentFile("lib"); + Image image = null; - SplitFile name = FileUtils.splitFilename(filename); - int scale = getScale(); - File libFolder = Base.getContentFile("lib"); - File imageFile1x = new File(libFolder, name.basename + "." + name.extension); - File imageFile2x = new File(libFolder, name.basename + "@2x." + name.extension); - - File imageFile; - int sourceScale; - if ((scale > 125 && imageFile2x.exists()) || !imageFile1x.exists()) { - imageFile = imageFile2x; - sourceScale = 200; - } else { - imageFile = imageFile1x; - sourceScale = 100; + // Use vector image when available + File vectorFile = new File(libFolder, filename + ".svg"); + if (vectorFile.exists()) { + try { + image = imageFromSVG(vectorFile.toURI().toURL(), width, height); + } catch (Exception e) { + System.err.println("Failed to load " + vectorFile.getAbsolutePath() + + ": " + e.getMessage()); + } + } + + // Otherwise fall-back to PNG bitmaps + if (image == null) { + File bitmapFile = new File(libFolder, filename + ".png"); + File bitmap2xFile = new File(libFolder, filename + "@2x.png"); + + File imageFile; + if ((getScale() > 125 && bitmap2xFile.exists()) || !bitmapFile.exists()) { + imageFile = bitmap2xFile; + } else { + imageFile = bitmapFile; + } + Toolkit tk = Toolkit.getDefaultToolkit(); + image = tk.getImage(imageFile.getAbsolutePath()); } - Image image = tk.getImage(imageFile.getAbsolutePath()); MediaTracker tracker = new MediaTracker(who); - tracker.addImage(image, 0); try { + tracker.addImage(image, 0); tracker.waitForAll(); } catch (InterruptedException e) { } - if (scale != sourceScale) { - int width = image.getWidth(null) * scale / sourceScale; - int height = image.getHeight(null) * scale / sourceScale; + if (image.getWidth(null) != width || image.getHeight(null) != height) { image = image.getScaledInstance(width, height, Image.SCALE_SMOOTH); - tracker.addImage(image, 1); try { + tracker.addImage(image, 1); tracker.waitForAll(); } catch (InterruptedException e) { } } + return image; } /** * Get an image associated with the current color theme. */ - static public Image getThemeImage(String name, Component who) { - return getLibImage("theme/" + name, who); + static public Image getThemeImage(String name, Component who, int width, + int height) { + return getLibImage("theme/" + name, who, width, height); + } + + private static Image imageFromSVG(URL url, int width, int height) + throws TranscoderException { + Transcoder t = new PNGTranscoder(); + t.addTranscodingHint(PNGTranscoder.KEY_WIDTH, new Float(width)); + t.addTranscodingHint(PNGTranscoder.KEY_HEIGHT, new Float(height)); + + TranscoderInput input = new TranscoderInput(url.toString()); + ByteArrayOutputStream ostream = new ByteArrayOutputStream(); + TranscoderOutput output = new TranscoderOutput(ostream); + t.transcode(input, output); + + byte[] imgData = ostream.toByteArray(); + return Toolkit.getDefaultToolkit().createImage(imgData); } }