1 /*
2 * Copyright (C) 2004-2009 Andrew Mihal
3 * Copyright (C) 2009-2016 Christoph Spiel
4 *
5 * This file is part of Enblend.
6 *
7 * Enblend is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * Enblend is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with Enblend; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 */
21 #ifndef __COMMON_H__
22 #define __COMMON_H__
23
24 #ifdef HAVE_CONFIG_H
25 #include <config.h>
26 #endif
27
28 #include <algorithm>
29 #include <cassert>
30 #include <fstream>
31 #include <iomanip>
32 #include <limits>
33 #include <locale>
34 #include <map>
35 #include <stdexcept>
36 #include <string>
37 #include <vector>
38
39 #include <vigra/numerictraits.hxx>
40
41 #include "error_message.h"
42 #include "filenameparse.h"
43
44 #define NUMERIC_OPTION_DELIMITERS ";:/" //< numeric-option-delimiters ;:/
45 #define PATH_OPTION_DELIMITERS ",;:" //< path-option-delimiters ,;:
46 #define ASSIGNMENT_CHARACTERS "=" //< assignment-characters =
47
48 #define MASK_COMPRESSION "DEFLATE"
49
50 // IMPLEMENTATION NOTE: For 30 or more pyramid levels, the full width
51 // will just barely fit in a 32-bit integer. When this range is added
52 // to a bounding box it will certainly overflow the vigra::Diff2D.
53 #define MAX_PYRAMID_LEVELS 29 //< maximum-pyramid-levels 29
54
55 // Colors used in the optimizer visualization
56 #define VISUALIZE_RGB_COLOR_BLUE1 vigra::RGBValue<vigra::UInt8>( 0, 0, 255)
57 #define VISUALIZE_RGB_COLOR_BLUE2 vigra::RGBValue<vigra::UInt8>( 0, 0, 238)
58 #define VISUALIZE_RGB_COLOR_BLUE3 vigra::RGBValue<vigra::UInt8>( 0, 0, 205)
59 #define VISUALIZE_RGB_COLOR_BLUE4 vigra::RGBValue<vigra::UInt8>( 0, 0, 139)
60
61 #define VISUALIZE_RGB_COLOR_CYAN1 vigra::RGBValue<vigra::UInt8>( 0, 255, 255)
62 #define VISUALIZE_RGB_COLOR_CYAN2 vigra::RGBValue<vigra::UInt8>( 0, 238, 238)
63 #define VISUALIZE_RGB_COLOR_CYAN3 vigra::RGBValue<vigra::UInt8>( 0, 205, 205)
64 #define VISUALIZE_RGB_COLOR_CYAN4 vigra::RGBValue<vigra::UInt8>( 0, 139, 139)
65
66 #define VISUALIZE_RGB_COLOR_GRAY0 vigra::RGBValue<vigra::UInt8>( 0, 0, 0)
67 #define VISUALIZE_RGB_COLOR_GRAY63 vigra::RGBValue<vigra::UInt8>( 63, 63, 63)
68 #define VISUALIZE_RGB_COLOR_GRAY127 vigra::RGBValue<vigra::UInt8>(127, 127, 127)
69 #define VISUALIZE_RGB_COLOR_GRAY139 vigra::RGBValue<vigra::UInt8>(139, 139, 139)
70 #define VISUALIZE_RGB_COLOR_GRAY191 vigra::RGBValue<vigra::UInt8>(191, 191, 191)
71 #define VISUALIZE_RGB_COLOR_GRAY205 vigra::RGBValue<vigra::UInt8>(205, 205, 205)
72 #define VISUALIZE_RGB_COLOR_GRAY238 vigra::RGBValue<vigra::UInt8>(238, 238, 238)
73 #define VISUALIZE_RGB_COLOR_GRAY255 vigra::RGBValue<vigra::UInt8>(255, 255, 255)
74
75 #define VISUALIZE_RGB_COLOR_GREEN1 vigra::RGBValue<vigra::UInt8>( 0, 255, 0)
76 #define VISUALIZE_RGB_COLOR_GREEN2 vigra::RGBValue<vigra::UInt8>( 0, 238, 0)
77 #define VISUALIZE_RGB_COLOR_GREEN3 vigra::RGBValue<vigra::UInt8>( 0, 205, 0)
78 #define VISUALIZE_RGB_COLOR_GREEN4 vigra::RGBValue<vigra::UInt8>( 0, 139, 0)
79
80 #define VISUALIZE_RGB_COLOR_MAGENTA1 vigra::RGBValue<vigra::UInt8>(255, 0, 255)
81 #define VISUALIZE_RGB_COLOR_MAGENTA2 vigra::RGBValue<vigra::UInt8>(238, 0, 238)
82 #define VISUALIZE_RGB_COLOR_MAGENTA3 vigra::RGBValue<vigra::UInt8>(205, 0, 205)
83 #define VISUALIZE_RGB_COLOR_MAGENTA4 vigra::RGBValue<vigra::UInt8>(139, 0, 139)
84
85 #define VISUALIZE_RGB_COLOR_ORANGE1 vigra::RGBValue<vigra::UInt8>(255, 165, 0)
86 #define VISUALIZE_RGB_COLOR_ORANGE2 vigra::RGBValue<vigra::UInt8>(238, 154, 0)
87 #define VISUALIZE_RGB_COLOR_ORANGE3 vigra::RGBValue<vigra::UInt8>(205, 133, 0)
88 #define VISUALIZE_RGB_COLOR_ORANGE4 vigra::RGBValue<vigra::UInt8>(139, 90, 0)
89
90 #define VISUALIZE_RGB_COLOR_RED1 vigra::RGBValue<vigra::UInt8>(255, 0, 0)
91 #define VISUALIZE_RGB_COLOR_RED2 vigra::RGBValue<vigra::UInt8>(238, 0, 0)
92 #define VISUALIZE_RGB_COLOR_RED3 vigra::RGBValue<vigra::UInt8>(205, 0, 0)
93 #define VISUALIZE_RGB_COLOR_RED4 vigra::RGBValue<vigra::UInt8>(139, 0, 0)
94
95 #define VISUALIZE_RGB_COLOR_YELLOW1 vigra::RGBValue<vigra::UInt8>(255, 255, 0)
96 #define VISUALIZE_RGB_COLOR_YELLOW2 vigra::RGBValue<vigra::UInt8>(238, 238, 0)
97 #define VISUALIZE_RGB_COLOR_YELLOW3 vigra::RGBValue<vigra::UInt8>(205, 205, 0)
98 #define VISUALIZE_RGB_COLOR_YELLOW4 vigra::RGBValue<vigra::UInt8>(139, 139, 0)
99
100
101 // Different marker types offered by visualizePoint()
102 typedef enum
103 {
104 NO_MARKER,
105 DOT_MARKER,
106 PLUS_MARKER,
107 CROSS_MARKER,
108 HOLLOW_SQUARE_MARKER,
109 HOLLOW_DIAMOND_MARKER,
110 } marker_t;
111
112
113 //< visualize-movable-point light orange
114 #define VISUALIZE_MOVABLE_POINT VISUALIZE_RGB_COLOR_ORANGE1
115 //< mark-movable-point diamond
116 #define MARK_MOVABLE_POINT HOLLOW_DIAMOND_MARKER
117 //< visualize-frozen-point bright white
118 #define VISUALIZE_FROZEN_POINT VISUALIZE_RGB_COLOR_GRAY255
119 //< mark-frozen-point cross
120 #define MARK_FROZEN_POINT CROSS_MARKER
121 //< visualize-initial-path-color dark yellow
122 #define VISUALIZE_INITIAL_PATH VISUALIZE_RGB_COLOR_YELLOW4
123 //< visualize-short-path-value-color bright yellow
124 #define VISUALIZE_SHORT_PATH_VALUE VISUALIZE_RGB_COLOR_YELLOW1
125 //< visualize-first-vertex-value-color medium green
126 #define VISUALIZE_FIRST_VERTEX_VALUE VISUALIZE_RGB_COLOR_GREEN3
127 //< visualize-next-vertex-value-color light green
128 #define VISUALIZE_NEXT_VERTEX_VALUE VISUALIZE_RGB_COLOR_GREEN2
129 //< visualize-no-overlap-value-color dark red
130 #define VISUALIZE_NO_OVERLAP_VALUE VISUALIZE_RGB_COLOR_RED4
131 //< visualize-state-space-color dark blue
132 #define VISUALIZE_STATE_SPACE VISUALIZE_RGB_COLOR_BLUE3
133 //< visualize-state-space-inside-color bright cyan
134 #define VISUALIZE_STATE_SPACE_INSIDE VISUALIZE_RGB_COLOR_CYAN1
135 //< visualize-state-space-unconverged-color bright magenta
136 #define VISUALIZE_STATE_SPACE_UNCONVERGED VISUALIZE_RGB_COLOR_MAGENTA1
137
138
139 // For CIELAB, CIELUV, and CIECAM blending, Enblend and Enfuse
140 // transform the input images from their respective RGB color spaces
141 // to XYZ space (and for CIECAM then on to JCh). The following two
142 // #defines control the color transformation from and to XYZ space.
143 #define RENDERING_INTENT_FOR_BLENDING INTENT_PERCEPTUAL
144 #define TRANSFORMATION_FLAGS_FOR_BLENDING cmsFLAGS_NOCACHE
145
146
147 // Select our preferred type of image depending on what ./configure
148 // tells us.
149 #ifdef CACHE_IMAGES
150 #error "The ImageCache feature has been withdrawn. Reconfigure without ImageCache and re-build."
151 #define IMAGETYPE vigra_ext::CachedFileImage
152 #else
153 #define IMAGETYPE vigra::BasicImage
154 #endif
155
156
157 #ifdef WIN32
158 #define sleep(m_duration) Sleep(m_duration)
159 #endif
160
161 #if defined __MINGW32__ || defined __MINGW64__
162 #undef rand_r
163 #undef strtok_r
164 #endif
165
166 #define PENALIZE_DEPRECATED_OPTION(m_old_name, m_new_name) \
167 do { \
168 std::cerr << command << \
169 ": info: option \"" m_old_name "\" is deprecated; use \"" m_new_name "\" instead" << \
170 std::endl; \
171 sleep(1); \
172 } while (false)
173
174
175 #define lengthof(m_array) (sizeof(m_array) / sizeof(m_array[0]))
176
177
178 // Replacement for `assert(0)' and `assert(false)'.
179 class never_reached : public std::runtime_error
180 {
181 public:
182 never_reached() = delete;
never_reached(const std::string & a_message)183 explicit never_reached(const std::string& a_message) : std::runtime_error(a_message) {}
~never_reached()184 virtual ~never_reached() noexcept {}
185 };
186
187 #ifdef __GNUC__
188 #define NEVER_REACHED(m_message) \
189 do {__builtin_unreachable(); throw ::never_reached(m_message);} while (false)
190 #else
191 #define NEVER_REACHED(m_message) throw ::never_reached(m_message)
192 #endif
193
194
195 namespace enblend {
196
197 /** The different image overlap classifications. */
198 enum Overlap {NoOverlap, PartialOverlap, CompleteOverlap};
199
200
201 /** Symbolic expressions for the three different metrics that
202 * vigra::distanceTransform understands. */
203 typedef enum
204 {
205 ChessboardDistance, // 0
206 ManhattanDistance, // 1, L1 norm
207 EuclideanDistance // 2, L2 norm
208 } nearest_neighbor_metric_t;
209
210
211 /** Define our own reentrant, uniform pseudo-random number generator. */
212 inline int
rand_r(unsigned int * seed)213 rand_r(unsigned int* seed)
214 {
215 *seed = *seed * 1103515245U + 12345U;
216 return static_cast<int>(*seed % (static_cast<unsigned int>(RAND_MAX) + 1U));
217 }
218
219
220 /** Answer the square of the argument x. */
221 template <typename t>
222 inline t
square(t x)223 square(t x)
224 {
225 return x * x;
226 }
227
228
229 /** Test whether s starts with p. */
230 inline static bool
starts_with(const std::string & s,const std::string & p)231 starts_with(const std::string& s, const std::string& p)
232 {
233 return s.substr(0U, p.length()) == p;
234 }
235
236
237 /** Answer the string representation of the boolean b. */
238 std::string
stringOfBool(bool b)239 stringOfBool(bool b)
240 {
241 return b ? "true" : "false";
242 }
243
244
245 /** Answer whether we can open aFilename. */
246 bool
can_open_file(const std::string & aFilename)247 can_open_file(const std::string& aFilename)
248 {
249 errno = 0;
250 std::ifstream file(aFilename.c_str());
251 if (!file)
252 {
253 std::cerr << command <<
254 ": failed to open \"" << aFilename << "\": " <<
255 errorMessage(errno) << "\n";
256 return false;
257 }
258 else
259 {
260 errno = 0;
261 file.close();
262 if (file.fail())
263 {
264 std::cerr << command <<
265 ": info: problems when closing \"" << aFilename << "\": " <<
266 errorMessage(errno) << "\n";
267 }
268 return true;
269 }
270 }
271
272
273 /** Answer the VIGRA file type as determined by the extension of
274 * aFileName. */
275 std::string
getFileType(const std::string & aFileName)276 getFileType(const std::string& aFileName)
277 {
278 const std::string ext(to_upper_copy(aFileName.substr(aFileName.rfind(".") + 1U)));
279
280 if (ext == "JPG") return "JPEG";
281 else if (ext == "TIF") return "TIFF";
282 else if (ext == "VIF") return "VIFF";
283 else if (ext == "PBM" || ext == "PGM" || ext == "PPM") return "PNM";
284 else return ext;
285 }
286
287
288 /** Convert aWraparoundMode given as string to the internal
289 * representation as enum. */
290 boundary_t
wraparoundOfString(const char * aWraparoundMode)291 wraparoundOfString(const char* aWraparoundMode)
292 {
293 const std::string mode(to_upper_copy(std::string(aWraparoundMode)));
294
295 if (mode == "NONE" || mode == "OPEN") return OpenBoundaries;
296 else if (mode == "HORIZONTAL") return HorizontalStrip;
297 else if (mode == "VERTICAL") return VerticalStrip;
298 else if (mode == "BOTH" ||
299 mode == "HORIZONTAL+VERTICAL" ||
300 mode == "VERTICAL+HORIZONTAL") return DoubleStrip;
301 else return UnknownWrapAround;
302 }
303
304
305 /** Convert aBoundaryMode to its string representation. */
306 std::string
stringOfWraparound(boundary_t aBoundaryMode)307 stringOfWraparound(boundary_t aBoundaryMode)
308 {
309 switch (aBoundaryMode)
310 {
311 case OpenBoundaries:
312 return "none";
313 case HorizontalStrip:
314 return "horizontal";
315 case VerticalStrip:
316 return "vertical";
317 case DoubleStrip:
318 return "both";
319 default:
320 NEVER_REACHED("switch control expression \"aBoundaryMode\" out of range");
321 }
322 }
323
324
325 /** Convert a_string into a number.
326 *
327 * Perform two validating tests in the numerical result. These are,
328 * for example, tests for lower and upper boundaries. */
329 template <class NumericType, class Validator1, class Validator2>
330 NumericType
numberOfString(const char * a_string,Validator1 is_valid1,const std::string & invalid_message1,NumericType replacement_value1,Validator2 is_valid2,const std::string & invalid_message2,NumericType replacement_value2)331 numberOfString(const char* a_string, // string we want to convert into a number
332 Validator1 is_valid1, // 1st validator function
333 const std::string& invalid_message1, // error message for failed 1st validation
334 NumericType replacement_value1, // replacement return value on 1st failure
335 Validator2 is_valid2, // 2nd validator function
336 const std::string& invalid_message2, // error message for failed 2nd validation
337 NumericType replacement_value2) // replacement return value on 2nd failure
338 {
339 typedef std::numeric_limits<NumericType> traits;
340
341 char* tail;
342 long int long_int_value;
343 double double_value;
344 NumericType value;
345
346 errno = 0;
347 if (traits::is_exact)
348 {
349 long_int_value = strtol(a_string, &tail, 10);
350 }
351 else
352 {
353 double_value = strtod(a_string, &tail);
354 }
355
356 if (errno != 0)
357 {
358 std::cerr << command << ": "
359 << "illegal numeric format of \""
360 << a_string
361 << "\": "
362 << errorMessage(errno)
363 << std::endl;
364 exit(1);
365 }
366
367 if (*tail != 0)
368 {
369 if (strcmp(a_string, tail) == 0)
370 {
371 std::cerr << command << ": "
372 << "number is garbage; maybe the option before \"" << a_string
373 << "\" needs an argument"
374 << std::endl;
375 }
376 else
377 {
378 std::cerr << command << ": "
379 << "trailing garbage \"" << tail << "\" in \"" << a_string << "\""
380 << std::endl;
381 }
382 exit(1);
383 }
384
385 if (traits::is_exact)
386 {
387 if (traits::is_signed)
388 {
389 if (long_int_value < traits::min() || long_int_value > traits::max())
390 {
391 std::cerr << command << ": "
392 << "signed number x = " << long_int_value
393 << " out of range " << traits::min() << " <= x <= " << traits::max()
394 << std::endl;
395 exit(1);
396 }
397 else
398 {
399 value = static_cast<NumericType>(long_int_value);
400 }
401 }
402 else
403 {
404 if (long_int_value < 0L || long_int_value > traits::max())
405 {
406 std::cerr << command << ": "
407 << "unsigned number x = " << long_int_value
408 << " out of range 0 <= x <= " << traits::max()
409 << std::endl;
410 exit(1);
411 }
412 else
413 {
414 value = static_cast<NumericType>(long_int_value);
415 }
416 }
417 }
418 else
419 {
420 value = static_cast<NumericType>(double_value);
421 }
422
423 if (is_valid1(value))
424 {
425 if (is_valid2(value))
426 {
427 return value;
428 }
429 else
430 {
431 std::cerr << command << ": warning: " << invalid_message2 << std::endl;
432 return replacement_value2;
433 }
434 }
435 else
436 {
437 std::cerr << command << ": warning: " << invalid_message1 << std::endl;
438 return replacement_value1;
439 }
440 }
441
442
443 template <class NumericType, class Validator1, class Validator2>
444 NumericType
numberOfString(const std::string & a_string,Validator1 is_valid1,const std::string & invalid_message1,NumericType replacement_value1,Validator2 is_valid2,const std::string & invalid_message2,NumericType replacement_value2)445 numberOfString(const std::string& a_string, // string we want to convert into a number
446 Validator1 is_valid1, // 1st validator function
447 const std::string& invalid_message1, // error message for failed 1st validation
448 NumericType replacement_value1, // replacement return value on 1st failure
449 Validator2 is_valid2, // 2nd validator function
450 const std::string& invalid_message2, // error message for failed 2nd validation
451 NumericType replacement_value2) // replacement return value on 2nd failure
452 {
453 numberOfString(&a_string[0],
454 is_valid1,
455 invalid_message1, replacement_value1,
456 is_valid2, invalid_message2, replacement_value2);
457 }
458
459
460 /** Convert a_string into a number.
461 */
462 template <class NumericType, class Validator>
463 NumericType
numberOfString(const char * a_string,Validator is_valid,const std::string & invalid_message,NumericType replacement_value)464 numberOfString(const char* a_string, // string we want to convert into a number
465 Validator is_valid, // validator function
466 const std::string& invalid_message, // error message for failed validation
467 NumericType replacement_value) // replacement return value on failure
468 {
469 return numberOfString(a_string,
470 is_valid, invalid_message, replacement_value,
471 [](NumericType) {return true;},
472 "<never reached>", NumericType());
473 }
474
475
476 template <class NumericType, class Validator>
477 NumericType
numberOfString(const std::string & a_string,Validator is_valid,const std::string & invalid_message,NumericType replacement_value)478 numberOfString(const std::string& a_string, // string we want to convert into a number
479 Validator is_valid, // validator function
480 const std::string& invalid_message, // error message for failed validation
481 NumericType replacement_value) // replacement return value on failure
482 {
483 return numberOfString(&a_string[0], is_valid, invalid_message, replacement_value);
484 }
485
486
487 inline bool
isFloatingPoint(const std::string & aPixelType)488 isFloatingPoint(const std::string& aPixelType)
489 {
490 return aPixelType == "FLOAT" || aPixelType == "DOUBLE";
491 }
492
493
494 /** Convert an anOutputDepth to a "pixel type" string understood by
495 * VIGRA. */
496 std::string
outputPixelTypeOfString(const char * anOutputDepth)497 outputPixelTypeOfString(const char* anOutputDepth)
498 {
499 typedef std::map<std::string, std::string> Str2StrMapType;
500
501 Str2StrMapType depthMap = {
502 {"INT16", "INT16"},
503 {"INT32", "INT32"},
504
505 {"8", "UINT8"},
506 {"16", "UINT16"},
507 {"32", "UINT32"},
508 {"UINT8", "UINT8"},
509 {"UINT16", "UINT16"},
510 {"UINT32", "UINT32"},
511
512 {"DOUBLE", "DOUBLE"},
513 {"FLOAT", "FLOAT"},
514 {"R32", "FLOAT"},
515 {"R64", "DOUBLE"},
516 {"REAL32", "FLOAT"},
517 {"REAL64", "DOUBLE"}
518 };
519
520 const std::string output_depth(to_upper_copy(std::string(anOutputDepth)));
521 Str2StrMapType::const_iterator p = depthMap.find(output_depth);
522 if (p == depthMap.end())
523 {
524 throw std::invalid_argument(std::string("unknown output depth \"") + anOutputDepth + "\"");
525 }
526 else
527 {
528 return p->second;
529 }
530 }
531
532
533 /** Answer the best pixel type of an image given aFileType with
534 * respect to aPixelType. This is the type with the largest range. */
535 std::string
bestPixelType(const std::string & aFileType,const std::string & aPixelType)536 bestPixelType(const std::string& aFileType, const std::string& aPixelType)
537 {
538 if (aFileType == "BMP" || aFileType == "JPEG" || aFileType == "RAS")
539 {
540 return "UINT8";
541 }
542 else if (aFileType == "PNG" &&
543 (aPixelType == "INT32" || aPixelType == "UINT32" ||
544 aPixelType == "FLOAT" || aPixelType == "DOUBLE"))
545 {
546 return "UINT16";
547 }
548 else if (aFileType == "EXR")
549 {
550 return "FLOAT";
551 }
552 else
553 {
554 return aPixelType;
555 }
556 }
557
558
559 typedef std::pair<double, double> range_t;
560
561
562 /** Answer the maximum range of values aPixelType can represent. */
563 range_t
rangeOfPixelType(const std::string & aPixelType)564 rangeOfPixelType(const std::string& aPixelType)
565 {
566 typedef std::map<std::string, range_t> Str2PairMapType;
567
568 Str2PairMapType rangeMap = {
569 {"INT8", std::make_pair(vigra::NumericTraits<vigra::Int8>::min(),
570 vigra::NumericTraits<vigra::Int8>::max())},
571 {"INT16", std::make_pair(vigra::NumericTraits<vigra::Int16>::min(),
572 vigra::NumericTraits<vigra::Int16>::max())},
573 {"INT32", std::make_pair(vigra::NumericTraits<vigra::Int32>::min(),
574 vigra::NumericTraits<vigra::Int32>::max())},
575
576 {"UINT8", std::make_pair(0.0, vigra::NumericTraits<vigra::UInt8>::max())},
577 {"UINT16", std::make_pair(0.0, vigra::NumericTraits<vigra::UInt16>::max())},
578 {"UINT32", std::make_pair(0.0, vigra::NumericTraits<vigra::UInt32>::max())},
579
580 {"FLOAT", std::make_pair(0.0, 1.0)},
581 {"DOUBLE", std::make_pair(0.0, 1.0)}
582 };
583
584 assert(!aPixelType.empty());
585 Str2PairMapType::const_iterator r = rangeMap.find(aPixelType);
586 if (r == rangeMap.end())
587 {
588 throw std::invalid_argument(std::string("unknown pixel type \"") + aPixelType + "\"");
589 }
590 else
591 {
592 return r->second;
593 }
594 }
595
596
597 /** Answer whether aPixelType defines a range that is so larges that
598 * it includes both aRange and anotherRange. */
599 bool
includesBothRanges(const std::string & aPixelType,const range_t & aRange,const range_t & anotherRange)600 includesBothRanges(const std::string& aPixelType,
601 const range_t& aRange,
602 const range_t& anotherRange)
603 {
604 const range_t range = rangeOfPixelType(aPixelType);
605
606 return (aRange.first >= range.first && aRange.second <= range.second &&
607 anotherRange.first >= range.first && anotherRange.second <= range.second);
608 }
609
610
611 /** Answer the smallest pixel type that is larger or equal to both
612 * aPixelType and anotherPixelType. */
613 std::string
maxPixelType(const std::string & aPixelType,const std::string & anotherPixelType)614 maxPixelType(const std::string& aPixelType, const std::string& anotherPixelType)
615 {
616 const range_t range1 = rangeOfPixelType(aPixelType);
617 const range_t range2 = rangeOfPixelType(anotherPixelType);
618
619 if (aPixelType == "DOUBLE" || anotherPixelType == "DOUBLE") {
620 return "DOUBLE";
621 } else if (aPixelType == "FLOAT" || anotherPixelType == "FLOAT") {
622 return "FLOAT";
623 } else if (range1.first <= range2.first && range1.second >= range2.second) {
624 return aPixelType; // first includes second
625 } else if (range2.first <= range1.first && range2.second >= range1.second) {
626 return anotherPixelType; // second includes first
627 } else {
628 // Types are different: look for the smallest containing type
629 typedef std::vector<std::string> string_array;
630 typedef string_array::const_iterator string_array_ci;
631
632 if (range1.first < 0 || range2.first < 0) {
633 const string_array types = {"INT8", "INT16", "INT32"};
634 for (string_array_ci i = types.begin(); i != types.end(); ++i) {
635 if (includesBothRanges(*i, range1, range2)) {
636 return *i;
637 }
638 }
639 return "INT32";
640 } else {
641 const string_array types = {"UINT8", "UINT16", "UINT32"};
642 for (string_array_ci i = types.begin(); i != types.end(); ++i) {
643 if (includesBothRanges(*i, range1, range2)) {
644 return *i;
645 }
646 }
647 return "UINT32";
648 }
649 }
650 }
651
652
653 /** Answer the sign of x.
654 */
655
656 template <typename T>
657 inline int
sign(T x)658 sign(T x)
659 {
660 return x > T() ? 1 : (x < T() ? -1 : 0);
661 }
662
663
664 /** Compute the integral logarithm of n to the base 10. We do not
665 * need to take special care of the case n == 0 for our purposes. */
666 inline unsigned
ilog10(unsigned n)667 ilog10(unsigned n)
668 {
669 return n <= 9 ? 0 : 1 + ilog10(n / 10);
670 }
671
672
673 /** Expand aTemplate filling the variable parts with anInputFilename,
674 * anOutputFilename, and aNumber depending on the conversion
675 * specifiers in aTemplate.
676 *
677 * Conversion Specifiers - lowercase characters refer to
678 * anInputFilename whereas uppercase ones refer to anOutputFilename:
679 * %% A single '%'-sign
680 * %i aNumber unaltered
681 * %n successor of aNumber
682 * %p aFilename unaltered
683 * %d directory part of aFilename
684 * %b non-directory part (aka basename) of aFilename
685 * %f basename of aFilename without extension
686 * %e extension of aFilename (including the leading dot)
687 * All other characters in aTemplate are passed through literally.
688 *
689 * The "%i" and "%n" conversions honor a flag which is either
690 * '0' pad with zeros (default) or
691 * PUNCT i.e. any punctuation character to pad with
692 * and a width specification. If no width is requested, the
693 * width is computed based on aNumberOfImages.
694 *
695 * For example
696 * expandFilenameTemplate("mask-%04n.tif", 2, "foobar.jpg", 9)
697 * evaluates to
698 * mask-0009.tif
699 */
700 std::string
expandFilenameTemplate(const std::string & aTemplate,unsigned aNumberOfImages,const std::string & anInputFilename,const std::string & anOutputFilename,unsigned aNumber)701 expandFilenameTemplate(const std::string& aTemplate,
702 unsigned aNumberOfImages,
703 const std::string& anInputFilename,
704 const std::string& anOutputFilename,
705 unsigned aNumber)
706 {
707 std::string result;
708
709 for (std::string::const_iterator c = aTemplate.begin();
710 c != aTemplate.end();
711 ++c)
712 {
713 if (*c == '%')
714 {
715 ++c;
716 if (c == aTemplate.end())
717 {
718 result.push_back(*c);
719 }
720 else
721 {
722 char pad = 0;
723 while (c != aTemplate.end() && (*c == '0' || ispunct(*c)))
724 {
725 pad = *c;
726 ++c;
727 }
728
729 std::string width;
730 while (c != aTemplate.end() && isdigit(*c))
731 {
732 width.push_back(*c);
733 ++c;
734 }
735
736 if (c != aTemplate.end())
737 {
738 switch (*c)
739 {
740 case '%':
741 result.push_back(*c);
742 break;
743 case 'n':
744 ++aNumber;
745 ++aNumberOfImages;
746 BOOST_FALLTHROUGH;
747 case 'i':
748 {
749 std::ostringstream oss;
750 oss <<
751 std::setw(width.empty() ? 1U + ilog10(aNumberOfImages - 1U) : atoi(width.c_str())) <<
752 std::setfill(pad == 0 ? '0' : pad) <<
753 aNumber;
754 result.append(oss.str());
755 break;
756 }
757 case 'P':
758 result.append(anOutputFilename);
759 break;
760 case 'p':
761 result.append(anInputFilename);
762 break;
763 case 'D':
764 result.append(extractDirname(anOutputFilename));
765 break;
766 case 'd':
767 result.append(extractDirname(anInputFilename));
768 break;
769 case 'B':
770 result.append(extractBasename(anOutputFilename));
771 break;
772 case 'b':
773 result.append(extractBasename(anInputFilename));
774 break;
775 case 'F':
776 result.append(extractFilename(anOutputFilename));
777 break;
778 case 'f':
779 result.append(extractFilename(anInputFilename));
780 break;
781 case 'E':
782 result.append(extractExtension(anOutputFilename));
783 break;
784 case 'e':
785 result.append(extractExtension(anInputFilename));
786 break;
787 default:
788 std::cerr <<
789 command <<
790 ": warning: ignoring unknown variable character ";
791 if (isprint(*c))
792 {
793 std::cerr << "'" << *c << "'";
794 }
795 else
796 {
797 std::cerr << "0x" << std::hex << *c;
798 }
799 std::cerr << " in\n"
800 << command
801 << ": warning: filename template \""
802 << aTemplate
803 << "\""
804 << std::endl;
805 } // switch (*c)
806 }
807 }
808 }
809 else
810 {
811 result.push_back(*c);
812 }
813 }
814
815 return result;
816 }
817
818
819 /** Answer a phrase that describes a layer in an image consisting of
820 * multiple layers. If the image has got only one layer, we avoid to
821 * confuse the user and answer an empty string. */
822 inline std::string
optional_layer_name(unsigned layer_number,unsigned layer_total)823 optional_layer_name(unsigned layer_number, unsigned layer_total)
824 {
825 if (layer_total <= 1U)
826 {
827 return std::string();
828 }
829 else
830 {
831 std::ostringstream oss;
832 oss << ", layer " << layer_number << "/" << layer_total;
833 return oss.str();
834 }
835 }
836
837
838 template <class predicate>
839 inline static void
trim_if(std::string & a_string,predicate a_predicate)840 trim_if(std::string& a_string, predicate a_predicate)
841 {
842 auto negated_predicate =
843 std::bind(std::logical_not<bool>(), std::bind(a_predicate, std::placeholders::_1));
844 std::string::iterator begin = std::find_if(a_string.begin(), a_string.end(), negated_predicate);
845
846 if (begin == a_string.end())
847 {
848 a_string.clear();
849 }
850 else
851 {
852 std::string::reverse_iterator reverse_end =
853 std::find_if(a_string.rbegin(), a_string.rend(), negated_predicate);
854
855 a_string.assign(begin, reverse_end.base());
856 }
857 }
858
859
860 inline std::string
profileInfo(cmsHPROFILE profile,cmsInfoType info)861 profileInfo(cmsHPROFILE profile, cmsInfoType info)
862 {
863 const size_t size = cmsGetProfileInfoASCII(profile, info, cmsNoLanguage, cmsNoCountry, nullptr, 0);
864 std::string information(size, '\000');
865 cmsGetProfileInfoASCII(profile, info, cmsNoLanguage, cmsNoCountry, &information[0], size);
866 trim_if(information, std::bind(std::less_equal<char>(), std::placeholders::_1, '\040'));
867
868 return information;
869 }
870
871
872 inline std::string
profileDescription(cmsHPROFILE profile)873 profileDescription(cmsHPROFILE profile)
874 {
875 return profileInfo(profile, cmsInfoDescription);
876 }
877
878
879 inline std::string
profileName(cmsHPROFILE profile)880 profileName(cmsHPROFILE profile)
881 {
882 return profileInfo(profile, cmsInfoModel);
883 }
884
885
886 inline unsigned
profileChannels(cmsHPROFILE profile)887 profileChannels(cmsHPROFILE profile)
888 {
889 const cmsColorSpaceSignature signature = cmsGetColorSpace(profile);
890 const unsigned number_of_channels = cmsChannelsOf(signature);
891 assert(number_of_channels == 1U || number_of_channels == 3U);
892 return number_of_channels;
893 }
894 } // namespace enblend
895
896
897 #ifndef HAVE_STRTOK_R
898 char*
strtok_r(char * str,const char * delim,char ** save_ptr)899 strtok_r(char* str, const char* delim, char** save_ptr)
900 {
901 char *s = str ? str : *save_ptr;
902
903 if (s)
904 {
905 while (*s != 0 && strchr(delim, (int) *s))
906 {
907 s++;
908 }
909
910 if (*s)
911 {
912 char *token = s;
913
914 while (*s != 0 && !strchr(delim, (int) *s))
915 {
916 s++;
917 }
918 if (*s)
919 {
920 *s = 0;
921 s++;
922 }
923 *save_ptr = s;
924
925 return token;
926 }
927 else
928 {
929 return nullptr;
930 }
931 }
932 else
933 {
934 return nullptr;
935 }
936 }
937 #endif
938
939 #endif /* __COMMON_H__ */
940
941 // Local Variables:
942 // mode: c++
943 // End:
944