Skip to content

Commit 66cc605

Browse files
Unify and improve image disablement calculation algorithm
Each OS-specific implementation of the Image class has its own implementation of an algorithm to calculate a disabled representation of a given image. In addition, the algorithm used on Windows and MacOS produces bad results, which is why usually pre-generated disabled icons are used. This change extracts the implementations of those algorithms into ImageProcessingDescriptors. The implementation for Windows and MacOS is improved and a legacy mode applying the old algorithm is added. Co-authored-by: Manuel Killinger <Manuel.Killinger@vector.com>
1 parent 42ed1da commit 66cc605

File tree

4 files changed

+86
-38
lines changed

4 files changed

+86
-38
lines changed

bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/graphics/Image.java

Lines changed: 14 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,8 @@
7777
*/
7878
public final class Image extends Resource implements Drawable {
7979

80+
private static final boolean USE_LEGACY_IMAGE_DISABLEMENT = Boolean.getBoolean("org.eclipse.swt.image.useLegacyDisablementAlgorithm");
81+
8082
/**
8183
* specifies whether the receiver is a bitmap or an icon
8284
* (one of <code>SWT.BITMAP</code>, <code>SWT.ICON</code>)
@@ -436,29 +438,24 @@ private void createRepFromSourceAndApplyFlag(NSBitmapImageRep srcRep, int srcWid
436438
long data = rep.bitmapData();
437439
C.memmove(data, srcData, srcWidth * srcHeight * 4);
438440
if (flag != SWT.IMAGE_COPY) {
439-
final int redOffset, greenOffset, blueOffset;
441+
final int redOffset, greenOffset, blueOffset, alphaOffset;
440442
if (srcBpp == 32 && (srcBitmapFormat & OS.NSAlphaFirstBitmapFormat) == 0) {
441443
redOffset = 0;
442444
greenOffset = 1;
443445
blueOffset = 2;
446+
alphaOffset = 3;
444447
} else {
448+
alphaOffset = 0;
445449
redOffset = 1;
446450
greenOffset = 2;
447451
blueOffset = 3;
448452
}
449453
/* Apply transformation */
450454
switch (flag) {
451455
case SWT.IMAGE_DISABLE: {
452-
Color zeroColor = this.device.getSystemColor(SWT.COLOR_WIDGET_NORMAL_SHADOW);
453-
RGB zeroRGB = zeroColor.getRGB();
454-
byte zeroRed = (byte)zeroRGB.red;
455-
byte zeroGreen = (byte)zeroRGB.green;
456-
byte zeroBlue = (byte)zeroRGB.blue;
457-
Color oneColor = this.device.getSystemColor(SWT.COLOR_WIDGET_BACKGROUND);
458-
RGB oneRGB = oneColor.getRGB();
459-
byte oneRed = (byte)oneRGB.red;
460-
byte oneGreen = (byte)oneRGB.green;
461-
byte oneBlue = (byte)oneRGB.blue;
456+
ImageColorTransformer disabledImageTransformer = USE_LEGACY_IMAGE_DISABLEMENT
457+
? ImageColorTransformer.forIntensityThreshold(device)
458+
: ImageColorTransformer.forHSB(1.0f, 0.2f, 0.9f, 0.5f);
462459
byte[] line = new byte[(int)srcBpr];
463460
for (int y=0; y<srcHeight; y++) {
464461
C.memmove(line, data + (y * srcBpr), srcBpr);
@@ -467,16 +464,12 @@ private void createRepFromSourceAndApplyFlag(NSBitmapImageRep srcRep, int srcWid
467464
int red = line[offset+redOffset] & 0xFF;
468465
int green = line[offset+greenOffset] & 0xFF;
469466
int blue = line[offset+blueOffset] & 0xFF;
470-
int intensity = red * red + green * green + blue * blue;
471-
if (intensity < 98304) {
472-
line[offset+redOffset] = zeroRed;
473-
line[offset+greenOffset] = zeroGreen;
474-
line[offset+blueOffset] = zeroBlue;
475-
} else {
476-
line[offset+redOffset] = oneRed;
477-
line[offset+greenOffset] = oneGreen;
478-
line[offset+blueOffset] = oneBlue;
479-
}
467+
int alpha = line[offset+alphaOffset] & 0xFF;
468+
RGBA result = disabledImageTransformer.adaptPixelValue(red, green, blue, alpha);
469+
line[offset+redOffset] = (byte) result.rgb.red;
470+
line[offset+greenOffset] = (byte) result.rgb.green;
471+
line[offset+blueOffset] = (byte) result.rgb.blue;
472+
line[offset+alphaOffset] = (byte) result.alpha;
480473
offset += 4;
481474
}
482475
C.memmove(data + (y * srcBpr), line, srcBpr);
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
/*******************************************************************************
2+
* Copyright (c) 2025 Vector Informatik GmbH and others.
3+
*
4+
* This program and the accompanying materials
5+
* are made available under the terms of the Eclipse Public License 2.0
6+
* which accompanies this distribution, and is available at
7+
* https://www.eclipse.org/legal/epl-2.0/
8+
*
9+
* SPDX-License-Identifier: EPL-2.0
10+
*******************************************************************************/
11+
package org.eclipse.swt.internal.image;
12+
13+
import org.eclipse.swt.*;
14+
import org.eclipse.swt.graphics.*;
15+
16+
public interface ImageColorTransformer {
17+
18+
RGBA adaptPixelValue(int red, int green, int blue, int alpha);
19+
20+
public static ImageColorTransformer forHSB(float hueFactor, float saturationFactor, float brightnessFactor,
21+
float alphaFactor) {
22+
return (red, green, blue, alpha) -> {
23+
float[] hsba = new RGBA(red, green, blue, alpha).getHSBA();
24+
float hue = Math.min(hueFactor * hsba[0], 1.0f);
25+
float saturation = Math.min(saturationFactor * hsba[1], 1.0f);
26+
float brightness = Math.min(brightnessFactor * hsba[2], 1.0f);
27+
float alphaResult = Math.min(alphaFactor * hsba[3], 255.0f);
28+
return new RGBA(hue, saturation, brightness, alphaResult);
29+
};
30+
}
31+
32+
public static ImageColorTransformer forRGB(float redFactor, float greenFactor, float blueFactor,
33+
float alphaFactor) {
34+
return (red, green, blue, alpha) -> {
35+
int redResult = (int) Math.min(redFactor * red, 255.0f);
36+
int greenResult = (int) Math.min(greenFactor * green, 255.0f);
37+
int blueResult = (int) Math.min(blueFactor * blue, 255.0f);
38+
int alphaResult = (int) Math.min(alphaFactor * alpha, 255.0f);
39+
return new RGBA(redResult, greenResult, blueResult, alphaResult);
40+
};
41+
}
42+
43+
public static ImageColorTransformer forIntensityThreshold(Device device) {
44+
RGBA lowIntensity = device.getSystemColor(SWT.COLOR_WIDGET_NORMAL_SHADOW).getRGBA();
45+
RGBA highIntensity = device.getSystemColor(SWT.COLOR_WIDGET_BACKGROUND).getRGBA();
46+
return (red, green, blue, alpha) -> {
47+
int intensity = red * red + green * green + blue * blue;
48+
RGBA usedGraytone = intensity < 98304 ? lowIntensity : highIntensity;
49+
return new RGBA(usedGraytone.rgb.red, usedGraytone.rgb.green, usedGraytone.rgb.blue, alpha);
50+
};
51+
}
52+
53+
}

bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/graphics/Image.java

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,8 @@
7676
*/
7777
public final class Image extends Resource implements Drawable {
7878

79+
private static final boolean USE_LEGACY_IMAGE_DISABLEMENT = Boolean.getBoolean("org.eclipse.swt.image.useLegacyDisablementAlgorithm");
80+
7981
/**
8082
* specifies whether the receiver is a bitmap or an icon
8183
* (one of <code>SWT.BITMAP</code>, <code>SWT.ICON</code>)
@@ -304,6 +306,9 @@ public Image(Device device, Image srcImage, int flag) {
304306
}
305307
switch (flag) {
306308
case SWT.IMAGE_DISABLE: {
309+
ImageColorTransformer disabledImageTransformer = USE_LEGACY_IMAGE_DISABLEMENT
310+
? ImageColorTransformer.forRGB(0.5f, 0.5f, 0.5f, 0.5f)
311+
: ImageColorTransformer.forHSB(1.0f, 0.2f, 0.9f, 0.5f);
307312
byte[] line = new byte[stride];
308313
for (int y=0; y<height; y++) {
309314
C.memmove(line, data + (y * stride), stride);
@@ -312,10 +317,11 @@ public Image(Device device, Image srcImage, int flag) {
312317
int r = line[offset + or] & 0xFF;
313318
int g = line[offset + og] & 0xFF;
314319
int b = line[offset + ob] & 0xFF;
315-
line[offset + oa] = (byte) Math.round((double) a * 0.5);
316-
line[offset + or] = (byte) Math.round((double) r * 0.5);
317-
line[offset + og] = (byte) Math.round((double) g * 0.5);
318-
line[offset + ob] = (byte) Math.round((double) b * 0.5);
320+
RGBA result = disabledImageTransformer.adaptPixelValue(r, g, b, a);
321+
line[offset + oa] = (byte) Math.round(result.alpha);
322+
line[offset + or] = (byte) Math.round(result.rgb.red);
323+
line[offset + og] = (byte) Math.round(result.rgb.green);
324+
line[offset + ob] = (byte) Math.round(result.rgb.blue);
319325
}
320326
C.memmove(data + (y * stride), line, stride);
321327
}

bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/Image.java

Lines changed: 9 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,8 @@
7676
*/
7777
public final class Image extends Resource implements Drawable {
7878

79+
private static final boolean USE_LEGACY_IMAGE_DISABLEMENT = Boolean.getBoolean("org.eclipse.swt.image.useLegacyDisablementAlgorithm");
80+
7981
/**
8082
* specifies whether the receiver is a bitmap or an icon
8183
* (one of <code>SWT.BITMAP</code>, <code>SWT.ICON</code>)
@@ -667,12 +669,11 @@ void init() {
667669
}
668670

669671
private ImageData applyDisableImageData(ImageData data, int height, int width) {
672+
ImageColorTransformer disabledImageTransformer = USE_LEGACY_IMAGE_DISABLEMENT
673+
? ImageColorTransformer.forIntensityThreshold(device)
674+
: ImageColorTransformer.forHSB(1.0f, 0.2f, 0.9f, 0.5f);
670675
PaletteData palette = data.palette;
671-
RGB[] rgbs = new RGB[3];
672-
rgbs[0] = this.device.getSystemColor(SWT.COLOR_BLACK).getRGB();
673-
rgbs[1] = this.device.getSystemColor(SWT.COLOR_WIDGET_NORMAL_SHADOW).getRGB();
674-
rgbs[2] = this.device.getSystemColor(SWT.COLOR_WIDGET_BACKGROUND).getRGB();
675-
ImageData newData = new ImageData(width, height, 8, new PaletteData(rgbs));
676+
ImageData newData = new ImageData(width, height, 32, new PaletteData(0xFF, 0xFF00, 0xFF0000));
676677
newData.alpha = data.alpha;
677678
newData.alphaData = data.alphaData;
678679
newData.maskData = data.maskData;
@@ -692,7 +693,6 @@ private ImageData applyDisableImageData(ImageData data, int height, int width) {
692693
int greenShift = palette.greenShift;
693694
int blueShift = palette.blueShift;
694695
for (int y=0; y<height; y++) {
695-
int offset = y * newData.bytesPerLine;
696696
data.getPixels(0, y, width, scanline, 0);
697697
if (mask != null) mask.getPixels(0, y, width, maskScanline, 0);
698698
for (int x=0; x<width; x++) {
@@ -711,14 +711,10 @@ private ImageData applyDisableImageData(ImageData data, int height, int width) {
711711
green = palette.colors[pixel].green;
712712
blue = palette.colors[pixel].blue;
713713
}
714-
int intensity = red * red + green * green + blue * blue;
715-
if (intensity < 98304) {
716-
newData.data[offset] = (byte)1;
717-
} else {
718-
newData.data[offset] = (byte)2;
719-
}
714+
RGBA result = disabledImageTransformer.adaptPixelValue(red, green, blue, data.getAlpha(x, y));
715+
newData.setAlpha(x, y, result.alpha);
716+
newData.setPixel(x, y, newData.palette.getPixel(result.rgb));
720717
}
721-
offset++;
722718
}
723719
}
724720
return newData;

0 commit comments

Comments
 (0)