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