Skip to content

Commit b4e063b

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 8ae6a51 commit b4e063b

File tree

4 files changed

+90
-38
lines changed

4 files changed

+90
-38
lines changed

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

Lines changed: 17 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@
1414
package org.eclipse.swt.graphics;
1515

1616

17+
import static org.eclipse.swt.internal.image.ImageProcessingDescriptor.createHSBImageProcessingDescriptor;
18+
import static org.eclipse.swt.internal.image.ImageProcessingDescriptor.createThresholdIntensityImageProcessingDescriptor;
19+
1720
import java.io.*;
1821
import java.util.*;
1922
import java.util.function.*;
@@ -77,6 +80,8 @@
7780
*/
7881
public final class Image extends Resource implements Drawable {
7982

83+
private static final boolean USE_LEGACY_IMAGE_DISABLEMENT = Boolean.getBoolean("org.eclipse.swt.image.useLegacyDisablementAlgorithm");
84+
8085
/**
8186
* specifies whether the receiver is a bitmap or an icon
8287
* (one of <code>SWT.BITMAP</code>, <code>SWT.ICON</code>)
@@ -436,29 +441,24 @@ private void createRepFromSourceAndApplyFlag(NSBitmapImageRep srcRep, int srcWid
436441
long data = rep.bitmapData();
437442
C.memmove(data, srcData, srcWidth * srcHeight * 4);
438443
if (flag != SWT.IMAGE_COPY) {
439-
final int redOffset, greenOffset, blueOffset;
444+
final int redOffset, greenOffset, blueOffset, alphaOffset;
440445
if (srcBpp == 32 && (srcBitmapFormat & OS.NSAlphaFirstBitmapFormat) == 0) {
441446
redOffset = 0;
442447
greenOffset = 1;
443448
blueOffset = 2;
449+
alphaOffset = 3;
444450
} else {
451+
alphaOffset = 0;
445452
redOffset = 1;
446453
greenOffset = 2;
447454
blueOffset = 3;
448455
}
449456
/* Apply transformation */
450457
switch (flag) {
451458
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;
459+
ImageProcessingDescriptor disabledImageProcessor = USE_LEGACY_IMAGE_DISABLEMENT
460+
? createThresholdIntensityImageProcessingDescriptor(device)
461+
: createHSBImageProcessingDescriptor(1.0f, 0.2f, 0.9f, 0.5f);
462462
byte[] line = new byte[(int)srcBpr];
463463
for (int y=0; y<srcHeight; y++) {
464464
C.memmove(line, data + (y * srcBpr), srcBpr);
@@ -467,16 +467,12 @@ private void createRepFromSourceAndApplyFlag(NSBitmapImageRep srcRep, int srcWid
467467
int red = line[offset+redOffset] & 0xFF;
468468
int green = line[offset+greenOffset] & 0xFF;
469469
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-
}
470+
int alpha = line[offset+alphaOffset] & 0xFF;
471+
RGBA result = disabledImageProcessor.adaptPixelValue(red, green, blue, alpha);
472+
line[offset+redOffset] = (byte) result.rgb.red;
473+
line[offset+greenOffset] = (byte) result.rgb.green;
474+
line[offset+blueOffset] = (byte) result.rgb.blue;
475+
line[offset+alphaOffset] = (byte) result.alpha;
480476
offset += 4;
481477
}
482478
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 ImageProcessingDescriptor {
17+
18+
RGBA adaptPixelValue(int red, int green, int blue, int alpha);
19+
20+
public static ImageProcessingDescriptor createHSBImageProcessingDescriptor(float hueFactor, float saturationFactor,
21+
float brightnessFactor, 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 ImageProcessingDescriptor createRGBImageProcessingDescriptor(float redFactor, float greenFactor,
33+
float blueFactor, 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 ImageProcessingDescriptor createThresholdIntensityImageProcessingDescriptor(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: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
package org.eclipse.swt.graphics;
1515

1616

17+
import static org.eclipse.swt.internal.image.ImageProcessingDescriptor.createRGBImageProcessingDescriptor;
18+
1719
import java.io.*;
1820
import java.util.*;
1921

@@ -304,6 +306,7 @@ public Image(Device device, Image srcImage, int flag) {
304306
}
305307
switch (flag) {
306308
case SWT.IMAGE_DISABLE: {
309+
ImageProcessingDescriptor disabledImageProcessor = createRGBImageProcessingDescriptor(0.5f, 0.5f, 0.5f, 0.5f);
307310
byte[] line = new byte[stride];
308311
for (int y=0; y<height; y++) {
309312
C.memmove(line, data + (y * stride), stride);
@@ -312,10 +315,11 @@ public Image(Device device, Image srcImage, int flag) {
312315
int r = line[offset + or] & 0xFF;
313316
int g = line[offset + og] & 0xFF;
314317
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);
318+
RGBA result = disabledImageProcessor.adaptPixelValue(r, g, b, a);
319+
line[offset + oa] = (byte) Math.round(result.alpha);
320+
line[offset + or] = (byte) Math.round(result.rgb.red);
321+
line[offset + og] = (byte) Math.round(result.rgb.green);
322+
line[offset + ob] = (byte) Math.round(result.rgb.blue);
319323
}
320324
C.memmove(data + (y * stride), line, stride);
321325
}

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

Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@
1414
package org.eclipse.swt.graphics;
1515

1616

17+
import static org.eclipse.swt.internal.image.ImageProcessingDescriptor.createHSBImageProcessingDescriptor;
18+
import static org.eclipse.swt.internal.image.ImageProcessingDescriptor.createThresholdIntensityImageProcessingDescriptor;
19+
1720
import java.io.*;
1821
import java.util.*;
1922

@@ -76,6 +79,8 @@
7679
*/
7780
public final class Image extends Resource implements Drawable {
7881

82+
private static final boolean USE_LEGACY_IMAGE_DISABLEMENT = Boolean.getBoolean("org.eclipse.swt.image.useLegacyDisablementAlgorithm");
83+
7984
/**
8085
* specifies whether the receiver is a bitmap or an icon
8186
* (one of <code>SWT.BITMAP</code>, <code>SWT.ICON</code>)
@@ -667,12 +672,11 @@ void init() {
667672
}
668673

669674
private ImageData applyDisableImageData(ImageData data, int height, int width) {
675+
ImageProcessingDescriptor disabledImageProcessor = USE_LEGACY_IMAGE_DISABLEMENT
676+
? createThresholdIntensityImageProcessingDescriptor(device)
677+
: createHSBImageProcessingDescriptor(1.0f, 0.2f, 0.9f, 0.5f);
670678
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));
679+
ImageData newData = new ImageData(width, height, 32, new PaletteData(0xFF, 0xFF00, 0xFF0000));
676680
newData.alpha = data.alpha;
677681
newData.alphaData = data.alphaData;
678682
newData.maskData = data.maskData;
@@ -692,7 +696,6 @@ private ImageData applyDisableImageData(ImageData data, int height, int width) {
692696
int greenShift = palette.greenShift;
693697
int blueShift = palette.blueShift;
694698
for (int y=0; y<height; y++) {
695-
int offset = y * newData.bytesPerLine;
696699
data.getPixels(0, y, width, scanline, 0);
697700
if (mask != null) mask.getPixels(0, y, width, maskScanline, 0);
698701
for (int x=0; x<width; x++) {
@@ -711,14 +714,10 @@ private ImageData applyDisableImageData(ImageData data, int height, int width) {
711714
green = palette.colors[pixel].green;
712715
blue = palette.colors[pixel].blue;
713716
}
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-
}
717+
RGBA result = disabledImageProcessor.adaptPixelValue(red, green, blue, data.getAlpha(x, y));
718+
newData.setAlpha(x, y, result.alpha);
719+
newData.setPixel(x, y, newData.palette.getPixel(result.rgb));
720720
}
721-
offset++;
722721
}
723722
}
724723
return newData;

0 commit comments

Comments
 (0)