1 /*
2 * Copyright (c) 2007, 2016, 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 <malloc.h>
27 #include <math.h>
28 #include <jlong.h>
29
30 #include "sun_java2d_d3d_D3DTextRenderer.h"
31 #include "sun_java2d_pipe_BufferedTextPipe.h"
32
33 #include "SurfaceData.h"
34 #include "D3DContext.h"
35 #include "D3DSurfaceData.h"
36 #include "D3DRenderQueue.h"
37 #include "D3DTextRenderer.h"
38 #include "D3DGlyphCache.h"
39 #include "AccelGlyphCache.h"
40 #include "fontscalerdefs.h"
41
42 /**
43 * The current "glyph mode" state. This variable is used to track the
44 * codepath used to render a particular glyph. This variable is reset to
45 * MODE_NOT_INITED at the beginning of every call to D3DTR_DrawGlyphList().
46 * As each glyph is rendered, the glyphMode variable is updated to reflect
47 * the current mode, so if the current mode is the same as the mode used
48 * to render the previous glyph, we can avoid doing costly setup operations
49 * each time.
50 */
51 typedef enum {
52 MODE_NOT_INITED,
53 MODE_USE_CACHE_GRAY,
54 MODE_USE_CACHE_LCD,
55 MODE_NO_CACHE_GRAY,
56 MODE_NO_CACHE_LCD
57 } GlyphMode;
58 static GlyphMode glyphMode = MODE_NOT_INITED;
59
60 /**
61 * The current bounds of the "cached destination" texture, in destination
62 * coordinate space. The width/height of these bounds will not exceed the
63 * D3DTR_CACHED_DEST_WIDTH/HEIGHT values defined above. These bounds are
64 * only considered valid when the isCachedDestValid flag is JNI_TRUE.
65 */
66 static SurfaceDataBounds cachedDestBounds;
67
68 /**
69 * This flag indicates whether the "cached destination" texture contains
70 * valid data. This flag is reset to JNI_FALSE at the beginning of every
71 * call to D3DTR_DrawGlyphList(). Once we copy valid destination data
72 * into the cached texture, this flag is set to JNI_TRUE. This way, we
73 * can limit the number of times we need to copy destination data, which
74 * is a very costly operation.
75 */
76 static jboolean isCachedDestValid = JNI_FALSE;
77
78 /**
79 * The bounds of the previously rendered LCD glyph, in destination
80 * coordinate space. We use these bounds to determine whether the glyph
81 * currently being rendered overlaps the previously rendered glyph (i.e.
82 * its bounding box intersects that of the previously rendered glyph).
83 * If so, we need to re-read the destination area associated with that
84 * previous glyph so that we can correctly blend with the actual
85 * destination data.
86 */
87 static SurfaceDataBounds previousGlyphBounds;
88
89 /**
90 * Updates the gamma and inverse gamma values for the LCD text shader.
91 */
92 static HRESULT
D3DTR_UpdateLCDTextContrast(D3DContext * d3dc,jint contrast)93 D3DTR_UpdateLCDTextContrast(D3DContext *d3dc, jint contrast)
94 {
95 HRESULT res;
96 IDirect3DDevice9 *pd3dDevice = d3dc->Get3DDevice();
97
98 jfloat fcon = ((jfloat)contrast) / 100.0f;
99 jfloat invgamma = fcon;
100 jfloat gamma = 1.0f / invgamma;
101 jfloat vals[4];
102
103 // update the "invgamma" parameter of the shader program
104 vals[0] = invgamma;
105 vals[1] = invgamma;
106 vals[2] = invgamma;
107 vals[3] = 0.0f; // unused
108 pd3dDevice->SetPixelShaderConstantF(1, vals, 1);
109
110 // update the "gamma" parameter of the shader program
111 vals[0] = gamma;
112 vals[1] = gamma;
113 vals[2] = gamma;
114 vals[3] = 0.0f; // unused
115 res = pd3dDevice->SetPixelShaderConstantF(2, vals, 1);
116
117 return res;
118 }
119
120 /**
121 * Updates the current gamma-adjusted source color ("src_adj") of the LCD
122 * text shader program. Note that we could calculate this value in the
123 * shader (e.g. just as we do for "dst_adj"), but would be unnecessary work
124 * (and a measurable performance hit, maybe around 5%) since this value is
125 * constant over the entire glyph list. So instead we just calculate the
126 * gamma-adjusted value once and update the uniform parameter of the LCD
127 * shader as needed.
128 */
129 static HRESULT
D3DTR_UpdateLCDTextColor(D3DContext * d3dc,jint contrast)130 D3DTR_UpdateLCDTextColor(D3DContext *d3dc, jint contrast)
131 {
132 IDirect3DDevice9 *pd3dDevice = d3dc->Get3DDevice();
133 jfloat gamma = ((jfloat)contrast) / 100.0f;
134 jfloat clr[4];
135
136 J2dTraceLn1(J2D_TRACE_INFO,
137 "D3DTR_UpdateLCDTextColor: contrast=%d", contrast);
138
139 /*
140 * Note: Ideally we would update the "srcAdj" uniform parameter only
141 * when there is a change in the source color. Fortunately, the cost
142 * of querying the current D3D color state and updating the uniform
143 * value is quite small, and in the common case we only need to do this
144 * once per GlyphList, so we gain little from trying to optimize too
145 * eagerly here.
146 */
147
148 // get the current D3D primary color state
149 jint color = d3dc->pVCacher->GetColor();
150 clr[0] = (jfloat)((color >> 16) & 0xff) / 255.0f;
151 clr[1] = (jfloat)((color >> 8) & 0xff) / 255.0f;
152 clr[2] = (jfloat)((color >> 0) & 0xff) / 255.0f;
153 clr[3] = 0.0f; // unused
154
155 // gamma adjust the primary color
156 clr[0] = (jfloat)pow(clr[0], gamma);
157 clr[1] = (jfloat)pow(clr[1], gamma);
158 clr[2] = (jfloat)pow(clr[2], gamma);
159
160 // update the "srcAdj" parameter of the shader program with this value
161 return pd3dDevice->SetPixelShaderConstantF(0, clr, 1);
162 }
163
164 /**
165 * Enables the LCD text shader and updates any related state, such as the
166 * gamma values.
167 */
168 static HRESULT
D3DTR_EnableLCDGlyphModeState(D3DContext * d3dc,D3DSDOps * dstOps,jboolean useCache,jint contrast)169 D3DTR_EnableLCDGlyphModeState(D3DContext *d3dc, D3DSDOps *dstOps,
170 jboolean useCache, jint contrast)
171 {
172 D3DResource *pGlyphTexRes, *pCachedDestTexRes;
173 IDirect3DTexture9 *pGlyphTex, *pCachedDestTex;
174
175 RETURN_STATUS_IF_NULL(dstOps->pResource, E_FAIL);
176
177 HRESULT res = S_OK;
178 if (useCache) {
179 // glyph cache had been already initialized
180 pGlyphTexRes = d3dc->GetLCDGlyphCache()->GetGlyphCacheTexture();
181 } else {
182 res = d3dc->GetResourceManager()->GetBlitTexture(&pGlyphTexRes);
183 }
184 RETURN_STATUS_IF_FAILED(res);
185
186 pGlyphTex = pGlyphTexRes->GetTexture();
187
188 res = d3dc->GetResourceManager()->
189 GetCachedDestTexture(dstOps->pResource->GetDesc()->Format,
190 &pCachedDestTexRes);
191 RETURN_STATUS_IF_FAILED(res);
192 pCachedDestTex = pCachedDestTexRes->GetTexture();
193
194 IDirect3DDevice9 *pd3dDevice = d3dc->Get3DDevice();
195 D3DTEXTUREFILTERTYPE fhint =
196 d3dc->IsTextureFilteringSupported(D3DTEXF_NONE) ?
197 D3DTEXF_NONE : D3DTEXF_POINT;
198 pd3dDevice->SetSamplerState(0, D3DSAMP_MAGFILTER, fhint);
199 pd3dDevice->SetSamplerState(0, D3DSAMP_MINFILTER, fhint);
200 pd3dDevice->SetSamplerState(1, D3DSAMP_MAGFILTER, fhint);
201 pd3dDevice->SetSamplerState(1, D3DSAMP_MINFILTER, fhint);
202 d3dc->UpdateTextureColorState(D3DTA_TEXTURE, 1);
203
204 // bind the texture containing glyph data to texture unit 0
205 d3dc->SetTexture(pGlyphTex, 0);
206
207 // bind the texture tile containing destination data to texture unit 1
208 d3dc->SetTexture(pCachedDestTex, 1);
209
210 // create/enable the LCD text shader
211 res = d3dc->EnableLCDTextProgram();
212 RETURN_STATUS_IF_FAILED(res);
213
214 // update the current contrast settings (note: these change very rarely,
215 // but it seems that D3D pixel shader registers aren't maintained as
216 // part of the pixel shader instance, so we need to update these
217 // everytime around in case another shader blew away the contents
218 // of those registers)
219 D3DTR_UpdateLCDTextContrast(d3dc, contrast);
220
221 // update the current color settings
222 return D3DTR_UpdateLCDTextColor(d3dc, contrast);
223 }
224
225 HRESULT
D3DTR_EnableGlyphVertexCache(D3DContext * d3dc)226 D3DTR_EnableGlyphVertexCache(D3DContext *d3dc)
227 {
228 J2dTraceLn(J2D_TRACE_INFO, "D3DTR_EnableGlyphVertexCache");
229
230 IDirect3DDevice9 *pd3dDevice = d3dc->Get3DDevice();
231 D3DTEXTUREFILTERTYPE fhint =
232 d3dc->IsTextureFilteringSupported(D3DTEXF_NONE) ?
233 D3DTEXF_NONE : D3DTEXF_POINT;
234 pd3dDevice->SetSamplerState(0, D3DSAMP_MAGFILTER, fhint);
235 pd3dDevice->SetSamplerState(0, D3DSAMP_MINFILTER, fhint);
236
237 // glyph cache had been successfully initialized if we got here
238 D3DResource *pGlyphCacheTexRes =
239 d3dc->GetGrayscaleGlyphCache()->GetGlyphCacheTexture();
240 return d3dc->SetTexture(pGlyphCacheTexRes->GetTexture(), 0);
241 }
242
243 HRESULT
D3DTR_DisableGlyphVertexCache(D3DContext * d3dc)244 D3DTR_DisableGlyphVertexCache(D3DContext *d3dc)
245 {
246 J2dTraceLn(J2D_TRACE_INFO, "D3DTR_DisableGlyphVertexCache");
247
248 return d3dc->SetTexture(NULL, 0);
249 }
250
251 /**
252 * Disables any pending state associated with the current "glyph mode".
253 */
254 static HRESULT
D3DTR_DisableGlyphModeState(D3DContext * d3dc)255 D3DTR_DisableGlyphModeState(D3DContext *d3dc)
256 {
257 HRESULT res = S_OK;
258 IDirect3DDevice9 *pd3dDevice = d3dc->Get3DDevice();
259
260 switch (glyphMode) {
261 case MODE_NO_CACHE_LCD:
262 case MODE_USE_CACHE_LCD:
263 d3dc->FlushVertexQueue();
264 pd3dDevice->SetPixelShader(NULL);
265 res = d3dc->SetTexture(NULL, 1);
266 break;
267
268 case MODE_NO_CACHE_GRAY:
269 case MODE_USE_CACHE_GRAY:
270 case MODE_NOT_INITED:
271 default:
272 break;
273 }
274 return res;
275 }
276
277 static HRESULT
D3DTR_DrawGrayscaleGlyphViaCache(D3DContext * d3dc,GlyphInfo * ginfo,jint x,jint y)278 D3DTR_DrawGrayscaleGlyphViaCache(D3DContext *d3dc,
279 GlyphInfo *ginfo, jint x, jint y)
280 {
281 HRESULT res = S_OK;
282 D3DGlyphCache *pGrayscaleGCache;
283 CacheCellInfo *cell;
284 GlyphCacheInfo *gcache;
285 jfloat x1, y1, x2, y2;
286
287 J2dTraceLn(J2D_TRACE_VERBOSE, "D3DTR_DrawGrayscaleGlyphViaCache");
288
289 if (glyphMode != MODE_USE_CACHE_GRAY) {
290 D3DTR_DisableGlyphModeState(d3dc);
291
292 res = d3dc->BeginScene(STATE_GLYPHOP);
293 RETURN_STATUS_IF_FAILED(res);
294
295 glyphMode = MODE_USE_CACHE_GRAY;
296 }
297
298 pGrayscaleGCache = d3dc->GetGrayscaleGlyphCache();
299 gcache = pGrayscaleGCache->GetGlyphCache();
300 cell = AccelGlyphCache_GetCellInfoForCache(ginfo, gcache);
301 if (cell == NULL) {
302 // attempt to add glyph to accelerated glyph cache
303 res = pGrayscaleGCache->AddGlyph(ginfo);
304 RETURN_STATUS_IF_FAILED(res);
305
306 cell = AccelGlyphCache_GetCellInfoForCache(ginfo, gcache);
307 RETURN_STATUS_IF_NULL(cell, E_FAIL);
308 }
309
310 cell->timesRendered++;
311
312 x1 = (jfloat)x;
313 y1 = (jfloat)y;
314 x2 = x1 + ginfo->width;
315 y2 = y1 + ginfo->height;
316
317 return d3dc->pVCacher->DrawTexture(x1, y1, x2, y2,
318 cell->tx1, cell->ty1,
319 cell->tx2, cell->ty2);
320 }
321
322 /**
323 * Evaluates to true if the rectangle defined by gx1/gy1/gx2/gy2 is
324 * inside outerBounds.
325 */
326 #define INSIDE(gx1, gy1, gx2, gy2, outerBounds) \
327 (((gx1) >= outerBounds.x1) && ((gy1) >= outerBounds.y1) && \
328 ((gx2) <= outerBounds.x2) && ((gy2) <= outerBounds.y2))
329
330 /**
331 * Evaluates to true if the rectangle defined by gx1/gy1/gx2/gy2 intersects
332 * the rectangle defined by bounds.
333 */
334 #define INTERSECTS(gx1, gy1, gx2, gy2, bounds) \
335 ((bounds.x2 > (gx1)) && (bounds.y2 > (gy1)) && \
336 (bounds.x1 < (gx2)) && (bounds.y1 < (gy2)))
337
338 /**
339 * This method checks to see if the given LCD glyph bounds fall within the
340 * cached destination texture bounds. If so, this method can return
341 * immediately. If not, this method will copy a chunk of framebuffer data
342 * into the cached destination texture and then update the current cached
343 * destination bounds before returning.
344 *
345 * The agx1, agx2 are "adjusted" glyph bounds, which are only used when checking
346 * against the previous glyph bounds.
347 */
348 static HRESULT
D3DTR_UpdateCachedDestination(D3DContext * d3dc,D3DSDOps * dstOps,GlyphInfo * ginfo,jint gx1,jint gy1,jint gx2,jint gy2,jint agx1,jint agx2,jint glyphIndex,jint totalGlyphs)349 D3DTR_UpdateCachedDestination(D3DContext *d3dc, D3DSDOps *dstOps,
350 GlyphInfo *ginfo,
351 jint gx1, jint gy1, jint gx2, jint gy2,
352 jint agx1, jint agx2,
353 jint glyphIndex, jint totalGlyphs)
354 {
355 jint dx1, dy1, dx2, dy2;
356 D3DResource *pCachedDestTexRes;
357 IDirect3DSurface9 *pCachedDestSurface, *pDst;
358 HRESULT res = S_OK;
359
360 if (isCachedDestValid && INSIDE(gx1, gy1, gx2, gy2, cachedDestBounds)) {
361 // glyph is already within the cached destination bounds; no need
362 // to read back the entire destination region again, but we do
363 // need to see if the current glyph overlaps the previous glyph...
364
365 // only use the "adjusted" glyph bounds when checking against
366 // previous glyph's bounds
367 gx1 = agx1;
368 gx2 = agx2;
369
370 if (INTERSECTS(gx1, gy1, gx2, gy2, previousGlyphBounds)) {
371 // the current glyph overlaps the destination region touched
372 // by the previous glyph, so now we need to read back the part
373 // of the destination corresponding to the previous glyph
374 dx1 = previousGlyphBounds.x1;
375 dy1 = previousGlyphBounds.y1;
376 dx2 = previousGlyphBounds.x2;
377 dy2 = previousGlyphBounds.y2;
378
379 // REMIND: make sure we flush any pending primitives that are
380 // dependent on the current contents of the cached dest
381 d3dc->FlushVertexQueue();
382
383 RETURN_STATUS_IF_NULL(dstOps->pResource, E_FAIL);
384 RETURN_STATUS_IF_NULL(pDst = dstOps->pResource->GetSurface(),
385 E_FAIL);
386 res = d3dc->GetResourceManager()->
387 GetCachedDestTexture(dstOps->pResource->GetDesc()->Format,
388 &pCachedDestTexRes);
389 RETURN_STATUS_IF_FAILED(res);
390 pCachedDestSurface = pCachedDestTexRes->GetSurface();
391
392 // now dxy12 represent the "desired" destination bounds, but the
393 // StretchRect() call may fail if these fall outside the actual
394 // surface bounds; therefore, we use cxy12 to represent the
395 // clamped bounds, and dxy12 are saved for later
396 jint cx1 = (dx1 < 0) ? 0 : dx1;
397 jint cy1 = (dy1 < 0) ? 0 : dy1;
398 jint cx2 = (dx2 > dstOps->width) ? dstOps->width : dx2;
399 jint cy2 = (dy2 > dstOps->height) ? dstOps->height : dy2;
400
401 if (cx2 > cx1 && cy2 > cy1) {
402 // copy destination into subregion of cached texture tile
403 // cx1-cachedDestBounds.x1 == +xoffset from left of texture
404 // cy1-cachedDestBounds.y1 == +yoffset from top of texture
405 // cx2-cachedDestBounds.x1 == +xoffset from left of texture
406 // cy2-cachedDestBounds.y1 == +yoffset from top of texture
407 jint cdx1 = cx1-cachedDestBounds.x1;
408 jint cdy1 = cy1-cachedDestBounds.y1;
409 jint cdx2 = cx2-cachedDestBounds.x1;
410 jint cdy2 = cy2-cachedDestBounds.y1;
411 RECT srcRect = { cx1, cy1, cx2, cy2 };
412 RECT dstRect = { cdx1, cdy1, cdx2, cdy2 };
413
414 IDirect3DDevice9 *pd3dDevice = d3dc->Get3DDevice();
415 res = pd3dDevice->StretchRect(pDst, &srcRect,
416 pCachedDestSurface, &dstRect,
417 D3DTEXF_NONE);
418 }
419 }
420 } else {
421 // destination region is not valid, so we need to read back a
422 // chunk of the destination into our cached texture
423
424 // position the upper-left corner of the destination region on the
425 // "top" line of glyph list
426 // REMIND: this isn't ideal; it would be better if we had some idea
427 // of the bounding box of the whole glyph list (this is
428 // do-able, but would require iterating through the whole
429 // list up front, which may present its own problems)
430 dx1 = gx1;
431 dy1 = gy1;
432
433 jint remainingWidth;
434 if (ginfo->advanceX > 0) {
435 // estimate the width based on our current position in the glyph
436 // list and using the x advance of the current glyph (this is just
437 // a quick and dirty heuristic; if this is a "thin" glyph image,
438 // then we're likely to underestimate, and if it's "thick" then we
439 // may end up reading back more than we need to)
440 remainingWidth =
441 (jint)(ginfo->advanceX * (totalGlyphs - glyphIndex));
442 if (remainingWidth > D3DTR_CACHED_DEST_WIDTH) {
443 remainingWidth = D3DTR_CACHED_DEST_WIDTH;
444 } else if (remainingWidth < ginfo->width) {
445 // in some cases, the x-advance may be slightly smaller
446 // than the actual width of the glyph; if so, adjust our
447 // estimate so that we can accommodate the entire glyph
448 remainingWidth = ginfo->width;
449 }
450 } else {
451 // a negative advance is possible when rendering rotated text,
452 // in which case it is difficult to estimate an appropriate
453 // region for readback, so we will pick a region that
454 // encompasses just the current glyph
455 remainingWidth = ginfo->width;
456 }
457 dx2 = dx1 + remainingWidth;
458
459 // estimate the height (this is another sloppy heuristic; we'll
460 // make the cached destination region tall enough to encompass most
461 // glyphs that are small enough to fit in the glyph cache, and then
462 // we add a little something extra to account for descenders
463 dy2 = dy1 + D3DTR_CACHE_CELL_HEIGHT + 2;
464
465 // REMIND: make sure we flush any pending primitives that are
466 // dependent on the current contents of the cached dest
467 d3dc->FlushVertexQueue();
468
469 RETURN_STATUS_IF_NULL(dstOps->pResource, E_FAIL);
470 RETURN_STATUS_IF_NULL(pDst = dstOps->pResource->GetSurface(), E_FAIL);
471 res = d3dc->GetResourceManager()->
472 GetCachedDestTexture(dstOps->pResource->GetDesc()->Format,
473 &pCachedDestTexRes);
474 RETURN_STATUS_IF_FAILED(res);
475 pCachedDestSurface = pCachedDestTexRes->GetSurface();
476
477 // now dxy12 represent the "desired" destination bounds, but the
478 // StretchRect() call may fail if these fall outside the actual
479 // surface bounds; therefore, we use cxy12 to represent the
480 // clamped bounds, and dxy12 are saved for later
481 jint cx1 = (dx1 < 0) ? 0 : dx1;
482 jint cy1 = (dy1 < 0) ? 0 : dy1;
483 jint cx2 = (dx2 > dstOps->width) ? dstOps->width : dx2;
484 jint cy2 = (dy2 > dstOps->height) ? dstOps->height : dy2;
485
486 if (cx2 > cx1 && cy2 > cy1) {
487 // copy destination into cached texture tile (the upper-left
488 // corner of the destination region will be positioned at the
489 // upper-left corner (0,0) of the texture)
490 RECT srcRect = { cx1, cy1, cx2, cy2 };
491 RECT dstRect = { cx1-dx1, cy1-dy1, cx2-dx1, cy2-dy1 };
492
493 IDirect3DDevice9 *pd3dDevice = d3dc->Get3DDevice();
494 res = pd3dDevice->StretchRect(pDst, &srcRect,
495 pCachedDestSurface, &dstRect,
496 D3DTEXF_NONE);
497 }
498
499 // update the cached bounds and mark it valid
500 cachedDestBounds.x1 = dx1;
501 cachedDestBounds.y1 = dy1;
502 cachedDestBounds.x2 = dx2;
503 cachedDestBounds.y2 = dy2;
504 isCachedDestValid = JNI_TRUE;
505 }
506
507 // always update the previous glyph bounds
508 previousGlyphBounds.x1 = gx1;
509 previousGlyphBounds.y1 = gy1;
510 previousGlyphBounds.x2 = gx2;
511 previousGlyphBounds.y2 = gy2;
512
513 return res;
514 }
515
516 static HRESULT
D3DTR_DrawLCDGlyphViaCache(D3DContext * d3dc,D3DSDOps * dstOps,GlyphInfo * ginfo,jint x,jint y,jint glyphIndex,jint totalGlyphs,jboolean rgbOrder,jint contrast)517 D3DTR_DrawLCDGlyphViaCache(D3DContext *d3dc, D3DSDOps *dstOps,
518 GlyphInfo *ginfo, jint x, jint y,
519 jint glyphIndex, jint totalGlyphs,
520 jboolean rgbOrder, jint contrast)
521 {
522 HRESULT res;
523 D3DGlyphCache *pLCDGCache;
524 CacheCellInfo *cell;
525 GlyphCacheInfo *gcache;
526 jint dx1, dy1, dx2, dy2;
527 jfloat dtx1, dty1, dtx2, dty2;
528
529 J2dTraceLn(J2D_TRACE_VERBOSE, "D3DTR_DrawLCDGlyphViaCache");
530
531 // the glyph cache is initialized before this method is called
532 pLCDGCache = d3dc->GetLCDGlyphCache();
533
534 if (glyphMode != MODE_USE_CACHE_LCD) {
535 D3DTR_DisableGlyphModeState(d3dc);
536
537 res = d3dc->BeginScene(STATE_TEXTUREOP);
538 RETURN_STATUS_IF_FAILED(res);
539
540 pLCDGCache->CheckGlyphCacheByteOrder(rgbOrder);
541
542 res = D3DTR_EnableLCDGlyphModeState(d3dc, dstOps, JNI_TRUE, contrast);
543 RETURN_STATUS_IF_FAILED(res);
544
545 glyphMode = MODE_USE_CACHE_LCD;
546 }
547
548 gcache = pLCDGCache->GetGlyphCache();
549 cell = AccelGlyphCache_GetCellInfoForCache(ginfo, gcache);
550 if (cell == NULL) {
551 // attempt to add glyph to accelerated glyph cache
552 res = pLCDGCache->AddGlyph(ginfo);
553 RETURN_STATUS_IF_FAILED(res);
554
555 // we'll just no-op in the rare case that the cell is NULL
556 cell = AccelGlyphCache_GetCellInfoForCache(ginfo, gcache);
557 RETURN_STATUS_IF_NULL(cell, E_FAIL);
558 }
559
560 cell->timesRendered++;
561
562 // location of the glyph in the destination's coordinate space
563 dx1 = x;
564 dy1 = y;
565 dx2 = dx1 + ginfo->width;
566 dy2 = dy1 + ginfo->height;
567
568 // copy destination into second cached texture, if necessary
569 D3DTR_UpdateCachedDestination(d3dc,
570 dstOps, ginfo,
571 dx1, dy1,
572 dx2, dy2,
573 dx1 + cell->leftOff, // adjusted dx1
574 dx2 + cell->rightOff, // adjusted dx2
575 glyphIndex, totalGlyphs);
576
577 // texture coordinates of the destination tile
578 dtx1 = ((jfloat)(dx1 - cachedDestBounds.x1)) / D3DTR_CACHED_DEST_WIDTH;
579 dty1 = ((jfloat)(dy1 - cachedDestBounds.y1)) / D3DTR_CACHED_DEST_HEIGHT;
580 dtx2 = ((jfloat)(dx2 - cachedDestBounds.x1)) / D3DTR_CACHED_DEST_WIDTH;
581 dty2 = ((jfloat)(dy2 - cachedDestBounds.y1)) / D3DTR_CACHED_DEST_HEIGHT;
582
583 // render composed texture to the destination surface
584 return d3dc->pVCacher->DrawTexture((jfloat)dx1, (jfloat)dy1,
585 (jfloat)dx2, (jfloat)dy2,
586 cell->tx1, cell->ty1,
587 cell->tx2, cell->ty2,
588 dtx1, dty1, dtx2, dty2);
589 }
590
591 static HRESULT
D3DTR_DrawGrayscaleGlyphNoCache(D3DContext * d3dc,GlyphInfo * ginfo,jint x,jint y)592 D3DTR_DrawGrayscaleGlyphNoCache(D3DContext *d3dc,
593 GlyphInfo *ginfo, jint x, jint y)
594 {
595 jint tw, th;
596 jint sx, sy, sw, sh;
597 jint x0;
598 jint w = ginfo->width;
599 jint h = ginfo->height;
600 HRESULT res = S_OK;
601
602 J2dTraceLn(J2D_TRACE_VERBOSE, "D3DTR_DrawGrayscaleGlyphNoCache");
603
604 if (glyphMode != MODE_NO_CACHE_GRAY) {
605 D3DTR_DisableGlyphModeState(d3dc);
606
607 res = d3dc->BeginScene(STATE_MASKOP);
608 RETURN_STATUS_IF_FAILED(res);
609
610 glyphMode = MODE_NO_CACHE_GRAY;
611 }
612
613 x0 = x;
614 tw = D3D_MASK_CACHE_TILE_WIDTH;
615 th = D3D_MASK_CACHE_TILE_HEIGHT;
616
617 for (sy = 0; sy < h; sy += th, y += th) {
618 x = x0;
619 sh = ((sy + th) > h) ? (h - sy) : th;
620
621 for (sx = 0; sx < w; sx += tw, x += tw) {
622 sw = ((sx + tw) > w) ? (w - sx) : tw;
623
624 res = d3dc->GetMaskCache()->AddMaskQuad(sx, sy, x, y, sw, sh,
625 w, ginfo->image);
626 }
627 }
628
629 return res;
630 }
631
632 static HRESULT
D3DTR_DrawLCDGlyphNoCache(D3DContext * d3dc,D3DSDOps * dstOps,GlyphInfo * ginfo,jint x,jint y,jint rowBytesOffset,jboolean rgbOrder,jint contrast)633 D3DTR_DrawLCDGlyphNoCache(D3DContext *d3dc, D3DSDOps *dstOps,
634 GlyphInfo *ginfo, jint x, jint y,
635 jint rowBytesOffset,
636 jboolean rgbOrder, jint contrast)
637 {
638 jfloat tx1, ty1, tx2, ty2;
639 jfloat dx1, dy1, dx2, dy2;
640 jfloat dtx1, dty1, dtx2, dty2;
641 jint tw, th;
642 jint sx, sy, sw, sh;
643 jint cx1, cy1, cx2, cy2;
644 jint x0;
645 jint w = ginfo->width;
646 jint h = ginfo->height;
647 TileFormat tileFormat = rgbOrder ? TILEFMT_3BYTE_RGB : TILEFMT_3BYTE_BGR;
648
649 IDirect3DDevice9 *pd3dDevice = d3dc->Get3DDevice();
650 D3DResource *pBlitTextureRes, *pCachedDestTextureRes;
651 IDirect3DTexture9 *pBlitTexture;
652 IDirect3DSurface9 *pCachedDestSurface, *pDst;
653 HRESULT res;
654
655 J2dTraceLn(J2D_TRACE_VERBOSE, "D3DTR_DrawLCDGlyphNoCache");
656
657 RETURN_STATUS_IF_NULL(dstOps->pResource, E_FAIL);
658 RETURN_STATUS_IF_NULL(pDst = dstOps->pResource->GetSurface(), E_FAIL);
659
660 res = d3dc->GetResourceManager()->GetBlitTexture(&pBlitTextureRes);
661 RETURN_STATUS_IF_FAILED(res);
662
663 res = d3dc->GetResourceManager()->
664 GetCachedDestTexture(dstOps->pResource->GetDesc()->Format,
665 &pCachedDestTextureRes);
666 RETURN_STATUS_IF_FAILED(res);
667
668 pBlitTexture = pBlitTextureRes->GetTexture();
669 pCachedDestSurface = pCachedDestTextureRes->GetSurface();
670
671 if (glyphMode != MODE_NO_CACHE_LCD) {
672 D3DTR_DisableGlyphModeState(d3dc);
673
674 res = d3dc->BeginScene(STATE_TEXTUREOP);
675 RETURN_STATUS_IF_FAILED(res);
676 res = D3DTR_EnableLCDGlyphModeState(d3dc,dstOps, JNI_FALSE, contrast);
677 RETURN_STATUS_IF_FAILED(res);
678
679 glyphMode = MODE_NO_CACHE_LCD;
680 }
681
682 x0 = x;
683 tx1 = 0.0f;
684 ty1 = 0.0f;
685 dtx1 = 0.0f;
686 dty1 = 0.0f;
687 tw = D3DTR_NOCACHE_TILE_SIZE;
688 th = D3DTR_NOCACHE_TILE_SIZE;
689
690 for (sy = 0; sy < h; sy += th, y += th) {
691 x = x0;
692 sh = ((sy + th) > h) ? (h - sy) : th;
693
694 for (sx = 0; sx < w; sx += tw, x += tw) {
695 sw = ((sx + tw) > w) ? (w - sx) : tw;
696
697 // calculate the bounds of the tile to be copied from the
698 // destination into the cached tile
699 cx1 = x;
700 cy1 = y;
701 cx2 = cx1 + sw;
702 cy2 = cy1 + sh;
703
704 // need to clamp to the destination bounds, otherwise the
705 // StretchRect() call may fail
706 if (cx1 < 0) cx1 = 0;
707 if (cy1 < 0) cy1 = 0;
708 if (cx2 > dstOps->width) cx2 = dstOps->width;
709 if (cy2 > dstOps->height) cy2 = dstOps->height;
710
711 if (cx2 > cx1 && cy2 > cy1) {
712 // copy LCD mask into glyph texture tile
713 d3dc->UploadTileToTexture(pBlitTextureRes,
714 ginfo->image+rowBytesOffset,
715 0, 0, sx, sy, sw, sh,
716 ginfo->rowBytes, tileFormat);
717
718 // update the lower-right glyph texture coordinates
719 tx2 = ((jfloat)sw) / D3DC_BLIT_TILE_SIZE;
720 ty2 = ((jfloat)sh) / D3DC_BLIT_TILE_SIZE;
721
722 // calculate the actual destination vertices
723 dx1 = (jfloat)x;
724 dy1 = (jfloat)y;
725 dx2 = dx1 + sw;
726 dy2 = dy1 + sh;
727
728 // copy destination into cached texture tile (the upper-left
729 // corner of the destination region will be positioned at the
730 // upper-left corner (0,0) of the texture)
731 RECT srcRect = { cx1, cy1, cx2, cy2 };
732 RECT dstRect = { cx1-x, cy1-y, cx2-x, cy2-y };
733 pd3dDevice->StretchRect(pDst, &srcRect,
734 pCachedDestSurface,
735 &dstRect,
736 D3DTEXF_NONE);
737
738 // update the remaining destination texture coordinates
739 dtx2 = ((jfloat)sw) / D3DTR_CACHED_DEST_WIDTH;
740 dty2 = ((jfloat)sh) / D3DTR_CACHED_DEST_HEIGHT;
741
742 // render composed texture to the destination surface
743 res = d3dc->pVCacher->DrawTexture( dx1, dy1, dx2, dy2,
744 tx1, ty1, tx2, ty2,
745 dtx1, dty1, dtx2, dty2);
746
747 // unfortunately we need to flush after each tile
748 d3dc->FlushVertexQueue();
749 }
750 }
751 }
752
753 return res;
754 }
755
756 // see DrawGlyphList.c for more on this macro...
757 #define FLOOR_ASSIGN(l, r) \
758 if ((r)<0) (l) = ((int)floor(r)); else (l) = ((int)(r))
759
760 HRESULT
D3DTR_DrawGlyphList(D3DContext * d3dc,D3DSDOps * dstOps,jint totalGlyphs,jboolean usePositions,jboolean subPixPos,jboolean rgbOrder,jint lcdContrast,jfloat glyphListOrigX,jfloat glyphListOrigY,unsigned char * images,unsigned char * positions)761 D3DTR_DrawGlyphList(D3DContext *d3dc, D3DSDOps *dstOps,
762 jint totalGlyphs, jboolean usePositions,
763 jboolean subPixPos, jboolean rgbOrder, jint lcdContrast,
764 jfloat glyphListOrigX, jfloat glyphListOrigY,
765 unsigned char *images, unsigned char *positions)
766 {
767 int glyphCounter;
768 HRESULT res = S_OK;
769 J2dTraceLn(J2D_TRACE_INFO, "D3DTR_DrawGlyphList");
770
771 RETURN_STATUS_IF_NULL(d3dc, E_FAIL);
772 RETURN_STATUS_IF_NULL(d3dc->Get3DDevice(), E_FAIL);
773 RETURN_STATUS_IF_NULL(dstOps, E_FAIL);
774 RETURN_STATUS_IF_NULL(images, E_FAIL);
775 if (usePositions) {
776 RETURN_STATUS_IF_NULL(positions, E_FAIL);
777 }
778
779 glyphMode = MODE_NOT_INITED;
780 isCachedDestValid = JNI_FALSE;
781
782 for (glyphCounter = 0; glyphCounter < totalGlyphs; glyphCounter++) {
783 jint x, y;
784 jfloat glyphx, glyphy;
785 jboolean grayscale;
786 GlyphInfo *ginfo = (GlyphInfo *)jlong_to_ptr(NEXT_LONG(images));
787
788 if (ginfo == NULL) {
789 // this shouldn't happen, but if it does we'll just break out...
790 J2dRlsTraceLn(J2D_TRACE_ERROR,
791 "D3DTR_DrawGlyphList: glyph info is null");
792 break;
793 }
794
795 grayscale = (ginfo->rowBytes == ginfo->width);
796
797 if (usePositions) {
798 jfloat posx = NEXT_FLOAT(positions);
799 jfloat posy = NEXT_FLOAT(positions);
800 glyphx = glyphListOrigX + posx + ginfo->topLeftX;
801 glyphy = glyphListOrigY + posy + ginfo->topLeftY;
802 FLOOR_ASSIGN(x, glyphx);
803 FLOOR_ASSIGN(y, glyphy);
804 } else {
805 glyphx = glyphListOrigX + ginfo->topLeftX;
806 glyphy = glyphListOrigY + ginfo->topLeftY;
807 FLOOR_ASSIGN(x, glyphx);
808 FLOOR_ASSIGN(y, glyphy);
809 glyphListOrigX += ginfo->advanceX;
810 glyphListOrigY += ginfo->advanceY;
811 }
812
813 if (ginfo->image == NULL) {
814 continue;
815 }
816
817 if (grayscale) {
818 // grayscale or monochrome glyph data
819 if (ginfo->width <= D3DTR_CACHE_CELL_WIDTH &&
820 ginfo->height <= D3DTR_CACHE_CELL_HEIGHT &&
821 SUCCEEDED(d3dc->InitGrayscaleGlyphCache()))
822 {
823 res = D3DTR_DrawGrayscaleGlyphViaCache(d3dc, ginfo, x, y);
824 } else {
825 res = D3DTR_DrawGrayscaleGlyphNoCache(d3dc, ginfo, x, y);
826 }
827 } else {
828 // LCD-optimized glyph data
829 jint rowBytesOffset = 0;
830
831 if (subPixPos) {
832 jint frac = (jint)((glyphx - x) * 3);
833 if (frac != 0) {
834 rowBytesOffset = 3 - frac;
835 x += 1;
836 }
837 }
838
839 if (rowBytesOffset == 0 &&
840 ginfo->width <= D3DTR_CACHE_CELL_WIDTH &&
841 ginfo->height <= D3DTR_CACHE_CELL_HEIGHT &&
842 SUCCEEDED(d3dc->InitLCDGlyphCache()))
843 {
844 res = D3DTR_DrawLCDGlyphViaCache(d3dc, dstOps,
845 ginfo, x, y,
846 glyphCounter, totalGlyphs,
847 rgbOrder, lcdContrast);
848 } else {
849 res = D3DTR_DrawLCDGlyphNoCache(d3dc, dstOps,
850 ginfo, x, y,
851 rowBytesOffset,
852 rgbOrder, lcdContrast);
853 }
854 }
855
856 if (FAILED(res)) {
857 break;
858 }
859 }
860
861 D3DTR_DisableGlyphModeState(d3dc);
862 return res;
863 }
864
865 JNIEXPORT void JNICALL
Java_sun_java2d_d3d_D3DTextRenderer_drawGlyphList(JNIEnv * env,jobject self,jint numGlyphs,jboolean usePositions,jboolean subPixPos,jboolean rgbOrder,jint lcdContrast,jfloat glyphListOrigX,jfloat glyphListOrigY,jlongArray imgArray,jfloatArray posArray)866 Java_sun_java2d_d3d_D3DTextRenderer_drawGlyphList
867 (JNIEnv *env, jobject self,
868 jint numGlyphs, jboolean usePositions,
869 jboolean subPixPos, jboolean rgbOrder, jint lcdContrast,
870 jfloat glyphListOrigX, jfloat glyphListOrigY,
871 jlongArray imgArray, jfloatArray posArray)
872 {
873 unsigned char *images;
874
875 J2dTraceLn(J2D_TRACE_INFO, "D3DTextRenderer_drawGlyphList");
876
877 images = (unsigned char *)
878 env->GetPrimitiveArrayCritical(imgArray, NULL);
879 if (images != NULL) {
880 D3DContext *d3dc = D3DRQ_GetCurrentContext();
881 D3DSDOps *dstOps = D3DRQ_GetCurrentDestination();
882
883 if (usePositions) {
884 unsigned char *positions = (unsigned char *)
885 env->GetPrimitiveArrayCritical(posArray, NULL);
886 if (positions != NULL) {
887 D3DTR_DrawGlyphList(d3dc, dstOps,
888 numGlyphs, usePositions,
889 subPixPos, rgbOrder, lcdContrast,
890 glyphListOrigX, glyphListOrigY,
891 images, positions);
892 env->ReleasePrimitiveArrayCritical(posArray,
893 positions, JNI_ABORT);
894 }
895 } else {
896 D3DTR_DrawGlyphList(d3dc, dstOps,
897 numGlyphs, usePositions,
898 subPixPos, rgbOrder, lcdContrast,
899 glyphListOrigX, glyphListOrigY,
900 images, NULL);
901 }
902
903 // reset current state, and ensure rendering is flushed to dest
904 if (d3dc != NULL) {
905 d3dc->FlushVertexQueue();
906 }
907
908 env->ReleasePrimitiveArrayCritical(imgArray,
909 images, JNI_ABORT);
910 }
911 }
912