4632N/A/*
4632N/A * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
4632N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4632N/A *
4632N/A * This code is free software; you can redistribute it and/or modify it
4632N/A * under the terms of the GNU General Public License version 2 only, as
4632N/A * published by the Free Software Foundation. Oracle designates this
4632N/A * particular file as subject to the "Classpath" exception as provided
4632N/A * by Oracle in the LICENSE file that accompanied this code.
4632N/A *
4632N/A * This code is distributed in the hope that it will be useful, but WITHOUT
4632N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
4632N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
4632N/A * version 2 for more details (a copy is included in the LICENSE file that
4632N/A * accompanied this code).
4632N/A *
4632N/A * You should have received a copy of the GNU General Public License version
4632N/A * 2 along with this work; if not, write to the Free Software Foundation,
4632N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
4632N/A *
4632N/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
4632N/A * or visit www.oracle.com if you need additional information or have any
4632N/A * questions.
4632N/A */
4632N/A
4632N/A#import <Accelerate/Accelerate.h> // for vImage_Buffer
4632N/A#import <JavaNativeFoundation/JavaNativeFoundation.h>
4632N/A
4632N/A#import "CGGlyphImages.h"
4632N/A#import "CoreTextSupport.h"
4632N/A#import "fontscalerdefs.h" // contains the definition of GlyphInfo struct
4632N/A
4632N/A#import "sun_awt_SunHints.h"
4632N/A
4632N/A//#define USE_IMAGE_ALIGNED_MEMORY 1
4632N/A//#define CGGI_DEBUG 1
4632N/A//#define CGGI_DEBUG_DUMP 1
4632N/A//#define CGGI_DEBUG_HIT_COUNT 1
4632N/A
4632N/A#define PRINT_TX(x) \
4632N/A NSLog(@"(%f, %f, %f, %f, %f, %f)", x.a, x.b, x.c, x.d, x.tx, x.ty);
4632N/A
4632N/A/*
4632N/A * The GlyphCanvas is a global shared CGContext that characters are struck into.
4632N/A * For each character, the glyph is struck, copied into a GlyphInfo struct, and
4632N/A * the canvas is cleared for the next glyph.
4632N/A *
4632N/A * If the necessary canvas is too large, the shared one will not be used and a
4632N/A * temporary one will be provided.
4632N/A */
4632N/A@interface CGGI_GlyphCanvas : NSObject {
4632N/A@public
4632N/A CGContextRef context;
4632N/A vImage_Buffer *image;
4632N/A}
4632N/A@end;
4632N/A
4632N/A@implementation CGGI_GlyphCanvas
4632N/A@end
4632N/A
4632N/A
4632N/A#pragma mark --- Debugging Helpers ---
4632N/A
4632N/A/*
4632N/A * These debug functions are only compiled when CGGI_DEBUG is activated.
4632N/A * They will print out a full UInt8 canvas and any pixels struck (assuming
4632N/A * the canvas is not too big).
4632N/A *
4632N/A * As another debug feature, the entire canvas will be filled with a light
4632N/A * alpha value so it is easy to see where the glyph painting regions are
4632N/A * at runtime.
4632N/A */
4632N/A
4632N/A#ifdef CGGI_DEBUG_DUMP
4632N/Astatic void
4632N/ADUMP_PIXELS(const char msg[], const UInt8 pixels[],
4632N/A const size_t bytesPerPixel, const int width, const int height)
4632N/A{
4632N/A printf("| %s: (%d, %d)\n", msg, width, height);
4632N/A
4632N/A if (width > 80 || height > 80) {
4632N/A printf("| too big\n");
4632N/A return;
4632N/A }
4632N/A
4632N/A size_t i, j = 0, k, size = width * height;
4632N/A for (i = 0; i < size; i++) {
4632N/A for (k = 0; k < bytesPerPixel; k++) {
4632N/A if (pixels[i * bytesPerPixel + k] > 0x80) j++;
4632N/A }
4632N/A }
4632N/A
4632N/A if (j == 0) {
4632N/A printf("| empty\n");
4632N/A return;
4632N/A }
4632N/A
4632N/A printf("|_");
4632N/A int x, y;
4632N/A for (x = 0; x < width; x++) {
4632N/A printf("__");
4632N/A }
4632N/A printf("_\n");
4632N/A
4632N/A for (y = 0; y < height; y++) {
4632N/A printf("| ");
4632N/A for (x = 0; x < width; x++) {
4632N/A int p = 0;
4632N/A for(k = 0; k < bytesPerPixel; k++) {
4632N/A p += pixels[(y * width + x) * bytesPerPixel + k];
4632N/A }
4632N/A
4632N/A if (p < 0x80) {
4632N/A printf(" ");
4632N/A } else {
4632N/A printf("[]");
4632N/A }
4632N/A }
4632N/A printf(" |\n");
4632N/A }
4632N/A}
4632N/A
4632N/Astatic void
4632N/ADUMP_IMG_PIXELS(const char msg[], const vImage_Buffer *image)
4632N/A{
4632N/A const void *pixels = image->data;
4632N/A const size_t pixelSize = image->rowBytes / image->width;
4632N/A const size_t width = image->width;
4632N/A const size_t height = image->height;
4632N/A
4632N/A DUMP_PIXELS(msg, pixels, pixelSize, width, height);
4632N/A}
4632N/A
4632N/Astatic void
4632N/APRINT_CGSTATES_INFO(const CGContextRef cgRef)
4632N/A{
4632N/A // TODO(cpc): lots of SPI use in this method; remove/rewrite?
4632N/A#if 0
4632N/A CGRect clip = CGContextGetClipBoundingBox(cgRef);
4632N/A fprintf(stderr, " clip: ((%f, %f), (%f, %f))\n",
4632N/A clip.origin.x, clip.origin.y, clip.size.width, clip.size.height);
4632N/A
4632N/A CGAffineTransform ctm = CGContextGetCTM(cgRef);
4632N/A fprintf(stderr, " ctm: (%f, %f, %f, %f, %f, %f)\n",
4632N/A ctm.a, ctm.b, ctm.c, ctm.d, ctm.tx, ctm.ty);
4632N/A
4632N/A CGAffineTransform txtTx = CGContextGetTextMatrix(cgRef);
4632N/A fprintf(stderr, " txtTx: (%f, %f, %f, %f, %f, %f)\n",
4632N/A txtTx.a, txtTx.b, txtTx.c, txtTx.d, txtTx.tx, txtTx.ty);
4632N/A
4632N/A if (CGContextIsPathEmpty(cgRef) == 0) {
4632N/A CGPoint pathpoint = CGContextGetPathCurrentPoint(cgRef);
4632N/A CGRect pathbbox = CGContextGetPathBoundingBox(cgRef);
4632N/A fprintf(stderr, " [pathpoint: (%f, %f)] [pathbbox: ((%f, %f), (%f, %f))]\n",
4632N/A pathpoint.x, pathpoint.y, pathbbox.origin.x, pathbbox.origin.y,
4632N/A pathbbox.size.width, pathbbox.size.width);
4632N/A }
4632N/A
4632N/A CGFloat linewidth = CGContextGetLineWidth(cgRef);
4632N/A CGLineCap linecap = CGContextGetLineCap(cgRef);
4632N/A CGLineJoin linejoin = CGContextGetLineJoin(cgRef);
4632N/A CGFloat miterlimit = CGContextGetMiterLimit(cgRef);
4632N/A size_t dashcount = CGContextGetLineDashCount(cgRef);
4632N/A fprintf(stderr, " [linewidth: %f] [linecap: %d] [linejoin: %d] [miterlimit: %f] [dashcount: %lu]\n",
4632N/A linewidth, linecap, linejoin, miterlimit, (unsigned long)dashcount);
4632N/A
4632N/A CGFloat smoothness = CGContextGetSmoothness(cgRef);
4632N/A bool antialias = CGContextGetShouldAntialias(cgRef);
4632N/A bool smoothfont = CGContextGetShouldSmoothFonts(cgRef);
4632N/A JRSFontRenderingStyle fRendMode = CGContextGetFontRenderingMode(cgRef);
4632N/A fprintf(stderr, " [smoothness: %f] [antialias: %d] [smoothfont: %d] [fontrenderingmode: %d]\n",
4632N/A smoothness, antialias, smoothfont, fRendMode);
4632N/A#endif
4632N/A}
4632N/A#endif
4632N/A
4632N/A#ifdef CGGI_DEBUG
4632N/A
4632N/Astatic void
4632N/ADUMP_GLYPHINFO(const GlyphInfo *info)
4632N/A{
4632N/A printf("size: (%d, %d) pixelSize: %d\n",
4632N/A info->width, info->height, info->rowBytes / info->width);
4632N/A printf("adv: (%f, %f) top: (%f, %f)\n",
4632N/A info->advanceX, info->advanceY, info->topLeftX, info->topLeftY);
4632N/A
4632N/A#ifdef CGGI_DEBUG_DUMP
4632N/A DUMP_PIXELS("Glyph Info Struct",
4632N/A info->image, info->rowBytes / info->width,
4632N/A info->width, info->height);
4632N/A#endif
4632N/A}
4632N/A
4632N/A#endif
4632N/A
4632N/A
4632N/A#pragma mark --- Font Rendering Mode Descriptors ---
4632N/A
4632N/Astatic inline void
4632N/ACGGI_CopyARGBPixelToRGBPixel(const UInt32 p, UInt8 *dst)
4632N/A{
4632N/A#if __LITTLE_ENDIAN__
4632N/A *(dst + 2) = 0xFF - (p >> 24 & 0xFF);
4632N/A *(dst + 1) = 0xFF - (p >> 16 & 0xFF);
4632N/A *(dst) = 0xFF - (p >> 8 & 0xFF);
4632N/A#else
4632N/A *(dst) = 0xFF - (p >> 16 & 0xFF);
4632N/A *(dst + 1) = 0xFF - (p >> 8 & 0xFF);
4632N/A *(dst + 2) = 0xFF - (p & 0xFF);
4632N/A#endif
4632N/A}
4632N/A
4632N/Astatic void
4632N/ACGGI_CopyImageFromCanvasToRGBInfo(CGGI_GlyphCanvas *canvas, GlyphInfo *info)
4632N/A{
4632N/A UInt32 *src = (UInt32 *)canvas->image->data;
4632N/A size_t srcRowWidth = canvas->image->width;
4632N/A
4632N/A UInt8 *dest = (UInt8 *)info->image;
4632N/A size_t destRowWidth = info->width;
4632N/A
4632N/A size_t height = info->height;
4632N/A
4632N/A size_t y;
4632N/A for (y = 0; y < height; y++) {
4632N/A size_t destRow = y * destRowWidth * 3;
4632N/A size_t srcRow = y * srcRowWidth;
4632N/A
4632N/A size_t x;
4632N/A for (x = 0; x < destRowWidth; x++) {
4632N/A // size_t x3 = x * 3;
4632N/A // UInt32 p = src[srcRow + x];
4632N/A // dest[destRow + x3] = 0xFF - (p >> 16 & 0xFF);
4632N/A // dest[destRow + x3 + 1] = 0xFF - (p >> 8 & 0xFF);
4632N/A // dest[destRow + x3 + 2] = 0xFF - (p & 0xFF);
4632N/A CGGI_CopyARGBPixelToRGBPixel(src[srcRow + x],
4632N/A dest + destRow + x * 3);
4632N/A }
4632N/A }
4632N/A}
4632N/A
4632N/A//static void CGGI_copyImageFromCanvasToAlphaInfo
4632N/A//(CGGI_GlyphCanvas *canvas, GlyphInfo *info)
4632N/A//{
4632N/A// vImage_Buffer infoBuffer;
4632N/A// infoBuffer.data = info->image;
4632N/A// infoBuffer.width = info->width;
4632N/A// infoBuffer.height = info->height;
4632N/A// infoBuffer.rowBytes = info->width; // three bytes per RGB pixel
4632N/A//
4632N/A// UInt8 scrapPixel[info->width * info->height];
4632N/A// vImage_Buffer scrapBuffer;
4632N/A// scrapBuffer.data = &scrapPixel;
4632N/A// scrapBuffer.width = info->width;
4632N/A// scrapBuffer.height = info->height;
4632N/A// scrapBuffer.rowBytes = info->width;
4632N/A//
4632N/A// vImageConvert_ARGB8888toPlanar8(canvas->image, &infoBuffer,
4632N/A// &scrapBuffer, &scrapBuffer, &scrapBuffer, kvImageNoFlags);
4632N/A//}
4632N/A
4632N/Astatic inline UInt8
4632N/ACGGI_ConvertPixelToGreyBit(UInt32 p)
4632N/A{
4632N/A#ifdef __LITTLE_ENDIAN__
4632N/A return 0xFF - ((p >> 24 & 0xFF) + (p >> 16 & 0xFF) + (p >> 8 & 0xFF)) / 3;
4632N/A#else
4632N/A return 0xFF - ((p >> 16 & 0xFF) + (p >> 8 & 0xFF) + (p & 0xFF)) / 3;
4632N/A#endif
4632N/A}
4632N/A
4632N/Astatic void
4632N/ACGGI_CopyImageFromCanvasToAlphaInfo(CGGI_GlyphCanvas *canvas, GlyphInfo *info)
4632N/A{
4632N/A UInt32 *src = (UInt32 *)canvas->image->data;
4632N/A size_t srcRowWidth = canvas->image->width;
4632N/A
4632N/A UInt8 *dest = (UInt8 *)info->image;
4632N/A size_t destRowWidth = info->width;
4632N/A
4632N/A size_t height = info->height;
4632N/A
4632N/A size_t y;
4632N/A for (y = 0; y < height; y++) {
4632N/A size_t destRow = y * destRowWidth;
4632N/A size_t srcRow = y * srcRowWidth;
4632N/A
4632N/A size_t x;
4632N/A for (x = 0; x < destRowWidth; x++) {
4632N/A UInt32 p = src[srcRow + x];
4632N/A dest[destRow + x] = CGGI_ConvertPixelToGreyBit(p);
4632N/A }
4632N/A }
4632N/A}
4632N/A
4632N/A
4632N/A#pragma mark --- Pixel Size, Modes, and Canvas Shaping Helper Functions ---
4632N/A
4632N/Atypedef struct CGGI_GlyphInfoDescriptor {
4632N/A size_t pixelSize;
4632N/A void (*copyFxnPtr)(CGGI_GlyphCanvas *canvas, GlyphInfo *info);
4632N/A} CGGI_GlyphInfoDescriptor;
4632N/A
4632N/Atypedef struct CGGI_RenderingMode {
4632N/A CGGI_GlyphInfoDescriptor *glyphDescriptor;
4632N/A JRSFontRenderingStyle cgFontMode;
4632N/A} CGGI_RenderingMode;
4632N/A
4632N/Astatic CGGI_GlyphInfoDescriptor grey =
4632N/A { 1, &CGGI_CopyImageFromCanvasToAlphaInfo };
4632N/Astatic CGGI_GlyphInfoDescriptor rgb =
4632N/A { 3, &CGGI_CopyImageFromCanvasToRGBInfo };
4632N/A
4632N/Astatic inline CGGI_RenderingMode
4632N/ACGGI_GetRenderingMode(const AWTStrike *strike)
4632N/A{
4632N/A CGGI_RenderingMode mode;
4632N/A mode.cgFontMode = strike->fStyle;
4632N/A
4632N/A switch (strike->fAAStyle) {
4632N/A case sun_awt_SunHints_INTVAL_TEXT_ANTIALIAS_DEFAULT:
4632N/A case sun_awt_SunHints_INTVAL_TEXT_ANTIALIAS_OFF:
4632N/A case sun_awt_SunHints_INTVAL_TEXT_ANTIALIAS_ON:
4632N/A case sun_awt_SunHints_INTVAL_TEXT_ANTIALIAS_GASP:
4632N/A default:
4632N/A mode.glyphDescriptor = &grey;
4632N/A break;
4632N/A case sun_awt_SunHints_INTVAL_TEXT_ANTIALIAS_LCD_HRGB:
4632N/A case sun_awt_SunHints_INTVAL_TEXT_ANTIALIAS_LCD_HBGR:
4632N/A case sun_awt_SunHints_INTVAL_TEXT_ANTIALIAS_LCD_VRGB:
4632N/A case sun_awt_SunHints_INTVAL_TEXT_ANTIALIAS_LCD_VBGR:
4632N/A mode.glyphDescriptor = &rgb;
4632N/A break;
4632N/A }
4632N/A
4632N/A return mode;
4632N/A}
4632N/A
4632N/A
4632N/A#pragma mark --- Canvas Managment ---
4632N/A
4632N/A/*
4632N/A * Creates a new canvas of a fixed size, and initializes the CGContext as
4632N/A * an 32-bit ARGB BitmapContext with some generic RGB color space.
4632N/A */
4632N/Astatic inline void
4632N/ACGGI_InitCanvas(CGGI_GlyphCanvas *canvas,
4632N/A const vImagePixelCount width, const vImagePixelCount height)
4632N/A{
4632N/A // our canvas is *always* 4-byte ARGB
4632N/A size_t bytesPerRow = width * sizeof(UInt32);
4632N/A size_t byteCount = bytesPerRow * height;
4632N/A
4632N/A canvas->image = malloc(sizeof(vImage_Buffer));
4632N/A canvas->image->width = width;
4632N/A canvas->image->height = height;
4632N/A canvas->image->rowBytes = bytesPerRow;
4632N/A
4632N/A canvas->image->data = (void *)calloc(byteCount, sizeof(UInt32));
4632N/A if (canvas->image->data == NULL) {
4632N/A [[NSException exceptionWithName:NSMallocException
4632N/A reason:@"Failed to allocate memory for the buffer which backs the CGContext for glyph strikes." userInfo:nil] raise];
4632N/A }
4632N/A
4632N/A CGColorSpaceRef colorSpace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);
4632N/A canvas->context = CGBitmapContextCreate(canvas->image->data,
4632N/A width, height, 8, bytesPerRow,
4632N/A colorSpace,
4632N/A kCGImageAlphaPremultipliedFirst);
4632N/A
4632N/A CGContextSetRGBFillColor(canvas->context, 0.0f, 0.0f, 0.0f, 1.0f);
4632N/A CGContextSetFontSize(canvas->context, 1);
4632N/A CGContextSaveGState(canvas->context);
4632N/A
4632N/A CGColorSpaceRelease(colorSpace);
4632N/A}
4632N/A
4632N/A/*
4632N/A * Releases the BitmapContext and the associated memory backing it.
4632N/A */
4632N/Astatic inline void
4632N/ACGGI_FreeCanvas(CGGI_GlyphCanvas *canvas)
4632N/A{
4632N/A if (canvas->context != NULL) {
4632N/A CGContextRelease(canvas->context);
4632N/A }
4632N/A
4632N/A if (canvas->image != NULL) {
4632N/A if (canvas->image->data != NULL) {
4632N/A free(canvas->image->data);
4632N/A }
4632N/A free(canvas->image);
4632N/A }
4632N/A}
4632N/A
4632N/A/*
4632N/A * This is the slack space that is preallocated for the global GlyphCanvas
4632N/A * when it needs to be expanded. It has been set somewhat liberally to
4632N/A * avoid re-upsizing frequently.
4632N/A */
4632N/A#define CGGI_GLYPH_CANVAS_SLACK 2.5
4632N/A
4632N/A/*
4632N/A * Quick and easy inline to check if this canvas is big enough.
4632N/A */
4632N/Astatic inline void
4632N/ACGGI_SizeCanvas(CGGI_GlyphCanvas *canvas, const vImagePixelCount width, const vImagePixelCount height, const JRSFontRenderingStyle style)
4632N/A{
4632N/A if (canvas->image != NULL &&
4632N/A width < canvas->image->width &&
4632N/A height < canvas->image->height)
4632N/A {
4632N/A return;
4632N/A }
4632N/A
4632N/A // if we don't have enough space to strike the largest glyph in the
4632N/A // run, resize the canvas
4632N/A CGGI_FreeCanvas(canvas);
4632N/A CGGI_InitCanvas(canvas,
4632N/A width * CGGI_GLYPH_CANVAS_SLACK,
4632N/A height * CGGI_GLYPH_CANVAS_SLACK);
4632N/A JRSFontSetRenderingStyleOnContext(canvas->context, style);
4632N/A}
4632N/A
4632N/A/*
4632N/A * Clear the canvas by blitting white only into the region of interest
4632N/A * (the rect which we will copy out of once the glyph is struck).
4632N/A */
4632N/Astatic inline void
4632N/ACGGI_ClearCanvas(CGGI_GlyphCanvas *canvas, GlyphInfo *info)
4632N/A{
4632N/A vImage_Buffer canvasRectToClear;
4632N/A canvasRectToClear.data = canvas->image->data;
4632N/A canvasRectToClear.height = info->height;
4632N/A canvasRectToClear.width = info->width;
4632N/A // use the row stride of the canvas, not the info
4632N/A canvasRectToClear.rowBytes = canvas->image->rowBytes;
4632N/A
4632N/A // clean the canvas
4632N/A#ifdef CGGI_DEBUG
4632N/A Pixel_8888 opaqueWhite = { 0xE0, 0xE0, 0xE0, 0xE0 };
4632N/A#else
4632N/A Pixel_8888 opaqueWhite = { 0xFF, 0xFF, 0xFF, 0xFF };
4632N/A#endif
4632N/A
4632N/A vImageBufferFill_ARGB8888(&canvasRectToClear, opaqueWhite, kvImageNoFlags);
4632N/A}
4632N/A
4632N/A
4632N/A#pragma mark --- GlyphInfo Creation & Copy Functions ---
4632N/A
4632N/A/*
4632N/A * Creates a GlyphInfo with exactly the correct size image and measurements.
4632N/A */
4632N/A#define CGGI_GLYPH_BBOX_PADDING 2.0f
4632N/Astatic inline GlyphInfo *
4632N/ACGGI_CreateNewGlyphInfoFrom(CGSize advance, CGRect bbox,
4632N/A const CGGI_RenderingMode *mode)
4632N/A{
4632N/A size_t pixelSize = mode->glyphDescriptor->pixelSize;
4632N/A
4632N/A // adjust the bounding box to be 1px bigger on each side than what
4632N/A // CGFont-whatever suggests - because it gives a bounding box that
4632N/A // is too tight
4632N/A bbox.size.width += CGGI_GLYPH_BBOX_PADDING * 2.0f;
4632N/A bbox.size.height += CGGI_GLYPH_BBOX_PADDING * 2.0f;
4632N/A bbox.origin.x -= CGGI_GLYPH_BBOX_PADDING;
4632N/A bbox.origin.y -= CGGI_GLYPH_BBOX_PADDING;
4632N/A
4632N/A vImagePixelCount width = ceilf(bbox.size.width);
4632N/A vImagePixelCount height = ceilf(bbox.size.height);
4632N/A
4632N/A // if the glyph is larger than 1MB, don't even try...
4632N/A // the GlyphVector path should have taken over by now
4632N/A // and zero pixels is ok
4632N/A if (width * height > 1024 * 1024) {
4632N/A width = 1;
4632N/A height = 1;
4632N/A }
4632N/A
4632N/A#ifdef USE_IMAGE_ALIGNED_MEMORY
4632N/A // create separate memory
4632N/A GlyphInfo *glyphInfo = (GlyphInfo *)malloc(sizeof(GlyphInfo));
4632N/A void *image = (void *)malloc(height * width * pixelSize);
4632N/A#else
4632N/A // create a GlyphInfo struct fused to the image it points to
4632N/A GlyphInfo *glyphInfo = (GlyphInfo *)malloc(sizeof(GlyphInfo) +
4632N/A height * width * pixelSize);
4632N/A#endif
4632N/A
4632N/A glyphInfo->advanceX = advance.width;
4632N/A glyphInfo->advanceY = advance.height;
4632N/A glyphInfo->topLeftX = round(bbox.origin.x);
4632N/A glyphInfo->topLeftY = round(bbox.origin.y);
4632N/A glyphInfo->width = width;
4632N/A glyphInfo->height = height;
4632N/A glyphInfo->rowBytes = width * pixelSize;
4632N/A glyphInfo->cellInfo = NULL;
4632N/A
4632N/A#ifdef USE_IMAGE_ALIGNED_MEMORY
4632N/A glyphInfo->image = image;
4632N/A#else
4632N/A glyphInfo->image = ((void *)glyphInfo) + sizeof(GlyphInfo);
4632N/A#endif
4632N/A
4632N/A return glyphInfo;
4632N/A}
4632N/A
4632N/A
4632N/A#pragma mark --- Glyph Striking onto Canvas ---
4632N/A
4632N/A/*
4632N/A * Clears the canvas, strikes the glyph with CoreGraphics, and then
4632N/A * copies the struck pixels into the GlyphInfo image.
4632N/A */
4632N/Astatic inline void
4632N/ACGGI_CreateImageForGlyph
4632N/A (CGGI_GlyphCanvas *canvas, const CGGlyph glyph,
4632N/A GlyphInfo *info, const CGGI_RenderingMode *mode)
4632N/A{
4632N/A // clean the canvas
4632N/A CGGI_ClearCanvas(canvas, info);
4632N/A
4632N/A // strike the glyph in the upper right corner
4632N/A CGContextShowGlyphsAtPoint(canvas->context,
4632N/A -info->topLeftX,
4632N/A canvas->image->height + info->topLeftY,
4632N/A &glyph, 1);
4632N/A
4632N/A // copy the glyph from the canvas into the info
4632N/A (*mode->glyphDescriptor->copyFxnPtr)(canvas, info);
4632N/A}
4632N/A
4632N/A/*
4632N/A * CoreText path...
4632N/A */
4632N/Astatic inline GlyphInfo *
4632N/ACGGI_CreateImageForUnicode
4632N/A (CGGI_GlyphCanvas *canvas, const AWTStrike *strike,
4632N/A const CGGI_RenderingMode *mode, const UniChar uniChar)
4632N/A{
4632N/A // save the state of the world
4632N/A CGContextSaveGState(canvas->context);
4632N/A
4632N/A // get the glyph, measure it using CG
4632N/A CGGlyph glyph;
4632N/A CTFontRef fallback;
4632N/A if (uniChar > 0xFFFF) {
4632N/A UTF16Char charRef[2];
4632N/A CTS_BreakupUnicodeIntoSurrogatePairs(uniChar, charRef);
4632N/A CGGlyph glyphTmp[2];
4632N/A fallback = CTS_CopyCTFallbackFontAndGlyphForUnicode(strike->fAWTFont, (const UTF16Char *)&charRef, (CGGlyph *)&glyphTmp, 2);
4632N/A glyph = glyphTmp[0];
4632N/A } else {
4632N/A UTF16Char charRef;
4632N/A charRef = (UTF16Char) uniChar; // truncate.
4632N/A fallback = CTS_CopyCTFallbackFontAndGlyphForUnicode(strike->fAWTFont, (const UTF16Char *)&charRef, &glyph, 1);
4632N/A }
4632N/A
4632N/A CGAffineTransform tx = strike->fTx;
4632N/A JRSFontRenderingStyle style = JRSFontAlignStyleForFractionalMeasurement(strike->fStyle);
4632N/A
4632N/A CGRect bbox;
4632N/A JRSFontGetBoundingBoxesForGlyphsAndStyle(fallback, &tx, style, &glyph, 1, &bbox);
4632N/A
4632N/A CGSize advance;
4632N/A JRSFontGetAdvancesForGlyphsAndStyle(fallback, &tx, strike->fStyle, &glyph, 1, &advance);
4632N/A
4632N/A // create the Sun2D GlyphInfo we are going to strike into
4632N/A GlyphInfo *info = CGGI_CreateNewGlyphInfoFrom(advance, bbox, mode);
4632N/A
4632N/A // fix the context size, just in case the substituted character is unexpectedly large
4632N/A CGGI_SizeCanvas(canvas, info->width, info->height, mode->cgFontMode);
4632N/A
4632N/A // align the transform for the real CoreText strike
4632N/A CGContextSetTextMatrix(canvas->context, strike->fAltTx);
4632N/A
4632N/A const CGFontRef cgFallback = CTFontCopyGraphicsFont(fallback, NULL);
4632N/A CGContextSetFont(canvas->context, cgFallback);
4632N/A CFRelease(cgFallback);
4632N/A
4632N/A // clean the canvas - align, strike, and copy the glyph from the canvas into the info
4632N/A CGGI_CreateImageForGlyph(canvas, glyph, info, mode);
4632N/A
4632N/A // restore the state of the world
4632N/A CGContextRestoreGState(canvas->context);
4632N/A
4632N/A CFRelease(fallback);
4632N/A#ifdef CGGI_DEBUG
4632N/A DUMP_GLYPHINFO(info);
4632N/A#endif
4632N/A
4632N/A#ifdef CGGI_DEBUG_DUMP
4632N/A DUMP_IMG_PIXELS("CGGI Canvas", canvas->image);
4632N/A#if 0
4632N/A PRINT_CGSTATES_INFO(NULL);
4632N/A#endif
4632N/A#endif
4632N/A
4632N/A return info;
4632N/A}
4632N/A
4632N/A
4632N/A#pragma mark --- GlyphInfo Filling and Canvas Managment ---
4632N/A
4632N/A/*
4632N/A * Sets all the per-run properties for the canvas, and then iterates through
4632N/A * the character run, and creates images in the GlyphInfo structs.
4632N/A *
4632N/A * Not inlined because it would create two copies in the function below
4632N/A */
4632N/Astatic void
4632N/ACGGI_FillImagesForGlyphsWithSizedCanvas(CGGI_GlyphCanvas *canvas,
4632N/A const AWTStrike *strike,
4632N/A const CGGI_RenderingMode *mode,
4632N/A jlong glyphInfos[],
4632N/A const UniChar uniChars[],
4632N/A const CGGlyph glyphs[],
4632N/A const CFIndex len)
4632N/A{
4632N/A CGContextSetTextMatrix(canvas->context, strike->fAltTx);
4632N/A
4632N/A CGContextSetFont(canvas->context, strike->fAWTFont->fNativeCGFont);
4632N/A JRSFontSetRenderingStyleOnContext(canvas->context, strike->fStyle);
4632N/A
4632N/A CFIndex i;
4632N/A for (i = 0; i < len; i++) {
4632N/A GlyphInfo *info = (GlyphInfo *)jlong_to_ptr(glyphInfos[i]);
4632N/A if (info != NULL) {
4632N/A CGGI_CreateImageForGlyph(canvas, glyphs[i], info, mode);
4632N/A } else {
4632N/A info = CGGI_CreateImageForUnicode(canvas, strike, mode, uniChars[i]);
4632N/A glyphInfos[i] = ptr_to_jlong(info);
4632N/A }
4632N/A#ifdef CGGI_DEBUG
4632N/A DUMP_GLYPHINFO(info);
4632N/A#endif
4632N/A
4632N/A#ifdef CGGI_DEBUG_DUMP
4632N/A DUMP_IMG_PIXELS("CGGI Canvas", canvas->image);
4632N/A#endif
4632N/A }
4632N/A#ifdef CGGI_DEBUG_DUMP
4632N/A DUMP_IMG_PIXELS("CGGI Canvas", canvas->image);
4632N/A PRINT_CGSTATES_INFO(canvas->context);
4632N/A#endif
4632N/A}
4632N/A
4632N/Astatic NSString *threadLocalCanvasKey =
4632N/A @"Java CoreGraphics Text Renderer Cached Canvas";
4632N/A
4632N/A/*
4632N/A * This is the maximum length and height times the above slack squared
4632N/A * to determine if we go with the global canvas, or malloc one on the spot.
4632N/A */
4632N/A#define CGGI_GLYPH_CANVAS_MAX 100
4632N/A
4632N/A/*
4632N/A * Based on the space needed to strike the largest character in the run,
4632N/A * either use the global shared canvas, or make one up on the spot, strike
4632N/A * the glyphs, and destroy it.
4632N/A */
4632N/Astatic inline void
4632N/ACGGI_FillImagesForGlyphs(jlong *glyphInfos, const AWTStrike *strike,
4632N/A const CGGI_RenderingMode *mode,
4632N/A const UniChar uniChars[], const CGGlyph glyphs[],
4632N/A const size_t maxWidth, const size_t maxHeight,
4632N/A const CFIndex len)
4632N/A{
4632N/A if (maxWidth*maxHeight*CGGI_GLYPH_CANVAS_SLACK*CGGI_GLYPH_CANVAS_SLACK >
4632N/A CGGI_GLYPH_CANVAS_MAX*CGGI_GLYPH_CANVAS_MAX*CGGI_GLYPH_CANVAS_SLACK*CGGI_GLYPH_CANVAS_SLACK)
4632N/A {
4632N/A CGGI_GlyphCanvas *tmpCanvas = [[CGGI_GlyphCanvas alloc] init];
4632N/A CGGI_InitCanvas(tmpCanvas, maxWidth, maxHeight);
4632N/A CGGI_FillImagesForGlyphsWithSizedCanvas(tmpCanvas, strike,
4632N/A mode, glyphInfos, uniChars,
4632N/A glyphs, len);
4632N/A CGGI_FreeCanvas(tmpCanvas);
4632N/A
4632N/A [tmpCanvas release];
4632N/A return;
4632N/A }
4632N/A
4632N/A NSMutableDictionary *threadDict =
4632N/A [[NSThread currentThread] threadDictionary];
4632N/A CGGI_GlyphCanvas *canvas = [threadDict objectForKey:threadLocalCanvasKey];
4632N/A if (canvas == nil) {
4632N/A canvas = [[CGGI_GlyphCanvas alloc] init];
4632N/A [threadDict setObject:canvas forKey:threadLocalCanvasKey];
4632N/A }
4632N/A
4632N/A CGGI_SizeCanvas(canvas, maxWidth, maxHeight, mode->cgFontMode);
4632N/A CGGI_FillImagesForGlyphsWithSizedCanvas(canvas, strike, mode,
4632N/A glyphInfos, uniChars, glyphs, len);
4632N/A}
4632N/A
4632N/A/*
4632N/A * Finds the advances and bounding boxes of the characters in the run,
4632N/A * cycles through all the bounds and calculates the maximum canvas space
4632N/A * required by the largest glyph.
4632N/A *
4632N/A * Creates a GlyphInfo struct with a malloc that also encapsulates the
4632N/A * image the struct points to. This is done to meet memory layout
4632N/A * expectations in the Sun text rasterizer memory managment code.
4632N/A * The image immediately follows the struct physically in memory.
4632N/A */
4632N/Astatic inline void
4632N/ACGGI_CreateGlyphInfos(jlong *glyphInfos, const AWTStrike *strike,
4632N/A const CGGI_RenderingMode *mode,
4632N/A const UniChar uniChars[], const CGGlyph glyphs[],
4632N/A CGSize advances[], CGRect bboxes[], const CFIndex len)
4632N/A{
4632N/A AWTFont *font = strike->fAWTFont;
4632N/A CGAffineTransform tx = strike->fTx;
4632N/A JRSFontRenderingStyle bboxCGMode = JRSFontAlignStyleForFractionalMeasurement(strike->fStyle);
4632N/A
4632N/A JRSFontGetBoundingBoxesForGlyphsAndStyle((CTFontRef)font->fFont, &tx, bboxCGMode, glyphs, len, bboxes);
4632N/A JRSFontGetAdvancesForGlyphsAndStyle((CTFontRef)font->fFont, &tx, strike->fStyle, glyphs, len, advances);
4632N/A
4632N/A size_t maxWidth = 1;
4632N/A size_t maxHeight = 1;
4632N/A
4632N/A CFIndex i;
4632N/A for (i = 0; i < len; i++)
4632N/A {
4632N/A if (uniChars[i] != 0)
4632N/A {
4632N/A glyphInfos[i] = 0L;
4632N/A continue; // will be handled later
4632N/A }
4632N/A
4632N/A CGSize advance = advances[i];
4632N/A CGRect bbox = bboxes[i];
4632N/A
4632N/A GlyphInfo *glyphInfo = CGGI_CreateNewGlyphInfoFrom(advance, bbox, mode);
4632N/A
4632N/A if (maxWidth < glyphInfo->width) maxWidth = glyphInfo->width;
4632N/A if (maxHeight < glyphInfo->height) maxHeight = glyphInfo->height;
4632N/A
4632N/A glyphInfos[i] = ptr_to_jlong(glyphInfo);
4632N/A }
4632N/A
4632N/A CGGI_FillImagesForGlyphs(glyphInfos, strike, mode, uniChars,
4632N/A glyphs, maxWidth, maxHeight, len);
4632N/A}
4632N/A
4632N/A
4632N/A#pragma mark --- Temporary Buffer Allocations and Initialization ---
4632N/A
4632N/A/*
4632N/A * This stage separates the already valid glyph codes from the unicode values
4632N/A * that need special handling - the rawGlyphCodes array is no longer used
4632N/A * after this stage.
4632N/A */
4632N/Astatic void
4632N/ACGGI_CreateGlyphsAndScanForComplexities(jlong *glyphInfos,
4632N/A const AWTStrike *strike,
4632N/A const CGGI_RenderingMode *mode,
4632N/A jint rawGlyphCodes[],
4632N/A UniChar uniChars[], CGGlyph glyphs[],
4632N/A CGSize advances[], CGRect bboxes[],
4632N/A const CFIndex len)
4632N/A{
4632N/A CFIndex i;
4632N/A for (i = 0; i < len; i++) {
4632N/A jint code = rawGlyphCodes[i];
4632N/A if (code < 0) {
4632N/A glyphs[i] = 0;
4632N/A uniChars[i] = -code;
4632N/A } else {
4632N/A glyphs[i] = code;
4632N/A uniChars[i] = 0;
4632N/A }
4632N/A }
4632N/A
4632N/A CGGI_CreateGlyphInfos(glyphInfos, strike, mode,
4632N/A uniChars, glyphs, advances, bboxes, len);
4632N/A
4632N/A#ifdef CGGI_DEBUG_HIT_COUNT
4632N/A static size_t hitCount = 0;
4632N/A hitCount++;
4632N/A printf("%d\n", (int)hitCount);
4632N/A#endif
4632N/A}
4632N/A
4632N/A/*
4632N/A * Conditionally stack allocates buffers for glyphs, bounding boxes,
4632N/A * and advances. Unfortunately to use CG or CT in bulk runs (which is
4632N/A * faster than calling them per character), we have to copy into and out
4632N/A * of these buffers. Still a net win though.
4632N/A */
4632N/Avoid
4632N/ACGGlyphImages_GetGlyphImagePtrs(jlong glyphInfos[],
4632N/A const AWTStrike *strike,
4632N/A jint rawGlyphCodes[], const CFIndex len)
4632N/A{
4632N/A const CGGI_RenderingMode mode = CGGI_GetRenderingMode(strike);
4632N/A
4632N/A if (len < MAX_STACK_ALLOC_GLYPH_BUFFER_SIZE) {
4632N/A CGRect bboxes[len];
4632N/A CGSize advances[len];
4632N/A CGGlyph glyphs[len];
4632N/A UniChar uniChars[len];
4632N/A
4632N/A CGGI_CreateGlyphsAndScanForComplexities(glyphInfos, strike, &mode,
4632N/A rawGlyphCodes, uniChars, glyphs,
4632N/A advances, bboxes, len);
4632N/A
4632N/A return;
4632N/A }
4632N/A
4632N/A // just do one malloc, and carve it up for all the buffers
4632N/A void *buffer = malloc(sizeof(CGRect) * sizeof(CGSize) *
4632N/A sizeof(CGGlyph) * sizeof(UniChar) * len);
4632N/A if (buffer == NULL) {
4632N/A [[NSException exceptionWithName:NSMallocException
4632N/A reason:@"Failed to allocate memory for the temporary glyph strike and measurement buffers." userInfo:nil] raise];
4632N/A }
4632N/A
4632N/A CGRect *bboxes = (CGRect *)(buffer);
4632N/A CGSize *advances = (CGSize *)(bboxes + sizeof(CGRect) * len);
4632N/A CGGlyph *glyphs = (CGGlyph *)(advances + sizeof(CGGlyph) * len);
4632N/A UniChar *uniChars = (UniChar *)(glyphs + sizeof(UniChar) * len);
4632N/A
4632N/A CGGI_CreateGlyphsAndScanForComplexities(glyphInfos, strike, &mode,
4632N/A rawGlyphCodes, uniChars, glyphs,
4632N/A advances, bboxes, len);
4632N/A
4632N/A free(buffer);
4632N/A}