[gegl] Revert "ctx: update from upstream"



commit 8efbcac89480bb0bc700c3302bd2f7d3c5538f1a
Author: Øyvind Kolås <pippin gimp org>
Date:   Sun Aug 29 21:29:40 2021 +0200

    Revert "ctx: update from upstream"
    
    This reverts commit 5cc8761fcf91d2506f3e07cf6c67c309c91d7b58.
    
    For yet to be determined reasons, this broke the composition gegl.xml
    while not triggering new failures in ctx which in ther interim has
    mostly received optimizations mostly focused on the RGBA u8 encodings.

 gegl/ctx/ctx.h | 53176 +++++++++++++++++++++----------------------------------
 1 file changed, 19807 insertions(+), 33369 deletions(-)
---
diff --git a/gegl/ctx/ctx.h b/gegl/ctx/ctx.h
index bacb219b8..24ca7e99b 100644
--- a/gegl/ctx/ctx.h
+++ b/gegl/ctx/ctx.h
@@ -1,4 +1,3 @@
-/* ctx git commit: b8eb59c */
 /* 
  * ctx.h is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -75,8 +74,6 @@ enum _CtxPixelFormat
   CTX_FORMAT_CMYK8,  //15
   CTX_FORMAT_CMYKA8, //16 
   CTX_FORMAT_CMYKAF, //17
-  CTX_FORMAT_YUV420, //18
-  CTX_FORMAT_RGBA8_SEPARATE_ALPHA, // 19
 };
 typedef enum   _CtxPixelFormat CtxPixelFormat;
 
@@ -298,14 +295,6 @@ void ctx_define_texture (Ctx *ctx,
                          void       *data,
                          char       *ret_eid);
 
-void
-ctx_source_transform (Ctx *ctx, float a, float b,  // hscale, hskew
-                      float c, float d,  // vskew,  vscale
-                      float e, float f);  // htran,  vtran
-typedef struct _CtxMatrix     CtxMatrix;
-void
-ctx_source_transform_matrix (Ctx *ctx, CtxMatrix *matrix);
-
 void
 ctx_get_image_data (Ctx *ctx, int sx, int sy, int sw, int sh,
                     CtxPixelFormat format, int dst_stride,
@@ -439,43 +428,25 @@ int      ctx_unichar_to_utf8 (uint32_t  ch, uint8_t  *dest);
 
 typedef enum
 {
-  CTX_FILL_RULE_WINDING,
-  CTX_FILL_RULE_EVEN_ODD
+  CTX_FILL_RULE_EVEN_ODD,
+  CTX_FILL_RULE_WINDING
 } CtxFillRule;
 
 typedef enum
 {
-#if 0
-  CTX_COMPOSITE_SOURCE_OVER      = 0,
-  CTX_COMPOSITE_COPY             = 32,
-  CTX_COMPOSITE_SOURCE_IN        = 64,
-  CTX_COMPOSITE_SOURCE_OUT       = 96,
-  CTX_COMPOSITE_SOURCE_ATOP      = 128,
-  CTX_COMPOSITE_CLEAR            = 160,
-
-  CTX_COMPOSITE_DESTINATION_OVER = 192,
-  CTX_COMPOSITE_DESTINATION      = 224,
-  CTX_COMPOSITE_DESTINATION_IN   = 256,
-  CTX_COMPOSITE_DESTINATION_OUT  = 288,
-  CTX_COMPOSITE_DESTINATION_ATOP = 320,
-  CTX_COMPOSITE_XOR              = 352,
-
-  CTX_COMPOSITE_ALL              = (32+64+128+256)
-#else
-  CTX_COMPOSITE_SOURCE_OVER      =0,
-  CTX_COMPOSITE_COPY             ,
-  CTX_COMPOSITE_SOURCE_IN        ,
-  CTX_COMPOSITE_SOURCE_OUT       ,
-  CTX_COMPOSITE_SOURCE_ATOP      ,
-  CTX_COMPOSITE_CLEAR            ,
-
-  CTX_COMPOSITE_DESTINATION_OVER ,
-  CTX_COMPOSITE_DESTINATION      ,
-  CTX_COMPOSITE_DESTINATION_IN   ,
-  CTX_COMPOSITE_DESTINATION_OUT  ,
-  CTX_COMPOSITE_DESTINATION_ATOP ,
-  CTX_COMPOSITE_XOR              ,
-#endif
+  CTX_COMPOSITE_SOURCE_OVER,
+  CTX_COMPOSITE_COPY,
+  CTX_COMPOSITE_SOURCE_IN,
+  CTX_COMPOSITE_SOURCE_OUT,
+  CTX_COMPOSITE_SOURCE_ATOP,
+  CTX_COMPOSITE_CLEAR,
+
+  CTX_COMPOSITE_DESTINATION_OVER,
+  CTX_COMPOSITE_DESTINATION,
+  CTX_COMPOSITE_DESTINATION_IN,
+  CTX_COMPOSITE_DESTINATION_OUT,
+  CTX_COMPOSITE_DESTINATION_ATOP,
+  CTX_COMPOSITE_XOR,
 } CtxCompositingMode;
 
 typedef enum
@@ -947,6 +918,7 @@ typedef enum
                          //    data can be a string with a name,
                          //    icc data or perhaps our own serialization
                          //    of profile data
+  CTX_EDGE_FLIPPED     = '`', // x0 y0 x1 y1 - s16
   CTX_REL_ARC_TO       = 'a', // x1 y1 x2 y2 radius
   CTX_CLIP             = 'b',
   CTX_REL_CURVE_TO     = 'c', // cx1 cy1 cx2 cy2 x y
@@ -976,16 +948,13 @@ typedef enum
   CTX_CLOSE_PATH       = 'z', //
   CTX_START_GROUP      = '{',
   CTX_END_GROUP        = '}',
-  CTX_SOURCE_TRANSFORM = '`',
-
-  CTX_EDGE             = '&',                        // will not occur in commandstream
-  CTX_EDGE_FLIPPED     = '^', // x0 y0 x1 y1 - s16   // thus these use reserved entries as code
+  CTX_EDGE             = ',',
 
   /* though expressed as two chars in serialization we have
    * dedicated byte commands for the setters to keep the dispatch
    * simpler. There is no need for these to be human readable thus we go >128
    *
-   * unused:        !&<=>?:.=/\`,
+   * unused:        !&<=>?:.=/\
    * reserved:      '"&   #  %^@
    */
 
@@ -1644,11 +1613,11 @@ typedef enum
 
 enum _CtxAntialias
 {
-  CTX_ANTIALIAS_DEFAULT, // fast - suitable for realtime UI
+  CTX_ANTIALIAS_DEFAULT,
   CTX_ANTIALIAS_NONE, // non-antialiased
-  CTX_ANTIALIAS_FAST, // aa 3    // deprected or is default equal to this now?
-  CTX_ANTIALIAS_GOOD, // aa 5    // this should perhaps still be 5?
-  CTX_ANTIALIAS_BEST  // aa 17   // accurate-suitable for saved assets
+  CTX_ANTIALIAS_FAST, // aa 3
+  CTX_ANTIALIAS_GOOD, // aa 5
+  CTX_ANTIALIAS_BEST  // aa 17
 };
 typedef enum _CtxAntialias CtxAntialias;
 
@@ -1740,7 +1709,7 @@ ctx_parser_set_size (CtxParser *parser,
                      float      cell_width,
                      float      cell_height);
 
-void ctx_parser_feed_bytes (CtxParser *parser, const char *data, int count);
+void ctx_parser_feed_byte (CtxParser *parser, int byte);
 
 int
 ctx_get_contents (const char     *path,
@@ -1758,63 +1727,9 @@ int
 ctx_base642bin (const char    *ascii,
                 int           *length,
                 unsigned char *bin);
-
-
-struct
-  _CtxMatrix
-{
-  float m[3][2];
-};
-
-void ctx_apply_matrix (Ctx *ctx, CtxMatrix *matrix);
-void ctx_matrix_apply_transform (const CtxMatrix *m, float *x, float *y);
-void ctx_matrix_invert (CtxMatrix *m);
-void ctx_matrix_identity (CtxMatrix *matrix);
-void ctx_matrix_scale (CtxMatrix *matrix, float x, float y);
-void ctx_matrix_rotate (CtxMatrix *matrix, float angle);
-void ctx_matrix_multiply (CtxMatrix       *result,
-                          const CtxMatrix *t,
-                          const CtxMatrix *s);
-
-
-
-
 float ctx_term_get_cell_width (Ctx *ctx);
 float ctx_term_get_cell_height (Ctx *ctx);
 
-
-
-
-
-#if 1 // CTX_VT
-
-typedef struct _VT VT;
-void vt_feed_keystring  (VT *vt, CtxEvent *event, const char *str);
-void vt_paste           (VT *vt, const char *str);
-char *vt_get_selection  (VT *vt);
-long vt_rev             (VT *vt);
-int  vt_has_blink       (VT *vt);
-
-int  ctx_clients_need_redraw   (Ctx *ctx);
-void ctx_clients_handle_events (Ctx *ctx);
-
-
-typedef struct _CtxBuffer CtxBuffer;
-CtxBuffer *ctx_buffer_new_for_data (void *data, int width, int height,
-                                    int stride,
-                                    CtxPixelFormat pixel_format,
-                                    void (*freefunc) (void *pixels, void *user_data),
-                                    void *user_data);
-
-
-
-
-
-
-
-#endif
-
-
 #ifndef CTX_CODEC_CHAR
 //#define CTX_CODEC_CHAR '\035'
 //#define CTX_CODEC_CHAR 'a'
@@ -1832,13 +1747,6 @@ CtxBuffer *ctx_buffer_new_for_data (void *data, int width, int height,
 #endif
 #ifndef __CTX_H__
 #define __CTX_H__
-#ifndef _DEFAULT_SOURCE
-#define _DEFAULT_SOURCE
-#endif
-#ifndef _XOPEN_SOURCE
-#define _XOPEN_SOURCE 600
-#endif
-
 #ifndef CTX_STRING_H
 #define CTX_STRING_H
 
@@ -4263,12 +4171,9 @@ static inline CtxList *ctx_list_find_custom (CtxList *list,
 #endif
 
 
-#define CTX_RASTERIZER_AA_SLOPE_LIMIT3          (65536/CTX_SUBDIV/15)
-//#define CTX_RASTERIZER_AA_SLOPE_LIMIT3_FAST_AA  (65536/CTX_SUBDIV/15)
-//#define CTX_RASTERIZER_AA_SLOPE_LIMIT3_FAST_AA (120536/CTX_SUBDIV/15)
-#define CTX_RASTERIZER_AA_SLOPE_LIMIT3_FAST_AA (105000/CTX_SUBDIV/15)
-#define CTX_RASTERIZER_AA_SLOPE_LIMIT5         (140425/CTX_SUBDIV/15)
-#define CTX_RASTERIZER_AA_SLOPE_LIMIT15        (260425/CTX_SUBDIV/15)
+#define CTX_RASTERIZER_AA_SLOPE_LIMIT3    (13424/CTX_SUBDIV/15)
+#define CTX_RASTERIZER_AA_SLOPE_LIMIT5    (20425/CTX_SUBDIV/15)
+#define CTX_RASTERIZER_AA_SLOPE_LIMIT15   (50425/CTX_SUBDIV/15)
 
 /* subpixel-aa coordinates used in BITPACKing of drawlist
  *
@@ -4276,7 +4181,7 @@ static inline CtxList *ctx_list_find_custom (CtxList *list,
  */
 #ifndef CTX_SUBDIV
 #define CTX_SUBDIV   8  //  max framebufer width 4095
-//#define CTX_SUBDIV  10  //  max framebufer width 3250
+//#define CTX_SUBDIV    10  //  max framebufer width 3250
 //#define CTX_SUBDIV  16  //  max framebufer width 2047
 //#define CTX_SUBDIV  24  //  max framebufer width 1350
 //#define CTX_SUBDIV  32  //  max framebufer width 1023
@@ -4371,6 +4276,12 @@ static inline CtxList *ctx_list_find_custom (CtxList *list,
 #define CTX_GRADIENTS             1
 #endif
 
+/* some optinal micro-optimizations that are known to increase code size
+ */
+#ifndef CTX_SORTING_NETWORKS
+#define CTX_SORTING_NETWORKS         1
+#endif
+
 #ifndef CTX_ALIGNED_STRUCTS
 #define CTX_ALIGNED_STRUCTS       1
 #endif
@@ -4380,7 +4291,7 @@ static inline CtxList *ctx_list_find_custom (CtxList *list,
 #endif
 
 #ifndef CTX_FONTS_FROM_FILE
-#define CTX_FONTS_FROM_FILE  0
+#define CTX_FONTS_FROM_FILE 1
 #endif
 
 #ifndef CTX_FORMATTER
@@ -4399,10 +4310,6 @@ static inline CtxList *ctx_list_find_custom (CtxList *list,
 #define CTX_XML             1
 #endif
 
-#ifndef CTX_VT
-#define CTX_VT              0
-#endif
-
 /* when ctx_math is defined, which it is by default, we use ctx' own
  * implementations of math functions, instead of relying on math.h
  * the possible inlining gives us a slight speed-gain, and on
@@ -4462,7 +4369,7 @@ static inline CtxList *ctx_list_find_custom (CtxList *list,
 /* whether we dither or not for gradients
  */
 #ifndef CTX_DITHER
-#define CTX_DITHER 0
+#define CTX_DITHER 1
 #endif
 
 /*  only source-over clear and copy will work, the API still
@@ -4480,8 +4387,7 @@ static inline CtxList *ctx_list_find_custom (CtxList *list,
 #define CTX_FORCE_INLINES               1
 #endif
 
-/* create one-off inlined inner loop for normal blend mode (for floating point,
- * for RGBA8 manual loops overrrides
+/* create one-off inlined inner loop for normal blend mode
  */
 #ifndef CTX_INLINED_NORMAL     
 #define CTX_INLINED_NORMAL      1
@@ -4495,11 +4401,24 @@ static inline CtxList *ctx_list_find_custom (CtxList *list,
 #define CTX_BRAILLE_TEXT        0
 #endif
 
-/* Build code paths for grayscale rasterization, this makes clipping
- * faster.
+/* including immintrin.h triggers building of AVX2 code paths, if - like
+ * sometimes when including SDL one does want it at all do a
+ * #define CTX_AVX2 0  before including ctx.h for implementation.
+ */
+#ifndef CTX_AVX2
+#ifdef _IMMINTRIN_H_INCLUDED
+#define CTX_AVX2         1
+#else
+#define CTX_AVX2         0
+#endif
+#endif
+
+/* Build code paths for grayscale rasterization, normally this is handled
+ * by the RGBA8 codepaths; on microcontrollers with eink this might be
+ * a better option.
  */
 #ifndef CTX_NATIVE_GRAYA8
-#define CTX_NATIVE_GRAYA8       1
+#define CTX_NATIVE_GRAYA8       0
 #endif
 
 /* enable CMYK rasterization targets
@@ -4524,7 +4443,7 @@ static inline CtxList *ctx_list_find_custom (CtxList *list,
 #endif
 
 #ifndef CTX_ENABLE_FLOAT
-#define CTX_ENABLE_FLOAT        0
+#define CTX_ENABLE_FLOAT 0
 #endif
 
 /* by default ctx includes all pixel formats, on microcontrollers
@@ -4533,10 +4452,6 @@ static inline CtxList *ctx_list_find_custom (CtxList *list,
  * manually add CTX_ENABLE_ flags for each of them.
  */
 #if CTX_LIMIT_FORMATS
-#if CTX_NATIVE_GRAYA8
-#define CTX_ENABLE_GRAYA8               1
-#define CTX_ENABLE_GRAY8                1
-#endif
 #else
 
 #define CTX_ENABLE_GRAY1                1
@@ -4558,7 +4473,6 @@ static inline CtxList *ctx_list_find_custom (CtxList *list,
 #undef CTX_ENABLE_FLOAT
 #endif
 #define CTX_ENABLE_FLOAT                1
-#define CTX_ENABLE_YUV420               1
 
 #if CTX_ENABLE_CMYK
 #define CTX_ENABLE_CMYK8                1
@@ -4609,9 +4523,6 @@ static inline CtxList *ctx_list_find_custom (CtxList *list,
 #define CTX_BABL 0
 #endif
 
-#ifndef CTX_ALWAYS_USE_NEAREST_FOR_SCALE1
-#define CTX_ALWAYS_USE_NEAREST_FOR_SCALE1 1
-#endif
 
 /* force add format if we have shape cache */
 #if CTX_SHAPE_CACHE
@@ -4705,11 +4616,7 @@ static inline CtxList *ctx_list_find_custom (CtxList *list,
 
 #define CTX_PI                              3.141592653589793f
 #ifndef CTX_RASTERIZER_MAX_CIRCLE_SEGMENTS
-#define CTX_RASTERIZER_MAX_CIRCLE_SEGMENTS  400
-#endif
-
-#ifndef CTX_MAX_FRAMEBUFFER_WIDTH
-#define CTX_MAX_FRAMEBUFFER_WIDTH 2560
+#define CTX_RASTERIZER_MAX_CIRCLE_SEGMENTS  120
 #endif
 
 #ifndef CTX_MAX_FONTS
@@ -4751,9 +4658,11 @@ static inline CtxList *ctx_list_find_custom (CtxList *list,
 
 
 #define CTX_RASTERIZER_EDGE_MULTIPLIER  1024
-                                        // increasing this to 2048
-                                        // removes artifacts in top half of res-diagram -
-                                        // but reduces maximum available buffer width
+
+#ifndef CTX_COMPOSITE_SUFFIX
+#define CTX_COMPOSITE_SUFFIX(symbol)     symbol##_default
+#endif
+
 #ifndef CTX_IMPLEMENTATION
 #define CTX_IMPLEMENTATION 0
 #else
@@ -4807,7 +4716,7 @@ static inline CtxList *ctx_list_find_custom (CtxList *list,
 #endif
 
 #ifndef CTX_AUDIO
-#define CTX_AUDIO 0
+#define CTX_AUDIO 1
 #endif
 
 #ifndef CTX_TILED
@@ -4818,17 +4727,13 @@ static inline CtxList *ctx_list_find_custom (CtxList *list,
 #endif
 #endif
 
-#ifndef CTX_THREADS
 #if CTX_FB
 #define CTX_THREADS 1
 #else
 #define CTX_THREADS 0
 #endif
-#endif
 
-#if CTX_THREADS
-#include <threads.h>
-#endif
+
  /* Copyright (C) 2020 Øyvind Kolås <pippin gimp org>
  */
 
@@ -4850,10 +4755,10 @@ int ctx_a85len (const char *src, int count);
 
 
 #define CTX_CLAMP(val,min,max) ((val)<(min)?(min):(val)>(max)?(max):(val))
-static inline int   ctx_mini (int a, int b)     { return (a < b) * a + (a >= b) * b; }
-static inline float ctx_minf (float a, float b) { return (a < b) * a + (a >= b) * b; }
-static inline int   ctx_maxi (int a, int b)     { return (a > b) * a + (a <= b) * b; }
-static inline float ctx_maxf (float a, float b) { return (a > b) * a + (a <= b) * b; }
+static inline int   ctx_mini (int a, int b)     { if (a < b) return a; return b; }
+static inline float ctx_minf (float a, float b) { if (a < b) return a; return b; }
+static inline int   ctx_maxi (int a, int b)     { if (a > b) return a; return b; }
+static inline float ctx_maxf (float a, float b) { if (a > b) return a; return b; }
 
 
 typedef enum CtxOutputmode
@@ -4906,39 +4811,6 @@ ctx_invsqrtf (float x)
   return x;
 }
 
-static inline float
-ctx_invsqrtf_fast (float x)
-{
-  void *foo = &x;
-//float xhalf = 0.5f * x;
-  int i=* (int *) foo;
-  void *bar = &i;
-  i = 0x5f3759df - (i >> 1);
-  x = * (float *) bar;
-//x *= (1.5f - xhalf * x * x);
-  return x;
-}
-
-CTX_INLINE static float ctx_sqrtf (float a)
-{
-  return 1.0f/ctx_invsqrtf (a);
-}
-
-CTX_INLINE static float ctx_sqrtf_fast (float a)
-{
-  return 1.0f/ctx_invsqrtf_fast (a);
-}
-
-CTX_INLINE static float ctx_hypotf (float a, float b)
-{
-  return ctx_sqrtf (ctx_pow2 (a)+ctx_pow2 (b) );
-}
-
-CTX_INLINE static float ctx_hypotf_fast (float a, float b)
-{
-  return ctx_sqrtf_fast (ctx_pow2 (a)+ctx_pow2 (b) );
-}
-
 CTX_INLINE static float
 ctx_sinf (float x)
 {
@@ -5020,6 +4892,15 @@ static inline float ctx_atan2f (float y, float x)
   return atan;
 }
 
+CTX_INLINE static float ctx_sqrtf (float a)
+{
+  return 1.0f/ctx_invsqrtf (a);
+}
+
+CTX_INLINE static float ctx_hypotf (float a, float b)
+{
+  return ctx_sqrtf (ctx_pow2 (a)+ctx_pow2 (b) );
+}
 
 static inline float ctx_atanf (float a)
 {
@@ -5054,7 +4935,7 @@ static inline float
 ctx_expf (float x)
 {
   union { uint32_t i; float f; } v =
-    {  (uint32_t)( (1 << 23) * (x + 183.1395965f)) };
+    { (1 << 23) * (x + 183.1395965) };
   return v.f;
 }
 
@@ -5084,11 +4965,14 @@ static inline float _ctx_parse_float (const char *str, char **endptr)
 const char *ctx_get_string (Ctx *ctx, uint64_t hash);
 void ctx_set_string (Ctx *ctx, uint64_t hash, const char *value);
 typedef struct _CtxColor CtxColor;
+typedef struct _CtxBuffer CtxBuffer;
 
-void
-ctx_matrix_translate (CtxMatrix *matrix, float x, float y);
-
-
+typedef struct _CtxMatrix     CtxMatrix;
+struct
+  _CtxMatrix
+{
+  float m[3][2];
+};
 void ctx_get_matrix (Ctx *ctx, CtxMatrix *matrix);
 
 int ctx_color (Ctx *ctx, const char *string);
@@ -5108,6 +4992,17 @@ int ctx_utf8_len (const unsigned char first_byte);
 void ctx_user_to_device          (Ctx *ctx, float *x, float *y);
 void ctx_user_to_device_distance (Ctx *ctx, float *x, float *y);
 const char *ctx_utf8_skip (const char *s, int utf8_length);
+void ctx_apply_matrix (Ctx *ctx, CtxMatrix *matrix);
+void ctx_matrix_apply_transform (const CtxMatrix *m, float *x, float *y);
+void ctx_matrix_invert (CtxMatrix *m);
+void ctx_matrix_identity (CtxMatrix *matrix);
+void ctx_matrix_scale (CtxMatrix *matrix, float x, float y);
+void ctx_matrix_rotate (CtxMatrix *matrix, float angle);
+void ctx_matrix_multiply (CtxMatrix       *result,
+                          const CtxMatrix *t,
+                          const CtxMatrix *s);
+void
+ctx_matrix_translate (CtxMatrix *matrix, float x, float y);
 int ctx_is_set_now (Ctx *ctx, uint64_t hash);
 void ctx_set_size (Ctx *ctx, int width, int height);
 
@@ -5264,8 +5159,6 @@ _ctx_file_get_contents (const char     *path,
 #define CTX_newPath    TOKENHASH(23510545975776)
 #define CTX_new_state  TOKENHASH(16912954280416)
 #define CTX_none       TOKENHASH(492640)
-#define CTX_nonzero     TOKENHASH(37865948256)
-#define CTX_non_zero    TOKENHASH(1211709359200)
 #define CTX_normal     TOKENHASH(946840672)
 #define CTX_quad_to    TOKENHASH(37986115046)
 #define CTX_quadTo     TOKENHASH(1233857682918)
@@ -5321,7 +5214,6 @@ _ctx_file_get_contents (const char     *path,
 #define CTX_sourceOut  TOKENHASH(7371294932695718)
 #define CTX_source_out         TOKENHASH(3851660580666474)
 #define CTX_sourceOver         TOKENHASH(7584784067385004)
-#define CTX_sourceTransform TOKENHASH(7515321363744130)
 #define CTX_source_over        TOKENHASH(8690648756484770)
 #define CTX_square     TOKENHASH(511950058)
 #define CTX_start      TOKENHASH(47455658)
@@ -5616,7 +5508,6 @@ struct _CtxGradient
 struct _CtxSource
 {
   int type;
-  CtxMatrix  set_transform;
   CtxMatrix  transform;
   union
   {
@@ -5625,6 +5516,8 @@ struct _CtxSource
     {
       uint8_t rgba[4]; // shares data with set color
       uint8_t pad;
+      float x0;
+      float y0;
       CtxBuffer *buffer;
     } texture;
     struct
@@ -5976,7 +5869,7 @@ struct _Ctx
 };
 
 
-static void ctx_process (Ctx *ctx, CtxEntry *entry);
+void ctx_process (Ctx *ctx, CtxEntry *entry);
 CtxBuffer *ctx_buffer_new (int width, int height,
                            CtxPixelFormat pixel_format);
 void ctx_buffer_free (CtxBuffer *buffer);
@@ -5984,10 +5877,10 @@ void ctx_buffer_free (CtxBuffer *buffer);
 void
 ctx_state_gradient_clear_stops (CtxState *state);
 
-static inline void ctx_interpret_style         (CtxState *state, CtxEntry *entry, void *data);
-static inline void ctx_interpret_transforms    (CtxState *state, CtxEntry *entry, void *data);
-static inline void ctx_interpret_pos           (CtxState *state, CtxEntry *entry, void *data);
-static inline void ctx_interpret_pos_transform (CtxState *state, CtxEntry *entry, void *data);
+void ctx_interpret_style         (CtxState *state, CtxEntry *entry, void *data);
+void ctx_interpret_transforms    (CtxState *state, CtxEntry *entry, void *data);
+void ctx_interpret_pos           (CtxState *state, CtxEntry *entry, void *data);
+void ctx_interpret_pos_transform (CtxState *state, CtxEntry *entry, void *data);
 
 struct _CtxInternalFsEntry
 {
@@ -6020,14 +5913,14 @@ struct _CtxPixelFormatInfo
 };
 
 
-static inline void
+static void
 _ctx_user_to_device (CtxState *state, float *x, float *y);
 static void
 _ctx_user_to_device_distance (CtxState *state, float *x, float *y);
 static void ctx_state_init (CtxState *state);
-static inline void
+void
 ctx_interpret_pos_bare (CtxState *state, CtxEntry *entry, void *data);
-static inline void
+void
 ctx_drawlist_deinit (CtxDrawlist *drawlist);
 
 CtxPixelFormatInfo *
@@ -6047,6 +5940,17 @@ ctx_utf8_to_unichar (const char *input);
 
 typedef struct _CtxHasher CtxHasher;
 
+typedef struct CtxEdge
+{
+#if CTX_ALIGNED_STRUCTS==1
+  uint32_t index;
+#else
+  uint16_t index;
+#endif
+  int32_t  val;     /* the center-line intersection      */
+  int32_t  delta;
+} CtxEdge;
+
 typedef void (*CtxFragment) (CtxRasterizer *rasterizer, float x, float y, void *out, int count, float dx, 
float dy);
 
 #define CTX_MAX_GAUSSIAN_KERNEL_DIM    512
@@ -6072,13 +5976,6 @@ struct _CtxShapeCache
 
 typedef struct _CtxShapeCache CtxShapeCache;
 
-typedef enum {
-   CTX_COV_PATH_FALLBACK =0,
-   CTX_COV_PATH_OVER,
-   CTX_COV_PATH_COPY,
-   CTX_COV_PATH_COPY_FRAGMENT,
-   CTX_COV_PATH_OVER_FRAGMENT
-} CtxCovPath;
 
 struct _CtxRasterizer
 {
@@ -6093,16 +5990,15 @@ struct _CtxRasterizer
   float      kernel[CTX_MAX_GAUSSIAN_KERNEL_DIM];
 #endif
 
-  unsigned int aa;          // level of vertical aa
-  int fast_aa;
-  int prev_active_edges;
-  int active_edges;
-  int pending_edges;
-  int ending_edges;
-  int edge_pos;         // where we're at in iterating all edges
-  int edges[CTX_MAX_EDGES]; // integer position in edge array
+  int        aa;          // level of vertical aa
+  int        active_edges;
+  int        active_edges2;
+  int        pending_edges;   // this-scanline
+  int        ending_edges;    // count of edges ending this scanline
+  int        edge_pos;         // where we're at in iterating all edges
+  CtxEdge    edges[CTX_MAX_EDGES];
 
-  int scanline;
+  int        scanline;
   int        scan_min;
   int        scan_max;
   int        col_min;
@@ -6128,14 +6024,13 @@ struct _CtxRasterizer
 
   float      first_x;
   float      first_y;
-  unsigned int needs_aa3; // count of how many edges implies antialiasing
-  unsigned int needs_aa5; // count of how many edges implies antialiasing
-  unsigned int needs_aa15; // count of how many edges implies antialiasing
-  int        horizontal_edges;
-  int        uses_transforms;
+  int8_t     needs_aa3; // count of how many edges implies antialiasing
+  int8_t     needs_aa5; // count of how many edges implies antialiasing
+  int8_t     needs_aa15; // count of how many edges implies antialiasing
   int        has_shape:2;
   int        has_prev:2;
   int        preserve:1;
+  int        uses_transforms:1;
 
   int16_t    blit_x;
   int16_t    blit_y;
@@ -6159,8 +6054,6 @@ struct _CtxRasterizer
 #define CTX_COMPOSITE_ARGUMENTS CtxRasterizer *rasterizer, uint8_t * __restrict__ dst, uint8_t * 
__restrict__ src, int x0, uint8_t * __restrict__ coverage, int count
 
   void (*comp_op)(CTX_COMPOSITE_ARGUMENTS);
-  CtxCovPath  comp;
-
 #if CTX_ENABLE_CLIP
   CtxBuffer *clip_buffer;
 #endif
@@ -6318,11 +6211,11 @@ static void ctx_color_set_graya (CtxState *state, CtxColor *color, float gray, f
 
 int ctx_color_model_get_components (CtxColorModel model);
 
-static void ctx_state_set (CtxState *state, uint64_t key, float value);
+void ctx_state_set (CtxState *state, uint64_t key, float value);
 static void
 ctx_matrix_set (CtxMatrix *matrix, float a, float b, float c, float d, float e, float f);
 static void ctx_font_setup ();
-static float ctx_state_get (CtxState *state, uint64_t hash);
+float ctx_state_get (CtxState *state, uint64_t hash);
 
 #if CTX_RASTERIZER
 
@@ -6395,6 +6288,12 @@ ctx_rasterizer_set_pixel (CtxRasterizer *rasterizer,
                           uint8_t b,
                           uint8_t a);
 static void
+ctx_rasterizer_rectangle (CtxRasterizer *rasterizer,
+                          float x,
+                          float y,
+                          float width,
+                          float height);
+static void
 ctx_rasterizer_round_rectangle (CtxRasterizer *rasterizer, float x, float y, float width, float height, 
float corner_radius);
 
 #endif
@@ -6425,70 +6324,6 @@ CTX_INLINE static uint8_t ctx_lerp_u8 (uint8_t v0, uint8_t v1, uint8_t dx)
 #endif
 }
 
-CTX_INLINE static uint32_t ctx_lerp_RGBA8 (const uint32_t v0, const uint32_t v1, const uint8_t dx)
-{
-#if 0
-  char bv0[4];
-  char bv1[4];
-  char res[4];
-  memcpy (&bv0[0], &v0, 4);
-  memcpy (&bv1[0], &v1, 4);
-  for (int c = 0; c < 4; c++)
-    res [c] = ctx_lerp_u8 (bv0[c], bv1[c], dx);
-  return ((uint32_t*)(&res[0]))[0];
-#else
-  const uint32_t cov = dx;
-  const uint32_t si_ga = (v1 & 0xff00ff00);
-  const uint32_t si_rb = v1 & 0x00ff00ff;
-  const uint32_t di_rb = v0 & 0x00ff00ff;
-  const uint32_t d_rb = si_rb - di_rb;
-  const uint32_t di_ga = v0 & 0xff00ff00;
-  const uint32_t d_ga = (si_ga >>8) - (di_ga>>8);
-  return
-     (((di_rb + ((0xff00ff + d_rb * cov)>>8)) & 0x00ff00ff)) |
-     (((di_ga + (0xff00ff + d_ga * cov))      & 0xff00ff00));
-
-#endif
-}
-
-CTX_INLINE static void ctx_lerp_RGBA8_split (const uint32_t v0, const uint32_t v1, const uint8_t dx,
-                                             uint32_t *dest_ga, uint32_t *dest_rb)
-{
-  const uint32_t cov = dx;
-  const uint32_t si_ga = (v1 & 0xff00ff00);
-  const uint32_t si_rb = v1 & 0x00ff00ff;
-  const uint32_t di_rb = v0 & 0x00ff00ff;
-  const uint32_t di_ga = v0 & 0xff00ff00;
-  const uint32_t d_rb = si_rb - di_rb;
-  const uint32_t d_ga = (si_ga >>8) - (di_ga >> 8);
-  *dest_rb = (((di_rb + ((0xff00ff + d_rb * cov)>>8)) & 0x00ff00ff));
-  *dest_ga = (((di_ga + (0xff00ff + d_ga * cov))      & 0xff00ff00));
-}
-
-CTX_INLINE static uint32_t ctx_lerp_RGBA8_merge (uint32_t di_ga, uint32_t di_rb, uint32_t si_ga, uint32_t 
si_rb, const uint8_t dx)
-{
-  const uint32_t cov = dx;
-  const uint32_t d_rb = si_rb - di_rb;
-  const uint32_t d_ga = (si_ga - di_ga) >> 8;
-  return
-     (((di_rb + ((0xff00ff + d_rb * cov)>>8)) & 0x00ff00ff))  |
-      ((di_ga + ((0xff00ff + d_ga * cov)      & 0xff00ff00)));
-}
-
-CTX_INLINE static uint32_t ctx_lerp_RGBA8_2 (const uint32_t v0, uint32_t si_ga, uint32_t si_rb, const 
uint8_t dx)
-{
-  const uint32_t cov = dx;
-  const uint32_t di_ga = ( v0 & 0xff00ff00);
-  const uint32_t di_rb = v0 & 0x00ff00ff;
-  const uint32_t d_rb = si_rb - di_rb;
-  const uint32_t d_ga = si_ga - (di_ga>>8);
-  return
-     (((di_rb + ((0xff00ff + d_rb * cov)>>8)) & 0x00ff00ff)) |
-     (((di_ga + (0xff00ff + d_ga * cov))      & 0xff00ff00));
-}
-
-
-
 CTX_INLINE static float
 ctx_lerpf (float v0, float v1, float dx)
 {
@@ -6585,21 +6420,13 @@ struct _CtxTiled
 #endif
 };
 
-static void
+void
 _ctx_texture_prepare_color_management (CtxRasterizer *rasterizer,
                                       CtxBuffer     *buffer);
 
 #endif
 
 
-uint64_t    thash              (const char *utf8);
-const char *thash_decode       (uint64_t hash);
-uint64_t    ctx_strhash        (const char *str);
-CtxColor   *ctx_color_new      (void);
-int         ctx_get_int        (Ctx *ctx, uint64_t hash);
-int         ctx_get_is_set     (Ctx *ctx, uint64_t hash);
-Ctx        *ctx_new_for_buffer (CtxBuffer *buffer);
-
 #if CTX_IMPLEMENTATION
 
 #define SHA1_IMPLEMENTATION
@@ -7499,7 +7326,7 @@ int ctx_a85dec (const char *src, char *dst, int count)
   {
     p = src[i];
     val *= 85;
-    if (CTX_UNLIKELY(p == '~'))
+    if (p == '~')
     {
       break;
     }
@@ -7517,10 +7344,10 @@ int ctx_a85dec (const char *src, char *dst, int count)
       k = 0;
     }
 #endif
-    else if (CTX_LIKELY(p >= '!' && p <= 'u'))
+    else if (p >= '!' && p <= 'u')
     {
       val += p-'!';
-      if (CTX_UNLIKELY (k % 5 == 4))
+      if (k % 5 == 4)
       {
          for (int j = 0; j < 4; j++)
          {
@@ -7533,7 +7360,7 @@ int ctx_a85dec (const char *src, char *dst, int count)
     }
     // we treat all other chars as whitespace
   }
-  if (CTX_LIKELY (p != '~'))
+  if (p != '~')
   { 
     val *= 85;
   }
@@ -7558,7 +7385,7 @@ int ctx_a85dec (const char *src, char *dst, int count)
   return out_len;
 }
 
-#if 1
+#if 0
 int ctx_a85len (const char *src, int count)
 {
   int out_len = 0;
@@ -7665,8 +7492,8 @@ typedef struct EncodeUtf5 {
   uint32_t current;
 } EncodeUtf5;
 
-static void thash_encode_utf5 (const char *input, int inlen,
-                               char *output, int *r_outlen)
+void thash_encode_utf5 (const char *input, int inlen,
+                   char *output, int *r_outlen)
 {
   uint32_t offset = THASH_START_OFFSET;
 
@@ -7763,7 +7590,7 @@ static void thash_encode_utf5 (const char *input, int inlen,
   *r_outlen = len;
 }
 
-static inline uint64_t _thash (const char *utf8)
+uint64_t _thash (const char *utf8)
 {
   char encoded[4096]="";
   int  encoded_len=0;
@@ -7822,6 +7649,7 @@ static int interned_compare (const void *a, const void *b)
   return 0;
 }
 
+
 uint64_t thash (const char *utf8)
 {
   uint64_t hash = _thash (utf8);
@@ -7855,9 +7683,7 @@ uint64_t thash (const char *utf8)
   }
   return hash;
 }
-uint64_t ctx_strhash(const char *str) {
-  return thash (str);
-}
+uint64_t ctx_strhash(const char *str, int ignored) { return thash (str);}
 
 typedef struct ThashUtf5Dec {
   int      is_utf5;
@@ -7882,7 +7708,7 @@ static void thash_decode_utf5_append_unichar_as_utf8 (uint32_t unichar, void *wr
   data->buf[data->length]=0;
 }
 
-static void thash_decode_utf5 (ThashUtf5Dec *dec, uint8_t in)
+void thash_decode_utf5 (ThashUtf5Dec *dec, uint8_t in)
 {
   if (dec->is_utf5)
   {
@@ -7924,7 +7750,7 @@ static void thash_decode_utf5 (ThashUtf5Dec *dec, uint8_t in)
   }
 }
 
-static void thash_decode_utf5_bytes (int is_utf5, 
+void thash_decode_utf5_bytes (int is_utf5, 
                         const unsigned char *input, int inlen,
                         char *output, int *r_outlen)
 {
@@ -8214,109 +8040,6 @@ ctx_base642bin (const char    *ascii,
     *length= outputno;
   return outputno;
 }
-#include <stdio.h>
-#include <string.h>
-
-static int ctx_yenc (const char *src, char *dst, int count)
-{
-  int out_len = 0;
-  for (int i = 0; i < count; i ++)
-  {
-    int o = (src[i] + 42) % 256;
-    switch (o)
-    {
-      case 0x00: //null
-      case 0x20: //space// but better safe
-      case 0x0A: //lf   // than sorry
-      case 0x0D: //cr
-      case 0x09: //tab  // not really needed
-      case 0x10: //datalink escape (used by ctx)
-      case 0x11: //xoff
-      case 0x13: //xon
-      case 0x1b: //
-      case 0xff: //
-      case 0x3D: //=
-        dst[out_len++] = '=';
-        o = (o + 64) % 256;
-        /* FALLTHROUGH */
-      default:
-        dst[out_len++] = o;
-        break;
-    }
-  }
-  dst[out_len]=0;
-  return out_len;
-}
-
-static int ctx_ydec (const char *tmp_src, char *dst, int count)
-{
-  const char *src = tmp_src;
-#if 0
-  if (tmp_src == dst)
-  {
-    src = malloc (count);
-    memcpy (src, tmp_src, count);
-  }
-#endif
-  int out_len = 0;
-  for (int i = 0; i < count; i ++)
-  {
-    int o = src[i];
-    switch (o)
-    {
-      case '=':
-        i++;
-        o = src[i];
-        if (o == 'y')
-        {
-          dst[out_len]=0;
-#if 0
-          if (tmp_src == dst) free (src);
-#endif
-          return out_len;
-        }
-        o = (o-42-64) % 256;
-        dst[out_len++] = o;
-        break;
-      case '\n':
-      case '\e':
-      case '\r':
-      case '\0':
-        break;
-      default:
-        o = (o-42) % 256;
-        dst[out_len++] = o;
-        break;
-    }
-  }
-  dst[out_len]=0;
-#if 0
-  if (tmp_src == dst) free (src);
-#endif
-  return out_len;
-}
-
-#if 0
-int main (){
-  char *input="this is a testæøåÅØ'''\"!:_asdac\n\r";
-  char  encoded[256]="";
-  char  decoded[256]="";
-  int   in_len = strlen (input);
-  int   out_len;
-  int   dec_len;
-
-  printf ("input: %s\n", input);
-
-  out_len = ctx_yenc (input, encoded, in_len);
-  printf ("encoded: %s\n", encoded);
-
-  dec_len = ydec (encoded, encoded, out_len);
-
-  printf ("decoded: %s\n", encoded);
-
-  return 0;
-}
-#endif
 #ifndef CTX_DRAWLIST_H
 #define CTX_DRAWLIST_H
 
@@ -8345,7 +8068,7 @@ int ctx_drawlist_add_data (CtxDrawlist *drawlist, const void *data, int length);
 
 static CtxEntry
 ctx_void (CtxCode code);
-static inline CtxEntry
+static CtxEntry
 ctx_f (CtxCode code, float x, float y);
 static CtxEntry
 ctx_u32 (CtxCode code, uint32_t x, uint32_t y);
@@ -8362,7 +8085,7 @@ ctx_u8 (CtxCode code,
         uint8_t e, uint8_t f, uint8_t g, uint8_t h);
 
 #define CTX_PROCESS_VOID(cmd) do {\
-  CtxEntry command = {cmd};\
+  CtxEntry command = ctx_void (cmd); \
   ctx_process (ctx, &command);}while(0) \
 
 #define CTX_PROCESS_F(cmd, x, y) do {\
@@ -8402,19 +8125,6 @@ ctx_process_cmd_str_float (Ctx *ctx, CtxCode code, const char *string, float arg
 static void
 ctx_process_cmd_str_with_len (Ctx *ctx, CtxCode code, const char *string, uint32_t arg0, uint32_t arg1, int 
len);
 
-#pragma pack(push,1)
-typedef struct 
-CtxSegment {
-  uint16_t code;
-  union {
-   int16_t s16[4];
-   uint32_t u32[2];
-  } data;
-  int32_t val;
-  int32_t delta;
-} CtxSegment;
-#pragma pack(pop)
-
 #endif
 
 #ifndef __CTX_UTIL_H
@@ -8556,7 +8266,6 @@ __ctx_file_get_contents (const char     *path,
 #endif
 
 
-
 static inline int
 ctx_conts_for_entry (CtxEntry *entry)
 {
@@ -8574,7 +8283,6 @@ ctx_conts_for_entry (CtxEntry *entry)
       case CTX_CURVE_TO:
       case CTX_REL_CURVE_TO:
       case CTX_APPLY_TRANSFORM:
-      case CTX_SOURCE_TRANSFORM:
       case CTX_COLOR:
       case CTX_ROUND_RECTANGLE:
       case CTX_SHADOW_COLOR:
@@ -8603,6 +8311,7 @@ ctx_conts_for_entry (CtxEntry *entry)
           int pix_len = entry[2 + eid_len + 1].data.u32[1];
           return eid_len + pix_len + 2 + 1;
         }
+
       default:
         return 0;
     }
@@ -8704,7 +8413,7 @@ ctx_iterator_next (CtxIterator *iterator)
 #if CTX_BITPACK
   int expand_bitpack = iterator->flags & CTX_ITERATOR_EXPAND_BITPACK;
 again:
-  if (CTX_UNLIKELY(iterator->bitpack_length))
+  if (iterator->bitpack_length)
     {
       ret = &iterator->bitpack_command[iterator->bitpack_pos];
       iterator->bitpack_pos += (ctx_conts_for_entry (ret) + 1);
@@ -8717,7 +8426,7 @@ again:
 #endif
   ret = _ctx_iterator_next (iterator);
 #if CTX_BITPACK
-  if (CTX_UNLIKELY(ret && expand_bitpack))
+  if (ret && expand_bitpack)
     switch ((CtxCode)(ret->code))
       {
         case CTX_REL_CURVE_TO_REL_LINE_TO:
@@ -8803,7 +8512,6 @@ again:
         case CTX_CURVE_TO:
         case CTX_REL_CURVE_TO:
         case CTX_APPLY_TRANSFORM:
-        case CTX_SOURCE_TRANSFORM:
         case CTX_ROUND_RECTANGLE:
         case CTX_TEXT:
         case CTX_STROKE_TEXT:
@@ -8884,15 +8592,14 @@ static void ctx_drawlist_compact (CtxDrawlist *drawlist);
 static void
 ctx_drawlist_resize (CtxDrawlist *drawlist, int desired_size)
 {
-  int flags=drawlist->flags;
 #if CTX_DRAWLIST_STATIC
-  if (flags & CTX_DRAWLIST_EDGE_LIST)
+  if (drawlist->flags & CTX_DRAWLIST_EDGE_LIST)
     {
-      static CtxSegment sbuf[CTX_MAX_EDGE_LIST_SIZE];
-      drawlist->entries = (CtxEntry*)&sbuf[0];
+      static CtxEntry sbuf[CTX_MAX_EDGE_LIST_SIZE];
+      drawlist->entries = &sbuf[0];
       drawlist->size = CTX_MAX_EDGE_LIST_SIZE;
     }
-  else if (flags & CTX_DRAWLIST_CURRENT_PATH)
+  else if (drawlist->flags & CTX_DRAWLIST_CURRENT_PATH)
     {
       static CtxEntry sbuf[CTX_MAX_EDGE_LIST_SIZE];
       drawlist->entries = &sbuf[0];
@@ -8909,49 +8616,47 @@ ctx_drawlist_resize (CtxDrawlist *drawlist, int desired_size)
   int new_size = desired_size;
   int min_size = CTX_MIN_JOURNAL_SIZE;
   int max_size = CTX_MAX_JOURNAL_SIZE;
-  if ((flags & CTX_DRAWLIST_EDGE_LIST))
+  if ((drawlist->flags & CTX_DRAWLIST_EDGE_LIST))
     {
       min_size = CTX_MIN_EDGE_LIST_SIZE;
       max_size = CTX_MAX_EDGE_LIST_SIZE;
     }
-  else if (flags & CTX_DRAWLIST_CURRENT_PATH)
+  else if (drawlist->flags & CTX_DRAWLIST_CURRENT_PATH)
     {
       min_size = CTX_MIN_EDGE_LIST_SIZE;
       max_size = CTX_MAX_EDGE_LIST_SIZE;
     }
   else
     {
-#if 0
       ctx_drawlist_compact (drawlist);
-#endif
     }
 
-  if (CTX_UNLIKELY(new_size < drawlist->size))
+  if (new_size < drawlist->size)
     { return; }
-  if (CTX_UNLIKELY(drawlist->size == max_size))
+  if (drawlist->size == max_size)
     { return; }
-  new_size = ctx_maxi (new_size, min_size);
-  //if (new_size < drawlist->count)
-  //  { new_size = drawlist->count + 4; }
-  new_size = ctx_mini (new_size, max_size);
+  if (new_size < min_size)
+    { new_size = min_size; }
+  if (new_size < drawlist->count)
+    { new_size = drawlist->count + 4; }
+  if (new_size >= max_size)
+    { new_size = max_size; }
   if (new_size != drawlist->size)
     {
-      int item_size = sizeof (CtxEntry);
-      if (flags & CTX_DRAWLIST_EDGE_LIST) item_size = sizeof (CtxSegment);
-      //fprintf (stderr, "growing drawlist %p %i to %d from %d\n", drawlist, flags, new_size, 
drawlist->size);
+      //fprintf (stderr, "growing drawlist %p %i to %d from %d\n", drawlist, drawlist->flags, new_size, 
drawlist->size);
   if (drawlist->entries)
     {
       //printf ("grow %p to %d from %d\n", drawlist, new_size, drawlist->size);
-      CtxEntry *ne =  (CtxEntry *) malloc (item_size * new_size);
-      memcpy (ne, drawlist->entries, drawlist->size * item_size );
+      CtxEntry *ne =  (CtxEntry *) malloc (sizeof (CtxEntry) * new_size);
+      memcpy (ne, drawlist->entries, drawlist->size * sizeof (CtxEntry) );
       free (drawlist->entries);
       drawlist->entries = ne;
-      //drawlist->entries = (CtxEntry*)malloc (drawlist->entries, item_size * new_size);
+      //drawlist->entries = (CtxEntry*)malloc (drawlist->entries, sizeof (CtxEntry) * new_size);
     }
   else
     {
       //fprintf (stderr, "allocating for %p %d\n", drawlist, new_size);
-      drawlist->entries = (CtxEntry *) malloc (item_size * new_size);
+      drawlist->entries = (CtxEntry *) malloc (sizeof (CtxEntry) * new_size);
     }
   drawlist->size = new_size;
     }
@@ -8959,35 +8664,34 @@ ctx_drawlist_resize (CtxDrawlist *drawlist, int desired_size)
 #endif
 }
 
-static inline int
+static int
 ctx_drawlist_add_single (CtxDrawlist *drawlist, CtxEntry *entry)
 {
   int max_size = CTX_MAX_JOURNAL_SIZE;
   int ret = drawlist->count;
-  int flags = drawlist->flags;
-  if (CTX_LIKELY((flags & CTX_DRAWLIST_EDGE_LIST ||
-       flags & CTX_DRAWLIST_CURRENT_PATH)))
+  if (drawlist->flags & CTX_DRAWLIST_EDGE_LIST)
+    {
+      max_size = CTX_MAX_EDGE_LIST_SIZE;
+    }
+  else if (drawlist->flags & CTX_DRAWLIST_CURRENT_PATH)
     {
       max_size = CTX_MAX_EDGE_LIST_SIZE;
     }
-  if (CTX_UNLIKELY(flags & CTX_DRAWLIST_DOESNT_OWN_ENTRIES))
+  if (drawlist->flags & CTX_DRAWLIST_DOESNT_OWN_ENTRIES)
     {
       return ret;
     }
-  if (CTX_UNLIKELY(ret + 64 >= drawlist->size - 40))
+  if (ret + 1024 >= drawlist->size - 40)
     {
       int new_ = CTX_MAX (drawlist->size * 2, ret + 1024);
       ctx_drawlist_resize (drawlist, new_);
     }
 
-  if (CTX_UNLIKELY(drawlist->count >= max_size - 20))
+  if (drawlist->count >= max_size - 20)
     {
       return 0;
     }
-  if ((flags & CTX_DRAWLIST_EDGE_LIST))
-    ((CtxSegment*)(drawlist->entries))[drawlist->count] = *(CtxSegment*)entry;
-  else
-    drawlist->entries[drawlist->count] = *entry;
+  drawlist->entries[drawlist->count] = *entry;
   ret = drawlist->count;
   drawlist->count++;
   return ret;
@@ -8999,7 +8703,7 @@ ctx_add_single (Ctx *ctx, void *entry)
   return ctx_drawlist_add_single (&ctx->drawlist, (CtxEntry *) entry);
 }
 
-static inline int
+static int
 ctx_drawlist_add_entry (CtxDrawlist *drawlist, CtxEntry *entry)
 {
   int length = ctx_conts_for_entry (entry) + 1;
@@ -9066,7 +8770,7 @@ int ctx_set_drawlist (Ctx *ctx, void *data, int length)
     {
       return -1;
     }
-  if (CTX_UNLIKELY(length % 9)) return -1;
+  if (length % 9) return -1;
   ctx_drawlist_resize (drawlist, length/9);
   memcpy (drawlist->entries, data, length);
   drawlist->count = length / 9;
@@ -9086,7 +8790,7 @@ const CtxEntry *ctx_get_drawlist (Ctx *ctx)
 int
 ctx_add_data (Ctx *ctx, void *data, int length)
 {
-  if (CTX_UNLIKELY(length % sizeof (CtxEntry) ))
+  if (length % sizeof (CtxEntry) )
     {
       //ctx_log("err\n");
       return -1;
@@ -9114,14 +8818,14 @@ int ctx_drawlist_add_data (CtxDrawlist *drawlist, const void *data, int length)
   entry.data.u32[0] = 0;
   entry.data.u32[1] = 0;
   int ret = ctx_drawlist_add_single (drawlist, &entry);
-  if (CTX_UNLIKELY(!data)) { return -1; }
+  if (!data) { return -1; }
   int length_in_blocks;
   if (length <= 0) { length = strlen ( (char *) data) + 1; }
   length_in_blocks = length / sizeof (CtxEntry);
   length_in_blocks += (length % sizeof (CtxEntry) ) ?1:0;
   if (drawlist->count + length_in_blocks + 4 > drawlist->size)
     { ctx_drawlist_resize (drawlist, drawlist->count * 1.2 + length_in_blocks + 32); }
-  if (CTX_UNLIKELY(drawlist->count >= drawlist->size))
+  if (drawlist->count >= drawlist->size)
     { return -1; }
   drawlist->count += length_in_blocks;
   drawlist->entries[ret].data.u32[0] = length;
@@ -9141,19 +8845,20 @@ int ctx_drawlist_add_data (CtxDrawlist *drawlist, const void *data, int length)
   return ret;
 }
 
-static inline CtxEntry
+static CtxEntry
 ctx_void (CtxCode code)
 {
   CtxEntry command;
   command.code = code;
+  command.data.u32[0] = 0;
+  command.data.u32[1] = 0;
   return command;
 }
 
-static inline CtxEntry
+static CtxEntry
 ctx_f (CtxCode code, float x, float y)
 {
-  CtxEntry command;
-  command.code = code;
+  CtxEntry command = ctx_void (code);
   command.data.f[0] = x;
   command.data.f[1] = y;
   return command;
@@ -9182,20 +8887,7 @@ ctx_s32 (CtxCode code, int32_t x, int32_t y)
 static inline CtxEntry
 ctx_s16 (CtxCode code, int x0, int y0, int x1, int y1)
 {
-  CtxEntry command;
-  command.code = code;
-  command.data.s16[0] = x0;
-  command.data.s16[1] = y0;
-  command.data.s16[2] = x1;
-  command.data.s16[3] = y1;
-  return command;
-}
-
-static inline CtxSegment
-ctx_segment_s16 (CtxCode code, int x0, int y0, int x1, int y1)
-{
-  CtxSegment command;
-  command.code = code;
+  CtxEntry command = ctx_void (code);
   command.data.s16[0] = x0;
   command.data.s16[1] = y0;
   command.data.s16[2] = x1;
@@ -9208,8 +8900,7 @@ ctx_u8 (CtxCode code,
         uint8_t a, uint8_t b, uint8_t c, uint8_t d,
         uint8_t e, uint8_t f, uint8_t g, uint8_t h)
 {
-  CtxEntry command;
-  command.code = code;
+  CtxEntry command = ctx_void (code);
   command.data.u8[0] = a;
   command.data.u8[1] = b;
   command.data.u8[2] = c;
@@ -9222,7 +8913,7 @@ ctx_u8 (CtxCode code,
 }
 
 #define CTX_PROCESS_VOID(cmd) do {\
-  CtxEntry command = {cmd};\
+  CtxEntry command = ctx_void (cmd); \
   ctx_process (ctx, &command);}while(0) \
 
 #define CTX_PROCESS_F(cmd, x, y) do {\
@@ -9604,36 +9295,20 @@ uint8_t *ctx_define_texture_pixel_data (CtxEntry *entry)
 {
   return &entry[2 + 1 + 1 + ctx_conts_for_entry (&entry[2])].data.u8[0];
 }
-
 #ifndef __CTX_TRANSFORM
 #define __CTX_TRANSFORM
 
-static inline void
-_ctx_matrix_apply_transform (const CtxMatrix *m, float *x, float *y)
-{
-  float x_in = *x;
-  float y_in = *y;
-  *x = ( (x_in * m->m[0][0]) + (y_in * m->m[1][0]) + m->m[2][0]);
-  *y = ( (y_in * m->m[1][1]) + (x_in * m->m[0][1]) + m->m[2][1]);
-}
-
-void
-ctx_matrix_apply_transform (const CtxMatrix *m, float *x, float *y)
-{
-  _ctx_matrix_apply_transform (m, x, y);
-}
-
-static inline void
+static void
 _ctx_user_to_device (CtxState *state, float *x, float *y)
 {
-  _ctx_matrix_apply_transform (&state->gstate.transform, x, y);
+  ctx_matrix_apply_transform (&state->gstate.transform, x, y);
 }
 
 static void
 _ctx_user_to_device_distance (CtxState *state, float *x, float *y)
 {
   const CtxMatrix *m = &state->gstate.transform;
-  _ctx_matrix_apply_transform (m, x, y);
+  ctx_matrix_apply_transform (m, x, y);
   *x -= m->m[2][0];
   *y -= m->m[2][1];
 }
@@ -9658,8 +9333,8 @@ ctx_matrix_set (CtxMatrix *matrix, float a, float b, float c, float d, float e,
   matrix->m[2][1] = f;
 }
 
-static inline void
-_ctx_matrix_identity (CtxMatrix *matrix)
+void
+ctx_matrix_identity (CtxMatrix *matrix)
 {
   matrix->m[0][0] = 1.0f;
   matrix->m[0][1] = 0.0f;
@@ -9670,15 +9345,9 @@ _ctx_matrix_identity (CtxMatrix *matrix)
 }
 
 void
-ctx_matrix_identity (CtxMatrix *matrix)
-{
-  _ctx_matrix_identity (matrix);
-}
-
-static void
-_ctx_matrix_multiply (CtxMatrix       *result,
-                      const CtxMatrix *t,
-                      const CtxMatrix *s)
+ctx_matrix_multiply (CtxMatrix       *result,
+                     const CtxMatrix *t,
+                     const CtxMatrix *s)
 {
   CtxMatrix r;
   r.m[0][0] = t->m[0][0] * s->m[0][0] + t->m[0][1] * s->m[1][0];
@@ -9690,13 +9359,6 @@ _ctx_matrix_multiply (CtxMatrix       *result,
   *result = r;
 }
 
-void
-ctx_matrix_multiply (CtxMatrix       *result,
-                     const CtxMatrix *t,
-                     const CtxMatrix *s)
-{
-  _ctx_matrix_multiply (result, t, s);
-}
 
 void
 ctx_matrix_translate (CtxMatrix *matrix, float x, float y)
@@ -9708,7 +9370,7 @@ ctx_matrix_translate (CtxMatrix *matrix, float x, float y)
   transform.m[1][1] = 1.0f;
   transform.m[2][0] = x;
   transform.m[2][1] = y;
-  _ctx_matrix_multiply (matrix, &transform, matrix);
+  ctx_matrix_multiply (matrix, &transform, matrix);
 }
 
 void
@@ -9721,7 +9383,7 @@ ctx_matrix_scale (CtxMatrix *matrix, float x, float y)
   transform.m[1][1] = y;
   transform.m[2][0] = 0.0f;
   transform.m[2][1] = 0.0f;
-  _ctx_matrix_multiply (matrix, &transform, matrix);
+  ctx_matrix_multiply (matrix, &transform, matrix);
 }
 
 void
@@ -9736,7 +9398,7 @@ ctx_matrix_rotate (CtxMatrix *matrix, float angle)
   transform.m[1][1] = val_cos;
   transform.m[2][0] =     0.0f;
   transform.m[2][1] = 0.0f;
-  _ctx_matrix_multiply (matrix, &transform, matrix);
+  ctx_matrix_multiply (matrix, &transform, matrix);
 }
 
 #if 0
@@ -9751,7 +9413,7 @@ ctx_matrix_skew_x (CtxMatrix *matrix, float angle)
   transform.m[1][1] = 1.0f;
   transform.m[2][0] =    0.0f;
   transform.m[2][1] = 0.0f;
-  _ctx_matrix_multiply (matrix, &transform, matrix);
+  ctx_matrix_multiply (matrix, &transform, matrix);
 }
 
 static void
@@ -9765,7 +9427,7 @@ ctx_matrix_skew_y (CtxMatrix *matrix, float angle)
   transform.m[1][1] = 1.0f;
   transform.m[2][0] =    0.0f;
   transform.m[2][1] = 0.0f;
-  _ctx_matrix_multiply (matrix, &transform, matrix);
+  ctx_matrix_multiply (matrix, &transform, matrix);
 }
 #endif
 
@@ -9776,8 +9438,6 @@ ctx_identity (Ctx *ctx)
   CTX_PROCESS_VOID (CTX_IDENTITY);
 }
 
-
-
 void
 ctx_apply_transform (Ctx *ctx, float a, float b,  // hscale, hskew
                      float c, float d,  // vskew,  vscale
@@ -9805,29 +9465,6 @@ ctx_get_transform  (Ctx *ctx, float *a, float *b,
   if (f) { *f = ctx->state.gstate.transform.m[2][1]; }
 }
 
-void
-ctx_source_transform (Ctx *ctx, float a, float b,  // hscale, hskew
-                      float c, float d,  // vskew,  vscale
-                      float e, float f)  // htran,  vtran
-{
-  CtxEntry command[3]=
-  {
-    ctx_f (CTX_SOURCE_TRANSFORM, a, b),
-    ctx_f (CTX_CONT,             c, d),
-    ctx_f (CTX_CONT,             e, f)
-  };
-  ctx_process (ctx, command);
-}
-
-void
-ctx_source_transform_matrix (Ctx *ctx, CtxMatrix *matrix)
-{
-  ctx_source_transform (ctx,
-    matrix->m[0][0], matrix->m[0][1],
-    matrix->m[1][0], matrix->m[1][1],
-    matrix->m[2][0], matrix->m[2][1]);
-}
-
 void ctx_apply_matrix (Ctx *ctx, CtxMatrix *matrix)
 {
   ctx_apply_transform (ctx,
@@ -9896,6 +9533,14 @@ ctx_matrix_invert (CtxMatrix *m)
   m->m[2][1] = (t.m[0][1] * t.m[2][0] - t.m[0][0] * t.m[2][1]) * invdet ;
 }
 
+void
+ctx_matrix_apply_transform (const CtxMatrix *m, float *x, float *y)
+{
+  float x_in = *x;
+  float y_in = *y;
+  *x = ( (x_in * m->m[0][0]) + (y_in * m->m[1][0]) + m->m[2][0]);
+  *y = ( (y_in * m->m[1][1]) + (x_in * m->m[0][1]) + m->m[2][1]);
+}
 
 
 #endif
@@ -9944,7 +9589,7 @@ inline static float ctx_u8_to_float (uint8_t val_u8)
 float ctx_u8_float[256];
 #endif
 
-CtxColor *ctx_color_new (void)
+CtxColor *ctx_color_new ()
 {
   CtxColor *color = (CtxColor*)ctx_calloc (sizeof (CtxColor), 1);
   return color;
@@ -10200,9 +9845,7 @@ static void ctx_color_get_drgba (CtxState *state, CtxColor *color, float *out)
   out[3] = color->alpha;
 }
 
-
-static inline void
-_ctx_color_get_rgba (CtxState *state, CtxColor *color, float *out)
+void ctx_color_get_rgba (CtxState *state, CtxColor *color, float *out)
 {
 #if CTX_ENABLE_CM
   if (! (color->valid & CTX_VALID_RGBA) )
@@ -10224,12 +9867,6 @@ _ctx_color_get_rgba (CtxState *state, CtxColor *color, float *out)
 #endif
 }
 
-void ctx_color_get_rgba (CtxState *state, CtxColor *color, float *out)
-{
-  _ctx_color_get_rgba (state, color, out);
-}
-
-
 
 float ctx_float_color_rgb_to_gray (CtxState *state, const float *rgb)
 {
@@ -10301,8 +9938,8 @@ static void ctx_color_get_cmyka_u8 (CtxState *state, CtxColor *color, uint8_t *o
 #endif
 #endif
 
-static inline void
-_ctx_color_get_rgba8 (CtxState *state, CtxColor *color, uint8_t *out)
+void
+ctx_color_get_rgba8 (CtxState *state, CtxColor *color, uint8_t *out)
 {
   if (! (color->valid & CTX_VALID_RGBA_U8) )
     {
@@ -10318,12 +9955,6 @@ _ctx_color_get_rgba8 (CtxState *state, CtxColor *color, uint8_t *out)
   out[3] = color->rgba[3];
 }
 
-void
-ctx_color_get_rgba8 (CtxState *state, CtxColor *color, uint8_t *out)
-{
-  _ctx_color_get_rgba8 (state, color, out);
-}
-
 void ctx_color_get_graya_u8 (CtxState *state, CtxColor *color, uint8_t *out)
 {
   if (! (color->valid & CTX_VALID_GRAYA_U8) )
@@ -10747,7 +10378,7 @@ mrg_color_parse_hex (CtxState *ctxstate, CtxColor *color, const char *color_stri
 int ctx_color_set_from_string (Ctx *ctx, CtxColor *color, const char *string)
 {
   int i;
-  uint64_t hash = ctx_strhash (string);
+  uint64_t hash = ctx_strhash (string, 0);
 //  ctx_color_set_rgba (&(ctx->state), color, 0.4,0.1,0.9,1.0);
 //  return 0;
     //rgba[0], rgba[1], rgba[2], rgba[3]);
@@ -10964,7 +10595,7 @@ void ctx_gradient_add_stop_string
 //  userRGB - settable at any time, stored in save|restore 
 //  texture - set as the space of data on subsequent 
 
-static float ctx_state_get (CtxState *state, uint64_t hash)
+float ctx_state_get (CtxState *state, uint64_t hash)
 {
   for (int i = state->gstate.keydb_pos-1; i>=0; i--)
     {
@@ -10974,7 +10605,7 @@ static float ctx_state_get (CtxState *state, uint64_t hash)
   return -0.0;
 }
 
-static void ctx_state_set (CtxState *state, uint64_t key, float value)
+void ctx_state_set (CtxState *state, uint64_t key, float value)
 {
   if (key != CTX_new_state)
     {
@@ -11160,36148 +10791,22955 @@ int ctx_is_set_now (Ctx *ctx, uint64_t hash)
 {
   return ctx_is_set (ctx, hash);
 }
+#if CTX_RASTERIZER
 
-#if CTX_COMPOSITE
-
-#define CTX_REFERENCE 0
-
-
-#define CTX_RGBA8_R_SHIFT  0
-#define CTX_RGBA8_G_SHIFT  8
-#define CTX_RGBA8_B_SHIFT  16
-#define CTX_RGBA8_A_SHIFT  24
-
-#define CTX_RGBA8_R_MASK   (0xff << CTX_RGBA8_R_SHIFT)
-#define CTX_RGBA8_G_MASK   (0xff << CTX_RGBA8_G_SHIFT)
-#define CTX_RGBA8_B_MASK   (0xff << CTX_RGBA8_B_SHIFT)
-#define CTX_RGBA8_A_MASK   (0xff << CTX_RGBA8_A_SHIFT)
-
-#define CTX_RGBA8_RB_MASK  (CTX_RGBA8_R_MASK | CTX_RGBA8_B_MASK)
-#define CTX_RGBA8_GA_MASK  (CTX_RGBA8_G_MASK | CTX_RGBA8_A_MASK)
-
+void ctx_compositor_setup_default (CtxRasterizer *rasterizer);
 
-CTX_INLINE static void
-ctx_RGBA8_associate_alpha (uint8_t *u8)
+#define CTX_FULL_AA 15
+inline static void
+ctx_rasterizer_apply_coverage (CtxRasterizer *rasterizer,
+                               uint8_t * __restrict__ dst,
+                               int            x,
+                               uint8_t * __restrict__ coverage,
+                               int            count)
 {
-#if 1
-  uint32_t val = *((uint32_t*)(u8));
-  uint8_t a = u8[3];
-  uint32_t g = (((val & CTX_RGBA8_G_MASK) * a) >> 8) & CTX_RGBA8_G_MASK;
-  uint32_t rb =(((val & CTX_RGBA8_RB_MASK) * a) >> 8) & CTX_RGBA8_RB_MASK;
-  *((uint32_t*)(u8)) = g|rb|(a << CTX_RGBA8_A_SHIFT);
-#else
-  uint32_t a = u8[3];
-  u8[0] = (u8[0] * a + 255) >> 8;
-  u8[1] = (u8[1] * a + 255) >> 8;
-  u8[2] = (u8[2] * a + 255) >> 8;
-#endif
+  if (rasterizer->format->apply_coverage)
+    rasterizer->format->apply_coverage(rasterizer, dst, rasterizer->color, x, coverage, count);
+  else
+    rasterizer->comp_op (rasterizer, dst, rasterizer->color, x, coverage, count);
 }
 
-CTX_INLINE static void
-ctx_RGBA8_associate_alpha_probably_opaque (uint8_t *u8)
+static void
+ctx_rasterizer_gradient_add_stop (CtxRasterizer *rasterizer, float pos, float *rgba)
 {
-  uint32_t val = *((uint32_t*)(u8));
-  uint32_t a = val>>24;//u8[3];
-  //if (CTX_UNLIKELY(a==0))
-  //   *((uint32_t*)(u8)) = 0;
-  if (CTX_UNLIKELY(a!=255))
-  {
-     uint32_t g = (((val & CTX_RGBA8_G_MASK) * a) >> 8) & CTX_RGBA8_G_MASK;
-     uint32_t rb =(((val & CTX_RGBA8_RB_MASK) * a) >> 8) & CTX_RGBA8_RB_MASK;
-     *((uint32_t*)(u8)) = g|rb|(a << CTX_RGBA8_A_SHIFT);
-  }
+  /* FIXME XXX we only have one gradient, but might need separate gradients
+   * for fill/stroke !
+   * 
+   */
+  CtxGradient *gradient = &rasterizer->state->gradient;
+  CtxGradientStop *stop = &gradient->stops[gradient->n_stops];
+  stop->pos = pos;
+  ctx_color_set_rgba (rasterizer->state, & (stop->color), rgba[0], rgba[1], rgba[2], rgba[3]);
+  if (gradient->n_stops < 15) //we'll keep overwriting the last when out of stops
+    { gradient->n_stops++; }
 }
 
-CTX_INLINE static uint32_t ctx_bi_RGBA8 (uint32_t isrc00, uint32_t isrc01, uint32_t isrc10, uint32_t isrc11, 
uint8_t dx, uint8_t dy)
+static int ctx_rasterizer_add_point (CtxRasterizer *rasterizer, int x1, int y1)
 {
+  CtxEntry entry = {CTX_EDGE, {{0},}};
+  if (y1 < rasterizer->scan_min)
+    { rasterizer->scan_min = y1; }
+  if (y1 > rasterizer->scan_max)
+    { rasterizer->scan_max = y1; }
+
+  if (x1 < rasterizer->col_min)
+    { rasterizer->col_min = x1; }
+  if (x1 > rasterizer->col_max)
+    { rasterizer->col_max = x1; }
+
+  entry.data.s16[2]=x1;
+  entry.data.s16[3]=y1;
+  return ctx_drawlist_add_single (&rasterizer->edge_list, &entry);
+}
+
 #if 0
-#if 0
-  uint8_t ret[4];
-  uint8_t *src00 = (uint8_t*)&isrc00;
-  uint8_t *src10 = (uint8_t*)&isrc10;
-  uint8_t *src01 = (uint8_t*)&isrc01;
-  uint8_t *src11 = (uint8_t*)&isrc11;
-  for (int c = 0; c < 4; c++)
-  {
-    ret[c] = ctx_lerp_u8 (ctx_lerp_u8 (src00[c], src01[c], dx),
-                         ctx_lerp_u8 (src10[c], src11[c], dx), dy);
-  }
-  return  ((uint32_t*)&ret[0])[0];
-#else
-  return ctx_lerp_RGBA8 (ctx_lerp_RGBA8 (isrc00, isrc01, dx),
-                         ctx_lerp_RGBA8 (isrc10, isrc11, dx), dy);
-#endif
+#define CTX_SHAPE_CACHE_PRIME1   7853
+#define CTX_SHAPE_CACHE_PRIME2   4129
+#define CTX_SHAPE_CACHE_PRIME3   3371
+#define CTX_SHAPE_CACHE_PRIME4   4221
 #else
-  uint32_t s0_ga, s0_rb, s1_ga, s1_rb;
-  ctx_lerp_RGBA8_split (isrc00, isrc01, dx, &s0_ga, &s0_rb);
-  ctx_lerp_RGBA8_split (isrc10, isrc11, dx, &s1_ga, &s1_rb);
-  return ctx_lerp_RGBA8_merge (s0_ga, s0_rb, s1_ga, s1_rb, dy);
+#define CTX_SHAPE_CACHE_PRIME1   283
+#define CTX_SHAPE_CACHE_PRIME2   599
+#define CTX_SHAPE_CACHE_PRIME3   101
+#define CTX_SHAPE_CACHE_PRIME4   661
 #endif
-}
 
-#if CTX_GRADIENTS
-#if CTX_GRADIENT_CACHE
-static uint8_t ctx_gradient_cache_u8[CTX_GRADIENT_CACHE_ELEMENTS][4];
-extern int ctx_gradient_cache_valid;
+float ctx_shape_cache_rate = 0.0;
+#if CTX_SHAPE_CACHE
+int   _ctx_shape_cache_enabled = 1;
 
+//static CtxShapeCache ctx_cache = {{NULL,}, 0};
 
-inline static int ctx_grad_index (float v)
-{
-  int ret = v * (CTX_GRADIENT_CACHE_ELEMENTS - 1) + 0.5f;
-  ret = ctx_maxi (0, ret);
-  ret = ctx_mini (CTX_GRADIENT_CACHE_ELEMENTS-1, ret);
-  return ret;
-}
+static long ctx_shape_cache_hits = 0;
+static long ctx_shape_cache_misses = 0;
 
-inline static int ctx_grad_index_i (int v)
+
+/* this returns the buffer to use for rendering, it always
+   succeeds..
+ */
+static CtxShapeEntry *ctx_shape_entry_find (CtxRasterizer *rasterizer, uint32_t hash, int width, int height)
 {
-  v = v >> 8;
-  return ctx_maxi (0, ctx_mini (CTX_GRADIENT_CACHE_ELEMENTS-1, v));
-}
+  /* use both some high and some low bits  */
+  int entry_no = ( (hash >> 10) ^ (hash & 1023) ) % CTX_SHAPE_CACHE_ENTRIES;
+  int i;
+  {
+    static int i = 0;
+    i++;
+    if (i>1000)
+      {
+        ctx_shape_cache_rate = ctx_shape_cache_hits * 100.0  / (ctx_shape_cache_hits+ctx_shape_cache_misses);
+        i = 0;
+        ctx_shape_cache_hits = 0;
+        ctx_shape_cache_misses = 0;
+      }
+  }
+// XXX : this 1 one is needed  to silence a false positive:
+// ==90718== Invalid write of size 1
+// ==90718==    at 0x1189EF: ctx_rasterizer_generate_coverage (ctx.h:4786)
+// ==90718==    by 0x118E57: ctx_rasterizer_rasterize_edges (ctx.h:4907)
+//
+  int size = sizeof (CtxShapeEntry) + width * height + 1;
 
+  i = entry_no;
+  if (rasterizer->shape_cache.entries[i])
+    {
+      CtxShapeEntry *entry = rasterizer->shape_cache.entries[i];
+      int old_size = sizeof (CtxShapeEntry) + width + height + 1;
+      if (entry->hash == hash &&
+          entry->width == width &&
+          entry->height == height)
+        {
+          if (entry->uses < 1<<30)
+            { entry->uses++; }
+          ctx_shape_cache_hits ++;
+          return entry;
+        }
 
-//static void
-//ctx_gradient_cache_reset (void)
-//{
-//  ctx_gradient_cache_valid = 0;
-//}
-#endif
+      if (old_size >= size)
+      {
+      }
+      else
+      {
+        rasterizer->shape_cache.entries[i] = NULL;
+        rasterizer->shape_cache.size -= entry->width * entry->height;
+        rasterizer->shape_cache.size -= sizeof (CtxShapeEntry);
+        free (entry);
+        rasterizer->shape_cache.entries[i] = (CtxShapeEntry *) calloc (size, 1);
+      }
+    }
+  else
+    {
+        rasterizer->shape_cache.entries[i] = (CtxShapeEntry *) calloc (size, 1);
+    }
+
+  ctx_shape_cache_misses ++;
+  rasterizer->shape_cache.size              += size;
+  rasterizer->shape_cache.entries[i]->hash   = hash;
+  rasterizer->shape_cache.entries[i]->width  = width;
+  rasterizer->shape_cache.entries[i]->height = height;
+  rasterizer->shape_cache.entries[i]->uses = 0;
+  return rasterizer->shape_cache.entries[i];
+}
 
+#endif
 
-CTX_INLINE static void
-_ctx_fragment_gradient_1d_RGBA8 (CtxRasterizer *rasterizer, float x, float y, uint8_t *rgba)
+static uint32_t ctx_rasterizer_poly_to_hash (CtxRasterizer *rasterizer)
 {
-  float v = x;
-  CtxGradient *g = &rasterizer->state->gradient;
-  if (v < 0) { v = 0; }
-  if (v > 1) { v = 1; }
+  int16_t x = 0;
+  int16_t y = 0;
 
-  if (g->n_stops == 0)
+  CtxEntry *entry = &rasterizer->edge_list.entries[0];
+  int ox = entry->data.s16[2];
+  int oy = entry->data.s16[3];
+  uint32_t hash = rasterizer->edge_list.count;
+  hash = ox;//(ox % CTX_SUBDIV);
+  hash *= CTX_SHAPE_CACHE_PRIME1;
+  hash += oy; //(oy % CTX_RASTERIZER_AA);
+  for (int i = 0; i < rasterizer->edge_list.count; i++)
     {
-      rgba[0] = rgba[1] = rgba[2] = v * 255;
-      rgba[3] = 255;
-      return;
+      CtxEntry *entry = &rasterizer->edge_list.entries[i];
+      x = entry->data.s16[2];
+      y = entry->data.s16[3];
+      int dx = x-ox;
+      int dy = y-oy;
+      ox = x;
+      oy = y;
+      hash *= CTX_SHAPE_CACHE_PRIME3;
+      hash += dx;
+      hash *= CTX_SHAPE_CACHE_PRIME4;
+      hash += dy;
     }
-  CtxGradientStop *stop      = NULL;
-  CtxGradientStop *next_stop = &g->stops[0];
-  CtxColor *color;
-  for (int s = 0; s < g->n_stops; s++)
+  return hash;
+}
+
+static uint32_t ctx_rasterizer_poly_to_edges (CtxRasterizer *rasterizer)
+{
+  int16_t x = 0;
+  int16_t y = 0;
+  if (rasterizer->edge_list.count == 0)
+     return 0;
+#if CTX_SHAPE_CACHE
+  CtxEntry *entry = &rasterizer->edge_list.entries[0];
+  int ox = entry->data.s16[2];
+  int oy = entry->data.s16[3];
+  uint32_t hash = rasterizer->edge_list.count;
+  hash = (ox % CTX_SUBDIV);
+  hash *= CTX_SHAPE_CACHE_PRIME1;
+  hash += (oy % CTX_FULL_AA);
+#endif
+  for (int i = 0; i < rasterizer->edge_list.count; i++)
     {
-      stop      = &g->stops[s];
-      next_stop = &g->stops[s+1];
-      if (s + 1 >= g->n_stops) { next_stop = NULL; }
-      if (v >= stop->pos && next_stop && v < next_stop->pos)
-        { break; }
-      stop = NULL;
-      next_stop = NULL;
+      CtxEntry *entry = &rasterizer->edge_list.entries[i];
+      if (entry->code == CTX_NEW_EDGE)
+        {
+          entry->code = CTX_EDGE;
+#if CTX_SHAPE_CACHE
+          hash *= CTX_SHAPE_CACHE_PRIME2;
+#endif
+        }
+      else
+        {
+          entry->data.s16[0] = x;
+          entry->data.s16[1] = y;
+        }
+      x = entry->data.s16[2];
+      y = entry->data.s16[3];
+#if CTX_SHAPE_CACHE
+      int dx = x-ox;
+      int dy = y-oy;
+      ox = x;
+      oy = y;
+      hash *= CTX_SHAPE_CACHE_PRIME3;
+      hash += dx;
+      hash *= CTX_SHAPE_CACHE_PRIME4;
+      hash += dy;
+#endif
+      if (entry->data.s16[3] < entry->data.s16[1])
+        {
+          *entry = ctx_s16 (CTX_EDGE_FLIPPED,
+                            entry->data.s16[2], entry->data.s16[3],
+                            entry->data.s16[0], entry->data.s16[1]);
+        }
     }
-  if (stop == NULL && next_stop)
+#if CTX_SHAPE_CACHE
+  return hash;
+#else
+  return 0;
+#endif
+}
+
+static void ctx_rasterizer_finish_shape (CtxRasterizer *rasterizer)
+{
+  if (rasterizer->has_shape && rasterizer->has_prev)
     {
-      color = & (next_stop->color);
+      ctx_rasterizer_line_to (rasterizer, rasterizer->first_x, rasterizer->first_y);
+      rasterizer->has_prev = 0;
     }
-  else if (stop && next_stop == NULL)
+}
+
+static void ctx_rasterizer_move_to (CtxRasterizer *rasterizer, float x, float y)
+{
+  float tx = x; float ty = y;
+  int aa = 15;//rasterizer->aa;
+  rasterizer->x        = x;
+  rasterizer->y        = y;
+  rasterizer->first_x  = x;
+  rasterizer->first_y  = y;
+  rasterizer->has_prev = -1;
+  if (rasterizer->uses_transforms)
     {
-      color = & (stop->color);
+      _ctx_user_to_device (rasterizer->state, &tx, &ty);
     }
-  else if (stop && next_stop)
+
+  tx = (tx - rasterizer->blit_x) * CTX_SUBDIV;
+  ty = ty * aa;
+
+  if (ty < rasterizer->scan_min)
+    { rasterizer->scan_min = ty; }
+  if (ty > rasterizer->scan_max)
+    { rasterizer->scan_max = ty; }
+  if (tx < rasterizer->col_min)
+    { rasterizer->col_min = tx; }
+  if (tx > rasterizer->col_max)
+    { rasterizer->col_max = tx; }
+}
+
+
+
+static inline void ctx_rasterizer_line_to (CtxRasterizer *rasterizer, float x, float y)
+{
+  float tx = x;
+  float ty = y;
+  float ox = rasterizer->x;
+  float oy = rasterizer->y;
+  if (rasterizer->uses_transforms)
     {
-      uint8_t stop_rgba[4];
-      uint8_t next_rgba[4];
-      ctx_color_get_rgba8 (rasterizer->state, & (stop->color), stop_rgba);
-      ctx_color_get_rgba8 (rasterizer->state, & (next_stop->color), next_rgba);
-      int dx = (v - stop->pos) * 255 / (next_stop->pos - stop->pos);
-#if 1
-      ((uint32_t*)rgba)[0] = ctx_lerp_RGBA8 (((uint32_t*)stop_rgba)[0],
-                                             ((uint32_t*)next_rgba)[0], dx);
-#else
-      for (int c = 0; c < 4; c++)
-        { rgba[c] = ctx_lerp_u8 (stop_rgba[c], next_rgba[c], dx); }
-#endif
-      ctx_RGBA8_associate_alpha (rgba);
-      return;
+      _ctx_user_to_device (rasterizer->state, &tx, &ty);
     }
-  else
+  tx -= rasterizer->blit_x;
+#define MIN_Y -1000
+#define MAX_Y 1400
+
+  if (ty < MIN_Y) ty = MIN_Y;
+  if (ty > MAX_Y) ty = MAX_Y;
+  
+  ctx_rasterizer_add_point (rasterizer, tx * CTX_SUBDIV, ty * 15);//rasterizer->aa);
+
+  if (rasterizer->has_prev<=0)
     {
-      color = & (g->stops[g->n_stops-1].color);
+      if (rasterizer->uses_transforms)
+      {
+        // storing transformed would save some processing for a tiny
+        // amount of runtime RAM XXX
+        _ctx_user_to_device (rasterizer->state, &ox, &oy);
+      }
+      ox -= rasterizer->blit_x;
+
+  if (oy < MIN_Y) oy = MIN_Y;
+  if (oy > MAX_Y) oy = MAX_Y;
+
+      rasterizer->edge_list.entries[rasterizer->edge_list.count-1].data.s16[0] = ox * CTX_SUBDIV;
+      rasterizer->edge_list.entries[rasterizer->edge_list.count-1].data.s16[1] = oy * 15;//rasterizer->aa;
+      rasterizer->edge_list.entries[rasterizer->edge_list.count-1].code = CTX_NEW_EDGE;
+      rasterizer->has_prev = 1;
     }
-  ctx_color_get_rgba8 (rasterizer->state, color, rgba);
-  if (rasterizer->swap_red_green)
-  {
-    uint8_t tmp = rgba[0];
-    rgba[0] = rgba[2];
-    rgba[2] = tmp;
-  }
-  ctx_RGBA8_associate_alpha (rgba);
+  rasterizer->has_shape = 1;
+  rasterizer->y         = y;
+  rasterizer->x         = x;
 }
 
-#if CTX_GRADIENT_CACHE
-static void
-ctx_gradient_cache_prime (CtxRasterizer *rasterizer);
-#endif
 
-CTX_INLINE static void
-ctx_fragment_gradient_1d_RGBA8 (CtxRasterizer *rasterizer, float x, float y, uint8_t *rgba)
+CTX_INLINE static float
+ctx_bezier_sample_1d (float x0, float x1, float x2, float x3, float dt)
 {
-#if CTX_GRADIENT_CACHE
-  *((uint32_t*)rgba) = *((uint32_t*)(&ctx_gradient_cache_u8[ctx_grad_index(x)][0]));
-#else
- _ctx_fragment_gradient_1d_RGBA8 (rasterizer, x, y, rgba);
-#endif
+  float ab   = ctx_lerpf (x0, x1, dt);
+  float bc   = ctx_lerpf (x1, x2, dt);
+  float cd   = ctx_lerpf (x2, x3, dt);
+  float abbc = ctx_lerpf (ab, bc, dt);
+  float bccd = ctx_lerpf (bc, cd, dt);
+  return ctx_lerpf (abbc, bccd, dt);
 }
-#endif
 
-CTX_INLINE static void
-ctx_u8_associate_alpha (int components, uint8_t *u8)
+inline static void
+ctx_bezier_sample (float x0, float y0,
+                   float x1, float y1,
+                   float x2, float y2,
+                   float x3, float y3,
+                   float dt, float *x, float *y)
 {
-  for (int c = 0; c < components-1; c++)
-    u8[c] = (u8[c] * u8[components-1] + 255)>>8;
+  *x = ctx_bezier_sample_1d (x0, x1, x2, x3, dt);
+  *y = ctx_bezier_sample_1d (y0, y1, y2, y3, dt);
 }
 
-#if CTX_GRADIENTS
-#if CTX_GRADIENT_CACHE
-static void
-ctx_gradient_cache_prime (CtxRasterizer *rasterizer)
+static inline void
+ctx_rasterizer_bezier_divide (CtxRasterizer *rasterizer,
+                              float ox, float oy,
+                              float x0, float y0,
+                              float x1, float y1,
+                              float x2, float y2,
+                              float sx, float sy,
+                              float ex, float ey,
+                              float s,
+                              float e,
+                              int   iteration,
+                              float tolerance)
 {
-  if (ctx_gradient_cache_valid)
-    return;
-  for (int u = 0; u < CTX_GRADIENT_CACHE_ELEMENTS; u++)
-  {
-    float v = u / (CTX_GRADIENT_CACHE_ELEMENTS - 1.0f);
-    _ctx_fragment_gradient_1d_RGBA8 (rasterizer, v, 0.0f, &ctx_gradient_cache_u8[u][0]);
-    //*((uint32_t*)(&ctx_gradient_cache_u8_a[u][0]))= *((uint32_t*)(&ctx_gradient_cache_u8[u][0]));
-    //memcpy(&ctx_gradient_cache_u8_a[u][0], &ctx_gradient_cache_u8[u][0], 4);
-    //ctx_RGBA8_associate_alpha (&ctx_gradient_cache_u8_a[u][0]);
-  }
-  ctx_gradient_cache_valid = 1;
+  if (iteration > 8)
+    { return; }
+  float t = (s + e) * 0.5f;
+  float x, y, lx, ly, dx, dy;
+  ctx_bezier_sample (ox, oy, x0, y0, x1, y1, x2, y2, t, &x, &y);
+  if (iteration)
+    {
+      lx = ctx_lerpf (sx, ex, t);
+      ly = ctx_lerpf (sy, ey, t);
+      dx = lx - x;
+      dy = ly - y;
+      if ( (dx*dx+dy*dy) < tolerance)
+        /* bailing - because for the mid-point straight line difference is
+           tiny */
+        { return; }
+      dx = sx - ex;
+      dy = ey - ey;
+      if ( (dx*dx+dy*dy) < tolerance)
+        /* bailing on tiny segments */
+        { return; }
+    }
+  ctx_rasterizer_bezier_divide (rasterizer, ox, oy, x0, y0, x1, y1, x2, y2,
+                                sx, sy, x, y, s, t, iteration + 1,
+                                tolerance);
+  ctx_rasterizer_line_to (rasterizer, x, y);
+  ctx_rasterizer_bezier_divide (rasterizer, ox, oy, x0, y0, x1, y1, x2, y2,
+                                x, y, ex, ey, t, e, iteration + 1,
+                                tolerance);
 }
-#endif
 
-CTX_INLINE static void
-ctx_fragment_gradient_1d_GRAYA8 (CtxRasterizer *rasterizer, float x, float y, uint8_t *rgba)
+static void
+ctx_rasterizer_curve_to (CtxRasterizer *rasterizer,
+                         float x0, float y0,
+                         float x1, float y1,
+                         float x2, float y2)
 {
-  float v = x;
-  CtxGradient *g = &rasterizer->state->gradient;
-  if (v < 0) { v = 0; }
-  if (v > 1) { v = 1; }
-  if (g->n_stops == 0)
-    {
-      rgba[0] = rgba[1] = rgba[2] = v * 255;
-      rgba[1] = 255;
-      return;
-    }
-  CtxGradientStop *stop      = NULL;
-  CtxGradientStop *next_stop = &g->stops[0];
-  CtxColor *color;
-  for (int s = 0; s < g->n_stops; s++)
-    {
-      stop      = &g->stops[s];
-      next_stop = &g->stops[s+1];
-      if (s + 1 >= g->n_stops) { next_stop = NULL; }
-      if (v >= stop->pos && next_stop && v < next_stop->pos)
-        { break; }
-      stop = NULL;
-      next_stop = NULL;
-    }
-  if (stop == NULL && next_stop)
-    {
-      color = & (next_stop->color);
-    }
-  else if (stop && next_stop == NULL)
+  float tolerance =
+    ctx_pow2 (rasterizer->state->gstate.transform.m[0][0]) +
+    ctx_pow2 (rasterizer->state->gstate.transform.m[1][1]);
+  float ox = rasterizer->x;
+  float oy = rasterizer->y;
+  ox = rasterizer->state->x;
+  oy = rasterizer->state->y;
+  tolerance = 1.0f/tolerance * 2;
+#if 1 // skipping this to preserve hash integrity
+  if (tolerance == 1.0f || 1)
+  {
+  float maxx = ctx_maxf (x1,x2);
+  maxx = ctx_maxf (maxx, ox);
+  maxx = ctx_maxf (maxx, x0);
+  float maxy = ctx_maxf (y1,y2);
+  maxy = ctx_maxf (maxy, oy);
+  maxy = ctx_maxf (maxy, y0);
+  float minx = ctx_minf (x1,x2);
+  minx = ctx_minf (minx, ox);
+  minx = ctx_minf (minx, x0);
+  float miny = ctx_minf (y1,y2);
+  miny = ctx_minf (miny, oy);
+  miny = ctx_minf (miny, y0);
+  
+  _ctx_user_to_device (rasterizer->state, &minx, &miny);
+  _ctx_user_to_device (rasterizer->state, &maxx, &maxy);
+#if 1
+    if(
+        (minx > rasterizer->blit_x + rasterizer->blit_width) ||
+        (miny > rasterizer->blit_y + rasterizer->blit_height) ||
+        (maxx < rasterizer->blit_x) ||
+        (maxy < rasterizer->blit_y) )
     {
-      color = & (stop->color);
     }
-  else if (stop && next_stop)
+    else
+#endif
     {
-      uint8_t stop_rgba[4];
-      uint8_t next_rgba[4];
-      ctx_color_get_graya_u8 (rasterizer->state, & (stop->color), stop_rgba);
-      ctx_color_get_graya_u8 (rasterizer->state, & (next_stop->color), next_rgba);
-      int dx = (v - stop->pos) * 255 / (next_stop->pos - stop->pos);
-      for (int c = 0; c < 2; c++)
-        { rgba[c] = ctx_lerp_u8 (stop_rgba[c], next_rgba[c], dx); }
-      return;
+      ctx_rasterizer_bezier_divide (rasterizer,
+                                    ox, oy, x0, y0,
+                                    x1, y1, x2, y2,
+                                    ox, oy, x2, y2,
+                                    0.0f, 1.0f, 0.0f, tolerance);
     }
+  }
   else
+#endif
     {
-      color = & (g->stops[g->n_stops-1].color);
+      ctx_rasterizer_bezier_divide (rasterizer,
+                                    ox, oy, x0, y0,
+                                    x1, y1, x2, y2,
+                                    ox, oy, x2, y2,
+                                    0.0f, 1.0f, 0.0f, tolerance);
     }
-  ctx_color_get_graya_u8 (rasterizer->state, color, rgba);
+  ctx_rasterizer_line_to (rasterizer, x2, y2);
 }
 
-CTX_INLINE static void
-ctx_fragment_gradient_1d_RGBAF (CtxRasterizer *rasterizer, float v, float y, float *rgba)
+static void
+ctx_rasterizer_rel_move_to (CtxRasterizer *rasterizer, float x, float y)
 {
-  CtxGradient *g = &rasterizer->state->gradient;
-  if (v < 0) { v = 0; }
-  if (v > 1) { v = 1; }
-  if (g->n_stops == 0)
-    {
-      rgba[0] = rgba[1] = rgba[2] = v;
-      rgba[3] = 1.0;
-      return;
-    }
-  CtxGradientStop *stop      = NULL;
-  CtxGradientStop *next_stop = &g->stops[0];
-  CtxColor *color;
-  for (int s = 0; s < g->n_stops; s++)
-    {
-      stop      = &g->stops[s];
-      next_stop = &g->stops[s+1];
-      if (s + 1 >= g->n_stops) { next_stop = NULL; }
-      if (v >= stop->pos && next_stop && v < next_stop->pos)
-        { break; }
-      stop = NULL;
-      next_stop = NULL;
-    }
-  if (stop == NULL && next_stop)
-    {
-      color = & (next_stop->color);
-    }
-  else if (stop && next_stop == NULL)
-    {
-      color = & (stop->color);
-    }
-  else if (stop && next_stop)
-    {
-      float stop_rgba[4];
-      float next_rgba[4];
-      ctx_color_get_rgba (rasterizer->state, & (stop->color), stop_rgba);
-      ctx_color_get_rgba (rasterizer->state, & (next_stop->color), next_rgba);
-      int dx = (v - stop->pos) / (next_stop->pos - stop->pos);
-      for (int c = 0; c < 4; c++)
-        { rgba[c] = ctx_lerpf (stop_rgba[c], next_rgba[c], dx); }
-      return;
-    }
-  else
-    {
-      color = & (g->stops[g->n_stops-1].color);
-    }
-  ctx_color_get_rgba (rasterizer->state, color, rgba);
+  if (x == 0.f && y == 0.f)
+    { return; }
+  x += rasterizer->x;
+  y += rasterizer->y;
+  ctx_rasterizer_move_to (rasterizer, x, y);
 }
-#endif
 
 static void
-ctx_fragment_image_RGBA8 (CtxRasterizer *rasterizer, float x, float y, void *out, int count, float dx, float 
dy)
+ctx_rasterizer_rel_line_to (CtxRasterizer *rasterizer, float x, float y)
 {
-  uint8_t *rgba = (uint8_t *) out;
-  CtxSource *g = &rasterizer->state->gstate.source_fill;
-  CtxBuffer *buffer = g->texture.buffer->color_managed;
-  ctx_assert (rasterizer);
-  ctx_assert (g);
-  ctx_assert (buffer);
+  if (x== 0.f && y==0.f)
+    { return; }
+  x += rasterizer->x;
+  y += rasterizer->y;
+  ctx_rasterizer_line_to (rasterizer, x, y);
+}
 
-  for (int i = 0; i < count; i ++)
+static void
+ctx_rasterizer_rel_curve_to (CtxRasterizer *rasterizer,
+                             float x0, float y0, float x1, float y1, float x2, float y2)
+{
+  x0 += rasterizer->x;
+  y0 += rasterizer->y;
+  x1 += rasterizer->x;
+  y1 += rasterizer->y;
+  x2 += rasterizer->x;
+  y2 += rasterizer->y;
+  ctx_rasterizer_curve_to (rasterizer, x0, y0, x1, y1, x2, y2);
+}
+
+
+static int
+ctx_rasterizer_find_texture (CtxRasterizer *rasterizer,
+                             const char *eid)
+{
+  int no;
+  for (no = 0; no < CTX_MAX_TEXTURES; no++)
   {
+    if (rasterizer->texture_source->texture[no].data &&
+        rasterizer->texture_source->texture[no].eid &&
+        !strcmp (rasterizer->texture_source->texture[no].eid, eid))
+      return no;
+  }
+  return -1;
+}
 
-  int u = x;
-  int v = y;
-  int width = buffer->width;
-  int height = buffer->height;
-  if ( u < 0 || v < 0 ||
-       u >= width ||
-       v >= height)
+static void
+ctx_rasterizer_set_texture (CtxRasterizer *rasterizer,
+                            const char *eid,
+                            float x,
+                            float y)
+{
+  int is_stroke = (rasterizer->state->source != 0);
+  CtxSource *source = is_stroke && (rasterizer->state->gstate.source_stroke.type != CTX_SOURCE_INHERIT_FILL)?
+                        &rasterizer->state->gstate.source_stroke:
+                        &rasterizer->state->gstate.source_fill;
+  rasterizer->state->source = 0;
+
+  int no = ctx_rasterizer_find_texture (rasterizer, eid);
+  if (no < 0 || no >= CTX_MAX_TEXTURES) { no = 0; }
+  if (rasterizer->texture_source->texture[no].data == NULL)
     {
-      *((uint32_t*)(rgba)) = 0;
+      fprintf (stderr, "ctx tex fail %p %s %i\n", rasterizer->texture_source, eid, no);
+      return;
     }
   else
-    {
-      int bpp = buffer->format->bpp/8;
-      if (rasterizer->state->gstate.image_smoothing)
-      {
-      uint8_t *src00 = (uint8_t *) buffer->data;
-      src00 += v * buffer->stride + u * bpp;
-      uint8_t *src01 = src00;
-      if ( u + 1 < width)
-      {
-        src01 = src00 + bpp;
-      }
-      uint8_t *src11 = src01;
-      uint8_t *src10 = src00;
-      if ( v + 1 < height)
-      {
-        src10 = src00 + buffer->stride;
-        src11 = src01 + buffer->stride;
-      }
-      float dx = (x-(int)(x)) * 255.9;
-      float dy = (y-(int)(y)) * 255.9;
+  {
+    rasterizer->texture_source->texture[no].frame = rasterizer->texture_source->frame;
+  }
+  source->type = CTX_SOURCE_TEXTURE;
+  source->texture.buffer = &rasterizer->texture_source->texture[no];
+  source->texture.x0 = 0;
+  source->texture.y0 = 0;
+  source->transform = rasterizer->state->gstate.transform;
+  ctx_matrix_translate (&source->transform, x, y);
+  ctx_matrix_invert (&source->transform);
+}
 
-      switch (bpp)
-      {
-      case 1:
-        rgba[0] = rgba[1] = rgba[2] = ctx_lerp_u8 (ctx_lerp_u8 (src00[0], src01[0], dx),
-                               ctx_lerp_u8 (src10[0], src11[0], dx), dy);
-        rgba[3] = 255;
-        break;
-      case 2:
-        rgba[0] = rgba[1] = rgba[2] = ctx_lerp_u8 (ctx_lerp_u8 (src00[0], src01[0], dx),
-                               ctx_lerp_u8 (src10[0], src11[0], dx), dy);
-        rgba[3] = ctx_lerp_u8 (ctx_lerp_u8 (src00[1], src01[1], dx),
-                               ctx_lerp_u8 (src10[1], src11[1], dx), dy);
-        break;
-      case 3:
-      for (int c = 0; c < bpp; c++)
-        { rgba[c] = ctx_lerp_u8 (ctx_lerp_u8 (src00[c], src01[c], dx),
-                                 ctx_lerp_u8 (src10[c], src11[c], dx), dy);
-                
-        }
-        rgba[3]=255;
-        break;
-      break;
-      case 4:
-      for (int c = 0; c < bpp; c++)
-        { rgba[c] = ctx_lerp_u8 (ctx_lerp_u8 (src00[c], src01[c], dx),
-                                 ctx_lerp_u8 (src10[c], src11[c], dx), dy);
-                
-        }
-      }
-      }
-      else
-      {
-      uint8_t *src = (uint8_t *) buffer->data;
-      src += v * buffer->stride + u * bpp;
-      switch (bpp)
+
+static void ctx_rasterizer_define_texture (CtxRasterizer *rasterizer,
+                                           const char *eid,
+                                           int width,
+                                           int height,
+                                           int format,
+                                           char unsigned *data)
+{
+  _ctx_texture_lock (); // we're using the same texture_source from all threads, keeping allocaitons down
+                        // need synchronizing (it could be better to do a pre-pass)
+  ctx_texture_init (rasterizer->texture_source,
+                    eid,
+                    width,
+                    height,
+                    ctx_pixel_format_get_stride ((CtxPixelFormat)format, width),
+                    (CtxPixelFormat)format,
+#if CTX_ENABLE_CM
+                    (void*)rasterizer->state->gstate.texture_space,
+#else
+                    NULL,
+#endif
+                    data,
+                    ctx_buffer_pixels_free, (void*)23);
+                    /*  when userdata for ctx_buffer_pixels_free is 23, texture_init dups the data on
+                     *  use
+                     */
+
+  _ctx_texture_unlock ();
+  ctx_rasterizer_set_texture (rasterizer, eid, 0.0, 0.0);
+}
+
+
+CTX_INLINE static int ctx_compare_edges (const void *ap, const void *bp)
+{
+  const CtxEntry *a = (const CtxEntry *) ap;
+  const CtxEntry *b = (const CtxEntry *) bp;
+  int ycompare = a->data.s16[1] - b->data.s16[1];
+  if (ycompare)
+    { return ycompare; }
+  int xcompare = a->data.s16[0] - b->data.s16[0];
+  return xcompare;
+}
+
+CTX_INLINE static int ctx_edge_qsort_partition (CtxEntry *A, int low, int high)
+{
+  CtxEntry pivot = A[ (high+low) /2];
+  int i = low;
+  int j = high;
+  while (i <= j)
+    {
+      while (ctx_compare_edges (&A[i], &pivot) <0) { i ++; }
+      while (ctx_compare_edges (&pivot, &A[j]) <0) { j --; }
+      if (i <= j)
         {
-          case 1:
-            for (int c = 0; c < 3; c++)
-              { rgba[c] = src[0]; }
-            rgba[3] = 255;
-            break;
-          case 2:
-            for (int c = 0; c < 3; c++)
-              { rgba[c] = src[0]; }
-            rgba[3] = src[1];
-            break;
-          case 3:
-            for (int c = 0; c < 3; c++)
-              { rgba[c] = src[c]; }
-            rgba[3] = 255;
-            break;
-          case 4:
-            for (int c = 0; c < 4; c++)
-              { rgba[c] = src[c]; }
-            break;
+          CtxEntry tmp = A[i];
+          A[i] = A[j];
+          A[j] = tmp;
+          i++;
+          j--;
         }
-      }
-      if (rasterizer->swap_red_green)
-      {
-        uint8_t tmp = rgba[0];
-        rgba[0] = rgba[2];
-        rgba[2] = tmp;
-      }
     }
-    ctx_RGBA8_associate_alpha_probably_opaque (rgba); // XXX: really?
-    rgba += 4;
-    x += dx;
-    y += dy;
-  }
+  return i;
 }
 
-#if CTX_DITHER
-static inline int ctx_dither_mask_a (int x, int y, int c, int divisor)
+static void ctx_edge_qsort (CtxEntry *entries, int low, int high)
 {
-  /* https://pippin.gimp.org/a_dither/ */
-  return ( ( ( ( (x + c * 67) + y * 236) * 119) & 255 )-127) / divisor;
+  {
+    int p = ctx_edge_qsort_partition (entries, low, high);
+    if (low < p -1 )
+      { ctx_edge_qsort (entries, low, p - 1); }
+    if (low < high)
+      { ctx_edge_qsort (entries, p, high); }
+  }
 }
 
-inline static void
-ctx_dither_rgba_u8 (uint8_t *rgba, int x, int y, int dither_red_blue, int dither_green)
+static CTX_INLINE void ctx_rasterizer_sort_edges (CtxRasterizer *rasterizer)
 {
-  if (dither_red_blue == 0)
-    { return; }
-  for (int c = 0; c < 3; c ++)
-    {
-      int val = rgba[c] + ctx_dither_mask_a (x, y, 0, c==1?dither_green:dither_red_blue);
-      rgba[c] = CTX_CLAMP (val, 0, 255);
-    }
+  ctx_edge_qsort (& (rasterizer->edge_list.entries[0]), 0, rasterizer->edge_list.count-1);
 }
 
-inline static void
-ctx_dither_graya_u8 (uint8_t *rgba, int x, int y, int dither_red_blue, int dither_green)
+
+static inline void ctx_rasterizer_discard_edges (CtxRasterizer *rasterizer)
 {
-  if (dither_red_blue == 0)
-    { return; }
-  for (int c = 0; c < 1; c ++)
+  int scanline = rasterizer->scanline;
+  int slope_limit3 = CTX_RASTERIZER_AA_SLOPE_LIMIT3;
+  int slope_limit5 = CTX_RASTERIZER_AA_SLOPE_LIMIT5;
+  int slope_limit15 = CTX_RASTERIZER_AA_SLOPE_LIMIT15;
+  rasterizer->ending_edges = 0;
+  for (int i = 0; i < rasterizer->active_edges; i++)
     {
-      int val = rgba[c] + ctx_dither_mask_a (x, y, 0, dither_red_blue);
-      rgba[c] = CTX_CLAMP (val, 0, 255);
+      int edge_end =rasterizer->edge_list.entries[rasterizer->edges[i].index].data.s16[3]-1;
+      if (CTX_UNLIKELY(edge_end < scanline))
+        {
+          int dx_dy = abs(rasterizer->edges[i].delta);
+          if (dx_dy > slope_limit15)
+            { rasterizer->needs_aa15 --; }
+          else if (dx_dy > slope_limit5)
+            { rasterizer->needs_aa5 --; }
+          else if (dx_dy > slope_limit3)
+            { rasterizer->needs_aa3 --; }
+          rasterizer->edges[i] = rasterizer->edges[rasterizer->active_edges-1];
+          rasterizer->active_edges--;
+          i--;
+        }
+      else if (edge_end < scanline + CTX_FULL_AA)
+        rasterizer->ending_edges = 1; // only used as a flag!
     }
 }
-#endif
 
-CTX_INLINE static void
-ctx_RGBA8_deassociate_alpha (const uint8_t *in, uint8_t *out)
+inline static void ctx_rasterizer_increment_edges (CtxRasterizer *rasterizer, int count)
 {
-    uint32_t val = *((uint32_t*)(in));
-    int a = val >> CTX_RGBA8_A_SHIFT;
-    if (a)
-    {
-    if (a ==255)
-    {
-      *((uint32_t*)(out)) = val;
-    } else
+  rasterizer->scanline += count;
+  for (int i = 0; i < rasterizer->active_edges; i++)
     {
-      uint32_t g = (((val & CTX_RGBA8_G_MASK) * 255 / a) >> 8) & CTX_RGBA8_G_MASK;
-      uint32_t rb =(((val & CTX_RGBA8_RB_MASK) * 255 / a) >> 8) & CTX_RGBA8_RB_MASK;
-      *((uint32_t*)(out)) = g|rb|(a << CTX_RGBA8_A_SHIFT);
-    }
+      rasterizer->edges[i].val += rasterizer->edges[i].delta * count;
     }
-    else
+  for (int i = 0; i < rasterizer->pending_edges; i++)
     {
-      *((uint32_t*)(out)) = 0;
+      rasterizer->edges[CTX_MAX_EDGES-1-i].val += rasterizer->edges[CTX_MAX_EDGES-1-i].delta * count;
     }
 }
 
-CTX_INLINE static void
-ctx_u8_deassociate_alpha (int components, const uint8_t *in, uint8_t *out)
+/* feeds up to rasterizer->scanline,
+   keeps a pending buffer of edges - that encompass
+   the full incoming scanline,
+   feed until the start of the scanline and check for need for aa
+   in all of pending + active edges, then
+   again feed_edges until middle of scanline if doing non-AA
+   or directly render when doing AA
+*/
+inline static void ctx_rasterizer_feed_edges (CtxRasterizer *rasterizer)
 {
-  if (in[components-1])
-  {
-    if (in[components-1] != 255)
-    for (int c = 0; c < components-1; c++)
-      out[c] = (in[c] * 255) / in[components-1];
-    else
-    for (int c = 0; c < components-1; c++)
-      out[c] = in[c];
-    out[components-1] = in[components-1];
-  }
-  else
-  {
-  for (int c = 0; c < components; c++)
-    out[c] = 0;
-  }
-}
+  int miny;
+  CtxEntry *entries = rasterizer->edge_list.entries;
+  for (int i = 0; i < rasterizer->pending_edges; i++)
+    {
+      if (entries[rasterizer->edges[CTX_MAX_EDGES-1-i].index].data.s16[1] - 1 <= rasterizer->scanline)
+        {
+          if (CTX_LIKELY(rasterizer->active_edges < CTX_MAX_EDGES-2))
+            {
+              int no = rasterizer->active_edges;
+              rasterizer->active_edges++;
+              rasterizer->edges[no] = rasterizer->edges[CTX_MAX_EDGES-1-i];
+              rasterizer->edges[CTX_MAX_EDGES-1-i] =
+                rasterizer->edges[CTX_MAX_EDGES-1-rasterizer->pending_edges + 1];
+              rasterizer->pending_edges--;
+              i--;
+            }
+        }
+    }
+  int scanline = rasterizer->scanline;
+  while (CTX_LIKELY(rasterizer->edge_pos < rasterizer->edge_list.count &&
+         (miny=entries[rasterizer->edge_pos].data.s16[1]-1)  <= scanline + 15))
+    {
+      int maxy=entries[rasterizer->edge_pos].data.s16[3]-1;
+      if (CTX_LIKELY(rasterizer->active_edges < CTX_MAX_EDGES-2) &&
+          maxy >= scanline)
+        {
+          int dy = (entries[rasterizer->edge_pos].data.s16[3] - 1 - miny);
+          if (dy) /* skipping horizontal edges */
+            {
+              int yd = scanline - miny;
+              int no = rasterizer->active_edges;
+              rasterizer->active_edges++;
+              rasterizer->edges[no].index = rasterizer->edge_pos;
+              int index = rasterizer->edges[no].index;
+              int x0 = entries[index].data.s16[0];
+              int x1 = entries[index].data.s16[2];
+              rasterizer->edges[no].val = x0 * CTX_RASTERIZER_EDGE_MULTIPLIER;
+              int dx_dy;
+              dx_dy = CTX_RASTERIZER_EDGE_MULTIPLIER * (x1 - x0) / dy;
+              rasterizer->edges[no].delta = dx_dy;
+              rasterizer->edges[no].val += (yd * dx_dy);
 
-CTX_INLINE static void
-ctx_float_associate_alpha (int components, float *rgba)
-{
-  float alpha = rgba[components-1];
-  for (int c = 0; c < components-1; c++)
-    rgba[c] *= alpha;
-}
+              {
+                int abs_dx_dy = abs(dx_dy);
+                if (abs_dx_dy> CTX_RASTERIZER_AA_SLOPE_LIMIT3)
+                  { rasterizer->needs_aa3 ++; }
+                if (abs_dx_dy> CTX_RASTERIZER_AA_SLOPE_LIMIT5)
+                  { rasterizer->needs_aa5 ++; }
+                if (abs_dx_dy> CTX_RASTERIZER_AA_SLOPE_LIMIT15)
+                  { rasterizer->needs_aa15 ++; }
+              }
 
-CTX_INLINE static void
-ctx_float_deassociate_alpha (int components, float *rgba, float *dst)
-{
-  float ralpha = rgba[components-1];
-  if (ralpha != 0.0) ralpha = 1.0/ralpha;
+              if ((miny > scanline) )
+                {
+                  /* it is a pending edge - we add it to the end of the array
+                     and keep a different count for items stored here, like
+                     a heap and stack growing against each other
+                  */
+                  if (rasterizer->pending_edges < CTX_MAX_PENDING-1)
+                  {
+                    rasterizer->edges[CTX_MAX_EDGES-1-rasterizer->pending_edges] =
+                    rasterizer->edges[no];
+                    rasterizer->pending_edges++;
+                    rasterizer->active_edges--;
+                  }
+                }
+            }
+        }
+      rasterizer->edge_pos++;
+    }
 
-  for (int c = 0; c < components-1; c++)
-    dst[c] = (rgba[c] * ralpha);
-  dst[components-1] = rgba[components-1];
+    ctx_rasterizer_discard_edges (rasterizer);
 }
 
-CTX_INLINE static void
-ctx_RGBAF_associate_alpha (float *rgba)
+CTX_INLINE static int ctx_compare_edges2 (const void *ap, const void *bp)
 {
-  ctx_float_associate_alpha (4, rgba);
+  const CtxEdge *a = (const CtxEdge *) ap;
+  const CtxEdge *b = (const CtxEdge *) bp;
+  return a->val - b->val;
 }
 
-CTX_INLINE static void
-ctx_RGBAF_deassociate_alpha (float *rgba, float *dst)
+CTX_INLINE static void ctx_edge2_insertion_sort (CtxEdge *entries, int count)
 {
-  ctx_float_deassociate_alpha (4, rgba, dst);
+  for(int i=1; i<count; i++)
+   {
+     CtxEdge temp = entries[i];
+     int j = i-1;
+     while (j >= 0 && ctx_compare_edges2 (&temp, &entries[j])<0)
+     {
+       entries[j+1] = entries[j];
+       j--;
+     }
+     entries[j+1] = temp;
+   }
 }
 
-
-static inline void ctx_swap_red_green_u8 (void *data)
+static inline void ctx_rasterizer_sort_active_edges (CtxRasterizer *rasterizer)
 {
-  uint8_t *rgba = (uint8_t*)data;
-  uint8_t tmp = rgba[0];
-  rgba[0] = rgba[2];
-  rgba[2] = tmp;
+  ctx_edge2_insertion_sort (rasterizer->edges, rasterizer->active_edges);
 }
 
-static void
-ctx_fragment_swap_red_green_u8 (void *out, int count)
+#undef CTX_CMPSWP
+
+void ctx_coverage_post_process (CtxRasterizer *rasterizer, int minx, int maxx, uint8_t *coverage)
 {
-  uint8_t *rgba = (uint8_t*)out;
-  for (int x = 0; x < count; x++)
+  int scanline     = rasterizer->scanline;
+#if CTX_ENABLE_SHADOW_BLUR
+  if (CTX_UNLIKELY(rasterizer->in_shadow))
   {
-    ctx_swap_red_green_u8 (rgba);
-    rgba += 4;
+    float radius = rasterizer->state->gstate.shadow_blur;
+    int dim = 2 * radius + 1;
+    if (CTX_UNLIKELY (dim > CTX_MAX_GAUSSIAN_KERNEL_DIM))
+      dim = CTX_MAX_GAUSSIAN_KERNEL_DIM;
+    {
+      uint16_t temp[maxx-minx+1];
+      memset (temp, 0, sizeof (temp));
+      for (int x = dim/2; x < maxx-minx + 1 - dim/2; x ++)
+        for (int u = 0; u < dim; u ++)
+        {
+            temp[x] += coverage[minx+x+u-dim/2] * rasterizer->kernel[u] * 256;
+        }
+      for (int x = 0; x < maxx-minx + 1; x ++)
+        coverage[minx+x] = temp[x] >> 8;
+    }
   }
-}
-
-/**** rgb8 ***/
-
-static void
-ctx_fragment_image_rgb8_RGBA8_box (CtxRasterizer *rasterizer,
-                                   float x,
-                                   float y,
-                                   void *out, int count, float dx, float dy)
-{
-  uint8_t *rgba = (uint8_t *) out;
-  CtxSource *g = &rasterizer->state->gstate.source_fill;
-  CtxBuffer *buffer = g->texture.buffer->color_managed;
-  int width = buffer->width;
-  int height = buffer->height;
+#endif
 
-  for (int i = 0; i < count; i++)
+#if CTX_ENABLE_CLIP
+  if (CTX_UNLIKELY(rasterizer->clip_buffer &&  !rasterizer->clip_rectangle))
   {
-
-  int u = x;
-  int v = y;
-  if ( u < 0 || v < 0 ||
-       u >= width ||
-       v >= height)
+    /* perhaps not working right for clear? */
+    int y = scanline / 15;//rasterizer->aa;
+    uint8_t *clip_line = &((uint8_t*)(rasterizer->clip_buffer->data))[rasterizer->blit_width*y];
+    // XXX SIMD candidate
+    for (int x = minx; x <= maxx; x ++)
     {
-      *((uint32_t*)(rgba))= 0;
+#if CTX_1BIT_CLIP
+        coverage[x] = (coverage[x] * ((clip_line[x/8]&(1<<(x%8)))?255:0))/255;
+#else
+        coverage[x] = (coverage[x] * clip_line[x])/255;
+#endif
     }
-  else
+  }
+  if (CTX_UNLIKELY(rasterizer->aa == 1))
+  {
+    for (int x = minx; x <= maxx; x ++)
+     coverage[x] = coverage[x] > 127?255:0;
+  }
+#endif
+}
+
+inline static void
+ctx_rasterizer_generate_coverage (CtxRasterizer *rasterizer,
+                                  int            minx,
+                                  int            maxx,
+                                  uint8_t       *coverage,
+                                  int            winding,
+                                  int            aa_factor)
+{
+  CtxEntry *entries = rasterizer->edge_list.entries;;
+  CtxEdge  *edges = rasterizer->edges;
+  int scanline     = rasterizer->scanline;
+  int active_edges = rasterizer->active_edges;
+  int parity = 0;
+  int fraction = 255/aa_factor;
+  coverage -= minx;
+#define CTX_EDGE(no)      entries[edges[no].index]
+#define CTX_EDGE_YMIN(no) (CTX_EDGE(no).data.s16[1]-1)
+#define CTX_EDGE_X(no)    (edges[no].val)
+  for (int t = 0; t < active_edges -1;t++)
     {
-      int bpp = 3;
-      rgba[3]=255;
-      float factor = ctx_matrix_get_scale (&rasterizer->state->gstate.transform);
-          int dim = (1.0 / factor) / 2;
-          uint64_t sum[4]={0,0,0,0};
-          int count = 0;
-          for (int ou = - dim; ou < dim; ou++)
-          for (int ov = - dim; ov < dim; ov++)
+      int ymin = CTX_EDGE_YMIN (t);
+      if (scanline != ymin)
+        {
+          if (winding)
+            { parity += ( (CTX_EDGE (t).code == CTX_EDGE_FLIPPED) ?1:-1); }
+          else
+            { parity = 1 - parity; }
+        }
+
+       if (parity)
+        {
+          int x0 = CTX_EDGE_X (t);
+          int x1 = CTX_EDGE_X (t+1);
+          int graystart = x0 / (CTX_RASTERIZER_EDGE_MULTIPLIER*CTX_SUBDIV/256);
+          int first     = graystart / 256;
+          int grayend   = x1 / (CTX_RASTERIZER_EDGE_MULTIPLIER*CTX_SUBDIV/256);
+          int last      = grayend / 256;
+
+          if (CTX_UNLIKELY(first < minx))
+          { 
+            first = minx;
+            graystart=0;//255;
+          }
+          if (CTX_UNLIKELY(last > maxx))
           {
-            uint8_t *src = (uint8_t *) buffer->data;
-            int o = (v+ov) * width + (u + ou);
+            last = maxx;
+            grayend=255;
+          }
 
-            if (o>=0 && o < width * height)
-            {
-              src += o * bpp;
+          graystart=fraction- (graystart&0xff)/aa_factor;
+          grayend = (grayend & 0xff) / aa_factor;
 
-              for (int c = 0; c < bpp; c++)
-                sum[c] += src[c];
-              count ++;
-            }
+          if (first == last)
+            coverage[first] += (graystart-(fraction-grayend));
+          else if (first < last)
+          {
+              coverage[first] += graystart;
+              for (int x = first + 1; x < last; x++)
+                coverage[x] += fraction;
+              coverage[last]  += grayend;
           }
-          if (count)
-            for (int c = 0; c < bpp; c++)
-              rgba[c] = sum[c]/count;
-          ctx_RGBA8_associate_alpha_probably_opaque (rgba);
-    }
-    rgba += 4;
-    x += dx;
-    y += dy;
-  }
-#if CTX_DITHER
-//ctx_dither_rgba_u8 (rgba, x, y, rasterizer->format->dither_red_blue,
-//                    rasterizer->format->dither_green);
-#endif
+        }
+   }
 }
 
-static void
-ctx_fragment_image_rgb8_RGBA8_box_swap_red_green (CtxRasterizer *rasterizer,
-                                  float x,
-                                  float y,
-                                  void *out, int count, float dx, float dy)
+inline static void
+ctx_rasterizer_generate_coverage_set (CtxRasterizer *rasterizer,
+                                  int            minx,
+                                  int            maxx,
+                                  uint8_t       *coverage,
+                                  int            winding,
+                                  int            aa_factor)
 {
-  ctx_fragment_image_rgb8_RGBA8_box (rasterizer, x, y, out, count, dx, dy);
-  ctx_fragment_swap_red_green_u8 (out, count);
-}
+  CtxEntry *entries = rasterizer->edge_list.entries;;
+  CtxEdge  *edges = rasterizer->edges;
+  int scanline     = rasterizer->scanline;
+  int active_edges = rasterizer->active_edges;
+  int parity = 0;
+  int fraction = 255/aa_factor;
+  coverage -= minx;
+#define CTX_EDGE(no)      entries[edges[no].index]
+#define CTX_EDGE_YMIN(no) (CTX_EDGE(no).data.s16[1]-1)
+#define CTX_EDGE_X(no)    (edges[no].val)
+  for (int t = 0; t < active_edges -1;t++)
+    {
+      int ymin = CTX_EDGE_YMIN (t);
+      if (scanline != ymin)
+        {
+          if (winding)
+            { parity += ( (CTX_EDGE (t).code == CTX_EDGE_FLIPPED) ?1:-1); }
+          else
+            { parity = 1 - parity; }
+        }
 
-static void
-ctx_fragment_image_rgb8_RGBA8_bi (CtxRasterizer *rasterizer,
-                                  float x,
-                                  float y,
-                                  void *out, int count, float dx, float dy)
-{
-  uint8_t *rgba = (uint8_t *) out;
+       if (parity)
+        {
+          int x0 = CTX_EDGE_X (t);
+          int x1 = CTX_EDGE_X (t+1);
+          int graystart = x0 / (CTX_RASTERIZER_EDGE_MULTIPLIER*CTX_SUBDIV/256);
+          int first     = graystart / 256;
+          int grayend   = x1 / (CTX_RASTERIZER_EDGE_MULTIPLIER*CTX_SUBDIV/256);
+          int last      = grayend / 256;
 
-  CtxSource *g = &rasterizer->state->gstate.source_fill;
-  CtxBuffer *buffer = g->texture.buffer->color_managed;
-  int width = buffer->width;
-  int height = buffer->height;
+          if (CTX_UNLIKELY(first < minx))
+          { 
+            first = minx;
+            graystart=0;//255;
+          }
+          if (CTX_UNLIKELY(last > maxx))
+          {
+            last = maxx;
+            grayend=255;
+          }
 
-  for (int i = 0; i < count; i++)
-  {
+          graystart=fraction- (graystart&0xff)/aa_factor;
+          grayend = (grayend & 0xff) / aa_factor;
 
-  int u = x;
-  int v = y;
-  if ( u < 0 || v < 0 ||
-       u >= width ||
-       v >= height)
-    {
-      *((uint32_t*)(rgba))= 0;
-    }
-  else
-    {
-      int bpp = 3;
-      rgba[3]=255;
-      uint8_t *src00 = (uint8_t *) buffer->data;
-      int stride = buffer->stride;
-      src00 += v * stride + u * bpp;
-      uint8_t *src01 = src00;
-      if ( u + 1 < width)
-      {
-        src01 = src00 + bpp;
-      }
-      uint8_t *src11 = src01;
-      uint8_t *src10 = src00;
-      if ( v + 1 < height)
-      {
-        src10 = src00 + stride;
-        src11 = src01 + stride;
-      }
-      float dx = (x-(int)(x)) * 255.9f;
-      float dy = (y-(int)(y)) * 255.9f;
-      for (int c = 0; c < bpp; c++)
-      {
-        rgba[c] = ctx_lerp_u8 (ctx_lerp_u8 (src00[c], src01[c], dx),
-                               ctx_lerp_u8 (src10[c], src11[c], dx), dy);
-      }
-      ctx_RGBA8_associate_alpha_probably_opaque (rgba);
-    }
-    x += dx;
-    y += dy;
-    rgba += 4;
-  }
-#if CTX_DITHER
-//ctx_dither_rgba_u8 (rgba, x, y, rasterizer->format->dither_red_blue,
-//                    rasterizer->format->dither_green);
-#endif
+          if (first == last)
+            coverage[first] += (graystart-(fraction-grayend));
+          else if (first < last)
+          {
+              coverage[first] += graystart;
+              for (int x = first + 1; x < last; x++)
+                coverage[x] = fraction;
+              coverage[last]  += grayend;
+          }
+        }
+   }
 }
 
+#undef CTX_EDGE_Y0
+#undef CTX_EDGE
+
 static void
-ctx_fragment_image_rgb8_RGBA8_bi_swap_red_green (CtxRasterizer *rasterizer,
-                                  float x,
-                                  float y,
-                                  void *out, int count, float dx, float dy)
+ctx_rasterizer_reset (CtxRasterizer *rasterizer)
 {
-  ctx_fragment_image_rgb8_RGBA8_bi (rasterizer, x, y, out, count, dx, dy);
-  ctx_fragment_swap_red_green_u8 (out, count);
+  rasterizer->pending_edges   = 0;
+  rasterizer->active_edges    = 0;
+  rasterizer->has_shape       = 0;
+  rasterizer->has_prev        = 0;
+  rasterizer->edge_list.count = 0; // ready for new edges
+  rasterizer->edge_pos        = 0;
+  rasterizer->needs_aa3       = 0;
+  rasterizer->needs_aa5       = 0;
+  rasterizer->needs_aa15      = 0;
+  rasterizer->scanline        = 0;
+  if (!rasterizer->preserve)
+  {
+    rasterizer->scan_min      = 5000;
+    rasterizer->scan_max      = -5000;
+    rasterizer->col_min       = 5000;
+    rasterizer->col_max       = -5000;
+  }
+  //rasterizer->comp_op       = NULL;
 }
 
-static CTX_INLINE void
-ctx_fragment_image_rgb8_RGBA8_nearest (CtxRasterizer *rasterizer,
-                                       float x,
-                                       float y,
-                                       void *out, int count, float dx, float dy)
+static void
+ctx_rasterizer_rasterize_edges (CtxRasterizer *rasterizer, int winding
+#if CTX_SHAPE_CACHE
+                                ,CtxShapeEntry *shape
+#endif
+                               )
 {
-  CtxSource *g = &rasterizer->state->gstate.source_fill;
-  CtxBuffer *buffer = g->texture.buffer;
-  if (buffer->color_managed)
-   buffer = buffer->color_managed;
-  uint8_t *rgba = (uint8_t *) out;
-  uint8_t *src = (uint8_t *) buffer->data;
-  int bwidth = buffer->width;
-  int bheight = buffer->height;
-  int stride = buffer->stride;
+  uint8_t *dst = ( (uint8_t *) rasterizer->buf);
 
-  x += 0.5f;
-  y += 0.5f;
+  int real_aa = rasterizer->aa;
 
-  if (CTX_UNLIKELY (dy == 0.0f && dx > 0.999f && dx < 1.001f))
-  {
-    int v = y;
-    int u = x;
-  
-    if (v < buffer->height && v > 0)
-    {
-      int o = v * stride + u * 3;
-      int i;
-      for (i = 0; i < count && u < bwidth && u <0; i++)
-      {
-        *((uint32_t*)(rgba))= 0;
-        rgba += 4;
-        o += 3;
-        u+=1;
-      }
+  int scan_start = rasterizer->blit_y * CTX_FULL_AA;
+  int scan_end   = scan_start + rasterizer->blit_height * CTX_FULL_AA;
+  int blit_width = rasterizer->blit_width;
+  int blit_max_x = rasterizer->blit_x + blit_width;
+  int minx       = rasterizer->col_min / CTX_SUBDIV - rasterizer->blit_x;
+  int maxx       = (rasterizer->col_max + CTX_SUBDIV-1) / CTX_SUBDIV - rasterizer->blit_x;
 
-      for (; i < count && u < bwidth; i++)
-      {
-        rgba[0] = src[o];
-        rgba[1] = src[o+1];
-        rgba[2] = src[o+2]; 
-        rgba[3]=255;
-        rgba += 4;
-        o += 3;
-        u+=1;
-      }
-      for (; i < count; i++)
-      {
-        *((uint32_t*)(rgba))= 0;
-        rgba += 4;
-      }
-    }
-    else
-    {
-      for (int i = 0; i < count; i++)
-      {
-        *((uint32_t*)(rgba))= 0;
-        rgba+=4;
-      }
-    }
-  }
-  else
-  {
-    int u = x;
-    int v = y;
-    int i;
-    for (i = 0; i < count && u < bwidth && u <0; i++)
-    {
-      u = x;
-      v = y;;
-      *((uint32_t*)(rgba))= 0;
-      x += dx;
-      y += dy;
-      rgba += 4;
-    }
-    for (; i < count && u < bwidth; i++)
+#if 1
+  if (
+#if CTX_SHAPE_CACHE
+    !shape &&
+#endif
+    maxx > blit_max_x - 1)
+    { maxx = blit_max_x - 1; }
+#endif
+#if 1
+  if (rasterizer->state->gstate.clip_min_x>
+      minx)
+    { minx = rasterizer->state->gstate.clip_min_x; }
+  if (rasterizer->state->gstate.clip_max_x <
+      maxx)
+    { maxx = rasterizer->state->gstate.clip_max_x; }
+#endif
+  if (minx < 0)
+    { minx = 0; }
+  if (minx >= maxx)
     {
-      u = x;
-      v = y;
-    if (CTX_UNLIKELY(v < 0 || v >= bheight))
-      {
-        *((uint32_t*)(rgba))= 0;
-      }
-    else
-      {
-        int o = v * stride + u * 3;
-        rgba[0] = src[o];
-        rgba[1] = src[o+1];
-        rgba[2] = src[o+2]; 
-        rgba[3]=255;
-      }
-  
-      rgba += 4;
-      x += dx;
-      y += dy;
+      ctx_rasterizer_reset (rasterizer);
+      return;
     }
-      for (; i < count; i++)
-      {
-        *((uint32_t*)(rgba))= 0;
-        rgba += 4;
-      }
-  }
-#if CTX_DITHER
-  //ctx_dither_rgba_u8 (rgba, x, y, rasterizer->format->dither_red_blue,
-  //                    rasterizer->format->dither_green);
+#if CTX_SHAPE_CACHE
+  uint8_t _coverage[shape?2:maxx-minx+1];
+#else
+  uint8_t _coverage[maxx-minx+1];
 #endif
-}
-
+  uint8_t *coverage = &_coverage[0];
 
-static CTX_INLINE void
-ctx_fragment_image_rgb8_RGBA8_nearest_swap_red_green (CtxRasterizer *rasterizer,
-                                                      float x,
-                                                      float y,
-                                                      void *out, int count, float dx, float dy)
-{
-  ctx_fragment_image_rgb8_RGBA8_nearest (rasterizer, x, y, out, count, dx, dy);
-  ctx_fragment_swap_red_green_u8 (out, count);
-}
 
-static void
-ctx_fragment_image_rgb8_RGBA8 (CtxRasterizer *rasterizer,
-                               float x,
-                               float y,
-                               void *out, int count, float dx, float dy)
-{
-  if (rasterizer->state->gstate.image_smoothing)
-  {
-    float factor = ctx_matrix_get_scale (&rasterizer->state->gstate.transform);
-    if (factor <= 0.50f)
+#if CTX_SHAPE_CACHE
+  if (shape)
     {
-      if (rasterizer->swap_red_green)
-        ctx_fragment_image_rgb8_RGBA8_box_swap_red_green (rasterizer, x, y, out, count, dx, dy);
-      else
-        ctx_fragment_image_rgb8_RGBA8_box (rasterizer, x, y, out, count, dx, dy);
+      coverage = &shape->data[0];
     }
-#if CTX_ALWAYS_USE_NEAREST_FOR_SCALE1
-    else if (factor > 0.99f && factor < 1.01f)
+#endif
+  ctx_assert (coverage);
+  rasterizer->scan_min -= (rasterizer->scan_min % CTX_FULL_AA);
+#if CTX_SHAPE_CACHE
+  if (shape)
     {
-      // XXX missing translate test
-      if (rasterizer->swap_red_green)
-        ctx_fragment_image_rgb8_RGBA8_nearest_swap_red_green (rasterizer, x, y, out, count, dx, dy);
-      else
-        ctx_fragment_image_rgb8_RGBA8_nearest (rasterizer, x, y, out, count, dx, dy);
+      scan_start = rasterizer->scan_min;
+      scan_end   = rasterizer->scan_max;
     }
+  else
 #endif
-    else
     {
-      if (rasterizer->swap_red_green)
-        ctx_fragment_image_rgb8_RGBA8_bi_swap_red_green (rasterizer, x, y, out, count, dx, dy);
-      else
-        ctx_fragment_image_rgb8_RGBA8_bi (rasterizer, x, y, out, count, dx, dy);
+      if (rasterizer->scan_min > scan_start)
+        {
+          dst += (rasterizer->blit_stride * (rasterizer->scan_min-scan_start) / CTX_FULL_AA);
+          scan_start = rasterizer->scan_min;
+        }
+      if (rasterizer->scan_max < scan_end)
+        { scan_end = rasterizer->scan_max; }
     }
+  if (rasterizer->state->gstate.clip_min_y * CTX_FULL_AA > scan_start )
+    { 
+       dst += (rasterizer->blit_stride * (rasterizer->state->gstate.clip_min_y * CTX_FULL_AA -scan_start) / 
CTX_FULL_AA);
+       scan_start = rasterizer->state->gstate.clip_min_y * CTX_FULL_AA; 
+    }
+  if (rasterizer->state->gstate.clip_max_y * CTX_FULL_AA < scan_end)
+    { scan_end = rasterizer->state->gstate.clip_max_y * CTX_FULL_AA; }
+  if (scan_start > scan_end ||
+      (scan_start > (rasterizer->blit_y + rasterizer->blit_height) * CTX_FULL_AA) ||
+      (scan_end < (rasterizer->blit_y) * CTX_FULL_AA))
+  { 
+    /* not affecting this rasterizers scanlines */
+    ctx_rasterizer_reset (rasterizer);
+    return;
   }
-  else
-  {
-    if (rasterizer->swap_red_green)
-      ctx_fragment_image_rgb8_RGBA8_nearest_swap_red_green (rasterizer, x, y, out, count, dx, dy);
-    else
-      ctx_fragment_image_rgb8_RGBA8_nearest (rasterizer, x, y, out, count, dx, dy);
-  }
-#if CTX_DITHER
-  {
-  uint8_t *rgba = (uint8_t*)out;
-  ctx_dither_rgba_u8 (rgba, x, y, rasterizer->format->dither_red_blue,
-                      rasterizer->format->dither_green);
-  }
-#endif
-}
-
 
-/************** rgba8 */
-
-static void
-ctx_fragment_image_rgba8_RGBA8_box (CtxRasterizer *rasterizer,
-                                    float x,
-                                    float y,
-                                    void *out, int count, float dx, float dy)
-{
-  uint8_t *rgba = (uint8_t *) out;
-  CtxSource *g = &rasterizer->state->gstate.source_fill;
-  CtxBuffer *buffer = g->texture.buffer->color_managed;
-
-  for (int i = 0; i < count; i ++)
+  ctx_rasterizer_sort_edges (rasterizer);
   {
+    int halfstep2 = CTX_FULL_AA/2;
+    int halfstep  = halfstep2 + 1;
+    rasterizer->needs_aa3  = 0;
+    rasterizer->needs_aa5  = 0;
+    rasterizer->needs_aa15 = 0;
+    rasterizer->scanline = scan_start;
+    ctx_rasterizer_feed_edges (rasterizer); 
 
-  int u = x;
-  int v = y;
-  if ( u < 0 || v < 0 ||
-       u >= buffer->width ||
-       v >= buffer->height)
-    {
-      *((uint32_t*)(rgba))= 0;
-    }
-  else
+  for (; rasterizer->scanline <= scan_end;)
     {
-      int bpp = 4;
-      float factor = ctx_matrix_get_scale (&rasterizer->state->gstate.transform);
-          int dim = (1.0 / factor) / 2;
-          uint64_t sum[4]={0,0,0,0};
-          int count = 0;
-          int width = buffer->width;
-          int height = buffer->height;
-          for (int ou = - dim; ou < dim; ou++)
-          for (int ov = - dim; ov < dim; ov++)
-          {
-            uint8_t *src = (uint8_t *) buffer->data;
-            int o = (v+ov) * width + (u + ou);
-
-            if (o>=0 && o < width * height)
-            {
-              src += o * bpp;
+      int contains_edge_end = rasterizer->pending_edges ||
+                              rasterizer->ending_edges;
 
-              for (int c = 0; c < bpp; c++)
-                sum[c] += src[c];
-              count ++;
-            }
-          }
-          if (count)
-            for (int c = 0; c < bpp; c++)
-              rgba[c] = sum[c]/count;
-          ctx_RGBA8_associate_alpha_probably_opaque (rgba);
-    }
-    rgba += 4;
-    x += dx;
-    y += dy;
-  }
-#if CTX_DITHER
-//ctx_dither_rgba_u8 (rgba, x, y, rasterizer->format->dither_red_blue,
-//                    rasterizer->format->dither_green);
+      // check if all pending edges are scanline aligned at start
+      ctx_memset (coverage, 0,
+#if CTX_SHAPE_CACHE
+                  shape?shape->width:
 #endif
-}
-
-
-static void
-ctx_fragment_image_rgba8_RGBA8_nearest (CtxRasterizer *rasterizer,
-                                        float x,
-                                        float y,
-                                        void *out, int count, float dx, float dy)
-{
-  CtxSource *g = &rasterizer->state->gstate.source_fill;
-  CtxBuffer *buffer = g->texture.buffer;
-  if (buffer->color_managed)
-    buffer = buffer->color_managed;
-  int ideltax = dx * 65536;
-  int ideltay = dy * 65536;
-  uint32_t *src = (uint32_t *) buffer->data;
-  uint32_t *dst = (uint32_t*)out;
-  int bwidth  = buffer->width;
-  int bheight = buffer->height;
-  x += 0.5f;
-  y += 0.5f;
+                  sizeof (_coverage) );
 
-#if 1
-  if (CTX_UNLIKELY(ideltay == 0 && ideltax == 65536))
-  {
-    int i = 0;
-    int u = x;
-    int v = y;
-    if (!(v >= 0 && v < bheight))
+    if (contains_edge_end)
     {
-      for (i = 0 ; i < count; i++)
-        *dst++ = 0;
-      return;
+        for (int i = 0; i < real_aa; i++)
+        {
+          ctx_rasterizer_feed_edges (rasterizer);
+          ctx_rasterizer_sort_active_edges (rasterizer);
+          ctx_rasterizer_generate_coverage (rasterizer, minx, maxx, coverage, winding, real_aa);
+          ctx_rasterizer_increment_edges (rasterizer, CTX_FULL_AA/real_aa);
+        }
     }
-    src += bwidth * v + u;
-    while (count && !(u >= 0))
+    else if (!rasterizer->needs_aa3) // if it doesnt need aa3 it doesnt need aa5 or aa15 either
     {
-      *dst++ = 0;
-      src ++;
-      u++;
-      count--;
+      ctx_rasterizer_increment_edges (rasterizer, halfstep2);
+      ctx_rasterizer_feed_edges (rasterizer);
+
+      ctx_rasterizer_sort_active_edges (rasterizer);
+      ctx_rasterizer_generate_coverage_set (rasterizer, minx, maxx, coverage, winding, 1);
+      ctx_rasterizer_increment_edges (rasterizer, halfstep);
     }
-    int limit = ctx_mini (count, bwidth - u);
-    if (limit>0)
-    {
-      memcpy (dst, src, limit * 4);
-      dst += limit;
-      i = limit;
+    else  if (rasterizer->needs_aa15)
+      {
+        for (int i = 0; i < CTX_FULL_AA; i++)
+        {
+          ctx_rasterizer_feed_edges (rasterizer);
+          ctx_rasterizer_sort_active_edges (rasterizer);
+          ctx_rasterizer_generate_coverage (rasterizer, minx, maxx, coverage, winding, CTX_FULL_AA);
+          ctx_rasterizer_increment_edges (rasterizer, 1);
+        }
     }
-    for (;i < count; i++)
-      *dst++ = 0;
-    return;
-  }
-#endif
-
-  {
-    int i = 0;
-    float u1 = x + dx * (count-1);
-    float v1 = y + dy * (count-1);
-    uint32_t *edst = ((uint32_t*)out)+count;
-    for (; i < count; )
+    else if (rasterizer->needs_aa5)
     {
-      if ((u1 < 0.0f || v1 < 0.0f || u1 >= bwidth || v1 >= bheight))
+      for (int i = 0; i < CTX_FULL_AA; i+=3)
       {
-        *edst-- = 0;
-        count --;
-        u1 -= dx;
-        v1 -= dy;
+        ctx_rasterizer_feed_edges (rasterizer);
+        ctx_rasterizer_sort_active_edges (rasterizer);
+        ctx_rasterizer_generate_coverage (rasterizer, minx, maxx, coverage, winding, CTX_FULL_AA/3);
+        ctx_rasterizer_increment_edges (rasterizer, 3);
       }
-      else break;
+        //for (int x = minx; x <= maxx; x++) coverage[x-minx] *= 0.5;
     }
-
-
-    for (i = 0; i < count; i ++)
+    else if (rasterizer->needs_aa3)
     {
-      if ((x < 0.0f || y < 0.0f || x >= bwidth || y >= bheight))
+      for (int i = 0; i < CTX_FULL_AA; i+=5)
       {
-        *dst = 0;
-        dst++;
-        x += dx;
-        y += dy;
+        ctx_rasterizer_feed_edges (rasterizer);
+        ctx_rasterizer_sort_active_edges (rasterizer);
+        ctx_rasterizer_generate_coverage (rasterizer, minx, maxx, coverage, winding, CTX_FULL_AA/5);
+        ctx_rasterizer_increment_edges (rasterizer, 5);
       }
-      else break;
+      //for (int x = minx; x <= maxx; x++) coverage[x-minx] *= 0.75;
     }
-
-
-    uint32_t ix = x * 65536;
-    uint32_t iy = y * 65536;
-
-    for (; i < count; i ++)
+    else
     {
-      *dst = src[(iy>>16) * bwidth + (ix>>16)];
-      ix += ideltax;
-      iy += ideltay;
-      dst++;
+      ctx_rasterizer_increment_edges (rasterizer, halfstep2);
+      ctx_rasterizer_feed_edges (rasterizer);
+
+      ctx_rasterizer_sort_active_edges (rasterizer);
+      ctx_rasterizer_generate_coverage_set (rasterizer, minx, maxx, coverage, winding, 1);
+      ctx_rasterizer_increment_edges (rasterizer, halfstep);
     }
-  }
-}
+  ctx_coverage_post_process (rasterizer, minx, maxx, coverage - minx);
 
 
 
-static void
-ctx_fragment_image_rgba8_RGBA8_bi (CtxRasterizer *rasterizer,
-                                   float x,
-                                   float y,
-                                   void *out, int count, float dx, float dy)
-{
-  uint8_t *rgba = (uint8_t *) out;
-  float ox = (x-(int)(x));
-  float oy = (y-(int)(y));
 
-  CtxSource *g = &rasterizer->state->gstate.source_fill;
-  CtxBuffer *buffer = g->texture.buffer->color_managed;
-  int bwidth = buffer->width;
-  int bheight = buffer->height;
-  int i = 0;
+        {
+#if CTX_SHAPE_CACHE
+          if (shape == NULL)
+#endif
+            {
+#if 0
+              if (CTX_FULL_AA==1)
+              {
+                for (int x = 0; x < maxx-minx; x++)
+                  coverage
+              }
+#endif
+              ctx_rasterizer_apply_coverage (rasterizer,
+                                             &dst[(minx * rasterizer->format->bpp) /8],
+                                             minx,
+                                             coverage, maxx-minx + 1);
+            }
+        }
+#if CTX_SHAPE_CACHE
+      if (shape)
+        {
+          coverage += shape->width;
+        }
+#endif
+      dst += rasterizer->blit_stride;
+    }
+  }
 
-  if (dy == 0.0f && dx > 0.0f)
+  if (rasterizer->state->gstate.compositing_mode == CTX_COMPOSITE_SOURCE_OUT ||
+      rasterizer->state->gstate.compositing_mode == CTX_COMPOSITE_SOURCE_IN ||
+      rasterizer->state->gstate.compositing_mode == CTX_COMPOSITE_DESTINATION_IN ||
+      rasterizer->state->gstate.compositing_mode == CTX_COMPOSITE_COPY ||
+      rasterizer->state->gstate.compositing_mode == CTX_COMPOSITE_DESTINATION_ATOP ||
+      rasterizer->state->gstate.compositing_mode == CTX_COMPOSITE_CLEAR)
   {
-    if ((dx > 0.99f && dx < 1.01f && 
-         ox < 0.01 && oy < 0.01))
-    {
-      ctx_fragment_image_rgba8_RGBA8_nearest (rasterizer,
-                                   x, y, out, count, dx, dy);
-      return;
-    }
+     /* fill in the rest of the blitrect when compositing mode permits it */
+     uint8_t nocoverage[rasterizer->blit_width];
+     //int gscan_start = rasterizer->state->gstate.clip_min_y * CTX_FULL_AA;
+     int gscan_start = rasterizer->state->gstate.clip_min_y * CTX_FULL_AA;
+     int gscan_end = rasterizer->state->gstate.clip_max_y * CTX_FULL_AA;
+     memset (nocoverage, 0, sizeof(nocoverage));
+     int startx   = rasterizer->state->gstate.clip_min_x;
+     int endx     = rasterizer->state->gstate.clip_max_x;
+     int clipw    = endx-startx + 1;
+     uint8_t *dst = ( (uint8_t *) rasterizer->buf);
 
-    uint32_t *data = ((uint32_t*)buffer->data);
+     dst = (uint8_t*)(rasterizer->buf) + rasterizer->blit_stride * (gscan_start / CTX_FULL_AA);
+     for (rasterizer->scanline = gscan_start; rasterizer->scanline < scan_start;)
+     {
+       ctx_rasterizer_apply_coverage (rasterizer,
+                                      &dst[ (startx * rasterizer->format->bpp) /8],
+                                      0,
+                                      nocoverage, clipw);
+       rasterizer->scanline += CTX_FULL_AA;
+       dst += rasterizer->blit_stride;
+     }
+     if (minx < startx)
+     {
+     dst = (uint8_t*)(rasterizer->buf) + rasterizer->blit_stride * (scan_start / CTX_FULL_AA);
+     for (rasterizer->scanline = scan_start; rasterizer->scanline < scan_end;)
+     {
+       ctx_rasterizer_apply_coverage (rasterizer,
+                                      &dst[ (startx * rasterizer->format->bpp) /8],
+                                      0,
+                                      nocoverage, minx-startx);
+       dst += rasterizer->blit_stride;
+     }
+     }
+     if (endx > maxx)
+     {
+     dst = (uint8_t*)(rasterizer->buf) + rasterizer->blit_stride * (scan_start / CTX_FULL_AA);
+     for (rasterizer->scanline = scan_start; rasterizer->scanline < scan_end;)
+     {
+       ctx_rasterizer_apply_coverage (rasterizer,
+                                      &dst[ (maxx * rasterizer->format->bpp) /8],
+                                      0,
+                                      nocoverage, endx-maxx);
 
+       rasterizer->scanline += CTX_FULL_AA;
+       dst += rasterizer->blit_stride;
+     }
+     }
+     dst = (uint8_t*)(rasterizer->buf) + rasterizer->blit_stride * (scan_end / CTX_FULL_AA);
+     // XXX valgrind/asan this
+     if(0)for (rasterizer->scanline = scan_end; rasterizer->scanline/CTX_FULL_AA < gscan_end-1;)
+     {
+       ctx_rasterizer_apply_coverage (rasterizer,
+                                      &dst[ (startx * rasterizer->format->bpp) /8],
+                                      0,
+                                      nocoverage, clipw-1);
 
-    for (i= 0; i < count; i ++)
-    {
-      int u = x;
-      int v = y;
-      int ut = x + 1.5;
-      int vt = y + 1.5;
-      if ( ut  <= 0 || vt  <= 0 || u >= buffer->width || v >= buffer->height)
-      {
-        *((uint32_t*)(rgba))= 0;
-      }
-      else
-        break;
-      x += dx;
-      y += dy;
-      rgba += 4;
-    }
+       rasterizer->scanline += CTX_FULL_AA;
+       dst += rasterizer->blit_stride;
+     }
+  }
+  ctx_rasterizer_reset (rasterizer);
+}
 
-  uint32_t yi = y * 65536;
-  uint32_t xi = x * 65536;
-  int xi_delta = dx * 65536;
-  int loaded = -4;
-  uint32_t s0_ga = 0, s0_rb = 0, s1_ga = 0, s1_rb = 0;
- 
-  int v = yi >> 16;
-  data += bwidth * v;
-  int dv = (yi >> 8) & 0xff;
-  if (y < bheight && y >= 0)
-  for (; i < count; i ++)
+inline static int
+ctx_is_transparent (CtxRasterizer *rasterizer, int stroke)
+{
+  CtxGState *gstate = &rasterizer->state->gstate;
+  if (gstate->global_alpha_u8 == 0)
+    return 1;
+  if (gstate->source_fill.type == CTX_SOURCE_COLOR)
   {
-  int u = xi >> 16;
+    uint8_t ga[2];
+    ctx_color_get_graya_u8 (rasterizer->state, &gstate->source_fill.color, ga);
+    if (ga[1] == 0)
+      return 1;
+  }
+  return 0;
+}
+
+static void
+ctx_rasterizer_fill (CtxRasterizer *rasterizer)
+{
+  int count = rasterizer->preserve?rasterizer->edge_list.count:0;
+
+  CtxEntry temp[count]; /* copy of already built up path's poly line
+                          XXX - by building a large enough path
+                          the stack can be smashed!
+                         */
+  if (rasterizer->preserve)
+    { memcpy (temp, rasterizer->edge_list.entries, sizeof (temp) ); }
 
-  if (u >= buffer->width)
+#if CTX_ENABLE_SHADOW_BLUR
+  if (rasterizer->in_shadow)
+  {
+  for (int i = 0; i < rasterizer->edge_list.count; i++)
     {
-      break;
+      CtxEntry *entry = &rasterizer->edge_list.entries[i];
+      entry->data.s16[2] += rasterizer->shadow_x * CTX_SUBDIV;
+      entry->data.s16[3] += rasterizer->shadow_y * CTX_FULL_AA;
     }
-  else if (u < 0 || v < 0) // default to next sample down and to right
-  {
-      int got_prev_pix = (u >= 0);
-      int got_prev_row = (v>=0);
-      uint32_t *src11 = data  + u + bwidth + 1;
-      uint32_t *src10 = src11 - got_prev_pix;
-      uint32_t *src01 = src11 - bwidth * got_prev_row;
-      uint32_t *src00 = src10 - bwidth * got_prev_row;
-      uint32_t s00 = *src00;
-      uint32_t s01 = *src01;
-      uint32_t s10 = *src10;
-      uint32_t s11 = *src11;
-      ctx_lerp_RGBA8_split (s00,s10, dv, &s0_ga, &s0_rb);
-      ctx_lerp_RGBA8_split (s01,s11, dv, &s1_ga, &s1_rb);
-  }
-  else if (loaded + 1 == u)
-  {
-      int next_row = ( v + 1 < bheight) * bwidth;
-      int next_pix = (u + 1 < bwidth);
-      uint32_t *src00 = data  + u;
-      uint32_t s01 = ((uint32_t*)src00)[next_pix];
-      uint32_t s11 = ((uint32_t*)src00)[next_row + next_pix];
-      s0_ga = s1_ga;
-      s0_rb = s1_rb;
-      ctx_lerp_RGBA8_split (s01,s11, dv, &s1_ga, &s1_rb);
-  }
-  else if (loaded != u)
-  {
-      int next_row = (v + 1 < bheight) * bwidth;
-      int next_pix = (u + 1 < bwidth);
-      uint32_t *src00 = data  + u;
-      uint32_t s00 = ((uint32_t*)src00)[0];
-      uint32_t s01 = ((uint32_t*)src00)[next_pix];
-      uint32_t s10 = ((uint32_t*)src00)[next_row];
-      uint32_t s11 = ((uint32_t*)src00)[next_row + next_pix];
-      ctx_lerp_RGBA8_split (s00,s10, dv, &s0_ga, &s0_rb);
-      ctx_lerp_RGBA8_split (s01,s11, dv, &s1_ga, &s1_rb);
-  }
-    ((uint32_t*)(&rgba[0]))[0] = 
-      ctx_lerp_RGBA8_merge (s0_ga, s0_rb, s1_ga, s1_rb, (xi>>8));
-    loaded = u;
-    xi += xi_delta;
-    rgba += 4;
+    rasterizer->scan_min += rasterizer->shadow_y * CTX_FULL_AA;
+    rasterizer->scan_max += rasterizer->shadow_y * CTX_FULL_AA;
+    rasterizer->col_min  += (rasterizer->shadow_x - rasterizer->state->gstate.shadow_blur * 3 + 1) * 
CTX_SUBDIV;
+    rasterizer->col_max  += (rasterizer->shadow_x + rasterizer->state->gstate.shadow_blur * 3 + 1) * 
CTX_SUBDIV;
   }
+#endif
 
-  }
+  if (ctx_is_transparent (rasterizer, 0) ||
+      rasterizer->scan_min / CTX_FULL_AA > rasterizer->blit_y + rasterizer->blit_height ||
+      rasterizer->scan_max / CTX_FULL_AA < rasterizer->blit_y ||
+      rasterizer->col_min / CTX_SUBDIV > rasterizer->blit_x + rasterizer->blit_width ||
+      rasterizer->col_max / CTX_SUBDIV < rasterizer->blit_x)
+    {
+      ctx_rasterizer_reset (rasterizer);
+    }
   else
   {
+    if (rasterizer->comp_op == NULL)
+      ctx_compositor_setup_default (rasterizer);
 
-  uint32_t *data = ((uint32_t*)buffer->data);
+    rasterizer->state->min_x =
+      ctx_mini (rasterizer->state->min_x, rasterizer->col_min / CTX_SUBDIV);
+    rasterizer->state->max_x =
+      ctx_maxi (rasterizer->state->max_x, rasterizer->col_max / CTX_SUBDIV);
+    rasterizer->state->min_y =
+      ctx_mini (rasterizer->state->min_y, rasterizer->scan_min / CTX_FULL_AA);
+    rasterizer->state->max_y =
+      ctx_maxi (rasterizer->state->max_y, rasterizer->scan_max / CTX_FULL_AA);
 
-    for (i= 0; i < count; i ++)
-    {
-      int u = x;
-      int v = y;
-      int ut = x + 1.5;
-      int vt = y + 1.5;
-      if ( ut  <= 0 || vt  <= 0 || u >= buffer->width || v >= buffer->height)
-      {
-        *((uint32_t*)(rgba))= 0;
-      }
-      else
-        break;
-      x += dx;
-      y += dy;
-      rgba += 4;
-    }
+    ctx_rasterizer_finish_shape (rasterizer);
 
-  uint32_t yi = y * 65536;
-  uint32_t xi = x * 65536;
+    uint32_t hash = ctx_rasterizer_poly_to_edges (rasterizer);
+    if (hash){};
 
-  int yi_delta = dy * 65536;
-  int xi_delta = dx * 65536;
+#if CTX_SHAPE_CACHE
+    int width = (rasterizer->col_max + (CTX_SUBDIV-1) ) / CTX_SUBDIV - rasterizer->col_min/CTX_SUBDIV + 1;
+    int height = (rasterizer->scan_max + (CTX_FULL_AA-1) ) / CTX_FULL_AA - rasterizer->scan_min / 
CTX_FULL_AA + 1;
+    if (width * height < CTX_SHAPE_CACHE_DIM && width >=1 && height >= 1
+        && width < CTX_SHAPE_CACHE_MAX_DIM
+        && height < CTX_SHAPE_CACHE_MAX_DIM 
+#if CTX_ENABLE_SHADOW_BLUR
+        && !rasterizer->in_shadow
+#endif
+        )
+      {
+        int scan_min = rasterizer->scan_min;
+        int col_min = rasterizer->col_min;
+        scan_min -= (scan_min % CTX_FULL_AA);
+        int y0 = scan_min / CTX_FULL_AA;
+        int y1 = y0 + height;
+        int x0 = col_min / CTX_SUBDIV;
+        int ymin = y0;
+        int x1 = x0 + width;
+        int clip_x_min = rasterizer->blit_x;
+        int clip_x_max = rasterizer->blit_x + rasterizer->blit_width - 1;
+        int clip_y_min = rasterizer->blit_y;
+        int clip_y_max = rasterizer->blit_y + rasterizer->blit_height - 1;
 
-  int loaded = -4;
-  uint32_t s00 = 0, s01 = 0, s10 = 0, s11 = 0;
+        int dont_cache = 0;
+        if (x1 >= clip_x_max)
+          { x1 = clip_x_max;
+            dont_cache = 1;
+          }
+        int xo = 0;
+        if (x0 < clip_x_min)
+          {
+            xo = clip_x_min - x0;
+            x0 = clip_x_min;
+            dont_cache = 1;
+          }
+        if (y0 < clip_y_min || y1 >= clip_y_max)
+          dont_cache = 1;
+        if (dont_cache || !_ctx_shape_cache_enabled)
+        {
+          ctx_rasterizer_rasterize_edges (rasterizer, rasterizer->state->gstate.fill_rule
+#if CTX_SHAPE_CACHE
+                                        , NULL
+#endif
+                                       );
+        }
+        else
+        {
 
-  for (; i < count; i ++)
-  {
-  int u = xi >> 16;
-  int v = yi >> 16;
-  //int ut = (xi + 3*65536/2)>>16;
-  //int vt = (yi + 3*65536/2)>>16;
+        rasterizer->scanline = scan_min;
+        CtxShapeEntry *shape = ctx_shape_entry_find (rasterizer, hash, width, height); 
 
-  int offset = bwidth * v + u;
+        if (shape->uses == 0)
+          {
+            CtxBuffer *buffer_backup = rasterizer->clip_buffer;
+            rasterizer->clip_buffer = NULL;
+            ctx_rasterizer_rasterize_edges (rasterizer, rasterizer->state->gstate.fill_rule, shape);
+            rasterizer->clip_buffer = buffer_backup;
+          }
+        rasterizer->scanline = scan_min;
 
-  if ((
-       u >= buffer->width ||
-       v  <= -65536 ||
-       u  <= -65536 ||
-       v >= buffer->height))
+        int ewidth = x1 - x0;
+        if (ewidth>0)
+        {
+          if (rasterizer->clip_buffer && !rasterizer->clip_rectangle)
+          {
+          uint8_t composite[ewidth];
+          for (int y = y0; y < y1; y++)
+            {
+              if ( (y >= clip_y_min) && (y <= clip_y_max) )
+                {
+                    for (int x = 0; x < ewidth; x++)
+                    {
+                      int val = shape->data[shape->width * (int)(y-ymin) + xo + x];
+                      // XXX : not valid for 1bit clip buffers
+                      val = (val*((uint8_t*)rasterizer->clip_buffer->data) [
+                              ((y-rasterizer->blit_y) * rasterizer->blit_width) + x0 + x])/255;
+                      composite[x] = val;
+                    }
+                    ctx_rasterizer_apply_coverage (rasterizer,
+                                                 ( (uint8_t *) rasterizer->buf) + (y-rasterizer->blit_y) * 
rasterizer->blit_stride + (int) (x0) * rasterizer->format->bpp/8,
+                                                 x0, // is 0
+                                                 composite,
+                                                 ewidth );
+               rasterizer->scanline += CTX_FULL_AA;
+            }
+          }
+          }
+          else
+          for (int y = y0; y < y1; y++)
+            {
+              if ( (y >= clip_y_min) && (y <= clip_y_max) )
+                {
+                    ctx_rasterizer_apply_coverage (rasterizer,
+                                                 ( (uint8_t *) rasterizer->buf) + (y-rasterizer->blit_y) * 
rasterizer->blit_stride + (int) (x0) * rasterizer->format->bpp/8,
+                                                 x0,
+                                                 &shape->data[shape->width * (int) (y-ymin) + xo],
+                                                 ewidth );
+                }
+               rasterizer->scanline += CTX_FULL_AA;
+            }
+        }
+        if (shape->uses != 0)
+          {
+            ctx_rasterizer_reset (rasterizer);
+          }
+        }
+      }
+    else
+#endif
+    ctx_rasterizer_rasterize_edges (rasterizer, rasterizer->state->gstate.fill_rule
+#if CTX_SHAPE_CACHE
+                                    , NULL
+#endif
+                                   );
+  }
+  if (rasterizer->preserve)
     {
-      break;
+      memcpy (rasterizer->edge_list.entries, temp, sizeof (temp) );
+      rasterizer->edge_list.count = count;
     }
-  else if (u < 0 || v < 0) // default to next sample down and to right
-  {
-      int got_prev_pix = (u >= 0);
-      int got_prev_row = (v>=0);
-      uint32_t *src11 = data  + offset + bwidth + 1;
-      uint32_t *src10 = src11 - got_prev_pix;
-      uint32_t *src01 = src11 - bwidth * got_prev_row;
-      uint32_t *src00 = src10 - bwidth * got_prev_row;
-      s00 = *src00;
-      s01 = *src01;
-      s10 = *src10;
-      s11 = *src11;
-  }
-  else if (loaded + 1 == offset)
-  {
-      int next_row = ( v + 1 < bheight) * bwidth;
-      int next_pix = (u + 1 < bwidth);
-      uint32_t *src00 = data  + offset;
-      s00 = s01;
-      s10 = s11;
-      s01 = ((uint32_t*)src00)[next_pix];
-      s11 = ((uint32_t*)src00)[next_row + next_pix];
-  }
-  else if (loaded != offset)
-  {
-      int next_row = ( v + 1 < bheight) * bwidth;
-      int next_pix = (u + 1 < bwidth);
-      uint32_t *src00 = data  + offset;
-      s00 = ((uint32_t*)src00)[0];
-      s01 = ((uint32_t*)src00)[next_pix];
-      s10 = ((uint32_t*)src00)[next_row];
-      s11 = ((uint32_t*)src00)[next_row + next_pix];
-  }
-    loaded = offset;
-    ((uint32_t*)(&rgba[0]))[0] = ctx_bi_RGBA8 (s00,s01,s10,s11, (xi>>8),(yi>>8)); // the argument type does 
the & 0xff
-    xi += xi_delta;
-    yi += yi_delta;
-    rgba += 4;
+#if CTX_ENABLE_SHADOW_BLUR
+  if (rasterizer->in_shadow)
+  {
+    rasterizer->scan_min -= rasterizer->shadow_y * CTX_FULL_AA;
+    rasterizer->scan_max -= rasterizer->shadow_y * CTX_FULL_AA;
+    rasterizer->col_min  -= (rasterizer->shadow_x - rasterizer->state->gstate.shadow_blur * 3 + 1) * 
CTX_SUBDIV;
+    rasterizer->col_max  -= (rasterizer->shadow_x + rasterizer->state->gstate.shadow_blur * 3 + 1) * 
CTX_SUBDIV;
   }
+#endif
+  rasterizer->preserve = 0;
+}
 
-  }
+#if 0
+static void
+ctx_rasterizer_triangle (CtxRasterizer *rasterizer,
+                         int x0, int y0,
+                         int x1, int y1,
+                         int x2, int y2,
+                         int r0, int g0, int b0, int a0,
+                         int r1, int g1, int b1, int a1,
+                         int r2, int g2, int b2, int a2,
+                         int u0, int v0,
+                         int u1, int v1)
+{
 
-  for (; i < count; i ++)
-  {
-    *((uint32_t*)(rgba))= 0;
-    rgba += 4;
-  }
 }
+#endif
+
 
-#define ctx_clampi(val,min,max) \
-     ctx_mini (ctx_maxi ((val), (min)), (max))
+typedef struct _CtxTermGlyph CtxTermGlyph;
 
-static inline uint32_t ctx_yuv_to_rgba32 (uint8_t y, uint8_t u, uint8_t v)
+struct _CtxTermGlyph
 {
-  int cy  = ((y - 16) * 76309) >> 16;
-  int cr  = (v - 128);
-  int cb  = (u - 128);
-  int red = cy + ((cr * 104597) >> 16);
-  int green = cy - ((cb * 25674 + cr * 53278) >> 16);
-  int blue = cy + ((cb * 132201) >> 16);
-  return  ctx_clampi (red, 0, 255) |
-          (ctx_clampi (green, 0, 255) << 8) |
-          (ctx_clampi (blue, 0, 255) << 16) |
-          (0xff << 24);
-}
+  uint32_t unichar;
+  int      col;
+  int      row;
+  uint8_t  rgba_bg[4];
+  uint8_t  rgba_fg[4];
+};
 
+static int _ctx_glyph (Ctx *ctx, uint32_t unichar, int stroke);
 static void
-ctx_fragment_image_yuv420_RGBA8_nearest (CtxRasterizer *rasterizer,
-                                         float x,
-                                         float y,
-                                         void *out, int count, float dx, float dy)
+ctx_rasterizer_glyph (CtxRasterizer *rasterizer, uint32_t unichar, int stroke)
 {
-  uint8_t *rgba = (uint8_t *) out;
-  CtxSource *g = &rasterizer->state->gstate.source_fill;
-  CtxBuffer *buffer = g->texture.buffer;
-  if (buffer->color_managed)
-    buffer = buffer->color_managed;
-  uint8_t *src = (uint8_t *) buffer->data;
-  int bwidth  = buffer->width;
-  int bheight = buffer->height;
-  int bwidth_div_2  = bwidth/2;
-  int bheight_div_2  = bheight/2;
-  x += 0.5f;
-  y += 0.5f;
+  float tx = rasterizer->state->x;
+  float ty = rasterizer->state->y - rasterizer->state->gstate.font_size;
+  float tx2 = rasterizer->state->x + rasterizer->state->gstate.font_size;
+  float ty2 = rasterizer->state->y + rasterizer->state->gstate.font_size;
+  _ctx_user_to_device (rasterizer->state, &tx, &ty);
+  _ctx_user_to_device (rasterizer->state, &tx2, &ty2);
 
-  {
-    int i = 0;
+  if (tx2 < rasterizer->blit_x || ty2 < rasterizer->blit_y) return;
+  if (tx  > rasterizer->blit_x + rasterizer->blit_width ||
+      ty  > rasterizer->blit_y + rasterizer->blit_height)
+          return;
 
-    for (; i < count; i ++)
-    {
-      int u = x;
-      int v = y;
-      if ((u < 0 || v < 0 || u >= bwidth || v >= bheight))
-      {
-        *((uint32_t*)(rgba))= 0;
-      }
-      else
-      {
-        break;
-      }
-      x += dx;
-      y += dy;
-      rgba += 4;
-    }
+#if CTX_BRAILLE_TEXT
+  float font_size = 0;
+  int ch = 1;
+  int cw = 1;
 
-    uint32_t u_offset = bheight * bwidth;
-    uint32_t v_offset = u_offset + bheight_div_2 * bwidth_div_2;
-    if (rasterizer->swap_red_green)
-    {
-      v_offset = bheight * bwidth;
-      u_offset = v_offset + bheight_div_2 * bwidth_div_2;
-    }
+  if (rasterizer->term_glyphs)
+  {
+    float tx = 0;
+    float ty = rasterizer->state->gstate.font_size;
+    float txb = 0;
+    float tyb = 0;
 
-    int ix = x * 65536;
-    int iy = y * 65536;
+    ch = ctx_term_get_cell_height (rasterizer->ctx);
+    cw = ctx_term_get_cell_width (rasterizer->ctx);
 
-    int ideltax = dx * 65536;
-    int ideltay = dy * 65536;
+    _ctx_user_to_device (rasterizer->state, &tx, &ty);
+    _ctx_user_to_device (rasterizer->state, &txb, &tyb);
+    font_size = ty-tyb;
+  }
+  if (rasterizer->term_glyphs && !stroke &&
+      fabs (font_size - ch) < 0.5)
+  {
+    float tx = rasterizer->x;
+    float ty = rasterizer->y;
+    _ctx_user_to_device (rasterizer->state, &tx, &ty);
+    int col = tx / cw + 1;
+    int row = ty / ch + 1;
+    CtxTermGlyph *glyph = ctx_calloc (sizeof (CtxTermGlyph), 1);
+    ctx_list_append (&rasterizer->glyphs, glyph);
+    glyph->unichar = unichar;
+    glyph->col = col;
+    glyph->row = row;
+    ctx_color_get_rgba8 (rasterizer->state, &rasterizer->state->gstate.source_fill.color,
+                         &glyph->rgba_fg[0]);
+  }
+  else
+#endif
+  _ctx_glyph (rasterizer->ctx, unichar, stroke);
+}
 
-    for (; i < count; i ++)
-    {
-      int u = ix >> 16;
-      int v = iy >> 16;
-      if (u >= 0 && v >= 0 && u < bwidth && v < bheight)
-      {
-        uint32_t y  = v * bwidth + u;
-        uint32_t uv = (v / 2) * bwidth_div_2 + (u / 2);
 
-        *((uint32_t*)(rgba))= ctx_yuv_to_rgba32 (src[y],
-                        //127, 127);
-                        src[u_offset+uv], src[v_offset+uv]);
-        //ctx_RGBA8_associate_alpha_probably_opaque (rgba);
-#if CTX_DITHER
-       ctx_dither_rgba_u8 (rgba, x, y, rasterizer->format->dither_red_blue,
-                           rasterizer->format->dither_green);
-#endif
-      }
-      else
-      {
-        break;
-      }
-      ix += ideltax;
-      iy += ideltay;
-      rgba += 4;
-    }
+static void
+_ctx_text (Ctx        *ctx,
+           const char *string,
+           int         stroke,
+           int         visible);
+static void
+ctx_rasterizer_text (CtxRasterizer *rasterizer, const char *string, int stroke)
+{
+#if CTX_BRAILLE_TEXT
+  float font_size = 0;
+  if (rasterizer->term_glyphs)
+  {
+  float tx = 0;
+  float ty = rasterizer->state->gstate.font_size;
+  _ctx_user_to_device (rasterizer->state, &tx, &ty);
+  font_size = ty;
+  }
+  int   ch = ctx_term_get_cell_height (rasterizer->ctx);
+  int   cw = ctx_term_get_cell_width (rasterizer->ctx);
 
-    for (; i < count; i++)
+  if (rasterizer->term_glyphs && !stroke &&
+      fabs (font_size - ch) < 0.5)
+  {
+    float tx = rasterizer->x;
+    float ty = rasterizer->y;
+    _ctx_user_to_device (rasterizer->state, &tx, &ty);
+    int col = tx / cw + 1;
+    int row = ty / ch + 1;
+    for (int i = 0; string[i]; i++, col++)
     {
-      *((uint32_t*)(rgba))= 0;
-      rgba += 4;
+      CtxTermGlyph *glyph = ctx_calloc (sizeof (CtxTermGlyph), 1);
+      ctx_list_prepend (&rasterizer->glyphs, glyph);
+      glyph->unichar = string[i];
+      glyph->col = col;
+      glyph->row = row;
+      ctx_color_get_rgba8 (rasterizer->state, &rasterizer->state->gstate.source_fill.color,
+                      glyph->rgba_fg);
     }
   }
+  else
+#endif
+  {
+    _ctx_text (rasterizer->ctx, string, stroke, 1);
+  }
 }
 
+void
+_ctx_font (Ctx *ctx, const char *name);
 static void
-ctx_fragment_image_rgba8_RGBA8_box_swap_red_green (CtxRasterizer *rasterizer,
-                                    float x,
-                                    float y,
-                                    void *out, int count, float dx, float dy)
+ctx_rasterizer_set_font (CtxRasterizer *rasterizer, const char *font_name)
 {
-  ctx_fragment_image_rgba8_RGBA8_box (rasterizer, x, y, out, count, dx, dy);
-  ctx_fragment_swap_red_green_u8 (out, count);
+  _ctx_font (rasterizer->ctx, font_name);
 }
 
 static void
-ctx_fragment_image_rgba8_RGBA8_bi_swap_red_green (CtxRasterizer *rasterizer,
-                                    float x,
-                                    float y,
-                                    void *out, int count, float dx, float dy)
+ctx_rasterizer_arc (CtxRasterizer *rasterizer,
+                    float        x,
+                    float        y,
+                    float        radius,
+                    float        start_angle,
+                    float        end_angle,
+                    int          anticlockwise)
 {
-  ctx_fragment_image_rgba8_RGBA8_bi (rasterizer, x, y, out, count, dx, dy);
-  ctx_fragment_swap_red_green_u8 (out, count);
-}
+  int full_segments = CTX_RASTERIZER_MAX_CIRCLE_SEGMENTS;
+  full_segments = radius * CTX_PI * 2;
+  if (full_segments > CTX_RASTERIZER_MAX_CIRCLE_SEGMENTS)
+    { full_segments = CTX_RASTERIZER_MAX_CIRCLE_SEGMENTS; }
+  float step = CTX_PI*2.0/full_segments;
+  int steps;
 
-static void
-ctx_fragment_image_rgba8_RGBA8_nearest_swap_red_green (CtxRasterizer *rasterizer,
-                                    float x,
-                                    float y,
-                                    void *out, int count, float dx, float dy)
-{
-  ctx_fragment_image_rgba8_RGBA8_nearest (rasterizer, x, y, out, count, dx, dy);
-  ctx_fragment_swap_red_green_u8 (out, count);
-}
+  if (end_angle < -30.0)
+    end_angle = -30.0;
+  if (start_angle < -30.0)
+    start_angle = -30.0;
+  if (end_angle > 30.0)
+    end_angle = 30.0;
+  if (start_angle > 30.0)
+    start_angle = 30.0;
 
-static void
-ctx_fragment_image_rgba8_RGBA8 (CtxRasterizer *rasterizer,
-                                float x,
-                                float y,
-                                void *out, int count, float dx, float dy)
-{
-  if (rasterizer->state->gstate.image_smoothing)
-  {
-    float factor = ctx_matrix_get_scale (&rasterizer->state->gstate.transform);
-    if (factor <= 0.50f)
+  if (radius <= 0.0001)
+          return;
+
+  if (end_angle == start_angle)
+          // XXX also detect arcs fully outside render view
     {
-      if (rasterizer->swap_red_green)
-        ctx_fragment_image_rgba8_RGBA8_box_swap_red_green (rasterizer, x, y, out, count, dx, dy);
+    if (rasterizer->has_prev!=0)
+      ctx_rasterizer_line_to (rasterizer, x + ctx_cosf (end_angle) * radius,
+                              y + ctx_sinf (end_angle) * radius);
       else
-        ctx_fragment_image_rgba8_RGBA8_box (rasterizer, x, y, out, count, dx, dy);
+      ctx_rasterizer_move_to (rasterizer, x + ctx_cosf (end_angle) * radius,
+                            y + ctx_sinf (end_angle) * radius);
+      return;
     }
-#if CTX_ALWAYS_USE_NEAREST_FOR_SCALE1
-    else if (factor > 0.99f && factor < 1.01f)
+#if 1
+  if ( (!anticlockwise && fabsf((end_angle - start_angle) - CTX_PI*2) < 0.01f)  ||
+       ( (anticlockwise && fabsf((start_angle - end_angle) - CTX_PI*2) < 0.01f ) ) 
+  ||   (anticlockwise && fabsf((end_angle - start_angle) - CTX_PI*2) < 0.01f)  ||  (!anticlockwise && 
fabsf((start_angle - end_angle) - CTX_PI*2) < 0.01f )  )
     {
-      // XXX: also verify translate == 0 for this fast path to be valid
-      if (rasterizer->swap_red_green)
-        ctx_fragment_image_rgba8_RGBA8_nearest_swap_red_green (rasterizer, x, y, out, count, dx, dy);
-      else
-        ctx_fragment_image_rgba8_RGBA8_nearest (rasterizer, x, y, out, count, dx, dy);
+      start_angle = start_angle;
+      steps = full_segments - 1;
     }
+  else
 #endif
-    else
     {
-      if (rasterizer->swap_red_green)
-        ctx_fragment_image_rgba8_RGBA8_bi_swap_red_green (rasterizer, x, y, out, count, dx, dy);
-      else
-        ctx_fragment_image_rgba8_RGBA8_bi (rasterizer, x, y, out, count, dx, dy);
+      steps = (end_angle - start_angle) / (CTX_PI*2) * full_segments;
+      if (anticlockwise)
+        { steps = full_segments - steps; };
+   // if (steps > full_segments)
+   //   steps = full_segments;
     }
-  }
-  else
-  {
-    if (rasterizer->swap_red_green)
-      ctx_fragment_image_rgba8_RGBA8_nearest_swap_red_green (rasterizer, x, y, out, count, dx, dy);
-    else
-      ctx_fragment_image_rgba8_RGBA8_nearest (rasterizer, x, y, out, count, dx, dy);
-  }
-  //ctx_fragment_swap_red_green_u8 (out, count);
-#if CTX_DITHER
-  uint8_t *rgba = (uint8_t*)out;
-  ctx_dither_rgba_u8 (rgba, x, y, rasterizer->format->dither_red_blue,
-                      rasterizer->format->dither_green);
-#endif
-}
-
-static void
-ctx_fragment_image_gray1_RGBA8 (CtxRasterizer *rasterizer, float x, float y, void *out, int count, float dx, 
float dy)
-{
-  uint8_t *rgba = (uint8_t *) out;
-  CtxSource *g = &rasterizer->state->gstate.source_fill;
-  CtxBuffer *buffer = g->texture.buffer;
-  ctx_assert (rasterizer);
-  ctx_assert (g);
-  ctx_assert (buffer);
-  for (int i = 0; i < count; i ++)
-  {
-  int u = x;
-  int v = y;
-  if ( u < 0 || v < 0 ||
-       u >= buffer->width ||
-       v >= buffer->height)
+  if (anticlockwise) { step = step * -1; }
+  int first = 1;
+  if (steps == 0 /* || steps==full_segments -1  || (anticlockwise && steps == full_segments) */)
     {
-      rgba[0] = rgba[1] = rgba[2] = rgba[3] = 0;
+      float xv = x + ctx_cosf (start_angle) * radius;
+      float yv = y + ctx_sinf (start_angle) * radius;
+      if (!rasterizer->has_prev)
+        { ctx_rasterizer_move_to (rasterizer, xv, yv); }
+      first = 0;
     }
   else
     {
-      uint8_t *src = (uint8_t *) buffer->data;
-      src += v * buffer->stride + u / 8;
-      if (*src & (1<< (u & 7) ) )
-        {
-          rgba[0] = rgba[1] = rgba[2] = rgba[3] = 0;
-        }
-      else
+      for (float angle = start_angle, i = 0; i < steps; angle += step, i++)
         {
-          for (int c = 0; c < 4; c++)
-            { rgba[c] = 255;
-            }//g->texture.rgba[c];
-            //}
+          float xv = x + ctx_cosf (angle) * radius;
+          float yv = y + ctx_sinf (angle) * radius;
+          if (first && !rasterizer->has_prev)
+            { ctx_rasterizer_move_to (rasterizer, xv, yv); }
+          else
+            { ctx_rasterizer_line_to (rasterizer, xv, yv); }
+          first = 0;
         }
     }
-
-    rgba += 4;
-    x += dx;
-    y += dy;
-  }
-}
-
-#if CTX_GRADIENTS
-static void
-ctx_fragment_radial_gradient_RGBA8 (CtxRasterizer *rasterizer, float x, float y, void *out, int count, float 
dx, float dy)
-{
-  uint8_t *rgba = (uint8_t *) out;
-  CtxSource *g = &rasterizer->state->gstate.source_fill;
-  for (int i = 0; i <  count; i ++)
-  {
-    float v = (ctx_hypotf_fast (g->radial_gradient.x0 - x, g->radial_gradient.y0 - y) -
-              g->radial_gradient.r0) * (g->radial_gradient.rdelta);
-#if CTX_GRADIENT_CACHE
-    uint32_t *rgbap = (uint32_t*)&ctx_gradient_cache_u8[ctx_grad_index(v)][0];
-    *((uint32_t*)rgba) = *rgbap;
-#else
-    ctx_fragment_gradient_1d_RGBA8 (rasterizer, v, 0.0, rgba);
-#endif
-#if CTX_DITHER
-    ctx_dither_rgba_u8 (rgba, x, y, rasterizer->format->dither_red_blue,
-                        rasterizer->format->dither_green);
-#endif
-    rgba += 4;
-    x += dx;
-    y += dy;
-  }
-}
-
-static void
-ctx_fragment_linear_gradient_RGBA8 (CtxRasterizer *rasterizer, float x, float y, void *out, int count, float 
dx, float dy)
-{
-#if 0
-  uint8_t *rgba = (uint8_t *) out;
-  CtxSource *g = &rasterizer->state->gstate.source_fill;
-  for (int i = 0; i <  count; i ++)
-  {
-  float v = ( ( (g->linear_gradient.dx * x + g->linear_gradient.dy * y) /
-                g->linear_gradient.length) -
-              g->linear_gradient.start) * (g->linear_gradient.rdelta);
-#if CTX_GRADIENT_CACHE
-  uint32_t*rgbap = ((uint32_t*)(&ctx_gradient_cache_u8[ctx_grad_index(v)][0]));
-  *((uint32_t*)rgba) = *rgbap;
-#else
-  _ctx_fragment_gradient_1d_RGBA8 (rasterizer, v, 1.0, rgba);
-#endif
-#if CTX_DITHER
-  ctx_dither_rgba_u8 (rgba, x, y, rasterizer->format->dither_red_blue,
-                      rasterizer->format->dither_green);
-#endif
-    rgba += 4;
-    x += dx;
-    y += dy;
-  }
-#else
-  uint8_t *rgba = (uint8_t *) out;
-
-  CtxSource *g = &rasterizer->state->gstate.source_fill;
-  float u0 = x; float v0 = y;
-  float ud = dx; float vd = dy;
-  float linear_gradient_rdelta = g->linear_gradient.rdelta;
-  float linear_gradient_length = g->linear_gradient.length;
-  float linear_gradient_length_recip = 1.0f/linear_gradient_length;
-  float linear_gradient_dx = g->linear_gradient.dx *linear_gradient_length_recip * linear_gradient_rdelta;
-  float linear_gradient_dy = g->linear_gradient.dy *linear_gradient_length_recip * linear_gradient_rdelta;
-  float linear_gradient_start = g->linear_gradient.start * linear_gradient_rdelta;
-
-#if CTX_DITHER
-  int dither_red_blue = rasterizer->format->dither_red_blue;
-  int dither_green = rasterizer->format->dither_green;
-#endif
-
-  u0 *= linear_gradient_dx;
-  v0 *= linear_gradient_dy;
-  ud *= linear_gradient_dx;
-  vd *= linear_gradient_dy;
-
-#if CTX_GRADIENT_CACHE
-  int vv = ((u0 + v0) - linear_gradient_start) * (CTX_GRADIENT_CACHE_ELEMENTS-1) * 256;
-  int ud_plus_vd = (ud + vd) * (CTX_GRADIENT_CACHE_ELEMENTS-1) * 256;
-#else
-  float vv = ((u0 + v0) - linear_gradient_start);
-  float ud_plus_vd = (ud + vd);
-#endif
-
-  for (int x = 0; x < count ; x++)
-  {
-#if CTX_GRADIENT_CACHE
-  uint32_t*rgbap = ((uint32_t*)(&ctx_gradient_cache_u8[ctx_grad_index_i (vv)][0]));
-  *((uint32_t*)rgba) = *rgbap;
-#else
-  _ctx_fragment_gradient_1d_RGBA8 (rasterizer, vv, 1.0, rgba);
-#endif
-#if CTX_DITHER
-      ctx_dither_rgba_u8 (rgba, u0, v0, dither_red_blue, dither_green);
-#endif
-    rgba+= 4;
-    vv += ud_plus_vd;
-  }
-#endif
-}
-
-#endif
-
-static void
-ctx_fragment_color_RGBA8 (CtxRasterizer *rasterizer, float x, float y, void *out, int count, float dx, float 
dy)
-{
-  uint8_t *rgba_out = (uint8_t *) out;
-  CtxSource *g = &rasterizer->state->gstate.source_fill;
-  _ctx_color_get_rgba8 (rasterizer->state, &g->color, rgba_out);
-  ctx_RGBA8_associate_alpha (rgba_out);
-  if (rasterizer->swap_red_green)
-  {
-    int tmp = rgba_out[0];
-    rgba_out[0] = rgba_out[2];
-    rgba_out[2] = tmp;
-  }
-  for (int i = 1; i < count; i++, rgba_out+=4)
-    memcpy (rgba_out + count * 4, rgba_out, 4);
+  ctx_rasterizer_line_to (rasterizer, x + ctx_cosf (end_angle) * radius,
+                          y + ctx_sinf (end_angle) * radius);
 }
-#if CTX_ENABLE_FLOAT
 
-#if CTX_GRADIENTS
 static void
-ctx_fragment_linear_gradient_RGBAF (CtxRasterizer *rasterizer, float x, float y, void *out, int count, float 
dx, float dy)
+ctx_rasterizer_quad_to (CtxRasterizer *rasterizer,
+                        float        cx,
+                        float        cy,
+                        float        x,
+                        float        y)
 {
-  float *rgba = (float *) out;
-  CtxSource *g = &rasterizer->state->gstate.source_fill;
-  for (int i = 0; i < count; i++)
-  {
-    float v = ( ( (g->linear_gradient.dx * x + g->linear_gradient.dy * y) /
-                  g->linear_gradient.length) -
-                g->linear_gradient.start) * (g->linear_gradient.rdelta);
-    ctx_fragment_gradient_1d_RGBAF (rasterizer, v, 1.0f, rgba);
-    x += dx;
-    y += dy;
-    rgba += 4;
-  }
+  /* XXX : it is probably cheaper/faster to do quad interpolation directly -
+   *       though it will increase the code-size, an
+   *       alternative is to turn everything into cubic
+   *       and deal with cubics more directly during
+   *       rasterization
+   */
+  ctx_rasterizer_curve_to (rasterizer,
+                           (cx * 2 + rasterizer->x) / 3.0f, (cy * 2 + rasterizer->y) / 3.0f,
+                           (cx * 2 + x) / 3.0f,           (cy * 2 + y) / 3.0f,
+                           x,                              y);
 }
 
 static void
-ctx_fragment_radial_gradient_RGBAF (CtxRasterizer *rasterizer, float x, float y, void *out, int count, float 
dx, float dy)
+ctx_rasterizer_rel_quad_to (CtxRasterizer *rasterizer,
+                            float cx, float cy,
+                            float x,  float y)
 {
-  float *rgba = (float *) out;
-  CtxSource *g = &rasterizer->state->gstate.source_fill;
-  for (int i = 0; i < count; i++)
-  {
-  float v = ctx_hypotf (g->radial_gradient.x0 - x, g->radial_gradient.y0 - y);
-        v = (v - g->radial_gradient.r0) * (g->radial_gradient.rdelta);
-  ctx_fragment_gradient_1d_RGBAF (rasterizer, v, 0.0f, rgba);
-    x+=dx;
-    y+=dy;
-    rgba +=4;
-  }
+  ctx_rasterizer_quad_to (rasterizer, cx + rasterizer->x, cy + rasterizer->y,
+                          x  + rasterizer->x, y  + rasterizer->y);
 }
-#endif
-
 
+#define LENGTH_OVERSAMPLE 1
 static void
-ctx_fragment_color_RGBAF (CtxRasterizer *rasterizer, float x, float y, void *out, int count, float dx, float 
dy)
-{
-  float *rgba = (float *) out;
-  for (int i = 0; i < count; i++)
-  {
-    CtxSource *g = &rasterizer->state->gstate.source_fill;
-    ctx_color_get_rgba (rasterizer->state, &g->color, rgba);
-    rgba += 4;
-  }
-}
-
-static void ctx_fragment_image_RGBAF (CtxRasterizer *rasterizer, float x, float y, void *out, int count, 
float dx, float dy)
+ctx_rasterizer_pset (CtxRasterizer *rasterizer, int x, int y, uint8_t cov)
 {
-  float *outf = (float *) out;
-  uint8_t rgba[4];
-  CtxGState *gstate = &rasterizer->state->gstate;
-  CtxBuffer *buffer = gstate->source_fill.texture.buffer;
-  switch (buffer->format->bpp)
+  // XXX - we avoid rendering here x==0 - to keep with
+  //  an off-by one elsewhere
+  //
+  //  XXX onlt works in rgba8 formats
+  if (x <= 0 || y < 0 || x >= rasterizer->blit_width ||
+      y >= rasterizer->blit_height)
+    { return; }
+  uint8_t fg_color[4];
+  ctx_color_get_rgba8 (rasterizer->state, &rasterizer->state->gstate.source_fill.color, fg_color);
+  uint8_t pixel[4];
+  uint8_t *dst = ( (uint8_t *) rasterizer->buf);
+  dst += y * rasterizer->blit_stride;
+  dst += x * rasterizer->format->bpp / 8;
+  if (!rasterizer->format->to_comp ||
+      !rasterizer->format->from_comp)
+    { return; }
+  if (cov == 255)
     {
-      case 1:  ctx_fragment_image_gray1_RGBA8 (rasterizer, x, y, rgba, count, dx, dy); break;
-      case 24: ctx_fragment_image_rgb8_RGBA8 (rasterizer, x, y, rgba, count, dx, dy);  break;
-      case 32: ctx_fragment_image_rgba8_RGBA8 (rasterizer, x, y, rgba, count, dx, dy); break;
-      default: ctx_fragment_image_RGBA8 (rasterizer, x, y, rgba, count, dx, dy);       break;
+      for (int c = 0; c < 4; c++)
+        {
+          pixel[c] = fg_color[c];
+        }
     }
-  for (int c = 0; c < 4 * count; c ++) { outf[c] = ctx_u8_to_float (rgba[c]); }
-}
-
-static CtxFragment ctx_rasterizer_get_fragment_RGBAF (CtxRasterizer *rasterizer)
-{
-  CtxGState *gstate = &rasterizer->state->gstate;
-  switch (gstate->source_fill.type)
+  else
     {
-      case CTX_SOURCE_TEXTURE:         return ctx_fragment_image_RGBAF;
-      case CTX_SOURCE_COLOR:           return ctx_fragment_color_RGBAF;
-#if CTX_GRADIENTS
-      case CTX_SOURCE_LINEAR_GRADIENT: return ctx_fragment_linear_gradient_RGBAF;
-      case CTX_SOURCE_RADIAL_GRADIENT: return ctx_fragment_radial_gradient_RGBAF;
-#endif
+      rasterizer->format->to_comp (rasterizer, x, dst, &pixel[0], 1);
+      for (int c = 0; c < 4; c++)
+        {
+          pixel[c] = ctx_lerp_u8 (pixel[c], fg_color[c], cov);
+        }
     }
-  return ctx_fragment_color_RGBAF;
+  rasterizer->format->from_comp (rasterizer, x, &pixel[0], dst, 1);
 }
-#endif
 
-static CtxFragment ctx_rasterizer_get_fragment_RGBA8 (CtxRasterizer *rasterizer)
+static void
+ctx_rasterizer_stroke_1px (CtxRasterizer *rasterizer)
 {
-  CtxGState *gstate = &rasterizer->state->gstate;
-  CtxBuffer *buffer = gstate->source_fill.texture.buffer;
-  switch (gstate->source_fill.type)
-    {
-      case CTX_SOURCE_TEXTURE:
-        if (!buffer || !buffer->format)
-          return ctx_fragment_color_RGBA8;
+  int count = rasterizer->edge_list.count;
+  CtxEntry *temp = rasterizer->edge_list.entries;
+  float prev_x = 0.0f;
+  float prev_y = 0.0f;
+  int aa = 15;//rasterizer->aa;
+  int start = 0;
+  int end = 0;
+#if 0
+  float factor = ctx_matrix_get_scale (&state->gstate.transform);
+#endif
 
-        if (buffer->format->pixel_format == CTX_FORMAT_YUV420)
+  while (start < count)
+    {
+      int started = 0;
+      int i;
+      for (i = start; i < count; i++)
         {
-          return ctx_fragment_image_yuv420_RGBA8_nearest;
-        }
-        else
-        switch (buffer->format->bpp)
-          {
-            case 1:  return ctx_fragment_image_gray1_RGBA8;
-            case 24: 
-              {
-                if (gstate->image_smoothing)
-                {
-                  float factor = ctx_matrix_get_scale (&gstate->transform);
-                          //fprintf (stderr, "{%.3f}", factor);
-                  if (factor < 0.5f)
-                  {
-                    if (rasterizer->swap_red_green)
-                      return ctx_fragment_image_rgb8_RGBA8_box_swap_red_green;
-                    return ctx_fragment_image_rgb8_RGBA8_box;
-                  }
-#if CTX_ALWAYS_USE_NEAREST_FOR_SCALE1
-                  else if (factor > 0.99f && factor < 1.01f)
-                  {
-                    if (rasterizer->swap_red_green)
-                      return ctx_fragment_image_rgb8_RGBA8_nearest_swap_red_green;
-                    return ctx_fragment_image_rgb8_RGBA8_nearest;
-                  }
-#endif
-                  else
-                  {
-                    if (rasterizer->swap_red_green)
-                      return ctx_fragment_image_rgb8_RGBA8_bi_swap_red_green;
-                    return ctx_fragment_image_rgb8_RGBA8_bi;
-                  }
-                }
-                else
-                {
-                  if (rasterizer->swap_red_green)
-                    return ctx_fragment_image_rgb8_RGBA8_nearest_swap_red_green;
-                  return ctx_fragment_image_rgb8_RGBA8_nearest;
-                }
-              }
-              break;
-            case 32:
-              {
-                if (gstate->image_smoothing)
+          CtxEntry *entry = &temp[i];
+          float x, y;
+          if (entry->code == CTX_NEW_EDGE)
+            {
+              if (started)
                 {
-                  float factor = ctx_matrix_get_scale (&gstate->transform);
-                          //fprintf (stderr, "[%.3f]", factor);
-                  if (factor < 0.5f)
-                  {
-                    if (rasterizer->swap_red_green)
-                      return ctx_fragment_image_rgba8_RGBA8_box_swap_red_green;
-                    return ctx_fragment_image_rgba8_RGBA8_box;
-                  }
-#if CTX_ALWAYS_USE_NEAREST_FOR_SCALE1
-                  else if (factor > 0.99f && factor < 1.01f)
-                  {
-                    if (rasterizer->swap_red_green)
-                      return ctx_fragment_image_rgba8_RGBA8_nearest_swap_red_green;
-                    return ctx_fragment_image_rgba8_RGBA8_nearest;
-                  }
-#endif
-                  else
-                  {
-                    if (rasterizer->swap_red_green)
-                      return ctx_fragment_image_rgba8_RGBA8_bi_swap_red_green;
-                    return ctx_fragment_image_rgba8_RGBA8_bi;
-                  }
+                  end = i - 1;
+                  goto foo;
                 }
-                else
+              prev_x = entry->data.s16[0] * 1.0f / CTX_SUBDIV;
+              prev_y = entry->data.s16[1] * 1.0f / aa;
+              started = 1;
+              start = i;
+            }
+          x = entry->data.s16[2] * 1.0f / CTX_SUBDIV;
+          y = entry->data.s16[3] * 1.0f / aa;
+          int dx = x - prev_x;
+          int dy = y - prev_y;
+          int length = ctx_maxf (abs (dx), abs (dy) );
+          if (length)
+            {
+              length *= LENGTH_OVERSAMPLE;
+              int len = length;
+              int tx = prev_x * 256;
+              int ty = prev_y * 256;
+              dx *= 256;
+              dy *= 256;
+              dx /= length;
+              dy /= length;
+              for (int i = 0; i < len; i++)
                 {
-                  if (rasterizer->swap_red_green)
-                    return ctx_fragment_image_rgba8_RGBA8_nearest_swap_red_green;
-                  return ctx_fragment_image_rgba8_RGBA8_nearest;
+                  ctx_rasterizer_pset (rasterizer, tx/256, ty/256, 255);
+                  tx += dx;
+                  ty += dy;
+                  ctx_rasterizer_pset (rasterizer, tx/256, ty/256, 255);
                 }
-              }
-            default: return ctx_fragment_image_RGBA8;
-          }
-      case CTX_SOURCE_COLOR:           return ctx_fragment_color_RGBA8;
-#if CTX_GRADIENTS
-      case CTX_SOURCE_LINEAR_GRADIENT: return ctx_fragment_linear_gradient_RGBA8;
-      case CTX_SOURCE_RADIAL_GRADIENT: return ctx_fragment_radial_gradient_RGBA8;
-#endif
+            }
+          prev_x = x;
+          prev_y = y;
+        }
+      end = i-1;
+foo:
+      start = end+1;
     }
-  return ctx_fragment_color_RGBA8;
+  ctx_rasterizer_reset (rasterizer);
 }
 
 static void
-ctx_init_uv (CtxRasterizer *rasterizer,
-             int x0, int count,
-             float *u0, float *v0, float *ud, float *vd)
+ctx_rasterizer_stroke (CtxRasterizer *rasterizer)
 {
-  CtxGState *gstate = &rasterizer->state->gstate;
-  *u0 = x0;
-  *v0 = rasterizer->scanline / 15;//rasterizer->aa;
-  float u1 = *u0 + count;
-  float v1 = *v0;
-
-  _ctx_matrix_apply_transform (&gstate->source_fill.transform, u0, v0);
-  _ctx_matrix_apply_transform (&gstate->source_fill.transform, &u1, &v1);
-
-  *ud = (u1-*u0) / (count);
-  *vd = (v1-*v0) / (count);
-}
-
+  CtxSource source_backup = rasterizer->state->gstate.source_fill;
+  if (rasterizer->state->gstate.source_stroke.type != CTX_SOURCE_INHERIT_FILL)
+    rasterizer->state->gstate.source_fill = rasterizer->state->gstate.source_stroke;
+  CtxState *state = rasterizer->state;
+  int count = rasterizer->edge_list.count;
+  int preserved = rasterizer->preserve;
+  float factor = ctx_matrix_get_scale (&state->gstate.transform);
 
-static void
-ctx_u8_copy_normal (int components, CTX_COMPOSITE_ARGUMENTS)
-{
-  if (CTX_UNLIKELY(rasterizer->fragment))
+  int aa = 15;//rasterizer->aa;
+  CtxEntry temp[count]; /* copy of already built up path's poly line  */
+  memcpy (temp, rasterizer->edge_list.entries, sizeof (temp) );
+#if 1
+  if (rasterizer->state->gstate.line_width * factor <= 0.0f &&
+      rasterizer->state->gstate.line_width * factor > -10.0f)
     {
-      float u0 = 0; float v0 = 0;
-      float ud = 0; float vd = 0;
-      ctx_init_uv (rasterizer, x0, count, &u0, &v0, &ud, &vd);
-      while (count--)
-      {
-        uint8_t cov = *coverage;
-        if (CTX_UNLIKELY(cov == 0))
+      ctx_rasterizer_stroke_1px (rasterizer);
+    }
+  else
+#endif
+    {
+      factor *= 0.86; /* this hack adjustment makes sharp 1px and 2px strokewidths
+                            end up sharp without erronious AA
+                       */
+      ctx_rasterizer_reset (rasterizer); /* then start afresh with our stroked shape  */
+      CtxMatrix transform_backup = rasterizer->state->gstate.transform;
+      ctx_matrix_identity (&rasterizer->state->gstate.transform);
+      float prev_x = 0.0f;
+      float prev_y = 0.0f;
+      float half_width_x = rasterizer->state->gstate.line_width * factor/2;
+      float half_width_y = rasterizer->state->gstate.line_width * factor/2;
+      if (rasterizer->state->gstate.line_width <= 0.0f)
         {
-          u0+=ud;
-          v0+=vd;
+          half_width_x = .5;
+          half_width_y = .5;
         }
-        else
+      int start = 0;
+      int end   = 0;
+      while (start < count)
         {
-          rasterizer->fragment (rasterizer, u0, v0, src, 1, ud, vd);
-          u0+=ud;
-          v0+=vd;
-          if (cov == 255)
-          {
-            for (int c = 0; c < components; c++)
-              dst[c] = src[c];
-          }
-          else
-          {
-            uint8_t rcov = 255 - cov;
-            for (int c = 0; c < components; c++)
-              { dst[c] = (src[c]*cov + dst[c]*rcov)/255; }
-          }
-        }
-        dst += components;
-        coverage ++;
-      }
-      return;
-    }
+          int started = 0;
+          int i;
+          for (i = start; i < count; i++)
+            {
+              CtxEntry *entry = &temp[i];
+              float x, y;
+              if (entry->code == CTX_NEW_EDGE)
+                {
+                  if (started)
+                    {
+                      end = i - 1;
+                      goto foo;
+                    }
+                  prev_x = entry->data.s16[0] * 1.0f / CTX_SUBDIV;
+                  prev_y = entry->data.s16[1] * 1.0f / aa;
+                  started = 1;
+                  start = i;
+                }
+              x = entry->data.s16[2] * 1.0f / CTX_SUBDIV;
+              y = entry->data.s16[3] * 1.0f / aa;
+              float dx = x - prev_x;
+              float dy = y - prev_y;
+              float length = ctx_fast_hypotf (dx, dy);
+              if (length>0.001f)
+                {
+                  dx = dx/length * half_width_x;
+                  dy = dy/length * half_width_y;
+                  if (entry->code == CTX_NEW_EDGE)
+                    {
+                      ctx_rasterizer_finish_shape (rasterizer);
+                      ctx_rasterizer_move_to (rasterizer, prev_x+dy, prev_y-dx);
+                    }
+                  ctx_rasterizer_line_to (rasterizer, prev_x-dy, prev_y+dx);
+                  
+                  // we need to know the slope of the other side
 
-  while (count--)
-  {
-    uint8_t cov = *coverage;
-    uint8_t rcov = 255-cov;
-    for (int c = 0; c < components; c++)
-      { dst[c] = (src[c]*cov+dst[c]*rcov)/255; }
-    dst += components;
-    coverage ++;
-  }
-}
+                  // XXX possible miter line-to
+                  //ctx_rasterizer_line_to (rasterizer, prev_x-dy+4, prev_y+dx+10);
+                  //ctx_rasterizer_line_to (rasterizer, prev_x-dy+8, prev_y+dx+0);
 
-static void
-ctx_u8_clear_normal (int components, CTX_COMPOSITE_ARGUMENTS)
-{
-  while (count--)
-  {
-    uint8_t cov = *coverage;
-    for (int c = 0; c < components; c++)
-      { dst[c] = (dst[c] * (256-cov)) >> 8; }
-    coverage ++;
-    dst += components;
-  }
-}
 
-typedef enum {
-  CTX_PORTER_DUFF_0,
-  CTX_PORTER_DUFF_1,
-  CTX_PORTER_DUFF_ALPHA,
-  CTX_PORTER_DUFF_1_MINUS_ALPHA,
-} CtxPorterDuffFactor;
-
-#define  \
-ctx_porter_duff_factors(mode, foo, bar)\
-{\
-  switch (mode)\
-  {\
-     case CTX_COMPOSITE_SOURCE_ATOP:\
-        f_s = CTX_PORTER_DUFF_ALPHA;\
-        f_d = CTX_PORTER_DUFF_1_MINUS_ALPHA;\
-      break;\
-     case CTX_COMPOSITE_DESTINATION_ATOP:\
-        f_s = CTX_PORTER_DUFF_1_MINUS_ALPHA;\
-        f_d = CTX_PORTER_DUFF_ALPHA;\
-      break;\
-     case CTX_COMPOSITE_DESTINATION_IN:\
-        f_s = CTX_PORTER_DUFF_0;\
-        f_d = CTX_PORTER_DUFF_ALPHA;\
-      break;\
-     case CTX_COMPOSITE_DESTINATION:\
-        f_s = CTX_PORTER_DUFF_0;\
-        f_d = CTX_PORTER_DUFF_1;\
-       break;\
-     case CTX_COMPOSITE_SOURCE_OVER:\
-        f_s = CTX_PORTER_DUFF_1;\
-        f_d = CTX_PORTER_DUFF_1_MINUS_ALPHA;\
-       break;\
-     case CTX_COMPOSITE_DESTINATION_OVER:\
-        f_s = CTX_PORTER_DUFF_1_MINUS_ALPHA;\
-        f_d = CTX_PORTER_DUFF_1;\
-       break;\
-     case CTX_COMPOSITE_XOR:\
-        f_s = CTX_PORTER_DUFF_1_MINUS_ALPHA;\
-        f_d = CTX_PORTER_DUFF_1_MINUS_ALPHA;\
-       break;\
-     case CTX_COMPOSITE_DESTINATION_OUT:\
-        f_s = CTX_PORTER_DUFF_0;\
-        f_d = CTX_PORTER_DUFF_1_MINUS_ALPHA;\
-       break;\
-     case CTX_COMPOSITE_SOURCE_OUT:\
-        f_s = CTX_PORTER_DUFF_1_MINUS_ALPHA;\
-        f_d = CTX_PORTER_DUFF_0;\
-       break;\
-     case CTX_COMPOSITE_SOURCE_IN:\
-        f_s = CTX_PORTER_DUFF_ALPHA;\
-        f_d = CTX_PORTER_DUFF_0;\
-       break;\
-     case CTX_COMPOSITE_COPY:\
-        f_s = CTX_PORTER_DUFF_1;\
-        f_d = CTX_PORTER_DUFF_1_MINUS_ALPHA;\
-       break;\
-     default:\
-     case CTX_COMPOSITE_CLEAR:\
-        f_s = CTX_PORTER_DUFF_0;\
-        f_d = CTX_PORTER_DUFF_0;\
-       break;\
-  }\
+                  ctx_rasterizer_line_to (rasterizer, x-dy, y+dx);
+                }
+              prev_x = x;
+              prev_y = y;
+            }
+          end = i-1;
+foo:
+          for (int i = end; i >= start; i--)
+            {
+              CtxEntry *entry = &temp[i];
+              float x, y, dx, dy;
+              x = entry->data.s16[2] * 1.0f / CTX_SUBDIV;
+              y = entry->data.s16[3] * 1.0f / aa;
+              dx = x - prev_x;
+              dy = y - prev_y;
+              float length = ctx_fast_hypotf (dx, dy);
+              dx = dx/length * half_width_x;
+              dy = dy/length * half_width_y;
+              if (length>0.001f)
+                {
+                  ctx_rasterizer_line_to (rasterizer, prev_x-dy, prev_y+dx);
+                  // XXX possible miter line-to
+             //   ctx_rasterizer_line_to (rasterizer, prev_x-dy+10, prev_y+dx+10);
+                  ctx_rasterizer_line_to (rasterizer, x-dy,      y+dx);
+                }
+              prev_x = x;
+              prev_y = y;
+              if (entry->code == CTX_NEW_EDGE)
+                {
+                  x = entry->data.s16[0] * 1.0f / CTX_SUBDIV;
+                  y = entry->data.s16[1] * 1.0f / aa;
+                  dx = x - prev_x;
+                  dy = y - prev_y;
+                  length = ctx_fast_hypotf (dx, dy);
+                  if (length>0.001f)
+                    {
+                      dx = dx / length * half_width_x;
+                      dy = dy / length * half_width_y;
+                      ctx_rasterizer_line_to (rasterizer, prev_x-dy, prev_y+dx);
+                      ctx_rasterizer_line_to (rasterizer, x-dy, y+dx);
+                    }
+                }
+              if ( (prev_x != x) && (prev_y != y) )
+                {
+                  prev_x = x;
+                  prev_y = y;
+                }
+            }
+          start = end+1;
+        }
+      ctx_rasterizer_finish_shape (rasterizer);
+      switch (rasterizer->state->gstate.line_cap)
+        {
+          case CTX_CAP_SQUARE: // XXX: incorrect - if rectangles were in
+                               //                  reverse order - rotation would be off
+                               //                  better implement correct here
+            {
+              float x = 0, y = 0;
+              int has_prev = 0;
+              for (int i = 0; i < count; i++)
+                {
+                  CtxEntry *entry = &temp[i];
+                  if (entry->code == CTX_NEW_EDGE)
+                    {
+                      if (has_prev)
+                        {
+                          ctx_rasterizer_rectangle (rasterizer, x - half_width_x, y - half_width_y, 
half_width_x, half_width_y);
+                          ctx_rasterizer_finish_shape (rasterizer);
+                        }
+                      x = entry->data.s16[0] * 1.0f / CTX_SUBDIV;
+                      y = entry->data.s16[1] * 1.0f / aa;
+                      ctx_rasterizer_rectangle (rasterizer, x - half_width_x, y - half_width_y, half_width_x 
* 2, half_width_y * 2);
+                      ctx_rasterizer_finish_shape (rasterizer);
+                    }
+                  x = entry->data.s16[2] * 1.0f / CTX_SUBDIV;
+                  y = entry->data.s16[3] * 1.0f / aa;
+                  has_prev = 1;
+                }
+              ctx_rasterizer_rectangle (rasterizer, x - half_width_x, y - half_width_y, half_width_x * 2, 
half_width_y * 2);
+              ctx_rasterizer_finish_shape (rasterizer);
+            }
+            break;
+          case CTX_CAP_NONE: /* nothing to do */
+            break;
+          case CTX_CAP_ROUND:
+            {
+              float x = 0, y = 0;
+              int has_prev = 0;
+              for (int i = 0; i < count; i++)
+                {
+                  CtxEntry *entry = &temp[i];
+                  if (entry->code == CTX_NEW_EDGE)
+                    {
+                      if (has_prev)
+                        {
+                          ctx_rasterizer_arc (rasterizer, x, y, half_width_x, CTX_PI*3, 0, 1);
+                          ctx_rasterizer_finish_shape (rasterizer);
+                        }
+                      x = entry->data.s16[0] * 1.0f / CTX_SUBDIV;
+                      y = entry->data.s16[1] * 1.0f / aa;
+                      ctx_rasterizer_arc (rasterizer, x, y, half_width_x, CTX_PI*3, 0, 1);
+                      ctx_rasterizer_finish_shape (rasterizer);
+                    }
+                  x = entry->data.s16[2] * 1.0f / CTX_SUBDIV;
+                  y = entry->data.s16[3] * 1.0f / aa;
+                  has_prev = 1;
+                }
+              ctx_rasterizer_move_to (rasterizer, x, y);
+              ctx_rasterizer_arc (rasterizer, x, y, half_width_x, CTX_PI*3, 0, 1);
+              ctx_rasterizer_finish_shape (rasterizer);
+              break;
+            }
+        }
+      switch (rasterizer->state->gstate.line_join)
+        {
+          case CTX_JOIN_BEVEL:
+          case CTX_JOIN_MITER:
+            break;
+          case CTX_JOIN_ROUND:
+            {
+              float x = 0, y = 0;
+              for (int i = 0; i < count-1; i++)
+                {
+                  CtxEntry *entry = &temp[i];
+                  x = entry->data.s16[2] * 1.0f / CTX_SUBDIV;
+                  y = entry->data.s16[3] * 1.0f / aa;
+                  if (entry[1].code == CTX_EDGE)
+                    {
+                      ctx_rasterizer_arc (rasterizer, x, y, half_width_x, CTX_PI*2, 0, 1);
+                      ctx_rasterizer_finish_shape (rasterizer);
+                    }
+                }
+              break;
+            }
+        }
+      CtxFillRule rule_backup = rasterizer->state->gstate.fill_rule;
+      rasterizer->state->gstate.fill_rule = CTX_FILL_RULE_WINDING;
+      rasterizer->preserve = 0; // so fill isn't tripped
+      ctx_rasterizer_fill (rasterizer);
+      rasterizer->state->gstate.fill_rule = rule_backup;
+      //rasterizer->state->gstate.source = source_backup;
+      rasterizer->state->gstate.transform = transform_backup;
+    }
+  if (preserved)
+    {
+      memcpy (rasterizer->edge_list.entries, temp, sizeof (temp) );
+      rasterizer->edge_list.count = count;
+      rasterizer->preserve = 0;
+    }
+  rasterizer->state->gstate.source_fill = source_backup;
 }
 
-static void
-ctx_u8_source_over_normal_color (int components,
-                                 CtxRasterizer         *rasterizer,
-                                 uint8_t * __restrict__ dst,
-                                 uint8_t * __restrict__ src,
-                                 int                    x0,
-                                 uint8_t * __restrict__ coverage,
-                                 int                    count)
-{
-  uint8_t tsrc[5];
-  *((uint32_t*)tsrc) = *((uint32_t*)src);
+#if CTX_1BIT_CLIP
+#define CTX_CLIP_FORMAT CTX_FORMAT_GRAY1
+#else
+#define CTX_CLIP_FORMAT CTX_FORMAT_GRAY8
+#endif
 
-  while (count--)
-  {
-    for (int c = 0; c < components; c++)
-      //dst[c] =  ((tsrc[c] * *coverage)>>8) + (dst[c] * (((65536)-(tsrc[components-1] * *coverage)))>>16);
-      dst[c] =  ((((tsrc[c] * *coverage)) + (dst[c] * (((255)-(((255+(tsrc[components-1] * 
*coverage))>>8))))))>>8);
-    coverage ++;
-    dst+=components;
-  }
-}
 
 static void
-ctx_u8_source_copy_normal_color (int components, CTX_COMPOSITE_ARGUMENTS)
-{
-  while (count--)
-  {
-    for (int c = 0; c < components; c++)
-      dst[c] =  ctx_lerp_u8(dst[c],src[c],coverage[0]);
-    coverage ++;
-    dst+=components;
-  }
-}
-
-static inline void
-ctx_RGBA8_source_over_normal_buf (CTX_COMPOSITE_ARGUMENTS, uint8_t *tsrc)
+ctx_rasterizer_clip_reset (CtxRasterizer *rasterizer)
 {
-  while (count--)
-  {
-     uint32_t si_ga = ((*((uint32_t*)tsrc)) & 0xff00ff00) >> 8;
-     uint32_t si_rb = (*((uint32_t*)tsrc)) & 0x00ff00ff;
-//     uint32_t di_ga = ((*((uint32_t*)dst)) & 0xff00ff00) >> 8;
-//     uint32_t di_rb = (*((uint32_t*)dst)) & 0x00ff00ff;
-     uint32_t si_a  = si_ga >> 16;
-     uint32_t cov = *coverage;
-     uint32_t racov = (255-((255+si_a*cov)>>8));
-     *((uint32_t*)(dst)) =
-
-     (((si_rb*cov+0xff00ff+(((*((uint32_t*)(dst)))&0x00ff00ff)*racov))>>8)&0x00ff00ff)|
-     ((si_ga*cov+0xff00ff+((((*((uint32_t*)(dst)))&0xff00ff00)>>8)*racov))&0xff00ff00);
-
-     coverage ++;
-     tsrc += 4;
-     dst  += 4;
-  }
-}
+#if CTX_ENABLE_CLIP
+  if (rasterizer->clip_buffer)
+   ctx_buffer_free (rasterizer->clip_buffer);
+  rasterizer->clip_buffer = NULL;
+#endif
+  rasterizer->state->gstate.clip_min_x = rasterizer->blit_x;
+  rasterizer->state->gstate.clip_min_y = rasterizer->blit_y;
 
-static inline void
-ctx_RGBA8_source_over_normal_full_cov_buf (CTX_COMPOSITE_ARGUMENTS, uint8_t *tsrc)
-{
-  while (count--)
-  {
-     uint32_t si_ga = ((*((uint32_t*)tsrc)) & 0xff00ff00) >> 8;
-     uint32_t si_rb = (*((uint32_t*)tsrc)) & 0x00ff00ff;
-     uint32_t si_a  = si_ga >> 16;
-     uint32_t racov = (255-si_a);
-     *((uint32_t*)(dst)) =
-     (((si_rb*255+0xff00ff+(((*((uint32_t*)(dst)))&0x00ff00ff)*racov))>>8)&0x00ff00ff)|
-     ((si_ga*255+0xff00ff+((((*((uint32_t*)(dst)))&0xff00ff00)>>8)*racov))&0xff00ff00);
-     tsrc += 4;
-     dst  += 4;
-  }
+  rasterizer->state->gstate.clip_max_x = rasterizer->blit_x + rasterizer->blit_width - 1;
+  rasterizer->state->gstate.clip_max_y = rasterizer->blit_y + rasterizer->blit_height - 1;
 }
 
 static void
-ctx_RGBA8_source_copy_normal_buf (CTX_COMPOSITE_ARGUMENTS, uint8_t *tsrc)
+ctx_rasterizer_clip_apply (CtxRasterizer *rasterizer,
+                           CtxEntry      *edges)
 {
-  while (count--)
-  {
-    ((uint32_t*)dst)[0]=ctx_lerp_RGBA8 (((uint32_t*)dst)[0],
-                                        ((uint32_t*)tsrc)[0], coverage[0]);
-    coverage ++;
-    tsrc += 4;
-    dst  += 4;
-  }
-}
+  int count = edges[0].data.u32[0];
 
-static void
-ctx_RGBA8_source_over_normal_fragment (CTX_COMPOSITE_ARGUMENTS)
-{
-  float u0 = 0; float v0 = 0;
-  float ud = 0; float vd = 0;
-  ctx_init_uv (rasterizer, x0, count, &u0, &v0, &ud, &vd);
-  uint8_t _tsrc[4 * (count)];
-  rasterizer->fragment (rasterizer, u0, v0, &_tsrc[0], count, ud, vd);
-  ctx_RGBA8_source_over_normal_buf (rasterizer,
-                       dst, src, x0, coverage, count, &_tsrc[0]);
-}
+  int minx = 5000;
+  int miny = 5000;
+  int maxx = -5000;
+  int maxy = -5000;
+  int prev_x = 0;
+  int prev_y = 0;
+  int blit_width = rasterizer->blit_width;
+  int blit_height = rasterizer->blit_height;
 
-static inline void
-ctx_RGBA8_source_over_normal_full_cov_fragment (CTX_COMPOSITE_ARGUMENTS)
-{
-  float u0 = 0; float v0 = 0;
-  float ud = 0; float vd = 0;
-  ctx_init_uv (rasterizer, x0, count, &u0, &v0, &ud, &vd);
-  uint8_t _tsrc[4 * (count)];
-  rasterizer->fragment (rasterizer, u0, v0, &_tsrc[0], count, ud, vd);
-  ctx_RGBA8_source_over_normal_full_cov_buf (rasterizer,
-                       dst, src, x0, coverage, count, &_tsrc[0]);
-}
+  int aa = 15;//rasterizer->aa;
+  float coords[6][2];
 
-static void
-ctx_RGBA8_source_copy_normal_fragment (CTX_COMPOSITE_ARGUMENTS)
-{
-  float u0 = 0; float v0 = 0;
-  float ud = 0; float vd = 0;
-  ctx_init_uv (rasterizer, x0, count, &u0, &v0, &ud, &vd);
-  uint8_t _tsrc[4 * (count)];
-  rasterizer->fragment (rasterizer, u0, v0, &_tsrc[0], count, ud, vd);
-  ctx_RGBA8_source_copy_normal_buf (rasterizer,
-                       dst, src, x0, coverage, count, &_tsrc[0]);
-}
+  for (int i = 0; i < count; i++)
+    {
+      CtxEntry *entry = &edges[i+1];
+      float x, y;
+      if (entry->code == CTX_NEW_EDGE)
+        {
+          prev_x = entry->data.s16[0] * 1.0f / CTX_SUBDIV;
+          prev_y = entry->data.s16[1] * 1.0f / aa;
+          if (prev_x < minx) { minx = prev_x; }
+          if (prev_y < miny) { miny = prev_y; }
+          if (prev_x > maxx) { maxx = prev_x; }
+          if (prev_y > maxy) { maxy = prev_y; }
+        }
+      x = entry->data.s16[2] * 1.0f / CTX_SUBDIV;
+      y = entry->data.s16[3] * 1.0f / aa;
+      if (x < minx) { minx = x; }
+      if (y < miny) { miny = y; }
+      if (x > maxx) { maxx = x; }
+      if (y > maxy) { maxy = y; }
 
+      if (i < 6)
+      {
+        coords[i][0] = x;
+        coords[i][1] = y;
+      }
+    }
 
-static void
-ctx_RGBA8_source_over_normal_color (CTX_COMPOSITE_ARGUMENTS)
-{
-#if CTX_REFERENCE
-  ctx_u8_source_over_normal_color (4, rasterizer, dst, src, x0, coverage, count);
-#else
-  uint32_t si_ga = ((uint32_t*)rasterizer->color)[1];
-  uint32_t si_rb = ((uint32_t*)rasterizer->color)[2];
-  uint32_t si_a  = si_ga >> 16;
+#if CTX_ENABLE_CLIP
 
-  while (count--)
+  if ((rasterizer->clip_rectangle==1
+                          || !rasterizer->clip_buffer)
+                  )
+  //  XXX  disabled, it makes clip test fail, a lot of unneded work
+  //  can be skipped here.
   {
-     uint32_t cov   = *coverage++;
-     uint32_t rcov  = (((255+si_a * cov)>>8))^255;
-     uint32_t di    = *((uint32_t*)dst);
-     uint32_t di_ga = ((di & 0xff00ff00) >> 8);
-     uint32_t di_rb = (di & 0x00ff00ff);
-     *((uint32_t*)(dst)) =
-     (((si_rb * cov + 0xff00ff + di_rb * rcov) & 0xff00ff00) >> 8)  |
-      ((si_ga * cov + 0xff00ff + di_ga * rcov) & 0xff00ff00);
-     dst+=4;
-  }
+    if (count == 6)
+    {
+      if (coords[3][0] == coords[5][0] &&
+          coords[3][1] == coords[5][1])
+      {
+#if 0
+        printf ("%d,%d %dx%d\n", minx, miny,
+                                       maxx-minx+1, maxy-miny+1);
 #endif
-}
-
-static void
-ctx_RGBA8_source_copy_normal_color (CTX_COMPOSITE_ARGUMENTS)
-{
-#if CTX_REFERENCE
-  ctx_u8_source_copy_normal_color (4, rasterizer, dst, src, x0, coverage, count);
-#else
-  uint32_t si_ga = ((uint32_t*)rasterizer->color)[1];
-  uint32_t si_rb = ((uint32_t*)rasterizer->color)[2];
 
-  while (count--)
-  {
-     uint32_t cov   = *coverage++;
-     uint32_t di    = *((uint32_t*)dst);
-     uint32_t di_ga = (di & 0xff00ff00);
-     uint32_t di_rb = (di & 0x00ff00ff);
+         rasterizer->state->gstate.clip_min_x =
+            ctx_maxi (minx, rasterizer->state->gstate.clip_min_x);
+         rasterizer->state->gstate.clip_min_y =
+            ctx_maxi (miny, rasterizer->state->gstate.clip_min_y);
+         rasterizer->state->gstate.clip_max_x =
+            ctx_mini (maxx, rasterizer->state->gstate.clip_max_x);
+         rasterizer->state->gstate.clip_max_y =
+            ctx_mini (maxy, rasterizer->state->gstate.clip_max_y);
 
-     uint32_t d_rb  = si_rb - di_rb;
-     uint32_t d_ga  = si_ga - (di_ga>>8);
+         rasterizer->clip_rectangle = 1;
 
-     *((uint32_t*)(dst)) =
+#if 0
+         if (!rasterizer->clip_buffer)
+           rasterizer->clip_buffer = ctx_buffer_new (blit_width,
+                                                     blit_height,
+                                                     CTX_CLIP_FORMAT);
 
-     (((di_rb + ((d_rb * cov)>>8)) & 0x00ff00ff))  |
-      ((di_ga + ((d_ga * cov)      & 0xff00ff00)));
-     dst +=4;
-  }
+         memset (rasterizer->clip_buffer->data, 0, blit_width * blit_height);
+         int i = 0;
+         for (int y = rasterizer->state->gstate.clip_min_y;
+                  y <= rasterizer->state->gstate.clip_max_y;
+                  y++)
+         for (int x = rasterizer->state->gstate.clip_min_x;
+                  x <= rasterizer->state->gstate.clip_max_x;
+                  x++, i++)
+         {
+           ((uint8_t*)(rasterizer->clip_buffer->data))[i] = 255;
+         }
 #endif
-}
 
-static void
-ctx_RGBA8_clear_normal (CTX_COMPOSITE_ARGUMENTS)
-{
-  ctx_u8_clear_normal (4, rasterizer, dst, src, x0, coverage, count);
-}
+         return;
+      }
+    }
+  }
+  rasterizer->clip_rectangle = 0;
 
-static void
-ctx_u8_blend_normal (int components, uint8_t * __restrict__ dst, uint8_t *src, uint8_t *blended, int count)
-{
-  for (int j = 0; j < count; j++)
-  {
-  switch (components)
+  if ((minx == maxx) || (miny == maxy)) // XXX : reset hack
   {
-     case 3:
-       ((uint8_t*)(blended))[2] = ((uint8_t*)(src))[2];
-       *((uint16_t*)(blended)) = *((uint16_t*)(src));
-       break;
-     case 2:
-       *((uint16_t*)(blended)) = *((uint16_t*)(src));
-       break;
-     case 5:
-       *((uint32_t*)(blended)) = *((uint32_t*)(src));
-       ((uint8_t*)(blended))[4] = ((uint8_t*)(src))[4];
-       break;
-     case 4:
-       *((uint32_t*)(blended)) = *((uint32_t*)(src));
-       break;
-     default:
-       {
-        for (int i = 0; i<components;i++)
-           blended[i] = src[i];
-       }
-       break;
-  }
-    blended+=components;
-    src+=components;
+    ctx_rasterizer_clip_reset (rasterizer);
+    return;//goto done;
   }
-}
-
-/* branchless 8bit add that maxes out at 255 */
-static inline uint8_t ctx_sadd8(uint8_t a, uint8_t b)
-{
-  uint16_t s = (uint16_t)a+b;
-  return -(s>>8) | (uint8_t)s;
-}
-
-#if CTX_BLENDING_AND_COMPOSITING
-
-#define ctx_u8_blend_define(name, CODE) \
-static void \
-ctx_u8_blend_##name (int components, uint8_t * __restrict__ dst, uint8_t *src, uint8_t *blended, int count)\
-{\
-  for (int j = 0; j < count; j++) { \
-  uint8_t *s=src; uint8_t b[components];\
-  ctx_u8_deassociate_alpha (components, dst, b);\
-    CODE;\
-  blended[components-1] = src[components-1];\
-  ctx_u8_associate_alpha (components, blended);\
-  src += components;\
-  dst += components;\
-  blended += components;\
-  }\
-}
 
-#define ctx_u8_blend_define_seperable(name, CODE) \
-        ctx_u8_blend_define(name, for (int c = 0; c < components-1; c++) { CODE ;}) \
+  int we_made_it = 0;
+  CtxBuffer *clip_buffer;
 
-ctx_u8_blend_define_seperable(multiply,     blended[c] = (b[c] * s[c])/255;)
-ctx_u8_blend_define_seperable(screen,       blended[c] = s[c] + b[c] - (s[c] * b[c])/255;)
-ctx_u8_blend_define_seperable(overlay,      blended[c] = b[c] < 127 ? (s[c] * b[c])/255 :
-                                                         s[c] + b[c] - (s[c] * b[c])/255;)
-ctx_u8_blend_define_seperable(darken,       blended[c] = ctx_mini (b[c], s[c]))
-ctx_u8_blend_define_seperable(lighten,      blended[c] = ctx_maxi (b[c], s[c]))
-ctx_u8_blend_define_seperable(color_dodge,  blended[c] = b[c] == 0 ? 0 :
-                                     s[c] == 255 ? 255 : ctx_mini(255, (255 * b[c]) / (255-s[c])))
-ctx_u8_blend_define_seperable(color_burn,   blended[c] = b[c] == 1 ? 1 :
-                                     s[c] == 0 ? 0 : 255 - ctx_mini(255, (255*(255 - b[c])) / s[c]))
-ctx_u8_blend_define_seperable(hard_light,   blended[c] = s[c] < 127 ? (b[c] * s[c])/255 :
-                                                          b[c] + s[c] - (b[c] * s[c])/255;)
-ctx_u8_blend_define_seperable(difference,   blended[c] = (b[c] - s[c]))
-ctx_u8_blend_define_seperable(divide,       blended[c] = s[c]?(255 * b[c]) / s[c]:0)
-ctx_u8_blend_define_seperable(addition,     blended[c] = ctx_sadd8 (s[c], b[c]))
-ctx_u8_blend_define_seperable(subtract,     blended[c] = ctx_maxi(0, s[c]-b[c]))
-ctx_u8_blend_define_seperable(exclusion,    blended[c] = b[c] + s[c] - 2 * (b[c] * s[c]/255))
-ctx_u8_blend_define_seperable(soft_light,
-  if (s[c] <= 255/2)
+  if (!rasterizer->clip_buffer)
   {
-    blended[c] = b[c] - (255 - 2 * s[c]) * b[c] * (255 - b[c]) / (255 * 255);
+    rasterizer->clip_buffer = ctx_buffer_new (blit_width,
+                                              blit_height,
+                                              CTX_CLIP_FORMAT);
+    clip_buffer = rasterizer->clip_buffer;
+    we_made_it = 1;
+    if (CTX_CLIP_FORMAT == CTX_FORMAT_GRAY1)
+    memset (rasterizer->clip_buffer->data, 0, blit_width * blit_height/8);
+    else
+    memset (rasterizer->clip_buffer->data, 0, blit_width * blit_height);
   }
   else
   {
-    int d;
-    if (b[c] <= 255/4)
-      d = (((16 * b[c] - 12 * 255)/255 * b[c] + 4 * 255) * b[c])/255;
-    else
-      d = ctx_sqrtf(b[c]/255.0) * 255.4;
-    blended[c] = (b[c] + (2 * s[c] - 255) * (d - b[c]))/255;
+    clip_buffer = ctx_buffer_new (blit_width, blit_height,
+                                  CTX_CLIP_FORMAT);
   }
-)
 
-static int ctx_int_get_max (int components, int *c)
-{
-  int max = 0;
-  for (int i = 0; i < components - 1; i ++)
   {
-    if (c[i] > max) max = c[i];
+
+  int prev_x = 0;
+  int prev_y = 0;
+
+    Ctx *ctx = ctx_new_for_framebuffer (clip_buffer->data, blit_width, blit_height,
+       blit_width,
+       CTX_CLIP_FORMAT);
+
+  for (int i = 0; i < count; i++)
+    {
+      CtxEntry *entry = &edges[i+1];
+      float x, y;
+      if (entry->code == CTX_NEW_EDGE)
+        {
+          prev_x = entry->data.s16[0] * 1.0f / CTX_SUBDIV;
+          prev_y = entry->data.s16[1] * 1.0f / aa;
+          ctx_move_to (ctx, prev_x, prev_y);
+        }
+      x = entry->data.s16[2] * 1.0f / CTX_SUBDIV;
+      y = entry->data.s16[3] * 1.0f / aa;
+      ctx_line_to (ctx, x, y);
+    }
+    ctx_gray (ctx, 1.0f);
+    ctx_fill (ctx);
+    ctx_free (ctx);
   }
-  return max;
-}
 
-static int ctx_int_get_min (int components, int *c)
-{
-  int min = 400;
-  for (int i = 0; i < components - 1; i ++)
+  int maybe_rect = 1;
+  rasterizer->clip_rectangle = 0;
+
+  if (CTX_CLIP_FORMAT == CTX_FORMAT_GRAY1)
   {
-    if (c[i] < min) min = c[i];
+    int count = blit_width * blit_height / 8;
+    for (int i = 0; i < count; i++)
+    {
+      ((uint8_t*)rasterizer->clip_buffer->data)[i] =
+      (((uint8_t*)rasterizer->clip_buffer->data)[i] &
+      ((uint8_t*)clip_buffer->data)[i]);
+    }
   }
-  return min;
-}
-
-static int ctx_int_get_lum (int components, int *c)
-{
-  switch (components)
+  else
   {
-    case 3:
-    case 4:
-            return CTX_CSS_RGB_TO_LUMINANCE(c);
-    case 1:
-    case 2:
-            return c[0];
-            break;
-    default:
-       {
-         int sum = 0;
-         for (int i = 0; i < components - 1; i ++)
-         {
-           sum += c[i];
-         }
-         return sum / (components - 1);
-       }
-            break;
-  }
-}
+    int count = blit_width * blit_height;
 
-static int ctx_u8_get_lum (int components, uint8_t *c)
-{
-  switch (components)
-  {
-    case 3:
-    case 4:
-            return CTX_CSS_RGB_TO_LUMINANCE(c);
-    case 1:
-    case 2:
-            return c[0];
-            break;
-    default:
-       {
-         int sum = 0;
-         for (int i = 0; i < components - 1; i ++)
-         {
-           sum += c[i];
-         }
-         return sum / (components - 1);
-       }
-            break;
-  }
-}
-static int ctx_u8_get_sat (int components, uint8_t *c)
-{
-  switch (components)
-  {
-    case 3:
-    case 4:
-            { int r = c[0];
-              int g = c[1];
-              int b = c[2];
-              return ctx_maxi(r, ctx_maxi(g,b)) - ctx_mini(r,ctx_mini(g,b));
-            }
-            break;
-    case 1:
-    case 2:
-            return 0.0;
-            break;
-    default:
-       {
-         int min = 1000;
-         int max = -1000;
-         for (int i = 0; i < components - 1; i ++)
-         {
-           if (c[i] < min) min = c[i];
-           if (c[i] > max) max = c[i];
-         }
-         return max-min;
-       }
-       break;
-  }
-}
 
-static void ctx_u8_set_lum (int components, uint8_t *c, uint8_t lum)
-{
-  int d = lum - ctx_u8_get_lum (components, c);
-  int tc[components];
-  for (int i = 0; i < components - 1; i++)
-  {
-    tc[i] = c[i] + d;
-  }
+    int i;
+    int x0 = 0;
+    int y0 = 0;
+    int width = -1;
+    int next_stage = 0;
+    uint8_t *p_data = (uint8_t*)rasterizer->clip_buffer->data;
+    uint8_t *data = (uint8_t*)clip_buffer->data;
 
-  int l = ctx_int_get_lum (components, tc);
-  int n = ctx_int_get_min (components, tc);
-  int x = ctx_int_get_max (components, tc);
+    i=0;
+    /* find upper left */
+    for (; i < count && maybe_rect && !next_stage; i++)
+    {
+      uint8_t val = (p_data[i] * data[i])/255;
+      data[i] = val;
+      switch (val)
+      {
+        case 255:
+          x0 = i % blit_width;
+          y0 = i / blit_width;
+          next_stage = 1;
+          break;
+        case 0: break;
+        default:
+          maybe_rect = 0;
+          break;
+      }
+    }
 
-  if (n < 0 && l!=n)
-  {
-    for (int i = 0; i < components - 1; i++)
-      tc[i] = l + (((tc[i] - l) * l) / (l-n));
-  }
+    next_stage = 0;
+    /* figure out with */
+    for (; i < count && !next_stage && maybe_rect; i++)
+    {
+      int x = i % blit_width;
+      int y = i / blit_width;
+      uint8_t val = (p_data[i] * data[i])/255;
+      data[i] = val;
 
-  if (x > 255 && x!=l)
-  {
-    for (int i = 0; i < components - 1; i++)
-      tc[i] = l + (((tc[i] - l) * (255 - l)) / (x-l));
-  }
-  for (int i = 0; i < components - 1; i++)
-    c[i] = tc[i];
-}
+      if (y == y0)
+      {
+        switch (val)
+        {
+          case 255:
+            width = x - x0 + 1;
+            break;
+          case 0:
+            next_stage = 1;
+            break;
+          default:
+            maybe_rect = 0;
+            break;
+        }
+        if (x % blit_width == blit_width - 1) next_stage = 1;
+      }
+      else next_stage = 1;
+    }
 
-static void ctx_u8_set_sat (int components, uint8_t *c, uint8_t sat)
-{
-  int max = 0, mid = 1, min = 2;
-  
-  if (c[min] > c[mid]){int t = min; min = mid; mid = t;}
-  if (c[mid] > c[max]){int t = mid; mid = max; max = t;}
-  if (c[min] > c[mid]){int t = min; min = mid; mid = t;}
+    next_stage = 0;
+    /* body */
+    for (; i < count && maybe_rect && !next_stage; i++)
+    {
+      int x = i % blit_width;
+      uint8_t val = (p_data[i] * data[i])/255;
+      data[i] = val;
 
-  if (c[max] > c[min])
-  {
-    c[mid] = ((c[mid]-c[min]) * sat) / (c[max] - c[min]);
-    c[max] = sat;
-  }
-  else
-  {
-    c[mid] = c[max] = 0;
-  }
-  c[min] = 0;
-}
+      if (x < x0)
+      {
+        if (val != 0){ maybe_rect = 0; next_stage = 1; }
+      } else if (x < x0 + width)
+      {
+        if (val != 255){ if (val != 0) maybe_rect = 0; next_stage = 1; }
+      } else {
+        if (val != 0){ maybe_rect = 0; next_stage = 1; }
+      }
+    }
 
-ctx_u8_blend_define(color,
-  for (int i = 0; i < components; i++)
-    blended[i] = s[i];
-  ctx_u8_set_lum(components, blended, ctx_u8_get_lum (components, s));
-)
+    next_stage = 0;
+    /* foot */
+    for (; i < count && maybe_rect && !next_stage; i++)
+    {
+      uint8_t val = (p_data[i] * data[i])/255;
+      data[i] = val;
 
-ctx_u8_blend_define(hue,
-  int in_sat = ctx_u8_get_sat(components, b);
-  int in_lum = ctx_u8_get_lum(components, b);
-  for (int i = 0; i < components; i++)
-    blended[i] = s[i];
-  ctx_u8_set_sat(components, blended, in_sat);
-  ctx_u8_set_lum(components, blended, in_lum);
-)
+      if (val != 0){ maybe_rect = 0; next_stage = 1; }
+    }
 
-ctx_u8_blend_define(saturation,
-  int in_sat = ctx_u8_get_sat(components, s);
-  int in_lum = ctx_u8_get_lum(components, b);
-  for (int i = 0; i < components; i++)
-    blended[i] = b[i];
-  ctx_u8_set_sat(components, blended, in_sat);
-  ctx_u8_set_lum(components, blended, in_lum);
-)
 
-ctx_u8_blend_define(luminosity,
-  int in_lum = ctx_u8_get_lum(components, s);
-  for (int i = 0; i < components; i++)
-    blended[i] = b[i];
-  ctx_u8_set_lum(components, blended, in_lum);
-)
-#endif
+    for (; i < count; i++)
+    {
+      uint8_t val = (p_data[i] * data[i])/255;
+      data[i] = val;
+    }
 
-CTX_INLINE static void
-ctx_u8_blend (int components, CtxBlend blend, uint8_t * __restrict__ dst, uint8_t *src, uint8_t *blended, 
int count)
-{
-#if CTX_BLENDING_AND_COMPOSITING
-  switch (blend)
-  {
-    case CTX_BLEND_NORMAL:      ctx_u8_blend_normal      (components, dst, src, blended, count); break;
-    case CTX_BLEND_MULTIPLY:    ctx_u8_blend_multiply    (components, dst, src, blended, count); break;
-    case CTX_BLEND_SCREEN:      ctx_u8_blend_screen      (components, dst, src, blended, count); break;
-    case CTX_BLEND_OVERLAY:     ctx_u8_blend_overlay     (components, dst, src, blended, count); break;
-    case CTX_BLEND_DARKEN:      ctx_u8_blend_darken      (components, dst, src, blended, count); break;
-    case CTX_BLEND_LIGHTEN:     ctx_u8_blend_lighten     (components, dst, src, blended, count); break;
-    case CTX_BLEND_COLOR_DODGE: ctx_u8_blend_color_dodge (components, dst, src, blended, count); break;
-    case CTX_BLEND_COLOR_BURN:  ctx_u8_blend_color_burn  (components, dst, src, blended, count); break;
-    case CTX_BLEND_HARD_LIGHT:  ctx_u8_blend_hard_light  (components, dst, src, blended, count); break;
-    case CTX_BLEND_SOFT_LIGHT:  ctx_u8_blend_soft_light  (components, dst, src, blended, count); break;
-    case CTX_BLEND_DIFFERENCE:  ctx_u8_blend_difference  (components, dst, src, blended, count); break;
-    case CTX_BLEND_EXCLUSION:   ctx_u8_blend_exclusion   (components, dst, src, blended, count); break;
-    case CTX_BLEND_COLOR:       ctx_u8_blend_color       (components, dst, src, blended, count); break;
-    case CTX_BLEND_HUE:         ctx_u8_blend_hue         (components, dst, src, blended, count); break;
-    case CTX_BLEND_SATURATION:  ctx_u8_blend_saturation  (components, dst, src, blended, count); break;
-    case CTX_BLEND_LUMINOSITY:  ctx_u8_blend_luminosity  (components, dst, src, blended, count); break;
-    case CTX_BLEND_ADDITION:    ctx_u8_blend_addition    (components, dst, src, blended, count); break;
-    case CTX_BLEND_DIVIDE:      ctx_u8_blend_divide      (components, dst, src, blended, count); break;
-    case CTX_BLEND_SUBTRACT:    ctx_u8_blend_subtract    (components, dst, src, blended, count); break;
+    if (maybe_rect)
+       rasterizer->clip_rectangle = 1;
   }
+  if (!we_made_it)
+   ctx_buffer_free (clip_buffer);
 #else
-  switch (blend)
-  {
-    default:                    ctx_u8_blend_normal      (components, dst, src, blended, count); break;
-  }
-
+  if (coords[0][0]){};
 #endif
+  
+  rasterizer->state->gstate.clip_min_x = ctx_maxi (minx,
+                                         rasterizer->state->gstate.clip_min_x);
+  rasterizer->state->gstate.clip_min_y = ctx_maxi (miny,
+                                         rasterizer->state->gstate.clip_min_y);
+  rasterizer->state->gstate.clip_max_x = ctx_mini (maxx,
+                                         rasterizer->state->gstate.clip_max_x);
+  rasterizer->state->gstate.clip_max_y = ctx_mini (maxy,
+                                         rasterizer->state->gstate.clip_max_y);
 }
 
-CTX_INLINE static void
-__ctx_u8_porter_duff (CtxRasterizer         *rasterizer,
-                     int                    components,
-                     uint8_t *              dst,
-                     uint8_t *              src,
-                     int                    x0,
-                     uint8_t * __restrict__ coverage,
-                     int                    count,
-                     CtxCompositingMode     compositing_mode,
-                     CtxFragment            fragment,
-                     CtxBlend               blend)
+static void
+ctx_rasterizer_clip (CtxRasterizer *rasterizer)
 {
-  CtxPorterDuffFactor f_s, f_d;
-  ctx_porter_duff_factors (compositing_mode, &f_s, &f_d);
-  CtxGState *gstate = &rasterizer->state->gstate;
-  uint8_t global_alpha_u8 = gstate->global_alpha_u8;
-  uint8_t tsrc[components * count];
-  int src_step = 0;
-
-  if (gstate->source_fill.type == CTX_SOURCE_COLOR)
-  {
-    src = &tsrc[0];
-    fragment (rasterizer, 0, 0, src, 1, 0, 0);
-    if (blend != CTX_BLEND_NORMAL)
-      ctx_u8_blend (components, blend, dst, src, src, 1);
-  }
-  else
-  {
-    float u0 = 0; float v0 = 0;
-    float ud = 0; float vd = 0;
-    src = &tsrc[0];
-
-    ctx_init_uv (rasterizer, x0, count, &u0, &v0, &ud, &vd);
-    fragment (rasterizer, u0, v0, src, count, ud, vd);
-    if (blend != CTX_BLEND_NORMAL)
-      ctx_u8_blend (components, blend, dst, src, src, count);
-    src_step = components;
-  }
-
-  while (count--)
-  {
-    uint32_t cov = *coverage;
-
-    if (CTX_UNLIKELY(global_alpha_u8 != 255))
-      cov = (cov * global_alpha_u8 + 255) >> 8;
-
-    uint8_t csrc[components];
-    for (int c = 0; c < components; c++)
-      csrc[c] = (src[c] * cov + 255) >> 8;
-
-    for (int c = 0; c < components; c++)
+  int count = rasterizer->edge_list.count;
+  CtxEntry temp[count+1]; /* copy of already built up path's poly line  */
+  rasterizer->state->has_clipped=1;
+  rasterizer->state->gstate.clipped=1;
+  //if (rasterizer->preserve)
+    { memcpy (temp + 1, rasterizer->edge_list.entries, sizeof (temp) - sizeof (temp[0]));
+      temp[0].code = CTX_NOP;
+      temp[0].data.u32[0] = count;
+      ctx_state_set_blob (rasterizer->state, CTX_clip, (uint8_t*)temp, sizeof(temp));
+    }
+  ctx_rasterizer_clip_apply (rasterizer, temp);
+  ctx_rasterizer_reset (rasterizer);
+  if (rasterizer->preserve)
     {
-      uint32_t res = 0;
-#if 1
-      switch (f_s)
-      {
-        case CTX_PORTER_DUFF_0:             break;
-        case CTX_PORTER_DUFF_1:             res += (csrc[c] ); break;
-        case CTX_PORTER_DUFF_ALPHA:         res += (csrc[c] * dst[components-1] + 255) >> 8; break;
-        case CTX_PORTER_DUFF_1_MINUS_ALPHA: res += (csrc[c] * (256-dst[components-1])) >> 8; break;
-      }
-      switch (f_d)
-      {
-        case CTX_PORTER_DUFF_0: break;
-        case CTX_PORTER_DUFF_1:             res += dst[c]; break;
-        case CTX_PORTER_DUFF_ALPHA:         res += (dst[c] * csrc[components-1] + 255) >> 8; break;
-        case CTX_PORTER_DUFF_1_MINUS_ALPHA: res += (dst[c] * (256-csrc[components-1])) >> 8; break;
-      }
-#else
-      switch (f_s)
-      {
-        case CTX_PORTER_DUFF_0:             break;
-        case CTX_PORTER_DUFF_1:             res += (csrc[c] ); break;
-        case CTX_PORTER_DUFF_ALPHA:         res += (csrc[c] * dst[components-1])/255; break;
-        case CTX_PORTER_DUFF_1_MINUS_ALPHA: res += (csrc[c] * (255-dst[components-1]))/255; break;
-      }
-      switch (f_d)
-      {
-        case CTX_PORTER_DUFF_0: break;
-        case CTX_PORTER_DUFF_1:             res += dst[c]; break;
-        case CTX_PORTER_DUFF_ALPHA:         res += (dst[c] * csrc[components-1])/255; break;
-        case CTX_PORTER_DUFF_1_MINUS_ALPHA: res += (dst[c] * (255-csrc[components-1]))/255; break;
-      }
-#endif
-      dst[c] = res;
+      memcpy (rasterizer->edge_list.entries, temp + 1, sizeof (temp) - sizeof(temp[0]));
+      rasterizer->edge_list.count = count;
+      rasterizer->preserve = 0;
     }
-    coverage ++;
-    src+=src_step;
-    dst+=components;
-  }
 }
 
-CTX_INLINE static void
-_ctx_u8_porter_duff (CtxRasterizer         *rasterizer,
-                     int                    components,
-                     uint8_t *              dst,
-                     uint8_t * __restrict__ src,
-                     int                    x0,
-                     uint8_t *              coverage,
-                     int                    count,
-                     CtxCompositingMode     compositing_mode,
-                     CtxFragment            fragment,
-                     CtxBlend               blend)
+
+#if 0
+static void
+ctx_rasterizer_load_image (CtxRasterizer *rasterizer,
+                           const char  *path,
+                           float x,
+                           float y)
 {
-  __ctx_u8_porter_duff (rasterizer, components, dst, src, x0, coverage, count, compositing_mode, fragment, 
blend);
+  // decode PNG, put it in image is slot 1,
+  // magic width height stride format data
+  ctx_buffer_load_png (&rasterizer->ctx->texture[0], path);
+  ctx_rasterizer_set_texture (rasterizer, 0, x, y);
 }
+#endif
 
-#define _ctx_u8_porter_duffs(comp_format, components, source, fragment, blend) \
-   switch (rasterizer->state->gstate.compositing_mode) \
-   { \
-     case CTX_COMPOSITE_SOURCE_ATOP: \
-      _ctx_u8_porter_duff (rasterizer, components, dst, src, x0, coverage, count, \
-        CTX_COMPOSITE_SOURCE_ATOP, fragment, blend);\
-      break;\
-     case CTX_COMPOSITE_DESTINATION_ATOP:\
-      _ctx_u8_porter_duff (rasterizer, components, dst, src, x0, coverage, count,\
-        CTX_COMPOSITE_DESTINATION_ATOP, fragment, blend);\
-      break;\
-     case CTX_COMPOSITE_DESTINATION_IN:\
-      _ctx_u8_porter_duff (rasterizer, components, dst, src, x0, coverage, count,\
-        CTX_COMPOSITE_DESTINATION_IN, fragment, blend);\
-      break;\
-     case CTX_COMPOSITE_DESTINATION:\
-      _ctx_u8_porter_duff (rasterizer, components, dst, src, x0, coverage, count,\
-        CTX_COMPOSITE_DESTINATION, fragment, blend);\
-       break;\
-     case CTX_COMPOSITE_SOURCE_OVER:\
-      _ctx_u8_porter_duff (rasterizer, components, dst, src, x0, coverage, count,\
-        CTX_COMPOSITE_SOURCE_OVER, fragment, blend);\
-       break;\
-     case CTX_COMPOSITE_DESTINATION_OVER:\
-      _ctx_u8_porter_duff (rasterizer, components, dst, src, x0, coverage, count,\
-        CTX_COMPOSITE_DESTINATION_OVER, fragment, blend);\
-       break;\
-     case CTX_COMPOSITE_XOR:\
-      _ctx_u8_porter_duff (rasterizer, components, dst, src, x0, coverage, count,\
-        CTX_COMPOSITE_XOR, fragment, blend);\
-       break;\
-     case CTX_COMPOSITE_DESTINATION_OUT:\
-       _ctx_u8_porter_duff (rasterizer, components, dst, src, x0, coverage, count,\
-        CTX_COMPOSITE_DESTINATION_OUT, fragment, blend);\
-       break;\
-     case CTX_COMPOSITE_SOURCE_OUT:\
-       _ctx_u8_porter_duff (rasterizer, components, dst, src, x0, coverage, count,\
-        CTX_COMPOSITE_SOURCE_OUT, fragment, blend);\
-       break;\
-     case CTX_COMPOSITE_SOURCE_IN:\
-       _ctx_u8_porter_duff (rasterizer, components, dst, src, x0, coverage, count,\
-        CTX_COMPOSITE_SOURCE_IN, fragment, blend);\
-       break;\
-     case CTX_COMPOSITE_COPY:\
-       _ctx_u8_porter_duff (rasterizer, components, dst, src, x0, coverage, count,\
-        CTX_COMPOSITE_COPY, fragment, blend);\
-       break;\
-     case CTX_COMPOSITE_CLEAR:\
-       _ctx_u8_porter_duff (rasterizer, components, dst, src, x0, coverage, count,\
-        CTX_COMPOSITE_CLEAR, fragment, blend);\
-       break;\
-   }
 
-/* generating one function per compositing_mode would be slightly more efficient,
- * but on embedded targets leads to slightly more code bloat,
- * here we trade off a slight amount of performance
- */
-#define ctx_u8_porter_duff(comp_format, components, source, fragment, blend) \
-static void \
-ctx_##comp_format##_porter_duff_##source (CTX_COMPOSITE_ARGUMENTS) \
-{ \
-  _ctx_u8_porter_duffs(comp_format, components, source, fragment, blend);\
+CTX_INLINE void
+ctx_rasterizer_rectangle (CtxRasterizer *rasterizer,
+                          float x,
+                          float y,
+                          float width,
+                          float height)
+{
+  ctx_rasterizer_move_to (rasterizer, x, y);
+  ctx_rasterizer_rel_line_to (rasterizer, width, 0);
+  ctx_rasterizer_rel_line_to (rasterizer, 0, height);
+  ctx_rasterizer_rel_line_to (rasterizer, -width, 0);
+  ctx_rasterizer_rel_line_to (rasterizer, 0, -height);
+  ctx_rasterizer_rel_line_to (rasterizer, width/2, 0);
+  ctx_rasterizer_finish_shape (rasterizer);
 }
 
-ctx_u8_porter_duff(RGBA8, 4,generic, rasterizer->fragment, rasterizer->state->gstate.blend_mode)
-//ctx_u8_porter_duff(comp_name, components,color_##blend_name,  NULL, blend_mode)
-
 static void
-ctx_RGBA8_nop (CTX_COMPOSITE_ARGUMENTS)
+ctx_rasterizer_set_pixel (CtxRasterizer *rasterizer,
+                          uint16_t x,
+                          uint16_t y,
+                          uint8_t r,
+                          uint8_t g,
+                          uint8_t b,
+                          uint8_t a)
 {
+  rasterizer->state->gstate.source_fill.type = CTX_SOURCE_COLOR;
+  ctx_color_set_RGBA8 (rasterizer->state, &rasterizer->state->gstate.source_fill.color, r, g, b, a);
+#if 0
+  // XXX : doesn't take transforms into account - and has
+  // received less testing than code paths part of protocol,
+  // using rectangle properly will trigger the fillrect fastpath
+  ctx_rasterizer_pset (rasterizer, x, y, 255);
+#else
+  ctx_rasterizer_rectangle (rasterizer, x, y, 1.0, 1.0);
+  ctx_rasterizer_fill (rasterizer);
+#endif
 }
 
+#if CTX_ENABLE_SHADOW_BLUR
+static float
+ctx_gaussian (float x, float mu, float sigma)
+{
+  float a = ( x- mu) / sigma;
+  return ctx_expf (-0.5 * a * a);
+}
 
 static void
-ctx_setup_RGBA8 (CtxRasterizer *rasterizer)
+ctx_compute_gaussian_kernel (int dim, float radius, float *kernel)
 {
-  CtxGState *gstate = &rasterizer->state->gstate;
-  int components       = 4;
-  rasterizer->fragment = ctx_rasterizer_get_fragment_RGBA8 (rasterizer);
-  rasterizer->comp_op  = ctx_RGBA8_porter_duff_generic;
-  rasterizer->comp = CTX_COV_PATH_FALLBACK;
-
-  int blend_mode       = gstate->blend_mode;
-  int compositing_mode = gstate->compositing_mode;
-
-  if (gstate->source_fill.type == CTX_SOURCE_COLOR)
+  float sigma = radius / 2;
+  float sum = 0.0;
+  int i = 0;
+  //for (int row = 0; row < dim; row ++)
+    for (int col = 0; col < dim; col ++, i++)
     {
-      ctx_fragment_color_RGBA8 (rasterizer, 0,0, rasterizer->color, 1, 0,0);
-      if (gstate->global_alpha_u8 != 255)
-      {
-        for (int c = 0; c < 4; c ++)
-          rasterizer->color[c] = (rasterizer->color[c] * gstate->global_alpha_u8 + 255)>>8;
-      }
-      uint32_t src_pix    = ((uint32_t*)rasterizer->color)[0];
-      uint32_t si_ga      = (src_pix & 0xff00ff00) >> 8;
-      uint32_t si_rb      = src_pix & 0x00ff00ff;
-      uint32_t si_ga_full = si_ga * 255;
-      uint32_t si_rb_full = si_rb * 255;
-//      uint32_t si_a       = si_ga >> 16;
-
-      ((uint32_t*)rasterizer->color)[1] = si_ga;
-      ((uint32_t*)rasterizer->color)[2] = si_rb;
-      ((uint32_t*)rasterizer->color)[3] = si_ga_full;
-      ((uint32_t*)rasterizer->color)[4] = si_rb_full;
-
-      if (blend_mode == CTX_BLEND_NORMAL &&
-           compositing_mode == CTX_COMPOSITE_COPY)
-      {
-        rasterizer->comp_op = ctx_RGBA8_source_copy_normal_color;
-        rasterizer->comp = CTX_COV_PATH_COPY;
-      }
-      else if (blend_mode == CTX_BLEND_NORMAL &&
-          compositing_mode == CTX_COMPOSITE_SOURCE_OVER)
-     {
-       if (rasterizer->color[components-1] == 255)
-       {
-        rasterizer->comp_op = ctx_RGBA8_source_copy_normal_color;
-        rasterizer->comp = CTX_COV_PATH_COPY;
-       }
-       else
-       {
-        rasterizer->comp_op = ctx_RGBA8_source_over_normal_color;
-        rasterizer->comp = CTX_COV_PATH_OVER;
-       }
-     }
-     else if (compositing_mode == CTX_COMPOSITE_CLEAR)
-     {
-       rasterizer->comp_op = ctx_RGBA8_clear_normal;
-     }
-  }
-  else if (blend_mode == CTX_BLEND_NORMAL &&
-           compositing_mode == CTX_COMPOSITE_SOURCE_OVER)
-  {
-     rasterizer->comp_op = ctx_RGBA8_source_over_normal_fragment;
-     rasterizer->comp = CTX_COV_PATH_OVER_FRAGMENT;
-  }
-  else if (blend_mode == CTX_BLEND_NORMAL &&
-           compositing_mode == CTX_COMPOSITE_COPY)
-  {
-     rasterizer->comp_op = ctx_RGBA8_source_copy_normal_fragment;
-     rasterizer->comp = CTX_COV_PATH_COPY_FRAGMENT;
-  }
+      float val = //ctx_gaussian (row, radius, sigma) *
+                            ctx_gaussian (col, radius, sigma);
+      kernel[i] = val;
+      sum += val;
+    }
+  i = 0;
+  //for (int row = 0; row < dim; row ++)
+    for (int col = 0; col < dim; col ++, i++)
+        kernel[i] /= sum;
 }
+#endif
 
 static void
-ctx_composite_convert (CTX_COMPOSITE_ARGUMENTS)
+ctx_rasterizer_round_rectangle (CtxRasterizer *rasterizer, float x, float y, float width, float height, 
float corner_radius)
 {
-  uint8_t pixels[count * rasterizer->format->ebpp];
-  rasterizer->format->to_comp (rasterizer, x0, dst, &pixels[0], count);
-  rasterizer->comp_op (rasterizer, &pixels[0], rasterizer->color, x0, coverage, count);
-  rasterizer->format->from_comp (rasterizer, x0, &pixels[0], dst, count);
+  float aspect  = 1.0f;
+  float radius  = corner_radius / aspect;
+  float degrees = CTX_PI / 180.0f;
+
+  if (radius > width/2) radius = width/2;
+  if (radius > height/2) radius = height/2;
+
+  ctx_rasterizer_finish_shape (rasterizer);
+  ctx_rasterizer_arc (rasterizer, x + width - radius, y + radius, radius, -90 * degrees, 0 * degrees, 0);
+  ctx_rasterizer_arc (rasterizer, x + width - radius, y + height - radius, radius, 0 * degrees, 90 * 
degrees, 0);
+  ctx_rasterizer_arc (rasterizer, x + radius, y + height - radius, radius, 90 * degrees, 180 * degrees, 0);
+  ctx_rasterizer_arc (rasterizer, x + radius, y + radius, radius, 180 * degrees, 270 * degrees, 0);
+  ctx_rasterizer_finish_shape (rasterizer);
 }
 
-#if CTX_ENABLE_FLOAT
 static void
-ctx_float_copy_normal (int components, CTX_COMPOSITE_ARGUMENTS)
-{
-  float *dstf = (float*)dst;
-  float *srcf = (float*)src;
-  float u0 = 0; float v0 = 0;
-  float ud = 0; float vd = 0;
-
-  ctx_init_uv (rasterizer, x0, count, &u0, &v0, &ud, &vd);
+ctx_rasterizer_process (void *user_data, CtxCommand *command);
 
-  while (count--)
-  {
-    uint8_t cov = *coverage;
-    float covf = ctx_u8_to_float (cov);
-    for (int c = 0; c < components; c++)
-      dstf[c] = dstf[c]*(1.0-covf) + srcf[c]*covf;
-    dstf += components;
-    coverage ++;
-  }
+int
+_ctx_is_rasterizer (Ctx *ctx)
+{
+  if (ctx->renderer && ctx->renderer->process == ctx_rasterizer_process)
+    return 1;
+  return 0;
 }
 
+#if CTX_COMPOSITING_GROUPS
 static void
-ctx_float_clear_normal (int components, CTX_COMPOSITE_ARGUMENTS)
+ctx_rasterizer_start_group (CtxRasterizer *rasterizer)
 {
-  float *dstf = (float*)dst;
-  while (count--)
+  CtxEntry save_command = ctx_void(CTX_SAVE);
+  // allocate buffer, and set it as temporary target
+  int no;
+  if (rasterizer->group[0] == NULL) // first group
   {
-#if 0
-    uint8_t cov = *coverage;
-    if (cov == 0)
-    {
-    }
-    else if (cov == 255)
-    {
-#endif
-      switch (components)
-      {
-        case 2:
-          ((uint64_t*)(dst))[0] = 0;
-          break;
-        case 4:
-          ((uint64_t*)(dst))[0] = 0;
-          ((uint64_t*)(dst))[1] = 0;
-          break;
-        default:
-          for (int c = 0; c < components; c++)
-            dstf[c] = 0.0f;
-      }
-#if 0
-    }
-    else
-    {
-      float ralpha = 1.0 - ctx_u8_to_float (cov);
-      for (int c = 0; c < components; c++)
-        { dstf[c] = (dstf[c] * ralpha); }
-    }
-    coverage ++;
-#endif
-    dstf += components;
+    rasterizer->saved_buf = rasterizer->buf;
   }
-}
-
+  for (no = 0; rasterizer->group[no] && no < CTX_GROUP_MAX; no++);
 
-static void
-ctx_float_source_over_normal_color (int components, CTX_COMPOSITE_ARGUMENTS)
-{
-  float *dstf = (float*)dst;
-  float *srcf = (float*)src;
-  while (count--)
-  {
-    uint8_t cov = *coverage;
-    float fcov = ctx_u8_to_float (cov);
-    float ralpha = 1.0f - fcov * srcf[components-1];
-    for (int c = 0; c < components-1; c++)
-      dstf[c] = (srcf[c]*fcov + dstf[c] * ralpha);
-    coverage ++;
-    dstf+= components;
-  }
+  if (no >= CTX_GROUP_MAX)
+     return;
+  rasterizer->group[no] = ctx_buffer_new (rasterizer->blit_width,
+                                          rasterizer->blit_height,
+                                          rasterizer->format->composite_format);
+  rasterizer->buf = rasterizer->group[no]->data;
+  ctx_rasterizer_process (rasterizer, (CtxCommand*)&save_command);
 }
 
 static void
-ctx_float_source_copy_normal_color (int components, CTX_COMPOSITE_ARGUMENTS)
-{
-  float *dstf = (float*)dst;
-  float *srcf = (float*)src;
-
-  while (count--)
-  {
-    uint8_t cov = *coverage;
-    float fcov = ctx_u8_to_float (cov);
-    float ralpha = 1.0f - fcov;
-    for (int c = 0; c < components-1; c++)
-      dstf[c] = (srcf[c]*fcov + dstf[c] * ralpha);
-    coverage ++;
-    dstf+= components;
-  }
-}
-
-inline static void
-ctx_float_blend_normal (int components, float *dst, float *src, float *blended)
+ctx_rasterizer_end_group (CtxRasterizer *rasterizer)
 {
-  float a = src[components-1];
-  for (int c = 0; c <  components - 1; c++)
-    blended[c] = src[c] * a;
-  blended[components-1]=a;
-}
+  CtxEntry restore_command = ctx_void(CTX_RESTORE);
+  CtxEntry save_command = ctx_void(CTX_SAVE);
+  int no = 0;
+  for (no = 0; rasterizer->group[no] && no < CTX_GROUP_MAX; no++);
+  no--;
 
-static float ctx_float_get_max (int components, float *c)
-{
-  float max = -1000.0f;
-  for (int i = 0; i < components - 1; i ++)
-  {
-    if (c[i] > max) max = c[i];
-  }
-  return max;
-}
+  if (no < 0)
+    return;
 
-static float ctx_float_get_min (int components, float *c)
-{
-  float min = 400.0;
-  for (int i = 0; i < components - 1; i ++)
+  CtxCompositingMode comp = rasterizer->state->gstate.compositing_mode;
+  CtxBlend blend = rasterizer->state->gstate.blend_mode;
+  float global_alpha = rasterizer->state->gstate.global_alpha_f;
+  // fetch compositing, blending, global alpha
+  ctx_rasterizer_process (rasterizer, (CtxCommand*)&restore_command);
+  ctx_rasterizer_process (rasterizer, (CtxCommand*)&save_command);
+  CtxEntry set_state[3]=
   {
-    if (c[i] < min) min = c[i];
-  }
-  return min;
-}
-
-static float ctx_float_get_lum (int components, float *c)
-{
-  switch (components)
+    ctx_u8 (CTX_COMPOSITING_MODE, comp,  0,0,0,0,0,0,0),
+    ctx_u8 (CTX_BLEND_MODE,       blend, 0,0,0,0,0,0,0),
+    ctx_f  (CTX_GLOBAL_ALPHA,     global_alpha, 0.0)
+  };
+  ctx_rasterizer_process (rasterizer, (CtxCommand*)&set_state[0]);
+  ctx_rasterizer_process (rasterizer, (CtxCommand*)&set_state[1]);
+  ctx_rasterizer_process (rasterizer, (CtxCommand*)&set_state[2]);
+  if (no == 0)
   {
-    case 3:
-    case 4:
-            return CTX_CSS_RGB_TO_LUMINANCE(c);
-    case 1:
-    case 2:
-            return c[0];
-            break;
-    default:
-       {
-         float sum = 0;
-         for (int i = 0; i < components - 1; i ++)
-         {
-           sum += c[i];
-         }
-         return sum / (components - 1);
-       }
+    rasterizer->buf = rasterizer->saved_buf;
   }
-}
-
-static float ctx_float_get_sat (int components, float *c)
-{
-  switch (components)
+  else
   {
-    case 3:
-    case 4:
-            { float r = c[0];
-              float g = c[1];
-              float b = c[2];
-              return ctx_maxf(r, ctx_maxf(g,b)) - ctx_minf(r,ctx_minf(g,b));
-            }
-            break;
-    case 1:
-    case 2: return 0.0;
-            break;
-    default:
-       {
-         float min = 1000;
-         float max = -1000;
-         for (int i = 0; i < components - 1; i ++)
-         {
-           if (c[i] < min) min = c[i];
-           if (c[i] > max) max = c[i];
-         }
-         return max-min;
-       }
+    rasterizer->buf = rasterizer->group[no-1]->data;
   }
-}
-
-static void ctx_float_set_lum (int components, float *c, float lum)
-{
-  float d = lum - ctx_float_get_lum (components, c);
-  float tc[components];
-  for (int i = 0; i < components - 1; i++)
+  // XXX use texture_source ?
+   ctx_texture_init (rasterizer->ctx, ".ctx-group", // XXX ? count groups..
+                  rasterizer->blit_width,  // or have group based on thread-id?
+                  rasterizer->blit_height, // .. this would mean threadsafe
+                                           // allocation
+                  rasterizer->blit_width * rasterizer->format->bpp/8,
+                  rasterizer->format->pixel_format,
+                  NULL, // space
+                  (uint8_t*)rasterizer->group[no]->data,
+                  NULL, NULL);
   {
-    tc[i] = c[i] + d;
-  }
+     char *eid = ".ctx-group";
+     int   eid_len = strlen (eid);
 
-  float l = ctx_float_get_lum (components, tc);
-  float n = ctx_float_get_min (components, tc);
-  float x = ctx_float_get_max (components, tc);
+     CtxEntry commands[4] =
+      {
+       ctx_f  (CTX_TEXTURE, rasterizer->blit_x, rasterizer->blit_y), 
+       ctx_u32 (CTX_DATA, eid_len, eid_len/9+1),
+       ctx_u32 (CTX_CONT, 0,0),
+       ctx_u32 (CTX_CONT, 0,0)
+      };
+     memcpy( (char *) &commands[2].data.u8[0], eid, eid_len);
+     ( (char *) (&commands[2].data.u8[0]) ) [eid_len]=0;
 
-  if (n < 0.0f && l != n)
+     ctx_rasterizer_process (rasterizer, (CtxCommand*)commands);
+  }
   {
-    for (int i = 0; i < components - 1; i++)
-      tc[i] = l + (((tc[i] - l) * l) / (l-n));
+    CtxEntry commands[2]=
+    {
+      ctx_f (CTX_RECTANGLE, rasterizer->blit_x, rasterizer->blit_y),
+      ctx_f (CTX_CONT,      rasterizer->blit_width, rasterizer->blit_height)
+    };
+    ctx_rasterizer_process (rasterizer, (CtxCommand*)commands);
   }
-
-  if (x > 1.0f && x != l)
   {
-    for (int i = 0; i < components - 1; i++)
-      tc[i] = l + (((tc[i] - l) * (1.0f - l)) / (x-l));
+    CtxEntry commands[1]= { ctx_void (CTX_FILL) };
+    ctx_rasterizer_process (rasterizer, (CtxCommand*)commands);
   }
-  for (int i = 0; i < components - 1; i++)
-    c[i] = tc[i];
+  //ctx_texture_release (rasterizer->ctx, ".ctx-group");
+  ctx_buffer_free (rasterizer->group[no]);
+  rasterizer->group[no] = 0;
+  ctx_rasterizer_process (rasterizer, (CtxCommand*)&restore_command);
 }
+#endif
 
-static void ctx_float_set_sat (int components, float *c, float sat)
+#if CTX_ENABLE_SHADOW_BLUR
+static void
+ctx_rasterizer_shadow_stroke (CtxRasterizer *rasterizer)
 {
-  int max = 0, mid = 1, min = 2;
-  
-  if (c[min] > c[mid]){int t = min; min = mid; mid = t;}
-  if (c[mid] > c[max]){int t = mid; mid = max; max = t;}
-  if (c[min] > c[mid]){int t = min; min = mid; mid = t;}
+  CtxColor color;
+  CtxEntry save_command = ctx_void(CTX_SAVE);
 
-  if (c[max] > c[min])
+  float rgba[4] = {0, 0, 0, 1.0};
+  if (ctx_get_color (rasterizer->ctx, CTX_shadowColor, &color) == 0)
+    ctx_color_get_rgba (rasterizer->state, &color, rgba);
+
+  CtxEntry set_color_command [3]=
   {
-    c[mid] = ((c[mid]-c[min]) * sat) / (c[max] - c[min]);
-    c[max] = sat;
-  }
-  else
+    ctx_f (CTX_COLOR, CTX_RGBA, rgba[0]),
+    ctx_f (CTX_CONT, rgba[1], rgba[2]),
+    ctx_f (CTX_CONT, rgba[3], 0)
+  };
+  CtxEntry restore_command = ctx_void(CTX_RESTORE);
+  float radius = rasterizer->state->gstate.shadow_blur;
+  int dim = 2 * radius + 1;
+  if (dim > CTX_MAX_GAUSSIAN_KERNEL_DIM)
+    dim = CTX_MAX_GAUSSIAN_KERNEL_DIM;
+  ctx_compute_gaussian_kernel (dim, radius, rasterizer->kernel);
+  ctx_rasterizer_process (rasterizer, (CtxCommand*)&save_command);
   {
-    c[mid] = c[max] = 0.0f;
+    int i = 0;
+    for (int v = 0; v < dim; v += 1, i++)
+      {
+        float dy = rasterizer->state->gstate.shadow_offset_y + v - dim/2;
+        set_color_command[2].data.f[0] = rasterizer->kernel[i] * rgba[3];
+        ctx_rasterizer_process (rasterizer, (CtxCommand*)&set_color_command[0]);
+#if CTX_ENABLE_SHADOW_BLUR
+        rasterizer->in_shadow = 1;
+#endif
+        rasterizer->shadow_x = rasterizer->state->gstate.shadow_offset_x;
+        rasterizer->shadow_y = dy;
+        rasterizer->preserve = 1;
+        ctx_rasterizer_stroke (rasterizer);
+#if CTX_ENABLE_SHADOW_BLUR
+        rasterizer->in_shadow = 0;
+#endif
+      }
   }
-  c[min] = 0.0f;
-
-}
-
-#define ctx_float_blend_define(name, CODE) \
-static void \
-ctx_float_blend_##name (int components, float * __restrict__ dst, float *src, float *blended)\
-{\
-  float *s = src; float b[components];\
-  ctx_float_deassociate_alpha (components, dst, b);\
-    CODE;\
-  blended[components-1] = s[components-1];\
-  ctx_float_associate_alpha (components, blended);\
+  //free (kernel);
+  ctx_rasterizer_process (rasterizer, (CtxCommand*)&restore_command);
 }
 
-#define ctx_float_blend_define_seperable(name, CODE) \
-        ctx_float_blend_define(name, for (int c = 0; c < components-1; c++) { CODE ;}) \
-
-ctx_float_blend_define_seperable(multiply,    blended[c] = (b[c] * s[c]);)
-ctx_float_blend_define_seperable(screen,      blended[c] = b[c] + s[c] - (b[c] * s[c]);)
-ctx_float_blend_define_seperable(overlay,     blended[c] = b[c] < 0.5f ? (s[c] * b[c]) :
-                                                          s[c] + b[c] - (s[c] * b[c]);)
-ctx_float_blend_define_seperable(darken,      blended[c] = ctx_minf (b[c], s[c]))
-ctx_float_blend_define_seperable(lighten,     blended[c] = ctx_maxf (b[c], s[c]))
-ctx_float_blend_define_seperable(color_dodge, blended[c] = (b[c] == 0.0f) ? 0.0f :
-                                     s[c] == 1.0f ? 1.0f : ctx_minf(1.0f, (b[c]) / (1.0f-s[c])))
-ctx_float_blend_define_seperable(color_burn,  blended[c] = (b[c] == 1.0f) ? 1.0f :
-                                     s[c] == 0.0f ? 0.0f : 1.0f - ctx_minf(1.0f, ((1.0f - b[c])) / s[c]))
-ctx_float_blend_define_seperable(hard_light,  blended[c] = s[c] < 0.f ? (b[c] * s[c]) :
-                                                          b[c] + s[c] - (b[c] * s[c]);)
-ctx_float_blend_define_seperable(difference,  blended[c] = (b[c] - s[c]))
+static void
+ctx_rasterizer_shadow_text (CtxRasterizer *rasterizer, const char *str)
+{
+  float x = rasterizer->state->x;
+  float y = rasterizer->state->y;
+  CtxColor color;
+  CtxEntry save_command = ctx_void(CTX_SAVE);
 
-ctx_float_blend_define_seperable(divide,      blended[c] = s[c]?(b[c]) / s[c]:0.0f)
-ctx_float_blend_define_seperable(addition,    blended[c] = s[c]+b[c])
-ctx_float_blend_define_seperable(subtract,    blended[c] = s[c]-b[c])
+  float rgba[4] = {0, 0, 0, 1.0};
+  if (ctx_get_color (rasterizer->ctx, CTX_shadowColor, &color) == 0)
+    ctx_color_get_rgba (rasterizer->state, &color, rgba);
 
-ctx_float_blend_define_seperable(exclusion,   blended[c] = b[c] + s[c] - 2.0f * b[c] * s[c])
-ctx_float_blend_define_seperable(soft_light,
-  if (s[c] <= 0.5f)
+  CtxEntry set_color_command [3]=
   {
-    blended[c] = b[c] - (1.0f - 2.0f * s[c]) * b[c] * (1.0f - b[c]);
-  }
-  else
+    ctx_f (CTX_COLOR, CTX_RGBA, rgba[0]),
+    ctx_f (CTX_CONT, rgba[1], rgba[2]),
+    ctx_f (CTX_CONT, rgba[3], 0)
+  };
+  CtxEntry move_to_command [1]=
   {
-    int d;
-    if (b[c] <= 255/4)
-      d = (((16 * b[c] - 12.0f) * b[c] + 4.0f) * b[c]);
-    else
-      d = ctx_sqrtf(b[c]);
-    blended[c] = (b[c] + (2.0f * s[c] - 1.0f) * (d - b[c]));
-  }
-)
-
-
-ctx_float_blend_define(color,
-  for (int i = 0; i < components; i++)
-    blended[i] = s[i];
-  ctx_float_set_lum(components, blended, ctx_float_get_lum (components, s));
-)
-
-ctx_float_blend_define(hue,
-  float in_sat = ctx_float_get_sat(components, b);
-  float in_lum = ctx_float_get_lum(components, b);
-  for (int i = 0; i < components; i++)
-    blended[i] = s[i];
-  ctx_float_set_sat(components, blended, in_sat);
-  ctx_float_set_lum(components, blended, in_lum);
-)
-
-ctx_float_blend_define(saturation,
-  float in_sat = ctx_float_get_sat(components, s);
-  float in_lum = ctx_float_get_lum(components, b);
-  for (int i = 0; i < components; i++)
-    blended[i] = b[i];
-  ctx_float_set_sat(components, blended, in_sat);
-  ctx_float_set_lum(components, blended, in_lum);
-)
-
-ctx_float_blend_define(luminosity,
-  float in_lum = ctx_float_get_lum(components, s);
-  for (int i = 0; i < components; i++)
-    blended[i] = b[i];
-  ctx_float_set_lum(components, blended, in_lum);
-)
+    ctx_f (CTX_MOVE_TO, x, y),
+  };
+  CtxEntry restore_command = ctx_void(CTX_RESTORE);
+  float radius = rasterizer->state->gstate.shadow_blur;
+  int dim = 2 * radius + 1;
+  if (dim > CTX_MAX_GAUSSIAN_KERNEL_DIM)
+    dim = CTX_MAX_GAUSSIAN_KERNEL_DIM;
+  ctx_compute_gaussian_kernel (dim, radius, rasterizer->kernel);
+  ctx_rasterizer_process (rasterizer, (CtxCommand*)&save_command);
 
-inline static void
-ctx_float_blend (int components, CtxBlend blend, float * __restrict__ dst, float *src, float *blended)
-{
-  switch (blend)
   {
-    case CTX_BLEND_NORMAL:      ctx_float_blend_normal      (components, dst, src, blended); break;
-    case CTX_BLEND_MULTIPLY:    ctx_float_blend_multiply    (components, dst, src, blended); break;
-    case CTX_BLEND_SCREEN:      ctx_float_blend_screen      (components, dst, src, blended); break;
-    case CTX_BLEND_OVERLAY:     ctx_float_blend_overlay     (components, dst, src, blended); break;
-    case CTX_BLEND_DARKEN:      ctx_float_blend_darken      (components, dst, src, blended); break;
-    case CTX_BLEND_LIGHTEN:     ctx_float_blend_lighten     (components, dst, src, blended); break;
-    case CTX_BLEND_COLOR_DODGE: ctx_float_blend_color_dodge (components, dst, src, blended); break;
-    case CTX_BLEND_COLOR_BURN:  ctx_float_blend_color_burn  (components, dst, src, blended); break;
-    case CTX_BLEND_HARD_LIGHT:  ctx_float_blend_hard_light  (components, dst, src, blended); break;
-    case CTX_BLEND_SOFT_LIGHT:  ctx_float_blend_soft_light  (components, dst, src, blended); break;
-    case CTX_BLEND_DIFFERENCE:  ctx_float_blend_difference  (components, dst, src, blended); break;
-    case CTX_BLEND_EXCLUSION:   ctx_float_blend_exclusion   (components, dst, src, blended); break;
-    case CTX_BLEND_COLOR:       ctx_float_blend_color       (components, dst, src, blended); break;
-    case CTX_BLEND_HUE:         ctx_float_blend_hue         (components, dst, src, blended); break;
-    case CTX_BLEND_SATURATION:  ctx_float_blend_saturation  (components, dst, src, blended); break;
-    case CTX_BLEND_LUMINOSITY:  ctx_float_blend_luminosity  (components, dst, src, blended); break;
-    case CTX_BLEND_ADDITION:    ctx_float_blend_addition    (components, dst, src, blended); break;
-    case CTX_BLEND_SUBTRACT:    ctx_float_blend_subtract    (components, dst, src, blended); break;
-    case CTX_BLEND_DIVIDE:      ctx_float_blend_divide      (components, dst, src, blended); break;
+      {
+        move_to_command[0].data.f[0] = x;
+        move_to_command[0].data.f[1] = y;
+        set_color_command[2].data.f[0] = rgba[3];
+        ctx_rasterizer_process (rasterizer, (CtxCommand*)&set_color_command);
+        ctx_rasterizer_process (rasterizer, (CtxCommand*)&move_to_command);
+        rasterizer->in_shadow=1;
+        ctx_rasterizer_text (rasterizer, str, 0);
+        rasterizer->in_shadow=0;
+      }
   }
+  ctx_rasterizer_process (rasterizer, (CtxCommand*)&restore_command);
+  move_to_command[0].data.f[0] = x;
+  move_to_command[0].data.f[1] = y;
+  ctx_rasterizer_process (rasterizer, (CtxCommand*)&move_to_command);
 }
 
-/* this is the grunt working function, when inlined code-path elimination makes
- * it produce efficient code.
- */
-CTX_INLINE static void
-ctx_float_porter_duff (CtxRasterizer         *rasterizer,
-                       int                    components,
-                       uint8_t * __restrict__ dst,
-                       uint8_t * __restrict__ src,
-                       int                    x0,
-                       uint8_t * __restrict__ coverage,
-                       int                    count,
-                       CtxCompositingMode     compositing_mode,
-                       CtxFragment            fragment,
-                       CtxBlend               blend)
+static void
+ctx_rasterizer_shadow_fill (CtxRasterizer *rasterizer)
 {
-  float *dstf = (float*)dst;
+  CtxColor color;
+  CtxEntry save_command = ctx_void(CTX_SAVE);
 
-  CtxPorterDuffFactor f_s, f_d;
-  ctx_porter_duff_factors (compositing_mode, &f_s, &f_d);
-  uint8_t global_alpha_u8 = rasterizer->state->gstate.global_alpha_u8;
-  float   global_alpha_f = rasterizer->state->gstate.global_alpha_f;
-  
-  {
-    float tsrc[components];
-    float u0 = 0; float v0 = 0;
-    float ud = 0; float vd = 0;
+  float rgba[4] = {0, 0, 0, 1.0};
+  if (ctx_get_color (rasterizer->ctx, CTX_shadowColor, &color) == 0)
+    ctx_color_get_rgba (rasterizer->state, &color, rgba);
 
-    ctx_init_uv (rasterizer, x0, count, &u0, &v0, &ud, &vd);
+  CtxEntry set_color_command [3]=
+  {
+    ctx_f (CTX_COLOR, CTX_RGBA, rgba[0]),
+    ctx_f (CTX_CONT, rgba[1], rgba[2]),
+    ctx_f (CTX_CONT, rgba[3], 0)
+  };
+  CtxEntry restore_command = ctx_void(CTX_RESTORE);
+  float radius = rasterizer->state->gstate.shadow_blur;
+  int dim = 2 * radius + 1;
+  if (dim > CTX_MAX_GAUSSIAN_KERNEL_DIM)
+    dim = CTX_MAX_GAUSSIAN_KERNEL_DIM;
+  ctx_compute_gaussian_kernel (dim, radius, rasterizer->kernel);
+  ctx_rasterizer_process (rasterizer, (CtxCommand*)&save_command);
 
-    while (count--)
-    {
-      uint8_t cov = *coverage;
-#if 1
-      if (
-        CTX_UNLIKELY((compositing_mode == CTX_COMPOSITE_DESTINATION_OVER && dst[components-1] == 1.0f)||
-        (cov == 0 && (compositing_mode == CTX_COMPOSITE_SOURCE_OVER ||
-        compositing_mode == CTX_COMPOSITE_XOR               ||
-        compositing_mode == CTX_COMPOSITE_DESTINATION_OUT   ||
-        compositing_mode == CTX_COMPOSITE_SOURCE_ATOP      
-        ))))
+  {
+    for (int v = 0; v < dim; v ++)
       {
-        u0 += ud;
-        v0 += vd;
-        coverage ++;
-        dstf+=components;
-        continue;
+        int i = v;
+        float dy = rasterizer->state->gstate.shadow_offset_y + v - dim/2;
+        set_color_command[2].data.f[0] = rasterizer->kernel[i] * rgba[3];
+        ctx_rasterizer_process (rasterizer, (CtxCommand*)&set_color_command);
+        rasterizer->in_shadow = 1;
+        rasterizer->shadow_x = rasterizer->state->gstate.shadow_offset_x;
+        rasterizer->shadow_y = dy;
+        rasterizer->preserve = 1;
+        ctx_rasterizer_fill (rasterizer);
+        rasterizer->in_shadow = 0;
       }
+  }
+  ctx_rasterizer_process (rasterizer, (CtxCommand*)&restore_command);
+}
 #endif
 
-      fragment (rasterizer, u0, v0, tsrc, 1, ud, vd);
-      if (blend != CTX_BLEND_NORMAL)
-        ctx_float_blend (components, blend, dstf, tsrc, tsrc);
-      u0 += ud;
-      v0 += vd;
-      float covf = ctx_u8_to_float (cov);
+static void
+ctx_rasterizer_line_dash (CtxRasterizer *rasterizer, int count, float *dashes)
+{
+  if (!dashes)
+  {
+    rasterizer->state->gstate.n_dashes = 0;
+    return;
+  }
+  count = CTX_MIN(count, CTX_PARSER_MAX_ARGS-1);
+  rasterizer->state->gstate.n_dashes = count;
+  memcpy(&rasterizer->state->gstate.dashes[0], dashes, count * sizeof(float));
+  for (int i = 0; i < count; i ++)
+  {
+    if (rasterizer->state->gstate.dashes[i] < 0.0001f)
+      rasterizer->state->gstate.dashes[i] = 0.0001f; // hang protection
+  }
+}
 
-      if (global_alpha_u8 != 255)
-        covf = covf * global_alpha_f;
 
-      if (covf != 1.0f)
-      {
-        for (int c = 0; c < components; c++)
-          tsrc[c] *= covf;
-      }
+static void
+ctx_rasterizer_process (void *user_data, CtxCommand *command)
+{
+  CtxEntry *entry = &command->entry;
+  CtxRasterizer *rasterizer = (CtxRasterizer *) user_data;
+  CtxState *state = rasterizer->state;
+  CtxCommand *c = (CtxCommand *) entry;
+  int clear_clip = 0;
+  ctx_interpret_style (rasterizer->state, entry, NULL);
+  switch (c->code)
+    {
+#if CTX_ENABLE_SHADOW_BLUR
+      case CTX_SHADOW_COLOR:
+        {
+          CtxColor  col;
+          CtxColor *color = &col;
+          //state->gstate.source_fill.type = CTX_SOURCE_COLOR;
+          switch ((int)c->rgba.model)
+            {
+              case CTX_RGB:
+                ctx_color_set_rgba (state, color, c->rgba.r, c->rgba.g, c->rgba.b, 1.0f);
+                break;
+              case CTX_RGBA:
+                //ctx_color_set_rgba (state, color, c->rgba.r, c->rgba.g, c->rgba.b, c->rgba.a);
+                ctx_color_set_rgba (state, color, c->rgba.r, c->rgba.g, c->rgba.b, c->rgba.a);
+                break;
+              case CTX_DRGBA:
+                ctx_color_set_drgba (state, color, c->rgba.r, c->rgba.g, c->rgba.b, c->rgba.a);
+                break;
+#if CTX_ENABLE_CMYK
+              case CTX_CMYKA:
+                ctx_color_set_cmyka (state, color, c->cmyka.c, c->cmyka.m, c->cmyka.y, c->cmyka.k, 
c->cmyka.a);
+                break;
+              case CTX_CMYK:
+                ctx_color_set_cmyka (state, color, c->cmyka.c, c->cmyka.m, c->cmyka.y, c->cmyka.k, 1.0f);
+                break;
+              case CTX_DCMYKA:
+                ctx_color_set_dcmyka (state, color, c->cmyka.c, c->cmyka.m, c->cmyka.y, c->cmyka.k, 
c->cmyka.a);
+                break;
+              case CTX_DCMYK:
+                ctx_color_set_dcmyka (state, color, c->cmyka.c, c->cmyka.m, c->cmyka.y, c->cmyka.k, 1.0f);
+                break;
+#endif
+              case CTX_GRAYA:
+                ctx_color_set_graya (state, color, c->graya.g, c->graya.a);
+                break;
+              case CTX_GRAY:
+                ctx_color_set_graya (state, color, c->graya.g, 1.0f);
+                break;
+            }
+          ctx_set_color (rasterizer->ctx, CTX_shadowColor, color);
+        }
+        break;
+#endif
+      case CTX_LINE_DASH:
+        if (c->line_dash.count)
+          {
+            ctx_rasterizer_line_dash (rasterizer, c->line_dash.count, c->line_dash.data);
+          }
+        else
+        ctx_rasterizer_line_dash (rasterizer, 0, NULL);
+        break;
 
-      for (int c = 0; c < components; c++)
-      {
-        float res;
-        /* these switches and this whole function is written to be
-         * inlined when compiled when the enum values passed in are
-         * constants.
-         */
-        switch (f_s)
+      case CTX_LINE_TO:
+        ctx_rasterizer_line_to (rasterizer, c->c.x0, c->c.y0);
+        break;
+      case CTX_REL_LINE_TO:
+        ctx_rasterizer_rel_line_to (rasterizer, c->c.x0, c->c.y0);
+        break;
+      case CTX_MOVE_TO:
+        ctx_rasterizer_move_to (rasterizer, c->c.x0, c->c.y0);
+        break;
+      case CTX_REL_MOVE_TO:
+        ctx_rasterizer_rel_move_to (rasterizer, c->c.x0, c->c.y0);
+        break;
+      case CTX_CURVE_TO:
+        ctx_rasterizer_curve_to (rasterizer, c->c.x0, c->c.y0,
+                                 c->c.x1, c->c.y1,
+                                 c->c.x2, c->c.y2);
+        break;
+      case CTX_REL_CURVE_TO:
+        ctx_rasterizer_rel_curve_to (rasterizer, c->c.x0, c->c.y0,
+                                     c->c.x1, c->c.y1,
+                                     c->c.x2, c->c.y2);
+        break;
+      case CTX_QUAD_TO:
+        ctx_rasterizer_quad_to (rasterizer, c->c.x0, c->c.y0, c->c.x1, c->c.y1);
+        break;
+      case CTX_REL_QUAD_TO:
+        ctx_rasterizer_rel_quad_to (rasterizer, c->c.x0, c->c.y0, c->c.x1, c->c.y1);
+        break;
+      case CTX_ARC:
+        ctx_rasterizer_arc (rasterizer, c->arc.x, c->arc.y, c->arc.radius, c->arc.angle1, c->arc.angle2, 
c->arc.direction);
+        break;
+      case CTX_RECTANGLE:
+        ctx_rasterizer_rectangle (rasterizer, c->rectangle.x, c->rectangle.y,
+                                  c->rectangle.width, c->rectangle.height);
+        break;
+      case CTX_ROUND_RECTANGLE:
+        ctx_rasterizer_round_rectangle (rasterizer, c->rectangle.x, c->rectangle.y,
+                                        c->rectangle.width, c->rectangle.height,
+                                        c->rectangle.radius);
+        break;
+      case CTX_SET_PIXEL:
+        ctx_rasterizer_set_pixel (rasterizer, c->set_pixel.x, c->set_pixel.y,
+                                  c->set_pixel.rgba[0],
+                                  c->set_pixel.rgba[1],
+                                  c->set_pixel.rgba[2],
+                                  c->set_pixel.rgba[3]);
+        break;
+      case CTX_DEFINE_TEXTURE:
         {
-          case CTX_PORTER_DUFF_0: res = 0.0f; break;
-          case CTX_PORTER_DUFF_1:             res = (tsrc[c]); break;
-          case CTX_PORTER_DUFF_ALPHA:         res = (tsrc[c] *       dstf[components-1]); break;
-          case CTX_PORTER_DUFF_1_MINUS_ALPHA: res = (tsrc[c] * (1.0f-dstf[components-1])); break;
+          uint8_t *pixel_data = ctx_define_texture_pixel_data (entry);
+          ctx_rasterizer_define_texture (rasterizer, c->define_texture.eid,
+                                         c->define_texture.width, c->define_texture.height,
+                                         c->define_texture.format,
+                                         pixel_data);
+          rasterizer->comp_op = NULL;
         }
-        switch (f_d)
+        break;
+      case CTX_TEXTURE:
+        ctx_rasterizer_set_texture (rasterizer, c->texture.eid,
+                                    c->texture.x, c->texture.y);
+        rasterizer->comp_op = NULL;
+        break;
+#if 0
+      case CTX_LOAD_IMAGE:
+        ctx_rasterizer_load_image (rasterizer, ctx_arg_string(),
+                                   ctx_arg_float (0), ctx_arg_float (1) );
+        break;
+#endif
+#if CTX_GRADIENTS
+      case CTX_GRADIENT_STOP:
         {
-          case CTX_PORTER_DUFF_0: dstf[c] = res; break;
-          case CTX_PORTER_DUFF_1:             dstf[c] = res + (dstf[c]); break;
-          case CTX_PORTER_DUFF_ALPHA:         dstf[c] = res + (dstf[c] *       tsrc[components-1]); break;
-          case CTX_PORTER_DUFF_1_MINUS_ALPHA: dstf[c] = res + (dstf[c] * (1.0f-tsrc[components-1])); break;
+          float rgba[4]= {ctx_u8_to_float (ctx_arg_u8 (4) ),
+                          ctx_u8_to_float (ctx_arg_u8 (4+1) ),
+                          ctx_u8_to_float (ctx_arg_u8 (4+2) ),
+                          ctx_u8_to_float (ctx_arg_u8 (4+3) )
+                         };
+          ctx_rasterizer_gradient_add_stop (rasterizer,
+                                            ctx_arg_float (0), rgba);
+          rasterizer->comp_op = NULL;
         }
-      }
-      coverage ++;
-      dstf     +=components;
-    }
-  }
-}
+        break;
+      case CTX_LINEAR_GRADIENT:
+        ctx_state_gradient_clear_stops (rasterizer->state);
+        rasterizer->comp_op = NULL;
+        break;
+      case CTX_RADIAL_GRADIENT:
+        ctx_state_gradient_clear_stops (rasterizer->state);
+        rasterizer->comp_op = NULL;
+        break;
+#endif
+      case CTX_PRESERVE:
+        rasterizer->preserve = 1;
+        break;
+      case CTX_COLOR:
+      case CTX_COMPOSITING_MODE:
+      case CTX_BLEND_MODE:
+        rasterizer->comp_op = NULL;
+        break;
+#if CTX_COMPOSITING_GROUPS
+      case CTX_START_GROUP:
+        ctx_rasterizer_start_group (rasterizer);
+        break;
+      case CTX_END_GROUP:
+        ctx_rasterizer_end_group (rasterizer);
+        break;
+#endif
 
-/* generating one function per compositing_mode would be slightly more efficient,
- * but on embedded targets leads to slightly more code bloat,
- * here we trade off a slight amount of performance
- */
-#define ctx_float_porter_duff(compformat, components, source, fragment, blend) \
-static void \
-ctx_##compformat##_porter_duff_##source (CTX_COMPOSITE_ARGUMENTS) \
-{ \
-   switch (rasterizer->state->gstate.compositing_mode) \
-   { \
-     case CTX_COMPOSITE_SOURCE_ATOP: \
-      ctx_float_porter_duff (rasterizer, components, dst, src, x0, coverage, count, \
-        CTX_COMPOSITE_SOURCE_ATOP, fragment, blend);\
-      break;\
-     case CTX_COMPOSITE_DESTINATION_ATOP:\
-      ctx_float_porter_duff (rasterizer, components, dst, src, x0, coverage, count,\
-        CTX_COMPOSITE_DESTINATION_ATOP, fragment, blend);\
-      break;\
-     case CTX_COMPOSITE_DESTINATION_IN:\
-      ctx_float_porter_duff (rasterizer, components, dst, src, x0, coverage, count,\
-        CTX_COMPOSITE_DESTINATION_IN, fragment, blend);\
-      break;\
-     case CTX_COMPOSITE_DESTINATION:\
-      ctx_float_porter_duff (rasterizer, components, dst, src, x0, coverage, count,\
-        CTX_COMPOSITE_DESTINATION, fragment, blend);\
-       break;\
-     case CTX_COMPOSITE_SOURCE_OVER:\
-      ctx_float_porter_duff (rasterizer, components, dst, src, x0, coverage, count,\
-        CTX_COMPOSITE_SOURCE_OVER, fragment, blend);\
-       break;\
-     case CTX_COMPOSITE_DESTINATION_OVER:\
-      ctx_float_porter_duff (rasterizer, components, dst, src, x0, coverage, count,\
-        CTX_COMPOSITE_DESTINATION_OVER, fragment, blend);\
-       break;\
-     case CTX_COMPOSITE_XOR:\
-      ctx_float_porter_duff (rasterizer, components, dst, src, x0, coverage, count,\
-        CTX_COMPOSITE_XOR, fragment, blend);\
-       break;\
-     case CTX_COMPOSITE_DESTINATION_OUT:\
-       ctx_float_porter_duff (rasterizer, components, dst, src, x0, coverage, count,\
-        CTX_COMPOSITE_DESTINATION_OUT, fragment, blend);\
-       break;\
-     case CTX_COMPOSITE_SOURCE_OUT:\
-       ctx_float_porter_duff (rasterizer, components, dst, src, x0, coverage, count,\
-        CTX_COMPOSITE_SOURCE_OUT, fragment, blend);\
-       break;\
-     case CTX_COMPOSITE_SOURCE_IN:\
-       ctx_float_porter_duff (rasterizer, components, dst, src, x0, coverage, count,\
-        CTX_COMPOSITE_SOURCE_IN, fragment, blend);\
-       break;\
-     case CTX_COMPOSITE_COPY:\
-       ctx_float_porter_duff (rasterizer, components, dst, src, x0, coverage, count,\
-        CTX_COMPOSITE_COPY, fragment, blend);\
-       break;\
-     case CTX_COMPOSITE_CLEAR:\
-       ctx_float_porter_duff (rasterizer, components, dst, src, x0, coverage, count,\
-        CTX_COMPOSITE_CLEAR, fragment, blend);\
-       break;\
-   }\
-}
+      case CTX_RESTORE:
+        for (int i = state->gstate_no?state->gstate_stack[state->gstate_no-1].keydb_pos:0;
+             i < state->gstate.keydb_pos; i++)
+        {
+          if (state->keydb[i].key == CTX_clip)
+          {
+            clear_clip = 1;
+          }
+        }
+        /* FALLTHROUGH */
+      case CTX_ROTATE:
+      case CTX_SCALE:
+      case CTX_TRANSLATE:
+      case CTX_IDENTITY:
+      case CTX_SAVE:
+        rasterizer->comp_op = NULL;
+        rasterizer->uses_transforms = 1;
+        ctx_interpret_transforms (rasterizer->state, entry, NULL);
+        if (clear_clip)
+        {
+          ctx_rasterizer_clip_reset (rasterizer);
+        for (int i = state->gstate_no?state->gstate_stack[state->gstate_no-1].keydb_pos:0;
+             i < state->gstate.keydb_pos; i++)
+        {
+          if (state->keydb[i].key == CTX_clip)
+          {
+            int idx = ctx_float_to_string_index (state->keydb[i].value);
+            if (idx >=0)
+            {
+              CtxEntry *edges = (CtxEntry*)&state->stringpool[idx];
+              ctx_rasterizer_clip_apply (rasterizer, edges);
+            }
+          }
+        }
+        }
+        break;
+      case CTX_STROKE:
+#if CTX_ENABLE_SHADOW_BLUR
+        if (rasterizer->state->gstate.shadow_blur > 0.0 &&
+            !rasterizer->in_text)
+          ctx_rasterizer_shadow_stroke (rasterizer);
 #endif
+        if (rasterizer->state->gstate.n_dashes)
+        {
+          int n_dashes = rasterizer->state->gstate.n_dashes;
+          float *dashes = rasterizer->state->gstate.dashes;
+          float factor = ctx_matrix_get_scale (&state->gstate.transform);
 
-#if CTX_ENABLE_RGBAF
+          int count = rasterizer->edge_list.count;
+          int aa = 15;//rasterizer->aa;
+          CtxEntry temp[count]; /* copy of already built up path's poly line  */
+          memcpy (temp, rasterizer->edge_list.entries, sizeof (temp));
+          int start = 0;
+          int end   = 0;
+      CtxMatrix transform_backup = rasterizer->state->gstate.transform;
+      ctx_matrix_identity (&rasterizer->state->gstate.transform);
+      ctx_rasterizer_reset (rasterizer); /* for dashing we create
+                                            a dashed path to stroke */
+      float prev_x = 0.0f;
+      float prev_y = 0.0f;
+      float pos = 0.0;
 
-ctx_float_porter_duff(RGBAF, 4,color,   rasterizer->fragment, rasterizer->state->gstate.blend_mode)
-ctx_float_porter_duff(RGBAF, 4,generic, rasterizer->fragment, rasterizer->state->gstate.blend_mode)
+      int   dash_no  = 0.0;
+      float dash_lpos = rasterizer->state->gstate.line_dash_offset * factor;
+      int   is_down = 0;
 
-#if CTX_INLINED_NORMAL
-#if CTX_GRADIENTS
-ctx_float_porter_duff(RGBAF, 4,linear_gradient, ctx_fragment_linear_gradient_RGBAF, 
rasterizer->state->gstate.blend_mode)
-ctx_float_porter_duff(RGBAF, 4,radial_gradient, ctx_fragment_radial_gradient_RGBAF, 
rasterizer->state->gstate.blend_mode)
-#endif
-ctx_float_porter_duff(RGBAF, 4,image,           ctx_fragment_image_RGBAF,           
rasterizer->state->gstate.blend_mode)
+          while (start < count)
+          {
+            int started = 0;
+            int i;
+            is_down = 0;
 
+            if (!is_down)
+            {
+              CtxEntry *entry = &temp[0];
+              prev_x = entry->data.s16[0] * 1.0f / CTX_SUBDIV;
+              prev_y = entry->data.s16[1] * 1.0f / aa;
+              ctx_rasterizer_move_to (rasterizer, prev_x, prev_y);
+              is_down = 1;
+            }
 
-#if CTX_GRADIENTS
-#define ctx_float_porter_duff_blend(comp_name, components, blend_mode, blend_name)\
-ctx_float_porter_duff(comp_name, components,color_##blend_name,            rasterizer->fragment,             
                  blend_mode)\
-ctx_float_porter_duff(comp_name, components,generic_##blend_name,          rasterizer->fragment,             
  blend_mode)\
-ctx_float_porter_duff(comp_name, components,linear_gradient_##blend_name,  
ctx_fragment_linear_gradient_RGBA8, blend_mode)\
-ctx_float_porter_duff(comp_name, components,radial_gradient_##blend_name,  
ctx_fragment_radial_gradient_RGBA8, blend_mode)\
-ctx_float_porter_duff(comp_name, components,image_##blend_name,            ctx_fragment_image_RGBAF,         
  blend_mode)
-#else
-#define ctx_float_porter_duff_blend(comp_name, components, blend_mode, blend_name)\
-ctx_float_porter_duff(comp_name, components,color_##blend_name,            rasterizer->fragment,             
                  blend_mode)\
-ctx_float_porter_duff(comp_name, components,generic_##blend_name,          rasterizer->fragment,             
  blend_mode)\
-ctx_float_porter_duff(comp_name, components,image_##blend_name,            ctx_fragment_image_RGBAF,         
  blend_mode)
-#endif
 
-ctx_float_porter_duff_blend(RGBAF, 4, CTX_BLEND_NORMAL, normal)
+            for (i = start; i < count; i++)
+            {
+              CtxEntry *entry = &temp[i];
+              float x, y;
+              if (entry->code == CTX_NEW_EDGE)
+                {
+                  if (started)
+                    {
+                      end = i - 1;
+                      dash_no = 0;
+                      dash_lpos = 0.0;
+                      goto foo;
+                    }
+                  prev_x = entry->data.s16[0] * 1.0f / CTX_SUBDIV;
+                  prev_y = entry->data.s16[1] * 1.0f / aa;
+                  started = 1;
+                  start = i;
+                  is_down = 1;
+                  ctx_rasterizer_move_to (rasterizer, prev_x, prev_y);
+                }
 
+again:
 
-static void
-ctx_RGBAF_copy_normal (CTX_COMPOSITE_ARGUMENTS)
-{
-  ctx_float_copy_normal (4, rasterizer, dst, src, x0, coverage, count);
-}
+              x = entry->data.s16[2] * 1.0f / CTX_SUBDIV;
+              y = entry->data.s16[3] * 1.0f / aa;
+              float dx = x - prev_x;
+              float dy = y - prev_y;
+              float length = ctx_fast_hypotf (dx, dy);
 
-static void
-ctx_RGBAF_clear_normal (CTX_COMPOSITE_ARGUMENTS)
-{
-  ctx_float_clear_normal (4, rasterizer, dst, src, x0, coverage, count);
-}
 
-#if 1
-static void
-ctx_RGBAF_source_over_normal_color (CTX_COMPOSITE_ARGUMENTS)
-{
-  ctx_float_source_over_normal_color (4, rasterizer, dst, rasterizer->color, x0, coverage, count);
-}
+              if (dash_lpos + length >= dashes[dash_no] * factor)
+              {
+                float p = (dashes[dash_no] * factor - dash_lpos) / length;
+                float splitx = x * p + (1.0f - p) * prev_x;
+                float splity = y * p + (1.0f - p) * prev_y;
+                if (is_down)
+                {
+                  ctx_rasterizer_line_to (rasterizer, splitx, splity);
+                  is_down = 0;
+                }
+                else
+                {
+                  ctx_rasterizer_move_to (rasterizer, splitx, splity);
+                  is_down = 1;
+                }
+                prev_x = splitx;
+                prev_y = splity;
+                dash_no++;
+                dash_lpos=0;
+                if (dash_no >= n_dashes) dash_no = 0;
+                goto again;
+              }
+              else
+              {
+                pos += length;
+                dash_lpos += length;
+                {
+                  if (is_down)
+                    ctx_rasterizer_line_to (rasterizer, x, y);
+                }
+              }
+              prev_x = x;
+              prev_y = y;
+            }
+          end = i-1;
+foo:
+          start = end+1;
+        }
+      rasterizer->state->gstate.transform = transform_backup;
+        }
+
+        ctx_rasterizer_stroke (rasterizer);
+        break;
+      case CTX_FONT:
+        ctx_rasterizer_set_font (rasterizer, ctx_arg_string() );
+        break;
+      case CTX_TEXT:
+        rasterizer->in_text++;
+#if CTX_ENABLE_SHADOW_BLUR
+        if (rasterizer->state->gstate.shadow_blur > 0.0)
+          ctx_rasterizer_shadow_text (rasterizer, ctx_arg_string ());
 #endif
+        ctx_rasterizer_text (rasterizer, ctx_arg_string(), 0);
+        rasterizer->in_text--;
+        ctx_rasterizer_reset (rasterizer);
+        break;
+      case CTX_STROKE_TEXT:
+        ctx_rasterizer_text (rasterizer, ctx_arg_string(), 1);
+        ctx_rasterizer_reset (rasterizer);
+        break;
+      case CTX_GLYPH:
+        ctx_rasterizer_glyph (rasterizer, entry[0].data.u32[0], entry[0].data.u8[4]);
+        break;
+      case CTX_FILL:
+#if CTX_ENABLE_SHADOW_BLUR
+        if (rasterizer->state->gstate.shadow_blur > 0.0 &&
+            !rasterizer->in_text)
+          ctx_rasterizer_shadow_fill (rasterizer);
 #endif
+        ctx_rasterizer_fill (rasterizer);
+        break;
+      case CTX_RESET:
+      case CTX_BEGIN_PATH:
+        ctx_rasterizer_reset (rasterizer);
+        break;
+      case CTX_CLIP:
+        ctx_rasterizer_clip (rasterizer);
+        break;
+      case CTX_CLOSE_PATH:
+        ctx_rasterizer_finish_shape (rasterizer);
+        break;
+    }
+  ctx_interpret_pos_bare (rasterizer->state, entry, NULL);
+}
 
-static void
-ctx_setup_RGBAF (CtxRasterizer *rasterizer)
+void
+ctx_rasterizer_deinit (CtxRasterizer *rasterizer)
 {
-  CtxGState *gstate = &rasterizer->state->gstate;
-  int components = 4;
-  rasterizer->fragment = ctx_rasterizer_get_fragment_RGBAF (rasterizer);
-#if 1
-  if (gstate->source_fill.type == CTX_SOURCE_COLOR)
+  ctx_drawlist_deinit (&rasterizer->edge_list);
+#if CTX_ENABLE_CLIP
+  if (rasterizer->clip_buffer)
+  {
+    ctx_buffer_free (rasterizer->clip_buffer);
+    rasterizer->clip_buffer = NULL;
+  }
+#endif
+#if CTX_SHAPE_CACHE
+  for (int i = 0; i < CTX_SHAPE_CACHE_ENTRIES; i ++)
+    if (rasterizer->shape_cache.entries[i])
     {
-      rasterizer->comp_op = ctx_RGBAF_porter_duff_color;
-      ctx_color_get_rgba (rasterizer->state, &gstate->source_fill.color, (float*)rasterizer->color);
-      if (gstate->global_alpha_u8 != 255)
-        for (int c = 0; c < components; c ++)
-          ((float*)rasterizer->color)[c] *= gstate->global_alpha_f;
+      free (rasterizer->shape_cache.entries[i]);
+      rasterizer->shape_cache.entries[i] = NULL;
     }
-  else
+
 #endif
+  free (rasterizer);
+}
+
+
+CtxAntialias ctx_get_antialias (Ctx *ctx)
+{
+#if CTX_EVENTS
+  if (ctx_renderer_is_sdl (ctx) || ctx_renderer_is_fb (ctx))
   {
-    rasterizer->comp_op = ctx_RGBAF_porter_duff_generic;
+     CtxTiled *fb = (CtxTiled*)(ctx->renderer);
+     return fb->antialias;
   }
+#endif
+  if (!_ctx_is_rasterizer (ctx)) return CTX_ANTIALIAS_DEFAULT;
 
-#if CTX_INLINED_NORMAL
-  if (gstate->compositing_mode == CTX_COMPOSITE_CLEAR)
-    rasterizer->comp_op = ctx_RGBAF_clear_normal;
-  else
-    switch (gstate->blend_mode)
-    {
-      case CTX_BLEND_NORMAL:
-        if (gstate->compositing_mode == CTX_COMPOSITE_COPY)
-        {
-          rasterizer->comp_op = ctx_RGBAF_copy_normal;
-        }
-        else if (gstate->global_alpha_u8 == 0)
-        {
-          rasterizer->comp_op = ctx_RGBA8_nop;
-        }
-        else
-        switch (gstate->source_fill.type)
-        {
-          case CTX_SOURCE_COLOR:
-            if (gstate->compositing_mode == CTX_COMPOSITE_SOURCE_OVER)
-            {
-              if (((float*)(rasterizer->color))[components-1] == 0.0f)
-                rasterizer->comp_op = ctx_RGBA8_nop;
-        //      else if (((float*)(rasterizer->color))[components-1] == 0.0f)
-              else
-                 rasterizer->comp_op = ctx_RGBAF_source_over_normal_color;
-                //rasterizer->comp_op = ctx_RGBAF_source_over_normal_color;
-            }
-            else
-            {
-              rasterizer->comp_op = ctx_RGBAF_porter_duff_color_normal;
-            }
-            break;
-#if CTX_GRADIENTS
-          case CTX_SOURCE_LINEAR_GRADIENT:
-            rasterizer->comp_op = ctx_RGBAF_porter_duff_linear_gradient_normal;
-            break;
-          case CTX_SOURCE_RADIAL_GRADIENT:
-            rasterizer->comp_op = ctx_RGBAF_porter_duff_radial_gradient_normal;
-            break;
-#endif
-          case CTX_SOURCE_TEXTURE:
-            rasterizer->comp_op = ctx_RGBAF_porter_duff_image_normal;
-            break;
-          default:
-            rasterizer->comp_op = ctx_RGBAF_porter_duff_generic_normal;
-            break;
-        }
-        break;
-      default:
-        switch (gstate->source_fill.type)
-        {
-          case CTX_SOURCE_COLOR:
-            rasterizer->comp_op = ctx_RGBAF_porter_duff_color;
-            //rasterizer->fragment = NULL;
-            break;
-#if CTX_GRADIENTS
-          case CTX_SOURCE_LINEAR_GRADIENT:
-            rasterizer->comp_op = ctx_RGBAF_porter_duff_linear_gradient;
-            break;
-          case CTX_SOURCE_RADIAL_GRADIENT:
-            rasterizer->comp_op = ctx_RGBAF_porter_duff_radial_gradient;
-            break;
-#endif
-          case CTX_SOURCE_TEXTURE:
-            rasterizer->comp_op = ctx_RGBAF_porter_duff_image;
-            break;
-          default:
-            rasterizer->comp_op = ctx_RGBAF_porter_duff_generic;
-            break;
-        }
-        break;
-    }
-#endif
-}
-
-#endif
-#if CTX_ENABLE_GRAYAF
-
-#if CTX_GRADIENTS
-static void
-ctx_fragment_linear_gradient_GRAYAF (CtxRasterizer *rasterizer, float x, float y, void *out, int count, 
float dx, float dy)
-{
-  float rgba[4];
-  CtxSource *g = &rasterizer->state->gstate.source_fill;
-  for (int i = 0 ; i < count; i++)
+  switch (((CtxRasterizer*)(ctx->renderer))->aa)
   {
-  float v = ( ( (g->linear_gradient.dx * x + g->linear_gradient.dy * y) /
-                g->linear_gradient.length) -
-              g->linear_gradient.start) * (g->linear_gradient.rdelta);
-  ctx_fragment_gradient_1d_RGBAF (rasterizer, v, 1.0, rgba);
-  ((float*)out)[0] = ctx_float_color_rgb_to_gray (rasterizer->state, rgba);
-  ((float*)out)[1] = rgba[3];
-     out = ((float*)(out)) + 2;
-     x += dx;
-     y += dy;
+    case 1: return CTX_ANTIALIAS_NONE;
+    case 3: return CTX_ANTIALIAS_FAST;
+    case 5: return CTX_ANTIALIAS_GOOD;
+    default:
+    case 15: return CTX_ANTIALIAS_DEFAULT;
+    case 17: return CTX_ANTIALIAS_BEST;
   }
 }
 
-static void
-ctx_fragment_radial_gradient_GRAYAF (CtxRasterizer *rasterizer, float x, float y, void *out, int count, 
float dx, float dy)
+int _ctx_antialias_to_aa (CtxAntialias antialias)
 {
-  float rgba[4];
-  CtxSource *g = &rasterizer->state->gstate.source_fill;
-  for (int i = 0; i < count; i ++)
+  switch (antialias)
   {
-  float v = 0.0f;
-  if ((g->radial_gradient.r1-g->radial_gradient.r0) > 0.0f)
-    {
-      v = ctx_hypotf (g->radial_gradient.x0 - x, g->radial_gradient.y0 - y);
-      v = (v - g->radial_gradient.r0) / (g->radial_gradient.rdelta);
-    }
-  ctx_fragment_gradient_1d_RGBAF (rasterizer, v, 0.0, rgba);
-  ((float*)out)[0] = ctx_float_color_rgb_to_gray (rasterizer->state, rgba);
-  ((float*)out)[1] = rgba[3];
-     out = ((float*)(out)) + 2;
-     x += dx;
-     y += dy;
+    case CTX_ANTIALIAS_NONE: return 1;
+    case CTX_ANTIALIAS_FAST: return 3;
+    case CTX_ANTIALIAS_GOOD: return 5;
+    default:
+    case CTX_ANTIALIAS_DEFAULT: return CTX_RASTERIZER_AA;
+    case CTX_ANTIALIAS_BEST: return 17;
   }
 }
-#endif
 
-static void
-ctx_fragment_color_GRAYAF (CtxRasterizer *rasterizer, float x, float y, void *out, int count, float dx, 
float dy)
+void
+ctx_set_antialias (Ctx *ctx, CtxAntialias antialias)
 {
-  CtxSource *g = &rasterizer->state->gstate.source_fill;
-  for (int i = 0; i < count; i++)
+#if CTX_EVENTS
+  if (ctx_renderer_is_sdl (ctx) || ctx_renderer_is_fb (ctx))
   {
-     ctx_color_get_graya (rasterizer->state, &g->color, (float*)out);
-     out = ((float*)(out)) + 2;
-     x += dx;
-     y += dy;
+     CtxTiled *fb = (CtxTiled*)(ctx->renderer);
+     fb->antialias = antialias;
+     for (int i = 0; i < _ctx_max_threads; i++)
+     {
+       ctx_set_antialias (fb->host[i], antialias);
+     }
+     return;
   }
-}
+#endif
+  if (!_ctx_is_rasterizer (ctx)) return;
 
-static void ctx_fragment_image_GRAYAF (CtxRasterizer *rasterizer, float x, float y, void *out, int count, 
float dx, float dy)
-{
-  uint8_t rgba[4];
-  float rgbaf[4];
-  CtxGState *gstate = &rasterizer->state->gstate;
-  CtxBuffer *buffer = gstate->source_fill.texture.buffer;
-  switch (buffer->format->bpp)
-    {
-      case 1:  ctx_fragment_image_gray1_RGBA8 (rasterizer, x, y, rgba, count, dx, dy); break;
-      case 24: ctx_fragment_image_rgb8_RGBA8 (rasterizer, x, y, rgba, count, dx, dy);  break;
-      case 32: ctx_fragment_image_rgba8_RGBA8 (rasterizer, x, y, rgba, count, dx, dy); break;
-      default: ctx_fragment_image_RGBA8 (rasterizer, x, y, rgba, count, dx, dy);       break;
-    }
-  for (int c = 0; c < 2 * count; c ++) { 
-    rgbaf[c] = ctx_u8_to_float (rgba[c]);
-    ((float*)out)[0] = ctx_float_color_rgb_to_gray (rasterizer->state, rgbaf);
-    ((float*)out)[1] = rgbaf[3];
-    out = ((float*)out) + 2;
-  }
+  ((CtxRasterizer*)(ctx->renderer))->aa = 
+     _ctx_antialias_to_aa (antialias);
+/* vertical level of supersampling at full/forced AA.
+ *
+ * 1 is none, 3 is fast 5 is good 15 or 17 is best for 8bit
+ *
+ * valid values:  - for other values we do not add up to 255
+ * 3 5 15 17 51
+ *
+ */
 }
 
-static CtxFragment ctx_rasterizer_get_fragment_GRAYAF (CtxRasterizer *rasterizer)
+CtxRasterizer *
+ctx_rasterizer_init (CtxRasterizer *rasterizer, Ctx *ctx, Ctx *texture_source, CtxState *state, void *data, 
int x, int y, int width, int height, int stride, CtxPixelFormat pixel_format, CtxAntialias antialias)
 {
-  CtxGState *gstate = &rasterizer->state->gstate;
-  switch (gstate->source_fill.type)
-    {
-      case CTX_SOURCE_TEXTURE:           return ctx_fragment_image_GRAYAF;
-      case CTX_SOURCE_COLOR:           return ctx_fragment_color_GRAYAF;
-#if CTX_GRADIENTS
-      case CTX_SOURCE_LINEAR_GRADIENT: return ctx_fragment_linear_gradient_GRAYAF;
-      case CTX_SOURCE_RADIAL_GRADIENT: return ctx_fragment_radial_gradient_GRAYAF;
+#if CTX_ENABLE_CLIP
+  if (rasterizer->clip_buffer)
+    ctx_buffer_free (rasterizer->clip_buffer);
 #endif
-    }
-  return ctx_fragment_color_GRAYAF;
-}
+  if (rasterizer->edge_list.size)
+    ctx_drawlist_deinit (&rasterizer->edge_list);
 
-ctx_float_porter_duff(GRAYAF, 2,color,   rasterizer->fragment, rasterizer->state->gstate.blend_mode)
-ctx_float_porter_duff(GRAYAF, 2,generic, rasterizer->fragment, rasterizer->state->gstate.blend_mode)
+  ctx_memset (rasterizer, 0, sizeof (CtxRasterizer) );
+  rasterizer->vfuncs.process = ctx_rasterizer_process;
+  rasterizer->vfuncs.free    = (CtxDestroyNotify)ctx_rasterizer_deinit;
+  rasterizer->edge_list.flags |= CTX_DRAWLIST_EDGE_LIST;
+  rasterizer->state       = state;
+  rasterizer->ctx         = ctx;
+  rasterizer->texture_source = texture_source?texture_source:ctx;
+  rasterizer->aa          = _ctx_antialias_to_aa (antialias);
+  ctx_state_init (rasterizer->state);
+  rasterizer->buf         = data;
+  rasterizer->blit_x      = x;
+  rasterizer->blit_y      = y;
+  rasterizer->blit_width  = width;
+  rasterizer->blit_height = height;
+  rasterizer->state->gstate.clip_min_x  = x;
+  rasterizer->state->gstate.clip_min_y  = y;
+  rasterizer->state->gstate.clip_max_x  = x + width - 1;
+  rasterizer->state->gstate.clip_max_y  = y + height - 1;
+  rasterizer->blit_stride = stride;
+  rasterizer->scan_min    = 5000;
+  rasterizer->scan_max    = -5000;
+  rasterizer->format = ctx_pixel_format_info (pixel_format);
 
-#if CTX_INLINED_NORMAL
-ctx_float_porter_duff(GRAYAF, 2,color_normal,   rasterizer->fragment, CTX_BLEND_NORMAL)
-ctx_float_porter_duff(GRAYAF, 2,generic_normal, rasterizer->fragment, CTX_BLEND_NORMAL)
+  return rasterizer;
+}
 
-static void
-ctx_GRAYAF_copy_normal (CTX_COMPOSITE_ARGUMENTS)
+Ctx *
+ctx_new_for_buffer (CtxBuffer *buffer)
 {
-  ctx_float_copy_normal (2, rasterizer, dst, src, x0, coverage, count);
+  Ctx *ctx = ctx_new ();
+  ctx_set_renderer (ctx,
+                    ctx_rasterizer_init ( (CtxRasterizer *) malloc (sizeof (CtxRasterizer) ),
+                                          ctx, NULL, &ctx->state,
+                                          buffer->data, 0, 0, buffer->width, buffer->height,
+                                          buffer->stride, buffer->format->pixel_format,
+                                          CTX_ANTIALIAS_DEFAULT));
+  return ctx;
 }
 
-static void
-ctx_GRAYAF_clear_normal (CTX_COMPOSITE_ARGUMENTS)
+Ctx *
+ctx_new_for_framebuffer (void *data, int width, int height,
+                         int stride,
+                         CtxPixelFormat pixel_format)
 {
-  ctx_float_clear_normal (2, rasterizer, dst, src, x0, coverage, count);
+  Ctx *ctx = ctx_new ();
+  CtxRasterizer *r = ctx_rasterizer_init ( (CtxRasterizer *) ctx_calloc (sizeof (CtxRasterizer), 1),
+                                          ctx, NULL, &ctx->state, data, 0, 0, width, height,
+                                          stride, pixel_format, CTX_ANTIALIAS_DEFAULT);
+  ctx_set_renderer (ctx, r);
+  return ctx;
 }
 
-static void
-ctx_GRAYAF_source_copy_normal_color (CTX_COMPOSITE_ARGUMENTS)
+// ctx_new_for_stream (FILE *stream);
+
+#if 0
+CtxRasterizer *ctx_rasterizer_new (void *data, int x, int y, int width, int height,
+                                   int stride, CtxPixelFormat pixel_format)
 {
-  ctx_float_source_copy_normal_color (2, rasterizer, dst, rasterizer->color, x0, coverage, count);
+  CtxState    *state    = (CtxState *) malloc (sizeof (CtxState) );
+  CtxRasterizer *rasterizer = (CtxRasterizer *) malloc (sizeof (CtxRenderer) );
+  ctx_rasterizer_init (rasterizer, state, data, x, y, width, height,
+                       stride, pixel_format, CTX_ANTIALIAS_DEFAULT);
 }
 #endif
 
-static void
-ctx_setup_GRAYAF (CtxRasterizer *rasterizer)
+CtxPixelFormatInfo *ctx_pixel_formats = NULL;
+
+extern CtxPixelFormatInfo ctx_pixel_formats_default[];
+
+
+CtxPixelFormatInfo *
+ctx_pixel_format_info (CtxPixelFormat format)
 {
-  CtxGState *gstate = &rasterizer->state->gstate;
-  int components = 2;
-  rasterizer->fragment = ctx_rasterizer_get_fragment_GRAYAF (rasterizer);
-  if (gstate->source_fill.type == CTX_SOURCE_COLOR)
-    {
-      rasterizer->comp_op = ctx_GRAYAF_porter_duff_color;
-  //  rasterizer->fragment = NULL;
-      ctx_color_get_rgba (rasterizer->state, &gstate->source_fill.color, (float*)rasterizer->color);
-      if (gstate->global_alpha_u8 != 255)
-        for (int c = 0; c < components; c ++)
-          ((float*)rasterizer->color)[c] *= gstate->global_alpha_f;
-    }
-  else
+  if (!ctx_pixel_formats)
   {
-    rasterizer->comp_op = ctx_GRAYAF_porter_duff_generic;
+    ctx_pixel_formats = ctx_pixel_formats_default;
+
   }
 
-#if CTX_INLINED_NORMAL
-  if (gstate->compositing_mode == CTX_COMPOSITE_CLEAR)
-    rasterizer->comp_op = ctx_GRAYAF_clear_normal;
-  else
-    switch (gstate->blend_mode)
+  for (unsigned int i = 0; ctx_pixel_formats[i].pixel_format; i++)
     {
-      case CTX_BLEND_NORMAL:
-        if (gstate->compositing_mode == CTX_COMPOSITE_COPY)
-        {
-          rasterizer->comp_op = ctx_GRAYAF_copy_normal;
-        }
-        else if (gstate->global_alpha_u8 == 0)
-          rasterizer->comp_op = ctx_RGBA8_nop;
-        else
-        switch (gstate->source_fill.type)
-        {
-          case CTX_SOURCE_COLOR:
-            if (gstate->compositing_mode == CTX_COMPOSITE_SOURCE_OVER)
-            {
-              if (((float*)rasterizer->color)[components-1] == 0.0f)
-                rasterizer->comp_op = ctx_RGBA8_nop;
-#if 1
-              else //if (((float*)rasterizer->color)[components-1] == 0.0f)
-                rasterizer->comp_op = ctx_GRAYAF_source_copy_normal_color;
-#endif
-              //else
-          //      rasterizer->comp_op = ctx_GRAYAF_porter_duff_color_normal;
-//            rasterizer->fragment = NULL;
-            }
-            else
-            {
-              rasterizer->comp_op = ctx_GRAYAF_porter_duff_color_normal;
-//            rasterizer->fragment = NULL;
-            }
-            break;
-          default:
-            rasterizer->comp_op = ctx_GRAYAF_porter_duff_generic_normal;
-            break;
-        }
-        break;
-      default:
-        switch (gstate->source_fill.type)
+      if (ctx_pixel_formats[i].pixel_format == format)
         {
-          case CTX_SOURCE_COLOR:
-            rasterizer->comp_op = ctx_GRAYAF_porter_duff_color;
-//          rasterizer->fragment = NULL;
-            break;
-          default:
-            rasterizer->comp_op = ctx_GRAYAF_porter_duff_generic;
-            break;
+          return &ctx_pixel_formats[i];
         }
-        break;
     }
-#endif
+  return NULL;
 }
+#else
 
-#endif
-#if CTX_ENABLE_GRAYF
-
-static void
-ctx_composite_GRAYF (CTX_COMPOSITE_ARGUMENTS)
+CtxPixelFormatInfo *
+ctx_pixel_format_info (CtxPixelFormat format)
 {
-  float *dstf = (float*)dst;
-
-  float temp[count*2];
-  for (int i = 0; i < count; i++)
-  {
-    temp[i*2] = dstf[i];
-    temp[i*2+1] = 1.0f;
-  }
-  rasterizer->comp_op (rasterizer, (uint8_t*)temp, rasterizer->color, x0, coverage, count);
-  for (int i = 0; i < count; i++)
-  {
-    dstf[i] = temp[i*2];
-  }
+  return NULL;
 }
-
 #endif
-#if CTX_ENABLE_BGRA8
-
-inline static void
-ctx_swap_red_green (uint8_t *rgba)
-{
-  uint32_t *buf  = (uint32_t *) rgba;
-  uint32_t  orig = *buf;
-  uint32_t  green_alpha = (orig & 0xff00ff00);
-  uint32_t  red_blue    = (orig & 0x00ff00ff);
-  uint32_t  red         = red_blue << 16;
-  uint32_t  blue        = red_blue >> 16;
-  *buf = green_alpha | red | blue;
-}
 
-static void
-ctx_BGRA8_to_RGBA8 (CtxRasterizer *rasterizer, int x, const void *buf, uint8_t *rgba, int count)
+void
+ctx_current_point (Ctx *ctx, float *x, float *y)
 {
-  uint32_t *srci = (uint32_t *) buf;
-  uint32_t *dsti = (uint32_t *) rgba;
-  while (count--)
+  if (!ctx)
+    { 
+      if (x) { *x = 0.0f; }
+      if (y) { *y = 0.0f; }
+    }
+#if CTX_RASTERIZER
+  if (ctx->renderer)
     {
-      uint32_t val = *srci++;
-      ctx_swap_red_green ( (uint8_t *) &val);
-      *dsti++      = val;
+      if (x) { *x = ( (CtxRasterizer *) (ctx->renderer) )->x; }
+      if (y) { *y = ( (CtxRasterizer *) (ctx->renderer) )->y; }
+      return;
     }
+#endif
+  if (x) { *x = ctx->state.x; }
+  if (y) { *y = ctx->state.y; }
 }
 
-static void
-ctx_RGBA8_to_BGRA8 (CtxRasterizer *rasterizer, int x, const uint8_t *rgba, void *buf, int count)
+float ctx_x (Ctx *ctx)
 {
-  ctx_BGRA8_to_RGBA8 (rasterizer, x, rgba, (uint8_t *) buf, count);
+  float x = 0, y = 0;
+  ctx_current_point (ctx, &x, &y);
+  return x;
 }
 
-static void
-ctx_composite_BGRA8 (CTX_COMPOSITE_ARGUMENTS)
+float ctx_y (Ctx *ctx)
 {
-  // for better performance, this could be done without a pre/post conversion,
-  // by swapping R and B of source instead... as long as it is a color instead
-  // of gradient or image
-  //
-  //
-  uint8_t pixels[count * 4];
-  ctx_BGRA8_to_RGBA8 (rasterizer, x0, dst, &pixels[0], count);
-  rasterizer->comp_op (rasterizer, &pixels[0], rasterizer->color, x0, coverage, count);
-  ctx_BGRA8_to_RGBA8  (rasterizer, x0, &pixels[0], dst, count);
+  float x = 0, y = 0;
+  ctx_current_point (ctx, &x, &y);
+  return y;
 }
 
-
-#endif
-#if CTX_ENABLE_CMYKAF
-
-static void
-ctx_fragment_other_CMYKAF (CtxRasterizer *rasterizer, float x, float y, void *out, int count, float dx, 
float dy)
+void
+ctx_process (Ctx *ctx, CtxEntry *entry)
 {
-  float *cmyka = (float*)out;
-  float _rgba[4 * count];
-  float *rgba = &_rgba[0];
-  CtxGState *gstate = &rasterizer->state->gstate;
-  switch (gstate->source_fill.type)
+#if CTX_CURRENT_PATH
+  switch (entry->code)
     {
-      case CTX_SOURCE_TEXTURE:
-        ctx_fragment_image_RGBAF (rasterizer, x, y, rgba, count, dx, dy);
-        break;
-      case CTX_SOURCE_COLOR:
-        ctx_fragment_color_RGBAF (rasterizer, x, y, rgba, count, dx, dy);
+      case CTX_TEXT:
+      case CTX_STROKE_TEXT:
+      case CTX_BEGIN_PATH:
+        ctx->current_path.count = 0;
         break;
-#if CTX_GRADIENTS
-      case CTX_SOURCE_LINEAR_GRADIENT:
-        ctx_fragment_linear_gradient_RGBAF (rasterizer, x, y, rgba, count, dx, dy);
+      case CTX_CLIP:
+      case CTX_FILL:
+      case CTX_STROKE:
+              // XXX unless preserve
+        ctx->current_path.count = 0;
         break;
-      case CTX_SOURCE_RADIAL_GRADIENT:
-        ctx_fragment_radial_gradient_RGBAF (rasterizer, x, y, rgba, count, dx, dy);
+      case CTX_CLOSE_PATH:
+      case CTX_LINE_TO:
+      case CTX_MOVE_TO:
+      case CTX_QUAD_TO:
+      case CTX_SMOOTH_TO:
+      case CTX_SMOOTHQ_TO:
+      case CTX_REL_QUAD_TO:
+      case CTX_REL_SMOOTH_TO:
+      case CTX_REL_SMOOTHQ_TO:
+      case CTX_CURVE_TO:
+      case CTX_REL_CURVE_TO:
+      case CTX_ARC:
+      case CTX_ARC_TO:
+      case CTX_REL_ARC_TO:
+      case CTX_RECTANGLE:
+      case CTX_ROUND_RECTANGLE:
+        ctx_drawlist_add_entry (&ctx->current_path, entry);
         break;
-#endif
       default:
-        rgba[0]=rgba[1]=rgba[2]=rgba[3]=0.0f;
         break;
     }
-  for (int i = 0; i < count; i++)
-  {
-    cmyka[4]=rgba[3];
-    ctx_rgb_to_cmyk (rgba[0], rgba[1], rgba[2], &cmyka[0], &cmyka[1], &cmyka[2], &cmyka[3]);
-    cmyka += 5;
-    rgba += 4;
-  }
-}
-
-static void
-ctx_fragment_color_CMYKAF (CtxRasterizer *rasterizer, float x, float y, void *out, int count, float dx, 
float dy)
-{
-  CtxGState *gstate = &rasterizer->state->gstate;
-  float *cmyka = (float*)out;
-  float cmyka_in[5];
-  ctx_color_get_cmyka (rasterizer->state, &gstate->source_fill.color, cmyka_in);
-  for (int i = 0; i < count; i++)
-  {
-    for (int c = 0; c < 4; c ++)
+#endif
+  if (ctx->renderer && ctx->renderer->process)
     {
-      cmyka[c] = (1.0f - cmyka_in[c]);
+      ctx->renderer->process (ctx->renderer, (CtxCommand *) entry);
     }
-    cmyka[4] = cmyka_in[4];
-    cmyka += 5;
-  }
-}
-
-static CtxFragment ctx_rasterizer_get_fragment_CMYKAF (CtxRasterizer *rasterizer)
-{
-  CtxGState *gstate = &rasterizer->state->gstate;
-  switch (gstate->source_fill.type)
+  else
     {
-      case CTX_SOURCE_COLOR:
-        return ctx_fragment_color_CMYKAF;
+      /* these functions might alter the code and coordinates of
+         command that in the end gets added to the drawlist
+       */
+      ctx_interpret_style (&ctx->state, entry, ctx);
+      ctx_interpret_transforms (&ctx->state, entry, ctx);
+      ctx_interpret_pos (&ctx->state, entry, ctx);
+      ctx_drawlist_add_entry (&ctx->drawlist, entry);
     }
-  return ctx_fragment_other_CMYKAF;
 }
 
-ctx_float_porter_duff (CMYKAF, 5,color,           rasterizer->fragment, rasterizer->state->gstate.blend_mode)
-ctx_float_porter_duff (CMYKAF, 5,generic,         rasterizer->fragment, rasterizer->state->gstate.blend_mode)
 
-#if CTX_INLINED_NORMAL
-ctx_float_porter_duff (CMYKAF, 5,color_normal,            rasterizer->fragment, CTX_BLEND_NORMAL)
-ctx_float_porter_duff (CMYKAF, 5,generic_normal,          rasterizer->fragment, CTX_BLEND_NORMAL)
+int ctx_gradient_cache_valid = 0;
 
-static void
-ctx_CMYKAF_copy_normal (CTX_COMPOSITE_ARGUMENTS)
+void
+ctx_state_gradient_clear_stops (CtxState *state)
 {
-  ctx_float_copy_normal (5, rasterizer, dst, src, x0, coverage, count);
+//#if CTX_GRADIENT_CACHE
+//  ctx_gradient_cache_reset ();
+//#endif
+  ctx_gradient_cache_valid = 0;
+  state->gradient.n_stops = 0;
 }
 
-static void
-ctx_CMYKAF_clear_normal (CTX_COMPOSITE_ARGUMENTS)
-{
-  ctx_float_clear_normal (5, rasterizer, dst, src, x0, coverage, count);
-}
+uint8_t ctx_gradient_cache_u8[CTX_GRADIENT_CACHE_ELEMENTS][4];
+uint8_t ctx_gradient_cache_u8_a[CTX_GRADIENT_CACHE_ELEMENTS][4];
 
-static void
-ctx_CMYKAF_source_copy_normal_color (CTX_COMPOSITE_ARGUMENTS)
+/****  end of engine ****/
+
+CtxBuffer *ctx_buffer_new_bare (void)
 {
-  ctx_float_source_copy_normal_color (5, rasterizer, dst, rasterizer->color, x0, coverage, count);
+  CtxBuffer *buffer = (CtxBuffer *) ctx_calloc (sizeof (CtxBuffer), 1);
+  return buffer;
 }
-#endif
 
-static void
-ctx_setup_CMYKAF (CtxRasterizer *rasterizer)
+void ctx_buffer_set_data (CtxBuffer *buffer,
+                          void *data, int width, int height,
+                          int stride,
+                          CtxPixelFormat pixel_format,
+                          void (*freefunc) (void *pixels, void *user_data),
+                          void *user_data)
 {
-  CtxGState *gstate = &rasterizer->state->gstate;
-  int components = 5;
-  rasterizer->fragment = ctx_rasterizer_get_fragment_CMYKAF (rasterizer);
-  if (gstate->source_fill.type == CTX_SOURCE_COLOR)
-    {
-      rasterizer->comp_op = ctx_CMYKAF_porter_duff_color;
-      rasterizer->comp_op = ctx_CMYKAF_porter_duff_generic;
- //     rasterizer->fragment = NULL;
-      ctx_color_get_cmyka (rasterizer->state, &gstate->source_fill.color, (float*)rasterizer->color);
-      if (gstate->global_alpha_u8 != 255)
-        ((float*)rasterizer->color)[components-1] *= gstate->global_alpha_f;
-    }
-  else
-  {
-    rasterizer->comp_op = ctx_CMYKAF_porter_duff_generic;
-  }
-
-#if CTX_INLINED_NORMAL
-  if (gstate->compositing_mode == CTX_COMPOSITE_CLEAR)
-    rasterizer->comp_op = ctx_CMYKAF_clear_normal;
-#if 1
-  else
-    switch (gstate->blend_mode)
-    {
-      case CTX_BLEND_NORMAL:
-        if (gstate->compositing_mode == CTX_COMPOSITE_COPY)
-        {
-          rasterizer->comp_op = ctx_CMYKAF_copy_normal;
-        }
-        else if (gstate->global_alpha_u8 == 0)
-          rasterizer->comp_op = ctx_RGBA8_nop;
-        else
-        switch (gstate->source_fill.type)
-        {
-          case CTX_SOURCE_COLOR:
-            if (gstate->compositing_mode == CTX_COMPOSITE_SOURCE_OVER)
-            {
-              if (((float*)rasterizer->color)[components-1] == 0.0f)
-                rasterizer->comp_op = ctx_RGBA8_nop;
-#if 1
-              else //if (((float*)rasterizer->color)[components-1] == 1.0f)
-                rasterizer->comp_op = ctx_CMYKAF_source_copy_normal_color;
-   //           else
-   //             rasterizer->comp_op = ctx_CMYKAF_porter_duff_color_normal;
-              rasterizer->fragment = NULL;
-#endif
-            }
-            else
-            {
-              rasterizer->comp_op = ctx_CMYKAF_porter_duff_color_normal;
-   //         rasterizer->fragment = NULL;
-            }
-            break;
-          default:
-            rasterizer->comp_op = ctx_CMYKAF_porter_duff_generic_normal;
-            break;
-        }
-        break;
-      default:
-        switch (gstate->source_fill.type)
-        {
-          case CTX_SOURCE_COLOR:
-            rasterizer->comp_op = ctx_CMYKAF_porter_duff_color;
-    //      rasterizer->fragment = NULL;
-            break;
-          default:
-            rasterizer->comp_op = ctx_CMYKAF_porter_duff_generic;
-            break;
-        }
-        break;
-    }
-#endif
-#endif
+  if (buffer->free_func)
+    { buffer->free_func (buffer->data, buffer->user_data); }
+  if (stride <= 0)
+    stride = ctx_pixel_format_get_stride (pixel_format, width);
+  buffer->data      = data;
+  buffer->width     = width;
+  buffer->height    = height;
+  buffer->stride    = stride;
+  buffer->format    = ctx_pixel_format_info (pixel_format);
+  buffer->free_func = freefunc;
+  buffer->user_data = user_data;
 }
 
-#endif
-#if CTX_ENABLE_CMYKA8
-
-static void
-ctx_CMYKA8_to_CMYKAF (CtxRasterizer *rasterizer, uint8_t *src, float *dst, int count)
-{
-  for (int i = 0; i < count; i ++)
-    {
-      for (int c = 0; c < 4; c ++)
-        { dst[c] = ctx_u8_to_float ( (255-src[c]) ); }
-      dst[4] = ctx_u8_to_float (src[4]);
-      for (int c = 0; c < 4; c++)
-        { dst[c] *= dst[4]; }
-      src += 5;
-      dst += 5;
-    }
-}
-static void
-ctx_CMYKAF_to_CMYKA8 (CtxRasterizer *rasterizer, float *src, uint8_t *dst, int count)
+CtxBuffer *ctx_buffer_new_for_data (void *data, int width, int height,
+                                    int stride,
+                                    CtxPixelFormat pixel_format,
+                                    void (*freefunc) (void *pixels, void *user_data),
+                                    void *user_data)
 {
-  for (int i = 0; i < count; i ++)
-    {
-      int a = ctx_float_to_u8 (src[4]);
-      if (a != 0 && a != 255)
-      {
-        float recip = 1.0f/src[4];
-        for (int c = 0; c < 4; c++)
-        {
-          dst[c] = ctx_float_to_u8 (1.0f - src[c] * recip);
-        }
-      }
-      else
-      {
-        for (int c = 0; c < 4; c++)
-          dst[c] = 255 - ctx_float_to_u8 (src[c]);
-      }
-      dst[4]=a;
-
-      src += 5;
-      dst += 5;
-    }
+  CtxBuffer *buffer = ctx_buffer_new_bare ();
+  ctx_buffer_set_data (buffer, data, width, height, stride, pixel_format,
+                       freefunc, user_data);
+  return buffer;
 }
 
-static void
-ctx_composite_CMYKA8 (CTX_COMPOSITE_ARGUMENTS)
+void ctx_buffer_pixels_free (void *pixels, void *userdata)
 {
-  float pixels[count * 5];
-  ctx_CMYKA8_to_CMYKAF (rasterizer, dst, &pixels[0], count);
-  rasterizer->comp_op (rasterizer, (uint8_t *) &pixels[0], rasterizer->color, x0, coverage, count);
-  ctx_CMYKAF_to_CMYKA8 (rasterizer, &pixels[0], dst, count);
+  free (pixels);
 }
 
-#endif
-#if CTX_ENABLE_CMYK8
-
-static void
-ctx_CMYK8_to_CMYKAF (CtxRasterizer *rasterizer, uint8_t *src, float *dst, int count)
+CtxBuffer *ctx_buffer_new (int width, int height,
+                           CtxPixelFormat pixel_format)
 {
-  for (int i = 0; i < count; i ++)
-    {
-      dst[0] = ctx_u8_to_float (255-src[0]);
-      dst[1] = ctx_u8_to_float (255-src[1]);
-      dst[2] = ctx_u8_to_float (255-src[2]);
-      dst[3] = ctx_u8_to_float (255-src[3]);
-      dst[4] = 1.0f;
-      src += 4;
-      dst += 5;
-    }
+  //CtxPixelFormatInfo *info = ctx_pixel_format_info (pixel_format);
+  CtxBuffer *buffer = ctx_buffer_new_bare ();
+  int stride = ctx_pixel_format_get_stride (pixel_format, width);
+  uint8_t *pixels = (uint8_t*)ctx_calloc (stride, height + 1);
+
+  ctx_buffer_set_data (buffer, pixels, width, height, stride, pixel_format,
+                       ctx_buffer_pixels_free, NULL);
+  return buffer;
 }
-static void
-ctx_CMYKAF_to_CMYK8 (CtxRasterizer *rasterizer, float *src, uint8_t *dst, int count)
+
+void ctx_buffer_deinit (CtxBuffer *buffer)
 {
-  for (int i = 0; i < count; i ++)
+  if (buffer->free_func)
+    buffer->free_func (buffer->data, buffer->user_data);
+  if (buffer->eid)
+  {
+    free (buffer->eid);
+  }
+  buffer->eid = NULL;
+  buffer->data = NULL;
+  buffer->free_func = NULL;
+  buffer->user_data  = NULL;
+  if (buffer->color_managed)
+  {
+    if (buffer->color_managed != buffer)
     {
-      float c = src[0];
-      float m = src[1];
-      float y = src[2];
-      float k = src[3];
-      float a = src[4];
-      if (a != 0.0f && a != 1.0f)
-        {
-          float recip = 1.0f/a;
-          c *= recip;
-          m *= recip;
-          y *= recip;
-          k *= recip;
-        }
-      c = 1.0 - c;
-      m = 1.0 - m;
-      y = 1.0 - y;
-      k = 1.0 - k;
-      dst[0] = ctx_float_to_u8 (c);
-      dst[1] = ctx_float_to_u8 (m);
-      dst[2] = ctx_float_to_u8 (y);
-      dst[3] = ctx_float_to_u8 (k);
-      src += 5;
-      dst += 4;
+      ctx_buffer_free (buffer->color_managed);
     }
+    buffer->color_managed = NULL;
+  }
 }
 
-static void
-ctx_composite_CMYK8 (CTX_COMPOSITE_ARGUMENTS)
+void ctx_buffer_free (CtxBuffer *buffer)
 {
-  float pixels[count * 5];
-  ctx_CMYK8_to_CMYKAF (rasterizer, dst, &pixels[0], count);
-  rasterizer->comp_op (rasterizer, (uint8_t *) &pixels[0], src, x0, coverage, count);
-  ctx_CMYKAF_to_CMYK8 (rasterizer, &pixels[0], dst, count);
+  ctx_buffer_deinit (buffer);
+  free (buffer);
 }
-#endif
-
-#if CTX_ENABLE_RGB8
 
-inline static void
-ctx_RGB8_to_RGBA8 (CtxRasterizer *rasterizer, int x, const void *buf, uint8_t *rgba, int count)
+static int
+ctx_texture_check_eid (Ctx *ctx, const char *eid, int *tw, int *th)
 {
-  const uint8_t *pixel = (const uint8_t *) buf;
-  while (count--)
+  for (int i = 0; i <  CTX_MAX_TEXTURES; i++)
+  {
+    if (ctx->texture[i].data &&
+        ctx->texture[i].eid  &&
+        !strcmp (ctx->texture[i].eid, eid))
     {
-      rgba[0] = pixel[0];
-      rgba[1] = pixel[1];
-      rgba[2] = pixel[2];
-      rgba[3] = 255;
-      pixel+=3;
-      rgba +=4;
+      if (tw) *tw = ctx->texture[i].width;
+      if (th) *th = ctx->texture[i].height;
+      ctx->texture[i].frame = ctx->texture_cache->frame;
+      return i;
     }
+  }
+  return -1;
 }
 
-inline static void
-ctx_RGBA8_to_RGB8 (CtxRasterizer *rasterizer, int x, const uint8_t *rgba, void *buf, int count)
+const char* ctx_texture_init (Ctx           *ctx,
+                              const char    *eid,
+                              int            width,
+                              int            height,
+                              int            stride,
+                              CtxPixelFormat format,
+                              void          *space,
+                              uint8_t       *pixels,
+                              void (*freefunc) (void *pixels, void *user_data),
+                              void *user_data)
 {
-  uint8_t *pixel = (uint8_t *) buf;
-  while (count--)
+  int id = 0;
+  if (eid)
+  {
+    for (int i = 0; i <  CTX_MAX_TEXTURES; i++)
     {
-      pixel[0] = rgba[0];
-      pixel[1] = rgba[1];
-      pixel[2] = rgba[2];
-      pixel+=3;
-      rgba +=4;
+      if (ctx->texture[i].data &&
+          ctx->texture[i].eid &&
+          !strcmp (ctx->texture[i].eid, eid))
+      {
+        ctx->texture[i].frame = ctx->texture_cache->frame;
+        if (freefunc && user_data != (void*)23)
+          freefunc (pixels, user_data);
+        return ctx->texture[i].eid;
+      }
+      if (ctx->texture[i].data == NULL 
+          ||   (ctx->texture_cache->frame - ctx->texture[i].frame >= 2))
+        id = i;
     }
-}
+  } else
+  {
+    for (int i = 0; i <  CTX_MAX_TEXTURES; i++)
+    {
+      if (ctx->texture[i].data == NULL 
+          || (ctx->texture_cache->frame - ctx->texture[i].frame > 2))
+        id = i;
+    }
+  }
+  //int bpp = ctx_pixel_format_bits_per_pixel (format);
+  ctx_buffer_deinit (&ctx->texture[id]);
+
+  if (stride<=0)
+  {
+    stride = ctx_pixel_format_get_stride ((CtxPixelFormat)format, width);
+  }
 
+  if (freefunc == ctx_buffer_pixels_free && user_data == (void*)23)
+  {
+     uint8_t *tmp = (uint8_t*)malloc (height * stride);
+     memcpy (tmp, pixels, height * stride);
+     pixels = tmp;
+  }
+
+  ctx_buffer_set_data (&ctx->texture[id],
+                       pixels, width, height,
+                       stride, format,
+                       freefunc, user_data);
+#if CTX_ENABLE_CM
+  ctx->texture[id].space = space;
 #endif
-#if CTX_ENABLE_GRAY1
+  ctx->texture[id].frame = ctx->texture_cache->frame;
+  if (eid)
+  {
+    /* we got an eid, this is the fast path */
+    ctx->texture[id].eid = strdup (eid);
+  }
+  else
+  {
+    uint8_t hash[20];
+    char ascii[41];
 
-#if CTX_NATIVE_GRAYA8
-inline static void
-ctx_GRAY1_to_GRAYA8 (CtxRasterizer *rasterizer, int x, const void *buf, uint8_t *rgba, int count)
-{
-  const uint8_t *pixel = (uint8_t *) buf;
-  while (count--)
+    CtxSHA1 *sha1 = ctx_sha1_new ();
+    ctx_sha1_process (sha1, pixels, stride * height);
+    ctx_sha1_done (sha1, hash);
+    ctx_sha1_free (sha1);
+    const char *hex="0123456789abcdef";
+    for (int i = 0; i < 20; i ++)
     {
-      rgba[0] = 255 * (*pixel & (1<< (x&7) ) );
-      rgba[1] = 255;
-      pixel+= ( (x&7) ==7);
-      x++;
-      rgba +=2;
+       ascii[i*2]=hex[hash[i]/16];
+       ascii[i*2+1]=hex[hash[i]%16];
     }
+    ascii[40]=0;
+    ctx->texture[id].eid = strdup (ascii);
+  }
+  return ctx->texture[id].eid;
 }
 
-inline static void
-ctx_GRAYA8_to_GRAY1 (CtxRasterizer *rasterizer, int x, const uint8_t *rgba, void *buf, int count)
+void
+_ctx_texture_prepare_color_management (CtxRasterizer *rasterizer,
+                                      CtxBuffer     *buffer)
 {
-  uint8_t *pixel = (uint8_t *) buf;
-  *pixel = 0;
-  while (count--)
-    {
-      int gray = rgba[0];
-      //gray += ctx_dither_mask_a (x, rasterizer->scanline/aa, 0, 127);
-      if (gray >= 127)
+   switch (buffer->format->pixel_format)
+   {
+#ifndef NO_BABL
+#if CTX_BABL
+     case CTX_FORMAT_RGBA8:
         {
-          *pixel = *pixel | ((1<< (x&7) ) * (gray >= 127));
-        }
-#if 0
-      else
-      {
-          *pixel = *pixel & (~ (1<< (x&7) ) );
-      }
+          buffer->color_managed = ctx_buffer_new (buffer->width, buffer->height,
+                                                  CTX_FORMAT_RGBA8);
+          babl_process (
+             babl_fish (babl_format_with_space ("R'G'B'A u8", buffer->space),
+                        babl_format_with_space ("R'G'B'A u8", rasterizer->state->gstate.device_space)),
+             buffer->data, buffer->color_managed->data,
+             buffer->width * buffer->height
+             );
+       }
+       break;
+     case CTX_FORMAT_RGB8:
+       {
+       buffer->color_managed = ctx_buffer_new (buffer->width, buffer->height,
+                                               CTX_FORMAT_RGB8);
+          babl_process (
+             babl_fish (babl_format_with_space ("R'G'B' u8", buffer->space),
+                        babl_format_with_space ("R'G'B' u8", rasterizer->state->gstate.device_space)),
+             buffer->data, buffer->color_managed->data,
+             buffer->width * buffer->height
+             );
+       }
+       break;
 #endif
-      if ( (x&7) ==7)
-        { pixel+=1;
-          if(count>0)*pixel = 0;
-        }
-      x++;
-      rgba +=2;
-    }
+#endif
+     default:
+       buffer->color_managed = buffer;
+   }
 }
 
-#else
 
-inline static void
-ctx_GRAY1_to_RGBA8 (CtxRasterizer *rasterizer, int x, const void *buf, uint8_t *rgba, int count)
-{
-  const uint8_t *pixel = (uint8_t *) buf;
-  while (count--)
-    {
-      *((uint32_t*)(rgba))=0xff000000 + 0x00ffffff * ((*pixel & (1<< (x&7) ) )!=0);
-      pixel+= ( (x&7) ==7);
-      x++;
-      rgba +=4;
-    }
-}
 
-inline static void
-ctx_RGBA8_to_GRAY1 (CtxRasterizer *rasterizer, int x, const uint8_t *rgba, void *buf, int count)
+int ctx_utf8_len (const unsigned char first_byte)
 {
-  uint8_t *pixel = (uint8_t *) buf;
-  *pixel = 0;
-  while (count--)
-    {
-      int gray = ctx_u8_color_rgb_to_gray (rasterizer->state, rgba);
-      //gray += ctx_dither_mask_a (x, rasterizer->scanline/aa, 0, 127);
-      if (gray <= 127)
-        {
-          //*pixel = *pixel & (~ (1<< (x&7) ) );
-        }
-      else
-        {
-          *pixel = *pixel | (1<< (x&7) );
-        }
-      if ( (x&7) ==7)
-        { pixel+=1;
-          if(count>0)*pixel = 0;
-        }
-      x++;
-      rgba +=4;
-    }
+  if      ( (first_byte & 0x80) == 0)
+    { return 1; } /* ASCII */
+  else if ( (first_byte & 0xE0) == 0xC0)
+    { return 2; }
+  else if ( (first_byte & 0xF0) == 0xE0)
+    { return 3; }
+  else if ( (first_byte & 0xF8) == 0xF0)
+    { return 4; }
+  return 1;
 }
-#endif
 
-#endif
-#if CTX_ENABLE_GRAY2
 
-#if CTX_NATIVE_GRAYA8
-inline static void
-ctx_GRAY2_to_GRAYA8 (CtxRasterizer *rasterizer, int x, const void *buf, uint8_t *rgba, int count)
+const char *ctx_utf8_skip (const char *s, int utf8_length)
 {
-  const uint8_t *pixel = (uint8_t *) buf;
-  while (count--)
+  int count;
+  if (!s)
+    { return NULL; }
+  for (count = 0; *s; s++)
     {
-      int val = (*pixel & (3 << ( (x & 3) <<1) ) ) >> ( (x&3) <<1);
-      val <<= 6;
-      rgba[0] = val;
-      rgba[1] = 255;
-      if ( (x&3) ==3)
-        { pixel+=1; }
-      x++;
-      rgba +=2;
+      if ( (*s & 0xC0) != 0x80)
+        { count++; }
+      if (count == utf8_length + 1)
+        { return s; }
     }
+  return s;
 }
 
-inline static void
-ctx_GRAYA8_to_GRAY2 (CtxRasterizer *rasterizer, int x, const uint8_t *rgba, void *buf, int count)
+//  XXX  :  unused
+int ctx_utf8_strlen (const char *s)
 {
-  uint8_t *pixel = (uint8_t *) buf;
-  while (count--)
-    {
-      int val = rgba[0];
-      val >>= 6;
-      *pixel = *pixel & (~ (3 << ( (x&3) <<1) ) );
-      *pixel = *pixel | ( (val << ( (x&3) <<1) ) );
-      if ( (x&3) ==3)
-        { pixel+=1; }
-      x++;
-      rgba +=2;
-    }
+  int count;
+  if (!s)
+    { return 0; }
+  for (count = 0; *s; s++)
+    if ( (*s & 0xC0) != 0x80)
+      { count++; }
+  return count;
 }
-#else
 
-inline static void
-ctx_GRAY2_to_RGBA8 (CtxRasterizer *rasterizer, int x, const void *buf, uint8_t *rgba, int count)
+int
+ctx_unichar_to_utf8 (uint32_t  ch,
+                     uint8_t  *dest)
 {
-  const uint8_t *pixel = (uint8_t *) buf;
-  while (count--)
+  /* http://www.cprogramming.com/tutorial/utf8.c  */
+  /*  Basic UTF-8 manipulation routines
+    by Jeff Bezanson
+    placed in the public domain Fall 2005 ... */
+  if (ch < 0x80)
     {
-      int val = (*pixel & (3 << ( (x & 3) <<1) ) ) >> ( (x&3) <<1);
-      val <<= 6;
-      rgba[0] = val;
-      rgba[1] = val;
-      rgba[2] = val;
-      rgba[3] = 255;
-      if ( (x&3) ==3)
-        { pixel+=1; }
-      x++;
-      rgba +=4;
+      dest[0] = (char) ch;
+      return 1;
     }
-}
-
-inline static void
-ctx_RGBA8_to_GRAY2 (CtxRasterizer *rasterizer, int x, const uint8_t *rgba, void *buf, int count)
-{
-  uint8_t *pixel = (uint8_t *) buf;
-  while (count--)
+  if (ch < 0x800)
     {
-      int val = ctx_u8_color_rgb_to_gray (rasterizer->state, rgba);
-      val >>= 6;
-      *pixel = *pixel & (~ (3 << ( (x&3) <<1) ) );
-      *pixel = *pixel | ( (val << ( (x&3) <<1) ) );
-      if ( (x&3) ==3)
-        { pixel+=1; }
-      x++;
-      rgba +=4;
+      dest[0] = (ch>>6) | 0xC0;
+      dest[1] = (ch & 0x3F) | 0x80;
+      return 2;
     }
-}
-#endif
-
-#endif
-#if CTX_ENABLE_GRAY4
-
-#if CTX_NATIVE_GRAYA8
-inline static void
-ctx_GRAY4_to_GRAYA8 (CtxRasterizer *rasterizer, int x, const void *buf, uint8_t *rgba, int count)
-{
-  const uint8_t *pixel = (uint8_t *) buf;
-  while (count--)
-    {
-      int val = (*pixel & (15 << ( (x & 1) <<2) ) ) >> ( (x&1) <<2);
-      val <<= 4;
-      rgba[0] = val;
-      rgba[1] = 255;
-      if ( (x&1) ==1)
-        { pixel+=1; }
-      x++;
-      rgba +=2;
-    }
-}
-
-inline static void
-ctx_GRAYA8_to_GRAY4 (CtxRasterizer *rasterizer, int x, const uint8_t *rgba, void *buf, int count)
-{
-  uint8_t *pixel = (uint8_t *) buf;
-  while (count--)
+  if (ch < 0x10000)
     {
-      int val = rgba[0];
-      val >>= 4;
-      *pixel = *pixel & (~ (15 << ( (x&1) <<2) ) );
-      *pixel = *pixel | ( (val << ( (x&1) <<2) ) );
-      if ( (x&1) ==1)
-        { pixel+=1; }
-      x++;
-      rgba +=2;
+      dest[0] = (ch>>12) | 0xE0;
+      dest[1] = ( (ch>>6) & 0x3F) | 0x80;
+      dest[2] = (ch & 0x3F) | 0x80;
+      return 3;
     }
-}
-#else
-inline static void
-ctx_GRAY4_to_RGBA8 (CtxRasterizer *rasterizer, int x, const void *buf, uint8_t *rgba, int count)
-{
-  const uint8_t *pixel = (uint8_t *) buf;
-  while (count--)
+  if (ch < 0x110000)
     {
-      int val = (*pixel & (15 << ( (x & 1) <<2) ) ) >> ( (x&1) <<2);
-      val <<= 4;
-      rgba[0] = val;
-      rgba[1] = val;
-      rgba[2] = val;
-      rgba[3] = 255;
-      if ( (x&1) ==1)
-        { pixel+=1; }
-      x++;
-      rgba +=4;
+      dest[0] = (ch>>18) | 0xF0;
+      dest[1] = ( (ch>>12) & 0x3F) | 0x80;
+      dest[2] = ( (ch>>6) & 0x3F) | 0x80;
+      dest[3] = (ch & 0x3F) | 0x80;
+      return 4;
     }
+  return 0;
 }
 
-inline static void
-ctx_RGBA8_to_GRAY4 (CtxRasterizer *rasterizer, int x, const uint8_t *rgba, void *buf, int count)
+uint32_t
+ctx_utf8_to_unichar (const char *input)
 {
-  uint8_t *pixel = (uint8_t *) buf;
-  while (count--)
-    {
-      int val = ctx_u8_color_rgb_to_gray (rasterizer->state, rgba);
-      val >>= 4;
-      *pixel = *pixel & (~ (15 << ( (x&1) <<2) ) );
-      *pixel = *pixel | ( (val << ( (x&1) <<2) ) );
-      if ( (x&1) ==1)
-        { pixel+=1; }
-      x++;
-      rgba +=4;
-    }
+  const uint8_t *utf8 = (const uint8_t *) input;
+  uint8_t c = utf8[0];
+  if ( (c & 0x80) == 0)
+    { return c; }
+  else if ( (c & 0xE0) == 0xC0)
+    return ( (utf8[0] & 0x1F) << 6) |
+           (utf8[1] & 0x3F);
+  else if ( (c & 0xF0) == 0xE0)
+    return ( (utf8[0] & 0xF)  << 12) |
+           ( (utf8[1] & 0x3F) << 6) |
+           (utf8[2] & 0x3F);
+  else if ( (c & 0xF8) == 0xF0)
+    return ( (utf8[0] & 0x7)  << 18) |
+           ( (utf8[1] & 0x3F) << 12) |
+           ( (utf8[2] & 0x3F) << 6) |
+           (utf8[3] & 0x3F);
+  else if ( (c & 0xFC) == 0xF8)
+    return ( (utf8[0] & 0x3)  << 24) |
+           ( (utf8[1] & 0x3F) << 18) |
+           ( (utf8[2] & 0x3F) << 12) |
+           ( (utf8[3] & 0x3F) << 6) |
+           (utf8[4] & 0x3F);
+  else if ( (c & 0xFE) == 0xFC)
+    return ( (utf8[0] & 0x1)  << 30) |
+           ( (utf8[1] & 0x3F) << 24) |
+           ( (utf8[2] & 0x3F) << 18) |
+           ( (utf8[3] & 0x3F) << 12) |
+           ( (utf8[4] & 0x3F) << 6) |
+           (utf8[5] & 0x3F);
+  return 0;
 }
-#endif
 
-#endif
-#if CTX_ENABLE_GRAY8
+#if CTX_RASTERIZER
 
-#if CTX_NATIVE_GRAYA8
-inline static void
-ctx_GRAY8_to_GRAYA8 (CtxRasterizer *rasterizer, int x, const void *buf, uint8_t *rgba, int count)
-{
-  const uint8_t *pixel = (uint8_t *) buf;
-  while (count--)
-    {
-      rgba[0] = pixel[0];
-      rgba[1] = 255;
-      pixel+=1;
-      rgba +=2;
-    }
-}
 
-inline static void
-ctx_GRAYA8_to_GRAY8 (CtxRasterizer *rasterizer, int x, const uint8_t *rgba, void *buf, int count)
-{
-  uint8_t *pixel = (uint8_t *) buf;
-  while (count--)
-    {
-      pixel[0] = rgba[0];
-      pixel+=1;
-      rgba +=2;
-    }
-}
-#else
-inline static void
-ctx_GRAY8_to_RGBA8 (CtxRasterizer *rasterizer, int x, const void *buf, uint8_t *rgba, int count)
-{
-  const uint8_t *pixel = (uint8_t *) buf;
-  while (count--)
-    {
-      rgba[0] = pixel[0];
-      rgba[1] = pixel[0];
-      rgba[2] = pixel[0];
-      rgba[3] = 255;
-      pixel+=1;
-      rgba +=4;
-    }
-}
 
-inline static void
-ctx_RGBA8_to_GRAY8 (CtxRasterizer *rasterizer, int x, const uint8_t *rgba, void *buf, int count)
+static int
+ctx_rect_intersect (const CtxIntRectangle *a, const CtxIntRectangle *b)
 {
-  uint8_t *pixel = (uint8_t *) buf;
-  for (int i = 0; i < count; i ++)
-    {
-      pixel[i] = ctx_u8_color_rgb_to_gray (rasterizer->state, rgba + i * 4);
-    }
-}
-#endif
-
-#endif
-#if CTX_ENABLE_GRAYA8
+  if (a->x >= b->x + b->width ||
+      b->x >= a->x + a->width ||
+      a->y >= b->y + b->height ||
+      b->y >= a->y + a->height) return 0;
 
-inline static void
-ctx_GRAYA8_to_RGBA8 (CtxRasterizer *rasterizer, int x, const void *buf, uint8_t *rgba, int count)
-{
-  const uint8_t *pixel = (const uint8_t *) buf;
-  while (count--)
-    {
-      rgba[0] = pixel[0];
-      rgba[1] = pixel[0];
-      rgba[2] = pixel[0];
-      rgba[3] = pixel[1];
-      pixel+=2;
-      rgba +=4;
-    }
+  return 1;
 }
 
-inline static void
-ctx_RGBA8_to_GRAYA8 (CtxRasterizer *rasterizer, int x, const uint8_t *rgba, void *buf, int count)
+static void
+_ctx_add_hash (CtxHasher *hasher, CtxIntRectangle *shape_rect, char *hash)
 {
-  uint8_t *pixel = (uint8_t *) buf;
-  while (count--)
-    {
-      pixel[0] = ctx_u8_color_rgb_to_gray (rasterizer->state, rgba);
-      pixel[1] = rgba[3];
-      pixel+=2;
-      rgba +=4;
+  CtxIntRectangle rect = {0,0, hasher->rasterizer.blit_width/hasher->cols,
+                            hasher->rasterizer.blit_height/hasher->rows};
+  int hno = 0;
+  for (int row = 0; row < hasher->rows; row++)
+    for (int col = 0; col < hasher->cols; col++, hno++)
+     {
+      rect.x = col * rect.width;
+      rect.y = row * rect.height;
+      if (ctx_rect_intersect (shape_rect, &rect))
+      {
+        int temp = hasher->hashes[(row * hasher->cols + col)  *20 + 0];
+        for (int i = 0; i <19;i++)
+           hasher->hashes[(row * hasher->cols + col)  *20 + i] =
+             hasher->hashes[(row * hasher->cols + col)  *20 + i+1]^
+             hash[i];
+        hasher->hashes[(row * hasher->cols + col)  *20 + 19] =
+                temp ^ hash[19];
+      }
     }
 }
 
-#if CTX_NATIVE_GRAYA8
-CTX_INLINE static void ctx_rgba_to_graya_u8 (CtxState *state, uint8_t *in, uint8_t *out)
-{
-  out[0] = ctx_u8_color_rgb_to_gray (state, in);
-  out[1] = in[3];
-}
 
-#if CTX_GRADIENTS
 static void
-ctx_fragment_linear_gradient_GRAYA8 (CtxRasterizer *rasterizer, float x, float y, void *out, int count, 
float dx, float dy)
+ctx_hasher_process (void *user_data, CtxCommand *command)
 {
-  CtxSource *g = &rasterizer->state->gstate.source_fill;
-        uint8_t *dst = (uint8_t*)out;
-  for (int i = 0; i < count;i ++)
-  {
-  float v = ( ( (g->linear_gradient.dx * x + g->linear_gradient.dy * y) /
-                g->linear_gradient.length) -
-              g->linear_gradient.start) * (g->linear_gradient.rdelta);
-  {
-    uint8_t rgba[4];
-    ctx_fragment_gradient_1d_RGBA8 (rasterizer, v, 1.0, rgba);
-    ctx_rgba_to_graya_u8 (rasterizer->state, rgba, dst);
-   
-  }
+  CtxEntry *entry = &command->entry;
+  CtxRasterizer *rasterizer = (CtxRasterizer *) user_data;
+  CtxHasher *hasher = (CtxHasher*) user_data;
+  CtxState *state = rasterizer->state;
+  CtxCommand *c = (CtxCommand *) entry;
+  int aa = 15;//rasterizer->aa;
+
+  ctx_interpret_pos_bare (rasterizer->state, entry, NULL);
+  ctx_interpret_style (rasterizer->state, entry, NULL);
+
+  switch (c->code)
+    {
+      case CTX_TEXT:
+        {
+          CtxSHA1 sha1;
+          memcpy (&sha1, &hasher->sha1_fill, sizeof (CtxSHA1));
+          char ctx_sha1_hash[20];
+          float width = ctx_text_width (rasterizer->ctx, ctx_arg_string());
 
 
-#if CTX_DITHER
-  ctx_dither_graya_u8 ((uint8_t*)dst, x, y, rasterizer->format->dither_red_blue,
-                      rasterizer->format->dither_green);
-#endif
-  dst += 2;
-  x += dx;
-  y += dy;
-  }
-}
+          float height = ctx_get_font_size (rasterizer->ctx);
+           CtxIntRectangle shape_rect;
+          
+           shape_rect.x=rasterizer->x;
+           shape_rect.y=rasterizer->y - height,
+           shape_rect.width = width;
+           shape_rect.height = height * 2;
+          switch ((int)ctx_state_get (rasterizer->state, CTX_text_align))
+          {
+          case CTX_TEXT_ALIGN_LEFT:
+          case CTX_TEXT_ALIGN_START:
+                  break;
+          case CTX_TEXT_ALIGN_END:
+          case CTX_TEXT_ALIGN_RIGHT:
+           shape_rect.x -= shape_rect.width;
+           break;
+          case CTX_TEXT_ALIGN_CENTER:
+           shape_rect.x -= shape_rect.width/2;
+           break;
+                   // XXX : doesn't take all text-alignments into account
+          }
 
 #if 0
-static void
-ctx_fragment_radial_gradient_RGBA8 (CtxRasterizer *rasterizer, float x, float y, void *out)
-{
-  uint8_t *rgba = (uint8_t *) out;
-  CtxSource *g = &rasterizer->state->gstate.source_fill;
-  float v = (ctx_hypotf (g->radial_gradient.x0 - x, g->radial_gradient.y0 - y) -
-              g->radial_gradient.r0) * (g->radial_gradient.rdelta);
-  ctx_fragment_gradient_1d_RGBA8 (rasterizer, v, 0.0, rgba);
-#if CTX_DITHER
-  ctx_dither_rgba_u8 (rgba, x, y, rasterizer->format->dither_red_blue,
-                      rasterizer->format->dither_green);
+          uint32_t color;
+          ctx_color_get_rgba8 (rasterizer->state, &rasterizer->state->gstate.source_fill.color, 
(uint8_t*)(&color));
 #endif
-}
+          ctx_sha1_process(&sha1, (const unsigned char*)ctx_arg_string(), strlen  (ctx_arg_string()));
+#if 0
+          ctx_sha1_process(&sha1, (unsigned char*)(&rasterizer->state->gstate.transform), sizeof 
(rasterizer->state->gstate.transform));
+          ctx_sha1_process(&sha1, (unsigned char*)&color, 4);
 #endif
+          ctx_sha1_process(&sha1, (unsigned char*)&shape_rect, sizeof (CtxIntRectangle));
+          ctx_sha1_done(&sha1, (unsigned char*)ctx_sha1_hash);
+          _ctx_add_hash (hasher, &shape_rect, ctx_sha1_hash);
 
+          ctx_rasterizer_rel_move_to (rasterizer, width, 0);
+        }
+        ctx_rasterizer_reset (rasterizer);
+        break;
+      case CTX_STROKE_TEXT:
+        {
+          CtxSHA1 sha1;
+          memcpy (&sha1, &hasher->sha1_stroke, sizeof (CtxSHA1));
+          char ctx_sha1_hash[20];
+          float width = ctx_text_width (rasterizer->ctx, ctx_arg_string());
+          float height = ctx_get_font_size (rasterizer->ctx);
 
-static void
-ctx_fragment_radial_gradient_GRAYA8 (CtxRasterizer *rasterizer, float x, float y, void *out, int count, 
float dx, float dy)
-{
-        uint8_t *dst = (uint8_t*)out;
-  for (int i = 0; i < count;i ++)
-  {
-  CtxSource *g = &rasterizer->state->gstate.source_fill;
-  float v = (ctx_hypotf (g->radial_gradient.x0 - x, g->radial_gradient.y0 - y) -
-              g->radial_gradient.r0) * (g->radial_gradient.rdelta);
-  {
-    uint8_t rgba[4];
-    ctx_fragment_gradient_1d_RGBA8 (rasterizer, v, 1.0, rgba);
-    ctx_rgba_to_graya_u8 (rasterizer->state, rgba, dst);
-  }
-#if CTX_DITHER
-  ctx_dither_graya_u8 ((uint8_t*)dst, x, y, rasterizer->format->dither_red_blue,
-                      rasterizer->format->dither_green);
+           CtxIntRectangle shape_rect = {
+              rasterizer->x, rasterizer->y - height,
+              width, height * 2
+           };
+
+#if 0
+          uint32_t color;
+          ctx_color_get_rgba8 (rasterizer->state, &rasterizer->state->gstate.source_stroke.color, 
(uint8_t*)(&color));
 #endif
-  dst += 2;
-  x += dx;
-  y += dy;
-  }
-}
+          ctx_sha1_process(&sha1, (unsigned char*)ctx_arg_string(), strlen  (ctx_arg_string()));
+#if 0
+          ctx_sha1_process(&sha1, (unsigned char*)(&rasterizer->state->gstate.transform), sizeof 
(rasterizer->state->gstate.transform));
+          ctx_sha1_process(&sha1, (unsigned char*)&color, 4);
 #endif
+          ctx_sha1_process(&sha1, (unsigned char*)&shape_rect, sizeof (CtxIntRectangle));
+          ctx_sha1_done(&sha1, (unsigned char*)ctx_sha1_hash);
+          _ctx_add_hash (hasher, &shape_rect, ctx_sha1_hash);
 
-static void
-ctx_fragment_color_GRAYA8 (CtxRasterizer *rasterizer, float x, float y, void *out, int count, float dx, 
float dy)
-{
-  CtxSource *g = &rasterizer->state->gstate.source_fill;
-  uint16_t *dst = (uint16_t*)out;
-  uint16_t pix;
-  ctx_color_get_graya_u8 (rasterizer->state, &g->color, (void*)&pix);
-  for (int i = 0; i <count; i++)
-  {
-    dst[i]=pix;
-  }
-}
+          ctx_rasterizer_rel_move_to (rasterizer, width, 0);
+        }
+        ctx_rasterizer_reset (rasterizer);
+        break;
+      case CTX_GLYPH:
+         {
+          CtxSHA1 sha1;
+          memcpy (&sha1, &hasher->sha1_fill, sizeof (CtxSHA1));
 
-static void ctx_fragment_image_GRAYA8 (CtxRasterizer *rasterizer, float x, float y, void *out, int count, 
float dx, float dy)
-{
-  uint8_t rgba[4*count];
-  CtxGState *gstate = &rasterizer->state->gstate;
-  CtxBuffer *buffer = gstate->source_fill.texture.buffer;
-  switch (buffer->format->bpp)
-    {
-      case 1:  ctx_fragment_image_gray1_RGBA8 (rasterizer, x, y, rgba, count, dx, dy); break;
-      case 24: ctx_fragment_image_rgb8_RGBA8 (rasterizer, x, y, rgba, count, dx, dy);  break;
-      case 32: ctx_fragment_image_rgba8_RGBA8 (rasterizer, x, y, rgba, count, dx, dy); break;
-      default: ctx_fragment_image_RGBA8 (rasterizer, x, y, rgba, count, dx, dy);       break;
-    }
-  for (int i = 0; i < count; i++)
-    ctx_rgba_to_graya_u8 (rasterizer->state, &rgba[i*4], &((uint8_t*)out)[i*2]);
-}
+          char ctx_sha1_hash[20];
+          uint8_t string[8];
+          string[ctx_unichar_to_utf8 (c->u32.a0, string)]=0;
+          float width = ctx_text_width (rasterizer->ctx, (char*)string);
+          float height = ctx_get_font_size (rasterizer->ctx);
 
-static CtxFragment ctx_rasterizer_get_fragment_GRAYA8 (CtxRasterizer *rasterizer)
-{
-  CtxGState *gstate = &rasterizer->state->gstate;
-  switch (gstate->source_fill.type)
-    {
-      case CTX_SOURCE_TEXTURE:           return ctx_fragment_image_GRAYA8;
-      case CTX_SOURCE_COLOR:           return ctx_fragment_color_GRAYA8;
-#if CTX_GRADIENTS
-      case CTX_SOURCE_LINEAR_GRADIENT: return ctx_fragment_linear_gradient_GRAYA8;
-      case CTX_SOURCE_RADIAL_GRADIENT: return ctx_fragment_radial_gradient_GRAYA8;
-#endif
-    }
-  return ctx_fragment_color_GRAYA8;
-}
+          float tx = rasterizer->x;
+          float ty = rasterizer->y;
+          float tw = width;
+          float th = height * 2;
 
-//ctx_u8_porter_duff(GRAYA8, 2,color,   rasterizer->fragment, rasterizer->state->gstate.blend_mode)
-ctx_u8_porter_duff(GRAYA8, 2,generic, rasterizer->fragment, rasterizer->state->gstate.blend_mode)
+          _ctx_user_to_device (rasterizer->state, &tx, &ty);
+          _ctx_user_to_device_distance (rasterizer->state, &tw, &th);
+          CtxIntRectangle shape_rect = {tx,ty-th/2,tw,th};
 
-#if CTX_INLINED_NORMAL
-//ctx_u8_porter_duff(GRAYA8, 2,color_normal,   rasterizer->fragment, CTX_BLEND_NORMAL)
-ctx_u8_porter_duff(GRAYA8, 2,generic_normal, rasterizer->fragment, CTX_BLEND_NORMAL)
 
-static void
-ctx_GRAYA8_copy_normal (CTX_COMPOSITE_ARGUMENTS)
-{
-  ctx_u8_copy_normal (2, rasterizer, dst, src, x0, coverage, count);
-}
+#if 0
+          uint32_t color;
+          ctx_color_get_rgba8 (rasterizer->state, &rasterizer->state->gstate.source_fill.color, 
(uint8_t*)(&color));
+#endif
+          ctx_sha1_process(&sha1, string, strlen ((const char*)string));
+#if 0
+          ctx_sha1_process(&sha1, (unsigned char*)(&rasterizer->state->gstate.transform), sizeof 
(rasterizer->state->gstate.transform));
+          ctx_sha1_process(&sha1, (unsigned char*)&color, 4);
+#endif
+          ctx_sha1_process(&sha1, (unsigned char*)&shape_rect, sizeof (CtxIntRectangle));
+          ctx_sha1_done(&sha1, (unsigned char*)ctx_sha1_hash);
+          _ctx_add_hash (hasher, &shape_rect, ctx_sha1_hash);
 
-static void
-ctx_GRAYA8_clear_normal (CTX_COMPOSITE_ARGUMENTS)
-{
-  ctx_u8_clear_normal (2, rasterizer, dst, src, x0, coverage, count);
-}
+          ctx_rasterizer_rel_move_to (rasterizer, width, 0);
+          ctx_rasterizer_reset (rasterizer);
+         }
+        break;
 
-static void
-ctx_GRAYA8_source_over_normal_color (CTX_COMPOSITE_ARGUMENTS)
-{
-  ctx_u8_source_over_normal_color (2, rasterizer, dst, rasterizer->color, x0, coverage, count);
-}
+      case CTX_FILL:
+        {
+          CtxSHA1 sha1;
+          memcpy (&sha1, &hasher->sha1_fill, sizeof (CtxSHA1));
+          char ctx_sha1_hash[20];
 
-static void
-ctx_GRAYA8_source_copy_normal_color (CTX_COMPOSITE_ARGUMENTS)
-{
-  ctx_u8_source_copy_normal_color (2, rasterizer, dst, rasterizer->color, x0, coverage, count);
-}
-#endif
+          /* we eant this hasher to be as good as possible internally,
+           * since it is also used in the small shapes rasterization
+           * cache
+           */
+        uint64_t hash = ctx_rasterizer_poly_to_hash (rasterizer); // + hasher->salt;
+        CtxIntRectangle shape_rect = {
+          rasterizer->col_min / CTX_SUBDIV - 2,
+          rasterizer->scan_min / aa - 2,
+          3+(rasterizer->col_max - rasterizer->col_min + 1) / CTX_SUBDIV,
+          3+(rasterizer->scan_max - rasterizer->scan_min + 1) / aa
+        };
 
-inline static int
-ctx_is_opaque_color (CtxRasterizer *rasterizer)
-{
-  CtxGState *gstate = &rasterizer->state->gstate;
-  if (gstate->global_alpha_u8 != 255)
-    return 0;
-  if (gstate->source_fill.type == CTX_SOURCE_COLOR)
-  {
-    uint8_t ga[2];
-    ctx_color_get_graya_u8 (rasterizer->state, &gstate->source_fill.color, ga);
-    return ga[1] == 255;
-  }
-  return 0;
-}
+        hash ^= (rasterizer->state->gstate.fill_rule * 23);
 
-static void
-ctx_setup_GRAYA8 (CtxRasterizer *rasterizer)
-{
-  CtxGState *gstate = &rasterizer->state->gstate;
-  int components = 2;
-  rasterizer->fragment = ctx_rasterizer_get_fragment_GRAYA8 (rasterizer);
-  rasterizer->comp_op  = ctx_GRAYA8_porter_duff_generic;
-  if (gstate->source_fill.type == CTX_SOURCE_COLOR)
-    {
-      ctx_fragment_color_GRAYA8 (rasterizer, 0,0, rasterizer->color, 1, 0,0);
-      if (gstate->global_alpha_u8 != 255)
-        for (int c = 0; c < components; c ++)
-          rasterizer->color[c] = (rasterizer->color[c] * gstate->global_alpha_u8)/255;
-    }
+        ctx_sha1_process(&sha1, (unsigned char*)&hash, 8);
 
-#if CTX_INLINED_NORMAL
-  if (gstate->compositing_mode == CTX_COMPOSITE_CLEAR)
-    rasterizer->comp_op = ctx_GRAYA8_clear_normal;
-  else
-    switch (gstate->blend_mode)
-    {
-      case CTX_BLEND_NORMAL:
-        if (gstate->compositing_mode == CTX_COMPOSITE_COPY)
         {
-          rasterizer->comp_op = ctx_GRAYA8_copy_normal;
+          int is = rasterizer->state->gstate.image_smoothing;
+          ctx_sha1_process(&sha1, (uint8_t*)&is, sizeof(int));
         }
-        else if (gstate->global_alpha_u8 == 0)
-          rasterizer->comp_op = ctx_RGBA8_nop;
-        else
-        switch (gstate->source_fill.type)
-        {
-          case CTX_SOURCE_COLOR:
-            if (gstate->compositing_mode == CTX_COMPOSITE_SOURCE_OVER)
-            {
-              if (rasterizer->color[components-1] == 0)
-                rasterizer->comp_op = ctx_RGBA8_nop;
-              else if (rasterizer->color[components-1] == 255)
-                rasterizer->comp_op = ctx_GRAYA8_source_copy_normal_color;
-              else
-                rasterizer->comp_op = ctx_GRAYA8_source_over_normal_color;
-            }
-            else
-            {
-              rasterizer->comp_op = ctx_GRAYA8_porter_duff_generic_normal;
-            }
-            break;
-          default:
-            rasterizer->comp_op = ctx_GRAYA8_porter_duff_generic_normal;
-            break;
+
+          ctx_sha1_done(&sha1, (unsigned char*)ctx_sha1_hash);
+          _ctx_add_hash (hasher, &shape_rect, ctx_sha1_hash);
+
+        if (!rasterizer->preserve)
+          ctx_rasterizer_reset (rasterizer);
+        rasterizer->preserve = 0;
         }
         break;
-      default:
-        rasterizer->comp_op = ctx_GRAYA8_porter_duff_generic;
-        break;
-    }
-#endif
-}
-#endif
+      case CTX_STROKE:
+        {
+          CtxSHA1 sha1;
+          memcpy (&sha1, &hasher->sha1_stroke, sizeof (CtxSHA1));
+          char ctx_sha1_hash[20];
+        uint64_t hash = ctx_rasterizer_poly_to_hash (rasterizer);
+        CtxIntRectangle shape_rect = {
+          rasterizer->col_min / CTX_SUBDIV - rasterizer->state->gstate.line_width,
+          rasterizer->scan_min / aa - rasterizer->state->gstate.line_width,
+          (rasterizer->col_max - rasterizer->col_min + 1) / CTX_SUBDIV + 
rasterizer->state->gstate.line_width,
+          (rasterizer->scan_max - rasterizer->scan_min + 1) / aa + rasterizer->state->gstate.line_width
+        };
 
-#endif
-#if CTX_ENABLE_RGB332
+        shape_rect.width += rasterizer->state->gstate.line_width * 2;
+        shape_rect.height += rasterizer->state->gstate.line_width * 2;
+        shape_rect.x -= rasterizer->state->gstate.line_width;
+        shape_rect.y -= rasterizer->state->gstate.line_width;
 
-inline static void
-ctx_332_unpack (uint8_t pixel,
-                uint8_t *red,
-                uint8_t *green,
-                uint8_t *blue)
-{
-  *blue   = (pixel & 3) <<6;
-  *green = ( (pixel >> 2) & 7) <<5;
-  *red   = ( (pixel >> 5) & 7) <<5;
-  if (*blue > 223)  { *blue  = 255; }
-  if (*green > 223) { *green = 255; }
-  if (*red > 223)   { *red   = 255; }
-}
+        hash ^= (int)(rasterizer->state->gstate.line_width * 110);
+        hash ^= (rasterizer->state->gstate.line_cap * 23);
+        hash ^= (rasterizer->state->gstate.source_stroke.type * 117);
 
-static inline uint8_t
-ctx_332_pack (uint8_t red,
-              uint8_t green,
-              uint8_t blue)
-{
-  uint8_t c  = (red >> 5) << 5;
-  c |= (green >> 5) << 2;
-  c |= (blue >> 6);
-  return c;
-}
+        ctx_sha1_process(&sha1, (unsigned char*)&hash, 8);
 
-static inline void
-ctx_RGB332_to_RGBA8 (CtxRasterizer *rasterizer, int x, const void *buf, uint8_t *rgba, int count)
-{
-  const uint8_t *pixel = (uint8_t *) buf;
-  while (count--)
-    {
-      ctx_332_unpack (*pixel, &rgba[0], &rgba[1], &rgba[2]);
-#if CTX_RGB332_ALPHA
-      if (rgba[0]==255 && rgba[2] == 255 && rgba[1]==0)
-        { rgba[3] = 0; }
-      else
+        uint32_t color;
+        ctx_color_get_rgba8 (rasterizer->state, &rasterizer->state->gstate.source_stroke.color, 
(uint8_t*)(&color));
+
+          ctx_sha1_process(&sha1, (unsigned char*)&color, 4);
+
+          ctx_sha1_done(&sha1, (unsigned char*)ctx_sha1_hash);
+          _ctx_add_hash (hasher, &shape_rect, ctx_sha1_hash);
+        }
+        if (!rasterizer->preserve)
+          ctx_rasterizer_reset (rasterizer);
+        rasterizer->preserve = 0;
+        break;
+        /* the above cases are the painting cases and 
+         * the only ones differing from the rasterizer's process switch
+         */
+
+      case CTX_LINE_TO:
+        ctx_rasterizer_line_to (rasterizer, c->c.x0, c->c.y0);
+        break;
+      case CTX_REL_LINE_TO:
+        ctx_rasterizer_rel_line_to (rasterizer, c->c.x0, c->c.y0);
+        break;
+      case CTX_MOVE_TO:
+        ctx_rasterizer_move_to (rasterizer, c->c.x0, c->c.y0);
+        break;
+      case CTX_REL_MOVE_TO:
+        ctx_rasterizer_rel_move_to (rasterizer, c->c.x0, c->c.y0);
+        break;
+      case CTX_CURVE_TO:
+        ctx_rasterizer_curve_to (rasterizer, c->c.x0, c->c.y0,
+                                 c->c.x1, c->c.y1,
+                                 c->c.x2, c->c.y2);
+        break;
+      case CTX_REL_CURVE_TO:
+        ctx_rasterizer_rel_curve_to (rasterizer, c->c.x0, c->c.y0,
+                                     c->c.x1, c->c.y1,
+                                     c->c.x2, c->c.y2);
+        break;
+      case CTX_QUAD_TO:
+        ctx_rasterizer_quad_to (rasterizer, c->c.x0, c->c.y0, c->c.x1, c->c.y1);
+        break;
+      case CTX_REL_QUAD_TO:
+        ctx_rasterizer_rel_quad_to (rasterizer, c->c.x0, c->c.y0, c->c.x1, c->c.y1);
+        break;
+      case CTX_ARC:
+        ctx_rasterizer_arc (rasterizer, c->arc.x, c->arc.y, c->arc.radius, c->arc.angle1, c->arc.angle2, 
c->arc.direction);
+        break;
+      case CTX_RECTANGLE:
+        ctx_rasterizer_rectangle (rasterizer, c->rectangle.x, c->rectangle.y,
+                                  c->rectangle.width, c->rectangle.height);
+        break;
+      case CTX_ROUND_RECTANGLE:
+        ctx_rasterizer_round_rectangle (rasterizer, c->rectangle.x, c->rectangle.y,
+                                        c->rectangle.width, c->rectangle.height,
+                                        c->rectangle.radius);
+        break;
+      case CTX_SET_PIXEL:
+        ctx_rasterizer_set_pixel (rasterizer, c->set_pixel.x, c->set_pixel.y,
+                                  c->set_pixel.rgba[0],
+                                  c->set_pixel.rgba[1],
+                                  c->set_pixel.rgba[2],
+                                  c->set_pixel.rgba[3]);
+        break;
+      case CTX_PRESERVE:
+        rasterizer->preserve = 1;
+        break;
+      case CTX_ROTATE:
+      case CTX_SCALE:
+      case CTX_TRANSLATE:
+      case CTX_SAVE:
+      case CTX_RESTORE:
+        rasterizer->uses_transforms = 1;
+        ctx_interpret_transforms (rasterizer->state, entry, NULL);
+
+        
+        break;
+      case CTX_FONT:
+        ctx_rasterizer_set_font (rasterizer, ctx_arg_string() );
+        break;
+      case CTX_BEGIN_PATH:
+        ctx_rasterizer_reset (rasterizer);
+        break;
+      case CTX_CLIP:
+        // should perhaps modify a global state to include
+        // in hash?
+        ctx_rasterizer_clip (rasterizer);
+        break;
+      case CTX_CLOSE_PATH:
+        ctx_rasterizer_finish_shape (rasterizer);
+        break;
+      case CTX_DEFINE_TEXTURE:
+        {
+        ctx_sha1_init (&hasher->sha1_fill);
+        ctx_sha1_process (&hasher->sha1_fill, (uint8_t*)c->define_texture.eid, strlen 
(c->define_texture.eid));
+        ctx_sha1_process(&hasher->sha1_fill, (unsigned char*)(&rasterizer->state->gstate.transform), sizeof 
(rasterizer->state->gstate.transform));
+
+        rasterizer->comp_op = NULL; // why?
+        }
+        break;
+      case CTX_TEXTURE:
+        ctx_sha1_init (&hasher->sha1_fill);
+        ctx_sha1_process (&hasher->sha1_fill, (uint8_t*)c->texture.eid, strlen (c->texture.eid));
+        ctx_sha1_process (&hasher->sha1_fill, (uint8_t*)(&rasterizer->state->gstate.transform), sizeof 
(rasterizer->state->gstate.transform));
+        rasterizer->comp_op = NULL; // why?
+        break;
+      case CTX_COLOR:
+        {
+          uint32_t color;
+          if (((int)(ctx_arg_float(0))&512))
+          {
+            ctx_color_get_rgba8 (rasterizer->state, &rasterizer->state->gstate.source_stroke.color, 
(uint8_t*)(&color));
+            ctx_sha1_init (&hasher->sha1_stroke);
+            ctx_sha1_process(&hasher->sha1_stroke, (unsigned char*)&color, 4);
+          }
+          else
+          {
+            ctx_color_get_rgba8 (rasterizer->state, &rasterizer->state->gstate.source_fill.color, 
(uint8_t*)(&color));
+            ctx_sha1_init (&hasher->sha1_fill);
+            ctx_sha1_process(&hasher->sha1_fill, (unsigned char*)&color, 4);
+          }
+        }
+        break;
+      case CTX_LINEAR_GRADIENT:
+          ctx_sha1_init (&hasher->sha1_fill);
+          ctx_sha1_process(&hasher->sha1_fill, 
+                           (uint8_t*)c, sizeof (c->linear_gradient));
+          ctx_sha1_process (&hasher->sha1_fill, (unsigned char*)(&rasterizer->state->gstate.transform), 
sizeof (rasterizer->state->gstate.transform));
+        break;
+      case CTX_RADIAL_GRADIENT:
+          ctx_sha1_init (&hasher->sha1_fill);
+          ctx_sha1_process(&hasher->sha1_fill, 
+                           (uint8_t*)c, sizeof (c->radial_gradient));
+          ctx_sha1_process (&hasher->sha1_fill, (unsigned char*)(&rasterizer->state->gstate.transform), 
sizeof (rasterizer->state->gstate.transform));
+        //ctx_state_gradient_clear_stops (rasterizer->state);
+        break;
+#if CTX_GRADIENTS
+      case CTX_GRADIENT_STOP:
+        {
+          float rgba[4]= {ctx_u8_to_float (ctx_arg_u8 (4) ),
+                          ctx_u8_to_float (ctx_arg_u8 (4+1) ),
+                          ctx_u8_to_float (ctx_arg_u8 (4+2) ),
+                          ctx_u8_to_float (ctx_arg_u8 (4+3) )
+                         };
+          ctx_sha1_process(&hasher->sha1_fill, 
+                           (uint8_t*) &rgba[0], sizeof(rgba));
+        }
+        break;
 #endif
-        { rgba[3] = 255; }
-      pixel+=1;
-      rgba +=4;
     }
-}
-
-static inline void
-ctx_RGBA8_to_RGB332 (CtxRasterizer *rasterizer, int x, const uint8_t *rgba, void *buf, int count)
-{
-  uint8_t *pixel = (uint8_t *) buf;
-  while (count--)
+  if (command->code == CTX_LINE_WIDTH)
     {
-#if CTX_RGB332_ALPHA
-      if (rgba[3]==0)
-        { pixel[0] = ctx_332_pack (255, 0, 255); }
-      else
-#endif
-        { pixel[0] = ctx_332_pack (rgba[0], rgba[1], rgba[2]); }
-      pixel+=1;
-      rgba +=4;
+      float x = state->gstate.line_width;
+      /* normalize line width according to scaling factor
+       */
+      x = x * ctx_maxf (ctx_maxf (ctx_fabsf (state->gstate.transform.m[0][0]),
+                                  ctx_fabsf (state->gstate.transform.m[0][1]) ),
+                        ctx_maxf (ctx_fabsf (state->gstate.transform.m[1][0]),
+                                  ctx_fabsf (state->gstate.transform.m[1][1]) ) );
+      state->gstate.line_width = x;
     }
 }
 
-#endif
-#if CTX_ENABLE_RGB565 | CTX_ENABLE_RGB565_BYTESWAPPED
-
-static inline void
-ctx_565_unpack (const uint16_t pixel,
-                uint8_t *red,
-                uint8_t *green,
-                uint8_t *blue,
-                const int byteswap)
+static CtxRasterizer *
+ctx_hasher_init (CtxRasterizer *rasterizer, Ctx *ctx, CtxState *state, int width, int height, int cols, int 
rows)
 {
-  uint16_t byteswapped;
-  if (byteswap)
-    { byteswapped = (pixel>>8) | (pixel<<8); }
-  else
-    { byteswapped  = pixel; }
-  *blue   =  (byteswapped & 31) <<3;
-  *green = ( (byteswapped>>5) & 63) <<2;
-  *red   = ( (byteswapped>>11) & 31) <<3;
-#if 0
-  if (*blue > 248) { *blue = 255; }
-  if (*green > 248) { *green = 255; }
-  if (*red > 248) { *red = 255; }
-#endif
-}
+  CtxHasher *hasher = (CtxHasher*)rasterizer;
+  ctx_memset (rasterizer, 0, sizeof (CtxHasher) );
+  rasterizer->vfuncs.process = ctx_hasher_process;
+  rasterizer->vfuncs.free    = (CtxDestroyNotify)ctx_rasterizer_deinit;
+  // XXX need own destructor to not leak ->hashes
+  rasterizer->edge_list.flags |= CTX_DRAWLIST_EDGE_LIST;
+  rasterizer->state       = state;
+  rasterizer->ctx         = ctx;
+  ctx_state_init (rasterizer->state);
+  rasterizer->blit_x      = 0;
+  rasterizer->blit_y      = 0;
+  rasterizer->blit_width  = width;
+  rasterizer->blit_height = height;
+  rasterizer->state->gstate.clip_min_x  = 0;
+  rasterizer->state->gstate.clip_min_y  = 0;
+  rasterizer->state->gstate.clip_max_x  = width - 1;
+  rasterizer->state->gstate.clip_max_y  = height - 1;
+  rasterizer->scan_min    = 5000;
+  rasterizer->scan_max    = -5000;
+  //rasterizer->aa          = 15;
 
-static inline uint32_t
-ctx_565_unpack_32 (const uint16_t pixel,
-                   const int byteswap)
-{
-  uint16_t byteswapped;
-  if (byteswap)
-    { byteswapped = (pixel>>8) | (pixel<<8); }
-  else
-    { byteswapped  = pixel; }
-  uint8_t blue   = (byteswapped & 31) <<3;
-  uint8_t green = ( (byteswapped>>5) & 63) <<2;
-  uint8_t red   = ( (byteswapped>>11) & 31) <<3;
-#if 0
-  if (*blue > 248) { *blue = 255; }
-  if (*green > 248) { *green = 255; }
-  if (*red > 248) { *red = 255; }
-#endif
-  return red +  (green << 8) + (blue << 16) + (0xff << 24);
-}
+  hasher->rows = rows;
+  hasher->cols = cols;
 
-static inline uint16_t
-ctx_565_pack (const uint8_t  red,
-              const uint8_t  green,
-              const uint8_t  blue,
-              const int      byteswap)
-{
-  uint32_t c = (red >> 3) << 11;
-  c |= (green >> 2) << 5;
-  c |= blue >> 3;
-  if (byteswap)
-    { return (c>>8) | (c<<8); } /* swap bytes */
-  return c;
+  hasher->hashes = (uint8_t*)ctx_calloc (20, rows * cols);
+  ctx_sha1_init (&hasher->sha1_fill);
+  ctx_sha1_init (&hasher->sha1_stroke);
+
+  return rasterizer;
 }
 
-static inline uint16_t
-ctx_888_to_565 (uint32_t in, int byteswap)
+Ctx *ctx_hasher_new (int width, int height, int cols, int rows)
 {
-  uint8_t *rgb=(uint8_t*)(&in);
-  return ctx_565_pack (rgb[0],rgb[1],rgb[2], byteswap);
+  Ctx *ctx           = ctx_new ();
+  CtxState    *state = &ctx->state;
+  CtxRasterizer *rasterizer = (CtxRasterizer *) ctx_calloc (sizeof (CtxHasher), 1);
+  ctx_hasher_init (rasterizer, ctx, state, width, height, cols, rows);
+  ctx_set_renderer (ctx, (void*)rasterizer);
+  return ctx;
 }
-
-static inline uint32_t
-ctx_565_to_888 (uint16_t in, int byteswap)
+uint8_t *ctx_hasher_get_hash (Ctx *ctx, int col, int row)
 {
-  uint32_t ret = 0;
-  uint8_t *rgba=(uint8_t*)&ret;
-  ctx_565_unpack (in,
-                  &rgba[0],
-                  &rgba[1],
-                  &rgba[2],
-                  byteswap);
-  return ret;
+  CtxHasher *hasher = (CtxHasher*)ctx->renderer;
+  if (row < 0) row =0;
+  if (col < 0) col =0;
+  if (row >= hasher->rows) row = hasher->rows-1;
+  if (col >= hasher->cols) col = hasher->cols-1;
+
+  return &hasher->hashes[(row*hasher->cols+col)*20];
 }
 
 #endif
-#if CTX_ENABLE_RGB565
+#if CTX_EVENTS
 
+#if !__COSMOPOLITAN__
+#include <termios.h>
 
-static inline void
-ctx_RGB565_to_RGBA8 (CtxRasterizer *rasterizer, int x, const void *buf, uint8_t *rgba, int count)
-{
-  const uint16_t *pixel = (uint16_t *) buf;
-  while (count--)
-    {
-      ((uint32_t*)(rgba))[0] = ctx_565_unpack_32 (*pixel, 0);
-#if CTX_RGB565_ALPHA
-      if (rgba[0]==255 && rgba[2] == 255 && rgba[1]==0)
-        { rgba[3] = 0; }
+#include <fcntl.h>
+#include <sys/ioctl.h>
 #endif
-      pixel+=1;
-      rgba +=4;
-    }
-}
 
-static inline void
-ctx_RGBA8_to_RGB565 (CtxRasterizer *rasterizer, int x, const uint8_t *rgba, void *buf, int count)
+int ctx_terminal_width (void)
 {
-  uint16_t *pixel = (uint16_t *) buf;
-  while (count--)
-    {
-#if CTX_RGB565_ALPHA
-      if (rgba[3]==0)
-        { pixel[0] = ctx_565_pack (255, 0, 255, 0); }
-      else
+  char buf[1024];
+  struct termios orig_attr;
+  struct termios raw;
+  tcgetattr (STDIN_FILENO, &orig_attr);
+  raw = orig_attr;
+  raw.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
+  raw.c_oflag &= ~(OPOST);
+  raw.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG);
+  raw.c_cc[VMIN] = 1; raw.c_cc[VTIME] = 0; /* 1 byte, no timer */
+  if (tcsetattr (STDIN_FILENO, TCSAFLUSH, &raw) < 0)
+    return 0;
+  fprintf (stderr, "\e[14t");
+  //tcflush(STDIN_FILENO, 1);
+#if __COSMOPOLITAN__
+  /// XXX ?
+#else
+  tcdrain(STDIN_FILENO);
 #endif
-        { pixel[0] = ctx_565_pack (rgba[0], rgba[1], rgba[2], 0); }
-      pixel+=1;
-      rgba +=4;
-    }
-}
-
-static void
-ctx_RGBA8_source_over_normal_color (CTX_COMPOSITE_ARGUMENTS);
-static void
-ctx_RGBA8_source_copy_normal_color (CTX_COMPOSITE_ARGUMENTS);
-
-static void
-ctx_composite_RGB565 (CTX_COMPOSITE_ARGUMENTS)
-{
-#if 0
-  if (CTX_LIKELY(rasterizer->comp_op == ctx_RGBA8_source_over_normal_color))
-  {
-     int byteswap = 0;
-     uint32_t si    = *((uint32_t*)(src));
-     uint16_t si_16 = ctx_888_to_565 (si, byteswap);
-     uint32_t sval  = (si_16 & ( (31 << 11 ) | 31));
-     uint32_t sg = (si_16 & (63 << 5)) >> 5;
-     uint32_t si_a = si >> (24 + 3);
-     while (count--)
-     {
-        uint32_t di_16 = *((uint16_t*)(dst));
-        uint32_t cov = (*coverage) >> 3;
-        uint32_t racov = (32-((31+si_a*cov)>>5));
-        uint32_t dval = (di_16 & ( (31 << 11 ) | 31));
-        uint32_t dg = (di_16 >> 5) & 63; // faster outside than
-                                         // remerged as part of dval
-        *((uint16_t*)(dst)) =
-                ((               
-                  (((sval * cov) + (dval * racov)) >> 5)
-                 ) & ((31 << 11 )|31)) |
-                  ((((sg * cov) + (dg * racov)) & 63) );
+  int length = 0;
+  usleep (1000 * 60); // to account for possibly lowish latency ssh,
+                      // should be made configurable ; perhaps in
+                      // an env var
+  struct timeval tv = {0,0};
+  fd_set rfds;
+  
+  FD_ZERO(&rfds);
+  FD_SET(0, &rfds);
+  tv.tv_usec = 1000 * 5;
 
-     }
-     return;
+  for (int n = 0; select(1, &rfds, NULL, NULL, &tv) && n < 20; n++)
+  {
+    length += read (STDIN_FILENO, &buf[length], 1);
   }
-#endif
-  uint8_t pixels[count * 4];
-  ctx_RGB565_to_RGBA8 (rasterizer, x0, dst, &pixels[0], count);
-  rasterizer->comp_op (rasterizer, &pixels[0], rasterizer->color, x0, coverage, count);
-  ctx_RGBA8_to_RGB565 (rasterizer, x0, &pixels[0], dst, count);
+  tcsetattr (STDIN_FILENO, TCSAFLUSH, &orig_attr);
+  if (length == -1)
+  {
+    return 0;
+  }
+  char *semi = strchr (buf, ';');
+  buf[length]=0;
+  if (semi) {semi++; semi = strchr (semi, ';');}
+  if (semi)
+  {
+    return atoi(semi + 1);
+  }
+  return 0;
 }
-#endif
-#if CTX_ENABLE_RGB565_BYTESWAPPED
 
-static inline void
-ctx_RGB565_BS_to_RGBA8 (CtxRasterizer *rasterizer, int x, const void *buf, uint8_t *rgba, int count)
+int ctx_terminal_height (void)
 {
-  const uint16_t *pixel = (uint16_t *) buf;
-  while (count--)
-    {
-      //ctx_565_unpack (*pixel, &rgba[0], &rgba[1], &rgba[2], 1);
-      ((uint32_t*)(rgba))[0] = ctx_565_unpack_32 (*pixel, 1);
-#if CTX_RGB565_ALPHA
-      if (rgba[0]==255 && rgba[2] == 255 && rgba[1]==0)
-        { rgba[3] = 0; }
-      else
-        { rgba[3] = 255; }
+  char buf[1024];
+  struct termios orig_attr;
+  struct termios raw;
+  tcgetattr (STDIN_FILENO, &orig_attr);
+  raw = orig_attr;
+  raw.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
+  raw.c_oflag &= ~(OPOST);
+  raw.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG);
+  raw.c_cc[VMIN] = 1; raw.c_cc[VTIME] = 0; /* 1 byte, no timer */
+  if (tcsetattr (STDIN_FILENO, TCSAFLUSH, &raw) < 0)
+    return 0;
+  fprintf (stderr, "\e[14t");
+  //tcflush(STDIN_FILENO, 1);
+#if !__COSMOPOLITAN__
+  tcdrain(STDIN_FILENO);
 #endif
-      pixel+=1;
-      rgba +=4;
-    }
+  int length = 0;
+  usleep (1000 * 60); // to account for possibly lowish latency ssh,
+                      // should be made configurable ; perhaps in
+                      // an env var
+  struct timeval tv = {0,0};
+  fd_set rfds;
+  
+  FD_ZERO(&rfds);
+  FD_SET(0, &rfds);
+  tv.tv_usec = 1000 * 5;
+
+  for (int n = 0; select(1, &rfds, NULL, NULL, &tv) && n < 20; n++)
+  {
+    length += read (STDIN_FILENO, &buf[length], 1);
+  }
+  tcsetattr (STDIN_FILENO, TCSAFLUSH, &orig_attr);
+  if (length == -1)
+  {
+    return 0;
+  }
+  char *semi = strchr (buf, ';');
+  buf[length]=0;
+  if (semi)
+  {
+    return atoi(semi + 1);
+  }
+  return 0;
 }
 
-static inline void
-ctx_RGBA8_to_RGB565_BS (CtxRasterizer *rasterizer, int x, const uint8_t *rgba, void *buf, int count)
+int ctx_terminal_cols (void)
 {
-  uint16_t *pixel = (uint16_t *) buf;
-  while (count--)
-    {
-#if CTX_RGB565_ALPHA
-      if (rgba[3]==0)
-        { pixel[0] = ctx_565_pack (255, 0, 255, 1); }
-      else
-#endif
-        { pixel[0] = ctx_565_pack (rgba[0], rgba[1], rgba[2], 1); }
-      pixel+=1;
-      rgba +=4;
-    }
-}
+  struct winsize ws; 
+  if (ioctl(0,TIOCGWINSZ,&ws)!=0)
+    return 80;
+  return ws.ws_col;
+} 
 
-static void
-ctx_composite_RGB565_BS (CTX_COMPOSITE_ARGUMENTS)
+int ctx_terminal_rows (void)
 {
-  uint8_t pixels[count * 4];
-  ctx_RGB565_BS_to_RGBA8 (rasterizer, x0, dst, &pixels[0], count);
-  rasterizer->comp_op (rasterizer, &pixels[0], rasterizer->color, x0, coverage, count);
-  ctx_RGBA8_to_RGB565_BS (rasterizer, x0, &pixels[0], dst, count);
+  struct winsize ws; 
+  if (ioctl(0,TIOCGWINSZ,&ws)!=0)
+    return 25;
+  return ws.ws_row;
 }
-#endif
 
-static CtxPixelFormatInfo ctx_pixel_formats[]=
-{
-#if CTX_ENABLE_RGBA8
-  {
-    CTX_FORMAT_RGBA8, 4, 32, 4, 0, 0, CTX_FORMAT_RGBA8,
-    NULL, NULL, NULL, ctx_setup_RGBA8
-  },
-#endif
-#if CTX_ENABLE_BGRA8
-  {
-    CTX_FORMAT_BGRA8, 4, 32, 4, 0, 0, CTX_FORMAT_RGBA8,
-    ctx_BGRA8_to_RGBA8, ctx_RGBA8_to_BGRA8, ctx_composite_BGRA8, ctx_setup_RGBA8,
-  },
-#endif
-#if CTX_ENABLE_GRAYF
-  {
-    CTX_FORMAT_GRAYF, 1, 32, 4 * 2, 0, 0, CTX_FORMAT_GRAYAF,
-    NULL, NULL, ctx_composite_GRAYF, ctx_setup_GRAYAF,
-  },
-#endif
-#if CTX_ENABLE_GRAYAF
-  {
-    CTX_FORMAT_GRAYAF, 2, 64, 4 * 2, 0, 0, CTX_FORMAT_GRAYAF,
-    NULL, NULL, NULL, ctx_setup_GRAYAF,
-  },
-#endif
-#if CTX_ENABLE_RGBAF
-  {
-    CTX_FORMAT_RGBAF, 4, 128, 4 * 4, 0, 0, CTX_FORMAT_RGBAF,
-    NULL, NULL, NULL, ctx_setup_RGBAF,
-  },
-#endif
-#if CTX_ENABLE_RGB8
-  {
-    CTX_FORMAT_RGB8, 3, 24, 4, 0, 0, CTX_FORMAT_RGBA8,
-    ctx_RGB8_to_RGBA8, ctx_RGBA8_to_RGB8, ctx_composite_convert, ctx_setup_RGBA8,
-  },
-#endif
-#if CTX_ENABLE_GRAY1
-  {
-#if CTX_NATIVE_GRAYA8
-    CTX_FORMAT_GRAY1, 1, 1, 2, 1, 1, CTX_FORMAT_GRAYA8,
-    ctx_GRAY1_to_GRAYA8, ctx_GRAYA8_to_GRAY1, ctx_composite_convert, ctx_setup_GRAYA8,
-#else
-    CTX_FORMAT_GRAY1, 1, 1, 4, 1, 1, CTX_FORMAT_RGBA8,
-    ctx_GRAY1_to_RGBA8, ctx_RGBA8_to_GRAY1, ctx_composite_convert, ctx_setup_RGBA8,
-#endif
-  },
-#endif
-#if CTX_ENABLE_GRAY2
-  {
-#if CTX_NATIVE_GRAYA8
-    CTX_FORMAT_GRAY2, 1, 2, 2, 4, 4, CTX_FORMAT_GRAYA8,
-    ctx_GRAY2_to_GRAYA8, ctx_GRAYA8_to_GRAY2, ctx_composite_convert, ctx_setup_GRAYA8,
-#else
-    CTX_FORMAT_GRAY2, 1, 2, 4, 4, 4, CTX_FORMAT_RGBA8,
-    ctx_GRAY2_to_RGBA8, ctx_RGBA8_to_GRAY2, ctx_composite_convert, ctx_setup_RGBA8,
-#endif
-  },
-#endif
-#if CTX_ENABLE_GRAY4
-  {
-#if CTX_NATIVE_GRAYA8
-    CTX_FORMAT_GRAY4, 1, 4, 2, 16, 16, CTX_FORMAT_GRAYA8,
-    ctx_GRAY4_to_GRAYA8, ctx_GRAYA8_to_GRAY4, ctx_composite_convert, ctx_setup_GRAYA8,
-#else
-    CTX_FORMAT_GRAY4, 1, 4, 4, 16, 16, CTX_FORMAT_GRAYA8,
-    ctx_GRAY4_to_RGBA8, ctx_RGBA8_to_GRAY4, ctx_composite_convert, ctx_setup_RGBA8,
-#endif
-  },
-#endif
-#if CTX_ENABLE_GRAY8
-  {
-#if CTX_NATIVE_GRAYA8
-    CTX_FORMAT_GRAY8, 1, 8, 2, 0, 0, CTX_FORMAT_GRAYA8,
-    ctx_GRAY8_to_GRAYA8, ctx_GRAYA8_to_GRAY8, ctx_composite_convert, ctx_setup_GRAYA8,
-#else
-    CTX_FORMAT_GRAY8, 1, 8, 4, 0, 0, CTX_FORMAT_RGBA8,
-    ctx_GRAY8_to_RGBA8, ctx_RGBA8_to_GRAY8, ctx_composite_convert, ctx_setup_RGBA8,
-#endif
-  },
-#endif
-#if CTX_ENABLE_GRAYA8
-  {
-#if CTX_NATIVE_GRAYA8
-    CTX_FORMAT_GRAYA8, 2, 16, 2, 0, 0, CTX_FORMAT_GRAYA8,
-    ctx_GRAYA8_to_RGBA8, ctx_RGBA8_to_GRAYA8, NULL, ctx_setup_GRAYA8,
-#else
-    CTX_FORMAT_GRAYA8, 2, 16, 4, 0, 0, CTX_FORMAT_RGBA8,
-    ctx_GRAYA8_to_RGBA8, ctx_RGBA8_to_GRAYA8, ctx_composite_convert, ctx_setup_RGBA8,
-#endif
-  },
-#endif
-#if CTX_ENABLE_RGB332
-  {
-    CTX_FORMAT_RGB332, 3, 8, 4, 10, 12, CTX_FORMAT_RGBA8,
-    ctx_RGB332_to_RGBA8, ctx_RGBA8_to_RGB332,
-    ctx_composite_convert, ctx_setup_RGBA8,
-  },
-#endif
-#if CTX_ENABLE_RGB565
-  {
-    CTX_FORMAT_RGB565, 3, 16, 4, 32, 64, CTX_FORMAT_RGBA8,
-    ctx_RGB565_to_RGBA8, ctx_RGBA8_to_RGB565,
-    ctx_composite_RGB565, ctx_setup_RGBA8,
-  },
-#endif
-#if CTX_ENABLE_RGB565_BYTESWAPPED
-  {
-    CTX_FORMAT_RGB565_BYTESWAPPED, 3, 16, 4, 32, 64, CTX_FORMAT_RGBA8,
-    ctx_RGB565_BS_to_RGBA8,
-    ctx_RGBA8_to_RGB565_BS,
-    ctx_composite_RGB565_BS, ctx_setup_RGBA8,
-  },
-#endif
-#if CTX_ENABLE_CMYKAF
-  {
-    CTX_FORMAT_CMYKAF, 5, 160, 4 * 5, 0, 0, CTX_FORMAT_CMYKAF,
-    NULL, NULL, NULL, ctx_setup_CMYKAF,
-  },
-#endif
-#if CTX_ENABLE_CMYKA8
-  {
-    CTX_FORMAT_CMYKA8, 5, 40, 4 * 5, 0, 0, CTX_FORMAT_CMYKAF,
-    NULL, NULL, ctx_composite_CMYKA8, ctx_setup_CMYKAF,
-  },
-#endif
-#if CTX_ENABLE_CMYK8
-  {
-    CTX_FORMAT_CMYK8, 5, 32, 4 * 5, 0, 0, CTX_FORMAT_CMYKAF,
-    NULL, NULL, ctx_composite_CMYK8, ctx_setup_CMYKAF,
-  },
-#endif
-#if CTX_ENABLE_YUV420
-  {
-    CTX_FORMAT_YUV420, 1, 8, 4, 0, 0, CTX_FORMAT_RGBA8,
-    NULL, NULL, ctx_composite_convert, ctx_setup_RGBA8,
-  },
-#endif
-  {
-    CTX_FORMAT_NONE
-  }
-};
 
 
-CtxPixelFormatInfo *
-ctx_pixel_format_info (CtxPixelFormat format)
-{
-  for (unsigned int i = 0; ctx_pixel_formats[i].pixel_format; i++)
-    {
-      if (ctx_pixel_formats[i].pixel_format == format)
-        {
-          return &ctx_pixel_formats[i];
-        }
-    }
-  return NULL;
-}
 
-#endif
-#if CTX_RASTERIZER
-#define CTX_AA_HALFSTEP2   (CTX_FULL_AA/2)
-#define CTX_AA_HALFSTEP    ((CTX_FULL_AA/2)+1)
 
-static void
-ctx_gradient_cache_prime (CtxRasterizer *rasterizer);
+#define DECTCEM_CURSOR_SHOW      "\033[?25h"
+#define DECTCEM_CURSOR_HIDE      "\033[?25l"
+#define TERMINAL_MOUSE_OFF       "\033[?1000l\033[?1003l"
+#define TERMINAL_MOUSE_ON_BASIC  "\033[?1000h"
+#define TERMINAL_MOUSE_ON_DRAG   "\033[?1000h\033[?1003h" /* +ON_BASIC for wider */
+#define TERMINAL_MOUSE_ON_FULL   "\033[?1000h\033[?1004h" /* compatibility */
+#define XTERM_ALTSCREEN_ON       "\033[?47h"
+#define XTERM_ALTSCREEN_OFF      "\033[?47l"
 
-static inline void
-_ctx_setup_compositor (CtxRasterizer *rasterizer)
-{
-  if (CTX_UNLIKELY (rasterizer->comp_op==0))
-  {
-    rasterizer->format->setup (rasterizer);
-#if CTX_GRADIENTS
-#if CTX_GRADIENT_CACHE
-  switch (rasterizer->state->gstate.source_fill.type)
-  {
-    case CTX_SOURCE_LINEAR_GRADIENT:
-    case CTX_SOURCE_RADIAL_GRADIENT:
-      ctx_gradient_cache_prime (rasterizer);
-      break;
-    case CTX_SOURCE_TEXTURE:
+/*************************** input handling *************************/
 
-      _ctx_matrix_multiply (&rasterizer->state->gstate.source_fill.transform,
-                            &rasterizer->state->gstate.source_fill.set_transform,
-                            &rasterizer->state->gstate.transform);
+#if !__COSMOPOLITAN__
+#include <termios.h>
+#include <errno.h>
+#include <signal.h>
+#endif
 
-      ctx_matrix_invert (&rasterizer->state->gstate.source_fill.transform);
+#define DELAY_MS  100  
 
-      if (!rasterizer->state->gstate.source_fill.texture.buffer->color_managed)
-        _ctx_texture_prepare_color_management (rasterizer,
-        rasterizer->state->gstate.source_fill.texture.buffer);
-      break;
-  }
-#endif
+#ifndef MIN
+#define MIN(a,b) (((a)<(b))?(a):(b))
 #endif
-  }
-}
 
-#define CTX_FULL_AA 15
-inline static void
-ctx_rasterizer_apply_coverage (CtxRasterizer *rasterizer,
-                               uint8_t * dst,
-                               int       x,
-                               uint8_t * coverage,
-                               int       count)
-{
-  if (CTX_UNLIKELY(rasterizer->format->apply_coverage))
-    rasterizer->format->apply_coverage(rasterizer, dst, rasterizer->color, x, coverage, count);
-  else
-    /* it is faster to dispatch in this condition, than using a shared
-     * direct trampoline
-     */
-    rasterizer->comp_op (rasterizer, dst, rasterizer->color, x, coverage, count);
-}
+static int  size_changed = 0;       /* XXX: global state */
+static int  signal_installed = 0;   /* XXX: global state */
 
-static void
-ctx_rasterizer_gradient_add_stop (CtxRasterizer *rasterizer, float pos, float *rgba)
-{
-  /* FIXME XXX we only have one gradient, but might need separate gradients
-   * for fill/stroke !
-   * 
-   */
-  CtxGradient *gradient = &rasterizer->state->gradient;
-  CtxGradientStop *stop = &gradient->stops[gradient->n_stops];
-  stop->pos = pos;
-  ctx_color_set_rgba (rasterizer->state, & (stop->color), rgba[0], rgba[1], rgba[2], rgba[3]);
-  if (gradient->n_stops < 15) //we'll keep overwriting the last when out of stops
-    { gradient->n_stops++; }
-}
+static const char *mouse_modes[]=
+{TERMINAL_MOUSE_OFF,
+ TERMINAL_MOUSE_ON_BASIC,
+ TERMINAL_MOUSE_ON_DRAG,
+ TERMINAL_MOUSE_ON_FULL,
+ NULL};
 
-static inline int ctx_rasterizer_add_point (CtxRasterizer *rasterizer, int x1, int y1)
-{
-  CtxSegment entry = {CTX_EDGE, {{0},}};
-  rasterizer->scan_min = ctx_mini (y1, rasterizer->scan_min);
-  rasterizer->scan_max = ctx_maxi (y1, rasterizer->scan_max);
+/* note that a nick can have multiple occurences, the labels
+ * should be kept the same for all occurences of a combination. */
+typedef struct NcKeyCode {
+  const char *nick;          /* programmers name for key (combo) */
+  const char *label;         /* utf8 label for key */
+  const char  sequence[10];  /* terminal sequence */
+} NcKeyCode;
+static const NcKeyCode keycodes[]={  
 
-  rasterizer->col_min = ctx_mini (x1, rasterizer->col_min);
-  rasterizer->col_max = ctx_maxi (x1, rasterizer->col_max);
+  {"up",                  "↑",     "\033[A"},
+  {"down",                "↓",     "\033[B"},
+  {"right",               "→",     "\033[C"},
+  {"left",                "←",     "\033[D"},
 
-  entry.data.s16[2]=x1;
-  entry.data.s16[3]=y1;
-  return ctx_drawlist_add_single (&rasterizer->edge_list, (CtxEntry*)&entry);
-}
+  {"shift-up",            "⇧↑",    "\033[1;2A"},
+  {"shift-down",          "⇧↓",    "\033[1;2B"},
+  {"shift-right",         "⇧→",    "\033[1;2C"},
+  {"shift-left",          "⇧←",    "\033[1;2D"},
 
-#if 0
-#define CTX_SHAPE_CACHE_PRIME1   7853
-#define CTX_SHAPE_CACHE_PRIME2   4129
-#define CTX_SHAPE_CACHE_PRIME3   3371
-#define CTX_SHAPE_CACHE_PRIME4   4221
-#else
-#define CTX_SHAPE_CACHE_PRIME1   283
-#define CTX_SHAPE_CACHE_PRIME2   599
-#define CTX_SHAPE_CACHE_PRIME3   101
-#define CTX_SHAPE_CACHE_PRIME4   661
-#endif
+  {"alt-up",              "^↑",    "\033[1;3A"},
+  {"alt-down",            "^↓",    "\033[1;3B"},
+  {"alt-right",           "^→",    "\033[1;3C"},
+  {"alt-left",            "^←",    "\033[1;3D"},
 
-float ctx_shape_cache_rate = 0.0;
-#if CTX_SHAPE_CACHE
-int   _ctx_shape_cache_enabled = 1;
+  {"alt-shift-up",        "alt-s↑", "\033[1;4A"},
+  {"alt-shift-down",      "alt-s↓", "\033[1;4B"},
+  {"alt-shift-right",     "alt-s→", "\033[1;4C"},
+  {"alt-shift-left",      "alt-s←", "\033[1;4D"},
 
-//static CtxShapeCache ctx_cache = {{NULL,}, 0};
+  {"control-up",          "^↑",    "\033[1;5A"},
+  {"control-down",        "^↓",    "\033[1;5B"},
+  {"control-right",       "^→",    "\033[1;5C"},
+  {"control-left",        "^←",    "\033[1;5D"},
 
-static long ctx_shape_cache_hits   = 0;
-static long ctx_shape_cache_misses = 0;
+  /* putty */
+  {"control-up",          "^↑",    "\033OA"},
+  {"control-down",        "^↓",    "\033OB"},
+  {"control-right",       "^→",    "\033OC"},
+  {"control-left",        "^←",    "\033OD"},
 
+  {"control-shift-up",    "^⇧↑",   "\033[1;6A"},
+  {"control-shift-down",  "^⇧↓",   "\033[1;6B"},
+  {"control-shift-right", "^⇧→",   "\033[1;6C"},
+  {"control-shift-left",  "^⇧←",   "\033[1;6D"},
 
-/* this returns the buffer to use for rendering, it always
-   succeeds..
- */
-static inline CtxShapeEntry *ctx_shape_entry_find (CtxRasterizer *rasterizer, uint32_t hash, int width, int 
height)
-{
-  /* use both some high and some low bits  */
-  int entry_no = ( (hash >> 10) ^ (hash & 1023) ) % CTX_SHAPE_CACHE_ENTRIES;
-  int i;
-  {
-    static int i = 0;
-    i++;
-    if (i>1000)
-      {
-        ctx_shape_cache_rate = ctx_shape_cache_hits * 100.0  / (ctx_shape_cache_hits+ctx_shape_cache_misses);
-        i = 0;
-        ctx_shape_cache_hits = 0;
-        ctx_shape_cache_misses = 0;
-      }
-  }
-// XXX : this 1 one is needed  to silence a false positive:
-// ==90718== Invalid write of size 1
-// ==90718==    at 0x1189EF: ctx_rasterizer_generate_coverage (ctx.h:4786)
-// ==90718==    by 0x118E57: ctx_rasterizer_rasterize_edges (ctx.h:4907)
-//
-  int size = sizeof (CtxShapeEntry) + width * height + 1;
+  {"control-up",          "^↑",    "\033Oa"},
+  {"control-down",        "^↓",    "\033Ob"},
+  {"control-right",       "^→",    "\033Oc"},
+  {"control-left",        "^←",    "\033Od"},
 
-  i = entry_no;
-  if (rasterizer->shape_cache.entries[i])
-    {
-      CtxShapeEntry *entry = rasterizer->shape_cache.entries[i];
-      int old_size = sizeof (CtxShapeEntry) + width + height + 1;
-      if (entry->hash == hash &&
-          entry->width == width &&
-          entry->height == height)
-        {
-          if (entry->uses < 1<<30)
-            { entry->uses++; }
-          ctx_shape_cache_hits ++;
-          return entry;
-        }
+  {"shift-up",            "⇧↑",    "\033[a"},
+  {"shift-down",          "⇧↓",    "\033[b"},
+  {"shift-right",         "⇧→",    "\033[c"},
+  {"shift-left",          "⇧←",    "\033[d"},
 
-      if (old_size >= size)
-      {
-      }
-      else
-      {
-        rasterizer->shape_cache.entries[i] = NULL;
-        rasterizer->shape_cache.size -= entry->width * entry->height;
-        rasterizer->shape_cache.size -= sizeof (CtxShapeEntry);
-        free (entry);
-        rasterizer->shape_cache.entries[i] = (CtxShapeEntry *) calloc (size, 1);
-      }
-    }
-  else
-    {
-        rasterizer->shape_cache.entries[i] = (CtxShapeEntry *) calloc (size, 1);
-    }
+  {"insert",              "ins",   "\033[2~"},
+  {"delete",              "del",   "\033[3~"},
+  {"page-up",             "PgUp",  "\033[5~"},
+  {"page-down",           "PdDn",  "\033[6~"},
+  {"home",                "Home",  "\033OH"},
+  {"end",                 "End",   "\033OF"},
+  {"home",                "Home",  "\033[H"},
+  {"end",                 "End",   "\033[F"},
+  {"control-delete",      "^del",  "\033[3;5~"},
+  {"shift-delete",        "⇧del",  "\033[3;2~"},
+  {"control-shift-delete","^⇧del", "\033[3;6~"},
 
-  ctx_shape_cache_misses ++;
-  rasterizer->shape_cache.size              += size;
-  rasterizer->shape_cache.entries[i]->hash   = hash;
-  rasterizer->shape_cache.entries[i]->width  = width;
-  rasterizer->shape_cache.entries[i]->height = height;
-  rasterizer->shape_cache.entries[i]->uses = 0;
-  return rasterizer->shape_cache.entries[i];
-}
+  {"F1",        "F1",  "\033[11~"},
+  {"F2",        "F2",  "\033[12~"},
+  {"F3",        "F3",  "\033[13~"},
+  {"F4",        "F4",  "\033[14~"},
+  {"F1",        "F1",  "\033OP"},
+  {"F2",        "F2",  "\033OQ"},
+  {"F3",        "F3",  "\033OR"},
+  {"F4",        "F4",  "\033OS"},
+  {"F5",        "F5",  "\033[15~"},
+  {"F6",        "F6",  "\033[16~"},
+  {"F7",        "F7",  "\033[17~"},
+  {"F8",        "F8",  "\033[18~"},
+  {"F9",        "F9",  "\033[19~"},
+  {"F9",        "F9",  "\033[20~"},
+  {"F10",       "F10", "\033[21~"},
+  {"F11",       "F11", "\033[22~"},
+  {"F12",       "F12", "\033[23~"},
+  {"tab",       "↹",     {9, '\0'}},
+  {"shift-tab", "shift+↹",  "\033[Z"},
+  {"backspace", "⌫",  {127, '\0'}},
+  {"space",     "␣",   " "},
+  {"esc",        "␛",  "\033"},
+  {"return",    "⏎",  {10,0}},
+  {"return",    "⏎",  {13,0}},
+  /* this section could be autogenerated by code */
+  {"control-a", "^A",  {1,0}},
+  {"control-b", "^B",  {2,0}},
+  {"control-c", "^C",  {3,0}},
+  {"control-d", "^D",  {4,0}},
+  {"control-e", "^E",  {5,0}},
+  {"control-f", "^F",  {6,0}},
+  {"control-g", "^G",  {7,0}},
+  {"control-h", "^H",  {8,0}}, /* backspace? */
+  {"control-i", "^I",  {9,0}}, /* tab */
+  {"control-j", "^J",  {10,0}},
+  {"control-k", "^K",  {11,0}},
+  {"control-l", "^L",  {12,0}},
+  {"control-n", "^N",  {14,0}},
+  {"control-o", "^O",  {15,0}},
+  {"control-p", "^P",  {16,0}},
+  {"control-q", "^Q",  {17,0}},
+  {"control-r", "^R",  {18,0}},
+  {"control-s", "^S",  {19,0}},
+  {"control-t", "^T",  {20,0}},
+  {"control-u", "^U",  {21,0}},
+  {"control-v", "^V",  {22,0}},
+  {"control-w", "^W",  {23,0}},
+  {"control-x", "^X",  {24,0}},
+  {"control-y", "^Y",  {25,0}},
+  {"control-z", "^Z",  {26,0}},
+  {"alt-0",     "%0",  "\0330"},
+  {"alt-1",     "%1",  "\0331"},
+  {"alt-2",     "%2",  "\0332"},
+  {"alt-3",     "%3",  "\0333"},
+  {"alt-4",     "%4",  "\0334"},
+  {"alt-5",     "%5",  "\0335"},
+  {"alt-6",     "%6",  "\0336"},
+  {"alt-7",     "%7",  "\0337"}, /* backspace? */
+  {"alt-8",     "%8",  "\0338"},
+  {"alt-9",     "%9",  "\0339"},
+  {"alt-+",     "%+",  "\033+"},
+  {"alt--",     "%-",  "\033-"},
+  {"alt-/",     "%/",  "\033/"},
+  {"alt-a",     "%A",  "\033a"},
+  {"alt-b",     "%B",  "\033b"},
+  {"alt-c",     "%C",  "\033c"},
+  {"alt-d",     "%D",  "\033d"},
+  {"alt-e",     "%E",  "\033e"},
+  {"alt-f",     "%F",  "\033f"},
+  {"alt-g",     "%G",  "\033g"},
+  {"alt-h",     "%H",  "\033h"}, /* backspace? */
+  {"alt-i",     "%I",  "\033i"},
+  {"alt-j",     "%J",  "\033j"},
+  {"alt-k",     "%K",  "\033k"},
+  {"alt-l",     "%L",  "\033l"},
+  {"alt-n",     "%N",  "\033m"},
+  {"alt-n",     "%N",  "\033n"},
+  {"alt-o",     "%O",  "\033o"},
+  {"alt-p",     "%P",  "\033p"},
+  {"alt-q",     "%Q",  "\033q"},
+  {"alt-r",     "%R",  "\033r"},
+  {"alt-s",     "%S",  "\033s"},
+  {"alt-t",     "%T",  "\033t"},
+  {"alt-u",     "%U",  "\033u"},
+  {"alt-v",     "%V",  "\033v"},
+  {"alt-w",     "%W",  "\033w"},
+  {"alt-x",     "%X",  "\033x"},
+  {"alt-y",     "%Y",  "\033y"},
+  {"alt-z",     "%Z",  "\033z"},
+  {"shift-tab", "shift-↹", {27, 9, 0}},
+  /* Linux Console  */
+  {"home",      "Home", "\033[1~"},
+  {"end",       "End",  "\033[4~"},
+  {"F1",        "F1",   "\033[[A"},
+  {"F2",        "F2",   "\033[[B"},
+  {"F3",        "F3",   "\033[[C"},
+  {"F4",        "F4",   "\033[[D"},
+  {"F5",        "F5",   "\033[[E"},
+  {"F6",        "F6",   "\033[[F"},
+  {"F7",        "F7",   "\033[[G"},
+  {"F8",        "F8",   "\033[[H"},
+  {"F9",        "F9",   "\033[[I"},
+  {"F10",       "F10",  "\033[[J"},
+  {"F11",       "F11",  "\033[[K"},
+  {"F12",       "F12",  "\033[[L"}, 
+  {"ok",        "",     "\033[0n"},
+  {NULL, }
+};
 
-#endif
+static struct termios orig_attr;    /* in order to restore at exit */
+static int    nc_is_raw = 0;
+static int    atexit_registered = 0;
+static int    mouse_mode = NC_MOUSE_NONE;
 
-static uint32_t ctx_rasterizer_poly_to_hash (CtxRasterizer *rasterizer)
+static void _nc_noraw (void)
 {
-  int x = 0;
-  int y = 0;
-
-  CtxSegment *entry = (CtxSegment*)&rasterizer->edge_list.entries[0];
+  if (nc_is_raw && tcsetattr (STDIN_FILENO, TCSAFLUSH, &orig_attr) != -1)
+    nc_is_raw = 0;
+}
 
-  int ox = entry->data.s16[2];
-  int oy = entry->data.s16[3];
-  uint32_t hash = rasterizer->edge_list.count;
-  hash = ox;//(ox % CTX_SUBDIV);
-  hash *= CTX_SHAPE_CACHE_PRIME1;
-  hash += oy; //(oy % CTX_RASTERIZER_AA);
-  for (int i = 0; i < rasterizer->edge_list.count; i++)
-    {
-      CtxSegment *entry = &(((CtxSegment*)(rasterizer->edge_list.entries)))[i];
-      x = entry->data.s16[2];
-      y = entry->data.s16[3];
-      int dx = x-ox;
-      int dy = y-oy;
-      ox = x;
-      oy = y;
-      hash *= CTX_SHAPE_CACHE_PRIME3;
-      hash += dx;
-      hash *= CTX_SHAPE_CACHE_PRIME4;
-      hash += dy;
-    }
-  return hash;
+void
+nc_at_exit (void)
+{
+  printf (TERMINAL_MOUSE_OFF);
+  printf (XTERM_ALTSCREEN_OFF);
+  _nc_noraw();
+  fprintf (stdout, "\e[?25h");
+  //if (ctx_native_events)
+  fprintf (stdout, "\e[?201l");
+  fprintf (stdout, "\e[?1049l");
 }
 
-static uint32_t ctx_rasterizer_poly_to_edges (CtxRasterizer *rasterizer)
+static const char *mouse_get_event_int (Ctx *n, int *x, int *y)
 {
-  int x = 0;
-  int y = 0;
-  int count = rasterizer->edge_list.count;
-  if (CTX_UNLIKELY (count == 0))
-     return 0;
-#if CTX_SHAPE_CACHE
-  CtxSegment *entry = (CtxSegment*)&rasterizer->edge_list.entries[0];
-  int ox = entry->data.s16[2];
-  int oy = entry->data.s16[3];
-  uint32_t hash = rasterizer->edge_list.count;
-  hash = (ox & CTX_SUBDIV);
-  hash *= CTX_SHAPE_CACHE_PRIME1;
-  hash += (oy & CTX_SUBDIV);
-#endif
-  for (int i = 0; i < count; i++)
-    {
-      CtxSegment *entry = &(((CtxSegment*)(rasterizer->edge_list.entries)))[i];
-      if (entry->code != CTX_NEW_EDGE)
-        {
-          entry->data.s16[0] = x;
-          entry->data.s16[1] = y;
-        }
-      else
-        {
-          entry->code = CTX_EDGE;
-#if CTX_SHAPE_CACHE
-          hash *= CTX_SHAPE_CACHE_PRIME2;
-#endif
-        }
-      x = entry->data.s16[2];
-      y = entry->data.s16[3];
-#if CTX_SHAPE_CACHE
-      int dx = x-ox;
-      int dy = y-oy;
-      ox = x;
-      oy = y;
-      hash *= CTX_SHAPE_CACHE_PRIME3;
-      hash += dx;
-      hash *= CTX_SHAPE_CACHE_PRIME4;
-      hash += dy;
-#endif
-      if (entry->data.s16[3] < entry->data.s16[1])
-        {
-          *entry = ctx_segment_s16 (CTX_EDGE_FLIPPED,
-                            entry->data.s16[2], entry->data.s16[3],
-                            entry->data.s16[0], entry->data.s16[1]);
-        }
-    }
-#if CTX_SHAPE_CACHE
-  return hash;
-#else
-  return 0;
-#endif
-}
-
-static inline void ctx_rasterizer_finish_shape (CtxRasterizer *rasterizer)
-{
-  if (rasterizer->has_shape && rasterizer->has_prev)
-    {
-      ctx_rasterizer_line_to (rasterizer, rasterizer->first_x, rasterizer->first_y);
-      rasterizer->has_prev = 0;
-    }
-}
-
-static inline void ctx_rasterizer_move_to (CtxRasterizer *rasterizer, float x, float y)
-{
-  float tx = x; float ty = y;
-  int aa = 15;//rasterizer->aa;
-  rasterizer->x        = x;
-  rasterizer->y        = y;
-  rasterizer->first_x  = x;
-  rasterizer->first_y  = y;
-  rasterizer->has_prev = -1;
-  if (rasterizer->uses_transforms)
-    {
-      _ctx_user_to_device (rasterizer->state, &tx, &ty);
-    }
+  static int prev_state = 0;
+  const char *ret = "mouse-motion";
+  float relx, rely;
+  signed char buf[3];
+  read (n->mouse_fd, buf, 3);
+  relx = buf[1];
+  rely = -buf[2];
 
-  tx = (tx - rasterizer->blit_x) * CTX_SUBDIV;
-  ty = ty * aa;
+  n->mouse_x += relx * 0.1;
+  n->mouse_y += rely * 0.1;
 
-  rasterizer->scan_min = ctx_mini (ty, rasterizer->scan_min);
-  rasterizer->scan_max = ctx_maxi (ty, rasterizer->scan_max);
+  if (n->mouse_x < 1) n->mouse_x = 1;
+  if (n->mouse_y < 1) n->mouse_y = 1;
+  if (n->mouse_x >= n->events.width)  n->mouse_x = n->events.width;
+  if (n->mouse_y >= n->events.height) n->mouse_y = n->events.height;
 
-  rasterizer->col_min = ctx_mini (tx, rasterizer->col_min);
-  rasterizer->col_max = ctx_maxi (tx, rasterizer->col_max);
-}
+  if (x) *x = n->mouse_x;
+  if (y) *y = n->mouse_y;
 
-static inline void ctx_rasterizer_line_to (CtxRasterizer *rasterizer, float x, float y)
-{
-  float tx = x;
-  float ty = y;
-  float ox = rasterizer->x;
-  float oy = rasterizer->y;
-  if ((rasterizer->uses_transforms))
+  if ((prev_state & 1) != (buf[0] & 1))
     {
-      _ctx_user_to_device (rasterizer->state, &tx, &ty);
+      if (buf[0] & 1) ret = "mouse-press";
     }
-  tx -= rasterizer->blit_x;
-#define MIN_Y -1000
-#define MAX_Y 1400
-
-  ty = ctx_maxf (MIN_Y, ty);
-  ty = ctx_minf (MAX_Y, ty);
-  
-  ctx_rasterizer_add_point (rasterizer, tx * CTX_SUBDIV, ty * CTX_FULL_AA);//rasterizer->aa);
+  else if (buf[0] & 1)
+    ret = "mouse-drag";
 
-  if (CTX_UNLIKELY(rasterizer->has_prev<=0))
+  if ((prev_state & 2) != (buf[0] & 2))
     {
-      if ((rasterizer->uses_transforms))
-      {
-        // storing transformed would save some processing for a tiny
-        // amount of runtime RAM XXX
-        _ctx_user_to_device (rasterizer->state, &ox, &oy);
-      }
-      ox -= rasterizer->blit_x;
-      oy = ctx_maxf (oy, MIN_Y);
-      oy = ctx_minf (oy, MAX_Y);
-      CtxSegment *entry = & ((CtxSegment*)rasterizer->edge_list.entries)[rasterizer->edge_list.count-1];
-      entry->data.s16[0] = ox * CTX_SUBDIV;
-      entry->data.s16[1] = oy * CTX_FULL_AA;
-      entry->code = CTX_NEW_EDGE;
-      rasterizer->has_prev = 1;
+      if (buf[0] & 2) ret = "mouse2-press";
     }
-  rasterizer->has_shape = 1;
-  rasterizer->y         = y;
-  rasterizer->x         = x;
-}
-
-
-CTX_INLINE static float
-ctx_bezier_sample_1d (float x0, float x1, float x2, float x3, float dt)
-{
-  float ab   = ctx_lerpf (x0, x1, dt);
-  float bc   = ctx_lerpf (x1, x2, dt);
-  float cd   = ctx_lerpf (x2, x3, dt);
-  float abbc = ctx_lerpf (ab, bc, dt);
-  float bccd = ctx_lerpf (bc, cd, dt);
-  return ctx_lerpf (abbc, bccd, dt);
-}
-
-inline static void
-ctx_bezier_sample (float x0, float y0,
-                   float x1, float y1,
-                   float x2, float y2,
-                   float x3, float y3,
-                   float dt, float *x, float *y)
-{
-  *x = ctx_bezier_sample_1d (x0, x1, x2, x3, dt);
-  *y = ctx_bezier_sample_1d (y0, y1, y2, y3, dt);
-}
+  else if (buf[0] & 2)
+    ret = "mouse2-drag";
 
-static inline void
-ctx_rasterizer_bezier_divide (CtxRasterizer *rasterizer,
-                              float ox, float oy,
-                              float x0, float y0,
-                              float x1, float y1,
-                              float x2, float y2,
-                              float sx, float sy,
-                              float ex, float ey,
-                              float s,
-                              float e,
-                              int   iteration,
-                              float tolerance)
-{
-  float t = (s + e) * 0.5f;
-  float x, y, lx, ly, dx, dy;
-  ctx_bezier_sample (ox, oy, x0, y0, x1, y1, x2, y2, t, &x, &y);
-  if (iteration)
+  if ((prev_state & 4) != (buf[0] & 4))
     {
-      lx = ctx_lerpf (sx, ex, t);
-      ly = ctx_lerpf (sy, ey, t);
-      dx = lx - x;
-      dy = ly - y;
-      if (CTX_UNLIKELY( (dx*dx+dy*dy) < tolerance))
-        /* bailing - because for the mid-point straight line difference is
-           tiny */
-        { return; }
-      dx = sx - ex;
-      dy = ey - ey;
-      if (CTX_UNLIKELY( (dx*dx+dy*dy) < tolerance))
-        /* bailing on tiny segments */
-        { return; }
+      if (buf[0] & 4) ret = "mouse1-press";
     }
-  if (iteration < 8)
-  {
-  ctx_rasterizer_bezier_divide (rasterizer, ox, oy, x0, y0, x1, y1, x2, y2,
-                                sx, sy, x, y, s, t, iteration + 1,
-                                tolerance);
-  ctx_rasterizer_line_to (rasterizer, x, y);
-  ctx_rasterizer_bezier_divide (rasterizer, ox, oy, x0, y0, x1, y1, x2, y2,
-                                x, y, ex, ey, t, e, iteration + 1,
-                                tolerance);
-  }
-}
+  else if (buf[0] & 4)
+    ret = "mouse1-drag";
 
-static void
-ctx_rasterizer_curve_to (CtxRasterizer *rasterizer,
-                         float x0, float y0,
-                         float x1, float y1,
-                         float x2, float y2)
-{
-  //float tolerance =
-  //  1.0f*(ctx_pow2 (rasterizer->state->gstate.transform.m[0][0]) +
-  //  ctx_pow2 (rasterizer->state->gstate.transform.m[1][1]));
-  float tolerance = ctx_matrix_get_scale (&rasterizer->state->gstate.transform);
-  float ox = rasterizer->x;
-  float oy = rasterizer->y;
-  //tolerance *= tolerance;
-  tolerance = 2.0/(tolerance*tolerance);
-  ox = rasterizer->state->x;
-  oy = rasterizer->state->y;
-  //tolerance = 10.0/(tolerance*tolerance);
-  //tolerance = 10.0f/tolerance;
-#if 0 // skipping this to preserve hash integrity
-  if (tolerance == 1.0f || 1)
-  {
-  float maxx = ctx_maxf (x1,x2);
-  maxx = ctx_maxf (maxx, ox);
-  maxx = ctx_maxf (maxx, x0);
-  float maxy = ctx_maxf (y1,y2);
-  maxy = ctx_maxf (maxy, oy);
-  maxy = ctx_maxf (maxy, y0);
-  float minx = ctx_minf (x1,x2);
-  minx = ctx_minf (minx, ox);
-  minx = ctx_minf (minx, x0);
-  float miny = ctx_minf (y1,y2);
-  miny = ctx_minf (miny, oy);
-  miny = ctx_minf (miny, y0);
-  
-  _ctx_user_to_device (rasterizer->state, &minx, &miny);
-  _ctx_user_to_device (rasterizer->state, &maxx, &maxy);
-#if 1
-    if(
-        (minx > rasterizer->blit_x + rasterizer->blit_width) ||
-        (miny > rasterizer->blit_y + rasterizer->blit_height) ||
-        (maxx < rasterizer->blit_x) ||
-        (maxy < rasterizer->blit_y) )
-    {
-    }
-    else
-#endif
-    {
-      ctx_rasterizer_bezier_divide (rasterizer,
-                                    ox, oy, x0, y0,
-                                    x1, y1, x2, y2,
-                                    ox, oy, x2, y2,
-                                    0.0f, 1.0f, 0.0f, tolerance);
-    }
-  }
-  else
-#endif
-    {
-      ctx_rasterizer_bezier_divide (rasterizer,
-                                    ox, oy, x0, y0,
-                                    x1, y1, x2, y2,
-                                    ox, oy, x2, y2,
-                                    0.0f, 1.0f, 0.0f, tolerance);
-    }
-  ctx_rasterizer_line_to (rasterizer, x2, y2);
+  prev_state = buf[0];
+  return ret;
 }
 
-static void
-ctx_rasterizer_rel_move_to (CtxRasterizer *rasterizer, float x, float y)
-{
-  //if (CTX_UNLIKELY(x == 0.f && y == 0.f))
-  //{ return; }
-  x += rasterizer->x;
-  y += rasterizer->y;
-  ctx_rasterizer_move_to (rasterizer, x, y);
-}
+static const char *mev_type = NULL;
+static int         mev_x = 0;
+static int         mev_y = 0;
+static int         mev_q = 0;
 
-static void
-ctx_rasterizer_rel_line_to (CtxRasterizer *rasterizer, float x, float y)
+static const char *mouse_get_event (Ctx  *n, int *x, int *y)
 {
-  //if (CTX_UNLIKELY(x== 0.f && y==0.f))
-  //  { return; }
-  x += rasterizer->x;
-  y += rasterizer->y;
-  ctx_rasterizer_line_to (rasterizer, x, y);
+  if (!mev_q)
+    return NULL;
+  *x = mev_x;
+  *y = mev_y;
+  mev_q = 0;
+  return mev_type;
 }
 
-static void
-ctx_rasterizer_rel_curve_to (CtxRasterizer *rasterizer,
-                             float x0, float y0, float x1, float y1, float x2, float y2)
+static int mouse_has_event (Ctx *n)
 {
-  x0 += rasterizer->x;
-  y0 += rasterizer->y;
-  x1 += rasterizer->x;
-  y1 += rasterizer->y;
-  x2 += rasterizer->x;
-  y2 += rasterizer->y;
-  ctx_rasterizer_curve_to (rasterizer, x0, y0, x1, y1, x2, y2);
-}
+  struct timeval tv;
+  int retval;
 
+  if (mouse_mode == NC_MOUSE_NONE)
+    return 0;
 
-static int
-ctx_rasterizer_find_texture (CtxRasterizer *rasterizer,
-                             const char *eid)
-{
-  int no;
-  for (no = 0; no < CTX_MAX_TEXTURES; no++)
-  {
-    if (rasterizer->texture_source->texture[no].data &&
-        rasterizer->texture_source->texture[no].eid &&
-        !strcmp (rasterizer->texture_source->texture[no].eid, eid))
-      return no;
-  }
-  return -1;
-}
+  if (mev_q)
+    return 1;
 
-static void
-ctx_rasterizer_set_texture (CtxRasterizer *rasterizer,
-                            const char *eid,
-                            float x,
-                            float y)
-{
-  int is_stroke = (rasterizer->state->source != 0);
-  CtxSource *source = is_stroke && (rasterizer->state->gstate.source_stroke.type != CTX_SOURCE_INHERIT_FILL)?
-                        &rasterizer->state->gstate.source_stroke:
-                        &rasterizer->state->gstate.source_fill;
-  rasterizer->state->source = 0;
+  if (n->mouse_fd == 0)
+    return 0;
+  return 0;
 
-  int no = ctx_rasterizer_find_texture (rasterizer, eid);
-  if (no < 0 || no >= CTX_MAX_TEXTURES) { no = 0; }
-  if (rasterizer->texture_source->texture[no].data == NULL)
-    {
-      fprintf (stderr, "ctx tex fail %p %s %i\n", rasterizer->texture_source, eid, no);
-      return;
-    }
-  else
   {
-    rasterizer->texture_source->texture[no].frame = rasterizer->texture_source->frame;
+    fd_set rfds;
+    FD_ZERO (&rfds);
+    FD_SET(n->mouse_fd, &rfds);
+    tv.tv_sec = 0; tv.tv_usec = 0;
+    retval = select (n->mouse_fd+1, &rfds, NULL, NULL, &tv);
   }
-  source->type = CTX_SOURCE_TEXTURE;
-  source->texture.buffer = &rasterizer->texture_source->texture[no];
-  ctx_matrix_identity (&source->set_transform);
-  ctx_matrix_translate (&source->set_transform, x, y);
-}
-
-
-static void ctx_rasterizer_define_texture (CtxRasterizer *rasterizer,
-                                           const char *eid,
-                                           int width,
-                                           int height,
-                                           int format,
-                                           char unsigned *data)
-{
-  _ctx_texture_lock (); // we're using the same texture_source from all threads, keeping allocaitons down
-                        // need synchronizing (it could be better to do a pre-pass)
-  ctx_texture_init (rasterizer->texture_source,
-                    eid,
-                    width,
-                    height,
-                    ctx_pixel_format_get_stride ((CtxPixelFormat)format, width),
-                    (CtxPixelFormat)format,
-#if CTX_ENABLE_CM
-                    (void*)rasterizer->state->gstate.texture_space,
-#else
-                    NULL,
-#endif
-                    data,
-                    ctx_buffer_pixels_free, (void*)23);
-                    /*  when userdata for ctx_buffer_pixels_free is 23, texture_init dups the data on
-                     *  use
-                     */
-
-  ctx_rasterizer_set_texture (rasterizer, eid, 0.0, 0.0);
-  _ctx_texture_unlock ();
-}
 
+  if (retval != 0)
+    {
+      int nx = 0, ny = 0;
+      const char *type = mouse_get_event_int (n, &nx, &ny);
 
-CTX_INLINE static int ctx_compare_edges (const void *ap, const void *bp)
-{
-  const CtxSegment *a = (const CtxSegment *) ap;
-  const CtxSegment *b = (const CtxSegment *) bp;
-  return a->data.s16[1] - b->data.s16[1];
-}
+      if ((mouse_mode < NC_MOUSE_DRAG && mev_type && !strcmp (mev_type, "drag")) ||
+          (mouse_mode < NC_MOUSE_ALL && mev_type && !strcmp (mev_type, "motion")))
+        {
+          mev_q = 0;
+          return mouse_has_event (n);
+        }
 
-CTX_INLINE static int ctx_edge_qsort_partition (CtxSegment *A, int low, int high)
-{
-  CtxSegment pivot = A[ (high+low) /2];
-  int i = low;
-  int j = high;
-  while (i <= j)
-    {
-      while (ctx_compare_edges (&A[i], &pivot) < 0) { i ++; }
-      while (ctx_compare_edges (&pivot, &A[j]) < 0) { j --; }
-      if (i <= j)
+      if ((mev_type && !strcmp (type, mev_type) && !strcmp (type, "mouse-motion")) ||
+         (mev_type && !strcmp (type, mev_type) && !strcmp (type, "mouse1-drag")) ||
+         (mev_type && !strcmp (type, mev_type) && !strcmp (type, "mouse2-drag")))
         {
-          CtxSegment tmp = A[i];
-          A[i] = A[j];
-          A[j] = tmp;
-          i++;
-          j--;
+          if (nx == mev_x && ny == mev_y)
+          {
+            mev_q = 0;
+            return mouse_has_event (n);
+          }
         }
+      mev_x = nx;
+      mev_y = ny;
+      mev_type = type;
+      mev_q = 1;
     }
-  return i;
-}
-
-static inline void ctx_edge_qsort (CtxSegment *entries, int low, int high)
-{
-  {
-    int p = ctx_edge_qsort_partition (entries, low, high);
-    if (low < p -1 )
-      { ctx_edge_qsort (entries, low, p - 1); }
-    if (low < high)
-      { ctx_edge_qsort (entries, p, high); }
-  }
+  return retval != 0;
 }
 
-static CTX_INLINE void ctx_rasterizer_sort_edges (CtxRasterizer *rasterizer)
-{
-  ctx_edge_qsort ((CtxSegment*)& (rasterizer->edge_list.entries[0]), 0, rasterizer->edge_list.count-1);
-}
 
-static inline void ctx_rasterizer_discard_edges (CtxRasterizer *rasterizer)
+static int _nc_raw (void)
 {
-  int scanline = rasterizer->scanline;
-  int limit3 = CTX_RASTERIZER_AA_SLOPE_LIMIT3;
-  if (rasterizer->fast_aa)
-    limit3 = CTX_RASTERIZER_AA_SLOPE_LIMIT3_FAST_AA;
-  CtxSegment *segments = &((CtxSegment*)(rasterizer->edge_list.entries))[0];
-  for (int i = 0; i < rasterizer->active_edges; i++)
-    {
-      CtxSegment *segment = segments + rasterizer->edges[i];
-      int edge_end = segment->data.s16[3]-1;
-      if (CTX_LIKELY(edge_end < scanline))
-        {
-
-          int dx_dy = abs(segment->delta);
-          rasterizer->needs_aa3  -= (dx_dy > limit3);
-          rasterizer->needs_aa5  -= (dx_dy > CTX_RASTERIZER_AA_SLOPE_LIMIT5);
-          rasterizer->needs_aa15 -= (dx_dy > CTX_RASTERIZER_AA_SLOPE_LIMIT15);
-          rasterizer->edges[i] = rasterizer->edges[rasterizer->active_edges-1];
-          rasterizer->active_edges--;
-          i--;
-        }
-      else if (edge_end < scanline + CTX_FULL_AA)
-        rasterizer->ending_edges++;
-    }
-#if 0
-  // we should - but for 99% of the cases we do not need to, so we skip it
-  for (int i = 0; i < rasterizer->pending_edges; i++)
+  struct termios raw;
+  if (!isatty (STDIN_FILENO))
+    return -1;
+  if (!atexit_registered)
     {
-      int edge_end = 
((CtxSegment*)(rasterizer->edge_list.entries))[rasterizer->edges[CTX_MAX_EDGES-1-i]].data.s16[3]-1;
-      if (edge_end < scanline + CTX_FULL_AA)
-        rasterizer->ending_edges++;
+      atexit (nc_at_exit);
+      atexit_registered = 1;
     }
+  if (tcgetattr (STDIN_FILENO, &orig_attr) == -1)
+    return -1;
+  raw = orig_attr;  /* modify the original mode */
+  raw.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
+  raw.c_oflag &= ~(OPOST);
+  raw.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG);
+  raw.c_cc[VMIN] = 1; raw.c_cc[VTIME] = 0; /* 1 byte, no timer */
+  if (tcsetattr (STDIN_FILENO, TCSAFLUSH, &raw) < 0)
+    return -1;
+  nc_is_raw = 1;
+#if !__COSMOPOLITAN__
+  tcdrain(STDIN_FILENO);
+  tcflush(STDIN_FILENO, 1);
 #endif
+  return 0;
 }
 
-inline static void ctx_rasterizer_increment_edges (CtxRasterizer *rasterizer, int count)
+static int match_keycode (const char *buf, int length, const NcKeyCode **ret)
 {
-  rasterizer->scanline += count;
-  CtxSegment *segments = &((CtxSegment*)(rasterizer->edge_list.entries))[0];
-  for (int i = 0; i < rasterizer->active_edges; i++)
-    {
-      CtxSegment *segment = segments + rasterizer->edges[i];
-      segment->val += segment->delta * count;
-    }
-  for (int i = 0; i < rasterizer->pending_edges; i++)
+  int i;
+  int matches = 0;
+
+  if (!strncmp (buf, "\033[M", MIN(length,3)))
     {
-      CtxSegment *segment = segments + rasterizer->edges[CTX_MAX_EDGES-1-i];
-      segment->val += segment->delta * count;
+      if (length >= 6)
+        return 9001;
+      return 2342;
     }
+  for (i = 0; keycodes[i].nick; i++)
+    if (!strncmp (buf, keycodes[i].sequence, length))
+      {
+        matches ++;
+        if ((int)strlen (keycodes[i].sequence) == length && ret)
+          {
+            *ret = &keycodes[i];
+            return 1;
+          }
+      }
+  if (matches != 1 && ret)
+    *ret = NULL;
+  return matches==1?2:matches;
 }
 
-/* feeds up to rasterizer->scanline,
-   keeps a pending buffer of edges - that encompass
-   the full incoming scanline,
-   feed until the start of the scanline and check for need for aa
-   in all of pending + active edges, then
-   again feed_edges until middle of scanline if doing non-AA
-   or directly render when doing AA
-*/
-CTX_INLINE static void ctx_edge2_insertion_sort (CtxSegment *segments, int *entries, int count)
+static void nc_resize_term (int  dummy)
 {
-  for(int i=1; i<count; i++)
-   {
-     int temp = entries[i];
-     int j = i-1;
-     while (j >= 0 && segments[temp].val - segments[entries[j]].val < 0)
-     {
-       entries[j+1] = entries[j];
-       j--;
-     }
-     entries[j+1] = temp;
-   }
+  size_changed = 1;
 }
 
-CTX_INLINE static int ctx_edge2_compare2 (CtxSegment *segments, int a, int b)
+int ctx_nct_has_event (Ctx  *n, int delay_ms)
 {
-  CtxSegment *seg_a = &segments[a];
-  CtxSegment *seg_b = &segments[b];
-  int minval_a = ctx_mini (seg_a->val - seg_a->delta * CTX_AA_HALFSTEP2, seg_a->val + seg_a->delta * 
CTX_AA_HALFSTEP);
-  int minval_b = ctx_mini (seg_b->val - seg_b->delta * CTX_AA_HALFSTEP2, seg_b->val + seg_b->delta * 
CTX_AA_HALFSTEP);
-  return minval_a - minval_b;
-}
+  struct timeval tv;
+  int retval;
+  fd_set rfds;
 
-CTX_INLINE static void ctx_edge2_insertion_sort2 (CtxSegment *segments, int *entries, int count)
-{
-  for(int i=1; i<count; i++)
-   {
-     int temp = entries[i];
-     int j = i-1;
-     while (j >= 0 && ctx_edge2_compare2 (segments, temp, entries[j]) < 0)
-     {
-       entries[j+1] = entries[j];
-       j--;
-     }
-     entries[j+1] = temp;
-   }
+  if (size_changed)
+    return 1;
+  FD_ZERO (&rfds);
+  FD_SET (STDIN_FILENO, &rfds);
+  tv.tv_sec = 0; tv.tv_usec = delay_ms * 1000; 
+  retval = select (1, &rfds, NULL, NULL, &tv);
+  if (size_changed)
+    return 1;
+  return retval == 1 && retval != -1;
 }
 
-inline static void ctx_rasterizer_feed_edges (CtxRasterizer *rasterizer, int apply2_sort)
+const char *ctx_nct_get_event (Ctx *n, int timeoutms, int *x, int *y)
 {
-  int miny;
-  CtxSegment *entries = (CtxSegment*)&rasterizer->edge_list.entries[0];
-  rasterizer->horizontal_edges = 0;
-  rasterizer->ending_edges = 0;
-  for (int i = 0; i < rasterizer->pending_edges; i++)
+  unsigned char buf[20];
+  int length;
+
+
+  if (x) *x = -1;
+  if (y) *y = -1;
+
+  if (!signal_installed)
     {
-      if (entries[rasterizer->edges[CTX_MAX_EDGES-1-i]].data.s16[1] - 1 <= rasterizer->scanline)
-        {
-          if (CTX_LIKELY(rasterizer->active_edges < CTX_MAX_EDGES-2))
-            {
-              int no = rasterizer->active_edges;
-              rasterizer->active_edges++;
-              rasterizer->edges[no] = rasterizer->edges[CTX_MAX_EDGES-1-i];
-              rasterizer->edges[CTX_MAX_EDGES-1-i] =
-                rasterizer->edges[CTX_MAX_EDGES-1-rasterizer->pending_edges + 1];
-              rasterizer->pending_edges--;
-              i--;
-            }
-        }
+      _nc_raw ();
+      signal_installed = 1;
+      signal (SIGWINCH, nc_resize_term);
     }
-  int limit3 = CTX_RASTERIZER_AA_SLOPE_LIMIT3;
-  if (rasterizer->fast_aa)
-    limit3 = CTX_RASTERIZER_AA_SLOPE_LIMIT3_FAST_AA;
-  int scanline = rasterizer->scanline;
-  int edge_pos = rasterizer->edge_pos;
-  int edge_count = rasterizer->edge_list.count;
-  int *edges = rasterizer->edges;
-  while ((edge_pos < edge_count &&
-         (miny=entries[edge_pos].data.s16[1]-1)  <= scanline + CTX_FULL_AA))
-    {
-      int maxy=entries[edge_pos].data.s16[3]-1;
-      if ((rasterizer->active_edges < CTX_MAX_EDGES-2 &&
-          maxy >= scanline))
+  if (mouse_mode) // XXX too often to do it all the time!
+    printf("%s", mouse_modes[mouse_mode]);
+
+  {
+    int elapsed = 0;
+    int got_event = 0;
+
+    do {
+      if (size_changed)
         {
-          int dy = (entries[edge_pos].data.s16[3] - 1 - miny);
-          if ((dy))
-            {
-              int yd = scanline - miny;
-              int no = rasterizer->active_edges;
-              rasterizer->active_edges++;
-              int index = edges[no] = edge_pos;
-              int x0 = entries[index].data.s16[0];
-              int x1 = entries[index].data.s16[2];
-              int dx_dy = CTX_RASTERIZER_EDGE_MULTIPLIER * (x1 - x0) / dy;
-              entries[index].delta = dx_dy;
-              entries[index].val = x0 * CTX_RASTERIZER_EDGE_MULTIPLIER +
-                                         (yd * dx_dy);
+          size_changed = 0;
+          return "size-changed";
+        }
+      got_event = mouse_has_event (n);
+      if (!got_event)
+        got_event = ctx_nct_has_event (n, MIN(DELAY_MS, timeoutms-elapsed));
+      if (size_changed)
+        {
+          size_changed = 0;
+          return "size-changed";
+        }
+      /* only do this if the client has asked for idle events,
+       * and perhaps programmed the ms timer?
+       */
+      elapsed += MIN(DELAY_MS, timeoutms-elapsed);
+      if (!got_event && timeoutms && elapsed >= timeoutms)
+        return "idle";
+    } while (!got_event);
+  }
+
+  if (mouse_has_event (n))
+    return mouse_get_event (n, x, y);
+
+  for (length = 0; length < 10; length ++)
+    if (read (STDIN_FILENO, &buf[length], 1) != -1)
+      {
+        const NcKeyCode *match = NULL;
+
+        /* special case ESC, so that we can use it alone in keybindings */
+        if (length == 0 && buf[0] == 27)
+          {
+            struct timeval tv;
+            fd_set rfds;
+            FD_ZERO (&rfds);
+            FD_SET (STDIN_FILENO, &rfds);
+            tv.tv_sec = 0;
+            tv.tv_usec = 1000 * DELAY_MS;
+            if (select (1, &rfds, NULL, NULL, &tv) == 0)
+              return "esc";
+          }
 
+        switch (match_keycode ((const char*)buf, length + 1, &match))
+          {
+            case 1: /* unique match */
+              if (!match)
+                return NULL;
+              if (!strcmp(match->nick, "ok"))
               {
-                int abs_dx_dy = abs(dx_dy);
-                rasterizer->needs_aa3  += (abs_dx_dy > limit3);
-                rasterizer->needs_aa5  += (abs_dx_dy > CTX_RASTERIZER_AA_SLOPE_LIMIT5);
-                rasterizer->needs_aa15 += (abs_dx_dy > CTX_RASTERIZER_AA_SLOPE_LIMIT15);
+                ctx_frame_ack = 1;
+                return NULL;
               }
-
-              if ((miny > scanline) )
+              return match->nick;
+              break;
+            case 9001: /* mouse event */
+              if (x) *x = ((unsigned char)buf[4]-32)*1.0;
+              if (y) *y = ((unsigned char)buf[5]-32)*1.0;
+              switch (buf[3])
                 {
-                  /* it is a pending edge - we add it to the end of the array
-                     and keep a different count for items stored here, like
-                     a heap and stack growing against each other
-                  */
-                  if ((rasterizer->pending_edges < CTX_MAX_PENDING-1))
+                        /* XXX : todo reduce this to less string constants */
+                  case 32:  return "mouse-press";
+                  case 33:  return "mouse1-press";
+                  case 34:  return "mouse2-press";
+                  case 40:  return "alt-mouse-press";
+                  case 41:  return "alt-mouse1-press";
+                  case 42:  return "alt-mouse2-press";
+                  case 48:  return "control-mouse-press";
+                  case 49:  return "control-mouse1-press";
+                  case 50:  return "control-mouse2-press";
+                  case 56:  return "alt-control-mouse-press";
+                  case 57:  return "alt-control-mouse1-press";
+                  case 58:  return "alt-control-mouse2-press";
+                  case 64:  return "mouse-drag";
+                  case 65:  return "mouse1-drag";
+                  case 66:  return "mouse2-drag";
+                  case 71:  return "mouse-motion"; /* shift+motion */
+                  case 72:  return "alt-mouse-drag";
+                  case 73:  return "alt-mouse1-drag";
+                  case 74:  return "alt-mouse2-drag";
+                  case 75:  return "mouse-motion"; /* alt+motion */
+                  case 80:  return "control-mouse-drag";
+                  case 81:  return "control-mouse1-drag";
+                  case 82:  return "control-mouse2-drag";
+                  case 83:  return "mouse-motion"; /* ctrl+motion */
+                  case 91:  return "mouse-motion"; /* ctrl+alt+motion */
+                  case 95:  return "mouse-motion"; /* ctrl+alt+shift+motion */
+                  case 96:  return "scroll-up";
+                  case 97:  return "scroll-down";
+                  case 100: return "shift-scroll-up";
+                  case 101: return "shift-scroll-down";
+                  case 104: return "alt-scroll-up";
+                  case 105: return "alt-scroll-down";
+                  case 112: return "control-scroll-up";
+                  case 113: return "control-scroll-down";
+                  case 116: return "control-shift-scroll-up";
+                  case 117: return "control-shift-scroll-down";
+                  case 35: /* (or release) */
+                  case 51: /* (or ctrl-release) */
+                  case 43: /* (or alt-release) */
+                  case 67: return "mouse-motion";
+                           /* have a separate mouse-drag ? */
+                  default: {
+                             static char rbuf[100];
+                             sprintf (rbuf, "mouse (unhandled state: %i)", buf[3]);
+                             return rbuf;
+                           }
+                }
+            case 0: /* no matches, bail*/
+              { 
+                static char ret[256];
+                if (length == 0 && ctx_utf8_len (buf[0])>1) /* single unicode
+                                                               char */
                   {
-                    edges[CTX_MAX_EDGES-1-rasterizer->pending_edges] =
-                    rasterizer->edges[no];
-                    rasterizer->pending_edges++;
-                    rasterizer->active_edges--;
+                    int n_read = 
+                    read (STDIN_FILENO, &buf[length+1], ctx_utf8_len(buf[0])-1);
+                    if (n_read)
+                    {
+                      buf[ctx_utf8_len(buf[0])]=0;
+                      strcpy (ret, (const char*)buf);
+                    }
+                    return ret;
                   }
-                }
-            }
-          else
-            rasterizer->horizontal_edges ++;
-        }
-      edge_pos++;
-    }
-    rasterizer->edge_pos = edge_pos;
-    ctx_rasterizer_discard_edges (rasterizer);
-    if (apply2_sort)
-      ctx_edge2_insertion_sort2 ((CtxSegment*)rasterizer->edge_list.entries, rasterizer->edges, 
rasterizer->active_edges);
+                if (length == 0) /* ascii */
+                  {
+                    buf[1]=0;
+                    strcpy (ret, (const char*)buf);
+                    return ret;
+                  }
+                sprintf (ret, "unhandled %i:'%c' %i:'%c' %i:'%c' %i:'%c' %i:'%c' %i:'%c' %i:'%c'",
+                  length>=0? buf[0]: 0, length>=0? buf[0]>31?buf[0]:'?': ' ', 
+                  length>=1? buf[1]: 0, length>=1? buf[1]>31?buf[1]:'?': ' ', 
+                  length>=2? buf[2]: 0, length>=2? buf[2]>31?buf[2]:'?': ' ', 
+                  length>=3? buf[3]: 0, length>=3? buf[3]>31?buf[3]:'?': ' ',
+                  length>=4? buf[4]: 0, length>=4? buf[4]>31?buf[4]:'?': ' ',
+                  length>=5? buf[5]: 0, length>=5? buf[5]>31?buf[5]:'?': ' ',
+                  length>=6? buf[6]: 0, length>=6? buf[6]>31?buf[6]:'?': ' ');
+                return ret;
+              }
+              return NULL;
+            default: /* continue */
+              break;
+          }
+      }
     else
-      ctx_edge2_insertion_sort ((CtxSegment*)rasterizer->edge_list.entries, rasterizer->edges, 
rasterizer->active_edges);
+      return "key read eek";
+  return "fail";
 }
 
-
-#undef CTX_CMPSWP
-
-static inline void ctx_coverage_post_process (CtxRasterizer *rasterizer, int minx, int maxx, uint8_t 
*coverage, int *first_col, int *last_col)
+int ctx_nct_consume_events (Ctx *ctx)
 {
-  int scanline     = rasterizer->scanline;
-#if CTX_ENABLE_SHADOW_BLUR
-  if (CTX_UNLIKELY(rasterizer->in_shadow))
+  int ix, iy;
+  CtxCtx *ctxctx = (CtxCtx*)ctx->renderer;
+  const char *event = NULL;
+
   {
-    float radius = rasterizer->state->gstate.shadow_blur;
-    int dim = 2 * radius + 1;
-    if (CTX_UNLIKELY (dim > CTX_MAX_GAUSSIAN_KERNEL_DIM))
-      dim = CTX_MAX_GAUSSIAN_KERNEL_DIM;
+    float x, y;
+    event = ctx_nct_get_event (ctx, 50, &ix, &iy);
+
+    x = (ix - 1.0 + 0.5) / ctxctx->cols * ctx->events.width;
+    y = (iy - 1.0)       / ctxctx->rows * ctx->events.height;
+
+    if (!strcmp (event, "mouse-press"))
     {
-      uint16_t temp[maxx-minx+1];
-      memset (temp, 0, sizeof (temp));
-      for (int x = dim/2; x < maxx-minx + 1 - dim/2; x ++)
-        for (int u = 0; u < dim; u ++)
-        {
-          temp[x] += coverage[minx+x+u-dim/2] * rasterizer->kernel[u] * 256;
-        }
-      for (int x = 0; x < maxx-minx + 1; x ++)
-        coverage[minx+x] = temp[x] >> 8;
-    }
-  }
+      ctx_pointer_press (ctx, x, y, 0, 0);
+      ctxctx->was_down = 1;
+    } else if (!strcmp (event, "mouse-release"))
+    {
+      ctx_pointer_release (ctx, x, y, 0, 0);
+      ctxctx->was_down = 0;
+    } else if (!strcmp (event, "mouse-motion"))
+    {
+      //nct_set_cursor_pos (backend->term, ix, iy);
+      //nct_flush (backend->term);
+      if (ctxctx->was_down)
+      {
+        ctx_pointer_release (ctx, x, y, 0, 0);
+        ctxctx->was_down = 0;
+      }
+      ctx_pointer_motion (ctx, x, y, 0, 0);
+    } else if (!strcmp (event, "mouse-drag"))
+    {
+      ctx_pointer_motion (ctx, x, y, 0, 0);
+    } else if (!strcmp (event, "size-changed"))
+    {
+#if 0
+      int width = nct_sys_terminal_width ();
+      int height = nct_sys_terminal_height ();
+      nct_set_size (backend->term, width, height);
+      width *= CPX;
+      height *= CPX;
+      free (mrg->glyphs);
+      free (mrg->styles);
+      free (backend->nct_pixels);
+      backend->nct_pixels = calloc (width * height * 4, 1);
+      mrg->glyphs = calloc ((width/CPX) * (height/CPX) * 4, 1);
+      mrg->styles = calloc ((width/CPX) * (height/CPX) * 1, 1);
+      mrg_set_size (mrg, width, height);
+      mrg_queue_draw (mrg, NULL);
 #endif
 
-#if CTX_ENABLE_CLIP
-  if (CTX_UNLIKELY(rasterizer->clip_buffer &&  !rasterizer->clip_rectangle))
-  {
-    /* perhaps not working right for clear? */
-    int y = scanline / 15;//rasterizer->aa;
-    uint8_t *clip_line = &((uint8_t*)(rasterizer->clip_buffer->data))[rasterizer->blit_width*y];
-    // XXX SIMD candidate
-    for (int x = minx; x <= maxx; x ++)
+    }
+    else
     {
-#if CTX_1BIT_CLIP
-       coverage[x] = (coverage[x] * ((clip_line[x/8]&(1<<(x&8)))?255:0))/255;
-#else
-       coverage[x] = (255 + coverage[x] * clip_line[x])>>8;
-#endif
+      if (!strcmp (event, "esc"))
+        ctx_key_press (ctx, 0, "escape", 0);
+      else if (!strcmp (event, "space"))
+        ctx_key_press (ctx, 0, "space", 0);
+      else if (!strcmp (event, "enter"))
+        ctx_key_press (ctx, 0, "\n", 0);
+      else if (!strcmp (event, "return"))
+        ctx_key_press (ctx, 0, "return", 0);
+      else if (!strcmp (event, "idle"))
+      {
+      }
+      else
+      ctx_key_press (ctx, 0, event, 0);
     }
   }
-  if (CTX_UNLIKELY(rasterizer->aa == 1))
-  {
-    for (int x = minx; x <= maxx; x ++)
-     coverage[x] = coverage[x] > 127?255:0;
-  }
-#endif
-}
 
-#define CTX_EDGE(no)      entries[edges[no]]
-#define CTX_EDGE_YMIN     (segment->data.s16[1]-1)
+  return 1;
+}
 
-#define UPDATE_PARITY \
-        { \
-          if (scanline!=CTX_EDGE_YMIN)\
-            parity = (fill_rule)? \
-             parity + -1+2*(segment->code == CTX_EDGE_FLIPPED) : \
-                        1 - parity;\
-        }
+const char *ctx_native_get_event (Ctx *n, int timeoutms)
+{
+  static unsigned char buf[256];
+  int length;
 
-inline static void
-ctx_rasterizer_generate_coverage (CtxRasterizer *rasterizer,
-                                  int            minx,
-                                  int            maxx,
-                                  uint8_t       *coverage,
-                                  int      fill_rule,
-                                  const uint8_t  aa_factor)
-{
-  fill_rule = fill_rule == CTX_FILL_RULE_WINDING;
-  CtxSegment *entries      = (CtxSegment*)(&rasterizer->edge_list.entries[0]);
-  int        *edges        = rasterizer->edges;
-  int         scanline     = rasterizer->scanline;
-  int         active_edges = rasterizer->active_edges;
-  int         parity       = 0;
-  coverage -= minx;
-  uint8_t fraction = 255/aa_factor;
-  for (int t = 0; t < active_edges -1;t++)
+  if (!signal_installed)
     {
-      CtxSegment *segment = &entries[edges[t]];
-      UPDATE_PARITY;
+      _nc_raw ();
+      signal_installed = 1;
+      signal (SIGWINCH, nc_resize_term);
+    }
+//if (mouse_mode) // XXX too often to do it all the time!
+//  printf("%s", mouse_modes[mouse_mode]);
+
+    int got_event = 0;
+  {
+    int elapsed = 0;
 
-      if (parity)
+    do {
+      if (size_changed)
         {
-          CtxSegment *next_segment = &entries[edges[t+1]];
-          const int x0 = segment->val;
-          const int x1 = next_segment->val;
-          int graystart = x0 / (CTX_RASTERIZER_EDGE_MULTIPLIER*CTX_SUBDIV/256);
-          int grayend   = x1 / (CTX_RASTERIZER_EDGE_MULTIPLIER*CTX_SUBDIV/256);
-          int first     = graystart >> 8;
-          int last      = grayend   >> 8;
+          size_changed = 0;
+          return "size-changed";
+        }
+      got_event = ctx_nct_has_event (n, MIN(DELAY_MS, timeoutms-elapsed));
+      if (size_changed)
+        {
+          size_changed = 0;
+          return "size-changed";
+        }
+      /* only do this if the client has asked for idle events,
+       * and perhaps programmed the ms timer?
+       */
+      elapsed += MIN(DELAY_MS, timeoutms-elapsed);
+      if (!got_event && timeoutms && elapsed >= timeoutms)
+      {
+        return "idle";
+      }
+    } while (!got_event);
+  }
 
-          if ((first < minx))
-          { 
-            first = minx;
-            graystart=0;
-          }
-          if ((last > maxx))
-          {
-            last = maxx;
-            grayend=255;
-          }
+  for (length = 0; got_event && length < 200; length ++)
+  {
+    if (read (STDIN_FILENO, &buf[length], 1) != -1)
+      {
+         buf[length+1] = 0;
+         if (!strcmp ((char*)buf, "\e[0n"))
+         {
+           ctx_frame_ack = 1;
+           return NULL;
+         }
+         else if (buf[length]=='\n')
+         {
+           buf[length]=0;
+           return (const char*)buf;
+         }
+      }
+      got_event = ctx_nct_has_event (n, 5);
+    }
+  return NULL;
+}
 
-          graystart = fraction- (graystart&0xff)/aa_factor;
-          grayend   = (grayend & 0xff) / aa_factor;
+const char *ctx_key_get_label (Ctx  *n, const char *nick)
+{
+  int j;
+  int found = -1;
+  for (j = 0; keycodes[j].nick; j++)
+    if (found == -1 && !strcmp (keycodes[j].nick, nick))
+      return keycodes[j].label;
+  return NULL;
+}
 
-          if (first < last)
-          {
-              coverage[first] += graystart;
-              for (int x = first + 1; x < last; x++)
-                coverage[x]  += fraction;
-              coverage[last] += grayend;
-          }
-          else if (first == last)
-            coverage[first] += (graystart-(fraction-grayend));
-        }
-   }
+void _ctx_mouse (Ctx *term, int mode)
+{
+  //if (term->is_st && mode > 1)
+  //  mode = 1;
+  if (mode != mouse_mode)
+  {
+    printf ("%s", mouse_modes[mode]);
+    fflush (stdout);
+  }
+  mouse_mode = mode;
 }
 
-inline static void
-ctx_rasterizer_generate_coverage_set (CtxRasterizer *rasterizer,
-                                      int            minx,
-                                      int            maxx,
-                                      uint8_t       *coverage,
-                                      int            fill_rule)
-{
-  fill_rule = fill_rule == CTX_FILL_RULE_WINDING;
-  CtxSegment *entries = (CtxSegment*)(&rasterizer->edge_list.entries[0]);
-  int      *edges = rasterizer->edges;
-  int scanline     = rasterizer->scanline;
-  int active_edges = rasterizer->active_edges;
-  int parity = 0;
-  coverage -= minx;
-  for (int t = 0; t < active_edges -1;t++)
-    {
-      CtxSegment *segment = &entries[edges[t]];
-      UPDATE_PARITY;
 
-       if (parity)
-        {
-          CtxSegment *next_segment = &entries[edges[t+1]];
-          const int x0        = segment->val;
-          const int x1        = next_segment->val;
-          int graystart = x0 / (CTX_RASTERIZER_EDGE_MULTIPLIER*CTX_SUBDIV/256);
-          int grayend   = x1 / (CTX_RASTERIZER_EDGE_MULTIPLIER*CTX_SUBDIV/256);
-          int first     = graystart >> 8;
-          int last      = grayend   >> 8;
+#endif
 
-          if ((first < minx))
-          { 
-            first = minx;
-            graystart=0;
-          }
-          if ((last > maxx))
-          {
-            last = maxx;
-            grayend=255;
-          }
+#if !__COSMOPOLITAN__
+#include <sys/time.h>
+#endif
 
-          graystart = 255 - (graystart&0xff);
-          grayend   = (grayend & 0xff);
 
-          if (first < last)
-          {
-              coverage[first] += graystart;
-#if 0
-              for (int x = first + 1; x < last; x++)
-                coverage[x] = 255;
-#else
-              memset(&coverage[first+1], 255, last-(first+1));
+#define usecs(time)    ((uint64_t)(time.tv_sec - start_time.tv_sec) * 1000000 + time.     tv_usec)
+
+#if CTX_EVENTS
+#if !__COSMOPOLITAN__
+#if CTX_THREADS
+#include <threads.h>
 #endif
-              coverage[last]  += grayend;
-          }
-          else if (first == last)
-            coverage[first] += (graystart-(255-grayend));
-        }
-   }
-}
+#endif
+static struct timeval start_time;
 
-static inline uint32_t
-ctx_over_RGBA8 (uint32_t dst, uint32_t src, uint32_t cov)
+static void
+_ctx_init_ticks (void)
 {
-  uint32_t si_ga = (src & 0xff00ff00) >> 8;
-  uint32_t si_rb = src & 0x00ff00ff;
-  uint32_t si_a  = si_ga >> 16;
-  uint32_t rcov  = ((255+si_a * cov)>>8)^255;
-  uint32_t di_ga = ( dst & 0xff00ff00) >> 8;
-  uint32_t di_rb = dst & 0x00ff00ff;
-  return
-     ((((si_rb * cov) + 0xff00ff + (di_rb * rcov)) & 0xff00ff00) >> 8)  |
-      (((si_ga * cov) + 0xff00ff + (di_ga * rcov)) & 0xff00ff00);
+  static int done = 0;
+  if (done)
+    return;
+  done = 1;
+  gettimeofday (&start_time, NULL);
 }
 
-
-static inline uint32_t
-ctx_over_RGBA8_full (uint32_t dst, uint32_t src)
+static inline unsigned long
+_ctx_ticks (void)
 {
-  uint32_t si_ga = (src & 0xff00ff00) >> 8;
-  uint32_t si_rb = src & 0x00ff00ff;
-  uint32_t si_a  = si_ga >> 16;
-  uint32_t rcov  = si_a^255;
-  uint32_t di_ga = (dst & 0xff00ff00) >> 8;
-  uint32_t di_rb = dst & 0x00ff00ff;
-  return
-     ((((si_rb * 255) + 0xff00ff + (di_rb * rcov)) & 0xff00ff00) >> 8)  |
-      (((si_ga * 255) + 0xff00ff + (di_ga * rcov)) & 0xff00ff00);
+  struct timeval measure_time;
+  gettimeofday (&measure_time, NULL);
+  return usecs (measure_time) - usecs (start_time);
 }
 
-static inline uint32_t
-ctx_over_RGBA8_2 (uint32_t dst, uint32_t si_ga, uint32_t si_rb, uint32_t si_a, uint32_t cov)
+unsigned long
+ctx_ticks (void)
 {
-  uint32_t rcov  = ((si_a * cov)/255)^255;
-  uint32_t di_ga = (dst & 0xff00ff00) >> 8;
-  uint32_t di_rb = dst & 0x00ff00ff;
-  return
-     ((((si_rb * cov) + 0xff00ff + (di_rb * rcov)) & 0xff00ff00) >> 8)  |
-      (((si_ga * cov) + 0xff00ff + (di_ga * rcov)) & 0xff00ff00);
+  _ctx_init_ticks ();
+  return _ctx_ticks ();
 }
 
-static inline uint32_t
-ctx_over_RGBA8_full_2 (uint32_t dst, uint32_t si_ga_full, uint32_t si_rb_full, uint32_t si_a)
+uint32_t ctx_ms (Ctx *ctx)
 {
-  uint32_t rcov = si_a^255;
-  uint32_t di_ga = ( dst & 0xff00ff00) >> 8;
-  uint32_t di_rb = dst & 0x00ff00ff;
-  return
-     ((((si_rb_full) + (di_rb * rcov)) & 0xff00ff00) >> 8)  |
-      (((si_ga_full) + (di_ga * rcov)) & 0xff00ff00);
+  return _ctx_ticks () / 1000;
 }
 
-static inline void ctx_span_set_color (uint32_t *dst_pix, uint32_t val, int count)
-{
-  if (count>0)
-  while(count--)
-    *dst_pix++=val;
-}
 
-inline static void
-ctx_rasterizer_generate_coverage_apply (CtxRasterizer *rasterizer,
-                                        int            minx,
-                                        int            maxx,
-                                        uint8_t       *coverage,
-                                        int            fill_rule,
-                                        CtxCovPath     comp)
-{
-  fill_rule = fill_rule == CTX_FILL_RULE_WINDING;
-  CtxSegment *entries = (CtxSegment*)(&rasterizer->edge_list.entries[0]);
-  int *edges  = rasterizer->edges;
-  int scanline        = rasterizer->scanline;
-  const int bpp             = rasterizer->format->bpp/8;
-  int active_edges    = rasterizer->active_edges;
-  int parity        = 0;
-  const uint32_t src_pix    = ((uint32_t*)rasterizer->color)[0];
-  const uint32_t si_ga = ((uint32_t*)rasterizer->color)[1];
-  const uint32_t si_rb = ((uint32_t*)rasterizer->color)[2];
-  const uint32_t si_ga_full = ((uint32_t*)rasterizer->color)[3];
-  const uint32_t si_rb_full = ((uint32_t*)rasterizer->color)[4];
-  const uint32_t si_a  = si_ga >> 16;
-
-  uint8_t *dst = ( (uint8_t *) rasterizer->buf) +
-         (rasterizer->blit_stride * (scanline / CTX_FULL_AA));
-  int accumulator_x=0;
-  uint8_t accumulated = 0;
-  for (int t = 0; t < active_edges -1;t++)
-    {
-      CtxSegment *segment = &entries[edges[t]];
-      UPDATE_PARITY;
+typedef enum _CtxFlags CtxFlags;
 
-       if (parity)
-        {
-          CtxSegment   *next_segment = &entries[edges[t+1]];
-          const int x0        = segment->val;
-          const int x1        = next_segment->val;
-          int graystart = x0 / (CTX_RASTERIZER_EDGE_MULTIPLIER*CTX_SUBDIV/256);
-          int grayend   = x1 / (CTX_RASTERIZER_EDGE_MULTIPLIER*CTX_SUBDIV/256);
-          int first     = graystart >> 8;
-          int last      = grayend   >> 8;
+enum _CtxFlags {
+   CTX_FLAG_DIRECT = (1<<0),
+};
 
-          if ((first < minx))
-          { 
-            first = minx;
-            graystart=0;
-          }
-          if ((last > maxx))
-          {
-            last = maxx;
-            grayend=255;
-          }
 
-          graystart = 255 - (graystart&0xff);
-          grayend   = (grayend & 0xff);
+int _ctx_max_threads = 1;
+int _ctx_enable_hash_cache = 1;
+#if CTX_SHAPE_CACHE
+extern int _ctx_shape_cache_enabled;
+#endif
 
-          if (accumulated)
-          {
-            if (accumulator_x == first)
-            {
-              graystart += accumulated;
-            }
-            else
-            {
-              uint32_t* dst_pix = (uint32_t*)(&dst[(accumulator_x*bpp)]);
-              switch (comp)
-              {
-                case CTX_COV_PATH_COPY:
-                  *dst_pix = ctx_lerp_RGBA8_2(*dst_pix, si_ga, si_rb, accumulated);
-                  break;
-                case CTX_COV_PATH_OVER:
-                  *dst_pix = ctx_over_RGBA8_2(*dst_pix, si_ga, si_rb, si_a, accumulated);
-                  break;
-                default:
-                  ctx_rasterizer_apply_coverage (rasterizer, (uint8_t*)dst_pix, accumulator_x, &accumulated, 
1);
-              }
-            }
-            accumulated = 0;
-          }
+#if CTX_THREADS
+static mtx_t _ctx_texture_mtx;
+#endif
 
-          if (first < last)
-          {
-            switch (comp)
-            {
-              case CTX_COV_PATH_COPY:
-            {
-              uint32_t* dst_pix = (uint32_t*)(&dst[(first *bpp)]);
-              *dst_pix = ctx_lerp_RGBA8_2(*dst_pix, si_ga, si_rb, graystart);
+void _ctx_texture_lock (void)
+{
+#if CTX_THREADS
+  mtx_lock (&_ctx_texture_mtx);
+#endif
+}
 
-              dst_pix++;
-              ctx_span_set_color (dst_pix, src_pix, last - first - 1);
-#if 0
-              for (int i = first + 1; i < last; i++)
-              {
-                *dst_pix = src_pix;
-                dst_pix++;
-              }
+void _ctx_texture_unlock (void)
+{
+#if CTX_THREADS
+  mtx_unlock (&_ctx_texture_mtx);
 #endif
-            }
-            break;
-              case CTX_COV_PATH_OVER:
-            {
-              uint32_t* dst_pix = (uint32_t*)(&dst[(first *bpp)]);
-              *dst_pix = ctx_over_RGBA8_2(*dst_pix, si_ga, si_rb, si_a, graystart);
-              dst_pix++;
-              for (int i = first + 1; i < last; i++)
-              {
-                *dst_pix = ctx_over_RGBA8_full_2(*dst_pix, si_ga_full, si_rb_full, si_a);
-                dst_pix++;
-              }
-            }
-            break;
-              case CTX_COV_PATH_COPY_FRAGMENT:
-            {
-              float u0 = 0; float v0 = 0;
-              float ud = 0; float vd = 0;
-              uint8_t gs = graystart;
-              ctx_RGBA8_source_copy_normal_fragment (rasterizer, &dst[(first * bpp)], NULL, first, &gs, 1);
-              ctx_init_uv (rasterizer, first+1, last-first-1, &u0, &v0, &ud, &vd);
-              rasterizer->fragment (rasterizer, u0, v0, &dst[(first+1)*bpp], last-first-1, ud, vd);
-            }
-            break;
-              case CTX_COV_PATH_OVER_FRAGMENT:
-            {
-              uint8_t gs = graystart;
-              ctx_RGBA8_source_over_normal_fragment (rasterizer, &dst[(first * bpp)], NULL, first, &gs, 1);
-              ctx_RGBA8_source_over_normal_full_cov_fragment (rasterizer,
-                                                     &dst[((first+1)*bpp)], NULL, first + 1, NULL, 
last-first-1);
-            }
-            break;
-              default:
-            {
-              uint8_t opaque[last-first];
-              memset (opaque, 255, sizeof (opaque));
-              opaque[0] = graystart;
-              ctx_rasterizer_apply_coverage (rasterizer,
-                                             &dst[(first * bpp)], first, opaque, last-first);
-            }
-            }
-            accumulated = grayend;
-          }
-          else if (first == last)
-          {
-            accumulated = (graystart-(255-grayend));
-          }
-          accumulator_x = last;
-        }
-   }
-
-   if (accumulated)
-   {
-     uint32_t* dst_pix = (uint32_t*)(&dst[(accumulator_x*bpp)]);
-     switch (comp)
-     {
-       case CTX_COV_PATH_COPY:
-         *dst_pix = ctx_lerp_RGBA8_2(*dst_pix, si_ga, si_rb, accumulated);
-         break;
-       case CTX_COV_PATH_OVER:
-         *dst_pix = ctx_over_RGBA8_2(*dst_pix, si_ga, si_rb, si_a, accumulated);
-         break;
-       default:
-         ctx_rasterizer_apply_coverage (rasterizer, (uint8_t*)dst_pix, accumulator_x, &accumulated, 1);
-     }
-   }
 }
 
-inline static int ctx_rasterizer_is_simple (CtxRasterizer *rasterizer)
-{
-  if (rasterizer->fast_aa == 0 ||
-      rasterizer->ending_edges ||
-      rasterizer->pending_edges)
-   return 0;
-  int *edges  = rasterizer->edges;
-  CtxSegment *segments = &((CtxSegment*)(rasterizer->edge_list.entries))[0];
 
-  int active_edges = rasterizer->active_edges;
-  for (int t = 0; t < active_edges -1;t++)
+void
+ctx_init (int *argc, char ***argv)
+{
+#if 0
+  if (!getenv ("CTX_VERSION"))
+  {
+    int i;
+    char *new_argv[*argc+3];
+    new_argv[0] = "ctx";
+    for (i = 0; i < *argc; i++)
     {
-      CtxSegment *segment0 = segments + edges[t];
-      CtxSegment *segment1 = segments + edges[t+1];
-      const int delta0    = segment0->delta;
-      const int delta1    = segment1->delta;
-      const int x0        = segment0->val;
-      const int x1        = segment1->val;
-      int x0_end   = x0 + delta0 * CTX_AA_HALFSTEP;
-      int x1_end   = x1 + delta1 * CTX_AA_HALFSTEP;
-      int x0_start = x0 - delta0 * CTX_AA_HALFSTEP2;
-      int x1_start = x1 - delta1 * CTX_AA_HALFSTEP2;
-      if (x1_end < x0_end   ||
-          x1_start < x0_end ||
-          x1_end < x0_start
-         )
-         return 0;
+      new_argv[i+1] = *argv[i];
     }
-  return 1;
+    new_argv[i+1] = NULL;
+    execvp (new_argv[0], new_argv);
+    // if this fails .. we continue normal startup
+    // and end up in self-hosted braille
+  }
+#endif
 }
 
+int ctx_count (Ctx *ctx)
+{
+  return ctx->drawlist.count;
+}
 
-inline static void
-ctx_rasterizer_generate_coverage_set2 (CtxRasterizer *rasterizer,
-                                         int            minx,
-                                         int            maxx,
-                                         uint8_t       *coverage,
-                                         int            fill_rule)
-{
-  fill_rule = fill_rule == CTX_FILL_RULE_WINDING;
-  CtxSegment *entries = (CtxSegment*)(&rasterizer->edge_list.entries[0]);
-  int *edges  = rasterizer->edges;
-  int scanline        = rasterizer->scanline;
-  int active_edges    = rasterizer->active_edges;
-  int parity        = 0;
-
-  coverage -= minx;
-
-  const int minx_ = minx * CTX_RASTERIZER_EDGE_MULTIPLIER * CTX_SUBDIV;
-  const int maxx_ = maxx * CTX_RASTERIZER_EDGE_MULTIPLIER * CTX_SUBDIV;
 
-  for (int t = 0; t < active_edges -1;t++)
-    {
-      CtxSegment   *segment = &entries[edges[t]];
-      UPDATE_PARITY;
+static int is_in_ctx (void)
+{
+  char buf[1024];
+  struct termios orig_attr;
+  struct termios raw;
+  tcgetattr (STDIN_FILENO, &orig_attr);
+  raw = orig_attr;
+  raw.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
+  raw.c_oflag &= ~(OPOST);
+  raw.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG);
+  raw.c_cc[VMIN] = 1; raw.c_cc[VTIME] = 0; /* 1 byte, no timer */
+  if (tcsetattr (STDIN_FILENO, TCSAFLUSH, &raw) < 0)
+    return 0;
+  fprintf (stderr, "\e[?200$p");
+  //tcflush(STDIN_FILENO, 1);
+#if !__COSMOPOLITAN__
+  tcdrain(STDIN_FILENO);
+#endif
+  int length = 0;
+  usleep (1000 * 60); // to account for possibly lowish latency ssh,
+                      // should be made configurable ; perhaps in
+                      // an env var
+  struct timeval tv = {0,0};
+  fd_set rfds;
+  
+  FD_ZERO(&rfds);
+  FD_SET(0, &rfds);
+  tv.tv_usec = 1000 * 5;
 
-       if (parity)
-        {
-          CtxSegment   *next_segment = &entries[edges[t+1]];
-          const int x0        = segment->val;
-          const int x1        = next_segment->val;
-          const int delta0    = segment->delta;
-          const int delta1    = next_segment->delta;
+  for (int n = 0; select(1, &rfds, NULL, NULL, &tv) && n < 20; n++)
+  {
+    length += read (STDIN_FILENO, &buf[length], 1);
+  }
+  tcsetattr (STDIN_FILENO, TCSAFLUSH, &orig_attr);
+  if (length == -1)
+  {
+    return 0;
+  }
+  char *semi = strchr (buf, ';');
+  buf[length]=0;
+  if (semi &&  semi[1] == '2')
+  {
+    return 1;
+  }
+  return 0;
+}
 
-          int x0_start = x0 - delta0 * CTX_AA_HALFSTEP2;
-          int x1_start = x1 - delta1 * CTX_AA_HALFSTEP2;
-          int x0_end   = x0 + delta0 * CTX_AA_HALFSTEP;
-          int x1_end   = x1 + delta1 * CTX_AA_HALFSTEP;
+extern int _ctx_damage_control;
 
-          int graystart = x0 / (CTX_RASTERIZER_EDGE_MULTIPLIER*CTX_SUBDIV/256);
-          int grayend   = x1 / (CTX_RASTERIZER_EDGE_MULTIPLIER*CTX_SUBDIV/256);
-          int first     = graystart >> 8;
-          int last      = grayend   >> 8;
+static void ctx_list_backends()
+{
+    fprintf (stderr, "possible values for CTX_BACKEND:\n");
+    fprintf (stderr, " ctx");
+#if CTX_SDL
+    fprintf (stderr, " SDL");
+#endif
+#if CTX_FB
+    fprintf (stderr, " fb");
+    fprintf (stderr, " drm");
+#endif
+    fprintf (stderr, " term");
+    fprintf (stderr, " termimg");
+    fprintf (stderr, "\n");
+}
 
-          first = ctx_maxi (first, minx);
-          last  = ctx_mini (last, maxx);
+Ctx *ctx_new_ui (int width, int height)
+{
+#if CTX_TILED
+  if (getenv ("CTX_DAMAGE_CONTROL"))
+  {
+    const char * val = getenv ("CTX_DAMAGE_CONTROL");
+    if (!strcmp (val, "0") ||
+        !strcmp (val, "off"))
+      _ctx_damage_control = 0;
+    else
+      _ctx_damage_control = 1;
+  }
+#endif
 
-          if (first < last)
-          {
-            int pre = 1;
-            int post = 1;
+  if (getenv ("CTX_HASH_CACHE"))
+  {
+    const char * val = getenv ("CTX_HASH_CACHE");
+    if (!strcmp (val, "0"))
+      _ctx_enable_hash_cache = 0;
+    if (!strcmp (val, "off"))
+      _ctx_enable_hash_cache = 0;
+  }
+#if CTX_SHAPE_CACHE
+  if (getenv ("CTX_SHAPE_CACHE"))
+  {
+    const char * val = getenv ("CTX_SHAPE_CACHE");
+    if (!strcmp (val, "0"))
+      _ctx_shape_cache_enabled = 0;
+    if (!strcmp (val, "off"))
+      _ctx_shape_cache_enabled = 0;
+  }
+#endif
 
-            if (abs(delta0) < CTX_RASTERIZER_AA_SLOPE_LIMIT3_FAST_AA)
-            {
-              graystart = 255 - (graystart&0xff);
-              coverage[first] += graystart;
-            }
-            else
-            {
-              int u0 = x0_start;
-              int u1 = x0_end;
-              if (u0 > u1)
-              {
-                 int tmp = u0;
-                 u0 = u1;u1=tmp;
-              }
-              u0 = ctx_maxi (u0, minx_);
-              u1 = ctx_mini (u1, maxx_);
-              u1 = ctx_maxi (u1, minx_);
-              u0 = ctx_mini (u0, maxx_);
+  if (getenv ("CTX_THREADS"))
+  {
+    int val = atoi (getenv ("CTX_THREADS"));
+    _ctx_max_threads = val;
+  }
+  else
+  {
+    _ctx_max_threads = 2;
+#ifdef _SC_NPROCESSORS_ONLN
+    _ctx_max_threads = sysconf (_SC_NPROCESSORS_ONLN) / 2;
+#endif
+  }
   
-              int us = u0 / (CTX_RASTERIZER_EDGE_MULTIPLIER*CTX_SUBDIV);
-              int count = 0;
+#if CTX_THREADS
+  mtx_init (&_ctx_texture_mtx, mtx_plain);
+#endif
 
-              int mod = (255-(u0 / (CTX_RASTERIZER_EDGE_MULTIPLIER*CTX_SUBDIV/256) % 256)) *
-                         (CTX_RASTERIZER_EDGE_MULTIPLIER*CTX_SUBDIV/255);
-              int sum = ((u1-u0+CTX_RASTERIZER_EDGE_MULTIPLIER * CTX_SUBDIV)/255);
+  if (_ctx_max_threads < 1) _ctx_max_threads = 1;
+  if (_ctx_max_threads > CTX_MAX_THREADS) _ctx_max_threads = CTX_MAX_THREADS;
 
-              for (int u = u0; u < u1; u+= CTX_RASTERIZER_EDGE_MULTIPLIER*CTX_SUBDIV)
-              {
-                coverage[us + count] = (u - u0 + mod) / sum;
-                count++;
-              }
-              pre = (us+count-1)-first+1;
-            }
-  
-            if (abs(delta1) < CTX_RASTERIZER_AA_SLOPE_LIMIT3_FAST_AA)
-            {
-               grayend   = (grayend & 0xff);
-               coverage[last] += grayend;
-            }
-            else
-            {
-              int u0 = x1_start;
-              int u1 = x1_end;
-              if (u0 > u1)
-              {
-                 int tmp = u0;
-                 u0 = u1;u1=tmp;
-              }
-              u0 = ctx_maxi (u0, minx_);
-              u1 = ctx_mini (u1, maxx_);
-              u1 = ctx_maxi (u1, minx_);
-              u0 = ctx_mini (u0, maxx_);
-              int us = u0 / (CTX_RASTERIZER_EDGE_MULTIPLIER*CTX_SUBDIV);
-              int count = 0;
-              int mod = ((255-(u0 / (CTX_RASTERIZER_EDGE_MULTIPLIER*CTX_SUBDIV/256) % 256)+64) *
-                    (CTX_RASTERIZER_EDGE_MULTIPLIER*CTX_SUBDIV/255));
-              int sum = ((u1-u0+CTX_RASTERIZER_EDGE_MULTIPLIER * CTX_SUBDIV * 1.25)/255);
-              for (int u = u0; u < u1; u+= CTX_RASTERIZER_EDGE_MULTIPLIER*CTX_SUBDIV)
-              {
-                coverage[us + count] = 255-((u - u0 + mod)/ sum);
-                count++;
-              }
-              post = last-us+1;
-            }
-            for (int i = first + pre; i <= last - post; i++)
-              coverage[i] = 255;
-          }
-          else if (first == last)
-          {
-            graystart = 255 - (graystart&0xff);
-            grayend   = (grayend & 0xff);
-            coverage[last]+=(graystart-(255-grayend));
-          }
-        }
-   }
-}
+  //fprintf (stderr, "ctx using %i threads\n", _ctx_max_threads);
+  const char *backend = getenv ("CTX_BACKEND");
 
+  if (backend && !strcmp (backend, ""))
+    backend = NULL;
+  if (backend && !strcmp (backend, "auto"))
+    backend = NULL;
+  if (backend && !strcmp (backend, "list"))
+  {
+    ctx_list_backends ();
+    exit (-1);
+  }
 
-inline static void
-ctx_rasterizer_generate_coverage_apply2 (CtxRasterizer *rasterizer,
-                                         int            minx,
-                                         int            maxx,
-                                         uint8_t       *coverage,
-                                         int            fill_rule,
-                                         CtxCovPath     comp)
-{
-  fill_rule = fill_rule == CTX_FILL_RULE_WINDING;
-  CtxSegment *entries = (CtxSegment*)(&rasterizer->edge_list.entries[0]);
-  int *edges          = rasterizer->edges;
-  int  scanline       = rasterizer->scanline;
-  const int  bpp      = rasterizer->format->bpp/8;
-  int  active_edges   = rasterizer->active_edges;
-  int  parity         = 0;
-
-  const uint32_t src_pix    = ((uint32_t*)rasterizer->color)[0];
-  const uint32_t si_ga      = ((uint32_t*)rasterizer->color)[1];
-  const uint32_t si_rb      = ((uint32_t*)rasterizer->color)[2];
-  const uint32_t si_ga_full = ((uint32_t*)rasterizer->color)[3];
-  const uint32_t si_rb_full = ((uint32_t*)rasterizer->color)[4];
-  const uint32_t si_a  = src_pix >> 24;
-
-  uint8_t *dst = ( (uint8_t *) rasterizer->buf) +
-         (rasterizer->blit_stride * (scanline / CTX_FULL_AA));
+  Ctx *ret = NULL;
 
-  coverage -= minx;
+  /* we do the query on auto but not on directly set ctx
+   *
+   */
+  if ((backend && !strcmp(backend, "ctx")) ||
+      (backend == NULL && is_in_ctx ()))
+  {
+    if (!backend || !strcmp (backend, "ctx"))
+    {
+      // full blown ctx protocol - in terminal or standalone
+      ret = ctx_new_ctx (width, height);
+    }
+  }
 
-  const int minx_ = minx * CTX_RASTERIZER_EDGE_MULTIPLIER * CTX_SUBDIV;
-  const int maxx_ = maxx * CTX_RASTERIZER_EDGE_MULTIPLIER * CTX_SUBDIV;
+#if CTX_SDL
+  if (!ret && getenv ("DISPLAY"))
+  {
+    if ((backend==NULL) || (!strcmp (backend, "SDL")))
+      ret = ctx_new_sdl (width, height);
+  }
+#endif
 
-  int accumulated_x0 = 65538;
-  int accumulated_x1 = 65536;
+#if CTX_FB
+  if (!ret && !getenv ("DISPLAY"))
+  {
+    if ((backend==NULL) || (!strcmp (backend, "drm")))
+    ret = ctx_new_fb (width, height, 1);
 
-  for (int t = 0; t < active_edges -1;t++)
+    if (!ret)
     {
-      CtxSegment   *segment = &entries[edges[t]];
-      UPDATE_PARITY;
-
-       if (parity)
-        {
-          CtxSegment   *next_segment = &entries[edges[t+1]];
-          const int x0        = segment->val;
-          const int x1        = next_segment->val;
-          const int delta0    = segment->delta;
-          const int delta1    = next_segment->delta;
+      if ((backend==NULL) || (!strcmp (backend, "fb")))
+        ret = ctx_new_fb (width, height, 0);
+    }
+  }
+#endif
 
-          int x0_start = x0 - delta0 * CTX_AA_HALFSTEP2;
-          int x1_start = x1 - delta1 * CTX_AA_HALFSTEP2;
-          int x0_end   = x0 + delta0 * CTX_AA_HALFSTEP;
-          int x1_end   = x1 + delta1 * CTX_AA_HALFSTEP;
+#if CTX_RASTERIZER
+  // braille in terminal
+  if (!ret)
+  {
+    if ((backend==NULL) || (!strcmp (backend, "term")))
+    ret = ctx_new_term (width, height);
+  }
+  if (!ret)
+  {
+    if ((backend==NULL) || (!strcmp (backend, "termimg")))
+    ret = ctx_new_termimg (width, height);
+  }
+#endif
+  if (!ret)
+  {
+    fprintf (stderr, "no interactive ctx backend\n");
+    ctx_list_backends ();
+    exit (2);
+  }
+  ctx_get_event (ret); // enables events
+  return ret;
+}
+#else
+void _ctx_texture_unlock (void)
+{
+}
+void _ctx_texture_lock (void)
+{
+}
 
-          int graystart = x0 / (CTX_RASTERIZER_EDGE_MULTIPLIER*CTX_SUBDIV/256);
-          int grayend   = x1 / (CTX_RASTERIZER_EDGE_MULTIPLIER*CTX_SUBDIV/256);
-          int first     = graystart >> 8;
-          int last      = grayend   >> 8;
+#endif
+void _ctx_resized (Ctx *ctx, int width, int height, long time);
 
-          first = ctx_maxi (first, minx);
-          last = ctx_mini (last, maxx);
-          graystart = 255 - (graystart&0xff);
-          grayend   = (grayend & 0xff);
+void ctx_set_size (Ctx *ctx, int width, int height)
+{
+#if CTX_EVENTS
+  if (ctx->events.width != width || ctx->events.height != height)
+  {
+    ctx->events.width = width;
+    ctx->events.height = height;
+    _ctx_resized (ctx, width, height, 0);
+  }
+#endif
+}
 
-          if (first < last)
-          {
-            int pre = 1;
-            int post = 1;
+#if CTX_EVENTS
+typedef struct CtxIdleCb {
+  int (*cb) (Ctx *ctx, void *idle_data);
+  void *idle_data;
 
-          if (abs(delta0) < CTX_RASTERIZER_AA_SLOPE_LIMIT3_FAST_AA)
-          {
-             coverage[first] += graystart;
+  void (*destroy_notify)(void *destroy_data);
+  void *destroy_data;
 
-            accumulated_x1 = first;
-            accumulated_x0 = ctx_mini (accumulated_x0, first);
-          }
-          else
-          {
-            int u0 = x0_start;
-            int u1 = x0_end;
-            if (u0 > u1)
-            {
-               int tmp = u0;
-               u0 = u1;u1=tmp;
-            }
-            u0 = ctx_maxi (u0, minx_);
-            u1 = ctx_mini (u1, maxx_);
-            u1 = ctx_maxi (u1, minx_);
-            u0 = ctx_mini (u0, maxx_);
-
-            int mod = (255-(u0 / (CTX_RASTERIZER_EDGE_MULTIPLIER*CTX_SUBDIV/256) % 256)) *
-                    (CTX_RASTERIZER_EDGE_MULTIPLIER*CTX_SUBDIV/255);
-            int sum = ((u1-u0+CTX_RASTERIZER_EDGE_MULTIPLIER * CTX_SUBDIV)/255);
-
-            int us = u0 / (CTX_RASTERIZER_EDGE_MULTIPLIER*CTX_SUBDIV);
-            int count = 0;
-            for (int u = u0; u < u1; u+= CTX_RASTERIZER_EDGE_MULTIPLIER*CTX_SUBDIV)
-            {
-              coverage[us + count] = (u - u0 + mod) / sum;
-              count++;
-            }
-            pre = (us+count-1)-first+1;
+  int   ticks_full;
+  int   ticks_remaining;
+  int   is_idle;
+  int   id;
+} CtxIdleCb;
 
-          if (
-              accumulated_x1-accumulated_x0>=0 &&
-              us - accumulated_x1 > 4
-                          )
-          {
-             switch (comp)
-             {
-                case CTX_COV_PATH_OVER:
-                {
-                  uint32_t *dst_i = (uint32_t*)&dst[((accumulated_x0) * bpp)];
-                  for (int i = 0; i < accumulated_x1-accumulated_x0+1; i++)
-                    {
-                      *dst_i = ctx_over_RGBA8_2 (*dst_i, si_ga, si_rb, si_a, coverage[accumulated_x0+i]);
-                      dst_i++;
-                    }
-                }
-                break;
-                case CTX_COV_PATH_COPY:
-                {
-                  uint32_t *dst_i = (uint32_t*)&dst[((accumulated_x0) * bpp)];
-                  for (int i = 0; i < accumulated_x1-accumulated_x0+1; i++)
-                  {
-                    *dst_i = ctx_lerp_RGBA8_2 (*dst_i, si_ga, si_rb, coverage[accumulated_x0+i]);
-                    dst_i++;
-                  }
-                }
-                  break;
-                default:
-                ctx_rasterizer_apply_coverage (rasterizer,
-                          &dst[((accumulated_x0) * bpp)],
-                          accumulated_x0,
-                          &coverage[accumulated_x0],
-                          accumulated_x1-accumulated_x0+1);
-             }
-          }
+void _ctx_events_init (Ctx *ctx)
+{
+  CtxEvents *events = &ctx->events;
+  _ctx_init_ticks ();
+  events->tap_delay_min  = 40;
+  events->tap_delay_max  = 800;
+  events->tap_delay_max  = 8000000; /* quick reflexes needed making it hard for some is an argument against 
very short values  */
 
-            accumulated_x0 = ctx_mini (accumulated_x0, us);
-            accumulated_x1 = us + count - 1;
-          }
+  events->tap_delay_hold = 1000;
+  events->tap_hysteresis = 32;  /* XXX: should be ppi dependent */
+}
 
-   if (accumulated_x1-accumulated_x0>=0)
-          {
-             switch (comp)
-             {
-                case CTX_COV_PATH_OVER:
-                {
-                  uint32_t *dst_i = (uint32_t*)&dst[((accumulated_x0) * bpp)];
-                  for (int i = 0; i < accumulated_x1-accumulated_x0+1; i++)
-                    {
-                      *dst_i = ctx_over_RGBA8_2 (*dst_i, si_ga, si_rb, si_a, coverage[accumulated_x0+i]);
-                      dst_i++;
-                    }
-                }
-                break;
-                case CTX_COV_PATH_COPY:
-                {
-                  uint32_t *dst_i = (uint32_t*)&dst[((accumulated_x0) * bpp)];
-                  for (int i = 0; i < accumulated_x1-accumulated_x0+1; i++)
-                  {
-                    *dst_i = ctx_lerp_RGBA8_2 (*dst_i, si_ga, si_rb, coverage[accumulated_x0+i]);
-                    dst_i++;
-                  }
-                }
-                  break;
-                default:
-                ctx_rasterizer_apply_coverage (rasterizer,
-                          &dst[((accumulated_x0) * bpp)],
-                          accumulated_x0,
-                          &coverage[accumulated_x0],
-                          accumulated_x1-accumulated_x0+1);
-             }
-             accumulated_x0 = 65538;
-             accumulated_x1 = 65536;
-          }
 
-          if (abs(delta1) < CTX_RASTERIZER_AA_SLOPE_LIMIT3_FAST_AA)
-          {
-             coverage[last] += grayend;
-             accumulated_x1 = last;
-             accumulated_x0 = last;
-          }
-          else
-          {
-            int u0 = x1_start;
-            int u1 = x1_end;
-            if (u0 > u1)
-            {
-               int tmp = u0;
-               u0 = u1;u1=tmp;
-            }
-            u0 = ctx_maxi (u0, minx_);
-            u1 = ctx_mini (u1, maxx_);
-            u1 = ctx_maxi (u1, minx_);
-            u0 = ctx_mini (u0, maxx_);
-            int us = u0 / (CTX_RASTERIZER_EDGE_MULTIPLIER*CTX_SUBDIV);
-            int count = 0;
-
-            int mod = ((255-(u0 / (CTX_RASTERIZER_EDGE_MULTIPLIER*CTX_SUBDIV/256) % 256)+64) *
-                    (CTX_RASTERIZER_EDGE_MULTIPLIER*CTX_SUBDIV/255));
-            int sum = ((u1-u0+CTX_RASTERIZER_EDGE_MULTIPLIER * CTX_SUBDIV * 1.25)/255);
-
-            for (int u = u0; u < u1; u+= CTX_RASTERIZER_EDGE_MULTIPLIER*CTX_SUBDIV)
-            {
-                coverage[us + count] = 255-((u - u0 + mod)/ sum);
-              count++;
-            }
-            post = last-us+1;
+void _ctx_idle_iteration (Ctx *ctx)
+{
+  static unsigned long prev_ticks = 0;
+  CtxList *l;
+  CtxList *to_remove = NULL;
+  unsigned long ticks = _ctx_ticks ();
+  unsigned long tick_delta = (prev_ticks == 0) ? 0 : ticks - prev_ticks;
+  prev_ticks = ticks;
 
-            accumulated_x1 = us + count;
-            accumulated_x0 = us;
-          }
-            switch (comp)
-            {
-              case CTX_COV_PATH_COPY:
-            {
-              uint32_t* dst_pix = (uint32_t*)(&dst[(first *bpp)]);
-              dst_pix+=pre;
-              ctx_span_set_color (dst_pix, src_pix, last-first-pre-post + 1);
-            }
-            break;
-              case CTX_COV_PATH_OVER:
-            {
-              uint32_t* dst_pix = (uint32_t*)(&dst[(first *bpp)]);
-              dst_pix+=pre;
-              for (int i = first + pre; i <= last - post; i++)
-              {
-                *dst_pix = ctx_over_RGBA8_full_2(*dst_pix, si_ga_full, si_rb_full, si_a);
-                dst_pix++;
-              }
-            }
-            break;
-              case CTX_COV_PATH_COPY_FRAGMENT:
-              {
-              int width = last-first-pre-post+1;
-            if (width>0)
-            {
-              {
-                float u0 = 0; float v0 = 0;
-                float ud = 0; float vd = 0;
-                ctx_init_uv (rasterizer, first+pre, width, &u0, &v0, &ud, &vd);
-                rasterizer->fragment (rasterizer, u0, v0, &dst[(first+pre)*bpp],                             
         width, ud, vd);
-              }
-            }
-              }
-            break;
-              case CTX_COV_PATH_OVER_FRAGMENT:
-              {
-                int width = last-first-pre-post+1;
-                if (width>0)
-                ctx_RGBA8_source_over_normal_full_cov_fragment (rasterizer,
-                               &dst[((first+pre)*bpp)],
-                               NULL,
-                               first + pre,
-                               NULL,
-                               width);
-              }
-              break;
-              default:
-              {
-                int width = last-first-pre-post+1;
-                if (width > 0)
-                {
-                uint8_t opaque[width];
-                memset (opaque, 255, sizeof (opaque));
-                ctx_rasterizer_apply_coverage (rasterizer,
-                            &dst[((first + pre) * bpp)],
-                            first + pre,
-                            opaque,
-                            width);
-                }
-              }
-            }
-            //}
-          }
-          else if (first == last)
-          {
-            coverage[last]+=(graystart-(255-grayend));
+  if (!ctx->events.idles)
+  {
+    return;
+  }
+  for (l = ctx->events.idles; l; l = l->next)
+  {
+    CtxIdleCb *item = l->data;
 
-            accumulated_x1 = last;
-            accumulated_x0 = ctx_mini (accumulated_x0, last);
-          }
-        }
-   }
+    if (item->ticks_remaining >= 0)
+      item->ticks_remaining -= tick_delta;
 
-   if (accumulated_x1-accumulated_x0>=0)
-   {
-             switch (comp)
-             {
-                case CTX_COV_PATH_OVER:
-                {
-                  uint32_t *dst_i = (uint32_t*)&dst[((accumulated_x0) * bpp)];
-                  for (int i = 0; i < accumulated_x1-accumulated_x0+1; i++)
-                    {
-                      *dst_i = ctx_over_RGBA8_2 (*dst_i, si_ga, si_rb, si_a, coverage[accumulated_x0+i]);
-                      dst_i++;
-                    }
-                }
-                break;
-                case CTX_COV_PATH_COPY:
-                {
-                  uint32_t *dst_i = (uint32_t*)&dst[((accumulated_x0) * bpp)];
-                  for (int i = 0; i < accumulated_x1-accumulated_x0+1; i++)
-                  {
-                    *dst_i = ctx_lerp_RGBA8_2 (*dst_i, si_ga, si_rb, coverage[accumulated_x0+i]);
-                    dst_i++;
-                  }
-                }
-                  break;
-                default:
-                ctx_rasterizer_apply_coverage (rasterizer,
-                          &dst[((accumulated_x0) * bpp)],
-                          accumulated_x0,
-                          &coverage[accumulated_x0],
-                          accumulated_x1-accumulated_x0+1);
-             }
-   }
+    if (item->ticks_remaining < 0)
+    {
+      if (item->cb (ctx, item->idle_data) == 0)
+        ctx_list_prepend (&to_remove, item);
+      else
+        item->ticks_remaining = item->ticks_full;
+    }
+  }
+  for (l = to_remove; l; l = l->next)
+  {
+    CtxIdleCb *item = l->data;
+    if (item->destroy_notify)
+      item->destroy_notify (item->destroy_data);
+    ctx_list_remove (&ctx->events.idles, l->data);
+  }
 }
 
-#undef CTX_EDGE_Y0
-#undef CTX_EDGE
 
-static inline void
-ctx_rasterizer_reset (CtxRasterizer *rasterizer)
+void ctx_add_key_binding_full (Ctx *ctx,
+                           const char *key,
+                           const char *action,
+                           const char *label,
+                           CtxCb       cb,
+                           void       *cb_data,
+                           CtxDestroyNotify destroy_notify,
+                           void       *destroy_data)
 {
-  rasterizer->pending_edges   = 0;
-  rasterizer->active_edges    = 0;
-  rasterizer->has_shape       = 0;
-  rasterizer->has_prev        = 0;
-  rasterizer->edge_list.count = 0; // ready for new edges
-  rasterizer->edge_pos        = 0;
-  rasterizer->scanline        = 0;
-  if (CTX_LIKELY(!rasterizer->preserve))
+  CtxEvents *events = &ctx->events;
+  if (events->n_bindings +1 >= CTX_MAX_KEYBINDINGS)
   {
-    rasterizer->scan_min      = 5000;
-    rasterizer->scan_max      = -5000;
-    rasterizer->col_min       = 5000;
-    rasterizer->col_max       = -5000;
+    fprintf (stderr, "warning: binding overflow\n");
+    return;
   }
-  //rasterizer->comp_op       = NULL; // keep comp_op cached 
-  //     between rasterizations where rendering attributes are
-  //     nonchanging
+  events->bindings[events->n_bindings].nick = strdup (key);
+  strcpy (events->bindings[events->n_bindings].nick, key);
+
+  if (action)
+    events->bindings[events->n_bindings].command = action ? strdup (action) : NULL;
+  if (label)
+    events->bindings[events->n_bindings].label = label ? strdup (label) : NULL;
+  events->bindings[events->n_bindings].cb = cb;
+  events->bindings[events->n_bindings].cb_data = cb_data;
+  events->bindings[events->n_bindings].destroy_notify = destroy_notify;
+  events->bindings[events->n_bindings].destroy_data = destroy_data;
+  events->n_bindings++;
 }
 
-static void
-ctx_rasterizer_rasterize_edges (CtxRasterizer *rasterizer, const int fill_rule 
-#if CTX_SHAPE_CACHE
-                                ,CtxShapeEntry *shape
-#endif
-                               )
+void ctx_add_key_binding (Ctx *ctx,
+                          const char *key,
+                          const char *action,
+                          const char *label,
+                          CtxCb       cb,
+                          void       *cb_data)
 {
-  uint8_t *dst = ( (uint8_t *) rasterizer->buf);
+  ctx_add_key_binding_full (ctx, key, action, label, cb, cb_data, NULL, NULL);
+}
 
-  const int real_aa = rasterizer->aa;
+void ctx_clear_bindings (Ctx *ctx)
+{
+  CtxEvents *events = &ctx->events;
+  int i;
+  for (i = 0; events->bindings[i].nick; i ++)
+  {
+    if (events->bindings[i].destroy_notify)
+      events->bindings[i].destroy_notify (events->bindings[i].destroy_data);
+    free (events->bindings[i].nick);
+    if (events->bindings[i].command)
+      free (events->bindings[i].command);
+    if (events->bindings[i].label)
+      free (events->bindings[i].label);
+  }
+  memset (&events->bindings, 0, sizeof (events->bindings));
+  events->n_bindings = 0;
+}
 
-  int scan_start = rasterizer->blit_y * CTX_FULL_AA;
-  int scan_end   = scan_start + rasterizer->blit_height * CTX_FULL_AA;
-  const int blit_width = rasterizer->blit_width;
-  const int blit_max_x = rasterizer->blit_x + blit_width;
-  int minx       = rasterizer->col_min / CTX_SUBDIV - rasterizer->blit_x;
-  int maxx       = (rasterizer->col_max + CTX_SUBDIV-1) / CTX_SUBDIV - rasterizer->blit_x;
-  const int blit_stride = rasterizer->blit_stride;
-  const CtxCovPath comp = rasterizer->comp;
+static void
+ctx_collect_events (CtxEvent *event, void *data, void *data2);
+static void _ctx_bindings_key_press (CtxEvent *event, void *data1, void *data2)
+{
+  Ctx *ctx = event->ctx;
+  CtxEvents *events = &ctx->events;
+  int i;
+  int handled = 0;
 
-  rasterizer->prev_active_edges = -1;
-  if (
-#if CTX_SHAPE_CACHE
-    !shape &&
-#endif
-    maxx > blit_max_x - 1)
-    { maxx = blit_max_x - 1; }
-
-  minx = ctx_maxi (rasterizer->state->gstate.clip_min_x, minx);
-  maxx = ctx_mini (rasterizer->state->gstate.clip_max_x, maxx);
-  minx = ctx_maxi (0, minx); // redundant?
-  if (minx >= maxx)
-    {
-      ctx_rasterizer_reset (rasterizer);
-      return;
-    }
-#if CTX_SHAPE_CACHE
-  uint8_t _coverage[shape?2:maxx-minx+1];
-#else
-  uint8_t _coverage[maxx-minx+1];
-#endif
-  uint8_t *coverage = &_coverage[0];
-
-  int coverage_size = 
-#if CTX_SHAPE_CACHE
-                  shape?shape->width:
-#endif
-                  sizeof (_coverage);
-
-#if CTX_SHAPE_CACHE
-  if (shape)
-    {
-      coverage = &shape->data[0];
-    }
-#endif
-  //ctx_assert (coverage);
-  rasterizer->scan_min -= (rasterizer->scan_min % CTX_FULL_AA);
-#if CTX_SHAPE_CACHE
-  if (shape)
-    {
-      scan_start = rasterizer->scan_min;
-      scan_end   = rasterizer->scan_max;
-    }
-  else
-#endif
-    {
-      if (rasterizer->scan_min > scan_start)
-        {
-          dst += (rasterizer->blit_stride * (rasterizer->scan_min-scan_start) / CTX_FULL_AA);
-          scan_start = rasterizer->scan_min;
-        }
-      scan_end = ctx_mini (rasterizer->scan_max, scan_end);
-    }
-  if (CTX_UNLIKELY(rasterizer->state->gstate.clip_min_y * CTX_FULL_AA > scan_start ))
-    { 
-       dst += (rasterizer->blit_stride * (rasterizer->state->gstate.clip_min_y * CTX_FULL_AA -scan_start) / 
CTX_FULL_AA);
-       scan_start = rasterizer->state->gstate.clip_min_y * CTX_FULL_AA; 
-    }
-  scan_end = ctx_mini (rasterizer->state->gstate.clip_max_y * CTX_FULL_AA, scan_end);
-  if (CTX_UNLIKELY(scan_start > scan_end ||
-      (scan_start > (rasterizer->blit_y + rasterizer->blit_height) * CTX_FULL_AA) ||
-      (scan_end < (rasterizer->blit_y) * CTX_FULL_AA)))
-  { 
-    /* not affecting this rasterizers scanlines */
-    ctx_rasterizer_reset (rasterizer);
-    return;
-  }
-
-  rasterizer->horizontal_edges = 0;
-  {
-    rasterizer->needs_aa3  = 0;
-    rasterizer->needs_aa5  = 0;
-    rasterizer->needs_aa15 = 0;
-    ctx_rasterizer_sort_edges (rasterizer);
-    rasterizer->scanline = scan_start;
-    ctx_rasterizer_feed_edges (rasterizer, 0); 
-
-      int avoid_direct = (0 
-#if CTX_ENABLE_CLIP
-         || rasterizer->clip_buffer
-#endif
-#if CTX_ENABLE_SHADOW_BLUR
-         || rasterizer->in_shadow
-#endif
-#if CTX_SHAPE_CACHE
-         || shape != NULL
-#endif
-         );
-
-  for (; rasterizer->scanline <= scan_end;)
-    {
-
-      int needs_full_aa =
-          ( (rasterizer->horizontal_edges!=0) 
-          | (rasterizer->active_edges != rasterizer->prev_active_edges)
-          | (rasterizer->active_edges + rasterizer->pending_edges == rasterizer->ending_edges)
-          );
-
-    if ((needs_full_aa))
-    {
-        int increment = CTX_FULL_AA/real_aa;
-        memset (coverage, 0, coverage_size);
-        for (int i = 0; i < real_aa; i++)
-        {
-          ctx_rasterizer_feed_edges (rasterizer, 0);
-          ctx_rasterizer_generate_coverage (rasterizer, minx, maxx, coverage, fill_rule, real_aa);
-          ctx_rasterizer_increment_edges (rasterizer, increment);
-        }
-    }
-    else if (! avoid_direct & (rasterizer->needs_aa3 == 0))
-    {
-      ctx_rasterizer_increment_edges (rasterizer, CTX_AA_HALFSTEP2);
-      ctx_rasterizer_feed_edges (rasterizer, 0);
-
-      ctx_rasterizer_generate_coverage_apply (rasterizer, minx, maxx, coverage, fill_rule,
-                      comp);
-      ctx_rasterizer_increment_edges (rasterizer, CTX_AA_HALFSTEP);
-
-      dst += blit_stride;
-      rasterizer->prev_active_edges = rasterizer->active_edges;
-      continue;
-    }
-    else if (avoid_direct & (rasterizer->needs_aa3 == 0))
-    {
-      ctx_rasterizer_increment_edges (rasterizer, CTX_AA_HALFSTEP2);
-      ctx_rasterizer_feed_edges (rasterizer, 0);
-
-      memset (coverage, 0, coverage_size);
-      ctx_rasterizer_generate_coverage_set (rasterizer, minx, maxx, coverage, fill_rule);
-      ctx_rasterizer_increment_edges (rasterizer, CTX_AA_HALFSTEP);
-    }
-    else if (ctx_rasterizer_is_simple (rasterizer))
+  for (i = events->n_bindings-1; i>=0; i--)
+    if (!strcmp (events->bindings[i].nick, event->string))
     {
-      ctx_rasterizer_increment_edges (rasterizer, CTX_AA_HALFSTEP2);
-      ctx_rasterizer_feed_edges (rasterizer, 1);
-      memset (coverage, 0, coverage_size);
-      if (!avoid_direct)
+      if (events->bindings[i].cb)
       {
-
-        ctx_rasterizer_generate_coverage_apply2 (rasterizer, minx, maxx, coverage, fill_rule,
-                      comp);
-        ctx_rasterizer_increment_edges (rasterizer, CTX_AA_HALFSTEP);
-
-        dst += blit_stride;
-        rasterizer->prev_active_edges = rasterizer->active_edges;
-        continue;
+        events->bindings[i].cb (event, events->bindings[i].cb_data, NULL);
+        if (event->stop_propagate)
+          return;
+        handled = 1;
       }
-      ctx_rasterizer_generate_coverage_set2 (rasterizer, minx, maxx, coverage, fill_rule);
-      ctx_rasterizer_increment_edges (rasterizer, CTX_AA_HALFSTEP);
     }
-    else 
+  if (!handled)
+  for (i = events->n_bindings-1; i>=0; i--)
+    if (!strcmp (events->bindings[i].nick, "unhandled"))
     {
-      int aa = 3;
-      if (rasterizer->needs_aa5 && real_aa >=5)
-      {
-         aa = 5;
-         if (rasterizer->needs_aa15 && real_aa >=15)
-           aa = 15;
-      }
-      int scanline_increment = 15/aa;
-
-      memset (coverage, 0, coverage_size);
-      for (int i = 0; i < CTX_FULL_AA; i+= scanline_increment)
+      if (events->bindings[i].cb)
       {
-        ctx_rasterizer_feed_edges (rasterizer, 0);
-        ctx_rasterizer_generate_coverage (rasterizer, minx, maxx, coverage, fill_rule, aa);
-        ctx_rasterizer_increment_edges (rasterizer, scanline_increment);
+        events->bindings[i].cb (event, events->bindings[i].cb_data, NULL);
+        if (event->stop_propagate)
+          return;
       }
     }
+  ctx_collect_events (event, data1, data2);
+}
 
-  ctx_coverage_post_process (rasterizer, minx, maxx, coverage - minx,
-                  NULL, NULL);
-#if CTX_SHAPE_CACHE
-  if (shape == NULL)
-#endif
+CtxBinding *ctx_get_bindings (Ctx *ctx)
+{
+  return &ctx->events.bindings[0];
+}
+
+void ctx_remove_idle (Ctx *ctx, int handle)
+{
+  CtxList *l;
+  CtxList *to_remove = NULL;
+
+  if (!ctx->events.idles)
   {
-    ctx_rasterizer_apply_coverage (rasterizer,
-                         &dst[(minx * rasterizer->format->bpp) /8],
-                         minx,
-                         coverage,
-                         maxx-minx+ 1);
+    return;
   }
-#if CTX_SHAPE_CACHE
-      if (shape)
-        {
-          coverage += shape->width;
-        }
-#endif
-      dst += blit_stride;
-      rasterizer->prev_active_edges = rasterizer->active_edges;
-    }
+  for (l = ctx->events.idles; l; l = l->next)
+  {
+    CtxIdleCb *item = l->data;
+    if (item->id == handle)
+      ctx_list_prepend (&to_remove, item);
   }
-
-  if (CTX_UNLIKELY(rasterizer->state->gstate.compositing_mode == CTX_COMPOSITE_SOURCE_OUT ||
-      rasterizer->state->gstate.compositing_mode == CTX_COMPOSITE_SOURCE_IN ||
-      rasterizer->state->gstate.compositing_mode == CTX_COMPOSITE_DESTINATION_IN ||
-      rasterizer->state->gstate.compositing_mode == CTX_COMPOSITE_DESTINATION_ATOP ||
-      rasterizer->state->gstate.compositing_mode == CTX_COMPOSITE_CLEAR))
+  for (l = to_remove; l; l = l->next)
   {
-     /* fill in the rest of the blitrect when compositing mode permits it */
-     uint8_t nocoverage[rasterizer->blit_width];
-     //int gscan_start = rasterizer->state->gstate.clip_min_y * CTX_FULL_AA;
-     int gscan_start = rasterizer->state->gstate.clip_min_y * CTX_FULL_AA;
-     int gscan_end = rasterizer->state->gstate.clip_max_y * CTX_FULL_AA;
-     memset (nocoverage, 0, sizeof(nocoverage));
-     int startx   = rasterizer->state->gstate.clip_min_x;
-     int endx     = rasterizer->state->gstate.clip_max_x;
-     int clipw    = endx-startx + 1;
-     uint8_t *dst = ( (uint8_t *) rasterizer->buf);
+    CtxIdleCb *item = l->data;
+    if (item->destroy_notify)
+      item->destroy_notify (item->destroy_data);
+    ctx_list_remove (&ctx->events.idles, l->data);
+  }
+}
 
-     dst = (uint8_t*)(rasterizer->buf) + rasterizer->blit_stride * (gscan_start / CTX_FULL_AA);
-     for (rasterizer->scanline = gscan_start; rasterizer->scanline < scan_start;)
-     {
-       ctx_rasterizer_apply_coverage (rasterizer,
-                                      &dst[ (startx * rasterizer->format->bpp) /8],
-                                      0,
-                                      nocoverage, clipw);
-       rasterizer->scanline += CTX_FULL_AA;
-       dst += rasterizer->blit_stride;
-     }
-     if (minx < startx)
-     {
-     dst = (uint8_t*)(rasterizer->buf) + rasterizer->blit_stride * (scan_start / CTX_FULL_AA);
-     for (rasterizer->scanline = scan_start; rasterizer->scanline < scan_end;)
-     {
-       ctx_rasterizer_apply_coverage (rasterizer,
-                                      &dst[ (startx * rasterizer->format->bpp) /8],
-                                      0,
-                                      nocoverage, minx-startx);
-       dst += blit_stride;
-     }
-     }
+int ctx_add_timeout_full (Ctx *ctx, int ms, int (*idle_cb)(Ctx *ctx, void *idle_data), void *idle_data,
+                          void (*destroy_notify)(void *destroy_data), void *destroy_data)
+{
+  CtxIdleCb *item = calloc (sizeof (CtxIdleCb), 1);
+  item->cb              = idle_cb;
+  item->idle_data       = idle_data;
+  item->id              = ++ctx->events.idle_id;
+  item->ticks_full      = 
+  item->ticks_remaining = ms * 1000;
+  item->destroy_notify  = destroy_notify;
+  item->destroy_data    = destroy_data;
+  ctx_list_append (&ctx->events.idles, item);
+  return item->id;
+}
 
-     if (endx > maxx)
-     {
-     dst = (uint8_t*)(rasterizer->buf) + rasterizer->blit_stride * (scan_start / CTX_FULL_AA);
-     for (rasterizer->scanline = scan_start; rasterizer->scanline < scan_end;)
-     {
-       ctx_rasterizer_apply_coverage (rasterizer,
-                                      &dst[ (maxx * rasterizer->format->bpp) /8],
-                                      0,
-                                      nocoverage, endx-maxx);
+int ctx_add_timeout (Ctx *ctx, int ms, int (*idle_cb)(Ctx *ctx, void *idle_data), void *idle_data)
+{
+  return ctx_add_timeout_full (ctx, ms, idle_cb, idle_data, NULL, NULL);
+}
 
-       rasterizer->scanline += CTX_FULL_AA;
-       dst += rasterizer->blit_stride;
-     }
-     }
-#if 1
-     dst = (uint8_t*)(rasterizer->buf) + rasterizer->blit_stride * (scan_end / CTX_FULL_AA);
-     // XXX valgrind/asan this
-     if(0)for (rasterizer->scanline = scan_end; rasterizer->scanline/CTX_FULL_AA < gscan_end-1;)
-     {
-       ctx_rasterizer_apply_coverage (rasterizer,
-                                      &dst[ (startx * rasterizer->format->bpp) /8],
-                                      0,
-                                      nocoverage, clipw-1);
+int ctx_add_idle_full (Ctx *ctx, int (*idle_cb)(Ctx *ctx, void *idle_data), void *idle_data,
+                                 void (*destroy_notify)(void *destroy_data), void *destroy_data)
+{
+  CtxIdleCb *item = calloc (sizeof (CtxIdleCb), 1);
+  item->cb = idle_cb;
+  item->idle_data = idle_data;
+  item->id = ++ctx->events.idle_id;
+  item->ticks_full =
+  item->ticks_remaining = -1;
+  item->is_idle = 1;
+  item->destroy_notify = destroy_notify;
+  item->destroy_data = destroy_data;
+  ctx_list_append (&ctx->events.idles, item);
+  return item->id;
+}
+
+int ctx_add_idle (Ctx *ctx, int (*idle_cb)(Ctx *ctx, void *idle_data), void *idle_data)
+{
+  return ctx_add_idle_full (ctx, idle_cb, idle_data, NULL, NULL);
+}
 
-       rasterizer->scanline += CTX_FULL_AA;
-       dst += blit_stride;
-     }
 #endif
+/* using bigger primes would be a good idea, this falls apart due to rounding
+ * when zoomed in close
+ */
+static inline double ctx_path_hash (void *path)
+{
+  double ret = 0;
+#if 0
+  int i;
+  cairo_path_data_t *data;
+  if (!path)
+    return 0.99999;
+  for (i = 0; i <path->num_data; i += path->data[i].header.length)
+  {
+    data = &path->data[i];
+    switch (data->header.type) {
+      case CAIRO_PATH_MOVE_TO:
+        ret *= 17;
+        ret += data[1].point.x;
+        ret *= 113;
+        ret += data[1].point.y;
+        break;
+      case CAIRO_PATH_LINE_TO:
+        ret *= 121;
+        ret += data[1].point.x;
+        ret *= 1021;
+        ret += data[1].point.y;
+        break;
+      case CAIRO_PATH_CURVE_TO:
+        ret *= 3111;
+        ret += data[1].point.x;
+        ret *= 23;
+        ret += data[1].point.y;
+        ret *= 107;
+        ret += data[2].point.x;
+        ret *= 739;
+        ret += data[2].point.y;
+        ret *= 3;
+        ret += data[3].point.x;
+        ret *= 51;
+        ret += data[3].point.y;
+        break;
+      case CAIRO_PATH_CLOSE_PATH:
+        ret *= 51;
+        break;
+    }
   }
-  ctx_rasterizer_reset (rasterizer);
+#endif
+  return ret;
 }
 
-inline static int
-ctx_is_transparent (CtxRasterizer *rasterizer, int stroke)
+#if CTX_EVENTS
+void _ctx_item_ref (CtxItem *item)
 {
-  CtxGState *gstate = &rasterizer->state->gstate;
-  if (gstate->global_alpha_u8 == 0)
-    return 1;
-  if (gstate->source_fill.type == CTX_SOURCE_COLOR)
+  if (item->ref_count < 0)
   {
-    uint8_t ga[2];
-    ctx_color_get_graya_u8 (rasterizer->state, &gstate->source_fill.color, ga);
-    if (ga[1] == 0)
-      return 1;
+    fprintf (stderr, "EEEEK!\n");
   }
-  return 0;
+  item->ref_count++;
 }
 
-#define CTX_RECT_FILL 1
-
-#if CTX_RECT_FILL
-static void
-ctx_rasterizer_fill_rect (CtxRasterizer *rasterizer,
-                          int          x0,
-                          int          y0,
-                          int          x1,
-                          int          y1,
-                          uint8_t      cov)
-{
-  int blit_x = rasterizer->blit_x;
-  int blit_y = rasterizer->blit_y;
-  int blit_width = rasterizer->blit_width;
-  int blit_height = rasterizer->blit_height;
-  int blit_stride = rasterizer->blit_stride;
-  x0 = ctx_maxi (x0, blit_x);
-  x1 = ctx_mini (x1, blit_x + blit_width);
-
-  int width = x1 - x0;
 
-  if (CTX_UNLIKELY(width <=0))
+void _ctx_item_unref (CtxItem *item)
+{
+  if (item->ref_count <= 0)
+  {
+    fprintf (stderr, "EEEEK!\n");
     return;
-
-  void (*comp_op)(CTX_COMPOSITE_ARGUMENTS) = rasterizer->comp_op;
-
-  y0 = ctx_maxi (y0, blit_y);
-  y1 = ctx_mini (y1, blit_y + blit_height);
-  rasterizer->scanline = y0 * CTX_FULL_AA;
-  _ctx_setup_compositor (rasterizer);
-  uint8_t *dst = ( (uint8_t *) rasterizer->buf);
-
-  dst += (y0 - blit_y) * blit_stride;
-  dst += (x0) * rasterizer->format->bpp/8;
-
-  if (cov == 255)
+  }
+  item->ref_count--;
+  if (item->ref_count <=0)
   {
-    if (comp_op == ctx_RGBA8_source_copy_normal_color)
-    {
-      uint32_t color = *((uint32_t*)rasterizer->color);
-      if ((width == 1))
-      {
-        for (int y = y0; y < y1; y++)
-        {
-          uint32_t *dst_i = (uint32_t*)&dst[0];
-          *dst_i = color;
-          dst += blit_stride;
-        }
-        return;
-      }
-      else
-      {
-        for (int y = y0; y < y1; y++)
-        {
-          ctx_span_set_color ((uint32_t*)&dst[0], color, width);
-          dst += blit_stride;
-        }
-        return;
-      }
-    }
-    else if (comp_op == ctx_RGBA8_source_over_normal_color)
-    {
-      uint32_t si_ga_full = ((uint32_t*)rasterizer->color)[3];
-      uint32_t si_rb_full = ((uint32_t*)rasterizer->color)[4];
-      uint32_t si_a  = rasterizer->color[3];
-
-      if ((width == 1))
-      {
-        for (int y = y0; y < y1; y++)
-        {
-          ((uint32_t*)(dst))[0] = ctx_over_RGBA8_full_2 (
-             ((uint32_t*)(dst))[0], si_ga_full, si_rb_full, si_a);
-          dst += blit_stride;
-        }
-        return;
-      }
-      else
-      {
-        for (int y = y0; y < y1; y++)
-        {
-          uint32_t *dst_i = (uint32_t*)&dst[0];
-          for (int i = 0; i < width; i++)
-          {
-            dst_i[i] = ctx_over_RGBA8_full_2 (dst_i[i], si_ga_full, si_rb_full, si_a);
-          }
-          dst += blit_stride;
-        }
-        return;
-      }
-    }
-    else if (comp_op == ctx_RGBA8_source_copy_normal_fragment)
     {
-      for (int y = y0; y < y1; y++)
+      int i;
+      for (i = 0; i < item->cb_count; i++)
       {
-        float u0 = 0; float v0 = 0;
-        float ud = 0; float vd = 0;
-        ctx_init_uv (rasterizer, x0, width, &u0, &v0, &ud, &vd);
-        rasterizer->fragment (rasterizer, u0, v0, &dst[0], width, ud, vd);
-        rasterizer->scanline += CTX_FULL_AA;
-        dst += blit_stride;
+        if (item->cb[i].finalize)
+          item->cb[i].finalize (item->cb[i].data1, item->cb[i].data2,
+                                   item->cb[i].finalize_data);
       }
-      return;
     }
-    else if (comp_op == ctx_RGBA8_source_over_normal_fragment)
+    if (item->path)
     {
-      for (int y = y0; y < y1; y++)
-      {
-        ctx_RGBA8_source_over_normal_full_cov_fragment (rasterizer,
-                                &dst[0], NULL, x0, NULL, width);
-        rasterizer->scanline += CTX_FULL_AA;
-        dst += blit_stride;
-      }
-      return;
+      //cairo_path_destroy (item->path);
     }
+    free (item);
   }
-  else
+}
+
+
+static int
+path_equal (void *path,
+            void *path2)
+{
+  //  XXX
+  return 0;
+}
+
+void ctx_listen_set_cursor (Ctx      *ctx,
+                            CtxCursor cursor)
+{
+  if (ctx->events.last_item)
+  {
+    ctx->events.last_item->cursor = cursor;
+  }
+}
+
+void ctx_listen_full (Ctx     *ctx,
+                      float    x,
+                      float    y,
+                      float    width,
+                      float    height,
+                      CtxEventType  types,
+                      CtxCb    cb,
+                      void    *data1,
+                      void    *data2,
+                      void   (*finalize)(void *listen_data,
+                                         void *listen_data2,
+                                         void *finalize_data),
+                      void    *finalize_data)
+{
+  if (!ctx->events.frozen)
   {
-  if (comp_op == ctx_RGBA8_source_copy_normal_color)
+    CtxItem *item;
+
+    /* early bail for listeners outside screen  */
+    /* XXX: fixme respect clipping */
     {
-      uint32_t color = *((uint32_t*)rasterizer->color);
-      if ((width == 1))
-      {
-        for (int y = y0; y < y1; y++)
-        {
-          uint32_t *dst_i = (uint32_t*)&dst[0];
-          *dst_i = ctx_lerp_RGBA8 (*dst_i, color, cov);
-          dst += blit_stride;
-        }
-        return;
-      }
-      else
+      float tx = x;
+      float ty = y;
+      float tw = width;
+      float th = height;
+      _ctx_user_to_device (&ctx->state, &tx, &ty);
+      _ctx_user_to_device_distance (&ctx->state, &tw, &th);
+      if (ty > ctx->events.height * 2 ||
+          tx > ctx->events.width * 2 ||
+          tx + tw < 0 ||
+          ty + th < 0)
       {
-        for (int y = y0; y < y1; y++)
-        {
-          uint32_t *dst_i = (uint32_t*)&dst[0];
-          for (int i = 0; i < width; i++)
-          {
-            dst_i[i] = ctx_lerp_RGBA8 (dst_i[i], color, cov);
-          }
-          dst += blit_stride;
-        }
+        if (finalize)
+          finalize (data1, data2, finalize_data);
         return;
       }
     }
-    else if (comp_op == ctx_RGBA8_source_over_normal_color)
+
+    item = calloc (sizeof (CtxItem), 1);
+    item->x0 = x;
+    item->y0 = y;
+    item->x1 = x + width;
+    item->y1 = y + height;
+    item->cb[0].types = types;
+    item->cb[0].cb = cb;
+    item->cb[0].data1 = data1;
+    item->cb[0].data2 = data2;
+    item->cb[0].finalize = finalize;
+    item->cb[0].finalize_data = finalize_data;
+    item->cb_count = 1;
+    item->types = types;
+    //item->path = cairo_copy_path (cr); // XXX
+    item->path_hash = ctx_path_hash (item->path);
+    ctx_get_matrix (ctx, &item->inv_matrix);
+    ctx_matrix_invert (&item->inv_matrix);
+
+    if (ctx->events.items)
     {
-      uint32_t color = *((uint32_t*)rasterizer->color);
-      if ((width == 1))
-      {
-        for (int y = y0; y < y1; y++)
-        {
-          uint32_t *dst_i = (uint32_t*)&dst[0];
-          *dst_i = ctx_over_RGBA8 (*dst_i, color, cov);
-          dst += blit_stride;
-        }
-        return;
-      }
-      else
+      CtxList *l;
+      for (l = ctx->events.items; l; l = l->next)
       {
-        for (int y = y0; y < y1; y++)
+        CtxItem *item2 = l->data;
+
+        /* store multiple callbacks for one entry when the paths
+         * are exact matches, reducing per event traversal checks at the
+         * cost of a little paint-hit (XXX: is this the right tradeoff,
+         * perhaps it is better to spend more time during event processing
+         * than during paint?)
+         */
+        if (item->path_hash == item2->path_hash &&
+            path_equal (item->path, item2->path))
         {
-          uint32_t *dst_i = (uint32_t*)&dst[0];
-          for (int i = 0; i < width; i++)
-          {
-            dst_i[i] = ctx_over_RGBA8 (dst_i[i], color, cov);
-          }
-          dst += blit_stride;
+          /* found an item, copy over cb data  */
+          item2->cb[item2->cb_count] = item->cb[0];
+          free (item);
+          item2->cb_count++;
+          item2->types |= types;
+          return;
         }
-        return;
       }
     }
-  }
-
-  {
-    uint8_t coverage[width];
-    memset (coverage, cov, sizeof (coverage) );
-    for (int y = y0; y < y1; y++)
-    {
-      ctx_rasterizer_apply_coverage (rasterizer, &dst[0], x0, coverage, width);
-      rasterizer->scanline += CTX_FULL_AA;
-      dst += blit_stride;
-    }
+    item->ref_count       = 1;
+    ctx->events.last_item = item;
+    ctx_list_prepend_full (&ctx->events.items, item, (void*)_ctx_item_unref, NULL);
   }
 }
-#endif
 
-static inline float ctx_fmod1f (float val)
+void ctx_event_stop_propagate (CtxEvent *event)
 {
-  int vali = val;
-  return val - vali;
+  if (event)
+    event->stop_propagate = 1;
 }
 
-static void
-ctx_rasterizer_fill (CtxRasterizer *rasterizer)
+void ctx_listen (Ctx          *ctx,
+                 CtxEventType  types,
+                 CtxCb         cb,
+                 void*         data1,
+                 void*         data2)
 {
-  int count = rasterizer->preserve?rasterizer->edge_list.count:0;
-  int blit_x = rasterizer->blit_x;
-  int blit_y = rasterizer->blit_y;
-  int blit_width = rasterizer->blit_width;
-  int blit_height = rasterizer->blit_height;
-  int blit_stride = rasterizer->blit_stride;
-
-  CtxSegment temp[count]; /* copy of already built up path's poly line
-                          XXX - by building a large enough path
-                          the stack can be smashed!
-                         */
-  if (CTX_UNLIKELY(rasterizer->preserve))
-    { memcpy (temp, rasterizer->edge_list.entries, sizeof (temp) ); }
-
-#if CTX_ENABLE_SHADOW_BLUR
-  if (CTX_UNLIKELY(rasterizer->in_shadow))
+  float x, y, width, height;
+  /* generate bounding box of what to listen for - from current cairo path */
+  if (types & CTX_KEY)
   {
-  for (int i = 0; i < rasterizer->edge_list.count; i++)
-    {
-      CtxSegment *entry = &((CtxSegment*)rasterizer->edge_list.entries)[i];
-      entry->data.s16[2] += rasterizer->shadow_x * CTX_SUBDIV;
-      entry->data.s16[3] += rasterizer->shadow_y * CTX_FULL_AA;
-    }
-    rasterizer->scan_min += rasterizer->shadow_y * CTX_FULL_AA;
-    rasterizer->scan_max += rasterizer->shadow_y * CTX_FULL_AA;
-    rasterizer->col_min  += (rasterizer->shadow_x - rasterizer->state->gstate.shadow_blur * 3 + 1) * 
CTX_SUBDIV;
-    rasterizer->col_max  += (rasterizer->shadow_x + rasterizer->state->gstate.shadow_blur * 3 + 1) * 
CTX_SUBDIV;
+    x = 0;
+    y = 0;
+    width = 0;
+    height = 0;
   }
-#endif
-
-  if (CTX_UNLIKELY(ctx_is_transparent (rasterizer, 0) ||
-      rasterizer->scan_min > CTX_FULL_AA * (blit_y + blit_height) ||
-      rasterizer->scan_max < CTX_FULL_AA * blit_y ||
-      rasterizer->col_min > CTX_SUBDIV * (blit_x + blit_width) ||
-      rasterizer->col_max < CTX_SUBDIV * blit_x))
-    {
-      ctx_rasterizer_reset (rasterizer);
-    }
   else
   {
-    _ctx_setup_compositor (rasterizer);
-
-#if 1
-    rasterizer->state->min_x = ctx_mini (rasterizer->state->min_x, rasterizer->col_min / CTX_SUBDIV);
-    rasterizer->state->max_x = ctx_maxi (rasterizer->state->min_x, rasterizer->col_max / CTX_SUBDIV);
-    rasterizer->state->min_y = ctx_mini (rasterizer->state->min_y, rasterizer->scan_min / CTX_FULL_AA);
-    rasterizer->state->max_y = ctx_maxi (rasterizer->state->max_y, rasterizer->scan_max / CTX_FULL_AA);
-#else
-    if (CTX_UNLIKELY ( rasterizer->col_min / CTX_SUBDIV < rasterizer->state->min_x))
-       rasterizer->state->min_x = rasterizer->col_min / CTX_SUBDIV;
-    if (CTX_UNLIKELY ( rasterizer->col_min / CTX_SUBDIV > rasterizer->state->max_x))
-       rasterizer->state->min_x = rasterizer->col_min / CTX_SUBDIV;
-
-    if (CTX_UNLIKELY ( rasterizer->scan_min / CTX_FULL_AA < rasterizer->state->min_y))
-       rasterizer->state->min_y = rasterizer->scan_min / CTX_FULL_AA;
-    if (CTX_UNLIKELY ( rasterizer->scan_min / CTX_FULL_AA > rasterizer->state->max_y))
-       rasterizer->state->max_y = rasterizer->scan_max / CTX_FULL_AA;
-#endif
-
-#if CTX_RECT_FILL
-  if (rasterizer->edge_list.count == 6)
-    {
-      CtxSegment *entry0 = &(((CtxSegment*)(rasterizer->edge_list.entries)))[0];
-      CtxSegment *entry1 = &(((CtxSegment*)(rasterizer->edge_list.entries)))[1];
-      CtxSegment *entry2 = &(((CtxSegment*)(rasterizer->edge_list.entries)))[2];
-      CtxSegment *entry3 = &(((CtxSegment*)(rasterizer->edge_list.entries)))[3];
-
-      if ((!rasterizer->state->gstate.clipped != 0) &
-          (entry0->data.s16[2] == entry1->data.s16[2]) &
-          (entry0->data.s16[3] == entry3->data.s16[3]) &
-          (entry1->data.s16[3] == entry2->data.s16[3]) &
-          (entry2->data.s16[2] == entry3->data.s16[2])
-#if CTX_ENABLE_SHADOW_BLUR
-           && !rasterizer->in_shadow
-#endif
-         )
-       {
-         if(((entry1->data.s16[2] % (CTX_SUBDIV))  == 0) &
-            ((entry1->data.s16[3] % (CTX_FULL_AA)) == 0) &
-            ((entry3->data.s16[2] % (CTX_SUBDIV))  == 0) &
-            ((entry3->data.s16[3] % (CTX_FULL_AA)) == 0))
-         {
-           /* best-case axis aligned rectangle */
-           int x0 = entry3->data.s16[2] / CTX_SUBDIV;
-           int y0 = entry3->data.s16[3] / CTX_FULL_AA;
-           int x1 = entry1->data.s16[2] / CTX_SUBDIV;
-           int y1 = entry1->data.s16[3] / CTX_FULL_AA;
-
-           ctx_rasterizer_fill_rect (rasterizer, x0, y0, x1, y1, 255);
-           ctx_rasterizer_reset (rasterizer);
-           goto done;
-         }
-        else
-         {
-           float x0 = entry3->data.s16[2] * (1.0f / CTX_SUBDIV);
-           float y0 = entry3->data.s16[3] * (1.0f / CTX_FULL_AA);
-           float x1 = entry1->data.s16[2] * (1.0f / CTX_SUBDIV);
-           float y1 = entry1->data.s16[3] * (1.0f / CTX_FULL_AA);
-
-           x0 = ctx_maxf (x0, blit_x);
-           y0 = ctx_maxf (y0, blit_y);
-           x1 = ctx_minf (x1, blit_x + blit_width);
-           y1 = ctx_minf (y1, blit_y + blit_height);
-
-           uint8_t left = 255-ctx_fmod1f (x0) * 255;
-           uint8_t top  = 255-ctx_fmod1f (y0) * 255;
-           uint8_t right  = ctx_fmod1f (x1) * 255;
-           uint8_t bottom = ctx_fmod1f (y1) * 255;
-
-           x0 = ctx_floorf (x0);
-           y0 = ctx_floorf (y0);
-           x1 = ctx_floorf (x1+7/8.0f);
-           y1 = ctx_floorf (y1+14/15.0f);
-
-           int has_top    = (top < 255);
-           int has_bottom = (bottom <255);
-           int has_right  = (right >0);
-           int has_left   = (left >0);
-
-           int width = x1 - x0;
-
-           if (CTX_LIKELY(width >0))
-           {
-              uint8_t *dst = ( (uint8_t *) rasterizer->buf);
-              uint8_t coverage[width+2];
-              dst += (((int)y0) - blit_y) * blit_stride;
-              dst += ((int)x0) * rasterizer->format->bpp/8;
-
-              if (has_top)
-              {
-                int i = 0;
-                if (has_left)
-                {
-                  coverage[i++] = top * left / 255;
-                }
-                for (int x = x0 + has_left; x < x1 - has_right; x++)
-                  coverage[i++] = top;
-                coverage[i++]= top * right / 255;
-
-                  ctx_rasterizer_apply_coverage (rasterizer,dst,
-                                                 x0,
-                                                 coverage, width);
-                 dst += blit_stride;
-               }
+     float ex1,ey1,ex2,ey2;
+     ctx_path_extents (ctx, &ex1, &ey1, &ex2, &ey2);
+     x = ex1;
+     y = ey1;
+     width = ex2 - ex1;
+     height = ey2 - ey1;
+  }
 
-#if 0
-           if (!(
-            (rasterizer->state->gstate.compositing_mode == CTX_COMPOSITE_COPY||
-             rasterizer->state->gstate.compositing_mode == CTX_COMPOSITE_SOURCE_OVER) &&
-             rasterizer->state->gstate.blend_mode == CTX_BLEND_NORMAL &&
-             rasterizer->state->gstate.source_fill.type == CTX_SOURCE_COLOR
-             ))
-           {
-             int i = 0;
-             if (has_left)
-             {
-               coverage[i++] = left;
-             }
-             for (int x = x0 + has_left; x < x1 - has_right; x++)
-               coverage[i++] = 255;
-             coverage[i++] = right;
+  if (types == CTX_DRAG_MOTION)
+    types = CTX_DRAG_MOTION | CTX_DRAG_PRESS;
+  return ctx_listen_full (ctx, x, y, width, height, types, cb, data1, data2, NULL, NULL);
+}
 
-             for (int ty = y0+has_top; ty < y1-has_bottom; ty++)
-             {
-               ctx_rasterizer_apply_coverage (rasterizer, dst, x0, coverage, width);
-               dst += blit_stride;
-             }
-           }
-           else
-#endif
-           {
-             if (has_left)
-               ctx_rasterizer_fill_rect (rasterizer, x0, y0 + has_top, x0+1, y1 - has_bottom, left);
-             if (has_right)
-               ctx_rasterizer_fill_rect (rasterizer, x1-1, y0 + has_top, x1, y1 - has_bottom, right);
-             x0 += has_left;
-             y0 += has_top;
-             y1 -= has_bottom;
-             x1 -= has_right;
-             ctx_rasterizer_fill_rect (rasterizer, x0,y0,x1,y1,255);
-
-             dst += blit_stride * ((((int)y1)-has_bottom) - (((int)y0)+has_top));
-           }
+void  ctx_listen_with_finalize (Ctx          *ctx,
+                                CtxEventType  types,
+                                CtxCb         cb,
+                                void*         data1,
+                                void*         data2,
+                      void   (*finalize)(void *listen_data, void *listen_data2,
+                                         void *finalize_data),
+                      void    *finalize_data)
+{
+  float x, y, width, height;
+  /* generate bounding box of what to listen for - from current cairo path */
+  if (types & CTX_KEY)
+  {
+    x = 0;
+    y = 0;
+    width = 0;
+    height = 0;
+  }
+  else
+  {
+     float ex1,ey1,ex2,ey2;
+     ctx_path_extents (ctx, &ex1, &ey1, &ex2, &ey2);
+     x = ex1;
+     y = ey1;
+     width = ex2 - ex1;
+     height = ey2 - ey1;
+  }
 
-           if (has_bottom)
-           {
-             int i = 0;
-             if (has_left)
-               coverage[i++] = bottom * left / 255;
-             for (int x = x0 + has_left; x < x1 - has_right; x++)
-               coverage[i++] = bottom;
-             coverage[i++]= bottom * right / 255;
-
-             ctx_rasterizer_apply_coverage (rasterizer,dst, x0, coverage, width);
-           }
-           }
+  if (types == CTX_DRAG_MOTION)
+    types = CTX_DRAG_MOTION | CTX_DRAG_PRESS;
+  return ctx_listen_full (ctx, x, y, width, height, types, cb, data1, data2, finalize, finalize_data);
+}
 
-           ctx_rasterizer_reset (rasterizer);
-           goto done;
-         }
 
-       }
-    }
-#endif
-    ctx_rasterizer_finish_shape (rasterizer);
+static void ctx_report_hit_region (CtxEvent *event,
+                       void     *data,
+                       void     *data2)
+{
+  const char *id = data;
 
-    uint32_t hash = ctx_rasterizer_poly_to_edges (rasterizer);
-    if (hash){};
+  fprintf (stderr, "hit region %s\n", id);
+  // XXX: NYI
+}
 
-#if CTX_SHAPE_CACHE
-    int width = (rasterizer->col_max + (CTX_SUBDIV-1) ) / CTX_SUBDIV - rasterizer->col_min/CTX_SUBDIV + 1;
-    int height = (rasterizer->scan_max + (CTX_FULL_AA-1) ) / CTX_FULL_AA - rasterizer->scan_min / 
CTX_FULL_AA + 1;
-    if (width * height < CTX_SHAPE_CACHE_DIM && width >=1 && height >= 1
-        && width < CTX_SHAPE_CACHE_MAX_DIM
-        && height < CTX_SHAPE_CACHE_MAX_DIM 
-#if CTX_ENABLE_SHADOW_BLUR
-        && !rasterizer->in_shadow
-#endif
-        )
-      {
-        int scan_min = rasterizer->scan_min;
-        int col_min = rasterizer->col_min;
-        scan_min -= (scan_min % CTX_FULL_AA);
-        int y0 = scan_min / CTX_FULL_AA;
-        int y1 = y0 + height;
-        int x0 = col_min / CTX_SUBDIV;
-        int ymin = y0;
-        int x1 = x0 + width;
-        int clip_x_min = blit_x;
-        int clip_x_max = blit_x + blit_width - 1;
-        int clip_y_min = blit_y;
-        int clip_y_max = blit_y + blit_height - 1;
+void ctx_add_hit_region (Ctx *ctx, const char *id)
+{
+  char *id_copy = strdup (id);
+  float x, y, width, height;
+  /* generate bounding box of what to listen for - from current cairo path */
+  {
+     float ex1,ey1,ex2,ey2;
+     ctx_path_extents (ctx, &ex1, &ey1, &ex2, &ey2);
+     x = ex1;
+     y = ey1;
+     width = ex2 - ex1;
+     height = ey2 - ey1;
+  }
+  
+  return ctx_listen_full (ctx, x, y, width, height,
+                          CTX_POINTER, ctx_report_hit_region,
+                          id_copy, NULL, (void*)free, NULL);
+}
 
-        int dont_cache = 0;
-        if (CTX_UNLIKELY(x1 >= clip_x_max))
-          { x1 = clip_x_max;
-            dont_cache = 1;
-          }
-        int xo = 0;
-        if (CTX_UNLIKELY(x0 < clip_x_min))
-          {
-            xo = clip_x_min - x0;
-            x0 = clip_x_min;
-            dont_cache = 1;
-          }
-        if (CTX_UNLIKELY(y0 < clip_y_min || y1 >= clip_y_max))
-          dont_cache = 1;
-        if (dont_cache || !_ctx_shape_cache_enabled)
-        {
-          ctx_rasterizer_rasterize_edges (rasterizer, rasterizer->state->gstate.fill_rule
-#if CTX_SHAPE_CACHE
-                                        , NULL
-#endif
-                                       );
-        }
-        else
-        {
-        rasterizer->scanline = scan_min;
-        CtxShapeEntry *shape = ctx_shape_entry_find (rasterizer, hash, width, height); 
+typedef struct _CtxGrab CtxGrab;
 
-        if (shape->uses == 0)
-          {
-            CtxBuffer *buffer_backup = rasterizer->clip_buffer;
-            rasterizer->clip_buffer = NULL;
-            ctx_rasterizer_rasterize_edges (rasterizer, rasterizer->state->gstate.fill_rule, shape);
-            rasterizer->clip_buffer = buffer_backup;
-          }
+struct _CtxGrab
+{
+  CtxItem *item;
+  int      device_no;
+  int      timeout_id;
+  int      start_time;
+  float    x; // for tap and hold
+  float    y;
+  CtxEventType  type;
+};
 
-        int ewidth = x1 - x0;
-        if (ewidth>0)
-        {
-          rasterizer->scanline = scan_min;
-          int bpp = rasterizer->format->bpp;
-          if (rasterizer->clip_buffer && !rasterizer->clip_rectangle)
-          {
-          uint8_t composite[ewidth];
-          uint8_t *clip_data = (uint8_t*)rasterizer->clip_buffer->data;
-          int shape_width = shape->width;
-          for (int y = y0; y < y1; y++)
-            {
-              if ( (y >= clip_y_min) && (y <= clip_y_max) )
-                {
-                    for (int x = 0; x < ewidth; x++)
-                    {
-                      int val = shape->data[shape_width * (int)(y-ymin) + xo + x];
-                      // XXX : not valid for 1bit clip buffers
-                      val = (val*(clip_data) [
-                              ((y-blit_y) * blit_width) + x0 + x])/255;
-                      composite[x] = val;
-                    }
-                    ctx_rasterizer_apply_coverage (rasterizer,
-                                                 ( (uint8_t *) rasterizer->buf) + (y-blit_y) * blit_stride + 
(int) (x0) * bpp/8,
-                                                 x0, // is 0
-                                                 composite,
-                                                 ewidth );
-               rasterizer->scanline += CTX_FULL_AA;
-            }
-          }
-          }
-          else
-          for (int y = y0; y < y1; y++)
-            {
-              if (CTX_LIKELY((y >= clip_y_min) && (y <= clip_y_max) ))
-                {
-                    ctx_rasterizer_apply_coverage (rasterizer,
-                                                 ( (uint8_t *) rasterizer->buf) + (y-blit_y) * blit_stride + 
(int) (x0) * bpp/8,
-                                                 x0,
-                                                 &shape->data[shape->width * (int) (y-ymin) + xo],
-                                                 ewidth );
-                }
-               rasterizer->scanline += CTX_FULL_AA;
-            }
-        }
-        if (shape->uses != 0)
-          {
-            ctx_rasterizer_reset (rasterizer);
-          }
-        }
-      }
-    else
-#endif
-    {
-            
-    ctx_rasterizer_rasterize_edges (rasterizer, rasterizer->state->gstate.fill_rule
-#if CTX_SHAPE_CACHE
-                                    , NULL
-#endif
-                                   );
-    }
-  }
-done:
-  if (CTX_UNLIKELY(rasterizer->preserve))
-    {
-      memcpy (rasterizer->edge_list.entries, temp, sizeof (temp) );
-      rasterizer->edge_list.count = count;
-    }
-#if CTX_ENABLE_SHADOW_BLUR
-  if (CTX_UNLIKELY(rasterizer->in_shadow))
+static void grab_free (Ctx *ctx, CtxGrab *grab)
+{
+  if (grab->timeout_id)
   {
-    rasterizer->scan_min -= rasterizer->shadow_y * CTX_FULL_AA;
-    rasterizer->scan_max -= rasterizer->shadow_y * CTX_FULL_AA;
-    rasterizer->col_min  -= (rasterizer->shadow_x - rasterizer->state->gstate.shadow_blur * 3 + 1) * 
CTX_SUBDIV;
-    rasterizer->col_max  -= (rasterizer->shadow_x + rasterizer->state->gstate.shadow_blur * 3 + 1) * 
CTX_SUBDIV;
+    ctx_remove_idle (ctx, grab->timeout_id);
+    grab->timeout_id = 0;
   }
-#endif
-  rasterizer->preserve = 0;
+  _ctx_item_unref (grab->item);
+  free (grab);
 }
 
-#if 0
-static void
-ctx_rasterizer_triangle (CtxRasterizer *rasterizer,
-                         int x0, int y0,
-                         int x1, int y1,
-                         int x2, int y2,
-                         int r0, int g0, int b0, int a0,
-                         int r1, int g1, int b1, int a1,
-                         int r2, int g2, int b2, int a2,
-                         int u0, int v0,
-                         int u1, int v1)
+static void device_remove_grab (Ctx *ctx, CtxGrab *grab)
 {
-
+  ctx_list_remove (&ctx->events.grabs, grab);
+  grab_free (ctx, grab);
 }
-#endif
-
-
-typedef struct _CtxTermGlyph CtxTermGlyph;
 
-struct _CtxTermGlyph
+static CtxGrab *device_add_grab (Ctx *ctx, int device_no, CtxItem *item, CtxEventType type)
 {
-  uint32_t unichar;
-  int      col;
-  int      row;
-  uint8_t  rgba_bg[4];
-  uint8_t  rgba_fg[4];
-};
+  CtxGrab *grab = calloc (1, sizeof (CtxGrab));
+  grab->item = item;
+  grab->type = type;
+  _ctx_item_ref (item);
+  grab->device_no = device_no;
+  ctx_list_append (&ctx->events.grabs, grab);
+  return grab;
+}
 
-static int _ctx_glyph (Ctx *ctx, uint32_t unichar, int stroke);
-static void
-ctx_rasterizer_glyph (CtxRasterizer *rasterizer, uint32_t unichar, int stroke)
+static CtxList *_ctx_device_get_grabs (Ctx *ctx, int device_no)
 {
-  float tx = rasterizer->state->x;
-  float ty = rasterizer->state->y - rasterizer->state->gstate.font_size;
-  float tx2 = rasterizer->state->x + rasterizer->state->gstate.font_size;
-  float ty2 = rasterizer->state->y + rasterizer->state->gstate.font_size;
-  _ctx_user_to_device (rasterizer->state, &tx, &ty);
-  _ctx_user_to_device (rasterizer->state, &tx2, &ty2);
+  CtxList *ret = NULL;
+  CtxList *l;
+  for (l = ctx->events.grabs; l; l = l->next)
+  {
+    CtxGrab *grab = l->data;
+    if (grab->device_no == device_no)
+      ctx_list_append (&ret, grab);
+  }
+  return ret;
+}
 
-  if (tx2 < rasterizer->blit_x || ty2 < rasterizer->blit_y) return;
-  if (tx  > rasterizer->blit_x + rasterizer->blit_width ||
-      ty  > rasterizer->blit_y + rasterizer->blit_height)
-          return;
+static void _mrg_restore_path (Ctx *ctx, void *path)  //XXX
+{
+  //int i;
+  //cairo_path_data_t *data;
+  //cairo_new_path (cr);
+  //cairo_append_path (cr, path);
+}
 
-#if CTX_BRAILLE_TEXT
-  float font_size = 0;
-  int ch = 1;
-  int cw = 1;
+CtxList *_ctx_detect_list (Ctx *ctx, float x, float y, CtxEventType type)
+{
+  CtxList *a;
+  CtxList *ret = NULL;
 
-  if (rasterizer->term_glyphs)
+  if (type == CTX_KEY_DOWN ||
+      type == CTX_KEY_UP ||
+      type == CTX_KEY_PRESS ||
+      type == CTX_MESSAGE ||
+      type == (CTX_KEY_DOWN|CTX_MESSAGE) ||
+      type == (CTX_KEY_DOWN|CTX_KEY_UP) ||
+      type == (CTX_KEY_DOWN|CTX_KEY_UP|CTX_MESSAGE))
   {
-    float tx = 0;
-    float ty = rasterizer->state->gstate.font_size;
-    float txb = 0;
-    float tyb = 0;
+    for (a = ctx->events.items; a; a = a->next)
+    {
+      CtxItem *item = a->data;
+      if (item->types & type)
+      {
+        ctx_list_prepend (&ret, item);
+        return ret;
+      }
+    }
+    return NULL;
+  }
 
-    ch = ctx_term_get_cell_height (rasterizer->ctx);
-    cw = ctx_term_get_cell_width (rasterizer->ctx);
+  for (a = ctx->events.items; a; a = a->next)
+  {
+    CtxItem *item= a->data;
+  
+    float u, v;
+    u = x;
+    v = y;
+    ctx_matrix_apply_transform (&item->inv_matrix, &u, &v);
 
-    _ctx_user_to_device (rasterizer->state, &tx, &ty);
-    _ctx_user_to_device (rasterizer->state, &txb, &tyb);
-    font_size = ty-tyb;
+    if (u >= item->x0 && v >= item->y0 &&
+        u <  item->x1 && v <  item->y1 && 
+        ((item->types & type) || ((type == CTX_SET_CURSOR) &&
+        item->cursor)))
+    {
+      if (item->path)
+      {
+        _mrg_restore_path (ctx, item->path);
+        if (ctx_in_fill (ctx, u, v))
+        {
+          ctx_begin_path (ctx);
+          ctx_list_prepend (&ret, item);
+        }
+        ctx_begin_path (ctx);
+      }
+      else
+      {
+        ctx_list_prepend (&ret, item);
+      }
+    }
   }
-  if (rasterizer->term_glyphs && !stroke &&
-      fabs (font_size - ch) < 0.5)
+  return ret;
+}
+
+CtxItem *_ctx_detect (Ctx *ctx, float x, float y, CtxEventType type)
+{
+  CtxList *l = _ctx_detect_list (ctx, x, y, type);
+  if (l)
   {
-    float tx = rasterizer->x;
-    float ty = rasterizer->y;
-    _ctx_user_to_device (rasterizer->state, &tx, &ty);
-    int col = tx / cw + 1;
-    int row = ty / ch + 1;
-    CtxTermGlyph *glyph = ctx_calloc (sizeof (CtxTermGlyph), 1);
-    ctx_list_append (&rasterizer->glyphs, glyph);
-    glyph->unichar = unichar;
-    glyph->col = col;
-    glyph->row = row;
-    ctx_color_get_rgba8 (rasterizer->state, &rasterizer->state->gstate.source_fill.color,
-                         &glyph->rgba_fg[0]);
+    ctx_list_reverse (&l);
+    CtxItem *ret = l->data;
+    ctx_list_free (&l);
+    return ret;
   }
-  else
-#endif
-  _ctx_glyph (rasterizer->ctx, unichar, stroke);
+  return NULL;
 }
 
-
-static void
-_ctx_text (Ctx        *ctx,
-           const char *string,
-           int         stroke,
-           int         visible);
-static void
-ctx_rasterizer_text (CtxRasterizer *rasterizer, const char *string, int stroke)
+static int
+_ctx_emit_cb_item (Ctx *ctx, CtxItem *item, CtxEvent *event, CtxEventType type, float x, float y)
 {
-#if CTX_BRAILLE_TEXT
-  float font_size = 0;
-  if (rasterizer->term_glyphs)
+  static CtxEvent s_event;
+  CtxEvent transformed_event;
+  int i;
+
+
+  if (!event)
   {
-  float tx = 0;
-  float ty = rasterizer->state->gstate.font_size;
-  _ctx_user_to_device (rasterizer->state, &tx, &ty);
-  font_size = ty;
+    event = &s_event;
+    event->type = type;
+    event->x = x;
+    event->y = y;
   }
-  int   ch = ctx_term_get_cell_height (rasterizer->ctx);
-  int   cw = ctx_term_get_cell_width (rasterizer->ctx);
+  event->ctx = ctx;
+  transformed_event = *event;
+  transformed_event.device_x = event->x;
+  transformed_event.device_y = event->y;
 
-  if (rasterizer->term_glyphs && !stroke &&
-      fabs (font_size - ch) < 0.5)
   {
-    float tx = rasterizer->x;
-    float ty = rasterizer->y;
-    _ctx_user_to_device (rasterizer->state, &tx, &ty);
-    int col = tx / cw + 1;
-    int row = ty / ch + 1;
-    for (int i = 0; string[i]; i++, col++)
+    float tx, ty;
+    tx = transformed_event.x;
+    ty = transformed_event.y;
+    ctx_matrix_apply_transform (&item->inv_matrix, &tx, &ty);
+    transformed_event.x = tx;
+    transformed_event.y = ty;
+
+    if ((type & CTX_DRAG_PRESS) ||
+        (type & CTX_DRAG_MOTION) ||
+        (type & CTX_MOTION))   /* probably a worthwhile check for the performance 
+                                  benefit
+                                */
     {
-      CtxTermGlyph *glyph = ctx_calloc (sizeof (CtxTermGlyph), 1);
-      ctx_list_prepend (&rasterizer->glyphs, glyph);
-      glyph->unichar = string[i];
-      glyph->col = col;
-      glyph->row = row;
-      ctx_color_get_rgba8 (rasterizer->state, &rasterizer->state->gstate.source_fill.color,
-                      glyph->rgba_fg);
+      tx = transformed_event.start_x;
+      ty = transformed_event.start_y;
+      ctx_matrix_apply_transform (&item->inv_matrix, &tx, &ty);
+      transformed_event.start_x = tx;
+      transformed_event.start_y = ty;
     }
+
+
+    tx = transformed_event.delta_x;
+    ty = transformed_event.delta_y;
+    ctx_matrix_apply_transform (&item->inv_matrix, &tx, &ty);
+    transformed_event.delta_x = tx;
+    transformed_event.delta_y = ty;
   }
-  else
-#endif
+
+  transformed_event.state = ctx->events.modifier_state;
+  transformed_event.type = type;
+
+  for (i = item->cb_count-1; i >= 0; i--)
   {
-    _ctx_text (rasterizer->ctx, string, stroke, 1);
+    if (item->cb[i].types & type)
+    {
+      item->cb[i].cb (&transformed_event, item->cb[i].data1, item->cb[i].data2);
+      event->stop_propagate = transformed_event.stop_propagate; /* copy back the response */
+      if (event->stop_propagate)
+        return event->stop_propagate;
+    }
   }
+  return 0;
 }
+#endif
 
-void
-_ctx_font (Ctx *ctx, const char *name);
-static void
-ctx_rasterizer_set_font (CtxRasterizer *rasterizer, const char *font_name)
-{
-  _ctx_font (rasterizer->ctx, font_name);
-}
+#if CTX_EVENTS
 
-static void
-ctx_rasterizer_arc (CtxRasterizer *rasterizer,
-                    float          x,
-                    float          y,
-                    float          radius,
-                    float          start_angle,
-                    float          end_angle,
-                    int            anticlockwise)
-{
-  int full_segments = CTX_RASTERIZER_MAX_CIRCLE_SEGMENTS;
-  full_segments = radius * CTX_PI * 2 / 4.0;
-  if (full_segments > CTX_RASTERIZER_MAX_CIRCLE_SEGMENTS)
-    { full_segments = CTX_RASTERIZER_MAX_CIRCLE_SEGMENTS; }
-  if (full_segments < 24) full_segments = 24;
-  float step = CTX_PI*2.0/full_segments;
-  int steps;
+#if !__COSMOPOLITAN__
+#include <stdatomic.h>
+#endif
 
-  if (end_angle < -30.0)
-    end_angle = -30.0;
-  if (start_angle < -30.0)
-    start_angle = -30.0;
-  if (end_angle > 30.0)
-    end_angle = 30.0;
-  if (start_angle > 30.0)
-    start_angle = 30.0;
+int ctx_native_events = 0;
+#if CTX_SDL
+int ctx_sdl_events = 0;
+int ctx_sdl_consume_events (Ctx *ctx);
+#endif
 
-  if (radius <= 0.0001)
-          return;
+#if CTX_FB
+int ctx_fb_events = 0;
+int ctx_fb_consume_events (Ctx *ctx);
+#endif
 
-  if (end_angle == start_angle)
-          // XXX also detect arcs fully outside render view
-    {
-    if (rasterizer->has_prev!=0)
-      ctx_rasterizer_line_to (rasterizer, x + ctx_cosf (end_angle) * radius,
-                              y + ctx_sinf (end_angle) * radius);
-      else
-      ctx_rasterizer_move_to (rasterizer, x + ctx_cosf (end_angle) * radius,
-                            y + ctx_sinf (end_angle) * radius);
-      return;
-    }
-#if 1
-  if ( (!anticlockwise && fabsf((end_angle - start_angle) - CTX_PI*2) < 0.01f)  ||
-       ( (anticlockwise && fabsf((start_angle - end_angle) - CTX_PI*2) < 0.01f ) ) 
-  ||   (anticlockwise && fabsf((end_angle - start_angle) - CTX_PI*2) < 0.01f)  ||  (!anticlockwise && 
fabsf((start_angle - end_angle) - CTX_PI*2) < 0.01f )  )
-    {
-      start_angle = start_angle;
-      steps = full_segments - 1;
-    }
+int ctx_nct_consume_events (Ctx *ctx);
+int ctx_nct_has_event (Ctx  *n, int delay_ms);
+int ctx_ctx_consume_events (Ctx *ctx);
+
+
+
+void ctx_consume_events (Ctx *ctx)
+{
+#if CTX_SDL
+  if (ctx_sdl_events)
+    ctx_sdl_consume_events (ctx);
+  else
+#endif
+#if CTX_FB
+  if (ctx_fb_events)
+    ctx_fb_consume_events (ctx);
+  else
+#endif
+  if (ctx_native_events)
+    ctx_ctx_consume_events (ctx);
+  else
+    ctx_nct_consume_events (ctx);
+}
+
+int ctx_has_event (Ctx *ctx, int timeout)
+{
+#if CTX_SDL
+  if (ctx_sdl_events)
+  {
+    return SDL_WaitEventTimeout (NULL, timeout);
+  }
+  else
+#endif
+#if CTX_FB
+  if (ctx_fb_events)
+  {
+    return ctx_nct_has_event (ctx, timeout);
+  }
+  else
+#endif
+  if (ctx_native_events)
+  {
+    return ctx_nct_has_event (ctx, timeout);
+  }
+  else
+  {
+    return ctx_nct_has_event (ctx, timeout);
+  }
+
+  ctx_consume_events (ctx);
+  if (ctx->events.events)
+    return 1;
+  return 0;
+}
+
+#if CTX_FB
+static int ctx_fb_get_mice_fd (Ctx *ctx);
+#endif
+
+void ctx_get_event_fds (Ctx *ctx, int *fd, int *count)
+{
+#if CTX_SDL
+  if (ctx_sdl_events)
+  {
+    *count = 0;
+  }
   else
 #endif
+#if CTX_FB
+  if (ctx_fb_events)
+  {
+    int mice_fd = ctx_fb_get_mice_fd (ctx);
+    fd[0] = STDIN_FILENO;
+    if (mice_fd)
     {
-      steps = (end_angle - start_angle) / (CTX_PI*2) * full_segments;
-      if (anticlockwise)
-        { steps = full_segments - steps; };
-   // if (steps > full_segments)
-   //   steps = full_segments;
+      fd[1] = mice_fd;
+      *count = 2;
     }
-  if (anticlockwise) { step = step * -1; }
-  int first = 1;
-  if (steps == 0 /* || steps==full_segments -1  || (anticlockwise && steps == full_segments) */)
+    else
     {
-      float xv = x + ctx_cosf (start_angle) * radius;
-      float yv = y + ctx_sinf (start_angle) * radius;
-      if (!rasterizer->has_prev)
-        { ctx_rasterizer_move_to (rasterizer, xv, yv); }
-      first = 0;
+      *count = 1;
     }
+  }
   else
-    {
-      for (float angle = start_angle, i = 0; i < steps; angle += step, i++)
-        {
-          float xv = x + ctx_cosf (angle) * radius;
-          float yv = y + ctx_sinf (angle) * radius;
-          if (first && !rasterizer->has_prev)
-            { ctx_rasterizer_move_to (rasterizer, xv, yv); }
-          else
-            { ctx_rasterizer_line_to (rasterizer, xv, yv); }
-          first = 0;
-        }
-    }
-  ctx_rasterizer_line_to (rasterizer, x + ctx_cosf (end_angle) * radius,
-                          y + ctx_sinf (end_angle) * radius);
+#endif
+  if (ctx_native_events)
+  {
+    fd[0] = STDIN_FILENO;
+    *count = 1;
+  }
+  else
+  {
+    fd[0] = STDIN_FILENO;
+    *count = 1;
+  }
 }
 
-static void
-ctx_rasterizer_quad_to (CtxRasterizer *rasterizer,
-                        float        cx,
-                        float        cy,
-                        float        x,
-                        float        y)
+CtxEvent *ctx_get_event (Ctx *ctx)
 {
-  /* XXX : it is probably cheaper/faster to do quad interpolation directly -
-   *       though it will increase the code-size, an
-   *       alternative is to turn everything into cubic
-   *       and deal with cubics more directly during
-   *       rasterization
-   */
-  ctx_rasterizer_curve_to (rasterizer,
-                           (cx * 2 + rasterizer->x) / 3.0f, (cy * 2 + rasterizer->y) / 3.0f,
-                           (cx * 2 + x) / 3.0f,           (cy * 2 + y) / 3.0f,
-                           x,                              y);
+  static CtxEvent event_copy;
+  _ctx_idle_iteration (ctx);
+  if (!ctx->events.ctx_get_event_enabled)
+    ctx->events.ctx_get_event_enabled = 1;
+
+  ctx_consume_events (ctx);
+
+  if (ctx->events.events)
+    {
+      event_copy = *((CtxEvent*)(ctx->events.events->data));
+      ctx_list_remove (&ctx->events.events, ctx->events.events->data);
+      return &event_copy;
+    }
+  return NULL;
 }
 
-static void
-ctx_rasterizer_rel_quad_to (CtxRasterizer *rasterizer,
-                            float cx, float cy,
-                            float x,  float y)
+static int
+_ctx_emit_cb (Ctx *ctx, CtxList *items, CtxEvent *event, CtxEventType type, float x, float y)
 {
-  ctx_rasterizer_quad_to (rasterizer, cx + rasterizer->x, cy + rasterizer->y,
-                          x  + rasterizer->x, y  + rasterizer->y);
+  CtxList *l;
+  event->stop_propagate = 0;
+  for (l = items; l; l = l->next)
+  {
+    _ctx_emit_cb_item (ctx, l->data, event, type, x, y);
+    if (event->stop_propagate)
+      return event->stop_propagate;
+  }
+  return 0;
 }
 
-#define LENGTH_OVERSAMPLE 1
-static void
-ctx_rasterizer_pset (CtxRasterizer *rasterizer, int x, int y, uint8_t cov)
+/*
+ * update what is the currently hovered item and returns it.. and the list of hits
+ * a well.
+ *
+ */
+static CtxItem *_ctx_update_item (Ctx *ctx, int device_no, float x, float y, CtxEventType type, CtxList 
**hitlist)
 {
-  // XXX - we avoid rendering here x==0 - to keep with
-  //  an off-by one elsewhere
-  //
-  //  XXX onlt works in rgba8 formats
-  if (x <= 0 || y < 0 || x >= rasterizer->blit_width ||
-      y >= rasterizer->blit_height)
-    { return; }
-  uint8_t fg_color[4];
-  ctx_color_get_rgba8 (rasterizer->state, &rasterizer->state->gstate.source_fill.color, fg_color);
-  uint8_t pixel[4];
-  uint8_t *dst = ( (uint8_t *) rasterizer->buf);
-  dst += y * rasterizer->blit_stride;
-  dst += x * rasterizer->format->bpp / 8;
-  if (!rasterizer->format->to_comp ||
-      !rasterizer->format->from_comp)
-    { return; }
-  if (cov == 255)
+  CtxItem *current = NULL;
+
+  CtxList *l = _ctx_detect_list (ctx, x, y, type);
+  if (l)
+  {
+    ctx_list_reverse (&l);
+    current = l->data;
+  }
+  if (hitlist)
+    *hitlist = l;
+  else
+    ctx_list_free (&l);
+
+  if (ctx->events.prev[device_no] == NULL || current == NULL || (current->path_hash != 
ctx->events.prev[device_no]->path_hash))
+  {
+// enter/leave should snapshot chain to root
+// and compare with previous snapshotted chain to root
+// and emit/enter/leave as appropriate..
+//
+// leave might be registered for emission on enter..emission?
+
+
+    //int focus_radius = 2;
+    if (current)
+      _ctx_item_ref (current);
+
+    if (ctx->events.prev[device_no])
     {
-      for (int c = 0; c < 4; c++)
-        {
-          pixel[c] = fg_color[c];
-        }
+      {
+#if 0
+        CtxIntRectangle rect = {floor(ctx->events.prev[device_no]->x0-focus_radius),
+                             floor(ctx->events.prev[device_no]->y0-focus_radius),
+                             ceil(ctx->events.prev[device_no]->x1)-floor(ctx->events.prev[device_no]->x0) + 
focus_radius * 2,
+                             ceil(ctx->events.prev[device_no]->y1)-floor(ctx->events.prev[device_no]->y0) + 
focus_radius * 2};
+        mrg_queue_draw (mrg, &rect);
+#endif 
+      }
+
+      _ctx_emit_cb_item (ctx, ctx->events.prev[device_no], NULL, CTX_LEAVE, x, y);
+      _ctx_item_unref (ctx->events.prev[device_no]);
+      ctx->events.prev[device_no] = NULL;
     }
-  else
+    if (current)
     {
-      rasterizer->format->to_comp (rasterizer, x, dst, &pixel[0], 1);
-      for (int c = 0; c < 4; c++)
-        {
-          pixel[c] = ctx_lerp_u8 (pixel[c], fg_color[c], cov);
-        }
+#if 0
+      {
+        CtxIntRectangle rect = {floor(current->x0-focus_radius),
+                             floor(current->y0-focus_radius),
+                             ceil(current->x1)-floor(current->x0) + focus_radius * 2,
+                             ceil(current->y1)-floor(current->y0) + focus_radius * 2};
+        mrg_queue_draw (mrg, &rect);
+      }
+#endif
+      _ctx_emit_cb_item (ctx, current, NULL, CTX_ENTER, x, y);
+      ctx->events.prev[device_no] = current;
     }
-  rasterizer->format->from_comp (rasterizer, x, &pixel[0], dst, 1);
+  }
+  current = _ctx_detect (ctx, x, y, type);
+  //fprintf (stderr, "%p\n", current);
+  return current;
 }
 
-static void
-ctx_rasterizer_stroke_1px (CtxRasterizer *rasterizer)
+static int tap_and_hold_fire (Ctx *ctx, void *data)
 {
-  int count = rasterizer->edge_list.count;
-  CtxSegment *temp = (CtxSegment*)rasterizer->edge_list.entries;
-  float prev_x = 0.0f;
-  float prev_y = 0.0f;
-  int aa = 15;//rasterizer->aa;
-  int start = 0;
-  int end = 0;
-#if 0
-  float factor = ctx_matrix_get_scale (&state->gstate.transform);
-#endif
+  CtxGrab *grab = data;
+  CtxList *list = NULL;
+  ctx_list_prepend (&list, grab->item);
+  CtxEvent event = {0, };
 
-  while (start < count)
+  event.ctx = ctx;
+  event.time = ctx_ms (ctx);
+
+  event.device_x = 
+  event.x = ctx->events.pointer_x[grab->device_no];
+  event.device_y = 
+  event.y = ctx->events.pointer_y[grab->device_no];
+
+  // XXX: x and y coordinates
+  int ret = _ctx_emit_cb (ctx, list, &event, CTX_TAP_AND_HOLD,
+      ctx->events.pointer_x[grab->device_no], ctx->events.pointer_y[grab->device_no]);
+
+  ctx_list_free (&list);
+
+  grab->timeout_id = 0;
+
+  return 0;
+
+  return ret;
+}
+
+int ctx_pointer_drop (Ctx *ctx, float x, float y, int device_no, uint32_t time,
+                      char *string)
+{
+  CtxList *l;
+  CtxList *hitlist = NULL;
+
+  ctx->events.pointer_x[device_no] = x;
+  ctx->events.pointer_y[device_no] = y;
+  if (device_no <= 3)
+  {
+    ctx->events.pointer_x[0] = x;
+    ctx->events.pointer_y[0] = y;
+  }
+
+  if (device_no < 0) device_no = 0;
+  if (device_no >= CTX_MAX_DEVICES) device_no = CTX_MAX_DEVICES-1;
+  CtxEvent *event = &ctx->events.drag_event[device_no];
+
+  if (time == 0)
+    time = ctx_ms (ctx);
+
+  event->ctx = ctx;
+  event->x = x;
+  event->y = y;
+
+  event->delta_x = event->delta_y = 0;
+
+  event->device_no = device_no;
+  event->string    = string;
+  event->time      = time;
+  event->stop_propagate = 0;
+
+  _ctx_update_item (ctx, device_no, x, y, CTX_DROP, &hitlist);
+
+  for (l = hitlist; l; l = l?l->next:NULL)
+  {
+    CtxItem *item = l->data;
+    _ctx_emit_cb_item (ctx, item, event, CTX_DROP, x, y);
+
+    if (event->stop_propagate)
     {
-      int started = 0;
-      int i;
-      for (i = start; i < count; i++)
-        {
-          CtxSegment *entry = &temp[i];
-          float x, y;
-          if (entry->code == CTX_NEW_EDGE)
-            {
-              if (started)
-                {
-                  end = i - 1;
-                  goto foo;
-                }
-              prev_x = entry->data.s16[0] * 1.0f / CTX_SUBDIV;
-              prev_y = entry->data.s16[1] * 1.0f / aa;
-              started = 1;
-              start = i;
-            }
-          x = entry->data.s16[2] * 1.0f / CTX_SUBDIV;
-          y = entry->data.s16[3] * 1.0f / aa;
-          int dx = x - prev_x;
-          int dy = y - prev_y;
-          int length = ctx_maxf (abs (dx), abs (dy) );
-          if (length)
-            {
-              length *= LENGTH_OVERSAMPLE;
-              int len = length;
-              int tx = prev_x * 256;
-              int ty = prev_y * 256;
-              dx *= 256;
-              dy *= 256;
-              dx /= length;
-              dy /= length;
-              for (int i = 0; i < len; i++)
-                {
-                  ctx_rasterizer_pset (rasterizer, tx/256, ty/256, 255);
-                  tx += dx;
-                  ty += dy;
-                  ctx_rasterizer_pset (rasterizer, tx/256, ty/256, 255);
-                }
-            }
-          prev_x = x;
-          prev_y = y;
-        }
-      end = i-1;
-foo:
-      start = end+1;
+      ctx_list_free (&hitlist);
+      return 0;
     }
-  ctx_rasterizer_reset (rasterizer);
+  }
+
+  //mrg_queue_draw (mrg, NULL); /* in case of style change, and more  */
+  ctx_list_free (&hitlist);
+
+  return 0;
 }
 
-static void
-ctx_rasterizer_stroke (CtxRasterizer *rasterizer)
+int ctx_pointer_press (Ctx *ctx, float x, float y, int device_no, uint32_t time)
 {
-  CtxGState *gstate = &rasterizer->state->gstate;
-  CtxSource source_backup;
-  if (gstate->source_stroke.type != CTX_SOURCE_INHERIT_FILL)
+  CtxEvents *events = &ctx->events;
+  CtxList *hitlist = NULL;
+  events->pointer_x[device_no] = x;
+  events->pointer_y[device_no] = y;
+  if (device_no <= 3)
   {
-    source_backup = gstate->source_fill;
-    gstate->source_fill = rasterizer->state->gstate.source_stroke;
+    events->pointer_x[0] = x;
+    events->pointer_y[0] = y;
   }
-  int count = rasterizer->edge_list.count;
-  int preserved = rasterizer->preserve;
-  float factor = ctx_matrix_get_scale (&gstate->transform);
-  float line_width = gstate->line_width * factor;
 
-  CtxSegment temp[count]; /* copy of already built up path's poly line  */
-  memcpy (temp, rasterizer->edge_list.entries, sizeof (temp) );
+  if (device_no < 0) device_no = 0;
+  if (device_no >= CTX_MAX_DEVICES) device_no = CTX_MAX_DEVICES-1;
+  CtxEvent *event = &events->drag_event[device_no];
 
-#if CTX_RECT_FILL
-  if (rasterizer->edge_list.count == 6)
-    {
-      CtxSegment *entry0 = &((CtxSegment*)rasterizer->edge_list.entries)[0];
-      CtxSegment *entry1 = &((CtxSegment*)rasterizer->edge_list.entries)[1];
-      CtxSegment *entry2 = &((CtxSegment*)rasterizer->edge_list.entries)[2];
-      CtxSegment *entry3 = &((CtxSegment*)rasterizer->edge_list.entries)[3];
-      //fprintf (stderr, "{%i %.2f %.2f}", lw, lwmod, line_width);
+  if (time == 0)
+    time = ctx_ms (ctx);
 
-      if (!rasterizer->state->gstate.clipped &&
-          (entry0->data.s16[2] == entry1->data.s16[2]) &&
-          (entry0->data.s16[3] == entry3->data.s16[3]) &&
-          (entry1->data.s16[3] == entry2->data.s16[3]) &&
-          (entry2->data.s16[2] == entry3->data.s16[2])
-#if CTX_ENABLE_SHADOW_BLUR
-           && !rasterizer->in_shadow
-#endif
-         )
-       {
-      float lwmod = ctx_fmod1f (line_width);
-      int lw = ctx_floorf (line_width + 0.5f);
-      int is_compat_even = (lw % 2 == 0) && (lwmod < 0.1); // only even linewidths implemented properly
-      int is_compat_odd = (lw % 2 == 1) && (lwmod < 0.1); // only even linewidths implemented properly
+  event->x = event->start_x = event->prev_x = x;
+  event->y = event->start_y = event->prev_y = y;
 
-      int off_x = 0;
-      int off_y = 0;
+  event->delta_x = event->delta_y = 0;
 
+  event->device_no = device_no;
+  event->time      = time;
+  event->stop_propagate = 0;
 
-      if (is_compat_odd)
-      {
-        off_x = CTX_SUBDIV/2;
-        off_y = CTX_FULL_AA/2;
-      }
+  if (events->pointer_down[device_no] == 1)
+  {
+    fprintf (stderr, "events thought device %i was already down\n", device_no);
+  }
+  /* doing just one of these two should be enough? */
+  events->pointer_down[device_no] = 1;
+  switch (device_no)
+  {
+    case 1:
+      events->modifier_state |= CTX_MODIFIER_STATE_BUTTON1;
+      break;
+    case 2:
+      events->modifier_state |= CTX_MODIFIER_STATE_BUTTON2;
+      break;
+    case 3:
+      events->modifier_state |= CTX_MODIFIER_STATE_BUTTON3;
+      break;
+    default:
+      break;
+  }
 
-      if((is_compat_odd || is_compat_even) &&
-         (((entry1->data.s16[2]-off_x) % (CTX_SUBDIV))  == 0)  &&
-         (((entry1->data.s16[3]-off_y) % (CTX_FULL_AA)) == 0) &&
-         (((entry3->data.s16[2]-off_x) % (CTX_SUBDIV))  == 0)  &&
-         (((entry3->data.s16[3]-off_y) % (CTX_FULL_AA)) == 0))
-      {
-        float x0 = entry3->data.s16[2] * 1.0f / CTX_SUBDIV;
-        float y0 = entry3->data.s16[3] * 1.0f / CTX_FULL_AA;
-        float x1 = entry1->data.s16[2] * 1.0f / CTX_SUBDIV;
-        float y1 = entry1->data.s16[3] * 1.0f / CTX_FULL_AA;
+  CtxGrab *grab = NULL;
+  CtxList *l;
 
-        int bw = lw/2+1;
-        int bwb = lw/2;
+  _ctx_update_item (ctx, device_no, x, y, 
+      CTX_PRESS | CTX_DRAG_PRESS | CTX_TAP | CTX_TAP_AND_HOLD, &hitlist);
 
-        if (is_compat_even)
-        {
-          bw = lw/2;
-        }
-        ctx_rasterizer_fill_rect (rasterizer, x0-bwb, y0-bwb, x1+bw, y0+bw, 255);
-        ctx_rasterizer_fill_rect (rasterizer, x0-bwb, y1-bwb, x1-bwb, y1+bw, 255);
-        ctx_rasterizer_fill_rect (rasterizer, x0-bwb, y0, x0+bw, y1, 255);
-        ctx_rasterizer_fill_rect (rasterizer, x1-bwb, y0, x1+bw, y1+bw, 255);
-        ctx_rasterizer_reset (rasterizer);
-        goto done;
-      }
-       }
-    }
-#endif
-  
+  for (l = hitlist; l; l = l?l->next:NULL)
+  {
+    CtxItem *item = l->data;
+    if (item &&
+        ((item->types & CTX_DRAG)||
+         (item->types & CTX_TAP) ||
+         (item->types & CTX_TAP_AND_HOLD)))
     {
+      grab = device_add_grab (ctx, device_no, item, item->types);
+      grab->start_time = time;
 
-  int aa = CTX_FULL_AA;
-#if 0
-  if (CTX_UNLIKELY(gstate->line_width * factor <= 0.0f &&
-      gstate->line_width * factor > -10.0f))
-    {
-      ctx_rasterizer_stroke_1px (rasterizer);
-    }
-  else
-#endif
-    {
-      if (line_width < 5.0f)
+      if (item->types & CTX_TAP_AND_HOLD)
       {
-      factor *= 0.89; /* this hack adjustment makes sharp 1px and 2px strokewidths
-      //                 end up sharp without erronious AA; we seem to be off by
-      //                 one somewhere else, causing the need for this
-      //                 */
-      line_width *= 0.89f;
+         grab->timeout_id = ctx_add_timeout (ctx, events->tap_delay_hold, tap_and_hold_fire, grab);
       }
-      ctx_rasterizer_reset (rasterizer); /* then start afresh with our stroked shape  */
-      CtxMatrix transform_backup = gstate->transform;
-      _ctx_matrix_identity (&gstate->transform);
-      float prev_x = 0.0f;
-      float prev_y = 0.0f;
-      float half_width_x = line_width/2;
-      float half_width_y = line_width/2;
-      if (CTX_UNLIKELY(line_width <= 0.0f))
-        { // makes 0 width be hairline
-          half_width_x = .5f;
-          half_width_y = .5f;
-        }
-      int start = 0;
-      int end   = 0;
-      while (start < count)
-        {
-          int started = 0;
-          int i;
-          for (i = start; i < count; i++)
-            {
-              CtxSegment *entry = &temp[i];
-              float x, y;
-              if (CTX_UNLIKELY(entry->code == CTX_NEW_EDGE))
-                {
-                  if (CTX_LIKELY(started))
-                    {
-                      end = i - 1;
-                      goto foo;
-                    }
-                  prev_x = entry->data.s16[0] * 1.0f / CTX_SUBDIV;
-                  prev_y = entry->data.s16[1] * 1.0f / aa;
-                  started = 1;
-                  start = i;
-                }
-              x = entry->data.s16[2] * 1.0f / CTX_SUBDIV;
-              y = entry->data.s16[3] * 1.0f / aa;
-              float dx = x - prev_x;
-              float dy = y - prev_y;
-              float length = ctx_fast_hypotf (dx, dy);
-              if (CTX_LIKELY(length>0.001f))
-                {
-                  float recip_length = 1.0/length;
-                  dx = dx * recip_length * half_width_x;
-                  dy = dy * recip_length * half_width_y;
-                  if (CTX_UNLIKELY(entry->code == CTX_NEW_EDGE))
-                    {
-                      ctx_rasterizer_finish_shape (rasterizer);
-                      ctx_rasterizer_move_to (rasterizer, prev_x+dy, prev_y-dx);
-                    }
-                  ctx_rasterizer_line_to (rasterizer, prev_x-dy, prev_y+dx);
-                  
-                  // we need to know the slope of the other side
-
-                  // XXX possible miter line-to
-                  //ctx_rasterizer_line_to (rasterizer, prev_x-dy+4, prev_y+dx+10);
-                  //ctx_rasterizer_line_to (rasterizer, prev_x-dy+8, prev_y+dx+0);
-
-
-                  ctx_rasterizer_line_to (rasterizer, x-dy, y+dx);
-                }
-              prev_x = x;
-              prev_y = y;
-            }
-          end = i-1;
-foo:
-          for (int i = end; i >= start; i--)
-            {
-              CtxSegment *entry = &temp[i];
-              float x, y, dx, dy;
-              x = entry->data.s16[2] * 1.0f / CTX_SUBDIV;
-              y = entry->data.s16[3] * 1.0f / aa;
-              dx = x - prev_x;
-              dy = y - prev_y;
-              float length = ctx_fast_hypotf (dx, dy);
-              float recip_length = 1.0f/length;
-              dx = dx * recip_length * half_width_x;
-              dy = dy * recip_length * half_width_y;
-              if (CTX_LIKELY(length>0.001f))
-                {
-                  ctx_rasterizer_line_to (rasterizer, prev_x-dy, prev_y+dx);
-                  // XXX possible miter line-to
-             //   ctx_rasterizer_line_to (rasterizer, prev_x-dy+10, prev_y+dx+10);
-                  ctx_rasterizer_line_to (rasterizer, x-dy,      y+dx);
-                }
-              prev_x = x;
-              prev_y = y;
-              if (CTX_UNLIKELY(entry->code == CTX_NEW_EDGE))
-                {
-                  x = entry->data.s16[0] * 1.0f / CTX_SUBDIV;
-                  y = entry->data.s16[1] * 1.0f / aa;
-                  dx = x - prev_x;
-                  dy = y - prev_y;
-                  length = ctx_fast_hypotf (dx, dy);
-                  recip_length = 1.0f/length;
-                  if (CTX_LIKELY(length>0.001f))
-                    {
-                      dx = dx * recip_length * half_width_x;
-                      dy = dy * recip_length * half_width_y;
-                      ctx_rasterizer_line_to (rasterizer, prev_x-dy, prev_y+dx);
-                      ctx_rasterizer_line_to (rasterizer, x-dy, y+dx);
-                    }
-                }
-              if ( (prev_x != x) && (prev_y != y) )
-                {
-                  prev_x = x;
-                  prev_y = y;
-                }
-            }
-          start = end+1;
-        }
-      ctx_rasterizer_finish_shape (rasterizer);
-      switch (gstate->line_cap)
-        {
-          case CTX_CAP_SQUARE: // XXX: incorrect - if rectangles were in
-                               //                  reverse order - rotation would be off
-                               //                  better implement correct here
-            {
-              float x = 0, y = 0;
-              int has_prev = 0;
-              for (int i = 0; i < count; i++)
-                {
-                  CtxSegment *entry = &temp[i];
-                  if (CTX_UNLIKELY(entry->code == CTX_NEW_EDGE))
-                    {
-                      if (has_prev)
-                        {
-                          ctx_rasterizer_rectangle (rasterizer, x - half_width_x, y - half_width_y, 
half_width_x, half_width_y);
-                          ctx_rasterizer_finish_shape (rasterizer);
-                        }
-                      x = entry->data.s16[0] * 1.0f / CTX_SUBDIV;
-                      y = entry->data.s16[1] * 1.0f / aa;
-                      ctx_rasterizer_rectangle (rasterizer, x - half_width_x, y - half_width_y, half_width_x 
* 2, half_width_y * 2);
-                      ctx_rasterizer_finish_shape (rasterizer);
-                    }
-                  x = entry->data.s16[2] * 1.0f / CTX_SUBDIV;
-                  y = entry->data.s16[3] * 1.0f / aa;
-                  has_prev = 1;
-                }
-              ctx_rasterizer_rectangle (rasterizer, x - half_width_x, y - half_width_y, half_width_x * 2, 
half_width_y * 2);
-              ctx_rasterizer_finish_shape (rasterizer);
-            }
-            break;
-          case CTX_CAP_NONE: /* nothing to do */
-            break;
-          case CTX_CAP_ROUND:
-            {
-              float x = 0, y = 0;
-              int has_prev = 0;
-              for (int i = 0; i < count; i++)
-                {
-                  CtxSegment *entry = &temp[i];
-                  if (CTX_UNLIKELY(entry->code == CTX_NEW_EDGE))
-                    {
-                      if (has_prev)
-                        {
-                          ctx_rasterizer_arc (rasterizer, x, y, half_width_x, CTX_PI*3, 0, 1);
-                          ctx_rasterizer_finish_shape (rasterizer);
-                        }
-                      x = entry->data.s16[0] * 1.0f / CTX_SUBDIV;
-                      y = entry->data.s16[1] * 1.0f / aa;
-                      ctx_rasterizer_arc (rasterizer, x, y, half_width_x, CTX_PI*3, 0, 1);
-                      ctx_rasterizer_finish_shape (rasterizer);
-                    }
-                  x = entry->data.s16[2] * 1.0f / CTX_SUBDIV;
-                  y = entry->data.s16[3] * 1.0f / aa;
-                  has_prev = 1;
-                }
-              ctx_rasterizer_move_to (rasterizer, x, y);
-              ctx_rasterizer_arc (rasterizer, x, y, half_width_x, CTX_PI*3, 0, 1);
-              ctx_rasterizer_finish_shape (rasterizer);
-              break;
-            }
-        }
-      switch (gstate->line_join)
-        {
-          case CTX_JOIN_BEVEL:
-          case CTX_JOIN_MITER:
-            break;
-          case CTX_JOIN_ROUND:
-            {
-              float x = 0, y = 0;
-              for (int i = 0; i < count-1; i++)
-                {
-                  CtxSegment *entry = &temp[i];
-                  x = entry->data.s16[2] * 1.0f / CTX_SUBDIV;
-                  y = entry->data.s16[3] * 1.0f / aa;
-                  if (CTX_UNLIKELY(entry[1].code == CTX_EDGE))
-                    {
-                      ctx_rasterizer_arc (rasterizer, x, y, half_width_x, CTX_PI*2, 0, 1);
-                      ctx_rasterizer_finish_shape (rasterizer);
-                    }
-                }
-              break;
-            }
-        }
-      CtxFillRule rule_backup = gstate->fill_rule;
-      gstate->fill_rule = CTX_FILL_RULE_WINDING;
-      rasterizer->preserve = 0; // so fill isn't tripped
-      ctx_rasterizer_fill (rasterizer);
-      gstate->fill_rule = rule_backup;
-      gstate->transform = transform_backup;
     }
-  }
-done:
-  if (preserved)
+    _ctx_emit_cb_item (ctx, item, event, CTX_PRESS, x, y);
+    if (!event->stop_propagate)
+      _ctx_emit_cb_item (ctx, item, event, CTX_DRAG_PRESS, x, y);
+
+    if (event->stop_propagate)
     {
-      memcpy (rasterizer->edge_list.entries, temp, sizeof (temp) );
-      rasterizer->edge_list.count = count;
-      rasterizer->preserve = 0;
+      ctx_list_free (&hitlist);
+      return 0;
     }
-  if (gstate->source_stroke.type != CTX_SOURCE_INHERIT_FILL)
-    gstate->source_fill = source_backup;
+  }
+
+  //events_queue_draw (mrg, NULL); /* in case of style change, and more  */
+  ctx_list_free (&hitlist);
+  return 0;
 }
 
-#if CTX_1BIT_CLIP
-#define CTX_CLIP_FORMAT CTX_FORMAT_GRAY1
-#else
-#define CTX_CLIP_FORMAT CTX_FORMAT_GRAY8
-#endif
+void _ctx_resized (Ctx *ctx, int width, int height, long time)
+{
+  CtxItem *item = _ctx_detect (ctx, 0, 0, CTX_KEY_PRESS);
+  CtxEvent event = {0, };
 
+  if (!time)
+    time = ctx_ms (ctx);
+  
+  event.ctx = ctx;
+  event.time = time;
+  event.string = "resize-event"; /* gets delivered to clients as a key_down event, maybe message shouldbe 
used instead?
+   */
 
-static void
-ctx_rasterizer_clip_reset (CtxRasterizer *rasterizer)
-{
-#if CTX_ENABLE_CLIP
-  if (rasterizer->clip_buffer)
-   ctx_buffer_free (rasterizer->clip_buffer);
-  rasterizer->clip_buffer = NULL;
-#endif
-  rasterizer->state->gstate.clip_min_x = rasterizer->blit_x;
-  rasterizer->state->gstate.clip_min_y = rasterizer->blit_y;
+  if (item)
+  {
+    event.stop_propagate = 0;
+    _ctx_emit_cb_item (ctx, item, &event, CTX_KEY_PRESS, 0, 0);
+  }
 
-  rasterizer->state->gstate.clip_max_x = rasterizer->blit_x + rasterizer->blit_width - 1;
-  rasterizer->state->gstate.clip_max_y = rasterizer->blit_y + rasterizer->blit_height - 1;
 }
 
-static void
-ctx_rasterizer_clip_apply (CtxRasterizer *rasterizer,
-                           CtxSegment    *edges)
+int ctx_pointer_release (Ctx *ctx, float x, float y, int device_no, uint32_t time)
 {
-  int count = edges[0].data.u32[0];
+  CtxEvents *events = &ctx->events;
+  if (time == 0)
+    time = ctx_ms (ctx);
 
-  int minx = 5000;
-  int miny = 5000;
-  int maxx = -5000;
-  int maxy = -5000;
-  int prev_x = 0;
-  int prev_y = 0;
-  int blit_width = rasterizer->blit_width;
-  int blit_height = rasterizer->blit_height;
+  if (device_no < 0) device_no = 0;
+  if (device_no >= CTX_MAX_DEVICES) device_no = CTX_MAX_DEVICES-1;
+  CtxEvent *event = &events->drag_event[device_no];
 
-  int aa = 15;//rasterizer->aa;
-  float coords[6][2];
+  event->time = time;
+  event->x = x;
+  event->ctx = ctx;
+  event->y = y;
+  event->device_no = device_no;
+  event->stop_propagate = 0;
 
-  for (int i = 0; i < count; i++)
-    {
-      CtxSegment *entry = &edges[i+1];
-      float x, y;
-      if (entry->code == CTX_NEW_EDGE)
-        {
-          prev_x = entry->data.s16[0] * 1.0f / CTX_SUBDIV;
-          prev_y = entry->data.s16[1] * 1.0f / aa;
-          if (prev_x < minx) { minx = prev_x; }
-          if (prev_y < miny) { miny = prev_y; }
-          if (prev_x > maxx) { maxx = prev_x; }
-          if (prev_y > maxy) { maxy = prev_y; }
-        }
-      x = entry->data.s16[2] * 1.0f / CTX_SUBDIV;
-      y = entry->data.s16[3] * 1.0f / aa;
-      if (x < minx) { minx = x; }
-      if (y < miny) { miny = y; }
-      if (x > maxx) { maxx = x; }
-      if (y > maxy) { maxy = y; }
+  switch (device_no)
+  {
+    case 1:
+      if (events->modifier_state & CTX_MODIFIER_STATE_BUTTON1)
+        events->modifier_state -= CTX_MODIFIER_STATE_BUTTON1;
+      break;
+    case 2:
+      if (events->modifier_state & CTX_MODIFIER_STATE_BUTTON2)
+        events->modifier_state -= CTX_MODIFIER_STATE_BUTTON2;
+      break;
+    case 3:
+      if (events->modifier_state & CTX_MODIFIER_STATE_BUTTON3)
+        events->modifier_state -= CTX_MODIFIER_STATE_BUTTON3;
+      break;
+    default:
+      break;
+  }
 
-      if (i < 6)
-      {
-        coords[i][0] = x;
-        coords[i][1] = y;
-      }
-    }
+  //events_queue_draw (mrg, NULL); /* in case of style change */
 
-#if CTX_ENABLE_CLIP
+  if (events->pointer_down[device_no] == 0)
+  {
+    fprintf (stderr, "device %i already up\n", device_no);
+  }
+  events->pointer_down[device_no] = 0;
 
-  if ((rasterizer->clip_rectangle==1
-                          || !rasterizer->clip_buffer)
-                  )
+  events->pointer_x[device_no] = x;
+  events->pointer_y[device_no] = y;
+  if (device_no <= 3)
   {
-    if (count == 6)
-    {
-      if (coords[3][0] == coords[5][0] &&
-          coords[3][1] == coords[5][1])
-      {
-#if 0
-        printf ("%d,%d %dx%d\n", minx, miny,
-                                       maxx-minx+1, maxy-miny+1);
-#endif
+    events->pointer_x[0] = x;
+    events->pointer_y[0] = y;
+  }
+  CtxList *hitlist = NULL;
+  CtxList *grablist = NULL , *g= NULL;
+  CtxGrab *grab;
 
-         rasterizer->state->gstate.clip_min_x =
-            ctx_maxi (minx, rasterizer->state->gstate.clip_min_x);
-         rasterizer->state->gstate.clip_min_y =
-            ctx_maxi (miny, rasterizer->state->gstate.clip_min_y);
-         rasterizer->state->gstate.clip_max_x =
-            ctx_mini (maxx, rasterizer->state->gstate.clip_max_x);
-         rasterizer->state->gstate.clip_max_y =
-            ctx_mini (maxy, rasterizer->state->gstate.clip_max_y);
+  _ctx_update_item (ctx, device_no, x, y, CTX_RELEASE | CTX_DRAG_RELEASE, &hitlist);
+  grablist = _ctx_device_get_grabs (ctx, device_no);
 
-         rasterizer->clip_rectangle = 1;
+  for (g = grablist; g; g = g->next)
+  {
+    grab = g->data;
 
-#if 0
-         if (!rasterizer->clip_buffer)
-           rasterizer->clip_buffer = ctx_buffer_new (blit_width,
-                                                     blit_height,
-                                                     CTX_CLIP_FORMAT);
+    if (!event->stop_propagate)
+    {
+      if (grab->item->types & CTX_TAP)
+      {
+        long delay = time - grab->start_time;
 
-         memset (rasterizer->clip_buffer->data, 0, blit_width * blit_height);
-         int i = 0;
-         for (int y = rasterizer->state->gstate.clip_min_y;
-                  y <= rasterizer->state->gstate.clip_max_y;
-                  y++)
-         for (int x = rasterizer->state->gstate.clip_min_x;
-                  x <= rasterizer->state->gstate.clip_max_x;
-                  x++, i++)
-         {
-           ((uint8_t*)(rasterizer->clip_buffer->data))[i] = 255;
-         }
-#endif
+        if (delay > events->tap_delay_min &&
+            delay < events->tap_delay_max &&
+            (
+              (event->start_x - x) * (event->start_x - x) +
+              (event->start_y - y) * (event->start_y - y)) < ctx_pow2(events->tap_hysteresis)
+            )
+        {
+          _ctx_emit_cb_item (ctx, grab->item, event, CTX_TAP, x, y);
+        }
+      }
 
-         return;
+      if (!event->stop_propagate && grab->item->types & CTX_DRAG_RELEASE)
+      {
+        _ctx_emit_cb_item (ctx, grab->item, event, CTX_DRAG_RELEASE, x, y);
       }
     }
-  }
-  rasterizer->clip_rectangle = 0;
 
-  if ((minx == maxx) || (miny == maxy)) // XXX : reset hack
-  {
-    ctx_rasterizer_clip_reset (rasterizer);
-    return;//goto done;
+    device_remove_grab (ctx, grab);
   }
 
-  int we_made_it = 0;
-  CtxBuffer *clip_buffer;
-
-  if (!rasterizer->clip_buffer)
-  {
-    rasterizer->clip_buffer = ctx_buffer_new (blit_width,
-                                              blit_height,
-                                              CTX_CLIP_FORMAT);
-    clip_buffer = rasterizer->clip_buffer;
-    we_made_it = 1;
-    if (CTX_CLIP_FORMAT == CTX_FORMAT_GRAY1)
-      memset (rasterizer->clip_buffer->data, 0, blit_width * blit_height/8);
-    else
-      memset (rasterizer->clip_buffer->data, 0, blit_width * blit_height);
-  }
-  else
+  if (hitlist)
   {
-    clip_buffer = ctx_buffer_new (blit_width, blit_height,
-                                  CTX_CLIP_FORMAT);
+    if (!event->stop_propagate)
+      _ctx_emit_cb (ctx, hitlist, event, CTX_RELEASE, x, y);
+    ctx_list_free (&hitlist);
   }
+  ctx_list_free (&grablist);
+  return 0;
+}
 
-  {
+/*  for multi-touch, we use a list of active grabs - thus a grab corresponds to
+ *  a device id. even during drag-grabs events propagate; to stop that stop
+ *  propagation.
+ */
+int ctx_pointer_motion (Ctx *ctx, float x, float y, int device_no, uint32_t time)
+{
+  CtxList *hitlist = NULL;
+  CtxList *grablist = NULL, *g;
+  CtxGrab *grab;
 
-  int prev_x = 0;
-  int prev_y = 0;
+  if (device_no < 0) device_no = 0;
+  if (device_no >= CTX_MAX_DEVICES) device_no = CTX_MAX_DEVICES-1;
+  CtxEvent *event = &ctx->events.drag_event[device_no];
 
-    Ctx *ctx = ctx_new_for_framebuffer (clip_buffer->data, blit_width, blit_height,
-       blit_width,
-       CTX_CLIP_FORMAT);
+  if (time == 0)
+    time = ctx_ms (ctx);
 
-  for (int i = 0; i < count; i++)
-    {
-      CtxSegment *entry = &edges[i+1];
-      float x, y;
-      if (entry->code == CTX_NEW_EDGE)
-        {
-          prev_x = entry->data.s16[0] * 1.0f / CTX_SUBDIV;
-          prev_y = entry->data.s16[1] * 1.0f / aa;
-          ctx_move_to (ctx, prev_x, prev_y);
-        }
-      x = entry->data.s16[2] * 1.0f / CTX_SUBDIV;
-      y = entry->data.s16[3] * 1.0f / aa;
-      ctx_line_to (ctx, x, y);
-    }
-    ctx_gray (ctx, 1.0f);
-    ctx_fill (ctx);
-    ctx_free (ctx);
+  event->ctx       = ctx;
+  event->x         = x;
+  event->y         = y;
+  event->time      = time;
+  event->device_no = device_no;
+  event->stop_propagate = 0;
+  
+  ctx->events.pointer_x[device_no] = x;
+  ctx->events.pointer_y[device_no] = y;
+
+  if (device_no <= 3)
+  {
+    ctx->events.pointer_x[0] = x;
+    ctx->events.pointer_y[0] = y;
   }
 
-  int maybe_rect = 1;
-  rasterizer->clip_rectangle = 0;
+  grablist = _ctx_device_get_grabs (ctx, device_no);
+  _ctx_update_item (ctx, device_no, x, y, CTX_MOTION, &hitlist);
 
-  if (CTX_CLIP_FORMAT == CTX_FORMAT_GRAY1)
   {
-    int count = blit_width * blit_height / 8;
-    for (int i = 0; i < count; i++)
+    CtxItem  *cursor_item = _ctx_detect (ctx, x, y, CTX_SET_CURSOR);
+    if (cursor_item)
     {
-      ((uint8_t*)rasterizer->clip_buffer->data)[i] =
-      (((uint8_t*)rasterizer->clip_buffer->data)[i] &
-      ((uint8_t*)clip_buffer->data)[i]);
+      ctx_set_cursor (ctx, cursor_item->cursor);
+    }
+    else
+    {
+      ctx_set_cursor (ctx, CTX_CURSOR_ARROW);
+    }
+    CtxItem  *hovered_item = _ctx_detect (ctx, x, y, CTX_ANY);
+    static CtxItem *prev_hovered_item = NULL;
+    if (prev_hovered_item != hovered_item)
+    {
+      ctx_set_dirty (ctx, 1);
     }
+    prev_hovered_item = hovered_item;
   }
-  else
-  {
-    int count = blit_width * blit_height;
 
+  event->delta_x = x - event->prev_x;
+  event->delta_y = y - event->prev_y;
+  event->prev_x  = x;
+  event->prev_y  = y;
 
-    int i;
-    int x0 = 0;
-    int y0 = 0;
-    int width = -1;
-    int next_stage = 0;
-    uint8_t *p_data = (uint8_t*)rasterizer->clip_buffer->data;
-    uint8_t *data = (uint8_t*)clip_buffer->data;
+  CtxList *remove_grabs = NULL;
 
-    i=0;
-    /* find upper left */
-    for (; i < count && maybe_rect && !next_stage; i++)
+  for (g = grablist; g; g = g->next)
+  {
+    grab = g->data;
+
+    if ((grab->type & CTX_TAP) ||
+        (grab->type & CTX_TAP_AND_HOLD))
     {
-      uint8_t val = (p_data[i] * data[i])/255;
-      data[i] = val;
-      switch (val)
+      if (
+          (
+            (event->start_x - x) * (event->start_x - x) +
+            (event->start_y - y) * (event->start_y - y)) >
+              ctx_pow2(ctx->events.tap_hysteresis)
+         )
       {
-        case 255:
-          x0 = i % blit_width;
-          y0 = i / blit_width;
-          next_stage = 1;
-          break;
-        case 0: break;
-        default:
-          maybe_rect = 0;
-          break;
+        //fprintf (stderr, "-");
+        ctx_list_prepend (&remove_grabs, grab);
+      }
+      else
+      {
+        //fprintf (stderr, ":");
       }
     }
 
-    next_stage = 0;
-    /* figure out with */
-    for (; i < count && !next_stage && maybe_rect; i++)
+    if (grab->type & CTX_DRAG_MOTION)
     {
-      int x = i % blit_width;
-      int y = i / blit_width;
-      uint8_t val = (p_data[i] * data[i])/255;
-      data[i] = val;
+      _ctx_emit_cb_item (ctx, grab->item, event, CTX_DRAG_MOTION, x, y);
+      if (event->stop_propagate)
+        break;
+    }
+  }
+  if (remove_grabs)
+  {
+    for (g = remove_grabs; g; g = g->next)
+      device_remove_grab (ctx, g->data);
+    ctx_list_free (&remove_grabs);
+  }
+  if (hitlist)
+  {
+    if (!event->stop_propagate)
+      _ctx_emit_cb (ctx, hitlist, event, CTX_MOTION, x, y);
+    ctx_list_free (&hitlist);
+  }
+  ctx_list_free (&grablist);
+  return 0;
+}
 
-      if (y == y0)
+void ctx_incoming_message (Ctx *ctx, const char *message, long time)
+{
+  CtxItem *item = _ctx_detect (ctx, 0, 0, CTX_MESSAGE);
+  CtxEvent event = {0, };
+
+  if (!time)
+    time = ctx_ms (ctx);
+
+  if (item)
+  {
+    int i;
+    event.ctx = ctx;
+    event.type = CTX_MESSAGE;
+    event.time = time;
+    event.string = message;
+
+    fprintf (stderr, "{%s|\n", message);
+
+      for (i = 0; i < item->cb_count; i++)
       {
-        switch (val)
+        if (item->cb[i].types & (CTX_MESSAGE))
         {
-          case 255:
-            width = x - x0 + 1;
-            break;
-          case 0:
-            next_stage = 1;
-            break;
-          default:
-            maybe_rect = 0;
-            break;
+          event.state = ctx->events.modifier_state;
+          item->cb[i].cb (&event, item->cb[i].data1, item->cb[i].data2);
+          if (event.stop_propagate)
+            return;// event.stop_propagate;
         }
-        if (x % blit_width == blit_width - 1) next_stage = 1;
       }
-      else next_stage = 1;
-    }
+  }
+}
 
-    next_stage = 0;
-    /* body */
-    for (; i < count && maybe_rect && !next_stage; i++)
-    {
-      int x = i % blit_width;
-      uint8_t val = (p_data[i] * data[i])/255;
-      data[i] = val;
+int ctx_scrolled (Ctx *ctx, float x, float y, CtxScrollDirection scroll_direction, uint32_t time)
+{
+  CtxList *hitlist = NULL;
+  CtxList *l;
 
-      if (x < x0)
-      {
-        if (val != 0){ maybe_rect = 0; next_stage = 1; }
-      } else if (x < x0 + width)
+  int device_no = 0;
+  ctx->events.pointer_x[device_no] = x;
+  ctx->events.pointer_y[device_no] = y;
+
+  CtxEvent *event = &ctx->events.drag_event[device_no];  /* XXX: might
+                                       conflict with other code
+                                       create a sibling member
+                                       of drag_event?*/
+  if (time == 0)
+    time = ctx_ms (ctx);
+
+  event->x         = event->start_x = event->prev_x = x;
+  event->y         = event->start_y = event->prev_y = y;
+  event->delta_x   = event->delta_y = 0;
+  event->device_no = device_no;
+  event->time      = time;
+  event->stop_propagate = 0;
+  event->scroll_direction = scroll_direction;
+
+  _ctx_update_item (ctx, device_no, x, y, CTX_SCROLL, &hitlist);
+
+  for (l = hitlist; l; l = l?l->next:NULL)
+  {
+    CtxItem *item = l->data;
+
+    _ctx_emit_cb_item (ctx, item, event, CTX_SCROLL, x, y);
+
+    if (event->stop_propagate)
+      l = NULL;
+  }
+
+  //mrg_queue_draw (mrg, NULL); /* in case of style change, and more  */
+  ctx_list_free (&hitlist);
+  return 0;
+}
+
+static int ctx_str_has_prefix (const char *string, const char *prefix)
+{
+  for (int i = 0; prefix[i]; i++)
+  {
+    if (!string[i]) return 0;
+    if (string[i] != prefix[i]) return 0;
+  }
+  return 0;
+}
+
+int ctx_key_press (Ctx *ctx, unsigned int keyval,
+                   const char *string, uint32_t time)
+{
+  char event_type[128]="";
+  float x, y; int b;
+  sscanf (string, "%s %f %f %i", event_type, &x, &y, &b);
+  if (!strcmp (event_type, "mouse-motion") ||
+      !strcmp (event_type, "mouse-drag"))
+    return ctx_pointer_motion (ctx, x, y, b, 0);
+  else if (!strcmp (event_type, "mouse-press"))
+    return ctx_pointer_press (ctx, x, y, b, 0);
+  else if (!strcmp (event_type, "mouse-release"))
+    return ctx_pointer_release (ctx, x, y, b, 0);
+  //else if (!strcmp (event_type, "keydown"))
+  //  return ctx_key_down (ctx, keyval, string + 8, time);
+  //else if (!strcmp (event_type, "keyup"))
+  //  return ctx_key_up (ctx, keyval, string + 6, time);
+
+  CtxItem *item = _ctx_detect (ctx, 0, 0, CTX_KEY_PRESS);
+  CtxEvent event = {0,};
+
+  if (time == 0)
+    time = ctx_ms (ctx);
+  if (item)
+  {
+    int i;
+    event.ctx = ctx;
+    event.type = CTX_KEY_PRESS;
+    event.unicode = keyval; 
+    event.string = strdup(string);
+    event.stop_propagate = 0;
+    event.time = time;
+
+    for (i = 0; i < item->cb_count; i++)
+    {
+      if (item->cb[i].types & (CTX_KEY_PRESS))
       {
-        if (val != 255){ if (val != 0) maybe_rect = 0; next_stage = 1; }
-      } else {
-        if (val != 0){ maybe_rect = 0; next_stage = 1; }
+        event.state = ctx->events.modifier_state;
+        item->cb[i].cb (&event, item->cb[i].data1, item->cb[i].data2);
+        if (event.stop_propagate)
+        {
+          free ((void*)event.string);
+          return event.stop_propagate;
+        }
       }
     }
+    free ((void*)event.string);
+  }
+  return 0;
+}
 
-    next_stage = 0;
-    /* foot */
-    for (; i < count && maybe_rect && !next_stage; i++)
-    {
-      uint8_t val = (p_data[i] * data[i])/255;
-      data[i] = val;
-
-      if (val != 0){ maybe_rect = 0; next_stage = 1; }
-    }
+int ctx_key_down (Ctx *ctx, unsigned int keyval,
+                  const char *string, uint32_t time)
+{
+  CtxItem *item = _ctx_detect (ctx, 0, 0, CTX_KEY_DOWN);
+  CtxEvent event = {0,};
 
+  if (time == 0)
+    time = ctx_ms (ctx);
+  if (item)
+  {
+    int i;
+    event.ctx     = ctx;
+    event.type    = CTX_KEY_DOWN;
+    event.unicode = keyval; 
+    event.string  = strdup(string);
+    event.stop_propagate = 0;
+    event.time    = time;
 
-    for (; i < count; i++)
+    for (i = 0; i < item->cb_count; i++)
     {
-      uint8_t val = (p_data[i] * data[i])/255;
-      data[i] = val;
+      if (item->cb[i].types & (CTX_KEY_DOWN))
+      {
+        event.state = ctx->events.modifier_state;
+        item->cb[i].cb (&event, item->cb[i].data1, item->cb[i].data2);
+        if (event.stop_propagate)
+        {
+          free ((void*)event.string);
+          return event.stop_propagate;
+        }
+      }
     }
-
-    if (maybe_rect)
-       rasterizer->clip_rectangle = 1;
+    free ((void*)event.string);
   }
-  if (!we_made_it)
-   ctx_buffer_free (clip_buffer);
-#else
-  if (coords[0][0]){};
-#endif
-  
-  rasterizer->state->gstate.clip_min_x = ctx_maxi (minx,
-                                         rasterizer->state->gstate.clip_min_x);
-  rasterizer->state->gstate.clip_min_y = ctx_maxi (miny,
-                                         rasterizer->state->gstate.clip_min_y);
-  rasterizer->state->gstate.clip_max_x = ctx_mini (maxx,
-                                         rasterizer->state->gstate.clip_max_x);
-  rasterizer->state->gstate.clip_max_y = ctx_mini (maxy,
-                                         rasterizer->state->gstate.clip_max_y);
+  return 0;
 }
 
-static void
-ctx_rasterizer_clip (CtxRasterizer *rasterizer)
+int ctx_key_up (Ctx *ctx, unsigned int keyval,
+                const char *string, uint32_t time)
 {
-  int count = rasterizer->edge_list.count;
-  CtxSegment temp[count+1]; /* copy of already built up path's poly line  */
-  rasterizer->state->has_clipped=1;
-  rasterizer->state->gstate.clipped=1;
-  //if (rasterizer->preserve)
-    { memcpy (temp + 1, rasterizer->edge_list.entries, sizeof (temp) - sizeof (temp[0]));
-      temp[0].code = CTX_NOP;
-      temp[0].data.u32[0] = count;
-      ctx_state_set_blob (rasterizer->state, CTX_clip, (uint8_t*)temp, sizeof(temp));
-    }
-  ctx_rasterizer_clip_apply (rasterizer, temp);
-  ctx_rasterizer_reset (rasterizer);
-  if (rasterizer->preserve)
+  CtxItem *item = _ctx_detect (ctx, 0, 0, CTX_KEY_UP);
+  CtxEvent event = {0,};
+
+  if (time == 0)
+    time = ctx_ms (ctx);
+  if (item)
+  {
+    int i;
+    event.ctx = ctx;
+    event.type = CTX_KEY_UP;
+    event.unicode = keyval; 
+    event.string = strdup(string);
+    event.stop_propagate = 0;
+    event.time = time;
+
+    for (i = 0; i < item->cb_count; i++)
     {
-      memcpy (rasterizer->edge_list.entries, temp + 1, sizeof (temp) - sizeof(temp[0]));
-      rasterizer->edge_list.count = count;
-      rasterizer->preserve = 0;
+      if (item->cb[i].types & (CTX_KEY_UP))
+      {
+        event.state = ctx->events.modifier_state;
+        item->cb[i].cb (&event, item->cb[i].data1, item->cb[i].data2);
+        if (event.stop_propagate)
+        {
+          free ((void*)event.string);
+          return event.stop_propagate;
+        }
+      }
     }
-}
+    free ((void*)event.string);
+  }
+  return 0;
 
+  return 0;
+}
 
-#if 0
-static void
-ctx_rasterizer_load_image (CtxRasterizer *rasterizer,
-                           const char  *path,
-                           float x,
-                           float y)
+void ctx_freeze           (Ctx *ctx)
 {
-  // decode PNG, put it in image is slot 1,
-  // magic width height stride format data
-  ctx_buffer_load_png (&rasterizer->ctx->texture[0], path);
-  ctx_rasterizer_set_texture (rasterizer, 0, x, y);
+  ctx->events.frozen ++;
 }
-#endif
 
+void ctx_thaw             (Ctx *ctx)
+{
+  ctx->events.frozen --;
+}
+int ctx_events_frozen (Ctx *ctx)
+{
+  return ctx && ctx->events.frozen;
+}
+void ctx_events_clear_items (Ctx *ctx)
+{
+  ctx_list_free (&ctx->events.items);
+}
+int ctx_events_width (Ctx *ctx)
+{
+  return ctx->events.width;
+}
+int ctx_events_height (Ctx *ctx)
+{
+  return ctx->events.height;
+}
 
-CTX_INLINE void
-ctx_rasterizer_rectangle (CtxRasterizer *rasterizer,
-                          float x,
-                          float y,
-                          float width,
-                          float height)
+float ctx_pointer_x (Ctx *ctx)
 {
-  ctx_rasterizer_move_to (rasterizer, x, y);
-  ctx_rasterizer_rel_line_to (rasterizer, width, 0);
-  ctx_rasterizer_rel_line_to (rasterizer, 0, height);
-  ctx_rasterizer_rel_line_to (rasterizer, -width, 0);
-  ctx_rasterizer_rel_line_to (rasterizer, 0, -height);
-  ctx_rasterizer_rel_line_to (rasterizer, width/2, 0);
-  ctx_rasterizer_finish_shape (rasterizer);
+  return ctx->events.pointer_x[0];
 }
 
-static void
-ctx_rasterizer_set_pixel (CtxRasterizer *rasterizer,
-                          uint16_t x,
-                          uint16_t y,
-                          uint8_t r,
-                          uint8_t g,
-                          uint8_t b,
-                          uint8_t a)
+float ctx_pointer_y (Ctx *ctx)
 {
-  rasterizer->state->gstate.source_fill.type = CTX_SOURCE_COLOR;
-  ctx_color_set_RGBA8 (rasterizer->state, &rasterizer->state->gstate.source_fill.color, r, g, b, a);
-  rasterizer->comp_op = NULL;
-#if 0
-  // XXX : doesn't take transforms into account - and has
-  // received less testing than code paths part of protocol,
-  // using rectangle properly will trigger the fillrect fastpath
-  ctx_rasterizer_pset (rasterizer, x, y, 255);
-#else
-  ctx_rasterizer_rectangle (rasterizer, x, y, 1.0, 1.0);
-  ctx_rasterizer_fill (rasterizer);
-#endif
+  return ctx->events.pointer_y[0];
 }
 
-#if CTX_ENABLE_SHADOW_BLUR
-static float
-ctx_gaussian (float x, float mu, float sigma)
+int ctx_pointer_is_down (Ctx *ctx, int no)
 {
-  float a = ( x- mu) / sigma;
-  return ctx_expf (-0.5 * a * a);
+  if (no < 0 || no > CTX_MAX_DEVICES) return 0;
+  return ctx->events.pointer_down[no];
 }
 
-static void
-ctx_compute_gaussian_kernel (int dim, float radius, float *kernel)
+void _ctx_debug_overlays (Ctx *ctx)
 {
-  float sigma = radius / 2;
-  float sum = 0.0;
-  int i = 0;
-  //for (int row = 0; row < dim; row ++)
-    for (int col = 0; col < dim; col ++, i++)
+  CtxList *a;
+  ctx_save (ctx);
+
+  ctx_line_width (ctx, 2);
+  ctx_rgba (ctx, 0,0,0.8,0.5);
+  for (a = ctx->events.items; a; a = a->next)
+  {
+    float current_x = ctx_pointer_x (ctx);
+    float current_y = ctx_pointer_y (ctx);
+    CtxItem *item = a->data;
+    CtxMatrix matrix = item->inv_matrix;
+
+    ctx_matrix_apply_transform (&matrix, &current_x, &current_y);
+
+    if (current_x >= item->x0 && current_x < item->x1 &&
+        current_y >= item->y0 && current_y < item->y1)
     {
-      float val = //ctx_gaussian (row, radius, sigma) *
-                            ctx_gaussian (col, radius, sigma);
-      kernel[i] = val;
-      sum += val;
+      ctx_matrix_invert (&matrix);
+      ctx_set_matrix (ctx, &matrix);
+      _mrg_restore_path (ctx, item->path);
+      ctx_stroke (ctx);
     }
-  i = 0;
-  //for (int row = 0; row < dim; row ++)
-    for (int col = 0; col < dim; col ++, i++)
-        kernel[i] /= sum;
+  }
+  ctx_restore (ctx);
 }
-#endif
 
-static void
-ctx_rasterizer_round_rectangle (CtxRasterizer *rasterizer, float x, float y, float width, float height, 
float corner_radius)
+void ctx_set_render_threads   (Ctx *ctx, int n_threads)
 {
-  float aspect  = 1.0f;
-  float radius  = corner_radius / aspect;
-  float degrees = CTX_PI / 180.0f;
+  // XXX
+}
+int ctx_get_render_threads   (Ctx *ctx)
+{
+  return _ctx_max_threads;
+}
+void ctx_set_hash_cache (Ctx *ctx, int enable_hash_cache)
+{
+  _ctx_enable_hash_cache = enable_hash_cache;
+}
+int ctx_get_hash_cache (Ctx *ctx)
+{
+  return _ctx_enable_hash_cache;
+}
 
-  if (radius > width*0.5f) radius = width/2;
-  if (radius > height*0.5f) radius = height/2;
+int ctx_is_dirty (Ctx *ctx)
+{
+  return ctx->dirty;
+}
+void ctx_set_dirty (Ctx *ctx, int dirty)
+{
+  ctx->dirty = dirty;
+}
 
-  ctx_rasterizer_finish_shape (rasterizer);
-  ctx_rasterizer_arc (rasterizer, x + width - radius, y + radius, radius, -90 * degrees, 0 * degrees, 0);
-  ctx_rasterizer_arc (rasterizer, x + width - radius, y + height - radius, radius, 0 * degrees, 90 * 
degrees, 0);
-  ctx_rasterizer_arc (rasterizer, x + radius, y + height - radius, radius, 90 * degrees, 180 * degrees, 0);
-  ctx_rasterizer_arc (rasterizer, x + radius, y + radius, radius, 180 * degrees, 270 * degrees, 0);
+/*
+ * centralized global API for managing file descriptors that
+ * wake us up, this to remove sleeping and polling
+ */
 
-  ctx_rasterizer_finish_shape (rasterizer);
-}
+#define CTX_MAX_LISTEN_FDS 128 // becomes max clients..
 
-static void
-ctx_rasterizer_process (void *user_data, CtxCommand *command);
+static int _ctx_listen_fd[CTX_MAX_LISTEN_FDS];
+static int _ctx_listen_fds    = 0;
+static int _ctx_listen_max_fd = 0;
 
-int
-_ctx_is_rasterizer (Ctx *ctx)
+void _ctx_add_listen_fd (int fd)
 {
-  if (ctx->renderer && ctx->renderer->process == ctx_rasterizer_process)
-    return 1;
-  return 0;
+  _ctx_listen_fd[_ctx_listen_fds++]=fd;
+  if (fd > _ctx_listen_max_fd)
+    _ctx_listen_max_fd = fd;
 }
 
-#if CTX_COMPOSITING_GROUPS
-static void
-ctx_rasterizer_start_group (CtxRasterizer *rasterizer)
+void _ctx_remove_listen_fd (int fd)
 {
-  CtxEntry save_command = ctx_void(CTX_SAVE);
-  // allocate buffer, and set it as temporary target
-  int no;
-  if (rasterizer->group[0] == NULL) // first group
+  for (int i = 0; i < _ctx_listen_fds; i++)
   {
-    rasterizer->saved_buf = rasterizer->buf;
+    if (_ctx_listen_fd[i] == fd)
+    {
+      _ctx_listen_fd[i] = _ctx_listen_fd[_ctx_listen_fds-1];
+      _ctx_listen_fds--;
+      return;
+    }
   }
-  for (no = 0; rasterizer->group[no] && no < CTX_GROUP_MAX; no++);
-
-  if (no >= CTX_GROUP_MAX)
-     return;
-  rasterizer->group[no] = ctx_buffer_new (rasterizer->blit_width,
-                                          rasterizer->blit_height,
-                                          rasterizer->format->composite_format);
-  rasterizer->buf = rasterizer->group[no]->data;
-  ctx_rasterizer_process (rasterizer, (CtxCommand*)&save_command);
 }
 
-static void
-ctx_rasterizer_end_group (CtxRasterizer *rasterizer)
+int ctx_input_pending (Ctx *ctx, int timeout)
 {
-  CtxEntry restore_command = ctx_void(CTX_RESTORE);
-  CtxEntry save_command = ctx_void(CTX_SAVE);
-  int no = 0;
-  for (no = 0; rasterizer->group[no] && no < CTX_GROUP_MAX; no++);
-  no--;
-
-  if (no < 0)
-    return;
-
-  CtxCompositingMode comp = rasterizer->state->gstate.compositing_mode;
-  CtxBlend blend = rasterizer->state->gstate.blend_mode;
-  float global_alpha = rasterizer->state->gstate.global_alpha_f;
-  // fetch compositing, blending, global alpha
-  ctx_rasterizer_process (rasterizer, (CtxCommand*)&restore_command);
-  ctx_rasterizer_process (rasterizer, (CtxCommand*)&save_command);
-  CtxEntry set_state[3]=
-  {
-    ctx_u32 (CTX_COMPOSITING_MODE, comp,  0),
-    ctx_u32 (CTX_BLEND_MODE,       blend, 0),
-    ctx_f  (CTX_GLOBAL_ALPHA,     global_alpha, 0.0)
-  };
-  ctx_rasterizer_process (rasterizer, (CtxCommand*)&set_state[0]);
-  ctx_rasterizer_process (rasterizer, (CtxCommand*)&set_state[1]);
-  ctx_rasterizer_process (rasterizer, (CtxCommand*)&set_state[2]);
-  if (no == 0)
+  struct timeval tv;
+  fd_set fdset;
+  FD_ZERO (&fdset);
+  for (int i = 0; i < _ctx_listen_fds; i++)
   {
-    rasterizer->buf = rasterizer->saved_buf;
-  }
-  else
-  {
-    rasterizer->buf = rasterizer->group[no-1]->data;
-  }
-  // XXX use texture_source ?
-   ctx_texture_init (rasterizer->ctx, ".ctx-group", // XXX ? count groups..
-                  rasterizer->blit_width,  // or have group based on thread-id?
-                  rasterizer->blit_height, // .. this would mean threadsafe
-                                           // allocation
-                  rasterizer->blit_width * rasterizer->format->bpp/8,
-                  rasterizer->format->pixel_format,
-                  NULL, // space
-                  (uint8_t*)rasterizer->group[no]->data,
-                  NULL, NULL);
-  {
-     const char *eid = ".ctx-group";
-     int   eid_len = strlen (eid);
-
-     CtxEntry commands[4] =
-      {
-       ctx_f  (CTX_TEXTURE, rasterizer->blit_x, rasterizer->blit_y), 
-       ctx_u32 (CTX_DATA, eid_len, eid_len/9+1),
-       ctx_u32 (CTX_CONT, 0,0),
-       ctx_u32 (CTX_CONT, 0,0)
-      };
-     memcpy( (char *) &commands[2].data.u8[0], eid, eid_len);
-     ( (char *) (&commands[2].data.u8[0]) ) [eid_len]=0;
-
-     ctx_rasterizer_process (rasterizer, (CtxCommand*)commands);
+    FD_SET (_ctx_listen_fd[i], &fdset);
   }
+  int input_fds[5];
+  int n_fds;
+  ctx_get_event_fds (ctx, input_fds, &n_fds);
+  for (int i = 0; i < n_fds; i++)
   {
-    CtxEntry commands[2]=
-    {
-      ctx_f (CTX_RECTANGLE, rasterizer->blit_x, rasterizer->blit_y),
-      ctx_f (CTX_CONT,      rasterizer->blit_width, rasterizer->blit_height)
-    };
-    ctx_rasterizer_process (rasterizer, (CtxCommand*)commands);
+    FD_SET (input_fds[i], &fdset);
   }
+
+  tv.tv_sec = 0;
+  tv.tv_usec = timeout;
+  tv.tv_sec = timeout / 1000000;
+  tv.tv_usec = timeout % 1000000;
+  int retval = select (_ctx_listen_max_fd + 1, &fdset, NULL, NULL, &tv);
+  if (retval == -1)
   {
-    CtxEntry commands[1]= { ctx_void (CTX_FILL) };
-    ctx_rasterizer_process (rasterizer, (CtxCommand*)commands);
+    perror ("select");
+    return 0;
   }
-  //ctx_texture_release (rasterizer->ctx, ".ctx-group");
-  ctx_buffer_free (rasterizer->group[no]);
-  rasterizer->group[no] = 0;
-  ctx_rasterizer_process (rasterizer, (CtxCommand*)&restore_command);
+  return retval;
 }
-#endif
 
-#if CTX_ENABLE_SHADOW_BLUR
-static void
-ctx_rasterizer_shadow_stroke (CtxRasterizer *rasterizer)
+void ctx_sdl_set_title (void *self, const char *new_title);
+void ctx_set_title (Ctx *ctx, const char *title)
 {
-  CtxColor color;
-  CtxEntry save_command = ctx_void(CTX_SAVE);
-
-  float rgba[4] = {0, 0, 0, 1.0};
-  if (ctx_get_color (rasterizer->ctx, CTX_shadowColor, &color) == 0)
-    ctx_color_get_rgba (rasterizer->state, &color, rgba);
-
-  CtxEntry set_color_command [3]=
-  {
-    ctx_f (CTX_COLOR, CTX_RGBA, rgba[0]),
-    ctx_f (CTX_CONT, rgba[1], rgba[2]),
-    ctx_f (CTX_CONT, rgba[3], 0)
-  };
-  CtxEntry restore_command = ctx_void(CTX_RESTORE);
-  float radius = rasterizer->state->gstate.shadow_blur;
-  int dim = 2 * radius + 1;
-  if (dim > CTX_MAX_GAUSSIAN_KERNEL_DIM)
-    dim = CTX_MAX_GAUSSIAN_KERNEL_DIM;
-  ctx_compute_gaussian_kernel (dim, radius, rasterizer->kernel);
-  ctx_rasterizer_process (rasterizer, (CtxCommand*)&save_command);
-  {
-    int i = 0;
-    for (int v = 0; v < dim; v += 1, i++)
-      {
-        float dy = rasterizer->state->gstate.shadow_offset_y + v - dim/2;
-        set_color_command[2].data.f[0] = rasterizer->kernel[i] * rgba[3];
-        ctx_rasterizer_process (rasterizer, (CtxCommand*)&set_color_command[0]);
-#if CTX_ENABLE_SHADOW_BLUR
-        rasterizer->in_shadow = 1;
-#endif
-        rasterizer->shadow_x = rasterizer->state->gstate.shadow_offset_x;
-        rasterizer->shadow_y = dy;
-        rasterizer->preserve = 1;
-        ctx_rasterizer_stroke (rasterizer);
-#if CTX_ENABLE_SHADOW_BLUR
-        rasterizer->in_shadow = 0;
+#if CTX_SDL
+     // XXX also check we're first/only client?
+   if (ctx_renderer_is_sdl (ctx))
+     ctx_sdl_set_title (ctx_get_renderer (ctx), title);
 #endif
-      }
-  }
-  //free (kernel);
-  ctx_rasterizer_process (rasterizer, (CtxCommand*)&restore_command);
 }
 
-static void
-ctx_rasterizer_shadow_text (CtxRasterizer *rasterizer, const char *str)
-{
-  float x = rasterizer->state->x;
-  float y = rasterizer->state->y;
-  CtxColor color;
-  CtxEntry save_command = ctx_void(CTX_SAVE);
+#endif
+/* the parser comes in the end, nothing in ctx knows about the parser  */
 
-  float rgba[4] = {0, 0, 0, 1.0};
-  if (ctx_get_color (rasterizer->ctx, CTX_shadowColor, &color) == 0)
-    ctx_color_get_rgba (rasterizer->state, &color, rgba);
+#if CTX_PARSER
 
-  CtxEntry set_color_command [3]=
-  {
-    ctx_f (CTX_COLOR, CTX_RGBA, rgba[0]),
-    ctx_f (CTX_CONT, rgba[1], rgba[2]),
-    ctx_f (CTX_CONT, rgba[3], 0)
-  };
-  CtxEntry move_to_command [1]=
-  {
-    ctx_f (CTX_MOVE_TO, x, y),
-  };
-  CtxEntry restore_command = ctx_void(CTX_RESTORE);
-  float radius = rasterizer->state->gstate.shadow_blur;
-  int dim = 2 * radius + 1;
-  if (dim > CTX_MAX_GAUSSIAN_KERNEL_DIM)
-    dim = CTX_MAX_GAUSSIAN_KERNEL_DIM;
-  ctx_compute_gaussian_kernel (dim, radius, rasterizer->kernel);
-  ctx_rasterizer_process (rasterizer, (CtxCommand*)&save_command);
+/* ctx parser, */
 
-  {
-      {
-        move_to_command[0].data.f[0] = x;
-        move_to_command[0].data.f[1] = y;
-        set_color_command[2].data.f[0] = rgba[3];
-        ctx_rasterizer_process (rasterizer, (CtxCommand*)&set_color_command);
-        ctx_rasterizer_process (rasterizer, (CtxCommand*)&move_to_command);
-        rasterizer->in_shadow=1;
-        ctx_rasterizer_text (rasterizer, str, 0);
-        rasterizer->in_shadow=0;
-      }
-  }
-  ctx_rasterizer_process (rasterizer, (CtxCommand*)&restore_command);
-  move_to_command[0].data.f[0] = x;
-  move_to_command[0].data.f[1] = y;
-  ctx_rasterizer_process (rasterizer, (CtxCommand*)&move_to_command);
-}
+#define CTX_ID_MAXLEN 64 // in use should not be more than 40!
+                         // to offer headroom for multiplexing
 
-static void
-ctx_rasterizer_shadow_fill (CtxRasterizer *rasterizer)
+
+struct
+  _CtxParser
 {
-  CtxColor color;
-  CtxEntry save_command = ctx_void(CTX_SAVE);
+  Ctx       *ctx;
+  int        t_args; // total number of arguments seen for current command
+  int        state;
+#if CTX_PARSER_FIXED_TEMP
+  uint8_t    holding[CTX_PARSER_MAXLEN]; /*  */
+#else
+  uint8_t   *holding;
+#endif
+  int        hold_len;
+  int        pos;
 
-  float rgba[4] = {0, 0, 0, 1.0};
-  if (ctx_get_color (rasterizer->ctx, CTX_shadowColor, &color) == 0)
-    ctx_color_get_rgba (rasterizer->state, &color, rgba);
 
-  CtxEntry set_color_command [3]=
-  {
-    ctx_f (CTX_COLOR, CTX_RGBA, rgba[0]),
-    ctx_f (CTX_CONT, rgba[1], rgba[2]),
-    ctx_f (CTX_CONT, rgba[3], 0)
-  };
-  CtxEntry restore_command = ctx_void(CTX_RESTORE);
-  float radius = rasterizer->state->gstate.shadow_blur;
-  int dim = 2 * radius + 1;
-  if (dim > CTX_MAX_GAUSSIAN_KERNEL_DIM)
-    dim = CTX_MAX_GAUSSIAN_KERNEL_DIM;
-  ctx_compute_gaussian_kernel (dim, radius, rasterizer->kernel);
-  ctx_rasterizer_process (rasterizer, (CtxCommand*)&save_command);
+  int        line; /*  for error reporting */
+  int        col;  /*  for error reporting */
+  float      numbers[CTX_PARSER_MAX_ARGS+1];
+  int        n_numbers;
+  int        decimal;
+  CtxCode    command;
+  int        expected_args; /* low digits are literal higher values
+                               carry special meaning */
+  int        n_args;
+  int        texture_done;
+  uint8_t    texture_id[CTX_ID_MAXLEN]; // used in defineTexture only
+  uint64_t   set_key_hash;
+  float      pcx;
+  float      pcy;
+  int        color_components;
+  int        color_stroke; // 0 is fill source  1 is stroke source
+  CtxColorModel   color_model; // 1 gray 3 rgb 4 cmyk
+  float      left_margin; // set by last user provided move_to
+  int        width;       // <- maybe should be float
+  int        height;
+  float      cell_width;
+  float      cell_height;
+  int        cursor_x;    // <- leaking in from terminal
+  int        cursor_y;
 
-  {
-    for (int v = 0; v < dim; v ++)
-      {
-        int i = v;
-        float dy = rasterizer->state->gstate.shadow_offset_y + v - dim/2;
-        set_color_command[2].data.f[0] = rasterizer->kernel[i] * rgba[3];
-        ctx_rasterizer_process (rasterizer, (CtxCommand*)&set_color_command);
-        rasterizer->in_shadow = 1;
-        rasterizer->shadow_x = rasterizer->state->gstate.shadow_offset_x;
-        rasterizer->shadow_y = dy;
-        rasterizer->preserve = 1;
-        ctx_rasterizer_fill (rasterizer);
-        rasterizer->in_shadow = 0;
-      }
-  }
-  ctx_rasterizer_process (rasterizer, (CtxCommand*)&restore_command);
+  int        translate_origin;
+
+  CtxColorSpace   color_space_slot;
+
+  void (*exit) (void *exit_data);
+  void *exit_data;
+  int   (*set_prop)(void *prop_data, uint64_t key, const char *data,  int len);
+  int   (*get_prop)(void *prop_data, const char *key, char **data, int *len);
+  void *prop_data;
+};
+
+void
+ctx_parser_set_size (CtxParser *parser,
+                 int        width,
+                 int        height,
+                 float      cell_width,
+                 float      cell_height)
+{
+  if (cell_width > 0)
+    parser->cell_width       = cell_width;
+  if (cell_height > 0)
+    parser->cell_height      = cell_height;
+  if (width > 0)
+    parser->width            = width;
+  if (height > 0)
+    parser->height           = height;
 }
-#endif
 
-static void
-ctx_rasterizer_line_dash (CtxRasterizer *rasterizer, int count, float *dashes)
+static CtxParser *
+ctx_parser_init (CtxParser *parser,
+                 Ctx       *ctx,
+                 int        width,
+                 int        height,
+                 float      cell_width,
+                 float      cell_height,
+                 int        cursor_x,
+                 int        cursor_y,
+  int   (*set_prop)(void *prop_data, uint64_t key, const char *data,  int len),
+  int   (*get_prop)(void *prop_Data, const char *key, char **data, int *len),
+                 void  *prop_data,
+                 void (*exit) (void *exit_data),
+                 void *exit_data
+                )
 {
-  if (!dashes)
-  {
-    rasterizer->state->gstate.n_dashes = 0;
-    return;
-  }
-  count = CTX_MIN(count, CTX_PARSER_MAX_ARGS-1);
-  rasterizer->state->gstate.n_dashes = count;
-  memcpy(&rasterizer->state->gstate.dashes[0], dashes, count * sizeof(float));
-  for (int i = 0; i < count; i ++)
-  {
-    if (rasterizer->state->gstate.dashes[i] < 0.0001f)
-      rasterizer->state->gstate.dashes[i] = 0.0001f; // hang protection
-  }
+  ctx_memset (parser, 0, sizeof (CtxParser) );
+  parser->line             = 1;
+  parser->ctx              = ctx;
+  parser->cell_width       = cell_width;
+  parser->cell_height      = cell_height;
+  parser->cursor_x         = cursor_x;
+  parser->cursor_y         = cursor_y;
+  parser->width            = width;
+  parser->height           = height;
+  parser->exit             = exit;
+  parser->exit_data        = exit_data;
+  parser->color_model      = CTX_RGBA;
+  parser->color_stroke     = 0;
+  parser->color_components = 4;
+  parser->command          = CTX_MOVE_TO;
+  parser->set_prop         = set_prop;
+  parser->get_prop         = get_prop;
+  parser->prop_data        = prop_data;
+  return parser;
 }
 
+CtxParser *ctx_parser_new (
+  Ctx       *ctx,
+  int        width,
+  int        height,
+  float      cell_width,
+  float      cell_height,
+  int        cursor_x,
+  int        cursor_y,
+  int   (*set_prop)(void *prop_data, uint64_t key, const char *data,  int len),
+  int   (*get_prop)(void *prop_Data, const char *key, char **data, int *len),
+  void  *prop_data,
+  void (*exit) (void *exit_data),
+  void *exit_data)
+{
+  return ctx_parser_init ( (CtxParser *) ctx_calloc (sizeof (CtxParser), 1),
+                           ctx,
+                           width, height,
+                           cell_width, cell_height,
+                           cursor_x, cursor_y, set_prop, get_prop, prop_data,
+                           exit, exit_data);
+}
 
-static void
-ctx_rasterizer_process (void *user_data, CtxCommand *command)
+void ctx_parser_free (CtxParser *parser)
 {
-  CtxEntry *entry = &command->entry;
-  CtxRasterizer *rasterizer = (CtxRasterizer *) user_data;
-  CtxState *state = rasterizer->state;
-  CtxCommand *c = (CtxCommand *) entry;
-  int clear_clip = 0;
-  ctx_interpret_style (state, entry, NULL);
-  switch (c->code)
-    {
-#if CTX_ENABLE_SHADOW_BLUR
-      case CTX_SHADOW_COLOR:
-        {
-          CtxColor  col;
-          CtxColor *color = &col;
-          //state->gstate.source_fill.type = CTX_SOURCE_COLOR;
-          switch ((int)c->rgba.model)
-            {
-              case CTX_RGB:
-                ctx_color_set_rgba (state, color, c->rgba.r, c->rgba.g, c->rgba.b, 1.0f);
-                break;
-              case CTX_RGBA:
-                //ctx_color_set_rgba (state, color, c->rgba.r, c->rgba.g, c->rgba.b, c->rgba.a);
-                ctx_color_set_rgba (state, color, c->rgba.r, c->rgba.g, c->rgba.b, c->rgba.a);
-                break;
-              case CTX_DRGBA:
-                ctx_color_set_drgba (state, color, c->rgba.r, c->rgba.g, c->rgba.b, c->rgba.a);
-                break;
-#if CTX_ENABLE_CMYK
-              case CTX_CMYKA:
-                ctx_color_set_cmyka (state, color, c->cmyka.c, c->cmyka.m, c->cmyka.y, c->cmyka.k, 
c->cmyka.a);
-                break;
-              case CTX_CMYK:
-                ctx_color_set_cmyka (state, color, c->cmyka.c, c->cmyka.m, c->cmyka.y, c->cmyka.k, 1.0f);
-                break;
-              case CTX_DCMYKA:
-                ctx_color_set_dcmyka (state, color, c->cmyka.c, c->cmyka.m, c->cmyka.y, c->cmyka.k, 
c->cmyka.a);
-                break;
-              case CTX_DCMYK:
-                ctx_color_set_dcmyka (state, color, c->cmyka.c, c->cmyka.m, c->cmyka.y, c->cmyka.k, 1.0f);
-                break;
-#endif
-              case CTX_GRAYA:
-                ctx_color_set_graya (state, color, c->graya.g, c->graya.a);
-                break;
-              case CTX_GRAY:
-                ctx_color_set_graya (state, color, c->graya.g, 1.0f);
-                break;
-            }
-          ctx_set_color (rasterizer->ctx, CTX_shadowColor, color);
-        }
-        break;
+#if !CTX_PARSER_FIXED_TEMP
+  if (parser->holding)
+    free (parser->holding);
 #endif
-      case CTX_LINE_DASH:
-        if (c->line_dash.count)
-          {
-            ctx_rasterizer_line_dash (rasterizer, c->line_dash.count, c->line_dash.data);
-          }
-        else
-        ctx_rasterizer_line_dash (rasterizer, 0, NULL);
-        break;
+  free (parser);
+}
+
+#define CTX_ARG_COLLECT_NUMBERS             50
+#define CTX_ARG_STRING_OR_NUMBER            100
+#define CTX_ARG_NUMBER_OF_COMPONENTS        200
+#define CTX_ARG_NUMBER_OF_COMPONENTS_PLUS_1 201
 
+static int ctx_arguments_for_code (CtxCode code)
+{
+  switch (code)
+    {
+      case CTX_SAVE:
+      case CTX_START_GROUP:
+      case CTX_END_GROUP:
+      case CTX_IDENTITY:
+      case CTX_CLOSE_PATH:
+      case CTX_BEGIN_PATH:
+      case CTX_RESET:
+      case CTX_FLUSH:
+      case CTX_RESTORE:
+      case CTX_STROKE:
+      case CTX_FILL:
+      case CTX_NEW_PAGE:
+      case CTX_CLIP:
+      case CTX_EXIT:
+        return 0;
+      case CTX_GLOBAL_ALPHA:
+      case CTX_COMPOSITING_MODE:
+      case CTX_BLEND_MODE:
+      case CTX_FONT_SIZE:
+      case CTX_LINE_JOIN:
+      case CTX_LINE_CAP:
+      case CTX_LINE_WIDTH:
+      case CTX_LINE_DASH_OFFSET:
+      case CTX_IMAGE_SMOOTHING:
+      case CTX_SHADOW_BLUR:
+      case CTX_SHADOW_OFFSET_X:
+      case CTX_SHADOW_OFFSET_Y:
+      case CTX_FILL_RULE:
+      case CTX_TEXT_ALIGN:
+      case CTX_TEXT_BASELINE:
+      case CTX_TEXT_DIRECTION:
+      case CTX_MITER_LIMIT:
+      case CTX_REL_VER_LINE_TO:
+      case CTX_REL_HOR_LINE_TO:
+      case CTX_HOR_LINE_TO:
+      case CTX_VER_LINE_TO:
+      case CTX_FONT:
+      case CTX_ROTATE:
+      case CTX_GLYPH:
+        return 1;
+      case CTX_TRANSLATE:
+      case CTX_REL_SMOOTHQ_TO:
       case CTX_LINE_TO:
-        ctx_rasterizer_line_to (rasterizer, c->c.x0, c->c.y0);
-        break;
-      case CTX_REL_LINE_TO:
-        ctx_rasterizer_rel_line_to (rasterizer, c->c.x0, c->c.y0);
-        break;
       case CTX_MOVE_TO:
-        ctx_rasterizer_move_to (rasterizer, c->c.x0, c->c.y0);
-        break;
+      case CTX_SCALE:
+      case CTX_REL_LINE_TO:
       case CTX_REL_MOVE_TO:
-        ctx_rasterizer_rel_move_to (rasterizer, c->c.x0, c->c.y0);
-        break;
-      case CTX_CURVE_TO:
-        ctx_rasterizer_curve_to (rasterizer, c->c.x0, c->c.y0,
-                                 c->c.x1, c->c.y1,
-                                 c->c.x2, c->c.y2);
-        break;
-      case CTX_REL_CURVE_TO:
-        ctx_rasterizer_rel_curve_to (rasterizer, c->c.x0, c->c.y0,
-                                     c->c.x1, c->c.y1,
-                                     c->c.x2, c->c.y2);
-        break;
-      case CTX_QUAD_TO:
-        ctx_rasterizer_quad_to (rasterizer, c->c.x0, c->c.y0, c->c.x1, c->c.y1);
-        break;
+      case CTX_SMOOTHQ_TO:
+        return 2;
+      case CTX_LINEAR_GRADIENT:
       case CTX_REL_QUAD_TO:
-        ctx_rasterizer_rel_quad_to (rasterizer, c->c.x0, c->c.y0, c->c.x1, c->c.y1);
-        break;
-      case CTX_ARC:
-        ctx_rasterizer_arc (rasterizer, c->arc.x, c->arc.y, c->arc.radius, c->arc.angle1, c->arc.angle2, 
c->arc.direction);
-        break;
+      case CTX_QUAD_TO:
       case CTX_RECTANGLE:
-        ctx_rasterizer_rectangle (rasterizer, c->rectangle.x, c->rectangle.y,
-                                  c->rectangle.width, c->rectangle.height);
-        break;
+      case CTX_FILL_RECT:
+      case CTX_STROKE_RECT:
+      case CTX_REL_SMOOTH_TO:
+      case CTX_VIEW_BOX:
+      case CTX_SMOOTH_TO:
+        return 4;
+      case CTX_ARC_TO:
+      case CTX_REL_ARC_TO:
       case CTX_ROUND_RECTANGLE:
-        ctx_rasterizer_round_rectangle (rasterizer, c->rectangle.x, c->rectangle.y,
-                                        c->rectangle.width, c->rectangle.height,
-                                        c->rectangle.radius);
-        break;
-      case CTX_SET_PIXEL:
-        ctx_rasterizer_set_pixel (rasterizer, c->set_pixel.x, c->set_pixel.y,
-                                  c->set_pixel.rgba[0],
-                                  c->set_pixel.rgba[1],
-                                  c->set_pixel.rgba[2],
-                                  c->set_pixel.rgba[3]);
-        break;
+        return 5;
+      case CTX_ARC:
+      case CTX_CURVE_TO:
+      case CTX_REL_CURVE_TO:
+      case CTX_APPLY_TRANSFORM:
+      case CTX_RADIAL_GRADIENT:
+        return 6;
+      case CTX_STROKE_TEXT:
+      case CTX_TEXT:
+      case CTX_COLOR_SPACE:
+      case CTX_DEFINE_GLYPH:
+      case CTX_KERNING_PAIR:
+      case CTX_TEXTURE:
       case CTX_DEFINE_TEXTURE:
-        {
-          uint8_t *pixel_data = ctx_define_texture_pixel_data (entry);
-          ctx_rasterizer_define_texture (rasterizer, c->define_texture.eid,
-                                         c->define_texture.width, c->define_texture.height,
-                                         c->define_texture.format,
-                                         pixel_data);
-          rasterizer->comp_op = NULL;
-          rasterizer->fragment = NULL;
-        }
-        break;
-      case CTX_TEXTURE:
-        ctx_rasterizer_set_texture (rasterizer, c->texture.eid,
-                                    c->texture.x, c->texture.y);
-        rasterizer->comp_op = NULL;
-        rasterizer->fragment = NULL;
-        break;
-      case CTX_SOURCE_TRANSFORM:
-        ctx_matrix_set (&state->gstate.source_fill.set_transform,
-                        ctx_arg_float (0), ctx_arg_float (1),
-                        ctx_arg_float (2), ctx_arg_float (3),
-                        ctx_arg_float (4), ctx_arg_float (5));
-        rasterizer->comp_op = NULL;
-        break;
-#if 0
-      case CTX_LOAD_IMAGE:
-        ctx_rasterizer_load_image (rasterizer, ctx_arg_string(),
-                                   ctx_arg_float (0), ctx_arg_float (1) );
-        break;
-#endif
-#if CTX_GRADIENTS
-      case CTX_GRADIENT_STOP:
-        {
-          float rgba[4]= {ctx_u8_to_float (ctx_arg_u8 (4) ),
-                          ctx_u8_to_float (ctx_arg_u8 (4+1) ),
-                          ctx_u8_to_float (ctx_arg_u8 (4+2) ),
-                          ctx_u8_to_float (ctx_arg_u8 (4+3) )
-                         };
-          ctx_rasterizer_gradient_add_stop (rasterizer,
-                                            ctx_arg_float (0), rgba);
-          rasterizer->comp_op = NULL;
-        }
-        break;
-      case CTX_LINEAR_GRADIENT:
-        ctx_state_gradient_clear_stops (state);
-        rasterizer->comp_op = NULL;
-        break;
-      case CTX_RADIAL_GRADIENT:
-        ctx_state_gradient_clear_stops (state);
-        rasterizer->comp_op = NULL;
-        break;
-#endif
-      case CTX_PRESERVE:
-        rasterizer->preserve = 1;
-        break;
+        return CTX_ARG_STRING_OR_NUMBER;
+      case CTX_LINE_DASH: /* append to current dashes for each argument encountered */
+        return CTX_ARG_COLLECT_NUMBERS;
+      //case CTX_SET_KEY:
       case CTX_COLOR:
-      case CTX_COMPOSITING_MODE:
-      case CTX_BLEND_MODE:
-        rasterizer->comp_op = NULL;
-        //_ctx_setup_compositor (rasterizer);
-        break;
-#if CTX_COMPOSITING_GROUPS
-      case CTX_START_GROUP:
-        ctx_rasterizer_start_group (rasterizer);
-        break;
-      case CTX_END_GROUP:
-        ctx_rasterizer_end_group (rasterizer);
-        break;
-#endif
-
-      case CTX_RESTORE:
-        for (int i = state->gstate_no?state->gstate_stack[state->gstate_no-1].keydb_pos:0;
-             i < state->gstate.keydb_pos; i++)
-        {
-          if (state->keydb[i].key == CTX_clip)
-          {
-            clear_clip = 1;
-          }
-        }
-        /* FALLTHROUGH */
-      case CTX_ROTATE:
-      case CTX_SCALE:
-      case CTX_TRANSLATE:
-      case CTX_IDENTITY:
-      case CTX_SAVE:
-        rasterizer->comp_op = NULL;
-        rasterizer->uses_transforms = 1;
-        ctx_interpret_transforms (state, entry, NULL);
-        if (clear_clip)
-        {
-          ctx_rasterizer_clip_reset (rasterizer);
-        for (int i = state->gstate_no?state->gstate_stack[state->gstate_no-1].keydb_pos:0;
-             i < state->gstate.keydb_pos; i++)
-        {
-          if (state->keydb[i].key == CTX_clip)
-          {
-            int idx = ctx_float_to_string_index (state->keydb[i].value);
-            if (idx >=0)
-            {
-              CtxSegment *edges = (CtxSegment*)&state->stringpool[idx];
-              ctx_rasterizer_clip_apply (rasterizer, edges);
-            }
-          }
-        }
-        }
-        break;
-      case CTX_STROKE:
-#if CTX_ENABLE_SHADOW_BLUR
-        if (state->gstate.shadow_blur > 0.0 &&
-            !rasterizer->in_text)
-          ctx_rasterizer_shadow_stroke (rasterizer);
-#endif
-        {
-        int count = rasterizer->edge_list.count;
-        if (state->gstate.n_dashes)
-        {
-          int n_dashes = state->gstate.n_dashes;
-          float *dashes = state->gstate.dashes;
-          float factor = ctx_matrix_get_scale (&state->gstate.transform);
-
-          int aa = 15;//rasterizer->aa;
-          CtxSegment temp[count]; /* copy of already built up path's poly line  */
-          memcpy (temp, rasterizer->edge_list.entries, sizeof (temp));
-          int start = 0;
-          int end   = 0;
-      CtxMatrix transform_backup = state->gstate.transform;
-      _ctx_matrix_identity (&state->gstate.transform);
-      ctx_rasterizer_reset (rasterizer); /* for dashing we create
-                                            a dashed path to stroke */
-      float prev_x = 0.0f;
-      float prev_y = 0.0f;
-      float pos = 0.0;
-
-      int   dash_no  = 0.0;
-      float dash_lpos = state->gstate.line_dash_offset * factor;
-      int   is_down = 0;
-
-          while (start < count)
-          {
-            int started = 0;
-            int i;
-            is_down = 0;
-
-            if (!is_down)
-            {
-              CtxSegment *entry = &temp[0];
-              prev_x = entry->data.s16[0] * 1.0f / CTX_SUBDIV;
-              prev_y = entry->data.s16[1] * 1.0f / aa;
-              ctx_rasterizer_move_to (rasterizer, prev_x, prev_y);
-              is_down = 1;
-            }
-
-
-            for (i = start; i < count; i++)
-            {
-              CtxSegment *entry = &temp[i];
-              float x, y;
-              if (entry->code == CTX_NEW_EDGE)
-                {
-                  if (started)
-                    {
-                      end = i - 1;
-                      dash_no = 0;
-                      dash_lpos = 0.0;
-                      goto foo;
-                    }
-                  prev_x = entry->data.s16[0] * 1.0f / CTX_SUBDIV;
-                  prev_y = entry->data.s16[1] * 1.0f / aa;
-                  started = 1;
-                  start = i;
-                  is_down = 1;
-                  ctx_rasterizer_move_to (rasterizer, prev_x, prev_y);
-                }
-
-again:
-
-              x = entry->data.s16[2] * 1.0f / CTX_SUBDIV;
-              y = entry->data.s16[3] * 1.0f / aa;
-              float dx = x - prev_x;
-              float dy = y - prev_y;
-              float length = ctx_fast_hypotf (dx, dy);
-
-              if (dash_lpos + length >= dashes[dash_no] * factor)
-              {
-                float p = (dashes[dash_no] * factor - dash_lpos) / length;
-                float splitx = x * p + (1.0f - p) * prev_x;
-                float splity = y * p + (1.0f - p) * prev_y;
-                if (is_down)
-                {
-                  ctx_rasterizer_line_to (rasterizer, splitx, splity);
-                  is_down = 0;
-                }
-                else
-                {
-                  ctx_rasterizer_move_to (rasterizer, splitx, splity);
-                  is_down = 1;
-                }
-                prev_x = splitx;
-                prev_y = splity;
-                dash_no++;
-                dash_lpos=0;
-                if (dash_no >= n_dashes) dash_no = 0;
-                goto again;
-              }
-              else
-              {
-                pos += length;
-                dash_lpos += length;
-                {
-                  if (is_down)
-                    ctx_rasterizer_line_to (rasterizer, x, y);
-                }
-              }
-              prev_x = x;
-              prev_y = y;
-            }
-          end = i-1;
-foo:
-          start = end+1;
-        }
-        state->gstate.transform = transform_backup;
-        }
-        ctx_rasterizer_stroke (rasterizer);
-        }
+      case CTX_SHADOW_COLOR:
+        return CTX_ARG_NUMBER_OF_COMPONENTS;
+      case CTX_GRADIENT_STOP:
+        return CTX_ARG_NUMBER_OF_COMPONENTS_PLUS_1;
 
-        break;
-      case CTX_FONT:
-        ctx_rasterizer_set_font (rasterizer, ctx_arg_string() );
-        break;
-      case CTX_TEXT:
-        rasterizer->in_text++;
-#if CTX_ENABLE_SHADOW_BLUR
-        if (state->gstate.shadow_blur > 0.0)
-          ctx_rasterizer_shadow_text (rasterizer, ctx_arg_string ());
-#endif
-        ctx_rasterizer_text (rasterizer, ctx_arg_string(), 0);
-        rasterizer->in_text--;
-        ctx_rasterizer_reset (rasterizer);
-        break;
-      case CTX_STROKE_TEXT:
-        ctx_rasterizer_text (rasterizer, ctx_arg_string(), 1);
-        ctx_rasterizer_reset (rasterizer);
-        break;
-      case CTX_GLYPH:
-        ctx_rasterizer_glyph (rasterizer, entry[0].data.u32[0], entry[0].data.u8[4]);
-        break;
-      case CTX_FILL:
-#if CTX_ENABLE_SHADOW_BLUR
-        if (state->gstate.shadow_blur > 0.0 &&
-            !rasterizer->in_text)
-          ctx_rasterizer_shadow_fill (rasterizer);
+        default:
+#if 1
+        case CTX_SET_RGBA_U8:
+        case CTX_NOP:
+        case CTX_NEW_EDGE:
+        case CTX_EDGE:
+        case CTX_EDGE_FLIPPED:
+        case CTX_CONT:
+        case CTX_DATA:
+        case CTX_DATA_REV:
+        case CTX_SET_PIXEL:
+        case CTX_REL_LINE_TO_X4:
+        case CTX_REL_LINE_TO_REL_CURVE_TO:
+        case CTX_REL_CURVE_TO_REL_LINE_TO:
+        case CTX_REL_CURVE_TO_REL_MOVE_TO:
+        case CTX_REL_LINE_TO_X2:
+        case CTX_MOVE_TO_REL_LINE_TO:
+        case CTX_REL_LINE_TO_REL_MOVE_TO:
+        case CTX_FILL_MOVE_TO:
+        case CTX_REL_QUAD_TO_REL_QUAD_TO:
+        case CTX_REL_QUAD_TO_S16:
+        case CTX_STROKE_SOURCE:
 #endif
-        ctx_rasterizer_fill (rasterizer);
-        break;
-      case CTX_RESET:
-      case CTX_BEGIN_PATH:
-        ctx_rasterizer_reset (rasterizer);
-        break;
-      case CTX_CLIP:
-        ctx_rasterizer_clip (rasterizer);
-        break;
-      case CTX_CLOSE_PATH:
-        ctx_rasterizer_finish_shape (rasterizer);
-        break;
-      case CTX_IMAGE_SMOOTHING:
-        rasterizer->comp_op = NULL;
-        break;
+        return 0;
     }
-  ctx_interpret_pos_bare (state, entry, NULL);
 }
 
-void
-ctx_rasterizer_deinit (CtxRasterizer *rasterizer)
+static int ctx_parser_set_command (CtxParser *parser, CtxCode code)
 {
-  ctx_drawlist_deinit (&rasterizer->edge_list);
-#if CTX_ENABLE_CLIP
-  if (rasterizer->clip_buffer)
+  if (code < 150 && code >= 32)
   {
-    ctx_buffer_free (rasterizer->clip_buffer);
-    rasterizer->clip_buffer = NULL;
-  }
-#endif
-#if CTX_SHAPE_CACHE
-  for (int i = 0; i < CTX_SHAPE_CACHE_ENTRIES; i ++)
-    if (rasterizer->shape_cache.entries[i])
+  parser->expected_args = ctx_arguments_for_code (code);
+  parser->n_args = 0;
+  parser->texture_done = 0;
+  if (parser->expected_args >= CTX_ARG_NUMBER_OF_COMPONENTS)
     {
-      free (rasterizer->shape_cache.entries[i]);
-      rasterizer->shape_cache.entries[i] = NULL;
+      parser->expected_args = (parser->expected_args % 100) + parser->color_components;
     }
-
-#endif
-  free (rasterizer);
+  }
+  return code;
 }
 
+static void ctx_parser_set_color_model (CtxParser *parser, CtxColorModel color_model, int stroke);
 
-CtxAntialias ctx_get_antialias (Ctx *ctx)
+static int ctx_parser_resolve_command (CtxParser *parser, const uint8_t *str)
 {
-#if CTX_EVENTS
-  if (ctx_renderer_is_sdl (ctx) || ctx_renderer_is_fb (ctx))
-  {
-     CtxTiled *fb = (CtxTiled*)(ctx->renderer);
-     return fb->antialias;
-  }
-#endif
-  if (!_ctx_is_rasterizer (ctx)) return CTX_ANTIALIAS_DEFAULT;
-
-  switch (((CtxRasterizer*)(ctx->renderer))->aa)
-  {
-    case 1: return CTX_ANTIALIAS_NONE;
-    case 3: return CTX_ANTIALIAS_FAST;
-    //case 5: return CTX_ANTIALIAS_GOOD;
-    default:
-    case 15: return CTX_ANTIALIAS_DEFAULT;
-    case 17: return CTX_ANTIALIAS_BEST;
-  }
-}
+  uint64_t ret = str[0]; /* if it is single char it already is the CtxCode */
 
-int _ctx_antialias_to_aa (CtxAntialias antialias)
-{
-  switch (antialias)
+  /* this is handled outside the hashing to make it possible to be case insensitive
+   * with the rest.
+   */
+  if (str[0] == CTX_SET_KEY && str[1] && str[2] == 0)
   {
-    case CTX_ANTIALIAS_NONE: return 1;
-    case CTX_ANTIALIAS_FAST: return 3;
-    case CTX_ANTIALIAS_GOOD: return 5;
-    default:
-    case CTX_ANTIALIAS_DEFAULT: return CTX_RASTERIZER_AA;
-    case CTX_ANTIALIAS_BEST: return 17;
+    switch (str[1])
+    {
+      case 'm': return ctx_parser_set_command (parser, CTX_COMPOSITING_MODE);
+      case 'B': return ctx_parser_set_command (parser, CTX_BLEND_MODE);
+      case 'l': return ctx_parser_set_command (parser, CTX_MITER_LIMIT);
+      case 't': return ctx_parser_set_command (parser, CTX_TEXT_ALIGN);
+      case 'b': return ctx_parser_set_command (parser, CTX_TEXT_BASELINE);
+      case 'd': return ctx_parser_set_command (parser, CTX_TEXT_DIRECTION);
+      case 'j': return ctx_parser_set_command (parser, CTX_LINE_JOIN);
+      case 'c': return ctx_parser_set_command (parser, CTX_LINE_CAP);
+      case 'w': return ctx_parser_set_command (parser, CTX_LINE_WIDTH);
+      case 'D': return ctx_parser_set_command (parser, CTX_LINE_DASH_OFFSET);
+      case 'S': return ctx_parser_set_command (parser, CTX_IMAGE_SMOOTHING);
+      case 'C': return ctx_parser_set_command (parser, CTX_SHADOW_COLOR);
+      case 's': return ctx_parser_set_command (parser, CTX_SHADOW_BLUR);
+      case 'x': return ctx_parser_set_command (parser, CTX_SHADOW_OFFSET_X);
+      case 'y': return ctx_parser_set_command (parser, CTX_SHADOW_OFFSET_Y);
+      case 'a': return ctx_parser_set_command (parser, CTX_GLOBAL_ALPHA);
+      case 'f': return ctx_parser_set_command (parser, CTX_FONT_SIZE);
+      case 'r': return ctx_parser_set_command (parser, CTX_FILL_RULE);
+    }
   }
-}
 
-void
-ctx_set_antialias (Ctx *ctx, CtxAntialias antialias)
-{
-#if CTX_EVENTS
-  if (ctx_renderer_is_sdl (ctx) || ctx_renderer_is_fb (ctx))
-  {
-     CtxTiled *fb = (CtxTiled*)(ctx->renderer);
-     fb->antialias = antialias;
-     for (int i = 0; i < _ctx_max_threads; i++)
-     {
-       ctx_set_antialias (fb->host[i], antialias);
-     }
-     return;
-  }
+  if (str[0] && str[1])
+    {
+      uint64_t str_hash;
+      /* trim ctx_ and CTX_ prefix */
+      if ( (str[0] == 'c' && str[1] == 't' && str[2] == 'x' && str[3] == '_') ||
+           (str[0] == 'C' && str[1] == 'T' && str[2] == 'X' && str[3] == '_') )
+        {
+          str += 4;
+        }
+      if ( (str[0] == 's' && str[1] == 'e' && str[2] == 't' && str[3] == '_') )
+        { str += 4; }
+      str_hash = ctx_strhash ( (char *) str, 0);
+      switch (str_hash)
+        {
+          /* first a list of mappings to one_char hashes, handled in a
+           * separate fast path switch without hashing
+           */
+          case CTX_arcTo:          ret = CTX_ARC_TO; break;
+          case CTX_arc:            ret = CTX_ARC; break;
+          case CTX_curveTo:        ret = CTX_CURVE_TO; break;
+          case CTX_restore:        ret = CTX_RESTORE; break;
+          case CTX_stroke:         ret = CTX_STROKE; break;
+          case CTX_fill:           ret = CTX_FILL; break;
+          case CTX_flush:          ret = CTX_FLUSH; break;
+          case CTX_horLineTo:      ret = CTX_HOR_LINE_TO; break;
+          case CTX_rotate:         ret = CTX_ROTATE; break;
+          case CTX_color:          ret = CTX_COLOR; break;
+          case CTX_lineTo:         ret = CTX_LINE_TO; break;
+          case CTX_moveTo:         ret = CTX_MOVE_TO; break;
+          case CTX_scale:          ret = CTX_SCALE; break;
+          case CTX_newPage:        ret = CTX_NEW_PAGE; break;
+          case CTX_quadTo:         ret = CTX_QUAD_TO; break;
+          case CTX_viewBox:        ret = CTX_VIEW_BOX; break;
+          case CTX_smooth_to:      ret = CTX_SMOOTH_TO; break;
+          case CTX_smooth_quad_to: ret = CTX_SMOOTHQ_TO; break;
+          case CTX_clear:          ret = CTX_COMPOSITE_CLEAR; break;
+          case CTX_copy:           ret = CTX_COMPOSITE_COPY; break;
+          case CTX_destinationOver:  ret = CTX_COMPOSITE_DESTINATION_OVER; break;
+          case CTX_destinationIn:    ret = CTX_COMPOSITE_DESTINATION_IN; break;
+          case CTX_destinationOut:   ret = CTX_COMPOSITE_DESTINATION_OUT; break;
+          case CTX_sourceOver:       ret = CTX_COMPOSITE_SOURCE_OVER; break;
+          case CTX_sourceAtop:       ret = CTX_COMPOSITE_SOURCE_ATOP; break;
+          case CTX_destinationAtop:  ret = CTX_COMPOSITE_DESTINATION_ATOP; break;
+          case CTX_sourceOut:        ret = CTX_COMPOSITE_SOURCE_OUT; break;
+          case CTX_sourceIn:         ret = CTX_COMPOSITE_SOURCE_IN; break;
+          case CTX_xor:              ret = CTX_COMPOSITE_XOR; break;
+          case CTX_darken:           ret = CTX_BLEND_DARKEN; break;
+          case CTX_lighten:          ret = CTX_BLEND_LIGHTEN; break;
+          //case CTX_color:          ret = CTX_BLEND_COLOR; break;
+          //
+          //  XXX check that he special casing for color works
+          //      it is the first collision and it is due to our own
+          //      color, not w3c for now unique use of it
+          //
+          case CTX_hue:            ret = CTX_BLEND_HUE; break;
+          case CTX_multiply:       ret = CTX_BLEND_MULTIPLY; break;
+          case CTX_normal:         ret = CTX_BLEND_NORMAL;break;
+          case CTX_screen:         ret = CTX_BLEND_SCREEN;break;
+          case CTX_difference:     ret = CTX_BLEND_DIFFERENCE; break;
+          case CTX_reset:          ret = CTX_RESET; break;
+          case CTX_verLineTo:      ret = CTX_VER_LINE_TO; break;
+          case CTX_exit:
+          case CTX_done:           ret = CTX_EXIT; break;
+          case CTX_closePath:      ret = CTX_CLOSE_PATH; break;
+          case CTX_beginPath:
+          case CTX_newPath:        ret = CTX_BEGIN_PATH; break;
+          case CTX_relArcTo:       ret = CTX_REL_ARC_TO; break;
+          case CTX_clip:           ret = CTX_CLIP; break;
+          case CTX_relCurveTo:     ret = CTX_REL_CURVE_TO; break;
+          case CTX_startGroup:     ret = CTX_START_GROUP; break;
+          case CTX_endGroup:       ret = CTX_END_GROUP; break;
+          case CTX_save:           ret = CTX_SAVE; break;
+          case CTX_translate:      ret = CTX_TRANSLATE; break;
+          case CTX_linearGradient: ret = CTX_LINEAR_GRADIENT; break;
+          case CTX_relHorLineTo:   ret = CTX_REL_HOR_LINE_TO; break;
+          case CTX_relLineTo:      ret = CTX_REL_LINE_TO; break;
+          case CTX_relMoveTo:      ret = CTX_REL_MOVE_TO; break;
+          case CTX_font:           ret = CTX_FONT; break;
+          case CTX_radialGradient:ret = CTX_RADIAL_GRADIENT; break;
+          case CTX_gradientAddStop:
+          case CTX_addStop:        ret = CTX_GRADIENT_STOP; break;
+          case CTX_relQuadTo:      ret = CTX_REL_QUAD_TO; break;
+          case CTX_rectangle:
+          case CTX_rect:           ret = CTX_RECTANGLE; break;
+          case CTX_roundRectangle: ret = CTX_ROUND_RECTANGLE; break;
+          case CTX_relSmoothTo:    ret = CTX_REL_SMOOTH_TO; break;
+          case CTX_relSmoothqTo:   ret = CTX_REL_SMOOTHQ_TO; break;
+          case CTX_strokeText:     ret = CTX_STROKE_TEXT; break;
+          case CTX_strokeRect:     ret = CTX_STROKE_RECT; break;
+          case CTX_fillRect:       ret = CTX_FILL_RECT; break;
+          case CTX_relVerLineTo:   ret = CTX_REL_VER_LINE_TO; break;
+          case CTX_text:           ret = CTX_TEXT; break;
+          case CTX_identity:       ret = CTX_IDENTITY; break;
+          case CTX_transform:      ret = CTX_APPLY_TRANSFORM; break;
+          case CTX_texture:        ret = CTX_TEXTURE; break;
+          case CTX_defineTexture:  ret = CTX_DEFINE_TEXTURE; break;
+#if 0
+          case CTX_rgbSpace:
+            return ctx_parser_set_command (parser, CTX_SET_RGB_SPACE);
+          case CTX_cmykSpace:
+            return ctx_parser_set_command (parser, CTX_SET_CMYK_SPACE);
+          case CTX_drgbSpace:
+            return ctx_parser_set_command (parser, CTX_SET_DRGB_SPACE);
 #endif
-  if (!_ctx_is_rasterizer (ctx)) return;
-
-  ((CtxRasterizer*)(ctx->renderer))->aa = 
-     _ctx_antialias_to_aa (antialias);
-  ((CtxRasterizer*)(ctx->renderer))->fast_aa = 0;
-  if (antialias == CTX_ANTIALIAS_DEFAULT||
-      antialias == CTX_ANTIALIAS_FAST)
-    ((CtxRasterizer*)(ctx->renderer))->fast_aa = 1;
-}
+          case CTX_defineGlyph:
+            return ctx_parser_set_command (parser, CTX_DEFINE_GLYPH);
+          case CTX_kerningPair:
+            return ctx_parser_set_command (parser, CTX_KERNING_PAIR);
 
-CtxRasterizer *
-ctx_rasterizer_init (CtxRasterizer *rasterizer, Ctx *ctx, Ctx *texture_source, CtxState *state, void *data, 
int x, int y, int width, int height, int stride, CtxPixelFormat pixel_format, CtxAntialias antialias)
-{
-#if CTX_ENABLE_CLIP
-  if (rasterizer->clip_buffer)
-    ctx_buffer_free (rasterizer->clip_buffer);
-#endif
-  if (rasterizer->edge_list.size)
-    ctx_drawlist_deinit (&rasterizer->edge_list);
+          case CTX_colorSpace:
+            return ctx_parser_set_command (parser, CTX_COLOR_SPACE);
+          case CTX_fillRule:
+            return ctx_parser_set_command (parser, CTX_FILL_RULE);
+          case CTX_fontSize:
+          case CTX_setFontSize:
+            return ctx_parser_set_command (parser, CTX_FONT_SIZE);
+          case CTX_compositingMode:
+            return ctx_parser_set_command (parser, CTX_COMPOSITING_MODE);
 
-  memset (rasterizer, 0, sizeof (CtxRasterizer) );
-  rasterizer->vfuncs.process = ctx_rasterizer_process;
-  rasterizer->vfuncs.free    = (CtxDestroyNotify)ctx_rasterizer_deinit;
-  rasterizer->edge_list.flags |= CTX_DRAWLIST_EDGE_LIST;
-  rasterizer->state       = state;
-  rasterizer->ctx         = ctx;
-  rasterizer->texture_source = texture_source?texture_source:ctx;
+          case CTX_blend:
+          case CTX_blending:
+          case CTX_blendMode:
+            return ctx_parser_set_command (parser, CTX_BLEND_MODE);
 
-  rasterizer->aa          = _ctx_antialias_to_aa (antialias);
-  rasterizer->fast_aa = (antialias == CTX_ANTIALIAS_DEFAULT||antialias == CTX_ANTIALIAS_FAST);
-  ctx_state_init (rasterizer->state);
-  rasterizer->buf         = data;
-  rasterizer->blit_x      = x;
-  rasterizer->blit_y      = y;
-  rasterizer->blit_width  = width;
-  rasterizer->blit_height = height;
-  rasterizer->state->gstate.clip_min_x  = x;
-  rasterizer->state->gstate.clip_min_y  = y;
-  rasterizer->state->gstate.clip_max_x  = x + width - 1;
-  rasterizer->state->gstate.clip_max_y  = y + height - 1;
-  rasterizer->blit_stride = stride;
-  rasterizer->scan_min    = 5000;
-  rasterizer->scan_max    = -5000;
-
-  if (pixel_format == CTX_FORMAT_BGRA8)
-  {
-    pixel_format = CTX_FORMAT_RGBA8;
-    rasterizer->swap_red_green = 1;
-  }
+          case CTX_miterLimit:
+            return ctx_parser_set_command (parser, CTX_MITER_LIMIT);
+          case CTX_textAlign:
+            return ctx_parser_set_command (parser, CTX_TEXT_ALIGN);
+          case CTX_textBaseline:
+            return ctx_parser_set_command (parser, CTX_TEXT_BASELINE);
+          case CTX_textDirection:
+            return ctx_parser_set_command (parser, CTX_TEXT_DIRECTION);
+          case CTX_join:
+          case CTX_lineJoin:
+          case CTX_setLineJoin:
+            return ctx_parser_set_command (parser, CTX_LINE_JOIN);
+          case CTX_glyph:
+            return ctx_parser_set_command (parser, CTX_GLYPH);
+          case CTX_cap:
+          case CTX_lineCap:
+          case CTX_setLineCap:
+            return ctx_parser_set_command (parser, CTX_LINE_CAP);
+          case CTX_lineDash:
+            return ctx_parser_set_command (parser, CTX_LINE_DASH);
+          case CTX_lineWidth:
+          case CTX_setLineWidth:
+            return ctx_parser_set_command (parser, CTX_LINE_WIDTH);
+          case CTX_lineDashOffset:
+            return ctx_parser_set_command (parser, CTX_LINE_DASH_OFFSET);
+          case CTX_imageSmoothing:
+            return ctx_parser_set_command (parser, CTX_IMAGE_SMOOTHING);
+          case CTX_shadowColor:
+            return ctx_parser_set_command (parser, CTX_SHADOW_COLOR);
+          case CTX_shadowBlur:
+            return ctx_parser_set_command (parser, CTX_SHADOW_BLUR);
+          case CTX_shadowOffsetX:
+            return ctx_parser_set_command (parser, CTX_SHADOW_OFFSET_X);
+          case CTX_shadowOffsetY:
+            return ctx_parser_set_command (parser, CTX_SHADOW_OFFSET_Y);
+          case CTX_globalAlpha:
+            return ctx_parser_set_command (parser, CTX_GLOBAL_ALPHA);
 
-  rasterizer->format = ctx_pixel_format_info (pixel_format);
+          case CTX_strokeSource:
+            return ctx_parser_set_command (parser, CTX_STROKE_SOURCE);
 
-  return rasterizer;
-}
+          /* strings are handled directly here,
+           * instead of in the one-char handler, using return instead of break
+           */
+          case CTX_gray:
+            ctx_parser_set_color_model (parser, CTX_GRAY, 0);
+            return ctx_parser_set_command (parser, CTX_COLOR);
+          case CTX_graya:
+            ctx_parser_set_color_model (parser, CTX_GRAYA, 0);
+            return ctx_parser_set_command (parser, CTX_COLOR);
+          case CTX_rgb:
+            ctx_parser_set_color_model (parser, CTX_RGB, 0);
+            return ctx_parser_set_command (parser, CTX_COLOR);
+          case CTX_drgb:
+            ctx_parser_set_color_model (parser, CTX_DRGB, 0);
+            return ctx_parser_set_command (parser, CTX_COLOR);
+          case CTX_rgba:
+            ctx_parser_set_color_model (parser, CTX_RGBA, 0);
+            return ctx_parser_set_command (parser, CTX_COLOR);
+          case CTX_drgba:
+            ctx_parser_set_color_model (parser, CTX_DRGBA, 0);
+            return ctx_parser_set_command (parser, CTX_COLOR);
+          case CTX_cmyk:
+            ctx_parser_set_color_model (parser, CTX_CMYK, 0);
+            return ctx_parser_set_command (parser, CTX_COLOR);
+          case CTX_cmyka:
+            ctx_parser_set_color_model (parser, CTX_CMYKA, 0);
+            return ctx_parser_set_command (parser, CTX_COLOR);
+          case CTX_lab:
+            ctx_parser_set_color_model (parser, CTX_LAB, 0);
+            return ctx_parser_set_command (parser, CTX_COLOR);
+          case CTX_laba:
+            ctx_parser_set_color_model (parser, CTX_LABA, 0);
+            return ctx_parser_set_command (parser, CTX_COLOR);
+          case CTX_lch:
+            ctx_parser_set_color_model (parser, CTX_LCH, 0);
+            return ctx_parser_set_command (parser, CTX_COLOR);
+          case CTX_lcha:
+            ctx_parser_set_color_model (parser, CTX_LCHA, 0);
+            return ctx_parser_set_command (parser, CTX_COLOR);
 
-Ctx *
-ctx_new_for_buffer (CtxBuffer *buffer)
-{
-  Ctx *ctx = ctx_new ();
-  ctx_set_renderer (ctx,
-                    ctx_rasterizer_init ( (CtxRasterizer *) malloc (sizeof (CtxRasterizer) ),
-                                          ctx, NULL, &ctx->state,
-                                          buffer->data, 0, 0, buffer->width, buffer->height,
-                                          buffer->stride, buffer->format->pixel_format,
-                                          CTX_ANTIALIAS_DEFAULT));
-  return ctx;
-}
+          /* and a full repeat of the above, with S for Stroke suffix */
+          case CTX_grayS:
+            ctx_parser_set_color_model (parser, CTX_GRAY, 1);
+            return ctx_parser_set_command (parser, CTX_COLOR);
+          case CTX_grayaS:
+            ctx_parser_set_color_model (parser, CTX_GRAYA, 1);
+            return ctx_parser_set_command (parser, CTX_COLOR);
+          case CTX_rgbS:
+            ctx_parser_set_color_model (parser, CTX_RGB, 1);
+            return ctx_parser_set_command (parser, CTX_COLOR);
+          case CTX_drgbS:
+            ctx_parser_set_color_model (parser, CTX_DRGB, 1);
+            return ctx_parser_set_command (parser, CTX_COLOR);
+          case CTX_rgbaS:
+            ctx_parser_set_color_model (parser, CTX_RGBA, 1);
+            return ctx_parser_set_command (parser, CTX_COLOR);
+          case CTX_drgbaS:
+            ctx_parser_set_color_model (parser, CTX_DRGBA, 1);
+            return ctx_parser_set_command (parser, CTX_COLOR);
+          case CTX_cmykS:
+            ctx_parser_set_color_model (parser, CTX_CMYK, 1);
+            return ctx_parser_set_command (parser, CTX_COLOR);
+          case CTX_cmykaS:
+            ctx_parser_set_color_model (parser, CTX_CMYKA, 1);
+            return ctx_parser_set_command (parser, CTX_COLOR);
+          case CTX_labS:
+            ctx_parser_set_color_model (parser, CTX_LAB, 1);
+            return ctx_parser_set_command (parser, CTX_COLOR);
+          case CTX_labaS:
+            ctx_parser_set_color_model (parser, CTX_LABA, 1);
+            return ctx_parser_set_command (parser, CTX_COLOR);
+          case CTX_lchS:
+            ctx_parser_set_color_model (parser, CTX_LCH, 1);
+            return ctx_parser_set_command (parser, CTX_COLOR);
+          case CTX_lchaS:
+            ctx_parser_set_color_model (parser, CTX_LCHA, 1);
+            return ctx_parser_set_command (parser, CTX_COLOR);
 
-Ctx *
-ctx_new_for_framebuffer (void *data, int width, int height,
-                         int stride,
-                         CtxPixelFormat pixel_format)
-{
-  Ctx *ctx = ctx_new ();
-  CtxRasterizer *r = ctx_rasterizer_init ( (CtxRasterizer *) ctx_calloc (sizeof (CtxRasterizer), 1),
-                                          ctx, NULL, &ctx->state, data, 0, 0, width, height,
-                                          stride, pixel_format, CTX_ANTIALIAS_DEFAULT);
-  ctx_set_renderer (ctx, r);
-  return ctx;
-}
+          /* words that correspond to low integer constants
+          */
+          case CTX_winding:     return CTX_FILL_RULE_WINDING;
+          case CTX_evenOdd:
+          case CTX_even_odd:    return CTX_FILL_RULE_EVEN_ODD;
+          case CTX_bevel:       return CTX_JOIN_BEVEL;
+          case CTX_round:       return CTX_JOIN_ROUND;
+          case CTX_miter:       return CTX_JOIN_MITER;
+          case CTX_none:        return CTX_CAP_NONE;
+          case CTX_square:      return CTX_CAP_SQUARE;
+          case CTX_start:       return CTX_TEXT_ALIGN_START;
+          case CTX_end:         return CTX_TEXT_ALIGN_END;
+          case CTX_left:        return CTX_TEXT_ALIGN_LEFT;
+          case CTX_right:       return CTX_TEXT_ALIGN_RIGHT;
+          case CTX_center:      return CTX_TEXT_ALIGN_CENTER;
+          case CTX_top:         return CTX_TEXT_BASELINE_TOP;
+          case CTX_bottom :     return CTX_TEXT_BASELINE_BOTTOM;
+          case CTX_middle:      return CTX_TEXT_BASELINE_MIDDLE;
+          case CTX_alphabetic:  return CTX_TEXT_BASELINE_ALPHABETIC;
+          case CTX_hanging:     return CTX_TEXT_BASELINE_HANGING;
+          case CTX_ideographic: return CTX_TEXT_BASELINE_IDEOGRAPHIC;
 
-// ctx_new_for_stream (FILE *stream);
+          case CTX_userRGB:     return CTX_COLOR_SPACE_USER_RGB;
+          case CTX_deviceRGB:   return CTX_COLOR_SPACE_DEVICE_RGB;
+          case CTX_userCMYK:    return CTX_COLOR_SPACE_USER_CMYK;
+          case CTX_deviceCMYK:  return CTX_COLOR_SPACE_DEVICE_CMYK;
+#undef STR
+#undef LOWER
+          default:
+            ret = str_hash;
+        }
+    }
+  if (ret == CTX_CLOSE_PATH2)
+   {
+     ret = CTX_CLOSE_PATH;
+   }
 
-#if 0
-CtxRasterizer *ctx_rasterizer_new (void *data, int x, int y, int width, int height,
-                                   int stride, CtxPixelFormat pixel_format)
-{
-  CtxState    *state    = (CtxState *) malloc (sizeof (CtxState) );
-  CtxRasterizer *rasterizer = (CtxRasterizer *) malloc (sizeof (CtxRenderer) );
-  ctx_rasterizer_init (rasterizer, state, data, x, y, width, height,
-                       stride, pixel_format, CTX_ANTIALIAS_DEFAULT);
+  return ctx_parser_set_command (parser, (CtxCode) ret);
 }
-#endif
-
-CtxPixelFormatInfo *
-ctx_pixel_format_info (CtxPixelFormat format);
 
-#else
+enum
+{
+  CTX_PARSER_NEUTRAL = 0,
+  CTX_PARSER_NUMBER,
+  CTX_PARSER_NEGATIVE_NUMBER,
+  CTX_PARSER_WORD,
+  CTX_PARSER_COMMENT,
+  CTX_PARSER_STRING_APOS,
+  CTX_PARSER_STRING_QUOT,
+  CTX_PARSER_STRING_APOS_ESCAPED,
+  CTX_PARSER_STRING_QUOT_ESCAPED,
+  CTX_PARSER_STRING_A85,
+} CTX_STATE;
 
-CtxPixelFormatInfo *
-ctx_pixel_format_info (CtxPixelFormat format)
+static void ctx_parser_set_color_model (CtxParser *parser, CtxColorModel color_model, int stroke)
 {
-  return NULL;
+  parser->color_model      = color_model;
+  parser->color_stroke     = stroke;
+  parser->color_components = ctx_color_model_get_components (color_model);
 }
-#endif
 
-void
-ctx_current_point (Ctx *ctx, float *x, float *y)
+static void ctx_parser_get_color_rgba (CtxParser *parser, int offset, float *red, float *green, float *blue, 
float *alpha)
 {
-  if (!ctx)
-    { 
-      if (x) { *x = 0.0f; }
-      if (y) { *y = 0.0f; }
-    }
-#if CTX_RASTERIZER
-  if (ctx->renderer)
+  /* XXX - this function is to be deprecated */
+  *alpha = 1.0;
+  switch (parser->color_model)
     {
-      if (x) { *x = ( (CtxRasterizer *) (ctx->renderer) )->x; }
-      if (y) { *y = ( (CtxRasterizer *) (ctx->renderer) )->y; }
-      return;
+      case CTX_GRAYA:
+        *alpha = parser->numbers[offset + 1];
+        /* FALLTHROUGH */
+      case CTX_GRAY:
+        *red = *green = *blue = parser->numbers[offset + 0];
+        break;
+      default:
+      case CTX_LABA: // NYI - needs RGB profile
+      case CTX_LCHA: // NYI - needs RGB profile
+      case CTX_RGBA:
+        *alpha = parser->numbers[offset + 3];
+        /* FALLTHROUGH */
+      case CTX_LAB: // NYI
+      case CTX_LCH: // NYI
+      case CTX_RGB:
+        *red = parser->numbers[offset + 0];
+        *green = parser->numbers[offset + 1];
+        *blue = parser->numbers[offset + 2];
+        break;
+      case CTX_CMYKA:
+        *alpha = parser->numbers[offset + 4];
+        /* FALLTHROUGH */
+      case CTX_CMYK:
+        /* should use profile instead  */
+        *red = (1.0-parser->numbers[offset + 0]) *
+               (1.0 - parser->numbers[offset + 3]);
+        *green = (1.0-parser->numbers[offset + 1]) *
+                 (1.0 - parser->numbers[offset + 3]);
+        *blue = (1.0-parser->numbers[offset + 2]) *
+                (1.0 - parser->numbers[offset + 3]);
+        break;
     }
-#endif
-  if (x) { *x = ctx->state.x; }
-  if (y) { *y = ctx->state.y; }
-}
-
-float ctx_x (Ctx *ctx)
-{
-  float x = 0, y = 0;
-  ctx_current_point (ctx, &x, &y);
-  return x;
 }
 
-float ctx_y (Ctx *ctx)
+static void ctx_parser_dispatch_command (CtxParser *parser)
 {
-  float x = 0, y = 0;
-  ctx_current_point (ctx, &x, &y);
-  return y;
-}
+  CtxCode cmd = parser->command;
+  Ctx *ctx = parser->ctx;
+#if 1
+  if (parser->expected_args != CTX_ARG_STRING_OR_NUMBER &&
+      parser->expected_args != CTX_ARG_COLLECT_NUMBERS &&
+      parser->expected_args != parser->n_numbers)
+    {
+      if (0)
+         fprintf (stderr, "ctx:%i:%i %c got %i instead of %i args\n",
+               parser->line, parser->col,
+               cmd, parser->n_numbers, parser->expected_args);
+    }
+#endif
 
-static void
-ctx_process (Ctx *ctx, CtxEntry *entry)
-{
-#if CTX_CURRENT_PATH
-  switch (entry->code)
+#define arg(a)  (parser->numbers[a])
+  parser->command = CTX_NOP;
+  //parser->n_args = 0;
+  switch (cmd)
     {
-      case CTX_TEXT:
-      case CTX_STROKE_TEXT:
-      case CTX_BEGIN_PATH:
-        ctx->current_path.count = 0;
+      default:
+        break; // to silence warnings about missing ones
+      case CTX_PRESERVE:
+        ctx_preserve (ctx);
         break;
-      case CTX_CLIP:
       case CTX_FILL:
+        ctx_fill (ctx);
+        break;
+      case CTX_SAVE:
+        ctx_save (ctx);
+        break;
+      case CTX_START_GROUP:
+        ctx_start_group (ctx);
+        break;
+      case CTX_END_GROUP:
+        ctx_end_group (ctx);
+        break;
       case CTX_STROKE:
-              // XXX unless preserve
-        ctx->current_path.count = 0;
+        ctx_stroke (ctx);
         break;
-      case CTX_CLOSE_PATH:
-      case CTX_LINE_TO:
-      case CTX_MOVE_TO:
-      case CTX_QUAD_TO:
-      case CTX_SMOOTH_TO:
-      case CTX_SMOOTHQ_TO:
-      case CTX_REL_QUAD_TO:
-      case CTX_REL_SMOOTH_TO:
-      case CTX_REL_SMOOTHQ_TO:
-      case CTX_CURVE_TO:
-      case CTX_REL_CURVE_TO:
-      case CTX_ARC:
-      case CTX_ARC_TO:
-      case CTX_REL_ARC_TO:
-      case CTX_RECTANGLE:
-      case CTX_ROUND_RECTANGLE:
-        ctx_drawlist_add_entry (&ctx->current_path, entry);
+      case CTX_STROKE_SOURCE:
+        ctx_stroke_source (ctx);
         break;
-      default:
+      case CTX_RESTORE:
+        ctx_restore (ctx);
+        break;
+#if CTX_ENABLE_CM
+      case CTX_COLOR_SPACE:
+        if (parser->n_numbers == 1)
+        {
+          parser->color_space_slot = (CtxColorSpace) arg(0);
+          parser->command = CTX_COLOR_SPACE; // did this work without?
+        }
+        else
+        {
+          ctx_colorspace (ctx, (CtxColorSpace)parser->color_space_slot,
+                               parser->holding, parser->pos);
+        }
         break;
-    }
-#endif
-#if CTX_RASTERIZER
-  if (CTX_LIKELY(ctx->renderer && ctx->renderer->process == ctx_rasterizer_process))
-    {
-      ctx_rasterizer_process (ctx->renderer, (CtxCommand *) entry);
-    }
-  else
 #endif
-  if (CTX_LIKELY(ctx->renderer && ctx->renderer->process))
-    {
-      ctx->renderer->process (ctx->renderer, (CtxCommand *) entry);
-    }
-  else
-    {
-      /* these functions might alter the code and coordinates of
-         command that in the end gets added to the drawlist
-       */
-      ctx_interpret_style (&ctx->state, entry, ctx);
-      ctx_interpret_transforms (&ctx->state, entry, ctx);
-      ctx_interpret_pos (&ctx->state, entry, ctx);
-      ctx_drawlist_add_entry (&ctx->drawlist, entry);
-    }
-}
-
-
-int ctx_gradient_cache_valid = 0;
+      case CTX_KERNING_PAIR:
+        switch (parser->n_args)
+        {
+          case 0:
+            parser->numbers[0] = ctx_utf8_to_unichar ((char*)parser->holding);
+            break;
+          case 1:
+            parser->numbers[1] = ctx_utf8_to_unichar ((char*)parser->holding);
+            break;
+          case 2:
+            parser->numbers[2] = strtod ((char*)parser->holding, NULL);
+            {
+              CtxEntry e = {CTX_KERNING_PAIR, };
+              e.data.u16[0] = parser->numbers[0];
+              e.data.u16[1] = parser->numbers[1];
+              e.data.s32[1] = parser->numbers[2] * 256;
+              ctx_process (ctx, &e);
+            }
+            break;
+        }
+        parser->command = CTX_KERNING_PAIR;
+        parser->n_args ++; // make this more generic?
+        break;             
+      case CTX_TEXTURE:
+        if (parser->texture_done)
+        {
+        }
+        else
+        if (parser->n_numbers == 2)
+        {
+          const char *eid = (char*)parser->holding;
+          float x0 = arg(0);
+          float x1 = arg(1);
+          ctx_texture (ctx, eid, x0, x1);
+          parser->texture_done = 1;
+        }
+        parser->command = CTX_TEXTURE;
+        //parser->n_args++;
+        break;
+      case CTX_DEFINE_TEXTURE:
+        if (parser->texture_done)
+        {
+          if (parser->texture_done++ == 1)
+          {
+             const char *eid = (char*)parser->texture_id;
+             int width  = arg(0);
+             int height = arg(1);
+             CtxPixelFormat format = (CtxPixelFormat)arg(2);
+             int stride = ctx_pixel_format_get_stride (format, width);
 
-void
-ctx_state_gradient_clear_stops (CtxState *state)
-{
-//#if CTX_GRADIENT_CACHE
-//  ctx_gradient_cache_reset ();
-//#endif
-  ctx_gradient_cache_valid = 0;
-  state->gradient.n_stops = 0;
-}
 
+             if (parser->pos != stride * height)
+             {
+             fprintf (stderr, "unexpected datasize for define texture %s %ix%i\n size:%i != expected:%i - 
start of data: %i %i %i %i\n", eid, width, height,
+                               parser->pos,
+                               stride * height,
+                               parser->holding[0],
+                               parser->holding[1],
+                               parser->holding[2],
+                               parser->holding[3]
+                               );
+             }
+             else
+             ctx_define_texture (ctx, eid, width, height, stride, format, parser->holding, NULL);
+          }
+        }
+        else
+        {
+        switch (parser->n_numbers)
+        {
+          case 0:
+             strncpy ((char*)parser->texture_id, (char*)parser->holding, sizeof(parser->texture_id));
+             parser->texture_id[sizeof(parser->texture_id)-1]=0;
+             break;
+          case 1:
+          case 2:
+             break;
+          case 3:
+             parser->texture_done = 1;
+             break;
+          default:
+             fprintf (stderr, "!!%i\n", parser->n_numbers);
+             break;
+        }
+        }
+        parser->command = CTX_DEFINE_TEXTURE;
+        break;
 
-/****  end of engine ****/
 
-CtxBuffer *ctx_buffer_new_bare (void)
-{
-  CtxBuffer *buffer = (CtxBuffer *) ctx_calloc (sizeof (CtxBuffer), 1);
-  return buffer;
-}
+      case CTX_DEFINE_GLYPH:
+        /* XXX : reuse n_args logic - to enforce order */
+        if (parser->n_numbers == 1)
+        {
+          CtxEntry e = {CTX_DEFINE_GLYPH, };
+          e.data.u32[0] = parser->color_space_slot;
+          e.data.u32[1] = arg(0) * 256;
+          ctx_process (ctx, &e);
+        }
+        else
+        {
+          int unichar = ctx_utf8_to_unichar ((char*)parser->holding);
+          parser->color_space_slot = (CtxColorSpace)unichar;
+        }
+        parser->command = CTX_DEFINE_GLYPH;
+        break;             
 
-void ctx_buffer_set_data (CtxBuffer *buffer,
-                          void *data, int width, int height,
-                          int stride,
-                          CtxPixelFormat pixel_format,
-                          void (*freefunc) (void *pixels, void *user_data),
-                          void *user_data)
-{
-  if (buffer->free_func)
-    { buffer->free_func (buffer->data, buffer->user_data); }
-  if (stride <= 0)
-    stride = ctx_pixel_format_get_stride (pixel_format, width);
-  buffer->data      = data;
-  buffer->width     = width;
-  buffer->height    = height;
-  buffer->stride    = stride;
-  buffer->format    = ctx_pixel_format_info (pixel_format);
-  buffer->free_func = freefunc;
-  buffer->user_data = user_data;
-}
+      case CTX_COLOR:
+        {
+          switch (parser->color_model)
+            {
+              case CTX_GRAY:
+              case CTX_GRAYA:
+              case CTX_RGB:
+              case CTX_RGBA:
+              case CTX_DRGB:
+              case CTX_DRGBA:
+                ctx_color_raw (ctx, parser->color_model, parser->numbers, parser->color_stroke);
+                break;
+#if CTX_ENABLE_CMYK
+              case CTX_CMYK:
+              case CTX_CMYKA:
+                ctx_color_raw (ctx, parser->color_model, parser->numbers, parser->color_stroke);
+                break;
+#else
+              /* when there is no cmyk support at all in rasterizer
+               * do a naive mapping to RGB on input.
+               */
+              case CTX_CMYK:
+              case CTX_CMYKA:
+              case CTX_DCMYKA:
+                {
+                  float rgba[4] = {1,1,1,1.0f};
 
-CtxBuffer *ctx_buffer_new_for_data (void *data, int width, int height,
-                                    int stride,
-                                    CtxPixelFormat pixel_format,
-                                    void (*freefunc) (void *pixels, void *user_data),
-                                    void *user_data)
-{
-  CtxBuffer *buffer = ctx_buffer_new_bare ();
-  ctx_buffer_set_data (buffer, data, width, height, stride, pixel_format,
-                       freefunc, user_data);
-  return buffer;
-}
+                  ctx_cmyk_to_rgb (arg(0), arg(1), arg(2), arg(3), &rgba[0], &rgba[1], &rgba[2]);
+                  if (parser->color_model == CTX_CMYKA)
+                    { rgba[3] = arg(4); }
+                  ctx_color_raw (ctx, CTX_RGBA, rgba, parser->color_stroke);
+                }
+                break;
+#endif
+              case CTX_LAB:
+              case CTX_LCH:
+              default:
+                break;
+            }
+        }
+        break;
+      case CTX_LINE_DASH:
+        if (parser->n_numbers)
+        {
+          ctx_line_dash (ctx, parser->numbers, parser->n_numbers);
+        }
+        else
+        {
+          ctx_line_dash (ctx, NULL, 0);
+        }
+        //append_dash_val (ctx, arg(0));
+        break;
+      case CTX_ARC_TO:
+        ctx_arc_to (ctx, arg(0), arg(1), arg(2), arg(3), arg(4));
+        break;
+      case CTX_REL_ARC_TO:
+        ctx_rel_arc_to (ctx, arg(0), arg(1), arg(2), arg(3), arg(4) );
+        break;
+      case CTX_REL_SMOOTH_TO:
+        {
+          float cx = parser->pcx;
+          float cy = parser->pcy;
+          float ax = 2 * ctx_x (ctx) - cx;
+          float ay = 2 * ctx_y (ctx) - cy;
+          ctx_curve_to (ctx, ax, ay, arg(0) +  cx, arg(1) + cy,
+                        arg(2) + cx, arg(3) + cy);
+          parser->pcx = arg(0) + cx;
+          parser->pcy = arg(1) + cy;
+        }
+        break;
+      case CTX_SMOOTH_TO:
+        {
+          float ax = 2 * ctx_x (ctx) - parser->pcx;
+          float ay = 2 * ctx_y (ctx) - parser->pcy;
+          ctx_curve_to (ctx, ax, ay, arg(0), arg(1),
+                        arg(2), arg(3) );
+          parser->pcx = arg(0);
+          parser->pcx = arg(1);
+        }
+        break;
+      case CTX_SMOOTHQ_TO:
+        ctx_quad_to (ctx, parser->pcx, parser->pcy, arg(0), arg(1) );
+        break;
+      case CTX_REL_SMOOTHQ_TO:
+        {
+          float cx = parser->pcx;
+          float cy = parser->pcy;
+          parser->pcx = 2 * ctx_x (ctx) - parser->pcx;
+          parser->pcy = 2 * ctx_y (ctx) - parser->pcy;
+          ctx_quad_to (ctx, parser->pcx, parser->pcy, arg(0) +  cx, arg(1) + cy);
+        }
+        break;
+      case CTX_VER_LINE_TO:
+        ctx_line_to (ctx, ctx_x (ctx), arg(0) );
+        parser->command = CTX_VER_LINE_TO;
+        parser->pcx = ctx_x (ctx);
+        parser->pcy = ctx_y (ctx);
+        break;
+      case CTX_HOR_LINE_TO:
+        ctx_line_to (ctx, arg(0), ctx_y (ctx) );
+        parser->command = CTX_HOR_LINE_TO;
+        parser->pcx = ctx_x (ctx);
+        parser->pcy = ctx_y (ctx);
+        break;
+      case CTX_REL_HOR_LINE_TO:
+        ctx_rel_line_to (ctx, arg(0), 0.0f);
+        parser->command = CTX_REL_HOR_LINE_TO;
+        parser->pcx = ctx_x (ctx);
+        parser->pcy = ctx_y (ctx);
+        break;
+      case CTX_REL_VER_LINE_TO:
+        ctx_rel_line_to (ctx, 0.0f, arg(0) );
+        parser->command = CTX_REL_VER_LINE_TO;
+        parser->pcx = ctx_x (ctx);
+        parser->pcy = ctx_y (ctx);
+        break;
+      case CTX_ARC:
+        ctx_arc (ctx, arg(0), arg(1), arg(2), arg(3), arg(4), arg(5) );
+        break;
+      case CTX_APPLY_TRANSFORM:
+        ctx_apply_transform (ctx, arg(0), arg(1), arg(2), arg(3), arg(4), arg(5) );
+        break;
+      case CTX_CURVE_TO:
+        ctx_curve_to (ctx, arg(0), arg(1), arg(2), arg(3), arg(4), arg(5) );
+        parser->pcx = arg(2);
+        parser->pcy = arg(3);
+        parser->command = CTX_CURVE_TO;
+        break;
+      case CTX_REL_CURVE_TO:
+        parser->pcx = arg(2) + ctx_x (ctx);
+        parser->pcy = arg(3) + ctx_y (ctx);
+        ctx_rel_curve_to (ctx, arg(0), arg(1), arg(2), arg(3), arg(4), arg(5) );
+        parser->command = CTX_REL_CURVE_TO;
+        break;
+      case CTX_LINE_TO:
+        ctx_line_to (ctx, arg(0), arg(1) );
+        parser->command = CTX_LINE_TO;
+        parser->pcx = arg(0);
+        parser->pcy = arg(1);
+        break;
+      case CTX_MOVE_TO:
+        ctx_move_to (ctx, arg(0), arg(1) );
+        parser->command = CTX_LINE_TO;
+        parser->pcx = arg(0);
+        parser->pcy = arg(1);
+        parser->left_margin = parser->pcx;
+        break;
+      case CTX_FONT_SIZE:
+        ctx_font_size (ctx, arg(0) );
+        break;
+      case CTX_MITER_LIMIT:
+        ctx_miter_limit (ctx, arg(0) );
+        break;
+      case CTX_SCALE:
+        ctx_scale (ctx, arg(0), arg(1) );
+        break;
+      case CTX_QUAD_TO:
+        parser->pcx = arg(0);
+        parser->pcy = arg(1);
+        ctx_quad_to (ctx, arg(0), arg(1), arg(2), arg(3) );
+        parser->command = CTX_QUAD_TO;
+        break;
+      case CTX_REL_QUAD_TO:
+        parser->pcx = arg(0) + ctx_x (ctx);
+        parser->pcy = arg(1) + ctx_y (ctx);
+        ctx_rel_quad_to (ctx, arg(0), arg(1), arg(2), arg(3) );
+        parser->command = CTX_REL_QUAD_TO;
+        break;
+      case CTX_CLIP:
+        ctx_clip (ctx);
+        break;
+      case CTX_TRANSLATE:
+        ctx_translate (ctx, arg(0), arg(1) );
+        break;
+      case CTX_ROTATE:
+        ctx_rotate (ctx, arg(0) );
+        break;
+      case CTX_FONT:
+        ctx_font (ctx, (char *) parser->holding);
+        break;
 
-void ctx_buffer_pixels_free (void *pixels, void *userdata)
-{
-  free (pixels);
+      case CTX_STROKE_TEXT:
+      case CTX_TEXT:
+        if (parser->n_numbers == 1)
+          { ctx_rel_move_to (ctx, -parser->numbers[0], 0.0); }  //  XXX : scale by font(size)
+        else
+          {
+            for (char *c = (char *) parser->holding; c; )
+              {
+                char *next_nl = ctx_strchr (c, '\n');
+                if (next_nl)
+                  { *next_nl = 0; }
+                /* do our own layouting on a per-word basis?, to get justified
+                 * margins? then we'd want explict margins rather than the
+                 * implicit ones from move_to's .. making move_to work within
+                 * margins.
+                 */
+                if (cmd == CTX_STROKE_TEXT)
+                  { ctx_text_stroke (ctx, c); }
+                else
+                  { ctx_text (ctx, c); }
+                if (next_nl)
+                  {
+                    *next_nl = '\n'; // swap it newline back in
+                    ctx_move_to (ctx, parser->left_margin, ctx_y (ctx) +
+                                 ctx_get_font_size (ctx) );
+                    c = next_nl + 1;
+                    if (c[0] == 0)
+                      { c = NULL; }
+                  }
+                else
+                  {
+                    c = NULL;
+                  }
+              }
+          }
+        if (cmd == CTX_STROKE_TEXT)
+          { parser->command = CTX_STROKE_TEXT; }
+        else
+          { parser->command = CTX_TEXT; }
+        break;
+      case CTX_REL_LINE_TO:
+        ctx_rel_line_to (ctx, arg(0), arg(1) );
+        parser->pcx += arg(0);
+        parser->pcy += arg(1);
+        break;
+      case CTX_REL_MOVE_TO:
+        ctx_rel_move_to (ctx, arg(0), arg(1) );
+        parser->pcx += arg(0);
+        parser->pcy += arg(1);
+        parser->left_margin = ctx_x (ctx);
+        break;
+      case CTX_LINE_WIDTH:
+        ctx_line_width (ctx, arg(0));
+        break;
+      case CTX_LINE_DASH_OFFSET:
+        ctx_line_dash_offset (ctx, arg(0));
+        break;
+      case CTX_IMAGE_SMOOTHING:
+        ctx_image_smoothing (ctx, arg(0));
+        break;
+      case CTX_SHADOW_COLOR:
+        ctx_shadow_rgba (ctx, arg(0), arg(1), arg(2), arg(3));
+        break;
+      case CTX_SHADOW_BLUR:
+        ctx_shadow_blur (ctx, arg(0) );
+        break;
+      case CTX_SHADOW_OFFSET_X:
+        ctx_shadow_offset_x (ctx, arg(0) );
+        break;
+      case CTX_SHADOW_OFFSET_Y:
+        ctx_shadow_offset_y (ctx, arg(0) );
+        break;
+      case CTX_LINE_JOIN:
+        ctx_line_join (ctx, (CtxLineJoin) arg(0) );
+        break;
+      case CTX_LINE_CAP:
+        ctx_line_cap (ctx, (CtxLineCap) arg(0) );
+        break;
+      case CTX_COMPOSITING_MODE:
+        ctx_compositing_mode (ctx, (CtxCompositingMode) arg(0) );
+        break;
+      case CTX_BLEND_MODE:
+        {
+          int blend_mode = arg(0);
+          if (blend_mode == CTX_COLOR) blend_mode = CTX_BLEND_COLOR;
+          ctx_blend_mode (ctx, (CtxBlend)blend_mode);
+        }
+        break;
+      case CTX_FILL_RULE:
+        ctx_fill_rule (ctx, (CtxFillRule) arg(0) );
+        break;
+      case CTX_TEXT_ALIGN:
+        ctx_text_align (ctx, (CtxTextAlign) arg(0) );
+        break;
+      case CTX_TEXT_BASELINE:
+        ctx_text_baseline (ctx, (CtxTextBaseline) arg(0) );
+        break;
+      case CTX_TEXT_DIRECTION:
+        ctx_text_direction (ctx, (CtxTextDirection) arg(0) );
+        break;
+      case CTX_IDENTITY:
+        ctx_identity (ctx);
+        break;
+      case CTX_RECTANGLE:
+        ctx_rectangle (ctx, arg(0), arg(1), arg(2), arg(3) );
+        break;
+      case CTX_FILL_RECT:
+        ctx_rectangle (ctx, arg(0), arg(1), arg(2), arg(3) );
+        ctx_fill (ctx);
+        break;
+      case CTX_STROKE_RECT:
+        ctx_rectangle (ctx, arg(0), arg(1), arg(2), arg(3) );
+        ctx_stroke (ctx);
+        break;
+      case CTX_ROUND_RECTANGLE:
+        ctx_round_rectangle (ctx, arg(0), arg(1), arg(2), arg(3), arg(4));
+        break;
+      case CTX_VIEW_BOX:
+        ctx_view_box (ctx, arg(0), arg(1), arg(2), arg(3) );
+        break;
+      case CTX_LINEAR_GRADIENT:
+        ctx_linear_gradient (ctx, arg(0), arg(1), arg(2), arg(3) );
+        break;
+      case CTX_RADIAL_GRADIENT:
+        ctx_radial_gradient (ctx, arg(0), arg(1), arg(2), arg(3), arg(4), arg(5) );
+        break;
+      case CTX_GRADIENT_STOP:
+        {
+          float red, green, blue, alpha;
+          ctx_parser_get_color_rgba (parser, 1, &red, &green, &blue, &alpha);
+          ctx_gradient_add_stop (ctx, arg(0), red, green, blue, alpha);
+        }
+        break;
+      case CTX_GLOBAL_ALPHA:
+        ctx_global_alpha (ctx, arg(0) );
+        break;
+      case CTX_BEGIN_PATH:
+        ctx_begin_path (ctx);
+        break;
+      case CTX_GLYPH:
+        ctx_glyph (ctx, arg(0), 0);
+        break;
+      case CTX_CLOSE_PATH:
+        ctx_close_path (ctx);
+        break;
+      case CTX_EXIT:
+        if (parser->exit)
+          { parser->exit (parser->exit_data);
+            return;
+          }
+        break;
+      case CTX_FLUSH:
+        //ctx_flush (ctx);
+        break;
+      case CTX_RESET:
+        ctx_reset (ctx);
+        if (parser->translate_origin)
+        {
+          ctx_translate (ctx,
+                         (parser->cursor_x-1) * parser->cell_width * 1.0,
+                         (parser->cursor_y-1) * parser->cell_height * 1.0);
+        }
+        break;
+    }
+#undef arg
+//  parser->n_numbers = 0;
 }
 
-CtxBuffer *ctx_buffer_new (int width, int height,
-                           CtxPixelFormat pixel_format)
+static void ctx_parser_holding_append (CtxParser *parser, int byte)
 {
-  //CtxPixelFormatInfo *info = ctx_pixel_format_info (pixel_format);
-  CtxBuffer *buffer = ctx_buffer_new_bare ();
-  int stride = ctx_pixel_format_get_stride (pixel_format, width);
-  int data_len = stride * height;
-  if (pixel_format == CTX_FORMAT_YUV420)
-    data_len = width * height + ((width/2) * (height/2)) * 2;
-
-  uint8_t *pixels = (uint8_t*)ctx_calloc (data_len, 1);
+#if !CTX_PARSER_FIXED_TEMP
+  if (parser->hold_len < parser->pos + 1 + 1)
+  {
+    int new_len = parser->hold_len * 1.5;
+    if (new_len < 512) new_len = 512;
+    parser->holding = (uint8_t*)realloc (parser->holding, new_len);
+    parser->hold_len = new_len;
+  }
+#endif
 
-  ctx_buffer_set_data (buffer, pixels, width, height, stride, pixel_format,
-                       ctx_buffer_pixels_free, NULL);
-  return buffer;
+  parser->holding[parser->pos++]=byte;
+#if CTX_PARSER_FIXED_TEMP
+  if (parser->pos > (int) sizeof (parser->holding)-2)
+    { parser->pos = sizeof (parser->holding)-2; }
+#endif
+  parser->holding[parser->pos]=0;
 }
 
-void ctx_buffer_deinit (CtxBuffer *buffer)
+static void ctx_parser_transform_percent (CtxParser *parser, CtxCode code, int arg_no, float *value)
 {
-  if (buffer->free_func)
-    buffer->free_func (buffer->data, buffer->user_data);
-  if (buffer->eid)
-  {
-    free (buffer->eid);
-  }
-  buffer->eid = NULL;
-  buffer->data = NULL;
-  buffer->free_func = NULL;
-  buffer->user_data  = NULL;
-  if (buffer->color_managed)
-  {
-    if (buffer->color_managed != buffer)
+  int big   = parser->width;
+  int small = parser->height;
+  if (big < small)
     {
-      ctx_buffer_free (buffer->color_managed);
+      small = parser->width;
+      big   = parser->height;
+    }
+  switch (code)
+    {
+      case CTX_RADIAL_GRADIENT:
+      case CTX_ARC:
+        switch (arg_no)
+          {
+            case 0:
+            case 3:
+              *value *= (parser->width/100.0);
+              break;
+            case 1:
+            case 4:
+              *value *= (parser->height/100.0);
+              break;
+            case 2:
+            case 5:
+              *value *= small/100.0;
+              break;
+          }
+        break;
+      case CTX_FONT_SIZE:
+      case CTX_MITER_LIMIT:
+      case CTX_LINE_WIDTH:
+      case CTX_LINE_DASH_OFFSET:
+        {
+          *value *= (small/100.0);
+        }
+        break;
+      case CTX_ARC_TO:
+      case CTX_REL_ARC_TO:
+        if (arg_no > 3)
+          {
+            *value *= (small/100.0);
+          }
+        else
+          {
+            if (arg_no % 2 == 0)
+              { *value  *= ( (parser->width) /100.0); }
+            else
+              { *value *= ( (parser->height) /100.0); }
+          }
+        break;
+      case CTX_ROUND_RECTANGLE:
+        if (arg_no == 4)
+        {
+          { *value *= ((parser->height)/100.0); }
+          return;
+        }
+        /* FALLTHROUGH */
+      default: // even means x coord
+        if (arg_no % 2 == 0)
+          { *value  *= ((parser->width)/100.0); }
+        else
+          { *value *= ((parser->height)/100.0); }
+        break;
     }
-    buffer->color_managed = NULL;
-  }
 }
 
-void ctx_buffer_free (CtxBuffer *buffer)
+static void ctx_parser_transform_percent_height (CtxParser *parser, CtxCode code, int arg_no, float *value)
 {
-  ctx_buffer_deinit (buffer);
-  free (buffer);
+  *value *= (parser->height/100.0);
 }
 
-static int
-ctx_texture_check_eid (Ctx *ctx, const char *eid, int *tw, int *th)
+static void ctx_parser_transform_percent_width (CtxParser *parser, CtxCode code, int arg_no, float *value)
 {
-  for (int i = 0; i <  CTX_MAX_TEXTURES; i++)
-  {
-    if (ctx->texture[i].data &&
-        ctx->texture[i].eid  &&
-        !strcmp (ctx->texture[i].eid, eid))
-    {
-      if (tw) *tw = ctx->texture[i].width;
-      if (th) *th = ctx->texture[i].height;
-      ctx->texture[i].frame = ctx->texture_cache->frame;
-      return i;
-    }
-  }
-  return -1;
+  *value *= (parser->height/100.0);
 }
 
-const char* ctx_texture_init (Ctx           *ctx,
-                              const char    *eid,
-                              int            width,
-                              int            height,
-                              int            stride,
-                              CtxPixelFormat format,
-                              void          *space,
-                              uint8_t       *pixels,
-                              void (*freefunc) (void *pixels, void *user_data),
-                              void *user_data)
+static void ctx_parser_transform_cell (CtxParser *parser, CtxCode code, int arg_no, float *value)
 {
-  int id = 0;
-  if (eid)
-  {
-    for (int i = 0; i <  CTX_MAX_TEXTURES; i++)
+  float small = parser->cell_width;
+  if (small > parser->cell_height)
+    { small = parser->cell_height; }
+  switch (code)
     {
-      if (ctx->texture[i].data &&
-          ctx->texture[i].eid &&
-          !strcmp (ctx->texture[i].eid, eid))
-      {
-        ctx->texture[i].frame = ctx->texture_cache->frame;
-        if (freefunc && user_data != (void*)23)
-          freefunc (pixels, user_data);
-        return ctx->texture[i].eid;
-      }
-      if (ctx->texture[i].data == NULL 
-          ||   (ctx->texture_cache->frame - ctx->texture[i].frame >= 2))
-        id = i;
-    }
-  } else
-  {
-    for (int i = 0; i <  CTX_MAX_TEXTURES; i++)
-    {
-      if (ctx->texture[i].data == NULL 
-          || (ctx->texture_cache->frame - ctx->texture[i].frame > 2))
-        id = i;
-    }
-  }
-  //int bpp = ctx_pixel_format_bits_per_pixel (format);
-  ctx_buffer_deinit (&ctx->texture[id]);
-
-  if (stride<=0)
-  {
-    stride = ctx_pixel_format_get_stride ((CtxPixelFormat)format, width);
-  }
-
-  int data_len = stride * height;
-  if (format == CTX_FORMAT_YUV420)
-          data_len = width * height +
-                  2 * ((width/2)*(height/2));
-
-  if (freefunc == ctx_buffer_pixels_free && user_data == (void*)23)
-  {
-     uint8_t *tmp = (uint8_t*)malloc (data_len);
-     memcpy (tmp, pixels, data_len);
-     pixels = tmp;
-  }
-
-  ctx_buffer_set_data (&ctx->texture[id],
-                       pixels, width, height,
-                       stride, format,
-                       freefunc, user_data);
-#if CTX_ENABLE_CM
-  ctx->texture[id].space = space;
-#endif
-  ctx->texture[id].frame = ctx->texture_cache->frame;
-  if (eid)
-  {
-    /* we got an eid, this is the fast path */
-    ctx->texture[id].eid = strdup (eid);
-  }
-  else
-  {
-    uint8_t hash[20];
-    char ascii[41];
-
-    CtxSHA1 *sha1 = ctx_sha1_new ();
-    ctx_sha1_process (sha1, pixels, stride * height);
-    ctx_sha1_done (sha1, hash);
-    ctx_sha1_free (sha1);
-    const char *hex="0123456789abcdef";
-    for (int i = 0; i < 20; i ++)
-    {
-       ascii[i*2]=hex[hash[i]/16];
-       ascii[i*2+1]=hex[hash[i]%16];
+      case CTX_RADIAL_GRADIENT:
+      case CTX_ARC:
+        switch (arg_no)
+          {
+            case 0:
+            case 3:
+              *value *= parser->cell_width;
+              break;
+            case 1:
+            case 4:
+              *value *= parser->cell_height;
+              break;
+            case 2:
+            case 5:
+              *value *= small; // use height?
+              break;
+          }
+        break;
+      case CTX_MITER_LIMIT:
+      case CTX_FONT_SIZE:
+      case CTX_LINE_WIDTH:
+      case CTX_LINE_DASH_OFFSET:
+        {
+          *value *= parser->cell_height;
+        }
+        break;
+      case CTX_ARC_TO:
+      case CTX_REL_ARC_TO:
+        if (arg_no > 3)
+          {
+            *value *= small;
+          }
+        else
+          {
+            *value *= (arg_no%2==0) ?parser->cell_width:parser->cell_height;
+          }
+        break;
+      case CTX_RECTANGLE:
+        if (arg_no % 2 == 0)
+          { *value *= parser->cell_width; }
+        else
+          {
+            if (! (arg_no > 1) )
+              { (*value) -= 1.0f; }
+            *value *= parser->cell_height;
+          }
+        break;
+      default: // even means x coord odd means y coord
+        *value *= (arg_no%2==0) ?parser->cell_width:parser->cell_height;
+        break;
     }
-    ascii[40]=0;
-    ctx->texture[id].eid = strdup (ascii);
-  }
-  return ctx->texture[id].eid;
-}
-
-static void
-_ctx_texture_prepare_color_management (CtxRasterizer *rasterizer,
-                                      CtxBuffer     *buffer)
-{
-   switch (buffer->format->pixel_format)
-   {
-#ifndef NO_BABL
-#if CTX_BABL
-     case CTX_FORMAT_RGBA8:
-       if (buffer->space == rasterizer->state->gstate.device_space)
-       {
-         buffer->color_managed = buffer;
-       }
-       else
-       {
-          buffer->color_managed = ctx_buffer_new (buffer->width, buffer->height,
-                                                  CTX_FORMAT_RGBA8);
-          babl_process (
-             babl_fish (babl_format_with_space ("R'G'B'A u8", buffer->space),
-                        babl_format_with_space ("R'G'B'A u8", rasterizer->state->gstate.device_space)),
-             buffer->data, buffer->color_managed->data,
-             buffer->width * buffer->height
-             );
-       }
-       break;
-     case CTX_FORMAT_RGB8:
-       if (buffer->space == rasterizer->state->gstate.device_space)
-       {
-         buffer->color_managed = buffer;
-       }
-       else
-       {
-         buffer->color_managed = ctx_buffer_new (buffer->width, buffer->height,
-                                               CTX_FORMAT_RGB8);
-         babl_process (
-            babl_fish (babl_format_with_space ("R'G'B' u8", buffer->space),
-                       babl_format_with_space ("R'G'B' u8", rasterizer->state->gstate.device_space)),
-            buffer->data, buffer->color_managed->data,
-            buffer->width * buffer->height
-          );
-       }
-       break;
-#endif
-#endif
-     default:
-       buffer->color_managed = buffer;
-   }
 }
 
+// %h %v %m %M
 
-
-int ctx_utf8_len (const unsigned char first_byte)
+static void ctx_parser_number_done (CtxParser *parser)
 {
-  if      ( (first_byte & 0x80) == 0)
-    { return 1; } /* ASCII */
-  else if ( (first_byte & 0xE0) == 0xC0)
-    { return 2; }
-  else if ( (first_byte & 0xF0) == 0xE0)
-    { return 3; }
-  else if ( (first_byte & 0xF8) == 0xF0)
-    { return 4; }
-  return 1;
-}
 
-
-const char *ctx_utf8_skip (const char *s, int utf8_length)
-{
-  int count;
-  if (!s)
-    { return NULL; }
-  for (count = 0; *s; s++)
-    {
-      if ( (*s & 0xC0) != 0x80)
-        { count++; }
-      if (count == utf8_length + 1)
-        { return s; }
-    }
-  return s;
 }
 
-//  XXX  :  unused
-int ctx_utf8_strlen (const char *s)
+static void ctx_parser_word_done (CtxParser *parser)
 {
-  int count;
-  if (!s)
-    { return 0; }
-  for (count = 0; *s; s++)
-    if ( (*s & 0xC0) != 0x80)
-      { count++; }
-  return count;
-}
+  parser->holding[parser->pos]=0;
+  //int old_args = parser->expected_args;
+  int command = ctx_parser_resolve_command (parser, parser->holding);
+  if ((command >= 0 && command < 32)
+      || (command > 150) || (command < 0)
+      )  // special case low enum values
+    {                   // and enum values too high to be
+                        // commands - permitting passing words
+                        // for strings in some cases
+      parser->numbers[parser->n_numbers] = command;
 
-int
-ctx_unichar_to_utf8 (uint32_t  ch,
-                     uint8_t  *dest)
-{
-  /* http://www.cprogramming.com/tutorial/utf8.c  */
-  /*  Basic UTF-8 manipulation routines
-    by Jeff Bezanson
-    placed in the public domain Fall 2005 ... */
-  if (ch < 0x80)
-    {
-      dest[0] = (char) ch;
-      return 1;
-    }
-  if (ch < 0x800)
-    {
-      dest[0] = (ch>>6) | 0xC0;
-      dest[1] = (ch & 0x3F) | 0x80;
-      return 2;
+      // trigger transition from number
+      parser->state = CTX_PARSER_NUMBER;
+      ctx_parser_feed_byte (parser, ',');
     }
-  if (ch < 0x10000)
+  else if (command > 0)
     {
-      dest[0] = (ch>>12) | 0xE0;
-      dest[1] = ( (ch>>6) & 0x3F) | 0x80;
-      dest[2] = (ch & 0x3F) | 0x80;
-      return 3;
+#if 0
+      if (old_args == CTX_ARG_COLLECT_NUMBERS ||
+          old_args == CTX_ARG_STRING_OR_NUMBER)
+      {
+        int tmp1 = parser->command;
+        int tmp2 = parser->expected_args;
+        int tmp3 = parser->n_numbers;
+ //     int tmp4 = parser->n_args;
+        ctx_parser_dispatch_command (parser);
+        parser->command = (CtxCode)tmp1;
+        parser->expected_args = tmp2;
+        parser->n_numbers = tmp3;
+ //     parser->n_args = tmp4;
+      }
+#endif
+
+      parser->command = (CtxCode) command;
+      parser->n_numbers = 0;
+      parser->n_args = 0;
+      if (parser->expected_args == 0)
+        {
+          ctx_parser_dispatch_command (parser);
+        }
     }
-  if (ch < 0x110000)
+  else
     {
-      dest[0] = (ch>>18) | 0xF0;
-      dest[1] = ( (ch>>12) & 0x3F) | 0x80;
-      dest[2] = ( (ch>>6) & 0x3F) | 0x80;
-      dest[3] = (ch & 0x3F) | 0x80;
-      return 4;
+      /* interpret char by char */
+      uint8_t buf[16]=" ";
+      for (int i = 0; parser->pos && parser->holding[i] > ' '; i++)
+        {
+          buf[0] = parser->holding[i];
+          parser->command = (CtxCode) ctx_parser_resolve_command (parser, buf);
+          parser->n_numbers = 0;
+          parser->n_args = 0;
+          if (parser->command > 0)
+            {
+              if (parser->expected_args == 0)
+                {
+                  ctx_parser_dispatch_command (parser);
+                }
+            }
+          else
+            {
+              ctx_log ("unhandled command '%c'\n", buf[0]);
+            }
+        }
     }
-  return 0;
-}
-
-uint32_t
-ctx_utf8_to_unichar (const char *input)
-{
-  const uint8_t *utf8 = (const uint8_t *) input;
-  uint8_t c = utf8[0];
-  if ( (c & 0x80) == 0)
-    { return c; }
-  else if ( (c & 0xE0) == 0xC0)
-    return ( (utf8[0] & 0x1F) << 6) |
-           (utf8[1] & 0x3F);
-  else if ( (c & 0xF0) == 0xE0)
-    return ( (utf8[0] & 0xF)  << 12) |
-           ( (utf8[1] & 0x3F) << 6) |
-           (utf8[2] & 0x3F);
-  else if ( (c & 0xF8) == 0xF0)
-    return ( (utf8[0] & 0x7)  << 18) |
-           ( (utf8[1] & 0x3F) << 12) |
-           ( (utf8[2] & 0x3F) << 6) |
-           (utf8[3] & 0x3F);
-  else if ( (c & 0xFC) == 0xF8)
-    return ( (utf8[0] & 0x3)  << 24) |
-           ( (utf8[1] & 0x3F) << 18) |
-           ( (utf8[2] & 0x3F) << 12) |
-           ( (utf8[3] & 0x3F) << 6) |
-           (utf8[4] & 0x3F);
-  else if ( (c & 0xFE) == 0xFC)
-    return ( (utf8[0] & 0x1)  << 30) |
-           ( (utf8[1] & 0x3F) << 24) |
-           ( (utf8[2] & 0x3F) << 18) |
-           ( (utf8[3] & 0x3F) << 12) |
-           ( (utf8[4] & 0x3F) << 6) |
-           (utf8[5] & 0x3F);
-  return 0;
-}
-
-#if CTX_RASTERIZER
-
-
-
-static int
-ctx_rect_intersect (const CtxIntRectangle *a, const CtxIntRectangle *b)
-{
-  if (a->x >= b->x + b->width ||
-      b->x >= a->x + a->width ||
-      a->y >= b->y + b->height ||
-      b->y >= a->y + a->height) return 0;
-
-  return 1;
 }
 
-static void
-_ctx_add_hash (CtxHasher *hasher, CtxIntRectangle *shape_rect, char *hash)
+static void ctx_parser_string_done (CtxParser *parser)
 {
-  CtxIntRectangle rect = {0,0, hasher->rasterizer.blit_width/hasher->cols,
-                            hasher->rasterizer.blit_height/hasher->rows};
-  int hno = 0;
-  for (int row = 0; row < hasher->rows; row++)
-    for (int col = 0; col < hasher->cols; col++, hno++)
-     {
-      rect.x = col * rect.width;
-      rect.y = row * rect.height;
-      if (ctx_rect_intersect (shape_rect, &rect))
-      {
-        int temp = hasher->hashes[(row * hasher->cols + col)  *20 + 0];
-        for (int i = 0; i <19;i++)
-           hasher->hashes[(row * hasher->cols + col)  *20 + i] =
-             hasher->hashes[(row * hasher->cols + col)  *20 + i+1]^
-             hash[i];
-        hasher->hashes[(row * hasher->cols + col)  *20 + 19] =
-                temp ^ hash[19];
-      }
+  if (parser->expected_args == CTX_ARG_STRING_OR_NUMBER)
+  {
+          /*
+    if (parser->state != CTX_PARSER_NUMBER &&
+        parser->state != CTX_PARSER_NEGATIVE_NUMBER &&
+        parser->state != CTX_PARSER_STRING_A85 &&
+        parser->state != CTX_PARSER_STRING_APOS &&
+        parser->state != CTX_PARSER_STRING_QUOT
+        )
+        */
+    {
+    int tmp1 = parser->command;
+    int tmp2 = parser->expected_args;
+    int tmp3 = parser->n_numbers;
+    int tmp4 = parser->n_args;
+    ctx_parser_dispatch_command (parser);
+    parser->command = (CtxCode)tmp1;
+    parser->expected_args = tmp2;
+    parser->n_numbers = tmp3;
+    parser->n_args = tmp4;
     }
+  }
+  else
+  {
+    ctx_parser_dispatch_command (parser);
+  }
 }
 
-
-static void
-ctx_hasher_process (void *user_data, CtxCommand *command)
+void ctx_parser_feed_byte (CtxParser *parser, int byte)
 {
-  CtxEntry *entry = &command->entry;
-  CtxRasterizer *rasterizer = (CtxRasterizer *) user_data;
-  CtxHasher *hasher = (CtxHasher*) user_data;
-  CtxState *state = rasterizer->state;
-  CtxCommand *c = (CtxCommand *) entry;
-  int aa = 15;//rasterizer->aa;
-
-  ctx_interpret_pos_bare (rasterizer->state, entry, NULL);
-  ctx_interpret_style (rasterizer->state, entry, NULL);
-
-  switch (c->code)
+  switch (byte)
     {
-      case CTX_TEXT:
-        {
-          CtxSHA1 sha1;
-          memcpy (&sha1, &hasher->sha1_fill, sizeof (CtxSHA1));
-          char ctx_sha1_hash[20];
-          float width = ctx_text_width (rasterizer->ctx, ctx_arg_string());
-
-
-          float height = ctx_get_font_size (rasterizer->ctx);
-           CtxIntRectangle shape_rect;
-          
-           shape_rect.x=rasterizer->x;
-           shape_rect.y=rasterizer->y - height,
-           shape_rect.width = width;
-           shape_rect.height = height * 2;
-          switch ((int)ctx_state_get (rasterizer->state, CTX_text_align))
+      case '\n':
+        parser->col=0;
+        parser->line++;
+        break;
+      default:
+        parser->col++;
+    }
+  switch (parser->state)
+    {
+      case CTX_PARSER_NEUTRAL:
+        switch (byte)
           {
-          case CTX_TEXT_ALIGN_LEFT:
-          case CTX_TEXT_ALIGN_START:
-                  break;
-          case CTX_TEXT_ALIGN_END:
-          case CTX_TEXT_ALIGN_RIGHT:
-           shape_rect.x -= shape_rect.width;
-           break;
-          case CTX_TEXT_ALIGN_CENTER:
-           shape_rect.x -= shape_rect.width/2;
-           break;
-                   // XXX : doesn't take all text-alignments into account
+            case  0: case  1: case  2: case  3:  case 4:  case 5:
+            case  6: case  7: case  8: case 11: case 12: case 14:
+            case 15: case 16: case 17: case 18: case 19: case 20:
+            case 21: case 22: case 23: case 24: case 25: case 26:
+            case 27: case 28: case 29: case 30: case 31:
+              break;
+            case ' ': case '\t': case '\r': case '\n':
+            case ';': case ',':
+            case '(': case ')':
+            case '{': case '}':
+            case '=':
+              break;
+            case '#':
+              parser->state = CTX_PARSER_COMMENT;
+              break;
+            case '\'':
+              parser->state = CTX_PARSER_STRING_APOS;
+              parser->pos = 0;
+              parser->holding[0] = 0;
+              break;
+            case '~':
+              parser->state = CTX_PARSER_STRING_A85;
+              parser->pos = 0;
+              parser->holding[0] = 0;
+              break;
+            case '"':
+              parser->state = CTX_PARSER_STRING_QUOT;
+              parser->pos = 0;
+              parser->holding[0] = 0;
+              break;
+            case '-':
+              parser->state = CTX_PARSER_NEGATIVE_NUMBER;
+              parser->numbers[parser->n_numbers] = 0;
+              parser->decimal = 0;
+              break;
+            case '0': case '1': case '2': case '3': case '4':
+            case '5': case '6': case '7': case '8': case '9':
+              parser->state = CTX_PARSER_NUMBER;
+              parser->numbers[parser->n_numbers] = 0;
+              parser->numbers[parser->n_numbers] += (byte - '0');
+              parser->decimal = 0;
+              break;
+            case '.':
+              parser->state = CTX_PARSER_NUMBER;
+              parser->numbers[parser->n_numbers] = 0;
+              parser->decimal = 1;
+              break;
+            default:
+              parser->state = CTX_PARSER_WORD;
+              parser->pos = 0;
+              ctx_parser_holding_append (parser, byte);
+              break;
           }
-
-#if 0
-          uint32_t color;
-          ctx_color_get_rgba8 (rasterizer->state, &rasterizer->state->gstate.source_fill.color, 
(uint8_t*)(&color));
-#endif
-          ctx_sha1_process(&sha1, (const unsigned char*)ctx_arg_string(), strlen  (ctx_arg_string()));
-#if 0
-          ctx_sha1_process(&sha1, (unsigned char*)(&rasterizer->state->gstate.transform), sizeof 
(rasterizer->state->gstate.transform));
-          ctx_sha1_process(&sha1, (unsigned char*)&color, 4);
-#endif
-          ctx_sha1_process(&sha1, (unsigned char*)&shape_rect, sizeof (CtxIntRectangle));
-          ctx_sha1_done(&sha1, (unsigned char*)ctx_sha1_hash);
-          _ctx_add_hash (hasher, &shape_rect, ctx_sha1_hash);
-
-          ctx_rasterizer_rel_move_to (rasterizer, width, 0);
-        }
-        ctx_rasterizer_reset (rasterizer);
         break;
-      case CTX_STROKE_TEXT:
+      case CTX_PARSER_NUMBER:
+      case CTX_PARSER_NEGATIVE_NUMBER:
         {
-          CtxSHA1 sha1;
-          memcpy (&sha1, &hasher->sha1_stroke, sizeof (CtxSHA1));
-          char ctx_sha1_hash[20];
-          float width = ctx_text_width (rasterizer->ctx, ctx_arg_string());
-          float height = ctx_get_font_size (rasterizer->ctx);
-
-           CtxIntRectangle shape_rect = {
-              (int)rasterizer->x, (int)(rasterizer->y - height),
-              (int)width, (int)(height * 2)
-           };
-
-#if 0
-          uint32_t color;
-          ctx_color_get_rgba8 (rasterizer->state, &rasterizer->state->gstate.source_stroke.color, 
(uint8_t*)(&color));
-#endif
-          ctx_sha1_process(&sha1, (unsigned char*)ctx_arg_string(), strlen  (ctx_arg_string()));
-#if 0
-          ctx_sha1_process(&sha1, (unsigned char*)(&rasterizer->state->gstate.transform), sizeof 
(rasterizer->state->gstate.transform));
-          ctx_sha1_process(&sha1, (unsigned char*)&color, 4);
-#endif
-          ctx_sha1_process(&sha1, (unsigned char*)&shape_rect, sizeof (CtxIntRectangle));
-          ctx_sha1_done(&sha1, (unsigned char*)ctx_sha1_hash);
-          _ctx_add_hash (hasher, &shape_rect, ctx_sha1_hash);
+          switch (byte)
+            {
+              case 0: case 1: case 2: case 3: case 4: case 5:
+              case 6: case 7: case 8:
+              case 11: case 12: case 14: case 15: case 16:
+              case 17: case 18: case 19: case 20: case 21:
+              case 22: case 23: case 24: case 25: case 26:
+              case 27: case 28: case 29: case 30: case 31:
+                parser->state = CTX_PARSER_NEUTRAL;
+                break;
+              case ' ':
+              case '\t':
+              case '\r':
+              case '\n':
+              case ';':
+              case ',':
+              case '(':
+              case ')':
+              case '{':
+              case '}':
+              case '=':
+                if (parser->state == CTX_PARSER_NEGATIVE_NUMBER)
+                  { parser->numbers[parser->n_numbers] *= -1; }
+                parser->state = CTX_PARSER_NEUTRAL;
+                break;
+              case '#':
+                parser->state = CTX_PARSER_COMMENT;
+                break;
+              case '-':
+                if (parser->state == CTX_PARSER_NEGATIVE_NUMBER)
+                  { parser->numbers[parser->n_numbers] *= -1; }
+                parser->state = CTX_PARSER_NEGATIVE_NUMBER;
+                parser->numbers[parser->n_numbers+1] = 0;
+                parser->n_numbers ++;
+                parser->decimal = 0;
+                break;
+              case '.':
+                //if (parser->decimal) // TODO permit .13.32.43 to equivalent to .12 .32 .43
+                parser->decimal = 1;
+                break;
+              case '0': case '1': case '2': case '3': case '4':
+              case '5': case '6': case '7': case '8': case '9':
+                if (parser->decimal)
+                  {
+                    parser->decimal *= 10;
+                    parser->numbers[parser->n_numbers] += (byte - '0') / (1.0 * parser->decimal);
+                  }
+                else
+                  {
+                    parser->numbers[parser->n_numbers] *= 10;
+                    parser->numbers[parser->n_numbers] += (byte - '0');
+                  }
+                break;
+              case '@': // cells
+                if (parser->state == CTX_PARSER_NEGATIVE_NUMBER)
+                  { parser->numbers[parser->n_numbers] *= -1; }
+                {
+                float fval = parser->numbers[parser->n_numbers];
+                ctx_parser_transform_cell (parser, parser->command, parser->n_numbers, &fval);
+                parser->numbers[parser->n_numbers]= fval;
+                }
+                parser->state = CTX_PARSER_NEUTRAL;
+                break;
+              case '%': // percent of width/height
+                if (parser->state == CTX_PARSER_NEGATIVE_NUMBER)
+                  { parser->numbers[parser->n_numbers] *= -1; }
+                {
+                float fval = parser->numbers[parser->n_numbers];
+                ctx_parser_transform_percent (parser, parser->command, parser->n_numbers, &fval);
+                parser->numbers[parser->n_numbers]= fval;
+                }
+                parser->state = CTX_PARSER_NEUTRAL;
+                break;
+              case '^': // percent of height
+                if (parser->state == CTX_PARSER_NEGATIVE_NUMBER)
+                  { parser->numbers[parser->n_numbers] *= -1; }
+                {
+                float fval = parser->numbers[parser->n_numbers];
+                ctx_parser_transform_percent_height (parser, parser->command, parser->n_numbers, &fval);
+                parser->numbers[parser->n_numbers]= fval;
+                }
+                parser->state = CTX_PARSER_NEUTRAL;
+                break;
+              case '~': // percent of width
+                if (parser->state == CTX_PARSER_NEGATIVE_NUMBER)
+                  { parser->numbers[parser->n_numbers] *= -1; }
+                {
+                float fval = parser->numbers[parser->n_numbers];
+                ctx_parser_transform_percent_width (parser, parser->command, parser->n_numbers, &fval);
+                parser->numbers[parser->n_numbers]= fval;
+                }
+                parser->state = CTX_PARSER_NEUTRAL;
+                break;
+              default:
+                if (parser->state == CTX_PARSER_NEGATIVE_NUMBER)
+                  { parser->numbers[parser->n_numbers] *= -1; }
+                parser->state = CTX_PARSER_WORD;
+                parser->pos = 0;
+                ctx_parser_holding_append (parser, byte);
+                break;
+            }
+          if ( (parser->state != CTX_PARSER_NUMBER) &&
+               (parser->state != CTX_PARSER_NEGATIVE_NUMBER))
+            {
+              parser->n_numbers ++;
+              ctx_parser_number_done (parser);
 
-          ctx_rasterizer_rel_move_to (rasterizer, width, 0);
+              if (parser->n_numbers == parser->expected_args ||
+                  parser->expected_args == CTX_ARG_COLLECT_NUMBERS ||
+                  parser->expected_args == CTX_ARG_STRING_OR_NUMBER)
+                {
+                  int tmp1 = parser->n_numbers;
+                  int tmp2 = parser->n_args;
+                  CtxCode tmp3 = parser->command;
+                  int tmp4 = parser->expected_args;
+                  ctx_parser_dispatch_command (parser);
+                  parser->command = tmp3;
+                  switch (parser->command)
+                  {
+                    case CTX_DEFINE_TEXTURE:
+                    case CTX_TEXTURE:
+                      parser->n_numbers = tmp1;
+                      parser->n_args = tmp2;
+                      break;
+                          default:
+                      parser->n_numbers = 0;
+                      parser->n_args = 0;
+                      break;
+                  }
+                  parser->expected_args = tmp4;
+                }
+              if (parser->n_numbers > CTX_PARSER_MAX_ARGS)
+                { parser->n_numbers = CTX_PARSER_MAX_ARGS;
+                }
+            }
         }
-        ctx_rasterizer_reset (rasterizer);
         break;
-      case CTX_GLYPH:
-         {
-          CtxSHA1 sha1;
-          memcpy (&sha1, &hasher->sha1_fill, sizeof (CtxSHA1));
+      case CTX_PARSER_WORD:
+        switch (byte)
+          {
+            case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7:
+            case 8: case 11: case 12: case 14: case 15: case 16: case 17:
+            case 18: case 19: case 20: case 21: case 22: case 23: case 24:
+            case 25: case 26: case 27: case 28: case 29: case 30: case 31:
+            case ' ': case '\t': case '\r': case '\n':
+            case ';': case ',':
+            case '(': case ')': case '=': case '{': case '}':
+              parser->state = CTX_PARSER_NEUTRAL;
+              break;
+            case '#':
+              parser->state = CTX_PARSER_COMMENT;
+              break;
+            case '-':
+              parser->state = CTX_PARSER_NEGATIVE_NUMBER;
+              parser->numbers[parser->n_numbers] = 0;
+              parser->decimal = 0;
+              break;
+            case '0': case '1': case '2': case '3': case '4':
+            case '5': case '6': case '7': case '8': case '9':
+              parser->state = CTX_PARSER_NUMBER;
+              parser->numbers[parser->n_numbers] = 0;
+              parser->numbers[parser->n_numbers] += (byte - '0');
+              parser->decimal = 0;
+              break;
+            case '.':
+              parser->state = CTX_PARSER_NUMBER;
+              parser->numbers[parser->n_numbers] = 0;
+              parser->decimal = 1;
+              break;
+            default:
+              ctx_parser_holding_append (parser, byte);
+              break;
+          }
+        if (parser->state != CTX_PARSER_WORD)
+          {
+            ctx_parser_word_done (parser);
+          }
+        break;
+      case CTX_PARSER_STRING_A85:
+        switch (byte)
+          {
+            case '~':
+              parser->state = CTX_PARSER_NEUTRAL;
+                 //   fprintf (stderr, "got %i\n", parser->pos);
+              parser->pos = ctx_a85dec ((char*)parser->holding, (char*)parser->holding, parser->pos);
+                 //   fprintf (stderr, "dec got %i\n", parser->pos);
+              ctx_parser_string_done (parser);
+              break;
+            default:
+              ctx_parser_holding_append (parser, byte);
+              break;
+          }
+        break;
+      case CTX_PARSER_STRING_APOS:
+        switch (byte)
+          {
+            case '\\': parser->state = CTX_PARSER_STRING_APOS_ESCAPED; break;
+            case '\'': parser->state = CTX_PARSER_NEUTRAL;
+              ctx_parser_string_done (parser);
+              break;
+            default:
+              ctx_parser_holding_append (parser, byte); break;
+          }
+        break;
+      case CTX_PARSER_STRING_APOS_ESCAPED:
+        switch (byte)
+          {
+            case '0': byte = '\0'; break;
+            case 'b': byte = '\b'; break;
+            case 'f': byte = '\f'; break;
+            case 'n': byte = '\n'; break;
+            case 'r': byte = '\r'; break;
+            case 't': byte = '\t'; break;
+            case 'v': byte = '\v'; break;
+            default: break;
+          }
+        ctx_parser_holding_append (parser, byte);
+        parser->state = CTX_PARSER_STRING_APOS;
+        break;
+      case CTX_PARSER_STRING_QUOT_ESCAPED:
+        switch (byte)
+          {
+            case '0': byte = '\0'; break;
+            case 'b': byte = '\b'; break;
+            case 'f': byte = '\f'; break;
+            case 'n': byte = '\n'; break;
+            case 'r': byte = '\r'; break;
+            case 't': byte = '\t'; break;
+            case 'v': byte = '\v'; break;
+            default: break;
+          }
+        ctx_parser_holding_append (parser, byte);
+        parser->state = CTX_PARSER_STRING_QUOT;
+        break;
+      case CTX_PARSER_STRING_QUOT:
+        switch (byte)
+          {
+            case '\\':
+              parser->state = CTX_PARSER_STRING_QUOT_ESCAPED;
+              break;
+            case '"':
+              parser->state = CTX_PARSER_NEUTRAL;
+              ctx_parser_string_done (parser);
+              break;
+            default:
+              ctx_parser_holding_append (parser, byte);
+              break;
+          }
+        break;
+      case CTX_PARSER_COMMENT:
+        switch (byte)
+          {
+            case '\r':
+            case '\n':
+              parser->state = CTX_PARSER_NEUTRAL;
+            default:
+              break;
+          }
+        break;
+    }
+}
 
-          char ctx_sha1_hash[20];
-          uint8_t string[8];
-          string[ctx_unichar_to_utf8 (c->u32.a0, string)]=0;
-          float width = ctx_text_width (rasterizer->ctx, (char*)string);
-          float height = ctx_get_font_size (rasterizer->ctx);
+void ctx_parse (Ctx *ctx, const char *string)
+{
+  if (!string)
+    return;
+  CtxParser *parser = ctx_parser_new (ctx, ctx_width(ctx),
+                                           ctx_height(ctx),
+                                           ctx_get_font_size(ctx),
+                                           ctx_get_font_size(ctx),
+                                           0, 0, NULL, NULL, NULL, NULL, NULL);
+  for (int i = 0; string[i]; i++)
+     ctx_parser_feed_byte (parser, string[i]);
+  ctx_parser_free (parser);
+}
 
-          float tx = rasterizer->x;
-          float ty = rasterizer->y;
-          float tw = width;
-          float th = height * 2;
+#endif
 
-          _ctx_user_to_device (rasterizer->state, &tx, &ty);
-          _ctx_user_to_device_distance (rasterizer->state, &tw, &th);
-          CtxIntRectangle shape_rect = {(int)tx,(int)(ty-th/2),(int)tw,(int)th};
+static CtxFont ctx_fonts[CTX_MAX_FONTS];
+static int     ctx_font_count = 0;
 
+#if CTX_FONT_ENGINE_STB
+static float
+ctx_glyph_width_stb (CtxFont *font, Ctx *ctx, uint32_t unichar);
+static float
+ctx_glyph_kern_stb (CtxFont *font, Ctx *ctx, uint32_t unicharA, uint32_t unicharB);
+static int
+ctx_glyph_stb (CtxFont *font, Ctx *ctx, uint32_t unichar, int stroke);
 
-#if 0
-          uint32_t color;
-          ctx_color_get_rgba8 (rasterizer->state, &rasterizer->state->gstate.source_fill.color, 
(uint8_t*)(&color));
-#endif
-          ctx_sha1_process(&sha1, string, strlen ((const char*)string));
-#if 0
-          ctx_sha1_process(&sha1, (unsigned char*)(&rasterizer->state->gstate.transform), sizeof 
(rasterizer->state->gstate.transform));
-          ctx_sha1_process(&sha1, (unsigned char*)&color, 4);
+CtxFontEngine ctx_font_engine_stb =
+{
+#if CTX_FONTS_FROM_FILE
+  ctx_load_font_ttf_file,
 #endif
-          ctx_sha1_process(&sha1, (unsigned char*)&shape_rect, sizeof (CtxIntRectangle));
-          ctx_sha1_done(&sha1, (unsigned char*)ctx_sha1_hash);
-          _ctx_add_hash (hasher, &shape_rect, ctx_sha1_hash);
+  ctx_load_font_ttf,
+  ctx_glyph_stb,
+  ctx_glyph_width_stb,
+  ctx_glyph_kern_stb,
+};
 
-          ctx_rasterizer_rel_move_to (rasterizer, width, 0);
-          ctx_rasterizer_reset (rasterizer);
-         }
-        break;
+int
+ctx_load_font_ttf (const char *name, const void *ttf_contents, int length)
+{
+  if (ctx_font_count >= CTX_MAX_FONTS)
+    { return -1; }
+  ctx_fonts[ctx_font_count].type = 1;
+  ctx_fonts[ctx_font_count].name = (char *) malloc (strlen (name) + 1);
+  ctx_strcpy ( (char *) ctx_fonts[ctx_font_count].name, name);
+  if (!stbtt_InitFont (&ctx_fonts[ctx_font_count].stb.ttf_info, ttf_contents, 0) )
+    {
+      ctx_log ( "Font init failed\n");
+      return -1;
+    }
+  ctx_fonts[ctx_font_count].engine = &ctx_font_engine_stb;
+  ctx_font_count ++;
+  return ctx_font_count-1;
+}
 
-      case CTX_FILL:
-        {
-          CtxSHA1 sha1;
-          memcpy (&sha1, &hasher->sha1_fill, sizeof (CtxSHA1));
-          char ctx_sha1_hash[20];
+#if CTX_FONTS_FROM_FILE
+int
+ctx_load_font_ttf_file (const char *name, const char *path)
+{
+  uint8_t *contents = NULL;
+  long length = 0;
+  ctx_get_contents (path, &contents, &length);
+  if (!contents)
+    {
+      ctx_log ( "File load failed\n");
+      return -1;
+    }
+  return ctx_load_font_ttf (name, contents, length);
+}
+#endif
 
-          /* we eant this hasher to be as good as possible internally,
-           * since it is also used in the small shapes rasterization
-           * cache
-           */
-        uint64_t hash = ctx_rasterizer_poly_to_hash (rasterizer); // + hasher->salt;
-        CtxIntRectangle shape_rect = {
-          (int)(rasterizer->col_min / CTX_SUBDIV - 2),
-          (int)(rasterizer->scan_min / aa - 2),
-          (int)(3+(rasterizer->col_max - rasterizer->col_min + 1) / CTX_SUBDIV),
-          (int)(3+(rasterizer->scan_max - rasterizer->scan_min + 1) / aa)
-        };
+static int
+ctx_glyph_stb_find (CtxFont *font, uint32_t unichar)
+{
+  stbtt_fontinfo *ttf_info = &font->stb.ttf_info;
+  int index = font->stb.cache_index;
+  if (font->stb.cache_unichar == unichar)
+    {
+      return index;
+    }
+  font->stb.cache_unichar = 0;
+  index = font->stb.cache_index = stbtt_FindGlyphIndex (ttf_info, unichar);
+  font->stb.cache_unichar = unichar;
+  return index;
+}
 
-        hash ^= (rasterizer->state->gstate.fill_rule * 23);
+static float
+ctx_glyph_width_stb (CtxFont *font, Ctx *ctx, uint32_t unichar)
+{
+  stbtt_fontinfo *ttf_info = &font->stb.ttf_info;
+  float font_size          = ctx->state.gstate.font_size;
+  float scale              = stbtt_ScaleForPixelHeight (ttf_info, font_size);
+  int advance, lsb;
+  int glyph = ctx_glyph_stb_find (font, unichar);
+  if (glyph==0)
+    { return 0.0f; }
+  stbtt_GetGlyphHMetrics (ttf_info, glyph, &advance, &lsb);
+  return (advance * scale);
+}
 
-        ctx_sha1_process(&sha1, (unsigned char*)&hash, 8);
+static float
+ctx_glyph_kern_stb (CtxFont *font, Ctx *ctx, uint32_t unicharA, uint32_t unicharB)
+{
+  stbtt_fontinfo *ttf_info = &font->stb.ttf_info;
+  float font_size = ctx->state.gstate.font_size;
+  float scale = stbtt_ScaleForPixelHeight (ttf_info, font_size);
+  int glyphA = ctx_glyph_stb_find (font, unicharA);
+  int glyphB = ctx_glyph_stb_find (font, unicharB);
+  return stbtt_GetGlyphKernAdvance (ttf_info, glyphA, glyphB) * scale;
+}
 
+static int
+ctx_glyph_stb (CtxFont *font, Ctx *ctx, uint32_t unichar, int stroke)
+{
+  stbtt_fontinfo *ttf_info = &font->stb.ttf_info;
+  int glyph = ctx_glyph_stb_find (font, unichar);
+  if (glyph==0)
+    { return -1; }
+  float font_size = ctx->state.gstate.font_size;
+  int   baseline = ctx->state.y;
+  float origin_x = ctx->state.x;
+  float origin_y = baseline;
+  float scale    = stbtt_ScaleForPixelHeight (ttf_info, font_size);;
+  stbtt_vertex *vertices = NULL;
+  ctx_begin_path (ctx);
+  int num_verts = stbtt_GetGlyphShape (ttf_info, glyph, &vertices);
+  for (int i = 0; i < num_verts; i++)
+    {
+      stbtt_vertex *vertex = &vertices[i];
+      switch (vertex->type)
         {
-          int is = rasterizer->state->gstate.image_smoothing;
-          ctx_sha1_process(&sha1, (uint8_t*)&is, sizeof(int));
+          case STBTT_vmove:
+            ctx_move_to (ctx,
+                         origin_x + vertex->x * scale, origin_y - vertex->y * scale);
+            break;
+          case STBTT_vline:
+            ctx_line_to (ctx,
+                         origin_x + vertex->x * scale, origin_y - vertex->y * scale);
+            break;
+          case STBTT_vcubic:
+            ctx_curve_to (ctx,
+                          origin_x + vertex->cx  * scale, origin_y - vertex->cy  * scale,
+                          origin_x + vertex->cx1 * scale, origin_y - vertex->cy1 * scale,
+                          origin_x + vertex->x   * scale, origin_y - vertex->y   * scale);
+            break;
+          case STBTT_vcurve:
+            ctx_quad_to (ctx,
+                         origin_x + vertex->cx  * scale, origin_y - vertex->cy  * scale,
+                         origin_x + vertex->x   * scale, origin_y - vertex->y   * scale);
+            break;
         }
+    }
+  stbtt_FreeShape (ttf_info, vertices);
+  if (stroke)
+    {
+      ctx_stroke (ctx);
+    }
+  else
+    { ctx_fill (ctx); }
+  return 0;
+}
+#endif
 
-          ctx_sha1_done(&sha1, (unsigned char*)ctx_sha1_hash);
-          _ctx_add_hash (hasher, &shape_rect, ctx_sha1_hash);
+#if CTX_FONT_ENGINE_CTX
 
-        if (!rasterizer->preserve)
-          ctx_rasterizer_reset (rasterizer);
-        rasterizer->preserve = 0;
-        }
-        break;
-      case CTX_STROKE:
-        {
-          CtxSHA1 sha1;
-          memcpy (&sha1, &hasher->sha1_stroke, sizeof (CtxSHA1));
-          char ctx_sha1_hash[20];
-        uint64_t hash = ctx_rasterizer_poly_to_hash (rasterizer);
-        CtxIntRectangle shape_rect = {
-          (int)(rasterizer->col_min / CTX_SUBDIV - rasterizer->state->gstate.line_width),
-          (int)(rasterizer->scan_min / aa - rasterizer->state->gstate.line_width),
-          (int)((rasterizer->col_max - rasterizer->col_min + 1) / CTX_SUBDIV + 
rasterizer->state->gstate.line_width),
-          (int)((rasterizer->scan_max - rasterizer->scan_min + 1) / aa + 
rasterizer->state->gstate.line_width)
-        };
+/* XXX: todo remove this, and rely on a binary search instead
+ */
+static int ctx_font_find_glyph_cached (CtxFont *font, uint32_t glyph)
+{
+  for (int i = 0; i < font->ctx.glyphs; i++)
+    {
+      if (font->ctx.index[i * 2] == glyph)
+        { return font->ctx.index[i * 2 + 1]; }
+    }
+  return -1;
+}
 
-        shape_rect.width += rasterizer->state->gstate.line_width * 2;
-        shape_rect.height += rasterizer->state->gstate.line_width * 2;
-        shape_rect.x -= rasterizer->state->gstate.line_width;
-        shape_rect.y -= rasterizer->state->gstate.line_width;
+static int ctx_glyph_find_ctx (CtxFont *font, Ctx *ctx, uint32_t unichar)
+{
+  int ret = ctx_font_find_glyph_cached (font, unichar);
+  if (ret >= 0) return ret;
 
-        hash ^= (int)(rasterizer->state->gstate.line_width * 110);
-        hash ^= (rasterizer->state->gstate.line_cap * 23);
-        hash ^= (rasterizer->state->gstate.source_stroke.type * 117);
+  for (int i = 0; i < font->ctx.length; i++)
+  {
+    CtxEntry *entry = (CtxEntry *) &font->ctx.data[i];
+    if (entry->code == CTX_DEFINE_GLYPH &&
+        entry->data.u32[0] == unichar)
+    {
+       return i;
+       // XXX this could be prone to insertion of valid header
+       // data in included bitmaps.. is that an issue?
+       //   
+    }
+  }
+  return -1;
+}
 
-        ctx_sha1_process(&sha1, (unsigned char*)&hash, 8);
-
-        uint32_t color;
-        ctx_color_get_rgba8 (rasterizer->state, &rasterizer->state->gstate.source_stroke.color, 
(uint8_t*)(&color));
 
-          ctx_sha1_process(&sha1, (unsigned char*)&color, 4);
-
-          ctx_sha1_done(&sha1, (unsigned char*)ctx_sha1_hash);
-          _ctx_add_hash (hasher, &shape_rect, ctx_sha1_hash);
+static float
+ctx_glyph_kern_ctx (CtxFont *font, Ctx *ctx, uint32_t unicharA, uint32_t unicharB)
+{
+  float font_size = ctx->state.gstate.font_size;
+  int first_kern = ctx_glyph_find_ctx (font, ctx, unicharA);
+  if (first_kern < 0) return 0.0;
+  for (int i = first_kern + 1; i < font->ctx.length; i++)
+    {
+      CtxEntry *entry = (CtxEntry *) &font->ctx.data[i];
+      if (entry->code == CTX_KERNING_PAIR)
+        {
+          if (entry->data.u16[0] == unicharA && entry->data.u16[1] == unicharB)
+            { return entry->data.s32[1] / 255.0 * font_size / CTX_BAKE_FONT_SIZE; }
         }
-        if (!rasterizer->preserve)
-          ctx_rasterizer_reset (rasterizer);
-        rasterizer->preserve = 0;
-        break;
-        /* the above cases are the painting cases and 
-         * the only ones differing from the rasterizer's process switch
-         */
+      if (entry->code == CTX_DEFINE_GLYPH)
+        return 0.0;
+    }
+  return 0.0;
+}
+#if 0
+static int ctx_glyph_find (Ctx *ctx, CtxFont *font, uint32_t unichar)
+{
+  for (int i = 0; i < font->ctx.length; i++)
+    {
+      CtxEntry *entry = (CtxEntry *) &font->ctx.data[i];
+      if (entry->code == CTX_DEFINE_GLYPH && entry->data.u32[0] == unichar)
+        { return i; }
+    }
+  return 0;
+}
+#endif
 
-      case CTX_LINE_TO:
-        ctx_rasterizer_line_to (rasterizer, c->c.x0, c->c.y0);
-        break;
-      case CTX_REL_LINE_TO:
-        ctx_rasterizer_rel_line_to (rasterizer, c->c.x0, c->c.y0);
-        break;
-      case CTX_MOVE_TO:
-        ctx_rasterizer_move_to (rasterizer, c->c.x0, c->c.y0);
-        break;
-      case CTX_REL_MOVE_TO:
-        ctx_rasterizer_rel_move_to (rasterizer, c->c.x0, c->c.y0);
-        break;
-      case CTX_CURVE_TO:
-        ctx_rasterizer_curve_to (rasterizer, c->c.x0, c->c.y0,
-                                 c->c.x1, c->c.y1,
-                                 c->c.x2, c->c.y2);
-        break;
-      case CTX_REL_CURVE_TO:
-        ctx_rasterizer_rel_curve_to (rasterizer, c->c.x0, c->c.y0,
-                                     c->c.x1, c->c.y1,
-                                     c->c.x2, c->c.y2);
-        break;
-      case CTX_QUAD_TO:
-        ctx_rasterizer_quad_to (rasterizer, c->c.x0, c->c.y0, c->c.x1, c->c.y1);
-        break;
-      case CTX_REL_QUAD_TO:
-        ctx_rasterizer_rel_quad_to (rasterizer, c->c.x0, c->c.y0, c->c.x1, c->c.y1);
-        break;
-      case CTX_ARC:
-        ctx_rasterizer_arc (rasterizer, c->arc.x, c->arc.y, c->arc.radius, c->arc.angle1, c->arc.angle2, 
c->arc.direction);
-        break;
-      case CTX_RECTANGLE:
-        ctx_rasterizer_rectangle (rasterizer, c->rectangle.x, c->rectangle.y,
-                                  c->rectangle.width, c->rectangle.height);
-        break;
-      case CTX_ROUND_RECTANGLE:
-        ctx_rasterizer_round_rectangle (rasterizer, c->rectangle.x, c->rectangle.y,
-                                        c->rectangle.width, c->rectangle.height,
-                                        c->rectangle.radius);
-        break;
-      case CTX_SET_PIXEL:
-        ctx_rasterizer_set_pixel (rasterizer, c->set_pixel.x, c->set_pixel.y,
-                                  c->set_pixel.rgba[0],
-                                  c->set_pixel.rgba[1],
-                                  c->set_pixel.rgba[2],
-                                  c->set_pixel.rgba[3]);
-        break;
-      case CTX_PRESERVE:
-        rasterizer->preserve = 1;
-        break;
-      case CTX_ROTATE:
-      case CTX_SCALE:
-      case CTX_TRANSLATE:
-      case CTX_SAVE:
-      case CTX_RESTORE:
-        rasterizer->uses_transforms = 1;
-        ctx_interpret_transforms (rasterizer->state, entry, NULL);
 
-        
-        break;
-      case CTX_FONT:
-        ctx_rasterizer_set_font (rasterizer, ctx_arg_string() );
-        break;
-      case CTX_BEGIN_PATH:
-        ctx_rasterizer_reset (rasterizer);
-        break;
-      case CTX_CLIP:
-        // should perhaps modify a global state to include
-        // in hash?
-        ctx_rasterizer_clip (rasterizer);
-        break;
-      case CTX_CLOSE_PATH:
-        ctx_rasterizer_finish_shape (rasterizer);
-        break;
-      case CTX_DEFINE_TEXTURE:
-        {
-        ctx_sha1_init (&hasher->sha1_fill);
-        ctx_sha1_process (&hasher->sha1_fill, (uint8_t*)c->define_texture.eid, strlen 
(c->define_texture.eid));
-        ctx_sha1_process(&hasher->sha1_fill, (unsigned char*)(&rasterizer->state->gstate.transform), sizeof 
(rasterizer->state->gstate.transform));
+static float
+ctx_glyph_width_ctx (CtxFont *font, Ctx *ctx, uint32_t unichar)
+{
+  CtxState *state = &ctx->state;
+  float font_size = state->gstate.font_size;
+  int   start     = ctx_glyph_find_ctx (font, ctx, unichar);
+  if (start < 0)
+    { return 0.0; }  // XXX : fallback
+  for (int i = start; i < font->ctx.length; i++)
+    {
+      CtxEntry *entry = (CtxEntry *) &font->ctx.data[i];
+      if (entry->code == CTX_DEFINE_GLYPH)
+        if (entry->data.u32[0] == (unsigned) unichar)
+          { return (entry->data.u32[1] / 255.0 * font_size / CTX_BAKE_FONT_SIZE); }
+    }
+  return 0.0;
+}
 
-        rasterizer->comp_op = NULL; // why?
-        }
-        break;
-      case CTX_TEXTURE:
-        ctx_sha1_init (&hasher->sha1_fill);
-        ctx_sha1_process (&hasher->sha1_fill, (uint8_t*)c->texture.eid, strlen (c->texture.eid));
-        ctx_sha1_process (&hasher->sha1_fill, (uint8_t*)(&rasterizer->state->gstate.transform), sizeof 
(rasterizer->state->gstate.transform));
-        rasterizer->comp_op = NULL; // why?
-        break;
-      case CTX_COLOR:
+static int
+ctx_glyph_drawlist (CtxFont *font, Ctx *ctx, CtxDrawlist *drawlist, uint32_t unichar, int stroke)
+{
+  CtxState *state = &ctx->state;
+  CtxIterator iterator;
+  float origin_x = state->x;
+  float origin_y = state->y;
+  ctx_current_point (ctx, &origin_x, &origin_y);
+  int in_glyph = 0;
+  float font_size = state->gstate.font_size;
+  int start = 0;
+  if (font->type == 0)
+  {
+  start = ctx_glyph_find_ctx (font, ctx, unichar);
+  if (start < 0)
+    { return -1; }  // XXX : fallback glyph
+  }
+  ctx_iterator_init (&iterator, drawlist, start, CTX_ITERATOR_EXPAND_BITPACK);
+  CtxCommand *command;
+
+  /* XXX :  do a binary search instead of a linear search */
+  while ( (command= ctx_iterator_next (&iterator) ) )
+    {
+      CtxEntry *entry = &command->entry;
+      if (in_glyph)
         {
-          uint32_t color;
-          if (((int)(ctx_arg_float(0))&512))
-          {
-            ctx_color_get_rgba8 (rasterizer->state, &rasterizer->state->gstate.source_stroke.color, 
(uint8_t*)(&color));
-            ctx_sha1_init (&hasher->sha1_stroke);
-            ctx_sha1_process(&hasher->sha1_stroke, (unsigned char*)&color, 4);
-          }
-          else
-          {
-            ctx_color_get_rgba8 (rasterizer->state, &rasterizer->state->gstate.source_fill.color, 
(uint8_t*)(&color));
-            ctx_sha1_init (&hasher->sha1_fill);
-            ctx_sha1_process(&hasher->sha1_fill, (unsigned char*)&color, 4);
-          }
+          if (entry->code == CTX_DEFINE_GLYPH)
+            {
+              if (stroke)
+                { ctx_stroke (ctx); }
+              else
+                {
+#if CTX_RASTERIZER
+#if CTX_ENABLE_SHADOW_BLUR
+      if (ctx->renderer && ((CtxRasterizer*)(ctx->renderer))->in_shadow)
+      {
+        ctx_rasterizer_shadow_fill ((CtxRasterizer*)ctx->renderer);
+        ((CtxRasterizer*)(ctx->renderer))->in_shadow = 1;
+      }
+      else
+#endif
+#endif
+         ctx_fill (ctx); 
+               
+                }
+              ctx_restore (ctx);
+              return 0;
+            }
+          ctx_process (ctx, entry);
         }
-        break;
-      case CTX_LINEAR_GRADIENT:
-          ctx_sha1_init (&hasher->sha1_fill);
-          ctx_sha1_process(&hasher->sha1_fill, 
-                           (uint8_t*)c, sizeof (c->linear_gradient));
-          ctx_sha1_process (&hasher->sha1_fill, (unsigned char*)(&rasterizer->state->gstate.transform), 
sizeof (rasterizer->state->gstate.transform));
-        break;
-      case CTX_RADIAL_GRADIENT:
-          ctx_sha1_init (&hasher->sha1_fill);
-          ctx_sha1_process(&hasher->sha1_fill, 
-                           (uint8_t*)c, sizeof (c->radial_gradient));
-          ctx_sha1_process (&hasher->sha1_fill, (unsigned char*)(&rasterizer->state->gstate.transform), 
sizeof (rasterizer->state->gstate.transform));
-        //ctx_state_gradient_clear_stops (rasterizer->state);
-        break;
-#if CTX_GRADIENTS
-      case CTX_GRADIENT_STOP:
+      else if (entry->code == CTX_DEFINE_GLYPH && entry->data.u32[0] == unichar)
         {
-          float rgba[4]= {ctx_u8_to_float (ctx_arg_u8 (4) ),
-                          ctx_u8_to_float (ctx_arg_u8 (4+1) ),
-                          ctx_u8_to_float (ctx_arg_u8 (4+2) ),
-                          ctx_u8_to_float (ctx_arg_u8 (4+3) )
-                         };
-          ctx_sha1_process(&hasher->sha1_fill, 
-                           (uint8_t*) &rgba[0], sizeof(rgba));
+          in_glyph = 1;
+          ctx_save (ctx);
+          ctx_translate (ctx, origin_x, origin_y);
+          ctx_move_to (ctx, 0, 0);
+          ctx_begin_path (ctx);
+          ctx_scale (ctx, font_size / CTX_BAKE_FONT_SIZE,
+                     font_size / CTX_BAKE_FONT_SIZE);
         }
-        break;
-#endif
     }
-  if (command->code == CTX_LINE_WIDTH)
-    {
-      float x = state->gstate.line_width;
-      /* normalize line width according to scaling factor
-       */
-      x = x * ctx_maxf (ctx_maxf (ctx_fabsf (state->gstate.transform.m[0][0]),
-                                  ctx_fabsf (state->gstate.transform.m[0][1]) ),
-                        ctx_maxf (ctx_fabsf (state->gstate.transform.m[1][0]),
-                                  ctx_fabsf (state->gstate.transform.m[1][1]) ) );
-      state->gstate.line_width = x;
+  if (stroke)
+    { ctx_stroke (ctx);
     }
+  else
+    { 
+    
+#if CTX_RASTERIZER
+#if CTX_ENABLE_SHADOW_BLUR
+      if (ctx->renderer && ((CtxRasterizer*)(ctx->renderer))->in_shadow)
+      {
+        ctx_rasterizer_shadow_fill ((CtxRasterizer*)ctx->renderer);
+        ((CtxRasterizer*)(ctx->renderer))->in_shadow = 1;
+      }
+      else
+#endif
+#endif
+      {
+         ctx_fill (ctx); 
+      }
+    }
+  ctx_restore (ctx);
+  return -1;
 }
 
-static CtxRasterizer *
-ctx_hasher_init (CtxRasterizer *rasterizer, Ctx *ctx, CtxState *state, int width, int height, int cols, int 
rows)
+static int
+ctx_glyph_ctx (CtxFont *font, Ctx *ctx, uint32_t unichar, int stroke)
 {
-  CtxHasher *hasher = (CtxHasher*)rasterizer;
-  ctx_memset (rasterizer, 0, sizeof (CtxHasher) );
-  rasterizer->vfuncs.process = ctx_hasher_process;
-  rasterizer->vfuncs.free    = (CtxDestroyNotify)ctx_rasterizer_deinit;
-  // XXX need own destructor to not leak ->hashes
-  rasterizer->edge_list.flags |= CTX_DRAWLIST_EDGE_LIST;
-  rasterizer->state       = state;
-  rasterizer->ctx         = ctx;
-  ctx_state_init (rasterizer->state);
-  rasterizer->blit_x      = 0;
-  rasterizer->blit_y      = 0;
-  rasterizer->blit_width  = width;
-  rasterizer->blit_height = height;
-  rasterizer->state->gstate.clip_min_x  = 0;
-  rasterizer->state->gstate.clip_min_y  = 0;
-  rasterizer->state->gstate.clip_max_x  = width - 1;
-  rasterizer->state->gstate.clip_max_y  = height - 1;
-  rasterizer->scan_min    = 5000;
-  rasterizer->scan_max    = -5000;
-  //rasterizer->aa          = 15;
-
-  hasher->rows = rows;
-  hasher->cols = cols;
-
-  hasher->hashes = (uint8_t*)ctx_calloc (20, rows * cols);
-  ctx_sha1_init (&hasher->sha1_fill);
-  ctx_sha1_init (&hasher->sha1_stroke);
-
-  return rasterizer;
+  CtxDrawlist drawlist = { (CtxEntry *) font->ctx.data,
+                           font->ctx.length,
+                           font->ctx.length, 0, 0
+                         };
+  return ctx_glyph_drawlist (font, ctx, &drawlist, unichar, stroke);
 }
 
-Ctx *ctx_hasher_new (int width, int height, int cols, int rows)
-{
-  Ctx *ctx           = ctx_new ();
-  CtxState    *state = &ctx->state;
-  CtxRasterizer *rasterizer = (CtxRasterizer *) ctx_calloc (sizeof (CtxHasher), 1);
-  ctx_hasher_init (rasterizer, ctx, state, width, height, cols, rows);
-  ctx_set_renderer (ctx, (void*)rasterizer);
-  return ctx;
-}
-uint8_t *ctx_hasher_get_hash (Ctx *ctx, int col, int row)
+uint32_t ctx_glyph_no (Ctx *ctx, int no)
 {
-  CtxHasher *hasher = (CtxHasher*)ctx->renderer;
-  if (row < 0) row =0;
-  if (col < 0) col =0;
-  if (row >= hasher->rows) row = hasher->rows-1;
-  if (col >= hasher->cols) col = hasher->cols-1;
-
-  return &hasher->hashes[(row*hasher->cols+col)*20];
+  CtxFont *font = &ctx_fonts[ctx->state.gstate.font];
+  if (no < 0 || no >= font->ctx.glyphs)
+    { return 0; }
+  return font->ctx.index[no*2];
 }
 
+static void ctx_font_init_ctx (CtxFont *font)
+{
+  int glyph_count = 0;
+  for (int i = 0; i < font->ctx.length; i++)
+    {
+      CtxEntry *entry = &font->ctx.data[i];
+      if (entry->code == CTX_DEFINE_GLYPH)
+        { glyph_count ++; }
+    }
+  font->ctx.glyphs = glyph_count;
+#if CTX_DRAWLIST_STATIC
+  static uint32_t idx[512]; // one might have to adjust this for
+  // larger fonts XXX
+  // should probably be made a #define
+  font->ctx.index = &idx[0];
+#else
+  font->ctx.index = (uint32_t *) malloc (sizeof (uint32_t) * 2 * glyph_count);
 #endif
-#if CTX_EVENTS
-
-#if !__COSMOPOLITAN__
-#include <termios.h>
+  int no = 0;
+  for (int i = 0; i < font->ctx.length; i++)
+    {
+      CtxEntry *entry = &font->ctx.data[i];
+      if (entry->code == CTX_DEFINE_GLYPH)
+        {
+          font->ctx.index[no*2]   = entry->data.u32[0];
+          font->ctx.index[no*2+1] = i;
+          no++;
+        }
+    }
+}
 
-#include <fcntl.h>
-#include <sys/ioctl.h>
+int
+ctx_load_font_ctx (const char *name, const void *data, int length);
+#if CTX_FONTS_FROM_FILE
+int
+ctx_load_font_ctx_file (const char *name, const char *path);
 #endif
 
-int ctx_terminal_width (void)
+static CtxFontEngine ctx_font_engine_ctx =
 {
-  char buf[1024];
-  struct termios orig_attr;
-  struct termios raw;
-  tcgetattr (STDIN_FILENO, &orig_attr);
-  raw = orig_attr;
-  raw.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
-  raw.c_oflag &= ~(OPOST);
-  raw.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG);
-  raw.c_cc[VMIN] = 1; raw.c_cc[VTIME] = 0; /* 1 byte, no timer */
-  if (tcsetattr (STDIN_FILENO, TCSAFLUSH, &raw) < 0)
-    return 0;
-  fprintf (stderr, "\e[14t");
-  //tcflush(STDIN_FILENO, 1);
-#if __COSMOPOLITAN__
-  /// XXX ?
-#else
-  tcdrain(STDIN_FILENO);
+#if CTX_FONTS_FROM_FILE
+  ctx_load_font_ctx_file,
 #endif
-  int length = 0;
-  usleep (1000 * 60); // to account for possibly lowish latency ssh,
-                      // should be made configurable ; perhaps in
-                      // an env var
-  struct timeval tv = {0,0};
-  fd_set rfds;
-  
-  FD_ZERO(&rfds);
-  FD_SET(0, &rfds);
-  tv.tv_usec = 1000 * 5;
+  ctx_load_font_ctx,
+  ctx_glyph_ctx,
+  ctx_glyph_width_ctx,
+  ctx_glyph_kern_ctx,
+};
 
-  for (int n = 0; select(1, &rfds, NULL, NULL, &tv) && n < 20; n++)
-  {
-    length += read (STDIN_FILENO, &buf[length], 1);
-  }
-  tcsetattr (STDIN_FILENO, TCSAFLUSH, &orig_attr);
-  if (length == -1)
-  {
-    return 0;
-  }
-  char *semi = strchr (buf, ';');
-  buf[length]=0;
-  if (semi) {semi++; semi = strchr (semi, ';');}
-  if (semi)
-  {
-    return atoi(semi + 1);
-  }
-  return 0;
+int
+ctx_load_font_ctx (const char *name, const void *data, int length)
+{
+  if (length % sizeof (CtxEntry) )
+    { return -1; }
+  if (ctx_font_count >= CTX_MAX_FONTS)
+    { return -1; }
+  ctx_fonts[ctx_font_count].type = 0;
+  ctx_fonts[ctx_font_count].name = name;
+  ctx_fonts[ctx_font_count].ctx.data = (CtxEntry *) data;
+  ctx_fonts[ctx_font_count].ctx.length = length / sizeof (CtxEntry);
+  ctx_font_init_ctx (&ctx_fonts[ctx_font_count]);
+  ctx_fonts[ctx_font_count].engine = &ctx_font_engine_ctx;
+  ctx_font_count++;
+  return ctx_font_count-1;
 }
 
-int ctx_terminal_height (void)
+#if CTX_FONTS_FROM_FILE
+int
+ctx_load_font_ctx_file (const char *name, const char *path)
 {
-  char buf[1024];
-  struct termios orig_attr;
-  struct termios raw;
-  tcgetattr (STDIN_FILENO, &orig_attr);
-  raw = orig_attr;
-  raw.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
-  raw.c_oflag &= ~(OPOST);
-  raw.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG);
-  raw.c_cc[VMIN] = 1; raw.c_cc[VTIME] = 0; /* 1 byte, no timer */
-  if (tcsetattr (STDIN_FILENO, TCSAFLUSH, &raw) < 0)
-    return 0;
-  fprintf (stderr, "\e[14t");
-  //tcflush(STDIN_FILENO, 1);
-#if !__COSMOPOLITAN__
-  tcdrain(STDIN_FILENO);
-#endif
-  int length = 0;
-  usleep (1000 * 60); // to account for possibly lowish latency ssh,
-                      // should be made configurable ; perhaps in
-                      // an env var
-  struct timeval tv = {0,0};
-  fd_set rfds;
-  
-  FD_ZERO(&rfds);
-  FD_SET(0, &rfds);
-  tv.tv_usec = 1000 * 5;
-
-  for (int n = 0; select(1, &rfds, NULL, NULL, &tv) && n < 20; n++)
-  {
-    length += read (STDIN_FILENO, &buf[length], 1);
-  }
-  tcsetattr (STDIN_FILENO, TCSAFLUSH, &orig_attr);
-  if (length == -1)
-  {
-    return 0;
-  }
-  char *semi = strchr (buf, ';');
-  buf[length]=0;
-  if (semi)
-  {
-    return atoi(semi + 1);
-  }
-  return 0;
+  uint8_t *contents = NULL;
+  long length = 0;
+  ctx_get_contents (path, &contents, &length);
+  if (!contents)
+    {
+      ctx_log ( "File load failed\n");
+      return -1;
+    }
+  return ctx_load_font_ctx (name, contents, length);
 }
+#endif
+#endif
 
-int ctx_terminal_cols (void)
-{
-  struct winsize ws; 
-  if (ioctl(0,TIOCGWINSZ,&ws)!=0)
-    return 80;
-  return ws.ws_col;
-} 
+#if CTX_FONT_ENGINE_CTX_FS
 
-int ctx_terminal_rows (void)
+static float
+ctx_glyph_kern_ctx_fs (CtxFont *font, Ctx *ctx, uint32_t unicharA, uint32_t unicharB)
 {
-  struct winsize ws; 
-  if (ioctl(0,TIOCGWINSZ,&ws)!=0)
-    return 25;
-  return ws.ws_row;
+#if 0
+  float font_size = ctx->state.gstate.font_size;
+  int first_kern = ctx_glyph_find_ctx (font, ctx, unicharA);
+  if (first_kern < 0) return 0.0;
+  for (int i = first_kern + 1; i < font->ctx.length; i++)
+    {
+      CtxEntry *entry = (CtxEntry *) &font->ctx.data[i];
+      if (entry->code == CTX_KERNING_PAIR)
+        {
+          if (entry->data.u16[0] == unicharA && entry->data.u16[1] == unicharB)
+            { return entry->data.s32[1] / 255.0 * font_size / CTX_BAKE_FONT_SIZE; }
+        }
+      if (entry->code == CTX_DEFINE_GLYPH)
+        return 0.0;
+    }
+#endif
+  return 0.0;
 }
 
+static float
+ctx_glyph_width_ctx_fs (CtxFont *font, Ctx *ctx, uint32_t unichar)
+{
+  CtxState *state = &ctx->state;
+  char path[1024];
+  sprintf (path, "%s/%010p", font->ctx_fs.path, unichar);
+  uint8_t *data = NULL;
+  long int len_bytes = 0;
+  ctx_get_contents (path, &data, &len_bytes);
+  float ret = 0.0;
+  float font_size = state->gstate.font_size;
+  if (data){
+    Ctx *glyph_ctx = ctx_new ();
+    ctx_parse (glyph_ctx, data);
+    for (int i = 0; i < glyph_ctx->drawlist.count; i++)
+    {
+      CtxEntry *e = &glyph_ctx->drawlist.entries[i];
+      if (e->code == CTX_DEFINE_GLYPH)
+        ret = e->data.u32[1] / 255.0 * font_size / CTX_BAKE_FONT_SIZE;
+    }
+    free (data);
+    ctx_free (glyph_ctx);
+  }
+  return ret;
+}
 
+static int
+ctx_glyph_ctx_fs (CtxFont *font, Ctx *ctx, uint32_t unichar, int stroke)
+{
+  char path[1024];
+  sprintf (path, "file://%s/%010p", font->ctx_fs.path, unichar);
+  uint8_t *data = NULL;
+  long int len_bytes = 0;
+  ctx_get_contents (path, &data, &len_bytes);
 
+  if (data){
+    Ctx *glyph_ctx = ctx_new ();
+    ctx_parse (glyph_ctx, data);
+    int ret = ctx_glyph_drawlist (font, ctx, &(glyph_ctx->drawlist),
+                                  unichar, stroke);
+    free (data);
+    ctx_free (glyph_ctx);
+    return ret;
+  }
+  return -1;
+}
 
+int
+ctx_load_font_ctx_fs (const char *name, const void *data, int length);
 
-#define DECTCEM_CURSOR_SHOW      "\033[?25h"
-#define DECTCEM_CURSOR_HIDE      "\033[?25l"
-#define TERMINAL_MOUSE_OFF       "\033[?1000l\033[?1003l"
-#define TERMINAL_MOUSE_ON_BASIC  "\033[?1000h"
-#define TERMINAL_MOUSE_ON_DRAG   "\033[?1000h\033[?1003h" /* +ON_BASIC for wider */
-#define TERMINAL_MOUSE_ON_FULL   "\033[?1000h\033[?1004h" /* compatibility */
-#define XTERM_ALTSCREEN_ON       "\033[?47h"
-#define XTERM_ALTSCREEN_OFF      "\033[?47l"
-
-/*************************** input handling *************************/
-
-#if !__COSMOPOLITAN__
-#include <termios.h>
-#include <errno.h>
-#include <signal.h>
-#endif
-
-#define DELAY_MS  100  
-
-#ifndef MIN
-#define MIN(a,b) (((a)<(b))?(a):(b))
+static CtxFontEngine ctx_font_engine_ctx_fs =
+{
+#if CTX_FONTS_FROM_FILE
+  NULL,
 #endif
+  ctx_load_font_ctx_fs,
+  ctx_glyph_ctx_fs,
+  ctx_glyph_width_ctx_fs,
+  ctx_glyph_kern_ctx_fs,
+};
 
-static int  size_changed = 0;       /* XXX: global state */
-static int  signal_installed = 0;   /* XXX: global state */
-
-static const char *mouse_modes[]=
-{TERMINAL_MOUSE_OFF,
- TERMINAL_MOUSE_ON_BASIC,
- TERMINAL_MOUSE_ON_DRAG,
- TERMINAL_MOUSE_ON_FULL,
- NULL};
-
-/* note that a nick can have multiple occurences, the labels
- * should be kept the same for all occurences of a combination. */
-typedef struct NcKeyCode {
-  const char *nick;          /* programmers name for key (combo) */
-  const char *label;         /* utf8 label for key */
-  const char  sequence[10];  /* terminal sequence */
-} NcKeyCode;
-static const NcKeyCode keycodes[]={  
-
-  {"up",                  "↑",     "\033[A"},
-  {"down",                "↓",     "\033[B"},
-  {"right",               "→",     "\033[C"},
-  {"left",                "←",     "\033[D"},
-
-  {"shift-up",            "⇧↑",    "\033[1;2A"},
-  {"shift-down",          "⇧↓",    "\033[1;2B"},
-  {"shift-right",         "⇧→",    "\033[1;2C"},
-  {"shift-left",          "⇧←",    "\033[1;2D"},
-
-  {"alt-up",              "^↑",    "\033[1;3A"},
-  {"alt-down",            "^↓",    "\033[1;3B"},
-  {"alt-right",           "^→",    "\033[1;3C"},
-  {"alt-left",            "^←",    "\033[1;3D"},
-
-  {"alt-shift-up",        "alt-s↑", "\033[1;4A"},
-  {"alt-shift-down",      "alt-s↓", "\033[1;4B"},
-  {"alt-shift-right",     "alt-s→", "\033[1;4C"},
-  {"alt-shift-left",      "alt-s←", "\033[1;4D"},
-
-  {"control-up",          "^↑",    "\033[1;5A"},
-  {"control-down",        "^↓",    "\033[1;5B"},
-  {"control-right",       "^→",    "\033[1;5C"},
-  {"control-left",        "^←",    "\033[1;5D"},
-
-  /* putty */
-  {"control-up",          "^↑",    "\033OA"},
-  {"control-down",        "^↓",    "\033OB"},
-  {"control-right",       "^→",    "\033OC"},
-  {"control-left",        "^←",    "\033OD"},
-
-  {"control-shift-up",    "^⇧↑",   "\033[1;6A"},
-  {"control-shift-down",  "^⇧↓",   "\033[1;6B"},
-  {"control-shift-right", "^⇧→",   "\033[1;6C"},
-  {"control-shift-left",  "^⇧←",   "\033[1;6D"},
-
-  {"control-up",          "^↑",    "\033Oa"},
-  {"control-down",        "^↓",    "\033Ob"},
-  {"control-right",       "^→",    "\033Oc"},
-  {"control-left",        "^←",    "\033Od"},
-
-  {"shift-up",            "⇧↑",    "\033[a"},
-  {"shift-down",          "⇧↓",    "\033[b"},
-  {"shift-right",         "⇧→",    "\033[c"},
-  {"shift-left",          "⇧←",    "\033[d"},
-
-  {"insert",              "ins",   "\033[2~"},
-  {"delete",              "del",   "\033[3~"},
-  {"page-up",             "PgUp",  "\033[5~"},
-  {"page-down",           "PdDn",  "\033[6~"},
-  {"home",                "Home",  "\033OH"},
-  {"end",                 "End",   "\033OF"},
-  {"home",                "Home",  "\033[H"},
-  {"end",                 "End",   "\033[F"},
-  {"control-delete",      "^del",  "\033[3;5~"},
-  {"shift-delete",        "⇧del",  "\033[3;2~"},
-  {"control-shift-delete","^⇧del", "\033[3;6~"},
+int
+ctx_load_font_ctx_fs (const char *name, const void *path, int length) // length is ignored
+{
+  if (ctx_font_count >= CTX_MAX_FONTS)
+    { return -1; }
 
-  {"F1",        "F1",  "\033[11~"},
-  {"F2",        "F2",  "\033[12~"},
-  {"F3",        "F3",  "\033[13~"},
-  {"F4",        "F4",  "\033[14~"},
-  {"F1",        "F1",  "\033OP"},
-  {"F2",        "F2",  "\033OQ"},
-  {"F3",        "F3",  "\033OR"},
-  {"F4",        "F4",  "\033OS"},
-  {"F5",        "F5",  "\033[15~"},
-  {"F6",        "F6",  "\033[16~"},
-  {"F7",        "F7",  "\033[17~"},
-  {"F8",        "F8",  "\033[18~"},
-  {"F9",        "F9",  "\033[19~"},
-  {"F9",        "F9",  "\033[20~"},
-  {"F10",       "F10", "\033[21~"},
-  {"F11",       "F11", "\033[22~"},
-  {"F12",       "F12", "\033[23~"},
-  {"tab",       "↹",     {9, '\0'}},
-  {"shift-tab", "shift+↹",  "\033[Z"},
-  {"backspace", "⌫",  {127, '\0'}},
-  {"space",     "␣",   " "},
-  {"esc",        "␛",  "\033"},
-  {"return",    "⏎",  {10,0}},
-  {"return",    "⏎",  {13,0}},
-  /* this section could be autogenerated by code */
-  {"control-a", "^A",  {1,0}},
-  {"control-b", "^B",  {2,0}},
-  {"control-c", "^C",  {3,0}},
-  {"control-d", "^D",  {4,0}},
-  {"control-e", "^E",  {5,0}},
-  {"control-f", "^F",  {6,0}},
-  {"control-g", "^G",  {7,0}},
-  {"control-h", "^H",  {8,0}}, /* backspace? */
-  {"control-i", "^I",  {9,0}}, /* tab */
-  {"control-j", "^J",  {10,0}},
-  {"control-k", "^K",  {11,0}},
-  {"control-l", "^L",  {12,0}},
-  {"control-n", "^N",  {14,0}},
-  {"control-o", "^O",  {15,0}},
-  {"control-p", "^P",  {16,0}},
-  {"control-q", "^Q",  {17,0}},
-  {"control-r", "^R",  {18,0}},
-  {"control-s", "^S",  {19,0}},
-  {"control-t", "^T",  {20,0}},
-  {"control-u", "^U",  {21,0}},
-  {"control-v", "^V",  {22,0}},
-  {"control-w", "^W",  {23,0}},
-  {"control-x", "^X",  {24,0}},
-  {"control-y", "^Y",  {25,0}},
-  {"control-z", "^Z",  {26,0}},
-  {"alt-0",     "%0",  "\0330"},
-  {"alt-1",     "%1",  "\0331"},
-  {"alt-2",     "%2",  "\0332"},
-  {"alt-3",     "%3",  "\0333"},
-  {"alt-4",     "%4",  "\0334"},
-  {"alt-5",     "%5",  "\0335"},
-  {"alt-6",     "%6",  "\0336"},
-  {"alt-7",     "%7",  "\0337"}, /* backspace? */
-  {"alt-8",     "%8",  "\0338"},
-  {"alt-9",     "%9",  "\0339"},
-  {"alt-+",     "%+",  "\033+"},
-  {"alt--",     "%-",  "\033-"},
-  {"alt-/",     "%/",  "\033/"},
-  {"alt-a",     "%A",  "\033a"},
-  {"alt-b",     "%B",  "\033b"},
-  {"alt-c",     "%C",  "\033c"},
-  {"alt-d",     "%D",  "\033d"},
-  {"alt-e",     "%E",  "\033e"},
-  {"alt-f",     "%F",  "\033f"},
-  {"alt-g",     "%G",  "\033g"},
-  {"alt-h",     "%H",  "\033h"}, /* backspace? */
-  {"alt-i",     "%I",  "\033i"},
-  {"alt-j",     "%J",  "\033j"},
-  {"alt-k",     "%K",  "\033k"},
-  {"alt-l",     "%L",  "\033l"},
-  {"alt-n",     "%N",  "\033m"},
-  {"alt-n",     "%N",  "\033n"},
-  {"alt-o",     "%O",  "\033o"},
-  {"alt-p",     "%P",  "\033p"},
-  {"alt-q",     "%Q",  "\033q"},
-  {"alt-r",     "%R",  "\033r"},
-  {"alt-s",     "%S",  "\033s"},
-  {"alt-t",     "%T",  "\033t"},
-  {"alt-u",     "%U",  "\033u"},
-  {"alt-v",     "%V",  "\033v"},
-  {"alt-w",     "%W",  "\033w"},
-  {"alt-x",     "%X",  "\033x"},
-  {"alt-y",     "%Y",  "\033y"},
-  {"alt-z",     "%Z",  "\033z"},
-  {"shift-tab", "shift-↹", {27, 9, 0}},
-  /* Linux Console  */
-  {"home",      "Home", "\033[1~"},
-  {"end",       "End",  "\033[4~"},
-  {"F1",        "F1",   "\033[[A"},
-  {"F2",        "F2",   "\033[[B"},
-  {"F3",        "F3",   "\033[[C"},
-  {"F4",        "F4",   "\033[[D"},
-  {"F5",        "F5",   "\033[[E"},
-  {"F6",        "F6",   "\033[[F"},
-  {"F7",        "F7",   "\033[[G"},
-  {"F8",        "F8",   "\033[[H"},
-  {"F9",        "F9",   "\033[[I"},
-  {"F10",       "F10",  "\033[[J"},
-  {"F11",       "F11",  "\033[[K"},
-  {"F12",       "F12",  "\033[[L"}, 
-  {"ok",        "",     "\033[0n"},
-  {NULL, }
-};
+  ctx_fonts[ctx_font_count].type = 42;
+  ctx_fonts[ctx_font_count].name = name;
+  ctx_fonts[ctx_font_count].ctx_fs.path = strdup (path);
+  int path_len = strlen (path);
+  if (ctx_fonts[ctx_font_count].ctx_fs.path[path_len-1] == '/')
+   ctx_fonts[ctx_font_count].ctx_fs.path[path_len-1] = 0;
+  ctx_fonts[ctx_font_count].engine = &ctx_font_engine_ctx_fs;
+  ctx_font_count++;
+  return ctx_font_count-1;
+}
 
-static struct termios orig_attr;    /* in order to restore at exit */
-static int    nc_is_raw = 0;
-static int    atexit_registered = 0;
-static int    mouse_mode = NC_MOUSE_NONE;
+#endif
 
-static void _nc_noraw (void)
+int
+_ctx_glyph (Ctx *ctx, uint32_t unichar, int stroke)
 {
-  if (nc_is_raw && tcsetattr (STDIN_FILENO, TCSAFLUSH, &orig_attr) != -1)
-    nc_is_raw = 0;
+  CtxFont *font = &ctx_fonts[ctx->state.gstate.font];
+  // a begin-path here did not remove stray spikes in terminal
+  return font->engine->glyph (font, ctx, unichar, stroke);
 }
 
-void
-nc_at_exit (void)
+int
+ctx_glyph (Ctx *ctx, uint32_t unichar, int stroke)
 {
-  printf (TERMINAL_MOUSE_OFF);
-  printf (XTERM_ALTSCREEN_OFF);
-  _nc_noraw();
-  fprintf (stdout, "\e[?25h");
-  //if (ctx_native_events)
-  fprintf (stdout, "\e[?201l");
-  fprintf (stdout, "\e[?1049l");
+#if CTX_BACKEND_TEXT
+  CtxEntry commands[3]; // 3 to silence incorrect warning from static analysis
+  ctx_memset (commands, 0, sizeof (commands) );
+  commands[0] = ctx_u32 (CTX_GLYPH, unichar, 0);
+  commands[0].data.u8[4] = stroke;
+  ctx_process (ctx, commands);
+  return 0; // XXX is return value used?
+#else
+  return _ctx_glyph (ctx, unichar, stroke);
+#endif
 }
 
-static const char *mouse_get_event_int (Ctx *n, int *x, int *y)
+float
+ctx_glyph_width (Ctx *ctx, int unichar)
 {
-  static int prev_state = 0;
-  const char *ret = "mouse-motion";
-  float relx, rely;
-  signed char buf[3];
-  read (n->mouse_fd, buf, 3);
-  relx = buf[1];
-  rely = -buf[2];
-
-  n->mouse_x += relx * 0.1;
-  n->mouse_y += rely * 0.1;
+  CtxFont *font = &ctx_fonts[ctx->state.gstate.font];
 
-  if (n->mouse_x < 1) n->mouse_x = 1;
-  if (n->mouse_y < 1) n->mouse_y = 1;
-  if (n->mouse_x >= n->events.width)  n->mouse_x = n->events.width;
-  if (n->mouse_y >= n->events.height) n->mouse_y = n->events.height;
+  return font->engine->glyph_width (font, ctx, unichar);
+}
 
-  if (x) *x = n->mouse_x;
-  if (y) *y = n->mouse_y;
+static float
+ctx_glyph_kern (Ctx *ctx, int unicharA, int unicharB)
+{
+  CtxFont *font = &ctx_fonts[ctx->state.gstate.font];
+  return font->engine->glyph_kern (font, ctx, unicharA, unicharB);
+}
 
-  if ((prev_state & 1) != (buf[0] & 1))
+float
+ctx_text_width (Ctx        *ctx,
+                const char *string)
+{
+  float sum = 0.0;
+  if (!string)
+    return 0.0f;
+  for (const char *utf8 = string; *utf8; utf8 = ctx_utf8_skip (utf8, 1) )
     {
-      if (buf[0] & 1) ret = "mouse-press";
+      sum += ctx_glyph_width (ctx, ctx_utf8_to_unichar (utf8) );
     }
-  else if (buf[0] & 1)
-    ret = "mouse-drag";
+  return sum;
+}
 
-  if ((prev_state & 2) != (buf[0] & 2))
+static void
+_ctx_glyphs (Ctx     *ctx,
+             CtxGlyph *glyphs,
+             int       n_glyphs,
+             int       stroke)
+{
+  for (int i = 0; i < n_glyphs; i++)
     {
-      if (buf[0] & 2) ret = "mouse2-press";
+      {
+        uint32_t unichar = glyphs[i].index;
+        ctx_move_to (ctx, glyphs[i].x, glyphs[i].y);
+        ctx_glyph (ctx, unichar, stroke);
+      }
     }
-  else if (buf[0] & 2)
-    ret = "mouse2-drag";
+}
 
-  if ((prev_state & 4) != (buf[0] & 4))
+static void
+_ctx_text (Ctx        *ctx,
+           const char *string,
+           int         stroke,
+           int         visible)
+{
+  CtxState *state = &ctx->state;
+  float x = ctx->state.x;
+  switch ( (int) ctx_state_get (state, CTX_text_align) )
+    //switch (state->gstate.text_align)
     {
-      if (buf[0] & 4) ret = "mouse1-press";
+      case CTX_TEXT_ALIGN_START:
+      case CTX_TEXT_ALIGN_LEFT:
+        break;
+      case CTX_TEXT_ALIGN_CENTER:
+        x -= ctx_text_width (ctx, string) /2;
+        break;
+      case CTX_TEXT_ALIGN_END:
+      case CTX_TEXT_ALIGN_RIGHT:
+        x -= ctx_text_width (ctx, string);
+        break;
     }
-  else if (buf[0] & 4)
-    ret = "mouse1-drag";
-
-  prev_state = buf[0];
-  return ret;
+  float y = ctx->state.y;
+  float baseline_offset = 0.0f;
+  switch ( (int) ctx_state_get (state, CTX_text_baseline) )
+    {
+      case CTX_TEXT_BASELINE_HANGING:
+        /* XXX : crude */
+        baseline_offset = ctx->state.gstate.font_size  * 0.55;
+        break;
+      case CTX_TEXT_BASELINE_TOP:
+        /* XXX : crude */
+        baseline_offset = ctx->state.gstate.font_size  * 0.7;
+        break;
+      case CTX_TEXT_BASELINE_BOTTOM:
+        baseline_offset = -ctx->state.gstate.font_size * 0.1;
+        break;
+      case CTX_TEXT_BASELINE_ALPHABETIC:
+      case CTX_TEXT_BASELINE_IDEOGRAPHIC:
+        baseline_offset = 0.0f;
+        break;
+      case CTX_TEXT_BASELINE_MIDDLE:
+        baseline_offset = ctx->state.gstate.font_size * 0.25;
+        break;
+    }
+  float x0 = x;
+  for (const char *utf8 = string; *utf8; utf8 = ctx_utf8_skip (utf8, 1) )
+    {
+      if (*utf8 == '\n')
+        {
+          y += ctx->state.gstate.font_size * ctx_state_get (state, CTX_line_spacing);
+          x = x0;
+          if (visible)
+            { ctx_move_to (ctx, x, y); }
+        }
+      else
+        {
+          uint32_t unichar = ctx_utf8_to_unichar (utf8);
+          if (visible)
+            {
+              ctx_move_to (ctx, x, y + baseline_offset);
+              _ctx_glyph (ctx, unichar, stroke);
+            }
+          const char *next_utf8 = ctx_utf8_skip (utf8, 1);
+          if (next_utf8)
+            {
+              x += ctx_glyph_width (ctx, unichar);
+              x += ctx_glyph_kern (ctx, unichar, ctx_utf8_to_unichar (next_utf8) );
+            }
+          if (visible)
+            { ctx_move_to (ctx, x, y); }
+        }
+    }
+  if (!visible)
+    { ctx_move_to (ctx, x, y); }
 }
 
-static const char *mev_type = NULL;
-static int         mev_x = 0;
-static int         mev_y = 0;
-static int         mev_q = 0;
 
-static const char *mouse_get_event (Ctx  *n, int *x, int *y)
+CtxGlyph *
+ctx_glyph_allocate (int n_glyphs)
 {
-  if (!mev_q)
-    return NULL;
-  *x = mev_x;
-  *y = mev_y;
-  mev_q = 0;
-  return mev_type;
+  return (CtxGlyph *) malloc (sizeof (CtxGlyph) * n_glyphs);
 }
-
-static int mouse_has_event (Ctx *n)
+void
+gtx_glyph_free     (CtxGlyph *glyphs)
 {
-  struct timeval tv;
-  int retval;
+  free (glyphs);
+}
 
-  if (mouse_mode == NC_MOUSE_NONE)
-    return 0;
+void
+ctx_glyphs (Ctx        *ctx,
+            CtxGlyph   *glyphs,
+            int         n_glyphs)
+{
+  _ctx_glyphs (ctx, glyphs, n_glyphs, 0);
+}
 
-  if (mev_q)
-    return 1;
+void
+ctx_glyphs_stroke (Ctx        *ctx,
+                   CtxGlyph   *glyphs,
+                   int         n_glyphs)
+{
+  _ctx_glyphs (ctx, glyphs, n_glyphs, 1);
+}
 
-  if (n->mouse_fd == 0)
-    return 0;
-  return 0;
+void
+ctx_text (Ctx        *ctx,
+          const char *string)
+{
+  if (!string)
+    return;
+#if CTX_BACKEND_TEXT
+  ctx_process_cmd_str (ctx, CTX_TEXT, string, 0, 0);
+  _ctx_text (ctx, string, 0, 0);
+#else
+  _ctx_text (ctx, string, 0, 1);
+#endif
+}
 
-  {
-    fd_set rfds;
-    FD_ZERO (&rfds);
-    FD_SET(n->mouse_fd, &rfds);
-    tv.tv_sec = 0; tv.tv_usec = 0;
-    retval = select (n->mouse_fd+1, &rfds, NULL, NULL, &tv);
-  }
 
-  if (retval != 0)
-    {
-      int nx = 0, ny = 0;
-      const char *type = mouse_get_event_int (n, &nx, &ny);
+void
+ctx_fill_text (Ctx *ctx, const char *string,
+               float x, float y)
+{
+  ctx_move_to (ctx, x, y);
+  ctx_text (ctx, string);
+}
 
-      if ((mouse_mode < NC_MOUSE_DRAG && mev_type && !strcmp (mev_type, "drag")) ||
-          (mouse_mode < NC_MOUSE_ALL && mev_type && !strcmp (mev_type, "motion")))
-        {
-          mev_q = 0;
-          return mouse_has_event (n);
-        }
+void
+ctx_text_stroke (Ctx        *ctx,
+                 const char *string)
+{
+  if (!string)
+    return;
+#if CTX_BACKEND_TEXT
+  ctx_process_cmd_str (ctx, CTX_STROKE_TEXT, string, 0, 0);
+  _ctx_text (ctx, string, 1, 0);
+#else
+  _ctx_text (ctx, string, 1, 1);
+#endif
+}
 
-      if ((mev_type && !strcmp (type, mev_type) && !strcmp (type, "mouse-motion")) ||
-         (mev_type && !strcmp (type, mev_type) && !strcmp (type, "mouse1-drag")) ||
-         (mev_type && !strcmp (type, mev_type) && !strcmp (type, "mouse2-drag")))
-        {
-          if (nx == mev_x && ny == mev_y)
-          {
-            mev_q = 0;
-            return mouse_has_event (n);
-          }
-        }
-      mev_x = nx;
-      mev_y = ny;
-      mev_type = type;
-      mev_q = 1;
-    }
-  return retval != 0;
+void
+ctx_stroke_text (Ctx *ctx, const char *string,
+               float x, float y)
+{
+  ctx_move_to (ctx, x, y);
+  ctx_text_stroke (ctx, string);
 }
 
+static int _ctx_resolve_font (const char *name)
+{
+  for (int i = 0; i < ctx_font_count; i ++)
+    {
+      if (!ctx_strcmp (ctx_fonts[i].name, name) )
+        { return i; }
+    }
+  for (int i = 0; i < ctx_font_count; i ++)
+    {
+      if (ctx_strstr (ctx_fonts[i].name, name) )
+        { return i; }
+    }
+  return -1;
+}
 
-static int _nc_raw (void)
+int ctx_resolve_font (const char *name)
 {
-  struct termios raw;
-  if (!isatty (STDIN_FILENO))
-    return -1;
-  if (!atexit_registered)
+  int ret = _ctx_resolve_font (name);
+  if (ret >= 0)
+    { return ret; }
+  if (!ctx_strcmp (name, "regular") )
     {
-      atexit (nc_at_exit);
-      atexit_registered = 1;
+      int ret = _ctx_resolve_font ("sans");
+      if (ret >= 0) { return ret; }
+      ret = _ctx_resolve_font ("serif");
+      if (ret >= 0) { return ret; }
     }
-  if (tcgetattr (STDIN_FILENO, &orig_attr) == -1)
-    return -1;
-  raw = orig_attr;  /* modify the original mode */
-  raw.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
-  raw.c_oflag &= ~(OPOST);
-  raw.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG);
-  raw.c_cc[VMIN] = 1; raw.c_cc[VTIME] = 0; /* 1 byte, no timer */
-  if (tcsetattr (STDIN_FILENO, TCSAFLUSH, &raw) < 0)
-    return -1;
-  nc_is_raw = 1;
-#if !__COSMOPOLITAN__
-  tcdrain(STDIN_FILENO);
-  tcflush(STDIN_FILENO, 1);
-#endif
   return 0;
 }
 
-static int match_keycode (const char *buf, int length, const NcKeyCode **ret)
+static void ctx_font_setup ()
 {
-  int i;
-  int matches = 0;
+  static int initialized = 0;
+  if (initialized) { return; }
+  initialized = 1;
+#if CTX_FONT_ENGINE_CTX
+  ctx_font_count = 0; // oddly - this is needed in arduino
 
-  if (!strncmp (buf, "\033[M", MIN(length,3)))
-    {
-      if (length >= 6)
-        return 9001;
-      return 2342;
-    }
-  for (i = 0; keycodes[i].nick; i++)
-    if (!strncmp (buf, keycodes[i].sequence, length))
-      {
-        matches ++;
-        if ((int)strlen (keycodes[i].sequence) == length && ret)
-          {
-            *ret = &keycodes[i];
-            return 1;
-          }
-      }
-  if (matches != 1 && ret)
-    *ret = NULL;
-  return matches==1?2:matches;
+#if CTX_FONT_ENGINE_CTX_FS
+  ctx_load_font_ctx_fs ("sans-ctx", "/tmp/ctx-regular", 0);
+#else
+#if CTX_FONT_ascii
+  ctx_load_font_ctx ("sans-ctx", ctx_font_ascii, sizeof (ctx_font_ascii) );
+#endif
+#if CTX_FONT_regular
+  ctx_load_font_ctx ("sans-ctx", ctx_font_regular, sizeof (ctx_font_regular) );
+#endif
+#endif
+
+#if CTX_FONT_mono
+  ctx_load_font_ctx ("mono-ctx", ctx_font_mono, sizeof (ctx_font_mono) );
+#endif
+#if CTX_FONT_bold
+  ctx_load_font_ctx ("bold-ctx", ctx_font_bold, sizeof (ctx_font_bold) );
+#endif
+#if CTX_FONT_italic
+  ctx_load_font_ctx ("italic-ctx", ctx_font_italic, sizeof (ctx_font_italic) );
+#endif
+#if CTX_FONT_sans
+  ctx_load_font_ctx ("sans-ctx", ctx_font_sans, sizeof (ctx_font_sans) );
+#endif
+#if CTX_FONT_serif
+  ctx_load_font_ctx ("serif-ctx", ctx_font_serif, sizeof (ctx_font_serif) );
+#endif
+#if CTX_FONT_symbol
+  ctx_load_font_ctx ("symbol-ctx", ctx_font_symbol, sizeof (ctx_font_symbol) );
+#endif
+#if CTX_FONT_emoji
+  ctx_load_font_ctx ("emoji-ctx", ctx_font_emoji, sizeof (ctx_font_emoji) );
+#endif
+#endif
+
+#if NOTO_EMOJI_REGULAR
+  ctx_load_font_ttf ("sans-NotoEmoji_Regular", ttf_NotoEmoji_Regular_ttf, ttf_NotoEmoji_Regular_ttf_len);
+#endif
+#if ROBOTO_LIGHT
+  ctx_load_font_ttf ("sans-light-Roboto_Light", ttf_Roboto_Light_ttf, ttf_Roboto_Light_ttf_len);
+#endif
+#if ROBOTO_REGULAR
+  ctx_load_font_ttf ("sans-Roboto_Regular", ttf_Roboto_Regular_ttf, ttf_Roboto_Regular_ttf_len);
+#endif
+#if ROBOTO_BOLD
+  ctx_load_font_ttf ("sans-bold-Roboto_Bold", ttf_Roboto_Bold_ttf, ttf_Roboto_Bold_ttf_len);
+#endif
+#if DEJAVU_SANS
+  ctx_load_font_ttf ("sans-DejaVuSans", ttf_DejaVuSans_ttf, ttf_DejaVuSans_ttf_len);
+#endif
+#if VERA
+  ctx_load_font_ttf ("sans-Vera", ttf_Vera_ttf, ttf_Vera_ttf_len);
+#endif
+#if UNSCII_16
+  ctx_load_font_ttf ("mono-unscii16", ttf_unscii_16_ttf, ttf_unscii_16_ttf_len);
+#endif
+#if XA000_MONO
+  ctx_load_font_ttf ("mono-0xA000", ttf_0xA000_Mono_ttf, ttf_0xA000_Mono_ttf_len);
+#endif
+#if DEJAVU_SANS_MONO
+  ctx_load_font_ttf ("mono-DejaVuSansMono", ttf_DejaVuSansMono_ttf, ttf_DejaVuSansMono_ttf_len);
+#endif
+#if NOTO_MONO_REGULAR
+  ctx_load_font_ttf ("mono-NotoMono_Regular", ttf_NotoMono_Regular_ttf, ttf_NotoMono_Regular_ttf_len);
+#endif
 }
 
-static void nc_resize_term (int  dummy)
+
+#ifndef _DEFAULT_SOURCE
+#define _DEFAULT_SOURCE
+#endif
+
+#if !__COSMOPOLITAN__
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#endif
+
+//#include "ctx.h"
+/* instead of including ctx.h we declare the few utf8
+ * functions we use
+ */
+uint32_t ctx_utf8_to_unichar (const char *input);
+int ctx_unichar_to_utf8 (uint32_t  ch, uint8_t  *dest);
+int ctx_utf8_strlen (const char *s);
+
+
+void ctx_string_init (CtxString *string, int initial_size)
 {
-  size_changed = 1;
+  string->allocated_length = initial_size;
+  string->length = 0;
+  string->utf8_length = 0;
+  string->str = (char*)malloc (string->allocated_length + 1);
+  string->str[0]='\0';
 }
 
-int ctx_nct_has_event (Ctx  *n, int delay_ms)
+static void ctx_string_destroy (CtxString *string)
 {
-  struct timeval tv;
-  int retval;
-  fd_set rfds;
+  if (string->str)
+    {
+      free (string->str);
+      string->str = NULL;
+    }
+}
 
-  if (size_changed)
-    return 1;
-  FD_ZERO (&rfds);
-  FD_SET (STDIN_FILENO, &rfds);
-  tv.tv_sec = 0; tv.tv_usec = delay_ms * 1000; 
-  retval = select (1, &rfds, NULL, NULL, &tv);
-  if (size_changed)
-    return 1;
-  return retval == 1 && retval != -1;
+void ctx_string_clear (CtxString *string)
+{
+  string->length = 0;
+  string->utf8_length = 0;
+  string->str[string->length]=0;
 }
 
-const char *ctx_nct_get_event (Ctx *n, int timeoutms, int *x, int *y)
+static inline void _ctx_string_append_byte (CtxString *string, char  val)
 {
-  unsigned char buf[20];
-  int length;
+  if ( (val & 0xC0) != 0x80)
+    { string->utf8_length++; }
+  if (string->length + 2 >= string->allocated_length)
+    {
+      char *old = string->str;
+      string->allocated_length = CTX_MAX (string->allocated_length * 2, string->length + 2);
+      string->str = (char*)realloc (old, string->allocated_length);
+    }
+  string->str[string->length++] = val;
+  string->str[string->length] = '\0';
+}
 
+void ctx_string_append_byte (CtxString *string, char  val)
+{
+  _ctx_string_append_byte (string, val);
+}
 
-  if (x) *x = -1;
-  if (y) *y = -1;
+void ctx_string_append_unichar (CtxString *string, unsigned int unichar)
+{
+  char *str;
+  char utf8[5];
+  utf8[ctx_unichar_to_utf8 (unichar, (unsigned char *) utf8)]=0;
+  str = utf8;
+  while (str && *str)
+    {
+      _ctx_string_append_byte (string, *str);
+      str++;
+    }
+}
 
-  if (!signal_installed)
+static inline void _ctx_string_append_str (CtxString *string, const char *str)
+{
+  if (!str) { return; }
+  while (*str)
     {
-      _nc_raw ();
-      signal_installed = 1;
-      signal (SIGWINCH, nc_resize_term);
+      _ctx_string_append_byte (string, *str);
+      str++;
     }
-  if (mouse_mode) // XXX too often to do it all the time!
-    printf("%s", mouse_modes[mouse_mode]);
+}
 
-  {
-    int elapsed = 0;
-    int got_event = 0;
+void ctx_string_append_utf8char (CtxString *string, const char *str)
+{
+  if (!str) { return; }
+  int len = ctx_utf8_len (*str);
+  for (int i = 0; i < len && *str; i++)
+    {
+      _ctx_string_append_byte (string, *str);
+      str++;
+    }
+}
 
-    do {
-      if (size_changed)
-        {
-          size_changed = 0;
-          return "size-changed";
-        }
-      got_event = mouse_has_event (n);
-      if (!got_event)
-        got_event = ctx_nct_has_event (n, MIN(DELAY_MS, timeoutms-elapsed));
-      if (size_changed)
-        {
-          size_changed = 0;
-          return "size-changed";
-        }
-      /* only do this if the client has asked for idle events,
-       * and perhaps programmed the ms timer?
-       */
-      elapsed += MIN(DELAY_MS, timeoutms-elapsed);
-      if (!got_event && timeoutms && elapsed >= timeoutms)
-        return "idle";
-    } while (!got_event);
-  }
+void ctx_string_append_str (CtxString *string, const char *str)
+{
+  _ctx_string_append_str (string, str);
+}
 
-  if (mouse_has_event (n))
-    return mouse_get_event (n, x, y);
+CtxString *ctx_string_new_with_size (const char *initial, int initial_size)
+{
+  CtxString *string = (CtxString*)ctx_calloc (sizeof (CtxString), 1);
+  ctx_string_init (string, initial_size);
+  if (initial)
+    { _ctx_string_append_str (string, initial); }
+  return string;
+}
 
-  for (length = 0; length < 10; length ++)
-    if (read (STDIN_FILENO, &buf[length], 1) != -1)
-      {
-        const NcKeyCode *match = NULL;
+CtxString *ctx_string_new (const char *initial)
+{
+  return ctx_string_new_with_size (initial, 8);
+}
 
-        /* special case ESC, so that we can use it alone in keybindings */
-        if (length == 0 && buf[0] == 27)
-          {
-            struct timeval tv;
-            fd_set rfds;
-            FD_ZERO (&rfds);
-            FD_SET (STDIN_FILENO, &rfds);
-            tv.tv_sec = 0;
-            tv.tv_usec = 1000 * DELAY_MS;
-            if (select (1, &rfds, NULL, NULL, &tv) == 0)
-              return "esc";
-          }
+void ctx_string_append_data (CtxString *string, const char *str, int len)
+{
+  int i;
+  for (i = 0; i<len; i++)
+    { _ctx_string_append_byte (string, str[i]); }
+}
 
-        switch (match_keycode ((const char*)buf, length + 1, &match))
-          {
-            case 1: /* unique match */
-              if (!match)
-                return NULL;
-              if (!strcmp(match->nick, "ok"))
-              {
-                ctx_frame_ack = 1;
-                return NULL;
-              }
-              return match->nick;
-              break;
-            case 9001: /* mouse event */
-              if (x) *x = ((unsigned char)buf[4]-32)*1.0;
-              if (y) *y = ((unsigned char)buf[5]-32)*1.0;
-              switch (buf[3])
-                {
-                        /* XXX : todo reduce this to less string constants */
-                  case 32:  return "mouse-press";
-                  case 33:  return "mouse1-press";
-                  case 34:  return "mouse2-press";
-                  case 40:  return "alt-mouse-press";
-                  case 41:  return "alt-mouse1-press";
-                  case 42:  return "alt-mouse2-press";
-                  case 48:  return "control-mouse-press";
-                  case 49:  return "control-mouse1-press";
-                  case 50:  return "control-mouse2-press";
-                  case 56:  return "alt-control-mouse-press";
-                  case 57:  return "alt-control-mouse1-press";
-                  case 58:  return "alt-control-mouse2-press";
-                  case 64:  return "mouse-drag";
-                  case 65:  return "mouse1-drag";
-                  case 66:  return "mouse2-drag";
-                  case 71:  return "mouse-motion"; /* shift+motion */
-                  case 72:  return "alt-mouse-drag";
-                  case 73:  return "alt-mouse1-drag";
-                  case 74:  return "alt-mouse2-drag";
-                  case 75:  return "mouse-motion"; /* alt+motion */
-                  case 80:  return "control-mouse-drag";
-                  case 81:  return "control-mouse1-drag";
-                  case 82:  return "control-mouse2-drag";
-                  case 83:  return "mouse-motion"; /* ctrl+motion */
-                  case 91:  return "mouse-motion"; /* ctrl+alt+motion */
-                  case 95:  return "mouse-motion"; /* ctrl+alt+shift+motion */
-                  case 96:  return "scroll-up";
-                  case 97:  return "scroll-down";
-                  case 100: return "shift-scroll-up";
-                  case 101: return "shift-scroll-down";
-                  case 104: return "alt-scroll-up";
-                  case 105: return "alt-scroll-down";
-                  case 112: return "control-scroll-up";
-                  case 113: return "control-scroll-down";
-                  case 116: return "control-shift-scroll-up";
-                  case 117: return "control-shift-scroll-down";
-                  case 35: /* (or release) */
-                  case 51: /* (or ctrl-release) */
-                  case 43: /* (or alt-release) */
-                  case 67: return "mouse-motion";
-                           /* have a separate mouse-drag ? */
-                  default: {
-                             static char rbuf[100];
-                             sprintf (rbuf, "mouse (unhandled state: %i)", buf[3]);
-                             return rbuf;
-                           }
-                }
-            case 0: /* no matches, bail*/
-              { 
-                static char ret[256];
-                if (length == 0 && ctx_utf8_len (buf[0])>1) /* single unicode
-                                                               char */
-                  {
-                    int n_read = 
-                    read (STDIN_FILENO, &buf[length+1], ctx_utf8_len(buf[0])-1);
-                    if (n_read)
-                    {
-                      buf[ctx_utf8_len(buf[0])]=0;
-                      strcpy (ret, (const char*)buf);
-                    }
-                    return ret;
-                  }
-                if (length == 0) /* ascii */
-                  {
-                    buf[1]=0;
-                    strcpy (ret, (const char*)buf);
-                    return ret;
-                  }
-                sprintf (ret, "unhandled %i:'%c' %i:'%c' %i:'%c' %i:'%c' %i:'%c' %i:'%c' %i:'%c'",
-                  length>=0? buf[0]: 0, length>=0? buf[0]>31?buf[0]:'?': ' ', 
-                  length>=1? buf[1]: 0, length>=1? buf[1]>31?buf[1]:'?': ' ', 
-                  length>=2? buf[2]: 0, length>=2? buf[2]>31?buf[2]:'?': ' ', 
-                  length>=3? buf[3]: 0, length>=3? buf[3]>31?buf[3]:'?': ' ',
-                  length>=4? buf[4]: 0, length>=4? buf[4]>31?buf[4]:'?': ' ',
-                  length>=5? buf[5]: 0, length>=5? buf[5]>31?buf[5]:'?': ' ',
-                  length>=6? buf[6]: 0, length>=6? buf[6]>31?buf[6]:'?': ' ');
-                return ret;
-              }
-              return NULL;
-            default: /* continue */
-              break;
-          }
-      }
-    else
-      return "key read eek";
-  return "fail";
+void ctx_string_append_string (CtxString *string, CtxString *string2)
+{
+  const char *str = ctx_string_get (string2);
+  while (str && *str)
+    {
+      _ctx_string_append_byte (string, *str);
+      str++;
+    }
 }
 
-int ctx_nct_consume_events (Ctx *ctx)
+const char *ctx_string_get (CtxString *string)
 {
-  int ix, iy;
-  CtxCtx *ctxctx = (CtxCtx*)ctx->renderer;
-  const char *event = NULL;
+  return string->str;
+}
 
-  {
-    float x, y;
-    event = ctx_nct_get_event (ctx, 50, &ix, &iy);
+int ctx_string_get_utf8length (CtxString *string)
+{
+  return string->utf8_length;
+}
 
-    x = (ix - 1.0 + 0.5) / ctxctx->cols * ctx->events.width;
-    y = (iy - 1.0)       / ctxctx->rows * ctx->events.height;
+int ctx_string_get_length (CtxString *string)
+{
+  return string->length;
+}
 
-    if (!strcmp (event, "mouse-press"))
-    {
-      ctx_pointer_press (ctx, x, y, 0, 0);
-      ctxctx->was_down = 1;
-    } else if (!strcmp (event, "mouse-release"))
-    {
-      ctx_pointer_release (ctx, x, y, 0, 0);
-      ctxctx->was_down = 0;
-    } else if (!strcmp (event, "mouse-motion"))
-    {
-      //nct_set_cursor_pos (backend->term, ix, iy);
-      //nct_flush (backend->term);
-      if (ctxctx->was_down)
-      {
-        ctx_pointer_release (ctx, x, y, 0, 0);
-        ctxctx->was_down = 0;
-      }
-      ctx_pointer_motion (ctx, x, y, 0, 0);
-    } else if (!strcmp (event, "mouse-drag"))
-    {
-      ctx_pointer_motion (ctx, x, y, 0, 0);
-    } else if (!strcmp (event, "size-changed"))
-    {
-#if 0
-      int width = nct_sys_terminal_width ();
-      int height = nct_sys_terminal_height ();
-      nct_set_size (backend->term, width, height);
-      width *= CPX;
-      height *= CPX;
-      free (mrg->glyphs);
-      free (mrg->styles);
-      free (backend->nct_pixels);
-      backend->nct_pixels = calloc (width * height * 4, 1);
-      mrg->glyphs = calloc ((width/CPX) * (height/CPX) * 4, 1);
-      mrg->styles = calloc ((width/CPX) * (height/CPX) * 1, 1);
-      mrg_set_size (mrg, width, height);
-      mrg_queue_draw (mrg, NULL);
-#endif
-
-    }
-    else
+void
+ctx_string_free (CtxString *string, int freealloc)
+{
+  if (freealloc)
     {
-      if (!strcmp (event, "esc"))
-        ctx_key_press (ctx, 0, "escape", 0);
-      else if (!strcmp (event, "space"))
-        ctx_key_press (ctx, 0, "space", 0);
-      else if (!strcmp (event, "enter"))
-        ctx_key_press (ctx, 0, "\n", 0);
-      else if (!strcmp (event, "return"))
-        ctx_key_press (ctx, 0, "return", 0);
-      else if (!strcmp (event, "idle"))
-      {
-      }
-      else
-      ctx_key_press (ctx, 0, event, 0);
+      ctx_string_destroy (string);
     }
+#if 0
+  if (string->is_line)
+  {
+    VtLine *line = (VtLine*)string;
+    if (line->style)
+      { free (line->style); }
+    if (line->ctx)
+      { ctx_free (line->ctx); }
+    if (line->ctx_copy)
+      { ctx_free (line->ctx_copy); }
   }
+#endif
+  free (string);
+}
 
-  return 1;
+void
+ctx_string_set (CtxString *string, const char *new_string)
+{
+  ctx_string_clear (string);
+  _ctx_string_append_str (string, new_string);
 }
 
-const char *ctx_native_get_event (Ctx *n, int timeoutms)
+static char *ctx_strdup (const char *str)
 {
-  static unsigned char buf[256];
-  int length;
+  int len = strlen (str);
+  char *ret = (char*)malloc (len + 1);
+  memcpy (ret, str, len);
+  ret[len]=0;
+  return ret;
+}
 
-  if (!signal_installed)
+void ctx_string_replace_utf8 (CtxString *string, int pos, const char *new_glyph)
+{
+  int new_len = ctx_utf8_len (*new_glyph);
+#if 1
+  int old_len = string->utf8_length;
+#else
+  int old_len = ctx_utf8_strlen (string->str);// string->utf8_length;
+#endif
+  char tmpg[3]=" ";
+  if (pos == old_len)
     {
-      _nc_raw ();
-      signal_installed = 1;
-      signal (SIGWINCH, nc_resize_term);
+      _ctx_string_append_str (string, new_glyph);
+      return;
+    }
+  if (new_len <= 1 && new_glyph[0] < 32)
+    {
+      new_len = 1;
+      tmpg[0]=new_glyph[0]+64;
+      new_glyph = tmpg;
     }
-//if (mouse_mode) // XXX too often to do it all the time!
-//  printf("%s", mouse_modes[mouse_mode]);
-
-    int got_event = 0;
   {
-    int elapsed = 0;
-
-    do {
-      if (size_changed)
-        {
-          size_changed = 0;
-          return "size-changed";
-        }
-      got_event = ctx_nct_has_event (n, MIN(DELAY_MS, timeoutms-elapsed));
-      if (size_changed)
-        {
-          size_changed = 0;
-          return "size-changed";
-        }
-      /* only do this if the client has asked for idle events,
-       * and perhaps programmed the ms timer?
-       */
-      elapsed += MIN(DELAY_MS, timeoutms-elapsed);
-      if (!got_event && timeoutms && elapsed >= timeoutms)
+    for (int i = old_len; i <= pos + 2; i++)
       {
-        return "idle";
+        _ctx_string_append_byte (string, ' ');
+        old_len++;
       }
-    } while (!got_event);
   }
-
-  for (length = 0; got_event && length < 200; length ++)
-  {
-    if (read (STDIN_FILENO, &buf[length], 1) != -1)
-      {
-         buf[length+1] = 0;
-         if (!strcmp ((char*)buf, "\e[0n"))
-         {
-           ctx_frame_ack = 1;
-           return NULL;
-         }
-         else if (buf[length]=='\n')
-         {
-           buf[length]=0;
-           return (const char*)buf;
-         }
-      }
-      got_event = ctx_nct_has_event (n, 5);
+  if (string->length + new_len  >= string->allocated_length - 2)
+    {
+      char *tmp;
+      char *defer;
+      string->allocated_length = string->length + new_len + 2;
+      tmp = (char*) ctx_calloc (string->allocated_length + 1 + 8, 1);
+      strcpy (tmp, string->str);
+      defer = string->str;
+      string->str = tmp;
+      free (defer);
     }
-  return NULL;
-}
-
-const char *ctx_key_get_label (Ctx  *n, const char *nick)
-{
-  int j;
-  int found = -1;
-  for (j = 0; keycodes[j].nick; j++)
-    if (found == -1 && !strcmp (keycodes[j].nick, nick))
-      return keycodes[j].label;
-  return NULL;
-}
-
-void _ctx_mouse (Ctx *term, int mode)
-{
-  //if (term->is_st && mode > 1)
-  //  mode = 1;
-  if (mode != mouse_mode)
-  {
-    printf ("%s", mouse_modes[mode]);
-    fflush (stdout);
-  }
-  mouse_mode = mode;
-}
-
-
-#endif
-
-#if !__COSMOPOLITAN__
-#include <sys/time.h>
-#endif
-
-
-#define usecs(time)    ((uint64_t)(time.tv_sec - start_time.tv_sec) * 1000000 + time.     tv_usec)
-
-#if !__COSMOPOLITAN__
-static struct timeval start_time;
-
-static void
-_ctx_init_ticks (void)
-{
-  static int done = 0;
-  if (done)
-    return;
-  done = 1;
-  gettimeofday (&start_time, NULL);
+  char *p = (char *) ctx_utf8_skip (string->str, pos);
+  int prev_len = ctx_utf8_len (*p);
+  char *rest;
+  if (*p == 0 || * (p+prev_len) == 0)
+    {
+      rest = ctx_strdup ("");
+    }
+  else
+    {
+      if (p + prev_len >= string->length  + string->str)
+        { rest = ctx_strdup (""); }
+      else
+        { rest = ctx_strdup (p + prev_len); }
+    }
+  memcpy (p, new_glyph, new_len);
+  memcpy (p + new_len, rest, strlen (rest) + 1);
+  string->length += new_len;
+  string->length -= prev_len;
+  free (rest);
+  //string->length = strlen (string->str);
+  //string->utf8_length = ctx_utf8_strlen (string->str);
 }
 
-static inline unsigned long
-_ctx_ticks (void)
+void ctx_string_replace_unichar (CtxString *string, int pos, uint32_t unichar)
 {
-  struct timeval measure_time;
-  gettimeofday (&measure_time, NULL);
-  return usecs (measure_time) - usecs (start_time);
+  uint8_t utf8[8];
+  ctx_unichar_to_utf8 (unichar, utf8);
+  ctx_string_replace_utf8 (string, pos, (char *) utf8);
 }
 
-unsigned long
-ctx_ticks (void)
+uint32_t ctx_string_get_unichar (CtxString *string, int pos)
 {
-  _ctx_init_ticks ();
-  return _ctx_ticks ();
+  char *p = (char *) ctx_utf8_skip (string->str, pos);
+  if (!p)
+    { return 0; }
+  return ctx_utf8_to_unichar (p);
 }
 
 
-
-enum _CtxFlags {
-   CTX_FLAG_DIRECT = (1<<0),
-};
-typedef enum _CtxFlags CtxFlags;
-
-
-int _ctx_max_threads = 1;
-int _ctx_enable_hash_cache = 1;
-#if CTX_SHAPE_CACHE
-extern int _ctx_shape_cache_enabled;
-#endif
-
-#if CTX_THREADS
-static mtx_t _ctx_texture_mtx;
-#endif
-
-void _ctx_texture_lock (void)
+void ctx_string_insert_utf8 (CtxString *string, int pos, const char *new_glyph)
 {
-#if CTX_THREADS
-  mtx_lock (&_ctx_texture_mtx);
-#endif
+  int new_len = ctx_utf8_len (*new_glyph);
+  int old_len = string->utf8_length;
+  char tmpg[3]=" ";
+  if (old_len == pos && 0)
+    {
+      ctx_string_append_str (string, new_glyph);
+      return;
+    }
+  if (new_len <= 1 && new_glyph[0] < 32)
+    {
+      tmpg[0]=new_glyph[0]+64;
+      new_glyph = tmpg;
+    }
+  {
+    for (int i = old_len; i <= pos; i++)
+      {
+        _ctx_string_append_byte (string, ' ');
+        old_len++;
+      }
+  }
+  if (string->length + new_len + 1  > string->allocated_length)
+    {
+      char *tmp;
+      char *defer;
+      string->allocated_length = string->length + new_len + 1;
+      tmp = (char*) ctx_calloc (string->allocated_length + 1, 1);
+      strcpy (tmp, string->str);
+      defer = string->str;
+      string->str = tmp;
+      free (defer);
+    }
+  char *p = (char *) ctx_utf8_skip (string->str, pos);
+  int prev_len = ctx_utf8_len (*p);
+  char *rest;
+  if ( (*p == 0 || * (p+prev_len) == 0) && pos != 0)
+    {
+      rest = ctx_strdup ("");
+    }
+  else
+    {
+      rest = ctx_strdup (p);
+    }
+  memcpy (p, new_glyph, new_len);
+  memcpy (p + new_len, rest, strlen (rest) + 1);
+  free (rest);
+  string->length = strlen (string->str);
+  string->utf8_length = ctx_utf8_strlen (string->str);
 }
 
-void _ctx_texture_unlock (void)
+void ctx_string_insert_unichar (CtxString *string, int pos, uint32_t unichar)
 {
-#if CTX_THREADS
-  mtx_unlock (&_ctx_texture_mtx);
-#endif
+  uint8_t utf8[5]="";
+  utf8[ctx_unichar_to_utf8(unichar, utf8)]=0;
+  ctx_string_insert_utf8 (string, pos, (char*)utf8);
 }
 
-
-void
-ctx_init (int *argc, char ***argv)
+void ctx_string_remove (CtxString *string, int pos)
 {
-#if 0
-  if (!getenv ("CTX_VERSION"))
+  int old_len = string->utf8_length;
   {
-    int i;
-    char *new_argv[*argc+3];
-    new_argv[0] = "ctx";
-    for (i = 0; i < *argc; i++)
+    for (int i = old_len; i <= pos; i++)
+      {
+        _ctx_string_append_byte (string, ' ');
+        old_len++;
+      }
+  }
+  char *p = (char *) ctx_utf8_skip (string->str, pos);
+  int prev_len = ctx_utf8_len (*p);
+  char *rest;
+  if (!p || *p == 0)
     {
-      new_argv[i+1] = *argv[i];
+      return;
+      rest = ctx_strdup ("");
+      prev_len = 0;
     }
-    new_argv[i+1] = NULL;
-    execvp (new_argv[0], new_argv);
-    // if this fails .. we continue normal startup
-    // and end up in self-hosted braille
+  else if (* (p+prev_len) == 0)
+  {
+      rest = ctx_strdup ("");
   }
-#endif
+  else
+    {
+      rest = ctx_strdup (p + prev_len);
+    }
+  strcpy (p, rest);
+  string->str[string->length - prev_len] = 0;
+  free (rest);
+  string->length = strlen (string->str);
+  string->utf8_length = ctx_utf8_strlen (string->str);
 }
 
-
-#if 0
-int ctx_count (Ctx *ctx)
+char *ctx_strdup_printf (const char *format, ...)
 {
-  return ctx->drawlist.count;
+  va_list ap;
+  size_t needed;
+  char *buffer;
+  va_start (ap, format);
+  needed = vsnprintf (NULL, 0, format, ap) + 1;
+  buffer = (char*)malloc (needed);
+  va_end (ap);
+  va_start (ap, format);
+  vsnprintf (buffer, needed, format, ap);
+  va_end (ap);
+  return buffer;
 }
-#endif
-
-extern int _ctx_damage_control;
 
-static void ctx_list_backends()
+void ctx_string_append_printf (CtxString *string, const char *format, ...)
 {
-    fprintf (stderr, "possible values for CTX_BACKEND:\n");
-    fprintf (stderr, " ctx");
-#if CTX_SDL
-    fprintf (stderr, " SDL");
-#endif
-#if CTX_FB
-    fprintf (stderr, " fb");
-    fprintf (stderr, " drm");
-#endif
-    fprintf (stderr, " term");
-    fprintf (stderr, " termimg");
-    fprintf (stderr, "\n");
+  va_list ap;
+  size_t needed;
+  char *buffer;
+  va_start (ap, format);
+  needed = vsnprintf (NULL, 0, format, ap) + 1;
+  buffer = (char*)malloc (needed);
+  va_end (ap);
+  va_start (ap, format);
+  vsnprintf (buffer, needed, format, ap);
+  va_end (ap);
+  ctx_string_append_str (string, buffer);
+  free (buffer);
 }
 
-#if CTX_EVENTS
+#if CTX_CAIRO
 
-static uint32_t ctx_ms (Ctx *ctx)
+typedef struct _CtxCairo CtxCairo;
+struct
+  _CtxCairo
 {
-  return _ctx_ticks () / 1000;
-}
-
+  CtxImplementation vfuncs;
+  Ctx              *ctx;
+  cairo_t          *cr;
+  cairo_pattern_t  *pat;
+  cairo_surface_t  *image;
+  int               preserve;
+};
 
-static int is_in_ctx (void);
-Ctx *ctx_new_ui (int width, int height)
+static void
+ctx_cairo_process (CtxCairo *ctx_cairo, CtxCommand *c)
 {
-#if CTX_TILED
-  if (getenv ("CTX_DAMAGE_CONTROL"))
-  {
-    const char * val = getenv ("CTX_DAMAGE_CONTROL");
-    if (!strcmp (val, "0") ||
-        !strcmp (val, "off"))
-      _ctx_damage_control = 0;
-    else
-      _ctx_damage_control = 1;
-  }
-#endif
-
-  if (getenv ("CTX_HASH_CACHE"))
-  {
-    const char * val = getenv ("CTX_HASH_CACHE");
-    if (!strcmp (val, "0"))
-      _ctx_enable_hash_cache = 0;
-    if (!strcmp (val, "off"))
-      _ctx_enable_hash_cache = 0;
-  }
-#if CTX_SHAPE_CACHE
-  if (getenv ("CTX_SHAPE_CACHE"))
-  {
-    const char * val = getenv ("CTX_SHAPE_CACHE");
-    if (!strcmp (val, "0"))
-      _ctx_shape_cache_enabled = 0;
-    if (!strcmp (val, "off"))
-      _ctx_shape_cache_enabled = 0;
-  }
-#endif
-
-  if (getenv ("CTX_THREADS"))
-  {
-    int val = atoi (getenv ("CTX_THREADS"));
-    _ctx_max_threads = val;
-  }
-  else
-  {
-    _ctx_max_threads = 2;
-#ifdef _SC_NPROCESSORS_ONLN
-    _ctx_max_threads = sysconf (_SC_NPROCESSORS_ONLN) / 2;
-#endif
-  }
-  
-#if CTX_THREADS
-  mtx_init (&_ctx_texture_mtx, mtx_plain);
-#endif
-
-  if (_ctx_max_threads < 1) _ctx_max_threads = 1;
-  if (_ctx_max_threads > CTX_MAX_THREADS) _ctx_max_threads = CTX_MAX_THREADS;
-
-  //fprintf (stderr, "ctx using %i threads\n", _ctx_max_threads);
-  const char *backend = getenv ("CTX_BACKEND");
-
-  if (backend && !strcmp (backend, ""))
-    backend = NULL;
-  if (backend && !strcmp (backend, "auto"))
-    backend = NULL;
-  if (backend && !strcmp (backend, "list"))
-  {
-    ctx_list_backends ();
-    exit (-1);
-  }
-
-  Ctx *ret = NULL;
-
-  /* we do the query on auto but not on directly set ctx
-   *
-   */
-  if ((backend && !strcmp(backend, "ctx")) ||
-      (backend == NULL && is_in_ctx ()))
-  {
-    if (!backend || !strcmp (backend, "ctx"))
+  CtxEntry *entry = (CtxEntry *) &c->entry;
+  cairo_t *cr = ctx_cairo->cr;
+  switch (entry->code)
     {
-      // full blown ctx protocol - in terminal or standalone
-      ret = ctx_new_ctx (width, height);
-    }
-  }
-
-#if CTX_SDL
-  if (!ret && getenv ("DISPLAY"))
-  {
-    if ((backend==NULL) || (!strcmp (backend, "SDL")))
-      ret = ctx_new_sdl (width, height);
-  }
+      case CTX_LINE_TO:
+        cairo_line_to (cr, c->line_to.x, c->line_to.y);
+        break;
+      case CTX_REL_LINE_TO:
+        cairo_rel_line_to (cr, c->rel_line_to.x, c->rel_line_to.y);
+        break;
+      case CTX_MOVE_TO:
+        cairo_move_to (cr, c->move_to.x, c->move_to.y);
+        break;
+      case CTX_REL_MOVE_TO:
+        cairo_rel_move_to (cr, ctx_arg_float (0), ctx_arg_float (1) );
+        break;
+      case CTX_CURVE_TO:
+        cairo_curve_to (cr, ctx_arg_float (0), ctx_arg_float (1),
+                        ctx_arg_float (2), ctx_arg_float (3),
+                        ctx_arg_float (4), ctx_arg_float (5) );
+        break;
+      case CTX_REL_CURVE_TO:
+        cairo_rel_curve_to (cr,ctx_arg_float (0), ctx_arg_float (1),
+                            ctx_arg_float (2), ctx_arg_float (3),
+                            ctx_arg_float (4), ctx_arg_float (5) );
+        break;
+      case CTX_PRESERVE:
+        ctx_cairo->preserve = 1;
+        break;
+      case CTX_QUAD_TO:
+        {
+          double x0, y0;
+          cairo_get_current_point (cr, &x0, &y0);
+          float cx = ctx_arg_float (0);
+          float cy = ctx_arg_float (1);
+          float  x = ctx_arg_float (2);
+          float  y = ctx_arg_float (3);
+          cairo_curve_to (cr,
+                          (cx * 2 + x0) / 3.0f, (cy * 2 + y0) / 3.0f,
+                          (cx * 2 + x) / 3.0f,           (cy * 2 + y) / 3.0f,
+                          x,                              y);
+        }
+        break;
+      case CTX_REL_QUAD_TO:
+        {
+          double x0, y0;
+          cairo_get_current_point (cr, &x0, &y0);
+          float cx = ctx_arg_float (0) + x0;
+          float cy = ctx_arg_float (1) + y0;
+          float  x = ctx_arg_float (2) + x0;
+          float  y = ctx_arg_float (3) + y0;
+          cairo_curve_to (cr,
+                          (cx * 2 + x0) / 3.0f, (cy * 2 + y0) / 3.0f,
+                          (cx * 2 + x) / 3.0f,           (cy * 2 + y) / 3.0f,
+                          x,                              y);
+        }
+        break;
+      /* rotate/scale/translate does not occur in fully minified data stream */
+      case CTX_ROTATE:
+        cairo_rotate (cr, ctx_arg_float (0) );
+        break;
+      case CTX_SCALE:
+        cairo_scale (cr, ctx_arg_float (0), ctx_arg_float (1) );
+        break;
+      case CTX_TRANSLATE:
+        cairo_translate (cr, ctx_arg_float (0), ctx_arg_float (1) );
+        break;
+      case CTX_LINE_WIDTH:
+        cairo_set_line_width (cr, ctx_arg_float (0) );
+        break;
+      case CTX_ARC:
+#if 0
+        fprintf (stderr, "F %2.1f %2.1f %2.1f %2.1f %2.1f %2.1f\n",
+                        ctx_arg_float(0),
+                        ctx_arg_float(1),
+                        ctx_arg_float(2),
+                        ctx_arg_float(3),
+                        ctx_arg_float(4),
+                        ctx_arg_float(5),
+                        ctx_arg_float(6));
 #endif
-
-#if CTX_FB
-  if (!ret && !getenv ("DISPLAY"))
-  {
-    if ((backend==NULL) || (!strcmp (backend, "drm")))
-    ret = ctx_new_fb (width, height, 1);
-
-    if (!ret)
-    {
-      if ((backend==NULL) || (!strcmp (backend, "fb")))
-        ret = ctx_new_fb (width, height, 0);
-    }
-  }
+        if (ctx_arg_float (5) == 1)
+          cairo_arc (cr, ctx_arg_float (0), ctx_arg_float (1),
+                     ctx_arg_float (2), ctx_arg_float (3),
+                     ctx_arg_float (4) );
+        else
+          cairo_arc_negative (cr, ctx_arg_float (0), ctx_arg_float (1),
+                              ctx_arg_float (2), ctx_arg_float (3),
+                              ctx_arg_float (4) );
+        break;
+      case CTX_SET_RGBA_U8:
+        cairo_set_source_rgba (cr, ctx_u8_to_float (ctx_arg_u8 (0) ),
+                               ctx_u8_to_float (ctx_arg_u8 (1) ),
+                               ctx_u8_to_float (ctx_arg_u8 (2) ),
+                               ctx_u8_to_float (ctx_arg_u8 (3) ) );
+        break;
+#if 0
+      case CTX_SET_RGBA_STROKE: // XXX : we need to maintain
+        //       state for the two kinds
+        cairo_set_source_rgba (cr, ctx_arg_u8 (0) /255.0,
+                               ctx_arg_u8 (1) /255.0,
+                               ctx_arg_u8 (2) /255.0,
+                               ctx_arg_u8 (3) /255.0);
+        break;
 #endif
-
-#if CTX_RASTERIZER
-  // braille in terminal
-  if (!ret)
-  {
-    if ((backend==NULL) || (!strcmp (backend, "term")))
-    ret = ctx_new_term (width, height);
-  }
-  if (!ret)
-  {
-    if ((backend==NULL) || (!strcmp (backend, "termimg")))
-    ret = ctx_new_termimg (width, height);
-  }
+      case CTX_RECTANGLE:
+      case CTX_ROUND_RECTANGLE: // XXX - arcs
+        cairo_rectangle (cr, c->rectangle.x, c->rectangle.y,
+                         c->rectangle.width, c->rectangle.height);
+        break;
+      case CTX_SET_PIXEL:
+        cairo_set_source_rgba (cr, ctx_u8_to_float (ctx_arg_u8 (0) ),
+                               ctx_u8_to_float (ctx_arg_u8 (1) ),
+                               ctx_u8_to_float (ctx_arg_u8 (2) ),
+                               ctx_u8_to_float (ctx_arg_u8 (3) ) );
+        cairo_rectangle (cr, ctx_arg_u16 (2), ctx_arg_u16 (3), 1, 1);
+        cairo_fill (cr);
+        break;
+      case CTX_FILL:
+        if (ctx_cairo->preserve)
+        {
+          cairo_fill_preserve (cr);
+          ctx_cairo->preserve = 0;
+        }
+        else
+        {
+          cairo_fill (cr);
+        }
+        break;
+      case CTX_STROKE:
+        if (ctx_cairo->preserve)
+        {
+          cairo_stroke_preserve (cr);
+          ctx_cairo->preserve = 0;
+        }
+        else
+        {
+          cairo_stroke (cr);
+        }
+        break;
+      case CTX_IDENTITY:
+        cairo_identity_matrix (cr);
+        break;
+      case CTX_CLIP:
+        if (ctx_cairo->preserve)
+        {
+          cairo_clip_preserve (cr);
+          ctx_cairo->preserve = 0;
+        }
+        else
+        {
+          cairo_clip (cr);
+        }
+        break;
+        break;
+      case CTX_BEGIN_PATH:
+        cairo_new_path (cr);
+        break;
+      case CTX_CLOSE_PATH:
+        cairo_close_path (cr);
+        break;
+      case CTX_SAVE:
+        cairo_save (cr);
+        break;
+      case CTX_RESTORE:
+        cairo_restore (cr);
+        break;
+      case CTX_FONT_SIZE:
+        cairo_set_font_size (cr, ctx_arg_float (0) );
+        break;
+      case CTX_MITER_LIMIT:
+        cairo_set_miter_limit (cr, ctx_arg_float (0) );
+        break;
+      case CTX_LINE_CAP:
+        {
+          int cairo_val = CAIRO_LINE_CAP_SQUARE;
+          switch (ctx_arg_u8 (0) )
+            {
+              case CTX_CAP_ROUND:
+                cairo_val = CAIRO_LINE_CAP_ROUND;
+                break;
+              case CTX_CAP_SQUARE:
+                cairo_val = CAIRO_LINE_CAP_SQUARE;
+                break;
+              case CTX_CAP_NONE:
+                cairo_val = CAIRO_LINE_CAP_BUTT;
+                break;
+            }
+          cairo_set_line_cap (cr, cairo_val);
+        }
+        break;
+      case CTX_BLEND_MODE:
+        {
+          // does not map to cairo
+        }
+        break;
+      case CTX_COMPOSITING_MODE:
+        {
+          int cairo_val = CAIRO_OPERATOR_OVER;
+          switch (ctx_arg_u8 (0) )
+            {
+              case CTX_COMPOSITE_SOURCE_OVER:
+                cairo_val = CAIRO_OPERATOR_OVER;
+                break;
+              case CTX_COMPOSITE_COPY:
+                cairo_val = CAIRO_OPERATOR_SOURCE;
+                break;
+            }
+          cairo_set_operator (cr, cairo_val);
+        }
+      case CTX_LINE_JOIN:
+        {
+          int cairo_val = CAIRO_LINE_JOIN_ROUND;
+          switch (ctx_arg_u8 (0) )
+            {
+              case CTX_JOIN_ROUND:
+                cairo_val = CAIRO_LINE_JOIN_ROUND;
+                break;
+              case CTX_JOIN_BEVEL:
+                cairo_val = CAIRO_LINE_JOIN_BEVEL;
+                break;
+              case CTX_JOIN_MITER:
+                cairo_val = CAIRO_LINE_JOIN_MITER;
+                break;
+            }
+          cairo_set_line_join (cr, cairo_val);
+        }
+        break;
+      case CTX_LINEAR_GRADIENT:
+        {
+          if (ctx_cairo->pat)
+            {
+              cairo_pattern_destroy (ctx_cairo->pat);
+              ctx_cairo->pat = NULL;
+            }
+          ctx_cairo->pat = cairo_pattern_create_linear (ctx_arg_float (0), ctx_arg_float (1),
+                           ctx_arg_float (2), ctx_arg_float (3) );
+          cairo_pattern_add_color_stop_rgba (ctx_cairo->pat, 0, 0, 0, 0, 1);
+          cairo_pattern_add_color_stop_rgba (ctx_cairo->pat, 1, 1, 1, 1, 1);
+          cairo_set_source (cr, ctx_cairo->pat);
+        }
+        break;
+      case CTX_RADIAL_GRADIENT:
+        {
+          if (ctx_cairo->pat)
+            {
+              cairo_pattern_destroy (ctx_cairo->pat);
+              ctx_cairo->pat = NULL;
+            }
+          ctx_cairo->pat = cairo_pattern_create_radial (ctx_arg_float (0), ctx_arg_float (1),
+                           ctx_arg_float (2), ctx_arg_float (3),
+                           ctx_arg_float (4), ctx_arg_float (5) );
+          cairo_set_source (cr, ctx_cairo->pat);
+        }
+        break;
+      case CTX_GRADIENT_STOP:
+        cairo_pattern_add_color_stop_rgba (ctx_cairo->pat,
+                                           ctx_arg_float (0),
+                                           ctx_u8_to_float (ctx_arg_u8 (4) ),
+                                           ctx_u8_to_float (ctx_arg_u8 (5) ),
+                                           ctx_u8_to_float (ctx_arg_u8 (6) ),
+                                           ctx_u8_to_float (ctx_arg_u8 (7) ) );
+        break;
+        // XXX  implement TEXTURE
+#if 0
+      case CTX_LOAD_IMAGE:
+        {
+          if (image)
+            {
+              cairo_surface_destroy (image);
+              image = NULL;
+            }
+          if (pat)
+            {
+              cairo_pattern_destroy (pat);
+              pat = NULL;
+            }
+          image = cairo_image_surface_create_from_png (ctx_arg_string() );
+          cairo_set_source_surface (cr, image, ctx_arg_float (0), ctx_arg_float (1) );
+        }
+        break;
 #endif
-  if (!ret)
-  {
-    fprintf (stderr, "no interactive ctx backend\n");
-    ctx_list_backends ();
-    exit (2);
-  }
-  ctx_get_event (ret); // enables events
-  return ret;
+      case CTX_TEXT:
+        /* XXX: implement some linebreaking/wrap, positioning
+         *      behavior here
+         */
+        cairo_show_text (cr, ctx_arg_string () );
+        break;
+      case CTX_CONT:
+      case CTX_EDGE:
+      case CTX_DATA:
+      case CTX_DATA_REV:
+      case CTX_FLUSH:
+        break;
+    }
+  ctx_process (ctx_cairo->ctx, entry);
 }
-#endif
-#else
-void _ctx_texture_unlock (void)
+
+void ctx_cairo_free (CtxCairo *ctx_cairo)
+{
+  if (ctx_cairo->pat)
+    { cairo_pattern_destroy (ctx_cairo->pat); }
+  if (ctx_cairo->image)
+    { cairo_surface_destroy (ctx_cairo->image); }
+  free (ctx_cairo);
+}
+
+void
+ctx_render_cairo (Ctx *ctx, cairo_t *cr)
 {
+  CtxIterator iterator;
+  CtxCommand *command;
+  CtxCairo    ctx_cairo = {{(void*)ctx_cairo_process, NULL, NULL}, ctx, cr, NULL, NULL};
+  ctx_iterator_init (&iterator, &ctx->drawlist, 0,
+                     CTX_ITERATOR_EXPAND_BITPACK);
+  while ( (command = ctx_iterator_next (&iterator) ) )
+    { ctx_cairo_process (&ctx_cairo, command); }
 }
-void _ctx_texture_lock (void)
+
+Ctx *
+ctx_new_for_cairo (cairo_t *cr)
 {
+  Ctx *ctx = ctx_new ();
+  CtxCairo *ctx_cairo = calloc(sizeof(CtxCairo),1);
+  ctx_cairo->vfuncs.free = (void*)ctx_cairo_free;
+  ctx_cairo->vfuncs.process = (void*)ctx_cairo_process;
+  ctx_cairo->ctx = ctx;
+  ctx_cairo->cr = cr;
+
+  ctx_set_renderer (ctx, (void*)ctx_cairo);
+  return ctx;
 }
 
 #endif
-void _ctx_resized (Ctx *ctx, int width, int height, long time);
 
-void ctx_set_size (Ctx *ctx, int width, int height)
-{
 #if CTX_EVENTS
-  if (ctx->events.width != width || ctx->events.height != height)
+
+static int ctx_find_largest_matching_substring
+ (const char *X, const char *Y, int m, int n, int *offsetY, int *offsetX) 
+{ 
+  int longest_common_suffix[2][n+1];
+  int best_length = 0;
+  for (int i=0; i<=m; i++)
   {
-    ctx->events.width = width;
-    ctx->events.height = height;
-    _ctx_resized (ctx, width, height, 0);
+    for (int j=0; j<=n; j++)
+    {
+      if (i == 0 || j == 0 || !(X[i-1] == Y[j-1]))
+      {
+        longest_common_suffix[i%2][j] = 0;
+      }
+      else
+      {
+          longest_common_suffix[i%2][j] = longest_common_suffix[(i-1)%2][j-1] + 1;
+          if (best_length < longest_common_suffix[i%2][j])
+          {
+            best_length = longest_common_suffix[i%2][j];
+            if (offsetY) *offsetY = j - best_length;
+            if (offsetX) *offsetX = i - best_length;
+          }
+      }
+    }
   }
-#endif
-}
+  return best_length;
+} 
 
-#if CTX_EVENTS
+typedef struct CtxSpan {
+  int from_prev;
+  int start;
+  int length;
+} CtxSpan;
 
+#define CHUNK_SIZE 32
+#define MIN_MATCH  7        // minimum match length to be encoded
+#define WINDOW_PADDING 16   // look-aside amount
 
-static int is_in_ctx (void)
+#if 0
+static void _dassert(int line, int condition, const char *str, int foo, int bar, int baz)
 {
-  char buf[1024];
-  struct termios orig_attr;
-  struct termios raw;
-  tcgetattr (STDIN_FILENO, &orig_attr);
-  raw = orig_attr;
-  raw.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
-  raw.c_oflag &= ~(OPOST);
-  raw.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG);
-  raw.c_cc[VMIN] = 1; raw.c_cc[VTIME] = 0; /* 1 byte, no timer */
-  if (tcsetattr (STDIN_FILENO, TCSAFLUSH, &raw) < 0)
-    return 0;
-  fprintf (stderr, "\e[?200$p");
-  //tcflush(STDIN_FILENO, 1);
-#if !__COSMOPOLITAN__
-  tcdrain(STDIN_FILENO);
-#endif
-  int length = 0;
-  usleep (1000 * 60); // to account for possibly lowish latency ssh,
-                      // should be made configurable ; perhaps in
-                      // an env var
-  struct timeval tv = {0,0};
-  fd_set rfds;
-  
-  FD_ZERO(&rfds);
-  FD_SET(0, &rfds);
-  tv.tv_usec = 1000 * 5;
-
-  for (int n = 0; select(1, &rfds, NULL, NULL, &tv) && n < 20; n++)
-  {
-    length += read (STDIN_FILENO, &buf[length], 1);
-  }
-  tcsetattr (STDIN_FILENO, TCSAFLUSH, &orig_attr);
-  if (length == -1)
-  {
-    return 0;
-  }
-  char *semi = strchr (buf, ';');
-  buf[length]=0;
-  if (semi &&  semi[1] == '2')
+  if (!condition)
   {
-    return 1;
+    FILE *f = fopen ("/tmp/cdebug", "a");
+    fprintf (f, "%i: %s    %i %i %i\n", line, str, foo, bar, baz);
+    fclose (f);
   }
-  return 0;
 }
+#define dassert(cond, foo, bar, baz) _dassert(__LINE__, cond, #cond, foo, bar ,baz)
+#endif
+#define dassert(cond, foo, bar, baz)
 
-typedef struct CtxIdleCb {
-  int (*cb) (Ctx *ctx, void *idle_data);
-  void *idle_data;
-
-  void (*destroy_notify)(void *destroy_data);
-  void *destroy_data;
-
-  int   ticks_full;
-  int   ticks_remaining;
-  int   is_idle;
-  int   id;
-} CtxIdleCb;
-
-void _ctx_events_init (Ctx *ctx)
+/* XXX repeated substring matching is slow, we'll be
+ * better off with a hash-table with linked lists of
+ * matching 3-4 characters in previous.. or even
+ * a naive approach that expects rough alignment..
+ */
+static char *encode_in_terms_of_previous (
+                const char *src,  int src_len,
+                const char *prev, int prev_len,
+                int *out_len,
+                int max_ticks)
 {
-  CtxEvents *events = &ctx->events;
-  _ctx_init_ticks ();
-  events->tap_delay_min  = 40;
-  events->tap_delay_max  = 800;
-  events->tap_delay_max  = 8000000; /* quick reflexes needed making it hard for some is an argument against 
very short values  */
-
-  events->tap_delay_hold = 1000;
-  events->tap_hysteresis = 32;  /* XXX: should be ppi dependent */
-}
-
+  CtxString *string = ctx_string_new ("");
+  CtxList *encoded_list = NULL;
 
-void _ctx_idle_iteration (Ctx *ctx)
-{
-  static unsigned long prev_ticks = 0;
-  CtxList *l;
-  CtxList *to_remove = NULL;
-  unsigned long ticks = _ctx_ticks ();
-  unsigned long tick_delta = (prev_ticks == 0) ? 0 : ticks - prev_ticks;
-  prev_ticks = ticks;
+  /* TODO : make expected position offset in prev slide based on
+   * matches and not be constant */
 
-  if (!ctx->events.idles)
+  long ticks_start = ctx_ticks ();
+  int start = 0;
+  int length = CHUNK_SIZE;
+  for (start = 0; start < src_len; start += length)
   {
-    return;
-  }
-  for (l = ctx->events.idles; l; l = l->next)
+    CtxSpan *span = calloc (sizeof (CtxSpan), 1);
+    span->start = start;
+    if (start + length > src_len)
+      span->length = src_len - start;
+    else
+      span->length = length;
+    span->from_prev = 0;
+    ctx_list_append (&encoded_list, span);
+  }
+
+  for (CtxList *l = encoded_list; l; l = l->next)
   {
-    CtxIdleCb *item = l->data;
+    CtxSpan *span = l->data;
+    if (!span->from_prev)
+    {
+      if (span->length >= MIN_MATCH)
+      {
+         int prev_pos = 0;
+         int curr_pos = 0;
+         assert(1);
+#if 0
+         int prev_start =  0;
+         int prev_window_length = prev_len;
+#else
+         int window_padding = WINDOW_PADDING;
+         int prev_start = span->start - window_padding;
+         if (prev_start < 0)
+           prev_start = 0;
 
-    if (item->ticks_remaining >= 0)
-      item->ticks_remaining -= tick_delta;
+         dassert(span->start>=0 , 0,0,0);
 
-    if (item->ticks_remaining < 0)
-    {
-      if (item->cb (ctx, item->idle_data) == 0)
-        ctx_list_prepend (&to_remove, item);
-      else
-        item->ticks_remaining = item->ticks_full;
-    }
-  }
-  for (l = to_remove; l; l = l->next)
-  {
-    CtxIdleCb *item = l->data;
-    if (item->destroy_notify)
-      item->destroy_notify (item->destroy_data);
-    ctx_list_remove (&ctx->events.idles, l->data);
-  }
-}
+         int prev_window_length = prev_len - prev_start;
+         if (prev_window_length > span->length + window_padding * 2 + span->start)
+           prev_window_length = span->length + window_padding * 2 + span->start;
+#endif
+         int match_len = 0;
+         if (prev_window_length > 0)
+           match_len = ctx_find_largest_matching_substring(prev + prev_start, src + span->start, 
prev_window_length, span->length, &curr_pos, &prev_pos);
+#if 1
+         prev_pos += prev_start;
+#endif
 
+         if (match_len >= MIN_MATCH)
+         {
+            int start  = span->start;
+            int length = span->length;
 
-void ctx_add_key_binding_full (Ctx *ctx,
-                           const char *key,
-                           const char *action,
-                           const char *label,
-                           CtxCb       cb,
-                           void       *cb_data,
-                           CtxDestroyNotify destroy_notify,
-                           void       *destroy_data)
-{
-  CtxEvents *events = &ctx->events;
-  if (events->n_bindings +1 >= CTX_MAX_KEYBINDINGS)
-  {
-    fprintf (stderr, "warning: binding overflow\n");
-    return;
-  }
-  events->bindings[events->n_bindings].nick = strdup (key);
-  strcpy (events->bindings[events->n_bindings].nick, key);
+            span->from_prev = 1;
+            span->start     = prev_pos;
+            span->length    = match_len;
+            dassert (span->start >= 0, prev_pos, prev_start, span->start);
+            dassert (span->length > 0, prev_pos, prev_start, span->length);
 
-  if (action)
-    events->bindings[events->n_bindings].command = action ? strdup (action) : NULL;
-  if (label)
-    events->bindings[events->n_bindings].label = label ? strdup (label) : NULL;
-  events->bindings[events->n_bindings].cb = cb;
-  events->bindings[events->n_bindings].cb_data = cb_data;
-  events->bindings[events->n_bindings].destroy_notify = destroy_notify;
-  events->bindings[events->n_bindings].destroy_data = destroy_data;
-  events->n_bindings++;
-}
+            if (curr_pos)
+            {
+              CtxSpan *prev = calloc (sizeof (CtxSpan), 1);
+              prev->start = start;
+              prev->length =  curr_pos;
+            dassert (prev->start >= 0, prev_pos, prev_start, prev->start);
+            dassert (prev->length > 0, prev_pos, prev_start, prev->length);
+              prev->from_prev = 0;
+              ctx_list_insert_before (&encoded_list, l, prev);
+            }
 
-void ctx_add_key_binding (Ctx *ctx,
-                          const char *key,
-                          const char *action,
-                          const char *label,
-                          CtxCb       cb,
-                          void       *cb_data)
-{
-  ctx_add_key_binding_full (ctx, key, action, label, cb, cb_data, NULL, NULL);
-}
 
-void ctx_clear_bindings (Ctx *ctx)
-{
-  CtxEvents *events = &ctx->events;
-  int i;
-  for (i = 0; events->bindings[i].nick; i ++)
-  {
-    if (events->bindings[i].destroy_notify)
-      events->bindings[i].destroy_notify (events->bindings[i].destroy_data);
-    free (events->bindings[i].nick);
-    if (events->bindings[i].command)
-      free (events->bindings[i].command);
-    if (events->bindings[i].label)
-      free (events->bindings[i].label);
-  }
-  memset (&events->bindings, 0, sizeof (events->bindings));
-  events->n_bindings = 0;
-}
+            if (match_len + curr_pos < start + length)
+            {
+              CtxSpan *next = calloc (sizeof (CtxSpan), 1);
+              next->start = start + curr_pos + match_len;
+              next->length = (start + length) - next->start;
+            dassert (next->start >= 0, prev_pos, prev_start, next->start);
+      //    dassert (next->length > 0, prev_pos, prev_start, next->length);
+              next->from_prev = 0;
+              if (next->length)
+              {
+                if (l->next)
+                  ctx_list_insert_before (&encoded_list, l->next, next);
+                else
+                  ctx_list_append (&encoded_list, next);
+              }
+              else
+                free (next);
+            }
 
-static void
-ctx_collect_events (CtxEvent *event, void *data, void *data2);
-static void _ctx_bindings_key_press (CtxEvent *event, void *data1, void *data2)
-{
-  Ctx *ctx = event->ctx;
-  CtxEvents *events = &ctx->events;
-  int i;
-  int handled = 0;
+            if (curr_pos) // step one item back for forloop
+            {
+              CtxList *tmp = encoded_list;
+              int found = 0;
+              while (!found && tmp && tmp->next)
+              {
+                if (tmp->next == l)
+                {
+                  l = tmp;
+                  break;
+                }
+                tmp = tmp->next;
+              }
+            }
+         }
+      }
+    }
 
-  for (i = events->n_bindings-1; i>=0; i--)
-    if (!strcmp (events->bindings[i].nick, event->string))
+    if (ctx_ticks ()-ticks_start > (unsigned long)max_ticks)
+      break;
+  }
+
+  /* merge adjecant prev span references  */
+  {
+    for (CtxList *l = encoded_list; l; l = l->next)
     {
-      if (events->bindings[i].cb)
+      CtxSpan *span = l->data;
+again:
+      if (l->next)
       {
-        events->bindings[i].cb (event, events->bindings[i].cb_data, NULL);
-        if (event->stop_propagate)
-          return;
-        handled = 1;
+        CtxSpan *next_span = l->next->data;
+        if (span->from_prev && next_span->from_prev &&
+            span->start + span->length == 
+            next_span->start)
+        {
+           span->length += next_span->length;
+           ctx_list_remove (&encoded_list, next_span);
+           goto again;
+        }
       }
     }
-  if (!handled)
-  for (i = events->n_bindings-1; i>=0; i--)
-    if (!strcmp (events->bindings[i].nick, "unhandled"))
+  }
+
+  while (encoded_list)
+  {
+    CtxSpan *span = encoded_list->data;
+    if (span->from_prev)
     {
-      if (events->bindings[i].cb)
+      char ref[128];
+      sprintf (ref, "%c%i %i%c", CTX_CODEC_CHAR, span->start, span->length, CTX_CODEC_CHAR);
+      ctx_string_append_data (string, ref, strlen(ref));
+    }
+    else
+    {
+      for (int i = span->start; i< span->start+span->length; i++)
       {
-        events->bindings[i].cb (event, events->bindings[i].cb_data, NULL);
-        if (event->stop_propagate)
-          return;
+        if (src[i] == CTX_CODEC_CHAR)
+        {
+          ctx_string_append_byte (string, CTX_CODEC_CHAR);
+          ctx_string_append_byte (string, CTX_CODEC_CHAR);
+        }
+        else
+        {
+          ctx_string_append_byte (string, src[i]);
+        }
       }
     }
-  ctx_collect_events (event, data1, data2);
-}
+    free (span);
+    ctx_list_remove (&encoded_list, span);
+  }
 
-CtxBinding *ctx_get_bindings (Ctx *ctx)
-{
-  return &ctx->events.bindings[0];
+  char *ret = string->str;
+  if (out_len) *out_len = string->length;
+  ctx_string_free (string, 0);
+  return ret;
 }
 
-void ctx_remove_idle (Ctx *ctx, int handle)
+#if 0 // for documentation/reference purposes
+static char *decode_ctx (const char *encoded, int enc_len, const char *prev, int prev_len, int *out_len)
 {
-  CtxList *l;
-  CtxList *to_remove = NULL;
-
-  if (!ctx->events.idles)
-  {
-    return;
-  }
-  for (l = ctx->events.idles; l; l = l->next)
-  {
-    CtxIdleCb *item = l->data;
-    if (item->id == handle)
-      ctx_list_prepend (&to_remove, item);
-  }
-  for (l = to_remove; l; l = l->next)
+  CtxString *string = ctx_string_new ("");
+  char reference[32]="";
+  int ref_len = 0;
+  int in_ref = 0;
+  for (int i = 0; i < enc_len; i++)
   {
-    CtxIdleCb *item = l->data;
-    if (item->destroy_notify)
-      item->destroy_notify (item->destroy_data);
-    ctx_list_remove (&ctx->events.idles, l->data);
-  }
-}
+    if (encoded[i] == CTX_CODEC_CHAR)
+    {
+      if (!in_ref)
+      {
+        in_ref = 1;
+      }
+      else
+      {
+        int start = atoi (reference);
+        int len = 0;
+        if (strchr (reference, ' '))
+          len = atoi (strchr (reference, ' ')+1);
 
-int ctx_add_timeout_full (Ctx *ctx, int ms, int (*idle_cb)(Ctx *ctx, void *idle_data), void *idle_data,
-                          void (*destroy_notify)(void *destroy_data), void *destroy_data)
-{
-  CtxIdleCb *item = calloc (sizeof (CtxIdleCb), 1);
-  item->cb              = idle_cb;
-  item->idle_data       = idle_data;
-  item->id              = ++ctx->events.idle_id;
-  item->ticks_full      = 
-  item->ticks_remaining = ms * 1000;
-  item->destroy_notify  = destroy_notify;
-  item->destroy_data    = destroy_data;
-  ctx_list_append (&ctx->events.idles, item);
-  return item->id;
-}
+        if (start < 0)start = 0;
+        if (start >= prev_len)start = prev_len-1;
+        if (len + start > prev_len)
+          len = prev_len - start;
 
-int ctx_add_timeout (Ctx *ctx, int ms, int (*idle_cb)(Ctx *ctx, void *idle_data), void *idle_data)
-{
-  return ctx_add_timeout_full (ctx, ms, idle_cb, idle_data, NULL, NULL);
+        if (start == 0 && len == 0)
+          ctx_string_append_byte (string, CTX_CODEC_CHAR);
+        else
+          ctx_string_append_data (string, prev + start, len);
+        ref_len = 0;
+        in_ref = 0;
+      }
+    }
+    else
+    {
+      if (in_ref)
+      {
+        if (ref_len < 16)
+        {
+          reference[ref_len++] = encoded[i];
+          reference[ref_len] = 0;
+        }
+      }
+      else
+      ctx_string_append_byte (string, encoded[i]);
+    }
+  }
+  char *ret = string->str;
+  if (out_len) *out_len = string->length;
+  ctx_string_free (string, 0);
+  return ret;
 }
+#endif
 
-int ctx_add_idle_full (Ctx *ctx, int (*idle_cb)(Ctx *ctx, void *idle_data), void *idle_data,
-                                 void (*destroy_notify)(void *destroy_data), void *destroy_data)
-{
-  CtxIdleCb *item = calloc (sizeof (CtxIdleCb), 1);
-  item->cb = idle_cb;
-  item->idle_data = idle_data;
-  item->id = ++ctx->events.idle_id;
-  item->ticks_full =
-  item->ticks_remaining = -1;
-  item->is_idle = 1;
-  item->destroy_notify = destroy_notify;
-  item->destroy_data = destroy_data;
-  ctx_list_append (&ctx->events.idles, item);
-  return item->id;
-}
+#define CTX_START_STRING "U\n"  // or " reset "
+#define CTX_END_STRING   "\nX"  // or "\ndone"
+#define CTX_END_STRING2  "\n\e"
 
-int ctx_add_idle (Ctx *ctx, int (*idle_cb)(Ctx *ctx, void *idle_data), void *idle_data)
-{
-  return ctx_add_idle_full (ctx, idle_cb, idle_data, NULL, NULL);
-}
+int ctx_frame_ack = -1;
+static char *prev_frame_contents = NULL;
+static int   prev_frame_len = 0;
 
-#endif
-/* using bigger primes would be a good idea, this falls apart due to rounding
- * when zoomed in close
- */
-static inline double ctx_path_hash (void *path)
+static void ctx_ctx_flush (CtxCtx *ctxctx)
 {
-  double ret = 0;
 #if 0
-  int i;
-  cairo_path_data_t *data;
-  if (!path)
-    return 0.99999;
-  for (i = 0; i <path->num_data; i += path->data[i].header.length)
-  {
-    data = &path->data[i];
-    switch (data->header.type) {
-      case CAIRO_PATH_MOVE_TO:
-        ret *= 17;
-        ret += data[1].point.x;
-        ret *= 113;
-        ret += data[1].point.y;
-        break;
-      case CAIRO_PATH_LINE_TO:
-        ret *= 121;
-        ret += data[1].point.x;
-        ret *= 1021;
-        ret += data[1].point.y;
-        break;
-      case CAIRO_PATH_CURVE_TO:
-        ret *= 3111;
-        ret += data[1].point.x;
-        ret *= 23;
-        ret += data[1].point.y;
-        ret *= 107;
-        ret += data[2].point.x;
-        ret *= 739;
-        ret += data[2].point.y;
-        ret *= 3;
-        ret += data[3].point.x;
-        ret *= 51;
-        ret += data[3].point.y;
-        break;
-      case CAIRO_PATH_CLOSE_PATH:
-        ret *= 51;
-        break;
-    }
-  }
+  FILE *debug = fopen ("/tmp/ctx-debug", "a");
+  fprintf (debug, "------\n");
 #endif
-  return ret;
-}
 
-#if CTX_EVENTS
-void _ctx_item_ref (CtxItem *item)
-{
-  if (item->ref_count < 0)
+  if (ctx_native_events)
+    fprintf (stdout, "\e[?201h");
+  fprintf (stdout, "\e[H\e[?25l\e[?200h");
+#if 0
+  fprintf (stdout, CTX_START_STRING);
+  ctx_render_stream (ctxctx->ctx, stdout, 0);
+  fprintf (stdout, CTX_END_STRING);
+#else
   {
-    fprintf (stderr, "EEEEK!\n");
-  }
-  item->ref_count++;
-}
+    int cur_frame_len = 0;
+    char *rest = ctx_render_string (ctxctx->ctx, 0, &cur_frame_len);
+    char *cur_frame_contents = malloc (cur_frame_len + strlen(CTX_START_STRING) + strlen (CTX_END_STRING) + 
1);
 
+    cur_frame_contents[0]=0;
+    strcat (cur_frame_contents, CTX_START_STRING);
+    strcat (cur_frame_contents, rest);
+    strcat (cur_frame_contents, CTX_END_STRING);
+    free (rest);
+    cur_frame_len += strlen (CTX_START_STRING) + strlen (CTX_END_STRING);
 
-void _ctx_item_unref (CtxItem *item)
-{
-  if (item->ref_count <= 0)
-  {
-    fprintf (stderr, "EEEEK!\n");
-    return;
-  }
-  item->ref_count--;
-  if (item->ref_count <=0)
-  {
+    if (prev_frame_contents && 0)  // XXX : 
     {
-      int i;
-      for (i = 0; i < item->cb_count; i++)
-      {
-        if (item->cb[i].finalize)
-          item->cb[i].finalize (item->cb[i].data1, item->cb[i].data2,
-                                   item->cb[i].finalize_data);
-      }
+      char *encoded;
+      int encoded_len = 0;
+      //uint64_t ticks_start = ctx_ticks ();
+
+      encoded = encode_in_terms_of_previous (cur_frame_contents, cur_frame_len, prev_frame_contents, 
prev_frame_len, &encoded_len, 1000 * 10);
+//    encoded = strdup (cur_frame_contents);
+//    encoded_len = strlen (encoded);
+      //uint64_t ticks_end = ctx_ticks ();
+
+      fwrite (encoded, encoded_len, 1, stdout);
+//    fwrite (encoded, cur_frame_len, 1, stdout);
+#if 0
+      fprintf (debug, "---prev-frame(%i)\n%s", (int)strlen(prev_frame_contents), prev_frame_contents);
+      fprintf (debug, "---cur-frame(%i)\n%s", (int)strlen(cur_frame_contents), cur_frame_contents);
+      fprintf (debug, "---encoded(%.4f %i)---\n%s--------\n",
+                      (ticks_end-ticks_start)/1000.0,
+                      (int)strlen(encoded), encoded);
+#endif
+      free (encoded);
     }
-    if (item->path)
+    else
     {
-      //cairo_path_destroy (item->path);
+      fwrite (cur_frame_contents, cur_frame_len, 1, stdout);
     }
-    free (item);
+
+    if (prev_frame_contents)
+      free (prev_frame_contents);
+    prev_frame_contents = cur_frame_contents;
+    prev_frame_len = cur_frame_len;
   }
-}
+#endif
+#if 0
+    fclose (debug);
+#endif
+  fprintf (stdout, CTX_END_STRING2);
 
+  fprintf (stdout, "\e[5n");
+  fflush (stdout);
 
-static int
-path_equal (void *path,
-            void *path2)
-{
-  //  XXX
-  return 0;
+  ctx_frame_ack = 0;
+  do {
+     ctx_consume_events (ctxctx->ctx);
+  } while (ctx_frame_ack != 1);
 }
 
-void ctx_listen_set_cursor (Ctx      *ctx,
-                            CtxCursor cursor)
+void ctx_ctx_free (CtxCtx *ctx)
 {
-  if (ctx->events.last_item)
-  {
-    ctx->events.last_item->cursor = cursor;
-  }
+  nc_at_exit ();
+  free (ctx);
+  /* we're not destoring the ctx member, this is function is called in ctx' teardown */
 }
 
-void ctx_listen_full (Ctx     *ctx,
-                      float    x,
-                      float    y,
-                      float    width,
-                      float    height,
-                      CtxEventType  types,
-                      CtxCb    cb,
-                      void    *data1,
-                      void    *data2,
-                      void   (*finalize)(void *listen_data,
-                                         void *listen_data2,
-                                         void *finalize_data),
-                      void    *finalize_data)
+Ctx *ctx_new_ctx (int width, int height)
 {
-  if (!ctx->events.frozen)
+  Ctx *ctx = ctx_new ();
+  CtxCtx *ctxctx = (CtxCtx*)calloc (sizeof (CtxCtx), 1);
+  fprintf (stdout, "\e[?1049h");
+  fflush (stdout);
+  //fprintf (stderr, "\e[H");
+  //fprintf (stderr, "\e[2J");
+  ctx_native_events = 1;
+  if (width <= 0 || height <= 0)
   {
-    CtxItem *item;
+    ctxctx->cols = ctx_terminal_cols ();
+    ctxctx->rows = ctx_terminal_rows ();
+    width  = ctxctx->width  = ctx_terminal_width ();
+    height = ctxctx->height = ctx_terminal_height ();
+  }
+  else
+  {
+    ctxctx->width  = width;
+    ctxctx->height = height;
+    ctxctx->cols   = width / 80;
+    ctxctx->rows   = height / 24;
+  }
+  ctxctx->ctx = ctx;
+  if (!ctx_native_events)
+    _ctx_mouse (ctx, NC_MOUSE_DRAG);
+  ctx_set_renderer (ctx, ctxctx);
+  ctx_set_size (ctx, width, height);
+  ctxctx->flush = (void(*)(void *))ctx_ctx_flush;
+  ctxctx->free  = (void(*)(void *))ctx_ctx_free;
+  return ctx;
+}
 
-    /* early bail for listeners outside screen  */
-    /* XXX: fixme respect clipping */
+void ctx_ctx_pcm (Ctx *ctx);
+
+int ctx_ctx_consume_events (Ctx *ctx)
+{
+  int ix, iy;
+  CtxCtx *ctxctx = (CtxCtx*)ctx->renderer;
+  const char *event = NULL;
+#if CTX_AUDIO
+  ctx_ctx_pcm (ctx);
+#endif
+  if (ctx_native_events)
     {
-      float tx = x;
-      float ty = y;
-      float tw = width;
-      float th = height;
-      _ctx_user_to_device (&ctx->state, &tx, &ty);
-      _ctx_user_to_device_distance (&ctx->state, &tw, &th);
-      if (ty > ctx->events.height * 2 ||
-          tx > ctx->events.width * 2 ||
-          tx + tw < 0 ||
-          ty + th < 0)
+      float x = 0, y = 0;
+      int b = 0;
+      char event_type[128]="";
+      event = ctx_native_get_event (ctx, 1000/120);
+#if 0
+      if(event){
+        FILE *file = fopen ("/tmp/log", "a");
+        fprintf (file, "[%s]\n", event);
+        fclose (file);
+      }
+#endif
+      if (event)
       {
-        if (finalize)
-          finalize (data1, data2, finalize_data);
-        return;
+      sscanf (event, "%s %f %f %i", event_type, &x, &y, &b);
+      if (!strcmp (event_type, "idle"))
+      {
+      }
+      else if (!strcmp (event_type, "mouse-press"))
+      {
+        ctx_pointer_press (ctx, x, y, b, 0);
+      }
+      else if (!strcmp (event_type, "mouse-drag")||
+               !strcmp (event_type, "mouse-motion"))
+      {
+        ctx_pointer_motion (ctx, x, y, b, 0);
+      }
+      else if (!strcmp (event_type, "mouse-release"))
+      {
+        ctx_pointer_release (ctx, x, y, b, 0);
+      }
+      else if (!strcmp (event_type, "message"))
+      {
+        ctx_incoming_message (ctx, event + strlen ("message"), 0);
+      } else if (!strcmp (event, "size-changed"))
+      {
+        fprintf (stdout, "\e[H\e[2J\e[?25l");
+        ctxctx->cols = ctx_terminal_cols ();
+        ctxctx->rows = ctx_terminal_rows ();
+        ctxctx->width  = ctx_terminal_width ();
+        ctxctx->height = ctx_terminal_height ();
+        ctx_set_size (ctx, ctxctx->width, ctxctx->height);
+
+        if (prev_frame_contents)
+          free (prev_frame_contents);
+        prev_frame_contents = NULL;
+        prev_frame_len = 0;
+        ctx_set_dirty (ctx, 1);
+        //ctx_key_press (ctx, 0, "size-changed", 0);
+      }
+      else if (!strcmp (event_type, "keyup"))
+      {
+        char buf[4]={ x, 0 };
+        ctx_key_up (ctx, (int)x, buf, 0);
+      }
+      else if (!strcmp (event_type, "keydown"))
+      {
+        char buf[4]={ x, 0 };
+        ctx_key_down (ctx, (int)x, buf, 0);
+      }
+      else
+      {
+        ctx_key_press (ctx, 0, event, 0);
+      }
       }
     }
+  else
+    {
+      float x, y;
+      event = ctx_nct_get_event (ctx, 20, &ix, &iy);
 
-    item = calloc (sizeof (CtxItem), 1);
-    item->x0 = x;
-    item->y0 = y;
-    item->x1 = x + width;
-    item->y1 = y + height;
-    item->cb[0].types = types;
-    item->cb[0].cb = cb;
-    item->cb[0].data1 = data1;
-    item->cb[0].data2 = data2;
-    item->cb[0].finalize = finalize;
-    item->cb[0].finalize_data = finalize_data;
-    item->cb_count = 1;
-    item->types = types;
-    //item->path = cairo_copy_path (cr); // XXX
-    item->path_hash = ctx_path_hash (item->path);
-    ctx_get_matrix (ctx, &item->inv_matrix);
-    ctx_matrix_invert (&item->inv_matrix);
+      x = (ix - 1.0 + 0.5) / ctxctx->cols * ctx->events.width;
+      y = (iy - 1.0)       / ctxctx->rows * ctx->events.height;
 
-    if (ctx->events.items)
-    {
-      CtxList *l;
-      for (l = ctx->events.items; l; l = l->next)
+      if (!strcmp (event, "mouse-press"))
       {
-        CtxItem *item2 = l->data;
-
-        /* store multiple callbacks for one entry when the paths
-         * are exact matches, reducing per event traversal checks at the
-         * cost of a little paint-hit (XXX: is this the right tradeoff,
-         * perhaps it is better to spend more time during event processing
-         * than during paint?)
-         */
-        if (item->path_hash == item2->path_hash &&
-            path_equal (item->path, item2->path))
+        ctx_pointer_press (ctx, x, y, 0, 0);
+        ctxctx->was_down = 1;
+      } else if (!strcmp (event, "mouse-release"))
+      {
+        ctx_pointer_release (ctx, x, y, 0, 0);
+      } else if (!strcmp (event, "mouse-motion"))
+      {
+        //nct_set_cursor_pos (backend->term, ix, iy);
+        //nct_flush (backend->term);
+        if (ctxctx->was_down)
         {
-          /* found an item, copy over cb data  */
-          item2->cb[item2->cb_count] = item->cb[0];
-          free (item);
-          item2->cb_count++;
-          item2->types |= types;
-          return;
+          ctx_pointer_release (ctx, x, y, 0, 0);
+          ctxctx->was_down = 0;
         }
+        ctx_pointer_motion (ctx, x, y, 0, 0);
+      } else if (!strcmp (event, "mouse-drag"))
+      {
+        ctx_pointer_motion (ctx, x, y, 0, 0);
+      } else if (!strcmp (event, "size-changed"))
+      {
+        fprintf (stdout, "\e[H\e[2J\e[?25l");
+        ctxctx->cols = ctx_terminal_cols ();
+        ctxctx->rows = ctx_terminal_rows ();
+        ctxctx->width  = ctx_terminal_width ();
+        ctxctx->height = ctx_terminal_height ();
+        ctx_set_size (ctx, ctxctx->width, ctxctx->height);
+
+        if (prev_frame_contents)
+           free (prev_frame_contents);
+        prev_frame_contents = NULL;
+        prev_frame_len = 0;
+        ctx_set_dirty (ctx, 1);
+        //ctx_key_press (ctx, 0, "size-changed", 0);
+      }
+      else
+      {
+        if (!strcmp (event, "esc"))
+          ctx_key_press (ctx, 0, "escape", 0);
+        else if (!strcmp (event, "space"))
+          ctx_key_press (ctx, 0, "space", 0);
+        else if (!strcmp (event, "enter")||
+                 !strcmp (event, "return"))
+          ctx_key_press (ctx, 0, "\n", 0);
+        else
+        ctx_key_press (ctx, 0, event, 0);
       }
     }
-    item->ref_count       = 1;
-    ctx->events.last_item = item;
-    ctx_list_prepend_full (&ctx->events.items, item, (void*)_ctx_item_unref, NULL);
-  }
-}
 
-void ctx_event_stop_propagate (CtxEvent *event)
-{
-  if (event)
-    event->stop_propagate = 1;
+  return 1;
 }
 
-void ctx_listen (Ctx          *ctx,
-                 CtxEventType  types,
-                 CtxCb         cb,
-                 void*         data1,
-                 void*         data2)
+int ctx_renderer_is_ctx (Ctx *ctx)
 {
-  float x, y, width, height;
-  /* generate bounding box of what to listen for - from current cairo path */
-  if (types & CTX_KEY)
-  {
-    x = 0;
-    y = 0;
-    width = 0;
-    height = 0;
-  }
-  else
-  {
-     float ex1,ey1,ex2,ey2;
-     ctx_path_extents (ctx, &ex1, &ey1, &ex2, &ey2);
-     x = ex1;
-     y = ey1;
-     width = ex2 - ex1;
-     height = ey2 - ey1;
-  }
-
-  if (types == CTX_DRAG_MOTION)
-    types = CTX_DRAG_MOTION | CTX_DRAG_PRESS;
-  return ctx_listen_full (ctx, x, y, width, height, types, cb, data1, data2, NULL, NULL);
+  if (ctx->renderer &&
+      ctx->renderer->free == (void*)ctx_ctx_free)
+          return 1;
+  return 0;
 }
 
-void  ctx_listen_with_finalize (Ctx          *ctx,
-                                CtxEventType  types,
-                                CtxCb         cb,
-                                void*         data1,
-                                void*         data2,
-                      void   (*finalize)(void *listen_data, void *listen_data2,
-                                         void *finalize_data),
-                      void    *finalize_data)
+#endif
+
+#if CTX_TILED
+static inline int
+ctx_tiled_threads_done (CtxTiled *tiled)
 {
-  float x, y, width, height;
-  /* generate bounding box of what to listen for - from current cairo path */
-  if (types & CTX_KEY)
-  {
-    x = 0;
-    y = 0;
-    width = 0;
-    height = 0;
-  }
-  else
+  int sum = 0;
+  for (int i = 0; i < _ctx_max_threads; i++)
   {
-     float ex1,ey1,ex2,ey2;
-     ctx_path_extents (ctx, &ex1, &ey1, &ex2, &ey2);
-     x = ex1;
-     y = ey1;
-     width = ex2 - ex1;
-     height = ey2 - ey1;
+     if (tiled->rendered_frame[i] == tiled->render_frame)
+       sum ++;
   }
-
-  if (types == CTX_DRAG_MOTION)
-    types = CTX_DRAG_MOTION | CTX_DRAG_PRESS;
-  return ctx_listen_full (ctx, x, y, width, height, types, cb, data1, data2, finalize, finalize_data);
+  return sum;
 }
 
+int _ctx_damage_control = 0;
 
-static void ctx_report_hit_region (CtxEvent *event,
-                       void     *data,
-                       void     *data2)
+void ctx_tiled_free (CtxTiled *tiled)
 {
-  const char *id = data;
+  tiled->quit = 1;
+  mtx_lock (&tiled->mtx);
+  cnd_broadcast (&tiled->cond);
+  mtx_unlock (&tiled->mtx);
 
-  fprintf (stderr, "hit region %s\n", id);
-  // XXX: NYI
-}
+  while (tiled->thread_quit < _ctx_max_threads)
+    usleep (1000);
 
-void ctx_add_hit_region (Ctx *ctx, const char *id)
-{
-  char *id_copy = strdup (id);
-  float x, y, width, height;
-  /* generate bounding box of what to listen for - from current cairo path */
+  if (tiled->pixels)
   {
-     float ex1,ey1,ex2,ey2;
-     ctx_path_extents (ctx, &ex1, &ey1, &ex2, &ey2);
-     x = ex1;
-     y = ey1;
-     width = ex2 - ex1;
-     height = ey2 - ey1;
-  }
-  
-  return ctx_listen_full (ctx, x, y, width, height,
-                          CTX_POINTER, ctx_report_hit_region,
-                          id_copy, NULL, (void*)free, NULL);
-}
-
-typedef struct _CtxGrab CtxGrab;
-
-struct _CtxGrab
-{
-  CtxItem *item;
-  int      device_no;
-  int      timeout_id;
-  int      start_time;
-  float    x; // for tap and hold
-  float    y;
-  CtxEventType  type;
-};
-
-static void grab_free (Ctx *ctx, CtxGrab *grab)
-{
-  if (grab->timeout_id)
+    free (tiled->pixels);
+  tiled->pixels = NULL;
+  for (int i = 0 ; i < _ctx_max_threads; i++)
   {
-    ctx_remove_idle (ctx, grab->timeout_id);
-    grab->timeout_id = 0;
+    ctx_free (tiled->host[i]);
+    tiled->host[i]=NULL;
   }
-  _ctx_item_unref (grab->item);
-  free (grab);
-}
-
-static void device_remove_grab (Ctx *ctx, CtxGrab *grab)
-{
-  ctx_list_remove (&ctx->events.grabs, grab);
-  grab_free (ctx, grab);
-}
-
-static CtxGrab *device_add_grab (Ctx *ctx, int device_no, CtxItem *item, CtxEventType type)
-{
-  CtxGrab *grab = calloc (1, sizeof (CtxGrab));
-  grab->item = item;
-  grab->type = type;
-  _ctx_item_ref (item);
-  grab->device_no = device_no;
-  ctx_list_append (&ctx->events.grabs, grab);
-  return grab;
-}
 
-static CtxList *_ctx_device_get_grabs (Ctx *ctx, int device_no)
-{
-  CtxList *ret = NULL;
-  CtxList *l;
-  for (l = ctx->events.grabs; l; l = l->next)
-  {
-    CtxGrab *grab = l->data;
-    if (grab->device_no == device_no)
-      ctx_list_append (&ret, grab);
+  ctx_free (tiled->ctx_copy);
   }
-  return ret;
-}
-
-static void _mrg_restore_path (Ctx *ctx, void *path)  //XXX
-{
-  //int i;
-  //cairo_path_data_t *data;
-  //cairo_new_path (cr);
-  //cairo_append_path (cr, path);
+  // leak?
 }
+static unsigned char *sdl_icc = NULL;
+static long sdl_icc_length = 0;
 
-CtxList *_ctx_detect_list (Ctx *ctx, float x, float y, CtxEventType type)
+inline static void ctx_tiled_flush (CtxTiled *tiled)
 {
-  CtxList *a;
-  CtxList *ret = NULL;
-
-  if (type == CTX_KEY_DOWN ||
-      type == CTX_KEY_UP ||
-      type == CTX_KEY_PRESS ||
-      type == CTX_MESSAGE ||
-      type == (CTX_KEY_DOWN|CTX_MESSAGE) ||
-      type == (CTX_KEY_DOWN|CTX_KEY_UP) ||
-      type == (CTX_KEY_DOWN|CTX_KEY_UP|CTX_MESSAGE))
+  if (tiled->shown_frame == tiled->render_frame)
   {
-    for (a = ctx->events.items; a; a = a->next)
+    int dirty_tiles = 0;
+    ctx_set_drawlist (tiled->ctx_copy, &tiled->ctx->drawlist.entries[0],
+                                           tiled->ctx->drawlist.count * 9);
+    if (_ctx_enable_hash_cache)
     {
-      CtxItem *item = a->data;
-      if (item->types & type)
-      {
-        ctx_list_prepend (&ret, item);
-        return ret;
-      }
-    }
-    return NULL;
-  }
-
-  for (a = ctx->events.items; a; a = a->next)
-  {
-    CtxItem *item= a->data;
-  
-    float u, v;
-    u = x;
-    v = y;
-    _ctx_matrix_apply_transform (&item->inv_matrix, &u, &v);
+      Ctx *hasher = ctx_hasher_new (tiled->width, tiled->height,
+                        CTX_HASH_COLS, CTX_HASH_ROWS);
+      ctx_render_ctx (tiled->ctx_copy, hasher);
 
-    if (u >= item->x0 && v >= item->y0 &&
-        u <  item->x1 && v <  item->y1 && 
-        ((item->types & type) || ((type == CTX_SET_CURSOR) &&
-        item->cursor)))
+      for (int row = 0; row < CTX_HASH_ROWS; row++)
+        for (int col = 0; col < CTX_HASH_COLS; col++)
+        {
+          uint8_t *new_hash = ctx_hasher_get_hash (hasher, col, row);
+          if (new_hash && memcmp (new_hash, &tiled->hashes[(row * CTX_HASH_COLS + col) *  20], 20))
+          {
+            memcpy (&tiled->hashes[(row * CTX_HASH_COLS +  col)*20], new_hash, 20);
+            tiled->tile_affinity[row * CTX_HASH_COLS + col] = 1;
+            dirty_tiles++;
+          }
+          else
+          {
+            tiled->tile_affinity[row * CTX_HASH_COLS + col] = -1;
+          }
+        }
+      free (((CtxHasher*)(hasher->renderer))->hashes);
+      ctx_free (hasher);
+    }
+    else
     {
-      if (item->path)
+    for (int row = 0; row < CTX_HASH_ROWS; row++)
+      for (int col = 0; col < CTX_HASH_COLS; col++)
+        {
+          tiled->tile_affinity[row * CTX_HASH_COLS + col] = 1;
+          dirty_tiles++;
+        }
+    }
+    int dirty_no = 0;
+    if (dirty_tiles)
+    for (int row = 0; row < CTX_HASH_ROWS; row++)
+      for (int col = 0; col < CTX_HASH_COLS; col++)
       {
-        _mrg_restore_path (ctx, item->path);
-        if (ctx_in_fill (ctx, u, v))
+        if (tiled->tile_affinity[row * CTX_HASH_COLS + col] != -1)
         {
-          ctx_begin_path (ctx);
-          ctx_list_prepend (&ret, item);
+          tiled->tile_affinity[row * CTX_HASH_COLS + col] = dirty_no * (_ctx_max_threads) / dirty_tiles;
+          dirty_no++;
+          if (col > tiled->max_col) tiled->max_col = col;
+          if (col < tiled->min_col) tiled->min_col = col;
+          if (row > tiled->max_row) tiled->max_row = row;
+          if (row < tiled->min_row) tiled->min_row = row;
         }
-        ctx_begin_path (ctx);
       }
-      else
+
+    if (_ctx_damage_control)
+    {
+      for (int i = 0; i < tiled->width * tiled->height; i++)
       {
-        ctx_list_prepend (&ret, item);
+        tiled->pixels[i*4+2]  = (tiled->pixels[i*4+2] + 255)/2;
       }
     }
-  }
-  return ret;
-}
 
-CtxItem *_ctx_detect (Ctx *ctx, float x, float y, CtxEventType type)
-{
-  CtxList *l = _ctx_detect_list (ctx, x, y, type);
-  if (l)
-  {
-    ctx_list_reverse (&l);
-    CtxItem *ret = l->data;
-    ctx_list_free (&l);
-    return ret;
-  }
-  return NULL;
-}
+    tiled->render_frame = ++tiled->frame;
 
-static int
-_ctx_emit_cb_item (Ctx *ctx, CtxItem *item, CtxEvent *event, CtxEventType type, float x, float y)
-{
-  static CtxEvent s_event;
-  CtxEvent transformed_event;
-  int i;
+#if 0
 
+          //if (tiled->tile_affinity[hno]==no)
+          {
+            int x0 = ((tiled->width)/CTX_HASH_COLS) * 0;
+            int y0 = ((tiled->height)/CTX_HASH_ROWS) * 0;
+            int width = tiled->width / CTX_HASH_COLS;
+            int height = tiled->height / CTX_HASH_ROWS;
+            Ctx *host = tiled->host[0];
 
-  if (!event)
-  {
-    event = &s_event;
-    event->type = type;
-    event->x = x;
-    event->y = y;
-  }
-  event->ctx = ctx;
-  transformed_event = *event;
-  transformed_event.device_x = event->x;
-  transformed_event.device_y = event->y;
-
-  {
-    float tx, ty;
-    tx = transformed_event.x;
-    ty = transformed_event.y;
-    _ctx_matrix_apply_transform (&item->inv_matrix, &tx, &ty);
-    transformed_event.x = tx;
-    transformed_event.y = ty;
+            CtxRasterizer *rasterizer = (CtxRasterizer*)host->renderer;
+            int swap_red_green = ((CtxRasterizer*)(host->renderer))->swap_red_green;
+            ctx_rasterizer_init (rasterizer,
+                                 host, tiled->ctx, &host->state,
+                                 &tiled->pixels[tiled->width * 4 * y0 + x0 * 4],
+                                 0, 0, 1, 1,
+                                 tiled->width*4, CTX_FORMAT_RGBA8,
+                                 tiled->antialias);
+            //((CtxRasterizer*)(host->renderer))->swap_red_green = swap_red_green;
+            if (sdl_icc_length)
+              ctx_colorspace (host, CTX_COLOR_SPACE_DEVICE_RGB, sdl_icc, sdl_icc_length);
 
-    if ((type & CTX_DRAG_PRESS) ||
-        (type & CTX_DRAG_MOTION) ||
-        (type & CTX_MOTION))   /* probably a worthwhile check for the performance 
-                                  benefit
-                                */
-    {
-      tx = transformed_event.start_x;
-      ty = transformed_event.start_y;
-      _ctx_matrix_apply_transform (&item->inv_matrix, &tx, &ty);
-      transformed_event.start_x = tx;
-      transformed_event.start_y = ty;
-    }
+            ctx_translate (host, -x0, -y0);
+            ctx_render_ctx (tiled->ctx_copy, host);
+          }
+#endif
 
 
-    tx = transformed_event.delta_x;
-    ty = transformed_event.delta_y;
-    _ctx_matrix_apply_transform (&item->inv_matrix, &tx, &ty);
-    transformed_event.delta_x = tx;
-    transformed_event.delta_y = ty;
+    mtx_lock (&tiled->mtx);
+    cnd_broadcast (&tiled->cond);
+    mtx_unlock (&tiled->mtx);
   }
+}
 
-  transformed_event.state = ctx->events.modifier_state;
-  transformed_event.type = type;
+static
+void ctx_tiled_render_fun (void **data)
+{
+  int      no = (size_t)data[0];
+  CtxTiled *tiled = data[1];
 
-  for (i = item->cb_count-1; i >= 0; i--)
+  while (!tiled->quit)
   {
-    if (item->cb[i].types & type)
+    Ctx *host = tiled->host[no];
+
+    mtx_lock (&tiled->mtx);
+    cnd_wait(&tiled->cond, &tiled->mtx);
+    mtx_unlock (&tiled->mtx);
+
+    if (tiled->render_frame != tiled->rendered_frame[no])
     {
-      item->cb[i].cb (&transformed_event, item->cb[i].data1, item->cb[i].data2);
-      event->stop_propagate = transformed_event.stop_propagate; /* copy back the response */
-      if (event->stop_propagate)
-        return event->stop_propagate;
+      int hno = 0;
+      for (int row = 0; row < CTX_HASH_ROWS; row++)
+        for (int col = 0; col < CTX_HASH_COLS; col++, hno++)
+        {
+          if (tiled->tile_affinity[hno]==no)
+          {
+            int x0 = ((tiled->width)/CTX_HASH_COLS) * col;
+            int y0 = ((tiled->height)/CTX_HASH_ROWS) * row;
+            int width = tiled->width / CTX_HASH_COLS;
+            int height = tiled->height / CTX_HASH_ROWS;
+
+            CtxRasterizer *rasterizer = (CtxRasterizer*)host->renderer;
+#if 1 // merge horizontally adjecant tiles of same affinity into one job
+            while (col + 1 < CTX_HASH_COLS &&
+                   tiled->tile_affinity[hno+1] == no)
+            {
+              width += tiled->width / CTX_HASH_COLS;
+              col++;
+              hno++;
+            }
+#endif
+            int swap_red_green = ((CtxRasterizer*)(host->renderer))->swap_red_green;
+            ctx_rasterizer_init (rasterizer,
+                                 host, tiled->ctx, &host->state,
+                                 &tiled->pixels[tiled->width * 4 * y0 + x0 * 4],
+                                 0, 0, width, height,
+                                 tiled->width*4, CTX_FORMAT_RGBA8,
+                                 tiled->antialias);
+            ((CtxRasterizer*)(host->renderer))->swap_red_green = swap_red_green;
+            if (sdl_icc_length)
+              ctx_colorspace (host, CTX_COLOR_SPACE_DEVICE_RGB, sdl_icc, sdl_icc_length);
+
+            ctx_translate (host, -x0, -y0);
+            ctx_render_ctx (tiled->ctx_copy, host);
+          }
+        }
+      tiled->rendered_frame[no] = tiled->render_frame;
     }
   }
-  return 0;
+  tiled->thread_quit++; // need atomic?
 }
+
 #endif
 
+
 #if CTX_EVENTS
 
 #if !__COSMOPOLITAN__
-#include <stdatomic.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <signal.h>
 #endif
 
-int ctx_native_events = 0;
-#if CTX_SDL
-int ctx_sdl_events = 0;
-int ctx_sdl_consume_events (Ctx *ctx);
-#endif
 
 #if CTX_FB
-int ctx_fb_events = 0;
-int ctx_fb_consume_events (Ctx *ctx);
-#endif
-
-int ctx_nct_consume_events (Ctx *ctx);
-int ctx_nct_has_event (Ctx  *n, int delay_ms);
-int ctx_ctx_consume_events (Ctx *ctx);
-
+  #include <linux/fb.h>
+  #include <linux/vt.h>
+  #include <linux/kd.h>
+  #include <sys/mman.h>
+  #include <threads.h>
+  #include <libdrm/drm.h>
+  #include <libdrm/drm_mode.h>
 
+typedef struct _EvSource EvSource;
+ 
 
-void ctx_consume_events (Ctx *ctx)
+struct _EvSource
 {
-#if CTX_SDL
-  if (ctx_sdl_events)
-    ctx_sdl_consume_events (ctx);
-  else
-#endif
-#if CTX_FB
-  if (ctx_fb_events)
-    ctx_fb_consume_events (ctx);
-  else
-#endif
-  if (ctx_native_events)
-    ctx_ctx_consume_events (ctx);
-  else
-    ctx_nct_consume_events (ctx);
-}
+  void   *priv; /* private storage  */
 
-int ctx_has_event (Ctx *ctx, int timeout)
-{
-#if CTX_SDL
-  if (ctx_sdl_events)
-  {
-    return SDL_WaitEventTimeout (NULL, timeout);
-  }
-  else
-#endif
-#if CTX_FB
-  if (ctx_fb_events)
-  {
-    return ctx_nct_has_event (ctx, timeout);
-  }
-  else
-#endif
-  if (ctx_native_events)
-  {
-    return ctx_nct_has_event (ctx, timeout);
-  }
-  else
-  {
-    return ctx_nct_has_event (ctx, timeout);
-  }
+  /* returns non 0 if there is events waiting */
+  int   (*has_event) (EvSource *ev_source);
 
-  ctx_consume_events (ctx);
-  if (ctx->events.events)
-    return 1;
-  return 0;
-}
+  /* get an event, the returned event should be freed by the caller  */
+  char *(*get_event) (EvSource *ev_source);
 
-#if CTX_FB
-static int ctx_fb_get_mice_fd (Ctx *ctx);
-#endif
+  /* destroy/unref this instance */
+  void  (*destroy)   (EvSource *ev_source);
 
-void ctx_get_event_fds (Ctx *ctx, int *fd, int *count)
-{
-#if CTX_SDL
-  if (ctx_sdl_events)
-  {
-    *count = 0;
-  }
-  else
-#endif
-#if CTX_FB
-  if (ctx_fb_events)
-  {
-    int mice_fd = ctx_fb_get_mice_fd (ctx);
-    fd[0] = STDIN_FILENO;
-    if (mice_fd)
-    {
-      fd[1] = mice_fd;
-      *count = 2;
-    }
-    else
-    {
-      *count = 1;
-    }
-  }
-  else
-#endif
-  if (ctx_native_events)
-  {
-    fd[0] = STDIN_FILENO;
-    *count = 1;
-  }
-  else
-  {
-    fd[0] = STDIN_FILENO;
-    *count = 1;
-  }
-}
+  /* get the underlying fd, useful for using select on  */
+  int   (*get_fd)    (EvSource *ev_source);
 
-CtxEvent *ctx_get_event (Ctx *ctx)
-{
-  static CtxEvent event_copy;
-  _ctx_idle_iteration (ctx);
-  if (!ctx->events.ctx_get_event_enabled)
-    ctx->events.ctx_get_event_enabled = 1;
 
-  ctx_consume_events (ctx);
+  void  (*set_coord) (EvSource *ev_source, double x, double y);
+  /* set_coord is needed to warp relative cursors into normalized range,
+   * like normal mice/trackpads/nipples - to obey edges and more.
+   */
 
-  if (ctx->events.events)
-    {
-      event_copy = *((CtxEvent*)(ctx->events.events->data));
-      ctx_list_remove (&ctx->events.events, ctx->events.events->data);
-      return &event_copy;
-    }
-  return NULL;
-}
+  /* if this returns non-0 select can be used for non-blocking.. */
+};
 
-static int
-_ctx_emit_cb (Ctx *ctx, CtxList *items, CtxEvent *event, CtxEventType type, float x, float y)
-{
-  CtxList *l;
-  event->stop_propagate = 0;
-  for (l = items; l; l = l->next)
-  {
-    _ctx_emit_cb_item (ctx, l->data, event, type, x, y);
-    if (event->stop_propagate)
-      return event->stop_propagate;
-  }
-  return 0;
-}
 
-/*
- * update what is the currently hovered item and returns it.. and the list of hits
- * a well.
- *
- */
-static CtxItem *_ctx_update_item (Ctx *ctx, int device_no, float x, float y, CtxEventType type, CtxList 
**hitlist)
+typedef struct _CtxFb CtxFb;
+struct _CtxFb
 {
-  CtxItem *current = NULL;
-
-  CtxList *l = _ctx_detect_list (ctx, x, y, type);
-  if (l)
-  {
-    ctx_list_reverse (&l);
-    current = l->data;
-  }
-  if (hitlist)
-    *hitlist = l;
-  else
-    ctx_list_free (&l);
+   CtxTiled tiled;
+#if 0
+   void (*render) (void *fb, CtxCommand *command);
+   void (*reset)  (void *fb);
+   void (*flush)  (void *fb);
+   char *(*get_clipboard) (void *ctxctx);
+   void (*set_clipboard) (void *ctxctx, const char *text);
+   void (*free)   (void *fb);
+   Ctx          *ctx;
+   int           width;
+   int           height;
+   int           cols; // unused
+   int           rows; // unused
+   int           was_down;
+   uint8_t      *pixels;
+   Ctx          *ctx_copy;
+   Ctx          *host[CTX_MAX_THREADS];
+   CtxAntialias  antialias;
+   int           quit;
+   _Atomic int   thread_quit;
+   int           shown_frame;
+   int           render_frame;
+   int           rendered_frame[CTX_MAX_THREADS];
+   int           frame;
+   int           min_col; // hasher cols and rows
+   int           min_row;
+   int           max_col;
+   int           max_row;
+   uint8_t       hashes[CTX_HASH_ROWS * CTX_HASH_COLS *  20];
+   int8_t        tile_affinity[CTX_HASH_ROWS * CTX_HASH_COLS]; // which render thread no is
+                                                           // responsible for a tile
+                                                           //
 
-  if (ctx->events.prev[device_no] == NULL || current == NULL || (current->path_hash != 
ctx->events.prev[device_no]->path_hash))
-  {
-// enter/leave should snapshot chain to root
-// and compare with previous snapshotted chain to root
-// and emit/enter/leave as appropriate..
-//
-// leave might be registered for emission on enter..emission?
 
+   int           pointer_down[3];
+#endif
+   int           key_balance;
+   int           key_repeat;
+   int           lctrl;
+   int           lalt;
+   int           rctrl;
 
-    //int focus_radius = 2;
-    if (current)
-      _ctx_item_ref (current);
+   uint8_t      *fb;
 
-    if (ctx->events.prev[device_no])
-    {
-      {
-#if 0
-        CtxIntRectangle rect = {floor(ctx->events.prev[device_no]->x0-focus_radius),
-                             floor(ctx->events.prev[device_no]->y0-focus_radius),
-                             ceil(ctx->events.prev[device_no]->x1)-floor(ctx->events.prev[device_no]->x0) + 
focus_radius * 2,
-                             ceil(ctx->events.prev[device_no]->y1)-floor(ctx->events.prev[device_no]->y0) + 
focus_radius * 2};
-        mrg_queue_draw (mrg, &rect);
-#endif 
-      }
+   int          fb_fd;
+   char        *fb_path;
+   int          fb_bits;
+   int          fb_bpp;
+   int          fb_mapped_size;
+   struct       fb_var_screeninfo vinfo;
+   struct       fb_fix_screeninfo finfo;
+   int          vt;
+   int          tty;
+   int          vt_active;
+   EvSource    *evsource[4];
+   int          evsource_count;
+   int          is_drm;
+   cnd_t        cond;
+   mtx_t        mtx;
+   struct drm_mode_crtc crtc;
+};
 
-      _ctx_emit_cb_item (ctx, ctx->events.prev[device_no], NULL, CTX_LEAVE, x, y);
-      _ctx_item_unref (ctx->events.prev[device_no]);
-      ctx->events.prev[device_no] = NULL;
-    }
-    if (current)
-    {
-#if 0
-      {
-        CtxIntRectangle rect = {floor(current->x0-focus_radius),
-                             floor(current->y0-focus_radius),
-                             ceil(current->x1)-floor(current->x0) + focus_radius * 2,
-                             ceil(current->y1)-floor(current->y0) + focus_radius * 2};
-        mrg_queue_draw (mrg, &rect);
-      }
-#endif
-      _ctx_emit_cb_item (ctx, current, NULL, CTX_ENTER, x, y);
-      ctx->events.prev[device_no] = current;
-    }
+static char *ctx_fb_clipboard = NULL;
+static void ctx_fb_set_clipboard (CtxFb *fb, const char *text)
+{
+  if (ctx_fb_clipboard)
+    free (ctx_fb_clipboard);
+  ctx_fb_clipboard = NULL;
+  if (text)
+  {
+    ctx_fb_clipboard = strdup (text);
   }
-  current = _ctx_detect (ctx, x, y, type);
-  //fprintf (stderr, "%p\n", current);
-  return current;
 }
 
-static int tap_and_hold_fire (Ctx *ctx, void *data)
+static char *ctx_fb_get_clipboard (CtxFb *sdl)
 {
-  CtxGrab *grab = data;
-  CtxList *list = NULL;
-  ctx_list_prepend (&list, grab->item);
-  CtxEvent event = {0, };
+  if (ctx_fb_clipboard) return strdup (ctx_fb_clipboard);
+  return strdup ("");
+}
 
-  event.ctx = ctx;
-  event.time = ctx_ms (ctx);
+#if UINTPTR_MAX == 0xffFFffFF
+  #define fbdrmuint_t uint32_t
+#elif UINTPTR_MAX == 0xffFFffFFffFFffFF
+  #define fbdrmuint_t uint64_t
+#endif
 
-  event.device_x = 
-  event.x = ctx->events.pointer_x[grab->device_no];
-  event.device_y = 
-  event.y = ctx->events.pointer_y[grab->device_no];
+void *ctx_fbdrm_new (CtxFb *fb, int *width, int *height)
+{
+   int got_master = 0;
+   fb->fb_fd = open("/dev/dri/card0", O_RDWR | O_CLOEXEC);
+   if (!fb->fb_fd)
+     return NULL;
+   static fbdrmuint_t res_conn_buf[20]={0}; // this is static since its contents
+                                         // are used by the flip callback
+   fbdrmuint_t res_fb_buf[20]={0};
+   fbdrmuint_t res_crtc_buf[20]={0};
+   fbdrmuint_t res_enc_buf[20]={0};
+   struct   drm_mode_card_res res={0};
 
-  // XXX: x and y coordinates
-  int ret = _ctx_emit_cb (ctx, list, &event, CTX_TAP_AND_HOLD,
-      ctx->events.pointer_x[grab->device_no], ctx->events.pointer_y[grab->device_no]);
+   if (ioctl(fb->fb_fd, DRM_IOCTL_SET_MASTER, 0))
+     goto cleanup;
+   got_master = 1;
 
-  ctx_list_free (&list);
+   if (ioctl(fb->fb_fd, DRM_IOCTL_MODE_GETRESOURCES, &res))
+     goto cleanup;
+   res.fb_id_ptr=(fbdrmuint_t)res_fb_buf;
+   res.crtc_id_ptr=(fbdrmuint_t)res_crtc_buf;
+   res.connector_id_ptr=(fbdrmuint_t)res_conn_buf;
+   res.encoder_id_ptr=(fbdrmuint_t)res_enc_buf;
+   if(ioctl(fb->fb_fd, DRM_IOCTL_MODE_GETRESOURCES, &res))
+      goto cleanup;
 
-  grab->timeout_id = 0;
 
-  return 0;
+   unsigned int i;
+   for (i=0;i<res.count_connectors;i++)
+   {
+     struct drm_mode_modeinfo conn_mode_buf[20]={0};
+     fbdrmuint_t conn_prop_buf[20]={0},
+                     conn_propval_buf[20]={0},
+                     conn_enc_buf[20]={0};
 
-  return ret;
-}
+     struct drm_mode_get_connector conn={0};
 
-int ctx_pointer_drop (Ctx *ctx, float x, float y, int device_no, uint32_t time,
-                      char *string)
-{
-  CtxList *l;
-  CtxList *hitlist = NULL;
+     conn.connector_id=res_conn_buf[i];
 
-  ctx->events.pointer_x[device_no] = x;
-  ctx->events.pointer_y[device_no] = y;
-  if (device_no <= 3)
-  {
-    ctx->events.pointer_x[0] = x;
-    ctx->events.pointer_y[0] = y;
-  }
+     if (ioctl(fb->fb_fd, DRM_IOCTL_MODE_GETCONNECTOR, &conn))
+       goto cleanup;
 
-  if (device_no < 0) device_no = 0;
-  if (device_no >= CTX_MAX_DEVICES) device_no = CTX_MAX_DEVICES-1;
-  CtxEvent *event = &ctx->events.drag_event[device_no];
+     conn.modes_ptr=(fbdrmuint_t)conn_mode_buf;
+     conn.props_ptr=(fbdrmuint_t)conn_prop_buf;
+     conn.prop_values_ptr=(fbdrmuint_t)conn_propval_buf;
+     conn.encoders_ptr=(fbdrmuint_t)conn_enc_buf;
 
-  if (time == 0)
-    time = ctx_ms (ctx);
+     if (ioctl(fb->fb_fd, DRM_IOCTL_MODE_GETCONNECTOR, &conn))
+       goto cleanup;
 
-  event->ctx = ctx;
-  event->x = x;
-  event->y = y;
+     //Check if the connector is OK to use (connected to something)
+     if (conn.count_encoders<1 || conn.count_modes<1 || !conn.encoder_id || !conn.connection)
+       continue;
 
-  event->delta_x = event->delta_y = 0;
+//------------------------------------------------------------------------------
+//Creating a dumb buffer
+//------------------------------------------------------------------------------
+     struct drm_mode_create_dumb create_dumb={0};
+     struct drm_mode_map_dumb    map_dumb={0};
+     struct drm_mode_fb_cmd      cmd_dumb={0};
+     create_dumb.width  = conn_mode_buf[0].hdisplay;
+     create_dumb.height = conn_mode_buf[0].vdisplay;
+     create_dumb.bpp   = 32;
+     create_dumb.flags = 0;
+     create_dumb.pitch = 0;
+     create_dumb.size  = 0;
+     create_dumb.handle = 0;
+     if (ioctl(fb->fb_fd, DRM_IOCTL_MODE_CREATE_DUMB, &create_dumb) ||
+         !create_dumb.handle)
+       goto cleanup;
 
-  event->device_no = device_no;
-  event->string    = string;
-  event->time      = time;
-  event->stop_propagate = 0;
+     cmd_dumb.width =create_dumb.width;
+     cmd_dumb.height=create_dumb.height;
+     cmd_dumb.bpp   =create_dumb.bpp;
+     cmd_dumb.pitch =create_dumb.pitch;
+     cmd_dumb.depth =24;
+     cmd_dumb.handle=create_dumb.handle;
+     if (ioctl(fb->fb_fd,DRM_IOCTL_MODE_ADDFB,&cmd_dumb))
+       goto cleanup;
 
-  _ctx_update_item (ctx, device_no, x, y, CTX_DROP, &hitlist);
+     map_dumb.handle=create_dumb.handle;
+     if (ioctl(fb->fb_fd,DRM_IOCTL_MODE_MAP_DUMB,&map_dumb))
+       goto cleanup;
 
-  for (l = hitlist; l; l = l?l->next:NULL)
-  {
-    CtxItem *item = l->data;
-    _ctx_emit_cb_item (ctx, item, event, CTX_DROP, x, y);
+     void *base = mmap(0, create_dumb.size, PROT_READ | PROT_WRITE, MAP_SHARED,
+                       fb->fb_fd, map_dumb.offset);
+     if (!base)
+     {
+       goto cleanup;
+     }
+     *width  = create_dumb.width;
+     *height = create_dumb.height;
 
-    if (event->stop_propagate)
-    {
-      ctx_list_free (&hitlist);
-      return 0;
-    }
-  }
+     struct drm_mode_get_encoder enc={0};
+     enc.encoder_id=conn.encoder_id;
+     if (ioctl(fb->fb_fd, DRM_IOCTL_MODE_GETENCODER, &enc))
+        goto cleanup;
 
-  //mrg_queue_draw (mrg, NULL); /* in case of style change, and more  */
-  ctx_list_free (&hitlist);
+     fb->crtc.crtc_id=enc.crtc_id;
+     if (ioctl(fb->fb_fd, DRM_IOCTL_MODE_GETCRTC, &fb->crtc))
+        goto cleanup;
 
-  return 0;
+     fb->crtc.fb_id=cmd_dumb.fb_id;
+     fb->crtc.set_connectors_ptr=(fbdrmuint_t)&res_conn_buf[i];
+     fb->crtc.count_connectors=1;
+     fb->crtc.mode=conn_mode_buf[0];
+     fb->crtc.mode_valid=1;
+     return base;
+   }
+cleanup:
+   if (got_master)
+     ioctl(fb->fb_fd, DRM_IOCTL_DROP_MASTER, 0);
+   fb->fb_fd = 0;
+   return NULL;
 }
 
-int ctx_pointer_press (Ctx *ctx, float x, float y, int device_no, uint32_t time)
+void ctx_fbdrm_flip (CtxFb *fb)
 {
-  CtxEvents *events = &ctx->events;
-  CtxList *hitlist = NULL;
-  events->pointer_x[device_no] = x;
-  events->pointer_y[device_no] = y;
-  if (device_no <= 3)
-  {
-    events->pointer_x[0] = x;
-    events->pointer_y[0] = y;
-  }
-
-  if (device_no < 0) device_no = 0;
-  if (device_no >= CTX_MAX_DEVICES) device_no = CTX_MAX_DEVICES-1;
-  CtxEvent *event = &events->drag_event[device_no];
+  if (!fb->fb_fd)
+    return;
+  ioctl(fb->fb_fd, DRM_IOCTL_MODE_SETCRTC, &fb->crtc);
+}
 
-  if (time == 0)
-    time = ctx_ms (ctx);
+void ctx_fbdrm_close (CtxFb *fb)
+{
+  if (!fb->fb_fd)
+    return;
+  ioctl(fb->fb_fd, DRM_IOCTL_DROP_MASTER, 0);
+  close (fb->fb_fd);
+  fb->fb_fd = 0;
+}
 
-  event->x = event->start_x = event->prev_x = x;
-  event->y = event->start_y = event->prev_y = y;
+static void ctx_fb_flip (CtxFb *fb)
+{
+  if (fb->is_drm)
+    ctx_fbdrm_flip (fb);
+  else
+    ioctl (fb->fb_fd, FBIOPAN_DISPLAY, &fb->vinfo);
+}
 
-  event->delta_x = event->delta_y = 0;
+inline static uint32_t
+ctx_swap_red_green2 (uint32_t orig)
+{
+  uint32_t  green_alpha = (orig & 0xff00ff00);
+  uint32_t  red_blue    = (orig & 0x00ff00ff);
+  uint32_t  red         = red_blue << 16;
+  uint32_t  blue        = red_blue >> 16;
+  return green_alpha | red | blue;
+}
 
-  event->device_no = device_no;
-  event->time      = time;
-  event->stop_propagate = 0;
+static int       ctx_fb_cursor_drawn   = 0;
+static int       ctx_fb_cursor_drawn_x = 0;
+static int       ctx_fb_cursor_drawn_y = 0;
+static CtxCursor ctx_fb_cursor_drawn_shape = 0;
 
-  if (events->pointer_down[device_no] == 1)
-  {
-    fprintf (stderr, "events thought device %i was already down\n", device_no);
-  }
-  /* doing just one of these two should be enough? */
-  events->pointer_down[device_no] = 1;
-  switch (device_no)
-  {
-    case 1:
-      events->modifier_state |= CTX_MODIFIER_STATE_BUTTON1;
-      break;
-    case 2:
-      events->modifier_state |= CTX_MODIFIER_STATE_BUTTON2;
-      break;
-    case 3:
-      events->modifier_state |= CTX_MODIFIER_STATE_BUTTON3;
-      break;
-    default:
-      break;
-  }
 
-  CtxGrab *grab = NULL;
-  CtxList *l;
+#define CTX_FB_HIDE_CURSOR_FRAMES 200
 
-  _ctx_update_item (ctx, device_no, x, y, 
-      CTX_PRESS | CTX_DRAG_PRESS | CTX_TAP | CTX_TAP_AND_HOLD, &hitlist);
+static int ctx_fb_cursor_same_pos = CTX_FB_HIDE_CURSOR_FRAMES;
 
-  for (l = hitlist; l; l = l?l->next:NULL)
+static inline int ctx_is_in_cursor (int x, int y, int size, CtxCursor shape)
+{
+  switch (shape)
   {
-    CtxItem *item = l->data;
-    if (item &&
-        ((item->types & CTX_DRAG)||
-         (item->types & CTX_TAP) ||
-         (item->types & CTX_TAP_AND_HOLD)))
-    {
-      grab = device_add_grab (ctx, device_no, item, item->types);
-      grab->start_time = time;
+    case CTX_CURSOR_ARROW:
+      if (x > ((size * 4)-y*4)) return 0;
+      if (x < y && x > y / 16)
+        return 1;
+      return 0;
 
-      if (item->types & CTX_TAP_AND_HOLD)
+    case CTX_CURSOR_RESIZE_SE:
+    case CTX_CURSOR_RESIZE_NW:
+    case CTX_CURSOR_RESIZE_SW:
+    case CTX_CURSOR_RESIZE_NE:
       {
-         grab->timeout_id = ctx_add_timeout (ctx, events->tap_delay_hold, tap_and_hold_fire, grab);
-      }
-    }
-    _ctx_emit_cb_item (ctx, item, event, CTX_PRESS, x, y);
-    if (!event->stop_propagate)
-      _ctx_emit_cb_item (ctx, item, event, CTX_DRAG_PRESS, x, y);
+        float theta = -45.0/180 * M_PI;
+        float cos_theta;
+        float sin_theta;
 
-    if (event->stop_propagate)
-    {
-      ctx_list_free (&hitlist);
-      return 0;
-    }
+        if ((shape == CTX_CURSOR_RESIZE_SW) ||
+            (shape == CTX_CURSOR_RESIZE_NE))
+        {
+          theta = -theta;
+          cos_theta = cos (theta);
+          sin_theta = sin (theta);
+        }
+        else
+        {
+          cos_theta = cos (theta);
+          sin_theta = sin (theta);
+        }
+        int rot_x = x * cos_theta - y * sin_theta;
+        int rot_y = y * cos_theta + x * sin_theta;
+        x = rot_x;
+        y = rot_y;
+      }
+      /*FALLTHROUGH*/
+    case CTX_CURSOR_RESIZE_W:
+    case CTX_CURSOR_RESIZE_E:
+    case CTX_CURSOR_RESIZE_ALL:
+      if (abs (x) < size/2 && abs (y) < size/2)
+      {
+        if (abs(y) < size/10)
+        {
+          return 1;
+        }
+      }
+      if ((abs (x) - size/ (shape == CTX_CURSOR_RESIZE_ALL?2:2.7)) >= 0)
+      {
+        if (abs(y) < (size/2.8)-(abs(x) - (size/2)))
+          return 1;
+      }
+      if (shape != CTX_CURSOR_RESIZE_ALL)
+        break;
+      /* FALLTHROUGH */
+    case CTX_CURSOR_RESIZE_S:
+    case CTX_CURSOR_RESIZE_N:
+      if (abs (y) < size/2 && abs (x) < size/2)
+      {
+        if (abs(x) < size/10)
+        {
+          return 1;
+        }
+      }
+      if ((abs (y) - size/ (shape == CTX_CURSOR_RESIZE_ALL?2:2.7)) >= 0)
+      {
+        if (abs(x) < (size/2.8)-(abs(y) - (size/2)))
+          return 1;
+      }
+      break;
+#if 0
+    case CTX_CURSOR_RESIZE_ALL:
+      if (abs (x) < size/2 && abs (y) < size/2)
+      {
+        if (abs (x) < size/10 || abs(y) < size/10)
+          return 1;
+      }
+      break;
+#endif
+    default:
+      return (x ^ y) & 1;
   }
-
-  //events_queue_draw (mrg, NULL); /* in case of style change, and more  */
-  ctx_list_free (&hitlist);
   return 0;
 }
 
-void _ctx_resized (Ctx *ctx, int width, int height, long time)
+static void ctx_fb_undraw_cursor (CtxFb *fb)
 {
-  CtxItem *item = _ctx_detect (ctx, 0, 0, CTX_KEY_PRESS);
-  CtxEvent event = {0, };
+    CtxTiled *tiled = (void*)fb;
+    int cursor_size = ctx_height (tiled->ctx) / 28;
 
-  if (!time)
-    time = ctx_ms (ctx);
-  
-  event.ctx = ctx;
-  event.time = time;
-  event.string = "resize-event"; /* gets delivered to clients as a key_down event, maybe message shouldbe 
used instead?
-   */
+    if (ctx_fb_cursor_drawn)
+    {
+      int no = 0;
+      int startx = -cursor_size;
+      int starty = -cursor_size;
+      if (ctx_fb_cursor_drawn_shape == CTX_CURSOR_ARROW)
+        startx = starty = 0;
 
-  if (item)
-  {
-    event.stop_propagate = 0;
-    _ctx_emit_cb_item (ctx, item, &event, CTX_KEY_PRESS, 0, 0);
-  }
+      for (int y = starty; y < cursor_size; y++)
+      for (int x = startx; x < cursor_size; x++, no+=4)
+      {
+        if (x + ctx_fb_cursor_drawn_x < tiled->width && y + ctx_fb_cursor_drawn_y < tiled->height)
+        {
+          if (ctx_is_in_cursor (x, y, cursor_size, ctx_fb_cursor_drawn_shape))
+          {
+            int o = ((ctx_fb_cursor_drawn_y + y) * tiled->width + (ctx_fb_cursor_drawn_x + x)) * 4;
+            fb->fb[o+0]^=0x88;
+            fb->fb[o+1]^=0x88;
+            fb->fb[o+2]^=0x88;
+          }
+        }
+      }
 
+    ctx_fb_cursor_drawn = 0;
+    }
 }
 
-int ctx_pointer_release (Ctx *ctx, float x, float y, int device_no, uint32_t time)
+static void ctx_fb_draw_cursor (CtxFb *fb)
 {
-  CtxEvents *events = &ctx->events;
-  if (time == 0)
-    time = ctx_ms (ctx);
-
-  if (device_no < 0) device_no = 0;
-  if (device_no >= CTX_MAX_DEVICES) device_no = CTX_MAX_DEVICES-1;
-  CtxEvent *event = &events->drag_event[device_no];
+    CtxTiled *tiled = (void*)fb;
+    int cursor_x    = ctx_pointer_x (tiled->ctx);
+    int cursor_y    = ctx_pointer_y (tiled->ctx);
+    int cursor_size = ctx_height (tiled->ctx) / 28;
+    CtxCursor cursor_shape = tiled->ctx->cursor;
+    int no = 0;
 
-  event->time = time;
-  event->x = x;
-  event->ctx = ctx;
-  event->y = y;
-  event->device_no = device_no;
-  event->stop_propagate = 0;
+    if (cursor_x == ctx_fb_cursor_drawn_x &&
+        cursor_y == ctx_fb_cursor_drawn_y &&
+        cursor_shape == ctx_fb_cursor_drawn_shape)
+      ctx_fb_cursor_same_pos ++;
+    else
+      ctx_fb_cursor_same_pos = 0;
 
-  switch (device_no)
-  {
-    case 1:
-      if (events->modifier_state & CTX_MODIFIER_STATE_BUTTON1)
-        events->modifier_state -= CTX_MODIFIER_STATE_BUTTON1;
-      break;
-    case 2:
-      if (events->modifier_state & CTX_MODIFIER_STATE_BUTTON2)
-        events->modifier_state -= CTX_MODIFIER_STATE_BUTTON2;
-      break;
-    case 3:
-      if (events->modifier_state & CTX_MODIFIER_STATE_BUTTON3)
-        events->modifier_state -= CTX_MODIFIER_STATE_BUTTON3;
-      break;
-    default:
-      break;
-  }
+    if (ctx_fb_cursor_same_pos >= CTX_FB_HIDE_CURSOR_FRAMES)
+    {
+      if (ctx_fb_cursor_drawn)
+        ctx_fb_undraw_cursor (fb);
+      return;
+    }
 
-  //events_queue_draw (mrg, NULL); /* in case of style change */
+    /* no need to flicker when stationary, motion flicker can also be removed
+     * by combining the previous and next position masks when a motion has
+     * occured..
+     */
+    if (ctx_fb_cursor_same_pos && ctx_fb_cursor_drawn)
+      return;
 
-  if (events->pointer_down[device_no] == 0)
-  {
-    fprintf (stderr, "device %i already up\n", device_no);
-  }
-  events->pointer_down[device_no] = 0;
+    ctx_fb_undraw_cursor (fb);
 
-  events->pointer_x[device_no] = x;
-  events->pointer_y[device_no] = y;
-  if (device_no <= 3)
-  {
-    events->pointer_x[0] = x;
-    events->pointer_y[0] = y;
-  }
-  CtxList *hitlist = NULL;
-  CtxList *grablist = NULL , *g= NULL;
-  CtxGrab *grab;
+    no = 0;
 
-  _ctx_update_item (ctx, device_no, x, y, CTX_RELEASE | CTX_DRAG_RELEASE, &hitlist);
-  grablist = _ctx_device_get_grabs (ctx, device_no);
+    int startx = -cursor_size;
+    int starty = -cursor_size;
 
-  for (g = grablist; g; g = g->next)
-  {
-    grab = g->data;
+    if (cursor_shape == CTX_CURSOR_ARROW)
+      startx = starty = 0;
 
-    if (!event->stop_propagate)
-    {
-      if (grab->item->types & CTX_TAP)
+    for (int y = starty; y < cursor_size; y++)
+      for (int x = startx; x < cursor_size; x++, no+=4)
       {
-        long delay = time - grab->start_time;
-
-        if (delay > events->tap_delay_min &&
-            delay < events->tap_delay_max &&
-            (
-              (event->start_x - x) * (event->start_x - x) +
-              (event->start_y - y) * (event->start_y - y)) < ctx_pow2(events->tap_hysteresis)
-            )
+        if (x + cursor_x < tiled->width && y + cursor_y < tiled->height)
         {
-          _ctx_emit_cb_item (ctx, grab->item, event, CTX_TAP, x, y);
+          if (ctx_is_in_cursor (x, y, cursor_size, cursor_shape))
+          {
+            int o = ((cursor_y + y) * tiled->width + (cursor_x + x)) * 4;
+            fb->fb[o+0]^=0x88;
+            fb->fb[o+1]^=0x88;
+            fb->fb[o+2]^=0x88;
+          }
         }
       }
-
-      if (!event->stop_propagate && grab->item->types & CTX_DRAG_RELEASE)
-      {
-        _ctx_emit_cb_item (ctx, grab->item, event, CTX_DRAG_RELEASE, x, y);
-      }
-    }
-
-    device_remove_grab (ctx, grab);
-  }
-
-  if (hitlist)
-  {
-    if (!event->stop_propagate)
-      _ctx_emit_cb (ctx, hitlist, event, CTX_RELEASE, x, y);
-    ctx_list_free (&hitlist);
-  }
-  ctx_list_free (&grablist);
-  return 0;
+    ctx_fb_cursor_drawn = 1;
+    ctx_fb_cursor_drawn_x = cursor_x;
+    ctx_fb_cursor_drawn_y = cursor_y;
+    ctx_fb_cursor_drawn_shape = cursor_shape;
 }
 
-/*  for multi-touch, we use a list of active grabs - thus a grab corresponds to
- *  a device id. even during drag-grabs events propagate; to stop that stop
- *  propagation.
- */
-int ctx_pointer_motion (Ctx *ctx, float x, float y, int device_no, uint32_t time)
+static void ctx_fb_show_frame (CtxFb *fb, int block)
 {
-  CtxList *hitlist = NULL;
-  CtxList *grablist = NULL, *g;
-  CtxGrab *grab;
-
-  if (device_no < 0) device_no = 0;
-  if (device_no >= CTX_MAX_DEVICES) device_no = CTX_MAX_DEVICES-1;
-  CtxEvent *event = &ctx->events.drag_event[device_no];
-
-  if (time == 0)
-    time = ctx_ms (ctx);
-
-  event->ctx       = ctx;
-  event->x         = x;
-  event->y         = y;
-  event->time      = time;
-  event->device_no = device_no;
-  event->stop_propagate = 0;
-  
-  ctx->events.pointer_x[device_no] = x;
-  ctx->events.pointer_y[device_no] = y;
-
-  if (device_no <= 3)
-  {
-    ctx->events.pointer_x[0] = x;
-    ctx->events.pointer_y[0] = y;
-  }
-
-  grablist = _ctx_device_get_grabs (ctx, device_no);
-  _ctx_update_item (ctx, device_no, x, y, CTX_MOTION, &hitlist);
-
+  CtxTiled *tiled = (void*)fb;
+  if (tiled->shown_frame == tiled->render_frame)
   {
-    CtxItem  *cursor_item = _ctx_detect (ctx, x, y, CTX_SET_CURSOR);
-    if (cursor_item)
-    {
-      ctx_set_cursor (ctx, cursor_item->cursor);
-    }
-    else
-    {
-      ctx_set_cursor (ctx, CTX_CURSOR_ARROW);
-    }
-    CtxItem  *hovered_item = _ctx_detect (ctx, x, y, CTX_ANY);
-    static CtxItem *prev_hovered_item = NULL;
-    if (prev_hovered_item != hovered_item)
+    if (block == 0) // consume event call
     {
-      ctx_set_dirty (ctx, 1);
+      ctx_fb_draw_cursor (fb);
+      ctx_fb_flip (fb);
     }
-    prev_hovered_item = hovered_item;
+    return;
   }
 
-  event->delta_x = x - event->prev_x;
-  event->delta_y = y - event->prev_y;
-  event->prev_x  = x;
-  event->prev_y  = y;
-
-  CtxList *remove_grabs = NULL;
-
-  for (g = grablist; g; g = g->next)
+  if (block)
   {
-    grab = g->data;
-
-    if ((grab->type & CTX_TAP) ||
-        (grab->type & CTX_TAP_AND_HOLD))
+    int count = 0;
+    while (ctx_tiled_threads_done (tiled) != _ctx_max_threads)
     {
-      if (
-          (
-            (event->start_x - x) * (event->start_x - x) +
-            (event->start_y - y) * (event->start_y - y)) >
-              ctx_pow2(ctx->events.tap_hysteresis)
-         )
-      {
-        //fprintf (stderr, "-");
-        ctx_list_prepend (&remove_grabs, grab);
-      }
-      else
+      usleep (500);
+      count ++;
+      if (count > 2000)
       {
-        //fprintf (stderr, ":");
+        tiled->shown_frame = tiled->render_frame;
+        return;
       }
     }
-
-    if (grab->type & CTX_DRAG_MOTION)
-    {
-      _ctx_emit_cb_item (ctx, grab->item, event, CTX_DRAG_MOTION, x, y);
-      if (event->stop_propagate)
-        break;
-    }
-  }
-  if (remove_grabs)
-  {
-    for (g = remove_grabs; g; g = g->next)
-      device_remove_grab (ctx, g->data);
-    ctx_list_free (&remove_grabs);
   }
-  if (hitlist)
+  else
   {
-    if (!event->stop_propagate)
-      _ctx_emit_cb (ctx, hitlist, event, CTX_MOTION, x, y);
-    ctx_list_free (&hitlist);
+    if (ctx_tiled_threads_done (tiled) != _ctx_max_threads)
+      return;
   }
-  ctx_list_free (&grablist);
-  return 0;
-}
-
-void ctx_incoming_message (Ctx *ctx, const char *message, long time)
-{
-  CtxItem *item = _ctx_detect (ctx, 0, 0, CTX_MESSAGE);
-  CtxEvent event = {0, };
 
-  if (!time)
-    time = ctx_ms (ctx);
+    if (fb->vt_active)
+    {
+       int pre_skip = tiled->min_row * tiled->height/CTX_HASH_ROWS * tiled->width;
+       int post_skip = (CTX_HASH_ROWS-tiled->max_row-1) * tiled->height/CTX_HASH_ROWS * tiled->width;
 
-  if (item)
-  {
-    int i;
-    event.ctx = ctx;
-    event.type = CTX_MESSAGE;
-    event.time = time;
-    event.string = message;
+       int rows = ((tiled->width * tiled->height) - pre_skip - post_skip)/tiled->width;
 
-    fprintf (stderr, "{%s|\n", message);
+       int col_pre_skip = tiled->min_col * tiled->width/CTX_HASH_COLS;
+       int col_post_skip = (CTX_HASH_COLS-tiled->max_col-1) * tiled->width/CTX_HASH_COLS;
+       if (_ctx_damage_control)
+       {
+         pre_skip = post_skip = col_pre_skip = col_post_skip = 0;
+       }
 
-      for (i = 0; i < item->cb_count; i++)
-      {
-        if (item->cb[i].types & (CTX_MESSAGE))
-        {
-          event.state = ctx->events.modifier_state;
-          item->cb[i].cb (&event, item->cb[i].data1, item->cb[i].data2);
-          if (event.stop_propagate)
-            return;// event.stop_propagate;
-        }
-      }
+       if (pre_skip < 0) pre_skip = 0;
+       if (post_skip < 0) post_skip = 0;
+
+     __u32 dummy = 0;
+
+       if (tiled->min_row == 100){
+          pre_skip = 0;
+          post_skip = 0;
+          // not when drm ?
+          ioctl (fb->fb_fd, FBIO_WAITFORVSYNC, &dummy);
+          ctx_fb_undraw_cursor (fb);
+       }
+       else
+       {
+
+      tiled->min_row = 100;
+      tiled->max_row = 0;
+      tiled->min_col = 100;
+      tiled->max_col = 0;
+
+     // not when drm ?
+     ioctl (fb->fb_fd, FBIO_WAITFORVSYNC, &dummy);
+     ctx_fb_undraw_cursor (fb);
+     switch (fb->fb_bits)
+     {
+       case 32:
+#if 1
+         {
+           uint8_t *dst = fb->fb + pre_skip * 4;
+           uint8_t *src = tiled->pixels + pre_skip * 4;
+           int pre = col_pre_skip * 4;
+           int post = col_post_skip * 4;
+           int core = tiled->width * 4 - pre - post;
+           for (int i = 0; i < rows; i++)
+           {
+             dst  += pre;
+             src  += pre;
+             memcpy (dst, src, core);
+             src  += core;
+             dst  += core;
+             dst  += post;
+             src  += post;
+           }
+         }
+#else
+         { int count = tiled->width * tiled->height;
+           const uint32_t *src = (void*)tiled->pixels;
+           uint32_t *dst = (void*)fb->fb;
+           count-= pre_skip;
+           src+= pre_skip;
+           dst+= pre_skip;
+           count-= post_skip;
+           while (count -- > 0)
+           {
+             dst[0] = ctx_swap_red_green2 (src[0]);
+             src++;
+             dst++;
+           }
+         }
+#endif
+         break;
+         /* XXX  :  note: converting a scanline (or all) to target and
+          * then doing a bulk memcpy be faster (at least with som /dev/fbs)  */
+       case 24:
+         { int count = tiled->width * tiled->height;
+           const uint8_t *src = tiled->pixels;
+           uint8_t *dst = fb->fb;
+           count-= pre_skip;
+           src+= pre_skip * 4;
+           dst+= pre_skip * 3;
+           count-= post_skip;
+           while (count -- > 0)
+           {
+             dst[0] = src[0];
+             dst[1] = src[1];
+             dst[2] = src[2];
+             dst+=3;
+             src+=4;
+           }
+         }
+         break;
+       case 16:
+         { int count = tiled->width * tiled->height;
+           const uint8_t *src = tiled->pixels;
+           uint8_t *dst = fb->fb;
+           count-= post_skip;
+           count-= pre_skip;
+           src+= pre_skip * 4;
+           dst+= pre_skip * 2;
+           while (count -- > 0)
+           {
+             int big = ((src[0] >> 3)) +
+                ((src[1] >> 2)<<5) +
+                ((src[2] >> 3)<<11);
+             dst[0] = big & 255;
+             dst[1] = big >>  8;
+             dst+=2;
+             src+=4;
+           }
+         }
+         break;
+       case 15:
+         { int count = tiled->width * tiled->height;
+           const uint8_t *src = tiled->pixels;
+           uint8_t *dst = fb->fb;
+           count-= post_skip;
+           count-= pre_skip;
+           src+= pre_skip * 4;
+           dst+= pre_skip * 2;
+           while (count -- > 0)
+           {
+             int big = ((src[2] >> 3)) +
+                       ((src[1] >> 2)<<5) +
+                       ((src[0] >> 3)<<10);
+             dst[0] = big & 255;
+             dst[1] = big >>  8;
+             dst+=2;
+             src+=4;
+           }
+         }
+         break;
+       case 8:
+         { int count = tiled->width * tiled->height;
+           const uint8_t *src = tiled->pixels;
+           uint8_t *dst = fb->fb;
+           count-= post_skip;
+           count-= pre_skip;
+           src+= pre_skip * 4;
+           dst+= pre_skip;
+           while (count -- > 0)
+           {
+             dst[0] = ((src[0] >> 5)) +
+                      ((src[1] >> 5)<<3) +
+                      ((src[2] >> 6)<<6);
+             dst+=1;
+             src+=4;
+           }
+         }
+         break;
+     }
+    }
+    ctx_fb_cursor_drawn = 0;
+    ctx_fb_draw_cursor (fb);
+    ctx_fb_flip (fb);
+    tiled->shown_frame = tiled->render_frame;
   }
 }
 
-int ctx_scrolled (Ctx *ctx, float x, float y, CtxScrollDirection scroll_direction, uint32_t time)
-{
-  CtxList *hitlist = NULL;
-  CtxList *l;
 
-  int device_no = 0;
-  ctx->events.pointer_x[device_no] = x;
-  ctx->events.pointer_y[device_no] = y;
+#define evsource_has_event(es)   (es)->has_event((es))
+#define evsource_get_event(es)   (es)->get_event((es))
+#define evsource_destroy(es)     do{if((es)->destroy)(es)->destroy((es));}while(0)
+#define evsource_set_coord(es,x,y) do{if((es)->set_coord)(es)->set_coord((es),(x),(y));}while(0)
+#define evsource_get_fd(es)      ((es)->get_fd?(es)->get_fd((es)):0)
 
-  CtxEvent *event = &ctx->events.drag_event[device_no];  /* XXX: might
-                                       conflict with other code
-                                       create a sibling member
-                                       of drag_event?*/
-  if (time == 0)
-    time = ctx_ms (ctx);
 
-  event->x         = event->start_x = event->prev_x = x;
-  event->y         = event->start_y = event->prev_y = y;
-  event->delta_x   = event->delta_y = 0;
-  event->device_no = device_no;
-  event->time      = time;
-  event->stop_propagate = 0;
-  event->scroll_direction = scroll_direction;
 
-  _ctx_update_item (ctx, device_no, x, y, CTX_SCROLL, &hitlist);
+static int mice_has_event ();
+static char *mice_get_event ();
+static void mice_destroy ();
+static int mice_get_fd (EvSource *ev_source);
+static void mice_set_coord (EvSource *ev_source, double x, double y);
 
-  for (l = hitlist; l; l = l?l->next:NULL)
-  {
-    CtxItem *item = l->data;
+static EvSource ctx_ev_src_mice = {
+  NULL,
+  (void*)mice_has_event,
+  (void*)mice_get_event,
+  (void*)mice_destroy,
+  mice_get_fd,
+  mice_set_coord
+};
 
-    _ctx_emit_cb_item (ctx, item, event, CTX_SCROLL, x, y);
+typedef struct Mice
+{
+  int     fd;
+  double  x;
+  double  y;
+  int     button;
+  int     prev_state;
+} Mice;
 
-    if (event->stop_propagate)
-      l = NULL;
-  }
+Mice *_mrg_evsrc_coord = NULL;
+static int _ctx_mice_fd = 0;
 
-  //mrg_queue_draw (mrg, NULL); /* in case of style change, and more  */
-  ctx_list_free (&hitlist);
-  return 0;
+void _mmm_get_coords (Ctx *ctx, double *x, double *y)
+{
+  if (!_mrg_evsrc_coord)
+    return;
+  if (x)
+    *x = _mrg_evsrc_coord->x;
+  if (y)
+    *y = _mrg_evsrc_coord->y;
 }
 
-static int ctx_str_has_prefix (const char *string, const char *prefix)
+static Mice  mice;
+static Mice* mrg_mice_this = &mice;
+
+static int mmm_evsource_mice_init ()
 {
-  for (int i = 0; prefix[i]; i++)
+  unsigned char reset[]={0xff};
+  /* need to detect which event */
+
+  mrg_mice_this->prev_state = 0;
+  mrg_mice_this->fd = open ("/dev/input/mice", O_RDONLY | O_NONBLOCK);
+  if (mrg_mice_this->fd == -1)
   {
-    if (!string[i]) return 0;
-    if (string[i] != prefix[i]) return 0;
+    fprintf (stderr, "error opening /dev/input/mice device, maybe add user to input group if such group 
exist, or otherwise make the rights be satisfied.\n");
+    return -1;
+  }
+  if (write (mrg_mice_this->fd, reset, 1) == -1)
+  {
+    // might happen if we're a regular user with only read permission
   }
+  _ctx_mice_fd = mrg_mice_this->fd;
+  _mrg_evsrc_coord = mrg_mice_this;
   return 0;
 }
 
-int ctx_key_press (Ctx *ctx, unsigned int keyval,
-                   const char *string, uint32_t time)
+static void mice_destroy ()
 {
-  char event_type[128]="";
-  float x, y; int b;
-  sscanf (string, "%s %f %f %i", event_type, &x, &y, &b);
-  if (!strcmp (event_type, "mouse-motion") ||
-      !strcmp (event_type, "mouse-drag"))
-    return ctx_pointer_motion (ctx, x, y, b, 0);
-  else if (!strcmp (event_type, "mouse-press"))
-    return ctx_pointer_press (ctx, x, y, b, 0);
-  else if (!strcmp (event_type, "mouse-release"))
-    return ctx_pointer_release (ctx, x, y, b, 0);
-  //else if (!strcmp (event_type, "keydown"))
-  //  return ctx_key_down (ctx, keyval, string + 8, time);
-  //else if (!strcmp (event_type, "keyup"))
-  //  return ctx_key_up (ctx, keyval, string + 6, time);
+  if (mrg_mice_this->fd != -1)
+    close (mrg_mice_this->fd);
+}
 
-  CtxItem *item = _ctx_detect (ctx, 0, 0, CTX_KEY_PRESS);
-  CtxEvent event = {0,};
+static int mice_has_event ()
+{
+  struct timeval tv;
+  int retval;
 
-  if (time == 0)
-    time = ctx_ms (ctx);
-  if (item)
-  {
-    int i;
-    event.ctx = ctx;
-    event.type = CTX_KEY_PRESS;
-    event.unicode = keyval; 
-    event.string = strdup(string);
-    event.stop_propagate = 0;
-    event.time = time;
+  if (mrg_mice_this->fd == -1)
+    return 0;
 
-    for (i = 0; i < item->cb_count; i++)
-    {
-      if (item->cb[i].types & (CTX_KEY_PRESS))
-      {
-        event.state = ctx->events.modifier_state;
-        item->cb[i].cb (&event, item->cb[i].data1, item->cb[i].data2);
-        if (event.stop_propagate)
-        {
-          free ((void*)event.string);
-          return event.stop_propagate;
-        }
-      }
-    }
-    free ((void*)event.string);
-  }
+  fd_set rfds;
+  FD_ZERO (&rfds);
+  FD_SET(mrg_mice_this->fd, &rfds);
+  tv.tv_sec = 0; tv.tv_usec = 0;
+  retval = select (mrg_mice_this->fd+1, &rfds, NULL, NULL, &tv);
+  if (retval == 1)
+    return FD_ISSET (mrg_mice_this->fd, &rfds);
   return 0;
 }
 
-int ctx_key_down (Ctx *ctx, unsigned int keyval,
-                  const char *string, uint32_t time)
+static char *mice_get_event ()
 {
-  CtxItem *item = _ctx_detect (ctx, 0, 0, CTX_KEY_DOWN);
-  CtxEvent event = {0,};
+  const char *ret = "mouse-motion";
+  double relx, rely;
+  signed char buf[3];
+  int n_read = 0;
+  CtxFb *fb = ctx_ev_src_mice.priv;
+  CtxTiled *tiled = (void*)fb;
+  n_read = read (mrg_mice_this->fd, buf, 3);
+  if (n_read == 0)
+     return strdup ("");
+  relx = buf[1];
+  rely = -buf[2];
 
-  if (time == 0)
-    time = ctx_ms (ctx);
-  if (item)
+  if (relx < 0)
   {
-    int i;
-    event.ctx     = ctx;
-    event.type    = CTX_KEY_DOWN;
-    event.unicode = keyval; 
-    event.string  = strdup(string);
-    event.stop_propagate = 0;
-    event.time    = time;
+    if (relx > -6)
+    relx = - relx*relx;
+    else
+    relx = -36;
+  }
+  else
+  {
+    if (relx < 6)
+    relx = relx*relx;
+    else
+    relx = 36;
+  }
 
-    for (i = 0; i < item->cb_count; i++)
+  if (rely < 0)
+  {
+    if (rely > -6)
+    rely = - rely*rely;
+    else
+    rely = -36;
+  }
+  else
+  {
+    if (rely < 6)
+    rely = rely*rely;
+    else
+    rely = 36;
+  }
+
+  mrg_mice_this->x += relx;
+  mrg_mice_this->y += rely;
+
+  if (mrg_mice_this->x < 0)
+    mrg_mice_this->x = 0;
+  if (mrg_mice_this->y < 0)
+    mrg_mice_this->y = 0;
+  if (mrg_mice_this->x >= tiled->width)
+    mrg_mice_this->x = tiled->width -1;
+  if (mrg_mice_this->y >= tiled->height)
+    mrg_mice_this->y = tiled->height -1;
+  int button = 0;
+  
+  if ((mrg_mice_this->prev_state & 1) != (buf[0] & 1))
     {
-      if (item->cb[i].types & (CTX_KEY_DOWN))
-      {
-        event.state = ctx->events.modifier_state;
-        item->cb[i].cb (&event, item->cb[i].data1, item->cb[i].data2);
-        if (event.stop_propagate)
+      if (buf[0] & 1)
         {
-          free ((void*)event.string);
-          return event.stop_propagate;
+          ret = "mouse-press";
         }
-      }
+      else
+        {
+          ret = "mouse-release";
+        }
+      button = 1;
     }
-    free ((void*)event.string);
+  else if (buf[0] & 1)
+  {
+    ret = "mouse-drag";
+    button = 1;
   }
-  return 0;
-}
-
-int ctx_key_up (Ctx *ctx, unsigned int keyval,
-                const char *string, uint32_t time)
-{
-  CtxItem *item = _ctx_detect (ctx, 0, 0, CTX_KEY_UP);
-  CtxEvent event = {0,};
 
-  if (time == 0)
-    time = ctx_ms (ctx);
-  if (item)
+  if (!button)
   {
-    int i;
-    event.ctx = ctx;
-    event.type = CTX_KEY_UP;
-    event.unicode = keyval; 
-    event.string = strdup(string);
-    event.stop_propagate = 0;
-    event.time = time;
+    if ((mrg_mice_this->prev_state & 2) != (buf[0] & 2))
+    {
+      if (buf[0] & 2)
+        {
+          ret = "mouse-press";
+        }
+      else
+        {
+          ret = "mouse-release";
+        }
+      button = 3;
+    }
+    else if (buf[0] & 2)
+    {
+      ret = "mouse-drag";
+      button = 3;
+    }
+  }
 
-    for (i = 0; i < item->cb_count; i++)
+  if (!button)
+  {
+    if ((mrg_mice_this->prev_state & 4) != (buf[0] & 4))
     {
-      if (item->cb[i].types & (CTX_KEY_UP))
-      {
-        event.state = ctx->events.modifier_state;
-        item->cb[i].cb (&event, item->cb[i].data1, item->cb[i].data2);
-        if (event.stop_propagate)
+      if (buf[0] & 4)
         {
-          free ((void*)event.string);
-          return event.stop_propagate;
+          ret = "mouse-press";
         }
-      }
+      else
+        {
+          ret = "mouse-release";
+        }
+      button = 2;
+    }
+    else if (buf[0] & 4)
+    {
+      ret = "mouse-drag";
+      button = 2;
     }
-    free ((void*)event.string);
   }
-  return 0;
 
-  return 0;
-}
+  mrg_mice_this->prev_state = buf[0];
 
-void ctx_freeze           (Ctx *ctx)
-{
-  ctx->events.frozen ++;
+  {
+    char *r = malloc (64);
+    sprintf (r, "%s %.0f %.0f %i", ret, mrg_mice_this->x, mrg_mice_this->y, button);
+    return r;
+  }
+
+  return NULL;
 }
 
-void ctx_thaw             (Ctx *ctx)
+static int mice_get_fd (EvSource *ev_source)
 {
-  ctx->events.frozen --;
+  return mrg_mice_this->fd;
 }
-int ctx_events_frozen (Ctx *ctx)
+
+static void mice_set_coord (EvSource *ev_source, double x, double y)
 {
-  return ctx && ctx->events.frozen;
+  mrg_mice_this->x = x;
+  mrg_mice_this->y = y;
 }
-void ctx_events_clear_items (Ctx *ctx)
+
+static EvSource *evsource_mice_new (void)
 {
-  ctx_list_free (&ctx->events.items);
-}
-int ctx_events_width (Ctx *ctx)
-{
-  return ctx->events.width;
-}
-int ctx_events_height (Ctx *ctx)
-{
-  return ctx->events.height;
+  if (mmm_evsource_mice_init () == 0)
+    {
+      mrg_mice_this->x = 0;
+      mrg_mice_this->y = 0;
+      return &ctx_ev_src_mice;
+    }
+  return NULL;
 }
 
-float ctx_pointer_x (Ctx *ctx)
-{
-  return ctx->events.pointer_x[0];
-}
+static int evsource_kb_has_event (void);
+static char *evsource_kb_get_event (void);
+static void evsource_kb_destroy (int sign);
+static int evsource_kb_get_fd (void);
 
-float ctx_pointer_y (Ctx *ctx)
-{
-  return ctx->events.pointer_y[0];
-}
+/* kept out of struct to be reachable by atexit */
+static EvSource ctx_ev_src_kb = {
+  NULL,
+  (void*)evsource_kb_has_event,
+  (void*)evsource_kb_get_event,
+  (void*)evsource_kb_destroy,
+  (void*)evsource_kb_get_fd,
+  NULL
+};
 
-int ctx_pointer_is_down (Ctx *ctx, int no)
-{
-  if (no < 0 || no > CTX_MAX_DEVICES) return 0;
-  return ctx->events.pointer_down[no];
-}
+static struct termios orig_attr;
 
-void _ctx_debug_overlays (Ctx *ctx)
+static void real_evsource_kb_destroy (int sign)
 {
-  CtxList *a;
-  ctx_save (ctx);
+  static int done = 0;
 
-  ctx_line_width (ctx, 2);
-  ctx_rgba (ctx, 0,0,0.8,0.5);
-  for (a = ctx->events.items; a; a = a->next)
-  {
-    float current_x = ctx_pointer_x (ctx);
-    float current_y = ctx_pointer_y (ctx);
-    CtxItem *item = a->data;
-    CtxMatrix matrix = item->inv_matrix;
+  if (sign == 0)
+    return;
 
-    _ctx_matrix_apply_transform (&matrix, &current_x, &current_y);
+  if (done)
+    return;
+  done = 1;
 
-    if (current_x >= item->x0 && current_x < item->x1 &&
-        current_y >= item->y0 && current_y < item->y1)
-    {
-      ctx_matrix_invert (&matrix);
-      ctx_set_matrix (ctx, &matrix);
-      _mrg_restore_path (ctx, item->path);
-      ctx_stroke (ctx);
-    }
+  switch (sign)
+  {
+    case  -11:break; /* will be called from atexit with sign==-11 */
+    case   SIGSEGV: break;//fprintf (stderr, " SIGSEGV\n");break;
+    case   SIGABRT: fprintf (stderr, " SIGABRT\n");break;
+    case   SIGBUS:  fprintf (stderr, " SIGBUS\n");break;
+    case   SIGKILL: fprintf (stderr, " SIGKILL\n");break;
+    case   SIGINT:  fprintf (stderr, " SIGINT\n");break;
+    case   SIGTERM: fprintf (stderr, " SIGTERM\n");break;
+    case   SIGQUIT: fprintf (stderr, " SIGQUIT\n");break;
+    default: fprintf (stderr, "sign: %i\n", sign);
+             fprintf (stderr, "%i %i %i %i %i %i %i\n", SIGSEGV, SIGABRT, SIGBUS, SIGKILL, SIGINT, SIGTERM, 
SIGQUIT);
   }
-  ctx_restore (ctx);
+  tcsetattr (STDIN_FILENO, TCSAFLUSH, &orig_attr);
+  //fprintf (stderr, "evsource kb destroy\n");
 }
 
-void ctx_set_render_threads   (Ctx *ctx, int n_threads)
-{
-  // XXX
-}
-int ctx_get_render_threads   (Ctx *ctx)
-{
-  return _ctx_max_threads;
-}
-void ctx_set_hash_cache (Ctx *ctx, int enable_hash_cache)
-{
-  _ctx_enable_hash_cache = enable_hash_cache;
-}
-int ctx_get_hash_cache (Ctx *ctx)
+static void evsource_kb_destroy (int sign)
 {
-  return _ctx_enable_hash_cache;
+  real_evsource_kb_destroy (-11);
 }
 
-int ctx_is_dirty (Ctx *ctx)
-{
-  return ctx->dirty;
-}
-void ctx_set_dirty (Ctx *ctx, int dirty)
+static int evsource_kb_init ()
 {
-  ctx->dirty = dirty;
-}
-
-/*
- * centralized global API for managing file descriptors that
- * wake us up, this to remove sleeping and polling
- */
+//  ioctl(STDIN_FILENO, KDSKBMODE, K_RAW);
+  atexit ((void*) real_evsource_kb_destroy);
+  signal (SIGSEGV, (void*) real_evsource_kb_destroy);
+  signal (SIGABRT, (void*) real_evsource_kb_destroy);
+  signal (SIGBUS,  (void*) real_evsource_kb_destroy);
+  signal (SIGKILL, (void*) real_evsource_kb_destroy);
+  signal (SIGINT,  (void*) real_evsource_kb_destroy);
+  signal (SIGTERM, (void*) real_evsource_kb_destroy);
+  signal (SIGQUIT, (void*) real_evsource_kb_destroy);
 
-#define CTX_MAX_LISTEN_FDS 128 // becomes max clients..
+  struct termios raw;
+  if (tcgetattr (STDIN_FILENO, &orig_attr) == -1)
+    {
+      fprintf (stderr, "error initializing keyboard\n");
+      return -1;
+    }
+  raw = orig_attr;
 
-static int _ctx_listen_fd[CTX_MAX_LISTEN_FDS];
-static int _ctx_listen_fds    = 0;
-static int _ctx_listen_max_fd = 0;
+  cfmakeraw (&raw);
 
-void _ctx_add_listen_fd (int fd)
-{
-  _ctx_listen_fd[_ctx_listen_fds++]=fd;
-  if (fd > _ctx_listen_max_fd)
-    _ctx_listen_max_fd = fd;
-}
+  raw.c_cc[VMIN] = 1; raw.c_cc[VTIME] = 0; /* 1 byte, no timer */
+  if (tcsetattr (STDIN_FILENO, TCSAFLUSH, &raw) < 0)
+    return 0; // XXX? return other value?
 
-void _ctx_remove_listen_fd (int fd)
-{
-  for (int i = 0; i < _ctx_listen_fds; i++)
-  {
-    if (_ctx_listen_fd[i] == fd)
-    {
-      _ctx_listen_fd[i] = _ctx_listen_fd[_ctx_listen_fds-1];
-      _ctx_listen_fds--;
-      return;
-    }
-  }
+  return 0;
 }
-
-int ctx_input_pending (Ctx *ctx, int timeout)
+static int evsource_kb_has_event (void)
 {
   struct timeval tv;
-  fd_set fdset;
-  FD_ZERO (&fdset);
-  for (int i = 0; i < _ctx_listen_fds; i++)
-  {
-    FD_SET (_ctx_listen_fd[i], &fdset);
-  }
-  int input_fds[5];
-  int n_fds;
-  ctx_get_event_fds (ctx, input_fds, &n_fds);
-  for (int i = 0; i < n_fds; i++)
-  {
-    FD_SET (input_fds[i], &fdset);
-  }
+  int retval;
 
-  tv.tv_sec = 0;
-  tv.tv_usec = timeout;
-  tv.tv_sec = timeout / 1000000;
-  tv.tv_usec = timeout % 1000000;
-  int retval = select (_ctx_listen_max_fd + 1, &fdset, NULL, NULL, &tv);
-  if (retval == -1)
-  {
-    perror ("select");
-    return 0;
-  }
-  return retval;
+  fd_set rfds;
+  FD_ZERO (&rfds);
+  FD_SET(STDIN_FILENO, &rfds);
+  tv.tv_sec = 0; tv.tv_usec = 0;
+  retval = select (STDIN_FILENO+1, &rfds, NULL, NULL, &tv);
+  return retval == 1;
 }
 
-void ctx_sdl_set_title (void *self, const char *new_title);
-void ctx_set_title (Ctx *ctx, const char *title)
-{
-#if CTX_SDL
-     // XXX also check we're first/only client?
-   if (ctx_renderer_is_sdl (ctx))
-     ctx_sdl_set_title (ctx_get_renderer (ctx), title);
-#endif
-}
+/* note that a nick can have multiple occurences, the labels
+ * should be kept the same for all occurences of a combination.
+ *
+ * this table is taken from nchanterm.
+ */
+typedef struct MmmKeyCode {
+  char *nick;          /* programmers name for key */
+  char  sequence[10];  /* terminal sequence */
+} MmmKeyCode;
+static const MmmKeyCode ufb_keycodes[]={
+  {"up",                  "\e[A"},
+  {"down",                "\e[B"},
+  {"right",               "\e[C"},
+  {"left",                "\e[D"},
 
-#endif
-/* the parser comes in the end, nothing in ctx knows about the parser  */
+  {"shift-up",            "\e[1;2A"},
+  {"shift-down",          "\e[1;2B"},
+  {"shift-right",         "\e[1;2C"},
+  {"shift-left",          "\e[1;2D"},
 
-#if CTX_PARSER
+  {"alt-up",              "\e[1;3A"},
+  {"alt-down",            "\e[1;3B"},
+  {"alt-right",           "\e[1;3C"},
+  {"alt-left",            "\e[1;3D"},
+  {"alt-shift-up",         "\e[1;4A"},
+  {"alt-shift-down",       "\e[1;4B"},
+  {"alt-shift-right",      "\e[1;4C"},
+  {"alt-shift-left",       "\e[1;4D"},
 
-/* ctx parser, */
+  {"control-up",          "\e[1;5A"},
+  {"control-down",        "\e[1;5B"},
+  {"control-right",       "\e[1;5C"},
+  {"control-left",        "\e[1;5D"},
 
-#define CTX_ID_MAXLEN 64 // in use should not be more than 40!
-                         // to offer headroom for multiplexing
+  /* putty */
+  {"control-up",          "\eOA"},
+  {"control-down",        "\eOB"},
+  {"control-right",       "\eOC"},
+  {"control-left",        "\eOD"},
 
+  {"control-shift-up",    "\e[1;6A"},
+  {"control-shift-down",  "\e[1;6B"},
+  {"control-shift-right", "\e[1;6C"},
+  {"control-shift-left",  "\e[1;6D"},
 
-#define CTX_REPORT_COL_ROW 0
+  {"control-up",          "\eOa"},
+  {"control-down",        "\eOb"},
+  {"control-right",       "\eOc"},
+  {"control-left",        "\eOd"},
 
-struct
-  _CtxParser
-{
-  Ctx       *ctx;
-  int        t_args; // total number of arguments seen for current command
-  int        state;
-#if CTX_PARSER_FIXED_TEMP
-  uint8_t    holding[CTX_PARSER_MAXLEN]; /*  */
-#else
-  uint8_t   *holding;
-#endif
-  int        hold_len;
-  int        pos;
+  {"shift-up",            "\e[a"},
+  {"shift-down",          "\e[b"},
+  {"shift-right",         "\e[c"},
+  {"shift-left",          "\e[d"},
 
-#if CTX_REPORT_COL_ROW
-  int        line; /*  for error reporting */
-  int        col;  /*  for error reporting */
-#endif
-  float      numbers[CTX_PARSER_MAX_ARGS+1];
-  int        n_numbers;
-  int        decimal;
-  CtxCode    command;
-  int        expected_args; /* low digits are literal higher values
-                               carry special meaning */
-  int        n_args;
-  int        texture_done;
-  uint8_t    texture_id[CTX_ID_MAXLEN]; // used in defineTexture only
-  uint64_t   set_key_hash;
-  float      pcx;
-  float      pcy;
-  int        color_components;
-  int        color_stroke; // 0 is fill source  1 is stroke source
-  CtxColorModel   color_model; // 1 gray 3 rgb 4 cmyk
-  float      left_margin; // set by last user provided move_to
-  int        width;       // <- maybe should be float
-  int        height;
-  float      cell_width;
-  float      cell_height;
-  int        cursor_x;    // <- leaking in from terminal
-  int        cursor_y;
+  {"insert",              "\e[2~"},
+  {"delete",              "\e[3~"},
+  {"page-up",             "\e[5~"},
+  {"page-down",           "\e[6~"},
+  {"home",                "\eOH"},
+  {"end",                 "\eOF"},
+  {"home",                "\e[H"},
+  {"end",                 "\e[F"},
+ {"control-delete",       "\e[3;5~"},
+  {"shift-delete",        "\e[3;2~"},
+  {"control-shift-delete","\e[3;6~"},
 
-  int        translate_origin;
+  {"F1",         "\e[25~"},
+  {"F2",         "\e[26~"},
+  {"F3",         "\e[27~"},
+  {"F4",         "\e[26~"},
 
-  CtxColorSpace   color_space_slot;
 
-  void (*exit) (void *exit_data);
-  void *exit_data;
-  int   (*set_prop)(void *prop_data, uint64_t key, const char *data,  int len);
-  int   (*get_prop)(void *prop_data, const char *key, char **data, int *len);
-  void *prop_data;
-  int   prev_byte;
+  {"F1",         "\e[11~"},
+  {"F2",         "\e[12~"},
+  {"F3",         "\e[13~"},
+  {"F4",         "\e[14~"},
+  {"F1",         "\eOP"},
+  {"F2",         "\eOQ"},
+  {"F3",         "\eOR"},
+  {"F4",         "\eOS"},
+  {"F5",         "\e[15~"},
+  {"F6",         "\e[16~"},
+  {"F7",         "\e[17~"},
+  {"F8",         "\e[18~"},
+  {"F9",         "\e[19~"},
+  {"F9",         "\e[20~"},
+  {"F10",        "\e[21~"},
+  {"F11",        "\e[22~"},
+  {"F12",        "\e[23~"},
+  {"tab",         {9, '\0'}},
+  {"shift-tab",   {27, 9, '\0'}}, // also generated by alt-tab in linux console
+  {"alt-space",   {27, ' ', '\0'}},
+  {"shift-tab",   "\e[Z"},
+  {"backspace",   {127, '\0'}},
+  {"space",       " "},
+  {"\e",          "\e"},
+  {"return",      {10,0}},
+  {"return",      {13,0}},
+  /* this section could be autogenerated by code */
+  {"control-a",   {1,0}},
+  {"control-b",   {2,0}},
+  {"control-c",   {3,0}},
+  {"control-d",   {4,0}},
+  {"control-e",   {5,0}},
+  {"control-f",   {6,0}},
+  {"control-g",   {7,0}},
+  {"control-h",   {8,0}}, /* backspace? */
+  {"control-i",   {9,0}},
+  {"control-j",   {10,0}},
+  {"control-k",   {11,0}},
+  {"control-l",   {12,0}},
+  {"control-n",   {14,0}},
+  {"control-o",   {15,0}},
+  {"control-p",   {16,0}},
+  {"control-q",   {17,0}},
+  {"control-r",   {18,0}},
+  {"control-s",   {19,0}},
+  {"control-t",   {20,0}},
+  {"control-u",   {21,0}},
+  {"control-v",   {22,0}},
+  {"control-w",   {23,0}},
+  {"control-x",   {24,0}},
+  {"control-y",   {25,0}},
+  {"control-z",   {26,0}},
+  {"alt-`",       "\e`"},
+  {"alt-0",       "\e0"},
+  {"alt-1",       "\e1"},
+  {"alt-2",       "\e2"},
+  {"alt-3",       "\e3"},
+  {"alt-4",       "\e4"},
+  {"alt-5",       "\e5"},
+  {"alt-6",       "\e6"},
+  {"alt-7",       "\e7"}, /* backspace? */
+  {"alt-8",       "\e8"},
+  {"alt-9",       "\e9"},
+  {"alt-+",       "\e+"},
+  {"alt--",       "\e-"},
+  {"alt-/",       "\e/"},
+  {"alt-a",       "\ea"},
+  {"alt-b",       "\eb"},
+  {"alt-c",       "\ec"},
+  {"alt-d",       "\ed"},
+  {"alt-e",       "\ee"},
+  {"alt-f",       "\ef"},
+  {"alt-g",       "\eg"},
+  {"alt-h",       "\eh"}, /* backspace? */
+  {"alt-i",       "\ei"},
+  {"alt-j",       "\ej"},
+  {"alt-k",       "\ek"},
+  {"alt-l",       "\el"},
+  {"alt-n",       "\em"},
+  {"alt-n",       "\en"},
+  {"alt-o",       "\eo"},
+  {"alt-p",       "\ep"},
+  {"alt-q",       "\eq"},
+  {"alt-r",       "\er"},
+  {"alt-s",       "\es"},
+  {"alt-t",       "\et"},
+  {"alt-u",       "\eu"},
+  {"alt-v",       "\ev"},
+  {"alt-w",       "\ew"},
+  {"alt-x",       "\ex"},
+  {"alt-y",       "\ey"},
+  {"alt-z",       "\ez"},
+  /* Linux Console  */
+  {"home",       "\e[1~"},
+  {"end",        "\e[4~"},
+  {"F1",         "\e[[A"},
+  {"F2",         "\e[[B"},
+  {"F3",         "\e[[C"},
+  {"F4",         "\e[[D"},
+  {"F5",         "\e[[E"},
+  {"F6",         "\e[[F"},
+  {"F7",         "\e[[G"},
+  {"F8",         "\e[[H"},
+  {"F9",         "\e[[I"},
+  {"F10",        "\e[[J"},
+  {"F11",        "\e[[K"},
+  {"F12",        "\e[[L"},
+  {NULL, }
 };
-
-void
-ctx_parser_set_size (CtxParser *parser,
-                 int        width,
-                 int        height,
-                 float      cell_width,
-                 float      cell_height)
+static int fb_keyboard_match_keycode (const char *buf, int length, const MmmKeyCode **ret)
 {
-  if (cell_width > 0)
-    parser->cell_width       = cell_width;
-  if (cell_height > 0)
-    parser->cell_height      = cell_height;
-  if (width > 0)
-    parser->width            = width;
-  if (height > 0)
-    parser->height           = height;
-}
+  int i;
+  int matches = 0;
 
-static CtxParser *
-ctx_parser_init (CtxParser *parser,
-                 Ctx       *ctx,
-                 int        width,
-                 int        height,
-                 float      cell_width,
-                 float      cell_height,
-                 int        cursor_x,
-                 int        cursor_y,
-  int   (*set_prop)(void *prop_data, uint64_t key, const char *data,  int len),
-  int   (*get_prop)(void *prop_Data, const char *key, char **data, int *len),
-                 void  *prop_data,
-                 void (*exit) (void *exit_data),
-                 void *exit_data
-                )
-{
-  ctx_memset (parser, 0, sizeof (CtxParser) );
-#if CTX_REPORT_COL_ROW
-  parser->line             = 1;
-#endif
-  parser->ctx              = ctx;
-  parser->cell_width       = cell_width;
-  parser->cell_height      = cell_height;
-  parser->cursor_x         = cursor_x;
-  parser->cursor_y         = cursor_y;
-  parser->width            = width;
-  parser->height           = height;
-  parser->exit             = exit;
-  parser->exit_data        = exit_data;
-  parser->color_model      = CTX_RGBA;
-  parser->color_stroke     = 0;
-  parser->color_components = 4;
-  parser->command          = CTX_MOVE_TO;
-  parser->set_prop         = set_prop;
-  parser->get_prop         = get_prop;
-  parser->prop_data        = prop_data;
-  return parser;
+  if (!strncmp (buf, "\e[M", MIN(length,3)))
+    {
+      if (length >= 6)
+        return 9001;
+      return 2342;
+    }
+  for (i = 0; ufb_keycodes[i].nick; i++)
+    if (!strncmp (buf, ufb_keycodes[i].sequence, length))
+      {
+        matches ++;
+        if ((int)strlen (ufb_keycodes[i].sequence) == length && ret)
+          {
+            *ret = &ufb_keycodes[i];
+            return 1;
+          }
+      }
+  if (matches != 1 && ret)
+    *ret = NULL;
+  return matches==1?2:matches;
 }
 
-CtxParser *ctx_parser_new (
-  Ctx       *ctx,
-  int        width,
-  int        height,
-  float      cell_width,
-  float      cell_height,
-  int        cursor_x,
-  int        cursor_y,
-  int   (*set_prop)(void *prop_data, uint64_t key, const char *data,  int len),
-  int   (*get_prop)(void *prop_Data, const char *key, char **data, int *len),
-  void  *prop_data,
-  void (*exit) (void *exit_data),
-  void *exit_data)
-{
-  return ctx_parser_init ( (CtxParser *) ctx_calloc (sizeof (CtxParser), 1),
-                           ctx,
-                           width, height,
-                           cell_width, cell_height,
-                           cursor_x, cursor_y, set_prop, get_prop, prop_data,
-                           exit, exit_data);
-}
+//int is_active (void *host)
+//{
+//        return 1;
+//}
 
-void ctx_parser_free (CtxParser *parser)
+static char *evsource_kb_get_event (void)
 {
-#if !CTX_PARSER_FIXED_TEMP
-  if (parser->holding)
-    free (parser->holding);
-#endif
-  free (parser);
+  unsigned char buf[20];
+  int length;
+
+
+  for (length = 0; length < 10; length ++)
+    if (read (STDIN_FILENO, &buf[length], 1) != -1)
+      {
+        const MmmKeyCode *match = NULL;
+
+        //if (!is_active (ctx_ev_src_kb.priv))
+        //  return NULL;
+
+        /* special case ESC, so that we can use it alone in keybindings */
+        if (length == 0 && buf[0] == 27)
+          {
+            struct timeval tv;
+            fd_set rfds;
+            FD_ZERO (&rfds);
+            FD_SET (STDIN_FILENO, &rfds);
+            tv.tv_sec = 0;
+            tv.tv_usec = 1000 * 120;
+            if (select (STDIN_FILENO+1, &rfds, NULL, NULL, &tv) == 0)
+              return strdup ("escape");
+          }
+
+        switch (fb_keyboard_match_keycode ((void*)buf, length + 1, &match))
+          {
+            case 1: /* unique match */
+              if (!match)
+                return NULL;
+              return strdup (match->nick);
+              break;
+            case 0: /* no matches, bail*/
+             {
+                static char ret[256]="";
+                if (length == 0 && ctx_utf8_len (buf[0])>1) /* read a
+                                                             * single unicode
+                                                             * utf8 character
+                                                             */
+                  {
+                    int bytes = read (STDIN_FILENO, &buf[length+1], ctx_utf8_len(buf[0])-1);
+                    if (bytes)
+                    {
+                      buf[ctx_utf8_len(buf[0])]=0;
+                      strcpy (ret, (void*)buf);
+                    }
+                    return strdup(ret); //XXX: simplify
+                  }
+                if (length == 0) /* ascii */
+                  {
+                    buf[1]=0;
+                    strcpy (ret, (void*)buf);
+                    return strdup(ret);
+                  }
+                sprintf (ret, "unhandled %i:'%c' %i:'%c' %i:'%c' %i:'%c' %i:'%c' %i:'%c' %i:'%c'",
+                    length >=0 ? buf[0] : 0,
+                    length >=0 ? buf[0]>31?buf[0]:'?' : ' ',
+                    length >=1 ? buf[1] : 0,
+                    length >=1 ? buf[1]>31?buf[1]:'?' : ' ',
+                    length >=2 ? buf[2] : 0,
+                    length >=2 ? buf[2]>31?buf[2]:'?' : ' ',
+                    length >=3 ? buf[3] : 0,
+                    length >=3 ? buf[3]>31?buf[3]:'?' : ' ',
+                    length >=4 ? buf[4] : 0,
+                    length >=4 ? buf[4]>31?buf[4]:'?' : ' ',
+                    length >=5 ? buf[5] : 0,
+                    length >=5 ? buf[5]>31?buf[5]:'?' : ' ',
+                    length >=6 ? buf[6] : 0,
+                    length >=6 ? buf[6]>31?buf[6]:'?' : ' '
+                    );
+                return strdup(ret);
+            }
+              return NULL;
+            default: /* continue */
+              break;
+          }
+      }
+    else
+      return strdup("key read eek");
+  return strdup("fail");
 }
 
-#define CTX_ARG_COLLECT_NUMBERS             50
-#define CTX_ARG_STRING_OR_NUMBER            100
-#define CTX_ARG_NUMBER_OF_COMPONENTS        200
-#define CTX_ARG_NUMBER_OF_COMPONENTS_PLUS_1 201
+static int evsource_kb_get_fd (void)
+{
+  return STDIN_FILENO;
+}
 
-static int ctx_arguments_for_code (CtxCode code)
+
+static EvSource *evsource_kb_new (void)
 {
-  switch (code)
+  if (evsource_kb_init() == 0)
+  {
+    return &ctx_ev_src_kb;
+  }
+  return NULL;
+}
+
+static int event_check_pending (CtxFb *fb)
+{
+  CtxTiled *tiled = (void*)fb;
+  int events = 0;
+  for (int i = 0; i < fb->evsource_count; i++)
+  {
+    while (evsource_has_event (fb->evsource[i]))
     {
-      case CTX_SAVE:
-      case CTX_START_GROUP:
-      case CTX_END_GROUP:
-      case CTX_IDENTITY:
-      case CTX_CLOSE_PATH:
-      case CTX_BEGIN_PATH:
-      case CTX_RESET:
-      case CTX_FLUSH:
-      case CTX_RESTORE:
-      case CTX_STROKE:
-      case CTX_FILL:
-      case CTX_NEW_PAGE:
-      case CTX_CLIP:
-      case CTX_EXIT:
-        return 0;
-      case CTX_GLOBAL_ALPHA:
-      case CTX_COMPOSITING_MODE:
-      case CTX_BLEND_MODE:
-      case CTX_FONT_SIZE:
-      case CTX_LINE_JOIN:
-      case CTX_LINE_CAP:
-      case CTX_LINE_WIDTH:
-      case CTX_LINE_DASH_OFFSET:
-      case CTX_IMAGE_SMOOTHING:
-      case CTX_SHADOW_BLUR:
-      case CTX_SHADOW_OFFSET_X:
-      case CTX_SHADOW_OFFSET_Y:
-      case CTX_FILL_RULE:
-      case CTX_TEXT_ALIGN:
-      case CTX_TEXT_BASELINE:
-      case CTX_TEXT_DIRECTION:
-      case CTX_MITER_LIMIT:
-      case CTX_REL_VER_LINE_TO:
-      case CTX_REL_HOR_LINE_TO:
-      case CTX_HOR_LINE_TO:
-      case CTX_VER_LINE_TO:
-      case CTX_FONT:
-      case CTX_ROTATE:
-      case CTX_GLYPH:
-        return 1;
-      case CTX_TRANSLATE:
-      case CTX_REL_SMOOTHQ_TO:
-      case CTX_LINE_TO:
-      case CTX_MOVE_TO:
-      case CTX_SCALE:
-      case CTX_REL_LINE_TO:
-      case CTX_REL_MOVE_TO:
-      case CTX_SMOOTHQ_TO:
-        return 2;
-      case CTX_LINEAR_GRADIENT:
-      case CTX_REL_QUAD_TO:
-      case CTX_QUAD_TO:
-      case CTX_RECTANGLE:
-      case CTX_FILL_RECT:
-      case CTX_STROKE_RECT:
-      case CTX_REL_SMOOTH_TO:
-      case CTX_VIEW_BOX:
-      case CTX_SMOOTH_TO:
-        return 4;
-      case CTX_ARC_TO:
-      case CTX_REL_ARC_TO:
-      case CTX_ROUND_RECTANGLE:
-        return 5;
-      case CTX_ARC:
-      case CTX_CURVE_TO:
-      case CTX_REL_CURVE_TO:
-      case CTX_APPLY_TRANSFORM:
-      case CTX_SOURCE_TRANSFORM:
-      case CTX_RADIAL_GRADIENT:
-        return 6;
-      case CTX_STROKE_TEXT:
-      case CTX_TEXT:
-      case CTX_COLOR_SPACE:
-      case CTX_DEFINE_GLYPH:
-      case CTX_KERNING_PAIR:
-      case CTX_TEXTURE:
-      case CTX_DEFINE_TEXTURE:
-        return CTX_ARG_STRING_OR_NUMBER;
-      case CTX_LINE_DASH: /* append to current dashes for each argument encountered */
-        return CTX_ARG_COLLECT_NUMBERS;
-      //case CTX_SET_KEY:
-      case CTX_COLOR:
-      case CTX_SHADOW_COLOR:
-        return CTX_ARG_NUMBER_OF_COMPONENTS;
-      case CTX_GRADIENT_STOP:
-        return CTX_ARG_NUMBER_OF_COMPONENTS_PLUS_1;
+      char *event = evsource_get_event (fb->evsource[i]);
+      if (event)
+      {
+        if (fb->vt_active)
+        {
+          ctx_key_press (tiled->ctx, 0, event, 0); // we deliver all events as key-press, the key_press 
handler disambiguates
+          events++;
+        }
+        free (event);
+      }
+    }
+  }
+  return events;
+}
 
-        default:
-#if 1
-        case CTX_SET_RGBA_U8:
-        case CTX_NOP:
-        case CTX_NEW_EDGE:
-        case CTX_EDGE:
-        case CTX_EDGE_FLIPPED:
-        case CTX_CONT:
-        case CTX_DATA:
-        case CTX_DATA_REV:
-        case CTX_SET_PIXEL:
-        case CTX_REL_LINE_TO_X4:
-        case CTX_REL_LINE_TO_REL_CURVE_TO:
-        case CTX_REL_CURVE_TO_REL_LINE_TO:
-        case CTX_REL_CURVE_TO_REL_MOVE_TO:
-        case CTX_REL_LINE_TO_X2:
-        case CTX_MOVE_TO_REL_LINE_TO:
-        case CTX_REL_LINE_TO_REL_MOVE_TO:
-        case CTX_FILL_MOVE_TO:
-        case CTX_REL_QUAD_TO_REL_QUAD_TO:
-        case CTX_REL_QUAD_TO_S16:
-        case CTX_STROKE_SOURCE:
+int ctx_fb_consume_events (Ctx *ctx)
+{
+  CtxFb *fb = (void*)ctx->renderer;
+  ctx_fb_show_frame (fb, 0);
+  event_check_pending (fb);
+  return 0;
+}
+
+inline static void ctx_fb_reset (CtxFb *fb)
+{
+  ctx_fb_show_frame (fb, 1);
+}
+
+inline static void ctx_fb_flush (CtxFb *fb)
+{
+  ctx_tiled_flush ((CtxTiled*)fb);
+}
+
+void ctx_fb_free (CtxFb *fb)
+{
+  if (fb->is_drm)
+  {
+    ctx_fbdrm_close (fb);
+  }
+
+  ioctl (0, KDSETMODE, KD_TEXT);
+  if (system("stty sane")){};
+  ctx_tiled_free ((CtxTiled*)fb);
+  //free (fb);
+#if CTX_BABL
+  babl_exit ();
 #endif
-        return 0;
-    }
 }
 
-static int ctx_parser_set_command (CtxParser *parser, CtxCode code)
+//static unsigned char *fb_icc = NULL;
+//static long fb_icc_length = 0;
+
+int ctx_renderer_is_fb (Ctx *ctx)
 {
-  if (code < 150 && code >= 32)
+  if (ctx->renderer &&
+      ctx->renderer->free == (void*)ctx_fb_free)
+          return 1;
+  return 0;
+}
+
+static CtxFb *ctx_fb = NULL;
+static void vt_switch_cb (int sig)
+{
+  CtxTiled *tiled = (void*)ctx_fb;
+  if (sig == SIGUSR1)
   {
-  parser->expected_args = ctx_arguments_for_code (code);
-  parser->n_args = 0;
-  parser->texture_done = 0;
-  if (parser->expected_args >= CTX_ARG_NUMBER_OF_COMPONENTS)
+    if (ctx_fb->is_drm)
+      ioctl(ctx_fb->fb_fd, DRM_IOCTL_DROP_MASTER, 0);
+    ioctl (0, VT_RELDISP, 1);
+    ctx_fb->vt_active = 0;
+    ioctl (0, KDSETMODE, KD_TEXT);
+  }
+  else
+  {
+    ioctl (0, VT_RELDISP, VT_ACKACQ);
+    ctx_fb->vt_active = 1;
+    // queue draw
+    tiled->render_frame = ++tiled->frame;
+    ioctl (0, KDSETMODE, KD_GRAPHICS);
+    if (ctx_fb->is_drm)
     {
-      parser->expected_args = (parser->expected_args % 100) + parser->color_components;
+      ioctl(ctx_fb->fb_fd, DRM_IOCTL_SET_MASTER, 0);
+      ctx_fb_flip (ctx_fb);
+    }
+    else
+    {
+      tiled->ctx->dirty=1;
+
+      for (int row = 0; row < CTX_HASH_ROWS; row++)
+      for (int col = 0; col < CTX_HASH_COLS; col++)
+      {
+        tiled->hashes[(row * CTX_HASH_COLS + col) *  20] += 1;
+      }
     }
   }
-  return code;
 }
 
-static void ctx_parser_set_color_model (CtxParser *parser, CtxColorModel color_model, int stroke);
+static int ctx_fb_get_mice_fd (Ctx *ctx)
+{
+  //CtxFb *fb = (void*)ctx->renderer;
+  return _ctx_mice_fd;
+}
 
-static int ctx_parser_resolve_command (CtxParser *parser, const uint8_t *str)
+Ctx *ctx_new_fb (int width, int height, int drm)
 {
-  uint64_t ret = str[0]; /* if it is single char it already is the CtxCode */
+#if CTX_RASTERIZER
+  CtxFb *fb = calloc (sizeof (CtxFb), 1);
 
-  /* this is handled outside the hashing to make it possible to be case insensitive
-   * with the rest.
-   */
-  if (str[0] == CTX_SET_KEY && str[1] && str[2] == 0)
+  CtxTiled *tiled = (void*)fb;
+  ctx_fb = fb;
+  if (drm)
+    fb->fb = ctx_fbdrm_new (fb, &tiled->width, &tiled->height);
+  if (fb->fb)
   {
-    switch (str[1])
+    fb->is_drm         = 1;
+    width              = tiled->width;
+    height             = tiled->height;
+    /*
+       we're ignoring the input width and height ,
+       maybe turn them into properties - for
+       more generic handling.
+     */
+    fb->fb_mapped_size = tiled->width * tiled->height * 4;
+    fb->fb_bits        = 32;
+    fb->fb_bpp         = 4;
+  }
+  else
+  {
+  fb->fb_fd = open ("/dev/fb0", O_RDWR);
+  if (fb->fb_fd > 0)
+    fb->fb_path = strdup ("/dev/fb0");
+  else
+  {
+    fb->fb_fd = open ("/dev/graphics/fb0", O_RDWR);
+    if (fb->fb_fd > 0)
     {
-      case 'm': return ctx_parser_set_command (parser, CTX_COMPOSITING_MODE);
-      case 'B': return ctx_parser_set_command (parser, CTX_BLEND_MODE);
-      case 'l': return ctx_parser_set_command (parser, CTX_MITER_LIMIT);
-      case 't': return ctx_parser_set_command (parser, CTX_TEXT_ALIGN);
-      case 'b': return ctx_parser_set_command (parser, CTX_TEXT_BASELINE);
-      case 'd': return ctx_parser_set_command (parser, CTX_TEXT_DIRECTION);
-      case 'j': return ctx_parser_set_command (parser, CTX_LINE_JOIN);
-      case 'c': return ctx_parser_set_command (parser, CTX_LINE_CAP);
-      case 'w': return ctx_parser_set_command (parser, CTX_LINE_WIDTH);
-      case 'D': return ctx_parser_set_command (parser, CTX_LINE_DASH_OFFSET);
-      case 'S': return ctx_parser_set_command (parser, CTX_IMAGE_SMOOTHING);
-      case 'C': return ctx_parser_set_command (parser, CTX_SHADOW_COLOR);
-      case 's': return ctx_parser_set_command (parser, CTX_SHADOW_BLUR);
-      case 'x': return ctx_parser_set_command (parser, CTX_SHADOW_OFFSET_X);
-      case 'y': return ctx_parser_set_command (parser, CTX_SHADOW_OFFSET_Y);
-      case 'a': return ctx_parser_set_command (parser, CTX_GLOBAL_ALPHA);
-      case 'f': return ctx_parser_set_command (parser, CTX_FONT_SIZE);
-      case 'r': return ctx_parser_set_command (parser, CTX_FILL_RULE);
+      fb->fb_path = strdup ("/dev/graphics/fb0");
+    }
+    else
+    {
+      free (fb);
+      return NULL;
     }
   }
 
-  if (str[0] && str[1])
+  if (ioctl(fb->fb_fd, FBIOGET_FSCREENINFO, &fb->finfo))
     {
-      uint64_t str_hash;
-      /* trim ctx_ and CTX_ prefix */
-      if ( (str[0] == 'c' && str[1] == 't' && str[2] == 'x' && str[3] == '_') ||
-           (str[0] == 'C' && str[1] == 'T' && str[2] == 'X' && str[3] == '_') )
-        {
-          str += 4;
-        }
-      if ( (str[0] == 's' && str[1] == 'e' && str[2] == 't' && str[3] == '_') )
-        { str += 4; }
-      str_hash = ctx_strhash ( (char *) str);
-      switch (str_hash)
-        {
-          /* first a list of mappings to one_char hashes, handled in a
-           * separate fast path switch without hashing
-           */
-          case CTX_arcTo:          ret = CTX_ARC_TO; break;
-          case CTX_arc:            ret = CTX_ARC; break;
-          case CTX_curveTo:        ret = CTX_CURVE_TO; break;
-          case CTX_restore:        ret = CTX_RESTORE; break;
-          case CTX_stroke:         ret = CTX_STROKE; break;
-          case CTX_fill:           ret = CTX_FILL; break;
-          case CTX_flush:          ret = CTX_FLUSH; break;
-          case CTX_horLineTo:      ret = CTX_HOR_LINE_TO; break;
-          case CTX_rotate:         ret = CTX_ROTATE; break;
-          case CTX_color:          ret = CTX_COLOR; break;
-          case CTX_lineTo:         ret = CTX_LINE_TO; break;
-          case CTX_moveTo:         ret = CTX_MOVE_TO; break;
-          case CTX_scale:          ret = CTX_SCALE; break;
-          case CTX_newPage:        ret = CTX_NEW_PAGE; break;
-          case CTX_quadTo:         ret = CTX_QUAD_TO; break;
-          case CTX_viewBox:        ret = CTX_VIEW_BOX; break;
-          case CTX_smooth_to:      ret = CTX_SMOOTH_TO; break;
-          case CTX_smooth_quad_to: ret = CTX_SMOOTHQ_TO; break;
-          case CTX_clear:          ret = CTX_COMPOSITE_CLEAR; break;
-          case CTX_copy:           ret = CTX_COMPOSITE_COPY; break;
-          case CTX_destinationOver:  ret = CTX_COMPOSITE_DESTINATION_OVER; break;
-          case CTX_destinationIn:    ret = CTX_COMPOSITE_DESTINATION_IN; break;
-          case CTX_destinationOut:   ret = CTX_COMPOSITE_DESTINATION_OUT; break;
-          case CTX_sourceOver:       ret = CTX_COMPOSITE_SOURCE_OVER; break;
-          case CTX_sourceAtop:       ret = CTX_COMPOSITE_SOURCE_ATOP; break;
-          case CTX_destinationAtop:  ret = CTX_COMPOSITE_DESTINATION_ATOP; break;
-          case CTX_sourceOut:        ret = CTX_COMPOSITE_SOURCE_OUT; break;
-          case CTX_sourceIn:         ret = CTX_COMPOSITE_SOURCE_IN; break;
-          case CTX_xor:              ret = CTX_COMPOSITE_XOR; break;
-          case CTX_darken:           ret = CTX_BLEND_DARKEN; break;
-          case CTX_lighten:          ret = CTX_BLEND_LIGHTEN; break;
-          //case CTX_color:          ret = CTX_BLEND_COLOR; break;
-          //
-          //  XXX check that he special casing for color works
-          //      it is the first collision and it is due to our own
-          //      color, not w3c for now unique use of it
-          //
-          case CTX_hue:            ret = CTX_BLEND_HUE; break;
-          case CTX_multiply:       ret = CTX_BLEND_MULTIPLY; break;
-          case CTX_normal:         ret = CTX_BLEND_NORMAL;break;
-          case CTX_screen:         ret = CTX_BLEND_SCREEN;break;
-          case CTX_difference:     ret = CTX_BLEND_DIFFERENCE; break;
-          case CTX_reset:          ret = CTX_RESET; break;
-          case CTX_verLineTo:      ret = CTX_VER_LINE_TO; break;
-          case CTX_exit:
-          case CTX_done:           ret = CTX_EXIT; break;
-          case CTX_closePath:      ret = CTX_CLOSE_PATH; break;
-          case CTX_beginPath:
-          case CTX_newPath:        ret = CTX_BEGIN_PATH; break;
-          case CTX_relArcTo:       ret = CTX_REL_ARC_TO; break;
-          case CTX_clip:           ret = CTX_CLIP; break;
-          case CTX_relCurveTo:     ret = CTX_REL_CURVE_TO; break;
-          case CTX_startGroup:     ret = CTX_START_GROUP; break;
-          case CTX_endGroup:       ret = CTX_END_GROUP; break;
-          case CTX_save:           ret = CTX_SAVE; break;
-          case CTX_translate:      ret = CTX_TRANSLATE; break;
-          case CTX_linearGradient: ret = CTX_LINEAR_GRADIENT; break;
-          case CTX_relHorLineTo:   ret = CTX_REL_HOR_LINE_TO; break;
-          case CTX_relLineTo:      ret = CTX_REL_LINE_TO; break;
-          case CTX_relMoveTo:      ret = CTX_REL_MOVE_TO; break;
-          case CTX_font:           ret = CTX_FONT; break;
-          case CTX_radialGradient:ret = CTX_RADIAL_GRADIENT; break;
-          case CTX_gradientAddStop:
-          case CTX_addStop:        ret = CTX_GRADIENT_STOP; break;
-          case CTX_relQuadTo:      ret = CTX_REL_QUAD_TO; break;
-          case CTX_rectangle:
-          case CTX_rect:           ret = CTX_RECTANGLE; break;
-          case CTX_roundRectangle: ret = CTX_ROUND_RECTANGLE; break;
-          case CTX_relSmoothTo:    ret = CTX_REL_SMOOTH_TO; break;
-          case CTX_relSmoothqTo:   ret = CTX_REL_SMOOTHQ_TO; break;
-          case CTX_strokeText:     ret = CTX_STROKE_TEXT; break;
-          case CTX_strokeRect:     ret = CTX_STROKE_RECT; break;
-          case CTX_fillRect:       ret = CTX_FILL_RECT; break;
-          case CTX_relVerLineTo:   ret = CTX_REL_VER_LINE_TO; break;
-          case CTX_text:           ret = CTX_TEXT; break;
-          case CTX_identity:       ret = CTX_IDENTITY; break;
-          case CTX_transform:      ret = CTX_APPLY_TRANSFORM; break;
-          case CTX_sourceTransform: ret = CTX_SOURCE_TRANSFORM; break;
-          case CTX_texture:        ret = CTX_TEXTURE; break;
-          case CTX_defineTexture:  ret = CTX_DEFINE_TEXTURE; break;
-#if 0
-          case CTX_rgbSpace:
-            return ctx_parser_set_command (parser, CTX_SET_RGB_SPACE);
-          case CTX_cmykSpace:
-            return ctx_parser_set_command (parser, CTX_SET_CMYK_SPACE);
-          case CTX_drgbSpace:
-            return ctx_parser_set_command (parser, CTX_SET_DRGB_SPACE);
-#endif
-          case CTX_defineGlyph:
-            return ctx_parser_set_command (parser, CTX_DEFINE_GLYPH);
-          case CTX_kerningPair:
-            return ctx_parser_set_command (parser, CTX_KERNING_PAIR);
+      fprintf (stderr, "error getting fbinfo\n");
+      close (fb->fb_fd);
+      free (fb->fb_path);
+      free (fb);
+      return NULL;
+    }
 
-          case CTX_colorSpace:
-            return ctx_parser_set_command (parser, CTX_COLOR_SPACE);
-          case CTX_fillRule:
-            return ctx_parser_set_command (parser, CTX_FILL_RULE);
-          case CTX_fontSize:
-          case CTX_setFontSize:
-            return ctx_parser_set_command (parser, CTX_FONT_SIZE);
-          case CTX_compositingMode:
-            return ctx_parser_set_command (parser, CTX_COMPOSITING_MODE);
+   if (ioctl(fb->fb_fd, FBIOGET_VSCREENINFO, &fb->vinfo))
+     {
+       fprintf (stderr, "error getting fbinfo\n");
+      close (fb->fb_fd);
+      free (fb->fb_path);
+      free (fb);
+      return NULL;
+     }
 
-          case CTX_blend:
-          case CTX_blending:
-          case CTX_blendMode:
-            return ctx_parser_set_command (parser, CTX_BLEND_MODE);
+//fprintf (stderr, "%s\n", fb->fb_path);
+  width = tiled->width = fb->vinfo.xres;
+  height = tiled->height = fb->vinfo.yres;
 
-          case CTX_miterLimit:
-            return ctx_parser_set_command (parser, CTX_MITER_LIMIT);
-          case CTX_textAlign:
-            return ctx_parser_set_command (parser, CTX_TEXT_ALIGN);
-          case CTX_textBaseline:
-            return ctx_parser_set_command (parser, CTX_TEXT_BASELINE);
-          case CTX_textDirection:
-            return ctx_parser_set_command (parser, CTX_TEXT_DIRECTION);
-          case CTX_join:
-          case CTX_lineJoin:
-          case CTX_setLineJoin:
-            return ctx_parser_set_command (parser, CTX_LINE_JOIN);
-          case CTX_glyph:
-            return ctx_parser_set_command (parser, CTX_GLYPH);
-          case CTX_cap:
-          case CTX_lineCap:
-          case CTX_setLineCap:
-            return ctx_parser_set_command (parser, CTX_LINE_CAP);
-          case CTX_lineDash:
-            return ctx_parser_set_command (parser, CTX_LINE_DASH);
-          case CTX_lineWidth:
-          case CTX_setLineWidth:
-            return ctx_parser_set_command (parser, CTX_LINE_WIDTH);
-          case CTX_lineDashOffset:
-            return ctx_parser_set_command (parser, CTX_LINE_DASH_OFFSET);
-          case CTX_imageSmoothing:
-            return ctx_parser_set_command (parser, CTX_IMAGE_SMOOTHING);
-          case CTX_shadowColor:
-            return ctx_parser_set_command (parser, CTX_SHADOW_COLOR);
-          case CTX_shadowBlur:
-            return ctx_parser_set_command (parser, CTX_SHADOW_BLUR);
-          case CTX_shadowOffsetX:
-            return ctx_parser_set_command (parser, CTX_SHADOW_OFFSET_X);
-          case CTX_shadowOffsetY:
-            return ctx_parser_set_command (parser, CTX_SHADOW_OFFSET_Y);
-          case CTX_globalAlpha:
-            return ctx_parser_set_command (parser, CTX_GLOBAL_ALPHA);
+  fb->fb_bits = fb->vinfo.bits_per_pixel;
+//fprintf (stderr, "fb bits: %i\n", fb->fb_bits);
 
-          case CTX_strokeSource:
-            return ctx_parser_set_command (parser, CTX_STROKE_SOURCE);
+  if (fb->fb_bits == 16)
+    fb->fb_bits =
+      fb->vinfo.red.length +
+      fb->vinfo.green.length +
+      fb->vinfo.blue.length;
 
-          /* strings are handled directly here,
-           * instead of in the one-char handler, using return instead of break
-           */
-          case CTX_gray:
-            ctx_parser_set_color_model (parser, CTX_GRAY, 0);
-            return ctx_parser_set_command (parser, CTX_COLOR);
-          case CTX_graya:
-            ctx_parser_set_color_model (parser, CTX_GRAYA, 0);
-            return ctx_parser_set_command (parser, CTX_COLOR);
-          case CTX_rgb:
-            ctx_parser_set_color_model (parser, CTX_RGB, 0);
-            return ctx_parser_set_command (parser, CTX_COLOR);
-          case CTX_drgb:
-            ctx_parser_set_color_model (parser, CTX_DRGB, 0);
-            return ctx_parser_set_command (parser, CTX_COLOR);
-          case CTX_rgba:
-            ctx_parser_set_color_model (parser, CTX_RGBA, 0);
-            return ctx_parser_set_command (parser, CTX_COLOR);
-          case CTX_drgba:
-            ctx_parser_set_color_model (parser, CTX_DRGBA, 0);
-            return ctx_parser_set_command (parser, CTX_COLOR);
-          case CTX_cmyk:
-            ctx_parser_set_color_model (parser, CTX_CMYK, 0);
-            return ctx_parser_set_command (parser, CTX_COLOR);
-          case CTX_cmyka:
-            ctx_parser_set_color_model (parser, CTX_CMYKA, 0);
-            return ctx_parser_set_command (parser, CTX_COLOR);
-          case CTX_lab:
-            ctx_parser_set_color_model (parser, CTX_LAB, 0);
-            return ctx_parser_set_command (parser, CTX_COLOR);
-          case CTX_laba:
-            ctx_parser_set_color_model (parser, CTX_LABA, 0);
-            return ctx_parser_set_command (parser, CTX_COLOR);
-          case CTX_lch:
-            ctx_parser_set_color_model (parser, CTX_LCH, 0);
-            return ctx_parser_set_command (parser, CTX_COLOR);
-          case CTX_lcha:
-            ctx_parser_set_color_model (parser, CTX_LCHA, 0);
-            return ctx_parser_set_command (parser, CTX_COLOR);
+   else if (fb->fb_bits == 8)
+  {
+    unsigned short red[256],  green[256],  blue[256];
+    unsigned short original_red[256];
+    unsigned short original_green[256];
+    unsigned short original_blue[256];
+    struct fb_cmap cmap = {0, 256, red, green, blue, NULL};
+    struct fb_cmap original_cmap = {0, 256, original_red, original_green, original_blue, NULL};
+    int i;
 
-          /* and a full repeat of the above, with S for Stroke suffix */
-          case CTX_grayS:
-            ctx_parser_set_color_model (parser, CTX_GRAY, 1);
-            return ctx_parser_set_command (parser, CTX_COLOR);
-          case CTX_grayaS:
-            ctx_parser_set_color_model (parser, CTX_GRAYA, 1);
-            return ctx_parser_set_command (parser, CTX_COLOR);
-          case CTX_rgbS:
-            ctx_parser_set_color_model (parser, CTX_RGB, 1);
-            return ctx_parser_set_command (parser, CTX_COLOR);
-          case CTX_drgbS:
-            ctx_parser_set_color_model (parser, CTX_DRGB, 1);
-            return ctx_parser_set_command (parser, CTX_COLOR);
-          case CTX_rgbaS:
-            ctx_parser_set_color_model (parser, CTX_RGBA, 1);
-            return ctx_parser_set_command (parser, CTX_COLOR);
-          case CTX_drgbaS:
-            ctx_parser_set_color_model (parser, CTX_DRGBA, 1);
-            return ctx_parser_set_command (parser, CTX_COLOR);
-          case CTX_cmykS:
-            ctx_parser_set_color_model (parser, CTX_CMYK, 1);
-            return ctx_parser_set_command (parser, CTX_COLOR);
-          case CTX_cmykaS:
-            ctx_parser_set_color_model (parser, CTX_CMYKA, 1);
-            return ctx_parser_set_command (parser, CTX_COLOR);
-          case CTX_labS:
-            ctx_parser_set_color_model (parser, CTX_LAB, 1);
-            return ctx_parser_set_command (parser, CTX_COLOR);
-          case CTX_labaS:
-            ctx_parser_set_color_model (parser, CTX_LABA, 1);
-            return ctx_parser_set_command (parser, CTX_COLOR);
-          case CTX_lchS:
-            ctx_parser_set_color_model (parser, CTX_LCH, 1);
-            return ctx_parser_set_command (parser, CTX_COLOR);
-          case CTX_lchaS:
-            ctx_parser_set_color_model (parser, CTX_LCHA, 1);
-            return ctx_parser_set_command (parser, CTX_COLOR);
+    /* do we really need to restore it ? */
+    if (ioctl (fb->fb_fd, FBIOPUTCMAP, &original_cmap) == -1)
+    {
+      fprintf (stderr, "palette initialization problem %i\n", __LINE__);
+    }
 
-          /* words that correspond to low integer constants
-          */
-          case CTX_nonzero:     return CTX_FILL_RULE_WINDING;
-          case CTX_non_zero:    return CTX_FILL_RULE_WINDING;
-          case CTX_winding:     return CTX_FILL_RULE_WINDING;
-          case CTX_evenOdd:
-          case CTX_even_odd:    return CTX_FILL_RULE_EVEN_ODD;
-          case CTX_bevel:       return CTX_JOIN_BEVEL;
-          case CTX_round:       return CTX_JOIN_ROUND;
-          case CTX_miter:       return CTX_JOIN_MITER;
-          case CTX_none:        return CTX_CAP_NONE;
-          case CTX_square:      return CTX_CAP_SQUARE;
-          case CTX_start:       return CTX_TEXT_ALIGN_START;
-          case CTX_end:         return CTX_TEXT_ALIGN_END;
-          case CTX_left:        return CTX_TEXT_ALIGN_LEFT;
-          case CTX_right:       return CTX_TEXT_ALIGN_RIGHT;
-          case CTX_center:      return CTX_TEXT_ALIGN_CENTER;
-          case CTX_top:         return CTX_TEXT_BASELINE_TOP;
-          case CTX_bottom :     return CTX_TEXT_BASELINE_BOTTOM;
-          case CTX_middle:      return CTX_TEXT_BASELINE_MIDDLE;
-          case CTX_alphabetic:  return CTX_TEXT_BASELINE_ALPHABETIC;
-          case CTX_hanging:     return CTX_TEXT_BASELINE_HANGING;
-          case CTX_ideographic: return CTX_TEXT_BASELINE_IDEOGRAPHIC;
+    for (i = 0; i < 256; i++)
+    {
+      red[i]   = ((( i >> 5) & 0x7) << 5) << 8;
+      green[i] = ((( i >> 2) & 0x7) << 5) << 8;
+      blue[i]  = ((( i >> 0) & 0x3) << 6) << 8;
+    }
 
-          case CTX_userRGB:     return CTX_COLOR_SPACE_USER_RGB;
-          case CTX_deviceRGB:   return CTX_COLOR_SPACE_DEVICE_RGB;
-          case CTX_userCMYK:    return CTX_COLOR_SPACE_USER_CMYK;
-          case CTX_deviceCMYK:  return CTX_COLOR_SPACE_DEVICE_CMYK;
-#undef STR
-#undef LOWER
-          default:
-            ret = str_hash;
-        }
+    if (ioctl (fb->fb_fd, FBIOPUTCMAP, &cmap) == -1)
+    {
+      fprintf (stderr, "palette initialization problem %i\n", __LINE__);
     }
-  if (ret == CTX_CLOSE_PATH2)
-   {
-     ret = CTX_CLOSE_PATH;
-   }
+  }
 
-  return ctx_parser_set_command (parser, (CtxCode) ret);
-}
+  fb->fb_bpp = fb->vinfo.bits_per_pixel / 8;
+  fb->fb_mapped_size = fb->finfo.smem_len;
+                                              
+  fb->fb = mmap (NULL, fb->fb_mapped_size, PROT_READ|PROT_WRITE, MAP_SHARED, fb->fb_fd, 0);
+  }
+  if (!fb->fb)
+    return NULL;
+  tiled->pixels = calloc (fb->fb_mapped_size, 1);
+  ctx_fb_events = 1;
 
-enum
-{
-  CTX_PARSER_NEUTRAL = 0,
-  CTX_PARSER_NUMBER,
-  CTX_PARSER_NEGATIVE_NUMBER,
-  CTX_PARSER_WORD,
-  CTX_PARSER_COMMENT,
-  CTX_PARSER_STRING_APOS,
-  CTX_PARSER_STRING_QUOT,
-  CTX_PARSER_STRING_APOS_ESCAPED,
-  CTX_PARSER_STRING_QUOT_ESCAPED,
-  CTX_PARSER_STRING_A85,
-  CTX_PARSER_STRING_YENC,
-} CTX_STATE;
+#if CTX_BABL
+  babl_init ();
+#endif
 
-static void ctx_parser_set_color_model (CtxParser *parser, CtxColorModel color_model, int stroke)
-{
-  parser->color_model      = color_model;
-  parser->color_stroke     = stroke;
-  parser->color_components = ctx_color_model_get_components (color_model);
+  ctx_get_contents ("file:///tmp/ctx.icc", &sdl_icc, &sdl_icc_length);
+
+  tiled->ctx      = ctx_new ();
+  tiled->ctx_copy = ctx_new ();
+  tiled->width    = width;
+  tiled->height   = height;
+
+  ctx_set_renderer (tiled->ctx, fb);
+  ctx_set_renderer (tiled->ctx_copy, fb);
+  ctx_set_texture_cache (tiled->ctx_copy, tiled->ctx);
+
+  ctx_set_size (tiled->ctx, width, height);
+  ctx_set_size (tiled->ctx_copy, width, height);
+
+  tiled->flush = (void*)ctx_fb_flush;
+  tiled->reset = (void*)ctx_fb_reset;
+  tiled->free  = (void*)ctx_fb_free;
+  tiled->set_clipboard = (void*)ctx_fb_set_clipboard;
+  tiled->get_clipboard = (void*)ctx_fb_get_clipboard;
+
+  for (int i = 0; i < _ctx_max_threads; i++)
+  {
+    tiled->host[i] = ctx_new_for_framebuffer (tiled->pixels,
+                   tiled->width/CTX_HASH_COLS, tiled->height/CTX_HASH_ROWS,
+                   tiled->width * 4, CTX_FORMAT_RGBA8); // this format
+                                  // is overriden in  thread
+    ((CtxRasterizer*)(tiled->host[i]->renderer))->swap_red_green = 1;
+    ctx_set_texture_source (tiled->host[i], tiled->ctx);
+  }
+
+  mtx_init (&tiled->mtx, mtx_plain);
+  cnd_init (&tiled->cond);
+
+#define start_thread(no)\
+  if(_ctx_max_threads>no){ \
+    static void *args[2]={(void*)no, };\
+    thrd_t tid;\
+    args[1]=fb;\
+    thrd_create (&tid, (void*)ctx_tiled_render_fun, args);\
+  }
+  start_thread(0);
+  start_thread(1);
+  start_thread(2);
+  start_thread(3);
+  start_thread(4);
+  start_thread(5);
+  start_thread(6);
+  start_thread(7);
+  start_thread(8);
+  start_thread(9);
+  start_thread(10);
+  start_thread(11);
+  start_thread(12);
+  start_thread(13);
+  start_thread(14);
+  start_thread(15);
+#undef start_thread
+
+  ctx_flush (tiled->ctx);
+
+  EvSource *kb = evsource_kb_new ();
+  if (kb)
+  {
+    fb->evsource[fb->evsource_count++] = kb;
+    kb->priv = fb;
+  }
+  EvSource *mice  = evsource_mice_new ();
+  if (mice)
+  {
+    fb->evsource[fb->evsource_count++] = mice;
+    mice->priv = fb;
+  }
+
+  fb->vt_active = 1;
+  ioctl(0, KDSETMODE, KD_GRAPHICS);
+  signal (SIGUSR1, vt_switch_cb);
+  signal (SIGUSR2, vt_switch_cb);
+  struct vt_stat st;
+  if (ioctl (0, VT_GETSTATE, &st) == -1)
+  {
+    ctx_log ("VT_GET_MODE on vt %i failed\n", fb->vt);
+    return NULL;
+  }
+
+  fb->vt = st.v_active;
+
+  struct vt_mode mode;
+  mode.mode   = VT_PROCESS;
+  mode.relsig = SIGUSR1;
+  mode.acqsig = SIGUSR2;
+  if (ioctl (0, VT_SETMODE, &mode) < 0)
+  {
+    ctx_log ("VT_SET_MODE on vt %i failed\n", fb->vt);
+    return NULL;
+  }
+
+  return tiled->ctx;
+#else
+  return NULL;
+#endif
 }
+#else
 
-static void ctx_parser_get_color_rgba (CtxParser *parser, int offset, float *red, float *green, float *blue, 
float *alpha)
+int ctx_renderer_is_fb (Ctx *ctx)
 {
-  /* XXX - this function is to be deprecated */
-  *alpha = 1.0;
-  switch (parser->color_model)
-    {
-      case CTX_GRAYA:
-        *alpha = parser->numbers[offset + 1];
-        /* FALLTHROUGH */
-      case CTX_GRAY:
-        *red = *green = *blue = parser->numbers[offset + 0];
-        break;
-      default:
-      case CTX_LABA: // NYI - needs RGB profile
-      case CTX_LCHA: // NYI - needs RGB profile
-      case CTX_RGBA:
-        *alpha = parser->numbers[offset + 3];
-        /* FALLTHROUGH */
-      case CTX_LAB: // NYI
-      case CTX_LCH: // NYI
-      case CTX_RGB:
-        *red = parser->numbers[offset + 0];
-        *green = parser->numbers[offset + 1];
-        *blue = parser->numbers[offset + 2];
-        break;
-      case CTX_CMYKA:
-        *alpha = parser->numbers[offset + 4];
-        /* FALLTHROUGH */
-      case CTX_CMYK:
-        /* should use profile instead  */
-        *red = (1.0-parser->numbers[offset + 0]) *
-               (1.0 - parser->numbers[offset + 3]);
-        *green = (1.0-parser->numbers[offset + 1]) *
-                 (1.0 - parser->numbers[offset + 3]);
-        *blue = (1.0-parser->numbers[offset + 2]) *
-                (1.0 - parser->numbers[offset + 3]);
-        break;
-    }
+  return 0;
 }
+#endif
+#endif
 
-static void ctx_parser_dispatch_command (CtxParser *parser)
+#if CTX_SDL
+
+/**/
+
+typedef struct _CtxSDL CtxSDL;
+struct _CtxSDL
 {
-  CtxCode cmd = parser->command;
-  Ctx *ctx = parser->ctx;
+   CtxTiled  tiled;
+   /* where we diverge from fb*/
+   int           key_balance;
+   int           key_repeat;
+   int           lctrl;
+   int           lalt;
+   int           rctrl;
+   int           lshift;
+   int           rshift;
 
-  if (parser->expected_args != CTX_ARG_STRING_OR_NUMBER &&
-      parser->expected_args != CTX_ARG_COLLECT_NUMBERS &&
-      parser->expected_args != parser->n_numbers)
-    {
-#if CTX_REPORT_COL_ROW
-         fprintf (stderr, "ctx:%i:%i %c got %i instead of %i args\n",
-               parser->line, parser->col,
-               cmd, parser->n_numbers, parser->expected_args);
+   SDL_Window   *window;
+   SDL_Renderer *renderer;
+   SDL_Texture  *texture;
+
+// cnd_t  cond;
+// mtx_t  mtx;
+   int           fullscreen;
+};
+
+#include "stb_image_write.h"
+
+void ctx_screenshot (Ctx *ctx, const char *output_path)
+{
+#if CTX_SCREENSHOT
+  int valid = 0;
+  CtxSDL *sdl = (void*)ctx->renderer;
+
+  if (ctx_renderer_is_sdl (ctx)) valid = 1;
+#if CTX_FB
+  if (ctx_renderer_is_fb  (ctx)) valid = 1;
 #endif
-      //return;
-    }
 
-#define arg(a)  (parser->numbers[a])
-  parser->command = CTX_NOP;
-  //parser->n_args = 0;
-  switch (cmd)
+  if (!valid)
+    return;
+
+#if CTX_FB
+  // we rely on the same layout
+  for (int i = 0; i < sdl->width * sdl->height; i++)
+  {
+    int tmp = sdl->pixels[i*4];
+    sdl->pixels[i*4] = sdl->pixels[i*4 + 2];
+    sdl->pixels[i*4 + 2] = tmp;
+  }
+#endif
+
+  stbi_write_png (output_path, sdl->width, sdl->height, 4, sdl->pixels, sdl->width*4);
+
+#if CTX_FB
+  for (int i = 0; i < sdl->width * sdl->height; i++)
+  {
+    int tmp = sdl->pixels[i*4];
+    sdl->pixels[i*4] = sdl->pixels[i*4 + 2];
+    sdl->pixels[i*4 + 2] = tmp;
+  }
+#endif
+#endif
+}
+
+void ctx_sdl_set_title (void *self, const char *new_title)
+{
+   CtxSDL *sdl = self;
+   SDL_SetWindowTitle (sdl->window, new_title);
+}
+
+static void ctx_sdl_show_frame (CtxSDL *sdl, int block)
+{
+  CtxTiled *tiled = &sdl->tiled;
+  if (tiled->shown_cursor != tiled->ctx->cursor)
+  {
+    tiled->shown_cursor = tiled->ctx->cursor;
+    SDL_Cursor *new_cursor =  NULL;
+    switch (tiled->shown_cursor)
     {
-      default:
-        break; // to silence warnings about missing ones
-      case CTX_PRESERVE:
-        ctx_preserve (ctx);
+      case CTX_CURSOR_UNSET: // XXX: document how this differs from none
+                             //      perhaps falling back to arrow?
         break;
-      case CTX_FILL:
-        ctx_fill (ctx);
+      case CTX_CURSOR_NONE:
+        new_cursor = NULL;
         break;
-      case CTX_SAVE:
-        ctx_save (ctx);
+      case CTX_CURSOR_ARROW:
+        new_cursor = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_ARROW);
         break;
-      case CTX_START_GROUP:
-        ctx_start_group (ctx);
+      case CTX_CURSOR_CROSSHAIR:
+        new_cursor = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_CROSSHAIR);
         break;
-      case CTX_END_GROUP:
-        ctx_end_group (ctx);
+      case CTX_CURSOR_WAIT:
+        new_cursor = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_WAIT);
         break;
-      case CTX_STROKE:
-        ctx_stroke (ctx);
+      case CTX_CURSOR_HAND:
+        new_cursor = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_HAND);
         break;
-      case CTX_STROKE_SOURCE:
-        ctx_stroke_source (ctx);
+      case CTX_CURSOR_IBEAM:
+        new_cursor = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_IBEAM);
         break;
-      case CTX_RESTORE:
-        ctx_restore (ctx);
+      case CTX_CURSOR_MOVE:
+      case CTX_CURSOR_RESIZE_ALL:
+        new_cursor = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZEALL);
         break;
-#if CTX_ENABLE_CM
-      case CTX_COLOR_SPACE:
-        if (parser->n_numbers == 1)
-        {
-          parser->color_space_slot = (CtxColorSpace) arg(0);
-          parser->command = CTX_COLOR_SPACE; // did this work without?
-        }
-        else
-        {
-          ctx_colorspace (ctx, (CtxColorSpace)parser->color_space_slot,
-                               parser->holding, parser->pos);
-        }
+      case CTX_CURSOR_RESIZE_N:
+      case CTX_CURSOR_RESIZE_S:
+        new_cursor = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZENS);
         break;
-#endif
-      case CTX_KERNING_PAIR:
-        switch (parser->n_args)
-        {
-          case 0:
-            parser->numbers[0] = ctx_utf8_to_unichar ((char*)parser->holding);
-            break;
-          case 1:
-            parser->numbers[1] = ctx_utf8_to_unichar ((char*)parser->holding);
-            break;
-          case 2:
-            parser->numbers[2] = strtod ((char*)parser->holding, NULL);
-            {
-              CtxEntry e = {CTX_KERNING_PAIR, };
-              e.data.u16[0] = parser->numbers[0];
-              e.data.u16[1] = parser->numbers[1];
-              e.data.s32[1] = parser->numbers[2] * 256;
-              ctx_process (ctx, &e);
-            }
-            break;
-        }
-        parser->command = CTX_KERNING_PAIR;
-        parser->n_args ++; // make this more generic?
-        break;             
-      case CTX_TEXTURE:
-        if (parser->texture_done)
-        {
-        }
-        else
-        if (parser->n_numbers == 2)
-        {
-          const char *eid = (char*)parser->holding;
-          float x0 = arg(0);
-          float x1 = arg(1);
-          ctx_texture (ctx, eid, x0, x1);
-          parser->texture_done = 1;
-        }
-        parser->command = CTX_TEXTURE;
-        //parser->n_args++;
+      case CTX_CURSOR_RESIZE_E:
+      case CTX_CURSOR_RESIZE_W:
+        new_cursor = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZEWE);
         break;
-      case CTX_DEFINE_TEXTURE:
-        if (parser->texture_done)
-        {
-          if (parser->texture_done++ == 1)
-          {
-             const char *eid = (char*)parser->texture_id;
-             int width  = arg(0);
-             int height = arg(1);
-             CtxPixelFormat format = (CtxPixelFormat)arg(2);
-             int stride = ctx_pixel_format_get_stride (format, width);
-             int data_len = stride * height;
-             if (format == CTX_FORMAT_YUV420)
-                 data_len = height * width + 2*(height/2) * (width/2);
+      case CTX_CURSOR_RESIZE_NE:
+      case CTX_CURSOR_RESIZE_SW:
+        new_cursor = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZENESW);
+        break;
+      case CTX_CURSOR_RESIZE_NW:
+      case CTX_CURSOR_RESIZE_SE:
+        new_cursor = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZENWSE);
+        break;
+    }
+    if (new_cursor)
+    {
+      SDL_Cursor *old_cursor = SDL_GetCursor();
+      SDL_SetCursor (new_cursor);
+      SDL_ShowCursor (1);
+      if (old_cursor)
+        SDL_FreeCursor (old_cursor);
+    }
+    else
+    {
+      SDL_ShowCursor (0);
+    }
+  }
 
+  if (tiled->shown_frame == tiled->render_frame)
+  {
+    return;
+  }
 
-             if (parser->pos != data_len)
-             {
-             fprintf (stderr, "unexpected datasize for define texture %s %ix%i\n size:%i != expected:%i - 
start of data: %i %i %i %i\n", eid, width, height,
-                               parser->pos,
-                               stride * height,
-                               parser->holding[0],
-                               parser->holding[1],
-                               parser->holding[2],
-                               parser->holding[3]
-                               );
-             }
-             else
-             ctx_define_texture (ctx, eid, width, height, stride, format, parser->holding, NULL);
-          }
-        }
-        else
-        {
-        switch (parser->n_numbers)
-        {
-          case 0:
-             strncpy ((char*)parser->texture_id, (char*)parser->holding, sizeof(parser->texture_id));
-             parser->texture_id[sizeof(parser->texture_id)-1]=0;
-             break;
-          case 1:
-          case 2:
-             break;
-          case 3:
-             parser->texture_done = 1;
-             break;
-          default:
-             fprintf (stderr, "!!%i\n", parser->n_numbers);
-             break;
-        }
-        }
-        parser->command = CTX_DEFINE_TEXTURE;
-        break;
+  if (block)
+  {
+    int count = 0;
+    while (ctx_tiled_threads_done (tiled) != _ctx_max_threads)
+    {
+      usleep (50);
+      count ++;
+      if (count > 2000)
+      {
+        tiled->shown_frame = tiled->render_frame;
+        return;
+      }
+    }
+  }
+  else
+  {
+    if (ctx_tiled_threads_done (tiled) != _ctx_max_threads)
+      return;
+  }
 
+  if (tiled->min_row == 100)
+  {
+  }
+  else
+  {
+#if 1
+    int x = tiled->min_col * tiled->width/CTX_HASH_COLS;
+    int y = tiled->min_row * tiled->height/CTX_HASH_ROWS;
+    int x1 = (tiled->max_col+1) * tiled->width/CTX_HASH_COLS;
+    int y1 = (tiled->max_row+1) * tiled->height/CTX_HASH_ROWS;
+    int width = x1 - x;
+    int height = y1 - y;
+#endif
+    tiled->min_row = 100;
+    tiled->max_row = 0;
+    tiled->min_col = 100;
+    tiled->max_col = 0;
 
-      case CTX_DEFINE_GLYPH:
-        /* XXX : reuse n_args logic - to enforce order */
-        if (parser->n_numbers == 1)
-        {
-          CtxEntry e = {CTX_DEFINE_GLYPH, };
-          e.data.u32[0] = parser->color_space_slot;
-          e.data.u32[1] = arg(0) * 256;
-          ctx_process (ctx, &e);
-        }
-        else
-        {
-          int unichar = ctx_utf8_to_unichar ((char*)parser->holding);
-          parser->color_space_slot = (CtxColorSpace)unichar;
-        }
-        parser->command = CTX_DEFINE_GLYPH;
-        break;             
+    SDL_Rect r = {x, y, width, height};
+    SDL_UpdateTexture (sdl->texture, &r,
+                      //(void*)sdl->pixels,
+                      (void*)(tiled->pixels + y * tiled->width * 4 + x * 4),
+                      
+                      tiled->width * 4);
+    SDL_RenderClear (sdl->renderer);
+    SDL_RenderCopy (sdl->renderer, sdl->texture, NULL, NULL);
+    SDL_RenderPresent (sdl->renderer);
+  }
+  tiled->shown_frame = tiled->render_frame;
+}
 
-      case CTX_COLOR:
-        {
-          switch (parser->color_model)
-            {
-              case CTX_GRAY:
-              case CTX_GRAYA:
-              case CTX_RGB:
-              case CTX_RGBA:
-              case CTX_DRGB:
-              case CTX_DRGBA:
-                ctx_color_raw (ctx, parser->color_model, parser->numbers, parser->color_stroke);
-                break;
-#if CTX_ENABLE_CMYK
-              case CTX_CMYK:
-              case CTX_CMYKA:
-                ctx_color_raw (ctx, parser->color_model, parser->numbers, parser->color_stroke);
-                break;
-#else
-              /* when there is no cmyk support at all in rasterizer
-               * do a naive mapping to RGB on input.
-               */
-              case CTX_CMYK:
-              case CTX_CMYKA:
-              case CTX_DCMYKA:
-                {
-                  float rgba[4] = {1,1,1,1.0f};
+static const char *ctx_sdl_keysym_to_name (unsigned int sym, int *r_keycode)
+{
+  static char buf[16]="";
+  buf[ctx_unichar_to_utf8 (sym, (void*)buf)]=0;
+  int code = sym;
+  const char *name = &buf[0];
+   switch (sym)
+   {
+     case SDLK_RSHIFT: code = 16 ; break;
+     case SDLK_LSHIFT: code = 16 ; break;
+     case SDLK_LCTRL: code = 17 ; break;
+     case SDLK_RCTRL: code = 17 ; break;
+     case SDLK_LALT:  code = 18 ; break;
+     case SDLK_RALT:  code = 18 ; break;
+     case SDLK_CAPSLOCK: name = "capslock"; code = 20 ; break;
+     //case SDLK_NUMLOCK: name = "numlock"; code = 144 ; break;
+     //case SDLK_SCROLLLOCK: name = "scrollock"; code = 145 ; break;
 
-                  ctx_cmyk_to_rgb (arg(0), arg(1), arg(2), arg(3), &rgba[0], &rgba[1], &rgba[2]);
-                  if (parser->color_model == CTX_CMYKA)
-                    { rgba[3] = arg(4); }
-                  ctx_color_raw (ctx, CTX_RGBA, rgba, parser->color_stroke);
-                }
-                break;
-#endif
-              case CTX_LAB:
-              case CTX_LCH:
-              default:
-                break;
-            }
-        }
+     case SDLK_F1:     name = "F1"; code = 112; break;
+     case SDLK_F2:     name = "F2"; code = 113; break;
+     case SDLK_F3:     name = "F3"; code = 114; break;
+     case SDLK_F4:     name = "F4"; code = 115; break;
+     case SDLK_F5:     name = "F5"; code = 116; break;
+     case SDLK_F6:     name = "F6"; code = 117; break;
+     case SDLK_F7:     name = "F7"; code = 118; break;
+     case SDLK_F8:     name = "F8"; code = 119; break;
+     case SDLK_F9:     name = "F9"; code = 120; break;
+     case SDLK_F10:    name = "F10"; code = 121; break;
+     case SDLK_F11:    name = "F11"; code = 122; break;
+     case SDLK_F12:    name = "F12"; code = 123; break;
+     case SDLK_ESCAPE: name = "escape"; break;
+     case SDLK_DOWN:   name = "down"; code = 40; break;
+     case SDLK_LEFT:   name = "left"; code = 37; break;
+     case SDLK_UP:     name = "up"; code = 38;  break;
+     case SDLK_RIGHT:  name = "right"; code = 39; break;
+     case SDLK_BACKSPACE: name = "backspace"; break;
+     case SDLK_SPACE:  name = "space"; break;
+     case SDLK_TAB:    name = "tab"; break;
+     case SDLK_DELETE: name = "delete"; code = 46; break;
+     case SDLK_INSERT: name = "insert"; code = 45; break;
+     case SDLK_RETURN:
+       //if (key_repeat == 0) // return never should repeat
+       name = "return";   // on a DEC like terminal
+       break;
+     case SDLK_HOME:     name = "home"; code = 36; break;
+     case SDLK_END:      name = "end"; code = 35; break;
+     case SDLK_PAGEDOWN: name = "page-down"; code = 34; break;
+     case SDLK_PAGEUP:   name = "page-up"; code = 33; break;
+     case ',': code = 188; break;
+     case '.': code = 190; break;
+     case '/': code = 191; break;
+     case '`': code = 192; break;
+     case '[': code = 219; break;
+     case '\\': code = 220; break;
+     case ']':  code = 221; break;
+     case '\'': code = 222; break;
+     default:
+       ;
+   }
+   if (sym >= 'a' && sym <='z') code -= 32;
+   if (r_keycode)
+   {
+     *r_keycode = code;
+   }
+   return name;
+}
+
+int ctx_sdl_consume_events (Ctx *ctx)
+{
+  CtxTiled *tiled = (void*)ctx->renderer;
+  CtxSDL *sdl = (void*)ctx->renderer;
+  SDL_Event event;
+  int got_events = 0;
+
+  ctx_sdl_show_frame (sdl, 0);
+
+  while (SDL_PollEvent (&event))
+  {
+    got_events ++;
+    switch (event.type)
+    {
+      case SDL_MOUSEBUTTONDOWN:
+        SDL_CaptureMouse (1);
+        ctx_pointer_press (ctx, event.button.x, event.button.y, event.button.button, 0);
         break;
-      case CTX_LINE_DASH:
-        if (parser->n_numbers)
+      case SDL_MOUSEBUTTONUP:
+        SDL_CaptureMouse (0);
+        ctx_pointer_release (ctx, event.button.x, event.button.y, event.button.button, 0);
+        break;
+      case SDL_MOUSEMOTION:
+        //  XXX : look at mask and generate motion for each pressed
+        //        button
+        ctx_pointer_motion (ctx, event.motion.x, event.motion.y, 1, 0);
+        break;
+      case SDL_FINGERMOTION:
+        ctx_pointer_motion (ctx, event.tfinger.x * tiled->width, event.tfinger.y * tiled->height,
+            (event.tfinger.fingerId%10) + 4, 0);
+        break;
+      case SDL_FINGERDOWN:
         {
-          ctx_line_dash (ctx, parser->numbers, parser->n_numbers);
-        }
-        else
+        static int fdowns = 0;
+        fdowns ++;
+        if (fdowns > 1) // the very first finger down from SDL seems to be
+                        // mirrored as mouse events, later ones not - at
+                        // least under wayland
         {
-          ctx_line_dash (ctx, NULL, 0);
+          ctx_pointer_press (ctx, event.tfinger.x * tiled->width, event.tfinger.y * tiled->height, 
+          (event.tfinger.fingerId%10) + 4, 0);
+        }
         }
-        //append_dash_val (ctx, arg(0));
         break;
-      case CTX_ARC_TO:
-        ctx_arc_to (ctx, arg(0), arg(1), arg(2), arg(3), arg(4));
+      case SDL_FINGERUP:
+        ctx_pointer_release (ctx, event.tfinger.x * tiled->width, event.tfinger.y * tiled->height,
+          (event.tfinger.fingerId%10) + 4, 0);
         break;
-      case CTX_REL_ARC_TO:
-        ctx_rel_arc_to (ctx, arg(0), arg(1), arg(2), arg(3), arg(4) );
+#if 1
+      case SDL_TEXTINPUT:
+    //  if (!active)
+    //    break;
+        if (!sdl->lctrl && !sdl->rctrl && !sdl->lalt 
+           //&& ( (vt && vt_keyrepeat (vt) ) || (key_repeat==0) )
+           )
+          {
+            const char *name = event.text.text;
+            int keycode = 0;
+            if (!strcmp (name, " ") ) { name = "space"; }
+            if (name[0] && name[1] == 0)
+            {
+              keycode = name[0];
+              keycode = toupper (keycode);
+              switch (keycode)
+              {
+                case '.':  keycode = 190; break;
+                case ';':  keycode = 59; break;
+                case ',':  keycode = 188; break;
+                case '/':  keycode = 191; break;
+                case '\'': keycode = 222; break;
+                case '`':  keycode = 192; break;
+                case '[':  keycode = 219; break;
+                case ']':  keycode = 221; break;
+                case '\\': keycode = 220; break;
+              }
+            }
+            ctx_key_press (ctx, keycode, name, 0);
+            //got_event = 1;
+          }
         break;
-      case CTX_REL_SMOOTH_TO:
+#endif
+      case SDL_KEYDOWN:
         {
-          float cx = parser->pcx;
-          float cy = parser->pcy;
-          float ax = 2 * ctx_x (ctx) - cx;
-          float ay = 2 * ctx_y (ctx) - cy;
-          ctx_curve_to (ctx, ax, ay, arg(0) +  cx, arg(1) + cy,
-                        arg(2) + cx, arg(3) + cy);
-          parser->pcx = arg(0) + cx;
-          parser->pcy = arg(1) + cy;
+          char buf[32] = "";
+          const char *name = buf;
+          if (!event.key.repeat)
+          {
+            sdl->key_balance ++;
+            sdl->key_repeat = 0;
+          }
+          else
+          {
+            sdl->key_repeat ++;
+          }
+          switch (event.key.keysym.sym)
+          {
+            case SDLK_LSHIFT: sdl->lshift = 1; break;
+            case SDLK_RSHIFT: sdl->rshift = 1; break;
+            case SDLK_LCTRL:  sdl->lctrl = 1; break;
+            case SDLK_LALT:   sdl->lalt = 1; break;
+            case SDLK_RCTRL:  sdl->rctrl = 1; break;
+          }
+          if (sdl->lshift | sdl->rshift | sdl->lctrl | sdl->lalt | sdl->rctrl)
+          {
+            ctx->events.modifier_state ^= ~(CTX_MODIFIER_STATE_CONTROL|
+                                            CTX_MODIFIER_STATE_ALT|
+                                            CTX_MODIFIER_STATE_SHIFT);
+            if (sdl->lshift | sdl->rshift)
+              ctx->events.modifier_state |= CTX_MODIFIER_STATE_SHIFT;
+            if (sdl->lctrl | sdl->rctrl)
+              ctx->events.modifier_state |= CTX_MODIFIER_STATE_CONTROL;
+            if (sdl->lalt)
+              ctx->events.modifier_state |= CTX_MODIFIER_STATE_ALT;
+          }
+          int keycode;
+          name = ctx_sdl_keysym_to_name (event.key.keysym.sym, &keycode);
+          ctx_key_down (ctx, keycode, name, 0);
+
+          if (strlen (name)
+              &&(event.key.keysym.mod & (KMOD_CTRL) ||
+                 event.key.keysym.mod & (KMOD_ALT) ||
+                 strlen (name) >= 2))
+          {
+            if (event.key.keysym.mod & (KMOD_CTRL) )
+              {
+                static char buf[64] = "";
+                sprintf (buf, "control-%s", name);
+                name = buf;
+              }
+            if (event.key.keysym.mod & (KMOD_ALT) )
+              {
+                static char buf[128] = "";
+                sprintf (buf, "alt-%s", name);
+                name = buf;
+              }
+            if (event.key.keysym.mod & (KMOD_SHIFT) )
+              {
+                static char buf[196] = "";
+                sprintf (buf, "shift-%s", name);
+                name = buf;
+              }
+            if (strcmp (name, "space"))
+              {
+               ctx_key_press (ctx, keycode, name, 0);
+              }
+          }
+          else
+          {
+#if 0
+             ctx_key_press (ctx, 0, buf, 0);
+#endif
+          }
         }
         break;
-      case CTX_SMOOTH_TO:
+      case SDL_KEYUP:
         {
-          float ax = 2 * ctx_x (ctx) - parser->pcx;
-          float ay = 2 * ctx_y (ctx) - parser->pcy;
-          ctx_curve_to (ctx, ax, ay, arg(0), arg(1),
-                        arg(2), arg(3) );
-          parser->pcx = arg(0);
-          parser->pcx = arg(1);
+           sdl->key_balance --;
+           switch (event.key.keysym.sym)
+           {
+             case SDLK_LSHIFT: sdl->lshift = 0; break;
+             case SDLK_RSHIFT: sdl->rshift = 0; break;
+             case SDLK_LCTRL: sdl->lctrl = 0; break;
+             case SDLK_RCTRL: sdl->rctrl = 0; break;
+             case SDLK_LALT:  sdl->lalt  = 0; break;
+           }
+
+          {
+            ctx->events.modifier_state ^= ~(CTX_MODIFIER_STATE_CONTROL|
+                                            CTX_MODIFIER_STATE_ALT|
+                                            CTX_MODIFIER_STATE_SHIFT);
+            if (sdl->lshift | sdl->rshift)
+              ctx->events.modifier_state |= CTX_MODIFIER_STATE_SHIFT;
+            if (sdl->lctrl | sdl->rctrl)
+              ctx->events.modifier_state |= CTX_MODIFIER_STATE_CONTROL;
+            if (sdl->lalt)
+              ctx->events.modifier_state |= CTX_MODIFIER_STATE_ALT;
+          }
+
+           int keycode;
+           const char *name = ctx_sdl_keysym_to_name (event.key.keysym.sym, &keycode);
+           ctx_key_up (ctx, keycode, name, 0);
         }
         break;
-      case CTX_SMOOTHQ_TO:
-        ctx_quad_to (ctx, parser->pcx, parser->pcy, arg(0), arg(1) );
+      case SDL_QUIT:
+        ctx_quit (ctx);
         break;
-      case CTX_REL_SMOOTHQ_TO:
+      case SDL_WINDOWEVENT:
+        if (event.window.event == SDL_WINDOWEVENT_RESIZED)
         {
-          float cx = parser->pcx;
-          float cy = parser->pcy;
-          parser->pcx = 2 * ctx_x (ctx) - parser->pcx;
-          parser->pcy = 2 * ctx_y (ctx) - parser->pcy;
-          ctx_quad_to (ctx, parser->pcx, parser->pcy, arg(0) +  cx, arg(1) + cy);
-        }
-        break;
-      case CTX_VER_LINE_TO:
-        ctx_line_to (ctx, ctx_x (ctx), arg(0) );
-        parser->command = CTX_VER_LINE_TO;
-        parser->pcx = ctx_x (ctx);
-        parser->pcy = ctx_y (ctx);
-        break;
-      case CTX_HOR_LINE_TO:
-        ctx_line_to (ctx, arg(0), ctx_y (ctx) );
-        parser->command = CTX_HOR_LINE_TO;
-        parser->pcx = ctx_x (ctx);
-        parser->pcy = ctx_y (ctx);
-        break;
-      case CTX_REL_HOR_LINE_TO:
-        ctx_rel_line_to (ctx, arg(0), 0.0f);
-        parser->command = CTX_REL_HOR_LINE_TO;
-        parser->pcx = ctx_x (ctx);
-        parser->pcy = ctx_y (ctx);
-        break;
-      case CTX_REL_VER_LINE_TO:
-        ctx_rel_line_to (ctx, 0.0f, arg(0) );
-        parser->command = CTX_REL_VER_LINE_TO;
-        parser->pcx = ctx_x (ctx);
-        parser->pcy = ctx_y (ctx);
-        break;
-      case CTX_ARC:
-        ctx_arc (ctx, arg(0), arg(1), arg(2), arg(3), arg(4), arg(5) );
-        break;
-      case CTX_APPLY_TRANSFORM:
-        ctx_apply_transform (ctx, arg(0), arg(1), arg(2), arg(3), arg(4), arg(5) );
-        break;
-      case CTX_SOURCE_TRANSFORM:
-        ctx_source_transform (ctx, arg(0), arg(1), arg(2), arg(3), arg(4), arg(5) );
-        break;
-      case CTX_CURVE_TO:
-        ctx_curve_to (ctx, arg(0), arg(1), arg(2), arg(3), arg(4), arg(5) );
-        parser->pcx = arg(2);
-        parser->pcy = arg(3);
-        parser->command = CTX_CURVE_TO;
-        break;
-      case CTX_REL_CURVE_TO:
-        parser->pcx = arg(2) + ctx_x (ctx);
-        parser->pcy = arg(3) + ctx_y (ctx);
-        ctx_rel_curve_to (ctx, arg(0), arg(1), arg(2), arg(3), arg(4), arg(5) );
-        parser->command = CTX_REL_CURVE_TO;
-        break;
-      case CTX_LINE_TO:
-        ctx_line_to (ctx, arg(0), arg(1) );
-        parser->command = CTX_LINE_TO;
-        parser->pcx = arg(0);
-        parser->pcy = arg(1);
-        break;
-      case CTX_MOVE_TO:
-        ctx_move_to (ctx, arg(0), arg(1) );
-        parser->command = CTX_LINE_TO;
-        parser->pcx = arg(0);
-        parser->pcy = arg(1);
-        parser->left_margin = parser->pcx;
-        break;
-      case CTX_FONT_SIZE:
-        ctx_font_size (ctx, arg(0) );
-        break;
-      case CTX_MITER_LIMIT:
-        ctx_miter_limit (ctx, arg(0) );
-        break;
-      case CTX_SCALE:
-        ctx_scale (ctx, arg(0), arg(1) );
-        break;
-      case CTX_QUAD_TO:
-        parser->pcx = arg(0);
-        parser->pcy = arg(1);
-        ctx_quad_to (ctx, arg(0), arg(1), arg(2), arg(3) );
-        parser->command = CTX_QUAD_TO;
-        break;
-      case CTX_REL_QUAD_TO:
-        parser->pcx = arg(0) + ctx_x (ctx);
-        parser->pcy = arg(1) + ctx_y (ctx);
-        ctx_rel_quad_to (ctx, arg(0), arg(1), arg(2), arg(3) );
-        parser->command = CTX_REL_QUAD_TO;
-        break;
-      case CTX_CLIP:
-        ctx_clip (ctx);
-        break;
-      case CTX_TRANSLATE:
-        ctx_translate (ctx, arg(0), arg(1) );
-        break;
-      case CTX_ROTATE:
-        ctx_rotate (ctx, arg(0) );
-        break;
-      case CTX_FONT:
-        ctx_font (ctx, (char *) parser->holding);
-        break;
-
-      case CTX_STROKE_TEXT:
-      case CTX_TEXT:
-        if (parser->n_numbers == 1)
-          { ctx_rel_move_to (ctx, -parser->numbers[0], 0.0); }  //  XXX : scale by font(size)
-        else
-          {
-            for (char *c = (char *) parser->holding; c; )
-              {
-                char *next_nl = ctx_strchr (c, '\n');
-                if (next_nl)
-                  { *next_nl = 0; }
-                /* do our own layouting on a per-word basis?, to get justified
-                 * margins? then we'd want explict margins rather than the
-                 * implicit ones from move_to's .. making move_to work within
-                 * margins.
-                 */
-                if (cmd == CTX_STROKE_TEXT)
-                  { ctx_text_stroke (ctx, c); }
-                else
-                  { ctx_text (ctx, c); }
-                if (next_nl)
-                  {
-                    *next_nl = '\n'; // swap it newline back in
-                    ctx_move_to (ctx, parser->left_margin, ctx_y (ctx) +
-                                 ctx_get_font_size (ctx) );
-                    c = next_nl + 1;
-                    if (c[0] == 0)
-                      { c = NULL; }
-                  }
-                else
-                  {
-                    c = NULL;
-                  }
-              }
-          }
-        if (cmd == CTX_STROKE_TEXT)
-          { parser->command = CTX_STROKE_TEXT; }
-        else
-          { parser->command = CTX_TEXT; }
-        break;
-      case CTX_REL_LINE_TO:
-        ctx_rel_line_to (ctx, arg(0), arg(1) );
-        parser->pcx += arg(0);
-        parser->pcy += arg(1);
-        break;
-      case CTX_REL_MOVE_TO:
-        ctx_rel_move_to (ctx, arg(0), arg(1) );
-        parser->pcx += arg(0);
-        parser->pcy += arg(1);
-        parser->left_margin = ctx_x (ctx);
-        break;
-      case CTX_LINE_WIDTH:
-        ctx_line_width (ctx, arg(0));
-        break;
-      case CTX_LINE_DASH_OFFSET:
-        ctx_line_dash_offset (ctx, arg(0));
-        break;
-      case CTX_IMAGE_SMOOTHING:
-        ctx_image_smoothing (ctx, arg(0));
-        break;
-      case CTX_SHADOW_COLOR:
-        ctx_shadow_rgba (ctx, arg(0), arg(1), arg(2), arg(3));
-        break;
-      case CTX_SHADOW_BLUR:
-        ctx_shadow_blur (ctx, arg(0) );
-        break;
-      case CTX_SHADOW_OFFSET_X:
-        ctx_shadow_offset_x (ctx, arg(0) );
-        break;
-      case CTX_SHADOW_OFFSET_Y:
-        ctx_shadow_offset_y (ctx, arg(0) );
-        break;
-      case CTX_LINE_JOIN:
-        ctx_line_join (ctx, (CtxLineJoin) arg(0) );
-        break;
-      case CTX_LINE_CAP:
-        ctx_line_cap (ctx, (CtxLineCap) arg(0) );
-        break;
-      case CTX_COMPOSITING_MODE:
-        ctx_compositing_mode (ctx, (CtxCompositingMode) arg(0) );
-        break;
-      case CTX_BLEND_MODE:
-        {
-          int blend_mode = arg(0);
-          if (blend_mode == CTX_COLOR) blend_mode = CTX_BLEND_COLOR;
-          ctx_blend_mode (ctx, (CtxBlend)blend_mode);
-        }
-        break;
-      case CTX_FILL_RULE:
-        ctx_fill_rule (ctx, (CtxFillRule) arg(0) );
-        break;
-      case CTX_TEXT_ALIGN:
-        ctx_text_align (ctx, (CtxTextAlign) arg(0) );
-        break;
-      case CTX_TEXT_BASELINE:
-        ctx_text_baseline (ctx, (CtxTextBaseline) arg(0) );
-        break;
-      case CTX_TEXT_DIRECTION:
-        ctx_text_direction (ctx, (CtxTextDirection) arg(0) );
-        break;
-      case CTX_IDENTITY:
-        ctx_identity (ctx);
-        break;
-      case CTX_RECTANGLE:
-        ctx_rectangle (ctx, arg(0), arg(1), arg(2), arg(3) );
-        break;
-      case CTX_FILL_RECT:
-        ctx_rectangle (ctx, arg(0), arg(1), arg(2), arg(3) );
-        ctx_fill (ctx);
-        break;
-      case CTX_STROKE_RECT:
-        ctx_rectangle (ctx, arg(0), arg(1), arg(2), arg(3) );
-        ctx_stroke (ctx);
-        break;
-      case CTX_ROUND_RECTANGLE:
-        ctx_round_rectangle (ctx, arg(0), arg(1), arg(2), arg(3), arg(4));
-        break;
-      case CTX_VIEW_BOX:
-        ctx_view_box (ctx, arg(0), arg(1), arg(2), arg(3) );
-        break;
-      case CTX_LINEAR_GRADIENT:
-        ctx_linear_gradient (ctx, arg(0), arg(1), arg(2), arg(3) );
-        break;
-      case CTX_RADIAL_GRADIENT:
-        ctx_radial_gradient (ctx, arg(0), arg(1), arg(2), arg(3), arg(4), arg(5) );
-        break;
-      case CTX_GRADIENT_STOP:
-        {
-          float red, green, blue, alpha;
-          ctx_parser_get_color_rgba (parser, 1, &red, &green, &blue, &alpha);
-          ctx_gradient_add_stop (ctx, arg(0), red, green, blue, alpha);
-        }
-        break;
-      case CTX_GLOBAL_ALPHA:
-        ctx_global_alpha (ctx, arg(0) );
-        break;
-      case CTX_BEGIN_PATH:
-        ctx_begin_path (ctx);
-        break;
-      case CTX_GLYPH:
-        ctx_glyph (ctx, arg(0), 0);
-        break;
-      case CTX_CLOSE_PATH:
-        ctx_close_path (ctx);
-        break;
-      case CTX_EXIT:
-        if (parser->exit)
-          { parser->exit (parser->exit_data);
-            return;
-          }
-        break;
-      case CTX_FLUSH:
-        //ctx_flush (ctx);
-        break;
-      case CTX_RESET:
-        ctx_reset (ctx);
-        if (parser->translate_origin)
-        {
-          ctx_translate (ctx,
-                         (parser->cursor_x-1) * parser->cell_width * 1.0,
-                         (parser->cursor_y-1) * parser->cell_height * 1.0);
+          ctx_sdl_show_frame (sdl, 1);
+          int width = event.window.data1;
+          int height = event.window.data2;
+          SDL_DestroyTexture (sdl->texture);
+          sdl->texture = SDL_CreateTexture (sdl->renderer, SDL_PIXELFORMAT_ABGR8888,
+                          SDL_TEXTUREACCESS_STREAMING, width, height);
+          free (tiled->pixels);
+          tiled->pixels = calloc (4, width * height);
+
+          tiled->width  = width;
+          tiled->height = height;
+          ctx_set_size (tiled->ctx, width, height);
+          ctx_set_size (tiled->ctx_copy, width, height);
         }
         break;
     }
-#undef arg
-//  parser->n_numbers = 0;
+  }
+  return 1;
 }
-
-static inline void ctx_parser_holding_append (CtxParser *parser, int byte)
+#else
+void ctx_screenshot (Ctx *ctx, const char *path)
 {
-#if !CTX_PARSER_FIXED_TEMP
-  if (CTX_UNLIKELY(parser->hold_len < parser->pos + 1 + 1))
-  {
-    int new_len = parser->hold_len * 2;
-    if (new_len < 512) new_len = 512;
-    parser->holding = (uint8_t*)realloc (parser->holding, new_len);
-    parser->hold_len = new_len;
-  }
+}
 #endif
 
-  parser->holding[parser->pos++]=byte;
-#if CTX_PARSER_FIXED_TEMP
-  if (CTX_UNLIKELY(parser->pos > (int) sizeof (parser->holding)-2))
-    { parser->pos = sizeof (parser->holding)-2; }
-#endif
-  parser->holding[parser->pos]=0;
-}
+#if CTX_SDL
 
-static void ctx_parser_transform_percent (CtxParser *parser, CtxCode code, int arg_no, float *value)
+static void ctx_sdl_set_clipboard (CtxSDL *sdl, const char *text)
 {
-  int big   = parser->width;
-  int small = parser->height;
-  if (big < small)
-    {
-      small = parser->width;
-      big   = parser->height;
-    }
-  switch (code)
-    {
-      case CTX_RADIAL_GRADIENT:
-      case CTX_ARC:
-        switch (arg_no)
-          {
-            case 0:
-            case 3:
-              *value *= (parser->width/100.0);
-              break;
-            case 1:
-            case 4:
-              *value *= (parser->height/100.0);
-              break;
-            case 2:
-            case 5:
-              *value *= small/100.0;
-              break;
-          }
-        break;
-      case CTX_FONT_SIZE:
-      case CTX_MITER_LIMIT:
-      case CTX_LINE_WIDTH:
-      case CTX_LINE_DASH_OFFSET:
-        {
-          *value *= (small/100.0);
-        }
-        break;
-      case CTX_ARC_TO:
-      case CTX_REL_ARC_TO:
-        if (arg_no > 3)
-          {
-            *value *= (small/100.0);
-          }
-        else
-          {
-            if (arg_no % 2 == 0)
-              { *value  *= ( (parser->width) /100.0); }
-            else
-              { *value *= ( (parser->height) /100.0); }
-          }
-        break;
-      case CTX_ROUND_RECTANGLE:
-        if (arg_no == 4)
-        {
-          { *value *= ((parser->height)/100.0); }
-          return;
-        }
-        /* FALLTHROUGH */
-      default: // even means x coord
-        if (arg_no % 2 == 0)
-          { *value  *= ((parser->width)/100.0); }
-        else
-          { *value *= ((parser->height)/100.0); }
-        break;
-    }
+  if (text)
+    SDL_SetClipboardText (text);
 }
 
-static void ctx_parser_transform_percent_height (CtxParser *parser, CtxCode code, int arg_no, float *value)
+static char *ctx_sdl_get_clipboard (CtxSDL *sdl)
 {
-  *value *= (parser->height/100.0);
+  return SDL_GetClipboardText ();
 }
 
-static void ctx_parser_transform_percent_width (CtxParser *parser, CtxCode code, int arg_no, float *value)
+inline static void ctx_sdl_reset (CtxSDL *sdl)
 {
-  *value *= (parser->height/100.0);
+  ctx_sdl_show_frame (sdl, 1);
 }
 
-static void ctx_parser_transform_cell (CtxParser *parser, CtxCode code, int arg_no, float *value)
+inline static void ctx_sdl_flush (CtxSDL *sdl)
 {
-  float small = parser->cell_width;
-  if (small > parser->cell_height)
-    { small = parser->cell_height; }
-  switch (code)
-    {
-      case CTX_RADIAL_GRADIENT:
-      case CTX_ARC:
-        switch (arg_no)
-          {
-            case 0:
-            case 3:
-              *value *= parser->cell_width;
-              break;
-            case 1:
-            case 4:
-              *value *= parser->cell_height;
-              break;
-            case 2:
-            case 5:
-              *value *= small; // use height?
-              break;
-          }
-        break;
-      case CTX_MITER_LIMIT:
-      case CTX_FONT_SIZE:
-      case CTX_LINE_WIDTH:
-      case CTX_LINE_DASH_OFFSET:
-        {
-          *value *= parser->cell_height;
-        }
-        break;
-      case CTX_ARC_TO:
-      case CTX_REL_ARC_TO:
-        if (arg_no > 3)
-          {
-            *value *= small;
-          }
-        else
-          {
-            *value *= (arg_no%2==0) ?parser->cell_width:parser->cell_height;
-          }
-        break;
-      case CTX_RECTANGLE:
-        if (arg_no % 2 == 0)
-          { *value *= parser->cell_width; }
-        else
-          {
-            if (! (arg_no > 1) )
-              { (*value) -= 1.0f; }
-            *value *= parser->cell_height;
-          }
-        break;
-      default: // even means x coord odd means y coord
-        *value *= (arg_no%2==0) ?parser->cell_width:parser->cell_height;
-        break;
-    }
+  ctx_tiled_flush ((void*)sdl);
+  //CtxTiled *tiled = (void*)sdl;
 }
 
-// %h %v %m %M
-
-static void ctx_parser_number_done (CtxParser *parser)
+void ctx_sdl_free (CtxSDL *sdl)
 {
 
-}
-
-static void ctx_parser_word_done (CtxParser *parser)
-{
-  parser->holding[parser->pos]=0;
-  //int old_args = parser->expected_args;
-  int command = ctx_parser_resolve_command (parser, parser->holding);
-  if ((command >= 0 && command < 32)
-      || (command > 150) || (command < 0)
-      )  // special case low enum values
-    {                   // and enum values too high to be
-                        // commands - permitting passing words
-                        // for strings in some cases
-      parser->numbers[parser->n_numbers] = command;
+  if (sdl->texture)
+  SDL_DestroyTexture (sdl->texture);
+  if (sdl->renderer)
+  SDL_DestroyRenderer (sdl->renderer);
+  if (sdl->window)
+  SDL_DestroyWindow (sdl->window);
 
-      // trigger transition from number
-      parser->state = CTX_PARSER_NUMBER;
-      char c = ',';
-      ctx_parser_feed_bytes (parser, &c, 1);
-    }
-  else if (command > 0)
-    {
-#if 0
-      if (old_args == CTX_ARG_COLLECT_NUMBERS ||
-          old_args == CTX_ARG_STRING_OR_NUMBER)
-      {
-        int tmp1 = parser->command;
-        int tmp2 = parser->expected_args;
-        int tmp3 = parser->n_numbers;
- //     int tmp4 = parser->n_args;
-        ctx_parser_dispatch_command (parser);
-        parser->command = (CtxCode)tmp1;
-        parser->expected_args = tmp2;
-        parser->n_numbers = tmp3;
- //     parser->n_args = tmp4;
-      }
+  ctx_tiled_free ((CtxTiled*)sdl);
+#if CTX_BABL
+  babl_exit ();
 #endif
+}
 
-      parser->command = (CtxCode) command;
-      parser->n_numbers = 0;
-      parser->n_args = 0;
-      if (parser->expected_args == 0)
-        {
-          ctx_parser_dispatch_command (parser);
-        }
-    }
-  else
-    {
-      /* interpret char by char */
-      uint8_t buf[16]=" ";
-      for (int i = 0; parser->pos && parser->holding[i] > ' '; i++)
-        {
-          buf[0] = parser->holding[i];
-          parser->command = (CtxCode) ctx_parser_resolve_command (parser, buf);
-          parser->n_numbers = 0;
-          parser->n_args = 0;
-          if (parser->command > 0)
-            {
-              if (parser->expected_args == 0)
-                {
-                  ctx_parser_dispatch_command (parser);
-                }
-            }
-          else
-            {
-              ctx_log ("unhandled command '%c'\n", buf[0]);
-            }
-        }
-    }
+
+int ctx_renderer_is_sdl (Ctx *ctx)
+{
+  if (ctx->renderer &&
+      ctx->renderer->free == (void*)ctx_sdl_free)
+          return 1;
+  return 0;
 }
 
-static void ctx_parser_string_done (CtxParser *parser)
+void ctx_sdl_set_fullscreen (Ctx *ctx, int val)
 {
-  if (parser->expected_args == CTX_ARG_STRING_OR_NUMBER)
+  CtxSDL *sdl = (void*)ctx->renderer;
+
+  if (val)
   {
-          /*
-    if (parser->state != CTX_PARSER_NUMBER &&
-        parser->state != CTX_PARSER_NEGATIVE_NUMBER &&
-        parser->state != CTX_PARSER_STRING_A85 &&
-        parser->state != CTX_PARSER_STRING_APOS &&
-        parser->state != CTX_PARSER_STRING_QUOT
-        )
-        */
-    {
-    int tmp1 = parser->command;
-    int tmp2 = parser->expected_args;
-    int tmp3 = parser->n_numbers;
-    int tmp4 = parser->n_args;
-    ctx_parser_dispatch_command (parser);
-    parser->command = (CtxCode)tmp1;
-    parser->expected_args = tmp2;
-    parser->n_numbers = tmp3;
-    parser->n_args = tmp4;
-    }
+    SDL_SetWindowFullscreen (sdl->window, SDL_WINDOW_FULLSCREEN_DESKTOP);
   }
   else
   {
-    ctx_parser_dispatch_command (parser);
+    SDL_SetWindowFullscreen (sdl->window, 0);
   }
+  // XXX we're presuming success
+  sdl->fullscreen = val;
+}
+int ctx_sdl_get_fullscreen (Ctx *ctx)
+{
+  CtxSDL *sdl = (void*)ctx->renderer;
+  return sdl->fullscreen;
 }
 
-static inline void ctx_parser_feed_byte (CtxParser *parser, char byte)
+
+Ctx *ctx_new_sdl (int width, int height)
 {
-#if CTX_REPORT_COL_ROW
-    if (CTX_UNLIKELY(byte == '\n'))
-    {
-        parser->col=0;
-        parser->line++;
-    }
-    else
-    {
-        parser->col++;
-    }
+#if CTX_RASTERIZER
+
+  CtxSDL *sdl = (CtxSDL*)calloc (sizeof (CtxSDL), 1);
+  CtxTiled *tiled = (void*)sdl;
+
+  ctx_get_contents ("file:///tmp/ctx.icc", &sdl_icc, &sdl_icc_length);
+  if (width <= 0 || height <= 0)
+  {
+    width  = 1920;
+    height = 1080;
+  }
+  sdl->window = SDL_CreateWindow("ctx", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, width, height, 
SDL_WINDOW_SHOWN|SDL_WINDOW_RESIZABLE);
+  //sdl->renderer = SDL_CreateRenderer (sdl->window, -1, SDL_RENDERER_SOFTWARE);
+  sdl->renderer = SDL_CreateRenderer (sdl->window, -1, 0);
+  if (!sdl->renderer)
+  {
+     ctx_free (tiled->ctx);
+     free (sdl);
+     return NULL;
+  }
+#if CTX_BABL
+  babl_init ();
 #endif
+  sdl->fullscreen = 0;
 
-    if (CTX_LIKELY(parser->state == CTX_PARSER_STRING_YENC))
-    {
-        if (CTX_UNLIKELY((parser->prev_byte == '=') && (byte == 'y')))
-        {
-          parser->state = CTX_PARSER_NEUTRAL;
-                 //   fprintf (stderr, "got %i\n", parser->pos);
-          parser->pos = ctx_ydec ((char*)parser->holding, (char*)parser->holding, parser->pos) - 1;
-#if 0
-          if (parser->pos > 5)
-                    fprintf (stderr, "dec got %i %c %c %c %c\n", parser->pos,
-                                    parser->holding[0],
-                                    parser->holding[1],
-                                    parser->holding[2],
-                                    parser->holding[3]
-                                    );
-#endif
-          ctx_parser_string_done (parser);
-        }
-        else
-        {
-          ctx_parser_holding_append (parser, byte);
-        }
-        parser->prev_byte = byte;
-        return;
-    }
-    else if (CTX_LIKELY(parser->state == CTX_PARSER_STRING_A85))
-    {
-        /* since these are our largest bulk transfers, minimize
-         * overhead for this case. */
-        if (CTX_LIKELY(byte!='~')) 
-        {
-          ctx_parser_holding_append (parser, byte);
-        }
-        else
-        {
-          parser->state = CTX_PARSER_NEUTRAL;
-                 //   fprintf (stderr, "got %i\n", parser->pos);
-          parser->pos = ctx_a85dec ((char*)parser->holding, (char*)parser->holding, parser->pos);
-                 //   fprintf (stderr, "dec got %i\n", parser->pos);
-          ctx_parser_string_done (parser);
-        }
-        return;
-    }
-  switch (parser->state)
-    {
-      case CTX_PARSER_NEUTRAL:
-        switch (byte)
-          {
-            case  0: case  1: case  2: case  3:  case 4:  case 5:
-            case  6: case  7: case  8: case 11: case 12: case 14:
-            case 15: case 16: case 17: case 18: case 19: case 20:
-            case 21: case 22: case 23: case 24: case 25: case 26:
-            case 27: case 28: case 29: case 30: case 31:
-              break;
-            case ' ': case '\t': case '\r': case '\n':
-            case ';': case ',':
-            case '(': case ')':
-            case '{': case '}':
-            //case '=':
-              break;
-            case '#':
-              parser->state = CTX_PARSER_COMMENT;
-              break;
-            case '\'':
-              parser->state = CTX_PARSER_STRING_APOS;
-              parser->pos = 0;
-              parser->holding[0] = 0;
-              break;
-            case '=':
-              parser->state = CTX_PARSER_STRING_YENC;
-              parser->pos = 0;
-              parser->holding[0] = 0;
-              break;
-            case '~':
-              parser->state = CTX_PARSER_STRING_A85;
-              parser->pos = 0;
-              parser->holding[0] = 0;
-              break;
-            case '"':
-              parser->state = CTX_PARSER_STRING_QUOT;
-              parser->pos = 0;
-              parser->holding[0] = 0;
-              break;
-            case '-':
-              parser->state = CTX_PARSER_NEGATIVE_NUMBER;
-              parser->numbers[parser->n_numbers] = 0;
-              parser->decimal = 0;
-              break;
-            case '0': case '1': case '2': case '3': case '4':
-            case '5': case '6': case '7': case '8': case '9':
-              parser->state = CTX_PARSER_NUMBER;
-              parser->numbers[parser->n_numbers] = 0;
-              parser->numbers[parser->n_numbers] += (byte - '0');
-              parser->decimal = 0;
-              break;
-            case '.':
-              parser->state = CTX_PARSER_NUMBER;
-              parser->numbers[parser->n_numbers] = 0;
-              parser->decimal = 1;
-              break;
-            default:
-              parser->state = CTX_PARSER_WORD;
-              parser->pos = 0;
-              ctx_parser_holding_append (parser, byte);
-              break;
-          }
-        break;
-      case CTX_PARSER_NUMBER:
-      case CTX_PARSER_NEGATIVE_NUMBER:
-        {
-          switch (byte)
-            {
-              case 0: case 1: case 2: case 3: case 4: case 5:
-              case 6: case 7: case 8:
-              case 11: case 12: case 14: case 15: case 16:
-              case 17: case 18: case 19: case 20: case 21:
-              case 22: case 23: case 24: case 25: case 26:
-              case 27: case 28: case 29: case 30: case 31:
-                parser->state = CTX_PARSER_NEUTRAL;
-                break;
-              case ' ':
-              case '\t':
-              case '\r':
-              case '\n':
-              case ';':
-              case ',':
-              case '(':
-              case ')':
-              case '{':
-              case '}':
-              case '=':
-                if (parser->state == CTX_PARSER_NEGATIVE_NUMBER)
-                  { parser->numbers[parser->n_numbers] *= -1; }
-                parser->state = CTX_PARSER_NEUTRAL;
-                break;
-              case '#':
-                parser->state = CTX_PARSER_COMMENT;
-                break;
-              case '-':
-                if (parser->state == CTX_PARSER_NEGATIVE_NUMBER)
-                  { parser->numbers[parser->n_numbers] *= -1; }
-                parser->state = CTX_PARSER_NEGATIVE_NUMBER;
-                parser->numbers[parser->n_numbers+1] = 0;
-                parser->n_numbers ++;
-                parser->decimal = 0;
-                break;
-              case '.':
-                //if (parser->decimal) // TODO permit .13.32.43 to equivalent to .12 .32 .43
-                parser->decimal = 1;
-                break;
-              case '0': case '1': case '2': case '3': case '4':
-              case '5': case '6': case '7': case '8': case '9':
-                if (parser->decimal)
-                  {
-                    parser->decimal *= 10;
-                    parser->numbers[parser->n_numbers] += (byte - '0') / (1.0 * parser->decimal);
-                  }
-                else
-                  {
-                    parser->numbers[parser->n_numbers] *= 10;
-                    parser->numbers[parser->n_numbers] += (byte - '0');
-                  }
-                break;
-              case '@': // cells
-                if (parser->state == CTX_PARSER_NEGATIVE_NUMBER)
-                  { parser->numbers[parser->n_numbers] *= -1; }
-                {
-                float fval = parser->numbers[parser->n_numbers];
-                ctx_parser_transform_cell (parser, parser->command, parser->n_numbers, &fval);
-                parser->numbers[parser->n_numbers]= fval;
-                }
-                parser->state = CTX_PARSER_NEUTRAL;
-                break;
-              case '%': // percent of width/height
-                if (parser->state == CTX_PARSER_NEGATIVE_NUMBER)
-                  { parser->numbers[parser->n_numbers] *= -1; }
-                {
-                float fval = parser->numbers[parser->n_numbers];
-                ctx_parser_transform_percent (parser, parser->command, parser->n_numbers, &fval);
-                parser->numbers[parser->n_numbers]= fval;
-                }
-                parser->state = CTX_PARSER_NEUTRAL;
-                break;
-              case '^': // percent of height
-                if (parser->state == CTX_PARSER_NEGATIVE_NUMBER)
-                  { parser->numbers[parser->n_numbers] *= -1; }
-                {
-                float fval = parser->numbers[parser->n_numbers];
-                ctx_parser_transform_percent_height (parser, parser->command, parser->n_numbers, &fval);
-                parser->numbers[parser->n_numbers]= fval;
-                }
-                parser->state = CTX_PARSER_NEUTRAL;
-                break;
-              case '~': // percent of width
-                if (parser->state == CTX_PARSER_NEGATIVE_NUMBER)
-                  { parser->numbers[parser->n_numbers] *= -1; }
-                {
-                float fval = parser->numbers[parser->n_numbers];
-                ctx_parser_transform_percent_width (parser, parser->command, parser->n_numbers, &fval);
-                parser->numbers[parser->n_numbers]= fval;
-                }
-                parser->state = CTX_PARSER_NEUTRAL;
-                break;
-              default:
-                if (parser->state == CTX_PARSER_NEGATIVE_NUMBER)
-                  { parser->numbers[parser->n_numbers] *= -1; }
-                parser->state = CTX_PARSER_WORD;
-                parser->pos = 0;
-                ctx_parser_holding_append (parser, byte);
-                break;
-            }
-          if ( (parser->state != CTX_PARSER_NUMBER) &&
-               (parser->state != CTX_PARSER_NEGATIVE_NUMBER))
-            {
-              parser->n_numbers ++;
-              ctx_parser_number_done (parser);
+  ctx_sdl_events = 1;
+  sdl->texture = SDL_CreateTexture (sdl->renderer,
+        SDL_PIXELFORMAT_ABGR8888,
+        SDL_TEXTUREACCESS_STREAMING,
+        width, height);
 
-              if (parser->n_numbers == parser->expected_args ||
-                  parser->expected_args == CTX_ARG_COLLECT_NUMBERS ||
-                  parser->expected_args == CTX_ARG_STRING_OR_NUMBER)
-                {
-                  int tmp1 = parser->n_numbers;
-                  int tmp2 = parser->n_args;
-                  CtxCode tmp3 = parser->command;
-                  int tmp4 = parser->expected_args;
-                  ctx_parser_dispatch_command (parser);
-                  parser->command = tmp3;
-                  switch (parser->command)
-                  {
-                    case CTX_DEFINE_TEXTURE:
-                    case CTX_TEXTURE:
-                      parser->n_numbers = tmp1;
-                      parser->n_args = tmp2;
-                      break;
-                          default:
-                      parser->n_numbers = 0;
-                      parser->n_args = 0;
-                      break;
-                  }
-                  parser->expected_args = tmp4;
-                }
-              if (parser->n_numbers > CTX_PARSER_MAX_ARGS)
-                { parser->n_numbers = CTX_PARSER_MAX_ARGS;
-                }
-            }
-        }
-        break;
-      case CTX_PARSER_WORD:
-        switch (byte)
-          {
-            case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7:
-            case 8: case 11: case 12: case 14: case 15: case 16: case 17:
-            case 18: case 19: case 20: case 21: case 22: case 23: case 24:
-            case 25: case 26: case 27: case 28: case 29: case 30: case 31:
-            case ' ': case '\t': case '\r': case '\n':
-            case ';': case ',':
-            case '(': case ')': case '=': case '{': case '}':
-              parser->state = CTX_PARSER_NEUTRAL;
-              break;
-            case '#':
-              parser->state = CTX_PARSER_COMMENT;
-              break;
-            case '-':
-              parser->state = CTX_PARSER_NEGATIVE_NUMBER;
-              parser->numbers[parser->n_numbers] = 0;
-              parser->decimal = 0;
-              break;
-            case '0': case '1': case '2': case '3': case '4':
-            case '5': case '6': case '7': case '8': case '9':
-              parser->state = CTX_PARSER_NUMBER;
-              parser->numbers[parser->n_numbers] = 0;
-              parser->numbers[parser->n_numbers] += (byte - '0');
-              parser->decimal = 0;
-              break;
-            case '.':
-              parser->state = CTX_PARSER_NUMBER;
-              parser->numbers[parser->n_numbers] = 0;
-              parser->decimal = 1;
-              break;
-            default:
-              ctx_parser_holding_append (parser, byte);
-              break;
-          }
-        if (parser->state != CTX_PARSER_WORD)
-          {
-            ctx_parser_word_done (parser);
-          }
-        break;
-      case CTX_PARSER_STRING_A85:
-        if (CTX_LIKELY(byte!='~'))
-        {
-          ctx_parser_holding_append (parser, byte);
-        }
-        else
-        {
-          parser->state = CTX_PARSER_NEUTRAL;
-                 //   fprintf (stderr, "got %i\n", parser->pos);
-          parser->pos = ctx_a85dec ((char*)parser->holding, (char*)parser->holding, parser->pos);
-                 //   fprintf (stderr, "dec got %i\n", parser->pos);
-          ctx_parser_string_done (parser);
-        }
-        break;
-      case CTX_PARSER_STRING_APOS:
-        switch (byte)
-          {
-            case '\\': parser->state = CTX_PARSER_STRING_APOS_ESCAPED; break;
-            case '\'': parser->state = CTX_PARSER_NEUTRAL;
-              ctx_parser_string_done (parser);
-              break;
-            default:
-              ctx_parser_holding_append (parser, byte); break;
-          }
-        break;
-      case CTX_PARSER_STRING_APOS_ESCAPED:
-        switch (byte)
-          {
-            case '0': byte = '\0'; break;
-            case 'b': byte = '\b'; break;
-            case 'f': byte = '\f'; break;
-            case 'n': byte = '\n'; break;
-            case 'r': byte = '\r'; break;
-            case 't': byte = '\t'; break;
-            case 'v': byte = '\v'; break;
-            default: break;
-          }
-        ctx_parser_holding_append (parser, byte);
-        parser->state = CTX_PARSER_STRING_APOS;
-        break;
-      case CTX_PARSER_STRING_QUOT_ESCAPED:
-        switch (byte)
-          {
-            case '0': byte = '\0'; break;
-            case 'b': byte = '\b'; break;
-            case 'f': byte = '\f'; break;
-            case 'n': byte = '\n'; break;
-            case 'r': byte = '\r'; break;
-            case 't': byte = '\t'; break;
-            case 'v': byte = '\v'; break;
-            default: break;
-          }
-        ctx_parser_holding_append (parser, byte);
-        parser->state = CTX_PARSER_STRING_QUOT;
-        break;
-      case CTX_PARSER_STRING_QUOT:
-        switch (byte)
-          {
-            case '\\':
-              parser->state = CTX_PARSER_STRING_QUOT_ESCAPED;
-              break;
-            case '"':
-              parser->state = CTX_PARSER_NEUTRAL;
-              ctx_parser_string_done (parser);
-              break;
-            default:
-              ctx_parser_holding_append (parser, byte);
-              break;
-          }
-        break;
-      case CTX_PARSER_COMMENT:
-        switch (byte)
-          {
-            case '\r':
-            case '\n':
-              parser->state = CTX_PARSER_NEUTRAL;
-            default:
-              break;
-          }
-        break;
-    }
-}
+  SDL_StartTextInput ();
+  SDL_EnableScreenSaver ();
 
-void ctx_parser_feed_bytes (CtxParser *parser, const char *data, int count)
-{
-  for (int i = 0; i < count; i++)
-    ctx_parser_feed_byte (parser, data[i]);
-}
+  tiled->ctx      = ctx_new ();
+  tiled->ctx_copy = ctx_new ();
+  tiled->width    = width;
+  tiled->height   = height;
+  tiled->cols     = 80;
+  tiled->rows     = 20;
+  ctx_set_renderer (tiled->ctx, sdl);
+  ctx_set_renderer (tiled->ctx_copy, sdl);
+  ctx_set_texture_cache (tiled->ctx_copy, tiled->ctx);
 
-void ctx_parse (Ctx *ctx, const char *string)
-{
-  if (!string)
-    return;
-  CtxParser *parser = ctx_parser_new (ctx, ctx_width(ctx),
-                                           ctx_height(ctx),
-                                           ctx_get_font_size(ctx),
-                                           ctx_get_font_size(ctx),
-                                           0, 0, NULL, NULL, NULL, NULL, NULL);
-  ctx_parser_feed_bytes (parser, string, strlen (string));
-  ctx_parser_free (parser);
-}
+  tiled->pixels = (uint8_t*)malloc (width * height * 4);
 
-#endif
+  ctx_set_size (tiled->ctx,      width, height);
+  ctx_set_size (tiled->ctx_copy, width, height);
 
-static CtxFont ctx_fonts[CTX_MAX_FONTS];
-static int     ctx_font_count = 0;
+  tiled->flush = (void*)ctx_sdl_flush;
+  tiled->reset = (void*)ctx_sdl_reset;
+  tiled->free  = (void*)ctx_sdl_free;
+  tiled->set_clipboard = (void*)ctx_sdl_set_clipboard;
+  tiled->get_clipboard = (void*)ctx_sdl_get_clipboard;
 
-#if CTX_FONT_ENGINE_STB
-static float
-ctx_glyph_width_stb (CtxFont *font, Ctx *ctx, uint32_t unichar);
-static float
-ctx_glyph_kern_stb (CtxFont *font, Ctx *ctx, uint32_t unicharA, uint32_t unicharB);
-static int
-ctx_glyph_stb (CtxFont *font, Ctx *ctx, uint32_t unichar, int stroke);
+  for (int i = 0; i < _ctx_max_threads; i++)
+  {
+    tiled->host[i] = ctx_new_for_framebuffer (tiled->pixels,
+                     tiled->width/CTX_HASH_COLS, tiled->height/CTX_HASH_ROWS,
+                     tiled->width * 4, CTX_FORMAT_RGBA8);
+    ctx_set_texture_source (tiled->host[i], tiled->ctx);
+  }
 
-CtxFontEngine ctx_font_engine_stb =
-{
-#if CTX_FONTS_FROM_FILE
-  ctx_load_font_ttf_file,
-#endif
-  ctx_load_font_ttf,
-  ctx_glyph_stb,
-  ctx_glyph_width_stb,
-  ctx_glyph_kern_stb,
-};
+  mtx_init (&tiled->mtx, mtx_plain);
+  cnd_init (&tiled->cond);
 
-int
-ctx_load_font_ttf (const char *name, const void *ttf_contents, int length)
-{
-  if (ctx_font_count >= CTX_MAX_FONTS)
-    { return -1; }
-  ctx_fonts[ctx_font_count].type = 1;
-  ctx_fonts[ctx_font_count].name = (char *) malloc (strlen (name) + 1);
-  ctx_strcpy ( (char *) ctx_fonts[ctx_font_count].name, name);
-  if (!stbtt_InitFont (&ctx_fonts[ctx_font_count].stb.ttf_info, ttf_contents, 0) )
-    {
-      ctx_log ( "Font init failed\n");
-      return -1;
-    }
-  ctx_fonts[ctx_font_count].engine = &ctx_font_engine_stb;
-  ctx_font_count ++;
-  return ctx_font_count-1;
+#define start_thread(no)\
+  if(_ctx_max_threads>no){ \
+    static void *args[2]={(void*)no, };\
+    thrd_t tid;\
+    args[1]=sdl;\
+    thrd_create (&tid, (void*)ctx_tiled_render_fun, args);\
+  }
+  start_thread(0);
+  start_thread(1);
+  start_thread(2);
+  start_thread(3);
+  start_thread(4);
+  start_thread(5);
+  start_thread(6);
+  start_thread(7);
+  start_thread(8);
+  start_thread(9);
+  start_thread(10);
+  start_thread(11);
+  start_thread(12);
+  start_thread(13);
+  start_thread(14);
+  start_thread(15);
+#undef start_thread
+
+  ctx_flush (tiled->ctx);
+  return tiled->ctx;
+#else
+  return NULL;
+#endif
 }
+#else
 
-#if CTX_FONTS_FROM_FILE
-int
-ctx_load_font_ttf_file (const char *name, const char *path)
+int ctx_renderer_is_sdl (Ctx *ctx)
 {
-  uint8_t *contents = NULL;
-  long length = 0;
-  ctx_get_contents (path, &contents, &length);
-  if (!contents)
-    {
-      ctx_log ( "File load failed\n");
-      return -1;
-    }
-  return ctx_load_font_ttf (name, contents, length);
+  return 0;
 }
 #endif
 
-static int
-ctx_glyph_stb_find (CtxFont *font, uint32_t unichar)
-{
-  stbtt_fontinfo *ttf_info = &font->stb.ttf_info;
-  int index = font->stb.cache_index;
-  if (font->stb.cache_unichar == unichar)
-    {
-      return index;
-    }
-  font->stb.cache_unichar = 0;
-  index = font->stb.cache_index = stbtt_FindGlyphIndex (ttf_info, unichar);
-  font->stb.cache_unichar = unichar;
-  return index;
-}
+#if CTX_EVENTS
 
-static float
-ctx_glyph_width_stb (CtxFont *font, Ctx *ctx, uint32_t unichar)
-{
-  stbtt_fontinfo *ttf_info = &font->stb.ttf_info;
-  float font_size          = ctx->state.gstate.font_size;
-  float scale              = stbtt_ScaleForPixelHeight (ttf_info, font_size);
-  int advance, lsb;
-  int glyph = ctx_glyph_stb_find (font, unichar);
-  if (glyph==0)
-    { return 0.0f; }
-  stbtt_GetGlyphHMetrics (ttf_info, glyph, &advance, &lsb);
-  return (advance * scale);
-}
+#if !__COSMOPOLITAN__
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#endif
 
-static float
-ctx_glyph_kern_stb (CtxFont *font, Ctx *ctx, uint32_t unicharA, uint32_t unicharB)
+typedef struct CtxTermCell
 {
-  stbtt_fontinfo *ttf_info = &font->stb.ttf_info;
-  float font_size = ctx->state.gstate.font_size;
-  float scale = stbtt_ScaleForPixelHeight (ttf_info, font_size);
-  int glyphA = ctx_glyph_stb_find (font, unicharA);
-  int glyphB = ctx_glyph_stb_find (font, unicharB);
-  return stbtt_GetGlyphKernAdvance (ttf_info, glyphA, glyphB) * scale;
-}
+  char    utf8[5];
+  uint8_t fg[4];
+  uint8_t bg[4];
 
-static int
-ctx_glyph_stb (CtxFont *font, Ctx *ctx, uint32_t unichar, int stroke)
-{
-  stbtt_fontinfo *ttf_info = &font->stb.ttf_info;
-  int glyph = ctx_glyph_stb_find (font, unichar);
-  if (glyph==0)
-    { return -1; }
-  float font_size = ctx->state.gstate.font_size;
-  int   baseline = ctx->state.y;
-  float origin_x = ctx->state.x;
-  float origin_y = baseline;
-  float scale    = stbtt_ScaleForPixelHeight (ttf_info, font_size);;
-  stbtt_vertex *vertices = NULL;
-  ctx_begin_path (ctx);
-  int num_verts = stbtt_GetGlyphShape (ttf_info, glyph, &vertices);
-  for (int i = 0; i < num_verts; i++)
-    {
-      stbtt_vertex *vertex = &vertices[i];
-      switch (vertex->type)
-        {
-          case STBTT_vmove:
-            ctx_move_to (ctx,
-                         origin_x + vertex->x * scale, origin_y - vertex->y * scale);
-            break;
-          case STBTT_vline:
-            ctx_line_to (ctx,
-                         origin_x + vertex->x * scale, origin_y - vertex->y * scale);
-            break;
-          case STBTT_vcubic:
-            ctx_curve_to (ctx,
-                          origin_x + vertex->cx  * scale, origin_y - vertex->cy  * scale,
-                          origin_x + vertex->cx1 * scale, origin_y - vertex->cy1 * scale,
-                          origin_x + vertex->x   * scale, origin_y - vertex->y   * scale);
-            break;
-          case STBTT_vcurve:
-            ctx_quad_to (ctx,
-                         origin_x + vertex->cx  * scale, origin_y - vertex->cy  * scale,
-                         origin_x + vertex->x   * scale, origin_y - vertex->y   * scale);
-            break;
-        }
-    }
-  stbtt_FreeShape (ttf_info, vertices);
-  if (stroke)
-    {
-      ctx_stroke (ctx);
-    }
-  else
-    { ctx_fill (ctx); }
-  return 0;
-}
-#endif
+  char    prev_utf8[5];
+  uint8_t prev_fg[4];
+  uint8_t prev_bg[4];
+} CtxTermCell;
 
-#if CTX_FONT_ENGINE_CTX
+typedef struct CtxTermLine
+{
+  CtxTermCell *cells;
+  int maxcol;
+  int size;
+} CtxTermLine;
 
-/* XXX: todo remove this, and rely on a binary search instead
- */
-static int ctx_font_find_glyph_cached (CtxFont *font, uint32_t glyph)
+typedef enum
 {
-  for (int i = 0; i < font->ctx.glyphs; i++)
-    {
-      if (font->ctx.index[i * 2] == glyph)
-        { return font->ctx.index[i * 2 + 1]; }
-    }
-  return -1;
-}
+  CTX_TERM_ASCII,
+  CTX_TERM_ASCII_MONO,
+  CTX_TERM_SEXTANT,
+  CTX_TERM_BRAILLE_MONO,
+  CTX_TERM_BRAILLE,
+  CTX_TERM_QUARTER,
+} CtxTermMode;
 
-static int ctx_glyph_find_ctx (CtxFont *font, Ctx *ctx, uint32_t unichar)
+typedef struct _CtxTerm CtxTerm;
+struct _CtxTerm
 {
-  int ret = ctx_font_find_glyph_cached (font, unichar);
-  if (ret >= 0) return ret;
+   void (*render) (void *term, CtxCommand *command);
+   void (*reset)  (void *term);
+   void (*flush)  (void *term);
+   char *(*get_clipboard) (void *ctxctx);
+   void (*set_clipboard) (void *ctxctx, const char *text);
+   void (*free)   (void *term);
+   Ctx      *ctx;
+   int       width;
+   int       height;
+   int       cols;
+   int       rows;
+   int       was_down;
 
-  for (int i = 0; i < font->ctx.length; i++)
+   uint8_t  *pixels;
+
+   Ctx      *host;
+   CtxList  *lines;
+   CtxTermMode mode;
+};
+
+static int ctx_term_ch = 8;
+static int ctx_term_cw = 8;
+
+void ctx_term_set (CtxTerm *term,
+                      int col, int row, const char *utf8,
+                      uint8_t *fg, uint8_t *bg)
+{
+  if (col < 1 || row < 1 || col > term->cols  || row > term->rows) return;
+  while (ctx_list_length (term->lines) < row)
   {
-    CtxEntry *entry = (CtxEntry *) &font->ctx.data[i];
-    if (entry->code == CTX_DEFINE_GLYPH &&
-        entry->data.u32[0] == unichar)
-    {
-       return i;
-       // XXX this could be prone to insertion of valid header
-       // data in included bitmaps.. is that an issue?
-       //   
-    }
+    ctx_list_append (&term->lines, calloc (sizeof (CtxTermLine), 1));
   }
-  return -1;
+  CtxTermLine *line = ctx_list_nth_data (term->lines, row-1);
+  assert (line);
+  if (line->size < col)
+  {
+     int new_size = ((col + 128)/128)*128;
+     line->cells = realloc (line->cells, sizeof (CtxTermCell) * new_size);
+     memset (&line->cells[line->size], 0, sizeof (CtxTermCell) * (new_size - line->size) );
+     line->size = new_size;
+  }
+  if (col > line->maxcol) line->maxcol = col;
+  strncpy (line->cells[col-1].utf8, (char*)utf8, 4);
+  memcpy  (line->cells[col-1].fg, fg, 4);
+  memcpy  (line->cells[col-1].bg, bg, 4);
 }
 
+static int _ctx_term256 = 0; // XXX TODO implement autodetect for this
+static long _ctx_curfg = -1;
+static long _ctx_curbg = -1;
 
-static float
-ctx_glyph_kern_ctx (CtxFont *font, Ctx *ctx, uint32_t unicharA, uint32_t unicharB)
-{
-  float font_size = ctx->state.gstate.font_size;
-  int first_kern = ctx_glyph_find_ctx (font, ctx, unicharA);
-  if (first_kern < 0) return 0.0;
-  for (int i = first_kern + 1; i < font->ctx.length; i++)
-    {
-      CtxEntry *entry = (CtxEntry *) &font->ctx.data[i];
-      if (entry->code == CTX_KERNING_PAIR)
-        {
-          if (entry->data.u16[0] == unicharA && entry->data.u16[1] == unicharB)
-            { return entry->data.s32[1] / 255.0 * font_size / CTX_BAKE_FONT_SIZE; }
-        }
-      if (entry->code == CTX_DEFINE_GLYPH)
-        return 0.0;
-    }
-  return 0.0;
-}
-#if 0
-static int ctx_glyph_find (Ctx *ctx, CtxFont *font, uint32_t unichar)
+static long ctx_rgb_to_long (int r,int g, int b)
 {
-  for (int i = 0; i < font->ctx.length; i++)
-    {
-      CtxEntry *entry = (CtxEntry *) &font->ctx.data[i];
-      if (entry->code == CTX_DEFINE_GLYPH && entry->data.u32[0] == unichar)
-        { return i; }
-    }
-  return 0;
+  return r * 256 * 256 + g * 256 + b;
 }
-#endif
 
 
-static float
-ctx_glyph_width_ctx (CtxFont *font, Ctx *ctx, uint32_t unichar)
+static void ctx_term_set_fg (int red, int green, int blue)
 {
-  CtxState *state = &ctx->state;
-  float font_size = state->gstate.font_size;
-  int   start     = ctx_glyph_find_ctx (font, ctx, unichar);
-  if (start < 0)
-    { return 0.0; }  // XXX : fallback
-  for (int i = start; i < font->ctx.length; i++)
+  long lc = ctx_rgb_to_long (red, green, blue);
+  if (lc == _ctx_curfg)
+    return;
+  _ctx_curfg=lc;
+  if (_ctx_term256 == 0)
+  {
+    printf("\e[38;2;%i;%i;%im", red,green,blue);
+  }
+  else
+  {
+    int gray = (green /255.0) * 24 + 0.5;
+    int r    = (red/255.0)    * 6 + 0.5;
+    int g    = (green/255.0)  * 6 + 0.5;
+    int b    = (blue/255.0)   * 6 + 0.5;
+    if (gray > 23) gray = 23;
+
+    if (r > 5) r = 5;
+    if (g > 5) g = 5;
+    if (b > 5) b = 5;
+
+    if (((int)(r/1.66)== (int)(g/1.66)) && ((int)(g/1.66) == ((int)(b/1.66))))
     {
-      CtxEntry *entry = (CtxEntry *) &font->ctx.data[i];
-      if (entry->code == CTX_DEFINE_GLYPH)
-        if (entry->data.u32[0] == (unsigned) unichar)
-          { return (entry->data.u32[1] / 255.0 * font_size / CTX_BAKE_FONT_SIZE); }
+      printf("\e[38;5;%im", 16 + 216 + gray);
     }
-  return 0.0;
+    else
+      printf("\e[38;5;%im", 16 + r * 6 * 6 + g * 6  + b);
+  }
 }
 
-static int
-ctx_glyph_drawlist (CtxFont *font, Ctx *ctx, CtxDrawlist *drawlist, uint32_t unichar, int stroke)
+static void ctx_term_set_bg(int red, int green, int blue)
 {
-  CtxState *state = &ctx->state;
-  CtxIterator iterator;
-  float origin_x = state->x;
-  float origin_y = state->y;
-  ctx_current_point (ctx, &origin_x, &origin_y);
-  int in_glyph = 0;
-  float font_size = state->gstate.font_size;
-  int start = 0;
-  if (font->type == 0)
+  long lc = ctx_rgb_to_long (red, green, blue);
+//if (lc == _ctx_curbg)
+//  return;
+  _ctx_curbg=lc;
+  if (_ctx_term256 == 0)
   {
-  start = ctx_glyph_find_ctx (font, ctx, unichar);
-  if (start < 0)
-    { return -1; }  // XXX : fallback glyph
+    printf("\e[48;2;%i;%i;%im", red,green,blue);
   }
-  ctx_iterator_init (&iterator, drawlist, start, CTX_ITERATOR_EXPAND_BITPACK);
-  CtxCommand *command;
+  else
+  {
+    int gray = (green /255.0) * 24 + 0.5;
+    int r    = (red/255.0)    * 6 + 0.5;
+    int g    = (green/255.0)  * 6 + 0.5;
+    int b    = (blue/255.0)   * 6 + 0.5;
+    if (gray > 23) gray = 23;
 
-  /* XXX :  do a binary search instead of a linear search */
-  while ( (command= ctx_iterator_next (&iterator) ) )
+    if (r > 5) r = 5;
+    if (g > 5) g = 5;
+    if (b > 5) b = 5;
+
+    if (((int)(r/1.66)== (int)(g/1.66)) && ((int)(g/1.66) == ((int)(b/1.66))))
     {
-      CtxEntry *entry = &command->entry;
-      if (in_glyph)
-        {
-          if (entry->code == CTX_DEFINE_GLYPH)
-            {
-              if (stroke)
-                { ctx_stroke (ctx); }
-              else
-                {
-#if CTX_RASTERIZER
-#if CTX_ENABLE_SHADOW_BLUR
-      if (ctx->renderer && ((CtxRasterizer*)(ctx->renderer))->in_shadow)
-      {
-        ctx_rasterizer_shadow_fill ((CtxRasterizer*)ctx->renderer);
-        ((CtxRasterizer*)(ctx->renderer))->in_shadow = 1;
-      }
-      else
-#endif
-#endif
-         ctx_fill (ctx); 
-               
-                }
-              ctx_restore (ctx);
-              return 0;
-            }
-          ctx_process (ctx, entry);
-        }
-      else if (entry->code == CTX_DEFINE_GLYPH && entry->data.u32[0] == unichar)
-        {
-          in_glyph = 1;
-          ctx_save (ctx);
-          ctx_translate (ctx, origin_x, origin_y);
-          ctx_move_to (ctx, 0, 0);
-          ctx_begin_path (ctx);
-          ctx_scale (ctx, font_size / CTX_BAKE_FONT_SIZE,
-                     font_size / CTX_BAKE_FONT_SIZE);
-        }
-    }
-  if (stroke)
-    { ctx_stroke (ctx);
+      printf("\e[48;5;%im", 16 + 216 + gray);
     }
-  else
-    { 
-    
-#if CTX_RASTERIZER
-#if CTX_ENABLE_SHADOW_BLUR
-      if (ctx->renderer && ((CtxRasterizer*)(ctx->renderer))->in_shadow)
+    else
+      printf("\e[48;5;%im", 16 + r * 6 * 6 + g * 6  + b);
+  }
+}
+
+static int _ctx_term_force_full = 0;
+
+void ctx_term_scanout (CtxTerm *term)
+{
+  int row = 1;
+  printf ("\e[H");
+//  printf ("\e[?25l");
+  printf ("\e[0m");
+  for (CtxList *l = term->lines; l; l = l->next)
+  {
+    CtxTermLine *line = l->data;
+    for (int col = 1; col <= line->maxcol; col++)
+    {
+      CtxTermCell *cell = &line->cells[col-1];
+
+      if (strcmp(cell->utf8, cell->prev_utf8) ||
+          memcmp(cell->fg, cell->prev_fg, 3) ||
+          memcmp(cell->bg, cell->prev_bg, 3) || _ctx_term_force_full)
       {
-        ctx_rasterizer_shadow_fill ((CtxRasterizer*)ctx->renderer);
-        ((CtxRasterizer*)(ctx->renderer))->in_shadow = 1;
+        ctx_term_set_fg (cell->fg[0], cell->fg[1], cell->fg[2]);
+        ctx_term_set_bg (cell->bg[0], cell->bg[1], cell->bg[2]);
+        printf ("%s", cell->utf8);
       }
       else
-#endif
-#endif
       {
-         ctx_fill (ctx); 
+        // TODO: accumulate succesive such, and compress them
+        // into one
+        printf ("\e[C");
       }
+      strcpy (cell->prev_utf8, cell->utf8);
+      memcpy (cell->prev_fg, cell->fg, 3);
+      memcpy (cell->prev_bg, cell->bg, 3);
     }
-  ctx_restore (ctx);
-  return -1;
+    if (row != term->rows)
+      printf ("\n\r");
+    row ++;
+  }
+  printf ("\e[0m");
+  //printf ("\e[?25h");
+  //
 }
 
-static int
-ctx_glyph_ctx (CtxFont *font, Ctx *ctx, uint32_t unichar, int stroke)
-{
-  CtxDrawlist drawlist = { (CtxEntry *) font->ctx.data,
-                           font->ctx.length,
-                           font->ctx.length, 0, 0
-                         };
-  return ctx_glyph_drawlist (font, ctx, &drawlist, unichar, stroke);
-}
+// xx
+// xx
+// xx
+//
 
-#if 0
-uint32_t ctx_glyph_no (Ctx *ctx, int no)
+static inline int _ctx_rgba8_manhattan_diff (const uint8_t *a, const uint8_t *b)
 {
-  CtxFont *font = &ctx_fonts[ctx->state.gstate.font];
-  if (no < 0 || no >= font->ctx.glyphs)
-    { return 0; }
-  return font->ctx.index[no*2];
+  int c;
+  int diff = 0;
+  for (c = 0; c<3;c++)
+    diff += ctx_pow2(a[c]-b[c]);
+  return sqrtf(diff);
+  return diff;
 }
-#endif
 
-static void ctx_font_init_ctx (CtxFont *font)
+static void ctx_term_output_buf_half (uint8_t *pixels,
+                          int width,
+                          int height,
+                          CtxTerm *term)
 {
-  int glyph_count = 0;
-  for (int i = 0; i < font->ctx.length; i++)
-    {
-      CtxEntry *entry = &font->ctx.data[i];
-      if (entry->code == CTX_DEFINE_GLYPH)
-        { glyph_count ++; }
-    }
-  font->ctx.glyphs = glyph_count;
-#if CTX_DRAWLIST_STATIC
-  static uint32_t idx[512]; // one might have to adjust this for
-  // larger fonts XXX
-  // should probably be made a #define
-  font->ctx.index = &idx[0];
-#else
-  font->ctx.index = (uint32_t *) malloc (sizeof (uint32_t) * 2 * glyph_count);
-#endif
-  int no = 0;
-  for (int i = 0; i < font->ctx.length; i++)
+  int stride = width * 4;
+  const char *sextants[]={
+   " ","▘","▝","▀","▖","▌", "▞", "▛", "▗", "▚", "▐", "▜","▄","▙","▟","█",
+
+  };
+  for (int row = 0; row < height/2; row++)
     {
-      CtxEntry *entry = &font->ctx.data[i];
-      if (entry->code == CTX_DEFINE_GLYPH)
+      for (int col = 0; col < width-3; col++)
         {
-          font->ctx.index[no*2]   = entry->data.u32[0];
-          font->ctx.index[no*2+1] = i;
-          no++;
-        }
-    }
-}
+          int     unicode = 0;
+          int     bitno = 0;
+          uint8_t rgba[2][4] = {
+                             {255,255,255,0},
+                             {0,0,0,0}};
+          int i = 0;
 
-int
-ctx_load_font_ctx (const char *name, const void *data, int length);
-#if CTX_FONTS_FROM_FILE
-int
-ctx_load_font_ctx_file (const char *name, const char *path);
-#endif
+          int  rgbasum[2][4] = {0,};
+          int  sumcount[2];
 
-static CtxFontEngine ctx_font_engine_ctx =
-{
-#if CTX_FONTS_FROM_FILE
-  ctx_load_font_ctx_file,
-#endif
-  ctx_load_font_ctx,
-  ctx_glyph_ctx,
-  ctx_glyph_width_ctx,
-  ctx_glyph_kern_ctx,
-};
+          int curdiff = 0;
+          /* first find starting point colors */
+          for (int yi = 0; yi < ctx_term_ch; yi++)
+            for (int xi = 0; xi < ctx_term_cw; xi++, i++)
+                {
+                  int noi = (row * ctx_term_ch + yi) * stride + (col*ctx_term_cw+xi) * 4;
 
-int
-ctx_load_font_ctx (const char *name, const void *data, int length)
-{
-  if (length % sizeof (CtxEntry) )
-    { return -1; }
-  if (ctx_font_count >= CTX_MAX_FONTS)
-    { return -1; }
-  ctx_fonts[ctx_font_count].type = 0;
-  ctx_fonts[ctx_font_count].name = name;
-  ctx_fonts[ctx_font_count].ctx.data = (CtxEntry *) data;
-  ctx_fonts[ctx_font_count].ctx.length = length / sizeof (CtxEntry);
-  ctx_font_init_ctx (&ctx_fonts[ctx_font_count]);
-  ctx_fonts[ctx_font_count].engine = &ctx_font_engine_ctx;
-  ctx_font_count++;
-  return ctx_font_count-1;
-}
+                  if (rgba[0][3] == 0)
+                  {
+                    for (int c = 0; c < 3; c++)
+                      rgba[0][c] = pixels[noi + c];
+                    rgba[0][3] = 255; // used only as mark of in-use
+                  }
+                  else
+                  {
+                    int diff = _ctx_rgba8_manhattan_diff (&pixels[noi], rgba[0]);
+                    if (diff > curdiff)
+                    {
+                      curdiff = diff;
+                      for (int c = 0; c < 3; c++)
+                        rgba[1][c] = pixels[noi + c];
+                    }
+                  }
 
-#if CTX_FONTS_FROM_FILE
-int
-ctx_load_font_ctx_file (const char *name, const char *path)
-{
-  uint8_t *contents = NULL;
-  long length = 0;
-  ctx_get_contents (path, &contents, &length);
-  if (!contents)
-    {
-      ctx_log ( "File load failed\n");
-      return -1;
-    }
-  return ctx_load_font_ctx (name, contents, length);
-}
-#endif
-#endif
+                }
 
-#if CTX_FONT_ENGINE_CTX_FS
+          for (int iters = 0; iters < 1; iters++)
+          {
+                  i= 0;
+          for (int i = 0; i < 4; i ++)
+             rgbasum[0][i] = rgbasum[1][i]=0;
+          sumcount[0] = sumcount[1] = 0;
 
-static float
-ctx_glyph_kern_ctx_fs (CtxFont *font, Ctx *ctx, uint32_t unicharA, uint32_t unicharB)
-{
-#if 0
-  float font_size = ctx->state.gstate.font_size;
-  int first_kern = ctx_glyph_find_ctx (font, ctx, unicharA);
-  if (first_kern < 0) return 0.0;
-  for (int i = first_kern + 1; i < font->ctx.length; i++)
-    {
-      CtxEntry *entry = (CtxEntry *) &font->ctx.data[i];
-      if (entry->code == CTX_KERNING_PAIR)
-        {
-          if (entry->data.u16[0] == unicharA && entry->data.u16[1] == unicharB)
-            { return entry->data.s32[1] / 255.0 * font_size / CTX_BAKE_FONT_SIZE; }
-        }
-      if (entry->code == CTX_DEFINE_GLYPH)
-        return 0.0;
-    }
-#endif
-  return 0.0;
-}
+          for (int yi = 0; yi < ctx_term_ch; yi++)
+            for (int xi = 0; xi < ctx_term_cw; xi++, i++)
+                {
+                  int noi = (row * ctx_term_ch + yi) * stride + (col*ctx_term_cw+xi) * 4;
 
-static float
-ctx_glyph_width_ctx_fs (CtxFont *font, Ctx *ctx, uint32_t unichar)
-{
-  CtxState *state = &ctx->state;
-  char path[1024];
-  sprintf (path, "%s/%010p", font->ctx_fs.path, unichar);
-  uint8_t *data = NULL;
-  long int len_bytes = 0;
-  ctx_get_contents (path, &data, &len_bytes);
-  float ret = 0.0;
-  float font_size = state->gstate.font_size;
-  if (data){
-    Ctx *glyph_ctx = ctx_new ();
-    ctx_parse (glyph_ctx, data);
-    for (int i = 0; i < glyph_ctx->drawlist.count; i++)
-    {
-      CtxEntry *e = &glyph_ctx->drawlist.entries[i];
-      if (e->code == CTX_DEFINE_GLYPH)
-        ret = e->data.u32[1] / 255.0 * font_size / CTX_BAKE_FONT_SIZE;
-    }
-    free (data);
-    ctx_free (glyph_ctx);
-  }
-  return ret;
-}
+                  int diff1 = _ctx_rgba8_manhattan_diff (&pixels[noi], rgba[0]);
+                  int diff2 = _ctx_rgba8_manhattan_diff (&pixels[noi], rgba[1]);
+                  int cluster = 0;
+                  if (diff1 <= diff2)
+                    cluster = 0;
+                  else
+                    cluster = 1;
+                  sumcount[cluster]++;
+                  for (int c = 0; c < 3; c++)
+                    rgbasum[cluster][c] += pixels[noi+c];
+                }
 
-static int
-ctx_glyph_ctx_fs (CtxFont *font, Ctx *ctx, uint32_t unichar, int stroke)
-{
-  char path[1024];
-  sprintf (path, "file://%s/%010p", font->ctx_fs.path, unichar);
-  uint8_t *data = NULL;
-  long int len_bytes = 0;
-  ctx_get_contents (path, &data, &len_bytes);
 
-  if (data){
-    Ctx *glyph_ctx = ctx_new ();
-    ctx_parse (glyph_ctx, data);
-    int ret = ctx_glyph_drawlist (font, ctx, &(glyph_ctx->drawlist),
-                                  unichar, stroke);
-    free (data);
-    ctx_free (glyph_ctx);
-    return ret;
-  }
-  return -1;
-}
+          if (sumcount[0])
+          for (int c = 0; c < 3; c++)
+          {
+            rgba[0][c] = rgbasum[0][c] / sumcount[0];
+          }
+          if (sumcount[1])
+          for (int c = 0; c < 3; c++)
+          {
+            rgba[1][c] = rgbasum[1][c] / sumcount[1];
+          }
+          }
 
-int
-ctx_load_font_ctx_fs (const char *name, const void *data, int length);
+          int pixels_set = 0;
+          for (int y = 0; y < ctx_term_ch; y++)
+            for (int x = 0; x < ctx_term_cw; x++)
+              {
+                int no = (row * ctx_term_ch + y) * stride + (col*ctx_term_cw+x) * 4;
+#define CHECK_IS_SET \
+      (_ctx_rgba8_manhattan_diff (&pixels[no], rgba[0])< \
+       _ctx_rgba8_manhattan_diff (&pixels[no], rgba[1]))
 
-static CtxFontEngine ctx_font_engine_ctx_fs =
-{
-#if CTX_FONTS_FROM_FILE
-  NULL,
-#endif
-  ctx_load_font_ctx_fs,
-  ctx_glyph_ctx_fs,
-  ctx_glyph_width_ctx_fs,
-  ctx_glyph_kern_ctx_fs,
-};
+                int set = CHECK_IS_SET;
+#undef CHECK_IS_SET
+                if (set)
+                  { unicode |=  (1<< (bitno) ); 
+                    pixels_set ++; 
+                  }
+                bitno++;
+              }
+           if (pixels_set == 4)
+             ctx_term_set (term, col +1, row + 1, " ",
+                           rgba[1], rgba[0]);
+           else
+             ctx_term_set (term, col +1, row + 1, sextants[unicode],
+                           rgba[0], rgba[1]);
+        }
+    }
+}
 
-int
-ctx_load_font_ctx_fs (const char *name, const void *path, int length) // length is ignored
+void ctx_term_find_color_pair (CtxTerm *term, int x0, int y0, int w, int h,
+                uint8_t rgba[2][4])
+        //uint8_t *rgba0, uint8_t *rgba1)
 {
-  if (ctx_font_count >= CTX_MAX_FONTS)
-    { return -1; }
+int curdiff = 0;
+int stride = term->width * 4;
+uint8_t *pixels = term->pixels;
+/* first find starting point colors */
+for (int y = y0; y < y0 + h; y++)
+  for (int x = x0; x < x0 + w; x++)
+      {
+        int noi = (y) * stride + (x) * 4;
 
-  ctx_fonts[ctx_font_count].type = 42;
-  ctx_fonts[ctx_font_count].name = name;
-  ctx_fonts[ctx_font_count].ctx_fs.path = strdup (path);
-  int path_len = strlen (path);
-  if (ctx_fonts[ctx_font_count].ctx_fs.path[path_len-1] == '/')
-   ctx_fonts[ctx_font_count].ctx_fs.path[path_len-1] = 0;
-  ctx_fonts[ctx_font_count].engine = &ctx_font_engine_ctx_fs;
-  ctx_font_count++;
-  return ctx_font_count-1;
-}
+        if (rgba[0][3] == 0)
+        {
+          for (int c = 0; c < 3; c++)
+            rgba[0][c] = pixels[noi + c];
+          rgba[0][3] = 255; // used only as mark of in-use
+        }
+        else
+        {
+          int diff = _ctx_rgba8_manhattan_diff (&pixels[noi], &rgba[0][0]);
+          if (diff > curdiff)
+          {
+            curdiff = diff;
+            for (int c = 0; c < 3; c++)
+              rgba[1][c] = pixels[noi + c];
+          }
+        }
+      }
+          int  rgbasum[2][4] = {0,};
+          int  sumcount[2];
 
-#endif
+          for (int iters = 0; iters < 1; iters++)
+          {
+          for (int i = 0; i < 4; i ++)
+             rgbasum[0][i] = rgbasum[1][i]=0;
+          sumcount[0] = sumcount[1] = 0;
 
-int
-_ctx_glyph (Ctx *ctx, uint32_t unichar, int stroke)
-{
-  CtxFont *font = &ctx_fonts[ctx->state.gstate.font];
-  // a begin-path here did not remove stray spikes in terminal
-  return font->engine->glyph (font, ctx, unichar, stroke);
-}
+          for (int y = y0; y < y0 + h; y++)
+            for (int x = x0; x < x0 + w; x++)
+                {
+                  int noi = (y) * stride + (x) * 4;
 
-int
-ctx_glyph (Ctx *ctx, uint32_t unichar, int stroke)
-{
-#if CTX_BACKEND_TEXT
-  CtxEntry commands[3]; // 3 to silence incorrect warning from static analysis
-  ctx_memset (commands, 0, sizeof (commands) );
-  commands[0] = ctx_u32 (CTX_GLYPH, unichar, 0);
-  commands[0].data.u8[4] = stroke;
-  ctx_process (ctx, commands);
-  return 0; // XXX is return value used?
-#else
-  return _ctx_glyph (ctx, unichar, stroke);
-#endif
-}
+                  int diff1 = _ctx_rgba8_manhattan_diff (&pixels[noi], rgba[0]);
+                  int diff2 = _ctx_rgba8_manhattan_diff (&pixels[noi], rgba[1]);
+                  int cluster = 0;
+                  if (diff1 <= diff2)
+                    cluster = 0;
+                  else
+                    cluster = 1;
+                  sumcount[cluster]++;
+                  for (int c = 0; c < 3; c++)
+                    rgbasum[cluster][c] += pixels[noi+c];
+                }
 
-float
-ctx_glyph_width (Ctx *ctx, int unichar)
-{
-  CtxFont *font = &ctx_fonts[ctx->state.gstate.font];
 
-  return font->engine->glyph_width (font, ctx, unichar);
-}
+          if (sumcount[0])
+          for (int c = 0; c < 3; c++)
+          {
+            rgba[0][c] = rgbasum[0][c] / sumcount[0];
+          }
+          if (sumcount[1])
+          for (int c = 0; c < 3; c++)
+          {
+            rgba[1][c] = rgbasum[1][c] / sumcount[1];
+          }
+          }
 
-static float
-ctx_glyph_kern (Ctx *ctx, int unicharA, int unicharB)
-{
-  CtxFont *font = &ctx_fonts[ctx->state.gstate.font];
-  return font->engine->glyph_kern (font, ctx, unicharA, unicharB);
 }
 
-float
-ctx_text_width (Ctx        *ctx,
-                const char *string)
-{
-  float sum = 0.0;
-  if (!string)
-    return 0.0f;
-  for (const char *utf8 = string; *utf8; utf8 = ctx_utf8_skip (utf8, 1) )
-    {
-      sum += ctx_glyph_width (ctx, ctx_utf8_to_unichar (utf8) );
-    }
-  return sum;
-}
 
-static void
-_ctx_glyphs (Ctx     *ctx,
-             CtxGlyph *glyphs,
-             int       n_glyphs,
-             int       stroke)
-{
-  for (int i = 0; i < n_glyphs; i++)
-    {
-      {
-        uint32_t unichar = glyphs[i].index;
-        ctx_move_to (ctx, glyphs[i].x, glyphs[i].y);
-        ctx_glyph (ctx, unichar, stroke);
-      }
-    }
-}
 
-static void
-_ctx_text (Ctx        *ctx,
-           const char *string,
-           int         stroke,
-           int         visible)
+static void ctx_term_output_buf_quarter (uint8_t *pixels,
+                          int width,
+                          int height,
+                          CtxTerm *term)
 {
-  CtxState *state = &ctx->state;
-  float x = ctx->state.x;
-  switch ( (int) ctx_state_get (state, CTX_text_align) )
-    //switch (state->gstate.text_align)
-    {
-      case CTX_TEXT_ALIGN_START:
-      case CTX_TEXT_ALIGN_LEFT:
-        break;
-      case CTX_TEXT_ALIGN_CENTER:
-        x -= ctx_text_width (ctx, string) /2;
-        break;
-      case CTX_TEXT_ALIGN_END:
-      case CTX_TEXT_ALIGN_RIGHT:
-        x -= ctx_text_width (ctx, string);
-        break;
-    }
-  float y = ctx->state.y;
-  float baseline_offset = 0.0f;
-  switch ( (int) ctx_state_get (state, CTX_text_baseline) )
-    {
-      case CTX_TEXT_BASELINE_HANGING:
-        /* XXX : crude */
-        baseline_offset = ctx->state.gstate.font_size  * 0.55;
-        break;
-      case CTX_TEXT_BASELINE_TOP:
-        /* XXX : crude */
-        baseline_offset = ctx->state.gstate.font_size  * 0.7;
-        break;
-      case CTX_TEXT_BASELINE_BOTTOM:
-        baseline_offset = -ctx->state.gstate.font_size * 0.1;
-        break;
-      case CTX_TEXT_BASELINE_ALPHABETIC:
-      case CTX_TEXT_BASELINE_IDEOGRAPHIC:
-        baseline_offset = 0.0f;
-        break;
-      case CTX_TEXT_BASELINE_MIDDLE:
-        baseline_offset = ctx->state.gstate.font_size * 0.25;
-        break;
-    }
-  float x0 = x;
-  for (const char *utf8 = string; *utf8; utf8 = ctx_utf8_skip (utf8, 1) )
+  int stride = width * 4;
+  const char *sextants[]={
+   " ","▘","▝","▀","▖","▌", "▞", "▛", "▗", "▚", "▐", "▜","▄","▙","▟","█"
+
+  };
+  for (int row = 0; row < height/ctx_term_ch; row++)
     {
-      if (*utf8 == '\n')
-        {
-          y += ctx->state.gstate.font_size * ctx_state_get (state, CTX_line_spacing);
-          x = x0;
-          if (visible)
-            { ctx_move_to (ctx, x, y); }
-        }
-      else
+      for (int col = 0; col < width /ctx_term_cw; col++)
         {
-          uint32_t unichar = ctx_utf8_to_unichar (utf8);
-          if (visible)
-            {
-              ctx_move_to (ctx, x, y + baseline_offset);
-              _ctx_glyph (ctx, unichar, stroke);
-            }
-          const char *next_utf8 = ctx_utf8_skip (utf8, 1);
-          if (next_utf8)
-            {
-              x += ctx_glyph_width (ctx, unichar);
-              x += ctx_glyph_kern (ctx, unichar, ctx_utf8_to_unichar (next_utf8) );
-            }
-          if (visible)
-            { ctx_move_to (ctx, x, y); }
-        }
-    }
-  if (!visible)
-    { ctx_move_to (ctx, x, y); }
-}
+          int     unicode = 0;
+          int     bitno = 0;
+          uint8_t rgba[2][4] = {
+                             {255,255,255,0},
+                             {0,0,0,0}};
+          ctx_term_find_color_pair (term, col * ctx_term_cw,
+                                    row * ctx_term_ch,
+                                    ctx_term_cw,
+                                    ctx_term_ch, rgba);
 
+          int pixels_set = 0;
+          for (int y = 0; y < 2; y++)
+            for (int x = 0; x < ctx_term_cw; x++)
+              {
+                int no = (row * ctx_term_ch + y) * stride + (col*ctx_term_cw+x) * 4;
+#define CHECK_IS_SET \
+      (_ctx_rgba8_manhattan_diff (&pixels[no], rgba[0])< \
+       _ctx_rgba8_manhattan_diff (&pixels[no], rgba[1]))
 
-CtxGlyph *
-ctx_glyph_allocate (int n_glyphs)
-{
-  return (CtxGlyph *) malloc (sizeof (CtxGlyph) * n_glyphs);
-}
-void
-gtx_glyph_free     (CtxGlyph *glyphs)
-{
-  free (glyphs);
+                int set = CHECK_IS_SET;
+#undef CHECK_IS_SET
+                if (set)
+                  { unicode |=  (1<< (bitno) ); 
+                    pixels_set ++; 
+                  }
+                bitno++;
+              }
+           if (pixels_set == 4)
+             ctx_term_set (term, col +1, row + 1, " ",
+                           rgba[1], rgba[0]);
+           else
+             ctx_term_set (term, col +1, row + 1, sextants[unicode],
+                           rgba[0], rgba[1]);
+        }
+    }
 }
 
-void
-ctx_glyphs (Ctx        *ctx,
-            CtxGlyph   *glyphs,
-            int         n_glyphs)
-{
-  _ctx_glyphs (ctx, glyphs, n_glyphs, 0);
-}
 
-void
-ctx_glyphs_stroke (Ctx        *ctx,
-                   CtxGlyph   *glyphs,
-                   int         n_glyphs)
+static void ctx_term_output_buf_sextant (uint8_t *pixels,
+                          int width,
+                          int height,
+                          CtxTerm *term)
 {
-  _ctx_glyphs (ctx, glyphs, n_glyphs, 1);
-}
+  int stride = width * 4;
 
-void
-ctx_text (Ctx        *ctx,
-          const char *string)
-{
-  if (!string)
-    return;
-#if CTX_BACKEND_TEXT
-  ctx_process_cmd_str (ctx, CTX_TEXT, string, 0, 0);
-  _ctx_text (ctx, string, 0, 0);
-#else
-  _ctx_text (ctx, string, 0, 1);
-#endif
-}
+  const char *sextants[]={
+   " 
","🬀","🬁","🬂","🬃","🬄","🬅","🬆","🬇","🬈","🬉","🬊","🬋","🬌","🬍","🬎","🬏","🬐","🬑","🬒","🬓","▌","🬔","🬕","🬖","🬗","🬘","🬙","🬚","🬛","🬜","🬝","🬞","🬟","🬠","🬡","🬢","🬣","🬤","🬥","🬦","🬧","▐","🬨","🬩","🬪","🬫","🬬","🬭","🬮","🬯","🬰","🬱","🬲","🬳","🬴","🬵","🬶","🬷","🬸","🬹","🬺","🬻","█"
+  };
 
+  for (int row = 0; row < height/ctx_term_ch; row++)
+    {
+      for (int col = 0; col < width /ctx_term_cw; col++)
+        {
+          int     unicode = 0;
+          int     bitno = 0;
+          uint8_t rgba[2][4] = {
+                             {255,255,255,0},
+                             {0,0,0,0}};
 
-void
-ctx_fill_text (Ctx *ctx, const char *string,
-               float x, float y)
-{
-  ctx_move_to (ctx, x, y);
-  ctx_text (ctx, string);
-}
+          ctx_term_find_color_pair (term, col * ctx_term_cw,
+                                    row * ctx_term_ch,
+                                    ctx_term_cw,
+                                    ctx_term_ch, rgba);
 
-void
-ctx_text_stroke (Ctx        *ctx,
-                 const char *string)
-{
-  if (!string)
-    return;
-#if CTX_BACKEND_TEXT
-  ctx_process_cmd_str (ctx, CTX_STROKE_TEXT, string, 0, 0);
-  _ctx_text (ctx, string, 1, 0);
-#else
-  _ctx_text (ctx, string, 1, 1);
-#endif
-}
+          int pixels_set = 0;
+          for (int y = 0; y < ctx_term_ch; y++)
+            for (int x = 0; x < ctx_term_cw; x++)
+              {
+                int no = (row * ctx_term_ch + y) * stride + (col*ctx_term_cw+x) * 4;
+#define CHECK_IS_SET \
+      (_ctx_rgba8_manhattan_diff (&pixels[no], rgba[0])< \
+       _ctx_rgba8_manhattan_diff (&pixels[no], rgba[1]))
 
-void
-ctx_stroke_text (Ctx *ctx, const char *string,
-               float x, float y)
-{
-  ctx_move_to (ctx, x, y);
-  ctx_text_stroke (ctx, string);
-}
+                int set = CHECK_IS_SET;
+#undef CHECK_IS_SET
+                if (set)
+                  { unicode |=  (1<< (bitno) ); 
+                    pixels_set ++; 
+                  }
+                bitno++;
+              }
 
-static int _ctx_resolve_font (const char *name)
-{
-  for (int i = 0; i < ctx_font_count; i ++)
-    {
-      if (!ctx_strcmp (ctx_fonts[i].name, name) )
-        { return i; }
-    }
-  for (int i = 0; i < ctx_font_count; i ++)
-    {
-      if (ctx_strstr (ctx_fonts[i].name, name) )
-        { return i; }
+          if (pixels_set == 6)
+            ctx_term_set (term, col +1, row + 1, " ",
+                          rgba[1], rgba[0]);
+          else
+            ctx_term_set (term, col +1, row + 1, sextants[unicode], rgba[0], rgba[1]);
+        }
     }
-  return -1;
 }
 
-int ctx_resolve_font (const char *name)
+static void ctx_term_output_buf_ascii (uint8_t *pixels,
+                          int width,
+                          int height,
+                          CtxTerm *term,
+                          int mono)
 {
-  int ret = _ctx_resolve_font (name);
-  if (ret >= 0)
-    { return ret; }
-  if (!ctx_strcmp (name, "regular") )
+  /* this is a crude ascii-mode built on a quick mapping of sexels to ascii */
+  int stride = width * 4;
+  const char *sextants[]={
+   " ","`","'","^","🬃","`","~","\"","-","\"","'","\"","-","\"","~","^",",",";",
+   "=","/","i","[","p","P","z",")","/","7","f",">","/","F",",","\\",":",":",
+   "\\","\\","(","T","j","T","]","?","s","\\","<","q","_","=","=","=","c","L",
+   "Q","C","a","b","J","]","m","b","d","@"
+  };
+  uint8_t black[4] = {0,0,0,255};
+  for (int row = 0; row < height/ctx_term_ch; row++)
     {
-      int ret = _ctx_resolve_font ("sans");
-      if (ret >= 0) { return ret; }
-      ret = _ctx_resolve_font ("serif");
-      if (ret >= 0) { return ret; }
-    }
-  return 0;
-}
+      for (int col = 0; col < width /ctx_term_cw; col++)
+        {
+          int     unicode = 0;
+          int     bitno = 0;
+          uint8_t rgba[2][4] = {
+                             {255,255,255,0},
+                             {0,0,0,0}};
 
-static void ctx_font_setup (void)
-{
-  static int initialized = 0;
-  if (initialized) { return; }
-  initialized = 1;
-#if CTX_FONT_ENGINE_CTX
-  ctx_font_count = 0; // oddly - this is needed in arduino
+          ctx_term_find_color_pair (term, col * ctx_term_cw,
+                                    row * ctx_term_ch,
+                                    ctx_term_cw,
+                                    ctx_term_ch, rgba);
 
-#if CTX_FONT_ENGINE_CTX_FS
-  ctx_load_font_ctx_fs ("sans-ctx", "/tmp/ctx-regular", 0);
-#else
-#if CTX_FONT_ascii
-  ctx_load_font_ctx ("sans-ctx", ctx_font_ascii, sizeof (ctx_font_ascii) );
-#endif
-#if CTX_FONT_regular
-  ctx_load_font_ctx ("sans-ctx", ctx_font_regular, sizeof (ctx_font_regular) );
-#endif
-#endif
 
-#if CTX_FONT_mono
-  ctx_load_font_ctx ("mono-ctx", ctx_font_mono, sizeof (ctx_font_mono) );
-#endif
-#if CTX_FONT_bold
-  ctx_load_font_ctx ("bold-ctx", ctx_font_bold, sizeof (ctx_font_bold) );
-#endif
-#if CTX_FONT_italic
-  ctx_load_font_ctx ("italic-ctx", ctx_font_italic, sizeof (ctx_font_italic) );
-#endif
-#if CTX_FONT_sans
-  ctx_load_font_ctx ("sans-ctx", ctx_font_sans, sizeof (ctx_font_sans) );
-#endif
-#if CTX_FONT_serif
-  ctx_load_font_ctx ("serif-ctx", ctx_font_serif, sizeof (ctx_font_serif) );
-#endif
-#if CTX_FONT_symbol
-  ctx_load_font_ctx ("symbol-ctx", ctx_font_symbol, sizeof (ctx_font_symbol) );
-#endif
-#if CTX_FONT_emoji
-  ctx_load_font_ctx ("emoji-ctx", ctx_font_emoji, sizeof (ctx_font_emoji) );
-#endif
-#endif
+          if (_ctx_rgba8_manhattan_diff (black, rgba[1]) >
+              _ctx_rgba8_manhattan_diff (black, rgba[0]))
+          {
+            for (int c = 0; c < 4; c ++)
+            {
+              int tmp = rgba[0][c];
+              rgba[0][c] = rgba[1][c];
+              rgba[1][c] = tmp;
+            }
+          }
+          if (mono)
+          {
+            rgba[1][0] = 0;
+            rgba[1][1] = 0;
+            rgba[1][2] = 0;
+          }
 
-#if NOTO_EMOJI_REGULAR
-  ctx_load_font_ttf ("sans-NotoEmoji_Regular", ttf_NotoEmoji_Regular_ttf, ttf_NotoEmoji_Regular_ttf_len);
-#endif
-#if ROBOTO_LIGHT
-  ctx_load_font_ttf ("sans-light-Roboto_Light", ttf_Roboto_Light_ttf, ttf_Roboto_Light_ttf_len);
-#endif
-#if ROBOTO_REGULAR
-  ctx_load_font_ttf ("sans-Roboto_Regular", ttf_Roboto_Regular_ttf, ttf_Roboto_Regular_ttf_len);
-#endif
-#if ROBOTO_BOLD
-  ctx_load_font_ttf ("sans-bold-Roboto_Bold", ttf_Roboto_Bold_ttf, ttf_Roboto_Bold_ttf_len);
-#endif
-#if DEJAVU_SANS
-  ctx_load_font_ttf ("sans-DejaVuSans", ttf_DejaVuSans_ttf, ttf_DejaVuSans_ttf_len);
-#endif
-#if VERA
-  ctx_load_font_ttf ("sans-Vera", ttf_Vera_ttf, ttf_Vera_ttf_len);
-#endif
-#if UNSCII_16
-  ctx_load_font_ttf ("mono-unscii16", ttf_unscii_16_ttf, ttf_unscii_16_ttf_len);
-#endif
-#if XA000_MONO
-  ctx_load_font_ttf ("mono-0xA000", ttf_0xA000_Mono_ttf, ttf_0xA000_Mono_ttf_len);
-#endif
-#if DEJAVU_SANS_MONO
-  ctx_load_font_ttf ("mono-DejaVuSansMono", ttf_DejaVuSansMono_ttf, ttf_DejaVuSansMono_ttf_len);
-#endif
-#if NOTO_MONO_REGULAR
-  ctx_load_font_ttf ("mono-NotoMono_Regular", ttf_NotoMono_Regular_ttf, ttf_NotoMono_Regular_ttf_len);
-#endif
-}
 
+          int brightest_dark_diff = _ctx_rgba8_manhattan_diff (black, rgba[0]);
 
+          int pixels_set = 0;
+          for (int y = 0; y < ctx_term_ch; y++)
+            for (int x = 0; x < ctx_term_cw; x++)
+              {
+                int no = (row * ctx_term_ch + y) * stride + (col*ctx_term_cw+x) * 4;
+#define CHECK_IS_SET \
+      (_ctx_rgba8_manhattan_diff (&pixels[no], rgba[0])< \
+       _ctx_rgba8_manhattan_diff (&pixels[no], rgba[1]))
 
-#if !__COSMOPOLITAN__
-#include <stdarg.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#endif
+                int set = CHECK_IS_SET;
+#undef CHECK_IS_SET
+                if (set)
+                  { unicode |=  (1<< (bitno) ); 
+                    pixels_set ++; 
+                  }
+                bitno++;
+              }
 
-//#include "ctx.h"
-/* instead of including ctx.h we declare the few utf8
- * functions we use
- */
-uint32_t ctx_utf8_to_unichar (const char *input);
-int ctx_unichar_to_utf8 (uint32_t  ch, uint8_t  *dest);
-int ctx_utf8_strlen (const char *s);
 
-static void ctx_string_init (CtxString *string, int initial_size)
-{
-  string->allocated_length = initial_size;
-  string->length = 0;
-  string->utf8_length = 0;
-  string->str = (char*)malloc (string->allocated_length + 1);
-  string->str[0]='\0';
+           if (pixels_set == 6 && brightest_dark_diff < 40)
+             ctx_term_set (term, col +1, row + 1, " ",
+                           rgba[1], rgba[0]);
+           else
+             ctx_term_set (term, col +1, row + 1, sextants[unicode],
+                           rgba[0], rgba[1]);
+        }
+    }
 }
 
-static void ctx_string_destroy (CtxString *string)
+static void ctx_term_output_buf_braille (uint8_t *pixels,
+                          int width,
+                          int height,
+                          CtxTerm *term,
+                          int mono)
 {
-  if (string->str)
+  int reverse = 0;
+  int stride = width * 4;
+  uint8_t black[4] = {0,0,0,255};
+  for (int row = 0; row < height/ctx_term_ch; row++)
     {
-      free (string->str);
-      string->str = NULL;
-    }
-}
+      for (int col = 0; col < width /ctx_term_cw; col++)
+        {
+          int     unicode = 0;
+          int     bitno = 0;
+          uint8_t rgba[2][4] = {
+                             {255,255,255,0},
+                             {0,0,0,0}};
 
-void ctx_string_clear (CtxString *string)
-{
-  string->length = 0;
-  string->utf8_length = 0;
-  string->str[string->length]=0;
-}
+          ctx_term_find_color_pair (term, col * ctx_term_cw,
+                                    row * ctx_term_ch,
+                                    ctx_term_cw,
+                                    ctx_term_ch, rgba);
 
-static inline void _ctx_string_append_byte (CtxString *string, char  val)
-{
-  if (CTX_LIKELY((val & 0xC0) != 0x80))
-    { string->utf8_length++; }
-  if (CTX_UNLIKELY(string->length + 2 >= string->allocated_length))
-    {
-      char *old = string->str;
-      string->allocated_length = CTX_MAX (string->allocated_length * 2, string->length + 2);
-      string->str = (char*)realloc (old, string->allocated_length);
-    }
-  string->str[string->length++] = val;
-  string->str[string->length] = '\0';
-}
 
-void ctx_string_append_unichar (CtxString *string, unsigned int unichar)
-{
-  char *str;
-  char utf8[5];
-  utf8[ctx_unichar_to_utf8 (unichar, (unsigned char *) utf8)]=0;
-  str = utf8;
-  while (str && *str)
-    {
-      _ctx_string_append_byte (string, *str);
-      str++;
+          /* make darkest consistently be background  */
+          if (_ctx_rgba8_manhattan_diff (black, rgba[1]) >
+              _ctx_rgba8_manhattan_diff (black, rgba[0]))
+          {
+            for (int c = 0; c < 4; c ++)
+            {
+              int tmp = rgba[0][c];
+              rgba[0][c] = rgba[1][c];
+              rgba[1][c] = tmp;
+            }
+          }
+          if (mono)
+          {
+            rgba[1][0] = 0;
+            rgba[1][1] = 0;
+            rgba[1][2] = 0;
+          }
+
+          int pixels_set = 0;
+          for (int x = 0; x < 2; x++)
+            for (int y = 0; y < 3; y++)
+              {
+                int no = (row * 4 + y) * stride + (col*2+x) * 4;
+#define CHECK_IS_SET \
+      (_ctx_rgba8_manhattan_diff (&pixels[no], rgba[0])< \
+       _ctx_rgba8_manhattan_diff (&pixels[no], rgba[1]))
+
+                int set = CHECK_IS_SET;
+                if (reverse) { set = !set; }
+                if (set)
+                  { unicode |=  (1<< (bitno) ); 
+                    pixels_set ++; 
+                  }
+                bitno++;
+              }
+          {
+            int x = 0;
+            int y = 3;
+            int no = (row * 4 + y) * stride + (col*2+x) * 4;
+            int setA = CHECK_IS_SET;
+            no = (row * 4 + y) * stride + (col*2+x+1) * 4;
+            int setB = CHECK_IS_SET;
+
+            pixels_set += setA;
+            pixels_set += setB;
+#undef CHECK_IS_SET
+            if (reverse) { setA = !setA; }
+            if (reverse) { setB = !setB; }
+            if (setA != 0 && setB==0)
+              { unicode += 0x2840; }
+            else if (setA == 0 && setB)
+              { unicode += 0x2880; }
+            else if ( (setA != 0) && (setB != 0) )
+              { unicode += 0x28C0; }
+            else
+              { unicode += 0x2800; }
+            char utf8[5];
+            utf8[ctx_unichar_to_utf8 (unicode, (uint8_t*)utf8)]=0;
+
+#if 0
+            if (pixels_set == 8)
+            {
+              if (rgba[0][0] < 32 && rgba[0][1] < 32 && rgba[0][2] < 32)
+              {
+                ctx_term_set (term, col +1, row + 1, " ",
+                                 rgba[1], rgba[0]);
+                continue;
+              }
+            }
+#endif
+            {
+              ctx_term_set (term, col +1, row + 1, utf8,
+                               rgba[0], rgba[1]);
+            }
+          }
+        }
     }
 }
 
-static inline void _ctx_string_append_str (CtxString *string, const char *str)
+
+inline static void ctx_term_render (void *ctx,
+                                       CtxCommand *command)
 {
-  if (!str) { return; }
-  while (*str)
-    {
-      _ctx_string_append_byte (string, *str);
-      str++;
-    }
+  CtxTerm *term = (void*)ctx;
+  /* directly forward */
+  ctx_process (term->host, &command->entry);
 }
 
-void ctx_string_append_utf8char (CtxString *string, const char *str)
+inline static void ctx_term_flush (CtxTerm *term)
 {
-  if (!str) { return; }
-  int len = ctx_utf8_len (*str);
-  for (int i = 0; i < len && *str; i++)
+  int width =  term->width;
+  int height = term->height;
+  switch (term->mode)
+  {
+    case CTX_TERM_QUARTER:
+       ctx_term_output_buf_quarter (term->pixels,
+                                width, height, term);
+       break;
+    case CTX_TERM_ASCII:
+       ctx_term_output_buf_ascii (term->pixels,
+                                width, height, term, 0);
+       break;
+    case CTX_TERM_ASCII_MONO:
+       ctx_term_output_buf_ascii (term->pixels,
+                                width, height, term, 1);
+       break;
+    case CTX_TERM_SEXTANT:
+       ctx_term_output_buf_sextant (term->pixels,
+                                width, height, term);
+       break;
+    case CTX_TERM_BRAILLE:
+       ctx_term_output_buf_braille (term->pixels,
+                                width, height, term, 0);
+       break;
+    case CTX_TERM_BRAILLE_MONO:
+       ctx_term_output_buf_braille (term->pixels,
+                                width, height, term, 1);
+       break;
+  }
+#if CTX_BRAILLE_TEXT
+  CtxRasterizer *rasterizer = (CtxRasterizer*)(term->host->renderer);
+  // XXX instead sort and inject along with braille
+  //
+
+  //uint8_t rgba_bg[4]={0,0,0,0};
+  //uint8_t rgba_fg[4]={255,0,255,255};
+
+  for (CtxList *l = rasterizer->glyphs; l; l = l->next)
+  {
+    CtxTermGlyph *glyph = l->data;
+
+    uint8_t *pixels = term->pixels;
+    long rgb_sum[4]={0,0,0};
+    for (int v = 0; v <  ctx_term_ch; v ++)
+    for (int u = 0; u <  ctx_term_cw; u ++)
     {
-      _ctx_string_append_byte (string, *str);
-      str++;
+      int i = ((glyph->row-1) * ctx_term_ch + v) * rasterizer->blit_width + 
+              ((glyph->col-1) * ctx_term_cw + u);
+      for (int c = 0; c < 3; c ++)
+        rgb_sum[c] += pixels[i*4+c];
     }
-}
+    for (int c = 0; c < 3; c ++)
+      glyph->rgba_bg[c] = rgb_sum[c] / (ctx_term_ch * ctx_term_cw);
+    char utf8[8];
+    utf8[ctx_unichar_to_utf8(glyph->unichar, (uint8_t*)utf8)]=0;
+    ctx_term_set (term, glyph->col, glyph->row, 
+                     utf8, glyph->rgba_fg, glyph->rgba_bg);
+    free (glyph);
+  }
 
-void ctx_string_append_str (CtxString *string, const char *str)
-{
-  _ctx_string_append_str (string, str);
+  printf ("\e[H");
+  printf ("\e[0m");
+  ctx_term_scanout (term);
+  printf ("\e[0m");
+  fflush(NULL);
+  while (rasterizer->glyphs)
+    ctx_list_remove (&rasterizer->glyphs, rasterizer->glyphs->data);
+#endif
 }
 
-CtxString *ctx_string_new_with_size (const char *initial, int initial_size)
+void ctx_term_free (CtxTerm *term)
 {
-  CtxString *string = (CtxString*)ctx_calloc (sizeof (CtxString), 1);
-  ctx_string_init (string, initial_size);
-  if (initial)
-    { _ctx_string_append_str (string, initial); }
-  return string;
+  while (term->lines)
+  {
+    free (term->lines->data);
+    ctx_list_remove (&term->lines, term->lines->data);
+  }
+  printf ("\e[?25h"); // cursor on
+  nc_at_exit ();
+  free (term->pixels);
+  ctx_free (term->host);
+  free (term);
+  /* we're not destoring the ctx member, this is function is called in ctx' teardown */
 }
 
-CtxString *ctx_string_new (const char *initial)
+int ctx_renderer_is_term (Ctx *ctx)
 {
-  return ctx_string_new_with_size (initial, 8);
+  if (ctx->renderer &&
+      ctx->renderer->free == (void*)ctx_term_free)
+          return 1;
+  return 0;
 }
 
-void ctx_string_append_data (CtxString *string, const char *str, int len)
+float ctx_term_get_cell_width (Ctx *ctx)
 {
-  int i;
-  for (i = 0; i<len; i++)
-    { _ctx_string_append_byte (string, str[i]); }
+  return ctx_term_cw;
 }
 
-void ctx_string_append_string (CtxString *string, CtxString *string2)
+float ctx_term_get_cell_height (Ctx *ctx)
 {
-  const char *str = ctx_string_get (string2);
-  while (str && *str)
-    {
-      _ctx_string_append_byte (string, *str);
-      str++;
-    }
+  return ctx_term_ch;
 }
 
-const char *ctx_string_get (CtxString *string)
+Ctx *ctx_new_term (int width, int height)
 {
-  return string->str;
-}
+  Ctx *ctx = ctx_new ();
+#if CTX_RASTERIZER
+  CtxTerm *term = (CtxTerm*)calloc (sizeof (CtxTerm), 1);
+ 
+  const char *mode = getenv ("CTX_TERM_MODE");
+  ctx_term_cw = 2;
+  ctx_term_ch = 3;
 
-int ctx_string_get_utf8length (CtxString *string)
-{
-  return string->utf8_length;
-}
+  if (!mode) term->mode = CTX_TERM_SEXTANT;
+  else if (!strcmp (mode, "sextant")) term->mode = CTX_TERM_SEXTANT;
+  else if (!strcmp (mode, "ascii")) term->mode = CTX_TERM_ASCII_MONO;
+  //else if (!strcmp (mode, "ascii-mono")) term->mode = CTX_TERM_ASCII_MONO;
+  else if (!strcmp (mode, "quarter")) term->mode = CTX_TERM_QUARTER;
+  //else if (!strcmp (mode, "braille")){
+  //  term->mode = CTX_TERM_BRAILLE;
+  //  ctx_term_ch = 4;
+  //}
+  else if (!strcmp (mode, "braille")){
+    term->mode = CTX_TERM_BRAILLE_MONO;
+    ctx_term_ch = 4;
+  }
+  else {
+    fprintf (stderr, "recognized values for CTX_TERM_MODE:\n"
+                    " sextant ascii quarter braille\n");
+    exit (1);
+  }
 
-int ctx_string_get_length (CtxString *string)
-{
-  return string->length;
-}
+  mode = getenv ("CTX_TERM_FORCE_FULL");
+  if (mode && strcmp (mode, "0") && strcmp (mode, "no"))
+    _ctx_term_force_full = 1;
 
-void
-ctx_string_free (CtxString *string, int freealloc)
-{
-  if (freealloc)
-    {
-      ctx_string_destroy (string);
-    }
-#if 0
-  if (string->is_line)
+  fprintf (stdout, "\e[?1049h");
+  fprintf (stdout, "\e[?25l"); // cursor off
+
+  int maxwidth = ctx_terminal_cols  () * ctx_term_cw;
+  int maxheight = (ctx_terminal_rows ()) * ctx_term_ch;
+  if (width <= 0 || height <= 0)
   {
-    VtLine *line = (VtLine*)string;
-    if (line->style)
-      { free (line->style); }
-    if (line->ctx)
-      { ctx_free (line->ctx); }
-    if (line->ctx_copy)
-      { ctx_free (line->ctx_copy); }
+    width = maxwidth;
+    height = maxheight;
   }
+  if (width > maxwidth) width = maxwidth;
+  if (height > maxheight) height = maxheight;
+  term->ctx = ctx;
+  term->width  = width;
+  term->height = height;
+
+  term->cols = (width + 1) / ctx_term_cw;
+  term->rows = (height + 2) / ctx_term_ch;
+  term->lines = 0;
+  term->pixels = (uint8_t*)malloc (width * height * 4);
+  term->host = ctx_new_for_framebuffer (term->pixels,
+                                           width, height,
+                                           width * 4, CTX_FORMAT_RGBA8);
+#if CTX_BRAILLE_TEXT
+  ((CtxRasterizer*)term->host->renderer)->term_glyphs=1;
+#endif
+  _ctx_mouse (ctx, NC_MOUSE_DRAG);
+  ctx_set_renderer (ctx, term);
+  ctx_set_size (ctx, width, height);
+  ctx_font_size (ctx, ctx_term_ch); 
+  term->render = ctx_term_render;
+  term->flush = (void(*)(void*))ctx_term_flush;
+  term->free  = (void(*)(void*))ctx_term_free;
 #endif
-  free (string);
-}
 
-void
-ctx_string_set (CtxString *string, const char *new_string)
-{
-  ctx_string_clear (string);
-  _ctx_string_append_str (string, new_string);
-}
 
-static char *ctx_strdup (const char *str)
+  return ctx;
+}
+
+#endif
+
+#if CTX_EVENTS
+
+#if !__COSMOPOLITAN__
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#endif
+
+typedef struct _CtxTermImg CtxTermImg;
+struct _CtxTermImg
 {
-  int len = strlen (str);
-  char *ret = (char*)malloc (len + 1);
-  memcpy (ret, str, len);
-  ret[len]=0;
-  return ret;
+   void (*render)         (void *termimg, CtxCommand *command);
+   void (*reset)          (void *termimg);
+   void (*flush)          (void *termimg);
+   char *(*get_clipboard) (void *ctxctx);
+   void (*set_clipboard)  (void *ctxctx, const char *text);
+   void (*free)           (void *termimg);
+   Ctx      *ctx;
+   int       width;
+   int       height;
+   int       cols;
+   int       rows;
+   int       was_down;
+   // we need to have the above members in that order up to here
+   uint8_t  *pixels;
+   Ctx      *host;
+   CtxList  *lines;
+};
+
+inline static void ctx_termimg_render (void       *ctx,
+                                       CtxCommand *command)
+{
+  CtxTermImg *termimg = (void*)ctx;
+  /* directly forward */
+  ctx_process (termimg->host, &command->entry);
 }
 
-void ctx_string_replace_utf8 (CtxString *string, int pos, const char *new_glyph)
+inline static void ctx_termimg_flush (CtxTermImg *termimg)
 {
-#if 1
-  int old_len = string->utf8_length;
-#else
-  int old_len = ctx_utf8_strlen (string->str);// string->utf8_length;
-#endif
-  if (CTX_LIKELY(pos == old_len))
-    {
-      _ctx_string_append_str (string, new_glyph);
-      return;
-    }
+  int width =  termimg->width;
+  int height = termimg->height;
+  if (!termimg->pixels) return;
+  char *encoded = malloc (width * height * 3 * 3);
+  ctx_bin2base64 (termimg->pixels, width * height * 3,
+                  encoded);
+  int encoded_len = strlen (encoded);
 
-  char tmpg[3]=" ";
-  int new_len = ctx_utf8_len (*new_glyph);
-  if (new_len <= 1 && new_glyph[0] < 32)
-    {
-      new_len = 1;
-      tmpg[0]=new_glyph[0]+64;
-      new_glyph = tmpg;
-    }
+  int i = 0;
+
+  printf ("\e[H");
+  printf ("\e_Gf=24,s=%i,v=%i,t=d,a=T,m=1;\e\\", width, height);
+  while (i <  encoded_len)
   {
-    for (int i = old_len; i <= pos + 2; i++)
-      {
-        _ctx_string_append_byte (string, ' ');
-        old_len++;
-      }
+     if (i + 4096 <  encoded_len)
+     {
+       printf  ("\e_Gm=1;");
+     }
+     else
+     {
+       printf  ("\e_Gm=0;");
+     }
+     for (int n = 0; n < 4000 && i < encoded_len; n++)
+     {
+       printf ("%c", encoded[i]);
+       i++;
+     }
+     printf ("\e\\");
   }
-  if (string->length + new_len  >= string->allocated_length - 2)
-    {
-      char *tmp;
-      char *defer;
-      string->allocated_length = string->length + new_len + 2;
-      tmp = (char*) ctx_calloc (string->allocated_length + 1 + 8, 1);
-      strcpy (tmp, string->str);
-      defer = string->str;
-      string->str = tmp;
-      free (defer);
-    }
-  char *p = (char *) ctx_utf8_skip (string->str, pos);
-  int prev_len = ctx_utf8_len (*p);
-  char *rest;
-  if (*p == 0 || * (p+prev_len) == 0)
-    {
-      rest = ctx_strdup ("");
-    }
-  else
-    {
-      if (p + prev_len >= string->length  + string->str)
-        { rest = ctx_strdup (""); }
-      else
-        { rest = ctx_strdup (p + prev_len); }
-    }
-  memcpy (p, new_glyph, new_len);
-  memcpy (p + new_len, rest, strlen (rest) + 1);
-  string->length += new_len;
-  string->length -= prev_len;
-  free (rest);
-  //string->length = strlen (string->str);
-  //string->utf8_length = ctx_utf8_strlen (string->str);
+  free (encoded);
+  
+  fflush (NULL);
 }
 
-void ctx_string_replace_unichar (CtxString *string, int pos, uint32_t unichar)
+void ctx_termimg_free (CtxTermImg *termimg)
 {
-  uint8_t utf8[8];
-  ctx_unichar_to_utf8 (unichar, utf8);
-  ctx_string_replace_utf8 (string, pos, (char *) utf8);
+  while (termimg->lines)
+  {
+    free (termimg->lines->data);
+    ctx_list_remove (&termimg->lines, termimg->lines->data);
+  }
+  printf ("\e[?25h"); // cursor on
+  nc_at_exit ();
+  free (termimg->pixels);
+  ctx_free (termimg->host);
+  free (termimg);
+  /* we're not destoring the ctx member, this is function is called in ctx' teardown */
 }
 
-uint32_t ctx_string_get_unichar (CtxString *string, int pos)
+int ctx_renderer_is_termimg (Ctx *ctx)
 {
-  char *p = (char *) ctx_utf8_skip (string->str, pos);
-  if (!p)
-    { return 0; }
-  return ctx_utf8_to_unichar (p);
+  if (ctx->renderer &&
+      ctx->renderer->free == (void*)ctx_termimg_free)
+          return 1;
+  return 0;
 }
 
-
-void ctx_string_insert_utf8 (CtxString *string, int pos, const char *new_glyph)
+Ctx *ctx_new_termimg (int width, int height)
 {
-  int new_len = ctx_utf8_len (*new_glyph);
-  int old_len = string->utf8_length;
-  char tmpg[3]=" ";
-  if (old_len == pos && 0)
-    {
-      ctx_string_append_str (string, new_glyph);
-      return;
-    }
-  if (new_len <= 1 && new_glyph[0] < 32)
-    {
-      tmpg[0]=new_glyph[0]+64;
-      new_glyph = tmpg;
-    }
+  Ctx *ctx = ctx_new ();
+#if CTX_RASTERIZER
+  fprintf (stdout, "\e[?1049h");
+  fprintf (stdout, "\e[?25l"); // cursor off
+  CtxTermImg *termimg = (CtxTermImg*)calloc (sizeof (CtxTermImg), 1);
+
+
+  int maxwidth = ctx_terminal_width ();
+
+  int colwidth = maxwidth/ctx_terminal_cols ();
+  maxwidth-=colwidth;
+
+  int maxheight = ctx_terminal_height ();
+  if (width <= 0 || height <= 0)
   {
-    for (int i = old_len; i <= pos; i++)
-      {
-        _ctx_string_append_byte (string, ' ');
-        old_len++;
-      }
+    width  = maxwidth;
+    height = maxheight;
   }
-  if (string->length + new_len + 1  > string->allocated_length)
-    {
-      char *tmp;
-      char *defer;
-      string->allocated_length = string->length + new_len + 1;
-      tmp = (char*) ctx_calloc (string->allocated_length + 1, 1);
-      strcpy (tmp, string->str);
-      defer = string->str;
-      string->str = tmp;
-      free (defer);
-    }
-  char *p = (char *) ctx_utf8_skip (string->str, pos);
-  int prev_len = ctx_utf8_len (*p);
-  char *rest;
-  if ( (*p == 0 || * (p+prev_len) == 0) && pos != 0)
-    {
-      rest = ctx_strdup ("");
-    }
-  else
-    {
-      rest = ctx_strdup (p);
-    }
-  memcpy (p, new_glyph, new_len);
-  memcpy (p + new_len, rest, strlen (rest) + 1);
-  free (rest);
-  string->length = strlen (string->str);
-  string->utf8_length = ctx_utf8_strlen (string->str);
+  if (width > maxwidth) width = maxwidth;
+  if (height > maxheight) height = maxheight;
+  termimg->ctx = ctx;
+  termimg->width  = width;
+  termimg->height = height;
+  termimg->lines = 0;
+  termimg->pixels = (uint8_t*)malloc (width * height * 3);
+  termimg->host = ctx_new_for_framebuffer (termimg->pixels,
+                                           width, height,
+                                           width * 3, CTX_FORMAT_RGB8);
+  _ctx_mouse (ctx, NC_MOUSE_DRAG);
+  ctx_set_renderer (ctx, termimg);
+  ctx_set_size (ctx, width, height);
+  ctx_font_size (ctx, 14.0f);
+  termimg->render = ctx_termimg_render;
+  termimg->flush = (void(*)(void*))ctx_termimg_flush;
+  termimg->free  = (void(*)(void*))ctx_termimg_free;
+#endif
+
+  return ctx;
 }
 
-void ctx_string_insert_unichar (CtxString *string, int pos, uint32_t unichar)
+#endif
+
+#if CTX_FORMATTER
+
+typedef struct _CtxFormatter  CtxFormatter;
+struct _CtxFormatter 
 {
-  uint8_t utf8[5]="";
-  utf8[ctx_unichar_to_utf8(unichar, utf8)]=0;
-  ctx_string_insert_utf8 (string, pos, (char*)utf8);
+  void *target; // FILE
+  int   longform;
+  int   indent;
+
+  void (*add_str)(CtxFormatter *formatter, const char *str, int len);
+};
+
+static void ctx_formatter_addstr (CtxFormatter *formatter, const char *str, int len)
+{
+  formatter->add_str (formatter, str, len);
 }
 
-void ctx_string_remove (CtxString *string, int pos)
+static void ctx_formatter_addstrf (CtxFormatter *formatter, const char *format, ...)
 {
-  int old_len = string->utf8_length;
+   va_list ap;
+   size_t needed;
+   char *buffer;
+   va_start (ap, format);
+   needed = vsnprintf (NULL, 0, format, ap) + 1;
+   buffer = (char*) malloc (needed);
+   va_end (ap);
+   va_start (ap, format);
+   vsnprintf (buffer, needed, format, ap);
+   va_end (ap);
+   ctx_formatter_addstr (formatter, buffer, -1);
+   free (buffer);
+}
+
+static void _ctx_stream_addstr (CtxFormatter *formatter, const char *str, int len)
+{
+  if (!str || len == 0)
   {
-    for (int i = old_len; i <= pos; i++)
-      {
-        _ctx_string_append_byte (string, ' ');
-        old_len++;
-      }
+    return;
   }
-  char *p = (char *) ctx_utf8_skip (string->str, pos);
-  int prev_len = ctx_utf8_len (*p);
-  char *rest;
-  if (!p || *p == 0)
-    {
-      return;
-      rest = ctx_strdup ("");
-      prev_len = 0;
-    }
-  else if (* (p+prev_len) == 0)
+  if (len < 0) len = strlen (str);
+  fwrite (str, len, 1, (FILE*)formatter->target);
+}
+
+void _ctx_string_addstr (CtxFormatter *formatter, const char *str, int len)
+{
+  if (!str || len == 0)
   {
-      rest = ctx_strdup ("");
+    return;
   }
-  else
+  if (len < 0) len = strlen (str);
+  ctx_string_append_data ((CtxString*)(formatter->target), str, len);
+}
+
+
+static void _ctx_print_endcmd (CtxFormatter *formatter)
+{
+  if (formatter->longform)
     {
-      rest = ctx_strdup (p + prev_len);
+      ctx_formatter_addstr (formatter, ");\n", 3);
     }
-  strcpy (p, rest);
-  string->str[string->length - prev_len] = 0;
-  free (rest);
-  string->length = strlen (string->str);
-  string->utf8_length = ctx_utf8_strlen (string->str);
 }
 
-char *ctx_strdup_printf (const char *format, ...)
+static void _ctx_indent (CtxFormatter *formatter)
 {
-  va_list ap;
-  size_t needed;
-  char *buffer;
-  va_start (ap, format);
-  needed = vsnprintf (NULL, 0, format, ap) + 1;
-  buffer = (char*)malloc (needed);
-  va_end (ap);
-  va_start (ap, format);
-  vsnprintf (buffer, needed, format, ap);
-  va_end (ap);
-  return buffer;
+  for (int i = 0; i < formatter->indent; i++)
+    { ctx_formatter_addstr (formatter, "  ", 2);
+    }
 }
 
-void ctx_string_append_printf (CtxString *string, const char *format, ...)
+const char *_ctx_code_to_name (int code)
 {
-  va_list ap;
-  size_t needed;
-  char *buffer;
-  va_start (ap, format);
-  needed = vsnprintf (NULL, 0, format, ap) + 1;
-  buffer = (char*)malloc (needed);
-  va_end (ap);
-  va_start (ap, format);
-  vsnprintf (buffer, needed, format, ap);
-  va_end (ap);
-  ctx_string_append_str (string, buffer);
-  free (buffer);
-}
+      switch (code)
+        {
+          case CTX_REL_LINE_TO_X4:           return "relLinetoX4"; break;
+          case CTX_REL_LINE_TO_REL_CURVE_TO: return "relLineToRelCurveTo"; break;
+          case CTX_REL_CURVE_TO_REL_LINE_TO: return "relCurveToRelLineTo"; break;
+          case CTX_REL_CURVE_TO_REL_MOVE_TO: return "relCurveToRelMoveTo"; break;
+          case CTX_REL_LINE_TO_X2:           return "relLineToX2"; break;
+          case CTX_MOVE_TO_REL_LINE_TO:      return "moveToRelLineTo"; break;
+          case CTX_REL_LINE_TO_REL_MOVE_TO:  return "relLineToRelMoveTo"; break;
+          case CTX_FILL_MOVE_TO:             return "fillMoveTo"; break;
+          case CTX_REL_QUAD_TO_REL_QUAD_TO:  return "relQuadToRelQuadTo"; break;
+          case CTX_REL_QUAD_TO_S16:          return "relQuadToS16"; break;
 
-#if CTX_CAIRO
+          case CTX_SET_KEY:              return "setParam"; break;
+          case CTX_COLOR:                return "setColor"; break;
+          case CTX_DEFINE_GLYPH:         return "defineGlyph"; break;
+          case CTX_KERNING_PAIR:         return "kerningPair"; break;
+          case CTX_SET_PIXEL:            return "setPixel"; break;
+          case CTX_GLOBAL_ALPHA:         return "globalAlpha"; break;
+          case CTX_TEXT:                 return "text"; break;
+          case CTX_STROKE_TEXT:          return "strokeText"; break;
+          case CTX_SAVE:                 return "save"; break;
+          case CTX_RESTORE:              return "restore"; break;
+          case CTX_STROKE_SOURCE:        return "strokeSource"; break;
+          case CTX_NEW_PAGE:             return "newPage"; break;
+          case CTX_START_GROUP:          return "startGroup"; break;
+          case CTX_END_GROUP:            return "endGroup"; break;
+          case CTX_RECTANGLE:            return "rectangle"; break;
+          case CTX_ROUND_RECTANGLE:      return "roundRectangle"; break;
+          case CTX_LINEAR_GRADIENT:      return "linearGradient"; break;
+          case CTX_RADIAL_GRADIENT:      return "radialGradient"; break;
+          case CTX_GRADIENT_STOP:        return "gradientAddStop"; break;
+          case CTX_VIEW_BOX:             return "viewBox"; break;
+          case CTX_MOVE_TO:              return "moveTo"; break;
+          case CTX_LINE_TO:              return "lineTo"; break;
+          case CTX_BEGIN_PATH:           return "beginPath"; break;
+          case CTX_REL_MOVE_TO:          return "relMoveTo"; break;
+          case CTX_REL_LINE_TO:          return "relLineTo"; break;
+          case CTX_FILL:                 return "fill"; break;
+          case CTX_EXIT:                 return "exit"; break;
+          case CTX_APPLY_TRANSFORM:      return "transform"; break;
+          case CTX_REL_ARC_TO:           return "relArcTo"; break;
+          case CTX_GLYPH:                return "glyph"; break;
+          case CTX_TEXTURE:              return "texture"; break;
+          case CTX_DEFINE_TEXTURE:       return "defineTexture"; break;
+          case CTX_IDENTITY:             return "identity"; break;
+          case CTX_CLOSE_PATH:           return "closePath"; break;
+          case CTX_PRESERVE:             return "preserve"; break;
+          case CTX_FLUSH:                return "flush"; break;
+          case CTX_RESET:                return "reset"; break;
+          case CTX_FONT:                 return "font"; break;
+          case CTX_STROKE:               return "stroke"; break;
+          case CTX_CLIP:                 return "clip"; break;
+          case CTX_ARC:                  return "arc"; break;
+          case CTX_SCALE:                return "scale"; break;
+          case CTX_TRANSLATE:            return "translate"; break;
+          case CTX_ROTATE:               return "rotate"; break;
+          case CTX_ARC_TO:               return "arcTo"; break;
+          case CTX_CURVE_TO:             return "curveTo"; break;
+          case CTX_REL_CURVE_TO:         return "relCurveTo"; break;
+          case CTX_REL_QUAD_TO:          return "relQuadTo"; break;
+          case CTX_QUAD_TO:              return "quadTo"; break;
+          case CTX_SMOOTH_TO:            return "smoothTo"; break;
+          case CTX_REL_SMOOTH_TO:        return "relSmoothTo"; break;
+          case CTX_SMOOTHQ_TO:           return "smoothqTo"; break;
+          case CTX_REL_SMOOTHQ_TO:       return "relSmoothqTo"; break;
+          case CTX_HOR_LINE_TO:          return "horLineTo"; break;
+          case CTX_VER_LINE_TO:          return "verLineTo"; break;
+          case CTX_REL_HOR_LINE_TO:      return "relHorLineTo"; break;
+          case CTX_REL_VER_LINE_TO:      return "relVerLineTo"; break;
+          case CTX_COMPOSITING_MODE:     return "compositingMode"; break;
+          case CTX_BLEND_MODE:           return "blendMode"; break;
+          case CTX_TEXT_ALIGN:           return "textAlign"; break;
+          case CTX_TEXT_BASELINE:        return "textBaseline"; break;
+          case CTX_TEXT_DIRECTION:       return "textDirection"; break;
+          case CTX_FONT_SIZE:            return "fontSize"; break;
+          case CTX_MITER_LIMIT:          return "miterLimit"; break;
+          case CTX_LINE_JOIN:            return "lineJoin"; break;
+          case CTX_LINE_CAP:             return "lineCap"; break;
+          case CTX_LINE_WIDTH:           return "lineWidth"; break;
+          case CTX_LINE_DASH_OFFSET:     return "lineDashOffset"; break;
+          case CTX_IMAGE_SMOOTHING:      return "imageSmoothing"; break;
+          case CTX_SHADOW_BLUR:          return "shadowBlur";  break;
+          case CTX_FILL_RULE:            return "fillRule"; break;
+        }
+      return NULL;
+}
 
-typedef struct _CtxCairo CtxCairo;
-struct
-  _CtxCairo
+static void _ctx_print_name (CtxFormatter *formatter, int code)
 {
-  CtxImplementation vfuncs;
-  Ctx              *ctx;
-  cairo_t          *cr;
-  cairo_pattern_t  *pat;
-  cairo_surface_t  *image;
-  int               preserve;
-};
+#define CTX_VERBOSE_NAMES 1
+#if CTX_VERBOSE_NAMES
+  if (formatter->longform)
+    {
+      const char *name = NULL;
+      _ctx_indent (formatter);
+      //switch ((CtxCode)code)
+      name = _ctx_code_to_name (code);
+      if (name)
+        {
+          ctx_formatter_addstr (formatter, name, -1);
+          ctx_formatter_addstr (formatter, " (", 2);
+          if (code == CTX_SAVE)
+            { formatter->indent ++; }
+          else if (code == CTX_RESTORE)
+            { formatter->indent --; }
+          return;
+        }
+    }
+#endif
+  {
+    char name[3];
+    name[0]=CTX_SET_KEY;
+    name[2]='\0';
+    switch (code)
+      {
+        case CTX_GLOBAL_ALPHA:      name[1]='a'; break;
+        case CTX_COMPOSITING_MODE:  name[1]='m'; break;
+        case CTX_BLEND_MODE:        name[1]='B'; break;
+        case CTX_TEXT_ALIGN:        name[1]='t'; break;
+        case CTX_TEXT_BASELINE:     name[1]='b'; break;
+        case CTX_TEXT_DIRECTION:    name[1]='d'; break;
+        case CTX_FONT_SIZE:         name[1]='f'; break;
+        case CTX_MITER_LIMIT:       name[1]='l'; break;
+        case CTX_LINE_JOIN:         name[1]='j'; break;
+        case CTX_LINE_CAP:          name[1]='c'; break;
+        case CTX_LINE_WIDTH:        name[1]='w'; break;
+        case CTX_LINE_DASH_OFFSET:  name[1]='D'; break;
+        case CTX_IMAGE_SMOOTHING:   name[1]='S'; break;
+        case CTX_SHADOW_BLUR:       name[1]='s'; break;
+        case CTX_SHADOW_COLOR:      name[1]='C'; break;
+        case CTX_SHADOW_OFFSET_X:   name[1]='x'; break;
+        case CTX_SHADOW_OFFSET_Y:   name[1]='y'; break;
+        case CTX_FILL_RULE:         name[1]='r'; break;
+        default:
+          name[0] = code;
+          name[1] = 0;
+          break;
+      }
+    ctx_formatter_addstr (formatter, name, -1);
+    if (formatter->longform)
+      ctx_formatter_addstr (formatter, " (", 2);
+    else
+      ctx_formatter_addstr (formatter, " ", 1);
+  }
+}
 
 static void
-ctx_cairo_process (CtxCairo *ctx_cairo, CtxCommand *c)
+ctx_print_entry_enum (CtxFormatter *formatter, CtxEntry *entry, int args)
 {
-  CtxEntry *entry = (CtxEntry *) &c->entry;
-  cairo_t *cr = ctx_cairo->cr;
-  switch (entry->code)
+  _ctx_print_name (formatter, entry->code);
+  for (int i = 0; i <  args; i ++)
     {
-      case CTX_LINE_TO:
-        cairo_line_to (cr, c->line_to.x, c->line_to.y);
-        break;
-      case CTX_REL_LINE_TO:
-        cairo_rel_line_to (cr, c->rel_line_to.x, c->rel_line_to.y);
-        break;
-      case CTX_MOVE_TO:
-        cairo_move_to (cr, c->move_to.x, c->move_to.y);
-        break;
-      case CTX_REL_MOVE_TO:
-        cairo_rel_move_to (cr, ctx_arg_float (0), ctx_arg_float (1) );
-        break;
-      case CTX_CURVE_TO:
-        cairo_curve_to (cr, ctx_arg_float (0), ctx_arg_float (1),
-                        ctx_arg_float (2), ctx_arg_float (3),
-                        ctx_arg_float (4), ctx_arg_float (5) );
-        break;
-      case CTX_REL_CURVE_TO:
-        cairo_rel_curve_to (cr,ctx_arg_float (0), ctx_arg_float (1),
-                            ctx_arg_float (2), ctx_arg_float (3),
-                            ctx_arg_float (4), ctx_arg_float (5) );
-        break;
-      case CTX_PRESERVE:
-        ctx_cairo->preserve = 1;
-        break;
-      case CTX_QUAD_TO:
-        {
-          double x0, y0;
-          cairo_get_current_point (cr, &x0, &y0);
-          float cx = ctx_arg_float (0);
-          float cy = ctx_arg_float (1);
-          float  x = ctx_arg_float (2);
-          float  y = ctx_arg_float (3);
-          cairo_curve_to (cr,
-                          (cx * 2 + x0) / 3.0f, (cy * 2 + y0) / 3.0f,
-                          (cx * 2 + x) / 3.0f,           (cy * 2 + y) / 3.0f,
-                          x,                              y);
-        }
-        break;
-      case CTX_REL_QUAD_TO:
-        {
-          double x0, y0;
-          cairo_get_current_point (cr, &x0, &y0);
-          float cx = ctx_arg_float (0) + x0;
-          float cy = ctx_arg_float (1) + y0;
-          float  x = ctx_arg_float (2) + x0;
-          float  y = ctx_arg_float (3) + y0;
-          cairo_curve_to (cr,
-                          (cx * 2 + x0) / 3.0f, (cy * 2 + y0) / 3.0f,
-                          (cx * 2 + x) / 3.0f,           (cy * 2 + y) / 3.0f,
-                          x,                              y);
-        }
-        break;
-      /* rotate/scale/translate does not occur in fully minified data stream */
-      case CTX_ROTATE:
-        cairo_rotate (cr, ctx_arg_float (0) );
-        break;
-      case CTX_SCALE:
-        cairo_scale (cr, ctx_arg_float (0), ctx_arg_float (1) );
-        break;
-      case CTX_TRANSLATE:
-        cairo_translate (cr, ctx_arg_float (0), ctx_arg_float (1) );
-        break;
-      case CTX_LINE_WIDTH:
-        cairo_set_line_width (cr, ctx_arg_float (0) );
-        break;
-      case CTX_ARC:
-#if 0
-        fprintf (stderr, "F %2.1f %2.1f %2.1f %2.1f %2.1f %2.1f\n",
-                        ctx_arg_float(0),
-                        ctx_arg_float(1),
-                        ctx_arg_float(2),
-                        ctx_arg_float(3),
-                        ctx_arg_float(4),
-                        ctx_arg_float(5),
-                        ctx_arg_float(6));
-#endif
-        if (ctx_arg_float (5) == 1)
-          cairo_arc (cr, ctx_arg_float (0), ctx_arg_float (1),
-                     ctx_arg_float (2), ctx_arg_float (3),
-                     ctx_arg_float (4) );
-        else
-          cairo_arc_negative (cr, ctx_arg_float (0), ctx_arg_float (1),
-                              ctx_arg_float (2), ctx_arg_float (3),
-                              ctx_arg_float (4) );
-        break;
-      case CTX_SET_RGBA_U8:
-        cairo_set_source_rgba (cr, ctx_u8_to_float (ctx_arg_u8 (0) ),
-                               ctx_u8_to_float (ctx_arg_u8 (1) ),
-                               ctx_u8_to_float (ctx_arg_u8 (2) ),
-                               ctx_u8_to_float (ctx_arg_u8 (3) ) );
-        break;
-#if 0
-      case CTX_SET_RGBA_STROKE: // XXX : we need to maintain
-        //       state for the two kinds
-        cairo_set_source_rgba (cr, ctx_arg_u8 (0) /255.0,
-                               ctx_arg_u8 (1) /255.0,
-                               ctx_arg_u8 (2) /255.0,
-                               ctx_arg_u8 (3) /255.0);
-        break;
-#endif
-      case CTX_RECTANGLE:
-      case CTX_ROUND_RECTANGLE: // XXX - arcs
-        cairo_rectangle (cr, c->rectangle.x, c->rectangle.y,
-                         c->rectangle.width, c->rectangle.height);
-        break;
-      case CTX_SET_PIXEL:
-        cairo_set_source_rgba (cr, ctx_u8_to_float (ctx_arg_u8 (0) ),
-                               ctx_u8_to_float (ctx_arg_u8 (1) ),
-                               ctx_u8_to_float (ctx_arg_u8 (2) ),
-                               ctx_u8_to_float (ctx_arg_u8 (3) ) );
-        cairo_rectangle (cr, ctx_arg_u16 (2), ctx_arg_u16 (3), 1, 1);
-        cairo_fill (cr);
-        break;
-      case CTX_FILL:
-        if (ctx_cairo->preserve)
-        {
-          cairo_fill_preserve (cr);
-          ctx_cairo->preserve = 0;
-        }
-        else
-        {
-          cairo_fill (cr);
-        }
-        break;
-      case CTX_STROKE:
-        if (ctx_cairo->preserve)
-        {
-          cairo_stroke_preserve (cr);
-          ctx_cairo->preserve = 0;
-        }
-        else
-        {
-          cairo_stroke (cr);
-        }
-        break;
-      case CTX_IDENTITY:
-        cairo_identity_matrix (cr);
-        break;
-      case CTX_CLIP:
-        if (ctx_cairo->preserve)
-        {
-          cairo_clip_preserve (cr);
-          ctx_cairo->preserve = 0;
-        }
-        else
-        {
-          cairo_clip (cr);
+      int val = ctx_arg_u8 (i);
+      if (i>0)
+        { 
+          ctx_formatter_addstr (formatter, " ", 1);
         }
-        break;
-        break;
-      case CTX_BEGIN_PATH:
-        cairo_new_path (cr);
-        break;
-      case CTX_CLOSE_PATH:
-        cairo_close_path (cr);
-        break;
-      case CTX_SAVE:
-        cairo_save (cr);
-        break;
-      case CTX_RESTORE:
-        cairo_restore (cr);
-        break;
-      case CTX_FONT_SIZE:
-        cairo_set_font_size (cr, ctx_arg_float (0) );
-        break;
-      case CTX_MITER_LIMIT:
-        cairo_set_miter_limit (cr, ctx_arg_float (0) );
-        break;
-      case CTX_LINE_CAP:
+#if CTX_VERBOSE_NAMES
+      if (formatter->longform)
         {
-          int cairo_val = CAIRO_LINE_CAP_SQUARE;
-          switch (ctx_arg_u8 (0) )
+          const char *str = NULL;
+          switch (entry->code)
             {
-              case CTX_CAP_ROUND:
-                cairo_val = CAIRO_LINE_CAP_ROUND;
-                break;
-              case CTX_CAP_SQUARE:
-                cairo_val = CAIRO_LINE_CAP_SQUARE;
-                break;
-              case CTX_CAP_NONE:
-                cairo_val = CAIRO_LINE_CAP_BUTT;
+              case CTX_TEXT_BASELINE:
+                switch (val)
+                  {
+                    case CTX_TEXT_BASELINE_ALPHABETIC: str = "alphabetic"; break;
+                    case CTX_TEXT_BASELINE_TOP:        str = "top";        break;
+                    case CTX_TEXT_BASELINE_BOTTOM:     str = "bottom";     break;
+                    case CTX_TEXT_BASELINE_HANGING:    str = "hanging";    break;
+                    case CTX_TEXT_BASELINE_MIDDLE:     str = "middle";     break;
+                    case CTX_TEXT_BASELINE_IDEOGRAPHIC:str = "ideographic";break;
+                  }
                 break;
-            }
-          cairo_set_line_cap (cr, cairo_val);
-        }
-        break;
-      case CTX_BLEND_MODE:
-        {
-          // does not map to cairo
-        }
-        break;
-      case CTX_COMPOSITING_MODE:
-        {
-          int cairo_val = CAIRO_OPERATOR_OVER;
-          switch (ctx_arg_u8 (0) )
-            {
-              case CTX_COMPOSITE_SOURCE_OVER:
-                cairo_val = CAIRO_OPERATOR_OVER;
+              case CTX_TEXT_ALIGN:
+                switch (val)
+                  {
+                    case CTX_TEXT_ALIGN_LEFT:   str = "left"; break;
+                    case CTX_TEXT_ALIGN_RIGHT:  str = "right"; break;
+                    case CTX_TEXT_ALIGN_START:  str = "start"; break;
+                    case CTX_TEXT_ALIGN_END:    str = "end"; break;
+                    case CTX_TEXT_ALIGN_CENTER: str = "center"; break;
+                  }
                 break;
-              case CTX_COMPOSITE_COPY:
-                cairo_val = CAIRO_OPERATOR_SOURCE;
+              case CTX_LINE_CAP:
+                switch (val)
+                  {
+                    case CTX_CAP_NONE:   str = "none"; break;
+                    case CTX_CAP_ROUND:  str = "round"; break;
+                    case CTX_CAP_SQUARE: str = "square"; break;
+                  }
                 break;
-            }
-          cairo_set_operator (cr, cairo_val);
-        }
-      case CTX_LINE_JOIN:
-        {
-          int cairo_val = CAIRO_LINE_JOIN_ROUND;
-          switch (ctx_arg_u8 (0) )
-            {
-              case CTX_JOIN_ROUND:
-                cairo_val = CAIRO_LINE_JOIN_ROUND;
+              case CTX_LINE_JOIN:
+                switch (val)
+                  {
+                    case CTX_JOIN_MITER: str = "miter"; break;
+                    case CTX_JOIN_ROUND: str = "round"; break;
+                    case CTX_JOIN_BEVEL: str = "bevel"; break;
+                  }
                 break;
-              case CTX_JOIN_BEVEL:
-                cairo_val = CAIRO_LINE_JOIN_BEVEL;
+              case CTX_FILL_RULE:
+                switch (val)
+                  {
+                    case CTX_FILL_RULE_WINDING:  str = "winding"; break;
+                    case CTX_FILL_RULE_EVEN_ODD: str = "evenodd"; break;
+                  }
                 break;
-              case CTX_JOIN_MITER:
-                cairo_val = CAIRO_LINE_JOIN_MITER;
+              case CTX_BLEND_MODE:
+                switch (val)
+                  {
+            case CTX_BLEND_NORMAL:      str = "normal"; break;
+            case CTX_BLEND_MULTIPLY:    str = "multiply"; break;
+            case CTX_BLEND_SCREEN:      str = "screen"; break;
+            case CTX_BLEND_OVERLAY:     str = "overlay"; break;
+            case CTX_BLEND_DARKEN:      str = "darken"; break;
+            case CTX_BLEND_LIGHTEN:     str = "lighten"; break;
+            case CTX_BLEND_COLOR_DODGE: str = "colorDodge"; break;
+            case CTX_BLEND_COLOR_BURN:  str = "colorBurn"; break;
+            case CTX_BLEND_HARD_LIGHT:  str = "hardLight"; break;
+            case CTX_BLEND_SOFT_LIGHT:  str = "softLight"; break;
+            case CTX_BLEND_DIFFERENCE:  str = "difference"; break;
+            case CTX_BLEND_EXCLUSION:   str = "exclusion"; break;
+            case CTX_BLEND_HUE:         str = "hue"; break;
+            case CTX_BLEND_SATURATION:  str = "saturation"; break;
+            case CTX_BLEND_COLOR:       str = "color"; break; 
+            case CTX_BLEND_LUMINOSITY:  str = "luminosity"; break;
+                  }
                 break;
+              case CTX_COMPOSITING_MODE:
+                switch (val)
+                  {
+              case CTX_COMPOSITE_SOURCE_OVER: str = "sourceOver"; break;
+              case CTX_COMPOSITE_COPY: str = "copy"; break;
+              case CTX_COMPOSITE_CLEAR: str = "clear"; break;
+              case CTX_COMPOSITE_SOURCE_IN: str = "sourceIn"; break;
+              case CTX_COMPOSITE_SOURCE_OUT: str = "sourceOut"; break;
+              case CTX_COMPOSITE_SOURCE_ATOP: str = "sourceAtop"; break;
+              case CTX_COMPOSITE_DESTINATION: str = "destination"; break;
+              case CTX_COMPOSITE_DESTINATION_OVER: str = "destinationOver"; break;
+              case CTX_COMPOSITE_DESTINATION_IN: str = "destinationIn"; break;
+              case CTX_COMPOSITE_DESTINATION_OUT: str = "destinationOut"; break;
+              case CTX_COMPOSITE_DESTINATION_ATOP: str = "destinationAtop"; break;
+              case CTX_COMPOSITE_XOR: str = "xor"; break;
+                  }
+
+               break;
             }
-          cairo_set_line_join (cr, cairo_val);
-        }
-        break;
-      case CTX_LINEAR_GRADIENT:
-        {
-          if (ctx_cairo->pat)
+          if (str)
             {
-              cairo_pattern_destroy (ctx_cairo->pat);
-              ctx_cairo->pat = NULL;
+              ctx_formatter_addstr (formatter, str, -1);
             }
-          ctx_cairo->pat = cairo_pattern_create_linear (ctx_arg_float (0), ctx_arg_float (1),
-                           ctx_arg_float (2), ctx_arg_float (3) );
-          cairo_pattern_add_color_stop_rgba (ctx_cairo->pat, 0, 0, 0, 0, 1);
-          cairo_pattern_add_color_stop_rgba (ctx_cairo->pat, 1, 1, 1, 1, 1);
-          cairo_set_source (cr, ctx_cairo->pat);
-        }
-        break;
-      case CTX_RADIAL_GRADIENT:
-        {
-          if (ctx_cairo->pat)
+          else
             {
-              cairo_pattern_destroy (ctx_cairo->pat);
-              ctx_cairo->pat = NULL;
+              ctx_formatter_addstrf (formatter, "%i", val);
             }
-          ctx_cairo->pat = cairo_pattern_create_radial (ctx_arg_float (0), ctx_arg_float (1),
-                           ctx_arg_float (2), ctx_arg_float (3),
-                           ctx_arg_float (4), ctx_arg_float (5) );
-          cairo_set_source (cr, ctx_cairo->pat);
         }
-        break;
-      case CTX_GRADIENT_STOP:
-        cairo_pattern_add_color_stop_rgba (ctx_cairo->pat,
-                                           ctx_arg_float (0),
-                                           ctx_u8_to_float (ctx_arg_u8 (4) ),
-                                           ctx_u8_to_float (ctx_arg_u8 (5) ),
-                                           ctx_u8_to_float (ctx_arg_u8 (6) ),
-                                           ctx_u8_to_float (ctx_arg_u8 (7) ) );
-        break;
-        // XXX  implement TEXTURE
-#if 0
-      case CTX_LOAD_IMAGE:
+      else
+#endif
         {
-          if (image)
-            {
-              cairo_surface_destroy (image);
-              image = NULL;
-            }
-          if (pat)
-            {
-              cairo_pattern_destroy (pat);
-              pat = NULL;
-            }
-          image = cairo_image_surface_create_from_png (ctx_arg_string() );
-          cairo_set_source_surface (cr, image, ctx_arg_float (0), ctx_arg_float (1) );
+          ctx_formatter_addstrf (formatter, "%i", val);
         }
-        break;
-#endif
-      case CTX_TEXT:
-        /* XXX: implement some linebreaking/wrap, positioning
-         *      behavior here
-         */
-        cairo_show_text (cr, ctx_arg_string () );
-        break;
-      case CTX_CONT:
-      case CTX_EDGE:
-      case CTX_DATA:
-      case CTX_DATA_REV:
-      case CTX_FLUSH:
-        break;
     }
-  ctx_process (ctx_cairo->ctx, entry);
+  _ctx_print_endcmd (formatter);
 }
 
-void ctx_cairo_free (CtxCairo *ctx_cairo)
+
+static void
+ctx_print_a85 (CtxFormatter *formatter, uint8_t *data, int length)
 {
-  if (ctx_cairo->pat)
-    { cairo_pattern_destroy (ctx_cairo->pat); }
-  if (ctx_cairo->image)
-    { cairo_surface_destroy (ctx_cairo->image); }
-  free (ctx_cairo);
+  char *tmp = (char*)malloc (ctx_a85enc_len (length));
+  ctx_a85enc (data, tmp, length);
+  ctx_formatter_addstr (formatter, " ~", 2);
+  ctx_formatter_addstr (formatter, tmp, -1);
+  ctx_formatter_addstr (formatter, "~ ", 2);
+  free (tmp);
 }
 
-void
-ctx_render_cairo (Ctx *ctx, cairo_t *cr)
+static void
+ctx_print_escaped_string (CtxFormatter *formatter, const char *string)
 {
-  CtxIterator iterator;
-  CtxCommand *command;
-  CtxCairo    ctx_cairo = {{(void*)ctx_cairo_process, NULL, NULL}, ctx, cr, NULL, NULL};
-  ctx_iterator_init (&iterator, &ctx->drawlist, 0,
-                     CTX_ITERATOR_EXPAND_BITPACK);
-  while ( (command = ctx_iterator_next (&iterator) ) )
-    { ctx_cairo_process (&ctx_cairo, command); }
+  if (!string) { return; }
+  for (int i = 0; string[i]; i++)
+    {
+      switch (string[i])
+        {
+          case '"':
+            ctx_formatter_addstr (formatter, "\\\"", 2);
+            break;
+          case '\\':
+            ctx_formatter_addstr (formatter, "\\\\", 2);
+            break;
+          case '\n':
+            ctx_formatter_addstr (formatter, "\\n", 2);
+            break;
+          default:
+            ctx_formatter_addstr (formatter, &string[i], 1);
+        }
+    }
 }
 
-Ctx *
-ctx_new_for_cairo (cairo_t *cr)
+static void
+ctx_print_float (CtxFormatter *formatter, float val)
 {
-  Ctx *ctx = ctx_new ();
-  CtxCairo *ctx_cairo = calloc(sizeof(CtxCairo),1);
-  ctx_cairo->vfuncs.free = (void*)ctx_cairo_free;
-  ctx_cairo->vfuncs.process = (void*)ctx_cairo_process;
-  ctx_cairo->ctx = ctx;
-  ctx_cairo->cr = cr;
-
-  ctx_set_renderer (ctx, (void*)ctx_cairo);
-  return ctx;
+  char temp[128];
+  sprintf (temp, "%0.3f", val);
+  int j;
+  for (j = 0; temp[j]; j++)
+    if (j == ',') { temp[j] = '.'; }
+  j--;
+  if (j>0)
+    while (temp[j] == '0')
+      {
+        temp[j]=0;
+        j--;
+      }
+  if (temp[j]=='.')
+    { temp[j]='\0'; }
+  ctx_formatter_addstr (formatter, temp, -1);
 }
 
-#endif
-
-#if CTX_EVENTS
+static void
+ctx_print_int (CtxFormatter *formatter, int val)
+{
+  ctx_formatter_addstrf (formatter, "%i", val);
+}
 
-static int ctx_find_largest_matching_substring
- (const char *X, const char *Y, int m, int n, int *offsetY, int *offsetX) 
-{ 
-  int longest_common_suffix[2][n+1];
-  int best_length = 0;
-  for (int i=0; i<=m; i++)
-  {
-    for (int j=0; j<=n; j++)
+static void
+ctx_print_entry (CtxFormatter *formatter, CtxEntry *entry, int args)
+{
+  _ctx_print_name (formatter, entry->code);
+  for (int i = 0; i <  args; i ++)
     {
-      if (i == 0 || j == 0 || !(X[i-1] == Y[j-1]))
-      {
-        longest_common_suffix[i%2][j] = 0;
-      }
-      else
-      {
-          longest_common_suffix[i%2][j] = longest_common_suffix[(i-1)%2][j-1] + 1;
-          if (best_length < longest_common_suffix[i%2][j])
-          {
-            best_length = longest_common_suffix[i%2][j];
-            if (offsetY) *offsetY = j - best_length;
-            if (offsetX) *offsetX = i - best_length;
-          }
-      }
+      float val = ctx_arg_float (i);
+      if (i>0 && val >= 0.0f)
+        {
+          if (formatter->longform)
+            {
+              ctx_formatter_addstr (formatter, ", ", 2);
+            }
+          else
+            {
+              if (val >= 0.0f)
+                ctx_formatter_addstr (formatter, " ", 1);
+            }
+        }
+      ctx_print_float (formatter, val);
     }
-  }
-  return best_length;
-} 
+  _ctx_print_endcmd (formatter);
+}
 
-typedef struct CtxSpan {
-  int from_prev;
-  int start;
-  int length;
-} CtxSpan;
+static void
+ctx_print_glyph (CtxFormatter *formatter, CtxEntry *entry, int args)
+{
+  _ctx_print_name (formatter, entry->code);
+  ctx_formatter_addstrf (formatter, "%i", entry->data.u32[0]);
+  _ctx_print_endcmd (formatter);
+}
 
-#define CHUNK_SIZE 32
-#define MIN_MATCH  7        // minimum match length to be encoded
-#define WINDOW_PADDING 16   // look-aside amount
+static void
+ctx_formatter_process (void *user_data, CtxCommand *c);
 
-#if 0
-static void _dassert(int line, int condition, const char *str, int foo, int bar, int baz)
-{
-  if (!condition)
-  {
-    FILE *f = fopen ("/tmp/cdebug", "a");
-    fprintf (f, "%i: %s    %i %i %i\n", line, str, foo, bar, baz);
-    fclose (f);
-  }
-}
-#define dassert(cond, foo, bar, baz) _dassert(__LINE__, cond, #cond, foo, bar ,baz)
-#endif
-#define dassert(cond, foo, bar, baz)
 
-/* XXX repeated substring matching is slow, we'll be
- * better off with a hash-table with linked lists of
- * matching 3-4 characters in previous.. or even
- * a naive approach that expects rough alignment..
- */
-static char *encode_in_terms_of_previous (
-                const char *src,  int src_len,
-                const char *prev, int prev_len,
-                int *out_len,
-                int max_ticks)
+static void
+ctx_formatter_process (void *user_data, CtxCommand *c)
 {
-  CtxString *string = ctx_string_new ("");
-  CtxList *encoded_list = NULL;
-
-  /* TODO : make expected position offset in prev slide based on
-   * matches and not be constant */
-
-  long ticks_start = ctx_ticks ();
-  int start = 0;
-  int length = CHUNK_SIZE;
-  for (start = 0; start < src_len; start += length)
-  {
-    CtxSpan *span = calloc (sizeof (CtxSpan), 1);
-    span->start = start;
-    if (start + length > src_len)
-      span->length = src_len - start;
-    else
-      span->length = length;
-    span->from_prev = 0;
-    ctx_list_append (&encoded_list, span);
-  }
+  CtxEntry *entry = &c->entry;
+  CtxFormatter *formatter = (CtxFormatter*)user_data;
 
-  for (CtxList *l = encoded_list; l; l = l->next)
-  {
-    CtxSpan *span = l->data;
-    if (!span->from_prev)
+  switch (entry->code)
+  //switch ((CtxCode)(entry->code))
     {
-      if (span->length >= MIN_MATCH)
-      {
-         int prev_pos = 0;
-         int curr_pos = 0;
-         assert(1);
-#if 0
-         int prev_start =  0;
-         int prev_window_length = prev_len;
-#else
-         int window_padding = WINDOW_PADDING;
-         int prev_start = span->start - window_padding;
-         if (prev_start < 0)
-           prev_start = 0;
+      case CTX_GLYPH:
+        ctx_print_glyph (formatter, entry, 1);
+        break;
+      case CTX_LINE_TO:
+      case CTX_REL_LINE_TO:
+      case CTX_SCALE:
+      case CTX_TRANSLATE:
+      case CTX_MOVE_TO:
+      case CTX_REL_MOVE_TO:
+      case CTX_SMOOTHQ_TO:
+      case CTX_REL_SMOOTHQ_TO:
+        ctx_print_entry (formatter, entry, 2);
+        break;
+      case CTX_TEXTURE:
+        _ctx_print_name (formatter, entry->code);
+        ctx_formatter_addstrf (formatter, "\"");
+        ctx_print_escaped_string (formatter, c->texture.eid);
+        ctx_formatter_addstrf (formatter, "\", ");
+        ctx_print_float (formatter, c->texture.x);
+        ctx_formatter_addstrf (formatter, ", ");
+        ctx_print_float (formatter, c->texture.y);
+        ctx_formatter_addstrf (formatter, " ");
+        _ctx_print_endcmd (formatter);
+        break;
 
-         dassert(span->start>=0 , 0,0,0);
+      case CTX_DEFINE_TEXTURE:
+        {
+        _ctx_print_name (formatter, entry->code);
+        ctx_formatter_addstrf (formatter, "\"");
+        ctx_print_escaped_string (formatter, c->define_texture.eid);
+        ctx_formatter_addstrf (formatter, "\", ");
+        ctx_print_int (formatter, c->define_texture.width);
+        ctx_formatter_addstrf (formatter, ", ");
+        ctx_print_int (formatter, c->define_texture.height);
+        ctx_formatter_addstrf (formatter, ",%i, ", c->define_texture.format);
 
-         int prev_window_length = prev_len - prev_start;
-         if (prev_window_length > span->length + window_padding * 2 + span->start)
-           prev_window_length = span->length + window_padding * 2 + span->start;
-#endif
-         int match_len = 0;
-         if (prev_window_length > 0)
-           match_len = ctx_find_largest_matching_substring(prev + prev_start, src + span->start, 
prev_window_length, span->length, &curr_pos, &prev_pos);
+        uint8_t *pixel_data = ctx_define_texture_pixel_data (entry);
 #if 1
-         prev_pos += prev_start;
-#endif
-
-         if (match_len >= MIN_MATCH)
-         {
-            int start  = span->start;
-            int length = span->length;
 
-            span->from_prev = 1;
-            span->start     = prev_pos;
-            span->length    = match_len;
-            dassert (span->start >= 0, prev_pos, prev_start, span->start);
-            dassert (span->length > 0, prev_pos, prev_start, span->length);
+        int stride = ctx_pixel_format_get_stride ((CtxPixelFormat)c->define_texture.format, 
c->define_texture.width);
+        //fprintf (stderr, "encoding %i bytes\n", c->define_texture.height *stride);
+        ctx_print_a85 (formatter, pixel_data, c->define_texture.height * stride);
+#else
+        ctx_formatter_addstrf (formatter, "\"");
+        ctx_print_escaped_string (formatter, pixel_data);
+        ctx_formatter_addstrf (formatter, "\" ");
 
-            if (curr_pos)
-            {
-              CtxSpan *prev = calloc (sizeof (CtxSpan), 1);
-              prev->start = start;
-              prev->length =  curr_pos;
-            dassert (prev->start >= 0, prev_pos, prev_start, prev->start);
-            dassert (prev->length > 0, prev_pos, prev_start, prev->length);
-              prev->from_prev = 0;
-              ctx_list_insert_before (&encoded_list, l, prev);
-            }
+#endif
 
+        _ctx_print_endcmd (formatter);
+        }
+        break;
 
-            if (match_len + curr_pos < start + length)
+      case CTX_REL_ARC_TO:
+      case CTX_ARC_TO:
+      case CTX_ROUND_RECTANGLE:
+        ctx_print_entry (formatter, entry, 5);
+        break;
+      case CTX_CURVE_TO:
+      case CTX_REL_CURVE_TO:
+      case CTX_ARC:
+      case CTX_RADIAL_GRADIENT:
+      case CTX_APPLY_TRANSFORM:
+        ctx_print_entry (formatter, entry, 6);
+        break;
+      case CTX_QUAD_TO:
+      case CTX_RECTANGLE:
+      case CTX_REL_QUAD_TO:
+      case CTX_LINEAR_GRADIENT:
+      case CTX_VIEW_BOX:
+      case CTX_SMOOTH_TO:
+      case CTX_REL_SMOOTH_TO:
+        ctx_print_entry (formatter, entry, 4);
+        break;
+      case CTX_FONT_SIZE:
+      case CTX_MITER_LIMIT:
+      case CTX_ROTATE:
+      case CTX_LINE_WIDTH:
+      case CTX_LINE_DASH_OFFSET:
+      case CTX_GLOBAL_ALPHA:
+      case CTX_SHADOW_BLUR:
+      case CTX_SHADOW_OFFSET_X:
+      case CTX_SHADOW_OFFSET_Y:
+      case CTX_VER_LINE_TO:
+      case CTX_HOR_LINE_TO:
+      case CTX_REL_VER_LINE_TO:
+      case CTX_REL_HOR_LINE_TO:
+        ctx_print_entry (formatter, entry, 1);
+        break;
+#if 0
+      case CTX_SET:
+        _ctx_print_name (formatter, entry->code);
+        switch (c->set.key_hash)
+        {
+           case CTX_x: ctx_formatter_addstrf (formatter, " 'x' "); break;
+           case CTX_y: ctx_formatter_addstrf (formatter, " 'y' "); break;
+           case CTX_width: ctx_formatter_addstrf (formatter, " width "); break;
+           case CTX_height: ctx_formatter_addstrf (formatter, " height "); break;
+           default:
+             ctx_formatter_addstrf (formatter, " %d ", c->set.key_hash);
+        }
+        ctx_formatter_addstrf (formatter, "\"");
+        ctx_print_escaped_string (formatter, (char*)c->set.utf8);
+        ctx_formatter_addstrf (formatter, "\"");
+        _ctx_print_endcmd (formatter);
+        break;
+#endif
+      case CTX_COLOR:
+        if (formatter->longform ||  1)
+          {
+            _ctx_indent (formatter);
+            int model = (int) c->set_color.model;
+            const char *suffix="";
+            if (model & 512)
             {
-              CtxSpan *next = calloc (sizeof (CtxSpan), 1);
-              next->start = start + curr_pos + match_len;
-              next->length = (start + length) - next->start;
-            dassert (next->start >= 0, prev_pos, prev_start, next->start);
-      //    dassert (next->length > 0, prev_pos, prev_start, next->length);
-              next->from_prev = 0;
-              if (next->length)
-              {
-                if (l->next)
-                  ctx_list_insert_before (&encoded_list, l->next, next);
-                else
-                  ctx_list_append (&encoded_list, next);
-              }
-              else
-                free (next);
+              model = model & 511;
+              suffix = "S";
             }
-
-            if (curr_pos) // step one item back for forloop
-            {
-              CtxList *tmp = encoded_list;
-              int found = 0;
-              while (!found && tmp && tmp->next)
+            switch (model)
               {
-                if (tmp->next == l)
-                {
-                  l = tmp;
+                case CTX_GRAY:
+                  ctx_formatter_addstrf (formatter, "gray%s ", suffix);
+                  ctx_print_float (formatter, c->graya.g);
+                  break;
+                case CTX_GRAYA:
+                  ctx_formatter_addstrf (formatter, "graya%s ", suffix);
+                  ctx_print_float (formatter, c->graya.g);
+                  ctx_formatter_addstrf (formatter, " ");
+                  ctx_print_float (formatter, c->graya.a);
+                  break;
+                case CTX_RGBA:
+                  if (c->rgba.a != 1.0)
+                  {
+                    ctx_formatter_addstrf (formatter, "rgba%s ", suffix);
+                    ctx_print_float (formatter, c->rgba.r);
+                    ctx_formatter_addstrf (formatter, " ");
+                    ctx_print_float (formatter, c->rgba.g);
+                    ctx_formatter_addstrf (formatter, " ");
+                    ctx_print_float (formatter, c->rgba.b);
+                    ctx_formatter_addstrf (formatter, " ");
+                    ctx_print_float (formatter, c->rgba.a);
+                    break;
+                  }
+                  /* FALLTHROUGH */
+                case CTX_RGB:
+                  if (c->rgba.r == c->rgba.g && c->rgba.g == c->rgba.b)
+                  {
+                    ctx_formatter_addstrf (formatter, "gray%s ", suffix);
+                    ctx_print_float (formatter, c->rgba.r);
+                    ctx_formatter_addstrf (formatter, " ");
+                    break;
+                  }
+                  ctx_formatter_addstrf (formatter, "rgb%s ", suffix);
+                  ctx_print_float (formatter, c->rgba.r);
+                  ctx_formatter_addstrf (formatter, " ");
+                  ctx_print_float (formatter, c->rgba.g);
+                  ctx_formatter_addstrf (formatter, " ");
+                  ctx_print_float (formatter, c->rgba.b);
+                  break;
+                case CTX_DRGB:
+                  ctx_formatter_addstrf (formatter, "drgb%s ", suffix);
+                  ctx_print_float (formatter, c->rgba.r);
+                  ctx_formatter_addstrf (formatter, " ");
+                  ctx_print_float (formatter, c->rgba.g);
+                  ctx_formatter_addstrf (formatter, " ");
+                  ctx_print_float (formatter, c->rgba.b);
+                  break;
+                case CTX_DRGBA:
+                  ctx_formatter_addstrf (formatter, "drgba%s ", suffix);
+                  ctx_print_float (formatter, c->rgba.r);
+                  ctx_formatter_addstrf (formatter, " ");
+                  ctx_print_float (formatter, c->rgba.g);
+                  ctx_formatter_addstrf (formatter, " ");
+                  ctx_print_float (formatter, c->rgba.b);
+                  ctx_formatter_addstrf (formatter, " ");
+                  ctx_print_float (formatter, c->rgba.a);
+                  break;
+                case CTX_CMYK:
+                  ctx_formatter_addstrf (formatter, "cmyk%s ", suffix);
+                  ctx_print_float (formatter, c->cmyka.c);
+                  ctx_formatter_addstrf (formatter, " ");
+                  ctx_print_float (formatter, c->cmyka.m);
+                  ctx_formatter_addstrf (formatter, " ");
+                  ctx_print_float (formatter, c->cmyka.y);
+                  ctx_formatter_addstrf (formatter, " ");
+                  ctx_print_float (formatter, c->cmyka.k);
+                  break;
+                case CTX_CMYKA:
+                  ctx_formatter_addstrf (formatter, "cmyka%s ", suffix);
+                  ctx_print_float (formatter, c->cmyka.c);
+                  ctx_formatter_addstrf (formatter, " ");
+                  ctx_print_float (formatter, c->cmyka.m);
+                  ctx_formatter_addstrf (formatter, " ");
+                  ctx_print_float (formatter, c->cmyka.y);
+                  ctx_formatter_addstrf (formatter, " ");
+                  ctx_print_float (formatter, c->cmyka.k);
+                  ctx_formatter_addstrf (formatter, " ");
+                  ctx_print_float (formatter, c->cmyka.a);
+                  break;
+                case CTX_DCMYK:
+                  ctx_formatter_addstrf (formatter, "dcmyk%s ", suffix);
+                  ctx_print_float (formatter, c->cmyka.c);
+                  ctx_formatter_addstrf (formatter, " ");
+                  ctx_print_float (formatter, c->cmyka.m);
+                  ctx_formatter_addstrf (formatter, " ");
+                  ctx_print_float (formatter, c->cmyka.y);
+                  ctx_formatter_addstrf (formatter, " ");
+                  ctx_print_float (formatter, c->cmyka.k);
+                  break;
+                case CTX_DCMYKA:
+                  ctx_formatter_addstrf (formatter, "dcmyka%s ", suffix);
+                  ctx_print_float (formatter, c->cmyka.c);
+                  ctx_formatter_addstrf (formatter, " ");
+                  ctx_print_float (formatter, c->cmyka.m);
+                  ctx_formatter_addstrf (formatter, " ");
+                  ctx_print_float (formatter, c->cmyka.y);
+                  ctx_formatter_addstrf (formatter, " ");
+                  ctx_print_float (formatter, c->cmyka.k);
+                  ctx_formatter_addstrf (formatter, " ");
+                  ctx_print_float (formatter, c->cmyka.a);
                   break;
-                }
-                tmp = tmp->next;
               }
-            }
-         }
-      }
-    }
-
-    if (ctx_ticks ()-ticks_start > (unsigned long)max_ticks)
-      break;
-  }
-
-  /* merge adjecant prev span references  */
-  {
-    for (CtxList *l = encoded_list; l; l = l->next)
-    {
-      CtxSpan *span = l->data;
-again:
-      if (l->next)
-      {
-        CtxSpan *next_span = l->next->data;
-        if (span->from_prev && next_span->from_prev &&
-            span->start + span->length == 
-            next_span->start)
+          }
+        else
+          {
+            ctx_print_entry (formatter, entry, 1);
+          }
+        break;
+      case CTX_SET_RGBA_U8:
+        if (formatter->longform)
+          {
+            _ctx_indent (formatter);
+            ctx_formatter_addstrf (formatter, "rgba (");
+          }
+        else
+          {
+            ctx_formatter_addstrf (formatter, "rgba (");
+          }
+        for (int c = 0; c < 4; c++)
+          {
+            if (c)
+              {
+                if (formatter->longform)
+                  ctx_formatter_addstrf (formatter, ", ");
+                else
+                  ctx_formatter_addstrf (formatter, " ");
+              }
+            ctx_print_float (formatter, ctx_u8_to_float (ctx_arg_u8 (c) ) );
+          }
+        _ctx_print_endcmd (formatter);
+        break;
+      case CTX_SET_PIXEL:
+#if 0
+        ctx_set_pixel_u8 (d_ctx,
+                          ctx_arg_u16 (2), ctx_arg_u16 (3),
+                          ctx_arg_u8 (0),
+                          ctx_arg_u8 (1),
+                          ctx_arg_u8 (2),
+                          ctx_arg_u8 (3) );
+#endif
+        break;
+      case CTX_FILL:
+      case CTX_RESET:
+      case CTX_STROKE:
+      case CTX_IDENTITY:
+      case CTX_CLIP:
+      case CTX_BEGIN_PATH:
+      case CTX_CLOSE_PATH:
+      case CTX_SAVE:
+      case CTX_PRESERVE:
+      case CTX_START_GROUP:
+      case CTX_NEW_PAGE:
+      case CTX_END_GROUP:
+      case CTX_RESTORE:
+      case CTX_STROKE_SOURCE:
+        ctx_print_entry (formatter, entry, 0);
+        break;
+      case CTX_TEXT_ALIGN:
+      case CTX_TEXT_BASELINE:
+      case CTX_TEXT_DIRECTION:
+      case CTX_FILL_RULE:
+      case CTX_LINE_CAP:
+      case CTX_LINE_JOIN:
+      case CTX_COMPOSITING_MODE:
+      case CTX_BLEND_MODE:
+      case CTX_IMAGE_SMOOTHING:
+        ctx_print_entry_enum (formatter, entry, 1);
+        break;
+      case CTX_GRADIENT_STOP:
+        _ctx_print_name (formatter, entry->code);
+        for (int c = 0; c < 4; c++)
+          {
+            if (c)
+              ctx_formatter_addstrf (formatter, " ");
+            ctx_print_float (formatter, ctx_u8_to_float (ctx_arg_u8 (4+c) ) );
+          }
+        _ctx_print_endcmd (formatter);
+        break;
+      case CTX_TEXT:
+      case CTX_STROKE_TEXT:
+      case CTX_FONT:
+        _ctx_print_name (formatter, entry->code);
+        ctx_formatter_addstrf (formatter, "\"");
+        ctx_print_escaped_string (formatter, ctx_arg_string() );
+        ctx_formatter_addstrf (formatter, "\"");
+        _ctx_print_endcmd (formatter);
+        break;
+      case CTX_CONT:
+      case CTX_EDGE:
+      case CTX_DATA:
+      case CTX_DATA_REV:
+      case CTX_FLUSH:
+        break;
+      case CTX_KERNING_PAIR:
+        _ctx_print_name (formatter, entry->code);
+        ctx_formatter_addstrf (formatter, "\"");
         {
-           span->length += next_span->length;
-           ctx_list_remove (&encoded_list, next_span);
-           goto again;
+           uint8_t utf8[16];
+           utf8[ctx_unichar_to_utf8 (c->kern.glyph_before, utf8)]=0;
+           ctx_print_escaped_string (formatter, (char*)utf8);
+           ctx_formatter_addstrf (formatter, "\", \"");
+           utf8[ctx_unichar_to_utf8 (c->kern.glyph_after, utf8)]=0;
+           ctx_print_escaped_string (formatter, (char*)utf8);
+           ctx_formatter_addstrf (formatter, "\"");
+           sprintf ((char*)utf8, ", %f", c->kern.amount / 256.0);
+           ctx_print_escaped_string (formatter, (char*)utf8);
         }
-      }
-    }
-  }
+        _ctx_print_endcmd (formatter);
+        break;
 
-  while (encoded_list)
-  {
-    CtxSpan *span = encoded_list->data;
-    if (span->from_prev)
-    {
-      char ref[128];
-      sprintf (ref, "%c%i %i%c", CTX_CODEC_CHAR, span->start, span->length, CTX_CODEC_CHAR);
-      ctx_string_append_data (string, ref, strlen(ref));
-    }
-    else
-    {
-      for (int i = span->start; i< span->start+span->length; i++)
-      {
-        if (src[i] == CTX_CODEC_CHAR)
-        {
-          char bytes[2]={CTX_CODEC_CHAR, CTX_CODEC_CHAR};
-          ctx_string_append_data (string, bytes, 2);
-        }
-        else
+      case CTX_DEFINE_GLYPH:
+        _ctx_print_name (formatter, entry->code);
+        ctx_formatter_addstrf (formatter, "\"");
         {
-          ctx_string_append_data (string, &src[i], 1);
+           uint8_t utf8[16];
+           utf8[ctx_unichar_to_utf8 (entry->data.u32[0], utf8)]=0;
+           ctx_print_escaped_string (formatter, (char*)utf8);
+           ctx_formatter_addstrf (formatter, "\"");
+           sprintf ((char*)utf8, ", %f", entry->data.u32[1]/256.0);
+           ctx_print_escaped_string (formatter, (char*)utf8);
         }
-      }
+        _ctx_print_endcmd (formatter);
+        break;
     }
-    free (span);
-    ctx_list_remove (&encoded_list, span);
-  }
-
-  char *ret = string->str;
-  if (out_len) *out_len = string->length;
-  ctx_string_free (string, 0);
-  return ret;
 }
 
-#if 0 // for documentation/reference purposes
-static char *decode_ctx (const char *encoded, int enc_len, const char *prev, int prev_len, int *out_len)
+void
+ctx_render_stream (Ctx *ctx, FILE *stream, int longform)
 {
-  CtxString *string = ctx_string_new ("");
-  char reference[32]="";
-  int ref_len = 0;
-  int in_ref = 0;
-  for (int i = 0; i < enc_len; i++)
-  {
-    if (encoded[i] == CTX_CODEC_CHAR)
-    {
-      if (!in_ref)
-      {
-        in_ref = 1;
-      }
-      else
-      {
-        int start = atoi (reference);
-        int len = 0;
-        if (strchr (reference, ' '))
-          len = atoi (strchr (reference, ' ')+1);
-
-        if (start < 0)start = 0;
-        if (start >= prev_len)start = prev_len-1;
-        if (len + start > prev_len)
-          len = prev_len - start;
+  CtxIterator iterator;
+  CtxFormatter formatter;
+  formatter.target= stream;
+  formatter.longform = longform;
+  formatter.indent = 0;
+  formatter.add_str = _ctx_stream_addstr;
+  CtxCommand *command;
+  ctx_iterator_init (&iterator, &ctx->drawlist, 0,
+                     CTX_ITERATOR_EXPAND_BITPACK);
+  while ( (command = ctx_iterator_next (&iterator) ) )
+    { ctx_formatter_process (&formatter, command); }
+  fprintf (stream, "\n");
+}
 
-        if (start == 0 && len == 0)
-          ctx_string_append_byte (string, CTX_CODEC_CHAR);
-        else
-          ctx_string_append_data (string, prev + start, len);
-        ref_len = 0;
-        in_ref = 0;
-      }
-    }
-    else
-    {
-      if (in_ref)
-      {
-        if (ref_len < 16)
-        {
-          reference[ref_len++] = encoded[i];
-          reference[ref_len] = 0;
-        }
-      }
-      else
-      ctx_string_append_data (string, &encoded[i], 1);
-    }
-  }
+char *
+ctx_render_string (Ctx *ctx, int longform, int *retlen)
+{
+  CtxString *string = ctx_string_new ("");
+  CtxIterator iterator;
+  CtxFormatter formatter;
+  formatter.target= string;
+  formatter.longform = longform;
+  formatter.indent = 0;
+  formatter.add_str = _ctx_string_addstr;
+  CtxCommand *command;
+  ctx_iterator_init (&iterator, &ctx->drawlist, 0,
+                     CTX_ITERATOR_EXPAND_BITPACK);
+  while ( (command = ctx_iterator_next (&iterator) ) )
+    { ctx_formatter_process (&formatter, command); }
   char *ret = string->str;
-  if (out_len) *out_len = string->length;
+  if (retlen)
+    *retlen = string->length;
   ctx_string_free (string, 0);
   return ret;
 }
-#endif
-
-#define CTX_START_STRING "U\n"  // or " reset "
-#define CTX_END_STRING   "\nX"  // or "\ndone"
-#define CTX_END_STRING2  "\n\e"
 
-int ctx_frame_ack = -1;
-static char *prev_frame_contents = NULL;
-static int   prev_frame_len = 0;
 
-static void ctx_ctx_flush (CtxCtx *ctxctx)
-{
-#if 0
-  FILE *debug = fopen ("/tmp/ctx-debug", "a");
-  fprintf (debug, "------\n");
 #endif
 
-  if (ctx_native_events)
-    fprintf (stdout, "\e[?201h");
-  fprintf (stdout, "\e[H\e[?25l\e[?200h");
-#if 1
-  fprintf (stdout, CTX_START_STRING);
-  ctx_render_stream (ctxctx->ctx, stdout, 0);
-  fprintf (stdout, CTX_END_STRING);
+#if CTX_EVENTS
+int ctx_width (Ctx *ctx)
+{
+  return ctx->events.width;
+}
+int ctx_height (Ctx *ctx)
+{
+  return ctx->events.height;
+}
 #else
-  {
-    int cur_frame_len = 0;
-    char *rest = ctx_render_string (ctxctx->ctx, 0, &cur_frame_len);
-    char *cur_frame_contents = malloc (cur_frame_len + strlen(CTX_START_STRING) + strlen (CTX_END_STRING) + 
1);
-
-    cur_frame_contents[0]=0;
-    strcat (cur_frame_contents, CTX_START_STRING);
-    strcat (cur_frame_contents, rest);
-    strcat (cur_frame_contents, CTX_END_STRING);
-    free (rest);
-    cur_frame_len += strlen (CTX_START_STRING) + strlen (CTX_END_STRING);
-
-    if (prev_frame_contents && 0)  // XXX : 
-    {
-      char *encoded;
-      int encoded_len = 0;
-      //uint64_t ticks_start = ctx_ticks ();
-
-      encoded = encode_in_terms_of_previous (cur_frame_contents, cur_frame_len, prev_frame_contents, 
prev_frame_len, &encoded_len, 1000 * 10);
-//    encoded = strdup (cur_frame_contents);
-//    encoded_len = strlen (encoded);
-      //uint64_t ticks_end = ctx_ticks ();
-
-      fwrite (encoded, encoded_len, 1, stdout);
-//    fwrite (encoded, cur_frame_len, 1, stdout);
-#if 0
-      fprintf (debug, "---prev-frame(%i)\n%s", (int)strlen(prev_frame_contents), prev_frame_contents);
-      fprintf (debug, "---cur-frame(%i)\n%s", (int)strlen(cur_frame_contents), cur_frame_contents);
-      fprintf (debug, "---encoded(%.4f %i)---\n%s--------\n",
-                      (ticks_end-ticks_start)/1000.0,
-                      (int)strlen(encoded), encoded);
-#endif
-      free (encoded);
-    }
-    else
-    {
-      fwrite (cur_frame_contents, cur_frame_len, 1, stdout);
-    }
-
-    if (prev_frame_contents)
-      free (prev_frame_contents);
-    prev_frame_contents = cur_frame_contents;
-    prev_frame_len = cur_frame_len;
-  }
-#endif
-#if 0
-    fclose (debug);
+int ctx_width (Ctx *ctx)
+{
+  return 512;
+}
+int ctx_height (Ctx *ctx)
+{
+  return 384;
+}
 #endif
-  fprintf (stdout, CTX_END_STRING2);
-
-  fprintf (stdout, "\e[5n");
-  fflush (stdout);
 
-  ctx_frame_ack = 0;
-  do {
-     ctx_consume_events (ctxctx->ctx);
-  } while (ctx_frame_ack != 1);
+int ctx_rev (Ctx *ctx)
+{
+  return ctx->rev;
 }
 
-void ctx_ctx_free (CtxCtx *ctx)
+CtxState *ctx_get_state (Ctx *ctx)
 {
-  nc_at_exit ();
-  free (ctx);
-  /* we're not destoring the ctx member, this is function is called in ctx' teardown */
+  return &ctx->state;
 }
 
-Ctx *ctx_new_ctx (int width, int height)
+void ctx_dirty_rect (Ctx *ctx, int *x, int *y, int *width, int *height)
 {
-  float font_size = 12.0;
-  Ctx *ctx = ctx_new ();
-  CtxCtx *ctxctx = (CtxCtx*)calloc (sizeof (CtxCtx), 1);
-  fprintf (stdout, "\e[?1049h");
-  fflush (stdout);
-  //fprintf (stderr, "\e[H");
-  //fprintf (stderr, "\e[2J");
-  ctx_native_events = 1;
-  if (width <= 0 || height <= 0)
-  {
-    ctxctx->cols = ctx_terminal_cols ();
-    ctxctx->rows = ctx_terminal_rows ();
-    width  = ctxctx->width  = ctx_terminal_width ();
-    height = ctxctx->height = ctx_terminal_height ();
-    font_size = height / ctxctx->rows;
-    ctx_font_size (ctx, font_size);
-  }
-  else
-  {
-    ctxctx->width  = width;
-    ctxctx->height = height;
-    ctxctx->cols   = width / 80;
-    ctxctx->rows   = height / 24;
-  }
-  ctxctx->ctx = ctx;
-  if (!ctx_native_events)
-    _ctx_mouse (ctx, NC_MOUSE_DRAG);
-  ctx_set_renderer (ctx, ctxctx);
-  ctx_set_size (ctx, width, height);
-  ctxctx->flush = (void(*)(void *))ctx_ctx_flush;
-  ctxctx->free  = (void(*)(void *))ctx_ctx_free;
-  return ctx;
+  if ( (ctx->state.min_x > ctx->state.max_x) ||
+       (ctx->state.min_y > ctx->state.max_y) )
+    {
+      if (x) { *x = 0; }
+      if (y) { *y = 0; }
+      if (width) { *width = 0; }
+      if (height) { *height = 0; }
+      return;
+    }
+  if (ctx->state.min_x < 0)
+    { ctx->state.min_x = 0; }
+  if (ctx->state.min_y < 0)
+    { ctx->state.min_y = 0; }
+  if (x) { *x = ctx->state.min_x; }
+  if (y) { *y = ctx->state.min_y; }
+  if (width) { *width = ctx->state.max_x - ctx->state.min_x; }
+  if (height) { *height = ctx->state.max_y - ctx->state.min_y; }
 }
 
-void ctx_ctx_pcm (Ctx *ctx);
+#if CTX_CURRENT_PATH
+CtxIterator *
+ctx_current_path (Ctx *ctx)
+{
+  CtxIterator *iterator = &ctx->current_path_iterator;
+  ctx_iterator_init (iterator, &ctx->current_path, 0, CTX_ITERATOR_EXPAND_BITPACK);
+  return iterator;
+}
 
-int ctx_ctx_consume_events (Ctx *ctx)
+void
+ctx_path_extents (Ctx *ctx, float *ex1, float *ey1, float *ex2, float *ey2)
 {
-  int ix, iy;
-  CtxCtx *ctxctx = (CtxCtx*)ctx->renderer;
-  const char *event = NULL;
-#if CTX_AUDIO
-  ctx_ctx_pcm (ctx);
-#endif
-  if (ctx_native_events)
-    {
-      float x = 0, y = 0;
-      int b = 0;
-      char event_type[128]="";
-      event = ctx_native_get_event (ctx, 1000/120);
-#if 0
-      if(event){
-        FILE *file = fopen ("/tmp/log", "a");
-        fprintf (file, "[%s]\n", event);
-        fclose (file);
-      }
-#endif
-      if (event)
-      {
-      sscanf (event, "%s %f %f %i", event_type, &x, &y, &b);
-      if (!strcmp (event_type, "idle"))
-      {
-      }
-      else if (!strcmp (event_type, "mouse-press"))
-      {
-        ctx_pointer_press (ctx, x, y, b, 0);
-      }
-      else if (!strcmp (event_type, "mouse-drag")||
-               !strcmp (event_type, "mouse-motion"))
-      {
-        ctx_pointer_motion (ctx, x, y, b, 0);
-      }
-      else if (!strcmp (event_type, "mouse-release"))
-      {
-        ctx_pointer_release (ctx, x, y, b, 0);
-      }
-      else if (!strcmp (event_type, "message"))
-      {
-        ctx_incoming_message (ctx, event + strlen ("message"), 0);
-      } else if (!strcmp (event, "size-changed"))
-      {
-        fprintf (stdout, "\e[H\e[2J\e[?25l");
-        ctxctx->cols = ctx_terminal_cols ();
-        ctxctx->rows = ctx_terminal_rows ();
-        ctxctx->width  = ctx_terminal_width ();
-        ctxctx->height = ctx_terminal_height ();
-        ctx_set_size (ctx, ctxctx->width, ctxctx->height);
+  float minx = 50000.0;
+  float miny = 50000.0;
+  float maxx = -50000.0;
+  float maxy = -50000.0;
+  float x = 0;
+  float y = 0;
 
-        if (prev_frame_contents)
-          free (prev_frame_contents);
-        prev_frame_contents = NULL;
-        prev_frame_len = 0;
-        ctx_set_dirty (ctx, 1);
-        //ctx_key_press (ctx, 0, "size-changed", 0);
-      }
-      else if (!strcmp (event_type, "keyup"))
-      {
-        char buf[4]={ x, 0 };
-        ctx_key_up (ctx, (int)x, buf, 0);
-      }
-      else if (!strcmp (event_type, "keydown"))
-      {
-        char buf[4]={ x, 0 };
-        ctx_key_down (ctx, (int)x, buf, 0);
-      }
-      else
-      {
-        ctx_key_press (ctx, 0, event, 0);
-      }
-      }
-    }
-  else
-    {
-      float x, y;
-      event = ctx_nct_get_event (ctx, 20, &ix, &iy);
+  CtxIterator *iterator = ctx_current_path (ctx);
+  CtxCommand *command;
 
-      x = (ix - 1.0 + 0.5) / ctxctx->cols * ctx->events.width;
-      y = (iy - 1.0)       / ctxctx->rows * ctx->events.height;
+  while ((command = ctx_iterator_next (iterator)))
+  {
+     int got_coord = 0;
+     switch (command->code)
+     {
+        // XXX missing many curve types
+        case CTX_LINE_TO:
+        case CTX_MOVE_TO:
+          x = command->move_to.x;
+          y = command->move_to.y;
+          got_coord++;
+          break;
+        case CTX_REL_LINE_TO:
+        case CTX_REL_MOVE_TO:
+          x += command->move_to.x;
+          y += command->move_to.y;
+          got_coord++;
+          break;
+        case CTX_CURVE_TO:
+          x = command->curve_to.x;
+          y = command->curve_to.y;
+          got_coord++;
+          break;
+        case CTX_REL_CURVE_TO:
+          x += command->curve_to.x;
+          y += command->curve_to.y;
+          got_coord++;
+          break;
+        case CTX_ARC:
+          minx = ctx_minf (minx, command->arc.x - command->arc.radius);
+          miny = ctx_minf (miny, command->arc.y - command->arc.radius);
+          maxx = ctx_maxf (maxx, command->arc.x + command->arc.radius);
+          maxy = ctx_maxf (maxy, command->arc.y + command->arc.radius);
 
-      if (!strcmp (event, "mouse-press"))
-      {
-        ctx_pointer_press (ctx, x, y, 0, 0);
-        ctxctx->was_down = 1;
-      } else if (!strcmp (event, "mouse-release"))
-      {
-        ctx_pointer_release (ctx, x, y, 0, 0);
-      } else if (!strcmp (event, "mouse-motion"))
-      {
-        //nct_set_cursor_pos (backend->term, ix, iy);
-        //nct_flush (backend->term);
-        if (ctxctx->was_down)
-        {
-          ctx_pointer_release (ctx, x, y, 0, 0);
-          ctxctx->was_down = 0;
-        }
-        ctx_pointer_motion (ctx, x, y, 0, 0);
-      } else if (!strcmp (event, "mouse-drag"))
-      {
-        ctx_pointer_motion (ctx, x, y, 0, 0);
-      } else if (!strcmp (event, "size-changed"))
-      {
-        fprintf (stdout, "\e[H\e[2J\e[?25l");
-        ctxctx->cols = ctx_terminal_cols ();
-        ctxctx->rows = ctx_terminal_rows ();
-        ctxctx->width  = ctx_terminal_width ();
-        ctxctx->height = ctx_terminal_height ();
-        ctx_set_size (ctx, ctxctx->width, ctxctx->height);
+          break;
+        case CTX_RECTANGLE:
+        case CTX_ROUND_RECTANGLE:
+          x = command->rectangle.x;
+          y = command->rectangle.y;
+          minx = ctx_minf (minx, x);
+          miny = ctx_minf (miny, y);
+          maxx = ctx_maxf (maxx, x);
+          maxy = ctx_maxf (maxy, y);
 
-        if (prev_frame_contents)
-           free (prev_frame_contents);
-        prev_frame_contents = NULL;
-        prev_frame_len = 0;
-        ctx_set_dirty (ctx, 1);
-        //ctx_key_press (ctx, 0, "size-changed", 0);
-      }
-      else
-      {
-        if (!strcmp (event, "esc"))
-          ctx_key_press (ctx, 0, "escape", 0);
-        else if (!strcmp (event, "space"))
-          ctx_key_press (ctx, 0, "space", 0);
-        else if (!strcmp (event, "enter")||
-                 !strcmp (event, "return"))
-          ctx_key_press (ctx, 0, "\n", 0);
-        else
-        ctx_key_press (ctx, 0, event, 0);
-      }
+          x += command->rectangle.width;
+          y += command->rectangle.height;
+          got_coord++;
+          break;
+     }
+    if (got_coord)
+    {
+      minx = ctx_minf (minx, x);
+      miny = ctx_minf (miny, y);
+      maxx = ctx_maxf (maxx, x);
+      maxy = ctx_maxf (maxy, y);
     }
+  }
 
-  return 1;
+  if (ex1) *ex1 = minx;
+  if (ey1) *ey1 = miny;
+  if (ex2) *ex2 = maxx;
+  if (ey2) *ey2 = maxy;
 }
 
-int ctx_renderer_is_ctx (Ctx *ctx)
+#else
+void
+ctx_path_extents (Ctx *ctx, float *ex1, float *ey1, float *ex2, float *ey2)
 {
-  if (ctx->renderer &&
-      ctx->renderer->free == (void*)ctx_ctx_free)
-          return 1;
-  return 0;
 }
-
 #endif
 
-#if CTX_TILED
-static inline int
-ctx_tiled_threads_done (CtxTiled *tiled)
+
+static void
+ctx_gstate_push (CtxState *state)
 {
-  int sum = 0;
-  for (int i = 0; i < _ctx_max_threads; i++)
-  {
-     if (tiled->rendered_frame[i] == tiled->render_frame)
-       sum ++;
-  }
-  return sum;
+  if (state->gstate_no + 1 >= CTX_MAX_STATES)
+    { return; }
+  state->gstate_stack[state->gstate_no] = state->gstate;
+  state->gstate_no++;
+  ctx_state_set (state, CTX_new_state, 0.0);
+  state->has_clipped=0;
 }
 
-int _ctx_damage_control = 0;
-
-void ctx_tiled_free (CtxTiled *tiled)
+static void
+ctx_gstate_pop (CtxState *state)
 {
-  tiled->quit = 1;
-  mtx_lock (&tiled->mtx);
-  cnd_broadcast (&tiled->cond);
-  mtx_unlock (&tiled->mtx);
-
-  while (tiled->thread_quit < _ctx_max_threads)
-    usleep (1000);
-
-  if (tiled->pixels)
-  {
-    free (tiled->pixels);
-  tiled->pixels = NULL;
-  for (int i = 0 ; i < _ctx_max_threads; i++)
-  {
-    ctx_free (tiled->host[i]);
-    tiled->host[i]=NULL;
-  }
-
-  ctx_free (tiled->ctx_copy);
-  }
-  // leak?
+  if (state->gstate_no <= 0)
+    { return; }
+  state->gstate = state->gstate_stack[state->gstate_no-1];
+  state->gstate_no--;
 }
-static unsigned char *sdl_icc = NULL;
-static long sdl_icc_length = 0;
 
-inline static void ctx_tiled_flush (CtxTiled *tiled)
+void
+ctx_close_path (Ctx *ctx)
 {
-  if (tiled->shown_frame == tiled->render_frame)
-  {
-    int dirty_tiles = 0;
-    ctx_set_drawlist (tiled->ctx_copy, &tiled->ctx->drawlist.entries[0],
-                                           tiled->ctx->drawlist.count * 9);
-    if (_ctx_enable_hash_cache)
-    {
-      Ctx *hasher = ctx_hasher_new (tiled->width, tiled->height,
-                        CTX_HASH_COLS, CTX_HASH_ROWS);
-      ctx_render_ctx (tiled->ctx_copy, hasher);
+  CTX_PROCESS_VOID (CTX_CLOSE_PATH);
+}
 
-      for (int row = 0; row < CTX_HASH_ROWS; row++)
-        for (int col = 0; col < CTX_HASH_COLS; col++)
-        {
-          uint8_t *new_hash = ctx_hasher_get_hash (hasher, col, row);
-          if (new_hash && memcmp (new_hash, &tiled->hashes[(row * CTX_HASH_COLS + col) *  20], 20))
-          {
-            memcpy (&tiled->hashes[(row * CTX_HASH_COLS +  col)*20], new_hash, 20);
-            tiled->tile_affinity[row * CTX_HASH_COLS + col] = 1;
-            dirty_tiles++;
-          }
-          else
-          {
-            tiled->tile_affinity[row * CTX_HASH_COLS + col] = -1;
-          }
-        }
-      free (((CtxHasher*)(hasher->renderer))->hashes);
-      ctx_free (hasher);
-    }
-    else
-    {
-    for (int row = 0; row < CTX_HASH_ROWS; row++)
-      for (int col = 0; col < CTX_HASH_COLS; col++)
-        {
-          tiled->tile_affinity[row * CTX_HASH_COLS + col] = 1;
-          dirty_tiles++;
-        }
-    }
-    int dirty_no = 0;
-    if (dirty_tiles)
-    for (int row = 0; row < CTX_HASH_ROWS; row++)
-      for (int col = 0; col < CTX_HASH_COLS; col++)
-      {
-        if (tiled->tile_affinity[row * CTX_HASH_COLS + col] != -1)
-        {
-          tiled->tile_affinity[row * CTX_HASH_COLS + col] = dirty_no * (_ctx_max_threads) / dirty_tiles;
-          dirty_no++;
-          if (col > tiled->max_col) tiled->max_col = col;
-          if (col < tiled->min_col) tiled->min_col = col;
-          if (row > tiled->max_row) tiled->max_row = row;
-          if (row < tiled->min_row) tiled->min_row = row;
-        }
-      }
-
-    if (_ctx_damage_control)
-    {
-      for (int i = 0; i < tiled->width * tiled->height; i++)
-      {
-        tiled->pixels[i*4+2]  = (tiled->pixels[i*4+2] + 255)/2;
-      }
-    }
-
-    tiled->render_frame = ++tiled->frame;
-
-#if 0
-
-          //if (tiled->tile_affinity[hno]==no)
-          {
-            int x0 = ((tiled->width)/CTX_HASH_COLS) * 0;
-            int y0 = ((tiled->height)/CTX_HASH_ROWS) * 0;
-            int width = tiled->width / CTX_HASH_COLS;
-            int height = tiled->height / CTX_HASH_ROWS;
-            Ctx *host = tiled->host[0];
-
-            CtxRasterizer *rasterizer = (CtxRasterizer*)host->renderer;
-            int swap_red_green = ((CtxRasterizer*)(host->renderer))->swap_red_green;
-            ctx_rasterizer_init (rasterizer,
-                                 host, tiled->ctx, &host->state,
-                                 &tiled->pixels[tiled->width * 4 * y0 + x0 * 4],
-                                 0, 0, 1, 1,
-                                 tiled->width*4, CTX_FORMAT_BGRA8,
-                                 tiled->antialias);
-            ((CtxRasterizer*)(host->renderer))->swap_red_green = swap_red_green;
-            if (sdl_icc_length)
-              ctx_colorspace (host, CTX_COLOR_SPACE_DEVICE_RGB, sdl_icc, sdl_icc_length);
+int _ctx_is_rasterizer (Ctx *ctx);
 
-            ctx_translate (host, -x0, -y0);
-            ctx_render_ctx (tiled->ctx_copy, host);
-          }
+void
+ctx_get_image_data (Ctx *ctx, int sx, int sy, int sw, int sh,
+                    CtxPixelFormat format, int dst_stride,
+                    uint8_t *dst_data)
+{
+   if (0)
+   {
+   }
+#if CTX_RASTERIZER
+   else if (_ctx_is_rasterizer (ctx))
+   {
+     CtxRasterizer *rasterizer = (CtxRasterizer*)ctx->renderer;
+     if (rasterizer->format->pixel_format == format)
+     {
+       if (dst_stride <= 0) dst_stride = ctx_pixel_format_get_stride (format, sw);
+       int bytes_per_pix = rasterizer->format->bpp/8;
+       int y = 0;
+       for (int v = sy; v < sy + sh; v++, y++)
+       {
+         int x = 0;
+         for (int u = sx; u < sx + sw; u++, x++)
+         {
+            uint8_t* src_buf = (uint8_t*)rasterizer->buf;
+            memcpy (&dst_data[y * dst_stride + x * bytes_per_pix], &src_buf[v * rasterizer->blit_stride + u 
* bytes_per_pix], bytes_per_pix);
+         }
+       }
+       return;
+     }
+   }
+#endif
+#if CTX_FB
+   else if (format == CTX_FORMAT_RGBA8 &&
+                   (
+                   ctx_renderer_is_fb (ctx)
+#if CTX_SDL
+                   || ctx_renderer_is_sdl (ctx)
+#endif
+                   ))
+   {
+     CtxTiled *tiled = (CtxTiled*)ctx->renderer;
+     {
+       if (dst_stride <= 0) dst_stride = ctx_pixel_format_get_stride (format, sw);
+       int bytes_per_pix = 4;
+       int y = 0;
+       for (int v = sy; v < sy + sh; v++, y++)
+       {
+         int x = 0;
+         for (int u = sx; u < sx + sw; u++, x++)
+         {
+            uint8_t* src_buf = (uint8_t*)tiled->pixels;
+            memcpy (&dst_data[y * dst_stride + x * bytes_per_pix], &src_buf[v * tiled->width * bytes_per_pix 
+ u * bytes_per_pix], bytes_per_pix);
+         }
+       }
+       return;
+     }
+   }
 #endif
-
-
-    mtx_lock (&tiled->mtx);
-    cnd_broadcast (&tiled->cond);
-    mtx_unlock (&tiled->mtx);
-  }
 }
 
-static
-void ctx_tiled_render_fun (void **data)
+void
+ctx_put_image_data (Ctx *ctx, int w, int h, int stride, int format,
+                    uint8_t *data,
+                    int ox, int oy,
+                    int dirtyX, int dirtyY,
+                    int dirtyWidth, int dirtyHeight)
 {
-  int      no = (size_t)data[0];
-  CtxTiled *tiled = data[1];
+   char eid[65]="";
+   ctx_save (ctx);
+   ctx_identity (ctx);
+   ctx_define_texture (ctx, NULL, w, h, stride, format, data, eid);
+   if (eid[0])
+   {
+     // XXX set compositor to source
+     ctx_compositing_mode (ctx, CTX_COMPOSITE_COPY);
+     ctx_draw_texture_clipped (ctx, eid, ox, oy, w, h, dirtyX, dirtyY, dirtyWidth, dirtyHeight);
+   }
+   ctx_restore (ctx);
+}
 
-  while (!tiled->quit)
+/* checking if an eid is valid also sets the frame for it
+ */
+static int ctx_eid_valid (Ctx *ctx, const char *eid, int *w, int *h)
+{
+  ctx = ctx->texture_cache;
+  CtxList *to_remove = NULL;
+  int ret = 0;
+  //fprintf (stderr, "{%i}\n", ctx->frame);
+  for (CtxList *l = ctx->eid_db; l; l = l->next)
   {
-    Ctx *host = tiled->host[no];
-
-    mtx_lock (&tiled->mtx);
-    cnd_wait(&tiled->cond, &tiled->mtx);
-    mtx_unlock (&tiled->mtx);
-
-    if (tiled->render_frame != tiled->rendered_frame[no])
+    CtxEidInfo *eid_info = (CtxEidInfo*)l->data;
+    if (ctx->frame - eid_info->frame >= 2)
+            /* XXX XXX XXX this breaks texture caching since
+             *   it is wrong in some cases where more frames
+             *   have passed?
+             */
     {
-      int hno = 0;
-      for (int row = 0; row < CTX_HASH_ROWS; row++)
-        for (int col = 0; col < CTX_HASH_COLS; col++, hno++)
-        {
-          if (tiled->tile_affinity[hno]==no)
-          {
-            int x0 = ((tiled->width)/CTX_HASH_COLS) * col;
-            int y0 = ((tiled->height)/CTX_HASH_ROWS) * row;
-            int width = tiled->width / CTX_HASH_COLS;
-            int height = tiled->height / CTX_HASH_ROWS;
-
-            CtxRasterizer *rasterizer = (CtxRasterizer*)host->renderer;
-#if 1 // merge horizontally adjecant tiles of same affinity into one job
-            while (col + 1 < CTX_HASH_COLS &&
-                   tiled->tile_affinity[hno+1] == no)
-            {
-              width += tiled->width / CTX_HASH_COLS;
-              col++;
-              hno++;
-            }
-#endif
-            int swap_red_green = ((CtxRasterizer*)(host->renderer))->swap_red_green;
-            ctx_rasterizer_init (rasterizer,
-                                 host, tiled->ctx, &host->state,
-                                 &tiled->pixels[tiled->width * 4 * y0 + x0 * 4],
-                                 0, 0, width, height,
-                                 tiled->width*4, CTX_FORMAT_BGRA8,
-                                 tiled->antialias);
-            ((CtxRasterizer*)(host->renderer))->swap_red_green = swap_red_green;
-            if (sdl_icc_length)
-              ctx_colorspace (host, CTX_COLOR_SPACE_DEVICE_RGB, sdl_icc, sdl_icc_length);
-
-            ctx_translate (host, -x0, -y0);
-            ctx_render_ctx (tiled->ctx_copy, host);
-          }
-        }
-      tiled->rendered_frame[no] = tiled->render_frame;
+      ctx_list_prepend (&to_remove, eid_info);
+    }
+    else if (!strcmp (eid_info->eid, eid) &&
+             ctx->frame - eid_info->frame < 2)
+    {
+    //fclose (f);
+      eid_info->frame = ctx->frame;
+      if (w) *w = eid_info->width;
+      if (h) *h = eid_info->height;
+      ret = 1;
     }
   }
-  tiled->thread_quit++; // need atomic?
+  while (to_remove)
+  {
+    CtxEidInfo *eid_info = (CtxEidInfo*)to_remove->data;
+    //FILE  *f  = fopen ("/tmp/l", "a");
+    //fprintf (f, "%i client removing %s\n", getpid(), eid_info->eid);
+    //fclose (f);
+    free (eid_info->eid);
+    free (eid_info);
+    ctx_list_remove (&ctx->eid_db, eid_info);
+    ctx_list_remove (&to_remove, eid_info);
+  }
+  return ret;
 }
 
-#endif
-
-
-#if CTX_EVENTS
-
-#if !__COSMOPOLITAN__
-#include <fcntl.h>
-#include <sys/ioctl.h>
-#include <signal.h>
-#endif
-
-
-#if CTX_FB
-  #include <linux/fb.h>
-  #include <linux/vt.h>
-  #include <linux/kd.h>
-  #include <sys/mman.h>
-  #include <threads.h>
-  #include <libdrm/drm.h>
-  #include <libdrm/drm_mode.h>
+void ctx_texture (Ctx *ctx, const char *eid, float x, float y)
+{
+  int eid_len = strlen (eid);
+  char ascii[41]="";
+  //fprintf (stderr, "tx %s\n", eid);
+  if (eid_len > 50)
+  {
+    CtxSHA1 *sha1 = ctx_sha1_new ();
+    uint8_t hash[20]="";
+    ctx_sha1_process (sha1, (uint8_t*)eid, eid_len);
+    ctx_sha1_done (sha1, hash);
+    ctx_sha1_free (sha1);
+    const char *hex="0123456789abcdef";
+    for (int i = 0; i < 20; i ++)
+    {
+       ascii[i*2]=hex[hash[i]/16];
+       ascii[i*2+1]=hex[hash[i]%16];
+    }
+    ascii[40]=0;
+    eid=ascii;
+  }
 
-typedef struct _EvSource EvSource;
- 
+    //FILE  *f = fopen ("/tmp/l", "a");
+  if (ctx_eid_valid (ctx, eid, 0, 0))
+  {
+    ctx_process_cmd_str_float (ctx, CTX_TEXTURE, eid, x, y);
+    //fprintf (stderr, "setting texture eid %s\n", eid);
+  }
+  else
+  {
+    //fprintf (stderr, "tried setting invalid texture eid %s\n", eid);
+  }
+    //fclose (f);
+}
+int
+_ctx_frame (Ctx *ctx)
+{
+   return ctx->frame;
+}
+int
+_ctx_set_frame (Ctx *ctx, int frame)
+{
+   return ctx->frame = frame;
+}
 
-struct _EvSource
+void ctx_define_texture (Ctx *ctx, const char *eid, int width, int height, int stride, int format, void 
*data, char *ret_eid)
 {
-  void   *priv; /* private storage  */
+  uint8_t hash[20]="";
+  char ascii[41]="";
+  int dst_stride = width;
+  //fprintf (stderr, "df %s\n", eid);
 
-  /* returns non 0 if there is events waiting */
-  int   (*has_event) (EvSource *ev_source);
+  dst_stride = ctx_pixel_format_get_stride ((CtxPixelFormat)format, width);
+  if (stride <= 0)
+    stride = dst_stride;
 
-  /* get an event, the returned event should be freed by the caller  */
-  char *(*get_event) (EvSource *ev_source);
+  int data_len = height * dst_stride;
 
-  /* destroy/unref this instance */
-  void  (*destroy)   (EvSource *ev_source);
+  if (eid == NULL)
+  {
+    CtxSHA1 *sha1 = ctx_sha1_new ();
 
-  /* get the underlying fd, useful for using select on  */
-  int   (*get_fd)    (EvSource *ev_source);
+    {
+      uint8_t *src = (uint8_t*)data;
+      for (int y = 0; y < height; y++)
+      {
+         ctx_sha1_process (sha1, src, dst_stride);
+         src += stride;
+      }
+    }
+    ctx_sha1_done (sha1, hash);
+    ctx_sha1_free (sha1);
+    const char *hex="0123456789abcdef";
+    for (int i = 0; i < 20; i ++)
+    {
+       ascii[i*2]=hex[hash[i]/16];
+       ascii[i*2+1]=hex[hash[i]%16];
+    }
+    ascii[40]=0;
+    eid = ascii;
+  }
 
+  int eid_len = strlen (eid);
 
-  void  (*set_coord) (EvSource *ev_source, double x, double y);
-  /* set_coord is needed to warp relative cursors into normalized range,
-   * like normal mice/trackpads/nipples - to obey edges and more.
-   */
+  if (eid_len > 50)
+  {
+    CtxSHA1 *sha1 = ctx_sha1_new ();
+    uint8_t hash[20]="";
+    ctx_sha1_process (sha1, (uint8_t*)eid, eid_len);
+    ctx_sha1_done (sha1, hash);
+    ctx_sha1_free (sha1);
+    const char *hex="0123456789abcdef";
+    for (int i = 0; i < 20; i ++)
+    {
+       ascii[i*2]=hex[hash[i]/16];
+       ascii[i*2+1]=hex[hash[i]%16];
+    }
+    ascii[40]=0;
+    eid = ascii;
+    eid_len = 40;
+  }
 
-  /* if this returns non-0 select can be used for non-blocking.. */
-};
+  // we now have eid
 
+  if (ctx_eid_valid (ctx, eid, 0, 0))
+  {
+    ctx_texture (ctx, eid, 0.0, 0.0);
+  }
+  else
 
-typedef struct _CtxFb CtxFb;
-struct _CtxFb
-{
-   CtxTiled tiled;
-#if 0
-   void (*render) (void *fb, CtxCommand *command);
-   void (*reset)  (void *fb);
-   void (*flush)  (void *fb);
-   char *(*get_clipboard) (void *ctxctx);
-   void (*set_clipboard) (void *ctxctx, const char *text);
-   void (*free)   (void *fb);
-   Ctx          *ctx;
-   int           width;
-   int           height;
-   int           cols; // unused
-   int           rows; // unused
-   int           was_down;
-   uint8_t      *pixels;
-   Ctx          *ctx_copy;
-   Ctx          *host[CTX_MAX_THREADS];
-   CtxAntialias  antialias;
-   int           quit;
-   _Atomic int   thread_quit;
-   int           shown_frame;
-   int           render_frame;
-   int           rendered_frame[CTX_MAX_THREADS];
-   int           frame;
-   int           min_col; // hasher cols and rows
-   int           min_row;
-   int           max_col;
-   int           max_row;
-   uint8_t       hashes[CTX_HASH_ROWS * CTX_HASH_COLS *  20];
-   int8_t        tile_affinity[CTX_HASH_ROWS * CTX_HASH_COLS]; // which render thread no is
-                                                           // responsible for a tile
-                                                           //
+  {
+    CtxEntry *commands;
+    int command_size = 1 + (data_len+1+1)/9 + 1 + (eid_len+1+1)/9 + 1 +   8;
+    if (ctx->renderer && ctx->renderer->process)
+    {
+       commands = (CtxEntry*)calloc (sizeof (CtxEntry), command_size);
+    }
+    else
+    {
+       commands = NULL;
+       ctx_drawlist_resize (&ctx->drawlist, ctx->drawlist.count + command_size);
+       commands = &(ctx->drawlist.entries[ctx->drawlist.count]);
+       memset (commands, 0, sizeof (CtxEntry) * command_size);
+    }
+    /* bottleneck,  we can avoid copying sometimes - and even when copying
+     * we should cut this down to one copy, direct to the drawlist.
+     *
+     */
+    commands[0] = ctx_u32 (CTX_DEFINE_TEXTURE, width, height);
+    commands[1].data.u16[0] = format;
 
+    int pos = 2;
 
-   int           pointer_down[3];
-#endif
-   int           key_balance;
-   int           key_repeat;
-   int           lctrl;
-   int           lalt;
-   int           rctrl;
+    commands[pos].code        = CTX_DATA;
+    commands[pos].data.u32[0] = eid_len;
+    commands[pos].data.u32[1] = (eid_len+1+1)/9 + 1;
+    memcpy ((char *) &commands[pos+1].data.u8[0], eid, eid_len);
+    ((char *) &commands[pos+1].data.u8[0])[eid_len]=0;
 
-   uint8_t      *fb;
+    pos = 2 + 1 + ctx_conts_for_entry (&commands[2]);
+    commands[pos].code        = CTX_DATA;
+    commands[pos].data.u32[0] = data_len;
+    commands[pos].data.u32[1] = (data_len+1+1)/9 + 1;
+    {
+      uint8_t *src = (uint8_t*)data;
+      uint8_t *dst = &commands[pos+1].data.u8[0];
+      for (int y = 0; y < height; y++)
+      {
+         memcpy (dst, src, dst_stride);
+         src += stride;
+         dst += dst_stride;
+      }
+    }
+    ((char *) &commands[pos+1].data.u8[0])[data_len]=0;
 
-   int          fb_fd;
-   char        *fb_path;
-   int          fb_bits;
-   int          fb_bpp;
-   int          fb_mapped_size;
-   struct       fb_var_screeninfo vinfo;
-   struct       fb_fix_screeninfo finfo;
-   int          vt;
-   int          tty;
-   int          vt_active;
-   EvSource    *evsource[4];
-   int          evsource_count;
-   int          is_drm;
-   cnd_t        cond;
-   mtx_t        mtx;
-   struct drm_mode_crtc crtc;
-};
+    if (ctx->renderer && ctx->renderer->process)
+    {
+      ctx_process (ctx, commands);
+      free (commands);
+    }
+    else
+    {
+       ctx->drawlist.count += ctx_conts_for_entry (commands) + 1;
+    }
 
-static char *ctx_fb_clipboard = NULL;
-static void ctx_fb_set_clipboard (CtxFb *fb, const char *text)
-{
-  if (ctx_fb_clipboard)
-    free (ctx_fb_clipboard);
-  ctx_fb_clipboard = NULL;
-  if (text)
+    CtxEidInfo *eid_info = (CtxEidInfo*)calloc (sizeof (CtxEidInfo), 1);
+    eid_info->eid        = strdup (eid);
+    eid_info->width      = width;
+    eid_info->height     = height;
+    eid_info->frame      = ctx->texture_cache->frame;
+    //fprintf (stderr, "%i\n", eid_info->frame);
+    ctx_list_prepend (&ctx->texture_cache->eid_db, eid_info);
+  }
+
+  if (ret_eid)
   {
-    ctx_fb_clipboard = strdup (text);
+    strcpy (ret_eid, eid);
+    ret_eid[64]=0;
   }
 }
 
-static char *ctx_fb_get_clipboard (CtxFb *sdl)
+void
+ctx_texture_load (Ctx *ctx, const char *path, int *tw, int *th, char *reid)
 {
-  if (ctx_fb_clipboard) return strdup (ctx_fb_clipboard);
-  return strdup ("");
-}
+  const char *eid = path;
+  char ascii[41]="";
+  int eid_len = strlen (eid);
+  if (eid_len > 50)
+  {
+    CtxSHA1 *sha1 = ctx_sha1_new ();
+    uint8_t hash[20]="";
+    ctx_sha1_process (sha1, (uint8_t*)eid, eid_len);
+    ctx_sha1_done (sha1, hash);
+    ctx_sha1_free (sha1);
+    const char *hex="0123456789abcdef";
+    for (int i = 0; i < 20; i ++)
+    {
+       ascii[i*2]=hex[hash[i]/16];
+       ascii[i*2+1]=hex[hash[i]%16];
+    }
+    ascii[40]=0;
+    eid = ascii;
+  }
 
-#if UINTPTR_MAX == 0xffFFffFF
-  #define fbdrmuint_t uint32_t
-#elif UINTPTR_MAX == 0xffFFffFFffFFffFF
-  #define fbdrmuint_t uint64_t
-#endif
-
-void *ctx_fbdrm_new (CtxFb *fb, int *width, int *height)
-{
-   int got_master = 0;
-   fb->fb_fd = open("/dev/dri/card0", O_RDWR | O_CLOEXEC);
-   if (!fb->fb_fd)
-     return NULL;
-   static fbdrmuint_t res_conn_buf[20]={0}; // this is static since its contents
-                                         // are used by the flip callback
-   fbdrmuint_t res_fb_buf[20]={0};
-   fbdrmuint_t res_crtc_buf[20]={0};
-   fbdrmuint_t res_enc_buf[20]={0};
-   struct   drm_mode_card_res res={0};
-
-   if (ioctl(fb->fb_fd, DRM_IOCTL_SET_MASTER, 0))
-     goto cleanup;
-   got_master = 1;
-
-   if (ioctl(fb->fb_fd, DRM_IOCTL_MODE_GETRESOURCES, &res))
-     goto cleanup;
-   res.fb_id_ptr=(fbdrmuint_t)res_fb_buf;
-   res.crtc_id_ptr=(fbdrmuint_t)res_crtc_buf;
-   res.connector_id_ptr=(fbdrmuint_t)res_conn_buf;
-   res.encoder_id_ptr=(fbdrmuint_t)res_enc_buf;
-   if(ioctl(fb->fb_fd, DRM_IOCTL_MODE_GETRESOURCES, &res))
-      goto cleanup;
-
-
-   unsigned int i;
-   for (i=0;i<res.count_connectors;i++)
-   {
-     struct drm_mode_modeinfo conn_mode_buf[20]={0};
-     fbdrmuint_t conn_prop_buf[20]={0},
-                     conn_propval_buf[20]={0},
-                     conn_enc_buf[20]={0};
+  if (ctx_eid_valid (ctx, eid , tw, th))
+  {
+     if (reid)
+     {
+       strcpy (reid, eid);
+     }
+     return;
+  }
 
-     struct drm_mode_get_connector conn={0};
+#ifdef STBI_INCLUDE_STB_IMAGE_H
+    CtxPixelFormat pixel_format = CTX_FORMAT_RGBA8;
+  int w, h, components;
+  unsigned char *pixels = NULL;
 
-     conn.connector_id=res_conn_buf[i];
+  if (!strncmp (path, "file://", 7))
+  {
+    pixels = stbi_load (path + 7, &w, &h, &components, 0);
+  }
+  else
+  {
+    unsigned char *data = NULL;
+    long length = 0;
+    ctx_get_contents (path, &data, &length);
+    if (data)
+    {
+       pixels = stbi_load_from_memory (data, length, &w, &h, &components, 0);
+       free (data);
+    }
+  }
 
-     if (ioctl(fb->fb_fd, DRM_IOCTL_MODE_GETCONNECTOR, &conn))
-       goto cleanup;
+  if (pixels)
+  {
+    switch (components)
+    {
+      case 1: pixel_format = CTX_FORMAT_GRAY8;  break;
+      case 2: pixel_format = CTX_FORMAT_GRAYA8; break;
+      case 3: pixel_format = CTX_FORMAT_RGB8;   break;
+      case 4: pixel_format = CTX_FORMAT_RGBA8;  break;
+    }
+    if (tw) *tw = w;
+    if (th) *th = h;
+    ctx_define_texture (ctx, eid, w, h, w * components, pixel_format, pixels, 
+                             reid);
+    free (pixels);
+  }
+  else
+  {
+    fprintf (stderr, "texture loading problem for %s\n", path);
+  }
+#endif
+}
 
-     conn.modes_ptr=(fbdrmuint_t)conn_mode_buf;
-     conn.props_ptr=(fbdrmuint_t)conn_prop_buf;
-     conn.prop_values_ptr=(fbdrmuint_t)conn_propval_buf;
-     conn.encoders_ptr=(fbdrmuint_t)conn_enc_buf;
+void
+ctx_draw_texture_clipped  (Ctx *ctx, const char *eid,
+                           float x, float y,
+                           float width, float height,
+                           float clip_x, float clip_y,
+                           float clip_width, float clip_height)
+{
+  int tex_width  = 0;
+  int tex_height = 0;
+  if (ctx_eid_valid (ctx, eid , &tex_width, &tex_height))
+  {
+    if (width > 0.0 && height > 0.0)
+    {
+      ctx_save (ctx);
+#if 0
+      if (clip_width > 0.0f)
+      {
+        ctx_rectangle (ctx, clip_x, clip_y, clip_width, clip_height);
+        ctx_clip (ctx);
+      }
+#endif
+      ctx_rectangle (ctx, x, y, width, height);
+      if (clip_width > 0.0f)
+      {
+        ctx_translate (ctx, -clip_x, -clip_y);
+        ctx_scale (ctx, width/clip_width, height/clip_height);
+      }
+      else
+      {
+        ctx_scale (ctx, width/tex_width, height/tex_height);
+      }
+      //ctx_texture (ctx, eid, x / (width/tex_width), y / (height/tex_height));
+      ctx_texture (ctx, eid, x, y);// / (width/tex_width), y / (height/tex_height));
+      ctx_fill (ctx);
+      ctx_restore (ctx);
+    }
+  }
+}
 
-     if (ioctl(fb->fb_fd, DRM_IOCTL_MODE_GETCONNECTOR, &conn))
-       goto cleanup;
+void ctx_draw_texture (Ctx *ctx, const char *eid, float x, float y, float w, float h)
+{
+  ctx_draw_texture_clipped (ctx, eid, x, y, w, h, 0,0,0,0);
+}
 
-     //Check if the connector is OK to use (connected to something)
-     if (conn.count_encoders<1 || conn.count_modes<1 || !conn.encoder_id || !conn.connection)
-       continue;
+void ctx_draw_image_clipped (Ctx *ctx, const char *path, float x, float y, float w, float h, float sx, float 
sy, float swidth, float sheight)
+{
+  char reteid[65];
+  int width, height;
+  ctx_texture_load (ctx, path, &width, &height, reteid);
+  if (reteid[0])
+  {
+    ctx_draw_texture_clipped (ctx, reteid, x, y, w, h, sx, sy, swidth, sheight);
+  }
+}
 
-//------------------------------------------------------------------------------
-//Creating a dumb buffer
-//------------------------------------------------------------------------------
-     struct drm_mode_create_dumb create_dumb={0};
-     struct drm_mode_map_dumb    map_dumb={0};
-     struct drm_mode_fb_cmd      cmd_dumb={0};
-     create_dumb.width  = conn_mode_buf[0].hdisplay;
-     create_dumb.height = conn_mode_buf[0].vdisplay;
-     create_dumb.bpp   = 32;
-     create_dumb.flags = 0;
-     create_dumb.pitch = 0;
-     create_dumb.size  = 0;
-     create_dumb.handle = 0;
-     if (ioctl(fb->fb_fd, DRM_IOCTL_MODE_CREATE_DUMB, &create_dumb) ||
-         !create_dumb.handle)
-       goto cleanup;
+void
+ctx_draw_image (Ctx *ctx, const char *path, float x, float y, float w, float h)
+{
+  ctx_draw_image_clipped (ctx, path, x, y, w, h, 0,0,0,0);
+}
 
-     cmd_dumb.width =create_dumb.width;
-     cmd_dumb.height=create_dumb.height;
-     cmd_dumb.bpp   =create_dumb.bpp;
-     cmd_dumb.pitch =create_dumb.pitch;
-     cmd_dumb.depth =24;
-     cmd_dumb.handle=create_dumb.handle;
-     if (ioctl(fb->fb_fd,DRM_IOCTL_MODE_ADDFB,&cmd_dumb))
-       goto cleanup;
+void
+ctx_set_pixel_u8 (Ctx *ctx, uint16_t x, uint16_t y, uint8_t r, uint8_t g, uint8_t b, uint8_t a)
+{
+  CtxEntry command = ctx_u8 (CTX_SET_PIXEL, r, g, b, a, 0, 0, 0, 0);
+  command.data.u16[2]=x;
+  command.data.u16[3]=y;
+  ctx_process (ctx, &command);
+}
 
-     map_dumb.handle=create_dumb.handle;
-     if (ioctl(fb->fb_fd,DRM_IOCTL_MODE_MAP_DUMB,&map_dumb))
-       goto cleanup;
+void
+ctx_linear_gradient (Ctx *ctx, float x0, float y0, float x1, float y1)
+{
+  CtxEntry command[2]=
+  {
+    ctx_f (CTX_LINEAR_GRADIENT, x0, y0),
+    ctx_f (CTX_CONT,            x1, y1)
+  };
+  ctx_process (ctx, command);
+}
 
-     void *base = mmap(0, create_dumb.size, PROT_READ | PROT_WRITE, MAP_SHARED,
-                       fb->fb_fd, map_dumb.offset);
-     if (!base)
-     {
-       goto cleanup;
-     }
-     *width  = create_dumb.width;
-     *height = create_dumb.height;
+void
+ctx_radial_gradient (Ctx *ctx, float x0, float y0, float r0, float x1, float y1, float r1)
+{
+  CtxEntry command[3]=
+  {
+    ctx_f (CTX_RADIAL_GRADIENT, x0, y0),
+    ctx_f (CTX_CONT,            r0, x1),
+    ctx_f (CTX_CONT,            y1, r1)
+  };
+  ctx_process (ctx, command);
+}
 
-     struct drm_mode_get_encoder enc={0};
-     enc.encoder_id=conn.encoder_id;
-     if (ioctl(fb->fb_fd, DRM_IOCTL_MODE_GETENCODER, &enc))
-        goto cleanup;
 
-     fb->crtc.crtc_id=enc.crtc_id;
-     if (ioctl(fb->fb_fd, DRM_IOCTL_MODE_GETCRTC, &fb->crtc))
-        goto cleanup;
+void ctx_preserve (Ctx *ctx)
+{
+  CTX_PROCESS_VOID (CTX_PRESERVE);
+}
 
-     fb->crtc.fb_id=cmd_dumb.fb_id;
-     fb->crtc.set_connectors_ptr=(fbdrmuint_t)&res_conn_buf[i];
-     fb->crtc.count_connectors=1;
-     fb->crtc.mode=conn_mode_buf[0];
-     fb->crtc.mode_valid=1;
-     return base;
-   }
-cleanup:
-   if (got_master)
-     ioctl(fb->fb_fd, DRM_IOCTL_DROP_MASTER, 0);
-   fb->fb_fd = 0;
-   return NULL;
+void ctx_fill (Ctx *ctx)
+{
+  CTX_PROCESS_VOID (CTX_FILL);
 }
 
-void ctx_fbdrm_flip (CtxFb *fb)
+void ctx_stroke (Ctx *ctx)
 {
-  if (!fb->fb_fd)
-    return;
-  ioctl(fb->fb_fd, DRM_IOCTL_MODE_SETCRTC, &fb->crtc);
+  CTX_PROCESS_VOID (CTX_STROKE);
 }
 
-void ctx_fbdrm_close (CtxFb *fb)
+
+static void ctx_empty (Ctx *ctx)
 {
-  if (!fb->fb_fd)
-    return;
-  ioctl(fb->fb_fd, DRM_IOCTL_DROP_MASTER, 0);
-  close (fb->fb_fd);
-  fb->fb_fd = 0;
+#if CTX_RASTERIZER
+  if (ctx->renderer == NULL)
+#endif
+    {
+      ctx->drawlist.count = 0;
+      ctx->drawlist.bitpack_pos = 0;
+    }
 }
 
-static void ctx_fb_flip (CtxFb *fb)
+void _ctx_set_store_clear (Ctx *ctx)
 {
-  if (fb->is_drm)
-    ctx_fbdrm_flip (fb);
-  else
-    ioctl (fb->fb_fd, FBIOPAN_DISPLAY, &fb->vinfo);
+  ctx->transformation |= CTX_TRANSFORMATION_STORE_CLEAR;
 }
 
-inline static uint32_t
-ctx_swap_red_green2 (uint32_t orig)
+#if CTX_EVENTS
+static void
+ctx_event_free (void *event, void *user_data)
 {
-  uint32_t  green_alpha = (orig & 0xff00ff00);
-  uint32_t  red_blue    = (orig & 0x00ff00ff);
-  uint32_t  red         = red_blue << 16;
-  uint32_t  blue        = red_blue >> 16;
-  return green_alpha | red | blue;
+  free (event);
 }
 
-static int       ctx_fb_cursor_drawn   = 0;
-static int       ctx_fb_cursor_drawn_x = 0;
-static int       ctx_fb_cursor_drawn_y = 0;
-static CtxCursor ctx_fb_cursor_drawn_shape = 0;
+static void
+ctx_collect_events (CtxEvent *event, void *data, void *data2)
+{
+  Ctx *ctx = (Ctx*)data;
+  CtxEvent *copy;
+  if (event->type == CTX_KEY_PRESS && !strcmp (event->string, "idle"))
+    return;
+  copy = (CtxEvent*)malloc (sizeof (CtxEvent));
+  *copy = *event;
+  if (copy->string)
+    copy->string = strdup (event->string);
+  ctx_list_append_full (&ctx->events.events, copy, ctx_event_free, NULL);
+}
+#endif
 
+#if CTX_EVENTS
+static void _ctx_bindings_key_press (CtxEvent *event, void *data1, void *data2);
+#endif
 
-#define CTX_FB_HIDE_CURSOR_FRAMES 200
+void ctx_reset (Ctx *ctx)
+{
+        /* we do the callback reset first - maybe we need two cbs,
+         * one for before and one after default impl?
+         *
+         * tiled fb and sdl needs to sync
+         */
+  if (ctx->renderer && ctx->renderer->reset)
+    ctx->renderer->reset (ctx->renderer);
 
-static int ctx_fb_cursor_same_pos = CTX_FB_HIDE_CURSOR_FRAMES;
+  //CTX_PROCESS_VOID (CTX_RESET);
+  //if (ctx->transformation & CTX_TRANSFORMATION_STORE_CLEAR)
+  //  { return; }
+  ctx_empty (ctx);
+  ctx_state_init (&ctx->state);
+#if CTX_EVENTS
+  ctx_list_free (&ctx->events.items);
+  ctx->events.last_item = NULL;
 
-static inline int ctx_is_in_cursor (int x, int y, int size, CtxCursor shape)
-{
-  switch (shape)
+  if (ctx->events.ctx_get_event_enabled)
   {
-    case CTX_CURSOR_ARROW:
-      if (x > ((size * 4)-y*4)) return 0;
-      if (x < y && x > y / 16)
-        return 1;
-      return 0;
+    ctx_clear_bindings (ctx);
+    ctx_listen_full (ctx, 0,0,0,0,
+                     CTX_KEY_PRESS, _ctx_bindings_key_press, ctx, ctx,
+                     NULL, NULL);
 
-    case CTX_CURSOR_RESIZE_SE:
-    case CTX_CURSOR_RESIZE_NW:
-    case CTX_CURSOR_RESIZE_SW:
-    case CTX_CURSOR_RESIZE_NE:
-      {
-        float theta = -45.0/180 * M_PI;
-        float cos_theta;
-        float sin_theta;
+    ctx_listen_full (ctx, 0,0,0,0,
+                     CTX_KEY_UP, ctx_collect_events, ctx, ctx,
+                     NULL, NULL);
+    ctx_listen_full (ctx, 0,0,0,0,
+                     CTX_KEY_DOWN, ctx_collect_events, ctx, ctx,
+                     NULL, NULL);
 
-        if ((shape == CTX_CURSOR_RESIZE_SW) ||
-            (shape == CTX_CURSOR_RESIZE_NE))
-        {
-          theta = -theta;
-          cos_theta = cos (theta);
-          sin_theta = sin (theta);
-        }
-        else
-        {
-          cos_theta = cos (theta);
-          sin_theta = sin (theta);
-        }
-        int rot_x = x * cos_theta - y * sin_theta;
-        int rot_y = y * cos_theta + x * sin_theta;
-        x = rot_x;
-        y = rot_y;
-      }
-      /*FALLTHROUGH*/
-    case CTX_CURSOR_RESIZE_W:
-    case CTX_CURSOR_RESIZE_E:
-    case CTX_CURSOR_RESIZE_ALL:
-      if (abs (x) < size/2 && abs (y) < size/2)
-      {
-        if (abs(y) < size/10)
-        {
-          return 1;
-        }
-      }
-      if ((abs (x) - size/ (shape == CTX_CURSOR_RESIZE_ALL?2:2.7)) >= 0)
-      {
-        if (abs(y) < (size/2.8)-(abs(x) - (size/2)))
-          return 1;
-      }
-      if (shape != CTX_CURSOR_RESIZE_ALL)
-        break;
-      /* FALLTHROUGH */
-    case CTX_CURSOR_RESIZE_S:
-    case CTX_CURSOR_RESIZE_N:
-      if (abs (y) < size/2 && abs (x) < size/2)
-      {
-        if (abs(x) < size/10)
-        {
-          return 1;
-        }
-      }
-      if ((abs (y) - size/ (shape == CTX_CURSOR_RESIZE_ALL?2:2.7)) >= 0)
-      {
-        if (abs(x) < (size/2.8)-(abs(y) - (size/2)))
-          return 1;
-      }
-      break;
-#if 0
-    case CTX_CURSOR_RESIZE_ALL:
-      if (abs (x) < size/2 && abs (y) < size/2)
-      {
-        if (abs (x) < size/10 || abs(y) < size/10)
-          return 1;
-      }
-      break;
-#endif
-    default:
-      return (x ^ y) & 1;
+    ctx_listen_full (ctx, 0, 0, ctx->events.width, ctx->events.height,
+                     (CtxEventType)(CTX_PRESS|CTX_RELEASE|CTX_MOTION),
+                     ctx_collect_events, ctx, ctx,
+                     NULL, NULL);
   }
-  return 0;
+#endif
 }
 
-static void ctx_fb_undraw_cursor (CtxFb *fb)
+void ctx_begin_path (Ctx *ctx)
 {
-    CtxTiled *tiled = (void*)fb;
-    int cursor_size = ctx_height (tiled->ctx) / 28;
+  CTX_PROCESS_VOID (CTX_BEGIN_PATH);
+}
 
-    if (ctx_fb_cursor_drawn)
-    {
-      int no = 0;
-      int startx = -cursor_size;
-      int starty = -cursor_size;
-      if (ctx_fb_cursor_drawn_shape == CTX_CURSOR_ARROW)
-        startx = starty = 0;
+void ctx_clip (Ctx *ctx)
+{
+  CTX_PROCESS_VOID (CTX_CLIP);
+}
 
-      for (int y = starty; y < cursor_size; y++)
-      for (int x = startx; x < cursor_size; x++, no+=4)
-      {
-        if (x + ctx_fb_cursor_drawn_x < tiled->width && y + ctx_fb_cursor_drawn_y < tiled->height)
-        {
-          if (ctx_is_in_cursor (x, y, cursor_size, ctx_fb_cursor_drawn_shape))
-          {
-            int o = ((ctx_fb_cursor_drawn_y + y) * tiled->width + (ctx_fb_cursor_drawn_x + x)) * 4;
-            fb->fb[o+0]^=0x88;
-            fb->fb[o+1]^=0x88;
-            fb->fb[o+2]^=0x88;
-          }
-        }
-      }
+void
+ctx_set (Ctx *ctx, uint64_t key_hash, const char *string, int len);
 
-    ctx_fb_cursor_drawn = 0;
-    }
+void ctx_save (Ctx *ctx)
+{
+  CTX_PROCESS_VOID (CTX_SAVE);
 }
-
-static void ctx_fb_draw_cursor (CtxFb *fb)
+void ctx_restore (Ctx *ctx)
 {
-    CtxTiled *tiled = (void*)fb;
-    int cursor_x    = ctx_pointer_x (tiled->ctx);
-    int cursor_y    = ctx_pointer_y (tiled->ctx);
-    int cursor_size = ctx_height (tiled->ctx) / 28;
-    CtxCursor cursor_shape = tiled->ctx->cursor;
-    int no = 0;
+  CTX_PROCESS_VOID (CTX_RESTORE);
+}
 
-    if (cursor_x == ctx_fb_cursor_drawn_x &&
-        cursor_y == ctx_fb_cursor_drawn_y &&
-        cursor_shape == ctx_fb_cursor_drawn_shape)
-      ctx_fb_cursor_same_pos ++;
-    else
-      ctx_fb_cursor_same_pos = 0;
+void ctx_start_group (Ctx *ctx)
+{
+  CTX_PROCESS_VOID (CTX_START_GROUP);
+}
 
-    if (ctx_fb_cursor_same_pos >= CTX_FB_HIDE_CURSOR_FRAMES)
-    {
-      if (ctx_fb_cursor_drawn)
-        ctx_fb_undraw_cursor (fb);
-      return;
-    }
+void ctx_end_group (Ctx *ctx)
+{
+  CTX_PROCESS_VOID (CTX_END_GROUP);
+}
 
-    /* no need to flicker when stationary, motion flicker can also be removed
-     * by combining the previous and next position masks when a motion has
-     * occured..
-     */
-    if (ctx_fb_cursor_same_pos && ctx_fb_cursor_drawn)
-      return;
+void ctx_line_width (Ctx *ctx, float x)
+{
+  if (ctx->state.gstate.line_width != x)
+    CTX_PROCESS_F1 (CTX_LINE_WIDTH, x);
+}
 
-    ctx_fb_undraw_cursor (fb);
+float ctx_get_miter_limit (Ctx *ctx)
+{
+  return ctx->state.gstate.miter_limit;
+}
 
-    no = 0;
+float ctx_get_line_dash_offset (Ctx *ctx)
+{
+  return ctx->state.gstate.line_dash_offset;
+}
 
-    int startx = -cursor_size;
-    int starty = -cursor_size;
+void ctx_line_dash_offset (Ctx *ctx, float x)
+{
+  if (ctx->state.gstate.line_dash_offset != x)
+    CTX_PROCESS_F1 (CTX_LINE_DASH_OFFSET, x);
+}
 
-    if (cursor_shape == CTX_CURSOR_ARROW)
-      startx = starty = 0;
-
-    for (int y = starty; y < cursor_size; y++)
-      for (int x = startx; x < cursor_size; x++, no+=4)
-      {
-        if (x + cursor_x < tiled->width && y + cursor_y < tiled->height)
-        {
-          if (ctx_is_in_cursor (x, y, cursor_size, cursor_shape))
-          {
-            int o = ((cursor_y + y) * tiled->width + (cursor_x + x)) * 4;
-            fb->fb[o+0]^=0x88;
-            fb->fb[o+1]^=0x88;
-            fb->fb[o+2]^=0x88;
-          }
-        }
-      }
-    ctx_fb_cursor_drawn = 1;
-    ctx_fb_cursor_drawn_x = cursor_x;
-    ctx_fb_cursor_drawn_y = cursor_y;
-    ctx_fb_cursor_drawn_shape = cursor_shape;
+int ctx_get_image_smoothing (Ctx *ctx)
+{
+  return ctx->state.gstate.image_smoothing;
 }
 
-static void ctx_fb_show_frame (CtxFb *fb, int block)
+void ctx_image_smoothing (Ctx *ctx, int enabled)
 {
-  CtxTiled *tiled = (void*)fb;
-  if (tiled->shown_frame == tiled->render_frame)
-  {
-    if (block == 0) // consume event call
-    {
-      ctx_fb_draw_cursor (fb);
-      ctx_fb_flip (fb);
-    }
-    return;
-  }
+  if (ctx_get_image_smoothing (ctx) != enabled)
+    CTX_PROCESS_U8 (CTX_IMAGE_SMOOTHING, enabled);
+}
 
-  if (block)
-  {
-    int count = 0;
-    while (ctx_tiled_threads_done (tiled) != _ctx_max_threads)
-    {
-      usleep (500);
-      count ++;
-      if (count > 2000)
-      {
-        tiled->shown_frame = tiled->render_frame;
-        return;
-      }
-    }
-  }
-  else
-  {
-    if (ctx_tiled_threads_done (tiled) != _ctx_max_threads)
-      return;
-  }
 
-    if (fb->vt_active)
-    {
-       int pre_skip = tiled->min_row * tiled->height/CTX_HASH_ROWS * tiled->width;
-       int post_skip = (CTX_HASH_ROWS-tiled->max_row-1) * tiled->height/CTX_HASH_ROWS * tiled->width;
+void ctx_line_dash (Ctx *ctx, float *dashes, int count)
+{
+  ctx_process_cmd_str_with_len (ctx, CTX_LINE_DASH, (char*)(dashes), count, 0, count * 4);
+}
 
-       int rows = ((tiled->width * tiled->height) - pre_skip - post_skip)/tiled->width;
+void ctx_shadow_blur (Ctx *ctx, float x)
+{
+#if CTX_ENABLE_SHADOW_BLUR
+  if (ctx->state.gstate.shadow_blur != x)
+#endif
+    CTX_PROCESS_F1 (CTX_SHADOW_BLUR, x);
+}
 
-       int col_pre_skip = tiled->min_col * tiled->width/CTX_HASH_COLS;
-       int col_post_skip = (CTX_HASH_COLS-tiled->max_col-1) * tiled->width/CTX_HASH_COLS;
-       if (_ctx_damage_control)
-       {
-         pre_skip = post_skip = col_pre_skip = col_post_skip = 0;
-       }
+void ctx_shadow_rgba (Ctx *ctx, float r, float g, float b, float a)
+{
+  CtxEntry command[3]=
+  {
+    ctx_f (CTX_SHADOW_COLOR, CTX_RGBA, r),
+    ctx_f (CTX_CONT, g, b),
+    ctx_f (CTX_CONT, a, 0)
+  };
+  ctx_process (ctx, command);
+}
 
-       if (pre_skip < 0) pre_skip = 0;
-       if (post_skip < 0) post_skip = 0;
+void ctx_shadow_offset_x (Ctx *ctx, float x)
+{
+#if CTX_ENABLE_SHADOW_BLUR
+  if (ctx->state.gstate.shadow_offset_x != x)
+#endif
+    CTX_PROCESS_F1 (CTX_SHADOW_OFFSET_X, x);
+}
 
-     __u32 dummy = 0;
+void ctx_shadow_offset_y (Ctx *ctx, float x)
+{
+#if CTX_ENABLE_SHADOW_BLUR
+  if (ctx->state.gstate.shadow_offset_y != x)
+#endif
+    CTX_PROCESS_F1 (CTX_SHADOW_OFFSET_Y, x);
+}
 
-       if (tiled->min_row == 100){
-          pre_skip = 0;
-          post_skip = 0;
-          // not when drm ?
-          ioctl (fb->fb_fd, FBIO_WAITFORVSYNC, &dummy);
-          ctx_fb_undraw_cursor (fb);
-       }
-       else
-       {
+void
+ctx_global_alpha (Ctx *ctx, float global_alpha)
+{
+  if (ctx->state.gstate.global_alpha_f != global_alpha)
+    CTX_PROCESS_F1 (CTX_GLOBAL_ALPHA, global_alpha);
+}
 
-      tiled->min_row = 100;
-      tiled->max_row = 0;
-      tiled->min_col = 100;
-      tiled->max_col = 0;
+float
+ctx_get_global_alpha (Ctx *ctx)
+{
+  return ctx->state.gstate.global_alpha_f;
+}
 
-     // not when drm ?
-     ioctl (fb->fb_fd, FBIO_WAITFORVSYNC, &dummy);
-     ctx_fb_undraw_cursor (fb);
-     switch (fb->fb_bits)
-     {
-       case 32:
-#if 1
-         {
-           uint8_t *dst = fb->fb + pre_skip * 4;
-           uint8_t *src = tiled->pixels + pre_skip * 4;
-           int pre = col_pre_skip * 4;
-           int post = col_post_skip * 4;
-           int core = tiled->width * 4 - pre - post;
-           for (int i = 0; i < rows; i++)
-           {
-             dst  += pre;
-             src  += pre;
-             memcpy (dst, src, core);
-             src  += core;
-             dst  += core;
-             dst  += post;
-             src  += post;
-           }
-         }
-#else
-         { int count = tiled->width * tiled->height;
-           const uint32_t *src = (void*)tiled->pixels;
-           uint32_t *dst = (void*)fb->fb;
-           count-= pre_skip;
-           src+= pre_skip;
-           dst+= pre_skip;
-           count-= post_skip;
-           while (count -- > 0)
-           {
-             dst[0] = ctx_swap_red_green2 (src[0]);
-             src++;
-             dst++;
-           }
-         }
-#endif
-         break;
-         /* XXX  :  note: converting a scanline (or all) to target and
-          * then doing a bulk memcpy be faster (at least with som /dev/fbs)  */
-       case 24:
-         { int count = tiled->width * tiled->height;
-           const uint8_t *src = tiled->pixels;
-           uint8_t *dst = fb->fb;
-           count-= pre_skip;
-           src+= pre_skip * 4;
-           dst+= pre_skip * 3;
-           count-= post_skip;
-           while (count -- > 0)
-           {
-             dst[0] = src[0];
-             dst[1] = src[1];
-             dst[2] = src[2];
-             dst+=3;
-             src+=4;
-           }
-         }
-         break;
-       case 16:
-         { int count = tiled->width * tiled->height;
-           const uint8_t *src = tiled->pixels;
-           uint8_t *dst = fb->fb;
-           count-= post_skip;
-           count-= pre_skip;
-           src+= pre_skip * 4;
-           dst+= pre_skip * 2;
-           while (count -- > 0)
-           {
-             int big = ((src[0] >> 3)) +
-                ((src[1] >> 2)<<5) +
-                ((src[2] >> 3)<<11);
-             dst[0] = big & 255;
-             dst[1] = big >>  8;
-             dst+=2;
-             src+=4;
-           }
-         }
-         break;
-       case 15:
-         { int count = tiled->width * tiled->height;
-           const uint8_t *src = tiled->pixels;
-           uint8_t *dst = fb->fb;
-           count-= post_skip;
-           count-= pre_skip;
-           src+= pre_skip * 4;
-           dst+= pre_skip * 2;
-           while (count -- > 0)
-           {
-             int big = ((src[2] >> 3)) +
-                       ((src[1] >> 2)<<5) +
-                       ((src[0] >> 3)<<10);
-             dst[0] = big & 255;
-             dst[1] = big >>  8;
-             dst+=2;
-             src+=4;
-           }
-         }
-         break;
-       case 8:
-         { int count = tiled->width * tiled->height;
-           const uint8_t *src = tiled->pixels;
-           uint8_t *dst = fb->fb;
-           count-= post_skip;
-           count-= pre_skip;
-           src+= pre_skip * 4;
-           dst+= pre_skip;
-           while (count -- > 0)
-           {
-             dst[0] = ((src[0] >> 5)) +
-                      ((src[1] >> 5)<<3) +
-                      ((src[2] >> 6)<<6);
-             dst+=1;
-             src+=4;
-           }
-         }
-         break;
-     }
-    }
-    ctx_fb_cursor_drawn = 0;
-    ctx_fb_draw_cursor (fb);
-    ctx_fb_flip (fb);
-    tiled->shown_frame = tiled->render_frame;
-  }
+void
+ctx_font_size (Ctx *ctx, float x)
+{
+  CTX_PROCESS_F1 (CTX_FONT_SIZE, x);
 }
 
+float ctx_get_font_size  (Ctx *ctx)
+{
+  return ctx->state.gstate.font_size;
+}
 
-#define evsource_has_event(es)   (es)->has_event((es))
-#define evsource_get_event(es)   (es)->get_event((es))
-#define evsource_destroy(es)     do{if((es)->destroy)(es)->destroy((es));}while(0)
-#define evsource_set_coord(es,x,y) do{if((es)->set_coord)(es)->set_coord((es),(x),(y));}while(0)
-#define evsource_get_fd(es)      ((es)->get_fd?(es)->get_fd((es)):0)
+void
+ctx_miter_limit (Ctx *ctx, float limit)
+{
+  CTX_PROCESS_F1 (CTX_MITER_LIMIT, limit);
+}
 
+float ctx_get_line_width (Ctx *ctx)
+{
+  return ctx->state.gstate.line_width;
+}
 
+void
+_ctx_font (Ctx *ctx, const char *name)
+{
+  ctx->state.gstate.font = ctx_resolve_font (name);
+}
 
-static int mice_has_event ();
-static char *mice_get_event ();
-static void mice_destroy ();
-static int mice_get_fd (EvSource *ev_source);
-static void mice_set_coord (EvSource *ev_source, double x, double y);
+#if 0
+void
+ctx_set (Ctx *ctx, uint64_t key_hash, const char *string, int len)
+{
+  if (len <= 0) len = strlen (string);
+  ctx_process_cmd_str (ctx, CTX_SET, string, key_hash, len);
+}
 
-static EvSource ctx_ev_src_mice = {
-  NULL,
-  (void*)mice_has_event,
-  (void*)mice_get_event,
-  (void*)mice_destroy,
-  mice_get_fd,
-  mice_set_coord
-};
+const char *
+ctx_get (Ctx *ctx, const char *key)
+{
+  static char retbuf[32];
+  int len = 0;
+  CTX_PROCESS_U32(CTX_GET, ctx_strhash (key, 0), 0);
+  while (read (STDIN_FILENO, &retbuf[len], 1) != -1)
+    {
+      if(retbuf[len]=='\n')
+        break;
+      retbuf[++len]=0;
+    }
+  return retbuf;
+}
+#endif
 
-typedef struct Mice
+void
+ctx_font_family (Ctx *ctx, const char *name)
 {
-  int     fd;
-  double  x;
-  double  y;
-  int     button;
-  int     prev_state;
-} Mice;
+#if CTX_BACKEND_TEXT
+  ctx_process_cmd_str (ctx, CTX_FONT, name, 0, 0);
+  _ctx_font (ctx, name);
+#else
+  _ctx_font (ctx, name);
+#endif
+}
 
-Mice *_mrg_evsrc_coord = NULL;
-static int _ctx_mice_fd = 0;
+void
+ctx_font (Ctx *ctx, const char *family_name)
+{
+  // should also parse size
+  ctx_font_family (ctx, family_name);
+}
 
-void _mmm_get_coords (Ctx *ctx, double *x, double *y)
+const char *
+ctx_get_font (Ctx *ctx)
 {
-  if (!_mrg_evsrc_coord)
-    return;
-  if (x)
-    *x = _mrg_evsrc_coord->x;
-  if (y)
-    *y = _mrg_evsrc_coord->y;
+  return ctx_fonts[ctx->state.gstate.font].name;
 }
 
-static Mice  mice;
-static Mice* mrg_mice_this = &mice;
+void ctx_line_to (Ctx *ctx, float x, float y)
+{
+  if (!ctx->state.has_moved)
+    { CTX_PROCESS_F (CTX_MOVE_TO, x, y); }
+  else
+    { CTX_PROCESS_F (CTX_LINE_TO, x, y); }
+}
 
-static int mmm_evsource_mice_init ()
+void ctx_move_to (Ctx *ctx, float x, float y)
 {
-  unsigned char reset[]={0xff};
-  /* need to detect which event */
+  CTX_PROCESS_F (CTX_MOVE_TO,x,y);
+}
 
-  mrg_mice_this->prev_state = 0;
-  mrg_mice_this->fd = open ("/dev/input/mice", O_RDONLY | O_NONBLOCK);
-  if (mrg_mice_this->fd == -1)
+void ctx_curve_to (Ctx *ctx, float x0, float y0,
+                   float x1, float y1,
+                   float x2, float y2)
+{
+  CtxEntry command[3]=
   {
-    fprintf (stderr, "error opening /dev/input/mice device, maybe add user to input group if such group 
exist, or otherwise make the rights be satisfied.\n");
-    return -1;
-  }
-  if (write (mrg_mice_this->fd, reset, 1) == -1)
+    ctx_f (CTX_CURVE_TO, x0, y0),
+    ctx_f (CTX_CONT,     x1, y1),
+    ctx_f (CTX_CONT,     x2, y2)
+  };
+  ctx_process (ctx, command);
+}
+
+void ctx_round_rectangle (Ctx *ctx,
+                          float x0, float y0,
+                          float w, float h,
+                          float radius)
+{
+  CtxEntry command[3]=
   {
-    // might happen if we're a regular user with only read permission
-  }
-  _ctx_mice_fd = mrg_mice_this->fd;
-  _mrg_evsrc_coord = mrg_mice_this;
-  return 0;
+    ctx_f (CTX_ROUND_RECTANGLE, x0, y0),
+    ctx_f (CTX_CONT,            w, h),
+    ctx_f (CTX_CONT,            radius, 0)
+  };
+  ctx_process (ctx, command);
 }
 
-static void mice_destroy ()
+void ctx_view_box (Ctx *ctx,
+                   float x0, float y0,
+                   float w, float h)
 {
-  if (mrg_mice_this->fd != -1)
-    close (mrg_mice_this->fd);
+  CtxEntry command[3]=
+  {
+    ctx_f (CTX_VIEW_BOX, x0, y0),
+    ctx_f (CTX_CONT,     w, h)
+  };
+  ctx_process (ctx, command);
 }
 
-static int mice_has_event ()
+void ctx_rectangle (Ctx *ctx,
+                    float x0, float y0,
+                    float w, float h)
 {
-  struct timeval tv;
-  int retval;
+  CtxEntry command[3]=
+  {
+    ctx_f (CTX_RECTANGLE, x0, y0),
+    ctx_f (CTX_CONT,      w, h)
+  };
+  ctx_process (ctx, command);
+}
 
-  if (mrg_mice_this->fd == -1)
-    return 0;
+void ctx_rel_line_to (Ctx *ctx, float x, float y)
+{
+  if (!ctx->state.has_moved)
+    { return; }
+  CTX_PROCESS_F (CTX_REL_LINE_TO,x,y);
+}
 
-  fd_set rfds;
-  FD_ZERO (&rfds);
-  FD_SET(mrg_mice_this->fd, &rfds);
-  tv.tv_sec = 0; tv.tv_usec = 0;
-  retval = select (mrg_mice_this->fd+1, &rfds, NULL, NULL, &tv);
-  if (retval == 1)
-    return FD_ISSET (mrg_mice_this->fd, &rfds);
-  return 0;
+void ctx_rel_move_to (Ctx *ctx, float x, float y)
+{
+  if (!ctx->state.has_moved)
+    {
+      CTX_PROCESS_F (CTX_MOVE_TO,x,y);
+      return;
+    }
+  CTX_PROCESS_F (CTX_REL_MOVE_TO,x,y);
 }
 
-static char *mice_get_event ()
+CtxLineJoin ctx_get_line_join (Ctx *ctx)
 {
-  const char *ret = "mouse-motion";
-  double relx, rely;
-  signed char buf[3];
-  int n_read = 0;
-  CtxFb *fb = ctx_ev_src_mice.priv;
-  CtxTiled *tiled = (void*)fb;
-  n_read = read (mrg_mice_this->fd, buf, 3);
-  if (n_read == 0)
-     return strdup ("");
-  relx = buf[1];
-  rely = -buf[2];
+  return ctx->state.gstate.line_join;
+}
 
-  if (relx < 0)
-  {
-    if (relx > -6)
-    relx = - relx*relx;
-    else
-    relx = -36;
-  }
-  else
-  {
-    if (relx < 6)
-    relx = relx*relx;
-    else
-    relx = 36;
-  }
+CtxCompositingMode ctx_get_compositing_mode (Ctx *ctx)
+{
+  return ctx->state.gstate.compositing_mode;
+}
 
-  if (rely < 0)
-  {
-    if (rely > -6)
-    rely = - rely*rely;
-    else
-    rely = -36;
-  }
-  else
-  {
-    if (rely < 6)
-    rely = rely*rely;
-    else
-    rely = 36;
-  }
+CtxBlend ctx_get_blend_mode (Ctx *ctx)
+{
+  return ctx->state.gstate.blend_mode;
+}
 
-  mrg_mice_this->x += relx;
-  mrg_mice_this->y += rely;
+CtxTextAlign ctx_get_text_align  (Ctx *ctx)
+{
+  return (CtxTextAlign)ctx_state_get (&ctx->state, CTX_text_align);
+}
 
-  if (mrg_mice_this->x < 0)
-    mrg_mice_this->x = 0;
-  if (mrg_mice_this->y < 0)
-    mrg_mice_this->y = 0;
-  if (mrg_mice_this->x >= tiled->width)
-    mrg_mice_this->x = tiled->width -1;
-  if (mrg_mice_this->y >= tiled->height)
-    mrg_mice_this->y = tiled->height -1;
-  int button = 0;
-  
-  if ((mrg_mice_this->prev_state & 1) != (buf[0] & 1))
-    {
-      if (buf[0] & 1)
-        {
-          ret = "mouse-press";
-        }
-      else
-        {
-          ret = "mouse-release";
-        }
-      button = 1;
-    }
-  else if (buf[0] & 1)
-  {
-    ret = "mouse-drag";
-    button = 1;
-  }
-
-  if (!button)
-  {
-    if ((mrg_mice_this->prev_state & 2) != (buf[0] & 2))
-    {
-      if (buf[0] & 2)
-        {
-          ret = "mouse-press";
-        }
-      else
-        {
-          ret = "mouse-release";
-        }
-      button = 3;
-    }
-    else if (buf[0] & 2)
-    {
-      ret = "mouse-drag";
-      button = 3;
-    }
-  }
-
-  if (!button)
-  {
-    if ((mrg_mice_this->prev_state & 4) != (buf[0] & 4))
-    {
-      if (buf[0] & 4)
-        {
-          ret = "mouse-press";
-        }
-      else
-        {
-          ret = "mouse-release";
-        }
-      button = 2;
-    }
-    else if (buf[0] & 4)
-    {
-      ret = "mouse-drag";
-      button = 2;
-    }
-  }
-
-  mrg_mice_this->prev_state = buf[0];
-
-  {
-    char *r = malloc (64);
-    sprintf (r, "%s %.0f %.0f %i", ret, mrg_mice_this->x, mrg_mice_this->y, button);
-    return r;
-  }
-
-  return NULL;
+CtxTextBaseline ctx_get_text_baseline (Ctx *ctx)
+{
+  return (CtxTextBaseline)ctx_state_get (&ctx->state, CTX_text_baseline);
 }
 
-static int mice_get_fd (EvSource *ev_source)
+CtxLineCap ctx_get_line_cap (Ctx *ctx)
 {
-  return mrg_mice_this->fd;
+  return ctx->state.gstate.line_cap;
 }
 
-static void mice_set_coord (EvSource *ev_source, double x, double y)
+CtxFillRule ctx_get_fill_rule (Ctx *ctx)
 {
-  mrg_mice_this->x = x;
-  mrg_mice_this->y = y;
+  return ctx->state.gstate.fill_rule;
 }
 
-static EvSource *evsource_mice_new (void)
+void ctx_line_cap (Ctx *ctx, CtxLineCap cap)
 {
-  if (mmm_evsource_mice_init () == 0)
-    {
-      mrg_mice_this->x = 0;
-      mrg_mice_this->y = 0;
-      return &ctx_ev_src_mice;
-    }
-  return NULL;
+  if (ctx->state.gstate.line_cap != cap)
+    CTX_PROCESS_U8 (CTX_LINE_CAP, cap);
 }
 
-static int evsource_kb_has_event (void);
-static char *evsource_kb_get_event (void);
-static void evsource_kb_destroy (int sign);
-static int evsource_kb_get_fd (void);
-
-/* kept out of struct to be reachable by atexit */
-static EvSource ctx_ev_src_kb = {
-  NULL,
-  (void*)evsource_kb_has_event,
-  (void*)evsource_kb_get_event,
-  (void*)evsource_kb_destroy,
-  (void*)evsource_kb_get_fd,
-  NULL
-};
-
-static struct termios orig_attr;
+void ctx_fill_rule (Ctx *ctx, CtxFillRule fill_rule)
+{
+  if (ctx->state.gstate.fill_rule != fill_rule)
+    CTX_PROCESS_U8 (CTX_FILL_RULE, fill_rule);
+}
+void ctx_line_join (Ctx *ctx, CtxLineJoin join)
+{
+  if (ctx->state.gstate.line_join != join)
+    CTX_PROCESS_U8 (CTX_LINE_JOIN, join);
+}
+void ctx_blend_mode (Ctx *ctx, CtxBlend mode)
+{
+  if (ctx->state.gstate.blend_mode != mode)
+    CTX_PROCESS_U8 (CTX_BLEND_MODE, mode);
+}
+void ctx_compositing_mode (Ctx *ctx, CtxCompositingMode mode)
+{
+  if (ctx->state.gstate.compositing_mode != mode)
+    CTX_PROCESS_U8 (CTX_COMPOSITING_MODE, mode);
+}
+void ctx_text_align (Ctx *ctx, CtxTextAlign text_align)
+{
+  CTX_PROCESS_U8 (CTX_TEXT_ALIGN, text_align);
+}
+void ctx_text_baseline (Ctx *ctx, CtxTextBaseline text_baseline)
+{
+  CTX_PROCESS_U8 (CTX_TEXT_BASELINE, text_baseline);
+}
+void ctx_text_direction (Ctx *ctx, CtxTextDirection text_direction)
+{
+  CTX_PROCESS_U8 (CTX_TEXT_DIRECTION, text_direction);
+}
 
-static void real_evsource_kb_destroy (int sign)
+void
+ctx_rel_curve_to (Ctx *ctx,
+                  float x0, float y0,
+                  float x1, float y1,
+                  float x2, float y2)
 {
-  static int done = 0;
+  if (!ctx->state.has_moved)
+    { return; }
+  CtxEntry command[3]=
+  {
+    ctx_f (CTX_REL_CURVE_TO, x0, y0),
+    ctx_f (CTX_CONT, x1, y1),
+    ctx_f (CTX_CONT, x2, y2)
+  };
+  ctx_process (ctx, command);
+}
 
-  if (sign == 0)
-    return;
+void
+ctx_rel_quad_to (Ctx *ctx,
+                 float cx, float cy,
+                 float x,  float y)
+{
+  if (!ctx->state.has_moved)
+    { return; }
+  CtxEntry command[2]=
+  {
+    ctx_f (CTX_REL_QUAD_TO, cx, cy),
+    ctx_f (CTX_CONT, x, y)
+  };
+  ctx_process (ctx, command);
+}
 
-  if (done)
-    return;
-  done = 1;
+void
+ctx_quad_to (Ctx *ctx,
+             float cx, float cy,
+             float x,  float y)
+{
+  if (!ctx->state.has_moved)
+    { return; }
+  CtxEntry command[2]=
+  {
+    ctx_f (CTX_QUAD_TO, cx, cy),
+    ctx_f (CTX_CONT, x, y)
+  };
+  ctx_process (ctx, command);
+}
 
-  switch (sign)
+void ctx_arc (Ctx  *ctx,
+              float x0, float y0,
+              float radius,
+              float angle1, float angle2,
+              int   direction)
+{
+  CtxEntry command[3]=
   {
-    case  -11:break; /* will be called from atexit with sign==-11 */
-    case   SIGSEGV: break;//fprintf (stderr, " SIGSEGV\n");break;
-    case   SIGABRT: fprintf (stderr, " SIGABRT\n");break;
-    case   SIGBUS:  fprintf (stderr, " SIGBUS\n");break;
-    case   SIGKILL: fprintf (stderr, " SIGKILL\n");break;
-    case   SIGINT:  fprintf (stderr, " SIGINT\n");break;
-    case   SIGTERM: fprintf (stderr, " SIGTERM\n");break;
-    case   SIGQUIT: fprintf (stderr, " SIGQUIT\n");break;
-    default: fprintf (stderr, "sign: %i\n", sign);
-             fprintf (stderr, "%i %i %i %i %i %i %i\n", SIGSEGV, SIGABRT, SIGBUS, SIGKILL, SIGINT, SIGTERM, 
SIGQUIT);
-  }
-  tcsetattr (STDIN_FILENO, TCSAFLUSH, &orig_attr);
-  //fprintf (stderr, "evsource kb destroy\n");
+    ctx_f (CTX_ARC, x0, y0),
+    ctx_f (CTX_CONT, radius, angle1),
+    ctx_f (CTX_CONT, angle2, direction)
+  };
+  ctx_process (ctx, command);
 }
 
-static void evsource_kb_destroy (int sign)
+static int ctx_coords_equal (float x1, float y1, float x2, float y2, float tol)
 {
-  real_evsource_kb_destroy (-11);
+  float dx = x2 - x1;
+  float dy = y2 - y1;
+  return dx*dx + dy*dy < tol*tol;
 }
 
-static int evsource_kb_init ()
+static float
+ctx_point_seg_dist_sq (float x, float y,
+                       float vx, float vy, float wx, float wy)
 {
-//  ioctl(STDIN_FILENO, KDSKBMODE, K_RAW);
-  atexit ((void*) real_evsource_kb_destroy);
-  signal (SIGSEGV, (void*) real_evsource_kb_destroy);
-  signal (SIGABRT, (void*) real_evsource_kb_destroy);
-  signal (SIGBUS,  (void*) real_evsource_kb_destroy);
-  signal (SIGKILL, (void*) real_evsource_kb_destroy);
-  signal (SIGINT,  (void*) real_evsource_kb_destroy);
-  signal (SIGTERM, (void*) real_evsource_kb_destroy);
-  signal (SIGQUIT, (void*) real_evsource_kb_destroy);
+  float l2 = ctx_pow2 (vx-wx) + ctx_pow2 (vy-wy);
+  if (l2 < 0.0001)
+    { return ctx_pow2 (x-vx) + ctx_pow2 (y-vy); }
+  float t = ( (x - vx) * (wx - vx) + (y - vy) * (wy - vy) ) / l2;
+  t = ctx_maxf (0, ctx_minf (1, t) );
+  float ix = vx + t * (wx - vx);
+  float iy = vy + t * (wy - vy);
+  return ctx_pow2 (x-ix) + ctx_pow2 (y-iy);
+}
 
-  struct termios raw;
-  if (tcgetattr (STDIN_FILENO, &orig_attr) == -1)
+static void
+ctx_normalize (float *x, float *y)
+{
+  float length = ctx_hypotf ( (*x), (*y) );
+  if (length > 1e-6f)
     {
-      fprintf (stderr, "error initializing keyboard\n");
-      return -1;
+      float r = 1.0f / length;
+      *x *= r;
+      *y *= r;
     }
-  raw = orig_attr;
-
-  cfmakeraw (&raw);
-
-  raw.c_cc[VMIN] = 1; raw.c_cc[VTIME] = 0; /* 1 byte, no timer */
-  if (tcsetattr (STDIN_FILENO, TCSAFLUSH, &raw) < 0)
-    return 0; // XXX? return other value?
-
-  return 0;
 }
-static int evsource_kb_has_event (void)
-{
-  struct timeval tv;
-  int retval;
 
-  fd_set rfds;
-  FD_ZERO (&rfds);
-  FD_SET(STDIN_FILENO, &rfds);
-  tv.tv_sec = 0; tv.tv_usec = 0;
-  retval = select (STDIN_FILENO+1, &rfds, NULL, NULL, &tv);
-  return retval == 1;
+void
+ctx_arc_to (Ctx *ctx, float x1, float y1, float x2, float y2, float radius)
+{
+  // XXX : should partially move into rasterizer to preserve comand
+  //       even if an arc preserves all geometry, just to ensure roundtripping
+  //       of data
+  /* from nanovg - but not quite working ; uncertain if arc or wrong
+   * transfusion is the cause.
+   */
+  float x0 = ctx->state.x;
+  float y0 = ctx->state.y;
+  float dx0,dy0, dx1,dy1, a, d, cx,cy, a0,a1;
+  int dir;
+  if (!ctx->state.has_moved)
+    { return; }
+  if (1)
+    {
+      // Handle degenerate cases.
+      if (ctx_coords_equal (x0,y0, x1,y1, 0.5f) ||
+          ctx_coords_equal (x1,y1, x2,y2, 0.5f) ||
+          ctx_point_seg_dist_sq (x1,y1, x0,y0, x2,y2) < 0.5 ||
+          radius < 0.5)
+        {
+          ctx_line_to (ctx, x1,y1);
+          return;
+        }
+    }
+  // Calculate tangential circle to lines (x0,y0)-(x1,y1) and (x1,y1)-(x2,y2).
+  dx0 = x0-x1;
+  dy0 = y0-y1;
+  dx1 = x2-x1;
+  dy1 = y2-y1;
+  ctx_normalize (&dx0,&dy0);
+  ctx_normalize (&dx1,&dy1);
+  a = ctx_acosf (dx0*dx1 + dy0*dy1);
+  d = radius / ctx_tanf (a/2.0f);
+#if 0
+  if (d > 10000.0f)
+    {
+      ctx_line_to (ctx, x1, y1);
+      return;
+    }
+#endif
+  if ( (dx1*dy0 - dx0*dy1) > 0.0f)
+    {
+      cx = x1 + dx0*d + dy0*radius;
+      cy = y1 + dy0*d + -dx0*radius;
+      a0 = ctx_atan2f (dx0, -dy0);
+      a1 = ctx_atan2f (-dx1, dy1);
+      dir = 0;
+    }
+  else
+    {
+      cx = x1 + dx0*d + -dy0*radius;
+      cy = y1 + dy0*d + dx0*radius;
+      a0 = ctx_atan2f (-dx0, dy0);
+      a1 = ctx_atan2f (dx1, -dy1);
+      dir = 1;
+    }
+  ctx_arc (ctx, cx, cy, radius, a0, a1, dir);
 }
 
-/* note that a nick can have multiple occurences, the labels
- * should be kept the same for all occurences of a combination.
- *
- * this table is taken from nchanterm.
- */
-typedef struct MmmKeyCode {
-  char *nick;          /* programmers name for key */
-  char  sequence[10];  /* terminal sequence */
-} MmmKeyCode;
-static const MmmKeyCode ufb_keycodes[]={
-  {"up",                  "\e[A"},
-  {"down",                "\e[B"},
-  {"right",               "\e[C"},
-  {"left",                "\e[D"},
-
-  {"shift-up",            "\e[1;2A"},
-  {"shift-down",          "\e[1;2B"},
-  {"shift-right",         "\e[1;2C"},
-  {"shift-left",          "\e[1;2D"},
-
-  {"alt-up",              "\e[1;3A"},
-  {"alt-down",            "\e[1;3B"},
-  {"alt-right",           "\e[1;3C"},
-  {"alt-left",            "\e[1;3D"},
-  {"alt-shift-up",         "\e[1;4A"},
-  {"alt-shift-down",       "\e[1;4B"},
-  {"alt-shift-right",      "\e[1;4C"},
-  {"alt-shift-left",       "\e[1;4D"},
+void
+ctx_rel_arc_to (Ctx *ctx, float x1, float y1, float x2, float y2, float radius)
+{
+  x1 += ctx->state.x;
+  y1 += ctx->state.y;
+  x2 += ctx->state.x;
+  y2 += ctx->state.y;
+  ctx_arc_to (ctx, x1, y1, x2, y2, radius);
+}
 
-  {"control-up",          "\e[1;5A"},
-  {"control-down",        "\e[1;5B"},
-  {"control-right",       "\e[1;5C"},
-  {"control-left",        "\e[1;5D"},
+void
+ctx_exit (Ctx *ctx)
+{
+  CTX_PROCESS_VOID (CTX_EXIT);
+}
 
-  /* putty */
-  {"control-up",          "\eOA"},
-  {"control-down",        "\eOB"},
-  {"control-right",       "\eOC"},
-  {"control-left",        "\eOD"},
+void
+ctx_flush (Ctx *ctx)
+{
+  /* XXX: should be fully moved into the renderers
+   *      to permit different behavior and get rid
+   *      of the extranous flush() vfunc.
+   */
+  ctx->rev++;
+//  CTX_PROCESS_VOID (CTX_FLUSH);
+#if 0
+  //printf (" \e[?2222h");
+  ctx_drawlist_compact (&ctx->drawlist);
+  for (int i = 0; i < ctx->drawlist.count - 1; i++)
+    {
+      CtxEntry *entry = &ctx->drawlist.entries[i];
+      fwrite (entry, 9, 1, stdout);
+#if 0
+      uint8_t  *buf = (void *) entry;
+      for (int j = 0; j < 9; j++)
+        { printf ("%c", buf[j]); }
+#endif
+    }
+  printf ("Xx.Xx.Xx.");
+  fflush (NULL);
+#endif
+  if (ctx->renderer && ctx->renderer->flush)
+    ctx->renderer->flush (ctx->renderer);
+  ctx->frame++;
+  if (ctx->texture_cache != ctx)
+    ctx->texture_cache->frame++;
+  ctx->drawlist.count = 0;
+  ctx_state_init (&ctx->state);
+}
 
-  {"control-shift-up",    "\e[1;6A"},
-  {"control-shift-down",  "\e[1;6B"},
-  {"control-shift-right", "\e[1;6C"},
-  {"control-shift-left",  "\e[1;6D"},
+////////////////////////////////////////
 
-  {"control-up",          "\eOa"},
-  {"control-down",        "\eOb"},
-  {"control-right",       "\eOc"},
-  {"control-left",        "\eOd"},
+void
+ctx_interpret_style (CtxState *state, CtxEntry *entry, void *data)
+{
+  CtxCommand *c = (CtxCommand *) entry;
+  switch (entry->code)
+    {
+      case CTX_LINE_DASH_OFFSET:
+        state->gstate.line_dash_offset = ctx_arg_float (0);
+        break;
+      case CTX_LINE_WIDTH:
+        state->gstate.line_width = ctx_arg_float (0);
+        break;
+#if CTX_ENABLE_SHADOW_BLUR
+      case CTX_SHADOW_BLUR:
+        state->gstate.shadow_blur = ctx_arg_float (0);
+        break;
+      case CTX_SHADOW_OFFSET_X:
+        state->gstate.shadow_offset_x = ctx_arg_float (0);
+        break;
+      case CTX_SHADOW_OFFSET_Y:
+        state->gstate.shadow_offset_y = ctx_arg_float (0);
+        break;
+#endif
+      case CTX_LINE_CAP:
+        state->gstate.line_cap = (CtxLineCap) ctx_arg_u8 (0);
+        break;
+      case CTX_FILL_RULE:
+        state->gstate.fill_rule = (CtxFillRule) ctx_arg_u8 (0);
+        break;
+      case CTX_LINE_JOIN:
+        state->gstate.line_join = (CtxLineJoin) ctx_arg_u8 (0);
+        break;
+      case CTX_COMPOSITING_MODE:
+        state->gstate.compositing_mode = (CtxCompositingMode) ctx_arg_u8 (0);
+        break;
+      case CTX_BLEND_MODE:
+        state->gstate.blend_mode = (CtxBlend) ctx_arg_u8 (0);
+        break;
+      case CTX_TEXT_ALIGN:
+        ctx_state_set (state, CTX_text_align, ctx_arg_u8 (0) );
+        break;
+      case CTX_TEXT_BASELINE:
+        ctx_state_set (state, CTX_text_baseline, ctx_arg_u8 (0) );
+        break;
+      case CTX_TEXT_DIRECTION:
+        ctx_state_set (state, CTX_text_direction, ctx_arg_u8 (0) );
+        break;
+      case CTX_GLOBAL_ALPHA:
+        state->gstate.global_alpha_u8 = ctx_float_to_u8 (ctx_arg_float (0) );
+        state->gstate.global_alpha_f = ctx_arg_float (0);
+        break;
+      case CTX_FONT_SIZE:
+        state->gstate.font_size = ctx_arg_float (0);
+        break;
+      case CTX_MITER_LIMIT:
+        state->gstate.miter_limit = ctx_arg_float (0);
+        break;
+      case CTX_COLOR_SPACE:
+        /* move this out of this function and only do it in rasterizer? XXX */
+        ctx_rasterizer_colorspace_icc (state, (CtxColorSpace)c->colorspace.space_slot,
+                                              (char*)c->colorspace.data,
+                                              c->colorspace.data_len);
+        break;
+      case CTX_IMAGE_SMOOTHING:
+        state->gstate.image_smoothing = c->entry.data.u8[0];
+        break;
+      case CTX_STROKE_SOURCE:
+        state->source = 1;
+        break;
 
-  {"shift-up",            "\e[a"},
-  {"shift-down",          "\e[b"},
-  {"shift-right",         "\e[c"},
-  {"shift-left",          "\e[d"},
+      case CTX_COLOR:
+        {
+          int is_stroke = (state->source != 0);
+          CtxSource *source = is_stroke ?
+                                &state->gstate.source_stroke:
+                                &state->gstate.source_fill;
+          state->source = 0;
 
-  {"insert",              "\e[2~"},
-  {"delete",              "\e[3~"},
-  {"page-up",             "\e[5~"},
-  {"page-down",           "\e[6~"},
-  {"home",                "\eOH"},
-  {"end",                 "\eOF"},
-  {"home",                "\e[H"},
-  {"end",                 "\e[F"},
- {"control-delete",       "\e[3;5~"},
-  {"shift-delete",        "\e[3;2~"},
-  {"control-shift-delete","\e[3;6~"},
-
-  {"F1",         "\e[25~"},
-  {"F2",         "\e[26~"},
-  {"F3",         "\e[27~"},
-  {"F4",         "\e[26~"},
-
-
-  {"F1",         "\e[11~"},
-  {"F2",         "\e[12~"},
-  {"F3",         "\e[13~"},
-  {"F4",         "\e[14~"},
-  {"F1",         "\eOP"},
-  {"F2",         "\eOQ"},
-  {"F3",         "\eOR"},
-  {"F4",         "\eOS"},
-  {"F5",         "\e[15~"},
-  {"F6",         "\e[16~"},
-  {"F7",         "\e[17~"},
-  {"F8",         "\e[18~"},
-  {"F9",         "\e[19~"},
-  {"F9",         "\e[20~"},
-  {"F10",        "\e[21~"},
-  {"F11",        "\e[22~"},
-  {"F12",        "\e[23~"},
-  {"tab",         {9, '\0'}},
-  {"shift-tab",   {27, 9, '\0'}}, // also generated by alt-tab in linux console
-  {"alt-space",   {27, ' ', '\0'}},
-  {"shift-tab",   "\e[Z"},
-  {"backspace",   {127, '\0'}},
-  {"space",       " "},
-  {"\e",          "\e"},
-  {"return",      {10,0}},
-  {"return",      {13,0}},
-  /* this section could be autogenerated by code */
-  {"control-a",   {1,0}},
-  {"control-b",   {2,0}},
-  {"control-c",   {3,0}},
-  {"control-d",   {4,0}},
-  {"control-e",   {5,0}},
-  {"control-f",   {6,0}},
-  {"control-g",   {7,0}},
-  {"control-h",   {8,0}}, /* backspace? */
-  {"control-i",   {9,0}},
-  {"control-j",   {10,0}},
-  {"control-k",   {11,0}},
-  {"control-l",   {12,0}},
-  {"control-n",   {14,0}},
-  {"control-o",   {15,0}},
-  {"control-p",   {16,0}},
-  {"control-q",   {17,0}},
-  {"control-r",   {18,0}},
-  {"control-s",   {19,0}},
-  {"control-t",   {20,0}},
-  {"control-u",   {21,0}},
-  {"control-v",   {22,0}},
-  {"control-w",   {23,0}},
-  {"control-x",   {24,0}},
-  {"control-y",   {25,0}},
-  {"control-z",   {26,0}},
-  {"alt-`",       "\e`"},
-  {"alt-0",       "\e0"},
-  {"alt-1",       "\e1"},
-  {"alt-2",       "\e2"},
-  {"alt-3",       "\e3"},
-  {"alt-4",       "\e4"},
-  {"alt-5",       "\e5"},
-  {"alt-6",       "\e6"},
-  {"alt-7",       "\e7"}, /* backspace? */
-  {"alt-8",       "\e8"},
-  {"alt-9",       "\e9"},
-  {"alt-+",       "\e+"},
-  {"alt--",       "\e-"},
-  {"alt-/",       "\e/"},
-  {"alt-a",       "\ea"},
-  {"alt-b",       "\eb"},
-  {"alt-c",       "\ec"},
-  {"alt-d",       "\ed"},
-  {"alt-e",       "\ee"},
-  {"alt-f",       "\ef"},
-  {"alt-g",       "\eg"},
-  {"alt-h",       "\eh"}, /* backspace? */
-  {"alt-i",       "\ei"},
-  {"alt-j",       "\ej"},
-  {"alt-k",       "\ek"},
-  {"alt-l",       "\el"},
-  {"alt-n",       "\em"},
-  {"alt-n",       "\en"},
-  {"alt-o",       "\eo"},
-  {"alt-p",       "\ep"},
-  {"alt-q",       "\eq"},
-  {"alt-r",       "\er"},
-  {"alt-s",       "\es"},
-  {"alt-t",       "\et"},
-  {"alt-u",       "\eu"},
-  {"alt-v",       "\ev"},
-  {"alt-w",       "\ew"},
-  {"alt-x",       "\ex"},
-  {"alt-y",       "\ey"},
-  {"alt-z",       "\ez"},
-  /* Linux Console  */
-  {"home",       "\e[1~"},
-  {"end",        "\e[4~"},
-  {"F1",         "\e[[A"},
-  {"F2",         "\e[[B"},
-  {"F3",         "\e[[C"},
-  {"F4",         "\e[[D"},
-  {"F5",         "\e[[E"},
-  {"F6",         "\e[[F"},
-  {"F7",         "\e[[G"},
-  {"F8",         "\e[[H"},
-  {"F9",         "\e[[I"},
-  {"F10",        "\e[[J"},
-  {"F11",        "\e[[K"},
-  {"F12",        "\e[[L"},
-  {NULL, }
-};
-static int fb_keyboard_match_keycode (const char *buf, int length, const MmmKeyCode **ret)
-{
-  int i;
-  int matches = 0;
-
-  if (!strncmp (buf, "\e[M", MIN(length,3)))
-    {
-      if (length >= 6)
-        return 9001;
-      return 2342;
-    }
-  for (i = 0; ufb_keycodes[i].nick; i++)
-    if (!strncmp (buf, ufb_keycodes[i].sequence, length))
-      {
-        matches ++;
-        if ((int)strlen (ufb_keycodes[i].sequence) == length && ret)
-          {
-            *ret = &ufb_keycodes[i];
-            return 1;
-          }
-      }
-  if (matches != 1 && ret)
-    *ret = NULL;
-  return matches==1?2:matches;
-}
-
-//int is_active (void *host)
-//{
-//        return 1;
-//}
-
-static char *evsource_kb_get_event (void)
-{
-  unsigned char buf[20];
-  int length;
-
-
-  for (length = 0; length < 10; length ++)
-    if (read (STDIN_FILENO, &buf[length], 1) != -1)
-      {
-        const MmmKeyCode *match = NULL;
-
-        //if (!is_active (ctx_ev_src_kb.priv))
-        //  return NULL;
-
-        /* special case ESC, so that we can use it alone in keybindings */
-        if (length == 0 && buf[0] == 27)
-          {
-            struct timeval tv;
-            fd_set rfds;
-            FD_ZERO (&rfds);
-            FD_SET (STDIN_FILENO, &rfds);
-            tv.tv_sec = 0;
-            tv.tv_usec = 1000 * 120;
-            if (select (STDIN_FILENO+1, &rfds, NULL, NULL, &tv) == 0)
-              return strdup ("escape");
-          }
-
-        switch (fb_keyboard_match_keycode ((void*)buf, length + 1, &match))
-          {
-            case 1: /* unique match */
-              if (!match)
-                return NULL;
-              return strdup (match->nick);
-              break;
-            case 0: /* no matches, bail*/
-             {
-                static char ret[256]="";
-                if (length == 0 && ctx_utf8_len (buf[0])>1) /* read a
-                                                             * single unicode
-                                                             * utf8 character
-                                                             */
-                  {
-                    int bytes = read (STDIN_FILENO, &buf[length+1], ctx_utf8_len(buf[0])-1);
-                    if (bytes)
-                    {
-                      buf[ctx_utf8_len(buf[0])]=0;
-                      strcpy (ret, (void*)buf);
-                    }
-                    return strdup(ret); //XXX: simplify
-                  }
-                if (length == 0) /* ascii */
-                  {
-                    buf[1]=0;
-                    strcpy (ret, (void*)buf);
-                    return strdup(ret);
-                  }
-                sprintf (ret, "unhandled %i:'%c' %i:'%c' %i:'%c' %i:'%c' %i:'%c' %i:'%c' %i:'%c'",
-                    length >=0 ? buf[0] : 0,
-                    length >=0 ? buf[0]>31?buf[0]:'?' : ' ',
-                    length >=1 ? buf[1] : 0,
-                    length >=1 ? buf[1]>31?buf[1]:'?' : ' ',
-                    length >=2 ? buf[2] : 0,
-                    length >=2 ? buf[2]>31?buf[2]:'?' : ' ',
-                    length >=3 ? buf[3] : 0,
-                    length >=3 ? buf[3]>31?buf[3]:'?' : ' ',
-                    length >=4 ? buf[4] : 0,
-                    length >=4 ? buf[4]>31?buf[4]:'?' : ' ',
-                    length >=5 ? buf[5] : 0,
-                    length >=5 ? buf[5]>31?buf[5]:'?' : ' ',
-                    length >=6 ? buf[6] : 0,
-                    length >=6 ? buf[6]>31?buf[6]:'?' : ' '
-                    );
-                return strdup(ret);
-            }
-              return NULL;
-            default: /* continue */
-              break;
-          }
-      }
-    else
-      return strdup("key read eek");
-  return strdup("fail");
-}
-
-static int evsource_kb_get_fd (void)
-{
-  return STDIN_FILENO;
-}
-
-
-static EvSource *evsource_kb_new (void)
-{
-  if (evsource_kb_init() == 0)
-  {
-    return &ctx_ev_src_kb;
-  }
-  return NULL;
-}
-
-static int event_check_pending (CtxFb *fb)
-{
-  CtxTiled *tiled = (void*)fb;
-  int events = 0;
-  for (int i = 0; i < fb->evsource_count; i++)
-  {
-    while (evsource_has_event (fb->evsource[i]))
-    {
-      char *event = evsource_get_event (fb->evsource[i]);
-      if (event)
-      {
-        if (fb->vt_active)
-        {
-          ctx_key_press (tiled->ctx, 0, event, 0); // we deliver all events as key-press, the key_press 
handler disambiguates
-          events++;
-        }
-        free (event);
-      }
-    }
-  }
-  return events;
-}
-
-int ctx_fb_consume_events (Ctx *ctx)
-{
-  CtxFb *fb = (void*)ctx->renderer;
-  ctx_fb_show_frame (fb, 0);
-  event_check_pending (fb);
-  return 0;
-}
-
-inline static void ctx_fb_reset (CtxFb *fb)
-{
-  ctx_fb_show_frame (fb, 1);
-}
-
-inline static void ctx_fb_flush (CtxFb *fb)
-{
-  ctx_tiled_flush ((CtxTiled*)fb);
-}
-
-void ctx_fb_free (CtxFb *fb)
-{
-  if (fb->is_drm)
-  {
-    ctx_fbdrm_close (fb);
-  }
-
-  ioctl (0, KDSETMODE, KD_TEXT);
-  if (system("stty sane")){};
-  ctx_tiled_free ((CtxTiled*)fb);
-  //free (fb);
-#if CTX_BABL
-  babl_exit ();
-#endif
-}
-
-//static unsigned char *fb_icc = NULL;
-//static long fb_icc_length = 0;
-
-int ctx_renderer_is_fb (Ctx *ctx)
-{
-  if (ctx->renderer &&
-      ctx->renderer->free == (void*)ctx_fb_free)
-          return 1;
-  return 0;
-}
-
-static CtxFb *ctx_fb = NULL;
-static void vt_switch_cb (int sig)
-{
-  CtxTiled *tiled = (void*)ctx_fb;
-  if (sig == SIGUSR1)
-  {
-    if (ctx_fb->is_drm)
-      ioctl(ctx_fb->fb_fd, DRM_IOCTL_DROP_MASTER, 0);
-    ioctl (0, VT_RELDISP, 1);
-    ctx_fb->vt_active = 0;
-    ioctl (0, KDSETMODE, KD_TEXT);
-  }
-  else
-  {
-    ioctl (0, VT_RELDISP, VT_ACKACQ);
-    ctx_fb->vt_active = 1;
-    // queue draw
-    tiled->render_frame = ++tiled->frame;
-    ioctl (0, KDSETMODE, KD_GRAPHICS);
-    if (ctx_fb->is_drm)
-    {
-      ioctl(ctx_fb->fb_fd, DRM_IOCTL_SET_MASTER, 0);
-      ctx_fb_flip (ctx_fb);
-    }
-    else
-    {
-      tiled->ctx->dirty=1;
-
-      for (int row = 0; row < CTX_HASH_ROWS; row++)
-      for (int col = 0; col < CTX_HASH_COLS; col++)
-      {
-        tiled->hashes[(row * CTX_HASH_COLS + col) *  20] += 1;
-      }
-    }
-  }
-}
-
-static int ctx_fb_get_mice_fd (Ctx *ctx)
-{
-  //CtxFb *fb = (void*)ctx->renderer;
-  return _ctx_mice_fd;
-}
-
-Ctx *ctx_new_fb (int width, int height, int drm)
-{
-#if CTX_RASTERIZER
-  CtxFb *fb = calloc (sizeof (CtxFb), 1);
-
-  CtxTiled *tiled = (void*)fb;
-  ctx_fb = fb;
-  if (drm)
-    fb->fb = ctx_fbdrm_new (fb, &tiled->width, &tiled->height);
-  if (fb->fb)
-  {
-    fb->is_drm         = 1;
-    width              = tiled->width;
-    height             = tiled->height;
-    /*
-       we're ignoring the input width and height ,
-       maybe turn them into properties - for
-       more generic handling.
-     */
-    fb->fb_mapped_size = tiled->width * tiled->height * 4;
-    fb->fb_bits        = 32;
-    fb->fb_bpp         = 4;
-  }
-  else
-  {
-  fb->fb_fd = open ("/dev/fb0", O_RDWR);
-  if (fb->fb_fd > 0)
-    fb->fb_path = strdup ("/dev/fb0");
-  else
-  {
-    fb->fb_fd = open ("/dev/graphics/fb0", O_RDWR);
-    if (fb->fb_fd > 0)
-    {
-      fb->fb_path = strdup ("/dev/graphics/fb0");
-    }
-    else
-    {
-      free (fb);
-      return NULL;
-    }
-  }
-
-  if (ioctl(fb->fb_fd, FBIOGET_FSCREENINFO, &fb->finfo))
-    {
-      fprintf (stderr, "error getting fbinfo\n");
-      close (fb->fb_fd);
-      free (fb->fb_path);
-      free (fb);
-      return NULL;
-    }
-
-   if (ioctl(fb->fb_fd, FBIOGET_VSCREENINFO, &fb->vinfo))
-     {
-       fprintf (stderr, "error getting fbinfo\n");
-      close (fb->fb_fd);
-      free (fb->fb_path);
-      free (fb);
-      return NULL;
-     }
-
-//fprintf (stderr, "%s\n", fb->fb_path);
-  width = tiled->width = fb->vinfo.xres;
-  height = tiled->height = fb->vinfo.yres;
-
-  fb->fb_bits = fb->vinfo.bits_per_pixel;
-//fprintf (stderr, "fb bits: %i\n", fb->fb_bits);
-
-  if (fb->fb_bits == 16)
-    fb->fb_bits =
-      fb->vinfo.red.length +
-      fb->vinfo.green.length +
-      fb->vinfo.blue.length;
-
-   else if (fb->fb_bits == 8)
-  {
-    unsigned short red[256],  green[256],  blue[256];
-    unsigned short original_red[256];
-    unsigned short original_green[256];
-    unsigned short original_blue[256];
-    struct fb_cmap cmap = {0, 256, red, green, blue, NULL};
-    struct fb_cmap original_cmap = {0, 256, original_red, original_green, original_blue, NULL};
-    int i;
-
-    /* do we really need to restore it ? */
-    if (ioctl (fb->fb_fd, FBIOPUTCMAP, &original_cmap) == -1)
-    {
-      fprintf (stderr, "palette initialization problem %i\n", __LINE__);
-    }
-
-    for (i = 0; i < 256; i++)
-    {
-      red[i]   = ((( i >> 5) & 0x7) << 5) << 8;
-      green[i] = ((( i >> 2) & 0x7) << 5) << 8;
-      blue[i]  = ((( i >> 0) & 0x3) << 6) << 8;
-    }
-
-    if (ioctl (fb->fb_fd, FBIOPUTCMAP, &cmap) == -1)
-    {
-      fprintf (stderr, "palette initialization problem %i\n", __LINE__);
-    }
-  }
-
-  fb->fb_bpp = fb->vinfo.bits_per_pixel / 8;
-  fb->fb_mapped_size = fb->finfo.smem_len;
-                                              
-  fb->fb = mmap (NULL, fb->fb_mapped_size, PROT_READ|PROT_WRITE, MAP_SHARED, fb->fb_fd, 0);
-  }
-  if (!fb->fb)
-    return NULL;
-  tiled->pixels = calloc (fb->fb_mapped_size, 1);
-  ctx_fb_events = 1;
-
-#if CTX_BABL
-  babl_init ();
-#endif
-
-  ctx_get_contents ("file:///tmp/ctx.icc", &sdl_icc, &sdl_icc_length);
-
-  tiled->ctx      = ctx_new ();
-  tiled->ctx_copy = ctx_new ();
-  tiled->width    = width;
-  tiled->height   = height;
-
-  ctx_set_renderer (tiled->ctx, fb);
-  ctx_set_renderer (tiled->ctx_copy, fb);
-  ctx_set_texture_cache (tiled->ctx_copy, tiled->ctx);
-
-  ctx_set_size (tiled->ctx, width, height);
-  ctx_set_size (tiled->ctx_copy, width, height);
-
-  tiled->flush = (void*)ctx_fb_flush;
-  tiled->reset = (void*)ctx_fb_reset;
-  tiled->free  = (void*)ctx_fb_free;
-  tiled->set_clipboard = (void*)ctx_fb_set_clipboard;
-  tiled->get_clipboard = (void*)ctx_fb_get_clipboard;
-
-  for (int i = 0; i < _ctx_max_threads; i++)
-  {
-    tiled->host[i] = ctx_new_for_framebuffer (tiled->pixels,
-                   tiled->width/CTX_HASH_COLS, tiled->height/CTX_HASH_ROWS,
-                   tiled->width * 4, CTX_FORMAT_BGRA8); // this format
-                                  // is overriden in  thread
-    ((CtxRasterizer*)(tiled->host[i]->renderer))->swap_red_green = 1;
-    ctx_set_texture_source (tiled->host[i], tiled->ctx);
-  }
-
-  mtx_init (&tiled->mtx, mtx_plain);
-  cnd_init (&tiled->cond);
-
-#define start_thread(no)\
-  if(_ctx_max_threads>no){ \
-    static void *args[2]={(void*)no, };\
-    thrd_t tid;\
-    args[1]=fb;\
-    thrd_create (&tid, (void*)ctx_tiled_render_fun, args);\
-  }
-  start_thread(0);
-  start_thread(1);
-  start_thread(2);
-  start_thread(3);
-  start_thread(4);
-  start_thread(5);
-  start_thread(6);
-  start_thread(7);
-  start_thread(8);
-  start_thread(9);
-  start_thread(10);
-  start_thread(11);
-  start_thread(12);
-  start_thread(13);
-  start_thread(14);
-  start_thread(15);
-#undef start_thread
-
-  ctx_flush (tiled->ctx);
-
-  EvSource *kb = evsource_kb_new ();
-  if (kb)
-  {
-    fb->evsource[fb->evsource_count++] = kb;
-    kb->priv = fb;
-  }
-  EvSource *mice  = evsource_mice_new ();
-  if (mice)
-  {
-    fb->evsource[fb->evsource_count++] = mice;
-    mice->priv = fb;
-  }
-
-  fb->vt_active = 1;
-  ioctl(0, KDSETMODE, KD_GRAPHICS);
-  signal (SIGUSR1, vt_switch_cb);
-  signal (SIGUSR2, vt_switch_cb);
-  struct vt_stat st;
-  if (ioctl (0, VT_GETSTATE, &st) == -1)
-  {
-    ctx_log ("VT_GET_MODE on vt %i failed\n", fb->vt);
-    return NULL;
-  }
-
-  fb->vt = st.v_active;
-
-  struct vt_mode mode;
-  mode.mode   = VT_PROCESS;
-  mode.relsig = SIGUSR1;
-  mode.acqsig = SIGUSR2;
-  if (ioctl (0, VT_SETMODE, &mode) < 0)
-  {
-    ctx_log ("VT_SET_MODE on vt %i failed\n", fb->vt);
-    return NULL;
-  }
-
-  return tiled->ctx;
-#else
-  return NULL;
-#endif
-}
-#else
-
-int ctx_renderer_is_fb (Ctx *ctx)
-{
-  return 0;
-}
-#endif
-#endif
-
-#if CTX_SDL
-
-/**/
-
-typedef struct _CtxSDL CtxSDL;
-struct _CtxSDL
-{
-   CtxTiled  tiled;
-   /* where we diverge from fb*/
-   int           key_balance;
-   int           key_repeat;
-   int           lctrl;
-   int           lalt;
-   int           rctrl;
-   int           lshift;
-   int           rshift;
-
-   SDL_Window   *window;
-   SDL_Renderer *renderer;
-   SDL_Texture  *texture;
-
-// cnd_t  cond;
-// mtx_t  mtx;
-   int           fullscreen;
-};
-
-#include "stb_image_write.h"
-
-void ctx_screenshot (Ctx *ctx, const char *output_path)
-{
-#if CTX_SCREENSHOT
-  int valid = 0;
-  CtxSDL *sdl = (void*)ctx->renderer;
-
-  if (ctx_renderer_is_sdl (ctx)) valid = 1;
-#if CTX_FB
-  if (ctx_renderer_is_fb  (ctx)) valid = 1;
-#endif
-
-  if (!valid)
-    return;
-
-#if CTX_FB
-  // we rely on the same layout
-  for (int i = 0; i < sdl->width * sdl->height; i++)
-  {
-    int tmp = sdl->pixels[i*4];
-    sdl->pixels[i*4] = sdl->pixels[i*4 + 2];
-    sdl->pixels[i*4 + 2] = tmp;
-  }
-#endif
-
-  stbi_write_png (output_path, sdl->width, sdl->height, 4, sdl->pixels, sdl->width*4);
-
-#if CTX_FB
-  for (int i = 0; i < sdl->width * sdl->height; i++)
-  {
-    int tmp = sdl->pixels[i*4];
-    sdl->pixels[i*4] = sdl->pixels[i*4 + 2];
-    sdl->pixels[i*4 + 2] = tmp;
-  }
-#endif
-#endif
-}
-
-int ctx_show_fps = 1;
-void ctx_sdl_set_title (void *self, const char *new_title)
-{
-   CtxSDL *sdl = self;
-   if (!ctx_show_fps)
-   SDL_SetWindowTitle (sdl->window, new_title);
-}
-
-static void ctx_sdl_show_frame (CtxSDL *sdl, int block)
-{
-  CtxTiled *tiled = &sdl->tiled;
-  if (tiled->shown_cursor != tiled->ctx->cursor)
-  {
-    tiled->shown_cursor = tiled->ctx->cursor;
-    SDL_Cursor *new_cursor =  NULL;
-    switch (tiled->shown_cursor)
-    {
-      case CTX_CURSOR_UNSET: // XXX: document how this differs from none
-                             //      perhaps falling back to arrow?
-        break;
-      case CTX_CURSOR_NONE:
-        new_cursor = NULL;
-        break;
-      case CTX_CURSOR_ARROW:
-        new_cursor = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_ARROW);
-        break;
-      case CTX_CURSOR_CROSSHAIR:
-        new_cursor = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_CROSSHAIR);
-        break;
-      case CTX_CURSOR_WAIT:
-        new_cursor = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_WAIT);
-        break;
-      case CTX_CURSOR_HAND:
-        new_cursor = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_HAND);
-        break;
-      case CTX_CURSOR_IBEAM:
-        new_cursor = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_IBEAM);
-        break;
-      case CTX_CURSOR_MOVE:
-      case CTX_CURSOR_RESIZE_ALL:
-        new_cursor = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZEALL);
-        break;
-      case CTX_CURSOR_RESIZE_N:
-      case CTX_CURSOR_RESIZE_S:
-        new_cursor = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZENS);
-        break;
-      case CTX_CURSOR_RESIZE_E:
-      case CTX_CURSOR_RESIZE_W:
-        new_cursor = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZEWE);
-        break;
-      case CTX_CURSOR_RESIZE_NE:
-      case CTX_CURSOR_RESIZE_SW:
-        new_cursor = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZENESW);
-        break;
-      case CTX_CURSOR_RESIZE_NW:
-      case CTX_CURSOR_RESIZE_SE:
-        new_cursor = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZENWSE);
-        break;
-    }
-    if (new_cursor)
-    {
-      SDL_Cursor *old_cursor = SDL_GetCursor();
-      SDL_SetCursor (new_cursor);
-      SDL_ShowCursor (1);
-      if (old_cursor)
-        SDL_FreeCursor (old_cursor);
-    }
-    else
-    {
-      SDL_ShowCursor (0);
-    }
-  }
-
-  if (tiled->shown_frame == tiled->render_frame)
-  {
-    return;
-  }
-
-  if (block)
-  {
-    int count = 0;
-    while (ctx_tiled_threads_done (tiled) != _ctx_max_threads)
-    {
-      usleep (50);
-      count ++;
-      if (count > 2000)
-      {
-        tiled->shown_frame = tiled->render_frame;
-        return;
-      }
-    }
-  }
-  else
-  {
-    if (ctx_tiled_threads_done (tiled) != _ctx_max_threads)
-      return;
-  }
-
-
-  if (tiled->min_row == 100)
-  {
-  }
-  else
-  {
-#if 1
-    int x = tiled->min_col * tiled->width/CTX_HASH_COLS;
-    int y = tiled->min_row * tiled->height/CTX_HASH_ROWS;
-    int x1 = (tiled->max_col+1) * tiled->width/CTX_HASH_COLS;
-    int y1 = (tiled->max_row+1) * tiled->height/CTX_HASH_ROWS;
-    int width = x1 - x;
-    int height = y1 - y;
-#endif
-    tiled->min_row = 100;
-    tiled->max_row = 0;
-    tiled->min_col = 100;
-    tiled->max_col = 0;
-
-    SDL_Rect r = {x, y, width, height};
-    SDL_UpdateTexture (sdl->texture, &r,
-                      //(void*)sdl->pixels,
-                      (void*)(tiled->pixels + y * tiled->width * 4 + x * 4),
-                      
-                      tiled->width * 4);
-    SDL_RenderClear (sdl->renderer);
-    SDL_RenderCopy (sdl->renderer, sdl->texture, NULL, NULL);
-    SDL_RenderPresent (sdl->renderer);
-
-
-  if (ctx_show_fps)
-  {
-    static uint64_t prev_time = 0;
-    static char tmp_title[1024];
-    uint64_t time = ctx_ticks ();
-    sprintf (tmp_title, "FPS: %.1f", 1000000.0/  (time - prev_time));
-    prev_time = time;
-    SDL_SetWindowTitle (sdl->window, tmp_title);
-  }
-  }
-  tiled->shown_frame = tiled->render_frame;
-}
-
-static const char *ctx_sdl_keysym_to_name (unsigned int sym, int *r_keycode)
-{
-  static char buf[16]="";
-  buf[ctx_unichar_to_utf8 (sym, (void*)buf)]=0;
-  int code = sym;
-  const char *name = &buf[0];
-   switch (sym)
-   {
-     case SDLK_RSHIFT: code = 16 ; break;
-     case SDLK_LSHIFT: code = 16 ; break;
-     case SDLK_LCTRL: code = 17 ; break;
-     case SDLK_RCTRL: code = 17 ; break;
-     case SDLK_LALT:  code = 18 ; break;
-     case SDLK_RALT:  code = 18 ; break;
-     case SDLK_CAPSLOCK: name = "capslock"; code = 20 ; break;
-     //case SDLK_NUMLOCK: name = "numlock"; code = 144 ; break;
-     //case SDLK_SCROLLLOCK: name = "scrollock"; code = 145 ; break;
-
-     case SDLK_F1:     name = "F1"; code = 112; break;
-     case SDLK_F2:     name = "F2"; code = 113; break;
-     case SDLK_F3:     name = "F3"; code = 114; break;
-     case SDLK_F4:     name = "F4"; code = 115; break;
-     case SDLK_F5:     name = "F5"; code = 116; break;
-     case SDLK_F6:     name = "F6"; code = 117; break;
-     case SDLK_F7:     name = "F7"; code = 118; break;
-     case SDLK_F8:     name = "F8"; code = 119; break;
-     case SDLK_F9:     name = "F9"; code = 120; break;
-     case SDLK_F10:    name = "F10"; code = 121; break;
-     case SDLK_F11:    name = "F11"; code = 122; break;
-     case SDLK_F12:    name = "F12"; code = 123; break;
-     case SDLK_ESCAPE: name = "escape"; break;
-     case SDLK_DOWN:   name = "down"; code = 40; break;
-     case SDLK_LEFT:   name = "left"; code = 37; break;
-     case SDLK_UP:     name = "up"; code = 38;  break;
-     case SDLK_RIGHT:  name = "right"; code = 39; break;
-     case SDLK_BACKSPACE: name = "backspace"; break;
-     case SDLK_SPACE:  name = "space"; break;
-     case SDLK_TAB:    name = "tab"; break;
-     case SDLK_DELETE: name = "delete"; code = 46; break;
-     case SDLK_INSERT: name = "insert"; code = 45; break;
-     case SDLK_RETURN:
-       //if (key_repeat == 0) // return never should repeat
-       name = "return";   // on a DEC like terminal
-       break;
-     case SDLK_HOME:     name = "home"; code = 36; break;
-     case SDLK_END:      name = "end"; code = 35; break;
-     case SDLK_PAGEDOWN: name = "page-down"; code = 34; break;
-     case SDLK_PAGEUP:   name = "page-up"; code = 33; break;
-     case ',': code = 188; break;
-     case '.': code = 190; break;
-     case '/': code = 191; break;
-     case '`': code = 192; break;
-     case '[': code = 219; break;
-     case '\\': code = 220; break;
-     case ']':  code = 221; break;
-     case '\'': code = 222; break;
-     default:
-       ;
-   }
-   if (sym >= 'a' && sym <='z') code -= 32;
-   if (r_keycode)
-   {
-     *r_keycode = code;
-   }
-   return name;
-}
-
-int ctx_sdl_consume_events (Ctx *ctx)
-{
-  CtxTiled *tiled = (void*)ctx->renderer;
-  CtxSDL *sdl = (void*)ctx->renderer;
-  SDL_Event event;
-  int got_events = 0;
-
-  ctx_sdl_show_frame (sdl, 0);
-
-  while (SDL_PollEvent (&event))
-  {
-    got_events ++;
-    switch (event.type)
-    {
-      case SDL_MOUSEBUTTONDOWN:
-        SDL_CaptureMouse (1);
-        ctx_pointer_press (ctx, event.button.x, event.button.y, event.button.button, 0);
-        break;
-      case SDL_MOUSEBUTTONUP:
-        SDL_CaptureMouse (0);
-        ctx_pointer_release (ctx, event.button.x, event.button.y, event.button.button, 0);
-        break;
-      case SDL_MOUSEMOTION:
-        //  XXX : look at mask and generate motion for each pressed
-        //        button
-        ctx_pointer_motion (ctx, event.motion.x, event.motion.y, 1, 0);
-        break;
-      case SDL_FINGERMOTION:
-        ctx_pointer_motion (ctx, event.tfinger.x * tiled->width, event.tfinger.y * tiled->height,
-            (event.tfinger.fingerId%10) + 4, 0);
-        break;
-      case SDL_FINGERDOWN:
-        {
-        static int fdowns = 0;
-        fdowns ++;
-        if (fdowns > 1) // the very first finger down from SDL seems to be
-                        // mirrored as mouse events, later ones not - at
-                        // least under wayland
-        {
-          ctx_pointer_press (ctx, event.tfinger.x * tiled->width, event.tfinger.y * tiled->height, 
-          (event.tfinger.fingerId%10) + 4, 0);
-        }
-        }
-        break;
-      case SDL_FINGERUP:
-        ctx_pointer_release (ctx, event.tfinger.x * tiled->width, event.tfinger.y * tiled->height,
-          (event.tfinger.fingerId%10) + 4, 0);
-        break;
-#if 1
-      case SDL_TEXTINPUT:
-    //  if (!active)
-    //    break;
-        if (!sdl->lctrl && !sdl->rctrl && !sdl->lalt 
-           //&& ( (vt && vt_keyrepeat (vt) ) || (key_repeat==0) )
-           )
-          {
-            const char *name = event.text.text;
-            int keycode = 0;
-            if (!strcmp (name, " ") ) { name = "space"; }
-            if (name[0] && name[1] == 0)
-            {
-              keycode = name[0];
-              keycode = toupper (keycode);
-              switch (keycode)
-              {
-                case '.':  keycode = 190; break;
-                case ';':  keycode = 59; break;
-                case ',':  keycode = 188; break;
-                case '/':  keycode = 191; break;
-                case '\'': keycode = 222; break;
-                case '`':  keycode = 192; break;
-                case '[':  keycode = 219; break;
-                case ']':  keycode = 221; break;
-                case '\\': keycode = 220; break;
-              }
-            }
-            ctx_key_press (ctx, keycode, name, 0);
-            //got_event = 1;
-          }
-        break;
-#endif
-      case SDL_KEYDOWN:
-        {
-          char buf[32] = "";
-          const char *name = buf;
-          if (!event.key.repeat)
-          {
-            sdl->key_balance ++;
-            sdl->key_repeat = 0;
-          }
-          else
-          {
-            sdl->key_repeat ++;
-          }
-          switch (event.key.keysym.sym)
-          {
-            case SDLK_LSHIFT: sdl->lshift = 1; break;
-            case SDLK_RSHIFT: sdl->rshift = 1; break;
-            case SDLK_LCTRL:  sdl->lctrl = 1; break;
-            case SDLK_LALT:   sdl->lalt = 1; break;
-            case SDLK_RCTRL:  sdl->rctrl = 1; break;
-          }
-          if (sdl->lshift | sdl->rshift | sdl->lctrl | sdl->lalt | sdl->rctrl)
-          {
-            ctx->events.modifier_state ^= ~(CTX_MODIFIER_STATE_CONTROL|
-                                            CTX_MODIFIER_STATE_ALT|
-                                            CTX_MODIFIER_STATE_SHIFT);
-            if (sdl->lshift | sdl->rshift)
-              ctx->events.modifier_state |= CTX_MODIFIER_STATE_SHIFT;
-            if (sdl->lctrl | sdl->rctrl)
-              ctx->events.modifier_state |= CTX_MODIFIER_STATE_CONTROL;
-            if (sdl->lalt)
-              ctx->events.modifier_state |= CTX_MODIFIER_STATE_ALT;
-          }
-          int keycode;
-          name = ctx_sdl_keysym_to_name (event.key.keysym.sym, &keycode);
-          ctx_key_down (ctx, keycode, name, 0);
-
-          if (strlen (name)
-              &&(event.key.keysym.mod & (KMOD_CTRL) ||
-                 event.key.keysym.mod & (KMOD_ALT) ||
-                 strlen (name) >= 2))
-          {
-            if (event.key.keysym.mod & (KMOD_CTRL) )
-              {
-                static char buf[64] = "";
-                sprintf (buf, "control-%s", name);
-                name = buf;
-              }
-            if (event.key.keysym.mod & (KMOD_ALT) )
-              {
-                static char buf[128] = "";
-                sprintf (buf, "alt-%s", name);
-                name = buf;
-              }
-            if (event.key.keysym.mod & (KMOD_SHIFT) )
-              {
-                static char buf[196] = "";
-                sprintf (buf, "shift-%s", name);
-                name = buf;
-              }
-            if (strcmp (name, "space"))
-              {
-               ctx_key_press (ctx, keycode, name, 0);
-              }
-          }
-          else
-          {
-#if 0
-             ctx_key_press (ctx, 0, buf, 0);
-#endif
-          }
-        }
-        break;
-      case SDL_KEYUP:
-        {
-           sdl->key_balance --;
-           switch (event.key.keysym.sym)
-           {
-             case SDLK_LSHIFT: sdl->lshift = 0; break;
-             case SDLK_RSHIFT: sdl->rshift = 0; break;
-             case SDLK_LCTRL: sdl->lctrl = 0; break;
-             case SDLK_RCTRL: sdl->rctrl = 0; break;
-             case SDLK_LALT:  sdl->lalt  = 0; break;
-           }
-
-          {
-            ctx->events.modifier_state ^= ~(CTX_MODIFIER_STATE_CONTROL|
-                                            CTX_MODIFIER_STATE_ALT|
-                                            CTX_MODIFIER_STATE_SHIFT);
-            if (sdl->lshift | sdl->rshift)
-              ctx->events.modifier_state |= CTX_MODIFIER_STATE_SHIFT;
-            if (sdl->lctrl | sdl->rctrl)
-              ctx->events.modifier_state |= CTX_MODIFIER_STATE_CONTROL;
-            if (sdl->lalt)
-              ctx->events.modifier_state |= CTX_MODIFIER_STATE_ALT;
-          }
-
-           int keycode;
-           const char *name = ctx_sdl_keysym_to_name (event.key.keysym.sym, &keycode);
-           ctx_key_up (ctx, keycode, name, 0);
-        }
-        break;
-      case SDL_QUIT:
-        ctx_quit (ctx);
-        break;
-      case SDL_WINDOWEVENT:
-        if (event.window.event == SDL_WINDOWEVENT_RESIZED)
-        {
-          ctx_sdl_show_frame (sdl, 1);
-          int width = event.window.data1;
-          int height = event.window.data2;
-          SDL_DestroyTexture (sdl->texture);
-          sdl->texture = SDL_CreateTexture (sdl->renderer, SDL_PIXELFORMAT_ABGR8888,
-                          SDL_TEXTUREACCESS_STREAMING, width, height);
-          free (tiled->pixels);
-          tiled->pixels = calloc (4, width * height);
-
-          tiled->width  = width;
-          tiled->height = height;
-          ctx_set_size (tiled->ctx, width, height);
-          ctx_set_size (tiled->ctx_copy, width, height);
-        }
-        break;
-    }
-  }
-  return 1;
-}
-#else
-void ctx_screenshot (Ctx *ctx, const char *path)
-{
-}
-#endif
-
-#if CTX_SDL
-
-static void ctx_sdl_set_clipboard (CtxSDL *sdl, const char *text)
-{
-  if (text)
-    SDL_SetClipboardText (text);
-}
-
-static char *ctx_sdl_get_clipboard (CtxSDL *sdl)
-{
-  return SDL_GetClipboardText ();
-}
-
-inline static void ctx_sdl_reset (CtxSDL *sdl)
-{
-  ctx_sdl_show_frame (sdl, 1);
-}
-
-inline static void ctx_sdl_flush (CtxSDL *sdl)
-{
-  ctx_tiled_flush ((void*)sdl);
-  //CtxTiled *tiled = (void*)sdl;
-}
-
-void ctx_sdl_free (CtxSDL *sdl)
-{
-
-  if (sdl->texture)
-  SDL_DestroyTexture (sdl->texture);
-  if (sdl->renderer)
-  SDL_DestroyRenderer (sdl->renderer);
-  if (sdl->window)
-  SDL_DestroyWindow (sdl->window);
-
-  ctx_tiled_free ((CtxTiled*)sdl);
-#if CTX_BABL
-  babl_exit ();
-#endif
-}
-
-
-int ctx_renderer_is_sdl (Ctx *ctx)
-{
-  if (ctx->renderer &&
-      ctx->renderer->free == (void*)ctx_sdl_free)
-          return 1;
-  return 0;
-}
-
-void ctx_sdl_set_fullscreen (Ctx *ctx, int val)
-{
-  CtxSDL *sdl = (void*)ctx->renderer;
-
-  if (val)
-  {
-    SDL_SetWindowFullscreen (sdl->window, SDL_WINDOW_FULLSCREEN_DESKTOP);
-  }
-  else
-  {
-    SDL_SetWindowFullscreen (sdl->window, 0);
-  }
-  // XXX we're presuming success
-  sdl->fullscreen = val;
-}
-int ctx_sdl_get_fullscreen (Ctx *ctx)
-{
-  CtxSDL *sdl = (void*)ctx->renderer;
-  return sdl->fullscreen;
-}
-
-
-Ctx *ctx_new_sdl (int width, int height)
-{
-#if CTX_RASTERIZER
-
-  CtxSDL *sdl = (CtxSDL*)calloc (sizeof (CtxSDL), 1);
-  CtxTiled *tiled = (void*)sdl;
-
-  ctx_get_contents ("file:///tmp/ctx.icc", &sdl_icc, &sdl_icc_length);
-  if (width <= 0 || height <= 0)
-  {
-    width  = 1920;
-    height = 1080;
-  }
-  sdl->window = SDL_CreateWindow("ctx", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, width, height, 
SDL_WINDOW_SHOWN|SDL_WINDOW_RESIZABLE);
-  //sdl->renderer = SDL_CreateRenderer (sdl->window, -1, SDL_RENDERER_SOFTWARE);
-  sdl->renderer = SDL_CreateRenderer (sdl->window, -1, 0);
-  if (!sdl->renderer)
-  {
-     ctx_free (tiled->ctx);
-     free (sdl);
-     return NULL;
-  }
-#if CTX_BABL
-  babl_init ();
-#endif
-  sdl->fullscreen = 0;
-
-
-  ctx_show_fps = getenv ("CTX_SHOW_FPS")!=NULL;
-
-  ctx_sdl_events = 1;
-  sdl->texture = SDL_CreateTexture (sdl->renderer,
-        SDL_PIXELFORMAT_ABGR8888,
-        SDL_TEXTUREACCESS_STREAMING,
-        width, height);
-
-  SDL_StartTextInput ();
-  SDL_EnableScreenSaver ();
-
-  tiled->ctx      = ctx_new ();
-  tiled->ctx_copy = ctx_new ();
-  tiled->width    = width;
-  tiled->height   = height;
-  tiled->cols     = 80;
-  tiled->rows     = 20;
-  ctx_set_renderer (tiled->ctx, sdl);
-  ctx_set_renderer (tiled->ctx_copy, sdl);
-  ctx_set_texture_cache (tiled->ctx_copy, tiled->ctx);
-
-  tiled->pixels = (uint8_t*)malloc (width * height * 4);
-
-  ctx_set_size (tiled->ctx,      width, height);
-  ctx_set_size (tiled->ctx_copy, width, height);
-
-  tiled->flush = (void*)ctx_sdl_flush;
-  tiled->reset = (void*)ctx_sdl_reset;
-  tiled->free  = (void*)ctx_sdl_free;
-  tiled->set_clipboard = (void*)ctx_sdl_set_clipboard;
-  tiled->get_clipboard = (void*)ctx_sdl_get_clipboard;
-
-  for (int i = 0; i < _ctx_max_threads; i++)
-  {
-    tiled->host[i] = ctx_new_for_framebuffer (tiled->pixels,
-                     tiled->width/CTX_HASH_COLS, tiled->height/CTX_HASH_ROWS,
-                     tiled->width * 4, CTX_FORMAT_RGBA8);
-    ctx_set_texture_source (tiled->host[i], tiled->ctx);
-  }
-
-  mtx_init (&tiled->mtx, mtx_plain);
-  cnd_init (&tiled->cond);
-
-#define start_thread(no)\
-  if(_ctx_max_threads>no){ \
-    static void *args[2]={(void*)no, };\
-    thrd_t tid;\
-    args[1]=sdl;\
-    thrd_create (&tid, (void*)ctx_tiled_render_fun, args);\
-  }
-  start_thread(0);
-  start_thread(1);
-  start_thread(2);
-  start_thread(3);
-  start_thread(4);
-  start_thread(5);
-  start_thread(6);
-  start_thread(7);
-  start_thread(8);
-  start_thread(9);
-  start_thread(10);
-  start_thread(11);
-  start_thread(12);
-  start_thread(13);
-  start_thread(14);
-  start_thread(15);
-#undef start_thread
-
-  ctx_flush (tiled->ctx);
-  return tiled->ctx;
-#else
-  return NULL;
-#endif
-}
-#else
-
-int ctx_renderer_is_sdl (Ctx *ctx)
-{
-  return 0;
-}
-#endif
-
-#if CTX_EVENTS
-
-#if !__COSMOPOLITAN__
-#include <fcntl.h>
-#include <sys/ioctl.h>
-#endif
-
-typedef struct CtxTermCell
-{
-  char    utf8[5];
-  uint8_t fg[4];
-  uint8_t bg[4];
-
-  char    prev_utf8[5];
-  uint8_t prev_fg[4];
-  uint8_t prev_bg[4];
-} CtxTermCell;
-
-typedef struct CtxTermLine
-{
-  CtxTermCell *cells;
-  int maxcol;
-  int size;
-} CtxTermLine;
-
-typedef enum
-{
-  CTX_TERM_ASCII,
-  CTX_TERM_ASCII_MONO,
-  CTX_TERM_SEXTANT,
-  CTX_TERM_BRAILLE_MONO,
-  CTX_TERM_BRAILLE,
-  CTX_TERM_QUARTER,
-} CtxTermMode;
-
-typedef struct _CtxTerm CtxTerm;
-struct _CtxTerm
-{
-   void (*render) (void *term, CtxCommand *command);
-   void (*reset)  (void *term);
-   void (*flush)  (void *term);
-   char *(*get_clipboard) (void *ctxctx);
-   void (*set_clipboard) (void *ctxctx, const char *text);
-   void (*free)   (void *term);
-   Ctx      *ctx;
-   int       width;
-   int       height;
-   int       cols;
-   int       rows;
-   int       was_down;
-
-   uint8_t  *pixels;
-
-   Ctx      *host;
-   CtxList  *lines;
-   CtxTermMode mode;
-};
-
-static int ctx_term_ch = 8;
-static int ctx_term_cw = 8;
-
-void ctx_term_set (CtxTerm *term,
-                      int col, int row, const char *utf8,
-                      uint8_t *fg, uint8_t *bg)
-{
-  if (col < 1 || row < 1 || col > term->cols  || row > term->rows) return;
-  while (ctx_list_length (term->lines) < row)
-  {
-    ctx_list_append (&term->lines, calloc (sizeof (CtxTermLine), 1));
-  }
-  CtxTermLine *line = ctx_list_nth_data (term->lines, row-1);
-  assert (line);
-  if (line->size < col)
-  {
-     int new_size = ((col + 128)/128)*128;
-     line->cells = realloc (line->cells, sizeof (CtxTermCell) * new_size);
-     memset (&line->cells[line->size], 0, sizeof (CtxTermCell) * (new_size - line->size) );
-     line->size = new_size;
-  }
-  if (col > line->maxcol) line->maxcol = col;
-  strncpy (line->cells[col-1].utf8, (char*)utf8, 4);
-  memcpy  (line->cells[col-1].fg, fg, 4);
-  memcpy  (line->cells[col-1].bg, bg, 4);
-}
-
-static int _ctx_term256 = 0; // XXX TODO implement autodetect for this
-static long _ctx_curfg = -1;
-static long _ctx_curbg = -1;
-
-static long ctx_rgb_to_long (int r,int g, int b)
-{
-  return r * 256 * 256 + g * 256 + b;
-}
-
-
-static void ctx_term_set_fg (int red, int green, int blue)
-{
-  long lc = ctx_rgb_to_long (red, green, blue);
-  if (lc == _ctx_curfg)
-    return;
-  _ctx_curfg=lc;
-  if (_ctx_term256 == 0)
-  {
-    printf("\e[38;2;%i;%i;%im", red,green,blue);
-  }
-  else
-  {
-    int gray = (green /255.0) * 24 + 0.5;
-    int r    = (red/255.0)    * 6 + 0.5;
-    int g    = (green/255.0)  * 6 + 0.5;
-    int b    = (blue/255.0)   * 6 + 0.5;
-    if (gray > 23) gray = 23;
-
-    if (r > 5) r = 5;
-    if (g > 5) g = 5;
-    if (b > 5) b = 5;
-
-    if (((int)(r/1.66)== (int)(g/1.66)) && ((int)(g/1.66) == ((int)(b/1.66))))
-    {
-      printf("\e[38;5;%im", 16 + 216 + gray);
-    }
-    else
-      printf("\e[38;5;%im", 16 + r * 6 * 6 + g * 6  + b);
-  }
-}
-
-static void ctx_term_set_bg(int red, int green, int blue)
-{
-  long lc = ctx_rgb_to_long (red, green, blue);
-//if (lc == _ctx_curbg)
-//  return;
-  _ctx_curbg=lc;
-  if (_ctx_term256 == 0)
-  {
-    printf("\e[48;2;%i;%i;%im", red,green,blue);
-  }
-  else
-  {
-    int gray = (green /255.0) * 24 + 0.5;
-    int r    = (red/255.0)    * 6 + 0.5;
-    int g    = (green/255.0)  * 6 + 0.5;
-    int b    = (blue/255.0)   * 6 + 0.5;
-    if (gray > 23) gray = 23;
-
-    if (r > 5) r = 5;
-    if (g > 5) g = 5;
-    if (b > 5) b = 5;
-
-    if (((int)(r/1.66)== (int)(g/1.66)) && ((int)(g/1.66) == ((int)(b/1.66))))
-    {
-      printf("\e[48;5;%im", 16 + 216 + gray);
-    }
-    else
-      printf("\e[48;5;%im", 16 + r * 6 * 6 + g * 6  + b);
-  }
-}
-
-static int _ctx_term_force_full = 0;
-
-void ctx_term_scanout (CtxTerm *term)
-{
-  int row = 1;
-  printf ("\e[H");
-//  printf ("\e[?25l");
-  printf ("\e[0m");
-  for (CtxList *l = term->lines; l; l = l->next)
-  {
-    CtxTermLine *line = l->data;
-    for (int col = 1; col <= line->maxcol; col++)
-    {
-      CtxTermCell *cell = &line->cells[col-1];
-
-      if (strcmp(cell->utf8, cell->prev_utf8) ||
-          memcmp(cell->fg, cell->prev_fg, 3) ||
-          memcmp(cell->bg, cell->prev_bg, 3) || _ctx_term_force_full)
-      {
-        ctx_term_set_fg (cell->fg[0], cell->fg[1], cell->fg[2]);
-        ctx_term_set_bg (cell->bg[0], cell->bg[1], cell->bg[2]);
-        printf ("%s", cell->utf8);
-      }
-      else
-      {
-        // TODO: accumulate succesive such, and compress them
-        // into one
-        printf ("\e[C");
-      }
-      strcpy (cell->prev_utf8, cell->utf8);
-      memcpy (cell->prev_fg, cell->fg, 3);
-      memcpy (cell->prev_bg, cell->bg, 3);
-    }
-    if (row != term->rows)
-      printf ("\n\r");
-    row ++;
-  }
-  printf ("\e[0m");
-  //printf ("\e[?25h");
-  //
-}
-
-// xx
-// xx
-// xx
-//
-
-static inline int _ctx_rgba8_manhattan_diff (const uint8_t *a, const uint8_t *b)
-{
-  int c;
-  int diff = 0;
-  for (c = 0; c<3;c++)
-    diff += ctx_pow2(a[c]-b[c]);
-  return sqrtf(diff);
-  return diff;
-}
-
-static void ctx_term_output_buf_half (uint8_t *pixels,
-                          int width,
-                          int height,
-                          CtxTerm *term)
-{
-  int stride = width * 4;
-  const char *sextants[]={
-   " ","▘","▝","▀","▖","▌", "▞", "▛", "▗", "▚", "▐", "▜","▄","▙","▟","█",
-
-  };
-  for (int row = 0; row < height/2; row++)
-    {
-      for (int col = 0; col < width-3; col++)
-        {
-          int     unicode = 0;
-          int     bitno = 0;
-          uint8_t rgba[2][4] = {
-                             {255,255,255,0},
-                             {0,0,0,0}};
-          int i = 0;
-
-          int  rgbasum[2][4] = {0,};
-          int  sumcount[2];
-
-          int curdiff = 0;
-          /* first find starting point colors */
-          for (int yi = 0; yi < ctx_term_ch; yi++)
-            for (int xi = 0; xi < ctx_term_cw; xi++, i++)
-                {
-                  int noi = (row * ctx_term_ch + yi) * stride + (col*ctx_term_cw+xi) * 4;
-
-                  if (rgba[0][3] == 0)
-                  {
-                    for (int c = 0; c < 3; c++)
-                      rgba[0][c] = pixels[noi + c];
-                    rgba[0][3] = 255; // used only as mark of in-use
-                  }
-                  else
-                  {
-                    int diff = _ctx_rgba8_manhattan_diff (&pixels[noi], rgba[0]);
-                    if (diff > curdiff)
-                    {
-                      curdiff = diff;
-                      for (int c = 0; c < 3; c++)
-                        rgba[1][c] = pixels[noi + c];
-                    }
-                  }
-
-                }
-
-          for (int iters = 0; iters < 1; iters++)
-          {
-                  i= 0;
-          for (int i = 0; i < 4; i ++)
-             rgbasum[0][i] = rgbasum[1][i]=0;
-          sumcount[0] = sumcount[1] = 0;
-
-          for (int yi = 0; yi < ctx_term_ch; yi++)
-            for (int xi = 0; xi < ctx_term_cw; xi++, i++)
-                {
-                  int noi = (row * ctx_term_ch + yi) * stride + (col*ctx_term_cw+xi) * 4;
-
-                  int diff1 = _ctx_rgba8_manhattan_diff (&pixels[noi], rgba[0]);
-                  int diff2 = _ctx_rgba8_manhattan_diff (&pixels[noi], rgba[1]);
-                  int cluster = 0;
-                  if (diff1 <= diff2)
-                    cluster = 0;
-                  else
-                    cluster = 1;
-                  sumcount[cluster]++;
-                  for (int c = 0; c < 3; c++)
-                    rgbasum[cluster][c] += pixels[noi+c];
-                }
-
-
-          if (sumcount[0])
-          for (int c = 0; c < 3; c++)
-          {
-            rgba[0][c] = rgbasum[0][c] / sumcount[0];
-          }
-          if (sumcount[1])
-          for (int c = 0; c < 3; c++)
-          {
-            rgba[1][c] = rgbasum[1][c] / sumcount[1];
-          }
-          }
-
-          int pixels_set = 0;
-          for (int y = 0; y < ctx_term_ch; y++)
-            for (int x = 0; x < ctx_term_cw; x++)
-              {
-                int no = (row * ctx_term_ch + y) * stride + (col*ctx_term_cw+x) * 4;
-#define CHECK_IS_SET \
-      (_ctx_rgba8_manhattan_diff (&pixels[no], rgba[0])< \
-       _ctx_rgba8_manhattan_diff (&pixels[no], rgba[1]))
-
-                int set = CHECK_IS_SET;
-#undef CHECK_IS_SET
-                if (set)
-                  { unicode |=  (1<< (bitno) ); 
-                    pixels_set ++; 
-                  }
-                bitno++;
-              }
-           if (pixels_set == 4)
-             ctx_term_set (term, col +1, row + 1, " ",
-                           rgba[1], rgba[0]);
-           else
-             ctx_term_set (term, col +1, row + 1, sextants[unicode],
-                           rgba[0], rgba[1]);
-        }
-    }
-}
-
-void ctx_term_find_color_pair (CtxTerm *term, int x0, int y0, int w, int h,
-                uint8_t rgba[2][4])
-        //uint8_t *rgba0, uint8_t *rgba1)
-{
-int curdiff = 0;
-int stride = term->width * 4;
-uint8_t *pixels = term->pixels;
-/* first find starting point colors */
-for (int y = y0; y < y0 + h; y++)
-  for (int x = x0; x < x0 + w; x++)
-      {
-        int noi = (y) * stride + (x) * 4;
-
-        if (rgba[0][3] == 0)
-        {
-          for (int c = 0; c < 3; c++)
-            rgba[0][c] = pixels[noi + c];
-          rgba[0][3] = 255; // used only as mark of in-use
-        }
-        else
-        {
-          int diff = _ctx_rgba8_manhattan_diff (&pixels[noi], &rgba[0][0]);
-          if (diff > curdiff)
-          {
-            curdiff = diff;
-            for (int c = 0; c < 3; c++)
-              rgba[1][c] = pixels[noi + c];
-          }
-        }
-      }
-          int  rgbasum[2][4] = {0,};
-          int  sumcount[2];
-
-          for (int iters = 0; iters < 1; iters++)
-          {
-          for (int i = 0; i < 4; i ++)
-             rgbasum[0][i] = rgbasum[1][i]=0;
-          sumcount[0] = sumcount[1] = 0;
-
-          for (int y = y0; y < y0 + h; y++)
-            for (int x = x0; x < x0 + w; x++)
-                {
-                  int noi = (y) * stride + (x) * 4;
-
-                  int diff1 = _ctx_rgba8_manhattan_diff (&pixels[noi], rgba[0]);
-                  int diff2 = _ctx_rgba8_manhattan_diff (&pixels[noi], rgba[1]);
-                  int cluster = 0;
-                  if (diff1 <= diff2)
-                    cluster = 0;
-                  else
-                    cluster = 1;
-                  sumcount[cluster]++;
-                  for (int c = 0; c < 3; c++)
-                    rgbasum[cluster][c] += pixels[noi+c];
-                }
-
-
-          if (sumcount[0])
-          for (int c = 0; c < 3; c++)
-          {
-            rgba[0][c] = rgbasum[0][c] / sumcount[0];
-          }
-          if (sumcount[1])
-          for (int c = 0; c < 3; c++)
-          {
-            rgba[1][c] = rgbasum[1][c] / sumcount[1];
-          }
-          }
-
-}
-
-
-
-static void ctx_term_output_buf_quarter (uint8_t *pixels,
-                          int width,
-                          int height,
-                          CtxTerm *term)
-{
-  int stride = width * 4;
-  const char *sextants[]={
-   " ","▘","▝","▀","▖","▌", "▞", "▛", "▗", "▚", "▐", "▜","▄","▙","▟","█"
-
-  };
-  for (int row = 0; row < height/ctx_term_ch; row++)
-    {
-      for (int col = 0; col < width /ctx_term_cw; col++)
-        {
-          int     unicode = 0;
-          int     bitno = 0;
-          uint8_t rgba[2][4] = {
-                             {255,255,255,0},
-                             {0,0,0,0}};
-          ctx_term_find_color_pair (term, col * ctx_term_cw,
-                                    row * ctx_term_ch,
-                                    ctx_term_cw,
-                                    ctx_term_ch, rgba);
-
-          int pixels_set = 0;
-          for (int y = 0; y < 2; y++)
-            for (int x = 0; x < ctx_term_cw; x++)
-              {
-                int no = (row * ctx_term_ch + y) * stride + (col*ctx_term_cw+x) * 4;
-#define CHECK_IS_SET \
-      (_ctx_rgba8_manhattan_diff (&pixels[no], rgba[0])< \
-       _ctx_rgba8_manhattan_diff (&pixels[no], rgba[1]))
-
-                int set = CHECK_IS_SET;
-#undef CHECK_IS_SET
-                if (set)
-                  { unicode |=  (1<< (bitno) ); 
-                    pixels_set ++; 
-                  }
-                bitno++;
-              }
-           if (pixels_set == 4)
-             ctx_term_set (term, col +1, row + 1, " ",
-                           rgba[1], rgba[0]);
-           else
-             ctx_term_set (term, col +1, row + 1, sextants[unicode],
-                           rgba[0], rgba[1]);
-        }
-    }
-}
-
-
-static void ctx_term_output_buf_sextant (uint8_t *pixels,
-                          int width,
-                          int height,
-                          CtxTerm *term)
-{
-  int stride = width * 4;
-
-  const char *sextants[]={
-   " 
","🬀","🬁","🬂","🬃","🬄","🬅","🬆","🬇","🬈","🬉","🬊","🬋","🬌","🬍","🬎","🬏","🬐","🬑","🬒","🬓","▌","🬔","🬕","🬖","🬗","🬘","🬙","🬚","🬛","🬜","🬝","🬞","🬟","🬠","🬡","🬢","🬣","🬤","🬥","🬦","🬧","▐","🬨","🬩","🬪","🬫","🬬","🬭","🬮","🬯","🬰","🬱","🬲","🬳","🬴","🬵","🬶","🬷","🬸","🬹","🬺","🬻","█"
-  };
-
-  for (int row = 0; row < height/ctx_term_ch; row++)
-    {
-      for (int col = 0; col < width /ctx_term_cw; col++)
-        {
-          int     unicode = 0;
-          int     bitno = 0;
-          uint8_t rgba[2][4] = {
-                             {255,255,255,0},
-                             {0,0,0,0}};
-
-          ctx_term_find_color_pair (term, col * ctx_term_cw,
-                                    row * ctx_term_ch,
-                                    ctx_term_cw,
-                                    ctx_term_ch, rgba);
-
-          int pixels_set = 0;
-          for (int y = 0; y < ctx_term_ch; y++)
-            for (int x = 0; x < ctx_term_cw; x++)
-              {
-                int no = (row * ctx_term_ch + y) * stride + (col*ctx_term_cw+x) * 4;
-#define CHECK_IS_SET \
-      (_ctx_rgba8_manhattan_diff (&pixels[no], rgba[0])< \
-       _ctx_rgba8_manhattan_diff (&pixels[no], rgba[1]))
-
-                int set = CHECK_IS_SET;
-#undef CHECK_IS_SET
-                if (set)
-                  { unicode |=  (1<< (bitno) ); 
-                    pixels_set ++; 
-                  }
-                bitno++;
-              }
-
-          if (pixels_set == 6)
-            ctx_term_set (term, col +1, row + 1, " ",
-                          rgba[1], rgba[0]);
-          else
-            ctx_term_set (term, col +1, row + 1, sextants[unicode], rgba[0], rgba[1]);
-        }
-    }
-}
-
-static void ctx_term_output_buf_ascii (uint8_t *pixels,
-                          int width,
-                          int height,
-                          CtxTerm *term,
-                          int mono)
-{
-  /* this is a crude ascii-mode built on a quick mapping of sexels to ascii */
-  int stride = width * 4;
-  const char *sextants[]={
-   " ","`","'","^","🬃","`","~","\"","-","\"","'","\"","-","\"","~","^",",",";",
-   "=","/","i","[","p","P","z",")","/","7","f",">","/","F",",","\\",":",":",
-   "\\","\\","(","T","j","T","]","?","s","\\","<","q","_","=","=","=","c","L",
-   "Q","C","a","b","J","]","m","b","d","@"
-  };
-  uint8_t black[4] = {0,0,0,255};
-  for (int row = 0; row < height/ctx_term_ch; row++)
-    {
-      for (int col = 0; col < width /ctx_term_cw; col++)
-        {
-          int     unicode = 0;
-          int     bitno = 0;
-          uint8_t rgba[2][4] = {
-                             {255,255,255,0},
-                             {0,0,0,0}};
-
-          ctx_term_find_color_pair (term, col * ctx_term_cw,
-                                    row * ctx_term_ch,
-                                    ctx_term_cw,
-                                    ctx_term_ch, rgba);
-
-
-          if (_ctx_rgba8_manhattan_diff (black, rgba[1]) >
-              _ctx_rgba8_manhattan_diff (black, rgba[0]))
-          {
-            for (int c = 0; c < 4; c ++)
-            {
-              int tmp = rgba[0][c];
-              rgba[0][c] = rgba[1][c];
-              rgba[1][c] = tmp;
-            }
-          }
-          if (mono)
-          {
-            rgba[1][0] = 0;
-            rgba[1][1] = 0;
-            rgba[1][2] = 0;
-          }
-
-
-          int brightest_dark_diff = _ctx_rgba8_manhattan_diff (black, rgba[0]);
-
-          int pixels_set = 0;
-          for (int y = 0; y < ctx_term_ch; y++)
-            for (int x = 0; x < ctx_term_cw; x++)
-              {
-                int no = (row * ctx_term_ch + y) * stride + (col*ctx_term_cw+x) * 4;
-#define CHECK_IS_SET \
-      (_ctx_rgba8_manhattan_diff (&pixels[no], rgba[0])< \
-       _ctx_rgba8_manhattan_diff (&pixels[no], rgba[1]))
-
-                int set = CHECK_IS_SET;
-#undef CHECK_IS_SET
-                if (set)
-                  { unicode |=  (1<< (bitno) ); 
-                    pixels_set ++; 
-                  }
-                bitno++;
-              }
-
-
-           if (pixels_set == 6 && brightest_dark_diff < 40)
-             ctx_term_set (term, col +1, row + 1, " ",
-                           rgba[1], rgba[0]);
-           else
-             ctx_term_set (term, col +1, row + 1, sextants[unicode],
-                           rgba[0], rgba[1]);
-        }
-    }
-}
-
-static void ctx_term_output_buf_braille (uint8_t *pixels,
-                          int width,
-                          int height,
-                          CtxTerm *term,
-                          int mono)
-{
-  int reverse = 0;
-  int stride = width * 4;
-  uint8_t black[4] = {0,0,0,255};
-  for (int row = 0; row < height/ctx_term_ch; row++)
-    {
-      for (int col = 0; col < width /ctx_term_cw; col++)
-        {
-          int     unicode = 0;
-          int     bitno = 0;
-          uint8_t rgba[2][4] = {
-                             {255,255,255,0},
-                             {0,0,0,0}};
-
-          ctx_term_find_color_pair (term, col * ctx_term_cw,
-                                    row * ctx_term_ch,
-                                    ctx_term_cw,
-                                    ctx_term_ch, rgba);
-
-
-          /* make darkest consistently be background  */
-          if (_ctx_rgba8_manhattan_diff (black, rgba[1]) >
-              _ctx_rgba8_manhattan_diff (black, rgba[0]))
-          {
-            for (int c = 0; c < 4; c ++)
-            {
-              int tmp = rgba[0][c];
-              rgba[0][c] = rgba[1][c];
-              rgba[1][c] = tmp;
-            }
-          }
-          if (mono)
-          {
-            rgba[1][0] = 0;
-            rgba[1][1] = 0;
-            rgba[1][2] = 0;
-          }
-
-          int pixels_set = 0;
-          for (int x = 0; x < 2; x++)
-            for (int y = 0; y < 3; y++)
-              {
-                int no = (row * 4 + y) * stride + (col*2+x) * 4;
-#define CHECK_IS_SET \
-      (_ctx_rgba8_manhattan_diff (&pixels[no], rgba[0])< \
-       _ctx_rgba8_manhattan_diff (&pixels[no], rgba[1]))
-
-                int set = CHECK_IS_SET;
-                if (reverse) { set = !set; }
-                if (set)
-                  { unicode |=  (1<< (bitno) ); 
-                    pixels_set ++; 
-                  }
-                bitno++;
-              }
-          {
-            int x = 0;
-            int y = 3;
-            int no = (row * 4 + y) * stride + (col*2+x) * 4;
-            int setA = CHECK_IS_SET;
-            no = (row * 4 + y) * stride + (col*2+x+1) * 4;
-            int setB = CHECK_IS_SET;
-
-            pixels_set += setA;
-            pixels_set += setB;
-#undef CHECK_IS_SET
-            if (reverse) { setA = !setA; }
-            if (reverse) { setB = !setB; }
-            if (setA != 0 && setB==0)
-              { unicode += 0x2840; }
-            else if (setA == 0 && setB)
-              { unicode += 0x2880; }
-            else if ( (setA != 0) && (setB != 0) )
-              { unicode += 0x28C0; }
-            else
-              { unicode += 0x2800; }
-            char utf8[5];
-            utf8[ctx_unichar_to_utf8 (unicode, (uint8_t*)utf8)]=0;
-
-#if 0
-            if (pixels_set == 8)
-            {
-              if (rgba[0][0] < 32 && rgba[0][1] < 32 && rgba[0][2] < 32)
-              {
-                ctx_term_set (term, col +1, row + 1, " ",
-                                 rgba[1], rgba[0]);
-                continue;
-              }
-            }
-#endif
-            {
-              ctx_term_set (term, col +1, row + 1, utf8,
-                               rgba[0], rgba[1]);
-            }
-          }
-        }
-    }
-}
-
-
-inline static void ctx_term_render (void *ctx,
-                                       CtxCommand *command)
-{
-  CtxTerm *term = (void*)ctx;
-  /* directly forward */
-  ctx_process (term->host, &command->entry);
-}
-
-inline static void ctx_term_flush (CtxTerm *term)
-{
-  int width =  term->width;
-  int height = term->height;
-  switch (term->mode)
-  {
-    case CTX_TERM_QUARTER:
-       ctx_term_output_buf_quarter (term->pixels,
-                                width, height, term);
-       break;
-    case CTX_TERM_ASCII:
-       ctx_term_output_buf_ascii (term->pixels,
-                                width, height, term, 0);
-       break;
-    case CTX_TERM_ASCII_MONO:
-       ctx_term_output_buf_ascii (term->pixels,
-                                width, height, term, 1);
-       break;
-    case CTX_TERM_SEXTANT:
-       ctx_term_output_buf_sextant (term->pixels,
-                                width, height, term);
-       break;
-    case CTX_TERM_BRAILLE:
-       ctx_term_output_buf_braille (term->pixels,
-                                width, height, term, 0);
-       break;
-    case CTX_TERM_BRAILLE_MONO:
-       ctx_term_output_buf_braille (term->pixels,
-                                width, height, term, 1);
-       break;
-  }
-#if CTX_BRAILLE_TEXT
-  CtxRasterizer *rasterizer = (CtxRasterizer*)(term->host->renderer);
-  // XXX instead sort and inject along with braille
-  //
-
-  //uint8_t rgba_bg[4]={0,0,0,0};
-  //uint8_t rgba_fg[4]={255,0,255,255};
-
-  for (CtxList *l = rasterizer->glyphs; l; l = l->next)
-  {
-    CtxTermGlyph *glyph = l->data;
-
-    uint8_t *pixels = term->pixels;
-    long rgb_sum[4]={0,0,0};
-    for (int v = 0; v <  ctx_term_ch; v ++)
-    for (int u = 0; u <  ctx_term_cw; u ++)
-    {
-      int i = ((glyph->row-1) * ctx_term_ch + v) * rasterizer->blit_width + 
-              ((glyph->col-1) * ctx_term_cw + u);
-      for (int c = 0; c < 3; c ++)
-        rgb_sum[c] += pixels[i*4+c];
-    }
-    for (int c = 0; c < 3; c ++)
-      glyph->rgba_bg[c] = rgb_sum[c] / (ctx_term_ch * ctx_term_cw);
-    char utf8[8];
-    utf8[ctx_unichar_to_utf8(glyph->unichar, (uint8_t*)utf8)]=0;
-    ctx_term_set (term, glyph->col, glyph->row, 
-                     utf8, glyph->rgba_fg, glyph->rgba_bg);
-    free (glyph);
-  }
-
-  printf ("\e[H");
-  printf ("\e[0m");
-  ctx_term_scanout (term);
-  printf ("\e[0m");
-  fflush(NULL);
-  while (rasterizer->glyphs)
-    ctx_list_remove (&rasterizer->glyphs, rasterizer->glyphs->data);
-#endif
-}
-
-void ctx_term_free (CtxTerm *term)
-{
-  while (term->lines)
-  {
-    free (term->lines->data);
-    ctx_list_remove (&term->lines, term->lines->data);
-  }
-  printf ("\e[?25h"); // cursor on
-  nc_at_exit ();
-  free (term->pixels);
-  ctx_free (term->host);
-  free (term);
-  /* we're not destoring the ctx member, this is function is called in ctx' teardown */
-}
-
-int ctx_renderer_is_term (Ctx *ctx)
-{
-  if (ctx->renderer &&
-      ctx->renderer->free == (void*)ctx_term_free)
-          return 1;
-  return 0;
-}
-
-float ctx_term_get_cell_width (Ctx *ctx)
-{
-  return ctx_term_cw;
-}
-
-float ctx_term_get_cell_height (Ctx *ctx)
-{
-  return ctx_term_ch;
-}
-
-Ctx *ctx_new_term (int width, int height)
-{
-  Ctx *ctx = ctx_new ();
-#if CTX_RASTERIZER
-  CtxTerm *term = (CtxTerm*)calloc (sizeof (CtxTerm), 1);
- 
-  const char *mode = getenv ("CTX_TERM_MODE");
-  ctx_term_cw = 2;
-  ctx_term_ch = 3;
-
-  if (!mode) term->mode = CTX_TERM_SEXTANT;
-  else if (!strcmp (mode, "sextant")) term->mode = CTX_TERM_SEXTANT;
-  else if (!strcmp (mode, "ascii")) term->mode = CTX_TERM_ASCII_MONO;
-  //else if (!strcmp (mode, "ascii-mono")) term->mode = CTX_TERM_ASCII_MONO;
-  else if (!strcmp (mode, "quarter")) term->mode = CTX_TERM_QUARTER;
-  //else if (!strcmp (mode, "braille")){
-  //  term->mode = CTX_TERM_BRAILLE;
-  //  ctx_term_ch = 4;
-  //}
-  else if (!strcmp (mode, "braille")){
-    term->mode = CTX_TERM_BRAILLE_MONO;
-    ctx_term_ch = 4;
-  }
-  else {
-    fprintf (stderr, "recognized values for CTX_TERM_MODE:\n"
-                    " sextant ascii quarter braille\n");
-    exit (1);
-  }
-
-  mode = getenv ("CTX_TERM_FORCE_FULL");
-  if (mode && strcmp (mode, "0") && strcmp (mode, "no"))
-    _ctx_term_force_full = 1;
-
-  fprintf (stdout, "\e[?1049h");
-  fprintf (stdout, "\e[?25l"); // cursor off
-
-  int maxwidth = ctx_terminal_cols  () * ctx_term_cw;
-  int maxheight = (ctx_terminal_rows ()) * ctx_term_ch;
-  if (width <= 0 || height <= 0)
-  {
-    width = maxwidth;
-    height = maxheight;
-  }
-  if (width > maxwidth) width = maxwidth;
-  if (height > maxheight) height = maxheight;
-  term->ctx = ctx;
-  term->width  = width;
-  term->height = height;
-
-  term->cols = (width + 1) / ctx_term_cw;
-  term->rows = (height + 2) / ctx_term_ch;
-  term->lines = 0;
-  term->pixels = (uint8_t*)malloc (width * height * 4);
-  term->host = ctx_new_for_framebuffer (term->pixels,
-                                           width, height,
-                                           width * 4, CTX_FORMAT_RGBA8);
-#if CTX_BRAILLE_TEXT
-  ((CtxRasterizer*)term->host->renderer)->term_glyphs=1;
-#endif
-  _ctx_mouse (ctx, NC_MOUSE_DRAG);
-  ctx_set_renderer (ctx, term);
-  ctx_set_size (ctx, width, height);
-  ctx_font_size (ctx, ctx_term_ch); 
-  term->render = ctx_term_render;
-  term->flush = (void(*)(void*))ctx_term_flush;
-  term->free  = (void(*)(void*))ctx_term_free;
-#endif
-
-
-  return ctx;
-}
-
-#endif
-
-#if CTX_EVENTS
-
-#if !__COSMOPOLITAN__
-#include <fcntl.h>
-#include <sys/ioctl.h>
-#endif
-
-typedef struct _CtxTermImg CtxTermImg;
-struct _CtxTermImg
-{
-   void (*render)         (void *termimg, CtxCommand *command);
-   void (*reset)          (void *termimg);
-   void (*flush)          (void *termimg);
-   char *(*get_clipboard) (void *ctxctx);
-   void (*set_clipboard)  (void *ctxctx, const char *text);
-   void (*free)           (void *termimg);
-   Ctx      *ctx;
-   int       width;
-   int       height;
-   int       cols;
-   int       rows;
-   int       was_down;
-   // we need to have the above members in that order up to here
-   uint8_t  *pixels;
-   Ctx      *host;
-   CtxList  *lines;
-};
-
-inline static void ctx_termimg_render (void       *ctx,
-                                       CtxCommand *command)
-{
-  CtxTermImg *termimg = (void*)ctx;
-  /* directly forward */
-  ctx_process (termimg->host, &command->entry);
-}
-
-inline static void ctx_termimg_flush (CtxTermImg *termimg)
-{
-  int width =  termimg->width;
-  int height = termimg->height;
-  if (!termimg->pixels) return;
-  char *encoded = malloc (width * height * 3 * 3);
-  ctx_bin2base64 (termimg->pixels, width * height * 3,
-                  encoded);
-  int encoded_len = strlen (encoded);
-
-  int i = 0;
-
-  printf ("\e[H");
-  printf ("\e_Gf=24,s=%i,v=%i,t=d,a=T,m=1;\e\\", width, height);
-  while (i <  encoded_len)
-  {
-     if (i + 4096 <  encoded_len)
-     {
-       printf  ("\e_Gm=1;");
-     }
-     else
-     {
-       printf  ("\e_Gm=0;");
-     }
-     for (int n = 0; n < 4000 && i < encoded_len; n++)
-     {
-       printf ("%c", encoded[i]);
-       i++;
-     }
-     printf ("\e\\");
-  }
-  free (encoded);
-  
-  fflush (NULL);
-}
-
-void ctx_termimg_free (CtxTermImg *termimg)
-{
-  while (termimg->lines)
-  {
-    free (termimg->lines->data);
-    ctx_list_remove (&termimg->lines, termimg->lines->data);
-  }
-  printf ("\e[?25h"); // cursor on
-  nc_at_exit ();
-  free (termimg->pixels);
-  ctx_free (termimg->host);
-  free (termimg);
-  /* we're not destoring the ctx member, this is function is called in ctx' teardown */
-}
-
-int ctx_renderer_is_termimg (Ctx *ctx)
-{
-  if (ctx->renderer &&
-      ctx->renderer->free == (void*)ctx_termimg_free)
-          return 1;
-  return 0;
-}
-
-Ctx *ctx_new_termimg (int width, int height)
-{
-  Ctx *ctx = ctx_new ();
-#if CTX_RASTERIZER
-  fprintf (stdout, "\e[?1049h");
-  fprintf (stdout, "\e[?25l"); // cursor off
-  CtxTermImg *termimg = (CtxTermImg*)calloc (sizeof (CtxTermImg), 1);
-
-
-  int maxwidth = ctx_terminal_width ();
-
-  int colwidth = maxwidth/ctx_terminal_cols ();
-  maxwidth-=colwidth;
-
-  int maxheight = ctx_terminal_height ();
-  if (width <= 0 || height <= 0)
-  {
-    width  = maxwidth;
-    height = maxheight;
-  }
-  if (width > maxwidth) width = maxwidth;
-  if (height > maxheight) height = maxheight;
-  termimg->ctx = ctx;
-  termimg->width  = width;
-  termimg->height = height;
-  termimg->lines = 0;
-  termimg->pixels = (uint8_t*)malloc (width * height * 3);
-  termimg->host = ctx_new_for_framebuffer (termimg->pixels,
-                                           width, height,
-                                           width * 3, CTX_FORMAT_RGB8);
-  _ctx_mouse (ctx, NC_MOUSE_DRAG);
-  ctx_set_renderer (ctx, termimg);
-  ctx_set_size (ctx, width, height);
-  ctx_font_size (ctx, 14.0f);
-  termimg->render = ctx_termimg_render;
-  termimg->flush = (void(*)(void*))ctx_termimg_flush;
-  termimg->free  = (void(*)(void*))ctx_termimg_free;
-#endif
-
-  return ctx;
-}
-
-#endif
-
-#if CTX_FORMATTER
-
-typedef struct _CtxFormatter  CtxFormatter;
-struct _CtxFormatter 
-{
-  void *target; // FILE
-  int   longform;
-  int   indent;
-
-  void (*add_str)(CtxFormatter *formatter, const char *str, int len);
-};
-
-static inline void ctx_formatter_addstr (CtxFormatter *formatter, const char *str, int len)
-{
-  formatter->add_str (formatter, str, len);
-}
-
-static inline void ctx_formatter_addstrf (CtxFormatter *formatter, const char *format, ...)
-{
-   va_list ap;
-   size_t needed;
-   char *buffer;
-   va_start (ap, format);
-   needed = vsnprintf (NULL, 0, format, ap) + 1;
-   buffer = (char*) malloc (needed);
-   va_end (ap);
-   va_start (ap, format);
-   vsnprintf (buffer, needed, format, ap);
-   va_end (ap);
-   ctx_formatter_addstr (formatter, buffer, -1);
-   free (buffer);
-}
-
-static void _ctx_stream_addstr (CtxFormatter *formatter, const char *str, int len)
-{
-  if (!str || len == 0)
-  {
-    return;
-  }
-  if (len < 0) len = strlen (str);
-  fwrite (str, len, 1, (FILE*)formatter->target);
-}
-
-void _ctx_string_addstr (CtxFormatter *formatter, const char *str, int len)
-{
-  if (!str || len == 0)
-  {
-    return;
-  }
-  if (len < 0) len = strlen (str);
-  ctx_string_append_data ((CtxString*)(formatter->target), str, len);
-}
-
-
-static void _ctx_print_endcmd (CtxFormatter *formatter)
-{
-  if (formatter->longform)
-    {
-      ctx_formatter_addstr (formatter, ");\n", 3);
-    }
-}
-
-static void _ctx_indent (CtxFormatter *formatter)
-{
-  for (int i = 0; i < formatter->indent; i++)
-    { ctx_formatter_addstr (formatter, "  ", 2);
-    }
-}
-
-const char *_ctx_code_to_name (int code)
-{
-      switch (code)
-        {
-          case CTX_REL_LINE_TO_X4:           return "relLinetoX4"; break;
-          case CTX_REL_LINE_TO_REL_CURVE_TO: return "relLineToRelCurveTo"; break;
-          case CTX_REL_CURVE_TO_REL_LINE_TO: return "relCurveToRelLineTo"; break;
-          case CTX_REL_CURVE_TO_REL_MOVE_TO: return "relCurveToRelMoveTo"; break;
-          case CTX_REL_LINE_TO_X2:           return "relLineToX2"; break;
-          case CTX_MOVE_TO_REL_LINE_TO:      return "moveToRelLineTo"; break;
-          case CTX_REL_LINE_TO_REL_MOVE_TO:  return "relLineToRelMoveTo"; break;
-          case CTX_FILL_MOVE_TO:             return "fillMoveTo"; break;
-          case CTX_REL_QUAD_TO_REL_QUAD_TO:  return "relQuadToRelQuadTo"; break;
-          case CTX_REL_QUAD_TO_S16:          return "relQuadToS16"; break;
-
-          case CTX_SET_KEY:              return "setParam"; break;
-          case CTX_COLOR:                return "setColor"; break;
-          case CTX_DEFINE_GLYPH:         return "defineGlyph"; break;
-          case CTX_KERNING_PAIR:         return "kerningPair"; break;
-          case CTX_SET_PIXEL:            return "setPixel"; break;
-          case CTX_GLOBAL_ALPHA:         return "globalAlpha"; break;
-          case CTX_TEXT:                 return "text"; break;
-          case CTX_STROKE_TEXT:          return "strokeText"; break;
-          case CTX_SAVE:                 return "save"; break;
-          case CTX_RESTORE:              return "restore"; break;
-          case CTX_STROKE_SOURCE:        return "strokeSource"; break;
-          case CTX_NEW_PAGE:             return "newPage"; break;
-          case CTX_START_GROUP:          return "startGroup"; break;
-          case CTX_END_GROUP:            return "endGroup"; break;
-          case CTX_RECTANGLE:            return "rectangle"; break;
-          case CTX_ROUND_RECTANGLE:      return "roundRectangle"; break;
-          case CTX_LINEAR_GRADIENT:      return "linearGradient"; break;
-          case CTX_RADIAL_GRADIENT:      return "radialGradient"; break;
-          case CTX_GRADIENT_STOP:        return "gradientAddStop"; break;
-          case CTX_VIEW_BOX:             return "viewBox"; break;
-          case CTX_MOVE_TO:              return "moveTo"; break;
-          case CTX_LINE_TO:              return "lineTo"; break;
-          case CTX_BEGIN_PATH:           return "beginPath"; break;
-          case CTX_REL_MOVE_TO:          return "relMoveTo"; break;
-          case CTX_REL_LINE_TO:          return "relLineTo"; break;
-          case CTX_FILL:                 return "fill"; break;
-          case CTX_EXIT:                 return "exit"; break;
-          case CTX_APPLY_TRANSFORM:      return "transform"; break;
-          case CTX_SOURCE_TRANSFORM:     return "sourceTransform"; break;
-          case CTX_REL_ARC_TO:           return "relArcTo"; break;
-          case CTX_GLYPH:                return "glyph"; break;
-          case CTX_TEXTURE:              return "texture"; break;
-          case CTX_DEFINE_TEXTURE:       return "defineTexture"; break;
-          case CTX_IDENTITY:             return "identity"; break;
-          case CTX_CLOSE_PATH:           return "closePath"; break;
-          case CTX_PRESERVE:             return "preserve"; break;
-          case CTX_FLUSH:                return "flush"; break;
-          case CTX_RESET:                return "reset"; break;
-          case CTX_FONT:                 return "font"; break;
-          case CTX_STROKE:               return "stroke"; break;
-          case CTX_CLIP:                 return "clip"; break;
-          case CTX_ARC:                  return "arc"; break;
-          case CTX_SCALE:                return "scale"; break;
-          case CTX_TRANSLATE:            return "translate"; break;
-          case CTX_ROTATE:               return "rotate"; break;
-          case CTX_ARC_TO:               return "arcTo"; break;
-          case CTX_CURVE_TO:             return "curveTo"; break;
-          case CTX_REL_CURVE_TO:         return "relCurveTo"; break;
-          case CTX_REL_QUAD_TO:          return "relQuadTo"; break;
-          case CTX_QUAD_TO:              return "quadTo"; break;
-          case CTX_SMOOTH_TO:            return "smoothTo"; break;
-          case CTX_REL_SMOOTH_TO:        return "relSmoothTo"; break;
-          case CTX_SMOOTHQ_TO:           return "smoothqTo"; break;
-          case CTX_REL_SMOOTHQ_TO:       return "relSmoothqTo"; break;
-          case CTX_HOR_LINE_TO:          return "horLineTo"; break;
-          case CTX_VER_LINE_TO:          return "verLineTo"; break;
-          case CTX_REL_HOR_LINE_TO:      return "relHorLineTo"; break;
-          case CTX_REL_VER_LINE_TO:      return "relVerLineTo"; break;
-          case CTX_COMPOSITING_MODE:     return "compositingMode"; break;
-          case CTX_BLEND_MODE:           return "blendMode"; break;
-          case CTX_TEXT_ALIGN:           return "textAlign"; break;
-          case CTX_TEXT_BASELINE:        return "textBaseline"; break;
-          case CTX_TEXT_DIRECTION:       return "textDirection"; break;
-          case CTX_FONT_SIZE:            return "fontSize"; break;
-          case CTX_MITER_LIMIT:          return "miterLimit"; break;
-          case CTX_LINE_JOIN:            return "lineJoin"; break;
-          case CTX_LINE_CAP:             return "lineCap"; break;
-          case CTX_LINE_WIDTH:           return "lineWidth"; break;
-          case CTX_LINE_DASH_OFFSET:     return "lineDashOffset"; break;
-          case CTX_IMAGE_SMOOTHING:      return "imageSmoothing"; break;
-          case CTX_SHADOW_BLUR:          return "shadowBlur";  break;
-          case CTX_FILL_RULE:            return "fillRule"; break;
-        }
-      return NULL;
-}
-
-static void _ctx_print_name (CtxFormatter *formatter, int code)
-{
-#define CTX_VERBOSE_NAMES 1
-#if CTX_VERBOSE_NAMES
-  if (formatter->longform)
-    {
-      const char *name = NULL;
-      _ctx_indent (formatter);
-      //switch ((CtxCode)code)
-      name = _ctx_code_to_name (code);
-      if (name)
-        {
-          ctx_formatter_addstr (formatter, name, -1);
-          ctx_formatter_addstr (formatter, " (", 2);
-          if (code == CTX_SAVE)
-            { formatter->indent ++; }
-          else if (code == CTX_RESTORE)
-            { formatter->indent --; }
-          return;
-        }
-    }
-#endif
-  {
-    char name[3];
-    name[0]=CTX_SET_KEY;
-    name[2]='\0';
-    switch (code)
-      {
-        case CTX_GLOBAL_ALPHA:      name[1]='a'; break;
-        case CTX_COMPOSITING_MODE:  name[1]='m'; break;
-        case CTX_BLEND_MODE:        name[1]='B'; break;
-        case CTX_TEXT_ALIGN:        name[1]='t'; break;
-        case CTX_TEXT_BASELINE:     name[1]='b'; break;
-        case CTX_TEXT_DIRECTION:    name[1]='d'; break;
-        case CTX_FONT_SIZE:         name[1]='f'; break;
-        case CTX_MITER_LIMIT:       name[1]='l'; break;
-        case CTX_LINE_JOIN:         name[1]='j'; break;
-        case CTX_LINE_CAP:          name[1]='c'; break;
-        case CTX_LINE_WIDTH:        name[1]='w'; break;
-        case CTX_LINE_DASH_OFFSET:  name[1]='D'; break;
-        case CTX_IMAGE_SMOOTHING:   name[1]='S'; break;
-        case CTX_SHADOW_BLUR:       name[1]='s'; break;
-        case CTX_SHADOW_COLOR:      name[1]='C'; break;
-        case CTX_SHADOW_OFFSET_X:   name[1]='x'; break;
-        case CTX_SHADOW_OFFSET_Y:   name[1]='y'; break;
-        case CTX_FILL_RULE:         name[1]='r'; break;
-        default:
-          name[0] = code;
-          name[1] = 0;
-          break;
-      }
-    ctx_formatter_addstr (formatter, name, -1);
-    if (formatter->longform)
-      ctx_formatter_addstr (formatter, " (", 2);
-    else
-      ctx_formatter_addstr (formatter, " ", 1);
-  }
-}
-
-static void
-ctx_print_entry_enum (CtxFormatter *formatter, CtxEntry *entry, int args)
-{
-  _ctx_print_name (formatter, entry->code);
-  for (int i = 0; i <  args; i ++)
-    {
-      int val = ctx_arg_u8 (i);
-      if (i>0)
-        { 
-          ctx_formatter_addstr (formatter, " ", 1);
-        }
-#if CTX_VERBOSE_NAMES
-      if (formatter->longform)
-        {
-          const char *str = NULL;
-          switch (entry->code)
-            {
-              case CTX_TEXT_BASELINE:
-                switch (val)
-                  {
-                    case CTX_TEXT_BASELINE_ALPHABETIC: str = "alphabetic"; break;
-                    case CTX_TEXT_BASELINE_TOP:        str = "top";        break;
-                    case CTX_TEXT_BASELINE_BOTTOM:     str = "bottom";     break;
-                    case CTX_TEXT_BASELINE_HANGING:    str = "hanging";    break;
-                    case CTX_TEXT_BASELINE_MIDDLE:     str = "middle";     break;
-                    case CTX_TEXT_BASELINE_IDEOGRAPHIC:str = "ideographic";break;
-                  }
-                break;
-              case CTX_TEXT_ALIGN:
-                switch (val)
-                  {
-                    case CTX_TEXT_ALIGN_LEFT:   str = "left"; break;
-                    case CTX_TEXT_ALIGN_RIGHT:  str = "right"; break;
-                    case CTX_TEXT_ALIGN_START:  str = "start"; break;
-                    case CTX_TEXT_ALIGN_END:    str = "end"; break;
-                    case CTX_TEXT_ALIGN_CENTER: str = "center"; break;
-                  }
-                break;
-              case CTX_LINE_CAP:
-                switch (val)
-                  {
-                    case CTX_CAP_NONE:   str = "none"; break;
-                    case CTX_CAP_ROUND:  str = "round"; break;
-                    case CTX_CAP_SQUARE: str = "square"; break;
-                  }
-                break;
-              case CTX_LINE_JOIN:
-                switch (val)
-                  {
-                    case CTX_JOIN_MITER: str = "miter"; break;
-                    case CTX_JOIN_ROUND: str = "round"; break;
-                    case CTX_JOIN_BEVEL: str = "bevel"; break;
-                  }
-                break;
-              case CTX_FILL_RULE:
-                switch (val)
-                  {
-                    case CTX_FILL_RULE_WINDING:  str = "winding"; break;
-                    case CTX_FILL_RULE_EVEN_ODD: str = "evenodd"; break;
-                  }
-                break;
-              case CTX_BLEND_MODE:
-                val = ctx_arg_u32 (i);
-                switch (val)
-                  {
-            case CTX_BLEND_NORMAL:      str = "normal"; break;
-            case CTX_BLEND_MULTIPLY:    str = "multiply"; break;
-            case CTX_BLEND_SCREEN:      str = "screen"; break;
-            case CTX_BLEND_OVERLAY:     str = "overlay"; break;
-            case CTX_BLEND_DARKEN:      str = "darken"; break;
-            case CTX_BLEND_LIGHTEN:     str = "lighten"; break;
-            case CTX_BLEND_COLOR_DODGE: str = "colorDodge"; break;
-            case CTX_BLEND_COLOR_BURN:  str = "colorBurn"; break;
-            case CTX_BLEND_HARD_LIGHT:  str = "hardLight"; break;
-            case CTX_BLEND_SOFT_LIGHT:  str = "softLight"; break;
-            case CTX_BLEND_DIFFERENCE:  str = "difference"; break;
-            case CTX_BLEND_EXCLUSION:   str = "exclusion"; break;
-            case CTX_BLEND_HUE:         str = "hue"; break;
-            case CTX_BLEND_SATURATION:  str = "saturation"; break;
-            case CTX_BLEND_COLOR:       str = "color"; break; 
-            case CTX_BLEND_LUMINOSITY:  str = "luminosity"; break;
-                  }
-                break;
-              case CTX_COMPOSITING_MODE:
-                val = ctx_arg_u32 (i);
-                switch (val)
-                  {
-              case CTX_COMPOSITE_SOURCE_OVER: str = "sourceOver"; break;
-              case CTX_COMPOSITE_COPY: str = "copy"; break;
-              case CTX_COMPOSITE_CLEAR: str = "clear"; break;
-              case CTX_COMPOSITE_SOURCE_IN: str = "sourceIn"; break;
-              case CTX_COMPOSITE_SOURCE_OUT: str = "sourceOut"; break;
-              case CTX_COMPOSITE_SOURCE_ATOP: str = "sourceAtop"; break;
-              case CTX_COMPOSITE_DESTINATION: str = "destination"; break;
-              case CTX_COMPOSITE_DESTINATION_OVER: str = "destinationOver"; break;
-              case CTX_COMPOSITE_DESTINATION_IN: str = "destinationIn"; break;
-              case CTX_COMPOSITE_DESTINATION_OUT: str = "destinationOut"; break;
-              case CTX_COMPOSITE_DESTINATION_ATOP: str = "destinationAtop"; break;
-              case CTX_COMPOSITE_XOR: str = "xor"; break;
-                  }
-
-               break;
-            }
-          if (str)
-            {
-              ctx_formatter_addstr (formatter, str, -1);
-            }
-          else
-            {
-              ctx_formatter_addstrf (formatter, "%i", val);
-            }
-        }
-      else
-#endif
-        {
-          ctx_formatter_addstrf (formatter, "%i", val);
-        }
-    }
-  _ctx_print_endcmd (formatter);
-}
-
-
-static void
-ctx_print_a85 (CtxFormatter *formatter, uint8_t *data, int length)
-{
-  char *tmp = (char*)malloc (ctx_a85enc_len (length));
-  ctx_a85enc (data, tmp, length);
-  ctx_formatter_addstr (formatter, " ~", 2);
-  ctx_formatter_addstr (formatter, tmp, -1);
-  ctx_formatter_addstr (formatter, "~ ", 2);
-  free (tmp);
-}
-
-static void
-ctx_print_yenc (CtxFormatter *formatter, uint8_t *data, int length)
-{
-  char *tmp = (char*)malloc (length * 2 + 2);// worst case scenario
-  int enclength = ctx_yenc ((char*)data, tmp, length);
-  data[enclength]=0;
-  ctx_formatter_addstr (formatter, " =", 2);
-  ctx_formatter_addstr (formatter, tmp, enclength);
-  ctx_formatter_addstr (formatter, "=y ", 2);
-  free (tmp);
-}
-
-static void
-ctx_print_escaped_string (CtxFormatter *formatter, const char *string)
-{
-  if (!string) { return; }
-  for (int i = 0; string[i]; i++)
-    {
-      switch (string[i])
-        {
-          case '"':
-            ctx_formatter_addstr (formatter, "\\\"", 2);
-            break;
-          case '\\':
-            ctx_formatter_addstr (formatter, "\\\\", 2);
-            break;
-          case '\n':
-            ctx_formatter_addstr (formatter, "\\n", 2);
-            break;
-          default:
-            ctx_formatter_addstr (formatter, &string[i], 1);
-        }
-    }
-}
-
-static void
-ctx_print_float (CtxFormatter *formatter, float val)
-{
-  char temp[128];
-  sprintf (temp, "%0.3f", val);
-  int j;
-  for (j = 0; temp[j]; j++)
-    if (j == ',') { temp[j] = '.'; }
-  j--;
-  if (j>0)
-    while (temp[j] == '0')
-      {
-        temp[j]=0;
-        j--;
-      }
-  if (temp[j]=='.')
-    { temp[j]='\0'; }
-  ctx_formatter_addstr (formatter, temp, -1);
-}
-
-static void
-ctx_print_int (CtxFormatter *formatter, int val)
-{
-  ctx_formatter_addstrf (formatter, "%i", val);
-}
-
-static void
-ctx_print_entry (CtxFormatter *formatter, CtxEntry *entry, int args)
-{
-  _ctx_print_name (formatter, entry->code);
-  for (int i = 0; i <  args; i ++)
-    {
-      float val = ctx_arg_float (i);
-      if (i>0 && val >= 0.0f)
-        {
-          if (formatter->longform)
-            {
-              ctx_formatter_addstr (formatter, ", ", 2);
-            }
-          else
-            {
-              if (val >= 0.0f)
-                ctx_formatter_addstr (formatter, " ", 1);
-            }
-        }
-      ctx_print_float (formatter, val);
-    }
-  _ctx_print_endcmd (formatter);
-}
-
-static void
-ctx_print_glyph (CtxFormatter *formatter, CtxEntry *entry, int args)
-{
-  _ctx_print_name (formatter, entry->code);
-  ctx_formatter_addstrf (formatter, "%i", entry->data.u32[0]);
-  _ctx_print_endcmd (formatter);
-}
-
-static void
-ctx_formatter_process (void *user_data, CtxCommand *c);
-
-
-static void
-ctx_formatter_process (void *user_data, CtxCommand *c)
-{
-  CtxEntry *entry = &c->entry;
-  CtxFormatter *formatter = (CtxFormatter*)user_data;
-
-  switch (entry->code)
-  //switch ((CtxCode)(entry->code))
-    {
-      case CTX_GLYPH:
-        ctx_print_glyph (formatter, entry, 1);
-        break;
-      case CTX_LINE_TO:
-      case CTX_REL_LINE_TO:
-      case CTX_SCALE:
-      case CTX_TRANSLATE:
-      case CTX_MOVE_TO:
-      case CTX_REL_MOVE_TO:
-      case CTX_SMOOTHQ_TO:
-      case CTX_REL_SMOOTHQ_TO:
-        ctx_print_entry (formatter, entry, 2);
-        break;
-      case CTX_TEXTURE:
-        _ctx_print_name (formatter, entry->code);
-        ctx_formatter_addstrf (formatter, "\"");
-        ctx_print_escaped_string (formatter, c->texture.eid);
-        ctx_formatter_addstrf (formatter, "\", ");
-        ctx_print_float (formatter, c->texture.x);
-        ctx_formatter_addstrf (formatter, ", ");
-        ctx_print_float (formatter, c->texture.y);
-        ctx_formatter_addstrf (formatter, " ");
-        _ctx_print_endcmd (formatter);
-        break;
-
-      case CTX_DEFINE_TEXTURE:
-        {
-        _ctx_print_name (formatter, entry->code);
-        ctx_formatter_addstrf (formatter, "\"");
-        ctx_print_escaped_string (formatter, c->define_texture.eid);
-        ctx_formatter_addstrf (formatter, "\", ");
-        ctx_print_int (formatter, c->define_texture.width);
-        ctx_formatter_addstrf (formatter, ", ");
-        ctx_print_int (formatter, c->define_texture.height);
-        ctx_formatter_addstrf (formatter, ",%i, ", c->define_texture.format);
-
-        uint8_t *pixel_data = ctx_define_texture_pixel_data (entry);
-#if 1
-
-        int stride = ctx_pixel_format_get_stride ((CtxPixelFormat)c->define_texture.format, 
c->define_texture.width);
-        int data_len = stride * c->define_texture.height;
-        if (c->define_texture.format == CTX_FORMAT_YUV420)
-          data_len = c->define_texture.height * c->define_texture.width +
-                       2*(c->define_texture.height/2) * (c->define_texture.width/2);
-        //fprintf (stderr, "encoding %i bytes\n", c->define_texture.height *stride);
-        //ctx_print_a85 (formatter, pixel_data, c->define_texture.height * stride);
-        ctx_print_yenc (formatter, pixel_data, data_len);
-#else
-        ctx_formatter_addstrf (formatter, "\"");
-        ctx_print_escaped_string (formatter, pixel_data);
-        ctx_formatter_addstrf (formatter, "\" ");
-
-#endif
-
-        _ctx_print_endcmd (formatter);
-        }
-        break;
-
-      case CTX_REL_ARC_TO:
-      case CTX_ARC_TO:
-      case CTX_ROUND_RECTANGLE:
-        ctx_print_entry (formatter, entry, 5);
-        break;
-      case CTX_CURVE_TO:
-      case CTX_REL_CURVE_TO:
-      case CTX_ARC:
-      case CTX_RADIAL_GRADIENT:
-      case CTX_APPLY_TRANSFORM:
-      case CTX_SOURCE_TRANSFORM:
-        ctx_print_entry (formatter, entry, 6);
-        break;
-      case CTX_QUAD_TO:
-      case CTX_RECTANGLE:
-      case CTX_REL_QUAD_TO:
-      case CTX_LINEAR_GRADIENT:
-      case CTX_VIEW_BOX:
-      case CTX_SMOOTH_TO:
-      case CTX_REL_SMOOTH_TO:
-        ctx_print_entry (formatter, entry, 4);
-        break;
-      case CTX_FONT_SIZE:
-      case CTX_MITER_LIMIT:
-      case CTX_ROTATE:
-      case CTX_LINE_WIDTH:
-      case CTX_LINE_DASH_OFFSET:
-      case CTX_GLOBAL_ALPHA:
-      case CTX_SHADOW_BLUR:
-      case CTX_SHADOW_OFFSET_X:
-      case CTX_SHADOW_OFFSET_Y:
-      case CTX_VER_LINE_TO:
-      case CTX_HOR_LINE_TO:
-      case CTX_REL_VER_LINE_TO:
-      case CTX_REL_HOR_LINE_TO:
-        ctx_print_entry (formatter, entry, 1);
-        break;
-#if 0
-      case CTX_SET:
-        _ctx_print_name (formatter, entry->code);
-        switch (c->set.key_hash)
-        {
-           case CTX_x: ctx_formatter_addstrf (formatter, " 'x' "); break;
-           case CTX_y: ctx_formatter_addstrf (formatter, " 'y' "); break;
-           case CTX_width: ctx_formatter_addstrf (formatter, " width "); break;
-           case CTX_height: ctx_formatter_addstrf (formatter, " height "); break;
-           default:
-             ctx_formatter_addstrf (formatter, " %d ", c->set.key_hash);
-        }
-        ctx_formatter_addstrf (formatter, "\"");
-        ctx_print_escaped_string (formatter, (char*)c->set.utf8);
-        ctx_formatter_addstrf (formatter, "\"");
-        _ctx_print_endcmd (formatter);
-        break;
-#endif
-      case CTX_COLOR:
-        if (formatter->longform ||  1)
-          {
-            _ctx_indent (formatter);
-            int model = (int) c->set_color.model;
-            const char *suffix="";
-            if (model & 512)
-            {
-              model = model & 511;
-              suffix = "S";
-            }
-            switch (model)
-              {
-                case CTX_GRAY:
-                  ctx_formatter_addstrf (formatter, "gray%s ", suffix);
-                  ctx_print_float (formatter, c->graya.g);
-                  break;
-                case CTX_GRAYA:
-                  ctx_formatter_addstrf (formatter, "graya%s ", suffix);
-                  ctx_print_float (formatter, c->graya.g);
-                  ctx_formatter_addstrf (formatter, " ");
-                  ctx_print_float (formatter, c->graya.a);
-                  break;
-                case CTX_RGBA:
-                  if (c->rgba.a != 1.0)
-                  {
-                    ctx_formatter_addstrf (formatter, "rgba%s ", suffix);
-                    ctx_print_float (formatter, c->rgba.r);
-                    ctx_formatter_addstrf (formatter, " ");
-                    ctx_print_float (formatter, c->rgba.g);
-                    ctx_formatter_addstrf (formatter, " ");
-                    ctx_print_float (formatter, c->rgba.b);
-                    ctx_formatter_addstrf (formatter, " ");
-                    ctx_print_float (formatter, c->rgba.a);
-                    break;
-                  }
-                  /* FALLTHROUGH */
-                case CTX_RGB:
-                  if (c->rgba.r == c->rgba.g && c->rgba.g == c->rgba.b)
-                  {
-                    ctx_formatter_addstrf (formatter, "gray%s ", suffix);
-                    ctx_print_float (formatter, c->rgba.r);
-                    ctx_formatter_addstrf (formatter, " ");
-                    break;
-                  }
-                  ctx_formatter_addstrf (formatter, "rgb%s ", suffix);
-                  ctx_print_float (formatter, c->rgba.r);
-                  ctx_formatter_addstrf (formatter, " ");
-                  ctx_print_float (formatter, c->rgba.g);
-                  ctx_formatter_addstrf (formatter, " ");
-                  ctx_print_float (formatter, c->rgba.b);
-                  break;
-                case CTX_DRGB:
-                  ctx_formatter_addstrf (formatter, "drgb%s ", suffix);
-                  ctx_print_float (formatter, c->rgba.r);
-                  ctx_formatter_addstrf (formatter, " ");
-                  ctx_print_float (formatter, c->rgba.g);
-                  ctx_formatter_addstrf (formatter, " ");
-                  ctx_print_float (formatter, c->rgba.b);
-                  break;
-                case CTX_DRGBA:
-                  ctx_formatter_addstrf (formatter, "drgba%s ", suffix);
-                  ctx_print_float (formatter, c->rgba.r);
-                  ctx_formatter_addstrf (formatter, " ");
-                  ctx_print_float (formatter, c->rgba.g);
-                  ctx_formatter_addstrf (formatter, " ");
-                  ctx_print_float (formatter, c->rgba.b);
-                  ctx_formatter_addstrf (formatter, " ");
-                  ctx_print_float (formatter, c->rgba.a);
-                  break;
-                case CTX_CMYK:
-                  ctx_formatter_addstrf (formatter, "cmyk%s ", suffix);
-                  ctx_print_float (formatter, c->cmyka.c);
-                  ctx_formatter_addstrf (formatter, " ");
-                  ctx_print_float (formatter, c->cmyka.m);
-                  ctx_formatter_addstrf (formatter, " ");
-                  ctx_print_float (formatter, c->cmyka.y);
-                  ctx_formatter_addstrf (formatter, " ");
-                  ctx_print_float (formatter, c->cmyka.k);
-                  break;
-                case CTX_CMYKA:
-                  ctx_formatter_addstrf (formatter, "cmyka%s ", suffix);
-                  ctx_print_float (formatter, c->cmyka.c);
-                  ctx_formatter_addstrf (formatter, " ");
-                  ctx_print_float (formatter, c->cmyka.m);
-                  ctx_formatter_addstrf (formatter, " ");
-                  ctx_print_float (formatter, c->cmyka.y);
-                  ctx_formatter_addstrf (formatter, " ");
-                  ctx_print_float (formatter, c->cmyka.k);
-                  ctx_formatter_addstrf (formatter, " ");
-                  ctx_print_float (formatter, c->cmyka.a);
-                  break;
-                case CTX_DCMYK:
-                  ctx_formatter_addstrf (formatter, "dcmyk%s ", suffix);
-                  ctx_print_float (formatter, c->cmyka.c);
-                  ctx_formatter_addstrf (formatter, " ");
-                  ctx_print_float (formatter, c->cmyka.m);
-                  ctx_formatter_addstrf (formatter, " ");
-                  ctx_print_float (formatter, c->cmyka.y);
-                  ctx_formatter_addstrf (formatter, " ");
-                  ctx_print_float (formatter, c->cmyka.k);
-                  break;
-                case CTX_DCMYKA:
-                  ctx_formatter_addstrf (formatter, "dcmyka%s ", suffix);
-                  ctx_print_float (formatter, c->cmyka.c);
-                  ctx_formatter_addstrf (formatter, " ");
-                  ctx_print_float (formatter, c->cmyka.m);
-                  ctx_formatter_addstrf (formatter, " ");
-                  ctx_print_float (formatter, c->cmyka.y);
-                  ctx_formatter_addstrf (formatter, " ");
-                  ctx_print_float (formatter, c->cmyka.k);
-                  ctx_formatter_addstrf (formatter, " ");
-                  ctx_print_float (formatter, c->cmyka.a);
-                  break;
-              }
-          }
-        else
-          {
-            ctx_print_entry (formatter, entry, 1);
-          }
-        break;
-      case CTX_SET_RGBA_U8:
-        if (formatter->longform)
-          {
-            _ctx_indent (formatter);
-            ctx_formatter_addstrf (formatter, "rgba (");
-          }
-        else
-          {
-            ctx_formatter_addstrf (formatter, "rgba (");
-          }
-        for (int c = 0; c < 4; c++)
-          {
-            if (c)
-              {
-                if (formatter->longform)
-                  ctx_formatter_addstrf (formatter, ", ");
-                else
-                  ctx_formatter_addstrf (formatter, " ");
-              }
-            ctx_print_float (formatter, ctx_u8_to_float (ctx_arg_u8 (c) ) );
-          }
-        _ctx_print_endcmd (formatter);
-        break;
-      case CTX_SET_PIXEL:
-#if 0
-        ctx_set_pixel_u8 (d_ctx,
-                          ctx_arg_u16 (2), ctx_arg_u16 (3),
-                          ctx_arg_u8 (0),
-                          ctx_arg_u8 (1),
-                          ctx_arg_u8 (2),
-                          ctx_arg_u8 (3) );
-#endif
-        break;
-      case CTX_FILL:
-      case CTX_RESET:
-      case CTX_STROKE:
-      case CTX_IDENTITY:
-      case CTX_CLIP:
-      case CTX_BEGIN_PATH:
-      case CTX_CLOSE_PATH:
-      case CTX_SAVE:
-      case CTX_PRESERVE:
-      case CTX_START_GROUP:
-      case CTX_NEW_PAGE:
-      case CTX_END_GROUP:
-      case CTX_RESTORE:
-      case CTX_STROKE_SOURCE:
-        ctx_print_entry (formatter, entry, 0);
-        break;
-      case CTX_TEXT_ALIGN:
-      case CTX_TEXT_BASELINE:
-      case CTX_TEXT_DIRECTION:
-      case CTX_FILL_RULE:
-      case CTX_LINE_CAP:
-      case CTX_LINE_JOIN:
-      case CTX_COMPOSITING_MODE:
-      case CTX_BLEND_MODE:
-      case CTX_IMAGE_SMOOTHING:
-        ctx_print_entry_enum (formatter, entry, 1);
-        break;
-      case CTX_GRADIENT_STOP:
-        _ctx_print_name (formatter, entry->code);
-        for (int c = 0; c < 4; c++)
-          {
-            if (c)
-              ctx_formatter_addstrf (formatter, " ");
-            ctx_print_float (formatter, ctx_u8_to_float (ctx_arg_u8 (4+c) ) );
-          }
-        _ctx_print_endcmd (formatter);
-        break;
-      case CTX_TEXT:
-      case CTX_STROKE_TEXT:
-      case CTX_FONT:
-        _ctx_print_name (formatter, entry->code);
-        ctx_formatter_addstrf (formatter, "\"");
-        ctx_print_escaped_string (formatter, ctx_arg_string() );
-        ctx_formatter_addstrf (formatter, "\"");
-        _ctx_print_endcmd (formatter);
-        break;
-      case CTX_CONT:
-      case CTX_EDGE:
-      case CTX_DATA:
-      case CTX_DATA_REV:
-      case CTX_FLUSH:
-        break;
-      case CTX_KERNING_PAIR:
-        _ctx_print_name (formatter, entry->code);
-        ctx_formatter_addstrf (formatter, "\"");
-        {
-           uint8_t utf8[16];
-           utf8[ctx_unichar_to_utf8 (c->kern.glyph_before, utf8)]=0;
-           ctx_print_escaped_string (formatter, (char*)utf8);
-           ctx_formatter_addstrf (formatter, "\", \"");
-           utf8[ctx_unichar_to_utf8 (c->kern.glyph_after, utf8)]=0;
-           ctx_print_escaped_string (formatter, (char*)utf8);
-           ctx_formatter_addstrf (formatter, "\"");
-           sprintf ((char*)utf8, ", %f", c->kern.amount / 256.0);
-           ctx_print_escaped_string (formatter, (char*)utf8);
-        }
-        _ctx_print_endcmd (formatter);
-        break;
-
-      case CTX_DEFINE_GLYPH:
-        _ctx_print_name (formatter, entry->code);
-        ctx_formatter_addstrf (formatter, "\"");
-        {
-           uint8_t utf8[16];
-           utf8[ctx_unichar_to_utf8 (entry->data.u32[0], utf8)]=0;
-           ctx_print_escaped_string (formatter, (char*)utf8);
-           ctx_formatter_addstrf (formatter, "\"");
-           sprintf ((char*)utf8, ", %f", entry->data.u32[1]/256.0);
-           ctx_print_escaped_string (formatter, (char*)utf8);
-        }
-        _ctx_print_endcmd (formatter);
-        break;
-    }
-}
-
-void
-ctx_render_stream (Ctx *ctx, FILE *stream, int longform)
-{
-  CtxIterator iterator;
-  CtxFormatter formatter;
-  formatter.target= stream;
-  formatter.longform = longform;
-  formatter.indent = 0;
-  formatter.add_str = _ctx_stream_addstr;
-  CtxCommand *command;
-  ctx_iterator_init (&iterator, &ctx->drawlist, 0,
-                     CTX_ITERATOR_EXPAND_BITPACK);
-  while ( (command = ctx_iterator_next (&iterator) ) )
-    { ctx_formatter_process (&formatter, command); }
-  fprintf (stream, "\n");
-}
-
-char *
-ctx_render_string (Ctx *ctx, int longform, int *retlen)
-{
-  CtxString *string = ctx_string_new ("");
-  CtxIterator iterator;
-  CtxFormatter formatter;
-  formatter.target= string;
-  formatter.longform = longform;
-  formatter.indent = 0;
-  formatter.add_str = _ctx_string_addstr;
-  CtxCommand *command;
-  ctx_iterator_init (&iterator, &ctx->drawlist, 0,
-                     CTX_ITERATOR_EXPAND_BITPACK);
-  while ( (command = ctx_iterator_next (&iterator) ) )
-    { ctx_formatter_process (&formatter, command); }
-  char *ret = string->str;
-  if (retlen)
-    *retlen = string->length;
-  ctx_string_free (string, 0);
-  return ret;
-}
-
-
-#endif
-
-#if CTX_EVENTS
-int ctx_width (Ctx *ctx)
-{
-  return ctx->events.width;
-}
-int ctx_height (Ctx *ctx)
-{
-  return ctx->events.height;
-}
-#else
-int ctx_width (Ctx *ctx)
-{
-  return 512;
-}
-int ctx_height (Ctx *ctx)
-{
-  return 384;
-}
-#endif
-
-int ctx_rev (Ctx *ctx)
-{
-  return ctx->rev;
-}
-
-CtxState *ctx_get_state (Ctx *ctx)
-{
-  return &ctx->state;
-}
-
-void ctx_dirty_rect (Ctx *ctx, int *x, int *y, int *width, int *height)
-{
-  if ( (ctx->state.min_x > ctx->state.max_x) ||
-       (ctx->state.min_y > ctx->state.max_y) )
-    {
-      if (x) { *x = 0; }
-      if (y) { *y = 0; }
-      if (width) { *width = 0; }
-      if (height) { *height = 0; }
-      return;
-    }
-  if (ctx->state.min_x < 0)
-    { ctx->state.min_x = 0; }
-  if (ctx->state.min_y < 0)
-    { ctx->state.min_y = 0; }
-  if (x) { *x = ctx->state.min_x; }
-  if (y) { *y = ctx->state.min_y; }
-  if (width) { *width = ctx->state.max_x - ctx->state.min_x; }
-  if (height) { *height = ctx->state.max_y - ctx->state.min_y; }
-}
-
-#if CTX_CURRENT_PATH
-CtxIterator *
-ctx_current_path (Ctx *ctx)
-{
-  CtxIterator *iterator = &ctx->current_path_iterator;
-  ctx_iterator_init (iterator, &ctx->current_path, 0, CTX_ITERATOR_EXPAND_BITPACK);
-  return iterator;
-}
-
-void
-ctx_path_extents (Ctx *ctx, float *ex1, float *ey1, float *ex2, float *ey2)
-{
-  float minx = 50000.0;
-  float miny = 50000.0;
-  float maxx = -50000.0;
-  float maxy = -50000.0;
-  float x = 0;
-  float y = 0;
-
-  CtxIterator *iterator = ctx_current_path (ctx);
-  CtxCommand *command;
-
-  while ((command = ctx_iterator_next (iterator)))
-  {
-     int got_coord = 0;
-     switch (command->code)
-     {
-        // XXX missing many curve types
-        case CTX_LINE_TO:
-        case CTX_MOVE_TO:
-          x = command->move_to.x;
-          y = command->move_to.y;
-          got_coord++;
-          break;
-        case CTX_REL_LINE_TO:
-        case CTX_REL_MOVE_TO:
-          x += command->move_to.x;
-          y += command->move_to.y;
-          got_coord++;
-          break;
-        case CTX_CURVE_TO:
-          x = command->curve_to.x;
-          y = command->curve_to.y;
-          got_coord++;
-          break;
-        case CTX_REL_CURVE_TO:
-          x += command->curve_to.x;
-          y += command->curve_to.y;
-          got_coord++;
-          break;
-        case CTX_ARC:
-          minx = ctx_minf (minx, command->arc.x - command->arc.radius);
-          miny = ctx_minf (miny, command->arc.y - command->arc.radius);
-          maxx = ctx_maxf (maxx, command->arc.x + command->arc.radius);
-          maxy = ctx_maxf (maxy, command->arc.y + command->arc.radius);
-
-          break;
-        case CTX_RECTANGLE:
-        case CTX_ROUND_RECTANGLE:
-          x = command->rectangle.x;
-          y = command->rectangle.y;
-          minx = ctx_minf (minx, x);
-          miny = ctx_minf (miny, y);
-          maxx = ctx_maxf (maxx, x);
-          maxy = ctx_maxf (maxy, y);
-
-          x += command->rectangle.width;
-          y += command->rectangle.height;
-          got_coord++;
-          break;
-     }
-    if (got_coord)
-    {
-      minx = ctx_minf (minx, x);
-      miny = ctx_minf (miny, y);
-      maxx = ctx_maxf (maxx, x);
-      maxy = ctx_maxf (maxy, y);
-    }
-  }
-
-  if (ex1) *ex1 = minx;
-  if (ey1) *ey1 = miny;
-  if (ex2) *ex2 = maxx;
-  if (ey2) *ey2 = maxy;
-}
-
-#else
-void
-ctx_path_extents (Ctx *ctx, float *ex1, float *ey1, float *ex2, float *ey2)
-{
-}
-#endif
-
-
-static inline void
-ctx_gstate_push (CtxState *state)
-{
-  if (state->gstate_no + 1 >= CTX_MAX_STATES)
-    { return; }
-  state->gstate_stack[state->gstate_no] = state->gstate;
-  state->gstate_no++;
-  ctx_state_set (state, CTX_new_state, 0.0);
-  state->has_clipped=0;
-}
-
-static inline void
-ctx_gstate_pop (CtxState *state)
-{
-  if (state->gstate_no <= 0)
-    { return; }
-  state->gstate = state->gstate_stack[state->gstate_no-1];
-  state->gstate_no--;
-}
-
-void
-ctx_close_path (Ctx *ctx)
-{
-  CTX_PROCESS_VOID (CTX_CLOSE_PATH);
-}
-
-int _ctx_is_rasterizer (Ctx *ctx);
-
-void
-ctx_get_image_data (Ctx *ctx, int sx, int sy, int sw, int sh,
-                    CtxPixelFormat format, int dst_stride,
-                    uint8_t *dst_data)
-{
-   if (0)
-   {
-   }
-#if CTX_RASTERIZER
-   else if (_ctx_is_rasterizer (ctx))
-   {
-     CtxRasterizer *rasterizer = (CtxRasterizer*)ctx->renderer;
-     if (rasterizer->format->pixel_format == format)
-     {
-       if (dst_stride <= 0) dst_stride = ctx_pixel_format_get_stride (format, sw);
-       int bytes_per_pix = rasterizer->format->bpp/8;
-       int y = 0;
-       for (int v = sy; v < sy + sh; v++, y++)
-       {
-         int x = 0;
-         for (int u = sx; u < sx + sw; u++, x++)
-         {
-            uint8_t* src_buf = (uint8_t*)rasterizer->buf;
-            memcpy (&dst_data[y * dst_stride + x * bytes_per_pix], &src_buf[v * rasterizer->blit_stride + u 
* bytes_per_pix], bytes_per_pix);
-         }
-       }
-       return;
-     }
-   }
-#endif
-#if CTX_FB
-   else if (format == CTX_FORMAT_RGBA8 &&
-                   (
-                   ctx_renderer_is_fb (ctx)
-#if CTX_SDL
-                   || ctx_renderer_is_sdl (ctx)
-#endif
-                   ))
-   {
-     CtxTiled *tiled = (CtxTiled*)ctx->renderer;
-     {
-       if (dst_stride <= 0) dst_stride = ctx_pixel_format_get_stride (format, sw);
-       int bytes_per_pix = 4;
-       int y = 0;
-       for (int v = sy; v < sy + sh; v++, y++)
-       {
-         int x = 0;
-         for (int u = sx; u < sx + sw; u++, x++)
-         {
-            uint8_t* src_buf = (uint8_t*)tiled->pixels;
-            memcpy (&dst_data[y * dst_stride + x * bytes_per_pix], &src_buf[v * tiled->width * bytes_per_pix 
+ u * bytes_per_pix], bytes_per_pix);
-         }
-       }
-       return;
-     }
-   }
-#endif
-}
-
-void
-ctx_put_image_data (Ctx *ctx, int w, int h, int stride, int format,
-                    uint8_t *data,
-                    int ox, int oy,
-                    int dirtyX, int dirtyY,
-                    int dirtyWidth, int dirtyHeight)
-{
-   char eid[65]="";
-   ctx_save (ctx);
-   ctx_identity (ctx);
-   ctx_define_texture (ctx, NULL, w, h, stride, format, data, eid);
-   if (eid[0])
-   {
-     // XXX set compositor to source
-     ctx_compositing_mode (ctx, CTX_COMPOSITE_COPY);
-     ctx_draw_texture_clipped (ctx, eid, ox, oy, w, h, dirtyX, dirtyY, dirtyWidth, dirtyHeight);
-   }
-   ctx_restore (ctx);
-}
-
-/* checking if an eid is valid also sets the frame for it
- */
-static int ctx_eid_valid (Ctx *ctx, const char *eid, int *w, int *h)
-{
-  ctx = ctx->texture_cache;
-  CtxList *to_remove = NULL;
-  int ret = 0;
-  //fprintf (stderr, "{%i}\n", ctx->frame);
-  for (CtxList *l = ctx->eid_db; l; l = l->next)
-  {
-    CtxEidInfo *eid_info = (CtxEidInfo*)l->data;
-    if (ctx->frame - eid_info->frame >= 2)
-            /* XXX XXX XXX this breaks texture caching since
-             *   it is wrong in some cases where more frames
-             *   have passed?
-             */
-    {
-      ctx_list_prepend (&to_remove, eid_info);
-    }
-    else if (!strcmp (eid_info->eid, eid) &&
-             ctx->frame - eid_info->frame < 2)
-    {
-    //fclose (f);
-      eid_info->frame = ctx->frame;
-      if (w) *w = eid_info->width;
-      if (h) *h = eid_info->height;
-      ret = 1;
-    }
-  }
-  while (to_remove)
-  {
-    CtxEidInfo *eid_info = (CtxEidInfo*)to_remove->data;
-    //FILE  *f  = fopen ("/tmp/l", "a");
-    //fprintf (f, "%i client removing %s\n", getpid(), eid_info->eid);
-    //fclose (f);
-    free (eid_info->eid);
-    free (eid_info);
-    ctx_list_remove (&ctx->eid_db, eid_info);
-    ctx_list_remove (&to_remove, eid_info);
-  }
-  return ret;
-}
-
-void ctx_texture (Ctx *ctx, const char *eid, float x, float y)
-{
-  int eid_len = strlen (eid);
-  char ascii[41]="";
-  //fprintf (stderr, "tx %s\n", eid);
-  if (eid_len > 50)
-  {
-    CtxSHA1 *sha1 = ctx_sha1_new ();
-    uint8_t hash[20]="";
-    ctx_sha1_process (sha1, (uint8_t*)eid, eid_len);
-    ctx_sha1_done (sha1, hash);
-    ctx_sha1_free (sha1);
-    const char *hex="0123456789abcdef";
-    for (int i = 0; i < 20; i ++)
-    {
-       ascii[i*2]=hex[hash[i]/16];
-       ascii[i*2+1]=hex[hash[i]%16];
-    }
-    ascii[40]=0;
-    eid=ascii;
-  }
-
-    //FILE  *f = fopen ("/tmp/l", "a");
-  if (ctx_eid_valid (ctx, eid, 0, 0))
-  {
-    ctx_process_cmd_str_float (ctx, CTX_TEXTURE, eid, x, y);
-    //fprintf (stderr, "setting texture eid %s\n", eid);
-  }
-  else
-  {
-    //fprintf (stderr, "tried setting invalid texture eid %s\n", eid);
-  }
-    //fclose (f);
-}
-int
-_ctx_frame (Ctx *ctx)
-{
-   return ctx->frame;
-}
-int
-_ctx_set_frame (Ctx *ctx, int frame)
-{
-   return ctx->frame = frame;
-}
-
-void ctx_define_texture (Ctx *ctx,
-                         const char *eid,
-                         int width, int height, int stride, int format, void *data,
-                         char *ret_eid)
-{
-  uint8_t hash[20]="";
-  char ascii[41]="";
-  int dst_stride = width;
-  //fprintf (stderr, "df %s\n", eid);
-
-  dst_stride = ctx_pixel_format_get_stride ((CtxPixelFormat)format, width);
-  if (stride <= 0)
-    stride = dst_stride;
-
-  int data_len;
- 
-  if (format == CTX_FORMAT_YUV420)
-  data_len = width * height + ((width/2) * (height/2)) * 2;
-  else
-  data_len = height * dst_stride;
-
-  if (eid == NULL)
-  {
-    CtxSHA1 *sha1 = ctx_sha1_new ();
-    uint8_t *src = (uint8_t*)data;
-    for (int y = 0; y < height; y++)
-    {
-       ctx_sha1_process (sha1, src, dst_stride);
-       src += stride;
-    }
-    ctx_sha1_done (sha1, hash);
-    ctx_sha1_free (sha1);
-    const char *hex="0123456789abcdef";
-    for (int i = 0; i < 20; i ++)
-    {
-       ascii[i*2]  =hex[hash[i]/16];
-       ascii[i*2+1]=hex[hash[i]%16];
-    }
-    ascii[40]=0;
-    eid = ascii;
-  }
-
-  int eid_len = strlen (eid);
-
-  if (eid_len > 50)
-  {
-    CtxSHA1 *sha1 = ctx_sha1_new ();
-    uint8_t hash[20]="";
-    ctx_sha1_process (sha1, (uint8_t*)eid, eid_len);
-    ctx_sha1_done (sha1, hash);
-    ctx_sha1_free (sha1);
-    const char *hex="0123456789abcdef";
-    for (int i = 0; i < 20; i ++)
-    {
-       ascii[i*2]  =hex[hash[i]/16];
-       ascii[i*2+1]=hex[hash[i]%16];
-    }
-    ascii[40]=0;
-    eid = ascii;
-    eid_len = 40;
-  }
-
-  // we now have eid
-
-  if (ctx_eid_valid (ctx, eid, 0, 0))
-  {
-    ctx_texture (ctx, eid, 0.0, 0.0);
-  }
-  else
-
-  {
-    CtxEntry *commands;
-    int command_size = 1 + (data_len+1+1)/9 + 1 + (eid_len+1+1)/9 + 1 +   8;
-    if (ctx->renderer && ctx->renderer->process)
-    {
-       commands = (CtxEntry*)calloc (sizeof (CtxEntry), command_size);
-    }
-    else
-    {
-       commands = NULL;
-       ctx_drawlist_resize (&ctx->drawlist, ctx->drawlist.count + command_size);
-       commands = &(ctx->drawlist.entries[ctx->drawlist.count]);
-       memset (commands, 0, sizeof (CtxEntry) * command_size);
-    }
-    /* bottleneck,  we can avoid copying sometimes - and even when copying
-     * we should cut this down to one copy, direct to the drawlist.
-     *
-     */
-    commands[0] = ctx_u32 (CTX_DEFINE_TEXTURE, width, height);
-    commands[1].data.u16[0] = format;
-
-    int pos = 2;
-
-    commands[pos].code        = CTX_DATA;
-    commands[pos].data.u32[0] = eid_len;
-    commands[pos].data.u32[1] = (eid_len+1+1)/9 + 1;
-    memcpy ((char *) &commands[pos+1].data.u8[0], eid, eid_len);
-    ((char *) &commands[pos+1].data.u8[0])[eid_len]=0;
-
-    pos = 2 + 1 + ctx_conts_for_entry (&commands[2]);
-    commands[pos].code        = CTX_DATA;
-    commands[pos].data.u32[0] = data_len;
-    commands[pos].data.u32[1] = (data_len+1+1)/9 + 1;
-    {
-      uint8_t *src = (uint8_t*)data;
-      uint8_t *dst = &commands[pos+1].data.u8[0];
-#if 1
-      memcpy (dst, src, data_len);
-#else
-      for (int y = 0; y < height; y++)
-      {
-         memcpy (dst, src, dst_stride);
-         src += stride;
-         dst += dst_stride;
-      }
-#endif
-    }
-    ((char *) &commands[pos+1].data.u8[0])[data_len]=0;
-
-    if (ctx->renderer && ctx->renderer->process)
-    {
-      ctx_process (ctx, commands);
-      free (commands);
-    }
-    else
-    {
-       ctx->drawlist.count += ctx_conts_for_entry (commands) + 1;
-    }
-
-    CtxEidInfo *eid_info = (CtxEidInfo*)calloc (sizeof (CtxEidInfo), 1);
-    eid_info->width      = width;
-    eid_info->height     = height;
-    eid_info->frame      = ctx->texture_cache->frame;
-    //fprintf (stderr, "%i\n", eid_info->frame);
-    eid_info->eid        = strdup (eid);
-    ctx_list_prepend (&ctx->texture_cache->eid_db, eid_info);
-  }
-
-  if (ret_eid)
-  {
-    strcpy (ret_eid, eid);
-    ret_eid[64]=0;
-  }
-}
-
-void
-ctx_texture_load (Ctx *ctx, const char *path, int *tw, int *th, char *reid)
-{
-  const char *eid = path;
-  char ascii[41]="";
-  int eid_len = strlen (eid);
-  if (eid_len > 50)
-  {
-    CtxSHA1 *sha1 = ctx_sha1_new ();
-    uint8_t hash[20]="";
-    ctx_sha1_process (sha1, (uint8_t*)eid, eid_len);
-    ctx_sha1_done (sha1, hash);
-    ctx_sha1_free (sha1);
-    const char *hex="0123456789abcdef";
-    for (int i = 0; i < 20; i ++)
-    {
-       ascii[i*2]=hex[hash[i]/16];
-       ascii[i*2+1]=hex[hash[i]%16];
-    }
-    ascii[40]=0;
-    eid = ascii;
-  }
-
-  if (ctx_eid_valid (ctx, eid , tw, th))
-  {
-     if (reid)
-     {
-       strcpy (reid, eid);
-     }
-     return;
-  }
-
-#ifdef STBI_INCLUDE_STB_IMAGE_H
-  CtxPixelFormat pixel_format = CTX_FORMAT_RGBA8;
-  int w, h, components;
-  unsigned char *pixels = NULL;
-
-  if (!strncmp (path, "file://", 7))
-  {
-    pixels = stbi_load (path + 7, &w, &h, &components, 0);
-  }
-  else
-  {
-    unsigned char *data = NULL;
-    long length = 0;
-    ctx_get_contents (path, &data, &length);
-    if (data)
-    {
-       pixels = stbi_load_from_memory (data, length, &w, &h, &components, 0);
-       free (data);
-    }
-  }
-
-  if (pixels)
-  {
-    switch (components)
-    {
-      case 1: pixel_format = CTX_FORMAT_GRAY8;  break;
-      case 2: pixel_format = CTX_FORMAT_GRAYA8; break;
-      case 3: pixel_format = CTX_FORMAT_RGB8;   break;
-      case 4: pixel_format = CTX_FORMAT_RGBA8;  break;
-    }
-    if (tw) *tw = w;
-    if (th) *th = h;
-    ctx_define_texture (ctx, eid, w, h, w * components, pixel_format, pixels, reid);
-    free (pixels);
-  }
-  else
-  {
-    fprintf (stderr, "texture loading problem for %s\n", path);
-  }
-#endif
-}
-
-void
-ctx_draw_texture_clipped  (Ctx *ctx, const char *eid,
-                           float x, float y,
-                           float width, float height,
-                           float clip_x, float clip_y,
-                           float clip_width, float clip_height)
-{
-  int tex_width  = 0;
-  int tex_height = 0;
-  if (ctx_eid_valid (ctx, eid , &tex_width, &tex_height))
-  {
-    if (width > 0.0 && height > 0.0)
-    {
-#if 0
-      if (clip_width > 0.0f)
-      {
-        ctx_rectangle (ctx, clip_x, clip_y, clip_width, clip_height);
-        ctx_clip (ctx);
-      }
-#endif
-      ctx_rectangle (ctx, x, y, width, height);
-      CtxMatrix matrix;
-      ctx_matrix_identity (&matrix);
-      
-      ctx_texture (ctx, eid, 0, 0);// / (width/tex_width), y / (height/tex_height));
-      //ctx_rgba (ctx, 1, 0,0,0.5);
-#if 1
-      if (clip_width > 0.0f)
-      {
-              // XXX scale is not yet determined to be correct
-              // in relation to the translate!
-        ctx_matrix_scale (&matrix, clip_width/width, clip_height/height);
-        ctx_matrix_translate (&matrix, -clip_x, -clip_y);
-      }
-      else
-      {
-        ctx_matrix_scale (&matrix, tex_width/width, tex_height/height);
-      }
-      ctx_matrix_translate (&matrix, x, y);
-#endif
-      //ctx_matrix_invert (&matrix);
-      ctx_source_transform_matrix (ctx, &matrix);
-      //ctx_texture (ctx, eid, x / (width/tex_width), y / (height/tex_height));
-      ctx_fill (ctx);
-    }
-  }
-}
-
-void ctx_draw_texture (Ctx *ctx, const char *eid, float x, float y, float w, float h)
-{
-  ctx_draw_texture_clipped (ctx, eid, x, y, w, h, 0,0,0,0);
-}
-
-void ctx_draw_image_clipped (Ctx *ctx, const char *path, float x, float y, float w, float h, float sx, float 
sy, float swidth, float sheight)
-{
-  char reteid[65];
-  int width, height;
-  ctx_texture_load (ctx, path, &width, &height, reteid);
-  if (reteid[0])
-  {
-    ctx_draw_texture_clipped (ctx, reteid, x, y, w, h, sx, sy, swidth, sheight);
-  }
-}
-
-void
-ctx_draw_image (Ctx *ctx, const char *path, float x, float y, float w, float h)
-{
-  ctx_draw_image_clipped (ctx, path, x, y, w, h, 0,0,0,0);
-}
-
-void
-ctx_set_pixel_u8 (Ctx *ctx, uint16_t x, uint16_t y, uint8_t r, uint8_t g, uint8_t b, uint8_t a)
-{
-  CtxEntry command = ctx_u8 (CTX_SET_PIXEL, r, g, b, a, 0, 0, 0, 0);
-  command.data.u16[2]=x;
-  command.data.u16[3]=y;
-  ctx_process (ctx, &command);
-}
-
-void
-ctx_linear_gradient (Ctx *ctx, float x0, float y0, float x1, float y1)
-{
-  CtxEntry command[2]=
-  {
-    ctx_f (CTX_LINEAR_GRADIENT, x0, y0),
-    ctx_f (CTX_CONT,            x1, y1)
-  };
-  ctx_process (ctx, command);
-}
-
-void
-ctx_radial_gradient (Ctx *ctx, float x0, float y0, float r0, float x1, float y1, float r1)
-{
-  CtxEntry command[3]=
-  {
-    ctx_f (CTX_RADIAL_GRADIENT, x0, y0),
-    ctx_f (CTX_CONT,            r0, x1),
-    ctx_f (CTX_CONT,            y1, r1)
-  };
-  ctx_process (ctx, command);
-}
-
-void ctx_preserve (Ctx *ctx)
-{
-  CTX_PROCESS_VOID (CTX_PRESERVE);
-}
-
-void ctx_fill (Ctx *ctx)
-{
-  CTX_PROCESS_VOID (CTX_FILL);
-}
-
-void ctx_stroke (Ctx *ctx)
-{
-  CTX_PROCESS_VOID (CTX_STROKE);
-}
-
-
-static void ctx_empty (Ctx *ctx)
-{
-#if CTX_RASTERIZER
-  if (ctx->renderer == NULL)
-#endif
-    {
-      ctx->drawlist.count = 0;
-      ctx->drawlist.bitpack_pos = 0;
-    }
-}
-
-void _ctx_set_store_clear (Ctx *ctx)
-{
-  ctx->transformation |= CTX_TRANSFORMATION_STORE_CLEAR;
-}
-
-#if CTX_EVENTS
-static void
-ctx_event_free (void *event, void *user_data)
-{
-  free (event);
-}
-
-static void
-ctx_collect_events (CtxEvent *event, void *data, void *data2)
-{
-  Ctx *ctx = (Ctx*)data;
-  CtxEvent *copy;
-  if (event->type == CTX_KEY_PRESS && !strcmp (event->string, "idle"))
-    return;
-  copy = (CtxEvent*)malloc (sizeof (CtxEvent));
-  *copy = *event;
-  if (copy->string)
-    copy->string = strdup (event->string);
-  ctx_list_append_full (&ctx->events.events, copy, ctx_event_free, NULL);
-}
-#endif
-
-#if CTX_EVENTS
-static void _ctx_bindings_key_press (CtxEvent *event, void *data1, void *data2);
-#endif
-
-void ctx_reset (Ctx *ctx)
-{
-        /* we do the callback reset first - maybe we need two cbs,
-         * one for before and one after default impl?
-         *
-         * tiled fb and sdl needs to sync
-         */
-  if (ctx->renderer && ctx->renderer->reset)
-    ctx->renderer->reset (ctx->renderer);
-
-  //CTX_PROCESS_VOID (CTX_RESET);
-  //if (ctx->transformation & CTX_TRANSFORMATION_STORE_CLEAR)
-  //  { return; }
-  ctx_empty (ctx);
-  ctx_state_init (&ctx->state);
-#if CTX_EVENTS
-  ctx_list_free (&ctx->events.items);
-  ctx->events.last_item = NULL;
-
-  if (ctx->events.ctx_get_event_enabled)
-  {
-    ctx_clear_bindings (ctx);
-    ctx_listen_full (ctx, 0,0,0,0,
-                     CTX_KEY_PRESS, _ctx_bindings_key_press, ctx, ctx,
-                     NULL, NULL);
-
-    ctx_listen_full (ctx, 0,0,0,0,
-                     CTX_KEY_UP, ctx_collect_events, ctx, ctx,
-                     NULL, NULL);
-    ctx_listen_full (ctx, 0,0,0,0,
-                     CTX_KEY_DOWN, ctx_collect_events, ctx, ctx,
-                     NULL, NULL);
-
-    ctx_listen_full (ctx, 0, 0, ctx->events.width, ctx->events.height,
-                     (CtxEventType)(CTX_PRESS|CTX_RELEASE|CTX_MOTION),
-                     ctx_collect_events, ctx, ctx,
-                     NULL, NULL);
-  }
-#endif
-}
-
-void ctx_begin_path (Ctx *ctx)
-{
-  CTX_PROCESS_VOID (CTX_BEGIN_PATH);
-}
-
-void ctx_clip (Ctx *ctx)
-{
-  CTX_PROCESS_VOID (CTX_CLIP);
-}
-
-void
-ctx_set (Ctx *ctx, uint64_t key_hash, const char *string, int len);
-
-void ctx_save (Ctx *ctx)
-{
-  CTX_PROCESS_VOID (CTX_SAVE);
-}
-void ctx_restore (Ctx *ctx)
-{
-  CTX_PROCESS_VOID (CTX_RESTORE);
-}
-
-void ctx_start_group (Ctx *ctx)
-{
-  CTX_PROCESS_VOID (CTX_START_GROUP);
-}
-
-void ctx_end_group (Ctx *ctx)
-{
-  CTX_PROCESS_VOID (CTX_END_GROUP);
-}
-
-void ctx_line_width (Ctx *ctx, float x)
-{
-  if (ctx->state.gstate.line_width != x)
-    CTX_PROCESS_F1 (CTX_LINE_WIDTH, x);
-}
-
-float ctx_get_miter_limit (Ctx *ctx)
-{
-  return ctx->state.gstate.miter_limit;
-}
-
-float ctx_get_line_dash_offset (Ctx *ctx)
-{
-  return ctx->state.gstate.line_dash_offset;
-}
-
-void ctx_line_dash_offset (Ctx *ctx, float x)
-{
-  if (ctx->state.gstate.line_dash_offset != x)
-    CTX_PROCESS_F1 (CTX_LINE_DASH_OFFSET, x);
-}
-
-int ctx_get_image_smoothing (Ctx *ctx)
-{
-  return ctx->state.gstate.image_smoothing;
-}
-
-void ctx_image_smoothing (Ctx *ctx, int enabled)
-{
-  if (ctx_get_image_smoothing (ctx) != enabled)
-    CTX_PROCESS_U8 (CTX_IMAGE_SMOOTHING, enabled);
-}
-
-
-void ctx_line_dash (Ctx *ctx, float *dashes, int count)
-{
-  ctx_process_cmd_str_with_len (ctx, CTX_LINE_DASH, (char*)(dashes), count, 0, count * 4);
-}
-
-void ctx_shadow_blur (Ctx *ctx, float x)
-{
-#if CTX_ENABLE_SHADOW_BLUR
-  if (ctx->state.gstate.shadow_blur != x)
-#endif
-    CTX_PROCESS_F1 (CTX_SHADOW_BLUR, x);
-}
-
-void ctx_shadow_rgba (Ctx *ctx, float r, float g, float b, float a)
-{
-  CtxEntry command[3]=
-  {
-    ctx_f (CTX_SHADOW_COLOR, CTX_RGBA, r),
-    ctx_f (CTX_CONT, g, b),
-    ctx_f (CTX_CONT, a, 0)
-  };
-  ctx_process (ctx, command);
-}
-
-void ctx_shadow_offset_x (Ctx *ctx, float x)
-{
-#if CTX_ENABLE_SHADOW_BLUR
-  if (ctx->state.gstate.shadow_offset_x != x)
-#endif
-    CTX_PROCESS_F1 (CTX_SHADOW_OFFSET_X, x);
-}
-
-void ctx_shadow_offset_y (Ctx *ctx, float x)
-{
-#if CTX_ENABLE_SHADOW_BLUR
-  if (ctx->state.gstate.shadow_offset_y != x)
-#endif
-    CTX_PROCESS_F1 (CTX_SHADOW_OFFSET_Y, x);
-}
-
-void
-ctx_global_alpha (Ctx *ctx, float global_alpha)
-{
-  if (ctx->state.gstate.global_alpha_f != global_alpha)
-    CTX_PROCESS_F1 (CTX_GLOBAL_ALPHA, global_alpha);
-}
-
-float
-ctx_get_global_alpha (Ctx *ctx)
-{
-  return ctx->state.gstate.global_alpha_f;
-}
-
-void
-ctx_font_size (Ctx *ctx, float x)
-{
-  CTX_PROCESS_F1 (CTX_FONT_SIZE, x);
-}
-
-float ctx_get_font_size  (Ctx *ctx)
-{
-  return ctx->state.gstate.font_size;
-}
-
-void
-ctx_miter_limit (Ctx *ctx, float limit)
-{
-  CTX_PROCESS_F1 (CTX_MITER_LIMIT, limit);
-}
-
-float ctx_get_line_width (Ctx *ctx)
-{
-  return ctx->state.gstate.line_width;
-}
-
-void
-_ctx_font (Ctx *ctx, const char *name)
-{
-  ctx->state.gstate.font = ctx_resolve_font (name);
-}
-
-#if 0
-void
-ctx_set (Ctx *ctx, uint64_t key_hash, const char *string, int len)
-{
-  if (len <= 0) len = strlen (string);
-  ctx_process_cmd_str (ctx, CTX_SET, string, key_hash, len);
-}
-
-const char *
-ctx_get (Ctx *ctx, const char *key)
-{
-  static char retbuf[32];
-  int len = 0;
-  CTX_PROCESS_U32(CTX_GET, ctx_strhash (key), 0);
-  while (read (STDIN_FILENO, &retbuf[len], 1) != -1)
-    {
-      if(retbuf[len]=='\n')
-        break;
-      retbuf[++len]=0;
-    }
-  return retbuf;
-}
-#endif
-
-void
-ctx_font_family (Ctx *ctx, const char *name)
-{
-#if CTX_BACKEND_TEXT
-  ctx_process_cmd_str (ctx, CTX_FONT, name, 0, 0);
-  _ctx_font (ctx, name);
-#else
-  _ctx_font (ctx, name);
-#endif
-}
-
-void
-ctx_font (Ctx *ctx, const char *family_name)
-{
-  // should also parse size
-  ctx_font_family (ctx, family_name);
-}
-
-const char *
-ctx_get_font (Ctx *ctx)
-{
-  return ctx_fonts[ctx->state.gstate.font].name;
-}
-
-void ctx_line_to (Ctx *ctx, float x, float y)
-{
-  if (CTX_UNLIKELY(!ctx->state.has_moved))
-    { CTX_PROCESS_F (CTX_MOVE_TO, x, y); }
-  else
-    { CTX_PROCESS_F (CTX_LINE_TO, x, y); }
-}
-
-void ctx_move_to (Ctx *ctx, float x, float y)
-{
-  CTX_PROCESS_F (CTX_MOVE_TO,x,y);
-}
-
-void ctx_curve_to (Ctx *ctx, float x0, float y0,
-                   float x1, float y1,
-                   float x2, float y2)
-{
-  CtxEntry command[3]=
-  {
-    ctx_f (CTX_CURVE_TO, x0, y0),
-    ctx_f (CTX_CONT,     x1, y1),
-    ctx_f (CTX_CONT,     x2, y2)
-  };
-  ctx_process (ctx, command);
-}
-
-void ctx_round_rectangle (Ctx *ctx,
-                          float x0, float y0,
-                          float w, float h,
-                          float radius)
-{
-  CtxEntry command[3]=
-  {
-    ctx_f (CTX_ROUND_RECTANGLE, x0, y0),
-    ctx_f (CTX_CONT,            w, h),
-    ctx_f (CTX_CONT,            radius, 0)
-  };
-  ctx_process (ctx, command);
-}
-
-void ctx_view_box (Ctx *ctx,
-                   float x0, float y0,
-                   float w, float h)
-{
-  CtxEntry command[3]=
-  {
-    ctx_f (CTX_VIEW_BOX, x0, y0),
-    ctx_f (CTX_CONT,     w, h)
-  };
-  ctx_process (ctx, command);
-}
-
-void ctx_rectangle (Ctx *ctx,
-                    float x0, float y0,
-                    float w, float h)
-{
-  CtxEntry command[3]=
-  {
-    ctx_f (CTX_RECTANGLE, x0, y0),
-    ctx_f (CTX_CONT,      w, h)
-  };
-  ctx_process (ctx, command);
-}
-
-void ctx_rel_line_to (Ctx *ctx, float x, float y)
-{
-  if (!ctx->state.has_moved)
-    { return; }
-  CTX_PROCESS_F (CTX_REL_LINE_TO,x,y);
-}
-
-void ctx_rel_move_to (Ctx *ctx, float x, float y)
-{
-  if (!ctx->state.has_moved)
-    {
-      CTX_PROCESS_F (CTX_MOVE_TO,x,y);
-      return;
-    }
-  CTX_PROCESS_F (CTX_REL_MOVE_TO,x,y);
-}
-
-CtxLineJoin ctx_get_line_join (Ctx *ctx)
-{
-  return ctx->state.gstate.line_join;
-}
-
-CtxCompositingMode ctx_get_compositing_mode (Ctx *ctx)
-{
-  return ctx->state.gstate.compositing_mode;
-}
-
-CtxBlend ctx_get_blend_mode (Ctx *ctx)
-{
-  return ctx->state.gstate.blend_mode;
-}
-
-CtxTextAlign ctx_get_text_align  (Ctx *ctx)
-{
-  return (CtxTextAlign)ctx_state_get (&ctx->state, CTX_text_align);
-}
-
-CtxTextBaseline ctx_get_text_baseline (Ctx *ctx)
-{
-  return (CtxTextBaseline)ctx_state_get (&ctx->state, CTX_text_baseline);
-}
-
-CtxLineCap ctx_get_line_cap (Ctx *ctx)
-{
-  return ctx->state.gstate.line_cap;
-}
-
-CtxFillRule ctx_get_fill_rule (Ctx *ctx)
-{
-  return ctx->state.gstate.fill_rule;
-}
-
-void ctx_line_cap (Ctx *ctx, CtxLineCap cap)
-{
-  if (ctx->state.gstate.line_cap != cap)
-    CTX_PROCESS_U8 (CTX_LINE_CAP, cap);
-}
-
-void ctx_fill_rule (Ctx *ctx, CtxFillRule fill_rule)
-{
-  if (ctx->state.gstate.fill_rule != fill_rule)
-    CTX_PROCESS_U8 (CTX_FILL_RULE, fill_rule);
-}
-void ctx_line_join (Ctx *ctx, CtxLineJoin join)
-{
-  if (ctx->state.gstate.line_join != join)
-    CTX_PROCESS_U8 (CTX_LINE_JOIN, join);
-}
-void ctx_blend_mode (Ctx *ctx, CtxBlend mode)
-{
-  if (ctx->state.gstate.blend_mode != mode)
-    CTX_PROCESS_U32 (CTX_BLEND_MODE, mode, 0);
-}
-void ctx_compositing_mode (Ctx *ctx, CtxCompositingMode mode)
-{
-  if (ctx->state.gstate.compositing_mode != mode)
-    CTX_PROCESS_U32 (CTX_COMPOSITING_MODE, mode, 0);
-}
-void ctx_text_align (Ctx *ctx, CtxTextAlign text_align)
-{
-  CTX_PROCESS_U8 (CTX_TEXT_ALIGN, text_align);
-}
-void ctx_text_baseline (Ctx *ctx, CtxTextBaseline text_baseline)
-{
-  CTX_PROCESS_U8 (CTX_TEXT_BASELINE, text_baseline);
-}
-void ctx_text_direction (Ctx *ctx, CtxTextDirection text_direction)
-{
-  CTX_PROCESS_U8 (CTX_TEXT_DIRECTION, text_direction);
-}
-
-void
-ctx_rel_curve_to (Ctx *ctx,
-                  float x0, float y0,
-                  float x1, float y1,
-                  float x2, float y2)
-{
-  if (!ctx->state.has_moved)
-    { return; }
-  CtxEntry command[3]=
-  {
-    ctx_f (CTX_REL_CURVE_TO, x0, y0),
-    ctx_f (CTX_CONT, x1, y1),
-    ctx_f (CTX_CONT, x2, y2)
-  };
-  ctx_process (ctx, command);
-}
-
-void
-ctx_rel_quad_to (Ctx *ctx,
-                 float cx, float cy,
-                 float x,  float y)
-{
-  if (!ctx->state.has_moved)
-    { return; }
-  CtxEntry command[2]=
-  {
-    ctx_f (CTX_REL_QUAD_TO, cx, cy),
-    ctx_f (CTX_CONT, x, y)
-  };
-  ctx_process (ctx, command);
-}
-
-void
-ctx_quad_to (Ctx *ctx,
-             float cx, float cy,
-             float x,  float y)
-{
-  if (!ctx->state.has_moved)
-    { return; }
-  CtxEntry command[2]=
-  {
-    ctx_f (CTX_QUAD_TO, cx, cy),
-    ctx_f (CTX_CONT, x, y)
-  };
-  ctx_process (ctx, command);
-}
-
-void ctx_arc (Ctx  *ctx,
-              float x0, float y0,
-              float radius,
-              float angle1, float angle2,
-              int   direction)
-{
-  CtxEntry command[3]=
-  {
-    ctx_f (CTX_ARC, x0, y0),
-    ctx_f (CTX_CONT, radius, angle1),
-    ctx_f (CTX_CONT, angle2, direction)
-  };
-  ctx_process (ctx, command);
-}
-
-static int ctx_coords_equal (float x1, float y1, float x2, float y2, float tol)
-{
-  float dx = x2 - x1;
-  float dy = y2 - y1;
-  return dx*dx + dy*dy < tol*tol;
-}
-
-static float
-ctx_point_seg_dist_sq (float x, float y,
-                       float vx, float vy, float wx, float wy)
-{
-  float l2 = ctx_pow2 (vx-wx) + ctx_pow2 (vy-wy);
-  if (l2 < 0.0001)
-    { return ctx_pow2 (x-vx) + ctx_pow2 (y-vy); }
-  float t = ( (x - vx) * (wx - vx) + (y - vy) * (wy - vy) ) / l2;
-  t = ctx_maxf (0, ctx_minf (1, t) );
-  float ix = vx + t * (wx - vx);
-  float iy = vy + t * (wy - vy);
-  return ctx_pow2 (x-ix) + ctx_pow2 (y-iy);
-}
-
-static void
-ctx_normalize (float *x, float *y)
-{
-  float length = ctx_hypotf ( (*x), (*y) );
-  if (length > 1e-6f)
-    {
-      float r = 1.0f / length;
-      *x *= r;
-      *y *= r;
-    }
-}
-
-void
-ctx_arc_to (Ctx *ctx, float x1, float y1, float x2, float y2, float radius)
-{
-  // XXX : should partially move into rasterizer to preserve comand
-  //       even if an arc preserves all geometry, just to ensure roundtripping
-  //       of data
-  /* from nanovg - but not quite working ; uncertain if arc or wrong
-   * transfusion is the cause.
-   */
-  float x0 = ctx->state.x;
-  float y0 = ctx->state.y;
-  float dx0,dy0, dx1,dy1, a, d, cx,cy, a0,a1;
-  int dir;
-  if (!ctx->state.has_moved)
-    { return; }
-  if (1)
-    {
-      // Handle degenerate cases.
-      if (ctx_coords_equal (x0,y0, x1,y1, 0.5f) ||
-          ctx_coords_equal (x1,y1, x2,y2, 0.5f) ||
-          ctx_point_seg_dist_sq (x1,y1, x0,y0, x2,y2) < 0.5 ||
-          radius < 0.5)
-        {
-          ctx_line_to (ctx, x1,y1);
-          return;
-        }
-    }
-  // Calculate tangential circle to lines (x0,y0)-(x1,y1) and (x1,y1)-(x2,y2).
-  dx0 = x0-x1;
-  dy0 = y0-y1;
-  dx1 = x2-x1;
-  dy1 = y2-y1;
-  ctx_normalize (&dx0,&dy0);
-  ctx_normalize (&dx1,&dy1);
-  a = ctx_acosf (dx0*dx1 + dy0*dy1);
-  d = radius / ctx_tanf (a/2.0f);
-#if 0
-  if (d > 10000.0f)
-    {
-      ctx_line_to (ctx, x1, y1);
-      return;
-    }
-#endif
-  if ( (dx1*dy0 - dx0*dy1) > 0.0f)
-    {
-      cx = x1 + dx0*d + dy0*radius;
-      cy = y1 + dy0*d + -dx0*radius;
-      a0 = ctx_atan2f (dx0, -dy0);
-      a1 = ctx_atan2f (-dx1, dy1);
-      dir = 0;
-    }
-  else
-    {
-      cx = x1 + dx0*d + -dy0*radius;
-      cy = y1 + dy0*d + dx0*radius;
-      a0 = ctx_atan2f (-dx0, dy0);
-      a1 = ctx_atan2f (dx1, -dy1);
-      dir = 1;
-    }
-  ctx_arc (ctx, cx, cy, radius, a0, a1, dir);
-}
-
-void
-ctx_rel_arc_to (Ctx *ctx, float x1, float y1, float x2, float y2, float radius)
-{
-  x1 += ctx->state.x;
-  y1 += ctx->state.y;
-  x2 += ctx->state.x;
-  y2 += ctx->state.y;
-  ctx_arc_to (ctx, x1, y1, x2, y2, radius);
-}
-
-void
-ctx_exit (Ctx *ctx)
-{
-  CTX_PROCESS_VOID (CTX_EXIT);
-}
-
-void
-ctx_flush (Ctx *ctx)
-{
-  /* XXX: should be fully moved into the renderers
-   *      to permit different behavior and get rid
-   *      of the extranous flush() vfunc.
-   */
-  ctx->rev++;
-//  CTX_PROCESS_VOID (CTX_FLUSH);
-#if 0
-  //printf (" \e[?2222h");
-  ctx_drawlist_compact (&ctx->drawlist);
-  for (int i = 0; i < ctx->drawlist.count - 1; i++)
-    {
-      CtxEntry *entry = &ctx->drawlist.entries[i];
-      fwrite (entry, 9, 1, stdout);
-#if 0
-      uint8_t  *buf = (void *) entry;
-      for (int j = 0; j < 9; j++)
-        { printf ("%c", buf[j]); }
-#endif
-    }
-  printf ("Xx.Xx.Xx.");
-  fflush (NULL);
-#endif
-  if (ctx->renderer && ctx->renderer->flush)
-    ctx->renderer->flush (ctx->renderer);
-  ctx->frame++;
-  if (ctx->texture_cache != ctx)
-    ctx->texture_cache->frame++;
-  ctx->drawlist.count = 0;
-  ctx_state_init (&ctx->state);
-}
-
-////////////////////////////////////////
-
-static inline void
-ctx_interpret_style (CtxState *state, CtxEntry *entry, void *data)
-{
-  CtxCommand *c = (CtxCommand *) entry;
-  switch (entry->code)
-    {
-      case CTX_LINE_DASH_OFFSET:
-        state->gstate.line_dash_offset = ctx_arg_float (0);
-        break;
-      case CTX_LINE_WIDTH:
-        state->gstate.line_width = ctx_arg_float (0);
-        break;
-#if CTX_ENABLE_SHADOW_BLUR
-      case CTX_SHADOW_BLUR:
-        state->gstate.shadow_blur = ctx_arg_float (0);
-        break;
-      case CTX_SHADOW_OFFSET_X:
-        state->gstate.shadow_offset_x = ctx_arg_float (0);
-        break;
-      case CTX_SHADOW_OFFSET_Y:
-        state->gstate.shadow_offset_y = ctx_arg_float (0);
-        break;
-#endif
-      case CTX_LINE_CAP:
-        state->gstate.line_cap = (CtxLineCap) ctx_arg_u8 (0);
-        break;
-      case CTX_FILL_RULE:
-        state->gstate.fill_rule = (CtxFillRule) ctx_arg_u8 (0);
-        break;
-      case CTX_LINE_JOIN:
-        state->gstate.line_join = (CtxLineJoin) ctx_arg_u8 (0);
-        break;
-      case CTX_COMPOSITING_MODE:
-        state->gstate.compositing_mode = (CtxCompositingMode) ctx_arg_u32 (0);
-        break;
-      case CTX_BLEND_MODE:
-        state->gstate.blend_mode = (CtxBlend) ctx_arg_u32 (0);
-        break;
-      case CTX_TEXT_ALIGN:
-        ctx_state_set (state, CTX_text_align, ctx_arg_u8 (0) );
-        break;
-      case CTX_TEXT_BASELINE:
-        ctx_state_set (state, CTX_text_baseline, ctx_arg_u8 (0) );
-        break;
-      case CTX_TEXT_DIRECTION:
-        ctx_state_set (state, CTX_text_direction, ctx_arg_u8 (0) );
-        break;
-      case CTX_GLOBAL_ALPHA:
-        state->gstate.global_alpha_u8 = ctx_float_to_u8 (ctx_arg_float (0) );
-        state->gstate.global_alpha_f = ctx_arg_float (0);
-        break;
-      case CTX_FONT_SIZE:
-        state->gstate.font_size = ctx_arg_float (0);
-        break;
-      case CTX_MITER_LIMIT:
-        state->gstate.miter_limit = ctx_arg_float (0);
-        break;
-      case CTX_COLOR_SPACE:
-        /* move this out of this function and only do it in rasterizer? XXX */
-        ctx_rasterizer_colorspace_icc (state, (CtxColorSpace)c->colorspace.space_slot,
-                                              (char*)c->colorspace.data,
-                                              c->colorspace.data_len);
-        break;
-      case CTX_IMAGE_SMOOTHING:
-        state->gstate.image_smoothing = c->entry.data.u8[0];
-        break;
-      case CTX_STROKE_SOURCE:
-        state->source = 1;
-        break;
-
-      case CTX_COLOR:
-        {
-          int is_stroke = (state->source != 0);
-          CtxSource *source = is_stroke ?
-                                &state->gstate.source_stroke:
-                                &state->gstate.source_fill;
-          state->source = 0;
-
-          source->type = CTX_SOURCE_COLOR;
-         
-          //float components[5]={c->cmyka.c, c->cmyka.m, c->cmyka.y, c->cmyka.k, c->cmyka.a};
-          switch ( ((int) ctx_arg_float (0)) & 511) // XXX remove 511 after stroke source is complete
-            {
-              case CTX_RGB:
-                ctx_color_set_rgba (state, &source->color, c->rgba.r, c->rgba.g, c->rgba.b, 1.0f);
-                break;
-              case CTX_RGBA:
-                ctx_color_set_rgba (state, &source->color, c->rgba.r, c->rgba.g, c->rgba.b, c->rgba.a);
-                break;
-              case CTX_DRGBA:
-                ctx_color_set_drgba (state, &source->color, c->rgba.r, c->rgba.g, c->rgba.b, c->rgba.a);
-                break;
-#if CTX_ENABLE_CMYK
-              case CTX_CMYKA:
-                ctx_color_set_cmyka (state, &source->color, c->cmyka.c, c->cmyka.m, c->cmyka.y, c->cmyka.k, 
c->cmyka.a);
-                break;
-              case CTX_CMYK:
-                ctx_color_set_cmyka (state, &source->color, c->cmyka.c, c->cmyka.m, c->cmyka.y, c->cmyka.k, 
1.0f);
-                break;
-              case CTX_DCMYKA:
-                ctx_color_set_dcmyka (state, &source->color, c->cmyka.c, c->cmyka.m, c->cmyka.y, c->cmyka.k, 
c->cmyka.a);
-                break;
-              case CTX_DCMYK:
-                ctx_color_set_dcmyka (state, &source->color, c->cmyka.c, c->cmyka.m, c->cmyka.y, c->cmyka.k, 
1.0f);
-                break;
-#endif
-              case CTX_GRAYA:
-                ctx_color_set_graya (state, &source->color, c->graya.g, c->graya.a);
-                break;
-              case CTX_GRAY:
-                ctx_color_set_graya (state, &source->color, c->graya.g, 1.0f);
-                break;
-            }
-        }
-        break;
-      case CTX_SET_RGBA_U8:
-        //ctx_source_deinit (&state->gstate.source);
-        //state->gstate.source_fill.type = CTX_SOURCE_COLOR;
-        {
-          int is_stroke = (state->source != 0);
-          CtxSource *source = is_stroke ?
-                                &state->gstate.source_stroke:
-                                &state->gstate.source_fill;
-          state->source = 0;
-
-          source->type = CTX_SOURCE_COLOR;
-
-          ctx_color_set_RGBA8 (state, &source->color,
-                               ctx_arg_u8 (0),
-                               ctx_arg_u8 (1),
-                               ctx_arg_u8 (2),
-                               ctx_arg_u8 (3) );
-        }
-        //for (int i = 0; i < 4; i ++)
-        //  state->gstate.source.color.rgba[i] = ctx_arg_u8(i);
-        break;
-      //case CTX_TEXTURE:
-      //  state->gstate.source.type = CTX_SOURCE_
-      //  break;
-      case CTX_LINEAR_GRADIENT:
-        {
-          int is_stroke = (state->source != 0);
-          CtxSource *source = is_stroke ?
-                                &state->gstate.source_stroke:
-                                &state->gstate.source_fill;
-          state->source = is_stroke ? 2 : 0;
-
-          float x0 = ctx_arg_float (0);
-          float y0 = ctx_arg_float (1);
-          float x1 = ctx_arg_float (2);
-          float y1 = ctx_arg_float (3);
-          float dx, dy, length, start, end;
-
-          length = ctx_hypotf (x1-x0,y1-y0);
-          dx = (x1-x0) / length;
-          dy = (y1-y0) / length;
-          start = (x0 * dx + y0 * dy) / length;
-          end =   (x1 * dx + y1 * dy) / length;
-          source->linear_gradient.length = length;
-          source->linear_gradient.dx = dx;
-          source->linear_gradient.dy = dy;
-          source->linear_gradient.start = start;
-          source->linear_gradient.end = end;
-          source->linear_gradient.rdelta = (end-start)!=0.0?1.0f/(end - start):1.0;
-          source->type = CTX_SOURCE_LINEAR_GRADIENT;
-          source->transform = state->gstate.transform;
-          ctx_matrix_invert (&source->transform);
-        }
-        break;
-      case CTX_RADIAL_GRADIENT:
-        {
-          int is_stroke = (state->source != 0);
-          CtxSource *source = is_stroke ?
-                                &state->gstate.source_stroke:
-                                &state->gstate.source_fill;
-          state->source = is_stroke ? 2 : 0;
-
-          float x0 = ctx_arg_float (0);
-          float y0 = ctx_arg_float (1);
-          float r0 = ctx_arg_float (2);
-          float x1 = ctx_arg_float (3);
-          float y1 = ctx_arg_float (4);
-          float r1 = ctx_arg_float (5);
-          source->radial_gradient.x0 = x0;
-          source->radial_gradient.y0 = y0;
-          source->radial_gradient.r0 = r0;
-          source->radial_gradient.x1 = x1;
-          source->radial_gradient.y1 = y1;
-          source->radial_gradient.r1 = r1;
-          source->radial_gradient.rdelta = (r1 - r0) != 0.0 ? 1.0f/(r1-r0):0.0;
-          source->type      = CTX_SOURCE_RADIAL_GRADIENT;
-          source->transform = state->gstate.transform;
-          ctx_matrix_invert (&source->transform);
-        }
-        break;
-    }
-}
-
-static inline void
-ctx_interpret_transforms (CtxState *state, CtxEntry *entry, void *data)
-{
-  switch (entry->code)
-    {
-      case CTX_SAVE:
-        ctx_gstate_push (state);
-        break;
-      case CTX_RESTORE:
-        ctx_gstate_pop (state);
-        break;
-      case CTX_IDENTITY:
-        _ctx_matrix_identity (&state->gstate.transform);
-        break;
-      case CTX_TRANSLATE:
-        ctx_matrix_translate (&state->gstate.transform,
-                              ctx_arg_float (0), ctx_arg_float (1) );
-        break;
-      case CTX_SCALE:
-        ctx_matrix_scale (&state->gstate.transform,
-                          ctx_arg_float (0), ctx_arg_float (1) );
-        break;
-      case CTX_ROTATE:
-        ctx_matrix_rotate (&state->gstate.transform, ctx_arg_float (0) );
-        break;
-      case CTX_APPLY_TRANSFORM:
-        {
-          CtxMatrix m;
-          ctx_matrix_set (&m,
-                          ctx_arg_float (0), ctx_arg_float (1),
-                          ctx_arg_float (2), ctx_arg_float (3),
-                          ctx_arg_float (4), ctx_arg_float (5) );
-          _ctx_matrix_multiply (&state->gstate.transform,
-                                &state->gstate.transform, &m); // XXX verify order
-        }
-#if 0
-        ctx_matrix_set (&state->gstate.transform,
-                        ctx_arg_float (0), ctx_arg_float (1),
-                        ctx_arg_float (2), ctx_arg_float (3),
-                        ctx_arg_float (4), ctx_arg_float (5) );
-#endif
-        break;
-    }
-}
-
-/*
- * this transforms the contents of entry according to ctx->transformation
- */
-static inline void
-ctx_interpret_pos_transform (CtxState *state, CtxEntry *entry, void *data)
-{
-  CtxCommand *c = (CtxCommand *) entry;
-  float start_x = state->x;
-  float start_y = state->y;
-  int had_moved = state->has_moved;
-  switch (entry->code)
-    {
-      case CTX_MOVE_TO:
-      case CTX_LINE_TO:
-        {
-          float x = c->c.x0;
-          float y = c->c.y0;
-          if ( ( ( (Ctx *) (data) )->transformation & CTX_TRANSFORMATION_SCREEN_SPACE) )
-            {
-              _ctx_user_to_device (state, &x, &y);
-              ctx_arg_float (0) = x;
-              ctx_arg_float (1) = y;
-            }
-        }
-        break;
-      case CTX_ARC:
-        if ( ( ( (Ctx *) (data) )->transformation & CTX_TRANSFORMATION_SCREEN_SPACE) )
-          {
-            float temp;
-            _ctx_user_to_device (state, &c->arc.x, &c->arc.y);
-            temp = 0;
-            _ctx_user_to_device_distance (state, &c->arc.radius, &temp);
-          }
-        break;
-      case CTX_LINEAR_GRADIENT:
-        if ( ( ( (Ctx *) (data) )->transformation & CTX_TRANSFORMATION_SCREEN_SPACE) )
-        {
-        _ctx_user_to_device (state, &c->linear_gradient.x1, &c->linear_gradient.y1);
-        _ctx_user_to_device (state, &c->linear_gradient.x2, &c->linear_gradient.y2);
-        }
-        break;
-      case CTX_RADIAL_GRADIENT:
-        if ( ( ( (Ctx *) (data) )->transformation & CTX_TRANSFORMATION_SCREEN_SPACE) )
-        {
-          float temp;
-          _ctx_user_to_device (state, &c->radial_gradient.x1, &c->radial_gradient.y1);
-          temp = 0;
-          _ctx_user_to_device_distance (state, &c->radial_gradient.r1, &temp);
-          _ctx_user_to_device (state, &c->radial_gradient.x2, &c->radial_gradient.y2);
-          temp = 0;
-          _ctx_user_to_device_distance (state, &c->radial_gradient.r2, &temp);
-        }
-        break;
-      case CTX_CURVE_TO:
-        if ( ( ( (Ctx *) (data) )->transformation & CTX_TRANSFORMATION_SCREEN_SPACE) )
-          {
-            for (int c = 0; c < 3; c ++)
-              {
-                float x = entry[c].data.f[0];
-                float y = entry[c].data.f[1];
-                _ctx_user_to_device (state, &x, &y);
-                entry[c].data.f[0] = x;
-                entry[c].data.f[1] = y;
-              }
-          }
-        break;
-      case CTX_QUAD_TO:
-        if ( ( ( (Ctx *) (data) )->transformation & CTX_TRANSFORMATION_SCREEN_SPACE) )
-          {
-            for (int c = 0; c < 2; c ++)
-              {
-                float x = entry[c].data.f[0];
-                float y = entry[c].data.f[1];
-                _ctx_user_to_device (state, &x, &y);
-                entry[c].data.f[0] = x;
-                entry[c].data.f[1] = y;
-              }
-          }
-        break;
-      case CTX_REL_MOVE_TO:
-      case CTX_REL_LINE_TO:
-        if ( ( ( (Ctx *) (data) )->transformation & CTX_TRANSFORMATION_SCREEN_SPACE) )
-          {
-            for (int c = 0; c < 1; c ++)
-              {
-                float x = state->x;
-                float y = state->y;
-                _ctx_user_to_device (state, &x, &y);
-                entry[c].data.f[0] = x;
-                entry[c].data.f[1] = y;
-              }
-            if (entry->code == CTX_REL_MOVE_TO)
-              { entry->code = CTX_MOVE_TO; }
-            else
-              { entry->code = CTX_LINE_TO; }
-          }
-        break;
-      case CTX_REL_CURVE_TO:
-        {
-          float nx = state->x + ctx_arg_float (4);
-          float ny = state->y + ctx_arg_float (5);
-          if ( ( ( (Ctx *) (data) )->transformation & CTX_TRANSFORMATION_SCREEN_SPACE) )
-            {
-              for (int c = 0; c < 3; c ++)
-                {
-                  float x = nx + entry[c].data.f[0];
-                  float y = ny + entry[c].data.f[1];
-                  _ctx_user_to_device (state, &x, &y);
-                  entry[c].data.f[0] = x;
-                  entry[c].data.f[1] = y;
-                }
-              entry->code = CTX_CURVE_TO;
-            }
-        }
-        break;
-      case CTX_REL_QUAD_TO:
-        {
-          float nx = state->x + ctx_arg_float (2);
-          float ny = state->y + ctx_arg_float (3);
-          if ( ( ( (Ctx *) (data) )->transformation & CTX_TRANSFORMATION_SCREEN_SPACE) )
-            {
-              for (int c = 0; c < 2; c ++)
-                {
-                  float x = nx + entry[c].data.f[0];
-                  float y = ny + entry[c].data.f[1];
-                  _ctx_user_to_device (state, &x, &y);
-                  entry[c].data.f[0] = x;
-                  entry[c].data.f[1] = y;
-                }
-              entry->code = CTX_QUAD_TO;
-            }
-        }
-        break;
-    }
-  if ((((Ctx *) (data) )->transformation & CTX_TRANSFORMATION_RELATIVE))
-    {
-      int components = 0;
-      _ctx_user_to_device (state, &start_x, &start_y);
-      switch (entry->code)
-        {
-          case CTX_MOVE_TO:
-            if (had_moved) { components = 1; }
-            break;
-          case CTX_LINE_TO:
-            components = 1;
-            break;
-          case CTX_CURVE_TO:
-            components = 3;
-            break;
-          case CTX_QUAD_TO:
-            components = 2;
-            break;
-        }
-      if (components)
-        {
-          for (int c = 0; c < components; c++)
-            {
-              entry[c].data.f[0] -= start_x;
-              entry[c].data.f[1] -= start_y;
-            }
-          switch (entry->code)
-            {
-              case CTX_MOVE_TO:
-                entry[0].code = CTX_REL_MOVE_TO;
-                break;
-              case CTX_LINE_TO:
-                entry[0].code = CTX_REL_LINE_TO;
-                break;
-                break;
-              case CTX_CURVE_TO:
-                entry[0].code = CTX_REL_CURVE_TO;
-                break;
-              case CTX_QUAD_TO:
-                entry[0].code = CTX_REL_QUAD_TO;
-                break;
-            }
-        }
-    }
-}
-
-static inline void
-ctx_interpret_pos_bare (CtxState *state, CtxEntry *entry, void *data)
-{
-  switch (entry->code)
-    {
-      case CTX_RESET:
-        ctx_state_init (state);
-        break;
-      case CTX_CLIP:
-      case CTX_BEGIN_PATH:
-      case CTX_FILL:
-      case CTX_STROKE:
-        state->has_moved = 0;
-        break;
-      case CTX_MOVE_TO:
-      case CTX_LINE_TO:
-        state->x = ctx_arg_float (0);
-        state->y = ctx_arg_float (1);
-        state->has_moved = 1;
-        break;
-      case CTX_CURVE_TO:
-        state->x = ctx_arg_float (4);
-        state->y = ctx_arg_float (5);
-        state->has_moved = 1;
-        break;
-      case CTX_QUAD_TO:
-        state->x = ctx_arg_float (2);
-        state->y = ctx_arg_float (3);
-        state->has_moved = 1;
-        break;
-      case CTX_ARC:
-        state->x = ctx_arg_float (0) + ctx_cosf (ctx_arg_float (4) ) * ctx_arg_float (2);
-        state->y = ctx_arg_float (1) + ctx_sinf (ctx_arg_float (4) ) * ctx_arg_float (2);
-        break;
-      case CTX_REL_MOVE_TO:
-      case CTX_REL_LINE_TO:
-        state->x += ctx_arg_float (0);
-        state->y += ctx_arg_float (1);
-        break;
-      case CTX_REL_CURVE_TO:
-        state->x += ctx_arg_float (4);
-        state->y += ctx_arg_float (5);
-        break;
-      case CTX_REL_QUAD_TO:
-        state->x += ctx_arg_float (2);
-        state->y += ctx_arg_float (3);
-        break;
-        // XXX missing some smooths
-    }
-}
-
-static inline void
-ctx_interpret_pos (CtxState *state, CtxEntry *entry, void *data)
-{
-  if ( ( ( (Ctx *) (data) )->transformation & CTX_TRANSFORMATION_SCREEN_SPACE) ||
-       ( ( (Ctx *) (data) )->transformation & CTX_TRANSFORMATION_RELATIVE) )
-    {
-      ctx_interpret_pos_transform (state, entry, data);
-    }
-  ctx_interpret_pos_bare (state, entry, data);
-}
-
-#if CTX_BABL
-void ctx_colorspace_babl (CtxState   *state,
-                          CtxColorSpace  icc_slot,
-                          const Babl *space);
-#endif
-
-static void
-ctx_state_init (CtxState *state)
-{
-  ctx_memset (state, 0, sizeof (CtxState) );
-  state->gstate.global_alpha_u8 = 255;
-  state->gstate.global_alpha_f  = 1.0;
-  state->gstate.font_size       = 12;
-  state->gstate.line_width      = 2.0;
-  state->gstate.image_smoothing = 1;
-  state->gstate.source_stroke.type = CTX_SOURCE_INHERIT_FILL;
-  ctx_state_set (state, CTX_line_spacing, 1.0f);
-  state->min_x                  = 8192;
-  state->min_y                  = 8192;
-  state->max_x                  = -8192;
-  state->max_y                  = -8192;
-  _ctx_matrix_identity (&state->gstate.transform);
-#if CTX_CM
-#if CTX_BABL
-  //ctx_colorspace_babl (state, CTX_COLOR_SPACE_USER_RGB,   babl_space ("sRGB"));
-  //ctx_colorspace_babl (state, CTX_COLOR_SPACE_DEVICE_RGB, babl_space ("ACEScg"));
-#endif
-#endif
-}
-
-void _ctx_set_transformation (Ctx *ctx, int transformation)
-{
-  ctx->transformation = transformation;
-}
-
-static void
-_ctx_init (Ctx *ctx)
-{
-  for (int i = 0; i <256;i++)
-    ctx_u8_float[i] = i/255.0f;
-
-  ctx_state_init (&ctx->state);
-
-  ctx->renderer = NULL;
-#if CTX_CURRENT_PATH
-  ctx->current_path.flags |= CTX_DRAWLIST_CURRENT_PATH;
-#endif
-  //ctx->transformation |= (CtxTransformation) CTX_TRANSFORMATION_SCREEN_SPACE;
-  //ctx->transformation |= (CtxTransformation) CTX_TRANSFORMATION_RELATIVE;
-#if CTX_BITPACK
-  ctx->drawlist.flags |= CTX_TRANSFORMATION_BITPACK;
-#endif
-  ctx->texture_cache = ctx;
-}
-
-static void ctx_setup ();
-
-#if CTX_DRAWLIST_STATIC
-static Ctx ctx_state;
-#endif
-
-void ctx_set_renderer (Ctx  *ctx,
-                       void *renderer)
-{
-  if (ctx->renderer && ctx->renderer->free)
-    ctx->renderer->free (ctx->renderer);
-  ctx->renderer = (CtxImplementation*)renderer;
-}
-
-void *ctx_get_renderer (Ctx *ctx)
-{
-  return ctx->renderer;
-}
-
-Ctx *
-ctx_new (void)
-{
-  ctx_setup ();
-#if CTX_DRAWLIST_STATIC
-  Ctx *ctx = &ctx_state;
-#else
-  Ctx *ctx = (Ctx *) malloc (sizeof (Ctx) );
-#endif
-  ctx_memset (ctx, 0, sizeof (Ctx) );
-  _ctx_init (ctx);
-  return ctx;
-}
-
-static inline void
-ctx_drawlist_deinit (CtxDrawlist *drawlist)
-{
-#if !CTX_DRAWLIST_STATIC
-  if (drawlist->entries && ! (drawlist->flags & CTX_DRAWLIST_DOESNT_OWN_ENTRIES) )
-    {
-      free (drawlist->entries); 
-    }
-#endif
-  drawlist->entries = NULL;
-  drawlist->size = 0;
-}
-
-static void ctx_deinit (Ctx *ctx)
-{
-  if (ctx->renderer)
-    {
-      if (ctx->renderer->free)
-        ctx->renderer->free (ctx->renderer);
-      ctx->renderer    = NULL;
-    }
-  ctx_drawlist_deinit (&ctx->drawlist);
-#if CTX_CURRENT_PATH
-  ctx_drawlist_deinit (&ctx->current_path);
-#endif
-}
-
-void ctx_free (Ctx *ctx)
-{
-  if (!ctx)
-    { return; }
-#if CTX_EVENTS
-  ctx_clear_bindings (ctx);
-#endif
-  ctx_deinit (ctx);
-#if !CTX_DRAWLIST_STATIC
-  free (ctx);
-#endif
-}
-
-Ctx *ctx_new_for_drawlist (void *data, size_t length)
-{
-  Ctx *ctx = ctx_new ();
-  ctx->drawlist.flags   |= CTX_DRAWLIST_DOESNT_OWN_ENTRIES;
-  ctx->drawlist.entries  = (CtxEntry *) data;
-  ctx->drawlist.count    = length / sizeof (CtxEntry);
-  return ctx;
-}
-
-static void ctx_setup ()
-{
-  ctx_font_setup ();
-}
-
-void
-ctx_render_ctx (Ctx *ctx, Ctx *d_ctx)
-{
-  CtxIterator iterator;
-  CtxCommand *command;
-  ctx_iterator_init (&iterator, &ctx->drawlist, 0,
-                     CTX_ITERATOR_EXPAND_BITPACK);
-  while ( (command = ctx_iterator_next (&iterator) ) )
-    {
-       ctx_process (d_ctx, &command->entry);
-    }
-}
-
-void
-ctx_render_ctx_textures (Ctx *ctx, Ctx *d_ctx)
-{
-  CtxIterator iterator;
-  CtxCommand *command;
-  ctx_iterator_init (&iterator, &ctx->drawlist, 0,
-                     CTX_ITERATOR_EXPAND_BITPACK);
-  while ( (command = ctx_iterator_next (&iterator) ) )
-    {
-       switch (command->code)
-       {
-         default:
-                 //fprintf (stderr, "[%c]", command->code);
-                 break;
-         case CTX_TEXTURE:
-             //fprintf (stderr, "t:%s\n", command->texture.eid);
-             ctx_process (d_ctx, &command->entry);
-             break;
-         case CTX_DEFINE_TEXTURE:
-             //fprintf (stderr, "d:%s\n", command->define_texture.eid);
-             ctx_process (d_ctx, &command->entry);
-           break;
-       }
-    }
-}
-
-void ctx_quit (Ctx *ctx)
-{
-#if CTX_EVENTS
-  ctx->quit ++;
-#endif
-}
-
-int  ctx_has_quit (Ctx *ctx)
-{
-#if CTX_EVENTS
-  return (ctx->quit);
-#else
-  return 1; 
-#endif
-}
-
-int ctx_pixel_format_bits_per_pixel (CtxPixelFormat format)
-{
-  CtxPixelFormatInfo *info = ctx_pixel_format_info (format);
-  if (info)
-    return info->bpp;
-  return -1;
-}
-
-int ctx_pixel_format_get_stride (CtxPixelFormat format, int width)
-{
-  CtxPixelFormatInfo *info = ctx_pixel_format_info (format);
-  if (info)
-  {
-    switch (info->bpp)
-    {
-      case 0:
-      case 1:
-        return (width + 7)/8;
-      case 2:
-        return (width + 3)/4;
-      case 4:
-        return (width + 1)/2;
-      default:
-        return width * (info->bpp / 8);
-    }
-  }
-  return width;
-}
-
-int ctx_pixel_format_ebpp (CtxPixelFormat format)
-{
-  CtxPixelFormatInfo *info = ctx_pixel_format_info (format);
-  if (info)
-    return info->ebpp;
-  return -1;
-}
-
-int ctx_pixel_format_components (CtxPixelFormat format)
-{
-  CtxPixelFormatInfo *info = ctx_pixel_format_info (format);
-  if (info)
-    return info->components;
-  return -1;
-}
-
-#if CTX_EVENTS
-void         ctx_set_cursor (Ctx *ctx, CtxCursor cursor)
-{
-  if (ctx->cursor != cursor)
-  {
-    ctx_set_dirty (ctx, 1);
-    ctx->cursor = cursor;
-  }
-}
-CtxCursor    ctx_get_cursor (Ctx *ctx)
-{
-  return ctx->cursor;
-}
-
-void ctx_set_clipboard (Ctx *ctx, const char *text)
-{
-  if (ctx->renderer && ctx->renderer->set_clipboard)
-  {
-    ctx->renderer->set_clipboard (ctx->renderer, text);
-    return;
-  }
-}
-
-char *ctx_get_clipboard (Ctx *ctx)
-{
-  if (ctx->renderer && ctx->renderer->get_clipboard)
-  {
-    return ctx->renderer->get_clipboard (ctx->renderer);
-  }
-  return strdup ("");
-}
-
-void ctx_set_texture_source (Ctx *ctx, Ctx *texture_source)
-{
-  ((CtxRasterizer*)ctx->renderer)->texture_source = texture_source;
-}
-
-void ctx_set_texture_cache (Ctx *ctx, Ctx *texture_cache)
-{
-  ctx->texture_cache = texture_cache;
-}
-
-void ctx_set_transform (Ctx *ctx, float a, float b, float c, float d, float e, float f)
-{
-  ctx_identity (ctx);
-  ctx_apply_transform (ctx, a, b, c, d, e, f);
-}
-#ifndef NO_LIBCURL
-#include <curl/curl.h>
-static size_t
-ctx_string_append_callback (void *contents, size_t size, size_t nmemb, void *userp)
-{
-  CtxString *string = (CtxString*)userp;
-  ctx_string_append_data ((CtxString*)string, contents, size * nmemb);
-  return size * nmemb;
-}
-
-#endif
-
-int
-ctx_get_contents (const char     *uri,
-                  unsigned char **contents,
-                  long           *length)
-{
-  char *temp_uri = NULL; // XXX XXX breaks with data uri's
-  int   success  = -1;
-
-  if (uri[0] == '/')
-  {
-    temp_uri = (char*) malloc (strlen (uri) + 8);
-    sprintf (temp_uri, "file://%s", uri);
-    uri = temp_uri;
-  }
-
-  if (strchr (uri, '#'))
-   strchr (uri, '#')[0]=0;
-
-  for (CtxList *l = registered_contents; l; l = l->next)
-  {
-    CtxFileContent *c = (CtxFileContent*)l->data;
-    if (!strcmp (c->path, uri))
-    {
-      contents = malloc (c->length+1);
-      contents[c->length]=0;
-      if (length) *length = c->length;
-      free (temp_uri);
-      return 0;
-    }
-  }
-
-  if (!strncmp (uri, "file://", 5))
-  {
-    if (strchr (uri, '?'))
-     strchr (uri, '?')[0]=0;
-  }
-
-  if (!strncmp (uri, "file://", 7))
-    success = __ctx_file_get_contents (uri + 7, contents, length);
-  else
-  {
-#ifndef NO_LIBCURL
-  CURL *curl = curl_easy_init ();
-  CURLcode res;
-
-  curl_easy_setopt(curl, CURLOPT_URL, uri);
-  curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
-    CtxString *string = ctx_string_new ("");
-
-      curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, ctx_string_append_callback);
-   /* we pass our 'chunk' struct to the callback function */
-  curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void*)string);
-
-  curl_easy_setopt(curl, CURLOPT_USERAGENT, "ctx/0.0");
-
-   res = curl_easy_perform(curl);
-  /* check for errors */
-  if(res != CURLE_OK) {
-          fprintf(stderr, "curl_easy_perform() failed: %s\n",
-            curl_easy_strerror(res));
-     curl_easy_cleanup (curl);
-  }
-  else
-  {
-     *contents = (unsigned char*)string->str;
-     *length = string->length;
-     ctx_string_free (string, 0);
-     curl_easy_cleanup (curl);
-     success = 0;
-  }
-#else
-    success = __ctx_file_get_contents (uri, contents, length);
-#endif
-  }
-  free (temp_uri);
-  return success;
-}
-
-
-#endif
-
-#endif // CTX_IMPLEMENTATION
-#ifndef __CTX_CLIENTS_H
-#define __CTX_CLIENTS_H
-
-typedef enum CtxClientFlags {
-  ITK_CLIENT_UI_RESIZABLE = 1<<0,
-  ITK_CLIENT_CAN_LAUNCH   = 1<<1,
-  ITK_CLIENT_MAXIMIZED    = 1<<2,
-  ITK_CLIENT_ICONIFIED    = 1<<3,
-  ITK_CLIENT_SHADED       = 1<<4,
-  ITK_CLIENT_TITLEBAR     = 1<<5
-} CtxClientFlags;
-
-struct _CtxClient {
-  VT    *vt;
-  Ctx   *ctx;
-  char  *title;
-  int    x;
-  int    y;
-  int    width;
-  int    height;
-  CtxClientFlags flags;
-#if 0
-  int    shaded;
-  int    iconified;
-  int    maximized;
-  int    resizable;
-#endif
-  int    unmaximized_x;
-  int    unmaximized_y;
-  int    unmaximized_width;
-  int    unmaximized_height;
-  int    do_quit;
-  long   drawn_rev;
-  int    id;
-  int    internal; // render a settings window rather than a vt
-#if CTX_THREADS
-  mtx_t  mtx;
-#endif
-#if VT_RECORD
-  Ctx   *recording;
-#endif
-};
-
-typedef struct _CtxClient CtxClient;
-
-
-extern CtxList *clients;
-extern CtxClient *active;
-extern CtxClient *active_tab;
-
-
-int ctx_client_resize (int id, int width, int height);
-void ctx_client_maximize (int id);
-
-CtxClient *vt_get_client (VT *vt);
-CtxClient *ctx_client_new (Ctx *ctx, const char *commandline,
-                       int x, int y, int width, int height,
-                       CtxClientFlags flags);
-CtxClient *ctx_client_new_argv (Ctx *ctx, const char **argv, int x, int y, int width, int height, 
CtxClientFlags flags);
-int ctx_clients_need_redraw (Ctx *ctx);
-
-extern float ctx_shape_cache_rate;
-extern int _ctx_max_threads;
-
-void ctx_client_move (int id, int x, int y);
-int ctx_client_resize (int id, int w, int h);
-void ctx_client_shade_toggle (int id);
-float ctx_client_min_y_pos (Ctx *ctx);
-float ctx_client_max_y_pos (Ctx *ctx);
-
-CtxClient *client_by_id (int id);
-
-void ctx_client_remove (Ctx *ctx, CtxClient *client);
-
-int ctx_client_height (int id);
-
-int ctx_client_x (int id);
-int ctx_client_y (int id);
-void ctx_client_raise_top (int id);
-void ctx_client_lower_bottom (int id);
-void ctx_client_iconify (int id);
-int ctx_client_is_iconified (int id);
-void ctx_client_uniconify (int id);
-void ctx_client_maximize (int id);
-int ctx_client_is_maximized (int id);
-void ctx_client_unmaximize (int id);
-void ctx_client_maximized_toggle (int id);
-void ctx_client_shade (int id);
-int ctx_client_is_shaded (int id);
-void ctx_client_unshade (int id);
-void ctx_client_toggle_maximized (int id);
-void ctx_client_shade_toggle (int id);
-void ctx_client_move (int id, int x, int y);
-int ctx_client_resize (int id, int width, int height);
-
-
-#endif
-#ifndef MRG_UTF8_H
-#define MRG_UTF8_H
-
-#if !__COSMOPOLITAN__
-#include <string.h>
-#include <stdint.h>
-#endif
-
-static inline int mrg_utf8_len (const unsigned char first_byte)
-{
-  if      ((first_byte & 0x80) == 0)
-    return 1; /* ASCII */
-  else if ((first_byte & 0xE0) == 0xC0)
-    return 2;
-  else if ((first_byte & 0xF0) == 0xE0)
-    return 3;
-  else if ((first_byte & 0xF8) == 0xF0)
-    return 4;
-  return 1;
-}
-
-static inline const char *mrg_utf8_skip (const char *s, int utf8_length)
-{
-   int count;
-   if (!s)
-     return NULL;
-   for (count = 0; *s; s++)
-   {
-     if ((*s & 0xC0) != 0x80)
-       count++;
-     if (count == utf8_length+1)
-       return s;
-   }
-   return s;
-}
-
-int          mrg_unichar_to_utf8       (unsigned int   ch,
-                                        unsigned char *dest);
-unsigned int mrg_utf8_to_unichar       (unsigned char *utf8);
-
-//////////////////////////////////////////////////////////////////////////////////
-
-// Copyright (c) 2008-2009 Bjoern Hoehrmann <bjoern hoehrmann de>
-// See http://bjoern.hoehrmann.de/utf-8/decoder/dfa/ for details.
-
-#define UTF8_ACCEPT 0
-#define UTF8_REJECT 1
-
-static const uint8_t utf8d[] = {
-  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 00..1f
-  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 20..3f
-  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 40..5f
-  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 60..7f
-  1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, // 80..9f
-  7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, // a0..bf
-  8,8,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, // c0..df
-  0xa,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x4,0x3,0x3, // e0..ef
-  0xb,0x6,0x6,0x6,0x5,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8, // f0..ff
-  0x0,0x1,0x2,0x3,0x5,0x8,0x7,0x1,0x1,0x1,0x4,0x6,0x1,0x1,0x1,0x1, // s0..s0
-  1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,0,1,0,1,1,1,1,1,1, // s1..s2
-  1,2,1,1,1,1,1,2,1,2,1,1,1,1,1,1,1,1,1,1,1,1,1,2,1,1,1,1,1,1,1,1, // s3..s4
-  1,2,1,1,1,1,1,1,1,2,1,1,1,1,1,1,1,1,1,1,1,1,1,3,1,3,1,1,1,1,1,1, // s5..s6
-  1,3,1,1,1,1,1,3,1,3,1,1,1,1,1,1,1,3,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // s7..s8
-};
-
-static inline uint32_t
-utf8_decode(uint32_t* state, uint32_t* codep, uint32_t byte) {
-  uint32_t type = utf8d[byte];
-
-  *codep = (*state != UTF8_ACCEPT) ?
-    (byte & 0x3fu) | (*codep << 6) :
-    (0xff >> type) & (byte);
-
-  *state = utf8d[256 + *state*16 + type];
-  return *state;
-}
-
-#endif
-#if CTX_VT
-
-/* mrg - MicroRaptor Gui
- * Copyright (c) 2014 Øyvind Kolås <pippin hodefoting com>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#ifndef VT_LINE_H
-#define VT_LINE_H
-
-#include "ctx.h"
-
-#ifndef CTX_UNLIKELY
-#define CTX_UNLIKELY(x)    __builtin_expect(!!(x), 0)
-#define CTX_LIKELY(x)      __builtin_expect(!!(x), 1)
-#endif
-#ifndef CTX_MAX
-#define CTX_MAX(a,b) (((a)>(b))?(a):(b))
-#endif
-
-typedef struct _VtLine   VtLine;
-
-struct _VtLine
-{
-  CtxString string;
-  /* line extends string, permitting string ops to operate on it  */
-
-  uint64_t *style;
-  int       style_size;
-
-  void     *ctx; // each line can have an attached ctx context;
-  char     *prev;
-  int       prev_length;
-  CtxString *frame;
-
-  int       wrapped;
-
-  void     *ctx_copy; // each line can have an attached ctx context;
-  // clearing should be brutal enough to unset the context of the current
-  // at least in alt-screen mode
-  int       double_width;
-  int       double_height_top;
-  int       double_height_bottom;
-  int       contains_proportional;
-  float     xscale;
-  float     yscale;
-  float     y_offset;
-  int       in_scrolling_region;
-
-  /*  XXX:  needs refactoring to a CtxList of links/images */
-  void     *images[4];
-  int       image_col[4];
-  float     image_X[4]; // 0.0 - 1.0 offset in cell
-  float     image_Y[4];
-  int       image_rows[4];
-  int       image_cols[4];
-  int       image_subx[4];
-  int       image_suby[4];
-  int       image_subw[4];
-  int       image_subh[4];
-};
-
-
-static inline uint64_t vt_line_get_style (VtLine *string, int pos)
-{
-  if (string->string.is_line==0)
-    return 0;
-  if (pos < 0 || pos >= string->style_size)
-    return 0;
-  return string->style[pos];
-}
-
-#if !__COSMOPOLITAN__
-#include <stdlib.h>
-#endif
-
-static inline void vt_line_set_style (VtLine *string, int pos, uint64_t style)
-{
-  if (string->string.is_line==0)
-    return;
-  if (pos < 0 || pos >= 512)
-    return;
-  if (pos >= string->style_size)
-    {
-      int new_size = pos + 16;
-      string->style = realloc (string->style, new_size * sizeof (uint64_t) );
-      memset (&string->style[string->style_size], 0, (new_size - string->style_size) * sizeof (uint64_t) );
-      string->style_size = new_size;
-    }
-  string->style[pos] = style;
-}
-
-VtLine *vt_line_new_with_size (const char *initial, int initial_size);
-VtLine *vt_line_new (const char *initial);
-
-static inline void        vt_line_free           (VtLine *line, int freealloc)
-{
-  CtxString *string = (CtxString*)line;
-
-#if 1
-  //if (string->is_line)
-  {
-    VtLine *line = (VtLine*)string;
-    if (line->frame)
-      ctx_string_free (line->frame, 1);
-    if (line->style)
-      { free (line->style); }
-    if (line->ctx)
-      { ctx_free (line->ctx); }
-    if (line->ctx_copy)
-      { ctx_free (line->ctx_copy); }
-  }
-#endif
-
-  ctx_string_free (string, freealloc);
-}
-static inline const char *vt_line_get            (VtLine *line)
-{
-  CtxString *string = (CtxString*)line;
-  return ctx_string_get (string);
-}
-static inline uint32_t    vt_line_get_unichar    (VtLine *line, int pos)
-{
-  CtxString *string = (CtxString*)line;
-  return ctx_string_get_unichar (string, pos);
-}
-static inline int         vt_line_get_length     (VtLine *line)
-{
-  CtxString *string = (CtxString*)line;
-  return ctx_string_get_length (string);
-}
-static inline int         vt_line_get_utf8length     (VtLine *line)
-{
-  CtxString *string = (CtxString*)line;
-  return ctx_string_get_utf8length (string);
-}
-static inline void        vt_line_set            (VtLine *line, const char *new_string)
-{
-  CtxString *string = (CtxString*)line;
-  ctx_string_set (string, new_string);
-}
-static inline void        vt_line_clear          (VtLine *line)
-{
-  CtxString *string = (CtxString*)line;
-  ctx_string_clear (string);
-}
-static inline void        vt_line_append_str     (VtLine *line, const char *str)
-{
-  CtxString *string = (CtxString*)line;
-  ctx_string_append_str (string, str);
-}
-
-#if 0
-static inline void _ctx_string_append_byte (CtxString *string, char  val)
-{
-  if (CTX_LIKELY((val & 0xC0) != 0x80))
-    { string->utf8_length++; }
-  if (CTX_UNLIKELY(string->length + 2 >= string->allocated_length))
-    {
-      char *old = string->str;
-      string->allocated_length = CTX_MAX (string->allocated_length * 2, string->length + 2);
-      string->str = (char*)realloc (old, string->allocated_length);
-    }
-  string->str[string->length++] = val;
-  string->str[string->length] = '\0';
-}
-#endif
-
-static inline void        vt_line_append_byte    (VtLine *line, char  val)
-{
-  CtxString *string = (CtxString*)line;
-  _ctx_string_append_byte (string, val);
-}
-static inline void        vt_line_append_string  (VtLine *line, CtxString *string2)
-{
-  CtxString *string = (CtxString*)line;
-  ctx_string_append_string (string, string2);
-}
-static inline void        vt_line_append_unichar (VtLine *line, unsigned int unichar)
-{
-  CtxString *string = (CtxString*)line;
-  ctx_string_append_unichar (string, unichar);
-}
-
-
-static inline void vt_line_append_data    (VtLine *line, const char *data, int len)
-{
-  CtxString *string = (CtxString*)line;
-  ctx_string_append_data (string, data, len);
-}
-static inline void vt_line_append_utf8char (VtLine *line, const char *str)
-{
-  CtxString *string = (CtxString*)line;
-  ctx_string_append_utf8char (string, str);
-}
-static inline void vt_line_replace_utf8   (VtLine *line, int pos, const char *new_glyph)
-{
-  CtxString *string = (CtxString*)line;
-  ctx_string_replace_utf8 (string, pos, new_glyph);
-}
-static inline void vt_line_insert_utf8    (VtLine *line, int pos, const char *new_glyph)
-{
-  CtxString *string = (CtxString*)line;
-  ctx_string_insert_utf8 (string, pos, new_glyph);
-  int len = vt_line_get_length (line);
-  for (int i = pos; i < len; i++)
-    vt_line_set_style (line, i, vt_line_get_style (line, i-1));
-}
-
-static inline void vt_line_insert_unichar (VtLine *line, int pos, uint32_t new_glyph)
-{
-  CtxString *string = (CtxString*)line;
-  ctx_string_insert_unichar (string, pos, new_glyph);
-  int len = vt_line_get_length (line);
-  for (int i = 1; i < len; i++)
-    vt_line_set_style (line, i, vt_line_get_style (line, i-1));
-}
-static inline void vt_line_replace_unichar (VtLine *line, int pos, uint32_t unichar)
-{
-  CtxString *string = (CtxString*)line;
-  ctx_string_replace_unichar (string, pos, unichar);
-}
-
-static inline void vt_line_remove (VtLine *line, int pos)
-{ 
-  CtxString *string = (CtxString*)line;
-  ctx_string_remove (string, pos);
-
-  for (int i = pos; i < line->style_size-1; i++)
-  {
-    line->style[i] = line->style[i+1];
-  }
-}
-
-
-#ifndef TRUE
-#define TRUE 1
-#endif
-#ifndef FALSE
-#define FALSE 0
-#endif
-
-#endif
-/* mrg - MicroRaptor Gui
- * Copyright (c) 2014 Øyvind Kolås <pippin hodefoting com>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library. If not, see <http://www.gnu.org/licenses/>.
- */
-
-
-#ifndef _DEFAULT_SOURCE
-#define _DEFAULT_SOURCE
-#endif
-
-#if !__COSMOPOLITAN__
-#include <stdarg.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
+          source->type = CTX_SOURCE_COLOR;
+         
+          //float components[5]={c->cmyka.c, c->cmyka.m, c->cmyka.y, c->cmyka.k, c->cmyka.a};
+          switch ( ((int) ctx_arg_float (0)) & 511) // XXX remove 511 after stroke source is complete
+            {
+              case CTX_RGB:
+                ctx_color_set_rgba (state, &source->color, c->rgba.r, c->rgba.g, c->rgba.b, 1.0f);
+                break;
+              case CTX_RGBA:
+                ctx_color_set_rgba (state, &source->color, c->rgba.r, c->rgba.g, c->rgba.b, c->rgba.a);
+                break;
+              case CTX_DRGBA:
+                ctx_color_set_drgba (state, &source->color, c->rgba.r, c->rgba.g, c->rgba.b, c->rgba.a);
+                break;
+#if CTX_ENABLE_CMYK
+              case CTX_CMYKA:
+                ctx_color_set_cmyka (state, &source->color, c->cmyka.c, c->cmyka.m, c->cmyka.y, c->cmyka.k, 
c->cmyka.a);
+                break;
+              case CTX_CMYK:
+                ctx_color_set_cmyka (state, &source->color, c->cmyka.c, c->cmyka.m, c->cmyka.y, c->cmyka.k, 
1.0f);
+                break;
+              case CTX_DCMYKA:
+                ctx_color_set_dcmyka (state, &source->color, c->cmyka.c, c->cmyka.m, c->cmyka.y, c->cmyka.k, 
c->cmyka.a);
+                break;
+              case CTX_DCMYK:
+                ctx_color_set_dcmyka (state, &source->color, c->cmyka.c, c->cmyka.m, c->cmyka.y, c->cmyka.k, 
1.0f);
+                break;
 #endif
+              case CTX_GRAYA:
+                ctx_color_set_graya (state, &source->color, c->graya.g, c->graya.a);
+                break;
+              case CTX_GRAY:
+                ctx_color_set_graya (state, &source->color, c->graya.g, 1.0f);
+                break;
+            }
+        }
+        break;
+      case CTX_SET_RGBA_U8:
+        //ctx_source_deinit (&state->gstate.source);
+        //state->gstate.source_fill.type = CTX_SOURCE_COLOR;
+        {
+          int is_stroke = (state->source != 0);
+          CtxSource *source = is_stroke ?
+                                &state->gstate.source_stroke:
+                                &state->gstate.source_fill;
+          state->source = 0;
 
-int ctx_unichar_to_utf8 (uint32_t  ch, uint8_t  *dest);
-#define mrg_unichar_to_utf8 ctx_unichar_to_utf8
-void ctx_string_init (CtxString *string, int initial_size);
-
-VtLine *vt_line_new_with_size (const char *initial, int initial_size)
-{
-  VtLine *line = calloc (sizeof (VtLine), 1);
-  CtxString *string = (CtxString*)line;
-  ctx_string_init (string, initial_size);
-  if (initial)
-    { ctx_string_append_str (string, initial); }
-  line->style = calloc (sizeof (uint64_t), initial_size);
-  line->style_size = initial_size;
-  string->is_line = 1;
-  return line;
-}
-
-VtLine *vt_line_new (const char *initial)
-{
-  return vt_line_new_with_size (initial, 8);
-}
-
-typedef struct VtPty
-{
-  int        pty;
-  pid_t      pid;
-  int        done;
-} VtPty;
-ssize_t vtpty_read     (void *vtpty, void *buf, size_t count);
-ssize_t vtpty_write    (void *vtpty, const void *buf, size_t count);
-void    vtpty_resize   (void *vtpty, int cols, int rows,
-                        int px_width, int px_height);
-int     vtpty_waitdata (void  *vtpty, int timeout);
-extern  CtxList *vts;
-#define MAX_COLS 2048 // used for tabstops
-
-
-typedef struct AudioState
-{
-  int action;
-  int samplerate; // 8000
-  int channels;   // 1
-  int bits;       // 8
-  int type;       // 'u'    u-law  f-loat  s-igned u-nsigned
-  int buffer_size; // desired size of audiofragment in frames
-  // (both for feeding SDL and as desired chunking
-  //  size)
-
-
-  int mic;        // <- should
-  //    request permisson,
-  //    and if gotten, start streaming
-  //    audio packets in the incoming direction
-  //
-  int encoding;   // 'a' ascci85 'b' base64
-  int compression; // '0': none , 'z': zlib   'o': opus(reserved)
-
-  int frames;
-
-  uint8_t *data;
-  int      data_size;
-} AudioState;
-
-typedef struct GfxState
-{
-  int action;
-  int id;
-  int buf_width;
-  int buf_height;
-  int format;
-  int compression;
-  int transmission;
-  int multichunk;
-  int buf_size;
-  int x;
-  int y;
-  int w;
-  int h;
-  int x_cell_offset;
-  int y_cell_offset;
-  int columns;
-  int rows;
-  int z_index;
-  int delete;
-
-  uint8_t *data;
-  int   data_size;
-} GfxState;
-
-struct _VT
-{
-  VtPty      vtpty;
-  int       id;
-  unsigned char buf[BUFSIZ]; // need one per vt
-  int keyrepeat;
-  int       lastx;
-  int       lasty;
-  int        result;
-  long       rev;
-  //SDL_Rect   dirty;
-  float  dirtpad;
-  float  dirtpad1;
-  float  dirtpad2;
-  float  dirtpad3;
-
-  void  *client;
-
-  ssize_t (*write)   (void *serial_obj, const void *buf, size_t count);
-  ssize_t (*read)    (void *serial_obj, void *buf, size_t count);
-  int     (*waitdata)(void *serial_obj, int timeout);
-  void    (*resize)  (void *serial_obj, int cols, int rows, int px_width, int px_height);
-
-
-  char     *title;
-  void    (*state) (VT *vt, int byte);
-
-  AudioState audio; // < want to move this one level up and share impl
-  GfxState   gfx;
-
-  CtxList   *saved_lines;
-  int       in_alt_screen;
-  int       saved_line_count;
-  CtxList   *lines;
-  int       line_count;
-  CtxList   *scrollback;
-  int       scrollback_count;
-  int       leds[4];
-  uint64_t  cstyle;
-
-  uint8_t   fg_color[3];
-  uint8_t   bg_color[3];
-
-  int       in_smooth_scroll;
-  int       smooth_scroll;
-  float     scroll_offset;
-  int       debug;
-  int       bell;
-  int       origin;
-  int       at_line_home;
-  int       charset[4];
-  int       saved_charset[4];
-  int       shifted_in;
-  int       reverse_video;
-  int       echo;
-  int       bracket_paste;
-  int       ctx_events;
-  int       font_is_mono;
-  int       palette_no;
-  int       has_blink; // if any of the set characters are blinking
-  // updated on each draw of the screen
-  
-  int can_launch;
-
-  int unit_pixels;
-  int mouse;
-  int mouse_drag;
-  int mouse_all;
-  int mouse_decimal;
-
-
-  uint8_t    utf8_holding[64]; /* only 4 needed for utf8 - but it's purpose
-                                 is also overloaded for ctx journal command
-                                 buffering , and the bigger sizes for the svg-like
-                                 ctx parsing mode */
-  int        utf8_expected_bytes;
-  int        utf8_pos;
-
-
-  int        ref_len;
-  char       reference[16];
-  int        in_prev_match;
-  CtxParser *ctxp;
-  // text related data
-  float      letter_spacing;
-
-  float      word_spacing;
-  float      font_stretch;  // horizontal expansion
-  float      font_size_adjust;
-  // font-variant
-  // font-weight
-  // text-decoration
-
-  int        encoding;  // 0 = utf8 1=pc vga 2=ascii
-
-  int        local_editing; /* terminal operates without pty  */
-
-  int        insert_mode;
-  int        autowrap;
-  int        justify;
-  float      cursor_x;
-  int        cursor_y;
-  int        cols;
-  int        rows;
-  VtLine    *current_line;
-
-
-  int        cr_on_lf;
-  int        cursor_visible;
-  int        saved_x;
-  int        saved_y;
-  uint32_t   saved_style;
-  int        saved_origin;
-  int        cursor_key_application;
-  int        margin_top;
-  int        margin_bottom;
-  int        margin_left;
-  int        margin_right;
-
-  int        left_right_margin_mode;
-
-  int        scrollback_limit;
-  float      scroll;
-  int        scroll_on_input;
-  int        scroll_on_output;
-
-  char       *argument_buf;
-  int        argument_buf_len;
-  int        argument_buf_cap;
-  uint8_t    tabs[MAX_COLS];
-  int        inert;
-
-  int        width;
-  int        height;
-
-  int        cw; // cell width
-  int        ch; // cell height
-  float      font_to_cell_scale;
-  float      font_size; // when set with set_font_size, cw and ch are recomputed
-  float      line_spacing; // using line_spacing
-  float      scale_x;
-  float      scale_y;
-
-  int        ctx_pos;  // 1 is graphics above text, 0 or -1 is below text
-  Ctx       *root_ctx; /* only used for knowledge of top-level dimensions */
-
-  int        blink_state;
-
-  FILE      *log;
-
-  int cursor_down;
-
-  int select_begin_col;
-  int select_begin_row;
-  int select_start_col;
-  int select_start_row;
-  int select_end_col;
-  int select_end_row;
-  int select_begin_x;
-  int select_begin_y;
-  int select_active;
-
-  int popped;
-
-  /* used to make runs of background on one line be drawn
-   * as a single filled rectangle
-   */
-  int   bg_active;
-  float bg_x0;
-  float bg_y0;
-  float bg_width;
-  float bg_height;
-  uint8_t bg_rgba[4];
-};
-
-
-VT *vt_new (const char *command, int width, int height, float font_size, float line_spacing, int id, int 
can_launch);
-
-void vt_open_log (VT *vt, const char *path);
-
-void        vt_set_px_size        (VT *vt, int width, int height);
-void        vt_set_term_size      (VT *vt, int cols, int rows);
-
-int         vt_cw                 (VT *vt);
-int         vt_ch                 (VT *vt);
-void        vt_set_font_size      (VT *vt, float font_size);
-float       vt_get_font_size      (VT *vt);
-void        vt_set_line_spacing   (VT *vt, float line_spacing);
-
-const char *vt_find_shell_command (void);
-
-int         vt_keyrepeat          (VT *vt);
-
-int         vt_get_result         (VT *vt);
-int         vt_is_done            (VT *vt);
-int         vt_poll               (VT *vt, int timeout);
-long        vt_rev                (VT *vt);
-void        vt_destroy            (VT *vt);
-int         vt_has_blink (VT *vt);
-
-/* this is how mrg/mmm based key-events are fed into the vt engine
- */
-void        vt_feed_keystring     (VT *vt, CtxEvent *event, const char *str);
-
-void        vt_paste              (VT *vt, const char *str);
-
-/* not needed when passing a commandline for command to
- * run, but could be used for injecting commands, or
- * output from stored shell commands/sessions to display
- */
-//void        vt_feed_byte          (VT *vt, int byte);
-
-//)#define DEFAULT_SCROLLBACK   (1<<16)
-#define DEFAULT_SCROLLBACK   (1<<13)
-#define DEFAULT_ROWS         24
-#define DEFAULT_COLS         80
-
-int         vt_get_line_count       (VT *vt);
-
-pid_t       vt_get_pid              (VT *vt);
-
-const char *vt_get_line             (VT *vt, int no);
-
-void        vt_set_scrollback_lines (VT *vt, int scrollback_lines);
-int         vt_get_scrollback_lines (VT *vt);
-
-void        vt_set_scroll           (VT *vt, int scroll);
-int         vt_get_scroll           (VT *vt);
-
-int         vt_get_cols             (VT *vt);
-int         vt_get_rows             (VT *vt);
-
-char       *vt_get_selection        (VT *vt);
-int         vt_get_cursor_x         (VT *vt);
-int         vt_get_cursor_y         (VT *vt);
-
-void        vt_draw                 (VT *vt, Ctx *ctx, double x, double y);
-void        vt_register_events      (VT *vt, Ctx *ctx, double x0, double y0);
-
-void        vt_rev_inc              (VT *vt);
-
-int         vt_mic (VT *vt);
-void        vt_set_ctx (VT *vt, Ctx *ctx);  /* XXX: rename, this sets the parent/global ctx  */
-
-
-int         vt_get_local (VT *vt);           // this is a hack for the settings tab
-void        vt_set_local (VT *vt, int local);
-
-
-typedef enum VtMouseEvent
-{
-  VT_MOUSE_MOTION = 0,
-  VT_MOUSE_PRESS,
-  VT_MOUSE_DRAG,
-  VT_MOUSE_RELEASE,
-} VtMouseEvent;
-
-void vt_mouse (VT *vt, CtxEvent *event, VtMouseEvent type, int button, int x, int y, int px_x, int px_y);
-
-static ssize_t vt_write (VT *vt, const void *buf, size_t count)
-{
-  if (!vt->write) { return 0; }
-  return vt->write (&vt->vtpty, buf, count);
-}
-static ssize_t vt_read (VT *vt, void *buf, size_t count)
-{
-  if (!vt->read) { return 0; }
-  return vt->read (&vt->vtpty, buf, count);
-}
-static int vt_waitdata (VT *vt, int timeout)
-{
-  if (!vt->waitdata) { return 0; }
-  return vt->waitdata (&vt->vtpty, timeout);
-}
-static void vt_resize (VT *vt, int cols, int rows, int px_width, int px_height)
-{
-  if (vt && vt->resize)
-    { vt->resize (&vt->vtpty, cols, rows, px_width, px_height); }
-}
+          source->type = CTX_SOURCE_COLOR;
 
-/* atty - audio interface and driver for terminals
- * Copyright (C) 2020 Øyvind Kolås <pippin gimp org>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library. If not, see <http://www.gnu.org/licenses/>. 
- */
+          ctx_color_set_RGBA8 (state, &source->color,
+                               ctx_arg_u8 (0),
+                               ctx_arg_u8 (1),
+                               ctx_arg_u8 (2),
+                               ctx_arg_u8 (3) );
+        }
+        //for (int i = 0; i < 4; i ++)
+        //  state->gstate.source.color.rgba[i] = ctx_arg_u8(i);
+        break;
+      //case CTX_TEXTURE:
+      //  state->gstate.source.type = CTX_SOURCE_
+      //  break;
+      case CTX_LINEAR_GRADIENT:
+        {
+          int is_stroke = (state->source != 0);
+          CtxSource *source = is_stroke ?
+                                &state->gstate.source_stroke:
+                                &state->gstate.source_fill;
+          state->source = is_stroke ? 2 : 0;
 
+          float x0 = ctx_arg_float (0);
+          float y0 = ctx_arg_float (1);
+          float x1 = ctx_arg_float (2);
+          float y1 = ctx_arg_float (3);
+          float dx, dy, length, start, end;
 
-#ifndef NO_SDL
-#include <SDL.h>
-#include <zlib.h>
+          length = ctx_hypotf (x1-x0,y1-y0);
+          dx = (x1-x0) / length;
+          dy = (y1-y0) / length;
+          start = (x0 * dx + y0 * dy) / length;
+          end =   (x1 * dx + y1 * dy) / length;
+          source->linear_gradient.length = length;
+          source->linear_gradient.dx = dx;
+          source->linear_gradient.dy = dy;
+          source->linear_gradient.start = start;
+          source->linear_gradient.end = end;
+          source->linear_gradient.rdelta = (end-start)!=0.0?1.0f/(end - start):1.0;
+          source->type = CTX_SOURCE_LINEAR_GRADIENT;
+          source->transform = state->gstate.transform;
+          ctx_matrix_invert (&source->transform);
+        }
+        break;
+      case CTX_RADIAL_GRADIENT:
+        {
+          int is_stroke = (state->source != 0);
+          CtxSource *source = is_stroke ?
+                                &state->gstate.source_stroke:
+                                &state->gstate.source_fill;
+          state->source = is_stroke ? 2 : 0;
 
-static int ydec (const void *srcp, void *dstp, int count)
-{
-  const char *src = srcp;
-  char *dst = dstp;
-  int out_len = 0;
-  for (int i = 0; i < count; i ++)
-  {
-    int o = src[i];
-    switch (o)
-    {
-      case '=':
-              i++;
-              o = src[i];
-              o = (o-42-64) % 256;
-              break;
-      case '\n':
-      case '\033':
-      case '\r':
-      case '\0':
-              break;
-      default:
-              o = (o-42) % 256;
-              break;
+          float x0 = ctx_arg_float (0);
+          float y0 = ctx_arg_float (1);
+          float r0 = ctx_arg_float (2);
+          float x1 = ctx_arg_float (3);
+          float y1 = ctx_arg_float (4);
+          float r1 = ctx_arg_float (5);
+          source->radial_gradient.x0 = x0;
+          source->radial_gradient.y0 = y0;
+          source->radial_gradient.r0 = r0;
+          source->radial_gradient.x1 = x1;
+          source->radial_gradient.y1 = y1;
+          source->radial_gradient.r1 = r1;
+          source->radial_gradient.rdelta = (r1 - r0) != 0.0 ? 1.0f/(r1-r0):0.0;
+          source->type      = CTX_SOURCE_RADIAL_GRADIENT;
+          source->transform = state->gstate.transform;
+          ctx_matrix_invert (&source->transform);
+        }
+        break;
     }
-    dst[out_len++] = o;
-  }
-  dst[out_len]=0;
-  return out_len;
 }
 
-#ifndef NO_SDL
-static SDL_AudioDeviceID speaker_device = 0;
-#endif
-
-//#define AUDIO_CHUNK_SIZE 512
-
-// our pcm queue is currently always 16 bit
-// signed stereo
-
-static int16_t pcm_queue[1<<18];
-static int     pcm_write_pos = 0;
-static int     pcm_read_pos  = 0;
-
-void terminal_queue_pcm (int16_t sample_left, int16_t sample_right)
+void
+ctx_interpret_transforms (CtxState *state, CtxEntry *entry, void *data)
 {
-  if (pcm_write_pos >= (1<<18)-1)
-  {
-    /*  TODO  :  fix cyclic buffer */
-    pcm_write_pos = 0;
-    pcm_read_pos  = 0;
-  }
-  pcm_queue[pcm_write_pos++]=sample_left;
-  pcm_queue[pcm_write_pos++]=sample_right;
-}
-
-float click_volume = 0.05;
-
-void vt_feed_audio (VT *vt, void *samples, int bytes);
-int mic_device = 0;   // when non 0 we have an active mic device
-
-
-/*  https://jonathanhays.me/2018/11/14/mu-law-and-a-law-compression-tutorial/
- */
-
+  switch (entry->code)
+    {
+      case CTX_SAVE:
+        ctx_gstate_push (state);
+        break;
+      case CTX_RESTORE:
+        ctx_gstate_pop (state);
+        break;
+      case CTX_IDENTITY:
+        ctx_matrix_identity (&state->gstate.transform);
+        break;
+      case CTX_TRANSLATE:
+        ctx_matrix_translate (&state->gstate.transform,
+                              ctx_arg_float (0), ctx_arg_float (1) );
+        break;
+      case CTX_SCALE:
+        ctx_matrix_scale (&state->gstate.transform,
+                          ctx_arg_float (0), ctx_arg_float (1) );
+        break;
+      case CTX_ROTATE:
+        ctx_matrix_rotate (&state->gstate.transform, ctx_arg_float (0) );
+        break;
+      case CTX_APPLY_TRANSFORM:
+        {
+          CtxMatrix m;
+          ctx_matrix_set (&m,
+                          ctx_arg_float (0), ctx_arg_float (1),
+                          ctx_arg_float (2), ctx_arg_float (3),
+                          ctx_arg_float (4), ctx_arg_float (5) );
+          ctx_matrix_multiply (&state->gstate.transform,
+                               &state->gstate.transform, &m); // XXX verify order
+        }
 #if 0
-static char MuLawCompressTable[256] =
-{
-   0,0,1,1,2,2,2,2,3,3,3,3,3,3,3,3,
-   4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
-   5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
-   5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
-   6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
-   6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
-   6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
-   6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
-   7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
-   7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
-   7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
-   7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
-   7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
-   7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
-   7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
-   7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7
-};
-
-unsigned char LinearToMuLawSample(int16_t sample)
-{
-  const int cBias = 0x84;
-  const int cClip = 32635;
-  int sign = (sample >> 8) & 0x80;
-
-  if (sign)
-    sample = (int16_t)-sample;
-
-  if (sample > cClip)
-    sample = cClip;
-
-  sample = (int16_t)(sample + cBias);
-
-  int exponent = (int)MuLawCompressTable[(sample>>7) & 0xFF];
-  int mantissa = (sample >> (exponent+3)) & 0x0F;
-
-  int compressedByte = ~ (sign | (exponent << 4) | mantissa);
-
-  return (unsigned char)compressedByte;
-}
+        ctx_matrix_set (&state->gstate.transform,
+                        ctx_arg_float (0), ctx_arg_float (1),
+                        ctx_arg_float (2), ctx_arg_float (3),
+                        ctx_arg_float (4), ctx_arg_float (5) );
 #endif
-
-void vt_feed_audio (VT *vt, void *samples, int bytes)
-{
-  char buf[256];
-  AudioState *audio = &vt->audio;
-  uint8_t *data = samples;
-  int frames = bytes / (audio->bits/8) / audio->channels;
-
-  if (audio->compression == 'z')
-  {
-    uLongf len = compressBound(bytes);
-    data = malloc (len);
-    int z_result = compress (data, &len, samples, len);
-    if (z_result != Z_OK)
-    {
-      char buf[256]= "\033_Ao=z;zlib error2\033\\";
-      vt_write (vt, buf, strlen(buf));
-      data = samples;
-    }
-    else
-    {
-      bytes = len;
+        break;
     }
-  }
-
-  char *encoded = malloc (bytes * 2);
-  encoded[0]=0;
-  if (audio->encoding == 'a')
-  {
-    ctx_a85enc (data, encoded, bytes);
-  }
-  else /* if (audio->encoding == 'b')  */
-  {
-    ctx_bin2base64 (data, bytes, encoded);
-  }
-
-  sprintf (buf, "\033[_Af=%i;", frames);
-  vt_write (vt, buf, strlen (buf));
-  vt_write (vt, encoded, strlen(encoded));
-  free (encoded);
-
-  if (data != samples)
-    free (data);
-
-  //vt_write (vt, samples, bytes);
-  buf[0]='\033';
-  buf[1]='\\';
-  buf[2]=0;
-  vt_write (vt, buf, 2);
 }
 
-#define MIC_BUF_LEN 40960
-
-uint8_t mic_buf[MIC_BUF_LEN];
-int     mic_buf_pos = 0;
-
-static void mic_callback(void*     userdata,
-                         uint8_t * stream,
-                         int       len)
+/*
+ * this transforms the contents of entry according to ctx->transformation
+ */
+void
+ctx_interpret_pos_transform (CtxState *state, CtxEntry *entry, void *data)
 {
-  AudioState *audio = userdata;
-  int16_t *sstream = (void*)stream;
-  int frames;
-  int channels = audio->channels;
-
-  frames = len / 2;
-
-  if (audio->bits == 8)
-  {
-    if (audio->type == 'u')
+  CtxCommand *c = (CtxCommand *) entry;
+  float start_x = state->x;
+  float start_y = state->y;
+  int had_moved = state->has_moved;
+  switch (entry->code)
     {
-      for (int i = 0; i < frames; i++)
-      {
-        for (int c = 0; c < channels; c++)
+      case CTX_MOVE_TO:
+      case CTX_LINE_TO:
+        {
+          float x = c->c.x0;
+          float y = c->c.y0;
+          if ( ( ( (Ctx *) (data) )->transformation & CTX_TRANSFORMATION_SCREEN_SPACE) )
+            {
+              _ctx_user_to_device (state, &x, &y);
+              ctx_arg_float (0) = x;
+              ctx_arg_float (1) = y;
+            }
+        }
+        break;
+      case CTX_ARC:
+        if ( ( ( (Ctx *) (data) )->transformation & CTX_TRANSFORMATION_SCREEN_SPACE) )
+          {
+            float temp;
+            _ctx_user_to_device (state, &c->arc.x, &c->arc.y);
+            temp = 0;
+            _ctx_user_to_device_distance (state, &c->arc.radius, &temp);
+          }
+        break;
+      case CTX_LINEAR_GRADIENT:
+        if ( ( ( (Ctx *) (data) )->transformation & CTX_TRANSFORMATION_SCREEN_SPACE) )
+        {
+        _ctx_user_to_device (state, &c->linear_gradient.x1, &c->linear_gradient.y1);
+        _ctx_user_to_device (state, &c->linear_gradient.x2, &c->linear_gradient.y2);
+        }
+        break;
+      case CTX_RADIAL_GRADIENT:
+        if ( ( ( (Ctx *) (data) )->transformation & CTX_TRANSFORMATION_SCREEN_SPACE) )
+        {
+          float temp;
+          _ctx_user_to_device (state, &c->radial_gradient.x1, &c->radial_gradient.y1);
+          temp = 0;
+          _ctx_user_to_device_distance (state, &c->radial_gradient.r1, &temp);
+          _ctx_user_to_device (state, &c->radial_gradient.x2, &c->radial_gradient.y2);
+          temp = 0;
+          _ctx_user_to_device_distance (state, &c->radial_gradient.r2, &temp);
+        }
+        break;
+      case CTX_CURVE_TO:
+        if ( ( ( (Ctx *) (data) )->transformation & CTX_TRANSFORMATION_SCREEN_SPACE) )
+          {
+            for (int c = 0; c < 3; c ++)
+              {
+                float x = entry[c].data.f[0];
+                float y = entry[c].data.f[1];
+                _ctx_user_to_device (state, &x, &y);
+                entry[c].data.f[0] = x;
+                entry[c].data.f[1] = y;
+              }
+          }
+        break;
+      case CTX_QUAD_TO:
+        if ( ( ( (Ctx *) (data) )->transformation & CTX_TRANSFORMATION_SCREEN_SPACE) )
+          {
+            for (int c = 0; c < 2; c ++)
+              {
+                float x = entry[c].data.f[0];
+                float y = entry[c].data.f[1];
+                _ctx_user_to_device (state, &x, &y);
+                entry[c].data.f[0] = x;
+                entry[c].data.f[1] = y;
+              }
+          }
+        break;
+      case CTX_REL_MOVE_TO:
+      case CTX_REL_LINE_TO:
+        if ( ( ( (Ctx *) (data) )->transformation & CTX_TRANSFORMATION_SCREEN_SPACE) )
+          {
+            for (int c = 0; c < 1; c ++)
+              {
+                float x = state->x;
+                float y = state->y;
+                _ctx_user_to_device (state, &x, &y);
+                entry[c].data.f[0] = x;
+                entry[c].data.f[1] = y;
+              }
+            if (entry->code == CTX_REL_MOVE_TO)
+              { entry->code = CTX_MOVE_TO; }
+            else
+              { entry->code = CTX_LINE_TO; }
+          }
+        break;
+      case CTX_REL_CURVE_TO:
+        {
+          float nx = state->x + ctx_arg_float (4);
+          float ny = state->y + ctx_arg_float (5);
+          if ( ( ( (Ctx *) (data) )->transformation & CTX_TRANSFORMATION_SCREEN_SPACE) )
+            {
+              for (int c = 0; c < 3; c ++)
+                {
+                  float x = nx + entry[c].data.f[0];
+                  float y = ny + entry[c].data.f[1];
+                  _ctx_user_to_device (state, &x, &y);
+                  entry[c].data.f[0] = x;
+                  entry[c].data.f[1] = y;
+                }
+              entry->code = CTX_CURVE_TO;
+            }
+        }
+        break;
+      case CTX_REL_QUAD_TO:
         {
-          mic_buf[mic_buf_pos++] = LinearToMuLawSample (sstream[i]);
-          if (mic_buf_pos >= MIC_BUF_LEN - 4)
-            mic_buf_pos = 0;
+          float nx = state->x + ctx_arg_float (2);
+          float ny = state->y + ctx_arg_float (3);
+          if ( ( ( (Ctx *) (data) )->transformation & CTX_TRANSFORMATION_SCREEN_SPACE) )
+            {
+              for (int c = 0; c < 2; c ++)
+                {
+                  float x = nx + entry[c].data.f[0];
+                  float y = ny + entry[c].data.f[1];
+                  _ctx_user_to_device (state, &x, &y);
+                  entry[c].data.f[0] = x;
+                  entry[c].data.f[1] = y;
+                }
+              entry->code = CTX_QUAD_TO;
+            }
         }
-      }
+        break;
     }
-    else
+  if ((((Ctx *) (data) )->transformation & CTX_TRANSFORMATION_RELATIVE))
     {
-      for (int i = 0; i < frames; i++)
-      {
-        for (int c = 0; c <  audio->channels; c++)
+      int components = 0;
+      _ctx_user_to_device (state, &start_x, &start_y);
+      switch (entry->code)
         {
-          mic_buf[mic_buf_pos++] = (sstream[i]) / 256;
-          if (mic_buf_pos >= MIC_BUF_LEN - 4)
-            mic_buf_pos = 0;
+          case CTX_MOVE_TO:
+            if (had_moved) { components = 1; }
+            break;
+          case CTX_LINE_TO:
+            components = 1;
+            break;
+          case CTX_CURVE_TO:
+            components = 3;
+            break;
+          case CTX_QUAD_TO:
+            components = 2;
+            break;
+        }
+      if (components)
+        {
+          for (int c = 0; c < components; c++)
+            {
+              entry[c].data.f[0] -= start_x;
+              entry[c].data.f[1] -= start_y;
+            }
+          switch (entry->code)
+            {
+              case CTX_MOVE_TO:
+                entry[0].code = CTX_REL_MOVE_TO;
+                break;
+              case CTX_LINE_TO:
+                entry[0].code = CTX_REL_LINE_TO;
+                break;
+                break;
+              case CTX_CURVE_TO:
+                entry[0].code = CTX_REL_CURVE_TO;
+                break;
+              case CTX_QUAD_TO:
+                entry[0].code = CTX_REL_QUAD_TO;
+                break;
+            }
         }
-      }
-    }
-  }
-  else
-  {
-    for (int i = 0; i < frames; i++)
-    {
-      for (int c = 0; c < audio->channels; c++)
-      {
-        *((int16_t*)(&mic_buf[mic_buf_pos])) = (sstream[i]);
-        mic_buf_pos+=2;
-        if (mic_buf_pos >= MIC_BUF_LEN - 4)
-          mic_buf_pos = 0;
-      }
-    }
-  }
-}
-
-static long int ticks (void)
-{
-  struct timeval tp;
-  gettimeofday(&tp, NULL);
-  return tp.tv_sec * 1000 + tp.tv_usec / 1000;
-}
-
-static long int silence_start = 0;
-
-static void sdl_audio_init ()
-{
-  static int done = 0;
-  if (!done)
-  {
-#ifndef NO_SDL
-  if (SDL_Init(SDL_INIT_AUDIO) < 0)
-  {
-    fprintf (stderr, "sdl audio init fail\n");
-  }
-#endif
-  done = 1;
-  }
-}
-
-void vt_audio_task (VT *vt, int click)
-{
-  if (!vt) return;
-  AudioState *audio = &vt->audio;
-#ifndef NO_SDL
-
-  if (audio->mic)
-  {
-    if (mic_device == 0)
-    {
-      SDL_AudioSpec spec_want, spec_got;
-      sdl_audio_init ();
-
-      spec_want.freq     = audio->samplerate;
-      spec_want.channels = 1;
-      spec_want.format   = AUDIO_S16;
-      spec_want.samples  = audio->buffer_size;
-      spec_want.callback = mic_callback;
-      spec_want.userdata = audio;
-      mic_device = SDL_OpenAudioDevice(SDL_GetAudioDeviceName(0, SDL_TRUE), 1, &spec_want, &spec_got, 0);
-
-      SDL_PauseAudioDevice(mic_device, 0);
-    }
-
-    if (mic_buf_pos)
-    {
-      SDL_LockAudioDevice (mic_device);
-      vt_feed_audio (vt, mic_buf, mic_buf_pos);
-      mic_buf_pos = 0;
-      SDL_UnlockAudioDevice (mic_device);
-    }
-  }
-  else
-  {
-    if (mic_device)
-    {
-      SDL_PauseAudioDevice(mic_device, 1);
-      SDL_CloseAudioDevice(mic_device);
-      mic_device = 0;
-    }
-  }
-
-  int free_frames = audio->buffer_size - SDL_GetQueuedAudioSize(speaker_device);
-  int queued = (pcm_write_pos - pcm_read_pos)/2; // 2 for stereo
-  //if (free_frames > 6) free_frames -= 4;
-  int frames = queued;
-
-  if (frames > free_frames) frames = free_frames;
-  if (frames > 0)
-  {
-    if (speaker_device == 0)
-    {
-      SDL_AudioSpec spec_want, spec_got;
-      sdl_audio_init ();
-
-       spec_want.freq = audio->samplerate;
-       if (audio->bits == 8 && audio->type == 'u')
-       {
-         spec_want.format = AUDIO_S16;
-         spec_want.channels = 2;
-       }
-       else if (audio->bits == 8 && audio->type == 's')
-       {
-         spec_want.format = AUDIO_S8;
-         spec_want.channels = audio->channels;
-       }
-       else if (audio->bits == 16 && audio->type == 's')
-       {
-         spec_want.format = AUDIO_S16;
-         spec_want.channels = audio->channels;
-       }
-       else
-       {
-         spec_want.format = AUDIO_S16; // XXX  : error
-         spec_want.channels = audio->channels;
-       }
-
-       /* In SDL we always set 16bit stereo, but with the
-        * requested sample rate.
-        */
-      spec_want.format = AUDIO_S16;
-      spec_want.channels = 2;
-
-      spec_want.samples = audio->buffer_size;
-      spec_want.callback = NULL;
-
-      speaker_device = SDL_OpenAudioDevice (NULL, 0, &spec_want, &spec_got, 0);
-      if (!speaker_device){
-        fprintf (stderr, "sdl openaudiodevice fail\n");
-      }
-      SDL_PauseAudioDevice (speaker_device, 0);
-    }
-
-#if 0
-    {
-       int i;
-       unsigned char *b = (void*)(&pcm_queue[pcm_read_pos]);
-       for (i = 0; i < frames * 4; i++)
-       {
-         if ((b[i] > ' ') && (b[i] <= '~'))
-           fprintf (stderr, "[%c]", b[i]);
-         else
-           fprintf (stderr, "[%i]", b[i]);
-       }
-    }
-#endif
-    SDL_QueueAudio (speaker_device, (void*)&pcm_queue[pcm_read_pos], frames * 4);
-    pcm_read_pos += frames*2;
-    silence_start = ticks();
-  }
-  else
-  {
-    if (speaker_device &&  (ticks() - silence_start >  2000))
-    {
-      SDL_PauseAudioDevice(speaker_device, 1);
-      SDL_CloseAudioDevice(speaker_device);
-      speaker_device = 0;
     }
-  }
-#endif
-}
-
-void terminal_queue_pcm (int16_t sample_left, int16_t sample_right);
-
-static unsigned char vt_bell_audio[] = {
-#if 1
-  0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf,
-  0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
-  0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf,
-  0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
-  0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf,
-  0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
-  0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf,
-  0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
-  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-#else
-  0x7e, 0xfe, 0x7e, 0x7d, 0x7e, 0x7e, 0x7e, 0x7d, 0x7e, 0x7e, 0x7e, 0xff,
-  0xff, 0xfe, 0xfe, 0x7e, 0xff, 0xfe, 0xfd, 0xfd, 0xfe, 0xfe, 0xfd, 0xfd,
-  0xfe, 0xfe, 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, 0x7d, 0x7d,
-  0xfe, 0x7e, 0x7e, 0x7e, 0x7e, 0xfd, 0xfd, 0x7e, 0x7e, 0xfd, 0xfe, 0xfe,
-  0xfe, 0x7d, 0x7d, 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, 0xfe, 0xfe, 0xff, 0xfe,
-  0xfe, 0xfe, 0x7d, 0x7c, 0xfb, 0xfa, 0xfc, 0xfd, 0xfc, 0x76, 0x75, 0xfa,
-  0xfb, 0x7b, 0xfc, 0xef, 0xf6, 0x77, 0x6d, 0x7b, 0xf8, 0x78, 0x78, 0xfa,
-  0xf7, 0xfd, 0xfd, 0xfc, 0xfc, 0xfa, 0xf5, 0xf7, 0x7d, 0x7b, 0x78, 0x77,
-  0x7c, 0x6f, 0x7b, 0xf5, 0xfb, 0x7b, 0x7c, 0x78, 0x76, 0xea, 0xf2, 0x6d,
-  0xfd, 0xed, 0x7a, 0x6d, 0x6e, 0x71, 0xfe, 0x76, 0x6d, 0xfb, 0xef, 0x7e,
-  0xfa, 0xef, 0xec, 0xed, 0xf8, 0xf0, 0xea, 0xf9, 0x70, 0x7c, 0x7c, 0x6b,
-  0x6d, 0x75, 0xfb, 0xf1, 0xf9, 0xfe, 0xec, 0xea, 0x7c, 0x75, 0xff, 0xfb,
-  0x7d, 0x77, 0x7a, 0x71, 0x6e, 0x6c, 0x6e, 0x7b, 0x7e, 0x7a, 0x7c, 0xf4,
-  0xf9, 0x7b, 0x7b, 0xfa, 0xfe, 0x73, 0x79, 0xfe, 0x7b, 0x76, 0xfe, 0xf3,
-  0xf9, 0x76, 0x77, 0x7e, 0x7e, 0x7d, 0x7c, 0xf9, 0xee, 0xf2, 0x7d, 0xf8,
-  0xec, 0xee, 0xf7, 0xfa, 0xf7, 0xf6, 0xfd, 0x77, 0x75, 0x7b, 0xfa, 0xfe,
-  0x78, 0x79, 0x7c, 0x76, 0x7e, 0xf7, 0xfb, 0xf5, 0xf6, 0x75, 0x6f, 0x74,
-  0x6e, 0x6e, 0x6d, 0x6c, 0x7a, 0xf9, 0x75, 0x77, 0xf4, 0xf0, 0xf0, 0xf1,
-  0xef, 0xf3, 0xf6, 0xfd, 0xfc, 0xfb, 0xfd, 0xfc, 0xf6, 0xf8, 0xfb, 0xf9,
-  0xfa, 0xfd, 0xfb, 0xfc, 0x7a, 0x7c, 0x77, 0x75, 0x78, 0x7a, 0x7a, 0x78,
-  0x7a, 0xfa, 0xf9, 0x7c, 0xff, 0xfb, 0x7d, 0x77, 0x73, 0x6c, 0x6e, 0x7b,
-  0xfc, 0xfe, 0x7e, 0xfb, 0xf1, 0xeb, 0xee, 0xf6, 0xf6, 0xef, 0xf7, 0x7c,
-  0x76, 0x76, 0x7b, 0x7a, 0x7b, 0x73, 0x73, 0x7c, 0x79, 0x70, 0x79, 0xfb,
-  0xfd, 0xf8, 0xf9, 0xfc, 0xfc, 0xf8, 0xfb, 0xff, 0xfc, 0xf9, 0x75, 0x6f,
-  0x74, 0xfe, 0xff, 0xfd, 0x7d, 0xf5, 0xef, 0xee, 0xf8, 0xfd, 0xfd, 0xf3,
-  0xfa, 0xfe, 0xfe, 0x7c, 0x77, 0x7a, 0xfb, 0x79, 0x7e, 0x7b, 0xfd, 0x6d,
-  0xfc, 0x7a, 0xf0, 0x74, 0xee, 0x79, 0xea, 0x79, 0xf9, 0x6d, 0xf7, 0x71,
-  0x79, 0x76, 0x7c, 0x77, 0x6f, 0xf3, 0x6c, 0xe8, 0x67, 0xe3, 0x5e, 0xdc,
-  0x58, 0xd8, 0x4e, 0xce, 0x46, 0xc5, 0x40, 0x67, 0xba, 0x49, 0xac, 0x26,
-  0xba, 0x3e, 0xc5, 0xc8, 0x2b, 0xa8, 0x32, 0xbd, 0xe4, 0x3e, 0xb7, 0x3b,
-  0xb7, 0x3a, 0x33, 0xab, 0x3f, 0xc8, 0x46, 0x5f, 0xb7, 0x69, 0xd4, 0x3d,
-  0xc0, 0x4c, 0xf2, 0xdb, 0x3b, 0xdd, 0x69, 0xc5, 0x5f, 0xd8, 0xd8, 0xda,
-  0xc6, 0x39, 0xba, 0x3f, 0x35, 0xb3, 0x3e, 0xbb, 0x4a, 0x4a, 0xe7, 0x60,
-  0xae, 0x2c, 0xcb, 0x53, 0x45, 0xaf, 0x2a, 0xae, 0x3e, 0x4a, 0xae, 0x2a,
-  0xad, 0x38, 0xcc, 0xbb, 0x36, 0xae, 0x2c, 0xc6, 0xce, 0x38, 0xb1, 0x2f,
-  0xb9, 0x54, 0x7c, 0xb3, 0x28, 0xae, 0x3d, 0xcf, 0xbb, 0x2e, 0xb4, 0x41,
-  0xc6, 0x78, 0x39, 0xbc, 0x41, 0xc8, 0x59, 0x5b, 0xc7, 0x43, 0xbc, 0x45,
-  0xf3, 0xdc, 0x69, 0xd6, 0x48, 0xc9, 0x4e, 0xd9, 0x59, 0x61, 0xde, 0x4b,
-  0xc9, 0x44, 0xc8, 0xf5, 0x43, 0xc5, 0x37, 0xba, 0x65, 0x4d, 0xc8, 0x31,
-  0xaf, 0x47, 0xdb, 0xd6, 0x36, 0xad, 0x37, 0xbb, 0x61, 0x3a, 0xae, 0x2d,
-  0xb4, 0x47, 0x49, 0xb2, 0x30, 0xac, 0x3a, 0xcd, 0xbc, 0x2e, 0xaf, 0x32,
-  0xbd, 0xd7, 0x34, 0xaf, 0x32, 0xbb, 0x55, 0x4a, 0xb4, 0x30, 0xbb, 0x40,
-  0xeb, 0xbf, 0x39, 0xba, 0x3a, 0xd6, 0xd3, 0x48, 0xc0, 0x3b, 0xce, 0x5e,
-  0xe7, 0xd3, 0x46, 0xcb, 0x4c, 0xce, 0x74, 0x7e, 0x7e, 0x55, 0xcf, 0x44,
-  0xc4, 0x5b, 0x7c, 0xd3, 0x3f, 0xbc, 0x44, 0xcb, 0xfa, 0x46, 0xb9, 0x37,
-  0xb8, 0x51, 0x54, 0xbe, 0x33, 0xb1, 0x3d, 0xce, 0xc4, 0x34, 0xaf, 0x2f,
-  0xbd, 0xf8, 0x37, 0xb0, 0x2d, 0xb1, 0x4c, 0x4a, 0xb3, 0x2c, 0xb0, 0x3c,
-  0xe4, 0xbf, 0x2f, 0xaf, 0x35, 0xc0, 0xdb, 0x39, 0xb3, 0x31, 0xbb, 0x5d,
-  0x4c, 0xb8, 0x37, 0xb9, 0x48, 0xe8, 0xc7, 0x3d, 0xba, 0x43, 0xce, 0xdd,
-  0x52, 0xc6, 0x46, 0xce, 0x55, 0xdf, 0xe8, 0x52, 0xd5, 0x48, 0xca, 0x4d,
-  0xef, 0x68, 0x4c, 0xc7, 0x42, 0xc2, 0x49, 0x78, 0xce, 0x3e, 0xb9, 0x3c,
-  0xc8, 0xef, 0x43, 0xb7, 0x35, 0xb8, 0x4a, 0x53, 0xb8, 0x32, 0xaf, 0x3b,
-  0xde, 0xc1, 0x34, 0xaf, 0x32, 0xc3, 0xde, 0x3b, 0xaf, 0x2e, 0xb6, 0x4e,
-  0x48, 0xb4, 0x2e, 0xb2, 0x3d, 0xf0, 0xbf, 0x33, 0xb2, 0x37, 0xc8, 0xd9,
-  0x3d, 0xb5, 0x36, 0xbc, 0x56, 0x4f, 0xbc, 0x39, 0xbc, 0x47, 0xf6, 0xcf,
-  0x44, 0xbf, 0x46, 0xce, 0x68, 0x5b, 0xd0, 0x4a, 0xcc, 0x4d, 0xd3, 0x60,
-  0x6a, 0xcf, 0x49, 0xc8, 0x45, 0xd0, 0x7b, 0x58, 0xc3, 0x3c, 0xbf, 0x48,
-  0xe2, 0xc9, 0x3b, 0xb7, 0x39, 0xc5, 0xdb, 0x40, 0xb6, 0x31, 0xb9, 0x50,
-  0x50, 0xb9, 0x2f, 0xb3, 0x3b, 0xdc, 0xbf, 0x33, 0xaf, 0x32, 0xc1, 0xd6,
-  0x3b, 0xb0, 0x2f, 0xb8, 0x54, 0x4a, 0xb6, 0x30, 0xb4, 0x3f, 0xfd, 0xc0,
-  0x36, 0xb5, 0x39, 0xcc, 0xd9, 0x41, 0xb9, 0x39, 0xc2, 0x59, 0x57, 0xc1,
-  0x3e, 0xc2, 0x49, 0xe2, 0xd7, 0x4c, 0xcb, 0x47, 0xcf, 0x5b, 0xec, 0xe0,
-  0x53, 0xcb, 0x4b, 0xca, 0x55, 0xf6, 0xdb, 0x48, 0xc0, 0x43, 0xc9, 0x5f,
-  0x54, 0xc0, 0x3c, 0xbb, 0x43, 0xe8, 0xc8, 0x39, 0xb5, 0x39, 0xc6, 0xde,
-  0x3d, 0xb4, 0x32, 0xba, 0x4f, 0x4c, 0xb9, 0x30, 0xb2, 0x3c, 0xec, 0xc1,
-  0x33, 0xaf, 0x35, 0xc4, 0xd7, 0x3a, 0xb2, 0x31, 0xba, 0x56, 0x48, 0xb9,
-  0x33, 0xb7, 0x44, 0x7e, 0xc3, 0x39, 0xb7, 0x3d, 0xcd, 0xe3, 0x42, 0xbd,
-  0x3d, 0xc2, 0x58, 0x5d, 0xcb, 0x43, 0xc4, 0x4c, 0xd8, 0xf8, 0x58, 0xcd,
-  0x4c, 0xcb, 0x4e, 0xda, 0x71, 0x5c, 0xcc, 0x46, 0xc4, 0x49, 0xdc, 0xdc,
-  0x46, 0xbe, 0x3d, 0xc4, 0x59, 0x53, 0xbe, 0x38, 0xb8, 0x41, 0xe1, 0xc5,
-  0x39, 0xb3, 0x38, 0xc4, 0xde, 0x3d, 0xb2, 0x32, 0xb9, 0x4e, 0x4b, 0xb7,
-  0x30, 0xb3, 0x3d, 0xf2, 0xbf, 0x33, 0xb1, 0x36, 0xc9, 0xd9, 0x3a, 0xb4,
-  0x33, 0xbc, 0x58, 0x49, 0xba, 0x36, 0xb9, 0x46, 0x7e, 0xc8, 0x3c, 0xba,
-  0x3f, 0xcd, 0xe8, 0x4b, 0xc1, 0x41, 0xc7, 0x57, 0xfe, 0xd3, 0x4e, 0xc9,
-  0x4d, 0xd0, 0x5e, 0x7c, 0xda, 0x4e, 0xca, 0x47, 0xcd, 0x5b, 0x68, 0xcc,
-  0x40, 0xbf, 0x42, 0xd2, 0xe4, 0x42, 0xbd, 0x3a, 0xbf, 0x56, 0x50, 0xbd,
-  0x36, 0xb6, 0x40, 0xe2, 0xc5, 0x36, 0xb2, 0x37, 0xc5, 0xde, 0x3c, 0xb3,
-  0x32, 0xba, 0x52, 0x4a, 0xb7, 0x31, 0xb4, 0x3f, 0xef, 0xbf, 0x34, 0xb2,
-  0x39, 0xc8, 0xd3, 0x3c, 0xb6, 0x37, 0xbe, 0x5c, 0x4c, 0xbd, 0x39, 0xbc,
-  0x49, 0xf2, 0xcc, 0x3f, 0xbf, 0x44, 0xcf, 0xfd, 0x51, 0xca, 0x48, 0xcb,
-  0x54, 0xe4, 0xeb, 0x57, 0xcf, 0x4d, 0xcc, 0x4f, 0xe0, 0xee, 0x51, 0xc7,
-  0x44, 0xc6, 0x4f, 0x78, 0xcc, 0x3f, 0xbd, 0x3e, 0xce, 0xe5, 0x42, 0xba,
-  0x38, 0xbe, 0x50, 0x4f, 0xbb, 0x35, 0xb6, 0x3e, 0xe8, 0xc2, 0x36, 0xb2,
-  0x37, 0xc6, 0xda, 0x3c, 0xb3, 0x32, 0xba, 0x55, 0x4a, 0xb7, 0x33, 0xb5,
-  0x41, 0x7e, 0xbf, 0x37, 0xb4, 0x3b, 0xcd, 0xd8, 0x3e, 0xb8, 0x39, 0xc2,
-  0x5b, 0x4f, 0xc0, 0x3c, 0xbf, 0x4a, 0xee, 0xd1, 0x47, 0xc5, 0x47, 0xd0,
-  0x68, 0x63, 0xd0, 0x4d, 0xcd, 0x4e, 0xd6, 0x67, 0x68, 0xd6, 0x4b, 0xc9,
-  0x4a, 0xd1, 0x6e, 0x52, 0xc5, 0x3f, 0xc0, 0x4b, 0xfd, 0xcb, 0x3d, 0xba,
-  0x3d, 0xcc, 0xe2, 0x41, 0xb8, 0x37, 0xbc, 0x53, 0x4e, 0xba, 0x34, 0xb6,
-  0x3f, 0xee, 0xc1, 0x36, 0xb2, 0x38, 0xc8, 0xd6, 0x3c, 0xb3, 0x34, 0xbc,
-  0x58, 0x49, 0xb9, 0x34, 0xb7, 0x44, 0x73, 0xc3, 0x38, 0xb7, 0x3d, 0xce,
-  0xd9, 0x40, 0xbc, 0x3c, 0xc5, 0x5e, 0x55, 0xc6, 0x40, 0xc3, 0x4d, 0xe5,
-  0xde, 0x4d, 0xca, 0x4b, 0xce, 0x5c, 0xfa, 0xe1, 0x54, 0xcd, 0x4d, 0xcd,
-  0x56, 0xf3, 0xd9, 0x4a, 0xc4, 0x44, 0xcb, 0x67, 0x53, 0xc3, 0x3d, 0xbe,
-  0x48, 0xf0, 0xca, 0x3c, 0xb8, 0x3b, 0xca, 0xdf, 0x3f, 0xb7, 0x36, 0xbc,
-  0x54, 0x4c, 0xb9, 0x34, 0xb6, 0x40, 0xf7, 0xc1, 0x36, 0xb3, 0x39, 0xca,
-  0xd6, 0x3c, 0xb4, 0x35, 0xbe, 0x5b, 0x49, 0xba, 0x36, 0xba, 0x47, 0x6f,
-  0xc5, 0x3b, 0xba, 0x3f, 0xd2, 0xdd, 0x46, 0xbe, 0x3f, 0xc9, 0x5c, 0x5d,
-  0xcc, 0x47, 0xc8, 0x4e, 0xdd, 0xf5, 0x5a, 0xd1, 0x4e, 0xcf, 0x52, 0xde,
-  0x7d, 0x5c, 0xcf, 0x49, 0xc9, 0x4d, 0xdd, 0xde, 0x49, 0xc1, 0x3f, 0xc6,
-  0x5d, 0x53, 0xc0, 0x3b, 0xbc, 0x46, 0xeb, 0xc8, 0x3b, 0xb7, 0x3b, 0xc8,
-  0xde, 0x3e, 0xb6, 0x35, 0xbb, 0x57, 0x4c, 0xb9, 0x34, 0xb6, 0x42, 0xff,
-  0xc1, 0x36, 0xb4, 0x3a, 0xcc, 0xd7, 0x3d, 0xb7, 0x37, 0xbf, 0x5e, 0x4a,
-  0xbc, 0x38, 0xbc, 0x4a, 0x6e, 0xc9, 0x3e, 0xbe, 0x43, 0xd5, 0xe2, 0x4b,
-  0xc5, 0x45, 0xcb, 0x5e, 0x6e, 0xd6, 0x4e, 0xcd, 0x51, 0xd7, 0x65, 0x74,
-  0xdc, 0x54, 0xcd, 0x4d, 0xd1, 0x5e, 0x6b, 0xcf, 0x46, 0xc4, 0x47, 0xd7,
-  0xe3, 0x48, 0xbe, 0x3d, 0xc3, 0x58, 0x54, 0xbe, 0x39, 0xba, 0x43, 0xee,
-  0xc7, 0x3a, 0xb6, 0x3a, 0xc9, 0xdc, 0x3e, 0xb5, 0x35, 0xbd, 0x57, 0x4b,
-  0xb9, 0x34, 0xb7, 0x43, 0x6f, 0xc1, 0x38, 0xb6, 0x3c, 0xcf, 0xd3, 0x3e,
-  0xb8, 0x3a, 0xc3, 0x64, 0x4c, 0xbe, 0x3c, 0xbf, 0x4d, 0x72, 0xcc, 0x42,
-  0xc1, 0x48, 0xd9, 0xed, 0x51, 0xcc, 0x4b, 0xcf, 0x5a, 0xef, 0xe8, 0x59,
-  0xd3, 0x50, 0xd1, 0x58, 0xe1, 0xec, 0x56, 0xcc, 0x49, 0xca, 0x55, 0x7c,
-  0xcf, 0x44, 0xbf, 0x43, 0xcf, 0xe5, 0x47, 0xbc, 0x3b, 0xbf, 0x56, 0x52,
-  0xbe, 0x38, 0xb9, 0x42, 0xee, 0xc6, 0x39, 0xb6, 0x3a, 0xca, 0xdc, 0x3d,
-  0xb6, 0x36, 0xbd, 0x59, 0x4a, 0xba, 0x35, 0xb9, 0x45, 0x6b, 0xc2, 0x39,
-  0xb8, 0x3d, 0xd2, 0xd5, 0x3f, 0xbb, 0x3c, 0xc6, 0x69, 0x4f, 0xc1, 0x3f,
-  0xc3, 0x50, 0x7d, 0xd0, 0x49, 0xc8, 0x4c, 0xd8, 0x7d, 0x5d, 0xd4, 0x4f,
-  0xd2, 0x57, 0xde, 0x6e, 0x69, 0xda, 0x50, 0xcd, 0x4e, 0xd6, 0x71, 0x59,
-  0xca, 0x44, 0xc5, 0x4d, 0xf3, 0xce, 0x41, 0xbd, 0x3f, 0xcd, 0xe5, 0x45,
-  0xbb, 0x3a, 0xbf, 0x55, 0x51, 0xbd, 0x37, 0xb9, 0x43, 0xf1, 0xc5, 0x39,
-  0xb6, 0x3b, 0xcc, 0xd9, 0x3d, 0xb7, 0x37, 0xbf, 0x5d, 0x49, 0xbb, 0x37,
-  0xba, 0x48, 0x69, 0xc4, 0x3b, 0xba, 0x40, 0xd5, 0xd7, 0x42, 0xbd, 0x3f,
-  0xc9, 0x69, 0x52, 0xc7, 0x44, 0xc7, 0x52, 0xfa, 0xda, 0x4f, 0xcd, 0x4f,
-  0xd8, 0x66, 0x72, 0xdf, 0x59, 0xd4, 0x52, 0xd6, 0x5d, 0xef, 0xde, 0x4f,
-  0xca, 0x49, 0xce, 0x64, 0x5a, 0xc8, 0x40, 0xc1, 0x4a, 0xec, 0xcd, 0x3f,
-  0xbc, 0x3e, 0xcc, 0xe5, 0x43, 0xba, 0x39, 0xbe, 0x55, 0x4f, 0xbc, 0x37,
-  0xb9, 0x43, 0xf8, 0xc4, 0x39, 0xb6, 0x3b, 0xcd, 0xd7, 0x3e, 0xb7, 0x38,
-  0xc0, 0x60, 0x4b, 0xbc, 0x39, 0xbc, 0x4b, 0x6b, 0xc6, 0x3d, 0xbd, 0x43,
-  0xd9, 0xda, 0x47, 0xc1, 0x42, 0xcd, 0x66, 0x5a, 0xcd, 0x48, 0xcc, 0x54,
-  0xe9, 0xe9, 0x59, 0xd5, 0x54, 0xd5, 0x5c, 0xe4, 0xfb, 0x61, 0xd4, 0x4f,
-  0xcd, 0x51, 0xdf, 0xe3, 0x4e, 0xc5, 0x45, 0xca, 0x5e, 0x5b, 0xc6, 0x3e,
-  0xbf, 0x48, 0xea, 0xcc, 0x3e, 0xbb, 0x3d, 0xcb, 0xe4, 0x42, 0xba, 0x38,
-  0xbe, 0x56, 0x4e, 0xbc, 0x37, 0xb9, 0x44, 0x7b, 0xc4, 0x39, 0xb7, 0x3c,
-  0xcf, 0xd7, 0x3e, 0xb9, 0x3a, 0xc2, 0x62, 0x4c, 0xbd, 0x3b, 0xbe, 0x4d,
-  0x6b, 0xc9, 0x3f, 0xbf, 0x46, 0xd9, 0xde, 0x4b, 0xc5, 0x47, 0xce, 0x63,
-  0x65, 0xd3, 0x4e, 0xcf, 0x55, 0xdf, 0x74, 0x67, 0xdc, 0x55, 0xd3, 0x54,
-  0xda, 0x68, 0x67, 0xd5, 0x4c, 0xca, 0x4d, 0xdc, 0xe7, 0x4d, 0xc4, 0x42,
-  0xc8, 0x5b, 0x59, 0xc4, 0x3d, 0xbe, 0x47, 0xec, 0xcc, 0x3d, 0xba, 0x3d,
-  0xcc, 0xe1, 0x42, 0xba, 0x39, 0xbf, 0x5a, 0x4f, 0xbc, 0x38, 0xba, 0x46,
-  0x7d, 0xc5, 0x3b, 0xb9, 0x3e, 0xd0, 0xd8, 0x40, 0xbb, 0x3c, 0xc5, 0x63,
-  0x4d, 0xc0, 0x3d, 0xc1, 0x4e, 0x6e, 0xcd, 0x44, 0xc4, 0x49, 0xdb, 0xec,
-  0x50, 0xcc, 0x4a, 0xd1, 0x5c, 0x7b, 0xde, 0x56, 0xd2, 0x54, 0xd8, 0x62,
-  0xf2, 0xe2, 0x58, 0xcf, 0x4e, 0xd0, 0x5d, 0x72, 0xd3, 0x4a, 0xc5, 0x49,
-  0xd6, 0xe8, 0x4b, 0xc0, 0x3f, 0xc5, 0x5b, 0x58, 0xc2, 0x3c, 0xbd, 0x47,
-  0xee, 0xca, 0x3d, 0xba, 0x3d, 0xcd, 0xdf, 0x41, 0xba, 0x3a, 0xc0, 0x5b,
-  0x4d, 0xbd, 0x39, 0xbc, 0x48, 0x73, 0xc6, 0x3c, 0xbb, 0x3f, 0xd4, 0xd9,
-  0x43, 0xbd, 0x3e, 0xc8, 0x67, 0x50, 0xc4, 0x40, 0xc4, 0x51, 0x7b, 0xd1,
-  0x4a, 0xc8, 0x4d, 0xd9, 0xf6, 0x5c, 0xd0, 0x51, 0xd2, 0x5c, 0xe8, 0xf2,
-  0x62, 0xd8, 0x55, 0xd4, 0x56, 0xe1, 0x7c, 0x59, 0xcf, 0x49, 0xcc, 0x53,
-  0x7a, 0xd4, 0x46, 0xc4, 0x45, 0xd5, 0xef, 0x49, 0xc0, 0x3d, 0xc5, 0x57,
-  0x55, 0xc2, 0x3b, 0xbd, 0x47, 0xed, 0xc9, 0x3d, 0xb9, 0x3e, 0xcc, 0xdb,
-  0x43, 0xb9, 0x3b, 0xc0, 0x61, 0x4f, 0xbd, 0x3b, 0xbc, 0x4b, 0x75, 0xc6,
-  0x3d, 0xbc, 0x43, 0xd7, 0xd9, 0x45, 0xbf, 0x40, 0xcc, 0x68, 0x54, 0xc9,
-  0x44, 0xca, 0x53, 0x7d, 0xda, 0x4d, 0xce, 0x4f, 0xdb, 0x6d, 0x68, 0xdd,
-  0x55, 0xd6, 0x56, 0xdc, 0x67, 0x71, 0xde, 0x53, 0xce, 0x4f, 0xd6, 0x6e,
-  0x5c, 0xcc, 0x47, 0xc7, 0x4f, 0xef, 0xd0, 0x45, 0xbf, 0x44, 0xcf, 0xe6,
-  0x48, 0xbe, 0x3d, 0xc3, 0x5a, 0x54, 0xc0, 0x3b, 0xbc, 0x47, 0xfa, 0xc9,
-  0x3c, 0xba, 0x3e, 0xd0, 0xdc, 0x41, 0xbb, 0x3b, 0xc4, 0x5f, 0x4d, 0xbf,
-  0x3c, 0xbf, 0x4c, 0x6d, 0xc9, 0x3f, 0xbe, 0x46, 0xda, 0xdc, 0x49, 0xc3,
-  0x45, 0xce, 0x6b, 0x5b, 0xcd, 0x4a, 0xcd, 0x57, 0xee, 0xe4, 0x58, 0xd4,
-  0x54, 0xd9, 0x60, 0xf1, 0xed, 0x5f, 0xd7, 0x53, 0xd3, 0x5a, 0xeb, 0xe1,
-  0x52, 0xca, 0x4b, 0xce, 0x68, 0x5c, 0xc9, 0x44, 0xc4, 0x4e, 0xec, 0xce,
-  0x43, 0xbe, 0x42, 0xcf, 0xe4, 0x47, 0xbd, 0x3c, 0xc3, 0x5b, 0x50, 0xbf,
-  0x3b, 0xbd, 0x48, 0x73, 0xc8, 0x3c, 0xbb, 0x3f, 0xd4, 0xda, 0x41, 0xbc,
-  0x3d, 0xc7, 0x67, 0x4d, 0xc0, 0x3d, 0xc2, 0x4f, 0x68, 0xcb, 0x42, 0xc2,
-  0x4a, 0xdd, 0xdd, 0x4d, 0xc7, 0x4a, 0xd1, 0x6d, 0x65, 0xd3, 0x52, 0xd1,
-  0x5b, 0xe3, 0xf8, 0x68, 0xdd, 0x5a, 0xd8, 0x59, 0xde, 0x6d, 0x68, 0xd9,
-  0x4e, 0xce, 0x4f, 0xe1, 0xeb, 0x4e, 0xc9, 0x45, 0xcd, 0x5d, 0x58, 0xc9,
-  0x3f, 0xc2, 0x4b, 0xf6, 0xce, 0x3f, 0xbd, 0x40, 0xcf, 0xe0, 0x45, 0xbc,
-  0x3d, 0xc3, 0x5e, 0x51, 0xbf, 0x3c, 0xbd, 0x4b, 0x7b, 0xc7, 0x3e, 0xbb,
-  0x42, 0xd4, 0xd6, 0x45, 0xbd, 0x3f, 0xc9, 0x6f, 0x50, 0xc3, 0x40, 0xc5,
-  0x53, 0x6c, 0xce, 0x46, 0xc7, 0x4c, 0xe0, 0xeb, 0x51, 0xce, 0x4d, 0xd7,
-  0x5f, 0x6c, 0xe3, 0x57, 0xd9, 0x57, 0xde, 0x64, 0xfd, 0xe9, 0x5b, 0xd5,
-  0x52, 0xd5, 0x61, 0x78, 0xd6, 0x4d, 0xc9, 0x4e, 0xd8, 0xe5, 0x4e, 0xc4,
-  0x44, 0xc9, 0x5f, 0x5a, 0xc6, 0x3f, 0xc0, 0x4b, 0xf5, 0xcd, 0x3f, 0xbd,
-  0x40, 0xd2, 0xdf, 0x43, 0xbd, 0x3c, 0xc6, 0x5e, 0x4e, 0xbf, 0x3b, 0xbf,
-  0x4c, 0x6b, 0xc9, 0x3e, 0xbd, 0x44, 0xda, 0xd8, 0x45, 0xbf, 0x42, 0xcc,
-  0x75, 0x52, 0xc6, 0x45, 0xc8, 0x58, 0x76, 0xd2, 0x4c, 0xca, 0x51, 0xdd,
-  0xed, 0x5d, 0xd3, 0x55, 0xd7, 0x61, 0xec, 0xef, 0x65, 0xdc, 0x58, 0xd7,
-  0x5a, 0xe4, 0xfd, 0x5c, 0xd2, 0x4c, 0xcf, 0x57, 0x77, 0xd8, 0x49, 0xc7,
-  0x48, 0xd8, 0xeb, 0x4b, 0xc3, 0x40, 0xc8, 0x5d, 0x57, 0xc4, 0x3e, 0xbf,
-  0x4b, 0xf8, 0xca, 0x3f, 0xbc, 0x42, 0xd1, 0xd9, 0x45, 0xbc, 0x3e, 0xc6,
-  0x6a, 0x4f, 0xbf, 0x3d, 0xc0, 0x4f, 0x67, 0xc9, 0x3f, 0xbf, 0x47, 0xdf,
-  0xdb, 0x47, 0xc4, 0x44, 0xd1, 0x6c, 0x53, 0xcc, 0x48, 0xce, 0x58, 0x74,
-  0xdc, 0x50, 0xd1, 0x54, 0xdf, 0x74, 0x6a, 0xde, 0x5b, 0xd9, 0x5d, 0xdd,
-  0x6e, 0xfc, 0xdd, 0x5a, 0xcf, 0x54, 0xd6, 0x7b, 0x60, 0xcd, 0x4b, 0xc9,
-  0x55, 0xf2, 0xd2, 0x48, 0xc3, 0x47, 0xd5, 0xe6, 0x4a, 0xc1, 0x3f, 0xc8,
-  0x5d, 0x52, 0xc4, 0x3d, 0xc0, 0x4b, 0x71, 0xcb, 0x3e, 0xbd, 0x41, 0xd7,
-  0xdc, 0x43, 0xbe, 0x3e, 0xc9, 0x6a, 0x4e, 0xc1, 0x3e, 0xc3, 0x52, 0x6a,
-  0xca, 0x43, 0xc1, 0x4b, 0xdd, 0xda, 0x4c, 0xc6, 0x4a, 0xd1, 0x77, 0x5d,
-  0xce, 0x4e, 0xcf, 0x5c, 0xf3, 0xe5, 0x5a, 0xd8, 0x58, 0xdd, 0x62, 0xfb,
-  0xf4, 0x5e, 0xdc, 0x54, 0xd9, 0x5a, 0xf6, 0xea, 0x52, 0xce, 0x4c, 0xd4,
-  0x67, 0x5c, 0xcc, 0x46, 0xc8, 0x50, 0xf8, 0xd0, 0x45, 0xc0, 0x46, 0xd3,
-  0xe1, 0x49, 0xbf, 0x3f, 0xc6, 0x63, 0x54, 0xc1, 0x3e, 0xbf, 0x4d, 0x76,
-  0xc9, 0x3f, 0xbd, 0x44, 0xd8, 0xda, 0x44, 0xbe, 0x3f, 0xcb, 0x6b, 0x4e,
-  0xc4, 0x3f, 0xc7, 0x53, 0x66, 0xcd, 0x45, 0xc6, 0x4d, 0xe3, 0xdf, 0x4e,
-  0xcb, 0x4d, 0xd5, 0x6f, 0x65, 0xd6, 0x55, 0xd4, 0x5e, 0xe5, 0xf1, 0x6b,
-  0xdc, 0x5d, 0xd8, 0x5e, 0xdf, 0x79, 0x6d, 0xd9, 0x53, 0xcf, 0x56, 0xe3,
-  0xe8, 0x52, 0xcb, 0x49, 0xcf, 0x61, 0x5a, 0xcb, 0x43, 0xc6, 0x4d, 0x7c,
-  0xd1, 0x42, 0xc0, 0x43, 0xd6, 0xe3, 0x47, 0xbf, 0x3e, 0xc8, 0x63, 0x51,
-  0xc1, 0x3e, 0xc0, 0x4e, 0x6f, 0xc9, 0x3f, 0xbe, 0x47, 0xd9, 0xd7, 0x47,
-  0xbf, 0x43, 0xcc, 0x79, 0x51, 0xc6, 0x44, 0xc9, 0x58, 0x6a, 0xd0, 0x49,
-  0xca, 0x4f, 0xe6, 0xea, 0x54, 0xd1, 0x50, 0xdb, 0x65, 0x6e, 0xe4, 0x5a,
-  0xdc, 0x5a, 0xe1, 0x67, 0xfe, 0xea, 0x5d, 0xd7, 0x55, 0xd8, 0x63, 0x74,
-  0xd8, 0x4f, 0xcb, 0x4f, 0xdc, 0xe5, 0x50, 0xc7, 0x47, 0xcc, 0x65, 0x5b,
-  0xc8, 0x43, 0xc4, 0x4e, 0xfa, 0xcd, 0x42, 0xbf, 0x44, 0xd6, 0xdf, 0x46,
-  0xbf, 0x3f, 0xc9, 0x64, 0x4f, 0xc3, 0x3e, 0xc3, 0x4f, 0x68, 0xcb, 0x40,
-  0xc0, 0x48, 0xde, 0xd9, 0x48, 0xc2, 0x45, 0xcf, 0x7b, 0x55, 0xc9, 0x48,
-  0xcb, 0x5c, 0x75, 0xd3, 0x4f, 0xcd, 0x56, 0xe0, 0xed, 0x5e, 0xd7, 0x58,
-  0xdb, 0x63, 0xef, 0xf5, 0x65, 0xdf, 0x5a, 0xdb, 0x5b, 0xea, 0x7d, 0x5d,
-  0xd6, 0x4e, 0xd3, 0x59, 0x73, 0xd9, 0x4b, 0xca, 0x4b, 0xdc, 0xeb, 0x4d,
-  0xc6, 0x44, 0xcb, 0x61, 0x59, 0xc7, 0x41, 0xc2, 0x4e, 0xfb, 0xcc, 0x42,
-  0xbe, 0x46, 0xd5, 0xdb, 0x47, 0xbe, 0x40, 0xc9, 0x6e, 0x50, 0xc2, 0x3f,
-  0xc4, 0x52, 0x69, 0xcb, 0x42, 0xc3, 0x4a, 0xe2, 0xdc, 0x49, 0xc6, 0x48,
-  0xd4, 0x71, 0x56, 0xcd, 0x4a, 0xcf, 0x5b, 0x73, 0xdd, 0x53, 0xd3, 0x57,
-  0xe2, 0x78, 0x6a, 0xdf, 0x5d, 0xdb, 0x5e, 0xe1, 0x6f, 0x7a, 0xe0, 0x5b,
-  0xd4, 0x57, 0xdb, 0x79, 0x5f, 0xd0, 0x4d, 0xcd, 0x58, 0xfd, 0xd6, 0x4a,
-  0xc7, 0x4a, 0xd9, 0xe8, 0x4c, 0xc5, 0x42, 0xcb, 0x5f, 0x55, 0xc7, 0x3f,
-  0xc4, 0x4e, 0x71, 0xcd, 0x41, 0xbf, 0x46, 0xd9, 0xdb, 0x47, 0xbf, 0x41,
-  0xcb, 0x70, 0x51, 0xc4, 0x42, 0xc5, 0x57, 0x6b, 0xcc, 0x46, 0xc4, 0x4d,
-  0xe0, 0xdb, 0x4d, 0xc8, 0x4c, 0xd5, 0x78, 0x5d, 0xd1, 0x4f, 0xd3, 0x5d,
-  0xfb, 0xe6, 0x5a, 0xda, 0x59, 0xe1, 0x67, 0x7c, 0xf1, 0x5f, 0xde, 0x58,
-  0xdc, 0x5e, 0xf9, 0xe8, 0x56, 0xd1, 0x4f, 0xd7, 0x6d, 0x5e, 0xce, 0x4a,
-  0xcb, 0x55, 0xf7, 0xd3, 0x49, 0xc4, 0x4a, 0xd7, 0xe3, 0x4c, 0xc2, 0x43,
-  0xca, 0x66, 0x56, 0xc5, 0x40, 0xc3, 0x4f, 0x74, 0xcc, 0x42, 0xc0, 0x47,
-  0xdb, 0xdc, 0x47, 0xc1, 0x42, 0xce, 0x6e, 0x50, 0xc7, 0x43, 0xc9, 0x57,
-  0x67, 0xcf, 0x48, 0xc9, 0x4e, 0xe6, 0xe0, 0x50, 0xcd, 0x4e, 0xd8, 0x6f,
-  0x65, 0xd7, 0x55, 0xd7, 0x5f, 0xeb, 0xf3, 0x68, 0xde, 0x5e, 0xdc, 0x60,
-  0xe5, 0x7b, 0x6b, 0xdc, 0x56, 0xd4, 0x59, 0xe8, 0xe8, 0x55, 0xcd, 0x4c,
-  0xd3, 0x68, 0x5c, 0xcd, 0x47, 0xc9, 0x52, 0xfe, 0xd2, 0x47, 0xc4, 0x48,
-  0xd8, 0xe2, 0x4a, 0xc2, 0x42, 0xcb, 0x68, 0x54, 0xc5, 0x40, 0xc4, 0x51,
-  0x6e, 0xcc, 0x42, 0xc1, 0x49, 0xdd, 0xda, 0x49, 0xc3, 0x46, 0xcf, 0x7a,
-  0x53, 0xc8, 0x46, 0xcb, 0x5b, 0x6a, 0xd2, 0x4b, 0xcc, 0x52, 0xe7, 0xe7,
-  0x56, 0xd2, 0x53, 0xdc, 0x6a, 0x6d, 0xe2, 0x5b, 0xdc, 0x5e, 0xe5, 0x6d,
-  0x7a, 0xea, 0x5f, 0xda, 0x59, 0xdc, 0x68, 0x70, 0xdb, 0x52, 0xcf, 0x53,
-  0xe0, 0xe9, 0x53, 0xcb, 0x4a, 0xcf, 0x66, 0x5c, 0xcb, 0x46, 0xc7, 0x51,
-  0xfb, 0xcf, 0x46, 0xc2, 0x48, 0xd8, 0xdf, 0x4a, 0xc2, 0x42, 0xcb, 0x69,
-  0x53, 0xc5, 0x41, 0xc5, 0x53, 0x6b, 0xcc, 0x44, 0xc3, 0x4a, 0xe0, 0xdb,
-  0x4a, 0xc5, 0x48, 0xd2, 0x78, 0x55, 0xcb, 0x49, 0xce, 0x5c, 0x6e, 0xd7,
-  0x4f, 0xcf, 0x56, 0xe6, 0xef, 0x5d, 0xd8, 0x59, 0xdc, 0x67, 0xf6, 0xee,
-  0x65, 0xdf, 0x5d, 0xdd, 0x61, 0xec, 0xf4, 0x5f, 0xd8, 0x54, 0xd6, 0x5f,
-  0x78, 0xda, 0x4f, 0xcc, 0x4f, 0xde, 0xea, 0x50, 0xc9, 0x48, 0xce, 0x64,
-  0x5a, 0xca, 0x45, 0xc7, 0x50, 0x7b, 0xcf, 0x45, 0xc3, 0x48, 0xda, 0xde,
-  0x4a, 0xc2, 0x43, 0xcc, 0x6d, 0x53, 0xc6, 0x43, 0xc7, 0x56, 0x6a, 0xcd,
-  0x46, 0xc5, 0x4d, 0xe2, 0xdc, 0x4c, 0xc8, 0x4b, 0xd5, 0x7a, 0x59, 0xce,
-  0x4d, 0xd0, 0x5e, 0x76, 0xdc, 0x55, 0xd4, 0x5a, 0xe5, 0x7d, 0x68, 0xdf,
-  0x5d, 0xde, 0x60, 0xe9, 0x73, 0x70, 0xe4, 0x5c, 0xd9, 0x59, 0xe1, 0x7a,
-  0x60, 0xd5, 0x4f, 0xd1, 0x5b, 0x7e, 0xd9, 0x4d, 0xca, 0x4d, 0xdb, 0xe8,
-  0x4f, 0xc8, 0x47, 0xcd, 0x66, 0x5a, 0xc9, 0x44, 0xc6, 0x51, 0x78, 0xce,
-  0x45, 0xc3, 0x49, 0xdb, 0xdd, 0x49, 0xc3, 0x44, 0xce, 0x6f, 0x53, 0xc7,
-  0x44, 0xc9, 0x57, 0x69, 0xce, 0x48, 0xc8, 0x4e, 0xe6, 0xde, 0x4e, 0xcb,
-  0x4d, 0xd8, 0x78, 0x5d, 0xd2, 0x50, 0xd5, 0x5f, 0xfc, 0xe4, 0x5c, 0xda,
-  0x5c, 0xe2, 0x6e, 0x7d, 0xeb, 0x63, 0xde, 0x5d, 0xde, 0x65, 0xf9, 0xe7,
-  0x5a, 0xd5, 0x54, 0xdb, 0x6f, 0x5f, 0xd2, 0x4d, 0xce, 0x58, 0x7d, 0xd7,
-  0x4b, 0xc9, 0x4c, 0xdb, 0xe7, 0x4e, 0xc6, 0x46, 0xcd, 0x67, 0x58, 0xc8,
-  0x44, 0xc7, 0x53, 0x72, 0xce, 0x45, 0xc3, 0x4a, 0xdd, 0xdc, 0x4a, 0xc4,
-  0x46, 0xcf, 0x76, 0x54, 0xc9, 0x46, 0xcb, 0x5a, 0x69, 0xd0, 0x4a, 0xcb,
-  0x51, 0xe8, 0xe1, 0x52, 0xce, 0x50, 0xdb, 0x72, 0x63, 0xda, 0x56, 0xda,
-  0x5f, 0xf1, 0xf3, 0x65, 0xe0, 0x5e, 0xe0, 0x63, 0xec, 0x7c, 0x6a, 0xde,
-  0x59, 0xd8, 0x5c, 0xec, 0xe9, 0x58, 0xd0, 0x4f, 0xd7, 0x6c, 0x5f, 0xcf,
-  0x4b, 0xcc, 0x56, 0xfc, 0xd5, 0x4a, 0xc7, 0x4b, 0xdb, 0xe4, 0x4d, 0xc6,
-  0x46, 0xcd, 0x69, 0x57, 0xc8, 0x44, 0xc7, 0x54, 0x6e, 0xce, 0x46, 0xc5,
-  0x4b, 0xdf, 0xdc, 0x4b, 0xc6, 0x48, 0xd2, 0x79, 0x55, 0xcb, 0x49, 0xcd,
-  0x5d, 0x6c, 0xd4, 0x4d, 0xcd, 0x55, 0xe8, 0xe6, 0x58, 0xd3, 0x55, 0xdd,
-  0x6e, 0x6d, 0xe0, 0x5d, 0xdd, 0x5f, 0xe9, 0x73, 0x75, 0xe9, 0x60, 0xdd,
-  0x5c, 0xe0, 0x6c, 0x6e, 0xde, 0x55, 0xd4, 0x57, 0xe6, 0xeb, 0x56, 0xce,
-  0x4d, 0xd4, 0x6a, 0x5e, 0xce, 0x49, 0xcb, 0x55, 0xfe, 0xd3, 0x49, 0xc6,
-  0x4b, 0xdb, 0xe2, 0x4c, 0xc5, 0x46, 0xce, 0x6c, 0x56, 0xc8, 0x45, 0xc8,
-  0x56, 0x6c, 0xce, 0x47, 0xc6, 0x4d, 0xe3, 0xdd, 0x4c, 0xc8, 0x4a, 0xd5,
-  0x7a, 0x57, 0xcd, 0x4b, 0xcf, 0x5e, 0x6d, 0xd8, 0x50, 0xd1, 0x58, 0xe9,
-  0xee, 0x5d, 0xd9, 0x5a, 0xde, 0x6a, 0xfe, 0xec, 0x65, 0xe0, 0x5f, 0xe0,
-  0x67, 0xf0, 0xf1, 0x63, 0xda, 0x58, 0xda, 0x65, 0x78, 0xdc, 0x53, 0xcf,
-  0x54, 0xe1, 0xeb, 0x54, 0xcc, 0x4b, 0xd1, 0x68, 0x5d, 0xcd, 0x48, 0xca,
-  0x54, 0x7b, 0xd2, 0x48, 0xc6, 0x4b, 0xdc, 0xe0, 0x4c, 0xc6, 0x47, 0xcf,
-  0x6e, 0x55, 0xc9, 0x45, 0xca, 0x58, 0x6b, 0xcf, 0x48, 0xc8, 0x4e, 0xe5,
-  0xdd, 0x4e, 0xca, 0x4c, 0xd8, 0x7b, 0x5a, 0xcf, 0x4e, 0xd3, 0x60, 0x73,
-  0xdd, 0x56, 0xd6, 0x5b, 0xe8, 0xfc, 0x67, 0xdf, 0x5e, 0xdf, 0x65, 0xed,
-  0x7b, 0x6e, 0xe5, 0x5e, 0xdc, 0x5d, 0xe6, 0xff, 0x63, 0xd8, 0x53, 0xd5,
-  0x5e, 0x7c, 0xdb, 0x4f, 0xcd, 0x50, 0xde, 0xea, 0x52, 0xcb, 0x4a, 0xcf,
-  0x68, 0x5c, 0xcc, 0x47, 0xc9, 0x54, 0x79, 0xd1, 0x48, 0xc6, 0x4b, 0xdd,
-  0xdf, 0x4c, 0xc6, 0x47, 0xd0, 0x6f, 0x56, 0xca, 0x47, 0xcb, 0x5a, 0x6b,
-  0xd1, 0x4a, 0xca, 0x50, 0xe7, 0xdf, 0x50, 0xcd, 0x4f, 0xda, 0x79, 0x5e,
-  0xd5, 0x52, 0xd7, 0x62, 0xff, 0xe4, 0x5c, 0xdb, 0x5d, 0xe5, 0x73, 0x77,
-  0xea, 0x64, 0xe0, 0x5f, 0xe2, 0x6b, 0xfe, 0xe8, 0x5c, 0xd8, 0x58, 0xde,
-  0x76, 0x63, 0xd5, 0x50, 0xd1, 0x5b, 0xff, 0xda, 0x4e, 0xcb, 0x4f, 0xdd,
-  0xe9, 0x50, 0xca, 0x49, 0xcf, 0x69, 0x5b, 0xcb, 0x47, 0xc9, 0x55, 0x75,
-  0xd1, 0x48, 0xc7, 0x4c, 0xde, 0xde, 0x4c, 0xc7, 0x49, 0xd2, 0x76, 0x57,
-  0xcb, 0x49, 0xcd, 0x5c, 0x6b, 0xd3, 0x4c, 0xcd, 0x54, 0xe9, 0xe3, 0x54,
-  0xcf, 0x52, 0xdc, 0x75, 0x64, 0xda, 0x58, 0xdb, 0x63, 0xf4, 0xee, 0x65,
-  0xe0, 0x5f, 0xe3, 0x68, 0xf1, 0xf9, 0x6a, 0xe0, 0x5d, 0xdc, 0x60, 0xef,
-  0xeb, 0x5b, 0xd5, 0x54, 0xda, 0x6d, 0x62, 0xd3, 0x4e, 0xcf, 0x59, 0xfd,
-  0xd9, 0x4d, 0xca, 0x4e, 0xdd, 0xe8, 0x4f, 0xc9, 0x49, 0xcf, 0x69, 0x5a,
-  0xcb, 0x47, 0xca, 0x56, 0x73, 0xd1, 0x49, 0xc7, 0x4d, 0xdf, 0xdf, 0x4d,
-  0xc8, 0x4a, 0xd4, 0x77, 0x58, 0xcd, 0x4b, 0xcf, 0x5d, 0x6d, 0xd6, 0x4e,
-  0xcf, 0x56, 0xea, 0xe9, 0x59, 0xd5, 0x56, 0xde, 0x6f, 0x6d, 0xdf, 0x5d,
-  0xdd, 0x62, 0xeb, 0x7c, 0x71, 0xe8, 0x62, 0xdf, 0x60, 0xe5, 0x73, 0x6f,
-  0xdf, 0x5a, 0xd7, 0x5c, 0xe9, 0xec, 0x5a, 0xd1, 0x50, 0xd7, 0x6c, 0x61,
-  0xd1, 0x4c, 0xcd, 0x58, 0xfd, 0xd7, 0x4c, 0xc9, 0x4d, 0xdd, 0xe7, 0x4f,
-  0xc9, 0x49, 0xcf, 0x6b, 0x59, 0xcb, 0x47, 0xcb, 0x57, 0x6f, 0xd1, 0x49,
-  0xc9, 0x4e, 0xe2, 0xdf, 0x4e, 0xca, 0x4c, 0xd6, 0x78, 0x5a, 0xcf, 0x4d,
-  0xd1, 0x5f, 0x70, 0xda, 0x52, 0xd3, 0x59, 0xe9, 0xef, 0x5e, 0xda, 0x5a,
-  0xdf, 0x6b, 0x7b, 0xeb, 0x63, 0xe1, 0x61, 0xe5, 0x6b, 0xfa, 0xf0, 0x64,
-  0xdd, 0x5b, 0xde, 0x68, 0x75, 0xdf, 0x56, 0xd4, 0x58, 0xe4, 0xed, 0x58,
-  0xcf, 0x4e, 0xd5, 0x6a, 0x60, 0xcf, 0x4b, 0xcc, 0x57, 0xfd, 0xd6, 0x4b,
-  0xc9, 0x4d, 0xdd, 0xe5, 0x4f, 0xc9, 0x49, 0xd0, 0x6d, 0x5a, 0xcc, 0x48,
-  0xcc, 0x59, 0x6f, 0xd3, 0x4b, 0xca, 0x4f, 0xe5, 0xe1, 0x50, 0xcc, 0x4e,
-  0xd9, 0x77, 0x5d, 0xd2, 0x4f, 0xd5, 0x60, 0x77, 0xde, 0x57, 0xd7, 0x5c,
-  0xe8, 0xfa, 0x67, 0xdf, 0x5e, 0xe0, 0x67, 0xf0, 0xfc, 0x6d, 0xe5, 0x5f,
-  0xdf, 0x61, 0xeb, 0xfb, 0x65, 0xdb, 0x57, 0xd9, 0x61, 0x7c, 0xde, 0x54,
-  0xd0, 0x54, 0xe1, 0xed, 0x56, 0xcd, 0x4d, 0xd3, 0x69, 0x5f, 0xce, 0x4b,
-  0xcc, 0x57, 0x7d, 0xd5, 0x4b, 0xc9, 0x4e, 0xde, 0xe4, 0x4f, 0xc9, 0x4a,
-  0xd2, 0x6f, 0x5a, 0xcc, 0x4a, 0xcd, 0x5b, 0x6f, 0xd4, 0x4c, 0xcc, 0x52,
-  0xe7, 0xe4, 0x53, 0xce, 0x50, 0xdb, 0x76, 0x60, 0xd6, 0x54, 0xd8, 0x62,
-  0xff, 0xe5, 0x5d, 0xdc, 0x5e, 0xe7, 0x75, 0x73, 0xe9, 0x64, 0xe2, 0x63,
-  0xe7, 0x6f, 0x7b, 0xe9, 0x5f, 0xdb, 0x5c, 0xe2, 0x78, 0x65, 0xd9, 0x54,
-  0xd6, 0x5e, 0xfe, 0xdd, 0x51, 0xce, 0x52, 0xdf, 0xec, 0x54, 0xcd, 0x4c,
-  0xd2, 0x69, 0x5d, 0xce, 0x4a, 0xcc, 0x57, 0x7a, 0xd5, 0x4b, 0xc9, 0x4e,
-  0xdf, 0xe3, 0x4f, 0xca, 0x4b, 0xd4, 0x70, 0x5a, 0xcd, 0x4b, 0xce, 0x5c,
-  0x6f, 0xd6, 0x4e, 0xce, 0x55, 0xe8, 0xe7, 0x57, 0xd2, 0x54, 0xdd, 0x73,
-  0x66, 0xdb, 0x59, 0xdb, 0x63, 0xf5, 0xee, 0x65, 0xe0, 0x60, 0xe5, 0x6b,
-  0xf7, 0xf6, 0x69, 0xe2, 0x5f, 0xdf, 0x65, 0xf5, 0xeb, 0x5d, 0xd8, 0x58,
-  0xdd, 0x71, 0x65, 0xd7, 0x51, 0xd2, 0x5c, 0xfd, 0xdb, 0x4f, 0xcd, 0x50,
-  0xde, 0xea, 0x53, 0xcc, 0x4c, 0xd2, 0x6a, 0x5d, 0xce, 0x4a, 0xcc, 0x58,
-  0x78, 0xd4, 0x4b, 0xca, 0x4f, 0xe1, 0xe3, 0x4f, 0xcb, 0x4c, 0xd6, 0x74,
-  0x5b, 0xcf, 0x4d, 0xd0, 0x5e, 0x70, 0xd9, 0x50, 0xd0, 0x58, 0xea, 0xeb,
-  0x5b, 0xd6, 0x58, 0xdf, 0x6f, 0x6d, 0xe1, 0x5d, 0xde, 0x64, 0xed, 0xff,
-  0x6e, 0xe8, 0x63, 0xe2, 0x64, 0xe9, 0x77, 0x6e, 0xe2, 0x5c, 0xdb, 0x5e,
-  0xec, 0xed, 0x5c, 0xd5, 0x54, 0xda, 0x6d, 0x64, 0xd5, 0x4f, 0xd0, 0x5a,
-  0xfd, 0xda, 0x4e, 0xcc, 0x4f, 0xde, 0xe9, 0x52, 0xcb, 0x4b, 0xd3, 0x6c,
-  0x5c, 0xce, 0x4a, 0xcd, 0x5a, 0x74, 0xd5, 0x4c, 0xcb, 0x50, 0xe4, 0xe3,
-  0x51, 0xcc, 0x4e, 0xd8, 0x77, 0x5c, 0xd1, 0x4f, 0xd4, 0x60, 0x72, 0xdc,
-  0x55, 0xd5, 0x5b, 0xeb, 0xef, 0x5f, 0xdb, 0x5c, 0xe1, 0x6d, 0x7a, 0xeb,
-  0x65, 0xe3, 0x64, 0xe8, 0x6e, 0xfc, 0xef, 0x66, 0xdf, 0x5e, 0xe0, 0x6b,
-  0x76, 0xe0, 0x59, 0xd7, 0x5a, 0xe8, 0xee, 0x5a, 0xd2, 0x51, 0xd8, 0x6c,
-  0x62, 0xd3, 0x4e, 0xcf, 0x5a, 0xff, 0xd9, 0x4e, 0xcc, 0x4f, 0xdf, 0xe7,
-  0x51, 0xcb, 0x4c, 0xd4, 0x6e, 0x5c, 0xce, 0x4b, 0xce, 0x5b, 0x71, 0xd5,
-  0x4d, 0xcd, 0x53, 0xe7, 0xe3, 0x53, 0xce, 0x50, 0xdb, 0x78, 0x5e, 0xd5,
-  0x52, 0xd7, 0x63, 0x78, 0xdf, 0x5a, 0xd9, 0x5e, 0xea, 0xf9, 0x69, 0xe1,
-  0x60, 0xe3, 0x6a, 0xf2, 0xfa, 0x6e, 0xe7, 0x63, 0xe1, 0x65, 0xed, 0xfa,
-  0x67, 0xdd, 0x5a, 0xdc, 0x65, 0x7b, 0xdf, 0x57, 0xd4, 0x57, 0xe4, 0xee,
-  0x59, 0xd0, 0x4f, 0xd6, 0x6b, 0x60, 0xd2, 0x4d, 0xce, 0x59, 0x7d, 0xd8,
-  0x4d, 0xcc, 0x4f, 0xe0, 0xe7, 0x51, 0xcc, 0x4c, 0xd5, 0x6f, 0x5b, 0xce,
-  0x4c, 0xcf, 0x5c, 0x70, 0xd7, 0x4e, 0xce, 0x55, 0xe9, 0xe5, 0x56, 0xd1,
-  0x53, 0xdd, 0x78, 0x62, 0xd9, 0x57, 0xda, 0x66, 0x7e, 0xe6, 0x5e, 0xde,
-  0x60, 0xe9, 0x78, 0x73, 0xea, 0x66, 0xe4, 0x66, 0xea, 0x71, 0x7a, 0xea,
-  0x61, 0xde, 0x5e, 0xe5, 0x7a, 0x67, 0xdb, 0x57, 0xd8, 0x60, 0x7e, 0xde,
-  0x54, 0xd1, 0x55, 0xe2, 0xed, 0x57, 0xcf, 0x4e, 0xd6, 0x6b, 0x5f, 0xd0,
-  0x4c, 0xce, 0x5a, 0x7a, 0xd8, 0x4d, 0xcc, 0x50, 0xe3, 0xe5, 0x51, 0xcc,
-  0x4d, 0xd7, 0x74, 0x5c, 0xcf, 0x4d, 0xd1, 0x5e, 0x6f, 0xd9, 0x50, 0xd0,
-  0x58, 0xea, 0xe7, 0x59, 0xd4, 0x57, 0xde, 0x77, 0x68, 0xdd, 0x5b, 0xdd,
-  0x66, 0xf7, 0xee, 0x67, 0xe3, 0x64, 0xe7, 0x6d, 0xf9, 0xf5, 0x6b, 0xe5,
-  0x62, 0xe2, 0x68, 0xf6, 0xed, 0x5f, 0xdb, 0x5a, 0xdf, 0x72, 0x67, 0xd9,
-  0x54, 0xd5, 0x5e, 0xfd, 0xdd, 0x52, 0xcf, 0x53, 0xe1, 0xed, 0x56, 0xce,
-  0x4e, 0xd5, 0x6b, 0x5e, 0xd0, 0x4c, 0xce, 0x5a, 0x77, 0xd7, 0x4d, 0xcc,
-  0x52, 0xe4, 0xe5, 0x53, 0xcd, 0x4e, 0xd8, 0x76, 0x5d, 0xd1, 0x4f, 0xd3,
-  0x5f, 0x71, 0xdb, 0x53, 0xd4, 0x5a, 0xec, 0xec, 0x5d, 0xd9, 0x5a, 0xe1,
-  0x72, 0x6e, 0xe3, 0x5f, 0xe0, 0x66, 0xef, 0xfe, 0x6f, 0xe9, 0x66, 0xe5,
-  0x67, 0xec, 0x79, 0x70, 0xe5, 0x5e, 0xdd, 0x60, 0xee, 0xee, 0x5e, 0xd8,
-  0x57, 0xdc, 0x6f, 0x67, 0xd8, 0x52, 0xd3, 0x5d, 0xfd, 0xdc, 0x51, 0xce,
-  0x53, 0xe0, 0xeb, 0x55, 0xce, 0x4e, 0xd6, 0x6d, 0x5e, 0xd0, 0x4c, 0xcf,
-  0x5b, 0x75, 0xd8, 0x4e, 0xcd, 0x53, 0xe6, 0xe5, 0x54, 0xce, 0x50, 0xda,
-  0x78, 0x5e, 0xd4, 0x51, 0xd6, 0x63, 0x74, 0xdd, 0x57, 0xd7, 0x5d, 0xec,
-  0xf0, 0x61, 0xdd, 0x5e, 0xe4, 0x6f, 0x7a, 0xec, 0x67, 0xe5, 0x67, 0xea,
-  0x70, 0xfe, 0xf0, 0x68, 0xe2, 0x61, 0xe3, 0x6d, 0x77, 0xe4, 0x5c, 0xda,
-  0x5d, 0xea, 0xef, 0x5d, 0xd6, 0x54, 0xdb, 0x6c, 0x65, 0xd6, 0x50, 0xd2,
-  0x5c, 0xfe, 0xdc, 0x50, 0xce, 0x52, 0xe1, 0xea, 0x55, 0xcd, 0x4e, 0xd6,
-  0x6e, 0x5e, 0xd0, 0x4d, 0xcf, 0x5d, 0x74, 0xd8, 0x4f, 0xce, 0x55, 0xe8,
-  0xe6, 0x56, 0xd0, 0x53, 0xdc, 0x78, 0x60, 0xd7, 0x55, 0xd9, 0x65, 0x78,
-  0xe2, 0x5b, 0xdb, 0x5f, 0xec, 0xfa, 0x69, 0xe3, 0x62, 0xe6, 0x6b, 0xf6,
-  0xfa, 0x6e, 0xe9, 0x66, 0xe5, 0x68, 0xee, 0xfb, 0x69, 0xdf, 0x5d, 0xde,
-  0x68, 0x7c, 0xe3, 0x5a, 0xd7, 0x5a, 0xe6, 0xef, 0x5c, 0xd3, 0x52, 0xd9,
-  0x6c, 0x64, 0xd5, 0x4f, 0xd1, 0x5b, 0xff, 0xdb, 0x4f, 0xce, 0x53, 0xe2,
-  0xe9, 0x55, 0xce, 0x4e, 0xd7, 0x70, 0x5e, 0xd1, 0x4e, 0xd1, 0x5e, 0x73,
-  0xd9, 0x50, 0xd0, 0x58, 0xea, 0xe7, 0x58, 0xd3, 0x56, 0xde, 0x78, 0x64,
-  0xdb, 0x59, 0xdc, 0x67, 0x7d, 0xe8, 0x5f, 0xdf, 0x62, 0xeb, 0x79, 0x72,
-  0xeb, 0x67, 0xe7, 0x68, 0xec, 0x74, 0x79, 0xec, 0x64, 0xe0, 0x60, 0xe8,
-  0x7a, 0x6a, 0xde, 0x5a, 0xdb, 0x63, 0xfe, 0xe1, 0x58, 0xd4, 0x58, 0xe4,
-  0xef, 0x5a, 0xd2, 0x51, 0xd8, 0x6c, 0x63, 0xd4, 0x4f, 0xd0, 0x5c, 0x7d,
-  0xda, 0x4f, 0xce, 0x53, 0xe3, 0xe8, 0x55, 0xce, 0x4f, 0xd9, 0x73, 0x5e,
-  0xd2, 0x4f, 0xd4, 0x5f, 0x72, 0xdb, 0x53, 0xd3, 0x5a, 0xeb, 0xea, 0x5b,
-  0xd7, 0x59, 0xe0, 0x75, 0x6a, 0xde, 0x5d, 0xde, 0x68, 0xf9, 0xef, 0x67,
-  0xe5, 0x65, 0xe9, 0x6f, 0xfb, 0xf5, 0x6c, 0xe7, 0x64, 0xe5, 0x6a, 0xf8,
-  0xee, 0x62, 0xdd, 0x5d, 0xe2, 0x73, 0x6a, 0xdc, 0x57, 0xd8, 0x5f, 0xfb,
-  0xe0, 0x56, 0xd2, 0x57, 0xe2, 0xee, 0x59, 0xd1, 0x50, 0xd8, 0x6d, 0x62,
-  0xd3, 0x4e, 0xd0, 0x5c, 0x7a, 0xda, 0x4f, 0xce, 0x54, 0xe5, 0xe8, 0x56,
-  0xcf, 0x50, 0xda, 0x75, 0x5f, 0xd4, 0x51, 0xd6, 0x62, 0x74, 0xdd, 0x56,
-  0xd6, 0x5c, 0xec, 0xed, 0x5e, 0xdb, 0x5c, 0xe3, 0x73, 0x6e, 0xe5, 0x60,
-  0xe3, 0x68, 0xf1, 0xfd, 0x6f, 0xeb, 0x67, 0xe7, 0x69, 0xee, 0x7a, 0x71,
-  0xe7, 0x61, 0xdf, 0x64, 0xef, 0xef, 0x60, 0xda, 0x5a, 0xde, 0x70, 0x69,
-  0xda, 0x55, 0xd6, 0x5e, 0xfb, 0xde, 0x55, 0xd1, 0x56, 0xe2, 0xed, 0x59,
-  0xd0, 0x50, 0xd8, 0x6d, 0x60, 0xd3, 0x4f, 0xd1, 0x5d, 0x79, 0xda, 0x50,
-  0xcf, 0x56, 0xe7, 0xe8, 0x57, 0xd1, 0x53, 0xdc, 0x77, 0x60, 0xd7, 0x54,
-  0xd8, 0x64, 0x76, 0xdf, 0x59, 0xd9, 0x5e, 0xed, 0xf2, 0x64, 0xde, 0x5f,
-  0xe5, 0x6f, 0x79, 0xec, 0x68, 0xe7, 0x68, 0xec, 0x73, 0x7e, 0xf1, 0x6a,
-  0xe5, 0x63, 0xe7, 0x6f, 0x77, 0xe7, 0x5e, 0xdc, 0x5e, 0xeb, 0xf2, 0x5f,
-  0xd9, 0x57, 0xdc, 0x6d, 0x68, 0xd9, 0x53, 0xd5, 0x5d, 0xfc, 0xde, 0x53,
-  0xd0, 0x55, 0xe3, 0xed, 0x58, 0xd0, 0x50, 0xd8, 0x6e, 0x60, 0xd3, 0x4f,
-  0xd2, 0x5e, 0x77, 0xdb, 0x52, 0xd1, 0x57, 0xe8, 0xe9, 0x59, 0xd3, 0x55,
-  0xdd, 0x78, 0x64, 0xd9, 0x57, 0xdb, 0x66, 0x7b, 0xe4, 0x5d, 0xdc, 0x61,
-  0xec, 0xfa, 0x6b, 0xe4, 0x64, 0xe7, 0x6d, 0xf7, 0xf8, 0x6f, 0xea, 0x68,
-  0xe8, 0x6a, 0xf2, 0xfa, 0x6b, 0xe3, 0x5f, 0xe1, 0x69, 0x7c, 0xe6, 0x5c,
-  0xda, 0x5c, 0xe9, 0xf3, 0x5e, 0xd7, 0x55, 0xdb, 0x6c, 0x67, 0xd8, 0x52,
-  0xd4, 0x5d, 0xfd, 0xdd, 0x53, 0xd0, 0x55, 0xe3, 0xec, 0x58, 0xd0, 0x50,
-  0xd9, 0x6f, 0x61, 0xd4, 0x50, 0xd4, 0x5f, 0x77, 0xdc, 0x54, 0xd3, 0x59,
-  0xea, 0xea, 0x5b, 0xd6, 0x58, 0xdf, 0x77, 0x67, 0xdc, 0x5a, 0xdd, 0x68,
-  0xfe, 0xea, 0x62, 0xdf, 0x64, 0xeb, 0x7b, 0x73, 0xeb, 0x68, 0xe8, 0x6a,
-  0xee, 0x77, 0x79, 0xed, 0x67, 0xe3, 0x64, 0xeb, 0x7c, 0x6b, 0xe0, 0x5c,
-  0xdd, 0x65, 0xfe, 0xe5, 0x5a, 0xd8, 0x5a, 0xe6, 0xf3, 0x5d, 0xd5, 0x54,
-  0xda, 0x6d, 0x67, 0xd7, 0x51, 0xd3, 0x5d, 0xfe, 0xdd, 0x53, 0xd0, 0x56,
-  0xe5, 0xeb, 0x58, 0xd1, 0x52, 0xda, 0x71, 0x61, 0xd6, 0x52, 0xd6, 0x61,
-  0x77, 0xdd, 0x56, 0xd5, 0x5b, 0xeb, 0xec, 0x5d, 0xd9, 0x5a, 0xe2, 0x75,
-  0x6b, 0xe0, 0x5e, 0xe0, 0x68, 0xf9, 0xf0, 0x68, 0xe5, 0x66, 0xeb, 0x70,
-  0xfe, 0xf4, 0x6c, 0xe9, 0x67, 0xe8, 0x6d, 0xfa, 0xef, 0x65, 0xdf, 0x5f,
-  0xe6, 0x75, 0x6b, 0xde, 0x5a, 0xdb, 0x62, 0xfc, 0xe3, 0x59, 0xd6, 0x59,
-  0xe5, 0xf2, 0x5c, 0xd4, 0x53, 0xda, 0x6d, 0x65, 0xd7, 0x51, 0xd4, 0x5e,
-  0x7e, 0xdd, 0x53, 0xd1, 0x57, 0xe6, 0xeb, 0x59, 0xd2, 0x53, 0xdc, 0x75,
-  0x62, 0xd7, 0x54, 0xd8, 0x64, 0x78, 0xdf, 0x59, 0xd8, 0x5d, 0xec, 0xee,
-  0x60, 0xdc, 0x5d, 0xe4, 0x74, 0x70, 0xe6, 0x63, 0xe3, 0x6a, 0xf2, 0xfc,
-  0x70, 0xeb, 0x69, 0xe9, 0x6b, 0xf0, 0x7d, 0x72, 0xe9, 0x64, 0xe3, 0x67,
-  0xf2, 0xf2, 0x63, 0xdd, 0x5c, 0xe1, 0x70, 0x6b, 0xdd, 0x58, 0xd9, 0x5f,
-  0xfb, 0xe2, 0x58, 0xd4, 0x58, 0xe5, 0xf1, 0x5b, 0xd4, 0x53, 0xda, 0x6d,
-  0x64, 0xd6, 0x51, 0xd4, 0x5e, 0x7b, 0xdd, 0x54, 0xd2, 0x58, 0xe8, 0xeb,
-  0x5a, 0xd4, 0x55, 0xdd, 0x76, 0x64, 0xd9, 0x57, 0xda, 0x65, 0x79, 0xe2,
-  0x5b, 0xdb, 0x5f, 0xed, 0xf3, 0x66, 0xdf, 0x61, 0xe7, 0x71, 0x7a, 0xed,
-  0x69, 0xe8, 0x6a, 0xed, 0x76, 0x7e, 0xf1, 0x6b, 0xe7, 0x66, 0xea, 0x71,
-  0x77, 0xe9, 0x60, 0xde, 0x61, 0xed, 0xf5, 0x61, 0xdb, 0x5a, 0xde, 0x6e,
-  0x6b, 0xdc, 0x57, 0xd8, 0x5f, 0xfb, 0xe1, 0x57, 0xd4, 0x57, 0xe5, 0xef,
-  0x5b, 0xd3, 0x53, 0xda, 0x6e, 0x64, 0xd7, 0x52, 0xd5, 0x5f, 0x7b, 0xdd,
-  0x55, 0xd3, 0x59, 0xe9, 0xeb, 0x5b, 0xd6, 0x58, 0xde, 0x77, 0x67, 0xdb,
-  0x59, 0xdc, 0x68, 0x7d, 0xe6, 0x5f, 0xde, 0x63, 0xed, 0xfb, 0x6b, 0xe6,
-  0x65, 0xe8, 0x6e, 0xfa, 0xf8, 0x6e, 0xeb, 0x69, 0xea, 0x6c, 0xf5, 0xfa,
-  0x6c, 0xe5, 0x61, 0xe4, 0x6c, 0x7c, 0xe8, 0x5e, 0xdc, 0x5e, 0xea, 0xf4,
-  0x60, 0xd9, 0x58, 0xdd, 0x6e, 0x6a, 0xdb, 0x55, 0xd7, 0x5e, 0xfc, 0xdf,
-  0x56, 0xd3, 0x58, 0xe5, 0xee, 0x5b, 0xd3, 0x54, 0xdb, 0x6f, 0x64, 0xd7,
-  0x53, 0xd7, 0x60, 0x79, 0xde, 0x56, 0xd5, 0x5b, 0xeb, 0xed, 0x5d, 0xd8,
-  0x5a, 0xe1, 0x76, 0x69, 0xde, 0x5c, 0xde, 0x69, 0xfd, 0xeb, 0x64, 0xe2,
-  0x66, 0xed, 0x7c, 0x74, 0xec, 0x6a, 0xe9, 0x6c, 0xef, 0x7a, 0x78, 0xee,
-  0x68, 0xe6, 0x67, 0xed, 0x7c, 0x6d, 0xe3, 0x5e, 0xdf, 0x68, 0xfe, 0xe7,
-  0x5d, 0xda, 0x5d, 0xe8, 0xf4, 0x5f, 0xd8, 0x57, 0xdc, 0x6e, 0x69, 0xda,
-  0x55, 0xd6, 0x5e, 0xfd, 0xdf, 0x56, 0xd3, 0x58, 0xe6, 0xed, 0x5a, 0xd4,
-  0x54, 0xdc, 0x72, 0x64, 0xd8, 0x55, 0xd8, 0x63, 0x78, 0xdf, 0x58, 0xd8,
-  0x5d, 0xed, 0xee, 0x5f, 0xdb, 0x5c, 0xe4, 0x76, 0x6d, 0xe2, 0x5f, 0xe2,
-  0x6a, 0xfa, 0xf1, 0x6a, 0xe7, 0x68, 0xec, 0x72, 0x7e, 0xf5, 0x6e, 0xea,
-  0x69, 0xea, 0x6e, 0xfc, 0xf0, 0x68, 0xe2, 0x62, 0xe7, 0x77, 0x6d, 0xe0,
-  0x5d, 0xdd, 0x65, 0xfb, 0xe6, 0x5b, 0xd9, 0x5b, 0xe7, 0xf4, 0x5e, 0xd7,
-  0x56, 0xdc, 0x6e, 0x68, 0xd9, 0x54, 0xd6, 0x5f, 0xfe, 0xdf, 0x56, 0xd4,
-  0x59, 0xe7, 0xed, 0x5b, 0xd5, 0x56, 0xdd, 0x73, 0x65, 0xda, 0x57, 0xda,
-  0x65, 0x79, 0xe2, 0x5b, 0xda, 0x5f, 0xed, 0xf1, 0x63, 0xde, 0x5f, 0xe6,
-  0x75, 0x72, 0xe8, 0x64, 0xe6, 0x6b, 0xf4, 0xfb, 0x70, 0xec, 0x6a, 0xeb,
-  0x6d, 0xf3, 0x7e, 0x72, 0xeb, 0x66, 0xe5, 0x69, 0xf5, 0xf3, 0x66, 0xdf,
-  0x5f, 0xe4, 0x73, 0x6d, 0xdf, 0x5b, 0xdc, 0x63, 0xfb, 0xe5, 0x5a, 0xd7,
-  0x5a, 0xe6, 0xf3, 0x5d, 0xd7, 0x56, 0xdc, 0x6e, 0x67, 0xd9, 0x55, 0xd7,
-  0x5f, 0x7e, 0xdf, 0x57, 0xd5, 0x5a, 0xe9, 0xed, 0x5c, 0xd6, 0x58, 0xde,
-  0x76, 0x67, 0xdb, 0x59, 0xdc, 0x66, 0x7b, 0xe5, 0x5d, 0xdc, 0x61, 0xee,
-  0xf6, 0x67, 0xe2, 0x62, 0xe8, 0x71, 0x7a, 0xee, 0x69, 0xe9, 0x6b, 0xef,
-  0x78, 0x7c, 0xf0, 0x6c, 0xe9, 0x69, 0xec, 0x76, 0x77, 0xea, 0x63, 0xe1,
-  0x65, 0xee, 0xf4, 0x65, 0xdd, 0x5d, 0xe1, 0x70, 0x6d, 0xde, 0x59, 0xda,
-  0x61, 0xfb, 0xe4, 0x59, 0xd7, 0x5a, 0xe6, 0xf2, 0x5d, 0xd6, 0x56, 0xdc,
-  0x6e, 0x67, 0xd9, 0x55, 0xd8, 0x61, 0x7c, 0xdf, 0x58, 0xd6, 0x5b, 0xea,
-  0xee, 0x5d, 0xd8, 0x59, 0xe0, 0x76, 0x69, 0xdd, 0x5b, 0xde, 0x68, 0x7d,
-  0xe9, 0x5f, 0xdf, 0x63, 0xee, 0xfc, 0x6c, 0xe7, 0x66, 0xe9, 0x6f, 0xfb,
-  0xf7, 0x6e, 0xec, 0x6a, 0xec, 0x6e, 0xf8, 0xf9, 0x6d, 0xe7, 0x65, 0xe7,
-  0x6e, 0x7c, 0xea, 0x61, 0xde, 0x61, 0xec, 0xf6, 0x64, 0xdc, 0x5b, 0xdf,
-  0x6f, 0x6c, 0xdd, 0x58, 0xd9, 0x61, 0xfb, 0xe3, 0x59, 0xd6, 0x5a, 0xe7,
-  0xf1, 0x5d, 0xd6, 0x56, 0xdd, 0x6f, 0x67, 0xda, 0x56, 0xd9, 0x62, 0x7c,
-  0xe0, 0x59, 0xd8, 0x5d, 0xeb, 0xee, 0x5f, 0xda, 0x5b, 0xe2, 0x76, 0x6b,
-  0xe0, 0x5d, 0xe0, 0x6a, 0xfe, 0xed, 0x65, 0xe4, 0x67, 0xee, 0x7b, 0x73,
-  0xed, 0x6a, 0xeb, 0x6d, 0xf2, 0x7b, 0x77, 0xee, 0x6a, 0xe9, 0x6a, 0xef,
-  0x7e, 0x6e, 0xe6, 0x61, 0xe2, 0x6a, 0xfe, 0xe9, 0x5f, 0xdc, 0x5f, 0xea,
-  0xf7, 0x62, 0xdb, 0x5a, 0xde, 0x6e, 0x6c, 0xdc, 0x58, 0xd9, 0x60, 0xfc,
-  0xe2, 0x59, 0xd6, 0x5a, 0xe7, 0xef, 0x5d, 0xd7, 0x57, 0xde, 0x72, 0x67,
-  0xdb, 0x57, 0xda, 0x64, 0x7b, 0xe2, 0x5b, 0xda, 0x5e, 0xed, 0xf0, 0x61,
-  0xdd, 0x5e, 0xe5, 0x77, 0x6e, 0xe4, 0x61, 0xe3, 0x6c, 0xf9, 0xf1, 0x6b,
-  0xe8, 0x6a, 0xed, 0x75, 0x7e, 0xf5, 0x6e, 0xec, 0x6b, 0xec, 0x71, 0xfe,
-  0xf1, 0x69, 0xe5, 0x65, 0xea, 0x78, 0x6e, 0xe4, 0x5e, 0xdf, 0x67, 0xfc,
-  0xe9, 0x5e, 0xdb, 0x5d, 0xe9, 0xf6, 0x61, 0xda, 0x59, 0xde, 0x6e, 0x6b,
-  0xdc, 0x57, 0xd9, 0x61, 0xfd, 0xe2, 0x59, 0xd7, 0x5b, 0xe9, 0xef, 0x5d,
-  0xd8, 0x58, 0xdf, 0x73, 0x68, 0xdc, 0x59, 0xdc, 0x66, 0x7b, 0xe4, 0x5c,
-  0xdc, 0x60, 0xee, 0xf4, 0x65, 0xdf, 0x60, 0xe7, 0x75, 0x73, 0xe9, 0x66,
-  0xe7, 0x6c, 0xf5, 0xfa, 0x71, 0xed, 0x6b, 0xec, 0x6e, 0xf6, 0xfe, 0x73,
-  0xec, 0x68, 0xe9, 0x6b, 0xf7, 0xf5, 0x68, 0xe2, 0x61, 0xe7, 0x73, 0x6e,
-  0xe2, 0x5d, 0xde, 0x65, 0xfa, 0xe8, 0x5d, 0xda, 0x5d, 0xe8, 0xf6, 0x5f,
-  0xd9, 0x59, 0xdd, 0x6f, 0x6a, 0xdc, 0x58, 0xd9, 0x62, 0xfe, 0xe2, 0x59,
-  0xd7, 0x5c, 0xe9, 0xef, 0x5e, 0xd9, 0x5a, 0xdf, 0x75, 0x69, 0xdd, 0x5b,
-  0xdd, 0x68, 0x7d, 0xe7, 0x5f, 0xde, 0x63, 0xee, 0xf7, 0x69, 0xe4, 0x64,
-  0xe9, 0x72, 0x7b, 0xee, 0x6b, 0xea, 0x6c, 0xf0, 0x79, 0x7b, 0xf2, 0x6d,
-  0xeb, 0x6b, 0xee, 0x76, 0x78, 0xec, 0x66, 0xe4, 0x67, 0xf0, 0xf7, 0x67,
-  0xdf, 0x5e, 0xe4, 0x70, 0x6e, 0xe0, 0x5c, 0xdc, 0x63, 0xfa, 0xe7, 0x5c,
-  0xd9, 0x5c, 0xe8, 0xf5, 0x5f, 0xd9, 0x58, 0xde, 0x6f, 0x6a, 0xdc, 0x58,
-  0xda, 0x62, 0x7e, 0xe3, 0x5a, 0xd9, 0x5d, 0xeb, 0xf0, 0x5f, 0xdb, 0x5b,
-  0xe2, 0x74, 0x6b, 0xdf, 0x5d, 0xdf, 0x69, 0x7e, 0xea, 0x63, 0xe1, 0x65,
-  0xee, 0xfd, 0x6e, 0xe8, 0x68, 0xea, 0x6f, 0xfc, 0xf8, 0x6f, 0xed, 0x6c,
-  0xed, 0x70, 0xf9, 0xf9, 0x6e, 0xe9, 0x68, 0xe9, 0x6f, 0x7c, 0xec, 0x64,
-  0xe1, 0x64, 0xed, 0xf8, 0x66, 0xde, 0x5d, 0xe1, 0x6f, 0x6e, 0xdf, 0x5b,
-  0xdc, 0x63, 0xfb, 0xe6, 0x5b, 0xd9, 0x5c, 0xe8, 0xf4, 0x5f, 0xd9, 0x59,
-  0xde, 0x70, 0x6a, 0xdc, 0x59, 0xdb, 0x64, 0x7e, 0xe3, 0x5b, 0xda, 0x5e,
-  0xec, 0xf0, 0x61, 0xdc, 0x5d, 0xe4, 0x75, 0x6d, 0xe2, 0x5f, 0xe1, 0x6b,
-  0xfb, 0xee, 0x67, 0xe5, 0x68, 0xee, 0x7c, 0x75, 0xed, 0x6b, 0xec, 0x6e,
-  0xf4, 0x7d, 0x77, 0xef, 0x6c, 0xea, 0x6b, 0xf1, 0xff, 0x6f, 0xe8, 0x64,
-  0xe6, 0x6c, 0xff, 0xeb, 0x62, 0xdf, 0x61, 0xec, 0xf8, 0x65, 0xdd, 0x5c,
-  0xe0, 0x6f, 0x6d, 0xdf, 0x5a, 0xdb, 0x63, 0xfa, 0xe6, 0x5b, 0xd9, 0x5c,
-  0xe8, 0xf3, 0x5f, 0xda, 0x59, 0xdf, 0x71, 0x6a, 0xdd, 0x59, 0xdc, 0x65,
-  0x7d, 0xe5, 0x5d, 0xdc, 0x5f, 0xed, 0xf3, 0x64, 0xde, 0x5f, 0xe6, 0x75,
-  0x70, 0xe6, 0x63, 0xe5, 0x6c, 0xf8, 0xf3, 0x6c, 0xe9, 0x6a, 0xed, 0x76,
-  0x7e, 0xf5, 0x6e, 0xec, 0x6c, 0xee, 0x74, 0xfe, 0xf2, 0x6b, 0xe8, 0x68,
-  0xec, 0x7a, 0x6f, 0xe6, 0x61, 0xe2, 0x6a, 0xfc, 0xea, 0x60, 0xdd, 0x5f,
-  0xea, 0xf8, 0x64, 0xdc, 0x5b, 0xdf, 0x6e, 0x6d, 0xde, 0x5a, 0xdb, 0x63,
-  0xfb, 0xe5, 0x5b, 0xd9, 0x5d, 0xea, 0xf3, 0x5f, 0xda, 0x5a, 0xe0, 0x72,
-  0x6b, 0xde, 0x5b, 0xdd, 0x67, 0x7e, 0xe7, 0x5e, 0xdd, 0x62, 0xee, 0xf6,
-  0x68, 0xe1, 0x62, 0xe8, 0x74, 0x75, 0xeb, 0x68, 0xe8, 0x6c, 0xf6, 0xfb,
-  0x72, 0xed, 0x6d, 0xed, 0x70, 0xf7, 0xfe, 0x74, 0xed, 0x6a, 0xea, 0x6d,
-  0xf7, 0xf6, 0x6a, 0xe5, 0x64, 0xe9, 0x75, 0x70, 0xe5, 0x5f, 0xdf, 0x67,
-  0xfa, 0xea, 0x5f, 0xdc, 0x5e, 0xe9, 0xf8, 0x63, 0xdc, 0x5b, 0xdf, 0x6f,
-  0x6c, 0xde, 0x5a, 0xdb, 0x63, 0xfc, 0xe6, 0x5c, 0xda, 0x5e, 0xea, 0xf3,
-  0x61, 0xdb, 0x5c, 0xe2, 0x74, 0x6c, 0xdf, 0x5d, 0xdf, 0x69, 0x7e, 0xe9,
-  0x61, 0xdf, 0x64, 0xef, 0xfa, 0x6b, 0xe6, 0x65, 0xea, 0x72, 0x7b, 0xef,
-  0x6c, 0xeb, 0x6d, 0xf1, 0x7a, 0x7a, 0xf3, 0x6e, 0xec, 0x6c, 0xef, 0x78,
-  0x78, 0xed, 0x68, 0xe7, 0x6a, 0xf2, 0xf7, 0x6a, 0xe2, 0x61, 0xe6, 0x72,
-  0x70, 0xe3, 0x5e, 0xde, 0x66, 0xfa, 0xe9, 0x5e, 0xdc, 0x5e, 0xe9, 0xf8,
-  0x62, 0xdb, 0x5b, 0xdf, 0x6f, 0x6c, 0xde, 0x5a, 0xdc, 0x64, 0xfd, 0xe6,
-  0x5c, 0xdb, 0x5e, 0xeb, 0xf4, 0x62, 0xdd, 0x5d, 0xe3, 0x74, 0x6d, 0xe2,
-  0x5e, 0xe1, 0x69, 0xfd, 0xed, 0x65, 0xe3, 0x67, 0xee, 0xfe, 0x6f, 0xea,
-  0x69, 0xeb, 0x70, 0xfb, 0xf7, 0x70, 0xed, 0x6d, 0xee, 0x72, 0xfa, 0xf9,
-  0x6f, 0xeb, 0x6a, 0xeb, 0x71, 0x7c, 0xed, 0x67, 0xe3, 0x66, 0xee, 0xf9,
-  0x69, 0xe0, 0x5f, 0xe4, 0x71, 0x71, 0xe2, 0x5d, 0xdd, 0x65, 0xf8, 0xe9,
-  0x5e, 0xdb, 0x5e, 0xe8, 0xf7, 0x63, 0xdb, 0x5b, 0xdf, 0x70, 0x6d, 0xde,
-  0x5b, 0xdc, 0x65, 0xfc, 0xe7, 0x5d, 0xdc, 0x5f, 0xec, 0xf5, 0x64, 0xde,
-  0x5e, 0xe5, 0x73, 0x6f, 0xe5, 0x60, 0xe3, 0x6a, 0xfc, 0xf0, 0x68, 0xe7,
-  0x69, 0xef, 0x7b, 0x75, 0xee, 0x6c, 0xec, 0x6f, 0xf5, 0x7d, 0x77, 0xef,
-  0x6d, 0xec, 0x6d, 0xf3, 0xff, 0x70, 0xea, 0x66, 0xe8, 0x6e, 0xfe, 0xed,
-  0x65, 0xe1, 0x64, 0xed, 0xfa, 0x68, 0xdf, 0x5e, 0xe2, 0x6f, 0x6f, 0xe1,
-  0x5d, 0xdd, 0x65, 0xf9, 0xe9, 0x5e, 0xdb, 0x5e, 0xe9, 0xf6, 0x63, 0xdc,
-  0x5b, 0xe0, 0x71, 0x6d, 0xdf, 0x5c, 0xdd, 0x66, 0xfe, 0xe9, 0x5f, 0xdd,
-  0x61, 0xed, 0xf7, 0x67, 0xe0, 0x60, 0xe7, 0x74, 0x73, 0xe8, 0x64, 0xe6,
-  0x6c, 0xf8, 0xf5, 0x6c, 0xeb, 0x6b, 0xee, 0x76, 0x7d, 0xf4, 0x6f, 0xed,
-  0x6d, 0xef, 0x75, 0x7e, 0xf4, 0x6c, 0xe9, 0x6a, 0xee, 0x7a, 0x71, 0xe9,
-  0x64, 0xe5, 0x6b, 0xfc, 0xed, 0x63, 0xdf, 0x62, 0xeb, 0xfa, 0x67, 0xde,
-  0x5d, 0xe1, 0x6f, 0x6f, 0xe0, 0x5c, 0xdd, 0x64, 0xf9, 0xe8, 0x5e, 0xdb,
-  0x5e, 0xea, 0xf6, 0x64, 0xdc, 0x5c, 0xe1, 0x72, 0x6d, 0xe0, 0x5d, 0xde,
-  0x68, 0xfd, 0xea, 0x61, 0xdf, 0x63, 0xed, 0xf9, 0x6a, 0xe4, 0x63, 0xe9,
-  0x74, 0x77, 0xec, 0x69, 0xe9, 0x6d, 0xf6, 0xfd, 0x73, 0xee, 0x6d, 0xee,
-  0x72, 0xf9, 0xfd, 0x74, 0xee, 0x6c, 0xec, 0x6f, 0xf9, 0xf6, 0x6c, 0xe8,
-  0x66, 0xeb, 0x76, 0x72, 0xe8, 0x62, 0xe2, 0x69, 0xfa, 0xec, 0x62, 0xde,
-  0x60, 0xea, 0xfb, 0x66, 0xde, 0x5d, 0xe1, 0x6f, 0x6f, 0xe0, 0x5c, 0xdd,
-  0x65, 0xfa, 0xe9, 0x5e, 0xdc, 0x5f, 0xeb, 0xf7, 0x64, 0xdd, 0x5d, 0xe3,
-  0x72, 0x6e, 0xe2, 0x5e, 0xe0, 0x69, 0xfc, 0xec, 0x64, 0xe1, 0x65, 0xef,
-  0xfc, 0x6c, 0xe7, 0x66, 0xeb, 0x72, 0x7c, 0xf1, 0x6c, 0xec, 0x6d, 0xf2,
-  0x7b, 0x7a, 0xf2, 0x6e, 0xed, 0x6e, 0xf1, 0x7a, 0x79, 0xee, 0x6a, 0xe9,
-  0x6c, 0xf4, 0xf9, 0x6b, 0xe5, 0x64, 0xe8, 0x73, 0x73, 0xe6, 0x60, 0xe0,
-  0x68, 0xf9, 0xec, 0x61, 0xde, 0x5f, 0xea, 0xfa, 0x66, 0xde, 0x5d, 0xe1,
-  0x6f, 0x6f, 0xe1, 0x5c, 0xdd, 0x66, 0xfb, 0xe9, 0x5e, 0xdd, 0x5f, 0xec,
-  0xf7, 0x65, 0xde, 0x5e, 0xe5, 0x74, 0x6f, 0xe5, 0x60, 0xe2, 0x6a, 0xfc,
-  0xee, 0x67, 0xe5, 0x68, 0xef, 0xfe, 0x71, 0xeb, 0x6a, 0xec, 0x72, 0xfc,
-  0xf8, 0x71, 0xee, 0x6e, 0xef, 0x74, 0xfb, 0xf9, 0x70, 0xed, 0x6c, 0xed,
-  0x73, 0x7d, 0xef, 0x69, 0xe6, 0x68, 0xef, 0xfa, 0x6b, 0xe3, 0x62, 0xe6,
-  0x72, 0x73, 0xe5, 0x5f, 0xdf, 0x67, 0xf8, 0xeb, 0x60, 0xdd, 0x5f, 0xea,
-  0xfb, 0x65, 0xde, 0x5d, 0xe2, 0x6f, 0x6e, 0xe1, 0x5d, 0xde, 0x66, 0xfd,
-  0xea, 0x5f, 0xde, 0x61, 0xed, 0xf8, 0x67, 0xe0, 0x60, 0xe7, 0x74, 0x71,
-  0xe8, 0x63, 0xe5, 0x6c, 0xfa, 0xf1, 0x6b, 0xe8, 0x6a, 0xef, 0x7c, 0x77,
-  0xef, 0x6d, 0xed, 0x71, 0xf5, 0x7e, 0x79, 0xf0, 0x6e, 0xed, 0x6f, 0xf4,
-  0xfe, 0x73, 0xeb, 0x69, 0xe9, 0x6f, 0xfd, 0xee, 0x67, 0xe4, 0x66, 0xed,
-  0xfb, 0x6a, 0xe2, 0x60, 0xe5, 0x70, 0x72, 0xe5, 0x5e, 0xdf, 0x66, 0xf8,
-  0xeb, 0x5f, 0xdd, 0x5f, 0xea, 0xfa, 0x65, 0xde, 0x5d, 0xe3, 0x6f, 0x6e,
-  0xe2, 0x5d, 0xdf, 0x67, 0xfc, 0xeb, 0x60, 0xdf, 0x63, 0xed, 0xf9, 0x69,
-  0xe3, 0x63, 0xe9, 0x74, 0x75, 0xea, 0x67, 0xe8, 0x6d, 0xf9, 0xf7, 0x6e,
-  0xec, 0x6c, 0xef, 0x78, 0x7e, 0xf5, 0x71, 0xee, 0x6e, 0xf0, 0x77, 0xfe,
-  0xf4, 0x6e, 0xeb, 0x6c, 0xef, 0x7b, 0x74, 0xea, 0x67, 0xe7, 0x6d, 0xfb,
-  0xee, 0x66, 0xe1, 0x64, 0xec, 0xfc, 0x69, 0xe1, 0x5f, 0xe4, 0x6f, 0x72,
-  0xe4, 0x5e, 0xdf, 0x66, 0xf9, 0xeb, 0x5f, 0xdd, 0x5f, 0xeb, 0xf9, 0x66,
-  0xde, 0x5e, 0xe3, 0x71, 0x6f, 0xe4, 0x5f, 0xe0, 0x69, 0xfc, 0xec, 0x63,
-  0xe1, 0x65, 0xef, 0xfb, 0x6b, 0xe6, 0x65, 0xea, 0x74, 0x79, 0xed, 0x6a,
-  0xea, 0x6e, 0xf6, 0xfc, 0x73, 0xef, 0x6e, 0xef, 0x73, 0xf9, 0xfd, 0x75,
-  0xef, 0x6d, 0xed, 0x70, 0xfa, 0xf7, 0x6e, 0xe9, 0x69, 0xec, 0x77, 0x74,
-  0xe9, 0x65, 0xe5, 0x6b, 0xfa, 0xee, 0x64, 0xe0, 0x63, 0xec, 0xfc, 0x69,
-  0xe0, 0x5f, 0xe3, 0x6f, 0x72, 0xe4, 0x5e, 0xdf, 0x66, 0xfa, 0xeb, 0x60,
-  0xde, 0x60, 0xeb, 0xf9, 0x67, 0xdf, 0x5f, 0xe5, 0x72, 0x70, 0xe5, 0x60,
-  0xe2, 0x6a, 0xfc, 0xee, 0x66, 0xe4, 0x67, 0xef, 0xfd, 0x6e, 0xe9, 0x68,
-  0xec, 0x73, 0x7d, 0xf3, 0x6e, 0xed, 0x6e, 0xf3, 0x7b, 0x7b, 0xf5, 0x70,
-  0xee, 0x6f, 0xf3, 0x7a, 0x7a, 0xef, 0x6c, 0xeb, 0x6d, 0xf5, 0xf9, 0x6d,
-  0xe7, 0x67, 0xea, 0x75, 0x75, 0xe8, 0x63, 0xe3, 0x69, 0xf8, 0xed, 0x64,
-  0xdf, 0x62, 0xeb, 0xfc, 0x69, 0xdf, 0x5f, 0xe3, 0x70, 0x71, 0xe3, 0x5e,
-  0xdf, 0x67, 0xfa, 0xeb, 0x61, 0xdf, 0x61, 0xed, 0xfa, 0x68, 0xe1, 0x60,
-  0xe7, 0x73, 0x71, 0xe7, 0x62, 0xe5, 0x6b, 0xfb, 0xf0, 0x69, 0xe7, 0x69,
-  0xef, 0x7e, 0x72, 0xed, 0x6b, 0xed, 0x72, 0xfc, 0xf9, 0x72, 0xef, 0x6e,
-  0xf1, 0x74, 0xfc, 0xf9, 0x71, 0xee, 0x6d, 0xee, 0x75, 0x7d, 0xf0, 0x6b,
-  0xe8, 0x6b, 0xf0, 0xfa, 0x6d, 0xe6, 0x64, 0xe8, 0x72, 0x75, 0xe8, 0x62,
-  0xe2, 0x69, 0xf8, 0xed, 0x63, 0xdf, 0x61, 0xeb, 0xfc, 0x68, 0xdf, 0x5f,
-  0xe4, 0x70, 0x71, 0xe4, 0x5f, 0xe0, 0x68, 0xfb, 0xec, 0x62, 0xdf, 0x63,
-  0xed, 0xfa, 0x69, 0xe3, 0x62, 0xe8, 0x74, 0x74, 0xea, 0x65, 0xe7, 0x6c,
-  0xfa, 0xf4, 0x6c, 0xea, 0x6b, 0xef, 0x7a, 0x78, 0xf0, 0x6e, 0xee, 0x71,
-  0xf6, 0xff, 0x79, 0xf2, 0x6f, 0xee, 0x70, 0xf5, 0xfd, 0x74, 0xed, 0x6b,
-  0xec, 0x70, 0xfe, 0xf0, 0x69, 0xe7, 0x68, 0xef, 0xfd, 0x6c, 0xe5, 0x62,
-  0xe7, 0x70, 0x74, 0xe7, 0x60, 0xe1, 0x67, 0xf9, 0xee, 0x62, 0xdf, 0x61,
-  0xeb, 0xfb, 0x68, 0xe0, 0x5f, 0xe4, 0x70, 0x71, 0xe5, 0x5f, 0xe1, 0x69,
-  0xfb, 0xed, 0x64, 0xe1, 0x65, 0xee, 0xfb, 0x6b, 0xe5, 0x65, 0xe9, 0x74,
-  0x78, 0xec, 0x69, 0xe9, 0x6e, 0xf8, 0xf8, 0x6f, 0xec, 0x6d, 0xef, 0x77,
-  0x7e, 0xf6, 0x72, 0xef, 0x70, 0xf2, 0x78, 0x7e, 0xf6, 0x6f, 0xed, 0x6d,
-  0xf0, 0x7b, 0x75, 0xed, 0x69, 0xe9, 0x6e, 0xfb, 0xf0, 0x68, 0xe5, 0x66,
-  0xee, 0xfd, 0x6b, 0xe4, 0x61, 0xe6, 0x70, 0x74, 0xe7, 0x60, 0xe1, 0x67,
-  0xf8, 0xed, 0x63, 0xdf, 0x62, 0xeb, 0xfb, 0x69, 0xe0, 0x5f, 0xe5, 0x71,
-  0x72, 0xe6, 0x61, 0xe2, 0x6a, 0xfa, 0xee, 0x66, 0xe3, 0x67, 0xee, 0xfc,
-  0x6e, 0xe8, 0x67, 0xeb, 0x74, 0x7c, 0xef, 0x6c, 0xeb, 0x6e, 0xf6, 0xfd,
-  0x75, 0xf0, 0x6f, 0xef, 0x74, 0xfa, 0xfd, 0x75, 0xf0, 0x6e, 0xef, 0x73,
-  0xfb, 0xf8, 0x6e, 0xeb, 0x6a, 0xee, 0x78, 0x76, 0xeb, 0x67, 0xe7, 0x6c,
-  0xfa, 0xf0, 0x67, 0xe3, 0x65, 0xed, 0xfe, 0x6b, 0xe3, 0x61, 0xe5, 0x6f,
-  0x74, 0xe7, 0x60, 0xe1, 0x67, 0xf9, 0xee, 0x63, 0xdf, 0x62, 0xec, 0xfc,
-  0x69, 0xe2, 0x60, 0xe6, 0x72, 0x73, 0xe8, 0x63, 0xe4, 0x6b, 0xfa, 0xef,
-  0x68, 0xe5, 0x69, 0xef, 0xfe, 0x70, 0xea, 0x6a, 0xec, 0x74, 0x7e, 0xf3,
-  0x6f, 0xed, 0x6f, 0xf3, 0x7c, 0x7b, 0xf4, 0x71, 0xef, 0x71, 0xf4, 0x7c,
-  0x7a, 0xf0, 0x6e, 0xec, 0x6f, 0xf6, 0xfa, 0x6f, 0xea, 0x69, 0xeb, 0x76,
-  0x77, 0xeb, 0x66, 0xe5, 0x6b, 0xf8, 0xef, 0x67, 0xe2, 0x64, 0xed, 0xfe,
-  0x6b, 0xe3, 0x60, 0xe5, 0x6f, 0x74, 0xe7, 0x60, 0xe1, 0x68, 0xf9, 0xee,
-  0x63, 0xe1, 0x63, 0xed, 0xfd, 0x6a, 0xe4, 0x62, 0xe8, 0x72, 0x75, 0xe9,
-  0x65, 0xe6, 0x6c, 0xfa, 0xf2, 0x6b, 0xe8, 0x6a, 0xef, 0x7d, 0x74, 0xed,
-  0x6d, 0xed, 0x73, 0xfb, 0xf9, 0x74, 0xef, 0x70, 0xf1, 0x77, 0xfd, 0xfa,
-  0x73, 0xef, 0x6e, 0xef, 0x76, 0x7d, 0xf2, 0x6c, 0xeb, 0x6c, 0xf2, 0xfc,
-  0x6e, 0xe9, 0x67, 0xea, 0x72, 0x77, 0xea, 0x65, 0xe4, 0x6a, 0xf8, 0xef,
-  0x66, 0xe2, 0x63, 0xec, 0xff, 0x6b, 0xe3, 0x60, 0xe5, 0x70, 0x75, 0xe7,
-  0x61, 0xe2, 0x69, 0xf9, 0xee, 0x65, 0xe2, 0x64, 0xed, 0xfd, 0x6c, 0xe5,
-  0x64, 0xe9, 0x73, 0x77, 0xeb, 0x67, 0xe8, 0x6d, 0xf9, 0xf6, 0x6d, 0xeb,
-  0x6c, 0xef, 0x7a, 0x7a, 0xf1, 0x6f, 0xee, 0x72, 0xf7, 0xff, 0x78, 0xf3,
-  0x70, 0xef, 0x72, 0xf7, 0xfe, 0x75, 0xee, 0x6c, 0xed, 0x72, 0xfd, 0xf2,
-  0x6b, 0xe8, 0x6a, 0xef, 0xfd, 0x6e, 0xe7, 0x65, 0xe9, 0x71, 0x78, 0xea,
-  0x63, 0xe4, 0x69, 0xf7, 0xef, 0x66, 0xe1, 0x64, 0xec, 0xff, 0x6b, 0xe3,
-  0x61, 0xe6, 0x70, 0x75, 0xe8, 0x62, 0xe3, 0x6a, 0xf9, 0xef, 0x67, 0xe3,
-  0x66, 0xee, 0xfe, 0x6d, 0xe7, 0x66, 0xea, 0x74, 0x7a, 0xee, 0x6a, 0xea,
-  0x6e, 0xf7, 0xfa, 0x70, 0xee, 0x6e, 0xf1, 0x77, 0x7e, 0xf7, 0x72, 0xf0,
-  0x71, 0xf4, 0x79, 0x7d, 0xf6, 0x71, 0xee, 0x6f, 0xf2, 0x7c, 0x77, 0xee,
-  0x6b, 0xeb, 0x6f, 0xfb, 0xf2, 0x6b, 0xe7, 0x68, 0xee, 0xff, 0x6e, 0xe6,
-  0x64, 0xe7, 0x70, 0x78, 0xea, 0x63, 0xe3, 0x69, 0xf7, 0xef, 0x66, 0xe2,
-  0x64, 0xec, 0xfe, 0x6b, 0xe4, 0x62, 0xe6, 0x71, 0x76, 0xe9, 0x63, 0xe4,
-  0x6a, 0xf9, 0xf1, 0x68, 0xe5, 0x67, 0xef, 0x7e, 0x6f, 0xea, 0x68, 0xec,
-  0x74, 0x7c, 0xf1, 0x6c, 0xec, 0x6f, 0xf6, 0xfe, 0x76, 0xf1, 0x6f, 0xf0,
-  0x75, 0xfb, 0xfd, 0x76, 0xf1, 0x6f, 0xef, 0x74, 0xfb, 0xf9, 0x70, 0xec,
-  0x6d, 0xee, 0x79, 0x78, 0xed, 0x6a, 0xe9, 0x6d, 0xf9, 0xf2, 0x6a, 0xe6,
-  0x67, 0xed, 0x7e, 0x6d, 0xe6, 0x63, 0xe7, 0x70, 0x78, 0xe9, 0x63, 0xe3,
-  0x69, 0xf7, 0xf0, 0x66, 0xe2, 0x64, 0xec, 0xfe, 0x6c, 0xe5, 0x63, 0xe7,
-  0x71, 0x77, 0xea, 0x65, 0xe6, 0x6b, 0xf8, 0xf3, 0x6a, 0xe8, 0x69, 0xef,
-  0x7e, 0x72, 0xec, 0x6b, 0xed, 0x74, 0xfe, 0xf5, 0x6f, 0xee, 0x6f, 0xf4,
-  0x7c, 0x7b, 0xf5, 0x71, 0xf0, 0x72, 0xf6, 0x7b, 0x79, 0xf3, 0x6e, 0xee,
-  0x6f, 0xf7, 0xfb, 0x6f, 0xec, 0x6b, 0xed, 0x76, 0x79, 0xed, 0x68, 0xe7,
-  0x6c, 0xf7, 0xf2, 0x69, 0xe5, 0x66, 0xec, 0x7e, 0x6e, 0xe5, 0x63, 0xe7,
-  0x6f, 0x78, 0xe9, 0x63, 0xe4, 0x69, 0xf7, 0xf0, 0x67, 0xe3, 0x65, 0xed,
-  0xff, 0x6d, 0xe6, 0x65, 0xe9, 0x73, 0x78, 0xeb, 0x67, 0xe7, 0x6c, 0xf8,
-  0xf5, 0x6d, 0xea, 0x6b, 0xf0, 0x7c, 0x76, 0xef, 0x6d, 0xee, 0x72, 0xfc,
-  0xfb, 0x74, 0xf2, 0x70, 0xf3, 0x76, 0xfe, 0xfb, 0x75, 0xf0, 0x6f, 0xf1,
-  0x77, 0x7d, 0xf4, 0x6e, 0xec, 0x6d, 0xf3, 0xfe, 0x70, 0xeb, 0x69, 0xeb,
-  0x73, 0x7a, 0xed, 0x67, 0xe7, 0x6b, 0xf7, 0xf4, 0x69, 0xe5, 0x66, 0xec,
-  0x7d, 0x6d, 0xe5, 0x63, 0xe6, 0x6f, 0x79, 0xea, 0x64, 0xe3, 0x6a, 0xf6,
-  0xf0, 0x68, 0xe4, 0x66, 0xed, 0xff, 0x6e, 0xe7, 0x66, 0xea, 0x73, 0x7a,
-  0xed, 0x69, 0xe9, 0x6d, 0xf8, 0xf8, 0x6f, 0xec, 0x6d, 0xf1, 0x7a, 0x7a,
-  0xf3, 0x6f, 0xf0, 0x72, 0xf8, 0x7e, 0x78, 0xf5, 0x70, 0xf1, 0x72, 0xf9,
-  0xff, 0x75, 0xf0, 0x6d, 0xef, 0x72, 0xfd, 0xf4, 0x6d, 0xeb, 0x6b, 0xf1,
-  0xff, 0x70, 0xea, 0x67, 0xea, 0x72, 0x7a, 0xec, 0x66, 0xe5, 0x6a, 0xf6,
-  0xf3, 0x68, 0xe4, 0x66, 0xec, 0x7d, 0x6e, 0xe6, 0x64, 0xe7, 0x70, 0x79,
-  0xea, 0x65, 0xe4, 0x6b, 0xf6, 0xf2, 0x69, 0xe5, 0x68, 0xed, 0xff, 0x6f,
-  0xe9, 0x68, 0xeb, 0x73, 0x7c, 0xef, 0x6c, 0xeb, 0x6e, 0xf6, 0xfc, 0x72,
-  0xee, 0x6e, 0xf1, 0x77, 0xfe, 0xf8, 0x73, 0xf2, 0x71, 0xf5, 0x79, 0x7d,
-  0xf7, 0x72, 0xef, 0x6f, 0xf4, 0x7b, 0x77, 0xf0, 0x6c, 0xec, 0x6f, 0xfb,
-  0xf5, 0x6c, 0xe9, 0x6a, 0xef, 0x7d, 0x6f, 0xe9, 0x66, 0xe9, 0x70, 0x7a,
-  0xec, 0x66, 0xe5, 0x6a, 0xf6, 0xf3, 0x69, 0xe4, 0x65, 0xec, 0x7d, 0x6e,
-  0xe7, 0x64, 0xe8, 0x70, 0x79, 0xeb, 0x66, 0xe6, 0x6b, 0xf7, 0xf4, 0x6b,
-  0xe7, 0x69, 0xee, 0x7d, 0x72, 0xeb, 0x6a, 0xec, 0x73, 0xff, 0xf2, 0x6e,
-  0xed, 0x6f, 0xf5, 0xfe, 0x78, 0xf2, 0x70, 0xf1, 0x75, 0xfb, 0xfd, 0x77,
-  0xf3, 0x70, 0xf1, 0x75, 0xfb, 0xfa, 0x73, 0xee, 0x6e, 0xf0, 0x78, 0x79,
-  0xef, 0x6b, 0xeb, 0x6e, 0xf8, 0xf5, 0x6c, 0xe8, 0x69, 0xee, 0x7c, 0x6f,
-  0xe9, 0x66, 0xe8, 0x6f, 0x7a, 0xec, 0x66, 0xe5, 0x6a, 0xf6, 0xf4, 0x69,
-  0xe5, 0x66, 0xed, 0x7c, 0x6e, 0xe7, 0x65, 0xe9, 0x71, 0x7a, 0xec, 0x68,
-  0xe7, 0x6c, 0xf7, 0xf6, 0x6c, 0xe9, 0x6b, 0xef, 0x7c, 0x75, 0xed, 0x6c,
-  0xed, 0x74, 0xfd, 0xf7, 0x71, 0xef, 0x70, 0xf4, 0x7c, 0x7c, 0xf6, 0x74,
-  0xf1, 0x73, 0xf6, 0x7d, 0x7b, 0xf3, 0x70, 0xef, 0x71, 0xf7, 0xfd, 0x72,
-  0xee, 0x6c, 0xee, 0x75, 0x7a, 0xef, 0x6a, 0xea, 0x6d, 0xf7, 0xf6, 0x6b,
-  0xe7, 0x68, 0xed, 0x7b, 0x6f, 0xe8, 0x66, 0xe9, 0x6f, 0x7b, 0xec, 0x66,
-  0xe5, 0x6a, 0xf6, 0xf4, 0x6a, 0xe6, 0x67, 0xed, 0x7c, 0x6f, 0xe9, 0x66,
-  0xe9, 0x72, 0x7a, 0xee, 0x69, 0xe9, 0x6d, 0xf8, 0xf8, 0x6e, 0xeb, 0x6c,
-  0xf0, 0x7b, 0x78, 0xf0, 0x6e, 0xef, 0x74, 0xfa, 0xfb, 0x76, 0xf1, 0x71,
-  0xf3, 0x78, 0xfd, 0xfa, 0x76, 0xf1, 0x71, 0xf3, 0x78, 0x7e, 0xf5, 0x6f,
-  0xed, 0x6f, 0xf4, 0xfe, 0x72, 0xec, 0x6b, 0xec, 0x74, 0x7c, 0xee, 0x6a,
-  0xe8, 0x6c, 0xf6, 0xf5, 0x6b, 0xe7, 0x67, 0xed, 0x7c, 0x6f, 0xe9, 0x65,
-  0xe9, 0x6f, 0x7a, 0xed, 0x66, 0xe6, 0x6a, 0xf6, 0xf5, 0x6a, 0xe7, 0x68,
-  0xee, 0x7c, 0x70, 0xea, 0x68, 0xeb, 0x72, 0x7c, 0xef, 0x6b, 0xeb, 0x6e,
-  0xf7, 0xfa, 0x70, 0xee, 0x6d, 0xf1, 0x79, 0x7b, 0xf5, 0x70, 0xf0, 0x74,
-  0xf7, 0x7e, 0x7a, 0xf4, 0x73, 0xf1, 0x75, 0xf9, 0xfe, 0x77, 0xf1, 0x6f,
-  0xef, 0x74, 0xfc, 0xf6, 0x6f, 0xec, 0x6d, 0xf1, 0x7e, 0x73, 0xeb, 0x6a,
-  0xeb, 0x73, 0x7d, 0xee, 0x69, 0xe7, 0x6b, 0xf5, 0xf6, 0x6b, 0xe7, 0x67,
-  0xed, 0x7b, 0x6f, 0xe9, 0x66, 0xe9, 0x6f, 0x7b, 0xed, 0x67, 0xe7, 0x6b,
-  0xf7, 0xf6, 0x6b, 0xe8, 0x68, 0xee, 0x7c, 0x72, 0xeb, 0x69, 0xec, 0x73,
-  0x7d, 0xf2, 0x6d, 0xec, 0x6f, 0xf7, 0xfc, 0x75, 0xef, 0x6f, 0xf1, 0x78,
-  0xfe, 0xf9, 0x75, 0xf2, 0x73, 0xf5, 0x7a, 0x7e, 0xf8, 0x74, 0xf1, 0x71,
-  0xf4, 0x7c, 0x79, 0xf1, 0x6e, 0xee, 0x71, 0xfb, 0xf7, 0x6e, 0xeb, 0x6b,
-  0xf0, 0x7c, 0x72, 0xeb, 0x69, 0xea, 0x71, 0x7d, 0xee, 0x68, 0xe7, 0x6b,
-  0xf5, 0xf6, 0x6b, 0xe7, 0x68, 0xed, 0x7c, 0x70, 0xe9, 0x67, 0xe9, 0x70,
-  0x7b, 0xed, 0x68, 0xe8, 0x6c, 0xf7, 0xf6, 0x6c, 0xe9, 0x6a, 0xef, 0x7b,
-  0x74, 0xed, 0x6b, 0xed, 0x73, 0xff, 0xf5, 0x6f, 0xee, 0x6f, 0xf6, 0x7e,
-  0x79, 0xf3, 0x71, 0xf2, 0x76, 0xfb, 0xfd, 0x78, 0xf5, 0x72, 0xf3, 0x76,
-  0xfc, 0xfa, 0x74, 0xef, 0x6f, 0xf2, 0x79, 0x7a, 0xf1, 0x6d, 0xec, 0x6f,
-  0xf9, 0xf7, 0x6d, 0xea, 0x6b, 0xee, 0x7c, 0x72, 0xeb, 0x68, 0xea, 0x70,
-  0x7d, 0xee, 0x69, 0xe7, 0x6b, 0xf4, 0xf6, 0x6c, 0xe7, 0x68, 0xed, 0x7b,
-  0x71, 0xea, 0x67, 0xea, 0x71, 0x7c, 0xee, 0x69, 0xe9, 0x6c, 0xf6, 0xf8,
-  0x6e, 0xeb, 0x6b, 0xf0, 0x7a, 0x76, 0xef, 0x6d, 0xee, 0x73, 0xfd, 0xf9,
-  0x72, 0xf0, 0x70, 0xf5, 0x7b, 0x7c, 0xf7, 0x73, 0xf3, 0x74, 0xf8, 0x7c,
-  0x7b, 0xf5, 0x71, 0xf1, 0x72, 0xf9, 0xfd, 0x74, 0xee, 0x6d, 0xef, 0x77,
-  0x7c, 0xf1, 0x6c, 0xeb, 0x6e, 0xf7, 0xf7, 0x6d, 0xe9, 0x6a, 0xee, 0x7c,
-  0x72, 0xea, 0x68, 0xea, 0x70, 0x7d, 0xee, 0x69, 0xe7, 0x6b, 0xf5, 0xf6,
-  0x6c, 0xe7, 0x69, 0xed, 0x7c, 0x72, 0xea, 0x68, 0xeb, 0x71, 0x7d, 0xef,
-  0x6b, 0xea, 0x6d, 0xf7, 0xfa, 0x6f, 0xec, 0x6d, 0xf1, 0x7a, 0x7a, 0xf2,
-  0x6f, 0xef, 0x74, 0xfa, 0xfc, 0x76, 0xf3, 0x72, 0xf4, 0x78, 0xfe, 0xfb,
-  0x75, 0xf3, 0x72, 0xf4, 0x79, 0x7e, 0xf7, 0x70, 0xef, 0x70, 0xf5, 0xfe,
-  0x75, 0xee, 0x6c, 0xee, 0x75, 0x7d, 0xf0, 0x6c, 0xea, 0x6d, 0xf7, 0xf8,
-  0x6d, 0xe9, 0x69, 0xee, 0x7b, 0x73, 0xea, 0x68, 0xea, 0x70, 0x7d, 0xee,
-  0x69, 0xe8, 0x6b, 0xf6, 0xf7, 0x6c, 0xe8, 0x69, 0xee, 0x7b, 0x73, 0xec,
-  0x6a, 0xec, 0x71, 0x7d, 0xf2, 0x6c, 0xec, 0x6e, 0xf7, 0xfc, 0x73, 0xee,
-  0x6e, 0xf1, 0x79, 0x7c, 0xf6, 0x72, 0xf1, 0x74, 0xf9, 0x7e, 0x7a, 0xf6,
-  0x73, 0xf3, 0x75, 0xfa, 0xfe, 0x78, 0xf3, 0x70, 0xf1, 0x76, 0xfd, 0xf7,
-  0x70, 0xed, 0x6e, 0xf3, 0x7e, 0x75, 0xed, 0x6c, 0xed, 0x73, 0x7e, 0xf0,
-  0x6b, 0xea, 0x6c, 0xf6, 0xf8, 0x6d, 0xe9, 0x69, 0xee, 0x7b, 0x72, 0xeb,
-  0x68, 0xea, 0x70, 0x7d, 0xee, 0x69, 0xe9, 0x6c, 0xf5, 0xf8, 0x6d, 0xea,
-  0x6a, 0xef, 0x7b, 0x75, 0xed, 0x6b, 0xed, 0x72, 0xff, 0xf4, 0x6e, 0xed,
-  0x6f, 0xf6, 0xfe, 0x75, 0xf1, 0x70, 0xf3, 0x78, 0xfe, 0xf9, 0x75, 0xf3,
-  0x73, 0xf6, 0x7b, 0x7d, 0xf9, 0x74, 0xf2, 0x72, 0xf7, 0x7c, 0x7a, 0xf3,
-  0x6f, 0xef, 0x73, 0xfb, 0xf8, 0x6f, 0xed, 0x6d, 0xf1, 0x7c, 0x75, 0xed,
-  0x6b, 0xec, 0x73, 0x7d, 0xef, 0x6b, 0xe9, 0x6c, 0xf6, 0xf8, 0x6d, 0xe9,
-  0x69, 0xee, 0x7a, 0x73, 0xeb, 0x69, 0xeb, 0x71, 0x7e, 0xef, 0x6b, 0xe9,
-  0x6d, 0xf6, 0xf8, 0x6e, 0xea, 0x6c, 0xef, 0x7c, 0x77, 0xee, 0x6d, 0xee,
-  0x74, 0xfe, 0xf7, 0x70, 0xef, 0x70, 0xf6, 0x7d, 0x78, 0xf5, 0x71, 0xf4,
-  0x76, 0xfb, 0xfe, 0x78, 0xf6, 0x73, 0xf5, 0x76, 0xfd, 0xfb, 0x75, 0xf2,
-  0x70, 0xf3, 0x7a, 0x7b, 0xf3, 0x6e, 0xed, 0x71, 0xf9, 0xf8, 0x6f, 0xec,
-  0x6c, 0xef, 0x7c, 0x75, 0xec, 0x6a, 0xec, 0x71, 0x7e, 0xf0, 0x6a, 0xe9,
-  0x6c, 0xf6, 0xf9, 0x6d, 0xe9, 0x69, 0xee, 0x7a, 0x73, 0xec, 0x69, 0xeb,
-  0x71, 0x7e, 0xf0, 0x6b, 0xea, 0x6d, 0xf6, 0xfa, 0x6f, 0xec, 0x6d, 0xf0,
-  0x7b, 0x79, 0xf0, 0x6e, 0xef, 0x75, 0xfc, 0xf9, 0x74, 0xf1, 0x72, 0xf6,
-  0x7b, 0x7c, 0xf8, 0x75, 0xf4, 0x74, 0xf8, 0x7d, 0x7c, 0xf7, 0x73, 0xf2,
-  0x74, 0xf9, 0xfd, 0x76, 0xf0, 0x6f, 0xf0, 0x77, 0x7d, 0xf3, 0x6d, 0xed,
-  0x6f, 0xf8, 0xfa, 0x6f, 0xeb, 0x6b, 0xef, 0x7b, 0x75, 0xec, 0x6a, 0xeb,
-  0x71, 0x7e, 0xf0, 0x6a, 0xe9, 0x6c, 0xf5, 0xf9, 0x6d, 0xea, 0x6a, 0xee,
-  0x7a, 0x74, 0xed, 0x6a, 0xec, 0x71, 0x7e, 0xf2, 0x6c, 0xec, 0x6e, 0xf5,
-  0xfc, 0x72, 0xed, 0x6e, 0xf1, 0x79, 0x7b, 0xf3, 0x70, 0xef, 0x74, 0xfa,
-  0xfc, 0x78, 0xf3, 0x73, 0xf4, 0x79, 0xfc, 0xfa, 0x77, 0xf3, 0x74, 0xf4,
-  0x7a, 0xfe, 0xf7, 0x73, 0xef, 0x71, 0xf7, 0x7e, 0x76, 0xef, 0x6e, 0xef,
-  0x75, 0x7d, 0xf3, 0x6d, 0xec, 0x6e, 0xf7, 0xfa, 0x6e, 0xeb, 0x6b, 0xef,
-  0x79, 0x74, 0xed, 0x69, 0xeb, 0x6f, 0x7e, 0xf1, 0x6a, 0xea, 0x6c, 0xf6,
-  0xfa, 0x6e, 0xeb, 0x6a, 0xef, 0x7a, 0x76, 0xee, 0x6b, 0xed, 0x72, 0xfe,
-  0xf4, 0x6e, 0xed, 0x6f, 0xf6, 0xfd, 0x75, 0xef, 0x6f, 0xf1, 0x79, 0x7e,
-  0xf6, 0x75, 0xf1, 0x76, 0xf8, 0xfd, 0x7a, 0xf5, 0x74, 0xf2, 0x76, 0xf8,
-  0x7e, 0x7a, 0xf6, 0x71, 0xec, 0x76, 0xfa, 0x7e, 0x7b, 0xf9, 0x72, 0xf5,
-  0x7d, 0xfb, 0xfb, 0x76, 0xf9, 0x75, 0x7b, 0xf1, 0x77, 0xfe, 0x7a, 0xfb,
-  0x7e, 0x73, 0xfa, 0x7e, 0xfb, 0x7d, 0x77, 0xf7, 0x7e, 0x7c, 0xfe, 0x78,
-  0xfa, 0x7e, 0xff, 0x7e, 0x76, 0xf9, 0xfd, 0xf9, 0xfe, 0x75, 0xf1, 0x76,
-  0x7c, 0xfc, 0x7a, 0xf3, 0x70, 0xfd, 0x7e, 0x7d, 0xf3, 0x70, 0xf7, 0x78,
-  0x7e, 0xf9, 0x72, 0xef, 0x77, 0xfc, 0xfd, 0x75, 0xee, 0x72, 0xf7, 0xfb
-#endif
-};
-
-static short MuLawDecompressTable[256] =
-{
-     -32124,-31100,-30076,-29052,-28028,-27004,-25980,-24956,
-     -23932,-22908,-21884,-20860,-19836,-18812,-17788,-16764,
-     -15996,-15484,-14972,-14460,-13948,-13436,-12924,-12412,
-     -11900,-11388,-10876,-10364, -9852, -9340, -8828, -8316,
-      -7932, -7676, -7420, -7164, -6908, -6652, -6396, -6140,
-      -5884, -5628, -5372, -5116, -4860, -4604, -4348, -4092,
-      -3900, -3772, -3644, -3516, -3388, -3260, -3132, -3004,
-      -2876, -2748, -2620, -2492, -2364, -2236, -2108, -1980,
-      -1884, -1820, -1756, -1692, -1628, -1564, -1500, -1436,
-      -1372, -1308, -1244, -1180, -1116, -1052,  -988,  -924,
-       -876,  -844,  -812,  -780,  -748,  -716,  -684,  -652,
-       -620,  -588,  -556,  -524,  -492,  -460,  -428,  -396,
-       -372,  -356,  -340,  -324,  -308,  -292,  -276,  -260,
-       -244,  -228,  -212,  -196,  -180,  -164,  -148,  -132,
-       -120,  -112,  -104,   -96,   -88,   -80,   -72,   -64,
-        -56,   -48,   -40,   -32,   -24,   -16,    -8,     -1,
-      32124, 31100, 30076, 29052, 28028, 27004, 25980, 24956,
-      23932, 22908, 21884, 20860, 19836, 18812, 17788, 16764,
-      15996, 15484, 14972, 14460, 13948, 13436, 12924, 12412,
-      11900, 11388, 10876, 10364,  9852,  9340,  8828,  8316,
-       7932,  7676,  7420,  7164,  6908,  6652,  6396,  6140,
-       5884,  5628,  5372,  5116,  4860,  4604,  4348,  4092,
-       3900,  3772,  3644,  3516,  3388,  3260,  3132,  3004,
-       2876,  2748,  2620,  2492,  2364,  2236,  2108,  1980,
-       1884,  1820,  1756,  1692,  1628,  1564,  1500,  1436,
-       1372,  1308,  1244,  1180,  1116,  1052,   988,   924,
-        876,   844,   812,   780,   748,   716,   684,   652,
-        620,   588,   556,   524,   492,   460,   428,   396,
-        372,   356,   340,   324,   308,   292,   276,   260,
-        244,   228,   212,   196,   180,   164,   148,   132,
-        120,   112,   104,    96,    88,    80,    72,    64,
-         56,    48,    40,    32,    24,    16,     8,     0
-};
-
-
-void vt_bell (VT *vt)
-{
-  if (vt->bell < 2)
-    return;
-  for (int i = 0; i < (int)sizeof (vt_bell_audio); i++)
-  {
-    int16_t val = MuLawDecompressTable[vt_bell_audio[i]] * vt->bell / 8;
-    terminal_queue_pcm (val, val);
-  }
 }
 
-
-void terminal_queue_pcm (int16_t sample_left, int16_t sample_right);
-
-void vt_audio (VT *vt, const char *command)
+void
+ctx_interpret_pos_bare (CtxState *state, CtxEntry *entry, void *data)
 {
-  AudioState *audio = &vt->audio;
-  // the simplest form of audio is raw audio
-  // _As=8000,c=2,b=8,e=u
-  //
-  // multiple voices:
-  //   ids to queue - store samples as images...
-  //
-  // reusing samples
-  //   .. pitch bend and be able to do a mod player?
-  const char *payload = NULL;
-  char key = 0;
-  int  value;
-  int  pos = 1;
-
-  audio->frames=0;
-  audio->action='t';
-
-  int configure = 0;
-  while (command[pos] != ';')
-  {
-    pos ++; // G or ,
-    if (command[pos] == ';') break;
-    key = command[pos]; pos++;
-    if (command[pos] == ';') break;
-    pos ++; // =
-    if (command[pos] == ';') break;
-
-    if (command[pos] >= '0' && command[pos] <= '9')
-      value = atoi(&command[pos]);
-    else
-      value = command[pos];
-    while (command[pos] &&
-           command[pos] != ',' &&
-           command[pos] != ';') pos++;
-    
-    if (value=='?')
-    {
-      char buf[256];
-      const char *range="";
-      switch (key)
-      {
-        case 's':range="8000,16000,24000,48000";break;
-        case 'b':range="8,16";break;
-        case 'B':range="512-65536";break;
-        case 'c':range="1";break;
-        case 'T':range="u,s,f";break;
-        case 'e':range="b,a";break;
-        case 'o':range="z,0";break;
-        case 'a':range="t,q";break;
-        default:range="unknown";break;
-      }
-      sprintf (buf, "\033_A%c=?;%s\033\\", key, range);
-      vt_write (vt, buf, strlen(buf));
-      return;
-    }
-
-    switch (key)
+  switch (entry->code)
     {
-      case 's': audio->samplerate = value; configure = 1; break;
-      case 'b': audio->bits = value; configure = 1; break;
-      case 'B': audio->buffer_size = value; configure = 1; break;
-      case 'c': audio->channels = value; configure = 1; break;
-      case 'a': audio->action = value; configure = 1; break;
-      case 'T': audio->type = value; configure = 1; break;
-      case 'f': audio->frames = value; configure = 1; break;
-      case 'e': audio->encoding = value; configure = 1; break;
-      case 'o': audio->compression = value; configure = 1; break;
-      case 'm': 
-        audio->mic = value?1:0;
+      case CTX_RESET:
+        ctx_state_init (state);
         break;
-    }
-
-    if (configure)
-    {
-      /* these are the specific sample rates supported by opus,
-       * instead of enabling anything SDL supports, the initial
-       * implementation limits itself to the opus sample rates
-       */
-      if (audio->samplerate <= 8000)
-      {
-        audio->samplerate = 8000;
-      }
-      else if (audio->samplerate <= 16000)
-      {
-        audio->samplerate = 16000;
-      }
-      else if (audio->samplerate <= 24000)
-      {
-        audio->samplerate = 24000;
-      }
-      else
-      {
-        audio->samplerate = 48000;
-      }
-
-      if (audio->bits != 8 && audio->bits != 16)
-        audio->bits = 8;
-
-      if (audio->buffer_size > 2048)
-        audio->buffer_size = 2048;
-      else if (audio->buffer_size < 512)
-        audio->buffer_size = 512;
-
-      switch (audio->type)
-      {
-        case 'u':
-        case 's':
-        case 'f':
-          break;
-        default:
-          audio->type = 's';
-      }
-
-      /* only 1 and 2 channels supported */
-      if (audio->channels <= 0 || audio->channels > 2)
-      {
-        audio->channels = 1;
-      }
-    }
-  }
-  
-  if (audio->frames ||  audio->action != 'd')
-  {
-  payload = &command[pos+1];
-
-  // accumulate incoming data
-  {
-     int chunk_size = strlen (payload);
-     int old_size = audio->data_size;
-     if (audio->data == NULL)
-     {
-       audio->data_size = chunk_size;
-       audio->data = malloc (audio->data_size + 1);
-     }
-     else
-     {
-       audio->data_size += chunk_size;
-       audio->data = realloc (audio->data, audio->data_size + 1);
-     }
-     memcpy (audio->data + old_size, payload, chunk_size);
-     audio->data[audio->data_size]=0;
-  }
-
-    if (audio->frames)
-    switch (audio->encoding)
-    {
-      case 'y':
-        audio->data_size = ydec (audio->data, audio->data, audio->data_size);
-      break;
-      case 'a':
-      {
-        int bin_length = audio->data_size;
-        if (bin_length)
+      case CTX_CLIP:
+      case CTX_BEGIN_PATH:
+      case CTX_FILL:
+      case CTX_STROKE:
+        state->has_moved = 0;
+        break;
+      case CTX_MOVE_TO:
+      case CTX_LINE_TO:
         {
-        uint8_t *data2 = malloc ((unsigned int)ctx_a85len ((char*)audio->data, audio->data_size) + 1);
-        // a85len is inaccurate but gives an upper bound,
-        // should be fixed.
-        bin_length = ctx_a85dec ((char*)audio->data,
-                                 (void*)data2,
-                                 bin_length);
-        free (audio->data);
-        audio->data = data2;
-        audio->data_size = bin_length;
+          float x = ctx_arg_float (0);
+          float y = ctx_arg_float (1);
+          state->x = x;
+          state->y = y;
+          if (!state->has_moved)
+            {
+              state->has_moved = 1;
+            }
         }
-      }
-      break;
-
-      case 'b':
-      {
-        int bin_length = audio->data_size;
-        uint8_t *data2 = malloc (audio->data_size);
-        bin_length = ctx_base642bin ((char*)audio->data,
-                                     &bin_length,
-                                     data2);
-        memcpy (audio->data, data2, bin_length + 1);
-        audio->data_size = bin_length;
-        free (data2);
-      }
-      break;
-    }
-
-    if (audio->frames)
-    switch (audio->compression)
-    {
-      case 'z':
-    {
-      unsigned long actual_uncompressed_size = audio->frames * audio->bits/8 * audio->channels + 512;
-      unsigned char *data2 = malloc (actual_uncompressed_size);
-      /* if a buf size is set (rather compression, but
-       * this works first..) then */
-      int z_result = uncompress (data2, &actual_uncompressed_size,
-                                 audio->data,
-                                 audio->data_size);
-      if (z_result != Z_OK)
-      {
-       // fprintf (stderr, "[z error %i %i]", __LINE__, z_result);
-      }
-
-#if 0
-      // XXX : we seem to get buf-error (-5) here, which indicates not enough
-      //       space in output buffer, which is odd
-      //
-      //       it is non fatal though so we ignore it and use the validly
-      //       decompressed bits.
-      {
-        char buf[256];
-        sprintf (buf, "\e_Ao=z;zlib error1 %i\e\\", z_result);
-        vt_write (vt, buf, strlen(buf));
-        //goto cleanup;
-      }
-#endif
-      free (audio->data);
-      audio->data = data2;
-      audio->data_size = actual_uncompressed_size;
-    }
-
         break;
-      case 'o':
+      case CTX_CURVE_TO:
+        state->x = ctx_arg_float (4);
+        state->y = ctx_arg_float (5);
+        if (!state->has_moved)
+          {
+            state->has_moved = 1;
+          }
+        break;
+      case CTX_QUAD_TO:
+        state->x = ctx_arg_float (2);
+        state->y = ctx_arg_float (3);
+        if (!state->has_moved)
+          {
+            state->has_moved = 1;
+          }
         break;
-      default:
+      case CTX_ARC:
+        state->x = ctx_arg_float (0) + ctx_cosf (ctx_arg_float (4) ) * ctx_arg_float (2);
+        state->y = ctx_arg_float (1) + ctx_sinf (ctx_arg_float (4) ) * ctx_arg_float (2);
         break;
+      case CTX_REL_MOVE_TO:
+      case CTX_REL_LINE_TO:
+        state->x += ctx_arg_float (0);
+        state->y += ctx_arg_float (1);
+        break;
+      case CTX_REL_CURVE_TO:
+        state->x += ctx_arg_float (4);
+        state->y += ctx_arg_float (5);
+        break;
+      case CTX_REL_QUAD_TO:
+        state->x += ctx_arg_float (2);
+        state->y += ctx_arg_float (3);
+        break;
+        // XXX missing some smooths
     }
+}
 
-    if (audio->frames == 0)
-    {
-      /* implicit frame count */
-      audio->frames = audio->data_size /
-                                (audio->bits/8) /
-                                   audio->channels;
-    }
-
-
-#if 0
-    if (audio->format == 100/* opus */)
+void
+ctx_interpret_pos (CtxState *state, CtxEntry *entry, void *data)
+{
+  if ( ( ( (Ctx *) (data) )->transformation & CTX_TRANSFORMATION_SCREEN_SPACE) ||
+       ( ( (Ctx *) (data) )->transformation & CTX_TRANSFORMATION_RELATIVE) )
     {
-      int channels;
-      uint8_t *new_data = NULL;//stbi_load_from_memory (audio->data, audio->data_size, &audio->buf_width, 
&audio->buf_height, &channels, 4);
-
-      if (!new_data)
-      {
-        char buf[256]= "\e_Gf=100;audio decode error\e\\";
-        vt_write (vt, buf, strlen(buf));
-        goto cleanup;
-      }
-      audio->format = 32;
-      free (audio->data);
-      audio->data = new_data;
-      audio->data_size = audio->buf_width * audio->buf_height * 4;
+      ctx_interpret_pos_transform (state, entry, data);
     }
-#endif
-
-  switch (audio->action)
-  {
-    case 't': // transfer
-       if (audio->type == 'u') // implied 8bit
-       {
-         if (audio->channels == 2)
-         {
-           for (int i = 0; i < audio->frames; i++)
-           {
-             int val_left = MuLawDecompressTable[audio->data[i*2]];
-             int val_right = MuLawDecompressTable[audio->data[i*2+1]];
-             terminal_queue_pcm (val_left, val_right);
-           }
-         }
-         else
-         {
-           for (int i = 0; i < audio->frames; i++)
-           {
-             int val = MuLawDecompressTable[audio->data[i]];
-             terminal_queue_pcm (val, val);
-           }
-         }
-       }
-       else if (audio->type == 's')
-       {
-         if (audio->bits == 8)
-         {
-           if (audio->channels == 2)
-           {
-             for (int i = 0; i < audio->frames; i++)
-             {
-               int val_left = 256*((int8_t*)(audio->data))[i*2];
-               int val_right = 256*((int8_t*)(audio->data))[i*2+1];
-               terminal_queue_pcm (val_left, val_right);
-             }
-           }
-           else
-           {
-             for (int i = 0; i < audio->frames; i++)
-             {
-               int val = 256*((int8_t*)(audio->data))[i];
-               terminal_queue_pcm (val, val);
-             }
-           }
-         }
-         else
-         {
-           if (audio->channels == 2)
-           {
-             for (int i = 0; i < audio->frames; i++)
-             {
-               int val_left = ((int16_t*)(audio->data))[i*2];
-               int val_right = ((int16_t*)(audio->data))[i*2+1];
-               terminal_queue_pcm (val_left, val_right);
-             }
-           }
-           else
-           {
-             for (int i = 0; i < audio->frames; i++)
-             {
-               int val = ((int16_t*)(audio->data))[i];
-               terminal_queue_pcm (val, val);
-             }
-           }
-         }
-       }
-       free (audio->data);
-       audio->data = NULL;
-       audio->data_size=0;
-       break;
-    case 'q': // query
-       {
-         char buf[512];
-         sprintf (buf, "\033_As=%i,b=%i,c=%i,T=%c,B=%i,e=%c,o=%c;OK\033\\",
-      audio->samplerate, audio->bits, audio->channels, audio->type,
-      audio->buffer_size,
-      audio->encoding?audio->encoding:'0',
-      audio->compression?audio->compression:'0'
-      /*audio->transmission*/);
-
-         vt_write (vt, buf, strlen(buf));
-       }
-      break;
-  }
-  }
-
-//cleanup:
-    if (audio->data)
-      free (audio->data);
-    audio->data = NULL;
-    audio->data_size=0;
+  ctx_interpret_pos_bare (state, entry, data);
 }
-#endif
-/* DEC terminals/xterm family terminal with ANSI, utf8, vector graphics and
- * audio.
- *
- * Copyright (c) 2014, 2016, 2018, 2020 Øyvind Kolås <pippin gimp org>
- *
- * Adhering to the standards with modern extensions.
- *
- * Features:
- *     vt100 - 101 points on scoresheet
- *     UTF8, cp437
- *     dim, bold, strikethrough, underline, italic, reverse
- *     ANSI colors, 256 colors (non-redefineable), 24bit color
- *     realtime audio transmission
- *     raster sprites (kitty spec)
- *     vector graphics
- *     vt320 - horizontal margins
- *     proportional fonts
- *
- *     BBS/ANSI-art mode
- *
- * 8bit clean
- *
- *
- * Todo:
- *     DECCIR - cursor state report https://vt100.net/docs/vt510-rm/DECCIR.html
- *     make absolute positioning take proportional into account
- *     HTML / PNG / SVG / PDF export of scrollback / screen
- *     sixels
- *
- */
 
-#define _GNU_SOURCE
-#define _BSD_SOURCE
-#ifndef _DEFAULT_SOURCE
-#define _DEFAULT_SOURCE
-#endif
-#ifndef _XOPEN_SOURCE
-#define _XOPEN_SOURCE 600 // for posix_openpt
+#if CTX_BABL
+void ctx_colorspace_babl (CtxState   *state,
+                          CtxColorSpace  icc_slot,
+                          const Babl *space);
 #endif
 
-#if !__COSMOPOLITAN__
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <sys/wait.h>
-#include <errno.h>
-#include <assert.h>
-
-#include <string.h>
-#include <signal.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <stdarg.h>
-#include <ctype.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <sys/stat.h>
-#include <sys/time.h>
-#include <sys/ioctl.h>
-#include <termios.h>
-#include <zlib.h>
+static void
+ctx_state_init (CtxState *state)
+{
+  ctx_memset (state, 0, sizeof (CtxState) );
+  state->gstate.global_alpha_u8 = 255;
+  state->gstate.global_alpha_f  = 1.0;
+  state->gstate.font_size       = 12;
+  state->gstate.line_width      = 2.0;
+  state->gstate.image_smoothing = 1;
+  state->gstate.source_stroke.type = CTX_SOURCE_INHERIT_FILL;
+  ctx_state_set (state, CTX_line_spacing, 1.0f);
+  state->min_x                  = 8192;
+  state->min_y                  = 8192;
+  state->max_x                  = -8192;
+  state->max_y                  = -8192;
+  ctx_matrix_identity (&state->gstate.transform);
+#if CTX_CM
+#if CTX_BABL
+  //ctx_colorspace_babl (state, CTX_COLOR_SPACE_USER_RGB,   babl_space ("sRGB"));
+  //ctx_colorspace_babl (state, CTX_COLOR_SPACE_DEVICE_RGB, babl_space ("ACEScg"));
 #endif
+#endif
+}
 
-#include "ctx.h"
-
-
-#define CTX_VT_USE_FRAMEDIFF 0  // is a larger drain than neccesary when everything is per-byte?
-                                // is anyways currently disabled also in ctx
-
-//#define STB_IMAGE_IMPLEMENTATION
-//#include "stb_image.h"
-
-//#include "vt-line.h"
-//#include "vt.h"
-//#include "ctx-clients.h"
-
-
-
-#define VT_LOG_INFO     (1<<0)
-#define VT_LOG_CURSOR   (1<<1)
-#define VT_LOG_COMMAND  (1<<2)
-#define VT_LOG_WARNING  (1<<3)
-#define VT_LOG_ERROR    (1<<4)
-#define VT_LOG_INPUT    (1<<5)
-#define VT_LOG_ALL       0xff
+void _ctx_set_transformation (Ctx *ctx, int transformation)
+{
+  ctx->transformation = transformation;
+}
 
-static int vt_log_mask = VT_LOG_INPUT;
-//static int vt_log_mask = VT_LOG_WARNING | VT_LOG_ERROR;// | VT_LOG_COMMAND;// | VT_LOG_INFO | 
VT_LOG_COMMAND;
-//static int vt_log_mask = VT_LOG_WARNING | VT_LOG_ERROR | VT_LOG_INFO | VT_LOG_COMMAND | VT_LOG_INPUT;
-//static int vt_log_mask = VT_LOG_ALL;
+static void
+_ctx_init (Ctx *ctx)
+{
+  for (int i = 0; i <256;i++)
+    ctx_u8_float[i] = i/255.0f;
 
-#if 0
-#define vt_log(domain, fmt, ...)
-
-#define VT_input(str, ...)
-#define VT_info(str, ...)
-#define VT_command(str, ...)
-#define VT_cursor(str, ...)
-#define VT_warning(str, ...)
-#define VT_error(str, ...)
-#else
-#define vt_log(domain, line, a...) \
-        do {fprintf (stderr, "%i %s ", line, domain);fprintf(stderr, ##a);fprintf(stderr, "\n");}while(0)
-#define VT_info(a...) if (vt_log_mask & VT_LOG_INFO) vt_log ("INFO  ", __LINE__, ##a)
-#define VT_input(a...) if (vt_log_mask & VT_LOG_INPUT) vt_log ("INPUT ", __LINE__, ##a)
-#define VT_command(a...) if (vt_log_mask & VT_LOG_COMMAND) vt_log ("CMD   ", __LINE__, ##a)
-#define VT_cursor(a...) if (vt_log_mask & VT_LOG_CURSOR) vt_log ("CURSOR",__LINE__, ##a)
-#define VT_warning(a...) if (vt_log_mask & VT_LOG_WARNING) vt_log ("WARN  ",__LINE__, ##a)
-#define VT_error(a...) if (vt_log_mask & VT_LOG_ERROR) vt_log ("ERROR",__LINE__, ##a)
+  ctx_state_init (&ctx->state);
 
+  ctx->renderer = NULL;
+#if CTX_CURRENT_PATH
+  ctx->current_path.flags |= CTX_DRAWLIST_CURRENT_PATH;
 #endif
-
-#ifndef MIN
-#define MIN(a,b)  ((a)<(b)?(a):(b))
+  //ctx->transformation |= (CtxTransformation) CTX_TRANSFORMATION_SCREEN_SPACE;
+  //ctx->transformation |= (CtxTransformation) CTX_TRANSFORMATION_RELATIVE;
+#if CTX_BITPACK
+  ctx->drawlist.flags |= CTX_TRANSFORMATION_BITPACK;
 #endif
+  ctx->texture_cache = ctx;
+}
 
-static void vt_state_neutral      (VT *vt, int byte);
-static void vt_state_esc          (VT *vt, int byte);
-static void vt_state_osc          (VT *vt, int byte);
-static void vt_state_apc          (VT *vt, int byte);
-static void vt_state_apc_generic  (VT *vt, int byte);
-static void vt_state_sixel        (VT *vt, int byte);
-static void vt_state_esc_sequence (VT *vt, int byte);
-static void vt_state_esc_foo      (VT *vt, int byte);
-static void vt_state_swallow      (VT *vt, int byte);
-static void vt_state_ctx          (VT *vt, int byte);
-static void vt_state_vt52         (VT *vt, int byte);
+static void ctx_setup ();
 
-#if 0
-/* barebones linked list */
+#if CTX_DRAWLIST_STATIC
+static Ctx ctx_state;
+#endif
 
-typedef struct _CtxList CtxList;
-struct _CtxList
+void ctx_set_renderer (Ctx  *ctx,
+                       void *renderer)
 {
-  void *data;
-  CtxList *next;
-};
+  if (ctx->renderer && ctx->renderer->free)
+    ctx->renderer->free (ctx->renderer);
+  ctx->renderer = (CtxImplementation*)renderer;
+}
 
-static inline int ctx_list_length (CtxList *list)
+void *ctx_get_renderer (Ctx *ctx)
 {
-  int length = 0;
-  for (CtxList *l = list; l; l = l->next, length++);
-  return length;
+  return ctx->renderer;
 }
 
-static inline void ctx_list_prepend (CtxList **list, void *data)
+Ctx *
+ctx_new (void)
 {
-  CtxList *new_=calloc (sizeof (CtxList), 1);
-  new_->next = *list;
-  new_->data = data;
-  *list = new_;
+  ctx_setup ();
+#if CTX_DRAWLIST_STATIC
+  Ctx *ctx = &ctx_state;
+#else
+  Ctx *ctx = (Ctx *) malloc (sizeof (Ctx) );
+#endif
+  ctx_memset (ctx, 0, sizeof (Ctx) );
+  _ctx_init (ctx);
+  return ctx;
 }
 
-static inline void *ctx_list_last (CtxList *list)
+void
+ctx_drawlist_deinit (CtxDrawlist *drawlist)
 {
-  if (list)
+#if !CTX_DRAWLIST_STATIC
+  if (drawlist->entries && ! (drawlist->flags & CTX_DRAWLIST_DOESNT_OWN_ENTRIES) )
     {
-      CtxList *last;
-      for (last = list; last->next; last=last->next);
-      return last->data;
+      free (drawlist->entries); 
     }
-  return NULL;
+#endif
+  drawlist->entries = NULL;
+  drawlist->size = 0;
 }
 
-static inline void ctx_list_append (CtxList **list, void *data)
+static void ctx_deinit (Ctx *ctx)
 {
-  CtxList *new_= calloc (sizeof (CtxList), 1);
-  new_->data=data;
-  if (*list)
+  if (ctx->renderer)
     {
-      CtxList *last;
-      for (last = *list; last->next; last=last->next);
-      last->next = new_;
-      return;
+      if (ctx->renderer->free)
+        ctx->renderer->free (ctx->renderer);
+      ctx->renderer    = NULL;
     }
-  *list = new_;
-  return;
+  ctx_drawlist_deinit (&ctx->drawlist);
+#if CTX_CURRENT_PATH
+  ctx_drawlist_deinit (&ctx->current_path);
+#endif
 }
 
-static inline void ctx_list_remove (CtxList **list, void *data)
+void ctx_free (Ctx *ctx)
 {
-  CtxList *iter, *prev = NULL;
-  if ( (*list)->data == data)
-    {
-      prev = (void *) (*list)->next;
-      free (*list);
-      *list = prev;
-      return;
-    }
-  for (iter = *list; iter; iter = iter->next)
-    if (iter->data == data)
-      {
-        prev->next = iter->next;
-        free (iter);
-        break;
-      }
-    else
-      { prev = iter; }
+  if (!ctx)
+    { return; }
+#if CTX_EVENTS
+  ctx_clear_bindings (ctx);
+#endif
+  ctx_deinit (ctx);
+#if !CTX_DRAWLIST_STATIC
+  free (ctx);
+#endif
 }
 
-static inline void
-ctx_list_insert_before (CtxList **list, CtxList *sibling,
-                       void *data)
+Ctx *ctx_new_for_drawlist (void *data, size_t length)
 {
-  if (*list == NULL || *list == sibling)
-    {
-      ctx_list_prepend (list, data);
-    }
-  else
-    {
-      CtxList *prev = NULL;
-      for (CtxList *l = *list; l; l=l->next)
-        {
-          if (l == sibling)
-            { break; }
-          prev = l;
-        }
-      if (prev)
-        {
-          CtxList *new_=calloc (sizeof (CtxList), 1);
-          new_->next = sibling;
-          new_->data = data;
-          prev->next=new_;
-        }
-    }
+  Ctx *ctx = ctx_new ();
+  ctx->drawlist.flags   |= CTX_DRAWLIST_DOESNT_OWN_ENTRIES;
+  ctx->drawlist.entries  = (CtxEntry *) data;
+  ctx->drawlist.count    = length / sizeof (CtxEntry);
+  return ctx;
 }
-#endif
 
+#ifdef CTX_HAVE_SIMD
+void ctx_simd_setup ();
+#endif
 
-typedef enum
+static void ctx_setup ()
 {
-  STYLE_REVERSE         = 1 << 0,
-  STYLE_BOLD            = 1 << 1,
-  STYLE_BLINK           = 1 << 2,
-  STYLE_UNDERLINE       = 1 << 3,
-  STYLE_DIM             = 1 << 4,
-  STYLE_HIDDEN          = 1 << 5,
-  STYLE_ITALIC          = 1 << 6,
-  STYLE_UNDERLINE_VAR   = 1 << 7,
-  STYLE_STRIKETHROUGH   = 1 << 8,
-  STYLE_OVERLINE        = 1 << 9,
-  STYLE_BLINK_FAST      = 1 << 10,
-  STYLE_PROPORTIONAL    = 1 << 11,
-  STYLE_FG_COLOR_SET    = 1 << 12,
-  STYLE_BG_COLOR_SET    = 1 << 13,
-  STYLE_FG24_COLOR_SET  = 1 << 14,
-  STYLE_BG24_COLOR_SET  = 1 << 15,
-  //STYLE_NONERASABLE     = 1 << 16  // needed for selective erase
-} TerminalStyle;
-
-typedef struct Image
-{
-  int kitty_format;
-  int width;
-  int height;
-  int id;
-  int eid_no;
-  int size;
-  uint8_t *data;
-} Image;
-
-#define MAX_IMAGES 128
-
-static Image image_db[MAX_IMAGES]= {{0,},};
+#ifdef CTX_HAVE_SIMD
+  ctx_simd_setup ();
+#endif
+  ctx_font_setup ();
+}
 
-static Image *image_query (int id)
+void
+ctx_render_ctx (Ctx *ctx, Ctx *d_ctx)
 {
-  for (int i = 0; i < MAX_IMAGES; i++)
+  CtxIterator iterator;
+  CtxCommand *command;
+  ctx_iterator_init (&iterator, &ctx->drawlist, 0,
+                     CTX_ITERATOR_EXPAND_BITPACK);
+  while ( (command = ctx_iterator_next (&iterator) ) )
     {
-      Image *image = &image_db[i];
-      if (image->id == id)
-        { return image; }
+       ctx_process (d_ctx, &command->entry);
     }
-  return NULL;
 }
 
-static int image_eid_no = 0;
-
-static Image *image_add (int width,
-                         int height,
-                         int id,
-                         int format,
-                         int size,
-                         uint8_t *data)
+void
+ctx_render_ctx_textures (Ctx *ctx, Ctx *d_ctx)
 {
-  // look for id if id is not 0
-  Image *image;
-  for (int i = 0; i < MAX_IMAGES; i++)
-    {
-      image = &image_db[i];
-      if (image->data == NULL)
-        { break; }
-    }
-  if (image->data)
+  CtxIterator iterator;
+  CtxCommand *command;
+  ctx_iterator_init (&iterator, &ctx->drawlist, 0,
+                     CTX_ITERATOR_EXPAND_BITPACK);
+  while ( (command = ctx_iterator_next (&iterator) ) )
     {
-      // not a good eviction strategy
-      image = &image_db[random() %MAX_IMAGES];
+       switch (command->code)
+       {
+         default:
+                 //fprintf (stderr, "[%c]", command->code);
+                 break;
+         case CTX_TEXTURE:
+             fprintf (stderr, "t:%s\n", command->texture.eid);
+             ctx_process (d_ctx, &command->entry);
+             break;
+         case CTX_DEFINE_TEXTURE:
+             fprintf (stderr, "d:%s\n", command->define_texture.eid);
+             ctx_process (d_ctx, &command->entry);
+           break;
+       }
     }
-  if (image->data)
-    { free (image->data); }
-  image->kitty_format = format;
-  image->width  = width;
-  image->height = height;
-  image->id     = id;
-  image->size   = size;
-  image->data   = data;
-  image->eid_no = image_eid_no++;
-  return image;
 }
 
-
-
-void vtpty_resize (void *data, int cols, int rows, int px_width, int px_height)
+void ctx_quit (Ctx *ctx)
 {
-  VtPty *vtpty = data;
-  struct winsize ws;
-  ws.ws_row = rows;
-  ws.ws_col = cols;
-  ws.ws_xpixel = px_width;
-  ws.ws_ypixel = px_height;
-  ioctl (vtpty->pty, TIOCSWINSZ, &ws);
+#if CTX_EVENTS
+  ctx->quit ++;
+#endif
 }
 
-ssize_t vtpty_write (void *data, const void *buf, size_t count)
+int  ctx_has_quit (Ctx *ctx)
 {
-  VtPty *vtpty = data;
-  return write (vtpty->pty, buf, count);
+#if CTX_EVENTS
+  return (ctx->quit);
+#else
+  return 1; 
+#endif
 }
 
-ssize_t vtpty_read (void  *data, void *buf, size_t count)
+int ctx_pixel_format_bits_per_pixel (CtxPixelFormat format)
 {
-  VtPty *vtpty = data;
-  return read (vtpty->pty, buf, count);
+  CtxPixelFormatInfo *info = ctx_pixel_format_info (format);
+  if (info)
+    return info->bpp;
+  return -1;
 }
 
-int vtpty_waitdata (void  *data, int timeout)
+int ctx_pixel_format_get_stride (CtxPixelFormat format, int width)
 {
-  VtPty *vtpty = data;
-  struct timeval tv;
-  fd_set fdset;
-  FD_ZERO (&fdset);
-  FD_SET (vtpty->pty, &fdset);
-  tv.tv_sec = 0;
-  tv.tv_usec = timeout;
-  tv.tv_sec  = timeout / 1000000;
-  tv.tv_usec = timeout % 1000000;
-  if (select (vtpty->pty+1, &fdset, NULL, NULL, &tv) == -1)
-    {
-      perror ("select");
-      return 0;
-    }
-  if (FD_ISSET (vtpty->pty, &fdset) )
+  CtxPixelFormatInfo *info = ctx_pixel_format_info (format);
+  if (info)
+  {
+    switch (info->bpp)
     {
-      return 1;
+      case 0:
+      case 1:
+        return (width + 7)/8;
+      case 2:
+        return (width + 3)/4;
+      case 4:
+        return (width + 1)/2;
+      default:
+        return width * (info->bpp / 8);
     }
-  return 0;
+  }
+  return width;
 }
 
-
-/* on current line */
-static int vt_col_to_pos (VT *vt, int col)
+int ctx_pixel_format_ebpp (CtxPixelFormat format)
 {
-  int pos = col;
-  if (vt->current_line->contains_proportional)
-    {
-      Ctx *ctx = ctx_new ();
-      ctx_font (ctx, "regular");
-      ctx_font_size (ctx, vt->font_size);
-      int x = 0;
-      pos = 0;
-      int prev_prop = 0;
-      while (x <= col * vt->cw)
-        {
-          if (vt_line_get_style (vt->current_line, pos) & STYLE_PROPORTIONAL)
-            {
-              x += ctx_glyph_width (ctx, vt_line_get_unichar (vt->current_line, pos) );
-              prev_prop = 1;
-            }
-          else
-            {
-              if (prev_prop)
-                {
-                  int new_cw = vt->cw - ( (x % vt->cw) );
-                  if (new_cw < vt->cw*3/2)
-                    { new_cw += vt->cw; }
-                  x += new_cw;
-                }
-              else
-                {
-                  x += vt->cw;
-                }
-              prev_prop = 0;
-            }
-          pos ++;
-        }
-      pos --;
-      ctx_free (ctx);
-    }
-  return pos;
+  CtxPixelFormatInfo *info = ctx_pixel_format_info (format);
+  if (info)
+    return info->ebpp;
+  return -1;
 }
 
-static int vt_margin_left (VT *vt)
+int ctx_pixel_format_components (CtxPixelFormat format)
 {
-  int left = vt->left_right_margin_mode?vt->margin_left:1;
-  return vt_col_to_pos (vt, left);
+  CtxPixelFormatInfo *info = ctx_pixel_format_info (format);
+  if (info)
+    return info->components;
+  return -1;
 }
 
-#define VT_MARGIN_LEFT vt_margin_left(vt)
-
-static int vt_margin_right (VT *vt)
+#if CTX_EVENTS
+void         ctx_set_cursor (Ctx *ctx, CtxCursor cursor)
 {
-  int right = vt->left_right_margin_mode?vt->margin_right:vt->cols;
-  return vt_col_to_pos (vt, right);
+  if (ctx->cursor != cursor)
+  {
+    ctx_set_dirty (ctx, 1);
+    ctx->cursor = cursor;
+  }
 }
-
-#define VT_MARGIN_RIGHT vt_margin_right(vt)
-
-
-void vt_rev_inc (VT *vt)
+CtxCursor    ctx_get_cursor (Ctx *ctx)
 {
-  if (vt)
-    vt->rev++;
+  return ctx->cursor;
 }
 
-long vt_rev (VT *vt)
+void ctx_set_clipboard (Ctx *ctx, const char *text)
 {
-  return vt?vt->rev:0;
+  if (ctx->renderer && ctx->renderer->set_clipboard)
+  {
+    ctx->renderer->set_clipboard (ctx->renderer, text);
+    return;
+  }
 }
 
-static void vtcmd_reset_to_initial_state (VT *vt, const char *sequence);
-int vt_set_prop (VT *vt, uint32_t key_hash, const char *val);
-uint64_t ctx_strhash (const char *utf8);
-
-static void vt_set_title (VT *vt, const char *new_title)
+char *ctx_get_clipboard (Ctx *ctx)
 {
-  if (vt->inert) return;
-
-  if (vt->title)
-    { free (vt->title); }
-  vt->title = strdup (new_title);
-  vt_set_prop (vt, ctx_strhash ("title"), (char*)new_title);
+  if (ctx->renderer && ctx->renderer->get_clipboard)
+  {
+    return ctx->renderer->get_clipboard (ctx->renderer);
+  }
+  return strdup ("");
 }
 
-const char *vt_get_title (VT *vt)
+void ctx_set_texture_source (Ctx *ctx, Ctx *texture_source)
 {
-  return vt->title;
+  ((CtxRasterizer*)ctx->renderer)->texture_source = texture_source;
 }
 
-static void vt_run_command (VT *vt, const char *command, const char *term);
-static void vtcmd_set_top_and_bottom_margins (VT *vt, const char *sequence);
-static void vtcmd_set_left_and_right_margins (VT *vt, const char *sequence);
-static void _vt_move_to (VT *vt, int y, int x);
-
-static void vtcmd_clear (VT *vt, const char *sequence)
+void ctx_set_texture_cache (Ctx *ctx, Ctx *texture_cache)
 {
-  while (vt->lines)
-    {
-      vt_line_free (vt->lines->data, 1);
-      ctx_list_remove (&vt->lines, vt->lines->data);
-    }
-  vt->lines = NULL;
-  vt->line_count = 0;
-
-  if (1)
-  { /* TODO: detect if this is neccesary.. due to images present
-             in lines in scrollback */
-    for (int i=0; i<vt->rows; i++)
-    {
-      vt->current_line = vt_line_new_with_size ("", vt->cols);
-      ctx_list_prepend (&vt->scrollback, vt->current_line);
-      vt->scrollback_count++;
-    }
-  }
-
-  /* populate lines */
-  for (int i=0; i<vt->rows; i++)
-    {
-      vt->current_line = vt_line_new_with_size ("", vt->cols);
-      ctx_list_prepend (&vt->lines, vt->current_line);
-      vt->line_count++;
-    }
-}
-
-#define set_fg_rgb(r, g, b) \
-    vt->cstyle ^= (vt->cstyle & (((uint64_t)((1l<<24)-1))<<16));\
-    vt->cstyle |=  ((uint64_t)(r)<<16);\
-    vt->cstyle |=  ((uint64_t)(g)<<(16+8));\
-    vt->cstyle |=  ((uint64_t)(b)<<(16+8+8));\
-    vt->cstyle |= STYLE_FG_COLOR_SET;\
-    vt->cstyle |= STYLE_FG24_COLOR_SET;\
-
-#define set_bg_rgb(r, g, b) \
-    vt->cstyle ^= (vt->cstyle & (((uint64_t)((1l<<24)-1))<<40));\
-    vt->cstyle |=  ((uint64_t)(r)<<40);\
-    vt->cstyle |=  ((uint64_t)(g)<<(40+8));\
-    vt->cstyle |=  ((uint64_t)(b)<<(40+8+8));\
-    vt->cstyle |= STYLE_BG_COLOR_SET;\
-    vt->cstyle |= STYLE_BG24_COLOR_SET;\
-
-#define set_fg_idx(idx) \
-    vt->cstyle ^= (vt->cstyle & (((uint64_t)((1l<<24)-1))<<16));\
-    vt->cstyle ^= (vt->cstyle & STYLE_FG24_COLOR_SET);\
-    vt->cstyle |=  ((idx)<<16);\
-    vt->cstyle |= STYLE_FG_COLOR_SET;
-
-#define set_bg_idx(idx) \
-    vt->cstyle ^= (vt->cstyle & (((uint64_t)((1l<<24)-1))<<40));\
-    vt->cstyle ^= (vt->cstyle & STYLE_BG24_COLOR_SET);\
-    vt->cstyle |= ((int64_t)(idx)<<40) ;\
-    vt->cstyle |= STYLE_BG_COLOR_SET;
-
-
-static void _vt_compute_cw_ch (VT *vt)
-{
-  vt->cw = (vt->font_size / vt->line_spacing * vt->scale_x) + 0.99;
-  vt->ch = vt->font_size;
-}
-
-static void vtcmd_set_132_col (VT  *vt, int set)
-{
-  // this should probably force the window as well
-  if (set == 0 && vt->scale_x == 1.0f) return;
-  if (set == 1 && vt->scale_x != 1.0f) return;
-  if (set) // 132 col
-    {
-      vt->scale_x = 74.0/132.0; // show all - po
-      //vt->scale_x = 80.0/132.0;
-      vt->scale_y = 1.0;
-      _vt_compute_cw_ch (vt);
-      vt_set_term_size (vt, vt->cols * 132/80.0, vt->rows);
-    }
-  else // 80 col
-    {
-      vt->scale_x = 1.0;
-      vt->scale_y = 1.0;
-      _vt_compute_cw_ch (vt);
-      vt_set_term_size (vt, vt->cols * 80/132.0, vt->rows);
-    }
-}
-
-static void vt_line_feed (VT *vt);
-static void vt_carriage_return (VT *vt);
-
-static int vt_trimlines (VT *vt, int max);
-static void vtcmd_reset_to_initial_state (VT *vt, const char *sequence)
-{
-  VT_info ("reset %s", sequence);
-  if (getenv ("VT_DEBUG") )
-    { vt->debug = 1; }
-  vtcmd_clear (vt, sequence);
-  vt->encoding = 0;
-  vt->bracket_paste = 0;
-  vt->ctx_events = 0;
-  vt->cr_on_lf = 0;
-  vtcmd_set_top_and_bottom_margins (vt, "[r");
-  vtcmd_set_left_and_right_margins (vt, "[s");
-  vt->autowrap               = 1;
-  vt->justify                = 0;
-  vt->cursor_visible         = 1;
-  vt->charset[0]             = 0;
-  vt->charset[1]             = 0;
-  vt->charset[2]             = 0;
-  vt->charset[3]             = 0;
-  vt->bell                   = 3;
-  vt->scale_x                = 1.0;
-  vt->scale_y                = 1.0;
-  vt->saved_x                = 1;
-  vt->saved_y                = 1;
-  vt->saved_style            = 1;
-  vt->reverse_video          = 0;
-  vt->cstyle                 = 0;
-  vt->keyrepeat              = 1;
-  vt->cursor_key_application = 0;
-  vt->argument_buf_len       = 0;
-  vt->argument_buf[0]        = 0;
-  vt->vtpty.done             = 0;
-  vt->result                 = -1;
-  vt->state                  = vt_state_neutral;
-  vt->scroll_on_output       = 0;
-  vt->scroll_on_input        = 1;
-  vt->unit_pixels            = 0;
-  vt->mouse                  = 0;
-  vt->mouse_drag             = 0;
-  vt->mouse_all              = 0;
-  vt->mouse_decimal          = 0;
-  _vt_compute_cw_ch (vt);
-  for (int i = 0; i < MAX_COLS; i++)
-    { vt->tabs[i] = i % 8 == 0? 1 : 0; }
-  _vt_move_to (vt, vt->margin_top, vt->cursor_x);
-  vt_carriage_return (vt);
-  //if (vt->ctx)
-  //  { ctx_reset (vt->ctx); }
-  vt->audio.bits = 8;
-  vt->audio.channels = 1;
-  vt->audio.type = 'u';
-  vt->audio.samplerate = 8000;
-  vt->audio.buffer_size = 1024;
-  vt->audio.encoding = 'a';
-  vt->audio.compression = '0';
-  vt->audio.mic = 0;
-  while (vt->scrollback)
-    {
-      vt_line_free (vt->scrollback->data, 1);
-      ctx_list_remove (&vt->scrollback, vt->scrollback->data);
-    }
-  vt->scrollback_count = 0;
-}
-
-void vt_set_font_size (VT *vt, float font_size)
-{
-  vt->font_size = font_size;
-  _vt_compute_cw_ch (vt);
-}
-
-float       vt_get_font_size      (VT *vt)
-{
-  return vt->font_size;
-}
-
-void vt_set_line_spacing (VT *vt, float line_spacing)
-{
-  vt->line_spacing = line_spacing;
-  _vt_compute_cw_ch (vt);
-}
-
-VT *vt_new (const char *command, int width, int height, float font_size, float line_spacing, int id, int 
can_launch)
-{
-  VT *vt                 = calloc (sizeof (VT), 1);
-  vt->id                 = id;
-  vt->lastx              = -1;
-  vt->lasty              = -1;
-  vt->state              = vt_state_neutral;
-  vt->smooth_scroll      = 0;
-  vt->can_launch         = can_launch;
-  vt->scroll_offset      = 0.0;
-  vt->waitdata           = vtpty_waitdata;
-  vt->read               = vtpty_read;
-  vt->write              = vtpty_write;
-  vt->resize             = vtpty_resize;
-  vt->font_to_cell_scale = 0.98;
-  vt->cursor_visible     = 1;
-  vt->lines              = NULL;
-  vt->line_count         = 0;
-  vt->current_line       = NULL;
-  vt->cols               = 0;
-  vt->rows               = 0;
-
-  vt->scrollback_limit   = DEFAULT_SCROLLBACK;
-  vt->argument_buf_len   = 0;
-  vt->argument_buf_cap   = 64;
-  vt->argument_buf       = malloc (vt->argument_buf_cap);
-  vt->argument_buf[0]    = 0;
-  vt->vtpty.done         = 0;
-  vt->result             = -1;
-  vt->line_spacing       = 1.0;
-  vt->scale_x            = 1.0;
-  vt->scale_y            = 1.0;
-  vt_set_font_size (vt, font_size);
-  vt_set_line_spacing (vt, line_spacing);
-  if (command)
-    {
-      vt_run_command (vt, command, NULL);
-    }
-  if (width <= 0) width = 640;
-  if (height <= 0) width = 480;
-  vt_set_px_size (vt, width, height);
-
-  vt->fg_color[0] = 216;
-  vt->fg_color[1] = 216;
-  vt->fg_color[2] = 216;
-  vt->bg_color[0] = 0;
-  vt->bg_color[1] = 0;
-  vt->bg_color[2] = 0;
-  vtcmd_reset_to_initial_state (vt, NULL);
-  //vt->ctx = ctx_new ();
-  ctx_list_prepend (&vts, vt);
-  return vt;
-}
-
-int vt_cw (VT *vt)
-{
-  return vt->cw;
+  ctx->texture_cache = texture_cache;
 }
 
-int vt_ch (VT *vt)
+void ctx_set_transform (Ctx *ctx, float a, float b, float c, float d, float e, float f)
 {
-  return vt->ch;
+  ctx_identity (ctx);
+  ctx_apply_transform (ctx, a, b, c, d, e, f);
 }
-
-static int vt_trimlines (VT *vt, int max)
+#ifndef NO_LIBCURL
+#include <curl/curl.h>
+static size_t
+ctx_string_append_callback (void *contents, size_t size, size_t nmemb, void *userp)
 {
-  CtxList *chop_point = NULL;
-  CtxList *l;
-  int i;
-  if (vt->line_count < max)
-    { 
-      return 0;
-    }
-  for (l = vt->lines, i = 0; l && i < max-1; l = l->next, i++);
-  if (l)
-    {
-      chop_point = l->next;
-      l->next = NULL;
-    }
-  while (chop_point)
-    {
-      if (vt->in_alt_screen)
-        {
-          vt_line_free (chop_point->data, 1);
-        }
-      else
-        {
-          ctx_list_prepend (&vt->scrollback, chop_point->data);
-          vt->scrollback_count ++;
-        }
-      ctx_list_remove (&chop_point, chop_point->data);
-      vt->line_count--;
-    }
-  if (vt->scrollback_count > vt->scrollback_limit + 1024)
-    {
-      CtxList *l = vt->scrollback;
-      int no = 0;
-      while (l && no < vt->scrollback_limit)
-        {
-          l = l->next;
-          no++;
-        }
-      chop_point = NULL;
-      if (l)
-        {
-          chop_point = l->next;
-          l->next = NULL;
-        }
-      while (chop_point)
-        {
-          vt_line_free (chop_point->data, 1);
-          ctx_list_remove (&chop_point, chop_point->data);
-          vt->scrollback_count --;
-        }
-    }
-  return 0;
+  CtxString *string = (CtxString*)userp;
+  ctx_string_append_data ((CtxString*)string, contents, size * nmemb);
+  return size * nmemb;
 }
 
-static void vt_rewrap_pair (VT *vt, VtLine *topline, VtLine *bottomline, int max_col)
-{
-  int toplen = 0;
+#endif
 
-  while ((toplen = vt_line_get_utf8length (topline)) > max_col)
+int
+ctx_get_contents (const char     *uri,
+                  unsigned char **contents,
+                  long           *length)
+{
+  char temp_uri[PATH_MAX]; // XXX XXX breaks with data uri's
+  if (uri[0] == '/')
   {
-     uint32_t unichar = vt_line_get_unichar (topline, toplen-1);
-     uint32_t style =  vt_line_get_style (topline, toplen-1);
-     vt_line_insert_unichar (bottomline, 0, unichar);
-     vt_line_remove (topline, toplen-1);
-     vt_line_set_style (bottomline, 0, style);
+    snprintf (temp_uri, sizeof (temp_uri)-1, "file://%s", uri);
+    uri = temp_uri;
   }
-
-  while (vt_line_get_length (bottomline) &&
-         (toplen = vt_line_get_utf8length (topline)) < max_col)
+  else
   {
-     uint32_t unichar = vt_line_get_unichar (bottomline, 0);
-     uint32_t style =  vt_line_get_style (bottomline, 0);
-     vt_line_append_unichar (topline, unichar);
-     vt_line_set_style (topline, toplen, style);
-     vt_line_remove (bottomline, 0);
+    snprintf (temp_uri, sizeof (temp_uri)-1, uri);
+    uri = temp_uri;
   }
-}
 
-static void vt_rewrap (VT *vt, int max_col)
-{
-  if (max_col < 8) max_col = 8;
-  CtxList *list = NULL;
+  if (strchr (uri, '#'))
+   strchr (uri, '#')[0]=0;
 
-  for (CtxList *l = vt->lines; l;)
+  for (CtxList *l = registered_contents; l; l = l->next)
   {
-    CtxList *next = l->next;
-    ctx_list_prepend (&list, l->data);
-    ctx_list_remove (&vt->lines, l->data);
-    l = next;
+    CtxFileContent *c = (CtxFileContent*)l->data;
+    if (!strcmp (c->path, uri))
+    {
+      contents = malloc (c->length+1);
+      contents[c->length]=0;
+      if (length) *length = c->length;
+      return 0;
+    }
   }
-  for (CtxList *l = vt->scrollback; l;)
+
+  if (!strncmp (uri, "file://", 5))
   {
-    CtxList *next = l->next;
-    ctx_list_prepend (&list, l->data);
-    ctx_list_remove (&vt->scrollback, l->data);
-    l = next;
+    if (strchr (uri, '?'))
+     strchr (uri, '?')[0]=0;
   }
 
-  for (CtxList *l = list; l; l = l->next)
-    {
-      VtLine *line = l->data;
-      VtLine *next = l->next ?l->next->data:NULL;
+  if (!strncmp (uri, "file://", 7))
+    return __ctx_file_get_contents (uri + 7, contents, length);
+  else
+  {
+#ifndef NO_LIBCURL
+  CURL *curl = curl_easy_init ();
+  CURLcode res;
 
-      if (vt_line_get_utf8length (line) >= max_col || (next && next->wrapped))
-      {
-        if (!next)
-        {
-          ctx_list_append (&list, vt_line_new (""));
-          next = l->next->data;
-          next->wrapped = 1;
-        }
-        else if (!next->wrapped)
-        {
-          ctx_list_insert_before (&list, l->next, vt_line_new (""));
-          next = l->next->data;
-          next->wrapped = 1;
-        } 
-        vt_rewrap_pair (vt, line, next, max_col);
-        if (vt_line_get_utf8length (next) == 0)
-          ctx_list_remove (&list, l->next->data);
-      }
-    }
+  curl_easy_setopt(curl, CURLOPT_URL, uri);
+  curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
+    CtxString *string = ctx_string_new ("");
 
-  int rows = vt->rows;
-  int total_rows = ctx_list_length (list);
+      curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, ctx_string_append_callback);
+   /* we pass our 'chunk' struct to the callback function */
+  curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void*)string);
 
-  int scrollback_rows = total_rows - rows;
+  curl_easy_setopt(curl, CURLOPT_USERAGENT, "ctx/0.0");
 
-  int c = 0;
-  CtxList *l;
-  for (l = list; l && c < scrollback_rows;)
-  {
-    CtxList *next = l->next;
-    ctx_list_prepend (&vt->scrollback, l->data);
-    ctx_list_remove (&list, l->data);
-    l = next;
-    c++;
+   res = curl_easy_perform(curl);
+  /* check for errors */
+  if(res != CURLE_OK) {
+          fprintf(stderr, "curl_easy_perform() failed: %s\n",
+            curl_easy_strerror(res));
+     curl_easy_cleanup (curl);
   }
-  for (; l ;)
+  else
   {
-    CtxList *next = l->next;
-    ctx_list_prepend (&vt->lines, l->data);
-    ctx_list_remove (&list, l->data);
-    l = next;
-    c++;
+     *contents = (unsigned char*)string->str;
+     *length = string->length;
+     ctx_string_free (string, 0);
+     curl_easy_cleanup (curl);
+     return 0;
+  }
+#else
+    return __ctx_file_get_contents (uri, contents, length);
+#endif
   }
+  return -1;
 }
 
-void vt_set_term_size (VT *vt, int icols, int irows)
-{
-  if (vt->rows == irows && vt->cols == icols)
-    return;
 
+#endif
 
-  if (vt->state == vt_state_ctx)
-  {
-    // we should queue a pending resize instead,
-    // .. or set a flag indicating that the last
-    // rendered frame is discarded?
-    return;
-  }
+#endif // CTX_IMPLEMENTATION
 
-  if(1)vt_rewrap (vt, icols);
+#if CTX_COMPOSITE
+
+
+#define CTX_RGBA8_R_SHIFT  0
+#define CTX_RGBA8_G_SHIFT  8
+#define CTX_RGBA8_B_SHIFT  16
+#define CTX_RGBA8_A_SHIFT  24
+
+#define CTX_RGBA8_R_MASK   (0xff << CTX_RGBA8_R_SHIFT)
+#define CTX_RGBA8_G_MASK   (0xff << CTX_RGBA8_G_SHIFT)
+#define CTX_RGBA8_B_MASK   (0xff << CTX_RGBA8_B_SHIFT)
+#define CTX_RGBA8_A_MASK   (0xff << CTX_RGBA8_A_SHIFT)
+
+#define CTX_RGBA8_RB_MASK  (CTX_RGBA8_R_MASK | CTX_RGBA8_B_MASK)
+#define CTX_RGBA8_GA_MASK  (CTX_RGBA8_G_MASK | CTX_RGBA8_A_MASK)
+
+#if CTX_GRADIENTS
+#if CTX_GRADIENT_CACHE
 
-  while (irows > vt->rows)
-    {
-      if (vt->scrollback_count)
-        {
-          vt->scrollback_count--;
-          ctx_list_append (&vt->lines, vt->scrollback->data);
-          ctx_list_remove (&vt->scrollback, vt->scrollback->data);
-          vt->cursor_y++;
-        }
-      else
-        {
-          ctx_list_prepend (&vt->lines, vt_line_new_with_size ("", vt->cols) );
-        }
-      vt->line_count++;
-      vt->rows++;
-    }
-  while (irows < vt->rows)
-    {
-      vt->cursor_y--;
-      vt->rows--;
-    }
-  vt->rows = irows;
-  vt->cols = icols;
-  vt_resize (vt, vt->cols, vt->rows, vt->width, vt->height);
-  vt_trimlines (vt, vt->rows);
-  vt->margin_top     = 1;
-  vt->margin_left    = 1;
-  vt->margin_bottom  = vt->rows;
-  vt->margin_right   = vt->cols;
-  _vt_move_to (vt, vt->cursor_y, vt->cursor_x);
-  vt->rev++;
-  VT_info ("resize %i %i", irows, icols);
-  if (vt->ctxp)
-    ctx_parser_free (vt->ctxp);
-  vt->ctxp = NULL;
-}
 
-void vt_set_px_size (VT *vt, int width, int height)
+inline static int ctx_grad_index (float v)
 {
-  int cols = width / vt->cw;
-  int rows = height / vt->ch;
-  vt->width = width;
-  vt->height = height;
-  vt_set_term_size (vt, cols, rows);
+  int ret = v * (CTX_GRADIENT_CACHE_ELEMENTS - 1.0f) + 0.5f;
+  if (ret >= CTX_GRADIENT_CACHE_ELEMENTS)
+    return CTX_GRADIENT_CACHE_ELEMENTS - 1;
+  if (ret >= 0 && ret < CTX_GRADIENT_CACHE_ELEMENTS)
+    return ret;
+  return 0;
 }
 
+extern uint8_t ctx_gradient_cache_u8[CTX_GRADIENT_CACHE_ELEMENTS][4];
+extern uint8_t ctx_gradient_cache_u8_a[CTX_GRADIENT_CACHE_ELEMENTS][4];
+extern int ctx_gradient_cache_valid;
+
+//static void
+//ctx_gradient_cache_reset (void)
+//{
+//  ctx_gradient_cache_valid = 0;
+//}
+
 
+#endif
 
-static void vt_argument_buf_reset (VT *vt, const char *start)
+CTX_INLINE static void
+_ctx_fragment_gradient_1d_RGBA8 (CtxRasterizer *rasterizer, float x, float y, uint8_t *rgba)
 {
-  if (start)
+  float v = x;
+  CtxGradient *g = &rasterizer->state->gradient;
+  if (v < 0) { v = 0; }
+  if (v > 1) { v = 1; }
+
+  if (g->n_stops == 0)
+    {
+      rgba[0] = rgba[1] = rgba[2] = v * 255;
+      rgba[3] = 255;
+      return;
+    }
+  CtxGradientStop *stop      = NULL;
+  CtxGradientStop *next_stop = &g->stops[0];
+  CtxColor *color;
+  for (int s = 0; s < g->n_stops; s++)
+    {
+      stop      = &g->stops[s];
+      next_stop = &g->stops[s+1];
+      if (s + 1 >= g->n_stops) { next_stop = NULL; }
+      if (v >= stop->pos && next_stop && v < next_stop->pos)
+        { break; }
+      stop = NULL;
+      next_stop = NULL;
+    }
+  if (stop == NULL && next_stop)
+    {
+      color = & (next_stop->color);
+    }
+  else if (stop && next_stop == NULL)
+    {
+      color = & (stop->color);
+    }
+  else if (stop && next_stop)
     {
-      strcpy (vt->argument_buf, start);
-      vt->argument_buf_len = strlen (start);
+      uint8_t stop_rgba[4];
+      uint8_t next_rgba[4];
+      ctx_color_get_rgba8 (rasterizer->state, & (stop->color), stop_rgba);
+      ctx_color_get_rgba8 (rasterizer->state, & (next_stop->color), next_rgba);
+      int dx = (v - stop->pos) * 255 / (next_stop->pos - stop->pos);
+      for (int c = 0; c < 4; c++)
+        { rgba[c] = ctx_lerp_u8 (stop_rgba[c], next_rgba[c], dx); }
+      return;
     }
   else
-    { vt->argument_buf[vt->argument_buf_len=0]=0; }
-}
-
-static inline void vt_argument_buf_add (VT *vt, int ch)
-{
-  if (vt->argument_buf_len + 1 >= 1024 * 1024 * 16)
-    return; 
-  //
-  if (vt->argument_buf_len + 1 >=
-      vt->argument_buf_cap)
     {
-      vt->argument_buf_cap = vt->argument_buf_cap * 2;
-      vt->argument_buf = realloc (vt->argument_buf, vt->argument_buf_cap);
+      color = & (g->stops[g->n_stops-1].color);
     }
-  vt->argument_buf[vt->argument_buf_len] = ch;
-  vt->argument_buf[++vt->argument_buf_len] = 0;
+  ctx_color_get_rgba8 (rasterizer->state, color, rgba);
+  if (rasterizer->swap_red_green)
+  {
+    uint8_t tmp = rgba[0];
+    rgba[0] = rgba[2];
+    rgba[2] = tmp;
+  }
 }
 
+#if CTX_GRADIENT_CACHE
 static void
-_vt_move_to (VT *vt, int y, int x)
+ctx_gradient_cache_prime (CtxRasterizer *rasterizer);
+#endif
+
+CTX_INLINE static void
+ctx_fragment_gradient_1d_RGBA8 (CtxRasterizer *rasterizer, float x, float y, uint8_t *rgba)
 {
-  int i;
-  x = x < 1 ? 1 : (x > vt->cols ? vt->cols : x);
-  y = y < 1 ? 1 : (y > vt->rows ? vt->rows : y);
-  vt->at_line_home = 0;
-  vt->cursor_x = x;
-  vt->cursor_y = y;
-  i = vt->rows - y;
-  CtxList *l;
-  for (l = vt->lines; l && i >= 1; l = l->next, i--);
-  if (l)
+#if CTX_GRADIENT_CACHE
+  uint32_t* rgbap = ((uint32_t*)(&ctx_gradient_cache_u8[ctx_grad_index(x)][0]));
+  *((uint32_t*)rgba) = *rgbap;
+#else
+ _ctx_fragment_gradient_1d_RGBA8 (rasterizer, x, y, rgba);
+#endif
+}
+#endif
+
+CTX_INLINE static void
+ctx_RGBA8_associate_alpha (uint8_t *u8)
+{
+  uint32_t val = *((uint32_t*)(u8));
+  int a = val >> CTX_RGBA8_A_SHIFT;
+  if (a!=255)
+  {
+    if (a)
     {
-      vt->current_line = l->data;
+      uint32_t g = (((val & CTX_RGBA8_G_MASK) * a) >> 8) & CTX_RGBA8_G_MASK;
+      uint32_t rb =(((val & CTX_RGBA8_RB_MASK) * a) >> 8) & CTX_RGBA8_RB_MASK;
+      *((uint32_t*)(u8)) = g|rb|(a << CTX_RGBA8_A_SHIFT);
     }
-  else
+    else
     {
-      for (; i > 0; i--)
-        {
-          vt->current_line = vt_line_new_with_size ("", vt->cols);
-          ctx_list_append (&vt->lines, vt->current_line);
-          vt->line_count++;
-        }
+      *((uint32_t*)(u8)) = 0;
     }
-  VT_cursor ("%i,%i (_vt_move_to)", y, x);
-  vt->rev++;
+  }
+}
+
+CTX_INLINE static void
+ctx_u8_associate_alpha (int components, uint8_t *u8)
+{
+  switch (u8[components-1])
+  {
+          case 255:break;
+          case 0: 
+            for (int c = 0; c < components-1; c++)
+             u8[c] = 0;
+            break;
+          default:
+  for (int c = 0; c < components-1; c++)
+    u8[c] = (u8[c] * u8[components-1]) /255;
+  }
 }
 
-static void vt_scroll (VT *vt, int amount);
+#if CTX_GRADIENTS
+#if CTX_GRADIENT_CACHE
+static void
+ctx_gradient_cache_prime (CtxRasterizer *rasterizer)
+{
+  if (ctx_gradient_cache_valid)
+    return;
+  for (int u = 0; u < CTX_GRADIENT_CACHE_ELEMENTS; u++)
+  {
+    float v = u / (CTX_GRADIENT_CACHE_ELEMENTS - 1.0f);
+    _ctx_fragment_gradient_1d_RGBA8 (rasterizer, v, 0.0f, &ctx_gradient_cache_u8[u][0]);
+    //*((uint32_t*)(&ctx_gradient_cache_u8_a[u][0]))= *((uint32_t*)(&ctx_gradient_cache_u8[u][0]));
+    memcpy(&ctx_gradient_cache_u8_a[u][0], &ctx_gradient_cache_u8[u][0], 4);
+    ctx_RGBA8_associate_alpha (&ctx_gradient_cache_u8_a[u][0]);
+  }
+  ctx_gradient_cache_valid = 1;
+}
+#endif
 
-static void _vt_add_str (VT *vt, const char *str)
+CTX_INLINE static void
+ctx_fragment_gradient_1d_GRAYA8 (CtxRasterizer *rasterizer, float x, float y, uint8_t *rgba)
 {
-  int logical_margin_right = VT_MARGIN_RIGHT;
-  if (vt->cstyle & STYLE_PROPORTIONAL)
-    { vt->current_line->contains_proportional = 1; }
-  if (vt->cursor_x > logical_margin_right)
+  float v = x;
+  CtxGradient *g = &rasterizer->state->gradient;
+  if (v < 0) { v = 0; }
+  if (v > 1) { v = 1; }
+  if (g->n_stops == 0)
     {
-      if (vt->autowrap)
-        {
-          int chars = 0;
-          int old_x = vt->cursor_x;
-          VtLine *old_line = vt->current_line;
-          if (vt->justify && str[0] != ' ')
-            {
-              while (old_x-1-chars >1 && vt_line_get_unichar (vt->current_line,
-                     old_x-1-chars) !=' ')
-                {
-                  chars++;
-                }
-              chars--;
-              if (chars > (vt->margin_right - vt->margin_left) * 3 / 2)
-                { chars = 0; }
-            }
-          if (vt->cursor_y == vt->margin_bottom)
-            {
-              vt_scroll (vt, -1);
-            }
-          else
-            {
-              _vt_move_to (vt, vt->cursor_y+1, 1);
-            }
-          vt->current_line->wrapped=1;
-          vt_carriage_return (vt);
-          for (int i = 0; i < chars; i++)
-            {
-              vt_line_set_style (vt->current_line, vt->cursor_x-1, vt->cstyle);
-              vt_line_replace_unichar (vt->current_line, vt->cursor_x - 1,
-                                         vt_line_get_unichar (old_line, old_x-1-chars+i) );
-              vt->cursor_x++;
-            }
-          for (int i = 0; i < chars; i++)
-            {
-              vt_line_replace_unichar (old_line, old_x-1-chars+i, ' ');
-            }
-          if (str[0] == ' ')
-            return;
-        }
-      else
-        {
-          vt->cursor_x = logical_margin_right;
-        }
+      rgba[0] = rgba[1] = rgba[2] = v * 255;
+      rgba[1] = 255;
+      return;
+    }
+  CtxGradientStop *stop      = NULL;
+  CtxGradientStop *next_stop = &g->stops[0];
+  CtxColor *color;
+  for (int s = 0; s < g->n_stops; s++)
+    {
+      stop      = &g->stops[s];
+      next_stop = &g->stops[s+1];
+      if (s + 1 >= g->n_stops) { next_stop = NULL; }
+      if (v >= stop->pos && next_stop && v < next_stop->pos)
+        { break; }
+      stop = NULL;
+      next_stop = NULL;
+    }
+  if (stop == NULL && next_stop)
+    {
+      color = & (next_stop->color);
+    }
+  else if (stop && next_stop == NULL)
+    {
+      color = & (stop->color);
     }
-  if (vt->insert_mode)
+  else if (stop && next_stop)
     {
-      vt_line_insert_utf8 (vt->current_line, vt->cursor_x - 1, str);
-      while (vt->current_line->string.utf8_length > logical_margin_right)
-        { vt_line_remove (vt->current_line, logical_margin_right); }
+      uint8_t stop_rgba[4];
+      uint8_t next_rgba[4];
+      ctx_color_get_graya_u8 (rasterizer->state, & (stop->color), stop_rgba);
+      ctx_color_get_graya_u8 (rasterizer->state, & (next_stop->color), next_rgba);
+      int dx = (v - stop->pos) * 255 / (next_stop->pos - stop->pos);
+      for (int c = 0; c < 2; c++)
+        { rgba[c] = ctx_lerp_u8 (stop_rgba[c], next_rgba[c], dx); }
+      return;
     }
   else
     {
-      vt_line_replace_utf8 (vt->current_line, vt->cursor_x - 1, str);
+      color = & (g->stops[g->n_stops-1].color);
     }
-  vt_line_set_style (vt->current_line, vt->cursor_x-1, vt->cstyle);
-  vt->cursor_x += 1;
-  vt->at_line_home = 0;
-  vt->rev++;
+  ctx_color_get_graya_u8 (rasterizer->state, color, rgba);
 }
 
-static void _vt_backspace (VT *vt)
+CTX_INLINE static void
+ctx_fragment_gradient_1d_RGBAF (CtxRasterizer *rasterizer, float v, float y, float *rgba)
 {
-  if (vt->current_line)
+  CtxGradient *g = &rasterizer->state->gradient;
+  if (v < 0) { v = 0; }
+  if (v > 1) { v = 1; }
+  if (g->n_stops == 0)
     {
-      vt->cursor_x --;
-      if (vt->cursor_x == VT_MARGIN_RIGHT) { vt->cursor_x--; }
-      if (vt->cursor_x < VT_MARGIN_LEFT)
-        {
-          vt->cursor_x = VT_MARGIN_LEFT;
-          vt->at_line_home = 1;
-        }
-      VT_cursor ("backspace");
+      rgba[0] = rgba[1] = rgba[2] = v;
+      rgba[3] = 1.0;
+      return;
     }
-  vt->rev++;
-}
-
-static void vtcmd_set_top_and_bottom_margins (VT *vt, const char *sequence)
-{
-  int top = 1, bottom = vt->rows;
-  /* w3m issues this; causing reset of cursor position, why it is issued
-   * is unknown 
-   */
-  if (!strcmp (sequence, "[?1001r"))
-    return;
-  if (strlen (sequence) > 2)
+  CtxGradientStop *stop      = NULL;
+  CtxGradientStop *next_stop = &g->stops[0];
+  CtxColor *color;
+  for (int s = 0; s < g->n_stops; s++)
+    {
+      stop      = &g->stops[s];
+      next_stop = &g->stops[s+1];
+      if (s + 1 >= g->n_stops) { next_stop = NULL; }
+      if (v >= stop->pos && next_stop && v < next_stop->pos)
+        { break; }
+      stop = NULL;
+      next_stop = NULL;
+    }
+  if (stop == NULL && next_stop)
     {
-      sscanf (sequence, "[%i;%ir", &top, &bottom);
+      color = & (next_stop->color);
     }
-  VT_info ("margins: %i %i", top, bottom);
-  if (top <1) { top = 1; }
-  if (top > vt->rows) { top = vt->rows; }
-  if (bottom > vt->rows) { bottom = vt->rows; }
-  if (bottom < top) { bottom = top; }
-  vt->margin_top = top;
-  vt->margin_bottom = bottom;
-#if 0
-  _vt_move_to (vt, top, 1);
-#endif
-  vt_carriage_return (vt);
-  VT_cursor ("%i, %i (home)", top, 1);
-}
-static void vtcmd_save_cursor_position (VT *vt, const char *sequence);
-
-static void vtcmd_set_left_and_right_margins (VT *vt, const char *sequence)
-{
-  int left = 1, right = vt->cols;
-  if (!vt->left_right_margin_mode)
+  else if (stop && next_stop == NULL)
+    {
+      color = & (stop->color);
+    }
+  else if (stop && next_stop)
     {
-      vtcmd_save_cursor_position (vt, sequence);
+      float stop_rgba[4];
+      float next_rgba[4];
+      ctx_color_get_rgba (rasterizer->state, & (stop->color), stop_rgba);
+      ctx_color_get_rgba (rasterizer->state, & (next_stop->color), next_rgba);
+      int dx = (v - stop->pos) / (next_stop->pos - stop->pos);
+      for (int c = 0; c < 4; c++)
+        { rgba[c] = ctx_lerpf (stop_rgba[c], next_rgba[c], dx); }
       return;
     }
-  if (strlen (sequence) > 2)
+  else
     {
-      sscanf (sequence, "[%i;%is", &left, &right);
+      color = & (g->stops[g->n_stops-1].color);
     }
-  VT_info ("hor margins: %i %i", left, right);
-  if (left <1) { left = 1; }
-  if (left > vt->cols) { left = vt->cols; }
-  if (right > vt->cols) { right = vt->cols; }
-  if (right < left) { right = left; }
-  vt->margin_left = left;
-  vt->margin_right = right;
-  _vt_move_to (vt, vt->cursor_y, vt->cursor_x);
-  vt_carriage_return (vt);
-  //VT_cursor ("%i, %i (home)", left, 1);
-}
-
-static inline int parse_int (const char *arg, int def_val)
-{
-  if (!isdigit (arg[1]) || strlen (arg) == 2)
-    { return def_val; }
-  return atoi (arg+1);
+  ctx_color_get_rgba (rasterizer->state, color, rgba);
 }
+#endif
 
-
-static void vtcmd_set_line_home (VT *vt, const char *sequence)
+static void
+ctx_fragment_image_RGBA8 (CtxRasterizer *rasterizer, float x, float y, void *out, int count, float dx, float 
dy)
 {
-  int val = parse_int (sequence, 1);
-  char buf[256];
-  vt->left_right_margin_mode = 1;
-  sprintf (buf, "[%i;%it", val, vt->margin_right);
-  vtcmd_set_left_and_right_margins (vt, buf);
-}
+  uint8_t *rgba = (uint8_t *) out;
+  CtxSource *g = &rasterizer->state->gstate.source_fill;
+  CtxBuffer *buffer = g->texture.buffer->color_managed;
+  ctx_assert (rasterizer);
+  ctx_assert (g);
+  ctx_assert (buffer);
 
-static void vtcmd_set_line_limit (VT *vt, const char *sequence)
-{
-  int val = parse_int (sequence, 0);
-  char buf[256];
-  vt->left_right_margin_mode = 1;
-  if (val < vt->margin_left) { val = vt->margin_left; }
-  sprintf (buf, "[%i;%it", vt->margin_left, val);
-  vtcmd_set_left_and_right_margins (vt, buf);
-}
+  for (int i = 0; i < count; i ++)
+  {
 
-static void vt_scroll (VT *vt, int amount)
-{
-  int remove_no, insert_before;
-  VtLine *string = NULL;
-  if (amount == 0) { amount = 1; }
-  if (amount < 0)
-    {
-      remove_no = vt->margin_top;
-      insert_before = vt->margin_bottom;
-    }
-  else
-    {
-      remove_no = vt->margin_bottom;
-      insert_before = vt->margin_top;
-    }
-  CtxList *l;
-  int i;
-  for (i=vt->rows, l = vt->lines; i > 0 && l; l=l->next, i--)
-    {
-      if (i == remove_no)
-        {
-          string = l->data;
-          ctx_list_remove (&vt->lines, string);
-          break;
-        }
-    }
-  if (string)
-    {
-      if (!vt->in_alt_screen &&
-          (vt->margin_top == 1 && vt->margin_bottom == vt->rows) )
-        {
-          ctx_list_prepend (&vt->scrollback, string);
-          vt->scrollback_count ++;
-        }
-      else
-        {
-          vt_line_free (string, 1);
-        }
-    }
-  string = vt_line_new_with_size ("", vt->cols/4);
-  if (amount > 0 && vt->margin_top == 1)
+  int u = x - g->texture.x0;
+  int v = y - g->texture.y0;
+  int width = buffer->width;
+  int height = buffer->height;
+  if ( u < 0 || v < 0 ||
+       u >= width ||
+       v >= height)
     {
-      ctx_list_append (&vt->lines, string);
+      *((uint32_t*)(rgba)) = 0;
     }
   else
     {
-      for (i=vt->rows, l = vt->lines; l; l=l->next, i--)
-        {
-          if (i == insert_before)
-            {
-              ctx_list_insert_before (&vt->lines, l, string);
-              break;
-            }
-        }
-      if (i != insert_before)
-        {
-          ctx_list_append (&vt->lines, string);
+      int bpp = buffer->format->bpp/8;
+      if (rasterizer->state->gstate.image_smoothing)
+      {
+      uint8_t *src00 = (uint8_t *) buffer->data;
+      src00 += v * buffer->stride + u * bpp;
+      uint8_t *src01 = src00;
+      if ( u + 1 < width)
+      {
+        src01 = src00 + bpp;
+      }
+      uint8_t *src11 = src01;
+      uint8_t *src10 = src00;
+      if ( v + 1 < height)
+      {
+        src10 = src00 + buffer->stride;
+        src11 = src01 + buffer->stride;
+      }
+      float dx = (x-(int)(x)) * 255.9;
+      float dy = (y-(int)(y)) * 255.9;
+
+      switch (bpp)
+      {
+      case 1:
+        rgba[0] = rgba[1] = rgba[2] = ctx_lerp_u8 (ctx_lerp_u8 (src00[0], src01[0], dx),
+                               ctx_lerp_u8 (src10[0], src11[0], dx), dy);
+        rgba[3] = 255;
+        break;
+      case 2:
+        rgba[0] = rgba[1] = rgba[2] = ctx_lerp_u8 (ctx_lerp_u8 (src00[0], src01[0], dx),
+                               ctx_lerp_u8 (src10[0], src11[0], dx), dy);
+        rgba[3] = ctx_lerp_u8 (ctx_lerp_u8 (src00[1], src01[1], dx),
+                               ctx_lerp_u8 (src10[1], src11[1], dx), dy);
+        break;
+      case 3:
+      for (int c = 0; c < bpp; c++)
+        { rgba[c] = ctx_lerp_u8 (ctx_lerp_u8 (src00[c], src01[c], dx),
+                                 ctx_lerp_u8 (src10[c], src11[c], dx), dy);
+                
         }
-    }
-  vt->current_line = string;
-  /* not updating line count since we should always remove one and add one */
-  if (vt->smooth_scroll)
-    {
-      if (amount < 0)
-        {
-          vt->scroll_offset = -1.0;
-          vt->in_smooth_scroll = -1;
+        rgba[3]=255;
+        break;
+      break;
+      case 4:
+      for (int c = 0; c < bpp; c++)
+        { rgba[c] = ctx_lerp_u8 (ctx_lerp_u8 (src00[c], src01[c], dx),
+                                 ctx_lerp_u8 (src10[c], src11[c], dx), dy);
+                
         }
+      }
+      }
       else
+      {
+      uint8_t *src = (uint8_t *) buffer->data;
+      src += v * buffer->stride + u * bpp;
+      switch (bpp)
         {
-          vt->scroll_offset = 1.0;
-          vt->in_smooth_scroll = 1;
+          case 1:
+            for (int c = 0; c < 3; c++)
+              { rgba[c] = src[0]; }
+            rgba[3] = 255;
+            break;
+          case 2:
+            for (int c = 0; c < 3; c++)
+              { rgba[c] = src[0]; }
+            rgba[3] = src[1];
+            break;
+          case 3:
+            for (int c = 0; c < 3; c++)
+              { rgba[c] = src[c]; }
+            rgba[3] = 255;
+            break;
+          case 4:
+            for (int c = 0; c < 4; c++)
+              { rgba[c] = src[c]; }
+            break;
         }
+      }
+      if (rasterizer->swap_red_green)
+      {
+        uint8_t tmp = rgba[0];
+        rgba[0] = rgba[2];
+        rgba[2] = tmp;
+      }
     }
-
-  {
-    vt->select_begin_row += amount;
-    vt->select_end_row += amount;
-    vt->select_start_row += amount;
+    rgba += 4;
+    x += dx;
+    y += dy;
   }
 }
 
-typedef struct Sequence
+#if CTX_DITHER
+static inline int ctx_dither_mask_a (int x, int y, int c, int divisor)
 {
-  const char *prefix;
-  char        suffix;
-  void (*vtcmd) (VT *vt, const char *sequence);
-  uint32_t    compat;
-} Sequence;
+  /* https://pippin.gimp.org/a_dither/ */
+  return ( ( ( ( (x + c * 67) + y * 236) * 119) & 255 )-127) / divisor;
+}
 
-static void vtcmd_cursor_position (VT *vt, const char *sequence)
+inline static void
+ctx_dither_rgba_u8 (uint8_t *rgba, int x, int y, int dither_red_blue, int dither_green)
 {
-  int y = 1, x = 1;
-  const char *semi;
-  if (sequence[0] != 'H' && sequence[0] != 'f')
-    {
-      y = parse_int (sequence, 1);
-      if ( (semi = strchr (sequence, ';') ) )
-        {
-          x = parse_int (semi, 1);
-        }
-    }
-  if (x == 0) { x = 1; }
-  if (y == 0) { y = 1; }
-  if (vt->origin)
+  if (dither_red_blue == 0)
+    { return; }
+  for (int c = 0; c < 3; c ++)
     {
-      y += vt->margin_top - 1;
-      _vt_move_to (vt, y, vt->cursor_x);
-      x += VT_MARGIN_LEFT - 1;
+      int val = rgba[c] + ctx_dither_mask_a (x, y, 0, c==1?dither_green:dither_red_blue);
+      rgba[c] = CTX_CLAMP (val, 0, 255);
     }
-  VT_cursor ("%i %i CUP", y, x);
-  _vt_move_to (vt, y, x);
-}
-
-
-static void vtcmd_horizontal_position_absolute (VT *vt, const char *sequence)
-{
-  int x = parse_int (sequence, 1);
-  if (x<=0) { x = 1; }
-  _vt_move_to (vt, vt->cursor_y, x);
-}
-
-static void vtcmd_goto_row (VT *vt, const char *sequence)
-{
-  int y = parse_int (sequence, 1);
-  _vt_move_to (vt, y, vt->cursor_x);
 }
 
-static void vtcmd_cursor_forward (VT *vt, const char *sequence)
+inline static void
+ctx_dither_graya_u8 (uint8_t *rgba, int x, int y, int dither_red_blue, int dither_green)
 {
-  int n = parse_int (sequence, 1);
-  if (n==0) { n = 1; }
-  for (int i = 0; i < n; i++)
+  if (dither_red_blue == 0)
+    { return; }
+  for (int c = 0; c < 1; c ++)
     {
-      vt->cursor_x++;
+      int val = rgba[c] + ctx_dither_mask_a (x, y, 0, dither_red_blue);
+      rgba[c] = CTX_CLAMP (val, 0, 255);
     }
-  if (vt->cursor_x > VT_MARGIN_RIGHT)
-    { vt->cursor_x = VT_MARGIN_RIGHT; }
 }
+#endif
 
-static void vtcmd_cursor_backward (VT *vt, const char *sequence)
+CTX_INLINE static void
+ctx_RGBA8_deassociate_alpha (const uint8_t *in, uint8_t *out)
 {
-  int n = parse_int (sequence, 1);
-  if (n==0) { n = 1; }
-  for (int i = 0; i < n; i++)
+    uint32_t val = *((uint32_t*)(in));
+    int a = val >> CTX_RGBA8_A_SHIFT;
+    if (a)
     {
-      vt->cursor_x--;
-    }
-  if (vt->cursor_x < VT_MARGIN_LEFT)
+    if (a ==255)
     {
-      vt->cursor_x = VT_MARGIN_LEFT; // should this wrap??
-      vt->at_line_home = 1;
-    }
-}
-
-static void vtcmd_reverse_index (VT *vt, const char *sequence)
-{
-  int n = parse_int (sequence, 1);
-  if (n==0) { n = 1; }
-  for (int i = 0; i < n; i++)
+      *((uint32_t*)(out)) = val;
+    } else
     {
-      if (vt->cursor_y == vt->margin_top)
-        {
-          vt_scroll (vt, 1);
-          _vt_move_to (vt, vt->margin_top, vt->cursor_x);
-        }
-      else
-        {
-          _vt_move_to (vt, vt->cursor_y-1, vt->cursor_x);
-        }
+      uint32_t g = (((val & CTX_RGBA8_G_MASK) * 255 / a) >> 8) & CTX_RGBA8_G_MASK;
+      uint32_t rb =(((val & CTX_RGBA8_RB_MASK) * 255 / a) >> 8) & CTX_RGBA8_RB_MASK;
+      *((uint32_t*)(out)) = g|rb|(a << CTX_RGBA8_A_SHIFT);
     }
-}
-
-static void vtcmd_cursor_up (VT *vt, const char *sequence)
-{
-  int n = parse_int (sequence, 1);
-  if (n==0) { n = 1; }
-  for (int i = 0; i < n; i++)
+    }
+    else
     {
-      if (vt->cursor_y == vt->margin_top)
-        {
-          //_vt_move_to (vt, 1, vt->cursor_x);
-        }
-      else
-        {
-          _vt_move_to (vt, vt->cursor_y-1, vt->cursor_x);
-        }
+      *((uint32_t*)(out)) = 0;
     }
 }
 
-static void vtcmd_back_index (VT *vt, const char *sequence)
+CTX_INLINE static void
+ctx_u8_deassociate_alpha (int components, const uint8_t *in, uint8_t *out)
 {
-  // XXX implement
+  if (in[components-1])
+  {
+    if (in[components-1] != 255)
+    for (int c = 0; c < components-1; c++)
+      out[c] = (in[c] * 255) / in[components-1];
+    else
+    for (int c = 0; c < components-1; c++)
+      out[c] = in[c];
+    out[components-1] = in[components-1];
+  }
+  else
+  {
+  for (int c = 0; c < components; c++)
+    out[c] = 0;
+  }
 }
 
-static void vtcmd_forward_index (VT *vt, const char *sequence)
+CTX_INLINE static void
+ctx_float_associate_alpha (int components, float *rgba)
 {
-  // XXX implement
+  float alpha = rgba[components-1];
+  for (int c = 0; c < components-1; c++)
+    rgba[c] *= alpha;
 }
 
-static void vtcmd_index (VT *vt, const char *sequence)
+CTX_INLINE static void
+ctx_float_deassociate_alpha (int components, float *rgba, float *dst)
 {
-  int n = parse_int (sequence, 1);
-  if (n==0) { n = 1; }
-  for (int i = 0; i < n; i++)
-    {
-      if (vt->cursor_y == vt->margin_bottom)
-        {
-          vt_scroll (vt, -1);
-          _vt_move_to (vt, vt->margin_bottom, vt->cursor_x);
-        }
-      else
-        {
-          _vt_move_to (vt, vt->cursor_y + 1, vt->cursor_x);
-        }
-    }
-}
+  float ralpha = rgba[components-1];
+  if (ralpha != 0.0) ralpha = 1.0/ralpha;
 
-static void vtcmd_cursor_down (VT *vt, const char *sequence)
-{
-  int n = parse_int (sequence, 1);
-  if (n==0) { n = 1; }
-  for (int i = 0; i < n; i++)
-    {
-      if (vt->cursor_y >= vt->margin_bottom)
-        {
-          _vt_move_to (vt, vt->margin_bottom, vt->cursor_x);
-        }
-      else
-        {
-          _vt_move_to (vt, vt->cursor_y + 1, vt->cursor_x);
-        }
-    }
+  for (int c = 0; c < components-1; c++)
+    dst[c] = (rgba[c] * ralpha);
+  dst[components-1] = rgba[components-1];
 }
 
-static void vtcmd_next_line (VT *vt, const char *sequence)
+CTX_INLINE static void
+ctx_RGBAF_associate_alpha (float *rgba)
 {
-  vtcmd_index (vt, sequence);
-  _vt_move_to (vt, vt->cursor_y, vt->cursor_x);
-  vt_carriage_return (vt);
-  vt->cursor_x = VT_MARGIN_LEFT;
+  ctx_float_associate_alpha (4, rgba);
 }
 
-static void vtcmd_cursor_preceding_line (VT *vt, const char *sequence)
+CTX_INLINE static void
+ctx_RGBAF_deassociate_alpha (float *rgba, float *dst)
 {
-  vtcmd_cursor_up (vt, sequence);
-  _vt_move_to (vt, vt->cursor_y, vt->cursor_x);
-  vt->cursor_x = VT_MARGIN_LEFT;
+  ctx_float_deassociate_alpha (4, rgba, dst);
 }
 
-static void vtcmd_erase_in_line (VT *vt, const char *sequence)
-{
-  int n = parse_int (sequence, 0);
-  switch (n)
-    {
-      case 0: // clear to end of line
-        {
-          char *p = (char *) mrg_utf8_skip (vt->current_line->string.str, vt->cursor_x-1);
-          if (p) { *p = 0; }
-          // XXX : this is chopping lines
-          for (int col = vt->cursor_x; col <= VT_MARGIN_RIGHT; col++)
-            { vt_line_set_style (vt->current_line, col - 1, vt->cstyle); }
-          vt->current_line->string.length = strlen (vt->current_line->string.str);
-          vt->current_line->string.utf8_length = ctx_utf8_strlen (vt->current_line->string.str);
-        }
-        break;
-      case 1: // clear from beginning to cursor
-        {
-          for (int col = VT_MARGIN_LEFT; col <= vt->cursor_x; col++)
-            {
-              vt_line_replace_utf8 (vt->current_line, col-1, " ");
-            }
-          for (int col = VT_MARGIN_LEFT; col <= vt->cursor_x; col++)
-            { vt_line_set_style (vt->current_line, col-1, vt->cstyle); }
-          vt->current_line->string.length = strlen (vt->current_line->string.str);
-          vt->current_line->string.utf8_length = ctx_utf8_strlen (vt->current_line->string.str); // should 
be a nop
-        }
-        break;
-      case 2: // clear entire line
-        for (int col = VT_MARGIN_LEFT; col <= VT_MARGIN_RIGHT; col++)
-          { vt_line_set_style (vt->current_line, col-1, vt->cstyle); }
-        vt_line_set (vt->current_line, ""); // XXX not all
-        break;
-    }
-}
 
-static void vtcmd_erase_in_display (VT *vt, const char *sequence)
+static inline void ctx_swap_red_green_u8 (void *data)
 {
-  int n = parse_int (sequence, 0);
-  switch (n)
-    {
-      case 0: // clear to end of screen
-        {
-          char *p = (char *) mrg_utf8_skip (vt->current_line->string.str, vt->cursor_x-1);
-          if (p) { *p = 0; }
-          vt->current_line->string.length = strlen (vt->current_line->string.str);
-          vt->current_line->string.utf8_length = ctx_utf8_strlen (vt->current_line->string.str);
-        }
-        for (int col = vt->cursor_x; col <= VT_MARGIN_RIGHT; col++)
-          { vt_line_set_style (vt->current_line, col-1, vt->cstyle); }
-        {
-          CtxList *l;
-          int no = vt->rows;
-          for (l = vt->lines; l->data != vt->current_line; l = l->next, no--)
-            {
-              VtLine *buf = l->data;
-              buf->string.str[0] = 0;
-              buf->string.length = 0;
-              buf->string.utf8_length = 0;
-              for (int col = 1; col <= vt->cols; col++)
-                { vt_line_set_style (buf, col-1, vt->cstyle); }
-            }
-        }
-        break;
-      case 1: // clear from beginning to cursor
-        {
-          for (int col = 1; col <= vt->cursor_x; col++)
-            {
-              vt_line_replace_utf8 (vt->current_line, col-1, " ");
-              vt_line_set_style (vt->current_line, col-1, vt->cstyle);
-            }
-        }
-        {
-          CtxList *l;
-          int there_yet = 0;
-          int no = vt->rows;
-          for (l = vt->lines; l; l = l->next, no--)
-            {
-              VtLine *buf = l->data;
-              if (there_yet)
-                {
-                  buf->string.str[0] = 0;
-                  buf->string.length = 0;
-                  buf->string.utf8_length = 0;
-                  for (int col = 1; col <= vt->cols; col++)
-                    { vt_line_set_style (buf, col-1, vt->cstyle); }
-                }
-              if (buf == vt->current_line)
-                {
-                  there_yet = 1;
-                }
-            }
-        }
-        break;
-      case 3: // also clear scrollback
-        while (vt->scrollback)
-        {
-           vt_line_free (vt->scrollback->data, 1);
-           ctx_list_remove (&vt->scrollback, vt->scrollback->data);
-         }
-        vt->scrollback_count = 0;
-        /* FALLTHROUGH */
-      case 2: // clear entire screen but keep cursor;
-        {
-          int tx = vt->cursor_x;
-          int ty = vt->cursor_y;
-          vtcmd_clear (vt, "");
-          _vt_move_to (vt, ty, tx);
-          for (CtxList *l = vt->lines; l; l = l->next)
-            {
-              VtLine *line = l->data;
-              for (int col = 1; col <= vt->cols; col++)
-                { vt_line_set_style (line, col-1, vt->cstyle); }
-            }
-        }
-        break;
-    }
+  uint8_t *rgba = (uint8_t*)data;
+  uint8_t tmp = rgba[0];
+  rgba[0] = rgba[2];
+  rgba[2] = tmp;
 }
 
-static void vtcmd_screen_alignment_display (VT *vt, const char *sequence)
+static void
+ctx_fragment_swap_red_green_u8 (void *out, int count)
 {
-  for (int y = 1; y <= vt->rows; y++)
-    {
-      _vt_move_to (vt, y, 1);
-      for (int x = 1; x <= vt->cols; x++)
-        {
-          _vt_add_str (vt, "E");
-        }
-    }
+  uint8_t *rgba = (uint8_t*)out;
+  for (int x = 0; x < count; x++)
+  {
+    ctx_swap_red_green_u8 (rgba);
+    rgba += 4;
+  }
 }
 
-#if 0
-static int find_idx (int r, int g, int b)
-{
-  r = r / 255.0 * 5;
-  g = g / 255.0 * 5;
-  b = b / 255.0 * 5;
-  return 16 + r * 6 * 6 + g * 6 + b;
-}
-#endif
+/**** rgb8 ***/
 
-static void vtcmd_set_graphics_rendition (VT *vt, const char *sequence)
+static void
+ctx_fragment_image_rgb8_RGBA8_box (CtxRasterizer *rasterizer,
+                                   float x,
+                                   float y,
+                                   void *out, int count, float dx, float dy)
 {
-  const char *s = sequence;
-  if (s[0]) { s++; }
-  while (s && *s)
+  uint8_t *rgba = (uint8_t *) out;
+  CtxSource *g = &rasterizer->state->gstate.source_fill;
+  CtxBuffer *buffer = g->texture.buffer->color_managed;
+  int width = buffer->width;
+  int height = buffer->height;
+
+  for (int i = 0; i < count; i++)
+  {
+
+  int u = x - g->texture.x0;
+  int v = y - g->texture.y0;
+  if ( u < 0 || v < 0 ||
+       u >= width ||
+       v >= height)
     {
-      int n = parse_int (s - 1, 0); // works until color
-      // both fg and bg could be set in 256 color mode FIXME
-      //
-      /* S_GR@38@Set forground color@foo bar baz@ */
-      if (n == 38) // set foreground
-        {
-          s = strchr (s, ';');
-          if (!s)
-            {
-              VT_warning ("incomplete [38m expected ;  %s", sequence);
-              return;
-            }
-          n = parse_int (s, 0);
-          if (n == 5)
-            {
-              s++;
-              if (strchr (s, ';') )
-                { s = strchr (s, ';'); }
-              else
-                { s = strchr (s, ':'); }
-              if (s)
-                {
-                  n = parse_int (s, 0);
-                  set_fg_idx (n);
-                  s++;
-                  while (*s && *s >= '0' && *s <='9') { s++; }
-                }
-            }
-          else if (n == 2)
-            {
-              int r = 0, g = 0, b = 0;
-              s++;
-              if (strchr (s, ';') )
-                {
-                  s = strchr (s, ';');
-                  if (s)
-                    { sscanf (s, ";%i;%i;%i", &r, &g, &b); }
-                }
-              else
-                {
-                  s = strchr (s, ':');
-                  if (s)
-                    { sscanf (s, ":%i:%i:%i", &r, &g, &b); }
-                }
-              if (s)
-              for (int i = 0; i < 3; i++)
-                {
-                  if (*s)
-                    {
-                      s++;
-                      while (*s && *s >= '0' && *s <='9') { s++; }
-                    }
-                }
-              set_fg_rgb (r,g,b);
-            }
-          else
-            {
-              VT_warning ("unhandled %s %i", sequence, n);
-              return;
-            }
-          //return; // XXX we should continue, and allow further style set after complex color
-        }
-      else if (n == 48) // set background
-        {
-          s = strchr (s, ';');
-          if (!s)
-            {
-              VT_warning ("incomplete [38m expected ;  %s", sequence);
-              return;
-            }
-          n = parse_int (s, 0);
-          if (n == 5)
-            {
-              s++;
-              if (strchr (s, ';') )
-                { s = strchr (s, ';'); }
-              else
-                { s = strchr (s, ':'); }
-              if (s)
-                { n = parse_int (s, 0); }
-              set_bg_idx (n);
-              if (s)
-                {
-                  s++;
-                  while (*s && *s >= '0' && *s <='9') { s++; }
-                }
-            }
-          else if (n == 2)
-            {
-              int r = 0, g = 0, b = 0;
-              s++;
-              if (strchr (s, ';') )
-                {
-                  s = strchr (s, ';');
-                  if (s)
-                    { sscanf (s, ";%i;%i;%i", &r, &g, &b); }
-                }
-              else
-                {
-                  s = strchr (s, ':');
-                  if (s)
-                    { sscanf (s, ":%i:%i:%i", &r, &g, &b); }
-                }
-              if (s)
-              for (int i = 0; i < 3; i++)
-                {
-                  s++;
-                  while (*s >= '0' && *s <='9') { s++; }
-                }
-              set_bg_rgb (r,g,b);
-            }
-          else
-            {
-              VT_warning ("unhandled %s %i", sequence, n);
-              return;
-            }
-          //return; // we XXX should continue, and allow further style set after complex color
-        }
-      else
-        switch (n)
+      *((uint32_t*)(rgba))= 0;
+    }
+  else
+    {
+      int bpp = 3;
+      rgba[3]=255;
+      float factor = ctx_matrix_get_scale (&rasterizer->state->gstate.transform);
+          int dim = (1.0 / factor) / 2;
+          uint64_t sum[4]={0,0,0,0};
+          int count = 0;
+          for (int ou = - dim; ou < dim; ou++)
+          for (int ov = - dim; ov < dim; ov++)
           {
-            case 0: /* SGR@0@Style reset@@ */
-              if (vt->cstyle & STYLE_PROPORTIONAL)
-                { vt->cstyle = STYLE_PROPORTIONAL; }
-              else
-                { vt->cstyle = 0; }
-              break;
-            case 1: /* SGR@@Bold@@ */
-              vt->cstyle |= STYLE_BOLD;
-              break;
-            case 2: /* SGR@@Dim@@ */
-              vt->cstyle |= STYLE_DIM;
-              break;
-            case 3: /* SGR@@Italic@@ */
-              vt->cstyle |= STYLE_ITALIC;
-              break;
-            case 4: /* SGR@@Underscore@@ */
-                    /* SGR@4:2@Double underscore@@ */
-                    /* SGR@4:3@Curvy underscore@@ */
-              if (s[1] == ':')
-                {
-                  switch (s[2])
-                    {
-                      case '0':
-                        break;
-                      case '1':
-                        vt->cstyle |= STYLE_UNDERLINE;
-                        break;
-                      case '2':
-                        vt->cstyle |= STYLE_UNDERLINE|
-                                      STYLE_UNDERLINE_VAR;
-                        break;
-                      default:
-                      case '3':
-                        vt->cstyle |= STYLE_UNDERLINE_VAR;
-                        break;
-                    }
-                }
-              else
-                {
-                  vt->cstyle |= STYLE_UNDERLINE;
-                }
-              break;
-            case 5: /* SGR@@Blink@@ */
-              vt->cstyle |= STYLE_BLINK;
-              break;
-            case 6: /* SGR@@Blink Fast@@ */
-              vt->cstyle |= STYLE_BLINK_FAST;
-              break;
-            case 7: /* SGR@@Reverse@@ */
-              vt->cstyle |= STYLE_REVERSE;
-              break;
-            case 8: /* SGR@@Hidden@@ */
-              vt->cstyle |= STYLE_HIDDEN;
-              break;
-            case 9: /* SGR@@Strikethrough@@ */
-              vt->cstyle |= STYLE_STRIKETHROUGH;
-              break;
-            case 10: /* SGR@@Font 0@@ */
-              break;
-            case 11: /* SGR@@Font 1@@ */
-              break;
-            case 12: /* SGR@@Font 2(ignored)@@ */
-            case 13: /* SGR@@Font 3(ignored)@@ */
-            case 14: /* SGR@@Font 4(ignored)@@ */
-              break;
-            case 22: /* SGR@@Bold off@@ */
-              vt->cstyle ^= (vt->cstyle & STYLE_BOLD);
-              vt->cstyle ^= (vt->cstyle & STYLE_DIM);
-              break;
-            case 23: /* SGR@@Italic off@@ */
-              vt->cstyle ^= (vt->cstyle & STYLE_ITALIC);
-              break;
-            case 24: /* SGR@@Underscore off@@ */
-              vt->cstyle ^= (vt->cstyle & (STYLE_UNDERLINE|STYLE_UNDERLINE_VAR) );
-              break;
-            case 25: /* SGR@@Blink off@@ */
-              vt->cstyle ^= (vt->cstyle & STYLE_BLINK);
-              vt->cstyle ^= (vt->cstyle & STYLE_BLINK_FAST);
-              break;
-            case 26: /* SGR@@Proportional spacing @@ */
-              vt->cstyle |= STYLE_PROPORTIONAL;
-              break;
-            case 27: /* SGR@@Reverse off@@ */
-              vt->cstyle ^= (vt->cstyle & STYLE_REVERSE);
-              break;
-            case 28: /* SGR@@Hidden off@@ */
-              vt->cstyle ^= (vt->cstyle & STYLE_HIDDEN);
-              break;
-            case 29: /* SGR@@Strikethrough off@@ */
-              vt->cstyle ^= (vt->cstyle & STYLE_STRIKETHROUGH);
-              break;
-            case 30: /* SGR@@black text color@@ */
-              set_fg_idx (0);
-              break;
-            case 31: /* SGR@@red text color@@ */
-              set_fg_idx (1);
-              break;
-            case 32: /* SGR@@green text color@@ */
-              set_fg_idx (2);
-              break;
-            case 33: /* SGR@@yellow text color@@ */
-              set_fg_idx (3);
-              break;
-            case 34: /* SGR@@blue text color@@ */
-              set_fg_idx (4);
-              break;
-            case 35: /* SGR@@magenta text color@@ */
-              set_fg_idx (5);
-              break;
-            case 36: /* SGR@@cyan text color@@ */
-              set_fg_idx (6);
-              break;
-            case 37: /* SGR@@light gray text color@@ */
-              set_fg_idx (7);
-              break;
-          /* SGR@38;5;Pn@256 color index foreground color@where Pn is 0-15 is system colors 16-(16+6*6*6) is 
a 6x6x6  RGB cube and in the end a grayscale without white and black.@ */
-              /* SGR@38;2;Pr;Pg;Pb@24 bit RGB foreground color each of Pr Pg and Pb have 0-255 range@@ */
-            case 39: /* SGR@@default text color@@ */
-              set_fg_idx (vt->reverse_video?0:15);
-              vt->cstyle ^= (vt->cstyle & STYLE_FG_COLOR_SET);
-              break;
-            case 40: /* SGR@@black background color@@ */
-              set_bg_idx (0);
-              break;
-            case 41: /* SGR@@red background color@@ */
-              set_bg_idx (1);
-              break;
-            case 42: /* SGR@@green background color@@ */
-              set_bg_idx (2);
-              break;
-            case 43: /* SGR@@yellow background color@@ */
-              set_bg_idx (3);
-              break;
-            case 44: /* SGR@@blue background color@@ */
-              set_bg_idx (4);
-              break;
-            case 45: /* SGR@@magenta background color@@ */
-              set_bg_idx (5);
-              break;
-            case 46: /* SGR@@cyan background color@@ */
-              set_bg_idx (6);
-              break;
-            case 47: /* SGR@@light gray background color@@ */
-              set_bg_idx (7);
-              break;
+            uint8_t *src = (uint8_t *) buffer->data;
+            int o = (v+ov) * width + (u + ou);
 
-          /* SGR@48;5;Pn@256 color index background color@where Pn is 0-15 is system colors 16-(16+6*6*6) is 
a 6x6x6  RGB cube and in the end a grayscale without white and black.@ */
-          /* SGR@48;2;Pr;Pg;Pb@24 bit RGB background color@Where Pr Pg and Pb have 0-255 range@ */
+            if (o>=0 && o < width * height)
+            {
+              src += o * bpp;
 
-            case 49: /* SGR@@default background color@@ */
-              set_bg_idx (vt->reverse_video?15:0);
-              vt->cstyle ^= (vt->cstyle & STYLE_BG_COLOR_SET);
-              break;
-            case 50: /* SGR@@Proportional spacing off @@ */
-              vt->cstyle ^= (vt->cstyle & STYLE_PROPORTIONAL);
-              break;
-            // 51 : framed
-            // 52 : encircled
-            case 53: /* SGR@@Overlined@@ */
-              vt->cstyle |= STYLE_OVERLINE;
-              break;
-            case 55: /* SGR@@Not Overlined@@ */
-              vt->cstyle ^= (vt->cstyle&STYLE_OVERLINE);
-              break;
-            case 90: /* SGR@@dark gray text color@@ */
-              set_fg_idx (8);
-              break;
-            case 91: /* SGR@@light red text color@@ */
-              set_fg_idx (9);
-              break;
-            case 92: /* SGR@@light green text color@@ */
-              set_fg_idx (10);
-              break;
-            case 93: /* SGR@@light yellow text color@@ */
-              set_fg_idx (11);
-              break;
-            case 94: /* SGR@@light blue text color@@ */
-              set_fg_idx (12);
-              break;
-            case 95: /* SGR@@light magenta text color@@ */
-              set_fg_idx (13);
-              break;
-            case 96: /* SGR@@light cyan text color@@ */
-              set_fg_idx (14);
-              break;
-            case 97: /* SGR@@white text color@@ */
-              set_fg_idx (15);
-              break;
-            case 100: /* SGR@@dark gray background color@@ */
-              set_bg_idx (8);
-              break;
-            case 101: /* SGR@@light red background color@@ */
-              set_bg_idx (9);
-              break;
-            case 102: /* SGR@@light green background color@@ */
-              set_bg_idx (10);
-              break;
-            case 103: /* SGR@@light yellow background color@@ */
-              set_bg_idx (11);
-              break;
-            case 104: /* SGR@@light blue background color@@ */
-              set_bg_idx (12);
-              break;
-            case 105: /* SGR@@light magenta background color@@ */
-              set_bg_idx (13);
-              break;
-            case 106: /* SGR@@light cyan background color@@ */
-              set_bg_idx (14);
-              break;
-            case 107: /* SGR@@white background color@@ */
-              set_bg_idx (15);
-              break;
-            default:
-              VT_warning ("unhandled style code %i in sequence \\033%s\n", n, sequence);
-              return;
+              for (int c = 0; c < bpp; c++)
+                sum[c] += src[c];
+              count ++;
+            }
           }
-      while (s && *s && *s != ';') { s++; }
-      if (s && *s == ';') { s++; }
+          if (count)
+            for (int c = 0; c < bpp; c++)
+              rgba[c] = sum[c]/count;
     }
+    rgba += 4;
+    x += dx;
+    y += dy;
+  }
+#if CTX_DITHER
+//ctx_dither_rgba_u8 (rgba, x, y, rasterizer->format->dither_red_blue,
+//                    rasterizer->format->dither_green);
+#endif
 }
 
-static void vtcmd_ignore (VT *vt, const char *sequence)
-{
-  VT_info ("ignoring sequence %s", sequence);
-}
-
-static void vtcmd_clear_all_tabs (VT *vt, const char *sequence)
-{
-  memset (vt->tabs, 0, sizeof (vt->tabs) );
-}
-
-static void vtcmd_clear_current_tab (VT *vt, const char *sequence)
-{
-  vt->tabs[ (int) (vt->cursor_x-1)] = 0;
-}
-
-static void vtcmd_horizontal_tab_set (VT *vt, const char *sequence)
-{
-  vt->tabs[ (int) vt->cursor_x-1] = 1;
-}
-
-static void vtcmd_save_cursor_position (VT *vt, const char *sequence)
-{
-  vt->saved_x = vt->cursor_x;
-  vt->saved_y = vt->cursor_y;
-}
-
-static void vtcmd_restore_cursor_position (VT *vt, const char *sequence)
+static void
+ctx_fragment_image_rgb8_RGBA8_box_swap_red_green (CtxRasterizer *rasterizer,
+                                  float x,
+                                  float y,
+                                  void *out, int count, float dx, float dy)
 {
-  _vt_move_to (vt, vt->saved_y, vt->saved_x);
+  ctx_fragment_image_rgb8_RGBA8_box (rasterizer, x, y, out, count, dx, dy);
+  ctx_fragment_swap_red_green_u8 (out, count);
 }
 
-
-static void vtcmd_save_cursor (VT *vt, const char *sequence)
+static void
+ctx_fragment_image_rgb8_RGBA8_bi (CtxRasterizer *rasterizer,
+                                  float x,
+                                  float y,
+                                  void *out, int count, float dx, float dy)
 {
-  vt->saved_style   = vt->cstyle;
-  vt->saved_origin  = vt->origin;
-  vtcmd_save_cursor_position (vt, sequence);
-  for (int i = 0; i < 4; i++)
-    { vt->saved_charset[i] = vt->charset[i]; }
-}
+  uint8_t *rgba = (uint8_t *) out;
 
-static void vtcmd_restore_cursor (VT *vt, const char *sequence)
-{
-  vtcmd_restore_cursor_position (vt, sequence);
-  vt->cstyle  = vt->saved_style;
-  vt->origin  = vt->saved_origin;
-  for (int i = 0; i < 4; i++)
-    { vt->charset[i] = vt->saved_charset[i]; }
-}
+  CtxSource *g = &rasterizer->state->gstate.source_fill;
+  CtxBuffer *buffer = g->texture.buffer->color_managed;
+  int width = buffer->width;
+  int height = buffer->height;
 
-static void vtcmd_erase_n_chars (VT *vt, const char *sequence)
-{
-  int n = parse_int (sequence, 1);
-  while (n--)
-    {
-      vt_line_replace_utf8 (vt->current_line, vt->cursor_x - 1 + n, " ");
-      vt_line_set_style (vt->current_line, vt->cursor_x + n - 1, vt->cstyle);
-    }
-}
+  for (int i = 0; i < count; i++)
+  {
 
-static void vtcmd_delete_n_chars (VT *vt, const char *sequence)
-{
-  int n = parse_int (sequence, 1);
-  int count = n;
-  while (count--)
+  int u = x - g->texture.x0;
+  int v = y - g->texture.y0;
+  if ( u < 0 || v < 0 ||
+       u >= width ||
+       v >= height)
     {
-      vt_line_remove (vt->current_line, vt->cursor_x - 1);
+      *((uint32_t*)(rgba))= 0;
     }
-}
-
-static void vtcmd_delete_n_lines (VT *vt, const char *sequence)
-{
-  int n = parse_int (sequence, 1);
-  for (int a = 0; a < n; a++)
+  else
     {
-      int i;
-      CtxList *l;
-      VtLine *string = vt->current_line;
-      vt_line_set (string, "");
-      ctx_list_remove (&vt->lines, vt->current_line);
-      for (i=vt->rows, l = vt->lines; l; l=l->next, i--)
-        {
-          if (i == vt->margin_bottom)
-            {
-              vt->current_line = string;
-              ctx_list_insert_before (&vt->lines, l, string);
-              break;
-            }
-        }
-      _vt_move_to (vt, vt->cursor_y, vt->cursor_x); // updates current_line
+      int bpp = 3;
+      rgba[3]=255;
+      uint8_t *src00 = (uint8_t *) buffer->data;
+      int stride = buffer->stride;
+      src00 += v * stride + u * bpp;
+      uint8_t *src01 = src00;
+      if ( u + 1 < width)
+      {
+        src01 = src00 + bpp;
+      }
+      uint8_t *src11 = src01;
+      uint8_t *src10 = src00;
+      if ( v + 1 < height)
+      {
+        src10 = src00 + stride;
+        src11 = src01 + stride;
+      }
+      float dx = (x-(int)(x)) * 255.9f;
+      float dy = (y-(int)(y)) * 255.9f;
+      for (int c = 0; c < bpp; c++)
+      {
+        rgba[c] = ctx_lerp_u8 (ctx_lerp_u8 (src00[c], src01[c], dx),
+                               ctx_lerp_u8 (src10[c], src11[c], dx), dy);
+      }
     }
+    x += dx;
+    y += dy;
+    rgba += 4;
+  }
+#if CTX_DITHER
+//ctx_dither_rgba_u8 (rgba, x, y, rasterizer->format->dither_red_blue,
+//                    rasterizer->format->dither_green);
+#endif
 }
 
-static void vtcmd_insert_character (VT *vt, const char *sequence)
+static void
+ctx_fragment_image_rgb8_RGBA8_bi_swap_red_green (CtxRasterizer *rasterizer,
+                                  float x,
+                                  float y,
+                                  void *out, int count, float dx, float dy)
 {
-  int n = parse_int (sequence, 1);
-  while (n--)
-    {
-      vt_line_insert_utf8 (vt->current_line, vt->cursor_x-1, " ");
-    }
-  while (vt->current_line->string.utf8_length > vt->cols)
-    { vt_line_remove (vt->current_line, vt->cols); }
-  // XXX update style
+  ctx_fragment_image_rgb8_RGBA8_bi_swap_red_green (rasterizer, x, y, out, count, dx, dy);
+  ctx_fragment_swap_red_green_u8 (out, count);
 }
 
-static void vtcmd_scroll_up (VT *vt, const char *sequence)
+static CTX_INLINE void
+ctx_fragment_image_rgb8_RGBA8_nearest (CtxRasterizer *rasterizer,
+                                       float x,
+                                       float y,
+                                       void *out, int count, float dx, float dy)
 {
-  int n = parse_int (sequence, 1);
-  if (n == 0) { n = 1; }
-  while (n--)
-    { vt_scroll (vt, -1); }
-}
+  CtxSource *g = &rasterizer->state->gstate.source_fill;
+  CtxBuffer *buffer = g->texture.buffer->color_managed;
+  uint8_t *rgba = (uint8_t *) out;
+  uint8_t *src = (uint8_t *) buffer->data;
 
-static void vtcmd_scroll_down (VT *vt, const char *sequence)
-{
-  int n = parse_int (sequence, 1);
-  if (n == 0) { n = 1; }
-  while (n--)
-    { vt_scroll (vt, 1); }
-}
+  x += 0.5f;
+  y += 0.5f;
 
-static void vtcmd_insert_blank_lines (VT *vt, const char *sequence)
-{
-  int n = parse_int (sequence, 1);
-  if (n == 0) { n = 1; }
+  if (dy == 0.0f && dx > 0.999f && dx < 1.001f)
   {
-    int st = vt->margin_top;
-    int sb = vt->margin_bottom;
-    vt->margin_top = vt->cursor_y;
-    while (n--)
+    int v = y - g->texture.y0;
+    int u = x - g->texture.x0;
+  
+    if (v < buffer->height && v > 0)
+    {
+      int o = v * buffer->stride + u * 3;
+      int i;
+      for (i = 0; i < count && u < buffer->width; i++)
+      {
+        if (CTX_UNLIKELY(u < 0))
+          {
+            *((uint32_t*)(rgba))= 0;
+          }
+        else
+          {
+            rgba[0] = src[o];
+            rgba[1] = src[o+1];
+            rgba[2] = src[o+2]; 
+            rgba[3]=255;
+          }
+        rgba += 4;
+        o += 3;
+        u+=1;
+      }
+      for (; i < count; i++)
+      {
+        *((uint32_t*)(rgba))= 0;
+        rgba += 4;
+      }
+    }
+    else
+    {
+      for (int i = 0; i < count; i++)
       {
-        vt_scroll (vt, 1);
+        *((uint32_t*)(rgba))= 0;
+        rgba+=4;
       }
-    vt->margin_top = st;
-    vt->margin_bottom = sb;
+    }
   }
-}
-
-static void vtcmd_set_default_font (VT *vt, const char *sequence)
-{
-  vt->charset[0] = 0;
-}
-
-static void vtcmd_set_alternate_font (VT *vt, const char *sequence)
-{
-  vt->charset[0] = 1;
-}
-
-int _ctx_set_frame (Ctx *ctx, int frame);
-int _ctx_frame (Ctx *ctx);
-
-static void vt_ctx_exit (void *data)
-{
-  VT *vt = data;
-  vt->state = vt_state_neutral;
-  vt->rev ++;
-  if (!vt->current_line)
-    return;
-#if 0
-  fprintf (stderr, "\n");
-  if (vt->current_line->prev)
-  fprintf (stderr, "---prev(%i)----\n%s", (int)strlen(vt->current_line->prev),vt->current_line->prev);
-  fprintf (stderr, "---new(%i)----\n%s", 
(int)strlen(vt->current_line->frame->str),vt->current_line->frame->str);
-  fprintf (stderr, "--------\n");
-#endif
-
-#if CTX_VT_USE_FRAME_DIFF
-  if (vt->current_line->prev)
-    free (vt->current_line->prev);
-  vt->current_line->prev = NULL;
-  if (vt->current_line->frame)
+  else
   {
-    vt->current_line->prev = vt->current_line->frame->str;
-    vt->current_line->prev_length = vt->current_line->frame->length;
-
-    ctx_string_free (vt->current_line->frame, 0);
-    vt->current_line->frame = NULL;
+    for (int i = 0; i < count; i++)
+    {
+  
+    int u = x - g->texture.x0;
+    int v = y - g->texture.y0;
+    if (CTX_UNLIKELY( u < 0 || v < 0 ||
+         u >= buffer->width ||
+         v >= buffer->height))
+      {
+        *((uint32_t*)(rgba))= 0;
+      }
+    else
+      {
+        int      o = v * buffer->stride + u * 3;
+        uint8_t *src00 = (uint8_t *) buffer->data;
+        rgba[0] = src00[o];
+        rgba[1] = src00[o+1];
+        rgba[2] = src00[o+2]; 
+        rgba[3]=255;
+      }
+  
+      rgba += 4;
+      x += dx;
+      y += dy;
+    }
   }
+#if CTX_DITHER
+  //ctx_dither_rgba_u8 (rgba, x, y, rasterizer->format->dither_red_blue,
+  //                    rasterizer->format->dither_green);
 #endif
-
-  void *tmp = vt->current_line->ctx;
-  vt->current_line->ctx = vt->current_line->ctx_copy;
-  vt->current_line->ctx_copy = tmp;
-
-  _ctx_set_frame (vt->current_line->ctx, _ctx_frame (vt->current_line->ctx) + 1);
-  _ctx_set_frame (vt->current_line->ctx_copy, _ctx_frame (vt->current_line->ctx));
-#if 1
-  if (vt->ctxp) // XXX: ugly hack to aid double buffering
-    ((void**)vt->ctxp)[0]= vt->current_line->ctx;
-#endif
-
-  //ctx_parser_free (vt->ctxp);
-  //vt->ctxp = NULL;
 }
-#if 0
-#define CTX_x            CTX_STRH('x',0,0,0,0,0,0,0,0,0,0,0,0,0)
-#define CTX_y            CTX_STRH('y',0,0,0,0,0,0,0,0,0,0,0,0,0)
-#define CTX_lower_bottom CTX_STRH('l','o','w','e','r','-','b','o','t','t','o','m',0,0)
-#define CTX_lower        CTX_STRH('l','o','w','e','r',0,0,0,0,0,0,0,0,0)
-#define CTX_raise        CTX_STRH('r','a','i','s','e',0,0,0,0,0,0,0,0,0)
-#define CTX_raise_top    CTX_STRH('r','a','i','s','e','-','t','o','p',0,0,0,0,0)
-#define CTX_terminate    CTX_STRH('t','e','r','m','i','n','a','t','e',0,0,0,0,0)
-#define CTX_maximize     CTX_STRH('m','a','x','i','m','i','z','e',0,0,0,0,0,0)
-#define CTX_unmaximize   CTX_STRH('u','n','m','a','x','i','m','i','z','e',0,0,0,0)
-#define CTX_width        CTX_STRH('w','i','d','t','h',0,0,0,0,0,0,0,0,0)
-#define CTX_title        CTX_STRH('t','i','t','l','e',0,0,0,0,0,0,0,0,0)
-#define CTX_action       CTX_STRH('a','c','t','i','o','n',0,0,0,0,0,0,0,0)
-#define CTX_height       CTX_STRH('h','e','i','g','h','t',0,0,0,0,0,0,0,0)
-#endif
 
 
-static int vt_get_prop (VT *vt, const char *key, const char **val, int *len)
+static CTX_INLINE void
+ctx_fragment_image_rgb8_RGBA8_nearest_swap_red_green (CtxRasterizer *rasterizer,
+                                                      float x,
+                                                      float y,
+                                                      void *out, int count, float dx, float dy)
 {
-#if 0
-  uint32_t key_hash = ctx_strhash (key);
-  char str[4096]="";
-  fprintf (stderr, "%s: %s %i\n", __FUNCTION__, key, key_hash);
-  CtxClient *client = ctx_client_by_id (ct->id);
-  if (!client)
-    return 0;
-  switch (key_hash)
-  {
-    case CTX_title:
-      sprintf (str, "setkey %s %s\n", key, client->title);
-      break;
-    case CTX_x:      
-      sprintf (str, "setkey %s %i\n", key, client->x);
-      break;
-    case CTX_y:    
-      sprintf (str, "setkey %s %i\n", key, client->y);
-      break;
-    case CTX_width:
-      sprintf (str, "setkey %s %i\n", key, client->width);
-      break;
-    case CTX_height:
-      sprintf (str, "setkey %s %i\n", key, client->width);
-      break;
-    default:
-      sprintf (str, "setkey %s undefined\n", key);
-      break;
-  }
-  if (str[0])
-  {
-    vtpty_write ((void*)ct, str, strlen (str));
-    fprintf (stderr, "%s", str);
-  }
-#endif
-  return 0;
+  ctx_fragment_image_rgb8_RGBA8_nearest (rasterizer, x, y, out, count, dx, dy);
+  ctx_fragment_swap_red_green_u8 (out, count);
 }
 
-static void vtcmd_set_mode (VT *vt, const char *sequence)
+static void
+ctx_fragment_image_rgb8_RGBA8 (CtxRasterizer *rasterizer,
+                               float x,
+                               float y,
+                               void *out, int count, float dx, float dy)
 {
-  int set = 1;
-  if (sequence[strlen (sequence)-1]=='l')
-    { set = 0; }
-  if (sequence[1]=='?')
-    {
-      int qval;
-      sequence++;
-qagain:
-      qval = parse_int (sequence, 1);
-      switch (qval)
-        {
-          case 1: /*MODE;DECCKM;Cursor key mode;Application;Cursor;*/
-            vt->cursor_key_application = set;
-            break;
-          case 2: /*MODE;DECANM;VT52 emulation;on;off; */
-            if (set==0)
-              { vt->state = vt_state_vt52; }
-            break;
-          case 3: /*MODE;DECCOLM;Column mode;132 columns;80 columns;*/
-            vtcmd_set_132_col (vt, set);
-            break; // set 132 col
-          case 4: /*MODE;DECSCLM;Scrolling mode;smooth;jump;*/
-            vt->smooth_scroll = set;
-            break; // set 132 col
-          case 5: /*MODE;DECSCNM;Screen mode;Reverse;Normal;*/
-            vt->reverse_video = set;
-            break;
-          case 6: /*MODE;DECOM;Origin mode;Relative;Absolute;*/
-            vt->origin = set;
-            if (set)
-              {
-                _vt_move_to (vt, vt->margin_top, 1);
-                vt_carriage_return (vt);
-              }
-            else
-              { _vt_move_to (vt, 1, 1); }
-            break;
-          case 7: /*MODE;DECAWM;Autowrap;on;off;*/
-            vt->autowrap = set;
-            break;
-          case 8: /*MODE;DECARM;Auto repeat;on;off;*/
-            vt->keyrepeat = set;
-            break;
-          // case 9: // send mouse x & y on button press 
-
-          // 10 - Block DECEDM
-          // 18 - Print form feed  DECPFF  default off
-          // 19 - Print extent fullscreen DECPEX  default on
-          case 12:
-            vtcmd_ignore (vt, sequence);
-            break; // blinking_cursor
-          case 25:/*MODE;DECTCEM;Cursor visible;on;off; */
-            vt->cursor_visible = set;
-            break;
-          case 30: // from rxvt - show/hide scrollbar
-            break;
-          case 34: // DECRLM - right to left mode
-            break;
-          case 38: // DECTEK - enter tektronix mode
-            break;
-          case 60: // horizontal cursor coupling
-          case 61: // vertical cursor coupling
-            break;
-          case 69:/*MODE;DECVSSM;Left right margin mode;on;off; */
-            vt->left_right_margin_mode = set;
-            break;
-          case 80:/* DECSDM Sixel scrolling */
-            break;
-          case 437:/*MODE;;Encoding/cp437mode;cp437;utf8; */
-            vt->encoding = set ? 1 : 0;
-            break;
-          case 1000:/*MODE;;Mouse reporting;on;off;*/
-            vt->mouse = set;
-            break;
-          case 1001:
-          case 1002:/*MODE;;Mouse drag;on;off;*/
-            vt->mouse_drag = set;
-            break;
-          case 1003:/*MODE;;Mouse all;on;off;*/
-            vt->mouse_all = set;
-            break;
-          case 1006:/*MODE;;Mouse decimal;on;off;*/
-            vt->mouse_decimal = set;
-            break;
-          case 47:
-          case 1047:
-          //case 1048:
-          case 1049:/*MODE;;Alt screen;on;off;*/
-            if (set)
-              {
-                if (vt->in_alt_screen)
-                  {
-                  }
-                else
-                  {
-                    vtcmd_save_cursor (vt, "");
-                    vt->saved_lines = vt->lines;
-                    vt->saved_line_count = vt->line_count;
-                    vt->line_count = 0;
-                    vt->lines = NULL;
-                    for (int i = 0; i <  vt->rows; i++)
-                      {
-                        vt->current_line = vt_line_new_with_size ("", vt->cols);
-                        ctx_list_append (&vt->lines, vt->current_line);
-                        vt->line_count++;
-                      }
-                    vt->in_alt_screen = 1;
-                    vt_line_feed (vt);
-                    _vt_move_to (vt, 1, 1);
-                    vt_carriage_return (vt);
-                  }
-              }
-            else
-              {
-                if (vt->in_alt_screen)
-                  {
-                    while (vt->lines)
-                      {
-                        vt_line_free (vt->lines->data, 1);
-                        ctx_list_remove (&vt->lines, vt->lines->data);
-                      }
-                    vt->line_count = vt->saved_line_count;
-                    vt->lines = vt->saved_lines;
-                    vtcmd_restore_cursor (vt, "");
-                    vt->saved_lines = NULL;
-                    vt->in_alt_screen = 0;
-                  }
-                else
-                  {
-                  }
-              }
-            break; // alt screen
-          case 1010: /*MODE;;scroll on output;on;off; */ //rxvt
-            vt->scroll_on_output = set;
-            break;
-          case 1011:/*MODE:;scroll on input;on;off; */ //rxvt)
-            vt->scroll_on_input = set;
-            break;
-          case 2004:/*MODE;;bracketed paste;on;off; */
-            vt->bracket_paste = set;
-            break;
-          case 201:/*MODE;;ctx-events;on;off;*/
-            vt->ctx_events = set;
-            break;
-          
-          case 200:/*MODE;;ctx vector graphics mode;on;;*/
-            if (set)
-              {
-                if (!vt->current_line->ctx)
-                  {
-                    vt->current_line->ctx = ctx_new ();
-                    vt->current_line->ctx_copy = ctx_new ();
-                    ctx_set_texture_cache (vt->current_line->ctx_copy, vt->current_line->ctx);
-                    _ctx_set_transformation (vt->current_line->ctx, 0);
-                    _ctx_set_transformation (vt->current_line->ctx_copy, 0);
-
-                    //ctx_set_texture_cache (vt->current_line->ctx, vt->current_line->ctx_copy);
-                    //ctx_set_texture_cache (vt->current_line->ctx_copy, vt->current_line->ctx);
-#if CTX_VT_USE_FRAME_DIFF
-                    vt->current_line->frame = ctx_string_new ("");
-#endif
-                  }
-                if (vt->ctxp)
-                  ctx_parser_free (vt->ctxp);
-
-                vt->ctxp = ctx_parser_new (vt->current_line->ctx,
-                                           vt->cols * vt->cw, vt->rows * vt->ch,
-                                           vt->cw, vt->ch, vt->cursor_x, vt->cursor_y,
-                                           (void*)vt_set_prop, (void*)vt_get_prop, vt, vt_ctx_exit, vt);
-                vt->utf8_holding[vt->utf8_pos=0]=0; // XXX : needed?
-                vt->state = vt_state_ctx;
-              }
-            break;
-          default:
-            VT_warning ("unhandled CSI ? %i%s", qval, set?"h":"l");
-            return;
-        }
-      if (strchr (sequence + 1, ';') )
-        {
-          sequence = strchr (sequence + 1, ';');
-          goto qagain;
-        }
-    }
-  else
+  if (rasterizer->state->gstate.image_smoothing)
+  {
+    float factor = ctx_matrix_get_scale (&rasterizer->state->gstate.transform);
+    if (factor <= 0.50f)
     {
-      int val;
-again:
-      val = parse_int (sequence, 1);
-      switch (val)
-        {
-          case 1:/*GATM - transfer enhanced data */
-          case 2:/*KAM - keyboard action mode */
-            break;
-          case 3:/*CRM - control representation mode */
-            /* show control chars? */
-            break;
-          case 4:/*MODE2;IRM;Insert Mode;Insert;Replace; */
-            vt->insert_mode = set;
-            break;
-          case 9: /* interlace mode */
-            break;
-          case 12:/*MODE2;SRM;Local echo;on;off; */
-            vt->echo = set;
-            break;
-          case 20:/*MODE2;LNM;Carriage Return on LF/Newline;on;off;*/
-            ;
-            vt->cr_on_lf = set;
-            break;
-          case 21: // GRCM - whether SGR accumulates or a reset on each command
-            break;
-          case 32: // WYCRTSAVM - screen saver
-            break;
-          case 33: // WYSTCURM  - steady cursor
-            break;
-          case 34: // WYULCURM  - underline cursor
-            break;
-          default:
-            VT_warning ("unhandled CSI %ih", val);
-            return;
-        }
-      if (strchr (sequence, ';') && sequence[0] != ';')
-        {
-          sequence = strchr (sequence, ';');
-          goto again;
-        }
+      if (rasterizer->swap_red_green)
+        ctx_fragment_image_rgb8_RGBA8_box_swap_red_green (rasterizer, x, y, out, count, dx, dy);
+      else
+        ctx_fragment_image_rgb8_RGBA8_box (rasterizer, x, y, out, count, dx, dy);
     }
-}
-
-static void vtcmd_request_mode (VT *vt, const char *sequence)
-{
-  char buf[64]="";
-  if (sequence[1]=='?')
+    else if (factor > 0.99f && factor < 1.01f)
     {
-      int qval;
-      sequence++;
-      qval = parse_int (sequence, 1);
-      int is_set = -1; // -1 undefiend   0 reset 1 set  1000 perm_reset  1001 perm_set
-      switch (qval)
-        {
-          case 1:
-            is_set = vt->cursor_key_application;
-            break;
-          case 2: /*VT52 emulation;;enable; */
-          //if (set==0) vt->in_vt52 = 1;
-            is_set = 1001;
-            break;
-          case 3:
-            is_set = 0;
-            break;
-          case 4:
-            is_set = vt->smooth_scroll;
-            break;
-          case 5:
-            is_set = vt->reverse_video;
-            break;
-          case 6:
-            is_set = vt->origin;
-            break;
-          case 7:
-            is_set = vt->autowrap;
-            break;
-          case 8:
-            is_set = vt->keyrepeat;
-            break;
-          case 9: // should be dynamic
-            is_set = 1000;
-            break;
-          case 10:
-          case 11:
-          case 12:
-          case 13:
-          case 14:
-          case 16:
-          case 18:
-          case 19:
-            is_set = 1000;
-            break;
-          case 25:
-            is_set = vt->cursor_visible;
-            break;
-          case 45:
-            is_set = 1000;
-            break;
-          case 47:
-            is_set = vt->in_alt_screen;
-            break;
-          case 69:
-            is_set = vt->left_right_margin_mode;
-            break;
-          case 437:
-            is_set = vt->encoding;
-            break;
-          case 1000:
-            is_set = vt->mouse;
-            break;
-            // 1001 hilite tracking
-          case 1002:
-            is_set = vt->mouse_drag;
-            break;
-          case 1003:
-            is_set = vt->mouse_all;
-            break;
-          case 1006:
-            is_set = vt->mouse_decimal;
-            break;
-          case 201:
-            is_set = vt->ctx_events;
-            break;
-          case 2004:
-            is_set = vt->bracket_paste;
-            break;
-          case 1010: // scroll to bottom on tty output (rxvt)
-            is_set = vt->scroll_on_output;
-            break;
-          case 1011: // scroll to bottom on key press (rxvt)
-            is_set = vt->scroll_on_input;
-            break;
-          case 1049:
-            is_set = vt->in_alt_screen;
-            break;
-            break;
-          case 200:/*ctx protocol;On;;*/
-            is_set = (vt->state == vt_state_ctx);
-            break;
-          case 80:/* DECSDM Sixel scrolling */
-          case 30: // from rxvt - show/hide scrollbar
-          case 34: // DECRLM - right to left mode
-          case 60: // horizontal cursor coupling
-          case 61: // vertical cursor coupling
-          default:
-            break;
-        }
-      switch (is_set)
-      {
-        case 0:
-          sprintf (buf, "\033[?%i;%i$y", qval, 2);
-          break;
-        case 1:
-          { sprintf (buf, "\033[?%i;%i$y", qval, 1); }
-          break;
-        case 1000:
-          sprintf (buf, "\033[?%i;%i$y", qval, 4);
-          break;
-        case 1001:
-          sprintf (buf, "\033[?%i;%i$y", qval, 3);
-          break;
-        case -1:
-          { sprintf (buf, "\033[?%i;%i$y", qval, 0); }
-      }
+      // XXX missing translate test
+      if (rasterizer->swap_red_green)
+        ctx_fragment_image_rgb8_RGBA8_nearest_swap_red_green (rasterizer, x, y, out, count, dx, dy);
+      else
+        ctx_fragment_image_rgb8_RGBA8_nearest (rasterizer, x, y, out, count, dx, dy);
     }
-  else
+    else
     {
-      int val;
-      val = parse_int (sequence, 1);
-      switch (val)
-        {
-          case 1:
-            sprintf (buf, "\033[%i;%i$y", val, 0);
-            break;
-          case 2:/* AM - keyboard action mode */
-            sprintf (buf, "\033[%i;%i$y", val, 0);
-            break;
-          case 3:/*CRM - control representation mode */
-            sprintf (buf, "\033[%i;%i$y", val, 0);
-            break;
-          case 4:/*Insert Mode;Insert;Replace; */
-            sprintf (buf, "\033[%i;%i$y", val, vt->insert_mode?1:2);
-            break;
-          case 9: /* interlace mode */
-            sprintf (buf, "\033[%i;%i$y", val, 1);
-            break;
-          case 12:
-            sprintf (buf, "\033[%i;%i$y", val, vt->echo?1:2);
-            break;
-          case 20:/*Carriage Return on LF/Newline;on;off;*/
-            ;
-            sprintf (buf, "\033[%i;%i$y", val, vt->cr_on_lf?1:2);
-            break;
-          case 21: // GRCM - whether SGR accumulates or a reset on each command
-          default:
-            sprintf (buf, "\033[%i;%i$y", val, 0);
-        }
+      if (rasterizer->swap_red_green)
+        ctx_fragment_image_rgb8_RGBA8_bi_swap_red_green (rasterizer, x, y, out, count, dx, dy);
+      else
+        ctx_fragment_image_rgb8_RGBA8_bi (rasterizer, x, y, out, count, dx, dy);
     }
-  if (buf[0])
-    { vt_write (vt, buf, strlen (buf) ); }
-}
-
-static void vtcmd_set_t (VT *vt, const char *sequence)
-{
-  /* \e[21y is request title - allows inserting keychars */
-  if      (!strcmp (sequence,  "[1t")) { ctx_client_unshade (vt->id); }
-  else if (!strcmp (sequence,  "[2t")) { ctx_client_shade (vt->id); } 
-  else if (!strncmp (sequence, "[3;", 3)) {
-    int x=0,y=0;
-    sscanf (sequence, "[3;%i;%ir", &y, &x);
-    ctx_client_move (vt->id, x, y);
   }
-  else if (!strncmp (sequence, "[4;", 3))
+  else
   {
-    int width = 0, height = 0;
-    sscanf (sequence, "[4;%i;%ir", &height , &width);
-    if (width < 0) width = vt->cols * vt->cw;
-    if (height < 0) height = vt->rows * vt->ch;
-    if (width == 0) width = ctx_width (vt->root_ctx);
-    if (height == 0) height = ctx_height (vt->root_ctx);
-    ctx_client_resize (vt->id, width, height);
+    if (rasterizer->swap_red_green)
+      ctx_fragment_image_rgb8_RGBA8_nearest_swap_red_green (rasterizer, x, y, out, count, dx, dy);
+    else
+      ctx_fragment_image_rgb8_RGBA8_nearest (rasterizer, x, y, out, count, dx, dy);
   }
-  else if (!strcmp (sequence, "[5t") ) { ctx_client_raise_top (vt->id); } 
-  else if (!strcmp (sequence, "[6t") ) { ctx_client_lower_bottom (vt->id); } 
-  else if (!strcmp (sequence, "[7t") ) { vt->rev++; /* refresh */ }
-  else if (!strncmp (sequence, "[8;", 3) )
+#if CTX_DITHER
   {
-    int cols = 0, rows = 0;
-    sscanf (sequence, "[8;%i;%ir", &rows, &cols);
-    if (cols < 0) cols = vt->cols;
-    if (rows < 0) rows = vt->rows;
-    if (cols == 0) cols = ctx_width (vt->root_ctx) / vt->cw;
-    if (rows == 0) rows = ctx_height (vt->root_ctx) / vt->ch;
-    ctx_client_resize (vt->id, cols * vt->cw, rows * vt->ch);
+  uint8_t *rgba = (uint8_t*)out;
+  ctx_dither_rgba_u8 (rgba, x, y, rasterizer->format->dither_red_blue,
+                      rasterizer->format->dither_green);
   }
-  else if (!strcmp (sequence, "[9;0t") ) { ctx_client_unmaximize (vt->id); } 
-  else if (!strcmp (sequence, "[9;1t") ) { ctx_client_maximize (vt->id);} 
+#endif
+}
 
-  /* should actually be full-screen */
-  else if (!strcmp (sequence, "[10;0t") ) { ctx_client_unmaximize (vt->id); } 
-  else if (!strcmp (sequence, "[10;1t") ) { ctx_client_maximize (vt->id);} 
-  else if (!strcmp (sequence, "[10;2t") ) { ctx_client_toggle_maximized (vt->id);} 
 
-  else if (!strcmp (sequence, "[11t") )  /* report window state  */
-    {
-      char buf[128];
-      if (ctx_client_is_iconified (vt->id))
-        sprintf (buf, "\033[2t");
-      else
-        sprintf (buf, "\033[1t");
-      vt_write (vt, buf, strlen (buf) );
-    }
-  else if (!strcmp (sequence, "[13t") ) /* request terminal position */
-    {
-      char buf[128];
-      sprintf (buf, "\033[3;%i;%it", ctx_client_y (vt->id), ctx_client_x (vt->id));
-      vt_write (vt, buf, strlen (buf) );
-    }
-  else if (!strcmp (sequence, "[14t") ) /* request terminal dimensions in px */
-    {
-      char buf[128];
-      sprintf (buf, "\033[4;%i;%it", vt->rows * vt->ch, vt->cols * vt->cw);
-      vt_write (vt, buf, strlen (buf) );
-    }
-  else if (!strcmp (sequence, "[15t") ) /* request root dimensions in px */
-    {
-      char buf[128];
-      sprintf (buf, "\033[5;%i;%it", ctx_height (vt->root_ctx), ctx_width(vt->root_ctx));
-      vt_write (vt, buf, strlen (buf) );
-    }
-  else if (!strcmp (sequence, "[16t") ) /* request char dimensions in px */
-    {
-      char buf[128];
-      sprintf (buf, "\033[6;%i;%it", vt->ch, vt->cw);
-      vt_write (vt, buf, strlen (buf) );
-    }
-  else if (!strcmp (sequence, "[18t") ) /* request terminal dimensions */
-    {
-      char buf[128];
-      sprintf (buf, "\033[8;%i;%it", vt->rows, vt->cols);
-      vt_write (vt, buf, strlen (buf) );
-    }
-  else if (!strcmp (sequence, "[19t") ) /* request root window size in char */
-    {
-      char buf[128];
-      sprintf (buf, "\033[9;%i;%it", ctx_height(vt->root_ctx)/vt->ch, ctx_width (vt->root_ctx)/vt->cw);
-      vt_write (vt, buf, strlen (buf) );
-    }
+/************** rgba8 */
 
-#if 0
-  {"[", 's',  foo, VT100}, /*args:PnSP id:DECSWBV Set warning bell volume */
-#endif
-  else if (sequence[strlen (sequence)-2]==' ') /* DECSWBV */
+static void
+ctx_fragment_image_rgba8_RGBA8_box (CtxRasterizer *rasterizer,
+                                    float x,
+                                    float y,
+                                    void *out, int count, float dx, float dy)
+{
+  uint8_t *rgba = (uint8_t *) out;
+  CtxSource *g = &rasterizer->state->gstate.source_fill;
+  CtxBuffer *buffer = g->texture.buffer->color_managed;
+
+  for (int i = 0; i < count; i ++)
+  {
+
+  int u = x - g->texture.x0;
+  int v = y - g->texture.y0;
+  if ( u < 0 || v < 0 ||
+       u >= buffer->width ||
+       v >= buffer->height)
     {
-      int val = parse_int (sequence, 0);
-      if (val <= 1) { vt->bell = 0; }
-      if (val <= 8) { vt->bell = val; }
+      *((uint32_t*)(rgba))= 0;
     }
   else
     {
-      // XXX: X for ints >=24 resize to that number of lines
-      VT_info ("unhandled subsequence %s", sequence);
-    }
-}
+      int bpp = 4;
+      float factor = ctx_matrix_get_scale (&rasterizer->state->gstate.transform);
+          int dim = (1.0 / factor) / 2;
+          uint64_t sum[4]={0,0,0,0};
+          int count = 0;
+          int width = buffer->width;
+          int height = buffer->height;
+          for (int ou = - dim; ou < dim; ou++)
+          for (int ov = - dim; ov < dim; ov++)
+          {
+            uint8_t *src = (uint8_t *) buffer->data;
+            int o = (v+ov) * width + (u + ou);
 
-static void _vt_htab (VT *vt)
-{
-  do
-    {
-      vt->cursor_x ++;
-    }
-  while (vt->cursor_x < VT_MARGIN_RIGHT &&  ! vt->tabs[ (int) vt->cursor_x-1]);
-  if (vt->cursor_x > VT_MARGIN_RIGHT)
-    { vt->cursor_x = VT_MARGIN_RIGHT; }
-}
+            if (o>=0 && o < width * height)
+            {
+              src += o * bpp;
 
-static void _vt_rev_htab (VT *vt)
-{
-  do
-    {
-      vt->cursor_x--;
+              for (int c = 0; c < bpp; c++)
+                sum[c] += src[c];
+              count ++;
+            }
+          }
+          if (count)
+            for (int c = 0; c < bpp; c++)
+              rgba[c] = sum[c]/count;
     }
-  while ( ! vt->tabs[ (int) vt->cursor_x-1] && vt->cursor_x > 1);
-  if (vt->cursor_x < VT_MARGIN_LEFT)
-    { vt_carriage_return (vt); }
+    rgba += 4;
+    x += dx;
+    y += dy;
+  }
+#if CTX_DITHER
+//ctx_dither_rgba_u8 (rgba, x, y, rasterizer->format->dither_red_blue,
+//                    rasterizer->format->dither_green);
+#endif
 }
 
-static void vtcmd_insert_n_tabs (VT *vt, const char *sequence)
+static void
+ctx_fragment_image_rgba8_RGBA8_bi (CtxRasterizer *rasterizer,
+                                   float x,
+                                   float y,
+                                   void *out, int count, float dx, float dy)
 {
-  int n = parse_int (sequence, 1);
-  while (n--)
+  uint8_t *rgba = (uint8_t *) out;
+
+  CtxSource *g = &rasterizer->state->gstate.source_fill;
+  CtxBuffer *buffer = g->texture.buffer->color_managed;
+
+  for (int i = 0; i < count; i ++)
+  {
+
+  int u = x - g->texture.x0;
+  int v = y - g->texture.y0;
+  if ( u < 0 || v < 0 ||
+       u >= buffer->width ||
+       v >= buffer->height)
     {
-      _vt_htab (vt);
+      *((uint32_t*)(rgba))= 0;
     }
-}
-
-static void vtcmd_rev_n_tabs (VT *vt, const char *sequence)
-{
-  int n = parse_int (sequence, 1);
-  while (n--)
+  else
     {
-      _vt_rev_htab (vt);
+      int bpp = 4;
+      uint8_t *src00 = (uint8_t *) buffer->data;
+      int stride = buffer->stride;
+      src00 += v * stride + u * bpp;
+      uint8_t *src01 = src00;
+      if ( u + 1 < buffer->width)
+      {
+        src01 = src00 + bpp;
+      }
+      uint8_t *src11 = src01;
+      uint8_t *src10 = src00;
+      if ( v + 1 < buffer->height)
+      {
+        src10 = src00 + stride;
+        src11 = src01 + stride;
+      }
+      float dx = (x-(int)(x)) * 255.9;
+      float dy = (y-(int)(y)) * 255.9;
+      for (int c = 0; c < bpp; c++)
+      {
+        rgba[c] = ctx_lerp_u8 (ctx_lerp_u8 (src00[c], src01[c], dx),
+                               ctx_lerp_u8 (src10[c], src11[c], dx), dy);
+      }
     }
-}
 
-static void vtcmd_set_double_width_double_height_top_line
-(VT *vt, const char *sequence)
-{
-  vt->current_line->double_width         = 1;
-  vt->current_line->double_height_top    = 1;
-  vt->current_line->double_height_bottom = 0;
-}
-static void vtcmd_set_double_width_double_height_bottom_line
-(VT *vt, const char *sequence)
-{
-  vt->current_line->double_width         = 1;
-  vt->current_line->double_height_top    = 0;
-  vt->current_line->double_height_bottom = 1;
-}
-static void vtcmd_set_single_width_single_height_line
-(VT *vt, const char *sequence)
-{
-  vt->current_line->double_width         = 0;
-  vt->current_line->double_height_top    = 0;
-  vt->current_line->double_height_bottom = 0;
+
+    x += dx;
+    y += dy;
+    rgba += 4;
+  }
+#if CTX_DITHER
+//ctx_dither_rgba_u8 (rgba, x, y, rasterizer->format->dither_red_blue,
+//                    rasterizer->format->dither_green);
+#endif
 }
+
 static void
-vtcmd_set_double_width_single_height_line
-(VT *vt, const char *sequence)
+ctx_fragment_image_rgba8_RGBA8_nearest (CtxRasterizer *rasterizer,
+                                        float x,
+                                        float y,
+                                        void *out, int count, float dx, float dy)
 {
-  vt->current_line->double_width         = 1;
-  vt->current_line->double_height_top    = 0;
-  vt->current_line->double_height_bottom = 0;
-}
+  uint8_t *rgba = (uint8_t *) out;
+  CtxSource *g = &rasterizer->state->gstate.source_fill;
+  CtxBuffer *buffer = g->texture.buffer->color_managed;
+  int bwidth = buffer->width;
+  int bheight = buffer->height;
+  x += 0.5f;
+  y += 0.5f;
 
-static void vtcmd_set_led (VT *vt, const char *sequence)
-{
-  int val = 0;
-  //fprintf (stderr, "%s\n", sequence);
-  for (const char *s = sequence; *s; s++)
+  if (dy == 0.0f && dx > 0.999f && dx < 1.001f)
+  {
+    int u = x - g->texture.x0;
+    int v = y - g->texture.y0;
+    if (u >= 0 && v >= 0 && u + count < bwidth && v < bheight)
     {
-      switch (*s)
-        {
-          case '0': val = 0; break;
-          case '1': val = 1; break;
-          case '2': val = 2; break;
-          case '3': val = 3; break;
-          case '4': val = 4; break;
-          case ';':
-          case 'q':
-            if (val == 0)
-              { vt->leds[0] = vt->leds[1] = vt->leds[2] = vt->leds[3] = 0; }
-            else
-              { vt->leds[val-1] = 1; }
-            val = 0;
-            break;
-        }
+      uint32_t *src = (uint32_t *) buffer->data;
+      memcpy (out, &src[v * bwidth + u], count * 4);
+      return;
+    }
+  }
+  //else
+  {
+    for (int i = 0; i < count; i ++)
+    {
+  
+    int u = x - g->texture.x0;
+    int v = y - g->texture.y0;
+    if ( u < 0 || v < 0 ||
+         u >= bwidth ||
+         v >= bheight)
+      {
+        *((uint32_t*)(rgba))= 0;
+      }
+    else
+      {
+        int      i = (v * bwidth + u);
+        uint32_t *src = (uint32_t *) buffer->data;
+        *((uint32_t*)(rgba))= src[i];
+      }
+  
+      x += dx;
+      y += dy;
+      rgba += 4;
     }
+  }
+#if CTX_DITHER
+  //ctx_dither_rgba_u8 (rgba, x, y, rasterizer->format->dither_red_blue,
+  //                    rasterizer->format->dither_green);
+#endif
+
+
+#if CTX_DITHER
+  //ctx_dither_rgba_u8 (rgba, x, y, rasterizer->format->dither_red_blue,
+  //                    rasterizer->format->dither_green);
+#endif
 }
 
-static void vtcmd_char_at_cursor (VT *vt, const char *sequence)
+static void
+ctx_fragment_image_rgba8_RGBA8_box_swap_red_green (CtxRasterizer *rasterizer,
+                                    float x,
+                                    float y,
+                                    void *out, int count, float dx, float dy)
 {
-  char *buf="";
-  vt_write (vt, buf, strlen (buf) );
+  ctx_fragment_image_rgba8_RGBA8_box (rasterizer, x, y, out, count, dx, dy);
+  ctx_fragment_swap_red_green_u8 (out, count);
 }
 
-static void vtcmd_DECELR (VT *vt, const char *sequence)
+static void
+ctx_fragment_image_rgba8_RGBA8_bi_swap_red_green (CtxRasterizer *rasterizer,
+                                    float x,
+                                    float y,
+                                    void *out, int count, float dx, float dy)
 {
-  int ps1 = parse_int (sequence, 0);
-  int ps2 = 0;
-  const char *s = strchr (sequence, ';');
-  if (ps1) {/* unused */};
-  if (s)
-    { ps2 = parse_int (s, 0); }
-  if (ps2 == 1)
-    { vt->unit_pixels = 1; }
-  else
-    { vt->unit_pixels = 0; }
+  ctx_fragment_image_rgba8_RGBA8_bi (rasterizer, x, y, out, count, dx, dy);
+  ctx_fragment_swap_red_green_u8 (out, count);
 }
 
-static void vtcmd_graphics (VT *vt, const char *sequence)
+static void
+ctx_fragment_image_rgba8_RGBA8_nearest_swap_red_green (CtxRasterizer *rasterizer,
+                                    float x,
+                                    float y,
+                                    void *out, int count, float dx, float dy)
 {
-  fprintf (stderr, "gfx intro [%s]\n",sequence); // maybe implement such as well?
+  ctx_fragment_image_rgba8_RGBA8_nearest (rasterizer, x, y, out, count, dx, dy);
+  ctx_fragment_swap_red_green_u8 (out, count);
 }
 
-static void vtcmd_report (VT *vt, const char *sequence)
+static void
+ctx_fragment_image_rgba8_RGBA8 (CtxRasterizer *rasterizer,
+                                float x,
+                                float y,
+                                void *out, int count, float dx, float dy)
 {
-  char buf[64]="";
-  if (!strcmp (sequence, "[5n") ) // DSR device status report
-    {
-      sprintf (buf, "\033[0n"); // we're always OK :)
-    }
-  else if (!strcmp (sequence, "[?15n") ) // printer status
-    {
-      sprintf (buf, "\033[?13n"); // no printer
-    }
-  else if (!strcmp (sequence, "[?26n") ) // keyboard dialect
-    {
-      sprintf (buf, "\033[?27;1n"); // north american/ascii
-    }
-  else if (!strcmp (sequence, "[?25n") ) // User Defined Key status
-    {
-      sprintf (buf, "\033[?21n"); // locked
-    }
-#if 0
-  {"[6n", 0, },  /* id:DSR  cursor position report, yields a reply <tt>\e[Pl;PcR</tt> */
-#endif
-  else if (!strcmp (sequence, "[6n") ) // DSR cursor position report
-    {
-      sprintf (buf, "\033[%i;%iR", vt->cursor_y - (vt->origin? (vt->margin_top - 1) :0), (int) vt->cursor_x 
- (vt->origin? (VT_MARGIN_LEFT-1) :0) );
-    }
-  else if (!strcmp (sequence, "[?6n") ) // DECXPR extended cursor position report
-    {
-#if 0
-  {"[?6n", 0, },  /* id:DEXCPR  extended cursor position report, yields a reply <tt>\e[Pl;PcR</tt> */
-#endif
-      sprintf (buf, "\033[?%i;%i;1R", vt->cursor_y - (vt->origin? (vt->margin_top - 1) :0), (int) 
vt->cursor_x - (vt->origin? (VT_MARGIN_LEFT-1) :0) );
-    }
-  else if (!strcmp (sequence, "[>c") )
+  if (rasterizer->state->gstate.image_smoothing)
+  {
+    float factor = ctx_matrix_get_scale (&rasterizer->state->gstate.transform);
+    if (factor <= 0.50f)
     {
-      sprintf (buf, "\033[>23;01;1c");
+      if (rasterizer->swap_red_green)
+        ctx_fragment_image_rgba8_RGBA8_box_swap_red_green (rasterizer, x, y, out, count, dx, dy);
+      else
+        ctx_fragment_image_rgba8_RGBA8_box (rasterizer, x, y, out, count, dx, dy);
     }
-  else if (sequence[strlen (sequence)-1]=='c') // device attributes
+    else if (factor > 0.99f && factor < 1.01f)
     {
-      //buf = "\033[?1;2c"; // what rxvt reports
-      //buf = "\033[?1;6c"; // VT100 with AVO ang GPO
-      //buf = "\033[?2c";     // VT102
-      sprintf (buf, "\033[?63;14;4;22c");
+      // XXX: also verify translate == 0 for this fast path to be valid
+      if (rasterizer->swap_red_green)
+        ctx_fragment_image_rgba8_RGBA8_nearest_swap_red_green (rasterizer, x, y, out, count, dx, dy);
+      else
+        ctx_fragment_image_rgba8_RGBA8_nearest (rasterizer, x, y, out, count, dx, dy);
     }
-  else if (sequence[strlen (sequence)-1]=='x') // terminal parameters
+    else
     {
-      if (!strcmp (sequence, "[1x") )
-        { sprintf (buf, "\033[3;1;1;120;120;1;0x"); }
+      if (rasterizer->swap_red_green)
+        ctx_fragment_image_rgba8_RGBA8_bi_swap_red_green (rasterizer, x, y, out, count, dx, dy);
       else
-        { sprintf (buf, "\033[2;1;1;120;120;1;0x"); }
-    }
-  if (buf[0])
-    { vt_write (vt, buf, strlen (buf) ); }
-}
-
-static char *charmap_cp437[]=
-{
-  " ","☺","☻","♥","♦","♣","♠","•","◘","○","◙","♂","♀","♪","♫","☼",
-  "►","◄","↕","‼","¶","§","▬","↨","↑","↓","→","←","∟","↔","▲","▼",
-  " ","!","\"","#","$","%","&","'","(",")","*","+",",","-",".","/",
-  "0","1","2","3","4","5","6","7","8","9",":",";","<","=",">","?",
-  "@","A","B","C","D","E","F","G","H","I","J","K","L","M","N","O",
-  "P","Q","R","S","T","U","V","W","X","Y","Z","[","\\","]","^","_",
-  "`","a","b","c","d","e","f","g","h","i","j","k","l","m","n","o",
-  "p","q","r","s","t","u","v","w","x","y","z","{","|","}","~","⌂",
-  "Ç","ü","é","â","ä","à","å","ç","ê","ë","è","ï","î","ì","Ä","Å",
-  "É","æ","Æ","ô","ö","ò","û","ù","ÿ","Ö","Ü","¢","£","¥","₧","ƒ",
-  "á","í","ó","ú","ñ","Ñ","ª","º","¿","⌐","¬","½","¼","¡","«","»",
-  "░","▒","▓","│","┤","╡","╢","╖","╕","╣","║","╗","╝","╜","╛","┐",
-  "└","┴","┬","├","─","┼","╞","╟","╚","╔","╩","╦","╠","═","╬","╧",
-  "╨","╤","╥","╙","╘","╒","╓","╫","╪","┘","┌","█","▄","▌","▐","▀",
-  "α","ß","Γ","π","Σ","σ","µ","τ","Φ","Θ","Ω","δ","∞","φ","ε","∩",
-  "≡","±","≥","≤","⌠","⌡","÷","≈","°","∙","·","√","ⁿ","²","■"," "
-};
-
-
-static char *charmap_graphics[]=
-{
-  " ","!","\"","#","$","%","&","'","(",")","*","+",",","-",".","/","0",
-  "1","2","3","4","5","6","7","8","9",":",";","<","=",">","?",
-  "@","A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P",
-  "Q","R","S","T","U","V","W","X","Y","Z","[","\\","]","^","_",
-  "◆","▒","␉","␌","␍","␊","°","±","␤","␋","┘","┐","┌","└","┼","⎺","⎻",
-  "─","⎼","⎽","├","┤","┴","┬","│","≤","≥","π","≠","£","·"," "
-};
-
-static char *charmap_uk[]=
-{
-  " ","!","\"","£","$","%","&","'","(",")","*","+",",","-",".","/","0",
-  "1","2","3","4","5","6","7","8","9",":",";","<","=",">","?",
-  "@","A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P",
-  "Q","R","S","T","U","V","W","X","Y","Z","[","\\","]","^","_",
-  "`","a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p",
-  "q","r","s","t","u","v","w","x","y","z","{","|","}","~"," "
-};
-
-static char *charmap_ascii[]=
-{
-  " ","!","\"","#","$","%","&","'","(",")","*","+",",","-",".","/","0",
-  "1","2","3","4","5","6","7","8","9",":",";","<","=",">","?",
-  "@","A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P",
-  "Q","R","S","T","U","V","W","X","Y","Z","[","\\","]","^","_",
-  "`","a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p",
-  "q","r","s","t","u","v","w","x","y","z","{","|","}","~"," "
-};
-
-static void vtcmd_justify (VT *vt, const char *sequence)
-{
-  int n = parse_int (vt->argument_buf, 0);
-  switch (n)
-    {
-      case 0:
-      case 1:
-      case 2:
-      case 3:
-      case 4:
-      case 5:
-      case 6:
-      case 7:
-      case 8:
-        vt->justify = n;
-        break;
-      default:
-        vt->justify = 0;
+        ctx_fragment_image_rgba8_RGBA8_bi (rasterizer, x, y, out, count, dx, dy);
     }
-}
-
-static void vtcmd_sixel_related_req (VT *vt, const char *sequence)
-{
-  fprintf (stderr, "it happens!\n");
-}
-
-static void vtcmd_set_charmap (VT *vt, const char *sequence)
-{
-  int slot = 0;
-  int set = sequence[1];
-  if (sequence[0] == ')') { slot = 1; }
-  if (set == 'G') { set = 'B'; }
-  vt->charset[slot] = set;
-}
-#if 0
-
-CSI Pm ' }    '
-Insert Ps Column (s) (default = 1) (DECIC), VT420 and up.
-
-CSI Pm ' ~    '
-Delete Ps Column (s) (default = 1) (DECDC), VT420 and up.
-
-
-in text.  When bracketed paste mode is set, the program will receive:
-ESC [ 2 0 0 ~,
-      followed by the pasted text, followed by
-      ESC [ 2 0 1 ~ .
-
-
-            CSI I
-            when the terminal gains focus, and CSI O  when it loses focus.
-#endif
-
-#define COMPAT_FLAG_LEVEL_0   (1<<1)
-#define COMPAT_FLAG_LEVEL_1   (1<<2)
-#define COMPAT_FLAG_LEVEL_2   (1<<3)
-#define COMPAT_FLAG_LEVEL_3   (1<<4)
-#define COMPAT_FLAG_LEVEL_4   (1<<5)
-#define COMPAT_FLAG_LEVEL_5   (1<<6)
-
-#define COMPAT_FLAG_LEVEL_102 (1<<7)
-
-#define COMPAT_FLAG_ANSI      (1<<8)
-#define COMPAT_FLAG_OBSOLETE  (1<<9)
-
-#define COMPAT_FLAG_ANSI_COLOR (1<<10)
-#define COMPAT_FLAG_256_COLOR  (1<<11)
-#define COMPAT_FLAG_24_COLOR   (1<<12)
-
-#define COMPAT_FLAG_MOUSE_REPORT (1<<13)
-
-#define COMPAT_FLAG_AUDIO      (1<<14)
-#define COMPAT_FLAG_GRAPHICS   (1<<15)
-
-#define ANSI    COMPAT_FLAG_ANSI
-#define OBS     COMPAT_FLAG_OBSOLETE
-
-#define VT100   (COMPAT_FLAG_LEVEL_0|COMPAT_FLAG_LEVEL_1)
-#define VT102   (VT100|COMPAT_FLAG_LEVEL_102)
-#define VT200   (VT102|COMPAT_FLAG_LEVEL_2)
-#define VT300   (VT200|COMPAT_FLAG_LEVEL_3)
-#define VT400   (VT300|COMPAT_FLAG_LEVEL_4)
-#define VT220   VT200
-#define VT320   VT300
-
-#define XTERM   (VT400|COMPAT_FLAG_24_COLOR|COMPAT_FLAG_256_COLOR|COMPAT_FLAG_ANSI_COLOR)
-
-#define VT2020  (XTERM|COMPAT_FLAG_GRAPHICS|COMPAT_FLAG_AUDIO)
-
-
-            static Sequence sequences[]=
+  }
+  else
   {
-    /*
-      prefix suffix  command */
-    //{"B",  0,  vtcmd_break_permitted},
-    //{"C",  0,  vtcmd_nobreak_here},
-    {"D", 0,    vtcmd_index, VT100}, /* args: id:IND Index  */
-    {"E",  0,   vtcmd_next_line}, /* ref:none id:  Next line */
-    {"_", 'G',  vtcmd_graphics},
-    {"H",   0,  vtcmd_horizontal_tab_set, VT100}, /* id:HTS Horizontal Tab Set */
-
-    //{"I",  0,  vtcmd_char_tabulation_with_justification},
-    //{"K",  0,  PLD partial line down
-    //{"L",  0,  PLU partial line up
-    {"M",  0,   vtcmd_reverse_index, VT100}, /* ref:none id:RI Reverse Index */
-    //{"N",  0,  vtcmd_ignore}, /* Set Single Shift 2 - SS2*/
-    //{"O",  0,  vtcmd_ignore}, /* Set Single Shift 3 - SS3*/
-
-#if 0
-    {"[0F", 0, vtcmd_justify, ANSI}, /* ref:none id:JFY disable justification and wordwrap  */ // needs 
special link to ANSI standard
-    {"[1F", 0, vtcmd_justify, ANSI}, /* ref:none id:JFY enable wordwrap  */
-#endif
-
-    /* these need to occur before vtcmd_preceding_line to have precedence */
-    {"[0 F", 0, vtcmd_justify, ANSI},
-    {"[1 F", 0, vtcmd_justify, ANSI},
-    {"[2 F", 0, vtcmd_justify},
-    {"[3 F", 0, vtcmd_justify},
-    {"[4 F", 0, vtcmd_justify},
-    {"[5 F", 0, vtcmd_justify},
-    {"[6 F", 0, vtcmd_justify},
-    {"[7 F", 0, vtcmd_justify},
-    {"[8 F", 0, vtcmd_justify},
-// XXX missing DECIC DECDC  insert and delete column
-    {"[", 'A', vtcmd_cursor_up, VT100},   /* args:Pn    id:CUU Cursor Up */
-    {"[",  'B', vtcmd_cursor_down, VT100}, /* args:Pn    id:CUD Cursor Down */
-    {"[",  'C', vtcmd_cursor_forward, VT100}, /* args:Pn id:CUF Cursor Forward */
-    {"[",  'D', vtcmd_cursor_backward, VT100}, /* args:Pn id:CUB Cursor Backward */
-    {"[",  'j', vtcmd_cursor_backward, ANSI}, /* args:Pn ref:none id:HPB Horizontal Position Backward */
-    {"[",  'k', vtcmd_cursor_up, ANSI}, /* args:Pn ref:none id:VPB Vertical Position Backward */
-    {"[",  'E', vtcmd_next_line, VT100}, /* args:Pn id:CNL Cursor Next Line */
-    {"[",  'F', vtcmd_cursor_preceding_line, VT100}, /* args:Pn id:CPL Cursor Preceding Line */
-    {"[",  'G', vtcmd_horizontal_position_absolute}, /* args:Pn id:CHA Cursor Horizontal Absolute */
-    {"[",  'H', vtcmd_cursor_position, VT100}, /* args:Pl;Pc id:CUP Cursor Position */
-    {"[",  'I', vtcmd_insert_n_tabs}, /* args:Pn id:CHT Cursor Horizontal Forward Tabulation */
-    {"[",  'J', vtcmd_erase_in_display, VT100}, /* args:Ps id:ED Erase in Display */
-    {"[",  'K', vtcmd_erase_in_line, VT100}, /* args:Ps id:EL Erase in Line */
-    {"[",  'L', vtcmd_insert_blank_lines, VT102}, /* args:Pn id:IL Insert Line */
-    {"[",  'M', vtcmd_delete_n_lines, VT102}, /* args:Pn id:DL Delete Line   */
-    // [ N is EA - Erase in field
-    // [ O is EA - Erase in area
-    {"[",  'P', vtcmd_delete_n_chars, VT102}, /* args:Pn id:DCH Delete Character */
-    // [ Q is SEE - Set editing extent
-    // [ R is CPR - active cursor position report
-    {"[?", 'S', vtcmd_sixel_related_req},
-    {"[",  'S', vtcmd_scroll_up, VT100},   /* args:Pn id:SU Scroll Up */
-    {"[",  'T', vtcmd_scroll_down, VT100}, /* args:Pn id:SD Scroll Down */
-    {"[",/*SP*/'U', vtcmd_set_line_home, ANSI}, /* args:PnSP id=SLH Set Line Home */
-    {"[",/*SP*/'V', vtcmd_set_line_limit, ANSI},/* args:PnSP id=SLL Set Line Limit */
-    // [ W is cursor tabulation control
-    // [ Pn Y  - cursor line tabulation
-    //
-    {"[",  'X', vtcmd_erase_n_chars}, /* args:Pn id:ECH Erase Character */
-    {"[",  'Z', vtcmd_rev_n_tabs},    /* args:Pn id:CBT Cursor Backward Tabulation */
-    {"[",  '^', vtcmd_scroll_down}  , /* muphry alternate from ECMA */
-    {"[",  '@', vtcmd_insert_character, VT102}, /* args:Pn id:ICH Insert Character */
-
-    {"[",  'a', vtcmd_cursor_forward, ANSI}, /* args:Pn id:HPR Horizontal Position Relative */
-    {"[",  'b', vtcmd_cursor_forward, ANSI}, /* REP previous char XXX incomplete */
-    {"[",  'c', vtcmd_report}, /* ref:none id:DA args:... Device Attributes */
-    {"[",  'd', vtcmd_goto_row},       /* args:Pn id:VPA Vertical Position Absolute  */
-    {"[",  'e', vtcmd_cursor_down},    /* args:Pn id:VPR Vertical Position Relative */
-    {"[",  'f', vtcmd_cursor_position, VT100}, /* args:Pl;Pc id:HVP Cursor Position */
-    {"[g", 0,   vtcmd_clear_current_tab, VT100}, /* id:TBC clear current tab */
-    {"[0g", 0,  vtcmd_clear_current_tab, VT100}, /* id:TBC clear current tab */
-    {"[3g", 0,  vtcmd_clear_all_tabs, VT100},    /* id:TBC clear all tabs */
-    {"[",  'm', vtcmd_set_graphics_rendition, VT100}, /* args:Ps;Ps;.. id:SGR Select Graphics Rendition */
-    {"[",  'n', vtcmd_report, VT200}, /* id:DSR args:... CPR Cursor Position Report  */
-    {"[",  'r', vtcmd_set_top_and_bottom_margins, VT100}, /* args:Pt;Pb id:DECSTBM Set Top and Bottom 
Margins */
-#if 0
-    // handled by set_left_and_right_margins - in if 0 to be documented
-    {"[s",  0,  vtcmd_save_cursor_position, VT100}, /*ref:none id:SCP Save Cursor Position */
-#endif
-    {"[u",  0,  vtcmd_restore_cursor_position, VT100}, /*ref:none id:RCP Restore Cursor Position */
-    {"[",  's', vtcmd_set_left_and_right_margins, VT400}, /* args:Pl;Pr id:DECSLRM Set Left and Right 
Margins */
-    {"[",  '`', vtcmd_horizontal_position_absolute, ANSI},  /* args:Pn id:HPA Horizontal Position Absolute */
-
-    {"[",  'h', vtcmd_set_mode, VT100},   /* args:Pn[;...] id:SM Set Mode */
-    {"[",  'l', vtcmd_set_mode, VT100}, /* args:Pn[;...]  id:RM Reset Mode */
-    {"[",  't', vtcmd_set_t},
-    {"[",  'q', vtcmd_set_led, VT100}, /* args:Ps id:DECLL Load LEDs */
-    {"[",  'x', vtcmd_report}, /* ref:none id:DECREQTPARM */
-    {"[",  'z', vtcmd_DECELR}, /* ref:none id:DECELR set locator res  */
-
-    {"5",   0,  vtcmd_char_at_cursor, VT300}, /* ref:none id:DECXMIT */
-    {"6",   0,  vtcmd_back_index, VT400}, /* id:DECBI Back index (hor. scroll) */
-    {"7",   0,  vtcmd_save_cursor, VT100}, /* id:DECSC Save Cursor */
-    {"8",   0,  vtcmd_restore_cursor, VT100}, /* id:DECRC Restore Cursor */
-    {"9",   0,  vtcmd_forward_index, VT400}, /* id:DECFI Forward index (hor. scroll)*/
-
-    //{"Z", 0,  vtcmd_device_attributes},
-    //{"%G",0,  vtcmd_set_default_font}, // set_alternate_font
-
-
-    {"(0",  0,   vtcmd_set_charmap},
-    {"(1",  0,   vtcmd_set_charmap},
-    {"(2",  0,   vtcmd_set_charmap},
-    {"(A",  0,   vtcmd_set_charmap},
-    {"(B",  0,   vtcmd_set_charmap},
-    {")0",  0,   vtcmd_set_charmap},
-    {")1",  0,   vtcmd_set_charmap},
-    {")2",  0,   vtcmd_set_charmap},
-    {")A",  0,   vtcmd_set_charmap},
-    {")B",  0,   vtcmd_set_charmap},
-    {"%G",  0,   vtcmd_set_charmap},
-
-    {"#3",  0,   vtcmd_set_double_width_double_height_top_line, VT100}, /*id:DECDHL Top half of 
double-width, double-height line */
-    {"#4",  0,   vtcmd_set_double_width_double_height_bottom_line, VT100}, /*id:DECDHL Bottom half of 
double-width, double-height line */
-    {"#5",  0,   vtcmd_set_single_width_single_height_line, VT100}, /* id:DECSWL Single-width line */
-    {"#6",  0,   vtcmd_set_double_width_single_height_line, VT100}, /* id:DECDWL Double-width line */
-
-    {"#8",  0,   vtcmd_screen_alignment_display, VT100}, /* id:DECALN Screen Alignment Pattern */
-    {"=",   0,   vtcmd_ignore},  // keypad mode change
-    {">",   0,   vtcmd_ignore},  // keypad mode change
-    {"c",   0,   vtcmd_reset_to_initial_state, VT100}, /* id:RIS Reset to Initial State */
-    {"[!", 'p',  vtcmd_ignore},       // soft reset?
-    {"[",  'p',  vtcmd_request_mode}, /* args:Pa$ id:DECRQM Request ANSI Mode */
-#if 0
-    {"[?",  'p',  vtcmd_request_mode}, /* args:Pd$ id:DECRQM Request DEC Mode */
-#endif
-
-    {NULL, 0, NULL}
-  };
-
-  static void handle_sequence (VT *vt, const char *sequence)
-{
-  int i0 = strlen (sequence)-1;
-  int i;
-  vt->rev ++;
-  for (i = 0; sequences[i].prefix; i++)
-    {
-      if (!strncmp (sequence, sequences[i].prefix, strlen (sequences[i].prefix) ) )
-        {
-          if (! (sequences[i].suffix && (sequence[i0] != sequences[i].suffix) ) )
-            {
-              VT_command ("%s", sequence);
-              sequences[i].vtcmd (vt, sequence);
-              return;
-            }
-        }
-    }
-#ifndef ASANBUILD
-  VT_warning ("unhandled: %c%c%c%c%c%c%c%c%c\n", sequence[0], sequence[1], sequence[2], sequence[3], 
sequence[4], sequence[5], sequence[6], sequence[7], sequence[8]);
+    if (rasterizer->swap_red_green)
+      ctx_fragment_image_rgba8_RGBA8_nearest_swap_red_green (rasterizer, x, y, out, count, dx, dy);
+    else
+      ctx_fragment_image_rgba8_RGBA8_nearest (rasterizer, x, y, out, count, dx, dy);
+  }
+  //ctx_fragment_swap_red_green_u8 (out, count);
+#if CTX_DITHER
+  uint8_t *rgba = (uint8_t*)out;
+  ctx_dither_rgba_u8 (rgba, x, y, rasterizer->format->dither_red_blue,
+                      rasterizer->format->dither_green);
 #endif
 }
 
-static void vt_line_feed (VT *vt)
+static void
+ctx_fragment_image_gray1_RGBA8 (CtxRasterizer *rasterizer, float x, float y, void *out, int count, float dx, 
float dy)
 {
-  int was_home = vt->at_line_home;
-  if (vt->margin_top == 1 && vt->margin_bottom == vt->rows)
+  uint8_t *rgba = (uint8_t *) out;
+  CtxSource *g = &rasterizer->state->gstate.source_fill;
+  CtxBuffer *buffer = g->texture.buffer;
+  ctx_assert (rasterizer);
+  ctx_assert (g);
+  ctx_assert (buffer);
+  for (int i = 0; i < count; i ++)
+  {
+  int u = x - g->texture.x0;
+  int v = y - g->texture.y0;
+  if ( u < 0 || v < 0 ||
+       u >= buffer->width ||
+       v >= buffer->height)
     {
-      if (vt->lines == NULL ||
-          (vt->lines->data == vt->current_line && vt->cursor_y != vt->rows) )
-        {
-          vt->current_line = vt_line_new_with_size ("", vt->cols);
-          ctx_list_prepend (&vt->lines, vt->current_line);
-          vt->line_count++;
-        }
-      if (vt->cursor_y >= vt->margin_bottom)
-        {
-          vt->cursor_y = vt->margin_bottom;
-          vt_scroll (vt, -1);
-        }
-      else
-        {
-          vt->cursor_y++;
-        }
+      rgba[0] = rgba[1] = rgba[2] = rgba[3] = 0;
     }
   else
     {
-      if (vt->lines->data == vt->current_line &&
-          (vt->cursor_y != vt->margin_bottom) && 0)
+      uint8_t *src = (uint8_t *) buffer->data;
+      src += v * buffer->stride + u / 8;
+      if (*src & (1<< (u & 7) ) )
         {
-          vt->current_line = vt_line_new_with_size ("", vt->cols);
-          ctx_list_prepend (&vt->lines, vt->current_line);
-          vt->line_count++;
+          rgba[0] = rgba[1] = rgba[2] = rgba[3] = 0;
         }
-      vt->cursor_y++;
-      if (vt->cursor_y > vt->margin_bottom)
+      else
         {
-          vt->cursor_y = vt->margin_bottom;
-          vt_scroll (vt, -1);
+          for (int c = 0; c < 4; c++)
+            { rgba[c] = 255;
+            }//g->texture.rgba[c];
+            //}
         }
     }
-  _vt_move_to (vt, vt->cursor_y, vt->cursor_x);
-  if (vt->cr_on_lf)
-    { vt_carriage_return (vt); }
-  vt_trimlines (vt, vt->rows);
-  if (was_home)
-    { vt_carriage_return (vt); }
-}
 
-//#include "vt-encodings.h"
-
-#ifndef NO_SDL
-static void vt_state_apc_audio (VT *vt, int byte)
-{
-  if ( (byte < 32) && ( (byte < 8) || (byte > 13) ) )
-    {
-      vt_audio (vt, vt->argument_buf);
-      vt->state = ( (byte == 27) ?  vt_state_swallow : vt_state_neutral);
-    }
-  else
-    {
-      vt_argument_buf_add (vt, byte);
-    }
+    rgba += 4;
+    x += dx;
+    y += dy;
+  }
 }
 
-#else
-
-void vt_audio_task (VT *vt, int click)
+#if CTX_GRADIENTS
+static void
+ctx_fragment_radial_gradient_RGBA8 (CtxRasterizer *rasterizer, float x, float y, void *out, int count, float 
dx, float dy)
 {
+  uint8_t *rgba = (uint8_t *) out;
+  CtxSource *g = &rasterizer->state->gstate.source_fill;
+  for (int i = 0; i <  count; i ++)
+  {
+    float v = (ctx_hypotf (g->radial_gradient.x0 - x, g->radial_gradient.y0 - y) -
+              g->radial_gradient.r0) * (g->radial_gradient.rdelta);
+#if CTX_GRADIENT_CACHE
+    uint32_t *rgbap = (uint32_t*)&ctx_gradient_cache_u8[ctx_grad_index(v)][0];
+    *((uint32_t*)rgba) = *rgbap;
+#else
+    ctx_fragment_gradient_1d_RGBA8 (rasterizer, v, 0.0, rgba);
+#endif
+#if CTX_DITHER
+    ctx_dither_rgba_u8 (rgba, x, y, rasterizer->format->dither_red_blue,
+                        rasterizer->format->dither_green);
+#endif
+    rgba += 4;
+    x += dx;
+    y += dy;
+  }
 }
 
-void vt_bell (VT *vt)
-{
-}
-static void vt_state_apc_audio (VT *vt, int byte)
+static void
+ctx_fragment_linear_gradient_RGBA8 (CtxRasterizer *rasterizer, float x, float y, void *out, int count, float 
dx, float dy)
 {
-  vt->state = vt_state_apc_generic;
+  uint8_t *rgba = (uint8_t *) out;
+  CtxSource *g = &rasterizer->state->gstate.source_fill;
+  for (int i = 0; i <  count; i ++)
+  {
+  float v = ( ( (g->linear_gradient.dx * x + g->linear_gradient.dy * y) /
+                g->linear_gradient.length) -
+              g->linear_gradient.start) * (g->linear_gradient.rdelta);
+#if CTX_GRADIENT_CACHE
+  uint32_t*rgbap = ((uint32_t*)(&ctx_gradient_cache_u8[ctx_grad_index(v)][0]));
+  *((uint32_t*)rgba) = *rgbap;
+#else
+  _ctx_fragment_gradient_1d_RGBA8 (rasterizer, v, 1.0, rgba);
+#endif
+#if CTX_DITHER
+  ctx_dither_rgba_u8 (rgba, x, y, rasterizer->format->dither_red_blue,
+                      rasterizer->format->dither_green);
+#endif
+    rgba += 4;
+    x += dx;
+    y += dy;
+  }
 }
 
 #endif
 
 static void
-vt_carriage_return (VT *vt)
+ctx_fragment_color_RGBA8 (CtxRasterizer *rasterizer, float x, float y, void *out, int count, float dx, float 
dy)
 {
-  _vt_move_to (vt, vt->cursor_y, vt->cursor_x);
-  vt->cursor_x = VT_MARGIN_LEFT;
-  vt->at_line_home = 1;
-}
+  uint8_t  rgba[4];
+  uint8_t *rgba_out = (uint8_t *) out;
+  CtxSource *g = &rasterizer->state->gstate.source_fill;
+  ctx_color_get_rgba8 (rasterizer->state, &g->color, rgba);
 
-/* if the byte is a non-print control character, handle it and return 1
- * oterhwise return 0*/
-static int _vt_handle_control (VT *vt, int byte)
-{
-  /* the big difference between ANSI-BBS mode and VT100+ mode is that
-   * most C0 characters are printable
-   */
-  if (CTX_UNLIKELY(vt->encoding == 1)) // this codepage is for ansi-bbs use
-    switch (byte)
-      {
-        case '\0':
-          return 1;
-        case 1:    /* SOH start of heading */
-        case 2:    /* STX start of text */
-        case 3:    /* ETX end of text */
-        case 4:    /* EOT end of transmission */
-        case 5:    /* ENQuiry */
-        case 6:    /* ACKnolwedge */
-        case '\v': /* VT vertical tab */
-        case '\f': /* VF form feed */
-        case 14: /* SO shift in - alternate charset */
-        case 15: /* SI shift out - (back to normal) */
-        case 16: /* DLE data link escape */
-        case 17: /* DC1 device control 1 - XON */
-        case 18: /* DC2 device control 2 */
-        case 19: /* DC3 device control 3 - XOFF */
-        case 20: /* DC4 device control 4 */
-        case 21: /* NAK negative ack */
-        case 22: /* SYNchronous idle */
-        case 23: /* ETB end of trans. blk */
-        case 24: /* CANcel (vt100 aborts sequence) */
-        case 25: /* EM  end of medium */
-        case 26: /* SUB stitute */
-        case 28: /* FS file separator */
-        case 29: /* GS group separator */
-        case 30: /* RS record separator */
-        case 31: /* US unit separator */
-          _vt_add_str (vt, charmap_cp437[byte]);
-          return 1;
-      }
-  switch (byte)
-    {
-      case '\0':
-      case 1:    /* SOH start of heading */
-      case 2:    /* STX start of text */
-      case 3:    /* ETX end of text */
-      case 4:    /* EOT end of transmission */
-      case 6:    /* ACKnolwedge */
-        return 1;
-      case 5:    /* ENQuiry */
-        {
-          const char *reply = getenv ("TERM_ENQ_REPLY");
-          if (reply)
-            {
-              char *copy = strdup (reply);
-              for (uint8_t *c = (uint8_t *) copy; *c; c++)
-                {
-                  if (*c < ' ' || * c > 127) { *c = 0; }
-                }
-              vt_write (vt, reply, strlen (reply) );
-              free (copy);
-            }
-        }
-        return 1;
-      case '\a': /* BELl */
-        vt_bell (vt);
-        return 1;
-      case '\b': /* BS */
-        _vt_backspace (vt);
-        return 1;
-      case '\t': /* HT tab */
-        _vt_htab (vt);
-        return 1;
-      case '\v': /* VT vertical tab */
-      case '\f': /* VF form feed */
-      case '\n': /* LF line ffed */
-        vt_line_feed (vt);
-        return 1;
-      case '\r': /* CR carriage return */
-        vt_carriage_return (vt);
-        return 1;
-      case 14: /* SO shift in - alternate charset */
-        vt->shifted_in = 1;  // XXX not in vt52
-        return 1;
-      case 15: /* SI shift out - (back to normal) */
-        vt->shifted_in = 0;
-        return 1;
-      case 16: /* DLE data link escape */
-      case 17: /* DC1 device control 1 - XON */
-      case 18: /* DC2 device control 2 */
-      case 19: /* DC3 device control 3 - XOFF */
-      case 20: /* DC4 device control 4 */
-      case 21: /* NAK negative ack */
-      case 22: /* SYNchronous idle */
-      case 23: /* ETB end of trans. blk */
-      case 24: /* CANcel (vt100 aborts sequence) */
-      case 25: /* EM  end of medium */
-      case 26: /* SUB stitute */
-        _vt_add_str (vt, "¿");  // in vt52? XXX
-        return 1;
-      case 27: /* ESCape */
-        return 0;
-        break;
-      case 28: /* FS file separator */
-      case 29: /* GS group separator */
-      case 30: /* RS record separator */
-      case 31: /* US unit separator */
-      case 127: /* DEL */
-        return 1;
-      default:
-        return 0;
-    }
+  if (rasterizer->swap_red_green)
+  {
+    int tmp = rgba[0];
+    rgba[0] = rgba[2];
+    rgba[2] = tmp;
+  }
+  for (int i = 0; i < count; i++, rgba_out+=4)
+    memcpy (rgba_out, rgba, 4);
 }
+#if CTX_ENABLE_FLOAT
 
-void vt_open_log (VT *vt, const char *path)
+#if CTX_GRADIENTS
+static void
+ctx_fragment_linear_gradient_RGBAF (CtxRasterizer *rasterizer, float x, float y, void *out, int count, float 
dx, float dy)
 {
-  unlink (path);
-  vt->log = fopen (path, "w");
+  float *rgba = (float *) out;
+  CtxSource *g = &rasterizer->state->gstate.source_fill;
+  for (int i = 0; i < count; i++)
+  {
+    float v = ( ( (g->linear_gradient.dx * x + g->linear_gradient.dy * y) /
+                  g->linear_gradient.length) -
+                g->linear_gradient.start) * (g->linear_gradient.rdelta);
+    ctx_fragment_gradient_1d_RGBAF (rasterizer, v, 1.0f, rgba);
+    x += dx;
+    y += dy;
+    rgba += 4;
+  }
 }
 
-/* the function shared by sixels, kitty mode and iterm2 mode for
- * doing inline images. it attaches an image to the current line
- */
-static void display_image (VT *vt, Image *image,
-                           int col,
-                           float xoffset,
-                           float yoffset,
-                           int rows,
-                           int cols,
-                           int subx,
-                           int suby,
-                           int subw,
-                           int subh
-                          )
+static void
+ctx_fragment_radial_gradient_RGBAF (CtxRasterizer *rasterizer, float x, float y, void *out, int count, float 
dx, float dy)
 {
-  int i = 0;
-  for (i = 0; vt->current_line->images[i] && i < 4; i++)
+  float *rgba = (float *) out;
+  CtxSource *g = &rasterizer->state->gstate.source_fill;
+  for (int i = 0; i < count; i++)
   {
-     if (vt->current_line->image_col[i] == vt->cursor_x)
-       break;
+  float v = ctx_hypotf (g->radial_gradient.x0 - x, g->radial_gradient.y0 - y);
+        v = (v - g->radial_gradient.r0) * (g->radial_gradient.rdelta);
+  ctx_fragment_gradient_1d_RGBAF (rasterizer, v, 0.0f, rgba);
+    x+=dx;
+    y+=dy;
+    rgba +=4;
   }
-  //for (i = 0; vt->current_line->images[i] && i < 4; i++);
-  if (i >= 4) { i = 3; }
-  /* this needs a struct and dynamic allocation */
-  vt->current_line->images[i] = image;
-  vt->current_line->image_col[i] = vt->cursor_x;
-  vt->current_line->image_X[i] = xoffset;
-  vt->current_line->image_Y[i] = yoffset;
-  vt->current_line->image_subx[i] = subx;
-  vt->current_line->image_suby[i] = suby;
-  vt->current_line->image_subw[i] = subw;
-  vt->current_line->image_subh[i] = subh;
-  vt->current_line->image_rows[i] = rows;
-  vt->current_line->image_cols[i] = cols;
-}
-
-static int vt_gfx_pending=0;
-
-void vt_gfx (VT *vt, const char *command)
-{
-  const char *payload = NULL;
-  char key = 0;
-  int  value;
-  int  pos = 1;
-  if (vt->gfx.multichunk == 0)
-    {
-      memset (&vt->gfx, 0, sizeof (GfxState) );
-      vt->gfx.action='t';
-      vt->gfx.transmission='d';
-    }
-  while (command[pos] != ';')
-    {
-      pos ++; // G or ,
-      if (command[pos] == ';') { break; }
-      key = command[pos];
-      pos++;
-      if (command[pos] == ';') { break; }
-      pos ++; // =
-      if (command[pos] == ';') { break; }
-      if (command[pos] >= '0' && command[pos] <= '9')
-        { value = atoi (&command[pos]); }
-      else
-        { value = command[pos]; }
-      while (command[pos] &&
-             command[pos] != ',' &&
-             command[pos] != ';') { pos++; }
-      switch (key)
-        {
-          case 'a':
-            vt->gfx.action = value;
-            break;
-          case 'd':
-            vt->gfx.delete = value;
-            break;
-          case 'i':
-            vt->gfx.id = value;
-            break;
-          case 'S':
-            vt->gfx.buf_size = value;
-            break;
-          case 's':
-            vt->gfx.buf_width = value;
-            break;
-          case 'v':
-            vt->gfx.buf_height = value;
-            break;
-          case 'f':
-            vt->gfx.format = value;
-            break;
-          case 'm':
-            vt->gfx.multichunk = value;
-            break;
-          case 'o':
-            vt->gfx.compression = value;
-            break;
-          case 't':
-            vt->gfx.transmission = value;
-            break;
-          case 'x':
-            vt->gfx.x = value;
-            break;
-          case 'y':
-            vt->gfx.y = value;
-            break;
-          case 'w':
-            vt->gfx.w = value;
-            break;
-          case 'h':
-            vt->gfx.h = value;
-            break;
-          case 'X':
-            vt->gfx.x_cell_offset = value;
-            break;
-          case 'Y':
-            vt->gfx.y_cell_offset = value;
-            break;
-          case 'c':
-            vt->gfx.columns = value;
-            break;
-          case 'r':
-            vt->gfx.rows = value;
-            break;
-          case 'z':
-            vt->gfx.z_index = value;
-            break;
-        }
-    }
-  payload = &command[pos+1];
+}
+#endif
+
+
+static void
+ctx_fragment_color_RGBAF (CtxRasterizer *rasterizer, float x, float y, void *out, int count, float dx, float 
dy)
+{
+  float *rgba = (float *) out;
+  for (int i = 0; i < count; i++)
   {
-    int chunk_size = strlen (payload);
-    int old_size = vt->gfx.data_size;
-    // accumulate incoming data
-    if (vt->gfx.data == NULL)
-      {
-        vt->gfx.data_size = chunk_size;
-        vt->gfx.data = malloc (vt->gfx.data_size + 1);
-      }
-    else
-      {
-        vt->gfx.data_size += chunk_size;
-        vt->gfx.data = realloc (vt->gfx.data, vt->gfx.data_size + 1);
-      }
-    memcpy (vt->gfx.data + old_size, payload, chunk_size);
-    vt->gfx.data[vt->gfx.data_size]=0;
+    CtxSource *g = &rasterizer->state->gstate.source_fill;
+    ctx_color_get_rgba (rasterizer->state, &g->color, rgba);
+    rgba += 4;
   }
-  if (vt->gfx.multichunk == 0)
+}
+
+static void ctx_fragment_image_RGBAF (CtxRasterizer *rasterizer, float x, float y, void *out, int count, 
float dx, float dy)
+{
+  float *outf = (float *) out;
+  uint8_t rgba[4];
+  CtxGState *gstate = &rasterizer->state->gstate;
+  CtxBuffer *buffer = gstate->source_fill.texture.buffer;
+  switch (buffer->format->bpp)
     {
-      if (vt->gfx.transmission != 'd') /* */
-        {
-          char buf[256];
-          sprintf (buf, "\033_Gi=%i;only direct transmission supported\033\\",
-                   vt->gfx.id);
-          vt_write (vt, buf, strlen (buf) );
-          goto cleanup;
-        }
-      {
-        int bin_length = vt->gfx.data_size;
-        uint8_t *data2 = malloc (vt->gfx.data_size);
-        bin_length = ctx_base642bin ( (char *) vt->gfx.data,
-                                     &bin_length,
-                                     data2);
-        memcpy (vt->gfx.data, data2, bin_length + 1);
-        vt->gfx.data_size = bin_length;
-        free (data2);
-      }
-      if (vt->gfx.buf_width)
-        {
-          // implicit buf_size
-          vt->gfx.buf_size = vt->gfx.buf_width * vt->gfx.buf_height *
-                             (vt->gfx.format == 24 ? 3 : 4);
-        }
-      if (vt->gfx.compression == 'z')
-        {
-          //vt->gfx.buf_size)
-          unsigned char *data2 = malloc (vt->gfx.buf_size + 1);
-          /* if a buf size is set (rather compression, but
-           * this works first..) then */
-          unsigned long actual_uncompressed_size = vt->gfx.buf_size;
-          int z_result = uncompress (data2, &actual_uncompressed_size,
-                                     vt->gfx.data,
-                                     vt->gfx.data_size);
-          if (z_result != Z_OK)
-            {
-              char buf[256]= "\033_Go=z;zlib error\033\\";
-              vt_write (vt, buf, strlen (buf) );
-              goto cleanup;
-            }
-          free (vt->gfx.data);
-          vt->gfx.data = data2;
-          vt->gfx.data_size = actual_uncompressed_size;
-          vt->gfx.compression = 0;
-        }
-      if (vt->gfx.format == 100)
-        {
-          int channels;
-          uint8_t *new_data = stbi_load_from_memory (vt->gfx.data, vt->gfx.data_size, &vt->gfx.buf_width, 
&vt->gfx.buf_height, &channels, 4);
-          if (!new_data)
-            {
-              char buf[256]= "\033_Gf=100;image decode error\033\\";
-              vt_write (vt, buf, strlen (buf) );
-              goto cleanup;
-            }
-          vt->gfx.format = 32;
-          free (vt->gfx.data);
-          vt->gfx.data = new_data;
-          vt->gfx.data_size= vt->gfx.buf_width * vt->gfx.buf_height * 4;
-        }
-      Image *image = NULL;
-      switch (vt->gfx.action)
-        {
-          case 't': // transfer
-          case 'T': // transfer and present
-            switch (vt->gfx.format)
-              {
-                case 24:
-                case 32:
-                  image = image_add (vt->gfx.buf_width, vt->gfx.buf_height, vt->gfx.id,
-                                     vt->gfx.format, vt->gfx.data_size, vt->gfx.data);
-                  vt->gfx.data = NULL;
-                  vt->gfx.data_size=0;
-                  break;
-              }
-            if (vt->gfx.action == 't')
-              { break; }
-          // fallthrough
-          case 'p': // present
-            if (!image && vt->gfx.id)
-              { image = image_query (vt->gfx.id); }
-            if (image)
-              {
-                display_image (vt, image, vt->cursor_x, vt->gfx.rows, vt->gfx.columns,
-                               vt->gfx.x_cell_offset * 1.0 / vt->cw,
-                               vt->gfx.y_cell_offset * 1.0 / vt->ch,
-                               vt->gfx.x,
-                               vt->gfx.y,
-                               vt->gfx.w,
-                               vt->gfx.h);
-                int right = (image->width + (vt->cw-1) ) /vt->cw;
-                int down = (image->height + (vt->ch-1) ) /vt->ch;
-                for (int i = 0; i<down - 1; i++)
-                  { vtcmd_index (vt, " "); }
-                for (int i = 0; i<right; i++)
-                  { vtcmd_cursor_forward (vt, " "); }
-              }
-            break;
-          case 'q': // query
-            if (image_query (vt->gfx.id) )
-              {
-                char buf[256];
-                sprintf (buf, "\033_Gi=%i;OK\033\\", vt->gfx.id);
-                vt_write (vt, buf, strlen (buf) );
-              }
-            break;
-          case 'd': // delete
-            {
-              int row = vt->rows; // probably not right at start of session XXX
-              for (CtxList *l = vt->lines; l; l = l->next, row --)
-                {
-                  VtLine *line = l->data;
-                  for (int i = 0; i < 4; i ++)
-                    {
-                      int free_resource = 0;
-                      int match = 0;
-                      if (line->images[i])
-                        switch (vt->gfx.delete)
-                          {
-                            case 'A':
-                              free_resource = 1;
-                              /* FALLTHROUGH */
-                            case 'a': /* all images visible on screen */
-                              match = 1;
-                              break;
-                            case 'I':
-                              free_resource = 1;
-                              /* FALLTHROUGH */
-                            case 'i': /* all images with specified id */
-                              if ( ( (Image *) (line->images[i]) )->id == vt->gfx.id)
-                                { match = 1; }
-                              break;
-                            case 'P':
-                              free_resource = 1;
-                              /* FALLTHROUGH */
-                            case 'p': /* all images intersecting cell
-          specified with x and y */
-                              if (line->image_col[i] == vt->gfx.x &&
-                                  row == vt->gfx.y)
-                                { match = 1; }
-                              break;
-                            case 'Q':
-                              free_resource = 1;
-                              /* FALLTHROUGH */
-                            case 'q': /* all images with specified cell (x), row(y) and z */
-                              if (line->image_col[i] == vt->gfx.x &&
-                                  row == vt->gfx.y)
-                                { match = 1; }
-                              break;
-                            case 'Y':
-                              free_resource = 1;
-                              /* FALLTHROUGH */
-                            case 'y': /* all images with specified row (y) */
-                              if (row == vt->gfx.y)
-                                { match = 1; }
-                              break;
-                            case 'X':
-                              free_resource = 1;
-                              /* FALLTHROUGH */
-                            case 'x': /* all images with specified column (x) */
-                              if (line->image_col[i] == vt->gfx.x)
-                                { match = 1; }
-                              break;
-                            case 'Z':
-                              free_resource = 1;
-                              /* FALLTHROUGH */
-                            case 'z': /* all images with specified z-index (z) */
-                              break;
-                          }
-                      if (match)
-                        {
-                          line->images[i] = NULL;
-                          if (free_resource)
-                            {
-                              // XXX : NYI
-                            }
-                        }
-                    }
-                }
-            }
-            break;
-        }
-cleanup:
-      if (vt->gfx.data)
-        { free (vt->gfx.data); }
-      vt->gfx.data = NULL;
-      vt->gfx.data_size=0;
-      vt->gfx.multichunk=0;
-      vt_gfx_pending = 0;
+      case 1:  ctx_fragment_image_gray1_RGBA8 (rasterizer, x, y, rgba, count, dx, dy); break;
+      case 24: ctx_fragment_image_rgb8_RGBA8 (rasterizer, x, y, rgba, count, dx, dy);  break;
+      case 32: ctx_fragment_image_rgba8_RGBA8 (rasterizer, x, y, rgba, count, dx, dy); break;
+      default: ctx_fragment_image_RGBA8 (rasterizer, x, y, rgba, count, dx, dy);       break;
     }
-  else
-     vt_gfx_pending = 1;
+  for (int c = 0; c < 4 * count; c ++) { outf[c] = ctx_u8_to_float (rgba[c]); }
 }
 
-static void vt_state_vt52 (VT *vt, int byte)
+static CtxFragment ctx_rasterizer_get_fragment_RGBAF (CtxRasterizer *rasterizer)
 {
-  /* in vt52 mode, utf8_pos being non 0 means we got ESC prior */
-  switch (vt->utf8_pos)
+  CtxGState *gstate = &rasterizer->state->gstate;
+  switch (gstate->source_fill.type)
     {
-      case 0:
-        if (_vt_handle_control (vt, byte) == 0)
-          switch (byte)
-            {
-              case 27: /* ESC */
-                vt->utf8_pos = 1;
-                break;
-              default:
-                {
-                  char str[2] = {byte & 127, 0};
-                  /* we're not validating utf8, and our utf8 manipulation
-                   * functions are not robust against malformed utf8,
-                   * hence we strip to ascii
-                   */
-                  _vt_add_str (vt, str);
-                }
-                break;
-            }
-        break;
-      case 1:
-        vt->utf8_pos = 0;
-        switch (byte)
-          {
-            case 'A':
-              vtcmd_cursor_up (vt, " ");
-              break;
-            case 'B':
-              vtcmd_cursor_down (vt, " ");
-              break;
-            case 'C':
-              vtcmd_cursor_forward (vt, " ");
-              break;
-            case 'D':
-              vtcmd_cursor_backward (vt, " ");
-              break;
-            case 'F':
-              vtcmd_set_alternate_font (vt, " ");
-              break;
-            case 'G':
-              vtcmd_set_default_font (vt, " ");
-              break;
-            case 'H':
-              _vt_move_to (vt, 1, 1);
-              break;
-            case 'I':
-              vtcmd_reverse_index (vt, " ");
-              break;
-            case 'J':
-              vtcmd_erase_in_display (vt, "[0J");
-              break;
-            case 'K':
-              vtcmd_erase_in_line (vt, "[0K");
-              break;
-            case 'Y':
-              vt->utf8_pos = 2;
-              break;
-            case 'Z':
-              vt_write (vt, "\033/Z", 3);
-              break;
-            case '<':
-              vt->state = vt_state_neutral;
-              break;
-            default:
-              break;
-          }
-        break;
-      case 2:
-        _vt_move_to (vt, byte - 31, vt->cursor_x);
-        vt->utf8_pos = 3;
-        break;
-      case 3:
-        _vt_move_to (vt, vt->cursor_y, byte - 31);
-        vt->utf8_pos = 0;
-        break;
+      case CTX_SOURCE_TEXTURE:           return ctx_fragment_image_RGBAF;
+      case CTX_SOURCE_COLOR:           return ctx_fragment_color_RGBAF;
+#if CTX_GRADIENTS
+      case CTX_SOURCE_LINEAR_GRADIENT: return ctx_fragment_linear_gradient_RGBAF;
+      case CTX_SOURCE_RADIAL_GRADIENT: return ctx_fragment_radial_gradient_RGBAF;
+#endif
     }
+  return ctx_fragment_color_RGBAF;
 }
+#endif
 
-static void vt_sixels (VT *vt, const char *sixels)
+static CtxFragment ctx_rasterizer_get_fragment_RGBA8 (CtxRasterizer *rasterizer)
 {
-  uint8_t colors[256][3];
-  int width = 0;
-  int height = 0;
-  int x = 0;
-  int y = 0;
-  Image *image;
-  uint8_t *pixels = NULL;
-  int repeat = 1;
-  const char *p = sixels;
-  int pal_no = 0;
-#if 0
-  for (; *p && *p != ';'; p++);
-  if (*p == ';') { p ++; }
-  printf ("%i:[%c]%i\n", __LINE__, *p, atoi (p) );
-  // should be 0
-  for (; *p && *p != ';'; p++);
-  if (*p == ';') { p ++; }
-  printf ("%i:[%c]%i\n", __LINE__, *p, atoi (p) );
-  // if 1 then transparency is enabled - otherwise use bg color
-  for (; *p && *p != 'q'; p++);
-#endif
-  //for (; *p && *p != '"'; p++);
-  while (*p && *p != 'q') { p++; }
-  if (*p == 'q') { p++; }
-  if (*p == '"') { p++; }
-  //printf ("%i:[%c]%i\n", __LINE__, *p, atoi (p));
-  for (; *p && *p != ';'; p++);
-  if (*p == ';') { p ++; }
-  //printf ("%i:[%c]%i\n", __LINE__, *p, atoi (p));
-  for (; *p && *p != ';'; p++);
-  if (*p == ';') { p ++; }
-  width = atoi (p);
-  for (; *p && *p != ';'; p++);
-  if (*p == ';') { p ++; }
-  height = atoi (p);
-  if (width * height > 2048 * 2048)
-    return;
-  if (width <= 0 || height <=0)
+  CtxGState *gstate = &rasterizer->state->gstate;
+  CtxBuffer *buffer = gstate->source_fill.texture.buffer;
+  switch (gstate->source_fill.type)
     {
-      width = 0;
-      height = 0;
-      // XXX  : a copy paste dry-run
-      for (const char *t=p; *t; t++)
-        {
-          if (*t == '#')
-            {
-              t++;
-              while (*t && *t >= '0' && *t <= '9') { t++; }
-              if (*t == ';')
+      case CTX_SOURCE_TEXTURE:
+        switch (buffer->format->bpp)
+          {
+            case 1:  return ctx_fragment_image_gray1_RGBA8;
+            case 24: 
+              {
+                if (gstate->image_smoothing)
+                {
+                  float factor = ctx_matrix_get_scale (&gstate->transform);
+                          //fprintf (stderr, "{%.3f}", factor);
+                  if (factor < 0.5f)
+                  {
+                    if (rasterizer->swap_red_green)
+                      return ctx_fragment_image_rgb8_RGBA8_box_swap_red_green;
+                    return ctx_fragment_image_rgb8_RGBA8_box;
+                  }
+                  else if (factor > 0.99f && factor < 1.01f)
+                  {
+                    if (rasterizer->swap_red_green)
+                      return ctx_fragment_image_rgb8_RGBA8_nearest_swap_red_green;
+                    return ctx_fragment_image_rgb8_RGBA8_nearest;
+                  }
+                  else
+                  {
+                    if (rasterizer->swap_red_green)
+                      return ctx_fragment_image_rgb8_RGBA8_bi_swap_red_green;
+                    return ctx_fragment_image_rgb8_RGBA8_bi;
+                  }
+                }
+                else
                 {
-                  for (; *t && *t != ';'; t++);
-                  if (*t == ';') { t ++; }
-                  for (; *t && *t != ';'; t++);
-                  if (*t == ';') { t ++; }
-                  for (; *t && *t != ';'; t++);
-                  if (*t == ';') { t ++; }
-                  for (; *t && *t != ';'; t++);
-                  if (*t == ';') { t ++; }
-                  while (*t && *t >= '0' && *t <= '9') { t++; }
-                  t--;
+                  if (rasterizer->swap_red_green)
+                    return ctx_fragment_image_rgb8_RGBA8_nearest_swap_red_green;
+                  return ctx_fragment_image_rgb8_RGBA8_nearest;
                 }
-              else
+              }
+              break;
+            case 32:
+              {
+                if (gstate->image_smoothing)
                 {
-                  t--;
+                  float factor = ctx_matrix_get_scale (&gstate->transform);
+                          //fprintf (stderr, "[%.3f]", factor);
+                  if (factor < 0.5f)
+                  {
+                    if (rasterizer->swap_red_green)
+                      return ctx_fragment_image_rgba8_RGBA8_box_swap_red_green;
+                    return ctx_fragment_image_rgba8_RGBA8_box;
+                  }
+                  else if (factor > 0.99f && factor < 1.01f)
+                  {
+                    if (rasterizer->swap_red_green)
+                      return ctx_fragment_image_rgba8_RGBA8_nearest_swap_red_green;
+                    return ctx_fragment_image_rgba8_RGBA8_nearest;
+                  }
+                  else
+                  {
+                    if (rasterizer->swap_red_green)
+                      return ctx_fragment_image_rgba8_RGBA8_bi_swap_red_green;
+                    return ctx_fragment_image_rgba8_RGBA8_bi;
+                  }
                 }
-            }
-          else if (*t == '$') // carriage return
-            {
-              if (x > width) { width = x; }
-              x = 0;
-            }
-          else if (*t == '-') // line feed
-            {
-              y += 6;
-              x = 0;
-            }
-          else if (*t == '!') // repeat
-            {
-              t++;
-              repeat = atoi (t);
-              while (*t && *t >= '0' && *t <= '9') { t++; }
-              t--;
-            }
-          else if (*t >= '?' && *t <= '~') /* sixel data */
-            {
-              x += repeat;
-              repeat = 1;
-            }
-        }
-      height = y;
-    }
-  x = 0;
-  y = 0;
-  pixels = calloc (width * (height + 6), 4);
-  image = image_add (width, height, 0,
-                     32, width*height*4, pixels);
-  uint8_t *dst = pixels;
-  for (; *p; p++)
-    {
-      if (*p == '#')
-        {
-          p++;
-          pal_no = atoi (p);
-          if (pal_no < 0 || pal_no > 255) { pal_no = 255; }
-          while (*p && *p >= '0' && *p <= '9') { p++; }
-          if (*p == ';')
-            {
-              /* define a palette */
-              for (; *p && *p != ';'; p++);
-              if (*p == ';') { p ++; }
-              // color_model , 2 is rgb
-              for (; *p && *p != ';'; p++);
-              if (*p == ';') { p ++; }
-              colors[pal_no][0] = atoi (p) * 255 / 100;
-              for (; *p && *p != ';'; p++);
-              if (*p == ';') { p ++; }
-              colors[pal_no][1] = atoi (p) * 255 / 100;
-              for (; *p && *p != ';'; p++);
-              if (*p == ';') { p ++; }
-              colors[pal_no][2] = atoi (p) * 255 / 100;
-              while (*p && *p >= '0' && *p <= '9') { p++; }
-              p--;
-            }
-          else
-            {
-              p--;
-            }
-        }
-      else if (*p == '$') // carriage return
-        {
-          x = 0;
-          dst = &pixels[ (4 * width * y)];
-        }
-      else if (*p == '-') // line feed
-        {
-          y += 6;
-          x = 0;
-          dst = &pixels[ (4 * width * y)];
-        }
-      else if (*p == '!') // repeat
-        {
-          p++;
-          repeat = atoi (p);
-          while (*p && *p >= '0' && *p <= '9') { p++; }
-          p--;
-        }
-      else if (*p >= '?' && *p <= '~') /* sixel data */
-        {
-          int sixel = (*p) - '?';
-          if (x + repeat <= width && y < height)
-            {
-              for (int bit = 0; bit < 6; bit ++)
+                else
                 {
-                  if (sixel & (1 << bit) )
-                    {
-                      for (int u = 0; u < repeat; u++)
-                        {
-                          for (int c = 0; c < 3; c++)
-                            {
-                              dst[ (bit * width * 4) + u * 4 + c] = colors[pal_no][c];
-                              dst[ (bit * width * 4) + u * 4 + 3] = 255;
-                            }
-                        }
-                    }
+                  if (rasterizer->swap_red_green)
+                    return ctx_fragment_image_rgba8_RGBA8_nearest_swap_red_green;
+                  return ctx_fragment_image_rgba8_RGBA8_nearest;
                 }
-            }
-          x   += repeat;
-          dst += (repeat * 4);
-          repeat = 1;
-        }
-    }
-  if (image)
-    {
-      display_image (vt, image, vt->cursor_x, 0,0, 0.0, 0.0, 0,0,0,0);
-      int right = (image->width + (vt->cw-1) ) /vt->cw;
-      int down = (image->height + (vt->ch-1) ) /vt->ch;
-      for (int i = 0; i<down - 1; i++)
-        { vtcmd_index (vt, " "); }
-      for (int i = 0; i<right; i++)
-        { vtcmd_cursor_forward (vt, " "); }
-      vt_line_feed (vt);
-      vt_carriage_return (vt);
+              }
+            default: return ctx_fragment_image_RGBA8;
+          }
+      case CTX_SOURCE_COLOR:           return ctx_fragment_color_RGBA8;
+#if CTX_GRADIENTS
+      case CTX_SOURCE_LINEAR_GRADIENT: return ctx_fragment_linear_gradient_RGBA8;
+      case CTX_SOURCE_RADIAL_GRADIENT: return ctx_fragment_radial_gradient_RGBA8;
+#endif
     }
-  vt->rev++;
+  return ctx_fragment_color_RGBA8;
 }
 
-static inline void vt_ctx_unrled (VT *vt, char byte)
+static void
+ctx_init_uv (CtxRasterizer *rasterizer,
+             int x0, int count,
+             float *u0, float *v0, float *ud, float *vd)
 {
-#if CTX_VT_USE_FRAMEDIFF
-  ctx_string_append_byte (vt->current_line->frame, byte);
-#endif
-  ctx_parser_feed_byte (vt->ctxp, byte);
+  CtxGState *gstate = &rasterizer->state->gstate;
+  *u0 = x0;
+  *v0 = rasterizer->scanline / 15;//rasterizer->aa;
+  float u1 = *u0 + count;
+  float v1 = *v0;
+
+  ctx_matrix_apply_transform (&gstate->source_fill.transform, u0, v0);
+  ctx_matrix_apply_transform (&gstate->source_fill.transform, &u1, &v1);
+
+  *ud = (u1-*u0) / (count);
+  *vd = (v1-*v0) / (count);
 }
 
-static void vt_state_ctx (VT *vt, int byte)
+#if 1
+static void
+ctx_u8_source_over_normal_opaque_color (int components, CTX_COMPOSITE_ARGUMENTS)
 {
-#if 0
-  //fprintf (stderr, "%c", byte);
-  if (CTX_UNLIKELY(byte == CTX_CODEC_CHAR))
+  while (count--)
   {
-    if (CTX_UNLIKELY(vt->in_prev_match))
+    int cov = *coverage;
+    if (cov)
     {
-      char *prev = vt->current_line->prev;
-      int prev_length = vt->current_line->prev_length;
-      int start = atoi (vt->reference);
-      int len = 0;
-      if (strchr (vt->reference, ' '))
-        len = atoi (strchr (vt->reference, ' ')+1);
-
-      //fprintf (stderr, "%i-%i:", start, len);
-
-      if (start < 0) start = 0;
-      if (start >= (prev_length))start = prev_length-1;
-      if (len + start > prev_length)
-        len = prev_length - start;
-
-      //fprintf (stderr, "%i-%i\n", start, len);
-
-      if (CTX_UNLIKELY (start == 0 && len == 0))
-      {
-        vt_ctx_unrled (vt, CTX_CODEC_CHAR);
-      }
-      else
-      {
-        if (prev)
-        for (int i = 0; i < len && start + i < prev_length; i++)
+    if (cov == 255)
+    {
+        switch (components)
         {
-          vt_ctx_unrled (vt, prev[start + i]);
+          case 4:
+            *((uint32_t*)(dst)) = *((uint32_t*)(src));
+            break;
+          default:
+            for (int c = 0; c < components; c++)
+              dst[c] = src[c];
         }
-      }
-      vt->ref_len = 0;
-      vt->reference[0]=0;
-      vt->in_prev_match = 0;
     }
     else
     {
-      vt->reference[0]=0;
-      vt->ref_len = 0;
-      vt->in_prev_match = 1;
+        for (int c = 0; c < components; c++)
+          dst[c] = dst[c]+((src[c]-dst[c]) * cov) / 255;
     }
+    }
+    coverage ++;
+    dst+=components;
   }
-  else
+}
+#endif
+
+static void
+ctx_u8_copy_normal (int components, CTX_COMPOSITE_ARGUMENTS)
+{
+  float u0 = 0; float v0 = 0;
+  float ud = 0; float vd = 0;
+  if (rasterizer->fragment)
+    {
+      ctx_init_uv (rasterizer, x0, count, &u0, &v0, &ud, &vd);
+    }
+
+  while (count--)
   {
-    if (CTX_UNLIKELY(vt->in_prev_match))
+    int cov = *coverage;
+    if (cov == 0)
+    {
+      for (int c = 0; c < components; c++)
+        { dst[c] = 0; }
+    }
+    else
     {
-      if (vt->ref_len < 15)
+      if (rasterizer->fragment)
       {
-        vt->reference[vt->ref_len++]=byte;
-        vt->reference[vt->ref_len]=0;
+        rasterizer->fragment (rasterizer, u0, v0, src, 1, ud, vd);
+        u0+=ud;
+        v0+=vd;
       }
+    if (cov == 255)
+    {
+      for (int c = 0; c < components; c++)
+        dst[c] = src[c];
     }
     else
     {
-      vt_ctx_unrled (vt, byte);
+      uint8_t ralpha = 255 - cov;
+      for (int c = 0; c < components; c++)
+        { dst[c] = (src[c]*cov + 0 * ralpha) / 255; }
     }
-  }
-#else
-      vt_ctx_unrled (vt, byte);
-#endif
-}
-
-static int vt_decoder_feed (VT *vt, int byte)
-{
-  int encoding = vt->encoding;
-  switch (encoding)
-    {
-      case 0: /* utf8 */
-        if (!vt->utf8_expected_bytes)
-          {
-            vt->utf8_expected_bytes = mrg_utf8_len (byte) - 1;
-            vt->utf8_pos = 0;
-          }
-        if (vt->utf8_expected_bytes)
-          {
-            vt->utf8_holding[vt->utf8_pos++] = byte;
-            vt->utf8_holding[vt->utf8_pos] = 0;
-            if (vt->utf8_pos == vt->utf8_expected_bytes + 1)
-              {
-                vt->utf8_expected_bytes = 0;
-                vt->utf8_pos = 0;
-              }
-            else
-              {
-                return 1;
-              }
-          }
-        else
-          {
-            vt->utf8_holding[0] = byte;
-            vt->utf8_holding[0] &= 127;
-            vt->utf8_holding[1] = 0;
-            if (vt->utf8_holding[0] == 0)
-              { vt->utf8_holding[0] = 32; }
-          }
-        break;
-      case 1:
-        if ( ! (byte>=0 && byte < 256) )
-          { byte = 255; }
-        strcpy ( (char *) &vt->utf8_holding[0], &charmap_cp437[byte][0]);
-        vt->utf8_expected_bytes = mrg_utf8_len (byte) - 1; // ?
-        break;
-      default:
-        vt->utf8_holding[0] = byte & 127;
-        vt->utf8_holding[1] = 0;
-        break;
     }
-  return 0;
-}
-
-static void vt_state_swallow (VT *vt, int byte)
-{
-  vt->state = vt_state_neutral;
-}
-
-static int vt_decode_hex_digit (char digit)
-{
-  if (digit >= '0' && digit <='9')
-    return digit - '0';
-  if (digit >= 'a' && digit <='f')
-    return digit - 'a' + 10;
-  if (digit >= 'A' && digit <='F')
-    return digit - 'A' + 10;
-  return 0;
-}
-
-static int vt_decode_hex (const char *two_digits)
-{
-  return vt_decode_hex_digit (two_digits[0]) * 16 +
-         vt_decode_hex_digit (two_digits[1]);
+    dst += components;
+    coverage ++;
+  }
 }
 
-static uint8_t palettes[][16][3]=
+static void
+ctx_u8_clear_normal (int components, CTX_COMPOSITE_ARGUMENTS)
 {
+  while (count--)
   {
-{0, 0, 0},
-{160, 41, 41},
-{74, 160, 139},
-{135, 132, 83},
-{36, 36, 237},
-{171, 74, 223},
-{59, 107, 177},
-{195, 195, 195},
-{111, 111, 111},
-{237, 172, 130},
-{153, 237, 186},
-{233, 216, 8},
-{130, 180, 237},
-{214, 111, 237},
-{29, 225, 237},
-{255, 255, 255},
-
-  },
-
-  {
-    {0, 0, 0},
-    {127, 0, 0},
-    {90, 209, 88},
-    {136, 109, 0},
-    {3, 9, 235},
-    {90, 4, 150},
-    {43, 111, 150},
-    {178, 178, 178},
-    {87, 87, 87},
-    {193, 122, 99},
-    {110, 254, 174},
-    {255, 200, 0},
-    {10, 126, 254},
-    {146, 155, 249},
-    {184, 208, 254},
-    {255, 255, 255},
-
-  },{
-    {0, 0, 0},
-    {147, 53, 38},
-    {30, 171, 82},
-    {188, 153, 0},
-    {32, 71, 193},
-    {236, 49, 188},
-    {42, 182, 253},
-    {149, 149, 149},
-    {73, 73, 73},
-    {210, 36, 0},
-    {96, 239, 97},
-    {247, 240, 2},
-    {93, 11, 249},
-    {222, 42, 255},
-    {11, 227, 255},
-    {233, 235, 235},
-  },
-
-
-  { {0, 0, 0},{97, 27, 0},{129, 180, 0},{127, 100, 0},{44, 15, 255},{135, 10, 167},{20, 133, 164},{174, 174, 
174},{71, 71, 71},{167, 114, 90},{162, 214, 127},{255, 251, 83},{118, 77, 253},{192, 121, 255},{14, 217, 
255},{255, 255, 255},
-  },{
-
-
 #if 0
+    int cov = *coverage;
+    if (cov)
     {
-      {0, 0, 0},
-      {144, 0, 0},
-      {9, 154, 9},
-      {255, 137, 113},
-      {3, 0, 255},
-      {56, 0, 132},
-      {0, 131, 131},
-      {204, 204, 204},
-      {127, 127, 127},
-      {255, 33, 0},
-      {113, 255, 88},
-      {255, 236, 8},
-      {1, 122, 255},
-      {235, 0, 222},
-      {0, 217, 255},
-      {255, 255, 255},
-    },{
-#endif
-
-
-    {0, 0, 0},
-    {139, 0, 0},
-    {9, 154, 9},
-    {255, 137, 113},
-    {3, 0, 255},
-    {56, 0, 132},
-    {0, 111, 111},
-    {204, 204, 204},
-    {127, 127, 127},
-    {255, 33, 0},
-    {118, 255, 92},
-    {255, 230, 15},
-    {1, 122, 255},
-    {232, 0, 220},
-    {1, 217, 255},
-    {255, 255, 255},
-  },
-  {
-
-    {0, 0, 0},
-    {191, 0, 0},
-    {3, 187, 0},
-    {254, 212, 0},
-    {0, 0, 255},
-    {80, 0, 128},
-    {0, 156, 255},
-    {166, 166, 166},
-    {84, 84, 84},
-    {255, 62, 0},
-    {85, 255, 143},
-    {255, 255, 0},
-    {67, 80, 255},
-    {243, 70, 255},
-    {30, 255, 222},
-    {255, 255, 255},
-  },
-  {
-    /* */
-    { 32, 32, 32}, // 0 - background (black)
-    {165, 15, 21}, // 1               red
-    { 95,130, 10}, // 2               green
-    {205,145, 60}, // 3               yellow
-    { 49,130,189}, // 4               blue
-    {120, 40,160}, // 5               magenta
-    {120,230,230}, // 6               cyan
-    {196,196,196},// 7                light-gray
-    { 85, 85, 85},// 8                dark gray
-
-    {251,106, 74},// 9                light red
-    {130,215,140},// 10               light green
-    {255,255,  0},// 11               light yellow
-    {107,174,214},// 12               light blue
-    {215,130,160},// 13               light magenta
-    {225,255,245},// 14               light cyan
-    {255,255,255},// 15 - foreground (white)
-  },{
-    /* */
-    { 32, 32, 32}, // 0 - background (black)
-    {160,  0,  0}, // 1               red
-    {  9,233,  0}, // 2               green
-    {220,110, 44}, // 3               yellow
-    {  0,  0,200}, // 4               blue
-    { 90,  0,130}, // 5               magenta
-    {  0,156,180}, // 6               cyan
-    {196,196,196}, // 7                light-gray
-    { 85, 85, 85}, // 8                dark gray
-
-    {240, 60, 40}, // 9                light red
-    {170,240, 80}, // 10               light green
-    {248,248,  0}, // 11               light yellow
-    {  0, 40,255}, // 12               light blue
-    {204, 62,214}, // 13               light magenta
-    { 10,234,254}, // 14               light cyan
-    {255,255,255}, // 15 - foreground (white)
-  },
-  /* inspired by DEC */
-  { {  0,  0,  0}, // 0 - background  black
-    {150, 10, 10}, // 1               red
-    { 21,133,  0}, // 2               green
-
-    {103,103, 24}, // 3               yellow
-    { 44, 44,153}, // 4               blue
-    {123, 94,183}, // 5               magenta
-
-    { 20,183,193}, // 6               cyan
-
-    {177,177,177},// 7                light-gray
-    {100,100,100},// 8                dark gray
-
-    {244, 39, 39},// 9                light red
-    { 61,224, 81},// 10               light green
-    {255,255,  0},// 11               light yellow
-    { 61, 61,244},// 12               light blue
-    {240, 11,240},// 13               light magenta
-    { 61,234,234},// 14               light cyan
-
-    {255,255,255},// 15 - foreground  white
-  },
-};
-
-static void vt_state_osc (VT *vt, int byte)
-{
-  // https://ttssh2.osdn.jp/manual/4/en/about/ctrlseq.html
-  // and in "\033\" rather than just "\033", this would cause
-  // a stray char
-  //if (byte == '\a' || byte == 27 || byte == 0 || byte < 32)
-  if ( (byte < 32) && ( (byte < 8) || (byte > 13) ) )
-    {
-      int n = parse_int (vt->argument_buf, 0);
-      switch (n)
-        {
-          case 0:
-          case 1:
-          case 2:
-#if 0
-    {"]0;New_title\e\",  0, , }, /* id: set window title */ "
-#endif
-            vt_set_title (vt, vt->argument_buf + 3);
-            break;
-          case 4: // set palette entry
-            {
-            int color_no = parse_int (vt->argument_buf + 2, 0);
-            char *rest = vt->argument_buf + 3;
-            rest = strchr (rest, ';');
-
-            if (rest++)
-            if (strlen(rest)>10 &&
-                rest[0] == 'r' &&
-                rest[1] == 'g' &&
-                rest[2] == 'b' &&
-                rest[3] == ':' &&
-                rest[6] == '/' &&
-                rest[9] == '/')
-            {
-              int red = vt_decode_hex (&rest[4]);
-              int green = vt_decode_hex (&rest[7]);
-              int blue = vt_decode_hex (&rest[10]);
-          //  fprintf (stderr, "set color:%i  %i %i %i\n", color_no, red, green, blue);
-              if (color_no >= 0 && color_no <= 15)
-              {
-                palettes[0][color_no][0]=red;
-                palettes[0][color_no][1]=green;
-                palettes[0][color_no][2]=blue;
-              }
-            }
-            }
-            break;
-          case 12: // text cursor color
-            break;
-          case 17: // highlight color
-            break;
-          case 19: // ??
-            break;
-
-          case 10: // text fg
-#if 0
-#if 0
-    {"]11;",  0, , }, /* id: set foreground color */
-#endif
-            {
-              /* request current foreground color, xterm does this to
-                 determine if it can use 256 colors, when this test fails,
-                 it still mixes in color 130 together with stock colors
-               */
-              char buf[128];
-              sprintf (buf, "\033]10;rgb:%2x/%2x/%2x\033\\",
-                       vt->fg_color[0], vt->fg_color[1], vt->fg_color[2]);
-              vt_write (vt, buf, strlen (buf) );
-            }
-#endif
-            break;
-          case 11: // text bg
-#if 0
-    {"]11;",  0, , }, /* id: get background color */
-            {
-              /* get background color */
-              char buf[128];
-              sprintf (buf, "\033]11;rgb:%2x/%2x/%2x\033\\",
-                       vt->bg_color[0], vt->bg_color[1], vt->bg_color[2]);
-              vt_write (vt, buf, strlen (buf) );
-            }
-#endif
-            break;
-#if 0
-    {"]1337;key=value:base64data\b\",  0, vtcmd_erase_in_line, VT100}, /* args:keyvalue id: iterm2 graphics 
*/ "
+      if (cov == 255)
+      {
 #endif
-          case 1337:
-            if (!strncmp (&vt->argument_buf[6], "File=", 5) )
-              {
-                {
-                  /* iTerm2 image protocol */
-                  int width = 0;
-                  int height = 0;
-                  int file_size = 0;
-                  int show_inline = 0;
-                  int preserve_aspect = 1;
-                  char *name = NULL;
-                  char *p = &vt->argument_buf[11];
-                  char key[128]="";
-                  char value[128]="";
-                  int in_key=1;
-                  if (preserve_aspect) {}; /* XXX : NYI */
-                  for (; *p && *p!=':'; p++)
-                    {
-                      if (in_key)
-                        {
-                          if (*p == '=')
-                            { in_key = 0; }
-                          else
-                            {
-                              if (strlen (key) < 124)
-                                {
-                                  key[strlen (key)+1] = 0;
-                                  key[strlen (key)] = *p;
-                                }
-                            }
-                        }
-                      else
-                        {
-                          if (*p == ';')
-                            {
-                              if (!strcmp (key, "name") )
-                                {
-                                  name = strdup (value);
-                                }
-                              else if (!strcmp (key, "width") )
-                                {
-                                  width = atoi (value);
-                                  if (strchr (value, 'x') )
-                                    { /* pixels */ }
-                                  else if (strchr (value, '%') )
-                                    {
-                                      /* percent */
-                                      width = width / 100.0 * (vt->cw * vt->cols);
-                                    }
-                                  else
-                                    { /* chars */ width = width * vt->cw; }
-                                }
-                              else if (!strcmp (key, "height") )
-                                {
-                                  height = atoi (value);
-                                  if (strchr (value, 'x') )
-                                    { /* pixels */ }
-                                  else if (strchr (value, '%') )
-                                    {
-                                      /* percent */
-                                      height = height / 100.0 * (vt->ch * vt->rows);
-                                    }
-                                  else
-                                    { /* chars */ height = height * vt->ch; }
-                                }
-                              else if (!strcmp (key, "preserveAspectRatio") )
-                                {
-                                  preserve_aspect = atoi (value);
-                                }
-                              else if (!strcmp (key, "inline") )
-                                {
-                                  show_inline = atoi (value);
-                                }
-                              key[0]=0;
-                              value[0]=0;
-                              in_key = 1;
-                            }
-                          else
-                            {
-                              if (strlen (value) < 124)
-                                {
-                                  value[strlen (value)+1] = 0;
-                                  value[strlen (value)] = *p;
-                                }
-                            }
-                        }
-                    }
-                  if (key[0])
-                    {
-                      // code-dup
-                      if (!strcmp (key, "name") )
-                        {
-                          name = strdup (value);
-                        }
-                      else if (!strcmp (key, "width") )
-                        {
-                          width = atoi (value);
-                          if (strchr (value, 'x') )
-                            { /* pixels */ }
-                          else if (strchr (value, '%') )
-                            {
-                              /* percent */
-                              width = width / 100.0 * (vt->cw * vt->cols);
-                            }
-                          else
-                            { /* chars */ width = width * vt->cw; }
-                        }
-                      else if (!strcmp (key, "height") )
-                        {
-                          height = atoi (value);
-                          if (strchr (value, 'x') )
-                            { /* pixels */ }
-                          else if (strchr (value, '%') )
-                            {
-                              /* percent */
-                              height = height / 100.0 * (vt->ch * vt->rows);
-                            }
-                          else
-                            { /* chars */ height = height * vt->ch; }
-                        }
-                      else if (!strcmp (key, "preserveAspectRatio") )
-                        {
-                          preserve_aspect = atoi (value);
-                        }
-                      else if (!strcmp (key, "inline") )
-                        {
-                          show_inline = atoi (value);
-                        }
-                    }
-                  if (*p == ':')
-                    {
-                      p++;
-                    }
-                  if (0)
-                    fprintf (stderr, "%s %i %i %i %i{%s\n", name?name:"",
-                             width, height, file_size, show_inline,
-                             p);
-                  Image *image = NULL;
-                  {
-                    int bin_length = vt->argument_buf_len;
-                    uint8_t *data2 = malloc (bin_length);
-                    bin_length = ctx_base642bin ( (char *) p,
-                                                 &bin_length,
-                                                 data2);
-                    int channels = 4;
-                    int buf_width = 0;
-                    int buf_height = 0;
-                    uint8_t *new_data = stbi_load_from_memory (data2, bin_length, &buf_width, &buf_height, 
&channels, 4);
-                    free (data2);
-                    if (new_data)
-                      {
-                        image = image_add (buf_width, buf_height, 0,
-                                           32, buf_width*buf_height*4, new_data);
-                      }
-                    else
-                      {
-                        fprintf (stderr, "image decoding problem %s\n", stbi_failure_reason());
-                        fprintf (stderr, "len: %i\n", bin_length);
-                      }
-                  }
-                  if (image)
-                    {
-                      display_image (vt, image, vt->cursor_x, 0,0, 0.0, 0.0, 0,0,0,0);
-                      int right = (image->width + (vt->cw-1) ) /vt->cw;
-                      int down = (image->height + (vt->ch-1) ) /vt->ch;
-                      for (int i = 0; i<down - 1; i++)
-                        { vtcmd_index (vt, " "); }
-                      for (int i = 0; i<right; i++)
-                        { vtcmd_cursor_forward (vt, " "); }
-                    }
-                }
-              }
-            break;
-          case 104:
-            break;
-          case 8:
-            fprintf (stderr, "unhandled OSC 8, hyperlink\n");
-            break;
+           //__attribute__ ((fallthrough));
+        switch (components)
+        {
+          case 1: dst[0] = 0; break;
+          case 3: dst[2] = 0;
+           /* FALLTHROUGH */
+          case 2: *((uint16_t*)(dst)) = 0; break;
+          case 5: dst[4] = 0;
+           /* FALLTHROUGH */
+          case 4: *((uint32_t*)(dst)) = 0; break;
           default:
-            fprintf (stderr, "unhandled OSC %i\n", n);
+            for (int c = 0; c < components; c ++)
+              dst[c] = 0;
             break;
         }
-      if (byte == 27)
-        {
-          vt->state = vt_state_swallow;
-        }
+#if 0
+      }
       else
-        {
-          vt->state = vt_state_neutral;
-        }
-    }
-  else
-    {
-      vt_argument_buf_add (vt, byte);
+      {
+        uint8_t ralpha = 255 - cov;
+        for (int c = 0; c < components; c++)
+          { dst[c] = (dst[c] * ralpha) / 255; }
+      }
     }
+    coverage ++;
+#endif
+    dst += components;
+  }
 }
 
+typedef enum {
+  CTX_PORTER_DUFF_0,
+  CTX_PORTER_DUFF_1,
+  CTX_PORTER_DUFF_ALPHA,
+  CTX_PORTER_DUFF_1_MINUS_ALPHA,
+} CtxPorterDuffFactor;
+
+#define  \
+ctx_porter_duff_factors(mode, foo, bar)\
+{\
+  switch (mode)\
+  {\
+     case CTX_COMPOSITE_SOURCE_ATOP:\
+        f_s = CTX_PORTER_DUFF_ALPHA;\
+        f_d = CTX_PORTER_DUFF_1_MINUS_ALPHA;\
+      break;\
+     case CTX_COMPOSITE_DESTINATION_ATOP:\
+        f_s = CTX_PORTER_DUFF_1_MINUS_ALPHA;\
+        f_d = CTX_PORTER_DUFF_ALPHA;\
+      break;\
+     case CTX_COMPOSITE_DESTINATION_IN:\
+        f_s = CTX_PORTER_DUFF_0;\
+        f_d = CTX_PORTER_DUFF_ALPHA;\
+      break;\
+     case CTX_COMPOSITE_DESTINATION:\
+        f_s = CTX_PORTER_DUFF_0;\
+        f_d = CTX_PORTER_DUFF_1;\
+       break;\
+     case CTX_COMPOSITE_SOURCE_OVER:\
+        f_s = CTX_PORTER_DUFF_1;\
+        f_d = CTX_PORTER_DUFF_1_MINUS_ALPHA;\
+       break;\
+     case CTX_COMPOSITE_DESTINATION_OVER:\
+        f_s = CTX_PORTER_DUFF_1_MINUS_ALPHA;\
+        f_d = CTX_PORTER_DUFF_1;\
+       break;\
+     case CTX_COMPOSITE_XOR:\
+        f_s = CTX_PORTER_DUFF_1_MINUS_ALPHA;\
+        f_d = CTX_PORTER_DUFF_1_MINUS_ALPHA;\
+       break;\
+     case CTX_COMPOSITE_DESTINATION_OUT:\
+        f_s = CTX_PORTER_DUFF_0;\
+        f_d = CTX_PORTER_DUFF_1_MINUS_ALPHA;\
+       break;\
+     case CTX_COMPOSITE_SOURCE_OUT:\
+        f_s = CTX_PORTER_DUFF_1_MINUS_ALPHA;\
+        f_d = CTX_PORTER_DUFF_0;\
+       break;\
+     case CTX_COMPOSITE_SOURCE_IN:\
+        f_s = CTX_PORTER_DUFF_ALPHA;\
+        f_d = CTX_PORTER_DUFF_0;\
+       break;\
+     case CTX_COMPOSITE_COPY:\
+        f_s = CTX_PORTER_DUFF_1;\
+        f_d = CTX_PORTER_DUFF_0;\
+       break;\
+     default:\
+     case CTX_COMPOSITE_CLEAR:\
+        f_s = CTX_PORTER_DUFF_0;\
+        f_d = CTX_PORTER_DUFF_0;\
+       break;\
+  }\
+}
 
-static void vt_state_sixel (VT *vt, int byte)
+#if 1
+static void
+ctx_u8_source_over_normal_color (int components,
+                                 CtxRasterizer         *rasterizer,
+                                 uint8_t * __restrict__ dst,
+                                 uint8_t * __restrict__ src,
+                                 int                    x0,
+                                 uint8_t * __restrict__ coverage,
+                                 int                    count)
 {
-  // https://ttssh2.osdn.jp/manual/4/en/about/ctrlseq.html
-  // and in "\033\" rather than just "\033", this would cause
-  // a stray char
-  if ( (byte < 32) && ( (byte < 8) || (byte > 13) ) )
+  uint8_t tsrc[5];
+  *((uint32_t*)tsrc) = *((uint32_t*)src);
+  ctx_u8_associate_alpha (components, tsrc);
+
+    while (count--)
     {
-      vt_sixels (vt, vt->argument_buf);
-      if (byte == 27)
+      int cov = *coverage;
+      if (cov)
+      {
+        if (cov == 255)
         {
-          vt->state = vt_state_swallow;
+        for (int c = 0; c < components; c++)
+          dst[c] = (tsrc[c]) + (dst[c] * (255-(tsrc[components-1])))/(255);
         }
-      else
+        else
         {
-          vt->state = vt_state_neutral;
-        }
-    }
-  else
-    {
-      vt_argument_buf_add (vt, byte);
-      //fprintf (stderr, "\r%i ", vt->argument_buf_len);
+          for (int c = 0; c < components; c++)
+            dst[c] = (tsrc[c] * cov)/255 + (dst[c] * ((255*255)-(tsrc[components-1] * cov)))/(255*255);
+         }
+      }
+      coverage ++;
+      dst+=components;
     }
 }
+#endif
+
+#if CTX_AVX2
 
-//void add_tab (Ctx *ctx, const char *commandline, int can_launch);
-//void vt_screenshot (const char *output_path);
+#if !__COSMOPOLITAN__
+#include <stdalign.h>
+#endif
+#endif
 
-static void vt_state_apc_generic (VT *vt, int byte)
+static void
+CTX_COMPOSITE_SUFFIX(ctx_RGBA8_source_over_normal_buf) (CTX_COMPOSITE_ARGUMENTS, uint8_t *tsrc)
 {
-  if ( (byte < 32) && ( (byte < 8) || (byte > 13) ) )
+    int x = 0;
+
+#if CTX_AVX2
+    if (((size_t)(dst) & 31))
+#endif
     {
-      if (vt->argument_buf[1] == 'G') /* graphics - from kitty */
-        {
-          vt_gfx (vt, vt->argument_buf);
-        }
-      else if (vt->argument_buf[1] == 'C') /* launch command */
+      for (; (x < count) 
+#if CTX_AVX2
+                        && ((size_t)(dst)&31)
+#endif
+                      ; 
+                      x++)
+    {
+      int cov = coverage[0];
+      if (cov)
       {
-        if (vt->can_launch)
-        {
-          int   can_launch = 0;
-          int   no_title = 0;
-          int   no_move = 0;
-          int   no_resize = 0;
-          int   layer = 0;
-  // escape subsequent arguments so that we dont have to pass a string?
-          float x = -1.0;
-          float y = -1.0;
-          int   z = 0;
-          float width = -1.0;
-          float height = -1.0;
-
-          for (int i=2; vt->argument_buf[i]; i++)
-          {
-            if (!strncmp (&vt->argument_buf[i], "can_launch=1", strlen ("can_launch=1")))
-              can_launch = 1;
-            if (!strncmp (&vt->argument_buf[i], "no_title=1", strlen("no_title=1")))
-              no_title = 1;
-            if (!strncmp (&vt->argument_buf[i], "no_move=1", strlen("no_move=1")))
-              no_move = 1;
-            else if (!strncmp (&vt->argument_buf[i], "z=", 2))
-              z=atoi(&vt->argument_buf[i]+strlen("z="));
-            else if (!strncmp (&vt->argument_buf[i], "x=", 2))
-              x=atof(&vt->argument_buf[i]+strlen("x="));
-            else if (!strncmp (&vt->argument_buf[i], "y=", 2))
-              y=atof(&vt->argument_buf[i]+strlen("y="));
-            else if (!strncmp (&vt->argument_buf[i], "width=", 6))
-              width=atof(&vt->argument_buf[i]+strlen("width="));
-            else if (!strncmp (&vt->argument_buf[i], "height=", 7))
-              height=atof(&vt->argument_buf[i]+strlen("height="));
-          }
+        uint32_t *sip = ((uint32_t*)(tsrc));
+        uint32_t si = *sip;
+        uint64_t si_ga = si & CTX_RGBA8_GA_MASK;
+        uint32_t si_rb = si & CTX_RGBA8_RB_MASK;
+        uint32_t *dip = ((uint32_t*)(dst));
+        int      si_a  = si >> CTX_RGBA8_A_SHIFT;
+        uint32_t di = *dip;
+        uint64_t di_ga = di & CTX_RGBA8_GA_MASK;
+        uint32_t di_rb = di & CTX_RGBA8_RB_MASK;
+        int ir_cov_si_a = 255-((cov*si_a)/255);
+        *((uint32_t*)(dst)) = 
+         (((si_rb * cov + di_rb * ir_cov_si_a) >> 8) & CTX_RGBA8_RB_MASK) |
+         (((si_ga * cov + di_ga * ir_cov_si_a) >> 8) & CTX_RGBA8_GA_MASK);
+      }
+      dst += 4;
+      tsrc += 4;
+      coverage ++;
+    }
+  }
 
-          if (width + no_resize + layer + height + x + y + no_title + no_move + z + can_launch) {};
+#if CTX_AVX2
+    __m256i a_mask = _mm256_set1_epi32 (0xFF000000);
+    __m256i lo_mask = _mm256_set1_epi32 (0x00FF00FF);
+    __m256i hi_mask = _mm256_set1_epi32 (0xFF00FF00);
+    __m256i x00ff =   _mm256_set1_epi16(255);
+    __m256i x0101 =   _mm256_set1_epi16(0x0101);
+    __m256i x0080 =   _mm256_set1_epi16(0x0080);
 
-          char *sep = strchr(vt->argument_buf, ';');
-          if (sep)
-          {
-            //fprintf (stderr, "[%s]", sep +  1);
-            if (!strncmp (sep + 1, "fbsave", 6))
-            {
-              // vt_screenshot (sep + 8);
-            }
-            else
-            {
-          //  add_tab (ctx, sep + 1, can_launch);
-            }
-          }
-        }
+    int trailer = (count - x) % 8 + 1;
 
-      }
-      vt->state = ( (byte == 27) ?  vt_state_swallow : vt_state_neutral);
-    }
-  else
+    for (; x <= count-trailer; x+=8)
     {
-      vt_argument_buf_add (vt, byte);
-    }
-}
+     if (((uint64_t*)(coverage))[0])
+     {
+       __m256i xcov;
+       __m256i x1_minus_cov_mul_a;
+       __m256i xsrc = _mm256_load_si256((__m256i*)(tsrc));
+
+       __m256i xsrc_a = _mm256_and_si256(xsrc, a_mask);
+       xsrc_a = _mm256_srli_epi32 (xsrc_a, 24);
+       xsrc_a |= xsrc_a << 16;
+       xcov  = _mm256_set_epi32(coverage[7],
+                                coverage[6],
+                                coverage[5],
+                                coverage[4],
+                                coverage[3],
+                                coverage[2],
+                                coverage[1],
+                                coverage[0]
+                                );
+       xcov |= xcov << 16;
+       __m256i xcov_mul_a  =
+         _mm256_mulhi_epu16(_mm256_adds_epu16(_mm256_mullo_epi16(xcov,  xsrc_a), x0080), x0101);
+       x1_minus_cov_mul_a = _mm256_sub_epi16(x00ff, xcov_mul_a);
+
+       __m256i xdst   = _mm256_load_si256((__m256i*)(dst));
+       __m256i dst_lo = _mm256_and_si256 (xdst, lo_mask);
+       __m256i dst_hi = _mm256_srli_epi16 (_mm256_and_si256 (xdst, hi_mask), 8);
+       __m256i src_lo = _mm256_and_si256 (xsrc, lo_mask);
+       __m256i src_hi = _mm256_srli_epi16 (_mm256_and_si256 (xsrc, hi_mask), 8);
+        
+       dst_hi  = _mm256_mulhi_epu16(_mm256_adds_epu16(_mm256_mullo_epi16(dst_hi,  x1_minus_cov_mul_a), 
x0080), x0101);
+       dst_lo  = _mm256_mulhi_epu16(_mm256_adds_epu16(_mm256_mullo_epi16(dst_lo,  x1_minus_cov_mul_a), 
x0080), x0101);
 
-#if 0
-    {"_G..\e\", 0, vtcmd_delete_n_chars, VT102}, /* ref:none id: <a 
href='https://sw.kovidgoyal.net/kitty/graphics-protocol.html'>kitty graphics</a> */ "
-    {"_A..\e\", 0, vtcmd_delete_n_chars, VT102}, /* id:  <a 
href='https://github.com/hodefoting/atty/'>atty</a> audio input/output */ "
-    {"_C..\e\", 0, vtcmd_delete_n_chars, VT102}, /* id:  run command */ "
-#endif
-static void vt_state_apc (VT *vt, int byte)
-{
-  if (byte == 'A')
-    {
-      vt_argument_buf_add (vt, byte);
-      vt->state = vt_state_apc_audio;
-    }
-  else if ( (byte < 32) && ( (byte < 8) || (byte > 13) ) )
-    {
-      vt->state = ( (byte == 27) ?  vt_state_swallow : vt_state_neutral);
+       src_lo  = _mm256_mulhi_epu16(_mm256_adds_epu16(_mm256_mullo_epi16(src_lo, xcov), x0080), x0101);
+       src_hi  = _mm256_mulhi_epu16(_mm256_adds_epu16(_mm256_mullo_epi16(src_hi, xcov), x0080), x0101);
+
+       dst_hi = _mm256_adds_epu16(dst_hi, src_hi);
+       dst_lo = _mm256_adds_epu16(dst_lo, src_lo);
+
+       _mm256_store_si256((__m256i*)dst, _mm256_slli_epi16 (dst_hi,8)|dst_lo);
+     }
+
+      dst  += 4 * 8;
+      tsrc += 4 * 8;
+      coverage += 8;
     }
-  else
+    for (; x < count; x++)
     {
-      vt_argument_buf_add (vt, byte);
-      vt->state = vt_state_apc_generic;
+      int cov = coverage[0];
+      if (cov)
+      {
+        uint32_t *sip = ((uint32_t*)(tsrc));
+        uint32_t si = *sip;
+        uint32_t *dip = ((uint32_t*)(dst));
+        uint32_t di = *dip;
+
+        uint64_t si_ga = si & CTX_RGBA8_GA_MASK;
+        uint32_t si_rb = si & CTX_RGBA8_RB_MASK;
+        int      si_a  = si >> CTX_RGBA8_A_SHIFT;
+
+        uint64_t di_ga = di & CTX_RGBA8_GA_MASK;
+        uint32_t di_rb = di & CTX_RGBA8_RB_MASK;
+        int ir_cov_si_a = 255-((cov*si_a)/255);
+        *((uint32_t*)(dst)) = 
+         (((si_rb * cov + di_rb * ir_cov_si_a) >> 8) & CTX_RGBA8_RB_MASK) |
+         (((si_ga * cov + di_ga * ir_cov_si_a) >> 8) & CTX_RGBA8_GA_MASK);
+      }
+      dst  += 4;
+      tsrc += 4;
+      coverage ++;
     }
+#endif
 }
 
-static void vt_state_esc_foo (VT *vt, int byte)
+static void
+CTX_COMPOSITE_SUFFIX(ctx_RGBA8_source_over_normal_fragment) (CTX_COMPOSITE_ARGUMENTS)
 {
-  vt_argument_buf_add (vt, byte);
-  vt->state = vt_state_neutral;
-  handle_sequence (vt, vt->argument_buf);
-}
+  float u0 = 0; float v0 = 0;
+  float ud = 0; float vd = 0;
+  ctx_init_uv (rasterizer, x0, count, &u0, &v0, &ud, &vd);
+#if CTX_DITHER
+  int dither_red_blue = rasterizer->format->dither_red_blue;
+  int dither_green = rasterizer->format->dither_green;
+#endif
+  CtxFragment fragment = rasterizer->fragment;
+  int fudge = ((size_t)(dst) & 31);
+#if CTX_AVX2
+  alignas(32)
+#endif
+  uint8_t _tsrc[4 * (count + fudge)];
+  uint8_t *tsrc = &_tsrc[fudge];
+  fragment (rasterizer, u0, v0, tsrc, count, ud, vd);
+#if CTX_DITHER
+  ctx_init_uv (rasterizer, x0, count, &u0, &v0, &ud, &vd);
+#endif
+  for (int x = 0; x < count ; x++)
+  {
+    ctx_RGBA8_associate_alpha (tsrc);
+#if CTX_DITHER
+    ctx_dither_rgba_u8 (tsrc, u0, v0, dither_red_blue, dither_green);
+#endif
+    tsrc += 4;
+#if CTX_DITHER
+    u0 += ud;
+    v0 += vd;
+#endif
+  }
+  tsrc = &_tsrc[fudge];
+
+  CTX_COMPOSITE_SUFFIX(ctx_RGBA8_source_over_normal_buf) (rasterizer,
+                       dst, src, x0, coverage, count, tsrc);
 
-static void vt_state_esc_sequence (VT *vt, int byte)
-{
-  if (_vt_handle_control (vt, byte) == 0)
-    {
-      if (byte == 27)
-        {
-        }
-      else if (byte >= '@' && byte <= '~')
-        {
-          vt_argument_buf_add (vt, byte);
-          vt->state = vt_state_neutral;
-          handle_sequence (vt, vt->argument_buf);
-        }
-      else
-        {
-          vt_argument_buf_add (vt, byte);
-        }
-    }
 }
 
-static void vt_state_esc (VT *vt, int byte)
-{
-  if (_vt_handle_control (vt, byte) == 0)
-    switch (byte)
-      {
-        case 27: /* ESCape */
-          break;
-        case ')':
-        case '#':
-        case '(':
-          {
-            char tmp[]= {byte, '\0'};
-            vt_argument_buf_reset (vt, tmp);
-            vt->state = vt_state_esc_foo;
-          }
-          break;
-        case '[':
-        case '%':
-        case '+':
-        case '*':
-          {
-            char tmp[]= {byte, '\0'};
-            vt_argument_buf_reset (vt, tmp);
-            vt->state = vt_state_esc_sequence;
-          }
-          break;
 
-#if 0
-    {"Psixel_data\e\",  0, , }, /* id: sixels */ "
+#if CTX_GRADIENTS
+#if CTX_INLINED_GRADIENTS
+static void
+CTX_COMPOSITE_SUFFIX(ctx_RGBA8_source_over_normal_linear_gradient) (CTX_COMPOSITE_ARGUMENTS)
+{
+  CtxSource *g = &rasterizer->state->gstate.source_fill;
+  float u0 = 0; float v0 = 0;
+  float ud = 0; float vd = 0;
+  ctx_init_uv (rasterizer, x0, count, &u0, &v0, &ud, &vd);
+  float linear_gradient_dx = g->linear_gradient.dx;
+  float linear_gradient_dy = g->linear_gradient.dy;
+  float linear_gradient_rdelta = g->linear_gradient.rdelta;
+  float linear_gradient_start = g->linear_gradient.start;
+  float linear_gradient_length = g->linear_gradient.length;
+#if CTX_DITHER
+  int dither_red_blue = rasterizer->format->dither_red_blue;
+  int dither_green = rasterizer->format->dither_green;
+#endif
+  int fudge = ((size_t)(dst) & 31);
+#if CTX_AVX2
+  alignas(32)
+#endif
+  uint8_t _tsrc[4 * (count + fudge)];
+  uint8_t *tsrc = &_tsrc[fudge];
+  for (int x = 0; x < count ; x++)
+  {
+      float vv = ( ( (linear_gradient_dx * u0 + linear_gradient_dy * v0) / linear_gradient_length) -
+            linear_gradient_start) * (linear_gradient_rdelta);
+      uint32_t *tsrci = (uint32_t*)tsrc;
+#if CTX_GRADIENT_CACHE
+      uint32_t *cachei = ((uint32_t*)(&ctx_gradient_cache_u8_a[ctx_grad_index(vv)][0]));
+      *tsrci = *cachei;
+#else
+      ctx_fragment_gradient_1d_RGBA8 (rasterizer, vv, 1.0, tsrc);
+      ctx_RGBA8_associate_alpha (tsrc);
+#endif
+#if CTX_DITHER
+      ctx_dither_rgba_u8 (tsrc, u0, v0, dither_red_blue, dither_green);
 #endif
 
-        case 'P':
-          {
-            char tmp[]= {byte, '\0'};
-            vt_argument_buf_reset (vt, tmp);
-            vt->state = vt_state_sixel;
-          }
-          break;
-        case ']':
-          {
-            char tmp[]= {byte, '\0'};
-            vt_argument_buf_reset (vt, tmp);
-            vt->state = vt_state_osc;
-          }
-          break;
-        case '^':  // privacy message
-        case '_':  // APC
-        case 'X':  // SOS
-          {
-            char tmp[]= {byte, '\0'};
-            vt_argument_buf_reset (vt, tmp);
-            vt->state = vt_state_apc;
-          }
-          break;
-        default:
-          {
-            char tmp[]= {byte, '\0'};
-            tmp[0]=byte;
-            vt->state = vt_state_neutral;
-            handle_sequence (vt, tmp);
-          }
-          break;
-      }
+    tsrc += 4;
+    u0 += ud;
+    v0 += vd;
+  }
+  tsrc = &_tsrc[fudge];
+  CTX_COMPOSITE_SUFFIX(ctx_RGBA8_source_over_normal_buf) (rasterizer,
+               dst, src, x0, coverage, count, tsrc);
 }
 
-static void vt_state_neutral (VT *vt, int byte)
+static void
+CTX_COMPOSITE_SUFFIX(ctx_RGBA8_source_over_normal_radial_gradient) (CTX_COMPOSITE_ARGUMENTS)
 {
-  if (CTX_UNLIKELY(_vt_handle_control (vt, byte) != 0))
-    return;
-  if (CTX_LIKELY(byte != 27))
-  {
-    if (vt_decoder_feed (vt, byte) )
-      return;
-    if (vt->charset[vt->shifted_in] != 0 &&
-        vt->charset[vt->shifted_in] != 'B')
-      {
-        char **charmap;
-        switch (vt->charset[vt->shifted_in])
-          {
-            case 'A':
-              charmap = charmap_uk;
-              break;
-            case 'B':
-              charmap = charmap_ascii;
-              break;
-            case '0':
-              charmap = charmap_graphics;
-              break;
-            case '1':
-              charmap = charmap_cp437;
-              break;
-            case '2':
-              charmap = charmap_graphics;
-              break;
-            default:
-              charmap = charmap_ascii;
-              break;
-          }
-        if ( (vt->utf8_holding[0] >= ' ') && (vt->utf8_holding[0] <= '~') )
-          {
-            _vt_add_str (vt, charmap[vt->utf8_holding[0]-' ']);
-          }
-      }
-    else
-      {
-        // ensure vt->utf8_holding contains a valid utf8
-        uint32_t codepoint;
-        uint32_t state = 0;
-        for (int i = 0; vt->utf8_holding[i]; i++)
-          { utf8_decode (&state, &codepoint, vt->utf8_holding[i]); }
-        if (state != UTF8_ACCEPT)
-          {
-            /* otherwise mangle it so that it does */
-            vt->utf8_holding[0] &= 127;
-            vt->utf8_holding[1] = 0;
-            if (vt->utf8_holding[0] == 0)
-              { vt->utf8_holding[0] = 32; }
-          }
-        _vt_add_str (vt, (char *) vt->utf8_holding);
-      }
-  }
-  else // ESCape
+  CtxSource *g = &rasterizer->state->gstate.source_fill;
+  float u0 = 0; float v0 = 0;
+  float ud = 0; float vd = 0;
+  ctx_init_uv (rasterizer, x0, count, &u0, &v0, &ud, &vd);
+  float radial_gradient_x0 = g->radial_gradient.x0;
+  float radial_gradient_y0 = g->radial_gradient.y0;
+  float radial_gradient_r0 = g->radial_gradient.r0;
+  float radial_gradient_rdelta = g->radial_gradient.rdelta;
+#if CTX_DITHER
+  int dither_red_blue = rasterizer->format->dither_red_blue;
+  int dither_green = rasterizer->format->dither_green;
+#endif
+  int fudge = ((size_t)(dst) & 31);
+#if CTX_AVX2
+  alignas(32)
+#endif
+  uint8_t _tsrc[4 * (count + fudge)];
+  uint8_t *tsrc = &_tsrc[fudge];
+  for (int x = 0; x < count ; x++)
   {
-    vt->state = vt_state_esc;
+      float vv = ctx_hypotf (radial_gradient_x0 - u0, radial_gradient_y0 - v0);
+            vv = (vv - radial_gradient_r0) * (radial_gradient_rdelta);
+#if CTX_GRADIENT_CACHE
+      uint32_t *tsrcp = (uint32_t*)tsrc;
+      uint32_t *cp = ((uint32_t*)(&ctx_gradient_cache_u8_a[ctx_grad_index(vv)][0]));
+      *tsrcp = *cp;
+#else
+      ctx_fragment_gradient_1d_RGBA8 (rasterizer, vv, 1.0, tsrc);
+      ctx_RGBA8_associate_alpha (tsrc);
+#endif
+
+#if CTX_DITHER
+      ctx_dither_rgba_u8 (tsrc, u0, v0, dither_red_blue, dither_green);
+#endif
+
+    tsrc += 4;
+    u0 += ud;
+    v0 += vd;
   }
+
+  CTX_COMPOSITE_SUFFIX(ctx_RGBA8_source_over_normal_buf) (rasterizer,
+               dst, src, x0, coverage, count, &_tsrc[fudge]);
 }
 
-int vt_poll (VT *vt, int timeout)
+
+#endif
+#endif
+
+static void
+CTX_COMPOSITE_SUFFIX(ctx_RGBA8_source_over_normal_color) (CTX_COMPOSITE_ARGUMENTS)
 {
-  if (!vt) return 0;
-  int read_size = sizeof (vt->buf);
-  int got_data = 0;
+#if 0
+  ctx_u8_source_over_normal_color (4, rasterizer, dst, src, clip, x0, coverage, count);
+  return;
+#endif
+  {
+    uint8_t tsrc[4];
+    memcpy (tsrc, src, 4);
+    ctx_RGBA8_associate_alpha (tsrc);
+    uint8_t a = src[3];
+    int x = 0;
 
-  // read_size 1m1.142s
-  // read_size*10  52s
-  // read_size*5   53.8s
-  // read_size*4   53.78s
-  // read_size*3   .....s
-  // read_size*2   56.99s
-  int remaining_chars = read_size * 3;// * 100;
-  int len = 0;
-  vt_audio_task (vt, 0);
-#if 1
-  if (vt->cursor_visible && vt->smooth_scroll)
-    {
-      remaining_chars = vt->cols / 2;
-    }
+#if CTX_AVX2
+    if ((size_t)(dst) & 31)
 #endif
-  read_size = MIN (read_size, remaining_chars);
-  long start_ticks = ctx_ticks ();
-  long ticks = start_ticks;
-  while (remaining_chars > 0 &&
-         vt_waitdata (vt, 0) &&
-         ( ticks - start_ticks < timeout ||  vt->state == vt_state_ctx))
     {
-  if (vt->in_smooth_scroll)
+      uint32_t *sip = ((uint32_t*)(tsrc));
+      uint32_t si = *sip;
+      uint64_t si_ga = si & CTX_RGBA8_GA_MASK;
+      uint32_t si_rb = si & CTX_RGBA8_RB_MASK;
+      if (a==255)
+      {
+
+      for (; (x < count) 
+#if CTX_AVX2
+                      && ((size_t)(dst)&31)
+#endif
+                      ; 
+                      x++)
     {
-      remaining_chars = 1;
-      // XXX : need a bail condition -
-      // /// so that we can stop accepting data until autowrap or similar
-    }
-      len = vt_read (vt, vt->buf, read_size);
-      if (len >0)
+      int cov = coverage[0];
+      if (cov)
       {
-     // fwrite (vt->buf, len, 1, vt->log);
-     // fwrite (vt->buf, len, 1, stdout);
-      }
-      for (int i = 0; i < len; i++)
-        { vt->state (vt, vt->buf[i]); }
-      // XXX allow state to break out in ctx mode on flush
-      got_data+=len;
-      remaining_chars -= len;
-      if (vt->state == vt_state_ctx) {
-         if (remaining_chars < read_size)
-         {
-           remaining_chars = read_size * 2;
-         }
+        int r_cov = 255-cov;
+        uint32_t di = *((uint32_t*)(dst));
+        uint64_t di_ga = di & CTX_RGBA8_GA_MASK;
+        uint32_t di_rb = di & CTX_RGBA8_RB_MASK;
+        *((uint32_t*)(dst)) = 
+         (((si_rb * cov + di_rb * r_cov) >> 8) & CTX_RGBA8_RB_MASK) |
+         (((si_ga * cov + di_ga * r_cov) >> 8) & CTX_RGBA8_GA_MASK);
       }
-      vt_audio_task (vt, 0);
-      ticks = ctx_ticks ();
+      dst += 4;
+      coverage ++;
     }
-  if (got_data < 0)
+  }
+    else
     {
-      if (kill (vt->vtpty.pid, 0) != 0)
-        {
-          vt->vtpty.done = 1;
-        }
+      int si_a = si >> CTX_RGBA8_A_SHIFT;
+      for (; (x < count) 
+#if CTX_AVX2
+                      && ((size_t)(dst)&31)
+#endif
+                      ; 
+                      x++)
+      {
+        int cov = *coverage;
+        if (cov)
+        {
+          uint32_t di = *((uint32_t*)(dst));
+          uint64_t di_ga = di & CTX_RGBA8_GA_MASK;
+          uint32_t di_rb = di & CTX_RGBA8_RB_MASK;
+          int ir_cov_si_a = 255-((cov*si_a)>>8);
+          *((uint32_t*)(dst)) = 
+           (((si_rb * cov + di_rb * ir_cov_si_a) >> 8) & CTX_RGBA8_RB_MASK) |
+           (((si_ga * cov + di_ga * ir_cov_si_a) >> 8) & CTX_RGBA8_GA_MASK);
+        }
+        dst += 4;
+        coverage ++;
+      }
+    }
     }
-  return got_data;
-}
 
-/******/
+#if CTX_AVX2
+                    
+    __m256i xsrc = _mm256_set1_epi32( *((uint32_t*)tsrc)) ;
+    __m256i lo_mask = _mm256_set1_epi32 (0x00FF00FF);
+    __m256i hi_mask = _mm256_set1_epi32 (0xFF00FF00);
+    __m256i x00ff =   _mm256_set1_epi16(255);
+    __m256i x0101 =   _mm256_set1_epi16(0x0101);
+    __m256i x0080 =   _mm256_set1_epi16(0x0080);
 
-static const char *keymap_vt52[][2]=
-{
-  {"up",    "\033A" },
-  {"down",  "\033B" },
-  {"right", "\033C" },
-  {"left",  "\033D" },
-};
+    int trailer = (count - x) % 8 + 1;
 
-static const char *keymap_application[][2]=
-{
-  {"up",    "\033OA" },
-  {"down",  "\033OB" },
-  {"right", "\033OC" },
-  {"left",  "\033OD" },
-};
+    for (; x <= count-trailer; x+=8)
+    {
+      __m256i xcov;
+      __m256i x1_minus_cov_mul_a;
+     
+     if (((uint64_t*)(coverage))[0])
+     {
+       if (CTX_LIKELY(((uint64_t*)(coverage))[0] != 0xffffffffffffffff))
+       {
+         xcov  = _mm256_set_epi32(coverage[7],
+                                  coverage[6],
+                                  coverage[5],
+                                  coverage[4],
+                                  coverage[3],
+                                  coverage[2],
+                                  coverage[1],
+                                  coverage[0]
+                                  );
+         xcov |= xcov << 16;
+
+        x1_minus_cov_mul_a = 
+           _mm256_sub_epi16(x00ff, _mm256_mulhi_epu16 (
+                   _mm256_adds_epu16 (_mm256_mullo_epi16(xcov,
+                                      _mm256_set1_epi16(a)), x0080), x0101));
+       }
+       else
+       {
+          if (a == 255)
+          {
+            _mm256_store_si256((__m256i*)dst, xsrc);
+            dst += 4 * 8;
+            coverage += 8;
+            continue;
+          }
+
+          xcov = x00ff;
+          x1_minus_cov_mul_a = _mm256_set1_epi16(255-a);
+       }
+      __m256i xdst   = _mm256_load_si256((__m256i*)(dst));
+      __m256i dst_lo = _mm256_and_si256 (xdst, lo_mask);
+      __m256i dst_hi = _mm256_srli_epi16 (_mm256_and_si256 (xdst, hi_mask), 8);
+      __m256i src_lo = _mm256_and_si256 (xsrc, lo_mask);
+      __m256i src_hi = _mm256_srli_epi16 (_mm256_and_si256 (xsrc, hi_mask), 8);
+        
+      dst_hi  = _mm256_mulhi_epu16(_mm256_adds_epu16(_mm256_mullo_epi16(dst_hi,  x1_minus_cov_mul_a), 
x0080), x0101);
+      dst_lo  = _mm256_mulhi_epu16(_mm256_adds_epu16(_mm256_mullo_epi16(dst_lo,  x1_minus_cov_mul_a), 
x0080), x0101);
 
-static const char *keymap_general[][2]=
-{
-  {"up",             "\033[A"},
-  {"down",           "\033[B"},
-  {"right",          "\033[C"},
-  {"left",           "\033[D"},
-  {"end",            "\033[F"},
-  {"home",           "\033[H"},
-  {"shift-up",       "\033[1;2A"},
-  {"shift-down",     "\033[1;2B"},
-  {"shift-right",    "\033[1;2C"},
-  {"shift-left",     "\033[1;2D"},
-  {"alt-a",          "\033a"},
-  {"alt-b",          "\033b"},
-  {"alt-c",          "\033c"},
-  {"alt-d",          "\033d"},
-  {"alt-e",          "\033e"},
-  {"alt-f",          "\033f"},
-  {"alt-g",          "\033g"},
-  {"alt-h",          "\033h"},
-  {"alt-i",          "\033i"},
-  {"alt-j",          "\033j"},
-  {"alt-k",          "\033k"},
-  {"alt-l",          "\033l"},
-  {"alt-m",          "\033m"},
-  {"alt-n",          "\033n"},
-  {"alt-o",          "\033o"},
-  {"alt-p",          "\033p"},
-  {"alt-q",          "\033q"},
-  {"alt-r",          "\033r"},
-  {"alt-s",          "\033s"},
-  {"alt-t",          "\033t"},
-  {"alt-u",          "\033u"},
-  {"alt-v",          "\033v"},
-  {"alt-w",          "\033w"},
-  {"alt-x",          "\033x"},
-  {"alt-y",          "\033y"},
-  {"alt-z",          "\033z"},
-  {"alt- ",          "\033 "},
-  {"alt-space",      "\033 "},
-  {"alt-0",          "\0330"},
-  {"alt-1",          "\0331"},
-  {"alt-2",          "\0332"},
-  {"alt-3",          "\0333"},
-  {"alt-4",          "\0334"},
-  {"alt-5",          "\0335"},
-  {"alt-6",          "\0336"},
-  {"alt-7",          "\0337"},
-  {"alt-8",          "\0338"},
-  {"alt-9",          "\0339"},
-  {"alt-return",     "\033\r"},
-  {"alt-backspace",  "\033\177"},
-  {"alt-up",         "\033[1;3A"},
-  {"alt-down",       "\033[1;3B"},
-  {"alt-right",      "\033[1;3C"},
-  {"alt-left",       "\033[1;3D"},
-  {"shift-alt-up",   "\033[1;4A"},
-  {"shift-alt-down", "\033[1;4B"},
-  {"shift-alt-right","\033[1;4C"},
-  {"shift-alt-left", "\033[1;4D"},
-  {"control-space",  "\000"},
-  {"control-up",     "\033[1;5A"},
-  {"control-down",   "\033[1;5B"},
-  {"control-right",  "\033[1;5C"},
-  {"control-left",   "\033[1;5D"},
-  {"shift-control-up",    "\033[1;6A"},
-  {"shift-control-down",  "\033[1;6B"},
-  {"shift-control-right", "\033[1;6C"},
-  {"shift-control-left",  "\033[1;6D"},
-  {"insert",         "\033[2~"},
-  {"delete",         "\033[3~"},
-  {"control-delete", "\033[3,5~"},
-  {"shift-delete",   "\033[3,2~"},
-  {"control-shift-delete",  "\033[3,6~"},
-  {"page-up",        "\033[5~"},
-  {"page-down",     "\033[6~"},
-  {"return",         "\r"},
-  {"shift-tab",      "\033Z"},
-  {"shift-return",   "\r"},
-  {"control-return", "\r"},
-  {"space",          " "},
-  {"shift-space",    " "},
-  {"control-a",      "\001"},
-  {"control-b",      "\002"},
-  {"control-c",      "\003"},
-  {"control-d",      "\004"},
-  {"control-e",      "\005"},
-  {"control-f",      "\006"},
-  {"control-g",      "\007"},
-  {"control-h",      "\010"},
-  {"control-i",      "\011"},
-  {"control-j",      "\012"},
-  {"control-k",      "\013"},
-  {"control-l",      "\014"},
-  {"control-m",      "\015"},
-  {"control-n",      "\016"},
-  {"control-o",      "\017"},
-  {"control-p",      "\020"},
-  {"control-q",      "\021"},
-  {"control-r",      "\022"},
-  {"control-s",      "\023"},
-  {"control-t",      "\024"},
-  {"control-u",      "\025"},
-  {"control-v",      "\026"},
-  {"control-w",      "\027"},
-  {"control-x",      "\030"},
-  {"control-y",      "\031"},
-  {"control-z",      "\032"},
-  {"escape",         "\033"},
-  {"tab",            "\t"},
-  {"backspace",      "\177"},
-  {"control-backspace", "\177"},
-  {"shift-backspace","\177"},
-  {"shift-tab",      "\033[Z"},
-
-  {"control-F1",     "\033[>11~"},
-  {"control-F2",     "\033[>12~"},
-  {"control-F3",     "\033[>13~"},
-  {"control-F4",     "\033[>14~"},
-  {"control-F5",     "\033[>15~"},
-
-  {"shift-F1",       "\033[?11~"},
-  {"shift-F2",       "\033[?12~"},
-  {"shift-F3",       "\033[?13~"},
-  {"shift-F4",       "\033[?14~"},
-  {"shift-F5",       "\033[?15~"},
-
-  {"F1",             "\033[11~"},  // hold screen   // ESC O P
-  {"F2",             "\033[12~"},  // print screen  //       Q
-  {"F3",             "\033[13~"},  // set-up                 R
-  {"F4",             "\033[14~"},  // data/talk              S
-  {"F5",             "\033[15~"},  // break
-  {"F6",             "\033[17~"},
-  {"F7",             "\033[18~"},
-  {"F8",             "\033[19~"},
-  {"F9",             "\033[20~"},
-  {"F10",            "\033[21~"},
-  {"F11",            "\033[22~"},
-  {"F12",            "\033[23~"},
-  {"control-/",       "\037"},
-  {"shift-control-/", "\037"},
-  {"control-[",       "\033"},
-  {"control-]",       "\035"},
-  {"shift-control-[", "\033"},
-  {"shift-control-]", "\031"},
-  {"shift-control-`", "\036"},
-  {"control-'",       "'"},
-  {"shift-control-'", "'"},
-  {"control-;",       ";"},
-  {"shift-control-;", ";"},
-  {"control-.",       "."},
-  {"shift-control-.", "."},
-  {"control-,",       ","},
-  {"shift-control-,", ","},
-  {"control-\\",      "\034"},
-  {"control-1",       "1"},
-  {"control-3",       "\033"},
-  {"control-4",       "\034"},
-  {"control-5",       "\035"},
-  {"control-6",       "\036"},
-  {"shift-control-6", "\036"},
-  {"control-7",       "\037"},
-  {"shift-control-7", "\036"},
-  {"control-8",       "\177"},
-  {"control-9",       "9"},
+      src_lo  = _mm256_mulhi_epu16(_mm256_adds_epu16(_mm256_mullo_epi16(src_lo, xcov), x0080), x0101);
+      src_hi  = _mm256_mulhi_epu16(_mm256_adds_epu16(_mm256_mullo_epi16(src_hi,  xcov), x0080), x0101);
 
+      dst_hi = _mm256_adds_epu16(dst_hi, src_hi);
+      dst_lo = _mm256_adds_epu16(dst_lo, src_lo);
 
-};
+      _mm256_store_si256((__m256i*)dst, _mm256_slli_epi16 (dst_hi,8)|dst_lo);
+     }
 
-void ctx_client_lock (CtxClient *client);
-void ctx_client_unlock (CtxClient *client);
+      dst += 4 * 8;
+      coverage += 8;
+    }
 
-void vt_feed_keystring (VT *vt, CtxEvent *event, const char *str)
-{
-  if (vt->ctx_events)
-  {
-    if (!strcmp (str, "control-l") )
+    if (x < count)
     {
-      vt->ctx_events = 0;
-      return;
+      uint32_t *sip = ((uint32_t*)(tsrc));
+      uint32_t si = *sip;
+      uint64_t si_ga = si & CTX_RGBA8_GA_MASK;
+      uint32_t si_rb = si & CTX_RGBA8_RB_MASK;
+      int si_a = si >> CTX_RGBA8_A_SHIFT;
+      for (; x < count; x++)
+      {
+        int cov = *coverage;
+        if (cov)
+        {
+          uint32_t *dip = ((uint32_t*)(dst));
+          uint32_t di = *dip;
+          uint64_t di_ga = di & CTX_RGBA8_GA_MASK;
+          uint32_t di_rb = di & CTX_RGBA8_RB_MASK;
+          int ir_cov_si_a = 255-((cov*si_a)>>8);
+          *((uint32_t*)(dst)) = 
+           (((si_rb * cov + di_rb * ir_cov_si_a) >> 8) & CTX_RGBA8_RB_MASK) |
+           (((si_ga * cov + di_ga * ir_cov_si_a) >> 8) & CTX_RGBA8_GA_MASK);
+        }
+        dst      += 4;
+        coverage ++;
+      }
     }
-    vt_write (vt, str, strlen (str) );
-    vt_write (vt, "\n", 1);
-    return;
+#endif
   }
-  if (!strncmp (str, "keyup",   5)) return;
-  if (!strncmp (str, "keydown", 7)) return;
+}
+
 
-  if (!strcmp (str, "capslock")) return;
 
+static void
+CTX_COMPOSITE_SUFFIX(ctx_RGBA8_source_over_normal_color_solid) (CTX_COMPOSITE_ARGUMENTS)
+{
 #if 0
-  if (!strstr (str, "-page"))
-    vt_set_scroll (vt, 0);
+  ctx_u8_source_over_normal_color (4, rasterizer, dst, src, clip, x0, coverage, count);
+  return;
 #endif
+    uint8_t *tsrc = src;
+    int x = 0;
 
-  if (!strcmp (str, "idle") )
-     return;
-  else if (!strcmp (str, "shift-control-home"))
-    {
-      vt_set_scroll (vt, vt->scrollback_count);
-      vt_rev_inc (vt);
-      return;
-    }
-  else if (!strcmp (str, "shift-control-end"))
-    {
-      int new_scroll = 0;
-      vt_set_scroll (vt, new_scroll);
-      vt_rev_inc (vt);
-      return;
-    }
-  else if (!strcmp (str, "shift-control-down"))
-    {
-      int new_scroll = vt_get_scroll (vt) - 1;
-      vt_set_scroll (vt, new_scroll);
-      vt_rev_inc (vt);
-      return;
-    }
-  else if (!strcmp (str, "shift-control-up"))
-    {
-      int new_scroll = vt_get_scroll (vt) + 1;
-      vt_set_scroll (vt, new_scroll);
-      vt_rev_inc (vt);
-      return;
-    }
-  else if (!strcmp (str, "shift-page-up") ||
-           !strcmp (str, "shift-control-page-up"))
-    {
-      int new_scroll = vt_get_scroll (vt) + vt_get_rows (vt) /2;
-      vt_set_scroll (vt, new_scroll);
-      vt_rev_inc (vt);
-      return;
-    }
-  else if (!strcmp (str, "shift-page-down") ||
-           !strcmp (str, "shift-control-page-down"))
-    {
-      int new_scroll = vt_get_scroll (vt) - vt_get_rows (vt) /2;
-      if (new_scroll < 0) { new_scroll = 0; }
-      vt_set_scroll (vt, new_scroll);
-      vt_rev_inc (vt);
-      return;
-    }
-  else if (!strcmp (str, "shift-control--") ||
-           !strcmp (str, "control--") )
-    {
-      float font_size = vt_get_font_size (vt);
-      //font_size /= 1.15;
-      font_size -=2;//= roundf (font_size);
-      if (font_size < 2) { font_size = 2; }
-      vt_set_font_size (vt, font_size);
-      vt_set_px_size (vt, vt->width, vt->height);
-      return;
-    }
-  else if (!strcmp (str, "shift-control-=") ||
-           !strcmp (str, "control-=") )
-    {
-      float font_size = vt_get_font_size (vt);
-      float old = font_size;
-      //font_size *= 1.15;
-      //
-      //font_size = roundf (font_size);
-      font_size+=2;
-
-      if (old == font_size) { font_size = old+1; }
-      if (font_size > 200) { font_size = 200; }
-      vt_set_font_size (vt, font_size);
-      vt_set_px_size (vt, vt->width, vt->height);
-
-      return;
-    }
-  else if (!strcmp (str, "shift-control-r") )
-    {
-      vt_open_log (vt, "/tmp/ctx-vt");
-      return;
-    }
-  else if (!strcmp (str, "shift-control-l") )
+#if CTX_AVX2
+    if ((size_t)(dst) & 31)
+#endif
     {
-      vt_set_local (vt, !vt_get_local (vt) );
-      return;
-    }
-  else if (!strncmp (str, "mouse-", 5) )
+      uint32_t *sip = ((uint32_t*)(tsrc));
+      uint32_t si = *sip;
+      uint64_t si_ga = si & CTX_RGBA8_GA_MASK;
+      uint32_t si_rb = si & CTX_RGBA8_RB_MASK;
+      for (; (x < count) 
+#if CTX_AVX2
+                      && ((size_t)(dst)&31)
+#endif
+                      ; 
+                      x++)
     {
-      int cw = vt_cw (vt);
-      int ch = vt_ch (vt);
-      if (!strncmp (str + 6, "motion", 6) )
-        {
-          int x = 0, y = 0;
-          char *s = strchr (str, ' ');
-          if (s)
-            {
-              x = atoi (s);
-              s = strchr (s + 1, ' ');
-              if (s)
-                {
-                  y = atoi (s);
-                  vt_mouse (vt, event, VT_MOUSE_MOTION, 1, x/cw + 1, y/ch + 1, x, y);
-                }
-            }
-        }
-      else if (!strncmp (str + 6, "press", 5) )
-        {
-          int x = 0, y = 0, b = 0;
-          char *s = strchr (str, ' ');
-          if (s)
-            {
-              x = atoi (s);
-              s = strchr (s + 1, ' ');
-              if (s)
-                {
-                  y = atoi (s);
-                  s = strchr (s + 1, ' ');
-                  if (s)
-                  {
-                    b = atoi (s);
-                  }
-                  vt_mouse (vt, event, VT_MOUSE_PRESS, b, x/cw + 1, y/ch + 1, x, y);
-                }
-            }
-          //clients[active].drawn_rev = 0;
-        }
-      else if (!strncmp (str + 6, "drag", 4) )
-        {
-          int x = 0, y = 0, b = 0; // XXX initialize B
-          char *s = strchr (str, ' ');
-          if (s)
-            {
-              x = atoi (s);
-              s = strchr (s + 1, ' ');
-              if (s)
-                {
-                  y = atoi (s);
-                  if (s)
-                  {
-                    b = atoi (s);
-                  }
-                  vt_mouse (vt, event, VT_MOUSE_DRAG, b, x/cw + 1, y/ch + 1, x, y);
-                }
-            }
-          //clients[active].drawn_rev = 0;
-        }
-      else if (!strncmp (str + 6, "release", 7) )
-        {
-          int x = 0, y = 0, b = 0;
-          char *s = strchr (str, ' ');
-          if (s)
-            {
-              x = atoi (s);
-              s = strchr (s + 1, ' ');
-              if (s)
-                {
-                  y = atoi (s);
-                  s = strchr (s + 1, ' ');
-                  if (s)
-                  {
-                    b = atoi (s);
-                  }
-                  vt_mouse (vt, event, VT_MOUSE_RELEASE, b, x/cw + 1, y/ch + 1, x, y);
-                }
-            }
-          //clients[active].drawn_rev = 0;
-          // queue-draw
-        }
-      return;
+      int cov = coverage[0];
+      if (cov)
+      {
+        int r_cov = 255-cov;
+        uint32_t *dip = ((uint32_t*)(dst));
+        uint32_t di = *dip;
+        uint64_t di_ga = di & CTX_RGBA8_GA_MASK;
+        uint32_t di_rb = di & CTX_RGBA8_RB_MASK;
+        *((uint32_t*)(dst)) = 
+         (((si_rb * cov + di_rb * r_cov) >> 8) & CTX_RGBA8_RB_MASK) |
+         (((si_ga * cov + di_ga * r_cov) >> 8) & CTX_RGBA8_GA_MASK);
+      }
+      dst += 4;
+      coverage ++;
     }
-
-  if (vt->scroll_on_input)
-  {
-    vt->scroll = 0.0;
   }
 
-
-  if (vt->state == vt_state_vt52)
-    {
-      for (unsigned int i = 0; i<sizeof (keymap_vt52) /sizeof (keymap_vt52[0]); i++)
-        if (!strcmp (str, keymap_vt52[i][0]) )
-          { str = keymap_vt52[i][1]; goto done; }
-    }
-  else
+#if CTX_AVX2
+                    
+    __m256i xsrc = _mm256_set1_epi32( *((uint32_t*)tsrc)) ;
+    int trailer = (count - x) % 8 + 1;
+    for (; x < count-trailer; x+=8)
     {
-      if (vt->cursor_key_application)
-        {
-          for (unsigned int i = 0; i<sizeof (keymap_application) /sizeof (keymap_application[0]); i++)
-            if (!strcmp (str, keymap_application[i][0]) )
-              { str = keymap_application[i][1]; goto done; }
-        }
-    }
+      __m256i xcov;
+      __m256i x1_minus_cov_mul_a;
+    __m256i lo_mask = _mm256_set1_epi32 (0x00FF00FF);
+    __m256i hi_mask = _mm256_set1_epi32 (0xFF00FF00);
+    __m256i x00ff =   _mm256_set1_epi16(255);
+    __m256i x0101 =   _mm256_set1_epi16(0x0101);
+    __m256i x0080 =   _mm256_set1_epi16(0x0080);
+     uint64_t cov = ((uint64_t*)(coverage))[0];
+     if (cov)
+     {
+       if (CTX_UNLIKELY(cov == 0xffffffffffffffff))
+       {
+         _mm256_store_si256((__m256i*)dst, xsrc);
+       }
+       else
+       {
+          __m256i xdst   = _mm256_load_si256((__m256i*)(dst));
+          __m256i dst_lo = _mm256_and_si256 (xdst, lo_mask);
+          __m256i dst_hi = _mm256_srli_epi16 (_mm256_and_si256 (xdst, hi_mask), 8);
+          __m256i src_lo = _mm256_and_si256 (xsrc, lo_mask);
+          __m256i src_hi = _mm256_srli_epi16 (_mm256_and_si256 (xsrc, hi_mask), 8);
+            
+          xcov  = _mm256_set_epi32(coverage[7],
+                                   coverage[6],
+                                   coverage[5],
+                                   coverage[4],
+                                   coverage[3],
+                                   coverage[2],
+                                   coverage[1],
+                                   coverage[0]
+                                   );
+          xcov |= xcov << 16;
 
+          x1_minus_cov_mul_a = _mm256_sub_epi16(x00ff, xcov);
+    
+          dst_hi  = _mm256_mulhi_epu16(_mm256_adds_epu16(_mm256_mullo_epi16(dst_hi,  x1_minus_cov_mul_a), 
x0080), x0101);
+          dst_lo  = _mm256_mulhi_epu16(_mm256_adds_epu16(_mm256_mullo_epi16(dst_lo,  x1_minus_cov_mul_a), 
x0080), x0101);
+    
+          src_lo  = _mm256_mulhi_epu16(_mm256_adds_epu16(_mm256_mullo_epi16(src_lo, xcov), x0080), x0101);
+          src_hi  = _mm256_mulhi_epu16(_mm256_adds_epu16(_mm256_mullo_epi16(src_hi,  xcov), x0080), x0101);
+    
+          dst_hi = _mm256_adds_epu16(dst_hi, src_hi);
+          dst_lo = _mm256_adds_epu16(dst_lo, src_lo);
+    
+          _mm256_store_si256((__m256i*)dst, _mm256_slli_epi16 (dst_hi,8)|dst_lo);
+        }
+      }
 
-  if (!strcmp (str, "return") )
-    {
-      if (vt->cr_on_lf)
-        { str = "\r\n"; }
-      else
-        { str = "\r"; }
-      goto done;
+      dst += 4 * 8;
+      coverage += 8;
     }
-  if (!strcmp (str, "control-space") ||
-      !strcmp (str, "control-`") ||
-      !strcmp (str, "control-2") ||
-      !strcmp (str, "shift-control-2") ||
-      !strcmp (str, "shift-control-space") )
+
+    if (x < count)
     {
-      str = "\0\0";
-      vt_write (vt, str, 1);
-      return;
-    }
-  for (unsigned int i = 0; i< sizeof (keymap_general) /
-                              sizeof (keymap_general[0]); i++)
-    if (!strcmp (str, keymap_general[i][0]) )
+      uint32_t *sip = ((uint32_t*)(tsrc));
+      uint32_t si = *sip;
+      uint64_t si_ga = si & CTX_RGBA8_GA_MASK;
+      uint32_t si_rb = si & CTX_RGBA8_RB_MASK;
+      int si_a = si >> CTX_RGBA8_A_SHIFT;
+      for (; x < count; x++)
       {
-        str = keymap_general[i][1];
-        break;
+        int cov = *coverage;
+        if (cov)
+        {
+          uint32_t *dip = ((uint32_t*)(dst));
+          uint32_t di = *dip;
+          uint64_t di_ga = di & CTX_RGBA8_GA_MASK;
+          uint32_t di_rb = di & CTX_RGBA8_RB_MASK;
+          int ir_cov_si_a = 255-((cov*si_a)>>8);
+          *((uint32_t*)(dst)) = 
+           (((si_rb * cov + di_rb * ir_cov_si_a) >> 8) & CTX_RGBA8_RB_MASK) |
+           (((si_ga * cov + di_ga * ir_cov_si_a) >> 8) & CTX_RGBA8_GA_MASK);
+        }
+        dst += 4;
+        coverage ++;
       }
-done:
-  if (strlen (str) )
-    {
-      if (vt->local_editing)
-        {
-          for (int i = 0; str[i]; i++)
-            {
-              vt->state (vt, str[i]);
-            }
-        }
-      else
-        {
-          vt_write (vt, str, strlen (str) );
-        }
     }
+#endif
 }
 
-void vt_paste (VT *vt, const char *str)
+static void
+CTX_COMPOSITE_SUFFIX(ctx_RGBA8_copy_normal) (CTX_COMPOSITE_ARGUMENTS)
 {
-  if (vt->bracket_paste)
-    {
-      vt_write (vt, "\033[200~", 6);
-    }
-  vt_feed_keystring (vt, NULL, str);
-  if (vt->bracket_paste)
-    {
-      vt_write (vt, "\033[201~", 6);
-    }
+  ctx_u8_copy_normal (4, rasterizer, dst, src, x0, coverage, count);
 }
 
-const char *vt_find_shell_command (void)
+static void
+ctx_RGBA8_clear_normal (CTX_COMPOSITE_ARGUMENTS)
 {
-  if (access ("/.flatpak-info", F_OK) != -1)
-  {
-    static char ret[512];
-    char buf[256];
-    FILE *fp = popen("flatpak-spawn --host getent passwd $USER|cut -f 7 -d :", "r");
-    if (fp)
-    {
-      while (fgets (buf, sizeof(buf), fp) != NULL)
-      {
-        if (buf[strlen(buf)-1]=='\n')
-          buf[strlen(buf)-1]=0;
-        sprintf (ret, "flatpak-spawn --env=TERM=xterm --host %s", buf);
-      }
-      pclose (fp);
-      return ret;
-    }
-  }
-
-  if (getenv ("SHELL"))
-  {
-    return getenv ("SHELL");
-  }
-  int i;
-  const char *command = NULL;
-  struct stat stat_buf;
-  static char *alts[][2] =
-  {
-    {"/bin/bash",     "/bin/bash"},
-    {"/usr/bin/bash", "/usr/bin/bash"},
-    {"/bin/sh",       "/bin/sh"},
-    {"/usr/bin/sh",   "/usr/bin/sh"},
-    {NULL, NULL}
-  };
-  for (i = 0; alts[i][0] && !command; i++)
-    {
-      lstat (alts[i][0], &stat_buf);
-      if (S_ISREG (stat_buf.st_mode) || S_ISLNK (stat_buf.st_mode) )
-        { command = alts[i][1]; }
-    }
-  return command;
+  ctx_u8_clear_normal (4, rasterizer, dst, src, x0, coverage, count);
 }
 
-static char *string_chop_head (char *orig) /* return pointer to reset after arg */
+static void
+ctx_u8_blend_normal (int components, uint8_t * __restrict__ dst, uint8_t *src, uint8_t *blended)
 {
-  int j=0;
-  int eat=0; /* number of chars to eat at start */
-
-  if(orig)
-    {
-      int got_more;
-      char *o = orig;
-      while(o[j] == ' ')
-        {j++;eat++;}
-
-      if (o[j]=='"')
-        {
-          eat++;j++;
-          while(o[j] != '"' &&
-                o[j] != 0)
-            j++;
-          o[j]='\0';
-          j++;
-        }
-      else if (o[j]=='\'')
-        {
-          eat++;j++;
-          while(o[j] != '\'' &&
-                o[j] != 0)
-            j++;
-          o[j]='\0';
-          j++;
-        }
-      else
-        {
-          while(o[j] != ' ' &&
-                o[j] != 0 &&
-                o[j] != ';')
-            j++;
-        }
-      if (o[j] == 0 ||
-          o[j] == ';')
-        got_more = 0;
-      else
-        got_more = 1;
-      o[j]=0; /* XXX: this is where foo;bar won't work but foo ;bar works*/
-
-      if(eat)
+  switch (components)
+  {
+     case 3:
+       ((uint8_t*)(blended))[2] = ((uint8_t*)(src))[2];
+      /* FALLTHROUGH */
+     case 2:
+       *((uint16_t*)(blended)) = *((uint16_t*)(src));
+       ctx_u8_associate_alpha (components, blended);
+       break;
+     case 5:
+       ((uint8_t*)(blended))[4] = ((uint8_t*)(src))[4];
+       /* FALLTHROUGH */
+     case 4:
+       *((uint32_t*)(blended)) = *((uint32_t*)(src));
+       ctx_u8_associate_alpha (components, blended);
+       break;
+     default:
        {
-         int k;
-         for (k=0; k<j-eat; k++)
-           orig[k] = orig[k+eat];
+         uint8_t alpha = src[components-1];
+         for (int i = 0; i<components - 1;i++)
+           blended[i] = (src[i] * alpha)/255;
+         blended[components-1]=alpha;
        }
-      if (got_more)
-        return &orig[j+1];
-    }
-  return NULL;
+       break;
+  }
 }
 
-void _ctx_add_listen_fd (int fd);
-void _ctx_remove_listen_fd (int fd);
-
-static pid_t
-vt_forkpty (int  *amaster,
-            char *aname,
-            const struct termios *termp,
-            const struct winsize *winsize)
+/* branchless 8bit add that maxes out at 255 */
+static inline uint8_t ctx_sadd8(uint8_t a, uint8_t b)
 {
-  pid_t pid;
-  int master = posix_openpt (O_RDWR|O_NOCTTY);
-  int slave;
+  uint16_t s = (uint16_t)a+b;
+  return -(s>>8) | (uint8_t)s;
+}
 
-  if (master < 0)
-    return -1;
-  if (grantpt (master) != 0)
-    return -1;
-  if (unlockpt (master) != 0)
-    return -1;
-#if 0
-  char name[1024];
-  if (ptsname_r (master, name, sizeof(name)-1))
-    return -1;
-#else
-  char *name = NULL;
-  if ((name = ptsname (master)) == NULL)
-    return -1;
-#endif
+#if CTX_BLENDING_AND_COMPOSITING
 
-  slave = open(name, O_RDWR|O_NOCTTY);
+#define ctx_u8_blend_define(name, CODE) \
+static void \
+ctx_u8_blend_##name (int components, uint8_t * __restrict__ dst, uint8_t *src, uint8_t *blended)\
+{\
+  uint8_t *s=src; uint8_t b[components];\
+  ctx_u8_deassociate_alpha (components, dst, b);\
+    CODE;\
+  blended[components-1] = src[components-1];\
+  ctx_u8_associate_alpha (components, blended);\
+}
 
-  if (termp)   tcsetattr(slave, TCSAFLUSH, termp);
-  if (winsize) ioctl(slave, TIOCSWINSZ, winsize);
+#define ctx_u8_blend_define_seperable(name, CODE) \
+        ctx_u8_blend_define(name, for (int c = 0; c < components-1; c++) { CODE ;}) \
 
-  pid = fork();
-  if (pid < 0)
+ctx_u8_blend_define_seperable(multiply,     blended[c] = (b[c] * s[c])/255;)
+ctx_u8_blend_define_seperable(screen,       blended[c] = s[c] + b[c] - (s[c] * b[c])/255;)
+ctx_u8_blend_define_seperable(overlay,      blended[c] = b[c] < 127 ? (s[c] * b[c])/255 :
+                                                         s[c] + b[c] - (s[c] * b[c])/255;)
+ctx_u8_blend_define_seperable(darken,       blended[c] = ctx_mini (b[c], s[c]))
+ctx_u8_blend_define_seperable(lighten,      blended[c] = ctx_maxi (b[c], s[c]))
+ctx_u8_blend_define_seperable(color_dodge,  blended[c] = b[c] == 0 ? 0 :
+                                     s[c] == 255 ? 255 : ctx_mini(255, (255 * b[c]) / (255-s[c])))
+ctx_u8_blend_define_seperable(color_burn,   blended[c] = b[c] == 1 ? 1 :
+                                     s[c] == 0 ? 0 : 255 - ctx_mini(255, (255*(255 - b[c])) / s[c]))
+ctx_u8_blend_define_seperable(hard_light,   blended[c] = s[c] < 127 ? (b[c] * s[c])/255 :
+                                                          b[c] + s[c] - (b[c] * s[c])/255;)
+ctx_u8_blend_define_seperable(difference,   blended[c] = (b[c] - s[c]))
+ctx_u8_blend_define_seperable(divide,       blended[c] = s[c]?(255 * b[c]) / s[c]:0)
+ctx_u8_blend_define_seperable(addition,     blended[c] = ctx_sadd8 (s[c], b[c]))
+ctx_u8_blend_define_seperable(subtract,     blended[c] = ctx_maxi(0, s[c]-b[c]))
+ctx_u8_blend_define_seperable(exclusion,    blended[c] = b[c] + s[c] - 2 * (b[c] * s[c]/255))
+ctx_u8_blend_define_seperable(soft_light,
+  if (s[c] <= 255/2)
   {
-    return pid;
-  } else if (pid == 0)
+    blended[c] = b[c] - (255 - 2 * s[c]) * b[c] * (255 - b[c]) / (255 * 255);
+  }
+  else
   {
-    close (master);
-    setsid ();
-    dup2 (slave, STDIN_FILENO);
-    dup2 (slave, STDOUT_FILENO);
-    dup2 (slave, STDERR_FILENO);
+    int d;
+    if (b[c] <= 255/4)
+      d = (((16 * b[c] - 12 * 255)/255 * b[c] + 4 * 255) * b[c])/255;
+    else
+      d = ctx_sqrtf(b[c]/255.0) * 255.4;
+    blended[c] = (b[c] + (2 * s[c] - 255) * (d - b[c]))/255;
+  }
+)
 
-    close (slave);
-    return 0;
+static int ctx_int_get_max (int components, int *c)
+{
+  int max = 0;
+  for (int i = 0; i < components - 1; i ++)
+  {
+    if (c[i] > max) max = c[i];
   }
-  ioctl (slave, TIOCSCTTY, NULL);
-  close (slave);
-  *amaster = master;
-  return pid;
+  return max;
 }
 
-static void vt_run_command (VT *vt, const char *command, const char *term)
+static int ctx_int_get_min (int components, int *c)
 {
-  struct winsize ws;
-  //signal (SIGCHLD,signal_child);
-#if 0
-  int was_pidone = (getpid () == 1);
-#else
-  int was_pidone = 0; // do no special treatment, all child processes belong
-                      // to root
-#endif
-  signal (SIGINT,SIG_DFL);
-  ws.ws_row = vt->rows;
-  ws.ws_col = vt->cols;
-  ws.ws_xpixel = ws.ws_col * vt->cw;
-  ws.ws_ypixel = ws.ws_row * vt->ch;
-  vt->vtpty.pid = vt_forkpty (&vt->vtpty.pty, NULL, NULL, &ws);
-  if (vt->vtpty.pid == 0)
-    {
-      int i;
-      if (was_pidone)
-      {
-        if (setuid(1000)) fprintf (stderr, "setuid failed\n");
-      }
-      else
-      {
-        for (i = 3; i<768; i++) { close (i); } /*hack, trying to close xcb */
-      }
-      unsetenv ("TERM");
-      unsetenv ("COLUMNS");
-      unsetenv ("LINES");
-      unsetenv ("TERMCAP");
-      unsetenv ("COLOR_TERM");
-      unsetenv ("COLORTERM");
-      unsetenv ("VTE_VERSION");
-      unsetenv ("CTX_BACKEND");
-      //setenv ("TERM", "ansi", 1);
-      //setenv ("TERM", "vt102", 1);
-      //setenv ("TERM", "vt100", 1);
-      // setenv ("TERM", term?term:"xterm", 1);
-      setenv ("TERM", term?term:"xterm-256color", 1);
-      setenv ("COLORTERM", "truecolor", 1);
-      //setenv ("CTX_VERSION", "0", 1);
-      setenv ("CTX_BACKEND", "ctx", 1); // speeds up launching of clients
-
-      {
-        char *cargv[32];
-        int   cargc;
-        char *rest, *copy;
-        copy = calloc (strlen (command)+2, 1);
-        strcpy (copy, command);
-        rest = copy;
-        cargc = 0;
-        while (rest && cargc < 30 && rest[0] != ';')
-        {
-          cargv[cargc++] = rest;
-          rest = string_chop_head (rest);
-        }
-        cargv[cargc] = NULL;
-        execvp (cargv[0], cargv);
-      }
-      exit (0);
-    }
-  else if (vt->vtpty.pid < 0)
-    {
-      VT_error ("forkpty failed (%s)", command);
-      return;
-    }
-  fcntl(vt->vtpty.pty, F_SETFL, O_NONBLOCK|O_NOCTTY);
-  _ctx_add_listen_fd (vt->vtpty.pty);
+  int min = 400;
+  for (int i = 0; i < components - 1; i ++)
+  {
+    if (c[i] < min) min = c[i];
+  }
+  return min;
 }
 
-void vt_destroy (VT *vt)
+static int ctx_int_get_lum (int components, int *c)
 {
-  while (vt->lines)
-    {
-      vt_line_free (vt->lines->data, 1);
-      ctx_list_remove (&vt->lines, vt->lines->data);
-      vt->line_count--;
-    }
-  while (vt->scrollback)
-    {
-      vt_line_free (vt->scrollback->data, 1);
-      ctx_list_remove (&vt->scrollback, vt->scrollback->data);
-    }
-  if (vt->ctxp)
-    ctx_parser_free (vt->ctxp);
-  //if (vt->ctx)
-  //  { ctx_free (vt->ctx); }
-  free (vt->argument_buf);
-  ctx_list_remove (&vts, vt);
-  kill (vt->vtpty.pid, 9);
-  _ctx_remove_listen_fd (vt->vtpty.pty);
-  close (vt->vtpty.pty);
-#if 1
-  if (vt->title)
-    free (vt->title);
-#endif
-  free (vt);
+  switch (components)
+  {
+    case 3:
+    case 4:
+            return CTX_CSS_RGB_TO_LUMINANCE(c);
+    case 1:
+    case 2:
+            return c[0];
+            break;
+    default:
+       {
+         int sum = 0;
+         for (int i = 0; i < components - 1; i ++)
+         {
+           sum += c[i];
+         }
+         return sum / (components - 1);
+       }
+            break;
+  }
 }
 
-int vt_get_line_count (VT *vt)
+static int ctx_u8_get_lum (int components, uint8_t *c)
 {
-  return vt->line_count;
+  switch (components)
+  {
+    case 3:
+    case 4:
+            return CTX_CSS_RGB_TO_LUMINANCE(c);
+    case 1:
+    case 2:
+            return c[0];
+            break;
+    default:
+       {
+         int sum = 0;
+         for (int i = 0; i < components - 1; i ++)
+         {
+           sum += c[i];
+         }
+         return sum / (components - 1);
+       }
+            break;
+  }
+}
+static int ctx_u8_get_sat (int components, uint8_t *c)
+{
+  switch (components)
+  {
+    case 3:
+    case 4:
+            { int r = c[0];
+              int g = c[1];
+              int b = c[2];
+              return ctx_maxi(r, ctx_maxi(g,b)) - ctx_mini(r,ctx_mini(g,b));
+            }
+            break;
+    case 1:
+    case 2:
+            return 0.0;
+            break;
+    default:
+       {
+         int min = 1000;
+         int max = -1000;
+         for (int i = 0; i < components - 1; i ++)
+         {
+           if (c[i] < min) min = c[i];
+           if (c[i] > max) max = c[i];
+         }
+         return max-min;
+       }
+       break;
+  }
 }
 
-const char *vt_get_line (VT *vt, int no)
+static void ctx_u8_set_lum (int components, uint8_t *c, uint8_t lum)
 {
-  if (no >= vt->rows)
+  int d = lum - ctx_u8_get_lum (components, c);
+  int tc[components];
+  for (int i = 0; i < components - 1; i++)
   {
-    CtxList *l = ctx_list_nth (vt->scrollback, no - vt->rows);
-    if (!l)
-      { 
-         return "";
-      }
-    CtxString *str = l->data;
-    return str->str;
+    tc[i] = c[i] + d;
   }
-  else
+
+  int l = ctx_int_get_lum (components, tc);
+  int n = ctx_int_get_min (components, tc);
+  int x = ctx_int_get_max (components, tc);
+
+  if (n < 0 && l!=n)
   {
-    CtxList *l = ctx_list_nth (vt->lines, no);
-    if (!l)
-      { 
-         return "-";
-      }
-    CtxString *str = l->data;
-    return str->str;
+    for (int i = 0; i < components - 1; i++)
+      tc[i] = l + (((tc[i] - l) * l) / (l-n));
+  }
+
+  if (x > 255 && x!=l)
+  {
+    for (int i = 0; i < components - 1; i++)
+      tc[i] = l + (((tc[i] - l) * (255 - l)) / (x-l));
   }
+  for (int i = 0; i < components - 1; i++)
+    c[i] = tc[i];
 }
 
-int vt_line_is_continuation (VT *vt, int no)
+static void ctx_u8_set_sat (int components, uint8_t *c, uint8_t sat)
 {
-  if (no >= vt->rows)
+  int max = 0, mid = 1, min = 2;
+  
+  if (c[min] > c[mid]){int t = min; min = mid; mid = t;}
+  if (c[mid] > c[max]){int t = mid; mid = max; max = t;}
+  if (c[min] > c[mid]){int t = min; min = mid; mid = t;}
+
+  if (c[max] > c[min])
   {
-    CtxList *l = ctx_list_nth (vt->scrollback, no - vt->rows);
-    if (!l)
-      { 
-         return 1;
-      }
-    VtLine *line = l->data;
-    return line->wrapped;
+    c[mid] = ((c[mid]-c[min]) * sat) / (c[max] - c[min]);
+    c[max] = sat;
   }
   else
   {
-    CtxList *l = ctx_list_nth (vt->lines, no);
-    if (!l)
-      { 
-         return 1;
-      }
-    VtLine *line = l->data;
-    return line->wrapped;
+    c[mid] = c[max] = 0;
   }
+  c[min] = 0;
 }
 
-int vt_get_cols (VT *vt)
-{
-  return vt->cols;
-}
+ctx_u8_blend_define(color,
+  for (int i = 0; i < components; i++)
+    blended[i] = s[i];
+  ctx_u8_set_lum(components, blended, ctx_u8_get_lum (components, s));
+)
+
+ctx_u8_blend_define(hue,
+  int in_sat = ctx_u8_get_sat(components, b);
+  int in_lum = ctx_u8_get_lum(components, b);
+  for (int i = 0; i < components; i++)
+    blended[i] = s[i];
+  ctx_u8_set_sat(components, blended, in_sat);
+  ctx_u8_set_lum(components, blended, in_lum);
+)
+
+ctx_u8_blend_define(saturation,
+  int in_sat = ctx_u8_get_sat(components, s);
+  int in_lum = ctx_u8_get_lum(components, b);
+  for (int i = 0; i < components; i++)
+    blended[i] = b[i];
+  ctx_u8_set_sat(components, blended, in_sat);
+  ctx_u8_set_lum(components, blended, in_lum);
+)
+
+ctx_u8_blend_define(luminosity,
+  int in_lum = ctx_u8_get_lum(components, s);
+  for (int i = 0; i < components; i++)
+    blended[i] = b[i];
+  ctx_u8_set_lum(components, blended, in_lum);
+)
+#endif
 
-int vt_get_rows (VT *vt)
+CTX_INLINE static void
+ctx_u8_blend (int components, CtxBlend blend, uint8_t * __restrict__ dst, uint8_t *src, uint8_t *blended)
 {
-  return vt->rows;
+#if CTX_BLENDING_AND_COMPOSITING
+  switch (blend)
+  {
+    case CTX_BLEND_NORMAL:      ctx_u8_blend_normal      (components, dst, src, blended); break;
+    case CTX_BLEND_MULTIPLY:    ctx_u8_blend_multiply    (components, dst, src, blended); break;
+    case CTX_BLEND_SCREEN:      ctx_u8_blend_screen      (components, dst, src, blended); break;
+    case CTX_BLEND_OVERLAY:     ctx_u8_blend_overlay     (components, dst, src, blended); break;
+    case CTX_BLEND_DARKEN:      ctx_u8_blend_darken      (components, dst, src, blended); break;
+    case CTX_BLEND_LIGHTEN:     ctx_u8_blend_lighten     (components, dst, src, blended); break;
+    case CTX_BLEND_COLOR_DODGE: ctx_u8_blend_color_dodge (components, dst, src, blended); break;
+    case CTX_BLEND_COLOR_BURN:  ctx_u8_blend_color_burn  (components, dst, src, blended); break;
+    case CTX_BLEND_HARD_LIGHT:  ctx_u8_blend_hard_light  (components, dst, src, blended); break;
+    case CTX_BLEND_SOFT_LIGHT:  ctx_u8_blend_soft_light  (components, dst, src, blended); break;
+    case CTX_BLEND_DIFFERENCE:  ctx_u8_blend_difference  (components, dst, src, blended); break;
+    case CTX_BLEND_EXCLUSION:   ctx_u8_blend_exclusion   (components, dst, src, blended); break;
+    case CTX_BLEND_COLOR:       ctx_u8_blend_color       (components, dst, src, blended); break;
+    case CTX_BLEND_HUE:         ctx_u8_blend_hue         (components, dst, src, blended); break;
+    case CTX_BLEND_SATURATION:  ctx_u8_blend_saturation  (components, dst, src, blended); break;
+    case CTX_BLEND_LUMINOSITY:  ctx_u8_blend_luminosity  (components, dst, src, blended); break;
+    case CTX_BLEND_ADDITION:    ctx_u8_blend_addition    (components, dst, src, blended); break;
+    case CTX_BLEND_DIVIDE:      ctx_u8_blend_divide      (components, dst, src, blended); break;
+    case CTX_BLEND_SUBTRACT:    ctx_u8_blend_subtract    (components, dst, src, blended); break;
+  }
+#else
+  switch (blend)
+  {
+    default:                    ctx_u8_blend_normal      (components, dst, src, blended); break;
+  }
+
+#endif
 }
 
-int vt_get_cursor_x (VT *vt)
+CTX_INLINE static void
+__ctx_u8_porter_duff (CtxRasterizer         *rasterizer,
+                     int                    components,
+                     uint8_t * __restrict__ dst,
+                     uint8_t * __restrict__ src,
+                     int                    x0,
+                     uint8_t * __restrict__ coverage,
+                     int                    count,
+                     CtxCompositingMode     compositing_mode,
+                     CtxFragment            fragment,
+                     CtxBlend               blend)
 {
-  return vt->cursor_x;
-}
+  CtxPorterDuffFactor f_s, f_d;
+  ctx_porter_duff_factors (compositing_mode, &f_s, &f_d);
+  uint8_t global_alpha_u8 = rasterizer->state->gstate.global_alpha_u8;
+
+  {
+    uint8_t tsrc[components];
+    float u0 = 0; float v0 = 0;
+    float ud = 0; float vd = 0;
+    if (CTX_UNLIKELY(fragment))
+      ctx_init_uv (rasterizer, x0, count, &u0, &v0, &ud, &vd);
+
+    while (count--)
+    {
+      int cov = *coverage;
+
+      if (CTX_UNLIKELY(
+        (compositing_mode == CTX_COMPOSITE_DESTINATION_OVER && dst[components-1] == 255)||
+        (compositing_mode == CTX_COMPOSITE_SOURCE_OVER      && cov == 0) ||
+        (compositing_mode == CTX_COMPOSITE_XOR              && cov == 0) ||
+        (compositing_mode == CTX_COMPOSITE_DESTINATION_OUT  && cov == 0) ||
+        (compositing_mode == CTX_COMPOSITE_SOURCE_ATOP      && cov == 0)
+        ))
+      {
+        u0 += ud;
+        v0 += vd;
+        coverage ++;
+        dst+=components;
+        continue;
+      }
 
-int vt_get_cursor_y (VT *vt)
-{
-  return vt->cursor_y;
-}
+      if (fragment)
+      {
+        fragment (rasterizer, u0, v0, tsrc, 1, ud, vd);
+        if (blend != CTX_BLEND_NORMAL)
+          ctx_u8_blend (components, blend, dst, tsrc, tsrc);
+      }
+      else
+      {
+        ctx_u8_blend (components, blend, dst, src, tsrc);
+      }
 
-static void draw_braille_bit (Ctx *ctx, float x, float y, float cw, float ch, int u, int v)
-{
-  ctx_rectangle (ctx, 0.167 * cw + x + u * cw * 0.5,
-                 y - ch + 0.080 * ch + v * ch * 0.25,
-                 0.33 *cw, 0.33 * cw);
-}
+      u0 += ud;
+      v0 += vd;
+      if (global_alpha_u8 != 255)
+        cov = (cov * global_alpha_u8)/255;
 
-static void draw_sextant_bit (Ctx *ctx, float x, float y, float cw, float ch, int u, int v)
-{
-  ctx_rectangle (ctx,  x + u * cw * 0.5,
-                       y - ch + v * ch * 0.3333,
-                       0.5 *cw, 0.34 * ch);
-}
+      if (cov != 255)
+        for (int c = 0; c < components; c++)
+          tsrc[c] = (tsrc[c] * cov)/255;
 
-int vt_special_glyph (Ctx *ctx, VT *vt, float x, float y, int cw, int ch, int unichar)
-{
-  switch (unichar)
-    {
-      case 0x2594: // UPPER_ONE_EIGHT_BLOCK
-        ctx_begin_path (ctx);
-        {
-          float factor = 1.0f/8.0f;
-          ctx_rectangle (ctx, x, y - ch, cw, ch * factor);
-          ctx_fill (ctx);
-        }
-        return 0;
-      case 0x2581: // LOWER_ONE_EIGHT_BLOCK:
-        ctx_begin_path (ctx);
-        {
-          float factor = 1.0f/8.0f;
-          ctx_rectangle (ctx, x, y - ch * factor, cw, ch * factor);
-          ctx_fill (ctx);
-        }
-        return 0;
-      case 0x2582: // LOWER_ONE_QUARTER_BLOCK:
-        ctx_begin_path (ctx);
-        {
-          float factor = 1.0f/4.0f;
-          ctx_rectangle (ctx, x, y - ch * factor, cw, ch * factor);
-          ctx_fill (ctx);
-        }
-        return 0;
-      case 0x2583: // LOWER_THREE_EIGHTS_BLOCK:
-        ctx_begin_path (ctx);
-        {
-          float factor = 3.0f/8.0f;
-          ctx_rectangle (ctx, x, y - ch * factor, cw, ch * factor);
-          ctx_fill (ctx);
-        }
-        return 0;
-      case 0x2585: // LOWER_FIVE_EIGHTS_BLOCK:
-        ctx_begin_path (ctx);
-        {
-          float factor = 5.0f/8.0f;
-          ctx_rectangle (ctx, x, y - ch * factor, cw, ch * factor);
-          ctx_fill (ctx);
-        }
-        return 0;
-      case 0x2586: // LOWER_THREE_QUARTERS_BLOCK:
-        ctx_begin_path (ctx);
-        {
-          float factor = 3.0f/4.0f;
-          ctx_rectangle (ctx, x, y - ch * factor, cw, ch * factor);
-          ctx_fill (ctx);
-        }
-        return 0;
-      case 0x2587: // LOWER_SEVEN_EIGHTS_BLOCK:
-        ctx_begin_path (ctx);
-        {
-          float factor = 7.0f/8.0f;
-          ctx_rectangle (ctx, x, y - ch * factor, cw, ch * factor);
-          ctx_fill (ctx);
-        }
-        return 0;
-      case 0x2589: // LEFT_SEVEN_EIGHTS_BLOCK:
-        ctx_begin_path (ctx);
-        ctx_rectangle (ctx, x, y - ch, cw*7/8, ch);
-        ctx_fill (ctx);
-        return 0;
-      case 0x258A: // LEFT_THREE_QUARTERS_BLOCK:
-        ctx_begin_path (ctx);
-        ctx_rectangle (ctx, x, y - ch, cw*3/4, ch);
-        ctx_fill (ctx);
-        return 0;
-      case 0x258B: // LEFT_FIVE_EIGHTS_BLOCK:
-        ctx_begin_path (ctx);
-        ctx_rectangle (ctx, x, y - ch, cw*5/8, ch);
-        ctx_fill (ctx);
-        return 0;
-      case 0x258D: // LEFT_THREE_EIGHTS_BLOCK:
-        ctx_begin_path (ctx);
-        ctx_rectangle (ctx, x, y - ch, cw*3/8, ch);
-        ctx_fill (ctx);
-        return 0;
-      case 0x258E: // LEFT_ONE_QUARTER_BLOCK:
-        ctx_begin_path (ctx);
-        ctx_rectangle (ctx, x, y - ch, cw/4, ch);
-        ctx_fill (ctx);
-        return 0;
-      case 0x258F: // LEFT_ONE_EIGHT_BLOCK:
-        ctx_begin_path (ctx);
-        ctx_rectangle (ctx, x, y - ch, cw/8, ch);
-        ctx_fill (ctx);
-        return 0;
-      case 0x258C: // HALF_LEFT_BLOCK:
-        ctx_begin_path (ctx);
-        ctx_rectangle (ctx, x, y - ch, cw/2, ch);
-        ctx_fill (ctx);
-        return 0;
-      case 0x2590: // HALF_RIGHT_BLOCK:
-        ctx_begin_path (ctx);
-        ctx_rectangle (ctx, x + cw/2, y - ch, cw/2, ch);
-        ctx_fill (ctx);
-        return 0;
-      case 0x1fb8f: // VT_RIGHT_SEVEN_EIGHTS_BLOCK:
-        ctx_begin_path (ctx);
-        ctx_rectangle (ctx, x + cw*1/8, y - ch, cw*7/8, ch);
-        ctx_fill (ctx);
-        return 0;
-      case 0x1fb8d: // VT_RIGHT_FIVE_EIGHTS_BLOCK:
-        ctx_begin_path (ctx);
-        ctx_rectangle (ctx, x + cw*3/8, y - ch, cw*5/8, ch);
-        ctx_fill (ctx);
-        return 0;
-      case 0x1fb8b: // VT_RIGHT_ONE_QUARTER_BLOCK:
-        ctx_begin_path (ctx);
-        ctx_rectangle (ctx, x + cw*3/4, y - ch, cw/4, ch);
-        ctx_fill (ctx);
-        return 0;
-      case 0x1fb8e: // VT_RIGHT_THREE_QUARTER_BLOCK:
-        ctx_begin_path (ctx);
-        ctx_rectangle (ctx, x + cw*1/4, y - ch, cw*3/4, ch);
-        ctx_fill (ctx);
-        return 0;
-      case 0x2595: // VT_RIGHT_ONE_EIGHT_BLOCK:
-        ctx_begin_path (ctx);
-        ctx_rectangle (ctx, x + cw*7/8, y - ch, cw/8, ch);
-        ctx_fill (ctx);
-        return 0;
-      case 0x2580: // HALF_UP_BLOCK:
-        ctx_begin_path (ctx);
-        ctx_rectangle (ctx, x, y - ch, cw, ch/2);
-        ctx_fill (ctx);
-        return 0;
-      case 0x2584: // _HALF_DOWN_BLOCK:
-        ctx_begin_path (ctx);
-        ctx_rectangle (ctx, x, y - ch/2, cw, ch/2);
-        ctx_fill (ctx);
-        return 0;
-      case 0x2596: // _QUADRANT LOWER LEFT
-        ctx_begin_path (ctx);
-        ctx_rectangle (ctx, x, y - ch/2, cw/2, ch/2);
-        ctx_fill (ctx);
-        return 0;
-      case 0x2597: // _QUADRANT LOWER RIGHT
-        ctx_begin_path (ctx);
-        ctx_rectangle (ctx, x+cw/2, y - ch/2, cw/2, ch/2);
-        ctx_fill (ctx);
-        return 0;
-      case 0x2598: // _QUADRANT UPPER LEFT
-        ctx_begin_path (ctx);
-        ctx_rectangle (ctx, x, y - ch, cw/2, ch/2);
-        ctx_fill (ctx);
-        return 0;
-      case 0x259D: // _QUADRANT UPPER RIGHT
-        ctx_begin_path (ctx);
-        ctx_rectangle (ctx, x + cw/2, y - ch, cw/2, ch/2);
-        ctx_fill (ctx);
-        return 0;
-      case 0x2599: // _QUADRANT UPPER LEFT AND LOWER LEFT AND LOWER RIGHT
-        vt_special_glyph (ctx, vt, x, y, cw, ch, 0x2598);
-        vt_special_glyph (ctx, vt, x, y, cw, ch, 0x2596);
-        vt_special_glyph (ctx, vt, x, y, cw, ch, 0x2597);
-        return 0;
-      case 0x259A: // _QUADRANT UPPER LEFT AND LOWER RIGHT
-        vt_special_glyph (ctx, vt, x, y, cw, ch, 0x2598);
-        vt_special_glyph (ctx, vt, x, y, cw, ch, 0x2597);
-        return 0;
-      case 0x259B: // _QUADRANT UPPER LEFT AND UPPER RIGHT AND LOWER LEFT
-        vt_special_glyph (ctx, vt, x, y, cw, ch, 0x2598);
-        vt_special_glyph (ctx, vt, x, y, cw, ch, 0x259D);
-        vt_special_glyph (ctx, vt, x, y, cw, ch, 0x2596);
-        return 0;
-      case 0x259C: // _QUADRANT UPPER LEFT AND UPPER RIGHT AND LOWER RIGHT
-        vt_special_glyph (ctx, vt, x, y, cw, ch, 0x2598);
-        vt_special_glyph (ctx, vt, x, y, cw, ch, 0x259D);
-        vt_special_glyph (ctx, vt, x, y, cw, ch, 0x2597);
-        return 0;
-      case 0x259E: // _QUADRANT UPPER RIGHT AND LOWER LEFT
-        vt_special_glyph (ctx, vt, x, y, cw, ch, 0x259D);
-        vt_special_glyph (ctx, vt, x, y, cw, ch, 0x2596);
-        return 0;
-      case 0x259F: // _QUADRANT UPPER RIGHT AND LOWER LEFT AND LOWER RIGHT
-        vt_special_glyph (ctx, vt, x, y, cw, ch, 0x259D);
-        vt_special_glyph (ctx, vt, x, y, cw, ch, 0x2596);
-        vt_special_glyph (ctx, vt, x, y, cw, ch, 0x2597);
-        return 0;
-      case 0x2588: // FULL_BLOCK:
-        ctx_begin_path (ctx);
-        ctx_rectangle (ctx, x, y - ch, cw, ch);
-        ctx_fill (ctx);
-        return 0;
-      case 0x2591: // LIGHT_SHADE:
-        ctx_begin_path (ctx);
-        ctx_rectangle (ctx, x, y - ch, cw, ch);
-        ctx_save (ctx);
-        ctx_global_alpha (ctx, 0.25);
-        ctx_fill (ctx);
-        ctx_restore (ctx);
-        return 0;
-      case 0x2592: // MEDIUM_SHADE:
-        ctx_begin_path (ctx);
-        ctx_rectangle (ctx, x, y - ch, cw, ch);
-        ctx_save (ctx);
-        ctx_global_alpha (ctx, 0.5);
-        ctx_fill (ctx);
-        ctx_restore (ctx);
-        return 0;
-      case 0x2593: // DARK SHADE:
-        ctx_begin_path (ctx);
-        ctx_rectangle (ctx, x, y - ch, cw, ch);
-        ctx_save (ctx);
-        ctx_global_alpha (ctx, 0.75);
-        ctx_fill (ctx);
-        ctx_restore (ctx);
-        return 0;
-      case 0x23BA: //HORIZONTAL_SCANLINE-1
-        ctx_begin_path (ctx);
-        ctx_rectangle (ctx, x,      y - ch + ch*0.1 - ch * 0.1,
-                       cw, ch * 0.1);
-        ctx_fill (ctx);
-        return 0;
-      case 0x23BB: //HORIZONTAL_SCANLINE-3
-        ctx_begin_path (ctx);
-        ctx_rectangle (ctx, x,      y - ch + ch*0.3 - ch * 0.075,
-                       cw, ch * 0.1);
-        ctx_fill (ctx);
-        return 0;
-      case 0x23BC: //HORIZONTAL_SCANLINE-7
-        ctx_begin_path (ctx);
-        ctx_rectangle (ctx, x,      y - ch + ch*0.7 - ch * 0.025,
-                       cw, ch * 0.1);
-        ctx_fill (ctx);
-        return 0;
-      case 0x23BD: //HORIZONTAL_SCANLINE-9
-        ctx_begin_path (ctx);
-        ctx_rectangle (ctx, x,      y - ch + ch*0.9 + ch * 0.0,
-                       cw, ch * 0.1);
-        ctx_fill (ctx);
-        return 0;
-      case 0x2500: //VT_BOX_DRAWINGS_LIGHT_HORIZONTAL // and scanline 5
-        ctx_begin_path (ctx);
-        ctx_rectangle (ctx, x, y - ch/2 - ch * 0.1 / 2, cw, ch * 0.1);
-        ctx_fill (ctx);
-        return 0;
-      case 0x2212: // minus -sign
-        ctx_begin_path (ctx);
-        ctx_rectangle (ctx, x + cw * 0.1, y - ch/2 - ch * 0.1 / 2, cw * 0.8, ch * 0.1);
-        ctx_fill (ctx);
-        return 0;
-      case 0x2502: // VT_BOX_DRAWINGS_LIGHT_VERTICAL:
-        ctx_begin_path (ctx);
-        ctx_rectangle (ctx, x + cw/2 - ch * 0.1 / 2, y - ch, ch * 0.1, ch + 1);
-        ctx_fill (ctx);
-        return 0;
-      case 0x250c: //VT_BOX_DRAWINGS_LIGHT_DOWN_AND_RIGHT:
-        ctx_begin_path (ctx);
-        ctx_rectangle (ctx, x + cw/2 - ch * 0.1 / 2, y - ch/2 - ch*0.1/2, ch * 0.1, ch/2 + ch*0.1);
-        ctx_fill (ctx);
-        ctx_rectangle (ctx, x + cw/2 - ch * 0.1 / 2, y - ch/2 - ch*0.1/2, cw/2+ ch * 0.1, ch*0.1);
-        ctx_fill (ctx);
-        return 0;
-      case 0x2510: //VT_BOX_DRAWINGS_LIGHT_DOWN_AND_LEFT:
-        ctx_begin_path (ctx);
-        ctx_rectangle (ctx, x + cw/2 - ch * 0.1 / 2, y - ch/2 - ch*0.1/2, ch * 0.1, ch/2 + ch*0.1);
-        ctx_fill (ctx);
-        ctx_rectangle (ctx, x, y - ch/2 - ch*0.1/2, cw/2+ ch * 0.1/2, ch*0.1);
-        ctx_fill (ctx);
-        return 0;
-      case 0x2514: //VT_BOX_DRAWINGS_LIGHT_UP_AND_RIGHT:
-        ctx_begin_path (ctx);
-        ctx_rectangle (ctx, x + cw/2 - ch * 0.1 / 2, y - ch, ch * 0.1, ch/2+ch*0.1/2);
-        ctx_fill (ctx);
-        ctx_rectangle (ctx, x + cw/2 - ch * 0.1 / 2, y - ch/2 - ch*0.1/2, cw/2 + ch * 0.1, ch*0.1);
-        ctx_fill (ctx);
-        return 0;
-      case 0x2518: //VT_BOX_DRAWINGS_LIGHT_UP_AND_LEFT:
-        ctx_begin_path (ctx);
-        ctx_rectangle (ctx, x + cw/2 - ch * 0.1 / 2, y - ch, ch * 0.1, ch/2+ ch*0.1/2);
-        ctx_fill (ctx);
-        ctx_rectangle (ctx, x, y - ch/2-ch*0.1/2, cw/2+ch * 0.1/2, ch*0.1);
-        ctx_fill (ctx);
-        return 0;
-      case 0x251C: //VT_BOX_DRAWINGS_LIGHT_VERTICAL_AND_RIGHT:
-        ctx_begin_path (ctx);
-        ctx_rectangle (ctx, x + cw/2 - ch * 0.1 / 2, y - ch/2-ch*0.1/2, cw/2+ch * 0.1, ch*0.1);
-        ctx_fill (ctx);
-        ctx_rectangle (ctx, x + cw/2 - ch * 0.1 / 2, y - ch, ch * 0.1, ch);
-        ctx_fill (ctx);
-        return 0;
-      case 0x2524: //VT_BOX_DRAWINGS_LIGHT_VERTICAL_AND_LEFT:
-        ctx_begin_path (ctx);
-        ctx_rectangle (ctx, x + cw/2 - ch * 0.1 / 2, y - ch, ch * 0.1, ch);
-        ctx_fill (ctx);
-        ctx_rectangle (ctx, x, y - ch/2-ch*0.1/2, cw/2+ch * 0.1/2, ch*0.1);
-        ctx_fill (ctx);
-        return 0;
-      case 0x252C: // VT_BOX_DRAWINGS_LIGHT_DOWN_AND_HORIZONTAL:
-        ctx_begin_path (ctx);
-        ctx_rectangle (ctx, x + cw/2 - ch * 0.1 / 2, y - ch/2-ch*0.1/2, ch * 0.1, ch/2+ch*0.1);
-        ctx_fill (ctx);
-        ctx_rectangle (ctx, x, y - ch/2 - ch * 0.1 / 2, cw, ch * 0.1);
-        ctx_fill (ctx);
-        return 0;
-      case 0x2534: // VT_BOX_DRAWINGS_LIGHT_UP_AND_HORIZONTAL:
-        ctx_begin_path (ctx);
-        ctx_rectangle (ctx, x, y - ch/2 - ch * 0.1 / 2, cw, ch * 0.1);
-        ctx_fill (ctx);
-        ctx_rectangle (ctx, x + cw/2 - ch * 0.1 / 2, y - ch, ch * 0.1, ch /2+ch*0.1/2);
-        ctx_fill (ctx);
-        return 0;
-      case 0x253C: // VT_BOX_DRAWINGS_LIGHT_VERTICAL_AND_HORIZONTAL:
-        ctx_begin_path (ctx);
-        ctx_rectangle (ctx, x, y - ch/2 - ch * 0.1 / 2, cw, ch * 0.1);
-        ctx_fill (ctx);
-        ctx_rectangle (ctx, x + cw/2 - ch * 0.1 / 2, y - ch, ch * 0.1, ch);
-        ctx_fill (ctx);
-        return 0;
-      case 0xe0a0: // PowerLine branch
-        ctx_save (ctx);
-        ctx_begin_path (ctx);
-        ctx_move_to (ctx, x+cw/2, y - 0.15 * ch);
-        ctx_rel_line_to (ctx, -cw/3, -ch * 0.7);
-        ctx_rel_line_to (ctx, cw/2, 0);
-        ctx_rel_line_to (ctx, -cw/3, ch * 0.7);
-        ctx_line_width (ctx, cw * 0.25);
-        ctx_stroke (ctx);
-        ctx_restore (ctx);
-        break;
-      // case 0xe0a1: // PowerLine LN
-      // case 0xe0a2: // PowerLine Lock
-      case 0xe0b0: // PowerLine left solid
-        ctx_begin_path (ctx);
-        ctx_move_to (ctx, x, y);
-        ctx_rel_line_to (ctx, 0, -ch);
-        ctx_rel_line_to (ctx, cw, ch/2);
-        ctx_fill (ctx);
-        return 0;
-      case 0xe0b1: // PowerLine left line
-        ctx_save (ctx);
-        ctx_begin_path (ctx);
-        ctx_move_to (ctx, x, y - ch * 0.1);
-        ctx_rel_line_to (ctx, cw * 0.9, -ch/2 * 0.8);
-        ctx_rel_line_to (ctx, -cw * 0.9, -ch/2 * 0.8);
-        ctx_line_width (ctx, cw * 0.2);
-        ctx_stroke (ctx);
-        ctx_restore (ctx);
-        return 0;
-      case 0xe0b2: // PowerLine Right solid
-        ctx_begin_path (ctx);
-        ctx_move_to (ctx, x, y);
-        ctx_rel_move_to (ctx, cw, 0);
-        ctx_rel_line_to (ctx, -cw, -ch/2);
-        ctx_rel_line_to (ctx, cw, -ch/2);
-        ctx_fill (ctx);
-        return 0;
-      case 0xe0b3: // PowerLine right line
-        ctx_save (ctx);
-        ctx_begin_path (ctx);
-        ctx_move_to (ctx, x, y - ch * 0.1);
-        ctx_rel_move_to (ctx, cw, 0);
-        ctx_rel_line_to (ctx, -cw * 0.9, -ch/2 * 0.8);
-        ctx_rel_line_to (ctx,  cw * 0.9, ch/2 * 0.8);
-        ctx_line_width (ctx, cw * 0.2);
-        ctx_stroke (ctx);
-        ctx_restore (ctx);
-        return 0;
-        /*
-      case 0x1fb70: // left triangular one quarter block
-        ctx_begin_path (ctx);
-        ctx_move_to (ctx, x, y);
-        ctx_rel_line_to (ctx, 0, -ch);
-        ctx_rel_line_to (ctx, cw/2, -ch/2);
-        ctx_fill (ctx);
-        return 0;
-      case 0x1fb72: // right triangular one quarter block
-        ctx_begin_path (ctx);
-        ctx_move_to (ctx, x, y);
-        ctx_rel_move_to (ctx, cw/2, -ch/2);
-        ctx_rel_line_to (ctx, cw/2, -ch/2);
-        ctx_rel_line_to (ctx, 0, ch);
-        ctx_fill (ctx);
-        return 0;
-      case 0x1fb73: // lower triangular one quarter block
-        ctx_begin_path (ctx);
-        ctx_move_to (ctx, x, y);
-        ctx_rel_line_to (ctx, cw/2, -ch/2);
-        ctx_rel_line_to (ctx, cw/2, ch/2);
-        ctx_fill (ctx);
-        return 0;
-      case 0x1fb71: // upper triangular one quarter block
-        ctx_begin_path (ctx);
-        ctx_move_to (ctx, x, y);
-        ctx_rel_move_to (ctx, cw/2, -ch/2);
-        ctx_rel_line_to (ctx, -cw/2, -ch/2);
-        ctx_rel_line_to (ctx, cw, 0);
-        ctx_fill (ctx);
-        return 0;
-        */
-      case 0x25E2: // VT_BLACK_LOWER_RIGHT_TRIANGLE:
-        ctx_begin_path (ctx);
-        ctx_move_to (ctx, x, y);
-        ctx_rel_line_to (ctx, cw, -ch);
-        ctx_rel_line_to (ctx, 0, ch);
-        ctx_fill (ctx);
-        return 0;
-      case 0x25E3: //  VT_BLACK_LOWER_LEFT_TRIANGLE:
-        ctx_begin_path (ctx);
-        ctx_move_to (ctx, x, y);
-        ctx_rel_line_to (ctx, 0, -ch);
-        ctx_rel_line_to (ctx, cw, ch);
-        ctx_fill (ctx);
-        return 0;
-      case 0x25E4: // tri
-        ctx_begin_path (ctx);
-        ctx_move_to (ctx, x, y);
-        ctx_rel_line_to (ctx, 0, -ch);
-        ctx_rel_line_to (ctx, cw, 0);
-        ctx_fill (ctx);
-        return 0;
-      case 0x25E5: // tri
-        ctx_begin_path (ctx);
-        ctx_move_to (ctx, x, y - ch);
-        ctx_rel_line_to (ctx, cw, 0);
-        ctx_rel_line_to (ctx, 0, ch);
-        ctx_fill (ctx);
-        return 0;
-      case 0x2800:
-      case 0x2801:
-      case 0x2802:
-      case 0x2803:
-      case 0x2804:
-      case 0x2805:
-      case 0x2806:
-      case 0x2807:
-      case 0x2808:
-      case 0x2809:
-      case 0x280A:
-      case 0x280B:
-      case 0x280C:
-      case 0x280D:
-      case 0x280E:
-      case 0x280F:
-      case 0x2810:
-      case 0x2811:
-      case 0x2812:
-      case 0x2813:
-      case 0x2814:
-      case 0x2815:
-      case 0x2816:
-      case 0x2817:
-      case 0x2818:
-      case 0x2819:
-      case 0x281A:
-      case 0x281B:
-      case 0x281C:
-      case 0x281D:
-      case 0x281E:
-      case 0x281F:
-      case 0x2820:
-      case 0x2821:
-      case 0x2822:
-      case 0x2823:
-      case 0x2824:
-      case 0x2825:
-      case 0x2826:
-      case 0x2827:
-      case 0x2828:
-      case 0x2829:
-      case 0x282A:
-      case 0x282B:
-      case 0x282C:
-      case 0x282D:
-      case 0x282E:
-      case 0x282F:
-      case 0x2830:
-      case 0x2831:
-      case 0x2832:
-      case 0x2833:
-      case 0x2834:
-      case 0x2835:
-      case 0x2836:
-      case 0x2837:
-      case 0x2838:
-      case 0x2839:
-      case 0x283A:
-      case 0x283B:
-      case 0x283C:
-      case 0x283D:
-      case 0x283E:
-      case 0x283F:
-        ctx_begin_path (ctx);
-        {
-          int bit_pattern = unichar - 0x2800;
-          int bit = 0;
-          int u = 0;
-          int v = 0;
-          for (bit = 0; bit < 6; bit++)
-            {
-              if (bit_pattern & (1<<bit) )
-                {
-                  draw_braille_bit (ctx, x, y, cw, ch, u, v);
-                }
-              v++;
-              if (v > 2)
-                {
-                  v = 0;
-                  u++;
-                }
-            }
-        }
-        ctx_fill (ctx);
-        return 0;
-      case 0x2840:
-      case 0x2841:
-      case 0x2842:
-      case 0x2843:
-      case 0x2844:
-      case 0x2845:
-      case 0x2846:
-      case 0x2847:
-      case 0x2848:
-      case 0x2849:
-      case 0x284A:
-      case 0x284B:
-      case 0x284C:
-      case 0x284D:
-      case 0x284E:
-      case 0x284F:
-      case 0x2850:
-      case 0x2851:
-      case 0x2852:
-      case 0x2853:
-      case 0x2854:
-      case 0x2855:
-      case 0x2856:
-      case 0x2857:
-      case 0x2858:
-      case 0x2859:
-      case 0x285A:
-      case 0x285B:
-      case 0x285C:
-      case 0x285D:
-      case 0x285E:
-      case 0x285F:
-      case 0x2860:
-      case 0x2861:
-      case 0x2862:
-      case 0x2863:
-      case 0x2864:
-      case 0x2865:
-      case 0x2866:
-      case 0x2867:
-      case 0x2868:
-      case 0x2869:
-      case 0x286A:
-      case 0x286B:
-      case 0x286C:
-      case 0x286D:
-      case 0x286E:
-      case 0x286F:
-      case 0x2870:
-      case 0x2871:
-      case 0x2872:
-      case 0x2873:
-      case 0x2874:
-      case 0x2875:
-      case 0x2876:
-      case 0x2877:
-      case 0x2878:
-      case 0x2879:
-      case 0x287A:
-      case 0x287B:
-      case 0x287C:
-      case 0x287D:
-      case 0x287E:
-      case 0x287F:
-        ctx_begin_path (ctx);
-        draw_braille_bit (ctx, x, y, cw, ch, 0, 3);
-        {
-          int bit_pattern = unichar - 0x2840;
-          int bit = 0;
-          int u = 0;
-          int v = 0;
-          for (bit = 0; bit < 6; bit++)
-            {
-              if (bit_pattern & (1<<bit) )
-                {
-                  draw_braille_bit (ctx, x, y, cw, ch, u, v);
-                }
-              v++;
-              if (v > 2)
-                {
-                  v = 0;
-                  u++;
-                }
-            }
-        }
-        ctx_fill (ctx);
-        return 0;
-      case 0x2880:
-      case 0x2881:
-      case 0x2882:
-      case 0x2883:
-      case 0x2884:
-      case 0x2885:
-      case 0x2886:
-      case 0x2887:
-      case 0x2888:
-      case 0x2889:
-      case 0x288A:
-      case 0x288B:
-      case 0x288C:
-      case 0x288D:
-      case 0x288E:
-      case 0x288F:
-      case 0x2890:
-      case 0x2891:
-      case 0x2892:
-      case 0x2893:
-      case 0x2894:
-      case 0x2895:
-      case 0x2896:
-      case 0x2897:
-      case 0x2898:
-      case 0x2899:
-      case 0x289A:
-      case 0x289B:
-      case 0x289C:
-      case 0x289D:
-      case 0x289E:
-      case 0x289F:
-      case 0x28A0:
-      case 0x28A1:
-      case 0x28A2:
-      case 0x28A3:
-      case 0x28A4:
-      case 0x28A5:
-      case 0x28A6:
-      case 0x28A7:
-      case 0x28A8:
-      case 0x28A9:
-      case 0x28AA:
-      case 0x28AB:
-      case 0x28AC:
-      case 0x28AD:
-      case 0x28AE:
-      case 0x28AF:
-      case 0x28B0:
-      case 0x28B1:
-      case 0x28B2:
-      case 0x28B3:
-      case 0x28B4:
-      case 0x28B5:
-      case 0x28B6:
-      case 0x28B7:
-      case 0x28B8:
-      case 0x28B9:
-      case 0x28BA:
-      case 0x28BB:
-      case 0x28BC:
-      case 0x28BD:
-      case 0x28BE:
-      case 0x28BF:
-        ctx_begin_path (ctx);
-        draw_braille_bit (ctx, x, y, cw, ch, 1, 3);
+      for (int c = 0; c < components; c++)
+      {
+        int res = 0;
+        switch (f_s)
         {
-          int bit_pattern = unichar - 0x2880;
-          int bit = 0;
-          int u = 0;
-          int v = 0;
-          for (bit = 0; bit < 6; bit++)
-            {
-              if (bit_pattern & (1<<bit) )
-                {
-                  draw_braille_bit (ctx, x, y, cw, ch, u, v);
-                }
-              v++;
-              if (v > 2)
-                {
-                  v = 0;
-                  u++;
-                }
-            }
+          case CTX_PORTER_DUFF_0: break;
+          case CTX_PORTER_DUFF_1:             res += (tsrc[c]); break;
+          case CTX_PORTER_DUFF_ALPHA:         res += (tsrc[c] * dst[components-1])/255; break;
+          case CTX_PORTER_DUFF_1_MINUS_ALPHA: res += (tsrc[c] * (255-dst[components-1]))/255; break;
         }
-        ctx_fill (ctx);
-        return 0;
-      case 0x28C0:
-      case 0x28C1:
-      case 0x28C2:
-      case 0x28C3:
-      case 0x28C4:
-      case 0x28C5:
-      case 0x28C6:
-      case 0x28C7:
-      case 0x28C8:
-      case 0x28C9:
-      case 0x28CA:
-      case 0x28CB:
-      case 0x28CC:
-      case 0x28CD:
-      case 0x28CE:
-      case 0x28CF:
-      case 0x28D0:
-      case 0x28D1:
-      case 0x28D2:
-      case 0x28D3:
-      case 0x28D4:
-      case 0x28D5:
-      case 0x28D6:
-      case 0x28D7:
-      case 0x28D8:
-      case 0x28D9:
-      case 0x28DA:
-      case 0x28DB:
-      case 0x28DC:
-      case 0x28DD:
-      case 0x28DE:
-      case 0x28DF:
-      case 0x28E0:
-      case 0x28E1:
-      case 0x28E2:
-      case 0x28E3:
-      case 0x28E4:
-      case 0x28E5:
-      case 0x28E6:
-      case 0x28E7:
-      case 0x28E8:
-      case 0x28E9:
-      case 0x28EA:
-      case 0x28EB:
-      case 0x28EC:
-      case 0x28ED:
-      case 0x28EE:
-      case 0x28EF:
-      case 0x28F0:
-      case 0x28F1:
-      case 0x28F2:
-      case 0x28F3:
-      case 0x28F4:
-      case 0x28F5:
-      case 0x28F6:
-      case 0x28F7:
-      case 0x28F8:
-      case 0x28F9:
-      case 0x28FA:
-      case 0x28FB:
-      case 0x28FC:
-      case 0x28FD:
-      case 0x28FE:
-      case 0x28FF:
-        ctx_begin_path (ctx);
-        draw_braille_bit (ctx, x, y, cw, ch, 0, 3);
-        draw_braille_bit (ctx, x, y, cw, ch, 1, 3);
+        switch (f_d)
         {
-          int bit_pattern = unichar - 0x28C0;
-          int bit = 0;
-          int u = 0;
-          int v = 0;
-          for (bit = 0; bit < 6; bit++)
-            {
-              if (bit_pattern & (1<<bit) )
-                {
-                  draw_braille_bit (ctx, x, y, cw, ch, u, v);
-                }
-              v++;
-              if (v > 2)
-                {
-                  v = 0;
-                  u++;
-                }
-            }
+          case CTX_PORTER_DUFF_0: break;
+          case CTX_PORTER_DUFF_1:             res += dst[c]; break;
+          case CTX_PORTER_DUFF_ALPHA:         res += (dst[c] * tsrc[components-1])/255; break;
+          case CTX_PORTER_DUFF_1_MINUS_ALPHA: res += (dst[c] * (255-tsrc[components-1]))/255; break;
         }
-        ctx_fill (ctx);
-        return 0;
-      case 0x1fb00:
-      case 0x1fb01:
-      case 0x1fb02:
-      case 0x1fb03:
-      case 0x1fb04:
-      case 0x1fb05:
-      case 0x1fb06:
-      case 0x1fb07:
-      case 0x1fb08:
-      case 0x1fb09:
-      case 0x1fb0a:
-      case 0x1fb0b:
-      case 0x1fb0c:
-      case 0x1fb0d:
-      case 0x1fb0e:
-      case 0x1fb0f:
-      case 0x1fb10:
-      case 0x1fb11:
-      case 0x1fb12:
-      case 0x1fb13:
-      case 0x1fb14:
-      case 0x1fb15:
-      case 0x1fb16:
-      case 0x1fb17:
-      case 0x1fb18:
-      case 0x1fb19:
-      case 0x1fb1a:
-      case 0x1fb1b:
-      case 0x1fb1c:
-      case 0x1fb1d:
-      case 0x1fb1e:
-      case 0x1fb1f:
-      case 0x1fb20:
-      case 0x1fb21:
-      case 0x1fb22:
-      case 0x1fb23:
-      case 0x1fb24:
-      case 0x1fb25:
-      case 0x1fb26:
-      case 0x1fb27:
-      case 0x1fb28:
-      case 0x1fb29:
-      case 0x1fb2a:
-      case 0x1fb2b:
-      case 0x1fb2c:
-      case 0x1fb2d:
-      case 0x1fb2e:
-      case 0x1fb2f:
-      case 0x1fb30:
-      case 0x1fb31:
-      case 0x1fb32:
-      case 0x1fb33:
-      case 0x1fb34:
-      case 0x1fb35:
-      case 0x1fb36:
-      case 0x1fb37:
-      case 0x1fb38:
-      case 0x1fb39:
-      case 0x1fb3a:
-      case 0x1fb3b:
+        dst[c] = res;
+      }
+      coverage ++;
+      dst+=components;
+    }
+  }
+}
 
-        {
-          ctx_begin_path (ctx);
-          uint32_t bitmask = (unichar - 0x1fb00) + 1;
-          if (bitmask > 20) bitmask ++;
-          if (bitmask > 41) bitmask ++;
-          int bit = 0;
-          for (int v = 0; v < 3; v ++)
-          for (int u = 0; u < 2; u ++, bit ++)
-          {
-            if (bitmask & (1<<bit))
-            {
-              draw_sextant_bit (ctx, x, y, cw, ch, u, v);
-            }
-          }
-          ctx_fill (ctx);
-          return 0;
-        }
-        break;
-      case 0x1fb3c:
-        {
-          ctx_begin_path (ctx);
-          ctx_move_to (ctx, x, y);
-          ctx_rel_line_to (ctx, 0, -ch / 3.0f);
-          ctx_rel_line_to (ctx, cw/2, ch/3.0f);
-          ctx_fill (ctx);
-          return 0;
-        }
+#if CTX_AVX2
+CTX_INLINE static void
+ctx_avx2_porter_duff (CtxRasterizer         *rasterizer,
+                      int                    components,
+                      uint8_t * dst,
+                      uint8_t * src,
+                      int                    x0,
+                      uint8_t * coverage,
+                      int                    count,
+                      CtxCompositingMode     compositing_mode,
+                      CtxFragment            fragment,
+                      CtxBlend               blend)
+{
+  CtxPorterDuffFactor f_s, f_d;
+  ctx_porter_duff_factors (compositing_mode, &f_s, &f_d);
+  uint8_t global_alpha_u8 = rasterizer->state->gstate.global_alpha_u8;
+//assert ((((size_t)dst) & 31) == 0);
+  int n_pix = 32/components;
+  uint8_t tsrc[components * n_pix];
+  float u0 = 0; float v0 = 0;
+  float ud = 0; float vd = 0;
+  int x = 0;
+  if (fragment)
+    ctx_init_uv (rasterizer, x0, count, &u0, &v0, &ud, &vd);
+
+  __m256i lo_mask = _mm256_set1_epi32 (0x00FF00FF);
+  __m256i hi_mask = _mm256_set1_epi32 (0xFF00FF00);
+  __m256i x00ff =   _mm256_set1_epi16(255);
+  __m256i x0101 =   _mm256_set1_epi16(0x0101);
+  __m256i x0080 =   _mm256_set1_epi16(0x0080);
+  for (; x < count; x+=n_pix)
+  {
+    __m256i xdst  = _mm256_loadu_si256((__m256i*)(dst)); 
+    __m256i xcov;
+    __m256i xsrc;
+    __m256i xsrc_a;
+    __m256i xdst_a;
+
+    int is_blank = 1;
+    int is_full = 0;
+    switch (n_pix)
+    {
+      case 16:
+        if (((uint64_t*)(coverage))[0] &&
+            ((uint64_t*)(coverage))[1])
+           is_blank = 0;
+        else if (((uint64_t*)(coverage))[0] == 0xffffffffffffffff &&
+                 ((uint64_t*)(coverage))[1] == 0xffffffffffffffff)
+           is_full = 1;
         break;
-      case 0x1fb3d:
-        {
-          ctx_begin_path (ctx);
-          ctx_move_to (ctx, x, y);
-          ctx_rel_line_to (ctx, 0, -ch/3.0f);
-          ctx_rel_line_to (ctx, cw, ch/3.0f);
-          ctx_fill (ctx);
-          return 0;
-        }
+      case 8:
+        if (((uint64_t*)(coverage))[0])
+           is_blank = 0;
+        else if (((uint64_t*)(coverage))[0] == 0xffffffffffffffff)
+           is_full = 1;
         break;
-      case 0x1fb3e:
-        {
-          ctx_begin_path (ctx);
-          ctx_move_to (ctx, x, y);
-          ctx_rel_line_to (ctx, 0,   -ch/3.0f * 2);
-          ctx_rel_line_to (ctx, cw/2, ch/3.0f * 2);
-          ctx_fill (ctx);
-          return 0;
-        }
+      case 4:
+        if (((uint32_t*)(coverage))[0])
+           is_blank = 0;
+        else if (((uint32_t*)(coverage))[0] == 0xffffffff)
+           is_full = 1;
         break;
-      case 0x1fb3f:
-        {
-          ctx_begin_path (ctx);
-          ctx_move_to (ctx, x, y);
-          ctx_rel_line_to (ctx, 0,  -ch/3.0f * 2);
-          ctx_rel_line_to (ctx, cw, ch/3.0f * 2);
-          ctx_fill (ctx);
-          return 0;
-        }
+      default:
         break;
-      case 0x1fb40:
-        {
-          ctx_begin_path (ctx);
-          ctx_move_to (ctx, x, y);
-          ctx_rel_line_to (ctx, 0, -ch);
-          ctx_rel_line_to (ctx, cw/2, ch);
-          ctx_fill (ctx);
-          return 0;
-        }
-      case 0x1fb41:
-        {
-          ctx_begin_path (ctx);
-          ctx_move_to (ctx, x, y);
-          ctx_rel_line_to (ctx, 0, -ch/3.0*2);
-          ctx_rel_line_to (ctx, cw/2, -ch/3.0);
-          ctx_rel_line_to (ctx, cw/2, 0);
-          ctx_rel_line_to (ctx, 0.0, ch);
-          ctx_rel_line_to (ctx, -cw, 0);
-          ctx_fill (ctx);
-          return 0;
-        }
-      case 0x1fb42:
-        {
-          ctx_begin_path (ctx);
-          ctx_move_to (ctx, x, y);
-          ctx_rel_line_to (ctx, 0, -ch/3.0*2);
-          ctx_rel_line_to (ctx, cw, -ch/3.0);
-          ctx_rel_line_to (ctx, 0.0, ch);
-          ctx_rel_line_to (ctx, -cw, 0);
-          ctx_fill (ctx);
-          return 0;
-        }
-      case 0x1fb43:
-        {
-          ctx_begin_path (ctx);
-          ctx_move_to (ctx, x, y);
-          ctx_rel_line_to (ctx, 0, -ch/3.0f);
-          ctx_rel_line_to (ctx, cw/2, -ch/3.0f*2.0f);
-          ctx_rel_line_to (ctx, cw/2, 0);
-          ctx_rel_line_to (ctx, 0.0, ch);
-          ctx_rel_line_to (ctx, -cw, 0);
-          ctx_fill (ctx);
-          return 0;
-        }
-      case 0x1fb44:
-        {
-          ctx_begin_path (ctx);
-          ctx_move_to (ctx, x, y);
-          ctx_rel_line_to (ctx, 0, -ch/3.0);
-          ctx_rel_line_to (ctx, cw, -ch/3.0 * 2);
-          ctx_rel_line_to (ctx, 0.0, ch);
-          ctx_rel_line_to (ctx, -cw, 0);
-          ctx_fill (ctx);
-          return 0;
-        }
-      case 0x1fb45:
-        {
-          ctx_begin_path (ctx);
-          ctx_move_to (ctx, x, y);
-          ctx_rel_line_to (ctx, cw/2, -ch);
-          ctx_rel_line_to (ctx, cw/2, 0);
-          ctx_rel_line_to (ctx, 0.0, ch);
-          ctx_rel_line_to (ctx, -cw, 0);
-          ctx_fill (ctx);
-          return 0;
-        }
-      case 0x1fb46:
-        {
-          ctx_begin_path (ctx);
-          ctx_move_to (ctx, x, y);
-          ctx_rel_line_to (ctx, 0, -ch/3.0);
-          ctx_rel_line_to (ctx, cw, -ch/3.0);
-          ctx_rel_line_to (ctx, 0.0, ch/3.0*2);
-          ctx_rel_line_to (ctx, -cw, 0);
-          ctx_fill (ctx);
-          return 0;
-        }
-      case 0x1fb47:
-        {
-          ctx_begin_path (ctx);
-          ctx_move_to (ctx, x, y);
-          ctx_rel_move_to (ctx, cw/2, 0);
-          ctx_rel_line_to (ctx, cw/2, -ch/3.0);
-          ctx_rel_line_to (ctx, 0.0, ch/3.0);
-          ctx_rel_line_to (ctx, -cw/2, 0);
-          ctx_fill (ctx);
-          return 0;
-        }
-      case 0x1fb48:
-        {
-          ctx_begin_path (ctx);
-          ctx_move_to (ctx, x, y);
-          ctx_rel_line_to (ctx, cw, -ch/3.0);
-          ctx_rel_line_to (ctx, 0.0, ch/3.0);
-          ctx_rel_line_to (ctx, -cw, 0);
-          ctx_fill (ctx);
-          return 0;
-        }
-      case 0x1fb49:
-        {
-          ctx_begin_path (ctx);
-          ctx_move_to (ctx, x, y);
-          ctx_rel_move_to (ctx, cw/2, 0);
-          ctx_rel_line_to (ctx, cw/2, -ch/3.0*2);
-          ctx_rel_line_to (ctx, 0.0, ch/3.0*2);
-          ctx_rel_line_to (ctx, -cw/2, 0);
-          ctx_fill (ctx);
-          return 0;
-        }
-      case 0x1fb4a:
-        {
-          ctx_begin_path (ctx);
-          ctx_move_to (ctx, x, y);
-          ctx_rel_line_to (ctx, cw, -ch/3.0*2);
-          ctx_rel_line_to (ctx, 0.0, ch/3.0*2);
-          ctx_rel_line_to (ctx, -cw, 0);
-          ctx_fill (ctx);
-          return 0;
-        }
-      case 0x1fb4b:
-        {
-          ctx_begin_path (ctx);
-          ctx_move_to (ctx, x, y);
-          ctx_rel_line_to (ctx, 0, -ch);
-          ctx_rel_line_to (ctx, cw/2, 0);
-          ctx_rel_line_to (ctx, cw/2, ch/3);
-          ctx_rel_line_to (ctx, 0, ch/3.0*2);
-          ctx_rel_line_to (ctx, -cw, 0);
-          ctx_fill (ctx);
-          return 0;
-        }
-      case 0x1fb4c:
-        {
-          ctx_begin_path (ctx);
-          ctx_move_to (ctx, x, y);
-          ctx_rel_line_to (ctx, 0, -ch);
-          ctx_rel_line_to (ctx, cw, ch/3);
-          ctx_rel_line_to (ctx, 0, ch/3.0*2);
-          ctx_rel_line_to (ctx, -cw, 0);
-          ctx_fill (ctx);
-          return 0;
-        }
-      case 0x1fb4d:
-        {
-          ctx_begin_path (ctx);
-          ctx_move_to (ctx, x, y);
-          ctx_rel_line_to (ctx, 0, -ch);
-          ctx_rel_line_to (ctx, cw, ch/3);
-          ctx_rel_line_to (ctx, 0, ch/3.0*2);
-          ctx_rel_line_to (ctx, -cw, 0);
-          ctx_fill (ctx);
-          return 0;
-        }
-      case 0x1fb4e:
-        {
-          ctx_begin_path (ctx);
-          ctx_move_to (ctx, x, y);
-          ctx_rel_line_to (ctx, 0, -ch);
-          ctx_rel_line_to (ctx, cw/2, 0);
-          ctx_rel_line_to (ctx, cw/2, ch/3 *  2);
-          ctx_rel_line_to (ctx, 0, ch/3.0);
-          ctx_rel_line_to (ctx, -cw, 0);
-          ctx_fill (ctx);
-          return 0;
-        }
-      case 0x1fb4f:
-        {
-          ctx_begin_path (ctx);
-          ctx_move_to (ctx, x, y);
-          ctx_rel_line_to (ctx, 0, -ch);
-          ctx_rel_line_to (ctx, cw, ch/3 *  2);
-          ctx_rel_line_to (ctx, 0, ch/3.0);
-          ctx_rel_line_to (ctx, -cw, 0);
-          ctx_fill (ctx);
-          return 0;
-        }
-      case 0x1fb50:
-        {
-          ctx_begin_path (ctx);
-          ctx_move_to (ctx, x, y);
-          ctx_rel_line_to (ctx, 0, -ch);
-          ctx_rel_line_to (ctx, cw/2, 0);
-          ctx_rel_line_to (ctx, cw/2, ch);
-          ctx_rel_line_to (ctx, -cw, 0);
-          ctx_fill (ctx);
-          return 0;
-        }
-      case 0x1fb51:
-        {
-          ctx_begin_path (ctx);
-          ctx_move_to (ctx, x, y);
-          ctx_rel_line_to (ctx, 0, -ch/3.0*2);
-          ctx_rel_line_to (ctx, cw, ch/3.0);
-          ctx_rel_line_to (ctx, 0, ch/3.0);
-          ctx_rel_line_to (ctx, -cw, 0);
-          ctx_fill (ctx);
-          return 0;
-        }
-      case 0x1fb52:
-        {
-          ctx_begin_path (ctx);
-          ctx_move_to (ctx, x, y);
-          ctx_rel_move_to (ctx, 0, -ch/3.0);
-          ctx_rel_line_to (ctx, 0, -ch/3.0*2);
-          ctx_rel_line_to (ctx, cw, 0);
-          ctx_rel_line_to (ctx, 0, ch);
-          ctx_rel_line_to (ctx, -cw/2, 0);
-          ctx_fill (ctx);
-          return 0;
-        }
-      case 0x1fb53:
-        {
-          ctx_begin_path (ctx);
-          ctx_move_to (ctx, x, y);
-          ctx_rel_move_to (ctx, 0, -ch/3.0);
-          ctx_rel_line_to (ctx, 0, -ch/3.0*2);
-          ctx_rel_line_to (ctx, cw, 0);
-          ctx_rel_line_to (ctx, 0, ch);
-          ctx_rel_line_to (ctx, -cw, -ch/3.0);
-          ctx_fill (ctx);
-          return 0;
-        }
-      case 0x1fb54:
-        {
-          ctx_begin_path (ctx);
-          ctx_move_to (ctx, x, y);
-          ctx_rel_move_to (ctx, 0, -ch/3.0*2);
-          ctx_rel_line_to (ctx, 0, -ch/3.0);
-          ctx_rel_line_to (ctx, cw, 0);
-          ctx_rel_line_to (ctx, 0, ch);
-          ctx_rel_line_to (ctx, -cw/2, 0);
-          ctx_fill (ctx);
-          return 0;
-        }
-      case 0x1fb55:
-        {
-          ctx_begin_path (ctx);
-          ctx_move_to (ctx, x, y);
-          ctx_rel_move_to (ctx, 0, -ch/3.0*2);
-          ctx_rel_line_to (ctx, 0, -ch/3.0);
-          ctx_rel_line_to (ctx, cw, 0);
-          ctx_rel_line_to (ctx, 0, ch);
-          ctx_rel_line_to (ctx, -cw, -ch/3.0*2);
-          ctx_fill (ctx);
-          return 0;
-        }
-      case 0x1fb56:
-        {
-          ctx_begin_path (ctx);
-          ctx_move_to (ctx, x, y);
-          ctx_rel_move_to (ctx, 0, -ch);
-          ctx_rel_line_to (ctx, cw, 0);
-          ctx_rel_line_to (ctx, 0, ch);
-          ctx_rel_line_to (ctx, -cw/2, 0);
-          ctx_rel_line_to (ctx, -cw/2, -ch);
-          ctx_fill (ctx);
-          return 0;
-        }
-      case 0x1fb57:
-        {
-          ctx_begin_path (ctx);
-          ctx_move_to (ctx, x, y);
-          ctx_rel_move_to (ctx, 0, -ch/3.0*2);
-          ctx_rel_line_to (ctx, 0, -ch/3);
-          ctx_rel_line_to (ctx, cw/2, 0);
-          ctx_rel_line_to (ctx, -cw/2, ch/3);
-          ctx_fill (ctx);
-          return 0;
-        }
-      case 0x1fb58:
-        {
-          ctx_begin_path (ctx);
-          ctx_move_to (ctx, x, y);
-          ctx_rel_move_to (ctx, 0, -ch/3.0*2);
-          ctx_rel_line_to (ctx, 0, -ch/3);
-          ctx_rel_line_to (ctx, cw, 0);
-          ctx_rel_line_to (ctx, -cw, ch/3);
-          ctx_fill (ctx);
-          return 0;
-        }
-      case 0x1fb59:
-        {
-          ctx_begin_path (ctx);
-          ctx_move_to (ctx, x, y);
-          ctx_rel_move_to (ctx, 0, -ch/3.0);
-          ctx_rel_line_to (ctx, 0, -ch/3.0*2);
-          ctx_rel_line_to (ctx, cw/2, 0);
-          ctx_rel_line_to (ctx, -cw/2, ch/3 * 2);
-          ctx_fill (ctx);
-          return 0;
-        }
-      case 0x1fb5a:
-        {
-          ctx_begin_path (ctx);
-          ctx_move_to (ctx, x, y);
-          ctx_rel_move_to (ctx, 0, -ch/3.0);
-          ctx_rel_line_to (ctx, 0, -ch/3.0*2);
-          ctx_rel_line_to (ctx, cw, 0);
-          ctx_rel_line_to (ctx, -cw, ch/3 * 2);
-          ctx_fill (ctx);
-          return 0;
-        }
-      case 0x1fb5b:
-        {
-          ctx_begin_path (ctx);
-          ctx_move_to (ctx, x, y);
-          ctx_rel_line_to (ctx, 0, -ch);
-          ctx_rel_line_to (ctx, cw/2, 0);
-          ctx_rel_line_to (ctx, -cw/2, ch);
-          ctx_fill (ctx);
-          return 0;
-        }
-      case 0x1fb5c:
-        {
-          ctx_begin_path (ctx);
-          ctx_move_to (ctx, x, y);
-          ctx_rel_move_to (ctx, 0, -ch/3);
-
-          ctx_rel_line_to (ctx, 0, -ch/3.0*2);
-          ctx_rel_line_to (ctx, cw, 0);
-          ctx_rel_line_to (ctx, 0, ch/3);
-          ctx_rel_line_to (ctx, -cw, ch/3);
-          ctx_fill (ctx);
-          return 0;
-        }
-      case 0x1fb5d:
-        {
-          ctx_begin_path (ctx);
-          ctx_move_to (ctx, x, y);
-          ctx_rel_line_to (ctx, 0, -ch);
-          ctx_rel_line_to (ctx, cw, 0);
-          ctx_rel_line_to (ctx, 0, ch/3 * 2);
-          ctx_rel_line_to (ctx, -cw/2, ch/3);
-          ctx_rel_line_to (ctx, -cw/2, 0);
-          ctx_fill (ctx);
-          return 0;
-        }
-      case 0x1fb5e:
-        {
-          ctx_begin_path (ctx);
-          ctx_move_to (ctx, x, y);
-          ctx_rel_line_to (ctx, 0, -ch);
-          ctx_rel_line_to (ctx, cw, 0);
-          ctx_rel_line_to (ctx, 0, ch/3 * 2);
-          ctx_rel_line_to (ctx, -cw, ch/3);
-          ctx_fill (ctx);
-          return 0;
-        }
-      case 0x1fb5f:
-        {
-          ctx_begin_path (ctx);
-          ctx_move_to (ctx, x, y);
-          ctx_rel_line_to (ctx, 0, -ch);
-          ctx_rel_line_to (ctx, cw, 0);
-          ctx_rel_line_to (ctx, 0, ch/3);
-          ctx_rel_line_to (ctx, -cw/2, ch/3*2);
-          ctx_rel_line_to (ctx, -cw/2, 0);
-          ctx_fill (ctx);
-          return 0;
-        }
-      case 0x1fb60:
-        {
-          ctx_begin_path (ctx);
-          ctx_move_to (ctx, x, y);
-          ctx_rel_line_to (ctx, 0, -ch);
-          ctx_rel_line_to (ctx, cw, 0);
-          ctx_rel_line_to (ctx, 0, ch/3);
-          ctx_rel_line_to (ctx, -cw, ch/3*2);
-          ctx_fill (ctx);
-          return 0;
-        }
-      case 0x1fb61:
-        {
-          ctx_begin_path (ctx);
-          ctx_move_to (ctx, x, y);
-          ctx_rel_line_to (ctx, 0, -ch);
-          ctx_rel_line_to (ctx, cw, 0);
-          ctx_rel_line_to (ctx, -cw/2, ch);
-          ctx_rel_line_to (ctx, -cw/2, 0);
-          ctx_fill (ctx);
-          return 0;
-        }
-      case 0x1fb62:
-        {
-          ctx_begin_path (ctx);
-          ctx_move_to (ctx, x, y);
-          ctx_rel_move_to (ctx, cw/2, -ch);
-          ctx_rel_line_to (ctx, cw/2, 0);
-          ctx_rel_line_to (ctx, 0, ch/3);
-          ctx_rel_line_to (ctx, -cw/2, -ch/3);
-          ctx_fill (ctx);
-          return 0;
-        }
-      case 0x1fb63:
-        {
-          ctx_begin_path (ctx);
-          ctx_move_to (ctx, x, y);
-          ctx_rel_move_to (ctx, 0, -ch);
-          ctx_rel_line_to (ctx, cw, 0);
-          ctx_rel_line_to (ctx, 0, ch/3);
-          ctx_rel_line_to (ctx, -cw, -ch/3);
-          ctx_fill (ctx);
-          return 0;
-        }
-      case 0x1fb64:
-        {
-          ctx_begin_path (ctx);
-          ctx_move_to (ctx, x, y);
-          ctx_rel_move_to (ctx, cw/2, -ch);
-          ctx_rel_line_to (ctx, cw/2, 0);
-          ctx_rel_line_to (ctx, 0, ch/3*2);
-          ctx_rel_line_to (ctx, -cw/2, -ch/3*2);
-          ctx_fill (ctx);
-          return 0;
-        }
-      case 0x1fb65:
-        {
-          ctx_begin_path (ctx);
-          ctx_move_to (ctx, x, y);
-          ctx_rel_move_to (ctx, 0, -ch);
-          ctx_rel_line_to (ctx, cw, 0);
-          ctx_rel_line_to (ctx, 0, ch/3*2);
-          ctx_rel_line_to (ctx, -cw, -ch/3*2);
-          ctx_fill (ctx);
-          return 0;
-        }
-      case 0x1fb66:
-        {
-          ctx_begin_path (ctx);
-          ctx_move_to (ctx, x, y);
-          ctx_rel_move_to (ctx, cw/2, -ch);
-          ctx_rel_line_to (ctx, cw/2, 0);
-          ctx_rel_line_to (ctx, 0, ch);
-          ctx_rel_line_to (ctx, -cw/2, -ch);
-          ctx_fill (ctx);
-          return 0;
-        }
-      case 0x1fb67:
-        {
-          ctx_begin_path (ctx);
-          ctx_move_to (ctx, x, y);
-          ctx_rel_move_to (ctx, 0, -ch/3.0*2);
-          ctx_rel_line_to (ctx, 0, -ch/3);
-          ctx_rel_line_to (ctx, cw, 0);
-          ctx_rel_line_to (ctx, 0, ch/3.0*2);
-          ctx_rel_line_to (ctx, -cw, -ch/3.0);
-          ctx_fill (ctx);
-          return 0;
-        }
-      case 0x1fb68:
-        {
-          ctx_begin_path (ctx);
-          ctx_move_to (ctx, x, y);
-          ctx_rel_line_to (ctx, cw/2, -ch/2);
-          ctx_rel_line_to (ctx, -cw/2, -ch/2);
-          ctx_rel_line_to (ctx, cw, 0);
-          ctx_rel_line_to (ctx, 0, ch);
-          ctx_rel_line_to (ctx, -cw, 0);
-          ctx_fill (ctx);
-          return 0;
-        }
-      case 0x1fb69:
-        {
-          ctx_begin_path (ctx);
-          ctx_move_to (ctx, x, y);
-          ctx_rel_line_to (ctx, 0, -ch);
-          ctx_rel_line_to (ctx, cw/2, ch/2);
-          ctx_rel_line_to (ctx, cw/2, -ch/2);
-          ctx_rel_line_to (ctx, 0, ch);
-          ctx_rel_line_to (ctx, -cw, 0);
-          ctx_fill (ctx);
-          return 0;
-        }
-      case 0x1fb6a:
-        {
-          ctx_begin_path (ctx);
-          ctx_move_to (ctx, x, y);
-          ctx_rel_line_to (ctx, 0, -ch);
-          ctx_rel_line_to (ctx, cw, 0);
-          ctx_rel_line_to (ctx, -cw/2, ch/2);
-          ctx_rel_line_to (ctx, cw/2, ch/2);
-          ctx_rel_line_to (ctx, -cw, 0);
-          ctx_fill (ctx);
-          return 0;
-        }
-      case 0x1fb6b:
-        {
-          ctx_begin_path (ctx);
-          ctx_move_to (ctx, x, y);
-          ctx_rel_line_to (ctx, 0, -ch);
-          ctx_rel_line_to (ctx, cw, 0);
-          ctx_rel_line_to (ctx, 0, ch);
-          ctx_rel_line_to (ctx, -cw/2, -ch/2);
-          ctx_rel_line_to (ctx, -cw/2, ch/2);
-          ctx_fill (ctx);
-          return 0;
-        }
-      case 0x1fb6c:
-        {
-          ctx_begin_path (ctx);
-          ctx_move_to (ctx, x, y);
-          ctx_rel_line_to (ctx, 0, -ch);
-          ctx_rel_line_to (ctx, cw/2, ch/2);
-          ctx_rel_line_to (ctx, -cw/2, ch/2);
-          ctx_fill (ctx);
-          return 0;
-        }
-      case 0x1fb6d:
-        {
-          ctx_begin_path (ctx);
-          ctx_move_to (ctx, x, y);
-          ctx_rel_move_to (ctx, 0, -ch);
-          ctx_rel_line_to (ctx, cw, 0);
-          ctx_rel_line_to (ctx, -cw/2, ch/2);
-          ctx_rel_line_to (ctx, -cw/2, -ch/2);
-          ctx_fill (ctx);
-          return 0;
-        }
-      case 0x1fb6e:
-        {
-          ctx_begin_path (ctx);
-          ctx_move_to (ctx, x, y);
-          ctx_rel_move_to (ctx, cw, -ch);
-          ctx_rel_line_to (ctx, 0, ch);
-          ctx_rel_line_to (ctx, -cw/2, -ch/2);
-          ctx_rel_line_to (ctx, cw/2, -ch/2);
-          ctx_fill (ctx);
-          return 0;
-        }
-      case 0x1fb6f:
-        {
-          ctx_begin_path (ctx);
-          ctx_move_to (ctx, x, y);
-          ctx_rel_line_to (ctx, cw/2, -ch/2);
-          ctx_rel_line_to (ctx, cw/2, ch/2);
-          ctx_rel_line_to (ctx, -cw, 0);
-          ctx_fill (ctx);
-          return 0;
-        }
-      case 0x1fb82:
-        {
-          ctx_begin_path (ctx);
-          ctx_move_to (ctx, x, y);
-          ctx_rel_move_to (ctx, 0, -ch/8 * 2);
-          ctx_rel_line_to (ctx, cw, 0);
-          ctx_rel_move_to (ctx, 0, ch/8 * 2);
-          ctx_rel_line_to (ctx, -cw, 0);
-          ctx_fill (ctx);
-          return 0;
-        }
-      case 0x1fb83:
-        {
-          ctx_begin_path (ctx);
-          ctx_move_to (ctx, x, y);
-          ctx_rel_move_to (ctx, 0, -ch/8 * 3);
-          ctx_rel_line_to (ctx, cw, 0);
-          ctx_rel_move_to (ctx, 0, ch/8 * 3);
-          ctx_rel_line_to (ctx, -cw, 0);
-          ctx_fill (ctx);
-          return 0;
-        }
-      case 0x1fb84:
-        {
-          ctx_begin_path (ctx);
-          ctx_move_to (ctx, x, y);
-          ctx_rel_move_to (ctx, 0, -ch/8 * 5);
-          ctx_rel_line_to (ctx, cw, 0);
-          ctx_rel_move_to (ctx, 0, ch/8 * 5);
-          ctx_rel_line_to (ctx, -cw, 0);
-          ctx_fill (ctx);
-          return 0;
-        }
-      case 0x1fb85:
-        {
-          ctx_begin_path (ctx);
-          ctx_move_to (ctx, x, y);
-          ctx_rel_move_to (ctx, 0, -ch/8 * 6);
-          ctx_rel_line_to (ctx, cw, 0);
-          ctx_rel_move_to (ctx, 0, ch/8 * 6);
-          ctx_rel_line_to (ctx, -cw, 0);
-          ctx_fill (ctx);
-          return 0;
-        }
-      case 0x1fb86:
-        {
-          ctx_begin_path (ctx);
-          ctx_move_to (ctx, x, y);
-          ctx_rel_move_to (ctx, 0, -ch/8 * 7);
-          ctx_rel_line_to (ctx, cw, 0);
-          ctx_rel_move_to (ctx, 0, ch/8 * 7);
-          ctx_rel_line_to (ctx, -cw, 0);
-          ctx_fill (ctx);
-          return 0;
-        }
-      case 0x1fb87:
-        {
-          ctx_begin_path (ctx);
-          ctx_move_to (ctx, x, y);
-          ctx_rel_move_to (ctx, cw/8*6, 0);
-          ctx_rel_line_to (ctx, 0, -ch);
-          ctx_rel_move_to (ctx, cw/8*2, 0);
-          ctx_rel_line_to (ctx, 0, ch);
-          ctx_rel_move_to (ctx, -cw/8*2, 0);
-          ctx_fill (ctx);
-          return 0;
-        }
-      case 0x1fb88:
-        {
-          ctx_begin_path (ctx);
-          ctx_move_to (ctx, x, y);
-          ctx_rel_move_to (ctx, cw/8*5, 0);
-          ctx_rel_line_to (ctx, 0, -ch);
-          ctx_rel_move_to (ctx, cw/8*3, 0);
-          ctx_rel_line_to (ctx, 0, ch);
-          ctx_rel_move_to (ctx, -cw/8*3, 0);
-          ctx_fill (ctx);
-          return 0;
-        }
-      case 0x1fb89:
-        {
-          ctx_begin_path (ctx);
-          ctx_move_to (ctx, x, y);
-          ctx_rel_move_to (ctx, cw/8*3, 0);
-          ctx_rel_line_to (ctx, 0, -ch);
-          ctx_rel_move_to (ctx, cw/8*5, 0);
-          ctx_rel_line_to (ctx, 0, ch);
-          ctx_rel_move_to (ctx, -cw/8*5, 0);
-          ctx_fill (ctx);
-          return 0;
-        }
-      case 0x1fb8a:
-        {
-          ctx_begin_path (ctx);
-          ctx_move_to (ctx, x, y);
-          ctx_rel_move_to (ctx, cw/8*2, 0);
-          ctx_rel_line_to (ctx, 0, -ch);
-          ctx_rel_move_to (ctx, cw/8*6, 0);
-          ctx_rel_line_to (ctx, 0, ch);
-          ctx_rel_move_to (ctx, -cw/8*6, 0);
-          ctx_fill (ctx);
-          return 0;
-        }
-      case 0x1fb97:
-        {
-          ctx_begin_path (ctx);
-          ctx_move_to (ctx, x, y);
-          ctx_rel_line_to (ctx, 0, -ch/4);
-          ctx_rel_move_to (ctx, cw, 0);
-          ctx_rel_line_to (ctx, 0, ch/4);
-          ctx_rel_line_to (ctx, -cw, 0);
-          ctx_close_path (ctx);
-          ctx_move_to (ctx, 0, -ch/2);
-          ctx_rel_line_to (ctx, 0, -ch/4);
-          ctx_rel_move_to (ctx, cw, 0);
-          ctx_rel_line_to (ctx, 0, ch/4);
-          ctx_rel_line_to (ctx, -cw, 0);
-          ctx_fill (ctx);
-          return 0;
-        }
-      case 0x1fb9a:
-        {
-          ctx_begin_path (ctx);
-          ctx_move_to (ctx, x, y);
-          ctx_rel_line_to (ctx, cw/2, -ch/2);
-          ctx_rel_line_to (ctx, -cw/2, -ch/2);
-          ctx_rel_move_to (ctx, cw, 0);
-          ctx_rel_line_to (ctx, -cw/2, ch/2);
-          ctx_rel_line_to (ctx, cw/2, ch/2);
-          ctx_rel_line_to (ctx, -cw, 0);
-          ctx_fill (ctx);
-          return 0;
-        }
-      case 0x1fb9b:
-        {
-          ctx_begin_path (ctx);
-          ctx_move_to (ctx, x, y);
-          ctx_rel_line_to (ctx, 0, -ch);
-          ctx_rel_line_to (ctx, cw/2, ch/2);
-          ctx_rel_line_to (ctx, cw/2, -ch/2);
-          ctx_rel_line_to (ctx, 0, ch);
-          ctx_rel_line_to (ctx, -cw/2, -ch/2);
-          ctx_rel_line_to (ctx, -cw/2, ch/2);
-          ctx_fill (ctx);
-          return 0;
-        }
-
     }
-  return -1;
-}
-
-void vt_ctx_glyph (Ctx *ctx, VT *vt, float x, float y, int unichar, int bold, float scale_x, float scale_y, 
float offset_y)
-{
-  int did_save = 0;
-  if (unichar <= ' ')
-    return;
-  scale_x *= vt->scale_x;
-  scale_y *= vt->scale_y;
 
-  if (!ctx_renderer_is_term (ctx))
-  {
-    // TODO : use our own special glyphs when glyphs are not passed through
-    if (!vt_special_glyph (ctx, vt, x, y + offset_y * vt->ch, vt->cw * scale_x, vt->ch * scale_y, unichar) )
-      return;
-  }
+#if 1
+    if (
+      //(compositing_mode == CTX_COMPOSITE_DESTINATION_OVER && dst[components-1] == 255)||
+      (compositing_mode == CTX_COMPOSITE_SOURCE_OVER      && is_blank) ||
+      (compositing_mode == CTX_COMPOSITE_XOR              && is_blank) ||
+      (compositing_mode == CTX_COMPOSITE_DESTINATION_OUT  && is_blank) ||
+      (compositing_mode == CTX_COMPOSITE_SOURCE_ATOP      && is_blank)
+      )
+    {
+      u0 += ud * n_pix;
+      v0 += vd * n_pix;
+      coverage += n_pix;
+      dst+=32;
+      continue;
+    }
+#endif
 
-  if (scale_x != 1.0 || scale_y != 1.0)
+    if (fragment)
+    {
+      for (int i = 0; i < n_pix; i++)
+      {
+         fragment (rasterizer, u0, v0, &tsrc[i*components], 1, ud, vd);
+         ctx_u8_associate_alpha (components, &tsrc[i*components]);
+         ctx_u8_blend (components, blend,
+                       &dst[i*components],
+                       &tsrc[i*components],
+                       &tsrc[i*components]);
+         u0 += ud;
+         v0 += vd;
+      }
+      xsrc = _mm256_loadu_si256((__m256i*)tsrc);
+    }
+    else
     {
-      if (!did_save)
+#if 0
+      if (blend == CTX_BLEND_NORMAL && components == 4)
+        xsrc = _mm256_set1_epi32 (*((uint32_t*)src));
+    else
+#endif
       {
-        ctx_save (ctx);
-        did_save = 1;
+ //     for (int i = 0; i < n_pix; i++)
+ //       for (int c = 0; c < components; c++)
+ //         tsrc[i*components+c]=src[c];
+#if 1
+        uint8_t lsrc[components];
+        for (int i = 0; i < components; i ++)
+          lsrc[i] = src[i];
+  //    ctx_u8_associate_alpha (components, lsrc);
+        for (int i = 0; i < n_pix; i++)
+          ctx_u8_blend (components, blend,
+                        &dst[i*components],
+                        lsrc,
+                        &tsrc[i*components]);
+#endif
+        xsrc = _mm256_loadu_si256((__m256i*)tsrc);
       }
+    }
 
-      ctx_translate (ctx, x, y);
-      ctx_scale (ctx, scale_x, scale_y);
-      ctx_translate (ctx, -x, -y);
+    if (is_full)
+       xcov = _mm256_set1_epi16(255);
+    else
+    switch (n_pix)
+    {
+      case 4: xcov  = _mm256_set_epi16(
+               (coverage[3]), (coverage[3]), coverage[3], coverage[3],
+               (coverage[2]), (coverage[2]), coverage[2], coverage[2],
+               (coverage[1]), (coverage[1]), coverage[1], coverage[1],
+               (coverage[0]), (coverage[0]), coverage[0], coverage[0]);
+              break;
+      case 8: 
+          xcov  = _mm256_set_epi32(coverage[7],
+                                   coverage[6],
+                                   coverage[5],
+                                   coverage[4],
+                                   coverage[3],
+                                   coverage[2],
+                                   coverage[1],
+                                   coverage[0]
+                                   );
+          xcov |= xcov << 16;
+              break;
+      case 16: xcov  = _mm256_set_epi16(
+               (coverage[15]),
+               (coverage[14]),
+               (coverage[13]),
+               (coverage[12]),
+               (coverage[11]),
+               (coverage[10]),
+               (coverage[9]),
+               (coverage[8]),
+               (coverage[7]),
+               (coverage[6]),
+               (coverage[5]),
+               (coverage[4]),
+               (coverage[3]),
+               (coverage[2]),
+               (coverage[1]),
+               (coverage[0]));
+              break;
     }
-  if (offset_y != 0.0f)
-  {
-    if (!did_save)
+#if 0
+    switch (n_pix)
     {
-      ctx_save (ctx);
-      did_save = 1;
+      case 4:
+      xsrc_a = _mm256_set_epi16(
+            tsrc[3*components+(components-1)], 
tsrc[3*components+(components-1)],tsrc[3*components+(components-1)], tsrc[3*components+(components-1)],
+            tsrc[2*components+(components-1)], 
tsrc[2*components+(components-1)],tsrc[2*components+(components-1)], tsrc[2*components+(components-1)],
+            tsrc[1*components+(components-1)], 
tsrc[1*components+(components-1)],tsrc[1*components+(components-1)], tsrc[1*components+(components-1)],
+            tsrc[0*components+(components-1)], 
tsrc[0*components+(components-1)],tsrc[0*components+(components-1)], tsrc[0*components+(components-1)]);
+      xdst_a = _mm256_set_epi16(
+            dst[3*components+(components-1)], 
dst[3*components+(components-1)],dst[3*components+(components-1)], dst[3*components+(components-1)],
+            dst[2*components+(components-1)], 
dst[2*components+(components-1)],dst[2*components+(components-1)], dst[2*components+(components-1)],
+            dst[1*components+(components-1)], 
dst[1*components+(components-1)],dst[1*components+(components-1)], dst[1*components+(components-1)],
+            dst[0*components+(components-1)], 
dst[0*components+(components-1)],dst[0*components+(components-1)], dst[0*components+(components-1)]);
+
+              break;
+      case 8:
+      xsrc_a = _mm256_set_epi16(
+            tsrc[7*components+(components-1)], tsrc[7*components+(components-1)],
+            tsrc[6*components+(components-1)], tsrc[6*components+(components-1)],
+            tsrc[5*components+(components-1)], tsrc[5*components+(components-1)],
+            tsrc[4*components+(components-1)], tsrc[4*components+(components-1)],
+            tsrc[3*components+(components-1)], tsrc[3*components+(components-1)],
+            tsrc[2*components+(components-1)], tsrc[2*components+(components-1)],
+            tsrc[1*components+(components-1)], tsrc[1*components+(components-1)],
+            tsrc[0*components+(components-1)], tsrc[0*components+(components-1)]);
+      xdst_a = _mm256_set_epi16(
+            dst[7*components+(components-1)], dst[7*components+(components-1)],
+            dst[6*components+(components-1)], dst[6*components+(components-1)],
+            dst[5*components+(components-1)], dst[5*components+(components-1)],
+            dst[4*components+(components-1)], dst[4*components+(components-1)],
+            dst[3*components+(components-1)], dst[3*components+(components-1)],
+            dst[2*components+(components-1)], dst[2*components+(components-1)],
+            dst[1*components+(components-1)], dst[1*components+(components-1)],
+            dst[0*components+(components-1)], dst[0*components+(components-1)]);
+              break;
+      case 16: 
+      xsrc_a = _mm256_set_epi16(
+            tsrc[15*components+(components-1)],
+            tsrc[14*components+(components-1)],
+            tsrc[13*components+(components-1)],
+            tsrc[12*components+(components-1)],
+            tsrc[11*components+(components-1)],
+            tsrc[10*components+(components-1)],
+            tsrc[9*components+(components-1)],
+            tsrc[8*components+(components-1)],
+            tsrc[7*components+(components-1)],
+            tsrc[6*components+(components-1)],
+            tsrc[5*components+(components-1)],
+            tsrc[4*components+(components-1)],
+            tsrc[3*components+(components-1)],
+            tsrc[2*components+(components-1)],
+            tsrc[1*components+(components-1)],
+            tsrc[0*components+(components-1)]);
+      xdst_a = _mm256_set_epi16(
+            dst[15*components+(components-1)],
+            dst[14*components+(components-1)],
+            dst[13*components+(components-1)],
+            dst[12*components+(components-1)],
+            dst[11*components+(components-1)],
+            dst[10*components+(components-1)],
+            dst[9*components+(components-1)],
+            dst[8*components+(components-1)],
+            dst[7*components+(components-1)],
+            dst[6*components+(components-1)],
+            dst[5*components+(components-1)],
+            dst[4*components+(components-1)],
+            dst[3*components+(components-1)],
+            dst[2*components+(components-1)],
+            dst[1*components+(components-1)],
+            dst[0*components+(components-1)]);
+              break;
     }
-    ctx_translate (ctx, 0, vt->font_size * offset_y);
-  }
-  y -= vt->font_size * 0.22;
-  if (bold
-      && !ctx_renderer_is_term (ctx)
-     )
+#endif
+
+    if (global_alpha_u8 != 255)
     {
-      ctx_move_to (ctx, x - vt->font_size/30.0, y);
-      //ctx_line_width (ctx, vt->font_size/30.0);
-      ctx_glyph (ctx, unichar, 0);
+      xcov = _mm256_mulhi_epu16(
+              _mm256_adds_epu16(
+                 _mm256_mullo_epi16(xcov,
+                                    _mm256_set1_epi16(global_alpha_u8)),
+                 x0080), x0101);
+      is_full = 0;
     }
-  ctx_move_to (ctx, x, y);
-  ctx_glyph (ctx, unichar, 0);
-  if (did_save)
-    ctx_restore (ctx);
-}
 
-//static uint8_t palette[256][3];
 
-/* optimized for ANSI ART - and avoidance of human metamers
- * among color deficient vision - by distributing and pertubating
- * until all 64 combinations - sans self application, have
- * likely to be discernable by humans.
- */
+    xsrc_a = _mm256_srli_epi32(xsrc, 24);  // XX 24 is RGB specific
+    if (!is_full)
+    xsrc_a = _mm256_mulhi_epu16(
+              _mm256_adds_epu16(
+                 _mm256_mullo_epi16(xsrc_a, xcov),
+                 x0080), x0101);
+    xsrc_a = xsrc_a | _mm256_slli_epi32(xsrc, 16);
+
+    xdst_a = _mm256_srli_epi32(xdst, 24);
+    xdst_a = xdst_a |  _mm256_slli_epi32(xdst, 16);
+
+
+ //  case CTX_COMPOSITE_SOURCE_OVER:
+ //     f_s = CTX_PORTER_DUFF_1;
+ //     f_d = CTX_PORTER_DUFF_1_MINUS_ALPHA;
+
+
+    __m256i dst_lo;
+    __m256i dst_hi; 
+    __m256i src_lo; 
+    __m256i src_hi;
+
+    switch (f_s)
+    {
+      case CTX_PORTER_DUFF_0:
+        src_lo = _mm256_set1_epi32(0);
+        src_hi = _mm256_set1_epi32(0);
+        break;
+      case CTX_PORTER_DUFF_1:
+        src_lo = _mm256_and_si256 (xsrc, lo_mask); 
+        src_hi = _mm256_srli_epi16 (_mm256_and_si256 (xsrc, hi_mask), 8);
+
+        //if (!is_full)
+        {
+          src_lo = _mm256_mulhi_epu16(
+                   _mm256_adds_epu16(
+                   _mm256_mullo_epi16(src_lo, xcov),
+                   x0080), x0101);
+          src_hi = _mm256_mulhi_epu16(
+                   _mm256_adds_epu16(
+                   _mm256_mullo_epi16(src_hi, xcov),
+                   x0080), x0101);
+        }
+        break;
+      case CTX_PORTER_DUFF_ALPHA:
+        // res += (tsrc[c] *      dst[components-1])/255;
+        src_lo = _mm256_and_si256 (xsrc, lo_mask); 
+        src_hi = _mm256_srli_epi16 (_mm256_and_si256 (xsrc, hi_mask), 8);
+        if (!is_full)
+        {
+           src_lo = _mm256_mulhi_epu16(
+                 _mm256_adds_epu16(
+                 _mm256_mullo_epi16(src_lo, xcov),
+                 x0080), x0101);
+           src_hi = _mm256_mulhi_epu16(
+                    _mm256_adds_epu16(
+                    _mm256_mullo_epi16(src_hi, xcov),
+                    x0080), x0101);
+        }
+        src_lo = _mm256_mulhi_epu16 (
+                      _mm256_adds_epu16 (_mm256_mullo_epi16(src_lo,
+                                         xdst_a), x0080), x0101);
+        src_hi = _mm256_mulhi_epu16 (
+                      _mm256_adds_epu16 (_mm256_mullo_epi16(src_hi,
+                                         xdst_a), x0080), x0101);
+        break;
+      case CTX_PORTER_DUFF_1_MINUS_ALPHA:
+        // res += (tsrc[c] * (255-dst[components-1]))/255;
+        src_lo = _mm256_and_si256 (xsrc, lo_mask); 
+        src_hi = _mm256_srli_epi16 (_mm256_and_si256 (xsrc, hi_mask), 8);
+  //    if (!is_full)
+        {
+          src_lo = _mm256_mulhi_epu16(
+                        _mm256_adds_epu16(
+                        _mm256_mullo_epi16(src_lo, xcov),
+                        x0080), x0101);
+          src_hi = _mm256_mulhi_epu16(
+                        _mm256_adds_epu16(
+                        _mm256_mullo_epi16(src_hi, xcov),
+                        x0080), x0101);
+        }
+        src_lo = _mm256_mulhi_epu16 (
+                  _mm256_adds_epu16 (_mm256_mullo_epi16(src_lo,
+                                     _mm256_sub_epi16(x00ff,xdst_a)), x0080),
+                  x0101);
+        src_hi = _mm256_mulhi_epu16 (
+                 _mm256_adds_epu16 (_mm256_mullo_epi16(src_hi,
+                                    _mm256_sub_epi16(x00ff,xdst_a)), x0080),
+                 x0101);
+        break;
+    }
+    switch (f_d)
+    {
+      case CTX_PORTER_DUFF_0: 
+        dst_lo = _mm256_set1_epi32(0);
+        dst_hi = _mm256_set1_epi32(0);
+        break;
+      case CTX_PORTER_DUFF_1:
+        dst_lo = _mm256_and_si256 (xdst, lo_mask); 
+        dst_hi = _mm256_srli_epi16 (_mm256_and_si256 (xdst, hi_mask), 8); 
+        break;
+      case CTX_PORTER_DUFF_ALPHA:        
+          //res += (dst[c] * tsrc[components-1])/255;
+          dst_lo = _mm256_and_si256 (xdst, lo_mask); 
+          dst_hi = _mm256_srli_epi16 (_mm256_and_si256 (xdst, hi_mask), 8); 
+
+          dst_lo =
+             _mm256_mulhi_epu16 (
+                 _mm256_adds_epu16 (_mm256_mullo_epi16(dst_lo,
+                                    xsrc_a), x0080), x0101);
+          dst_hi =
+             _mm256_mulhi_epu16 (
+                 _mm256_adds_epu16 (_mm256_mullo_epi16(dst_hi,
+                                    xsrc_a), x0080), x0101);
+          break;
+      case CTX_PORTER_DUFF_1_MINUS_ALPHA:
+          dst_lo = _mm256_and_si256 (xdst, lo_mask); 
+          dst_hi = _mm256_srli_epi16 (_mm256_and_si256 (xdst, hi_mask), 8); 
+          dst_lo = 
+             _mm256_mulhi_epu16 (
+                 _mm256_adds_epu16 (_mm256_mullo_epi16(dst_lo,
+                                    _mm256_sub_epi16(x00ff,xsrc_a)), x0080),
+                 x0101);
+          dst_hi = 
+             _mm256_mulhi_epu16 (
+                 _mm256_adds_epu16 (_mm256_mullo_epi16(dst_hi,
+                                    _mm256_sub_epi16(x00ff,xsrc_a)), x0080),
+                 x0101);
+          break;
+    }
+
+    dst_hi = _mm256_adds_epu16(dst_hi, src_hi);
+    dst_lo = _mm256_adds_epu16(dst_lo, src_lo);
 
+#if 0 // to toggle source vs dst
+      src_hi = _mm256_slli_epi16 (src_hi, 8);
+      _mm256_storeu_si256((__m256i*)dst, _mm256_blendv_epi8(src_lo, src_hi, hi_mask));
+#else
+      _mm256_storeu_si256((__m256i*)dst, _mm256_slli_epi16 (dst_hi, 8) | dst_lo);
+#endif
+
+    coverage += n_pix;
+    dst      += 32;
+  }
+}
+#endif
 
-void vt_ctx_get_color (VT *vt, int no, int intensity, uint8_t *rgba)
+CTX_INLINE static void
+_ctx_u8_porter_duff (CtxRasterizer         *rasterizer,
+                     int                    components,
+                     uint8_t *              dst,
+                     uint8_t * __restrict__ src,
+                     int                    x0,
+                     uint8_t *              coverage,
+                     int                    count,
+                     CtxCompositingMode     compositing_mode,
+                     CtxFragment            fragment,
+                     CtxBlend               blend)
 {
-  uint8_t r = 0, g = 0, b = 0;
-  if (no < 16 && no >= 0)
-    {
-      switch (intensity)
-        {
-          case 0:
-            no = 0;
-            break;
-          case 1:
-            // 15 becomes 7
-            if (no == 15) { no = 8; }
-            else if (no > 8) { no -= 8; }
-            break;
-          case 2:
-            /* give the normal color special treatment, and in really normal
-             * cirumstances it is the dim variant of foreground that is used
-             */
-            if (no == 15) { no = 7; }
-            break;
-          case 3:
-          case 4:
-            if (no < 8)
-              { no += 8; }
-            break;
-          default:
-            break;
-        }
-      r = palettes[vt->palette_no][no][0];
-      g = palettes[vt->palette_no][no][1];
-      b = palettes[vt->palette_no][no][2];
-    }
-  else if (no < 16 + 6*6*6)
-    {
-      no = no-16;
-      b = (no % 6) * 255 / 5;
-      no /= 6;
-      g = (no % 6) * 255 / 5;
-      no /= 6;
-      r = (no % 6) * 255 / 5;
-    }
-  else
-    {
-      int gray = no - (16 + 6*6*6);
-      float val = gray * 255 / 24;
-      r = g = b = val;
-    }
-  rgba[0]=r;
-  rgba[1]=g;
-  rgba[2]=b;
-  rgba[3]=255;
+#if NOT_USABLE_CTX_AVX2
+  int pre_count = 0;
+  if ((size_t)(dst)&31)
+  {
+    pre_count = (32-(((size_t)(dst))&31))/components;
+  __ctx_u8_porter_duff (rasterizer, components,
+     dst, src, x0, coverage, pre_count, compositing_mode, fragment, blend);
+    dst += components * pre_count;
+    x0 += pre_count;
+    coverage += pre_count;
+    count -= pre_count;
+  }
+  if (count < 0)
+     return;
+  int post_count = (count & 31);
+  if (src && 0)
+  {
+    src[0]/=2;
+    src[1]/=2;
+    src[2]/=2;
+    src[3]/=2;
+  }
+#if 0
+  __ctx_u8_porter_duff (rasterizer, components, dst, src, x0, coverage, count-post_count, compositing_mode, 
fragment, blend);
+#else
+  ctx_avx2_porter_duff (rasterizer, components, dst, src, x0, coverage, count-post_count, compositing_mode, 
fragment, blend);
+#endif
+  if (src && 0)
+  {
+    src[0]*=2;
+    src[1]*=2;
+    src[2]*=2;
+    src[3]*=2;
+  }
+  if (post_count > 0)
+  {
+       x0 += (count - post_count);
+       dst += components * (count-post_count);
+       coverage += (count - post_count);
+       __ctx_u8_porter_duff (rasterizer, components, dst, src, x0, coverage, post_count, compositing_mode, 
fragment, blend);
+  }
+#else
+  __ctx_u8_porter_duff (rasterizer, components, dst, src, x0, coverage, count, compositing_mode, fragment, 
blend);
+#endif
+}
+
+#define _ctx_u8_porter_duffs(comp_format, components, source, fragment, blend) \
+   switch (rasterizer->state->gstate.compositing_mode) \
+   { \
+     case CTX_COMPOSITE_SOURCE_ATOP: \
+      _ctx_u8_porter_duff (rasterizer, components, dst, src, x0, coverage, count, \
+        CTX_COMPOSITE_SOURCE_ATOP, fragment, blend);\
+      break;\
+     case CTX_COMPOSITE_DESTINATION_ATOP:\
+      _ctx_u8_porter_duff (rasterizer, components, dst, src, x0, coverage, count,\
+        CTX_COMPOSITE_DESTINATION_ATOP, fragment, blend);\
+      break;\
+     case CTX_COMPOSITE_DESTINATION_IN:\
+      _ctx_u8_porter_duff (rasterizer, components, dst, src, x0, coverage, count,\
+        CTX_COMPOSITE_DESTINATION_IN, fragment, blend);\
+      break;\
+     case CTX_COMPOSITE_DESTINATION:\
+      _ctx_u8_porter_duff (rasterizer, components, dst, src, x0, coverage, count,\
+        CTX_COMPOSITE_DESTINATION, fragment, blend);\
+       break;\
+     case CTX_COMPOSITE_SOURCE_OVER:\
+      _ctx_u8_porter_duff (rasterizer, components, dst, src, x0, coverage, count,\
+        CTX_COMPOSITE_SOURCE_OVER, fragment, blend);\
+       break;\
+     case CTX_COMPOSITE_DESTINATION_OVER:\
+      _ctx_u8_porter_duff (rasterizer, components, dst, src, x0, coverage, count,\
+        CTX_COMPOSITE_DESTINATION_OVER, fragment, blend);\
+       break;\
+     case CTX_COMPOSITE_XOR:\
+      _ctx_u8_porter_duff (rasterizer, components, dst, src, x0, coverage, count,\
+        CTX_COMPOSITE_XOR, fragment, blend);\
+       break;\
+     case CTX_COMPOSITE_DESTINATION_OUT:\
+       _ctx_u8_porter_duff (rasterizer, components, dst, src, x0, coverage, count,\
+        CTX_COMPOSITE_DESTINATION_OUT, fragment, blend);\
+       break;\
+     case CTX_COMPOSITE_SOURCE_OUT:\
+       _ctx_u8_porter_duff (rasterizer, components, dst, src, x0, coverage, count,\
+        CTX_COMPOSITE_SOURCE_OUT, fragment, blend);\
+       break;\
+     case CTX_COMPOSITE_SOURCE_IN:\
+       _ctx_u8_porter_duff (rasterizer, components, dst, src, x0, coverage, count,\
+        CTX_COMPOSITE_SOURCE_IN, fragment, blend);\
+       break;\
+     case CTX_COMPOSITE_COPY:\
+       _ctx_u8_porter_duff (rasterizer, components, dst, src, x0, coverage, count,\
+        CTX_COMPOSITE_COPY, fragment, blend);\
+       break;\
+     case CTX_COMPOSITE_CLEAR:\
+       _ctx_u8_porter_duff (rasterizer, components, dst, src, x0, coverage, count,\
+        CTX_COMPOSITE_CLEAR, fragment, blend);\
+       break;\
+   }
+
+/* generating one function per compositing_mode would be slightly more efficient,
+ * but on embedded targets leads to slightly more code bloat,
+ * here we trade off a slight amount of performance
+ */
+#define ctx_u8_porter_duff(comp_format, components, source, fragment, blend) \
+static void \
+CTX_COMPOSITE_SUFFIX(ctx_##comp_format##_porter_duff_##source) (CTX_COMPOSITE_ARGUMENTS) \
+{ \
+  _ctx_u8_porter_duffs(comp_format, components, source, fragment, blend);\
 }
 
-int vt_keyrepeat (VT *vt)
+ctx_u8_porter_duff(RGBA8, 4,color,   NULL,                 rasterizer->state->gstate.blend_mode)
+ctx_u8_porter_duff(RGBA8, 4,generic, rasterizer->fragment, rasterizer->state->gstate.blend_mode)
+
+//ctx_u8_porter_duff(comp_name, components,color_##blend_name,  NULL, blend_mode)
+
+static void
+CTX_COMPOSITE_SUFFIX(ctx_RGBA8_nop) (CTX_COMPOSITE_ARGUMENTS)
 {
-  return vt->keyrepeat;
 }
 
-static void vt_flush_bg (VT *vt, Ctx *ctx)
+
+static void
+ctx_setup_RGBA8 (CtxRasterizer *rasterizer)
 {
-  if (vt->bg_active)
+  CtxGState *gstate = &rasterizer->state->gstate;
+  int components = 4;
+  rasterizer->fragment = ctx_rasterizer_get_fragment_RGBA8 (rasterizer);
+  rasterizer->comp_op = CTX_COMPOSITE_SUFFIX(ctx_RGBA8_porter_duff_generic);
+
+#if 1
+  if (gstate->compositing_mode == CTX_COMPOSITE_CLEAR)
   {
-    ctx_rgba8 (ctx, vt->bg_rgba[0], vt->bg_rgba[1], vt->bg_rgba[2], vt->bg_rgba[3]);
-    ctx_rectangle (ctx, vt->bg_x0, vt->bg_y0, vt->bg_width, vt->bg_height);
-    ctx_fill (ctx);
-    vt->bg_active = 0;
+    rasterizer->comp_op = ctx_RGBA8_clear_normal;
+    return;
+  }
+#endif
+
+
+#if CTX_INLINED_GRADIENTS
+#if CTX_GRADIENTS
+  if (gstate->source_fill.type == CTX_SOURCE_LINEAR_GRADIENT &&
+      gstate->blend_mode == CTX_BLEND_NORMAL &&
+      gstate->compositing_mode == CTX_COMPOSITE_SOURCE_OVER)
+  {
+     rasterizer->comp_op = CTX_COMPOSITE_SUFFIX(ctx_RGBA8_source_over_normal_linear_gradient);
+     return;
+  }
+  if (gstate->source_fill.type == CTX_SOURCE_RADIAL_GRADIENT &&
+      gstate->blend_mode == CTX_BLEND_NORMAL &&
+      gstate->compositing_mode == CTX_COMPOSITE_SOURCE_OVER)
+  {
+     rasterizer->comp_op = CTX_COMPOSITE_SUFFIX (ctx_RGBA8_source_over_normal_radial_gradient);
+     return;
+  }
+#endif
+#endif
+
+  if (gstate->source_fill.type == CTX_SOURCE_COLOR)
+    {
+      ctx_color_get_rgba8 (rasterizer->state, &gstate->source_fill.color, rasterizer->color);
+      if (gstate->global_alpha_u8 != 255)
+        rasterizer->color[components-1] = (rasterizer->color[components-1] * gstate->global_alpha_u8)/255;
+      if (rasterizer->swap_red_green)
+      {
+        uint8_t *rgba = (uint8_t*)&rasterizer->color[0];
+        uint8_t tmp = rgba[0];
+        rgba[0] = rgba[2];
+        rgba[2] = tmp;
+      }
+
+      switch (gstate->blend_mode)
+      {
+        case CTX_BLEND_NORMAL:
+          if (gstate->compositing_mode == CTX_COMPOSITE_COPY)
+          {
+            rasterizer->comp_op = CTX_COMPOSITE_SUFFIX(ctx_RGBA8_copy_normal);
+            return;
+          }
+          else if (gstate->global_alpha_u8 == 0)
+          {
+            rasterizer->comp_op = CTX_COMPOSITE_SUFFIX(ctx_RGBA8_nop);
+          }
+          else if (gstate->compositing_mode == CTX_COMPOSITE_SOURCE_OVER)
+          {
+             if (rasterizer->color[components-1] == 0)
+                 rasterizer->comp_op = CTX_COMPOSITE_SUFFIX(ctx_RGBA8_nop);
+             else if (rasterizer->color[components-1] == 255)
+                 rasterizer->comp_op = CTX_COMPOSITE_SUFFIX(ctx_RGBA8_source_over_normal_color_solid);
+             else
+                 rasterizer->comp_op = CTX_COMPOSITE_SUFFIX(ctx_RGBA8_source_over_normal_color);
+         }
+         break;
+      default:
+         rasterizer->comp_op = CTX_COMPOSITE_SUFFIX(ctx_RGBA8_porter_duff_color);
+         break;
+    }
+    //rasterizer->comp_op = ctx_RGBA8_porter_duff_color; // XXX overide to make all go
+                                                       // through generic code path
+    rasterizer->fragment = NULL;
+    return;
+  }
+
+
+#if 1
+  if (gstate->blend_mode == CTX_BLEND_NORMAL &&
+      gstate->compositing_mode == CTX_COMPOSITE_SOURCE_OVER &&
+      rasterizer->fragment)
+  {
+          // only really valid for image sources
+     rasterizer->comp_op = CTX_COMPOSITE_SUFFIX(ctx_RGBA8_source_over_normal_fragment);
+     return;
   }
+#endif
 }
 
-static void vt_draw_bg (VT *vt, Ctx *ctx,
-                        float x0, float y0,
-                        float width, float height,
-                        uint8_t *rgba)
+/*
+ * we could use this instead of NULL in the pixfmt table - but such dispatch
+ * is slightly slower
+ */
+inline static void
+ctx_composite_direct (CTX_COMPOSITE_ARGUMENTS)
 {
-   int same_color = !memcmp(rgba, vt->bg_rgba, 4);
-   if (vt->bg_active && !same_color)
-   {
-     vt_flush_bg (vt, ctx);
-   }
+  rasterizer->comp_op (rasterizer, dst, rasterizer->color, x0, coverage, count);
+}
 
-   if (vt->bg_active && same_color)
-   {
-     vt->bg_width += width;
-   }
-   else
-   {
-     memcpy (vt->bg_rgba, rgba, 4);
-     vt->bg_active = 1;
-     vt->bg_x0 = x0;
-     vt->bg_y0 = y0;
-     vt->bg_width = width;
-     vt->bg_height = height;
-   }
+static void
+ctx_composite_convert (CTX_COMPOSITE_ARGUMENTS)
+{
+  uint8_t pixels[count * rasterizer->format->ebpp];
+  rasterizer->format->to_comp (rasterizer, x0, dst, &pixels[0], count);
+  rasterizer->comp_op (rasterizer, &pixels[0], rasterizer->color, x0, coverage, count);
+  rasterizer->format->from_comp (rasterizer, x0, &pixels[0], dst, count);
 }
 
-float vt_draw_cell (VT      *vt, Ctx *ctx,
-                    int      row, int col, // pass 0 to force draw - like
-                    float    x0, float y0, // for scrollback visible
-                    uint64_t style,
-                    uint32_t unichar,
-                    int      dw, int dh,
-                    int      in_smooth_scroll,
-                    int      in_select,
-                    int      is_fg)
-// dw is 0 or 1
-// dh is 0 1 or -1  1 is upper -1 is lower
-{
-  int on_white = vt->reverse_video;
-  int color = 0;
-  int bold = (style & STYLE_BOLD) != 0;
-  int dim = (style & STYLE_DIM) != 0;
-  int is_hidden = (style & STYLE_HIDDEN) != 0;
-  int proportional = (style & STYLE_PROPORTIONAL) != 0;
-  int fg_set = (style & STYLE_FG_COLOR_SET) != 0;
-  int bg_intensity = 0;
-  int fg_intensity = 2;
-  int reverse = ( (style & STYLE_REVERSE) != 0) ^ in_select;
-  int blink = ( (style & STYLE_BLINK) != 0);
-  int blink_fast = ( (style & STYLE_BLINK_FAST) != 0);
-  int cw = vt->cw;
-  int ch = vt->ch;
-  if (proportional)
-    {
-      if (vt->font_is_mono)
-        {
-          ctx_font (ctx, "regular");
-          vt->font_is_mono = 0;
-        }
-      cw = ctx_glyph_width (ctx, unichar);
-    }
-  else
-    {
-      if (vt->font_is_mono == 0)
-        {
-          ctx_font (ctx, "mono");
-          vt->font_is_mono = 1;
-          if (col > 1)
-            {
-              int x = x0;
-              int new_cw = cw - ( (x % cw) );
-              if (new_cw < cw*3/2)
-                { new_cw += cw; }
-              cw = new_cw;
-            }
-        }
-    }
-  float scale_x  = 1.0f;
-  float scale_y  = 1.0f;
-  float offset_y = 0.0f;
-  if (dw)
-    {
-      scale_x = 2.0f;
-    }
-  if (dh)
-    {
-      scale_y = 2.0f;
-    }
-  if (dh == 1)
-    {
-      offset_y = 0.5f;
-    }
-  else if (dh == -1)
-    {
-      offset_y =  0.0f;
-    }
-  if (in_smooth_scroll)
+#if CTX_ENABLE_FLOAT
+static void
+ctx_float_copy_normal (int components, CTX_COMPOSITE_ARGUMENTS)
+{
+  float *dstf = (float*)dst;
+  float *srcf = (float*)src;
+  float u0 = 0; float v0 = 0;
+  float ud = 0; float vd = 0;
+
+  if (rasterizer->fragment)
     {
-      offset_y -= vt->scroll_offset / (dh?2:1);
+      ctx_init_uv (rasterizer, x0, count, &u0, &v0, &ud, &vd);
     }
-  cw *= scale_x;
-  if (blink_fast)
+
+  while (count--)
+  {
+    int cov = *coverage;
+    if (cov == 0)
     {
-      if ( (vt->blink_state % 2) == 0)
-        { blink = 1; }
-      else
-        { blink = 0; }
+      for (int c = 0; c < components; c++)
+        { dst[c] = 0; }
     }
-  else if (blink)
+    else
     {
-      if ( (vt->blink_state % 10) < 5)
-        { blink = 1; }
-      else
-        { blink = 0; }
-    }
-  /*
-     from the vt100 technical-manual:
-
-     "Reverse characters [..] normally have dim backgrounds with
-     black characters so that large white spaces have the same impact
-     on the viewer's eye as the smaller brighter white areas of
-     normal characters. Bold and reverse asserted together give a
-     background of normal intensity. Blink applied to nonreverse
-     characters causes them to alternate between their usual
-     intensity and the next lower intensity. (Normal characters vary
-     between normal and dim intensity. Bold characters vary between
-     bright and normal intensity.) Blink applied to a reverse
-     character causes that character to alternate between normal and
-     reverse video representations of that character."
-
-     This is in contrast with how the truth table appears to be
-     meant used, since it uses a reverse computed as the xor of
-     the global screen reverse and the reverse attribute of the
-     cell.
-
-     To fulfil the more asthethic resulting from implementing the
-     text, and would be useful to show how the on_bright background
-     mode of the vt100 actually displays the vttest.
-
-     */
-  if (on_white)
+      if (rasterizer->fragment)
+      {
+        rasterizer->fragment (rasterizer, u0, v0, src, 1, ud, vd);
+        u0+=ud;
+        v0+=vd;
+      }
+    if (cov == 255)
     {
-          if (bold)
-            {
-              bg_intensity =           2;
-              fg_intensity = blink?1:  0;
-            }
-          else if (dim)
-            {
-              bg_intensity =           2;
-              fg_intensity = blink?3:  1;
-            }
-          else
-            {
-              bg_intensity =           2;
-              fg_intensity = blink?1:  0;
-            }
-          if (fg_set)
-            {
-              fg_intensity = blink?2:3;
-            }
+      for (int c = 0; c < components; c++)
+        dstf[c] = srcf[c];
     }
-  else /* bright on dark */
+    else
     {
-          if (bold)
-            {
-              bg_intensity =           0;
-              fg_intensity = blink?2:  3;
-            }
-          else if (dim)
-            {
-              bg_intensity =           0;
-              fg_intensity = blink?0:  1;
-            }
-          else
-            {
-              bg_intensity =           0;
-              fg_intensity = blink?1:  2;
-            }
+      float covf = ctx_u8_to_float (cov);
+      for (int c = 0; c < components; c++)
+        dstf[c] = srcf[c]*covf;
     }
-  uint8_t bg_rgb[4]= {0,0,0,255};
-  uint8_t fg_rgb[4]= {255,255,255,255};
-  {
-      //ctx_begin_path (ctx);
-      if (style &  STYLE_BG24_COLOR_SET)
-        {
-          uint64_t temp = style >> 40;
-          bg_rgb[0] = temp & 0xff;
-          temp >>= 8;
-          bg_rgb[1] = temp & 0xff;
-          temp >>= 8;
-          bg_rgb[2] = temp & 0xff;
-#if 0
-          if (dh)
-          {
-             bg_rgb[0] = 
-             bg_rgb[1] =
-             bg_rgb[2] = 30;
-          }
-#endif
-        }
-      else
-        {
-          if (style & STYLE_BG_COLOR_SET)
-            {
-              color = (style >> 40) & 255;
-              bg_intensity = -1;
-              vt_ctx_get_color (vt, color, bg_intensity, bg_rgb);
-            }
-          else
-            {
-              switch (bg_intensity)
-                {
-                  case 0:
-                    for (int i = 0; i <3 ; i++)
-                      { bg_rgb[i] = vt->bg_color[i]; }
-                    break;
-                  case 1:
-                    for (int i = 0; i <3 ; i++)
-                      { bg_rgb[i] = vt->bg_color[i] * 0.5 + vt->fg_color[i] * 0.5; }
-                    break;
-                  case 2:
-                    for (int i = 0; i <3 ; i++)
-                      { bg_rgb[i] = vt->bg_color[i] * 0.05 + vt->fg_color[i] * 0.95; }
-                    break;
-                  case 3:
-                    for (int i = 0; i <3 ; i++)
-                      { bg_rgb[i] = vt->fg_color[i]; }
-                    break;
-                }
-            }
-        }
-  }
-  if (style & STYLE_FG24_COLOR_SET)
-    {
-      uint64_t temp = style >> 16;
-      fg_rgb[0] = temp & 0xff;
-      temp >>= 8;
-      fg_rgb[1] = temp & 0xff;
-      temp >>= 8;
-      fg_rgb[2] = temp & 0xff;
     }
-  else
-    {
-      if ( (style & STYLE_FG_COLOR_SET) == 0)
-        {
-          switch (fg_intensity)
-            {
-              case 0:
-                for (int i = 0; i <3 ; i++)
-                  { fg_rgb[i] = vt->bg_color[i] * 0.7 + vt->fg_color[i] * 0.3; }
-                break;
-              case 1:
-                for (int i = 0; i <3 ; i++)
-                  { fg_rgb[i] = vt->bg_color[i] * 0.5 + vt->fg_color[i] * 0.5; }
-                break;
-              case 2:
-                for (int i = 0; i <3 ; i++)
-                  { fg_rgb[i] = vt->bg_color[i] * 0.20 + vt->fg_color[i] * 0.80; }
-                break;
-              case 3:
-                for (int i = 0; i <3 ; i++)
-                  { fg_rgb[i] = vt->fg_color[i]; }
-            }
-        }
-      else
-        {
-          color = (style >> 16) & 255;
-          vt_ctx_get_color (vt, color, fg_intensity, fg_rgb);
-        }
+    dstf += components;
+    coverage ++;
   }
+}
 
-  if (reverse)
+static void
+ctx_float_clear_normal (int components, CTX_COMPOSITE_ARGUMENTS)
+{
+  float *dstf = (float*)dst;
+  while (count--)
   {
-    for (int c = 0; c < 3; c ++)
+#if 0
+    int cov = *coverage;
+    if (cov == 0)
     {
-      int t = bg_rgb[c];
-      bg_rgb[c] = fg_rgb[c];
-      fg_rgb[c] = t;
     }
-  }
-
-  if (is_fg ||
-      ((!on_white) && bg_rgb[0]==0 && bg_rgb[1]==0 && bg_rgb[2]==0) ||
-      ((on_white) && bg_rgb[0]==255 && bg_rgb[1]==255 && bg_rgb[2]==255))
-          /* these comparisons are not entirely correct, when on dark background we assume black to
-           * be default and non-set, even when theme might differ
-           */
-  {
-    /* skipping draw of background */
-  }
-  else
-  {
-    if (dh)
+    else if (cov == 255)
     {
-      vt_draw_bg (vt, ctx, ctx_floorf(x0),
-         ctx_floorf(y0 - ch - ch * (vt->scroll_offset)), cw, ch, bg_rgb);
+#endif
+      switch (components)
+      {
+        case 2:
+          ((uint64_t*)(dst))[0] = 0;
+          break;
+        case 4:
+          ((uint64_t*)(dst))[0] = 0;
+          ((uint64_t*)(dst))[1] = 0;
+          break;
+        default:
+          for (int c = 0; c < components; c++)
+            dstf[c] = 0.0f;
+      }
+#if 0
     }
     else
     {
-      vt_draw_bg (vt, ctx, x0, y0 - ch + ch * offset_y, cw, ch, bg_rgb);
+      float ralpha = 1.0 - ctx_u8_to_float (cov);
+      for (int c = 0; c < components; c++)
+        { dstf[c] = (dstf[c] * ralpha); }
     }
+    coverage ++;
+#endif
+    dstf += components;
   }
+}
 
-  if (!is_fg)
-    return cw;
+static void
+ctx_float_source_over_normal_opaque_color (int components, CTX_COMPOSITE_ARGUMENTS)
+{
+  float *dstf = (float*)dst;
+  float *srcf = (float*)src;
 
-  int italic        = (style & STYLE_ITALIC) != 0;
-  int strikethrough = (style & STYLE_STRIKETHROUGH) != 0;
-  int overline      = (style & STYLE_OVERLINE) != 0;
-  int underline     = (style & STYLE_UNDERLINE) != 0;
-  int underline_var = (style & STYLE_UNDERLINE_VAR) != 0;
-  if (dh == 1)
-    {
-      underline = underline_var = 0;
-    }
-  int double_underline = 0;
-  int curved_underline = 0;
-  if (underline_var)
+  while (count--)
+  {
+    int cov = *coverage;
+    if (cov)
     {
-      if (underline)
-        {
-          double_underline = 1;
-        }
+      if (cov == 255)
+      {
+        for (int c = 0; c < components; c++)
+          dstf[c] = srcf[c];
+      }
       else
-        {
-          curved_underline = 1;
-        }
+      {
+        float fcov = ctx_u8_to_float (cov);
+        float ralpha = 1.0f - fcov;
+        for (int c = 0; c < components-1; c++)
+          dstf[c] = (srcf[c]*fcov + dstf[c] * ralpha);
+      }
     }
+    coverage ++;
+    dstf+= components;
+  }
+}
 
-  int has_underline = (underline || double_underline || curved_underline);
-
-  if (unichar == ' ' && !has_underline)
-    is_hidden = 1;
-
-  if (!is_hidden)
-    {
-
-      ctx_rgba8 (ctx, fg_rgb[0], fg_rgb[1], fg_rgb[2], 255);
-
-
-      if (italic)
-        {
-          ctx_save (ctx);
-          ctx_translate (ctx, (x0 + cw/3), (y0 + vt->ch/2) );
-          ctx_scale (ctx, 0.9, 0.9);
-          ctx_rotate (ctx, 0.15);
-          ctx_translate (ctx, - (x0 + cw/3), - (y0 + vt->ch/2) );
-        }
-      vt_ctx_glyph (ctx, vt, x0, y0, unichar, bold, scale_x, scale_y, offset_y);
-      if (italic)
-        {
-          ctx_restore (ctx);
-        }
-      if (curved_underline)
-        {
-          ctx_begin_path (ctx);
-          ctx_move_to (ctx, x0, y0 - vt->font_size * 0.07 - vt->ch * vt->scroll_offset);
-          ctx_rel_line_to (ctx, (cw+2) /3, -vt->ch * 0.05);
-          ctx_rel_line_to (ctx, (cw+2) /3, vt->ch * 0.1);
-          ctx_rel_line_to (ctx, (cw+2) /3, -vt->ch * 0.05);
-          //ctx_rel_line_to (ctx, cw, 0);
-          ctx_line_width (ctx, vt->font_size * (style &  STYLE_BOLD?0.050:0.04) );
-          ctx_stroke (ctx);
-        }
-      else if (double_underline)
-        {
-          ctx_begin_path (ctx);
-          ctx_move_to (ctx, x0, y0 - vt->font_size * 0.130 - vt->ch * vt->scroll_offset);
-          ctx_rel_line_to (ctx, cw, 0);
-          ctx_move_to (ctx, x0, y0 - vt->font_size * 0.030 - vt->ch * vt->scroll_offset);
-          ctx_rel_line_to (ctx, cw, 0);
-          ctx_line_width (ctx, vt->font_size * (style &  STYLE_BOLD?0.050:0.04) );
-          ctx_stroke (ctx);
-        }
-      else if (underline)
-        {
-          ctx_begin_path (ctx);
-          ctx_move_to (ctx, x0, y0 - vt->font_size * 0.07 - vt->ch * vt->scroll_offset);
-          ctx_rel_line_to (ctx, cw, 0);
-          ctx_line_width (ctx, vt->font_size * (style &  STYLE_BOLD?0.075:0.05) );
-          ctx_stroke (ctx);
-        }
-      if (overline)
-        {
-          ctx_begin_path (ctx);
-          ctx_move_to (ctx, x0, y0 - vt->font_size * 0.94 - vt->ch * vt->scroll_offset);
-          ctx_rel_line_to (ctx, cw, 0);
-          ctx_line_width (ctx, vt->font_size * (style &  STYLE_BOLD?0.075:0.05) );
-          ctx_stroke (ctx);
-        }
-      if (strikethrough)
-        {
-          ctx_begin_path (ctx);
-          ctx_move_to (ctx, x0, y0 - vt->font_size * 0.43 - vt->ch * vt->scroll_offset);
-          ctx_rel_line_to (ctx, cw, 0);
-          ctx_line_width (ctx, vt->font_size * (style &  STYLE_BOLD?0.075:0.05) );
-          ctx_stroke (ctx);
-        }
-    }
-  return cw;
+inline static void
+ctx_float_blend_normal (int components, float *dst, float *src, float *blended)
+{
+  float a = src[components-1];
+  for (int c = 0; c <  components - 1; c++)
+    blended[c] = src[c] * a;
+  blended[components-1]=a;
 }
 
-int vt_has_blink (VT *vt)
+static float ctx_float_get_max (int components, float *c)
 {
-  if (!vt) return 0;
-  return (vt->in_smooth_scroll ?  10 : 0);
-  //return vt->has_blink + (vt->in_smooth_scroll ?  10 : 0);
+  float max = -1000.0f;
+  for (int i = 0; i < components - 1; i ++)
+  {
+    if (c[i] > max) max = c[i];
+  }
+  return max;
 }
 
-//extern int enable_terminal_menu;
-//
+static float ctx_float_get_min (int components, float *c)
+{
+  float min = 400.0;
+  for (int i = 0; i < components - 1; i ++)
+  {
+    if (c[i] < min) min = c[i];
+  }
+  return min;
+}
 
-//void ctx_set_popup (Ctx *ctx, void (*popup)(Ctx *ctx, void *data), void *popup_data);
-
-static char *primary = NULL;
-static void scrollbar_drag (CtxEvent *event, void *data, void *data2);
-static int scrollbar_down = 0;
-
-void vt_mouse_event (CtxEvent *event, void *data, void *data2)
-{
-  VT   *vt = data;
-  CtxClient *client = vt_get_client (vt);
-  float  x = event->x;
-  float  y = event->y;
-  int device_no = event->device_no;
-  char buf[128]="";
-  if ((!vt->in_alt_screen) &&
-      (event->x > vt->width - vt->cw * 1.5 || scrollbar_down) &&
-      (event->type == CTX_DRAG_MOTION ||
-      event->type == CTX_DRAG_PRESS ||
-      event->type == CTX_DRAG_RELEASE))
-    return scrollbar_drag (event, data, data2);
-  switch (event->type)
-  {
-    case CTX_MOTION:
-    case CTX_DRAG_MOTION:
-      //if (event->device_no==1)
-      {
-        sprintf (buf, "mouse-motion %.0f %.0f %i", x, y, device_no);
-//      ctx_set_dirty (event->ctx, 1);
-        ctx_client_lock (client);
-        vt_feed_keystring (vt, event, buf);
-        ctx_client_unlock (client);
-//      vt->rev++;
-      }
-      break;
-    case CTX_DRAG_PRESS:
-      if (event->device_no==2)
-      {
-        if (primary)
-        {
-          if (vt)
-            vt_paste (vt, primary);
-        }
-      }
-      else if (event->device_no==3 && !vt->in_alt_screen)
-      {
-        vt->popped = 1;
-      }
-      else
-      {
-        sprintf (buf, "mouse-press %.0f %.0f %i", x, y, device_no);
-        ctx_client_lock (client);
-        vt_feed_keystring (vt, event, buf);
-        ctx_client_unlock (client);
-//      ctx_set_dirty (event->ctx, 1);
-//      vt->rev++;
-      }
-      break;
-    case CTX_DRAG_RELEASE:
-      if (event->device_no==3 && !vt->in_alt_screen)
-      {
-        vt->popped = 0;
-      }
-        ctx_set_dirty (event->ctx, 1);
-        sprintf (buf, "mouse-release %.0f %.0f %i", x, y, device_no);
-        ctx_client_lock (client);
-        vt_feed_keystring (vt, event, buf);
-        ctx_client_unlock (client);
-      break;
+static float ctx_float_get_lum (int components, float *c)
+{
+  switch (components)
+  {
+    case 3:
+    case 4:
+            return CTX_CSS_RGB_TO_LUMINANCE(c);
+    case 1:
+    case 2:
+            return c[0];
+            break;
     default:
-      // we should not stop propagation
-      return;
-      break;
+       {
+         float sum = 0;
+         for (int i = 0; i < components - 1; i ++)
+         {
+           sum += c[i];
+         }
+         return sum / (components - 1);
+       }
   }
-  event->stop_propagate = 1;
-//vt->rev++;
 }
-static int scrollbar_focused = 0;
-#if 0
-static void scrollbar_enter (CtxEvent *event, void *data, void *data2)
+
+static float ctx_float_get_sat (int components, float *c)
 {
-  VT *vt = data;
-  vt->rev++;
-  scrollbar_focused = 1;
+  switch (components)
+  {
+    case 3:
+    case 4:
+            { float r = c[0];
+              float g = c[1];
+              float b = c[2];
+              return ctx_maxf(r, ctx_maxf(g,b)) - ctx_minf(r,ctx_minf(g,b));
+            }
+            break;
+    case 1:
+    case 2: return 0.0;
+            break;
+    default:
+       {
+         float min = 1000;
+         float max = -1000;
+         for (int i = 0; i < components - 1; i ++)
+         {
+           if (c[i] < min) min = c[i];
+           if (c[i] > max) max = c[i];
+         }
+         return max-min;
+       }
+  }
 }
 
-static void scrollbar_leave (CtxEvent *event, void *data, void *data2)
+static void ctx_float_set_lum (int components, float *c, float lum)
 {
-  VT *vt = data;
-  vt->rev++;
-  scrollbar_focused = 0;
-}
-#endif
+  float d = lum - ctx_float_get_lum (components, c);
+  float tc[components];
+  for (int i = 0; i < components - 1; i++)
+  {
+    tc[i] = c[i] + d;
+  }
 
-static void scrollbar_drag (CtxEvent *event, void *data, void *data2)
-{
-  VT *vt = data;
-  float disp_lines = vt->rows;
-  float tot_lines = vt->line_count + vt->scrollback_count;
+  float l = ctx_float_get_lum (components, tc);
+  float n = ctx_float_get_min (components, tc);
+  float x = ctx_float_get_max (components, tc);
 
-  vt->scroll = tot_lines - disp_lines - (event->y*1.0/ ctx_client_height (vt->id)) * tot_lines + 
disp_lines/2;
-  if (vt->scroll < 0) { vt->scroll = 0.0; }
-  if (vt->scroll > vt->scrollback_count) { vt->scroll = vt->scrollback_count; }
-  vt->rev++;
-  ctx_set_dirty (event->ctx, 1);
-  event->stop_propagate = 1;
+  if (n < 0.0f && l != n)
+  {
+    for (int i = 0; i < components - 1; i++)
+      tc[i] = l + (((tc[i] - l) * l) / (l-n));
+  }
 
-  switch (event->type)
+  if (x > 1.0f && x != l)
   {
-    case CTX_DRAG_PRESS:
-      scrollbar_down = 1;
-      break;
-    case CTX_DRAG_RELEASE:
-      scrollbar_down = 0;
-      break;
-    default:
-      break;
+    for (int i = 0; i < components - 1; i++)
+      tc[i] = l + (((tc[i] - l) * (1.0f - l)) / (x-l));
   }
+  for (int i = 0; i < components - 1; i++)
+    c[i] = tc[i];
 }
 
-#if 0
-static void scroll_handle_drag (CtxEvent *event, void *data, void *data2)
+static void ctx_float_set_sat (int components, float *c, float sat)
 {
-  VT *vt = data;
-  float tot_lines = vt->line_count + vt->scrollback_count;
-  if (event->type == CTX_DRAG_MOTION)
+  int max = 0, mid = 1, min = 2;
+  
+  if (c[min] > c[mid]){int t = min; min = mid; mid = t;}
+  if (c[mid] > c[max]){int t = mid; mid = max; max = t;}
+  if (c[min] > c[mid]){int t = min; min = mid; mid = t;}
+
+  if (c[max] > c[min])
   {
-    vt->scroll -= (event->delta_y * tot_lines) / (vt->rows * vt->ch);
+    c[mid] = ((c[mid]-c[min]) * sat) / (c[max] - c[min]);
+    c[max] = sat;
   }
-  if (vt->scroll < 0) { vt->scroll = 0.0; }
-  if (vt->scroll > vt->scrollback_count) { vt->scroll = vt->scrollback_count; }
-  vt->rev++;
-  event->stop_propagate = 1;
-}
-#endif
+  else
+  {
+    c[mid] = c[max] = 0.0f;
+  }
+  c[min] = 0.0f;
 
-#if 0
-static void test_popup (Ctx *ctx, void *data)
-{
-  VT *vt = data;
+}
 
-  float x = ctx_client_x (vt->id);
-  float y = ctx_client_y (vt->id);
-  ctx_rectangle (ctx, x, y, 100, 100);
-  ctx_rgb (ctx, 1,0,0);
-  ctx_fill (ctx);
+#define ctx_float_blend_define(name, CODE) \
+static void \
+ctx_float_blend_##name (int components, float * __restrict__ dst, float *src, float *blended)\
+{\
+  float *s = src; float b[components];\
+  ctx_float_deassociate_alpha (components, dst, b);\
+    CODE;\
+  blended[components-1] = s[components-1];\
+  ctx_float_associate_alpha (components, blended);\
 }
-#endif
 
-void itk_style_color (Ctx *ctx, const char *name); // only itk fun used in vt
+#define ctx_float_blend_define_seperable(name, CODE) \
+        ctx_float_blend_define(name, for (int c = 0; c < components-1; c++) { CODE ;}) \
 
-void vt_use_images (VT *vt, Ctx *ctx)
-{
-  /*  this is a call intended for minimized/shaded fully obscured
-   *  clients to make sure their textures are kept alive
-   *  in the server
-   */
-  //float x0=0;
-  float y0=0;
-  //vt->has_blink = 0;
-  //vt->blink_state++;
+ctx_float_blend_define_seperable(multiply,    blended[c] = (b[c] * s[c]);)
+ctx_float_blend_define_seperable(screen,      blended[c] = b[c] + s[c] - (b[c] * s[c]);)
+ctx_float_blend_define_seperable(overlay,     blended[c] = b[c] < 0.5f ? (s[c] * b[c]) :
+                                                          s[c] + b[c] - (s[c] * b[c]);)
+ctx_float_blend_define_seperable(darken,      blended[c] = ctx_minf (b[c], s[c]))
+ctx_float_blend_define_seperable(lighten,     blended[c] = ctx_maxf (b[c], s[c]))
+ctx_float_blend_define_seperable(color_dodge, blended[c] = (b[c] == 0.0f) ? 0.0f :
+                                     s[c] == 1.0f ? 1.0f : ctx_minf(1.0f, (b[c]) / (1.0f-s[c])))
+ctx_float_blend_define_seperable(color_burn,  blended[c] = (b[c] == 1.0f) ? 1.0f :
+                                     s[c] == 0.0f ? 0.0f : 1.0f - ctx_minf(1.0f, ((1.0f - b[c])) / s[c]))
+ctx_float_blend_define_seperable(hard_light,  blended[c] = s[c] < 0.f ? (b[c] * s[c]) :
+                                                          b[c] + s[c] - (b[c] * s[c]);)
+ctx_float_blend_define_seperable(difference,  blended[c] = (b[c] - s[c]))
 
-  ctx_save (ctx);
+ctx_float_blend_define_seperable(divide,      blended[c] = s[c]?(b[c]) / s[c]:0.0f)
+ctx_float_blend_define_seperable(addition,    blended[c] = s[c]+b[c])
+ctx_float_blend_define_seperable(subtract,    blended[c] = s[c]-b[c])
 
+ctx_float_blend_define_seperable(exclusion,   blended[c] = b[c] + s[c] - 2.0f * b[c] * s[c])
+ctx_float_blend_define_seperable(soft_light,
+  if (s[c] <= 0.5f)
   {
-    /* draw graphics */
-    for (int row = ((vt->scroll!=0.0f)?vt->scroll:0);
-         row < (vt->scroll) + vt->rows * 4;
-         row ++)
-    {
-       CtxList *l = ctx_list_nth (vt->lines, row);
-       float y = y0 + vt->ch * (vt->rows - row);
+    blended[c] = b[c] - (1.0f - 2.0f * s[c]) * b[c] * (1.0f - b[c]);
+  }
+  else
+  {
+    int d;
+    if (b[c] <= 255/4)
+      d = (((16 * b[c] - 12.0f) * b[c] + 4.0f) * b[c]);
+    else
+      d = ctx_sqrtf(b[c]);
+    blended[c] = (b[c] + (2.0f * s[c] - 1.0f) * (d - b[c]));
+  }
+)
 
-       if (row >= vt->rows && !vt->in_alt_screen)
-         {
-           l = ctx_list_nth (vt->scrollback, row-vt->rows);
-         }
 
-       if (l && y <= (vt->rows - vt->scroll) *  vt->ch)
-         {
-           VtLine *line = l->data;
-           if (line->ctx_copy)
-             {
-               ctx_render_ctx_textures (line->ctx_copy, ctx);
-             }
-         }
-    }
-  }
-  ctx_restore (ctx);
-}
+ctx_float_blend_define(color,
+  for (int i = 0; i < components; i++)
+    blended[i] = s[i];
+  ctx_float_set_lum(components, blended, ctx_float_get_lum (components, s));
+)
+
+ctx_float_blend_define(hue,
+  float in_sat = ctx_float_get_sat(components, b);
+  float in_lum = ctx_float_get_lum(components, b);
+  for (int i = 0; i < components; i++)
+    blended[i] = s[i];
+  ctx_float_set_sat(components, blended, in_sat);
+  ctx_float_set_lum(components, blended, in_lum);
+)
+
+ctx_float_blend_define(saturation,
+  float in_sat = ctx_float_get_sat(components, s);
+  float in_lum = ctx_float_get_lum(components, b);
+  for (int i = 0; i < components; i++)
+    blended[i] = b[i];
+  ctx_float_set_sat(components, blended, in_sat);
+  ctx_float_set_lum(components, blended, in_lum);
+)
 
+ctx_float_blend_define(luminosity,
+  float in_lum = ctx_float_get_lum(components, s);
+  for (int i = 0; i < components; i++)
+    blended[i] = b[i];
+  ctx_float_set_lum(components, blended, in_lum);
+)
 
-void vt_register_events (VT *vt, Ctx *ctx, double x0, double y0)
+inline static void
+ctx_float_blend (int components, CtxBlend blend, float * __restrict__ dst, float *src, float *blended)
 {
-  ctx_begin_path (ctx);
-  ctx_save (ctx);
-  ctx_translate (ctx, x0, y0);
-  ctx_rectangle (ctx, 0, 0, vt->cols * vt->cw, vt->rows * vt->ch);
-  ctx_listen (ctx, CTX_DRAG,   vt_mouse_event, vt, NULL);
-  ctx_listen (ctx, CTX_MOTION, vt_mouse_event, vt, NULL);
-  ctx_begin_path (ctx);
-  ctx_restore (ctx);
+  switch (blend)
+  {
+    case CTX_BLEND_NORMAL:      ctx_float_blend_normal      (components, dst, src, blended); break;
+    case CTX_BLEND_MULTIPLY:    ctx_float_blend_multiply    (components, dst, src, blended); break;
+    case CTX_BLEND_SCREEN:      ctx_float_blend_screen      (components, dst, src, blended); break;
+    case CTX_BLEND_OVERLAY:     ctx_float_blend_overlay     (components, dst, src, blended); break;
+    case CTX_BLEND_DARKEN:      ctx_float_blend_darken      (components, dst, src, blended); break;
+    case CTX_BLEND_LIGHTEN:     ctx_float_blend_lighten     (components, dst, src, blended); break;
+    case CTX_BLEND_COLOR_DODGE: ctx_float_blend_color_dodge (components, dst, src, blended); break;
+    case CTX_BLEND_COLOR_BURN:  ctx_float_blend_color_burn  (components, dst, src, blended); break;
+    case CTX_BLEND_HARD_LIGHT:  ctx_float_blend_hard_light  (components, dst, src, blended); break;
+    case CTX_BLEND_SOFT_LIGHT:  ctx_float_blend_soft_light  (components, dst, src, blended); break;
+    case CTX_BLEND_DIFFERENCE:  ctx_float_blend_difference  (components, dst, src, blended); break;
+    case CTX_BLEND_EXCLUSION:   ctx_float_blend_exclusion   (components, dst, src, blended); break;
+    case CTX_BLEND_COLOR:       ctx_float_blend_color       (components, dst, src, blended); break;
+    case CTX_BLEND_HUE:         ctx_float_blend_hue         (components, dst, src, blended); break;
+    case CTX_BLEND_SATURATION:  ctx_float_blend_saturation  (components, dst, src, blended); break;
+    case CTX_BLEND_LUMINOSITY:  ctx_float_blend_luminosity  (components, dst, src, blended); break;
+    case CTX_BLEND_ADDITION:    ctx_float_blend_addition    (components, dst, src, blended); break;
+    case CTX_BLEND_SUBTRACT:    ctx_float_blend_subtract    (components, dst, src, blended); break;
+    case CTX_BLEND_DIVIDE:      ctx_float_blend_divide      (components, dst, src, blended); break;
+  }
 }
 
-void vt_draw (VT *vt, Ctx *ctx, double x0, double y0)
+/* this is the grunt working function, when inlined code-path elimination makes
+ * it produce efficient code.
+ */
+CTX_INLINE static void
+ctx_float_porter_duff (CtxRasterizer         *rasterizer,
+                       int                    components,
+                       uint8_t * __restrict__ dst,
+                       uint8_t * __restrict__ src,
+                       int                    x0,
+                       uint8_t * __restrict__ coverage,
+                       int                    count,
+                       CtxCompositingMode     compositing_mode,
+                       CtxFragment            fragment,
+                       CtxBlend               blend)
 {
-  ctx_begin_path (ctx);
-  ctx_save (ctx);
-  ctx_translate (ctx, x0, y0);
-  x0 = 0;
-  y0 = 0;
-  ctx_font (ctx, "mono");
-  vt->font_is_mono = 0;
-  ctx_font_size (ctx, vt->font_size * vt->font_to_cell_scale);
-  vt->has_blink = 0;
-  vt->blink_state++;
-#if 0
-  int cursor_x_px = 0;
-  int cursor_y_px = 0;
-  int cursor_w = vt->cw;
-  int cursor_h = vt->ch;
-  cursor_x_px = x0 + (vt->cursor_x - 1) * vt->cw;
-  cursor_y_px = y0 + (vt->cursor_y - 1) * vt->ch;
-  cursor_w = vt->cw;
-  cursor_h = vt->ch;
-#endif
-  ctx_save (ctx);
-  //if (vt->scroll || full)
-    {
-      ctx_begin_path (ctx);
-#if 1
-      ctx_rectangle (ctx, 0, 0, vt->width, //(vt->cols) * vt->cw,
-                     (vt->rows) * vt->ch);
-      if (vt->reverse_video)
-        {
-          itk_style_color (ctx, "terminal-bg-reverse");
-          ctx_fill  (ctx);
-        }
-      else
-        {
-          itk_style_color (ctx, "terminal-bg");
-          ctx_fill  (ctx);
-        }
-#endif
-      if (vt->scroll != 0.0f)
-        ctx_translate (ctx, 0.0, vt->ch * vt->scroll);
-    }
-  /* draw terminal lines */
-   {
-     for (int row = (vt->scroll!=0.0f)?vt->scroll:0; row < (vt->scroll) + vt->rows; row ++)
-       {
-         CtxList *l = ctx_list_nth (vt->lines, row);
-         float y = y0 + vt->ch * (vt->rows - row);
-         if (row >= vt->rows)
-           {
-             l = ctx_list_nth (vt->scrollback, row-vt->rows);
-           }
-         if (l && y <= (vt->rows - vt->scroll) *  vt->ch)
-           {
-             VtLine *line = l->data;
-             int r = vt->rows - row;
-             const char *data = line->string.str;
+  float *dstf = (float*)dst;
 
-             vt->bg_active = 0;
-             for (int is_fg = 0; is_fg < 2; is_fg++)
-             {
-               const char *d = data;
-               float x = x0;
-               uint64_t style = 0;
-               uint32_t unichar = 0;
-               int in_scrolling_region = vt->in_smooth_scroll &&
-                   ((r >= vt->margin_top && r <= vt->margin_bottom) || r <= 0);
-               if (is_fg)
-                  vt_flush_bg (vt, ctx);
-  
-               for (int col = 1; col <= vt->cols * 1.33 && x < vt->cols * vt->cw; col++)
-                 {
-                   int c = col;
-                   int real_cw;
-                   int in_selected_region = 0;
-                   //if (vt->in_alt_screen == 0)
-                   {
-                   if (r > vt->select_start_row && r < vt->select_end_row)
-                     {
-                       in_selected_region = 1;
-                     }
-                   else if (r == vt->select_start_row)
-                     {
-                       if (col >= vt->select_start_col) { in_selected_region = 1; }
-                       if (r == vt->select_end_row)
-                         {
-                           if (col > vt->select_end_col) { in_selected_region = 0; }
-                         }
-                     }
-                   else if (r == vt->select_end_row)
-                     {
-                       in_selected_region = 1;
-                       if (col > vt->select_end_col) { in_selected_region = 0; }
-                     }
-                   }
-                   if (vt->select_active == 0) in_selected_region = 0;
-                   style = vt_line_get_style (line, col-1);
-                   unichar = d?ctx_utf8_to_unichar (d) :' ';
-  
-                   int is_cursor = 0;
-                   if (vt->cursor_x == col && vt->cursor_y == vt->rows - row && vt->cursor_visible)
-                      is_cursor = 1;
+  CtxPorterDuffFactor f_s, f_d;
+  ctx_porter_duff_factors (compositing_mode, &f_s, &f_d);
+  uint8_t global_alpha_u8 = rasterizer->state->gstate.global_alpha_u8;
+  float   global_alpha_f = rasterizer->state->gstate.global_alpha_f;
   
-                   real_cw=vt_draw_cell (vt, ctx, r, c, x, y, style, unichar,
-                                         line->double_width,
-                                         line->double_height_top?1:
-                                         line->double_height_bottom?-1:0,
-                                         in_scrolling_region,
-                                         in_selected_region ^ is_cursor, is_fg);
-                   if (r == vt->cursor_y && col == vt->cursor_x)
-                     {
-#if 0
-                       cursor_x_px = x;
-#endif
-                     }
-                   x+=real_cw;
-                   if (style & STYLE_BLINK ||
-                       style & STYLE_BLINK_FAST)
-                     {
-                       vt->has_blink = 1;
-                     }
-                   if (d)
-                     {
-                       d = mrg_utf8_skip (d, 1);
-                       if (!*d) { d = NULL; }
-                     }
-                 }
-             }
-#if 0
-             if (line->wrapped)
-             {
-               ctx_rectangle (ctx, x0, y, 10, 10);
-               ctx_rgb (ctx, 1,0,0);
-               ctx_fill (ctx);
-             }
-#endif
-          }
-      }
-  }
-
-#if 0
-  /* draw cursor (done inline with fg/bg reversing, some cursor styles might need
-   * additional drawing though
-   */
-  if (vt->cursor_visible)
-    {
-    //  ctx_rgba (ctx, 0.9, 0.8, 0.0, 0.5333);
-      ctx_rgba (ctx, 1.0,1.0,1.0,1.0);
-      ctx_begin_path (ctx);
-      ctx_rectangle (ctx,
-                     cursor_x_px, cursor_y_px,
-                     cursor_w, cursor_h);
-      ctx_fill (ctx);
-    }
-#endif
+  {
+    float tsrc[components];
+    float u0 = 0; float v0 = 0;
+    float ud = 0; float vd = 0;
 
+    ctx_init_uv (rasterizer, x0, count, &u0, &v0, &ud, &vd);
 
-  {
-    /* draw graphics */
-     for (int row = ((vt->scroll!=0.0f)?vt->scroll:0); row < (vt->scroll) + vt->rows * 4; row ++)
+    while (count--)
+    {
+      int cov = *coverage;
+#if 1
+      if (
+        CTX_UNLIKELY((compositing_mode == CTX_COMPOSITE_DESTINATION_OVER && dst[components-1] == 1.0f)||
+        (cov == 0 && (compositing_mode == CTX_COMPOSITE_SOURCE_OVER ||
+        compositing_mode == CTX_COMPOSITE_XOR               ||
+        compositing_mode == CTX_COMPOSITE_DESTINATION_OUT   ||
+        compositing_mode == CTX_COMPOSITE_SOURCE_ATOP      
+        ))))
       {
-        CtxList *l = ctx_list_nth (vt->lines, row);
-        float y = y0 + vt->ch * (vt->rows - row);
+        u0 += ud;
+        v0 += vd;
+        coverage ++;
+        dstf+=components;
+        continue;
+      }
+#endif
 
-        if (row >= vt->rows && !vt->in_alt_screen)
-          {
-            l = ctx_list_nth (vt->scrollback, row-vt->rows);
-          }
+      fragment (rasterizer, u0, v0, tsrc, 1, ud, vd);
+      if (blend != CTX_BLEND_NORMAL)
+        ctx_float_blend (components, blend, dstf, tsrc, tsrc);
+      u0 += ud;
+      v0 += vd;
+      float covf = ctx_u8_to_float (cov);
 
-        if (l && y <= (vt->rows - vt->scroll) *  vt->ch)
-          {
-            VtLine *line = l->data;
-            {
-            for (int i = 0; i < 4; i++)
-              {
-                Image *image = line->images[i];
-                if (image)
-                  {
-                    int u = (line->image_col[i]-1) * vt->cw + (line->image_X[i] * vt->cw);
-                    int v = y - vt->ch + (line->image_Y[i] * vt->ch);
-                //  int rows = (image->height + (vt->ch-1) ) /vt->ch;
-                //
-                //
-                    if (v + image->height +vt->scroll * vt->ch > 0.0 &&
-                        image->width && image->height /* some ghost images appear with these */
-                        )
-                    {
-                    ctx_save (ctx);
-                    ctx_rectangle (ctx, x0, y0 - vt->scroll * vt->ch, vt->cw * vt->cols,
-                                    vt->ch * vt->rows);
-                    ctx_clip (ctx);
-                    char texture_n[65]; 
-
-                    sprintf (texture_n, "vtimg%i", image->eid_no);
-                    ctx_rectangle (ctx, u, v, image->width, image->height);
-                    ctx_translate (ctx, u, v);
-
-                    //replace this texture_n with NULL to
-                    // be content addressed - but bit slower
-                    ctx_define_texture (ctx, texture_n, image->width,
-                                        image->height,
-                                        0,
-                                        image->kitty_format == 32 ?
-                                                 CTX_FORMAT_RGBA8 :
-                                                 CTX_FORMAT_RGB8,
-                                        image->data, texture_n);
-                    ctx_fill (ctx);
-
-                    ctx_restore (ctx);
-                    }
-                  }
-              }
+      if (global_alpha_u8 != 255)
+        covf = covf * global_alpha_f;
 
-            if (line->ctx_copy)
-              {
-                //fprintf (stderr, " [%i]\n", _ctx_frame (ctx));
-                //ctx_render_stream (line->ctx_copy, stderr, 1);
-
-                ctx_begin_path (ctx);
-                ctx_save (ctx);
-                ctx_rectangle (ctx, x0, y0 - vt->scroll * vt->ch, vt->cw * vt->cols,
-                                    vt->ch * vt->rows);
-                ctx_clip (ctx);
-                ctx_translate (ctx, 0.0, y - vt->ch);
-
-                //(vt->rows-row-1) * (vt->ch) );
-                //float factor = vt->cols * vt->cw / 1000.0;
-                //ctx_scale (ctx, factor, factor);
-                //
-                ctx_render_ctx (line->ctx_copy, ctx);
-                ctx_restore (ctx);
-              }
-            }
-          }
-    //  y -= vt->ch;
+      if (covf != 1.0f)
+      {
+        for (int c = 0; c < components; c++)
+          tsrc[c] *= covf;
       }
-  }
 
-
-  for (int i = 0; i < 4; i++)
-    {
-      if (vt->leds[i])
+      for (int c = 0; c < components; c++)
+      {
+        float res;
+        /* these switches and this whole function is written to be
+         * inlined when compiled when the enum values passed in are
+         * constants.
+         */
+        switch (f_s)
         {
-          ctx_rgba (ctx, .5,1,.5,0.8);
-          ctx_rectangle (ctx, vt->cw * i + vt->cw * 0.25, vt->ch * 0.25, vt->cw/2, vt->ch/2);
-          ctx_fill (ctx);
+          case CTX_PORTER_DUFF_0: res = 0.0f; break;
+          case CTX_PORTER_DUFF_1:             res = (tsrc[c]); break;
+          case CTX_PORTER_DUFF_ALPHA:         res = (tsrc[c] *       dstf[components-1]); break;
+          case CTX_PORTER_DUFF_1_MINUS_ALPHA: res = (tsrc[c] * (1.0f-dstf[components-1])); break;
+        }
+        switch (f_d)
+        {
+          case CTX_PORTER_DUFF_0: dstf[c] = res; break;
+          case CTX_PORTER_DUFF_1:             dstf[c] = res + (dstf[c]); break;
+          case CTX_PORTER_DUFF_ALPHA:         dstf[c] = res + (dstf[c] *       tsrc[components-1]); break;
+          case CTX_PORTER_DUFF_1_MINUS_ALPHA: dstf[c] = res + (dstf[c] * (1.0f-tsrc[components-1])); break;
         }
+      }
+      coverage ++;
+      dstf+=components;
     }
-  ctx_restore (ctx);
-//#define SCROLL_SPEED 0.25;
-#define SCROLL_SPEED 0.001;
-  if (vt->in_smooth_scroll)
-   {
-     if (vt->in_smooth_scroll<0)
-       {
-         vt->scroll_offset += SCROLL_SPEED;
-         if (vt->scroll_offset >= 0.0)
-           {
-             vt->scroll_offset = 0;
-             vt->in_smooth_scroll = 0;
-             vt->rev++;
-           }
-       }
-     else
-       {
-         vt->scroll_offset -= SCROLL_SPEED;
-         if (vt->scroll_offset <= 0.0)
-           {
-             vt->scroll_offset = 0;
-             vt->in_smooth_scroll = 0;
-             vt->rev++;
-           }
-       }
-   }
-
-    /* scrollbar */
-    if (!vt->in_alt_screen)
-    {
-      float disp_lines = vt->rows;
-      float tot_lines = vt->line_count + vt->scrollback_count;
-      float offset = (tot_lines - disp_lines - vt->scroll) / tot_lines;
-      float win_len = disp_lines / tot_lines;
+  }
+}
 
-#if 0
-      ctx_rectangle (ctx, (vt->cols *vt->cw), 0, 
-                       (vt->width) - (vt->cols * vt->cw),
-                       vt->rows *  vt->ch);
-      ctx_rgb (ctx,1,0,0);
-      ctx_fill (ctx);
+/* generating one function per compositing_mode would be slightly more efficient,
+ * but on embedded targets leads to slightly more code bloat,
+ * here we trade off a slight amount of performance
+ */
+#define ctx_float_porter_duff(compformat, components, source, fragment, blend) \
+static void \
+ctx_##compformat##_porter_duff_##source (CTX_COMPOSITE_ARGUMENTS) \
+{ \
+   switch (rasterizer->state->gstate.compositing_mode) \
+   { \
+     case CTX_COMPOSITE_SOURCE_ATOP: \
+      ctx_float_porter_duff (rasterizer, components, dst, src, x0, coverage, count, \
+        CTX_COMPOSITE_SOURCE_ATOP, fragment, blend);\
+      break;\
+     case CTX_COMPOSITE_DESTINATION_ATOP:\
+      ctx_float_porter_duff (rasterizer, components, dst, src, x0, coverage, count,\
+        CTX_COMPOSITE_DESTINATION_ATOP, fragment, blend);\
+      break;\
+     case CTX_COMPOSITE_DESTINATION_IN:\
+      ctx_float_porter_duff (rasterizer, components, dst, src, x0, coverage, count,\
+        CTX_COMPOSITE_DESTINATION_IN, fragment, blend);\
+      break;\
+     case CTX_COMPOSITE_DESTINATION:\
+      ctx_float_porter_duff (rasterizer, components, dst, src, x0, coverage, count,\
+        CTX_COMPOSITE_DESTINATION, fragment, blend);\
+       break;\
+     case CTX_COMPOSITE_SOURCE_OVER:\
+      ctx_float_porter_duff (rasterizer, components, dst, src, x0, coverage, count,\
+        CTX_COMPOSITE_SOURCE_OVER, fragment, blend);\
+       break;\
+     case CTX_COMPOSITE_DESTINATION_OVER:\
+      ctx_float_porter_duff (rasterizer, components, dst, src, x0, coverage, count,\
+        CTX_COMPOSITE_DESTINATION_OVER, fragment, blend);\
+       break;\
+     case CTX_COMPOSITE_XOR:\
+      ctx_float_porter_duff (rasterizer, components, dst, src, x0, coverage, count,\
+        CTX_COMPOSITE_XOR, fragment, blend);\
+       break;\
+     case CTX_COMPOSITE_DESTINATION_OUT:\
+       ctx_float_porter_duff (rasterizer, components, dst, src, x0, coverage, count,\
+        CTX_COMPOSITE_DESTINATION_OUT, fragment, blend);\
+       break;\
+     case CTX_COMPOSITE_SOURCE_OUT:\
+       ctx_float_porter_duff (rasterizer, components, dst, src, x0, coverage, count,\
+        CTX_COMPOSITE_SOURCE_OUT, fragment, blend);\
+       break;\
+     case CTX_COMPOSITE_SOURCE_IN:\
+       ctx_float_porter_duff (rasterizer, components, dst, src, x0, coverage, count,\
+        CTX_COMPOSITE_SOURCE_IN, fragment, blend);\
+       break;\
+     case CTX_COMPOSITE_COPY:\
+       ctx_float_porter_duff (rasterizer, components, dst, src, x0, coverage, count,\
+        CTX_COMPOSITE_COPY, fragment, blend);\
+       break;\
+     case CTX_COMPOSITE_CLEAR:\
+       ctx_float_porter_duff (rasterizer, components, dst, src, x0, coverage, count,\
+        CTX_COMPOSITE_CLEAR, fragment, blend);\
+       break;\
+   }\
+}
 #endif
 
-      ctx_rectangle (ctx, (vt->width) - vt->cw * 1.5,
-                     0, 1.5 * vt->cw,
-                     vt->rows * vt->ch);
-      //ctx_listen (ctx, CTX_DRAG,  scrollbar_drag, vt, NULL);
-      //ctx_listen (ctx, CTX_ENTER, scrollbar_enter, vt, NULL);
-      //ctx_listen (ctx, CTX_LEAVE, scrollbar_leave, vt, NULL);
-      if (vt->scroll != 0 || scrollbar_focused)
-        ctx_rgba (ctx, 0.5, 0.5, 0.5, .25);
-      else
-        ctx_rgba (ctx, 0.5, 0.5, 0.5, .10);
-      ctx_fill (ctx);
-      ctx_round_rectangle (ctx, (vt->width) - vt->cw * 1.5,
-                           offset * vt->rows * vt->ch, (1.5-0.2) * vt->cw,
-                           win_len * vt->rows * vt->ch,
-                           vt->cw * 1.5 /2);
-      //ctx_listen (ctx, CTX_DRAG, scroll_handle_drag, vt, NULL);
-      if (vt->scroll != 0 || scrollbar_focused)
-        ctx_rgba (ctx, 1, 1, 1, .25);
-      else
-        ctx_rgba (ctx, 1, 1, 1, .10);
-      ctx_fill (ctx);
-    }
+#if CTX_ENABLE_RGBAF
 
-    ctx_rectangle (ctx, 0, 0, vt->cols * vt->cw, vt->rows * vt->ch);
-    ctx_listen (ctx, CTX_DRAG,   vt_mouse_event, vt, NULL);
-    ctx_listen (ctx, CTX_MOTION, vt_mouse_event, vt, NULL);
-    ctx_begin_path (ctx);
+ctx_float_porter_duff(RGBAF, 4,color,           rasterizer->fragment,                               
rasterizer->state->gstate.blend_mode)
+ctx_float_porter_duff(RGBAF, 4,generic,         rasterizer->fragment,               
rasterizer->state->gstate.blend_mode)
 
-    ctx_restore (ctx);
+#if CTX_INLINED_NORMAL
+#if CTX_GRADIENTS
+ctx_float_porter_duff(RGBAF, 4,linear_gradient, ctx_fragment_linear_gradient_RGBAF, 
rasterizer->state->gstate.blend_mode)
+ctx_float_porter_duff(RGBAF, 4,radial_gradient, ctx_fragment_radial_gradient_RGBAF, 
rasterizer->state->gstate.blend_mode)
+#endif
+ctx_float_porter_duff(RGBAF, 4,image,           ctx_fragment_image_RGBAF,           
rasterizer->state->gstate.blend_mode)
 
-    if (vt->popped)
-    {
-       //ctx_set_popup (ctx, test_popup, vt);
-    }
-}
 
+#if CTX_GRADIENTS
+#define ctx_float_porter_duff_blend(comp_name, components, blend_mode, blend_name)\
+ctx_float_porter_duff(comp_name, components,color_##blend_name,            rasterizer->fragment,             
                  blend_mode)\
+ctx_float_porter_duff(comp_name, components,generic_##blend_name,          rasterizer->fragment,             
  blend_mode)\
+ctx_float_porter_duff(comp_name, components,linear_gradient_##blend_name,  
ctx_fragment_linear_gradient_RGBA8, blend_mode)\
+ctx_float_porter_duff(comp_name, components,radial_gradient_##blend_name,  
ctx_fragment_radial_gradient_RGBA8, blend_mode)\
+ctx_float_porter_duff(comp_name, components,image_##blend_name,            ctx_fragment_image_RGBAF,         
  blend_mode)
+#else
+#define ctx_float_porter_duff_blend(comp_name, components, blend_mode, blend_name)\
+ctx_float_porter_duff(comp_name, components,color_##blend_name,            rasterizer->fragment,             
                  blend_mode)\
+ctx_float_porter_duff(comp_name, components,generic_##blend_name,          rasterizer->fragment,             
  blend_mode)\
+ctx_float_porter_duff(comp_name, components,image_##blend_name,            ctx_fragment_image_RGBAF,         
  blend_mode)
+#endif
+
+ctx_float_porter_duff_blend(RGBAF, 4, CTX_BLEND_NORMAL, normal)
 
-int vt_is_done (VT *vt)
-{
-  return vt->vtpty.done;
-}
 
-int vt_get_result (VT *vt)
+static void
+ctx_RGBAF_copy_normal (CTX_COMPOSITE_ARGUMENTS)
 {
-  /* we could block - at least for a while, here..? */
-  return vt->result;
+  ctx_float_copy_normal (4, rasterizer, dst, src, x0, coverage, count);
 }
 
-void vt_set_scrollback_lines (VT *vt, int scrollback_lines)
+static void
+ctx_RGBAF_clear_normal (CTX_COMPOSITE_ARGUMENTS)
 {
-  vt->scrollback_limit = scrollback_lines;
+  ctx_float_clear_normal (4, rasterizer, dst, src, x0, coverage, count);
 }
 
-int  vt_get_scrollback_lines (VT *vt)
+#if 0
+static void
+ctx_RGBAF_source_over_normal_opaque_color (CTX_COMPOSITE_ARGUMENTS)
 {
-  return vt->scrollback_limit;
+  ctx_float_source_over_normal_opaque_color (4, rasterizer, dst, rasterizer->color, x0, coverage, count);
 }
+#endif
+#endif
 
-void vt_set_scroll (VT *vt, int scroll)
+static void
+ctx_setup_RGBAF (CtxRasterizer *rasterizer)
 {
-  if (vt->scroll == scroll)
-    return;
-  vt->scroll = scroll;
-  if (vt->scroll > ctx_list_length (vt->scrollback) )
-    { vt->scroll = ctx_list_length (vt->scrollback); }
-  if (vt->scroll < 0)
-    { vt->scroll = 0; }
+  CtxGState *gstate = &rasterizer->state->gstate;
+  int components = 4;
+  rasterizer->fragment = ctx_rasterizer_get_fragment_RGBAF (rasterizer);
+#if 1
+  if (gstate->source_fill.type == CTX_SOURCE_COLOR)
+    {
+      rasterizer->comp_op = ctx_RGBAF_porter_duff_color;
+      ctx_color_get_rgba (rasterizer->state, &gstate->source_fill.color, (float*)rasterizer->color);
+      if (gstate->global_alpha_u8 != 255)
+        for (int c = 0; c < components; c ++)
+          ((float*)rasterizer->color)[c] *= gstate->global_alpha_f;
+    }
+  else
+#endif
+  {
+    rasterizer->comp_op = ctx_RGBAF_porter_duff_generic;
+  }
+
+#if CTX_INLINED_NORMAL
+  if (gstate->compositing_mode == CTX_COMPOSITE_CLEAR)
+    rasterizer->comp_op = ctx_RGBAF_clear_normal;
+  else
+    switch (gstate->blend_mode)
+    {
+      case CTX_BLEND_NORMAL:
+        if (gstate->compositing_mode == CTX_COMPOSITE_COPY)
+        {
+          rasterizer->comp_op = ctx_RGBAF_copy_normal;
+        }
+        else if (gstate->global_alpha_u8 == 0)
+        {
+          rasterizer->comp_op = CTX_COMPOSITE_SUFFIX(ctx_RGBA8_nop);
+        }
+        else
+        switch (gstate->source_fill.type)
+        {
+          case CTX_SOURCE_COLOR:
+            if (gstate->compositing_mode == CTX_COMPOSITE_SOURCE_OVER)
+            {
+              if (((float*)(rasterizer->color))[components-1] == 0.0f)
+                rasterizer->comp_op = CTX_COMPOSITE_SUFFIX(ctx_RGBA8_nop);
+            }
+            else
+            {
+              rasterizer->comp_op = ctx_RGBAF_porter_duff_color_normal;
+            }
+            break;
+#if CTX_GRADIENTS
+          case CTX_SOURCE_LINEAR_GRADIENT:
+            rasterizer->comp_op = ctx_RGBAF_porter_duff_linear_gradient_normal;
+            break;
+          case CTX_SOURCE_RADIAL_GRADIENT:
+            rasterizer->comp_op = ctx_RGBAF_porter_duff_radial_gradient_normal;
+            break;
+#endif
+          case CTX_SOURCE_TEXTURE:
+            rasterizer->comp_op = ctx_RGBAF_porter_duff_image_normal;
+            break;
+          default:
+            rasterizer->comp_op = ctx_RGBAF_porter_duff_generic_normal;
+            break;
+        }
+        break;
+      default:
+        switch (gstate->source_fill.type)
+        {
+          case CTX_SOURCE_COLOR:
+            rasterizer->comp_op = ctx_RGBAF_porter_duff_color;
+//          rasterizer->fragment = NULL;
+            break;
+#if CTX_GRADIENTS
+          case CTX_SOURCE_LINEAR_GRADIENT:
+            rasterizer->comp_op = ctx_RGBAF_porter_duff_linear_gradient;
+            break;
+          case CTX_SOURCE_RADIAL_GRADIENT:
+            rasterizer->comp_op = ctx_RGBAF_porter_duff_radial_gradient;
+            break;
+#endif
+          case CTX_SOURCE_TEXTURE:
+            rasterizer->comp_op = ctx_RGBAF_porter_duff_image;
+            break;
+          default:
+            rasterizer->comp_op = ctx_RGBAF_porter_duff_generic;
+            break;
+        }
+        break;
+    }
+#endif
 }
 
-int vt_get_scroll (VT *vt)
+#endif
+#if CTX_ENABLE_GRAYAF
+
+#if CTX_GRADIENTS
+static void
+ctx_fragment_linear_gradient_GRAYAF (CtxRasterizer *rasterizer, float x, float y, void *out, int count, 
float dx, float dy)
 {
-  return vt->scroll;
+  float rgba[4];
+  CtxSource *g = &rasterizer->state->gstate.source_fill;
+  for (int i = 0 ; i < count; i++)
+  {
+  float v = ( ( (g->linear_gradient.dx * x + g->linear_gradient.dy * y) /
+                g->linear_gradient.length) -
+              g->linear_gradient.start) * (g->linear_gradient.rdelta);
+  ctx_fragment_gradient_1d_RGBAF (rasterizer, v, 1.0, rgba);
+  ((float*)out)[0] = ctx_float_color_rgb_to_gray (rasterizer->state, rgba);
+  ((float*)out)[1] = rgba[3];
+     out = ((float*)(out)) + 2;
+     x += dx;
+     y += dy;
+  }
 }
 
-char *
-vt_get_selection (VT *vt)
+static void
+ctx_fragment_radial_gradient_GRAYAF (CtxRasterizer *rasterizer, float x, float y, void *out, int count, 
float dx, float dy)
 {
-  CtxString *str = ctx_string_new ("");
-  char *ret;
-  for (int row = vt->select_start_row; row <= vt->select_end_row; row++)
+  float rgba[4];
+  CtxSource *g = &rasterizer->state->gstate.source_fill;
+  for (int i = 0; i < count; i ++)
+  {
+  float v = 0.0f;
+  if ((g->radial_gradient.r1-g->radial_gradient.r0) > 0.0f)
     {
-      const char *line_str = vt_get_line (vt, vt->rows - row);
-      int col = 1;
-      for (const char *c = line_str; *c; c = mrg_utf8_skip (c, 1), col ++)
-        {
-          if (row == vt->select_end_row && col > vt->select_end_col)
-            { continue; }
-          if (row == vt->select_start_row && col < vt->select_start_col)
-            { continue; }
-          ctx_string_append_utf8char (str, c);
-        }
-      if (row < vt->select_end_row && !vt_line_is_continuation (vt, vt->rows-row-1))
-      {
-        _ctx_string_append_byte (str, '\n');
-      }
+      v = ctx_hypotf (g->radial_gradient.x0 - x, g->radial_gradient.y0 - y);
+      v = (v - g->radial_gradient.r0) / (g->radial_gradient.rdelta);
     }
-  ret = str->str;
-  ctx_string_free (str, 0);
-  return ret;
-}
-
-int vt_get_local (VT *vt)
-{
-  return vt->local_editing;
+  ctx_fragment_gradient_1d_RGBAF (rasterizer, v, 0.0, rgba);
+  ((float*)out)[0] = ctx_float_color_rgb_to_gray (rasterizer->state, rgba);
+  ((float*)out)[1] = rgba[3];
+     out = ((float*)(out)) + 2;
+     x += dx;
+     y += dy;
+  }
 }
+#endif
 
-void vt_set_local (VT *vt, int local)
+static void
+ctx_fragment_color_GRAYAF (CtxRasterizer *rasterizer, float x, float y, void *out, int count, float dx, 
float dy)
 {
-  vt->local_editing = local;
+  CtxSource *g = &rasterizer->state->gstate.source_fill;
+  for (int i = 0; i < count; i++)
+  {
+     ctx_color_get_graya (rasterizer->state, &g->color, (float*)out);
+     out = ((float*)(out)) + 2;
+     x += dx;
+     y += dy;
+  }
 }
 
-static unsigned long prev_press_time = 0;
-static int short_count = 0;
-
-
-void terminal_set_primary (const char *text)
+static void ctx_fragment_image_GRAYAF (CtxRasterizer *rasterizer, float x, float y, void *out, int count, 
float dx, float dy)
 {
-  if (primary) free (primary);
-  primary = NULL;
-  if (text) primary = strdup (text);
+  uint8_t rgba[4];
+  float rgbaf[4];
+  CtxGState *gstate = &rasterizer->state->gstate;
+  CtxBuffer *buffer = gstate->source_fill.texture.buffer;
+  switch (buffer->format->bpp)
+    {
+      case 1:  ctx_fragment_image_gray1_RGBA8 (rasterizer, x, y, rgba, count, dx, dy); break;
+      case 24: ctx_fragment_image_rgb8_RGBA8 (rasterizer, x, y, rgba, count, dx, dy);  break;
+      case 32: ctx_fragment_image_rgba8_RGBA8 (rasterizer, x, y, rgba, count, dx, dy); break;
+      default: ctx_fragment_image_RGBA8 (rasterizer, x, y, rgba, count, dx, dy);       break;
+    }
+  for (int c = 0; c < 2 * count; c ++) { 
+    rgbaf[c] = ctx_u8_to_float (rgba[c]);
+    ((float*)out)[0] = ctx_float_color_rgb_to_gray (rasterizer->state, rgbaf);
+    ((float*)out)[1] = rgbaf[3];
+    out = ((float*)out) + 2;
+  }
 }
 
-void terminal_long_tap (Ctx *ctx, VT *vt);
-static int long_tap_cb_id = 0;
-static int single_tap (Ctx *ctx, void *data)
+static CtxFragment ctx_rasterizer_get_fragment_GRAYAF (CtxRasterizer *rasterizer)
 {
-#if 0 // XXX
-  VT *vt = data;
-  if (short_count == 0 && !vt->select_active)
-    terminal_long_tap (ctx, vt);
+  CtxGState *gstate = &rasterizer->state->gstate;
+  switch (gstate->source_fill.type)
+    {
+      case CTX_SOURCE_TEXTURE:           return ctx_fragment_image_GRAYAF;
+      case CTX_SOURCE_COLOR:           return ctx_fragment_color_GRAYAF;
+#if CTX_GRADIENTS
+      case CTX_SOURCE_LINEAR_GRADIENT: return ctx_fragment_linear_gradient_GRAYAF;
+      case CTX_SOURCE_RADIAL_GRADIENT: return ctx_fragment_radial_gradient_GRAYAF;
 #endif
-  return 0;
+    }
+  return ctx_fragment_color_GRAYAF;
 }
 
-void vt_mouse (VT *vt, CtxEvent *event, VtMouseEvent type, int button, int x, int y, int px_x, int px_y)
-{
- char buf[64]="";
- int button_state = 0;
- vt->rev++;
- ctx_ticks();
- if ((! (vt->mouse | vt->mouse_all | vt->mouse_drag)) ||
-     (event && (event->state & CTX_MODIFIER_STATE_SHIFT)))
-   {
-     // regular mouse select, this is incomplete
-     // fully ignorant of scrollback for now
-     //
-     if (type == VT_MOUSE_PRESS)
-       {
-         vt->cursor_down = 1;
-         vt->select_begin_col = x;
-         vt->select_begin_row = y - (int)vt->scroll;
-         vt->select_start_col = x;
-         vt->select_start_row = y - (int)vt->scroll;
-         vt->select_end_col = x;
-         vt->select_end_row = y - (int)vt->scroll;
-         vt->select_active = 0;
-         if (long_tap_cb_id)
-           {
-             ctx_remove_idle (vt->root_ctx, long_tap_cb_id);
-             long_tap_cb_id = 0;
-           }
-         
-         if ((ctx_ticks () - prev_press_time) < 1000*300 &&
-             abs(px_x - vt->select_begin_x) + 
-             abs(px_y - vt->select_begin_y) < 8)
-         {
-           short_count++;
-           switch (short_count)
-           {
-             case 1:
-             {
-               /* extend selection until space, XXX  should handle utf8 instead of ascii here!  */
-
-               int hit_space = 0;
-           
-               while (vt->select_start_col > 1 && !hit_space)
-               {
-                 vt->select_start_col --;
-                 char *sel = vt_get_selection (vt);
-                 if (sel[0] == ' ' || sel[0] == '\0')
-                   hit_space = 1;
-                 free (sel);
-               }
-               if (hit_space)
-                 vt->select_start_col++;
-
-               hit_space = 0;
-               while ((hit_space == 0) &&
-                      (vt->select_end_col < vt->cols))
-               {
-                 vt->select_end_col ++;
-                 char *sel = vt_get_selection (vt);
-                 int len = strlen(sel);
-                 if (sel[len-1]==' ')
-                   hit_space = 1;
-                 free (sel);
-               }
-               if (hit_space)
-                 vt->select_end_col--;
-
-               vt->select_active = 1;
-
-               { char *sel = vt_get_selection (vt);
-                 if (sel)
-                 {
-                    terminal_set_primary (sel);
-                    free (sel);
-                 }
-               }
-               }
-               break;
-             case 2:
-               vt->select_start_col = 1;
-               vt->select_end_col = vt->cols;
-               vt->select_active = 1;
-               {
-                 char *sel = vt_get_selection (vt);
-                 if (sel){
-                    terminal_set_primary (sel);
-                    free (sel);
-                 }
-               }
-               break;
-             case 3:
-               short_count = 0;
-               vt->select_start_col = 
-               vt->select_end_col = vt->select_begin_col;
-               vt->select_active = 0;
-               terminal_set_primary ("");
-               break;
-           }
-         }
-         else
-         {
-           if (vt->root_ctx && short_count == 0)
-             long_tap_cb_id = ctx_add_timeout (vt->root_ctx, 1000, single_tap, vt);
-           short_count = 0;
-           //vt->select_start_col = 
-           //vt->select_end_col = vt->select_begin_col;
-         }
-         vt->select_begin_x = px_x;
-         vt->select_begin_y = px_y;
-         prev_press_time = ctx_ticks ();
-         vt->rev++;
-       }
-     else if (type == VT_MOUSE_RELEASE)
-       {
-         if (long_tap_cb_id)
-           {
-             ctx_remove_idle (vt->root_ctx, long_tap_cb_id);
-             long_tap_cb_id = 0;
-           }
-         vt->cursor_down = 0;
-       }
-     else if (type == VT_MOUSE_MOTION && vt->cursor_down)
-       {
-         int row = y - (int)vt->scroll;
-         int col = x;
-         if ((row > vt->select_begin_row) ||
-             ((row == vt->select_begin_row) && (col >= vt->select_begin_col)))
-         {
-           vt->select_start_col = vt->select_begin_col;
-           vt->select_start_row = vt->select_begin_row;
-           vt->select_end_col = col;
-           vt->select_end_row = row;
-         }
-         else
-         {
-           vt->select_start_col = col;
-           vt->select_start_row = row;
-           vt->select_end_col = vt->select_begin_col;
-           vt->select_end_row = vt->select_begin_row;
-         }
-         if (vt->select_end_row == vt->select_start_row &&
-             abs (vt->select_begin_x - px_x) < vt->cw/2)
-         {
-           vt->select_active = 0;
-         }
-         else
-         {
-           vt->select_active = 1;
-           char *selection = vt_get_selection (vt);
-           if (selection)
-           {
-             terminal_set_primary (selection);
-             free (selection);
-           }
-         }
-
-         if (y < 1)
-         {
-           vt->scroll += 1.0f;
-           if (vt->scroll > vt->scrollback_count)
-             vt->scroll = vt->scrollback_count;
-         }
-         else if (y > vt->rows)
-         {
-           vt->scroll -= 1.0f;
-           if (vt->scroll < 0)
-             vt->scroll = 0.0f;
-         }
+ctx_float_porter_duff(GRAYAF, 2,color,   rasterizer->fragment, rasterizer->state->gstate.blend_mode)
+ctx_float_porter_duff(GRAYAF, 2,generic, rasterizer->fragment, rasterizer->state->gstate.blend_mode)
 
-         vt->rev++;
-       }
-     return;
-   }
- if (type == VT_MOUSE_MOTION)
-   { button_state = 3; }
+#if CTX_INLINED_NORMAL
+ctx_float_porter_duff(GRAYAF, 2,color_normal,   rasterizer->fragment, CTX_BLEND_NORMAL)
+ctx_float_porter_duff(GRAYAF, 2,generic_normal, rasterizer->fragment, CTX_BLEND_NORMAL)
 
- if (vt->unit_pixels && vt->mouse_decimal)
-   {
-     x = px_x;
-     y = px_y;
-   }
- switch (type)
-   {
-     case VT_MOUSE_MOTION:
-       if (!vt->mouse_all)
-         return;
-       if (x==vt->lastx && y==vt->lasty)
-         return;
-       vt->lastx = x;
-       vt->lasty = y;
-   //  sprintf (buf, "\033[<35;%i;%iM", x, y);
-       break;
-     case VT_MOUSE_RELEASE:
-       if (vt->mouse_decimal == 0)
-         button_state = 3;
-       break;
-     case VT_MOUSE_PRESS:
-       button_state = 0;
-       break;
-     case VT_MOUSE_DRAG: // XXX not really used - remove
-       if (! (vt->mouse_all || vt->mouse_drag) )
-         return;
-       button_state = 32;
-       break;
-   }
- // todo : mix in ctrl/meta state
- if (vt->mouse_decimal)
-   {
-     sprintf (buf, "\033[<%i;%i;%i%c", button_state, x, y, type == VT_MOUSE_RELEASE?'m':'M');
-   }
- else
-   { 
-     sprintf (buf, "\033[M%c%c%c", button_state + 32, x + 32, y + 32);
-   }
- if (buf[0])
-   {
-     vt_write (vt, buf, strlen (buf) );
-     fflush (NULL);
-   }
+static void
+ctx_GRAYAF_copy_normal (CTX_COMPOSITE_ARGUMENTS)
+{
+  ctx_float_copy_normal (2, rasterizer, dst, src, x0, coverage, count);
 }
 
-pid_t vt_get_pid (VT *vt)
+static void
+ctx_GRAYAF_clear_normal (CTX_COMPOSITE_ARGUMENTS)
 {
-  return vt->vtpty.pid;
+  ctx_float_clear_normal (2, rasterizer, dst, src, x0, coverage, count);
 }
 
-void vt_set_ctx (VT *vt, Ctx *ctx)
+static void
+ctx_GRAYAF_source_over_normal_opaque_color (CTX_COMPOSITE_ARGUMENTS)
 {
-  vt->root_ctx = ctx;
+  ctx_float_source_over_normal_opaque_color (2, rasterizer, dst, rasterizer->color, x0, coverage, count);
 }
-#ifndef _DEFAULT_SOURCE
-#define _DEFAULT_SOURCE
 #endif
 
-#if !__COSMOPOLITAN__
-#include <unistd.h>
-#include <assert.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <stdint.h>
-#include <string.h>
-#include <sys/types.h>
-#include <sys/wait.h>
-#include <sys/ioctl.h>
-#include <signal.h>
-#include <pty.h>
-#include <math.h>
-#include <malloc.h>
-#include <sys/time.h>
-#include <time.h>
-#endif
-
-#define VT_RECORD 0
-extern Ctx *ctx;
-#define flag_is_set(a, f) (((a) & (f))!=0)
-#define flag_set(a, f)    ((a) |= (f));
-#define flag_unset(a, f)  ((a) &= ~(f));
-
-//#define CTX_x            CTX_STRH('x',0,0,0,0,0,0,0,0,0,0,0,0,0)
-//#define CTX_y            CTX_STRH('y',0,0,0,0,0,0,0,0,0,0,0,0,0)
-#define CTX_lower_bottom CTX_STRH('l','o','w','e','r','-','b','o','t','t','o','m',0,0)
-#define CTX_lower        CTX_STRH('l','o','w','e','r',0,0,0,0,0,0,0,0,0)
-#define CTX_raise        CTX_STRH('r','a','i','s','e',0,0,0,0,0,0,0,0,0)
-#define CTX_raise_top    CTX_STRH('r','a','i','s','e','-','t','o','p',0,0,0,0,0)
-#define CTX_terminate    CTX_STRH('t','e','r','m','i','n','a','t','e',0,0,0,0,0)
-#define CTX_maximize     CTX_STRH('m','a','x','i','m','i','z','e',0,0,0,0,0,0)
-#define CTX_unmaximize   CTX_STRH('u','n','m','a','x','i','m','i','z','e',0,0,0,0)
-//#define CTX_width        CTX_STRH('w','i','d','t','h',0,0,0,0,0,0,0,0,0)
-//#define CTX_title        CTX_STRH('t','i','t','l','e',0,0,0,0,0,0,0,0,0)
-#define CTX_title        15643372
-#define CTX_action       CTX_STRH('a','c','t','i','o','n',0,0,0,0,0,0,0,0)
-//#define CTX_height       CTX_STRH('h','e','i','g','h','t',0,0,0,0,0,0,0,0)
-
-void terminal_update_title  (const char *title);
-int  ctx_renderer_is_sdl    (Ctx *ctx);
-int  ctx_renderer_is_fb     (Ctx *ctx);
-int  ctx_renderer_is_term   (Ctx *ctx);
-void ctx_sdl_set_fullscreen (Ctx *ctx, int val);
-int  ctx_sdl_get_fullscreen (Ctx *ctx);
-float ctx_target_fps = 25.0;
-static int ctx_fetched_bytes = 1;
-
-CtxClient *vt_get_client (VT *vt);
-
-CtxList *vts = NULL;
-
-void ctx_clients_signal_child (int signum)
-{
-  pid_t pid;
-  int   status;
-  if ( (pid = waitpid (-1, &status, WNOHANG) ) != -1)
-    {
-      if (pid)
+static void
+ctx_setup_GRAYAF (CtxRasterizer *rasterizer)
+{
+  CtxGState *gstate = &rasterizer->state->gstate;
+  int components = 2;
+  rasterizer->fragment = ctx_rasterizer_get_fragment_GRAYAF (rasterizer);
+  if (gstate->source_fill.type == CTX_SOURCE_COLOR)
+    {
+      rasterizer->comp_op = ctx_GRAYAF_porter_duff_color;
+  //  rasterizer->fragment = NULL;
+      ctx_color_get_rgba (rasterizer->state, &gstate->source_fill.color, (float*)rasterizer->color);
+      if (gstate->global_alpha_u8 != 255)
+        for (int c = 0; c < components; c ++)
+          ((float*)rasterizer->color)[c] *= gstate->global_alpha_f;
+    }
+  else
+  {
+    rasterizer->comp_op = ctx_GRAYAF_porter_duff_generic;
+  }
+
+#if CTX_INLINED_NORMAL
+  if (gstate->compositing_mode == CTX_COMPOSITE_CLEAR)
+    rasterizer->comp_op = ctx_GRAYAF_clear_normal;
+  else
+    switch (gstate->blend_mode)
+    {
+      case CTX_BLEND_NORMAL:
+        if (gstate->compositing_mode == CTX_COMPOSITE_COPY)
+        {
+          rasterizer->comp_op = ctx_GRAYAF_copy_normal;
+        }
+        else if (gstate->global_alpha_u8 == 0)
+          rasterizer->comp_op = CTX_COMPOSITE_SUFFIX(ctx_RGBA8_nop);
+        else
+        switch (gstate->source_fill.type)
         {
-          for (CtxList *l = vts; l; l=l->next)
+          case CTX_SOURCE_COLOR:
+            if (gstate->compositing_mode == CTX_COMPOSITE_SOURCE_OVER)
             {
-              VtPty *vt = l->data;
-              if (vt->pid == pid)
-                {
-                  vt->done = 1;
-                  //vt->result = status;
-                }
+              if (((float*)rasterizer->color)[components-1] == 0.0f)
+                rasterizer->comp_op = CTX_COMPOSITE_SUFFIX(ctx_RGBA8_nop);
+#if 0
+              else if (((float*)rasterizer->color)[components-1] == 0.0f)
+                rasterizer->comp_op = ctx_GRAYAF_source_over_normal_opaque_color;
+#endif
+              else
+                rasterizer->comp_op = ctx_GRAYAF_porter_duff_color_normal;
+//            rasterizer->fragment = NULL;
+            }
+            else
+            {
+              rasterizer->comp_op = ctx_GRAYAF_porter_duff_color_normal;
+//            rasterizer->fragment = NULL;
             }
+            break;
+          default:
+            rasterizer->comp_op = ctx_GRAYAF_porter_duff_generic_normal;
+            break;
+        }
+        break;
+      default:
+        switch (gstate->source_fill.type)
+        {
+          case CTX_SOURCE_COLOR:
+            rasterizer->comp_op = ctx_GRAYAF_porter_duff_color;
+//          rasterizer->fragment = NULL;
+            break;
+          default:
+            rasterizer->comp_op = ctx_GRAYAF_porter_duff_generic;
+            break;
         }
+        break;
     }
+#endif
 }
 
+#endif
+#if CTX_ENABLE_GRAYF
 
-
-int vt_set_prop (VT *vt, uint32_t key_hash, const char *val)
+static void
+ctx_composite_GRAYF (CTX_COMPOSITE_ARGUMENTS)
 {
-#if 1
-  switch (key_hash)
-  {
-    case CTX_title:  
-     {
-       CtxClient *client = vt_get_client (vt);
-       if (client)
-       {
-         if (client->title) free (client->title);
-         client->title = strdup (val);
-       }
-     }
+  float *dstf = (float*)dst;
 
-     break;
+  float temp[count*2];
+  for (int i = 0; i < count; i++)
+  {
+    temp[i*2] = dstf[i];
+    temp[i*2+1] = 1.0f;
   }
-#else
-  float fval = strtod (val, NULL);
-  CtxClient *client = ctx_client_by_id (ct->id);
-  uint32_t val_hash = ctx_strhash (val);
-  if (!client)
-    return 0;
-
-  if (key_hash == ctx_strhash("start_move"))
+  rasterizer->comp_op (rasterizer, (uint8_t*)temp, rasterizer->color, x0, coverage, count);
+  for (int i = 0; i < count; i++)
   {
-    start_moving (client);
-    moving_client = 1;
-    return 0;
+    dstf[i] = temp[i*2];
   }
+}
 
-// set "pcm-hz"       "8000"
-// set "pcm-bits"     "8"
-// set "pcm-encoding" "ulaw"
-// set "play-pcm"     "d41ata312313"
-// set "play-pcm-ref" "foo.wav"
+#endif
+#if CTX_ENABLE_BGRA8
 
-// get "free"
-// storage of blobs for referencing when drawing or for playback
-// set "foo.wav"      "\3\1\1\4\"
-// set "fnord.png"    "PNG12.4a312"
+inline static void
+ctx_swap_red_green (uint8_t *rgba)
+{
+  uint32_t *buf  = (uint32_t *) rgba;
+  uint32_t  orig = *buf;
+  uint32_t  green_alpha = (orig & 0xff00ff00);
+  uint32_t  red_blue    = (orig & 0x00ff00ff);
+  uint32_t  red         = red_blue << 16;
+  uint32_t  blue        = red_blue >> 16;
+  *buf = green_alpha | red | blue;
+}
 
-  switch (key_hash)
-  {
-    case CTX_title:  ctx_client_set_title (ct->id, val); break;
-    case CTX_x:      client->x = fval; break;
-    case CTX_y:      client->y = fval; break;
-    case CTX_width:  ctx_client_resize (ct->id, fval, client->height); break;
-    case CTX_height: ctx_client_resize (ct->id, client->width, fval); break;
-    case CTX_action:
-      switch (val_hash)
-      {
-        case CTX_maximize:     ctx_client_maximize (client); break;
-        case CTX_unmaximize:   ctx_client_unmaximize (client); break;
-        case CTX_lower:        ctx_client_lower (client); break;
-        case CTX_lower_bottom: ctx_client_lower_bottom (client);  break;
-        case CTX_raise:        ctx_client_raise (client); break;
-        case CTX_raise_top:    ctx_client_raise_top (client); break;
-      }
-      break;
-  }
-  ct->rev++;
-#endif
-  return 0;
+static void
+ctx_BGRA8_to_RGBA8 (CtxRasterizer *rasterizer, int x, const void *buf, uint8_t *rgba, int count)
+{
+  uint32_t *srci = (uint32_t *) buf;
+  uint32_t *dsti = (uint32_t *) rgba;
+  while (count--)
+    {
+      uint32_t val = *srci++;
+      ctx_swap_red_green ( (uint8_t *) &val);
+      *dsti++      = val;
+    }
 }
 
-static float _ctx_font_size = 10.0;
+static void
+ctx_RGBA8_to_BGRA8 (CtxRasterizer *rasterizer, int x, const uint8_t *rgba, void *buf, int count)
+{
+  ctx_BGRA8_to_RGBA8 (rasterizer, x, rgba, (uint8_t *) buf, count);
+}
 
-CtxList *clients = NULL;
-CtxClient *active = NULL;
-CtxClient *active_tab = NULL;
+static void
+ctx_composite_BGRA8 (CTX_COMPOSITE_ARGUMENTS)
+{
+  // for better performance, this could be done without a pre/post conversion,
+  // by swapping R and B of source instead... as long as it is a color instead
+  // of gradient or image
+  //
+  //
+  uint8_t pixels[count * 4];
+  ctx_BGRA8_to_RGBA8 (rasterizer, x0, dst, &pixels[0], count);
+  rasterizer->comp_op (rasterizer, &pixels[0], rasterizer->color, x0, coverage, count);
+  ctx_BGRA8_to_RGBA8  (rasterizer, x0, &pixels[0], dst, count);
+}
 
-static CtxClient *ctx_client_by_id (int id);
 
-int ctx_client_resize (int id, int width, int height);
-void ctx_client_maximize (int id);
+#endif
+#if CTX_ENABLE_CMYKAF
 
-CtxClient *vt_get_client (VT *vt)
+static void
+ctx_fragment_other_CMYKAF (CtxRasterizer *rasterizer, float x, float y, void *out, int count, float dx, 
float dy)
 {
-  for (CtxList *l = clients; l; l =l->next)
+  float *cmyka = (float*)out;
+  float _rgba[4 * count];
+  float *rgba = &_rgba[0];
+  CtxGState *gstate = &rasterizer->state->gstate;
+  switch (gstate->source_fill.type)
+    {
+      case CTX_SOURCE_TEXTURE:
+        ctx_fragment_image_RGBAF (rasterizer, x, y, rgba, count, dx, dy);
+        break;
+      case CTX_SOURCE_COLOR:
+        ctx_fragment_color_RGBAF (rasterizer, x, y, rgba, count, dx, dy);
+        break;
+#if CTX_GRADIENTS
+      case CTX_SOURCE_LINEAR_GRADIENT:
+        ctx_fragment_linear_gradient_RGBAF (rasterizer, x, y, rgba, count, dx, dy);
+        break;
+      case CTX_SOURCE_RADIAL_GRADIENT:
+        ctx_fragment_radial_gradient_RGBAF (rasterizer, x, y, rgba, count, dx, dy);
+        break;
+#endif
+      default:
+        rgba[0]=rgba[1]=rgba[2]=rgba[3]=0.0f;
+        break;
+    }
+  for (int i = 0; i < count; i++)
   {
-    CtxClient *client = l->data;
-    if (client->vt == vt)
-            return client;
+    cmyka[4]=rgba[3];
+    ctx_rgb_to_cmyk (rgba[0], rgba[1], rgba[2], &cmyka[0], &cmyka[1], &cmyka[2], &cmyka[3]);
+    cmyka += 5;
+    rgba += 4;
   }
-  return NULL;
 }
 
-CtxClient *ctx_client_new (Ctx *ctx,
-                           const char *commandline,
-                           int x, int y, int width, int height,
-                           CtxClientFlags flags)
+static void
+ctx_fragment_color_CMYKAF (CtxRasterizer *rasterizer, float x, float y, void *out, int count, float dx, 
float dy)
 {
-  static int global_id = 0;
-  float font_size = ctx_get_font_size (ctx);
-  CtxClient *client = calloc (sizeof (CtxClient), 1);
-  ctx_list_append (&clients, client);
-  client->id = global_id++;
-  client->x = x;
-  client->y = y;
-  client->flags = flags;
-  client->ctx = ctx;
-  client->width = width;
-  client->height = height;
-
-  if (ctx_renderer_is_term (ctx))
+  CtxGState *gstate = &rasterizer->state->gstate;
+  float *cmyka = (float*)out;
+  float cmyka_in[5];
+  ctx_color_get_cmyka (rasterizer->state, &gstate->source_fill.color, cmyka_in);
+  for (int i = 0; i < count; i++)
   {
-    font_size = 3;
+    for (int c = 0; c < 4; c ++)
+    {
+      cmyka[c] = (1.0f - cmyka_in[c]);
+    }
+    cmyka[4] = cmyka_in[4];
+    cmyka += 5;
   }
-
-      //fprintf (stderr, "client new:%f\n", font_size);
-#if CTX_THREADS
-  mtx_init (&client->mtx, mtx_plain);
-#endif
-  float line_spacing = 2.0f;
-  client->vt = vt_new (commandline, width, height, font_size,line_spacing, client->id, (flags & 
ITK_CLIENT_CAN_LAUNCH)!=0);
-  vt_set_ctx (client->vt, ctx);
-  return client;
 }
 
-CtxClient *ctx_client_new_argv (Ctx *ctx, const char **argv, int x, int y, int width, int height, 
CtxClientFlags flags)
+static CtxFragment ctx_rasterizer_get_fragment_CMYKAF (CtxRasterizer *rasterizer)
 {
-  CtxString *string = ctx_string_new ("");
-  for (int i = 0; argv[i]; i++)
-  {
-    char space = ' ';
-    if (i > 0)
-      ctx_string_append_data (string, &space, 1);
-    for (int c = 0; argv[i][c]; c++)
+  CtxGState *gstate = &rasterizer->state->gstate;
+  switch (gstate->source_fill.type)
     {
-       switch (argv[i][c])
-       {
-         case '"':ctx_string_append_str (string, "\\\"");break;
-         case '\'':ctx_string_append_str (string, "\\\'");break;
-         default:ctx_string_append_data (string, &argv[i][c], 1);break;
-       }
+      case CTX_SOURCE_COLOR:
+        return ctx_fragment_color_CMYKAF;
     }
-  }
-  CtxClient *ret = ctx_client_new (ctx, string->str, x, y, width, height, flags);
-  ctx_string_free (string, 1);
-  return ret;
+  return ctx_fragment_other_CMYKAF;
 }
 
-extern float ctx_shape_cache_rate;
-extern int _ctx_max_threads;
-
-static int focus_follows_mouse = 0;
+ctx_float_porter_duff (CMYKAF, 5,color,           rasterizer->fragment, rasterizer->state->gstate.blend_mode)
+ctx_float_porter_duff (CMYKAF, 5,generic,         rasterizer->fragment, rasterizer->state->gstate.blend_mode)
 
-static CtxClient *find_active (int x, int y)
-{
-  CtxClient *ret = NULL;
-  float titlebar_height = _ctx_font_size;
-  int resize_border = titlebar_height/2;
+#if CTX_INLINED_NORMAL
 
-  for (CtxList *l = clients; l; l = l->next)
-  {
-     CtxClient *c = l->data;
-     if ((c->flags & ITK_CLIENT_MAXIMIZED) && c == active_tab)
-     if (x > c->x - resize_border && x < c->x+c->width + resize_border &&
-         y > c->y - titlebar_height && y < c->y+c->height + resize_border)
-     {
-       ret = c;
-     }
-  }
+ctx_float_porter_duff (CMYKAF, 5,color_normal,            rasterizer->fragment, CTX_BLEND_NORMAL)
+ctx_float_porter_duff (CMYKAF, 5,generic_normal,          rasterizer->fragment, CTX_BLEND_NORMAL)
 
-  for (CtxList *l = clients; l; l = l->next)
-  {
-     CtxClient *c = l->data;
-     if (!(c->flags &  ITK_CLIENT_MAXIMIZED))
-     if (x > c->x - resize_border && x < c->x+c->width + resize_border &&
-         y > c->y - titlebar_height && y < c->y+c->height + resize_border)
-     {
-       ret = c;
-     }
-  }
-  return ret;
+static void
+ctx_CMYKAF_copy_normal (CTX_COMPOSITE_ARGUMENTS)
+{
+  ctx_float_copy_normal (5, rasterizer, dst, src, x0, coverage, count);
 }
 
-int id_to_no (int id)
+static void
+ctx_CMYKAF_clear_normal (CTX_COMPOSITE_ARGUMENTS)
 {
-  CtxList *l;
-  int no = 0;
-
-  for (l = clients; l; l = l->next)
-  {
-    CtxClient *client = l->data;
-    if (client->id == id)
-      return no;
-    no++;
-  }
-  return -1;
+  ctx_float_clear_normal (5, rasterizer, dst, src, x0, coverage, count);
 }
 
-void ctx_client_move (int id, int x, int y);
-int ctx_client_resize (int id, int w, int h);
-void ctx_client_shade_toggle (int id);
-float ctx_client_min_y_pos (Ctx *ctx);
-float ctx_client_max_y_pos (Ctx *ctx);
+static void
+ctx_CMYKAF_source_over_normal_opaque_color (CTX_COMPOSITE_ARGUMENTS)
+{
+  ctx_float_source_over_normal_opaque_color (5, rasterizer, dst, rasterizer->color, x0, coverage, count);
+}
+#endif
 
-#if 0
-void ensure_layout ()
+static void
+ctx_setup_CMYKAF (CtxRasterizer *rasterizer)
 {
-  int n_clients = ctx_list_length (clients);
-  if (n_clients == 1)
-  {
-    CtxClient *client = clients->data;
-    if (client->flags & ITK_CLIENT_MAXIMIZED)
+  CtxGState *gstate = &rasterizer->state->gstate;
+  int components = 5;
+  rasterizer->fragment = ctx_rasterizer_get_fragment_CMYKAF (rasterizer);
+  if (gstate->source_fill.type == CTX_SOURCE_COLOR)
     {
-      ctx_client_move (client->id, 0, 0);
-      ctx_client_resize (client->id, ctx_width (ctx), ctx_height(ctx));
-      if (active_tab == NULL)
-        active_tab = client;
+      rasterizer->comp_op = ctx_CMYKAF_porter_duff_color;
+      rasterizer->comp_op = ctx_CMYKAF_porter_duff_generic;
+ //     rasterizer->fragment = NULL;
+      ctx_color_get_cmyka (rasterizer->state, &gstate->source_fill.color, (float*)rasterizer->color);
+      if (gstate->global_alpha_u8 != 255)
+        ((float*)rasterizer->color)[components-1] *= gstate->global_alpha_f;
     }
-  }
   else
-  for (CtxList *l = clients; l; l = l->next)
   {
-    CtxClient *client = l->data;
-    if (client->flags & ITK_CLIENT_MAXIMIZED)
+    rasterizer->comp_op = ctx_CMYKAF_porter_duff_generic;
+  }
+
+#if CTX_INLINED_NORMAL
+  if (gstate->compositing_mode == CTX_COMPOSITE_CLEAR)
+    rasterizer->comp_op = ctx_CMYKAF_clear_normal;
+#if 0
+  else
+    switch (gstate->blend_mode)
     {
-      ctx_client_move   (client->id, 0, client_min_y_pos (ctx));
-      ctx_client_resize (client->id, ctx_width (ctx), ctx_height(ctx) -
-                      ctx_client_min_y_pos (ctx) / 2);   // /2 to counter the double titlebar of 
non-maximized
-      if (active_tab == NULL)
-        active_tab = client;
+      case CTX_BLEND_NORMAL:
+        if (gstate->compositing_mode == CTX_COMPOSITE_COPY)
+        {
+          rasterizer->comp_op = ctx_CMYKAF_copy_normal;
+        }
+        else if (gstate->global_alpha_u8 == 0)
+          rasterizer->comp_op = CTX_COMPOSITE_SUFFIX(ctx_RGBA8_nop);
+        else
+        switch (gstate->source_fill.type)
+        {
+          case CTX_SOURCE_COLOR:
+            if (gstate->compositing_mode == CTX_COMPOSITE_SOURCE_OVER)
+            {
+              if (((float*)rasterizer->color)[components-1] == 0.0f)
+                rasterizer->comp_op = CTX_COMPOSITE_SUFFIX(ctx_RGBA8_nop);
+#if 0
+              else if (((float*)rasterizer->color)[components-1] == 1.0f)
+                rasterizer->comp_op = ctx_CMYKAF_source_over_normal_opaque_color;
+              else
+                rasterizer->comp_op = ctx_CMYKAF_porter_duff_color_normal;
+              rasterizer->fragment = NULL;
+#endif
+            }
+            else
+            {
+              rasterizer->comp_op = ctx_CMYKAF_porter_duff_color_normal;
+   //         rasterizer->fragment = NULL;
+            }
+            break;
+          default:
+            rasterizer->comp_op = ctx_CMYKAF_porter_duff_generic_normal;
+            break;
+        }
+        break;
+      default:
+        switch (gstate->source_fill.type)
+        {
+          case CTX_SOURCE_COLOR:
+            rasterizer->comp_op = ctx_CMYKAF_porter_duff_color;
+    //      rasterizer->fragment = NULL;
+            break;
+          default:
+            rasterizer->comp_op = ctx_CMYKAF_porter_duff_generic;
+            break;
+        }
+        break;
     }
-  }
+#endif
+#endif
 }
+
 #endif
+#if CTX_ENABLE_CMYKA8
 
-static CtxClient *ctx_client_by_id (int id)
+static void
+ctx_CMYKA8_to_CMYKAF (CtxRasterizer *rasterizer, uint8_t *src, float *dst, int count)
 {
-  for (CtxList *l = clients; l; l = l->next)
-  {
-    CtxClient *client = l->data;
-    if (client->id == id)
-      return client;
-  }
-  return NULL;
+  for (int i = 0; i < count; i ++)
+    {
+      for (int c = 0; c < 4; c ++)
+        { dst[c] = ctx_u8_to_float ( (255-src[c]) ); }
+      dst[4] = ctx_u8_to_float (src[4]);
+      for (int c = 0; c < 4; c++)
+        { dst[c] *= dst[4]; }
+      src += 5;
+      dst += 5;
+    }
 }
-
-void ctx_client_remove (Ctx *ctx, CtxClient *client)
+static void
+ctx_CMYKAF_to_CMYKA8 (CtxRasterizer *rasterizer, float *src, uint8_t *dst, int count)
 {
-  ctx_client_lock (client);
-  if (!client->internal)
-  {
-
-    if (client->vt)
-      vt_destroy (client->vt);
-  }
-
-  if (client->title)
-    free (client->title);
-
-#if VT_RECORD
-  if (client->recording)
-    ctx_free (client->recording);
-#endif
-
-  ctx_list_remove (&clients, client);
-
-  if (client == active_tab)
-  {
-    active_tab = NULL;
-  }
-
-  if (ctx)
-  if (client == active)
-  {
-    active = find_active (ctx_pointer_x (ctx), ctx_pointer_y (ctx));
-    if (!active) active = clients?clients->data:NULL;
-  }
+  for (int i = 0; i < count; i ++)
+    {
+      int a = ctx_float_to_u8 (src[4]);
+      if (a != 0 && a != 255)
+      {
+        float recip = 1.0f/src[4];
+        for (int c = 0; c < 4; c++)
+        {
+          dst[c] = ctx_float_to_u8 (1.0f - src[c] * recip);
+        }
+      }
+      else
+      {
+        for (int c = 0; c < 4; c++)
+          dst[c] = 255 - ctx_float_to_u8 (src[c]);
+      }
+      dst[4]=a;
 
-  ctx_client_unlock (client);
-  free (client);
-  //ensure_layout();
+      src += 5;
+      dst += 5;
+    }
 }
 
-#if 0
-void ctx_client_remove_by_id (int id)
+static void
+ctx_composite_CMYKA8 (CTX_COMPOSITE_ARGUMENTS)
 {
-  int no = id_to_no (id);
-  if (no>=0)
-    ctx_client_remove (no);
+  float pixels[count * 5];
+  ctx_CMYKA8_to_CMYKAF (rasterizer, dst, &pixels[0], count);
+  rasterizer->comp_op (rasterizer, (uint8_t *) &pixels[0], rasterizer->color, x0, coverage, count);
+  ctx_CMYKAF_to_CMYKA8 (rasterizer, &pixels[0], dst, count);
 }
+
 #endif
+#if CTX_ENABLE_CMYK8
 
-int ctx_client_height (int id)
+static void
+ctx_CMYK8_to_CMYKAF (CtxRasterizer *rasterizer, uint8_t *src, float *dst, int count)
 {
-  CtxClient *client = ctx_client_by_id (id);
-  if (!client) return 0;
-  return client->height;
+  for (int i = 0; i < count; i ++)
+    {
+      dst[0] = ctx_u8_to_float (255-src[0]);
+      dst[1] = ctx_u8_to_float (255-src[1]);
+      dst[2] = ctx_u8_to_float (255-src[2]);
+      dst[3] = ctx_u8_to_float (255-src[3]);
+      dst[4] = 1.0f;
+      src += 4;
+      dst += 5;
+    }
 }
-
-int ctx_client_x (int id)
+static void
+ctx_CMYKAF_to_CMYK8 (CtxRasterizer *rasterizer, float *src, uint8_t *dst, int count)
 {
-  CtxClient *client = ctx_client_by_id (id);
-  if (!client) return 0;
-  return client->x;
+  for (int i = 0; i < count; i ++)
+    {
+      float c = src[0];
+      float m = src[1];
+      float y = src[2];
+      float k = src[3];
+      float a = src[4];
+      if (a != 0.0f && a != 1.0f)
+        {
+          float recip = 1.0f/a;
+          c *= recip;
+          m *= recip;
+          y *= recip;
+          k *= recip;
+        }
+      c = 1.0 - c;
+      m = 1.0 - m;
+      y = 1.0 - y;
+      k = 1.0 - k;
+      dst[0] = ctx_float_to_u8 (c);
+      dst[1] = ctx_float_to_u8 (m);
+      dst[2] = ctx_float_to_u8 (y);
+      dst[3] = ctx_float_to_u8 (k);
+      src += 5;
+      dst += 4;
+    }
 }
 
-int ctx_client_y (int id)
+static void
+ctx_composite_CMYK8 (CTX_COMPOSITE_ARGUMENTS)
 {
-  CtxClient *client = ctx_client_by_id (id);
-  if (!client) return 0;
-  return client->y;
+  float pixels[count * 5];
+  ctx_CMYK8_to_CMYKAF (rasterizer, dst, &pixels[0], count);
+  rasterizer->comp_op (rasterizer, (uint8_t *) &pixels[0], src, x0, coverage, count);
+  ctx_CMYKAF_to_CMYK8 (rasterizer, &pixels[0], dst, count);
 }
+#endif
+
+#if CTX_ENABLE_RGB8
 
-void ctx_client_raise_top (int id)
+inline static void
+ctx_RGB8_to_RGBA8 (CtxRasterizer *rasterizer, int x, const void *buf, uint8_t *rgba, int count)
 {
-  CtxClient *client = ctx_client_by_id (id);
-  if (!client) return;
-  ctx_list_remove (&clients, client);
-  ctx_list_append (&clients, client);
+  const uint8_t *pixel = (const uint8_t *) buf;
+  while (count--)
+    {
+      rgba[0] = pixel[0];
+      rgba[1] = pixel[1];
+      rgba[2] = pixel[2];
+      rgba[3] = 255;
+      pixel+=3;
+      rgba +=4;
+    }
 }
 
-void ctx_client_lower_bottom (int id)
+inline static void
+ctx_RGBA8_to_RGB8 (CtxRasterizer *rasterizer, int x, const uint8_t *rgba, void *buf, int count)
 {
-  CtxClient *client = ctx_client_by_id (id);
-  if (!client) return;
-  ctx_list_remove (&clients, client);
-  ctx_list_prepend (&clients, client);
+  uint8_t *pixel = (uint8_t *) buf;
+  while (count--)
+    {
+      pixel[0] = rgba[0];
+      pixel[1] = rgba[1];
+      pixel[2] = rgba[2];
+      pixel+=3;
+      rgba +=4;
+    }
 }
 
+#endif
+#if CTX_ENABLE_GRAY1
 
-void ctx_client_iconify (int id)
+#if CTX_NATIVE_GRAYA8
+inline static void
+ctx_GRAY1_to_GRAYA8 (CtxRasterizer *rasterizer, int x, const void *buf, uint8_t *rgba, int count)
 {
-   CtxClient *client = ctx_client_by_id (id);
-   if (!client) return;
-   client->flags |= ITK_CLIENT_ICONIFIED;
+  const uint8_t *pixel = (uint8_t *) buf;
+  while (count--)
+    {
+      if (*pixel & (1<< (x&7) ) )
+        {
+          rgba[0] = 255;
+          rgba[1] = 255;
+        }
+      else
+        {
+          rgba[0] = 0;
+          rgba[1] = 255;
+        }
+      if ( (x&7) ==7)
+        { pixel+=1; }
+      x++;
+      rgba +=2;
+    }
 }
 
-int ctx_client_is_iconified (int id)
+inline static void
+ctx_GRAYA8_to_GRAY1 (CtxRasterizer *rasterizer, int x, const uint8_t *rgba, void *buf, int count)
 {
-   CtxClient *client = ctx_client_by_id (id);
-   if (!client) return -1;
-   return (client->flags & ITK_CLIENT_ICONIFIED) != 0;
+  uint8_t *pixel = (uint8_t *) buf;
+  while (count--)
+    {
+      int gray = rgba[0];
+      //gray += ctx_dither_mask_a (x, rasterizer->scanline/aa, 0, 127);
+      if (gray < 127)
+        {
+          *pixel = *pixel & (~ (1<< (x&7) ) );
+        }
+      else
+        {
+          *pixel = *pixel | (1<< (x&7) );
+        }
+      if ( (x&7) ==7)
+        { pixel+=1; }
+      x++;
+      rgba +=2;
+    }
 }
 
-void ctx_client_uniconify (int id)
+#else
+
+inline static void
+ctx_GRAY1_to_RGBA8 (CtxRasterizer *rasterizer, int x, const void *buf, uint8_t *rgba, int count)
 {
-   CtxClient *client = ctx_client_by_id (id);
-   if (!client) return;
-   client->flags &= ~ITK_CLIENT_ICONIFIED;
+  const uint8_t *pixel = (uint8_t *) buf;
+  while (count--)
+    {
+      if (*pixel & (1<< (x&7) ) )
+        {
+          rgba[0] = 255;
+          rgba[1] = 255;
+          rgba[2] = 255;
+          rgba[3] = 255;
+        }
+      else
+        {
+          rgba[0] = 0;
+          rgba[1] = 0;
+          rgba[2] = 0;
+          rgba[3] = 255;
+        }
+      if ( (x&7) ==7)
+        { pixel+=1; }
+      x++;
+      rgba +=4;
+    }
 }
 
-void ctx_client_maximize (int id)
+inline static void
+ctx_RGBA8_to_GRAY1 (CtxRasterizer *rasterizer, int x, const uint8_t *rgba, void *buf, int count)
 {
-   CtxClient *client = ctx_client_by_id (id);
-   if (!client) return;
-   if (client->flags &  ITK_CLIENT_MAXIMIZED)
-     return;
-   client->flags |= ITK_CLIENT_MAXIMIZED;
-   client->unmaximized_x = client->x;
-   client->unmaximized_y = client->y;
-   client->unmaximized_width  = client->width;
-   client->unmaximized_height = client->height;
+  uint8_t *pixel = (uint8_t *) buf;
+  while (count--)
+    {
+      int gray = ctx_u8_color_rgb_to_gray (rasterizer->state, rgba);
+      //gray += ctx_dither_mask_a (x, rasterizer->scanline/aa, 0, 127);
+      if (gray < 127)
+        {
+          *pixel = *pixel & (~ (1<< (x&7) ) );
+        }
+      else
+        {
+          *pixel = *pixel | (1<< (x&7) );
+        }
+      if ( (x&7) ==7)
+        { pixel+=1; }
+      x++;
+      rgba +=4;
+    }
+}
+#endif
+
+#endif
+#if CTX_ENABLE_GRAY2
 
-   // enforce_layout does the size
-   //client_resize (id, ctx_width (ctx), ctx_height(ctx) - ctx_client_min_y_pos (ctx));
-   
-   ctx_client_move (id, 0, ctx_client_min_y_pos (client->ctx));
-   active_tab = client;
+#if CTX_NATIVE_GRAYA8
+inline static void
+ctx_GRAY2_to_GRAYA8 (CtxRasterizer *rasterizer, int x, const void *buf, uint8_t *rgba, int count)
+{
+  const uint8_t *pixel = (uint8_t *) buf;
+  while (count--)
+    {
+      int val = (*pixel & (3 << ( (x & 3) <<1) ) ) >> ( (x&3) <<1);
+      val <<= 6;
+      rgba[0] = val;
+      rgba[1] = 255;
+      if ( (x&3) ==3)
+        { pixel+=1; }
+      x++;
+      rgba +=2;
+    }
 }
 
-int ctx_client_is_maximized (int id)
+inline static void
+ctx_GRAYA8_to_GRAY2 (CtxRasterizer *rasterizer, int x, const uint8_t *rgba, void *buf, int count)
 {
-   CtxClient *client = ctx_client_by_id (id);
-   if (!client) return -1;
-   return (client->flags & ITK_CLIENT_MAXIMIZED) != 0;
+  uint8_t *pixel = (uint8_t *) buf;
+  while (count--)
+    {
+      int val = rgba[0];
+      val >>= 6;
+      *pixel = *pixel & (~ (3 << ( (x&3) <<1) ) );
+      *pixel = *pixel | ( (val << ( (x&3) <<1) ) );
+      if ( (x&3) ==3)
+        { pixel+=1; }
+      x++;
+      rgba +=2;
+    }
 }
+#else
 
-void ctx_client_unmaximize (int id)
+inline static void
+ctx_GRAY2_to_RGBA8 (CtxRasterizer *rasterizer, int x, const void *buf, uint8_t *rgba, int count)
 {
-   CtxClient *client = ctx_client_by_id (id);
-   if (!client) return;
-   if ((client->flags & ITK_CLIENT_MAXIMIZED) == 0)
-     return;
-   client->flags &= ~ITK_CLIENT_MAXIMIZED;
-   ctx_client_resize (id, client->unmaximized_width, client->unmaximized_height);
-   ctx_client_move (id, client->unmaximized_x, client->unmaximized_y);
-   active_tab = NULL;
+  const uint8_t *pixel = (uint8_t *) buf;
+  while (count--)
+    {
+      int val = (*pixel & (3 << ( (x & 3) <<1) ) ) >> ( (x&3) <<1);
+      val <<= 6;
+      rgba[0] = val;
+      rgba[1] = val;
+      rgba[2] = val;
+      rgba[3] = 255;
+      if ( (x&3) ==3)
+        { pixel+=1; }
+      x++;
+      rgba +=4;
+    }
 }
 
-void ctx_client_maximized_toggle (int id)
+inline static void
+ctx_RGBA8_to_GRAY2 (CtxRasterizer *rasterizer, int x, const uint8_t *rgba, void *buf, int count)
 {
-  if (ctx_client_is_maximized (id))
-    ctx_client_unmaximize (id);
-  else
-    ctx_client_maximize (id);
+  uint8_t *pixel = (uint8_t *) buf;
+  while (count--)
+    {
+      int val = ctx_u8_color_rgb_to_gray (rasterizer->state, rgba);
+      val >>= 6;
+      *pixel = *pixel & (~ (3 << ( (x&3) <<1) ) );
+      *pixel = *pixel | ( (val << ( (x&3) <<1) ) );
+      if ( (x&3) ==3)
+        { pixel+=1; }
+      x++;
+      rgba +=4;
+    }
 }
+#endif
 
+#endif
+#if CTX_ENABLE_GRAY4
 
-void ctx_client_shade (int id)
+#if CTX_NATIVE_GRAYA8
+inline static void
+ctx_GRAY4_to_GRAYA8 (CtxRasterizer *rasterizer, int x, const void *buf, uint8_t *rgba, int count)
 {
-   CtxClient *client = ctx_client_by_id (id);
-   if (!client) return;
-   client->flags |= ITK_CLIENT_SHADED;
+  const uint8_t *pixel = (uint8_t *) buf;
+  while (count--)
+    {
+      int val = (*pixel & (15 << ( (x & 1) <<2) ) ) >> ( (x&1) <<2);
+      val <<= 4;
+      rgba[0] = val;
+      rgba[1] = 255;
+      if ( (x&1) ==1)
+        { pixel+=1; }
+      x++;
+      rgba +=2;
+    }
 }
 
-int ctx_client_is_shaded (int id)
+inline static void
+ctx_GRAYA8_to_GRAY4 (CtxRasterizer *rasterizer, int x, const uint8_t *rgba, void *buf, int count)
 {
-   CtxClient *client = ctx_client_by_id (id);
-   if (!client) return -1;
-   return (client->flags & ITK_CLIENT_SHADED) != 0;
+  uint8_t *pixel = (uint8_t *) buf;
+  while (count--)
+    {
+      int val = rgba[0];
+      val >>= 4;
+      *pixel = *pixel & (~ (15 << ( (x&1) <<2) ) );
+      *pixel = *pixel | ( (val << ( (x&1) <<2) ) );
+      if ( (x&1) ==1)
+        { pixel+=1; }
+      x++;
+      rgba +=2;
+    }
 }
-
-void ctx_client_unshade (int id)
+#else
+inline static void
+ctx_GRAY4_to_RGBA8 (CtxRasterizer *rasterizer, int x, const void *buf, uint8_t *rgba, int count)
 {
-   CtxClient *client = ctx_client_by_id (id);
-   if (!client) return;
-   client->flags &= ~ITK_CLIENT_SHADED;
+  const uint8_t *pixel = (uint8_t *) buf;
+  while (count--)
+    {
+      int val = (*pixel & (15 << ( (x & 1) <<2) ) ) >> ( (x&1) <<2);
+      val <<= 4;
+      rgba[0] = val;
+      rgba[1] = val;
+      rgba[2] = val;
+      rgba[3] = 255;
+      if ( (x&1) ==1)
+        { pixel+=1; }
+      x++;
+      rgba +=4;
+    }
 }
 
-void ctx_client_toggle_maximized (int id)
+inline static void
+ctx_RGBA8_to_GRAY4 (CtxRasterizer *rasterizer, int x, const uint8_t *rgba, void *buf, int count)
 {
-   CtxClient *client = ctx_client_by_id (id);
-   if (!client) return;
-   if (ctx_client_is_maximized (id))
-     ctx_client_unmaximize (id);
-   else
-     ctx_client_maximize (id);
+  uint8_t *pixel = (uint8_t *) buf;
+  while (count--)
+    {
+      int val = ctx_u8_color_rgb_to_gray (rasterizer->state, rgba);
+      val >>= 4;
+      *pixel = *pixel & (~ (15 << ( (x&1) <<2) ) );
+      *pixel = *pixel | ( (val << ( (x&1) <<2) ) );
+      if ( (x&1) ==1)
+        { pixel+=1; }
+      x++;
+      rgba +=4;
+    }
 }
+#endif
+
+#endif
+#if CTX_ENABLE_GRAY8
 
-void ctx_client_shade_toggle (int id)
+#if CTX_NATIVE_GRAYA8
+inline static void
+ctx_GRAY8_to_GRAYA8 (CtxRasterizer *rasterizer, int x, const void *buf, uint8_t *rgba, int count)
 {
-   CtxClient *client = ctx_client_by_id (id);
-   if (!client) return;
-   if (ctx_client_is_shaded (id))
-    ctx_client_shade (id);
-   else
-    ctx_client_unshade (id);
+  const uint8_t *pixel = (uint8_t *) buf;
+  while (count--)
+    {
+      rgba[0] = pixel[0];
+      rgba[1] = 255;
+      pixel+=1;
+      rgba +=2;
+    }
 }
 
-void ctx_client_move (int id, int x, int y)
+inline static void
+ctx_GRAYA8_to_GRAY8 (CtxRasterizer *rasterizer, int x, const uint8_t *rgba, void *buf, int count)
 {
-   CtxClient *client = ctx_client_by_id (id);
-   if (client && (client->x != x || client->y != y))
-   {
-     client->x = x;
-     client->y = y;
-     vt_rev_inc (client->vt);
-   }
+  uint8_t *pixel = (uint8_t *) buf;
+  while (count--)
+    {
+      pixel[0] = rgba[0];
+      pixel+=1;
+      rgba +=2;
+    }
 }
-
-int ctx_client_resize (int id, int width, int height)
+#else
+inline static void
+ctx_GRAY8_to_RGBA8 (CtxRasterizer *rasterizer, int x, const void *buf, uint8_t *rgba, int count)
 {
-   CtxClient *client = ctx_client_by_id (id);
-
-   if (client && ((height != client->height) || (width != client->width) ))
-   {
-     client->width  = width;
-     client->height = height;
-     if (client->vt)
-       vt_set_px_size (client->vt, width, height);
-     return 1;
-   }
-   return 0;
+  const uint8_t *pixel = (uint8_t *) buf;
+  while (count--)
+    {
+      rgba[0] = pixel[0];
+      rgba[1] = pixel[0];
+      rgba[2] = pixel[0];
+      rgba[3] = 255;
+      pixel+=1;
+      rgba +=4;
+    }
 }
 
-static void ctx_client_titlebar_drag (CtxEvent *event, void *data, void *data2)
+inline static void
+ctx_RGBA8_to_GRAY8 (CtxRasterizer *rasterizer, int x, const uint8_t *rgba, void *buf, int count)
 {
-  CtxClient *client = data;
-
-  if (event->type == CTX_DRAG_RELEASE)
-  {
-    static int prev_drag_end_time = 0;
-    if (event->time - prev_drag_end_time < 500)
+  uint8_t *pixel = (uint8_t *) buf;
+  for (int i = 0; i < count; i ++)
     {
-      //client_shade_toggle (client->id);
-      ctx_client_maximized_toggle (client->id);
+      pixel[i] = ctx_u8_color_rgb_to_gray (rasterizer->state, rgba + i * 4);
     }
-    prev_drag_end_time = event->time;
-  }
-
-  float new_x = client->x +  event->delta_x;
-  float new_y = client->y +  event->delta_y;
-
-  float snap_threshold = 8;
-
-  if (ctx_renderer_is_term (event->ctx))
-     snap_threshold = 1;
-
-  if (new_y < ctx_client_min_y_pos (event->ctx)) new_y = ctx_client_min_y_pos (event->ctx);
-  if (new_y > ctx_client_max_y_pos (event->ctx)) new_y = ctx_client_max_y_pos (event->ctx);
-
-  if (fabs (new_x - 0) < snap_threshold) new_x = 0.0;
-  if (fabs (ctx_width (event->ctx) - (new_x + client->width)) < snap_threshold) new_x = ctx_width 
(event->ctx) - client->width;
-
-  ctx_client_move (client->id, new_x, new_y);
-
-  //vt_rev_inc (client->vt);
-  ctx_set_dirty (event->ctx, 1);
-
-  event->stop_propagate = 1;
 }
+#endif
 
-static float min_win_dim = 32;
+#endif
+#if CTX_ENABLE_GRAYA8
 
-static void ctx_client_resize_se (CtxEvent *event, void *data, void *data2)
+inline static void
+ctx_GRAYA8_to_RGBA8 (CtxRasterizer *rasterizer, int x, const void *buf, uint8_t *rgba, int count)
 {
-  CtxClient *client = data;
-  int new_w = client->width + event->delta_x;
-  int new_h = client->height + event->delta_y;
-  if (new_w <= min_win_dim) new_w = min_win_dim;
-  if (new_h <= min_win_dim) new_h = min_win_dim;
-  ctx_client_resize (client->id, new_w, new_h);
-  if (client->vt) // force redraw
-    vt_rev_inc (client->vt);
-  ctx_set_dirty (event->ctx, 1);
-  event->stop_propagate = 1;
+  const uint8_t *pixel = (const uint8_t *) buf;
+  while (count--)
+    {
+      rgba[0] = pixel[0];
+      rgba[1] = pixel[0];
+      rgba[2] = pixel[0];
+      rgba[3] = pixel[1];
+      pixel+=2;
+      rgba +=4;
+    }
 }
 
-static void ctx_client_resize_e (CtxEvent *event, void *data, void *data2)
+inline static void
+ctx_RGBA8_to_GRAYA8 (CtxRasterizer *rasterizer, int x, const uint8_t *rgba, void *buf, int count)
 {
-  CtxClient *client = data;
-  int new_w = client->width + event->delta_x;
-  if (new_w <= min_win_dim) new_w = min_win_dim;
-  ctx_client_resize (client->id, new_w, client->height);
-  if (client->vt) // force redraw
-    vt_rev_inc (client->vt);
-  ctx_set_dirty (event->ctx, 1);
-  event->stop_propagate = 1;
+  uint8_t *pixel = (uint8_t *) buf;
+  while (count--)
+    {
+      pixel[0] = ctx_u8_color_rgb_to_gray (rasterizer->state, rgba);
+      pixel[1] = rgba[3];
+      pixel+=2;
+      rgba +=4;
+    }
 }
 
-static void ctx_client_resize_s (CtxEvent *event, void *data, void *data2)
+#if CTX_NATIVE_GRAYA8
+CTX_INLINE static void ctx_rgba_to_graya_u8 (CtxState *state, uint8_t *in, uint8_t *out)
 {
-  CtxClient *client = data;
-  int new_h = client->height + event->delta_y;
-  if (new_h <= min_win_dim) new_h = min_win_dim;
-  ctx_client_resize (client->id, client->width, new_h);
-  if (client->vt) // force redraw
-    vt_rev_inc (client->vt);
-  ctx_set_dirty (event->ctx, 1);
-  event->stop_propagate = 1;
+  out[0] = ctx_u8_color_rgb_to_gray (state, in);
+  out[1] = in[3];
 }
 
-static void ctx_client_resize_n (CtxEvent *event, void *data, void *data2)
+#if CTX_GRADIENTS
+static void
+ctx_fragment_linear_gradient_GRAYA8 (CtxRasterizer *rasterizer, float x, float y, void *out, int count, 
float dx, float dy)
 {
-  CtxClient *client = data;
-  float new_y = client->y +  event->delta_y;
-  int new_h = client->height - event->delta_y;
-  if (new_h <= min_win_dim) new_h = min_win_dim;
-  ctx_client_resize (client->id, client->width, new_h);
-  ctx_client_move (client->id, client->x, new_y);
-  if (client->vt) // force redraw
-    vt_rev_inc (client->vt);
-  ctx_set_dirty (event->ctx, 1);
-  event->stop_propagate = 1;
+  CtxSource *g = &rasterizer->state->gstate.source_fill;
+  float v = ( ( (g->linear_gradient.dx * x + g->linear_gradient.dy * y) /
+                g->linear_gradient.length) -
+              g->linear_gradient.start) * (g->linear_gradient.rdelta);
+  ctx_fragment_gradient_1d_GRAYA8 (rasterizer, v, 1.0, (uint8_t*)out);
+#if CTX_DITHER
+  ctx_dither_graya_u8 ((uint8_t*)out, x, y, rasterizer->format->dither_red_blue,
+                      rasterizer->format->dither_green);
+#endif
 }
 
-static void ctx_client_resize_ne (CtxEvent *event, void *data, void *data2)
+#if 0
+static void
+ctx_fragment_radial_gradient_RGBA8 (CtxRasterizer *rasterizer, float x, float y, void *out)
 {
-  CtxClient *client = data;
-  float new_y = client->y +  event->delta_y;
-  int new_h = client->height - event->delta_y;
-  int new_w = client->width + event->delta_x;
-  if (new_h <= min_win_dim) new_h = min_win_dim;
-  if (new_w <= min_win_dim) new_w = min_win_dim;
-  ctx_client_resize (client->id, new_w, new_h);
-  ctx_client_move (client->id, client->x, new_y);
-  if (client->vt) // force redraw
-    vt_rev_inc (client->vt);
-  ctx_set_dirty (event->ctx, 1);
-  event->stop_propagate = 1;
+  uint8_t *rgba = (uint8_t *) out;
+  CtxSource *g = &rasterizer->state->gstate.source_fill;
+  float v = (ctx_hypotf (g->radial_gradient.x0 - x, g->radial_gradient.y0 - y) -
+              g->radial_gradient.r0) * (g->radial_gradient.rdelta);
+  ctx_fragment_gradient_1d_RGBA8 (rasterizer, v, 0.0, rgba);
+#if CTX_DITHER
+  ctx_dither_rgba_u8 (rgba, x, y, rasterizer->format->dither_red_blue,
+                      rasterizer->format->dither_green);
+#endif
 }
+#endif
 
-static void ctx_client_resize_sw (CtxEvent *event, void *data, void *data2)
-{
-  CtxClient *client = data;
-
-  float new_x = client->x +  event->delta_x;
-  int new_w = client->width - event->delta_x;
-  int new_h = client->height + event->delta_y;
 
-  if (new_h <= min_win_dim) new_h = min_win_dim;
-  if (new_w <= min_win_dim) new_w = min_win_dim;
-  ctx_client_resize (client->id, new_w, new_h);
-  ctx_client_move (client->id, new_x, client->y);
-  if (client->vt) // force redraw
-    vt_rev_inc (client->vt);
-  ctx_set_dirty (event->ctx, 1);
-  event->stop_propagate = 1;
+static void
+ctx_fragment_radial_gradient_GRAYA8 (CtxRasterizer *rasterizer, float x, float y, void *out, int count, 
float dx, float dy)
+{
+  CtxSource *g = &rasterizer->state->gstate.source_fill;
+  float v = (ctx_hypotf (g->radial_gradient.x0 - x, g->radial_gradient.y0 - y) -
+              g->radial_gradient.r0) * (g->radial_gradient.rdelta);
+  ctx_fragment_gradient_1d_RGBA8 (rasterizer, v, 0.0, (uint8_t*)out);
+#if CTX_DITHER
+  ctx_dither_graya_u8 ((uint8_t*)out, x, y, rasterizer->format->dither_red_blue,
+                      rasterizer->format->dither_green);
+#endif
 }
+#endif
 
-static void ctx_client_resize_nw (CtxEvent *event, void *data, void *data2)
+static void
+ctx_fragment_color_GRAYA8 (CtxRasterizer *rasterizer, float x, float y, void *out, int count, float dx, 
float dy)
 {
-  CtxClient *client = data;
-  float new_x = client->x +  event->delta_x;
-  float new_y = client->y +  event->delta_y;
-  int new_w = client->width - event->delta_x;
-  int new_h = client->height - event->delta_y;
-  if (new_h <= min_win_dim) new_h = min_win_dim;
-  if (new_w <= min_win_dim) new_w = min_win_dim;
-  ctx_client_resize (client->id, new_w, new_h);
-  ctx_client_move (client->id, new_x, new_y);
-  if (client->vt) // force redraw
-    vt_rev_inc (client->vt);
-  ctx_set_dirty (event->ctx, 1);
-  event->stop_propagate = 1;
-}
-
-static void ctx_client_resize_w (CtxEvent *event, void *data, void *data2)
-{
-  CtxClient *client = data;
-
-  float new_x = client->x +  event->delta_x;
-  int new_w = client->width - event->delta_x;
-  if (new_w <= min_win_dim) new_w = min_win_dim;
-  ctx_client_resize (client->id, new_w, client->height);
-  ctx_client_move (client->id, new_x, client->y);
-  if (client->vt) // force redraw
-    vt_rev_inc (client->vt);
-  ctx_set_dirty (event->ctx, 1);
-
-  event->stop_propagate = 1;
-}
+  CtxSource *g = &rasterizer->state->gstate.source_fill;
+  ctx_color_get_graya_u8 (rasterizer->state, &g->color, out);
+}
 
-static void ctx_client_close (CtxEvent *event, void *data, void *data2)
+static void ctx_fragment_image_GRAYA8 (CtxRasterizer *rasterizer, float x, float y, void *out, int count, 
float dx, float dy)
 {
-  //Ctx *ctx = event->ctx;
-  CtxClient *client = data;
-
- // client->do_quit = 1;
-  
-  ctx_client_remove (event->ctx, client);
-
-  ctx_set_dirty (event->ctx, 1);
-  event->stop_propagate = 1;
+  uint8_t rgba[4];
+  CtxGState *gstate = &rasterizer->state->gstate;
+  CtxBuffer *buffer = gstate->source_fill.texture.buffer;
+  switch (buffer->format->bpp)
+    {
+      case 1:  ctx_fragment_image_gray1_RGBA8 (rasterizer, x, y, rgba, count, dx, dy); break;
+      case 24: ctx_fragment_image_rgb8_RGBA8 (rasterizer, x, y, rgba, count, dx, dy);  break;
+      case 32: ctx_fragment_image_rgba8_RGBA8 (rasterizer, x, y, rgba, count, dx, dy); break;
+      default: ctx_fragment_image_RGBA8 (rasterizer, x, y, rgba, count, dx, dy);       break;
+    }
+  ctx_rgba_to_graya_u8 (rasterizer->state, rgba, (uint8_t*)out);
 }
 
-/********************/
-void vt_use_images (VT *vt, Ctx *ctx);
-float _ctx_green = 0.5;
-
-static void ctx_client_draw (Ctx *ctx, CtxClient *client, float x, float y)
+static CtxFragment ctx_rasterizer_get_fragment_GRAYA8 (CtxRasterizer *rasterizer)
 {
-    if (client->internal)
+  CtxGState *gstate = &rasterizer->state->gstate;
+  switch (gstate->source_fill.type)
     {
-#if 0
-      ctx_save (ctx);
-
-      ctx_translate (ctx, x, y);
-      int width = client->width;
-      int height = client->height;
-
-      itk_panel_start (itk, "", 0, 0, width, height);
-      //itk_seperator (itk);
-#if 0
-      if (itk_button (itk, "add tab"))
-      {
-        add_tab (vt_find_shell_command(), 1);
-      }
-#endif
-      //itk_sameline (itk);
-      //itk_toggle (itk, "on screen keyboard", &on_screen_keyboard);
-      //itk_toggle (itk, "focus follows mouse", &focus_follows_mouse);
-      itk_slider_float (itk, "CTX_GREEN", &_ctx_green, 0.0, 1.0, 0.5);
-      itk_ctx_settings (itk);
-      itk_itk_settings (itk);
-
-      itk_panel_end (itk);
-      itk_done (itk);
-      //itk_key_bindings (itk);
-
-      ctx_restore (ctx);
+      case CTX_SOURCE_TEXTURE:           return ctx_fragment_image_GRAYA8;
+#if CTX_GRADIENTS
+      case CTX_SOURCE_COLOR:           return ctx_fragment_color_GRAYA8;
+      case CTX_SOURCE_LINEAR_GRADIENT: return ctx_fragment_linear_gradient_GRAYA8;
+      case CTX_SOURCE_RADIAL_GRADIENT: return ctx_fragment_radial_gradient_GRAYA8;
 #endif
     }
-    else
-    {
-       ctx_client_lock (client);
+  return ctx_fragment_color_GRAYA8;
+}
 
-          int found = 0;
-          for (CtxList *l2 = clients; l2; l2 = l2->next)
-            if (l2->data == client) found = 1;
-          if (found)
-          {
+ctx_u8_porter_duff(GRAYA8, 2,color,   rasterizer->fragment, rasterizer->state->gstate.blend_mode)
+ctx_u8_porter_duff(GRAYA8, 2,generic, rasterizer->fragment, rasterizer->state->gstate.blend_mode)
 
-      int rev = vt_rev (client->vt);
-#if VT_RECORD
-      if (client->drawn_rev != rev)
-      {
-        if (!client->recording)
-          client->recording = ctx_new ();
-        else
-          ctx_reset (client->recording);
-        vt_draw (client->vt, client->recording, 0.0, 0.0);
-      }
+#if CTX_INLINED_NORMAL
 
-      if (client->recording)
-      {
-        ctx_save (ctx);
-        ctx_translate (ctx, x, y);
-        ctx_render_ctx (client->recording, ctx);
-        vt_register_events (client->vt, ctx, 0.0, 0.0);
-        ctx_restore (ctx);
-      }
-#else
+ctx_u8_porter_duff(GRAYA8, 2,color_normal,   rasterizer->fragment, CTX_BLEND_NORMAL)
+ctx_u8_porter_duff(GRAYA8, 2,generic_normal, rasterizer->fragment, CTX_BLEND_NORMAL)
 
-      vt_draw (client->vt, ctx, x, y);
-      vt_register_events (client->vt, ctx, x, y);
-#endif
-      client->drawn_rev = rev;
-      ctx_client_unlock (client);
-          }
-    }
+static void
+ctx_GRAYA8_copy_normal (CTX_COMPOSITE_ARGUMENTS)
+{
+  ctx_u8_copy_normal (2, rasterizer, dst, src, x0, coverage, count);
 }
 
-static void ctx_client_use_images (Ctx *ctx, CtxClient *client)
+static void
+ctx_GRAYA8_clear_normal (CTX_COMPOSITE_ARGUMENTS)
 {
-  if (!client->internal)
-  {
-      uint32_t rev = vt_rev (client->vt);
-#if VT_RECORD
-      if (client->drawn_rev != rev)
-      {
-        if (!client->recording)
-          client->recording = ctx_new ();
-        else
-          ctx_reset (client->recording);
-        vt_draw (client->vt, client->recording, 0.0, 0.0);
-      }
-
-      if (client->recording)
-      {
-        ctx_save (ctx);
-        ctx_render_ctx_textures (client->recording, ctx);
-        ctx_restore (ctx);
-      }
-#else
-    vt_use_images (client->vt, ctx);
-#endif
-    client->drawn_rev = rev;
-  }
+  ctx_u8_clear_normal (2, rasterizer, dst, src, x0, coverage, count);
 }
 
-void ctx_client_lock (CtxClient *client)
+static void
+CTX_COMPOSITE_SUFFIX (ctx_GRAYA8_source_over_normal_color) (CTX_COMPOSITE_ARGUMENTS)
 {
-#if CTX_THREADS
-    mtx_lock (&client->mtx);
-#endif
+  ctx_u8_source_over_normal_color (2, rasterizer, dst, rasterizer->color, x0, coverage, count);
 }
 
-void ctx_client_unlock (CtxClient *client)
+static void
+CTX_COMPOSITE_SUFFIX (ctx_GRAYA8_source_over_normal_opaque_color) (CTX_COMPOSITE_ARGUMENTS)
 {
-#if CTX_THREADS
-    mtx_unlock (&client->mtx);
-#endif
+  ctx_u8_source_over_normal_opaque_color (2, rasterizer, dst, rasterizer->color, x0, coverage, count);
 }
+#endif
 
-#if 0
-void ctx_client_handle_event (Ctx *ctx, CtxEvent *ctx_event, const char *event)
+inline static int
+ctx_is_opaque_color (CtxRasterizer *rasterizer)
 {
-  if (!active)
-    return;
-  if (active->internal)
-    return;
-  VT *vt = active->vt;
-  CtxClient *client = vt_get_client (vt);
-
-  ctx_client_lock (client);
+  CtxGState *gstate = &rasterizer->state->gstate;
+  if (gstate->global_alpha_u8 != 255)
+    return 0;
+  if (gstate->source_fill.type == CTX_SOURCE_COLOR)
+  {
+    uint8_t ga[2];
+    ctx_color_get_graya_u8 (rasterizer->state, &gstate->source_fill.color, ga);
+    return ga[1] == 255;
+  }
+  return 0;
+}
 
-  if (!strcmp (event, "F11"))
-  {
-#ifndef NO_SDL
-    if (ctx_renderer_is_sdl (ctx))
+static void
+ctx_setup_GRAYA8 (CtxRasterizer *rasterizer)
+{
+  CtxGState *gstate = &rasterizer->state->gstate;
+  int components = 2;
+  if (gstate->source_fill.type == CTX_SOURCE_COLOR)
     {
-      ctx_sdl_set_fullscreen (ctx, !ctx_sdl_get_fullscreen (ctx));
+      rasterizer->comp_op = CTX_COMPOSITE_SUFFIX(ctx_GRAYA8_porter_duff_color);
+      rasterizer->fragment = NULL;
+      ctx_color_get_rgba8 (rasterizer->state, &gstate->source_fill.color, rasterizer->color);
+      if (gstate->global_alpha_u8 != 255)
+        for (int c = 0; c < components; c ++)
+          rasterizer->color[c] = (rasterizer->color[c] * gstate->global_alpha_u8)/255;
+      rasterizer->color[0] = ctx_u8_color_rgb_to_gray (rasterizer->state, rasterizer->color);
+      rasterizer->color[1] = rasterizer->color[3];
     }
-#endif
-  }
-  else if (!strcmp (event, "shift-return"))
+  else
   {
-    vt_feed_keystring (vt, ctx_event, "return");
+    rasterizer->fragment = ctx_rasterizer_get_fragment_GRAYA8 (rasterizer);
+    rasterizer->comp_op  = CTX_COMPOSITE_SUFFIX(ctx_GRAYA8_porter_duff_generic);
   }
-  else if (!strcmp (event, "shift-control-v") )
+
+#if CTX_INLINED_NORMAL
+  if (gstate->compositing_mode == CTX_COMPOSITE_CLEAR)
+    rasterizer->comp_op = ctx_GRAYA8_clear_normal;
+  else
+    switch (gstate->blend_mode)
     {
-      char *text = ctx_get_clipboard (ctx);
-      if (text)
+      case CTX_BLEND_NORMAL:
+        if (gstate->compositing_mode == CTX_COMPOSITE_COPY)
         {
-          if (vt)
-            vt_paste (vt, text);
-          free (text);
+          rasterizer->comp_op = ctx_GRAYA8_copy_normal;
         }
-    }
-  else if (!strcmp (event, "shift-control-c") && vt)
-    {
-      char *text = vt_get_selection (vt);
-      if (text)
+        else if (gstate->global_alpha_u8 == 0)
+          rasterizer->comp_op = CTX_COMPOSITE_SUFFIX(ctx_RGBA8_nop);
+        else
+        switch (gstate->source_fill.type)
         {
-          ctx_set_clipboard (ctx, text);
-          free (text);
+          case CTX_SOURCE_COLOR:
+            if (gstate->compositing_mode == CTX_COMPOSITE_SOURCE_OVER)
+            {
+              if (rasterizer->color[components-1] == 0)
+                rasterizer->comp_op = CTX_COMPOSITE_SUFFIX(ctx_RGBA8_nop);
+              else if (rasterizer->color[components-1] == 255)
+                rasterizer->comp_op = CTX_COMPOSITE_SUFFIX(ctx_GRAYA8_source_over_normal_opaque_color);
+              else
+                rasterizer->comp_op = CTX_COMPOSITE_SUFFIX(ctx_GRAYA8_source_over_normal_color);
+              rasterizer->fragment = NULL;
+            }
+            else
+            {
+              rasterizer->comp_op = CTX_COMPOSITE_SUFFIX(ctx_GRAYA8_porter_duff_color_normal);
+              rasterizer->fragment = NULL;
+            }
+            break;
+          default:
+            rasterizer->comp_op = CTX_COMPOSITE_SUFFIX(ctx_GRAYA8_porter_duff_generic_normal);
+            break;
         }
-    }
-  else if (!strcmp (event, "shift-control-t") ||
-           ((ctx_renderer_is_fb (ctx) || ctx_renderer_is_term (ctx))
-           &&   !strcmp (event, "control-t") ))
-  {
-    //XXX add_tab (vt_find_shell_command(), 1);
-  }
-  else if (!strcmp (event, "shift-control-n") )
-    {
-      pid_t pid;
-      if ( (pid=fork() ) ==0)
+        break;
+      default:
+        switch (gstate->source_fill.type)
         {
-          unsetenv ("CTX_VERSION");
-         // execlp (execute_self, execute_self, NULL);
-          exit (0);
+          case CTX_SOURCE_COLOR:
+            rasterizer->comp_op = CTX_COMPOSITE_SUFFIX(ctx_GRAYA8_porter_duff_color);
+            rasterizer->fragment = NULL;
+            break;
+          default:
+            rasterizer->comp_op = CTX_COMPOSITE_SUFFIX(ctx_GRAYA8_porter_duff_generic);
+            break;
         }
+        break;
     }
-
-#if 0
-  else if (!strcmp (event, "alt-1"))   switch_to_tab(0);
-  else if (!strcmp (event, "alt-2"))   switch_to_tab(1);
-  else if (!strcmp (event, "alt-3"))   switch_to_tab(2);
-  else if (!strcmp (event, "alt-4"))   switch_to_tab(3);
-  else if (!strcmp (event, "alt-5"))   switch_to_tab(4);
-  else if (!strcmp (event, "alt-6"))   switch_to_tab(5);
-  else if (!strcmp (event, "alt-7"))   switch_to_tab(6);
-  else if (!strcmp (event, "alt-8"))   switch_to_tab(7);
-  else if (!strcmp (event, "alt-9"))   switch_to_tab(8);
-  else if (!strcmp (event, "alt-0"))   switch_to_tab(9);
 #endif
-  else if (!strcmp (event, "shift-control-q") )
-    {
-      ctx_quit (ctx);
-    }
-  else if (!strcmp (event, "shift-control-w") )
-    {
-      active->do_quit = 1;
-    }
-  else if (!strcmp (event, "shift-control-s") )
-    {
-      if (vt)
-      {
-        char *sel = vt_get_selection (vt);
-        if (sel)
-        {
-          vt_feed_keystring (vt, ctx_event, sel);
-          free (sel);
-        }
-      }
-    }
-  else
-    {
-      if (vt)
-        vt_feed_keystring (vt, ctx_event, event);
-    }
-  ctx_client_unlock (client);
 }
 #endif
 
-static int ctx_clients_dirty_count (void)
+#endif
+#if CTX_ENABLE_RGB332
+
+inline static void
+ctx_332_unpack (uint8_t pixel,
+                uint8_t *red,
+                uint8_t *green,
+                uint8_t *blue)
 {
-  int changes = 0;
-  for (CtxList *l = clients; l; l = l->next)
-  {
-    CtxClient *client = l->data;
-    if ((client->drawn_rev != vt_rev (client->vt) ) ||
-        vt_has_blink (client->vt))
-      changes++;
-  }
-  return changes;
+  *blue   = (pixel & 3) <<6;
+  *green = ( (pixel >> 2) & 7) <<5;
+  *red   = ( (pixel >> 5) & 7) <<5;
+  if (*blue > 223)  { *blue  = 255; }
+  if (*green > 223) { *green = 255; }
+  if (*red > 223)   { *red   = 255; }
 }
 
-static void ctx_client_titlebar_drag_maximized (CtxEvent *event, void *data, void *data2)
+static inline uint8_t
+ctx_332_pack (uint8_t red,
+              uint8_t green,
+              uint8_t blue)
 {
-  CtxClient *client = data;
+  uint8_t c  = (red >> 5) << 5;
+  c |= (green >> 5) << 2;
+  c |= (blue >> 6);
+  return c;
+}
 
-  active = active_tab = client;
-  if (event->type == CTX_DRAG_RELEASE)
-  {
-    static int prev_drag_end_time = 0;
-    if (event->time - prev_drag_end_time < 500)
+static inline void
+ctx_RGB332_to_RGBA8 (CtxRasterizer *rasterizer, int x, const void *buf, uint8_t *rgba, int count)
+{
+  const uint8_t *pixel = (uint8_t *) buf;
+  while (count--)
     {
-      //client_shade_toggle (client->id);
-      ctx_client_unmaximize (client->id);
-      ctx_client_raise_top (client->id);
-      active_tab = NULL;
+      ctx_332_unpack (*pixel, &rgba[0], &rgba[1], &rgba[2]);
+#if CTX_RGB332_ALPHA
+      if (rgba[0]==255 && rgba[2] == 255 && rgba[1]==0)
+        { rgba[3] = 0; }
+      else
+#endif
+        { rgba[3] = 255; }
+      pixel+=1;
+      rgba +=4;
     }
-    prev_drag_end_time = event->time;
-  }
-  ctx_set_dirty (event->ctx, 1);
-  vt_rev_inc (client->vt);
-  event->stop_propagate = 1;
 }
 
-float ctx_client_min_y_pos (Ctx *ctx)
+static inline void
+ctx_RGBA8_to_RGB332 (CtxRasterizer *rasterizer, int x, const uint8_t *rgba, void *buf, int count)
 {
-  return _ctx_font_size * 2; // a titlebar and a panel
+  uint8_t *pixel = (uint8_t *) buf;
+  while (count--)
+    {
+#if CTX_RGB332_ALPHA
+      if (rgba[3]==0)
+        { pixel[0] = ctx_332_pack (255, 0, 255); }
+      else
+#endif
+        { pixel[0] = ctx_332_pack (rgba[0], rgba[1], rgba[2]); }
+      pixel+=1;
+      rgba +=4;
+    }
 }
 
-float ctx_client_max_y_pos (Ctx *ctx)
-{
-  return ctx_height (ctx);
-}
+#endif
+#if CTX_ENABLE_RGB565 | CTX_ENABLE_RGB565_BYTESWAPPED
 
-void ctx_client_titlebar_draw (Ctx *ctx, CtxClient *client,
-                               float x, float y, float width, float titlebar_height)
+static inline void
+ctx_565_unpack (uint16_t pixel,
+                uint8_t *red,
+                uint8_t *green,
+                uint8_t *blue,
+                int      byteswap)
 {
-#if 0
-  ctx_move_to (ctx, x, y + height * 0.8);
-  if (client == active)
-    ctx_rgba (ctx, 1, 1,0.4, 1.0);
-  else
-    ctx_rgba (ctx, 1, 1,1, 0.8);
-  ctx_text (ctx, client->title);
-#else
-  ctx_rectangle (ctx, x, y - titlebar_height,
-                 width, titlebar_height);
-  if (client == active)
-     itk_style_color (ctx, "titlebar-focused-bg");
-  else
-     itk_style_color (ctx, "titlebar-bg");
-
-  if (flag_is_set(client->flags, ITK_CLIENT_MAXIMIZED) || y == titlebar_height)
-  {
-    ctx_listen (ctx, CTX_DRAG, ctx_client_titlebar_drag_maximized, client, NULL);
-    ctx_listen_set_cursor (ctx, CTX_CURSOR_RESIZE_ALL);
-  }
-  else
-  {
-    ctx_listen (ctx, CTX_DRAG, ctx_client_titlebar_drag, client, NULL);
-    ctx_listen_set_cursor (ctx, CTX_CURSOR_RESIZE_ALL);
-  }
-  ctx_fill (ctx);
-  //ctx_font_size (ctx, itk->font_size);//titlebar_height);// * 0.85);
-
-  if (client == active &&
-      (flag_is_set(client->flags, ITK_CLIENT_MAXIMIZED) || y != titlebar_height))
-#if 1
-  ctx_rectangle (ctx, x + width - titlebar_height,
-                  y - titlebar_height, titlebar_height,
-                  titlebar_height);
-#endif
-  ctx_rgb (ctx, 1, 0,0);
-  ctx_listen (ctx, CTX_PRESS, ctx_client_close, client, NULL);
-  ctx_listen_set_cursor (ctx, CTX_CURSOR_ARROW);
-  //ctx_fill (ctx);
-  ctx_begin_path (ctx);
-  ctx_move_to (ctx, x + width - titlebar_height * 0.8, y - titlebar_height * 0.22);
-  if (client == active)
-    itk_style_color (ctx, "titlebar-focused-close");
-  else
-    itk_style_color (ctx, "titlebar-close");
-  ctx_text (ctx, "X");
-
-  ctx_move_to (ctx, x +  width/2, y - titlebar_height * 0.22);
-  if (client == active)
-    itk_style_color (ctx, "titlebar-focused-fg");
-  else
-    itk_style_color (ctx, "titlebar-fg");
-
-  ctx_save (ctx);
-  ctx_text_align (ctx, CTX_TEXT_ALIGN_CENTER);
-  if (client->title)
-    ctx_text (ctx, client->title);
+  uint16_t byteswapped;
+  if (byteswap)
+    { byteswapped = (pixel>>8) | (pixel<<8); }
   else
-    ctx_text (ctx, "untitled");
-  ctx_restore (ctx);
-#endif
+    { byteswapped  = pixel; }
+  *blue   = (byteswapped & 31) <<3;
+  *green = ( (byteswapped>>5) & 63) <<2;
+  *red   = ( (byteswapped>>11) & 31) <<3;
+  if (*blue > 248) { *blue = 255; }
+  if (*green > 248) { *green = 255; }
+  if (*red > 248) { *red = 255; }
 }
 
-#if 0
-static void key_down (CtxEvent *event, void *data1, void *data2)
+static inline uint16_t
+ctx_565_pack (uint8_t red,
+              uint8_t green,
+              uint8_t blue,
+              int     byteswap)
 {
-  fprintf (stderr, "down %i %s\n", event->unicode, event->string);
+  uint32_t c = (red >> 3) << 11;
+  c |= (green >> 2) << 5;
+  c |= blue >> 3;
+  if (byteswap)
+    { return (c>>8) | (c<<8); } /* swap bytes */
+  return c;
 }
-static void key_up (CtxEvent *event, void *data1, void *data2)
+
+static inline uint16_t
+ctx_888_to_565 (uint32_t in, int byteswap)
 {
-  fprintf (stderr, "up %i %s\n", event->unicode, event->string);
+  uint8_t *rgb=(uint8_t*)(&in);
+  return ctx_565_pack (rgb[0],rgb[1],rgb[2], byteswap);
 }
-static void key_press (CtxEvent *event, void *data1, void *data2)
+
+static inline uint32_t
+ctx_565_to_888 (uint16_t in, int byteswap)
 {
-  fprintf (stderr, "press %i %s\n", event->unicode, event->string);
+  uint32_t ret = 0;
+  uint8_t *rgba=(uint8_t*)&ret;
+  ctx_565_unpack (in,
+                  &rgba[0],
+                  &rgba[1],
+                  &rgba[2],
+                  byteswap);
+  return ret;
 }
-#endif
 
-int ctx_clients_draw (Ctx *ctx)
-{
-  _ctx_font_size = ctx_get_font_size (ctx);
-  float titlebar_height = _ctx_font_size;
-  int n_clients         = ctx_list_length (clients);
+#endif
+#if CTX_ENABLE_RGB565
 
-  if (active && flag_is_set(active->flags, ITK_CLIENT_MAXIMIZED) && n_clients == 1)
-  {
-    ctx_client_draw (ctx, active, 0, 0);
-    return 0;
-  }
 
-  for (CtxList *l = clients; l; l = l->next)
-  {
-    CtxClient *client = l->data;
-    if (flag_is_set(client->flags, ITK_CLIENT_MAXIMIZED))
+static inline void
+ctx_RGB565_to_RGBA8 (CtxRasterizer *rasterizer, int x, const void *buf, uint8_t *rgba, int count)
+{
+  const uint16_t *pixel = (uint16_t *) buf;
+  while (count--)
     {
-      if (client == active_tab)
-      {
-        ctx_client_draw (ctx, client, 0, titlebar_height);
-      }
+      ctx_565_unpack (*pixel, &rgba[0], &rgba[1], &rgba[2], 0);
+#if CTX_RGB565_ALPHA
+      if (rgba[0]==255 && rgba[2] == 255 && rgba[1]==0)
+        { rgba[3] = 0; }
       else
-      {
-        ctx_client_use_images (ctx, client);
-      }
+#endif
+        { rgba[3] = 255; }
+      pixel+=1;
+      rgba +=4;
     }
-  }
+}
 
-  {
-  for (CtxList *l = clients; l; l = l->next)
-  {
-    CtxClient *client = l->data;
-    VT *vt = client->vt;
-    if (vt && !flag_is_set(client->flags, ITK_CLIENT_MAXIMIZED))
+static inline void
+ctx_RGBA8_to_RGB565 (CtxRasterizer *rasterizer, int x, const uint8_t *rgba, void *buf, int count)
+{
+  uint16_t *pixel = (uint16_t *) buf;
+  while (count--)
     {
-      if (flag_is_set(client->flags, ITK_CLIENT_SHADED))
-      {
-        ctx_client_use_images (ctx, client);
-      }
+#if CTX_RGB565_ALPHA
+      if (rgba[3]==0)
+        { pixel[0] = ctx_565_pack (255, 0, 255, 0); }
       else
-      {
-        ctx_client_draw (ctx, client, client->x, client->y);
-      }
-
-      // resize regions
-      if (client == active &&
-         !flag_is_set(client->flags, ITK_CLIENT_SHADED) &&
-         !flag_is_set(client->flags, ITK_CLIENT_MAXIMIZED) &&
-         flag_is_set(client->flags, ITK_CLIENT_UI_RESIZABLE))
-      {
-        itk_style_color (ctx, "titlebar-focused-bg");
-
-        ctx_rectangle (ctx,
-                       client->x,
-                       client->y - titlebar_height * 2,
-                       client->width, titlebar_height);
-        ctx_listen (ctx, CTX_DRAG, ctx_client_resize_n, client, NULL);
-        ctx_listen_set_cursor (ctx, CTX_CURSOR_RESIZE_N);
-        ctx_begin_path (ctx);
-
-        ctx_rectangle (ctx,
-                       client->x,
-                       client->y + client->height - titlebar_height,
-                       client->width, titlebar_height * 2);
-        ctx_listen (ctx, CTX_DRAG, ctx_client_resize_s, client, NULL);
-        ctx_listen_set_cursor (ctx, CTX_CURSOR_RESIZE_S);
-        ctx_begin_path (ctx);
-
-        ctx_rectangle (ctx,
-                       client->x + client->width,
-                       client->y - titlebar_height,
-                       titlebar_height, client->height + titlebar_height);
-        ctx_listen (ctx, CTX_DRAG, ctx_client_resize_e, client, NULL);
-        ctx_listen_set_cursor (ctx, CTX_CURSOR_RESIZE_E);
-        ctx_begin_path (ctx);
-
-        ctx_rectangle (ctx,
-                       client->x - titlebar_height,
-                       client->y - titlebar_height,
-                       titlebar_height, client->height + titlebar_height);
-        ctx_listen (ctx, CTX_DRAG, ctx_client_resize_w, client, NULL);
-        ctx_listen_set_cursor (ctx, CTX_CURSOR_RESIZE_W);
-        ctx_begin_path (ctx); 
-
-        ctx_rectangle (ctx,
-                       client->x + client->width - titlebar_height,
-                       client->y - titlebar_height * 2,
-                       titlebar_height * 2, titlebar_height * 2);
-        ctx_listen (ctx, CTX_DRAG, ctx_client_resize_ne, client, NULL);
-        ctx_listen_set_cursor (ctx, CTX_CURSOR_RESIZE_NE);
-        ctx_begin_path (ctx);
-
-        ctx_rectangle (ctx,
-                       client->x - titlebar_height,
-                       client->y - titlebar_height * 2,
-                       titlebar_height * 2, titlebar_height * 2);
-        ctx_listen (ctx, CTX_DRAG, ctx_client_resize_nw, client, NULL);
-        ctx_listen_set_cursor (ctx, CTX_CURSOR_RESIZE_NW);
-        ctx_begin_path (ctx);
-
-        ctx_rectangle (ctx,
-                       client->x - titlebar_height,
-                       client->y + client->height - titlebar_height,
-                       titlebar_height * 2, titlebar_height * 2);
-        ctx_listen (ctx, CTX_DRAG, ctx_client_resize_sw, client, NULL);
-        ctx_listen_set_cursor (ctx, CTX_CURSOR_RESIZE_SW);
-        ctx_begin_path (ctx);
-
-        ctx_rectangle (ctx,
-                       client->x + client->width - titlebar_height,
-                       client->y + client->height - titlebar_height,
-                       titlebar_height * 2, titlebar_height * 2);
-        ctx_listen (ctx, CTX_DRAG, ctx_client_resize_se, client, NULL);
-        ctx_listen_set_cursor (ctx, CTX_CURSOR_RESIZE_SE);
-        ctx_begin_path (ctx);
-
-      }
-
-      if (client->flags & ITK_CLIENT_TITLEBAR)
-        ctx_client_titlebar_draw (ctx, client, client->x, client->y, client->width, titlebar_height);
+#endif
+        { pixel[0] = ctx_565_pack (rgba[0], rgba[1], rgba[2], 0); }
+      pixel+=1;
+      rgba +=4;
     }
-  }
-  }
-  return 0;
 }
 
-extern int _ctx_enable_hash_cache;
-
-void vt_audio_task (VT *vt, int click);
-
-int ctx_input_pending (Ctx *ctx, int timeout);
-
-int ctx_clients_need_redraw (Ctx *ctx)
+static void
+ctx_composite_RGB565 (CTX_COMPOSITE_ARGUMENTS)
 {
-  int changes = 0;
-  int follow_mouse = focus_follows_mouse;
-      CtxList *to_remove = NULL;
-  //ensure_layout ();
-
-//  if (print_shape_cache_rate)
-//    fprintf (stderr, "\r%f ", ctx_shape_cache_rate);
+  uint8_t pixels[count * 4];
+  ctx_RGB565_to_RGBA8 (rasterizer, x0, dst, &pixels[0], count);
+  rasterizer->comp_op (rasterizer, &pixels[0], rasterizer->color, x0, coverage, count);
+  ctx_RGBA8_to_RGB565 (rasterizer, x0, &pixels[0], dst, count);
+}
 
-   CtxClient *client = find_active (ctx_pointer_x (ctx),
-                                    ctx_pointer_y (ctx));
 
-   if (follow_mouse || ctx_pointer_is_down (ctx, 0) ||
-       ctx_pointer_is_down (ctx, 1) || (active==NULL))
-   {
-        if (client)
-        {
-          if (active != client)
-          {
-            active = client;
-            if (follow_mouse == 0 ||
-                (ctx_pointer_is_down (ctx, 0) ||
-                 ctx_pointer_is_down (ctx, 1)))
-            {
-              //if (client != clients->data)
-       #if 1
-              if ((client->flags & ITK_CLIENT_MAXIMIZED)==0)
-              {
-                ctx_list_remove (&clients, client);
-                ctx_list_append (&clients, client);
-              }
 #endif
-            }
-            changes ++;
-          }
-        }
-   }
-
-   for (CtxList *l = clients; l; l = l->next)
-   {
-     CtxClient *client = l->data;
-     if (client->vt)
-       {
-         if (vt_is_done (client->vt))
-           ctx_list_prepend (&to_remove, client);
-       }
-   }
-   for (CtxList *l = to_remove; l; l = l->next)
-   {
-     ctx_client_remove (ctx, l->data);
-     changes++;
-   }
-   while (to_remove)
-   {
-     ctx_list_remove (&to_remove, to_remove->data);
-   }
-
-   changes += ctx_clients_dirty_count ();
-   return changes != 0;
-}
-
-float ctx_avg_bytespeed = 0.0;
+#if CTX_ENABLE_RGB565_BYTESWAPPED
 
-static void ctx_client_handle_events_iteration (Ctx *ctx)
+static inline void
+ctx_RGB565_BS_to_RGBA8 (CtxRasterizer *rasterizer, int x, const void *buf, uint8_t *rgba, int count)
 {
-  static int fail_safe = 0;
-  //int n_clients = ctx_list_length (clients);
-      int pending_data = 0;
-      long time_start = ctx_ticks ();
-      int sleep_time = 1000000/ctx_target_fps;
-
-      pending_data = ctx_input_pending (ctx, sleep_time);
-
-      ctx_fetched_bytes = 0;
-      if (pending_data || fail_safe>100)
-      {
-        if (!pending_data)pending_data = 1;
-        /* record amount of time spent - and adjust time of reading for
-         * vts?
-         */
-        long int fractional_sleep = sleep_time / pending_data;
-        for (CtxList *l = clients; l; l = l->next)
-        {
-          CtxClient *client = l->data;
-          ctx_client_lock (client);
-          int found = 0;
-          for (CtxList *l2 = clients; l2; l2 = l2->next)
-            if (l2->data == client) found = 1;
-          if (!found)
-            goto done;
-          
-          ctx_fetched_bytes += vt_poll (client->vt, fractional_sleep);
-          //ctx_fetched_bytes += vt_poll (client->vt, sleep_time); //fractional_sleep);
-          ctx_client_unlock (client);
-        }
-done:
-        fail_safe = 0;
-      }
+  const uint16_t *pixel = (uint16_t *) buf;
+  while (count--)
+    {
+      ctx_565_unpack (*pixel, &rgba[0], &rgba[1], &rgba[2], 1);
+#if CTX_RGB565_ALPHA
+      if (rgba[0]==255 && rgba[2] == 255 && rgba[1]==0)
+        { rgba[3] = 0; }
       else
-      {
-        fail_safe ++;
-        for (CtxList *l = clients; l; l = l->next)
-        {
-          CtxClient *client = l->data;
-          vt_audio_task (client->vt, 0);
-        }
-      }
-
-      //int got_events = 0;
+#endif
+        { rgba[3] = 255; }
+      pixel+=1;
+      rgba +=4;
+    }
+}
 
-      //while (ctx_get_event (ctx)) { }
-#if 0
-      if (changes /*|| pending_data */)
-      {
-        ctx_target_fps *= 1.6;
-        if (ctx_target_fps > 60) ctx_target_fps = 60;
-      }
+static inline void
+ctx_RGBA8_to_RGB565_BS (CtxRasterizer *rasterizer, int x, const uint8_t *rgba, void *buf, int count)
+{
+  uint16_t *pixel = (uint16_t *) buf;
+  while (count--)
+    {
+#if CTX_RGB565_ALPHA
+      if (rgba[3]==0)
+        { pixel[0] = ctx_565_pack (255, 0, 255, 1); }
       else
-      {
-        ctx_target_fps = ctx_target_fps * 0.95 + 30.0 * 0.05;
+#endif
+        { pixel[0] = ctx_565_pack (rgba[0], rgba[1], rgba[2], 1); }
+      pixel+=1;
+      rgba +=4;
+    }
+}
 
-        // 20fps is the lowest where sun 8bit ulaw 8khz works reliably
-      }
+static void
+ctx_composite_RGB565_BS (CTX_COMPOSITE_ARGUMENTS)
+{
+  uint8_t pixels[count * 4];
+  ctx_RGB565_BS_to_RGBA8 (rasterizer, x0, dst, &pixels[0], count);
+  rasterizer->comp_op (rasterizer, &pixels[0], rasterizer->color, x0, coverage, count);
+  ctx_RGBA8_to_RGB565_BS (rasterizer, x0, &pixels[0], dst, count);
+}
 
-      if (ctx_avg_bytespeed > 1024 * 1024) ctx_target_fps = 10.0;
 
-      if (_ctx_green < 0.4)
-        ctx_target_fps = 120.0;
-      else if (_ctx_green > 0.6)
-        ctx_target_fps = 25.0;
+#endif
 
-      //ctx_target_fps = 30.0;
+CtxPixelFormatInfo CTX_COMPOSITE_SUFFIX(ctx_pixel_formats)[]=
+{
+#if CTX_ENABLE_RGBA8
+  {
+    CTX_FORMAT_RGBA8, 4, 32, 4, 0, 0, CTX_FORMAT_RGBA8,
+    NULL, NULL, NULL, ctx_setup_RGBA8
+  },
+#endif
+#if CTX_ENABLE_BGRA8
+  {
+    CTX_FORMAT_BGRA8, 4, 32, 4, 0, 0, CTX_FORMAT_RGBA8,
+    ctx_BGRA8_to_RGBA8, ctx_RGBA8_to_BGRA8, ctx_composite_BGRA8, ctx_setup_RGBA8,
+  },
+#endif
+#if CTX_ENABLE_GRAYF
+  {
+    CTX_FORMAT_GRAYF, 1, 32, 4 * 2, 0, 0, CTX_FORMAT_GRAYAF,
+    NULL, NULL, ctx_composite_GRAYF, ctx_setup_GRAYAF,
+  },
+#endif
+#if CTX_ENABLE_GRAYAF
+  {
+    CTX_FORMAT_GRAYAF, 2, 64, 4 * 2, 0, 0, CTX_FORMAT_GRAYAF,
+    NULL, NULL, NULL, ctx_setup_GRAYAF,
+  },
+#endif
+#if CTX_ENABLE_RGBAF
+  {
+    CTX_FORMAT_RGBAF, 4, 128, 4 * 4, 0, 0, CTX_FORMAT_RGBAF,
+    NULL, NULL, NULL, ctx_setup_RGBAF,
+  },
+#endif
+#if CTX_ENABLE_RGB8
+  {
+    CTX_FORMAT_RGB8, 3, 24, 4, 0, 0, CTX_FORMAT_RGBA8,
+    ctx_RGB8_to_RGBA8, ctx_RGBA8_to_RGB8, ctx_composite_convert, ctx_setup_RGBA8,
+  },
+#endif
+#if CTX_ENABLE_GRAY1
+  {
+#if CTX_NATIVE_GRAYA8
+    CTX_FORMAT_GRAY1, 1, 1, 2, 1, 1, CTX_FORMAT_GRAYA8,
+    ctx_GRAY1_to_GRAYA8, ctx_GRAYA8_to_GRAY1, ctx_composite_convert, ctx_setup_GRAYA8,
 #else
-      ctx_target_fps = 30.0;
+    CTX_FORMAT_GRAY1, 1, 1, 4, 1, 1, CTX_FORMAT_RGBA8,
+    ctx_GRAY1_to_RGBA8, ctx_RGBA8_to_GRAY1, ctx_composite_convert, ctx_setup_RGBA8,
 #endif
-
-      long time_end = ctx_ticks ();
-
-      int timed = (time_end-time_start);
-      float bytespeed = ctx_fetched_bytes / ((timed)/ (1000.0f * 1000.0f));
-
-      ctx_avg_bytespeed = bytespeed * 0.2 + ctx_avg_bytespeed * 0.8;
-#if 0
-      fprintf (stderr, "%.2fmb/s %i/%i  %.2f                    \r", ctx_avg_bytespeed/1024/1024, 
ctx_fetched_bytes, timed, ctx_target_fps);
+  },
 #endif
-}
+#if CTX_ENABLE_GRAY2
+  {
+#if CTX_NATIVE_GRAYA8
+    CTX_FORMAT_GRAY2, 1, 2, 2, 4, 4, CTX_FORMAT_GRAYA8,
+    ctx_GRAY2_to_GRAYA8, ctx_GRAYA8_to_GRAY2, ctx_composite_convert, ctx_setup_GRAYA8,
+#else
+    CTX_FORMAT_GRAY2, 1, 2, 4, 4, 4, CTX_FORMAT_RGBA8,
+    ctx_GRAY2_to_RGBA8, ctx_RGBA8_to_GRAY2, ctx_composite_convert, ctx_setup_RGBA8,
+#endif
+  },
+#endif
+#if CTX_ENABLE_GRAY4
+  {
+#if CTX_NATIVE_GRAYA8
+    CTX_FORMAT_GRAY4, 1, 4, 2, 16, 16, CTX_FORMAT_GRAYA8,
+    ctx_GRAY4_to_GRAYA8, ctx_GRAYA8_to_GRAY4, ctx_composite_convert, ctx_setup_GRAYA8,
+#else
+    CTX_FORMAT_GRAY4, 1, 4, 4, 16, 16, CTX_FORMAT_GRAYA8,
+    ctx_GRAY4_to_RGBA8, ctx_RGBA8_to_GRAY4, ctx_composite_convert, ctx_setup_RGBA8,
+#endif
+  },
+#endif
+#if CTX_ENABLE_GRAY8
+  {
+#if CTX_NATIVE_GRAYA8
+    CTX_FORMAT_GRAY8, 1, 8, 2, 0, 0, CTX_FORMAT_GRAYA8,
+    ctx_GRAY8_to_GRAYA8, ctx_GRAYA8_to_GRAY8, ctx_composite_convert, ctx_setup_GRAYA8,
+#else
+    CTX_FORMAT_GRAY8, 1, 8, 4, 0, 0, CTX_FORMAT_RGBA8,
+    ctx_GRAY8_to_RGBA8, ctx_RGBA8_to_GRAY8, ctx_composite_convert, ctx_setup_RGBA8,
+#endif
+  },
+#endif
+#if CTX_ENABLE_GRAYA8
+  {
+#if CTX_NATIVE_GRAYA8
+    CTX_FORMAT_GRAYA8, 2, 16, 2, 0, 0, CTX_FORMAT_GRAYA8,
+    ctx_GRAYA8_to_RGBA8, ctx_RGBA8_to_GRAYA8, NULL, ctx_setup_GRAYA8,
+#else
+    CTX_FORMAT_GRAYA8, 2, 16, 4, 0, 0, CTX_FORMAT_RGBA8,
+    ctx_GRAYA8_to_RGBA8, ctx_RGBA8_to_GRAYA8, ctx_composite_convert, ctx_setup_RGBA8,
+#endif
+  },
+#endif
+#if CTX_ENABLE_RGB332
+  {
+    CTX_FORMAT_RGB332, 3, 8, 4, 10, 12, CTX_FORMAT_RGBA8,
+    ctx_RGB332_to_RGBA8, ctx_RGBA8_to_RGB332,
+    ctx_composite_convert, ctx_setup_RGBA8,
+  },
+#endif
+#if CTX_ENABLE_RGB565
+  {
+    CTX_FORMAT_RGB565, 3, 16, 4, 32, 64, CTX_FORMAT_RGBA8,
+    ctx_RGB565_to_RGBA8, ctx_RGBA8_to_RGB565,
+    ctx_composite_RGB565, ctx_setup_RGBA8,
+  },
+#endif
+#if CTX_ENABLE_RGB565_BYTESWAPPED
+  {
+    CTX_FORMAT_RGB565_BYTESWAPPED, 3, 16, 4, 32, 64, CTX_FORMAT_RGBA8,
+    ctx_RGB565_BS_to_RGBA8,
+    ctx_RGBA8_to_RGB565_BS,
+    ctx_composite_RGB565_BS, ctx_setup_RGBA8,
+  },
+#endif
+#if CTX_ENABLE_CMYKAF
+  {
+    CTX_FORMAT_CMYKAF, 5, 160, 4 * 5, 0, 0, CTX_FORMAT_CMYKAF,
+    NULL, NULL, NULL, ctx_setup_CMYKAF,
+  },
+#endif
+#if CTX_ENABLE_CMYKA8
+  {
+    CTX_FORMAT_CMYKA8, 5, 40, 4 * 5, 0, 0, CTX_FORMAT_CMYKAF,
+    NULL, NULL, ctx_composite_CMYKA8, ctx_setup_CMYKAF,
+  },
+#endif
+#if CTX_ENABLE_CMYK8
+  {
+    CTX_FORMAT_CMYK8, 5, 32, 4 * 5, 0, 0, CTX_FORMAT_CMYKAF,
+    NULL, NULL, ctx_composite_CMYK8, ctx_setup_CMYKAF,
+  },
+#endif
+  {
+    CTX_FORMAT_NONE
+  }
+};
 
 
-static int ctx_clients_handle_events_fun (void *data)
+void
+CTX_COMPOSITE_SUFFIX(ctx_compositor_setup) (CtxRasterizer *rasterizer)
 {
-  Ctx *ctx = data;
-  while (!ctx->quit)
+  if (rasterizer->state->gstate.source_fill.type == CTX_SOURCE_TEXTURE)
   {
-    int n_clients = ctx_list_length (clients);
-    ctx_client_handle_events_iteration (data);
-    switch (n_clients)
-    {
-      case 0:
-        usleep (1000 * 10);
-        break;
-      case 1:
-        usleep (1); // letting quit work - and also makes framerate for dump
-        break;
-      default:
-        usleep (0); // the switching between clients should be enough
-        break;
-    }
+    if (!rasterizer->state->gstate.source_fill.texture.buffer->color_managed)
+      _ctx_texture_prepare_color_management (rasterizer,
+        rasterizer->state->gstate.source_fill.texture.buffer);
   }
-  return 0;
-}
 
-void ctx_clients_handle_events (Ctx *ctx)
-{
-#if CTX_THREADS==0
-    ctx_client_handle_events_iteration (ctx);
-#else
-    static thrd_t tid = 0;
-    if (tid == 0)
-    {
-      thrd_create (&tid, (void*)ctx_clients_handle_events_fun, ctx);
-    }
+  if (rasterizer->format->setup)
+  {
+    // this works also when _default is passed
+    rasterizer->format->setup (rasterizer);
+  }
+#if CTX_GRADIENTS
+#if CTX_GRADIENT_CACHE
+  CtxGState *gstate = &rasterizer->state->gstate;
+  switch (gstate->source_fill.type)
+  {
+    case CTX_SOURCE_LINEAR_GRADIENT:
+    case CTX_SOURCE_RADIAL_GRADIENT:
+      ctx_gradient_cache_prime (rasterizer);
+  }
+#endif
 #endif
 }
 
-#endif /* CTX_VT */
+#endif
 #endif //  __CTX_H__


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]