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 /// \file
6 /// Implementation of ImageBufAlgo algorithms that do math on
7 /// single pixels at a time.
8 
9 #include <cmath>
10 #include <iostream>
11 #include <limits>
12 
13 #include <OpenImageIO/dassert.h>
14 #include <OpenImageIO/deepdata.h>
15 #include <OpenImageIO/imagebuf.h>
16 #include <OpenImageIO/imagebufalgo.h>
17 #include <OpenImageIO/imagebufalgo_util.h>
18 #include <OpenImageIO/simd.h>
19 
20 #include "imageio_pvt.h"
21 
22 
23 OIIO_NAMESPACE_BEGIN
24 
25 
26 template<class Rtype, class Atype, class Btype>
27 static bool
mul_impl(ImageBuf & R,const ImageBuf & A,const ImageBuf & B,ROI roi,int nthreads)28 mul_impl(ImageBuf& R, const ImageBuf& A, const ImageBuf& B, ROI roi,
29          int nthreads)
30 {
31     ImageBufAlgo::parallel_image(roi, nthreads, [&](ROI roi) {
32         ImageBuf::Iterator<Rtype> r(R, roi);
33         ImageBuf::ConstIterator<Atype> a(A, roi);
34         ImageBuf::ConstIterator<Btype> b(B, roi);
35         for (; !r.done(); ++r, ++a, ++b)
36             for (int c = roi.chbegin; c < roi.chend; ++c)
37                 r[c] = a[c] * b[c];
38     });
39     return true;
40 }
41 
42 
43 
44 template<class Rtype, class Atype>
45 static bool
mul_impl(ImageBuf & R,const ImageBuf & A,cspan<float> b,ROI roi,int nthreads)46 mul_impl(ImageBuf& R, const ImageBuf& A, cspan<float> b, ROI roi, int nthreads)
47 {
48     ImageBufAlgo::parallel_image(roi, nthreads, [&](ROI roi) {
49         ImageBuf::ConstIterator<Atype> a(A, roi);
50         for (ImageBuf::Iterator<Rtype> r(R, roi); !r.done(); ++r, ++a)
51             for (int c = roi.chbegin; c < roi.chend; ++c)
52                 r[c] = a[c] * b[c];
53     });
54     return true;
55 }
56 
57 
58 
59 static bool
mul_impl_deep(ImageBuf & R,const ImageBuf & A,cspan<float> b,ROI roi,int nthreads)60 mul_impl_deep(ImageBuf& R, const ImageBuf& A, cspan<float> b, ROI roi,
61               int nthreads)
62 {
63     ImageBufAlgo::parallel_image(roi, nthreads, [&](ROI roi) {
64         // Deep case
65         cspan<TypeDesc> channeltypes(R.deepdata()->all_channeltypes());
66         ImageBuf::Iterator<float> r(R, roi);
67         ImageBuf::ConstIterator<float> a(A, roi);
68         for (; !r.done(); ++r, ++a) {
69             for (int samp = 0, samples = r.deep_samples(); samp < samples;
70                  ++samp) {
71                 for (int c = roi.chbegin; c < roi.chend; ++c) {
72                     if (channeltypes[c].basetype == TypeDesc::UINT32)
73                         r.set_deep_value(c, samp, a.deep_value_uint(c, samp));
74                     else
75                         r.set_deep_value(c, samp, a.deep_value(c, samp) * b[c]);
76                 }
77             }
78         }
79     });
80     return true;
81 }
82 
83 
84 
85 bool
mul(ImageBuf & dst,Image_or_Const A_,Image_or_Const B_,ROI roi,int nthreads)86 ImageBufAlgo::mul(ImageBuf& dst, Image_or_Const A_, Image_or_Const B_, ROI roi,
87                   int nthreads)
88 {
89     pvt::LoggedTimer logtime("IBA::mul");
90     if (A_.is_img() && B_.is_img()) {
91         const ImageBuf &A(A_.img()), &B(B_.img());
92         if (!IBAprep(roi, &dst, &A, &B, IBAprep_CLAMP_MUTUAL_NCHANNELS))
93             return false;
94         bool ok;
95         OIIO_DISPATCH_COMMON_TYPES3(ok, "mul", mul_impl, dst.spec().format,
96                                     A.spec().format, B.spec().format, dst, A, B,
97                                     roi, nthreads);
98         return ok;
99     }
100     if (A_.is_val() && B_.is_img())  // canonicalize to A_img, B_val
101         A_.swap(B_);
102     if (A_.is_img() && B_.is_val()) {
103         const ImageBuf& A(A_.img());
104         cspan<float> b = B_.val();
105         if (!IBAprep(roi, &dst, &A,
106                      IBAprep_CLAMP_MUTUAL_NCHANNELS | IBAprep_SUPPORT_DEEP))
107             return false;
108         IBA_FIX_PERCHAN_LEN_DEF(b, A.nchannels());
109         if (dst.deep()) {
110             // While still serial, set up all the sample counts
111             dst.deepdata()->set_all_samples(A.deepdata()->all_samples());
112             return mul_impl_deep(dst, A, b, roi, nthreads);
113         }
114         bool ok;
115         OIIO_DISPATCH_COMMON_TYPES2(ok, "mul", mul_impl, dst.spec().format,
116                                     A.spec().format, dst, A, b, roi, nthreads);
117         return ok;
118     }
119     // Remaining cases: error
120     dst.errorf("ImageBufAlgo::mul(): at least one argument must be an image");
121     return false;
122 }
123 
124 
125 
126 ImageBuf
mul(Image_or_Const A,Image_or_Const B,ROI roi,int nthreads)127 ImageBufAlgo::mul(Image_or_Const A, Image_or_Const B, ROI roi, int nthreads)
128 {
129     ImageBuf result;
130     bool ok = mul(result, A, B, roi, nthreads);
131     if (!ok && !result.has_error())
132         result.errorf("ImageBufAlgo::mul() error");
133     return result;
134 }
135 
136 
137 
138 template<class Rtype, class Atype, class Btype>
139 static bool
div_impl(ImageBuf & R,const ImageBuf & A,const ImageBuf & B,ROI roi,int nthreads)140 div_impl(ImageBuf& R, const ImageBuf& A, const ImageBuf& B, ROI roi,
141          int nthreads)
142 {
143     ImageBufAlgo::parallel_image(roi, nthreads, [&](ROI roi) {
144         ImageBuf::Iterator<Rtype> r(R, roi);
145         ImageBuf::ConstIterator<Atype> a(A, roi);
146         ImageBuf::ConstIterator<Btype> b(B, roi);
147         for (; !r.done(); ++r, ++a, ++b)
148             for (int c = roi.chbegin; c < roi.chend; ++c) {
149                 float v = b[c];
150                 r[c]    = (v == 0.0f) ? 0.0f : (a[c] / v);
151             }
152     });
153     return true;
154 }
155 
156 
157 
158 bool
div(ImageBuf & dst,Image_or_Const A_,Image_or_Const B_,ROI roi,int nthreads)159 ImageBufAlgo::div(ImageBuf& dst, Image_or_Const A_, Image_or_Const B_, ROI roi,
160                   int nthreads)
161 {
162     pvt::LoggedTimer logtime("IBA::div");
163     if (A_.is_img() && B_.is_img()) {
164         const ImageBuf &A(A_.img()), &B(B_.img());
165         if (!IBAprep(roi, &dst, &A, &B, IBAprep_CLAMP_MUTUAL_NCHANNELS))
166             return false;
167         bool ok;
168         OIIO_DISPATCH_COMMON_TYPES3(ok, "div", div_impl, dst.spec().format,
169                                     A.spec().format, B.spec().format, dst, A, B,
170                                     roi, nthreads);
171         return ok;
172     }
173     if (A_.is_val() && B_.is_img())  // canonicalize to A_img, B_val
174         A_.swap(B_);
175     if (A_.is_img() && B_.is_val()) {
176         const ImageBuf& A(A_.img());
177         cspan<float> b = B_.val();
178         if (!IBAprep(roi, &dst, &A,
179                      IBAprep_CLAMP_MUTUAL_NCHANNELS | IBAprep_SUPPORT_DEEP))
180             return false;
181 
182         IBA_FIX_PERCHAN_LEN_DEF(b, dst.nchannels());
183         int nc      = dst.nchannels();
184         float* binv = OIIO_ALLOCA(float, nc);
185         for (int c = 0; c < nc; ++c)
186             binv[c] = (b[c] == 0.0f) ? 0.0f : 1.0f / b[c];
187         b = cspan<float>(binv, nc);  // re-wrap
188 
189         if (dst.deep()) {
190             // While still serial, set up all the sample counts
191             dst.deepdata()->set_all_samples(A.deepdata()->all_samples());
192             return mul_impl_deep(dst, A, b, roi, nthreads);
193         }
194         bool ok;
195         OIIO_DISPATCH_COMMON_TYPES2(ok, "div", mul_impl, dst.spec().format,
196                                     A.spec().format, dst, A, b, roi, nthreads);
197         return ok;
198     }
199     // Remaining cases: error
200     dst.errorf("ImageBufAlgo::div(): at least one argument must be an image");
201     return false;
202 }
203 
204 
205 
206 ImageBuf
div(Image_or_Const A,Image_or_Const B,ROI roi,int nthreads)207 ImageBufAlgo::div(Image_or_Const A, Image_or_Const B, ROI roi, int nthreads)
208 {
209     ImageBuf result;
210     bool ok = div(result, A, B, roi, nthreads);
211     if (!ok && !result.has_error())
212         result.errorf("ImageBufAlgo::div() error");
213     return result;
214 }
215 
216 
217 
218 OIIO_NAMESPACE_END
219