1/*
2 * Copyright (c) 2008, 2010, 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 "math.h"
27#include "GraphicsPrimitiveMgr.h"
28#include "LineUtils.h"
29#include "Trace.h"
30#include "ParallelogramUtils.h"
31
32#include "sun_java2d_loops_DrawParallelogram.h"
33
34#define HANDLE_PGRAM_EDGE(X1, Y1, X2, Y2, \
35 pRasInfo, pixel, pPrim, pFunc, pCompInfo) \
36 do { \
37 jint ix1 = (jint) floor(X1); \
38 jint ix2 = (jint) floor(X2); \
39 jint iy1 = (jint) floor(Y1); \
40 jint iy2 = (jint) floor(Y2); \
41 LineUtils_ProcessLine(pRasInfo, pixel, \
42 pFunc, pPrim, pCompInfo, \
43 ix1, iy1, ix2, iy2, JNI_TRUE); \
44 } while (0)
45
46typedef struct {
47 jdouble x0;
48 jdouble y0;
49 jdouble y1;
50 jdouble slope;
51 jlong dx;
52 jint ystart;
53 jint yend;
54} EdgeInfo;
55
56#define STORE_EDGE(pEDGE, X0, Y0, Y1, SLOPE, DELTAX) \
57 do { \
58 (pEDGE)->x0 = (X0); \
59 (pEDGE)->y0 = (Y0); \
60 (pEDGE)->y1 = (Y1); \
61 (pEDGE)->slope = (SLOPE); \
62 (pEDGE)->dx = (DELTAX); \
63 (pEDGE)->ystart = (jint) floor((Y0) + 0.5); \
64 (pEDGE)->yend = (jint) floor((Y1) + 0.5); \
65 } while (0)
66
67#define STORE_PGRAM(pLTEDGE, pRTEDGE, \
68 X0, Y0, dX1, dY1, dX2, dY2, \
69 SLOPE1, SLOPE2, DELTAX1, DELTAX2) \
70 do { \
71 STORE_EDGE((pLTEDGE)+0, \
72 (X0), (Y0), (Y0) + (dY1), \
73 (SLOPE1), (DELTAX1)); \
74 STORE_EDGE((pRTEDGE)+0, \
75 (X0), (Y0), (Y0) + (dY2), \
76 (SLOPE2), (DELTAX2)); \
77 STORE_EDGE((pLTEDGE)+1, \
78 (X0) + (dX1), (Y0) + (dY1), (Y0) + (dY1) + (dY2), \
79 (SLOPE2), (DELTAX2)); \
80 STORE_EDGE((pRTEDGE)+1, \
81 (X0) + (dX2), (Y0) + (dY2), (Y0) + (dY1) + (dY2), \
82 (SLOPE1), (DELTAX1)); \
83 } while (0)
84
85/*
86 * Class: sun_java2d_loops_DrawParallelogram
87 * Method: DrawParallelogram
88 * Signature: (Lsun/java2d/SunGraphics2D;Lsun/java2d/SurfaceData;DDDDDDDD)V
89 */
90JNIEXPORT void JNICALL
91Java_sun_java2d_loops_DrawParallelogram_DrawParallelogram
92 (JNIEnv *env, jobject self,
93 jobject sg2d, jobject sData,
94 jdouble x0, jdouble y0,
95 jdouble dx1, jdouble dy1,
96 jdouble dx2, jdouble dy2,
97 jdouble lw1, jdouble lw2)
98{
99 SurfaceDataOps *sdOps;
100 SurfaceDataRasInfo rasInfo;
101 NativePrimitive *pPrim;
102 CompositeInfo compInfo;
103 jint pixel;
104 EdgeInfo edges[8];
105 EdgeInfo *active[4];
106 jint ix1, iy1, ix2, iy2;
107 jdouble ldx1, ldy1, ldx2, ldy2;
108 jdouble ox0, oy0;
109
110 /*
111 * Sort parallelogram by y values, ensure that each delta vector
112 * has a non-negative y delta.
113 */
114 SORT_PGRAM(x0, y0, dx1, dy1, dx2, dy2,
115 v = lw1; lw1 = lw2; lw2 = v;);
116
117 // dx,dy for line width in the "1" and "2" directions.
118 ldx1 = dx1 * lw1;
119 ldy1 = dy1 * lw1;
120 ldx2 = dx2 * lw2;
121 ldy2 = dy2 * lw2;
122
123 // calculate origin of the outer parallelogram
124 ox0 = x0 - (ldx1 + ldx2) / 2.0;
125 oy0 = y0 - (ldy1 + ldy2) / 2.0;
126
127 PGRAM_MIN_MAX(ix1, ix2, ox0, dx1+ldx1, dx2+ldx2, JNI_FALSE);
128 iy1 = (jint) floor(oy0 + 0.5);
129 iy2 = (jint) floor(oy0 + dy1 + ldy1 + dy2 + ldy2 + 0.5);
130
131 pPrim = GetNativePrim(env, self);
132 if (pPrim == NULL) {
133 return;
134 }
135 pixel = GrPrim_Sg2dGetPixel(env, sg2d);
136 if (pPrim->pCompType->getCompInfo != NULL) {
137 GrPrim_Sg2dGetCompInfo(env, sg2d, pPrim, &compInfo);
138 }
139
140 sdOps = SurfaceData_GetOps(env, sData);
141 if (sdOps == NULL) {
142 return;
143 }
144
145 GrPrim_Sg2dGetClip(env, sg2d, &rasInfo.bounds);
146 SurfaceData_IntersectBoundsXYXY(&rasInfo.bounds, ix1, iy1, ix2, iy2);
147 if (rasInfo.bounds.y2 <= rasInfo.bounds.y1 ||
148 rasInfo.bounds.x2 <= rasInfo.bounds.x1)
149 {
150 return;
151 }
152
153 if (sdOps->Lock(env, sdOps, &rasInfo, pPrim->dstflags) != SD_SUCCESS) {
154 return;
155 }
156
157 ix1 = rasInfo.bounds.x1;
158 iy1 = rasInfo.bounds.y1;
159 ix2 = rasInfo.bounds.x2;
160 iy2 = rasInfo.bounds.y2;
161 if (ix2 > ix1 && iy2 > iy1) {
162 sdOps->GetRasInfo(env, sdOps, &rasInfo);
163 if (rasInfo.rasBase) {
164 jdouble lslope, rslope;
165 jlong ldx, rdx;
166 jint loy, hiy, numedges;
167 FillParallelogramFunc *pFill =
168 pPrim->funcs.drawparallelogram->fillpgram;
169
170 lslope = (dy1 == 0) ? 0 : dx1 / dy1;
171 rslope = (dy2 == 0) ? 0 : dx2 / dy2;
172 ldx = DblToLong(lslope);
173 rdx = DblToLong(rslope);
174
175 // Only need to generate 4 quads if the interior still
176 // has a hole in it (i.e. if the line width ratios were
177 // both less than 1.0)
178 if (lw1 < 1.0 && lw2 < 1.0) {
179 // If the line widths are both less than a pixel wide
180 // then we can use a drawline function instead for even
181 // more performance.
182 lw1 = sqrt(ldx1*ldx1 + ldy1*ldy1);
183 lw2 = sqrt(ldx2*ldx2 + ldy2*ldy2);
184 if (lw1 <= 1.0001 && lw2 <= 1.0001) {
185 jdouble x3, y3;
186 DrawLineFunc *pLine =
187 pPrim->funcs.drawparallelogram->drawline;
188
189 x3 = (dx1 += x0);
190 y3 = (dy1 += y0);
191 x3 += dx2;
192 y3 += dy2;
193 dx2 += x0;
194 dy2 += y0;
195
196 HANDLE_PGRAM_EDGE( x0, y0, dx1, dy1,
197 &rasInfo, pixel, pPrim, pLine, &compInfo);
198 HANDLE_PGRAM_EDGE(dx1, dy1, x3, y3,
199 &rasInfo, pixel, pPrim, pLine, &compInfo);
200 HANDLE_PGRAM_EDGE( x3, y3, dx2, dy2,
201 &rasInfo, pixel, pPrim, pLine, &compInfo);
202 HANDLE_PGRAM_EDGE(dx2, dy2, x0, y0,
203 &rasInfo, pixel, pPrim, pLine, &compInfo);
204 SurfaceData_InvokeRelease(env, sdOps, &rasInfo);
205 SurfaceData_InvokeUnlock(env, sdOps, &rasInfo);
206 return;
207 }
208
209 // To simplify the edge management below we presort the
210 // inner and outer edges so that they are globally sorted
211 // from left to right. If you scan across the array of
212 // edges for a given Y range then the edges you encounter
213 // will be sorted in X as well.
214 // If AB are left top and bottom edges of outer parallelogram,
215 // and CD are the right pair of edges, and abcd are the
216 // corresponding inner parallelogram edges then we want them
217 // sorted as ABabcdCD to ensure this horizontal ordering.
218 // Conceptually it is like 2 pairs of nested parentheses.
219 STORE_PGRAM(edges + 2, edges + 4,
220 ox0 + ldx1 + ldx2, oy0 + ldy1 + ldy2,
221 dx1 - ldx1, dy1 - ldy1,
222 dx2 - ldx2, dy2 - ldy2,
223 lslope, rslope, ldx, rdx);
224 numedges = 8;
225 } else {
226 // The line width ratios were large enough to consume
227 // the entire hole in the middle of the parallelogram
228 // so we can just issue one large quad for the outer
229 // parallelogram.
230 numedges = 4;
231 }
232
233 // The outer parallelogram always goes in the first two
234 // and last two entries in the array so we either have
235 // ABabcdCD ordering for 8 edges or ABCD ordering for 4
236 // edges. See comment above where we store the inner
237 // parallelogram for a more complete description.
238 STORE_PGRAM(edges + 0, edges + numedges-2,
239 ox0, oy0,
240 dx1 + ldx1, dy1 + ldy1,
241 dx2 + ldx2, dy2 + ldy2,
242 lslope, rslope, ldx, rdx);
243
244 loy = edges[0].ystart;
245 if (loy < iy1) loy = iy1;
246 while (loy < iy2) {
247 jint numactive = 0;
248 jint cur;
249
250 hiy = iy2;
251 // Maintaining a sorted edge list is probably overkill for
252 // 4 or 8 edges. The indices chosen above for storing the
253 // inner and outer left and right edges already guarantee
254 // left to right ordering so we just need to scan for edges
255 // that overlap the current Y range (and also determine the
256 // maximum Y value for which the range is valid).
257 for (cur = 0; cur < numedges; cur++) {
258 EdgeInfo *pEdge = &edges[cur];
259 jint yend = pEdge->yend;
260 if (loy < yend) {
261 // This edge is still in play, have we reached it yet?
262 jint ystart = pEdge->ystart;
263 if (loy < ystart) {
264 // This edge is not active (yet)
265 // Stop before we get to the top of it
266 if (hiy > ystart) hiy = ystart;
267 } else {
268 // This edge is active, store it
269 active[numactive++] = pEdge;
270 // And stop when we get to the bottom of it
271 if (hiy > yend) hiy = yend;
272 }
273 }
274 }
275#ifdef DEBUG
276 if ((numactive & 1) != 0) {
277 J2dTraceLn1(J2D_TRACE_ERROR,
278 "DrawParallelogram: "
279 "ODD NUMBER OF PGRAM EDGES (%d)!!",
280 numactive);
281 }
282#endif
283 for (cur = 0; cur < numactive; cur += 2) {
284 EdgeInfo *pLeft = active[cur+0];
285 EdgeInfo *pRight = active[cur+1];
286 jlong lx = PGRAM_INIT_X(loy,
287 pLeft->x0, pLeft->y0,
288 pLeft->slope);
289 jlong rx = PGRAM_INIT_X(loy,
290 pRight->x0, pRight->y0,
291 pRight->slope);
292 (*pFill)(&rasInfo,
293 ix1, loy, ix2, hiy,
294 lx, pLeft->dx,
295 rx, pRight->dx,
296 pixel, pPrim, &compInfo);
297 }
298 loy = hiy;
299 }
300 }
301 SurfaceData_InvokeRelease(env, sdOps, &rasInfo);
302 }
303 SurfaceData_InvokeUnlock(env, sdOps, &rasInfo);
304}
305