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