1 // Copyright 2008-present Contributors to the OpenImageIO project.
2 // SPDX-License-Identifier: BSD-3-Clause
3 // https://github.com/OpenImageIO/oiio/blob/master/LICENSE.md
4
5 // clang-format off
6
7 #pragma once
8
9 #include <functional>
10
11 #include <OpenImageIO/parallel.h>
12 #include <OpenImageIO/imagebufalgo.h>
13
14
15
16 OIIO_NAMESPACE_BEGIN
17
18 using std::bind;
19 using std::ref;
20 using std::cref;
21 using namespace std::placeholders;
22 using std::placeholders::_1;
23
24
25 namespace ImageBufAlgo {
26
27
28
29 /// Helper template for generalized multithreading for image processing
30 /// functions. Some function/functor f is applied to every pixel the
31 /// region of interest roi, dividing the region into multiple threads if
32 /// threads != 1. Note that threads == 0 indicates that the number of
33 /// threads should be as set by the global OIIO "threads" attribute.
34 ///
35 /// The optional splitdir determines along which axis the split will be
36 /// made. The default is Split_Y (vertical splits), which generally seems
37 /// the fastest (due to cache layout issues?), but perhaps there are
38 /// algorithms where it's better to split in X, Z, or along the longest
39 /// axis.
40 ///
41 /// Most image operations will require additional arguments, including
42 /// additional input and output images or other parameters. The
43 /// parallel_image template can still be used by employing the
44 /// std::bind. For example, suppose you have an image operation defined as:
45 /// void my_image_op (ImageBuf &out, const ImageBuf &in,
46 /// float scale, ROI roi);
47 /// Then you can parallelize it as follows:
48 /// ImageBuf R, A; // result, input
49 /// ROI roi = get_roi (R.spec());
50 /// parallel_image (bind(my_image_op,ref(R), cref(A),3.14,_1), roi);
51 inline void
parallel_image(ROI roi,parallel_image_options opt,std::function<void (ROI)> f)52 parallel_image (ROI roi, parallel_image_options opt,
53 std::function<void(ROI)> f)
54 {
55 opt.resolve ();
56 // Try not to assign a thread less than 16k pixels, or it's not worth
57 // the thread startup/teardown cost.
58 opt.maxthreads = std::min (opt.maxthreads, 1 + int(roi.npixels() / opt.minitems));
59 if (opt.singlethread()) {
60 // Just one thread, or a small image region, or if recursive use of
61 // parallel_image is disallowed: use this thread only
62 f (roi);
63 return;
64 }
65
66 // If splitdir was not explicit, find the longest edge.
67 SplitDir splitdir = opt.splitdir;
68 if (splitdir == Split_Biggest)
69 splitdir = roi.width() > roi.height() ? Split_X : Split_Y;
70
71 int64_t xchunk = 0, ychunk = 0;
72 if (splitdir == Split_Y) {
73 xchunk = roi.width();
74 // ychunk = std::max (64, minitems/xchunk);
75 } else if (splitdir == Split_X) {
76 ychunk = roi.height();
77 // ychunk = std::max (64, minitems/xchunk);
78 } else if (splitdir == Split_Tile) {
79 int64_t n = std::min<imagesize_t>(opt.minitems, roi.npixels());
80 xchunk = ychunk = std::max (1, int(std::sqrt(n))/4);
81 } else {
82 xchunk = ychunk = std::max (int64_t(1), int64_t(std::sqrt(opt.maxthreads))/2);
83 }
84
85 auto task = [&](int /*id*/, int64_t xbegin, int64_t xend,
86 int64_t ybegin, int64_t yend) {
87 f (ROI (xbegin, xend, ybegin, yend, roi.zbegin, roi.zend,
88 roi.chbegin, roi.chend));
89 };
90 parallel_for_chunked_2D (roi.xbegin, roi.xend, xchunk,
91 roi.ybegin, roi.yend, ychunk, task, opt);
92 }
93
94
95 inline void
parallel_image(ROI roi,std::function<void (ROI)> f)96 parallel_image (ROI roi, std::function<void(ROI)> f)
97 {
98 parallel_image (roi, parallel_image_options(), f);
99 }
100
101
102
103 // DEPRECATED(1.8) -- eventually enable the OIIO_DEPRECATION
104 template <class Func>
105 // OIIO_DEPRECATED("switch to new parallel_image (1.8)")
106 void
107 parallel_image (Func f, ROI roi, int nthreads=0, SplitDir splitdir=Split_Y)
108 {
109 parallel_image (roi, parallel_image_options (nthreads, splitdir), f);
110 }
111
112
113
114 /// Common preparation for IBA functions: Given an ROI (which may or may not
115 /// be the default ROI::All()), destination image (which may or may not yet
116 /// be allocated), and optional input images, adjust roi if necessary and
117 /// allocate pixels for dst if necessary. If dst is already initialized, it
118 /// will keep its "full" (aka display) window, otherwise its full/display
119 /// window will be set to the union of A's and B's full/display windows. If
120 /// dst is uninitialized and force_spec is not NULL, use *force_spec as
121 /// dst's new spec rather than using A's. Also, if A or B inputs are
122 /// specified but not initialized or broken, it's an error so return false.
123 /// If all is ok, return true. Some additional checks and behaviors may be
124 /// specified by the 'prepflags', which is a bit field defined by
125 /// IBAprep_flags.
126 bool OIIO_API IBAprep (ROI &roi, ImageBuf *dst, const ImageBuf *A=NULL,
127 const ImageBuf *B=NULL, const ImageBuf *C=NULL,
128 ImageSpec *force_spec=NULL, int prepflags=0);
129 inline bool IBAprep (ROI &roi, ImageBuf *dst, const ImageBuf *A,
130 const ImageBuf *B, ImageSpec *force_spec,
131 int prepflags=0) {
132 return IBAprep (roi, dst, A, B, NULL, force_spec, prepflags);
133 }
IBAprep(ROI & roi,ImageBuf * dst,const ImageBuf * A,const ImageBuf * B,int prepflags)134 inline bool IBAprep (ROI &roi, ImageBuf *dst,
135 const ImageBuf *A, const ImageBuf *B, int prepflags) {
136 return IBAprep (roi, dst, A, B, NULL, NULL, prepflags);
137 }
IBAprep(ROI & roi,ImageBuf * dst,const ImageBuf * A,int prepflags)138 inline bool IBAprep (ROI &roi, ImageBuf *dst,
139 const ImageBuf *A, int prepflags) {
140 return IBAprep (roi, dst, A, NULL, NULL, NULL, prepflags);
141 }
142
143 enum IBAprep_flags {
144 IBAprep_DEFAULT = 0,
145 IBAprep_REQUIRE_ALPHA = 1<<0,
146 IBAprep_REQUIRE_Z = 1<<1,
147 IBAprep_REQUIRE_SAME_NCHANNELS = 1<<2,
148 IBAprep_NO_COPY_ROI_FULL = 1<<3, // Don't copy the src's roi_full
149 IBAprep_NO_SUPPORT_VOLUME = 1<<4, // Don't know how to do volumes
150 IBAprep_NO_COPY_METADATA = 1<<8, // N.B. default copies all metadata
151 IBAprep_COPY_ALL_METADATA = 1<<9, // Even unsafe things
152 IBAprep_CLAMP_MUTUAL_NCHANNELS = 1<<10, // Clamp roi.chend to min of inputs
153 IBAprep_SUPPORT_DEEP = 1<<11, // Operation allows deep images
154 IBAprep_DEEP_MIXED = 1<<12, // Allow deep & non-deep combinations
155 IBAprep_DST_FLOAT_PIXELS = 1<<13, // If dst is uninit, make it float
156 IBAprep_MINIMIZE_NCHANNELS = 1<<14, // Multi-inputs get min(nchannels)
157 IBAprep_REQUIRE_MATCHING_CHANNELS = 1<<15, // Channel names must match
158 IBAprep_MERGE_METADATA = 1 << 16, // Merge all inputs' metadata
159 };
160
161
162
163 // DEPRECATED(2.3): Prefer TypeDesc::basetype_merge().
164 TypeDesc::BASETYPE OIIO_API type_merge (TypeDesc::BASETYPE a, TypeDesc::BASETYPE b);
165
166 // DEPRECATED(2.3): Prefer TypeDesc::basetype_merge().
type_merge(TypeDesc a,TypeDesc b)167 inline TypeDesc type_merge (TypeDesc a, TypeDesc b) {
168 return TypeDesc::basetype_merge(a, b);
169 }
170
171 // DEPRECATED(2.3): Prefer TypeDesc::basetype_merge().
type_merge(TypeDesc a,TypeDesc b,TypeDesc c)172 inline TypeDesc type_merge (TypeDesc a, TypeDesc b, TypeDesc c)
173 {
174 return TypeDesc::basetype_merge(TypeDesc::basetype_merge(a,b), c);
175 }
176
177
178
179 // Macro to call a type-specialzed version func<type>(R,...)
180 #define OIIO_DISPATCH_TYPES(ret,name,func,type,R,...) \
181 switch (type.basetype) { \
182 case TypeDesc::FLOAT : \
183 ret = func<float> (R, __VA_ARGS__); break; \
184 case TypeDesc::UINT8 : \
185 ret = func<unsigned char> (R, __VA_ARGS__); break; \
186 case TypeDesc::HALF : \
187 ret = func<half> (R, __VA_ARGS__); break; \
188 case TypeDesc::UINT16: \
189 ret = func<unsigned short> (R, __VA_ARGS__); break; \
190 case TypeDesc::INT8 : \
191 ret = func<char> (R, __VA_ARGS__); break; \
192 case TypeDesc::INT16 : \
193 ret = func<short> (R, __VA_ARGS__); break; \
194 case TypeDesc::UINT : \
195 ret = func<unsigned int> (R, __VA_ARGS__); break; \
196 case TypeDesc::INT : \
197 ret = func<int> (R, __VA_ARGS__); break; \
198 case TypeDesc::DOUBLE: \
199 ret = func<double> (R, __VA_ARGS__); break; \
200 default: \
201 (R).errorf("%s: Unsupported pixel data format '%s'", name, type); \
202 ret = false; \
203 }
204
205 // Helper, do not call from the outside world.
206 #define OIIO_DISPATCH_TYPES2_HELP(ret,name,func,Rtype,Atype,R,...) \
207 switch (Atype.basetype) { \
208 case TypeDesc::FLOAT : \
209 ret = func<Rtype,float> (R, __VA_ARGS__); break; \
210 case TypeDesc::UINT8 : \
211 ret = func<Rtype,unsigned char> (R, __VA_ARGS__); break; \
212 case TypeDesc::HALF : \
213 ret = func<Rtype,half> (R, __VA_ARGS__); break; \
214 case TypeDesc::UINT16: \
215 ret = func<Rtype,unsigned short> (R, __VA_ARGS__); break; \
216 case TypeDesc::INT8 : \
217 ret = func<Rtype,char> (R, __VA_ARGS__); break; \
218 case TypeDesc::INT16 : \
219 ret = func<Rtype,short> (R, __VA_ARGS__); break; \
220 case TypeDesc::UINT : \
221 ret = func<Rtype,unsigned int> (R, __VA_ARGS__); break; \
222 case TypeDesc::INT : \
223 ret = func<Rtype,int> (R, __VA_ARGS__); break; \
224 case TypeDesc::DOUBLE : \
225 ret = func<Rtype,double> (R, __VA_ARGS__); break; \
226 default: \
227 (R).errorf("%s: Unsupported pixel data format '%s'", name, Atype); \
228 ret = false; \
229 }
230
231 // Macro to call a type-specialzed version func<Rtype,Atype>(R,...).
232 #define OIIO_DISPATCH_TYPES2(ret,name,func,Rtype,Atype,R,...) \
233 switch (Rtype.basetype) { \
234 case TypeDesc::FLOAT : \
235 OIIO_DISPATCH_TYPES2_HELP(ret,name,func,float,Atype,R,__VA_ARGS__); \
236 break; \
237 case TypeDesc::UINT8 : \
238 OIIO_DISPATCH_TYPES2_HELP(ret,name,func,unsigned char,Atype,R,__VA_ARGS__); \
239 break; \
240 case TypeDesc::HALF : \
241 OIIO_DISPATCH_TYPES2_HELP(ret,name,func,half,Atype,R,__VA_ARGS__); \
242 break; \
243 case TypeDesc::UINT16: \
244 OIIO_DISPATCH_TYPES2_HELP(ret,name,func,unsigned short,Atype,R,__VA_ARGS__); \
245 break; \
246 case TypeDesc::INT8: \
247 OIIO_DISPATCH_TYPES2_HELP(ret,name,func,char,Atype,R,__VA_ARGS__); \
248 break; \
249 case TypeDesc::INT16: \
250 OIIO_DISPATCH_TYPES2_HELP(ret,name,func,short,Atype,R,__VA_ARGS__); \
251 break; \
252 case TypeDesc::UINT: \
253 OIIO_DISPATCH_TYPES2_HELP(ret,name,func,unsigned int,Atype,R,__VA_ARGS__); \
254 break; \
255 case TypeDesc::INT: \
256 OIIO_DISPATCH_TYPES2_HELP(ret,name,func,int,Atype,R,__VA_ARGS__); \
257 break; \
258 case TypeDesc::DOUBLE: \
259 OIIO_DISPATCH_TYPES2_HELP(ret,name,func,double,Atype,R,__VA_ARGS__);\
260 break; \
261 default: \
262 (R).errorf("%s: Unsupported pixel data format '%s'", name, Rtype); \
263 ret = false; \
264 }
265
266
267 // Macro to call a type-specialzed version func<type>(R,...) for
268 // the most common types, will auto-convert the rest to float.
269 #define OIIO_DISPATCH_COMMON_TYPES(ret,name,func,type,R,...) \
270 switch (type.basetype) { \
271 case TypeDesc::FLOAT : \
272 ret = func<float> (R, __VA_ARGS__); break; \
273 case TypeDesc::UINT8 : \
274 ret = func<unsigned char> (R, __VA_ARGS__); break; \
275 case TypeDesc::HALF : \
276 ret = func<half> (R, __VA_ARGS__); break; \
277 case TypeDesc::UINT16: \
278 ret = func<unsigned short> (R, __VA_ARGS__); break; \
279 default: { \
280 /* other types: punt and convert to float, then copy back */ \
281 ImageBuf Rtmp; \
282 if ((R).initialized()) \
283 Rtmp.copy (R, TypeDesc::FLOAT); \
284 ret = func<float> (Rtmp, __VA_ARGS__); \
285 if (ret) \
286 (R).copy (Rtmp); \
287 else \
288 (R).errorf("%s", Rtmp.geterror()); \
289 } \
290 }
291
292 // Helper, do not call from the outside world.
293 #define OIIO_DISPATCH_COMMON_TYPES2_HELP(ret,name,func,Rtype,Atype,R,A,...) \
294 switch (Atype.basetype) { \
295 case TypeDesc::FLOAT : \
296 ret = func<Rtype,float> (R, A, __VA_ARGS__); break; \
297 case TypeDesc::UINT8 : \
298 ret = func<Rtype,unsigned char> (R, A, __VA_ARGS__); break; \
299 case TypeDesc::HALF : \
300 ret = func<Rtype,half> (R, A, __VA_ARGS__); break; \
301 case TypeDesc::UINT16: \
302 ret = func<Rtype,unsigned short> (R, A, __VA_ARGS__); break; \
303 default: { \
304 /* other types: punt and convert to float, then copy back */ \
305 ImageBuf Atmp; \
306 Atmp.copy (A, TypeDesc::FLOAT); \
307 ret = func<Rtype,float> (R, Atmp, __VA_ARGS__); \
308 } \
309 }
310
311 // Macro to call a type-specialzed version func<Rtype,Atype>(R,A,...) for
312 // the most common types, and even for uncommon types when src and dst types
313 // are identical. It will auto-convert remaining rare cases to float.
314 #define OIIO_DISPATCH_COMMON_TYPES2(ret,name,func,Rtype,Atype,R,A,...) \
315 if (Rtype == Atype) { \
316 /* All data types, when Rtype == Atype */ \
317 switch (Atype.basetype) { \
318 case TypeDesc::FLOAT : \
319 ret = func<float,float> (R, A, __VA_ARGS__); break; \
320 case TypeDesc::UINT8 : \
321 ret = func<uint8_t,uint8_t> (R, A, __VA_ARGS__); break; \
322 case TypeDesc::UINT16: \
323 ret = func<uint16_t,uint16_t> (R, A, __VA_ARGS__); break; \
324 case TypeDesc::HALF : \
325 ret = func<half,half> (R, A, __VA_ARGS__); break; \
326 case TypeDesc::INT8 : \
327 ret = func<char,char> (R, A, __VA_ARGS__); break; \
328 case TypeDesc::INT16 : \
329 ret = func<short,short> (R, A, __VA_ARGS__); break; \
330 case TypeDesc::UINT : \
331 ret = func<uint32_t,uint32_t> (R, A, __VA_ARGS__); break; \
332 case TypeDesc::INT : \
333 ret = func<int,int> (R, A, __VA_ARGS__); break; \
334 case TypeDesc::DOUBLE : \
335 ret = func<double,double> (R, A, __VA_ARGS__); break; \
336 default: \
337 (R).errorf("%s: Unsupported pixel data format '%s'", name, Atype); \
338 ret = false; \
339 } \
340 } else { \
341 /* When Rtype != Atype, handle the common pairs */ \
342 switch (Rtype.basetype) { \
343 case TypeDesc::FLOAT : \
344 OIIO_DISPATCH_COMMON_TYPES2_HELP(ret,name,func,float,Atype,R,A,__VA_ARGS__); \
345 break; \
346 case TypeDesc::UINT8 : \
347 OIIO_DISPATCH_COMMON_TYPES2_HELP(ret,name,func,uint8_t,Atype,R,A,__VA_ARGS__); \
348 break; \
349 case TypeDesc::HALF : \
350 OIIO_DISPATCH_COMMON_TYPES2_HELP(ret,name,func,half,Atype,R,A,__VA_ARGS__); \
351 break; \
352 case TypeDesc::UINT16: \
353 OIIO_DISPATCH_COMMON_TYPES2_HELP(ret,name,func,uint16_t,Atype,R,A,__VA_ARGS__); \
354 break; \
355 default: { \
356 /* other combinations: convert to float, then copy back */ \
357 ImageBuf Rtmp; \
358 if ((R).initialized()) \
359 Rtmp.copy (R, TypeDesc::FLOAT); \
360 OIIO_DISPATCH_COMMON_TYPES2_HELP(ret,name,func,float,Atype,Rtmp,A,__VA_ARGS__); \
361 if (ret) \
362 (R).copy (Rtmp); \
363 else \
364 (R).errorf("%s", Rtmp.geterror()); \
365 } \
366 } \
367 }
368
369
370 // Macro to call a type-specialzed version func<Rtype,Atype>(R,A,...) for
371 // the most common types. It will auto-convert other cases to/from float.
372 // This is the case for when we don't actually write to the read-only R image.
373 #define OIIO_DISPATCH_COMMON_TYPES2_CONST(ret,name,func,Rtype,Atype,R,A,...) \
374 switch (Rtype.basetype) { \
375 case TypeDesc::FLOAT : \
376 OIIO_DISPATCH_COMMON_TYPES2_HELP(ret,name,func,float,Atype,R,A,__VA_ARGS__); \
377 break; \
378 case TypeDesc::UINT8 : \
379 OIIO_DISPATCH_COMMON_TYPES2_HELP(ret,name,func,unsigned char,Atype,R,A,__VA_ARGS__); \
380 break; \
381 case TypeDesc::HALF : \
382 OIIO_DISPATCH_COMMON_TYPES2_HELP(ret,name,func,half,Atype,R,A,__VA_ARGS__); \
383 break; \
384 case TypeDesc::UINT16: \
385 OIIO_DISPATCH_COMMON_TYPES2_HELP(ret,name,func,unsigned short,Atype,R,A,__VA_ARGS__); \
386 break; \
387 default: { \
388 /* other types: punt and convert to float, then copy back */ \
389 ImageBuf Rtmp; \
390 if ((R).initialized()) \
391 Rtmp.copy (R, TypeDesc::FLOAT); \
392 OIIO_DISPATCH_COMMON_TYPES2_HELP(ret,name,func,float,Atype,Rtmp,A,__VA_ARGS__); \
393 } }
394
395
396 // Helper, do not call from the outside world.
397 #define OIIO_DISPATCH_COMMON_TYPES3_HELP2(ret,name,func,Rtype,Atype,Btype,R,A,B,...) \
398 switch (Rtype.basetype) { \
399 case TypeDesc::FLOAT : \
400 ret = func<float,Atype,Btype> (R,A,B,__VA_ARGS__); break; \
401 case TypeDesc::UINT8 : \
402 ret = func<unsigned char,Atype,Btype> (R,A,B,__VA_ARGS__); break; \
403 case TypeDesc::HALF : \
404 ret = func<half,Atype,Btype> (R,A,B,__VA_ARGS__); break; \
405 case TypeDesc::UINT16: \
406 ret = func<unsigned short,Atype,Btype> (R,A,B,__VA_ARGS__); break; \
407 default: { \
408 /* other types: punt and convert to float, then copy back */ \
409 ImageBuf Rtmp; \
410 if ((R).initialized()) \
411 Rtmp.copy (R, TypeDesc::FLOAT); \
412 ret = func<float,Atype,Btype> (R,A,B,__VA_ARGS__); \
413 if (ret) \
414 (R).copy (Rtmp); \
415 else \
416 (R).errorf("%s", Rtmp.geterror()); \
417 } \
418 }
419
420 // Helper, do not call from the outside world.
421 #define OIIO_DISPATCH_COMMON_TYPES3_HELP(ret,name,func,Rtype,Atype,Btype,R,A,B,...) \
422 switch (Btype.basetype) { \
423 case TypeDesc::FLOAT : \
424 OIIO_DISPATCH_COMMON_TYPES3_HELP2(ret,name,func,Rtype,Atype,float,R,A,B,__VA_ARGS__); \
425 break; \
426 case TypeDesc::UINT8 : \
427 OIIO_DISPATCH_COMMON_TYPES3_HELP2(ret,name,func,Rtype,Atype,unsigned char,R,A,B,__VA_ARGS__); \
428 break; \
429 case TypeDesc::HALF : \
430 OIIO_DISPATCH_COMMON_TYPES3_HELP2(ret,name,func,Rtype,Atype,half,R,A,B,__VA_ARGS__); \
431 break; \
432 case TypeDesc::UINT16 : \
433 OIIO_DISPATCH_COMMON_TYPES3_HELP2(ret,name,func,Rtype,Atype,unsigned short,R,A,B,__VA_ARGS__); \
434 break; \
435 default: { \
436 /* other types: punt and convert to float */ \
437 ImageBuf Btmp; \
438 Btmp.copy (B, TypeDesc::FLOAT); \
439 OIIO_DISPATCH_COMMON_TYPES3_HELP2(ret,name,func,Rtype,Atype,float,R,A,Btmp,__VA_ARGS__); \
440 } \
441 }
442
443 // Macro to call a type-specialzed version func<Rtype,Atype,Btype>(R,A,B,...)
444 // the most common types, and for all types when all three images have
445 // the same type. Remaining rare cases will auto-convert to float.
446 #define OIIO_DISPATCH_COMMON_TYPES3(ret,name,func,Rtype,Atype,Btype,R,A,B,...) \
447 if (Rtype == Atype && Rtype == Btype) { \
448 /* All data types, when Rtype == Atype */ \
449 switch (Atype.basetype) { \
450 case TypeDesc::FLOAT : \
451 ret = func<float,float,float> (R, A, B, __VA_ARGS__); break;\
452 case TypeDesc::UINT8 : \
453 ret = func<uint8_t,uint8_t,uint8_t> (R, A, B, __VA_ARGS__); break; \
454 case TypeDesc::UINT16: \
455 ret = func<uint16_t,uint16_t,uint16_t> (R, A, B, __VA_ARGS__); break; \
456 case TypeDesc::HALF : \
457 ret = func<half,half,half> (R, A, B, __VA_ARGS__); break; \
458 case TypeDesc::INT8 : \
459 ret = func<char,char,char> (R, A, B, __VA_ARGS__); break; \
460 case TypeDesc::INT16 : \
461 ret = func<int16_t,int16_t,int16_t> (R, A, B, __VA_ARGS__); break; \
462 case TypeDesc::UINT : \
463 ret = func<uint32_t,uint32_t,uint32_t> (R, A, B, __VA_ARGS__); break; \
464 case TypeDesc::INT : \
465 ret = func<int,int,int> (R, A, B, __VA_ARGS__); break; \
466 case TypeDesc::DOUBLE : \
467 ret = func<double,double,double> (R, A, B, __VA_ARGS__); break; \
468 default: \
469 (R).errorf("%s: Unsupported pixel data format '%s'", name, Atype); \
470 ret = false; \
471 } \
472 } else { \
473 switch (Atype.basetype) { \
474 case TypeDesc::FLOAT : \
475 OIIO_DISPATCH_COMMON_TYPES3_HELP(ret,name,func,Rtype,float,Btype,R,A,B,__VA_ARGS__); \
476 break; \
477 case TypeDesc::UINT8 : \
478 OIIO_DISPATCH_COMMON_TYPES3_HELP(ret,name,func,Rtype,unsigned char,Btype,R,A,B,__VA_ARGS__); \
479 break; \
480 case TypeDesc::HALF : \
481 OIIO_DISPATCH_COMMON_TYPES3_HELP(ret,name,func,Rtype,half,Btype,R,A,B,__VA_ARGS__); \
482 break; \
483 case TypeDesc::UINT16: \
484 OIIO_DISPATCH_COMMON_TYPES3_HELP(ret,name,func,Rtype,unsigned short,Btype,R,A,B,__VA_ARGS__); \
485 break; \
486 default: \
487 /* other types: punt and convert to float */ \
488 ImageBuf Atmp; \
489 Atmp.copy (A, TypeDesc::FLOAT); \
490 OIIO_DISPATCH_COMMON_TYPES3_HELP(ret,name,func,Rtype,float,Btype,R,Atmp,B,__VA_ARGS__); \
491 } \
492 }
493
494
495 // Utility: for span av, if it had fewer elements than len, alloca a new
496 // copy that's the right length. Use the `missing` value for missing entries
497 // (one or more supplied, but not all), and `zdef` default to use if there
498 // were no entries at all. This is used in many IBA functions that take
499 // constant per-channel values.
500 #define IBA_FIX_PERCHAN_LEN(av,len,missing,zdef) \
501 if (av.size() < len) { \
502 int nc = len; \
503 float *vals = OIIO_ALLOCA(float, nc); \
504 for (int i = 0; i < nc; ++i) \
505 vals[i] = i < av.size() ? av[i] : (i ? vals[i-1] : zdef); \
506 av = cspan<float>(vals, nc); \
507 }
508
509 // Default IBA_FIX_PERCHAN_LEN, with zdef=0.0 and missing = the last value
510 // that was supplied.
511 #define IBA_FIX_PERCHAN_LEN_DEF(av,len) \
512 IBA_FIX_PERCHAN_LEN (av, len, 0.0f, av.size() ? av.back() : 0.0f);
513
514
515 } // end namespace ImageBufAlgo
516
517
518 OIIO_NAMESPACE_END
519