1 /*
2 OFX I/O utility functions.
3 Adds OpenColorIO functionality to any plugin.
4
5 Copyright (C) 2013-2018 INRIA
6 Author: Frederic Devernay <frederic.devernay@inria.fr>
7
8 Redistribution and use in source and binary forms, with or without modification,
9 are permitted provided that the following conditions are met:
10
11 Redistributions of source code must retain the above copyright notice, this
12 list of conditions and the following disclaimer.
13
14 Redistributions in binary form must reproduce the above copyright notice, this
15 list of conditions and the following disclaimer in the documentation and/or
16 other materials provided with the distribution.
17
18 Neither the name of the {organization} nor the names of its
19 contributors may be used to endorse or promote products derived from
20 this software without specific prior written permission.
21
22 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
23 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
24 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
25 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
26 ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
27 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
29 ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
31 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32
33 INRIA
34 Domaine de Voluceau
35 Rocquencourt - B.P. 105
36 78153 Le Chesnay Cedex - France
37
38 */
39
40 #ifndef IO_Utility_h
41 #define IO_Utility_h
42
43
44 #include <cmath>
45 #include <cassert>
46 #include <algorithm>
47 #include <string>
48 #include <functional>
49 #include <locale>
50
51 #include "ofxsImageEffect.h"
52
53 #define NAMESPACE_OFX_ENTER namespace OFX {
54 #define NAMESPACE_OFX_EXIT }
55
56 #define NAMESPACE_OFX_IO_ENTER namespace IO {
57 #define NAMESPACE_OFX_IO_EXIT }
58
59
60 NAMESPACE_OFX_ENTER
61 NAMESPACE_OFX_IO_ENTER
62
63 inline std::string
basename(std::string const & pathname)64 basename( std::string const& pathname )
65 {
66 #if defined(_WIN32) || defined(WIN64)
67 std::size_t found = pathname.find_last_of("/\\");
68 #else
69 std::size_t found = pathname.find_last_of("/");
70 #endif
71
72 return pathname.substr(found + 1);
73 }
74
75 inline std::string
dirname(std::string const & pathname)76 dirname( std::string const& pathname )
77 {
78 #if defined(_WIN32) || defined(WIN64)
79 std::size_t found = pathname.find_last_of("/\\");
80 #else
81 std::size_t found = pathname.find_last_of("/");
82 #endif
83
84 return pathname.substr(0, found);
85 }
86
87 inline std::string
extension(const std::string & filename)88 extension(const std::string& filename)
89 {
90 std::string::const_reverse_iterator pivot = std::find( filename.rbegin(), filename.rend(), '.' );
91 if ( pivot == filename.rend() ) {
92 return "";
93 }
94 std::string ext;
95 std::locale loc;
96 for (std::string::const_iterator it = pivot.base(); it != filename.end(); ++it) {
97 ext.append( 1, std::tolower(*it, loc) );
98 }
99
100 return ext;
101 }
102
103 /// numvals should be 256 for byte, 65536 for 16-bits, etc.
104 template<int numvals>
105 float
intToFloat(int value)106 intToFloat(int value)
107 {
108 return value / (float)(numvals - 1);
109 }
110
111 template<int numvals>
112 int
floatToInt(float value)113 floatToInt(float value)
114 {
115 if (value <= 0) {
116 return 0;
117 } else if (value >= 1.) {
118 return numvals - 1;
119 }
120
121 return (int)(value * (numvals - 1) + 0.5);
122 }
123
124 /**
125 * @brief Upscales the bounds assuming this rectangle is the Nth level of mipmap
126 **/
127 inline OfxRectI
upscalePowerOfTwo(const OfxRectI & r,unsigned int thisLevel)128 upscalePowerOfTwo(const OfxRectI& r,
129 unsigned int thisLevel)
130 {
131 if (thisLevel == 0) {
132 return r;
133 }
134 OfxRectI ret;
135 ret.x1 = r.x1 << thisLevel;
136 ret.x2 = r.x2 << thisLevel;
137 ret.y1 = r.y1 << thisLevel;
138 ret.y2 = r.y2 << thisLevel;
139
140 return ret;
141 }
142
143 /**
144 * @brief Upscales the bounds assuming this rectangle is the Nth level of mipmap
145 **/
146 inline OfxRectD
upscalePowerOfTwo(const OfxRectD & r,double thisLevel)147 upscalePowerOfTwo(const OfxRectD& r,
148 double thisLevel)
149 {
150 if (thisLevel == 0) {
151 return r;
152 }
153 OfxRectD ret;
154 ret.x1 = std::pow(r.x1, thisLevel);
155 ret.x2 = std::pow(r.x2, thisLevel);
156 ret.y1 = std::pow(r.y1, thisLevel);
157 ret.y2 = std::pow(r.y2, thisLevel);
158
159 return ret;
160 }
161
162 /**
163 * @brief Scales down the rectangle by the given power of 2
164 **/
165 inline OfxRectI
downscalePowerOfTwo(const OfxRectI & r,unsigned int thisLevel)166 downscalePowerOfTwo(const OfxRectI& r,
167 unsigned int thisLevel)
168 {
169 if (thisLevel == 0) {
170 return r;
171 }
172 OfxRectI ret;
173 assert(r.x1 % (1 << thisLevel) == 0 && r.x2 % (1 << thisLevel) == 0 && r.y1 % (1 << thisLevel) == 0 && r.y2 % (1 << thisLevel) == 0);
174 ret.x1 = r.x1 >> thisLevel;
175 ret.x2 = r.x2 >> thisLevel;
176 ret.y1 = r.y1 >> thisLevel;
177 ret.y2 = r.y2 >> thisLevel;
178
179 return ret;
180 }
181
182 inline bool
isRectNull(const OfxRectI & r)183 isRectNull(const OfxRectI& r)
184 {
185 return (r.x2 <= r.x1) || (r.y2 <= r.y1);
186 }
187
188 inline bool
intersect(const OfxRectI & r1,const OfxRectI & r2,OfxRectI * intersection)189 intersect(const OfxRectI& r1,
190 const OfxRectI& r2,
191 OfxRectI* intersection)
192 {
193 if ( isRectNull(r1) || isRectNull(r2) ) {
194 return false;
195 }
196
197 if ( (r1.x1 > r2.x2) || (r2.x1 > r1.x2) || (r1.y1 > r2.y2) || (r2.y1 > r1.y2) ) {
198 return false;
199 }
200
201 intersection->x1 = std::max(r1.x1, r2.x1);
202 intersection->x2 = std::min(r1.x2, r2.x2);
203 intersection->y1 = std::max(r1.y1, r2.y1);
204 intersection->y2 = std::min(r1.y2, r2.y2);
205
206 return true;
207 }
208
209 /*
210 test program for rounding integer to the next/previous pot:
211 #include <stdio.h>
212 int main()
213 {
214 int i;
215 int pot = 3;
216 int scale = 1 << pot;
217 int scalem1 = scale - 1;
218 for(i=-100; i<100; ++i)
219 {
220 printf("%d => %d,%d %d,%d\n", i, i & ~scalem1, i+scalem1 & ~scalem1, (i >> pot) << pot, ((i+scalem1)>>pot) << pot);
221 }
222 }
223 */
224 /**
225 * @brief round the rectangle by the given power of 2, and return the largest *enclosed* (inside) rectangle
226 **/
227 inline OfxRectI
roundPowerOfTwoLargestEnclosed(const OfxRectI & r,unsigned int thisLevel)228 roundPowerOfTwoLargestEnclosed(const OfxRectI& r,
229 unsigned int thisLevel)
230 {
231 if (thisLevel == 0) {
232 return r;
233 }
234 OfxRectI ret;
235 int pot = (1 << thisLevel);
236 int pot_minus1 = pot - 1;
237 ret.x1 = (r.x1 + pot_minus1) & ~pot_minus1;
238 ret.x2 = r.x2 & ~pot_minus1;
239 ret.y1 = (r.y1 + pot_minus1) & ~pot_minus1;
240 ret.y2 = r.y2 & ~pot_minus1;
241 // check that it's enclosed
242 assert(ret.x1 >= r.x1 && ret.x2 <= r.x2 && ret.y1 >= r.y1 && ret.y2 <= r.y2);
243
244 return ret;
245 }
246
247 /**
248 * @brief round the rectangle by the given power of 2, and return the smallest *enclosing* rectangle
249 **/
250 inline OfxRectI
roundPowerOfTwoSmallestEnclosing(const OfxRectI & r,unsigned int thisLevel)251 roundPowerOfTwoSmallestEnclosing(const OfxRectI& r,
252 unsigned int thisLevel)
253 {
254 if (thisLevel == 0) {
255 return r;
256 }
257 OfxRectI ret;
258 int pot = (1 << thisLevel);
259 int pot_minus1 = pot - 1;
260 ret.x1 = r.x1 & ~pot_minus1;
261 ret.x2 = (r.x2 + pot_minus1) & ~pot_minus1;
262 ret.y1 = r.y1 & ~pot_minus1;
263 ret.y2 = (r.y2 + pot_minus1) & ~pot_minus1;
264 // check that it's enclosing
265 assert(ret.x1 <= r.x1 && ret.x2 >= r.x2 && ret.y1 <= r.y1 && ret.y2 >= r.y2);
266
267 return ret;
268 }
269
270 /**
271 * @brief Scales down the rectangle by the given power of 2, and return the largest *enclosed* (inside) rectangle
272 **/
273 inline OfxRectI
downscalePowerOfTwoLargestEnclosed(const OfxRectI & r,unsigned int thisLevel)274 downscalePowerOfTwoLargestEnclosed(const OfxRectI& r,
275 unsigned int thisLevel)
276 {
277 if (thisLevel == 0) {
278 return r;
279 }
280 OfxRectI ret;
281 int pot = (1 << thisLevel);
282 int pot_minus1 = pot - 1;
283 ret.x1 = (r.x1 + pot_minus1) >> thisLevel;
284 ret.x2 = r.x2 >> thisLevel;
285 ret.y1 = (r.y1 + pot_minus1) >> thisLevel;
286 ret.y2 = r.y2 >> thisLevel;
287 // check that it's enclosed
288 assert(ret.x1 * pot >= r.x1 && ret.x2 * pot <= r.x2 && ret.y1 * pot >= r.y1 && ret.y2 * pot <= r.y2);
289
290 return ret;
291 }
292
293 /**
294 * @brief Scales down the rectangle by the given power of 2, and return the smallest *enclosing* rectangle
295 **/
296 inline OfxRectI
downscalePowerOfTwoSmallestEnclosing(const OfxRectI & r,unsigned int thisLevel)297 downscalePowerOfTwoSmallestEnclosing(const OfxRectI& r,
298 unsigned int thisLevel)
299 {
300 if (thisLevel == 0) {
301 return r;
302 }
303 OfxRectI ret;
304 int pot = (1 << thisLevel);
305 int pot_minus1 = pot - 1;
306 ret.x1 = r.x1 >> thisLevel;
307 ret.x2 = (r.x2 + pot_minus1) >> thisLevel;
308 ret.y1 = r.y1 >> thisLevel;
309 ret.y2 = (r.y2 + pot_minus1) >> thisLevel;
310 // check that it's enclosing
311 assert(ret.x1 * pot <= r.x1 && ret.x2 * pot >= r.x2 && ret.y1 * pot <= r.y1 && ret.y2 * pot >= r.y2);
312
313 return ret;
314 }
315
316 inline OfxRectI
nextRectLevel(const OfxRectI & r)317 nextRectLevel(const OfxRectI& r)
318 {
319 OfxRectI ret = r;
320
321 ret.x1 /= 2;
322 ret.y1 /= 2;
323 ret.x2 /= 2;
324 ret.y2 /= 2;
325
326 return ret;
327 }
328
329 inline double
getScaleFromMipMapLevel(unsigned int level)330 getScaleFromMipMapLevel(unsigned int level)
331 {
332 return 1. / (1 << level);
333 }
334
335 #ifndef M_LN2
336 #define M_LN2 0.693147180559945309417232121458176568 /* loge(2) */
337 #endif
338 inline unsigned int
getLevelFromScale(double s)339 getLevelFromScale(double s)
340 {
341 assert(0. < s && s <= 1.);
342 int retval = -(int)std::floor(std::log(s) / M_LN2 + 0.5);
343 assert(retval >= 0);
344
345 return retval;
346 }
347
348 /**
349 * @brief Helper class to make fast buffers that are ensured to be deallocated in a RAII style
350 **/
351 class RamBuffer
352 {
353 unsigned char* data;
354
355 public:
356
RamBuffer(std::size_t nBytes)357 RamBuffer(std::size_t nBytes)
358 : data(0)
359 {
360 data = (unsigned char*)malloc(nBytes);
361 }
362
getData()363 unsigned char* getData() const
364 {
365 return data;
366 }
367
~RamBuffer()368 ~RamBuffer()
369 {
370 if (data) {
371 free(data);
372 }
373 }
374 };
375
376 NAMESPACE_OFX_IO_EXIT
377 NAMESPACE_OFX_EXIT
378
379 #endif // ifndef IO_Utility_h
380