Abby
Abby

Reputation: 1836

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:

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.

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:

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)
}

Upvotes: 6

Views: 1281

Answers (1)

PHPirate
PHPirate

Reputation: 7572

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!

Upvotes: 4

Related Questions