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