From 8aa01a22f564aafa369cb58fa2201756241ab4c3 Mon Sep 17 00:00:00 2001 From: Alexander Malmberg Date: Sat, 17 May 2003 22:06:05 +0000 Subject: [PATCH] Implement basic version of shfill operator. git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/back/trunk@16745 72102866-910b-0410-8b05-ffd578937521 --- ChangeLog | 5 + Source/art/GNUmakefile | 3 +- Source/art/shfill.m | 750 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 757 insertions(+), 1 deletion(-) create mode 100644 Source/art/shfill.m diff --git a/ChangeLog b/ChangeLog index 45dc9e0..240bf4f 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2003-05-18 00:03 Alexander Malmberg + + * Source/art/GNUmakefile, Source/art/shfill.m: Implement basic + version of shfill operator. + 2003-05-17 14:49 Alexander Malmberg * configure.ac: Make sure to add -lm when adding -ltiff (wasn't done diff --git a/Source/art/GNUmakefile b/Source/art/GNUmakefile index 536a1a6..360cd03 100644 --- a/Source/art/GNUmakefile +++ b/Source/art/GNUmakefile @@ -39,7 +39,8 @@ art_OBJC_FILES = \ ftfont.m \ image.m \ composite.m \ - path.m + path.m \ + shfill.m art_HEADER_FILES_DIR = ../../Headers/art art_HEADER_FILES_INSTALL_DIR = gnustep/art diff --git a/Source/art/shfill.m b/Source/art/shfill.m new file mode 100644 index 0000000..22f111e --- /dev/null +++ b/Source/art/shfill.m @@ -0,0 +1,750 @@ +/* + 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. +*/ + +#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]); + 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] intValue]; + + a=[d objectForKey: @"Range"]; + for (i=0;inum_out*2;i++) + f->decode[i]=f->range[i]=[[a objectAtIndex: i] intValue]; + + a=[d objectForKey: @"Decode"]; + if (a) + { + for (i=0;inum_out*2;i++) + f->decode[i]=[[a objectAtIndex: i] intValue]; + } + + a=[d objectForKey: @"Encode"]; + if (a) + { + for (i=0;inum_in*2;i++) + f->encode[i]=[[a objectAtIndex: i] intValue]; + } + + 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 +