Skip to content

Commit 1a40098

Browse files
committed
fixed problems with repainting icons on macos
1 parent 079e4ba commit 1a40098

4 files changed

Lines changed: 215 additions & 20 deletions

File tree

src/main/java/com/ss/editor/JFXApplication.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
package com.ss.editor;
22

33
import static com.jme3x.jfx.injfx.JmeToJFXIntegrator.bind;
4-
import static com.jme3x.jfx.injfx.processor.FrameTransferSceneProcessor.TransferMode.*;
4+
import static com.jme3x.jfx.injfx.processor.FrameTransferSceneProcessor.TransferMode.ON_CHANGES;
55
import static com.ss.rlib.util.ObjectUtils.notNull;
66
import static java.nio.file.Files.newOutputStream;
77
import com.jme3.renderer.Renderer;
@@ -30,14 +30,14 @@
3030
import com.ss.editor.ui.dialog.ConfirmDialog;
3131
import com.ss.editor.ui.scene.EditorFXScene;
3232
import com.ss.editor.util.OpenGLVersion;
33+
import com.ss.editor.util.svg.SvgImageLoaderFactory;
3334
import com.ss.rlib.logging.Logger;
3435
import com.ss.rlib.logging.LoggerManager;
3536
import com.ss.rlib.manager.InitializeManager;
3637
import com.ss.rlib.util.ArrayUtils;
3738
import com.ss.rlib.util.array.Array;
3839
import com.ss.rlib.util.array.ArrayFactory;
3940
import com.ss.rlib.util.array.ConcurrentArray;
40-
import de.codecentric.centerdevice.javafxsvg.SvgImageLoaderFactory;
4141
import javafx.application.Application;
4242
import javafx.application.Platform;
4343
import javafx.collections.ObservableList;

src/main/java/com/ss/editor/manager/FileIconManager.java

