Third party ImageResampler library added. 26/116326/5
authorVictor Cebollada <v.cebollada@samsung.com>
Thu, 23 Feb 2017 10:50:24 +0000 (10:50 +0000)
committerVictor Cebollada <v.cebollada@samsung.com>
Mon, 27 Feb 2017 07:30:09 +0000 (07:30 +0000)
Change-Id: I64db36734d4e12c1334df38f66f4dcbec0e02409
Signed-off-by: Victor Cebollada <v.cebollada@samsung.com>
build/tizen/adaptor-uv/Makefile.am
build/tizen/adaptor/Makefile.am
third-party/image-resampler/file.list [new file with mode: 0644]
third-party/image-resampler/resampler.cpp [new file with mode: 0644]
third-party/image-resampler/resampler.h [new file with mode: 0644]

index 4e8e1d7..7747336 100644 (file)
@@ -101,6 +101,9 @@ include ../../../adaptors/devel-api/file.list
 static_libraries_libunibreak_src_dir = ../../../text/dali/internal/libunibreak
 include ../../../text/dali/internal/libunibreak/file.list
 
+static_libraries_image_resampler_src_dir = ../../../third-party/image-resampler
+include ../../../third-party/image-resampler/file.list
+
 # Package doc
 package_doxy_dir = ../../../doc
 include ../../../doc/file.list
@@ -284,7 +287,8 @@ libdali_adaptor_uv_la_SOURCES = \
                      $(devel_api_src_files) \
                      $(public_api_src_files) \
                      $(adaptor_internal_src_files) \
-                     $(input_event_handler_src_files)
+                     $(input_event_handler_src_files) \
+                     $(image_resampler_src_files)
 
 
 if ENABLE_NETWORK_LOGGING
@@ -309,7 +313,8 @@ libdali_adaptor_uv_la_includes = \
                       -I../../../adaptors/base/interfaces \
                       -I../../../adaptors/ \
                       -I../../../text \
-                      -I../../../text/dali/internal/libunibreak
+                      -I../../../text/dali/internal/libunibreak \
+                      -I../../../third-party/image-resampler
 
 if WAYLAND
 libdali_adaptor_uv_la_includes += -I../../../adaptors/integration-api/wayland
index 93afbca..e0319e9 100644 (file)
@@ -110,6 +110,9 @@ include ../../../text/dali/internal/libunibreak/file.list
 static_libraries_glyphy_src_dir = ../../../text/dali/internal/glyphy
 include ../../../text/dali/internal/glyphy/file.list
 
+static_libraries_image_resampler_src_dir = ../../../third-party/image-resampler
+include ../../../third-party/image-resampler/file.list
+
 # Package doc
 package_doxy_dir = ../../../doc
 include ../../../doc/file.list
@@ -304,7 +307,8 @@ libdali_adaptor_la_SOURCES = \
                      $(devel_api_src_files) \
                      $(public_api_src_files) \
                      $(adaptor_internal_src_files) \
-                     $(input_event_handler_src_files)
+                     $(input_event_handler_src_files) \
+                     $(image_resampler_src_files)
 
 
 if ENABLE_NETWORK_LOGGING
@@ -329,7 +333,8 @@ libdali_adaptor_la_includes = \
                       -I../../../adaptors/base/interfaces \
                       -I../../../adaptors/ \
                       -I../../../text \
-                      -I../../../text/dali/internal/libunibreak
+                      -I../../../text/dali/internal/libunibreak \
+                      -I../../../third-party/image-resampler
 
 if WAYLAND
 libdali_adaptor_la_includes += -I../../../adaptors/integration-api/wayland
