Commits

shemnon committed 22190dc

Windows Icons

Comments (0)

Files changed (3)

File contents unchanged.

gradle-javafx-plugin/build.gradle

 
 dependencies {
     groovy 'org.codehaus.groovy:groovy-all:2.0.5'
+    compile 'org.jclarion:image4j:0.7'
     compile gradleApi()
     compile files("${System.properties['java.home']}/../lib/ant-javafx.jar")
     compile files("${System.properties['java.home']}/lib/jfxrt.jar")
                 scope 'system'
                 systemPath '${java.home}/../lib/ant-javafx.jar'
             }
+            dependency {
+                groupId 'org.jclarion'
+                artifactId 'image4j'
+                version '0.7'
+            }
         }
     }
 }

gradle-javafx-plugin/src/main/groovy/org/bitbucket/shemnon/javafxplugin/tasks/JavaFXDeployTask.groovy

 
 import com.sun.javafx.tools.packager.DeployParams
 import com.sun.javafx.tools.packager.DeployParams.RunMode
-import com.sun.javafx.tools.packager.Log;
+import com.sun.javafx.tools.packager.Log
 import com.sun.javafx.tools.packager.PackagerLib
 import com.sun.javafx.tools.packager.bundlers.Bundler
+import net.sf.image4j.codec.bmp.BMPEncoder
+import net.sf.image4j.codec.ico.ICOEncoder
 import org.apache.tools.ant.taskdefs.condition.Os
-import org.gradle.api.tasks.InputDirectory;
+import org.gradle.api.file.FileCollection
+import org.gradle.api.internal.ConventionTask
+import org.gradle.api.tasks.InputDirectory
 import org.gradle.api.tasks.InputFiles
-import org.gradle.api.tasks.TaskAction
 import org.gradle.api.tasks.OutputDirectory
-import org.gradle.api.internal.ConventionTask
-import org.gradle.api.file.FileCollection
+import org.gradle.api.tasks.TaskAction
 import org.gradle.util.ConfigureUtil
 
+import java.awt.*
+import java.awt.geom.AffineTransform
+import java.awt.image.BufferedImage
+import java.util.List
+
 class JavaFXDeployTask extends ConventionTask {
 
 
         if (getCodebase() != null) {
             try {
                 deployParams.codebase = getCodebase()
-            } catch (MissingPropertyException moe) {
+            } catch (MissingPropertyException ignored) {
                 getLogger().error("JavaFXDeployTask.codebase is only available in JavaFX 8 or later, codebase setting is ignored")
             }
         }
             void debug(String msg) {
                 getLogger().debug(msg)
             }
-        })
+        } as Log.Logger)
 
         packager.generateDeploymentPackages(deployParams)
         Log.setLogger(null)
     }
 
     def icon(Closure closure) {
-         icons.add(new IconInfo(closure))
+        icons.add(new IconInfo(closure))
     }
 
 
     protected void processIcons(File destination) {
         if (Os.isFamily(Os.FAMILY_MAC)) {
-            processMacIcons(destination);
+            processMacOSXIcons(destination);
+        }
+        if (Os.isFamily(Os.FAMILY_WINDOWS)) {
+            processWindowsIcons(destination);
         }
     }
 
