From 741f8b92c9980065532cfd866b87d0f494c1a38f Mon Sep 17 00:00:00 2001
From: Doug Felt <dougfelt@google.com>
Date: Fri, 7 Oct 2016 14:17:09 -0700
Subject: [PATCH] Update waveflag.c to upstream version.

Also updates LDFLAGS to match upstream.
---
 Makefile   |   2 +-
 waveflag.c | 692 +++++++++++++++++++++++++++++------------------------
 2 files changed, 382 insertions(+), 312 deletions(-)

diff --git a/Makefile b/Makefile
index 3d1e3529c..1f0a66507 100644
--- a/Makefile
+++ b/Makefile
@@ -16,7 +16,7 @@ EMOJI = NotoColorEmoji
 font: $(EMOJI).ttf
 
 CFLAGS = -std=c99 -Wall -Wextra `pkg-config --cflags --libs cairo`
-LDFLAGS = `pkg-config --libs cairo`
+LDFLAGS = -lm `pkg-config --libs cairo`
 PNGQUANTDIR := third_party/pngquant
 PNGQUANT := $(PNGQUANTDIR)/pngquant
 PNGQUANTFLAGS = --speed 1 --skip-if-larger --force
diff --git a/waveflag.c b/waveflag.c
index ce997d8b5..8e0cb07b3 100644
--- a/waveflag.c
+++ b/waveflag.c
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  *
- * Google contributors: Behdad Esfahbod, Roozbeh Pournader
+ * Google contributors: Behdad Esfahbod
  */
 
 #include <cairo.h>
@@ -26,352 +26,422 @@
 
 #define SCALE 8
 #define SIZE 128
-#define MARGIN (debug ? 24 : 0)
+#define MARGIN (debug ? 4 : 0)
 
 static unsigned int debug;
 
