1 | /* |
2 | * Copyright (c) 2000, 2013, 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 <stdlib.h> |
28 | #include <string.h> |
29 | #include "GraphicsPrimitiveMgr.h" |
30 | #include "ParallelogramUtils.h" |
31 | |
32 | #include "sun_java2d_loops_MaskFill.h" |
33 | |
34 | /* |
35 | * Class: sun_java2d_loops_MaskFill |
36 | * Method: MaskFill |
37 | * Signature: (Lsun/java2d/SunGraphics2D;Lsun/java2d/SurfaceData;Ljava/awt/Composite;IIII[BII)V |
38 | */ |
39 | JNIEXPORT void JNICALL |
40 | Java_sun_java2d_loops_MaskFill_MaskFill |
41 | (JNIEnv *env, jobject self, |
42 | jobject sg2d, jobject sData, jobject comp, |
43 | jint x, jint y, jint w, jint h, |
44 | jbyteArray maskArray, jint maskoff, jint maskscan) |
45 | { |
46 | SurfaceDataOps *sdOps; |
47 | SurfaceDataRasInfo rasInfo; |
48 | NativePrimitive *pPrim; |
49 | CompositeInfo compInfo; |
50 | |
51 | pPrim = GetNativePrim(env, self); |
52 | if (pPrim == NULL) { |
53 | return; |
54 | } |
55 | if (pPrim->pCompType->getCompInfo != NULL) { |
56 | (*pPrim->pCompType->getCompInfo)(env, &compInfo, comp); |
57 | } |
58 | |
59 | sdOps = SurfaceData_GetOps(env, sData); |
60 | if (sdOps == 0) { |
61 | return; |
62 | } |
63 | |
64 | rasInfo.bounds.x1 = x; |
65 | rasInfo.bounds.y1 = y; |
66 | rasInfo.bounds.x2 = x + w; |
67 | rasInfo.bounds.y2 = y + h; |
68 | if (sdOps->Lock(env, sdOps, &rasInfo, pPrim->dstflags) != SD_SUCCESS) { |
69 | return; |
70 | } |
71 | |
72 | if (rasInfo.bounds.x2 > rasInfo.bounds.x1 && |
73 | rasInfo.bounds.y2 > rasInfo.bounds.y1) |
74 | { |
75 | jint color = GrPrim_Sg2dGetEaRGB(env, sg2d); |
76 | sdOps->GetRasInfo(env, sdOps, &rasInfo); |
77 | if (rasInfo.rasBase) { |
78 | jint width = rasInfo.bounds.x2 - rasInfo.bounds.x1; |
79 | jint height = rasInfo.bounds.y2 - rasInfo.bounds.y1; |
80 | void *pDst = PtrCoord(rasInfo.rasBase, |
81 | rasInfo.bounds.x1, rasInfo.pixelStride, |
82 | rasInfo.bounds.y1, rasInfo.scanStride); |
83 | unsigned char *pMask = |
84 | (maskArray |
85 | ? (*env)->GetPrimitiveArrayCritical(env, maskArray, 0) |
86 | : 0); |
87 | if (maskArray != NULL && pMask == NULL) { |
88 | SurfaceData_InvokeRelease(env, sdOps, &rasInfo); |
89 | SurfaceData_InvokeUnlock(env, sdOps, &rasInfo); |
90 | return; |
91 | } |
92 | maskoff += ((rasInfo.bounds.y1 - y) * maskscan + |
93 | (rasInfo.bounds.x1 - x)); |
94 | (*pPrim->funcs.maskfill)(pDst, |
95 | pMask, maskoff, maskscan, |
96 | width, height, |
97 | color, &rasInfo, |
98 | pPrim, &compInfo); |
99 | if (pMask) { |
100 | (*env)->ReleasePrimitiveArrayCritical(env, maskArray, |
101 | pMask, JNI_ABORT); |
102 | } |
103 | } |
104 | SurfaceData_InvokeRelease(env, sdOps, &rasInfo); |
105 | } |
106 | SurfaceData_InvokeUnlock(env, sdOps, &rasInfo); |
107 | } |
108 | |
109 | #define MASK_BUF_LEN 1024 |
110 | |
111 | #define DblToMask(v) ((unsigned char) ((v)*255.9999)) |
112 | |
113 | /* Fills an aligned rectangle with potentially translucent edges. */ |
114 | static void |
115 | fillAARect(NativePrimitive *pPrim, SurfaceDataRasInfo *pRasInfo, |
116 | CompositeInfo *pCompInfo, jint color, unsigned char *pMask, |
117 | void *pDst, |
118 | jdouble x1, jdouble y1, jdouble x2, jdouble y2) |
119 | { |
120 | jint cx1 = pRasInfo->bounds.x1; |
121 | jint cy1 = pRasInfo->bounds.y1; |
122 | jint cx2 = pRasInfo->bounds.x2; |
123 | jint cy2 = pRasInfo->bounds.y2; |
124 | jint rx1 = (jint) ceil(x1); |
125 | jint ry1 = (jint) ceil(y1); |
126 | jint rx2 = (jint) floor(x2); |
127 | jint ry2 = (jint) floor(y2); |
128 | jint width = cx2 - cx1; |
129 | jint scan = pRasInfo->scanStride; |
130 | /* Convert xy12 into the edge coverage fractions for those edges. */ |
131 | x1 = rx1-x1; |
132 | y1 = ry1-y1; |
133 | x2 = x2-rx2; |
134 | y2 = y2-ry2; |
135 | if (ry2 < ry1) { |
136 | /* Accumulate bottom coverage into top coverage. */ |
137 | y1 = y1 + y2 - 1.0; |
138 | /* prevent processing of "bottom fractional row" */ |
139 | ry2 = cy2; |
140 | } |
141 | if (rx2 < rx1) { |
142 | /* Accumulate right coverage into left coverage. */ |
143 | x1 = x1 + x2 - 1.0; |
144 | /* prevent processing of "right fractional column" */ |
145 | rx2 = cx2; |
146 | } |
147 | /* Check for a visible "top fractional row" and process it */ |
148 | if (cy1 < ry1) { |
149 | unsigned char midcov = DblToMask(y1); |
150 | jint x; |
151 | for (x = 0; x < width; x++) { |
152 | pMask[x] = midcov; |
153 | } |
154 | if (cx1 < rx1) { |
155 | pMask[0] = DblToMask(y1 * x1); |
156 | } |
157 | if (cx2 > rx2) { |
158 | pMask[width-1] = DblToMask(y1 * x2); |
159 | } |
160 | (*pPrim->funcs.maskfill)(pDst, |
161 | pMask, 0, 0, |
162 | width, 1, |
163 | color, pRasInfo, |
164 | pPrim, pCompInfo); |
165 | pDst = PtrAddBytes(pDst, scan); |
166 | cy1++; |
167 | } |
168 | /* Check for a visible "left fract, solid middle, right fract" section. */ |
169 | if (cy1 < ry2 && cy1 < cy2) { |
170 | jint midh = ((ry2 < cy2) ? ry2 : cy2) - cy1; |
171 | jint midx = cx1; |
172 | void *pMid = pDst; |
173 | /* First process the left "fractional column" if it is visible. */ |
174 | if (midx < rx1) { |
175 | pMask[0] = DblToMask(x1); |
176 | /* Note: maskscan == 0 means we reuse this value for every row. */ |
177 | (*pPrim->funcs.maskfill)(pMid, |
178 | pMask, 0, 0, |
179 | 1, midh, |
180 | color, pRasInfo, |
181 | pPrim, pCompInfo); |
182 | pMid = PtrAddBytes(pMid, pRasInfo->pixelStride); |
183 | midx++; |
184 | } |
185 | /* Process the central solid section if it is visible. */ |
186 | if (midx < rx2 && midx < cx2) { |
187 | jint midw = ((rx2 < cx2) ? rx2 : cx2) - midx; |
188 | /* A NULL mask buffer means "all coverages are 0xff" */ |
189 | (*pPrim->funcs.maskfill)(pMid, |
190 | NULL, 0, 0, |
191 | midw, midh, |
192 | color, pRasInfo, |
193 | pPrim, pCompInfo); |
194 | pMid = PtrCoord(pMid, midw, pRasInfo->pixelStride, 0, 0); |
195 | midx += midw; |
196 | } |
197 | /* Finally process the right "fractional column" if it is visible. */ |
198 | if (midx < cx2) { |
199 | pMask[0] = DblToMask(x2); |
200 | /* Note: maskscan == 0 means we reuse this value for every row. */ |
201 | (*pPrim->funcs.maskfill)(pMid, |
202 | pMask, 0, 0, |
203 | 1, midh, |
204 | color, pRasInfo, |
205 | pPrim, pCompInfo); |
206 | } |
207 | cy1 += midh; |
208 | pDst = PtrCoord(pDst, 0, 0, midh, scan); |
209 | } |
210 | /* Check for a visible "bottom fractional row" and process it */ |
211 | if (cy1 < cy2) { |
212 | unsigned char midcov = DblToMask(y2); |
213 | jint x; |
214 | for (x = 0; x < width; x++) { |
215 | pMask[x] = midcov; |
216 | } |
217 | if (cx1 < rx1) { |
218 | pMask[0] = DblToMask(y2 * x1); |
219 | } |
220 | if (cx2 > rx2) { |
221 | pMask[width-1] = DblToMask(y2 * x2); |
222 | } |
223 | (*pPrim->funcs.maskfill)(pDst, |
224 | pMask, 0, 0, |
225 | width, 1, |
226 | color, pRasInfo, |
227 | pPrim, pCompInfo); |
228 | } |
229 | } |
230 | |
231 | /* |
232 | * Support code for arbitrary tracing and MaskFill filling of |
233 | * non-rectilinear (diagonal) parallelograms. |
234 | * |
235 | * This code is based upon the following model of AA coverage. |
236 | * |
237 | * Each edge of a parallelogram (for fillPgram) or a double |
238 | * parallelogram (inner and outer parallelograms for drawPgram) |
239 | * can be rasterized independently because the geometry is well |
240 | * defined in such a way that none of the sides will ever cross |
241 | * each other and they have a fixed ordering that is fairly |
242 | * well predetermined. |
243 | * |
244 | * So, for each edge we will look at the diagonal line that |
245 | * the edge makes as it passes through a row of pixels. Some |
246 | * such diagonal lines may pass entirely through the row of |
247 | * pixels in a single pixel column. Some may cut across the |
248 | * row and pass through several pixel columns before they pass |
249 | * on to the next row. |
250 | * |
251 | * As the edge passes through the row of pixels it will affect |
252 | * the coverage of the pixels it passes through as well as all |
253 | * of the pixels to the right of the edge. The coverage will |
254 | * either be increased (by a left edge of a parallelogram) or |
255 | * decreased (by a right edge) for all pixels to the right, until |
256 | * another edge passing the opposite direction is encountered. |
257 | * |
258 | * The coverage added or subtracted by an edge as it crosses a |
259 | * given pixel is calculated using a trapezoid formula in the |
260 | * following manner: |
261 | * |
262 | * / |
263 | * +-----+---/-+-----+ |
264 | * | | / | | |
265 | * | | / | | |
266 | * +-----+/----+-----+ |
267 | * / |
268 | * |
269 | * The area to the right of that edge for the pixel where it |
270 | * crosses is given as: |
271 | * |
272 | * trapheight * (topedge + bottomedge)/2 |
273 | * |
274 | * Another thing to note is that the above formula gives the |
275 | * contribution of that edge to the given pixel where it crossed, |
276 | * but in so crossing the pixel row, it also created 100% coverage |
277 | * for all of the pixels to the right. |
278 | * |
279 | * This example was simplified in that the edge depicted crossed |
280 | * the complete pixel row and it did so entirely within the bounds |
281 | * of a single pixel column. In practice, many edges may start or |
282 | * end in a given row and thus provide only partial row coverage |
283 | * (i.e. the total "trapheight" in the formula never reaches 1.0). |
284 | * And in other cases, edges may travel sideways through several |
285 | * pixel columns on a given pixel row from where they enter it to |
286 | * where the leave it (which also mans that the trapheight for a |
287 | * given pixel will be less than 1.0, but by the time the edge |
288 | * completes its journey through the pixel row the "coverage shadow" |
289 | * that it casts on all pixels to the right eventually reaches 100%). |
290 | * |
291 | * In order to simplify the calculations so that we don't have to |
292 | * keep propagating coverages we calculate for one edge "until we |
293 | * reach another edge" we will process one edge at a time and |
294 | * simply record in a buffer the amount that an edge added to |
295 | * or subtracted from the coverage for a given pixel and its |
296 | * following right-side neighbors. Thus, the true total coverage |
297 | * of a given pixel is only determined by summing the deltas for |
298 | * that pixel and all of the pixels to its left. Since we already |
299 | * have to scan the buffer to change floating point coverages into |
300 | * mask values for a MaskFill loop, it is simple enough to sum the |
301 | * values as we perform that scan from left to right. |
302 | * |
303 | * In the above example, note that 2 deltas need to be recorded even |
304 | * though the edge only intersected a single pixel. The delta recorded |
305 | * for the pixel where the edge crossed will be approximately 55% |
306 | * (guesstimating by examining the poor ascii art) which is fine for |
307 | * determining how to render that pixel, but the rest of the pixels |
308 | * to its right should have their coverage modified by a full 100% |
309 | * and the 55% delta value we recorded for the pixel that the edge |
310 | * crossed will not get them there. We adjust for this by adding |
311 | * the "remainder" of the coverage implied by the shadow to the |
312 | * pixel immediately to the right of where we record a trapezoidal |
313 | * contribution. In this case a delta of 45% will be recorded in |
314 | * the pixel immediately to the right to raise the total to 100%. |
315 | * |
316 | * As we sum these delta values as we process the line from left |
317 | * to right, these delta values will typically drive the sum from |
318 | * 0% up to 100% and back down to 0% over the course of a single |
319 | * pixel row. In the case of a drawn (double) parallelogram the |
320 | * sum will go to 100% and back to 0% twice on most scanlines. |
321 | * |
322 | * The fillAAPgram and drawAAPgram functions drive the main flow |
323 | * of the algorithm with help from the following structures, |
324 | * macros, and functions. It is probably best to start with |
325 | * those 2 functions to gain an understanding of the algorithm. |
326 | */ |
327 | typedef struct { |
328 | jdouble x; |
329 | jdouble y; |
330 | jdouble xbot; |
331 | jdouble ybot; |
332 | jdouble xnexty; |
333 | jdouble ynextx; |
334 | jdouble xnextx; |
335 | jdouble linedx; |
336 | jdouble celldx; |
337 | jdouble celldy; |
338 | jboolean isTrailing; |
339 | } EdgeInfo; |
340 | |
341 | #define MIN_DELTA (1.0/256.0) |
342 | |
343 | /* |
344 | * Calculates slopes and deltas for an edge and stores results in an EdgeInfo. |
345 | * Returns true if the edge was valid (i.e. not ignored for some reason). |
346 | */ |
347 | static jboolean |
348 | storeEdge(EdgeInfo *pEdge, |
349 | jdouble x, jdouble y, jdouble dx, jdouble dy, |
350 | jint cx1, jint cy1, jint cx2, jint cy2, |
351 | jboolean isTrailing) |
352 | { |
353 | jdouble xbot = x + dx; |
354 | jdouble ybot = y + dy; |
355 | jboolean ret; |
356 | |
357 | pEdge->x = x; |
358 | pEdge->y = y; |
359 | pEdge->xbot = xbot; |
360 | pEdge->ybot = ybot; |
361 | |
362 | /* Note that parallelograms are sorted so dy is always non-negative */ |
363 | if (dy > MIN_DELTA && /* NaN and horizontal protection */ |
364 | ybot > cy1 && /* NaN and "OUT_ABOVE" protection */ |
365 | y < cy2 && /* NaN and "OUT_BELOW" protection */ |
366 | xbot == xbot && /* NaN protection */ |
367 | (x < cx2 || xbot < cx2)) /* "OUT_RIGHT" protection */ |
368 | /* Note: "OUT_LEFT" segments may still contribute coverage... */ |
369 | { |
370 | /* no NaNs, dy is not horizontal, and segment contributes to clip */ |
371 | if (dx < -MIN_DELTA || dx > MIN_DELTA) { |
372 | /* dx is not vertical */ |
373 | jdouble linedx; |
374 | jdouble celldy; |
375 | jdouble nextx; |
376 | |
377 | linedx = dx / dy; |
378 | celldy = dy / dx; |
379 | if (y < cy1) { |
380 | pEdge->x = x = x + (cy1 - y) * linedx; |
381 | pEdge->y = y = cy1; |
382 | } |
383 | pEdge->linedx = linedx; |
384 | if (dx < 0) { |
385 | pEdge->celldx = -1.0; |
386 | pEdge->celldy = -celldy; |
387 | pEdge->xnextx = nextx = ceil(x) - 1.0; |
388 | } else { |
389 | pEdge->celldx = +1.0; |
390 | pEdge->celldy = celldy; |
391 | pEdge->xnextx = nextx = floor(x) + 1.0; |
392 | } |
393 | pEdge->ynextx = y + (nextx - x) * celldy; |
394 | pEdge->xnexty = x + ((floor(y) + 1) - y) * linedx; |
395 | } else { |
396 | /* dx is essentially vertical */ |
397 | if (y < cy1) { |
398 | pEdge->y = y = cy1; |
399 | } |
400 | pEdge->xbot = x; |
401 | pEdge->linedx = 0.0; |
402 | pEdge->celldx = 0.0; |
403 | pEdge->celldy = 1.0; |
404 | pEdge->xnextx = x; |
405 | pEdge->xnexty = x; |
406 | pEdge->ynextx = ybot; |
407 | } |
408 | ret = JNI_TRUE; |
409 | } else { |
410 | /* There is some reason to ignore this segment, "celldy=0" omits it */ |
411 | pEdge->ybot = y; |
412 | pEdge->linedx = dx; |
413 | pEdge->celldx = dx; |
414 | pEdge->celldy = 0.0; |
415 | pEdge->xnextx = xbot; |
416 | pEdge->xnexty = xbot; |
417 | pEdge->ynextx = y; |
418 | ret = JNI_FALSE; |
419 | } |
420 | pEdge->isTrailing = isTrailing; |
421 | return ret; |
422 | } |
423 | |
424 | /* |
425 | * Calculates and stores slopes and deltas for all edges of a parallelogram. |
426 | * Returns true if at least 1 edge was valid (i.e. not ignored for some reason). |
427 | * |
428 | * The inverted flag is true for an outer parallelogram (left and right |
429 | * edges are leading and trailing) and false for an inner parallelogram |
430 | * (where the left edge is trailing and the right edge is leading). |
431 | */ |
432 | static jboolean |
433 | storePgram(EdgeInfo *pLeftEdge, EdgeInfo *pRightEdge, |
434 | jdouble x, jdouble y, |
435 | jdouble dx1, jdouble dy1, |
436 | jdouble dx2, jdouble dy2, |
437 | jint cx1, jint cy1, jint cx2, jint cy2, |
438 | jboolean inverted) |
439 | { |
440 | jboolean ret = JNI_FALSE; |
441 | ret = (storeEdge(pLeftEdge + 0, |
442 | x , y , dx1, dy1, |
443 | cx1, cy1, cx2, cy2, inverted) || ret); |
444 | ret = (storeEdge(pLeftEdge + 1, |
445 | x+dx1, y+dy1, dx2, dy2, |
446 | cx1, cy1, cx2, cy2, inverted) || ret); |
447 | ret = (storeEdge(pRightEdge + 0, |
448 | x , y , dx2, dy2, |
449 | cx1, cy1, cx2, cy2, !inverted) || ret); |
450 | ret = (storeEdge(pRightEdge + 1, |
451 | x+dx2, y+dy2, dx1, dy1, |
452 | cx1, cy1, cx2, cy2, !inverted) || ret); |
453 | return ret; |
454 | } |
455 | |
456 | /* |
457 | * The X0,Y0,X1,Y1 values represent a trapezoidal fragment whose |
458 | * coverage must be accounted for in the accum buffer. |
459 | * |
460 | * All four values are assumed to fall within (or on the edge of) |
461 | * a single pixel. |
462 | * |
463 | * The trapezoid area is accumulated into the proper element of |
464 | * the accum buffer and the remainder of the "slice height" is |
465 | * accumulated into the element to its right. |
466 | */ |
467 | #define INSERT_ACCUM(pACCUM, IMIN, IMAX, X0, Y0, X1, Y1, CX1, CX2, MULT) \ |
468 | do { \ |
469 | jdouble xmid = ((X0) + (X1)) * 0.5; \ |
470 | if (xmid <= (CX2)) { \ |
471 | jdouble sliceh = ((Y1) - (Y0)); \ |
472 | jdouble slicearea; \ |
473 | jint i; \ |
474 | if (xmid < (CX1)) { \ |
475 | /* Accumulate the entire slice height into accum[0]. */ \ |
476 | i = 0; \ |
477 | slicearea = sliceh; \ |
478 | } else { \ |
479 | jdouble xpos = floor(xmid); \ |
480 | i = ((jint) xpos) - (CX1); \ |
481 | slicearea = (xpos+1-xmid) * sliceh; \ |
482 | } \ |
483 | if (IMIN > i) { \ |
484 | IMIN = i; \ |
485 | } \ |
486 | (pACCUM)[i++] += (jfloat) ((MULT) * slicearea); \ |
487 | (pACCUM)[i++] += (jfloat) ((MULT) * (sliceh - slicearea)); \ |
488 | if (IMAX < i) { \ |
489 | IMAX = i; \ |
490 | } \ |
491 | } \ |
492 | } while (0) |
493 | |
494 | /* |
495 | * Accumulate the contributions for a given edge crossing a given |
496 | * scan line into the corresponding entries of the accum buffer. |
497 | * CY1 is the Y coordinate of the top edge of the scanline and CY2 |
498 | * is equal to (CY1 + 1) and is the Y coordinate of the bottom edge |
499 | * of the scanline. CX1 and CX2 are the left and right edges of the |
500 | * clip (or area of interest) being rendered. |
501 | * |
502 | * The edge is processed from the top edge to the bottom edge and |
503 | * a single pixel column at a time. |
504 | */ |
505 | #define ACCUM_EDGE(pEDGE, pACCUM, IMIN, IMAX, CX1, CY1, CX2, CY2) \ |
506 | do { \ |
507 | jdouble x, y, xnext, ynext, xlast, ylast, dx, dy, mult; \ |
508 | y = (pEDGE)->y; \ |
509 | dy = (pEDGE)->celldy; \ |
510 | ylast = (pEDGE)->ybot; \ |
511 | if (ylast <= (CY1) || y >= (CY2) || dy == 0.0) { \ |
512 | break; \ |
513 | } \ |
514 | x = (pEDGE)->x; \ |
515 | dx = (pEDGE)->celldx; \ |
516 | if (ylast > (CY2)) { \ |
517 | ylast = (CY2); \ |
518 | xlast = (pEDGE)->xnexty; \ |
519 | } else { \ |
520 | xlast = (pEDGE)->xbot; \ |
521 | } \ |
522 | xnext = (pEDGE)->xnextx; \ |
523 | ynext = (pEDGE)->ynextx; \ |
524 | mult = ((pEDGE)->isTrailing) ? -1.0 : 1.0; \ |
525 | while (ynext <= ylast) { \ |
526 | INSERT_ACCUM(pACCUM, IMIN, IMAX, \ |
527 | x, y, xnext, ynext, \ |
528 | CX1, CX2, mult); \ |
529 | x = xnext; \ |
530 | y = ynext; \ |
531 | xnext += dx; \ |
532 | ynext += dy; \ |
533 | } \ |
534 | (pEDGE)->ynextx = ynext; \ |
535 | (pEDGE)->xnextx = xnext; \ |
536 | INSERT_ACCUM(pACCUM, IMIN, IMAX, \ |
537 | x, y, xlast, ylast, \ |
538 | CX1, CX2, mult); \ |
539 | (pEDGE)->x = xlast; \ |
540 | (pEDGE)->y = ylast; \ |
541 | (pEDGE)->xnexty = xlast + (pEDGE)->linedx; \ |
542 | } while(0) |
543 | |
544 | /* Main function to fill a single Parallelogram */ |
545 | static void |
546 | fillAAPgram(NativePrimitive *pPrim, SurfaceDataRasInfo *pRasInfo, |
547 | CompositeInfo *pCompInfo, jint color, unsigned char *pMask, |
548 | void *pDst, |
549 | jdouble x1, jdouble y1, |
550 | jdouble dx1, jdouble dy1, |
551 | jdouble dx2, jdouble dy2) |
552 | { |
553 | jint cx1 = pRasInfo->bounds.x1; |
554 | jint cy1 = pRasInfo->bounds.y1; |
555 | jint cx2 = pRasInfo->bounds.x2; |
556 | jint cy2 = pRasInfo->bounds.y2; |
557 | jint width = cx2 - cx1; |
558 | EdgeInfo edges[4]; |
559 | jfloat localaccum[MASK_BUF_LEN + 1]; |
560 | jfloat *pAccum; |
561 | |
562 | if (!storePgram(edges + 0, edges + 2, |
563 | x1, y1, dx1, dy1, dx2, dy2, |
564 | cx1, cy1, cx2, cy2, |
565 | JNI_FALSE)) |
566 | { |
567 | return; |
568 | } |
569 | |
570 | pAccum = ((width > MASK_BUF_LEN) |
571 | ? malloc((width + 1) * sizeof(jfloat)) |
572 | : localaccum); |
573 | if (pAccum == NULL) { |
574 | return; |
575 | } |
576 | memset(pAccum, 0, (width+1) * sizeof(jfloat)); |
577 | |
578 | while (cy1 < cy2) { |
579 | jint lmin, lmax, rmin, rmax; |
580 | jint moff, x; |
581 | jdouble accum; |
582 | unsigned char lastcov; |
583 | |
584 | lmin = rmin = width + 2; |
585 | lmax = rmax = 0; |
586 | ACCUM_EDGE(&edges[0], pAccum, lmin, lmax, |
587 | cx1, cy1, cx2, cy1+1); |
588 | ACCUM_EDGE(&edges[1], pAccum, lmin, lmax, |
589 | cx1, cy1, cx2, cy1+1); |
590 | ACCUM_EDGE(&edges[2], pAccum, rmin, rmax, |
591 | cx1, cy1, cx2, cy1+1); |
592 | ACCUM_EDGE(&edges[3], pAccum, rmin, rmax, |
593 | cx1, cy1, cx2, cy1+1); |
594 | if (lmax > width) { |
595 | lmax = width; /* Extra col has data we do not need. */ |
596 | } |
597 | if (rmax > width) { |
598 | rmax = width; /* Extra col has data we do not need. */ |
599 | } |
600 | /* If ranges overlap, handle both in the first pass. */ |
601 | if (rmin <= lmax) { |
602 | lmax = rmax; |
603 | } |
604 | |
605 | x = lmin; |
606 | accum = 0.0; |
607 | moff = 0; |
608 | lastcov = 0; |
609 | while (x < lmax) { |
610 | accum += pAccum[x]; |
611 | pAccum[x] = 0.0f; |
612 | pMask[moff++] = lastcov = DblToMask(accum); |
613 | x++; |
614 | } |
615 | /* Check for a solid center section. */ |
616 | if (lastcov == 0xFF) { |
617 | jint endx; |
618 | void *pRow; |
619 | |
620 | /* First process the existing partial coverage data. */ |
621 | if (moff > 0) { |
622 | pRow = PtrCoord(pDst, x-moff, pRasInfo->pixelStride, 0, 0); |
623 | (*pPrim->funcs.maskfill)(pRow, |
624 | pMask, 0, 0, |
625 | moff, 1, |
626 | color, pRasInfo, |
627 | pPrim, pCompInfo); |
628 | moff = 0; |
629 | } |
630 | |
631 | /* Where does the center section end? */ |
632 | /* If there is no right AA edge in the accum buffer, then */ |
633 | /* the right edge was beyond the clip, so fill out to width */ |
634 | endx = (rmin < rmax) ? rmin : width; |
635 | if (x < endx) { |
636 | pRow = PtrCoord(pDst, x, pRasInfo->pixelStride, 0, 0); |
637 | (*pPrim->funcs.maskfill)(pRow, |
638 | NULL, 0, 0, |
639 | endx - x, 1, |
640 | color, pRasInfo, |
641 | pPrim, pCompInfo); |
642 | x = endx; |
643 | } |
644 | } else if (lastcov > 0 && rmin >= rmax) { |
645 | /* We are not at 0 coverage, but there is no right edge, */ |
646 | /* force a right edge so we process pixels out to width. */ |
647 | rmax = width; |
648 | } |
649 | /* The following loop will process the right AA edge and/or any */ |
650 | /* partial coverage center section not processed above. */ |
651 | while (x < rmax) { |
652 | accum += pAccum[x]; |
653 | pAccum[x] = 0.0f; |
654 | pMask[moff++] = DblToMask(accum); |
655 | x++; |
656 | } |
657 | if (moff > 0) { |
658 | void *pRow = PtrCoord(pDst, x-moff, pRasInfo->pixelStride, 0, 0); |
659 | (*pPrim->funcs.maskfill)(pRow, |
660 | pMask, 0, 0, |
661 | moff, 1, |
662 | color, pRasInfo, |
663 | pPrim, pCompInfo); |
664 | } |
665 | pDst = PtrAddBytes(pDst, pRasInfo->scanStride); |
666 | cy1++; |
667 | } |
668 | if (pAccum != localaccum) { |
669 | free(pAccum); |
670 | } |
671 | } |
672 | |
673 | /* |
674 | * Class: sun_java2d_loops_MaskFill |
675 | * Method: FillAAPgram |
676 | * Signature: (Lsun/java2d/SunGraphics2D;Lsun/java2d/SurfaceData;Ljava/awt/Composite;DDDDDD)V |
677 | */ |
678 | JNIEXPORT void JNICALL |
679 | Java_sun_java2d_loops_MaskFill_FillAAPgram |
680 | (JNIEnv *env, jobject self, |
681 | jobject sg2d, jobject sData, jobject comp, |
682 | jdouble x0, jdouble y0, |
683 | jdouble dx1, jdouble dy1, |
684 | jdouble dx2, jdouble dy2) |
685 | { |
686 | SurfaceDataOps *sdOps; |
687 | SurfaceDataRasInfo rasInfo; |
688 | NativePrimitive *pPrim; |
689 | CompositeInfo compInfo; |
690 | jint ix1, iy1, ix2, iy2; |
691 | |
692 | if ((dy1 == 0 && dx1 == 0) || (dy2 == 0 && dx2 == 0)) { |
693 | return; |
694 | } |
695 | |
696 | /* |
697 | * Sort parallelogram by y values, ensure that each delta vector |
698 | * has a non-negative y delta. |
699 | */ |
700 | SORT_PGRAM(x0, y0, dx1, dy1, dx2, dy2, ); |
701 | |
702 | PGRAM_MIN_MAX(ix1, ix2, x0, dx1, dx2, JNI_TRUE); |
703 | iy1 = (jint) floor(y0); |
704 | iy2 = (jint) ceil(y0 + dy1 + dy2); |
705 | |
706 | pPrim = GetNativePrim(env, self); |
707 | if (pPrim == NULL) { |
708 | return; |
709 | } |
710 | if (pPrim->pCompType->getCompInfo != NULL) { |
711 | (*pPrim->pCompType->getCompInfo)(env, &compInfo, comp); |
712 | } |
713 | |
714 | sdOps = SurfaceData_GetOps(env, sData); |
715 | if (sdOps == 0) { |
716 | return; |
717 | } |
718 | |
719 | GrPrim_Sg2dGetClip(env, sg2d, &rasInfo.bounds); |
720 | SurfaceData_IntersectBoundsXYXY(&rasInfo.bounds, ix1, iy1, ix2, iy2); |
721 | if (rasInfo.bounds.y2 <= rasInfo.bounds.y1 || |
722 | rasInfo.bounds.x2 <= rasInfo.bounds.x1) |
723 | { |
724 | return; |
725 | } |
726 | |
727 | if (sdOps->Lock(env, sdOps, &rasInfo, pPrim->dstflags) != SD_SUCCESS) { |
728 | return; |
729 | } |
730 | |
731 | ix1 = rasInfo.bounds.x1; |
732 | iy1 = rasInfo.bounds.y1; |
733 | ix2 = rasInfo.bounds.x2; |
734 | iy2 = rasInfo.bounds.y2; |
735 | if (ix2 > ix1 && iy2 > iy1) { |
736 | jint width = ix2 - ix1; |
737 | jint color = GrPrim_Sg2dGetEaRGB(env, sg2d); |
738 | unsigned char localmask[MASK_BUF_LEN]; |
739 | unsigned char *pMask = ((width > MASK_BUF_LEN) |
740 | ? malloc(width) |
741 | : localmask); |
742 | |
743 | sdOps->GetRasInfo(env, sdOps, &rasInfo); |
744 | if (rasInfo.rasBase != NULL && pMask != NULL) { |
745 | void *pDst = PtrCoord(rasInfo.rasBase, |
746 | ix1, rasInfo.pixelStride, |
747 | iy1, rasInfo.scanStride); |
748 | if (dy1 == 0 && dx2 == 0) { |
749 | if (dx1 < 0) { |
750 | // We sorted by Y above, but not by X |
751 | x0 += dx1; |
752 | dx1 = -dx1; |
753 | } |
754 | fillAARect(pPrim, &rasInfo, &compInfo, |
755 | color, pMask, pDst, |
756 | x0, y0, x0+dx1, y0+dy2); |
757 | } else if (dx1 == 0 && dy2 == 0) { |
758 | if (dx2 < 0) { |
759 | // We sorted by Y above, but not by X |
760 | x0 += dx2; |
761 | dx2 = -dx2; |
762 | } |
763 | fillAARect(pPrim, &rasInfo, &compInfo, |
764 | color, pMask, pDst, |
765 | x0, y0, x0+dx2, y0+dy1); |
766 | } else { |
767 | fillAAPgram(pPrim, &rasInfo, &compInfo, |
768 | color, pMask, pDst, |
769 | x0, y0, dx1, dy1, dx2, dy2); |
770 | } |
771 | } |
772 | SurfaceData_InvokeRelease(env, sdOps, &rasInfo); |
773 | if (pMask != NULL && pMask != localmask) { |
774 | free(pMask); |
775 | } |
776 | } |
777 | SurfaceData_InvokeUnlock(env, sdOps, &rasInfo); |
778 | } |
779 | |
780 | /* Main function to fill a double pair of (inner and outer) parallelograms */ |
781 | static void |
782 | drawAAPgram(NativePrimitive *pPrim, SurfaceDataRasInfo *pRasInfo, |
783 | CompositeInfo *pCompInfo, jint color, unsigned char *pMask, |
784 | void *pDst, |
785 | jdouble ox0, jdouble oy0, |
786 | jdouble dx1, jdouble dy1, |
787 | jdouble dx2, jdouble dy2, |
788 | jdouble ldx1, jdouble ldy1, |
789 | jdouble ldx2, jdouble ldy2) |
790 | { |
791 | jint cx1 = pRasInfo->bounds.x1; |
792 | jint cy1 = pRasInfo->bounds.y1; |
793 | jint cx2 = pRasInfo->bounds.x2; |
794 | jint cy2 = pRasInfo->bounds.y2; |
795 | jint width = cx2 - cx1; |
796 | EdgeInfo edges[8]; |
797 | jfloat localaccum[MASK_BUF_LEN + 1]; |
798 | jfloat *pAccum; |
799 | |
800 | if (!storePgram(edges + 0, edges + 6, |
801 | ox0, oy0, |
802 | dx1 + ldx1, dy1 + ldy1, |
803 | dx2 + ldx2, dy2 + ldy2, |
804 | cx1, cy1, cx2, cy2, |
805 | JNI_FALSE)) |
806 | { |
807 | /* If outer pgram does not contribute, then inner cannot either. */ |
808 | return; |
809 | } |
810 | storePgram(edges + 2, edges + 4, |
811 | ox0 + ldx1 + ldx2, oy0 + ldy1 + ldy2, |
812 | dx1 - ldx1, dy1 - ldy1, |
813 | dx2 - ldx2, dy2 - ldy2, |
814 | cx1, cy1, cx2, cy2, |
815 | JNI_TRUE); |
816 | |
817 | pAccum = ((width > MASK_BUF_LEN) |
818 | ? malloc((width + 1) * sizeof(jfloat)) |
819 | : localaccum); |
820 | if (pAccum == NULL) { |
821 | return; |
822 | } |
823 | memset(pAccum, 0, (width+1) * sizeof(jfloat)); |
824 | |
825 | while (cy1 < cy2) { |
826 | jint lmin, lmax, rmin, rmax; |
827 | jint moff, x; |
828 | jdouble accum; |
829 | unsigned char lastcov; |
830 | |
831 | lmin = rmin = width + 2; |
832 | lmax = rmax = 0; |
833 | ACCUM_EDGE(&edges[0], pAccum, lmin, lmax, |
834 | cx1, cy1, cx2, cy1+1); |
835 | ACCUM_EDGE(&edges[1], pAccum, lmin, lmax, |
836 | cx1, cy1, cx2, cy1+1); |
837 | ACCUM_EDGE(&edges[2], pAccum, lmin, lmax, |
838 | cx1, cy1, cx2, cy1+1); |
839 | ACCUM_EDGE(&edges[3], pAccum, lmin, lmax, |
840 | cx1, cy1, cx2, cy1+1); |
841 | ACCUM_EDGE(&edges[4], pAccum, rmin, rmax, |
842 | cx1, cy1, cx2, cy1+1); |
843 | ACCUM_EDGE(&edges[5], pAccum, rmin, rmax, |
844 | cx1, cy1, cx2, cy1+1); |
845 | ACCUM_EDGE(&edges[6], pAccum, rmin, rmax, |
846 | cx1, cy1, cx2, cy1+1); |
847 | ACCUM_EDGE(&edges[7], pAccum, rmin, rmax, |
848 | cx1, cy1, cx2, cy1+1); |
849 | if (lmax > width) { |
850 | lmax = width; /* Extra col has data we do not need. */ |
851 | } |
852 | if (rmax > width) { |
853 | rmax = width; /* Extra col has data we do not need. */ |
854 | } |
855 | /* If ranges overlap, handle both in the first pass. */ |
856 | if (rmin <= lmax) { |
857 | lmax = rmax; |
858 | } |
859 | |
860 | x = lmin; |
861 | accum = 0.0; |
862 | moff = 0; |
863 | lastcov = 0; |
864 | while (x < lmax) { |
865 | accum += pAccum[x]; |
866 | pAccum[x] = 0.0f; |
867 | pMask[moff++] = lastcov = DblToMask(accum); |
868 | x++; |
869 | } |
870 | /* Check for an empty or solidcenter section. */ |
871 | if (lastcov == 0 || lastcov == 0xFF) { |
872 | jint endx; |
873 | void *pRow; |
874 | |
875 | /* First process the existing partial coverage data. */ |
876 | if (moff > 0) { |
877 | pRow = PtrCoord(pDst, x-moff, pRasInfo->pixelStride, 0, 0); |
878 | (*pPrim->funcs.maskfill)(pRow, |
879 | pMask, 0, 0, |
880 | moff, 1, |
881 | color, pRasInfo, |
882 | pPrim, pCompInfo); |
883 | moff = 0; |
884 | } |
885 | |
886 | /* Where does the center section end? */ |
887 | /* If there is no right AA edge in the accum buffer, then */ |
888 | /* the right edge was beyond the clip, so fill out to width */ |
889 | endx = (rmin < rmax) ? rmin : width; |
890 | if (x < endx) { |
891 | if (lastcov == 0xFF) { |
892 | pRow = PtrCoord(pDst, x, pRasInfo->pixelStride, 0, 0); |
893 | (*pPrim->funcs.maskfill)(pRow, |
894 | NULL, 0, 0, |
895 | endx - x, 1, |
896 | color, pRasInfo, |
897 | pPrim, pCompInfo); |
898 | } |
899 | x = endx; |
900 | } |
901 | } else if (rmin >= rmax) { |
902 | /* We are not at 0 coverage, but there is no right edge, */ |
903 | /* force a right edge so we process pixels out to width. */ |
904 | rmax = width; |
905 | } |
906 | /* The following loop will process the right AA edge and/or any */ |
907 | /* partial coverage center section not processed above. */ |
908 | while (x < rmax) { |
909 | accum += pAccum[x]; |
910 | pAccum[x] = 0.0f; |
911 | pMask[moff++] = lastcov = DblToMask(accum); |
912 | x++; |
913 | } |
914 | if (moff > 0) { |
915 | void *pRow = PtrCoord(pDst, x-moff, pRasInfo->pixelStride, 0, 0); |
916 | (*pPrim->funcs.maskfill)(pRow, |
917 | pMask, 0, 0, |
918 | moff, 1, |
919 | color, pRasInfo, |
920 | pPrim, pCompInfo); |
921 | } |
922 | if (lastcov == 0xFF && x < width) { |
923 | void *pRow = PtrCoord(pDst, x, pRasInfo->pixelStride, 0, 0); |
924 | (*pPrim->funcs.maskfill)(pRow, |
925 | NULL, 0, 0, |
926 | width - x, 1, |
927 | color, pRasInfo, |
928 | pPrim, pCompInfo); |
929 | } |
930 | pDst = PtrAddBytes(pDst, pRasInfo->scanStride); |
931 | cy1++; |
932 | } |
933 | if (pAccum != localaccum) { |
934 | free(pAccum); |
935 | } |
936 | } |
937 | |
938 | /* |
939 | * Class: sun_java2d_loops_MaskFill |
940 | * Method: DrawAAPgram |
941 | * Signature: (Lsun/java2d/SunGraphics2D;Lsun/java2d/SurfaceData;Ljava/awt/Composite;DDDDDDDD)V |
942 | */ |
943 | JNIEXPORT void JNICALL |
944 | Java_sun_java2d_loops_MaskFill_DrawAAPgram |
945 | (JNIEnv *env, jobject self, |
946 | jobject sg2d, jobject sData, jobject comp, |
947 | jdouble x0, jdouble y0, |
948 | jdouble dx1, jdouble dy1, |
949 | jdouble dx2, jdouble dy2, |
950 | jdouble lw1, jdouble lw2) |
951 | { |
952 | SurfaceDataOps *sdOps; |
953 | SurfaceDataRasInfo rasInfo; |
954 | NativePrimitive *pPrim; |
955 | CompositeInfo compInfo; |
956 | jint ix1, iy1, ix2, iy2; |
957 | jdouble ldx1, ldy1, ldx2, ldy2; |
958 | jdouble ox0, oy0; |
959 | |
960 | if ((dy1 == 0 && dx1 == 0) || (dy2 == 0 && dx2 == 0)) { |
961 | return; |
962 | } |
963 | |
964 | /* |
965 | * Sort parallelogram by y values, ensure that each delta vector |
966 | * has a non-negative y delta. |
967 | */ |
968 | SORT_PGRAM(x0, y0, dx1, dy1, dx2, dy2, |
969 | v = lw1; lw1 = lw2; lw2 = v;); |
970 | |
971 | // dx,dy for line width in the "1" and "2" directions. |
972 | ldx1 = dx1 * lw1; |
973 | ldy1 = dy1 * lw1; |
974 | ldx2 = dx2 * lw2; |
975 | ldy2 = dy2 * lw2; |
976 | |
977 | // calculate origin of the outer parallelogram |
978 | ox0 = x0 - (ldx1 + ldx2) / 2.0; |
979 | oy0 = y0 - (ldy1 + ldy2) / 2.0; |
980 | |
981 | if (lw1 >= 1.0 || lw2 >= 1.0) { |
982 | /* Only need to fill an outer pgram if the interior no longer |
983 | * has a hole in it (i.e. if either of the line width ratios |
984 | * were greater than or equal to 1.0). |
985 | */ |
986 | Java_sun_java2d_loops_MaskFill_FillAAPgram(env, self, |
987 | sg2d, sData, comp, |
988 | ox0, oy0, |
989 | dx1 + ldx1, dy1 + ldy1, |
990 | dx2 + ldx2, dy2 + ldy2); |
991 | return; |
992 | } |
993 | |
994 | PGRAM_MIN_MAX(ix1, ix2, ox0, dx1+ldx1, dx2+ldx2, JNI_TRUE); |
995 | iy1 = (jint) floor(oy0); |
996 | iy2 = (jint) ceil(oy0 + dy1 + ldy1 + dy2 + ldy2); |
997 | |
998 | pPrim = GetNativePrim(env, self); |
999 | if (pPrim == NULL) { |
1000 | return; |
1001 | } |
1002 | if (pPrim->pCompType->getCompInfo != NULL) { |
1003 | (*pPrim->pCompType->getCompInfo)(env, &compInfo, comp); |
1004 | } |
1005 | |
1006 | sdOps = SurfaceData_GetOps(env, sData); |
1007 | if (sdOps == 0) { |
1008 | return; |
1009 | } |
1010 | |
1011 | GrPrim_Sg2dGetClip(env, sg2d, &rasInfo.bounds); |
1012 | SurfaceData_IntersectBoundsXYXY(&rasInfo.bounds, ix1, iy1, ix2, iy2); |
1013 | if (rasInfo.bounds.y2 <= rasInfo.bounds.y1 || |
1014 | rasInfo.bounds.x2 <= rasInfo.bounds.x1) |
1015 | { |
1016 | return; |
1017 | } |
1018 | |
1019 | if (sdOps->Lock(env, sdOps, &rasInfo, pPrim->dstflags) != SD_SUCCESS) { |
1020 | return; |
1021 | } |
1022 | |
1023 | ix1 = rasInfo.bounds.x1; |
1024 | iy1 = rasInfo.bounds.y1; |
1025 | ix2 = rasInfo.bounds.x2; |
1026 | iy2 = rasInfo.bounds.y2; |
1027 | if (ix2 > ix1 && iy2 > iy1) { |
1028 | jint width = ix2 - ix1; |
1029 | jint color = GrPrim_Sg2dGetEaRGB(env, sg2d); |
1030 | unsigned char localmask[MASK_BUF_LEN]; |
1031 | unsigned char *pMask = ((width > MASK_BUF_LEN) |
1032 | ? malloc(width) |
1033 | : localmask); |
1034 | |
1035 | sdOps->GetRasInfo(env, sdOps, &rasInfo); |
1036 | if (rasInfo.rasBase != NULL && pMask != NULL) { |
1037 | void *pDst = PtrCoord(rasInfo.rasBase, |
1038 | ix1, rasInfo.pixelStride, |
1039 | iy1, rasInfo.scanStride); |
1040 | /* |
1041 | * NOTE: aligned rects could probably be drawn |
1042 | * even faster with a little work here. |
1043 | * if (dy1 == 0 && dx2 == 0) { |
1044 | * drawAARect(pPrim, &rasInfo, &compInfo, |
1045 | * color, pMask, pDst, |
1046 | * ox0, oy0, ox0+dx1+ldx1, oy0+dy2+ldy2, ldx1, ldy2); |
1047 | * } else if (dx1 == 0 && dy2 == 0) { |
1048 | * drawAARect(pPrim, &rasInfo, &compInfo, |
1049 | * color, pMask, pDst, |
1050 | * ox0, oy0, ox0+dx2+ldx2, oy0+dy1+ldy1, ldx2, ldy1); |
1051 | * } else { |
1052 | */ |
1053 | drawAAPgram(pPrim, &rasInfo, &compInfo, |
1054 | color, pMask, pDst, |
1055 | ox0, oy0, |
1056 | dx1, dy1, dx2, dy2, |
1057 | ldx1, ldy1, ldx2, ldy2); |
1058 | /* |
1059 | * } |
1060 | */ |
1061 | } |
1062 | SurfaceData_InvokeRelease(env, sdOps, &rasInfo); |
1063 | if (pMask != NULL && pMask != localmask) { |
1064 | free(pMask); |
1065 | } |
1066 | } |
1067 | SurfaceData_InvokeUnlock(env, sdOps, &rasInfo); |
1068 | } |
1069 | |