diff options
6 files changed, 271 insertions, 68 deletions
diff --git a/src/java.desktop/macosx/classes/sun/font/CStrike.java b/src/java.desktop/macosx/classes/sun/font/CStrike.java index 77fb013817b..42f0b179f85 100644 --- a/src/java.desktop/macosx/classes/sun/font/CStrike.java +++ b/src/java.desktop/macosx/classes/sun/font/CStrike.java @@ -40,7 +40,9 @@ public final class CStrike extends PhysicalStrike { double[] glyphTx, double[] invDevTxMatrix, int aaHint, - int fmHint); + int fmHint, + int subpixelResolutionX, + int subpixelResolutionY); // Disposes the native strike private static native void disposeNativeStrikePtr(long nativeStrikePtr); @@ -127,7 +129,9 @@ public final class CStrike extends PhysicalStrike { } nativeStrikePtr = createNativeStrikePtr(nativeFont.getNativeFontPtr(), - glyphTx, invDevTxMatrix, aaHint, fmHint); + glyphTx, invDevTxMatrix, aaHint, fmHint, + FontUtilities.supplementarySubpixelGlyphResolution.width, + FontUtilities.supplementarySubpixelGlyphResolution.height); } return nativeStrikePtr; diff --git a/src/java.desktop/macosx/native/libawt_lwawt/font/AWTStrike.h b/src/java.desktop/macosx/native/libawt_lwawt/font/AWTStrike.h index 905c730d770..cd8ba219036 100644 --- a/src/java.desktop/macosx/native/libawt_lwawt/font/AWTStrike.h +++ b/src/java.desktop/macosx/native/libawt_lwawt/font/AWTStrike.h @@ -35,6 +35,8 @@ JRSFontRenderingStyle fStyle; jint fAAStyle; jint fFmHint; + jint fSubpixelResolutionX; + jint fSubpixelResolutionY; CGAffineTransform fTx; CGAffineTransform fDevTx; @@ -42,6 +44,13 @@ CGAffineTransform fFontTx; } -+ (AWTStrike *) awtStrikeForFont:(AWTFont *)awtFont tx:(CGAffineTransform)tx invDevTx:(CGAffineTransform)invDevTx style:(JRSFontRenderingStyle)style aaStyle:(jint)aaStyle fmHint:(jint)fmHint; ++ (AWTStrike *) awtStrikeForFont:(AWTFont *)awtFont + tx:(CGAffineTransform)tx + invDevTx:(CGAffineTransform)invDevTx + style:(JRSFontRenderingStyle)style + aaStyle:(jint)aaStyle + fmHint:(jint)fmHint + subpixelResolutionX:(jint)subpixelResolutionX + subpixelResolutionY:(jint)subpixelResolutionY; @end diff --git a/src/java.desktop/macosx/native/libawt_lwawt/font/AWTStrike.m b/src/java.desktop/macosx/native/libawt_lwawt/font/AWTStrike.m index 415574f6d76..d737398cc42 100644 --- a/src/java.desktop/macosx/native/libawt_lwawt/font/AWTStrike.m +++ b/src/java.desktop/macosx/native/libawt_lwawt/font/AWTStrike.m @@ -42,7 +42,9 @@ static CGAffineTransform sInverseTX = { 1, 0, 0, -1, 0, 0 }; invDevTx:(CGAffineTransform)invDevTx style:(JRSFontRenderingStyle)style aaStyle:(jint)aaStyle - fmHint:(jint)fmHint { + fmHint:(jint)fmHint +subpixelResolutionX:(jint)subpixelResolutionX +subpixelResolutionY:(jint)subpixelResolutionY { self = [super init]; if (self) { @@ -50,6 +52,8 @@ static CGAffineTransform sInverseTX = { 1, 0, 0, -1, 0, 0 }; fStyle = style; fAAStyle = aaStyle; fFmHint = fmHint; + fSubpixelResolutionX = subpixelResolutionX; + fSubpixelResolutionY = subpixelResolutionY; fTx = tx; // composited glyph and device transform @@ -80,13 +84,17 @@ static CGAffineTransform sInverseTX = { 1, 0, 0, -1, 0, 0 }; invDevTx:(CGAffineTransform)invDevTx style:(JRSFontRenderingStyle)style aaStyle:(jint)aaStyle - fmHint:(jint)fmHint { + fmHint:(jint)fmHint + subpixelResolutionX:(jint)subpixelResolutionX + subpixelResolutionY:(jint)subpixelResolutionY { return [[[AWTStrike alloc] initWithFont:awtFont tx:tx invDevTx:invDevTx style:style aaStyle:aaStyle - fmHint:fmHint] autorelease]; + fmHint:fmHint + subpixelResolutionX:subpixelResolutionX + subpixelResolutionY:subpixelResolutionY] autorelease]; } @end @@ -405,7 +413,8 @@ JNF_COCOA_EXIT(env); * Signature: (J[D[DII)J */ JNIEXPORT jlong JNICALL Java_sun_font_CStrike_createNativeStrikePtr -(JNIEnv *env, jclass clazz, jlong nativeFontPtr, jdoubleArray glyphTxArray, jdoubleArray invDevTxArray, jint aaStyle, jint fmHint) +(JNIEnv *env, jclass clazz, jlong nativeFontPtr, jdoubleArray glyphTxArray, jdoubleArray invDevTxArray, + jint aaStyle, jint fmHint, jint subpixelResolutionX, jint subpixelResolutionY) { AWTStrike *awtStrike = nil; JNF_COCOA_ENTER(env); @@ -416,7 +425,8 @@ JNF_COCOA_ENTER(env); CGAffineTransform glyphTx = GetTxFromDoubles(env, glyphTxArray); CGAffineTransform invDevTx = GetTxFromDoubles(env, invDevTxArray); - awtStrike = [AWTStrike awtStrikeForFont:awtFont tx:glyphTx invDevTx:invDevTx style:style aaStyle:aaStyle fmHint:fmHint]; // autoreleased + awtStrike = [AWTStrike awtStrikeForFont:awtFont tx:glyphTx invDevTx:invDevTx style:style + aaStyle:aaStyle fmHint:fmHint subpixelResolutionX:subpixelResolutionX subpixelResolutionY:subpixelResolutionY]; // autoreleased if (awtStrike) { diff --git a/src/java.desktop/macosx/native/libawt_lwawt/font/CGGlyphImages.m b/src/java.desktop/macosx/native/libawt_lwawt/font/CGGlyphImages.m index 47362ea6ba7..736e5dbb200 100644 --- a/src/java.desktop/macosx/native/libawt_lwawt/font/CGGlyphImages.m +++ b/src/java.desktop/macosx/native/libawt_lwawt/font/CGGlyphImages.m @@ -286,6 +286,64 @@ CGGI_ConvertBWPixelToByteGray(UInt32 p) } static void +CGGI_CopySubpixelImageFromCanvasToAlphaInfo(const UInt32* srcImage, int srcRowWidth, + UInt8* dstImage, + int dstWidth, int dstHeight, + int subpixelResolutionX, + int subpixelResolutionY, + short *tempBuffer) +{ + int srcWidth = dstWidth * subpixelResolutionX; + int srcHeight = dstHeight * subpixelResolutionY; + int imageSize = dstWidth * dstHeight; + int xGlyph, yGlyph, x, y; + // For each subpixel offset by x axis + for (xGlyph = 0; xGlyph < subpixelResolutionX; xGlyph++) { + // Sum values by x axis and store into temporary buffer + for (y = 0; y < srcHeight; y++) { + for (x = 0; x < dstWidth; x++) { + int value = 0; + int xFrom = x * subpixelResolutionX - xGlyph, + xTo = xFrom + subpixelResolutionX; + if (xFrom < 0) xFrom = 0; + if (xTo > srcWidth) xTo = srcWidth; + int i; + for (i = xFrom; i < xTo; i++) { + value += CGGI_ConvertBWPixelToByteGray( + srcImage[y * srcRowWidth + i]); + } + tempBuffer[y * dstWidth + x] = (short) value; + } + } + // For each subpixel offset by y axis + for (yGlyph = 0; yGlyph < subpixelResolutionY; yGlyph++) { + UInt8 *dst = dstImage + + imageSize * (xGlyph + yGlyph * subpixelResolutionX); + // Sum values by y axis and store average into destination image + for (y = 0; y < dstHeight; y++) { + for (x = 0; x < dstWidth; x++) { + int value = 0; + int yFrom = y * subpixelResolutionY - yGlyph, + yTo = yFrom + subpixelResolutionY; + if (yFrom < 0) yFrom = 0; + if (yTo > srcHeight) yTo = srcHeight; + int j; + for (j = yFrom; j < yTo; j++) { + value += tempBuffer[j * dstWidth + x]; + } + dst[y * dstWidth + x] = + value / subpixelResolutionX / subpixelResolutionY; + } + } + } + } +} + +/* Size (in pixels) of stack-allocated temporary buffer for glyph downscaling. + * If glyph is too big and requires more memory, it will use malloc. */ +#define SUBPIXEL_DOWNSCALE_STATIC_BUFFER_SIZE 2048 + +static void CGGI_CopyImageFromCanvasToAlphaInfo(CGGI_GlyphCanvas *canvas, GlyphInfo *info) { UInt32 *src = (UInt32 *)canvas->image->data; @@ -296,16 +354,35 @@ CGGI_CopyImageFromCanvasToAlphaInfo(CGGI_GlyphCanvas *canvas, GlyphInfo *info) size_t height = info->height; - size_t y; - - // fill empty glyph image with black-on-white glyph - for (y = 0; y < height; y++) { - size_t destRow = y * destRowWidth; - size_t srcRow = y * srcRowWidth; - size_t x; - for (x = 0; x < destRowWidth; x++) { - UInt32 p = src[srcRow + x]; - dest[destRow + x] = CGGI_ConvertBWPixelToByteGray(p); + if (info->subpixelResolutionX > 1 || info->subpixelResolutionY > 1) { + int bufferSize = destRowWidth * height * info->subpixelResolutionY; + if (bufferSize <= SUBPIXEL_DOWNSCALE_STATIC_BUFFER_SIZE) { + short staticBuffer[bufferSize]; + CGGI_CopySubpixelImageFromCanvasToAlphaInfo( + src, srcRowWidth, + dest, destRowWidth, height, + info->subpixelResolutionX, info->subpixelResolutionY, + staticBuffer); + } else { + short *buffer = malloc(sizeof(short) * bufferSize); + CGGI_CopySubpixelImageFromCanvasToAlphaInfo( + src, srcRowWidth, + dest, destRowWidth, height, + info->subpixelResolutionX, info->subpixelResolutionY, + buffer); + free(buffer); + } + } else { + size_t y; + // fill empty glyph image with black-on-white glyph + for (y = 0; y < height; y++) { + size_t destRow = y * destRowWidth; + size_t srcRow = y * srcRowWidth; + size_t x; + for (x = 0; x < destRowWidth; x++) { + UInt32 p = src[srcRow + x]; + dest[destRow + x] = CGGI_ConvertBWPixelToByteGray(p); + } } } } @@ -353,8 +430,10 @@ typedef struct CGGI_GlyphInfoDescriptor { } CGGI_GlyphInfoDescriptor; typedef struct CGGI_RenderingMode { - CGGI_GlyphInfoDescriptor *glyphDescriptor; + CGGI_GlyphInfoDescriptor *mainFontDescriptor; JRSFontRenderingStyle cgFontMode; + bool lcdRendering; + bool subpixelResolution; } CGGI_RenderingMode; static CGGI_GlyphInfoDescriptor grey = @@ -379,7 +458,7 @@ static inline CGGI_GlyphInfoDescriptor* CGGI_GetGlyphInfoDescriptor(const CGGI_RenderingMode *mode, CGFontRef font) { bool isFixedColor = CGGI_IsColorFont(font); - return isFixedColor ? &argb : mode->glyphDescriptor; + return isFixedColor ? &argb : mode->mainFontDescriptor; } static inline CGGI_RenderingMode @@ -389,16 +468,26 @@ CGGI_GetRenderingMode(const AWTStrike *strike) mode.cgFontMode = strike->fStyle; NSException *e = nil; +#ifdef USE_IMAGE_ALIGNED_MEMORY + mode.subpixelResolution = false; +#else + mode.subpixelResolution = strike->fAAStyle == sun_awt_SunHints_INTVAL_TEXT_ANTIALIAS_ON && + strike->fFmHint == sun_awt_SunHints_INTVAL_FRACTIONALMETRICS_ON && + (strike->fSubpixelResolutionX > 1 || strike->fSubpixelResolutionY > 1); +#endif + switch (strike->fAAStyle) { case sun_awt_SunHints_INTVAL_TEXT_ANTIALIAS_OFF: case sun_awt_SunHints_INTVAL_TEXT_ANTIALIAS_ON: - mode.glyphDescriptor = &grey; + mode.lcdRendering = false; + mode.mainFontDescriptor = &grey; break; case sun_awt_SunHints_INTVAL_TEXT_ANTIALIAS_LCD_HRGB: case sun_awt_SunHints_INTVAL_TEXT_ANTIALIAS_LCD_HBGR: case sun_awt_SunHints_INTVAL_TEXT_ANTIALIAS_LCD_VRGB: case sun_awt_SunHints_INTVAL_TEXT_ANTIALIAS_LCD_VBGR: - mode.glyphDescriptor = &rgb; + mode.lcdRendering = true; + mode.mainFontDescriptor = &rgb; break; case sun_awt_SunHints_INTVAL_TEXT_ANTIALIAS_GASP: case sun_awt_SunHints_INTVAL_TEXT_ANTIALIAS_DEFAULT: @@ -412,6 +501,10 @@ CGGI_GetRenderingMode(const AWTStrike *strike) userInfo:nil]; @throw e; } + if (CGGI_IsColorFont(strike->fAWTFont->fNativeCGFont)) { + mode.mainFontDescriptor = &argb; + mode.subpixelResolution = false; + } return mode; } @@ -444,7 +537,7 @@ CGGI_InitCanvas(CGGI_GlyphCanvas *canvas, } uint32_t bmpInfo = kCGImageAlphaPremultipliedFirst; - if (mode->glyphDescriptor == &rgb) { + if (mode->lcdRendering) { bmpInfo |= kCGBitmapByteOrder32Host; } @@ -522,8 +615,8 @@ CGGI_ClearCanvas(CGGI_GlyphCanvas *canvas, GlyphInfo *info, bool transparent) { vImage_Buffer canvasRectToClear; canvasRectToClear.data = canvas->image->data; - canvasRectToClear.height = info->height; - canvasRectToClear.width = info->width; + canvasRectToClear.height = info->height * info->subpixelResolutionY; + canvasRectToClear.width = info->width * info->subpixelResolutionX; // use the row stride of the canvas, not the info canvasRectToClear.rowBytes = canvas->image->rowBytes; @@ -561,7 +654,8 @@ static inline CGSize CGGI_ScaleAdvance(CGSize advance, const AWTStrike *strike) static inline GlyphInfo * CGGI_CreateNewGlyphInfoFrom(CGSize advance, CGRect bbox, const AWTStrike *strike, - const CGGI_GlyphInfoDescriptor *glyphDescriptor) + const CGGI_GlyphInfoDescriptor *glyphDescriptor, + bool subpixelResolution) { size_t pixelSize = glyphDescriptor->pixelSize; @@ -573,8 +667,23 @@ CGGI_CreateNewGlyphInfoFrom(CGSize advance, CGRect bbox, bbox.origin.x -= CGGI_GLYPH_BBOX_PADDING; bbox.origin.y -= CGGI_GLYPH_BBOX_PADDING; - vImagePixelCount width = ceilf(bbox.size.width); - vImagePixelCount height = ceilf(bbox.size.height); + int subpixelResX = 1; + int subpixelResY = 1; + int topLeftX, topLeftY; + vImagePixelCount width, height; + if (subpixelResolution) { + subpixelResX = strike->fSubpixelResolutionX; + subpixelResY = strike->fSubpixelResolutionY; + topLeftX = floorf(bbox.origin.x / (float) subpixelResX); + topLeftY = floorf(bbox.origin.y / (float) subpixelResY); + width = ceilf((bbox.origin.x + bbox.size.width) / (float) subpixelResX) - topLeftX; + height = ceilf((bbox.origin.y + bbox.size.height) / (float) subpixelResY) - topLeftY; + } else { + topLeftX = round(bbox.origin.x); + topLeftY = round(bbox.origin.y); + width = ceilf(bbox.size.width); + height = ceilf(bbox.size.height); + } // if the glyph is larger than 1MB, don't even try... // the GlyphVector path should have taken over by now @@ -595,19 +704,20 @@ CGGI_CreateNewGlyphInfoFrom(CGSize advance, CGRect bbox, void *image = (void *)malloc(imageBytes + extraPixelStorage); #else // create a GlyphInfo struct fused to the image it points to - GlyphInfo *glyphInfo = (GlyphInfo *)malloc(sizeof(GlyphInfo) + imageBytes + extraPixelStorage); + GlyphInfo *glyphInfo = (GlyphInfo *)malloc(sizeof(GlyphInfo) + + (imageBytes + extraPixelStorage) * subpixelResX * subpixelResY); #endif glyphInfo->advanceX = advance.width; glyphInfo->advanceY = advance.height; - glyphInfo->topLeftX = round(bbox.origin.x); - glyphInfo->topLeftY = round(bbox.origin.y); + glyphInfo->topLeftX = topLeftX; + glyphInfo->topLeftY = topLeftY; glyphInfo->width = width; glyphInfo->height = height; glyphInfo->rowBytes = width * pixelSize; glyphInfo->cellInfo = NULL; - glyphInfo->subpixelResolutionX = 1; - glyphInfo->subpixelResolutionY = 1; + glyphInfo->subpixelResolutionX = subpixelResX; + glyphInfo->subpixelResolutionY = subpixelResY; #ifdef USE_IMAGE_ALIGNED_MEMORY glyphInfo->image = image; @@ -624,6 +734,19 @@ CGGI_CreateNewGlyphInfoFrom(CGSize advance, CGRect bbox, #pragma mark --- Glyph Striking onto Canvas --- +static inline void CGGI_ScaleTXForSubpixelResolution(CGAffineTransform *tx, + const AWTStrike *strike, + bool subpixelResolution) { + if (subpixelResolution) { + float subResX = (float) strike->fSubpixelResolutionX; + float subResY = (float) strike->fSubpixelResolutionY; + tx->a *= subResX; + tx->b *= subResY; + tx->c *= subResX; + tx->d *= subResY; + } +} + /* * Clears the canvas, strikes the glyph with CoreGraphics, and then * copies the struck pixels into the GlyphInfo image. @@ -649,8 +772,8 @@ CGGI_CreateImageForGlyph CGGI_ClearCanvas(canvas, info, glyphDescriptor == &argb); // strike the glyph in the upper right corner - CGFloat x = -info->topLeftX; - CGFloat y = canvas->image->height + info->topLeftY; + CGFloat x = -info->topLeftX * (float) info->subpixelResolutionX; + CGFloat y = canvas->image->height + info->topLeftY * (float) info->subpixelResolutionY; if (isCatalinaOrAbove || glyphDescriptor == &argb) { CGAffineTransform matrix = CGContextGetTextMatrix(canvas->context); @@ -699,8 +822,10 @@ CGGI_CreateImageForUnicode const CGGI_RenderingMode *mode, const UnicodeScalarValue uniChar, const bool isCatalinaOrAbove) { - // save the state of the world + // save the graphics state CGContextSaveGState(canvas->context); + // text matrix is not considered part of graphics state + CGAffineTransform originalTx = CGContextGetTextMatrix(canvas->context); // get the glyph, measure it using CG CGGlyph glyph; @@ -717,26 +842,32 @@ CGGI_CreateImageForUnicode fallback = CTS_CopyCTFallbackFontAndGlyphForUnicode(strike->fAWTFont, (const UTF16Char *)&charRef, &glyph, 1); } - CGAffineTransform tx = strike->fTx; JRSFontRenderingStyle style = JRSFontAlignStyleForFractionalMeasurement(strike->fStyle); + const CGFontRef cgFallback = CTFontCopyGraphicsFont(fallback, NULL); + CGGI_GlyphInfoDescriptor *glyphDescriptor = CGGI_GetGlyphInfoDescriptor(mode, cgFallback); + + bool subpixelResolution = mode->subpixelResolution && glyphDescriptor == &grey; + + CGAffineTransform tx = strike->fTx; + CGGI_ScaleTXForSubpixelResolution(&tx, strike, subpixelResolution); CGRect bbox; JRSFontGetBoundingBoxesForGlyphsAndStyle(fallback, &tx, style, &glyph, 1, &bbox); CGSize advance; CTFontGetAdvancesForGlyphs(fallback, kCTFontDefaultOrientation, &glyph, &advance, 1); - const CGFontRef cgFallback = CTFontCopyGraphicsFont(fallback, NULL); - CGGI_GlyphInfoDescriptor *glyphDescriptor = CGGI_GetGlyphInfoDescriptor(mode, cgFallback); // create the Sun2D GlyphInfo we are going to strike into - GlyphInfo *info = CGGI_CreateNewGlyphInfoFrom(advance, bbox, strike, glyphDescriptor); + GlyphInfo *info = CGGI_CreateNewGlyphInfoFrom(advance, bbox, strike, glyphDescriptor, subpixelResolution); // fix the context size, just in case the substituted character is unexpectedly large - CGGI_SizeCanvas(canvas, info->width, info->height, mode); + CGGI_SizeCanvas(canvas, info->width * info->subpixelResolutionX, info->height * info->subpixelResolutionY, mode); // align the transform for the real CoreText strike - CGContextSetTextMatrix(canvas->context, strike->fAltTx); + CGAffineTransform altTx = strike->fAltTx; + CGGI_ScaleTXForSubpixelResolution(&altTx, strike, subpixelResolution); + CGContextSetTextMatrix(canvas->context, altTx); CGContextSetFont(canvas->context, cgFallback); CFRelease(cgFallback); @@ -744,8 +875,9 @@ CGGI_CreateImageForUnicode // clean the canvas - align, strike, and copy the glyph from the canvas into the info CGGI_CreateImageForGlyph(cgFallback, canvas, glyph, info, glyphDescriptor, strike, isCatalinaOrAbove); - // restore the state of the world + // restore graphics state CGContextRestoreGState(canvas->context); + CGContextSetTextMatrix(canvas->context, originalTx); CFRelease(fallback); #ifdef CGGI_DEBUG @@ -780,20 +912,20 @@ CGGI_FillImagesForGlyphsWithSizedCanvas(CGGI_GlyphCanvas *canvas, const CGGlyph glyphs[], const CFIndex len) { - CGContextSetTextMatrix(canvas->context, strike->fAltTx); + CGAffineTransform tx = strike->fAltTx; + CGGI_ScaleTXForSubpixelResolution(&tx, strike, mode->subpixelResolution); + CGContextSetTextMatrix(canvas->context, tx); CGContextSetFont(canvas->context, strike->fAWTFont->fNativeCGFont); JRSFontSetRenderingStyleOnContext(canvas->context, strike->fStyle); - CGGI_GlyphInfoDescriptor* mainFontDescriptor = CGGI_GetGlyphInfoDescriptor(mode, strike->fAWTFont->fNativeCGFont); - const bool isMojaveOrAbove = IS_OSX_GT10_13; CFIndex i; for (i = 0; i < len; i++) { GlyphInfo *info = (GlyphInfo *)jlong_to_ptr(glyphInfos[i]); if (info != NULL) { CGGI_CreateImageForGlyph(strike->fAWTFont->fNativeCGFont, - canvas, glyphs[i], info, mainFontDescriptor, strike, isMojaveOrAbove); + canvas, glyphs[i], info, mode->mainFontDescriptor, strike, isMojaveOrAbove); } else { info = CGGI_CreateImageForUnicode(canvas, strike, mode, uniChars[i], isMojaveOrAbove); glyphInfos[i] = ptr_to_jlong(info); @@ -852,7 +984,7 @@ CGGI_FillImagesForGlyphs(jlong *glyphInfos, const AWTStrike *strike, NSMutableDictionary *threadDict = [[NSThread currentThread] threadDictionary]; - NSString* theKey = (mode->glyphDescriptor == &rgb) ? + NSString* theKey = mode->lcdRendering ? threadLocalLCDCanvasKey : threadLocalAACanvasKey; CGGI_GlyphCanvas *canvas = [threadDict objectForKey:theKey]; @@ -884,6 +1016,7 @@ CGGI_CreateGlyphInfos(jlong *glyphInfos, const AWTStrike *strike, { AWTFont *font = strike->fAWTFont; CGAffineTransform tx = strike->fTx; + CGGI_ScaleTXForSubpixelResolution(&tx, strike, mode->subpixelResolution); JRSFontRenderingStyle bboxCGMode = JRSFontAlignStyleForFractionalMeasurement(strike->fStyle); JRSFontGetBoundingBoxesForGlyphsAndStyle((CTFontRef)font->fFont, &tx, bboxCGMode, glyphs, len, bboxes); @@ -892,8 +1025,6 @@ CGGI_CreateGlyphInfos(jlong *glyphInfos, const AWTStrike *strike, size_t maxWidth = 1; size_t maxHeight = 1; - CGGI_GlyphInfoDescriptor* mainFontDescriptor = CGGI_GetGlyphInfoDescriptor(mode, strike->fAWTFont->fNativeCGFont); - CFIndex i; for (i = 0; i < len; i++) { @@ -906,10 +1037,15 @@ CGGI_CreateGlyphInfos(jlong *glyphInfos, const AWTStrike *strike, CGSize advance = advances[i]; CGRect bbox = bboxes[i]; - GlyphInfo *glyphInfo = CGGI_CreateNewGlyphInfoFrom(advance, bbox, strike, mainFontDescriptor); + GlyphInfo *glyphInfo = CGGI_CreateNewGlyphInfoFrom(advance, bbox, strike, + mode->mainFontDescriptor, + mode->subpixelResolution); + + int w = glyphInfo->width * glyphInfo->subpixelResolutionX; + int h = glyphInfo->height * glyphInfo->subpixelResolutionY; - if (maxWidth < glyphInfo->width) maxWidth = glyphInfo->width; - if (maxHeight < glyphInfo->height) maxHeight = glyphInfo->height; + if (maxWidth < w) maxWidth = w; + if (maxHeight < h) maxHeight = h; glyphInfos[i] = ptr_to_jlong(glyphInfo); } diff --git a/src/java.desktop/share/native/common/font/AccelGlyphCache.h b/src/java.desktop/share/native/common/font/AccelGlyphCache.h index 4ffb15a4ea5..b4aa7d3c2f7 100644 --- a/src/java.desktop/share/native/common/font/AccelGlyphCache.h +++ b/src/java.desktop/share/native/common/font/AccelGlyphCache.h @@ -57,6 +57,9 @@ struct _CacheCellInfo { // REMIND: find better name? // next cell info in the glyph's cell list (next Glyph Cache Info) CacheCellInfo *nextGCI; + // Glyph subimage ID, used to distinguish between different images + // Only makes sense when subpixel resolution is enabled for the glyph + jint glyphSubimage; jint timesRendered; jint x; jint y; diff --git a/src/java.desktop/share/native/common/java2d/opengl/OGLTextRenderer.c b/src/java.desktop/share/native/common/java2d/opengl/OGLTextRenderer.c index 1e65eabc95a..47654aeb22c 100644 --- a/src/java.desktop/share/native/common/java2d/opengl/OGLTextRenderer.c +++ b/src/java.desktop/share/native/common/java2d/opengl/OGLTextRenderer.c @@ -242,7 +242,7 @@ OGLTR_InitGlyphCache(jboolean lcdCache) * associated with the given OGLContext. */ static void -OGLTR_AddToGlyphCache(GlyphInfo *glyph, GLenum pixelFormat) +OGLTR_AddToGlyphCache(GlyphInfo *glyph, GLenum pixelFormat, jint subimage) { CacheCellInfo *ccinfo; GlyphCacheInfo *gcinfo; @@ -261,15 +261,16 @@ OGLTR_AddToGlyphCache(GlyphInfo *glyph, GLenum pixelFormat) return; } - AccelGlyphCache_AddGlyph(gcinfo, glyph); - ccinfo = (CacheCellInfo *) glyph->cellInfo; + ccinfo = AccelGlyphCache_AddGlyph(gcinfo, glyph); if (ccinfo != NULL) { + ccinfo->glyphSubimage = subimage; // store glyph image in texture cell j2d_glTexSubImage2D(GL_TEXTURE_2D, 0, ccinfo->x, ccinfo->y, glyph->width, glyph->height, - pixelFormat, GL_UNSIGNED_BYTE, glyph->image); + pixelFormat, GL_UNSIGNED_BYTE, glyph->image + + (glyph->rowBytes * glyph->height) * subimage); } } @@ -811,7 +812,8 @@ OGLTR_DisableGlyphModeState() } static jboolean -OGLTR_DrawGrayscaleGlyphViaCache(OGLContext *oglc, GlyphInfo *ginfo, jint x, jint y, jboolean useFontSmoothing) +OGLTR_DrawGrayscaleGlyphViaCache(OGLContext *oglc, GlyphInfo *ginfo, jint x, jint y, + jboolean useFontSmoothing, jint subimage) { CacheCellInfo *cell; jfloat x1, y1, x2, y2; @@ -834,17 +836,39 @@ OGLTR_DrawGrayscaleGlyphViaCache(OGLContext *oglc, GlyphInfo *ginfo, jint x, jin glyphMode = MODE_USE_CACHE_GRAY; } - if (ginfo->cellInfo == NULL) { - // attempt to add glyph to accelerated glyph cache - OGLTR_AddToGlyphCache(ginfo, GL_LUMINANCE); + int rx = ginfo->subpixelResolutionX; + int ry = ginfo->subpixelResolutionY; + if (subimage == 0 && ((rx == 1 && ry == 1) || rx <= 0 || ry <= 0)) { + // Subpixel rendering disabled, there must be only one cell info + cell = (CacheCellInfo *) (ginfo->cellInfo); + } else { + // Subpixel rendering enabled, find subimage in cell list + cell = NULL; + CacheCellInfo *c = (CacheCellInfo *) (ginfo->cellInfo); + while (c != NULL) { + if (c->glyphSubimage == subimage) { + cell = c; + break; + } + c = c->nextGCI; + } + } - if (ginfo->cellInfo == NULL) { + if (cell == NULL) { + // attempt to add glyph to accelerated glyph cache + OGLTR_AddToGlyphCache(ginfo, GL_LUMINANCE, subimage); + // Our image, added to cache will be the first, so we take it. + // If for whatever reason we failed to add it to our cache, + // take first cell anyway, it's still better to render glyph + // image with wrong subpixel offset than render nothing. + cell = (CacheCellInfo *) (ginfo->cellInfo); + + if (cell == NULL) { // we'll just no-op in the rare case that the cell is NULL return JNI_TRUE; } } - cell = (CacheCellInfo *) (ginfo->cellInfo); cell->timesRendered++; x1 = (jfloat)x; @@ -1039,7 +1063,7 @@ OGLTR_DrawLCDGlyphViaCache(OGLContext *oglc, OGLSDOps *dstOps, GlyphInfo *ginfo, j2d_glActiveTextureARB(GL_TEXTURE0_ARB); // attempt to add glyph to accelerated glyph cache - OGLTR_AddToGlyphCache(ginfo, rgbOrder ? GL_RGB : GL_BGR); + OGLTR_AddToGlyphCache(ginfo, rgbOrder ? GL_RGB : GL_BGR, 0); if (ginfo->cellInfo == NULL) { // we'll just no-op in the rare case that the cell is NULL @@ -1096,7 +1120,8 @@ OGLTR_DrawLCDGlyphViaCache(OGLContext *oglc, OGLSDOps *dstOps, GlyphInfo *ginfo, static jboolean OGLTR_DrawGrayscaleGlyphNoCache(OGLContext *oglc, - GlyphInfo *ginfo, jint x, jint y) + GlyphInfo *ginfo, jint x, jint y, + jint subimage) { jint tw, th; jint sx, sy, sw, sh; @@ -1123,7 +1148,8 @@ OGLTR_DrawGrayscaleGlyphNoCache(OGLContext *oglc, OGLVertexCache_AddMaskQuad(oglc, sx, sy, x, y, sw, sh, - w, ginfo->image); + w, ginfo->image + + (ginfo->rowBytes * ginfo->height) * subimage); } } @@ -1317,6 +1343,9 @@ extern int useFontSmoothing; #define FLOOR_ASSIGN(l, r) \ if ((r)<0) (l) = ((int)floor(r)); else (l) = ((int)(r)) +#define ADJUST_SUBPIXEL_GLYPH_POSITION(coord, res) \ + if ((res) > 1) (coord) += 0.5f / ((float)(res)) - 0.5f + void OGLTR_DrawGlyphList(JNIEnv *env, OGLContext *oglc, OGLSDOps *dstOps, jint totalGlyphs, jboolean usePositions, @@ -1388,11 +1417,15 @@ OGLTR_DrawGlyphList(JNIEnv *env, OGLContext *oglc, OGLSDOps *dstOps, jfloat posy = NEXT_FLOAT(positions); glyphx = glyphListOrigX + posx + ginfo->topLeftX; glyphy = glyphListOrigY + posy + ginfo->topLeftY; + ADJUST_SUBPIXEL_GLYPH_POSITION(glyphx, ginfo->subpixelResolutionX); + ADJUST_SUBPIXEL_GLYPH_POSITION(glyphy, ginfo->subpixelResolutionY); FLOOR_ASSIGN(x, glyphx); FLOOR_ASSIGN(y, glyphy); } else { glyphx = glyphListOrigX + ginfo->topLeftX; glyphy = glyphListOrigY + ginfo->topLeftY; + ADJUST_SUBPIXEL_GLYPH_POSITION(glyphx, ginfo->subpixelResolutionX); + ADJUST_SUBPIXEL_GLYPH_POSITION(glyphy, ginfo->subpixelResolutionY); FLOOR_ASSIGN(x, glyphx); FLOOR_ASSIGN(y, glyphy); glyphListOrigX += ginfo->advanceX; @@ -1406,12 +1439,20 @@ OGLTR_DrawGlyphList(JNIEnv *env, OGLContext *oglc, OGLSDOps *dstOps, if (ginfo->rowBytes == ginfo->width) { OGLMTVertexCache_disable(); // grayscale or monochrome glyph data + int rx = ginfo->subpixelResolutionX; + int ry = ginfo->subpixelResolutionY; + int subimage; + if ((rx == 1 && ry == 1) || rx <= 0 || ry <= 0) { + subimage = 0; + } else { + subimage = (jint)((glyphx - x) * rx) + (jint)((glyphy - y) * ry) * rx; + } if (ginfo->width <= OGLTR_CACHE_CELL_WIDTH && ginfo->height <= OGLTR_CACHE_CELL_HEIGHT) { - ok = OGLTR_DrawGrayscaleGlyphViaCache(oglc, ginfo, x, y, fontSmoothing); + ok = OGLTR_DrawGrayscaleGlyphViaCache(oglc, ginfo, x, y, fontSmoothing, subimage); } else { - ok = OGLTR_DrawGrayscaleGlyphNoCache(oglc, ginfo, x, y); + ok = OGLTR_DrawGrayscaleGlyphNoCache(oglc, ginfo, x, y, subimage); } } else if (ginfo->rowBytes == ginfo->width * 4) { OGLMTVertexCache_disable(); |