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