-static cairo_path_t *wave_path_create(void) {
-  cairo_surface_t *surface = cairo_image_surface_create(
-      CAIRO_FORMAT_ARGB32, 0, 0);
-  cairo_t *cr = cairo_create(surface);
-  cairo_path_t *path;
-
-  cairo_scale(cr, SIZE/128.*SCALE, SIZE/128.*SCALE);
-
-  cairo_move_to(cr, 127.15, 81.52);
-  cairo_rel_line_to(cr, -20.51, -66.94);
-  cairo_rel_curve_to(cr, -0.61, -2, -2.22, -3.53, -4.25, -4.03);
-  cairo_rel_curve_to(cr, -0.48, -0.12, -0.96, -0.18, -1.44, -0.18);
-  cairo_rel_curve_to(cr, -1.56, 0, -3.07, 0.61, -4.2, 1.74);
-  cairo_rel_curve_to(cr, -9.56, 9.56, -17.94, 11.38, -30.07, 11.38);
-  cairo_rel_curve_to(cr, -3.68, 0, -7.72, -0.18, -11.99, -0.38);
-  cairo_rel_curve_to(cr, -3.37, -0.15, -6.85, -0.31, -10.62, -0.4);
-  cairo_rel_curve_to(cr, -0.66, -0.02, -1.31, -0.02, -1.95, -0.02);
-  cairo_rel_curve_to(cr, -30.67, 0, -40.49, 18.56, -40.89, 19.35);
-  cairo_rel_curve_to(cr, -0.52, 1.01, -0.73, 2.16, -0.62, 3.29);
-  cairo_rel_line_to(cr, 6.72, 66.95);
-  cairo_rel_curve_to(cr, 0.22, 2.22, 1.67, 4.13, 3.75, 4.95);
-  cairo_rel_curve_to(cr, 0.7, 0.27, 1.43, 0.4, 2.16, 0.4);
-  cairo_rel_curve_to(cr, 1.43, 0, 2.84, -0.52, 3.95, -1.5);
-  cairo_rel_curve_to(cr, 0.1, -0.09, 12.42, -10.63, 32.13, -10.63);
-  cairo_rel_curve_to(cr, 2.52, 0, 5.09, 0.17, 7.64, 0.51);
-  cairo_rel_curve_to(cr, 9.27, 1.23, 16.03, 1.78, 21.95, 1.78);
-  cairo_rel_curve_to(cr, 18.93, 0, 32.93, -6.1, 46.82, -20.38);
-  cairo_curve_to(cr, 127.24, 85.85, 127.79, 83.59, 127.15, 81.52);
-  cairo_close_path(cr);
-
-  cairo_identity_matrix(cr);
-  path = cairo_copy_path(cr);
-  cairo_destroy(cr);
-  cairo_surface_destroy(surface);
-
-  return path;
-}
-
-static struct { double x, y; } mesh_points[] = {
-  { -1,  43},
-  { 30,  -3},
-  { 77,  47},
-  {104,   1},
-  {130,  84},
-  {100, 138},
-  { 45,  80},
-  {  7, 127},
+#define std_aspect (5./3.)
+#define top 21
+#define bot 128-top
+#define B 27
+static struct { double x, y; } mesh_points[] =
+{
+  {  1, top},
+  { 43, top-B},
+  { 85, top+B},
+  {127, top},
+  {127, bot},
+  { 85, bot+B},
+  { 43, bot-B},
+  {  1, bot},
 };
-#define M(i) mesh_points[i].x, mesh_points[i].y
+#define M(i) \
+	x_aspect (mesh_points[i].x, aspect), \
+	y_aspect (mesh_points[i].y, aspect)
 
-static cairo_pattern_t *wave_mesh_create(void) {
-  cairo_pattern_t *pattern = cairo_pattern_create_mesh();
-  cairo_matrix_t scale_matrix = {128./SIZE/SCALE, 0, 0, 128./SIZE/SCALE, 0, 0};
-  cairo_pattern_set_matrix(pattern, &scale_matrix);
-  cairo_mesh_pattern_begin_patch(pattern);
-
-  cairo_mesh_pattern_line_to(pattern,   M(0));
-  cairo_mesh_pattern_curve_to(pattern,  M(1), M(2), M(3));
-  cairo_mesh_pattern_line_to(pattern,   M(4));
-  cairo_mesh_pattern_curve_to(pattern,  M(5), M(6), M(7));
-
-  cairo_mesh_pattern_set_corner_color_rgb(pattern, 0, 0, 0, .5);
-  cairo_mesh_pattern_set_corner_color_rgb(pattern, 1, 1, 0, .5);
-  cairo_mesh_pattern_set_corner_color_rgb(pattern, 2, 1, 1, .5);
-  cairo_mesh_pattern_set_corner_color_rgb(pattern, 3, 0, 1, .5);
-
-  cairo_mesh_pattern_end_patch(pattern);
-
-  return pattern;
+static inline double x_aspect (double v, double aspect)
+{
+	return aspect >= 1. ? v : (v - 64) * aspect + 64;
+}
+static inline double y_aspect (double v, double aspect)
+{
+	return aspect <= 1. ? v : (v - 64) / aspect + 64;
 }
 
-static cairo_surface_t *scale_flag(cairo_surface_t *flag) {
-  unsigned int w = cairo_image_surface_get_width(flag);
-  unsigned int h = cairo_image_surface_get_height(flag);
-  cairo_surface_t *scaled = cairo_image_surface_create(
-      CAIRO_FORMAT_ARGB32, 256, 256);
-  cairo_t *cr = cairo_create(scaled);
+static cairo_path_t *
+wave_path_create (double aspect)
+{
+	cairo_surface_t *surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 0,0);
+	cairo_t *cr = cairo_create (surface);
+	cairo_path_t *path;
 
-  cairo_scale(cr, 256./w, 256./h);
+	cairo_scale (cr, SIZE/128.*SCALE, SIZE/128.*SCALE);
 
-  cairo_set_source_surface(cr, flag, 0, 0);
-  cairo_pattern_set_filter(cairo_get_source(cr), CAIRO_FILTER_BEST);
-  cairo_pattern_set_extend(cairo_get_source(cr), CAIRO_EXTEND_PAD);
-  cairo_paint(cr);
+	cairo_line_to(cr,   M(0));
+	cairo_curve_to(cr,  M(1), M(2), M(3));
+	cairo_line_to(cr,   M(4));
+	cairo_curve_to(cr,  M(5), M(6), M(7));
+	cairo_close_path (cr);
 
-  cairo_destroy(cr);
-  return scaled;
+	cairo_identity_matrix (cr);
+	path = cairo_copy_path (cr);
+	cairo_destroy (cr);
+	cairo_surface_destroy (surface);
+
+	return path;
 }
 
-static cairo_surface_t *load_scaled_flag(const char *filename) {
-  cairo_surface_t *flag = cairo_image_surface_create_from_png(filename);
-  cairo_surface_t *scaled = scale_flag(flag);
-  cairo_surface_destroy(flag);
-  return scaled;
+static cairo_pattern_t *
+wave_mesh_create (double aspect, int alpha)
+{
+	cairo_pattern_t *pattern = cairo_pattern_create_mesh();
+	cairo_matrix_t scale_matrix = {128./SIZE/SCALE, 0, 0, 128./SIZE/SCALE, 0, 0};
+	cairo_pattern_set_matrix (pattern, &scale_matrix);
+	cairo_mesh_pattern_begin_patch(pattern);
+
+	cairo_mesh_pattern_line_to(pattern,   M(0));
+	cairo_mesh_pattern_curve_to(pattern,  M(1), M(2), M(3));
+	cairo_mesh_pattern_line_to(pattern,   M(4));
+	cairo_mesh_pattern_curve_to(pattern,  M(5), M(6), M(7));
+
+	if (alpha)
+	{
+		cairo_mesh_pattern_set_corner_color_rgba(pattern, 0, 0, 0, 0, 0);
+		cairo_mesh_pattern_set_corner_color_rgba(pattern, 1, 0, 0, 0, .5);
+		cairo_mesh_pattern_set_corner_color_rgba(pattern, 2, 0, 0, 0, 1);
+		cairo_mesh_pattern_set_corner_color_rgba(pattern, 3, 0, 0, 0, .5);
+	}
+	else
+	{
+		cairo_mesh_pattern_set_corner_color_rgb(pattern, 0, 0, 0, .5);
+		cairo_mesh_pattern_set_corner_color_rgb(pattern, 1, 1, 0, .5);
+		cairo_mesh_pattern_set_corner_color_rgb(pattern, 2, 1, 1, .5);
+		cairo_mesh_pattern_set_corner_color_rgb(pattern, 3, 0, 1, .5);
+	}
+
+	cairo_mesh_pattern_end_patch(pattern);
+
+	return pattern;
 }
 
-/* Returns 65536 for luminosoty of 1.0. */
-static int luminosity(uint32_t pix) {
-  unsigned int sr = (pix >> 16) & 0xFF;
-  unsigned int sg = (pix >> 8) & 0xFF;
-  unsigned int sb = pix & 0xFF;
+static cairo_surface_t *
+scale_flag (cairo_surface_t *flag)
+{
+	unsigned int w = cairo_image_surface_get_width  (flag);
+	unsigned int h = cairo_image_surface_get_height (flag);
+	cairo_surface_t *scaled = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 256,256);
+	cairo_t *cr = cairo_create (scaled);
 
-  /* Apply gamma of 2.0 */
-  sr = sr * sr;
-  sg = sg * sg;
-  sb = sb * sb;
+	cairo_scale (cr, 256./w, 256./h);
 
-  return  (sr * 13933u /* 0.2126 * 65536 */ +
-     sg * 46871u /* 0.7152 * 65536 */ +
-     sb *  4731u /* 0.0722 * 65536 */) / (255*255);
+	cairo_set_source_surface (cr, flag, 0, 0);
+	cairo_pattern_set_filter (cairo_get_source (cr), CAIRO_FILTER_BEST);
+	cairo_pattern_set_extend (cairo_get_source (cr), CAIRO_EXTEND_PAD);
+	cairo_paint (cr);
+
+	cairo_destroy (cr);
+	return scaled;
 }
 
-/* Returns luminosity. Only miningul if pixel is opaque.
- * If pixel is not opaque, sets *transparent to 1. */
-static int luminosity_and_transparency(uint32_t pix, int *transparent) {
-  if ((pix>>24) < 0xff) {
-    *transparent = 1;
-    return 0;
-  }
-  return luminosity(pix);
+static cairo_surface_t *
+load_scaled_flag (const char *filename, double *aspect)
+{
+	cairo_surface_t *flag = cairo_image_surface_create_from_png (filename);
+	*aspect = (double) cairo_image_surface_get_width (flag) /
+		  (double) cairo_image_surface_get_height (flag);
+	cairo_surface_t *scaled = scale_flag (flag);
+	cairo_surface_destroy (flag);
+	return scaled;
 }
 
-static double calculate_border_luminosity_and_transparency(
-    cairo_surface_t *scaled_flag,
-    int *transparent) {
-  /* Some flags might have a border already.  As such, skip
-   * a few pixels on each side... */
-  const unsigned int skip = 5;
-  uint32_t *s = (uint32_t *) cairo_image_surface_get_data(scaled_flag);
-  unsigned int width  = cairo_image_surface_get_width(scaled_flag);
-  unsigned int height = cairo_image_surface_get_height(scaled_flag);
-  unsigned int sstride = cairo_image_surface_get_stride(scaled_flag) / 4;
-
-  unsigned int luma = 0;
-  unsigned int perimeter = (2 * ((width-2*skip) + (height-2*skip) - 2));
-
-  assert(width > 2 * skip && height > 2 * skip);
-
-  *transparent = 0;
-
-  for (unsigned int x = skip; x < width - skip; x++)
-    luma += luminosity_and_transparency(s[x], transparent);
-  s += sstride;
-  for (unsigned int y = 1 + skip; y < height - 1 - skip; y++) {
-    luma += luminosity_and_transparency(s[skip], transparent);
-    luma += luminosity_and_transparency(s[width - 1 - skip], transparent);
-    s += sstride;
-  }
-  for (unsigned int x = skip; x < width - skip; x++)
-    luma += luminosity_and_transparency(s[x], transparent);
-
-  if (*transparent) {
-    /* Flag is non-rectangular; eg. Nepal.
-     * Don't draw a border. */
-    return 0;
-  }
-
-  return luma / (65536. * perimeter);
+static int
+is_transparent (uint32_t pix)
+{
+	return ((pix>>24) < 0xff);
 }
 
-static cairo_t *create_image(void) {
-  cairo_surface_t *surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32,
-                                                        (SIZE+2*MARGIN)*SCALE,
-                                                        (SIZE+2*MARGIN)*SCALE);
-  cairo_t *cr = cairo_create(surface);
-  cairo_surface_destroy(surface);
-  return cr;
+static int
+border_is_transparent (cairo_surface_t *scaled_flag)
+{
+	/* Some flags might have a border already.  As such, skip
+	 * a few pixels on each side... */
+	const unsigned int skip = 5;
+	uint32_t *s = (uint32_t *) cairo_image_surface_get_data (scaled_flag);
+	unsigned int width  = cairo_image_surface_get_width (scaled_flag);
+	unsigned int height = cairo_image_surface_get_height (scaled_flag);
+	unsigned int sstride = cairo_image_surface_get_stride (scaled_flag) / 4;
+
+	int transparent = 0;
+
+	assert (width > 2 * skip && height > 2 * skip);
+
+
+	for (unsigned int x = skip; x < width - skip; x++)
+		transparent |= is_transparent (s[x]);
+	s += sstride;
+	for (unsigned int y = 1 + skip; y < height - 1 - skip; y++)
+	{
+		transparent |= is_transparent (s[skip]);
+		transparent |= is_transparent (s[width - 1 - skip]);
+		s += sstride;
+	}
+	for (unsigned int x = skip; x < width - skip; x++)
+		transparent |= is_transparent (s[x]);
+
+	return transparent;
 }
 
-static cairo_surface_t *wave_surface_create(void) {
-  cairo_t *cr = create_image();
-  cairo_surface_t *surface = cairo_surface_reference(cairo_get_target(cr));
-  cairo_pattern_t *mesh = wave_mesh_create();
-  cairo_set_source(cr, mesh);
-  cairo_paint(cr);
-  cairo_pattern_destroy(mesh);
-  cairo_destroy(cr);
-  return surface;
+static cairo_t *
+create_image (void)
+{
+	cairo_surface_t *surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
+							       (SIZE+2*MARGIN)*SCALE,
+							       (SIZE+2*MARGIN)*SCALE);
+	cairo_t *cr = cairo_create (surface);
+	cairo_surface_destroy (surface);
+	return cr;
 }
 
-static cairo_surface_t *texture_map(cairo_surface_t *src,
-                                    cairo_surface_t *tex) {
-  uint32_t *s = (uint32_t *) cairo_image_surface_get_data(src);
-  unsigned int width  = cairo_image_surface_get_width(src);
-  unsigned int height = cairo_image_surface_get_height(src);
-  unsigned int sstride = cairo_image_surface_get_stride(src) / 4;
-
-  cairo_surface_t *dst = cairo_image_surface_create(CAIRO_FORMAT_ARGB32,
-                                                    width, height);
-  uint32_t *d = (uint32_t *) cairo_image_surface_get_data(dst);
-  unsigned int dstride = cairo_image_surface_get_stride(dst) / 4;
-
-  uint32_t *t = (uint32_t *) cairo_image_surface_get_data(tex);
-  unsigned int twidth  = cairo_image_surface_get_width(tex);
-  unsigned int theight = cairo_image_surface_get_height(tex);
-  unsigned int tstride = cairo_image_surface_get_stride(tex) / 4;
-
-  assert(twidth == 256 && theight == 256);
-
-  for (unsigned int y = 0; y < height; y++) {
-    for (unsigned int x = 0; x < width; x++) {
-      unsigned int pix = s[x];
-      unsigned int sa = pix >> 24;
-      unsigned int sr = (pix >> 16) & 0xFF;
-      unsigned int sg = (pix >> 8) & 0xFF;
-      unsigned int sb = pix & 0xFF;
-      if (sa == 0) {
-        d[x] = 0;
-        continue;
-      }
-      if (sa != 255) {
-        sr = sr * 255 / sa;
-        sg = sg * 255 / sa;
-        sb = sb * 255 / sa;
-      }
-      assert(sb >= 127 && sb <= 129);
-      d[x] = t[tstride * sg + sr];
-    }
-    s += sstride;
-    d += dstride;
-  }
-  cairo_surface_mark_dirty(dst);
-
-  return dst;
+static cairo_surface_t *
+wave_surface_create (double aspect)
+{
+	cairo_t *cr = create_image ();
+	cairo_surface_t *surface = cairo_surface_reference (cairo_get_target (cr));
+	cairo_pattern_t *mesh = wave_mesh_create (aspect, 0);
+	cairo_set_source (cr, mesh);
+	cairo_paint (cr);
+	cairo_pattern_destroy (mesh);
+	cairo_destroy (cr);
+	return surface;
 }
 
-static void wave_flag(const char *input_filename, const char *output_filename) {
-  static cairo_path_t *wave_path;
-  static cairo_surface_t *wave_surface;
-  double border_luminosity;
-  int border_transparent;
+static cairo_surface_t *
+texture_map (cairo_surface_t *src, cairo_surface_t *tex)
+{
+	uint32_t *s = (uint32_t *) cairo_image_surface_get_data (src);
+	unsigned int width  = cairo_image_surface_get_width (src);
+	unsigned int height = cairo_image_surface_get_height (src);
+	unsigned int sstride = cairo_image_surface_get_stride (src) / 4;
 
-  cairo_surface_t *scaled_flag, *waved_flag;
-  cairo_t *cr;
+	cairo_surface_t *dst = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height);
+	uint32_t *d = (uint32_t *) cairo_image_surface_get_data (dst);
+	unsigned int dstride = cairo_image_surface_get_stride (dst) / 4;
 
-  if (!wave_path)
-    wave_path = wave_path_create();
-  if (!wave_surface)
-    wave_surface = wave_surface_create();
+	uint32_t *t = (uint32_t *) cairo_image_surface_get_data (tex);
+	unsigned int twidth  = cairo_image_surface_get_width (tex);
+	unsigned int theight = cairo_image_surface_get_height (tex);
+	unsigned int tstride = cairo_image_surface_get_stride (tex) / 4;
 
-  printf("Processing %s\n", input_filename);
+	assert (twidth == 256 && theight == 256);
 
-  scaled_flag = load_scaled_flag(input_filename);
-  border_luminosity = calculate_border_luminosity_and_transparency(
-      scaled_flag,
-      &border_transparent);
-  waved_flag = texture_map(wave_surface, scaled_flag);
-  cairo_surface_destroy(scaled_flag);
+	for (unsigned int y = 0; y < height; y++)
+	{
+		for (unsigned int x = 0; x < width; x++)
+		{
+			unsigned int pix = s[x];
+			unsigned int sa = pix >> 24;
+			unsigned int sr = (pix >> 16) & 0xFF;
+			unsigned int sg = (pix >>  8) & 0xFF;
+			unsigned int sb = (pix      ) & 0xFF;
+			if (sa == 0)
+			{
+				d[x] = 0;
+				continue;
+			}
+			if (sa != 255)
+			{
+				sr = sr * 255 / sa;
+				sg = sg * 255 / sa;
+				sb = sb * 255 / sa;
+			}
+			assert (sb >= 127 && sb <= 129);
+			d[x] = t[tstride * sg + sr];
+		}
+		s += sstride;
+		d += dstride;
+	}
+	cairo_surface_mark_dirty (dst);
 
-  cr = create_image();
-  cairo_translate(cr, SCALE * MARGIN, SCALE * MARGIN);
-
-  cairo_set_source_surface(cr, waved_flag, 0, 0);
-  cairo_append_path(cr, wave_path);
-  if (!debug)
-    cairo_clip_preserve(cr);
-  cairo_paint(cr);
-  if (!border_transparent) {
-    double border_alpha = .5 + fabs(.5 - border_luminosity);
-    double border_width = 3 * SCALE;
-    double border_gray = (1 - border_luminosity) * border_alpha;
-    if (debug)
-      printf("Border: alpha %g width %g gray %g\n",
-             border_alpha, border_width/SCALE, border_gray);
-
-    cairo_save(cr);
-    cairo_set_source_rgba(cr,
-                          border_gray, border_gray, border_gray, border_alpha);
-    cairo_set_line_width(cr, border_width);
-    if (!debug)
-      cairo_set_operator(cr, CAIRO_OPERATOR_HSL_LUMINOSITY);
-    cairo_stroke(cr);
-    cairo_restore(cr);
-  } else {
-    printf("Transparent border\n");
-    cairo_new_path(cr);
-  }
-
-  if (debug) {
-    /* Draw mesh points. */
-    cairo_save(cr);
-    cairo_scale(cr, SIZE/128.*SCALE, SIZE/128.*SCALE);
-    cairo_set_source_rgba(cr, .5, .0, .0, .9);
-    cairo_set_line_cap(cr, CAIRO_LINE_CAP_ROUND);
-    for (unsigned int i = 0;
-         i < sizeof(mesh_points) / sizeof(mesh_points[0]);
-         i++) {
-      cairo_move_to(cr, M(i));
-      cairo_rel_line_to(cr, 0, 0);
-    }
-    cairo_set_line_width(cr, 2);
-    cairo_stroke(cr);
-    for (unsigned int i = 0; i < 4; i++) {
-      cairo_move_to(cr, M(2*i));
-      cairo_line_to(cr, M(2*i+1));
-      cairo_move_to(cr, M(2*i));
-      cairo_line_to(cr, M(7 - 2*i));
-    }
-    cairo_set_line_width(cr, .5);
-    cairo_stroke(cr);
-    cairo_restore(cr);
-  }
-
-  if (!debug) {
-    /* Scale down, 2x at a time, to get best downscaling, because cairo's
-     * downscaling is crap... :( */
-    unsigned int scale = SCALE;
-    while (scale > 1) {
-      cairo_surface_t *old_surface, *new_surface;
-
-      old_surface = cairo_surface_reference(cairo_get_target(cr));
-      assert(scale % 2 == 0);
-      scale /= 2;
-      cairo_destroy(cr);
-      new_surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32,
-                                               (SIZE+2*MARGIN)*scale,
-                                               (SIZE+2*MARGIN)*scale);
-      cr = cairo_create(new_surface);
-      cairo_scale(cr, .5, .5);
-      cairo_set_source_surface(cr, old_surface, 0, 0);
-      cairo_paint(cr);
-      cairo_surface_destroy(old_surface);
-      cairo_surface_destroy(new_surface);
-    }
-  }
-
-  cairo_surface_write_to_png(cairo_get_target(cr), output_filename);
-  cairo_destroy(cr);
+	return dst;
 }
 
-int main(int argc, char **argv) {
-  if (argc != 3) {
-    fprintf(stderr, "Usage: waveflag [-debug] in.png out.png\n");
-    return 1;
-  }
+static void
+wave_flag (const char *filename, const char *out_prefix)
+{
+	static cairo_path_t *standard_wave_path;
+	static cairo_surface_t *standard_wave_surface;
+	cairo_path_t *wave_path;
+	cairo_surface_t *wave_surface;
+	int border_transparent;
+	char out[1000];
+	double aspect = 0;
 
-  if (!strcmp(argv[1], "-debug")) {
-    debug = 1;
-    argc--, argv++;
-  }
+	cairo_surface_t *scaled_flag, *waved_flag;
+	cairo_t *cr;
 
-  wave_flag(argv[1], argv[2]);
+	printf ("Processing %s\n", filename);
 
-  return 0;
+	scaled_flag = load_scaled_flag (filename, &aspect);
+
+	aspect /= std_aspect;
+	aspect = sqrt (aspect); // Discount the effect
+	if (.9 <= aspect && aspect <= 1.1)
+	{
+		printf ("Standard aspect ratio\n");
+		aspect = 1.;
+	}
+
+	if (aspect == 1.)
+	{
+		if (!standard_wave_path)
+			standard_wave_path = wave_path_create (aspect);
+		if (!standard_wave_surface)
+			standard_wave_surface = wave_surface_create (aspect);
+		wave_path = standard_wave_path;
+		wave_surface = standard_wave_surface;
+	}
+	else
+	{
+		wave_path = wave_path_create (aspect);
+		wave_surface = wave_surface_create (aspect);
+	}
+
+
+	border_transparent = border_is_transparent (scaled_flag);
+	waved_flag = texture_map (wave_surface, scaled_flag);
+	cairo_surface_destroy (scaled_flag);
+
+	cr = create_image ();
+	cairo_translate (cr, SCALE * MARGIN, SCALE * MARGIN);
+
+	// Paint waved flag
+	cairo_set_source_surface (cr, waved_flag, 0, 0);
+	cairo_append_path (cr, wave_path);
+	if (!debug)
+		cairo_clip_preserve (cr);
+	cairo_paint (cr);
+
+	// Paint border
+	if (!border_transparent)
+	{
+		double border_alpha = .2;
+		double border_width = 4 * SCALE;
+		double border_gray = 0x42/255.;
+		if (debug)
+			printf ("Border: alpha %g width %g gray %g\n",
+				border_alpha, border_width/SCALE, border_gray);
+
+		cairo_save (cr);
+		cairo_set_source_rgba (cr,
+				       border_gray * border_alpha,
+				       border_gray * border_alpha,
+				       border_gray * border_alpha,
+				       border_alpha);
+		cairo_set_line_width (cr, 2*border_width);
+		if (!debug)
+			cairo_set_operator (cr, CAIRO_OPERATOR_MULTIPLY);
+		cairo_stroke (cr);
+		cairo_restore (cr);
+	}
+	else
+	{
+		printf ("Transparent border\n");
+		cairo_new_path (cr);
+	}
+
+	// Paint shade gradient
+	{
+		cairo_save (cr);
+		cairo_pattern_t *gradient = wave_mesh_create (aspect, 1);
+		cairo_set_source (cr, gradient);
+
+		if (border_transparent)
+		{
+			cairo_set_operator (cr, CAIRO_OPERATOR_ATOP);
+			cairo_paint_with_alpha (cr, .3);
+		}
+		else
+		{
+			cairo_set_operator (cr, CAIRO_OPERATOR_SOFT_LIGHT);
+			cairo_paint (cr);
+		}
+
+		cairo_restore (cr);
+	}
+
+	if (debug)
+	{
+		/* Draw mesh points. */
+		cairo_save (cr);
+		cairo_scale (cr, SIZE/128.*SCALE, SIZE/128.*SCALE);
+		cairo_set_source_rgba (cr, .5,.0,.0,.9);
+		cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND);
+		for (unsigned int i = 0; i < sizeof (mesh_points) / sizeof (mesh_points[0]); i++)
+		{
+			cairo_move_to (cr, M(i));
+			cairo_rel_line_to (cr, 0, 0);
+		}
+		cairo_set_line_width (cr, 2);
+		cairo_stroke (cr);
+		for (unsigned int i = 0; i < 4; i++)
+		{
+			cairo_move_to (cr, M(2*i));
+			cairo_line_to (cr, M(2*i+1));
+			cairo_move_to (cr, M(2*i));
+			cairo_line_to (cr, M(7 - 2*i));
+		}
+		cairo_set_line_width (cr, .5);
+		cairo_stroke (cr);
+		cairo_restore (cr);
+	}
+
+	if (!debug)
+	{
+		/* Scale down, 2x at a time, to get best downscaling, because cairo's
+		 * downscaling is crap... :( */
+		unsigned int scale = SCALE;
+		while (scale > 1)
+		{
+			cairo_surface_t *old_surface, *new_surface;
+
+			old_surface = cairo_surface_reference (cairo_get_target (cr));
+			assert (scale % 2 == 0);
+			scale /= 2;
+			cairo_destroy (cr);
+			new_surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, (SIZE+2*MARGIN)*scale, (SIZE+2*MARGIN)*scale);
+			cr = cairo_create (new_surface);
+			cairo_scale (cr, .5, .5);
+			cairo_set_source_surface (cr, old_surface, 0, 0);
+			cairo_paint (cr);
+			cairo_surface_destroy (old_surface);
+			cairo_surface_destroy (new_surface);
+		}
+	}
+
+	*out = '\0';
+	strcat (out, out_prefix);
+	strcat (out, filename);
+
+	cairo_surface_write_to_png (cairo_get_target (cr), out);
+	cairo_destroy (cr);
+	if (wave_path != standard_wave_path)
+		cairo_path_destroy (wave_path);
+	if (wave_surface != standard_wave_surface)
+		cairo_surface_destroy (wave_surface);
+}
+
+int
+main (int argc, char **argv)
+{
+	const char *out_prefix;
+
+	if (argc < 3)
+	{
+	  fprintf (stderr, "Usage: waveflag [-debug] out-prefix [in.png]...\n");
+	  return 1;
+	}
+
+	if (!strcmp (argv[1], "-debug"))
+	{
+	  debug = 1;
+	  argc--, argv++;
+	}
+
+	out_prefix = argv[1];
+	argc--, argv++;
+
+	for (argc--, argv++; argc; argc--, argv++)
+		wave_flag (*argv, out_prefix);
+
+	return 0;
 }