All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.eclipse.swt.internal.ScalingSWTFontRegistry Maven / Gradle / Ivy

/*******************************************************************************
 * Copyright (c) 2024 Yatta Solutions
 *
 * This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License 2.0
 * which accompanies this distribution, and is available at
 * https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 *
 * Contributors:
 *     Yatta Solutions - initial API and implementation
 *******************************************************************************/
package org.eclipse.swt.internal;

import java.util.*;
import java.util.Map.*;

import org.eclipse.swt.graphics.*;
import org.eclipse.swt.internal.win32.*;

/**
 * This class is used in the win32 implementation only to support
 * scaling of fonts in multiple DPI zoom levels.
 *
 * As this class is only intended to be used internally via {@code SWTFontProvider},
 * it should neither be instantiated nor referenced in a client application.
 * The behavior can change any time in a future release.
 */
final class ScalingSWTFontRegistry implements SWTFontRegistry {
	private class ScaledFontContainer {
		// the first (unknown) font to be requested as scaled variant
		// usually it is scaled to the primary monitor zoom, but that is not guaranteed
		private Font baseFont;
		private Map scaledFonts = new HashMap<>();

		ScaledFontContainer(Font baseFont, int fontZoom) {
			this.baseFont = baseFont;
			scaledFonts.put(fontZoom, baseFont);
		}

		private Font getScaledFont(int targetZoom) {
			if (scaledFonts.containsKey(targetZoom)) {
				Font font = scaledFonts.get(targetZoom);
				if (font.isDisposed()) {
					scaledFonts.remove(targetZoom);
					return null;
				}
				return font;
			}
			return null;
		}

		private Font scaleFont(int zoom) {
			FontData fontData = baseFont.getFontData()[0];
			fontData.data.lfHeight = computePixels(zoom, fontData);
			Font scaledFont = Font.win32_new(device, fontData, zoom);
			addScaledFont(zoom, scaledFont);
			return scaledFont;
		}

		private void addScaledFont(int targetZoom, Font scaledFont) {
			scaledFonts.put(targetZoom, scaledFont);
		}
	}

	private static FontData KEY_SYSTEM_FONTS = new FontData();
	private Map fontHandleMap = new HashMap<>();
	private Map fontKeyMap = new HashMap<>();
	private Device device;

	ScalingSWTFontRegistry(Device device) {
		this.device = device;
	}

	@Override
	public Font getSystemFont(int zoom) {
		ScaledFontContainer container = getOrCreateBaseSystemFontContainer(device);

		Font systemFont = container.getScaledFont(zoom);
		if (systemFont != null) {
			return systemFont;
		}
		long systemFontHandle = createSystemFont(zoom);
		systemFont = Font.win32_new(device, systemFontHandle, zoom);
		container.addScaledFont(zoom, systemFont);
		return systemFont;
	}

	private ScaledFontContainer getOrCreateBaseSystemFontContainer(Device device) {
		ScaledFontContainer systemFontContainer = fontKeyMap.get(KEY_SYSTEM_FONTS);
		if (systemFontContainer == null) {
			long hDC = device.internal_new_GC (null);
			int dpiX = OS.GetDeviceCaps (hDC, OS.LOGPIXELSX);
			device.internal_dispose_GC (hDC, null);
			int primaryZoom = DPIUtil.mapDPIToZoom(dpiX);
			long systemFontHandle = createSystemFont(primaryZoom);
			Font systemFont = Font.win32_new(device, systemFontHandle, primaryZoom);
			systemFontContainer = new ScaledFontContainer(systemFont, primaryZoom);
			fontHandleMap.put(systemFont.handle, systemFontContainer);
			fontKeyMap.put(KEY_SYSTEM_FONTS, systemFontContainer);
		}
		return systemFontContainer;
	}

	private long createSystemFont(int targetZoom) {
		long hFont = 0;
		NONCLIENTMETRICS info = new NONCLIENTMETRICS();
		info.cbSize = NONCLIENTMETRICS.sizeof;
		if (fetchSystemParametersInfo(info, targetZoom)) {
			LOGFONT logFont = info.lfMessageFont;
			hFont = OS.CreateFontIndirect(logFont);
		}
		if (hFont == 0)
			hFont = OS.GetStockObject(OS.DEFAULT_GUI_FONT);
		if (hFont == 0)
			hFont = OS.GetStockObject(OS.SYSTEM_FONT);
		return hFont;
	}

	private static boolean fetchSystemParametersInfo(NONCLIENTMETRICS info, int targetZoom) {
		if (OS.WIN32_BUILD >= OS.WIN32_BUILD_WIN10_1607) {
			return OS.SystemParametersInfoForDpi(OS.SPI_GETNONCLIENTMETRICS, NONCLIENTMETRICS.sizeof, info, 0,
					DPIUtil.mapZoomToDPI(targetZoom));
		} else {
			return OS.SystemParametersInfo(OS.SPI_GETNONCLIENTMETRICS, 0, info, 0);
		}
	}

	@Override
	public Font getFont(FontData fontData, int zoom) {
		ScaledFontContainer container;
		if (fontKeyMap.containsKey(fontData)) {
			container = fontKeyMap.get(fontData);
		} else {
			int calculatedZoom = computeZoom(fontData);
			Font newFont = Font.win32_new(device, fontData, calculatedZoom);
			container = new ScaledFontContainer(newFont, calculatedZoom);
			fontHandleMap.put(newFont.handle, container);
			fontKeyMap.put(fontData, container);
		}
		return getOrCreateFont(container, zoom);
	}

	@Override
	public void dispose() {
		for (Entry fontContainerEntry : fontKeyMap.entrySet()) {
			if (KEY_SYSTEM_FONTS.equals(fontContainerEntry.getKey())) {
				// do not dispose the system fonts here, they are not tied to the device of this registry
				continue;
			}
			ScaledFontContainer scaledFontContainer = fontContainerEntry.getValue();
			for (Font font : scaledFontContainer.scaledFonts.values()) {
				font.dispose();
			}
		}
		fontKeyMap.clear();
	}

	private Font getOrCreateFont(ScaledFontContainer container, int zoom) {
		Font scaledFont = container.getScaledFont(zoom);
		if (scaledFont == null) {
			scaledFont = container.scaleFont(zoom);
			fontHandleMap.put(scaledFont.handle, container);
			fontKeyMap.put(scaledFont.getFontData()[0], container);
		}
		return scaledFont;
	}

	private int computeZoom(FontData fontData) {
		int dpi = device.getDPI().x;
		int pixelsAtPrimaryMonitorZoom = computePixels(fontData.height);
		int value = DPIUtil.mapDPIToZoom(dpi) * fontData.data.lfHeight / pixelsAtPrimaryMonitorZoom;
		return value;
	}

	private int computePixels(int zoom, FontData fontData) {
		int dpi = device.getDPI().x;
		int adjustedLogFontHeight = computePixels(fontData.height);
		int primaryZoom = DPIUtil.mapDPIToZoom(dpi);
		if (zoom != primaryZoom) {
			adjustedLogFontHeight *= (1f * zoom / primaryZoom);
		}
		return adjustedLogFontHeight;
	}

	private int computePixels(float height) {
		int dpi = device.getDPI().x;
		return -(int)(0.5f + (height * dpi / 72f));
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy