/** * $Id$ * * ***** BEGIN GPL/BL DUAL LICENSE BLOCK ***** * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. The Blender * Foundation also sells licenses for use in proprietary software under * the Blender License. See http://www.blender.org/BL/ for information * about this. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV. * All rights reserved. * * The Original Code is: all of this file. * * Contributor(s): none yet. * * ***** END GPL/BL DUAL LICENSE BLOCK ***** * Add enhanced edges on a rendered image (toon shading, edge shading). */ /* * Edge rendering: use a mask to weigh the depth of neighbouring * pixels, and do a colour correction. * * We need: * - a buffer to store the depths (ints) * - a function that alters the colours in R.rectot (copy edge_enhance?) * The max. z buffer depth is 0x7FFF.FFFF (7 F's) * * - We 'ignore' the pixels falling outside the regular buffer (we fill) * these with the max depth. This causes artefacts when rendering in * parts. */ /* ------------------------------------------------------------------------- */ /* enable extra bounds checking and tracing */ /* #define RE_EDGERENDERSAFE */ /* disable the actual edge correction */ /* #define RE_EDGERENDER_NO_CORRECTION */ #include #include #include /* INT_MIN,MAX are used here */ #include #include "MEM_guardedalloc.h" #include "MTC_vectorops.h" #include "RE_callbacks.h" #include "edgeRender.h" #include "render.h" #include "render_intern.h" #include "zbuf.h" /* for zbufclipwire and zbufclip */ #include "jitter.h" #ifdef RE_EDGERENDERSAFE char edgeRender_h[] = EDGERENDER_H; char edgeRender_c[] = "$Id$"; #include "errorHandler.h" #endif /* ------------------------------------------------------------------------- */ /* the lazy way: */ #define MIN2(x,y) ( (x)<(y) ? (x) : (y) ) /* ------------------------------------------------------------------------- */ /* These function pointers are used for z buffer filling. */ extern void (*zbuffunc)(float *, float *, float *); extern void (*zbuflinefunc)(float *v1, float *v2); extern float Zmulx, Zmuly; /* Some kind of scale? */ extern float Zjitx,Zjity; /* The x,y values for jitter offset */ extern unsigned int Zvlnr; /* Face rendering pointer and counter: these */ extern VlakRen *Zvlr; /* are used for 'caching' render results. */ /* ------------------------------------------------------------------------- */ /* exp: */ static Material** matBuffer; /* buffer with material indices */ static Material* mat_cache; /* material of the face being buffered */ static char* colBuffer; /* buffer with colour correction */ static int *edgeBuffer; /* buffer with distances */ static int bufWidth; /* x-dimension of the buffer */ static int bufHeight; /* y-dimension of the buffer */ static int imWidth; /* x-dimension of the image */ static int imHeight; /* y-dimension of the image */ static int osaCount; /* oversample count */ static int maskBorder; /* size of the mask border */ static short int intensity; /* edge intensity */ static short int same_mat_redux; /* intensity reduction for boundaries with the same material */ static int compatible_mode; /* edge positioning compatible with old rederer */ static int selectmode; /* 0: only solid faces, 1: also transparent f's */ static int Aminy; /* y value of first line in the accu buffer */ static int Amaxy; /* y value of last line in the accu buffer */ /* -also used to clip when zbuffering */ static char edgeR; /* Colour for the edges. The edges will receive */ static char edgeG; /* this tint. The colour is fully used! */ static char edgeB; /* static float edgeBlend; */ /* Indicate opaqueness of the edge colour. */ /* Local functions --------------------------------------------------------- */ /** * Initialise the edge render buffer memory. */ void initEdgeRenderBuffer(void); /** * Release buffer memory. */ void freeEdgeRenderBuffer(void); /** * Set all distances in the distance buffer to the maximum distance. */ void resetDistanceBuffer(void); /** * Insert this distance at these pixel coordinates. */ void insertInEdgeBuffer(int x, int y, int dist); /** * Renders enhanced edges. Distances from distRect are used to * determine a correction on colourRect */ void renderEdges(char * colourRect); /** * Buffer an edge between these two vertices in the e.r. distance buffer. */ void fillEdgeRenderEdge(float *vec1, float *vec2); /** * Buffer a face between these two vertices in the e.r. distance buffer. */ void fillEdgeRenderFace(float *v1, float *v2, float *v3); /** * Compose the edge render colour buffer. */ void calcEdgeRenderColBuf(char * tarbuf); /** * Loop over all objects that need to be edge rendered. This loop determines * which objects get to be elected for edge rendering. */ int zBufferEdgeRenderObjects(void); /** * Add edge pixels to the original image. It blends over . */ void addEdgeOver(char *doel, char *bron); /* ------------------------------------------------------------------------- */ void addEdges( char * targetbuf, int iw, int ih, int osanr, short int intens, short int intens_redux, int compat, int mode, float r, float g, float b ) { float rf, gf ,bf; /* render parameters */ selectmode = mode; imWidth = iw; imHeight = ih; compatible_mode = compat; osaCount = osanr; intensity = intens; /* Reduction doesn't exceed intensity. */ same_mat_redux = ((intens_redux < intensity)? intens_redux : intensity); rf = r * 255.0; if (rf > 255) edgeR = 255; else edgeR = rf; gf = g * 255.0; if (gf > 255) edgeG = 255; else edgeG = gf; bf = b * 255.0; if (bf > 255) edgeB = 255; else edgeB = bf; /* Go! */ initEdgeRenderBuffer(); calcEdgeRenderColBuf(targetbuf); freeEdgeRenderBuffer(); } /* end of void addEdges(char *, int, int, int, short int , int) */ /* ------------------------------------------------------------------------- */ void initEdgeRenderBuffer() { char *ptr; int i; maskBorder = 1; /* for 3 by 3 mask*/ bufWidth = imWidth + (2 * maskBorder); bufHeight = imHeight + (2 * maskBorder); /* Experimental: store the material indices. */ if (same_mat_redux) { matBuffer = MEM_callocN(sizeof(Material*) * bufWidth * bufHeight, "matBuffer"); } edgeBuffer = MEM_callocN(sizeof(int) * bufWidth * bufHeight, "edgeBuffer"); colBuffer = MEM_callocN(sizeof(char) * 4 * imWidth * imHeight, "colBuffer"); if ((edgeR != 0) || (edgeG != 0) || (edgeB != 0)) { /* Set all colbuf pixels to the edge colour. Leave alpha channel */ /* cleared. Actually, we could blend in any image here... */ ptr = colBuffer; for (i = 0; i < imWidth * imHeight; i++, ptr+=4) { ptr[0] = edgeR; ptr[1] = edgeG; ptr[2] = edgeB; ptr[3] = 0; } } #ifdef RE_EDGERENDERSAFE if (!edgeBuffer || !colBuffer) { char *fname = "initEdgeRenderBuffer"; RE_error(RE_CANNOT_ALLOCATE_MEMORY, fname); } #endif } /* end of void initEdgeRenderBuffer(void) */ /* ------------------------------------------------------------------------- */ void freeEdgeRenderBuffer(void) { if(edgeBuffer) MEM_freeN(edgeBuffer); if(colBuffer) MEM_freeN(colBuffer); if(matBuffer) MEM_freeN(matBuffer); } /* end of void freeEdgeRenderBuffer(void) */ /* ------------------------------------------------------------------------- */ void resetDistanceBuffer(void) { int i; for(i = 0; i < bufWidth * bufHeight; i++) edgeBuffer[i] = 0x7FFFFFFF; } /* end of void resetDistanceBuffer(void) */ /* ------------------------------------------------------------------------- */ void insertInEdgeBuffer(int x, int y, int dist) { int index; #ifdef RE_EDGERENDERSAFE char *fname = "insertInEdgeBuffer"; if ((x < 0) || (x > imWidth ) || (y < 0) || (y > (imHeight-1) ) ) { RE_error(RE_EDGERENDER_WRITE_OUTSIDE_BUFFER, fname); return; } #endif /* +1? */ index = (y * bufWidth) + x + maskBorder; /*exp: just dump a single index here. Maybe we can do more * sophisticated things later on. */ if (same_mat_redux) { matBuffer[index] = mat_cache; } if (edgeBuffer[index] >dist ) edgeBuffer[index] = dist; } /* end of void insertInEdgeBuffer(int x, int y, int dist) */ /* ------------------------------------------------------------------------- */ /* Modelled after rendercore.c/edge_enhance() */ void renderEdges(char *colourRect) { /* use zbuffer to define edges, add it to the image */ int val, y, x, col, *rz, *rz1, *rz2, *rz3; char *cp; int targetoffset, heightoffset; int i; int matdif; /* For now: just a bogus int, 0 when all materials * under the mask are the same, non-0 otherwise*/ int *matptr_low = 0, *matptr_cent = 0, *matptr_high = 0; int matdiffac = 0; #ifdef RE_EDGERENDER_NO_CORRECTION return; /* no edge correction */ #endif #ifdef RE_EDGERENDERSAFE fprintf(stderr, "\n*** Activated full error trace on " "edge rendering using:\n\t%s\n\t%s" "\n*** Rendering edges at %d intensity", edgeRender_c, edgeRender_h, intensity); #endif /* Old renderer uses wrong positions! With the compat switch on, the po- */ /* sitions will be corrected to be offset in the same way. */ if (compatible_mode) { targetoffset = 4 * (imWidth - 1); heightoffset = -1; } else { targetoffset = 0; heightoffset = 0; } /* Fill edges with some default values. We just copy what is in the edge */ /* This looks messy, but it appears to be ok. */ edgeBuffer[0] = edgeBuffer[bufWidth + 1]; edgeBuffer[bufWidth - 1] = edgeBuffer[(2 * bufWidth) - 2]; edgeBuffer[bufWidth * (bufHeight - 1)] = edgeBuffer[bufWidth * (bufHeight - 2) + 1]; edgeBuffer[(bufWidth * bufHeight) - 1] = edgeBuffer[(bufWidth * (bufHeight - 1)) - 2]; for (i = 1; i < bufWidth - 1; i++) { /* lieing edges */ edgeBuffer[i] = edgeBuffer[bufWidth + i]; /* bottom*/ edgeBuffer[((bufHeight - 1)*bufWidth) + i] = edgeBuffer[((bufHeight - 2)*bufWidth) + i]; /* top */ } for (i = 1; i < bufHeight - 2; i++) { /* standing edges */ edgeBuffer[i * bufWidth] = edgeBuffer[(i * bufWidth) + 1]; /* left */ edgeBuffer[((i + 1) * bufWidth) - 1] = edgeBuffer[((i + 1) * bufWidth) - 2]; /* right */ } /* same hack for the materials: */ if (same_mat_redux) { matBuffer[0] = matBuffer[bufWidth + 1]; matBuffer[bufWidth - 1] = matBuffer[(2 * bufWidth) - 2]; matBuffer[bufWidth * (bufHeight - 1)] = matBuffer[bufWidth * (bufHeight - 2) + 1]; matBuffer[(bufWidth * bufHeight) - 1] = matBuffer[(bufWidth * (bufHeight - 1)) - 2]; for (i = 1; i < bufWidth - 1; i++) { /* lieing mats */ matBuffer[i] = matBuffer[bufWidth + i]; /* bottom*/ matBuffer[((bufHeight - 1)*bufWidth) + i] = matBuffer[((bufHeight - 2)*bufWidth) + i]; /* top */ } for (i = 1; i < bufHeight - 2; i++) { /* standing mats */ matBuffer[i * bufWidth] = matBuffer[(i * bufWidth) + 1]; /* left */ matBuffer[((i + 1) * bufWidth) - 1] = matBuffer[((i + 1) * bufWidth) - 2]; /* right */ } } /* alle getallen in zbuffer 3 naar rechts shiften */ rz = edgeBuffer; if(rz==0) return; for(y=0; y < bufHeight * bufWidth; y++, rz++) { (*rz)>>= 3; } /* Distance pointers */ rz1= edgeBuffer; rz2= rz1 + bufWidth; rz3= rz2 + bufWidth; if (same_mat_redux) { matptr_low = matBuffer; matptr_cent = matptr_low + bufWidth; matptr_high = matptr_cent + bufWidth; } if (osaCount == 1) { cp = colourRect + targetoffset; } else { cp = colBuffer + targetoffset; } i = 0; for(y = 0; y < (imHeight + heightoffset) ; y++) { /* All these indices are a bit silly. I need to * rewrite this, so all buffers use the same * indexing. */ for(x = 0; x < imWidth; x++, rz1++, rz2++, rz3++, cp+=4, matptr_low++, matptr_cent++, matptr_high++) { col= abs( - rz1[0] - 2*rz1[1] - rz1[2] - 2*rz2[0] + 12*rz2[1] - 2*rz2[2] - rz3[0] - 2*rz3[1] - rz3[2]) / 3; /* Several options for matdif: * * - suppress all boundaries with 0 dif * * - weaken col dif? Or decrease intensity by * a factor when non 0 dif?? */ /* exp: matdif is non-0 if the mask-center * material differs from any of the * corners. */ if (same_mat_redux) { matdif = abs (matptr_cent[1] - matptr_low[0]) + abs (matptr_cent[1] - matptr_low[1]) + abs (matptr_cent[1] - matptr_low[2]) + abs (matptr_cent[1] - matptr_cent[0]) + abs (matptr_cent[1] - matptr_low[2]) + abs (matptr_cent[1] - matptr_high[0]) + abs (matptr_cent[1] - matptr_high[1]) + abs (matptr_cent[1] - matptr_high[2]); matdiffac = (matdif ? 0 : same_mat_redux); } col= ((intensity - matdiffac) * col)>>14; if(col>255) col= 255; /* Colour edge if * * 1. there is an appreciable, non-uniform * gradient, * * 2. there are different materials bordering * on the center pixel */ if( (col>0) /* && (matdif != 0) */) { if(osaCount > 1) { /* Currently done by tweaking alpha. The colBuffer is */ /* filled with pixels of the colour appropriate for the */ /* edges. This colour is alpha-blended over the image. */ /* This calculation determines how much colour each pixel */ /* gets. */ col/= osaCount; val= cp[3]+col; if(val>255) cp[3]= 255; else cp[3]= val; } else { /* the pixel is blackened when col is too big */ val = cp[0] - col; if(val<=0) { cp[0]= edgeR; } else { cp[0]= val; } val = cp[1] - col; if(val<=0) { cp[1]= edgeG; }else { cp[1]= val; } val = cp[2] - col; if(val<=0) { cp[2]= edgeB; } else { cp[2]= val; } } } } rz1+= 2; rz2+= 2; rz3+= 2; if (same_mat_redux) { matptr_low += 2; matptr_cent += 2; matptr_high += 2; } } } /* end of void renderEdges() */ /* ------------------------------------------------------------------------- */ void addEdgeOver(char *doel, char *bron) /* telt bron bij doel */ { float c; int mul; if( bron[3] == 0) return; if( bron[3] == 255) { /* is getest, scheelt */ *((unsigned int *)doel)= *((unsigned int *)bron); return; } /* This must be a special blend-mode, because we get a 'weird' data */ /* input format now. With edge = (c_e, a_e), picture = (c_p, a_p), we */ /* get: result = ( c_e*a_e + c_p(1 - a_e), a_p ). */ mul = 255 - bron[3]; /* Not sure if the conversion to float is really necessary here... I will*/ /* think about it another day. */ c = ((mul * doel[0] + bron[0] * bron[3])/255.0); if(c>255) {doel[0]=255;} else { doel[0]= c;} c = ((mul * doel[1] + bron[1] * bron[3])/255.0); if(c>255) {doel[1]=255;} else { doel[1]= c;} c = ((mul * doel[2] + bron[2] * bron[3])/255.0); if(c>255) {doel[2]=255;} else { doel[2]= c;} } void calcEdgeRenderColBuf(char* colTargetBuffer) { /* int part; */ int keepLooping = 1; int sample; /* zbuffer fix: here? */ Zmulx= ((float) imWidth)/2.0; Zmuly= ((float) imHeight)/2.0; /* use these buffer fill functions */ zbuffunc = fillEdgeRenderFace; zbuflinefunc = fillEdgeRenderEdge; /* always buffer the max. extent */ Aminy = 0; Amaxy = imHeight; sample = 0; /* Zsample is used internally ! */ while ( (sample < osaCount) && keepLooping ) { /* jitter */ Zjitx= -jit[sample][0]; Zjity= -jit[sample][1]; /* should reset dis buffer here */ resetDistanceBuffer(); /* kick all into a z buffer */ keepLooping = zBufferEdgeRenderObjects(); /* do filtering */ renderEdges(colTargetBuffer); if(RE_local_test_break()) keepLooping = 0; sample++; } /* correction for osa-sampling...*/ if( osaCount != 1) { char *rp, *rt; int a; rt= colTargetBuffer; rp= colBuffer; for(a = imWidth * imHeight; a>0; a--, rt+=4, rp+=4) { /* there seem to be rounding errors here... */ addEdgeOver(rt, rp); } } } /*End of void calcEdgeRenderZBuf(void) */ /* ------------------------------------------------------------------------- */ /* Clip flags etc. should still be set. When called in the span of 'normal' */ /* rendering, this should be ok. */ int zBufferEdgeRenderObjects(void) { int keepLooping; int faceCounter; /* counter for face number */ Material *ma; keepLooping = 1; ma = NULL; faceCounter = 0; while ( (faceCounter < R.totvlak) && keepLooping) { if((faceCounter & 255)==0) { Zvlr= R.blovl[faceCounter>>8]; } else Zvlr++; ma= Zvlr->mat; /*exp*/ mat_cache = ma; /* face number is used in the fill functions */ Zvlnr = faceCounter + 1; if(Zvlr->flag & R_VISIBLE) { /* here we cull all transparent faces if mode == 0 */ if (selectmode || !(ma->mode & MA_ZTRA)) { /* here we can add all kinds of extra selection criteria */ if(ma->mode & (MA_WIRE)) zbufclipwire(Zvlr); else { zbufclip(Zvlr->v1->ho, Zvlr->v2->ho, Zvlr->v3->ho, Zvlr->v1->clip, Zvlr->v2->clip, Zvlr->v3->clip); if(Zvlr->v4) { Zvlnr+= 0x800000; /* in a sense, the 'adjoint' face */ zbufclip(Zvlr->v1->ho, Zvlr->v3->ho, Zvlr->v4->ho, Zvlr->v1->clip, Zvlr->v3->clip, Zvlr->v4->clip); } } } }; if(RE_local_test_break()) keepLooping = 0; faceCounter++; } return keepLooping; } /* End of int zBufferEdgeRenderObjects(void) */ /* ------------------------------------------------------------------------- */ void fillEdgeRenderFace(float *v1, float *v2, float *v3) { /* Coordinates of the vertices are specified in ZCS */ double z0; /* used as temp var*/ double xx1; double zxd,zyd,zy0, tmp; float *minv,*maxv,*midv; int zverg,x; int my0,my2,sn1,sn2,rectx,zd; int y,omsl,xs0,xs1,xs2,xs3, dx0,dx1,dx2/* , mask */; int linex, liney, xoffset, yoffset; /* pointers to the pixel number */ /* These used to be doubles. We may want to change them back if the */ /* loss of accuracy proves to be a problem? There does not seem to be */ /* any performance issues here, so I'll just keep the doubles. */ /* float vec0[3], vec1[3], vec2[3]; */ double vec0[3], vec1[3], vec2[3]; /* MIN MAX */ /* sort vertices for min mid max y value */ if(v1[1] Amaxy)) return; if(my02.0/65536.0) { z0= (maxv[0]-minv[0])/xx1; tmp= (-65536.0*z0); dx0= CLAMPIS(tmp, INT_MIN, INT_MAX); tmp= 65536.0*(z0*(my2-minv[1])+minv[0]); xs0= CLAMPIS(tmp, INT_MIN, INT_MAX); } else { dx0= 0; xs0= 65536.0*(MIN2(minv[0],maxv[0])); } /* EDGES : DE BOVENSTE */ xx1= maxv[1]-midv[1]; if(xx1>2.0/65536.0) { z0= (maxv[0]-midv[0])/xx1; tmp= (-65536.0*z0); dx1= CLAMPIS(tmp, INT_MIN, INT_MAX); tmp= 65536.0*(z0*(my2-midv[1])+midv[0]); xs1= CLAMPIS(tmp, INT_MIN, INT_MAX); } else { dx1= 0; xs1= 65536.0*(MIN2(midv[0],maxv[0])); } /* EDGES : DE ONDERSTE */ xx1= midv[1]-minv[1]; if(xx1>2.0/65536.0) { z0= (midv[0]-minv[0])/xx1; tmp= (-65536.0*z0); dx2= CLAMPIS(tmp, INT_MIN, INT_MAX); tmp= 65536.0*(z0*(omsl-minv[1])+minv[0]); xs2= CLAMPIS(tmp, INT_MIN, INT_MAX); } else { dx2= 0; xs2= 65536.0*(MIN2(minv[0],midv[0])); } /* ZBUF DX DY */ MTC_diff3DFF(vec1, v1, v2); MTC_diff3DFF(vec2, v2, v3); MTC_cross3Double(vec0, vec1, vec2); /* cross product of two of the sides is 0 => this face is too small */ if(vec0[2]==0.0) return; if(midv[1] == maxv[1]) omsl= my2; if(omsl < Aminy) omsl= Aminy-1; /* dan neemt ie de eerste lus helemaal */ while (my2 > Amaxy) { /* my2 kan groter zijn */ xs0+=dx0; if (my2<=omsl) { xs2+= dx2; } else{ xs1+= dx1; } my2--; } xx1= (vec0[0]*v1[0]+vec0[1]*v1[1])/vec0[2]+v1[2]; zxd= -vec0[0]/vec0[2]; zyd= -vec0[1]/vec0[2]; zy0= my2*zyd+xx1; zd= (int)CLAMPIS(zxd, INT_MIN, INT_MAX); /* start-ofset in rect */ /* rectx= R.rectx; */ /* I suspect this var needs very careful setting... When edge rendering */ /* is on, this is strange */ rectx = imWidth; yoffset = my2; xoffset = 0; xs3= 0; /* flag */ if(dx0>dx1) { MTC_swapInt(&xs0, &xs1); MTC_swapInt(&dx0, &dx1); xs3= 1; /* flag */ } liney = yoffset; for(y=my2;y>omsl;y--) { sn1= xs0>>16; xs0+= dx0; sn2= xs1>>16; xs1+= dx1; sn1++; if(sn2>=rectx) sn2= rectx-1; if(sn1<0) sn1= 0; zverg= (int) CLAMPIS((sn1*zxd+zy0), INT_MIN, INT_MAX); if ((sn1 < 0) || (sn1>imWidth) ) printf("\n sn1 exceeds line"); linex = xoffset + sn1; liney = yoffset; x= sn2-sn1; while(x>=0) { insertInEdgeBuffer(linex , liney, zverg); /* line y not needed here */ zverg+= zd; linex++; x--; } zy0-= zyd; yoffset--; } if(xs3) { xs0= xs1; dx0= dx1; } if(xs0>xs2) { xs3= xs0; xs0= xs2; xs2= xs3; xs3= dx0; dx0= dx2; dx2= xs3; } for(; y>=my0; y--) { sn1= xs0>>16; xs0+= dx0; sn2= xs2>>16; xs2+= dx2; sn1++; if(sn2>=rectx) sn2= rectx-1; if(sn1<0) sn1= 0; zverg= (int) CLAMPIS((sn1*zxd+zy0), INT_MIN, INT_MAX); linex = sn1; liney = yoffset; x= sn2-sn1; while(x>=0) { insertInEdgeBuffer(linex, liney, zverg); /* line y not needed here */ zverg+= zd; linex++; x--; } zy0-=zyd; yoffset--; } } /* end of void fillEdgeRenderFace(float *v1, float *v2, float *v3) */ /* ------------------------------------------------------------------------- */ void fillEdgeRenderEdge(float *vec1, float *vec2) { int start, end, x, y, oldx, oldy, ofs; int dz, vergz/* , mask */; float dx, dy; float v1[3], v2[3]; int linex, liney; int xoffset, yoffset; dx= vec2[0]-vec1[0]; dy= vec2[1]-vec1[1]; if(fabs(dx) > fabs(dy)) { /* alle lijnen van links naar rechts */ if(vec1[0]= imWidth) end = imWidth - 1; oldy= floor(v1[1]); dy/= dx; vergz= v1[2]; dz= (v2[2]-v1[2])/dx; yoffset = oldy; xoffset = start; if(dy<0) ofs= -imWidth; else ofs= imWidth; liney = yoffset; linex = xoffset; for(x= start; x<=end; x++, xoffset++) { y= floor(v1[1]); if(y!=oldy) { oldy= y; liney++; } if(x>=0 && y>=Aminy && y<=Amaxy) { insertInEdgeBuffer(linex , liney, vergz); } v1[1]+= dy; vergz+= dz; } } else { /* alle lijnen van onder naar boven */ if(vec1[1]Amaxy || endAmaxy) end= Amaxy; oldx= floor(v1[0]); dx/= dy; vergz= v1[2]; dz= (v2[2]-v1[2])/dy; yoffset = start; xoffset = oldx; if(dx<0) ofs= -1; else ofs= 1; linex = xoffset; liney = yoffset; for(y= start; y<=end; y++, liney++) { x= floor(v1[0]); if(x!=oldx) { oldx= x; linex += ofs; } if(x>=0 && y>=Aminy && (x < imWidth)) { insertInEdgeBuffer(linex, liney, vergz); } v1[0]+= dx; vergz+= dz; } } } /* End of void fillEdgeRenderEdge(float *vec1, float *vec2) */ /* ------------------------------------------------------------------------- */ /* eof edgeRender.c */