blob: a52d32a5dea9751f545428c087a731460cbea934 [file]
/*
* Copyright (c) 2019, 2021, Oracle and/or its affiliates. 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. 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 <stdlib.h>
#include <limits.h>
#include <math.h>
#include <jlong.h>
#include "sun_java2d_metal_MTLTextRenderer.h"
#include "SurfaceData.h"
#include "MTLContext.h"
#include "MTLRenderQueue.h"
#include "MTLTextRenderer.h"
#include "MTLVertexCache.h"
#include "MTLGlyphCache.h"
#include "MTLBlitLoops.h"
/**
* The following constants define the inner and outer bounds of the
* accelerated glyph cache.
*/
#define MTLTR_CACHE_WIDTH 512
#define MTLTR_CACHE_HEIGHT 512
#define MTLTR_CACHE_CELL_WIDTH 32
#define MTLTR_CACHE_CELL_HEIGHT 32
/**
* The current "glyph mode" state. This variable is used to track the
* codepath used to render a particular glyph. This variable is reset to
* MODE_NOT_INITED at the beginning of every call to MTLTR_DrawGlyphList().
* As each glyph is rendered, the glyphMode variable is updated to reflect
* the current mode, so if the current mode is the same as the mode used
* to render the previous glyph, we can avoid doing costly setup operations
* each time.
*/
typedef enum {
MODE_NOT_INITED,
MODE_USE_CACHE_GRAY,
MODE_USE_CACHE_LCD,
MODE_NO_CACHE_GRAY,
MODE_NO_CACHE_LCD,
MODE_NO_CACHE_COLOR
} GlyphMode;
static GlyphMode glyphMode = MODE_NOT_INITED;
/**
* There are two separate glyph caches: for AA and for LCD.
* Once one of them is initialized as either GRAY or LCD, it
* stays in that mode for the duration of the application. It should
* be safe to use this one glyph cache for all screens in a multimon
* environment, since the glyph cache texture is shared between all contexts,
* and (in theory) Metal drivers should be smart enough to manage that
* texture across all screens.
*/
static MTLGlyphCacheInfo *glyphCacheLCD = NULL;
static MTLGlyphCacheInfo *glyphCacheAA = NULL;
/**
* This value tracks the previous LCD rgbOrder setting, so if the rgbOrder
* value has changed since the last time, it indicates that we need to
* invalidate the cache, which may already store glyph images in the reverse
* order. Note that in most real world applications this value will not
* change over the course of the application, but tests like Font2DTest
* allow for changing the ordering at runtime, so we need to handle that case.
*/
static jboolean lastRGBOrder = JNI_TRUE;
/**
* This constant defines the size of the tile to use in the
* MTLTR_DrawLCDGlyphNoCache() method. See below for more on why we
* restrict this value to a particular size.
*/
#define MTLTR_NOCACHE_TILE_SIZE 32
static struct TxtVertex txtVertices[6];
static jint vertexCacheIndex = 0;
static id<MTLRenderCommandEncoder> lcdCacheEncoder = nil;
#define LCD_ADD_VERTEX(TX, TY, DX, DY, DZ) \
do { \
struct TxtVertex *v = &txtVertices[vertexCacheIndex++]; \
v->txtpos[0] = TX; \
v->txtpos[1] = TY; \
v->position[0]= DX; \
v->position[1] = DY; \
} while (0)
#define LCD_ADD_TRIANGLES(TX1, TY1, TX2, TY2, DX1, DY1, DX2, DY2) \
do { \
LCD_ADD_VERTEX(TX1, TY1, DX1, DY1, 0); \
LCD_ADD_VERTEX(TX2, TY1, DX2, DY1, 0); \
LCD_ADD_VERTEX(TX2, TY2, DX2, DY2, 0); \
LCD_ADD_VERTEX(TX2, TY2, DX2, DY2, 0); \
LCD_ADD_VERTEX(TX1, TY2, DX1, DY2, 0); \
LCD_ADD_VERTEX(TX1, TY1, DX1, DY1, 0); \
} while (0)
/**
* Initializes the one glyph cache (texture and data structure).
* If lcdCache is JNI_TRUE, the texture will contain RGB data,
* otherwise we will simply store the grayscale/monochrome glyph images
* as intensity values.
*/
static jboolean
MTLTR_InitGlyphCache(MTLContext *mtlc, jboolean lcdCache)
{
J2dTraceLn(J2D_TRACE_INFO, "MTLTR_InitGlyphCache");
// TODO : Need to verify RGB order in case of LCD
MTLPixelFormat pixelFormat =
lcdCache ? MTLPixelFormatBGRA8Unorm : MTLPixelFormatA8Unorm;
MTLGlyphCacheInfo *gcinfo;
// init glyph cache data structure
gcinfo = MTLGlyphCache_Init(MTLTR_CACHE_WIDTH,
MTLTR_CACHE_HEIGHT,
MTLTR_CACHE_CELL_WIDTH,
MTLTR_CACHE_CELL_HEIGHT,
MTLVertexCache_FlushGlyphVertexCache);
if (gcinfo == NULL) {
J2dRlsTraceLn(J2D_TRACE_ERROR,
"MTLTR_InitGlyphCache: could not init MTL glyph cache");
return JNI_FALSE;
}
MTLTextureDescriptor *textureDescriptor =
[MTLTextureDescriptor texture2DDescriptorWithPixelFormat:pixelFormat
width:MTLTR_CACHE_WIDTH
height:MTLTR_CACHE_HEIGHT
mipmapped:NO];
gcinfo->texture = [mtlc.device newTextureWithDescriptor:textureDescriptor];
if (lcdCache) {
glyphCacheLCD = gcinfo;
} else {
glyphCacheAA = gcinfo;
}
return JNI_TRUE;
}
id<MTLTexture>
MTLTR_GetGlyphCacheTexture()
{
J2dTraceLn(J2D_TRACE_INFO, "MTLTR_GetGlyphCacheTexture");
if (glyphCacheAA != NULL) {
return glyphCacheAA->texture;
}
return NULL;
}
/**
* Adds the given glyph to the glyph cache (texture and data structure)
* associated with the given MTLContext.
*/
static void
MTLTR_AddToGlyphCache(GlyphInfo *glyph, MTLContext *mtlc,
jboolean lcdCache)
{
MTLCacheCellInfo *ccinfo;
MTLGlyphCacheInfo *gcinfo;
jint w = glyph->width;
jint h = glyph->height;
J2dTraceLn(J2D_TRACE_INFO, "MTLTR_AddToGlyphCache");
if (!lcdCache) {
gcinfo = glyphCacheAA;
} else {
gcinfo = glyphCacheLCD;
}
if ((gcinfo == NULL) || (glyph->image == NULL)) {
return;
}
bool isCacheFull = MTLGlyphCache_IsCacheFull(gcinfo, glyph);
if (isCacheFull) {
MTLGlyphCache_Free(gcinfo);
if (!lcdCache) {
MTLTR_InitGlyphCache(mtlc, JNI_FALSE);
gcinfo = glyphCacheAA;
} else {
MTLTR_InitGlyphCache(mtlc, JNI_TRUE);
gcinfo = glyphCacheLCD;
}
}
MTLGlyphCache_AddGlyph(gcinfo, glyph);
ccinfo = (MTLCacheCellInfo *) glyph->cellInfo;
if (ccinfo != NULL) {
// store glyph image in texture cell
MTLRegion region = {
{ccinfo->x, ccinfo->y, 0},
{w, h, 1}
};
if (!lcdCache) {
NSUInteger bytesPerRow = 1 * w;
[gcinfo->texture replaceRegion:region
mipmapLevel:0
withBytes:glyph->image
bytesPerRow:bytesPerRow];
} else {
unsigned int imageBytes = w * h * 4;
unsigned char imageData[imageBytes];
memset(&imageData, 0, sizeof(imageData));
int srcIndex = 0;
int dstIndex = 0;
for (int i = 0; i < (w * h); i++) {
imageData[dstIndex++] = glyph->image[srcIndex++];
imageData[dstIndex++] = glyph->image[srcIndex++];
imageData[dstIndex++] = glyph->image[srcIndex++];
imageData[dstIndex++] = 0xFF;
}
NSUInteger bytesPerRow = 4 * w;
[gcinfo->texture replaceRegion:region
mipmapLevel:0
withBytes:imageData
bytesPerRow:bytesPerRow];
}
}
}
static jboolean
MTLTR_SetLCDContrast(MTLContext *mtlc,
jint contrast,
id<MTLRenderCommandEncoder> encoder)
{
if (![mtlc.paint isKindOfClass:[MTLColorPaint class]]) {
return JNI_FALSE;
}
MTLColorPaint* cPaint = (MTLColorPaint *) mtlc.paint;
// update the current color settings
double gamma = ((double)contrast) / 100.0;
double invgamma = 1.0/gamma;
jfloat radj, gadj, badj;
jfloat clr[4];
jint col = cPaint.color;
J2dTraceLn2(J2D_TRACE_INFO, "primary color %x, contrast %d", col, contrast);
J2dTraceLn2(J2D_TRACE_INFO, "gamma %f, invgamma %f", gamma, invgamma);
clr[0] = ((col >> 16) & 0xFF)/255.0f;
clr[1] = ((col >> 8) & 0xFF)/255.0f;
clr[2] = ((col) & 0xFF)/255.0f;
// gamma adjust the primary color
radj = (float)pow(clr[0], gamma);
gadj = (float)pow(clr[1], gamma);
badj = (float)pow(clr[2], gamma);
struct LCDFrameUniforms uf = {
{radj, gadj, badj},
{gamma, gamma, gamma},
{invgamma, invgamma, invgamma}};
[encoder setFragmentBytes:&uf length:sizeof(uf) atIndex:FrameUniformBuffer];
return JNI_TRUE;
}
void
MTLTR_EnableGlyphVertexCache(MTLContext *mtlc, BMTLSDOps *dstOps)
{
J2dTraceLn(J2D_TRACE_INFO, "MTLTR_EnableGlyphVertexCache");
if (!MTLVertexCache_InitVertexCache()) {
return;
}
if (glyphCacheAA == NULL) {
if (!MTLTR_InitGlyphCache(mtlc, JNI_FALSE)) {
return;
}
}
MTLVertexCache_CreateSamplingEncoder(mtlc, dstOps);
}
void
MTLTR_DisableGlyphVertexCache(MTLContext *mtlc)
{
J2dTraceLn(J2D_TRACE_INFO, "MTLTR_DisableGlyphVertexCache");
MTLVertexCache_FlushGlyphVertexCache();
MTLVertexCache_FreeVertexCache();
}
void MTLTR_FreeGlyphCaches() {
J2dTraceLn(J2D_TRACE_INFO, "MTLTR_FreeGlyphCaches : freeing glyph caches.");
if (glyphCacheAA != NULL) {
[glyphCacheAA->texture release];
MTLGlyphCache_Free(glyphCacheAA);
glyphCacheAA = NULL;
}
if (glyphCacheLCD != NULL) {
[glyphCacheLCD->texture release];
MTLGlyphCache_Free(glyphCacheLCD);
glyphCacheLCD = NULL;
}
}
static MTLPaint* storedPaint = nil;
static void EnableColorGlyphPainting(MTLContext *mtlc) {
storedPaint = mtlc.paint;
mtlc.paint = [[MTLPaint alloc] init];
}
static void DisableColorGlyphPainting(MTLContext *mtlc) {
[mtlc.paint release];
mtlc.paint = storedPaint;
storedPaint = nil;
}
static jboolean
MTLTR_DrawGrayscaleGlyphViaCache(MTLContext *mtlc,
GlyphInfo *ginfo, jint x, jint y, BMTLSDOps *dstOps)
{
MTLCacheCellInfo *cell;
jfloat x1, y1, x2, y2;
if (glyphMode != MODE_USE_CACHE_GRAY) {
if (glyphMode == MODE_NO_CACHE_GRAY) {
MTLVertexCache_DisableMaskCache(mtlc);
} else if (glyphMode == MODE_USE_CACHE_LCD) {
[mtlc.encoderManager endEncoder];
lcdCacheEncoder = nil;
} else if (glyphMode == MODE_NO_CACHE_COLOR) {
DisableColorGlyphPainting(mtlc);
}
MTLTR_EnableGlyphVertexCache(mtlc, dstOps);
glyphMode = MODE_USE_CACHE_GRAY;
}
if (ginfo->cellInfo == NULL) {
// attempt to add glyph to accelerated glyph cache
MTLTR_AddToGlyphCache(ginfo, mtlc, JNI_FALSE);
if (ginfo->cellInfo == NULL) {
// we'll just no-op in the rare case that the cell is NULL
return JNI_TRUE;
}
}
cell = (MTLCacheCellInfo *) (ginfo->cellInfo);
cell->timesRendered++;
x1 = (jfloat)x;
y1 = (jfloat)y;
x2 = x1 + ginfo->width;
y2 = y1 + ginfo->height;
MTLVertexCache_AddGlyphQuad(mtlc,
cell->tx1, cell->ty1,
cell->tx2, cell->ty2,
x1, y1, x2, y2);
return JNI_TRUE;
}
static jboolean
MTLTR_DrawLCDGlyphViaCache(MTLContext *mtlc, BMTLSDOps *dstOps,
GlyphInfo *ginfo, jint x, jint y,
jboolean rgbOrder, jint contrast)
{
CacheCellInfo *cell;
jfloat tx1, ty1, tx2, ty2;
jint w = ginfo->width;
jint h = ginfo->height;
if (glyphMode != MODE_USE_CACHE_LCD) {
if (glyphMode == MODE_NO_CACHE_GRAY) {
MTLVertexCache_DisableMaskCache(mtlc);
} else if (glyphMode == MODE_USE_CACHE_GRAY) {
MTLTR_DisableGlyphVertexCache(mtlc);
} else if (glyphMode == MODE_NO_CACHE_COLOR) {
DisableColorGlyphPainting(mtlc);
}
if (glyphCacheLCD == NULL) {
if (!MTLTR_InitGlyphCache(mtlc, JNI_TRUE)) {
return JNI_FALSE;
}
}
if (lcdCacheEncoder == nil) {
lcdCacheEncoder = [mtlc.encoderManager getLCDEncoder:dstOps->pTexture isSrcOpaque:YES isDstOpaque:YES];
}
if (rgbOrder != lastRGBOrder) {
// need to invalidate the cache in this case; see comments
// for lastRGBOrder above
MTLGlyphCache_Invalidate(glyphCacheLCD);
lastRGBOrder = rgbOrder;
}
glyphMode = MODE_USE_CACHE_LCD;
}
if (ginfo->cellInfo == NULL) {
// attempt to add glyph to accelerated glyph cache
// TODO : Handle RGB order
MTLTR_AddToGlyphCache(ginfo, mtlc, JNI_TRUE);
if (ginfo->cellInfo == NULL) {
// we'll just no-op in the rare case that the cell is NULL
return JNI_TRUE;
}
}
cell = (CacheCellInfo *) (ginfo->cellInfo);
cell->timesRendered++;
MTLTR_SetLCDContrast(mtlc, contrast, lcdCacheEncoder);
tx1 = cell->tx1;
ty1 = cell->ty1;
tx2 = cell->tx2;
ty2 = cell->ty2;
J2dTraceLn4(J2D_TRACE_INFO, "tx1 = %f, ty1 = %f, tx2 = %f, ty2 = %f", tx1, ty1, tx2, ty2);
J2dTraceLn2(J2D_TRACE_INFO, "width = %d height = %d", dstOps->width, dstOps->height);
LCD_ADD_TRIANGLES(tx1, ty1, tx2, ty2, x, y, x+w, y+h);
[lcdCacheEncoder setVertexBytes:txtVertices length:sizeof(txtVertices) atIndex:MeshVertexBuffer];
[lcdCacheEncoder setFragmentTexture:glyphCacheLCD->texture atIndex:0];
[lcdCacheEncoder setFragmentTexture:dstOps->pTexture atIndex:1];
[lcdCacheEncoder drawPrimitives:MTLPrimitiveTypeTriangle vertexStart:0 vertexCount:6];
vertexCacheIndex = 0;
return JNI_TRUE;
}
static jboolean
MTLTR_DrawGrayscaleGlyphNoCache(MTLContext *mtlc,
GlyphInfo *ginfo, jint x, jint y, BMTLSDOps *dstOps)
{
jint tw, th;
jint sx, sy, sw, sh;
jint x0;
jint w = ginfo->width;
jint h = ginfo->height;
J2dTraceLn(J2D_TRACE_INFO, "MTLTR_DrawGrayscaleGlyphNoCache");
if (glyphMode != MODE_NO_CACHE_GRAY) {
if (glyphMode == MODE_USE_CACHE_GRAY) {
MTLTR_DisableGlyphVertexCache(mtlc);
} else if (glyphMode == MODE_USE_CACHE_LCD) {
[mtlc.encoderManager endEncoder];
lcdCacheEncoder = nil;
} else if (glyphMode == MODE_NO_CACHE_COLOR) {
DisableColorGlyphPainting(mtlc);
}
MTLVertexCache_EnableMaskCache(mtlc, dstOps);
glyphMode = MODE_NO_CACHE_GRAY;
}
x0 = x;
tw = MTLVC_MASK_CACHE_TILE_WIDTH;
th = MTLVC_MASK_CACHE_TILE_HEIGHT;
for (sy = 0; sy < h; sy += th, y += th) {
x = x0;
sh = ((sy + th) > h) ? (h - sy) : th;
for (sx = 0; sx < w; sx += tw, x += tw) {
sw = ((sx + tw) > w) ? (w - sx) : tw;
J2dTraceLn7(J2D_TRACE_INFO, "sx = %d sy = %d x = %d y = %d sw = %d sh = %d w = %d", sx, sy, x, y, sw, sh, w);
MTLVertexCache_AddMaskQuad(mtlc,
sx, sy, x, y, sw, sh,
w, ginfo->image,
dstOps);
}
}
return JNI_TRUE;
}
static jboolean
MTLTR_DrawLCDGlyphNoCache(MTLContext *mtlc, BMTLSDOps *dstOps,
GlyphInfo *ginfo, jint x, jint y,
jint rowBytesOffset,
jboolean rgbOrder, jint contrast)
{
jfloat tx1, ty1, tx2, ty2;
jint tw, th;
jint w = ginfo->width;
jint h = ginfo->height;
id<MTLTexture> blitTexture = nil;
J2dTraceLn2(J2D_TRACE_INFO, "MTLTR_DrawLCDGlyphNoCache x %d, y%d", x, y);
J2dTraceLn3(J2D_TRACE_INFO, "MTLTR_DrawLCDGlyphNoCache rowBytesOffset=%d, rgbOrder=%d, contrast=%d", rowBytesOffset, rgbOrder, contrast);
id<MTLRenderCommandEncoder> encoder = nil;
MTLTextureDescriptor *textureDescriptor =
[MTLTextureDescriptor texture2DDescriptorWithPixelFormat:MTLPixelFormatBGRA8Unorm
width:w
height:h
mipmapped:NO];
blitTexture = [mtlc.device newTextureWithDescriptor:textureDescriptor];
if (glyphMode != MODE_NO_CACHE_LCD) {
if (glyphMode == MODE_NO_CACHE_GRAY) {
MTLVertexCache_DisableMaskCache(mtlc);
} else if (glyphMode == MODE_USE_CACHE_GRAY) {
MTLTR_DisableGlyphVertexCache(mtlc);
} else if (glyphMode == MODE_USE_CACHE_LCD) {
[mtlc.encoderManager endEncoder];
lcdCacheEncoder = nil;
} else if (glyphMode == MODE_NO_CACHE_COLOR) {
DisableColorGlyphPainting(mtlc);
}
if (blitTexture == nil) {
J2dTraceLn(J2D_TRACE_ERROR, "can't obtain temporary texture object from pool");
return JNI_FALSE;
}
glyphMode = MODE_NO_CACHE_LCD;
}
encoder = [mtlc.encoderManager getLCDEncoder:dstOps->pTexture isSrcOpaque:YES isDstOpaque:YES];
MTLTR_SetLCDContrast(mtlc, contrast, encoder);
unsigned int imageBytes = w * h * 4;
unsigned char imageData[imageBytes];
memset(&imageData, 0, sizeof(imageData));
int srcIndex = 0;
int dstIndex = 0;
for (int i = 0; i < (w * h); i++) {
imageData[dstIndex++] = ginfo->image[srcIndex++ + rowBytesOffset];
imageData[dstIndex++] = ginfo->image[srcIndex++ + rowBytesOffset];
imageData[dstIndex++] = ginfo->image[srcIndex++ + rowBytesOffset];
imageData[dstIndex++] = 0xFF;
}
// copy LCD mask into glyph texture tile
MTLRegion region = MTLRegionMake2D(0, 0, w, h);
NSUInteger bytesPerRow = 4 * ginfo->width;
[blitTexture replaceRegion:region
mipmapLevel:0
withBytes:imageData
bytesPerRow:bytesPerRow];
tx1 = 0.0f;
ty1 = 0.0f;
tx2 = 1.0f;
ty2 = 1.0f;
J2dTraceLn2(J2D_TRACE_INFO, "MTLTR_DrawLCDGlyphNoCache : dstOps->width = %d, dstOps->height = %d", dstOps->width, dstOps->height);
LCD_ADD_TRIANGLES(tx1, ty1, tx2, ty2, x, y, x+w, y+h);
[encoder setVertexBytes:txtVertices length:sizeof(txtVertices) atIndex:MeshVertexBuffer];
[encoder setFragmentTexture:blitTexture atIndex:0];
[encoder setFragmentTexture:dstOps->pTexture atIndex:1];
[encoder drawPrimitives:MTLPrimitiveTypeTriangle vertexStart:0 vertexCount:6];
vertexCacheIndex = 0;
[mtlc.encoderManager endEncoder];
[blitTexture release];
MTLCommandBufferWrapper* cbwrapper = [mtlc pullCommandBufferWrapper];
id<MTLCommandBuffer> commandbuf = [cbwrapper getCommandBuffer];
[commandbuf addCompletedHandler:^(id <MTLCommandBuffer> commandbuf) {
[cbwrapper release];
}];
[commandbuf commit];
[commandbuf waitUntilCompleted];
return JNI_TRUE;
}
static jboolean
MTLTR_DrawColorGlyphNoCache(MTLContext *mtlc,
GlyphInfo *ginfo, jint x, jint y, BMTLSDOps *dstOps)
{
id<MTLTexture> dest = dstOps->pTexture;
const void *src = ginfo->image;
jint w = ginfo->width;
jint h = ginfo->height;
jint rowBytes = ginfo->rowBytes;
unsigned int imageSize = rowBytes * h;
J2dTraceLn(J2D_TRACE_INFO, "MTLTR_DrawColorGlyphNoCache");
if (glyphMode != MODE_NO_CACHE_COLOR) {
if (glyphMode == MODE_NO_CACHE_GRAY) {
MTLVertexCache_DisableMaskCache(mtlc);
} else if (glyphMode == MODE_USE_CACHE_GRAY) {
MTLTR_DisableGlyphVertexCache(mtlc);
} else if (glyphMode == MODE_USE_CACHE_LCD) {
[mtlc.encoderManager endEncoder];
lcdCacheEncoder = nil;
}
glyphMode = MODE_NO_CACHE_COLOR;
EnableColorGlyphPainting(mtlc);
}
MTLPooledTextureHandle* texHandle = [mtlc.texturePool getTexture:w height:h format:MTLPixelFormatBGRA8Unorm];
if (texHandle == nil) {
J2dTraceLn(J2D_TRACE_ERROR, "MTLTR_DrawColorGlyphNoCache: can't obtain temporary texture object from pool");
return JNI_FALSE;
}
[[mtlc getCommandBufferWrapper] registerPooledTexture:texHandle];
[texHandle.texture replaceRegion:MTLRegionMake2D(0, 0, w, h)
mipmapLevel:0
withBytes:src
bytesPerRow:rowBytes];
drawTex2Tex(mtlc, texHandle.texture, dest, JNI_FALSE, dstOps->isOpaque, INTERPOLATION_NEAREST_NEIGHBOR,
0, 0, w, h, x, y, x + w, y + h);
return JNI_TRUE;
}
// see DrawGlyphList.c for more on this macro...
#define FLOOR_ASSIGN(l, r) \
if ((r)<0) (l) = ((int)floor(r)); else (l) = ((int)(r))
void
MTLTR_DrawGlyphList(JNIEnv *env, MTLContext *mtlc, BMTLSDOps *dstOps,
jint totalGlyphs, jboolean usePositions,
jboolean subPixPos, jboolean rgbOrder, jint lcdContrast,
jfloat glyphListOrigX, jfloat glyphListOrigY,
unsigned char *images, unsigned char *positions)
{
int glyphCounter;
J2dTraceLn(J2D_TRACE_INFO, "MTLTR_DrawGlyphList");
RETURN_IF_NULL(mtlc);
RETURN_IF_NULL(dstOps);
RETURN_IF_NULL(images);
if (usePositions) {
RETURN_IF_NULL(positions);
}
glyphMode = MODE_NOT_INITED;
J2dTraceLn1(J2D_TRACE_INFO, "totalGlyphs = %d", totalGlyphs);
jboolean flushBeforeLCD = JNI_FALSE;
for (glyphCounter = 0; glyphCounter < totalGlyphs; glyphCounter++) {
J2dTraceLn(J2D_TRACE_INFO, "Entered for loop for glyph list");
jint x, y;
jfloat glyphx, glyphy;
jboolean ok;
GlyphInfo *ginfo = (GlyphInfo *)jlong_to_ptr(NEXT_LONG(images));
if (ginfo == NULL) {
// this shouldn't happen, but if it does we'll just break out...
J2dRlsTraceLn(J2D_TRACE_ERROR,
"MTLTR_DrawGlyphList: glyph info is null");
break;
}
if (usePositions) {
jfloat posx = NEXT_FLOAT(positions);
jfloat posy = NEXT_FLOAT(positions);
glyphx = glyphListOrigX + posx + ginfo->topLeftX;
glyphy = glyphListOrigY + posy + ginfo->topLeftY;
FLOOR_ASSIGN(x, glyphx);
FLOOR_ASSIGN(y, glyphy);
} else {
glyphx = glyphListOrigX + ginfo->topLeftX;
glyphy = glyphListOrigY + ginfo->topLeftY;
FLOOR_ASSIGN(x, glyphx);
FLOOR_ASSIGN(y, glyphy);
glyphListOrigX += ginfo->advanceX;
glyphListOrigY += ginfo->advanceY;
}
if (ginfo->image == NULL) {
J2dTraceLn(J2D_TRACE_INFO, "Glyph image is null");
continue;
}
J2dTraceLn2(J2D_TRACE_INFO, "Glyph width = %d height = %d", ginfo->width, ginfo->height);
J2dTraceLn1(J2D_TRACE_INFO, "rowBytes = %d", ginfo->rowBytes);
if (ginfo->rowBytes == ginfo->width) {
// grayscale or monochrome glyph data
if (ginfo->width <= MTLTR_CACHE_CELL_WIDTH &&
ginfo->height <= MTLTR_CACHE_CELL_HEIGHT)
{
J2dTraceLn(J2D_TRACE_INFO, "MTLTR_DrawGlyphList Grayscale cache");
ok = MTLTR_DrawGrayscaleGlyphViaCache(mtlc, ginfo, x, y, dstOps);
} else {
J2dTraceLn(J2D_TRACE_INFO, "MTLTR_DrawGlyphList Grayscale no cache");
ok = MTLTR_DrawGrayscaleGlyphNoCache(mtlc, ginfo, x, y, dstOps);
}
} else if (ginfo->rowBytes == ginfo->width * 4) {
J2dTraceLn(J2D_TRACE_INFO, "MTLTR_DrawGlyphList color glyph no cache");
ok = MTLTR_DrawColorGlyphNoCache(mtlc, ginfo, x, y, dstOps);
flushBeforeLCD = JNI_FALSE;
} else {
if (!flushBeforeLCD) {
[mtlc.encoderManager endEncoder];
MTLCommandBufferWrapper* cbwrapper = [mtlc pullCommandBufferWrapper];
id<MTLCommandBuffer> commandbuf = [cbwrapper getCommandBuffer];
[commandbuf addCompletedHandler:^(id <MTLCommandBuffer> commandbuf) {
[cbwrapper release];
}];
[commandbuf commit];
flushBeforeLCD = JNI_TRUE;
}
// LCD-optimized glyph data
jint rowBytesOffset = 0;
if (subPixPos) {
jint frac = (jint)((glyphx - x) * 3);
if (frac != 0) {
rowBytesOffset = 3 - frac;
x += 1;
}
}
if (rowBytesOffset == 0 &&
ginfo->width <= MTLTR_CACHE_CELL_WIDTH &&
ginfo->height <= MTLTR_CACHE_CELL_HEIGHT)
{
J2dTraceLn(J2D_TRACE_INFO, "MTLTR_DrawGlyphList LCD cache");
ok = MTLTR_DrawLCDGlyphViaCache(mtlc, dstOps,
ginfo, x, y,
rgbOrder, lcdContrast);
} else {
J2dTraceLn(J2D_TRACE_INFO, "MTLTR_DrawGlyphList LCD no cache");
ok = MTLTR_DrawLCDGlyphNoCache(mtlc, dstOps,
ginfo, x, y,
rowBytesOffset,
rgbOrder, lcdContrast);
}
}
if (!ok) {
break;
}
}
/*
* Only in case of grayscale text drawing we need to flush
* cache. Still in case of LCD we are not using any intermediate
* cache.
*/
if (glyphMode == MODE_NO_CACHE_GRAY) {
MTLVertexCache_DisableMaskCache(mtlc);
} else if (glyphMode == MODE_USE_CACHE_GRAY) {
MTLTR_DisableGlyphVertexCache(mtlc);
} else if (glyphMode == MODE_USE_CACHE_LCD) {
[mtlc.encoderManager endEncoder];
lcdCacheEncoder = nil;
} else if (glyphMode == MODE_NO_CACHE_COLOR) {
DisableColorGlyphPainting(mtlc);
}
}
JNIEXPORT void JNICALL
Java_sun_java2d_metal_MTLTextRenderer_drawGlyphList
(JNIEnv *env, jobject self,
jint numGlyphs, jboolean usePositions,
jboolean subPixPos, jboolean rgbOrder, jint lcdContrast,
jfloat glyphListOrigX, jfloat glyphListOrigY,
jlongArray imgArray, jfloatArray posArray)
{
unsigned char *images;
J2dTraceLn(J2D_TRACE_INFO, "MTLTextRenderer_drawGlyphList");
images = (unsigned char *)
(*env)->GetPrimitiveArrayCritical(env, imgArray, NULL);
if (images != NULL) {
MTLContext *mtlc = MTLRenderQueue_GetCurrentContext();
BMTLSDOps *dstOps = MTLRenderQueue_GetCurrentDestination();
if (usePositions) {
unsigned char *positions = (unsigned char *)
(*env)->GetPrimitiveArrayCritical(env, posArray, NULL);
if (positions != NULL) {
MTLTR_DrawGlyphList(env, mtlc, dstOps,
numGlyphs, usePositions,
subPixPos, rgbOrder, lcdContrast,
glyphListOrigX, glyphListOrigY,
images, positions);
(*env)->ReleasePrimitiveArrayCritical(env, posArray,
positions, JNI_ABORT);
}
} else {
MTLTR_DrawGlyphList(env, mtlc, dstOps,
numGlyphs, usePositions,
subPixPos, rgbOrder, lcdContrast,
glyphListOrigX, glyphListOrigY,
images, NULL);
}
if (mtlc != NULL) {
RESET_PREVIOUS_OP();
[mtlc.encoderManager endEncoder];
MTLCommandBufferWrapper * cbwrapper = [mtlc pullCommandBufferWrapper];
id<MTLCommandBuffer> commandbuf = [cbwrapper getCommandBuffer];
[commandbuf addCompletedHandler:^(id <MTLCommandBuffer> commandbuf) {
[cbwrapper release];
}];
[commandbuf commit];
}
(*env)->ReleasePrimitiveArrayCritical(env, imgArray,
images, JNI_ABORT);
}
}