430N/A/*
2362N/A * Copyright (c) 2007, 2008, Oracle and/or its affiliates. All rights reserved.
430N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
430N/A *
430N/A * This code is free software; you can redistribute it and/or modify it
430N/A * under the terms of the GNU General Public License version 2 only, as
2362N/A * published by the Free Software Foundation. Oracle designates this
430N/A * particular file as subject to the "Classpath" exception as provided
2362N/A * by Oracle in the LICENSE file that accompanied this code.
430N/A *
430N/A * This code is distributed in the hope that it will be useful, but WITHOUT
430N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
430N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
430N/A * version 2 for more details (a copy is included in the LICENSE file that
430N/A * accompanied this code).
430N/A *
430N/A * You should have received a copy of the GNU General Public License version
430N/A * 2 along with this work; if not, write to the Free Software Foundation,
430N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
430N/A *
2362N/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
2362N/A * or visit www.oracle.com if you need additional information or have any
2362N/A * questions.
430N/A */
430N/A
430N/A#include "D3DPipeline.h"
430N/A#include "D3DVertexCacher.h"
430N/A#include "D3DPaints.h"
430N/A
430N/A#include "math.h"
430N/A
430N/A// non-texturized macros
430N/A
430N/A#define ADD_VERTEX_XYC(X, Y, VCOLOR) \
430N/Ado { \
430N/A vertices[firstUnusedVertex].x = (X); \
430N/A vertices[firstUnusedVertex].y = (Y); \
430N/A vertices[firstUnusedVertex].color = (DWORD)(VCOLOR); \
430N/A firstUnusedVertex++; \
430N/A} while (0)
430N/A
430N/A#define ADD_LINE_XYC(X1, Y1, X2, Y2, VCOLOR) \
430N/Ado { \
430N/A ADD_VERTEX_XYC(X1, Y1, VCOLOR); \
430N/A ADD_VERTEX_XYC(X2, Y2, VCOLOR); \
430N/A batches[currentBatch].pNum++; \
430N/A} while (0)
430N/A
430N/A#define ADD_LINE_SEG_XYC(X, Y, VCOLOR) \
430N/Ado { \
430N/A ADD_VERTEX_XYC(X, Y, VCOLOR); \
430N/A batches[currentBatch].pNum++; \
430N/A} while (0)
430N/A
430N/A#define ADD_TRIANGLE_XYC(X1, Y1, X2, Y2, X3, Y3, VCOLOR) \
430N/Ado { \
430N/A ADD_VERTEX_XYC(X1, Y1, VCOLOR); \
430N/A ADD_VERTEX_XYC(X2, Y2, VCOLOR); \
430N/A ADD_VERTEX_XYC(X3, Y3, VCOLOR); \
430N/A batches[currentBatch].pNum++; \
430N/A} while (0)
430N/A
430N/A// texturized macros
430N/A
430N/A#define ADD_VERTEX_XYUVC(X, Y, U1, V1, VCOLOR) \
430N/Ado { \
430N/A vertices[firstUnusedVertex].x = (X); \
430N/A vertices[firstUnusedVertex].y = (Y); \
430N/A vertices[firstUnusedVertex].tu1 = (U1); \
430N/A vertices[firstUnusedVertex].tv1 = (V1); \
430N/A vertices[firstUnusedVertex].color = (DWORD)(VCOLOR); \
430N/A firstUnusedVertex++; \
430N/A} while (0)
430N/A
430N/A#define ADD_VERTEX_XYUVUVC(X, Y, U1, V1, U2, V2, VCOLOR) \
430N/Ado { \
430N/A vertices[firstUnusedVertex].tu2 = (U2); \
430N/A vertices[firstUnusedVertex].tv2 = (V2); \
430N/A ADD_VERTEX_XYUVC(X, Y, U1, V1, VCOLOR); \
430N/A} while (0)
430N/A
430N/A#define ADD_TRIANGLE_XYUVC(X1, Y1, X2, Y2, X3, Y3, \
430N/A U1, V1, U2, V2, U3, V3, VCOLOR) \
430N/Ado { \
430N/A ADD_VERTEX_XYUVC(X1, Y1, U1, V1, VCOLOR); \
430N/A ADD_VERTEX_XYUVC(X2, Y2, U2, V2, VCOLOR); \
430N/A ADD_VERTEX_XYUVC(X3, Y3, U3, V3, VCOLOR); \
430N/A batches[currentBatch].pNum++; \
430N/A} while (0)
430N/A
430N/A#define ADD_TRIANGLE_XYUVUVC(X1, Y1, X2, Y2, X3, Y3, \
430N/A U11, V11, U12, V12, U13, V13, \
430N/A U21, V21, U22, V22, U23, V23, \
430N/A VCOLOR) \
430N/Ado { \
430N/A ADD_VERTEX_XYUVUVC(X1, Y1, U11, V11, U21, V21, VCOLOR); \
430N/A ADD_VERTEX_XYUVUVC(X2, Y2, U12, V12, U22, V22, VCOLOR); \
430N/A ADD_VERTEX_XYUVUVC(X3, Y3, U13, V13, U23, V23, VCOLOR); \
430N/A batches[currentBatch].pNum++; \
430N/A} while (0)
430N/A
430N/A// These are fudge factors for rendering lines found by experimenting.
430N/A// They are used to tweak the geometry such that the rendering (mostly) matches
430N/A// our software rendering on most hardware. The main goal was to pick the
430N/A// numbers such that the beginning and ending pixels of lines match.
430N/A#define LINE_FUDGE
430N/A// fudge factors
430N/A#ifdef LINE_FUDGE
430N/A
430N/A// Horiz/vertical
430N/A#define HV_FF1 ( 0.0f)
430N/A#define HV_FF2 ( 0.51f)
430N/A// For the record: value below (or larger) is required for Intel 855, but
430N/A// breaks Nvidia, ATI and Intel 965, and since the pipeline is disabled on
430N/A// 855 anyway we'll use 0.51f.
430N/A//#define HV_FF2 ( 0.5315f)
430N/A#define HV_FF3 (-0.2f)
430N/A// single pixel
430N/A#define SP_FF4 ( 0.3f)
430N/A
430N/A// diagonal, down
430N/A#define DD_FX1 (-0.1f)
430N/A#define DD_FY1 (-0.25f)
430N/A#define DD_FX2 ( 0.2f)
430N/A#define DD_FY2 ( 0.304f)
430N/A// For the record: with this value diagonal-down lines with Texture paint
430N/A// are a bit off on all chipsets but Intel 965. So instead we'll use
430N/A// .304f which makes it better for the rest, but at a price of a bit
430N/A// of pixel/texel shifting on 965G
430N/A//#define DD_FY2 ( 0.4f)
430N/A// diagonal, up
430N/A#define DU_FX1 (-0.1f)
430N/A#define DU_FY1 ( 0.4f)
430N/A#define DU_FX2 ( 0.3f)
430N/A#define DU_FY2 (-0.3f)
430N/A
430N/A#else
430N/A
430N/A#define HV_FF1 (0.0f)
430N/A#define HV_FF2 (0.0f)
430N/A#define HV_FF3 (0.0f)
430N/A#define SP_FF4 (0.0f)
430N/A
430N/A#define DD_FX1 (0.0f)
430N/A#define DD_FY1 (0.0f)
430N/A#define DD_FX2 (0.0f)
430N/A#define DD_FY2 (0.0f)
430N/A#define DU_FX1 (0.0f)
430N/A#define DU_FY1 (0.0f)
430N/A#define DU_FX2 (0.0f)
430N/A#define DU_FY2 (0.0f)
430N/A
430N/A#endif
430N/A
430N/AHRESULT
430N/AD3DVertexCacher::CreateInstance(D3DContext *pCtx, D3DVertexCacher **ppVC)
430N/A{
430N/A HRESULT res;
430N/A
430N/A J2dTraceLn(J2D_TRACE_INFO, "D3DVertexCacher::CreateInstance");
430N/A
430N/A *ppVC = new D3DVertexCacher();
430N/A if (FAILED(res = (*ppVC)->Init(pCtx))) {
430N/A delete *ppVC;
430N/A *ppVC = NULL;
430N/A }
430N/A return res;
430N/A}
430N/A
430N/AD3DVertexCacher::D3DVertexCacher()
430N/A{
430N/A lpD3DDevice = NULL;
430N/A lpD3DVertexBuffer = NULL;
430N/A}
430N/A
430N/AHRESULT
430N/AD3DVertexCacher::Init(D3DContext *pCtx)
430N/A{
430N/A D3DCAPS9 caps;
430N/A
430N/A RETURN_STATUS_IF_NULL(pCtx, E_FAIL);
430N/A
430N/A ReleaseDefPoolResources();
430N/A
430N/A this->pCtx = pCtx;
430N/A
430N/A firstPendingBatch = 0;
430N/A firstPendingVertex = 0;
430N/A firstUnusedVertex = 0;
430N/A currentBatch = 0;
430N/A ZeroMemory(vertices, sizeof(vertices));
430N/A ZeroMemory(batches, sizeof(batches));
430N/A
430N/A lpD3DDevice = pCtx->Get3DDevice();
430N/A RETURN_STATUS_IF_NULL(lpD3DDevice, E_FAIL);
430N/A
430N/A ZeroMemory(&caps, sizeof(caps));
430N/A lpD3DDevice->GetDeviceCaps(&caps);
430N/A
430N/A D3DPOOL pool = (caps.DeviceType == D3DDEVTYPE_HAL) ?
430N/A D3DPOOL_DEFAULT : D3DPOOL_SYSTEMMEM;
430N/A // usage depends on whether we use hw or sw vertex processing
430N/A HRESULT res =
430N/A lpD3DDevice->CreateVertexBuffer(MAX_BATCH_SIZE*sizeof(J2DLVERTEX),
430N/A D3DUSAGE_DYNAMIC|D3DUSAGE_WRITEONLY, D3DFVF_J2DLVERTEX,
430N/A pool, &lpD3DVertexBuffer, NULL);
430N/A RETURN_STATUS_IF_FAILED(res);
430N/A
430N/A res = lpD3DDevice->SetStreamSource(0, lpD3DVertexBuffer, 0,
430N/A sizeof(J2DLVERTEX));
430N/A RETURN_STATUS_IF_FAILED(res);
430N/A
430N/A lpD3DDevice->SetFVF(D3DFVF_J2DLVERTEX);
430N/A return res;
430N/A}
430N/A
430N/Avoid
430N/AD3DVertexCacher::ReleaseDefPoolResources()
430N/A{
430N/A SAFE_RELEASE(lpD3DVertexBuffer);
430N/A pCtx = NULL;
430N/A}
430N/A
430N/AHRESULT D3DVertexCacher::DrawLine(int x1, int y1, int x2, int y2)
430N/A{
430N/A HRESULT res;
430N/A if (SUCCEEDED(res = EnsureCapacity(D3DPT_LINELIST, 1*2))) {
430N/A float fx1, fy1, fx2, fy2;
430N/A if (y1 == y2) {
430N/A // horizontal
430N/A fy1 = (float)y1+HV_FF1;
430N/A fy2 = fy1;
430N/A
430N/A if (x1 > x2) {
430N/A fx1 = (float)x2+HV_FF3;
430N/A fx2 = (float)x1+HV_FF2;
430N/A } else if (x1 < x2) {
430N/A fx1 = (float)x1+HV_FF3;
430N/A fx2 = (float)x2+HV_FF2;
430N/A } else {
430N/A // single point, offset a little so that a single
430N/A // pixel is rendered
430N/A fx1 = (float)x1-SP_FF4;
430N/A fy1 = (float)y1-SP_FF4;
430N/A fx2 = (float)x2+SP_FF4;
430N/A fy2 = (float)y2+SP_FF4;
430N/A }
430N/A } else if (x1 == x2) {
430N/A // vertical
430N/A fx1 = (float)x1+HV_FF1;
430N/A fx2 = fx1;
430N/A if (y1 > y2) {
430N/A fy1 = (float)y2+HV_FF3;
430N/A fy2 = (float)y1+HV_FF2;
430N/A } else {
430N/A fy1 = (float)y1+HV_FF3;
430N/A fy2 = (float)y2+HV_FF2;
430N/A }
430N/A } else {
430N/A // diagonal
430N/A if (x1 > x2 && y1 > y2) {
430N/A // ^
430N/A // \ case -> inverse
430N/A fx1 = (float)x2;
430N/A fy1 = (float)y2;
430N/A fx2 = (float)x1;
430N/A fy2 = (float)y1;
430N/A } else if (x1 > x2 && y2 > y1) {
430N/A // /
430N/A // v case - inverse
430N/A fx1 = (float)x2;
430N/A fy1 = (float)y2;
430N/A fx2 = (float)x1;
430N/A fy2 = (float)y1;
430N/A } else {
430N/A // \ ^
430N/A // v or / - leave as is
430N/A fx1 = (float)x1;
430N/A fy1 = (float)y1;
430N/A fx2 = (float)x2;
430N/A fy2 = (float)y2;
430N/A }
430N/A
430N/A if (fx2 > fx1 && fy2 > fy1) {
430N/A // \
430N/A // v
430N/A fx1 += DD_FX1;
430N/A fy1 += DD_FY1;
430N/A fx2 += DD_FX2;
430N/A fy2 += DD_FY2;
430N/A } else {
430N/A // ^
430N/A // /
430N/A fx1 += DU_FX1;
430N/A fy1 += DU_FY1;
430N/A fx2 += DU_FX2;
430N/A fy2 += DU_FY2;
430N/A }
430N/A }
430N/A ADD_LINE_XYC(fx1, fy1, fx2, fy2, color);
430N/A }
430N/A return res;
430N/A}
430N/A
430N/AHRESULT
430N/AD3DVertexCacher::DrawPoly(jint nPoints, jboolean isClosed,
430N/A jint transX, jint transY,
430N/A jint *xPoints, jint *yPoints)
430N/A{
430N/A HRESULT res;
430N/A jfloat mx = (jfloat)xPoints[0];
430N/A jfloat my = (jfloat)yPoints[0];
430N/A jboolean isEmpty = TRUE;
430N/A
430N/A if (nPoints == 0) {
430N/A return S_OK;
430N/A }
430N/A
430N/A if (isClosed &&
430N/A xPoints[nPoints - 1] == xPoints[0] &&
430N/A yPoints[nPoints - 1] == yPoints[0])
430N/A {
430N/A isClosed = FALSE;
430N/A }
430N/A
430N/A // npoints is exactly the number of vertices we need,
430N/A // possibly plus one (if the path is closed)
430N/A UINT reqVerts = nPoints * 1;
430N/A int i = 0;
430N/A do {
430N/A // leave room for one possible additional closing point
430N/A UINT vertsInBatch = min(MAX_BATCH_SIZE-1, max(2, reqVerts));
430N/A if (SUCCEEDED(res = EnsureCapacity(D3DPT_LINESTRIP, vertsInBatch+1))) {
430N/A reqVerts -= vertsInBatch;
430N/A do {
430N/A jfloat x = (jfloat)xPoints[i];
430N/A jfloat y = (jfloat)yPoints[i];
430N/A
430N/A isEmpty = isEmpty && (x == mx && y == my);
430N/A
430N/A ADD_LINE_SEG_XYC(x + transX, y + transY, color);
430N/A i++;
430N/A vertsInBatch--;
430N/A } while (vertsInBatch > 0);
430N/A // include the last point from the current batch into the next
430N/A if (reqVerts > 0) {
430N/A i--;
430N/A reqVerts++;
430N/A // loop continues
430N/A } else if (isClosed && !isEmpty) {
430N/A // if this was the last batch, see if the closing point is needed;
430N/A // note that we have left the room for it
430N/A ADD_LINE_SEG_XYC(mx + transX, my + transY, color);
430N/A // for clarity, the loop is ended anyway
430N/A break;
430N/A } else if (isEmpty || !isClosed) {
430N/A // - either we went nowhere, then change the last point
430N/A // so that a single pixel is rendered
430N/A // - or it's not empty and not closed - add another
430N/A // point because on some boards the last point is not rendered
430N/A mx = xPoints[nPoints-1] + transX +SP_FF4;
430N/A my = yPoints[nPoints-1] + transY +SP_FF4;
430N/A ADD_LINE_SEG_XYC(mx, my, color);
430N/A // for clarity
430N/A break;
430N/A }
430N/A }
430N/A } while (reqVerts > 0 && SUCCEEDED(res));
430N/A
430N/A return res;
430N/A}
430N/A
430N/AHRESULT
430N/AD3DVertexCacher::DrawScanlines(jint scanlineCount, jint *scanlines)
430N/A{
430N/A HRESULT res;
430N/A float x1, x2, y;
430N/A UINT reqVerts = scanlineCount*2/*vertices per line*/;
430N/A
430N/A if (scanlineCount == 0) {
430N/A return S_OK;
430N/A }
430N/A
430N/A do {
430N/A UINT vertsInBatch = min(2*(MAX_BATCH_SIZE/2), reqVerts);
430N/A if (SUCCEEDED(res = EnsureCapacity(D3DPT_LINELIST, vertsInBatch))) {
430N/A reqVerts -= vertsInBatch;
430N/A do {
430N/A x1 = ((float)*(scanlines++)) +HV_FF3;
430N/A x2 = ((float)*(scanlines++)) +HV_FF2;
430N/A y = ((float)*(scanlines++)) +HV_FF1;
430N/A ADD_LINE_XYC(x1, y, x2, y, color);
430N/A vertsInBatch -= 2;
430N/A } while (vertsInBatch > 0);
430N/A }
430N/A } while (reqVerts > 0 && SUCCEEDED(res));
430N/A return res;
430N/A}
430N/A
430N/AHRESULT
430N/AD3DVertexCacher::FillSpans(jint spanCount, jint *spans)
430N/A{
430N/A HRESULT res;
430N/A float x1, y1, x2, y2;
430N/A UINT reqVerts = spanCount*2*3/*vertices per span: two triangles*/;
430N/A
430N/A if (spanCount == 0) {
430N/A return S_OK;
430N/A }
430N/A
430N/A do {
430N/A UINT vertsInBatch = min(6*(MAX_BATCH_SIZE/6), reqVerts);
430N/A if (SUCCEEDED(res = EnsureCapacity(D3DPT_TRIANGLELIST, vertsInBatch))) {
430N/A reqVerts -= vertsInBatch;
430N/A do {
430N/A x1 = ((float)*(spans++));
430N/A y1 = ((float)*(spans++));
430N/A x2 = ((float)*(spans++));
430N/A y2 = ((float)*(spans++));
430N/A
430N/A ADD_TRIANGLE_XYC(x1, y1, x2, y1, x1, y2, color);
430N/A ADD_TRIANGLE_XYC(x1, y2, x2, y1, x2, y2, color);
430N/A vertsInBatch -= 6;
430N/A } while (vertsInBatch > 0);
430N/A }
430N/A } while (reqVerts > 0 && SUCCEEDED(res));
430N/A
430N/A return res;
430N/A}
430N/A
430N/AHRESULT D3DVertexCacher::DrawRect(int x1, int y1, int x2, int y2)
430N/A{
430N/A HRESULT res;
430N/A
430N/A if ((x2 - x1) < 2 || (y2 - y1) < 2) {
430N/A return FillRect(x1, y1, x2+1, y2+1);
430N/A }
430N/A if (SUCCEEDED(res = EnsureCapacity(D3DPT_LINELIST, 4*2))) {
430N/A
430N/A float fx1 = (float)x1;
430N/A float fy1 = (float)y1;
430N/A float fx2 = (float)x2;
430N/A float fy2 = (float)y2;
430N/A
430N/A // horiz: top left - top right
430N/A ADD_LINE_XYC(fx1+HV_FF3, fy1+HV_FF1, fx2-1.0f+HV_FF2, fy1+HV_FF1,color);
430N/A // horiz: bottom left - bottom right
430N/A ADD_LINE_XYC(fx1+1.0f+HV_FF3, fy2+HV_FF1, fx2+HV_FF2, fy2+HV_FF1,color);
430N/A // vert : top right - bottom right
430N/A ADD_LINE_XYC(fx2+HV_FF1, fy1+HV_FF3, fx2+HV_FF1, fy2-1.0f+HV_FF2,color);
430N/A // vert : top left - bottom left
430N/A ADD_LINE_XYC(fx1+HV_FF1, fy1+1.0f+HV_FF3, fx1+HV_FF1, fy2+HV_FF2,color);
430N/A }
430N/A return res;
430N/A}
430N/A
430N/AHRESULT D3DVertexCacher::FillRect(int x1, int y1, int x2, int y2)
430N/A{
430N/A HRESULT res;
430N/A if (SUCCEEDED(res = EnsureCapacity(D3DPT_TRIANGLELIST, 2*3))) {
430N/A float fx1 = (float)x1;
430N/A float fy1 = (float)y1;
430N/A float fx2 = (float)x2;
430N/A float fy2 = (float)y2;
430N/A ADD_TRIANGLE_XYUVC(fx1, fy1, fx2, fy1, fx1, fy2,
430N/A 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f,
430N/A color);
430N/A ADD_TRIANGLE_XYUVC(fx1, fy2, fx2, fy1, fx2, fy2,
430N/A 0.0f, 1.0f, 1.0f, 0.0f, 1.0f, 1.0f,
430N/A color);
430N/A }
430N/A return res;
430N/A}
430N/A
430N/AHRESULT D3DVertexCacher::FillParallelogram(float fx11, float fy11,
430N/A float dx21, float dy21,
430N/A float dx12, float dy12)
430N/A{
430N/A HRESULT res;
430N/A if (SUCCEEDED(res = EnsureCapacity(D3DPT_TRIANGLELIST, 2*3))) {
430N/A // correct texel to pixel mapping; see D3DContext::SetTransform()
430N/A // for non-id tx case
430N/A if (pCtx->IsIdentityTx()) {
430N/A fx11 -= 0.5f;
430N/A fy11 -= 0.5f;
430N/A }
430N/A dx21 += fx11;
430N/A dy21 += fy11;
430N/A float fx22 = dx21 + dx12;
430N/A float fy22 = dy21 + dy12;
430N/A dx12 += fx11;
430N/A dy12 += fy11;
430N/A
430N/A ADD_TRIANGLE_XYUVC(fx11, fy11, dx21, dy21, dx12, dy12,
430N/A 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f,
430N/A color);
430N/A ADD_TRIANGLE_XYUVC(dx12, dy12, dx21, dy21, fx22, fy22,
430N/A 0.0f, 1.0f, 1.0f, 0.0f, 1.0f, 1.0f,
430N/A color);
430N/A }
430N/A return res;
430N/A}
430N/A
430N/A#define ADJUST_PGRAM(V, DV, DIM) \
430N/A do { \
430N/A if ((DV) >= 0) { \
430N/A (DIM) += (DV); \
430N/A } else { \
430N/A (DIM) -= (DV); \
430N/A (V) += (DV); \
430N/A } \
430N/A } while (0)
430N/A
430N/A// Invert the following transform:
430N/A// DeltaT(0, 0) == (0, 0)
430N/A// DeltaT(1, 0) == (DX1, DY1)
430N/A// DeltaT(0, 1) == (DX2, DY2)
430N/A// DeltaT(1, 1) == (DX1+DX2, DY1+DY2)
430N/A// TM00 = DX1, TM01 = DX2, (TM02 = X11)
430N/A// TM10 = DY1, TM11 = DY2, (TM12 = Y11)
430N/A// Determinant = TM00*TM11 - TM01*TM10
430N/A// = DX1*DY2 - DX2*DY1
430N/A// Inverse is:
430N/A// IM00 = TM11/det, IM01 = -TM01/det
430N/A// IM10 = -TM10/det, IM11 = TM00/det
430N/A// IM02 = (TM01 * TM12 - TM11 * TM02) / det,
430N/A// IM12 = (TM10 * TM02 - TM00 * TM12) / det,
430N/A
430N/A#define DECLARE_MATRIX(MAT) \
430N/A float MAT ## 00, MAT ## 01, MAT ## 02, MAT ## 10, MAT ## 11, MAT ## 12
430N/A
430N/A#define GET_INVERTED_MATRIX(MAT, X11, Y11, DX1, DY1, DX2, DY2, RET_CODE) \
430N/A do { \
430N/A float det = DX1*DY2 - DX2*DY1; \
430N/A if (det == 0) { \
430N/A RET_CODE; \
430N/A } \
430N/A MAT ## 00 = DY2/det; \
430N/A MAT ## 01 = -DX2/det; \
430N/A MAT ## 10 = -DY1/det; \
430N/A MAT ## 11 = DX1/det; \
430N/A MAT ## 02 = (DX2 * Y11 - DY2 * X11) / det; \
430N/A MAT ## 12 = (DY1 * X11 - DX1 * Y11) / det; \
430N/A } while (0)
430N/A
430N/A#define TRANSFORM(MAT, TX, TY, X, Y) \
430N/A do { \
430N/A TX = (X) * MAT ## 00 + (Y) * MAT ## 01 + MAT ## 02; \
430N/A TY = (X) * MAT ## 10 + (Y) * MAT ## 11 + MAT ## 12; \
430N/A } while (0)
430N/A
430N/AHRESULT D3DVertexCacher::FillParallelogramAA(float fx11, float fy11,
430N/A float dx21, float dy21,
430N/A float dx12, float dy12)
430N/A{
430N/A HRESULT res;
430N/A DECLARE_MATRIX(om);
430N/A
430N/A GET_INVERTED_MATRIX(om, fx11, fy11, dx21, dy21, dx12, dy12,
430N/A return D3D_OK);
430N/A
430N/A if (SUCCEEDED(res = EnsureCapacity(D3DPT_TRIANGLELIST, 2*3))) {
430N/A float px = fx11, py = fy11;
430N/A float pw = 0.0f, ph = 0.0f;
430N/A ADJUST_PGRAM(px, dx21, pw);
430N/A ADJUST_PGRAM(py, dy21, ph);
430N/A ADJUST_PGRAM(px, dx12, pw);
430N/A ADJUST_PGRAM(py, dy12, ph);
430N/A float px1 = floor(px);
430N/A float py1 = floor(py);
430N/A float px2 = ceil(px + pw);
430N/A float py2 = ceil(py + ph);
430N/A float u11, v11, u12, v12, u21, v21, u22, v22;
430N/A TRANSFORM(om, u11, v11, px1, py1);
430N/A TRANSFORM(om, u21, v21, px2, py1);
430N/A TRANSFORM(om, u12, v12, px1, py2);
430N/A TRANSFORM(om, u22, v22, px2, py2);
430N/A ADD_TRIANGLE_XYUVUVC(px1, py1, px2, py1, px1, py2,
430N/A u11, v11, u21, v21, u12, v12,
430N/A 5.0, 5.0, 6.0, 5.0, 5.0, 6.0,
430N/A color);
430N/A ADD_TRIANGLE_XYUVUVC(px1, py2, px2, py1, px2, py2,
430N/A u12, v12, u21, v21, u22, v22,
430N/A 5.0, 6.0, 6.0, 5.0, 6.0, 6.0,
430N/A color);
430N/A }
430N/A return res;
430N/A}
430N/A
430N/AHRESULT D3DVertexCacher::DrawParallelogramAA(float ox11, float oy11,
430N/A float ox21, float oy21,
430N/A float ox12, float oy12,
430N/A float ix11, float iy11,
430N/A float ix21, float iy21,
430N/A float ix12, float iy12)
430N/A{
430N/A HRESULT res;
430N/A DECLARE_MATRIX(om);
430N/A DECLARE_MATRIX(im);
430N/A
430N/A GET_INVERTED_MATRIX(im, ix11, iy11, ix21, iy21, ix12, iy12,
430N/A // inner parallelogram is degenerate
430N/A // therefore it encloses no area
430N/A // fill outer
430N/A return FillParallelogramAA(ox11, oy11,
430N/A ox21, oy21,
430N/A ox12, oy12));
430N/A GET_INVERTED_MATRIX(om, ox11, oy11, ox21, oy21, ox12, oy12,
430N/A return D3D_OK);
430N/A
430N/A if (SUCCEEDED(res = EnsureCapacity(D3DPT_TRIANGLELIST, 2*3))) {
430N/A float ox = ox11, oy = oy11;
430N/A float ow = 0.0f, oh = 0.0f;
430N/A ADJUST_PGRAM(ox, ox21, ow);
430N/A ADJUST_PGRAM(oy, oy21, oh);
430N/A ADJUST_PGRAM(ox, ox12, ow);
430N/A ADJUST_PGRAM(oy, oy12, oh);
430N/A float ox11 = floor(ox);
430N/A float oy11 = floor(oy);
430N/A float ox22 = ceil(ox + ow);
430N/A float oy22 = ceil(oy + oh);
430N/A float ou11, ov11, ou12, ov12, ou21, ov21, ou22, ov22;
430N/A TRANSFORM(om, ou11, ov11, ox11, oy11);
430N/A TRANSFORM(om, ou21, ov21, ox22, oy11);
430N/A TRANSFORM(om, ou12, ov12, ox11, oy22);
430N/A TRANSFORM(om, ou22, ov22, ox22, oy22);
430N/A float iu11, iv11, iu12, iv12, iu21, iv21, iu22, iv22;
430N/A TRANSFORM(im, iu11, iv11, ox11, oy11);
430N/A TRANSFORM(im, iu21, iv21, ox22, oy11);
430N/A TRANSFORM(im, iu12, iv12, ox11, oy22);
430N/A TRANSFORM(im, iu22, iv22, ox22, oy22);
430N/A ADD_TRIANGLE_XYUVUVC(ox11, oy11, ox22, oy11, ox11, oy22,
430N/A ou11, ov11, ou21, ov21, ou12, ov12,
430N/A iu11, iv11, iu21, iv21, iu12, iv12,
430N/A color);
430N/A ADD_TRIANGLE_XYUVUVC(ox11, oy22, ox22, oy11, ox22, oy22,
430N/A ou12, ov12, ou21, ov21, ou22, ov22,
430N/A iu12, iv12, iu21, iv21, iu22, iv22,
430N/A color);
430N/A }
430N/A return res;
430N/A}
430N/A
430N/AHRESULT
430N/AD3DVertexCacher::DrawTexture(float x1, float y1, float x2, float y2,
430N/A float u1, float v1, float u2, float v2)
430N/A{
430N/A HRESULT res;
430N/A if (SUCCEEDED(res = EnsureCapacity(D3DPT_TRIANGLELIST, 2*3))) {
430N/A // correct texel to pixel mapping; see D3DContext::SetTransform()
430N/A // for non-id tx case
430N/A if (pCtx->IsIdentityTx()) {
430N/A x1 -= 0.5f;
430N/A y1 -= 0.5f;
430N/A x2 -= 0.5f;
430N/A y2 -= 0.5f;
430N/A }
430N/A
430N/A ADD_TRIANGLE_XYUVC(x1, y1, x2, y1, x1, y2,
430N/A u1, v1, u2, v1, u1, v2,
430N/A color);
430N/A ADD_TRIANGLE_XYUVC(x1, y2, x2, y1, x2, y2,
430N/A u1, v2, u2, v1, u2, v2,
430N/A color);
430N/A }
430N/A return res;
430N/A}
430N/A
430N/AHRESULT
430N/AD3DVertexCacher::DrawTexture(float x1, float y1, float x2, float y2,
430N/A float u11, float v11, float u12, float v12,
430N/A float u21, float v21, float u22, float v22)
430N/A{
430N/A HRESULT res;
430N/A if (SUCCEEDED(res = EnsureCapacity(D3DPT_TRIANGLELIST, 2*3))) {
430N/A // correct texel to pixel mapping; see D3DContext::SetTransform()
430N/A // for non-id tx case
430N/A if (pCtx->IsIdentityTx()) {
430N/A x1 -= 0.5f;
430N/A y1 -= 0.5f;
430N/A x2 -= 0.5f;
430N/A y2 -= 0.5f;
430N/A }
430N/A
430N/A ADD_TRIANGLE_XYUVUVC(x1, y1, x2, y1, x1, y2,
430N/A u11, v11, u12, v11, u11, v12,
430N/A u21, v21, u22, v21, u21, v22,
430N/A color);
430N/A ADD_TRIANGLE_XYUVUVC(x1, y2, x2, y1, x2, y2,
430N/A u11, v12, u12, v11, u12, v12,
430N/A u21, v22, u22, v21, u22, v22,
430N/A color);
430N/A }
430N/A return res;
430N/A}
430N/A
430N/AHRESULT D3DVertexCacher::Render(int actionType)
430N/A{
430N/A J2DLVERTEX *lpVert;
430N/A HRESULT res;
430N/A DWORD dwLockFlags;
430N/A UINT pendingVertices = firstUnusedVertex - firstPendingVertex;
430N/A
430N/A // nothing to render
430N/A if (pendingVertices == 0) {
430N/A if (actionType == RESET_ACTION) {
430N/A firstPendingBatch = 0;
430N/A firstPendingVertex = 0;
430N/A firstUnusedVertex = 0;
430N/A currentBatch = 0;
430N/A }
430N/A return D3D_OK;
430N/A }
430N/A
430N/A if (firstPendingVertex == 0) {
430N/A // no data in the buffer yet, we don't care about
430N/A // vertex buffer's contents
430N/A dwLockFlags = D3DLOCK_DISCARD;
430N/A } else {
430N/A // append to the existing data in the vertex buffer
430N/A dwLockFlags = D3DLOCK_NOOVERWRITE;
430N/A }
430N/A
430N/A if (SUCCEEDED(res =
430N/A lpD3DVertexBuffer->Lock((UINT)firstPendingVertex*sizeof(J2DLVERTEX),
430N/A (UINT)pendingVertices*sizeof(J2DLVERTEX),
430N/A (void**)&lpVert, dwLockFlags)))
430N/A {
430N/A // copy only new vertices
430N/A memcpy((void *)lpVert,
430N/A (void *)(vertices + firstPendingVertex),
430N/A pendingVertices * sizeof(J2DLVERTEX));
430N/A res = lpD3DVertexBuffer->Unlock();
430N/A UINT currentVertex = firstPendingVertex;
430N/A UINT batchSize;
430N/A J2dTraceLn2(J2D_TRACE_VERBOSE,
430N/A "D3DVC::Render Starting flushing of %d vertices "\
430N/A "in %d batches",
430N/A pendingVertices,
430N/A (currentBatch - firstPendingBatch + 1));
430N/A
430N/A
430N/A for (UINT b = firstPendingBatch; b <= currentBatch; b++) {
430N/A D3DPRIMITIVETYPE pType = batches[b].pType;
430N/A UINT primCount = batches[b].pNum;
430N/A switch (pType) {
430N/A // the macro for adding a line segment adds one too many prims
430N/A case D3DPT_LINESTRIP: batchSize = primCount; primCount--; break;
430N/A case D3DPT_LINELIST: batchSize = primCount*2; break;
430N/A default: batchSize = primCount*3; break;
430N/A }
430N/A res = lpD3DDevice->DrawPrimitive(pType, currentVertex, primCount);
430N/A currentVertex += batchSize;
430N/A // init to something it can never be
430N/A batches[b].pType = (D3DPRIMITIVETYPE)0;
430N/A batches[b].pNum = 0;
430N/A }
430N/A } else {
430N/A DebugPrintD3DError(res, "Can't lock vertex buffer");
430N/A }
430N/A
430N/A // REMIND: may need to rethink what to do in case of an error,
430N/A // should we try to render them later?
430N/A if (actionType == RESET_ACTION) {
430N/A firstPendingBatch = 0;
430N/A firstPendingVertex = 0;
430N/A firstUnusedVertex = 0;
430N/A currentBatch = 0;
430N/A } else {
430N/A firstPendingBatch = currentBatch;
430N/A firstPendingVertex = firstUnusedVertex;
430N/A }
430N/A
430N/A return res;
430N/A}
430N/A
430N/AHRESULT D3DVertexCacher::EnsureCapacity(D3DPRIMITIVETYPE newPType, UINT vNum)
430N/A{
430N/A HRESULT res = D3D_OK;
430N/A if (vNum > MAX_BATCH_SIZE) {
430N/A // REMIND: need to define our own errors
430N/A return D3DERR_NOTAVAILABLE;
430N/A }
430N/A if ((firstUnusedVertex + vNum) > MAX_BATCH_SIZE) {
430N/A // if we can't fit new vertices in the vertex buffer,
430N/A // render whatever we have in the buffer and start
430N/A // from the beginning of the vertex buffer
430N/A J2dTraceLn2(J2D_TRACE_VERBOSE,
430N/A "D3DVC::EnsureCapacity exceeded capacity. "\
430N/A "current v: %d, requested vertices: %d\n",
430N/A firstUnusedVertex, vNum);
430N/A if (FAILED(res = Render(RESET_ACTION))) {
430N/A return res;
430N/A }
430N/A }
430N/A
430N/A J2dTraceLn5(J2D_TRACE_VERBOSE,
430N/A "D3DVC::EnsureCapacity current batch: %d "\
430N/A " batch.type=%d newType=%d vNum=%d firstUnusedV=%d",
430N/A currentBatch, batches[currentBatch].pType, newPType, vNum,
430N/A firstUnusedVertex);
430N/A // there should not be multiple linestrips in a batch,
430N/A // or they will be counted as a single line strip
430N/A if (batches[currentBatch].pType != newPType ||
430N/A batches[currentBatch].pType == D3DPT_LINESTRIP)
430N/A {
430N/A // if this is a first unused batch, use it
430N/A if (firstUnusedVertex == firstPendingVertex) {
430N/A // record the first batch and vertex scheduled for rendering
430N/A firstPendingBatch = currentBatch;
430N/A firstPendingVertex = firstUnusedVertex;
430N/A } else {
430N/A // otherwise go to the next batch
430N/A currentBatch++;
430N/A }
430N/A batches[currentBatch].pType = newPType;
430N/A batches[currentBatch].pNum = 0;
430N/A }
430N/A // firstUnusedVertex is updated when new vertices are added
430N/A // to the vertices array
430N/A
430N/A return res;
430N/A}