New dilation function from Morten Mikkelsen (aka sparky).
authorSergey Sharybin <sergey.vfx@gmail.com>
Sun, 24 Jul 2011 10:26:22 +0000 (10:26 +0000)
committerSergey Sharybin <sergey.vfx@gmail.com>
Sun, 24 Jul 2011 10:26:22 +0000 (10:26 +0000)
This commit fixes very noticeable seams caused by margins
calculated incorrectly. This commit changes way margin is
calculated in and makes textures really seamless.

Also margin limited to 32 isn't good now -- artists are baking
really large textures nowadays so margin is now limited to 64px.

Thank you, Morten!

source/blender/imbuf/IMB_imbuf.h
source/blender/imbuf/intern/filter.c
source/blender/makesrna/intern/rna_scene.c
source/blender/render/intern/source/rendercore.c

index ff01e3a8a1e7838a9a1118b7d9670e2ea7e7c00f..36123592c546e289f519a871c60d99b99b551dc2 100644 (file)
@@ -252,7 +252,7 @@ void IMB_filter(struct ImBuf *ibuf);
 void IMB_filterN(struct ImBuf *out, struct ImBuf *in);
 void IMB_mask_filter_extend(char *mask, int width, int height);
 void IMB_mask_clear(struct ImBuf *ibuf, char *mask, int val);
-void IMB_filter_extend(struct ImBuf *ibuf, char *mask);
+void IMB_filter_extend(struct ImBuf *ibuf, char *mask, int filter);
 void IMB_makemipmap(struct ImBuf *ibuf, int use_filter);
 void IMB_remakemipmap(struct ImBuf *ibuf, int use_filter);
 struct ImBuf *IMB_getmipmap(struct ImBuf *ibuf, int level);
index d12360e5a7ead984f9436ec12f621018d9bdd6e6..1644e653df4aa87826999835ee81f3974d25f4cf 100644 (file)
@@ -21,7 +21,7 @@
  *
  * The Original Code is: all of this file.
  *
- * Contributor(s): none yet.
+ * Contributor(s): Morten Mikkelsen.
  *
  * ***** END GPL LICENSE BLOCK *****
  * filter.c
@@ -326,121 +326,132 @@ void IMB_mask_clear(ImBuf *ibuf, char *mask, int val)
        }
 }
 
-#define EXTEND_PIXEL(color, w) if((color)[3]) {r+= w*(color)[0]; g+= w*(color)[1]; b+= w*(color)[2]; a+= w*(color)[3]; tot+=w;}
+static int filter_make_index(const int x, const int y, const int w, const int h)
+{
+       if(x<0 || x>=w || y<0 || y>=h) return -1;       /* return bad index */
+       else return y*w+x;
+}
+
+static int check_pixel_assigned(const void *buffer, const char *mask, const int index, const int depth, const int is_float)
+{
+       int res = 0;
+
+       if(index>=0) {
+               const int alpha_index = depth*index+(depth-1);
+
+               if(mask!=NULL) {
+                       res = mask[index]!=0 ? 1 : 0;
+               }
+               else if( (is_float && ((const float *) buffer)[alpha_index]!=0.0f) ||
+                               (!is_float && ((const unsigned char *) buffer)[alpha_index]!=0) ) {
+                       res=1;
+               }
+       }
+
+       return res;
+}
 
 /* if alpha is zero, it checks surrounding pixels and averages color. sets new alphas to 1.0
  * 
  * When a mask is given, only effect pixels with a mask value of 1, defined as BAKE_MASK_MARGIN in rendercore.c
  * */