-    protected void processMacIcons(File destination) {
-        processMacIcons('shortcut',
+    protected void processMacOSXIcons(File destination) {
+        processMacOSXIcns('shortcut',
                 new File(destination, "macosx/${project.javafx.appName}.icns"))
-        processMacIcons('volume',
+        processMacOSXIcns('volume',
                 new File(destination, "macosx/${project.javafx.appName}-volume.icns"))
     }
 
     def macIcnsSizes = [16,32,128,256,512]
-    protected void processMacIcons(String kind, File iconLocation) {
+    protected void processMacOSXIcns(String kind, File iconLocation) {
         // get explicit
         def dest = "$project.buildDir/icons/${kind}.iconset"
         project.mkdir(dest)
         for (IconInfo ii : icons) {
             if (kind == ii.kind) {
                 if (ii.width != ii.height) {
-                    logger.error("Icon $ii.kind rejected from MacOSX bundling because it is not square: $ii.width x $ii.height")
+                    logger.info("Icon $ii.href for $ii.kind rejected from MacOSX bundling because it is not square: $ii.width x $ii.height")
                     continue;
                 }
                 if (ii.scale != 1 && ii.scale != 2) {
-                    logger.error("Icon $ii.kind rejected from MacOSX bundling because it has an invalid scale")
+                    logger.info("Icon $ii.href for $ii.kind rejected from MacOSX bundling because it has an invalid scale")
                     continue;
                 }
                 int index = macIcnsSizes.indexOf(ii.height)
                 if (index == -1) {
-                    logger.error("Icon $ii.kind rejected from MacOSX bundling because it is an unsupported dimension.  $macIcnsSizes dimensions are supported")
+                    logger.info("Icon $ii.href for $ii.kind rejected from MacOSX bundling because it is an unsupported dimension.  $macIcnsSizes dimensions are supported")
                     continue;
                 }
                 File file = project.file(ii.href)
                     file = new File(getResourcesDir(), ii.href)
                 }
                 if (!file.isFile()) {
-                    logger.error("Icon $ii.kind rejected from MacOSX bundling because $ii.href does not exist or it is a directory.")
+                    logger.error("Icon $ii.href for $ii.kind rejected from MacOSX bundling because $ii.href does not exist or it is a directory.")
                     continue;
                 }
 
             }
             ant.copy(file: "$project.buildDir/icons/${kind}.icns", toFile: iconLocation)
         }
+    }
 
+    protected void processWindowsIcons(File destination) {
+        processWindowsIco('shortcut',
+                new File(destination, "windows/${project.javafx.appName}.ico"))
+        processWidnowsBMP('setup-icon',
+                new File(destination, "windows/${project.javafx.appName}-setup-icon.bmp"))
     }
 
+    void processWidnowsBMP(String kind, File destination) {
+        boolean processed = false
+        for (IconInfo ii : icons) {
+            if (kind == ii.kind) {
+
+                File file = project.file(ii.href)
+                if (!file.exists()) {
+                    // try to resolve relative to output
+                    file = new File(getResourcesDir(), ii.href)
+                }
+                if (!file.isFile()) {
+                    logger.error("Icon $ii.href for $ii.kind rejected from Windows bundling because $ii.href does not exist or it is a directory.")
+                    continue;
+                }
+                if (processed) {
+                    logger.info("Icon $ii.href for $ii.kind rejected from Windows bundling because only one icon can be used.")
+                    continue;
+                }
+
+                Image icon = Toolkit.defaultToolkit.getImage(file.toURI().toURL())
+
+
+                double scale = Math.min(Math.min(55.0 / icon.width, 58.0 / icon.height), 1.0)
+
+                BufferedImage bi = new BufferedImage((int)icon.width*scale, (int)icon.height*scale, BufferedImage.TYPE_INT_ARGB)
+                def g = bi.graphics
+                def t = new AffineTransform()
+                println scale
+                t.scale(scale, scale)
+                g.transform = t
+                g.drawImage(icon, 0, 0, null)
+                BMPEncoder.write(bi, destination)
+                processed = true
+            }
+        }
+    }
+
+    void processWindowsIco(String kind, File destination) {
+        Map<Integer, BufferedImage> images = new TreeMap<Integer, BufferedImage>()
+        for (IconInfo ii : icons) {
+            if (kind == ii.kind) {
+                File file = project.file(ii.href)
+                if (!file.exists()) {
+                    // try to resolve relative to output
+                    file = new File(getResourcesDir(), ii.href)
+                }
+                if (!file.isFile()) {
+                    logger.error("Icon $ii.href for $ii.kind rejected from Windows bundling because $ii.href does not exist or it is a directory.")
+                    continue;
+                }
+                if (ii.scale != 1) {
+                    logger.info("Icon $ii.href for $ii.kind rejected from Widnows bundling because it has a scale other than '1'")
+                    continue;
+                }
+
+                Image icon = Toolkit.defaultToolkit.getImage(file.toURI().toURL())
+
+                if (icon.width != icon.height) {
+                    logger.info("Icon $ii.href for $ii.kind rejected from Windows bundling because it is not square: $icon.width x $icon.height")
+                    continue;
+                }
+                BufferedImage bi = new BufferedImage(icon.width, icon.height, BufferedImage.TYPE_INT_ARGB)
+                bi.graphics.drawImage(icon, 0, 0, null)
+                images.put(bi.width, bi)
+            }
+
+        }
+        if (images) {
+            destination.parentFile.mkdirs()
+            List<BufferedImage> icons = images.values() as List
+            icons.addAll(images.values())
+            icons.addAll(images.values())
+            int imageCount = images.size()
+            int[] depths = [-1]*imageCount + [8]*imageCount + [4]*imageCount
+            ICOEncoder.write(icons, depths, destination)
+        }
+    }
 }
 
 class IconInfo {
     String href
     String kind = 'default'
-    int width = DeployParams.Icon.UNDEFINED
-    int height = DeployParams.Icon.UNDEFINED
-    int depth = DeployParams.Icon.UNDEFINED
+    int width = -1
+    int height = -1
+    int depth = -1
     double scale = 1 // for retina
     RunMode mode = RunMode.ALL
 
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.