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
53GlyphBlitVector* 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
141jint 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 */
174static 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
236static unsigned char* getLCDGammaLUT(int gamma);
237static unsigned char* getInvLCDGammaLUT(int gamma);
238
239static 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 */
310JNIEXPORT void JNICALL
311Java_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 */
340JNIEXPORT void JNICALL
341Java_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 */
368JNIEXPORT void JNICALL
369Java_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 */
484GlyphBlitVector* 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 /* rowBytes==width tests if its a B&W or LCD glyph */
536 if (ginfo->width == ginfo->rowBytes) {
537 subPixPos = JNI_FALSE;
538 }
539 }
540 if (subPixPos) {
541 x += 0.1666667f;
542 y += 0.1666667f;
543 } else {
544 x += 0.5f;
545 y += 0.5f;
546 }
547
548 if (glyphPositions) {
549 int n = -1;
550
551 positions =
552 (*env)->GetPrimitiveArrayCritical(env, glyphPositions, NULL);
553 if (positions == NULL) {
554 (*env)->ReleasePrimitiveArrayCritical(env, glyphImages,
555 imagePtrs, JNI_ABORT);
556 free(gbv);
557 return (GlyphBlitVector*)NULL;
558 }
559
560 for (g=0; g<len; g++) {
561 jfloat px, py;
562
563 ginfo = (GlyphInfo*)imagePtrs[g];
564 gbv->glyphs[g].glyphInfo = ginfo;
565 gbv->glyphs[g].pixels = ginfo->image;
566 gbv->glyphs[g].width = ginfo->width;
567 gbv->glyphs[g].rowBytes = ginfo->rowBytes;
568 gbv->glyphs[g].height = ginfo->height;
569
570 px = x + positions[++n];
571 py = y + positions[++n];
572
573 /*
574 * Subpixel positioning may be requested for LCD text.
575 *
576 * Subpixel positioning can take place only in the direction in
577 * which the subpixels increase the resolution.
578 * So this is useful for the typical case of vertical stripes
579 * increasing the resolution in the direction of the glyph
580 * advances - ie typical horizontally laid out text.
581 * If the subpixel stripes are horizontal, subpixel positioning
582 * can take place only in the vertical direction, which isn't
583 * as useful - you would have to be drawing rotated text on
584 * a display which actually had that organisation. A pretty
585 * unlikely combination.
586 * So this is supported only for vertical stripes which
587 * increase the horizontal resolution.
588 * If in this case the client also rotates the text then there
589 * will still be some benefit for small rotations. For 90 degree
590 * rotation there's no horizontal advance and less benefit
591 * from the subpixel rendering too.
592 * The test for width==rowBytes detects the case where the glyph
593 * is a B&W image obtained from an embedded bitmap. In that
594 * case we cannot apply sub-pixel positioning so ignore it.
595 * This is handled on a per glyph basis.
596 */
597 if (subPixPos) {
598 int frac;
599 float pos = px + ginfo->topLeftX;
600 FLOOR_ASSIGN(gbv->glyphs[g].x, pos);
601 /* Calculate the fractional pixel position - ie the subpixel
602 * position within the RGB/BGR triple. We are rounding to
603 * the nearest, even though we just do (int) since at the
604 * start of the loop the position was already adjusted by
605 * 0.5 (sub)pixels to get rounding.
606 * Thus the "fractional" position will be 0, 1 or 2.
607 * eg 0->0.32 is 0, 0.33->0.66 is 1, > 0.66->0.99 is 2.
608 * We can use an (int) cast here since the floor operation
609 * above guarantees us that the value is positive.
610 */
611 frac = (int)((pos - gbv->glyphs[g].x)*3);
612 if (frac == 0) {
613 /* frac rounded down to zero, so this is equivalent
614 * to no sub-pixel positioning.
615 */
616 gbv->glyphs[g].rowBytesOffset = 0;
617 } else {
618 /* In this case we need to adjust both the position at
619 * which the glyph will be positioned by one pixel to the
620 * left and adjust the position in the glyph image row
621 * from which to extract the data
622 * Every glyph image row has 2 bytes padding
623 * on the right to account for this.
624 */
625 gbv->glyphs[g].rowBytesOffset = 3-frac;
626 gbv->glyphs[g].x += 1;
627 }
628 } else {
629 FLOOR_ASSIGN(gbv->glyphs[g].x, px + ginfo->topLeftX);
630 gbv->glyphs[g].rowBytesOffset = 0;
631 }
632 FLOOR_ASSIGN(gbv->glyphs[g].y, py + ginfo->topLeftY);
633 }
634 (*env)->ReleasePrimitiveArrayCritical(env,glyphPositions,
635 positions, JNI_ABORT);
636 } else {
637 for (g=0; g<len; g++) {
638 ginfo = (GlyphInfo*)imagePtrs[g];
639 gbv->glyphs[g].glyphInfo = ginfo;
640 gbv->glyphs[g].pixels = ginfo->image;
641 gbv->glyphs[g].width = ginfo->width;
642 gbv->glyphs[g].rowBytes = ginfo->rowBytes;
643 gbv->glyphs[g].height = ginfo->height;
644
645 if (subPixPos) {
646 int frac;
647 float pos = x + ginfo->topLeftX;
648 FLOOR_ASSIGN(gbv->glyphs[g].x, pos);
649 frac = (int)((pos - gbv->glyphs[g].x)*3);
650 if (frac == 0) {
651 gbv->glyphs[g].rowBytesOffset = 0;
652 } else {
653 gbv->glyphs[g].rowBytesOffset = 3-frac;
654 gbv->glyphs[g].x += 1;
655 }
656 } else {
657 FLOOR_ASSIGN(gbv->glyphs[g].x, x + ginfo->topLeftX);
658 gbv->glyphs[g].rowBytesOffset = 0;
659 }
660 FLOOR_ASSIGN(gbv->glyphs[g].y, y + ginfo->topLeftY);
661 /* copy image data into this array at x/y locations */
662 x += ginfo->advanceX;
663 y += ginfo->advanceY;
664 }
665 }
666
667 (*env)->ReleasePrimitiveArrayCritical(env, glyphImages, imagePtrs,
668 JNI_ABORT);
669 return gbv;
670}
671
672/* LCD text needs to go through a gamma (contrast) adjustment.
673 * Gamma is constrained to the range 1.0->2.2 with a quantization of
674 * 0.01 (more than good enough). Representing as an integer with that
675 * precision yields a range 100->250 thus we need to store up to 151 LUTs
676 * and inverse LUTs.
677 * We allocate the actual LUTs on an as needed basis. Typically zero or
678 * one is what will be needed.
679 * Colour component values are in the range 0.0->1.0 represented as an integer
680 * in the range 0->255 (ie in a byte). It is assumed that even if we have 5
681 * bit colour components these are presented mapped on to 8 bit components.
682 * lcdGammaLUT references LUTs which convert linear colour components
683 * to a gamma adjusted space, and
684 * lcdInvGammaLUT references LUTs which convert gamma adjusted colour
685 * components to a linear space.
686 */
687#define MIN_GAMMA 100
688#define MAX_GAMMA 250
689#define LCDLUTCOUNT (MAX_GAMMA-MIN_GAMMA+1)
690 UInt8 *lcdGammaLUT[LCDLUTCOUNT];
691 UInt8 *lcdInvGammaLUT[LCDLUTCOUNT];
692
693void initLUT(int gamma) {
694 int i,index;
695 double ig,g;
696
697 index = gamma-MIN_GAMMA;
698
699 lcdGammaLUT[index] = (UInt8*)malloc(256);
700 lcdInvGammaLUT[index] = (UInt8*)malloc(256);
701 if (gamma==100) {
702 for (i=0;i<256;i++) {
703 lcdGammaLUT[index][i] = (UInt8)i;
704 lcdInvGammaLUT[index][i] = (UInt8)i;
705 }
706 return;
707 }
708
709 ig = ((double)gamma)/100.0;
710 g = 1.0/ig;
711 lcdGammaLUT[index][0] = (UInt8)0;
712 lcdInvGammaLUT[index][0] = (UInt8)0;
713 lcdGammaLUT[index][255] = (UInt8)255;
714 lcdInvGammaLUT[index][255] = (UInt8)255;
715 for (i=1;i<255;i++) {
716 double val = ((double)i)/255.0;
717 double gval = pow(val, g);
718 double igval = pow(val, ig);
719 lcdGammaLUT[index][i] = (UInt8)(255*gval);
720 lcdInvGammaLUT[index][i] = (UInt8)(255*igval);
721 }
722}
723
724static unsigned char* getLCDGammaLUT(int gamma) {
725 int index;
726
727 if (gamma<MIN_GAMMA) {
728 gamma = MIN_GAMMA;
729 } else if (gamma>MAX_GAMMA) {
730 gamma = MAX_GAMMA;
731 }
732 index = gamma-MIN_GAMMA;
733 if (!lcdGammaLUT[index]) {
734 initLUT(gamma);
735 }
736 return (unsigned char*)lcdGammaLUT[index];
737}
738
739static unsigned char* getInvLCDGammaLUT(int gamma) {
740 int index;
741
742 if (gamma<MIN_GAMMA) {
743 gamma = MIN_GAMMA;
744 } else if (gamma>MAX_GAMMA) {
745 gamma = MAX_GAMMA;
746 }
747 index = gamma-MIN_GAMMA;
748 if (!lcdInvGammaLUT[index]) {
749 initLUT(gamma);
750 }
751 return (unsigned char*)lcdInvGammaLUT[index];
752}
753
754#if 0
755void printDefaultTables(int gamma) {
756 int i;
757 UInt8 *g, *ig;
758 lcdGammaLUT[gamma-MIN_GAMMA] = NULL;
759 lcdInvGammaLUT[gamma-MIN_GAMMA] = NULL;
760 g = getLCDGammaLUT(gamma);
761 ig = getInvLCDGammaLUT(gamma);
762 printf("UInt8 defaultGammaLUT[256] = {\n");
763 for (i=0;i<256;i++) {
764 if (i % 8 == 0) {
765 printf(" /* %3d */ ", i);
766 }
767 printf("%4d, ",(int)(g[i]&0xff));
768 if ((i+1) % 8 == 0) {
769 printf("\n");
770 }
771 }
772 printf("};\n");
773
774 printf("UInt8 defaultInvGammaLUT[256] = {\n");
775 for (i=0;i<256;i++) {
776 if (i % 8 == 0) {
777 printf(" /* %3d */ ", i);
778 }
779 printf("%4d, ",(int)(ig[i]&0xff));
780 if ((i+1) % 8 == 0) {
781 printf("\n");
782 }
783 }
784 printf("};\n");
785}
786#endif
787
788/* These tables are generated for a Gamma adjustment of 1.4 */
789UInt8 defaultGammaLUT[256] = {
790 /* 0 */ 0, 4, 7, 10, 13, 15, 17, 19,
791 /* 8 */ 21, 23, 25, 27, 28, 30, 32, 33,
792 /* 16 */ 35, 36, 38, 39, 41, 42, 44, 45,
793 /* 24 */ 47, 48, 49, 51, 52, 53, 55, 56,
794 /* 32 */ 57, 59, 60, 61, 62, 64, 65, 66,
795 /* 40 */ 67, 69, 70, 71, 72, 73, 75, 76,
796 /* 48 */ 77, 78, 79, 80, 81, 83, 84, 85,
797 /* 56 */ 86, 87, 88, 89, 90, 91, 92, 93,
798 /* 64 */ 94, 96, 97, 98, 99, 100, 101, 102,
799 /* 72 */ 103, 104, 105, 106, 107, 108, 109, 110,
800 /* 80 */ 111, 112, 113, 114, 115, 116, 117, 118,
801 /* 88 */ 119, 120, 121, 122, 123, 124, 125, 125,
802 /* 96 */ 126, 127, 128, 129, 130, 131, 132, 133,
803 /* 104 */ 134, 135, 136, 137, 138, 138, 139, 140,
804 /* 112 */ 141, 142, 143, 144, 145, 146, 147, 147,
805 /* 120 */ 148, 149, 150, 151, 152, 153, 154, 154,
806 /* 128 */ 155, 156, 157, 158, 159, 160, 161, 161,
807 /* 136 */ 162, 163, 164, 165, 166, 167, 167, 168,
808 /* 144 */ 169, 170, 171, 172, 172, 173, 174, 175,
809 /* 152 */ 176, 177, 177, 178, 179, 180, 181, 181,
810 /* 160 */ 182, 183, 184, 185, 186, 186, 187, 188,
811 /* 168 */ 189, 190, 190, 191, 192, 193, 194, 194,
812 /* 176 */ 195, 196, 197, 198, 198, 199, 200, 201,
813 /* 184 */ 201, 202, 203, 204, 205, 205, 206, 207,
814 /* 192 */ 208, 208, 209, 210, 211, 212, 212, 213,
815 /* 200 */ 214, 215, 215, 216, 217, 218, 218, 219,
816 /* 208 */ 220, 221, 221, 222, 223, 224, 224, 225,
817 /* 216 */ 226, 227, 227, 228, 229, 230, 230, 231,
818 /* 224 */ 232, 233, 233, 234, 235, 236, 236, 237,
819 /* 232 */ 238, 239, 239, 240, 241, 242, 242, 243,
820 /* 240 */ 244, 244, 245, 246, 247, 247, 248, 249,
821 /* 248 */ 249, 250, 251, 252, 252, 253, 254, 255,
822};
823
824UInt8 defaultInvGammaLUT[256] = {
825 /* 0 */ 0, 0, 0, 0, 0, 1, 1, 1,
826 /* 8 */ 2, 2, 2, 3, 3, 3, 4, 4,
827 /* 16 */ 5, 5, 6, 6, 7, 7, 8, 8,
828 /* 24 */ 9, 9, 10, 10, 11, 12, 12, 13,
829 /* 32 */ 13, 14, 15, 15, 16, 17, 17, 18,
830 /* 40 */ 19, 19, 20, 21, 21, 22, 23, 23,
831 /* 48 */ 24, 25, 26, 26, 27, 28, 29, 29,
832 /* 56 */ 30, 31, 32, 32, 33, 34, 35, 36,
833 /* 64 */ 36, 37, 38, 39, 40, 40, 41, 42,
834 /* 72 */ 43, 44, 45, 45, 46, 47, 48, 49,
835 /* 80 */ 50, 51, 52, 52, 53, 54, 55, 56,
836 /* 88 */ 57, 58, 59, 60, 61, 62, 63, 64,
837 /* 96 */ 64, 65, 66, 67, 68, 69, 70, 71,
838 /* 104 */ 72, 73, 74, 75, 76, 77, 78, 79,
839 /* 112 */ 80, 81, 82, 83, 84, 85, 86, 87,
840 /* 120 */ 88, 89, 90, 91, 92, 93, 95, 96,
841 /* 128 */ 97, 98, 99, 100, 101, 102, 103, 104,
842 /* 136 */ 105, 106, 107, 109, 110, 111, 112, 113,
843 /* 144 */ 114, 115, 116, 117, 119, 120, 121, 122,
844 /* 152 */ 123, 124, 125, 127, 128, 129, 130, 131,
845 /* 160 */ 132, 133, 135, 136, 137, 138, 139, 140,
846 /* 168 */ 142, 143, 144, 145, 146, 148, 149, 150,
847 /* 176 */ 151, 152, 154, 155, 156, 157, 159, 160,
848 /* 184 */ 161, 162, 163, 165, 166, 167, 168, 170,
849 /* 192 */ 171, 172, 173, 175, 176, 177, 178, 180,
850 /* 200 */ 181, 182, 184, 185, 186, 187, 189, 190,
851 /* 208 */ 191, 193, 194, 195, 196, 198, 199, 200,
852 /* 216 */ 202, 203, 204, 206, 207, 208, 210, 211,
853 /* 224 */ 212, 214, 215, 216, 218, 219, 220, 222,
854 /* 232 */ 223, 224, 226, 227, 228, 230, 231, 232,
855 /* 240 */ 234, 235, 236, 238, 239, 241, 242, 243,
856 /* 248 */ 245, 246, 248, 249, 250, 252, 253, 255,
857};
858
859
860/* Since our default is 140, here we can populate that from pre-calculated
861 * data, it needs only 512 bytes - plus a few more of overhead - and saves
862 * about that many intrinsic function calls plus other FP calculations.
863 */
864void initLCDGammaTables() {
865 memset(lcdGammaLUT, 0, LCDLUTCOUNT * sizeof(UInt8*));
866 memset(lcdInvGammaLUT, 0, LCDLUTCOUNT * sizeof(UInt8*));
867/* printDefaultTables(140); */
868 lcdGammaLUT[40] = defaultGammaLUT;
869 lcdInvGammaLUT[40] = defaultInvGammaLUT;
870}
871