Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/share/native/sun/font/DrawGlyphList.c
38829 views
/*1* Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425#include "jlong.h"26#include "math.h"27#include "string.h"28#include "stdlib.h"29#include "sunfontids.h"30#include "fontscalerdefs.h"31#include "glyphblitting.h"32#include "GraphicsPrimitiveMgr.h"33#include "sun_java2d_loops_DrawGlyphList.h"34#include "sun_java2d_loops_DrawGlyphListAA.h"353637/*38* Need to account for the rare case when (eg) repainting damaged39* areas results in the drawing location being negative, in which40* case (int) rounding always goes towards zero. We need to always41* round down instead, so that we paint at the correct position.42* We only call "floor" when value is < 0 (ie rarely).43* Storing the result of (eg) (x+ginfo->topLeftX) benchmarks is more44* expensive than repeating the calculation as we do here.45* "floor" shows up as a significant cost in app-level microbenchmarks.46* This macro avoids calling it on positive values, instead using an47* (int) cast.48*/49#define FLOOR_ASSIGN(l, r)\50if ((r)<0) (l) = ((int)floor(r)); else (l) = ((int)(r))5152GlyphBlitVector* setupBlitVector(JNIEnv *env, jobject glyphlist) {5354int g;55size_t bytesNeeded;56jlong *imagePtrs;57jfloat* positions = NULL;58GlyphInfo *ginfo;59GlyphBlitVector *gbv;6061jfloat x = (*env)->GetFloatField(env, glyphlist, sunFontIDs.glyphListX);62jfloat y = (*env)->GetFloatField(env, glyphlist, sunFontIDs.glyphListY);63jint len = (*env)->GetIntField(env, glyphlist, sunFontIDs.glyphListLen);64jlongArray glyphImages = (jlongArray)65(*env)->GetObjectField(env, glyphlist, sunFontIDs.glyphImages);66jfloatArray glyphPositions =67(*env)->GetBooleanField(env, glyphlist, sunFontIDs.glyphListUsePos)68? (jfloatArray)69(*env)->GetObjectField(env, glyphlist, sunFontIDs.glyphListPos)70: NULL;7172bytesNeeded = sizeof(GlyphBlitVector)+sizeof(ImageRef)*len;73gbv = (GlyphBlitVector*)malloc(bytesNeeded);74if (gbv == NULL) {75return NULL;76}77gbv->numGlyphs = len;78gbv->glyphs = (ImageRef*)((unsigned char*)gbv+sizeof(GlyphBlitVector));7980imagePtrs = (*env)->GetPrimitiveArrayCritical(env, glyphImages, NULL);81if (imagePtrs == NULL) {82free(gbv);83return (GlyphBlitVector*)NULL;84}8586/* Add 0.5 to x and y and then use floor (or an equivalent operation)87* to round down the glyph positions to integral pixel positions.88*/89x += 0.5f;90y += 0.5f;91if (glyphPositions) {92int n = -1;9394positions =95(*env)->GetPrimitiveArrayCritical(env, glyphPositions, NULL);96if (positions == NULL) {97(*env)->ReleasePrimitiveArrayCritical(env, glyphImages,98imagePtrs, JNI_ABORT);99free(gbv);100return (GlyphBlitVector*)NULL;101}102103for (g=0; g<len; g++) {104jfloat px = x + positions[++n];105jfloat py = y + positions[++n];106107ginfo = (GlyphInfo*) imagePtrs[g];108gbv->glyphs[g].glyphInfo = ginfo;109gbv->glyphs[g].pixels = ginfo->image;110gbv->glyphs[g].width = ginfo->width;111gbv->glyphs[g].rowBytes = ginfo->rowBytes;112gbv->glyphs[g].height = ginfo->height;113FLOOR_ASSIGN(gbv->glyphs[g].x, px + ginfo->topLeftX);114FLOOR_ASSIGN(gbv->glyphs[g].y, py + ginfo->topLeftY);115}116(*env)->ReleasePrimitiveArrayCritical(env,glyphPositions,117positions, JNI_ABORT);118} else {119for (g=0; g<len; g++) {120ginfo = (GlyphInfo*)imagePtrs[g];121gbv->glyphs[g].glyphInfo = ginfo;122gbv->glyphs[g].pixels = ginfo->image;123gbv->glyphs[g].width = ginfo->width;124gbv->glyphs[g].rowBytes = ginfo->rowBytes;125gbv->glyphs[g].height = ginfo->height;126FLOOR_ASSIGN(gbv->glyphs[g].x, x + ginfo->topLeftX);127FLOOR_ASSIGN(gbv->glyphs[g].y, y + ginfo->topLeftY);128129/* copy image data into this array at x/y locations */130x += ginfo->advanceX;131y += ginfo->advanceY;132}133}134135(*env)->ReleasePrimitiveArrayCritical(env, glyphImages, imagePtrs,136JNI_ABORT);137return gbv;138}139140jint RefineBounds(GlyphBlitVector *gbv, SurfaceDataBounds *bounds) {141int index;142jint dx1, dy1, dx2, dy2;143ImageRef glyphImage;144int num = gbv->numGlyphs;145SurfaceDataBounds glyphs;146147glyphs.x1 = glyphs.y1 = 0x7fffffff;148glyphs.x2 = glyphs.y2 = 0x80000000;149for (index = 0; index < num; index++) {150glyphImage = gbv->glyphs[index];151dx1 = (jint) glyphImage.x;152dy1 = (jint) glyphImage.y;153dx2 = dx1 + glyphImage.width;154dy2 = dy1 + glyphImage.height;155if (glyphs.x1 > dx1) glyphs.x1 = dx1;156if (glyphs.y1 > dy1) glyphs.y1 = dy1;157if (glyphs.x2 < dx2) glyphs.x2 = dx2;158if (glyphs.y2 < dy2) glyphs.y2 = dy2;159}160161SurfaceData_IntersectBounds(bounds, &glyphs);162return (bounds->x1 < bounds->x2 && bounds->y1 < bounds->y2);163}164165166167168/* since the AA and non-AA loop functions share a common method169* signature, can call both through this common function since170* there's no difference except for the inner loop.171* This could be a macro but there's enough of those already.172*/173static void drawGlyphList(JNIEnv *env, jobject self,174jobject sg2d, jobject sData,175GlyphBlitVector *gbv, jint pixel, jint color,176NativePrimitive *pPrim, DrawGlyphListFunc *func) {177178SurfaceDataOps *sdOps;179SurfaceDataRasInfo rasInfo;180CompositeInfo compInfo;181int clipLeft, clipRight, clipTop, clipBottom;182int ret;183184sdOps = SurfaceData_GetOps(env, sData);185if (sdOps == 0) {186return;187}188189if (pPrim->pCompType->getCompInfo != NULL) {190GrPrim_Sg2dGetCompInfo(env, sg2d, pPrim, &compInfo);191}192193GrPrim_Sg2dGetClip(env, sg2d, &rasInfo.bounds);194if (rasInfo.bounds.y2 <= rasInfo.bounds.y1 ||195rasInfo.bounds.x2 <= rasInfo.bounds.x1)196{197return;198}199200ret = sdOps->Lock(env, sdOps, &rasInfo, pPrim->dstflags);201if (ret != SD_SUCCESS) {202if (ret == SD_SLOWLOCK) {203if (!RefineBounds(gbv, &rasInfo.bounds)) {204SurfaceData_InvokeUnlock(env, sdOps, &rasInfo);205return;206}207} else {208return;209}210}211212sdOps->GetRasInfo(env, sdOps, &rasInfo);213if (!rasInfo.rasBase) {214SurfaceData_InvokeUnlock(env, sdOps, &rasInfo);215return;216}217clipLeft = rasInfo.bounds.x1;218clipRight = rasInfo.bounds.x2;219clipTop = rasInfo.bounds.y1;220clipBottom = rasInfo.bounds.y2;221if (clipRight > clipLeft && clipBottom > clipTop) {222223(*func)(&rasInfo,224gbv->glyphs, gbv->numGlyphs,225pixel, color,226clipLeft, clipTop,227clipRight, clipBottom,228pPrim, &compInfo);229SurfaceData_InvokeRelease(env, sdOps, &rasInfo);230231}232SurfaceData_InvokeUnlock(env, sdOps, &rasInfo);233}234235static unsigned char* getLCDGammaLUT(int gamma);236static unsigned char* getInvLCDGammaLUT(int gamma);237238static void drawGlyphListLCD(JNIEnv *env, jobject self,239jobject sg2d, jobject sData,240GlyphBlitVector *gbv, jint pixel, jint color,241jboolean rgbOrder, int contrast,242NativePrimitive *pPrim,243DrawGlyphListLCDFunc *func) {244245SurfaceDataOps *sdOps;246SurfaceDataRasInfo rasInfo;247CompositeInfo compInfo;248int clipLeft, clipRight, clipTop, clipBottom;249int ret;250251sdOps = SurfaceData_GetOps(env, sData);252if (sdOps == 0) {253return;254}255256if (pPrim->pCompType->getCompInfo != NULL) {257GrPrim_Sg2dGetCompInfo(env, sg2d, pPrim, &compInfo);258}259260GrPrim_Sg2dGetClip(env, sg2d, &rasInfo.bounds);261if (rasInfo.bounds.y2 <= rasInfo.bounds.y1 ||262rasInfo.bounds.x2 <= rasInfo.bounds.x1)263{264return;265}266267ret = sdOps->Lock(env, sdOps, &rasInfo, pPrim->dstflags);268if (ret != SD_SUCCESS) {269if (ret == SD_SLOWLOCK) {270if (!RefineBounds(gbv, &rasInfo.bounds)) {271SurfaceData_InvokeUnlock(env, sdOps, &rasInfo);272return;273}274} else {275return;276}277}278279sdOps->GetRasInfo(env, sdOps, &rasInfo);280if (!rasInfo.rasBase) {281SurfaceData_InvokeUnlock(env, sdOps, &rasInfo);282return;283}284clipLeft = rasInfo.bounds.x1;285clipRight = rasInfo.bounds.x2;286clipTop = rasInfo.bounds.y1;287clipBottom = rasInfo.bounds.y2;288289if (clipRight > clipLeft && clipBottom > clipTop) {290291(*func)(&rasInfo,292gbv->glyphs, gbv->numGlyphs,293pixel, color,294clipLeft, clipTop,295clipRight, clipBottom, (jint)rgbOrder,296getLCDGammaLUT(contrast), getInvLCDGammaLUT(contrast),297pPrim, &compInfo);298SurfaceData_InvokeRelease(env, sdOps, &rasInfo);299300}301SurfaceData_InvokeUnlock(env, sdOps, &rasInfo);302}303304/*305* Class: sun_java2d_loops_DrawGlyphList306* Method: DrawGlyphList307* Signature: (Lsun/java2d/SunGraphics2D;Lsun/java2d/SurfaceData;Lsun/java2d/font/GlyphList;J)V308*/309JNIEXPORT void JNICALL310Java_sun_java2d_loops_DrawGlyphList_DrawGlyphList311(JNIEnv *env, jobject self,312jobject sg2d, jobject sData, jobject glyphlist) {313314jint pixel, color;315GlyphBlitVector* gbv;316NativePrimitive *pPrim;317318if ((pPrim = GetNativePrim(env, self)) == NULL) {319return;320}321322if ((gbv = setupBlitVector(env, glyphlist)) == NULL) {323return;324}325326pixel = GrPrim_Sg2dGetPixel(env, sg2d);327color = GrPrim_Sg2dGetEaRGB(env, sg2d);328drawGlyphList(env, self, sg2d, sData, gbv, pixel, color,329pPrim, pPrim->funcs.drawglyphlist);330free(gbv);331332}333334/*335* Class: sun_java2d_loops_DrawGlyphListAA336* Method: DrawGlyphListAA337* Signature: (Lsun/java2d/SunGraphics2D;Lsun/java2d/SurfaceData;Lsun/java2d/font/GlyphList;J)V338*/339JNIEXPORT void JNICALL340Java_sun_java2d_loops_DrawGlyphListAA_DrawGlyphListAA341(JNIEnv *env, jobject self,342jobject sg2d, jobject sData, jobject glyphlist) {343344jint pixel, color;345GlyphBlitVector* gbv;346NativePrimitive *pPrim;347348if ((pPrim = GetNativePrim(env, self)) == NULL) {349return;350}351352if ((gbv = setupBlitVector(env, glyphlist)) == NULL) {353return;354}355pixel = GrPrim_Sg2dGetPixel(env, sg2d);356color = GrPrim_Sg2dGetEaRGB(env, sg2d);357drawGlyphList(env, self, sg2d, sData, gbv, pixel, color,358pPrim, pPrim->funcs.drawglyphlistaa);359free(gbv);360}361362/*363* Class: sun_java2d_loops_DrawGlyphListLCD364* Method: DrawGlyphListLCD365* Signature: (Lsun/java2d/SunGraphics2D;Lsun/java2d/SurfaceData;Lsun/java2d/font/GlyphList;J)V366*/367JNIEXPORT void JNICALL368Java_sun_java2d_loops_DrawGlyphListLCD_DrawGlyphListLCD369(JNIEnv *env, jobject self,370jobject sg2d, jobject sData, jobject glyphlist) {371372jint pixel, color, contrast;373jboolean rgbOrder;374GlyphBlitVector* gbv;375NativePrimitive *pPrim;376377if ((pPrim = GetNativePrim(env, self)) == NULL) {378return;379}380381if ((gbv = setupLCDBlitVector(env, glyphlist)) == NULL) {382return;383}384pixel = GrPrim_Sg2dGetPixel(env, sg2d);385color = GrPrim_Sg2dGetEaRGB(env, sg2d);386contrast = GrPrim_Sg2dGetLCDTextContrast(env, sg2d);387rgbOrder = (*env)->GetBooleanField(env,glyphlist, sunFontIDs.lcdRGBOrder);388drawGlyphListLCD(env, self, sg2d, sData, gbv, pixel, color,389rgbOrder, contrast,390pPrim, pPrim->funcs.drawglyphlistlcd);391free(gbv);392}393394/*395* LCD text utilises a filter which spreads energy to adjacent subpixels.396* So we add 3 bytes (one whole pixel) of padding at the start of every row397* to hold energy from the very leftmost sub-pixel.398* This is to the left of the intended glyph image position so LCD text also399* adjusts the top-left X position of the padded image one pixel to the left400* so a glyph image is drawn in the same place it would be if the padding401* were not present.402*403* So in the glyph cache for LCD text the first two bytes of every row are404* zero.405* We make use of this to be able to adjust the rendering position of the406* text when the client specifies a fractional metrics sub-pixel positioning407* rendering hint.408*409* So the first 6 bytes in a cache row looks like :410* 00 00 Ex G0 G1 G2411*412* where413* 00 are the always zero bytes414* Ex is extra energy spread from the glyph into the left padding pixel.415* Gn are the RGB component bytes of the first pixel of the glyph image416* For an RGB display G0 is the red component, etc.417*418* If a glyph is drawn at X=12 then the G0 G1 G2 pixel is placed at that419* position : ie G0 is drawn in the first sub-pixel at X=12420*421* Draw at X=12,0422* PIXEL POS 11 11 11 12 12 12 13 13 13423* SUBPX POS 0 1 2 0 1 2 0 1 2424* 00 00 Ex G0 G1 G2425*426* If a sub-pixel rounded glyph position is calculated as being X=12.33 -427* ie 12 and one-third pixels, we want the result to look like this :428* Draw at X=12,1429* PIXEL POS 11 11 11 12 12 12 13 13 13430* SUBPX POS 0 1 2 0 1 2 0 1 2431* 00 00 Ex G0 G1 G2432*433* ie the G0 byte is moved one sub-pixel to the right.434* To do this we need to make two adjustments :435* - set X=X+1436* - set start of scan row to start+2, ie index past the two zero bytes437* ie we don't need the 00 00 bytes at all any more. Rendering start X438* can skip over those.439*440* Lets look at the final case :441* If a sub-pixel rounded glyph position is calculated as being X=12.67 -442* ie 12 and two-third pixels, we want the result to look like this :443* Draw at X=12,2444* PIXEL POS 11 11 11 12 12 12 13 13 13445* SUBPX POS 0 1 2 0 1 2 0 1 2446* 00 00 Ex G0 G1 G2447*448* ie the G0 byte is moved two sub-pixels to the right, so that the image449* starts at 12.67450* To do this we need to make these two adjustments :451* - set X=X+1452* - set start of scan row to start+1, ie index past the first zero byte453* In this case the second of the 00 bytes is used as a no-op on the first454* red sub-pixel position.455*456* The final adjustment needed to make all this work is note that if457* we moved the start of row one or two bytes in we will go one or two bytes458* past the end of the row. So the glyph cache needs to have 2 bytes of459* zero padding at the end of each row. This is the extra memory cost to460* accommodate this algorithm.461*462* The resulting text is perhaps fractionally better in overall perception463* than rounding to the whole pixel grid, as a few issues arise.464*465* * the improvement in inter-glyph spacing as well as being limited466* to 1/3 pixel resolution, is also limited because the glyphs were hinted467* so they fit to the whole pixel grid. It may be worthwhile to pursue468* disabling x-axis gridfitting.469*470* * an LCD display may have gaps between the pixels that are greater471* than the subpixels. Thus for thin stemmed fonts, if the shift causes472* the "heart" of a stem to span whole pixels it may appear more diffuse -473* less sharp. Eliminating hinting would probably not make this worse - in474* effect we have already doing that here. But it would improve the spacing.475*476* * perhaps contradicting the above point in some ways, more diffuse glyphs477* are better at reducing colour fringing, but what appears to be more478* colour fringing in this FM case is more likely attributable to a greater479* likelihood for glyphs to abutt. In integer metrics or even whole pixel480* rendered fractional metrics, there's typically more space between the481* glyphs. Perhaps disabling X-axis grid-fitting will help with that.482*/483GlyphBlitVector* setupLCDBlitVector(JNIEnv *env, jobject glyphlist) {484485int g;486size_t bytesNeeded;487jlong *imagePtrs;488jfloat* positions = NULL;489GlyphInfo *ginfo;490GlyphBlitVector *gbv;491492jfloat x = (*env)->GetFloatField(env, glyphlist, sunFontIDs.glyphListX);493jfloat y = (*env)->GetFloatField(env, glyphlist, sunFontIDs.glyphListY);494jint len = (*env)->GetIntField(env, glyphlist, sunFontIDs.glyphListLen);495jlongArray glyphImages = (jlongArray)496(*env)->GetObjectField(env, glyphlist, sunFontIDs.glyphImages);497jfloatArray glyphPositions =498(*env)->GetBooleanField(env, glyphlist, sunFontIDs.glyphListUsePos)499? (jfloatArray)500(*env)->GetObjectField(env, glyphlist, sunFontIDs.glyphListPos)501: NULL;502jboolean subPixPos =503(*env)->GetBooleanField(env,glyphlist, sunFontIDs.lcdSubPixPos);504505bytesNeeded = sizeof(GlyphBlitVector)+sizeof(ImageRef)*len;506gbv = (GlyphBlitVector*)malloc(bytesNeeded);507if (gbv == NULL) {508return NULL;509}510gbv->numGlyphs = len;511gbv->glyphs = (ImageRef*)((unsigned char*)gbv+sizeof(GlyphBlitVector));512513imagePtrs = (*env)->GetPrimitiveArrayCritical(env, glyphImages, NULL);514if (imagePtrs == NULL) {515free(gbv);516return (GlyphBlitVector*)NULL;517}518519/* The position of the start of the text is adjusted up so520* that we can round it to an integral pixel position for a521* bitmap glyph or non-subpixel positioning, and round it to an522* integral subpixel position for that case, hence 0.5/3 = 0.166667523* Presently subPixPos means FM, and FM disables embedded bitmaps524* Therefore if subPixPos is true we should never get embedded bitmaps525* and the glyphlist will be homogenous. This test and the position526* adjustments will need to be per glyph once this case becomes527* heterogenous.528* Also set subPixPos=false if detect a B&W bitmap as we only529* need to test that on a per glyph basis once the list becomes530* heterogenous531*/532if (subPixPos && len > 0) {533ginfo = (GlyphInfo*)imagePtrs[0];534if (ginfo == NULL) {535(*env)->ReleasePrimitiveArrayCritical(env, glyphImages,536imagePtrs, JNI_ABORT);537free(gbv);538return (GlyphBlitVector*)NULL;539}540/* rowBytes==width tests if its a B&W or LCD glyph */541if (ginfo->width == ginfo->rowBytes) {542subPixPos = JNI_FALSE;543}544}545if (subPixPos) {546x += 0.1666667f;547y += 0.1666667f;548} else {549x += 0.5f;550y += 0.5f;551}552553if (glyphPositions) {554int n = -1;555556positions =557(*env)->GetPrimitiveArrayCritical(env, glyphPositions, NULL);558if (positions == NULL) {559(*env)->ReleasePrimitiveArrayCritical(env, glyphImages,560imagePtrs, JNI_ABORT);561free(gbv);562return (GlyphBlitVector*)NULL;563}564565for (g=0; g<len; g++) {566jfloat px, py;567568ginfo = (GlyphInfo*)imagePtrs[g];569if (ginfo == NULL) {570(*env)->ReleasePrimitiveArrayCritical(env, glyphImages,571imagePtrs, JNI_ABORT);572free(gbv);573return (GlyphBlitVector*)NULL;574}575gbv->glyphs[g].glyphInfo = ginfo;576gbv->glyphs[g].pixels = ginfo->image;577gbv->glyphs[g].width = ginfo->width;578gbv->glyphs[g].rowBytes = ginfo->rowBytes;579gbv->glyphs[g].height = ginfo->height;580581px = x + positions[++n];582py = y + positions[++n];583584/*585* Subpixel positioning may be requested for LCD text.586*587* Subpixel positioning can take place only in the direction in588* which the subpixels increase the resolution.589* So this is useful for the typical case of vertical stripes590* increasing the resolution in the direction of the glyph591* advances - ie typical horizontally laid out text.592* If the subpixel stripes are horizontal, subpixel positioning593* can take place only in the vertical direction, which isn't594* as useful - you would have to be drawing rotated text on595* a display which actually had that organisation. A pretty596* unlikely combination.597* So this is supported only for vertical stripes which598* increase the horizontal resolution.599* If in this case the client also rotates the text then there600* will still be some benefit for small rotations. For 90 degree601* rotation there's no horizontal advance and less benefit602* from the subpixel rendering too.603* The test for width==rowBytes detects the case where the glyph604* is a B&W image obtained from an embedded bitmap. In that605* case we cannot apply sub-pixel positioning so ignore it.606* This is handled on a per glyph basis.607*/608if (subPixPos) {609int frac;610float pos = px + ginfo->topLeftX;611FLOOR_ASSIGN(gbv->glyphs[g].x, pos);612/* Calculate the fractional pixel position - ie the subpixel613* position within the RGB/BGR triple. We are rounding to614* the nearest, even though we just do (int) since at the615* start of the loop the position was already adjusted by616* 0.5 (sub)pixels to get rounding.617* Thus the "fractional" position will be 0, 1 or 2.618* eg 0->0.32 is 0, 0.33->0.66 is 1, > 0.66->0.99 is 2.619* We can use an (int) cast here since the floor operation620* above guarantees us that the value is positive.621*/622frac = (int)((pos - gbv->glyphs[g].x)*3);623if (frac == 0) {624/* frac rounded down to zero, so this is equivalent625* to no sub-pixel positioning.626*/627gbv->glyphs[g].rowBytesOffset = 0;628} else {629/* In this case we need to adjust both the position at630* which the glyph will be positioned by one pixel to the631* left and adjust the position in the glyph image row632* from which to extract the data633* Every glyph image row has 2 bytes padding634* on the right to account for this.635*/636gbv->glyphs[g].rowBytesOffset = 3-frac;637gbv->glyphs[g].x += 1;638}639} else {640FLOOR_ASSIGN(gbv->glyphs[g].x, px + ginfo->topLeftX);641gbv->glyphs[g].rowBytesOffset = 0;642}643FLOOR_ASSIGN(gbv->glyphs[g].y, py + ginfo->topLeftY);644}645(*env)->ReleasePrimitiveArrayCritical(env,glyphPositions,646positions, JNI_ABORT);647} else {648for (g=0; g<len; g++) {649ginfo = (GlyphInfo*)imagePtrs[g];650if (ginfo == NULL) {651(*env)->ReleasePrimitiveArrayCritical(env, glyphImages,652imagePtrs, JNI_ABORT);653free(gbv);654return (GlyphBlitVector*)NULL;655}656gbv->glyphs[g].glyphInfo = ginfo;657gbv->glyphs[g].pixels = ginfo->image;658gbv->glyphs[g].width = ginfo->width;659gbv->glyphs[g].rowBytes = ginfo->rowBytes;660gbv->glyphs[g].height = ginfo->height;661662if (subPixPos) {663int frac;664float pos = x + ginfo->topLeftX;665FLOOR_ASSIGN(gbv->glyphs[g].x, pos);666frac = (int)((pos - gbv->glyphs[g].x)*3);667if (frac == 0) {668gbv->glyphs[g].rowBytesOffset = 0;669} else {670gbv->glyphs[g].rowBytesOffset = 3-frac;671gbv->glyphs[g].x += 1;672}673} else {674FLOOR_ASSIGN(gbv->glyphs[g].x, x + ginfo->topLeftX);675gbv->glyphs[g].rowBytesOffset = 0;676}677FLOOR_ASSIGN(gbv->glyphs[g].y, y + ginfo->topLeftY);678/* copy image data into this array at x/y locations */679x += ginfo->advanceX;680y += ginfo->advanceY;681}682}683684(*env)->ReleasePrimitiveArrayCritical(env, glyphImages, imagePtrs,685JNI_ABORT);686return gbv;687}688689/* LCD text needs to go through a gamma (contrast) adjustment.690* Gamma is constrained to the range 1.0->2.2 with a quantization of691* 0.01 (more than good enough). Representing as an integer with that692* precision yields a range 100->250 thus we need to store up to 151 LUTs693* and inverse LUTs.694* We allocate the actual LUTs on an as needed basis. Typically zero or695* one is what will be needed.696* Colour component values are in the range 0.0->1.0 represented as an integer697* in the range 0->255 (ie in a byte). It is assumed that even if we have 5698* bit colour components these are presented mapped on to 8 bit components.699* lcdGammaLUT references LUTs which convert linear colour components700* to a gamma adjusted space, and701* lcdInvGammaLUT references LUTs which convert gamma adjusted colour702* components to a linear space.703*/704#define MIN_GAMMA 100705#define MAX_GAMMA 250706#define LCDLUTCOUNT (MAX_GAMMA-MIN_GAMMA+1)707UInt8 *lcdGammaLUT[LCDLUTCOUNT];708UInt8 *lcdInvGammaLUT[LCDLUTCOUNT];709710void initLUT(int gamma) {711int i,index;712double ig,g;713714index = gamma-MIN_GAMMA;715716lcdGammaLUT[index] = (UInt8*)malloc(256);717lcdInvGammaLUT[index] = (UInt8*)malloc(256);718if (gamma==100) {719for (i=0;i<256;i++) {720lcdGammaLUT[index][i] = (UInt8)i;721lcdInvGammaLUT[index][i] = (UInt8)i;722}723return;724}725726ig = ((double)gamma)/100.0;727g = 1.0/ig;728lcdGammaLUT[index][0] = (UInt8)0;729lcdInvGammaLUT[index][0] = (UInt8)0;730lcdGammaLUT[index][255] = (UInt8)255;731lcdInvGammaLUT[index][255] = (UInt8)255;732for (i=1;i<255;i++) {733double val = ((double)i)/255.0;734double gval = pow(val, g);735double igval = pow(val, ig);736lcdGammaLUT[index][i] = (UInt8)(255*gval);737lcdInvGammaLUT[index][i] = (UInt8)(255*igval);738}739}740741static unsigned char* getLCDGammaLUT(int gamma) {742int index;743744if (gamma<MIN_GAMMA) {745gamma = MIN_GAMMA;746} else if (gamma>MAX_GAMMA) {747gamma = MAX_GAMMA;748}749index = gamma-MIN_GAMMA;750if (!lcdGammaLUT[index]) {751initLUT(gamma);752}753return (unsigned char*)lcdGammaLUT[index];754}755756static unsigned char* getInvLCDGammaLUT(int gamma) {757int index;758759if (gamma<MIN_GAMMA) {760gamma = MIN_GAMMA;761} else if (gamma>MAX_GAMMA) {762gamma = MAX_GAMMA;763}764index = gamma-MIN_GAMMA;765if (!lcdInvGammaLUT[index]) {766initLUT(gamma);767}768return (unsigned char*)lcdInvGammaLUT[index];769}770771#if 0772void printDefaultTables(int gamma) {773int i;774UInt8 *g, *ig;775lcdGammaLUT[gamma-MIN_GAMMA] = NULL;776lcdInvGammaLUT[gamma-MIN_GAMMA] = NULL;777g = getLCDGammaLUT(gamma);778ig = getInvLCDGammaLUT(gamma);779printf("UInt8 defaultGammaLUT[256] = {\n");780for (i=0;i<256;i++) {781if (i % 8 == 0) {782printf(" /* %3d */ ", i);783}784printf("%4d, ",(int)(g[i]&0xff));785if ((i+1) % 8 == 0) {786printf("\n");787}788}789printf("};\n");790791printf("UInt8 defaultInvGammaLUT[256] = {\n");792for (i=0;i<256;i++) {793if (i % 8 == 0) {794printf(" /* %3d */ ", i);795}796printf("%4d, ",(int)(ig[i]&0xff));797if ((i+1) % 8 == 0) {798printf("\n");799}800}801printf("};\n");802}803#endif804805/* These tables are generated for a Gamma adjustment of 1.4 */806UInt8 defaultGammaLUT[256] = {807/* 0 */ 0, 4, 7, 10, 13, 15, 17, 19,808/* 8 */ 21, 23, 25, 27, 28, 30, 32, 33,809/* 16 */ 35, 36, 38, 39, 41, 42, 44, 45,810/* 24 */ 47, 48, 49, 51, 52, 53, 55, 56,811/* 32 */ 57, 59, 60, 61, 62, 64, 65, 66,812/* 40 */ 67, 69, 70, 71, 72, 73, 75, 76,813/* 48 */ 77, 78, 79, 80, 81, 83, 84, 85,814/* 56 */ 86, 87, 88, 89, 90, 91, 92, 93,815/* 64 */ 94, 96, 97, 98, 99, 100, 101, 102,816/* 72 */ 103, 104, 105, 106, 107, 108, 109, 110,817/* 80 */ 111, 112, 113, 114, 115, 116, 117, 118,818/* 88 */ 119, 120, 121, 122, 123, 124, 125, 125,819/* 96 */ 126, 127, 128, 129, 130, 131, 132, 133,820/* 104 */ 134, 135, 136, 137, 138, 138, 139, 140,821/* 112 */ 141, 142, 143, 144, 145, 146, 147, 147,822/* 120 */ 148, 149, 150, 151, 152, 153, 154, 154,823/* 128 */ 155, 156, 157, 158, 159, 160, 161, 161,824/* 136 */ 162, 163, 164, 165, 166, 167, 167, 168,825/* 144 */ 169, 170, 171, 172, 172, 173, 174, 175,826/* 152 */ 176, 177, 177, 178, 179, 180, 181, 181,827/* 160 */ 182, 183, 184, 185, 186, 186, 187, 188,828/* 168 */ 189, 190, 190, 191, 192, 193, 194, 194,829/* 176 */ 195, 196, 197, 198, 198, 199, 200, 201,830/* 184 */ 201, 202, 203, 204, 205, 205, 206, 207,831/* 192 */ 208, 208, 209, 210, 211, 212, 212, 213,832/* 200 */ 214, 215, 215, 216, 217, 218, 218, 219,833/* 208 */ 220, 221, 221, 222, 223, 224, 224, 225,834/* 216 */ 226, 227, 227, 228, 229, 230, 230, 231,835/* 224 */ 232, 233, 233, 234, 235, 236, 236, 237,836/* 232 */ 238, 239, 239, 240, 241, 242, 242, 243,837/* 240 */ 244, 244, 245, 246, 247, 247, 248, 249,838/* 248 */ 249, 250, 251, 252, 252, 253, 254, 255,839};840841UInt8 defaultInvGammaLUT[256] = {842/* 0 */ 0, 0, 0, 0, 0, 1, 1, 1,843/* 8 */ 2, 2, 2, 3, 3, 3, 4, 4,844/* 16 */ 5, 5, 6, 6, 7, 7, 8, 8,845/* 24 */ 9, 9, 10, 10, 11, 12, 12, 13,846/* 32 */ 13, 14, 15, 15, 16, 17, 17, 18,847/* 40 */ 19, 19, 20, 21, 21, 22, 23, 23,848/* 48 */ 24, 25, 26, 26, 27, 28, 29, 29,849/* 56 */ 30, 31, 32, 32, 33, 34, 35, 36,850/* 64 */ 36, 37, 38, 39, 40, 40, 41, 42,851/* 72 */ 43, 44, 45, 45, 46, 47, 48, 49,852/* 80 */ 50, 51, 52, 52, 53, 54, 55, 56,853/* 88 */ 57, 58, 59, 60, 61, 62, 63, 64,854/* 96 */ 64, 65, 66, 67, 68, 69, 70, 71,855/* 104 */ 72, 73, 74, 75, 76, 77, 78, 79,856/* 112 */ 80, 81, 82, 83, 84, 85, 86, 87,857/* 120 */ 88, 89, 90, 91, 92, 93, 95, 96,858/* 128 */ 97, 98, 99, 100, 101, 102, 103, 104,859/* 136 */ 105, 106, 107, 109, 110, 111, 112, 113,860/* 144 */ 114, 115, 116, 117, 119, 120, 121, 122,861/* 152 */ 123, 124, 125, 127, 128, 129, 130, 131,862/* 160 */ 132, 133, 135, 136, 137, 138, 139, 140,863/* 168 */ 142, 143, 144, 145, 146, 148, 149, 150,864/* 176 */ 151, 152, 154, 155, 156, 157, 159, 160,865/* 184 */ 161, 162, 163, 165, 166, 167, 168, 170,866/* 192 */ 171, 172, 173, 175, 176, 177, 178, 180,867/* 200 */ 181, 182, 184, 185, 186, 187, 189, 190,868/* 208 */ 191, 193, 194, 195, 196, 198, 199, 200,869/* 216 */ 202, 203, 204, 206, 207, 208, 210, 211,870/* 224 */ 212, 214, 215, 216, 218, 219, 220, 222,871/* 232 */ 223, 224, 226, 227, 228, 230, 231, 232,872/* 240 */ 234, 235, 236, 238, 239, 241, 242, 243,873/* 248 */ 245, 246, 248, 249, 250, 252, 253, 255,874};875876877/* Since our default is 140, here we can populate that from pre-calculated878* data, it needs only 512 bytes - plus a few more of overhead - and saves879* about that many intrinsic function calls plus other FP calculations.880*/881void initLCDGammaTables() {882memset(lcdGammaLUT, 0, LCDLUTCOUNT * sizeof(UInt8*));883memset(lcdInvGammaLUT, 0, LCDLUTCOUNT * sizeof(UInt8*));884/* printDefaultTables(140); */885lcdGammaLUT[40] = defaultGammaLUT;886lcdInvGammaLUT[40] = defaultInvGammaLUT;887}888889890