From e3c6ae9b89d1d0c7bb6957f81b9fd9a89477b2d0 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Tue, 21 Jul 2009 13:20:35 +0000 Subject: 2.5: Texture Filtering Patch by Alfredo de Greef with high quality image texture filters. This adds 3 new filters: * SAT: Summed Area Tables. This is like mipmaps, but using somewhat more memory avoids some artifacts. * EWA: Ellipitical Weighted Average, anisotropic filter. * FELINE: Fast elliptical lines for anisotropic texture mapping. The one change I made to this was to try to fix an alpha/premul problem, hopefully I didn't break anything, it looks compatible with the existing filter now for me. --- source/blender/blenkernel/intern/texture.c | 5 +- source/blender/blenloader/intern/readfile.c | 6 + .../editors/interface/interface_templates.c | 8 +- source/blender/editors/space_view3d/view3d_draw.c | 2 +- source/blender/imbuf/IMB_imbuf.h | 2 +- source/blender/imbuf/intern/filter.c | 88 +- source/blender/makesdna/DNA_texture_types.h | 13 +- source/blender/makesrna/intern/rna_texture.c | 59 +- source/blender/render/intern/source/imagetexture.c | 947 ++++++++++++++++++++- source/blender/render/intern/source/texture.c | 50 +- 10 files changed, 1118 insertions(+), 62 deletions(-) (limited to 'source') diff --git a/source/blender/blenkernel/intern/texture.c b/source/blender/blenkernel/intern/texture.c index bcdea06936f..db864dc9f1e 100644 --- a/source/blender/blenkernel/intern/texture.c +++ b/source/blender/blenkernel/intern/texture.c @@ -435,12 +435,15 @@ void default_tex(Tex *tex) VarStruct *varstr; int a; + tex->type= TEX_CLOUDS; tex->stype= 0; tex->flag= TEX_CHECKER_ODD; - tex->imaflag= TEX_INTERPOL+TEX_MIPMAP+TEX_USEALPHA; + tex->imaflag= TEX_INTERPOL|TEX_MIPMAP|TEX_USEALPHA; tex->extend= TEX_REPEAT; tex->cropxmin= tex->cropymin= 0.0; tex->cropxmax= tex->cropymax= 1.0; + tex->texfilter = TXF_DEFAULT; + tex->afmax = 8; tex->xrepeat= tex->yrepeat= 1; tex->fie_ima= 2; tex->sfra= 1; diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c index c43c720bad0..6da444bc88e 100644 --- a/source/blender/blenloader/intern/readfile.c +++ b/source/blender/blenloader/intern/readfile.c @@ -9273,6 +9273,7 @@ static void do_versions(FileData *fd, Library *lib, Main *main) World *wo; Object *ob; Material *ma; + Tex *tex; Scene *sce; ToolSettings *ts; int i; @@ -9349,6 +9350,11 @@ static void do_versions(FileData *fd, Library *lib, Main *main) } } + /* texture filter */ + for(tex = main->tex.first; tex; tex = tex->id.next) + if(tex->afmax == 0) + tex->afmax= 8; + for(ma = main->mat.first; ma; ma = ma->id.next) { if(ma->mode & MA_HALO) { ma->material_type= MA_TYPE_HALO; diff --git a/source/blender/editors/interface/interface_templates.c b/source/blender/editors/interface/interface_templates.c index f8d1e1d7ab9..8b01c341565 100644 --- a/source/blender/editors/interface/interface_templates.c +++ b/source/blender/editors/interface/interface_templates.c @@ -1215,10 +1215,10 @@ void uiTemplatePreview(uiLayout *layout, ID *id, ID *parent) uiBlockSetHandleFunc(block, do_preview_buttons, NULL); /* add buttons */ - if(id) { - if(GS(id->name) == ID_MA || (parent && GS(parent->name) == ID_MA)) { - if(GS(id->name) == ID_MA) ma= (Material*)id; - else ma= (Material*)parent; + if(pid) { + if(GS(pid->name) == ID_MA || (pparent && GS(pparent->name) == ID_MA)) { + if(GS(pid->name) == ID_MA) ma= (Material*)pid; + else ma= (Material*)pparent; uiLayoutColumn(row, 1); diff --git a/source/blender/editors/space_view3d/view3d_draw.c b/source/blender/editors/space_view3d/view3d_draw.c index dd4e67e612c..d80a26e50f8 100644 --- a/source/blender/editors/space_view3d/view3d_draw.c +++ b/source/blender/editors/space_view3d/view3d_draw.c @@ -1345,7 +1345,7 @@ static void draw_bgpic(Scene *scene, ARegion *ar, View3D *v3d) int mip= 0; if(ibuf->mipmap[0]==NULL) - IMB_makemipmap(ibuf, 0); + IMB_makemipmap(ibuf, 0, 0); while(tzoom < 1.0f && mip<8 && ibuf->mipmap[mip]) { tzoom*= 2.0f; diff --git a/source/blender/imbuf/IMB_imbuf.h b/source/blender/imbuf/IMB_imbuf.h index 1d8035a2358..d2f561438b9 100644 --- a/source/blender/imbuf/IMB_imbuf.h +++ b/source/blender/imbuf/IMB_imbuf.h @@ -319,7 +319,7 @@ void IMB_antialias(struct ImBuf * ibuf); void IMB_filter(struct ImBuf *ibuf); void IMB_filterN(struct ImBuf *out, struct ImBuf *in); void IMB_filter_extend(struct ImBuf *ibuf, char *mask); -void IMB_makemipmap(struct ImBuf *ibuf, int use_filter); +void IMB_makemipmap(struct ImBuf *ibuf, int use_filter, int SAT); /** * diff --git a/source/blender/imbuf/intern/filter.c b/source/blender/imbuf/intern/filter.c index 9802405fd8d..cc3315c7696 100644 --- a/source/blender/imbuf/intern/filter.c +++ b/source/blender/imbuf/intern/filter.c @@ -371,6 +371,7 @@ void IMB_filter_extend(struct ImBuf *ibuf, char *mask) } } +#if 0 void IMB_makemipmap(ImBuf *ibuf, int use_filter) { ImBuf *hbuf= ibuf; @@ -394,5 +395,90 @@ void IMB_makemipmap(ImBuf *ibuf, int use_filter) minsize= hbuf->xy?hbuf->x:hbuf->y; } } +#endif - +void IMB_makemipmap(ImBuf *ibuf, int use_filter, int SAT) +{ + if (SAT) { + // to maximize precision subtract image average, use intermediate double SAT, + // only convert to float at the end + const double dv = 1.0/255.0; + double avg[4] = {0, 0, 0, 0}; + const int x4 = ibuf->x << 2; + int x, y, i; + ImBuf* sbuf = IMB_allocImBuf(ibuf->x, ibuf->y, 32, IB_rectfloat, 0); + double *satp, *satbuf = MEM_callocN(sizeof(double)*ibuf->x*ibuf->y*4, "tmp SAT buf"); + const double mf = ibuf->x*ibuf->y; + float* fp; + ibuf->mipmap[0] = sbuf; + if (ibuf->rect_float) { + fp = ibuf->rect_float; + for (y=0; yy; ++y) + for (x=0; xx; ++x) { + avg[0] += *fp++; + avg[1] += *fp++; + avg[2] += *fp++; + avg[3] += *fp++; + } + } + else { + char* cp = (char*)ibuf->rect; + for (y=0; yy; ++y) + for (x=0; xx; ++x) { + avg[0] += *cp++ * dv; + avg[1] += *cp++ * dv; + avg[2] += *cp++ * dv; + avg[3] += *cp++ * dv; + } + } + avg[0] /= mf; + avg[1] /= mf; + avg[2] /= mf; + avg[3] /= mf; + for (y=0; yy; ++y) + for (x=0; xx; ++x) { + const unsigned int p = (x + y*ibuf->x) << 2; + char* cp = (char*)ibuf->rect + p; + fp = ibuf->rect_float + p; + satp = satbuf + p; + for (i=0; i<4; ++i, ++cp, ++fp, ++satp) { + double sv = (ibuf->rect_float ? (double)*fp : (double)(*cp)*dv) - avg[i]; + if (x > 0) sv += satp[-4]; + if (y > 0) sv += satp[-x4]; + if (x > 0 && y > 0) sv -= satp[-x4 - 4]; + *satp = sv; + } + } + fp = sbuf->rect_float; + satp = satbuf; + for (y=0; yy; ++y) + for (x=0; xx; ++x) { + *fp++ = (float)*satp++; + *fp++ = (float)*satp++; + *fp++ = (float)*satp++; + *fp++ = (float)*satp++; + } + MEM_freeN(satbuf); + fp = &sbuf->rect_float[(sbuf->x - 1 + (sbuf->y - 1)*sbuf->x) << 2]; + fp[0] = avg[0]; + fp[1] = avg[1]; + fp[2] = avg[2]; + fp[3] = avg[3]; + } + else { + ImBuf *hbuf = ibuf; + int curmap = 0; + while (curmap < IB_MIPMAP_LEVELS) { + if (use_filter) { + ImBuf *nbuf= IMB_allocImBuf(hbuf->x, hbuf->y, 32, IB_rect, 0); + IMB_filterN(nbuf, hbuf); + ibuf->mipmap[curmap] = IMB_onehalf(nbuf); + IMB_freeImBuf(nbuf); + } + else ibuf->mipmap[curmap] = IMB_onehalf(hbuf); + hbuf = ibuf->mipmap[curmap]; + if (hbuf->x == 1 && hbuf->y == 1) break; + curmap++; + } + } +} diff --git a/source/blender/makesdna/DNA_texture_types.h b/source/blender/makesdna/DNA_texture_types.h index 6b7bfbdcd83..e1dd21a8ccb 100644 --- a/source/blender/makesdna/DNA_texture_types.h +++ b/source/blender/makesdna/DNA_texture_types.h @@ -150,11 +150,13 @@ typedef struct Tex { /* newnoise: noisebasis type for clouds/marble/etc, noisebasis2 only used for distorted noise */ short noisebasis, noisebasis2; - + short imaflag, flag; short type, stype; float cropxmin, cropymin, cropxmax, cropymax; + int texfilter; + int afmax; // anisotropic filter maximum value, ewa -> max eccentricity, feline -> max probes short xrepeat, yrepeat; short extend; @@ -253,6 +255,15 @@ typedef struct TexMapping { #define TEX_GAUSS_MIP 4096 #define TEX_FILTER_MIN 8192 +/* texfilter */ +// TXF_DEFAULT -> blender's old texture filtering method +#define TXF_DEFAULT 0 +#define TXF_EWA 1 +#define TXF_FELINE 2 +#define TXF_AREA 3 +// TXF_SAT only available when mipmaps disabled +#define TXF_SAT 4 + /* imaflag unused, only for version check */ #define TEX_FIELDS_ 8 #define TEX_ANIMCYCLIC_ 64 diff --git a/source/blender/makesrna/intern/rna_texture.c b/source/blender/makesrna/intern/rna_texture.c index 2573ad8394a..330d38502ce 100644 --- a/source/blender/makesrna/intern/rna_texture.c +++ b/source/blender/makesrna/intern/rna_texture.c @@ -39,6 +39,14 @@ #include "WM_types.h" +static EnumPropertyItem texture_filter_items[] = { + {TXF_DEFAULT, "DEFAULT", 0, "Default", ""}, + {TXF_EWA, "EWA", 0, "EWA", ""}, + {TXF_FELINE, "FELINE", 0, "FELINE", ""}, + {TXF_AREA, "AREA", 0, "Area", ""}, + {TXF_SAT, "SAT", 0, "SAT (4x mem)", ""}, + {0, NULL, 0, NULL, NULL}}; + #ifdef RNA_RUNTIME #include "BKE_texture.h" @@ -110,6 +118,35 @@ static void rna_Texture_use_color_ramp_set(PointerRNA *ptr, int value) tex->coba= add_colorband(0); } +static void rna_ImageTexture_mipmap_set(PointerRNA *ptr, int value) +{ + Tex *tex= (Tex*)ptr->data; + + if(value) tex->imaflag |= TEX_MIPMAP; + else tex->imaflag &= ~TEX_MIPMAP; + + if((tex->imaflag & TEX_MIPMAP) && tex->texfilter == TXF_SAT) + tex->texfilter = TXF_DEFAULT; +} + +static EnumPropertyItem *rna_ImageTexture_filter_itemf(bContext *C, PointerRNA *ptr, int *free) +{ + Tex *tex= (Tex*)ptr->data; + EnumPropertyItem *item= NULL; + int totitem= 0; + + RNA_enum_items_add_value(&item, &totitem, texture_filter_items, TXF_DEFAULT); + RNA_enum_items_add_value(&item, &totitem, texture_filter_items, TXF_EWA); + RNA_enum_items_add_value(&item, &totitem, texture_filter_items, TXF_FELINE); + RNA_enum_items_add_value(&item, &totitem, texture_filter_items, TXF_AREA); + if(tex->imaflag & TEX_MIPMAP) + RNA_enum_items_add_value(&item, &totitem, texture_filter_items, TXF_SAT); + + *free= 1; + + return item; +} + #else static void rna_def_color_ramp_element(BlenderRNA *brna) @@ -751,7 +788,7 @@ static void rna_def_texture_image(BlenderRNA *brna) prop= RNA_def_property(srna, "mipmap", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "imaflag", TEX_MIPMAP); - //RNA_def_property_boolean_funcs(prop, NULL, "rna_ImageTexture_mipmap_set"); + RNA_def_property_boolean_funcs(prop, NULL, "rna_ImageTexture_mipmap_set"); RNA_def_property_ui_text(prop, "MIP Map", "Uses auto-generated MIP maps for the image"); RNA_def_property_update(prop, NC_TEXTURE, NULL); @@ -889,6 +926,26 @@ static void rna_def_texture_image(BlenderRNA *brna) RNA_def_property_flag(prop, PROP_EDITABLE); RNA_def_property_ui_text(prop, "Image", ""); RNA_def_property_update(prop, NC_TEXTURE, NULL); + + /* filtering */ + prop= RNA_def_property(srna, "filter", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "texfilter"); + RNA_def_property_enum_items(prop, texture_filter_items); + RNA_def_property_enum_funcs(prop, NULL, NULL, "rna_ImageTexture_filter_itemf"); + RNA_def_property_ui_text(prop, "Filter", "Texture filter to use for sampling image."); + RNA_def_property_update(prop, NC_TEXTURE, NULL); + + prop= RNA_def_property(srna, "filter_probes", PROP_INT, PROP_NONE); + RNA_def_property_int_sdna(prop, NULL, "afmax"); + RNA_def_property_range(prop, 1, 256); + RNA_def_property_ui_text(prop, "Filter Probes", "Maximum number of samples. Higher gives less blur at distant/oblique angles, but is also slower."); + RNA_def_property_update(prop, NC_TEXTURE, NULL); + + prop= RNA_def_property(srna, "filter_eccentricity", PROP_INT, PROP_NONE); + RNA_def_property_int_sdna(prop, NULL, "afmax"); + RNA_def_property_range(prop, 1, 256); + RNA_def_property_ui_text(prop, "Filter Eccentricity", "Maximum eccentricity. Higher gives less blur at distant/oblique angles, but is also slower."); + RNA_def_property_update(prop, NC_TEXTURE, NULL); } static void rna_def_texture_plugin(BlenderRNA *brna) diff --git a/source/blender/render/intern/source/imagetexture.c b/source/blender/render/intern/source/imagetexture.c index b9a2acb8b1c..743dbb72e7c 100644 --- a/source/blender/render/intern/source/imagetexture.c +++ b/source/blender/render/intern/source/imagetexture.c @@ -416,6 +416,107 @@ static float clipy_rctf(rctf *rf, float y1, float y2) } +// used in SAT_get_color_bilerp() below +static void SAT_getcol(float* col, ImBuf* ibuf, int x, int y) +{ + if ((x == (ibuf->x - 1)) && (y == (ibuf->y - 1))) { // avg val pos + col[0] = col[1] = col[2] = col[3] = 0.f; + return; + } + ibuf_get_color(col, ibuf, x, y); +} + +// used in boxsampleclip_SAT() below +static void SAT_get_color_bilerp(float *col, ImBuf *ibuf, float u, float v) +{ + float c00[4], c01[4], c10[4], c11[4]; + const float ufl = floorf(u -= 0.5f), vfl = floorf(v -= 0.5f); + const float uf = u - ufl, vf = v - vfl; + const float w00=(1.f-uf)*(1.f-vf), w10=uf*(1.f-vf), w01=(1.f-uf)*vf, w11=uf*vf; + int x1 = (int)ufl, y1 = (int)vfl, x2 = x1 + 1, y2 = y1 + 1; + x1 = (x1 < 0) ? 0 : (x1 >= ibuf->x ? ibuf->x - 1 : x1); + x2 = (x2 < 0) ? 0 : (x2 >= ibuf->x ? ibuf->x - 1 : x2); + y1 = (y1 < 0) ? 0 : (y1 >= ibuf->y ? ibuf->y - 1 : y1); + y2 = (y2 < 0) ? 0 : (y2 >= ibuf->y ? ibuf->y - 1 : y2); + SAT_getcol(c00, ibuf, x1, y1); + SAT_getcol(c10, ibuf, x2, y1); + SAT_getcol(c01, ibuf, x1, y2); + SAT_getcol(c11, ibuf, x2, y2); + col[0] = w00*c00[0] + w10*c10[0] + w01*c01[0] + w11*c11[0]; + col[1] = w00*c00[1] + w10*c10[1] + w01*c01[1] + w11*c11[1]; + col[2] = w00*c00[2] + w10*c10[2] + w01*c01[2] + w11*c11[2]; + col[3] = w00*c00[3] + w10*c10[3] + w01*c01[3] + w11*c11[3]; +} + +static void boxsampleclip_SAT(ImBuf *ibuf, rctf *rf, TexResult *texres, int intpol) +{ + float div, col[4]; + if (intpol) { + div = 1.f/((rf->xmax - rf->xmin + 1.f)*(rf->ymax - rf->ymin + 1.f)); + SAT_get_color_bilerp(&texres->tr, ibuf, rf->xmax, rf->ymax); + if (rf->ymin >= 1.f) { + SAT_get_color_bilerp(col, ibuf, rf->xmax, rf->ymin - 1.f); + texres->tr -= col[0]; + texres->tg -= col[1]; + texres->tb -= col[2]; + texres->ta -= col[3]; + } + if (rf->xmin >= 1.f) { + SAT_get_color_bilerp(col, ibuf, rf->xmin - 1.f, rf->ymax); + texres->tr -= col[0]; + texres->tg -= col[1]; + texres->tb -= col[2]; + texres->ta -= col[3]; + } + if (rf->xmin >= 1.f && rf->ymin >= 1.f) { + SAT_get_color_bilerp(col, ibuf, rf->xmin - 1.f, rf->ymin - 1.f); + texres->tr += col[0]; + texres->tg += col[1]; + texres->tb += col[2]; + texres->ta += col[3]; + } + } + else { + int startx = (int)floorf(rf->xmin); + int endx = (int)floorf(rf->xmax); + int starty = (int)floorf(rf->ymin); + int endy = (int)floorf(rf->ymax); + if (startx < 0) startx = 0; + if (starty < 0) starty = 0; + if (endx >= ibuf->x) endx = ibuf->x - 1; + if (endy >= ibuf->y) endy = ibuf->y - 1; + div = 1.f/((endx - startx + 1)*(endy - starty + 1)); + SAT_getcol(&texres->tr, ibuf, endx, endy); + if (starty >= 1) { + SAT_getcol(col, ibuf, endx, starty - 1); + texres->tr -= col[0]; + texres->tg -= col[1]; + texres->tb -= col[2]; + texres->ta -= col[3]; + } + if (startx >= 1) { + SAT_getcol(col, ibuf, startx - 1, endy); + texres->tr -= col[0]; + texres->tg -= col[1]; + texres->tb -= col[2]; + texres->ta -= col[3]; + } + if (startx >=1 && starty >= 1) { + SAT_getcol(col, ibuf, startx - 1, starty - 1); + texres->tr += col[0]; + texres->tg += col[1]; + texres->tb += col[2]; + texres->ta += col[3]; + } + } + // avg + ibuf_get_color(col, ibuf, ibuf->x - 1, ibuf->y - 1); + texres->tr = texres->tr*div + col[0]; + texres->tg = texres->tg*div + col[1]; + texres->tb = texres->tb*div + col[2]; + texres->ta = texres->ta*div + col[3]; +} + static void boxsampleclip(struct ImBuf *ibuf, rctf *rf, TexResult *texres) { /* sample box, is clipped already, and minx etc. have been set at ibuf size. @@ -485,6 +586,7 @@ static void boxsampleclip(struct ImBuf *ibuf, rctf *rf, TexResult *texres) } } } + if(div!=0.0) { div= 1.0f/div; texres->tb*= div; @@ -498,13 +600,13 @@ static void boxsampleclip(struct ImBuf *ibuf, rctf *rf, TexResult *texres) } } -static void boxsample(ImBuf *ibuf, float minx, float miny, float maxx, float maxy, TexResult *texres, int imaprepeat, int imapextend) +static void boxsample(ImBuf *ibuf, float minx, float miny, float maxx, float maxy, TexResult *texres, int imaprepeat, int imapextend, int SAT, int intpol) { /* Sample box, performs clip. minx etc are in range 0.0 - 1.0 . - * Enlarge with antialiased edges of pixels. - * If variable 'imaprepeat' has been set, the - * clipped-away parts are sampled as well. - */ + * Enlarge with antialiased edges of pixels. + * If variable 'imaprepeat' has been set, the + * clipped-away parts are sampled as well. + */ /* note: actually minx etc isnt in the proper range... this due to filter size and offset vectors for bump */ TexResult texr; rctf *rf, stack[8]; @@ -552,7 +654,10 @@ static void boxsample(ImBuf *ibuf, float minx, float miny, float maxx, float max if(count>1) { tot= texres->tr= texres->tb= texres->tg= texres->ta= 0.0; while(count--) { - boxsampleclip(ibuf, rf, &texr); + if (SAT) + boxsampleclip_SAT(ibuf, rf, &texr, intpol); + else + boxsampleclip(ibuf, rf, &texr); opp= square_rctf(rf); tot+= opp; @@ -571,7 +676,10 @@ static void boxsample(ImBuf *ibuf, float minx, float miny, float maxx, float max } } else { - boxsampleclip(ibuf, rf, texres); + if (SAT) + boxsampleclip_SAT(ibuf, rf, texres, intpol); + else + boxsampleclip(ibuf, rf, texres); } if(texres->talpha==0) texres->ta= 1.0; @@ -598,7 +706,7 @@ void image_sample(Image *ima, float fx, float fy, float dx, float dy, float *res if( (R.flag & R_SEC_FIELD) && (ibuf->flags & IB_fields) ) ibuf->rect+= (ibuf->x*ibuf->y); - boxsample(ibuf, fx, fy, fx+dx, fy+dy, &texres, 0, 1); + boxsample(ibuf, fx, fy, fx+dx, fy+dy, &texres, 0, 1, 0, 0); result[0]= texres.tr; result[1]= texres.tg; result[2]= texres.tb; @@ -617,7 +725,7 @@ void ibuf_sample(ImBuf *ibuf, float fx, float fy, float dx, float dy, float *res } memset(&texres, 0, sizeof(texres)); - boxsample(ibuf, fx, fy, fx+dx, fy+dy, &texres, 0, 1); + boxsample(ibuf, fx, fy, fx+dx, fy+dy, &texres, 0, 1, 0, 0); result[0]= texres.tr; result[1]= texres.tg; result[2]= texres.tb; @@ -625,13 +733,777 @@ void ibuf_sample(ImBuf *ibuf, float fx, float fy, float dx, float dy, float *res } +//----------------------------------------------------------------------------------------------------------------- +// from here, some functions only used for the new filtering + +// this only used here to make it easier to pass extend flags as single int +enum {TXC_XMIR=1, TXC_YMIR, TXC_REPT, TXC_EXTD}; + +// similar to ibuf_get_color() but clips/wraps coords according to repeat/extend flags +// returns true if out of range in clipmode +static int ibuf_get_color_clip(float *col, ImBuf *ibuf, int x, int y, int extflag) +{ + int clip = 0; + switch (extflag) { + case TXC_XMIR: // y rep + x %= 2*ibuf->x; + x += x < 0 ? 2*ibuf->x : 0; + x = x >= ibuf->x ? 2*ibuf->x - x - 1 : x; + y %= ibuf->y; + y += y < 0 ? ibuf->y : 0; + break; + case TXC_YMIR: // x rep + x %= ibuf->x; + x += x < 0 ? ibuf->x : 0; + y %= 2*ibuf->y; + y += y < 0 ? 2*ibuf->y : 0; + y = y >= ibuf->y ? 2*ibuf->y - y - 1 : y; + break; + case TXC_EXTD: + x = (x < 0) ? 0 : ((x >= ibuf->x) ? (ibuf->x - 1) : x); + y = (y < 0) ? 0 : ((y >= ibuf->y) ? (ibuf->y - 1) : y); + break; + case TXC_REPT: + x %= ibuf->x; + x += (x < 0) ? ibuf->x : 0; + y %= ibuf->y; + y += (y < 0) ? ibuf->y : 0; + break; + default: { // as extend, if clipped, set alpha to 0.0 + if (x < 0) { x = 0; } // TXF alpha: clip = 1; } + if (x >= ibuf->x) { x = ibuf->x - 1; } // TXF alpha: clip = 1; } + if (y < 0) { y = 0; } // TXF alpha: clip = 1; } + if (y >= ibuf->y) { y = ibuf->y - 1; } // TXF alpha: clip = 1; } + } + } + + if (ibuf->rect_float) { + const float* fp = ibuf->rect_float + (x + y*ibuf->x)*ibuf->channels; + if (ibuf->channels == 1) + col[0] = col[1] = col[2] = col[3] = *fp; + else { + col[0] = fp[0]; + col[1] = fp[1]; + col[2] = fp[2]; + col[3] = clip ? 0.f : (ibuf->channels == 4 ? fp[3] : 1.f); + } + } + else { + char* rect = (char*)(ibuf->rect + x + y*ibuf->x); + col[0] = rect[0]*(1.f/255.f); + col[1] = rect[1]*(1.f/255.f); + col[2] = rect[2]*(1.f/255.f); + col[3] = clip ? 0.f : rect[3]*(1.f/255.f); + } + return clip; +} + +// as above + bilerp +static int ibuf_get_color_clip_bilerp(float *col, ImBuf *ibuf, float u, float v, int intpol, int extflag) +{ + if (intpol) { + float c00[4], c01[4], c10[4], c11[4]; + const float ufl = floorf(u -= 0.5f), vfl = floorf(v -= 0.5f); + const float uf = u - ufl, vf = v - vfl; + const float w00=(1.f-uf)*(1.f-vf), w10=uf*(1.f-vf), w01=(1.f-uf)*vf, w11=uf*vf; + const int x1 = (int)ufl, y1 = (int)vfl, x2 = x1 + 1, y2 = y1 + 1; + int clip = ibuf_get_color_clip(c00, ibuf, x1, y1, extflag); + clip |= ibuf_get_color_clip(c10, ibuf, x2, y1, extflag); + clip |= ibuf_get_color_clip(c01, ibuf, x1, y2, extflag); + clip |= ibuf_get_color_clip(c11, ibuf, x2, y2, extflag); + col[0] = w00*c00[0] + w10*c10[0] + w01*c01[0] + w11*c11[0]; + col[1] = w00*c00[1] + w10*c10[1] + w01*c01[1] + w11*c11[1]; + col[2] = w00*c00[2] + w10*c10[2] + w01*c01[2] + w11*c11[2]; + col[3] = clip ? 0.f : w00*c00[3] + w10*c10[3] + w01*c01[3] + w11*c11[3]; + return clip; + } + return ibuf_get_color_clip(col, ibuf, (int)u, (int)v, extflag); +} + +// anisotropic filters, data struct used instead of long line of (possibly unused) func args +typedef struct afdata_t { + float *dxt, *dyt; + int intpol, extflag; + // feline only + float majrad, minrad, theta; + int iProbes; + float dusc, dvsc; +} afdata_t; + +static void area_sample(TexResult* texr, ImBuf* ibuf, float fx, float fy, afdata_t* AFD) +{ + int xs, ys, clip = 0; + float tc[4], xsd, ysd, cw = 0.f; + const float ux = ibuf->x*AFD->dxt[0], uy = ibuf->y*AFD->dxt[1]; + const float vx = ibuf->x*AFD->dyt[0], vy = ibuf->y*AFD->dyt[1]; + int xsam = (int)(0.5f*sqrtf(ux*ux + uy*uy) + 0.5f); + int ysam = (int)(0.5f*sqrtf(vx*vx + vy*vy) + 0.5f); + const int minsam = AFD->intpol ? 2 : 4; + xsam = xsam < minsam ? minsam : xsam; + ysam = ysam < minsam ? minsam : ysam; + xsd = 1.f / xsam; + ysd = 1.f / ysam; + texr->tr = texr->tg = texr->tb = texr->ta = 0.f; + for (ys=0; ysdxt[0] + sv*AFD->dyt[0]; + const float pv = fy + su*AFD->dxt[1] + sv*AFD->dyt[1]; + const int out = ibuf_get_color_clip_bilerp(tc, ibuf, pu*ibuf->x, pv*ibuf->y, AFD->intpol, AFD->extflag); + clip |= out; + cw += out ? 0.f : 1.f; + texr->tr += tc[0]; + texr->tg += tc[1]; + texr->tb += tc[2]; + texr->ta += texr->talpha ? tc[3] : 0.f; + } + } + xsd *= ysd; + texr->tr *= xsd; + texr->tg *= xsd; + texr->tb *= xsd; + // clipping can be ignored if alpha used, texr->ta already includes filtered edge + texr->ta = texr->talpha ? texr->ta*xsd : (clip ? cw*xsd : 1.f); +} + +// table of (exp(ar) - exp(a)) / (1 - exp(a)) for r in range [0, 1] and a = -2 +// used instead of actual gaussian, otherwise at high texture magnifications circular artifacts are visible +#define EWA_MAXIDX 255 +static float EWA_WTS[EWA_MAXIDX + 1] = +{ 1.f, 0.990965f, 0.982f, 0.973105f, 0.96428f, 0.955524f, 0.946836f, 0.938216f, 0.929664f, + 0.921178f, 0.912759f, 0.904405f, 0.896117f, 0.887893f, 0.879734f, 0.871638f, 0.863605f, + 0.855636f, 0.847728f, 0.839883f, 0.832098f, 0.824375f, 0.816712f, 0.809108f, 0.801564f, + 0.794079f, 0.786653f, 0.779284f, 0.771974f, 0.76472f, 0.757523f, 0.750382f, 0.743297f, + 0.736267f, 0.729292f, 0.722372f, 0.715505f, 0.708693f, 0.701933f, 0.695227f, 0.688572f, + 0.68197f, 0.67542f, 0.66892f, 0.662471f, 0.656073f, 0.649725f, 0.643426f, 0.637176f, + 0.630976f, 0.624824f, 0.618719f, 0.612663f, 0.606654f, 0.600691f, 0.594776f, 0.588906f, + 0.583083f, 0.577305f, 0.571572f, 0.565883f, 0.56024f, 0.55464f, 0.549084f, 0.543572f, + 0.538102f, 0.532676f, 0.527291f, 0.521949f, 0.516649f, 0.511389f, 0.506171f, 0.500994f, + 0.495857f, 0.490761f, 0.485704f, 0.480687f, 0.475709f, 0.470769f, 0.465869f, 0.461006f, + 0.456182f, 0.451395f, 0.446646f, 0.441934f, 0.437258f, 0.432619f, 0.428017f, 0.42345f, + 0.418919f, 0.414424f, 0.409963f, 0.405538f, 0.401147f, 0.39679f, 0.392467f, 0.388178f, + 0.383923f, 0.379701f, 0.375511f, 0.371355f, 0.367231f, 0.363139f, 0.359079f, 0.355051f, + 0.351055f, 0.347089f, 0.343155f, 0.339251f, 0.335378f, 0.331535f, 0.327722f, 0.323939f, + 0.320186f, 0.316461f, 0.312766f, 0.3091f, 0.305462f, 0.301853f, 0.298272f, 0.294719f, + 0.291194f, 0.287696f, 0.284226f, 0.280782f, 0.277366f, 0.273976f, 0.270613f, 0.267276f, + 0.263965f, 0.26068f, 0.257421f, 0.254187f, 0.250979f, 0.247795f, 0.244636f, 0.241502f, + 0.238393f, 0.235308f, 0.232246f, 0.229209f, 0.226196f, 0.223206f, 0.220239f, 0.217296f, + 0.214375f, 0.211478f, 0.208603f, 0.20575f, 0.20292f, 0.200112f, 0.197326f, 0.194562f, + 0.191819f, 0.189097f, 0.186397f, 0.183718f, 0.18106f, 0.178423f, 0.175806f, 0.17321f, + 0.170634f, 0.168078f, 0.165542f, 0.163026f, 0.16053f, 0.158053f, 0.155595f, 0.153157f, + 0.150738f, 0.148337f, 0.145955f, 0.143592f, 0.141248f, 0.138921f, 0.136613f, 0.134323f, + 0.132051f, 0.129797f, 0.12756f, 0.125341f, 0.123139f, 0.120954f, 0.118786f, 0.116635f, + 0.114501f, 0.112384f, 0.110283f, 0.108199f, 0.106131f, 0.104079f, 0.102043f, 0.100023f, + 0.0980186f, 0.09603f, 0.094057f, 0.0920994f, 0.0901571f, 0.08823f, 0.0863179f, 0.0844208f, + 0.0825384f, 0.0806708f, 0.0788178f, 0.0769792f, 0.0751551f, 0.0733451f, 0.0715493f, 0.0697676f, + 0.0679997f, 0.0662457f, 0.0645054f, 0.0627786f, 0.0610654f, 0.0593655f, 0.0576789f, 0.0560055f, + 0.0543452f, 0.0526979f, 0.0510634f, 0.0494416f, 0.0478326f, 0.0462361f, 0.0446521f, 0.0430805f, + 0.0415211f, 0.039974f, 0.0384389f, 0.0369158f, 0.0354046f, 0.0339052f, 0.0324175f, 0.0309415f, + 0.029477f, 0.0280239f, 0.0265822f, 0.0251517f, 0.0237324f, 0.0223242f, 0.020927f, 0.0195408f, + 0.0181653f, 0.0168006f, 0.0154466f, 0.0141031f, 0.0127701f, 0.0114476f, 0.0101354f, 0.00883339f, + 0.00754159f, 0.00625989f, 0.00498819f, 0.00372644f, 0.00247454f, 0.00123242f, 0.f +}; + +// test if a float value is 'nan' +// there is a C99 function for this: isnan(), but blender seems to use C90 (according to gcc warns), +// and may not be supported by other compilers either +#ifndef ISNAN +#define ISNAN(x) ((x) != (x)) +#endif +//static int ISNAN(float x) { return (x != x); } + +static void radangle2imp(float a2, float b2, float th, float* A, float* B, float* C, float* F) +{ + float ct2 = cosf(th); + const float st2 = 1.f - ct2*ct2; // <- sin(th)^2 + ct2 *= ct2; + *A = a2*st2 + b2*ct2; + *B = (b2 - a2)*sinf(2.f*th); + *C = a2*ct2 + b2*st2; + *F = a2*b2; +} + +// all tests here are done to make sure possible overflows are hopefully minimized +static void imp2radangle(float A, float B, float C, float F, float* a, float* b, float* th, float* ecc) +{ + if (F <= 1e-5f) { // use arbitrary major radius, zero minor, infinite eccentricity + *a = sqrtf(A > C ? A : C); + *b = 0.f; + *ecc = 1e10f; + *th = 0.5f*(atan2f(B, A - C) + (float)M_PI); + } + else { + const float AmC = A - C, ApC = A + C, F2 = F*2.f; + const float r = sqrtf(AmC*AmC + B*B); + float d = ApC - r; + *a = (d <= 0.f) ? sqrtf(A > C ? A : C) : sqrtf(F2 / d); + d = ApC + r; + if (d <= 0.f) { + *b = 0.f; + *ecc = 1e10f; + } + else { + *b = sqrtf(F2 / d); + *ecc = *a / *b; + } + // incr theta by 0.5*pi (angle of major axis) + *th = 0.5f*(atan2f(B, AmC) + (float)M_PI); + } +} + +static void ewa_eval(TexResult* texr, ImBuf* ibuf, float fx, float fy, afdata_t* AFD) +{ + // scaling dxt/dyt by full resolution can cause overflow because of huge A/B/C and esp. F values, + // scaling by aspect ratio alone does the opposite, so try something inbetween instead... + const float ff2 = ibuf->x, ff = sqrtf(ff2), q = ibuf->y / ff; + const float Ux = AFD->dxt[0]*ff, Vx = AFD->dxt[1]*q, Uy = AFD->dyt[0]*ff, Vy = AFD->dyt[1]*q; + float A = Vx*Vx + Vy*Vy; + float B = -2.f*(Ux*Vx + Uy*Vy); + float C = Ux*Ux + Uy*Uy; + float F = A*C - B*B*0.25f; + float a, b, th, ecc, a2, b2, ue, ve, U0, V0, DDQ, U, ac1, ac2, BU, d; // TXF alpha: cw = 0.f; + int u, v, u1, u2, v1, v2; // TXF alpha: clip = 0; + + // The so-called 'high' quality ewa method simply adds a constant of 1 to both A & C, + // so the ellipse always covers at least some texels. But since the filter is now always larger, + // it also means that everywhere else it's also more blurry then ideally should be the case. + // So instead here the ellipse radii are modified instead whenever either is too low. + // Use a different radius based on interpolation switch, just enough to anti-alias when interpolation is off, + // and slightly larger to make result a bit smoother than bilinear interpolation when interpolation is on + // (minimum values: const float rmin = intpol ? 1.f : 0.5f;) + const float rmin = (AFD->intpol ? 1.5625f : 0.765625f)/ff2; + imp2radangle(A, B, C, F, &a, &b, &th, &ecc); + if ((b2 = b*b) < rmin) { + if ((a2 = a*a) < rmin) { + B = 0.f; + A = C = rmin; + F = A*C; + } + else { + b2 = rmin; + radangle2imp(a2, b2, th, &A, &B, &C, &F); + } + } + + ue = ff*sqrtf(C); + ve = ff*sqrtf(A); + d = (float)(EWA_MAXIDX + 1) / (F*ff2); + A *= d; + B *= d; + C *= d; + + U0 = fx*ibuf->x; + V0 = fy*ibuf->y; + u1 = (int)(floorf(U0 - ue)); + u2 = (int)(ceilf(U0 + ue)); + v1 = (int)(floorf(V0 - ve)); + v2 = (int)(ceilf(V0 + ve)); + U0 -= 0.5f; + V0 -= 0.5f; + DDQ = 2.f*A; + U = u1 - U0; + ac1 = A*(2.f*U + 1.f); + ac2 = A*U*U; + BU = B*U; + + d = texr->tr = texr->tb = texr->tg = texr->ta = 0.f; + for (v=v1; v<=v2; ++v) { + const float V = v - V0; + float DQ = ac1 + B*V; + float Q = (C*V + BU)*V + ac2; + for (u=u1; u<=u2; ++u) { + if (Q < (float)(EWA_MAXIDX + 1)) { + float tc[4]; + const float wt = EWA_WTS[(Q < 0.f) ? 0 : (unsigned int)Q]; + /*const int out =*/ ibuf_get_color_clip(tc, ibuf, u, v, AFD->extflag); + // TXF alpha: clip |= out; + // TXF alpha: cw += out ? 0.f : wt; + texr->tr += tc[0]*wt; + texr->tg += tc[1]*wt; + texr->tb += tc[2]*wt; + texr->ta += texr->talpha ? tc[3]*wt : 0.f; + d += wt; + } + Q += DQ; + DQ += DDQ; + } + } + + // d should hopefully never be zero anymore + d = 1.f/d; + texr->tr *= d; + texr->tg *= d; + texr->tb *= d; + // clipping can be ignored if alpha used, texr->ta already includes filtered edge + texr->ta = texr->talpha ? texr->ta*d : 1.f; // TXF alpha (clip ? cw*d : 1.f); +} + +static void feline_eval(TexResult* texr, ImBuf* ibuf, float fx, float fy, afdata_t* AFD) +{ + const int maxn = AFD->iProbes - 1; + const float ll = ((AFD->majrad == AFD->minrad) ? 2.f*AFD->majrad : 2.f*(AFD->majrad - AFD->minrad)) / (maxn ? (float)maxn : 1.f); + float du = maxn ? cosf(AFD->theta)*ll : 0.f; + float dv = maxn ? sinf(AFD->theta)*ll : 0.f; + //const float D = -0.5f*(du*du + dv*dv) / (AFD->majrad*AFD->majrad); + const float D = (EWA_MAXIDX + 1)*0.25f*(du*du + dv*dv) / (AFD->majrad*AFD->majrad); + float d; // TXF alpha: cw = 0.f; + int n; // TXF alpha: clip = 0; + // have to use same scaling for du/dv here as for Ux/Vx/Uy/Vy (*after* D calc.) + du *= AFD->dusc; + dv *= AFD->dvsc; + d = texr->tr = texr->tb = texr->tg = texr->ta = 0.f; + for (n=-maxn; n<=maxn; n+=2) { + float tc[4]; + const float hn = n*0.5f; + const float u = fx + hn*du, v = fy + hn*dv; + //const float wt = expf(n*n*D); + // can use ewa table here too + const float wt = EWA_WTS[(int)(n*n*D)]; + /*const int out =*/ ibuf_get_color_clip_bilerp(tc, ibuf, ibuf->x*u, ibuf->y*v, AFD->intpol, AFD->extflag); + // TXF alpha: clip |= out; + // TXF alpha: cw += out ? 0.f : wt; + texr->tr += tc[0]*wt; + texr->tg += tc[1]*wt; + texr->tb += tc[2]*wt; + texr->ta += texr->talpha ? tc[3]*wt : 0.f; + d += wt; + } + + d = 1.f/d; + texr->tr *= d; + texr->tg *= d; + texr->tb *= d; + // clipping can be ignored if alpha used, texr->ta already includes filtered edge + texr->ta = texr->talpha ? texr->ta*d : 1.f; // TXF alpha: (clip ? cw*d : 1.f); +} +#undef EWA_MAXIDX + +static void alpha_clip_aniso(ImBuf *ibuf, float minx, float miny, float maxx, float maxy, int extflag, TexResult *texres) +{ + float alphaclip; + rctf rf; + + // TXF apha: we're doing the same alphaclip here as boxsample, but i'm doubting + // if this is actually correct for the all the filtering algorithms .. + + if(!(extflag == TXC_REPT || extflag == TXC_EXTD)) { + rf.xmin= minx*(ibuf->x); + rf.xmax= maxx*(ibuf->x); + rf.ymin= miny*(ibuf->y); + rf.ymax= maxy*(ibuf->y); + + alphaclip = clipx_rctf(&rf, 0.0, (float)(ibuf->x)); + alphaclip*= clipy_rctf(&rf, 0.0, (float)(ibuf->y)); + alphaclip= MAX2(alphaclip, 0.0f); + + if(alphaclip!=1.0) { + /* premul it all */ + texres->tr*= alphaclip; + texres->tg*= alphaclip; + texres->tb*= alphaclip; + texres->ta*= alphaclip; + } + } +} + +static int imagewraposa_aniso(Tex *tex, Image *ima, ImBuf *ibuf, float *texvec, float *dxt, float *dyt, TexResult *texres) +{ + TexResult texr; + float fx, fy, minx, maxx, miny, maxy; + float maxd, val1, val2, val3; + int curmap, retval, intpol, extflag = 0; + afdata_t AFD; + + void (*filterfunc)(TexResult*, ImBuf*, float, float, afdata_t*); + switch (tex->texfilter) { + case TXF_EWA: + filterfunc = ewa_eval; + break; + case TXF_FELINE: + filterfunc = feline_eval; + break; + case TXF_AREA: + default: + filterfunc = area_sample; + } + + texres->tin = texres->ta = texres->tr = texres->tg = texres->tb = 0.f; + + // we need to set retval OK, otherwise texture code generates normals itself... + retval = texres->nor ? 3 : 1; + + // quick tests + if (ibuf==NULL && ima==NULL) return retval; + + if (ima) { // hack for icon render + if ((ima->ibufs.first == NULL) && (R.r.scemode & R_NO_IMAGE_LOAD)) return retval; + ibuf = BKE_image_get_ibuf(ima, &tex->iuser); + } + + if ((ibuf == NULL) || ((ibuf->rect == NULL) && (ibuf->rect_float == NULL))) return retval; + + // mipmap test + if (tex->imaflag & TEX_MIPMAP) { + if (((ibuf->flags & IB_fields) == 0) && (ibuf->mipmap[0] == NULL)) { + BLI_lock_thread(LOCK_IMAGE); + if (ibuf->mipmap[0] == NULL) IMB_makemipmap(ibuf, tex->imaflag & TEX_GAUSS_MIP, 0); + BLI_unlock_thread(LOCK_IMAGE); + } + } + + if ((tex->imaflag & TEX_USEALPHA) && ((tex->imaflag & TEX_CALCALPHA) == 0)) texres->talpha = 1; + texr.talpha = texres->talpha; + + if (tex->imaflag & TEX_IMAROT) { + fy = texvec[0]; + fx = texvec[1]; + } + else { + fx = texvec[0]; + fy = texvec[1]; + } + + if (ibuf->flags & IB_fields) { + if (R.r.mode & R_FIELDS) { /* field render */ + if (R.flag & R_SEC_FIELD) { /* correction for 2nd field */ + /* fac1= 0.5/( (float)ibuf->y ); */ + /* fy-= fac1; */ + } + else /* first field */ + fy += 0.5f/( (float)ibuf->y ); + } + } + + // pixel coordinates + minx = MIN3(dxt[0], dyt[0], dxt[0] + dyt[0]); + maxx = MAX3(dxt[0], dyt[0], dxt[0] + dyt[0]); + miny = MIN3(dxt[1], dyt[1], dxt[1] + dyt[1]); + maxy = MAX3(dxt[1], dyt[1], dxt[1] + dyt[1]); + + // tex_sharper has been removed + minx = (maxx - minx)*0.5f; + miny = (maxy - miny)*0.5f; + + if (tex->imaflag & TEX_FILTER_MIN) { + // make sure the filtersize is minimal in pixels (normal, ref map can have miniature pixel dx/dy) + const float addval = (0.5f * tex->filtersize) / (float)MIN2(ibuf->x, ibuf->y); + if (addval > minx) minx = addval; + if (addval > miny) miny = addval; + } + else if (tex->filtersize != 1.f) { + minx *= tex->filtersize; + miny *= tex->filtersize; + dxt[0] *= tex->filtersize; + dxt[1] *= tex->filtersize; + dyt[0] *= tex->filtersize; + dyt[1] *= tex->filtersize; + } + + if (tex->imaflag & TEX_IMAROT) { + float t; + SWAP(float, minx, miny); + // must rotate dxt/dyt 90 deg + // yet another blender problem is that swapping X/Y axes (or any tex proj switches) should do something similar, + // but it doesn't, it only swaps coords, so filter area will be incorrect in those cases. + t = dxt[0]; + dxt[0] = dxt[1]; + dxt[1] = -t; + t = dyt[0]; + dyt[0] = dyt[1]; + dyt[1] = -t; + } + + // side faces of unit-cube + minx = (minx > 0.25f) ? 0.25f : ((minx < 1e-5f) ? 1e-5 : minx); + miny = (miny > 0.25f) ? 0.25f : ((miny < 1e-5f) ? 1e-5 : miny); + + // repeat and clip + + if (tex->extend == TEX_REPEAT) { + if ((tex->flag & (TEX_REPEAT_XMIR | TEX_REPEAT_YMIR)) == (TEX_REPEAT_XMIR | TEX_REPEAT_YMIR)) + extflag = TXC_EXTD; + else if (tex->flag & TEX_REPEAT_XMIR) + extflag = TXC_XMIR; + else if (tex->flag & TEX_REPEAT_YMIR) + extflag = TXC_YMIR; + else + extflag = TXC_REPT; + } + else if (tex->extend == TEX_EXTEND) + extflag = TXC_EXTD; + + if (tex->extend == TEX_CHECKER) { + int xs = (int)floorf(fx), ys = (int)floorf(fy); + // both checkers available, no boundary exceptions, checkerdist will eat aliasing + if ((tex->flag & TEX_CHECKER_ODD) && (tex->flag & TEX_CHECKER_EVEN)) { + fx -= xs; + fy -= ys; + } + else { + int xs1 = (int)floorf(fx - minx); + int ys1 = (int)floorf(fy - miny); + int xs2 = (int)floorf(fx + minx); + int ys2 = (int)floorf(fy + miny); + if ((xs1 != xs2) || (ys1 != ys2)) { + if (tex->flag & TEX_CHECKER_ODD) { + fx -= ((xs1 + ys) & 1) ? xs2 : xs1; + fy -= ((ys1 + xs) & 1) ? ys2 : ys1; + } + if (tex->flag & TEX_CHECKER_EVEN) { + fx -= ((xs1 + ys) & 1) ? xs1 : xs2; + fy -= ((ys1 + xs) & 1) ? ys1 : ys2; + } + } + else { + if ((tex->flag & TEX_CHECKER_ODD) == 0 && ((xs + ys) & 1) == 0) return retval; + if ((tex->flag & TEX_CHECKER_EVEN) == 0 && (xs + ys) & 1) return retval; + fx -= xs; + fy -= ys; + } + } + // scale around center, (0.5, 0.5) + if (tex->checkerdist < 1.f) { + const float omcd = 1.f / (1.f - tex->checkerdist); + fx = (fx - 0.5f)*omcd + 0.5f; + fy = (fy - 0.5f)*omcd + 0.5f; + minx *= omcd; + miny *= omcd; + } + } + + if (tex->extend == TEX_CLIPCUBE) { + if ((fx + minx) < 0.f || (fy + miny) < 0.f || (fx - minx) > 1.f || (fy - miny) > 1.f || texvec[2] < -1.f || texvec[2] > 1.f) return retval; + } + else if (tex->extend == TEX_CLIP || tex->extend == TEX_CHECKER) { + if ((fx + minx) < 0.f || (fy + miny) < 0.f || (fx - minx) > 1.f || (fy - miny) > 1.f) return retval; + } + else { + if (tex->extend == TEX_EXTEND) { + fx = (fx > 1.f) ? 1.f : ((fx < 0.f) ? 0.f : fx); + fy = (fy > 1.f) ? 1.f : ((fy < 0.f) ? 0.f : fy); + } + else { + fx -= floorf(fx); + fy -= floorf(fy); + } + } + + intpol = tex->imaflag & TEX_INTERPOL; + + // warning no return! + if ((R.flag & R_SEC_FIELD) && (ibuf->flags & IB_fields)) + ibuf->rect += ibuf->x*ibuf->y; + + // struct common data + AFD.dxt = dxt; + AFD.dyt = dyt; + AFD.intpol = intpol; + AFD.extflag = extflag; + + // choice: + if (tex->imaflag & TEX_MIPMAP) { + ImBuf *previbuf, *curibuf; + float levf; + int maxlev; + ImBuf* mipmaps[IB_MIPMAP_LEVELS + 1]; + + // modify ellipse minor axis if too eccentric, use for area sampling as well + // scaling dxt/dyt as done in pbrt is not the same + // (as in ewa_eval(), scale by sqrt(ibuf->x) to maximize precision) + const float ff = sqrtf(ibuf->x), q = ibuf->y/ff; + const float Ux = dxt[0]*ff, Vx = dxt[1]*q, Uy = dyt[0]*ff, Vy = dyt[1]*q; + const float A = Vx*Vx + Vy*Vy; + const float B = -2.f*(Ux*Vx + Uy*Vy); + const float C = Ux*Ux + Uy*Uy; + const float F = A*C - B*B*0.25f; + float a, b, th, ecc; + imp2radangle(A, B, C, F, &a, &b, &th, &ecc); + if (tex->texfilter == TXF_FELINE) { + float fProbes; + a *= ff; + b *= ff; + a = MAX2(a, 1.f); + b = MAX2(b, 1.f); + fProbes = 2.f*(a / b) - 1.f; + AFD.iProbes = (int)floorf(fProbes + 0.5f); + AFD.iProbes = MIN2(AFD.iProbes, tex->afmax); + if (AFD.iProbes < fProbes) + b = 2.f*a / (float)(AFD.iProbes + 1); + AFD.majrad = a/ff; + AFD.minrad = b/ff; + AFD.theta = th; + AFD.dusc = 1.f/ff; + AFD.dvsc = ff / (float)ibuf->y; + } + else { // EWA & area + if (ecc > (float)tex->afmax) b = a / (float)tex->afmax; + b *= ff; + } + maxd = MAX2(b, 1e-8f); + levf = logf(maxd)*(float)M_LOG2E; + + curmap = 0; + maxlev = 1; + mipmaps[0] = ibuf; + while (curmap < IB_MIPMAP_LEVELS) { + mipmaps[curmap + 1] = ibuf->mipmap[curmap]; + if (ibuf->mipmap[curmap]) maxlev++; + curmap++; + } + + // mipmap level + if (levf < 0.f) { // original image only + previbuf = curibuf = mipmaps[0]; + levf = 0.f; + } + else if (levf >= maxlev - 1) { + previbuf = curibuf = mipmaps[maxlev - 1]; + levf = 0.f; + if (tex->texfilter == TXF_FELINE) AFD.iProbes = 1; + } + else { + const int lev = ISNAN(levf) ? 0 : (int)levf; + curibuf = mipmaps[lev]; + previbuf = mipmaps[lev + 1]; + levf -= floorf(levf); + } + + // filter functions take care of interpolation themselves, no need to modify dxt/dyt here + + if (texres->nor && ((tex->imaflag & TEX_NORMALMAP) == 0)) { + // color & normal + filterfunc(texres, curibuf, fx, fy, &AFD); + val1 = texres->tr + texres->tg + texres->tb; + filterfunc(&texr, curibuf, fx + dxt[0], fy + dxt[1], &AFD); + val2 = texr.tr + texr.tg + texr.tb; + filterfunc(&texr, curibuf, fx + dyt[0], fy + dyt[1], &AFD); + val3 = texr.tr + texr.tg + texr.tb; + // don't switch x or y! + texres->nor[0] = val1 - val2; + texres->nor[1] = val1 - val3; + if (previbuf != curibuf) { // interpolate + filterfunc(&texr, previbuf, fx, fy, &AFD); + // rgb + texres->tr += levf*(texr.tr - texres->tr); + texres->tg += levf*(texr.tg - texres->tg); + texres->tb += levf*(texr.tb - texres->tb); + texres->ta += levf*(texr.ta - texres->ta); + // normal + val1 += levf*((texr.tr + texr.tg + texr.tb) - val1); + filterfunc(&texr, previbuf, fx + dxt[0], fy + dxt[1], &AFD); + val2 += levf*((texr.tr + texr.tg + texr.tb) - val2); + filterfunc(&texr, previbuf, fx + dyt[0], fy + dyt[1], &AFD); + val3 += levf*((texr.tr + texr.tg + texr.tb) - val3); + texres->nor[0] = val1 - val2; // vals have been interpolated above! + texres->nor[1] = val1 - val3; + } + } + else { // color + filterfunc(texres, curibuf, fx, fy, &AFD); + if (previbuf != curibuf) { // interpolate + filterfunc(&texr, previbuf, fx, fy, &AFD); + texres->tr += levf*(texr.tr - texres->tr); + texres->tg += levf*(texr.tg - texres->tg); + texres->tb += levf*(texr.tb - texres->tb); + texres->ta += levf*(texr.ta - texres->ta); + } + + alpha_clip_aniso(ibuf, fx-minx, fy-miny, fx+minx, fy+miny, extflag, texres); + } + } + else { // no mipmap + // filter functions take care of interpolation themselves, no need to modify dxt/dyt here + if (tex->texfilter == TXF_FELINE) { + const float ff = sqrtf(ibuf->x), q = ibuf->y/ff; + const float Ux = dxt[0]*ff, Vx = dxt[1]*q, Uy = dyt[0]*ff, Vy = dyt[1]*q; + const float A = Vx*Vx + Vy*Vy; + const float B = -2.f*(Ux*Vx + Uy*Vy); + const float C = Ux*Ux + Uy*Uy; + const float F = A*C - B*B*0.25f; + float a, b, th, ecc, fProbes; + imp2radangle(A, B, C, F, &a, &b, &th, &ecc); + a *= ff; + b *= ff; + a = MAX2(a, 1.f); + b = MAX2(b, 1.f); + fProbes = 2.f*(a / b) - 1.f; + // no limit to number of Probes here + AFD.iProbes = (int)floorf(fProbes + 0.5f); + if (AFD.iProbes < fProbes) b = 2.f*a / (float)(AFD.iProbes + 1); + AFD.majrad = a/ff; + AFD.minrad = b/ff; + AFD.theta = th; + AFD.dusc = 1.f/ff; + AFD.dvsc = ff / (float)ibuf->y; + } + if (texres->nor && ((tex->imaflag & TEX_NORMALMAP) == 0)) { + // color & normal + filterfunc(texres, ibuf, fx, fy, &AFD); + val1 = texres->tr + texres->tg + texres->tb; + filterfunc(&texr, ibuf, fx + dxt[0], fy + dxt[1], &AFD); + val2 = texr.tr + texr.tg + texr.tb; + filterfunc(&texr, ibuf, fx + dyt[0], fy + dyt[1], &AFD); + val3 = texr.tr + texr.tg + texr.tb; + // don't switch x or y! + texres->nor[0] = val1 - val2; + texres->nor[1] = val1 - val3; + } + else { + filterfunc(texres, ibuf, fx, fy, &AFD); + alpha_clip_aniso(ibuf, fx-minx, fy-miny, fx+minx, fy+miny, extflag, texres); + } + } + + BRICONTRGB; + + if (tex->imaflag & TEX_CALCALPHA) + texres->ta = texres->tin = texres->ta * MAX3(texres->tr, texres->tg, texres->tb); + else + texres->tin = texres->ta; + if (tex->flag & TEX_NEGALPHA) texres->ta = 1.f - texres->ta; + + if ((R.flag & R_SEC_FIELD) && (ibuf->flags & IB_fields)) + ibuf->rect -= ibuf->x*ibuf->y; + + if (texres->nor && (tex->imaflag & TEX_NORMALMAP)) { // normal from color + texres->nor[0] = 2.f*(texres->tr - 0.5f); + texres->nor[1] = 2.f*(0.5f - texres->tg); + texres->nor[2] = 2.f*(texres->tb - 0.5f); + } + + // de-premul, this is being premulled in shade_input_do_shade() + // TXF: this currently does not (yet?) work properly, destroys edge AA in clip/checker mode, so for now commented out + // also disabled in imagewraposa() to be able to compare results with blender's default texture filtering + + // brecht: tried to fix this, see "TXF alpha" comments + + if (texres->ta != 1.f && (texres->ta > FLT_EPSILON)) { + fx = 1.f/texres->ta; + texres->tr *= fx; + texres->tg *= fx; + texres->tb *= fx; + } + + return retval; +} -int imagewraposa(Tex *tex, Image *ima, ImBuf *ibuf, float *texvec, float *dxt, float *dyt, TexResult *texres) + +int imagewraposa(Tex *tex, Image *ima, ImBuf *ibuf, float *texvec, float *DXT, float *DYT, TexResult *texres) { TexResult texr; - float fx, fy, minx, maxx, miny, maxy, dx, dy; + float fx, fy, minx, maxx, miny, maxy, dx, dy, dxt[3], dyt[3]; float maxd, pixsize, val1, val2, val3; - int curmap, retval, imaprepeat, imapextend; + int curmap, retval, imaprepeat, imapextend, SAT = (tex->texfilter == TXF_SAT); + + // TXF: since dxt/dyt might be modified here and since they might be needed after imagewraposa() call, + // make a local copy here so that original vecs remain untouched + VECCOPY(dxt, DXT); + VECCOPY(dyt, DYT); + + // anisotropic filtering + if (!SAT && (tex->texfilter != TXF_DEFAULT)) + return imagewraposa_aniso(tex, ima, ibuf, texvec, dxt, dyt, texres); texres->tin= texres->ta= texres->tr= texres->tg= texres->tb= 0.0f; @@ -653,13 +1525,13 @@ int imagewraposa(Tex *tex, Image *ima, ImBuf *ibuf, float *texvec, float *dxt, f return retval; /* mipmap test */ - if(tex->imaflag & TEX_MIPMAP) { + if (SAT || tex->imaflag & TEX_MIPMAP) { if(ibuf->flags & IB_fields); else if(ibuf->mipmap[0]==NULL) { BLI_lock_thread(LOCK_IMAGE); if(ibuf->mipmap[0]==NULL) - IMB_makemipmap(ibuf, tex->imaflag & TEX_GAUSS_MIP); + IMB_makemipmap(ibuf, tex->imaflag & TEX_GAUSS_MIP, SAT); BLI_unlock_thread(LOCK_IMAGE); } @@ -871,11 +1743,11 @@ int imagewraposa(Tex *tex, Image *ima, ImBuf *ibuf, float *texvec, float *dxt, f //minx*= 1.35f; //miny*= 1.35f; - boxsample(curibuf, fx-minx, fy-miny, fx+minx, fy+miny, texres, imaprepeat, imapextend); + boxsample(curibuf, fx-minx, fy-miny, fx+minx, fy+miny, texres, imaprepeat, imapextend, 0, 0); val1= texres->tr+texres->tg+texres->tb; - boxsample(curibuf, fx-minx+dxt[0], fy-miny+dxt[1], fx+minx+dxt[0], fy+miny+dxt[1], &texr, imaprepeat, imapextend); + boxsample(curibuf, fx-minx+dxt[0], fy-miny+dxt[1], fx+minx+dxt[0], fy+miny+dxt[1], &texr, imaprepeat, imapextend, 0, 0); val2= texr.tr + texr.tg + texr.tb; - boxsample(curibuf, fx-minx+dyt[0], fy-miny+dyt[1], fx+minx+dyt[0], fy+miny+dyt[1], &texr, imaprepeat, imapextend); + boxsample(curibuf, fx-minx+dyt[0], fy-miny+dyt[1], fx+minx+dyt[0], fy+miny+dyt[1], &texr, imaprepeat, imapextend, 0, 0); val3= texr.tr + texr.tg + texr.tb; /* don't switch x or y! */ @@ -884,7 +1756,7 @@ int imagewraposa(Tex *tex, Image *ima, ImBuf *ibuf, float *texvec, float *dxt, f if(previbuf!=curibuf) { /* interpolate */ - boxsample(previbuf, fx-minx, fy-miny, fx+minx, fy+miny, &texr, imaprepeat, imapextend); + boxsample(previbuf, fx-minx, fy-miny, fx+minx, fy+miny, &texr, imaprepeat, imapextend, 0, 0); /* calc rgb */ dx= 2.0f*(pixsize-maxd)/pixsize; @@ -901,9 +1773,9 @@ int imagewraposa(Tex *tex, Image *ima, ImBuf *ibuf, float *texvec, float *dxt, f } val1= dy*val1+ dx*(texr.tr + texr.tg + texr.tb); - boxsample(previbuf, fx-minx+dxt[0], fy-miny+dxt[1], fx+minx+dxt[0], fy+miny+dxt[1], &texr, imaprepeat, imapextend); + boxsample(previbuf, fx-minx+dxt[0], fy-miny+dxt[1], fx+minx+dxt[0], fy+miny+dxt[1], &texr, imaprepeat, imapextend, 0, 0); val2= dy*val2+ dx*(texr.tr + texr.tg + texr.tb); - boxsample(previbuf, fx-minx+dyt[0], fy-miny+dyt[1], fx+minx+dyt[0], fy+miny+dyt[1], &texr, imaprepeat, imapextend); + boxsample(previbuf, fx-minx+dyt[0], fy-miny+dyt[1], fx+minx+dyt[0], fy+miny+dyt[1], &texr, imaprepeat, imapextend, 0, 0); val3= dy*val3+ dx*(texr.tr + texr.tg + texr.tb); texres->nor[0]= (val1-val2); /* vals have been interpolated above! */ @@ -926,10 +1798,10 @@ int imagewraposa(Tex *tex, Image *ima, ImBuf *ibuf, float *texvec, float *dxt, f maxy= fy+miny; miny= fy-miny; - boxsample(curibuf, minx, miny, maxx, maxy, texres, imaprepeat, imapextend); + boxsample(curibuf, minx, miny, maxx, maxy, texres, imaprepeat, imapextend, 0, 0); if(previbuf!=curibuf) { /* interpolate */ - boxsample(previbuf, minx, miny, maxx, maxy, &texr, imaprepeat, imapextend); + boxsample(previbuf, minx, miny, maxx, maxy, &texr, imaprepeat, imapextend, 0, 0); fx= 2.0f*(pixsize-maxd)/pixsize; @@ -947,26 +1819,39 @@ int imagewraposa(Tex *tex, Image *ima, ImBuf *ibuf, float *texvec, float *dxt, f } } else { - if((tex->imaflag & TEX_INTERPOL)) { + const int intpol = tex->imaflag & TEX_INTERPOL; + if (intpol && !SAT) { /* sample 1 pixel minimum */ if (minx < 0.5f / ibuf->x) minx = 0.5f / ibuf->x; if (miny < 0.5f / ibuf->y) miny = 0.5f / ibuf->y; } if(texres->nor && (tex->imaflag & TEX_NORMALMAP)==0) { - - boxsample(ibuf, fx-minx, fy-miny, fx+minx, fy+miny, texres, imaprepeat, imapextend); - val1= texres->tr+texres->tg+texres->tb; - boxsample(ibuf, fx-minx+dxt[0], fy-miny+dxt[1], fx+minx+dxt[0], fy+miny+dxt[1], &texr, imaprepeat, imapextend); - val2= texr.tr + texr.tg + texr.tb; - boxsample(ibuf, fx-minx+dyt[0], fy-miny+dyt[1], fx+minx+dyt[0], fy+miny+dyt[1], &texr, imaprepeat, imapextend); - val3= texr.tr + texr.tg + texr.tb; + if (SAT) { + boxsample(ibuf->mipmap[0], fx-minx, fy-miny, fx+minx, fy+miny, texres, imaprepeat, imapextend, 1, intpol); + val1 = texres->tr + texres->tg + texres->tb; + boxsample(ibuf->mipmap[0], fx-minx+dxt[0], fy-miny+dxt[1], fx+minx+dxt[0], fy+miny+dxt[1], &texr, imaprepeat, imapextend, 1, intpol); + val2 = texr.tr + texr.tg + texr.tb; + boxsample(ibuf->mipmap[0], fx-minx+dyt[0], fy-miny+dyt[1], fx+minx+dyt[0], fy+miny+dyt[1], &texr, imaprepeat, imapextend, 1, intpol); + val3 = texr.tr + texr.tg + texr.tb; + } + else { + boxsample(ibuf, fx-minx, fy-miny, fx+minx, fy+miny, texres, imaprepeat, imapextend, 0, 0); + val1= texres->tr+texres->tg+texres->tb; + boxsample(ibuf, fx-minx+dxt[0], fy-miny+dxt[1], fx+minx+dxt[0], fy+miny+dxt[1], &texr, imaprepeat, imapextend, 0, 0); + val2= texr.tr + texr.tg + texr.tb; + boxsample(ibuf, fx-minx+dyt[0], fy-miny+dyt[1], fx+minx+dyt[0], fy+miny+dyt[1], &texr, imaprepeat, imapextend, 0, 0); + val3= texr.tr + texr.tg + texr.tb; + } /* don't switch x or y! */ texres->nor[0]= (val1-val2); texres->nor[1]= (val1-val3); } else { - boxsample(ibuf, fx-minx, fy-miny, fx+minx, fy+miny, texres, imaprepeat, imapextend); + if (SAT) + boxsample(ibuf->mipmap[0], fx-minx, fy-miny, fx+minx, fy+miny, texres, imaprepeat, imapextend, 1, intpol); + else + boxsample(ibuf, fx-minx, fy-miny, fx+minx, fy+miny, texres, imaprepeat, imapextend, 0, 0); } } diff --git a/source/blender/render/intern/source/texture.c b/source/blender/render/intern/source/texture.c index e11bb0004b2..9466bd45420 100644 --- a/source/blender/render/intern/source/texture.c +++ b/source/blender/render/intern/source/texture.c @@ -984,7 +984,7 @@ static void do_2d_mapping(MTex *mtex, float *t, VlakRen *vlr, float *n, float *d if(tex->flag & TEX_REPEAT_XMIR) { int orig= (int)floor(origf); - if(orig & 1) + if(orig & 1) fx= 1.0-fx; } } @@ -1077,13 +1077,15 @@ static void do_2d_mapping(MTex *mtex, float *t, VlakRen *vlr, float *n, float *d dxt[2]= f1; dyt[2]= f2; } - dxt[0]/= 2.0; - dxt[1]/= 2.0; - dxt[2]/= 2.0; - - dyt[0]/= 2.0; - dyt[1]/= 2.0; - dyt[2]/= 2.0; + + dxt[0] *= 0.5f; + dxt[1] *= 0.5f; + dxt[2] *= 0.5f; + + dyt[0] *= 0.5f; + dyt[1] *= 0.5f; + dyt[2] *= 0.5f; + } /* if area, then reacalculate dxt[] and dyt[] */ @@ -1102,13 +1104,16 @@ static void do_2d_mapping(MTex *mtex, float *t, VlakRen *vlr, float *n, float *d if(tex->xrepeat>1) { float origf= fx *= tex->xrepeat; - if(fx>1.0f) fx -= (int)(fx); - else if(fx<0.0f) fx+= 1-(int)(fx); + // TXF: omit mirror here, see comments in do_material_tex() after do_2d_mapping() call + if (tex->texfilter == TXF_DEFAULT) { + if(fx>1.0f) fx -= (int)(fx); + else if(fx<0.0f) fx+= 1-(int)(fx); - if(tex->flag & TEX_REPEAT_XMIR) { - int orig= (int)floor(origf); - if(orig & 1) - fx= 1.0f-fx; + if(tex->flag & TEX_REPEAT_XMIR) { + int orig= (int)floor(origf); + if(orig & 1) + fx= 1.0f-fx; + } } max= tex->xrepeat; @@ -1119,13 +1124,16 @@ static void do_2d_mapping(MTex *mtex, float *t, VlakRen *vlr, float *n, float *d if(tex->yrepeat>1) { float origf= fy *= tex->yrepeat; - if(fy>1.0f) fy -= (int)(fy); - else if(fy<0.0f) fy+= 1-(int)(fy); + // TXF: omit mirror here, see comments in do_material_tex() after do_2d_mapping() call + if (tex->texfilter == TXF_DEFAULT) { + if(fy>1.0f) fy -= (int)(fy); + else if(fy<0.0f) fy+= 1-(int)(fy); - if(tex->flag & TEX_REPEAT_YMIR) { - int orig= (int)floor(origf); - if(orig & 1) - fy= 1.0f-fy; + if(tex->flag & TEX_REPEAT_YMIR) { + int orig= (int)floor(origf); + if(orig & 1) + fy= 1.0f-fy; + } } if(maxyrepeat) @@ -1200,7 +1208,7 @@ static int multitex(Tex *tex, float *texvec, float *dxt, float *dyt, int osatex, retval= texnoise(tex, texres); break; case TEX_IMAGE: - if(osatex) retval= imagewraposa(tex, tex->ima, NULL, texvec, dxt, dyt, texres); + if(osatex) retval= imagewraposa(tex, tex->ima, NULL, texvec, dxt, dyt, texres); else retval= imagewrap(tex, tex->ima, NULL, texvec, texres); tag_image_time(tex->ima); /* tag image as having being used */ break; -- cgit v1.2.3