1 /****************************************************************************
2  * 		imagetex.cc: a texture class for images
3  *      This is part of the yafray package
4  *      Based on the original by: Mathias Wein; Copyright (C) 2006 Mathias Wein
5  *		Copyright (C) 2010 Rodrigo Placencia Vazquez (DarkTide)
6  *
7  *      This library is free software; you can redistribute it and/or
8  *      modify it under the terms of the GNU Lesser General Public
9  *      License as published by the Free Software Foundation; either
10  *      version 2.1 of the License, or (at your option) any later version.
11  *
12  *      This library is distributed in the hope that it will be useful,
13  *      but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  *      Lesser General Public License for more details.
16  *
17  *      You should have received a copy of the GNU Lesser General Public
18  *      License along with this library; if not, write to the Free Software
19  *      Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20  */
21 
22 #include <textures/imagetex.h>
23 #include <core_api/session.h>
24 #include <utilities/stringUtils.h>
25 #include <core_api/params.h>
26 
27 __BEGIN_YAFRAY
28 
29 float * textureImage_t::ewaWeightLut = nullptr;
30 
textureImage_t(imageHandler_t * ih,int intp,float gamma,colorSpaces_t color_space)31 textureImage_t::textureImage_t(imageHandler_t *ih, int intp, float gamma, colorSpaces_t color_space):
32 				image(ih), colorSpace(color_space), gamma(gamma), mirrorX(false), mirrorY(false)
33 {
34 	intp_type = intp;
35 }
36 
~textureImage_t()37 textureImage_t::~textureImage_t()
38 {
39 	// Here we simply clear the pointer, yafaray's core will handle the memory cleanup
40 	image = nullptr;
41 }
42 
resolution(int & x,int & y,int & z) const43 void textureImage_t::resolution(int &x, int &y, int &z) const
44 {
45 	x=image->getWidth();
46 	y=image->getHeight();
47 	z=0;
48 }
49 
interpolateImage(const point3d_t & p,mipMapParams_t * mmParams) const50 colorA_t textureImage_t::interpolateImage(const point3d_t &p, mipMapParams_t * mmParams) const
51 {
52 	if(mmParams && mmParams->forceImageLevel > 0.f) return mipMapsTrilinearInterpolation(p, mmParams);
53 
54 	colorA_t interpolatedColor(0.f);
55 
56 	switch(intp_type)
57 	{
58 		case INTP_NONE: interpolatedColor = noInterpolation(p); break;
59 		case INTP_BICUBIC: interpolatedColor = bicubicInterpolation(p); break;
60 		case INTP_MIPMAP_TRILINEAR:
61 									if(mmParams) interpolatedColor = mipMapsTrilinearInterpolation(p, mmParams);
62 									else interpolatedColor = bilinearInterpolation(p);
63 									break;
64 		case INTP_MIPMAP_EWA:
65 									if(mmParams) interpolatedColor = mipMapsEWAInterpolation(p, ewa_max_anisotropy, mmParams);
66 									else interpolatedColor = bilinearInterpolation(p);
67 									break;
68 		case INTP_BILINEAR:
69 		default: 			interpolatedColor = bilinearInterpolation(p); break;	//By default use Bilinear
70 	}
71 
72 	return interpolatedColor;
73 }
74 
getColor(const point3d_t & p,mipMapParams_t * mmParams) const75 colorA_t textureImage_t::getColor(const point3d_t &p, mipMapParams_t * mmParams) const
76 {
77 	point3d_t p1 = point3d_t(p.x, -p.y, p.z);
78 	colorA_t ret(0.f);
79 
80 	bool outside = doMapping(p1);
81 
82 	if(outside) return ret;
83 
84 	ret = interpolateImage(p1, mmParams);
85 
86 	return applyAdjustments(ret);
87 }
88 
getRawColor(const point3d_t & p,mipMapParams_t * mmParams) const89 colorA_t textureImage_t::getRawColor(const point3d_t &p, mipMapParams_t * mmParams) const
90 {
91 	// As from v3.2.0 all color buffers are already Linear RGB, if any part of the code requires the original "raw" color, a "de-linearization" (encoding again into the original color space) takes place in this function.
92 
93 	// For example for Non-RGB / Stencil / Bump / Normal maps, etc, textures are typically already linear and the user should select "linearRGB" in the texture properties, but if the user (by mistake) keeps the default sRGB for them, for example, the default linearization would apply a sRGB to linearRGB conversion that messes up the texture values. This function will try to reconstruct the original texture values. In this case (if the user selected incorrectly sRGB for a normal map, for example), this function will prevent wrong results, but it will be slower and it could be slightly inaccurate as the interpolation will take place in the (incorrectly) linearized texels.
94 
95 	//If the user correctly selected "linearRGB" for these kind of textures, the function below will not make any changes to the color and will keep the texture "as is" without any linearization nor de-linearization, which is the ideal case (fast and correct).
96 
97 	//The user is responsible to select the correct textures color spaces, if the user does not do it, results may not be the expected. This function is only a coarse "fail safe"
98 
99 	colorA_t ret = getColor(p, mmParams);
100 	ret.ColorSpace_from_linearRGB(colorSpace, gamma);
101 
102 	return ret;
103 }
104 
getColor(int x,int y,int z,mipMapParams_t * mmParams) const105 colorA_t textureImage_t::getColor(int x, int y, int z, mipMapParams_t * mmParams) const
106 {
107 	int resx=image->getWidth();
108 	int resy=image->getHeight();
109 
110 	y = resy - y; //on occasion change image storage from bottom to top...
111 
112 	x = std::max(0, std::min(resx-1, x));
113 	y = std::max(0, std::min(resy-1, y));
114 
115 	colorA_t ret(0.f);
116 
117 	if(mmParams && mmParams->forceImageLevel > 0.f) ret = image->getPixel(x, y, (int) floorf(mmParams->forceImageLevel * image->getHighestImgIndex()));
118 	else ret = image->getPixel(x, y);
119 
120 	return applyAdjustments(ret);
121 }
122 
getRawColor(int x,int y,int z,mipMapParams_t * mmParams) const123 colorA_t textureImage_t::getRawColor(int x, int y, int z, mipMapParams_t * mmParams) const
124 {
125 	// As from v3.2.0 all color buffers are already Linear RGB, if any part of the code requires the original "raw" color, a "de-linearization" (encoding again into the original color space) takes place in this function.
126 
127 	// For example for Non-RGB / Stencil / Bump / Normal maps, etc, textures are typically already linear and the user should select "linearRGB" in the texture properties, but if the user (by mistake) keeps the default sRGB for them, for example, the default linearization would apply a sRGB to linearRGB conversion that messes up the texture values. This function will try to reconstruct the original texture values. In this case (if the user selected incorrectly sRGB for a normal map, for example), this function will prevent wrong results, but it will be slower and it could be slightly inaccurate as the interpolation will take place in the (incorrectly) linearized texels.
128 
129 	//If the user correctly selected "linearRGB" for these kind of textures, the function below will not make any changes to the color and will keep the texture "as is" without any linearization nor de-linearization, which is the ideal case (fast and correct).
130 
131 	//The user is responsible to select the correct textures color spaces, if the user does not do it, results may not be the expected. This function is only a coarse "fail safe"
132 
133 	colorA_t ret = getColor(x, y, z, mmParams);
134 	ret.ColorSpace_from_linearRGB(colorSpace, gamma);
135 
136 	return ret;
137 }
138 
doMapping(point3d_t & texpt) const139 bool textureImage_t::doMapping(point3d_t &texpt) const
140 {
141 	bool outside = false;
142 
143 	texpt = 0.5f*texpt + 0.5f;
144 	// repeat, only valid for REPEAT clipmode
145 	if (tex_clipmode==TCL_REPEAT) {
146 
147 		if(xrepeat > 1) texpt.x *= (float)xrepeat;
148 		if(yrepeat > 1) texpt.y *= (float)yrepeat;
149 
150 		if (mirrorX && int(ceilf(texpt.x)) % 2 == 0) texpt.x = -texpt.x;
151 		if (mirrorY && int(ceilf(texpt.y)) % 2 == 0) texpt.y = -texpt.y;
152 
153 		if (texpt.x>1.f) texpt.x -= int(texpt.x);
154 		else if (texpt.x<0.f) texpt.x += 1 - int(texpt.x);
155 
156 		if (texpt.y>1.f) texpt.y -= int(texpt.y);
157 		else if (texpt.y<0.f) texpt.y += 1 - int(texpt.y);
158 	}
159 
160 	// crop
161 	if (cropx) texpt.x = cropminx + texpt.x*(cropmaxx - cropminx);
162 	if (cropy) texpt.y = cropminy + texpt.y*(cropmaxy - cropminy);
163 
164 	// rot90
165 	if(rot90) std::swap(texpt.x, texpt.y);
166 
167 	// clipping
168 	switch (tex_clipmode)
169 	{
170 		case TCL_CLIPCUBE: {
171 			if ((texpt.x<0) || (texpt.x>1) || (texpt.y<0) || (texpt.y>1) || (texpt.z<-1) || (texpt.z>1))
172 				outside = true;
173 			break;
174 		}
175 		case TCL_CHECKER: {
176 			int xs=(int)floor(texpt.x), ys=(int)floor(texpt.y);
177 			texpt.x -= xs;
178 			texpt.y -= ys;
179 			if ( !checker_odd && !((xs+ys) & 1) )
180 			{
181 				outside = true;
182 				break;
183 			}
184 			if ( !checker_even && ((xs+ys) & 1) )
185 			{
186 				outside = true;
187 				break;
188 			}
189 			// scale around center, (0.5, 0.5)
190 			if (checker_dist<1.0) {
191 				texpt.x = (texpt.x-0.5) / (1.0-checker_dist) + 0.5;
192 				texpt.y = (texpt.y-0.5) / (1.0-checker_dist) + 0.5;
193 			}
194 			// continue to TCL_CLIP
195 		}
196 		case TCL_CLIP: {
197 			if ((texpt.x<0) || (texpt.x>1) || (texpt.y<0) || (texpt.y>1))
198 				outside = true;
199 			break;
200 		}
201 		case TCL_EXTEND: {
202 			if (texpt.x>0.99999f) texpt.x=0.99999f; else if (texpt.x<0) texpt.x=0;
203 			if (texpt.y>0.99999f) texpt.y=0.99999f; else if (texpt.y<0) texpt.y=0;
204 			// no break, fall thru to TEX_REPEAT
205 		}
206 		default:
207 		case TCL_REPEAT: outside = false;
208 	}
209 	return outside;
210 }
211 
setCrop(float minx,float miny,float maxx,float maxy)212 void textureImage_t::setCrop(float minx, float miny, float maxx, float maxy)
213 {
214 	cropminx=minx, cropmaxx=maxx, cropminy=miny, cropmaxy=maxy;
215 	cropx = ((cropminx!=0.0) || (cropmaxx!=1.0));
216 	cropy = ((cropminy!=0.0) || (cropmaxy!=1.0));
217 }
218 
findTextureInterpolationCoordinates(int & coord0,int & coord1,int & coord2,int & coord3,float & coord_decimal_part,float coord_float,int resolution,bool repeat,bool mirror) const219 void textureImage_t::findTextureInterpolationCoordinates(int &coord0, int &coord1, int &coord2, int &coord3, float &coord_decimal_part, float coord_float, int resolution, bool repeat, bool mirror) const
220 {
221 	if(repeat)
222 	{
223 		coord1 = ((int)coord_float) % resolution;
224 
225 		if(mirror)
226 		{
227 
228 			if(coord_float < 0.f)
229 			{
230 				coord0 = 1 % resolution;
231 				coord2 = coord1;
232 				coord3 = coord0;
233 				coord_decimal_part = -coord_float;
234 			}
235 			else if(coord_float >= resolution - 1.f)
236 			{
237 				coord0 = (resolution+resolution-1) % resolution;
238 				coord2 = coord1;
239 				coord3 = coord0;
240 				coord_decimal_part = coord_float - ((int)coord_float);
241 			}
242 			else
243 			{
244 				coord0 = (resolution+coord1-1) % resolution;
245 				coord2 = coord1+1;
246 				if(coord2 >= resolution) coord2 = (resolution + resolution - coord2) % resolution;
247 				coord3 = coord1+2;
248 				if(coord3 >= resolution) coord3 = (resolution + resolution - coord3) % resolution;
249 				coord_decimal_part = coord_float - ((int)coord_float);
250 			}
251 		}
252 		else
253 		{
254 			if(coord_float > 0.f)
255 			{
256 				coord0 = (resolution+coord1-1) % resolution;
257 				coord2 = (coord1+1) % resolution;
258 				coord3 = (coord1+2) % resolution;
259 				coord_decimal_part = coord_float - ((int)coord_float);
260 			}
261 			else
262 			{
263 				coord0 = 1 % resolution;
264 				coord2 = (resolution-1) % resolution;
265 				coord3 = (resolution-2) % resolution;
266 				coord_decimal_part = -coord_float;
267 			}
268 		}
269 	}
270 	else
271 	{
272 		coord1 = std::max(0, std::min(resolution-1, ((int)coord_float)));
273 
274 		if(coord_float > 0.f) coord2 = std::min(resolution-1, coord1+1);
275 		else coord2 = 0;
276 
277 		coord0 = std::max(0, coord1-1);
278 		coord3 = std::min(resolution-1, coord2+1);
279 
280 		coord_decimal_part = coord_float - floor(coord_float);
281 	}
282 }
283 
noInterpolation(const point3d_t & p,int mipmaplevel) const284 colorA_t textureImage_t::noInterpolation(const point3d_t &p, int mipmaplevel) const
285 {
286 	int resx = image->getWidth(mipmaplevel);
287 	int resy = image->getHeight(mipmaplevel);
288 
289 	float xf = ((float)resx * (p.x - floor(p.x)));
290 	float yf = ((float)resy * (p.y - floor(p.y)));
291 
292 	int x0, x1, x2, x3, y0, y1, y2, y3;
293 	float dx, dy;
294 	findTextureInterpolationCoordinates(x0, x1, x2, x3, dx, xf, resx, tex_clipmode==TCL_REPEAT, mirrorX);
295 	findTextureInterpolationCoordinates(y0, y1, y2, y3, dy, yf, resy, tex_clipmode==TCL_REPEAT, mirrorY);
296 
297 	return image->getPixel(x1, y1, mipmaplevel);
298 }
299 
bilinearInterpolation(const point3d_t & p,int mipmaplevel) const300 colorA_t textureImage_t::bilinearInterpolation(const point3d_t &p, int mipmaplevel) const
301 {
302 	int resx = image->getWidth(mipmaplevel);
303 	int resy = image->getHeight(mipmaplevel);
304 
305 	float xf = ((float)resx * (p.x - floor(p.x))) - 0.5f;
306 	float yf = ((float)resy * (p.y - floor(p.y))) - 0.5f;
307 
308 	int x0, x1, x2, x3, y0, y1, y2, y3;
309 	float dx, dy;
310 	findTextureInterpolationCoordinates(x0, x1, x2, x3, dx, xf, resx, tex_clipmode==TCL_REPEAT, mirrorX);
311 	findTextureInterpolationCoordinates(y0, y1, y2, y3, dy, yf, resy, tex_clipmode==TCL_REPEAT, mirrorY);
312 
313 	colorA_t c11 = image->getPixel(x1, y1, mipmaplevel);
314 	colorA_t c21 = image->getPixel(x2, y1, mipmaplevel);
315 	colorA_t c12 = image->getPixel(x1, y2, mipmaplevel);
316 	colorA_t c22 = image->getPixel(x2, y2, mipmaplevel);
317 
318 	float w11 = (1-dx) * (1-dy);
319 	float w12 = (1-dx) * dy;
320 	float w21 = dx * (1-dy);
321 	float w22 = dx * dy;
322 
323 	return (w11 * c11) + (w12 * c12) + (w21 * c21) + (w22 * c22);
324 }
325 
bicubicInterpolation(const point3d_t & p,int mipmaplevel) const326 colorA_t textureImage_t::bicubicInterpolation(const point3d_t &p, int mipmaplevel) const
327 {
328 	int resx = image->getWidth(mipmaplevel);
329 	int resy = image->getHeight(mipmaplevel);
330 
331 	float xf = ((float)resx * (p.x - floor(p.x))) - 0.5f;
332 	float yf = ((float)resy * (p.y - floor(p.y))) - 0.5f;
333 
334 	int x0, x1, x2, x3, y0, y1, y2, y3;
335 	float dx, dy;
336 	findTextureInterpolationCoordinates(x0, x1, x2, x3, dx, xf, resx, tex_clipmode==TCL_REPEAT, mirrorX);
337 	findTextureInterpolationCoordinates(y0, y1, y2, y3, dy, yf, resy, tex_clipmode==TCL_REPEAT, mirrorY);
338 
339 	colorA_t c00 = image->getPixel(x0, y0, mipmaplevel);
340 	colorA_t c01 = image->getPixel(x0, y1, mipmaplevel);
341 	colorA_t c02 = image->getPixel(x0, y2, mipmaplevel);
342 	colorA_t c03 = image->getPixel(x0, y3, mipmaplevel);
343 
344 	colorA_t c10 = image->getPixel(x1, y0, mipmaplevel);
345 	colorA_t c11 = image->getPixel(x1, y1, mipmaplevel);
346 	colorA_t c12 = image->getPixel(x1, y2, mipmaplevel);
347 	colorA_t c13 = image->getPixel(x1, y3, mipmaplevel);
348 
349 	colorA_t c20 = image->getPixel(x2, y0, mipmaplevel);
350 	colorA_t c21 = image->getPixel(x2, y1, mipmaplevel);
351 	colorA_t c22 = image->getPixel(x2, y2, mipmaplevel);
352 	colorA_t c23 = image->getPixel(x2, y3, mipmaplevel);
353 
354 	colorA_t c30 = image->getPixel(x3, y0, mipmaplevel);
355 	colorA_t c31 = image->getPixel(x3, y1, mipmaplevel);
356 	colorA_t c32 = image->getPixel(x3, y2, mipmaplevel);
357 	colorA_t c33 = image->getPixel(x3, y3, mipmaplevel);
358 
359 	colorA_t cy0 = CubicInterpolate(c00, c10, c20, c30, dx);
360 	colorA_t cy1 = CubicInterpolate(c01, c11, c21, c31, dx);
361 	colorA_t cy2 = CubicInterpolate(c02, c12, c22, c32, dx);
362 	colorA_t cy3 = CubicInterpolate(c03, c13, c23, c33, dx);
363 
364 	return CubicInterpolate(cy0, cy1, cy2, cy3, dy);
365 }
366 
mipMapsTrilinearInterpolation(const point3d_t & p,mipMapParams_t * mmParams) const367 colorA_t textureImage_t::mipMapsTrilinearInterpolation(const point3d_t &p, mipMapParams_t * mmParams) const
368 {
369 	float dS = std::max(fabsf(mmParams->dSdx), fabsf(mmParams->dSdy)) * image->getWidth();
370 	float dT = std::max(fabsf(mmParams->dTdx), fabsf(mmParams->dTdy)) * image->getHeight();
371 	float mipmaplevel = 0.5f * log2(dS*dS + dT*dT);
372 
373 	if(mmParams->forceImageLevel > 0.f) mipmaplevel = mmParams->forceImageLevel * image->getHighestImgIndex();
374 
375 	mipmaplevel += trilinear_level_bias;
376 
377 	mipmaplevel = std::min(std::max(0.f, mipmaplevel), (float) image->getHighestImgIndex());
378 
379 	int mipmaplevelA = (int) floor(mipmaplevel);
380 	int mipmaplevelB = (int) ceil(mipmaplevel);
381 	float mipmaplevelDelta = mipmaplevel - (float) mipmaplevelA;
382 
383 	colorA_t col = bilinearInterpolation(p, mipmaplevelA);
384 	colorA_t colB = bilinearInterpolation(p, mipmaplevelB);
385 
386 	col.blend(colB, mipmaplevelDelta);
387 
388 	return col;
389 }
390 
391 //All EWA interpolation/calculation code has been adapted from PBRT v2 (https://github.com/mmp/pbrt-v2). see LICENSES file
392 
mipMapsEWAInterpolation(const point3d_t & p,float maxAnisotropy,mipMapParams_t * mmParams) const393 colorA_t textureImage_t::mipMapsEWAInterpolation(const point3d_t &p, float maxAnisotropy, mipMapParams_t * mmParams) const
394 {
395 	float dS0 = fabsf(mmParams->dSdx);
396 	float dS1 = fabsf(mmParams->dSdy);
397 	float dT0 = fabsf(mmParams->dTdx);
398 	float dT1 = fabsf(mmParams->dTdy);
399 
400 	if((dS0*dS0 + dT0*dT0) < (dS1*dS1 + dT1*dT1))
401 	{
402 		std::swap(dS0, dS1);
403 		std::swap(dT0, dT1);
404 	}
405 
406 	float majorLength = sqrtf(dS0*dS0 + dT0*dT0);
407 	float minorLength = sqrtf(dS1*dS1 + dT1*dT1);
408 
409 	if((minorLength * maxAnisotropy < majorLength) && (minorLength > 0.f))
410 	{
411 		float scale = majorLength / (minorLength * maxAnisotropy);
412 		dS1 *= scale;
413 		dT1 *= scale;
414 		minorLength *= scale;
415 	}
416 
417 	if(minorLength <= 0.f) return bilinearInterpolation(p);
418 
419 	float mipmaplevel = image->getHighestImgIndex() - 1.f + log2(minorLength);
420 
421 	mipmaplevel = std::min(std::max(0.f, mipmaplevel), (float) image->getHighestImgIndex());
422 
423 
424 	int mipmaplevelA = (int) floor(mipmaplevel);
425 	int mipmaplevelB = (int) ceil(mipmaplevel);
426 	float mipmaplevelDelta = mipmaplevel - (float) mipmaplevelA;
427 
428 	colorA_t col = EWAEllipticCalculation(p, dS0, dT0, dS1, dT1, mipmaplevelA);
429 	colorA_t colB = EWAEllipticCalculation(p, dS0, dT0, dS1, dT1, mipmaplevelB);
430 
431 	col.blend(colB, mipmaplevelDelta);
432 
433 	return col;
434 }
435 
Mod(int a,int b)436 inline int Mod(int a, int b) {
437     int n = int(a/b);
438     a -= n*b;
439     if (a < 0) a += b;
440     return a;
441 }
442 
EWAEllipticCalculation(const point3d_t & p,float dS0,float dT0,float dS1,float dT1,int mipmaplevel) const443 colorA_t textureImage_t::EWAEllipticCalculation(const point3d_t &p, float dS0, float dT0, float dS1, float dT1, int mipmaplevel) const
444 {
445 	if(mipmaplevel >= image->getHighestImgIndex())
446 	{
447 		int resx = image->getWidth(mipmaplevel);
448 		int resy = image->getHeight(mipmaplevel);
449 
450 		return image->getPixel(Mod(p.x, resx), Mod(p.y, resy), image->getHighestImgIndex());
451 	}
452 
453 	int resx = image->getWidth(mipmaplevel);
454 	int resy = image->getHeight(mipmaplevel);
455 
456 	float xf = ((float)resx * (p.x - floor(p.x))) - 0.5f;
457 	float yf = ((float)resy * (p.y - floor(p.y))) - 0.5f;
458 
459 	dS0 *= resx;
460 	dS1 *= resx;
461 	dT0 *= resy;
462 	dT1 *= resy;
463 
464 	float A = dT0*dT0 + dT1*dT1 + 1;
465 	float B = -2.f * (dS0*dT0 + dS1*dT1);
466 	float C = dS0*dS0 + dS1*dS1 + 1;
467 	float invF = 1.f / (A*C - B*B*0.25f);
468 	A *= invF;
469 	B *= invF;
470 	C *= invF;
471 
472 	float det = -B*B + 4.f*A*C;
473 	float invDet = 1.f / det;
474 	float uSqrt = sqrtf(det * C);
475 	float vSqrt = sqrtf(A * det);
476 
477 	int s0 = (int) ceilf(xf - 2.f * invDet * uSqrt);
478 	int s1 = (int) floorf(xf + 2.f * invDet * uSqrt);
479 	int t0 = (int) ceilf(yf - 2.f * invDet * vSqrt);
480 	int t1 = (int) floorf(yf + 2.f * invDet * vSqrt);
481 
482 	colorA_t sumCol(0.f);
483 
484 	float sumWts = 0.f;
485 	for(int it = t0; it <= t1; ++it)
486 	{
487 		float tt = it - yf;
488 		for(int is = s0; is <= s1; ++is)
489 		{
490 			float ss = is - xf;
491 
492 			float r2 = A*ss*ss + B*ss*tt + C*tt*tt;
493 			if(r2 < 1.f)
494 			{
495 				float weight = ewaWeightLut[std::min((int)floorf(r2*EWA_WEIGHT_LUT_SIZE), EWA_WEIGHT_LUT_SIZE-1)];
496 				int ismod = Mod(is, resx);
497 				int itmod = Mod(it, resy);
498 
499 				sumCol += image->getPixel(ismod, itmod, mipmaplevel) * weight;
500 				sumWts += weight;
501 			}
502 		}
503 	}
504 
505 	if(sumWts > 0.f) sumCol = sumCol / sumWts;
506 	else sumCol = colorA_t(0.f);
507 
508 	return sumCol;
509 }
510 
generateEWALookupTable()511 void textureImage_t::generateEWALookupTable()
512 {
513 	if(!ewaWeightLut)
514 	{
515 		Y_DEBUG << "** GENERATING EWA LOOKUP **" << yendl;
516 		ewaWeightLut = (float *) malloc(sizeof(float) * EWA_WEIGHT_LUT_SIZE);
517 		for(int i = 0; i < EWA_WEIGHT_LUT_SIZE; ++i)
518 		{
519 			float alpha = 2.f;
520 			float r2 = float(i) / float(EWA_WEIGHT_LUT_SIZE - 1);
521 			ewaWeightLut[i] = expf(-alpha * r2) - expf(-alpha);
522 		}
523 	}
524 }
525 
string2cliptype(const std::string * clipname)526 int string2cliptype(const std::string *clipname)
527 {
528 	// default "repeat"
529 	int	tex_clipmode = TCL_REPEAT;
530 	if(!clipname) return tex_clipmode;
531 	if (*clipname=="extend")			tex_clipmode = TCL_EXTEND;
532 	else if (*clipname=="clip")		tex_clipmode = TCL_CLIP;
533 	else if (*clipname=="clipcube")	tex_clipmode = TCL_CLIPCUBE;
534 	else if (*clipname=="checker")	tex_clipmode = TCL_CHECKER;
535 	return tex_clipmode;
536 }
537 
factory(paraMap_t & params,renderEnvironment_t & render)538 texture_t *textureImage_t::factory(paraMap_t &params, renderEnvironment_t &render)
539 {
540 	const std::string *name = nullptr;
541 	const std::string *intpstr = nullptr;
542 	double gamma = 1.0;
543 	double expadj = 0.0;
544 	bool normalmap = false;
545 	std::string color_space_string = "Raw_Manual_Gamma";
546 	colorSpaces_t color_space = RAW_MANUAL_GAMMA;
547 	std::string texture_optimization_string = "optimized";
548 	textureOptimization_t texture_optimization = TEX_OPTIMIZATION_OPTIMIZED;
549 	bool img_grayscale = false;
550 	textureImage_t *tex = nullptr;
551 	imageHandler_t *ih = nullptr;
552 	params.getParam("interpolate", intpstr);
553 	params.getParam("color_space", color_space_string);
554 	params.getParam("gamma", gamma);
555 	params.getParam("exposure_adjust", expadj);
556 	params.getParam("normalmap", normalmap);
557 	params.getParam("filename", name);
558 	params.getParam("texture_optimization", texture_optimization_string);
559 	params.getParam("img_grayscale", img_grayscale);
560 
561 	if(!name)
562 	{
563 		Y_ERROR << "ImageTexture: Required argument filename not found for image texture" << yendl;
564 		return nullptr;
565 	}
566 
567 	// interpolation type, bilinear default
568 	int intp = INTP_BILINEAR;
569 
570 	if(intpstr)
571 	{
572 		if (*intpstr == "none") intp = INTP_NONE;
573 		else if (*intpstr == "bicubic") intp = INTP_BICUBIC;
574 		else if (*intpstr == "mipmap_trilinear") intp = INTP_MIPMAP_TRILINEAR;
575 		else if (*intpstr == "mipmap_ewa") intp = INTP_MIPMAP_EWA;
576 	}
577 
578 	size_t lDot = name->rfind(".") + 1;
579 	size_t lSlash = name->rfind("/") + 1;
580 
581 	std::string ext = toLower(name->substr(lDot));
582 
583 	std::string fmt = render.getImageFormatFromExtension(ext);
584 
585 	if(fmt == "")
586 	{
587 		Y_ERROR << "ImageTexture: Image extension not recognized, dropping texture." << yendl;
588 		return nullptr;
589 	}
590 
591 	paraMap_t ihpm;
592 	ihpm["type"] = fmt;
593 	ihpm["for_output"] = false;
594 	std::string ihname = "ih";
595 	ihname.append(toLower(name->substr(lSlash, lDot - lSlash - 1)));
596 
597 	ih = render.createImageHandler(ihname, ihpm);
598 
599 	if(!ih)
600 	{
601 		Y_ERROR << "ImageTexture: Couldn't create image handler, dropping texture." << yendl;
602 		return nullptr;
603 	}
604 
605 
606 	if(ih->isHDR())
607 	{
608 		if(color_space_string != "LinearRGB") Y_VERBOSE << "ImageTexture: The image is a HDR/EXR file: forcing linear RGB and ignoring selected color space '" << color_space_string <<"' and the gamma setting." << yendl;
609 		color_space = LINEAR_RGB;
610 		if(texture_optimization_string != "none") Y_VERBOSE << "ImageTexture: The image is a HDR/EXR file: forcing texture optimization to 'none' and ignoring selected texture optimization '" << texture_optimization_string <<"'" << yendl;
611 		texture_optimization = TEX_OPTIMIZATION_NONE;	//FIXME DAVID: Maybe we should leave this to imageHandler factory code...
612 	}
613 	else
614 	{
615 		if(color_space_string == "sRGB") color_space = SRGB;
616 		else if(color_space_string == "XYZ") color_space = XYZ_D65;
617 		else if(color_space_string == "LinearRGB") color_space = LINEAR_RGB;
618 		else if(color_space_string == "Raw_Manual_Gamma") color_space = RAW_MANUAL_GAMMA;
619 		else color_space = SRGB;
620 
621 		if(texture_optimization_string == "none") texture_optimization = TEX_OPTIMIZATION_NONE;
622 		else if(texture_optimization_string == "optimized") texture_optimization = TEX_OPTIMIZATION_OPTIMIZED;
623 		else if(texture_optimization_string == "compressed") texture_optimization = TEX_OPTIMIZATION_COMPRESSED;
624 		else texture_optimization = TEX_OPTIMIZATION_NONE;
625 	}
626 
627 	ih->setColorSpace(color_space, gamma);
628 	ih->setTextureOptimization(texture_optimization);	//FIXME DAVID: Maybe we should leave this to imageHandler factory code...
629 	ih->setGrayScaleSetting(img_grayscale);
630 
631 	if(!ih->loadFromFile(*name))
632 	{
633 		Y_ERROR << "ImageTexture: Couldn't load image file, dropping texture." << yendl;
634 		return nullptr;
635 	}
636 
637 	tex = new textureImage_t(ih, intp, gamma, color_space);
638 
639 	if(!tex)
640 	{
641 		Y_ERROR << "ImageTexture: Couldn't create image texture." << yendl;
642 		return nullptr;
643 	}
644 
645 	if(intp == INTP_MIPMAP_TRILINEAR || intp == INTP_MIPMAP_EWA)
646 	{
647 		ih->generateMipMaps();
648 		if(!session.getDifferentialRaysEnabled())
649 		{
650 			Y_VERBOSE << "At least one texture using mipmaps interpolation, enabling ray differentials." << yendl;
651 			session.setDifferentialRaysEnabled(true);	//If there is at least one texture using mipmaps, then enable differential rays in the rendering process.
652 		}
653 
654 		/*//FIXME DAVID: TEST SAVING MIPMAPS. CAREFUL: IT COULD CAUSE CRASHES!
655 		for(int i=0; i<=ih->getHighestImgIndex(); ++i)
656 		{
657 			std::stringstream ss;
658 			ss << "//tmp//saved_mipmap_" << ihname << "__" << i;
659 			ih->saveToFile(ss.str(), i);
660 		}*/
661 	}
662 
663 	// setup image
664 	bool rot90 = false;
665 	bool even_tiles=false, odd_tiles=true;
666 	bool use_alpha=true, calc_alpha=false;
667 	int xrep=1, yrep=1;
668 	double minx=0.0, miny=0.0, maxx=1.0, maxy=1.0;
669 	double cdist=0.0;
670 	const std::string *clipmode = nullptr;
671 	bool mirror_x = false;
672 	bool mirror_y = false;
673 	float intensity = 1.f, contrast = 1.f, saturation = 1.f, hue = 0.f, factor_red = 1.f, factor_green = 1.f, factor_blue = 1.f;
674 	bool clamp = false;
675 	float trilinear_level_bias = 0.f;
676 	float ewa_max_anisotropy = 8.f;
677 
678 	params.getParam("xrepeat", xrep);
679 	params.getParam("yrepeat", yrep);
680 	params.getParam("cropmin_x", minx);
681 	params.getParam("cropmin_y", miny);
682 	params.getParam("cropmax_x", maxx);
683 	params.getParam("cropmax_y", maxy);
684 	params.getParam("rot90", rot90);
685 	params.getParam("clipping", clipmode);
686 	params.getParam("even_tiles", even_tiles);
687 	params.getParam("odd_tiles", odd_tiles);
688 	params.getParam("checker_dist", cdist);
689 	params.getParam("use_alpha", use_alpha);
690 	params.getParam("calc_alpha", calc_alpha);
691 	params.getParam("mirror_x", mirror_x);
692 	params.getParam("mirror_y", mirror_y);
693 	params.getParam("trilinear_level_bias", trilinear_level_bias);
694 	params.getParam("ewa_max_anisotropy", ewa_max_anisotropy);
695 
696 	params.getParam("adj_mult_factor_red", factor_red);
697 	params.getParam("adj_mult_factor_green", factor_green);
698 	params.getParam("adj_mult_factor_blue", factor_blue);
699 	params.getParam("adj_intensity", intensity);
700 	params.getParam("adj_contrast", contrast);
701 	params.getParam("adj_saturation", saturation);
702 	params.getParam("adj_hue", hue);
703 	params.getParam("adj_clamp", clamp);
704 
705 	tex->xrepeat = xrep;
706 	tex->yrepeat = yrep;
707 	tex->rot90 = rot90;
708 	tex->setCrop(minx, miny, maxx, maxy);
709 	tex->use_alpha = use_alpha;
710 	tex->calc_alpha = calc_alpha;
711 	tex->normalmap = normalmap;
712 	tex->tex_clipmode = string2cliptype(clipmode);
713 	tex->checker_even = even_tiles;
714 	tex->checker_odd = odd_tiles;
715 	tex->checker_dist = cdist;
716 	tex->mirrorX = mirror_x;
717 	tex->mirrorY = mirror_y;
718 
719 	tex->setAdjustments(intensity, contrast, saturation, hue, clamp, factor_red, factor_green, factor_blue);
720 
721 	tex->trilinear_level_bias = trilinear_level_bias;
722 	tex->ewa_max_anisotropy = ewa_max_anisotropy;
723 
724 	if(intp == INTP_MIPMAP_EWA) tex->generateEWALookupTable();
725 
726 	return tex;
727 }
728 
729 __END_YAFRAY
730