mirror of
https://github.com/openjdk/jdk.git
synced 2026-04-27 07:10:45 +00:00
6656651: Windows Look and Feel LCD glyph images have some differences from native applications
Reviewed-by: igor, tdv
This commit is contained in:
parent
c672506f95
commit
faadc39966
@ -113,7 +113,9 @@ FILES_cpp_shared = \
|
||||
|
||||
|
||||
ifeq ($(PLATFORM),windows)
|
||||
FILES_c_platform = fontpath.c
|
||||
FILES_c_platform = fontpath.c \
|
||||
lcdglyph.c
|
||||
|
||||
FILES_cpp_platform = D3DTextRenderer.cpp
|
||||
else
|
||||
FILES_c_platform = X11FontScaler.c \
|
||||
|
||||
@ -63,6 +63,7 @@ FILES_export = \
|
||||
java/awt/Font.java \
|
||||
java/text/Bidi.java \
|
||||
sun/font/FileFont.java \
|
||||
sun/font/FileFontStrike.java \
|
||||
sun/font/FontManager.java \
|
||||
sun/font/GlyphList.java \
|
||||
sun/font/NativeFont.java \
|
||||
|
||||
@ -27,6 +27,7 @@ package sun.font;
|
||||
|
||||
import java.lang.ref.SoftReference;
|
||||
import java.awt.Font;
|
||||
import java.awt.GraphicsEnvironment;
|
||||
import java.awt.Rectangle;
|
||||
import java.awt.geom.AffineTransform;
|
||||
import java.awt.geom.GeneralPath;
|
||||
@ -105,6 +106,19 @@ public class FileFontStrike extends PhysicalStrike {
|
||||
boolean useNatives;
|
||||
NativeStrike[] nativeStrikes;
|
||||
|
||||
/* Used only for communication to native layer */
|
||||
private int intPtSize;
|
||||
|
||||
/* Perform global initialisation needed for Windows native rasterizer */
|
||||
private static native boolean initNative();
|
||||
private static boolean isXPorLater = false;
|
||||
static {
|
||||
if (FontManager.isWindows && !FontManager.useT2K &&
|
||||
!GraphicsEnvironment.isHeadless()) {
|
||||
isXPorLater = initNative();
|
||||
}
|
||||
}
|
||||
|
||||
FileFontStrike(FileFont fileFont, FontStrikeDesc desc) {
|
||||
super(fileFont, desc);
|
||||
this.fileFont = fileFont;
|
||||
@ -165,7 +179,7 @@ public class FileFontStrike extends PhysicalStrike {
|
||||
* should not segment unless there's another reason to do so.
|
||||
*/
|
||||
float ptSize = (float)matrix[3]; // interpreted only when meaningful.
|
||||
int iSize = (int)ptSize;
|
||||
int iSize = intPtSize = (int)ptSize;
|
||||
boolean isSimpleTx = (at.getType() & complexTX) == 0;
|
||||
segmentedCache =
|
||||
(numGlyphs > SEGSIZE << 3) ||
|
||||
@ -189,8 +203,26 @@ public class FileFontStrike extends PhysicalStrike {
|
||||
FontManager.deRegisterBadFont(fileFont);
|
||||
return;
|
||||
}
|
||||
|
||||
if (fileFont.checkUseNatives() && desc.aaHint==0 && !algoStyle) {
|
||||
/* First, see if native code should be used to create the glyph.
|
||||
* GDI will return the integer metrics, not fractional metrics, which
|
||||
* may be requested for this strike, so we would require here that :
|
||||
* desc.fmHint != INTVAL_FRACTIONALMETRICS_ON
|
||||
* except that the advance returned by GDI is always overwritten by
|
||||
* the JDK rasteriser supplied one (see getGlyphImageFromWindows()).
|
||||
*/
|
||||
if (FontManager.isWindows && isXPorLater &&
|
||||
!FontManager.useT2K &&
|
||||
!GraphicsEnvironment.isHeadless() &&
|
||||
!fileFont.useJavaRasterizer &&
|
||||
(desc.aaHint == INTVAL_TEXT_ANTIALIAS_LCD_HRGB ||
|
||||
desc.aaHint == INTVAL_TEXT_ANTIALIAS_LCD_HBGR) &&
|
||||
(matrix[1] == 0.0 && matrix[2] == 0.0 &&
|
||||
matrix[0] == matrix[3] &&
|
||||
matrix[0] >= 3.0 && matrix[0] <= 100.0) &&
|
||||
!((TrueTypeFont)fileFont).useEmbeddedBitmapsForSize(intPtSize)) {
|
||||
useNatives = true;
|
||||
}
|
||||
else if (fileFont.checkUseNatives() && desc.aaHint==0 && !algoStyle) {
|
||||
/* Check its a simple scale of a pt size in the range
|
||||
* where native bitmaps typically exist (6-36 pts) */
|
||||
if (matrix[1] == 0.0 && matrix[2] == 0.0 &&
|
||||
@ -208,7 +240,16 @@ public class FileFontStrike extends PhysicalStrike {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (FontManager.logging && FontManager.isWindows) {
|
||||
FontManager.logger.info
|
||||
("Strike for " + fileFont + " at size = " + intPtSize +
|
||||
" use natives = " + useNatives +
|
||||
" useJavaRasteriser = " + fileFont.useJavaRasterizer +
|
||||
" AAHint = " + desc.aaHint +
|
||||
" Has Embedded bitmaps = " +
|
||||
((TrueTypeFont)fileFont).
|
||||
useEmbeddedBitmapsForSize(intPtSize));
|
||||
}
|
||||
this.disposer = new FontStrikeDisposer(fileFont, desc, pScalerContext);
|
||||
|
||||
/* Always get the image and the advance together for smaller sizes
|
||||
@ -255,8 +296,50 @@ public class FileFontStrike extends PhysicalStrike {
|
||||
return fileFont.getNumGlyphs();
|
||||
}
|
||||
|
||||
/* Try the native strikes first, then try the fileFont strike */
|
||||
long getGlyphImageFromNative(int glyphCode) {
|
||||
if (FontManager.isWindows) {
|
||||
return getGlyphImageFromWindows(glyphCode);
|
||||
} else {
|
||||
return getGlyphImageFromX11(glyphCode);
|
||||
}
|
||||
}
|
||||
|
||||
/* There's no global state conflicts, so this method is not
|
||||
* presently synchronized.
|
||||
*/
|
||||
private native long _getGlyphImageFromWindows(String family,
|
||||
int style,
|
||||
int size,
|
||||
int glyphCode,
|
||||
boolean fracMetrics);
|
||||
|
||||
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);
|
||||
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.
|
||||
*/
|
||||
float advance = getGlyphAdvance(glyphCode, false);
|
||||
StrikeCache.unsafe.putFloat(ptr + StrikeCache.xAdvanceOffset,
|
||||
advance);
|
||||
return ptr;
|
||||
} else {
|
||||
return fileFont.getGlyphImage(pScalerContext, glyphCode);
|
||||
}
|
||||
}
|
||||
|
||||
/* Try the native strikes first, then try the fileFont strike */
|
||||
long getGlyphImageFromX11(int glyphCode) {
|
||||
long glyphPtr;
|
||||
char charCode = fileFont.glyphToCharMap[glyphCode];
|
||||
for (int i=0;i<nativeStrikes.length;i++) {
|
||||
@ -276,13 +359,19 @@ public class FileFontStrike extends PhysicalStrike {
|
||||
if (glyphCode >= INVISIBLE_GLYPHS) {
|
||||
return StrikeCache.invisibleGlyphPtr;
|
||||
}
|
||||
long glyphPtr;
|
||||
long glyphPtr = 0L;
|
||||
if ((glyphPtr = getCachedGlyphPtr(glyphCode)) != 0L) {
|
||||
return glyphPtr;
|
||||
} else {
|
||||
if (useNatives) {
|
||||
glyphPtr = getGlyphImageFromNative(glyphCode);
|
||||
} else {
|
||||
if (glyphPtr == 0L && FontManager.logging) {
|
||||
FontManager.logger.info
|
||||
("Strike for " + fileFont +
|
||||
" at size = " + intPtSize +
|
||||
" couldn't get native glyph for code = " + glyphCode);
|
||||
}
|
||||
} if (glyphPtr == 0L) {
|
||||
glyphPtr = fileFont.getGlyphImage(pScalerContext,
|
||||
glyphCode);
|
||||
}
|
||||
@ -300,10 +389,10 @@ public class FileFontStrike extends PhysicalStrike {
|
||||
} else if ((images[i] = getCachedGlyphPtr(glyphCode)) != 0L) {
|
||||
continue;
|
||||
} else {
|
||||
long glyphPtr;
|
||||
long glyphPtr = 0L;
|
||||
if (useNatives) {
|
||||
glyphPtr = getGlyphImageFromNative(glyphCode);
|
||||
} else {
|
||||
} if (glyphPtr == 0L) {
|
||||
glyphPtr = fileFont.getGlyphImage(pScalerContext,
|
||||
glyphCode);
|
||||
}
|
||||
@ -332,10 +421,11 @@ public class FileFontStrike extends PhysicalStrike {
|
||||
} else if ((images[i] = getCachedGlyphPtr(glyphCode)) != 0L) {
|
||||
continue;
|
||||
} else {
|
||||
long glyphPtr;
|
||||
long glyphPtr = 0L;
|
||||
if (useNatives) {
|
||||
glyphPtr = getGlyphImageFromNative(glyphCode);
|
||||
} else {
|
||||
}
|
||||
if (glyphPtr == 0L) {
|
||||
glyphPtr = fileFont.getGlyphImage(pScalerContext,
|
||||
glyphCode);
|
||||
}
|
||||
@ -459,11 +549,16 @@ public class FileFontStrike extends PhysicalStrike {
|
||||
}
|
||||
}
|
||||
|
||||
float getGlyphAdvance(int glyphCode) {
|
||||
return getGlyphAdvance(glyphCode, true);
|
||||
}
|
||||
|
||||
/* Metrics info is always retrieved. If the GlyphInfo address is non-zero
|
||||
* then metrics info there is valid and can just be copied.
|
||||
* This is in user space coordinates.
|
||||
* This is in user space coordinates unless getUserAdv == false.
|
||||
* Device space advance should not be propagated out of this class.
|
||||
*/
|
||||
float getGlyphAdvance(int glyphCode) {
|
||||
private float getGlyphAdvance(int glyphCode, boolean getUserAdv) {
|
||||
float advance;
|
||||
|
||||
if (glyphCode >= INVISIBLE_GLYPHS) {
|
||||
@ -485,11 +580,11 @@ public class FileFontStrike extends PhysicalStrike {
|
||||
}
|
||||
}
|
||||
|
||||
if (invertDevTx != null) {
|
||||
if (invertDevTx != null || !getUserAdv) {
|
||||
/* If there is a device transform need x & y advance to
|
||||
* transform back into user space.
|
||||
*/
|
||||
advance = getGlyphMetrics(glyphCode).x;
|
||||
advance = getGlyphMetrics(glyphCode, getUserAdv).x;
|
||||
} else {
|
||||
long glyphPtr;
|
||||
if (getImageWithAdvance) {
|
||||
@ -625,6 +720,10 @@ public class FileFontStrike extends PhysicalStrike {
|
||||
}
|
||||
|
||||
Point2D.Float getGlyphMetrics(int glyphCode) {
|
||||
return getGlyphMetrics(glyphCode, true);
|
||||
}
|
||||
|
||||
private Point2D.Float getGlyphMetrics(int glyphCode, boolean getUserAdv) {
|
||||
Point2D.Float metrics = new Point2D.Float();
|
||||
|
||||
// !!! or do we force sgv user glyphs?
|
||||
@ -632,7 +731,7 @@ public class FileFontStrike extends PhysicalStrike {
|
||||
return metrics;
|
||||
}
|
||||
long glyphPtr;
|
||||
if (getImageWithAdvance) {
|
||||
if (getImageWithAdvance && getUserAdv) {
|
||||
/* A heuristic optimisation says that for most cases its
|
||||
* worthwhile retrieving the image at the same time as the
|
||||
* metrics. So here we get the image data even if its not
|
||||
@ -649,9 +748,9 @@ public class FileFontStrike extends PhysicalStrike {
|
||||
metrics.y = StrikeCache.unsafe.getFloat
|
||||
(glyphPtr + StrikeCache.yAdvanceOffset);
|
||||
/* advance is currently in device space, need to convert back
|
||||
* into user space.
|
||||
* into user space, unless getUserAdv == false.
|
||||
* This must not include the translation component. */
|
||||
if (invertDevTx != null) {
|
||||
if (invertDevTx != null && getUserAdv) {
|
||||
invertDevTx.deltaTransform(metrics, metrics);
|
||||
}
|
||||
} else {
|
||||
@ -680,9 +779,9 @@ public class FileFontStrike extends PhysicalStrike {
|
||||
if (value == null) {
|
||||
fileFont.getGlyphMetrics(pScalerContext, glyphCode, metrics);
|
||||
/* advance is currently in device space, need to convert back
|
||||
* into user space.
|
||||
* into user space, unless getUserAdv == false.
|
||||
*/
|
||||
if (invertDevTx != null) {
|
||||
if (invertDevTx != null && getUserAdv) {
|
||||
invertDevTx.deltaTransform(metrics, metrics);
|
||||
}
|
||||
value = new Point2D.Float(metrics.x, metrics.y);
|
||||
|
||||
@ -244,9 +244,11 @@ public final class FontManager {
|
||||
osName = System.getProperty("os.name", "unknownOS");
|
||||
isSolaris = osName.startsWith("SunOS");
|
||||
|
||||
if (isSolaris) {
|
||||
String t2kStr= System.getProperty("sun.java2d.font.scaler");
|
||||
String t2kStr = System.getProperty("sun.java2d.font.scaler");
|
||||
if (t2kStr != null) {
|
||||
useT2K = "t2k".equals(t2kStr);
|
||||
}
|
||||
if (isSolaris) {
|
||||
String version = System.getProperty("os.version", "unk");
|
||||
isSolaris8 = version.equals("5.8");
|
||||
isSolaris9 = version.equals("5.9");
|
||||
|
||||
@ -893,6 +893,31 @@ public class TrueTypeFont extends FileFont {
|
||||
return null;
|
||||
}
|
||||
|
||||
/* Used to determine if this size has embedded bitmaps, which
|
||||
* for CJK fonts should be used in preference to LCD glyphs.
|
||||
*/
|
||||
boolean useEmbeddedBitmapsForSize(int ptSize) {
|
||||
if (!supportsCJK) {
|
||||
return false;
|
||||
}
|
||||
if (getDirectoryEntry(EBLCTag) == null) {
|
||||
return false;
|
||||
}
|
||||
ByteBuffer eblcTable = getTableBuffer(EBLCTag);
|
||||
int numSizes = eblcTable.getInt(4);
|
||||
/* The bitmapSizeTable's start at offset of 8.
|
||||
* Each bitmapSizeTable entry is 48 bytes.
|
||||
* The offset of ppemY in the entry is 45.
|
||||
*/
|
||||
for (int i=0;i<numSizes;i++) {
|
||||
int ppemY = eblcTable.get(8+(i*48)+45) &0xff;
|
||||
if (ppemY == ptSize) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public String getFullName() {
|
||||
return fullName;
|
||||
}
|
||||
|
||||
@ -260,6 +260,7 @@ public class Win32GraphicsEnvironment
|
||||
try {
|
||||
while (!found && parser.hasMoreTokens()) {
|
||||
String newPath = parser.nextToken();
|
||||
boolean ujr = newPath.equals(jreFontDirName);
|
||||
File theFile = new File(newPath, fontFileName);
|
||||
if (theFile.canRead()) {
|
||||
found = true;
|
||||
@ -267,11 +268,11 @@ public class Win32GraphicsEnvironment
|
||||
if (defer) {
|
||||
FontManager.registerDeferredFont(fontFileName, path,
|
||||
nativeNames,
|
||||
fontFormat, true,
|
||||
fontFormat, ujr,
|
||||
fontRank);
|
||||
} else {
|
||||
FontManager.registerFontFile(path, nativeNames,
|
||||
fontFormat, true,
|
||||
fontFormat, ujr,
|
||||
fontRank);
|
||||
}
|
||||
break;
|
||||
|
||||
481
jdk/src/windows/native/sun/font/lcdglyph.c
Normal file
481
jdk/src/windows/native/sun/font/lcdglyph.c
Normal file
@ -0,0 +1,481 @@
|
||||
/*
|
||||
* Copyright 2008 Sun Microsystems, Inc. All Rights Reserved.
|
||||
* 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. Sun designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
|
||||
* CA 95054 USA or visit www.sun.com if you need additional information or
|
||||
* have any questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* The function here is used to get a GDI rasterized LCD glyph and place it
|
||||
* into the JDK glyph cache. The benefit is rendering fidelity for the
|
||||
* most common cases, with no impact on the 2D rendering pipelines.
|
||||
*
|
||||
* Requires that the font and graphics are unrotated, and the scale is
|
||||
* a simple one, and the font is a TT font registered with windows.
|
||||
* Those conditions are established by the calling code.
|
||||
*
|
||||
* This code
|
||||
* - Receives the family name, style, and size of the font
|
||||
* and creates a Font object.
|
||||
* - Create a surface from which we can get a DC : must be 16 bit or more.
|
||||
* Ideally we'd be able to specify the depth of this, but in practice we
|
||||
* have to accept it will be the same as the default screen.
|
||||
* - Selects the GDI font on to the device
|
||||
* - Uses GetGlyphOutline to estimate the bounds.
|
||||
* - Creates a DIB on to which to blit the image.
|
||||
* - Creates a GlyphInfo structure and copies the GDI glyph and offsets
|
||||
* into the glyph which is returned.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <malloc.h>
|
||||
#include <math.h>
|
||||
#include <windows.h>
|
||||
#include <winuser.h>
|
||||
|
||||
#include <jni.h>
|
||||
#include <jni_util.h>
|
||||
#include <jlong_md.h>
|
||||
#include <sun_font_FileFontStrike.h>
|
||||
|
||||
#include "fontscalerdefs.h"
|
||||
|
||||
/* Some of these are also defined in awtmsg.h but I don't want a dependency
|
||||
* on that here. They are needed here - and in awtmsg.h - until we
|
||||
* move up our build to define WIN32_WINNT >= 0x501 (ie XP), since MS
|
||||
* headers will not define them otherwise.
|
||||
*/
|
||||
#ifndef SPI_GETFONTSMOOTHINGTYPE
|
||||
#define SPI_GETFONTSMOOTHINGTYPE 0x200A
|
||||
#endif //SPI_GETFONTSMOOTHINGTYPE
|
||||
|
||||
#ifndef SPI_GETFONTSMOOTHINGCONTRAST
|
||||
#define SPI_GETFONTSMOOTHINGCONTRAST 0x200C
|
||||
#endif //SPI_GETFONTSMOOTHINGCONTRAST
|
||||
|
||||
#ifndef SPI_GETFONTSMOOTHINGORIENTATION
|
||||
#define SPI_GETFONTSMOOTHINGORIENTATION 0x2012
|
||||
#endif //SPI_GETFONTSMOOTHINGORIENTATION
|
||||
|
||||
#ifndef FE_FONTSMOOTHINGORIENTATIONBGR
|
||||
#define FE_FONTSMOOTHINGORIENTATIONBGR 0x0000
|
||||
#endif //FE_FONTSMOOTHINGORIENTATIONBGR
|
||||
|
||||
#ifndef FE_FONTSMOOTHINGORIENTATIONRGB
|
||||
#define FE_FONTSMOOTHINGORIENTATIONRGB 0x0001
|
||||
#endif //FE_FONTSMOOTHINGORIENTATIONRGB
|
||||
|
||||
#define MIN_GAMMA 100
|
||||
#define MAX_GAMMA 220
|
||||
#define LCDLUTCOUNT (MAX_GAMMA-MIN_GAMMA+1)
|
||||
|
||||
static unsigned char* igLUTable[LCDLUTCOUNT];
|
||||
|
||||
static unsigned char* getIGTable(int gamma) {
|
||||
int i, index;
|
||||
double ig;
|
||||
char *igTable;
|
||||
|
||||
if (gamma < MIN_GAMMA) {
|
||||
gamma = MIN_GAMMA;
|
||||
} else if (gamma > MAX_GAMMA) {
|
||||
gamma = MAX_GAMMA;
|
||||
}
|
||||
|
||||
index = gamma - MIN_GAMMA;
|
||||
|
||||
if (igLUTable[index] != NULL) {
|
||||
return igLUTable[index];
|
||||
}
|
||||
igTable = (unsigned char*)malloc(256);
|
||||
if (igTable == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
igTable[0] = 0;
|
||||
igTable[255] = 255;
|
||||
ig = ((double)gamma)/100.0;
|
||||
|
||||
for (i=1;i<255;i++) {
|
||||
igTable[i] = (unsigned char)(pow(((double)i)/255.0, ig)*255);
|
||||
}
|
||||
igLUTable[index] = igTable;
|
||||
return igTable;
|
||||
}
|
||||
|
||||
|
||||
JNIEXPORT jboolean JNICALL
|
||||
Java_sun_font_FileFontStrike_initNative(JNIEnv *env, jclass unused) {
|
||||
|
||||
DWORD osVersion = GetVersion();
|
||||
DWORD majorVersion = (DWORD)(LOBYTE(LOWORD(osVersion)));
|
||||
DWORD minorVersion = (DWORD)(HIBYTE(LOWORD(osVersion)));
|
||||
|
||||
/* Need at least XP which is 5.1 */
|
||||
if (majorVersion < 5 || (majorVersion == 5 && minorVersion < 1)) {
|
||||
return JNI_FALSE;
|
||||
}
|
||||
|
||||
memset(igLUTable, 0, LCDLUTCOUNT);
|
||||
|
||||
return JNI_TRUE;
|
||||
}
|
||||
|
||||
#ifndef CLEARTYPE_QUALITY
|
||||
#define CLEARTYPE_QUALITY 5
|
||||
#endif
|
||||
|
||||
#ifndef CLEARTYPE_NATURAL_QUALITY
|
||||
#define CLEARTYPE_NATURAL_QUALITY 6
|
||||
#endif
|
||||
|
||||
#define FREE_AND_RETURN \
|
||||
if (hDesktopDC != 0 && hWnd != 0) { \
|
||||
ReleaseDC(hWnd, hDesktopDC); \
|
||||
}\
|
||||
if (hMemoryDC != 0) { \
|
||||
DeleteObject(hMemoryDC); \
|
||||
} \
|
||||
if (hBitmap != 0) { \
|
||||
DeleteObject(hBitmap); \
|
||||
} \
|
||||
if (dibImage != NULL) { \
|
||||
free(dibImage); \
|
||||
} \
|
||||
if (glyphInfo != NULL) { \
|
||||
free(glyphInfo); \
|
||||
} \
|
||||
return (jlong)0;
|
||||
/* end define */
|
||||
|
||||
JNIEXPORT jlong JNICALL
|
||||
Java_sun_font_FileFontStrike__1getGlyphImageFromWindows
|
||||
(JNIEnv *env, jobject unused,
|
||||
jstring fontFamily, jint style, jint size, jint glyphCode, jboolean fm) {
|
||||
|
||||
GLYPHMETRICS glyphMetrics;
|
||||
LOGFONTW lf;
|
||||
BITMAPINFO bmi;
|
||||
TEXTMETRIC textMetric;
|
||||
RECT rect;
|
||||
int bytesWidth, dibBytesWidth, extra, imageSize, dibImageSize;
|
||||
unsigned char* dibImage = NULL, *rowPtr, *pixelPtr, *dibPixPtr, *dibRowPtr;
|
||||
unsigned char r,g,b;
|
||||
unsigned char* igTable;
|
||||
GlyphInfo* glyphInfo = NULL;
|
||||
int nameLen;
|
||||
LPWSTR name;
|
||||
HFONT oldFont, hFont;
|
||||
MAT2 mat2;
|
||||
|
||||
unsigned short width;
|
||||
unsigned short height;
|
||||
short advanceX;
|
||||
short advanceY;
|
||||
int topLeftX;
|
||||
int topLeftY;
|
||||
int err;
|
||||
int bmWidth, bmHeight;
|
||||
int x, y;
|
||||
HBITMAP hBitmap = NULL, hOrigBM;
|
||||
int gamma, orient;
|
||||
|
||||
HWND hWnd = NULL;
|
||||
HDC hDesktopDC = NULL;
|
||||
HDC hMemoryDC = NULL;
|
||||
|
||||
hWnd = GetDesktopWindow();
|
||||
hDesktopDC = GetWindowDC(hWnd);
|
||||
if (hDesktopDC == NULL) {
|
||||
return (jlong)0;
|
||||
}
|
||||
if (GetDeviceCaps(hDesktopDC, BITSPIXEL) < 15) {
|
||||
FREE_AND_RETURN;
|
||||
}
|
||||
|
||||
hMemoryDC = CreateCompatibleDC(hDesktopDC);
|
||||
if (hMemoryDC == NULL || fontFamily == NULL) {
|
||||
FREE_AND_RETURN;
|
||||
}
|
||||
err = SetMapMode(hMemoryDC, MM_TEXT);
|
||||
if (err == 0) {
|
||||
FREE_AND_RETURN;
|
||||
}
|
||||
|
||||
memset(&lf, 0, sizeof(LOGFONTW));
|
||||
lf.lfHeight = -size;
|
||||
lf.lfWeight = (style & 1) ? FW_BOLD : FW_NORMAL;
|
||||
lf.lfItalic = (style & 2) ? 0xff : 0;
|
||||
lf.lfCharSet = DEFAULT_CHARSET;
|
||||
lf.lfQuality = CLEARTYPE_QUALITY;
|
||||
lf.lfOutPrecision = OUT_TT_PRECIS;
|
||||
lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
|
||||
lf.lfPitchAndFamily = DEFAULT_PITCH;
|
||||
|
||||
nameLen = (*env)->GetStringLength(env, fontFamily);
|
||||
name = (LPWSTR)alloca((nameLen+1)*2);
|
||||
if (name == NULL) {
|
||||
FREE_AND_RETURN;
|
||||
}
|
||||
(*env)->GetStringRegion(env, fontFamily, 0, nameLen, name);
|
||||
name[nameLen] = '\0';
|
||||
|
||||
if (nameLen < (sizeof(lf.lfFaceName) / sizeof(lf.lfFaceName[0]))) {
|
||||
wcscpy(lf.lfFaceName, name);
|
||||
} else {
|
||||
FREE_AND_RETURN;
|
||||
}
|
||||
|
||||
hFont = CreateFontIndirectW(&lf);
|
||||
if (hFont == NULL) {
|
||||
FREE_AND_RETURN;
|
||||
}
|
||||
oldFont = SelectObject(hMemoryDC, hFont);
|
||||
|
||||
memset(&textMetric, 0, sizeof(TEXTMETRIC));
|
||||
err = GetTextMetrics(hMemoryDC, &textMetric);
|
||||
if (err == 0) {
|
||||
FREE_AND_RETURN;
|
||||
}
|
||||
memset(&glyphMetrics, 0, sizeof(GLYPHMETRICS));
|
||||
memset(&mat2, 0, sizeof(MAT2));
|
||||
mat2.eM11.value = 1; mat2.eM22.value = 1;
|
||||
err = GetGlyphOutline(hMemoryDC, glyphCode,
|
||||
GGO_METRICS|GGO_GLYPH_INDEX,
|
||||
&glyphMetrics,
|
||||
0, NULL, &mat2);
|
||||
if (err == GDI_ERROR) {
|
||||
/* Probably no such glyph - ie the font wasn't the one we expected. */
|
||||
FREE_AND_RETURN;
|
||||
}
|
||||
|
||||
width = (unsigned short)glyphMetrics.gmBlackBoxX;
|
||||
height = (unsigned short)glyphMetrics.gmBlackBoxY;
|
||||
|
||||
/* Don't handle "invisible" glyphs in this code */
|
||||
if (width <= 0 || height == 0) {
|
||||
FREE_AND_RETURN;
|
||||
}
|
||||
|
||||
advanceX = glyphMetrics.gmCellIncX;
|
||||
advanceY = glyphMetrics.gmCellIncY;
|
||||
topLeftX = glyphMetrics.gmptGlyphOrigin.x;
|
||||
topLeftY = glyphMetrics.gmptGlyphOrigin.y;
|
||||
|
||||
/* GetGlyphOutline pre-dates cleartype and I'm not sure that it will
|
||||
* account for all pixels touched by the rendering. Need to widen,
|
||||
* and also adjust by one the x position at which it is rendered.
|
||||
* The extra pixels of width are used as follows :
|
||||
* One extra pixel at the left and the right will be needed to absorb
|
||||
* the pixels that will be touched by filtering by GDI to compensate
|
||||
* for colour fringing.
|
||||
* However there seem to be some cases where GDI renders two extra
|
||||
* pixels to the right, so we add one additional pixel to the right,
|
||||
* and in the code that copies this to the image cache we test for
|
||||
* the (rare) cases when this is touched, and if its not reduce the
|
||||
* stated image width for the blitting loops.
|
||||
* For fractional metrics :
|
||||
* One extra pixel at each end to account for sub-pixel positioning used
|
||||
* when fractional metrics is on in LCD mode.
|
||||
* The pixel at the left is needed so the blitting loop can index into
|
||||
* that a byte at a time to more accurately position the glyph.
|
||||
* The pixel at the right is needed so that when such indexing happens,
|
||||
* the blitting still can use the same width.
|
||||
* Consequently the width that is specified for the glyph is one less
|
||||
* than that of the actual image.
|
||||
* Note that in the FM case as a consequence we need to adjust the
|
||||
* position at which GDI renders, and the declared width of the glyph
|
||||
* See the if (fm) {} cases in the code.
|
||||
* For the non-FM case, we not only save 3 bytes per row, but this
|
||||
* prevents apparent glyph overlapping which affects the rendering
|
||||
* performance of accelerated pipelines since it adds additional
|
||||
* read-back requirements.
|
||||
*/
|
||||
width+=3;
|
||||
if (fm) {
|
||||
width+=1;
|
||||
}
|
||||
/* DIB scanline must end on a DWORD boundary. We specify 3 bytes per pixel,
|
||||
* so must round up as needed to a multiple of 4 bytes.
|
||||
*/
|
||||
dibBytesWidth = bytesWidth = width*3;
|
||||
extra = dibBytesWidth % 4;
|
||||
if (extra != 0) {
|
||||
dibBytesWidth += (4-extra);
|
||||
}
|
||||
/* The glyph cache image must be a multiple of 3 bytes wide. */
|
||||
extra = bytesWidth % 3;
|
||||
if (extra != 0) {
|
||||
bytesWidth += (3-extra);
|
||||
}
|
||||
bmWidth = width;
|
||||
bmHeight = height;
|
||||
|
||||
/* Must use desktop DC to create a bitmap of that depth */
|
||||
hBitmap = CreateCompatibleBitmap(hDesktopDC, bmWidth, bmHeight);
|
||||
if (hBitmap == NULL) {
|
||||
FREE_AND_RETURN;
|
||||
}
|
||||
hOrigBM = (HBITMAP)SelectObject(hMemoryDC, hBitmap);
|
||||
|
||||
/* Fill in black */
|
||||
rect.left = 0;
|
||||
rect.top = 0;
|
||||
rect.right = bmWidth;
|
||||
rect.bottom = bmHeight;
|
||||
FillRect(hMemoryDC, (LPRECT)&rect, GetStockObject(BLACK_BRUSH));
|
||||
|
||||
/* Set text color to white, background to black. */
|
||||
SetBkColor(hMemoryDC, RGB(0,0,0));
|
||||
SetTextColor(hMemoryDC, RGB(255,255,255));
|
||||
|
||||
/* adjust rendering position */
|
||||
x = -topLeftX+1;
|
||||
if (fm) {
|
||||
x += 1;
|
||||
}
|
||||
y = topLeftY - textMetric.tmAscent;
|
||||
err = ExtTextOutW(hMemoryDC, x, y, ETO_GLYPH_INDEX|ETO_OPAQUE,
|
||||
(LPRECT)&rect, (LPCWSTR)&glyphCode, 1, NULL);
|
||||
if (err == 0) {
|
||||
FREE_AND_RETURN;
|
||||
}
|
||||
|
||||
/* Now get the image into a DIB.
|
||||
* MS docs for GetDIBits says the compatible bitmap must not be
|
||||
* selected into a DC, so restore the original first.
|
||||
*/
|
||||
SelectObject(hMemoryDC, hOrigBM);
|
||||
SelectObject(hMemoryDC, oldFont);
|
||||
DeleteObject(hFont);
|
||||
|
||||
memset(&bmi, 0, sizeof(BITMAPINFO));
|
||||
bmi.bmiHeader.biSize = sizeof(bmi.bmiHeader);
|
||||
bmi.bmiHeader.biWidth = width;
|
||||
bmi.bmiHeader.biHeight = -height;
|
||||
bmi.bmiHeader.biPlanes = 1;
|
||||
bmi.bmiHeader.biBitCount = 24;
|
||||
bmi.bmiHeader.biCompression = BI_RGB;
|
||||
|
||||
dibImageSize = dibBytesWidth*height;
|
||||
dibImage = malloc(dibImageSize);
|
||||
if (dibImage == NULL) {
|
||||
FREE_AND_RETURN;
|
||||
}
|
||||
memset(dibImage, 0, dibImageSize);
|
||||
|
||||
err = GetDIBits(hMemoryDC, hBitmap, 0, height, dibImage,
|
||||
&bmi, DIB_RGB_COLORS);
|
||||
|
||||
if (err == 0) { /* GetDIBits failed. */
|
||||
FREE_AND_RETURN;
|
||||
}
|
||||
|
||||
err = SystemParametersInfo(SPI_GETFONTSMOOTHINGORIENTATION, 0, &orient, 0);
|
||||
if (err == 0) {
|
||||
FREE_AND_RETURN;
|
||||
}
|
||||
err = SystemParametersInfo(SPI_GETFONTSMOOTHINGCONTRAST, 0, &gamma, 0);
|
||||
if (err == 0) {
|
||||
FREE_AND_RETURN;
|
||||
}
|
||||
igTable = getIGTable(gamma/10);
|
||||
if (igTable == NULL) {
|
||||
FREE_AND_RETURN;
|
||||
}
|
||||
|
||||
/* Now copy glyph image into a GlyphInfo structure and return it.
|
||||
* NB the xadvance calculated here may be overwritten by the caller.
|
||||
* 1 is subtracted from the bitmap width to get the glyph width, since
|
||||
* that extra "1" was added as padding, so the sub-pixel positioning of
|
||||
* fractional metrics could index into it.
|
||||
*/
|
||||
imageSize = bytesWidth*height;
|
||||
glyphInfo = (GlyphInfo*)malloc(sizeof(GlyphInfo)+imageSize);
|
||||
if (malloc == NULL) {
|
||||
FREE_AND_RETURN;
|
||||
}
|
||||
glyphInfo->cellInfo = NULL;
|
||||
glyphInfo->rowBytes = bytesWidth;
|
||||
glyphInfo->width = width;
|
||||
if (fm) {
|
||||
glyphInfo->width -= 1; // must subtract 1
|
||||
}
|
||||
glyphInfo->height = height;
|
||||
glyphInfo->advanceX = advanceX;
|
||||
glyphInfo->advanceY = advanceY;
|
||||
glyphInfo->topLeftX = (float)(topLeftX-1);
|
||||
if (fm) {
|
||||
glyphInfo->topLeftX -= 1;
|
||||
}
|
||||
glyphInfo->topLeftY = (float)-topLeftY;
|
||||
glyphInfo->image = (unsigned char*)glyphInfo+sizeof(GlyphInfo);
|
||||
memset(glyphInfo->image, 0, imageSize);
|
||||
|
||||
/* DIB 24bpp data is always stored in BGR order, but we usually
|
||||
* need this in RGB, so we can't just memcpy and need to swap B and R.
|
||||
* Also need to apply inverse gamma adjustment here.
|
||||
* We re-use the variable "extra" to see if the last pixel is touched
|
||||
* at all. If its not we can reduce the glyph image width. This comes
|
||||
* into play in some cases where GDI touches more pixels than accounted
|
||||
* for by increasing width by two pixels over the B&W image. Whilst
|
||||
* the bytes are in the cache, it doesn't affect rendering performance
|
||||
* of the hardware pipelines.
|
||||
*/
|
||||
extra = 0;
|
||||
if (fm) {
|
||||
extra = 1; // always need it.
|
||||
}
|
||||
dibRowPtr = dibImage;
|
||||
rowPtr = glyphInfo->image;
|
||||
for (y=0;y<height;y++) {
|
||||
pixelPtr = rowPtr;
|
||||
dibPixPtr = dibRowPtr;
|
||||
for (x=0;x<width;x++) {
|
||||
if (orient == FE_FONTSMOOTHINGORIENTATIONRGB) {
|
||||
b = *dibPixPtr++;
|
||||
g = *dibPixPtr++;
|
||||
r = *dibPixPtr++;
|
||||
} else {
|
||||
r = *dibPixPtr++;
|
||||
g = *dibPixPtr++;
|
||||
b = *dibPixPtr++;
|
||||
}
|
||||
*pixelPtr++ = igTable[r];
|
||||
*pixelPtr++ = igTable[g];
|
||||
*pixelPtr++ = igTable[b];
|
||||
if (!fm && (x==(width-1)) && (r|g|b)) {
|
||||
extra = 1;
|
||||
}
|
||||
}
|
||||
dibRowPtr += dibBytesWidth;
|
||||
rowPtr += bytesWidth;
|
||||
}
|
||||
if (!extra) {
|
||||
glyphInfo->width -= 1;
|
||||
}
|
||||
|
||||
free(dibImage);
|
||||
ReleaseDC(hWnd, hDesktopDC);
|
||||
DeleteObject(hMemoryDC);
|
||||
DeleteObject(hBitmap);
|
||||
|
||||
return ptr_to_jlong(glyphInfo);
|
||||
}
|
||||
@ -0,0 +1,82 @@
|
||||
/*
|
||||
* Copyright 2008 Sun Microsystems, Inc. All Rights Reserved.
|
||||
* 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.
|
||||
*
|
||||
* 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
|
||||
* CA 95054 USA or visit www.sun.com if you need additional information or
|
||||
* have any questions.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @test
|
||||
* @bug 6685312
|
||||
* @summary Check advance of LCD text on a scaled graphics.
|
||||
*/
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import static java.awt.RenderingHints.*;
|
||||
|
||||
public class ScaledLCDTextMetrics extends Component {
|
||||
|
||||
public static void main(String[] args) {
|
||||
JFrame f = new JFrame();
|
||||
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
|
||||
f.add("Center", new ScaledLCDTextMetrics());
|
||||
f.pack();
|
||||
f.setVisible(true);
|
||||
}
|
||||
|
||||
public Dimension getPreferredSize() {
|
||||
return new Dimension(200,100);
|
||||
}
|
||||
public void paint(Graphics g) {
|
||||
Graphics2D g2 = (Graphics2D)g;
|
||||
|
||||
Font f = new Font("Tahoma", Font.PLAIN, 11);
|
||||
g.setFont(f);
|
||||
g.setColor(Color.white);
|
||||
g.fillRect(0,0,400,300);
|
||||
g.setColor(Color.black);
|
||||
g2.setRenderingHint(KEY_TEXT_ANTIALIASING,VALUE_TEXT_ANTIALIAS_LCD_HRGB);
|
||||
String text = "ABCDEFGHIJKLI";
|
||||
|
||||
FontMetrics fm1 = g2.getFontMetrics();
|
||||
int adv1 = fm1.stringWidth(text);
|
||||
g.drawString(text, 5, 20);
|
||||
|
||||
g2.scale(2,2);
|
||||
|
||||
FontMetrics fm2 = g2.getFontMetrics();
|
||||
int adv2 = fm2.stringWidth(text);
|
||||
g.drawString(text, 5, 40);
|
||||
|
||||
double frac = Math.abs(adv1/(double)adv2);
|
||||
|
||||
System.out.println("scalex1: " + adv1);
|
||||
System.out.println("scalex2: " + adv2);
|
||||
System.out.println("Fraction : "+ frac);
|
||||
|
||||
// adv1 will not be exactly the same as adv2, but should differ
|
||||
// only by a fraction.
|
||||
|
||||
if (frac < 0.8 || frac > 1.2) {
|
||||
throw new RuntimeException("Metrics differ " +
|
||||
"Adv1="+adv1+" Adv2="+adv2+" Fraction="+frac);
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user