Lines changed: 13 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import com.ss.editor.config.EditorConfig;
99
import com.ss.editor.ui.css.CssColorTheme;
1010
import com.ss.editor.util.EditorUtil;
11+
import com.ss.editor.util.svg.SvgImageLoader;
1112
import com.ss.rlib.logging.Logger;
1213
import com.ss.rlib.logging.LoggerManager;
1314
import com.ss.rlib.manager.InitializeManager;
@@ -18,9 +19,6 @@
1819
import com.ss.rlib.util.dictionary.IntegerDictionary;
1920
import com.ss.rlib.util.dictionary.ObjectDictionary;
2021
import javafx.scene.image.Image;
21-
import javafx.scene.image.PixelReader;
22-
import javafx.scene.image.PixelWriter;
23-
import javafx.scene.image.WritableImage;
2422
import javafx.scene.paint.Color;
2523
import org.jetbrains.annotations.NotNull;
2624
import org.jetbrains.annotations.Nullable;
@@ -321,29 +319,26 @@ private Image buildImage(@NotNull final String url, @Nullable final InputStream
321319
try {
322320

323321
final Color iconColor = theme.getIconColor();
322+
final Image coloredImage;
324323

325-
final int width = (int) image.getWidth();
326-
final int height = (int) image.getHeight();
324+
SvgImageLoader.OVERRIDE_COLOR.set(iconColor);
325+
try {
327326

328-
final WritableImage wimage = new WritableImage(width, height);
329-
330-
final PixelWriter pixelWriter = wimage.getPixelWriter();
331-
final PixelReader pixelReader = image.getPixelReader();
332-
333-
for (int i = 0; i < width; i++) {
334-
for (int j = 0; j < height; j++) {
335-
final Color color = pixelReader.getColor(i, j);
336-
if (color.getOpacity() > 0.1) {
337-
pixelWriter.setColor(i, j, new Color(iconColor.getRed(), iconColor.getGreen(), iconColor.getBlue(), color.getOpacity()));
338-
}
327+
if (in != null) {
328+
coloredImage = new Image(in, size, size, false, true);
329+
} else {
330+
coloredImage = new Image(url, size, size, false, true);
339331
}
332+
} finally {
333+
SvgImageLoader.OVERRIDE_COLOR.set(null);
340334
}
341335

342-
originalImageCache.put(wimage, image);
336+
originalImageCache.put(coloredImage, image);
343337

344-
return wimage;
338+
return coloredImage;
345339

346340
} catch (final Throwable e) {
341+
e.printStackTrace();
347342
supportedRepaint = false;
348343
}
349344
}
Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
package com.ss.editor.util.svg;
2+
3+
import static org.apache.batik.transcoder.SVGAbstractTranscoder.KEY_HEIGHT;
4+
import static org.apache.batik.transcoder.SVGAbstractTranscoder.KEY_WIDTH;
5+
import com.sun.javafx.iio.ImageFrame;
6+
import com.sun.javafx.iio.ImageStorage;
7+
import de.codecentric.centerdevice.javafxsvg.BufferedImageTranscoder;
8+
import de.codecentric.centerdevice.javafxsvg.FixedPixelDensityImageFrame;
9+
import de.codecentric.centerdevice.javafxsvg.ScreenHelper;
10+
import javafx.scene.paint.Color;
11+
import javafx.stage.Screen;
12+
import org.apache.batik.transcoder.TranscoderException;
13+
import org.apache.batik.transcoder.TranscoderInput;
14+
import org.jetbrains.annotations.NotNull;
15+
16+
import java.awt.image.BufferedImage;
17+
import java.io.IOException;
18+
import java.io.InputStream;
19+
import java.nio.ByteBuffer;
20+
21+
/**
22+
* @author JavaSaBr
23+
*/
24+
public class SvgImageLoader extends de.codecentric.centerdevice.javafxsvg.SvgImageLoader {
25+
26+
@NotNull
27+
public static ThreadLocal<Color> OVERRIDE_COLOR = new ThreadLocal<>();
28+
29+
private static final int DEFAULT_SIZE = 400;
30+
private static final int BYTES_PER_PIXEL = 4; // RGBA
31+
32+
private static Float pixelScale;
33+
34+
@NotNull
35+
private final InputStream input;
36+
37+
protected SvgImageLoader(@NotNull final InputStream input) {
38+
super(input);
39+
this.input = input;
40+
}
41+
42+
@Override
43+
public float getPixelScale() {
44+
45+
if (pixelScale == null) {
46+
pixelScale = calculateMaxRenderScale();
47+
}
48+
49+
return pixelScale;
50+
}
51+
52+
@Override
53+
public ImageFrame load(int imageIndex, int width, int height, boolean preserveAspectRatio, boolean smooth)
54+
throws IOException {
55+
if (0 != imageIndex) {
56+
return null;
57+
}
58+
59+
int imageWidth = width > 0 ? width : DEFAULT_SIZE;
60+
int imageHeight = height > 0 ? height : DEFAULT_SIZE;
61+
62+
try {
63+
return createImageFrame(imageWidth, imageHeight, getPixelScale());
64+
} catch (final TranscoderException ex) {
65+
throw new IOException(ex);
66+
}
67+
}
68+
69+
@Override
70+
public float calculateMaxRenderScale() {
71+
72+
float maxRenderScale = 0;
73+
74+
ScreenHelper.ScreenAccessor accessor = ScreenHelper.getScreenAccessor();
75+
76+
for (final Screen screen : Screen.getScreens()) {
77+
maxRenderScale = Math.max(maxRenderScale, accessor.getRenderScale(screen));
78+
}
79+
80+
return maxRenderScale;
81+
}
82+
83+
@NotNull
84+
private ImageFrame createImageFrame(final int width, final int height, final float pixelScale)
85+
throws TranscoderException {
86+
87+
final BufferedImage bufferedImage = getTranscodedImage(width * pixelScale, height * pixelScale);
88+
final ByteBuffer imageData = getImageData(bufferedImage);
89+
90+
return new FixedPixelDensityImageFrame(ImageStorage.ImageType.RGBA, imageData, bufferedImage.getWidth(),
91+
bufferedImage.getHeight(), getStride(bufferedImage), null, pixelScale, null);
92+
}
93+
94+
private BufferedImage getTranscodedImage(final float width, final float height) throws TranscoderException {
95+
final BufferedImageTranscoder trans = new BufferedImageTranscoder(BufferedImage.TYPE_INT_ARGB);
96+
trans.addTranscodingHint(KEY_WIDTH, width);
97+
trans.addTranscodingHint(KEY_HEIGHT, height);
98+
trans.transcode(new TranscoderInput(this.input), null);
99+
return trans.getBufferedImage();
100+
}
101+
102+
private int getStride(@NotNull final BufferedImage bufferedImage) {
103+
return bufferedImage.getWidth() * BYTES_PER_PIXEL;
104+
}
105+
106+
/**
107+
* Extract bytes pixels from the image.
108+
*
109+
* @param bufferedImage the image.
110+
* @return the bytes pixels.
111+
*/
112+
@NotNull
113+
private ByteBuffer getImageData(@NotNull final BufferedImage bufferedImage) {
114+
115+
final int[] argbData = bufferedImage.getRGB(0, 0, bufferedImage.getWidth(),
116+
bufferedImage.getHeight(), null, 0, bufferedImage.getWidth());
117+
final byte[] imageData = new byte[getStride(bufferedImage) * bufferedImage.getHeight()];
118+
119+
copyColorToBytes(argbData, imageData);
120+
121+
return ByteBuffer.wrap(imageData);
122+
}
123+
124+
/**
125+
* Copy pixels from image to byte array.
126+
*
127+
* @param argbData the argb pixels data.
128+
* @param imageData the bytes pixels data.
129+
*/
130+
private void copyColorToBytes(final int[] argbData, final byte[] imageData) {
131+
132+
if (argbData.length * BYTES_PER_PIXEL != imageData.length) {
133+
throw new ArrayIndexOutOfBoundsException();
134+
}
135+
136+
final Color overrideColor = OVERRIDE_COLOR.get();
137+
138+
for (int i = 0; i < argbData.length; i++) {
139+
140+
final int argb = argbData[i];
141+
142+
int alpha = argb >>> 24;
143+
int red = (argb >> 16) & 0xff;
144+
int green = (argb >> 8) & 0xff;
145+
int blue = (argb) & 0xff;
146+
147+
if (overrideColor != null && alpha < 255) {
148+
red = (int) (overrideColor.getRed() * 255);
149+
green = (int) (overrideColor.getGreen() * 255);
150+
blue = (int) (overrideColor.getBlue() * 255);
151+
}
152+
153+
int dataOffset = BYTES_PER_PIXEL * i;
154+
imageData[dataOffset] = (byte) red;
155+
imageData[dataOffset + 1] = (byte) green;
156+
imageData[dataOffset + 2] = (byte) blue;
157+
imageData[dataOffset + 3] = (byte) alpha;
158+
}
159+
}
160+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
package com.ss.editor.util.svg;
2+
3+
import com.sun.javafx.iio.ImageFormatDescription;
4+
import com.sun.javafx.iio.ImageLoader;
5+
import com.sun.javafx.iio.ImageLoaderFactory;
6+
import com.sun.javafx.iio.ImageStorage;
7+
import de.codecentric.centerdevice.javafxsvg.SvgDescriptor;
8+
import org.jetbrains.annotations.NotNull;
9+
10+
import java.io.IOException;
11+
import java.io.InputStream;
12+
13+
/**
14+
* @author JavaSaBr
15+
*/
16+
public class SvgImageLoaderFactory implements ImageLoaderFactory {
17+
18+
@NotNull
19+
private static final ImageLoaderFactory instance = new SvgImageLoaderFactory();
20+
21+
public static void install() {
22+
ImageStorage.addImageLoaderFactory(instance);
23+
}
24+
25+
@NotNull
26+
public static ImageLoaderFactory getInstance() {
27+
return instance;
28+
}
29+
30+
@Override
31+
public ImageFormatDescription getFormatDescription() {
32+
return SvgDescriptor.getInstance();
33+
}
34+
35+
@Override
36+
public ImageLoader createImageLoader(@NotNull InputStream input) throws IOException {
37+
return new SvgImageLoader(input);
38+
}
39+
40+
}

0 commit comments

Comments
 (0)