/* Copyright (C) 2003 Free Software Foundation, Inc. Author: Alexander Malmberg This file is part of GNUstep. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ /* this code is rather experimental */ #include #include "ARTGState.h" #ifndef RDS #include "x11/XWindowBuffer.h" #endif #include "blit.h" #include #include #include #include /* TODO: share this with composite.m */ /* Handle compositing in transformed coordinate spaces. _rect_setup sets up the a rect_trace_t structure, and each call to _rect_advance returns YES and the left and right points on the next row, or NO if all rows are done. */ typedef struct { int x[4], y[4]; int cy, ey; int left_delta; int lx, lx_frac, ldx, ldx_frac, l_de, le; int rx, rx_frac, rdx, rdx_frac, r_de, re; int cx0, cx1; } rect_trace_t; static void _rect_setup(rect_trace_t *t, NSRect r, int cx0, int cx1, NSAffineTransform *ctm, int up, int *y0, int wi_sy) { float fx[4], fy[4]; NSPoint p; t->cx0 = cx0; t->cx1 = cx1; p = r.origin; p = [ctm pointInMatrixSpace: p]; fx[0] = p.x; fy[0] = p.y; p = r.origin; p.x += r.size.width; p = [ctm pointInMatrixSpace: p]; fx[1] = p.x; fy[1] = p.y; p = r.origin; p.x += r.size.width; p.y += r.size.height; p = [ctm pointInMatrixSpace: p]; fx[2] = p.x; fy[2] = p.y; p = r.origin; p.y += r.size.height; p = [ctm pointInMatrixSpace: p]; fx[3] = p.x; fy[3] = p.y; if (fabs(fx[0] - floor(fx[0] + .5)) < 0.001) fx[0] = floor(fx[0] + .5); if (fabs(fx[1] - floor(fx[1] + .5)) < 0.001) fx[1] = floor(fx[1] + .5); if (fabs(fx[2] - floor(fx[2] + .5)) < 0.001) fx[2] = floor(fx[2] + .5); if (fabs(fx[3] - floor(fx[3] + .5)) < 0.001) fx[3] = floor(fx[3] + .5); if (fabs(fy[0] - floor(fy[0] + .5)) < 0.001) fy[0] = floor(fy[0] + .5); if (fabs(fy[1] - floor(fy[1] + .5)) < 0.001) fy[1] = floor(fy[1] + .5); if (fabs(fy[2] - floor(fy[2] + .5)) < 0.001) fy[2] = floor(fy[2] + .5); if (fabs(fy[3] - floor(fy[3] + .5)) < 0.001) fy[3] = floor(fy[3] + .5); t->x[0] = floor(fx[0]); t->y[0] = wi_sy - floor(fy[0]); t->x[1] = floor(fx[1]); t->y[1] = wi_sy - floor(fy[1]); t->x[2] = floor(fx[2]); t->y[2] = wi_sy - floor(fy[2]); t->x[3] = floor(fx[3]); t->y[3] = wi_sy - floor(fy[3]); /* If we're tracing the 'other way', we just flip the y-coordinates and unflip when returning them */ if (up) { t->y[0] = -t->y[0]; t->y[1] = -t->y[1]; t->y[2] = -t->y[2]; t->y[3] = -t->y[3]; } t->cy = t->y[t->le = 0]; if (t->y[1] < t->cy) t->cy = t->y[t->le = 1]; if (t->y[2] < t->cy) t->cy = t->y[t->le = 2]; if (t->y[3] < t->cy) t->cy = t->y[t->le = 3]; t->re = t->le; t->ey = t->y[0]; if (t->y[1] > t->ey) t->ey = t->y[1]; if (t->y[2] > t->ey) t->ey = t->y[2]; if (t->y[3] > t->ey) t->ey = t->y[3]; if (t->x[(t->le + 1) & 3] < t->x[(t->le - 1) & 3]) t->left_delta = 1; else t->left_delta = -1; /* silence the compiler */ t->lx = t->lx_frac = t->ldx = t->ldx_frac = t->l_de = 0; t->rx = t->rx_frac = t->rdx = t->rdx_frac = t->r_de = 0; if (up) *y0 = -t->cy; else *y0 = t->cy; } static BOOL _rect_advance(rect_trace_t *t, int *x0, int *x1) { int next; if (t->cy > t->ey) return NO; if (t->cy == t->y[t->le]) { next = (t->le + t->left_delta) & 3; if (t->y[t->le] == t->y[next]) { t->le = next; next = (t->le + t->left_delta) & 3; } t->l_de = t->y[next] - t->y[t->le]; if (!t->l_de) return NO; t->lx = t->x[t->le]; t->lx_frac = 0; t->ldx = (t->x[next] - t->x[t->le]) / t->l_de; t->ldx_frac = (t->x[next] - t->x[t->le]) % t->l_de; t->le = next; } else { t->lx += t->ldx; t->lx_frac += t->ldx_frac; if (t->lx_frac < 0) t->lx--, t->lx_frac += t->l_de; if (t->lx_frac > t->l_de) t->lx++, t->lx_frac -= t->l_de; } if (t->cy == t->y[t->re]) { next = (t->re - t->left_delta) & 3; if (t->y[t->re] == t->y[next]) { t->re = next; next = (t->re - t->left_delta) & 3; } t->r_de = t->y[next] - t->y[t->re]; if (!t->r_de) return NO; t->rx = t->x[t->re]; t->rx_frac = t->r_de - 1; /* TODO? */ t->rdx = (t->x[next] - t->x[t->re]) / t->r_de; t->rdx_frac = (t->x[next] - t->x[t->re]) % t->r_de; t->re = next; } else { t->rx += t->rdx; t->rx_frac += t->rdx_frac; if (t->rx_frac < 0) t->rx--, t->rx_frac += t->r_de; if (t->rx_frac > t->r_de) t->rx++, t->rx_frac -= t->r_de; } if (t->rx > t->lx && t->rx >= t->cx0 && t->lx < t->cx1) { *x0 = t->lx - t->cx0; if (*x0 < 0) *x0 = 0; *x1 = t->rx - t->cx0; if (*x1 > t->cx1 - t->cx0) *x1 = t->cx1 - t->cx0; } else { *x0 = *x1 = 0; } t->cy++; return YES; } @interface ARTGState (shfill) -(void) DPSshfill: (NSDictionary *)shader; @end @implementation ARTGState (shfill) typedef struct function_s { /* General information about the function. */ int num_in,num_out; void (*eval)(struct function_s *f,double *in,double *out); double *domain; /* num_in*2 */ double *range; /* num_out*2 */ /* Type specific information */ int *size; /* num_in */ const unsigned char *data_source; int bits_per_sample; double *encode; /* num_in*2 */ double *decode; /* num_out*2 */ /* sample cache for in==2, out==3 */ int sample_index[2]; double sample_cache[4][3]; } function_t; static double function_getsample(function_t *f,int sample,int i) { double v; if (f->bits_per_sample==8) { // printf("get at %i\n",sample*f->num_out+i); v=f->data_source[sample*f->num_out+i]/255.0; // printf("got %g\n",v); } else if (f->bits_per_sample==16) { int c0,c1; c0=f->data_source[(sample*f->num_out+i)*2+0]; c1=f->data_source[(sample*f->num_out+i)*2+1]; v=(c0*256+c1)/65535.0; } else { NSLog(@"unhandled bits per sample %i",f->bits_per_sample); v=0.0; } v=f->decode[i*2]+v*(f->decode[i*2+1]-f->decode[i*2]); if (vrange[i*2]) v=f->range[i*2]; if (v>f->range[i*2+1]) v=f->range[i*2+1]; return v; } static void function_eval(function_t *f,double *a_in,double *out) { double in[f->num_in]; int sample[f->num_in]; int i,j,sample_index,sample_factor; unsigned int u,v; double c; for (i=0;inum_in;i++) { in[i]=(a_in[i]-f->domain[i*2])/(f->domain[i*2+1]-f->domain[i*2]); if (in[i]<0.0) in[i]=0.0; if (in[i]>1.0) in[i]=1.0; in[i]=f->encode[i*2]+in[i]*(f->encode[i*2+1]-f->encode[i*2]); sample[i]=floor(in[i]); /* we only want sample[i]==f->size[i]-1 when f->size[i]==1 */ if (sample[i]>=f->size[i]-1) sample[i]=f->size[i]-2; if (sample[i]<0) sample[i]=0; in[i]=in[i]-sample[i]; if (in[i]<0.0) in[i]=0.0; if (in[i]>1.0) in[i]=1.0; // printf(" coord %i, sample %i, frac %g\n",i,sample[i],in[i]); } for (i=0;inum_out;i++) { double out_value; /* iterate over all corners in the f->num_in-dimensional hypercube we're in */ out_value=0.0; for (u=0;u<1<num_in;u++) { sample_index=0; sample_factor=1; c=1; for (v=1,j=0;jnum_in;j++,v<<=1) { sample_index+=sample[j]*sample_factor; if (u&v) { c*=in[j]; sample_index+=sample_factor; } else c*=(1.0-in[j]); sample_factor*=f->size[j]; if (c==0.0) break; } // printf(" %08x index %i, factor %i, c=%g\n",u,sample_index,sample_factor,c); if (c>0.0) out_value+=c*function_getsample(f,sample_index,i); } // printf(" final=%g\n",out_value); out[i]=out_value; } } /* special case: f->num_in==2, f->num_out==3 */ static void function_eval_in2_out3(function_t *f,double *a_in,double *out) { double in[2]; int sample[2]; int i; for (i=0;i<2;i++) { in[i]=(a_in[i]-f->domain[i*2])/(f->domain[i*2+1]-f->domain[i*2]); if (in[i]<0.0) in[i]=0.0; if (in[i]>1.0) in[i]=1.0; in[i]=f->encode[i*2]+in[i]*(f->encode[i*2+1]-f->encode[i*2]); sample[i]=floor(in[i]); /* we only want sample[i]==f->size[i]-1 when f->size[i]==1 */ if (sample[i]>=f->size[i]-1) sample[i]=f->size[i]-2; if (sample[i]<0) sample[i]=0; in[i]=in[i]-sample[i]; if (in[i]<0.0) in[i]=0.0; if (in[i]>1.0) in[i]=1.0; // printf(" coord %i, sample %i, frac %g\n",i,sample[i],in[i]); } if (sample[0]!=f->sample_index[0] || sample[1]!=f->sample_index[1]) { f->sample_index[0]=sample[0]; f->sample_index[1]=sample[1]; for (i=0;i<3;i++) { f->sample_cache[0][i]=function_getsample(f,sample[0] +(sample[1] )*f->size[0],i); if (sample[0]+1size[0]) f->sample_cache[1][i]=function_getsample(f,sample[0]+1+(sample[1] )*f->size[0],i); if (sample[1]+1size[1]) f->sample_cache[2][i]=function_getsample(f,sample[0] +(sample[1]+1)*f->size[0],i); if (sample[0]+1size[0] && sample[1]+1size[1]) f->sample_cache[3][i]=function_getsample(f,sample[0]+1+(sample[1]+1)*f->size[0],i); } } for (i=0;i<3;i++) { double out_value; double A,B,C,D; double p,q,pq; A=f->sample_cache[0][i]; B=f->sample_cache[1][i]; C=f->sample_cache[2][i]; D=f->sample_cache[3][i]; out_value=0.0; p=in[0]; q=in[1]; pq=p*q; if (p!=1.0 && q!=1.0) out_value+=A*(1-p-q+pq); if (p!=0.0 && q!=1.0) out_value+=B*(p-pq); if (p!=1.0 && q!=0.0) out_value+=C*(q-pq); if (p!=0.0 && q!=0.0) out_value+=D*pq; out[i]=out_value; } } static BOOL function_setup(NSDictionary *d,function_t *f) { NSNumber *v=[d objectForKey: @"FunctionType"]; NSArray *a; NSData *data; int i,j; if ([v intValue]!=0) return NO; memset(f,0,sizeof(function_t)); a=[d objectForKey: @"Size"]; f->num_in=[a count]; if (!f->num_in) return NO; f->num_out=[[d objectForKey: @"Range"] count]/2; if (!f->num_out) return NO; f->bits_per_sample=[[d objectForKey: @"BitsPerSample"] intValue]; if (!(f->bits_per_sample==8 || f->bits_per_sample==16)) return NO; data=[d objectForKey: @"DataSource"]; if (!data || ![data isKindOfClass: [NSData class]]) return NO; f->data_source=[data bytes]; f->size=malloc(sizeof(int)*f->num_in); f->domain=malloc(sizeof(double)*f->num_in*2); f->range=malloc(sizeof(double)*f->num_out*2); f->encode=malloc(sizeof(double)*f->num_in*2); f->decode=malloc(sizeof(double)*f->num_out*2); if (!f->size || !f->domain || !f->range || !f->encode || !f->decode) { free(f->size); f->size=NULL; free(f->domain); f->domain=NULL; free(f->range); f->range=NULL; free(f->encode); f->encode=NULL; free(f->decode); f->decode=NULL; return NO; } j=1; for (i=0;inum_in;i++) { f->size[i]=[[a objectAtIndex: i] intValue]; j*=f->size[i]; f->encode[i*2+0]=0; f->encode[i*2+1]=f->size[i]-1; } j*=f->bits_per_sample*f->num_out; j=(j+7)/8; if ([data length]size); f->size=NULL; free(f->domain); f->domain=NULL; free(f->range); f->range=NULL; free(f->encode); f->encode=NULL; free(f->decode); f->decode=NULL; return NO; } a=[d objectForKey: @"Domain"]; for (i=0;inum_in*2;i++) f->domain[i]=[[a objectAtIndex: i] doubleValue]; a=[d objectForKey: @"Range"]; for (i=0;inum_out*2;i++) f->decode[i]=f->range[i]=[[a objectAtIndex: i] doubleValue]; a=[d objectForKey: @"Decode"]; if (a) { for (i=0;inum_out*2;i++) f->decode[i]=[[a objectAtIndex: i] doubleValue]; } a=[d objectForKey: @"Encode"]; if (a) { for (i=0;inum_in*2;i++) f->encode[i]=[[a objectAtIndex: i] doubleValue]; } f->eval=function_eval; if (f->num_in==2 && f->num_out==3) { f->eval=function_eval_in2_out3; f->sample_index[0]=f->sample_index[1]=-1; } return YES; } static void function_free(function_t *f) { free(f->size); f->size=NULL; free(f->domain); f->domain=NULL; free(f->range); f->range=NULL; free(f->encode); f->encode=NULL; free(f->decode); f->decode=NULL; } /* just fail silently for now */ #define ERROR return -(void) DPSshfill: (NSDictionary *)shader { NSNumber *v; NSDictionary *function_dict; function_t function; NSAffineTransform *matrix,*inverse; if (!wi || !wi->data || all_clipped) return; // printf("DPSshfill: %@\n",shader); v=[shader objectForKey: @"ShadingType"]; /* only type 1 shaders */ if ([v intValue]!=1) ERROR; /* in device rgb space */ if ([shader objectForKey: @"ColorSpace"]) if (![[shader objectForKey: @"ColorSpace"] isEqual: NSDeviceRGBColorSpace]) ERROR; function_dict=[shader objectForKey: @"Function"]; if (!function_dict) ERROR; if (!function_setup(function_dict,&function)) ERROR; if (function.num_in!=2 || function.num_out!=3) { function_free(&function); ERROR; } matrix=[ctm copy]; if ([shader objectForKey: @"Matrix"]) { [matrix appendTransform: [shader objectForKey: @"Matrix"]]; } inverse=[matrix copy]; [inverse inverse]; { rect_trace_t rt; NSRect rect; int y,x0,x1,x; render_run_t r; unsigned char *dst,*dsta; double in[2],out[3]; NSPoint p; rect.origin.x=function.domain[0]; rect.size.width=function.domain[1]-function.domain[0]; rect.origin.y=function.domain[2]; rect.size.height=function.domain[3]-function.domain[2]; /* printf("rect=(%g %g)+(%g %g)\n", rect.origin.x,rect.origin.y, rect.size.width,rect.size.height);*/ dst=wi->data+wi->bytes_per_line*clip_y0+clip_x0*DI.bytes_per_pixel; dsta=wi->alpha+wi->sx*clip_y0+clip_x0; _rect_setup(&rt,rect,clip_x0,clip_x1,matrix,0,&y,wi->sy); while (yclip_y0) { dst+=wi->bytes_per_line*(y-clip_y0); dsta+=wi->sx*(y-clip_y0); } while (ysy-y)]; in[0]=p.x; in[1]=p.y; out[0]=out[1]=out[2]=0.0; for (x=x0;xhas_alpha) DI.render_run_opaque_a(&r,1); else DI.render_run_opaque(&r,1); r.dsta++; r.dst+=DI.bytes_per_pixel; in[0]+=inverse->matrix.m11; in[1]+=inverse->matrix.m12; } } else { unsigned int *span, *end; BOOL state = NO; span = &clip_span[clip_index[y - clip_y0]]; end = &clip_span[clip_index[y - clip_y0 + 1]]; while (span != end && *span < x0) { state = !state; span++; if (span == end) break; } if (span != end) { while (span != end && *span < x1) { if (state) { p=[inverse transformPoint: NSMakePoint(clip_x0+x0,wi->sy-y)]; in[0]=p.x; in[1]=p.y; out[0]=out[1]=out[2]=0.0; r.dst=dst+x0*DI.bytes_per_pixel; r.dsta=dsta+x0; for (x=x0;x<*span;x++) { function.eval(&function,in,out); r.r=out[0]*255; r.g=out[1]*255; r.b=out[2]*255; if (wi->has_alpha) DI.render_run_opaque_a(&r,1); else DI.render_run_opaque(&r,1); r.dsta++; r.dst+=DI.bytes_per_pixel; in[0]+=inverse->matrix.m11; in[1]+=inverse->matrix.m12; } } x0 = *span; state = !state; span++; if (span == end) break; } if (state) { p=[inverse transformPoint: NSMakePoint(clip_x0+x0,wi->sy-y)]; in[0]=p.x; in[1]=p.y; out[0]=out[1]=out[2]=0.0; r.dst=dst+x0*DI.bytes_per_pixel; r.dsta=dsta+x0; for (x=x0;xhas_alpha) DI.render_run_opaque_a(&r,1); else DI.render_run_opaque(&r,1); r.dsta++; r.dst+=DI.bytes_per_pixel; in[0]+=inverse->matrix.m11; in[1]+=inverse->matrix.m12; } } } } y++; dst+=wi->bytes_per_line; dsta+=wi->sx; } } UPDATE_UNBUFFERED done: DESTROY(matrix); DESTROY(inverse); function_free(&function); } @end @implementation ARTContext (shfill) /* TODO: move to gsc? */ -(void) DPSshfill: (NSDictionary *)shader { [(ARTGState *)gstate DPSshfill: shader]; } @end