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