From 86b840652f1b63463ebddc69acea6ddac61dc99c Mon Sep 17 00:00:00 2001 From: Vladimir Krivosheev Date: Mon, 13 Mar 2023 08:01:55 +0100 Subject: [PATCH] fix icon cache - avoid wrapping, avoid not needed scale context update as a part of cache lookup (CachedImageIcon is responsible for that and does it perfectly) GitOrigin-RevId: 02758f152b6fb7ce3d6b2e5fbe7d7e2af50ee01f --- .../com/intellij/openapi/util/IconLoader.kt | 27 ++++-------- .../com/intellij/ui/icons/CachedImageIcon.kt | 42 +++++++++++++------ .../ui/src/com/intellij/ui/scale/JBUIScale.kt | 26 +++++++----- 3 files changed, 53 insertions(+), 42 deletions(-) diff --git a/platform/util/ui/src/com/intellij/openapi/util/IconLoader.kt b/platform/util/ui/src/com/intellij/openapi/util/IconLoader.kt index 5615fe692501..6bcbe67597d4 100644 --- a/platform/util/ui/src/com/intellij/openapi/util/IconLoader.kt +++ b/platform/util/ui/src/com/intellij/openapi/util/IconLoader.kt @@ -623,33 +623,24 @@ fun findIconUsingDeprecatedImplementation(originalPath: String, var effectiveClassLoader = classLoader val startTime = StartUpMeasurer.getCurrentTimeIfEnabled() val patchedPath = CachedImageIcon.patchPath(originalPath = originalPath, classLoader = effectiveClassLoader) - val path = patchedPath?.first ?: originalPath + val effectivePath = patchedPath?.first ?: originalPath if (patchedPath?.second != null) { effectiveClassLoader = patchedPath.second } - val icon: Icon? - if (isReflectivePath(path)) { - icon = getReflectiveIcon(path = path, classLoader = effectiveClassLoader) + var icon: Icon? + if (isReflectivePath(effectivePath)) { + icon = getReflectiveIcon(path = effectivePath, classLoader = effectiveClassLoader) } else { val key = Pair(originalPath, effectiveClassLoader) - var cachedIcon = iconCache.getIfPresent(key) - if (cachedIcon == null) { - cachedIcon = iconCache.get(key) { k -> - val resolver = ImageDataByPathResourceLoader(path = path, ownerClass = aClass, classLoader = k.second, strict = strict) - CachedImageIcon(originalPath = originalPath, resolver = resolver, toolTip = toolTip) + icon = iconCache.getIfPresent(key) + if (icon == null) { + icon = iconCache.get(key) { k -> + val resolver = ImageDataByPathResourceLoader(path = effectivePath, ownerClass = aClass, classLoader = k.second, strict = strict) + CachedImageIcon(originalPath = k.first, resolver = resolver, toolTip = toolTip) } } - else { - val scaleContext = ScaleContext.create() - if (cachedIcon.scaleContext != scaleContext) { - // honor scale context as 'iconCache' doesn't do that - cachedIcon = cachedIcon.copy() - cachedIcon.updateScaleContext(scaleContext) - } - } - icon = cachedIcon } if (startTime != -1L) { IconLoadMeasurer.findIcon.end(startTime) diff --git a/platform/util/ui/src/com/intellij/ui/icons/CachedImageIcon.kt b/platform/util/ui/src/com/intellij/ui/icons/CachedImageIcon.kt index d2215f5b47f6..3b6884907987 100644 --- a/platform/util/ui/src/com/intellij/ui/icons/CachedImageIcon.kt +++ b/platform/util/ui/src/com/intellij/ui/icons/CachedImageIcon.kt @@ -8,9 +8,7 @@ import com.intellij.openapi.util.IconPathPatcher import com.intellij.openapi.util.ScalableIcon import com.intellij.openapi.util.SystemInfoRt import com.intellij.reference.SoftReference -import com.intellij.ui.scale.AbstractScaleContextAware -import com.intellij.ui.scale.ScaleContext -import com.intellij.ui.scale.ScaleType +import com.intellij.ui.scale.* import com.intellij.util.SVGLoader import com.intellij.util.containers.CollectionFactory import com.intellij.util.ui.MultiResolutionImageProvider @@ -39,8 +37,8 @@ open class CachedImageIcon protected constructor( private val colorPatcher: SVGLoader.SvgElementColorPatcherProvider? = null, private val useStroke: Boolean = false, private val toolTip: Supplier? = null, -) : AbstractScaleContextAware(ScaleContext.create()), CopyableIcon, ScalableIcon, DarkIconProvider, MenuBarIconProvider, - IconWithToolTip { + private val scaleContext: ScaleContext = ScaleContext.create(), +) : CopyableIcon, ScalableIcon, DarkIconProvider, MenuBarIconProvider, IconWithToolTip, ScaleContextAware { companion object { @JvmField internal var isActivated: Boolean = !GraphicsEnvironment.isHeadless() @@ -101,22 +99,32 @@ open class CachedImageIcon protected constructor( } } + final override fun getScaleContext(): ScaleContext = scaleContext + + final override fun updateScaleContext(ctx: UserScaleContext?): Boolean = scaleContext.update(ctx) + + final override fun getScale(type: ScaleType): Double = scaleContext.getScale(type) + + final override fun getScale(type: DerivedScaleType): Double = scaleContext.getScale(type) + + final override fun setScale(scale: Scale): Boolean = scaleContext.setScale(scale) + override fun getToolTip(composite: Boolean): String? = toolTip?.get() - override fun paintIcon(c: Component?, g: Graphics, x: Int, y: Int) { - getRealIcon(ScaleContext.create(if (g is Graphics2D) g else null)).paintIcon(c, g, x, y) + final override fun paintIcon(c: Component?, g: Graphics, x: Int, y: Int) { + getRealIcon(scaleContext = null, sysScale = JBUIScale.sysScale(g as? Graphics2D?).toDouble()).paintIcon(c, g, x, y) } - override fun getIconWidth(): Int = getRealIcon(scaleContext = null).iconWidth + final override fun getIconWidth(): Int = getRealIcon(scaleContext = null).iconWidth - override fun getIconHeight(): Int = getRealIcon(scaleContext = null).iconHeight + final override fun getIconHeight(): Int = getRealIcon(scaleContext = null).iconHeight - override fun getScale(): Float = 1.0f + final override fun getScale(): Float = 1.0f @ApiStatus.Internal fun getRealIcon(): ImageIcon = getRealIcon(scaleContext = null) - internal fun getRealIcon(scaleContext: ScaleContext?): ImageIcon { + internal fun getRealIcon(scaleContext: ScaleContext?, sysScale: Double = -1.0): ImageIcon { if (resolver == null || !isActivated) { return EMPTY_ICON } @@ -147,14 +155,22 @@ open class CachedImageIcon protected constructor( } synchronized(lock) { + val updated = if (sysScale == -1.0) { + this.scaleContext.update(scaleContext) + } + else { + assert(scaleContext == null) + this.scaleContext.setScale(ScaleType.SYS_SCALE.of(sysScale)) + } // try returning the current icon as the context is up-to-date - if (!updateScaleContext(scaleContext) && realIcon != null) { + if (!updated && realIcon != null) { unwrapIcon(realIcon)?.let { return it } } - scaledIconCache.getOrScaleIcon(scale = 1.0f, host = this, scaleContext = this.scaleContext)?.let { icon -> + val icon = scaledIconCache.getOrScaleIcon(scale = 1.0f, host = this, scaleContext = this.scaleContext) + if (icon != null) { this.realIcon = if (icon.iconWidth < 50 && icon.iconHeight < 50) icon else SoftReference(icon) return icon } diff --git a/platform/util/ui/src/com/intellij/ui/scale/JBUIScale.kt b/platform/util/ui/src/com/intellij/ui/scale/JBUIScale.kt index 808943130eb2..c5999a22c71b 100644 --- a/platform/util/ui/src/com/intellij/ui/scale/JBUIScale.kt +++ b/platform/util/ui/src/com/intellij/ui/scale/JBUIScale.kt @@ -1,4 +1,6 @@ // Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +@file:Suppress("LiftReturnOrAssignment") + package com.intellij.ui.scale import com.intellij.diagnostic.runActivity @@ -25,7 +27,7 @@ import kotlin.math.roundToInt object JBUIScale { @JvmField @Internal - val SCALE_VERBOSE = java.lang.Boolean.getBoolean("ide.ui.scale.verbose") + val SCALE_VERBOSE: Boolean = java.lang.Boolean.getBoolean("ide.ui.scale.verbose") private const val USER_SCALE_FACTOR_PROPERTY = "JBUIScale.userScaleFactor" @@ -87,7 +89,7 @@ object JBUIScale { // with JB Linux JDK the label font comes properly scaled based on Xft.dpi settings. var font: Font if (SystemInfoRt.isMac) { - // see AquaFonts.getControlTextFont() - lucida13Pt is hardcoded + // see AquaFonts.getControlTextFont() - lucida13Pt is a hardcoded // text family should be used for relatively small sizes (<20pt), don't change to Display // see more about SF https://medium.com/@mach/the-secret-of-san-francisco-fonts-4b5295d9a745#.2ndr50z2v font = Font(".SF NS Text", Font.PLAIN, 13) @@ -115,14 +117,14 @@ object JBUIScale { var dpi = value / 1024 if (dpi < 50) dpi = 50 val scale = if (JreHiDpiUtil.isJreHiDPIEnabled()) 1f else discreteScale(dpi / 96f) // no scaling in JRE-HiDPI mode - // derive actual system base font size + // derive the actual system base font size DEF_SYSTEM_FONT_SIZE = font.size / scale if (isScaleVerbose) { log.info(String.format("DEF_SYSTEM_FONT_SIZE: %.2f", DEF_SYSTEM_FONT_SIZE)) } } else if (!SystemInfo.isJetBrainsJvm) { - // With Oracle JDK: derive scale from X server DPI, do not change DEF_SYSTEM_FONT_SIZE + // With Oracle JDK: derive a scale from X server DPI, do not change DEF_SYSTEM_FONT_SIZE val size = DEF_SYSTEM_FONT_SIZE * screenScale font = font.deriveFont(size) if (isScaleVerbose) { @@ -225,7 +227,8 @@ object JBUIScale { /** * Sets the user scale factor. - * The method is used by the IDE, it's not recommended to call the method directly from the client code. + * The IDE uses the method. + * It's not recommended to call the method directly from the client code. * For debugging purposes, the following JVM system property can be used: * ide.ui.scale=float * or the IDE registry keys (for backward compatibility): @@ -262,14 +265,14 @@ object JBUIScale { scale = discreteScale(scale) // downgrading user scale below 1.0 may be uncomfortable (tiny icons), - // whereas some users prefer font size slightly below normal which is ok + // whereas some users prefer font size slightly below normal, which is ok if (scale < 1 && systemScaleFactor.value >= 1) { scale = 1f } // ignore the correction when UIUtil.DEF_SYSTEM_FONT_SIZE is overridden, see UIUtil.initSystemFontData if (SystemInfoRt.isLinux && scale == 1.25f && DEF_SYSTEM_FONT_SIZE == 12f) { - // Default UI font size for Unity and Gnome is 15. Scaling factor 1.25f works badly on Linux. + // The default UI font size for Unity and Gnome is 15. Scaling factor 1.25f works badly on Linux. return 1f } else { @@ -396,11 +399,11 @@ object JBUIScale { val gc = g.deviceConfiguration if (gc == null || gc.device.type == GraphicsDevice.TYPE_IMAGE_BUFFER || gc.device.type == GraphicsDevice.TYPE_PRINTER) { - // in this case gc doesn't provide a valid scale + // in this case, gc doesn't provide a valid scale return abs(g.transform.scaleX.toFloat()) } else { - return sysScale(gc) + return gc.defaultTransform.scaleX.toFloat() } } @@ -414,8 +417,9 @@ object JBUIScale { */ @JvmStatic fun isHiDPI(scale: Double): Boolean { - // Scale below 1.0 is impractical, it's rather accepted for debug purpose. - // Treat it as "hidpi" to correctly manage images which have different user and real size + // The scale below 1.0 is impractical. + // It's rather accepted for debug purpose. + // Treat it as "hidpi" to correctly manage images which have different users and real size // (for scale below 1.0 the real size will be smaller). return scale != 1.0 }