-void IMB_filter_extend(struct ImBuf *ibuf, char *mask)
+void IMB_filter_extend(struct ImBuf *ibuf, char *mask, int filter)
 {
-       register char *row1, *row2, *row3;
-       register char *cp; 
-       int rowlen, x, y;
-       
-       rowlen= ibuf->x;
-       
-       
-       if (ibuf->rect_float) {
-               float *temprect;
-               float *row1f, *row2f, *row3f;
-               float *fp;
-               temprect= MEM_dupallocN(ibuf->rect_float);
-               
-               for(y=1; y<=ibuf->y; y++) {
-                       /* setup rows */
-                       row1f= (float *)(temprect + (y-2)*rowlen*4);
-                       row2f= row1f + 4*rowlen;
-                       row3f= row2f + 4*rowlen;
-                       if(y==1)
-                               row1f= row2f;
-                       else if(y==ibuf->y)
-                               row3f= row2f;
-                       
-                       fp= (float *)(ibuf->rect_float + (y-1)*rowlen*4);
-                               
-                       for(x=0; x<rowlen; x++) {
-                               if((mask==NULL && fp[3]==0.0f) || (mask && mask[((y-1)*rowlen)+x]==1)) {
-                                       int tot= 0;
-                                       float r=0.0f, g=0.0f, b=0.0f, a=0.0f;
-                                       
-                                       EXTEND_PIXEL(row1f, 1);
-                                       EXTEND_PIXEL(row2f, 2);
-                                       EXTEND_PIXEL(row3f, 1);
-                                       EXTEND_PIXEL(row1f+4, 2);
-                                       EXTEND_PIXEL(row3f+4, 2);
-                                       if(x!=rowlen-1) {
-                                               EXTEND_PIXEL(row1f+8, 1);
-                                               EXTEND_PIXEL(row2f+8, 2);
-                                               EXTEND_PIXEL(row3f+8, 1);
-                                       }                                       
-                                       if(tot) {
-                                               fp[0]= r/tot;
-                                               fp[1]= g/tot;
-                                               fp[2]= b/tot;
-                                               fp[3]= a/tot;
+       const int width= ibuf->x;
+       const int height= ibuf->y;
+       const int depth= 4;             /* always 4 channels */
+       const int chsize= ibuf->rect_float ? sizeof(float) : sizeof(unsigned char);
+       const int bsize= width*height*depth*chsize;
+       const int is_float= ibuf->rect_float!=NULL;
+       void *dstbuf= (void *) MEM_dupallocN(ibuf->rect_float ? (void *) ibuf->rect_float : (void *) ibuf->rect);
+       char *dstmask= mask==NULL ? NULL : (char *) MEM_dupallocN(mask);
+       void *srcbuf= ibuf->rect_float ? (void *) ibuf->rect_float : (void *) ibuf->rect;
+       char *srcmask= mask;
+       int cannot_early_out= 1, r, n, k, i, j, c;
+       float weight[25];
+
+       /* build a weights buffer */
+       n= 2;
+       k= 0;
+       for(i = -n; i <= n; i++)
+               for(j = -n; j <= n; j++)
+                       weight[k++] = sqrt((float) i * i + j * j);
+
+       /* run passes */
+       for(r = 0; cannot_early_out == 1 && r < filter; r++) {
+               int x, y;
+               cannot_early_out = 0;
+
+               for(y= 0; y<height; y++) {
+                       for(x= 0; x<width; x++) {
+                               const int index= filter_make_index(x, y, width, height);
+
+                               /* only update unassigned pixels */
+                               if(!check_pixel_assigned(srcbuf, srcmask, index, depth, is_float)) {
+                                       float tmp[4];
+                                       float wsum=0;
+                                       float acc[4]={0,0,0,0};
+                                       k = 0;
+
+                                       if (check_pixel_assigned(srcbuf, srcmask, filter_make_index(x-1, y, width, height), depth, is_float) ||
+                                               check_pixel_assigned(srcbuf, srcmask, filter_make_index(x+1, y, width, height), depth, is_float) ||
+                                               check_pixel_assigned(srcbuf, srcmask, filter_make_index(x, y-1, width, height), depth, is_float) ||
+                                               check_pixel_assigned(srcbuf, srcmask, filter_make_index(x, y+1, width, height), depth, is_float)) {
+                                               for(i= -n; i<=n; i++) {
+                                                       for(j=-n; j<=n; j++) {
+                                                               if(i != 0 || j != 0) {
+                                                                       const int tmpindex= filter_make_index(x+i, y+j, width, height);
+
+                                                                       if(check_pixel_assigned(srcbuf, srcmask, tmpindex, depth, is_float))    { 
+                                                                               if(is_float) {
+                                                                                       for(c=0; c<depth; c++)
+                                                                                               tmp[c] = ((const float *) srcbuf)[depth*tmpindex+c];
+                                                                               }
+                                                                               else {
+                                                                                       for(c=0; c<depth; c++)
+                                                                                               tmp[c] = (float) ((const unsigned char *) srcbuf)[depth*tmpindex+c];
+                                                                               }
+
+                                                                               wsum+= weight[k];
+
+                                                                               for(c=0; c<depth; c++)
+                                                                                       acc[c]+= weight[k] * tmp[c];
+                                                                       }
+                                                               }
+                                                               k++;
+                                                       }
+                                               }
+
+                                               if(wsum!=0) {
+                                                       for(c=0; c<depth; c++)
+                                                               acc[c]/= wsum;
+
+                                                       if(is_float) {
+                                                               for(c=0; c<depth; c++)
+                                                                       ((float *) dstbuf)[depth*index+c] = acc[c];
+                                                       }
+                                                       else {
+                                                               for(c=0; c<depth; c++) {
+                                                                       ((unsigned char *) dstbuf)[depth*index+c]= acc[c] > 255 ? 255 : (acc[c] < 0 ? 0 : ((unsigned char) (acc[c]+0.5f)));
+                                                               }
+                                                       }
+
+                                                       if(dstmask!=NULL) dstmask[index]=FILTER_MASK_MARGIN;    /* assigned */
+                                                       cannot_early_out = 1;
+                                               }
                                        }
                                }
-                               fp+=4; 
-                               
-                               if(x!=0) {
-                                       row1f+=4; row2f+=4; row3f+=4;
-                               }
                        }
                }
 
-               MEM_freeN(temprect);
-       }
-       else if(ibuf->rect) {
-               int *temprect;
-               
-               /* make a copy, to prevent flooding */
-               temprect= MEM_dupallocN(ibuf->rect);
-               
-               for(y=1; y<=ibuf->y; y++) {
-                       /* setup rows */
-                       row1= (char *)(temprect + (y-2)*rowlen);
-                       row2= row1 + 4*rowlen;
-                       row3= row2 + 4*rowlen;
-                       if(y==1)
-                               row1= row2;
-                       else if(y==ibuf->y)
-                               row3= row2;
-                       
-                       cp= (char *)(ibuf->rect + (y-1)*rowlen);
-                       
-                       for(x=0; x<rowlen; x++) {
-                               /*if(cp[3]==0) {*/
-                               if((mask==NULL && cp[3]==0) || (mask && mask[((y-1)*rowlen)+x]==1)) {
-                                       int tot= 0, r=0, g=0, b=0, a=0;
-                                       
-                                       EXTEND_PIXEL(row1, 1);
-                                       EXTEND_PIXEL(row2, 2);
-                                       EXTEND_PIXEL(row3, 1);
-                                       EXTEND_PIXEL(row1+4, 2);
-                                       EXTEND_PIXEL(row3+4, 2);
-                                       if(x!=rowlen-1) {
-                                               EXTEND_PIXEL(row1+8, 1);
-                                               EXTEND_PIXEL(row2+8, 2);
-                                               EXTEND_PIXEL(row3+8, 1);
-                                       }                                       
-                                       if(tot) {
-                                               cp[0]= r/tot;
-                                               cp[1]= g/tot;
-                                               cp[2]= b/tot;
-                                               cp[3]= a/tot;
-                                       }
-                               }
-                               cp+=4;
-                               
-                               if(x!=0) {
-                                       row1+=4; row2+=4; row3+=4;
-                               }
-                       }
-               }
-               
-               MEM_freeN(temprect);
+               /* keep the original buffer up to date. */
+               memcpy(srcbuf, dstbuf, bsize);
+               if(dstmask!=NULL) memcpy(srcmask, dstmask, width*height);
        }
+
+       /* free memory */
+       MEM_freeN(dstbuf);
+       if(dstmask!=NULL) MEM_freeN(dstmask);
 }
 
 /* threadsafe version, only recreates existing maps */
index 9f751da484e43349f07c811f43a7b68d7bdd29af..d9475eaa6832d65bb7bf13d8ae9a26902d1b379f 100644 (file)
@@ -2815,7 +2815,7 @@ static void rna_def_scene_render_data(BlenderRNA *brna)
        
        prop= RNA_def_property(srna, "bake_margin", PROP_INT, PROP_NONE);
        RNA_def_property_int_sdna(prop, NULL, "bake_filter");
-       RNA_def_property_range(prop, 0, 32);
+       RNA_def_property_range(prop, 0, 64);
        RNA_def_property_ui_text(prop, "Margin", "Amount of pixels to extend the baked result with, as post process filter");
 
        prop= RNA_def_property(srna, "bake_distance", PROP_FLOAT, PROP_NONE);
index 3aca334cffe096d74a57fd6a6d8e45a15f2972ee..a7e19c8db4f474123ee73f975aaf7e3847e43424 100644 (file)
@@ -2577,27 +2577,7 @@ void RE_bake_ibuf_filter(ImBuf *ibuf, char *mask, const int filter)
 
        /* Margin */
        if(filter) {
-               char *temprect;
-               int i;
-
-               /* extend the mask +2 pixels from the image,
-                * this is so colors dont blend in from outside */
-
-               for(i=0; i< filter; i++)
-                       IMB_mask_filter_extend(mask, ibuf->x, ibuf->y);
-
-               temprect = MEM_dupallocN(mask);
-
-               /* expand twice to clear this many pixels, so they blend back in */
-               IMB_mask_filter_extend(temprect, ibuf->x, ibuf->y);
-               IMB_mask_filter_extend(temprect, ibuf->x, ibuf->y);
-
-               /* clear all pixels in the margin */
-               IMB_mask_clear(ibuf, temprect, FILTER_MASK_MARGIN);
-               MEM_freeN(temprect);
-
-               for(i= 0; i < filter; i++)
-                       IMB_filter_extend(ibuf, mask);
+               IMB_filter_extend(ibuf, mask, filter);
        }
 
        /* if the bake results in new alpha then change the image setting */