1 /*
2 * Copyright 2011-2016 Blender Foundation
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #ifndef __KERNEL_CPU_IMAGE_H__
18 #define __KERNEL_CPU_IMAGE_H__
19
20 #ifdef WITH_NANOVDB
21 # include <nanovdb/NanoVDB.h>
22 # include <nanovdb/util/SampleFromVoxels.h>
23 #endif
24
25 CCL_NAMESPACE_BEGIN
26
27 /* Make template functions private so symbols don't conflict between kernels with different
28 * instruction sets. */
29 namespace {
30
31 template<typename T> struct TextureInterpolator {
32 #define SET_CUBIC_SPLINE_WEIGHTS(u, t) \
33 { \
34 u[0] = (((-1.0f / 6.0f) * t + 0.5f) * t - 0.5f) * t + (1.0f / 6.0f); \
35 u[1] = ((0.5f * t - 1.0f) * t) * t + (2.0f / 3.0f); \
36 u[2] = ((-0.5f * t + 0.5f) * t + 0.5f) * t + (1.0f / 6.0f); \
37 u[3] = (1.0f / 6.0f) * t * t * t; \
38 } \
39 (void)0
40
readTextureInterpolator41 static ccl_always_inline float4 read(float4 r)
42 {
43 return r;
44 }
45
readTextureInterpolator46 static ccl_always_inline float4 read(uchar4 r)
47 {
48 float f = 1.0f / 255.0f;
49 return make_float4(r.x * f, r.y * f, r.z * f, r.w * f);
50 }
51
readTextureInterpolator52 static ccl_always_inline float4 read(uchar r)
53 {
54 float f = r * (1.0f / 255.0f);
55 return make_float4(f, f, f, 1.0f);
56 }
57
readTextureInterpolator58 static ccl_always_inline float4 read(float r)
59 {
60 /* TODO(dingto): Optimize this, so interpolation
61 * happens on float instead of float4 */
62 return make_float4(r, r, r, 1.0f);
63 }
64
readTextureInterpolator65 static ccl_always_inline float4 read(half4 r)
66 {
67 return half4_to_float4(r);
68 }
69
readTextureInterpolator70 static ccl_always_inline float4 read(half r)
71 {
72 float f = half_to_float(r);
73 return make_float4(f, f, f, 1.0f);
74 }
75
readTextureInterpolator76 static ccl_always_inline float4 read(uint16_t r)
77 {
78 float f = r * (1.0f / 65535.0f);
79 return make_float4(f, f, f, 1.0f);
80 }
81
readTextureInterpolator82 static ccl_always_inline float4 read(ushort4 r)
83 {
84 float f = 1.0f / 65535.0f;
85 return make_float4(r.x * f, r.y * f, r.z * f, r.w * f);
86 }
87
readTextureInterpolator88 static ccl_always_inline float4 read(const T *data, int x, int y, int width, int height)
89 {
90 if (x < 0 || y < 0 || x >= width || y >= height) {
91 return make_float4(0.0f, 0.0f, 0.0f, 0.0f);
92 }
93 return read(data[y * width + x]);
94 }
95
wrap_periodicTextureInterpolator96 static ccl_always_inline int wrap_periodic(int x, int width)
97 {
98 x %= width;
99 if (x < 0)
100 x += width;
101 return x;
102 }
103
wrap_clampTextureInterpolator104 static ccl_always_inline int wrap_clamp(int x, int width)
105 {
106 return clamp(x, 0, width - 1);
107 }
108
fracTextureInterpolator109 static ccl_always_inline float frac(float x, int *ix)
110 {
111 int i = float_to_int(x) - ((x < 0.0f) ? 1 : 0);
112 *ix = i;
113 return x - (float)i;
114 }
115
116 /* ******** 2D interpolation ******** */
117
interp_closestTextureInterpolator118 static ccl_always_inline float4 interp_closest(const TextureInfo &info, float x, float y)
119 {
120 const T *data = (const T *)info.data;
121 const int width = info.width;
122 const int height = info.height;
123 int ix, iy;
124 frac(x * (float)width, &ix);
125 frac(y * (float)height, &iy);
126 switch (info.extension) {
127 case EXTENSION_REPEAT:
128 ix = wrap_periodic(ix, width);
129 iy = wrap_periodic(iy, height);
130 break;
131 case EXTENSION_CLIP:
132 if (x < 0.0f || y < 0.0f || x > 1.0f || y > 1.0f) {
133 return make_float4(0.0f, 0.0f, 0.0f, 0.0f);
134 }
135 ATTR_FALLTHROUGH;
136 case EXTENSION_EXTEND:
137 ix = wrap_clamp(ix, width);
138 iy = wrap_clamp(iy, height);
139 break;
140 default:
141 kernel_assert(0);
142 return make_float4(0.0f, 0.0f, 0.0f, 0.0f);
143 }
144 return read(data[ix + iy * width]);
145 }
146
interp_linearTextureInterpolator147 static ccl_always_inline float4 interp_linear(const TextureInfo &info, float x, float y)
148 {
149 const T *data = (const T *)info.data;
150 const int width = info.width;
151 const int height = info.height;
152 int ix, iy, nix, niy;
153 const float tx = frac(x * (float)width - 0.5f, &ix);
154 const float ty = frac(y * (float)height - 0.5f, &iy);
155 switch (info.extension) {
156 case EXTENSION_REPEAT:
157 ix = wrap_periodic(ix, width);
158 iy = wrap_periodic(iy, height);
159 nix = wrap_periodic(ix + 1, width);
160 niy = wrap_periodic(iy + 1, height);
161 break;
162 case EXTENSION_CLIP:
163 nix = ix + 1;
164 niy = iy + 1;
165 break;
166 case EXTENSION_EXTEND:
167 nix = wrap_clamp(ix + 1, width);
168 niy = wrap_clamp(iy + 1, height);
169 ix = wrap_clamp(ix, width);
170 iy = wrap_clamp(iy, height);
171 break;
172 default:
173 kernel_assert(0);
174 return make_float4(0.0f, 0.0f, 0.0f, 0.0f);
175 }
176 return (1.0f - ty) * (1.0f - tx) * read(data, ix, iy, width, height) +
177 (1.0f - ty) * tx * read(data, nix, iy, width, height) +
178 ty * (1.0f - tx) * read(data, ix, niy, width, height) +
179 ty * tx * read(data, nix, niy, width, height);
180 }
181
interp_cubicTextureInterpolator182 static ccl_always_inline float4 interp_cubic(const TextureInfo &info, float x, float y)
183 {
184 const T *data = (const T *)info.data;
185 const int width = info.width;
186 const int height = info.height;
187 int ix, iy, nix, niy;
188 const float tx = frac(x * (float)width - 0.5f, &ix);
189 const float ty = frac(y * (float)height - 0.5f, &iy);
190 int pix, piy, nnix, nniy;
191 switch (info.extension) {
192 case EXTENSION_REPEAT:
193 ix = wrap_periodic(ix, width);
194 iy = wrap_periodic(iy, height);
195 pix = wrap_periodic(ix - 1, width);
196 piy = wrap_periodic(iy - 1, height);
197 nix = wrap_periodic(ix + 1, width);
198 niy = wrap_periodic(iy + 1, height);
199 nnix = wrap_periodic(ix + 2, width);
200 nniy = wrap_periodic(iy + 2, height);
201 break;
202 case EXTENSION_CLIP:
203 pix = ix - 1;
204 piy = iy - 1;
205 nix = ix + 1;
206 niy = iy + 1;
207 nnix = ix + 2;
208 nniy = iy + 2;
209 break;
210 case EXTENSION_EXTEND:
211 pix = wrap_clamp(ix - 1, width);
212 piy = wrap_clamp(iy - 1, height);
213 nix = wrap_clamp(ix + 1, width);
214 niy = wrap_clamp(iy + 1, height);
215 nnix = wrap_clamp(ix + 2, width);
216 nniy = wrap_clamp(iy + 2, height);
217 ix = wrap_clamp(ix, width);
218 iy = wrap_clamp(iy, height);
219 break;
220 default:
221 kernel_assert(0);
222 return make_float4(0.0f, 0.0f, 0.0f, 0.0f);
223 }
224 const int xc[4] = {pix, ix, nix, nnix};
225 const int yc[4] = {piy, iy, niy, nniy};
226 float u[4], v[4];
227 /* Some helper macro to keep code reasonable size,
228 * let compiler to inline all the matrix multiplications.
229 */
230 #define DATA(x, y) (read(data, xc[x], yc[y], width, height))
231 #define TERM(col) \
232 (v[col] * \
233 (u[0] * DATA(0, col) + u[1] * DATA(1, col) + u[2] * DATA(2, col) + u[3] * DATA(3, col)))
234
235 SET_CUBIC_SPLINE_WEIGHTS(u, tx);
236 SET_CUBIC_SPLINE_WEIGHTS(v, ty);
237
238 /* Actual interpolation. */
239 return TERM(0) + TERM(1) + TERM(2) + TERM(3);
240 #undef TERM
241 #undef DATA
242 }
243
interpTextureInterpolator244 static ccl_always_inline float4 interp(const TextureInfo &info, float x, float y)
245 {
246 if (UNLIKELY(!info.data)) {
247 return make_float4(0.0f, 0.0f, 0.0f, 0.0f);
248 }
249 switch (info.interpolation) {
250 case INTERPOLATION_CLOSEST:
251 return interp_closest(info, x, y);
252 case INTERPOLATION_LINEAR:
253 return interp_linear(info, x, y);
254 default:
255 return interp_cubic(info, x, y);
256 }
257 }
258
259 /* ******** 3D interpolation ******** */
260
interp_3d_closestTextureInterpolator261 static ccl_always_inline float4 interp_3d_closest(const TextureInfo &info,
262 float x,
263 float y,
264 float z)
265 {
266 int width = info.width;
267 int height = info.height;
268 int depth = info.depth;
269 int ix, iy, iz;
270
271 frac(x * (float)width, &ix);
272 frac(y * (float)height, &iy);
273 frac(z * (float)depth, &iz);
274
275 switch (info.extension) {
276 case EXTENSION_REPEAT:
277 ix = wrap_periodic(ix, width);
278 iy = wrap_periodic(iy, height);
279 iz = wrap_periodic(iz, depth);
280 break;
281 case EXTENSION_CLIP:
282 if (x < 0.0f || y < 0.0f || z < 0.0f || x > 1.0f || y > 1.0f || z > 1.0f) {
283 return make_float4(0.0f, 0.0f, 0.0f, 0.0f);
284 }
285 ATTR_FALLTHROUGH;
286 case EXTENSION_EXTEND:
287 ix = wrap_clamp(ix, width);
288 iy = wrap_clamp(iy, height);
289 iz = wrap_clamp(iz, depth);
290 break;
291 default:
292 kernel_assert(0);
293 return make_float4(0.0f, 0.0f, 0.0f, 0.0f);
294 }
295
296 const T *data = (const T *)info.data;
297 return read(data[ix + iy * width + iz * width * height]);
298 }
299
interp_3d_linearTextureInterpolator300 static ccl_always_inline float4 interp_3d_linear(const TextureInfo &info,
301 float x,
302 float y,
303 float z)
304 {
305 int width = info.width;
306 int height = info.height;
307 int depth = info.depth;
308 int ix, iy, iz;
309 int nix, niy, niz;
310
311 float tx = frac(x * (float)width - 0.5f, &ix);
312 float ty = frac(y * (float)height - 0.5f, &iy);
313 float tz = frac(z * (float)depth - 0.5f, &iz);
314
315 switch (info.extension) {
316 case EXTENSION_REPEAT:
317 ix = wrap_periodic(ix, width);
318 iy = wrap_periodic(iy, height);
319 iz = wrap_periodic(iz, depth);
320
321 nix = wrap_periodic(ix + 1, width);
322 niy = wrap_periodic(iy + 1, height);
323 niz = wrap_periodic(iz + 1, depth);
324 break;
325 case EXTENSION_CLIP:
326 if (x < 0.0f || y < 0.0f || z < 0.0f || x > 1.0f || y > 1.0f || z > 1.0f) {
327 return make_float4(0.0f, 0.0f, 0.0f, 0.0f);
328 }
329 ATTR_FALLTHROUGH;
330 case EXTENSION_EXTEND:
331 nix = wrap_clamp(ix + 1, width);
332 niy = wrap_clamp(iy + 1, height);
333 niz = wrap_clamp(iz + 1, depth);
334
335 ix = wrap_clamp(ix, width);
336 iy = wrap_clamp(iy, height);
337 iz = wrap_clamp(iz, depth);
338 break;
339 default:
340 kernel_assert(0);
341 return make_float4(0.0f, 0.0f, 0.0f, 0.0f);
342 }
343
344 const T *data = (const T *)info.data;
345 float4 r;
346
347 r = (1.0f - tz) * (1.0f - ty) * (1.0f - tx) *
348 read(data[ix + iy * width + iz * width * height]);
349 r += (1.0f - tz) * (1.0f - ty) * tx * read(data[nix + iy * width + iz * width * height]);
350 r += (1.0f - tz) * ty * (1.0f - tx) * read(data[ix + niy * width + iz * width * height]);
351 r += (1.0f - tz) * ty * tx * read(data[nix + niy * width + iz * width * height]);
352
353 r += tz * (1.0f - ty) * (1.0f - tx) * read(data[ix + iy * width + niz * width * height]);
354 r += tz * (1.0f - ty) * tx * read(data[nix + iy * width + niz * width * height]);
355 r += tz * ty * (1.0f - tx) * read(data[ix + niy * width + niz * width * height]);
356 r += tz * ty * tx * read(data[nix + niy * width + niz * width * height]);
357
358 return r;
359 }
360
361 /* TODO(sergey): For some unspeakable reason both GCC-6 and Clang-3.9 are
362 * causing stack overflow issue in this function unless it is inlined.
363 *
364 * Only happens for AVX2 kernel and global __KERNEL_SSE__ vectorization
365 * enabled.
366 */
367 #if defined(__GNUC__) || defined(__clang__)
368 static ccl_always_inline
369 #else
370 static ccl_never_inline
371 #endif
372 float4
interp_3d_tricubicTextureInterpolator373 interp_3d_tricubic(const TextureInfo &info, float x, float y, float z)
374 {
375 int width = info.width;
376 int height = info.height;
377 int depth = info.depth;
378 int ix, iy, iz;
379 int nix, niy, niz;
380 /* Tricubic b-spline interpolation. */
381 const float tx = frac(x * (float)width - 0.5f, &ix);
382 const float ty = frac(y * (float)height - 0.5f, &iy);
383 const float tz = frac(z * (float)depth - 0.5f, &iz);
384 int pix, piy, piz, nnix, nniy, nniz;
385
386 switch (info.extension) {
387 case EXTENSION_REPEAT:
388 ix = wrap_periodic(ix, width);
389 iy = wrap_periodic(iy, height);
390 iz = wrap_periodic(iz, depth);
391
392 pix = wrap_periodic(ix - 1, width);
393 piy = wrap_periodic(iy - 1, height);
394 piz = wrap_periodic(iz - 1, depth);
395
396 nix = wrap_periodic(ix + 1, width);
397 niy = wrap_periodic(iy + 1, height);
398 niz = wrap_periodic(iz + 1, depth);
399
400 nnix = wrap_periodic(ix + 2, width);
401 nniy = wrap_periodic(iy + 2, height);
402 nniz = wrap_periodic(iz + 2, depth);
403 break;
404 case EXTENSION_CLIP:
405 if (x < 0.0f || y < 0.0f || z < 0.0f || x > 1.0f || y > 1.0f || z > 1.0f) {
406 return make_float4(0.0f, 0.0f, 0.0f, 0.0f);
407 }
408 ATTR_FALLTHROUGH;
409 case EXTENSION_EXTEND:
410 pix = wrap_clamp(ix - 1, width);
411 piy = wrap_clamp(iy - 1, height);
412 piz = wrap_clamp(iz - 1, depth);
413
414 nix = wrap_clamp(ix + 1, width);
415 niy = wrap_clamp(iy + 1, height);
416 niz = wrap_clamp(iz + 1, depth);
417
418 nnix = wrap_clamp(ix + 2, width);
419 nniy = wrap_clamp(iy + 2, height);
420 nniz = wrap_clamp(iz + 2, depth);
421
422 ix = wrap_clamp(ix, width);
423 iy = wrap_clamp(iy, height);
424 iz = wrap_clamp(iz, depth);
425 break;
426 default:
427 kernel_assert(0);
428 return make_float4(0.0f, 0.0f, 0.0f, 0.0f);
429 }
430
431 const int xc[4] = {pix, ix, nix, nnix};
432 const int yc[4] = {width * piy, width * iy, width * niy, width * nniy};
433 const int zc[4] = {
434 width * height * piz, width * height * iz, width * height * niz, width * height * nniz};
435 float u[4], v[4], w[4];
436
437 /* Some helper macro to keep code reasonable size,
438 * let compiler to inline all the matrix multiplications.
439 */
440 #define DATA(x, y, z) (read(data[xc[x] + yc[y] + zc[z]]))
441 #define COL_TERM(col, row) \
442 (v[col] * (u[0] * DATA(0, col, row) + u[1] * DATA(1, col, row) + u[2] * DATA(2, col, row) + \
443 u[3] * DATA(3, col, row)))
444 #define ROW_TERM(row) \
445 (w[row] * (COL_TERM(0, row) + COL_TERM(1, row) + COL_TERM(2, row) + COL_TERM(3, row)))
446
447 SET_CUBIC_SPLINE_WEIGHTS(u, tx);
448 SET_CUBIC_SPLINE_WEIGHTS(v, ty);
449 SET_CUBIC_SPLINE_WEIGHTS(w, tz);
450
451 /* Actual interpolation. */
452 const T *data = (const T *)info.data;
453 return ROW_TERM(0) + ROW_TERM(1) + ROW_TERM(2) + ROW_TERM(3);
454
455 #undef COL_TERM
456 #undef ROW_TERM
457 #undef DATA
458 }
459
460 static ccl_always_inline float4
interp_3dTextureInterpolator461 interp_3d(const TextureInfo &info, float x, float y, float z, InterpolationType interp)
462 {
463 if (UNLIKELY(!info.data))
464 return make_float4(0.0f, 0.0f, 0.0f, 0.0f);
465
466 switch ((interp == INTERPOLATION_NONE) ? info.interpolation : interp) {
467 case INTERPOLATION_CLOSEST:
468 return interp_3d_closest(info, x, y, z);
469 case INTERPOLATION_LINEAR:
470 return interp_3d_linear(info, x, y, z);
471 default:
472 return interp_3d_tricubic(info, x, y, z);
473 }
474 }
475 #undef SET_CUBIC_SPLINE_WEIGHTS
476 };
477
478 #ifdef WITH_NANOVDB
479 template<typename T> struct NanoVDBInterpolator {
readNanoVDBInterpolator480 static ccl_always_inline float4 read(float r)
481 {
482 return make_float4(r, r, r, 1.0f);
483 }
484
readNanoVDBInterpolator485 static ccl_always_inline float4 read(nanovdb::Vec3f r)
486 {
487 return make_float4(r[0], r[1], r[2], 1.0f);
488 }
489
490 static ccl_always_inline float4
interp_3dNanoVDBInterpolator491 interp_3d(const TextureInfo &info, float x, float y, float z, InterpolationType interp)
492 {
493 nanovdb::NanoGrid<T> *const grid = (nanovdb::NanoGrid<T> *)info.data;
494 const nanovdb::NanoRoot<T> &root = grid->tree().root();
495
496 const nanovdb::Coord off(root.bbox().min());
497 const nanovdb::Coord dim(root.bbox().dim());
498 const nanovdb::Vec3f xyz(off[0] + x * dim[0], off[1] + y * dim[1], off[2] + z * dim[2]);
499
500 typedef nanovdb::ReadAccessor<nanovdb::NanoRoot<T>> ReadAccessorT;
501 switch ((interp == INTERPOLATION_NONE) ? info.interpolation : interp) {
502 default:
503 case INTERPOLATION_LINEAR:
504 return read(nanovdb::SampleFromVoxels<ReadAccessorT, 1, false>(root)(xyz));
505 case INTERPOLATION_CLOSEST:
506 return read(nanovdb::SampleFromVoxels<ReadAccessorT, 0, false>(root)(xyz));
507 case INTERPOLATION_CUBIC:
508 return read(nanovdb::SampleFromVoxels<ReadAccessorT, 3, false>(root)(xyz));
509 }
510 }
511 };
512 #endif
513
kernel_tex_image_interp(KernelGlobals * kg,int id,float x,float y)514 ccl_device float4 kernel_tex_image_interp(KernelGlobals *kg, int id, float x, float y)
515 {
516 const TextureInfo &info = kernel_tex_fetch(__texture_info, id);
517
518 switch (info.data_type) {
519 case IMAGE_DATA_TYPE_HALF:
520 return TextureInterpolator<half>::interp(info, x, y);
521 case IMAGE_DATA_TYPE_BYTE:
522 return TextureInterpolator<uchar>::interp(info, x, y);
523 case IMAGE_DATA_TYPE_USHORT:
524 return TextureInterpolator<uint16_t>::interp(info, x, y);
525 case IMAGE_DATA_TYPE_FLOAT:
526 return TextureInterpolator<float>::interp(info, x, y);
527 case IMAGE_DATA_TYPE_HALF4:
528 return TextureInterpolator<half4>::interp(info, x, y);
529 case IMAGE_DATA_TYPE_BYTE4:
530 return TextureInterpolator<uchar4>::interp(info, x, y);
531 case IMAGE_DATA_TYPE_USHORT4:
532 return TextureInterpolator<ushort4>::interp(info, x, y);
533 case IMAGE_DATA_TYPE_FLOAT4:
534 return TextureInterpolator<float4>::interp(info, x, y);
535 default:
536 assert(0);
537 return make_float4(
538 TEX_IMAGE_MISSING_R, TEX_IMAGE_MISSING_G, TEX_IMAGE_MISSING_B, TEX_IMAGE_MISSING_A);
539 }
540 }
541
kernel_tex_image_interp_3d(KernelGlobals * kg,int id,float3 P,InterpolationType interp)542 ccl_device float4 kernel_tex_image_interp_3d(KernelGlobals *kg,
543 int id,
544 float3 P,
545 InterpolationType interp)
546 {
547 const TextureInfo &info = kernel_tex_fetch(__texture_info, id);
548
549 if (info.use_transform_3d) {
550 P = transform_point(&info.transform_3d, P);
551 }
552
553 switch (info.data_type) {
554 case IMAGE_DATA_TYPE_HALF:
555 return TextureInterpolator<half>::interp_3d(info, P.x, P.y, P.z, interp);
556 case IMAGE_DATA_TYPE_BYTE:
557 return TextureInterpolator<uchar>::interp_3d(info, P.x, P.y, P.z, interp);
558 case IMAGE_DATA_TYPE_USHORT:
559 return TextureInterpolator<uint16_t>::interp_3d(info, P.x, P.y, P.z, interp);
560 case IMAGE_DATA_TYPE_FLOAT:
561 return TextureInterpolator<float>::interp_3d(info, P.x, P.y, P.z, interp);
562 case IMAGE_DATA_TYPE_HALF4:
563 return TextureInterpolator<half4>::interp_3d(info, P.x, P.y, P.z, interp);
564 case IMAGE_DATA_TYPE_BYTE4:
565 return TextureInterpolator<uchar4>::interp_3d(info, P.x, P.y, P.z, interp);
566 case IMAGE_DATA_TYPE_USHORT4:
567 return TextureInterpolator<ushort4>::interp_3d(info, P.x, P.y, P.z, interp);
568 case IMAGE_DATA_TYPE_FLOAT4:
569 return TextureInterpolator<float4>::interp_3d(info, P.x, P.y, P.z, interp);
570 #ifdef WITH_NANOVDB
571 case IMAGE_DATA_TYPE_NANOVDB_FLOAT:
572 return NanoVDBInterpolator<float>::interp_3d(info, P.x, P.y, P.z, interp);
573 case IMAGE_DATA_TYPE_NANOVDB_FLOAT3:
574 return NanoVDBInterpolator<nanovdb::Vec3f>::interp_3d(info, P.x, P.y, P.z, interp);
575 #endif
576 default:
577 assert(0);
578 return make_float4(
579 TEX_IMAGE_MISSING_R, TEX_IMAGE_MISSING_G, TEX_IMAGE_MISSING_B, TEX_IMAGE_MISSING_A);
580 }
581 }
582
583 } /* Namespace. */
584
585 CCL_NAMESPACE_END
586
587 #endif // __KERNEL_CPU_IMAGE_H__
588