/* * Fill with non-standard X and Y stepping. * ptile is pdevc->colors.pattern.{m,p}_tile. * tbits_or_tmask is whichever of tbits and tmask is supplying * the tile size. * This implementation could be sped up considerably! */ static int tile_by_steps(tile_fill_state_t * ptfs, int x0, int y0, int w0, int h0, const gx_color_tile * ptile, const gx_strip_bitmap * tbits_or_tmask, int (*fill_proc) (const tile_fill_state_t * ptfs, int x, int y, int w, int h)) { int x1 = x0 + w0, y1 = y0 + h0; int i0, i1, j0, j1, i, j; gs_matrix step_matrix; /* translated by phase */ int code; ptfs->x0 = x0, ptfs->w0 = w0; ptfs->y0 = y0, ptfs->h0 = h0; step_matrix = ptile->step_matrix; step_matrix.tx -= ptfs->phase.x; step_matrix.ty -= ptfs->phase.y; { gs_rect bbox; /* bounding box in device space */ gs_rect ibbox; /* bounding box in stepping space */ double bbw = ptile->bbox.q.x - ptile->bbox.p.x; double bbh = ptile->bbox.q.y - ptile->bbox.p.y; double u0, v0, u1, v1; bbox.p.x = x0, bbox.p.y = y0; bbox.q.x = x1, bbox.q.y = y1; gs_bbox_transform_inverse(&bbox, &step_matrix, &ibbox); if_debug10('T', "[T]x,y=(%d,%d) w,h=(%d,%d) => (%g,%g),(%g,%g), offset=(%g,%g)\n", x0, y0, w0, h0, ibbox.p.x, ibbox.p.y, ibbox.q.x, ibbox.q.y, step_matrix.tx, step_matrix.ty); /* * If the pattern is partly transparent and XStep/YStep is smaller * than the device space BBox, we need to ensure that we cover * each pixel of the rectangle being filled with *every* pattern * that overlaps it, not just *some* pattern copy. */ u0 = ibbox.p.x - max(ptile->bbox.p.x, 0) - 0.000001; v0 = ibbox.p.y - max(ptile->bbox.p.y, 0) - 0.000001; u1 = ibbox.q.x - min(ptile->bbox.q.x, 0) + 0.000001; v1 = ibbox.q.y - min(ptile->bbox.q.y, 0) + 0.000001; if (!ptile->is_simple) u0 -= bbw, v0 -= bbh, u1 += bbw, v1 += bbh; i0 = (int)fastfloor(u0); j0 = (int)fastfloor(v0); i1 = (int)ceil(u1); j1 = (int)ceil(v1); } if_debug4('T', "[T]i=(%d,%d) j=(%d,%d)\n", i0, i1, j0, j1); for (i = i0; i < i1; i++) for (j = j0; j < j1; j++) { int x = (int)fastfloor(step_matrix.xx * i + step_matrix.yx * j + step_matrix.tx); int y = (int)fastfloor(step_matrix.xy * i + step_matrix.yy * j + step_matrix.ty); int w = tbits_or_tmask->size.x; int h = tbits_or_tmask->size.y; int xoff, yoff; if_debug4('T', "[T]i=%d j=%d x,y=(%d,%d)", i, j, x, y); if (x < x0) xoff = x0 - x, x = x0, w -= xoff; else xoff = 0; if (y < y0) yoff = y0 - y, y = y0, h -= yoff; else yoff = 0; if (x + w > x1) w = x1 - x; if (y + h > y1) h = y1 - y; if_debug6('T', "=>(%d,%d) w,h=(%d,%d) x/yoff=(%d,%d)\n", x, y, w, h, xoff, yoff); if (w > 0 && h > 0) { if (ptfs->pcdev == (gx_device *) & ptfs->cdev) tile_clip_set_phase(&ptfs->cdev, imod(xoff - x, ptfs->tmask->rep_width), imod(yoff - y, ptfs->tmask->rep_height)); /* Set the offsets for colored pattern fills */ ptfs->xoff = xoff; ptfs->yoff = yoff; code = (*fill_proc) (ptfs, x, y, w, h); if (code < 0) return code; } } return 0; }
/* * This is somewhat a clone of the tile_by_steps function but one * that performs filling from and to pdf14dev (transparency) buffers. * At some point it may be desirable to do some optimization here. */ static int tile_by_steps_trans(tile_fill_trans_state_t * ptfs, int x0, int y0, int w0, int h0, gx_pattern_trans_t *fill_trans_buffer, const gx_color_tile * ptile) { int x1 = x0 + w0, y1 = y0 + h0; int i0, i1, j0, j1, i, j; gs_matrix step_matrix; /* translated by phase */ gx_pattern_trans_t *ptrans_pat = ptile->ttrans; ptfs->x0 = x0, ptfs->w0 = w0; ptfs->y0 = y0, ptfs->h0 = h0; step_matrix = ptile->step_matrix; step_matrix.tx -= ptfs->phase.x; step_matrix.ty -= ptfs->phase.y; { gs_rect bbox; /* bounding box in device space */ gs_rect ibbox; /* bounding box in stepping space */ double bbw = ptile->bbox.q.x - ptile->bbox.p.x; double bbh = ptile->bbox.q.y - ptile->bbox.p.y; double u0, v0, u1, v1; bbox.p.x = x0, bbox.p.y = y0; bbox.q.x = x1, bbox.q.y = y1; gs_bbox_transform_inverse(&bbox, &step_matrix, &ibbox); if_debug10('T', "[T]x,y=(%d,%d) w,h=(%d,%d) => (%g,%g),(%g,%g), offset=(%g,%g)\n", x0, y0, w0, h0, ibbox.p.x, ibbox.p.y, ibbox.q.x, ibbox.q.y, step_matrix.tx, step_matrix.ty); /* * If the pattern is partly transparent and XStep/YStep is smaller * than the device space BBox, we need to ensure that we cover * each pixel of the rectangle being filled with *every* pattern * that overlaps it, not just *some* pattern copy. */ u0 = ibbox.p.x - max(ptile->bbox.p.x, 0) - 0.000001; v0 = ibbox.p.y - max(ptile->bbox.p.y, 0) - 0.000001; u1 = ibbox.q.x - min(ptile->bbox.q.x, 0) + 0.000001; v1 = ibbox.q.y - min(ptile->bbox.q.y, 0) + 0.000001; if (!ptile->is_simple) u0 -= bbw, v0 -= bbh, u1 += bbw, v1 += bbh; i0 = (int)fastfloor(u0); j0 = (int)fastfloor(v0); i1 = (int)ceil(u1); j1 = (int)ceil(v1); } if_debug4('T', "[T]i=(%d,%d) j=(%d,%d)\n", i0, i1, j0, j1); for (i = i0; i < i1; i++) for (j = j0; j < j1; j++) { int x = (int)fastfloor(step_matrix.xx * i + step_matrix.yx * j + step_matrix.tx); int y = (int)fastfloor(step_matrix.xy * i + step_matrix.yy * j + step_matrix.ty); int w = ptrans_pat->width; int h = ptrans_pat->height; int xoff, yoff; int px, py; if_debug4('T', "[T]i=%d j=%d x,y=(%d,%d)", i, j, x, y); if (x < x0) xoff = x0 - x, x = x0, w -= xoff; else xoff = 0; if (y < y0) yoff = y0 - y, y = y0, h -= yoff; else yoff = 0; if (x + w > x1) w = x1 - x; if (y + h > y1) h = y1 - y; if_debug6('T', "=>(%d,%d) w,h=(%d,%d) x/yoff=(%d,%d)\n", x, y, w, h, xoff, yoff); if (w > 0 && h > 0) { px = imod(xoff - x, ptile->ttrans->width); py = imod(yoff - y, ptile->ttrans->height); /* Set the offsets for colored pattern fills */ ptfs->xoff = xoff; ptfs->yoff = yoff; /* We only go through blending during tiling, if there was overlap as defined by the step matrix and the bounding box */ ptile->ttrans->pat_trans_fill(x, y, x+w, y+h, px, py, ptile, fill_trans_buffer); } } return 0; }
/* the routine sets ph->actual_frequency and ph->actual_angle. */ static int pick_cell_size(gs_screen_halftone * ph, const gs_matrix * pmat, ulong max_size, uint min_levels, bool accurate, gx_ht_cell_params_t * phcp) { const bool landscape = (pmat->xy != 0.0 || pmat->yx != 0.0); /* Account for a possibly reflected coordinate system. */ /* See gxstroke.c for the algorithm. */ const bool reflected = pmat->xy * pmat->yx > pmat->xx * pmat->yy; const int reflection = (reflected ? -1 : 1); const int rotation = (landscape ? (pmat->yx < 0 ? 90 : -90) : pmat->xx < 0 ? 180 : 0); const double f0 = ph->frequency, a0 = ph->angle; const double T = fabs((landscape ? pmat->yx / pmat->xy : pmat->xx / pmat->yy)); gs_point uv0; #define u0 uv0.x #define v0 uv0.y int rt = 1; double f = 0, a = 0; double e_best = 1000; bool better; /* * We need to find a vector in device space whose length is * 1 inch / ph->frequency and whose angle is ph->angle. * Because device pixels may not be square, we can't simply * map the length to device space and then rotate it; * instead, since we know that user space is uniform in X and Y, * we calculate the correct angle in user space before rotation. */ /* Compute trial values of u and v. */ { gs_matrix rmat; gs_make_rotation(a0 * reflection + rotation, &rmat); gs_distance_transform(72.0 / f0, 0.0, &rmat, &uv0); gs_distance_transform(u0, v0, pmat, &uv0); if_debug10('h', "[h]Requested: f=%g a=%g mat=[%g %g %g %g] max_size=%lu min_levels=%u =>\n u=%g v=%g\n", ph->frequency, ph->angle, pmat->xx, pmat->xy, pmat->yx, pmat->yy, max_size, min_levels, u0, v0); } /* Adjust u and v to reasonable values. */ if (u0 == 0 && v0 == 0) return_error(gs_error_rangecheck); while ((fabs(u0) + fabs(v0)) * rt < 4) ++rt; try_size: better = false; { double fm0 = u0 * rt; double fn0 = v0 * rt; int m0 = (int)floor(u0 * rt + 0.0001); int n0 = (int)floor(v0 * rt + 0.0001); gx_ht_cell_params_t p; p.R = p.R1 = rt; for (p.M = m0 + 1; p.M >= m0; p.M--) for (p.N = n0 + 1; p.N >= n0; p.N--) { long raster, wt, wt_size; double fr, ar, ft, at, f_diff, a_diff, f_err, a_err; p.M1 = (int)floor(p.M / T + 0.5); p.N1 = (int)floor(p.N * T + 0.5); gx_compute_cell_values(&p); if_debug3('h', "[h]trying m=%d, n=%d, r=%d\n", p.M, p.N, rt); wt = p.W; if (wt >= max_short) continue; /* Check the strip size, not the full tile size, */ /* against max_size. */ raster = bitmap_raster(wt); if (raster > max_size / p.D || raster > max_long / wt) continue; wt_size = raster * wt; /* Compute the corresponding values of F and A. */ if (landscape) ar = atan2(p.M * pmat->xy, p.N * pmat->yx), fr = 72.0 * (p.M == 0 ? pmat->xy / p.N * cos(ar) : pmat->yx / p.M * sin(ar)); else ar = atan2(p.N * pmat->xx, p.M * pmat->yy), fr = 72.0 * (p.M == 0 ? pmat->yy / p.N * sin(ar) : pmat->xx / p.M * cos(ar)); ft = fabs(fr) * rt; /* Normalize the angle to the requested quadrant. */ at = (ar * radians_to_degrees - rotation) * reflection; at -= floor(at / 180.0) * 180.0; at += floor(a0 / 180.0) * 180.0; f_diff = fabs(ft - f0); a_diff = fabs(at - a0); f_err = f_diff / fabs(f0); /* * We used to compute the percentage difference here: * a_err = (a0 == 0 ? a_diff : a_diff / fabs(a0)); * but using the angle difference makes more sense: */ a_err = a_diff; if_debug5('h', " ==> d=%d, wt=%ld, wt_size=%ld, f=%g, a=%g\n", p.D, wt, bitmap_raster(wt) * wt, ft, at); { /* * Compute the error in position between ideal location. * and the current integer location. */ double error = (fn0 - p.N) * (fn0 - p.N) + (fm0 - p.M) * (fm0 - p.M); /* * Adjust the error by the length of the vector. This gives * a slight bias toward larger cell sizzes. */ error /= p.N * p.N + p.M * p.M; error = sqrt(error); /* The previous calcs. gave value squared */ if (error > e_best) continue; e_best = error; } *phcp = p; f = ft, a = at; better = true; if_debug3('h', "*** best wt_size=%ld, f_diff=%g, a_diff=%g\n", wt_size, f_diff, a_diff); /* * We want a maximum relative frequency error of 1% and a * maximum angle error of 1% (of 90 degrees). */ if (f_err <= 0.01 && a_err <= 0.9 /*degrees*/) goto done; } } if (phcp->C < min_levels) { /* We don't have enough levels yet. Keep going. */ ++rt; goto try_size; } if (better) { /* If we want accurate screens, continue till we fail. */ if (accurate) { ++rt; goto try_size; } } else { /* * We couldn't find an acceptable M and N. If R > 1, * take what we've got; if R = 1, give up. */ if (rt == 1) return_error(gs_error_rangecheck); } /* Deliver the results. */ done: if_debug5('h', "[h]Chosen: f=%g a=%g M=%d N=%d R=%d\n", f, a, phcp->M, phcp->N, phcp->R); ph->actual_frequency = f; ph->actual_angle = a; return 0; #undef u0 #undef v0 }