aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDmitry Batrak <Dmitry.Batrak@jetbrains.com>2016-08-03 16:19:43 +0300
committerDmitry Batrak <Dmitry.Batrak@jetbrains.com>2016-08-03 16:19:43 +0300
commit14b183ce63fb5c44357cbeae4adcd179bc5ed1bb (patch)
tree07de0be371119e9df1f4584021e0cba5708608ff
parent2843de1556c161521d9663344290e8cc71e7d70b (diff)
downloadjdk8u_jdk-14b183ce63fb5c44357cbeae4adcd179bc5ed1bb.tar.gz
JRE-11 Support text rendering via DirectWrite API on Windows - initial versionjb8u112-b283
-rw-r--r--make/lib/Awt2dLibraries.gmk8
-rw-r--r--src/share/classes/sun/font/FileFontStrike.java93
-rw-r--r--src/windows/native/sun/font/lcdglyphDW.cpp258
3 files changed, 342 insertions, 17 deletions
diff --git a/make/lib/Awt2dLibraries.gmk b/make/lib/Awt2dLibraries.gmk
index 34c3d38c3a..ff7af4605d 100644
--- a/make/lib/Awt2dLibraries.gmk
+++ b/make/lib/Awt2dLibraries.gmk
@@ -906,10 +906,12 @@ else ifeq ($(OPENJDK_TARGET_OS), macosx)
LIBFONTMANAGER_EXCLUDE_FILES += X11FontScaler.c \
X11TextRenderer.c \
fontpath.c \
- lcdglyph.c
+ lcdglyph.c \
+ lcdglyphDW.cpp
else
LIBFONTMANAGER_EXCLUDE_FILES += fontpath.c \
- lcdglyph.c
+ lcdglyph.c \
+ lcdglyphDW.cpp
endif
BUILD_LIBFONTMANAGER_CFLAGS_COMMON := \
@@ -954,7 +956,7 @@ $(eval $(call SetupNativeCompilation,BUILD_LIBFONTMANAGER, \
LDFLAGS_SUFFIX_aix := -lawt -lawt_headless $(LIBM) $(LIBCXX) -ljava -ljvm,\
LDFLAGS_SUFFIX_macosx := -lawt $(LIBM) $(LIBCXX) -undefined dynamic_lookup \
-ljava -ljvm, \
- LDFLAGS_SUFFIX_windows := $(WIN_JAVA_LIB) advapi32.lib user32.lib gdi32.lib \
+ LDFLAGS_SUFFIX_windows := $(WIN_JAVA_LIB) advapi32.lib user32.lib gdi32.lib Dwrite.lib \
$(WIN_AWT_LIB), \
VERSIONINFO_RESOURCE := $(JDK_TOPDIR)/src/windows/resource/version.rc, \
RC_FLAGS := $(RC_FLAGS) \
diff --git a/src/share/classes/sun/font/FileFontStrike.java b/src/share/classes/sun/font/FileFontStrike.java
index 2d6a6df47d..8d0061de44 100644
--- a/src/share/classes/sun/font/FileFontStrike.java
+++ b/src/share/classes/sun/font/FileFontStrike.java
@@ -35,6 +35,8 @@ import java.awt.geom.GeneralPath;
import java.awt.geom.NoninvertibleTransformException;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
import java.util.concurrent.ConcurrentHashMap;
import static sun.awt.SunHints.*;
@@ -115,11 +117,53 @@ public class FileFontStrike extends PhysicalStrike {
/* Perform global initialisation needed for Windows native rasterizer */
private static native boolean initNative();
+ private static native boolean isDirectWriteAvailable();
private static boolean isXPorLater = false;
+
+ private static boolean useDirectWrite;
+ private static int dwMeasuringMode = 0; // 'natural' mode
+ private static int dwRenderingMode = -1;
+ private static float dwClearTypeLevel = -1;
+ private static float dwEnhancedContrast = -1;
+ private static float dwGamma = 1; // gamma correction will be applied when glyph image is blitted onto target surface, so disabling correction here by default
+ private static int dwPixelGeometry = -1;
+
static {
if (FontUtilities.isWindows && !FontUtilities.useT2K &&
!GraphicsEnvironment.isHeadless()) {
isXPorLater = initNative();
+ if (isXPorLater) {
+ AccessController.doPrivileged(new PrivilegedAction() {
+ public Object run() {
+ useDirectWrite = Boolean.getBoolean("directwrite.font.rendering") && isDirectWriteAvailable();
+ if (useDirectWrite) {
+ String options = System.getProperty("directwrite.font.rendering.options");
+ if (options != null) {
+ String[] parts = options.split(":");
+ if (parts.length > 0 && parts[0].length() > 0) {
+ try { dwMeasuringMode = Integer.parseInt(parts[0]); } catch (NumberFormatException ignored) { }
+ }
+ if (parts.length > 1 && parts[1].length() > 0) {
+ try { dwRenderingMode = Integer.parseInt(parts[1]); } catch (NumberFormatException ignored) { }
+ }
+ if (parts.length > 2 && parts[2].length() > 0) {
+ try { dwClearTypeLevel = Float.parseFloat(parts[2]); } catch (NumberFormatException ignored) { }
+ }
+ if (parts.length > 3 && parts[3].length() > 0) {
+ try { dwEnhancedContrast = Float.parseFloat(parts[3]); } catch (NumberFormatException ignored) { }
+ }
+ if (parts.length > 4 && parts[4].length() > 0) {
+ try { dwGamma = Float.parseFloat(parts[4]); } catch (NumberFormatException ignored) { }
+ }
+ if (parts.length > 5 && parts[5].length() > 0) {
+ try { dwPixelGeometry = Integer.parseInt(parts[5]); } catch (NumberFormatException ignored) { }
+ }
+ }
+ }
+ return null;
+ }
+ });
+ }
}
}
@@ -340,29 +384,50 @@ public class FileFontStrike extends PhysicalStrike {
boolean fracMetrics,
int rotation);
+ private native long _getGlyphImageFromWindowsUsingDirectWrite(String family,
+ int style,
+ int size,
+ int glyphCode,
+ int measuringMode,
+ int renderingMode,
+ float clearTypeLevel,
+ float enhancedContrast,
+ float gamma,
+ int pixelGeometry);
+
long getGlyphImageFromWindows(int glyphCode) {
String family = fileFont.getFamilyName(null);
int style = desc.style & Font.BOLD | desc.style & Font.ITALIC
| fileFont.getStyle();
int size = intPtSize;
- long ptr = _getGlyphImageFromWindows(family, style, size, glyphCode,
- desc.fmHint == INTVAL_FRACTIONALMETRICS_ON, rotation);
- if (ptr != 0) {
- /* Get the advance from the JDK rasterizer. This is mostly
- * necessary for the fractional metrics case, but there are
- * also some very small number (<0.25%) of marginal cases where
- * there is some rounding difference between windows and JDK.
- * After these are resolved, we can restrict this extra
- * work to the FM case.
- */
- if (rotation == 0 || rotation == 2) {
+ long ptr = 0;
+ if (useDirectWrite && rotation == 0) {
+ ptr = _getGlyphImageFromWindowsUsingDirectWrite(family, style, size, glyphCode,
+ dwMeasuringMode, dwRenderingMode, dwClearTypeLevel, dwEnhancedContrast, dwGamma, dwPixelGeometry);
+ if (ptr == 0 && FontUtilities.isLogging()) {
+ FontUtilities.getLogger().warning("Failed to render glyph via DirectWrite: code=" + glyphCode
+ + ", fontFamily=" + family + ", style=" + style + ", size=" + size);
+ }
+ }
+ if (ptr == 0) {
+ ptr = _getGlyphImageFromWindows(family, style, size, glyphCode,
+ desc.fmHint == INTVAL_FRACTIONALMETRICS_ON, rotation);
+ if (ptr != 0 && (rotation == 0 || rotation == 2)) {
+ /* Get the advance from the JDK rasterizer. This is mostly
+ * necessary for the fractional metrics case, but there are
+ * also some very small number (<0.25%) of marginal cases where
+ * there is some rounding difference between windows and JDK.
+ * After these are resolved, we can restrict this extra
+ * work to the FM case.
+ */
float advance = getGlyphAdvance(glyphCode, false);
StrikeCache.unsafe.putFloat(ptr + StrikeCache.xAdvanceOffset, advance);
}
- return ptr;
- } else {
- return fileFont.getGlyphImage(pScalerContext, glyphCode);
}
+ if (ptr == 0) {
+ ptr = fileFont.getGlyphImage(pScalerContext, glyphCode);
+ }
+ return ptr;
}
/* Try the native strikes first, then try the fileFont strike */
diff --git a/src/windows/native/sun/font/lcdglyphDW.cpp b/src/windows/native/sun/font/lcdglyphDW.cpp
new file mode 100644
index 0000000000..bfcb732885
--- /dev/null
+++ b/src/windows/native/sun/font/lcdglyphDW.cpp
@@ -0,0 +1,258 @@
+/*
+ * Copyright (c) 2016 JetBrains s.r.o.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#include <stdio.h>
+#include <malloc.h>
+#include <math.h>
+#include <windows.h>
+#include <winuser.h>
+#include <Dwrite.h>
+
+#include <jni.h>
+#include <jni_util.h>
+#include <jlong_md.h>
+#include <sizecalc.h>
+#include <sun_font_FileFontStrike.h>
+
+#include "fontscalerdefs.h"
+
+extern "C" {
+
+#define FREE \
+ if (target != NULL) { \
+ target->Release(); \
+ }\
+ if (params != NULL) { \
+ params->Release(); \
+ }\
+ if (defaultParams != NULL) { \
+ defaultParams->Release(); \
+ }\
+ if (face != NULL) { \
+ face->Release(); \
+ }\
+ if (font != NULL) { \
+ font->Release(); \
+ }\
+ if (interop != NULL) { \
+ interop->Release(); \
+ }\
+ if (factory != NULL) { \
+ factory->Release(); \
+ }
+
+#define FREE_AND_RETURN \
+ FREE\
+ return (jlong)0;
+
+JNIEXPORT jboolean JNICALL
+Java_sun_font_FileFontStrike_isDirectWriteAvailable(JNIEnv *env, jclass unused) {
+ // This is an equivalent of IsWindows7OrGreater defined in VersionHelpers.h
+
+ OSVERSIONINFOEXW osvi;
+ osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEXW);
+ osvi.dwMajorVersion = 6;
+ osvi.dwMinorVersion = 1;
+
+ DWORDLONG conditionMask = VerSetConditionMask(VerSetConditionMask(0,
+ VER_MAJORVERSION, VER_GREATER_EQUAL),
+ VER_MINORVERSION, VER_GREATER_EQUAL);
+
+ BOOL result = VerifyVersionInfoW(&osvi, VER_MAJORVERSION | VER_MINORVERSION, conditionMask);
+ return result ? JNI_TRUE : JNI_FALSE;
+}
+
+JNIEXPORT jlong JNICALL
+Java_sun_font_FileFontStrike__1getGlyphImageFromWindowsUsingDirectWrite
+(JNIEnv *env, jobject unused, jstring fontFamily, jint style, jint size, jint glyphCode,
+ jint measuringMode, jint renderingMode, jfloat clearTypeLevel, jfloat enhancedContrast, jfloat gamma, jint pixelGeometry) {
+ // variables cleared by FREE macro
+ IDWriteFactory* factory = NULL;
+ IDWriteGdiInterop* interop = NULL;
+ IDWriteFont* font = NULL;
+ IDWriteFontFace* face = NULL;
+ IDWriteRenderingParams* defaultParams = NULL;
+ IDWriteRenderingParams* params = NULL;
+ IDWriteBitmapRenderTarget* target = NULL;
+
+ LOGFONTW lf;
+ memset(&lf, 0, sizeof(LOGFONTW));
+ lf.lfWeight = (style & 1) ? FW_BOLD : FW_NORMAL;
+ lf.lfItalic = (style & 2) ? TRUE : FALSE;
+
+ int nameLen = env->GetStringLength(fontFamily);
+ if (nameLen >= (sizeof(lf.lfFaceName) / sizeof(lf.lfFaceName[0]))) {
+ FREE_AND_RETURN
+ }
+ env->GetStringRegion(fontFamily, 0, nameLen, lf.lfFaceName);
+ lf.lfFaceName[nameLen] = '\0';
+
+ HRESULT hr = DWriteCreateFactory(DWRITE_FACTORY_TYPE_ISOLATED,
+ __uuidof(IDWriteFactory),
+ reinterpret_cast<IUnknown**>(&factory));
+ if (FAILED(hr)) {
+ FREE_AND_RETURN
+ }
+ hr = factory->GetGdiInterop(&interop);
+ if (FAILED(hr)) {
+ FREE_AND_RETURN
+ }
+ hr = interop->CreateFontFromLOGFONT(&lf, &font);
+ if (FAILED(hr)) {
+ FREE_AND_RETURN
+ }
+ hr = font->CreateFontFace(&face);
+ if (FAILED(hr)) {
+ FREE_AND_RETURN
+ }
+ hr = factory->CreateRenderingParams(&defaultParams);
+ if (FAILED(hr)) {
+ FREE_AND_RETURN
+ }
+ hr = factory->CreateCustomRenderingParams(
+ gamma > 0 && gamma <= 256 ? gamma : defaultParams->GetGamma(),
+ enhancedContrast >= 0 ? enhancedContrast : defaultParams->GetEnhancedContrast(),
+ clearTypeLevel >= 0 && clearTypeLevel <= 1 ? clearTypeLevel : defaultParams->GetClearTypeLevel(),
+ pixelGeometry >= 0 && pixelGeometry <= 2 ? (DWRITE_PIXEL_GEOMETRY)pixelGeometry : defaultParams->GetPixelGeometry(),
+ renderingMode >= 0 && renderingMode <= 6 ? (DWRITE_RENDERING_MODE)renderingMode : defaultParams->GetRenderingMode(),
+ &params);
+ if (FAILED(hr)) {
+ FREE_AND_RETURN
+ }
+
+ UINT16 indices[] = {(UINT16)glyphCode};
+ FLOAT advances[] = {0};
+ DWRITE_GLYPH_OFFSET offsets[] = {{0, 0}};
+ DWRITE_GLYPH_RUN glyphRun;
+ glyphRun.fontFace = face;
+ glyphRun.fontEmSize = (FLOAT)size;
+ glyphRun.glyphCount = 1;
+ glyphRun.glyphIndices = indices;
+ glyphRun.glyphAdvances = advances;
+ glyphRun.glyphOffsets = offsets;
+ glyphRun.isSideways = FALSE;
+ glyphRun.bidiLevel = 0;
+
+ DWRITE_FONT_METRICS fontMetrics;
+ face->GetMetrics(&fontMetrics);
+ FLOAT pxPerDU = ((FLOAT)size) / fontMetrics.designUnitsPerEm;
+
+ DWRITE_GLYPH_METRICS metrics[1];
+ hr = face->GetDesignGlyphMetrics(indices, 1, metrics, FALSE);
+ if (FAILED(hr)) {
+ FREE_AND_RETURN
+ }
+
+ // trying to derive required bitmap size from glyph metrics (adding several spare pixels on each border)
+ // if that will fail, we'll perform a second attempt based on the output of DrawGlyphRun
+ int width = (int)((metrics[0].advanceWidth - metrics[0].leftSideBearing - metrics[0].rightSideBearing) * pxPerDU) + 10;
+ int height = (int)((metrics[0].advanceHeight - metrics[0].topSideBearing - metrics[0].bottomSideBearing) * pxPerDU) + 10;
+ int x = (int)(-metrics[0].leftSideBearing * pxPerDU) + 5;
+ int y = (int)((metrics[0].verticalOriginY - metrics[0].topSideBearing) * pxPerDU) + 5;
+ RECT bbRect;
+
+ for (int attempt = 0; attempt < 2 && target == NULL; attempt++) {
+ hr = interop->CreateBitmapRenderTarget(NULL, width, height, &target);
+ if (FAILED(hr)) {
+ FREE_AND_RETURN
+ }
+ hr = target->DrawGlyphRun((FLOAT)x,
+ (FLOAT)y,
+ measuringMode >= 0 && measuringMode <= 2 ? (DWRITE_MEASURING_MODE)measuringMode : DWRITE_MEASURING_MODE_NATURAL,
+ &glyphRun,
+ params,
+ RGB(255,255,255),
+ &bbRect);
+ if (FAILED(hr) || bbRect.left > bbRect.right || bbRect.top > bbRect.bottom
+ || attempt > 0 && (bbRect.left < 0 || bbRect.top < 0 || bbRect.right > width || bbRect.bottom > height)) {
+ FREE_AND_RETURN
+ }
+ if (bbRect.left < 0 || bbRect.top < 0 || bbRect.right > width || bbRect.bottom > height) {
+ target->Release();
+ target = NULL;
+ if (bbRect.right > width) width = bbRect.right;
+ if (bbRect.bottom > height) height = bbRect.bottom;
+ if (bbRect.left < 0) {
+ width -= bbRect.left;
+ x -= bbRect.left;
+ }
+ if (bbRect.top < 0) {
+ height -= bbRect.top;
+ y -= bbRect.top;
+ }
+ }
+ }
+
+ HDC glyphDC = target->GetMemoryDC();
+ HGDIOBJ glyphBitmap = GetCurrentObject(glyphDC, OBJ_BITMAP);
+ if (glyphBitmap == NULL) {
+ FREE_AND_RETURN
+ }
+ DIBSECTION dibSection;
+ if (GetObject(glyphBitmap, sizeof(DIBSECTION), &dibSection) == 0) {
+ FREE_AND_RETURN
+ }
+
+ int glyphWidth = bbRect.right - bbRect.left;
+ int glyphHeight = bbRect.bottom - bbRect.top;
+ int glyphBytesWidth = glyphWidth * 3;
+ GlyphInfo* glyphInfo = (GlyphInfo*)SAFE_SIZE_STRUCT_ALLOC(malloc, sizeof(GlyphInfo), glyphBytesWidth, glyphHeight);
+ if (glyphInfo == NULL) {
+ FREE_AND_RETURN
+ }
+ glyphInfo->managed = UNMANAGED_GLYPH;
+ glyphInfo->cellInfo = NULL;
+ glyphInfo->image = (unsigned char*)glyphInfo+sizeof(GlyphInfo);
+ glyphInfo->rowBytes = glyphBytesWidth;
+ glyphInfo->width = glyphWidth;
+ glyphInfo->height = glyphHeight;
+ glyphInfo->advanceX = (float)((int)(metrics[0].advanceWidth * pxPerDU + 0.5));
+ glyphInfo->advanceY = 0;
+ glyphInfo->topLeftX = (float)(bbRect.left - x);
+ glyphInfo->topLeftY = (float)(bbRect.top - y);
+
+ int srcRowBytes = width * 4;
+ unsigned char* srcPtr = (unsigned char*) dibSection.dsBm.bmBits + srcRowBytes * bbRect.top;
+ unsigned char* destPtr = glyphInfo->image;
+ for (int y = 0; y < glyphHeight; y++) {
+ srcPtr += bbRect.left * 4;
+ for (int x = 0; x < glyphWidth; x++) {
+ // converting from BGRA to RGB
+ unsigned char b = *srcPtr++;
+ unsigned char g = *srcPtr++;
+ unsigned char r = *srcPtr++;
+ srcPtr++;
+ *destPtr++ = r;
+ *destPtr++ = g;
+ *destPtr++ = b;
+ }
+ srcPtr += (width - bbRect.right) * 4;
+ }
+
+ FREE
+ return ptr_to_jlong(glyphInfo);
+}
+
+} // extern "C" \ No newline at end of file