Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JavaFX icon quality in taskbar

When setting a png image as the stage icon in JavaFX (using Kotlin, but this is also a problem in Java), the icon seems to be deformed.

I have googled for this problem, and found the following things:

  • It was (and probably still is) a bug. On the issue page of this bug they explain that a workaround would be to avoid using semi-transparent pixels. I have tried this, but still my images get deformed, as is shown below.

32x32 Deformed 32x32

Left. Original 32x32 image provided to JavaFx. Right. The image JavaFx put in the taskbar.

Original 48x48 Deformed 48x48

Left. Original 48x48 image provided to JavaFx. Right. The image JavaFx put in the taskbar.

It looks like the 32x32 one has to be scaled up, and the 48x48 one has to be scaled down, to something around 42x42 (I also made a 42x42 but that didn't help either). Since the sizes that Windows 'wants' are either a power of two or 48x48, you would say that those sizes would work.

  • I came upon this older question about this topic. It is discussed here that JavaFX does not automatically pick the best size of the icon, but usually the last icon that you add to the icon set, so it is suggested to put the icon you think would be the best fit as last.

As I am aware that this is (probably) an unresolved bug in JavaFX and the other question was last active about three years ago, I am wondering if anyone has found a better workaround in the meantime.

I have created an MWE in Kotlin similar to the one provided in the issue page, as you can easily compare the original image to the one that ends up in the task bar. The images used are the following:

  • icon48.png: Original 48x48 icon
  • icon32.png: Original 32x32 icon

MWE

class Main : Application() {
    override fun start(primaryStage: Stage) {
        val icon48 = Image("sample/icon48.png")
        val icon32 = Image("sample/icon32.png")
        primaryStage.scene = Scene(Group(
                ImageView(icon48)
                ImageView(icon32)
        ))

        primaryStage.icons.addAll(
                icon48,
                icon32
        )

        primaryStage.show()
    }
}

fun main(args: Array<String>) {
    Application.launch(Main::class.java, *args)
}
like image 288
Abby Avatar asked Apr 09 '18 18:04

Abby


1 Answers

A solution for Windows release versions of your program (where it most matters, this doesn't help for the debug version) is using launch4j and InnoSetup. launch4j generates an executable file, and InnoSetup wraps an installation procedure around it.

How to add a good-looking icon to your JavaFX application for Windows (and make it pinnable to the taskbar)

1. Using Gradle, add the launch4j plugin. For example when using Kotlin Gradle DSL, add

plugins {
    // Plugin to build .exe files.
    id("edu.sc.seis.launch4j") version "2.4.4"
}

dependencies {
    // JNA, used to e.g. make a program pinnable to taskbar.
    compile("net.java.dev.jna:jna:4.5.1")
    compile("net.java.dev.jna:jna-platform:4.5.1")
}

launch4j {
    mainClassName = "nl.mynamespace.myapp.MainKt"
    icon = "$projectDir/src/main/resources/myapp32.ico"
    manifest = "$projectDir/releasing/launch4j/myapp.manifest"
}

and put the icon and manifest file in the paths you specified, with the manifest containing

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
    <trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
        <security>
            <requestedPrivileges>
            <requestedExecutionLevel level="highestAvailable"
                uiAccess="False" />
        </requestedPrivileges>
    </security>
    </trustInfo>
</assembly> 

2. Run the Gradle task launch4j/createExe.

3. Download InnoSetup.

4. Adapt any configuration (.iss) file to your project, for example like below

#define MyAppName "Myapp"
#define MyAppVersion "2.0.0-beta.9"
#define MyAppPublisher "mynamespace"
#define MyAppURL "https://github.com/mynamespace/myapp"
#define MyAppExeName "myapp.exe"

[Setup]
; NOTE: The value of AppId uniquely identifies this application.
; Do not use the same AppId value in installers for other applications.
; (To generate a new GUID, click Tools | Generate GUID inside the IDE.)
AppId={{something generated here}}
AppName={#MyAppName}
AppVersion={#MyAppVersion}
;AppVerName={#MyAppName} {#MyAppVersion}
AppPublisher={#MyAppPublisher}
AppPublisherURL={#MyAppURL}
AppSupportURL={#MyAppURL}
AppUpdatesURL={#MyAppURL}
DefaultDirName={pf}\{#MyAppName}
DisableProgramGroupPage=yes
LicenseFile=..\..\LICENSE
OutputDir=..\innoSetup
OutputBaseFilename=setup_myapp_{#MyAppVersion}
SetupIconFile=..\..\src\main\resources\myapp32.ico
Compression=lzma
SolidCompression=yes

[Languages]
Name: "english"; MessagesFile: "compiler:Default.isl"

[Tasks]
Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked

[Files]
Source: "..\..\build\launch4j\myapp.exe"; DestDir: "{app}"; Flags: ignoreversion
; NOTE: Don't use "Flags: ignoreversion" on any shared system files

[Icons]
Name: "{commonprograms}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"
Name: "{commondesktop}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; Tasks: desktopicon; Comment: "Myapp {#MyAppVersion}"; AppUserModelID: "nl.mynamespace.myapp.Main"
; create icon shortcut that embeds AppUserModelID information, which is the same as
; set in the program, to enable pinning to taskbar.


[Run]
Filename: "{app}\{#MyAppExeName}"; Description: "{cm:LaunchProgram,{#StringChange(MyAppName, '&', '&&')}}"; Flags: shellexec postinstall skipifsilent

5. In InnoSetup, click Build | Compile. If you got all the paths right you will now have an installation file, if you use it you will install your JavaFX application which you can pin to the taskbar.

Resulting icon:

nice!

like image 119
PHPirate Avatar answered Oct 19 '22 05:10

PHPirate