From e3343b4a4c18a84fab97ed43231b9e43b15f1b9b Mon Sep 17 00:00:00 2001 From: Brice Dutheil Date: Fri, 10 Jun 2022 00:16:15 +0200 Subject: [PATCH 1/7] feat(flamegraph): Introduce a better frame coloring when highlighting --- .../fireplace/DimmingFrameColorProvider.java | 84 +++++++++ .../github/bric3/fireplace/FlameGraphTab.java | 19 +-- .../bric3/fireplace/core/ui/Colors.java | 160 ++++++++++++++++-- .../flamegraph/FrameColorProvider.java | 5 +- .../flamegraph/FrameFontProvider.java | 1 + .../bric3/fireplace/FirePlaceSwtMain.java | 7 +- 6 files changed, 247 insertions(+), 29 deletions(-) create mode 100644 fireplace-app/src/main/java/io/github/bric3/fireplace/DimmingFrameColorProvider.java diff --git a/fireplace-app/src/main/java/io/github/bric3/fireplace/DimmingFrameColorProvider.java b/fireplace-app/src/main/java/io/github/bric3/fireplace/DimmingFrameColorProvider.java new file mode 100644 index 00000000..c2dbb178 --- /dev/null +++ b/fireplace-app/src/main/java/io/github/bric3/fireplace/DimmingFrameColorProvider.java @@ -0,0 +1,84 @@ +/* + * Copyright 2021 Datadog, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.github.bric3.fireplace; + +import io.github.bric3.fireplace.core.ui.Colors; +import io.github.bric3.fireplace.flamegraph.FrameBox; +import io.github.bric3.fireplace.flamegraph.FrameColorProvider; + +import java.awt.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Function; + +import static io.github.bric3.fireplace.flamegraph.FrameRenderingFlags.isHighlightedFrame; +import static io.github.bric3.fireplace.flamegraph.FrameRenderingFlags.isHighlighting; + +class DimmingFrameColorProvider implements FrameColorProvider { + public static final Color DIMMED_TEXT_DARK = Colors.rgba(255, 255, 255, 0.51f); + public static final Color DIMMED_TEXT_LIGHT = Colors.rgba(28, 43, 52, 0.68f); + public static final Color ROOT_NODE_LIGHT = new Color(0xffeaf6fc); + public static final Color ROOT_NODE_DARK = new Color(0xff091222); + private final Function, Color> baseColorFunction; + private final ColorModel reusableDataStructure = new ColorModel(null, null); + + private final ConcurrentHashMap dimmedColorCache = new ConcurrentHashMap<>(); + + public DimmingFrameColorProvider(Function, Color> baseColorFunction) { + this.baseColorFunction = baseColorFunction; + } + + + @Override + public ColorModel getColors(FrameBox frame, int flags) { + Color backgroundColor; + Color foreground; + + if (isRootNode(frame)) { + backgroundColor = Colors.isDarkMode() ? + ROOT_NODE_DARK : + ROOT_NODE_LIGHT; + } else { + backgroundColor = baseColorFunction.apply(frame); + } + + if (isDimmed(frame, flags)) { + backgroundColor = cachedDim(backgroundColor); + foreground = Colors.isDarkMode() ? + DIMMED_TEXT_DARK : + DIMMED_TEXT_LIGHT; + } else { + foreground = Colors.foregroundColor(backgroundColor); + } + + return reusableDataStructure.set( + backgroundColor, + foreground + ); + } + + private Color cachedDim(Color color) { + return dimmedColorCache.computeIfAbsent(color, Colors::dim); + } + + private boolean isDimmed(FrameBox frame, int flags) { + return isHighlighting(flags) && !isHighlightedFrame(flags); + } + + private boolean isRootNode(FrameBox frame) { + return frame.stackDepth == 0; + } +} diff --git a/fireplace-app/src/main/java/io/github/bric3/fireplace/FlameGraphTab.java b/fireplace-app/src/main/java/io/github/bric3/fireplace/FlameGraphTab.java index 1557248e..880ebc10 100644 --- a/fireplace-app/src/main/java/io/github/bric3/fireplace/FlameGraphTab.java +++ b/fireplace-app/src/main/java/io/github/bric3/fireplace/FlameGraphTab.java @@ -13,6 +13,7 @@ import io.github.bric3.fireplace.core.ui.Colors.Palette; import io.github.bric3.fireplace.flamegraph.ColorMapper; import io.github.bric3.fireplace.flamegraph.FlamegraphView; +import io.github.bric3.fireplace.flamegraph.FrameFontProvider; import io.github.bric3.fireplace.flamegraph.FrameTextsProvider; import io.github.bric3.fireplace.flamegraph.ZoomAnimation; import org.openjdk.jmc.common.util.FormatToolkit; @@ -57,10 +58,10 @@ public FlameGraphTab() { colorModeJComboBox.setSelectedItem(defaultFrameColorMode); ActionListener updateColorSettingsListener = e -> { - jfrFlamegraphView.setColorFunction( - ((JfrFrameColorMode) colorModeJComboBox.getSelectedItem()) - .colorMapperUsing(ColorMapper.ofObjectHashUsing( - ((Palette) colorPaletteJComboBox.getSelectedItem()).colors()))); + var frameBoxColorFunction = ((JfrFrameColorMode) colorModeJComboBox.getSelectedItem()) + .colorMapperUsing(ColorMapper.ofObjectHashUsing( + ((Palette) colorPaletteJComboBox.getSelectedItem()).colors())); + jfrFlamegraphView.setFrameColorProvider(new DimmingFrameColorProvider<>(frameBoxColorFunction)); jfrFlamegraphView.requestRepaint(); }; colorPaletteJComboBox.addActionListener(updateColorSettingsListener); @@ -175,12 +176,7 @@ public FlameGraphTab() { add(controlPanel, BorderLayout.NORTH); add(wrapper, BorderLayout.CENTER); } - - public FlameGraphTab(StacktraceTreeModel stacktraceTreeModel) { - this(); - setStacktraceTreeModel(stacktraceTreeModel); - } - + public void setStacktraceTreeModel(StacktraceTreeModel stacktraceTreeModel) { dataApplier = dataApplier(stacktraceTreeModel); dataApplier.accept(jfrFlamegraphView); @@ -205,7 +201,8 @@ private Consumer> dataApplier(StacktraceTreeModel stacktrac frame -> frame.isRoot() ? "" : FormatToolkit.getHumanReadable(frame.actualNode.getFrame().getMethod(), false, false, false, false, true, false), frame -> frame.isRoot() ? "" : frame.actualNode.getFrame().getMethod().getMethodName() ), - defaultFrameColorMode.colorMapperUsing(ColorMapper.ofObjectHashUsing(defaultColorPalette.colors())), + new DimmingFrameColorProvider(defaultFrameColorMode.colorMapperUsing(ColorMapper.ofObjectHashUsing(defaultColorPalette.colors()))), + FrameFontProvider.defaultFontProvider(), frame -> { if (frame.isRoot()) { return ""; diff --git a/fireplace-swing/src/main/java/io/github/bric3/fireplace/core/ui/Colors.java b/fireplace-swing/src/main/java/io/github/bric3/fireplace/core/ui/Colors.java index bc1b3b83..83626fee 100644 --- a/fireplace-swing/src/main/java/io/github/bric3/fireplace/core/ui/Colors.java +++ b/fireplace-swing/src/main/java/io/github/bric3/fireplace/core/ui/Colors.java @@ -20,6 +20,18 @@ * Various color related utilities. */ public class Colors { + /** + * Hue + */ + public static final int H = 0; + /** + * Saturation + */ + public static final int S = 1; + /** + * Luminance + */ + public static final int L = 2; private static volatile boolean darkMode = false; /** @@ -35,40 +47,64 @@ public class Colors { */ public static final int DARK_PERCEIVED_BRIGHTNESS_THRESHOLD = gammaFunction(0.45); - /** Color BLACK with alpha 0xD0. */ + /** + * Color BLACK with alpha 0xD0. + */ public static Color translucent_black_D0 = new Color(0xD0000000, true); - /** Color BLACK with alpha 0xB0. */ + /** + * Color BLACK with alpha 0xB0. + */ public static Color translucent_black_B0 = new Color(0xB0000000, true); - /** Color BLACK with alpha 0x80. */ + /** + * Color BLACK with alpha 0x80. + */ public static Color translucent_black_80 = new Color(0x80000000, true); - /** Color BLACK with alpha 0x60. */ + /** + * Color BLACK with alpha 0x60. + */ public static Color translucent_black_60 = new Color(0x60000000, true); - /** Color BLACK with alpha 0x40. */ + /** + * Color BLACK with alpha 0x40. + */ public static Color translucent_black_40 = new Color(0x40000000, true); - /** Color BLACK with alpha 0x20. */ + /** + * Color BLACK with alpha 0x20. + */ public static Color translucent_black_20 = new Color(0x20000000, true); - /** Color WHITE with alpha 0xD0. */ + /** + * Color WHITE with alpha 0xD0. + */ public static Color translucent_white_D0 = new Color(0xD0FFFFFF, true); - /** Color WHITE with alpha 0xB0. */ + /** + * Color WHITE with alpha 0xB0. + */ public static Color translucent_white_B0 = new Color(0xB0FFFFFF, true); - /** Color WHITE with alpha 0x80. */ + /** + * Color WHITE with alpha 0x80. + */ public static Color translucent_white_80 = new Color(0x80FFFFFF, true); - /** Color WHITE with alpha 0x60. */ + /** + * Color WHITE with alpha 0x60. + */ public static Color translucent_white_60 = new Color(0x60FFFFFF, true); - /** Color WHITE with alpha 0x40. */ + /** + * Color WHITE with alpha 0x40. + */ public static Color translucent_white_40 = new Color(0x40FFFFFF, true); - /** Color WHITE with alpha 0x20. */ + /** + * Color WHITE with alpha 0x20. + */ public static Color translucent_white_20 = new Color(0x20FFFFFF, true); /** @@ -175,7 +211,7 @@ public enum Palette { ), /** - * Light rainbow color palette. + * Light rainbow color palette. */ LIGHT_BLUE_GREEN_ORANGE_RED( /*blue*/ "#003565", "#004F99", "#1272CB", "#0084FF", @@ -187,7 +223,7 @@ public enum Palette { ), /** - * Datadog color palette. + * Datadog color palette. */ DATADOG( new Color(0x3399CC), @@ -213,7 +249,7 @@ public enum Palette { ), /** - * Pyroscope color palette. + * Pyroscope color palette. */ PYROSCOPE( new Color(0xDF8B53), @@ -356,6 +392,100 @@ public static Color blend(Color c0, Color c1) { return new Color((int) r, (int) g, (int) b, (int) a); } + public static Color dim(Color color) { + var hsl = rgbToHsl(color); + + if (darkMode) { + // if color is grayish, keep the saturation, otherwise set it to 0.2 + hsl[S] = hsl[S] < 0.1f ? hsl[S] : 0.2f; + hsl[L] = 0.2f; + } else { + // if color is grayish, keep the saturation, otherwise set it to 0.4 + hsl[S] = hsl[S] < 0.2 ? hsl[S] : 0.4f; + hsl[L] = 0.93f; + } + + return hslToRgb(hsl[0], hsl[1], hsl[2], color.getAlpha() / 255.0f); + } + + + /** + * Convert an RGB Color to it corresponding HSL components. + *

+ * From d3-colors. + * + * @param color The color to convert + * @return an array containing the 3 HSL values. + */ + public static float[] rgbToHsl(Color color) { + float r = color.getRed() / 255.0f; + float g = color.getGreen() / 255.0f; + float b = color.getBlue() / 255.0f; + + float min = Math.min(r, Math.min(g, b)), + max = Math.max(r, Math.max(g, b)), + h = 0f, + s = max - min, + l = (max + min) / 2; + if (s >= 0) { + if (r == max) { + h = (g - b) / s + ((g < b) ? 6 : 0); + } else if (g == max) { + h = (b - r) / s + 2; + } else { + h = (r - g) / s + 4; + } + s /= l < 0.5 ? max + min : 2 - max - min; + h *= 60; + } else { + s = ((l > 0) && (l < 1)) ? 0 : h; + } + + return new float[]{h, s, l}; + } + + /** + * Convert HSL values to a RGB Color. + * + * From d3-colors. + * @param h Hue is specified as degrees in the range 0 - 360. + * @param s Saturation is specified as a percentage in the range 1 - 100. + * @param l Luminance is specified as a percentage in the range 1 - 100. + * @param alpha the alpha value between 0 - 1 + * @return the RGB Color object + */ + public static Color hslToRgb(float h, float s, float l, float alpha) { + h = h % 360 + ((h < 0) ? 360 : 0); + s = Float.isNaN(h) || Float.isNaN(s) ? 0 : s; + + float m2 = l + (l < 0.5 ? l : 1 - l) * s, + m1 = 2 * l - m2; + + return new Color( + ((int) (alpha * 255)) << 24 + | hueToRgb(h >= 240 ? h - 240 : h + 120, m1, m2) << 16 + | hueToRgb(h, m1, m2) << 8 + | hueToRgb(h < 120 ? h + 240 : h - 120, m1, m2) + + ); + } + + /* From FvD 13.37, CSS Color Module Level 3 */ + private static int hueToRgb(float h, float m1, float m2) { + return (int) (((h < 60) ? (m1 + (m2 - m1) * h / 60) + : ((h < 180) ? m2 + : ((h < 240) ? (m1 + (m2 - m1) * (240 - h) / 60) + : m1 + ) + ) + ) * 255); + } + + public static Color rgba(int r, int g, int b, float a) { + return new Color(r, g, b, (int) (a * 255)); + } + + /** * Utility method to print out the colors for the look and feel defaults. */ diff --git a/fireplace-swing/src/main/java/io/github/bric3/fireplace/flamegraph/FrameColorProvider.java b/fireplace-swing/src/main/java/io/github/bric3/fireplace/flamegraph/FrameColorProvider.java index 2d3c5a1d..ceef36d8 100644 --- a/fireplace-swing/src/main/java/io/github/bric3/fireplace/flamegraph/FrameColorProvider.java +++ b/fireplace-swing/src/main/java/io/github/bric3/fireplace/flamegraph/FrameColorProvider.java @@ -33,6 +33,7 @@ * * @param The type of the frame node (depends on the source of profiling data). */ +@FunctionalInterface public interface FrameColorProvider { class ColorModel { public Color background; @@ -44,12 +45,12 @@ class ColorModel { * @param background The background color of the frame. * @param foreground The foreground color of the frame. */ - ColorModel(Color background, Color foreground) { + public ColorModel(Color background, Color foreground) { this.background = background; this.foreground = foreground; } - ColorModel set(Color background, Color foreground) { + public ColorModel set(Color background, Color foreground) { this.background = background; this.foreground = foreground; return this; diff --git a/fireplace-swing/src/main/java/io/github/bric3/fireplace/flamegraph/FrameFontProvider.java b/fireplace-swing/src/main/java/io/github/bric3/fireplace/flamegraph/FrameFontProvider.java index d154bf77..56afaff7 100644 --- a/fireplace-swing/src/main/java/io/github/bric3/fireplace/flamegraph/FrameFontProvider.java +++ b/fireplace-swing/src/main/java/io/github/bric3/fireplace/flamegraph/FrameFontProvider.java @@ -26,6 +26,7 @@ * * @param The type of the frame node (depends on the source of profiling data). */ +@FunctionalInterface public interface FrameFontProvider { /** diff --git a/fireplace-swt-experiment/src/main/java/io/github/bric3/fireplace/FirePlaceSwtMain.java b/fireplace-swt-experiment/src/main/java/io/github/bric3/fireplace/FirePlaceSwtMain.java index bc5fb330..66773f72 100644 --- a/fireplace-swt-experiment/src/main/java/io/github/bric3/fireplace/FirePlaceSwtMain.java +++ b/fireplace-swt-experiment/src/main/java/io/github/bric3/fireplace/FirePlaceSwtMain.java @@ -4,6 +4,8 @@ import io.github.bric3.fireplace.flamegraph.ColorMapper; import io.github.bric3.fireplace.flamegraph.FlamegraphView; import io.github.bric3.fireplace.flamegraph.FrameBox; +import io.github.bric3.fireplace.flamegraph.FrameColorProvider; +import io.github.bric3.fireplace.flamegraph.FrameFontProvider; import io.github.bric3.fireplace.flamegraph.FrameTextsProvider; import org.eclipse.swt.SWT; import org.eclipse.swt.awt.SWT_AWT; @@ -186,7 +188,10 @@ private void loadJfr(String[] args, Label text, FlamegraphView fg) { frame -> frame.isRoot() ? "" : FormatToolkit.getHumanReadable(frame.actualNode.getFrame().getMethod(), false, false, false, false, true, false), frame -> frame.isRoot() ? "" : frame.actualNode.getFrame().getMethod().getMethodName() ), - frame -> ColorMapper.ofObjectHashUsing(Palette.DATADOG.colors()).apply(frame.actualNode.getFrame().getMethod().getType().getPackage()), + FrameColorProvider.defaultColorProvider( + frame -> ColorMapper.ofObjectHashUsing(Palette.DATADOG.colors()).apply(frame.actualNode.getFrame().getMethod().getType().getPackage()) + ), + FrameFontProvider.defaultFontProvider(), frame -> "" ); flameGraph.requestRepaint(); From ba30317454a2c7608889f45a6b227c72b98fa929 Mon Sep 17 00:00:00 2001 From: Brice Dutheil Date: Fri, 10 Jun 2022 09:14:46 +0200 Subject: [PATCH 2/7] feat(flamegraph): Enable new dimming for focus as well --- .../fireplace/DimmingFrameColorProvider.java | 36 +++++++++++++++---- 1 file changed, 30 insertions(+), 6 deletions(-) diff --git a/fireplace-app/src/main/java/io/github/bric3/fireplace/DimmingFrameColorProvider.java b/fireplace-app/src/main/java/io/github/bric3/fireplace/DimmingFrameColorProvider.java index c2dbb178..53525ba6 100644 --- a/fireplace-app/src/main/java/io/github/bric3/fireplace/DimmingFrameColorProvider.java +++ b/fireplace-app/src/main/java/io/github/bric3/fireplace/DimmingFrameColorProvider.java @@ -24,6 +24,8 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.function.Function; +import static io.github.bric3.fireplace.flamegraph.FrameRenderingFlags.isFocusedFrame; +import static io.github.bric3.fireplace.flamegraph.FrameRenderingFlags.isFocusing; import static io.github.bric3.fireplace.flamegraph.FrameRenderingFlags.isHighlightedFrame; import static io.github.bric3.fireplace.flamegraph.FrameRenderingFlags.isHighlighting; @@ -55,7 +57,7 @@ public ColorModel getColors(FrameBox frame, int flags) { backgroundColor = baseColorFunction.apply(frame); } - if (isDimmed(frame, flags)) { + if (shouldDim(flags)) { backgroundColor = cachedDim(backgroundColor); foreground = Colors.isDarkMode() ? DIMMED_TEXT_DARK : @@ -70,14 +72,36 @@ public ColorModel getColors(FrameBox frame, int flags) { ); } - private Color cachedDim(Color color) { - return dimmedColorCache.computeIfAbsent(color, Colors::dim); - } + /** + * Dim only if not highlighted or not focused + * + * - highlighting and not highlighted => dim + * - focusing and not focused => dim + * - highlighting and focusing + * - highlighted => nope + * - focusing => nope + */ + private boolean shouldDim(int flags) { + var highlighting = isHighlighting(flags); + var highlightedFrame = isHighlightedFrame(flags); + var focusing = isFocusing(flags); + var focusedFrame = isFocusedFrame(flags); + + + var dimmedForHighlighting = highlighting && !highlightedFrame; + var dimmedForFocus = focusing && !focusedFrame; - private boolean isDimmed(FrameBox frame, int flags) { - return isHighlighting(flags) && !isHighlightedFrame(flags); + + return (dimmedForHighlighting || dimmedForFocus) + && !(highlighting + && focusing + && (highlightedFrame || focusedFrame)); } + private Color cachedDim(Color color) { + return dimmedColorCache.computeIfAbsent(color, Colors::dim); + } + private boolean isRootNode(FrameBox frame) { return frame.stackDepth == 0; } From b51a1c65498b03a93e3c7a77cc57fce9b70d3a37 Mon Sep 17 00:00:00 2001 From: Brice Dutheil Date: Fri, 10 Jun 2022 09:50:48 +0200 Subject: [PATCH 3/7] fix(flamegraph): Frame color caching works with dark/light switch --- .../fireplace/DimmingFrameColorProvider.java | 5 ++- .../bric3/fireplace/core/ui/Colors.java | 44 ++++++++++++------- 2 files changed, 31 insertions(+), 18 deletions(-) diff --git a/fireplace-app/src/main/java/io/github/bric3/fireplace/DimmingFrameColorProvider.java b/fireplace-app/src/main/java/io/github/bric3/fireplace/DimmingFrameColorProvider.java index 53525ba6..ed0d5f26 100644 --- a/fireplace-app/src/main/java/io/github/bric3/fireplace/DimmingFrameColorProvider.java +++ b/fireplace-app/src/main/java/io/github/bric3/fireplace/DimmingFrameColorProvider.java @@ -49,7 +49,8 @@ public ColorModel getColors(FrameBox frame, int flags) { Color backgroundColor; Color foreground; - if (isRootNode(frame)) { + var rootNode = isRootNode(frame); + if (rootNode) { backgroundColor = Colors.isDarkMode() ? ROOT_NODE_DARK : ROOT_NODE_LIGHT; @@ -57,7 +58,7 @@ public ColorModel getColors(FrameBox frame, int flags) { backgroundColor = baseColorFunction.apply(frame); } - if (shouldDim(flags)) { + if (!rootNode && shouldDim(flags)) { backgroundColor = cachedDim(backgroundColor); foreground = Colors.isDarkMode() ? DIMMED_TEXT_DARK : diff --git a/fireplace-swing/src/main/java/io/github/bric3/fireplace/core/ui/Colors.java b/fireplace-swing/src/main/java/io/github/bric3/fireplace/core/ui/Colors.java index 83626fee..80663524 100644 --- a/fireplace-swing/src/main/java/io/github/bric3/fireplace/core/ui/Colors.java +++ b/fireplace-swing/src/main/java/io/github/bric3/fireplace/core/ui/Colors.java @@ -392,20 +392,31 @@ public static Color blend(Color c0, Color c1) { return new Color((int) r, (int) g, (int) b, (int) a); } + /** + * Dim the given color and returns a {@link #darkMode} aware color. + * + * @param color The color to dim + * @return The dimmed color ({@link #darkMode} aware) + * @see DarkLightColor + */ public static Color dim(Color color) { - var hsl = rgbToHsl(color); - - if (darkMode) { - // if color is grayish, keep the saturation, otherwise set it to 0.2 - hsl[S] = hsl[S] < 0.1f ? hsl[S] : 0.2f; - hsl[L] = 0.2f; - } else { - // if color is grayish, keep the saturation, otherwise set it to 0.4 - hsl[S] = hsl[S] < 0.2 ? hsl[S] : 0.4f; - hsl[L] = 0.93f; - } - - return hslToRgb(hsl[0], hsl[1], hsl[2], color.getAlpha() / 255.0f); + var hslLight = hslComponents(color); + var hslDark = Arrays.copyOf(hslLight, hslLight.length); + + // if (darkMode) { + // if color is grayish, keep the saturation, otherwise set it to 0.2 + hslDark[S] = hslDark[S] < 0.1f ? hslDark[S] : 0.2f; + hslDark[L] = 0.2f; + // } else { + // if color is grayish, keep the saturation, otherwise set it to 0.4 + hslLight[S] = hslLight[S] < 0.2 ? hslLight[S] : 0.4f; + hslLight[L] = 0.93f; + // } + + return new DarkLightColor( + hsl(hslLight[0], hslLight[1], hslLight[2], color.getAlpha() / 255.0f), + hsl(hslDark[0], hslDark[1], hslDark[2], color.getAlpha() / 255.0f) + ); } @@ -417,7 +428,7 @@ public static Color dim(Color color) { * @param color The color to convert * @return an array containing the 3 HSL values. */ - public static float[] rgbToHsl(Color color) { + public static float[] hslComponents(Color color) { float r = color.getRed() / 255.0f; float g = color.getGreen() / 255.0f; float b = color.getBlue() / 255.0f; @@ -446,15 +457,16 @@ public static float[] rgbToHsl(Color color) { /** * Convert HSL values to a RGB Color. - * + *

* From d3-colors. + * * @param h Hue is specified as degrees in the range 0 - 360. * @param s Saturation is specified as a percentage in the range 1 - 100. * @param l Luminance is specified as a percentage in the range 1 - 100. * @param alpha the alpha value between 0 - 1 * @return the RGB Color object */ - public static Color hslToRgb(float h, float s, float l, float alpha) { + public static Color hsl(float h, float s, float l, float alpha) { h = h % 360 + ((h < 0) ? 360 : 0); s = Float.isNaN(h) || Float.isNaN(s) ? 0 : s; From 7fe89c3d649935680336b7f9cbb1a6218bdde2ec Mon Sep 17 00:00:00 2001 From: Brice Dutheil Date: Fri, 10 Jun 2022 09:56:31 +0200 Subject: [PATCH 4/7] fix(flamegraph): Minimap colors shouldn't be dimmed --- .../io/github/bric3/fireplace/DimmingFrameColorProvider.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fireplace-app/src/main/java/io/github/bric3/fireplace/DimmingFrameColorProvider.java b/fireplace-app/src/main/java/io/github/bric3/fireplace/DimmingFrameColorProvider.java index ed0d5f26..a3b4b888 100644 --- a/fireplace-app/src/main/java/io/github/bric3/fireplace/DimmingFrameColorProvider.java +++ b/fireplace-app/src/main/java/io/github/bric3/fireplace/DimmingFrameColorProvider.java @@ -28,6 +28,7 @@ import static io.github.bric3.fireplace.flamegraph.FrameRenderingFlags.isFocusing; import static io.github.bric3.fireplace.flamegraph.FrameRenderingFlags.isHighlightedFrame; import static io.github.bric3.fireplace.flamegraph.FrameRenderingFlags.isHighlighting; +import static io.github.bric3.fireplace.flamegraph.FrameRenderingFlags.isMinimapMode; class DimmingFrameColorProvider implements FrameColorProvider { public static final Color DIMMED_TEXT_DARK = Colors.rgba(255, 255, 255, 0.51f); @@ -58,7 +59,7 @@ public ColorModel getColors(FrameBox frame, int flags) { backgroundColor = baseColorFunction.apply(frame); } - if (!rootNode && shouldDim(flags)) { + if (!rootNode && shouldDim(flags) && !isMinimapMode(flags)) { backgroundColor = cachedDim(backgroundColor); foreground = Colors.isDarkMode() ? DIMMED_TEXT_DARK : From c044eeab411af7a1b723cdba586f1ff90da3584f Mon Sep 17 00:00:00 2001 From: Brice Dutheil Date: Fri, 10 Jun 2022 10:17:23 +0200 Subject: [PATCH 5/7] chore(flamegraph): Use DarkLightColor in more occasions --- .../fireplace/DimmingFrameColorProvider.java | 22 ++++++++++--------- .../github/bric3/fireplace/FlameGraphTab.java | 6 +++-- .../bric3/fireplace/ui/GlassJPanel.java | 8 ++++++- .../core/ui/JScrollPaneWithButton.java | 12 +++++++--- 4 files changed, 32 insertions(+), 16 deletions(-) diff --git a/fireplace-app/src/main/java/io/github/bric3/fireplace/DimmingFrameColorProvider.java b/fireplace-app/src/main/java/io/github/bric3/fireplace/DimmingFrameColorProvider.java index a3b4b888..b59e3120 100644 --- a/fireplace-app/src/main/java/io/github/bric3/fireplace/DimmingFrameColorProvider.java +++ b/fireplace-app/src/main/java/io/github/bric3/fireplace/DimmingFrameColorProvider.java @@ -17,6 +17,7 @@ package io.github.bric3.fireplace; import io.github.bric3.fireplace.core.ui.Colors; +import io.github.bric3.fireplace.core.ui.DarkLightColor; import io.github.bric3.fireplace.flamegraph.FrameBox; import io.github.bric3.fireplace.flamegraph.FrameColorProvider; @@ -31,10 +32,15 @@ import static io.github.bric3.fireplace.flamegraph.FrameRenderingFlags.isMinimapMode; class DimmingFrameColorProvider implements FrameColorProvider { - public static final Color DIMMED_TEXT_DARK = Colors.rgba(255, 255, 255, 0.51f); - public static final Color DIMMED_TEXT_LIGHT = Colors.rgba(28, 43, 52, 0.68f); - public static final Color ROOT_NODE_LIGHT = new Color(0xffeaf6fc); - public static final Color ROOT_NODE_DARK = new Color(0xff091222); + public static final Color DIMMED_TEXT = new DarkLightColor( + Colors.rgba(28, 43, 52, 0.68f), + Colors.rgba(255, 255, 255, 0.51f) + ); + + public static final Color ROOT_NODE = new DarkLightColor( + new Color(0xffeaf6fc), + new Color(0xff091222) + ); private final Function, Color> baseColorFunction; private final ColorModel reusableDataStructure = new ColorModel(null, null); @@ -52,18 +58,14 @@ public ColorModel getColors(FrameBox frame, int flags) { var rootNode = isRootNode(frame); if (rootNode) { - backgroundColor = Colors.isDarkMode() ? - ROOT_NODE_DARK : - ROOT_NODE_LIGHT; + backgroundColor = ROOT_NODE; } else { backgroundColor = baseColorFunction.apply(frame); } if (!rootNode && shouldDim(flags) && !isMinimapMode(flags)) { backgroundColor = cachedDim(backgroundColor); - foreground = Colors.isDarkMode() ? - DIMMED_TEXT_DARK : - DIMMED_TEXT_LIGHT; + foreground = DIMMED_TEXT; } else { foreground = Colors.foregroundColor(backgroundColor); } diff --git a/fireplace-app/src/main/java/io/github/bric3/fireplace/FlameGraphTab.java b/fireplace-app/src/main/java/io/github/bric3/fireplace/FlameGraphTab.java index 880ebc10..8dfc1bca 100644 --- a/fireplace-app/src/main/java/io/github/bric3/fireplace/FlameGraphTab.java +++ b/fireplace-app/src/main/java/io/github/bric3/fireplace/FlameGraphTab.java @@ -11,6 +11,7 @@ import io.github.bric3.fireplace.core.ui.Colors; import io.github.bric3.fireplace.core.ui.Colors.Palette; +import io.github.bric3.fireplace.core.ui.DarkLightColor; import io.github.bric3.fireplace.flamegraph.ColorMapper; import io.github.bric3.fireplace.flamegraph.FlamegraphView; import io.github.bric3.fireplace.flamegraph.FrameFontProvider; @@ -48,7 +49,8 @@ public FlameGraphTab() { jfrFlamegraphView.configureCanvas(ToolTipManager.sharedInstance()::registerComponent); jfrFlamegraphView.putClientProperty(FlamegraphView.SHOW_STATS, true); // jfrFlameGraph.setTooltipComponentSupplier(BalloonToolTip::new); - jfrFlamegraphView.setMinimapShadeColorSupplier(() -> Colors.isDarkMode() ? Colors.translucent_black_40 : Colors.translucent_white_80); + var minimapShade = new DarkLightColor(Colors.translucent_white_80, Colors.translucent_black_40); + jfrFlamegraphView.setMinimapShadeColorSupplier(() -> minimapShade); var zoomAnimation = new ZoomAnimation(); zoomAnimation.install(jfrFlamegraphView); @@ -99,7 +101,7 @@ public FlameGraphTab() { jfrFlamegraphView.showMinimap(defaultShowMinimap); jfrFlamegraphView.configureCanvas(ToolTipManager.sharedInstance()::registerComponent); jfrFlamegraphView.putClientProperty(FlamegraphView.SHOW_STATS, true); - jfrFlamegraphView.setMinimapShadeColorSupplier(() -> Colors.isDarkMode() ? Colors.translucent_black_40 : Colors.translucent_white_80); + jfrFlamegraphView.setMinimapShadeColorSupplier(() -> minimapShade); zoomAnimation.install(jfrFlamegraphView); if (dataApplier != null) { dataApplier.accept(jfrFlamegraphView); diff --git a/fireplace-app/src/main/java/io/github/bric3/fireplace/ui/GlassJPanel.java b/fireplace-app/src/main/java/io/github/bric3/fireplace/ui/GlassJPanel.java index 2fc71ca0..0d159127 100644 --- a/fireplace-app/src/main/java/io/github/bric3/fireplace/ui/GlassJPanel.java +++ b/fireplace-app/src/main/java/io/github/bric3/fireplace/ui/GlassJPanel.java @@ -1,11 +1,17 @@ package io.github.bric3.fireplace.ui; import io.github.bric3.fireplace.core.ui.Colors; +import io.github.bric3.fireplace.core.ui.DarkLightColor; import javax.swing.*; import java.awt.*; public class GlassJPanel extends JPanel { + private static final Color TRANSLUCENT_BACKGROUND = new DarkLightColor( + Colors.translucent_white_D0, + Colors.translucent_black_80 + ); + public GlassJPanel() { this(new GridBagLayout()); } @@ -17,7 +23,7 @@ public GlassJPanel(LayoutManager layout) { @Override protected void paintComponent(Graphics g) { var g2 = (Graphics2D) g; - g2.setColor(Colors.isDarkMode() ? Colors.translucent_black_80 : Colors.translucent_white_D0); + g2.setColor(TRANSLUCENT_BACKGROUND); g2.fillRect(0, 0, getWidth(), getHeight()); } } diff --git a/fireplace-swing/src/main/java/io/github/bric3/fireplace/core/ui/JScrollPaneWithButton.java b/fireplace-swing/src/main/java/io/github/bric3/fireplace/core/ui/JScrollPaneWithButton.java index d7e01a32..1769fe87 100644 --- a/fireplace-swing/src/main/java/io/github/bric3/fireplace/core/ui/JScrollPaneWithButton.java +++ b/fireplace-swing/src/main/java/io/github/bric3/fireplace/core/ui/JScrollPaneWithButton.java @@ -42,8 +42,14 @@ private static class ScrollBackToTopLayerUI extends LayerUI { public int yGap; private final JPanel buttonContainer = new JPanel(); private final Point currentMousePoint = new Point(); - private final JButton button = new JButton(new UpArrowIcon(new Color(0xAA_3D_42_44, true), - new Color(0xAA_38_9F_D6, true))) { + private final JButton button = new JButton(new UpArrowIcon(new Color(0xAA3D4244, true), + new Color(0xAA389FD6, true))) { + + private final Color ARMED_BUTTON_COLOR = new DarkLightColor( + Color.darkGray, + Color.lightGray + ); + @Override public void updateUI() { super.updateUI(); @@ -62,7 +68,7 @@ protected void paintComponent(Graphics g) { g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); if (getModel().isArmed()) { - g2.setColor(Colors.isDarkMode() ? Color.lightGray : Color.darkGray); + g2.setColor(ARMED_BUTTON_COLOR); } else { g2.setColor(UIManager.getColor("Button.background")); } From 9d0d3f96be47025a3c5fe68ef8d6e3d34f0ad68c Mon Sep 17 00:00:00 2001 From: Brice Dutheil Date: Fri, 10 Jun 2022 10:24:13 +0200 Subject: [PATCH 6/7] chore(flamegraph): Display root frame text in bold --- .../fireplace/DimmingFrameColorProvider.java | 6 +----- .../fireplace/flamegraph/FrameFontProvider.java | 16 ++++++++++------ 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/fireplace-app/src/main/java/io/github/bric3/fireplace/DimmingFrameColorProvider.java b/fireplace-app/src/main/java/io/github/bric3/fireplace/DimmingFrameColorProvider.java index b59e3120..f0fbe1fb 100644 --- a/fireplace-app/src/main/java/io/github/bric3/fireplace/DimmingFrameColorProvider.java +++ b/fireplace-app/src/main/java/io/github/bric3/fireplace/DimmingFrameColorProvider.java @@ -56,7 +56,7 @@ public ColorModel getColors(FrameBox frame, int flags) { Color backgroundColor; Color foreground; - var rootNode = isRootNode(frame); + var rootNode = frame.isRoot(); if (rootNode) { backgroundColor = ROOT_NODE; } else { @@ -105,8 +105,4 @@ private boolean shouldDim(int flags) { private Color cachedDim(Color color) { return dimmedColorCache.computeIfAbsent(color, Colors::dim); } - - private boolean isRootNode(FrameBox frame) { - return frame.stackDepth == 0; - } } diff --git a/fireplace-swing/src/main/java/io/github/bric3/fireplace/flamegraph/FrameFontProvider.java b/fireplace-swing/src/main/java/io/github/bric3/fireplace/flamegraph/FrameFontProvider.java index 56afaff7..d1fd64ca 100644 --- a/fireplace-swing/src/main/java/io/github/bric3/fireplace/flamegraph/FrameFontProvider.java +++ b/fireplace-swing/src/main/java/io/github/bric3/fireplace/flamegraph/FrameFontProvider.java @@ -48,35 +48,39 @@ static FrameFontProvider defaultFontProvider() { /** * The font used to display frame labels */ - private final Font frameLabelFont = new Font(Font.SANS_SERIF, Font.PLAIN, 12); + private final Font regular = new Font(Font.SANS_SERIF, Font.PLAIN, 12); /** * If a frame is clipped, we'll shift the label to make it visible but show it with * a modified (italicised by default) font to highlight that the frame is only partially * visible. */ - private final Font partialFrameLabelFont = new Font(Font.SANS_SERIF, Font.ITALIC, 12); + private final Font italic = new Font(Font.SANS_SERIF, Font.ITALIC, 12); /** * The font used to display frame labels */ - private final Font highlightedFrameLabelFont = new Font(Font.SANS_SERIF, Font.PLAIN | Font.BOLD, 12); + private final Font bold = new Font(Font.SANS_SERIF, Font.PLAIN | Font.BOLD, 12); /** * If a frame is clipped, we'll shift the label to make it visible but show it with * a modified (italicised by default) font to highlight that the frame is only partially * visible. */ - private final Font highlightedPartialFrameLabelFont = new Font(Font.SANS_SERIF, Font.ITALIC | Font.BOLD, 12); + private final Font italicBold = new Font(Font.SANS_SERIF, Font.ITALIC | Font.BOLD, 12); @Override public Font getFont(FrameBox frame, int flags) { + if (frame != null && frame.isRoot()) { + return bold; + } + if (isHighlightedFrame(flags)) { // when parent frame are larger than view port - return isPartialFrame(flags) ? highlightedPartialFrameLabelFont : highlightedFrameLabelFont; + return isPartialFrame(flags) ? italicBold : bold; } // when parent frame are larger than view port - return isPartialFrame(flags) ? partialFrameLabelFont : frameLabelFont; + return isPartialFrame(flags) ? italic : regular; } }; } From 2048f291f614fb237095cb8bb27cda34c8251a0e Mon Sep 17 00:00:00 2001 From: Brice Dutheil Date: Fri, 10 Jun 2022 10:28:37 +0200 Subject: [PATCH 7/7] fix(flamegraph): Add back hovering --- .../io/github/bric3/fireplace/DimmingFrameColorProvider.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/fireplace-app/src/main/java/io/github/bric3/fireplace/DimmingFrameColorProvider.java b/fireplace-app/src/main/java/io/github/bric3/fireplace/DimmingFrameColorProvider.java index f0fbe1fb..7c22b0b3 100644 --- a/fireplace-app/src/main/java/io/github/bric3/fireplace/DimmingFrameColorProvider.java +++ b/fireplace-app/src/main/java/io/github/bric3/fireplace/DimmingFrameColorProvider.java @@ -29,6 +29,7 @@ import static io.github.bric3.fireplace.flamegraph.FrameRenderingFlags.isFocusing; import static io.github.bric3.fireplace.flamegraph.FrameRenderingFlags.isHighlightedFrame; import static io.github.bric3.fireplace.flamegraph.FrameRenderingFlags.isHighlighting; +import static io.github.bric3.fireplace.flamegraph.FrameRenderingFlags.isHovered; import static io.github.bric3.fireplace.flamegraph.FrameRenderingFlags.isMinimapMode; class DimmingFrameColorProvider implements FrameColorProvider { @@ -70,6 +71,10 @@ public ColorModel getColors(FrameBox frame, int flags) { foreground = Colors.foregroundColor(backgroundColor); } + if (isHovered(flags)) { + backgroundColor = Colors.blend(backgroundColor, Colors.translucent_black_40); + } + return reusableDataStructure.set( backgroundColor, foreground