| 1 | /*********************************************************** |
| 2 | |
| 3 | Copyright 1987, 1998 The Open Group |
| 4 | |
| 5 | Permission to use, copy, modify, distribute, and sell this software and its |
| 6 | documentation for any purpose is hereby granted without fee, provided that |
| 7 | the above copyright notice appear in all copies and that both that |
| 8 | copyright notice and this permission notice appear in supporting |
| 9 | documentation. |
| 10 | |
| 11 | The above copyright notice and this permission notice shall be included in |
| 12 | all copies or substantial portions of the Software. |
| 13 | |
| 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
| 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
| 17 | OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN |
| 18 | AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN |
| 19 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
| 20 | |
| 21 | Except as contained in this notice, the name of The Open Group shall not be |
| 22 | used in advertising or otherwise to promote the sale, use or other dealings |
| 23 | in this Software without prior written authorization from The Open Group. |
| 24 | |
| 25 | Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts. |
| 26 | |
| 27 | All Rights Reserved |
| 28 | |
| 29 | Permission to use, copy, modify, and distribute this software and its |
| 30 | documentation for any purpose and without fee is hereby granted, |
| 31 | provided that the above copyright notice appear in all copies and that |
| 32 | both that copyright notice and this permission notice appear in |
| 33 | supporting documentation, and that the name of Digital not be |
| 34 | used in advertising or publicity pertaining to distribution of the |
| 35 | software without specific, written prior permission. |
| 36 | |
| 37 | DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING |
| 38 | ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL |
| 39 | DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR |
| 40 | ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, |
| 41 | WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, |
| 42 | ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS |
| 43 | SOFTWARE. |
| 44 | |
| 45 | ******************************************************************/ |
| 46 | #ifdef HAVE_DIX_CONFIG_H |
| 47 | #include <dix-config.h> |
| 48 | #endif |
| 49 | |
| 50 | #include <X11/X.h> |
| 51 | |
| 52 | #include "misc.h" |
| 53 | #include "scrnintstr.h" |
| 54 | #include "gcstruct.h" |
| 55 | #include "windowstr.h" |
| 56 | #include "pixmap.h" |
| 57 | #include "mi.h" |
| 58 | #include "miline.h" |
| 59 | |
| 60 | /* Draw lineSolid, fillStyle-independent zero width lines. |
| 61 | * |
| 62 | * Must keep X and Y coordinates in "ints" at least until after they're |
| 63 | * translated and clipped to accomodate CoordModePrevious lines with very |
| 64 | * large coordinates. |
| 65 | * |
| 66 | * Draws the same pixels regardless of sign(dx) or sign(dy). |
| 67 | * |
| 68 | * Ken Whaley |
| 69 | * |
| 70 | */ |
| 71 | |
| 72 | /* largest positive value that can fit into a component of a point. |
| 73 | * Assumes that the point structure is {type x, y;} where type is |
| 74 | * a signed type. |
| 75 | */ |
| 76 | #define MAX_COORDINATE ((1 << (((sizeof(DDXPointRec) >> 1) << 3) - 1)) - 1) |
| 77 | |
| 78 | #define MI_OUTPUT_POINT(xx, yy)\ |
| 79 | {\ |
| 80 | if ( !new_span && yy == current_y)\ |
| 81 | {\ |
| 82 | if (xx < spans->x)\ |
| 83 | spans->x = xx;\ |
| 84 | ++*widths;\ |
| 85 | }\ |
| 86 | else\ |
| 87 | {\ |
| 88 | ++Nspans;\ |
| 89 | ++spans;\ |
| 90 | ++widths;\ |
| 91 | spans->x = xx;\ |
| 92 | spans->y = yy;\ |
| 93 | *widths = 1;\ |
| 94 | current_y = yy;\ |
| 95 | new_span = FALSE;\ |
| 96 | }\ |
| 97 | } |
| 98 | |
| 99 | void |
| 100 | miZeroLine(DrawablePtr pDraw, GCPtr pGC, int mode, /* Origin or Previous */ |
| 101 | int npt, /* number of points */ |
| 102 | DDXPointPtr pptInit) |
| 103 | { |
| 104 | int Nspans, current_y = 0; |
| 105 | DDXPointPtr ppt; |
| 106 | DDXPointPtr pspanInit, spans; |
| 107 | int *pwidthInit, *widths, list_len; |
| 108 | int xleft, ytop, xright, ybottom; |
| 109 | int new_x1, new_y1, new_x2, new_y2; |
| 110 | int x = 0, y = 0, x1, y1, x2, y2, xstart, ystart; |
| 111 | int oc1, oc2; |
| 112 | int result; |
| 113 | int pt1_clipped, pt2_clipped = 0; |
| 114 | Bool new_span; |
| 115 | int signdx, signdy; |
| 116 | int clipdx, clipdy; |
| 117 | int width, height; |
| 118 | int adx, ady; |
| 119 | int octant; |
| 120 | unsigned int bias = miGetZeroLineBias(pDraw->pScreen); |
| 121 | int e, e1, e2, e3; /* Bresenham error terms */ |
| 122 | int length; /* length of lines == # of pixels on major axis */ |
| 123 | |
| 124 | xleft = pDraw->x; |
| 125 | ytop = pDraw->y; |
| 126 | xright = pDraw->x + pDraw->width - 1; |
| 127 | ybottom = pDraw->y + pDraw->height - 1; |
| 128 | |
| 129 | if (!pGC->miTranslate) { |
| 130 | /* do everything in drawable-relative coordinates */ |
| 131 | xleft = 0; |
| 132 | ytop = 0; |
| 133 | xright -= pDraw->x; |
| 134 | ybottom -= pDraw->y; |
| 135 | } |
| 136 | |
| 137 | /* it doesn't matter whether we're in drawable or screen coordinates, |
| 138 | * FillSpans simply cannot take starting coordinates outside of the |
| 139 | * range of a DDXPointRec component. |
| 140 | */ |
| 141 | if (xright > MAX_COORDINATE) |
| 142 | xright = MAX_COORDINATE; |
| 143 | if (ybottom > MAX_COORDINATE) |
| 144 | ybottom = MAX_COORDINATE; |
| 145 | |
| 146 | /* since we're clipping to the drawable's boundaries & coordinate |
| 147 | * space boundaries, we're guaranteed that the larger of width/height |
| 148 | * is the longest span we'll need to output |
| 149 | */ |
| 150 | width = xright - xleft + 1; |
| 151 | height = ybottom - ytop + 1; |
| 152 | list_len = (height >= width) ? height : width; |
| 153 | pspanInit = malloc(list_len * sizeof(DDXPointRec)); |
| 154 | pwidthInit = malloc(list_len * sizeof(int)); |
| 155 | if (!pspanInit || !pwidthInit) { |
| 156 | free(pspanInit); |
| 157 | free(pwidthInit); |
| 158 | return; |
| 159 | } |
| 160 | Nspans = 0; |
| 161 | new_span = TRUE; |
| 162 | spans = pspanInit - 1; |
| 163 | widths = pwidthInit - 1; |
| 164 | ppt = pptInit; |
| 165 | |
| 166 | xstart = ppt->x; |
| 167 | ystart = ppt->y; |
| 168 | if (pGC->miTranslate) { |
| 169 | xstart += pDraw->x; |
| 170 | ystart += pDraw->y; |
| 171 | } |
| 172 | |
| 173 | /* x2, y2, oc2 copied to x1, y1, oc1 at top of loop to simplify |
| 174 | * iteration logic |
| 175 | */ |
| 176 | x2 = xstart; |
| 177 | y2 = ystart; |
| 178 | oc2 = 0; |
| 179 | MIOUTCODES(oc2, x2, y2, xleft, ytop, xright, ybottom); |
| 180 | |
| 181 | while (--npt > 0) { |
| 182 | if (Nspans > 0) |
| 183 | (*pGC->ops->FillSpans) (pDraw, pGC, Nspans, pspanInit, |
| 184 | pwidthInit, FALSE); |
| 185 | Nspans = 0; |
| 186 | new_span = TRUE; |
| 187 | spans = pspanInit - 1; |
| 188 | widths = pwidthInit - 1; |
| 189 | |
| 190 | x1 = x2; |
| 191 | y1 = y2; |
| 192 | oc1 = oc2; |
| 193 | ++ppt; |
| 194 | |
| 195 | x2 = ppt->x; |
| 196 | y2 = ppt->y; |
| 197 | if (pGC->miTranslate && (mode != CoordModePrevious)) { |
| 198 | x2 += pDraw->x; |
| 199 | y2 += pDraw->y; |
| 200 | } |
| 201 | else if (mode == CoordModePrevious) { |
| 202 | x2 += x1; |
| 203 | y2 += y1; |
| 204 | } |
| 205 | |
| 206 | oc2 = 0; |
| 207 | MIOUTCODES(oc2, x2, y2, xleft, ytop, xright, ybottom); |
| 208 | |
| 209 | CalcLineDeltas(x1, y1, x2, y2, adx, ady, signdx, signdy, 1, 1, octant); |
| 210 | |
| 211 | if (adx > ady) { |
| 212 | e1 = ady << 1; |
| 213 | e2 = e1 - (adx << 1); |
| 214 | e = e1 - adx; |
| 215 | length = adx; /* don't draw endpoint in main loop */ |
| 216 | |
| 217 | FIXUP_ERROR(e, octant, bias); |
| 218 | |
| 219 | new_x1 = x1; |
| 220 | new_y1 = y1; |
| 221 | new_x2 = x2; |
| 222 | new_y2 = y2; |
| 223 | pt1_clipped = 0; |
| 224 | pt2_clipped = 0; |
| 225 | |
| 226 | if ((oc1 | oc2) != 0) { |
| 227 | result = miZeroClipLine(xleft, ytop, xright, ybottom, |
| 228 | &new_x1, &new_y1, &new_x2, &new_y2, |
| 229 | adx, ady, |
| 230 | &pt1_clipped, &pt2_clipped, |
| 231 | octant, bias, oc1, oc2); |
| 232 | if (result == -1) |
| 233 | continue; |
| 234 | |
| 235 | length = abs(new_x2 - new_x1); |
| 236 | |
| 237 | /* if we've clipped the endpoint, always draw the full length |
| 238 | * of the segment, because then the capstyle doesn't matter |
| 239 | */ |
| 240 | if (pt2_clipped) |
| 241 | length++; |
| 242 | |
| 243 | if (pt1_clipped) { |
| 244 | /* must calculate new error terms */ |
| 245 | clipdx = abs(new_x1 - x1); |
| 246 | clipdy = abs(new_y1 - y1); |
| 247 | e += (clipdy * e2) + ((clipdx - clipdy) * e1); |
| 248 | } |
| 249 | } |
| 250 | |
| 251 | /* draw the segment */ |
| 252 | |
| 253 | x = new_x1; |
| 254 | y = new_y1; |
| 255 | |
| 256 | e3 = e2 - e1; |
| 257 | e = e - e1; |
| 258 | |
| 259 | while (length--) { |
| 260 | MI_OUTPUT_POINT(x, y); |
| 261 | e += e1; |
| 262 | if (e >= 0) { |
| 263 | y += signdy; |
| 264 | e += e3; |
| 265 | } |
| 266 | x += signdx; |
| 267 | } |
| 268 | } |
| 269 | else { /* Y major line */ |
| 270 | |
| 271 | e1 = adx << 1; |
| 272 | e2 = e1 - (ady << 1); |
| 273 | e = e1 - ady; |
| 274 | length = ady; /* don't draw endpoint in main loop */ |
| 275 | |
| 276 | SetYMajorOctant(octant); |
| 277 | FIXUP_ERROR(e, octant, bias); |
| 278 | |
| 279 | new_x1 = x1; |
| 280 | new_y1 = y1; |
| 281 | new_x2 = x2; |
| 282 | new_y2 = y2; |
| 283 | pt1_clipped = 0; |
| 284 | pt2_clipped = 0; |
| 285 | |
| 286 | if ((oc1 | oc2) != 0) { |
| 287 | result = miZeroClipLine(xleft, ytop, xright, ybottom, |
| 288 | &new_x1, &new_y1, &new_x2, &new_y2, |
| 289 | adx, ady, |
| 290 | &pt1_clipped, &pt2_clipped, |
| 291 | octant, bias, oc1, oc2); |
| 292 | if (result == -1) |
| 293 | continue; |
| 294 | |
| 295 | length = abs(new_y2 - new_y1); |
| 296 | |
| 297 | /* if we've clipped the endpoint, always draw the full length |
| 298 | * of the segment, because then the capstyle doesn't matter |
| 299 | */ |
| 300 | if (pt2_clipped) |
| 301 | length++; |
| 302 | |
| 303 | if (pt1_clipped) { |
| 304 | /* must calculate new error terms */ |
| 305 | clipdx = abs(new_x1 - x1); |
| 306 | clipdy = abs(new_y1 - y1); |
| 307 | e += (clipdx * e2) + ((clipdy - clipdx) * e1); |
| 308 | } |
| 309 | } |
| 310 | |
| 311 | /* draw the segment */ |
| 312 | |
| 313 | x = new_x1; |
| 314 | y = new_y1; |
| 315 | |
| 316 | e3 = e2 - e1; |
| 317 | e = e - e1; |
| 318 | |
| 319 | while (length--) { |
| 320 | MI_OUTPUT_POINT(x, y); |
| 321 | e += e1; |
| 322 | if (e >= 0) { |
| 323 | x += signdx; |
| 324 | e += e3; |
| 325 | } |
| 326 | y += signdy; |
| 327 | } |
| 328 | } |
| 329 | } |
| 330 | |
| 331 | /* only do the capnotlast check on the last segment |
| 332 | * and only if the endpoint wasn't clipped. And then, if the last |
| 333 | * point is the same as the first point, do not draw it, unless the |
| 334 | * line is degenerate |
| 335 | */ |
| 336 | if ((!pt2_clipped) && (pGC->capStyle != CapNotLast) && |
| 337 | (((xstart != x2) || (ystart != y2)) || (ppt == pptInit + 1))) { |
| 338 | MI_OUTPUT_POINT(x, y); |
| 339 | } |
| 340 | |
| 341 | if (Nspans > 0) |
| 342 | (*pGC->ops->FillSpans) (pDraw, pGC, Nspans, pspanInit, |
| 343 | pwidthInit, FALSE); |
| 344 | |
| 345 | free(pwidthInit); |
| 346 | free(pspanInit); |
| 347 | } |
| 348 | |
| 349 | void |
| 350 | miZeroDashLine(DrawablePtr dst, GCPtr pgc, int mode, int nptInit, /* number of points in polyline */ |
| 351 | DDXPointRec * pptInit /* points in the polyline */ |
| 352 | ) |
| 353 | { |
| 354 | /* XXX kludge until real zero-width dash code is written */ |
| 355 | pgc->lineWidth = 1; |
| 356 | miWideDash(dst, pgc, mode, nptInit, pptInit); |
| 357 | pgc->lineWidth = 0; |
| 358 | } |