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