1/*
2 * Copyright (c) 2007, 2008, 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 "D3DPipeline.h"
27#include "D3DVertexCacher.h"
28#include "D3DPaints.h"
29
30#include "math.h"
31
32// non-texturized macros
33
34#define ADD_VERTEX_XYC(X, Y, VCOLOR) \
35do { \
36    vertices[firstUnusedVertex].x = (X); \
37    vertices[firstUnusedVertex].y = (Y); \
38    vertices[firstUnusedVertex].color = (DWORD)(VCOLOR); \
39    firstUnusedVertex++; \
40} while (0)
41
42#define ADD_LINE_XYC(X1, Y1, X2, Y2, VCOLOR) \
43do { \
44    ADD_VERTEX_XYC(X1, Y1, VCOLOR); \
45    ADD_VERTEX_XYC(X2, Y2, VCOLOR); \
46    batches[currentBatch].pNum++;   \
47} while (0)
48
49#define ADD_LINE_SEG_XYC(X, Y, VCOLOR) \
50do { \
51    ADD_VERTEX_XYC(X, Y, VCOLOR); \
52    batches[currentBatch].pNum++;   \
53} while (0)
54
55#define ADD_TRIANGLE_XYC(X1, Y1, X2, Y2, X3, Y3, VCOLOR) \
56do { \
57    ADD_VERTEX_XYC(X1, Y1, VCOLOR); \
58    ADD_VERTEX_XYC(X2, Y2, VCOLOR); \
59    ADD_VERTEX_XYC(X3, Y3, VCOLOR); \
60    batches[currentBatch].pNum++;   \
61} while (0)
62
63// texturized macros
64
65#define ADD_VERTEX_XYUVC(X, Y, U1, V1, VCOLOR) \
66do { \
67    vertices[firstUnusedVertex].x = (X); \
68    vertices[firstUnusedVertex].y = (Y); \
69    vertices[firstUnusedVertex].tu1 = (U1); \
70    vertices[firstUnusedVertex].tv1 = (V1); \
71    vertices[firstUnusedVertex].color = (DWORD)(VCOLOR); \
72    firstUnusedVertex++; \
73} while (0)
74
75#define ADD_VERTEX_XYUVUVC(X, Y, U1, V1, U2, V2, VCOLOR) \
76do { \
77    vertices[firstUnusedVertex].tu2 = (U2); \
78    vertices[firstUnusedVertex].tv2 = (V2); \
79    ADD_VERTEX_XYUVC(X, Y, U1, V1, VCOLOR); \
80} while (0)
81
82#define ADD_TRIANGLE_XYUVC(X1, Y1, X2, Y2, X3, Y3,         \
83                           U1, V1, U2, V2, U3, V3, VCOLOR) \
84do { \
85    ADD_VERTEX_XYUVC(X1, Y1, U1, V1, VCOLOR); \
86    ADD_VERTEX_XYUVC(X2, Y2, U2, V2, VCOLOR); \
87    ADD_VERTEX_XYUVC(X3, Y3, U3, V3, VCOLOR); \
88    batches[currentBatch].pNum++;   \
89} while (0)
90
91#define ADD_TRIANGLE_XYUVUVC(X1, Y1, X2, Y2, X3, Y3,       \
92                             U11, V11, U12, V12, U13, V13, \
93                             U21, V21, U22, V22, U23, V23, \
94                             VCOLOR)                       \
95do { \
96    ADD_VERTEX_XYUVUVC(X1, Y1, U11, V11, U21, V21, VCOLOR); \
97    ADD_VERTEX_XYUVUVC(X2, Y2, U12, V12, U22, V22, VCOLOR); \
98    ADD_VERTEX_XYUVUVC(X3, Y3, U13, V13, U23, V23, VCOLOR); \
99    batches[currentBatch].pNum++;   \
100} while (0)
101
102// These are fudge factors for rendering lines found by experimenting.
103// They are used to tweak the geometry such that the rendering (mostly) matches
104// our software rendering on most hardware. The main goal was to pick the
105// numbers such that the beginning and ending pixels of lines match.
106#define LINE_FUDGE
107// fudge factors
108#ifdef LINE_FUDGE
109
110// Horiz/vertical
111#define HV_FF1 ( 0.0f)
112#define HV_FF2 ( 0.51f)
113// For the record: value below (or larger) is required for Intel 855, but
114// breaks Nvidia, ATI and Intel 965, and since the pipeline is disabled on
115// 855 anyway we'll use 0.51f.
116//#define HV_FF2 ( 0.5315f)
117#define HV_FF3 (-0.2f)
118// single pixel
119#define SP_FF4 ( 0.3f)
120
121// diagonal, down
122#define DD_FX1 (-0.1f)
123#define DD_FY1 (-0.25f)
124#define DD_FX2 ( 0.2f)
125#define DD_FY2 ( 0.304f)
126// For the record: with this value diagonal-down lines with Texture paint
127// are a bit off on all chipsets but Intel 965. So instead we'll use
128// .304f which makes it better for the rest, but at a price of a bit
129// of pixel/texel shifting on 965G
130//#define DD_FY2 ( 0.4f)
131// diagonal, up
132#define DU_FX1 (-0.1f)
133#define DU_FY1 ( 0.4f)
134#define DU_FX2 ( 0.3f)
135#define DU_FY2 (-0.3f)
136
137#else
138
139#define HV_FF1 (0.0f)
140#define HV_FF2 (0.0f)
141#define HV_FF3 (0.0f)
142#define SP_FF4 (0.0f)
143
144#define DD_FX1 (0.0f)
145#define DD_FY1 (0.0f)
146#define DD_FX2 (0.0f)
147#define DD_FY2 (0.0f)
148#define DU_FX1 (0.0f)
149#define DU_FY1 (0.0f)
150#define DU_FX2 (0.0f)
151#define DU_FY2 (0.0f)
152
153#endif
154
155HRESULT
156D3DVertexCacher::CreateInstance(D3DContext *pCtx, D3DVertexCacher **ppVC)
157{
158    HRESULT res;
159
160    J2dTraceLn(J2D_TRACE_INFO, "D3DVertexCacher::CreateInstance");
161
162    *ppVC = new D3DVertexCacher();
163    if (FAILED(res = (*ppVC)->Init(pCtx))) {
164        delete *ppVC;
165        *ppVC = NULL;
166    }
167    return res;
168}
169
170D3DVertexCacher::D3DVertexCacher()
171{
172    lpD3DDevice = NULL;
173    lpD3DVertexBuffer = NULL;
174}
175
176HRESULT
177D3DVertexCacher::Init(D3DContext *pCtx)
178{
179    D3DCAPS9 caps;
180
181    RETURN_STATUS_IF_NULL(pCtx, E_FAIL);
182
183    ReleaseDefPoolResources();
184
185    this->pCtx = pCtx;
186
187    firstPendingBatch = 0;
188    firstPendingVertex = 0;
189    firstUnusedVertex = 0;
190    currentBatch = 0;
191    ZeroMemory(vertices, sizeof(vertices));
192    ZeroMemory(batches, sizeof(batches));
193
194    lpD3DDevice = pCtx->Get3DDevice();
195    RETURN_STATUS_IF_NULL(lpD3DDevice, E_FAIL);
196
197    ZeroMemory(&caps, sizeof(caps));
198    lpD3DDevice->GetDeviceCaps(&caps);
199
200    D3DPOOL pool = (caps.DeviceType == D3DDEVTYPE_HAL) ?
201            D3DPOOL_DEFAULT : D3DPOOL_SYSTEMMEM;
202    // usage depends on whether we use hw or sw vertex processing
203    HRESULT res =
204        lpD3DDevice->CreateVertexBuffer(MAX_BATCH_SIZE*sizeof(J2DLVERTEX),
205            D3DUSAGE_DYNAMIC|D3DUSAGE_WRITEONLY, D3DFVF_J2DLVERTEX,
206            pool, &lpD3DVertexBuffer, NULL);
207    RETURN_STATUS_IF_FAILED(res);
208
209    res = lpD3DDevice->SetStreamSource(0, lpD3DVertexBuffer, 0,
210                                       sizeof(J2DLVERTEX));
211    RETURN_STATUS_IF_FAILED(res);
212
213    lpD3DDevice->SetFVF(D3DFVF_J2DLVERTEX);
214    return res;
215}
216
217void
218D3DVertexCacher::ReleaseDefPoolResources()
219{
220    SAFE_RELEASE(lpD3DVertexBuffer);
221    pCtx = NULL;
222}
223
224HRESULT D3DVertexCacher::DrawLine(int x1, int y1, int x2, int y2)
225{
226    HRESULT res;
227    if (SUCCEEDED(res = EnsureCapacity(D3DPT_LINELIST, 1*2))) {
228        float fx1, fy1, fx2, fy2;
229        if (y1 == y2) {
230            // horizontal
231            fy1  = (float)y1+HV_FF1;
232            fy2  = fy1;
233
234            if (x1 > x2) {
235                fx1 = (float)x2+HV_FF3;
236                fx2 = (float)x1+HV_FF2;
237            } else if (x1 < x2) {
238                fx1 = (float)x1+HV_FF3;
239                fx2 = (float)x2+HV_FF2;
240            } else {
241                // single point, offset a little so that a single
242                // pixel is rendered
243                fx1 = (float)x1-SP_FF4;
244                fy1 = (float)y1-SP_FF4;
245                fx2 = (float)x2+SP_FF4;
246                fy2 = (float)y2+SP_FF4;
247            }
248        } else if (x1 == x2) {
249            // vertical
250            fx1  = (float)x1+HV_FF1;
251            fx2  = fx1;
252            if (y1 > y2) {
253                fy1 = (float)y2+HV_FF3;
254                fy2 = (float)y1+HV_FF2;
255            } else {
256                fy1 = (float)y1+HV_FF3;
257                fy2 = (float)y2+HV_FF2;
258            }
259        } else {
260            // diagonal
261            if (x1 > x2 && y1 > y2) {
262                // ^
263                //  \ case -> inverse
264                fx1 = (float)x2;
265                fy1 = (float)y2;
266                fx2 = (float)x1;
267                fy2 = (float)y1;
268            } else if (x1 > x2 && y2 > y1) {
269                //  /
270                // v  case - inverse
271                fx1 = (float)x2;
272                fy1 = (float)y2;
273                fx2 = (float)x1;
274                fy2 = (float)y1;
275            } else {
276                // \      ^
277                //  v or /  - leave as is
278                fx1 = (float)x1;
279                fy1 = (float)y1;
280                fx2 = (float)x2;
281                fy2 = (float)y2;
282            }
283
284            if (fx2 > fx1 && fy2 > fy1) {
285                // \
286                //  v
287                fx1 += DD_FX1;
288                fy1 += DD_FY1;
289                fx2 += DD_FX2;
290                fy2 += DD_FY2;
291            } else {
292                //   ^
293                //  /
294                fx1 += DU_FX1;
295                fy1 += DU_FY1;
296                fx2 += DU_FX2;
297                fy2 += DU_FY2;
298            }
299        }
300        ADD_LINE_XYC(fx1, fy1, fx2, fy2, color);
301    }
302    return res;
303}
304
305HRESULT
306D3DVertexCacher::DrawPoly(jint nPoints, jboolean isClosed,
307                          jint transX, jint transY,
308                          jint *xPoints, jint *yPoints)
309{
310    HRESULT res;
311    jfloat mx = (jfloat)xPoints[0];
312    jfloat my = (jfloat)yPoints[0];
313    jboolean isEmpty = TRUE;
314
315    if (nPoints == 0) {
316        return S_OK;
317    }
318
319    if (isClosed &&
320        xPoints[nPoints - 1] == xPoints[0] &&
321        yPoints[nPoints - 1] == yPoints[0])
322    {
323        isClosed = FALSE;
324    }
325
326    // npoints is exactly the number of vertices we need,
327    // possibly plus one (if the path is closed)
328    UINT reqVerts = nPoints * 1;
329    int i = 0;
330    do {
331        // leave room for one possible additional closing point
332        UINT vertsInBatch = min(MAX_BATCH_SIZE-1, max(2, reqVerts));
333        if (SUCCEEDED(res = EnsureCapacity(D3DPT_LINESTRIP, vertsInBatch+1))) {
334            reqVerts -= vertsInBatch;
335            do {
336                jfloat x = (jfloat)xPoints[i];
337                jfloat y = (jfloat)yPoints[i];
338
339                isEmpty = isEmpty && (x == mx && y == my);
340
341                ADD_LINE_SEG_XYC(x + transX, y + transY, color);
342                i++;
343                vertsInBatch--;
344            } while (vertsInBatch > 0);
345            // include the last point from the current batch into the next
346            if (reqVerts > 0) {
347                i--;
348                reqVerts++;
349                // loop continues
350            } else if (isClosed && !isEmpty) {
351                // if this was the last batch, see if the closing point is needed;
352                // note that we have left the room for it
353                ADD_LINE_SEG_XYC(mx + transX, my + transY, color);
354                // for clarity, the loop is ended anyway
355                break;
356            } else if (isEmpty || !isClosed) {
357                // - either we went nowhere, then change the last point
358                // so that a single pixel is rendered
359                // - or it's not empty and not closed - add another
360                // point because on some boards the last point is not rendered
361                mx = xPoints[nPoints-1] + transX +SP_FF4;
362                my = yPoints[nPoints-1] + transY +SP_FF4;
363                ADD_LINE_SEG_XYC(mx, my, color);
364                // for clarity
365                break;
366            }
367        }
368    } while (reqVerts > 0 && SUCCEEDED(res));
369
370    return res;
371}
372
373HRESULT
374D3DVertexCacher::DrawScanlines(jint scanlineCount, jint *scanlines)
375{
376    HRESULT res;
377    float x1, x2, y;
378    UINT reqVerts = scanlineCount*2/*vertices per line*/;
379
380    if (scanlineCount == 0) {
381        return S_OK;
382    }
383
384    do {
385        UINT vertsInBatch = min(2*(MAX_BATCH_SIZE/2), reqVerts);
386        if (SUCCEEDED(res = EnsureCapacity(D3DPT_LINELIST, vertsInBatch))) {
387            reqVerts -= vertsInBatch;
388            do {
389                x1 = ((float)*(scanlines++)) +HV_FF3;
390                x2 = ((float)*(scanlines++)) +HV_FF2;
391                y  = ((float)*(scanlines++)) +HV_FF1;
392                ADD_LINE_XYC(x1, y, x2, y, color);
393                vertsInBatch -= 2;
394            } while (vertsInBatch > 0);
395        }
396    } while (reqVerts > 0 && SUCCEEDED(res));
397    return res;
398}
399
400HRESULT
401D3DVertexCacher::FillSpans(jint spanCount, jint *spans)
402{
403    HRESULT res;
404    float x1, y1, x2, y2;
405    UINT reqVerts = spanCount*2*3/*vertices per span: two triangles*/;
406
407    if (spanCount == 0) {
408        return S_OK;
409    }
410
411    do {
412        UINT vertsInBatch = min(6*(MAX_BATCH_SIZE/6), reqVerts);
413        if (SUCCEEDED(res = EnsureCapacity(D3DPT_TRIANGLELIST, vertsInBatch))) {
414            reqVerts -= vertsInBatch;
415            do {
416                x1 = ((float)*(spans++));
417                y1 = ((float)*(spans++));
418                x2 = ((float)*(spans++));
419                y2 = ((float)*(spans++));
420
421                ADD_TRIANGLE_XYC(x1, y1, x2, y1, x1, y2, color);
422                ADD_TRIANGLE_XYC(x1, y2, x2, y1, x2, y2, color);
423                vertsInBatch -= 6;
424            } while (vertsInBatch > 0);
425        }
426    } while (reqVerts > 0 && SUCCEEDED(res));
427
428    return res;
429}
430
431HRESULT D3DVertexCacher::DrawRect(int x1, int y1, int x2, int y2)
432{
433    HRESULT res;
434
435    if ((x2 - x1) < 2 || (y2 - y1) < 2) {
436        return FillRect(x1, y1, x2+1, y2+1);
437    }
438    if (SUCCEEDED(res = EnsureCapacity(D3DPT_LINELIST, 4*2))) {
439
440        float fx1 = (float)x1;
441        float fy1 = (float)y1;
442        float fx2 = (float)x2;
443        float fy2 = (float)y2;
444
445        // horiz: top left - top right
446        ADD_LINE_XYC(fx1+HV_FF3, fy1+HV_FF1, fx2-1.0f+HV_FF2, fy1+HV_FF1,color);
447        // horiz: bottom left - bottom right
448        ADD_LINE_XYC(fx1+1.0f+HV_FF3, fy2+HV_FF1, fx2+HV_FF2, fy2+HV_FF1,color);
449        // vert : top right - bottom right
450        ADD_LINE_XYC(fx2+HV_FF1, fy1+HV_FF3, fx2+HV_FF1, fy2-1.0f+HV_FF2,color);
451        // vert : top left - bottom left
452        ADD_LINE_XYC(fx1+HV_FF1, fy1+1.0f+HV_FF3, fx1+HV_FF1, fy2+HV_FF2,color);
453    }
454    return res;
455}
456
457HRESULT D3DVertexCacher::FillRect(int x1, int y1, int x2, int y2)
458{
459    HRESULT res;
460    if (SUCCEEDED(res = EnsureCapacity(D3DPT_TRIANGLELIST, 2*3))) {
461        float fx1 = (float)x1;
462        float fy1 = (float)y1;
463        float fx2 = (float)x2;
464        float fy2 = (float)y2;
465        ADD_TRIANGLE_XYUVC(fx1, fy1, fx2, fy1, fx1, fy2,
466                           0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f,
467                           color);
468        ADD_TRIANGLE_XYUVC(fx1, fy2, fx2, fy1, fx2, fy2,
469                           0.0f, 1.0f, 1.0f, 0.0f, 1.0f, 1.0f,
470                           color);
471    }
472    return res;
473}
474
475HRESULT D3DVertexCacher::FillParallelogram(float fx11, float fy11,
476                                           float dx21, float dy21,
477                                           float dx12, float dy12)
478{
479    HRESULT res;
480    if (SUCCEEDED(res = EnsureCapacity(D3DPT_TRIANGLELIST, 2*3))) {
481        // correct texel to pixel mapping; see D3DContext::SetTransform()
482        // for non-id tx case
483        if (pCtx->IsIdentityTx()) {
484            fx11 -= 0.5f;
485            fy11 -= 0.5f;
486        }
487        dx21 += fx11;
488        dy21 += fy11;
489        float fx22 = dx21 + dx12;
490        float fy22 = dy21 + dy12;
491        dx12 += fx11;
492        dy12 += fy11;
493
494        ADD_TRIANGLE_XYUVC(fx11, fy11, dx21, dy21, dx12, dy12,
495                           0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f,
496                           color);
497        ADD_TRIANGLE_XYUVC(dx12, dy12, dx21, dy21, fx22, fy22,
498                           0.0f, 1.0f, 1.0f, 0.0f, 1.0f, 1.0f,
499                           color);
500    }
501    return res;
502}
503
504#define ADJUST_PGRAM(V, DV, DIM) \
505    do { \
506        if ((DV) >= 0) { \
507            (DIM) += (DV); \
508        } else { \
509            (DIM) -= (DV); \
510            (V) += (DV); \
511        } \
512    } while (0)
513
514// Invert the following transform:
515// DeltaT(0, 0) == (0,       0)
516// DeltaT(1, 0) == (DX1,     DY1)
517// DeltaT(0, 1) == (DX2,     DY2)
518// DeltaT(1, 1) == (DX1+DX2, DY1+DY2)
519// TM00 = DX1,   TM01 = DX2,   (TM02 = X11)
520// TM10 = DY1,   TM11 = DY2,   (TM12 = Y11)
521// Determinant = TM00*TM11 - TM01*TM10
522//             =  DX1*DY2  -  DX2*DY1
523// Inverse is:
524// IM00 =  TM11/det,   IM01 = -TM01/det
525// IM10 = -TM10/det,   IM11 =  TM00/det
526// IM02 = (TM01 * TM12 - TM11 * TM02) / det,
527// IM12 = (TM10 * TM02 - TM00 * TM12) / det,
528
529#define DECLARE_MATRIX(MAT) \
530    float MAT ## 00, MAT ## 01, MAT ## 02, MAT ## 10, MAT ## 11, MAT ## 12
531
532#define GET_INVERTED_MATRIX(MAT, X11, Y11, DX1, DY1, DX2, DY2, RET_CODE) \
533    do { \
534        float det = DX1*DY2 - DX2*DY1; \
535        if (det == 0) { \
536            RET_CODE; \
537        } \
538        MAT ## 00 = DY2/det; \
539        MAT ## 01 = -DX2/det; \
540        MAT ## 10 = -DY1/det; \
541        MAT ## 11 = DX1/det; \
542        MAT ## 02 = (DX2 * Y11 - DY2 * X11) / det; \
543        MAT ## 12 = (DY1 * X11 - DX1 * Y11) / det; \
544    } while (0)
545
546#define TRANSFORM(MAT, TX, TY, X, Y) \
547    do { \
548        TX = (X) * MAT ## 00 + (Y) * MAT ## 01 + MAT ## 02; \
549        TY = (X) * MAT ## 10 + (Y) * MAT ## 11 + MAT ## 12; \
550    } while (0)
551
552HRESULT D3DVertexCacher::FillParallelogramAA(float fx11, float fy11,
553                                             float dx21, float dy21,
554                                             float dx12, float dy12)
555{
556    HRESULT res;
557    DECLARE_MATRIX(om);
558
559    GET_INVERTED_MATRIX(om, fx11, fy11, dx21, dy21, dx12, dy12,
560                        return D3D_OK);
561
562    if (SUCCEEDED(res = EnsureCapacity(D3DPT_TRIANGLELIST, 2*3))) {
563        float px = fx11, py = fy11;
564        float pw = 0.0f, ph = 0.0f;
565        ADJUST_PGRAM(px, dx21, pw);
566        ADJUST_PGRAM(py, dy21, ph);
567        ADJUST_PGRAM(px, dx12, pw);
568        ADJUST_PGRAM(py, dy12, ph);
569        float px1 = floor(px);
570        float py1 = floor(py);
571        float px2 = ceil(px + pw);
572        float py2 = ceil(py + ph);
573        float u11, v11, u12, v12, u21, v21, u22, v22;
574        TRANSFORM(om, u11, v11, px1, py1);
575        TRANSFORM(om, u21, v21, px2, py1);
576        TRANSFORM(om, u12, v12, px1, py2);
577        TRANSFORM(om, u22, v22, px2, py2);
578        ADD_TRIANGLE_XYUVUVC(px1, py1, px2, py1, px1, py2,
579                             u11, v11, u21, v21, u12, v12,
580                             5.0, 5.0, 6.0, 5.0, 5.0, 6.0,
581                             color);
582        ADD_TRIANGLE_XYUVUVC(px1, py2, px2, py1, px2, py2,
583                             u12, v12, u21, v21, u22, v22,
584                             5.0, 6.0, 6.0, 5.0, 6.0, 6.0,
585                             color);
586    }
587    return res;
588}
589
590HRESULT D3DVertexCacher::DrawParallelogramAA(float ox11, float oy11,
591                                             float ox21, float oy21,
592                                             float ox12, float oy12,
593                                             float ix11, float iy11,
594                                             float ix21, float iy21,
595                                             float ix12, float iy12)
596{
597    HRESULT res;
598    DECLARE_MATRIX(om);
599    DECLARE_MATRIX(im);
600
601    GET_INVERTED_MATRIX(im, ix11, iy11, ix21, iy21, ix12, iy12,
602                        // inner parallelogram is degenerate
603                        // therefore it encloses no area
604                        // fill outer
605                        return FillParallelogramAA(ox11, oy11,
606                                                   ox21, oy21,
607                                                   ox12, oy12));
608    GET_INVERTED_MATRIX(om, ox11, oy11, ox21, oy21, ox12, oy12,
609                        return D3D_OK);
610
611    if (SUCCEEDED(res = EnsureCapacity(D3DPT_TRIANGLELIST, 2*3))) {
612        float ox = ox11, oy = oy11;
613        float ow = 0.0f, oh = 0.0f;
614        ADJUST_PGRAM(ox, ox21, ow);
615        ADJUST_PGRAM(oy, oy21, oh);
616        ADJUST_PGRAM(ox, ox12, ow);
617        ADJUST_PGRAM(oy, oy12, oh);
618        float ox11 = floor(ox);
619        float oy11 = floor(oy);
620        float ox22 = ceil(ox + ow);
621        float oy22 = ceil(oy + oh);
622        float ou11, ov11, ou12, ov12, ou21, ov21, ou22, ov22;
623        TRANSFORM(om, ou11, ov11, ox11, oy11);
624        TRANSFORM(om, ou21, ov21, ox22, oy11);
625        TRANSFORM(om, ou12, ov12, ox11, oy22);
626        TRANSFORM(om, ou22, ov22, ox22, oy22);
627        float iu11, iv11, iu12, iv12, iu21, iv21, iu22, iv22;
628        TRANSFORM(im, iu11, iv11, ox11, oy11);
629        TRANSFORM(im, iu21, iv21, ox22, oy11);
630        TRANSFORM(im, iu12, iv12, ox11, oy22);
631        TRANSFORM(im, iu22, iv22, ox22, oy22);
632        ADD_TRIANGLE_XYUVUVC(ox11, oy11, ox22, oy11, ox11, oy22,
633                             ou11, ov11, ou21, ov21, ou12, ov12,
634                             iu11, iv11, iu21, iv21, iu12, iv12,
635                             color);
636        ADD_TRIANGLE_XYUVUVC(ox11, oy22, ox22, oy11, ox22, oy22,
637                             ou12, ov12, ou21, ov21, ou22, ov22,
638                             iu12, iv12, iu21, iv21, iu22, iv22,
639                             color);
640    }
641    return res;
642}
643
644HRESULT
645D3DVertexCacher::DrawTexture(float x1, float y1, float x2, float y2,
646                             float u1, float v1, float u2, float v2)
647{
648    HRESULT res;
649    if (SUCCEEDED(res = EnsureCapacity(D3DPT_TRIANGLELIST, 2*3))) {
650        // correct texel to pixel mapping; see D3DContext::SetTransform()
651        // for non-id tx case
652        if (pCtx->IsIdentityTx()) {
653            x1 -= 0.5f;
654            y1 -= 0.5f;
655            x2 -= 0.5f;
656            y2 -= 0.5f;
657        }
658
659        ADD_TRIANGLE_XYUVC(x1, y1, x2, y1, x1, y2,
660                           u1, v1, u2, v1, u1, v2,
661                           color);
662        ADD_TRIANGLE_XYUVC(x1, y2, x2, y1, x2, y2,
663                           u1, v2, u2, v1, u2, v2,
664                           color);
665    }
666    return res;
667}
668
669HRESULT
670D3DVertexCacher::DrawTexture(float  x1, float  y1, float  x2, float  y2,
671                             float u11, float v11, float u12, float v12,
672                             float u21, float v21, float u22, float v22)
673{
674    HRESULT res;
675    if (SUCCEEDED(res = EnsureCapacity(D3DPT_TRIANGLELIST, 2*3))) {
676        // correct texel to pixel mapping; see D3DContext::SetTransform()
677        // for non-id tx case
678        if (pCtx->IsIdentityTx()) {
679            x1 -= 0.5f;
680            y1 -= 0.5f;
681            x2 -= 0.5f;
682            y2 -= 0.5f;
683        }
684
685        ADD_TRIANGLE_XYUVUVC(x1, y1, x2, y1, x1, y2,
686                             u11, v11, u12, v11, u11, v12,
687                             u21, v21, u22, v21, u21, v22,
688                             color);
689        ADD_TRIANGLE_XYUVUVC(x1, y2, x2, y1, x2, y2,
690                             u11, v12, u12, v11, u12, v12,
691                             u21, v22, u22, v21, u22, v22,
692                             color);
693    }
694    return res;
695}
696
697HRESULT D3DVertexCacher::Render(int actionType)
698{
699    J2DLVERTEX *lpVert;
700    HRESULT res;
701    DWORD dwLockFlags;
702    UINT pendingVertices = firstUnusedVertex - firstPendingVertex;
703
704    // nothing to render
705    if (pendingVertices == 0) {
706        if (actionType == RESET_ACTION) {
707            firstPendingBatch = 0;
708            firstPendingVertex = 0;
709            firstUnusedVertex = 0;
710            currentBatch = 0;
711        }
712        return D3D_OK;
713    }
714
715    if (firstPendingVertex == 0) {
716        // no data in the buffer yet, we don't care about
717        // vertex buffer's contents
718        dwLockFlags = D3DLOCK_DISCARD;
719    } else {
720        // append to the existing data in the vertex buffer
721        dwLockFlags = D3DLOCK_NOOVERWRITE;
722    }
723
724    if (SUCCEEDED(res =
725        lpD3DVertexBuffer->Lock((UINT)firstPendingVertex*sizeof(J2DLVERTEX),
726                                (UINT)pendingVertices*sizeof(J2DLVERTEX),
727                                (void**)&lpVert, dwLockFlags)))
728    {
729        // copy only new vertices
730        memcpy((void *)lpVert,
731               (void *)(vertices + firstPendingVertex),
732               pendingVertices * sizeof(J2DLVERTEX));
733        res = lpD3DVertexBuffer->Unlock();
734        UINT currentVertex = firstPendingVertex;
735        UINT batchSize;
736        J2dTraceLn2(J2D_TRACE_VERBOSE,
737                    "D3DVC::Render Starting flushing of %d vertices "\
738                    "in %d batches",
739                    pendingVertices,
740                    (currentBatch - firstPendingBatch + 1));
741
742
743        for (UINT b = firstPendingBatch; b <= currentBatch; b++) {
744            D3DPRIMITIVETYPE pType = batches[b].pType;
745            UINT primCount = batches[b].pNum;
746            switch (pType) {
747                // the macro for adding a line segment adds one too many prims
748                case D3DPT_LINESTRIP: batchSize = primCount; primCount--; break;
749                case D3DPT_LINELIST: batchSize = primCount*2; break;
750                default: batchSize = primCount*3; break;
751            }
752            res = lpD3DDevice->DrawPrimitive(pType, currentVertex, primCount);
753            currentVertex += batchSize;
754            // init to something it can never be
755            batches[b].pType = (D3DPRIMITIVETYPE)0;
756            batches[b].pNum = 0;
757        }
758    } else {
759        DebugPrintD3DError(res, "Can't lock vertex buffer");
760    }
761
762    // REMIND: may need to rethink what to do in case of an error,
763    // should we try to render them later?
764    if (actionType == RESET_ACTION) {
765        firstPendingBatch = 0;
766        firstPendingVertex = 0;
767        firstUnusedVertex = 0;
768        currentBatch = 0;
769    } else {
770        firstPendingBatch = currentBatch;
771        firstPendingVertex = firstUnusedVertex;
772    }
773
774    return res;
775}
776
777HRESULT D3DVertexCacher::EnsureCapacity(D3DPRIMITIVETYPE newPType, UINT vNum)
778{
779    HRESULT res = D3D_OK;
780    if (vNum > MAX_BATCH_SIZE) {
781        // REMIND: need to define our own errors
782        return D3DERR_NOTAVAILABLE;
783    }
784    if ((firstUnusedVertex + vNum) > MAX_BATCH_SIZE) {
785        // if we can't fit new vertices in the vertex buffer,
786        // render whatever we have in the buffer and start
787        // from the beginning of the vertex buffer
788        J2dTraceLn2(J2D_TRACE_VERBOSE,
789                    "D3DVC::EnsureCapacity exceeded capacity. "\
790                    "current v: %d, requested vertices: %d\n",
791                    firstUnusedVertex, vNum);
792        if (FAILED(res = Render(RESET_ACTION))) {
793            return res;
794        }
795    }
796
797    J2dTraceLn5(J2D_TRACE_VERBOSE,
798                "D3DVC::EnsureCapacity current batch: %d "\
799                " batch.type=%d newType=%d vNum=%d firstUnusedV=%d",
800                currentBatch, batches[currentBatch].pType, newPType, vNum,
801                firstUnusedVertex);
802    // there should not be multiple linestrips in a batch,
803    // or they will be counted as a single line strip
804    if (batches[currentBatch].pType != newPType ||
805        batches[currentBatch].pType == D3DPT_LINESTRIP)
806    {
807        // if this is a first unused batch, use it
808        if (firstUnusedVertex == firstPendingVertex) {
809            // record the first batch and vertex scheduled for rendering
810            firstPendingBatch = currentBatch;
811            firstPendingVertex = firstUnusedVertex;
812        } else {
813            // otherwise go to the next batch
814            currentBatch++;
815        }
816        batches[currentBatch].pType = newPType;
817        batches[currentBatch].pNum = 0;
818    }
819    // firstUnusedVertex is updated when new vertices are added
820    // to the vertices array
821
822    return res;
823}
824