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) \
  35 do { \
  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) \
  43 do { \
  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) \
  50 do { \
  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) \
  56 do { \
  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) \
  66 do { \
  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) \
  76 do { \
  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) \
  84 do { \
  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)                       \
  95 do { \
  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 
 155 HRESULT
 156 D3DVertexCacher::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 
 170 D3DVertexCacher::D3DVertexCacher()
 171 {
 172     lpD3DDevice = NULL;
 173     lpD3DVertexBuffer = NULL;
 174 }
 175 
 176 HRESULT
 177 D3DVertexCacher::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 
 215     fudge2 = HV_FF2;
 216     if (fudge2 > 0) {
 217         D3DADAPTER_IDENTIFIER9 aid;
 218         if (SUCCEEDED(pCtx->Get3DObject()->GetAdapterIdentifier(pCtx->
 219                      getAdapterOrdinal(), 0, &aid)) && aid.VendorId == 0x8086) {
 220             // Intel
 221             fudge2 -= 0.09f;
 222         }
 223     }
 224     return res;
 225 }
 226 
 227 void
 228 D3DVertexCacher::ReleaseDefPoolResources()
 229 {
 230     SAFE_RELEASE(lpD3DVertexBuffer);
 231     pCtx = NULL;
 232 }
 233 
 234 HRESULT D3DVertexCacher::DrawLine(int x1, int y1, int x2, int y2)
 235 {
 236     HRESULT res;
 237     if (SUCCEEDED(res = EnsureCapacity(D3DPT_LINELIST, 1*2))) {
 238         float fx1, fy1, fx2, fy2;
 239         if (y1 == y2) {
 240             // horizontal
 241             fy1  = (float)y1+HV_FF1;
 242             fy2  = fy1;
 243 
 244             if (x1 > x2) {
 245                 fx1 = (float)x2+HV_FF3;
 246                 fx2 = (float)x1+fudge2;
 247             } else if (x1 < x2) {
 248                 fx1 = (float)x1+HV_FF3;
 249                 fx2 = (float)x2+fudge2;
 250             } else {
 251                 // single point, offset a little so that a single
 252                 // pixel is rendered
 253                 fx1 = (float)x1-SP_FF4;
 254                 fy1 = (float)y1-SP_FF4;
 255                 fx2 = (float)x2+SP_FF4;
 256                 fy2 = (float)y2+SP_FF4;
 257             }
 258         } else if (x1 == x2) {
 259             // vertical
 260             fx1  = (float)x1+HV_FF1;
 261             fx2  = fx1;
 262             if (y1 > y2) {
 263                 fy1 = (float)y2+HV_FF3;
 264                 fy2 = (float)y1+fudge2;
 265             } else {
 266                 fy1 = (float)y1+HV_FF3;
 267                 fy2 = (float)y2+fudge2;
 268             }
 269         } else {
 270             // diagonal
 271             if (x1 > x2 && y1 > y2) {
 272                 // ^
 273                 //  \ case -> inverse
 274                 fx1 = (float)x2;
 275                 fy1 = (float)y2;
 276                 fx2 = (float)x1;
 277                 fy2 = (float)y1;
 278             } else if (x1 > x2 && y2 > y1) {
 279                 //  /
 280                 // v  case - inverse
 281                 fx1 = (float)x2;
 282                 fy1 = (float)y2;
 283                 fx2 = (float)x1;
 284                 fy2 = (float)y1;
 285             } else {
 286                 // \      ^
 287                 //  v or /  - leave as is
 288                 fx1 = (float)x1;
 289                 fy1 = (float)y1;
 290                 fx2 = (float)x2;
 291                 fy2 = (float)y2;
 292             }
 293 
 294             if (fx2 > fx1 && fy2 > fy1) {
 295                 // \
 296                 //  v
 297                 fx1 += DD_FX1;
 298                 fy1 += DD_FY1;
 299                 fx2 += DD_FX2;
 300                 fy2 += DD_FY2;
 301             } else {
 302                 //   ^
 303                 //  /
 304                 fx1 += DU_FX1;
 305                 fy1 += DU_FY1;
 306                 fx2 += DU_FX2;
 307                 fy2 += DU_FY2;
 308             }
 309         }
 310         ADD_LINE_XYC(fx1, fy1, fx2, fy2, color);
 311     }
 312     return res;
 313 }
 314 
 315 HRESULT
 316 D3DVertexCacher::DrawPoly(jint nPoints, jboolean isClosed,
 317                           jint transX, jint transY,
 318                           jint *xPoints, jint *yPoints)
 319 {
 320     HRESULT res;
 321     jfloat mx = (jfloat)xPoints[0];
 322     jfloat my = (jfloat)yPoints[0];
 323     jboolean isEmpty = TRUE;
 324 
 325     if (nPoints == 0) {
 326         return S_OK;
 327     }
 328 
 329     if (isClosed &&
 330         xPoints[nPoints - 1] == xPoints[0] &&
 331         yPoints[nPoints - 1] == yPoints[0])
 332     {
 333         isClosed = FALSE;
 334     }
 335 
 336     // npoints is exactly the number of vertices we need,
 337     // possibly plus one (if the path is closed)
 338     UINT reqVerts = nPoints * 1;
 339     int i = 0;
 340     do {
 341         // leave room for one possible additional closing point
 342         UINT vertsInBatch = min(MAX_BATCH_SIZE-1, max(2, reqVerts));
 343         if (SUCCEEDED(res = EnsureCapacity(D3DPT_LINESTRIP, vertsInBatch+1))) {
 344             reqVerts -= vertsInBatch;
 345             do {
 346                 jfloat x = (jfloat)xPoints[i];
 347                 jfloat y = (jfloat)yPoints[i];
 348 
 349                 isEmpty = isEmpty && (x == mx && y == my);
 350 
 351                 ADD_LINE_SEG_XYC(x + transX, y + transY, color);
 352                 i++;
 353                 vertsInBatch--;
 354             } while (vertsInBatch > 0);
 355             // include the last point from the current batch into the next
 356             if (reqVerts > 0) {
 357                 i--;
 358                 reqVerts++;
 359                 // loop continues
 360             } else if (isClosed && !isEmpty) {
 361                 // if this was the last batch, see if the closing point is needed;
 362                 // note that we have left the room for it
 363                 ADD_LINE_SEG_XYC(mx + transX, my + transY, color);
 364                 // for clarity, the loop is ended anyway
 365                 break;
 366             } else if (isEmpty || !isClosed) {
 367                 // - either we went nowhere, then change the last point
 368                 // so that a single pixel is rendered
 369                 // - or it's not empty and not closed - add another
 370                 // point because on some boards the last point is not rendered
 371                 mx = xPoints[nPoints-1] + transX +SP_FF4;
 372                 my = yPoints[nPoints-1] + transY +SP_FF4;
 373                 ADD_LINE_SEG_XYC(mx, my, color);
 374                 // for clarity
 375                 break;
 376             }
 377         }
 378     } while (reqVerts > 0 && SUCCEEDED(res));
 379 
 380     return res;
 381 }
 382 
 383 HRESULT
 384 D3DVertexCacher::DrawScanlines(jint scanlineCount, jint *scanlines)
 385 {
 386     HRESULT res;
 387     float x1, x2, y;
 388     UINT reqVerts = scanlineCount*2/*vertices per line*/;
 389 
 390     if (scanlineCount == 0) {
 391         return S_OK;
 392     }
 393 
 394     do {
 395         UINT vertsInBatch = min(2*(MAX_BATCH_SIZE/2), reqVerts);
 396         if (SUCCEEDED(res = EnsureCapacity(D3DPT_LINELIST, vertsInBatch))) {
 397             reqVerts -= vertsInBatch;
 398             do {
 399                 x1 = ((float)*(scanlines++)) +HV_FF3;
 400                 x2 = ((float)*(scanlines++)) +fudge2;
 401                 y  = ((float)*(scanlines++)) +HV_FF1;
 402                 ADD_LINE_XYC(x1, y, x2, y, color);
 403                 vertsInBatch -= 2;
 404             } while (vertsInBatch > 0);
 405         }
 406     } while (reqVerts > 0 && SUCCEEDED(res));
 407     return res;
 408 }
 409 
 410 HRESULT
 411 D3DVertexCacher::FillSpans(jint spanCount, jint *spans)
 412 {
 413     HRESULT res;
 414     float x1, y1, x2, y2;
 415     UINT reqVerts = spanCount*2*3/*vertices per span: two triangles*/;
 416 
 417     if (spanCount == 0) {
 418         return S_OK;
 419     }
 420 
 421     do {
 422         UINT vertsInBatch = min(6*(MAX_BATCH_SIZE/6), reqVerts);
 423         if (SUCCEEDED(res = EnsureCapacity(D3DPT_TRIANGLELIST, vertsInBatch))) {
 424             reqVerts -= vertsInBatch;
 425             do {
 426                 x1 = ((float)*(spans++));
 427                 y1 = ((float)*(spans++));
 428                 x2 = ((float)*(spans++));
 429                 y2 = ((float)*(spans++));
 430 
 431                 ADD_TRIANGLE_XYC(x1, y1, x2, y1, x1, y2, color);
 432                 ADD_TRIANGLE_XYC(x1, y2, x2, y1, x2, y2, color);
 433                 vertsInBatch -= 6;
 434             } while (vertsInBatch > 0);
 435         }
 436     } while (reqVerts > 0 && SUCCEEDED(res));
 437 
 438     return res;
 439 }
 440 
 441 HRESULT D3DVertexCacher::DrawRect(int x1, int y1, int x2, int y2)
 442 {
 443     HRESULT res;
 444 
 445     if ((x2 - x1) < 2 || (y2 - y1) < 2) {
 446         return FillRect(x1, y1, x2+1, y2+1);
 447     }
 448     if (SUCCEEDED(res = EnsureCapacity(D3DPT_LINELIST, 4*2))) {
 449 
 450         float fx1 = (float)x1;
 451         float fy1 = (float)y1;
 452         float fx2 = (float)x2;
 453         float fy2 = (float)y2;
 454 
 455         // horiz: top left - top right
 456         ADD_LINE_XYC(fx1+HV_FF3, fy1+HV_FF1, fx2-1.0f+fudge2, fy1+HV_FF1,color);
 457         // horiz: bottom left - bottom right
 458         ADD_LINE_XYC(fx1+1.0f+HV_FF3, fy2+HV_FF1, fx2+fudge2, fy2+HV_FF1,color);
 459         // vert : top right - bottom right
 460         ADD_LINE_XYC(fx2+HV_FF1, fy1+HV_FF3, fx2+HV_FF1, fy2-1.0f+fudge2,color);
 461         // vert : top left - bottom left
 462         ADD_LINE_XYC(fx1+HV_FF1, fy1+1.0f+HV_FF3, fx1+HV_FF1, fy2+fudge2,color);
 463     }
 464     return res;
 465 }
 466 
 467 HRESULT D3DVertexCacher::FillRect(int x1, int y1, int x2, int y2)
 468 {
 469     HRESULT res;
 470     if (SUCCEEDED(res = EnsureCapacity(D3DPT_TRIANGLELIST, 2*3))) {
 471         float fx1 = (float)x1;
 472         float fy1 = (float)y1;
 473         float fx2 = (float)x2;
 474         float fy2 = (float)y2;
 475         ADD_TRIANGLE_XYUVC(fx1, fy1, fx2, fy1, fx1, fy2,
 476                            0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f,
 477                            color);
 478         ADD_TRIANGLE_XYUVC(fx1, fy2, fx2, fy1, fx2, fy2,
 479                            0.0f, 1.0f, 1.0f, 0.0f, 1.0f, 1.0f,
 480                            color);
 481     }
 482     return res;
 483 }
 484 
 485 HRESULT D3DVertexCacher::FillParallelogram(float fx11, float fy11,
 486                                            float dx21, float dy21,
 487                                            float dx12, float dy12)
 488 {
 489     HRESULT res;
 490     if (SUCCEEDED(res = EnsureCapacity(D3DPT_TRIANGLELIST, 2*3))) {
 491         // correct texel to pixel mapping; see D3DContext::SetTransform()
 492         // for non-id tx case
 493         if (pCtx->IsIdentityTx()) {
 494             fx11 -= 0.5f;
 495             fy11 -= 0.5f;
 496         }
 497         dx21 += fx11;
 498         dy21 += fy11;
 499         float fx22 = dx21 + dx12;
 500         float fy22 = dy21 + dy12;
 501         dx12 += fx11;
 502         dy12 += fy11;
 503 
 504         ADD_TRIANGLE_XYUVC(fx11, fy11, dx21, dy21, dx12, dy12,
 505                            0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f,
 506                            color);
 507         ADD_TRIANGLE_XYUVC(dx12, dy12, dx21, dy21, fx22, fy22,
 508                            0.0f, 1.0f, 1.0f, 0.0f, 1.0f, 1.0f,
 509                            color);
 510     }
 511     return res;
 512 }
 513 
 514 #define ADJUST_PGRAM(V, DV, DIM) \
 515     do { \
 516         if ((DV) >= 0) { \
 517             (DIM) += (DV); \
 518         } else { \
 519             (DIM) -= (DV); \
 520             (V) += (DV); \
 521         } \
 522     } while (0)
 523 
 524 // Invert the following transform:
 525 // DeltaT(0, 0) == (0,       0)
 526 // DeltaT(1, 0) == (DX1,     DY1)
 527 // DeltaT(0, 1) == (DX2,     DY2)
 528 // DeltaT(1, 1) == (DX1+DX2, DY1+DY2)
 529 // TM00 = DX1,   TM01 = DX2,   (TM02 = X11)
 530 // TM10 = DY1,   TM11 = DY2,   (TM12 = Y11)
 531 // Determinant = TM00*TM11 - TM01*TM10
 532 //             =  DX1*DY2  -  DX2*DY1
 533 // Inverse is:
 534 // IM00 =  TM11/det,   IM01 = -TM01/det
 535 // IM10 = -TM10/det,   IM11 =  TM00/det
 536 // IM02 = (TM01 * TM12 - TM11 * TM02) / det,
 537 // IM12 = (TM10 * TM02 - TM00 * TM12) / det,
 538 
 539 #define DECLARE_MATRIX(MAT) \
 540     float MAT ## 00, MAT ## 01, MAT ## 02, MAT ## 10, MAT ## 11, MAT ## 12
 541 
 542 #define GET_INVERTED_MATRIX(MAT, X11, Y11, DX1, DY1, DX2, DY2, RET_CODE) \
 543     do { \
 544         float det = DX1*DY2 - DX2*DY1; \
 545         if (det == 0) { \
 546             RET_CODE; \
 547         } \
 548         MAT ## 00 = DY2/det; \
 549         MAT ## 01 = -DX2/det; \
 550         MAT ## 10 = -DY1/det; \
 551         MAT ## 11 = DX1/det; \
 552         MAT ## 02 = (DX2 * Y11 - DY2 * X11) / det; \
 553         MAT ## 12 = (DY1 * X11 - DX1 * Y11) / det; \
 554     } while (0)
 555 
 556 #define TRANSFORM(MAT, TX, TY, X, Y) \
 557     do { \
 558         TX = (X) * MAT ## 00 + (Y) * MAT ## 01 + MAT ## 02; \
 559         TY = (X) * MAT ## 10 + (Y) * MAT ## 11 + MAT ## 12; \
 560     } while (0)
 561 
 562 HRESULT D3DVertexCacher::FillParallelogramAA(float fx11, float fy11,
 563                                              float dx21, float dy21,
 564                                              float dx12, float dy12)
 565 {
 566     HRESULT res;
 567     DECLARE_MATRIX(om);
 568 
 569     GET_INVERTED_MATRIX(om, fx11, fy11, dx21, dy21, dx12, dy12,
 570                         return D3D_OK);
 571 
 572     if (SUCCEEDED(res = EnsureCapacity(D3DPT_TRIANGLELIST, 2*3))) {
 573         float px = fx11, py = fy11;
 574         float pw = 0.0f, ph = 0.0f;
 575         ADJUST_PGRAM(px, dx21, pw);
 576         ADJUST_PGRAM(py, dy21, ph);
 577         ADJUST_PGRAM(px, dx12, pw);
 578         ADJUST_PGRAM(py, dy12, ph);
 579         float px1 = floor(px);
 580         float py1 = floor(py);
 581         float px2 = ceil(px + pw);
 582         float py2 = ceil(py + ph);
 583         float u11, v11, u12, v12, u21, v21, u22, v22;
 584         TRANSFORM(om, u11, v11, px1, py1);
 585         TRANSFORM(om, u21, v21, px2, py1);
 586         TRANSFORM(om, u12, v12, px1, py2);
 587         TRANSFORM(om, u22, v22, px2, py2);
 588         ADD_TRIANGLE_XYUVUVC(px1, py1, px2, py1, px1, py2,
 589                              u11, v11, u21, v21, u12, v12,
 590                              5.0, 5.0, 6.0, 5.0, 5.0, 6.0,
 591                              color);
 592         ADD_TRIANGLE_XYUVUVC(px1, py2, px2, py1, px2, py2,
 593                              u12, v12, u21, v21, u22, v22,
 594                              5.0, 6.0, 6.0, 5.0, 6.0, 6.0,
 595                              color);
 596     }
 597     return res;
 598 }
 599 
 600 HRESULT D3DVertexCacher::DrawParallelogramAA(float ox11, float oy11,
 601                                              float ox21, float oy21,
 602                                              float ox12, float oy12,
 603                                              float ix11, float iy11,
 604                                              float ix21, float iy21,
 605                                              float ix12, float iy12)
 606 {
 607     HRESULT res;
 608     DECLARE_MATRIX(om);
 609     DECLARE_MATRIX(im);
 610 
 611     GET_INVERTED_MATRIX(im, ix11, iy11, ix21, iy21, ix12, iy12,
 612                         // inner parallelogram is degenerate
 613                         // therefore it encloses no area
 614                         // fill outer
 615                         return FillParallelogramAA(ox11, oy11,
 616                                                    ox21, oy21,
 617                                                    ox12, oy12));
 618     GET_INVERTED_MATRIX(om, ox11, oy11, ox21, oy21, ox12, oy12,
 619                         return D3D_OK);
 620 
 621     if (SUCCEEDED(res = EnsureCapacity(D3DPT_TRIANGLELIST, 2*3))) {
 622         float ox = ox11, oy = oy11;
 623         float ow = 0.0f, oh = 0.0f;
 624         ADJUST_PGRAM(ox, ox21, ow);
 625         ADJUST_PGRAM(oy, oy21, oh);
 626         ADJUST_PGRAM(ox, ox12, ow);
 627         ADJUST_PGRAM(oy, oy12, oh);
 628         float ox11 = floor(ox);
 629         float oy11 = floor(oy);
 630         float ox22 = ceil(ox + ow);
 631         float oy22 = ceil(oy + oh);
 632         float ou11, ov11, ou12, ov12, ou21, ov21, ou22, ov22;
 633         TRANSFORM(om, ou11, ov11, ox11, oy11);
 634         TRANSFORM(om, ou21, ov21, ox22, oy11);
 635         TRANSFORM(om, ou12, ov12, ox11, oy22);
 636         TRANSFORM(om, ou22, ov22, ox22, oy22);
 637         float iu11, iv11, iu12, iv12, iu21, iv21, iu22, iv22;
 638         TRANSFORM(im, iu11, iv11, ox11, oy11);
 639         TRANSFORM(im, iu21, iv21, ox22, oy11);
 640         TRANSFORM(im, iu12, iv12, ox11, oy22);
 641         TRANSFORM(im, iu22, iv22, ox22, oy22);
 642         ADD_TRIANGLE_XYUVUVC(ox11, oy11, ox22, oy11, ox11, oy22,
 643                              ou11, ov11, ou21, ov21, ou12, ov12,
 644                              iu11, iv11, iu21, iv21, iu12, iv12,
 645                              color);
 646         ADD_TRIANGLE_XYUVUVC(ox11, oy22, ox22, oy11, ox22, oy22,
 647                              ou12, ov12, ou21, ov21, ou22, ov22,
 648                              iu12, iv12, iu21, iv21, iu22, iv22,
 649                              color);
 650     }
 651     return res;
 652 }
 653 
 654 HRESULT
 655 D3DVertexCacher::DrawTexture(float x1, float y1, float x2, float y2,
 656                              float u1, float v1, float u2, float v2)
 657 {
 658     HRESULT res;
 659     if (SUCCEEDED(res = EnsureCapacity(D3DPT_TRIANGLELIST, 2*3))) {
 660         // correct texel to pixel mapping; see D3DContext::SetTransform()
 661         // for non-id tx case
 662         if (pCtx->IsIdentityTx()) {
 663             x1 -= 0.5f;
 664             y1 -= 0.5f;
 665             x2 -= 0.5f;
 666             y2 -= 0.5f;
 667         }
 668 
 669         ADD_TRIANGLE_XYUVC(x1, y1, x2, y1, x1, y2,
 670                            u1, v1, u2, v1, u1, v2,
 671                            color);
 672         ADD_TRIANGLE_XYUVC(x1, y2, x2, y1, x2, y2,
 673                            u1, v2, u2, v1, u2, v2,
 674                            color);
 675     }
 676     return res;
 677 }
 678 
 679 HRESULT
 680 D3DVertexCacher::DrawTexture(float  x1, float  y1, float  x2, float  y2,
 681                              float u11, float v11, float u12, float v12,
 682                              float u21, float v21, float u22, float v22)
 683 {
 684     HRESULT res;
 685     if (SUCCEEDED(res = EnsureCapacity(D3DPT_TRIANGLELIST, 2*3))) {
 686         // correct texel to pixel mapping; see D3DContext::SetTransform()
 687         // for non-id tx case
 688         if (pCtx->IsIdentityTx()) {
 689             x1 -= 0.5f;
 690             y1 -= 0.5f;
 691             x2 -= 0.5f;
 692             y2 -= 0.5f;
 693         }
 694 
 695         ADD_TRIANGLE_XYUVUVC(x1, y1, x2, y1, x1, y2,
 696                              u11, v11, u12, v11, u11, v12,
 697                              u21, v21, u22, v21, u21, v22,
 698                              color);
 699         ADD_TRIANGLE_XYUVUVC(x1, y2, x2, y1, x2, y2,
 700                              u11, v12, u12, v11, u12, v12,
 701                              u21, v22, u22, v21, u22, v22,
 702                              color);
 703     }
 704     return res;
 705 }
 706 
 707 HRESULT D3DVertexCacher::Render(int actionType)
 708 {
 709     J2DLVERTEX *lpVert;
 710     HRESULT res;
 711     DWORD dwLockFlags;
 712     UINT pendingVertices = firstUnusedVertex - firstPendingVertex;
 713 
 714     // nothing to render
 715     if (pendingVertices == 0) {
 716         if (actionType == RESET_ACTION) {
 717             firstPendingBatch = 0;
 718             firstPendingVertex = 0;
 719             firstUnusedVertex = 0;
 720             currentBatch = 0;
 721         }
 722         return D3D_OK;
 723     }
 724 
 725     if (firstPendingVertex == 0) {
 726         // no data in the buffer yet, we don't care about
 727         // vertex buffer's contents
 728         dwLockFlags = D3DLOCK_DISCARD;
 729     } else {
 730         // append to the existing data in the vertex buffer
 731         dwLockFlags = D3DLOCK_NOOVERWRITE;
 732     }
 733 
 734     if (SUCCEEDED(res =
 735         lpD3DVertexBuffer->Lock((UINT)firstPendingVertex*sizeof(J2DLVERTEX),
 736                                 (UINT)pendingVertices*sizeof(J2DLVERTEX),
 737                                 (void**)&lpVert, dwLockFlags)))
 738     {
 739         // copy only new vertices
 740         memcpy((void *)lpVert,
 741                (void *)(vertices + firstPendingVertex),
 742                pendingVertices * sizeof(J2DLVERTEX));
 743         res = lpD3DVertexBuffer->Unlock();
 744         UINT currentVertex = firstPendingVertex;
 745         UINT batchSize;
 746         J2dTraceLn2(J2D_TRACE_VERBOSE,
 747                     "D3DVC::Render Starting flushing of %d vertices "\
 748                     "in %d batches",
 749                     pendingVertices,
 750                     (currentBatch - firstPendingBatch + 1));
 751 
 752 
 753         for (UINT b = firstPendingBatch; b <= currentBatch; b++) {
 754             D3DPRIMITIVETYPE pType = batches[b].pType;
 755             UINT primCount = batches[b].pNum;
 756             switch (pType) {
 757                 // the macro for adding a line segment adds one too many prims
 758                 case D3DPT_LINESTRIP: batchSize = primCount; primCount--; break;
 759                 case D3DPT_LINELIST: batchSize = primCount*2; break;
 760                 default: batchSize = primCount*3; break;
 761             }
 762             res = lpD3DDevice->DrawPrimitive(pType, currentVertex, primCount);
 763             currentVertex += batchSize;
 764             // init to something it can never be
 765             batches[b].pType = (D3DPRIMITIVETYPE)0;
 766             batches[b].pNum = 0;
 767         }
 768     } else {
 769         DebugPrintD3DError(res, "Can't lock vertex buffer");
 770     }
 771 
 772     // REMIND: may need to rethink what to do in case of an error,
 773     // should we try to render them later?
 774     if (actionType == RESET_ACTION) {
 775         firstPendingBatch = 0;
 776         firstPendingVertex = 0;
 777         firstUnusedVertex = 0;
 778         currentBatch = 0;
 779     } else {
 780         firstPendingBatch = currentBatch;
 781         firstPendingVertex = firstUnusedVertex;
 782     }
 783 
 784     return res;
 785 }
 786 
 787 HRESULT D3DVertexCacher::EnsureCapacity(D3DPRIMITIVETYPE newPType, UINT vNum)
 788 {
 789     HRESULT res = D3D_OK;
 790     if (vNum > MAX_BATCH_SIZE) {
 791         // REMIND: need to define our own errors
 792         return D3DERR_NOTAVAILABLE;
 793     }
 794     if ((firstUnusedVertex + vNum) > MAX_BATCH_SIZE) {
 795         // if we can't fit new vertices in the vertex buffer,
 796         // render whatever we have in the buffer and start
 797         // from the beginning of the vertex buffer
 798         J2dTraceLn2(J2D_TRACE_VERBOSE,
 799                     "D3DVC::EnsureCapacity exceeded capacity. "\
 800                     "current v: %d, requested vertices: %d\n",
 801                     firstUnusedVertex, vNum);
 802         if (FAILED(res = Render(RESET_ACTION))) {
 803             return res;
 804         }
 805     }
 806 
 807     J2dTraceLn5(J2D_TRACE_VERBOSE,
 808                 "D3DVC::EnsureCapacity current batch: %d "\
 809                 " batch.type=%d newType=%d vNum=%d firstUnusedV=%d",
 810                 currentBatch, batches[currentBatch].pType, newPType, vNum,
 811                 firstUnusedVertex);
 812     // there should not be multiple linestrips in a batch,
 813     // or they will be counted as a single line strip
 814     if (batches[currentBatch].pType != newPType ||
 815         batches[currentBatch].pType == D3DPT_LINESTRIP)
 816     {
 817         // if this is a first unused batch, use it
 818         if (firstUnusedVertex == firstPendingVertex) {
 819             // record the first batch and vertex scheduled for rendering
 820             firstPendingBatch = currentBatch;
 821             firstPendingVertex = firstUnusedVertex;
 822         } else {
 823             // otherwise go to the next batch
 824             currentBatch++;
 825         }
 826         batches[currentBatch].pType = newPType;
 827         batches[currentBatch].pNum = 0;
 828     }
 829     // firstUnusedVertex is updated when new vertices are added
 830     // to the vertices array
 831 
 832     return res;
 833 }