1 /*
2  * Copyright (c) 2008, 2013, 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 /*
27  * The function here is used to get a GDI rasterized LCD glyph and place it
28  * into the JDK glyph cache. The benefit is rendering fidelity for the
29  * most common cases, with no impact on the 2D rendering pipelines.
30  *
31  * Requires that the font and graphics are unrotated, and the scale is
32  * a simple one, and the font is a TT font registered with windows.
33  * Those conditions are established by the calling code.
34  *
35  * This code
36  * - Receives the family name, style, and size of the font
37  * and creates a Font object.
38  * - Create a surface from which we can get a DC : must be 16 bit or more.
39  * Ideally we'd be able to specify the depth of this, but in practice we
40  * have to accept it will be the same as the default screen.
41  * - Selects the GDI font on to the device
42  * - Uses GetGlyphOutline to estimate the bounds.
43  * - Creates a DIB on to which to blit the image.
44  * - Creates a GlyphInfo structure and copies the GDI glyph and offsets
45  * into the glyph which is returned.
46  */
47 
48 #include <stdio.h>
49 #include <malloc.h>
50 #include <math.h>
51 #include <windows.h>
52 #include <winuser.h>
53 
54 #include <jni.h>
55 #include <jni_util.h>
56 #include <jlong_md.h>
57 #include <sizecalc.h>
58 #include <sun_font_FileFontStrike.h>
59 
60 #include "fontscalerdefs.h"
61 
62 /* Some of these are also defined in awtmsg.h but I don't want a dependency
63  * on that here. They are needed here - and in awtmsg.h - until we
64  * move up our build to define WIN32_WINNT >= 0x501 (ie XP), since MS
65  * headers will not define them otherwise.
66  */
67 #ifndef SPI_GETFONTSMOOTHINGTYPE
68 #define SPI_GETFONTSMOOTHINGTYPE        0x200A
69 #endif //SPI_GETFONTSMOOTHINGTYPE
70 
71 #ifndef SPI_GETFONTSMOOTHINGCONTRAST
72 #define SPI_GETFONTSMOOTHINGCONTRAST    0x200C
73 #endif //SPI_GETFONTSMOOTHINGCONTRAST
74 
75 #ifndef SPI_GETFONTSMOOTHINGORIENTATION
76 #define SPI_GETFONTSMOOTHINGORIENTATION    0x2012
77 #endif //SPI_GETFONTSMOOTHINGORIENTATION
78 
79 #ifndef FE_FONTSMOOTHINGORIENTATIONBGR
80 #define FE_FONTSMOOTHINGORIENTATIONBGR 0x0000
81 #endif //FE_FONTSMOOTHINGORIENTATIONBGR
82 
83 #ifndef FE_FONTSMOOTHINGORIENTATIONRGB
84 #define FE_FONTSMOOTHINGORIENTATIONRGB 0x0001
85 #endif //FE_FONTSMOOTHINGORIENTATIONRGB
86 
87 #define MIN_GAMMA 100
88 #define MAX_GAMMA 220
89 #define LCDLUTCOUNT (MAX_GAMMA-MIN_GAMMA+1)
90 
91 static unsigned char* igLUTable[LCDLUTCOUNT];
92 
getIGTable(int gamma)93 static unsigned char* getIGTable(int gamma) {
94     int i, index;
95     double ig;
96     char *igTable;
97 
98     if (gamma < MIN_GAMMA) {
99         gamma = MIN_GAMMA;
100     } else if (gamma > MAX_GAMMA) {
101         gamma = MAX_GAMMA;
102     }
103 
104     index = gamma - MIN_GAMMA;
105 
106     if (igLUTable[index] != NULL) {
107         return igLUTable[index];
108     }
109     igTable = (unsigned char*)malloc(256);
110     if (igTable == NULL) {
111       return NULL;
112     }
113     igTable[0] = 0;
114     igTable[255] = 255;
115     ig = ((double)gamma)/100.0;
116 
117     for (i=1;i<255;i++) {
118         igTable[i] = (unsigned char)(pow(((double)i)/255.0, ig)*255);
119     }
120     igLUTable[index] = igTable;
121     return igTable;
122 }
123 
124 
125 JNIEXPORT jboolean JNICALL
Java_sun_font_FileFontStrike_initNative(JNIEnv * env,jclass unused)126     Java_sun_font_FileFontStrike_initNative(JNIEnv *env, jclass unused) {
127 
128     memset(igLUTable, 0,  LCDLUTCOUNT);
129 
130     return JNI_TRUE;
131 }
132 
133 #ifndef CLEARTYPE_QUALITY
134 #define CLEARTYPE_QUALITY 5
135 #endif
136 
137 #ifndef CLEARTYPE_NATURAL_QUALITY
138 #define CLEARTYPE_NATURAL_QUALITY 6
139 #endif
140 
141 #define FREE_AND_RETURN \
142     if (hDesktopDC != 0 && hWnd != 0) { \
143        ReleaseDC(hWnd, hDesktopDC); \
144     }\
145     if (hMemoryDC != 0) { \
146         DeleteObject(hMemoryDC); \
147     } \
148     if (hBitmap != 0) { \
149         DeleteObject(hBitmap); \
150     } \
151     if (tmpBitmap != 0) { \
152         DeleteObject(tmpBitmap); \
153     } \
154     if (dibImage != NULL) { \
155         free(dibImage); \
156     } \
157     if (glyphInfo != NULL) { \
158         free(glyphInfo); \
159     } \
160     return (jlong)0;
161 /* end define */
162 
163 JNIEXPORT jlong JNICALL
Java_sun_font_FileFontStrike__1getGlyphImageFromWindows(JNIEnv * env,jobject unused,jstring fontFamily,jint style,jint size,jint glyphCode,jboolean fm,jint fontDataSize)164 Java_sun_font_FileFontStrike__1getGlyphImageFromWindows
165 (JNIEnv *env, jobject unused,
166  jstring fontFamily, jint style, jint size, jint glyphCode, jboolean fm,
167  jint fontDataSize) {
168 
169     GLYPHMETRICS glyphMetrics;
170     LOGFONTW lf;
171     BITMAPINFO bmi;
172     TEXTMETRIC textMetric;
173     RECT rect;
174     int bytesWidth, dibBytesWidth, extra, imageSize, dibImageSize;
175     unsigned char* dibImage = NULL, *rowPtr, *pixelPtr, *dibPixPtr, *dibRowPtr;
176     unsigned char r,g,b;
177     unsigned char* igTable;
178     GlyphInfo* glyphInfo = NULL;
179     int nameLen;
180     LPWSTR name;
181     HFONT oldFont, hFont;
182     MAT2 mat2;
183     DWORD actualFontDataSize;
184 
185     unsigned short width;
186     unsigned short height;
187     short advanceX;
188     short advanceY;
189     int topLeftX;
190     int topLeftY;
191     int err;
192     int bmWidth, bmHeight;
193     int x, y;
194     HBITMAP hBitmap = NULL, hOrigBM;
195     HBITMAP tmpBitmap = NULL;
196     int gamma, orient;
197 
198     HWND hWnd = NULL;
199     HDC hDesktopDC = NULL;
200     HDC hMemoryDC = NULL;
201 
202     hWnd = GetDesktopWindow();
203     hDesktopDC = GetWindowDC(hWnd);
204     if (hDesktopDC == NULL) {
205         return (jlong)0;
206     }
207     if (GetDeviceCaps(hDesktopDC, BITSPIXEL) < 15) {
208         FREE_AND_RETURN;
209     }
210 
211     hMemoryDC = CreateCompatibleDC(hDesktopDC);
212     if (hMemoryDC == NULL || fontFamily == NULL) {
213         FREE_AND_RETURN;
214     }
215     err = SetMapMode(hMemoryDC, MM_TEXT);
216     if (err == 0) {
217         FREE_AND_RETURN;
218     }
219 
220     memset(&lf, 0, sizeof(LOGFONTW));
221     lf.lfHeight = -size;
222     lf.lfWeight = (style & 1) ? FW_BOLD : FW_NORMAL;
223     lf.lfItalic = (style & 2) ? 0xff : 0;
224     lf.lfCharSet = DEFAULT_CHARSET;
225     lf.lfQuality = CLEARTYPE_QUALITY;
226     lf.lfOutPrecision = OUT_TT_PRECIS;
227     lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
228     lf.lfPitchAndFamily = DEFAULT_PITCH;
229 
230     nameLen = (*env)->GetStringLength(env, fontFamily);
231     name = (LPWSTR)alloca((nameLen+1)*2);
232     if (name == NULL) {
233        FREE_AND_RETURN;
234     }
235     (*env)->GetStringRegion(env, fontFamily, 0, nameLen, name);
236     name[nameLen] = '\0';
237 
238     if (nameLen < (sizeof(lf.lfFaceName) / sizeof(lf.lfFaceName[0]))) {
239         wcscpy(lf.lfFaceName, name);
240     } else {
241         FREE_AND_RETURN;
242     }
243 
244     hFont = CreateFontIndirectW(&lf);
245     if (hFont == NULL) {
246         FREE_AND_RETURN;
247     }
248     oldFont = SelectObject(hMemoryDC, hFont);
249 
250     if (fontDataSize > 0) {
251         // GDI doesn't allow to select a specific font file for drawing, we can
252         // only check that it picks the file we need by validating font size.
253         // If it doesn't match, we cannot proceed, as the same glyph code can
254         // correspond to a completely different glyph in the selected font.
255         actualFontDataSize = GetFontData(hMemoryDC, 0, 0, NULL, 0);
256         if (actualFontDataSize != fontDataSize) {
257             FREE_AND_RETURN;
258         }
259     }
260 
261     tmpBitmap = CreateCompatibleBitmap(hDesktopDC, 1, 1);
262     if (tmpBitmap == NULL) {
263         FREE_AND_RETURN;
264     }
265     hOrigBM = (HBITMAP)SelectObject(hMemoryDC, tmpBitmap);
266 
267     memset(&textMetric, 0, sizeof(TEXTMETRIC));
268     err = GetTextMetrics(hMemoryDC, &textMetric);
269     if (err == 0) {
270         FREE_AND_RETURN;
271     }
272     memset(&glyphMetrics, 0, sizeof(GLYPHMETRICS));
273     memset(&mat2, 0, sizeof(MAT2));
274     mat2.eM11.value = 1; mat2.eM22.value = 1;
275     err = GetGlyphOutline(hMemoryDC, glyphCode,
276                           GGO_METRICS|GGO_GLYPH_INDEX,
277                           &glyphMetrics,
278                           0, NULL, &mat2);
279     if (err == GDI_ERROR) {
280         /* Probably no such glyph - ie the font wasn't the one we expected. */
281         FREE_AND_RETURN;
282     }
283 
284     width  = (unsigned short)glyphMetrics.gmBlackBoxX;
285     height = (unsigned short)glyphMetrics.gmBlackBoxY;
286 
287     /* Don't handle "invisible" glyphs in this code */
288     if (width <= 0 || height == 0) {
289        FREE_AND_RETURN;
290     }
291 
292     advanceX = glyphMetrics.gmCellIncX;
293     advanceY = glyphMetrics.gmCellIncY;
294     topLeftX = glyphMetrics.gmptGlyphOrigin.x;
295     topLeftY = glyphMetrics.gmptGlyphOrigin.y;
296 
297     /* GetGlyphOutline pre-dates cleartype and I'm not sure that it will
298      * account for all pixels touched by the rendering. Need to widen,
299      * and also adjust by one the x position at which it is rendered.
300      * The extra pixels of width are used as follows :
301      * One extra pixel at the left and the right will be needed to absorb
302      * the pixels that will be touched by filtering by GDI to compensate
303      * for colour fringing.
304      * However there seem to be some cases where GDI renders two extra
305      * pixels to the right, so we add one additional pixel to the right,
306      * and in the code that copies this to the image cache we test for
307      * the (rare) cases when this is touched, and if its not reduce the
308      * stated image width for the blitting loops.
309      * For fractional metrics :
310      * One extra pixel at each end to account for sub-pixel positioning used
311      * when fractional metrics is on in LCD mode.
312      * The pixel at the left is needed so the blitting loop can index into
313      * that a byte at a time to more accurately position the glyph.
314      * The pixel at the right is needed so that when such indexing happens,
315      * the blitting still can use the same width.
316      * Consequently the width that is specified for the glyph is one less
317      * than that of the actual image.
318      * Note that in the FM case as a consequence we need to adjust the
319      * position at which GDI renders, and the declared width of the glyph
320      * See the if (fm) {} cases in the code.
321      * For the non-FM case, we not only save 3 bytes per row, but this
322      * prevents apparent glyph overlapping which affects the rendering
323      * performance of accelerated pipelines since it adds additional
324      * read-back requirements.
325      */
326     width+=3;
327     if (fm) {
328         width+=1;
329     }
330     /* DIB scanline must end on a DWORD boundary. We specify 3 bytes per pixel,
331      * so must round up as needed to a multiple of 4 bytes.
332      */
333     dibBytesWidth = bytesWidth = width*3;
334     extra = dibBytesWidth % 4;
335     if (extra != 0) {
336         dibBytesWidth += (4-extra);
337     }
338     /* The glyph cache image must be a multiple of 3 bytes wide. */
339     extra = bytesWidth % 3;
340     if (extra != 0) {
341         bytesWidth += (3-extra);
342     }
343     bmWidth = width;
344     bmHeight = height;
345 
346     /* Must use desktop DC to create a bitmap of that depth */
347     hBitmap = CreateCompatibleBitmap(hDesktopDC, bmWidth, bmHeight);
348     if (hBitmap == NULL) {
349         FREE_AND_RETURN;
350     }
351     SelectObject(hMemoryDC, hBitmap);
352 
353     /* Fill in black */
354     rect.left = 0;
355     rect.top = 0;
356     rect.right = bmWidth;
357     rect.bottom = bmHeight;
358     FillRect(hMemoryDC, (LPRECT)&rect, GetStockObject(BLACK_BRUSH));
359 
360     /* Set text color to white, background to black. */
361     SetBkColor(hMemoryDC, RGB(0,0,0));
362     SetTextColor(hMemoryDC, RGB(255,255,255));
363 
364     /* adjust rendering position */
365     x = -topLeftX+1;
366     if (fm) {
367         x += 1;
368     }
369     y = topLeftY - textMetric.tmAscent;
370     err = ExtTextOutW(hMemoryDC, x, y, ETO_GLYPH_INDEX|ETO_OPAQUE,
371                 (LPRECT)&rect, (LPCWSTR)&glyphCode, 1, NULL);
372     if (err == 0) {
373         FREE_AND_RETURN;
374     }
375 
376     /* Now get the image into a DIB.
377      * MS docs for GetDIBits says the compatible bitmap must not be
378      * selected into a DC, so restore the original first.
379      */
380     SelectObject(hMemoryDC, hOrigBM);
381     SelectObject(hMemoryDC, oldFont);
382     DeleteObject(hFont);
383 
384     memset(&bmi, 0, sizeof(BITMAPINFO));
385     bmi.bmiHeader.biSize = sizeof(bmi.bmiHeader);
386     bmi.bmiHeader.biWidth = width;
387     bmi.bmiHeader.biHeight = -height;
388     bmi.bmiHeader.biPlanes = 1;
389     bmi.bmiHeader.biBitCount = 24;
390     bmi.bmiHeader.biCompression = BI_RGB;
391 
392     dibImage = SAFE_SIZE_ARRAY_ALLOC(malloc, dibBytesWidth, height);
393     if (dibImage == NULL) {
394         FREE_AND_RETURN;
395     }
396     dibImageSize = dibBytesWidth*height;
397     memset(dibImage, 0, dibImageSize);
398 
399     err = GetDIBits(hMemoryDC, hBitmap, 0, height, dibImage,
400                     &bmi, DIB_RGB_COLORS);
401 
402     if (err == 0) {        /* GetDIBits failed. */
403         FREE_AND_RETURN;
404     }
405 
406     err = SystemParametersInfo(SPI_GETFONTSMOOTHINGORIENTATION, 0, &orient, 0);
407     if (err == 0) {
408         FREE_AND_RETURN;
409     }
410     err = SystemParametersInfo(SPI_GETFONTSMOOTHINGCONTRAST, 0, &gamma, 0);
411     if (err == 0) {
412         FREE_AND_RETURN;
413     }
414     igTable = getIGTable(gamma/10);
415     if (igTable == NULL) {
416         FREE_AND_RETURN;
417     }
418 
419     /* Now copy glyph image into a GlyphInfo structure and return it.
420      * NB the xadvance calculated here may be overwritten by the caller.
421      * 1 is subtracted from the bitmap width to get the glyph width, since
422      * that extra "1" was added as padding, so the sub-pixel positioning of
423      * fractional metrics could index into it.
424      */
425     glyphInfo = (GlyphInfo*)SAFE_SIZE_STRUCT_ALLOC(malloc, sizeof(GlyphInfo),
426             bytesWidth, height);
427     if (glyphInfo == NULL) {
428         FREE_AND_RETURN;
429     }
430     imageSize = bytesWidth*height;
431     glyphInfo->cellInfo = NULL;
432     glyphInfo->rowBytes = bytesWidth;
433     glyphInfo->width = width;
434     if (fm) {
435         glyphInfo->width -= 1; // must subtract 1
436     }
437     glyphInfo->height = height;
438     glyphInfo->advanceX = advanceX;
439     glyphInfo->advanceY = advanceY;
440     glyphInfo->topLeftX = (float)(topLeftX-1);
441     if (fm) {
442         glyphInfo->topLeftX -= 1;
443     }
444     glyphInfo->topLeftY = (float)-topLeftY;
445     glyphInfo->image = (unsigned char*)glyphInfo+sizeof(GlyphInfo);
446     memset(glyphInfo->image, 0, imageSize);
447 
448     /* DIB 24bpp data is always stored in BGR order, but we usually
449      * need this in RGB, so we can't just memcpy and need to swap B and R.
450      * Also need to apply inverse gamma adjustment here.
451      * We re-use the variable "extra" to see if the last pixel is touched
452      * at all. If its not we can reduce the glyph image width. This comes
453      * into play in some cases where GDI touches more pixels than accounted
454      * for by increasing width by two pixels over the B&W image. Whilst
455      * the bytes are in the cache, it doesn't affect rendering performance
456      * of the hardware pipelines.
457      */
458     extra = 0;
459     if (fm) {
460         extra = 1; // always need it.
461     }
462     dibRowPtr = dibImage;
463     rowPtr = glyphInfo->image;
464     for (y=0;y<height;y++) {
465         pixelPtr = rowPtr;
466         dibPixPtr = dibRowPtr;
467         for (x=0;x<width;x++) {
468             if (orient == FE_FONTSMOOTHINGORIENTATIONRGB) {
469                 b = *dibPixPtr++;
470                 g = *dibPixPtr++;
471                 r = *dibPixPtr++;
472             } else {
473                 r = *dibPixPtr++;
474                 g = *dibPixPtr++;
475                 b = *dibPixPtr++;
476             }
477             *pixelPtr++ = igTable[r];
478             *pixelPtr++ = igTable[g];
479             *pixelPtr++ = igTable[b];
480             if (!fm && (x==(width-1)) && (r|g|b)) {
481                 extra = 1;
482             }
483         }
484         dibRowPtr += dibBytesWidth;
485         rowPtr  += bytesWidth;
486     }
487     if (!extra) {
488         glyphInfo->width -= 1;
489     }
490 
491     free(dibImage);
492     ReleaseDC(hWnd, hDesktopDC);
493     DeleteObject(hMemoryDC);
494     DeleteObject(hBitmap);
495     DeleteObject(tmpBitmap);
496 
497     return ptr_to_jlong(glyphInfo);
498 }
499