diff --git a/ChangeLog b/ChangeLog index 3d86416..0f25471 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,10 @@ +2002-08-31 14:40 Alexander Malmberg + + * Source/art/ARTContext.m, Source/art/ARTGState.h, Source/art/path.m: + Store clipping path as a set of spans instead of as an svp. Build + spans from DPSclip and DPSeoclip, and use it when clipping (only + implemented for paths, so far). + 2002-08-30 15:42 Alexander Malmberg * Source/art/blit.h, Source/art/blit.m, Source/art/path.m: Move diff --git a/Source/art/ARTContext.m b/Source/art/ARTContext.m index 0d20b3f..73469b0 100644 --- a/Source/art/ARTContext.m +++ b/Source/art/ARTContext.m @@ -468,42 +468,6 @@ very expensive @end -static ArtSVP *copy_svp(ArtSVP *svp) -{ - int i; - ArtSVP *svp2; - ArtSVPSeg *dst,*src; - - if (!svp->n_segs) - return NULL; - - svp2=malloc(sizeof(ArtSVP)+sizeof(ArtSVPSeg)*(svp->n_segs-1)); - if (!svp2) - { - NSLog(@"out of memory copying svp"); - return NULL; - } - - svp2->n_segs=svp->n_segs; - - for (i=0,src=svp->segs,dst=svp2->segs;in_segs;i++,src++,dst++) - { - dst->n_points=src->n_points; - dst->dir=src->dir; - dst->bbox=src->bbox; - if (src->n_points) - { - dst->points=malloc(sizeof(ArtPoint)*src->n_points); - memcpy(dst->points,src->points,sizeof(ArtPoint)*src->n_points); - } - else - dst->points=NULL; - } - - return svp2; -} - - @interface ARTGState (internal_stuff) #ifdef RDS -(void) _setup_stuff: (int)window : (RDSClient *)remote; @@ -521,8 +485,10 @@ static ArtSVP *copy_svp(ArtSVP *svp) if (dash.dash) free(dash.dash); - if (clip_path) - art_svp_free(clip_path); + if (clip_span) + free(clip_span); + if (clip_index) + free(clip_index); DESTROY(wi); @@ -550,9 +516,32 @@ static ArtSVP *copy_svp(ArtSVP *svp) } } - if (clip_path) + if (clip_span) { - clip_path=copy_svp(clip_path); + unsigned int *n; + n=malloc(sizeof(unsigned int)*clip_num_span); + if (n) + { + memcpy(n,clip_span,sizeof(unsigned int)*clip_num_span); + clip_span=n; + n=malloc(sizeof(unsigned int *)*(clip_sy+1)); + if (n) + { + memcpy(n,clip_index,sizeof(unsigned int *)*(clip_sy+1)); + clip_index = n; + } + else + { + free(clip_span); + clip_span=clip_index=NULL; + clip_num_span=0; + } + } + else + { + clip_span=clip_index=NULL; + clip_num_span=0; + } } wi=RETAIN(wi); diff --git a/Source/art/ARTGState.h b/Source/art/ARTGState.h index 4bb451b..8e62c5e 100644 --- a/Source/art/ARTGState.h +++ b/Source/art/ARTGState.h @@ -34,7 +34,7 @@ #endif -#include +#include @class ARTWindowBuffer; @@ -59,7 +59,26 @@ #define CLIP_DATA (wi->data+clip_x0*wi->bytes_per_pixel+clip_y0*wi->bytes_per_line) int clip_sx,clip_sy; - ArtSVP *clip_path; + /* + Clipping spans are stored this way. clip_index has an index to the + spans (in clip_span) for each line. clip_span has the x-starting + coordinate for each span. A line starts 'off', each coordinate flips + the state. The spans are stored in increasing y order, so + clip_index[y+1]-1 is the index of the last span coordinate. Thus, if + clip_index[y]==clip_index[y+1], the entire line is clipped. + clip_index actually has clip_sy+1 entries, so clip_index[y+1] is + valid for _all_ lines. + + All coordinates are in device space and counted inside the clipping + rectangle. + + To make things easier, each line also ends in the off state (so the + last entry in clip_span might be a dummy entry at the end of the + line). + */ + unsigned int *clip_span; + unsigned int *clip_index; + int clip_num_span; } @end diff --git a/Source/art/path.m b/Source/art/path.m index cf42cc5..a9320a9 100644 --- a/Source/art/path.m +++ b/Source/art/path.m @@ -34,6 +34,8 @@ Path handling. #include "ARTWindowBuffer.h" #include "blit.h" + +#include #include @@ -90,6 +92,20 @@ static void dump_bpath(ArtBpath *vp) } } +{ + int i,j; + printf("size=%i num=%i\n",ci.span_size,ci.num_span); + for (i=0;iri.dsta = dsta; } +static void render_svp_clipped_callback(void *data, int y, int start, + ArtSVPRenderAAStep *steps, int n_steps) +{ + svp_render_info_t *ri = data; + int x0, x1; + int num; + int alpha; + unsigned char *dst, *dsta; + + unsigned int *span, *end; + BOOL state; + + alpha = start; + + /* empty line; very common case */ + if (alpha < 0x10000 && !n_steps) + { + ri->ri.dst += ri->rowstride; + ri->ri.dsta += ri->arowstride; + return; + } + + /* completely clipped line? */ + if (ri->clip_index[y - ri->y0] == ri->clip_index[y - ri->y0 + 1]) + { + ri->ri.dst += ri->rowstride; + ri->ri.dsta += ri->arowstride; + return; + } + + span = &ri->clip_span[ri->clip_index[y - ri->y0]]; + end = &ri->clip_span[ri->clip_index[y - ri->y0 + 1]]; + state = NO; + + dst = ri->ri.dst + ri->rowstride; + dsta = ri->ri.dsta + ri->arowstride; + + x0 = 0; + for (; n_steps; n_steps--, steps++) + { + x1 = steps->x - ri->x0; + + ri->ri.a = (alpha * ri->real_a + 0x800000) >> 24; + if (ri->ri.a) + { + while (*span < x1) + { + num = *span - x0; + if (state) + { + if (ri->ri.a == 255) + ri->run_opaque(&ri->ri, num); + else + ri->run_alpha(&ri->ri, num); + } + x0 = *span; + ri->ri.dst += ri->bpp * num; + ri->ri.dsta += num; + state = !state; + span++; + if (span == end) + { + ri->ri.dst = dst; + ri->ri.dsta = dsta; + return; + } + } + num = x1 - x0; + if (num && state) + { + if (ri->ri.a == 255) + ri->run_opaque(&ri->ri, num); + else + ri->run_alpha(&ri->ri, num); + } + ri->ri.dst += ri->bpp * num; + ri->ri.dsta += num; + } + else + { + num = x1 - x0; + while (*span <= x1) + { + state = !state; + span++; + if (span == end) + { + ri->ri.dst = dst; + ri->ri.dsta = dsta; + return; + } + } + ri->ri.dst += ri->bpp * num; + ri->ri.dsta += num; + } + + alpha += steps->delta; + x0 = x1; + } + x1 = ri->x1 - ri->x0; + num = x1 - x0; + ri->ri.a = (alpha * ri->real_a + 0x800000) >> 24; + if (ri->ri.a && num) + { + while (*span < x1) + { + num = *span - x0; + if (state) + { + if (ri->ri.a == 255) + ri->run_opaque(&ri->ri, num); + else + ri->run_alpha(&ri->ri, num); + } + x0 = *span; + ri->ri.dst += ri->bpp * num; + ri->ri.dsta += num; + state = !state; + span++; + if (span == end) + { + ri->ri.dst = dst; + ri->ri.dsta = dsta; + return; + } + } + num = x1 - x0; + if (num && state) + { + if (ri->ri.a == 255) + ri->run_opaque(&ri->ri, num); + else + ri->run_alpha(&ri->ri, num); + } + } + + ri->ri.dst = dst; + ri->ri.dsta = dsta; +} + static void artcontext_render_svp(const ArtSVP *svp, int x0, int y0, int x1, int y1, unsigned char r, unsigned char g, unsigned char b, unsigned char a, unsigned char *dst, int rowstride, unsigned char *dsta, int arowstride, int has_alpha, - draw_info_t *di) + draw_info_t *di, + unsigned int *clip_span, unsigned int *clip_index) { svp_render_info_t ri; ri.x0 = x0; ri.x1 = x1; + ri.y0 = y0; ri.ri.r = r; ri.ri.g = g; @@ -184,6 +344,9 @@ static void artcontext_render_svp(const ArtSVP *svp, int x0, int y0, int x1, int ri.ri.dst = dst; ri.rowstride = rowstride; + ri.clip_span = clip_span; + ri.clip_index = clip_index; + if (has_alpha) { ri.ri.dsta = dsta; @@ -197,7 +360,8 @@ static void artcontext_render_svp(const ArtSVP *svp, int x0, int y0, int x1, int ri.run_opaque = di->render_run_opaque; } - art_svp_render_aa(svp, x0, y0, x1, y1, render_svp_callback, &ri); + art_svp_render_aa(svp, x0, y0, x1, y1, + clip_span? render_svp_clipped_callback : render_svp_callback, &ri); } @@ -292,7 +456,7 @@ within one pixel.) */ else { /* This is used when clipping, so we need to make sure we - contain all pixels. */ + contain all pixels. */ if (vp[0].x < vp[2].x) *px0 = floor(vp[0].x), *px1 = ceil(vp[2].x); else @@ -351,7 +515,7 @@ within one pixel.) */ { case NSMoveToBezierPathElement: /* When filling, the path must be closed, so if - it isn't already closed, we fix that here. */ + it isn't already closed, we fix that here. */ if (fill) { if (cur_start != -1 && cur_line) @@ -448,27 +612,112 @@ within one pixel.) */ /** Clipping **/ +typedef struct +{ + int x0, x1, y0, sy; + + unsigned int *span; + unsigned int *index; + int span_size, num_span; + + unsigned int *cur_span; + unsigned int *cur_index; +} clip_info_t; + + +static void clip_svp_callback(void *data, int y, int start, + ArtSVPRenderAAStep *steps, int n_steps) +{ + clip_info_t *ci = data; + int x; + int alpha; + BOOL state, nstate; + + alpha = start; + + ci->index[y - ci->y0] = ci->num_span; + if (y-ci->y0<0 || y-ci->y0>=ci->sy) + { + printf("weird y=%i (%i)\n",y,y-ci->y0); + } + + /* empty line; very common case */ + if (!alpha && !n_steps) + { + return; + } + + x = 0; + state = alpha >= 0x10000; + if (state) + { + if (ci->num_span == ci->span_size) + { + ci->span_size += 16; + ci->span = realloc(ci->span, sizeof(unsigned int) * ci->span_size); + } + ci->span[ci->num_span++] = x; + } + + + for (; n_steps; n_steps--, steps++) + { + alpha += steps->delta; + x = steps->x - ci->x0; + nstate = alpha >= 0x10000; + if (state != nstate) + { + if (ci->num_span == ci->span_size) + { + ci->span_size += 16; + ci->span = realloc(ci->span, sizeof(unsigned int) * ci->span_size); + } + ci->span[ci->num_span++] = x; + state = nstate; + } + } + if (state) + { + if (ci->num_span == ci->span_size) + { + ci->span_size += 16; + ci->span = realloc(ci->span, sizeof(unsigned int) * ci->span_size); + } + ci->span[ci->num_span++] = ci->x1; + } +} + /* will free the passed in svp */ -(void) _clip_add_svp: (ArtSVP *)svp { - if (clip_path) + clip_info_t ci; + ci.span = NULL; + ci.index = malloc(sizeof(unsigned int) * (clip_sy + 1)); + if (!ci.index) { - ArtSVP *svp2; -/* ArtSvpWriter *svpw; - - svpw = art_svp_writer_rewind_new(ART_WIND_RULE_INTERSECT); - art_svp_intersector(svp, svpw); - art_svp_intersector(clip_path, svpw); - svp2 = art_svp_writer_rewind_reap(svpw); */ - svp2 = art_svp_intersect(svp, clip_path); - art_svp_free(svp); - art_svp_free(clip_path); - clip_path = svp2; + NSLog(@"Warning: out of memory calculating clipping spans (%i bytes)", + sizeof(unsigned int) * (clip_sy + 1)); + return; + } + ci.span_size = ci.num_span = 0; + ci.x0 = clip_x0; + ci.x1 = clip_x1; + ci.y0 = clip_y0; + ci.sy = clip_sy; + if (clip_span) + { + NSLog(@"TODO: _clip_add_svp: with existing clip_span not implemented"); + free(ci.index); + return; } else { - clip_path = svp; + art_svp_render_aa(svp, clip_x0, clip_y0, clip_x1, clip_y1, clip_svp_callback, &ci); + clip_span = ci.span; + clip_index = ci.index; + clip_index[clip_sy] = clip_num_span = ci.num_span; } + art_svp_free(svp); } -(void) _clip: (int)rule @@ -496,26 +745,6 @@ within one pixel.) */ [self _clip_add_svp: svp]; } --(ArtSVP *) _clip_svp: (ArtSVP *)svp -{ - ArtSVP *svp2; -// ArtSvpWriter *svpw; - - if (!clip_path) - return svp; -/* TODO */ -/* svpw = art_svp_writer_rewind_new(ART_WIND_RULE_INTERSECT); - art_svp_intersector(svp, svpw); - art_svp_intersector(clip_path, svpw); - svp2 = art_svp_writer_rewind_reap(svpw); - art_svp_free(svp);*/ - - svp2 = art_svp_intersect(svp, clip_path); - art_svp_free(svp); - - return svp2; -} - - (void) DPSclip { @@ -588,10 +817,12 @@ within one pixel.) */ clip_sx = clip_x1 - clip_x0; clip_sy = clip_y1 - clip_y0; - if (clip_path) + if (clip_span) { - art_svp_free(clip_path); - clip_path = NULL; + free(clip_span); + free(clip_index); + clip_span = clip_index = NULL; + clip_num_span = 0; } } @@ -624,16 +855,13 @@ within one pixel.) */ svp = svp2; } - if (clip_path) - svp = [self _clip_svp: svp]; - artcontext_render_svp(svp, clip_x0, clip_y0, clip_x1, clip_y1, fill_color[0], fill_color[1], fill_color[2], fill_color[3], CLIP_DATA, wi->bytes_per_line, wi->has_alpha? wi->alpha + clip_x0 + clip_y0 * wi->sx : NULL, wi->sx, wi->has_alpha, - &DI); + &DI, clip_span, clip_index); art_svp_free(svp); @@ -665,20 +893,17 @@ within one pixel.) */ axis: &x0 : &y0 : &x1 : &y1 pixel: YES]; - if (!axis_aligned || clip_path) + if (!axis_aligned || clip_span) { /* Not properly aligned. Handle the general case. */ svp = art_svp_from_vpath(vp); - if (clip_path) - svp = [self _clip_svp: svp]; - artcontext_render_svp(svp, clip_x0, clip_y0, clip_x1, clip_y1, fill_color[0], fill_color[1], fill_color[2], fill_color[3], CLIP_DATA, wi->bytes_per_line, wi->has_alpha? wi->alpha + clip_x0 + clip_y0 * wi->sx : NULL, wi->sx, wi->has_alpha, - &DI); + &DI, clip_span, clip_index); art_svp_free(svp); return; @@ -768,7 +993,7 @@ within one pixel.) */ if (do_dash) { /* try to adjust the offset so dashes appear on pixel boundaries - (otherwise it turns into an antialiased blur) */ + (otherwise it turns into an antialiased blur) */ int i; if (adjust_dash_ofs) @@ -792,15 +1017,12 @@ within one pixel.) */ temp_scale * line_width, miter_limit, 0.5); art_free(vp); - if (clip_path) - svp = [self _clip_svp: svp]; - artcontext_render_svp(svp, clip_x0, clip_y0, clip_x1, clip_y1, stroke_color[0], stroke_color[1], stroke_color[2], stroke_color[3], CLIP_DATA, wi->bytes_per_line, wi->has_alpha? wi->alpha + clip_x0 + clip_y0 * wi->sx : NULL, wi->sx, wi->has_alpha, - &DI); + &DI, clip_span, clip_index); art_svp_free(svp); }