/* * * ***** BEGIN GPL 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. * * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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): Morten Mikkelsen. * * ***** END GPL LICENSE BLOCK ***** * filter.c * */ /** \file blender/imbuf/intern/filter.c * \ingroup imbuf */ #include "MEM_guardedalloc.h" #include "BLI_utildefines.h" #include "IMB_imbuf_types.h" #include "IMB_imbuf.h" #include "IMB_filter.h" #include "imbuf.h" /************************************************************************/ /* FILTERS */ /************************************************************************/ static void filtrow(unsigned char *point, int x) { unsigned int c1,c2,c3,error; if (x>1){ c1 = c2 = *point; error = 2; for(x--;x>0;x--){ c3 = point[4]; c1 += (c2<<1) + c3 + error; error = c1 & 3; *point = c1 >> 2; point += 4; c1=c2; c2=c3; } *point = (c1 + (c2<<1) + c2 + error) >> 2; } } static void filtrowf(float *point, int x) { float c1,c2,c3; if (x>1){ c1 = c2 = *point; for(x--;x>0;x--){ c3 = point[4]; c1 += (c2 * 2) + c3; *point = 0.25f*c1; point += 4; c1=c2; c2=c3; } *point = 0.25f*(c1 + (c2 * 2) + c2); } } static void filtcolum(unsigned char *point, int y, int skip) { unsigned int c1,c2,c3,error; unsigned char *point2; if (y>1){ c1 = c2 = *point; point2 = point; error = 2; for(y--;y>0;y--){ point2 += skip; c3 = *point2; c1 += (c2<<1) + c3 +error; error = c1 & 3; *point = c1 >> 2; point=point2; c1=c2; c2=c3; } *point = (c1 + (c2<<1) + c2 + error) >> 2; } } static void filtcolumf(float *point, int y, int skip) { float c1,c2,c3, *point2; if (y>1){ c1 = c2 = *point; point2 = point; for(y--;y>0;y--){ point2 += skip; c3 = *point2; c1 += (c2 * 2) + c3; *point = 0.25f*c1; point=point2; c1=c2; c2=c3; } *point = 0.25f*(c1 + (c2 * 2) + c2); } } void IMB_filtery(struct ImBuf *ibuf) { unsigned char *point; float *pointf; int x, y, skip; point = (unsigned char *)ibuf->rect; pointf = ibuf->rect_float; x = ibuf->x; y = ibuf->y; skip = x<<2; for (;x>0;x--){ if (point) { if (ibuf->depth > 24) filtcolum(point,y,skip); point++; filtcolum(point,y,skip); point++; filtcolum(point,y,skip); point++; filtcolum(point,y,skip); point++; } if (pointf) { if (ibuf->depth > 24) filtcolumf(pointf,y,skip); pointf++; filtcolumf(pointf,y,skip); pointf++; filtcolumf(pointf,y,skip); pointf++; filtcolumf(pointf,y,skip); pointf++; } } } void imb_filterx(struct ImBuf *ibuf) { unsigned char *point; float *pointf; int x, y, skip; point = (unsigned char *)ibuf->rect; pointf = ibuf->rect_float; x = ibuf->x; y = ibuf->y; skip = (x<<2) - 3; for (;y>0;y--){ if (point) { if (ibuf->depth > 24) filtrow(point,x); point++; filtrow(point,x); point++; filtrow(point,x); point++; filtrow(point,x); point+=skip; } if (pointf) { if (ibuf->depth > 24) filtrowf(pointf,x); pointf++; filtrowf(pointf,x); pointf++; filtrowf(pointf,x); pointf++; filtrowf(pointf,x); pointf+=skip; } } } void IMB_filterN(ImBuf *out, ImBuf *in) { register char *row1, *row2, *row3; register char *cp, *r11, *r13, *r21, *r23, *r31, *r33; int rowlen, x, y; rowlen= in->x; for(y=0; yy; y++) { /* setup rows */ row2= (char*)(in->rect + y*rowlen); row1= (y == 0)? row2: row2 - 4*rowlen; row3= (y == in->y-1)? row2: row2 + 4*rowlen; cp= (char *)(out->rect + y*rowlen); for(x=0; x>4; cp[1]= (r11[1] + 2*row1[1] + r13[1] + 2*r21[1] + 4*row2[1] + 2*r23[1] + r31[1] + 2*row3[1] + r33[1])>>4; cp[2]= (r11[2] + 2*row1[2] + r13[2] + 2*r21[2] + 4*row2[2] + 2*r23[2] + r31[2] + 2*row3[2] + r33[2])>>4; cp[3]= (r11[3] + 2*row1[3] + r13[3] + 2*r21[3] + 4*row2[3] + 2*r23[3] + r31[3] + 2*row3[3] + r33[3])>>4; cp+=4; row1+=4; row2+=4; row3+=4; } } } void IMB_filter(struct ImBuf *ibuf) { IMB_filtery(ibuf); imb_filterx(ibuf); } void IMB_mask_filter_extend(char *mask, int width, int height) { char *row1, *row2, *row3; int rowlen, x, y; char *temprect; rowlen= width; /* make a copy, to prevent flooding */ temprect= MEM_dupallocN(mask); for(y=1; y<=height; y++) { /* setup rows */ row1= (char *)(temprect + (y-2)*rowlen); row2= row1 + rowlen; row3= row2 + rowlen; if(y==1) row1= row2; else if(y==height) row3= row2; for(x=0; xrect_float) { for(x=0; xx; x++) { for(y=0; yy; y++) { if (mask[ibuf->x*y + x] == val) { float *col= ibuf->rect_float + 4*(ibuf->x*y + x); col[0] = col[1] = col[2] = col[3] = 0.0f; } } } } else { /* char buffer */ for(x=0; xx; x++) { for(y=0; yy; y++) { if (mask[ibuf->x*y + x] == val) { char *col= (char *)(ibuf->rect + ibuf->x*y + x); col[0] = col[1] = col[2] = col[3] = 0; } } } } } 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, int filter) { 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= 1; /*k= 0; for(i = -n; i <= n; i++) for(j = -n; j <= n; j++) weight[k++] = sqrt((float) i * i + j * j); */ weight[0]=1; weight[1]=2; weight[2]=1; weight[3]=2; weight[4]=0; weight[5]=2; weight[6]=1; weight[7]=2; weight[8]=1; /* run passes */ for(r = 0; cannot_early_out == 1 && r < filter; r++) { int x, y; cannot_early_out = 0; for(y= 0; y 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; } } } } } /* 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 */ void IMB_remakemipmap(ImBuf *ibuf, int use_filter) { ImBuf *hbuf = ibuf; int curmap = 0; ibuf->miptot= 1; while(curmap < IB_MIPMAP_LEVELS) { if(ibuf->mipmap[curmap]) { if(use_filter) { ImBuf *nbuf= IMB_allocImBuf(hbuf->x, hbuf->y, 32, IB_rect); IMB_filterN(nbuf, hbuf); imb_onehalf_no_alloc(ibuf->mipmap[curmap], nbuf); IMB_freeImBuf(nbuf); } else imb_onehalf_no_alloc(ibuf->mipmap[curmap], hbuf); } ibuf->miptot= curmap+2; hbuf= ibuf->mipmap[curmap]; if(hbuf) hbuf->miplevel= curmap+1; if(!hbuf || (hbuf->x <= 2 && hbuf->y <= 2)) break; curmap++; } } /* frees too (if there) and recreates new data */ void IMB_makemipmap(ImBuf *ibuf, int use_filter) { ImBuf *hbuf = ibuf; int curmap = 0; imb_freemipmapImBuf(ibuf); ibuf->miptot= 1; while(curmap < IB_MIPMAP_LEVELS) { if(use_filter) { ImBuf *nbuf= IMB_allocImBuf(hbuf->x, hbuf->y, 32, IB_rect); IMB_filterN(nbuf, hbuf); ibuf->mipmap[curmap] = IMB_onehalf(nbuf); IMB_freeImBuf(nbuf); } else ibuf->mipmap[curmap] = IMB_onehalf(hbuf); ibuf->miptot= curmap+2; hbuf= ibuf->mipmap[curmap]; hbuf->miplevel= curmap+1; if(hbuf->x <= 2 && hbuf->y <= 2) break; curmap++; } } ImBuf *IMB_getmipmap(ImBuf *ibuf, int level) { CLAMP(level, 0, ibuf->miptot-1); return (level == 0)? ibuf: ibuf->mipmap[level-1]; } void IMB_premultiply_rect(unsigned int *rect, int depth, int w, int h) { char *cp; int x, y, val; if(depth == 24) { /* put alpha at 255 */ cp= (char *)(rect); for(y=0; y>8; cp[1]= (cp[1]*val)>>8; cp[2]= (cp[2]*val)>>8; } } } } void IMB_premultiply_rect_float(float *rect_float, int depth, int w, int h) { float val, *cp; int x, y; if(depth==24) { /* put alpha at 1.0 */ cp= rect_float; for(y=0; yrect) IMB_premultiply_rect(ibuf->rect, ibuf->depth, ibuf->x, ibuf->y); if(ibuf->rect_float) IMB_premultiply_rect_float(ibuf->rect_float, ibuf->depth, ibuf->x, ibuf->y); }