/* copyright 2002 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. */ /* Image drawing. DPSimage and helpers. */ #include #include #include #include "ARTGState.h" #include "ARTWindowBuffer.h" #include "blit.h" static unsigned int _get_8_bits(const unsigned char *ptr,int bit_ofs,int num_bits) { /* TODO: if we get values with more than 8 bits, we should round properly and not just discard the extra bits */ int i; unsigned int v; ptr+=bit_ofs/8; bit_ofs%=8; v=0; for (i=0;i<8 && i>bit_ofs)) v|=1; bit_ofs++; if (bit_ofs==8) { ptr++; bit_ofs=0; } } switch (num_bits) { case 1: v*=255; break; case 2: v*=85; break; case 3: v=(v<<5)|(v<<2)|(v>>1); break; case 4: v=(v<<4)|v; break; case 5: v=(v<<3)|(v>>2); break; case 6: v=(v<<2)|(v>>4); break; case 7: v=(v<<1)|(v>>6); break; } return v; } typedef struct { int width,height; int bits_per_sample,samples_per_pixel,bits_per_pixel,bytes_per_row; BOOL is_planar,has_alpha; const unsigned char **data; /* 0 unknown, use colorspacename 1 rgb 2 cmyk 3 gray */ int colorspace; NSString *colorspacename; } image_info_t; static void _image_get_color_rgb_8(image_info_t *ii,render_run_t *ri,int x,int y) { int ofs; if (x<0) x=0; if (y<0) y=0; if (x>=ii->width) x=ii->width-1; if (x>=ii->height) y=ii->height-1; ofs=ii->bytes_per_row*y+x*ii->bits_per_pixel/8; if (ii->is_planar) { ri->r=ii->data[0][ofs]; ri->g=ii->data[1][ofs]; ri->b=ii->data[2][ofs]; if (ii->has_alpha) ri->a=ii->data[3][ofs]; else ri->a=255; } else { ri->r=ii->data[0][ofs]; ri->g=ii->data[0][ofs+1]; ri->b=ii->data[0][ofs+2]; if (ii->has_alpha) ri->a=ii->data[0][ofs+3]; else ri->a=255; } } static void _image_get_color_rgb_cmyk_gray(image_info_t *ii,render_run_t *ri,int x,int y) { int ofs,bit_ofs; int values[5]; int i,j; if (x<0) x=0; if (y<0) y=0; if (x>=ii->width) x=ii->width-1; if (x>=ii->height) y=ii->height-1; ofs=y*ii->bytes_per_row; bit_ofs=x*ii->bits_per_pixel; for (i=j=0;isamples_per_pixel;i++) { values[i]=_get_8_bits(ii->data[j]+ofs,bit_ofs,ii->bits_per_sample); if (ii->is_planar) j++; else bit_ofs+=ii->bits_per_sample; } if (ii->has_alpha) ri->a=values[i-1]; else ri->a=255; if (ii->colorspace==1) { ri->r=values[0]; ri->g=values[1]; ri->b=values[2]; } else if (ii->colorspace==2) { j=255-values[0]-values[3]; ri->r=j<0?0:j; j=255-values[1]-values[3]; ri->g=j<0?0:j; j=255-values[2]-values[3]; ri->b=j<0?0:j; } else if (ii->colorspace==3) { ri->r=ri->g=ri->b=values[0]; } } @implementation ARTGState (image) -(void) _image_do_rgb_transform: (image_info_t *)ii : (NSAffineTransform *)matrix : (void (*)(image_info_t *ii,render_run_t *ri,int x,int y))ifunc { /* TODO: seems to have problems with rotations. 0, 90, 180, 270 work fine, but others seem to cause edges to be off by a pixel */ int x[4],y[4]; int tx[4],ty[4]; float fx[4],fy[4]; int cy,ey; int left_delta,next; int lx,lx_frac,ldx,ldx_frac,l_de,le; int rx,rx_frac,rdx,rdx_frac,r_de,re; int ltx,lty,ltx_frac,lty_frac,ldtx,ldty,ldtx_frac,ldty_frac; int rtx,rty,rtx_frac,rty_frac,rdtx,rdty,rdtx_frac,rdty_frac; NSPoint p; p=[matrix transformPoint: NSMakePoint(0,0)]; fx[0]=p.x; fy[0]=p.y; p=[matrix transformPoint: NSMakePoint(ii->width,0)]; fx[1]=p.x; fy[1]=p.y; p=[matrix transformPoint: NSMakePoint(ii->width,ii->height)]; fx[2]=p.x; fy[2]=p.y; p=[matrix transformPoint: NSMakePoint(0,ii->height)]; 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); x[0]=floor(fx[0]); y[0]=wi->sy-floor(fy[0]); x[1]=floor(fx[1]); y[1]=wi->sy-floor(fy[1]); x[2]=floor(fx[2]); y[2]=wi->sy-floor(fy[2]); x[3]=floor(fx[3]); y[3]=wi->sy-floor(fy[3]); tx[0]=0; ty[0]=ii->height; tx[1]=ii->width; ty[1]=ii->height; tx[2]=ii->width; ty[2]=0; tx[3]=0; ty[3]=0; cy=y[le=0]; if (y[1]ey) ey=y[1]; if (y[2]>ey) ey=y[2]; if (y[3]>ey) ey=y[3]; if (x[(le+1)&3]l_de) lx++,lx_frac-=l_de; ltx+=ldtx; ltx_frac+=ldtx_frac; if (ltx_frac<0) ltx--,ltx_frac+=l_de; if (ltx_frac>l_de) ltx++,ltx_frac-=l_de; lty+=ldty; lty_frac+=ldty_frac; if (lty_frac<0) lty--,lty_frac+=l_de; if (lty_frac>l_de) lty++,lty_frac-=l_de; } if (cy==y[re]) { next=(re-left_delta)&3; while (y[re]==y[next]) { re=next; next=(re-left_delta)&3; } r_de=y[next]-y[re]; rx=x[re]; rx_frac=r_de-1; /* TODO? */ rdx=(x[next]-x[re])/r_de; rdx_frac=(x[next]-x[re])%r_de; rtx=tx[re]; rty=ty[re]; rtx_frac=0; rty_frac=0; rdtx=(tx[next]-tx[re])/r_de; rdty=(ty[next]-ty[re])/r_de; rdtx_frac=(tx[next]-tx[re])%r_de; rdty_frac=(ty[next]-ty[re])%r_de; re=next; } else { rx+=rdx; rx_frac+=rdx_frac; if (rx_frac<0) rx--,rx_frac+=r_de; if (rx_frac>r_de) rx++,rx_frac-=r_de; rtx+=rdtx; rtx_frac+=rdtx_frac; if (rtx_frac<0) rtx--,rtx_frac+=r_de; if (rtx_frac>r_de) rtx++,rtx_frac-=r_de; rty+=rdty; rty_frac+=rdty_frac; if (rty_frac<0) rty--,rty_frac+=r_de; if (rty_frac>r_de) rty++,rty_frac-=r_de; } if (cy>=clip_y0 && rx>lx) { render_run_t ri; int x0,x1,de; int tx,ty,tx_frac,ty_frac,dtx,dty,dtx_frac,dty_frac; int delta; x0=lx; x1=rx; de=x1-x0; tx=ltx; ty=lty; tx_frac=ltx_frac*de*r_de; ty_frac=lty_frac*de*r_de; de*=r_de*l_de; delta=(rtx*r_de+rtx_frac)*l_de-(ltx*l_de+ltx_frac)*r_de; dtx=delta/de; dtx_frac=delta%de; delta=(rty*r_de+rty_frac)*l_de-(lty*l_de+lty_frac)*r_de; dty=delta/de; dty_frac=delta%de; /* x0 x1/x2 -> y0 y1/y2 z ((y0*y2+y1)*x2-(x0*x2+x1)*y2)/(x2*y2*z) */ if (x0=de) tx++,tx_frac-=de; ty_frac+=dty_frac*(clip_x0-x0); while (ty_frac<0) ty--,ty_frac+=de; while (ty_frac>=de) ty++,ty_frac-=de; x0=clip_x0; } if (x1>=clip_x1) x1=clip_x1-1; ri.dst=wi->data+x0*DI.bytes_per_pixel+cy*wi->bytes_per_line; ri.dsta=wi->alpha+x0+cy*wi->sx; if (wi->has_alpha) { for (;x0=de) tx++,tx_frac-=de; ty_frac+=dty_frac; if (ty_frac<0) ty--,ty_frac+=de; if (ty_frac>=de) ty++,ty_frac-=de; } } else { for (;x0=de) tx++,tx_frac-=de; ty_frac+=dty_frac; if (ty_frac<0) ty--,ty_frac+=de; if (ty_frac>=de) ty++,ty_frac-=de; } } } cy++; } } - (void)DPSimage: (NSAffineTransform*) matrix : (int) pixelsWide : (int) pixelsHigh : (int) bitsPerSample : (int) samplesPerPixel : (int) bitsPerPixel : (int) bytesPerRow : (BOOL) isPlanar : (BOOL) hasAlpha : (NSString *) colorSpaceName : (const unsigned char *const [5]) data { BOOL identity_transform,is_rgb; image_info_t ii; if (!wi || !wi->data) return; if (all_clipped) return; [matrix prependTransform: ctm]; if (fabs(matrix->matrix.m11-1.0)<0.001 && fabs(matrix->matrix.m12)<0.001 && fabs(matrix->matrix.m22-1.0)<0.001 && fabs(matrix->matrix.m21)<0.001) identity_transform=YES; else identity_transform=NO; if (colorSpaceName==NSDeviceRGBColorSpace || colorSpaceName==NSCalibratedRGBColorSpace) is_rgb=YES; else is_rgb=NO; /* optimize common case */ if (identity_transform && is_rgb && bitsPerSample==8 && !isPlanar && bytesPerRow==samplesPerPixel*pixelsWide && ((samplesPerPixel==3 && bitsPerPixel==24 && !hasAlpha) || (samplesPerPixel==4 && bitsPerPixel==32 && hasAlpha))) { int x,y,ox,oy; const unsigned char *src=data[0]; unsigned char *alpha_dest; render_run_t ri; if (wi->has_alpha) { if (DI.inline_alpha) alpha_dest=wi->data+DI.inline_alpha_ofs; else alpha_dest=wi->alpha; } else alpha_dest=NULL; ox=[matrix transformPoint: NSMakePoint(0,0)].x; oy=wi->sy-[matrix transformPoint: NSMakePoint(0,0)].y-pixelsHigh; for (y=0;y=clip_x1 || y+oy=clip_y1) { if (hasAlpha) src+=4; else src+=3; continue; } ri.dst=wi->data+(x+ox)*DI.bytes_per_pixel+(y+oy)*wi->bytes_per_line; ri.dsta=wi->alpha+(x+ox)+(y+oy)*wi->sx; ri.r=src[0]; ri.g=src[1]; ri.b=src[2]; if (hasAlpha) { ri.a=src[3]; /* TODO: find out if input is premultiplied or not */ if (ri.a && ri.a!=255) { ri.r=(255*ri.r)/ri.a; ri.g=(255*ri.g)/ri.a; ri.b=(255*ri.b)/ri.a; } if (alpha_dest) { if (src[3]==255) RENDER_RUN_OPAQUE_A(&ri,1); else if (src[3]) RENDER_RUN_ALPHA_A(&ri,1); } else { if (src[3]==255) RENDER_RUN_OPAQUE(&ri,1); else if (src[3]) RENDER_RUN_ALPHA(&ri,1); } src+=4; } else { ri.a=255; if (alpha_dest) RENDER_RUN_OPAQUE_A(&ri,1); else RENDER_RUN_OPAQUE(&ri,1); src+=3; } } } return; } ii.bits_per_sample=bitsPerSample; ii.bits_per_pixel=bitsPerPixel; ii.is_planar=isPlanar; ii.has_alpha=hasAlpha; ii.width=pixelsWide; ii.height=pixelsHigh; ii.samples_per_pixel=samplesPerPixel; ii.bytes_per_row=bytesPerRow; ii.data=(const unsigned char **)data; if (bitsPerSample==8 && is_rgb && ((samplesPerPixel==3 && !hasAlpha) || (samplesPerPixel==4 && hasAlpha))) { [self _image_do_rgb_transform: &ii : matrix : _image_get_color_rgb_8]; return; } if (is_rgb) ii.colorspace=1; else if (colorSpaceName==NSDeviceCMYKColorSpace) ii.colorspace=2; else if (colorSpaceName==NSDeviceWhiteColorSpace || colorSpaceName==NSDeviceBlackColorSpace || colorSpaceName==NSCalibratedWhiteColorSpace || colorSpaceName==NSCalibratedBlackColorSpace) ii.colorspace=3; else ii.colorspace=0; if (ii.colorspace!=0) { [self _image_do_rgb_transform: &ii : matrix : _image_get_color_rgb_cmyk_gray]; return; } NSLog(@"unimplemented DPSimage %ix%i |%@| bips=%i spp=%i bipp=%i bypr=%i planar=%i alpha=%i\n", pixelsWide,pixelsHigh,matrix, bitsPerSample,samplesPerPixel,bitsPerPixel,bytesPerRow,isPlanar, hasAlpha); } @end