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