1 /*
2 * Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26 #include "jlong.h"
27 #include "math.h"
28 #include "string.h"
29 #include "stdlib.h"
30 #include "sunfontids.h"
31 #include "fontscalerdefs.h"
32 #include "glyphblitting.h"
33 #include "GraphicsPrimitiveMgr.h"
34 #include "sun_java2d_loops_DrawGlyphList.h"
35 #include "sun_java2d_loops_DrawGlyphListAA.h"
36
37
38 /*
39 * Need to account for the rare case when (eg) repainting damaged
40 * areas results in the drawing location being negative, in which
41 * case (int) rounding always goes towards zero. We need to always
42 * round down instead, so that we paint at the correct position.
43 * We only call "floor" when value is < 0 (ie rarely).
44 * Storing the result of (eg) (x+ginfo->topLeftX) benchmarks is more
45 * expensive than repeating the calculation as we do here.
46 * "floor" shows up as a significant cost in app-level microbenchmarks.
47 * This macro avoids calling it on positive values, instead using an
48 * (int) cast.
49 */
50 #define FLOOR_ASSIGN(l, r)\
51 if ((r)<0) (l) = ((int)floor(r)); else (l) = ((int)(r))
52
setupBlitVector(JNIEnv * env,jobject glyphlist)53 GlyphBlitVector* setupBlitVector(JNIEnv *env, jobject glyphlist) {
54
55 int g;
56 size_t bytesNeeded;
57 jlong *imagePtrs;
58 jfloat* positions = NULL;
59 GlyphInfo *ginfo;
60 GlyphBlitVector *gbv;
61
62 jfloat x = (*env)->GetFloatField(env, glyphlist, sunFontIDs.glyphListX);
63 jfloat y = (*env)->GetFloatField(env, glyphlist, sunFontIDs.glyphListY);
64 jint len = (*env)->GetIntField(env, glyphlist, sunFontIDs.glyphListLen);
65 jlongArray glyphImages = (jlongArray)
66 (*env)->GetObjectField(env, glyphlist, sunFontIDs.glyphImages);
67 jfloatArray glyphPositions =
68 (*env)->GetBooleanField(env, glyphlist, sunFontIDs.glyphListUsePos)
69 ? (jfloatArray)
70 (*env)->GetObjectField(env, glyphlist, sunFontIDs.glyphListPos)
71 : NULL;
72
73 bytesNeeded = sizeof(GlyphBlitVector)+sizeof(ImageRef)*len;
74 gbv = (GlyphBlitVector*)malloc(bytesNeeded);
75 if (gbv == NULL) {
76 return NULL;
77 }
78 gbv->numGlyphs = len;
79 gbv->glyphs = (ImageRef*)((unsigned char*)gbv+sizeof(GlyphBlitVector));
80
81 imagePtrs = (*env)->GetPrimitiveArrayCritical(env, glyphImages, NULL);
82 if (imagePtrs == NULL) {
83 free(gbv);
84 return (GlyphBlitVector*)NULL;
85 }
86
87 /* Add 0.5 to x and y and then use floor (or an equivalent operation)
88 * to round down the glyph positions to integral pixel positions.
89 */
90 x += 0.5f;
91 y += 0.5f;
92 if (glyphPositions) {
93 int n = -1;
94
95 positions =
96 (*env)->GetPrimitiveArrayCritical(env, glyphPositions, NULL);
97 if (positions == NULL) {
98 (*env)->ReleasePrimitiveArrayCritical(env, glyphImages,
99 imagePtrs, JNI_ABORT);
100 free(gbv);
101 return (GlyphBlitVector*)NULL;
102 }
103
104 for (g=0; g<len; g++) {
105 jfloat px = x + positions[++n];
106 jfloat py = y + positions[++n];
107
108 ginfo = (GlyphInfo*)imagePtrs[g];
109 gbv->glyphs[g].glyphInfo = ginfo;
110 gbv->glyphs[g].pixels = ginfo->image;
111 gbv->glyphs[g].width = ginfo->width;
112 gbv->glyphs[g].rowBytes = ginfo->rowBytes;
113 gbv->glyphs[g].height = ginfo->height;
114 FLOOR_ASSIGN(gbv->glyphs[g].x, px + ginfo->topLeftX);
115 FLOOR_ASSIGN(gbv->glyphs[g].y, py + ginfo->topLeftY);
116 }
117 (*env)->ReleasePrimitiveArrayCritical(env,glyphPositions,
118 positions, JNI_ABORT);
119 } else {
120 for (g=0; g<len; g++) {
121 ginfo = (GlyphInfo*)imagePtrs[g];
122 gbv->glyphs[g].glyphInfo = ginfo;
123 gbv->glyphs[g].pixels = ginfo->image;
124 gbv->glyphs[g].width = ginfo->width;
125 gbv->glyphs[g].rowBytes = ginfo->rowBytes;
126 gbv->glyphs[g].height = ginfo->height;
127 FLOOR_ASSIGN(gbv->glyphs[g].x, x + ginfo->topLeftX);
128 FLOOR_ASSIGN(gbv->glyphs[g].y, y + ginfo->topLeftY);
129
130 /* copy image data into this array at x/y locations */
131 x += ginfo->advanceX;
132 y += ginfo->advanceY;
133 }
134 }
135
136 (*env)->ReleasePrimitiveArrayCritical(env, glyphImages, imagePtrs,
137 JNI_ABORT);
138 return gbv;
139 }
140
RefineBounds(GlyphBlitVector * gbv,SurfaceDataBounds * bounds)141 jint RefineBounds(GlyphBlitVector *gbv, SurfaceDataBounds *bounds) {
142 int index;
143 jint dx1, dy1, dx2, dy2;
144 ImageRef glyphImage;
145 int num = gbv->numGlyphs;
146 SurfaceDataBounds glyphs;
147
148 glyphs.x1 = glyphs.y1 = 0x7fffffff;
149 glyphs.x2 = glyphs.y2 = 0x80000000;
150 for (index = 0; index < num; index++) {
151 glyphImage = gbv->glyphs[index];
152 dx1 = (jint) glyphImage.x;
153 dy1 = (jint) glyphImage.y;
154 dx2 = dx1 + glyphImage.width;
155 dy2 = dy1 + glyphImage.height;
156 if (glyphs.x1 > dx1) glyphs.x1 = dx1;
157 if (glyphs.y1 > dy1) glyphs.y1 = dy1;
158 if (glyphs.x2 < dx2) glyphs.x2 = dx2;
159 if (glyphs.y2 < dy2) glyphs.y2 = dy2;
160 }
161
162 SurfaceData_IntersectBounds(bounds, &glyphs);
163 return (bounds->x1 < bounds->x2 && bounds->y1 < bounds->y2);
164 }
165
166
167
168
169 /* since the AA and non-AA loop functions share a common method
170 * signature, can call both through this common function since
171 * there's no difference except for the inner loop.
172 * This could be a macro but there's enough of those already.
173 */
drawGlyphList(JNIEnv * env,jobject self,jobject sg2d,jobject sData,GlyphBlitVector * gbv,jint pixel,jint color,NativePrimitive * pPrim,DrawGlyphListFunc * func)174 static void drawGlyphList(JNIEnv *env, jobject self,
175 jobject sg2d, jobject sData,
176 GlyphBlitVector *gbv, jint pixel, jint color,
177 NativePrimitive *pPrim, DrawGlyphListFunc *func) {
178
179 SurfaceDataOps *sdOps;
180 SurfaceDataRasInfo rasInfo;
181 CompositeInfo compInfo;
182 int clipLeft, clipRight, clipTop, clipBottom;
183 int ret;
184
185 sdOps = SurfaceData_GetOps(env, sData);
186 if (sdOps == 0) {
187 return;
188 }
189
190 if (pPrim->pCompType->getCompInfo != NULL) {
191 GrPrim_Sg2dGetCompInfo(env, sg2d, pPrim, &compInfo);
192 }
193
194 GrPrim_Sg2dGetClip(env, sg2d, &rasInfo.bounds);
195 if (rasInfo.bounds.y2 <= rasInfo.bounds.y1 ||
196 rasInfo.bounds.x2 <= rasInfo.bounds.x1)
197 {
198 return;
199 }
200
201 ret = sdOps->Lock(env, sdOps, &rasInfo, pPrim->dstflags);
202 if (ret != SD_SUCCESS) {
203 if (ret == SD_SLOWLOCK) {
204 if (!RefineBounds(gbv, &rasInfo.bounds)) {
205 SurfaceData_InvokeUnlock(env, sdOps, &rasInfo);
206 return;
207 }
208 } else {
209 return;
210 }
211 }
212
213 sdOps->GetRasInfo(env, sdOps, &rasInfo);
214 if (!rasInfo.rasBase) {
215 SurfaceData_InvokeUnlock(env, sdOps, &rasInfo);
216 return;
217 }
218 clipLeft = rasInfo.bounds.x1;
219 clipRight = rasInfo.bounds.x2;
220 clipTop = rasInfo.bounds.y1;
221 clipBottom = rasInfo.bounds.y2;
222 if (clipRight > clipLeft && clipBottom > clipTop) {
223
224 (*func)(&rasInfo,
225 gbv->glyphs, gbv->numGlyphs,
226 pixel, color,
227 clipLeft, clipTop,
228 clipRight, clipBottom,
229 pPrim, &compInfo);
230 SurfaceData_InvokeRelease(env, sdOps, &rasInfo);
231
232 }
233 SurfaceData_InvokeUnlock(env, sdOps, &rasInfo);
234 }
235
236 static unsigned char* getLCDGammaLUT(int gamma);
237 static unsigned char* getInvLCDGammaLUT(int gamma);
238
drawGlyphListLCD(JNIEnv * env,jobject self,jobject sg2d,jobject sData,GlyphBlitVector * gbv,jint pixel,jint color,jboolean rgbOrder,int contrast,NativePrimitive * pPrim,DrawGlyphListLCDFunc * func)239 static void drawGlyphListLCD(JNIEnv *env, jobject self,
240 jobject sg2d, jobject sData,
241 GlyphBlitVector *gbv, jint pixel, jint color,
242 jboolean rgbOrder, int contrast,
243 NativePrimitive *pPrim,
244 DrawGlyphListLCDFunc *func) {
245
246 SurfaceDataOps *sdOps;
247 SurfaceDataRasInfo rasInfo;
248 CompositeInfo compInfo;
249 int clipLeft, clipRight, clipTop, clipBottom;
250 int ret;
251
252 sdOps = SurfaceData_GetOps(env, sData);
253 if (sdOps == 0) {
254 return;
255 }
256
257 if (pPrim->pCompType->getCompInfo != NULL) {
258 GrPrim_Sg2dGetCompInfo(env, sg2d, pPrim, &compInfo);
259 }
260
261 GrPrim_Sg2dGetClip(env, sg2d, &rasInfo.bounds);
262 if (rasInfo.bounds.y2 <= rasInfo.bounds.y1 ||
263 rasInfo.bounds.x2 <= rasInfo.bounds.x1)
264 {
265 return;
266 }
267
268 ret = sdOps->Lock(env, sdOps, &rasInfo, pPrim->dstflags);
269 if (ret != SD_SUCCESS) {
270 if (ret == SD_SLOWLOCK) {
271 if (!RefineBounds(gbv, &rasInfo.bounds)) {
272 SurfaceData_InvokeUnlock(env, sdOps, &rasInfo);
273 return;
274 }
275 } else {
276 return;
277 }
278 }
279
280 sdOps->GetRasInfo(env, sdOps, &rasInfo);
281 if (!rasInfo.rasBase) {
282 SurfaceData_InvokeUnlock(env, sdOps, &rasInfo);
283 return;
284 }
285 clipLeft = rasInfo.bounds.x1;
286 clipRight = rasInfo.bounds.x2;
287 clipTop = rasInfo.bounds.y1;
288 clipBottom = rasInfo.bounds.y2;
289
290 if (clipRight > clipLeft && clipBottom > clipTop) {
291
292 (*func)(&rasInfo,
293 gbv->glyphs, gbv->numGlyphs,
294 pixel, color,
295 clipLeft, clipTop,
296 clipRight, clipBottom, (jint)rgbOrder,
297 getLCDGammaLUT(contrast), getInvLCDGammaLUT(contrast),
298 pPrim, &compInfo);
299 SurfaceData_InvokeRelease(env, sdOps, &rasInfo);
300
301 }
302 SurfaceData_InvokeUnlock(env, sdOps, &rasInfo);
303 }
304
305 /*
306 * Class: sun_java2d_loops_DrawGlyphList
307 * Method: DrawGlyphList
308 * Signature: (Lsun/java2d/SunGraphics2D;Lsun/java2d/SurfaceData;Lsun/java2d/font/GlyphList;J)V
309 */
310 JNIEXPORT void JNICALL
Java_sun_java2d_loops_DrawGlyphList_DrawGlyphList(JNIEnv * env,jobject self,jobject sg2d,jobject sData,jobject glyphlist)311 Java_sun_java2d_loops_DrawGlyphList_DrawGlyphList
312 (JNIEnv *env, jobject self,
313 jobject sg2d, jobject sData, jobject glyphlist) {
314
315 jint pixel, color;
316 GlyphBlitVector* gbv;
317 NativePrimitive *pPrim;
318
319 if ((pPrim = GetNativePrim(env, self)) == NULL) {
320 return;
321 }
322
323 if ((gbv = setupBlitVector(env, glyphlist)) == NULL) {
324 return;
325 }
326
327 pixel = GrPrim_Sg2dGetPixel(env, sg2d);
328 color = GrPrim_Sg2dGetEaRGB(env, sg2d);
329 drawGlyphList(env, self, sg2d, sData, gbv, pixel, color,
330 pPrim, pPrim->funcs.drawglyphlist);
331 free(gbv);
332
333 }
334
335 /*
336 * Class: sun_java2d_loops_DrawGlyphListAA
337 * Method: DrawGlyphListAA
338 * Signature: (Lsun/java2d/SunGraphics2D;Lsun/java2d/SurfaceData;Lsun/java2d/font/GlyphList;J)V
339 */
340 JNIEXPORT void JNICALL
Java_sun_java2d_loops_DrawGlyphListAA_DrawGlyphListAA(JNIEnv * env,jobject self,jobject sg2d,jobject sData,jobject glyphlist)341 Java_sun_java2d_loops_DrawGlyphListAA_DrawGlyphListAA
342 (JNIEnv *env, jobject self,
343 jobject sg2d, jobject sData, jobject glyphlist) {
344
345 jint pixel, color;
346 GlyphBlitVector* gbv;
347 NativePrimitive *pPrim;
348
349 if ((pPrim = GetNativePrim(env, self)) == NULL) {
350 return;
351 }
352
353 if ((gbv = setupBlitVector(env, glyphlist)) == NULL) {
354 return;
355 }
356 pixel = GrPrim_Sg2dGetPixel(env, sg2d);
357 color = GrPrim_Sg2dGetEaRGB(env, sg2d);
358 drawGlyphList(env, self, sg2d, sData, gbv, pixel, color,
359 pPrim, pPrim->funcs.drawglyphlistaa);
360 free(gbv);
361 }
362
363 /*
364 * Class: sun_java2d_loops_DrawGlyphListLCD
365 * Method: DrawGlyphListLCD
366 * Signature: (Lsun/java2d/SunGraphics2D;Lsun/java2d/SurfaceData;Lsun/java2d/font/GlyphList;J)V
367 */
368 JNIEXPORT void JNICALL
Java_sun_java2d_loops_DrawGlyphListLCD_DrawGlyphListLCD(JNIEnv * env,jobject self,jobject sg2d,jobject sData,jobject glyphlist)369 Java_sun_java2d_loops_DrawGlyphListLCD_DrawGlyphListLCD
370 (JNIEnv *env, jobject self,
371 jobject sg2d, jobject sData, jobject glyphlist) {
372
373 jint pixel, color, contrast;
374 jboolean rgbOrder;
375 GlyphBlitVector* gbv;
376 NativePrimitive *pPrim;
377
378 if ((pPrim = GetNativePrim(env, self)) == NULL) {
379 return;
380 }
381
382 if ((gbv = setupLCDBlitVector(env, glyphlist)) == NULL) {
383 return;
384 }
385 pixel = GrPrim_Sg2dGetPixel(env, sg2d);
386 color = GrPrim_Sg2dGetEaRGB(env, sg2d);
387 contrast = GrPrim_Sg2dGetLCDTextContrast(env, sg2d);
388 rgbOrder = (*env)->GetBooleanField(env,glyphlist, sunFontIDs.lcdRGBOrder);
389 drawGlyphListLCD(env, self, sg2d, sData, gbv, pixel, color,
390 rgbOrder, contrast,
391 pPrim, pPrim->funcs.drawglyphlistlcd);
392 free(gbv);
393 }
394
395 /*
396 * LCD text utilises a filter which spreads energy to adjacent subpixels.
397 * So we add 3 bytes (one whole pixel) of padding at the start of every row
398 * to hold energy from the very leftmost sub-pixel.
399 * This is to the left of the intended glyph image position so LCD text also
400 * adjusts the top-left X position of the padded image one pixel to the left
401 * so a glyph image is drawn in the same place it would be if the padding
402 * were not present.
403 *
404 * So in the glyph cache for LCD text the first two bytes of every row are
405 * zero.
406 * We make use of this to be able to adjust the rendering position of the
407 * text when the client specifies a fractional metrics sub-pixel positioning
408 * rendering hint.
409 *
410 * So the first 6 bytes in a cache row looks like :
411 * 00 00 Ex G0 G1 G2
412 *
413 * where
414 * 00 are the always zero bytes
415 * Ex is extra energy spread from the glyph into the left padding pixel.
416 * Gn are the RGB component bytes of the first pixel of the glyph image
417 * For an RGB display G0 is the red component, etc.
418 *
419 * If a glyph is drawn at X=12 then the G0 G1 G2 pixel is placed at that
420 * position : ie G0 is drawn in the first sub-pixel at X=12
421 *
422 * Draw at X=12,0
423 * PIXEL POS 11 11 11 12 12 12 13 13 13
424 * SUBPX POS 0 1 2 0 1 2 0 1 2
425 * 00 00 Ex G0 G1 G2
426 *
427 * If a sub-pixel rounded glyph position is calculated as being X=12.33 -
428 * ie 12 and one-third pixels, we want the result to look like this :
429 * Draw at X=12,1
430 * PIXEL POS 11 11 11 12 12 12 13 13 13
431 * SUBPX POS 0 1 2 0 1 2 0 1 2
432 * 00 00 Ex G0 G1 G2
433 *
434 * ie the G0 byte is moved one sub-pixel to the right.
435 * To do this we need to make two adjustments :
436 * - set X=X+1
437 * - set start of scan row to start+2, ie index past the two zero bytes
438 * ie we don't need the 00 00 bytes at all any more. Rendering start X
439 * can skip over those.
440 *
441 * Lets look at the final case :
442 * If a sub-pixel rounded glyph position is calculated as being X=12.67 -
443 * ie 12 and two-third pixels, we want the result to look like this :
444 * Draw at X=12,2
445 * PIXEL POS 11 11 11 12 12 12 13 13 13
446 * SUBPX POS 0 1 2 0 1 2 0 1 2
447 * 00 00 Ex G0 G1 G2
448 *
449 * ie the G0 byte is moved two sub-pixels to the right, so that the image
450 * starts at 12.67
451 * To do this we need to make these two adjustments :
452 * - set X=X+1
453 * - set start of scan row to start+1, ie index past the first zero byte
454 * In this case the second of the 00 bytes is used as a no-op on the first
455 * red sub-pixel position.
456 *
457 * The final adjustment needed to make all this work is note that if
458 * we moved the start of row one or two bytes in we will go one or two bytes
459 * past the end of the row. So the glyph cache needs to have 2 bytes of
460 * zero padding at the end of each row. This is the extra memory cost to
461 * accommodate this algorithm.
462 *
463 * The resulting text is perhaps fractionally better in overall perception
464 * than rounding to the whole pixel grid, as a few issues arise.
465 *
466 * * the improvement in inter-glyph spacing as well as being limited
467 * to 1/3 pixel resolution, is also limited because the glyphs were hinted
468 * so they fit to the whole pixel grid. It may be worthwhile to pursue
469 * disabling x-axis gridfitting.
470 *
471 * * an LCD display may have gaps between the pixels that are greater
472 * than the subpixels. Thus for thin stemmed fonts, if the shift causes
473 * the "heart" of a stem to span whole pixels it may appear more diffuse -
474 * less sharp. Eliminating hinting would probably not make this worse - in
475 * effect we have already doing that here. But it would improve the spacing.
476 *
477 * * perhaps contradicting the above point in some ways, more diffuse glyphs
478 * are better at reducing colour fringing, but what appears to be more
479 * colour fringing in this FM case is more likely attributable to a greater
480 * likelihood for glyphs to abutt. In integer metrics or even whole pixel
481 * rendered fractional metrics, there's typically more space between the
482 * glyphs. Perhaps disabling X-axis grid-fitting will help with that.
483 */
setupLCDBlitVector(JNIEnv * env,jobject glyphlist)484 GlyphBlitVector* setupLCDBlitVector(JNIEnv *env, jobject glyphlist) {
485
486 int g;
487 size_t bytesNeeded;
488 jlong *imagePtrs;
489 jfloat* positions = NULL;
490 GlyphInfo *ginfo;
491 GlyphBlitVector *gbv;
492
493 jfloat x = (*env)->GetFloatField(env, glyphlist, sunFontIDs.glyphListX);
494 jfloat y = (*env)->GetFloatField(env, glyphlist, sunFontIDs.glyphListY);
495 jint len = (*env)->GetIntField(env, glyphlist, sunFontIDs.glyphListLen);
496 jlongArray glyphImages = (jlongArray)
497 (*env)->GetObjectField(env, glyphlist, sunFontIDs.glyphImages);
498 jfloatArray glyphPositions =
499 (*env)->GetBooleanField(env, glyphlist, sunFontIDs.glyphListUsePos)
500 ? (jfloatArray)
501 (*env)->GetObjectField(env, glyphlist, sunFontIDs.glyphListPos)
502 : NULL;
503 jboolean subPixPos =
504 (*env)->GetBooleanField(env,glyphlist, sunFontIDs.lcdSubPixPos);
505
506 bytesNeeded = sizeof(GlyphBlitVector)+sizeof(ImageRef)*len;
507 gbv = (GlyphBlitVector*)malloc(bytesNeeded);
508 if (gbv == NULL) {
509 return NULL;
510 }
511 gbv->numGlyphs = len;
512 gbv->glyphs = (ImageRef*)((unsigned char*)gbv+sizeof(GlyphBlitVector));
513
514 imagePtrs = (*env)->GetPrimitiveArrayCritical(env, glyphImages, NULL);
515 if (imagePtrs == NULL) {
516 free(gbv);
517 return (GlyphBlitVector*)NULL;
518 }
519
520 /* The position of the start of the text is adjusted up so
521 * that we can round it to an integral pixel position for a
522 * bitmap glyph or non-subpixel positioning, and round it to an
523 * integral subpixel position for that case, hence 0.5/3 = 0.166667
524 * Presently subPixPos means FM, and FM disables embedded bitmaps
525 * Therefore if subPixPos is true we should never get embedded bitmaps
526 * and the glyphlist will be homogenous. This test and the position
527 * adjustments will need to be per glyph once this case becomes
528 * heterogenous.
529 * Also set subPixPos=false if detect a B&W bitmap as we only
530 * need to test that on a per glyph basis once the list becomes
531 * heterogenous
532 */
533 if (subPixPos && len > 0) {
534 ginfo = (GlyphInfo*)imagePtrs[0];
535 if (ginfo == NULL) {
536 (*env)->ReleasePrimitiveArrayCritical(env, glyphImages,
537 imagePtrs, JNI_ABORT);
538 free(gbv);
539 return (GlyphBlitVector*)NULL;
540 }
541 /* rowBytes==width tests if its a B&W or LCD glyph */
542 if (ginfo->width == ginfo->rowBytes) {
543 subPixPos = JNI_FALSE;
544 }
545 }
546 if (subPixPos) {
547 x += 0.1666667f;
548 y += 0.1666667f;
549 } else {
550 x += 0.5f;
551 y += 0.5f;
552 }
553
554 if (glyphPositions) {
555 int n = -1;
556
557 positions =
558 (*env)->GetPrimitiveArrayCritical(env, glyphPositions, NULL);
559 if (positions == NULL) {
560 (*env)->ReleasePrimitiveArrayCritical(env, glyphImages,
561 imagePtrs, JNI_ABORT);
562 free(gbv);
563 return (GlyphBlitVector*)NULL;
564 }
565
566 for (g=0; g<len; g++) {
567 jfloat px, py;
568
569 ginfo = (GlyphInfo*)imagePtrs[g];
570 if (ginfo == NULL) {
571 (*env)->ReleasePrimitiveArrayCritical(env, glyphImages,
572 imagePtrs, JNI_ABORT);
573 free(gbv);
574 return (GlyphBlitVector*)NULL;
575 }
576 gbv->glyphs[g].glyphInfo = ginfo;
577 gbv->glyphs[g].pixels = ginfo->image;
578 gbv->glyphs[g].width = ginfo->width;
579 gbv->glyphs[g].rowBytes = ginfo->rowBytes;
580 gbv->glyphs[g].height = ginfo->height;
581
582 px = x + positions[++n];
583 py = y + positions[++n];
584
585 /*
586 * Subpixel positioning may be requested for LCD text.
587 *
588 * Subpixel positioning can take place only in the direction in
589 * which the subpixels increase the resolution.
590 * So this is useful for the typical case of vertical stripes
591 * increasing the resolution in the direction of the glyph
592 * advances - ie typical horizontally laid out text.
593 * If the subpixel stripes are horizontal, subpixel positioning
594 * can take place only in the vertical direction, which isn't
595 * as useful - you would have to be drawing rotated text on
596 * a display which actually had that organisation. A pretty
597 * unlikely combination.
598 * So this is supported only for vertical stripes which
599 * increase the horizontal resolution.
600 * If in this case the client also rotates the text then there
601 * will still be some benefit for small rotations. For 90 degree
602 * rotation there's no horizontal advance and less benefit
603 * from the subpixel rendering too.
604 * The test for width==rowBytes detects the case where the glyph
605 * is a B&W image obtained from an embedded bitmap. In that
606 * case we cannot apply sub-pixel positioning so ignore it.
607 * This is handled on a per glyph basis.
608 */
609 if (subPixPos) {
610 int frac;
611 float pos = px + ginfo->topLeftX;
612 FLOOR_ASSIGN(gbv->glyphs[g].x, pos);
613 /* Calculate the fractional pixel position - ie the subpixel
614 * position within the RGB/BGR triple. We are rounding to
615 * the nearest, even though we just do (int) since at the
616 * start of the loop the position was already adjusted by
617 * 0.5 (sub)pixels to get rounding.
618 * Thus the "fractional" position will be 0, 1 or 2.
619 * eg 0->0.32 is 0, 0.33->0.66 is 1, > 0.66->0.99 is 2.
620 * We can use an (int) cast here since the floor operation
621 * above guarantees us that the value is positive.
622 */
623 frac = (int)((pos - gbv->glyphs[g].x)*3);
624 if (frac == 0) {
625 /* frac rounded down to zero, so this is equivalent
626 * to no sub-pixel positioning.
627 */
628 gbv->glyphs[g].rowBytesOffset = 0;
629 } else {
630 /* In this case we need to adjust both the position at
631 * which the glyph will be positioned by one pixel to the
632 * left and adjust the position in the glyph image row
633 * from which to extract the data
634 * Every glyph image row has 2 bytes padding
635 * on the right to account for this.
636 */
637 gbv->glyphs[g].rowBytesOffset = 3-frac;
638 gbv->glyphs[g].x += 1;
639 }
640 } else {
641 FLOOR_ASSIGN(gbv->glyphs[g].x, px + ginfo->topLeftX);
642 gbv->glyphs[g].rowBytesOffset = 0;
643 }
644 FLOOR_ASSIGN(gbv->glyphs[g].y, py + ginfo->topLeftY);
645 }
646 (*env)->ReleasePrimitiveArrayCritical(env,glyphPositions,
647 positions, JNI_ABORT);
648 } else {
649 for (g=0; g<len; g++) {
650 ginfo = (GlyphInfo*)imagePtrs[g];
651 if (ginfo == NULL) {
652 (*env)->ReleasePrimitiveArrayCritical(env, glyphImages,
653 imagePtrs, JNI_ABORT);
654 free(gbv);
655 return (GlyphBlitVector*)NULL;
656 }
657 gbv->glyphs[g].glyphInfo = ginfo;
658 gbv->glyphs[g].pixels = ginfo->image;
659 gbv->glyphs[g].width = ginfo->width;
660 gbv->glyphs[g].rowBytes = ginfo->rowBytes;
661 gbv->glyphs[g].height = ginfo->height;
662
663 if (subPixPos) {
664 int frac;
665 float pos = x + ginfo->topLeftX;
666 FLOOR_ASSIGN(gbv->glyphs[g].x, pos);
667 frac = (int)((pos - gbv->glyphs[g].x)*3);
668 if (frac == 0) {
669 gbv->glyphs[g].rowBytesOffset = 0;
670 } else {
671 gbv->glyphs[g].rowBytesOffset = 3-frac;
672 gbv->glyphs[g].x += 1;
673 }
674 } else {
675 FLOOR_ASSIGN(gbv->glyphs[g].x, x + ginfo->topLeftX);
676 gbv->glyphs[g].rowBytesOffset = 0;
677 }
678 FLOOR_ASSIGN(gbv->glyphs[g].y, y + ginfo->topLeftY);
679 /* copy image data into this array at x/y locations */
680 x += ginfo->advanceX;
681 y += ginfo->advanceY;
682 }
683 }
684
685 (*env)->ReleasePrimitiveArrayCritical(env, glyphImages, imagePtrs,
686 JNI_ABORT);
687 return gbv;
688 }
689
690 /* LCD text needs to go through a gamma (contrast) adjustment.
691 * Gamma is constrained to the range 1.0->2.2 with a quantization of
692 * 0.01 (more than good enough). Representing as an integer with that
693 * precision yields a range 100->250 thus we need to store up to 151 LUTs
694 * and inverse LUTs.
695 * We allocate the actual LUTs on an as needed basis. Typically zero or
696 * one is what will be needed.
697 * Colour component values are in the range 0.0->1.0 represented as an integer
698 * in the range 0->255 (ie in a byte). It is assumed that even if we have 5
699 * bit colour components these are presented mapped on to 8 bit components.
700 * lcdGammaLUT references LUTs which convert linear colour components
701 * to a gamma adjusted space, and
702 * lcdInvGammaLUT references LUTs which convert gamma adjusted colour
703 * components to a linear space.
704 */
705 #define MIN_GAMMA 100
706 #define MAX_GAMMA 250
707 #define LCDLUTCOUNT (MAX_GAMMA-MIN_GAMMA+1)
708 UInt8 *lcdGammaLUT[LCDLUTCOUNT];
709 UInt8 *lcdInvGammaLUT[LCDLUTCOUNT];
710
initLUT(int gamma)711 void initLUT(int gamma) {
712 int i,index;
713 double ig,g;
714
715 index = gamma-MIN_GAMMA;
716
717 lcdGammaLUT[index] = (UInt8*)malloc(256);
718 lcdInvGammaLUT[index] = (UInt8*)malloc(256);
719 if (gamma==100) {
720 for (i=0;i<256;i++) {
721 lcdGammaLUT[index][i] = (UInt8)i;
722 lcdInvGammaLUT[index][i] = (UInt8)i;
723 }
724 return;
725 }
726
727 ig = ((double)gamma)/100.0;
728 g = 1.0/ig;
729 lcdGammaLUT[index][0] = (UInt8)0;
730 lcdInvGammaLUT[index][0] = (UInt8)0;
731 lcdGammaLUT[index][255] = (UInt8)255;
732 lcdInvGammaLUT[index][255] = (UInt8)255;
733 for (i=1;i<255;i++) {
734 double val = ((double)i)/255.0;
735 double gval = pow(val, g);
736 double igval = pow(val, ig);
737 lcdGammaLUT[index][i] = (UInt8)(255*gval);
738 lcdInvGammaLUT[index][i] = (UInt8)(255*igval);
739 }
740 }
741
getLCDGammaLUT(int gamma)742 static unsigned char* getLCDGammaLUT(int gamma) {
743 int index;
744
745 if (gamma<MIN_GAMMA) {
746 gamma = MIN_GAMMA;
747 } else if (gamma>MAX_GAMMA) {
748 gamma = MAX_GAMMA;
749 }
750 index = gamma-MIN_GAMMA;
751 if (!lcdGammaLUT[index]) {
752 initLUT(gamma);
753 }
754 return (unsigned char*)lcdGammaLUT[index];
755 }
756
getInvLCDGammaLUT(int gamma)757 static unsigned char* getInvLCDGammaLUT(int gamma) {
758 int index;
759
760 if (gamma<MIN_GAMMA) {
761 gamma = MIN_GAMMA;
762 } else if (gamma>MAX_GAMMA) {
763 gamma = MAX_GAMMA;
764 }
765 index = gamma-MIN_GAMMA;
766 if (!lcdInvGammaLUT[index]) {
767 initLUT(gamma);
768 }
769 return (unsigned char*)lcdInvGammaLUT[index];
770 }
771
772 #if 0
773 void printDefaultTables(int gamma) {
774 int i;
775 UInt8 *g, *ig;
776 lcdGammaLUT[gamma-MIN_GAMMA] = NULL;
777 lcdInvGammaLUT[gamma-MIN_GAMMA] = NULL;
778 g = getLCDGammaLUT(gamma);
779 ig = getInvLCDGammaLUT(gamma);
780 printf("UInt8 defaultGammaLUT[256] = {\n");
781 for (i=0;i<256;i++) {
782 if (i % 8 == 0) {
783 printf(" /* %3d */ ", i);
784 }
785 printf("%4d, ",(int)(g[i]&0xff));
786 if ((i+1) % 8 == 0) {
787 printf("\n");
788 }
789 }
790 printf("};\n");
791
792 printf("UInt8 defaultInvGammaLUT[256] = {\n");
793 for (i=0;i<256;i++) {
794 if (i % 8 == 0) {
795 printf(" /* %3d */ ", i);
796 }
797 printf("%4d, ",(int)(ig[i]&0xff));
798 if ((i+1) % 8 == 0) {
799 printf("\n");
800 }
801 }
802 printf("};\n");
803 }
804 #endif
805
806 /* These tables are generated for a Gamma adjustment of 1.4 */
807 UInt8 defaultGammaLUT[256] = {
808 /* 0 */ 0, 4, 7, 10, 13, 15, 17, 19,
809 /* 8 */ 21, 23, 25, 27, 28, 30, 32, 33,
810 /* 16 */ 35, 36, 38, 39, 41, 42, 44, 45,
811 /* 24 */ 47, 48, 49, 51, 52, 53, 55, 56,
812 /* 32 */ 57, 59, 60, 61, 62, 64, 65, 66,
813 /* 40 */ 67, 69, 70, 71, 72, 73, 75, 76,
814 /* 48 */ 77, 78, 79, 80, 81, 83, 84, 85,
815 /* 56 */ 86, 87, 88, 89, 90, 91, 92, 93,
816 /* 64 */ 94, 96, 97, 98, 99, 100, 101, 102,
817 /* 72 */ 103, 104, 105, 106, 107, 108, 109, 110,
818 /* 80 */ 111, 112, 113, 114, 115, 116, 117, 118,
819 /* 88 */ 119, 120, 121, 122, 123, 124, 125, 125,
820 /* 96 */ 126, 127, 128, 129, 130, 131, 132, 133,
821 /* 104 */ 134, 135, 136, 137, 138, 138, 139, 140,
822 /* 112 */ 141, 142, 143, 144, 145, 146, 147, 147,
823 /* 120 */ 148, 149, 150, 151, 152, 153, 154, 154,
824 /* 128 */ 155, 156, 157, 158, 159, 160, 161, 161,
825 /* 136 */ 162, 163, 164, 165, 166, 167, 167, 168,
826 /* 144 */ 169, 170, 171, 172, 172, 173, 174, 175,
827 /* 152 */ 176, 177, 177, 178, 179, 180, 181, 181,
828 /* 160 */ 182, 183, 184, 185, 186, 186, 187, 188,
829 /* 168 */ 189, 190, 190, 191, 192, 193, 194, 194,
830 /* 176 */ 195, 196, 197, 198, 198, 199, 200, 201,
831 /* 184 */ 201, 202, 203, 204, 205, 205, 206, 207,
832 /* 192 */ 208, 208, 209, 210, 211, 212, 212, 213,
833 /* 200 */ 214, 215, 215, 216, 217, 218, 218, 219,
834 /* 208 */ 220, 221, 221, 222, 223, 224, 224, 225,
835 /* 216 */ 226, 227, 227, 228, 229, 230, 230, 231,
836 /* 224 */ 232, 233, 233, 234, 235, 236, 236, 237,
837 /* 232 */ 238, 239, 239, 240, 241, 242, 242, 243,
838 /* 240 */ 244, 244, 245, 246, 247, 247, 248, 249,
839 /* 248 */ 249, 250, 251, 252, 252, 253, 254, 255,
840 };
841
842 UInt8 defaultInvGammaLUT[256] = {
843 /* 0 */ 0, 0, 0, 0, 0, 1, 1, 1,
844 /* 8 */ 2, 2, 2, 3, 3, 3, 4, 4,
845 /* 16 */ 5, 5, 6, 6, 7, 7, 8, 8,
846 /* 24 */ 9, 9, 10, 10, 11, 12, 12, 13,
847 /* 32 */ 13, 14, 15, 15, 16, 17, 17, 18,
848 /* 40 */ 19, 19, 20, 21, 21, 22, 23, 23,
849 /* 48 */ 24, 25, 26, 26, 27, 28, 29, 29,
850 /* 56 */ 30, 31, 32, 32, 33, 34, 35, 36,
851 /* 64 */ 36, 37, 38, 39, 40, 40, 41, 42,
852 /* 72 */ 43, 44, 45, 45, 46, 47, 48, 49,
853 /* 80 */ 50, 51, 52, 52, 53, 54, 55, 56,
854 /* 88 */ 57, 58, 59, 60, 61, 62, 63, 64,
855 /* 96 */ 64, 65, 66, 67, 68, 69, 70, 71,
856 /* 104 */ 72, 73, 74, 75, 76, 77, 78, 79,
857 /* 112 */ 80, 81, 82, 83, 84, 85, 86, 87,
858 /* 120 */ 88, 89, 90, 91, 92, 93, 95, 96,
859 /* 128 */ 97, 98, 99, 100, 101, 102, 103, 104,
860 /* 136 */ 105, 106, 107, 109, 110, 111, 112, 113,
861 /* 144 */ 114, 115, 116, 117, 119, 120, 121, 122,
862 /* 152 */ 123, 124, 125, 127, 128, 129, 130, 131,
863 /* 160 */ 132, 133, 135, 136, 137, 138, 139, 140,
864 /* 168 */ 142, 143, 144, 145, 146, 148, 149, 150,
865 /* 176 */ 151, 152, 154, 155, 156, 157, 159, 160,
866 /* 184 */ 161, 162, 163, 165, 166, 167, 168, 170,
867 /* 192 */ 171, 172, 173, 175, 176, 177, 178, 180,
868 /* 200 */ 181, 182, 184, 185, 186, 187, 189, 190,
869 /* 208 */ 191, 193, 194, 195, 196, 198, 199, 200,
870 /* 216 */ 202, 203, 204, 206, 207, 208, 210, 211,
871 /* 224 */ 212, 214, 215, 216, 218, 219, 220, 222,
872 /* 232 */ 223, 224, 226, 227, 228, 230, 231, 232,
873 /* 240 */ 234, 235, 236, 238, 239, 241, 242, 243,
874 /* 248 */ 245, 246, 248, 249, 250, 252, 253, 255,
875 };
876
877
878 /* Since our default is 140, here we can populate that from pre-calculated
879 * data, it needs only 512 bytes - plus a few more of overhead - and saves
880 * about that many intrinsic function calls plus other FP calculations.
881 */
initLCDGammaTables()882 void initLCDGammaTables() {
883 memset(lcdGammaLUT, 0, LCDLUTCOUNT * sizeof(UInt8*));
884 memset(lcdInvGammaLUT, 0, LCDLUTCOUNT * sizeof(UInt8*));
885 /* printDefaultTables(140); */
886 lcdGammaLUT[40] = defaultGammaLUT;
887 lcdInvGammaLUT[40] = defaultInvGammaLUT;
888 }
889