diff --git a/third-party/image-resampler/file.list b/third-party/image-resampler/file.list
new file mode 100644 (file)
index 0000000..9f412fa
--- /dev/null
@@ -0,0 +1,4 @@
+# Add local source files here
+
+image_resampler_src_files = \
+   $(static_libraries_image_resampler_src_dir)/resampler.cpp
diff --git a/third-party/image-resampler/resampler.cpp b/third-party/image-resampler/resampler.cpp
new file mode 100644 (file)
index 0000000..9a40f24
--- /dev/null
@@ -0,0 +1,1215 @@
+// resampler.cpp, Separable filtering image rescaler v2.21, Rich Geldreich - richgel99@gmail.com
+// See unlicense at the bottom of resampler.h, or at http://unlicense.org/
+//
+// Feb. 1996: Creation, losely based on a heavily bugfixed version of Schumacher's resampler in Graphics Gems 3.
+// Oct. 2000: Ported to C++, tweaks.
+// May 2001: Continous to discrete mapping, box filter tweaks.
+// March 9, 2002: Kaiser filter grabbed from Jonathan Blow's GD magazine mipmap sample code.
+// Sept. 8, 2002: Comments cleaned up a bit.
+// Dec. 31, 2008: v2.2: Bit more cleanup, released as public domain.
+// June 4, 2012: v2.21: Switched to unlicense.org, integrated GCC fixes supplied by Peter Nagy <petern@crytek.com>, Anteru at anteru.net, and clay@coge.net,
+// added Codeblocks project (for testing with MinGW and GCC), VS2008 static code analysis pass.
+#include <stdlib.h>
+#include <math.h>
+#include <float.h>
+#include <assert.h>
+#include <string.h>
+#include "resampler.h"
+
+#define resampler_assert assert
+
+static inline int resampler_range_check(int v, int h) { (void)h; resampler_assert((v >= 0) && (v < h)); return v; }
+
+#ifndef max
+   #define max(a,b) (((a) > (b)) ? (a) : (b))
+#endif
+
+#ifndef min
+   #define min(a,b) (((a) < (b)) ? (a) : (b))
+#endif
+
+#ifndef TRUE
+   #define TRUE (1)
+#endif
+
+#ifndef FALSE
+   #define FALSE (0)
+#endif
+
+#define RESAMPLER_DEBUG 0
+
+#define M_PI 3.14159265358979323846
+
+// Float to int cast with truncation.
+static inline int cast_to_int(Resample_Real i)
+{
+   return (int)i;
+}
+
+// (x mod y) with special handling for negative x values.
+static inline int posmod(int x, int y)
+{
+   if (x >= 0)
+      return (x % y);
+   else
+   {
+      int m = (-x) % y;
+
+      if (m != 0)
+         m = y - m;
+
+      return (m);
+   }
+}
+
+// To add your own filter, insert the new function below and update the filter table.
+// There is no need to make the filter function particularly fast, because it's
+// only called during initializing to create the X and Y axis contributor tables.
+
+#define BOX_FILTER_SUPPORT (0.5f)
+static Resample_Real box_filter(Resample_Real t)    /* pulse/Fourier window */
+{
+   // make_clist() calls the filter function with t inverted (pos = left, neg = right)
+   if ((t >= -0.5f) && (t < 0.5f))
+      return 1.0f;
+   else
+      return 0.0f;
+}
+
+#define TENT_FILTER_SUPPORT (1.0f)
+static Resample_Real tent_filter(Resample_Real t)   /* box (*) box, bilinear/triangle */
+{
+   if (t < 0.0f)
+      t = -t;
+
+   if (t < 1.0f)
+      return 1.0f - t;
+   else
+      return 0.0f;
+}
+
+#define BELL_SUPPORT (1.5f)
+static Resample_Real bell_filter(Resample_Real t)    /* box (*) box (*) box */
+{
+   if (t < 0.0f)
+      t = -t;
+
+   if (t < .5f)
+      return (.75f - (t * t));
+
+   if (t < 1.5f)
+   {
+      t = (t - 1.5f);
+      return (.5f * (t * t));
+   }
+
+   return (0.0f);
+}
+
+#define B_SPLINE_SUPPORT (2.0f)
+static Resample_Real B_spline_filter(Resample_Real t)  /* box (*) box (*) box (*) box */
+{
+   Resample_Real tt;
+
+   if (t < 0.0f)
+      t = -t;
+
+   if (t < 1.0f)
+   {
+      tt = t * t;
+      return ((.5f * tt * t) - tt + (2.0f / 3.0f));
+   }
+   else if (t < 2.0f)
+   {
+      t = 2.0f - t;
+      return ((1.0f / 6.0f) * (t * t * t));
+   }
+
+   return (0.0f);
+}
+
+// Dodgson, N., "Quadratic Interpolation for Image Resampling"
+#define QUADRATIC_SUPPORT 1.5f
+static Resample_Real quadratic(Resample_Real t, const Resample_Real R)
+{
+   if (t < 0.0f)
+      t = -t;
+   if (t < QUADRATIC_SUPPORT)
+   {
+      Resample_Real tt = t * t;
+      if (t <= .5f)
+         return (-2.0f * R) * tt + .5f * (R + 1.0f);
+      else
+         return (R * tt) + (-2.0f * R - .5f) * t + (3.0f / 4.0f) * (R + 1.0f);
+   }
+   else
+      return 0.0f;
+}
+
+static Resample_Real quadratic_interp_filter(Resample_Real t)
+{
+   return quadratic(t, 1.0f);
+}
+
+static Resample_Real quadratic_approx_filter(Resample_Real t)
+{
+   return quadratic(t, .5f);
+}
+
+static Resample_Real quadratic_mix_filter(Resample_Real t)
+{
+   return quadratic(t, .8f);
+}
+
+// Mitchell, D. and A. Netravali, "Reconstruction Filters in Computer Graphics."
+// Computer Graphics, Vol. 22, No. 4, pp. 221-228.
+// (B, C)
+// (1/3, 1/3) - Defaults recommended by Mitchell and Netravali
+// (1, 0)     - Equivalent to the Cubic B-Spline
+// (0, 0.5)   - Equivalent to the Catmull-Rom Spline
+// (0, C)     - The family of Cardinal Cubic Splines
+// (B, 0)     - Duff's tensioned B-Splines.
+static Resample_Real mitchell(Resample_Real t, const Resample_Real B, const Resample_Real C)
+{
+   Resample_Real tt;
+
+   tt = t * t;
+
+   if(t < 0.0f)
+      t = -t;
+
+   if(t < 1.0f)
+   {
+      t = (((12.0f - 9.0f * B - 6.0f * C) * (t * tt))
+         + ((-18.0f + 12.0f * B + 6.0f * C) * tt)
+         + (6.0f - 2.0f * B));
+
+      return (t / 6.0f);
+   }
+   else if (t < 2.0f)
+   {
+      t = (((-1.0f * B - 6.0f * C) * (t * tt))
+         + ((6.0f * B + 30.0f * C) * tt)
+         + ((-12.0f * B - 48.0f * C) * t)
+         + (8.0f * B + 24.0f * C));
+
+      return (t / 6.0f);
+   }
+
+   return (0.0f);
+}
+
+#define MITCHELL_SUPPORT (2.0f)
+static Resample_Real mitchell_filter(Resample_Real t)
+{
+   return mitchell(t, 1.0f / 3.0f, 1.0f / 3.0f);
+}
+
+#define CATMULL_ROM_SUPPORT (2.0f)
+static Resample_Real catmull_rom_filter(Resample_Real t)
+{
+   return mitchell(t, 0.0f, .5f);
+}
+
+static double sinc(double x)
+{
+   x = (x * M_PI);
+
+   if ((x < 0.01f) && (x > -0.01f))
+      return 1.0f + x*x*(-1.0f/6.0f + x*x*1.0f/120.0f);
+
+   return sin(x) / x;
+}
+
+static Resample_Real clean(double t)
+{
+   const Resample_Real EPSILON = .0000125f;
+   if (fabs(t) < EPSILON)
+      return 0.0f;
+   return (Resample_Real)t;
+}
+
+static double blackman_exact_window(double x)
+{
+   return 0.42659071f + 0.49656062f * cos(M_PI*x) + 0.07684867f * cos(2.0f*M_PI*x);
+}
+
+#define BLACKMAN_SUPPORT (3.0f)
+static Resample_Real blackman_filter(Resample_Real t)
+{
+   if (t < 0.0f)
+      t = -t;
+
+   if (t < 3.0f)
+      //return clean(sinc(t) * blackman_window(t / 3.0f));
+      return clean(sinc(t) * blackman_exact_window(t / 3.0f));
+   else
+      return (0.0f);
+}
+
+#define GAUSSIAN_SUPPORT (1.25f)
+static Resample_Real gaussian_filter(Resample_Real t) // with blackman window
+{
+   if (t < 0)
+      t = -t;
+   if (t < GAUSSIAN_SUPPORT)
+      return clean(exp(-2.0f * t * t) * sqrt(2.0f / M_PI) * blackman_exact_window(t / GAUSSIAN_SUPPORT));
+   else
+      return 0.0f;
+}
+
+// Windowed sinc -- see "Jimm Blinn's Corner: Dirty Pixels" pg. 26.
+#define LANCZOS3_SUPPORT (3.0f)
+static Resample_Real lanczos3_filter(Resample_Real t)
+{
+   if (t < 0.0f)
+      t = -t;
+
+   if (t < 3.0f)
+      return clean(sinc(t) * sinc(t / 3.0f));
+   else
+      return (0.0f);
+}
+
+#define LANCZOS4_SUPPORT (4.0f)
+static Resample_Real lanczos4_filter(Resample_Real t)
+{
+   if (t < 0.0f)
+      t = -t;
+
+   if (t < 4.0f)
+      return clean(sinc(t) * sinc(t / 4.0f));
+   else
+      return (0.0f);
+}
+
+#define LANCZOS6_SUPPORT (6.0f)
+static Resample_Real lanczos6_filter(Resample_Real t)
+{
+   if (t < 0.0f)
+      t = -t;
+
+   if (t < 6.0f)
+      return clean(sinc(t) * sinc(t / 6.0f));
+   else
+      return (0.0f);
+}
+
+#define LANCZOS12_SUPPORT (12.0f)
+static Resample_Real lanczos12_filter(Resample_Real t)
+{
+   if (t < 0.0f)
+      t = -t;
+
+   if (t < 12.0f)
+      return clean(sinc(t) * sinc(t / 12.0f));
+   else
+      return (0.0f);
+}
+
+static double bessel0(double x)
+{
+   const double EPSILON_RATIO = 1E-16;
+   double xh, sum, pow, ds;
+   int k;
+
+   xh = 0.5 * x;
+   sum = 1.0;
+   pow = 1.0;
+   k = 0;
+   ds = 1.0;
+   while (ds > sum * EPSILON_RATIO) // FIXME: Shouldn't this stop after X iterations for max. safety?
+   {
+      ++k;
+      pow = pow * (xh / k);
+      ds = pow * pow;
+      sum = sum + ds;
+   }
+
+   return sum;
+}
+
+static const Resample_Real KAISER_ALPHA = 4.0;
+static double kaiser(double alpha, double half_width, double x)
+{
+   const double ratio = (x / half_width);
+   return bessel0(alpha * sqrt(1 - ratio * ratio)) / bessel0(alpha);
+}
+
+#define KAISER_SUPPORT 3
+static Resample_Real kaiser_filter(Resample_Real t)
+{
+   if (t < 0.0f)
+      t = -t;
+
+   if (t < KAISER_SUPPORT)
+   {
+      // db atten
+      const Resample_Real att = 40.0f;
+      const Resample_Real alpha = (Resample_Real)(exp(log((double)0.58417 * (att - 20.96)) * 0.4) + 0.07886 * (att - 20.96));
+      //const Resample_Real alpha = KAISER_ALPHA;
+      return (Resample_Real)clean(sinc(t) * kaiser(alpha, KAISER_SUPPORT, t));
+   }
+
+   return 0.0f;
+}
+
+// filters[] is a list of all the available filter functions.
+static struct
+{
+   char name[32];
+   Resample_Real (*func)(Resample_Real t);
+   Resample_Real support;
+} g_filters[] =
+{
+   { "box",              box_filter,              BOX_FILTER_SUPPORT },
+   { "tent",             tent_filter,             TENT_FILTER_SUPPORT },
+   { "bell",             bell_filter,             BELL_SUPPORT },
+   { "b-spline",         B_spline_filter,         B_SPLINE_SUPPORT },
+   { "mitchell",         mitchell_filter,         MITCHELL_SUPPORT },
+   { "lanczos3",         lanczos3_filter,         LANCZOS3_SUPPORT },
+   { "blackman",         blackman_filter,         BLACKMAN_SUPPORT },
+   { "lanczos4",         lanczos4_filter,         LANCZOS4_SUPPORT },
+   { "lanczos6",         lanczos6_filter,         LANCZOS6_SUPPORT },
+   { "lanczos12",        lanczos12_filter,        LANCZOS12_SUPPORT },
+   { "kaiser",           kaiser_filter,           KAISER_SUPPORT },
+   { "gaussian",         gaussian_filter,         GAUSSIAN_SUPPORT },
+   { "catmullrom",       catmull_rom_filter,      CATMULL_ROM_SUPPORT },
+   { "quadratic_interp", quadratic_interp_filter, QUADRATIC_SUPPORT },
+   { "quadratic_approx", quadratic_approx_filter, QUADRATIC_SUPPORT },
+   { "quadratic_mix",    quadratic_mix_filter,    QUADRATIC_SUPPORT },
+};
+
+static const int NUM_FILTERS = sizeof(g_filters) / sizeof(g_filters[0]);
+
+/* Ensure that the contributing source sample is
+* within bounds. If not, reflect, clamp, or wrap.
+*/
+int Resampler::reflect(const int j, const int src_x, const Boundary_Op boundary_op)
+{
+   int n;
+
+   if (j < 0)
+   {
+      if (boundary_op == BOUNDARY_REFLECT)
+      {
+         n = -j;
+
+         if (n >= src_x)
+            n = src_x - 1;
+      }
+      else if (boundary_op == BOUNDARY_WRAP)
+         n = posmod(j, src_x);
+      else
+         n = 0;
+   }
+   else if (j >= src_x)
+   {
+      if (boundary_op == BOUNDARY_REFLECT)
+      {
+         n = (src_x - j) + (src_x - 1);
+
+         if (n < 0)
+            n = 0;
+      }
+      else if (boundary_op == BOUNDARY_WRAP)
+         n = posmod(j, src_x);
+      else
+         n = src_x - 1;
+   }
+   else
+      n = j;
+
+   return n;
+}
+
+// The make_clist() method generates, for all destination samples,
+// the list of all source samples with non-zero weighted contributions.
+Resampler::Contrib_List* Resampler::make_clist(
+   int src_x, int dst_x, Boundary_Op boundary_op,
+   Resample_Real (*Pfilter)(Resample_Real),
+   Resample_Real filter_support,
+   Resample_Real filter_scale,
+   Resample_Real src_ofs)
+{
+   typedef struct
+   {
+      // The center of the range in DISCRETE coordinates (pixel center = 0.0f).
+      Resample_Real center;
+      int left, right;
+   } Contrib_Bounds;
+
+   int i, j, k, n, left, right;
+   Resample_Real total_weight;
+   Resample_Real xscale, center, half_width, weight;
+   Contrib_List* Pcontrib;
+   Contrib* Pcpool;
+   Contrib* Pcpool_next;
+   Contrib_Bounds* Pcontrib_bounds;
+
+   if ((Pcontrib = (Contrib_List*)calloc(dst_x, sizeof(Contrib_List))) == NULL)
+      return NULL;
+
+   Pcontrib_bounds = (Contrib_Bounds*)calloc(dst_x, sizeof(Contrib_Bounds));
+   if (!Pcontrib_bounds)
+   {
+      free(Pcontrib);
+      return (NULL);
+   }
+
+   const Resample_Real oo_filter_scale = 1.0f / filter_scale;
+
+   const Resample_Real NUDGE = 0.5f;
+   xscale = dst_x / (Resample_Real)src_x;
+
+   if (xscale < 1.0f)
+   {
+      int total; (void)total;
+
+      /* Handle case when there are fewer destination
+      * samples than source samples (downsampling/minification).
+      */
+
+      // stretched half width of filter
+      half_width = (filter_support / xscale) * filter_scale;
+
+      // Find the range of source sample(s) that will contribute to each destination sample.
+
+      for (i = 0, n = 0; i < dst_x; i++)
+      {
+         // Convert from discrete to continuous coordinates, scale, then convert back to discrete.
+         center = ((Resample_Real)i + NUDGE) / xscale;
+         center -= NUDGE;
+         center += src_ofs;
+
+         left   = cast_to_int((Resample_Real)floor(center - half_width));
+         right  = cast_to_int((Resample_Real)ceil(center + half_width));
+
+         Pcontrib_bounds[i].center = center;
+         Pcontrib_bounds[i].left = left;
+         Pcontrib_bounds[i].right = right;
+
+         n += (right - left + 1);
+      }
+
+      /* Allocate memory for contributors. */
+
+      if ((n == 0) || ((Pcpool = (Contrib*)calloc(n, sizeof(Contrib))) == NULL))
+      {
+         free(Pcontrib);
+         free(Pcontrib_bounds);
+         return NULL;
+      }
+      total = n;
+
+      Pcpool_next = Pcpool;
+
+      /* Create the list of source samples which
+      * contribute to each destination sample.
+      */
+
+      for (i = 0; i < dst_x; i++)
+      {
+         int max_k = -1;
+         Resample_Real max_w = -1e+20f;
+
+         center = Pcontrib_bounds[i].center;
+         left   = Pcontrib_bounds[i].left;
+         right  = Pcontrib_bounds[i].right;
+
+         Pcontrib[i].n = 0;
+         Pcontrib[i].p = Pcpool_next;
+         Pcpool_next += (right - left + 1);
+         resampler_assert ((Pcpool_next - Pcpool) <= total);
+
+         total_weight = 0;
+
+         for (j = left; j <= right; j++)
+            total_weight += (*Pfilter)((center - (Resample_Real)j) * xscale * oo_filter_scale);
+         const Resample_Real norm = static_cast<Resample_Real>(1.0f / total_weight);
+
+         total_weight = 0;
+
+#if RESAMPLER_DEBUG
+         printf("%i: ", i);
+#endif
+
+         for (j = left; j <= right; j++)
+         {
+            weight = (*Pfilter)((center - (Resample_Real)j) * xscale * oo_filter_scale) * norm;
+            if (weight == 0.0f)
+               continue;
+
+            n = reflect(j, src_x, boundary_op);
+
+#if RESAMPLER_DEBUG
+            printf("%i(%f), ", n, weight);
+#endif
+
+            /* Increment the number of source
+            * samples which contribute to the
+            * current destination sample.
+            */
+
+            k = Pcontrib[i].n++;
+
+            Pcontrib[i].p[k].pixel  = (unsigned short)(n);       /* store src sample number */
+            Pcontrib[i].p[k].weight = weight; /* store src sample weight */
+
+            total_weight += weight;          /* total weight of all contributors */
+
+            if (weight > max_w)
+            {
+               max_w = weight;
+               max_k = k;
+            }
+         }
+
+#if RESAMPLER_DEBUG
+         printf("\n\n");
+#endif
+
+         //resampler_assert(Pcontrib[i].n);
+         //resampler_assert(max_k != -1);
+         if ((max_k == -1) || (Pcontrib[i].n == 0))
+         {
+            free(Pcpool);
+            free(Pcontrib);
+            free(Pcontrib_bounds);
+            return NULL;
+         }
+
+         if (total_weight != 1.0f)
+            Pcontrib[i].p[max_k].weight += 1.0f - total_weight;
+      }
+   }
+   else
+   {
+      /* Handle case when there are more
+      * destination samples than source
+      * samples (upsampling).
+      */
+
+      half_width = filter_support * filter_scale;
+
+      // Find the source sample(s) that contribute to each destination sample.
+
+      for (i = 0, n = 0; i < dst_x; i++)
+      {
+         // Convert from discrete to continuous coordinates, scale, then convert back to discrete.
+         center = ((Resample_Real)i + NUDGE) / xscale;
+         center -= NUDGE;
+         center += src_ofs;
+
+         left   = cast_to_int((Resample_Real)floor(center - half_width));
+         right  = cast_to_int((Resample_Real)ceil(center + half_width));
+
+         Pcontrib_bounds[i].center = center;
+         Pcontrib_bounds[i].left = left;
+         Pcontrib_bounds[i].right = right;
+
+         n += (right - left + 1);
+      }
+
+      /* Allocate memory for contributors. */
+
+      int total = n;
+      if ((total == 0) || ((Pcpool = (Contrib*)calloc(total, sizeof(Contrib))) == NULL))
+      {
+         free(Pcontrib);
+         free(Pcontrib_bounds);
+         return NULL;
+      }
+
+      Pcpool_next = Pcpool;
+
+      /* Create the list of source samples which
+      * contribute to each destination sample.
+      */
+
+      for (i = 0; i < dst_x; i++)
+      {
+         int max_k = -1;
+         Resample_Real max_w = -1e+20f;
+
+         center = Pcontrib_bounds[i].center;
+         left   = Pcontrib_bounds[i].left;
+         right  = Pcontrib_bounds[i].right;
+
+         Pcontrib[i].n = 0;
+         Pcontrib[i].p = Pcpool_next;
+         Pcpool_next += (right - left + 1);
+         resampler_assert((Pcpool_next - Pcpool) <= total);
+
+         total_weight = 0;
+         for (j = left; j <= right; j++)
+            total_weight += (*Pfilter)((center - (Resample_Real)j) * oo_filter_scale);
+
+         const Resample_Real norm = static_cast<Resample_Real>(1.0f / total_weight);
+
+         total_weight = 0;
+
+#if RESAMPLER_DEBUG
+         printf("%i: ", i);
+#endif
+
+         for (j = left; j <= right; j++)
+         {
+            weight = (*Pfilter)((center - (Resample_Real)j) * oo_filter_scale) * norm;
+            if (weight == 0.0f)
+               continue;
+
+            n = reflect(j, src_x, boundary_op);
+
+#if RESAMPLER_DEBUG
+            printf("%i(%f), ", n, weight);
+#endif
+
+            /* Increment the number of source
+            * samples which contribute to the
+            * current destination sample.
+            */
+
+            k = Pcontrib[i].n++;
+
+            Pcontrib[i].p[k].pixel  = (unsigned short)(n);       /* store src sample number */
+            Pcontrib[i].p[k].weight = weight; /* store src sample weight */
+
+            total_weight += weight;          /* total weight of all contributors */
+
+            if (weight > max_w)
+            {
+               max_w = weight;
+               max_k = k;
+            }
+         }
+
+#if RESAMPLER_DEBUG
+         printf("\n\n");
+#endif
+
+         //resampler_assert(Pcontrib[i].n);
+         //resampler_assert(max_k != -1);
+
+         if ((max_k == -1) || (Pcontrib[i].n == 0))
+         {
+            free(Pcpool);
+            free(Pcontrib);
+            free(Pcontrib_bounds);
+            return NULL;
+         }
+
+         if (total_weight != 1.0f)
+            Pcontrib[i].p[max_k].weight += 1.0f - total_weight;
+      }
+   }
+
+#if RESAMPLER_DEBUG
+   printf("*******\n");
+#endif
+
+   free(Pcontrib_bounds);
+
+   return Pcontrib;
+}
+
+void Resampler::resample_x(Sample* Pdst, const Sample* Psrc)
+{
+   resampler_assert(Pdst);
+   resampler_assert(Psrc);
+
+   int i, j;
+   Sample total;
+   Contrib_List *Pclist = m_Pclist_x;
+   Contrib *p;
+
+   for (i = m_resample_dst_x; i > 0; i--, Pclist++)
+   {
+#if RESAMPLER_DEBUG_OPS
+      total_ops += Pclist->n;
+#endif
+
+      for (j = Pclist->n, p = Pclist->p, total = 0; j > 0; j--, p++)
+         total += Psrc[p->pixel] * p->weight;
+
+      *Pdst++ = total;
+   }
+}
+
+void Resampler::scale_y_mov(Sample* Ptmp, const Sample* Psrc, Resample_Real weight, int dst_x)
+{
+   int i;
+
+#if RESAMPLER_DEBUG_OPS
+   total_ops += dst_x;
+#endif
+
+   // Not += because temp buf wasn't cleared.
+   for (i = dst_x; i > 0; i--)
+      *Ptmp++ = *Psrc++ * weight;
+}
+
+void Resampler::scale_y_add(Sample* Ptmp, const Sample* Psrc, Resample_Real weight, int dst_x)
+{
+#if RESAMPLER_DEBUG_OPS
+   total_ops += dst_x;
+#endif
+
+   for (int i = dst_x; i > 0; i--)
+      (*Ptmp++) += *Psrc++ * weight;
+}
+
+void Resampler::clamp(Sample* Pdst, int n)
+{
+   while (n > 0)
+   {
+      *Pdst = clamp_sample(*Pdst);
+      ++Pdst;
+      n--;
+   }
+}
+
+void Resampler::resample_y(Sample* Pdst)
+{
+   int i, j;
+   Sample* Psrc;
+   Contrib_List* Pclist = &m_Pclist_y[m_cur_dst_y];
+
+   Sample* Ptmp = m_delay_x_resample ? m_Ptmp_buf : Pdst;
+   resampler_assert(Ptmp);
+
+   /* Process each contributor. */
+
+   for (i = 0; i < Pclist->n; i++)
+   {
+      /* locate the contributor's location in the scan
+      * buffer -- the contributor must always be found!
+      */
+
+      for (j = 0; j < MAX_SCAN_BUF_SIZE; j++)
+         if (m_Pscan_buf->scan_buf_y[j] == Pclist->p[i].pixel)
+            break;
+
+      resampler_assert(j < MAX_SCAN_BUF_SIZE);
+
+      Psrc = m_Pscan_buf->scan_buf_l[j];
+
+      if (!i)
+         scale_y_mov(Ptmp, Psrc, Pclist->p[i].weight, m_intermediate_x);
+      else
+         scale_y_add(Ptmp, Psrc, Pclist->p[i].weight, m_intermediate_x);
+
+      /* If this source line doesn't contribute to any
+      * more destination lines then mark the scanline buffer slot
+      * which holds this source line as free.
+      * (The max. number of slots used depends on the Y
+      * axis sampling factor and the scaled filter width.)
+      */
+
+      if (--m_Psrc_y_count[resampler_range_check(Pclist->p[i].pixel, m_resample_src_y)] == 0)
+      {
+         m_Psrc_y_flag[resampler_range_check(Pclist->p[i].pixel, m_resample_src_y)] = FALSE;
+         m_Pscan_buf->scan_buf_y[j] = -1;
+      }
+   }
+
+   /* Now generate the destination line */
+
+   if (m_delay_x_resample) // Was X resampling delayed until after Y resampling?
+   {
+      resampler_assert(Pdst != Ptmp);
+      resample_x(Pdst, Ptmp);
+   }
+   else
+   {
+      resampler_assert(Pdst == Ptmp);
+   }
+
+   if (m_lo < m_hi)
+      clamp(Pdst, m_resample_dst_x);
+}
+
+bool Resampler::put_line(const Sample* Psrc)
+{
+   int i;
+
+   if (m_cur_src_y >= m_resample_src_y)
+      return false;
+
+   /* Does this source line contribute
+   * to any destination line? if not,
+   * exit now.
+   */
+
+   if (!m_Psrc_y_count[resampler_range_check(m_cur_src_y, m_resample_src_y)])
+   {
+      m_cur_src_y++;
+      return true;
+   }
+
+   /* Find an empty slot in the scanline buffer. (FIXME: Perf. is terrible here with extreme scaling ratios.) */
+
+   for (i = 0; i < MAX_SCAN_BUF_SIZE; i++)
+      if (m_Pscan_buf->scan_buf_y[i] == -1)
+         break;
+
+   /* If the buffer is full, exit with an error. */
+
+   if (i == MAX_SCAN_BUF_SIZE)
+   {
+      m_status = STATUS_SCAN_BUFFER_FULL;
+      return false;
+   }
+
+   m_Psrc_y_flag[resampler_range_check(m_cur_src_y, m_resample_src_y)] = TRUE;
+   m_Pscan_buf->scan_buf_y[i]  = m_cur_src_y;
+
+   /* Does this slot have any memory allocated to it? */
+
+   if (!m_Pscan_buf->scan_buf_l[i])
+   {
+      if ((m_Pscan_buf->scan_buf_l[i] = (Sample*)malloc(m_intermediate_x * sizeof(Sample))) == NULL)
+      {
+         m_status = STATUS_OUT_OF_MEMORY;
+         return false;
+      }
+   }
+
+   // Resampling on the X axis first?
+   if (m_delay_x_resample)
+   {
+      resampler_assert(m_intermediate_x == m_resample_src_x);
+
+      // Y-X resampling order
+      memcpy(m_Pscan_buf->scan_buf_l[i], Psrc, m_intermediate_x * sizeof(Sample));
+   }
+   else
+   {
+      resampler_assert(m_intermediate_x == m_resample_dst_x);
+
+      // X-Y resampling order
+      resample_x(m_Pscan_buf->scan_buf_l[i], Psrc);
+   }
+
+   m_cur_src_y++;
+
+   return true;
+}
+
+const Resampler::Sample* Resampler::get_line()
+{
+   int i;
+
+   /* If all the destination lines have been
+   * generated, then always return NULL.
+   */
+
+   if (m_cur_dst_y == m_resample_dst_y)
+      return NULL;
+
+   /* Check to see if all the required
+   * contributors are present, if not,
+   * return NULL.
+   */
+
+   for (i = 0; i < m_Pclist_y[m_cur_dst_y].n; i++)
+      if (!m_Psrc_y_flag[resampler_range_check(m_Pclist_y[m_cur_dst_y].p[i].pixel, m_resample_src_y)])
+         return NULL;
+
+   resample_y(m_Pdst_buf);
+
+   m_cur_dst_y++;
+
+   return m_Pdst_buf;
+}
+
+Resampler::~Resampler()
+{
+   int i;
+
+#if RESAMPLER_DEBUG_OPS
+   printf("actual ops: %i\n", total_ops);
+#endif
+
+   free(m_Pdst_buf);
+   m_Pdst_buf = NULL;
+
+   if (m_Ptmp_buf)
+   {
+      free(m_Ptmp_buf);
+      m_Ptmp_buf = NULL;
+   }
+
+   /* Don't deallocate a contibutor list
+   * if the user passed us one of their own.
+   */
+
+   if ((m_Pclist_x) && (!m_clist_x_forced))
+   {
+      free(m_Pclist_x->p);
+      free(m_Pclist_x);
+      m_Pclist_x = NULL;
+   }
+
+   if ((m_Pclist_y) && (!m_clist_y_forced))
+   {
+      free(m_Pclist_y->p);
+      free(m_Pclist_y);
+      m_Pclist_y = NULL;
+   }
+
+   free(m_Psrc_y_count);
+   m_Psrc_y_count = NULL;
+
+   free(m_Psrc_y_flag);
+   m_Psrc_y_flag = NULL;
+
+   if (m_Pscan_buf)
+   {
+      for (i = 0; i < MAX_SCAN_BUF_SIZE; i++)
+         free(m_Pscan_buf->scan_buf_l[i]);
+
+      free(m_Pscan_buf);
+      m_Pscan_buf = NULL;
+   }
+}
+
+void Resampler::restart()
+{
+   if (STATUS_OKAY != m_status)
+      return;
+
+   m_cur_src_y = m_cur_dst_y = 0;
+
+   int i, j;
+   for (i = 0; i < m_resample_src_y; i++)
+   {
+      m_Psrc_y_count[i] = 0;
+      m_Psrc_y_flag[i] = FALSE;
+   }
+
+   for (i = 0; i < m_resample_dst_y; i++)
+   {
+      for (j = 0; j < m_Pclist_y[i].n; j++)
+         m_Psrc_y_count[resampler_range_check(m_Pclist_y[i].p[j].pixel, m_resample_src_y)]++;
+   }
+
+   for (i = 0; i < MAX_SCAN_BUF_SIZE; i++)
+   {
+      m_Pscan_buf->scan_buf_y[i] = -1;
+
+      free(m_Pscan_buf->scan_buf_l[i]);
+      m_Pscan_buf->scan_buf_l[i] = NULL;
+   }
+}
+
+Resampler::Resampler(int src_x, int src_y,
+                     int dst_x, int dst_y,
+                     Boundary_Op boundary_op,
+                     Resample_Real sample_low, Resample_Real sample_high,
+                     const char* Pfilter_name,
+                     Contrib_List* Pclist_x,
+                     Contrib_List* Pclist_y,
+                     Resample_Real filter_x_scale,
+                     Resample_Real filter_y_scale,
+                     Resample_Real src_x_ofs,
+                     Resample_Real src_y_ofs)
+{
+   int i, j;
+   Resample_Real support, (*func)(Resample_Real);
+
+   resampler_assert(src_x > 0);
+   resampler_assert(src_y > 0);
+   resampler_assert(dst_x > 0);
+   resampler_assert(dst_y > 0);
+
+#if RESAMPLER_DEBUG_OPS
+   total_ops = 0;
+#endif
+
+   m_lo = sample_low;
+   m_hi = sample_high;
+
+   m_delay_x_resample = false;
+   m_intermediate_x = 0;
+   m_Pdst_buf = NULL;
+   m_Ptmp_buf = NULL;
+   m_clist_x_forced = false;
+   m_Pclist_x = NULL;
+   m_clist_y_forced = false;
+   m_Pclist_y = NULL;
+   m_Psrc_y_count = NULL;
+   m_Psrc_y_flag = NULL;
+   m_Pscan_buf = NULL;
+   m_status = STATUS_OKAY;
+
+   m_resample_src_x = src_x;
+   m_resample_src_y = src_y;
+   m_resample_dst_x = dst_x;
+   m_resample_dst_y = dst_y;
+
+   m_boundary_op = boundary_op;
+
+   if ((m_Pdst_buf = (Sample*)malloc(m_resample_dst_x * sizeof(Sample))) == NULL)
+   {
+      m_status = STATUS_OUT_OF_MEMORY;
+      return;
+   }
+
+   // Find the specified filter.
+
+   if (Pfilter_name == NULL)
+      Pfilter_name = RESAMPLER_DEFAULT_FILTER;
+
+   for (i = 0; i < NUM_FILTERS; i++)
+      if (strcmp(Pfilter_name, g_filters[i].name) == 0)
+         break;
+
+   if (i == NUM_FILTERS)
+   {
+      m_status = STATUS_BAD_FILTER_NAME;
+      return;
+   }
+
+   func = g_filters[i].func;
+   support = g_filters[i].support;
+
+   /* Create contributor lists, unless the user supplied custom lists. */
+
+   if (!Pclist_x)
+   {
+      m_Pclist_x = make_clist(m_resample_src_x, m_resample_dst_x, m_boundary_op, func, support, filter_x_scale, src_x_ofs);
+      if (!m_Pclist_x)
+      {
+         m_status = STATUS_OUT_OF_MEMORY;
+         return;
+      }
+   }
+   else
+   {
+      m_Pclist_x = Pclist_x;
+      m_clist_x_forced = true;
+   }
+
+   if (!Pclist_y)
+   {
+      m_Pclist_y = make_clist(m_resample_src_y, m_resample_dst_y, m_boundary_op, func, support, filter_y_scale, src_y_ofs);
+      if (!m_Pclist_y)
+      {
+         m_status = STATUS_OUT_OF_MEMORY;
+         return;
+      }
+   }
+   else
+   {
+      m_Pclist_y = Pclist_y;
+      m_clist_y_forced = true;
+   }
+
+   if ((m_Psrc_y_count = (int*)calloc(m_resample_src_y, sizeof(int))) == NULL)
+   {
+      m_status = STATUS_OUT_OF_MEMORY;
+      return;
+   }
+
+   if ((m_Psrc_y_flag = (unsigned char*)calloc(m_resample_src_y, sizeof(unsigned char))) == NULL)
+   {
+      m_status = STATUS_OUT_OF_MEMORY;
+      return;
+   }
+
+   /* Count how many times each source line
+   * contributes to a destination line.
+   */
+
+   for (i = 0; i < m_resample_dst_y; i++)
+      for (j = 0; j < m_Pclist_y[i].n; j++)
+         m_Psrc_y_count[resampler_range_check(m_Pclist_y[i].p[j].pixel, m_resample_src_y)]++;
+
+   if ((m_Pscan_buf = (Scan_Buf*)malloc(sizeof(Scan_Buf))) == NULL)
+   {
+      m_status = STATUS_OUT_OF_MEMORY;
+      return;
+   }
+
+   for (i = 0; i < MAX_SCAN_BUF_SIZE; i++)
+   {
+      m_Pscan_buf->scan_buf_y[i] = -1;
+      m_Pscan_buf->scan_buf_l[i] = NULL;
+   }
+
+   m_cur_src_y = m_cur_dst_y = 0;
+   {
+      // Determine which axis to resample first by comparing the number of multiplies required
+      // for each possibility.
+      int x_ops = count_ops(m_Pclist_x, m_resample_dst_x);
+      int y_ops = count_ops(m_Pclist_y, m_resample_dst_y);
+
+      // Hack 10/2000: Weight Y axis ops a little more than X axis ops.
+      // (Y axis ops use more cache resources.)
+      int xy_ops = x_ops * m_resample_src_y +
+         (4 * y_ops * m_resample_dst_x)/3;
+
+      int yx_ops = (4 * y_ops * m_resample_src_x)/3 +
+         x_ops * m_resample_dst_y;
+
+#if RESAMPLER_DEBUG_OPS
+      printf("src: %i %i\n", m_resample_src_x, m_resample_src_y);
+      printf("dst: %i %i\n", m_resample_dst_x, m_resample_dst_y);
+      printf("x_ops: %i\n", x_ops);
+      printf("y_ops: %i\n", y_ops);
+      printf("xy_ops: %i\n", xy_ops);
+      printf("yx_ops: %i\n", yx_ops);
+#endif
+
+      // Now check which resample order is better. In case of a tie, choose the order
+      // which buffers the least amount of data.
+      if ((xy_ops > yx_ops) ||
+         ((xy_ops == yx_ops) && (m_resample_src_x < m_resample_dst_x))
+         )
+      {
+         m_delay_x_resample = true;
+         m_intermediate_x = m_resample_src_x;
+      }
+      else
+      {
+         m_delay_x_resample = false;
+         m_intermediate_x = m_resample_dst_x;
+      }
+#if RESAMPLER_DEBUG_OPS
+      printf("delaying: %i\n", m_delay_x_resample);
+#endif
+   }
+
+   if (m_delay_x_resample)
+   {
+      if ((m_Ptmp_buf = (Sample*)malloc(m_intermediate_x * sizeof(Sample))) == NULL)
+      {
+         m_status = STATUS_OUT_OF_MEMORY;
+         return;
+      }
+   }
+}
+
+void Resampler::get_clists(Contrib_List** ptr_clist_x, Contrib_List** ptr_clist_y)
+{
+   if (ptr_clist_x)
+      *ptr_clist_x = m_Pclist_x;
+
+   if (ptr_clist_y)
+      *ptr_clist_y = m_Pclist_y;
+}
+
+int Resampler::get_filter_num()
+{
+   return NUM_FILTERS;
+}
+
+char* Resampler::get_filter_name(int filter_num)
+{
+   if ((filter_num < 0) || (filter_num >= NUM_FILTERS))
+      return NULL;
+   else
+      return g_filters[filter_num].name;
+}
+
diff --git a/third-party/image-resampler/resampler.h b/third-party/image-resampler/resampler.h
new file mode 100644 (file)
index 0000000..c28daaf
--- /dev/null
@@ -0,0 +1,196 @@
+// resampler.h, Separable filtering image rescaler v2.21, Rich Geldreich - richgel99@gmail.com
+// See unlicense.org text at the bottom of this file.
+#ifndef __RESAMPLER_H__
+#define __RESAMPLER_H__
+
+#define RESAMPLER_DEBUG_OPS 0
+#define RESAMPLER_DEFAULT_FILTER "lanczos4"
+
+#define RESAMPLER_MAX_DIMENSION 16384
+
+// float or double
+typedef float Resample_Real;
+
+class Resampler
+{
+public:
+   typedef Resample_Real Sample;
+
+   struct Contrib
+   {
+      Resample_Real weight;
+      unsigned short pixel;
+   };
+
+   struct Contrib_List
+   {
+      unsigned short n;
+      Contrib* p;
+   };
+
+   enum Boundary_Op
+   {
+      BOUNDARY_WRAP = 0,
+      BOUNDARY_REFLECT = 1,
+      BOUNDARY_CLAMP = 2
+   };
+
+   enum Status
+   {
+      STATUS_OKAY = 0,
+      STATUS_OUT_OF_MEMORY = 1,
+      STATUS_BAD_FILTER_NAME = 2,
+      STATUS_SCAN_BUFFER_FULL = 3
+   };
+
+   // src_x/src_y - Input dimensions
+   // dst_x/dst_y - Output dimensions
+   // boundary_op - How to sample pixels near the image boundaries
+   // sample_low/sample_high - Clamp output samples to specified range, or disable clamping if sample_low >= sample_high
+   // Pclist_x/Pclist_y - Optional pointers to contributor lists from another instance of a Resampler
+   // src_x_ofs/src_y_ofs - Offset input image by specified amount (fractional values okay)
+   Resampler(
+      int src_x, int src_y,
+      int dst_x, int dst_y,
+      Boundary_Op boundary_op = BOUNDARY_CLAMP,
+      Resample_Real sample_low = 0.0f, Resample_Real sample_high = 0.0f,
+      const char* Pfilter_name = RESAMPLER_DEFAULT_FILTER,
+      Contrib_List* Pclist_x = NULL,
+      Contrib_List* Pclist_y = NULL,
+      Resample_Real filter_x_scale = 1.0f,
+      Resample_Real filter_y_scale = 1.0f,
+      Resample_Real src_x_ofs = 0.0f,
+      Resample_Real src_y_ofs = 0.0f);
+
+   ~Resampler();
+
+   // Reinits resampler so it can handle another frame.
+   void restart();
+
+   // false on out of memory.
+   bool put_line(const Sample* Psrc);
+
+   // NULL if no scanlines are currently available (give the resampler more scanlines!)
+   const Sample* get_line();
+
+   Status status() const { return m_status; }
+
+   // Returned contributor lists can be shared with another Resampler.
+   void get_clists(Contrib_List** ptr_clist_x, Contrib_List** ptr_clist_y);
+   Contrib_List* get_clist_x() const { return m_Pclist_x; }
+   Contrib_List* get_clist_y() const { return m_Pclist_y; }
+
+   // Filter accessors.
+   static int get_filter_num();
+   static char* get_filter_name(int filter_num);
+
+private:
+   Resampler();
+   Resampler(const Resampler& o);
+   Resampler& operator= (const Resampler& o);
+
+#ifdef RESAMPLER_DEBUG_OPS
+   int total_ops;
+#endif
+
+   int m_intermediate_x;
+
+   int m_resample_src_x;
+   int m_resample_src_y;
+   int m_resample_dst_x;
+   int m_resample_dst_y;
+
+   Boundary_Op m_boundary_op;
+
+   Sample* m_Pdst_buf;
+   Sample* m_Ptmp_buf;
+
+   Contrib_List* m_Pclist_x;
+   Contrib_List* m_Pclist_y;
+
+   bool m_clist_x_forced;
+   bool m_clist_y_forced;
+
+   bool m_delay_x_resample;
+
+   int* m_Psrc_y_count;
+   unsigned char* m_Psrc_y_flag;
+
+   // The maximum number of scanlines that can be buffered at one time.
+   enum { MAX_SCAN_BUF_SIZE = RESAMPLER_MAX_DIMENSION };
+
+   struct Scan_Buf
+   {
+      int scan_buf_y[MAX_SCAN_BUF_SIZE];
+      Sample* scan_buf_l[MAX_SCAN_BUF_SIZE];
+   };
+
+   Scan_Buf* m_Pscan_buf;
+
+   int m_cur_src_y;
+   int m_cur_dst_y;
+
+   Status m_status;
+
+   void resample_x(Sample* Pdst, const Sample* Psrc);
+   void scale_y_mov(Sample* Ptmp, const Sample* Psrc, Resample_Real weight, int dst_x);
+   void scale_y_add(Sample* Ptmp, const Sample* Psrc, Resample_Real weight, int dst_x);
+   void clamp(Sample* Pdst, int n);
+   void resample_y(Sample* Pdst);
+
+   int reflect(const int j, const int src_x, const Boundary_Op boundary_op);
+
+   Contrib_List* make_clist(
+      int src_x, int dst_x, Boundary_Op boundary_op,
+      Resample_Real (*Pfilter)(Resample_Real),
+      Resample_Real filter_support,
+      Resample_Real filter_scale,
+      Resample_Real src_ofs);
+
+   inline int count_ops(Contrib_List* Pclist, int k)
+   {
+      int i, t = 0;
+      for (i = 0; i < k; i++)
+         t += Pclist[i].n;
+      return (t);
+   }
+
+   Resample_Real m_lo;
+   Resample_Real m_hi;
+
+   inline Resample_Real clamp_sample(Resample_Real f) const
+   {
+      if (f < m_lo)
+         f = m_lo;
+      else if (f > m_hi)
+         f = m_hi;
+      return f;
+   }
+};
+
+#endif // __RESAMPLER_H__
+
+// This is free and unencumbered software released into the public domain.
+//
+// Anyone is free to copy, modify, publish, use, compile, sell, or
+// distribute this software, either in source code form or as a compiled
+// binary, for any purpose, commercial or non-commercial, and by any
+// means.
+//
+// In jurisdictions that recognize copyright laws, the author or authors
+// of this software dedicate any and all copyright interest in the
+// software to the public domain. We make this dedication for the benefit
+// of the public at large and to the detriment of our heirs and
+// successors. We intend this dedication to be an overt act of
+// relinquishment in perpetuity of all present and future rights to this
+// software under copyright law.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+// IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+//
+// For more information, please refer to <http://unlicense.org/>