diff --git a/components/ctx/ctx.c b/components/ctx/ctx.c index 56d8a15..7553cf1 100644 --- a/components/ctx/ctx.c +++ b/components/ctx/ctx.c @@ -11,5 +11,11 @@ #ifdef CONFIG_FLOW3R_CTX_FLAVOUR_FULL #endif +int mp_ctx_vfs_load_file (const char *path, + unsigned char **contents, + long *length, + long max_length); +#define CTX_LOAD_FILE mp_ctx_vfs_load_file + #define CTX_IMPLEMENTATION #include "ctx.h" diff --git a/components/ctx/ctx.h b/components/ctx/ctx.h index 29ea5f4..5b5f314 100644 --- a/components/ctx/ctx.h +++ b/components/ctx/ctx.h @@ -1,4 +1,4 @@ -/* ctx git commit: 2db2b527 */ +/* ctx git commit: 26287002 */ /* * ctx.h is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -422,7 +422,7 @@ void ctx_line_dash_offset (Ctx *ctx, float line_dash); * * Specify the line dash pattern. */ -void ctx_line_dash (Ctx *ctx, float *dashes, int count); +void ctx_line_dash (Ctx *ctx, const float *dashes, int count); /** * ctx_font_size: @@ -445,12 +445,25 @@ ctx_font_extents (Ctx *ctx, float *descent, float *line_gap); +void ctx_parse (Ctx *ctx, const char *string); + /** - * ctx_parse: + * ctx_parse_animation: + * elapsed_time the + * + * Parses a string containg ctx protocol data, including an overlayed + * scene and key-framing synax. * - * Parses a string containg text ctx protocol data. + * pass in the scene_no you expect to render in the pointer for scene_no + * actual rendered scene is returned here. + * + * elapsed time for scene to render, if we are beyond the specified scene + * adjust elapsed_time to reflect elapsed time in actually rendered scene. */ -void ctx_parse (Ctx *ctx, const char *string); +void +ctx_parse_animation (Ctx *ctx, const char *string, + float *elapsed_time, + int *scene_no); /** * low level glyph drawing calls, unless you are integrating harfbuzz @@ -466,34 +479,53 @@ CtxGlyph *ctx_glyph_allocate (int n_glyphs); void ctx_glyph_free (CtxGlyph *glyphs); /** */ -int ctx_glyph (Ctx *ctx, uint32_t unichar, int stroke); -/** - */ +int ctx_glyph_id (Ctx *ctx, uint32_t glyphid, int stroke); +int ctx_glyph_unichar (Ctx *ctx, uint32_t unichar, int stroke); + +int ctx_glyph (Ctx *ctx, uint32_t unichar, int stroke); + void ctx_glyphs (Ctx *ctx, CtxGlyph *glyphs, int n_glyphs); + +int +ctx_glyph_lookup (Ctx *ctx, uint32_t unichar); + /** */ -void ctx_glyphs_stroke (Ctx *ctx, - CtxGlyph *glyphs, - int n_glyphs); +void ctx_glyphs_stroke (Ctx *ctx, + CtxGlyph *glyphs, + int n_glyphs); +/* sets the color of the shadow-blur, use a < 1.0 for softer blur + */ void ctx_shadow_rgba (Ctx *ctx, float r, float g, float b, float a); -void ctx_shadow_blur (Ctx *ctx, float x); + +/* set the shadow_blur radius, which in HTML5 canvas is double the standard + * deviation of an expected gaussian blur. + */ +void ctx_shadow_blur (Ctx *ctx, float stddev_x_2); + +/** + * specify offset of generated shadow blur + */ void ctx_shadow_offset_x (Ctx *ctx, float x); void ctx_shadow_offset_y (Ctx *ctx, float y); +/* create a new page + */ +void ctx_new_page (Ctx *ctx); + /** * ctx_view_box: * - * Specify the view box for the current page. + * Specify the view box for the current page, should immediately follow + * new_page if present, the PDF backend in particular makes use of this. */ void ctx_view_box (Ctx *ctx, float x0, float y0, float w, float h); -void ctx_new_page (Ctx *ctx); - /** * ctx_set_pixel_u8: * @@ -501,14 +533,16 @@ void ctx_new_page (Ctx *ctx); * for individual few pixels, slow for doing textures. */ 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); +ctx_set_pixel_u8 (Ctx *ctx, uint16_t x, uint16_t y, + uint8_t r, uint8_t g, uint8_t b, uint8_t a); /** * ctx_global_alpha: * - * Set a global alpha value that the colors, textures and gradients are modulated by. + * Set a global alpha value that the colors, textures and gradients are + * modulated by. */ -void ctx_global_alpha (Ctx *ctx, float global_alpha); +void ctx_global_alpha (Ctx *ctx, float global_alpha); /** @@ -517,7 +551,7 @@ void ctx_global_alpha (Ctx *ctx, float global_alpha); * The next source definition applies to stroking rather than filling, when a stroke source is * not explicitly set the value of filling is inherited. */ -void ctx_stroke_source (Ctx *ctx); // next source definition is for stroking +void ctx_stroke_source (Ctx *ctx); // next source definition is for stroking void ctx_rgba_stroke (Ctx *ctx, float r, float g, float b, float a); void ctx_rgb_stroke (Ctx *ctx, float r, float g, float b); @@ -553,6 +587,7 @@ void ctx_get_dcmyka (Ctx *ctx, float *dcmyka); int ctx_in_fill (Ctx *ctx, float x, float y); int ctx_in_stroke (Ctx *ctx, float x, float y); + /** * ctx_linear_gradient: * Change the source to a linear gradient from x0,y0 to x1 y1, by default an empty gradient @@ -567,6 +602,13 @@ void ctx_linear_gradient (Ctx *ctx, float x0, float y0, float x1, float y1); void ctx_radial_gradient (Ctx *ctx, float x0, float y0, float r0, float x1, float y1, float r1); +/** + * ctx_conic_gradient: + * Change the source to a conic/conic gradient cenetered at cx,cy with gradient starting at angle start_angle. + * TODO: add repeat-count? + */ +void ctx_conic_gradient (Ctx *ctx, float cx, float cy, float start_angle, float cycles); + /* ctx_graident_add_stop: * * Add an RGBA gradient stop to the current gradient at position pos. @@ -1004,7 +1046,7 @@ typedef enum struct _CtxGlyph { - uint32_t index; + uint32_t index; // glyph index in font float x; float y; }; @@ -1058,8 +1100,6 @@ struct void ctx_text (Ctx *ctx, const char *string); -void ctx_text_stroke (Ctx *ctx, - const char *string); // XXX do not use? void ctx_fill_text (Ctx *ctx, @@ -1067,12 +1107,6 @@ void ctx_fill_text (Ctx *ctx, float x, float y); -// XXX do not use? -void ctx_stroke_text (Ctx *ctx, - const char *string, - float x, - float y); - /* returns the total horizontal advance if string had been rendered */ float ctx_text_width (Ctx *ctx, const char *string); @@ -1080,7 +1114,9 @@ float ctx_text_width (Ctx *ctx, float ctx_glyph_width (Ctx *ctx, int unichar); int ctx_load_font_ttf (const char *name, const void *ttf_contents, int length); +int ctx_load_font_hb (const char *name, const void *path, int length_ignored); +int ctx_load_font_ttf_file (const char *name, const char *path); /** * ctx_dirty_rect: @@ -1153,8 +1189,13 @@ struct _CtxFloatRectangle { float height; }; -void ctx_quit (Ctx *ctx); -int ctx_has_quit (Ctx *ctx); +void ctx_exit (Ctx *ctx); +int ctx_has_exited (Ctx *ctx); +void ctx_reset_has_exited (Ctx *ctx); + +// XXX : compat - in case someone were using it +#define ctx_quit ctx_exit +#define ctx_has_quit ctx_has_exited typedef void (*CtxCb) (CtxEvent *event, void *data, @@ -1363,6 +1404,8 @@ char *vt_get_selection (VT *vt); long vt_rev (VT *vt); int vt_has_blink (VT *vt); int ctx_vt_had_alt_screen (VT *vt); +int vt_get_cursor_x (VT *vt); +int vt_get_cursor_y (VT *vt); int ctx_clients_handle_events (Ctx *ctx); @@ -1393,6 +1436,7 @@ typedef enum CtxBackendType { CTX_BACKEND_SDL, CTX_BACKEND_DRAWLIST, CTX_BACKEND_PDF, + CTX_BACKEND_CB, } CtxBackendType; CtxBackendType ctx_backend_type (Ctx *ctx); @@ -1414,22 +1458,22 @@ static inline int ctx_backend_is_tiled (Ctx *ctx) #endif 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, - ITK_CLIENT_LAYER2 = 1<<6, // used for having a second set + CSS_CLIENT_UI_RESIZABLE = 1<<0, + CSS_CLIENT_CAN_LAUNCH = 1<<1, + CSS_CLIENT_MAXIMIZED = 1<<2, + CSS_CLIENT_ICONIFIED = 1<<3, + CSS_CLIENT_SHADED = 1<<4, + CSS_CLIENT_TITLEBAR = 1<<5, + CSS_CLIENT_LAYER2 = 1<<6, // used for having a second set // to draw - useful for splitting // scrolled and HUD items // with HUD being LAYER2 - ITK_CLIENT_KEEP_ALIVE = 1<<7, // do not automatically - ITK_CLIENT_FINISHED = 1<<8, // do not automatically + CSS_CLIENT_KEEP_ALIVE = 1<<7, // do not automatically + CSS_CLIENT_FINISHED = 1<<8, // do not automatically // remove after process quits - ITK_CLIENT_PRELOAD = 1<<9, - ITK_CLIENT_LIVE = 1<<10 + CSS_CLIENT_PRELOAD = 1<<9, + CSS_CLIENT_LIVE = 1<<10 } CtxClientFlags; typedef void (*CtxClientFinalize)(CtxClient *client, void *user_data); @@ -1511,27 +1555,45 @@ float ctx_client_get_opacity (Ctx *ctx, int id); void ctx_client_set_title (Ctx *ctx, int id, const char *title); const char *ctx_client_get_title (Ctx *ctx, int id); +typedef enum +{ + CTX_EDGE = 0, + CTX_EDGE_FLIPPED = 1, + CTX_NEW_EDGE = 2, + CTX_CLOSE_EDGE = 3 +} CtxRasterizerCode; + + typedef enum { CTX_CONT = '\0', // - contains args from preceding entry CTX_NOP = ' ', // - // ! UNUSED - // " start/end string - // # comment in parser - // $ UNUSED - // % percent of viewport width or height - CTX_EDGE = '&', // not occuring in commandstream - // ' start/end string CTX_DATA = '(', // size size-in-entries - u32 CTX_DATA_REV = ')', // reverse traversal data marker CTX_SET_RGBA_U8 = '*', // r g b a - u8 - CTX_NEW_EDGE = '+', // x0 y0 x1 y1 - s16 // , UNUSED/RESERVED CTX_SET_PIXEL = '-', // 8bit "fast-path" r g b a x y - u8 for rgba, and u16 for x,y // set pixel might want a shorter ascii form with hex-color? or keep it an embedded // only option? + // ^ used for unit + // & UNUSED + // + UNUSED + // ! UNUSED + // " start/end string + // # comment in parser + // $ UNUSED + // % percent of viewport width or height + // ' start/end string // . decimal seperator // / UNUSED + // : UNUSED + // < UNUSED + // = UNUSED/RESERVED + // > UNUSED + // ? UNUSED + // \ UNUSED + // ^ PARSER - vh unit + // ~ UNUSED/textenc /* optimizations that reduce the number of entries used, * not visible outside the drawlist compression, thus @@ -1549,12 +1611,7 @@ typedef enum CTX_FILL_MOVE_TO = '7', // x y CTX_REL_QUAD_TO_REL_QUAD_TO = '8', // cx1 x1 cy1 y1 cx1 x2 cy1 y1 -- s8 CTX_REL_QUAD_TO_S16 = '9', // cx1 cy1 x y - s16 - // : UNUSED CTX_END_FRAME = ';', - // < UNUSED - // = UNUSED/RESERVED - // > UNUSED - // ? UNUSED CTX_DEFINE_FONT = 15, @@ -1563,7 +1620,7 @@ typedef enum CTX_ARC = 'B', // x y radius angle1 angle2 direction CTX_CURVE_TO = 'C', // cx1 cy1 cx2 cy2 x y CTX_PAINT = 'D', // - CTX_STROKE = 'E', // + // 'E' // scientific notation CTX_FILL = 'F', // CTX_RESTORE = 'G', // CTX_HOR_LINE_TO = 'H', // x @@ -1579,28 +1636,27 @@ typedef enum CTX_VIEW_BOX = 'R', // x y width height CTX_SMOOTH_TO = 'S', // cx cy x y CTX_SMOOTHQ_TO = 'T', // x y - CTX_START_FRAME = 'U', // XXX does this belong here? + CTX_CONIC_GRADIENT = 'U', // cx cy start_angle cycles CTX_VER_LINE_TO = 'V', // y CTX_APPLY_TRANSFORM = 'W', // a b c d e f g h i j - for set_transform combine with identity CTX_EXIT = 'X', // - CTX_ROUND_RECTANGLE = 'Y', // x y width height radius + CTX_TRANSLATE = 'Y', // x y CTX_CLOSE_PATH2 = 'Z', // + + CTX_START_FRAME = ':', // XXX does this belong here? CTX_KERNING_PAIR = '[', // glA glB kerning, glA and glB in u16 kerning in s32 - // \ UNUSED - // ^ PARSER - vh unit CTX_COLOR_SPACE = ']', // IccSlot data data_len, // 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 | also unit CTX_STROKE_SOURCE = '_', // next source definition applies to strokes CTX_SOURCE_TRANSFORM = '`', CTX_REL_ARC_TO = 'a', // x1 y1 x2 y2 radius CTX_CLIP = 'b', CTX_REL_CURVE_TO = 'c', // cx1 cy1 cx2 cy2 x y CTX_LINE_DASH = 'd', // dashlen0 [dashlen1 ...] - CTX_TRANSLATE = 'e', // x y + // 'e' -- scientific notation for SVG numbers CTX_LINEAR_GRADIENT = 'f', // x1 y1 x2 y2 CTX_SAVE = 'g', CTX_REL_HOR_LINE_TO = 'h', // x @@ -1617,16 +1673,14 @@ typedef enum CTX_RECTANGLE = 'r', // x y width height CTX_REL_SMOOTH_TO = 's', // cx cy x y CTX_REL_SMOOTHQ_TO = 't', // x y - CTX_STROKE_TEXT = 'u', // string - utf8 string + CTX_STROKE = 'u', // string - utf8 string CTX_REL_VER_LINE_TO = 'v', // y CTX_GLYPH = 'w', // unichar fontsize CTX_TEXT = 'x', // string | kern - utf8 data to shape or horizontal kerning amount - CTX_IDENTITY = 'y', // XXX remove? + CTX_IDENTITY = 'y', // XXX remove? - or reset to baseline.. which is what identity expects CTX_CLOSE_PATH = 'z', // CTX_START_GROUP = '{', - // | UNUSED CTX_END_GROUP = '}', - // ~ UNUSED/textenc /* though expressed as two chars in serialization we have @@ -1669,9 +1723,15 @@ typedef enum CTX_WRAP_LEFT = 147, // kL CTX_WRAP_RIGHT = 148, // kR CTX_LINE_HEIGHT = 149, // kH - // + + CTX_STROKE_POS = 150, // kp + CTX_FEATHER = 151, // kp + +#define CTX_LAST_COMMAND CTX_FEATHER + CTX_STROKE_RECT = 200, // strokeRect - only exist in long form CTX_FILL_RECT = 201, // fillRect - only exist in long form + CTX_ROUND_RECTANGLE = '|', // x y width height radius } CtxCode; @@ -1679,7 +1739,6 @@ typedef enum typedef struct _CtxCommand CtxCommand; -#define CTX_ASSERT 0 #if CTX_ASSERT==1 #define ctx_assert(a) if(!(a)){fprintf(stderr,"%s:%i assertion failed\n", __FUNCTION__, __LINE__); } @@ -1941,6 +2000,15 @@ struct float y2; } linear_gradient; struct + { + uint8_t code; + float x; + float y; + uint8_t pad0; + float start_angle; + float cycles; + } conic_gradient; + struct { uint8_t code; float x; @@ -2237,7 +2305,7 @@ struct _CtxBackend { Ctx *ctx; - void (*process) (Ctx *ctx, CtxCommand *entry); + void (*process) (Ctx *ctx, const CtxCommand *entry); /* for interactive/event-handling backends */ void (*start_frame) (Ctx *ctx); @@ -2399,10 +2467,10 @@ typedef enum _CtxColorSpace CtxColorSpace; * * The set profiles follows the graphics state. */ -void ctx_colorspace (Ctx *ctx, - CtxColorSpace space_slot, - unsigned char *data, - int data_length); +void ctx_colorspace (Ctx *ctx, + CtxColorSpace space_slot, + const unsigned char *data, + int data_length); @@ -2514,7 +2582,8 @@ int ctx_vt_read (Ctx *ctx); int ctx_vt_cursor_y (CtxClient *client); -#if CTX_GSTATE_PROTECT + +//#if CTX_GSTATE_PROTECT /* sets the current gstate stack (number of unpaired ctx_save calls) as a * limit that can not be restored beyond. For now can not be used recursively. */ @@ -2524,7 +2593,7 @@ void ctx_gstate_protect (Ctx *ctx); * calls have been made, */ void ctx_gstate_unprotect (Ctx *ctx); -#endif +//#endif /* set the logical clock used for the texture eviction policy */ void ctx_set_textureclock (Ctx *ctx, int frame); @@ -2540,6 +2609,25 @@ ctx_rasterizer_reinit (Ctx *ctx, int stride, CtxPixelFormat pixel_format); +float ctx_get_stroke_pos (Ctx *ctx); +void ctx_stroke_pos (Ctx *ctx, float x); + +/* only valid for rasterizer backends, not kept in graphics state + */ +enum _CtxAntialias +{ + CTX_ANTIALIAS_DEFAULT, // + CTX_ANTIALIAS_NONE, // non-antialiased + CTX_ANTIALIAS_FAST, // vertical aa 3 for complex scanlines + CTX_ANTIALIAS_GOOD, // vertical aa 5 for complex scanlines + CTX_ANTIALIAS_FULL, // vertical aa 15 for complex scanlines +}; +typedef enum _CtxAntialias CtxAntialias; +void ctx_set_antialias (Ctx *ctx, CtxAntialias antialias); +CtxAntialias ctx_get_antialias (Ctx *ctx); + +int ctx_is_set (Ctx *ctx, uint32_t hash); + #ifdef __cplusplus } #endif @@ -2613,7 +2701,7 @@ void ctx_string_append_float (CtxString *string, float val); jklmnopqrstuvwxyz{|}~ */ static const struct __attribute__ ((packed)) {uint8_t code; uint32_t a; uint32_t b;} ctx_font_ascii[]={ -{15, 0x00000000, 0x000007a7},/* length:1959 CTX_SUBDIV:8 CTX_BAKE_FONT_SIZE:160 */ +{15, 0x00000000, 0x00000926},/* length:2342 CTX_SUBDIV:8 CTX_BAKE_FONT_SIZE:160 */ {'(', 0x00000008, 0x00000001},/* Roboto*/ {32, 0x6f626f52, 0x00006f74}, {')', 0x00000008, 0x00000001}, @@ -2630,6 +2718,7 @@ ctx_font_ascii[]={ {32, 0x00000032, 0x00000000}, {')', 0x0000004b, 0x00000009}, {'@', 0x00000020, 0x000021dd},/* x-advance: 33.863281 */ +{'[', 0x00540020, 0xfffffd56},/*kerning T : -2.674510 */ {'@', 0x00000021, 0x00002333},/* ! x-advance: 35.199219 */ {'M', 0x41c08889, 0xc2c22223}, {'l', 0xbf5ddde0, 0x428b5556}, @@ -2650,7 +2739,21 @@ ctx_font_ascii[]={ {'l', 0x00000000, 0xc2037778}, {'l', 0x41166668, 0x00000000}, {'l', 0x00000000, 0x41111118}, -{'[', 0x00770022, 0x000000bb},/*kerning*/ +{'[', 0x00220022, 0xfffff8de},/*kerning " " : -7.160784 */ +{'[', 0x00270022, 0xfffff8de},/*kerning " ' : -7.160784 */ +{'[', 0x00410022, 0xfffff800},/*kerning " A : -8.031373 */ +{'[', 0x00610022, 0xfffffcab},/*kerning " a : -3.345098 */ +{'[', 0x00630022, 0xfffffc12},/*kerning " c : -3.945098 */ +{'[', 0x00640022, 0xfffffc12},/*kerning " d : -3.945098 */ +{'[', 0x00650022, 0xfffffc12},/*kerning " e : -3.945098 */ +{'[', 0x00670022, 0xfffffc12},/*kerning " g : -3.945098 */ +{'[', 0x006d0022, 0xfffffeab},/*kerning " m : -1.337255 */ +{'[', 0x006e0022, 0xfffffeab},/*kerning " n : -1.337255 */ +{'[', 0x006f0022, 0xfffffbef},/*kerning " o : -4.082353 */ +{'[', 0x00700022, 0xfffffeab},/*kerning " p : -1.337255 */ +{'[', 0x00710022, 0xfffffc12},/*kerning " q : -3.945098 */ +{'[', 0x00730022, 0xfffffaab},/*kerning " s : -5.352941 */ +{'[', 0x00770022, 0x000000bb},/*kerning " w : 0.733333 */ {'@', 0x00000023, 0x00005411},/* # x-advance: 84.066406 */ {'M', 0x4236eef0, 0x00000000}, {'l', 0x40aaaaa8, 0xc1daaaab}, @@ -2821,7 +2924,21 @@ ctx_font_ascii[]={ {'l', 0xc109999a, 0x00000000}, {'l', 0x3d8888c0, 0xc1feeef0}, {'l', 0x411eeef0, 0x00000000}, -{'[', 0x00770027, 0x000000bb},/*kerning*/ +{'[', 0x00220027, 0xfffff8de},/*kerning ' " : -7.160784 */ +{'[', 0x00270027, 0xfffff8de},/*kerning ' ' : -7.160784 */ +{'[', 0x00410027, 0xfffff800},/*kerning ' A : -8.031373 */ +{'[', 0x00610027, 0xfffffcab},/*kerning ' a : -3.345098 */ +{'[', 0x00630027, 0xfffffc12},/*kerning ' c : -3.945098 */ +{'[', 0x00640027, 0xfffffc12},/*kerning ' d : -3.945098 */ +{'[', 0x00650027, 0xfffffc12},/*kerning ' e : -3.945098 */ +{'[', 0x00670027, 0xfffffc12},/*kerning ' g : -3.945098 */ +{'[', 0x006d0027, 0xfffffeab},/*kerning ' m : -1.337255 */ +{'[', 0x006e0027, 0xfffffeab},/*kerning ' n : -1.337255 */ +{'[', 0x006f0027, 0xfffffbef},/*kerning ' o : -4.082353 */ +{'[', 0x00700027, 0xfffffeab},/*kerning ' p : -1.337255 */ +{'[', 0x00710027, 0xfffffc12},/*kerning ' q : -3.945098 */ +{'[', 0x00730027, 0xfffffaab},/*kerning ' s : -5.352941 */ +{'[', 0x00770027, 0x000000bb},/*kerning ' w : 0.733333 */ {'@', 0x00000028, 0x00002ebb},/* ( x-advance: 46.730469 */ {'M', 0x410eeeef, 0xc21dddde}, {'q', 0x00000000, 0xc19aaaac}, @@ -2844,9 +2961,9 @@ ctx_font_ascii[]={ {0, 0xc1522224, 0xc1bc4444}, {'q', 0xc0b11112, 0xc167777a}, {0, 0xc0b11112, 0xc20aaaab}, -{'[', 0x00560028, 0x00000155},/*kerning*/ -{'[', 0x00570028, 0x00000133},/*kerning*/ -{'[', 0x00590028, 0x00000177},/*kerning*/ +{'[', 0x00560028, 0x00000155},/*kerning ( V : 1.337255 */ +{'[', 0x00570028, 0x00000133},/*kerning ( W : 1.203922 */ +{'[', 0x00590028, 0x00000177},/*kerning ( Y : 1.470588 */ {'@', 0x00000029, 0x00002f88},/* ) x-advance: 47.531250 */ {'M', 0x42173334, 0xc21b3334}, {'q', 0x00000000, 0x419bbbbd}, @@ -2910,6 +3027,8 @@ ctx_font_ascii[]={ {'9', 0xffb90033, 0xff6e0034}, {'l', 0x00000000, 0xc12eeef0}, {'l', 0x41411111, 0x00000000}, +{'[', 0x0022002c, 0xfffff4ab},/*kerning , " : -11.376471 */ +{'[', 0x0027002c, 0xfffff4ab},/*kerning , ' : -11.376471 */ {'@', 0x0000002d, 0x000025bb},/* - x-advance: 37.730469 */ {'M', 0x420c4445, 0xc2395556}, {'l', 0x00000000, 0x41222224}, @@ -2922,12 +3041,15 @@ ctx_font_ascii[]={ {'8', 0x112d001d, 0x2a101110}, {'8', 0x29f01800, 0x11d311f1}, {'8', 0xefd300e3, 0xd7f1eff1}, +{'[', 0x0022002e, 0xfffff4ab},/*kerning . " : -11.376471 */ +{'[', 0x0027002e, 0xfffff4ab},/*kerning . ' : -11.376471 */ {'@', 0x0000002f, 0x00003855},/* / x-advance: 56.332031 */ {'M', 0x42515556, 0xc2c22223}, {'l', 0xc221ddde, 0x42d2ccce}, {'l', 0xc129999c, 0xb6000000}, {'l', 0x42222223, 0xc2d2ccce}, {'l', 0x41288888, 0x00000000}, +{'[', 0x002f002f, 0xfffff112},/*kerning / / : -14.988235 */ {'@', 0x00000030, 0x00004cbb},/* 0 x-advance: 76.730469 */ {'M', 0x428a0000, 0xc225ddde}, {'q', 0x00000000, 0x41beeeef}, @@ -3364,7 +3486,25 @@ ctx_font_ascii[]={ {'l', 0x4203bbbc, 0x00000000}, {'l', 0xc183bbbc, 0xc2351112}, {'l', 0xc183bbbc, 0x42351112}, -{'[', 0x007a0041, 0x000000cc},/*kerning*/ +{'[', 0x00220041, 0xfffff800},/*kerning A " : -8.031373 */ +{'[', 0x00270041, 0xfffff800},/*kerning A ' : -8.031373 */ +{'[', 0x003f0041, 0xfffffbef},/*kerning A ? : -4.082353 */ +{'[', 0x00430041, 0xffffff45},/*kerning A C : -0.733333 */ +{'[', 0x00470041, 0xffffff45},/*kerning A G : -0.733333 */ +{'[', 0x004f0041, 0xffffff45},/*kerning A O : -0.733333 */ +{'[', 0x00510041, 0xffffff45},/*kerning A Q : -0.733333 */ +{'[', 0x00540041, 0xfffff767},/*kerning A T : -8.631372 */ +{'[', 0x00550041, 0xfffffede},/*kerning A U : -1.137255 */ +{'[', 0x00560041, 0xfffffa34},/*kerning A V : -5.819608 */ +{'[', 0x00570041, 0xfffffb67},/*kerning A W : -4.615686 */ +{'[', 0x00590041, 0xfffff9bc},/*kerning A Y : -6.290196 */ +{'[', 0x006f0041, 0xffffff34},/*kerning A o : -0.800000 */ +{'[', 0x00740041, 0xfffffede},/*kerning A t : -1.137255 */ +{'[', 0x00750041, 0xffffff45},/*kerning A u : -0.733333 */ +{'[', 0x00760041, 0xfffffcab},/*kerning A v : -3.345098 */ +{'[', 0x00770041, 0xfffffdcd},/*kerning A w : -2.207843 */ +{'[', 0x00790041, 0xfffffcab},/*kerning A y : -3.345098 */ +{'[', 0x007a0041, 0x000000cc},/*kerning A z : 0.800000 */ {'@', 0x00000042, 0x00005511},/* B x-advance: 85.066406 */ {'M', 0x429aaaab, 0xc1e00001}, {'q', 0x00000000, 0x4159999b}, @@ -3394,6 +3534,9 @@ ctx_font_ascii[]={ {0, 0x41633334, 0xc0955556}, {'q', 0x40a22228, 0xc0977776}, {0, 0x40a22228, 0xc14bbbbd}, +{'[', 0x00540042, 0xfffffe34},/*kerning B T : -1.803922 */ +{'[', 0x00560042, 0xfffffe67},/*kerning B V : -1.603922 */ +{'[', 0x00590042, 0xfffffc56},/*kerning B Y : -3.678431 */ {'@', 0x00000043, 0x000058dd},/* C x-advance: 88.863281 */ {'M', 0x428bbbbc, 0xc1f6eef0}, {'l', 0x414cccd0, 0x00000000}, @@ -3429,6 +3572,10 @@ ctx_font_ascii[]={ {0, 0x418a2222, 0xc0b77778}, {'q', 0x40a22228, 0xc0b77776}, {0, 0x40d77778, 0xc1800000}, +{'[', 0x00290043, 0xfffffe45},/*kerning C ) : -1.737255 */ +{'[', 0x00540043, 0xfffffe12},/*kerning C T : -1.937255 */ +{'[', 0x005d0043, 0xffffff34},/*kerning C ] : -0.800000 */ +{'[', 0x007d0043, 0xfffffede},/*kerning C } : -1.137255 */ {'@', 0x00000044, 0x00005999},/* D x-advance: 89.597656 */ {'M', 0x41344445, 0x00000000}, {'4', 0xfcf80000, 0x000000db}, @@ -3450,6 +3597,14 @@ ctx_font_ascii[]={ {'q', 0xc0fbbbc0, 0xc1122228}, {0, 0xc1b1999c, 0xc1122228}, {'l', 0xc1699998, 0x00000000}, +{'[', 0x002c0044, 0xfffff934},/*kerning D , : -6.823529 */ +{'[', 0x002e0044, 0xfffff934},/*kerning D . : -6.823529 */ +{'[', 0x00410044, 0xfffffe9a},/*kerning D A : -1.403922 */ +{'[', 0x00540044, 0xfffffe34},/*kerning D T : -1.803922 */ +{'[', 0x00560044, 0xfffffe89},/*kerning D V : -1.470588 */ +{'[', 0x00580044, 0xfffffe89},/*kerning D X : -1.470588 */ +{'[', 0x00590044, 0xfffffd23},/*kerning D Y : -2.874510 */ +{'[', 0x005a0044, 0xfffffe78},/*kerning D Z : -1.537255 */ {'@', 0x00000045, 0x00004d99},/* E x-advance: 77.597656 */ {'M', 0x41344445, 0x00000000}, {'l', 0x00000000, 0xc2c22223}, @@ -3464,7 +3619,18 @@ ctx_font_ascii[]={ {'l', 0x42433333, 0x00000000}, {'l', 0x00000000, 0x41277778}, {'l', 0xc276aaab, 0x00000000}, -{'[', 0x00540045, 0x00000155},/*kerning*/ +{'[', 0x00540045, 0x00000155},/*kerning E T : 1.337255 */ +{'[', 0x00630045, 0xfffffebc},/*kerning E c : -1.270588 */ +{'[', 0x00640045, 0xfffffebc},/*kerning E d : -1.270588 */ +{'[', 0x00650045, 0xfffffebc},/*kerning E e : -1.270588 */ +{'[', 0x00660045, 0xfffffecd},/*kerning E f : -1.203922 */ +{'[', 0x00670045, 0xfffffebc},/*kerning E g : -1.270588 */ +{'[', 0x006f0045, 0xfffffebc},/*kerning E o : -1.270588 */ +{'[', 0x00710045, 0xfffffebc},/*kerning E q : -1.270588 */ +{'[', 0x00750045, 0xfffffede},/*kerning E u : -1.137255 */ +{'[', 0x00760045, 0xfffffe45},/*kerning E v : -1.737255 */ +{'[', 0x00770045, 0xfffffe89},/*kerning E w : -1.470588 */ +{'[', 0x00790045, 0xfffffe45},/*kerning E y : -1.737255 */ {'@', 0x00000046, 0x00004b77},/* F x-advance: 75.464844 */ {'M', 0x41344445, 0x00000000}, {'l', 0x00000000, 0xc2c22223}, @@ -3477,7 +3643,22 @@ ctx_font_ascii[]={ {'l', 0xc222aaab, 0x00000000}, {'l', 0x00000000, 0x422b7778}, {'l', 0xc14ddddf, 0x00000000}, -{'[', 0x00540046, 0x00000155},/*kerning*/ +{'[', 0x002c0046, 0xfffff067},/*kerning F , : -15.658824 */ +{'[', 0x002e0046, 0xfffff067},/*kerning F . : -15.658824 */ +{'[', 0x00410046, 0xfffff4ab},/*kerning F A : -11.376471 */ +{'[', 0x004a0046, 0xffffee67},/*kerning F J : -17.666666 */ +{'[', 0x00540046, 0x00000155},/*kerning F T : 1.337255 */ +{'[', 0x00610046, 0xfffffdbc},/*kerning F a : -2.274510 */ +{'[', 0x00630046, 0xfffffe9a},/*kerning F c : -1.403922 */ +{'[', 0x00640046, 0xfffffe9a},/*kerning F d : -1.403922 */ +{'[', 0x00650046, 0xfffffe9a},/*kerning F e : -1.403922 */ +{'[', 0x00670046, 0xfffffe9a},/*kerning F g : -1.403922 */ +{'[', 0x006f0046, 0xfffffe9a},/*kerning F o : -1.403922 */ +{'[', 0x00710046, 0xfffffe9a},/*kerning F q : -1.403922 */ +{'[', 0x00720046, 0xfffffe45},/*kerning F r : -1.737255 */ +{'[', 0x00750046, 0xfffffe89},/*kerning F u : -1.470588 */ +{'[', 0x00760046, 0xfffffe67},/*kerning F v : -1.603922 */ +{'[', 0x00790046, 0xfffffe67},/*kerning F y : -1.603922 */ {'@', 0x00000047, 0x00005d00},/* G x-advance: 93.000000 */ {'M', 0x42a60001, 0xc2415556}, {'l', 0x00000000, 0x420e2222}, @@ -3530,12 +3711,20 @@ ctx_font_ascii[]={ {'l', 0x414cccc8, 0x00000000}, {'l', 0x00000000, 0x42c22223}, {'l', 0xc14cccc8, 0x00000000}, +{'[', 0x00410048, 0x00000133},/*kerning H A : 1.203922 */ +{'[', 0x00540048, 0xfffffe12},/*kerning H T : -1.937255 */ +{'[', 0x00580048, 0x00000122},/*kerning H X : 1.137255 */ +{'[', 0x00590048, 0xfffffe23},/*kerning H Y : -1.870588 */ {'@', 0x00000049, 0x00002522},/* I x-advance: 37.132812 */ {'M', 0x41c88889, 0xc2c22223}, {'l', 0x00000000, 0x42c22223}, {'l', 0xc14dddde, 0x00000000}, {'l', 0x00000000, 0xc2c22223}, {'l', 0x414dddde, 0x00000000}, +{'[', 0x00410049, 0x00000133},/*kerning I A : 1.203922 */ +{'[', 0x00540049, 0xfffffe12},/*kerning I T : -1.937255 */ +{'[', 0x00580049, 0x00000122},/*kerning I X : 1.137255 */ +{'[', 0x00590049, 0xfffffe23},/*kerning I Y : -1.870588 */ {'@', 0x0000004a, 0x00004b55},/* J x-advance: 75.332031 */ {'M', 0x42500001, 0xc2c22223}, {'4', 0x00000066, 0x02250000}, @@ -3553,6 +3742,7 @@ ctx_font_ascii[]={ {'q', 0x40a44448, 0xc09bbbba}, {0, 0x40a44448, 0xc1655555}, {'l', 0x00000000, 0xc2897778}, +{'[', 0x0041004a, 0xfffffe89},/*kerning J A : -1.470588 */ {'@', 0x0000004b, 0x000055aa},/* K x-advance: 85.664062 */ {'M', 0x428caaab, 0x00000000}, {'l', 0xc2095556, 0xc234cccd}, @@ -3567,6 +3757,24 @@ ctx_font_ascii[]={ {'l', 0xc2188889, 0x422b7778}, {'l', 0x42244445, 0x4258ccce}, {'l', 0xc1755558, 0x00000000}, +{'[', 0x002d004b, 0xfffffbbc},/*kerning K - : -4.282353 */ +{'[', 0x0043004b, 0xfffffdef},/*kerning K C : -2.074510 */ +{'[', 0x0047004b, 0xfffffdef},/*kerning K G : -2.074510 */ +{'[', 0x004f004b, 0xfffffdef},/*kerning K O : -2.074510 */ +{'[', 0x0051004b, 0xfffffdef},/*kerning K Q : -2.074510 */ +{'[', 0x0063004b, 0xfffffe45},/*kerning K c : -1.737255 */ +{'[', 0x0064004b, 0xfffffe45},/*kerning K d : -1.737255 */ +{'[', 0x0065004b, 0xfffffe45},/*kerning K e : -1.737255 */ +{'[', 0x0067004b, 0xfffffe45},/*kerning K g : -1.737255 */ +{'[', 0x006d004b, 0xfffffe78},/*kerning K m : -1.537255 */ +{'[', 0x006e004b, 0xfffffe78},/*kerning K n : -1.537255 */ +{'[', 0x006f004b, 0xfffffe34},/*kerning K o : -1.803922 */ +{'[', 0x0070004b, 0xfffffe78},/*kerning K p : -1.537255 */ +{'[', 0x0071004b, 0xfffffe45},/*kerning K q : -1.737255 */ +{'[', 0x0075004b, 0xfffffe78},/*kerning K u : -1.537255 */ +{'[', 0x0076004b, 0xfffffd56},/*kerning K v : -2.674510 */ +{'[', 0x0077004b, 0xfffffbcd},/*kerning K w : -4.215686 */ +{'[', 0x0079004b, 0xfffffd56},/*kerning K y : -2.674510 */ {'@', 0x0000004c, 0x00004988},/* L x-advance: 73.531250 */ {'M', 0x428c4445, 0xc1277778}, {'l', 0x00000000, 0x41277778}, @@ -3575,7 +3783,22 @@ ctx_font_ascii[]={ {'l', 0x414ddddf, 0x00000000}, {'l', 0x00000000, 0x42ad3334}, {'l', 0x42380001, 0x00000000}, -{'[', 0x0041004c, 0x00000144},/*kerning*/ +{'[', 0x0022004c, 0xffffe99a},/*kerning L " : -22.486275 */ +{'[', 0x0027004c, 0xffffe99a},/*kerning L ' : -22.486275 */ +{'[', 0x0041004c, 0x00000144},/*kerning L A : 1.270588 */ +{'[', 0x0043004c, 0xfffffbab},/*kerning L C : -4.349020 */ +{'[', 0x0047004c, 0xfffffbab},/*kerning L G : -4.349020 */ +{'[', 0x004f004c, 0xfffffbab},/*kerning L O : -4.349020 */ +{'[', 0x0051004c, 0xfffffbab},/*kerning L Q : -4.349020 */ +{'[', 0x0054004c, 0xffffedab},/*kerning L T : -18.403921 */ +{'[', 0x0055004c, 0xfffffc67},/*kerning L U : -3.611765 */ +{'[', 0x0056004c, 0xfffff456},/*kerning L V : -11.709804 */ +{'[', 0x0057004c, 0xfffff678},/*kerning L W : -9.568627 */ +{'[', 0x0059004c, 0xfffff012},/*kerning L Y : -15.992157 */ +{'[', 0x0075004c, 0xfffffd12},/*kerning L u : -2.941176 */ +{'[', 0x0076004c, 0xfffff723},/*kerning L v : -8.898039 */ +{'[', 0x0077004c, 0xfffff9de},/*kerning L w : -6.156863 */ +{'[', 0x0079004c, 0xfffff723},/*kerning L y : -8.898039 */ {'@', 0x0000004d, 0x00007733},/* M x-advance: 119.199219 */ {'M', 0x426e6667, 0xc18f7778}, {'l', 0x41fdddde, 0xc29e4445}, @@ -3593,6 +3816,10 @@ ctx_font_ascii[]={ {'l', 0x00000000, 0xc2c22223}, {'l', 0x41844444, 0x00000000}, {'l', 0x41fe6668, 0x429e4445}, +{'[', 0x0041004d, 0x00000133},/*kerning M A : 1.203922 */ +{'[', 0x0054004d, 0xfffffe12},/*kerning M T : -1.937255 */ +{'[', 0x0058004d, 0x00000122},/*kerning M X : 1.137255 */ +{'[', 0x0059004d, 0xfffffe23},/*kerning M Y : -1.870588 */ {'@', 0x0000004e, 0x00006166},/* N x-advance: 97.398438 */ {'M', 0x42abddde, 0xc2c22223}, {'l', 0x00000000, 0x42c22223}, @@ -3605,6 +3832,10 @@ ctx_font_ascii[]={ {'l', 0x42444445, 0x42962223}, {'l', 0x00000000, 0xc2962223}, {'l', 0x414bbbb8, 0x00000000}, +{'[', 0x0041004e, 0x00000133},/*kerning N A : 1.203922 */ +{'[', 0x0054004e, 0xfffffe12},/*kerning N T : -1.937255 */ +{'[', 0x0058004e, 0x00000122},/*kerning N X : 1.137255 */ +{'[', 0x0059004e, 0xfffffe23},/*kerning N Y : -1.870588 */ {'@', 0x0000004f, 0x00005dee},/* O x-advance: 93.929688 */ {'M', 0x42ac0001, 0xc235ddde}, {'q', 0x00000000, 0x41aeeeef}, @@ -3640,6 +3871,14 @@ ctx_font_ascii[]={ {'q', 0x40dbbbc0, 0xc1144446}, {0, 0x40dbbbc0, 0xc1d5ddde}, {'l', 0x00000000, 0xc0c88890}, +{'[', 0x002c004f, 0xfffff934},/*kerning O , : -6.823529 */ +{'[', 0x002e004f, 0xfffff934},/*kerning O . : -6.823529 */ +{'[', 0x0041004f, 0xfffffe9a},/*kerning O A : -1.403922 */ +{'[', 0x0054004f, 0xfffffe34},/*kerning O T : -1.803922 */ +{'[', 0x0056004f, 0xfffffe89},/*kerning O V : -1.470588 */ +{'[', 0x0058004f, 0xfffffe89},/*kerning O X : -1.470588 */ +{'[', 0x0059004f, 0xfffffd23},/*kerning O Y : -2.874510 */ +{'[', 0x005a004f, 0xfffffe78},/*kerning O Z : -1.537255 */ {'@', 0x00000050, 0x00005622},/* P x-advance: 86.132812 */ {'M', 0x41c11112, 0xc2184445}, {'l', 0x00000000, 0x42184445}, @@ -3660,9 +3899,22 @@ ctx_font_ascii[]={ {'q', 0xc09ddde0, 0xc0bbbbc0}, {0, 0xc1822222, 0xc0bbbbc0}, {'l', 0xc1b77778, 0x00000000}, -{'[', 0x00740050, 0x000000ee},/*kerning*/ -{'[', 0x00760050, 0x00000100},/*kerning*/ -{'[', 0x00790050, 0x00000100},/*kerning*/ +{'[', 0x002c0050, 0xffffea67},/*kerning P , : -21.682352 */ +{'[', 0x002e0050, 0xffffea67},/*kerning P . : -21.682352 */ +{'[', 0x00410050, 0xfffff6cd},/*kerning P A : -9.235294 */ +{'[', 0x004a0050, 0xfffff2ab},/*kerning P J : -13.384314 */ +{'[', 0x00580050, 0xfffffdef},/*kerning P X : -2.074510 */ +{'[', 0x005a0050, 0xfffffe45},/*kerning P Z : -1.737255 */ +{'[', 0x00610050, 0xffffff45},/*kerning P a : -0.733333 */ +{'[', 0x00630050, 0xffffff23},/*kerning P c : -0.866667 */ +{'[', 0x00640050, 0xffffff23},/*kerning P d : -0.866667 */ +{'[', 0x00650050, 0xffffff23},/*kerning P e : -0.866667 */ +{'[', 0x00670050, 0xffffff23},/*kerning P g : -0.866667 */ +{'[', 0x006f0050, 0xffffff23},/*kerning P o : -0.866667 */ +{'[', 0x00710050, 0xffffff23},/*kerning P q : -0.866667 */ +{'[', 0x00740050, 0x000000ee},/*kerning P t : 0.933333 */ +{'[', 0x00760050, 0x00000100},/*kerning P v : 1.003922 */ +{'[', 0x00790050, 0x00000100},/*kerning P y : 1.003922 */ {'@', 0x00000051, 0x00005dee},/* Q x-advance: 93.929688 */ {'M', 0x42ab7778, 0x41066667}, {'4', 0x0040ffbb, 0xff7eff5d}, @@ -3701,6 +3953,10 @@ ctx_font_ascii[]={ {'q', 0x40dbbbc0, 0xc1144446}, {0, 0x40dbbbc0, 0xc1d5ddde}, {'l', 0x00000000, 0xc0c88890}, +{'[', 0x00540051, 0xfffffd23},/*kerning Q T : -2.874510 */ +{'[', 0x00560051, 0xfffffe23},/*kerning Q V : -1.870588 */ +{'[', 0x00570051, 0xfffffeab},/*kerning Q W : -1.337255 */ +{'[', 0x00590051, 0xfffffdab},/*kerning Q Y : -2.341177 */ {'@', 0x00000052, 0x00005422},/* R x-advance: 84.132812 */ {'M', 0x42880000, 0x00000000}, {'l', 0xc1a88888, 0xc21d5556}, @@ -3724,6 +3980,9 @@ ctx_font_ascii[]={ {'q', 0xc0a00000, 0xc0a44450}, {0, 0xc1811112, 0xc0a44450}, {'l', 0xc19a2222, 0x00000000}, +{'[', 0x00540052, 0xfffffaab},/*kerning R T : -5.352941 */ +{'[', 0x00560052, 0xfffffebc},/*kerning R V : -1.270588 */ +{'[', 0x00590052, 0xfffffccd},/*kerning R Y : -3.211765 */ {'@', 0x00000053, 0x00005111},/* S x-advance: 81.066406 */ {'M', 0x427c0001, 0xc1c44445}, {'q', 0x00000000, 0xc0d55554}, @@ -3783,10 +4042,39 @@ ctx_font_ascii[]={ {'l', 0xc14aaaac, 0x00000000}, {'l', 0x00000000, 0xc2ad1112}, {'l', 0xc1f91111, 0x00000000}, -{'[', 0x00540054, 0x00000111},/*kerning*/ -{'[', 0x00560054, 0x00000111},/*kerning*/ -{'[', 0x00570054, 0x00000100},/*kerning*/ -{'[', 0x00590054, 0x00000111},/*kerning*/ +{'[', 0x00200054, 0xfffffd56},/*kerning T : -2.674510 */ +{'[', 0x002c0054, 0xfffff178},/*kerning T , : -14.588235 */ +{'[', 0x002d0054, 0xfffff089},/*kerning T - : -15.525490 */ +{'[', 0x002e0054, 0xfffff178},/*kerning T . : -14.588235 */ +{'[', 0x00410054, 0xfffffabc},/*kerning T A : -5.286274 */ +{'[', 0x00430054, 0xfffffe23},/*kerning T C : -1.870588 */ +{'[', 0x00470054, 0xfffffe23},/*kerning T G : -1.870588 */ +{'[', 0x004a0054, 0xfffff000},/*kerning T J : -16.062746 */ +{'[', 0x004f0054, 0xfffffe23},/*kerning T O : -1.870588 */ +{'[', 0x00510054, 0xfffffe23},/*kerning T Q : -1.870588 */ +{'[', 0x00530054, 0xfffffeef},/*kerning T S : -1.070588 */ +{'[', 0x00540054, 0x00000111},/*kerning T T : 1.070588 */ +{'[', 0x00560054, 0x00000111},/*kerning T V : 1.070588 */ +{'[', 0x00570054, 0x00000100},/*kerning T W : 1.003922 */ +{'[', 0x00590054, 0x00000111},/*kerning T Y : 1.070588 */ +{'[', 0x00610054, 0xfffff878},/*kerning T a : -7.560784 */ +{'[', 0x00630054, 0xfffff967},/*kerning T c : -6.623529 */ +{'[', 0x00640054, 0xfffff967},/*kerning T d : -6.623529 */ +{'[', 0x00650054, 0xfffff967},/*kerning T e : -6.623529 */ +{'[', 0x00670054, 0xfffff967},/*kerning T g : -6.623529 */ +{'[', 0x006d0054, 0xfffff8bc},/*kerning T m : -7.294117 */ +{'[', 0x006e0054, 0xfffff8bc},/*kerning T n : -7.294117 */ +{'[', 0x006f0054, 0xfffff967},/*kerning T o : -6.623529 */ +{'[', 0x00700054, 0xfffff8bc},/*kerning T p : -7.294117 */ +{'[', 0x00710054, 0xfffff967},/*kerning T q : -6.623529 */ +{'[', 0x00720054, 0xfffffb00},/*kerning T r : -5.019608 */ +{'[', 0x00730054, 0xfffff845},/*kerning T s : -7.760784 */ +{'[', 0x00750054, 0xfffff9ab},/*kerning T u : -6.356863 */ +{'[', 0x00760054, 0xfffffb34},/*kerning T v : -4.815686 */ +{'[', 0x00770054, 0xfffffc34},/*kerning T w : -3.811765 */ +{'[', 0x00780054, 0xfffffade},/*kerning T x : -5.152941 */ +{'[', 0x00790054, 0xfffffb34},/*kerning T y : -4.815686 */ +{'[', 0x007a0054, 0xfffffc00},/*kerning T z : -4.015687 */ {'@', 0x00000055, 0x00005888},/* U x-advance: 88.531250 */ {'M', 0x4285999a, 0xc2c22223}, {'4', 0x00000066, 0x020d0000}, @@ -3808,6 +4096,7 @@ ctx_font_ascii[]={ {'q', 0x40c44448, 0xc0aeeef2}, {0, 0x40c44448, 0xc186eef0}, {'l', 0x00000000, 0xc2835556}, +{'[', 0x00410055, 0xfffffe89},/*kerning U A : -1.470588 */ {'@', 0x00000056, 0x000056ee},/* V x-advance: 86.929688 */ {'M', 0x42aa4445, 0xc2c22223}, {'l', 0xc20fbbbd, 0x42c22223}, @@ -3817,9 +4106,28 @@ ctx_font_ascii[]={ {'l', 0x41dc4444, 0x42a00001}, {'l', 0x41de6668, 0xc2a00001}, {'l', 0x415eeef0, 0x00000000}, -{'[', 0x00290056, 0x00000155},/*kerning*/ -{'[', 0x005d0056, 0x00000122},/*kerning*/ -{'[', 0x007d0056, 0x00000144},/*kerning*/ +{'[', 0x00290056, 0x00000155},/*kerning V ) : 1.337255 */ +{'[', 0x002c0056, 0xfffff100},/*kerning V , : -15.058824 */ +{'[', 0x002d0056, 0xfffffd89},/*kerning V - : -2.474510 */ +{'[', 0x002e0056, 0xfffff100},/*kerning V . : -15.058824 */ +{'[', 0x00410056, 0xfffffb00},/*kerning V A : -5.019608 */ +{'[', 0x00430056, 0xffffff23},/*kerning V C : -0.866667 */ +{'[', 0x00470056, 0xffffff23},/*kerning V G : -0.866667 */ +{'[', 0x004f0056, 0xffffff23},/*kerning V O : -0.866667 */ +{'[', 0x00510056, 0xffffff23},/*kerning V Q : -0.866667 */ +{'[', 0x005d0056, 0x00000122},/*kerning V ] : 1.137255 */ +{'[', 0x00610056, 0xfffffcef},/*kerning V a : -3.078431 */ +{'[', 0x00630056, 0xfffffd12},/*kerning V c : -2.941176 */ +{'[', 0x00640056, 0xfffffd12},/*kerning V d : -2.941176 */ +{'[', 0x00650056, 0xfffffd12},/*kerning V e : -2.941176 */ +{'[', 0x00670056, 0xfffffd12},/*kerning V g : -2.941176 */ +{'[', 0x006f0056, 0xfffffcef},/*kerning V o : -3.078431 */ +{'[', 0x00710056, 0xfffffd12},/*kerning V q : -2.941176 */ +{'[', 0x00720056, 0xfffffe00},/*kerning V r : -2.007843 */ +{'[', 0x00750056, 0xfffffe23},/*kerning V u : -1.870588 */ +{'[', 0x00760056, 0xffffff45},/*kerning V v : -0.733333 */ +{'[', 0x00790056, 0xffffff45},/*kerning V y : -0.733333 */ +{'[', 0x007d0056, 0x00000144},/*kerning V } : 1.270588 */ {'@', 0x00000057, 0x00007922},/* W x-advance: 121.132812 */ {'M', 0x42ec6667, 0xc2c22223}, {'l', 0xc1bbbbbc, 0x42c22223}, @@ -3841,10 +4149,23 @@ ctx_font_ascii[]={ {'l', 0x40044440, 0xc1533334}, {'l', 0x41700000, 0xc284aaab}, {'l', 0x414ddde0, 0x00000000}, -{'[', 0x00290057, 0x00000100},/*kerning*/ -{'[', 0x00540057, 0x000000ee},/*kerning*/ -{'[', 0x005d0057, 0x000000cc},/*kerning*/ -{'[', 0x007d0057, 0x000000ee},/*kerning*/ +{'[', 0x00290057, 0x00000100},/*kerning W ) : 1.003922 */ +{'[', 0x002c0057, 0xfffff7cd},/*kerning W , : -8.231373 */ +{'[', 0x002d0057, 0xfffffc00},/*kerning W - : -4.015687 */ +{'[', 0x002e0057, 0xfffff7cd},/*kerning W . : -8.231373 */ +{'[', 0x00410057, 0xfffffd23},/*kerning W A : -2.874510 */ +{'[', 0x00540057, 0x000000ee},/*kerning W T : 0.933333 */ +{'[', 0x005d0057, 0x000000cc},/*kerning W ] : 0.800000 */ +{'[', 0x00610057, 0xfffffdcd},/*kerning W a : -2.207843 */ +{'[', 0x00630057, 0xfffffdef},/*kerning W c : -2.074510 */ +{'[', 0x00640057, 0xfffffdef},/*kerning W d : -2.074510 */ +{'[', 0x00650057, 0xfffffdef},/*kerning W e : -2.074510 */ +{'[', 0x00670057, 0xfffffdef},/*kerning W g : -2.074510 */ +{'[', 0x006f0057, 0xfffffdef},/*kerning W o : -2.074510 */ +{'[', 0x00710057, 0xfffffdef},/*kerning W q : -2.074510 */ +{'[', 0x00720057, 0xfffffe9a},/*kerning W r : -1.403922 */ +{'[', 0x00750057, 0xfffffebc},/*kerning W u : -1.270588 */ +{'[', 0x007d0057, 0x000000ee},/*kerning W } : 0.933333 */ {'@', 0x00000058, 0x00005599},/* X x-advance: 85.597656 */ {'M', 0x419ccccd, 0xc2c22223}, {'l', 0x41baaaab, 0x4214ccce}, @@ -3859,7 +4180,21 @@ ctx_font_ascii[]={ {'l', 0x41fb3335, 0xc2440001}, {'l', 0xc1f55557, 0xc2404445}, {'l', 0x41700000, 0x00000000}, -{'[', 0x00560058, 0x000000ee},/*kerning*/ +{'[', 0x002d0058, 0xfffffcef},/*kerning X - : -3.078431 */ +{'[', 0x00430058, 0xfffffe56},/*kerning X C : -1.670588 */ +{'[', 0x00470058, 0xfffffe56},/*kerning X G : -1.670588 */ +{'[', 0x004f0058, 0xfffffe56},/*kerning X O : -1.670588 */ +{'[', 0x00510058, 0xfffffe56},/*kerning X Q : -1.670588 */ +{'[', 0x00560058, 0x000000ee},/*kerning X V : 0.933333 */ +{'[', 0x00630058, 0xfffffe45},/*kerning X c : -1.737255 */ +{'[', 0x00640058, 0xfffffe45},/*kerning X d : -1.737255 */ +{'[', 0x00650058, 0xfffffe45},/*kerning X e : -1.737255 */ +{'[', 0x00670058, 0xfffffe45},/*kerning X g : -1.737255 */ +{'[', 0x006f0058, 0xfffffe9a},/*kerning X o : -1.403922 */ +{'[', 0x00710058, 0xfffffe45},/*kerning X q : -1.737255 */ +{'[', 0x00750058, 0xfffffe9a},/*kerning X u : -1.403922 */ +{'[', 0x00760058, 0xfffffdef},/*kerning X v : -2.074510 */ +{'[', 0x00790058, 0xfffffdef},/*kerning X y : -2.074510 */ {'@', 0x00000059, 0x00005200},/* Y x-advance: 82.000000 */ {'M', 0x417bbbbd, 0xc2c22223}, {'l', 0x41c9999a, 0x4242eef0}, @@ -3871,14 +4206,46 @@ ctx_font_ascii[]={ {'l', 0x00000000, 0xc2111112}, {'l', 0xc205ddde, 0xc2733334}, {'l', 0x416bbbbd, 0x00000000}, -{'[', 0x00290059, 0x00000155},/*kerning*/ -{'[', 0x00540059, 0x00000122},/*kerning*/ -{'[', 0x00560059, 0x00000133},/*kerning*/ -{'[', 0x00570059, 0x00000122},/*kerning*/ -{'[', 0x00580059, 0x000000dd},/*kerning*/ -{'[', 0x00590059, 0x00000133},/*kerning*/ -{'[', 0x005d0059, 0x00000133},/*kerning*/ -{'[', 0x007d0059, 0x00000144},/*kerning*/ +{'[', 0x00260059, 0xfffffe00},/*kerning Y & : -2.007843 */ +{'[', 0x00290059, 0x00000155},/*kerning Y ) : 1.337255 */ +{'[', 0x002a0059, 0xfffffcbc},/*kerning Y * : -3.278431 */ +{'[', 0x002c0059, 0xfffff1ef},/*kerning Y , : -14.121569 */ +{'[', 0x002d0059, 0xfffffc89},/*kerning Y - : -3.478431 */ +{'[', 0x002e0059, 0xfffff1ef},/*kerning Y . : -14.121569 */ +{'[', 0x00410059, 0xfffff9bc},/*kerning Y A : -6.290196 */ +{'[', 0x00430059, 0xfffffe12},/*kerning Y C : -1.937255 */ +{'[', 0x00470059, 0xfffffe12},/*kerning Y G : -1.937255 */ +{'[', 0x004a0059, 0xfffff99a},/*kerning Y J : -6.423530 */ +{'[', 0x004f0059, 0xfffffe12},/*kerning Y O : -1.937255 */ +{'[', 0x00510059, 0xfffffe12},/*kerning Y Q : -1.937255 */ +{'[', 0x00530059, 0xfffffeef},/*kerning Y S : -1.070588 */ +{'[', 0x00540059, 0x00000122},/*kerning Y T : 1.137255 */ +{'[', 0x00550059, 0xfffff99a},/*kerning Y U : -6.423530 */ +{'[', 0x00560059, 0x00000133},/*kerning Y V : 1.203922 */ +{'[', 0x00570059, 0x00000122},/*kerning Y W : 1.137255 */ +{'[', 0x00580059, 0x000000dd},/*kerning Y X : 0.866667 */ +{'[', 0x00590059, 0x00000133},/*kerning Y Y : 1.203922 */ +{'[', 0x005d0059, 0x00000133},/*kerning Y ] : 1.203922 */ +{'[', 0x00610059, 0xfffffb23},/*kerning Y a : -4.882353 */ +{'[', 0x00630059, 0xfffffbab},/*kerning Y c : -4.349020 */ +{'[', 0x00640059, 0xfffffbab},/*kerning Y d : -4.349020 */ +{'[', 0x00650059, 0xfffffbab},/*kerning Y e : -4.349020 */ +{'[', 0x00660059, 0xfffffe89},/*kerning Y f : -1.470588 */ +{'[', 0x00670059, 0xfffffbab},/*kerning Y g : -4.349020 */ +{'[', 0x006d0059, 0xfffffd56},/*kerning Y m : -2.674510 */ +{'[', 0x006e0059, 0xfffffd56},/*kerning Y n : -2.674510 */ +{'[', 0x006f0059, 0xfffffbab},/*kerning Y o : -4.349020 */ +{'[', 0x00700059, 0xfffffd56},/*kerning Y p : -2.674510 */ +{'[', 0x00710059, 0xfffffbab},/*kerning Y q : -4.349020 */ +{'[', 0x00720059, 0xfffffd56},/*kerning Y r : -2.674510 */ +{'[', 0x00730059, 0xfffffc23},/*kerning Y s : -3.878431 */ +{'[', 0x00740059, 0xfffffe89},/*kerning Y t : -1.470588 */ +{'[', 0x00750059, 0xfffffd67},/*kerning Y u : -2.607843 */ +{'[', 0x00760059, 0xfffffeab},/*kerning Y v : -1.337255 */ +{'[', 0x00780059, 0xfffffe78},/*kerning Y x : -1.537255 */ +{'[', 0x00790059, 0xfffffeab},/*kerning Y y : -1.337255 */ +{'[', 0x007a0059, 0xfffffe00},/*kerning Y z : -2.007843 */ +{'[', 0x007d0059, 0x00000144},/*kerning Y } : 1.270588 */ {'@', 0x0000005a, 0x000051cc},/* Z x-advance: 81.796875 */ {'M', 0x40d55556, 0xc2ad1112}, {'l', 0x00000000, 0xc1288888}, @@ -3891,7 +4258,21 @@ ctx_font_ascii[]={ {'l', 0xb6400000, 0xc119999a}, {'l', 0x4254ccce, 0xc299dddf}, {'l', 0xc2515556, 0x00000000}, -{'[', 0x0041005a, 0x000000dd},/*kerning*/ +{'[', 0x0041005a, 0x000000dd},/*kerning Z A : 0.866667 */ +{'[', 0x0043005a, 0xfffffe45},/*kerning Z C : -1.737255 */ +{'[', 0x0047005a, 0xfffffe45},/*kerning Z G : -1.737255 */ +{'[', 0x004f005a, 0xfffffe45},/*kerning Z O : -1.737255 */ +{'[', 0x0051005a, 0xfffffe45},/*kerning Z Q : -1.737255 */ +{'[', 0x0063005a, 0xfffffe9a},/*kerning Z c : -1.403922 */ +{'[', 0x0064005a, 0xfffffe9a},/*kerning Z d : -1.403922 */ +{'[', 0x0065005a, 0xfffffe9a},/*kerning Z e : -1.403922 */ +{'[', 0x0067005a, 0xfffffe9a},/*kerning Z g : -1.403922 */ +{'[', 0x006f005a, 0xfffffe9a},/*kerning Z o : -1.403922 */ +{'[', 0x0071005a, 0xfffffe9a},/*kerning Z q : -1.403922 */ +{'[', 0x0075005a, 0xfffffebc},/*kerning Z u : -1.270588 */ +{'[', 0x0076005a, 0xfffffe34},/*kerning Z v : -1.803922 */ +{'[', 0x0077005a, 0xfffffe34},/*kerning Z w : -1.803922 */ +{'[', 0x0079005a, 0xfffffe34},/*kerning Z y : -1.803922 */ {'@', 0x0000005b, 0x00002433},/* [ x-advance: 36.199219 */ {'M', 0x420b7778, 0xc2dddddf}, {'l', 0x00000000, 0x41222228}, @@ -3902,6 +4283,8 @@ ctx_font_ascii[]={ {'l', 0xc1c8888a, 0x00000000}, {'l', 0x35800000, 0xc303bbbc}, {'l', 0x41c8888a, 0xb7000000}, +{'[', 0x004a005b, 0xfffffecd},/*kerning [ J : -1.203922 */ +{'[', 0x0055005b, 0xfffffecd},/*kerning [ U : -1.203922 */ {'@', 0x0000005c, 0x00003811},/* \ x-advance: 56.066406 */ {'M', 0x422d1112, 0x41055556}, {'l', 0xc2222223, 0xc2d2ccce}, @@ -3971,6 +4354,10 @@ ctx_font_ascii[]={ {'q', 0xc1a66666, 0x3ecccd00}, {0, 0xc1a66666, 0x41555558}, {'8', 0x451b2900, 0x1c521c1b}, +{'[', 0x00220061, 0xfffffb89},/*kerning a " : -4.482353 */ +{'[', 0x00270061, 0xfffffb89},/*kerning a ' : -4.482353 */ +{'[', 0x00760061, 0xffffff00},/*kerning a v : -1.003922 */ +{'[', 0x00790061, 0xffffff00},/*kerning a y : -1.003922 */ {'@', 0x00000062, 0x00004caa},/* b x-advance: 76.664062 */ {'M', 0x428ceeef, 0xc20d1112}, {'q', 0x00000000, 0x417cccd0}, @@ -3999,6 +4386,12 @@ ctx_font_ascii[]={ {0, 0xc0866668, 0xc1944446}, {'q', 0xc0844440, 0xc0fbbbb8}, {0, 0xc16aaaac, 0xc0fbbbb8}, +{'[', 0x00220062, 0xfffffe12},/*kerning b " : -1.937255 */ +{'[', 0x00270062, 0xfffffe12},/*kerning b ' : -1.937255 */ +{'[', 0x00760062, 0xffffff45},/*kerning b v : -0.733333 */ +{'[', 0x00780062, 0xffffff00},/*kerning b x : -1.003922 */ +{'[', 0x00790062, 0xffffff45},/*kerning b y : -0.733333 */ +{'[', 0x007a0062, 0xffffff00},/*kerning b z : -1.003922 */ {'@', 0x00000063, 0x00004777},/* c x-advance: 71.464844 */ {'M', 0x42191112, 0xc10ccccd}, {'8', 0xe15c0034, 0xb02be128}, @@ -4026,6 +4419,8 @@ ctx_font_ascii[]={ {'8', 0x620d3200, 0x4e302f0d}, {'q', 0x408aaaac, 0x40733334}, {0, 0x41400002, 0x40733334}, +{'[', 0x00220063, 0xffffff45},/*kerning c " : -0.733333 */ +{'[', 0x00270063, 0xffffff45},/*kerning c ' : -0.733333 */ {'@', 0x00000064, 0x00004d00},/* d x-advance: 77.000000 */ {'M', 0x425fbbbc, 0x00000000}, {'l', 0xbf199980, 0xc0f77778}, @@ -4087,6 +4482,10 @@ ctx_font_ascii[]={ {'9', 0x002affd8, 0x007affce}, {'4', 0x00000120, 0xfff90000}, {'8', 0x95dfc7fd, 0xce97cee3}, +{'[', 0x00220065, 0xffffff12},/*kerning e " : -0.933333 */ +{'[', 0x00270065, 0xffffff12},/*kerning e ' : -0.933333 */ +{'[', 0x00760065, 0xffffff23},/*kerning e v : -0.866667 */ +{'[', 0x00790065, 0xffffff23},/*kerning e y : -0.866667 */ {'@', 0x00000066, 0x00002f77},/* f x-advance: 47.464844 */ {'M', 0x422c8889, 0xc27aaaac}, {'l', 0xc1755556, 0x00000000}, @@ -4104,11 +4503,16 @@ ctx_font_ascii[]={ {'l', 0x00000000, 0x41000000}, {'l', 0x41755556, 0x00000000}, {'l', 0x00000000, 0x41177778}, -{'[', 0x00220066, 0x00000111},/*kerning*/ -{'[', 0x00270066, 0x00000111},/*kerning*/ -{'[', 0x00290066, 0x00000155},/*kerning*/ -{'[', 0x005d0066, 0x00000133},/*kerning*/ -{'[', 0x007d0066, 0x00000144},/*kerning*/ +{'[', 0x00220066, 0x00000111},/*kerning f " : 1.070588 */ +{'[', 0x00270066, 0x00000111},/*kerning f ' : 1.070588 */ +{'[', 0x00290066, 0x00000155},/*kerning f ) : 1.337255 */ +{'[', 0x005d0066, 0x00000133},/*kerning f ] : 1.203922 */ +{'[', 0x00630066, 0xfffffe67},/*kerning f c : -1.603922 */ +{'[', 0x00640066, 0xfffffe67},/*kerning f d : -1.603922 */ +{'[', 0x00650066, 0xfffffe67},/*kerning f e : -1.603922 */ +{'[', 0x00670066, 0xfffffe67},/*kerning f g : -1.603922 */ +{'[', 0x00710066, 0xfffffe67},/*kerning f q : -1.603922 */ +{'[', 0x007d0066, 0x00000144},/*kerning f } : 1.270588 */ {'@', 0x00000067, 0x00004caa},/* g x-advance: 76.664062 */ {'M', 0x42133334, 0x41e3bbbd}, {'8', 0xeb9300d4, 0xb298ebbf}, @@ -4163,6 +4567,8 @@ ctx_font_ascii[]={ {'4', 0x017c0000, 0x0000ff9d}, {'l', 0x00000000, 0xc23d999a}, {'8', 0xa0e3bd00, 0xe4abe4e3}, +{'[', 0x00220068, 0xfffff912},/*kerning h " : -6.956863 */ +{'[', 0x00270068, 0xfffff912},/*kerning h ' : -6.956863 */ {'@', 0x00000069, 0x00002133},/* i x-advance: 33.199219 */ {'M', 0x41177778, 0xc2b68889}, {'8', 0xd80ee800, 0xf02bf00e}, @@ -4203,6 +4609,11 @@ ctx_font_ascii[]={ {'l', 0xc1e11112, 0x41f0888a}, {'l', 0x41fb3336, 0x42284445}, {'l', 0xc168888c, 0x00000000}, +{'[', 0x0063006b, 0xfffffeab},/*kerning k c : -1.337255 */ +{'[', 0x0064006b, 0xfffffeab},/*kerning k d : -1.337255 */ +{'[', 0x0065006b, 0xfffffeab},/*kerning k e : -1.337255 */ +{'[', 0x0067006b, 0xfffffeab},/*kerning k g : -1.337255 */ +{'[', 0x0071006b, 0xfffffeab},/*kerning k q : -1.337255 */ {'@', 0x0000006c, 0x00002133},/* l x-advance: 33.199219 */ {'M', 0x41b66667, 0xc2ccccce}, {'l', 0x00000000, 0x42ccccce}, @@ -4230,6 +4641,8 @@ ctx_font_ascii[]={ {'4', 0x017e0000, 0x0000ff9e}, {'l', 0x00000000, 0xc23ddddf}, {'8', 0xa0dfbd00, 0xe4a7e4df}, +{'[', 0x0022006d, 0xfffff912},/*kerning m " : -6.956863 */ +{'[', 0x0027006d, 0xfffff912},/*kerning m ' : -6.956863 */ {'@', 0x0000006e, 0x00004b66},/* n x-advance: 75.398438 */ {'M', 0x421d5556, 0xc27c4445}, {'8', 0x19ad00d1, 0x42c719dc}, @@ -4245,6 +4658,8 @@ ctx_font_ascii[]={ {'4', 0x017c0000, 0x0000ff9d}, {'l', 0x00000000, 0xc23d999a}, {'8', 0xa0e3bd00, 0xe4abe4e3}, +{'[', 0x0022006e, 0xfffff912},/*kerning n " : -6.956863 */ +{'[', 0x0027006e, 0xfffff912},/*kerning n ' : -6.956863 */ {'@', 0x0000006f, 0x00004ddd},/* o x-advance: 77.863281 */ {'M', 0x40c44445, 0xc2133334}, {'q', 0x00000000, 0xc17aaaac}, @@ -4280,6 +4695,12 @@ ctx_font_ascii[]={ {'q', 0xc0a22224, 0x40fddde0}, {0, 0xc0a22224, 0x41944444}, {'l', 0x00000000, 0x3fc44440}, +{'[', 0x0022006f, 0xfffff6ef},/*kerning o " : -9.101961 */ +{'[', 0x0027006f, 0xfffff6ef},/*kerning o ' : -9.101961 */ +{'[', 0x0076006f, 0xffffff00},/*kerning o v : -1.003922 */ +{'[', 0x0078006f, 0xfffffe9a},/*kerning o x : -1.403922 */ +{'[', 0x0079006f, 0xffffff00},/*kerning o y : -1.003922 */ +{'[', 0x007a006f, 0xfffffeef},/*kerning o z : -1.070588 */ {'@', 0x00000070, 0x00004caa},/* p x-advance: 76.664062 */ {'M', 0x42295556, 0x3faaaaab}, {'9', 0x0000ff98, 0xffbeff5c}, @@ -4309,6 +4730,12 @@ ctx_font_ascii[]={ {0, 0xc0955558, 0xc1944446}, {'q', 0xc0933338, 0xc0fbbbb8}, {0, 0xc16bbbbc, 0xc0fbbbb8}, +{'[', 0x00220070, 0xfffffe12},/*kerning p " : -1.937255 */ +{'[', 0x00270070, 0xfffffe12},/*kerning p ' : -1.937255 */ +{'[', 0x00760070, 0xffffff45},/*kerning p v : -0.733333 */ +{'[', 0x00780070, 0xffffff00},/*kerning p x : -1.003922 */ +{'[', 0x00790070, 0xffffff45},/*kerning p y : -0.733333 */ +{'[', 0x007a0070, 0xffffff00},/*kerning p z : -1.003922 */ {'@', 0x00000071, 0x00004d99},/* q x-advance: 77.597656 */ {'M', 0x42866667, 0xc2904445}, {'4', 0x031e0000, 0x0000ff9d}, @@ -4352,13 +4779,22 @@ ctx_font_ascii[]={ {'l', 0xbd888a00, 0x4137777c}, {'q', 0xc02eeef0, 0xbf088880}, {0, 0xc0c00000, 0xbf088880}, -{'[', 0x00220072, 0x00000111},/*kerning*/ -{'[', 0x00270072, 0x00000111},/*kerning*/ -{'[', 0x00660072, 0x00000100},/*kerning*/ -{'[', 0x00740072, 0x00000355},/*kerning*/ -{'[', 0x00760072, 0x00000133},/*kerning*/ -{'[', 0x00770072, 0x00000122},/*kerning*/ -{'[', 0x00790072, 0x00000133},/*kerning*/ +{'[', 0x00220072, 0x00000111},/*kerning r " : 1.070588 */ +{'[', 0x00270072, 0x00000111},/*kerning r ' : 1.070588 */ +{'[', 0x002c0072, 0xfffff7cd},/*kerning r , : -8.231373 */ +{'[', 0x002e0072, 0xfffff7cd},/*kerning r . : -8.231373 */ +{'[', 0x00610072, 0xfffffd56},/*kerning r a : -2.674510 */ +{'[', 0x00630072, 0xfffffebc},/*kerning r c : -1.270588 */ +{'[', 0x00640072, 0xfffffebc},/*kerning r d : -1.270588 */ +{'[', 0x00650072, 0xfffffebc},/*kerning r e : -1.270588 */ +{'[', 0x00660072, 0x00000100},/*kerning r f : 1.003922 */ +{'[', 0x00670072, 0xfffffebc},/*kerning r g : -1.270588 */ +{'[', 0x006f0072, 0xfffffeab},/*kerning r o : -1.337255 */ +{'[', 0x00710072, 0xfffffebc},/*kerning r q : -1.270588 */ +{'[', 0x00740072, 0x00000355},/*kerning r t : 3.345098 */ +{'[', 0x00760072, 0x00000133},/*kerning r v : 1.203922 */ +{'[', 0x00770072, 0x00000122},/*kerning r w : 1.137255 */ +{'[', 0x00790072, 0x00000133},/*kerning r y : 1.203922 */ {'@', 0x00000073, 0x00004677},/* s x-advance: 70.464844 */ {'M', 0x424d999a, 0xc1991112}, {'8', 0xc8e9e100, 0xd696e7ea}, @@ -4409,6 +4845,7 @@ ctx_font_ascii[]={ {'q', 0x40155550, 0x00000000}, {0, 0x40b99998, 0xbf4cccd0}, {'l', 0x3d888800, 0x41211112}, +{'[', 0x006f0074, 0xfffffeab},/*kerning t o : -1.337255 */ {'@', 0x00000075, 0x00004b44},/* u x-advance: 75.265625 */ {'M', 0x42588889, 0x00000000}, {'l', 0xbe888880, 0xc0e44445}, @@ -4434,9 +4871,18 @@ ctx_font_ascii[]={ {'l', 0x4192aaaa, 0x425d1112}, {'l', 0x418eeef0, 0xc25d1112}, {'l', 0x4149999c, 0x00000000}, -{'[', 0x00220076, 0x00000100},/*kerning*/ -{'[', 0x00270076, 0x00000100},/*kerning*/ -{'[', 0x00660076, 0x000000dd},/*kerning*/ +{'[', 0x00220076, 0x00000100},/*kerning v " : 1.003922 */ +{'[', 0x00270076, 0x00000100},/*kerning v ' : 1.003922 */ +{'[', 0x002c0076, 0xfffff8de},/*kerning v , : -7.160784 */ +{'[', 0x002e0076, 0xfffff8de},/*kerning v . : -7.160784 */ +{'[', 0x00610076, 0xffffff00},/*kerning v a : -1.003922 */ +{'[', 0x00630076, 0xffffff23},/*kerning v c : -0.866667 */ +{'[', 0x00640076, 0xffffff23},/*kerning v d : -0.866667 */ +{'[', 0x00650076, 0xffffff23},/*kerning v e : -0.866667 */ +{'[', 0x00660076, 0x000000dd},/*kerning v f : 0.866667 */ +{'[', 0x00670076, 0xffffff23},/*kerning v g : -0.866667 */ +{'[', 0x006f0076, 0xffffff00},/*kerning v o : -1.003922 */ +{'[', 0x00710076, 0xffffff23},/*kerning v q : -0.866667 */ {'@', 0x00000077, 0x00006699},/* w x-advance: 102.597656 */ {'M', 0x42c6cccd, 0xc2904445}, {'l', 0xc1a77778, 0x42904445}, @@ -4452,6 +4898,8 @@ ctx_font_ascii[]={ {'l', 0x4188888a, 0x425c0001}, {'l', 0x415eeef0, 0xc25c0001}, {'l', 0x41444440, 0x00000000}, +{'[', 0x002c0077, 0xfffff7bc},/*kerning w , : -8.298039 */ +{'[', 0x002e0077, 0xfffff7bc},/*kerning w . : -8.298039 */ {'@', 0x00000078, 0x000043bb},/* x x-advance: 67.730469 */ {'M', 0x418dddde, 0xc2904445}, {'l', 0x417cccd0, 0x41d22224}, @@ -4466,6 +4914,12 @@ ctx_font_ascii[]={ {'l', 0x41c22222, 0xc2122223}, {'l', 0xc1bc4444, 0xc20e6667}, {'l', 0x41633334, 0x00000000}, +{'[', 0x00630078, 0xfffffeab},/*kerning x c : -1.337255 */ +{'[', 0x00640078, 0xfffffeab},/*kerning x d : -1.337255 */ +{'[', 0x00650078, 0xfffffeab},/*kerning x e : -1.337255 */ +{'[', 0x00670078, 0xfffffeab},/*kerning x g : -1.337255 */ +{'[', 0x006f0078, 0xfffffeab},/*kerning x o : -1.337255 */ +{'[', 0x00710078, 0xfffffeab},/*kerning x q : -1.337255 */ {'@', 0x00000079, 0x00004099},/* y x-advance: 64.597656 */ {'M', 0x42080000, 0x41322223}, {'9', 0x0087ffcd, 0x008fff66}, @@ -4480,9 +4934,18 @@ ctx_font_ascii[]={ {'l', 0x4185dde0, 0xc2580001}, {'l', 0x41533334, 0x00000000}, {'l', 0xc1e7777a, 0x42a68889}, -{'[', 0x00220079, 0x00000100},/*kerning*/ -{'[', 0x00270079, 0x00000100},/*kerning*/ -{'[', 0x00660079, 0x000000dd},/*kerning*/ +{'[', 0x00220079, 0x00000100},/*kerning y " : 1.003922 */ +{'[', 0x00270079, 0x00000100},/*kerning y ' : 1.003922 */ +{'[', 0x002c0079, 0xfffff8de},/*kerning y , : -7.160784 */ +{'[', 0x002e0079, 0xfffff8de},/*kerning y . : -7.160784 */ +{'[', 0x00610079, 0xffffff00},/*kerning y a : -1.003922 */ +{'[', 0x00630079, 0xffffff23},/*kerning y c : -0.866667 */ +{'[', 0x00640079, 0xffffff23},/*kerning y d : -0.866667 */ +{'[', 0x00650079, 0xffffff23},/*kerning y e : -0.866667 */ +{'[', 0x00660079, 0x000000dd},/*kerning y f : 0.866667 */ +{'[', 0x00670079, 0xffffff23},/*kerning y g : -0.866667 */ +{'[', 0x006f0079, 0xffffff00},/*kerning y o : -1.003922 */ +{'[', 0x00710079, 0xffffff23},/*kerning y q : -0.866667 */ {'@', 0x0000007a, 0x000043bb},/* z x-advance: 67.730469 */ {'M', 0x40ceeef0, 0xc277bbbd}, {'l', 0x00000000, 0xc1233334}, @@ -4495,6 +4958,12 @@ ctx_font_ascii[]={ {'l', 0xb5000000, 0xc1111112}, {'l', 0x421eeeef, 0xc2537778}, {'l', 0xc21ccccd, 0xb6800000}, +{'[', 0x0063007a, 0xfffffeef},/*kerning z c : -1.070588 */ +{'[', 0x0064007a, 0xfffffeef},/*kerning z d : -1.070588 */ +{'[', 0x0065007a, 0xfffffeef},/*kerning z e : -1.070588 */ +{'[', 0x0067007a, 0xfffffeef},/*kerning z g : -1.070588 */ +{'[', 0x006f007a, 0xfffffeef},/*kerning z o : -1.070588 */ +{'[', 0x0071007a, 0xfffffeef},/*kerning z q : -1.070588 */ {'@', 0x0000007b, 0x00002e33},/* { x-advance: 46.199219 */ {'M', 0x40888889, 0xc210cccd}, {'l', 0x00000000, 0xc11aaaac}, @@ -4522,6 +4991,8 @@ ctx_font_ascii[]={ {'l', 0x00000000, 0xc1555556}, {'q', 0x00000000, 0xc1822222}, {0, 0xc1622224, 0xc1822222}, +{'[', 0x004a007b, 0xfffffeab},/*kerning { J : -1.337255 */ +{'[', 0x0055007b, 0xfffffeab},/*kerning { U : -1.337255 */ {'@', 0x0000007c, 0x00002155},/* | x-advance: 33.332031 */ {'M', 0x41ad5556, 0xc2c22223}, {'l', 0x00000000, 0x42e62223}, @@ -4585,6 +5056,7 @@ ctx_font_ascii[]={ #ifndef CTX_EXTERNAL_MALLOC static inline void *ctx_realloc (void *mem, size_t old_size, size_t new_size) { + if (old_size){}; return (void*)realloc (mem, new_size); } @@ -4912,13 +5384,13 @@ static inline void ctx_list_insert_sorted (CtxList **list, static inline CtxList *ctx_list_find_custom (CtxList *list, void *needle, - int(*compare)(const void *a, const void *b), + int(*compare)(const void *a, const void *b, void *userdata), void *userdata) { CtxList *l; for (l = list; l; l = l->next) { - if (compare (l->data, needle) == 0) + if (compare (l->data, needle, userdata) == 0) return l; } return NULL; @@ -4947,13 +5419,10 @@ static inline CtxList *ctx_list_find_custom (CtxList *list, #define CTX_BACKEND_TEXT 1 #endif +#ifndef CTX_MAX_SCANLINES +#define CTX_MAX_SCANLINES 2048 +#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) /* subpixel-aa coordinates used in BITPACKing of drawlist * @@ -5001,6 +5470,13 @@ static inline CtxList *ctx_list_find_custom (CtxList *list, #endif #endif +#ifndef CTX_RASTERIZER_ALLOW_DIRECT +#define CTX_RASTERIZER_ALLOW_DIRECT 1 +#endif + +#ifndef CTX_RASTERIZER_BEZIER_FIXED_POINT +#define CTX_RASTERIZER_BEZIER_FIXED_POINT 1 +#endif #ifndef CTX_FAST_FILL_RECT #define CTX_FAST_FILL_RECT 1 /* matters most for tiny rectangles where it shaves overhead, for larger rectangles @@ -5008,7 +5484,7 @@ static inline CtxList *ctx_list_find_custom (CtxList *list, #endif #ifndef CTX_FAST_STROKE_RECT -#define CTX_FAST_STROKE_RECT 0 +#define CTX_FAST_STROKE_RECT 1 #endif @@ -5035,7 +5511,13 @@ static inline CtxList *ctx_list_find_custom (CtxList *list, #ifndef CTX_ENABLE_SHADOW_BLUR -#define CTX_ENABLE_SHADOW_BLUR 0 +#define CTX_ENABLE_SHADOW_BLUR 1 +#endif + +// fudge geomtry slightly with smoother blend between edges, +// busting some SDF artifacts apparent in acute angles +#ifndef CTX_RASTERIZER_BLUR_FUDGE +#define CTX_RASTERIZER_BLUR_FUDGE 0 #endif #ifndef CTX_GRADIENTS @@ -5051,6 +5533,10 @@ static inline CtxList *ctx_list_find_custom (CtxList *list, #endif +#ifndef CTX_FONT_SHAPE_CACHE +#define CTX_FONT_SHAPE_CACHE 0 +#endif + #ifndef CTX_FONTS_FROM_FILE #define CTX_FONTS_FROM_FILE 0 #endif @@ -5115,8 +5601,10 @@ static inline CtxList *ctx_list_find_custom (CtxList *list, #define CTX_MIN_EDGE_LIST_SIZE 1024*4 #endif + +// 3 5 or 15 - this is the AA used for worst-case scanlines; with crossings or edge start|ends #ifndef CTX_RASTERIZER_AA -#define CTX_RASTERIZER_AA 15 // vertical-AA of CTX_ANTIALIAS_DEFAULT +#define CTX_RASTERIZER_AA 5 // vertical-AA of CTX_ANTIALIAS_DEFAULT #endif /* The maximum complexity of a single path @@ -5130,16 +5618,6 @@ static inline CtxList *ctx_list_find_custom (CtxList *list, // entries are "copy-on-change" between states #endif -#ifndef CTX_STRINGPOOL_SIZE - // XXX should be possible to make zero and disappear when codepaths not in use - // to save size, for card10 this is defined as a low number (some text - // properties still make use of it) - // - // for desktop-use this should be fully dynamic, possibly - // with chained pools, gradients are stored here. -#define CTX_STRINGPOOL_SIZE 2000 // -#endif - #ifndef CTX_32BIT_SEGMENTS #define CTX_32BIT_SEGMENTS 1 // without this clipping problems might // occur when drawing far outside the viewport @@ -5180,10 +5658,11 @@ static inline CtxList *ctx_list_find_custom (CtxList *list, /* * do not use manual RGBA8 code but rely on ctx inline templating */ -#ifndef CTX_INLINED_NORMAL +#ifndef CTX_INLINED_NORMAL_RGBA8 #define CTX_INLINED_NORMAL_RGBA8 0 #endif +#undef CTX_RASTERIZER_SWITCH_DISPATCH #ifndef CTX_RASTERIZER_SWITCH_DISPATCH #define CTX_RASTERIZER_SWITCH_DISPATCH 1 // marginal improvement for some // modes, maybe get rid of this? @@ -5336,6 +5815,15 @@ static inline CtxList *ctx_list_find_custom (CtxList *list, #define CTX_FONT_ENGINE_STB 0 #endif + +#if CTX_HARFBUZZ +#ifndef CTX_FONT_ENGINE_HARFBUZZ +#define CTX_FONT_ENGINE_HARFBUZZ 1 +#endif +#else +#define CTX_FONT_ENGINE_HARFBUZZ 0 +#endif + #ifndef CTX_BABL #ifdef _BABL_H #define CTX_BABL 1 @@ -5440,10 +5928,6 @@ static inline CtxList *ctx_list_find_custom (CtxList *list, #define CTX_RASTERIZER_MAX_CIRCLE_SEGMENTS (128) #endif -#ifndef CTX_MAX_FRAMEBUFFER_WIDTH -#define CTX_MAX_FRAMEBUFFER_WIDTH 2560 -#endif - #ifndef CTX_MAX_FONTS #define CTX_MAX_FONTS 32 #endif @@ -5461,14 +5945,9 @@ static inline CtxList *ctx_list_find_custom (CtxList *list, #endif #ifndef CTX_MAX_EDGES -#define CTX_MAX_EDGES 257 +#define CTX_MAX_EDGES 255 #endif -#ifndef CTX_MAX_LINGERING_EDGES -#define CTX_MAX_LINGERING_EDGES 64 -#endif - - #ifndef CTX_MAX_PENDING #define CTX_MAX_PENDING 128 #endif @@ -5496,7 +5975,7 @@ static inline CtxList *ctx_list_find_custom (CtxList *list, #define CTX_FRAGMENT_SPECIALIZE 1 #endif -#define CTX_RASTERIZER_EDGE_MULTIPLIER 1024 +#define CTX_RASTERIZER_EDGE_MULTIPLIER 2048 // increasing this to 2048 // removes artifacts in top half of res-diagram - // but reduces maximum available buffer width @@ -5513,7 +5992,7 @@ static inline CtxList *ctx_list_find_custom (CtxList *list, #ifndef CTX_MAX_CBS -#define CTX_MAX_CBS 1 //128 +#define CTX_MAX_CBS 128 #endif #ifndef static_OPAQUE // causes a CTX_MAX_SCANLINE_LENGTH @@ -5680,7 +6159,7 @@ static inline CtxList *ctx_list_find_custom (CtxList *list, #endif #ifndef CTX_BRANCH_HINTS -#define CTX_BRANCH_HINTS 1 +#define CTX_BRANCH_HINTS 0 #endif #ifdef EMSCRIPTEN @@ -5783,18 +6262,22 @@ static inline CtxList *ctx_list_find_custom (CtxList *list, #define CTX_GSTATE_PROTECT 0 #endif +// only applies with gcc not clang #ifndef CTX_COMPOSITE_O3 #define CTX_COMPOSITE_O3 0 #endif +// only applies with gcc not clang #ifndef CTX_COMPOSITE_O2 #define CTX_COMPOSITE_O2 0 #endif +// only applies with gcc not clang #ifndef CTX_RASTERIZER_O3 #define CTX_RASTERIZER_O3 0 #endif +// only applies with gcc not clang #ifndef CTX_RASTERIZER_O2 #define CTX_RASTERIZER_O2 0 #endif @@ -5814,23 +6297,50 @@ static inline CtxList *ctx_list_find_custom (CtxList *list, #endif #ifndef CTX_VT_STYLE_SIZE -#define CTX_VT_STYLE_SIZE 32 +#define CTX_VT_STYLE_SIZE 64 +#endif + +#ifndef CTX_ASSERT +#define CTX_ASSERT 0 +#endif + + +#ifndef CTX_SCANBIN +#define CTX_SCANBIN 0 #endif + +#ifndef CTX_LOAD_FILE +#define CTX_LOAD_FILE ___ctx_file_get_contents +#endif + +#ifndef CTX_MAGIC +#define CTX_MAGIC 0 +#endif + +#ifndef CTX_CSS +#define CTX_CSS 0 +#endif + +#ifndef CTX_SVG_FREE_AGE +#define CTX_SVG_FREE_AGE 62 +#endif + /* Copyright (C) 2020 Øyvind Kolås */ -#if CTX_FORMATTER +#if CTX_FORMATTER||CTX_AUDIO /* returns the maximum string length including terminating \0 */ int ctx_a85enc_len (int input_length); int ctx_a85enc (const void *srcp, char *dst, int count); + +#endif + #if CTX_PARSER int ctx_a85dec (const char *src, char *dst, int count); int ctx_a85len (const char *src, int count); -#endif - #endif #ifndef __CTX_EXTRA_H #define __CTX_EXTRA_H @@ -5843,11 +6353,19 @@ int ctx_a85len (const char *src, int count); #define CTX_CLAMP(val,min,max) ((val)<(min)?(min):(val)>(max)?(max):(val)) -static CTX_INLINE int ctx_mini (int a, int b) { return (a < b) * a + (a >= b) * b; } -static CTX_INLINE float ctx_minf (float a, float b) { return (a < b) * a + (a >= b) * b; } -static CTX_INLINE int ctx_maxi (int a, int b) { return (a > b) * a + (a <= b) * b; } -static CTX_INLINE float ctx_maxf (float a, float b) { return (a > b) * a + (a <= b) * b; } -static CTX_INLINE float ctx_clampf (float v, float min, float max) { +//static CTX_INLINE int ctx_mini (const int a, const int b) { if (a < b) return a; return b; } +//static CTX_INLINE int ctx_maxi (const int a, const int b) { if (a > b) return a; return b; } +static CTX_INLINE int ctx_mini (const int a, const int b) { +return (a=b)*b; + //if (a < b) return a; return b; +} +static CTX_INLINE int ctx_maxi (const int a, const int b) { +return (a>b)*a+(a<=b)*b; + //if (a > b) return a; return b; +} +static CTX_INLINE float ctx_minf (const float a, const float b) { if (a < b) return a; return b; } +static CTX_INLINE float ctx_maxf (const float a, const float b) { if (a > b) return a; return b; } +static CTX_INLINE float ctx_clampf (const float v, const float min, const float max) { return CTX_CLAMP(v,min,max); } @@ -5865,16 +6383,11 @@ typedef enum CtxOutputmode CTX_OUTPUT_MODE_UI } CtxOutputmode; - - - - - -static inline float ctx_pow2 (float a) { return a * a; } +static CTX_INLINE float ctx_pow2 (const float a) { return a * a; } #if CTX_MATH static CTX_INLINE float -ctx_fabsf (float x) +ctx_fabsf (const float x) { union { @@ -5886,7 +6399,7 @@ ctx_fabsf (float x) } static CTX_INLINE float -ctx_invsqrtf (float x) +ctx_invsqrtf (const float x) { union { @@ -5899,38 +6412,17 @@ ctx_invsqrtf (float x) return u.f; } -static CTX_INLINE float -ctx_invsqrtf_fast (float x) -{ - union - { - float f; - uint32_t i; - } u = { x }; - u.i = 0x5f3759df - (u.i >> 1); - return u.f; -} -CTX_INLINE static float ctx_sqrtf (float a) +CTX_INLINE static float ctx_sqrtf (const 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) +CTX_INLINE static float ctx_hypotf (const float a, const 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) { @@ -5943,13 +6435,13 @@ ctx_sinf (float x) } if (x < -CTX_PI * 1000) { - x = -0.5; + x = -0.5f; } if (x > CTX_PI * 1000) { - // really large numbers tend to cause practically inifinite - // loops since the > CTX_PI * 2 seemingly fails - x = 0.5; + // really large numbers tend to cause practically inifinite + // loops since the > CTX_PI * 2 seemingly fails + x = 0.5f; } if (x > CTX_PI * 2) { @@ -5962,7 +6454,7 @@ ctx_sinf (float x) { x -= CTX_PI * 2; } /* source : http://mooooo.ooo/chebyshev-sine-approximation/ */ - float coeffs[]= + const float coeffs[]= { -0.10132118f, // x 0.0066208798f, // x^3 @@ -5982,7 +6474,7 @@ ctx_sinf (float x) (x + CTX_PI - 0.00000008742278f) * p1 * x; } -static CTX_INLINE float ctx_atan2f (float y, float x) +static CTX_INLINE float ctx_atan2f (const float y, const float x) { float atan, z; if ( x == 0.0f ) @@ -6013,37 +6505,37 @@ static CTX_INLINE float ctx_atan2f (float y, float x) } -static CTX_INLINE float ctx_atanf (float a) +static CTX_INLINE float ctx_atanf (const float a) { - return ctx_atan2f ( (a), 1.0f); + return ctx_atan2f (a, 1.0f); } -static CTX_INLINE float ctx_asinf (float x) +static CTX_INLINE float ctx_asinf (const float x) { - return ctx_atanf ( (x) * (ctx_invsqrtf (1.0f-ctx_pow2 (x) ) ) ); + return ctx_atanf ( x * ctx_invsqrtf (1.0f-ctx_pow2 (x) )); } -static CTX_INLINE float ctx_acosf (float x) +static CTX_INLINE float ctx_acosf (const float x) { - return ctx_atanf ( (ctx_sqrtf (1.0f-ctx_pow2 (x) ) / (x) ) ); + return ctx_atanf ( ctx_sqrtf (1.0f-ctx_pow2 (x) ) / (x) ); } -CTX_INLINE static float ctx_cosf (float a) +CTX_INLINE static float ctx_cosf (const float a) { return ctx_sinf ( (a) + CTX_PI/2.0f); } -static CTX_INLINE float ctx_tanf (float a) +static CTX_INLINE float ctx_tanf (const float a) { - return (ctx_cosf (a) /ctx_sinf (a) ); + return (ctx_cosf (a) / ctx_sinf (a) ); } static CTX_INLINE float -ctx_floorf (float x) +ctx_floorf (const float x) { return (int)x; // XXX } static CTX_INLINE float -ctx_expf (float x) +ctx_expf (const float x) { union { uint32_t i; float f; } v = { (uint32_t)( (1 << 23) * (x + 183.1395965f)) }; @@ -6056,23 +6548,63 @@ ctx_expf (float x) #if !__COSMOPOLITAN__ #include #endif -static inline float ctx_fabsf (float x) { return fabsf (x); } -static inline float ctx_floorf (float x) { return floorf (x); } -static inline float ctx_asinf (float x) { return asinf (x); } -static inline float ctx_sinf (float x) { return sinf (x); } -static inline float ctx_atan2f (float y, float x) { return atan2f (y, x); } -static inline float ctx_hypotf (float a, float b) { return hypotf (a, b); } -static inline float ctx_acosf (float a) { return acosf (a); } -static inline float ctx_cosf (float a) { return cosf (a); } -static inline float ctx_tanf (float a) { return tanf (a); } -static inline float ctx_expf (float p) { return expf (p); } -static inline float ctx_sqrtf (float a) { return sqrtf (a); } +static CTX_INLINE float ctx_fabsf (const float x) { return fabsf (x); } +static CTX_INLINE float ctx_floorf (const float x) { return floorf (x); } +static CTX_INLINE float ctx_asinf (const float x) { return asinf (x); } +static CTX_INLINE float ctx_sinf (const float x) { return sinf (x); } +static CTX_INLINE float ctx_atan2f (const float y, float x) { return atan2f (y, x); } +static CTX_INLINE float ctx_hypotf (const float a, float b) { return hypotf (a, b); } +static CTX_INLINE float ctx_acosf (const float a) { return acosf (a); } +static CTX_INLINE float ctx_cosf (const float a) { return cosf (a); } +static CTX_INLINE float ctx_tanf (const float a) { return tanf (a); } +static CTX_INLINE float ctx_expf (const float p) { return expf (p); } +static CTX_INLINE float ctx_sqrtf (const float a) { return sqrtf (a); } +static CTX_INLINE float ctx_atanf (const float a) { return atanf (a); } +#endif -static inline float ctx_hypotf_fast (float a, float b) +static CTX_INLINE float +ctx_invsqrtf_fast (const float x) { - return ctx_sqrtf (ctx_pow2 (a)+ctx_pow2 (b) ); + union + { + float f; + uint32_t i; + } u = { x }; + u.i = 0x5f3759df - (u.i >> 1); + return u.f; +} +CTX_INLINE static float ctx_sqrtf_fast (const float a) +{ + return 1.0f/ctx_invsqrtf_fast (a); +} +CTX_INLINE static float ctx_hypotf_fast (const float a, const float b) +{ + return ctx_sqrtf_fast (ctx_pow2 (a)+ctx_pow2 (b) ); } -#endif + + +static CTX_INLINE float ctx_atan2f_rest ( + const float x, const float y_recip) +{ + float atan, z = x * y_recip; + if ( ctx_fabsf ( z ) < 1.0f ) + { + atan = z/ (1.0f + 0.28f*z*z); + if (y_recip < 0.0f) + { + if ( x < 0.0f ) + { return atan - CTX_PI; } + return atan + CTX_PI; + } + } + else + { + atan = CTX_PI/2 - z/ (z*z + 0.28f); + if ( x < 0.0f ) { return atan - CTX_PI; } + } + return atan; +} + static inline float _ctx_parse_float (const char *str, char **endptr) { @@ -6140,10 +6672,10 @@ void ctx_rasterizer_colorspace_babl (CtxState *state, CtxColorSpace space_slot, const Babl *space); #endif -void ctx_rasterizer_colorspace_icc (CtxState *state, - CtxColorSpace space_slot, - char *icc_data, - int icc_length); +void ctx_rasterizer_colorspace_icc (CtxState *state, + CtxColorSpace space_slot, + const unsigned char *icc_data, + int icc_length); CtxBuffer *ctx_buffer_new_bare (void); @@ -6158,208 +6690,751 @@ void ctx_buffer_set_data (CtxBuffer *buffer, int ctx_textureclock (Ctx *ctx); -void ctx_exit (Ctx *ctx); +void ctx_done_frame (Ctx *ctx); void ctx_list_backends(void); int ctx_pixel_format_ebpp (CtxPixelFormat format); #endif +#if 0 +#if !__COSMOPOLITAN__ +#include +#include +#include + +#endif +#include "ctx.h" +#endif + +/* An immediate mode toolkit for ctx, ctx expects to receive full frames of + * data to draw and by keeping knowledge of the contents of the previous frame + * avoid re-drawing unchanged areas of the display. + * + * + * TODO/BUGS: + * - more than one scroll per panel + * - horizontal scroll + */ + +typedef struct _Css Css; +typedef struct _CssPanel CssPanel; + + +extern int _css_key_bindings_active; +Css *css_new (Ctx *ctx); +void css_destroy (Css *itk); +void css_reset (Css *itk); + +CssPanel *css_panel_start (Css *itk, const char *title, int x, int y, int width, int height); +void css_panel_end (Css *itk); + +void css_newline (Css *itk); +void css_seperator (Css *itk); +void css_titlebar (Css *itk, const char *label); + +void css_label (Css *itk, const char *label); +void css_labelf (Css *itk, const char *format, ...); + + +int css_toggle (Css *itk, const char *label, int in_val); + +int css_button (Css *itk, const char *label); + +/* this is already modernized - it is the same as css_toggle but gets rendered + * differently + */ +int css_radio (Css *itk, const char *label, int set); + + +/* needs tweaking / expander2 it should return the state of the expander */ +int css_expander (Css *itk, const char *label, int *val); + +/* return newly set value if ant change */ +int css_choice (Css *itk, const char *label, int in_val); +void css_choice_add (Css *itk, int value, const char *label); +void css_set_focus_no (Css *itk, int pos); + +int css_control_no (Css *itk); + + +/* + * returns NULL if value is unchanged or a newly allocated string + * when entry has been changed. + * + */ +char *css_entry (Css *itk, + const char *label, + const char *fallback, + const char *val); + + + +/* returns the new value - if it has changed due to interaction + */ +float css_slider (Css *itk, const char *label, float value, double min, double max, double step); + +/* these are utilities to keep some code a little bit shorter + */ +void css_slider_int (Css *itk, const char *label, int *val, int min, int max, int step); +void css_slider_float (Css *itk, const char *label, float *val, float min, float max, float step); +void css_slider_uint8 (Css *itk, const char *label, uint8_t *val, uint8_t min, uint8_t max, uint8_t step); + +/* returns 1 when the value has been changed + * + * this expects a string to write to maxlen is length including + * room for terminating \0 + * + * return 1 when the value has been changed + */ +int css_entry_str_len (Css *itk, + const char *label, + const char *fallback, + char *val, + int maxlen); + +/* to be called on focus changes that might take focus away from + * edited css_entry + */ +void css_entry_commit (Css *itk); +void css_lost_focus (Css *itk); + +/* return new value if changed */ + + +//void css_choice_add (Css *itk, int value, const char *label); +void css_done (Css *itk); +void css_style_color (Ctx *ctx, const char *name); +//void css_style_color2 (Css *itk, const char *klass, const char*attr); +void css_style_bg (Css *itk, const char *klass); +void css_style_fg (Css *itk, const char *klass); +const char *css_style_string (const char *name); +float css_style_float (char *name); +float css_em (Css *itk); + +Ctx *css_ctx (Css *itk); +float css_x (Css *itk); +float css_y (Css *itk); +void css_set_x (Css *itk, float x); +void css_set_y (Css *itk, float y); +void css_set_xy (Css *itk, float x, float y); +void css_set_edge_left (Css *itk, float edge); +void css_set_edge_right (Css *itk, float edge); +void css_set_edge_top (Css *itk, float edge); +void css_set_edge_bottom(Css *itk, float edge); +float css_wrap_width (Css *itk); +float css_height (Css *itk); +void css_set_height (Css *itk, float height); +float css_edge_left (Css *itk); +float css_edge_right (Css *itk); +float css_edge_top (Css *itk); +float css_edge_bottom (Css *itk); +void css_set_wrap_width (Css *itk, float wwidth); + +/* runs until ctx_exit itk->ctx) is called */ +void css_run_ui (Css *itk, int (*ui_fun)(Css *itk, void *data), void *ui_data); +void css_set_font_size (Css *itk, float font_size); + +void css_set_scale (Css *itk, float scale); +float css_scale (Css *itk); +float css_rel_ver_advance (Css *itk); + +int css_focus_no (Css *itk); +int css_is_editing_entry (Css *itk); + +/* + A helper function that does css_run_ui on a ctx context that is both created + and destroyed, this helps keep small programs tiny. + + void css_main (int (*ui_fun)(Css *itk, void *data), void *ui_data) + { + Ctx *ctx = ctx_new (-1, -1, NULL); + Css *itk = css_new (ctx); + css_run_ui (itk, ui_fun, ui_data); + css_destroy (itk); + ctx_destroy (ctx); + } + */ + +void css_main (int (*ui_fun)(Css *itk, void *data), void *ui_data); +void css_key_bindings (Css *itk); + +typedef struct _CtxControl CtxControl; +CtxControl *css_focused_control (Css *itk); +CtxControl *css_find_control (Css *itk, int no); +CtxControl *css_add_control (Css *itk, + int type, + const char *label, + float x, float y, + float width, float height); +void css_set_flag (Css *itk, int flag, int on); + + +void css_panels_reset_scroll (Css *itk); + +void css_ctx_settings (Css *itk); +void css_css_settings (Css *itk); +void css_key_quit (CtxEvent *event, void *userdata, void *userdata2); + +enum { + UI_SLIDER = 1, + UI_EXPANDER, + UI_TOGGLE, + UI_LABEL, + UI_TITLEBAR, + UI_BUTTON, + UI_CHOICE, + UI_ENTRY, + UI_MENU, + UI_SEPARATOR, + UI_RADIO +}; + +enum { + CSS_FLAG_SHOW_LABEL = (1<<0), + CSS_FLAG_ACTIVE = (1<<1), + CSS_FLAG_CANCEL_ON_LOST_FOCUS = (1<<2), + CSS_FLAG_DEFAULT = (CSS_FLAG_SHOW_LABEL|CSS_FLAG_ACTIVE) +}; + // XXX : commit or cancel entry on focus change + // + + +struct _CssPanel{ + int x; + int y; + int width; + int height; + int expanded; + int max_y; + float scroll_start_y; + float scroll; + + int do_scroll_jump; + const char *title; +}; + +typedef struct CssPal{ + char id; + uint8_t r; + uint8_t g; + uint8_t b; + uint8_t a; +} CssPal; + +struct _CtxControl{ + int no; + int ref_count; + uint64_t flags; + int type; /* this should be a pointer to the vfuncs/class struct + instead - along with one optional instance data per control */ + char *label; + void *id; /* possibly unique identifier */ + + float x; + float y; + float width; + float height; + void *val; + + float value; + + char *entry_value; + + char *fallback; + float min; + float max; + float step; +}; + + +float css_panel_scroll (Css *itk); +void css_panel_set_scroll (Css *itk, float scroll); + +typedef struct _Css Css; +typedef struct _CtxStyle CtxStyle; + +void css_start (Css *mrg, const char *class_name, void *id_ptr); +void css_start_with_style (Css *mrg, + const char *style_id, + void *id_ptr, + const char *style); + +void css_start_with_stylef (Css *mrg, const char *style_id, void *id_ptr, + const char *format, ...); +void css_xml_render (Css *mrg, + char *uri_base, + void (*link_cb) (CtxEvent *event, void *href, void *link_data), + void *link_data, + void *(finalize)(void *listen_data, void *listen_data2, void *finalize_data), + void *finalize_data, + char *html_); + +void +css_printf (Css *mrg, const char *format, ...); + +void css_print_xml (Css *mrg, const char *xml); + +void +css_printf_xml (Css *mrg, const char *format, ...); + +void +css_print_xml (Css *mrg, const char *utf8); + +// returns width added horizontally +float css_addstr (Css *mrg, const char *string, int utf8_length); + +void ctx_stylesheet_add (Css *mrg, const char *css, const char *uri_base, + int priority, char **error); +CtxStyle *ctx_style (Css *mrg); + +void css_end (Css *mrg, CtxFloatRectangle *ret_rect); + +int +mrg_get_contents (Css *mrg, + const char *referer, + const char *input_uri, + char **contents, + long *length); + +int css_xml_extent (Css *mrg, uint8_t *contents, float *width, float *height, float *vb_x, float *vb_y, float *vb_width, float *vb_height); #ifndef __CTX_CONSTANTS #define __CTX_CONSTANTS -#define SQZ_action 3696112672u -#define SQZ_addStop 220908742u -#define SQZ_alphabetic 2966120946u -#define SQZ_aqua 1635086787u -#define SQZ_arc 6517443u -#define SQZ_arcTo 3982854812u -#define SQZ_beginPath 120180698u -#define SQZ_bevel 761062270u -#define SQZ_black 271321868u -#define SQZ_blend 316843154u -#define SQZ_blending 3694082958u -#define SQZ_blendMode 644815934u -#define SQZ_blue 1702194373u -#define SQZ_bottom 1302706776u -#define SQZ_cap 7365063u -#define SQZ_center 1006603526u -#define SQZ_clear 1094071360u -#define SQZ_clip 1885957319u -#define SQZ_closePath 3537486488u -#define SQZ_cmyk 1803120071u -#define SQZ_cmyka 3355381580u -#define SQZ_cmykaS 3917993734u -#define SQZ_cmykS 3263315852u -#define SQZ_cmykSpace 2366647638u -#define SQZ_color 4231809138u -#define SQZ_colorSpace 4246256736u -#define SQZ_compositingMode 3764262848u -#define SQZ_copy 2037411783u -#define SQZ_currentColor 3452186816u -#define SQZ_curveTo 48499966u -#define SQZ_cyan 1851881927u -#define SQZ_darken 2939689930u -#define SQZ_defineFont 813704086u -#define SQZ_defineGlyph 1628031142u -#define SQZ_defineTexture 4030922434u -#define SQZ_destinationAtop 1605909240u -#define SQZ_destinationIn 4096489814u -#define SQZ_destinationOut 1966109282u -#define SQZ_destinationOver 507903672u -#define SQZ_deviceCMYK 3879736092u -#define SQZ_deviceRGB 911778270u -#define SQZ_difference 3137481792u -#define SQZ_done 1701736393u -#define SQZ_drgb 1650946761u -#define SQZ_drgba 465014226u -#define SQZ_drgbaS 2465325300u -#define SQZ_drgbS 179784888u -#define SQZ_drgbSpace 1000873868u -#define SQZ_end 6581963u -#define SQZ_endFrame 2645960260u -#define SQZ_endGroup 2864376370u -#define SQZ_evenOdd 3373267632u -#define SQZ_exit 1953069259u -#define SQZ_extend 2652659078u -#define SQZ_fill 1819044301u -#define SQZ_fillRect 3070816944u -#define SQZ_fillRule 2262201016u -#define SQZ_font 1953394637u -#define SQZ_fontSize 2620910512u -#define SQZ_fuchsia 439362132u -#define SQZ_globalAlpha 3833809790u -#define SQZ_glyph 1308254186u -#define SQZ_gradientAddStop 2831884664u -#define SQZ_gray 2036429519u -#define SQZ_graya 2560443068u -#define SQZ_grayaS 2408801086u -#define SQZ_grayS 417614710u -#define SQZ_green 1517032782u -#define SQZ_hanging 72134188u -#define SQZ_height 3762298230u -#define SQZ_horLineTo 2754532u -#define SQZ_hue 6649297u -#define SQZ_identity 4029142280u -#define SQZ_ideographic 3361616408u -#define SQZ_imageSmoothing 3109175850u -#define SQZ_join 1852403669u -#define SQZ_kerningPair 2079485344u -#define SQZ_lab 6447577u -#define SQZ_laba 1633837529u -#define SQZ_labaS 4170772618u -#define SQZ_labS 1398956505u -#define SQZ_lch 6841305u -#define SQZ_lcha 1634231257u -#define SQZ_lchaS 2598918966u -#define SQZ_lchS 1399350233u -#define SQZ_left 1952867801u -#define SQZ_lighten 2693650260u -#define SQZ_lime 1701669337u -#define SQZ_linearGradient 905023680u -#define SQZ_lineCap 3957741450u -#define SQZ_lineDash 2886130602u -#define SQZ_lineDashOffset 1904302200u -#define SQZ_lineHeight 1698077880u -#define SQZ_lineJoin 3891781172u -#define SQZ_lineTo 3077153258u -#define SQZ_lineWidth 3851910782u -#define SQZ_lower 697158190u -#define SQZ_lowerBottom 4240938844u -#define SQZ_magenta 578523642u -#define SQZ_maroon 3386542482u -#define SQZ_maximize 4009606768u -#define SQZ_middle 2223770148u -#define SQZ_miter 886459200u -#define SQZ_miterLimit 1856773288u -#define SQZ_moveTo 3083476356u -#define SQZ_multiply 3976122014u -#define SQZ_navy 2037801437u -#define SQZ_newPage 2687321890u -#define SQZ_newPath 4208019970u -#define SQZ_newState 3121230612u -#define SQZ_none 1701736413u -#define SQZ_nonzero 2746451764u -#define SQZ_normal 1883425054u -#define SQZ_olive 3415799870u -#define SQZ_paint 1082699806u -#define SQZ_purple 3066163412u -#define SQZ_quadTo 3205866160u -#define SQZ_radialGradient 83850682u -#define SQZ_raise 2772216630u -#define SQZ_raiseTop 1913256554u -#define SQZ_rect 1952671205u -#define SQZ_rectangle 1861211308u -#define SQZ_red 6579685u -#define SQZ_relArcTo 4253296276u -#define SQZ_relCurveTo 2548821600u -#define SQZ_relHorLineTo 3243288302u -#define SQZ_relLineTo 1630005260u -#define SQZ_relMoveTo 429673596u -#define SQZ_relQuadTo 2362773920u -#define SQZ_relSmoothqTo 2960208730u -#define SQZ_relSmoothTo 1725151068u -#define SQZ_relVerLineTo 1112835164u -#define SQZ_restore 1405984258u -#define SQZ_rgb 6449125u -#define SQZ_rgba 1633839077u -#define SQZ_rgbaS 4158357036u -#define SQZ_rgbS 1398958053u -#define SQZ_rgbSpace 1625332122u -#define SQZ_right 1751820526u -#define SQZ_rotate 1488065704u -#define SQZ_round 3173447652u -#define SQZ_roundRectangle 3273785582u -#define SQZ_save 1702257127u -#define SQZ_scale 2647970994u -#define SQZ_screen 3670530854u -#define SQZ_setFontSize 231476456u -#define SQZ_setLineCap 174619460u -#define SQZ_setLineJoin 4048631422u -#define SQZ_setLineWidth 3926586244u -#define SQZ_shadowBlur 3889925774u -#define SQZ_shadowColor 291132682u -#define SQZ_shadowOffsetX 1630263752u -#define SQZ_shadowOffsetY 89733304u -#define SQZ_silver 2643959904u -#define SQZ_smoothQuadTo 954100048u -#define SQZ_smoothTo 174420282u -#define SQZ_sourceAtop 864901378u -#define SQZ_sourceIn 1369048320u -#define SQZ_sourceOut 1938332472u -#define SQZ_sourceOver 134897678u -#define SQZ_sourceTransform 1611809620u -#define SQZ_square 239664392u -#define SQZ_start 4080984002u -#define SQZ_startFrame 2128007688u -#define SQZ_startGroup 4085444064u -#define SQZ_stroke 1444212908u -#define SQZ_strokeRect 1131907664u -#define SQZ_strokeSource 2685374474u -#define SQZ_strokeText 1728824940u -#define SQZ_teal 1818322409u -#define SQZ_text 1954047465u -#define SQZ_textAlign 3594701278u -#define SQZ_textBaseline 1453773018u -#define SQZ_textDirection 1179776176u -#define SQZ_texture 785032878u -#define SQZ_title 300059882u -#define SQZ_top 7368681u -#define SQZ_transform 3615253204u -#define SQZ_translate 1137670376u -#define SQZ_transparent 1911736550u -#define SQZ_unmaximize 3435737582u -#define SQZ_userCMYK 622108702u -#define SQZ_userRGB 4035904520u -#define SQZ_verLineTo 1200482574u -#define SQZ_viewBox 1582737754u -#define SQZ_white 518020662u -#define SQZ_width 3799171678u -#define SQZ_winding 2304820652u -#define SQZ_wrapLeft 3331521568u -#define SQZ_wrapRight 1810250152u -#define SQZ_x 241u -#define SQZ_xor 7499761u -#define SQZ_y 243u -#define SQZ_yellow 490403164u +#define SQZ_a 195u // "a" +#define SQZ_absolute 1840437120u // "absolute" +#define SQZ_action 3696112672u // "action" +#define SQZ_addStop 220908742u // "addStop" +#define SQZ_aelig 1120987016u // "aelig" +#define SQZ_alias 2034413622u // "alias" +#define SQZ_all_scroll 1118648896u // "all-scroll" +#define SQZ_alpha 4000549904u // "alpha" +#define SQZ_alphabetic 2966120946u // "alphabetic" +#define SQZ_amp 7368131u // "amp" +#define SQZ_apos 1936683203u // "apos" +#define SQZ_aqua 1635086787u // "aqua" +#define SQZ_arc 6517443u // "arc" +#define SQZ_arcTo 3982854812u // "arcTo" +#define SQZ_aring 855903140u // "aring" +#define SQZ_auto 1869903299u // "auto" +#define SQZ_background 1071035380u // "background" +#define SQZ_background_color 609802584u // "background-color" +#define SQZ_beginPath 120180698u // "beginPath" +#define SQZ_bevel 761062270u // "bevel" +#define SQZ_bidi_override 29328268u // "bidi-override" +#define SQZ_black 271321868u // "black" +#define SQZ_blend 316843154u // "blend" +#define SQZ_blending 3694082958u // "blending" +#define SQZ_blendMode 644815934u // "blendMode" +#define SQZ_blink 2515894180u // "blink" +#define SQZ_block 2220858820u // "block" +#define SQZ_blue 1702194373u // "blue" +#define SQZ_bold 1684828101u // "bold" +#define SQZ_bolder 2370434142u // "bolder" +#define SQZ_border 1170187232u // "border" +#define SQZ_border_bottom 3975273498u // "border-bottom" +#define SQZ_border_bottom_color 3186075110u // "border-bottom-color" +#define SQZ_border_bottom_width 1124387196u // "border-bottom-width" +#define SQZ_border_box 1245790092u // "border-box" +#define SQZ_border_color 541811290u // "border-color" +#define SQZ_border_left 2975583868u // "border-left" +#define SQZ_border_left_color 3331269224u // "border-left-color" +#define SQZ_border_left_width 3310378862u // "border-left-width" +#define SQZ_border_right 4045413762u // "border-right" +#define SQZ_border_right_color 243329524u // "border-right-color" +#define SQZ_border_right_width 1360128814u // "border-right-width" +#define SQZ_border_top 1801458758u // "border-top" +#define SQZ_border_top_color 3407512826u // "border-top-color" +#define SQZ_border_top_width 4176048594u // "border-top-width" +#define SQZ_border_width 3642950774u // "border-width" +#define SQZ_both 1752461253u // "both" +#define SQZ_bottom 1302706776u // "bottom" +#define SQZ_box_sizing 3965777366u // "box-sizing" +#define SQZ_br 29381u // "br" +#define SQZ_bull 1819047365u // "bull" +#define SQZ_butt 1953789381u // "butt" +#define SQZ_c 199u // "c" +#define SQZ_cap 7365063u // "cap" +#define SQZ_cedil 1951672802u // "cedil" +#define SQZ_cell 1819043271u // "cell" +#define SQZ_cent 1953392071u // "cent" +#define SQZ_center 1006603526u // "center" +#define SQZ_circle 196442712u // "circle" +#define SQZ_class 680581762u // "class" +#define SQZ_clear 1094071360u // "clear" +#define SQZ_clip 1885957319u // "clip" +#define SQZ_closePath 3537486488u // "closePath" +#define SQZ_cmyk 1803120071u // "cmyk" +#define SQZ_cmyka 3355381580u // "cmyka" +#define SQZ_cmykaS 3917993734u // "cmykaS" +#define SQZ_cmykS 3263315852u // "cmykS" +#define SQZ_cmykSpace 2366647638u // "cmykSpace" +#define SQZ_colgroup 4270888898u // "colgroup" +#define SQZ_color 4231809138u // "color" +#define SQZ_colorSpace 4246256736u // "colorSpace" +#define SQZ_col_resize 2272161114u // "col-resize" +#define SQZ_compositingMode 3764262848u // "compositingMode" +#define SQZ_conicGradient 1669326832u // "conicGradient" +#define SQZ_context_menu 434478918u // "context-menu" +#define SQZ_copy 2037411783u // "copy" +#define SQZ_crosshair 4030082594u // "crosshair" +#define SQZ_curren 3230040072u // "curren" +#define SQZ_currentColor 3452186816u // "currentColor" +#define SQZ_cursor 4212479966u // "cursor" +#define SQZ_cursor_wait 1647783762u // "cursor-wait" +#define SQZ_curveTo 48499966u // "curveTo" +#define SQZ_cx 30919u // "cx" +#define SQZ_cy 31175u // "cy" +#define SQZ_cyan 1851881927u // "cyan" +#define SQZ_d 201u // "d" +#define SQZ_darken 2939689930u // "darken" +#define SQZ_dd 25801u // "dd" +#define SQZ_default 2280700682u // "default" +#define SQZ_defineFont 813704086u // "defineFont" +#define SQZ_defineGlyph 1628031142u // "defineGlyph" +#define SQZ_defineTexture 4030922434u // "defineTexture" +#define SQZ_defs 1936090569u // "defs" +#define SQZ_deg 6776265u // "deg" +#define SQZ_destinationAtop 1605909240u // "destinationAtop" +#define SQZ_destinationIn 4096489814u // "destinationIn" +#define SQZ_destinationOut 1966109282u // "destinationOut" +#define SQZ_destinationOver 507903672u // "destinationOver" +#define SQZ_deviceCMYK 3879736092u // "deviceCMYK" +#define SQZ_deviceRGB 911778270u // "deviceRGB" +#define SQZ_difference 3137481792u // "difference" +#define SQZ_direction 626501720u // "direction" +#define SQZ_display 512467722u // "display" +#define SQZ_div 7760329u // "div" +#define SQZ_done 1701736393u // "done" +#define SQZ_dotted 1961166438u // "dotted" +#define SQZ_drgb 1650946761u // "drgb" +#define SQZ_drgba 465014226u // "drgba" +#define SQZ_drgbaS 2465325300u // "drgbaS" +#define SQZ_drgbS 179784888u // "drgbS" +#define SQZ_drgbSpace 1000873868u // "drgbSpace" +#define SQZ_dt 29897u // "dt" +#define SQZ_ellipse 3790670476u // "ellipse" +#define SQZ_embed 3279610738u // "embed" +#define SQZ_end 6581963u // "end" +#define SQZ_endFrame 2645960260u // "endFrame" +#define SQZ_endGroup 2864376370u // "endGroup" +#define SQZ_e_resize 3569673796u // "e-resize" +#define SQZ_euro 1869772235u // "euro" +#define SQZ_evenodd 1852152574u // "evenodd" +#define SQZ_evenOdd 3373267632u // "evenOdd" +#define SQZ_ew_resize 2458267842u // "ew-resize" +#define SQZ_exit 1953069259u // "exit" +#define SQZ_extend 2652659078u // "extend" +#define SQZ_feather 4162344430u // "feather" +#define SQZ_file 1701603789u // "file" +#define SQZ_fill 1819044301u // "fill" +#define SQZ_fill_color 2350805940u // "fill-color" +#define SQZ_fillRect 3070816944u // "fillRect" +#define SQZ_fill_rule 1151472398u // "fill-rule" +#define SQZ_fillRule 2262201016u // "fillRule" +#define SQZ_first_child 439258010u // "first-child" +#define SQZ_fixed 778407114u // "fixed" +#define SQZ_float 2455282620u // "float" +#define SQZ_flow_root 518738066u // "flow-root" +#define SQZ_font 1953394637u // "font" +#define SQZ_font_family 2798191102u // "font-family" +#define SQZ_font_size 3477901146u // "font-size" +#define SQZ_fontSize 2620910512u // "fontSize" +#define SQZ_font_style 2628706306u // "font-style" +#define SQZ_font_weight 1197895732u // "font-weight" +#define SQZ_fr 29389u // "fr" +#define SQZ_fuchsia 439362132u // "fuchsia" +#define SQZ_fx 30925u // "fx" +#define SQZ_fy 31181u // "fy" +#define SQZ_g 207u // "g" +#define SQZ_globalAlpha 3833809790u // "globalAlpha" +#define SQZ_glyph 1308254186u // "glyph" +#define SQZ_gradientAddStop 2831884664u // "gradientAddStop" +#define SQZ_gradientTransform 1288938878u // "gradientTransform" +#define SQZ_gradientUnits 2968072588u // "gradientUnits" +#define SQZ_gray 2036429519u // "gray" +#define SQZ_graya 2560443068u // "graya" +#define SQZ_grayaS 2408801086u // "grayaS" +#define SQZ_grayS 417614710u // "grayS" +#define SQZ_green 1517032782u // "green" +#define SQZ_gt 29903u // "gt" +#define SQZ_hanging 72134188u // "hanging" +#define SQZ_head 1684104657u // "head" +#define SQZ_height 3762298230u // "height" +#define SQZ_hellip 4254915686u // "hellip" +#define SQZ_help 1886152145u // "help" +#define SQZ_hidden 2737189728u // "hidden" +#define SQZ_horLineTo 2754532u // "horLineTo" +#define SQZ_hr 29393u // "hr" +#define SQZ_href 1717924561u // "href" +#define SQZ_html 1819112657u // "html" +#define SQZ_http 1886680273u // "http" +#define SQZ_hue 6649297u // "hue" +#define SQZ_id 25811u // "id" +#define SQZ_identity 4029142280u // "identity" +#define SQZ_ideographic 3361616408u // "ideographic" +#define SQZ_iexcl 2255292952u // "iexcl" +#define SQZ_imageSmoothing 3109175850u // "imageSmoothing" +#define SQZ_img 6778323u // "img" +#define SQZ_inline_block 3464466506u // "inline-block" +#define SQZ_input 3954510188u // "input" +#define SQZ_inset 3128333578u // "inset" +#define SQZ_italic 396886224u // "italic" +#define SQZ_join 1852403669u // "join" +#define SQZ_justify 233888506u // "justify" +#define SQZ_kerningPair 2079485344u // "kerningPair" +#define SQZ_lab 6447577u // "lab" +#define SQZ_laba 1633837529u // "laba" +#define SQZ_labaS 4170772618u // "labaS" +#define SQZ_labS 1398956505u // "labS" +#define SQZ_laquo 64667100u // "laquo" +#define SQZ_lch 6841305u // "lch" +#define SQZ_lcha 1634231257u // "lcha" +#define SQZ_lchaS 2598918966u // "lchaS" +#define SQZ_lchS 1399350233u // "lchS" +#define SQZ_left 1952867801u // "left" +#define SQZ_letter_spacing 1326564462u // "letter-spacing" +#define SQZ_li 27097u // "li" +#define SQZ_lighten 2693650260u // "lighten" +#define SQZ_lime 1701669337u // "lime" +#define SQZ_line 1701734873u // "line" +#define SQZ_linearGradient 905023680u // "linearGradient" +#define SQZ_lineCap 3957741450u // "lineCap" +#define SQZ_lineDash 2886130602u // "lineDash" +#define SQZ_lineDashOffset 1904302200u // "lineDashOffset" +#define SQZ_line_height 3733591786u // "line-height" +#define SQZ_lineHeight 1698077880u // "lineHeight" +#define SQZ_lineJoin 3891781172u // "lineJoin" +#define SQZ_linethrough 34244248u // "linethrough" +#define SQZ_lineTo 3077153258u // "lineTo" +#define SQZ_line_width 1869007654u // "line-width" +#define SQZ_lineWidth 3851910782u // "lineWidth" +#define SQZ_link 1802398169u // "link" +#define SQZ_list_item 2339943222u // "list-item" +#define SQZ_lower 697158190u // "lower" +#define SQZ_lowerBottom 4240938844u // "lowerBottom" +#define SQZ_lt 29913u // "lt" +#define SQZ_ltr 7501017u // "ltr" +#define SQZ_magenta 578523642u // "magenta" +#define SQZ_margin 2593567380u // "margin" +#define SQZ_margin_bottom 2905482030u // "margin-bottom" +#define SQZ_margin_left 1656714300u // "margin-left" +#define SQZ_margin_right 3017942990u // "margin-right" +#define SQZ_margin_top 625296560u // "margin-top" +#define SQZ_maroon 3386542482u // "maroon" +#define SQZ_max_height 129479668u // "max-height" +#define SQZ_maximize 4009606768u // "maximize" +#define SQZ_max_width 713333008u // "max-width" +#define SQZ_mdash 2043309408u // "mdash" +#define SQZ_meta 1635018203u // "meta" +#define SQZ_middle 2223770148u // "middle" +#define SQZ_middot 435157574u // "middot" +#define SQZ_min_height 3589941308u // "min-height" +#define SQZ_min_width 570636676u // "min-width" +#define SQZ_miter 886459200u // "miter" +#define SQZ_miterLimit 1856773288u // "miterLimit" +#define SQZ_move 1702260699u // "move" +#define SQZ_moveTo 3083476356u // "moveTo" +#define SQZ_multiply 3976122014u // "multiply" +#define SQZ_navy 2037801437u // "navy" +#define SQZ_nbsp 1886610141u // "nbsp" +#define SQZ_ne_resize 2436514350u // "ne-resize" +#define SQZ_nesw_resize 366763636u // "nesw-resize" +#define SQZ_newPage 2687321890u // "newPage" +#define SQZ_newPath 4208019970u // "newPath" +#define SQZ_newState 3121230612u // "newState" +#define SQZ_no_drop 890688824u // "no-drop" +#define SQZ_none 1701736413u // "none" +#define SQZ_nonzero 2746451764u // "nonzero" +#define SQZ_normal 1883425054u // "normal" +#define SQZ_not_allowed 224860282u // "not-allowed" +#define SQZ_nowrap 3884678112u // "nowrap" +#define SQZ_n_resize 202450980u // "n-resize" +#define SQZ_ns_resize 1753032392u // "ns-resize" +#define SQZ_nw_resize 3642132534u // "nw-resize" +#define SQZ_oblique 2262696070u // "oblique" +#define SQZ_offset 1396589030u // "offset" +#define SQZ_olive 3415799870u // "olive" +#define SQZ_omega 1147793334u // "omega" +#define SQZ_opacity 2184299270u // "opacity" +#define SQZ_option 537535526u // "option" +#define SQZ_ordm 1835299551u // "ordm" +#define SQZ_oslash 1567583338u // "oslash" +#define SQZ_overflow 3253908870u // "overflow" +#define SQZ_overline 2037328102u // "overline" +#define SQZ_p 225u // "p" +#define SQZ_padding 2806228288u // "padding" +#define SQZ_padding_bottom 2393940612u // "padding-bottom" +#define SQZ_padding_left 2955272020u // "padding-left" +#define SQZ_padding_right 4039401684u // "padding-right" +#define SQZ_padding_top 439515192u // "padding-top" +#define SQZ_paint 1082699806u // "paint" +#define SQZ_para 1634886113u // "para" +#define SQZ_path 1752457697u // "path" +#define SQZ_phi 6908129u // "phi" +#define SQZ_plusmn 279695816u // "plusmn" +#define SQZ_pointer 132752978u // "pointer" +#define SQZ_polygon 1804794854u // "polygon" +#define SQZ_polyline 845479076u // "polyline" +#define SQZ_position 3883240572u // "position" +#define SQZ_pound 363756384u // "pound" +#define SQZ_pre 6648545u // "pre" +#define SQZ_pre_line 4122418998u // "pre-line" +#define SQZ_preserve 1666261276u // "preserve" +#define SQZ_pre_wrap 837003298u // "pre-wrap" +#define SQZ_print_symbols 669612738u // "print-symbols" +#define SQZ_progress 2815872894u // "progress" +#define SQZ_purple 3066163412u // "purple" +#define SQZ_quadTo 3205866160u // "quadTo" +#define SQZ_quot 1953461731u // "quot" +#define SQZ_r 229u // "r" +#define SQZ_radialGradient 83850682u // "radialGradient" +#define SQZ_raise 2772216630u // "raise" +#define SQZ_raiseTop 1913256554u // "raiseTop" +#define SQZ_raquo 3261968210u // "raquo" +#define SQZ_rect 1952671205u // "rect" +#define SQZ_rectangle 1861211308u // "rectangle" +#define SQZ_red 6579685u // "red" +#define SQZ_reg 6776293u // "reg" +#define SQZ_rel 7103973u // "rel" +#define SQZ_relArcTo 4253296276u // "relArcTo" +#define SQZ_relative 979899102u // "relative" +#define SQZ_relCurveTo 2548821600u // "relCurveTo" +#define SQZ_relHorLineTo 3243288302u // "relHorLineTo" +#define SQZ_relLineTo 1630005260u // "relLineTo" +#define SQZ_relMoveTo 429673596u // "relMoveTo" +#define SQZ_relQuadTo 2362773920u // "relQuadTo" +#define SQZ_relSmoothqTo 2960208730u // "relSmoothqTo" +#define SQZ_relSmoothTo 1725151068u // "relSmoothTo" +#define SQZ_relVerLineTo 1112835164u // "relVerLineTo" +#define SQZ_restore 1405984258u // "restore" +#define SQZ_reverse 2464792996u // "reverse" +#define SQZ_rgb 6449125u // "rgb" +#define SQZ_rgba 1633839077u // "rgba" +#define SQZ_rgbaS 4158357036u // "rgbaS" +#define SQZ_rgbS 1398958053u // "rgbS" +#define SQZ_rgbSpace 1625332122u // "rgbSpace" +#define SQZ_right 1751820526u // "right" +#define SQZ_rotate 1488065704u // "rotate" +#define SQZ_round 3173447652u // "round" +#define SQZ_roundRectangle 3273785582u // "roundRectangle" +#define SQZ_row_resize 702013530u // "row-resize" +#define SQZ_rtl 7107813u // "rtl" +#define SQZ_rx 30949u // "rx" +#define SQZ_ry 31205u // "ry" +#define SQZ_save 1702257127u // "save" +#define SQZ_scale 2647970994u // "scale" +#define SQZ_screen 3670530854u // "screen" +#define SQZ_scroll 3099410214u // "scroll" +#define SQZ_sect 1952671207u // "sect" +#define SQZ_se_resize 315956726u // "se-resize" +#define SQZ_setFontSize 231476456u // "setFontSize" +#define SQZ_setLineCap 174619460u // "setLineCap" +#define SQZ_setLineJoin 4048631422u // "setLineJoin" +#define SQZ_setLineWidth 3926586244u // "setLineWidth" +#define SQZ_shadowBlur 3889925774u // "shadowBlur" +#define SQZ_shadowColor 291132682u // "shadowColor" +#define SQZ_shadowOffsetX 1630263752u // "shadowOffsetX" +#define SQZ_shadowOffsetY 89733304u // "shadowOffsetY" +#define SQZ_shy 7956711u // "shy" +#define SQZ_silver 2643959904u // "silver" +#define SQZ_smoothQuadTo 954100048u // "smoothQuadTo" +#define SQZ_smoothTo 174420282u // "smoothTo" +#define SQZ_solid 2770487110u // "solid" +#define SQZ_sourceAtop 864901378u // "sourceAtop" +#define SQZ_sourceIn 1369048320u // "sourceIn" +#define SQZ_sourceOut 1938332472u // "sourceOut" +#define SQZ_sourceOver 134897678u // "sourceOver" +#define SQZ_sourceTransform 1611809620u // "sourceTransform" +#define SQZ_spreadMethod 3574032566u // "spreadMethod" +#define SQZ_square 239664392u // "square" +#define SQZ_src 6517479u // "src" +#define SQZ_s_resize 125328402u // "s-resize" +#define SQZ_start 4080984002u // "start" +#define SQZ_startFrame 2128007688u // "startFrame" +#define SQZ_startGroup 4085444064u // "startGroup" +#define SQZ_static 3471421972u // "static" +#define SQZ_stop 1886352615u // "stop" +#define SQZ_stop_color 1175890462u // "stop-color" +#define SQZ_stop_opacity 250359768u // "stop-opacity" +#define SQZ_stroke 1444212908u // "stroke" +#define SQZ_stroke_color 1804158464u // "stroke-color" +#define SQZ_stroke_linecap 535668102u // "stroke-linecap" +#define SQZ_stroke_linejoin 1888005366u // "stroke-linejoin" +#define SQZ_stroke_miterlimit 1499817720u // "stroke-miterlimit" +#define SQZ_strokePos 888669104u // "strokePos" +#define SQZ_strokeRect 1131907664u // "strokeRect" +#define SQZ_strokeSource 2685374474u // "strokeSource" +#define SQZ_stroke_width 2655701078u // "stroke-width" +#define SQZ_style 3511288852u // "style" +#define SQZ_sub 6452711u // "sub" +#define SQZ_sup1 829453799u // "sup1" +#define SQZ_sup2 846231015u // "sup2" +#define SQZ_sup3 863008231u // "sup3" +#define SQZ_super 532067904u // "super" +#define SQZ_svg 6780647u // "svg" +#define SQZ_sw_resize 2121684268u // "sw-resize" +#define SQZ_syntax_highlight 1534043236u // "syntax-highlight" +#define SQZ_table 2705307032u // "table" +#define SQZ_tab_size 945510526u // "tab-size" +#define SQZ_tbody 3472781808u // "tbody" +#define SQZ_td 25833u // "td" +#define SQZ_teal 1818322409u // "teal" +#define SQZ_text 1954047465u // "text" +#define SQZ_text_align 519203916u // "text-align" +#define SQZ_textAlign 3594701278u // "textAlign" +#define SQZ_text_anchor 2618811808u // "text-anchor" +#define SQZ_textBaseline 1453773018u // "textBaseline" +#define SQZ_text_decoration 2191990606u // "text-decoration" +#define SQZ_textDirection 1179776176u // "textDirection" +#define SQZ_text_indent 1164860048u // "text-indent" +#define SQZ_text_stroke 3855125514u // "text-stroke" +#define SQZ_text_stroke_color 357760164u // "text-stroke-color" +#define SQZ_text_stroke_width 376828216u // "text-stroke-width" +#define SQZ_texture 785032878u // "texture" +#define SQZ_tfoot 3061216442u // "tfoot" +#define SQZ_th 26857u // "th" +#define SQZ_thead 460193516u // "thead" +#define SQZ_title 300059882u // "title" +#define SQZ_top 7368681u // "top" +#define SQZ_tr 29417u // "tr" +#define SQZ_trade 3023660122u // "trade" +#define SQZ_transform 3615253204u // "transform" +#define SQZ_translate 1137670376u // "translate" +#define SQZ_transparent 1911736550u // "transparent" +#define SQZ_true 1702195945u // "true" +#define SQZ_underline 4021545710u // "underline" +#define SQZ_unicode_bidi 185934494u // "unicode-bidi" +#define SQZ_unmaximize 3435737582u // "unmaximize" +#define SQZ_userCMYK 622108702u // "userCMYK" +#define SQZ_userRGB 4035904520u // "userRGB" +#define SQZ_verLineTo 1200482574u // "verLineTo" +#define SQZ_version 3945712782u // "version" +#define SQZ_vertical_align 3047242218u // "vertical-align" +#define SQZ_vertical_text 580215056u // "vertical-text" +#define SQZ_viewbox 816983354u // "viewbox" +#define SQZ_viewBox 1582737754u // "viewBox" +#define SQZ_visibility 4182119682u // "visibility" +#define SQZ_visible 2712656970u // "visible" +#define SQZ_white 518020662u // "white" +#define SQZ_white_space 2063040106u // "white-space" +#define SQZ_width 3799171678u // "width" +#define SQZ_winding 2304820652u // "winding" +#define SQZ_word_spacing 2779764612u // "word-spacing" +#define SQZ_wrapLeft 3331521568u // "wrapLeft" +#define SQZ_wrapRight 1810250152u // "wrapRight" +#define SQZ_w_resize 505786646u // "w-resize" +#define SQZ_x 241u // "x" +#define SQZ_x1 12785u // "x1" +#define SQZ_x2 13041u // "x2" +#define SQZ_xor 7499761u // "xor" +#define SQZ_y 243u // "y" +#define SQZ_y1 12787u // "y1" +#define SQZ_y2 13043u // "y2" +#define SQZ_yellow 490403164u // "yellow" +#define SQZ_yen 7235059u // "yen" +#define SQZ_yes 7562739u // "yes" +#define SQZ_z_index 448175280u // "z-index" +#define SQZ_zoom_in 2458604508u // "zoom-in" +#define SQZ_zoom_out 3603903986u // "zoom-out" #endif #ifndef __CTX_LIBC_H #define __CTX_LIBC_H @@ -10171,16 +11246,6 @@ typedef struct _CtxGState CtxGState; typedef struct _CtxSource CtxSource; -enum _CtxAntialias -{ - 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? -}; -typedef enum _CtxAntialias CtxAntialias; -void ctx_set_antialias (Ctx *ctx, CtxAntialias antialias); -CtxAntialias ctx_get_antialias (Ctx *ctx); #define CTX_VALID_RGBA_U8 (1<<0) #define CTX_VALID_RGBA_DEVICE (1<<1) @@ -10252,6 +11317,7 @@ enum _CtxSourceType CTX_SOURCE_TEXTURE, CTX_SOURCE_LINEAR_GRADIENT, CTX_SOURCE_RADIAL_GRADIENT, + CTX_SOURCE_CONIC_GRADIENT, CTX_SOURCE_INHERIT_FILL }; @@ -10268,7 +11334,7 @@ struct _CtxBuffer int frame; // last frame used in, everything > 3 can be removed, // as clients wont rely on it. char *eid; // might be NULL, when not - should be unique for pixel contents - CtxPixelFormatInfo *format; + const CtxPixelFormatInfo *format; void (*free_func) (void *pixels, void *user_data); void *user_data; @@ -10279,7 +11345,7 @@ struct _CtxBuffer void *space; #endif #endif -#if 1 +#if CTX_ENABLE_CM CtxBuffer *color_managed; /* only valid for one render target, cache for a specific space */ @@ -10319,14 +11385,19 @@ struct _CtxSource float y0; float x1; float y1; - float dx; - float dy; - float start; - float end; float length; - float rdelta; + float dx_scaled; + float dy_scaled; + float start_scaled; } linear_gradient; struct + { + float x; + float y; + float start_angle; + float cycles; + } conic_gradient; + struct { float x0; float y0; @@ -10371,6 +11442,8 @@ struct _CtxGState float line_width; float line_dash_offset; + float stroke_pos; + float feather; float miter_limit; float font_size; #if CTX_ENABLE_SHADOW_BLUR @@ -10421,7 +11494,8 @@ struct _CtxGState CtxCompositingMode compositing_mode; // bitfield refs lead to CtxBlend blend_mode; // non-vectorization CtxExtend extend; - + long tolerance_fixed; + float tolerance; float dashes[CTX_MAX_DASHES]; // XXX moving dashes // to state storage,. will // allow it to be larger, @@ -10468,7 +11542,7 @@ struct _CtxKeyDbEntry struct _CtxState { - unsigned int has_moved:1; + int has_moved; unsigned int has_clipped:1; int8_t source; // used for the single-shifting to stroking // 0 = fill @@ -10481,6 +11555,8 @@ struct _CtxState float x; float y; + float first_x; + float first_y; int ink_min_x; int ink_min_y; int ink_max_x; @@ -10499,8 +11575,9 @@ struct _CtxState */ #endif CtxKeyDbEntry keydb[CTX_MAX_KEYDB]; - char stringpool[CTX_STRINGPOOL_SIZE]; CtxGState gstate_stack[CTX_MAX_STATES];//at end, so can be made dynamic + char *stringpool; + int stringpool_size; }; @@ -10513,11 +11590,17 @@ struct _CtxFontEngine int (*load_file) (const char *name, const char *path); #endif int (*load_memory) (const char *name, const void *data, int length); - int (*glyph) (CtxFont *font, Ctx *ctx, uint32_t unichar, int stroke); - float (*glyph_width) (CtxFont *font, Ctx *ctx, uint32_t unichar); + int (*glyph) (CtxFont *font, Ctx *ctx, int glyphid, int stroke); + float (*glyph_width) (CtxFont *font, Ctx *ctx, int glyphid); float (*glyph_kern) (CtxFont *font, Ctx *ctx, uint32_t unicharA, uint32_t unicharB); + + // return -1 for not found or 0 or positive number for found glyph + int (*glyph_lookup) (CtxFont *font, Ctx *ctx, uint32_t unichar); }; +#if CTX_FONT_ENGINE_HARFBUZZ +#include +#endif #pragma pack(push,1) struct _CtxFont @@ -10549,6 +11632,26 @@ struct _CtxFont stbtt_fontinfo ttf_info; } stb; #endif +#if CTX_FONT_ENGINE_HARFBUZZ + struct + { + const char *name; + char *path; + hb_blob_t *blob; + hb_face_t *face; + hb_font_t *font; + hb_draw_funcs_t *draw_funcs; +#if HB_VERSION_MAJOR >= 7 + hb_paint_funcs_t *paint_funcs; +#endif + //int x_scale; + //int y_scale; + float scale; + } hb; +#endif + + + #if 0 struct { int start; int end; int gw; int gh; const uint8_t *data;} monobitmap; #endif @@ -10680,6 +11783,7 @@ typedef struct _CtxGlyphEntry CtxGlyphEntry; struct _Ctx { CtxBackend *backend; + void (*process) (Ctx *ctx, const CtxCommand *entry); CtxState state; /**/ CtxDrawlist drawlist; int transformation; @@ -10693,9 +11797,9 @@ struct _Ctx uint32_t bail; CtxBackend *backend_pushed; CtxBuffer texture[CTX_MAX_TEXTURES]; + int exit; #if CTX_EVENTS CtxCursor cursor; - int quit; CtxEvents events; int mouse_fd; int mouse_x; @@ -10715,21 +11819,25 @@ struct _Ctx }; +#if 0 +#define ctx_process(ctx,entry) ctx->process (ctx, (CtxCommand *)(entry)); +#else static inline void -ctx_process (Ctx *ctx, CtxEntry *entry) +ctx_process (Ctx *ctx, const CtxEntry *entry) { - ctx->backend->process (ctx, (CtxCommand *) entry); + ctx->process (ctx, (CtxCommand *) entry); } +#endif CtxBuffer *ctx_buffer_new (int width, int height, CtxPixelFormat pixel_format); void ctx_buffer_destroy (CtxBuffer *buffer); -void +static 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_style (CtxState *state, const CtxEntry *entry, void *data); +static inline void ctx_interpret_transforms (CtxState *state, const 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); @@ -10741,8 +11849,7 @@ struct _CtxInternalFsEntry }; -typedef void (*ctx_apply_coverage_fun) (CtxRasterizer *r, uint8_t * __restrict__ dst, uint8_t * __restrict__ src, int x, uint8_t *coverage, - unsigned int count); +typedef void (*ctx_apply_coverage_fun) (unsigned int count, uint8_t * __restrict__ dst, uint8_t * __restrict__ src, uint8_t *coverage, CtxRasterizer *r, int x); struct _CtxPixelFormatInfo { @@ -10773,12 +11880,12 @@ static void _ctx_user_to_device_distance (CtxState *state, float *x, float *y); static void ctx_state_init (CtxState *state); static inline void -ctx_interpret_pos_bare (CtxState *state, CtxEntry *entry, void *data); +ctx_interpret_pos_bare (CtxState *state, const CtxEntry *entry, void *data); static inline void ctx_drawlist_deinit (CtxDrawlist *drawlist); //extern CtxPixelFormatInfo *(*ctx_pixel_format_info) (CtxPixelFormat format); -CtxPixelFormatInfo *ctx_pixel_format_info (CtxPixelFormat format); +const CtxPixelFormatInfo *ctx_pixel_format_info (CtxPixelFormat format); @@ -10849,22 +11956,24 @@ struct _CtxRasterizer */ -#define CTX_COMPOSITE_ARGUMENTS CtxRasterizer *rasterizer, uint8_t * __restrict__ dst, uint8_t * __restrict__ src, int x0, uint8_t * __restrict__ coverage, unsigned int count +#define CTX_COMPOSITE_ARGUMENTS unsigned int count, uint8_t * __restrict__ dst, uint8_t * __restrict__ src, uint8_t * __restrict__ coverage, CtxRasterizer *rasterizer, int x0 void (*comp_op)(CTX_COMPOSITE_ARGUMENTS); CtxFragment fragment; //Ctx *ctx; CtxState *state; - void *buf; - int fast_aa; CtxCovPath comp; - void (*apply_coverage) (CtxRasterizer *r, uint8_t * __restrict__ dst, uint8_t * __restrict__ src, int x, uint8_t *coverage, unsigned int count); + unsigned int swap_red_green; + ctx_apply_coverage_fun apply_coverage; - unsigned int aa; // level of vertical aa unsigned int active_edges; + unsigned int edge_pos; // where we're at in iterating all edges unsigned int pending_edges; unsigned int horizontal_edges; unsigned int ending_edges; - unsigned int edge_pos; // where we're at in iterating all edges + + unsigned int aa; // level of vertical aa + int convex; + unsigned int scan_aa[4]; // 0=none, 1 = 3, 2 = 5, 3 = 15 int scanline; int scan_min; @@ -10878,42 +11987,45 @@ struct _CtxRasterizer float x; float y; - float first_x; - float first_y; + int first_edge; uint16_t blit_x; uint16_t blit_y; - uint16_t blit_width; - uint16_t blit_height; - uint16_t blit_stride; - - unsigned int clip_rectangle:1; - unsigned int has_shape:2; - int has_prev:2; - unsigned int preserve:1; + int32_t blit_width; + int32_t blit_height; + uint32_t blit_stride; + + + unsigned int unused; // kept for layout + unsigned int clip_rectangle; + int has_prev; + void *buf; #if CTX_ENABLE_SHADOW_BLUR unsigned int in_shadow:1; + float feather_x; + float feather_y; + float feather; #endif - unsigned int in_text:1; - unsigned int swap_red_green:1; -#if CTX_BRAILLE_TEXT - unsigned int term_glyphs:1; // store appropriate glyphs for redisplay -#endif - int shadow_x; -#if CTX_BRAILLE_TEXT - CtxList *glyphs; -#endif - CtxPixelFormatInfo *format; + const CtxPixelFormatInfo *format; Ctx *texture_source; /* normally same as ctx */ - int shadow_y; - uint8_t color[4*5]; // in compositing format uint16_t color_native; // - uint16_t color_nativeB[5]; - + uint16_t color_nativeB[8]; + uint8_t color[8*5]; // in compositing format int edges[CTX_MAX_EDGES]; // integer position in edge array CtxDrawlist edge_list; + + unsigned int preserve; + unsigned int in_text; + + +#if static_OPAQUE + uint8_t opaque[CTX_MAX_SCANLINE_LENGTH]; +#endif +#if CTX_ENABLE_CLIP + CtxBuffer *clip_buffer; +#endif #if CTX_GRADIENTS #if CTX_GRADIENT_CACHE @@ -10923,8 +12035,12 @@ struct _CtxRasterizer #endif #endif -#if CTX_ENABLE_CLIP - CtxBuffer *clip_buffer; + +#if CTX_BRAILLE_TEXT + unsigned int term_glyphs:1; // store appropriate glyphs for redisplay +#endif +#if CTX_BRAILLE_TEXT + CtxList *glyphs; #endif #if CTX_COMPOSITING_GROUPS @@ -10934,10 +12050,20 @@ struct _CtxRasterizer #if CTX_ENABLE_SHADOW_BLUR float kernel[CTX_MAX_GAUSSIAN_KERNEL_DIM]; #endif + unsigned int shadow_active_edges; + unsigned int shadow_edge_pos; + int shadow_edges[CTX_MAX_EDGES*2]; -#if static_OPAQUE - uint8_t opaque[CTX_MAX_SCANLINE_LENGTH]; +#if CTX_SCANBIN + uint32_t scan_bins[CTX_MAX_SCANLINES][CTX_MAX_EDGES]; +#if CTX_MAX_EDGES>255 + uint32_t scan_bin_count[CTX_MAX_SCANLINES]; +#else + uint8_t scan_bin_count[CTX_MAX_SCANLINES]; #endif +#endif + + }; struct _CtxSHA1 { @@ -11140,7 +12266,7 @@ ctx_rasterizer_rectangle (CtxRasterizer *rasterizer, float width, float height); -static void ctx_rasterizer_finish_shape (CtxRasterizer *rasterizer); +static void ctx_rasterizer_close_path (CtxRasterizer *rasterizer); static void ctx_rasterizer_clip (CtxRasterizer *rasterizer); static void ctx_rasterizer_set_font (CtxRasterizer *rasterizer, const char *font_name); @@ -11311,7 +12437,7 @@ int ctx_sha1_done(CtxSHA1 * sha1, unsigned char *out); void _ctx_texture_lock (void); void _ctx_texture_unlock (void); -uint8_t *ctx_define_texture_pixel_data (CtxEntry *entry); +uint8_t *ctx_define_texture_pixel_data (const CtxEntry *entry); void ctx_buffer_pixels_free (void *pixels, void *userdata); /*ctx_texture_init: @@ -11433,8 +12559,8 @@ _ctx_matrix_apply_transform (const CtxMatrix *m, float *x, float *y) float y_in = *y; float w = (x_in * m->m[2][0]) + (y_in * m->m[2][1]) + m->m[2][2]; float w_recip = 1.0f/w; - *x = ( (x_in * m->m[0][0]) + (y_in * m->m[0][1]) + m->m[0][2]) * w_recip; - *y = ( (x_in * m->m[1][0]) + (y_in * m->m[1][1]) + m->m[1][2]) * w_recip; + *x = ( (x_in * m->m[0][0]) + (y_in * m->m[0][1]) + m->m[0][2]) * w_recip; + *y = ( (x_in * m->m[1][0]) + (y_in * m->m[1][1]) + m->m[1][2]) * w_recip; } @@ -11496,22 +12622,32 @@ void ctx_push_backend (Ctx *ctx, void ctx_pop_backend (Ctx *ctx); -static inline float ctx_fmod1f (float val) +static CTX_INLINE float ctx_fmod1f (float val) { - return ctx_fabsf (val - (int)(val)); + val = ctx_fabsf(val); + return val - (int)(val); } -static inline float ctx_fmodf (float val, float modulus) +static CTX_INLINE float ctx_fmodf (float val, float modulus) { return ctx_fmod1f(val/modulus) * modulus; } +static CTX_INLINE int ctx_nearly_zero(float val) +{ + return (val > 0.001f) & (val > -0.001f); +} + +int ctx_glyph_find (Ctx *ctx, CtxFont *font, uint32_t unichar); + #if EMSCRIPTEN #define CTX_EXPORT EMSCRIPTEN_KEEPALIVE #else #define CTX_EXPORT #endif +float ctx_get_feather (Ctx *ctx); +void ctx_feather (Ctx *ctx, float x); #endif @@ -11523,7 +12659,7 @@ static inline float ctx_fmodf (float val, float modulus) #define CTX_DRAWLIST_H static int -ctx_conts_for_entry (CtxEntry *entry); +ctx_conts_for_entry (const CtxEntry *entry); void ctx_iterator_init (CtxIterator *iterator, CtxDrawlist *drawlist, @@ -11535,8 +12671,8 @@ int ctx_iterator_pos (CtxIterator *iterator); static void ctx_drawlist_resize (CtxDrawlist *drawlist, int desired_size); static int -ctx_drawlist_add_single (CtxDrawlist *drawlist, CtxEntry *entry); -static int ctx_drawlist_add_entry (CtxDrawlist *drawlist, CtxEntry *entry); +ctx_drawlist_add_single (CtxDrawlist *drawlist, const CtxEntry *entry); +static int ctx_drawlist_add_entry (CtxDrawlist *drawlist, const CtxEntry *entry); int ctx_drawlist_insert_entry (CtxDrawlist *drawlist, int pos, CtxEntry *entry); int @@ -11564,19 +12700,19 @@ ctx_u8 (CtxCode code, uint8_t e, uint8_t f, uint8_t g, uint8_t h); #define CTX_PROCESS_VOID(cmd) do {\ - CtxEntry commands[4] = {{cmd,{{0}}}};\ + CtxEntry commands[1] = {{cmd,{{0}}}};\ ctx_process (ctx, &commands[0]);}while(0) \ #define CTX_PROCESS_F(cmd,x,y) do {\ - CtxEntry commands[4] = {ctx_f(cmd,x,y),};\ + CtxEntry commands[1] = {ctx_f(cmd,x,y),};\ ctx_process (ctx, &commands[0]);}while(0) \ #define CTX_PROCESS_F1(cmd,x) do {\ - CtxEntry commands[4] = {ctx_f(cmd,x,0),};\ + CtxEntry commands[1] = {ctx_f(cmd,x,0),};\ ctx_process (ctx, &commands[0]);}while(0) \ #define CTX_PROCESS_U32(cmd, x, y) do {\ - CtxEntry commands[4] = {ctx_u32(cmd, x, y)};\ + CtxEntry commands[1] = {ctx_u32(cmd, x, y)};\ ctx_process (ctx, &commands[0]);}while(0) #define CTX_PROCESS_U8(cmd, x) do {\ @@ -11605,38 +12741,57 @@ 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 +typedef union CtxSegment { #if CTX_32BIT_SEGMENTS - uint32_t code; + struct { + int16_t code; + int16_t aa; + int32_t x0; + int32_t y0; + int32_t y1; + int32_t x1; + int32_t val; + int32_t delta; + }; + struct { + int16_t code__; + int16_t aa__; + int32_t y0_; + int32_t y1_; + }; #else - uint16_t code; -#endif - union { -#if CTX_32BIT_SEGMENTS - int32_t s16[4]; -#else - int16_t s16[4]; + struct { + int8_t code; + int8_t aa; + int32_t x0; + int16_t y0; + int16_t y1; + int32_t x1; + }; + struct { + int8_t code_; + int8_t aa_; + int32_t val; + int16_t y0_; + int16_t y1_; + int32_t delta; + }; #endif uint32_t u32[2]; - } data; - int32_t val; - int32_t delta; -} CtxSegment; + } CtxSegment; #pragma pack(pop) - - static inline CtxSegment -ctx_segment_s16 (CtxCode code, int x0, int y0, int x1, int y1) +ctx_segment_s16 (CtxRasterizerCode code, int x0, int y0, int x1, int y1) { - CtxSegment 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; + CtxSegment segment; + segment.x0 = x0; + segment.y0 = y0; + segment.x1 = x1; + segment.y1 = y1; + segment.code = code; + return segment; } static inline void @@ -11647,6 +12802,7 @@ ctx_edgelist_resize (CtxDrawlist *drawlist, int desired_size) static CtxSegment sbuf[CTX_MAX_EDGE_LIST_SIZE]; drawlist->entries = (CtxEntry*)&sbuf[0]; drawlist->size = CTX_MAX_EDGE_LIST_SIZE; + drawlist->flags = CTX_DRAWLIST_DOESNT_OWN_ENTRIES; } #else int new_size = desired_size; @@ -11687,18 +12843,15 @@ ctx_edgelist_resize (CtxDrawlist *drawlist, int desired_size) #endif } - -static inline int +static CTX_INLINE int ctx_edgelist_add_single (CtxDrawlist *drawlist, CtxEntry *entry) { int ret = drawlist->count; - if (CTX_UNLIKELY(ret >= CTX_MAX_EDGE_LIST_SIZE- 20)) - { - return 0; - } if (CTX_UNLIKELY(ret + 2 >= drawlist->size)) { + if (CTX_UNLIKELY(ret+2 >= CTX_MAX_EDGE_LIST_SIZE- 20)) + return 0; int new_ = ctx_maxi (drawlist->size * 2, ret + 1024); new_ = ctx_mini (CTX_MAX_EDGE_LIST_SIZE, new_); ctx_edgelist_resize (drawlist, new_); @@ -11709,6 +12862,137 @@ ctx_edgelist_add_single (CtxDrawlist *drawlist, CtxEntry *entry) return ret; } +// special return values - controlling argument behavior for some codes +#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 inline 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_START_FRAME: + case CTX_END_FRAME: + case CTX_RESTORE: + case CTX_STROKE: + case CTX_FILL: + case CTX_PAINT: + case CTX_DEFINE_FONT: + 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_EXTEND: + case CTX_FONT_SIZE: + case CTX_LINE_JOIN: + case CTX_LINE_CAP: + case CTX_LINE_WIDTH: + case CTX_LINE_DASH_OFFSET: + case CTX_STROKE_POS: + case CTX_FEATHER: + case CTX_LINE_HEIGHT: + case CTX_WRAP_LEFT: + case CTX_WRAP_RIGHT: + 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_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_CONIC_GRADIENT: + 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_ROUND_RECTANGLE: + return 5; + case CTX_ARC: + case CTX_CURVE_TO: + case CTX_REL_CURVE_TO: + case CTX_RADIAL_GRADIENT: + return 6; + case CTX_ARC_TO: + case CTX_REL_ARC_TO: + return 7; + case CTX_APPLY_TRANSFORM: + case CTX_SOURCE_TRANSFORM: + return 9; + case CTX_TEXT: + case CTX_FONT: + 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; + + default: +#if 1 + case CTX_SET_RGBA_U8: + case CTX_NOP: + 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 + return 0; + } +} + #endif @@ -11781,43 +13065,24 @@ ctx_RGBA8_associate_global_alpha_u32 (uint32_t val, uint8_t global_alpha) return g|rb|(a << CTX_RGBA8_A_SHIFT); } -CTX_INLINE static void -ctx_RGBA8_associate_alpha_probably_opaque (uint8_t *u8) +// mixes global alpha in with existing global alpha +inline static uint32_t +ctx_RGBA8_mul_alpha_u32(uint32_t val, uint8_t global_alpha) { - uint32_t a = u8[3];//val>>24;//u8[3]; - if (CTX_UNLIKELY(a!=255)) - { - u8[0] = (u8[0] * a + 255) >> 8; - u8[1] = (u8[1] * a + 255) >> 8; - u8[2] = (u8[2] * a + 255) >> 8; - } + uint32_t a = ((val>>24) * global_alpha + 255) >> 8; + uint32_t g = (((val & CTX_RGBA8_G_MASK) * global_alpha) >> 8) & CTX_RGBA8_G_MASK; + uint32_t rb =(((val & CTX_RGBA8_RB_MASK) * global_alpha) >> 8) & CTX_RGBA8_RB_MASK; + return g|rb|(a << CTX_RGBA8_A_SHIFT); } -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) +CTX_INLINE static uint32_t ctx_bi_RGBA8_alpha (uint32_t isrc00, uint32_t isrc01, uint32_t isrc10, uint32_t isrc11, uint8_t dx, uint8_t dy) { -#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 -#else + if (((isrc00 | isrc01 | isrc10 | isrc11) & CTX_RGBA8_A_MASK) == 0) + return 0; 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); -#endif } #if CTX_GRADIENTS @@ -11826,15 +13091,16 @@ CTX_INLINE static uint32_t ctx_bi_RGBA8 (uint32_t isrc00, uint32_t isrc01, uint3 inline static int ctx_grad_index (CtxRasterizer *rasterizer, float v) { int ret = (int)(v * (rasterizer->gradient_cache_elements - 1) + 0.5f); - ret = ctx_maxi (0, ret); + ret *= (ret>0); ret = ctx_mini (rasterizer->gradient_cache_elements-1, ret); return ret; } -inline static int ctx_grad_index_i (CtxRasterizer *rasterizer, int v) +CTX_INLINE static int ctx_grad_index_i (CtxRasterizer *rasterizer, int v) { v = v >> 8; - return ctx_maxi (0, ctx_mini (rasterizer->gradient_cache_elements-1, v)); + v *= (v>0); + return ctx_mini (rasterizer->gradient_cache_elements-1, v); } //static void @@ -11851,7 +13117,7 @@ _ctx_fragment_gradient_1d_RGBA8 (CtxRasterizer *rasterizer, float x, float y, ui float v = x; uint8_t global_alpha_u8 = rasterizer->state->gstate.global_alpha_u8; CtxGradient *g = &rasterizer->state->gradient; - if (v < 0) { v = 0; } + v *= (v>0); if (v > 1) { v = 1; } if (g->n_stops == 0) @@ -11888,14 +13154,15 @@ _ctx_fragment_gradient_1d_RGBA8 (CtxRasterizer *rasterizer, float x, float y, ui ctx_color_get_rgba8 (rasterizer->state, & (stop->color), stop_rgba); ctx_color_get_rgba8 (rasterizer->state, & (next_stop->color), next_rgba); int dx = (int)((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 rgba[3]=(rgba[3]*global_alpha_u8+255)>>8; + if (rasterizer->swap_red_green) + { + uint8_t tmp = rgba[0]; + rgba[0] = rgba[2]; + rgba[2] = tmp; + } ctx_RGBA8_associate_alpha (rgba); return; } @@ -11953,15 +13220,11 @@ ctx_gradient_cache_prime (CtxRasterizer *rasterizer) CtxSource *source = &rasterizer->state->gstate.source_fill; float length = 100; if (source->type == CTX_SOURCE_LINEAR_GRADIENT) - { length = source->linear_gradient.length; - } - else - if (source->type == CTX_SOURCE_RADIAL_GRADIENT) - { + else if (source->type == CTX_SOURCE_RADIAL_GRADIENT) length = ctx_maxf (source->radial_gradient.r1, source->radial_gradient.r0); - } - // length = CTX_GRADIENT_CACHE_ELEMENTS; + else if (source->type == CTX_SOURCE_CONIC_GRADIENT) + length = CTX_GRADIENT_CACHE_ELEMENTS; { float u = length; float v = length; const CtxMatrix *m = &rasterizer->state->gstate.transform; @@ -12044,7 +13307,7 @@ ctx_fragment_gradient_1d_RGBAF (CtxRasterizer *rasterizer, float v, float y, flo { float global_alpha = rasterizer->state->gstate.global_alpha_f; CtxGradient *g = &rasterizer->state->gradient; - if (v < 0) { v = 0; } + v *= (v>0); if (v > 1) { v = 1; } if (g->n_stops == 0) { @@ -12079,10 +13342,13 @@ ctx_fragment_gradient_1d_RGBAF (CtxRasterizer *rasterizer, float v, float y, flo 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 = (int)((v - stop->pos) / (next_stop->pos - stop->pos)); + float 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); } rgba[3] *= global_alpha; + for (int c = 0; c < 3; c++) + rgba[c] *= rgba[3]; + return; } else @@ -12091,6 +13357,8 @@ ctx_fragment_gradient_1d_RGBAF (CtxRasterizer *rasterizer, float v, float y, flo } ctx_color_get_rgba (rasterizer->state, color, rgba); rgba[3] *= global_alpha; + for (int c = 0; c < 3; c++) + rgba[c] *= rgba[3]; } #endif @@ -12099,78 +13367,94 @@ ctx_fragment_image_RGBA8 (CtxRasterizer *rasterizer, float x, float y, float z, { uint8_t *rgba = (uint8_t *) out; CtxSource *g = &rasterizer->state->gstate.source_fill; +#if CTX_ENABLE_CM CtxBuffer *buffer = g->texture.buffer->color_managed?g->texture.buffer->color_managed:g->texture.buffer; +#else + CtxBuffer *buffer = g->texture.buffer; +#endif uint8_t global_alpha_u8 = rasterizer->state->gstate.global_alpha_u8; + uint8_t is_assoc = (buffer->format->pixel_format == CTX_FORMAT_RGBA8 || + buffer->format->pixel_format == CTX_FORMAT_BGRA8); + int width = buffer->width; + int height = buffer->height; for (int i = 0; i < count; i ++) { int u = (int)x; int v = (int)y; - int width = buffer->width; - int height = buffer->height; - if ( u < 0 || v < 0 || - u >= width || - v >= height) - { + if ( (u < 0) | (v < 0) | (u >= width) | (v >= height)) *((uint32_t*)(rgba)) = 0; - } 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.9f; - float dy = (y-(int)(y)) * 255.9f; - uint8_t dxb = (uint8_t)dx; - uint8_t dyb = (uint8_t)dy; - - switch (bpp) - { - case 1: - rgba[0] = rgba[1] = rgba[2] = ctx_lerp_u8 (ctx_lerp_u8 (src00[0], src01[0], dxb), - ctx_lerp_u8 (src10[0], src11[0], dxb), dyb); - rgba[3] = global_alpha_u8; - break; - case 2: - rgba[0] = rgba[1] = rgba[2] = ctx_lerp_u8 (ctx_lerp_u8 (src00[0], src01[0], dxb), - ctx_lerp_u8 (src10[0], src11[0], dxb), dyb); - rgba[3] = ctx_lerp_u8 (ctx_lerp_u8 (src00[1], src01[1], dxb), - ctx_lerp_u8 (src10[1], src11[1], dxb), dyb); - rgba[3] = (rgba[3] * global_alpha_u8) / 255; - break; - case 3: - for (int c = 0; c < bpp; c++) - { rgba[c] = ctx_lerp_u8 (ctx_lerp_u8 (src00[c], src01[c], dxb), - ctx_lerp_u8 (src10[c], src11[c], dxb), dyb); - + uint8_t *src00 = (uint8_t *) buffer->data; + src00 += v * buffer->stride + u * bpp; + uint8_t *src01 = src00; + if ( u + 1 < width) + { + src01 = src00 + bpp; } - rgba[3]=global_alpha_u8; - break; - break; - case 4: - for (int c = 0; c < bpp; c++) - { rgba[c] = ctx_lerp_u8 (ctx_lerp_u8 (src00[c], src01[c], dxb), - ctx_lerp_u8 (src10[c], src11[c], dxb), dyb); - + 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.9f; + float dy = (y-(int)(y)) * 255.9f; + uint8_t dxb = (uint8_t)dx; + uint8_t dyb = (uint8_t)dy; + + switch (bpp) + { + case 1: + rgba[0] = rgba[1] = rgba[2] = ctx_lerp_u8 (ctx_lerp_u8 (src00[0], src01[0], dxb), + ctx_lerp_u8 (src10[0], src11[0], dxb), dyb); + rgba[3] = global_alpha_u8; + break; + case 2: // TODO : could be RGB565 + rgba[0] = rgba[1] = rgba[2] = ctx_lerp_u8 (ctx_lerp_u8 (src00[0], src01[0], dxb), + ctx_lerp_u8 (src10[0], src11[0], dxb), dyb); + rgba[3] = ctx_lerp_u8 (ctx_lerp_u8 (src00[1], src01[1], dxb), + ctx_lerp_u8 (src10[1], src11[1], dxb), dyb); + rgba[3] = (rgba[3] * global_alpha_u8) / 255; + break; + case 3: + for (int c = 0; c < bpp; c++) + { rgba[c] = ctx_lerp_u8 (ctx_lerp_u8 (src00[c], src01[c], dxb), + ctx_lerp_u8 (src10[c], src11[c], dxb), dyb); + + } + rgba[3]=global_alpha_u8; + break; + break; + case 4: + if (is_assoc) + { + if (global_alpha_u8==255) { + for (int c = 0; c < bpp; c++) + rgba[c] = ctx_lerp_u8 (ctx_lerp_u8 (src00[c], src01[c], dxb), + ctx_lerp_u8 (src10[c], src11[c], dxb), dyb); + } + else + for (int c = 0; c < bpp; c++) + rgba[c] = (ctx_lerp_u8 (ctx_lerp_u8 (src00[c], src01[c], dxb), + ctx_lerp_u8 (src10[c], src11[c], dxb), dyb) * global_alpha_u8) / 255; + } + else + { + for (int c = 0; c < bpp; c++) + { rgba[c] = ctx_lerp_u8 (ctx_lerp_u8 (src00[c], src01[c], dxb), + ctx_lerp_u8 (src10[c], src11[c], dxb), dyb); + + } + rgba[3] = (rgba[3] * global_alpha_u8) / 255; + } } - rgba[3] = (rgba[3] * global_alpha_u8) / 255; - } - } else { @@ -12183,7 +13467,7 @@ ctx_fragment_image_RGBA8 (CtxRasterizer *rasterizer, float x, float y, float z, { rgba[c] = src[0]; } rgba[3] = global_alpha_u8; break; - case 2: + case 2: // todo could be RGB 565 for (int c = 0; c < 3; c++) { rgba[c] = src[0]; } rgba[3] = src[1]; @@ -12195,9 +13479,21 @@ ctx_fragment_image_RGBA8 (CtxRasterizer *rasterizer, float x, float y, float z, rgba[3] = global_alpha_u8; break; case 4: - for (int c = 0; c < 4; c++) - { rgba[c] = src[c]; } - rgba[3] = (rgba[3] * global_alpha_u8) / 255; + if (is_assoc) + { + if (global_alpha_u8==255) + for (int c = 0; c < 4; c++) + rgba[c] = src[c]; + else + for (int c = 0; c < 4; c++) + rgba[c] = (src[c] * global_alpha_u8)/255; + } + else + { + for (int c = 0; c < 4; c++) + { rgba[c] = src[c]; } + rgba[3] = (rgba[3] * global_alpha_u8) / 255; + } break; } @@ -12209,7 +13505,8 @@ ctx_fragment_image_RGBA8 (CtxRasterizer *rasterizer, float x, float y, float z, rgba[2] = tmp; } } - ctx_RGBA8_associate_alpha_probably_opaque (rgba); + if (!is_assoc) + ctx_RGBA8_associate_alpha (rgba); rgba += 4; x += dx; y += dy; @@ -12353,7 +13650,11 @@ ctx_fragment_image_rgb8_RGBA8_box (CtxRasterizer *rasterizer, { uint8_t *rgba = (uint8_t *) out; CtxSource *g = &rasterizer->state->gstate.source_fill; +#if CTX_ENABLE_CM CtxBuffer *buffer = g->texture.buffer->color_managed?g->texture.buffer->color_managed:g->texture.buffer; +#else + CtxBuffer *buffer = g->texture.buffer; +#endif int width = buffer->width; int height = buffer->height; uint8_t global_alpha_u8 = rasterizer->state->gstate.global_alpha_u8; @@ -12402,7 +13703,7 @@ ctx_fragment_image_rgb8_RGBA8_box (CtxRasterizer *rasterizer, int recip = 65536/count; for (int c = 0; c < bpp; c++) rgba[c] = sum[c] * recip >> 16; - ctx_RGBA8_associate_alpha_probably_opaque (rgba); + ctx_RGBA8_associate_alpha (rgba); } rgba += 4; x += dx; @@ -12446,7 +13747,7 @@ ctx_RGBA8_apply_global_alpha_and_associate (CtxRasterizer *rasterizer, { for (int i = 0; i < count; i++) { - ctx_RGBA8_associate_alpha_probably_opaque (rgba); + ctx_RGBA8_associate_alpha (rgba); rgba += 4; } } @@ -12482,7 +13783,11 @@ ctx_fragment_image_rgb8_RGBA8_nearest (CtxRasterizer *rasterizer, uint8_t global_alpha_u8 = rasterizer->state->gstate.global_alpha_u8; uint8_t *rgba = (uint8_t *) out; CtxSource *g = &rasterizer->state->gstate.source_fill; +#if CTX_ENABLE_CM CtxBuffer *buffer = g->texture.buffer->color_managed?g->texture.buffer->color_managed:g->texture.buffer; +#else + CtxBuffer *buffer = g->texture.buffer; +#endif const int bwidth = buffer->width; const int bheight = buffer->height; unsigned int i = 0; @@ -12542,7 +13847,7 @@ ctx_fragment_image_rgb8_RGBA8_nearest (CtxRasterizer *rasterizer, for (unsigned int c = 0; c < 3; c++) rgba[c] = data[(bwidth *v +u)*3+c]; rgba[3] = global_alpha_u8; - ctx_RGBA8_associate_alpha_probably_opaque (rgba); + ctx_RGBA8_associate_alpha (rgba); xi += xi_delta; yi += yi_delta; zi += zi_delta; @@ -12573,7 +13878,7 @@ ctx_fragment_image_rgb8_RGBA8 (CtxRasterizer *rasterizer, if (factor <= 0.50f) ctx_fragment_image_rgb8_RGBA8_box_swap_red_green (rasterizer,x,y,z,out,count,dx,dy,dz); #if CTX_ALWAYS_USE_NEAREST_FOR_SCALE1 - else if (factor > 0.99f && factor < 1.01f) + else if ((factor > 0.99f) & (factor < 1.01f)) ctx_fragment_image_rgb8_RGBA8_nearest_swap_red_green (rasterizer,x,y,z, out,count,dx,dy,dz); #endif @@ -12596,7 +13901,7 @@ ctx_fragment_image_rgb8_RGBA8 (CtxRasterizer *rasterizer, ctx_fragment_image_rgb8_RGBA8_box (rasterizer,x,y,z,out, count,dx,dy,dz); #if CTX_ALWAYS_USE_NEAREST_FOR_SCALE1 - else if (factor > 0.99f && factor < 1.01f) + else if ((factor > 0.99f) & (factor < 1.01f)) ctx_fragment_image_rgb8_RGBA8_nearest (rasterizer, x, y, z, out, count, dx, dy, dz); #endif else @@ -12620,7 +13925,11 @@ ctx_fragment_image_rgba8_RGBA8_box (CtxRasterizer *rasterizer, { uint8_t *rgba = (uint8_t *) out; CtxSource *g = &rasterizer->state->gstate.source_fill; +#if CTX_ENABLE_CM CtxBuffer *buffer = g->texture.buffer->color_managed?g->texture.buffer->color_managed:g->texture.buffer; +#else + CtxBuffer *buffer = g->texture.buffer; +#endif int width = buffer->width; int height = buffer->height; uint8_t global_alpha_u8 = rasterizer->state->gstate.global_alpha_u8; @@ -12669,7 +13978,7 @@ ctx_fragment_image_rgba8_RGBA8_box (CtxRasterizer *rasterizer, for (int c = 0; c < bpp; c++) rgba[c] = sum[c] * recip >> 16; rgba[3]=rgba[3]*global_alpha_u8/255; // gets lost - ctx_RGBA8_associate_alpha_probably_opaque (rgba); + ctx_RGBA8_associate_alpha (rgba); } rgba += 4; x += dx; @@ -12693,66 +14002,77 @@ static void ctx_fragment_image_rgba8_RGBA8_nearest_copy (CtxRasterizer *rasterizer, float x, float y, float z, void *out, int scount, float dx, float dy, float dz) -{ +{ unsigned int count = scount; CtxSource *g = &rasterizer->state->gstate.source_fill; - CtxBuffer *buffer = - g->texture.buffer->color_managed?g->texture.buffer->color_managed:g->texture.buffer; - uint32_t *dst = (uint32_t*)out; -#if 0 - for (int i = 0; i < scount; i++) - dst[i] = (255<<24)+128; - return; +#if CTX_ENABLE_CM + CtxBuffer *buffer = g->texture.buffer->color_managed?g->texture.buffer->color_managed:g->texture.buffer; +#else + CtxBuffer *buffer = g->texture.buffer; #endif + uint32_t *dst = (uint32_t*)out; int bwidth = buffer->width; int bheight = buffer->height; int u = (int)x; int v = (int)y; - uint32_t *src = ((uint32_t*)buffer->data) + bwidth * v + u; - if (CTX_UNLIKELY(!(v >= 0 && v < bheight))) + if ((!((v >= 0) & (v < bheight)))) { - for (unsigned i = 0 ; i < count; i++) - *dst++ = 0; + memset (dst, 0, count*4); return; } - -#if 1 + uint32_t *src = ((uint32_t*)buffer->data) + bwidth * v + u; +#if defined(__GNUC__) && !defined(__clang__) int pre = ctx_mini(ctx_maxi(-u,0), count); - memset (dst, 0, pre); - dst +=pre; + for (int i = 0; i < pre;i++) + { *dst++ = 0; } count-=pre; src+=pre; u+=pre; -#else - while (count && !(u >= 0)) - { - *dst++ = 0; - src ++; - u++; - count--; - } -#endif - + int limit = ctx_mini (count, bwidth - u); if (limit>0) { - memcpy (dst, src, limit * 4); - dst += limit; + for (int i = 0; i < limit;i++) + { *dst++ = *src++; } } - memset (dst, 0, count - limit); -//ctx_RGBA8_apply_global_alpha_and_associate (rasterizer, (uint8_t*)out, count); + count-=limit; + for (unsigned int i = 0; i < count; i++) + *dst++ = 0; +#else + int i = 0; + for (; (u<0) & ((unsigned)i < count); i++,u++,src++) + *dst++ = 0; + for (; (ustate->gstate.source_fill; - CtxBuffer *buffer = - g->texture.buffer->color_managed?g->texture.buffer->color_managed:g->texture.buffer; +#if CTX_ENABLE_CM + CtxBuffer *buffer = g->texture.buffer->color_managed?g->texture.buffer->color_managed:g->texture.buffer; +#else + CtxBuffer *buffer = g->texture.buffer; +#endif uint32_t *dst = (uint32_t*)out; int bwidth = buffer->width; int bheight = buffer->height; @@ -12775,7 +14095,7 @@ ctx_fragment_image_rgba8_RGBA8_nearest_copy_repeat (CtxRasterizer *rasterizer, } } -static inline int +static CTX_INLINE void _ctx_coords_restrict (CtxExtend extend, int *u, int *v, int bwidth, int bheight) @@ -12793,7 +14113,8 @@ _ctx_coords_restrict (CtxExtend extend, while (*v < 0) *v += bheight * 4096; *v %= bheight; } - return 1; + // return 1; + break; case CTX_EXTEND_REFLECT: if (u) { @@ -12812,25 +14133,21 @@ _ctx_coords_restrict (CtxExtend extend, (*v= bwidth) return 0; + if (u) { int val=*u; val *= (val>0); val= (val>=bwidth)*bwidth + val * (val0); val= (val>=bheight)*bheight + val * (val= bheight) return 0; - } - return 1; } - return 0; + //return 0; } static void @@ -12839,10 +14156,14 @@ ctx_fragment_image_rgba8_RGBA8_nearest_affine (CtxRasterizer *rasterizer, void *out, int scount, float dx, float dy, float dz) { unsigned int count = scount; - //uint8_t global_alpha_u8 = rasterizer->state->gstate.global_alpha_u8; + uint8_t global_alpha_u8 = rasterizer->state->gstate.global_alpha_u8; uint8_t *rgba = (uint8_t *) out; CtxSource *g = &rasterizer->state->gstate.source_fill; +#if CTX_ENABLE_CM CtxBuffer *buffer = g->texture.buffer->color_managed?g->texture.buffer->color_managed:g->texture.buffer; +#else + CtxBuffer *buffer = g->texture.buffer; +#endif CtxExtend extend = rasterizer->state->gstate.extend; const int bwidth = buffer->width; const int bheight = buffer->height; @@ -12862,10 +14183,10 @@ ctx_fragment_image_rgba8_RGBA8_nearest_affine (CtxRasterizer *rasterizer, uint32_t *edst = ((uint32_t*)out)+(count-1); for (; i < count; ) { - if ((u1>>16) <0 || - (v1>>16) <0 || - (u1>>16) >= (bwidth) - 1 || - (v1>>16) >= (bheight) - 1) + if (((u1>>16) <0) | + ((v1>>16) <0) | + ((u1>>16) >= (bwidth) - 1) | + ((v1>>16) >= (bheight) - 1)) { *edst-- = 0; count --; @@ -12879,51 +14200,72 @@ ctx_fragment_image_rgba8_RGBA8_nearest_affine (CtxRasterizer *rasterizer, { int u = xi >> 16; int v = yi >> 16; - if ( u <= 0 || v <= 0 || u+1 >= bwidth-1 || v+1 >= bheight-1) + if ((u <= 0) | (v <= 0) | (u+1 >= bwidth-1) | (v+1 >= bheight-1)) { *((uint32_t*)(rgba))= 0; } - else - break; + else break; xi += xi_delta; yi += yi_delta; rgba += 4; } + if (global_alpha_u8 == 255) while (i < count) { int u = xi >> 16; int v = yi >> 16; - //((uint32_t*)(&rgba[0]))[0] = - // ctx_RGBA8_associate_global_alpha_u32 (data[bwidth *v +u], global_alpha_u8); ((uint32_t*)(&rgba[0]))[0] = data[bwidth *v +u]; xi += xi_delta; yi += yi_delta; rgba += 4; i++; + } + else + while (i < count) + { + int u = xi >> 16; + int v = yi >> 16; + ((uint32_t*)(&rgba[0]))[0] = + ctx_RGBA8_mul_alpha_u32 (data[bwidth *v +u], global_alpha_u8); + xi += xi_delta; + yi += yi_delta; + rgba += 4; + i++; } } break; default: + if (global_alpha_u8 == 255) while (i < count) { int u = xi >> 16; int v = yi >> 16; _ctx_coords_restrict (extend, &u, &v, bwidth, bheight); - //((uint32_t*)(&rgba[0]))[0] = - // ctx_RGBA8_associate_global_alpha_u32 (data[bwidth *v +u], global_alpha_u8); ((uint32_t*)(&rgba[0]))[0] = data[bwidth *v +u]; xi += xi_delta; yi += yi_delta; rgba += 4; i++; } - break; + else + while (i < count) + { + int u = xi >> 16; + int v = yi >> 16; + _ctx_coords_restrict (extend, &u, &v, bwidth, bheight); + ((uint32_t*)(&rgba[0]))[0] = + ctx_RGBA8_mul_alpha_u32 (data[bwidth *v +u], global_alpha_u8); + xi += xi_delta; + yi += yi_delta; + rgba += 4; + i++; + } + break; } } - static void ctx_fragment_image_rgba8_RGBA8_nearest_scale (CtxRasterizer *rasterizer, float x, float y, float z, @@ -12931,10 +14273,14 @@ ctx_fragment_image_rgba8_RGBA8_nearest_scale (CtxRasterizer *rasterizer, { unsigned int count = scount; CtxSource *g = &rasterizer->state->gstate.source_fill; - CtxBuffer *buffer = NULL; + uint8_t global_alpha_u8 = rasterizer->state->gstate.global_alpha_u8; CtxExtend extend = rasterizer->state->gstate.extend; uint32_t *src = NULL; - buffer = g->texture.buffer->color_managed?g->texture.buffer->color_managed:g->texture.buffer; +#if CTX_ENABLE_CM + CtxBuffer *buffer = g->texture.buffer->color_managed?g->texture.buffer->color_managed:g->texture.buffer; +#else + CtxBuffer *buffer = g->texture.buffer; +#endif int ideltax = (int)(dx * 65536); uint32_t *dst = (uint32_t*)out; int bwidth = buffer->width; @@ -12959,7 +14305,7 @@ ctx_fragment_image_rgba8_RGBA8_nearest_scale (CtxRasterizer *rasterizer, uint32_t *edst = ((uint32_t*)out)+count - 1; for (; i < count; ) { - if (u1 <0 || v1 < 0 || u1 >= bbwidth || v1 >= bbheight) + if ((u1 <0) | (v1 < 0) | (u1 >= bbwidth) | (v1 >= bbheight)) { *edst-- = 0; count --; @@ -12970,7 +14316,7 @@ ctx_fragment_image_rgba8_RGBA8_nearest_scale (CtxRasterizer *rasterizer, for (i = 0; i < count; i ++) { - if (ix < 0 || iy < 0 || ix >= bbwidth || iy >= bbheight) + if ((ix < 0) | (iy < 0) | (ix >= bbwidth) | (iy >= bbheight)) { *dst++ = 0; x += dx; @@ -12982,12 +14328,20 @@ ctx_fragment_image_rgba8_RGBA8_nearest_scale (CtxRasterizer *rasterizer, int v = iy >> 16; int u = ix >> 16; int o = (v)*bwidth; - for (; i < count; i ++) - { - u = ix >> 16; - *dst++ = src[o + (u)]; - ix += ideltax; - } + if (global_alpha_u8==255) + for (; i < count; i ++) + { + u = ix >> 16; + *dst++ = src[o + (u)]; + ix += ideltax; + } + else + for (; i < count; i ++) + { + u = ix >> 16; + *dst++ = ctx_RGBA8_mul_alpha_u32 (src[o + (u)], global_alpha_u8); + ix += ideltax; + } } else { @@ -12996,6 +14350,7 @@ ctx_fragment_image_rgba8_RGBA8_nearest_scale (CtxRasterizer *rasterizer, int u = ix >> 16; _ctx_coords_restrict (extend, &u, &v, bwidth, bheight); int o = (v)*bwidth; + if (global_alpha_u8==255) for (; i < count; i ++) { u = ix >> 16; @@ -13003,9 +14358,15 @@ ctx_fragment_image_rgba8_RGBA8_nearest_scale (CtxRasterizer *rasterizer, *dst++ = src[o + (u)]; ix += ideltax; } + else + { + u = ix >> 16; + _ctx_coords_restrict (extend, &u, &v, bwidth, bheight); + *dst++ = ctx_RGBA8_mul_alpha_u32 (src[o + (u)], global_alpha_u8); + ix += ideltax; + } } } -// ctx_RGBA8_apply_global_alpha_and_associate (rasterizer, (uint8_t*)out, count); } static void @@ -13014,10 +14375,14 @@ ctx_fragment_image_rgba8_RGBA8_nearest_generic (CtxRasterizer *rasterizer, void *out, int scount, float dx, float dy, float dz) { unsigned int count = scount; - //uint8_t global_alpha_u8 = rasterizer->state->gstate.global_alpha_u8; + uint8_t global_alpha_u8 = rasterizer->state->gstate.global_alpha_u8; uint8_t *rgba = (uint8_t *) out; CtxSource *g = &rasterizer->state->gstate.source_fill; +#if CTX_ENABLE_CM CtxBuffer *buffer = g->texture.buffer->color_managed?g->texture.buffer->color_managed:g->texture.buffer; +#else + CtxBuffer *buffer = g->texture.buffer; +#endif CtxExtend extend = rasterizer->state->gstate.extend; const int bwidth = buffer->width; const int bheight = buffer->height; @@ -13041,10 +14406,11 @@ ctx_fragment_image_rgba8_RGBA8_nearest_generic (CtxRasterizer *rasterizer, for (; i < count; ) { float z_recip = (z1!=0) * (1.0f/z1); - if ((u1*z_recip) <0 || - (v1*z_recip) <0 || - (u1*z_recip) >= (bwidth) - 1 || - (v1*z_recip) >= (bheight) - 1) + + if (((u1*z_recip) <0) | + ((v1*z_recip) <0) | + ((u1*z_recip) >= (bwidth) - 1) | + ((v1*z_recip) >= (bheight) - 1)) { *edst-- = 0; count --; @@ -13060,7 +14426,7 @@ ctx_fragment_image_rgba8_RGBA8_nearest_generic (CtxRasterizer *rasterizer, float z_recip = (zi!=0) * (1.0f/zi); int u = (int)(xi * z_recip); int v = (int)(yi * z_recip); - if ( u <= 0 || v <= 0 || u+1 >= bwidth-1 || v+1 >= bheight-1) + if ( (u <= 0) | (v <= 0) | (u+1 >= bwidth-1) | (v+1 >= bheight-1)) { *((uint32_t*)(rgba))= 0; } @@ -13072,13 +14438,26 @@ ctx_fragment_image_rgba8_RGBA8_nearest_generic (CtxRasterizer *rasterizer, rgba += 4; } + if (global_alpha_u8!=255) + while (i < count) + { + float z_recip = (zi!=0) * (1.0f/zi); + int u = (int)(xi * z_recip); + int v = (int)(yi * z_recip); + ((uint32_t*)(&rgba[0]))[0] = + ctx_RGBA8_mul_alpha_u32 (data[bwidth *v +u], global_alpha_u8); + xi += xi_delta; + yi += yi_delta; + zi += zi_delta; + rgba += 4; + i++; + } + else while (i < count) { float z_recip = (zi!=0) * (1.0f/zi); int u = (int)(xi * z_recip); int v = (int)(yi * z_recip); - //((uint32_t*)(&rgba[0]))[0] = - // ctx_RGBA8_associate_global_alpha_u32 (data[bwidth *v +u], global_alpha_u8); ((uint32_t*)(&rgba[0]))[0] = data[bwidth *v +u]; xi += xi_delta; yi += yi_delta; @@ -13089,14 +14468,28 @@ ctx_fragment_image_rgba8_RGBA8_nearest_generic (CtxRasterizer *rasterizer, } break; default: + if (global_alpha_u8!=255) + while (i < count) + { + float z_recip = (zi!=0) * (1.0f/zi); + int u = (int)(xi * z_recip); + int v = (int)(yi * z_recip); + _ctx_coords_restrict (extend, &u, &v, bwidth, bheight); + ((uint32_t*)(&rgba[0]))[0] = + ctx_RGBA8_mul_alpha_u32 (data[bwidth *v +u], global_alpha_u8); + xi += xi_delta; + yi += yi_delta; + zi += zi_delta; + rgba += 4; + i++; + } + else while (i < count) { float z_recip = (zi!=0) * (1.0f/zi); int u = (int)(xi * z_recip); int v = (int)(yi * z_recip); _ctx_coords_restrict (extend, &u, &v, bwidth, bheight); - //((uint32_t*)(&rgba[0]))[0] = - // ctx_RGBA8_associate_global_alpha_u32 (data[bwidth *v +u], global_alpha_u8); ((uint32_t*)(&rgba[0]))[0] = data[bwidth *v +u]; xi += xi_delta; yi += yi_delta; @@ -13115,9 +14508,9 @@ ctx_fragment_image_rgba8_RGBA8_nearest (CtxRasterizer *rasterizer, { unsigned int count = icount; CtxExtend extend = rasterizer->state->gstate.extend; - if (z == 1.0f && dz == 0.0f) // this also catches other constant z! + if ((z == 1.0f) & (dz == 0.0f)) // this also catches other constant z! { - if (dy == 0.0f && dx == 1.0f && extend == CTX_EXTEND_NONE) + if ((dy == 0.0f) & (dx == 1.0f) & (extend == CTX_EXTEND_NONE)) ctx_fragment_image_rgba8_RGBA8_nearest_copy (rasterizer, x, y, z, out, count, dx, dy, dz); else ctx_fragment_image_rgba8_RGBA8_nearest_affine (rasterizer, x, y, z, out, count, dx, dy, dz); @@ -13129,6 +14522,204 @@ ctx_fragment_image_rgba8_RGBA8_nearest (CtxRasterizer *rasterizer, } + +static inline void +ctx_fragment_image_rgba8_RGBA8_bi_scale_with_alpha (CtxRasterizer *rasterizer, + float x, float y, float z, + void *out, int scount, float dx, float dy, float dz) +{ + uint8_t global_alpha_u8 = rasterizer->state->gstate.global_alpha_u8; + uint32_t count = scount; + x -= 0.5f; + y -= 0.5f; + uint8_t *rgba = (uint8_t *) out; + CtxSource *g = &rasterizer->state->gstate.source_fill; + CtxExtend extend = rasterizer->state->gstate.extend; +#if CTX_ENABLE_CM + CtxBuffer *buffer = g->texture.buffer->color_managed?g->texture.buffer->color_managed:g->texture.buffer; +#else + CtxBuffer *buffer = g->texture.buffer; +#endif + const int bwidth = buffer->width; + const int bheight = buffer->height; + unsigned int i = 0; + + if (!extend) + { + if (!((y >= 0) & (y < bheight))) + { + uint32_t *dst = (uint32_t*)rgba; + for (i = 0 ; i < count; i++) + *dst++ = 0; + return; + } + } + + //x+=1; // XXX off by one somewhere? ,, needed for alignment with nearest + + int32_t yi = (int)(y * 65536); + int32_t xi = (int)(x * 65536); + + int xi_delta = (int)(dx * 65536); + + if (!extend) + { + int32_t u1 = xi + xi_delta* (count-1); + uint32_t *edst = ((uint32_t*)out)+(count-1); + for (; i < count; ) + { + if ((u1 <0) | (u1 +65536 >= (bwidth<<16))) + { + *edst-- = 0; + count --; + u1 -= xi_delta; + } + else break; + } + for (i= 0; i < count; i ++) + { + int u = xi >> 16; + if ((u < 0) | (u >= bwidth-1)) + { + *((uint32_t*)(rgba))= 0; + xi += xi_delta; + rgba += 4; + } + else + break; + } + } + + + int v = yi >> 16; + + + int dv = (yi >> 8) & 0xff; + + int u = xi >> 16; + + int v1 = v+1; + + _ctx_coords_restrict (extend, &u, &v, bwidth, bheight); + _ctx_coords_restrict (extend, NULL, &v1, bwidth, bheight); + + uint32_t *data = ((uint32_t*)buffer->data) + bwidth * v; + uint32_t *ndata = data + bwidth * !((!extend) & (v1 > bheight-1)); + + if (extend) + { + if (xi_delta == 65536) + { + uint32_t *src0 = data, *src1 = ndata; + uint32_t s1_ga = 0, s1_rb = 0; + int du = (xi >> 8) & 0xff; + + src0 = data + u; + src1 = ndata + u; + ctx_lerp_RGBA8_split (src0[0],src1[0], dv, &s1_ga, &s1_rb); + + for (; i < count; i ++) + { + uint32_t s0_ga = s1_ga; + uint32_t s0_rb = s1_rb; + _ctx_coords_restrict (extend, &u, NULL, bwidth, bheight); + ctx_lerp_RGBA8_split (src0[1],src1[1], dv, &s1_ga, &s1_rb); + ((uint32_t*)(&rgba[0]))[0] = + ctx_RGBA8_mul_alpha_u32 ( + ctx_lerp_RGBA8_merge (s0_ga, s0_rb, s1_ga, s1_rb, du), global_alpha_u8); + rgba += 4; + u++; + src0 ++; + src1 ++; + } + } + else + { + uint32_t s0_ga = 0, s1_ga = 0, s0_rb = 0, s1_rb = 0; + int prev_u = -1000; + for (; (i < count); i++) + { + if (prev_u != u) + { + if (prev_u == u-1) + { + s0_ga = s1_ga; + s0_rb = s1_rb; + ctx_lerp_RGBA8_split (data[u+1],ndata[u+1], dv, &s1_ga, &s1_rb); + } + else + { + ctx_lerp_RGBA8_split (data[u],ndata[u], dv, &s0_ga, &s0_rb); + ctx_lerp_RGBA8_split (data[u+1],ndata[u+1], dv, &s1_ga, &s1_rb); + } + prev_u = u; + } + ((uint32_t*)(&rgba[0]))[0] = + ctx_RGBA8_mul_alpha_u32 ( + ctx_lerp_RGBA8_merge (s0_ga, s0_rb, s1_ga, s1_rb, (xi>>8)), global_alpha_u8); + rgba += 4; + u = (xi+=xi_delta) >> 16; + _ctx_coords_restrict (extend, &u, NULL, bwidth, bheight); + } + } + } + else + { + if (xi_delta == 65536) + { + uint32_t *src0 = data, *src1 = ndata; + uint32_t s1_ga = 0, s1_rb = 0; + int du = (xi >> 8) & 0xff; + + src0 = data + u; + src1 = ndata + u; + ctx_lerp_RGBA8_split (src0[0],src1[0], dv, &s1_ga, &s1_rb); + + for (; i < count; i ++) + { + uint32_t s0_ga = s1_ga; + uint32_t s0_rb = s1_rb; + ctx_lerp_RGBA8_split (src0[1],src1[1], dv, &s1_ga, &s1_rb); + ((uint32_t*)(&rgba[0]))[0] = + ctx_RGBA8_mul_alpha_u32 ( + ctx_lerp_RGBA8_merge (s0_ga, s0_rb, s1_ga, s1_rb, du), global_alpha_u8); + rgba += 4; + u++; + src0 ++; + src1 ++; + } + } + else + { + uint32_t s0_ga = 0, s1_ga = 0, s0_rb = 0, s1_rb = 0; + int prev_u = -1000; + for (; (i < count); i++) + { + if (prev_u != u) + { + if (prev_u == u-1) + { + s0_ga = s1_ga; + s0_rb = s1_rb; + ctx_lerp_RGBA8_split (data[u+1],ndata[u+1], dv, &s1_ga, &s1_rb); + } + else + { + ctx_lerp_RGBA8_split (data[u],ndata[u], dv, &s0_ga, &s0_rb); + ctx_lerp_RGBA8_split (data[u+1],ndata[u+1], dv, &s1_ga, &s1_rb); + } + prev_u = u; + } + ((uint32_t*)(&rgba[0]))[0] = + ctx_RGBA8_mul_alpha_u32 ( + ctx_lerp_RGBA8_merge (s0_ga, s0_rb, s1_ga, s1_rb, (xi>>8)), global_alpha_u8); + rgba += 4; + u = (xi+=xi_delta) >> 16; + } + } + } +} + static inline void ctx_fragment_image_rgba8_RGBA8_bi_scale (CtxRasterizer *rasterizer, float x, float y, float z, @@ -13137,18 +14728,21 @@ ctx_fragment_image_rgba8_RGBA8_bi_scale (CtxRasterizer *rasterizer, uint32_t count = scount; x -= 0.5f; y -= 0.5f; - uint8_t global_alpha_u8 = rasterizer->state->gstate.global_alpha_u8; uint8_t *rgba = (uint8_t *) out; CtxSource *g = &rasterizer->state->gstate.source_fill; CtxExtend extend = rasterizer->state->gstate.extend; - CtxBuffer *buffer = g->texture.buffer->color_managed?g->texture.buffer->color_managed:g->texture.buffer; +#if CTX_ENABLE_CM + CtxBuffer *buffer = g->texture.buffer->color_managed?g->texture.buffer->color_managed:g->texture.buffer; +#else + CtxBuffer *buffer = g->texture.buffer; +#endif const int bwidth = buffer->width; const int bheight = buffer->height; unsigned int i = 0; if (!extend) { - if (!(y >= 0 && y < bheight)) + if (!((y >= 0) & (y < bheight))) { uint32_t *dst = (uint32_t*)rgba; for (i = 0 ; i < count; i++) @@ -13170,7 +14764,7 @@ ctx_fragment_image_rgba8_RGBA8_bi_scale (CtxRasterizer *rasterizer, uint32_t *edst = ((uint32_t*)out)+(count-1); for (; i < count; ) { - if (u1 <0 || u1 +65536 >= (bwidth<<16)) + if ((u1 <0) | (u1 +65536 >= (bwidth<<16))) { *edst-- = 0; count --; @@ -13181,7 +14775,7 @@ ctx_fragment_image_rgba8_RGBA8_bi_scale (CtxRasterizer *rasterizer, for (i= 0; i < count; i ++) { int u = xi >> 16; - if ( u < 0 || u >= bwidth-1) + if ((u < 0) | (u >= bwidth-1)) { *((uint32_t*)(rgba))= 0; xi += xi_delta; @@ -13194,10 +14788,7 @@ ctx_fragment_image_rgba8_RGBA8_bi_scale (CtxRasterizer *rasterizer, int v = yi >> 16; - - int dv = (yi >> 8) & 0xff; - int u = xi >> 16; int v1 = v+1; @@ -13206,9 +14797,7 @@ ctx_fragment_image_rgba8_RGBA8_bi_scale (CtxRasterizer *rasterizer, _ctx_coords_restrict (extend, NULL, &v1, bwidth, bheight); uint32_t *data = ((uint32_t*)buffer->data) + bwidth * v; - uint32_t *ndata = ((uint32_t*)buffer->data) + bwidth * v1; - - if (!extend && v1 > bheight-1) ndata = data; + uint32_t *ndata = data + bwidth * !((!extend) & (v1 > bheight-1)); if (extend) { @@ -13225,12 +14814,10 @@ ctx_fragment_image_rgba8_RGBA8_bi_scale (CtxRasterizer *rasterizer, for (; i < count; i ++) { uint32_t s0_ga = s1_ga; - uint32_t s0_rb = s1_rb; + uint32_t s0_rb = s1_rb; _ctx_coords_restrict (extend, &u, NULL, bwidth, bheight); ctx_lerp_RGBA8_split (src0[1],src1[1], dv, &s1_ga, &s1_rb); - ((uint32_t*)(&rgba[0]))[0] = - ctx_RGBA8_associate_global_alpha_u32 ( - ctx_lerp_RGBA8_merge (s0_ga, s0_rb, s1_ga, s1_rb, du), global_alpha_u8); + ((uint32_t*)(&rgba[0]))[0] = ctx_lerp_RGBA8_merge (s0_ga, s0_rb, s1_ga, s1_rb, du); rgba += 4; u++; src0 ++; @@ -13243,34 +14830,20 @@ ctx_fragment_image_rgba8_RGBA8_bi_scale (CtxRasterizer *rasterizer, int prev_u = -1000; for (; (i < count); i++) { - if (CTX_LIKELY(prev_u == u)) - { - } - else if (prev_u == u-1) - { - s0_ga = s1_ga; - s0_rb = s1_rb; - ctx_lerp_RGBA8_split (data[u+1],ndata[u+1], dv, &s1_ga, &s1_rb); - prev_u++; - } - else + if (prev_u != u) { ctx_lerp_RGBA8_split (data[u],ndata[u], dv, &s0_ga, &s0_rb); ctx_lerp_RGBA8_split (data[u+1],ndata[u+1], dv, &s1_ga, &s1_rb); prev_u = u; } - ((uint32_t*)(&rgba[0]))[0] = - ctx_RGBA8_associate_global_alpha_u32 ( - ctx_lerp_RGBA8_merge (s0_ga, s0_rb, s1_ga, s1_rb, (xi>>8)), global_alpha_u8); - xi += xi_delta; + ((uint32_t*)(&rgba[0]))[0] = ctx_lerp_RGBA8_merge (s0_ga, s0_rb, s1_ga, s1_rb, (xi>>8)); rgba += 4; - u = xi >> 16; + u = (xi+=xi_delta) >> 16; _ctx_coords_restrict (extend, &u, NULL, bwidth, bheight); } } - } - else + else // no extend { if (xi_delta == 65536) { @@ -13287,62 +14860,50 @@ ctx_fragment_image_rgba8_RGBA8_bi_scale (CtxRasterizer *rasterizer, uint32_t s0_ga = s1_ga; uint32_t s0_rb = s1_rb; ctx_lerp_RGBA8_split (src0[1],src1[1], dv, &s1_ga, &s1_rb); - ((uint32_t*)(&rgba[0]))[0] = - ctx_RGBA8_associate_global_alpha_u32 ( - ctx_lerp_RGBA8_merge (s0_ga, s0_rb, s1_ga, s1_rb, du), global_alpha_u8); + ((uint32_t*)(&rgba[0]))[0] = ctx_lerp_RGBA8_merge (s0_ga, s0_rb, s1_ga, s1_rb, du); rgba += 4; u++; src0 ++; src1 ++; } } - else + else { uint32_t s0_ga = 0, s1_ga = 0, s0_rb = 0, s1_rb = 0; int prev_u = -1000; for (; (i < count); i++) { - if (CTX_LIKELY(prev_u == u)) - { - } - else if (prev_u == u-1) - { - s0_ga = s1_ga; - s0_rb = s1_rb; - ctx_lerp_RGBA8_split (data[u+1],ndata[u+1], dv, &s1_ga, &s1_rb); - prev_u++; - } - else + if (prev_u != u) { ctx_lerp_RGBA8_split (data[u],ndata[u], dv, &s0_ga, &s0_rb); ctx_lerp_RGBA8_split (data[u+1],ndata[u+1], dv, &s1_ga, &s1_rb); prev_u = u; } - ((uint32_t*)(&rgba[0]))[0] = - ctx_RGBA8_associate_global_alpha_u32 ( - ctx_lerp_RGBA8_merge (s0_ga, s0_rb, s1_ga, s1_rb, (xi>>8)), global_alpha_u8); - xi += xi_delta; + ((uint32_t*)(&rgba[0]))[0] = ctx_lerp_RGBA8_merge (s0_ga, s0_rb, s1_ga, s1_rb, (xi>>8)); rgba += 4; - u = xi >> 16; + u = (xi+=xi_delta) >> 16; } } } } - static inline void -ctx_fragment_image_rgba8_RGBA8_bi_affine (CtxRasterizer *rasterizer, +ctx_fragment_image_rgba8_RGBA8_bi_affine_with_alpha (CtxRasterizer *rasterizer, float x, float y, float z, void *out, int scount, float dx, float dy, float dz) { + uint8_t global_alpha_u8 = rasterizer->state->gstate.global_alpha_u8; x-=0.5f; y-=0.5f; uint32_t count = scount; - uint8_t global_alpha_u8 = rasterizer->state->gstate.global_alpha_u8; uint8_t *rgba = (uint8_t *) out; CtxSource *g = &rasterizer->state->gstate.source_fill; +#if CTX_ENABLE_CM CtxBuffer *buffer = g->texture.buffer->color_managed?g->texture.buffer->color_managed:g->texture.buffer; +#else + CtxBuffer *buffer = g->texture.buffer; +#endif CtxExtend extend = rasterizer->state->gstate.extend; const int bwidth = buffer->width; const int bheight = buffer->height; @@ -13361,10 +14922,10 @@ ctx_fragment_image_rgba8_RGBA8_bi_affine (CtxRasterizer *rasterizer, uint32_t *edst = ((uint32_t*)out)+(count-1); for (; i < count; ) { - if ((u1>>16) <0 || - (v1>>16) <0 || - (u1>>16) >= (bwidth) - 1 || - (v1>>16) >= (bheight) - 1) + if (((u1>>16) <0) | + ((v1>>16) <0) | + ((u1>>16) >= (bwidth) - 1) | + ((v1>>16) >= (bheight) - 1)) { *edst-- = 0; count --; @@ -13378,7 +14939,7 @@ ctx_fragment_image_rgba8_RGBA8_bi_affine (CtxRasterizer *rasterizer, { int u = xi >> 16; int v = yi >> 16; - if ( u <= 0 || v <= 0 || u+1 >= bwidth-1 || v+1 >= bheight-1) + if ((u <= 0) | (v <= 0) | (u+1 >= bwidth-1) | (v+1 >= bheight-1)) { *((uint32_t*)(rgba))= 0; } @@ -13401,7 +14962,8 @@ ctx_fragment_image_rgba8_RGBA8_bi_affine (CtxRasterizer *rasterizer, int u = du >> 8; int dv = yi >> 8; int v = dv >> 8; - if (CTX_UNLIKELY(u < 0 || v < 0 || u+1 >= bwidth || v+1 >=bheight)) // default to next sample down and to right +#if 0 + if (CTX_UNLIKELY((u < 0) | (v < 0) | (u+1 >= bwidth) | (v+1 >=bheight))) // default to next sample down and to right { int u1 = u + 1; int v1 = v + 1; @@ -13415,15 +14977,121 @@ ctx_fragment_image_rgba8_RGBA8_bi_affine (CtxRasterizer *rasterizer, src11 = data + bwidth * v1 + u1; } else +#endif { src00 = data + bwidth * v + u; src01 = src00 + 1; src10 = src00 + bwidth; src11 = src01 + bwidth; } - ((uint32_t*)(&rgba[0]))[0] = - ctx_RGBA8_associate_global_alpha_u32 ( - ctx_bi_RGBA8 (*src00,*src01,*src10,*src11, du,dv), global_alpha_u8); + ((uint32_t*)(&rgba[0]))[0] = ctx_RGBA8_mul_alpha_u32 ( ctx_bi_RGBA8_alpha (*src00,*src01,*src10,*src11, du,dv), global_alpha_u8); + xi += xi_delta; + yi += yi_delta; + rgba += 4; + + i++; + } +} + +static inline void +ctx_fragment_image_rgba8_RGBA8_bi_affine (CtxRasterizer *rasterizer, + float x, float y, float z, + void *out, int scount, + float dx, float dy, float dz) +{ + x-=0.5f; + y-=0.5f; + uint32_t count = scount; + uint8_t *rgba = (uint8_t *) out; + CtxSource *g = &rasterizer->state->gstate.source_fill; +#if CTX_ENABLE_CM + CtxBuffer *buffer = g->texture.buffer->color_managed?g->texture.buffer->color_managed:g->texture.buffer; +#else + CtxBuffer *buffer = g->texture.buffer; +#endif + CtxExtend extend = rasterizer->state->gstate.extend; + const int bwidth = buffer->width; + const int bheight = buffer->height; + unsigned int i = 0; + uint32_t *data = ((uint32_t*)buffer->data); + + int yi_delta = (int)(dy * 65536); + int xi_delta = (int)(dx * 65536); + int32_t yi = (int)(y * 65536); + int32_t xi = (int)(x * 65536); + + if (extend == CTX_EXTEND_NONE) + { + int32_t u1 = xi + xi_delta* (count-1); + int32_t v1 = yi + yi_delta* (count-1); + uint32_t *edst = ((uint32_t*)out)+(count-1); + for (; i < count; ) + { + if (((u1>>16) <0) | + ((v1>>16) <0) | + ((u1>>16) >= (bwidth) - 1) | + ((v1>>16) >= (bheight) - 1)) + { + *edst-- = 0; + count --; + u1 -= xi_delta; + v1 -= yi_delta; + } + else break; + } + + for (i= 0; i < count; i ++) + { + int u = xi >> 16; + int v = yi >> 16; + if ((u <= 0) | (v <= 0) | (u+1 >= bwidth-1) | (v+1 >= bheight-1)) + { + *((uint32_t*)(rgba))= 0; + } + else + break; + xi += xi_delta; + yi += yi_delta; + rgba += 4; + } + } + + uint32_t *src00=data; + uint32_t *src01=data; + uint32_t *src10=data; + uint32_t *src11=data; + + while (i < count) + { + int du = xi >> 8; + int u = du >> 8; + int dv = yi >> 8; + int v = dv >> 8; + + + //if (((u < 0) | (v < 0) | (u+1 >= bwidth) | (v+1 >=bheight))) // default to next sample down and to right +#if 0 + if(0){ + int u1 = u + 1; + int v1 = v + 1; + + _ctx_coords_restrict (extend, &u, &v, bwidth, bheight); + _ctx_coords_restrict (extend, &u1, &v1, bwidth, bheight); + + src00 = data + bwidth * v + u; + src01 = data + bwidth * v + u1; + src10 = data + bwidth * v1 + u; + src11 = data + bwidth * v1 + u1; + } + else +#endif + { + src00 = data + bwidth * v + u; + src01 = src00 + 1; + src10 = src00 + bwidth; + src11 = src01 + bwidth; + } + ((uint32_t*)(&rgba[0]))[0] = ctx_bi_RGBA8_alpha (*src00,*src01,*src10,*src11, du,dv); xi += xi_delta; yi += yi_delta; rgba += 4; @@ -13445,7 +15113,11 @@ ctx_fragment_image_rgba8_RGBA8_bi_generic (CtxRasterizer *rasterizer, uint8_t global_alpha_u8 = rasterizer->state->gstate.global_alpha_u8; uint8_t *rgba = (uint8_t *) out; CtxSource *g = &rasterizer->state->gstate.source_fill; +#if CTX_ENABLE_CM CtxBuffer *buffer = g->texture.buffer->color_managed?g->texture.buffer->color_managed:g->texture.buffer; +#else + CtxBuffer *buffer = g->texture.buffer; +#endif CtxExtend extend = rasterizer->state->gstate.extend; const int bwidth = buffer->width; const int bheight = buffer->height; @@ -13485,7 +15157,7 @@ ctx_fragment_image_rgba8_RGBA8_bi_generic (CtxRasterizer *rasterizer, float z_recip = (zi!=0) * (1.0f/zi); int u = (int)(xi * z_recip); int v = (int)(yi * z_recip); - if ( u <= 0 || v <= 0 || u+1 >= bwidth-1 || v+1 >= bheight-1) + if ((u <= 0) | (v <= 0) | (u+1 >= bwidth-1) | (v+1 >= bheight-1)) { *((uint32_t*)(rgba))= 0; } @@ -13503,6 +15175,43 @@ ctx_fragment_image_rgba8_RGBA8_bi_generic (CtxRasterizer *rasterizer, uint32_t *src10=data; uint32_t *src11=data; + if (global_alpha_u8==255) + while (i < count) + { + float zr = (zi!=0)*(1.0f/zi) * 256; + int du = (int)(xi * zr); + int u = du >> 8; + int dv = (int)(yi * zr); + int v = dv >> 8; + if (CTX_UNLIKELY((u < 0) | (v < 0) | (u+1 >= bwidth) | (v+1 >=bheight))) // default to next sample down and to right + { + int u1 = u + 1; + int v1 = v + 1; + + _ctx_coords_restrict (extend, &u, &v, bwidth, bheight); + _ctx_coords_restrict (extend, &u1, &v1, bwidth, bheight); + + src00 = data + bwidth * v + u; + src01 = data + bwidth * v + u1; + src10 = data + bwidth * v1 + u; + src11 = data + bwidth * v1 + u1; + } + else + { + src00 = data + bwidth * v + u; + src01 = src00 + 1; + src10 = src00 + bwidth; + src11 = src01 + bwidth; + } + ((uint32_t*)(&rgba[0]))[0] = ctx_bi_RGBA8_alpha (*src00,*src01,*src10,*src11, du,dv); + xi += xi_delta; + yi += yi_delta; + zi += zi_delta; + rgba += 4; + + i++; + } + else while (i < count) { float zr = (zi!=0)*(1.0f/zi) * 256; @@ -13510,7 +15219,7 @@ ctx_fragment_image_rgba8_RGBA8_bi_generic (CtxRasterizer *rasterizer, int u = du >> 8; int dv = (int)(yi * zr); int v = dv >> 8; - if (CTX_UNLIKELY(u < 0 || v < 0 || u+1 >= bwidth || v+1 >=bheight)) // default to next sample down and to right + if (CTX_UNLIKELY((u < 0) | (v < 0) | (u+1 >= bwidth) | (v+1 >=bheight))) // default to next sample down and to right { int u1 = u + 1; int v1 = v + 1; @@ -13531,8 +15240,8 @@ ctx_fragment_image_rgba8_RGBA8_bi_generic (CtxRasterizer *rasterizer, src11 = src01 + bwidth; } ((uint32_t*)(&rgba[0]))[0] = - ctx_RGBA8_associate_global_alpha_u32 ( - ctx_bi_RGBA8 (*src00,*src01,*src10,*src11, du,dv), global_alpha_u8); + ctx_RGBA8_mul_alpha_u32 ( + ctx_bi_RGBA8_alpha (*src00,*src01,*src10,*src11, du,dv), global_alpha_u8); xi += xi_delta; yi += yi_delta; zi += zi_delta; @@ -13549,11 +15258,11 @@ ctx_fragment_image_rgba8_RGBA8_bi (CtxRasterizer *rasterizer, void *out, int icount, float dx, float dy, float dz) { unsigned int count = icount; - if (dy == 0.0f && dx > 0.0f && z==1.0f && dz==0.0f) + if ((dy == 0.0f) & (dx > 0.0f) & (z==1.0f) & (dz==0.0f)) { ctx_fragment_image_rgba8_RGBA8_bi_scale (rasterizer, x, y, z, out, count, dx, dy, dz); } - else if (z == 1.0f && dz == 0.0f) + else if ((z == 1.0f) & (dz == 0.0f)) ctx_fragment_image_rgba8_RGBA8_bi_affine (rasterizer, x, y, z, out, count, dx, dy, dz); else { @@ -13659,8 +15368,10 @@ ctx_fragment_image_yuv420_RGBA8_nearest (CtxRasterizer *rasterizer, uint8_t *rgba = (uint8_t *) out; CtxSource *g = &rasterizer->state->gstate.source_fill; CtxBuffer *buffer = g->texture.buffer; +#if CTX_ENABLE_CM if (buffer->color_managed) buffer = buffer->color_managed; +#endif uint8_t *src = (uint8_t *) buffer->data; int bwidth = buffer->width; int bheight = buffer->height; @@ -13687,7 +15398,7 @@ ctx_fragment_image_yuv420_RGBA8_nearest (CtxRasterizer *rasterizer, uint32_t *edst = ((uint32_t*)out)+count - 1; for (; i < count; ) { - if (u1 <0 || v1 < 0 || u1 >= bwidth || v1 >= bheight) + if ((u1 <0) | (v1 < 0) | (u1 >= bwidth) | (v1 >= bheight)) { *edst-- = 0; count --; @@ -13701,7 +15412,7 @@ ctx_fragment_image_yuv420_RGBA8_nearest (CtxRasterizer *rasterizer, { int u = (int)x; int v = (int)y; - if ((u < 0 || v < 0 || u >= bwidth || v >= bheight)) + if ((u < 0) | (v < 0) | (u >= bwidth) | (v >= bheight)) { *((uint32_t*)(rgba))= 0; } @@ -13738,7 +15449,7 @@ ctx_fragment_image_yuv420_RGBA8_nearest (CtxRasterizer *rasterizer, uint32_t y = v * bwidth; uint32_t uv = (v / 2) * bwidth_div_2; - if (v >= 0 && v < bheight) + if ((v >= 0) & (v < bheight)) { #if CTX_DITHER if (bits < 24) @@ -13838,6 +15549,8 @@ CTX_DECLARE_SWAP_RED_GREEN_FRAGMENT(ctx_fragment_image_rgba8_RGBA8_nearest_gener CTX_DECLARE_SWAP_RED_GREEN_FRAGMENT(ctx_fragment_image_rgba8_RGBA8_bi_scale) CTX_DECLARE_SWAP_RED_GREEN_FRAGMENT(ctx_fragment_image_rgba8_RGBA8_bi_affine) CTX_DECLARE_SWAP_RED_GREEN_FRAGMENT(ctx_fragment_image_rgba8_RGBA8_bi_generic) +CTX_DECLARE_SWAP_RED_GREEN_FRAGMENT(ctx_fragment_image_rgba8_RGBA8_bi_scale_with_alpha) +CTX_DECLARE_SWAP_RED_GREEN_FRAGMENT(ctx_fragment_image_rgba8_RGBA8_bi_affine_with_alpha) static inline void ctx_fragment_image_rgba8_RGBA8 (CtxRasterizer *rasterizer, @@ -13855,7 +15568,7 @@ ctx_fragment_image_rgba8_RGBA8 (CtxRasterizer *rasterizer, ctx_fragment_image_rgba8_RGBA8_box (rasterizer, x, y, z, out, count, dx, dy, dz); } #if CTX_ALWAYS_USE_NEAREST_FOR_SCALE1 - else if (factor > 0.99f && factor < 1.01f) + else if ((factor > 0.99f) & (factor < 1.01f)) { // XXX: also verify translate == 0 for this fast path to be valid if (rasterizer->swap_red_green) @@ -13900,9 +15613,9 @@ ctx_fragment_image_gray1_RGBA8 (CtxRasterizer *rasterizer, float x, float y, flo { int u = (int)x; int v = (int)y; - if ( u < 0 || v < 0 || - u >= buffer->width || - v >= buffer->height) + if ( (u < 0) | (v < 0) | + (u >= buffer->width) | + (v >= buffer->height)) { rgba[0] = rgba[1] = rgba[2] = rgba[3] = 0; } @@ -13941,63 +15654,177 @@ ctx_fragment_radial_gradient_RGBA8 (CtxRasterizer *rasterizer, float x, float y, int dither_green = rasterizer->format->dither_green; int ox = (int)x; #endif + + float rg_x0 = g->radial_gradient.x0; + float rg_y0 = g->radial_gradient.y0; + float rg_r0 = g->radial_gradient.r0; + float rg_rdelta = g->radial_gradient.rdelta; + + x = rg_x0 - x; + y = rg_y0 - y; + + uint8_t global_alpha_u8 = rasterizer->state->gstate.global_alpha_u8; + if (global_alpha_u8 != 255) 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); + float v = (ctx_hypotf_fast (x, y) - rg_r0) * (rg_rdelta); #if CTX_GRADIENT_CACHE uint32_t *rgbap = (uint32_t*)&rasterizer->gradient_cache_u8[ctx_grad_index(rasterizer, 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, ox+i, scan, dither_red_blue, dither_green); #endif + *((uint32_t*)rgba) = + ctx_RGBA8_mul_alpha_u32(*((uint32_t*)rgba), global_alpha_u8); rgba += 4; - x += dx; - y += dy; + x -= dx; + y -= dy; + } + else + if (dy == 0.0f) + { + float sq_y = y * y; + for (int i = 0; i < count; i ++) + { + float v = (ctx_sqrtf_fast (x*x+sq_y) - rg_r0) * (rg_rdelta); +#if CTX_GRADIENT_CACHE + uint32_t *rgbap = (uint32_t*)&rasterizer->gradient_cache_u8[ctx_grad_index(rasterizer, 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, ox+i, scan, dither_red_blue, dither_green); +#endif + rgba += 4; + x -= dx; + } + } + else + for (int i = 0; i < count; i ++) + { + float v = (ctx_hypotf_fast (x, y) - rg_r0) * (rg_rdelta); +#if CTX_GRADIENT_CACHE + uint32_t *rgbap = (uint32_t*)&rasterizer->gradient_cache_u8[ctx_grad_index(rasterizer, 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, ox+i, scan, dither_red_blue, dither_green); +#endif + rgba += 4; + x -= dx; + y -= dy; } } static void -ctx_fragment_linear_gradient_RGBA8 (CtxRasterizer *rasterizer, float x, float y, float z, void *out, int count, float dx, float dy, float dz) +ctx_fragment_conic_gradient_RGBA8 (CtxRasterizer *rasterizer, float x, float y, float z, void *out, int count, float dx, float dy, float dz) { -#if 0 uint8_t *rgba = (uint8_t *) out; +#if CTX_DITHER + int scan = rasterizer->scanline / CTX_FULL_AA; + int dither_red_blue = rasterizer->format->dither_red_blue; + int dither_green = rasterizer->format->dither_green; + int ox = (int)x; +#endif CtxSource *g = &rasterizer->state->gstate.source_fill; - for (int i = 0; i < count; i ++) + float cx = g->conic_gradient.x; + float cy = g->conic_gradient.y; + float offset = g->conic_gradient.start_angle; + float cycles = g->conic_gradient.cycles; + if (cycles < 0.01) cycles = 1.0f; + + float scale = cycles/(M_PI * 2); +#if CTX_GRADIENT_CACHE + float fscale = (rasterizer->gradient_cache_elements-1) * 256; +#endif + + x-=cx; + y-=cy; + + offset += M_PI; + + uint8_t global_alpha_u8 = rasterizer->state->gstate.global_alpha_u8; + if (global_alpha_u8 != 255) + 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; + int vv = ctx_fmod1f((ctx_atan2f (x,y) + offset) * scale) * fscale; + *((uint32_t*)rgba) = *((uint32_t*)(&rasterizer->gradient_cache_u8[ctx_grad_index_i (rasterizer, vv)][0])); #else - _ctx_fragment_gradient_1d_RGBA8 (rasterizer, v, 1.0, rgba); + float vv = ctx_fmod1f((ctx_atan2f (x,y) + offset) * scale); + _ctx_fragment_gradient_1d_RGBA8 (rasterizer, vv, 1.0, rgba); #endif #if CTX_DITHER - ctx_dither_rgba_u8 (rgba, x+i, y, rasterizer->format->dither_red_blue, - rasterizer->format->dither_green); + ctx_dither_rgba_u8 (rgba, ox+i, scan, dither_red_blue, dither_green); #endif - rgba += 4; + *((uint32_t*)rgba) = + ctx_RGBA8_mul_alpha_u32(*((uint32_t*)rgba), global_alpha_u8); + rgba+= 4; x += dx; y += dy; } + else + { + if ((dy == 0.0f) & (y != 0.0f)) + { + float y_recip = 1.0f/y; + for (int i = 0; i < count ; i++) + { +#if CTX_GRADIENT_CACHE + int vv = ctx_fmod1f((ctx_atan2f_rest (x,y_recip) + offset) * scale) * fscale; + *((uint32_t*)rgba) = *((uint32_t*)(&rasterizer->gradient_cache_u8[ctx_grad_index_i (rasterizer, vv)][0])); +#else + float vv = ctx_fmod1f((ctx_atan2f_rest (x,y_recip) + offset) * scale); + _ctx_fragment_gradient_1d_RGBA8 (rasterizer, vv, 1.0f, rgba); +#endif +#if CTX_DITHER + ctx_dither_rgba_u8 (rgba, ox+i, scan, dither_red_blue, dither_green); +#endif + rgba+= 4; + x += dx; + } + } + else + for (int i = 0; i < count ; i++) + { +#if CTX_GRADIENT_CACHE + int vv = ctx_fmod1f((ctx_atan2f (x,y) + offset) * scale) * fscale; + *((uint32_t*)rgba) = *((uint32_t*)(&rasterizer->gradient_cache_u8[ctx_grad_index_i (rasterizer, vv)][0])); #else + float vv = ctx_fmod1f((ctx_atan2f (x,y) + offset) * scale); + _ctx_fragment_gradient_1d_RGBA8 (rasterizer, vv, 1.0f, rgba); +#endif +#if CTX_DITHER + ctx_dither_rgba_u8 (rgba, ox+i, scan, dither_red_blue, dither_green); +#endif + rgba+= 4; + x += dx; + y += dy; + } + + } +} + +static void +ctx_fragment_linear_gradient_RGBA8 (CtxRasterizer *rasterizer, float x, float y, float z, void *out, int count, float dx, float dy, float dz) +{ 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; + float linear_gradient_dx = g->linear_gradient.dx_scaled; + float linear_gradient_dy = g->linear_gradient.dy_scaled; + float linear_gradient_start = g->linear_gradient.start_scaled; #if CTX_DITHER int dither_red_blue = rasterizer->format->dither_red_blue; @@ -14019,21 +15846,37 @@ ctx_fragment_linear_gradient_RGBA8 (CtxRasterizer *rasterizer, float x, float y, float ud_plus_vd = (ud + vd); #endif + uint8_t global_alpha_u8 = rasterizer->state->gstate.global_alpha_u8; + if (global_alpha_u8 != 255) for (int i = 0; i < count ; i++) { #if CTX_GRADIENT_CACHE - uint32_t*rgbap = ((uint32_t*)(&rasterizer->gradient_cache_u8[ctx_grad_index_i (rasterizer, vv)][0])); - *((uint32_t*)rgba) = *rgbap; + *((uint32_t*)rgba) = *((uint32_t*)(&rasterizer->gradient_cache_u8[ctx_grad_index_i (rasterizer, vv)][0])); #else _ctx_fragment_gradient_1d_RGBA8 (rasterizer, vv, 1.0, rgba); #endif #if CTX_DITHER ctx_dither_rgba_u8 (rgba, ox+i, scan, dither_red_blue, dither_green); #endif + *((uint32_t*)rgba) = + ctx_RGBA8_mul_alpha_u32(*((uint32_t*)rgba), global_alpha_u8); rgba+= 4; vv += ud_plus_vd; } + else + for (int i = 0; i < count ; i++) + { +#if CTX_GRADIENT_CACHE + *((uint32_t*)rgba) = *((uint32_t*)(&rasterizer->gradient_cache_u8[ctx_grad_index_i (rasterizer, vv)][0])); +#else + _ctx_fragment_gradient_1d_RGBA8 (rasterizer, vv, 1.0, rgba); +#endif +#if CTX_DITHER + ctx_dither_rgba_u8 (rgba, ox+i, scan, dither_red_blue, dither_green); #endif + rgba+= 4; + vv += ud_plus_vd; + } } #endif @@ -14058,19 +15901,27 @@ ctx_fragment_color_RGBA8 (CtxRasterizer *rasterizer, float x, float y, float z, #if CTX_GRADIENTS static void -ctx_fragment_linear_gradient_RGBAF (CtxRasterizer *rasterizer, float x, float y, float z, void *out, int count, float dx, float dy, float dz) +ctx_fragment_linear_gradient_RGBAF (CtxRasterizer *rasterizer, float u0, float v0, float z, void *out, int count, float ud, float vd, float dz) { float *rgba = (float *) out; CtxSource *g = &rasterizer->state->gstate.source_fill; - for (int i = 0; i < count; i++) + float linear_gradient_dx = g->linear_gradient.dx_scaled; + float linear_gradient_dy = g->linear_gradient.dy_scaled; + float linear_gradient_start = g->linear_gradient.start_scaled; + + u0 *= linear_gradient_dx; + v0 *= linear_gradient_dy; + ud *= linear_gradient_dx; + vd *= linear_gradient_dy; + + float vv = ((u0 + v0) - linear_gradient_start); + float ud_plus_vd = (ud + vd); + + 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; + ctx_fragment_gradient_1d_RGBAF (rasterizer, vv, 1.0f, rgba); + rgba+= 4; + vv += ud_plus_vd; } } @@ -14079,16 +15930,49 @@ ctx_fragment_radial_gradient_RGBAF (CtxRasterizer *rasterizer, float x, float y, { float *rgba = (float *) out; CtxSource *g = &rasterizer->state->gstate.source_fill; + float rg_x0 = g->radial_gradient.x0; + float rg_y0 = g->radial_gradient.y0; + float rg_r0 = g->radial_gradient.r0; + float rg_rdelta = g->radial_gradient.rdelta; 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); + float v = (ctx_hypotf (rg_x0 - x, rg_y0 - y) - rg_r0) * rg_rdelta; + ctx_fragment_gradient_1d_RGBAF (rasterizer, v, 0.0f, rgba); x+=dx; y+=dy; rgba +=4; } } + +static void +ctx_fragment_conic_gradient_RGBAF (CtxRasterizer *rasterizer, float x, float y, float z, void *out, int count, float dx, float dy, float dz) +{ + float *rgba = (float *) out; + CtxSource *g = &rasterizer->state->gstate.source_fill; + float cx = g->conic_gradient.x; + float cy = g->conic_gradient.y; + float offset = g->conic_gradient.start_angle; + float cycles = g->conic_gradient.cycles; + if (cycles < 0.01) cycles = 1.0f; + + float scale = cycles/(M_PI * 2); + + x-=cx; + y-=cy; + + offset += M_PI; + + for (int i = 0; i < count ; i++) + { + float v = (ctx_atan2f (x,y) + offset) * scale; + v = ctx_fmod1f(v); + ctx_fragment_gradient_1d_RGBAF (rasterizer, v, 0.0f, rgba); + rgba+= 4; + x += dx; + y += dy; + } +} + #endif @@ -14115,7 +15999,11 @@ static void ctx_fragment_image_RGBAF (CtxRasterizer *rasterizer, float x, float float *outf = (float *) out; uint8_t rgba[4 * count]; CtxSource *g = &rasterizer->state->gstate.source_fill; +#if CTX_ENABLE_CM CtxBuffer *buffer = g->texture.buffer->color_managed?g->texture.buffer->color_managed:g->texture.buffer; +#else + CtxBuffer *buffer = g->texture.buffer; +#endif switch (buffer->format->bpp) { #if CTX_FRAGMENT_SPECIALIZE @@ -14138,6 +16026,7 @@ static CtxFragment ctx_rasterizer_get_fragment_RGBAF (CtxRasterizer *rasterizer) #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; + case CTX_SOURCE_CONIC_GRADIENT: return ctx_fragment_conic_gradient_RGBAF; #endif } return ctx_fragment_color_RGBAF; @@ -14172,9 +16061,12 @@ static CtxFragment ctx_rasterizer_get_fragment_RGBA8 (CtxRasterizer *rasterizer) { case CTX_SOURCE_TEXTURE: { - CtxBuffer *buffer = g->texture.buffer; - if (buffer) - buffer = buffer->color_managed?buffer->color_managed:buffer; +#if CTX_ENABLE_CM + CtxBuffer *buffer = g->texture.buffer->color_managed?g->texture.buffer->color_managed:g->texture.buffer; +#else + CtxBuffer *buffer = g->texture.buffer; +#endif + if (!buffer || !buffer->format) return ctx_fragment_color_RGBA8; @@ -14201,7 +16093,7 @@ static CtxFragment ctx_rasterizer_get_fragment_RGBA8 (CtxRasterizer *rasterizer) return ctx_fragment_image_rgb8_RGBA8_box; } #if CTX_ALWAYS_USE_NEAREST_FOR_SCALE1 - else if (factor > 0.99f && factor < 1.01f) + else if ((factor > 0.99f) & (factor < 1.01f)) { if (rasterizer->swap_red_green) return ctx_fragment_image_rgb8_RGBA8_nearest_swap_red_green; @@ -14239,7 +16131,7 @@ static CtxFragment ctx_rasterizer_get_fragment_RGBA8 (CtxRasterizer *rasterizer) return ctx_fragment_image_rgba8_RGBA8_box; } #if CTX_ALWAYS_USE_NEAREST_FOR_SCALE1 - else if (factor > 0.99f && factor < 1.01f && extend == CTX_EXTEND_NONE) + else if ((factor > 0.99f) & (factor < 1.01f) & (extend == CTX_EXTEND_NONE)) { if (rasterizer->swap_red_green) return ctx_fragment_image_rgba8_RGBA8_nearest_copy_swap_red_green; @@ -14254,42 +16146,50 @@ static CtxFragment ctx_rasterizer_get_fragment_RGBA8 (CtxRasterizer *rasterizer) { if (ctx_matrix_no_skew_or_rotate (transform)) { - if (ctx_fabsf (transform->m[0][0] - 1.0f) < 0.001f && - ctx_fabsf (transform->m[1][1] - 1.0f) < 0.001f && - ctx_fmod1f (transform->m[0][2]) < 0.001f && - ctx_fmod1f (transform->m[1][2]) < 0.001f) + if ((int)(ctx_fabsf (transform->m[0][0] - 1.0f) < 0.001f) & + (ctx_fabsf (transform->m[1][1] - 1.0f) < 0.001f) & + (ctx_fmod1f (transform->m[0][2]) < 0.001f) & + (ctx_fmod1f (transform->m[1][2]) < 0.001f)) { if (extend == CTX_EXTEND_NONE) return ctx_fragment_image_rgba8_RGBA8_nearest_copy_swap_red_green; else if (extend == CTX_EXTEND_REPEAT) return ctx_fragment_image_rgba8_RGBA8_nearest_copy_repeat_swap_red_green; } - return ctx_fragment_image_rgba8_RGBA8_bi_scale_swap_red_green; + return gstate->global_alpha_u8==255? + ctx_fragment_image_rgba8_RGBA8_bi_scale_swap_red_green + :ctx_fragment_image_rgba8_RGBA8_bi_scale_with_alpha_swap_red_green; } - return ctx_fragment_image_rgba8_RGBA8_bi_affine_swap_red_green; + return gstate->global_alpha_u8==255? + ctx_fragment_image_rgba8_RGBA8_bi_affine_swap_red_green + :ctx_fragment_image_rgba8_RGBA8_bi_affine_with_alpha_swap_red_green; } - return ctx_fragment_image_rgba8_RGBA8_bi_generic_swap_red_green; + return ctx_fragment_image_rgba8_RGBA8_bi_generic_swap_red_green; } if (ctx_matrix_no_perspective (transform)) { if (ctx_matrix_no_skew_or_rotate (transform)) { - if (ctx_fabsf (transform->m[0][0] - 1.0f) < 0.001f && - ctx_fabsf (transform->m[1][1] - 1.0f) < 0.001f && - ctx_fmod1f (transform->m[0][2]) < 0.001f && - ctx_fmod1f (transform->m[1][2]) < 0.001f) + if ((int)(ctx_fabsf (transform->m[0][0] - 1.0f) < 0.001f) & + (ctx_fabsf (transform->m[1][1] - 1.0f) < 0.001f) & + (ctx_fmod1f (transform->m[0][2]) < 0.001f) & + (ctx_fmod1f (transform->m[1][2]) < 0.001f)) { if (extend == CTX_EXTEND_NONE) return ctx_fragment_image_rgba8_RGBA8_nearest_copy; else if (extend == CTX_EXTEND_REPEAT) return ctx_fragment_image_rgba8_RGBA8_nearest_copy_repeat; } - return ctx_fragment_image_rgba8_RGBA8_bi_scale; + return gstate->global_alpha_u8==255? + ctx_fragment_image_rgba8_RGBA8_bi_scale: + ctx_fragment_image_rgba8_RGBA8_bi_scale_with_alpha; } - return ctx_fragment_image_rgba8_RGBA8_bi_affine; + return gstate->global_alpha_u8==255? + ctx_fragment_image_rgba8_RGBA8_bi_affine: + ctx_fragment_image_rgba8_RGBA8_bi_affine_with_alpha; } - return ctx_fragment_image_rgba8_RGBA8_bi_generic; + return ctx_fragment_image_rgba8_RGBA8_bi_generic; } } else @@ -14300,8 +16200,8 @@ static CtxFragment ctx_rasterizer_get_fragment_RGBA8 (CtxRasterizer *rasterizer) { if (ctx_matrix_no_skew_or_rotate (transform)) { - if (ctx_fabsf (transform->m[0][0] - 1.0f) < 0.001f && - ctx_fabsf (transform->m[1][1] - 1.0f) < 0.001f) + if ((int)(ctx_fabsf (transform->m[0][0] - 1.0f) < 0.001f) & + (ctx_fabsf (transform->m[1][1] - 1.0f) < 0.001f)) { return ctx_fragment_image_rgba8_RGBA8_nearest_copy_swap_red_green; if (extend == CTX_EXTEND_NONE) @@ -14319,8 +16219,8 @@ static CtxFragment ctx_rasterizer_get_fragment_RGBA8 (CtxRasterizer *rasterizer) { if (ctx_matrix_no_skew_or_rotate (transform)) { - if (ctx_fabsf (transform->m[0][0] - 1.0f) < 0.001f && - ctx_fabsf (transform->m[1][1] - 1.0f) < 0.001f) + if ((int)(ctx_fabsf (transform->m[0][0] - 1.0f) < 0.001f) & + (ctx_fabsf (transform->m[1][1] - 1.0f) < 0.001f)) { if (extend == CTX_EXTEND_NONE) return ctx_fragment_image_rgba8_RGBA8_nearest_copy; @@ -14343,6 +16243,7 @@ static CtxFragment ctx_rasterizer_get_fragment_RGBA8 (CtxRasterizer *rasterizer) case CTX_SOURCE_COLOR: return ctx_fragment_color_RGBA8; #if CTX_GRADIENTS + case CTX_SOURCE_CONIC_GRADIENT: return ctx_fragment_conic_gradient_RGBA8; case CTX_SOURCE_LINEAR_GRADIENT: return ctx_fragment_linear_gradient_RGBA8; case CTX_SOURCE_RADIAL_GRADIENT: return ctx_fragment_radial_gradient_RGBA8; #endif @@ -14502,12 +16403,12 @@ ctx_porter_duff_factors(mode, foo, bar)\ static inline void ctx_u8_source_over_normal_color (int components, - CtxRasterizer *rasterizer, + unsigned int count, uint8_t * __restrict__ dst, uint8_t * __restrict__ src, - int x0, uint8_t * __restrict__ coverage, - int count) + CtxRasterizer *rasterizer, + int x0) { uint8_t tsrc[5]; *((uint32_t*)tsrc) = *((uint32_t*)src); @@ -14533,7 +16434,7 @@ ctx_u8_source_copy_normal_color (int components, CTX_COMPOSITE_ARGUMENTS) } } -static inline void +static CTX_INLINE void ctx_RGBA8_source_over_normal_buf (CTX_COMPOSITE_ARGUMENTS, uint8_t *tsrc) { while (count--) @@ -14556,8 +16457,8 @@ ctx_RGBA8_source_over_normal_buf (CTX_COMPOSITE_ARGUMENTS, uint8_t *tsrc) } } -static inline void -ctx_RGBA8_source_over_normal_full_cov_buf (CTX_COMPOSITE_ARGUMENTS, uint8_t *tsrc) +static CTX_INLINE void +ctx_RGBA8_source_over_normal_full_cov_buf (CTX_COMPOSITE_ARGUMENTS, uint8_t *__restrict__ tsrc) { uint32_t *ttsrc = (uint32_t*)tsrc; uint32_t *ddst = (uint32_t*)dst; @@ -14595,8 +16496,8 @@ ctx_RGBA8_source_over_normal_fragment (CTX_COMPOSITE_ARGUMENTS) ctx_init_uv (rasterizer, x0, rasterizer->scanline/CTX_FULL_AA, &u0, &v0, &w0, &ud, &vd, &wd); uint8_t _tsrc[4 * (count)]; rasterizer->fragment (rasterizer, u0, v0, w0, &_tsrc[0], count, ud, vd, wd); - ctx_RGBA8_source_over_normal_buf (rasterizer, - dst, src, x0, coverage, count, &_tsrc[0]); + ctx_RGBA8_source_over_normal_buf (count, + dst, src, coverage, rasterizer, x0, &_tsrc[0]); } static inline void @@ -14604,17 +16505,18 @@ ctx_RGBA8_source_over_normal_full_cov_fragment (CTX_COMPOSITE_ARGUMENTS, int sca { CtxMatrix *transform = &rasterizer->state->gstate.source_fill.transform; int scan = rasterizer->scanline /CTX_FULL_AA; + CtxFragment fragment = rasterizer->fragment; if (CTX_LIKELY(ctx_matrix_no_perspective (transform))) { float u0, v0, ud, vd, w0, wd; + uint8_t _tsrc[4 * count]; ctx_init_uv (rasterizer, x0, scan, &u0, &v0, &w0, &ud, &vd, &wd); for (int y = 0; y < scanlines; y++) { - uint8_t _tsrc[4 * count]; - rasterizer->fragment (rasterizer, u0, v0, w0, &_tsrc[0], count, ud, vd, wd); - ctx_RGBA8_source_over_normal_full_cov_buf (rasterizer, - dst, src, x0, coverage, count, &_tsrc[0]); + fragment (rasterizer, u0, v0, w0, &_tsrc[0], count, ud, vd, wd); + ctx_RGBA8_source_over_normal_full_cov_buf (count, + dst, src, coverage, rasterizer, x0, &_tsrc[0]); u0 -= vd; v0 += ud; dst += rasterizer->blit_stride; @@ -14622,14 +16524,14 @@ ctx_RGBA8_source_over_normal_full_cov_fragment (CTX_COMPOSITE_ARGUMENTS, int sca } else { + uint8_t _tsrc[4 * count]; for (int y = 0; y < scanlines; y++) { - uint8_t _tsrc[4 * count]; float u0, v0, ud, vd, w0, wd; ctx_init_uv (rasterizer, x0, scan+y, &u0, &v0, &w0, &ud, &vd, &wd); - rasterizer->fragment (rasterizer, u0, v0, w0, &_tsrc[0], count, ud, vd, wd); - ctx_RGBA8_source_over_normal_full_cov_buf (rasterizer, - dst, src, x0, coverage, count, &_tsrc[0]); + fragment (rasterizer, u0, v0, w0, &_tsrc[0], count, ud, vd, wd); + ctx_RGBA8_source_over_normal_full_cov_buf (count, + dst, src, coverage, rasterizer, x0, &_tsrc[0]); dst += rasterizer->blit_stride; } } @@ -14644,8 +16546,8 @@ ctx_RGBA8_source_copy_normal_fragment (CTX_COMPOSITE_ARGUMENTS) ctx_init_uv (rasterizer, x0, rasterizer->scanline/CTX_FULL_AA, &u0, &v0, &w0, &ud, &vd, &wd); uint8_t _tsrc[4 * (count)]; rasterizer->fragment (rasterizer, u0, v0, w0, &_tsrc[0], count, ud, vd, wd); - ctx_RGBA8_source_copy_normal_buf (rasterizer, - dst, src, x0, coverage, count, &_tsrc[0]); + ctx_RGBA8_source_copy_normal_buf (count, + dst, src, coverage, rasterizer, x0, &_tsrc[0]); } @@ -14653,7 +16555,7 @@ 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); + ctx_u8_source_over_normal_color (4, count, dst, src, coverage, count, rasterizer, x0); #else uint32_t si_ga = ((uint32_t*)rasterizer->color)[1]; uint32_t si_rb = ((uint32_t*)rasterizer->color)[2]; @@ -14705,7 +16607,7 @@ ctx_RGBA8_source_copy_normal_color (CTX_COMPOSITE_ARGUMENTS) static void ctx_RGBA8_clear_normal (CTX_COMPOSITE_ARGUMENTS) { - ctx_u8_clear_normal (4, rasterizer, dst, src, x0, coverage, count); + ctx_u8_clear_normal (4, count, dst, src, coverage, rasterizer, x0); } static void @@ -14913,13 +16815,13 @@ static void ctx_u8_set_lum (int components, uint8_t *c, uint8_t lum) int n = ctx_int_get_min (components, tc); int x = ctx_int_get_max (components, tc); - if (n < 0 && l!=n) + if ((n < 0) & (l!=n)) { for (int i = 0; i < components - 1; i++) tc[i] = l + (((tc[i] - l) * l) / (l-n)); } - if (x > 255 && x!=l) + if ((x > 255) & (x!=l)) { for (int i = 0; i < components - 1; i++) tc[i] = l + (((tc[i] - l) * (255 - l)) / (x-l)); @@ -15233,15 +17135,15 @@ static void ctx_setup_RGBA8 (CtxRasterizer *rasterizer) { 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; + CtxSourceType source_type = (CtxSourceType)gstate->source_fill.type; int blend_mode = gstate->blend_mode; int compositing_mode = gstate->compositing_mode; - if (gstate->source_fill.type == CTX_SOURCE_COLOR) + if (source_type == CTX_SOURCE_COLOR) { ctx_fragment_color_RGBA8 (rasterizer, 0,0, 1,rasterizer->color, 1, 0,0,0); if (gstate->global_alpha_u8 != 255) @@ -15252,9 +17154,8 @@ ctx_setup_RGBA8 (CtxRasterizer *rasterizer) 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 si_ga_full = si_ga * 255 + 0xff00ff; + uint32_t si_rb_full = si_rb * 255 + 0xff00ff; ((uint32_t*)rasterizer->color)[1] = si_ga; ((uint32_t*)rasterizer->color)[2] = si_rb; @@ -15281,18 +17182,18 @@ ctx_setup_RGBA8 (CtxRasterizer *rasterizer) rasterizer->comp_op = ctx_RGBA8_nop; } else - switch (gstate->source_fill.type) + switch (source_type) { case CTX_SOURCE_COLOR: if (gstate->compositing_mode == CTX_COMPOSITE_SOURCE_OVER) { rasterizer->comp_op = ctx_RGBA8_source_over_normal_color; - if ( ((float*)rasterizer->color)[3] >= 0.999f) + if (rasterizer->color[3] == 255) rasterizer->comp = CTX_COV_PATH_RGBA8_COPY; } else { - rasterizer->comp_op = ctx_RGBAF_porter_duff_color_normal; + rasterizer->comp_op = ctx_RGBA8_porter_duff_color_normal; } break; #if CTX_GRADIENTS @@ -15312,7 +17213,7 @@ ctx_setup_RGBA8 (CtxRasterizer *rasterizer) } break; default: - switch (gstate->source_fill.type) + switch (source_type) { case CTX_SOURCE_COLOR: rasterizer->comp_op = ctx_RGBA8_porter_duff_color; @@ -15337,7 +17238,7 @@ ctx_setup_RGBA8 (CtxRasterizer *rasterizer) #else - if (gstate->source_fill.type == CTX_SOURCE_COLOR) + if (source_type == CTX_SOURCE_COLOR) { if (blend_mode == CTX_BLEND_NORMAL) @@ -15349,7 +17250,7 @@ ctx_setup_RGBA8 (CtxRasterizer *rasterizer) } else if (compositing_mode == CTX_COMPOSITE_SOURCE_OVER) { - if (rasterizer->color[components-1] == 255) + if (rasterizer->color[3] == 255) { rasterizer->comp_op = ctx_RGBA8_source_copy_normal_color; rasterizer->comp = CTX_COV_PATH_RGBA8_COPY; @@ -15442,7 +17343,7 @@ 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->comp_op (count, &pixels[0], rasterizer->color, coverage, rasterizer, x0); rasterizer->format->from_comp (rasterizer, x0, &pixels[0], dst, count); } @@ -15640,13 +17541,13 @@ static void ctx_float_set_lum (int components, float *c, float lum) float n = ctx_float_get_min (components, tc); float x = ctx_float_get_max (components, tc); - if (n < 0.0f && l != n) + if ((n < 0.0f) & (l != n)) { for (int i = 0; i < components - 1; i++) tc[i] = l + (((tc[i] - l) * l) / (l-n)); } - if (x > 1.0f && x != l) + 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)); @@ -16010,6 +17911,7 @@ ctx_float_porter_duff(RGBAF, 4,generic, rasterizer->fragment, rasterizer->state- #if CTX_INLINED_NORMAL #if CTX_GRADIENTS +ctx_float_porter_duff(RGBAF, 4,conic_gradient, ctx_fragment_conic_gradient_RGBAF, rasterizer->state->gstate.blend_mode) 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 @@ -16036,20 +17938,20 @@ ctx_float_porter_duff_blend(RGBAF, 4, CTX_BLEND_NORMAL, normal) static void ctx_RGBAF_copy_normal (CTX_COMPOSITE_ARGUMENTS) { - ctx_float_copy_normal (4, rasterizer, dst, src, x0, coverage, count); + ctx_float_copy_normal (4, count, dst, src, coverage, rasterizer, x0); } static void ctx_RGBAF_clear_normal (CTX_COMPOSITE_ARGUMENTS) { - ctx_float_clear_normal (4, rasterizer, dst, src, x0, coverage, count); + ctx_float_clear_normal (4, count, dst, src, coverage, rasterizer, x0); } #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); + ctx_float_source_over_normal_color (4, count, dst, rasterizer->color, coverage, rasterizer, x0); } #endif #endif @@ -16163,21 +18065,29 @@ ctx_setup_RGBAF (CtxRasterizer *rasterizer) #if CTX_GRADIENTS static void -ctx_fragment_linear_gradient_GRAYAF (CtxRasterizer *rasterizer, float x, float y, float z, void *out, int count, float dx, float dy, float dz) +ctx_fragment_linear_gradient_GRAYAF (CtxRasterizer *rasterizer, float u0, float v0, float z, void *out, int count, float ud, float vd, float dz) { float rgba[4]; CtxSource *g = &rasterizer->state->gstate.source_fill; + float linear_gradient_dx = g->linear_gradient.dx_scaled; + float linear_gradient_dy = g->linear_gradient.dy_scaled; + float linear_gradient_start = g->linear_gradient.start_scaled; + + u0 *= linear_gradient_dx; + v0 *= linear_gradient_dy; + ud *= linear_gradient_dx; + vd *= linear_gradient_dy; + + float vv = ((u0 + v0) - linear_gradient_start); + float ud_plus_vd = ud + vd; + 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); - ((float*)out)[0] = ctx_float_color_rgb_to_gray (rasterizer->state, rgba); - ((float*)out)[1] = rgba[3]; + ctx_fragment_gradient_1d_RGBAF (rasterizer, vv, 1.0f, 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; + vv += ud_plus_vd; } } @@ -16186,14 +18096,13 @@ ctx_fragment_radial_gradient_GRAYAF (CtxRasterizer *rasterizer, float x, float y { float rgba[4]; CtxSource *g = &rasterizer->state->gstate.source_fill; + float rg_x0 = g->radial_gradient.x0; + float rg_y0 = g->radial_gradient.y0; + float rg_r0 = g->radial_gradient.r0; + float rg_rdelta = g->radial_gradient.rdelta; for (int i = 0; i < count; i ++) { - 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); - } + float v = (ctx_hypotf (rg_x0 - x, rg_y0 - y) - rg_r0) * (rg_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]; @@ -16222,7 +18131,11 @@ static void ctx_fragment_image_GRAYAF (CtxRasterizer *rasterizer, float x, float uint8_t rgba[4*count]; float rgbaf[4*count]; CtxSource *g = &rasterizer->state->gstate.source_fill; - CtxBuffer *buffer = g->texture.buffer->color_managed?g->texture.buffer->color_managed:g->texture.buffer; +#if CTX_ENABLE_CM + CtxBuffer *buffer = g->texture.buffer->color_managed?g->texture.buffer->color_managed:g->texture.buffer; +#else + CtxBuffer *buffer = g->texture.buffer; +#endif switch (buffer->format->bpp) { #if CTX_FRAGMENT_SPECIALIZE @@ -16265,19 +18178,19 @@ ctx_float_porter_duff(GRAYAF, 2,generic_normal, rasterizer->fragment, CTX_BLEND_ static void ctx_GRAYAF_copy_normal (CTX_COMPOSITE_ARGUMENTS) { - ctx_float_copy_normal (2, rasterizer, dst, src, x0, coverage, count); + ctx_float_copy_normal (2, count, dst, src, coverage, rasterizer, x0); } static void ctx_GRAYAF_clear_normal (CTX_COMPOSITE_ARGUMENTS) { - ctx_float_clear_normal (2, rasterizer, dst, src, x0, coverage, count); + ctx_float_clear_normal (2, count, dst, src, coverage, rasterizer, x0); } static void ctx_GRAYAF_source_copy_normal_color (CTX_COMPOSITE_ARGUMENTS) { - ctx_float_source_copy_normal_color (2, rasterizer, dst, rasterizer->color, x0, coverage, count); + ctx_float_source_copy_normal_color (2, count, dst, rasterizer->color, coverage, rasterizer, x0); } #endif @@ -16375,7 +18288,7 @@ ctx_composite_GRAYF (CTX_COMPOSITE_ARGUMENTS) temp[i*2] = dstf[i]; temp[i*2+1] = 1.0f; } - rasterizer->comp_op (rasterizer, (uint8_t*)temp, rasterizer->color, x0, coverage, count); + rasterizer->comp_op (count, (uint8_t*)temp, rasterizer->color, coverage, rasterizer, x0); for (unsigned int i = 0; i < count; i++) { dstf[i] = temp[i*2]; @@ -16426,7 +18339,7 @@ ctx_composite_BGRA8 (CTX_COMPOSITE_ARGUMENTS) // 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); + rasterizer->comp_op (count, &pixels[0], rasterizer->color, coverage, rasterizer, x0); ctx_BGRA8_to_RGBA8 (rasterizer, x0, &pixels[0], dst, count); } @@ -16440,7 +18353,7 @@ ctx_composite_direct (CTX_COMPOSITE_ARGUMENTS) // of gradient or image // // - rasterizer->comp_op (rasterizer, dst, rasterizer->color, x0, coverage, count); + rasterizer->comp_op (count, dst, rasterizer->color, coverage, rasterizer, x0); } #if CTX_ENABLE_CMYKAF @@ -16461,6 +18374,9 @@ ctx_fragment_other_CMYKAF (CtxRasterizer *rasterizer, float x, float y, float z, ctx_fragment_color_RGBAF (rasterizer, x, y, z, rgba, count, dx, dy, dz); break; #if CTX_GRADIENTS + case CTX_SOURCE_CONIC_GRADIENT: + ctx_fragment_conic_gradient_RGBAF (rasterizer, x, y, z, rgba, count, dx, dy, dz); + break; case CTX_SOURCE_LINEAR_GRADIENT: ctx_fragment_linear_gradient_RGBAF (rasterizer, x, y, z, rgba, count, dx, dy, dz); break; @@ -16520,19 +18436,19 @@ ctx_float_porter_duff (CMYKAF, 5,generic_normal, rasterizer->fragment, static void ctx_CMYKAF_copy_normal (CTX_COMPOSITE_ARGUMENTS) { - ctx_float_copy_normal (5, rasterizer, dst, src, x0, coverage, count); + ctx_float_copy_normal (5, count, dst, src, coverage, rasterizer, x0); } static void ctx_CMYKAF_clear_normal (CTX_COMPOSITE_ARGUMENTS) { - ctx_float_clear_normal (5, rasterizer, dst, src, x0, coverage, count); + ctx_float_clear_normal (5, count, dst, src, coverage, rasterizer, x0); } static void ctx_CMYKAF_source_copy_normal_color (CTX_COMPOSITE_ARGUMENTS) { - ctx_float_source_copy_normal_color (5, rasterizer, dst, rasterizer->color, x0, coverage, count); + ctx_float_source_copy_normal_color (5, count, dst, rasterizer->color, coverage, rasterizer, x0); } #endif @@ -16672,7 +18588,7 @@ ctx_CMYKAF_to_CMYKA8 (CtxRasterizer *rasterizer, float *src, uint8_t *dst, int c for (int i = 0; i < count; i ++) { int a = ctx_float_to_u8 (src[4]); - if (a != 0 && a != 255) + if ((a != 0) & (a != 255)) { float recip = 1.0f/src[4]; for (int c = 0; c < 4; c++) @@ -16697,7 +18613,7 @@ ctx_composite_CMYKA8 (CTX_COMPOSITE_ARGUMENTS) { 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); + rasterizer->comp_op (count, (uint8_t *) &pixels[0], rasterizer->color, coverage, rasterizer, x0); ctx_CMYKAF_to_CMYKA8 (rasterizer, &pixels[0], dst, count); } @@ -16728,7 +18644,7 @@ ctx_CMYKAF_to_CMYK8 (CtxRasterizer *rasterizer, float *src, uint8_t *dst, int co float y = src[2]; float k = src[3]; float a = src[4]; - if (a != 0.0f && a != 1.0f) + if ((a != 0.0f) & (a != 1.0f)) { float recip = 1.0f/a; c *= recip; @@ -16754,7 +18670,7 @@ ctx_composite_CMYK8 (CTX_COMPOSITE_ARGUMENTS) { 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); + rasterizer->comp_op (count, (uint8_t *) &pixels[0], src, coverage, rasterizer, x0); ctx_CMYKAF_to_CMYK8 (rasterizer, &pixels[0], dst, count); } #endif @@ -16801,7 +18717,7 @@ ctx_GRAY1_to_GRAYA8 (CtxRasterizer *rasterizer, int x, const void *buf, uint8_t while (count--) { int bitno = x&7; - if (bitno == 0 && count >= 7) + if ((bitno == 0) & (count >= 7)) { if (*pixel == 0) { @@ -16858,7 +18774,7 @@ ctx_GRAY1_to_RGBA8 (CtxRasterizer *rasterizer, int x, const void *buf, uint8_t * { int bitno = x&7; - if ((bitno) == 0 && count >=7) + if ((bitno == 0) & (count >=7)) { /* special case some bit patterns when decoding */ if (*pixel == 0) @@ -16999,7 +18915,7 @@ ctx_GRAY2_to_RGBA8 (CtxRasterizer *rasterizer, int x, const void *buf, uint8_t * while (count--) { int bitno = x & 3; - if ((bitno) == 0 && count >=3) + if ((bitno == 0) & (count >=3)) { /* special case some bit patterns when decoding */ if (*pixel == 0) @@ -17265,33 +19181,39 @@ CTX_INLINE static void ctx_rgba_to_graya_u8 (CtxState *state, uint8_t *in, uint8 #if CTX_GRADIENTS static void -ctx_fragment_linear_gradient_GRAYA8 (CtxRasterizer *rasterizer, float x, float y, float z, void *out, int count, float dx, float dy, float dz) +ctx_fragment_linear_gradient_GRAYA8 (CtxRasterizer *rasterizer, float u0, float v0, float z, void *out, int count, float ud, float vd, float dz) { CtxSource *g = &rasterizer->state->gstate.source_fill; uint8_t *dst = (uint8_t*)out; + + float linear_gradient_dx = g->linear_gradient.dx_scaled; + float linear_gradient_dy = g->linear_gradient.dy_scaled; + float linear_gradient_start = g->linear_gradient.start_scaled; + + u0 *= linear_gradient_dx; + v0 *= linear_gradient_dy; + ud *= linear_gradient_dx; + vd *= linear_gradient_dy; + + float vv = ((u0 + v0) - linear_gradient_start); + float ud_plus_vd = (ud + vd); + #if CTX_DITHER int scan = rasterizer->scanline / CTX_FULL_AA; int ox = (int)x; #endif 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.0f, rgba); + ctx_fragment_gradient_1d_RGBA8 (rasterizer, vv, 1.0f, rgba); ctx_rgba_to_graya_u8 (rasterizer->state, rgba, dst); - - } #if CTX_DITHER ctx_dither_graya_u8 ((uint8_t*)dst, ox + i, scan, rasterizer->format->dither_red_blue, rasterizer->format->dither_green); #endif - dst += 2; - x += dx; - y += dy; + dst += 2; + vv += ud_plus_vd; } } @@ -17304,11 +19226,14 @@ ctx_fragment_radial_gradient_GRAYA8 (CtxRasterizer *rasterizer, float x, float y int ox = (int)x; #endif + CtxSource *g = &rasterizer->state->gstate.source_fill; + float rg_x0 = g->radial_gradient.x0; + float rg_y0 = g->radial_gradient.y0; + float rg_r0 = g->radial_gradient.r0; + float rg_rdelta = g->radial_gradient.rdelta; 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); + float v = (ctx_hypotf (rg_x0 - x, rg_y0 - y) - rg_r0) * (rg_rdelta); { uint8_t rgba[4]; ctx_fragment_gradient_1d_RGBA8 (rasterizer, v, 1.0, rgba); @@ -17342,7 +19267,11 @@ static void ctx_fragment_image_GRAYA8 (CtxRasterizer *rasterizer, float x, float { uint8_t rgba[4*count]; CtxSource *g = &rasterizer->state->gstate.source_fill; - CtxBuffer *buffer = g->texture.buffer->color_managed?g->texture.buffer->color_managed:g->texture.buffer; +#if CTX_ENABLE_CM + CtxBuffer *buffer = g->texture.buffer->color_managed?g->texture.buffer->color_managed:g->texture.buffer; +#else + CtxBuffer *buffer = g->texture.buffer; +#endif switch (buffer->format->bpp) { #if CTX_FRAGMENT_SPECIALIZE @@ -17379,20 +19308,20 @@ ctx_u8_porter_duff(GRAYA8, 2,generic_normal, rasterizer->fragment, CTX_BLEND_NOR static void ctx_GRAYA8_copy_normal (CTX_COMPOSITE_ARGUMENTS) { - ctx_u8_copy_normal (2, rasterizer, dst, src, x0, coverage, count); + ctx_u8_copy_normal (2, count, dst, src, coverage, rasterizer, x0); } static void ctx_GRAYA8_clear_normal (CTX_COMPOSITE_ARGUMENTS) { - ctx_u8_clear_normal (2, rasterizer, dst, src, x0, coverage, count); + ctx_u8_clear_normal (2, count, dst, src, coverage, rasterizer, x0); } static void ctx_GRAYA8_source_over_normal_color (CTX_COMPOSITE_ARGUMENTS) { #if 1 - ctx_u8_source_over_normal_color (2, rasterizer, dst, rasterizer->color, x0, coverage, count); + ctx_u8_source_over_normal_color (2, count, dst, rasterizer->color, coverage, rasterizer, x0); #else uint8_t tsrc[5]; *((uint32_t*)tsrc) = *((uint32_t*)src); @@ -17411,7 +19340,7 @@ ctx_GRAYA8_source_over_normal_color (CTX_COMPOSITE_ARGUMENTS) 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); + ctx_u8_source_copy_normal_color (2, count, dst, rasterizer->color, coverage, rasterizer, x0); } #endif @@ -17497,15 +19426,15 @@ ctx_setup_GRAYA8 (CtxRasterizer *rasterizer) break; } #else - if (gstate->blend_mode == CTX_BLEND_NORMAL && - gstate->source_fill.type == CTX_SOURCE_COLOR) + if ((gstate->blend_mode == CTX_BLEND_NORMAL) & + (gstate->source_fill.type == CTX_SOURCE_COLOR)) { if (gstate->compositing_mode == CTX_COMPOSITE_COPY) { rasterizer->comp = CTX_COV_PATH_GRAYA8_COPY; } - else if (gstate->compositing_mode == CTX_COMPOSITE_SOURCE_OVER && - rasterizer->color[components-1] == 255) + else if ((gstate->compositing_mode == CTX_COMPOSITE_SOURCE_OVER) & + (rasterizer->color[components-1] == 255)) { rasterizer->comp = CTX_COV_PATH_GRAYA8_COPY; } @@ -17614,7 +19543,7 @@ ctx_RGB332_to_RGBA8 (CtxRasterizer *rasterizer, int x, const void *buf, uint8_t { ctx_332_unpack (*pixel, &rgba[0], &rgba[1], &rgba[2]); #if CTX_RGB332_ALPHA - if (rgba[0]==255 && rgba[2] == 255 && rgba[1]==0) + if ((rgba[0]==255) & (rgba[2] == 255) & (rgba[1]==0)) { rgba[3] = 0; } else #endif @@ -17668,7 +19597,7 @@ ctx_composite_RGB332 (CTX_COMPOSITE_ARGUMENTS) #endif uint8_t pixels[count * 4]; ctx_RGB332_to_RGBA8 (rasterizer, x0, dst, &pixels[0], count); - rasterizer->comp_op (rasterizer, &pixels[0], rasterizer->color, x0, coverage, count); + rasterizer->comp_op (count, &pixels[0], rasterizer->color, coverage, rasterizer, x0); ctx_RGBA8_to_RGB332 (rasterizer, x0, &pixels[0], dst, count); } @@ -17781,7 +19710,7 @@ ctx_RGB565_to_RGBA8 (CtxRasterizer *rasterizer, int x, const void *buf, uint8_t // XXX : checking the raw value for alpha before unpack will be faster ((uint32_t*)(rgba))[0] = ctx_565_unpack_32 (*pixel, 0); #if CTX_RGB565_ALPHA - if (rgba[0]==255 && rgba[2] == 255 && rgba[1]==0) + if ((rgba[0]==255) & (rgba[2] == 255) & (rgba[1]==0)) { rgba[3] = 0; } #endif pixel+=1; @@ -17857,47 +19786,16 @@ ctx_composite_RGB565 (CTX_COMPOSITE_ARGUMENTS) 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); + rasterizer->comp_op (count, &pixels[0], rasterizer->color, coverage, rasterizer, x0); ctx_RGBA8_to_RGB565 (rasterizer, x0, &pixels[0], dst, count); } #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) -{ - 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; } -#endif - pixel+=1; - rgba +=4; - } -} - -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 -#endif - { pixel[0] = ctx_565_pack (rgba[0], rgba[1], rgba[2], 1); } - pixel+=1; - rgba +=4; - } -} +void +ctx_RGB565_BS_to_RGBA8 (CtxRasterizer *rasterizer, int x, const void *buf, uint8_t *rgba, int count); +void +ctx_RGBA8_to_RGB565_BS (CtxRasterizer *rasterizer, int x, const uint8_t *rgba, void *buf, int count); static void ctx_composite_RGB565_BS (CTX_COMPOSITE_ARGUMENTS) @@ -17947,7 +19845,7 @@ 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); + rasterizer->comp_op (count, &pixels[0], rasterizer->color, coverage, rasterizer, x0); ctx_RGBA8_to_RGB565_BS (rasterizer, x0, &pixels[0], dst, count); } #endif @@ -18037,281 +19935,16 @@ static inline void ctx_span_set_color_x4 (uint32_t *dst_pix, uint32_t *val, int #if CTX_FAST_FILL_RECT -static inline void ctx_RGBA8_image_rgba8_RGBA8_bi_scaled_fill_rect (CtxRasterizer *rasterizer, int x0, int y0, int x1, int y1, int copy) -{ - CtxExtend extend = rasterizer->state->gstate.extend; - float u0 = 0; float v0 = 0; - float ud = 0; float vd = 0; - float w0 = 1; float wd = 0; - ctx_init_uv (rasterizer, x0, rasterizer->scanline/CTX_FULL_AA,&u0, &v0, &w0, &ud, &vd, &wd); - u0-=0.5f; - v0-=0.5f; - - uint8_t *dst = ( (uint8_t *) rasterizer->buf); - int blit_stride = rasterizer->blit_stride; - dst += (y0 - rasterizer->blit_y) * blit_stride; - dst += (x0) * rasterizer->format->bpp/8; - - unsigned int width = x1-x0+1; - unsigned int height = y1-y0+1; - - CtxSource *g = &rasterizer->state->gstate.source_fill; - CtxBuffer *buffer = g->texture.buffer->color_managed?g->texture.buffer->color_managed:g->texture.buffer; - - int bwidth = buffer->width; - int bheight = buffer->height; - uint8_t tsrc[copy?1:width*4]; /* unused when not copy */ - - //uint8_t global_alpha_u8 = rasterizer->state->gstate.global_alpha_u8; - uint32_t *data = ((uint32_t*)buffer->data); - uint32_t rb_row[2][width*2]; - //uint32_t ga_row[2][width]; - - int32_t row_u = (int)(u0 * 65536); - int32_t row_v = (int)(v0 * 65536); - int ui_delta = (int)(ud * 65536); - int vi_delta = (int)(vd * 65536); - - int iter = 0; - - int loaded_v = -1; - int top = iter % 2; - - - { // preload previous row for first row - int32_t ui = row_u; - int32_t vi = row_v; - unsigned int xa=0; - for (unsigned int x = 0; x < width; x++, xa+=2) - { - int u = ui >> 16; - int v = vi >> 16; - int u1 = u + 1; - uint32_t blank = 0; - uint32_t *src0 = ␣ - uint32_t *src1 = src0; - - if (CTX_LIKELY(_ctx_coords_restrict (extend, &u, &v, bwidth, bheight))) - { - src0 = data + u + bwidth * (v); - } - if (CTX_LIKELY(_ctx_coords_restrict (extend, &u1, &v, bwidth, bheight))) - { - src1 = data + u1 + bwidth * (v); - } - - ctx_lerp_RGBA8_split (*src0, *src1, ui>>8, &rb_row[!top][xa], - &rb_row[!top][xa+1]); - ui += ui_delta; - vi += vi_delta; - } - } - - { // scale/translate only - for (unsigned int y = 0; y < height; y++) - { - int top = iter % 2; - int32_t ui = row_u; - int32_t vi = row_v; - int v = (vi >> 16) + 1; - uint8_t dv = ((row_v)>> 8); - - if (v != loaded_v) - { - loaded_v = v; - unsigned int xa=0; - - for (unsigned int x = 0; x < width; x++, xa+=2) - { - int u = ui >> 16; - int u1 = u+1; - uint32_t blank = 0; - uint32_t *src0 = ␣ - uint32_t *src1 = src0; - - - if (CTX_LIKELY(_ctx_coords_restrict (extend, &u, &v, bwidth, bheight))) - { - src0 = data + u + bwidth * (v); - } - if (CTX_LIKELY(_ctx_coords_restrict (extend, &u1, &v, bwidth, bheight))) - { - src1 = data + u1 + bwidth * (v); - } - - ctx_lerp_RGBA8_split (*src0, *src1, ui>>8, &rb_row[top][xa], &rb_row[top][xa+1]); - ui += ui_delta; - } - iter++; - top = iter % 2; - } - - { - uint32_t*dst_i = copy?(uint32_t*)dst:(uint32_t*)tsrc; - int ntop = !top; - for (unsigned int xa = 0; xa < width * 2; xa+=2) - { - *dst_i ++ = - ctx_lerp_RGBA8_merge (rb_row[top][xa], rb_row[top][xa+1], - rb_row[ntop][xa], rb_row[ntop][xa+1], - dv); - } - if (!copy) - ctx_RGBA8_source_over_normal_full_cov_buf (rasterizer, - dst, NULL, x0, NULL, width, &tsrc[0]); - } - row_u -= vi_delta; - row_v += ui_delta; - dst += blit_stride; - } - } -} - -static inline void ctx_RGBA8_image_rgba8_RGBA8_bi_affine_fill_rect (CtxRasterizer *rasterizer, int x0, int y0, int x1, int y1, int copy) -{ - float u0 = 0; float v0 = 0; - float ud = 0; float vd = 0; - float w0 = 1; float wd = 0; - ctx_init_uv (rasterizer, x0, rasterizer->scanline/CTX_FULL_AA,&u0, &v0, &w0, &ud, &vd, &wd); - u0-=0.5f; - v0-=0.5f; - - uint8_t *dst = ( (uint8_t *) rasterizer->buf); - int blit_stride = rasterizer->blit_stride; - dst += (y0 - rasterizer->blit_y) * blit_stride; - dst += (x0) * rasterizer->format->bpp/8; - - unsigned int width = x1-x0+1; - unsigned int height = y1-y0+1; - - CtxSource *g = &rasterizer->state->gstate.source_fill; - CtxBuffer *buffer = g->texture.buffer->color_managed?g->texture.buffer->color_managed:g->texture.buffer; - - int bwidth = buffer->width; - int bheight = buffer->height; - uint8_t tsrc[copy?1:width*4]; /* unused when not copy */ - - //uint8_t global_alpha_u8 = rasterizer->state->gstate.global_alpha_u8; - uint32_t *data = ((uint32_t*)buffer->data); - uint32_t rb_row[2][width*2]; - //uint32_t ga_row[2][width]; - - uint32_t row_u = (int)(u0 * 65536); - uint32_t row_v = (int)(v0 * 65536); - int ui_delta = (int)(ud * 65536); - int vi_delta = (int)(vd * 65536); - - int iter = 0; - - int loaded_v = -1; - int top = iter % 2; - - - { // preload previous row for first row - uint32_t ui = row_u; - uint32_t vi = row_v; - unsigned int xa=0; - for (unsigned int x = 0; x < width; x++, xa+=2) - { - int u = ui >> 16; - int v = vi >> 16; - uint32_t blank = 0; - uint32_t *src0 = ␣ - uint32_t *src1 = src0; - - if (CTX_LIKELY (v >= 0 && v < bheight)) - { - if (CTX_LIKELY (u >= 0 && u + 1 < bwidth)) - { - src0 = data + u + bwidth * (v); - src1 = src0 + 1; - } - else - { - if (u >= 0 && u < bwidth) - src0 = data + u + bwidth * (v); - if (u + 1>= 0 && u + 1 < bwidth) - src1 = data + (u+1) + bwidth * (v); - } - } - - ctx_lerp_RGBA8_split (*src0, *src1, ui>>8, &rb_row[!top][xa], &rb_row[!top][xa+1]); - ui += ui_delta; - vi += vi_delta; - } - } - - for (unsigned int y = 0; y < height; y++) - { - int top = iter % 2; - uint32_t ui = row_u; - uint32_t vi = row_v; - - int v = (vi >> 16) + 1; - uint8_t dv = ((row_v)>> 8); - - if (v != loaded_v) - { - loaded_v = v; - unsigned int xa=0; - for (unsigned int x = 0; x < width; x++, xa+=2) - { - int u = ui >> 16; - v = (vi >> 16) + 1; - uint32_t blank = 0; - uint32_t *src0 = ␣ - uint32_t *src1 = src0; - if (CTX_LIKELY (v >= 0 && v < bheight)) - { - if (CTX_LIKELY(u >= 0 && u + 1 < bwidth)) - { - src0 = data + u + bwidth * (v); - src1 = src0 + 1; - } - else - { - if (u >= 0 && u < bwidth) - src0 = data + u + bwidth * (v); - if (u + 1>= 0 && u + 1 < bwidth) - src1 = src0 + 1; - } - } - ctx_lerp_RGBA8_split (*src0, *src1, ui>>8, &rb_row[top][xa], - &rb_row[top][xa+1]); - ui += ui_delta; - vi += vi_delta; - } - iter++; - top = iter % 2; - } - - { - uint32_t*dst_i = copy?(uint32_t*)dst:(uint32_t*)tsrc; - int ntop = !top; - for (unsigned int xa = 0; xa < width * 2; xa+=2) - { - *dst_i ++ = - ctx_lerp_RGBA8_merge (rb_row[top][xa], rb_row[top][xa+1], - rb_row[ntop][xa], rb_row[ntop][xa+1], - dv); - } - if (!copy) - ctx_RGBA8_source_over_normal_full_cov_buf (rasterizer, - dst, NULL, x0, NULL, width, &tsrc[0]); - } - row_u -= vi_delta; - row_v += ui_delta; - dst += blit_stride; - } -} +#if 1 -#if 0 -static inline void ctx_RGBA8_image_rgba8_RGBA8_nearest_fill_rect_copy (CtxRasterizer *rasterizer, int x0, int y0, int x1, int y1, int copy) +static inline void ctx_RGBA8_image_rgba8_RGBA8_nearest_fill_rect_copy (CtxRasterizer *rasterizer, int x0, int y0, int x1, int y1, const int copy) { +#if 1 float u0 = 0; float v0 = 0; float ud = 0; float vd = 0; float w0 = 1; float wd = 0; - ctx_init_uv (rasterizer, x0, rasterizer->scanline/CTX_FULL_AA,&u0, &v0, &w0, &ud, &vd, &wd); + ctx_init_uv (rasterizer, x0, y0,&u0, &v0, &w0, &ud, &vd, &wd); +#endif uint32_t *dst = ( (uint32_t *) rasterizer->buf); int blit_stride = rasterizer->blit_stride/4; @@ -18321,16 +19954,16 @@ static inline void ctx_RGBA8_image_rgba8_RGBA8_nearest_fill_rect_copy (CtxRaster unsigned int width = x1-x0+1; unsigned int height = y1-y0+1; - //CtxSource *g = &rasterizer->state->gstate.source_fill; - //CtxBuffer *buffer = g->texture.buffer->color_managed?g->texture.buffer->color_managed:g->texture.buffer; - CtxSource *g = &rasterizer->state->gstate.source_fill; - CtxBuffer *buffer = - g->texture.buffer->color_managed?g->texture.buffer->color_managed:g->texture.buffer; +#if CTX_ENABLE_CM + CtxBuffer *buffer = g->texture.buffer->color_managed?g->texture.buffer->color_managed:g->texture.buffer; +#else + CtxBuffer *buffer = g->texture.buffer; +#endif int bwidth = buffer->width; int bheight = buffer->height; - int u = x0;// + 0.5f; - int v = y0;// + 0.5f; + int u = u0;// + 0.5f; + int v = v0;// + 0.5f; uint32_t *src = ((uint32_t*)buffer->data) + bwidth * v + u; @@ -18341,56 +19974,47 @@ static inline void ctx_RGBA8_image_rgba8_RGBA8_nearest_fill_rect_copy (CtxRaster int core = ctx_mini (width, bwidth - u); + if (core<0) + return; if (copy) { - if (core>0) - { uint32_t *t_dst = dst; - for (unsigned int y = 0; y < height; y++) + src += pre; + for (unsigned int y = 0; (y < height) & (v < bheight); y++) { - if (CTX_LIKELY((v >= 0 && v < bheight))) - { - memcpy (t_dst, src + pre, core * 4); - } + memcpy (t_dst, src, core * 4); v++; src += bwidth; t_dst += blit_stride; } - } } else { - if (core>0) - { uint32_t *t_dst = dst; - for (unsigned int y = 0; y < height; y++) + for (unsigned int y = 0; (y < height) & (v < bheight); y++) { - if (CTX_LIKELY((v >= 0 && v < bheight))) - { - ctx_RGBA8_source_over_normal_full_cov_buf (rasterizer, - (uint8_t*)t_dst, NULL, x0+pre, NULL, core, (uint8_t*)src); - } + ctx_RGBA8_source_over_normal_full_cov_buf (core, + (uint8_t*)t_dst, NULL, NULL, rasterizer, x0 + pre, (uint8_t*)src); v++; src += bwidth; t_dst += blit_stride; } - } } } #endif -static void +static CTX_INLINE void ctx_composite_fill_rect_aligned (CtxRasterizer *rasterizer, int x0, int y0, int x1, int y1, - uint8_t cov) + const uint8_t cov) { - int blit_x = rasterizer->blit_x; - int blit_y = rasterizer->blit_y; - int blit_width = rasterizer->blit_width; + 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; @@ -18399,10 +20023,10 @@ ctx_composite_fill_rect_aligned (CtxRasterizer *rasterizer, y0 = ctx_maxi (y0, blit_y); y1 = ctx_mini (y1, blit_y + blit_height - 1); - int width = x1 - x0 + 1; - int height= y1 - y0 + 1; + const int width = x1 - x0 + 1; + const int height= y1 - y0 + 1; // - if (CTX_UNLIKELY (width <=0 || height <= 0)) + if (((width <=0) | (height <= 0))) return; CtxCovPath comp = rasterizer->comp; @@ -18416,16 +20040,13 @@ ctx_composite_fill_rect_aligned (CtxRasterizer *rasterizer, dst += (y0 - blit_y) * blit_stride; \ dst += (x0 * rasterizer->format->bpp)/8;}while(0); -//if (CTX_UNLIKELY(width <=0 || height <= 0)) -// return; if (cov == 255) { switch (comp) { case CTX_COV_PATH_RGBA8_COPY: { - uint32_t color; - memcpy (&color, (uint32_t*)rasterizer->color, sizeof (color)); + uint32_t color = ((uint32_t*)(rasterizer->color))[0]; INIT_ENV; if (CTX_UNLIKELY(width == 1)) { @@ -18440,14 +20061,7 @@ ctx_composite_fill_rect_aligned (CtxRasterizer *rasterizer, { for (unsigned int y = y0; y <= (unsigned)y1; y++) { -#if 0 - uint32_t *dst_pix = (uint32_t*)&dst[0]; - int count = width; - while(count--) - *dst_pix++=color; -#else ctx_span_set_colorbu ((uint32_t*)&dst[0], color, width); -#endif dst += blit_stride; } } @@ -18567,7 +20181,7 @@ ctx_composite_fill_rect_aligned (CtxRasterizer *rasterizer, uint32_t si_a = rasterizer->color[3]; INIT_ENV; - if (CTX_UNLIKELY(width == 1)) + if (width == 1) { for (unsigned int y = y0; y <= (unsigned)y1; y++) { @@ -18581,10 +20195,8 @@ ctx_composite_fill_rect_aligned (CtxRasterizer *rasterizer, for (unsigned int y = y0; y <= (unsigned)y1; y++) { uint32_t *dst_i = (uint32_t*)&dst[0]; - for (int i = 0; i < width; i++) - { + for (unsigned int i = 0; i < (unsigned)width; i++) dst_i[i] = ctx_over_RGBA8_full_2 (dst_i[i], si_ga_full, si_rb_full, si_a); - } dst += blit_stride; } } @@ -18594,21 +20206,24 @@ ctx_composite_fill_rect_aligned (CtxRasterizer *rasterizer, { CtxFragment fragment = rasterizer->fragment; CtxMatrix *transform = &rasterizer->state->gstate.source_fill.transform; - CtxExtend extend = rasterizer->state->gstate.extend; + //CtxExtend extend = rasterizer->state->gstate.extend; INIT_ENV; - if (fragment == ctx_fragment_image_rgba8_RGBA8_bi_scale) +#if CTX_FRAGMENT_SPECIALIZE + if (fragment == ctx_fragment_image_rgba8_RGBA8_nearest_copy) { - ctx_RGBA8_image_rgba8_RGBA8_bi_scaled_fill_rect (rasterizer, x0, y0, x1, -y1, 1); + ctx_RGBA8_image_rgba8_RGBA8_nearest_fill_rect_copy (rasterizer, x0, y0, x1, y1, 1); return; } - else if (fragment == ctx_fragment_image_rgba8_RGBA8_bi_affine && extend == CTX_EXTEND_NONE) +#endif +#if 0 + if (fragment == ctx_fragment_image_rgba8_RGBA8_bi_scale) { - ctx_RGBA8_image_rgba8_RGBA8_bi_affine_fill_rect (rasterizer, x0, y0, x1, + ctx_RGBA8_image_rgba8_RGBA8_bi_scaled_fill_rect (rasterizer, x0, y0, x1, y1, 1); return; } +#endif if (CTX_LIKELY(ctx_matrix_no_perspective (transform))) { @@ -18638,9 +20253,9 @@ y1, 1); } case CTX_COV_PATH_RGBA8_OVER_FRAGMENT: { +#if CTX_FRAGMENT_SPECIALIZE CtxFragment fragment = rasterizer->fragment; - CtxExtend extend = rasterizer->state->gstate.extend; -#if 0 + //CtxExtend extend = rasterizer->state->gstate.extend; if (fragment == ctx_fragment_image_rgba8_RGBA8_nearest_copy) { ctx_RGBA8_image_rgba8_RGBA8_nearest_fill_rect_copy (rasterizer, x0, y0, x1, y1, 0); @@ -18648,22 +20263,17 @@ y1, 1); } else #endif +#if 0 if (fragment == ctx_fragment_image_rgba8_RGBA8_bi_scale) { ctx_RGBA8_image_rgba8_RGBA8_bi_scaled_fill_rect (rasterizer, x0, y0, x1, y1, 0); return; } - else if (fragment == ctx_fragment_image_rgba8_RGBA8_bi_affine && extend == CTX_EXTEND_NONE) - { - ctx_RGBA8_image_rgba8_RGBA8_bi_affine_fill_rect (rasterizer, x0, y0, x1, -y1, 0); - return; - } - +#endif INIT_ENV; - ctx_RGBA8_source_over_normal_full_cov_fragment (rasterizer, - &dst[0], NULL, x0, NULL, width, y1-y0+1); + ctx_RGBA8_source_over_normal_full_cov_fragment (width, + &dst[0], NULL, NULL, rasterizer, x0, y1-y0+1); return; } break; @@ -18677,17 +20287,14 @@ y1, 0); { case CTX_COV_PATH_RGBA8_COPY: { - uint32_t color; - memcpy (&color, (uint32_t*)rasterizer->color, sizeof (color)); + uint32_t color = ((uint32_t*)(rasterizer->color))[0]; INIT_ENV; { for (unsigned int y = y0; y <= (unsigned)y1; y++) { uint32_t *dst_i = (uint32_t*)&dst[0]; for (unsigned int i = 0; i < (unsigned)width; i++) - { dst_i[i] = ctx_lerp_RGBA8 (dst_i[i], color, cov); - } dst += blit_stride; } return; @@ -18714,8 +20321,7 @@ y1, 0); } case CTX_COV_PATH_RGBA8_OVER: { - uint32_t color; - memcpy (&color, (uint32_t*)rasterizer->color, sizeof (color)); + uint32_t color = ((uint32_t*)(rasterizer->color))[0]; INIT_ENV; if (width == 1) { @@ -18732,9 +20338,7 @@ y1, 0); { uint32_t *dst_i = (uint32_t*)&dst[0]; for (unsigned int i = 0; i < (unsigned)width; i++) - { dst_i[i] = ctx_over_RGBA8 (dst_i[i], color, cov); - } dst += blit_stride; } } @@ -18755,13 +20359,12 @@ y1, 0); uint8_t coverage[width]; memset (coverage, cov, sizeof (coverage) ); uint8_t *rasterizer_src = rasterizer->color; - void (*apply_coverage)(CtxRasterizer *r, uint8_t *dst, uint8_t *src, - int x, uint8_t *coverage, unsigned int count) = + ctx_apply_coverage_fun apply_coverage = rasterizer->apply_coverage; for (unsigned int y = y0; y <= (unsigned)y1; y++) { - apply_coverage (rasterizer, &dst[0], rasterizer_src, x0, coverage, width); + apply_coverage (width, &dst[0], rasterizer_src, coverage, rasterizer, (int)x0); rasterizer->scanline += CTX_FULL_AA; dst += blit_stride; } @@ -18784,10 +20387,15 @@ CTX_SIMD_SUFFIX (ctx_composite_fill_rect) (CtxRasterizer *rasterizer, float y1, uint8_t cov) { - if((ctx_fmod1f (x0) < 0.01f || ctx_fmod1f(x0) > 0.99f) && - (ctx_fmod1f (y0) < 0.01f || ctx_fmod1f(y0) > 0.99f) && - (ctx_fmod1f (x1) < 0.01f || ctx_fmod1f(x1) > 0.99f) && - (ctx_fmod1f (y1) < 0.01f || ctx_fmod1f(y1) > 0.99f)) + float x0_fm = ctx_fmod1f (x0); + float y0_fm = ctx_fmod1f (y0); + float x1_fm = ctx_fmod1f (x1); + float y1_fm = ctx_fmod1f (y1); + + if(((int)(x0_fm < 0.01f) | (x0_fm > 0.99f)) & + ((int)(y0_fm < 0.01f) | (y0_fm > 0.99f)) & + ((int)(x1_fm < 0.01f) | (x1_fm > 0.99f)) & + ((int)(y1_fm < 0.01f) | (y1_fm > 0.99f))) { /* best-case scenario axis aligned rectangle */ ctx_composite_fill_rect_aligned (rasterizer, (int)x0, (int)y0, (int)(x1-1), (int)(y1-1), 255); @@ -18800,8 +20408,7 @@ CTX_SIMD_SUFFIX (ctx_composite_fill_rect) (CtxRasterizer *rasterizer, int blit_width = rasterizer->blit_width; int blit_height = rasterizer->blit_height; uint8_t *rasterizer_src = rasterizer->color; - void (*apply_coverage)(CtxRasterizer *r, uint8_t *dst, uint8_t *src, - int x, uint8_t *coverage, unsigned int count) = + ctx_apply_coverage_fun apply_coverage = rasterizer->apply_coverage; x0 = ctx_maxf (x0, blit_x); @@ -18809,10 +20416,10 @@ CTX_SIMD_SUFFIX (ctx_composite_fill_rect) (CtxRasterizer *rasterizer, x1 = ctx_minf (x1, blit_x + blit_width); y1 = ctx_minf (y1, blit_y + blit_height); - uint8_t left = (int)(255-ctx_fmod1f (x0) * 255); - uint8_t top = (int)(255-ctx_fmod1f (y0) * 255); - uint8_t right = (int)(ctx_fmod1f (x1) * 255); - uint8_t bottom = (int)(ctx_fmod1f (y1) * 255); + uint8_t left = (int)(255-x0_fm * 255); + uint8_t top = (int)(255-y0_fm * 255); + uint8_t right = (int)(x1_fm * 255); + uint8_t bottom = (int)(y1_fm * 255); x0 = ctx_floorf (x0); y0 = ctx_floorf (y0); @@ -18824,8 +20431,8 @@ CTX_SIMD_SUFFIX (ctx_composite_fill_rect) (CtxRasterizer *rasterizer, int has_right = (right >0); int has_left = (left >0); - if (x1 >= blit_x + blit_width) has_right = 0; - if (y1 >= blit_y + blit_height) has_bottom = 0; + has_right *= !(x1 >= blit_x + blit_width); + has_bottom *= !(y1 >= blit_y + blit_height); int width = (int)(x1 - x0); @@ -18852,7 +20459,7 @@ CTX_SIMD_SUFFIX (ctx_composite_fill_rect) (CtxRasterizer *rasterizer, if (has_right) coverage[i++]= (top * right + 255) >> 8; - apply_coverage (rasterizer, dst, rasterizer_src, (int)x0, coverage, width); + apply_coverage (width, dst, rasterizer_src, coverage, rasterizer, (int)x0); dst += blit_stride; } @@ -18880,12 +20487,11 @@ CTX_SIMD_SUFFIX (ctx_composite_fill_rect) (CtxRasterizer *rasterizer, coverage[i++] = bottom; coverage[i++]= (bottom * right + 255) >> 8; - apply_coverage (rasterizer,dst, rasterizer_src, (int)x0, coverage, width); + apply_coverage (width, dst, rasterizer_src, coverage, rasterizer, (int)x0); } } } -#endif #if CTX_FAST_STROKE_RECT void CTX_SIMD_SUFFIX(ctx_composite_stroke_rect) (CtxRasterizer *rasterizer, @@ -18917,12 +20523,12 @@ CTX_SIMD_SUFFIX(ctx_composite_stroke_rect) (CtxRasterizer *rasterizer, off_y = (CTX_FULL_AA/2)*1.0f / (CTX_FULL_AA); } - if((is_compat_odd || is_compat_even) && + if((is_compat_odd | is_compat_even) & - ((ctx_fmod1f (x0-off_x) < 0.01f || ctx_fmod1f(x0-off_x) > 0.99f) && - (ctx_fmod1f (y0-off_y) < 0.01f || ctx_fmod1f(y0-off_y) > 0.99f) && - (ctx_fmod1f (x1-off_x) < 0.01f || ctx_fmod1f(x1-off_x) > 0.99f) && - (ctx_fmod1f (y1-off_y) < 0.01f || ctx_fmod1f(y1-off_y) > 0.99f))) + (((int)(ctx_fmod1f (x0-off_x) < 0.01f) | (ctx_fmod1f(x0-off_x) > 0.99f)) & + ((int)(ctx_fmod1f (y0-off_y) < 0.01f) | (ctx_fmod1f(y0-off_y) > 0.99f)) & + ((int)(ctx_fmod1f (x1-off_x) < 0.01f) | (ctx_fmod1f(x1-off_x) > 0.99f)) & + ((int)(ctx_fmod1f (y1-off_y) < 0.01f) | (ctx_fmod1f(y1-off_y) > 0.99f)))) { @@ -18992,18 +20598,20 @@ CTX_SIMD_SUFFIX(ctx_composite_stroke_rect) (CtxRasterizer *rasterizer, } } #endif +#endif static void CTX_SIMD_SUFFIX (ctx_composite_setup) (CtxRasterizer *rasterizer) { - if (CTX_UNLIKELY (rasterizer->comp_op==NULL)) + if (rasterizer->comp_op==NULL) { #if CTX_GRADIENTS #if CTX_GRADIENT_CACHE switch (rasterizer->state->gstate.source_fill.type) { + case CTX_SOURCE_CONIC_GRADIENT: case CTX_SOURCE_LINEAR_GRADIENT: case CTX_SOURCE_RADIAL_GRADIENT: ctx_gradient_cache_prime (rasterizer); @@ -19011,8 +20619,9 @@ CTX_SIMD_SUFFIX (ctx_composite_setup) (CtxRasterizer *rasterizer) case CTX_SOURCE_TEXTURE: _ctx_matrix_multiply (&rasterizer->state->gstate.source_fill.transform, - &rasterizer->state->gstate.source_fill.set_transform, - &rasterizer->state->gstate.transform); + &rasterizer->state->gstate.transform, + &rasterizer->state->gstate.source_fill.set_transform + ); #if 0 rasterizer->state->gstate.source_fill.transform_inv = rasterizer->state->gstate.source_fill.transform; @@ -19030,13 +20639,12 @@ CTX_SIMD_SUFFIX (ctx_composite_setup) (CtxRasterizer *rasterizer) } #endif #endif + rasterizer->format->setup (rasterizer); } - rasterizer->format->setup (rasterizer); - } -CtxPixelFormatInfo CTX_SIMD_SUFFIX(ctx_pixel_formats)[]= +const CtxPixelFormatInfo CTX_SIMD_SUFFIX(ctx_pixel_formats)[]= { #if CTX_ENABLE_RGBA8 { @@ -19176,7 +20784,7 @@ CtxPixelFormatInfo CTX_SIMD_SUFFIX(ctx_pixel_formats)[]= }, #endif { - CTX_FORMAT_NONE, 0, 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL, + CTX_FORMAT_NONE, 0, 0, 0, 0, 0, (CtxPixelFormat)0, NULL, NULL, NULL, NULL, } }; @@ -19193,6 +20801,8 @@ CtxPixelFormatInfo CTX_SIMD_SUFFIX(ctx_pixel_formats)[]= #endif // CTX_IMPLEMENTATION + + #ifndef __clang__ #if CTX_RASTERIZER_O3 #pragma GCC push_options @@ -19207,50 +20817,15 @@ CtxPixelFormatInfo CTX_SIMD_SUFFIX(ctx_pixel_formats)[]= #if CTX_IMPLEMENTATION || CTX_SIMD_BUILD #if CTX_COMPOSITE -#define CTX_AA_HALFSTEP2 (CTX_FULL_AA/2) #define CTX_AA_HALFSTEP ((CTX_FULL_AA/2)+1) +#define CTX_AA_HALFSTEP2 (CTX_FULL_AA/2) -static inline 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]; -} - -static inline 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) - { - CtxSegment tmp = A[i]; - A[i] = A[j]; - A[j] = tmp; - i++; - j--; - } - } - 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); } -} +#define CTX_MAGIC_OFFSET 1 // without this we get scanline glitches static inline void ctx_rasterizer_discard_edges (CtxRasterizer *rasterizer) { - int scanline = rasterizer->scanline + 1; + int scanline = rasterizer->scanline + CTX_MAGIC_OFFSET; int next_scanline = scanline + CTX_FULL_AA; CtxSegment *segments = &((CtxSegment*)(rasterizer->edge_list.entries))[0]; int *edges = rasterizer->edges; @@ -19259,10 +20834,16 @@ static inline void ctx_rasterizer_discard_edges (CtxRasterizer *rasterizer) for (unsigned int i = 0; i < active_edges; i++) { CtxSegment *segment = segments + edges[i]; - int edge_end = segment->data.s16[3]; + int edge_end = segment->y1; if (edge_end < scanline) { - rasterizer->edges[i] = rasterizer->edges[active_edges-1]; +#if 0 + for (unsigned int j = i; j < active_edges -1; j++) + rasterizer->edges[j] = rasterizer->edges[j+1]; +#else + rasterizer->edges[i] = rasterizer->edges[active_edges-1]; +#endif + rasterizer->scan_aa[segment->aa]--; active_edges--; i--; } @@ -19273,15 +20854,14 @@ static inline void ctx_rasterizer_discard_edges (CtxRasterizer *rasterizer) unsigned int pending_edges = rasterizer->pending_edges; for (unsigned int i = 0; i < pending_edges; i++) { - int edge_end = ((CtxSegment*)(rasterizer->edge_list.entries))[rasterizer->edges[CTX_MAX_EDGES-1-i]].data.s16[3]; + int edge_end = ((CtxSegment*)(rasterizer->edge_list.entries))[rasterizer->edges[CTX_MAX_EDGES-1-i]].y1; ending_edges += (edge_end < next_scanline); } rasterizer->ending_edges = ending_edges; } -inline static void ctx_rasterizer_increment_edges (CtxRasterizer *rasterizer, int count) +CTX_INLINE static void ctx_rasterizer_increment_edges (CtxRasterizer *rasterizer, int count) { - rasterizer->scanline += count; CtxSegment *__restrict__ segments = &((CtxSegment*)(rasterizer->edge_list.entries))[0]; unsigned int active_edges = rasterizer->active_edges; unsigned int pending_edges = rasterizer->pending_edges; @@ -19298,21 +20878,18 @@ inline static void ctx_rasterizer_increment_edges (CtxRasterizer *rasterizer, in } } -/* 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_edge2_insertion_sort (CtxSegment *segments, int *entries, unsigned int count) +CTX_INLINE static void ctx_rasterizer_sort_active_edges (CtxRasterizer *rasterizer) { + CtxSegment *segments= (CtxSegment*)rasterizer->edge_list.entries; + int *entries = rasterizer->edges; + unsigned int count = rasterizer->active_edges; + for(unsigned int i=1; i= 0 && segments[temp].val - segments[entries[j]].val < 0) + while (j >= 0 && tv - segments[entries[j]].val < 0) { entries[j+1] = entries[j]; j--; @@ -19321,17 +20898,17 @@ inline static void ctx_edge2_insertion_sort (CtxSegment *segments, int *entries, } } -inline static void ctx_rasterizer_feed_edges (CtxRasterizer *rasterizer) +CTX_INLINE static void ctx_rasterizer_feed_pending_edges (CtxRasterizer *rasterizer) { CtxSegment *__restrict__ entries = (CtxSegment*)&rasterizer->edge_list.entries[0]; int *edges = rasterizer->edges; unsigned int pending_edges = rasterizer->pending_edges; - int scanline = rasterizer->scanline + 1; + int scanline = rasterizer->scanline + CTX_MAGIC_OFFSET; int active_edges = rasterizer->active_edges; for (unsigned int i = 0; i < pending_edges; i++) { - if (entries[rasterizer->edges[CTX_MAX_EDGES-1-i]].data.s16[1] <= scanline && - active_edges < CTX_MAX_EDGES-2) + if ((entries[edges[CTX_MAX_EDGES-1-i]].y0 <= scanline) & + (active_edges < CTX_MAX_EDGES-2)) { edges[active_edges] = edges[CTX_MAX_EDGES-1-i]; active_edges++; @@ -19346,203 +20923,188 @@ inline static void ctx_rasterizer_feed_edges (CtxRasterizer *rasterizer) ctx_rasterizer_discard_edges (rasterizer); } -inline static int analyze_scanline (CtxRasterizer *rasterizer) +// makes us up-to date with ready to render rasterizer->scanline +inline static int ctx_rasterizer_feed_edges_full (CtxRasterizer *rasterizer, + int with_shadow, + float blur_radius) { - if (rasterizer->active_edges + rasterizer->pending_edges == 0) - return -1; - if ((rasterizer->fast_aa == 0) | - rasterizer->horizontal_edges| - rasterizer->ending_edges| - rasterizer->pending_edges) - { - return CTX_RASTERIZER_AA; - } - - const int *edges = rasterizer->edges; - const CtxSegment *segments = &((CtxSegment*)(rasterizer->edge_list.entries))[0]; + int miny; + const int max_vaa = rasterizer->aa; + ctx_rasterizer_feed_pending_edges (rasterizer); + CtxSegment *__restrict__ entries = (CtxSegment*)&rasterizer->edge_list.entries[0]; + int *edges = rasterizer->edges; + unsigned int pending_edges = rasterizer->pending_edges; + int scanline = rasterizer->scanline + CTX_MAGIC_OFFSET; int active_edges = rasterizer->active_edges; + int horizontal_edges = 0; - int crossings = 0; -#if CTX_RASTERIZER_AA>5 - int needs_aa15 =0; -#endif -#if CTX_RASTERIZER_AA>3 - int needs_aa5 =0; -#endif - int needs_aa3 =0; - - const CtxSegment *segment0 = segments + edges[0]; - const int delta0 = segment0->delta; - const int x0 = segment0->val; - int x0_end = x0 + delta0 * CTX_AA_HALFSTEP; - int x0_start = x0 - delta0 * CTX_AA_HALFSTEP2; - -#if CTX_RASTERIZER_AA>5 - needs_aa15 += (abs(delta0) > CTX_RASTERIZER_AA_SLOPE_LIMIT15); -#endif -#if CTX_RASTERIZER_AA>3 - needs_aa5 += (abs(delta0) > CTX_RASTERIZER_AA_SLOPE_LIMIT5); -#endif - needs_aa3 += (abs(delta0) > CTX_RASTERIZER_AA_SLOPE_LIMIT3_FAST_AA); - - for (int t = 0; t < active_edges -1;t++) + if (with_shadow) + { + int shadow_active_edges = rasterizer->shadow_active_edges; + int *edges = rasterizer->shadow_edges; + int blur_scanline_start = scanline - CTX_FULL_AA * (int)blur_radius; + int next_scanline = scanline + CTX_FULL_AA * (int)blur_radius; + unsigned int edge_pos = rasterizer->shadow_edge_pos; + unsigned int edge_count = rasterizer->edge_list.count; + for (int i = 0; i < shadow_active_edges;i++) + { + if (entries[edges[i]].y1 < blur_scanline_start) { - const CtxSegment *segment1 = segments + edges[t+1]; - const int delta1 = segment1->delta; - const int x1 = segment1->val; - -#if CTX_RASTERIZER_AA>5 - needs_aa15 += (abs(delta1) > CTX_RASTERIZER_AA_SLOPE_LIMIT15); -#endif -#if CTX_RASTERIZER_AA>3 - needs_aa5 += (abs(delta1) > CTX_RASTERIZER_AA_SLOPE_LIMIT5); -#endif - needs_aa3 += (abs(delta1) > CTX_RASTERIZER_AA_SLOPE_LIMIT3_FAST_AA); - - const int x1_end = x1 + delta1 * CTX_AA_HALFSTEP; - const int x1_start = x1 - delta1 * CTX_AA_HALFSTEP2; - if (x1_end < x0_end || - x1_start < x0_end || - x1_end < x0_start - ) - { - crossings++; -#if CTX_RASTERIZER_AA==3 - if (needs_aa3) - break; -#elif CTX_RASTERIZER_AA==5 - if (needs_aa5) - break; -#elif CTX_RASTERIZER_AA==15 - if (needs_aa15) - break; -#endif - } - x0_end = x1_end; - x0_start = x1_start; + edges[i]=edges[shadow_active_edges-1]; + shadow_active_edges--; + i--; } + } - if (crossings) + while ((edge_pos < edge_count && + (miny=entries[edge_pos].y0) <= next_scanline)) { -#if CTX_RASTERIZER_AA>5 - if (needs_aa15) return 15; -#endif -#if CTX_RASTERIZER_AA>3 - if (needs_aa5) return 5; -#endif - if (needs_aa3) return 3; - return 1; + int y1 = entries[edge_pos].y1; + if ((shadow_active_edges < CTX_MAX_EDGES-2) & + (y1 >= blur_scanline_start)) + { + edges[shadow_active_edges++] = edge_pos; + } + edge_pos++; + } + rasterizer->shadow_edge_pos = edge_pos; + rasterizer->shadow_active_edges = shadow_active_edges; } - return 0; -} -inline static int ctx_rasterizer_feed_edges_full (CtxRasterizer *rasterizer) -{ - int miny; - ctx_rasterizer_feed_edges (rasterizer); - CtxSegment *__restrict__ entries = (CtxSegment*)&rasterizer->edge_list.entries[0]; - int *edges = rasterizer->edges; - unsigned int pending_edges = rasterizer->pending_edges; - int scanline = rasterizer->scanline + 1; - unsigned int edge_pos = rasterizer->edge_pos; + +#if CTX_SCANBIN + int scan = scanline / CTX_FULL_AA; + int count = rasterizer->scan_bin_count[scan]; + if (count) + for (int i = 0; i < count; i++) + { + int edge_pos = rasterizer->scan_bins[scan][i]; + miny = entries[edge_pos].y0; +#else int next_scanline = scanline + CTX_FULL_AA; + unsigned int edge_pos = rasterizer->edge_pos; unsigned int edge_count = rasterizer->edge_list.count; - int active_edges = rasterizer->active_edges; - rasterizer->horizontal_edges = 0; while ((edge_pos < edge_count && - (miny=entries[edge_pos].data.s16[1]) <= next_scanline)) - { - if (active_edges < CTX_MAX_EDGES-2 && - entries[edge_pos].data.s16[3] /* (maxy) */ >= scanline) + (miny=entries[edge_pos].y0) <= next_scanline)) + { +#endif + int y1 = entries[edge_pos].y1; + if ((active_edges < CTX_MAX_EDGES-2) & + (y1 >= scanline)) { - int dy = (entries[edge_pos].data.s16[3] - miny); + int dy = (y1 - miny); if (dy) { - int yd = scanline - miny; + int yd = (scanline + CTX_AA_HALFSTEP2) - miny; unsigned int index = edges[active_edges] = edge_pos; - int x0 = entries[index].data.s16[0]; - int x1 = entries[index].data.s16[2]; + int x0 = entries[index].x0; + int x1 = entries[index].x1; 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); + entries[index].val = x0 * CTX_RASTERIZER_EDGE_MULTIPLIER + (yd * dx_dy); + + { + dx_dy = abs(dx_dy); - if (miny > scanline && - pending_edges < CTX_MAX_PENDING-1) +#if 0 +#define CTX_RASTERIZER_AA_SLOPE_LIMIT3 ((65536*CTX_RASTERIZER_EDGE_MULTIPLIER*1.3)/CTX_SUBDIV/15/1024) +#define CTX_RASTERIZER_AA_SLOPE_LIMIT5 ((65536*CTX_RASTERIZER_EDGE_MULTIPLIER*14)/CTX_SUBDIV/15/1024) +#define CTX_RASTERIZER_AA_SLOPE_LIMIT15 ((65536*CTX_RASTERIZER_EDGE_MULTIPLIER*15)/CTX_SUBDIV/15/1024) +#else +#if 0 +#define CTX_RASTERIZER_AA_SLOPE_LIMIT3 ((65536*CTX_RASTERIZER_EDGE_MULTIPLIER)/CTX_SUBDIV/15/1024) +#define CTX_RASTERIZER_AA_SLOPE_LIMIT5 ((65536*CTX_RASTERIZER_EDGE_MULTIPLIER*3)/CTX_SUBDIV/15/1024) +#define CTX_RASTERIZER_AA_SLOPE_LIMIT15 ((65536*CTX_RASTERIZER_EDGE_MULTIPLIER*5)/CTX_SUBDIV/15/1024) +#else +#define CTX_RASTERIZER_AA_SLOPE_LIMIT3 ((65536*CTX_RASTERIZER_EDGE_MULTIPLIER*0.95)/CTX_SUBDIV/15/1024) +#define CTX_RASTERIZER_AA_SLOPE_LIMIT5 ((65536*CTX_RASTERIZER_EDGE_MULTIPLIER*6.5)/CTX_SUBDIV/15/1024) +#define CTX_RASTERIZER_AA_SLOPE_LIMIT15 ((65536*CTX_RASTERIZER_EDGE_MULTIPLIER*10.5)/CTX_SUBDIV/15/1024) +#endif +#endif + + int aa = 0; + if (max_vaa > 5) + aa = (dx_dy > CTX_RASTERIZER_AA_SLOPE_LIMIT3) + + (dx_dy > CTX_RASTERIZER_AA_SLOPE_LIMIT5) + + (dx_dy > CTX_RASTERIZER_AA_SLOPE_LIMIT15); + else + aa = (dx_dy > CTX_RASTERIZER_AA_SLOPE_LIMIT3) + + (dx_dy > CTX_RASTERIZER_AA_SLOPE_LIMIT5) * (max_vaa>3); + + rasterizer->scan_aa[aa]++; + entries[index].aa = aa; + } + + if ((miny > scanline) & + (pending_edges < CTX_MAX_PENDING-1)) { /* 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 */ - edges[CTX_MAX_EDGES-1-pending_edges] = - rasterizer->edges[active_edges]; - pending_edges++; - active_edges--; + edges[CTX_MAX_EDGES-1-pending_edges] = edges[active_edges]; + pending_edges++; + active_edges--; } active_edges++; } else - rasterizer->horizontal_edges++; + { + horizontal_edges++; + } } +#if CTX_SCANBIN +#else edge_pos++; - } - rasterizer->active_edges = active_edges; - rasterizer->edge_pos = edge_pos; - rasterizer->pending_edges = pending_edges; - return analyze_scanline (rasterizer); -} +#endif + } +#if CTX_SCANBIN==0 + rasterizer->edge_pos = edge_pos; +#endif + rasterizer->active_edges = active_edges; + rasterizer->pending_edges = pending_edges; + if (active_edges + pending_edges == 0) + return -1; -static inline void ctx_coverage_post_process (CtxRasterizer *rasterizer, unsigned int minx, unsigned int maxx, uint8_t *coverage, int *first_col, int *last_col) -{ -#if CTX_ENABLE_SHADOW_BLUR - if (CTX_UNLIKELY(rasterizer->in_shadow)) - { - float radius = rasterizer->state->gstate.shadow_blur; - unsigned int dim = 2 * radius + 1; - if (CTX_UNLIKELY (dim > CTX_MAX_GAUSSIAN_KERNEL_DIM)) - dim = CTX_MAX_GAUSSIAN_KERNEL_DIM; + if (rasterizer->ending_edges|pending_edges|horizontal_edges) { - uint16_t temp[maxx-minx+1]; - memset (temp, 0, sizeof (temp)); - for (unsigned int x = dim/2; x < maxx-minx + 1 - dim/2; x ++) - for (unsigned int u = 0; u < dim; u ++) - { - temp[x] += coverage[minx+x+u-dim/2] * rasterizer->kernel[u] * 256; - } - for (unsigned int x = 0; x < maxx-minx + 1; x ++) - coverage[minx+x] = temp[x] >> 8; + const unsigned int *scan_aa = rasterizer->scan_aa; + int aa = scan_aa[3]?15:scan_aa[2]?5:3; + return aa; + //return ctx_mini(aa, rasterizer->aa); } - } -#endif + return 0; +} +static inline void ctx_coverage_post_process (CtxRasterizer *rasterizer, const unsigned int minx, const unsigned int maxx, uint8_t *coverage, int *first_col, int *last_col) +{ #if CTX_ENABLE_CLIP - if (CTX_UNLIKELY(rasterizer->clip_buffer && !rasterizer->clip_rectangle)) + if (CTX_UNLIKELY((rasterizer->clip_buffer!=NULL) & (!rasterizer->clip_rectangle))) { int scanline = rasterizer->scanline - CTX_FULL_AA; // we do the // post process after // coverage generation icnrement /* perhaps not working right for clear? */ - int y = scanline / CTX_FULL_AA;//rasterizer->aa; + int y = scanline / CTX_FULL_AA; uint8_t *clip_line = &((uint8_t*)(rasterizer->clip_buffer->data))[rasterizer->blit_width*y]; - // XXX SIMD candidate +#if CTX_1BIT_CLIP==0 + int blit_x = rasterizer->blit_x; +#endif for (unsigned int x = minx; x <= maxx; x ++) { #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-rasterizer->blit_x])>>8; + coverage[x] = (255 + coverage[x] * clip_line[x-blit_x])>>8; #endif } } #endif } -#define CTX_EDGE(no) entries[edges[no]] -#define CTX_EDGE_YMIN (segment->data.s16[1]-1) - #define UPDATE_PARITY \ - if (CTX_LIKELY(scanline!=CTX_EDGE_YMIN))\ + if (scanline!=segment->y0-1)\ { \ if (is_winding)\ parity = parity + -1+2*(segment->code == CTX_EDGE_FLIPPED);\ @@ -19551,20 +21113,25 @@ static inline void ctx_coverage_post_process (CtxRasterizer *rasterizer, unsigne } -inline static void +CTX_INLINE static void ctx_rasterizer_generate_coverage (CtxRasterizer *rasterizer, int minx, int maxx, uint8_t *coverage, int is_winding, const uint8_t aa_factor, - const uint8_t fraction) + const uint8_t fraction, + int *ret_c0, + int *ret_c1 + ) { 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; + int c0 = *ret_c0; + int c1 = *ret_c1; coverage -= minx; for (int t = 0; t < active_edges -1;t++) { @@ -19574,19 +21141,19 @@ ctx_rasterizer_generate_coverage (CtxRasterizer *rasterizer, if (parity) { CtxSegment *next_segment = &entries[edges[t+1]]; - const int x0 = segment->val; - const int x1 = next_segment->val; + 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; - if (CTX_UNLIKELY (first < minx)) + if (first < minx) { first = minx; graystart=0; } - if (CTX_UNLIKELY (last > maxx)) + if (last > maxx) { last = maxx; grayend=255; @@ -19604,496 +21171,256 @@ ctx_rasterizer_generate_coverage (CtxRasterizer *rasterizer, } else if (first == last) coverage[first] += (graystart-fraction+grayend); + c0 = ctx_mini(first, c0); + c1 = ctx_maxi(last, c1); } } + *ret_c0 = c0; + *ret_c1 = c1; } -#if 1 -inline static void -ctx_rasterizer_generate_coverage_set (CtxRasterizer *rasterizer, - int minx, - int maxx, - uint8_t *coverage, - int is_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; - - if (CTX_UNLIKELY (first < minx)) - { - first = minx; - graystart=0; - } - if (CTX_UNLIKELY (last > maxx)) - { - last = maxx; - grayend=255; - } +static inline float ctx_p_line_sq_dist (float x, float y, float x1, float y1, float x2, float y2) { + float A = x - x1; + float B = y - y1; + float C = x2 - x1; + float D = y2 - y1; - graystart = (graystart&0xff) ^ 255; - grayend = (grayend & 0xff); + float dot = A * C + B * D; + float len_sq = C * C + D * D; + float param = -1.0f; + float xx, yy; - coverage[first] += graystart; - coverage[last] += grayend; - if (first + 1< last) - memset(&coverage[first+1], 255, last-(first+1)); - } - } -} -#endif + if (len_sq != 0.0f) //in case of 0 length line + param = dot / len_sq; -#if 1 -inline static void -ctx_rasterizer_generate_coverage_apply (CtxRasterizer *rasterizer, - int minx, - int maxx, - // uint8_t* __restrict__ coverage, - int is_winding, - CtxCovPath comp, - ctx_apply_coverage_fun apply_coverage) -{ - CtxSegment *entries = (CtxSegment*)(&rasterizer->edge_list.entries[0]); - uint8_t *rasterizer_src = rasterizer->color; - int *edges = rasterizer->edges; - int scanline = rasterizer->scanline; - const int bpp = rasterizer->format->bpp; - int active_edges = rasterizer->active_edges; - int parity = 0; -#if CTX_RASTERIZER_SWITCH_DISPATCH - uint32_t *src_pixp; - uint32_t src_pix, si_ga, si_rb, si_ga_full, si_rb_full, si_a; - if (comp != CTX_COV_PATH_FALLBACK && - comp != CTX_COV_PATH_RGBA8_COPY_FRAGMENT && - comp != CTX_COV_PATH_RGBA8_OVER_FRAGMENT) - { - src_pixp = ((uint32_t*)rasterizer->color); - src_pix = src_pixp[0]; - si_ga = ((uint32_t*)rasterizer->color)[1]; - si_rb = ((uint32_t*)rasterizer->color)[2]; - si_ga_full = ((uint32_t*)rasterizer->color)[3]; - si_rb_full = ((uint32_t*)rasterizer->color)[4]; - si_a = src_pix >> 24; + if (param < 0.0f) { + xx = x1; + yy = y1; } - else - { - src_pix = - si_ga = - si_rb = - si_ga_full = - si_rb_full = - si_a = 0; - src_pixp = &src_pix; + else if (param > 1.0f) { + xx = x2; + yy = y2; + } + else { + xx = x1 + param * C; + yy = y1 + param * D; } -#endif - - 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; - if (parity) - { - CtxSegment *next_segment = &entries[edges[t+1]]; - const int x0 = segment->val; - const int x1 = next_segment->val; + float dx = x - xx; + float dy = y - yy; + return dx * dx + dy * dy; +} - int graystart = x0 / (CTX_RASTERIZER_EDGE_MULTIPLIER*CTX_SUBDIV/256); - int first = graystart >> 8; - int grayend = x1 / (CTX_RASTERIZER_EDGE_MULTIPLIER*CTX_SUBDIV/256); - int last = grayend >> 8; +static inline float dist_to_edge_sq (int u, int v, CtxSegment *__restrict__ entries, int edge_no) +{ + CtxSegment *segment = &entries[edge_no]; + float y0 = segment->y0; + float y1 = segment->y1; - if (CTX_UNLIKELY(first < minx)) - { - graystart = 0; - first = minx; - } - if (CTX_UNLIKELY(last > maxx)) - { - last = maxx; - grayend=255; - } - graystart = (graystart&0xff) ^ 255; + float x0 = segment->x0 * (1.0f * CTX_FULL_AA / CTX_SUBDIV ); + float x1 = segment->x1 * (1.0f * CTX_FULL_AA / CTX_SUBDIV ); + return ctx_p_line_sq_dist (u, v, x0, y0, x1, y1); +} - grayend = (grayend & 0xff); +static inline float dist_to_edge (int u, int v, CtxSegment *__restrict__ entries, int edge_no) +{ + return ctx_sqrtf_fast (dist_to_edge_sq(u,v,entries,edge_no)); +} - if (accumulated) - { - if (accumulator_x == first) - { - graystart += accumulated; - } - else - { - uint32_t* dst_pix = (uint32_t*)(&dst[(accumulator_x*bpp)/8]); - switch (comp) - { -#if CTX_RASTERIZER_SWITCH_DISPATCH - case CTX_COV_PATH_RGBA8_COPY: - *dst_pix = ctx_lerp_RGBA8_2(*dst_pix, si_ga, si_rb, accumulated); - break; - case CTX_COV_PATH_RGBA8_OVER: - *dst_pix = ctx_over_RGBA8_2(*dst_pix, si_ga, si_rb, si_a, accumulated); - break; -#endif - default: - apply_coverage (rasterizer, (uint8_t*)dst_pix, rasterizer_src, accumulator_x, &accumulated, 1); - } - } - accumulated = 0; - } +static inline float smin_exp( float a, float b, float k ) +{ + k *= 1.0; + float r = exp2(-a/k) + exp2(-b/k); + return -k*log2(r); +} - if (first < last) - { - switch (comp) - { -#if CTX_RASTERIZER_SWITCH_DISPATCH - case CTX_COV_PATH_RGBA8_COPY: - { - uint32_t* dst_pix = (uint32_t*)(&dst[(first *bpp)/8]); - *dst_pix = ctx_lerp_RGBA8_2(*dst_pix, si_ga, si_rb, graystart); +static inline float smin_cubic( float a, float b, float k ) +{ + k *= 4.0f; + float h = k-ctx_fabsf(a-b); + h = (h * (h>0))/k; + return ctx_minf(a,b) - h*h*k*0.25f; +} - dst_pix++; - ctx_span_set_colorb (dst_pix, src_pix, last - first - 1); - } - break; - case CTX_COV_PATH_RGB8_COPY: - case CTX_COV_PATH_RGBAF_COPY: - case CTX_COV_PATH_RGB565_COPY: - case CTX_COV_PATH_RGB332_COPY: - case CTX_COV_PATH_GRAYA8_COPY: - case CTX_COV_PATH_GRAYAF_COPY: - case CTX_COV_PATH_CMYKAF_COPY: - case CTX_COV_PATH_GRAY8_COPY: - case CTX_COV_PATH_CMYKA8_COPY: - case CTX_COV_PATH_CMYK8_COPY: - { - uint8_t* dsts = (uint8_t*)(&dst[(first *bpp)/8]); - uint8_t startcov = graystart; - apply_coverage (rasterizer, (uint8_t*)dsts, rasterizer_src, first, &startcov, 1); - uint8_t* dst_i = (uint8_t*)dsts; - uint8_t *color = ((uint8_t*)&rasterizer->color_native); - unsigned int bytes = rasterizer->format->bpp/8; - dst_i+=bytes; - - unsigned int count = last-(first+1);// (last - post) - (first+pre) + 1; - - //for (int i = first + pre; i <= last - post; i++) - if (CTX_LIKELY(count>0)) - switch (bytes) - { - case 1: -#if 1 - memset (dst_i, color[0], count); +static CTX_INLINE float ctx_sdf_f (CtxSegment *entries, int u, int v, float sign, int edge_count, float blur, int *edges) +{ + float min_dist_sq = 2048 * 2048 * 15 * 15; + float min_dist = 2048 * 15; + for (int j = 0; j < edge_count; j++) + { +#if CTX_RASTERIZER_BLUR_FUDGE + float dist = dist_to_edge(u, v, entries, edges[j]); + min_dist = smin_cubic(min_dist,dist, blur/2); #else - while (count--) - { - dst_i[0] = color[0]; - dst_i++; - } + float sq_dist = dist_to_edge_sq(u, v, entries, edges[j]); + min_dist_sq = ctx_minf(min_dist_sq, sq_dist); #endif - break; - case 2: - { - uint16_t val = ((uint16_t*)color)[0]; - while (count--) - { - ((uint16_t*)dst_i)[0] = val; - dst_i+=2; - } - } - break; - case 4: - { - uint32_t val = ((uint32_t*)color)[0]; - ctx_span_set_colorb ((uint32_t*)dst, val, count); - } - break; - case 16: - ctx_span_set_color_x4 ((uint32_t*)dst, (uint32_t*)color, count); - break; - case 3: - while (count--) - { - *dst_i ++ = color[0]; - *dst_i ++ = color[1]; - *dst_i ++ = color[2]; - } - break; - case 5: - while (count--) - { - *dst_i ++ = color[0]; - *dst_i ++ = color[1]; - *dst_i ++ = color[2]; - *dst_i ++ = color[3]; - *dst_i ++ = color[4]; - } - break; - default: - while (count--) - { - for (unsigned int b = 0; b < bytes; b++) - *dst_i++ = color[b]; - } - break; - } - } - break; + } -#if CTX_ENABLE_GRAY4 - case CTX_COV_PATH_GRAY4_COPY: - { - uint8_t* dstp = (uint8_t*)(&dst[(first *bpp)/8]); - uint8_t *srcp = (uint8_t*)src_pixp; - uint8_t startcov = graystart; - apply_coverage (rasterizer, (uint8_t*)dstp, rasterizer_src, first, &startcov, 1); - dstp = (uint8_t*)(&dst[((first+1)*bpp)/8]); - unsigned int count = last - first - 1; - int val = srcp[0]/17; - - uint8_t val_x2 = val + (val << 4); - { - int x = first + 1; - for (unsigned int i = 0; i < count && x & 1; count--) - { - int bitno = x & 1; - *dstp &= ~(15<<(bitno*4)); - *dstp |= (val<<(bitno*4)); - dstp += (bitno == 1); - x++; - } +#if CTX_RASTERIZER_BLUR_FUDGE==0 + min_dist = ctx_sqrtf_fast (min_dist_sq); +#endif + return min_dist * sign; +} +static inline float ctx_erf2(float x) +{ + #define CTX_2_SQRTPI 1.12837916709551257390f /* 2/sqrt(pi) */ + x = x * CTX_2_SQRTPI; + float xx = x * x; + x = x + (0.24295f + (0.03395f + 0.0104f * xx) * xx) * (x * xx); + return x * ctx_invsqrtf_fast (1.0f + x * x); +} - for (unsigned int i = 0; i < count && count>2; count-=2, x+=2, dstp++) - *dstp = val_x2; +static inline uint8_t gaussian_approximation(float x) +{ + x = ctx_erf2(x); + x+= 0.5f; + if (x > 1.0f) return 255; + if (x < 0.0f) return 0; + return x * 255.0f; +} - for (unsigned int i = 0; i < count; count--) - { - int bitno = x & 1; - *dstp &= ~(15<<(bitno*4)); - *dstp |= (val<<(bitno*4)); - dstp += (bitno == 1); - x++; - } - } - } - break; +#ifndef CTX_RASTERIZER_SDF_SKIP +#define CTX_RASTERIZER_SDF_SKIP 1 #endif -#if CTX_ENABLE_GRAY2 - case CTX_COV_PATH_GRAY2_COPY: - { - uint8_t* dstp = (uint8_t*)(&dst[(first *bpp)/8]); - uint8_t *srcp = (uint8_t*)src_pixp; - uint8_t startcov = graystart; - apply_coverage (rasterizer, (uint8_t*)dstp, rasterizer_src, first, &startcov, 1); - dstp = (uint8_t*)(&dst[((first+1)*bpp)/8]); - unsigned int count = last - first - 1; - int val = srcp[0]/85; - - uint8_t val_x4 = val + (val << 2) + (val << 4) + (val << 6); - { - int x = first + 1; - for (unsigned int i = 0; i < count && x & 3; count--) - { - int bitno = x & 3; - *dstp &= ~(3<<(bitno*2)); - *dstp |= (val<<(bitno*2)); - dstp += (bitno == 3); - x++; - } +inline static void +ctx_rasterizer_generate_sdf (CtxRasterizer *rasterizer, + const int minx, + const int maxx, + uint8_t *coverage, + const int is_winding, + float blur) +{ + CtxSegment *entries = (CtxSegment*)(&rasterizer->edge_list.entries[0]); + int *edges = rasterizer->edges; + int active_edges = rasterizer->active_edges; + int *shadow_edges = rasterizer->shadow_edges; + int shadow_active_edges = rasterizer->shadow_active_edges; + int scanline = rasterizer->scanline; + int parity = 0; + float inv_blur = 1.0/(blur * CTX_FULL_AA); +#if CTX_RASTERIZER_SDF_SKIP + const int skip_len = blur / 2 + 1; + // how far ahead we jump looking for + // same alpha runs - speeding up solid/blank and +#endif + coverage -= minx; - for (unsigned int i = 0; i < count && count>4; count-=4, x+=4, dstp++) - *dstp = val_x4; - for (unsigned int i = 0; i < count; count--) - { - int bitno = x & 3; - *dstp &= ~(3<<(bitno*2)); - *dstp |= (val<<(bitno*2)); - dstp += (bitno == 3); - x++; - } - } - } - break; -#endif + int c0 = maxx; + int c1 = minx; -#if CTX_ENABLE_GRAY1 - case CTX_COV_PATH_GRAY1_COPY: - { - uint8_t* dstp = (uint8_t*)(&dst[(first *bpp)/8]); - uint8_t *srcp = (uint8_t*)src_pixp; - uint8_t startcov = graystart; - apply_coverage (rasterizer, (uint8_t*)dstp, rasterizer_src, first, &startcov, 1); - dstp = (uint8_t*)(&dst[((first+1)*bpp)/8]); - unsigned int count = last - first - 1; - if (srcp[0]>=127) - { - int x = first + 1; - for (unsigned int i = 0; i < count && x & 7; count--) - { - int bitno = x & 7; - *dstp |= (1<8; count-=8) - { - *dstp = 255; - dstp++; - x+=8; - } + CtxSegment *next_segment = &entries[edges[t+1]]; + int x0 = segment->val; + const int x1 = next_segment->val; - for (unsigned int i = 0; i < count; i++) - { - int bitno = x & 7; - *dstp |= (1<> 8; + int last = grayend >> 8; - for (unsigned int i = 0; i < count && count>8; count-=8) - { - *dstp = 0; - dstp++; - x+=8; - } + if (first < minx) + first = minx; + if (last > maxx) + last = maxx; - for (unsigned int i = 0; i < count; i++) - { - int bitno = x & 7; - *dstp &= ~(1<fragment (rasterizer, u0, v0, w0, &dst[((first+1)*bpp)/8], last-first-1, ud, vd, wd); - } - break; - case CTX_COV_PATH_RGBA8_OVER_FRAGMENT: - { - uint8_t gs = graystart; - ctx_RGBA8_source_over_normal_fragment (rasterizer, &dst[(first * bpp)/8], NULL, first, &gs, 1); - ctx_RGBA8_source_over_normal_full_cov_fragment (rasterizer, - &dst[((first+1)*bpp)/8], NULL, first + 1, NULL, last-first-1, 1); - } - break; -#endif - default: - { -#if static_OPAQUE - uint8_t *opaque = &rasterizer->opaque[0]; -#else - uint8_t opaque[last-first]; - memset (opaque, 255, sizeof (opaque)); + int i; +#if CTX_RASTERIZER_SDF_SKIP + int prev = -1; #endif - opaque[0] = graystart; - apply_coverage (rasterizer, - &dst[(first * bpp)/8], - rasterizer_src, first, opaque, last-first); + float sign = parity?1.0f:-1.0f; + for (i = first; i <= last; i++) + { + coverage[i] = COMPUTE_SDF(u,scanline); -#if static_OPAQUE - opaque[0] = 255; -#endif - } - } - accumulated = grayend; - } - else if (first == last) +#if CTX_RASTERIZER_SDF_SKIP + if ((prev == coverage[i]) & ((prev == 0)|(prev==255))) { - accumulated = (graystart-(grayend^255)); + if (last-i > skip_len + && COMPUTE_SDF(u+15*skip_len, scanline) == prev + && COMPUTE_SDF(u+15*skip_len/2, scanline) == prev) + { + for (int j = 1; j < skip_len; j++) + coverage[i+j] = prev; + u += 15 * skip_len; + i += (skip_len-1); + continue; + } } - accumulator_x = last; + prev = coverage[i]; +#endif + u += 15; } + } + c0 = ctx_mini (c0, first); + c1 = ctx_maxi (c1, last); } - if (accumulated) - { - uint32_t* dst_pix = (uint32_t*)(&dst[(accumulator_x*bpp)/8]); - switch (comp) + float sign = -1.0f; + + { + int i = minx; + +#if CTX_RASTERIZER_SDF_SKIP + int prev = -1; +#endif + for (; i < c0; i++) + { + coverage[i] = COMPUTE_SDF(i*15, scanline); +#if CTX_RASTERIZER_SDF_SKIP + if (c0-i > skip_len && + COMPUTE_SDF((i+skip_len)*15, scanline) == prev) { -#if CTX_RASTERIZER_SWITCH_DISPATCH - case CTX_COV_PATH_RGBA8_COPY: - *dst_pix = ctx_lerp_RGBA8_2(*dst_pix, si_ga, si_rb, accumulated); - break; - case CTX_COV_PATH_RGBA8_OVER: - *dst_pix = ctx_over_RGBA8_2(*dst_pix, si_ga, si_rb, si_a, accumulated); - break; + for (int j = 1; j < skip_len; j++) + coverage[i+j] = prev; + i += (skip_len-1); + continue; + } + prev = coverage[i]; #endif - default: - apply_coverage (rasterizer, (uint8_t*)dst_pix, rasterizer_src, - accumulator_x, &accumulated, 1); + } +#if CTX_RASTERIZER_SDF_SKIP + prev = -1; +#endif + for (int i = c1+1; i < maxx; i++) + { + coverage[i] = COMPUTE_SDF(i*15, scanline); +#if CTX_RASTERIZER_SDF_SKIP + if (maxx-i > skip_len && COMPUTE_SDF((i+skip_len)*15, scanline) == prev) + { + for (int j = 1; j < skip_len; j++) + coverage[i+j] = prev; + i += (skip_len-1); + continue; } - } -} + prev = coverage[i]; #endif + } + } +} inline static void -ctx_rasterizer_generate_coverage_set2 (CtxRasterizer *rasterizer, - int minx, - int maxx, - uint8_t *coverage, - int is_winding) +ctx_rasterizer_generate_coverage_grads (CtxRasterizer *rasterizer, + const int minx, + const int maxx, + uint8_t *coverage, + const int is_winding, + int *c0_ret, + int *c1_ret) { CtxSegment *entries = (CtxSegment*)(&rasterizer->edge_list.entries[0]); int *edges = rasterizer->edges; @@ -20106,6 +21433,9 @@ ctx_rasterizer_generate_coverage_set2 (CtxRasterizer *rasterizer, const int minx_ = minx * CTX_RASTERIZER_EDGE_MULTIPLIER * CTX_SUBDIV; const int maxx_ = maxx * CTX_RASTERIZER_EDGE_MULTIPLIER * CTX_SUBDIV; + int c0 = maxx; + int c1 = minx; + for (int t = 0; t < active_edges -1;t++) { CtxSegment *segment = &entries[edges[t]]; @@ -20116,25 +21446,18 @@ ctx_rasterizer_generate_coverage_set2 (CtxRasterizer *rasterizer, 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; - - 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; 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; - if (CTX_UNLIKELY (first < minx)) + if (first < minx) { first = minx; graystart=0; } - if (CTX_UNLIKELY (last > maxx)) + if (last > maxx) { last = maxx; grayend=255; @@ -20147,490 +21470,479 @@ ctx_rasterizer_generate_coverage_set2 (CtxRasterizer *rasterizer, int pre = 1; int post = 1; - if (abs(delta0) < CTX_RASTERIZER_AA_SLOPE_LIMIT3_FAST_AA) + if (segment->aa == 0) { coverage[first] += graystart; + c0 = ctx_mini(first, c0); } else { - unsigned int u0 = ctx_mini (maxx_, ctx_maxi (minx_, ctx_mini (x0_start, x0_end))); - unsigned int u1 = ctx_mini (maxx_, ctx_maxi (minx_, ctx_maxi (x0_start, x0_end))); + const int delta0 = segment->delta; + int x0_start = x0 - delta0 * CTX_AA_HALFSTEP2; + int x0_end = x0 + delta0 * CTX_AA_HALFSTEP; + unsigned int u0x0 = ctx_mini (maxx_, ctx_maxi (minx_, ctx_mini (x0_start, x0_end))); + unsigned int u1x0 = ctx_mini (maxx_, ctx_maxi (minx_, ctx_maxi (x0_start, x0_end))); - int us = u0 / (CTX_RASTERIZER_EDGE_MULTIPLIER*CTX_SUBDIV); - int count = 0; + int us = u0x0 / (CTX_RASTERIZER_EDGE_MULTIPLIER*CTX_SUBDIV); - int mod = ((u0 / (CTX_RASTERIZER_EDGE_MULTIPLIER*CTX_SUBDIV/256) % 256)^255) * + int mod = ((u0x0 / (CTX_RASTERIZER_EDGE_MULTIPLIER*CTX_SUBDIV/256) % 256)^255) * (CTX_RASTERIZER_EDGE_MULTIPLIER*CTX_SUBDIV/255); - int sum = ((u1-u0+CTX_RASTERIZER_EDGE_MULTIPLIER * CTX_SUBDIV)/255); + int sum = ((u1x0-u0x0+CTX_RASTERIZER_EDGE_MULTIPLIER * CTX_SUBDIV)/255); - int recip = 65536/sum; - for (unsigned int u = u0; u < u1; u+= CTX_RASTERIZER_EDGE_MULTIPLIER*CTX_SUBDIV) + int recip = (65535)/sum; + int a = mod * recip; + recip *= CTX_RASTERIZER_EDGE_MULTIPLIER*CTX_SUBDIV; + c0 = ctx_mini(us, c0); + for (unsigned int u = u0x0; u < u1x0; u+= CTX_RASTERIZER_EDGE_MULTIPLIER*CTX_SUBDIV) { - coverage[us + count] += ((u - u0 + mod) * recip)>>16; - count++; + coverage[us ++] += a>>16; + a += recip; } - pre = (us+count-1)-first+1; + pre = (us-1)-first+1; } - if (abs(delta1) < CTX_RASTERIZER_AA_SLOPE_LIMIT3_FAST_AA) + if (next_segment->aa == 0) { coverage[last] += grayend; + c1 = ctx_maxi(last, c1); } else { + const int delta1 = next_segment->delta; + int x1_start = x1 - delta1 * CTX_AA_HALFSTEP2; + int x1_end = x1 + delta1 * CTX_AA_HALFSTEP; unsigned int u0 = ctx_mini (maxx_, ctx_maxi (minx_, ctx_mini (x1_start, x1_end))); unsigned int u1 = ctx_mini (maxx_, ctx_maxi (minx_, ctx_maxi (x1_start, x1_end))); int us = u0 / (CTX_RASTERIZER_EDGE_MULTIPLIER*CTX_SUBDIV); - int count = 0; - int mod = ((((u0 / (CTX_RASTERIZER_EDGE_MULTIPLIER*CTX_SUBDIV/256) % 256)^255)+64) * + int mod = ((((u0 / (CTX_RASTERIZER_EDGE_MULTIPLIER*CTX_SUBDIV/256) % 256)^255)) * (CTX_RASTERIZER_EDGE_MULTIPLIER*CTX_SUBDIV/255)); - int sum = ((u1-u0+CTX_RASTERIZER_EDGE_MULTIPLIER * CTX_SUBDIV * 5/4)/255); - int recip = 65536 / sum; + int sum = ((u1-u0+CTX_RASTERIZER_EDGE_MULTIPLIER * CTX_SUBDIV)/255); + int recip = (65535) / sum; + int a = (65536 * 255) - mod * recip; + recip *= CTX_RASTERIZER_EDGE_MULTIPLIER*CTX_SUBDIV; + post = last-us; for (unsigned int u = u0; u < u1; u+= CTX_RASTERIZER_EDGE_MULTIPLIER*CTX_SUBDIV) { - coverage[us + count] += (((u - u0 + mod) * recip)>>16) ^ 255; - count++; + coverage[us ++] += (a>>16); + a -= recip; } - post = last-us+1; + c1 = ctx_maxi(us, c1); } - for (int i = first + pre; i <= last - post; i++) + last-=post; + for (int i = first + pre; i <= last; i++) coverage[i] = 255; } else if (first == last) { coverage[last]+=(graystart-(grayend^255)); + c0 = ctx_mini(first, c0); + c1 = ctx_maxi(last, c1); } } } + + *c0_ret = c0; + *c1_ret = c1; } +#define CTX_RASTERIZER_MAX_EMPTIES 16 +#define CTX_RASTERIZER_MAX_SOLID 16 inline static void -ctx_rasterizer_generate_coverage_apply2 (CtxRasterizer *rasterizer, - int minx, - int maxx, - uint8_t *coverage, - int is_winding, - CtxCovPath comp, - ctx_apply_coverage_fun apply_coverage) -{ - CtxSegment *entries = (CtxSegment*)(&rasterizer->edge_list.entries[0]); - int *edges = rasterizer->edges; - int scanline = rasterizer->scanline; +ctx_rasterizer_apply_grads_generic (CtxRasterizer *rasterizer, + const int minx, + const int maxx, + uint8_t *coverage, + const int is_winding, + ctx_apply_coverage_fun apply_coverage) +{ +#define CTX_APPLY_GRAD_A \ + CtxSegment *entries = (CtxSegment*)(&rasterizer->edge_list.entries[0]);\ + int *edges = rasterizer->edges;\ + uint8_t *rasterizer_src = rasterizer->color;\ + int scanline = rasterizer->scanline;\ + unsigned int active_edges = rasterizer->active_edges - 1;\ + int parity = 0;\ +\ + uint8_t *dst = ( (uint8_t *) rasterizer->buf) +\ + (rasterizer->blit_stride * (scanline / CTX_FULL_AA));\ + coverage -= minx;\ +\ + const int minx_ = minx * CTX_RASTERIZER_EDGE_MULTIPLIER * CTX_SUBDIV;\ + const int maxx_ = maxx * CTX_RASTERIZER_EDGE_MULTIPLIER * CTX_SUBDIV;\ +\ + int cov_min = maxx;\ + int cov_max = minx;\ const int bpp = rasterizer->format->bpp; - int active_edges = rasterizer->active_edges; - uint8_t *rasterizer_src = rasterizer->color; - int parity = 0; + CTX_APPLY_GRAD_A + +#define CTX_APPLY_GRAD_B(empty_factor, solid_factor) \ + for (unsigned int t = 0; t < active_edges;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; \ + \ + if (CTX_UNLIKELY (first < minx)) \ + { \ + first = minx; \ + graystart=0; \ + } \ + if (CTX_UNLIKELY (last > maxx)) \ + { \ + last = maxx; \ + grayend=255; \ + } \ + graystart = (graystart&0xff) ^ 255; \ + grayend = (grayend & 0xff); \ +\ + if (first < last)\ + {\ + const int delta1 = next_segment->delta; \ + int x1_start = x1 - delta1 * CTX_AA_HALFSTEP2; \ + int x1_end = x1 + delta1 * CTX_AA_HALFSTEP; \ + unsigned int u0x1 = ctx_mini (maxx_, ctx_maxi (minx_, ctx_mini (x1_start, x1_end)));\ +\ + unsigned int pre = 1;\ + unsigned int post = 1;\ +\ + if (first - cov_max > CTX_RASTERIZER_MAX_EMPTIES * empty_factor)\ + {\ + if (cov_max>=cov_min)\ + {\ + apply_coverage (cov_max-cov_min+1, &dst[((cov_min) * bpp)/8], rasterizer_src,\ + &coverage[cov_min], rasterizer, cov_min);\ + cov_min = maxx;\ + cov_max = minx;\ + }\ + }\ +\ + if (segment->aa == 0)\ + {\ + coverage[first] += graystart;\ + cov_min = ctx_mini (cov_min, first);\ + cov_max = ctx_maxi (cov_max, first);\ + }\ + else\ + {\ + const int delta0 = segment->delta; \ + int x0_start = x0 - delta0 * CTX_AA_HALFSTEP2; \ + int x0_end = x0 + delta0 * CTX_AA_HALFSTEP; \ + unsigned int u0x0 = ctx_mini (maxx_, ctx_maxi (minx_, ctx_mini (x0_start, x0_end)));\ + unsigned int u1x0 = ctx_mini (maxx_, ctx_maxi (minx_, ctx_maxi (x0_start, x0_end)));\ +\ + int us = u0x0 / (CTX_RASTERIZER_EDGE_MULTIPLIER*CTX_SUBDIV);\ + int mod = ((u0x0 / (CTX_RASTERIZER_EDGE_MULTIPLIER*CTX_SUBDIV/256) % 256)^255) *\ + (CTX_RASTERIZER_EDGE_MULTIPLIER*CTX_SUBDIV/255);\ + int sum = ((u1x0-u0x0+CTX_RASTERIZER_EDGE_MULTIPLIER * CTX_SUBDIV)/255);\ +\ + int recip = (65535)/sum;\ + int a = mod * recip;\ + recip *= CTX_RASTERIZER_EDGE_MULTIPLIER*CTX_SUBDIV;\ +\ + cov_min = ctx_mini (cov_min, us);\ + for (unsigned int u = u0x0; u < u1x0; u+= CTX_RASTERIZER_EDGE_MULTIPLIER*CTX_SUBDIV)\ + {\ + coverage[us ++] += a>>16;\ + a += recip;\ + }\ + cov_max = us;\ +\ + pre = (us-1)-first+1;\ + }\ + if (next_segment->aa != 0) \ + { \ + post = last - u0x1 / (CTX_RASTERIZER_EDGE_MULTIPLIER*CTX_SUBDIV); \ + }\ + {\ + int width = (last-post) - (first+pre) + 1;\ + if (width > CTX_RASTERIZER_MAX_SOLID * solid_factor)\ + {\ + if (cov_max>=cov_min)\ + {\ + apply_coverage (cov_max-cov_min+1, &dst[((cov_min) * bpp)/8], rasterizer_src,\ + &coverage[cov_min], rasterizer, cov_min);\ + cov_min = maxx;\ + cov_max = minx;\ + } + CTX_APPLY_GRAD_B(1, 1) + { +#if static_OPAQUE + uint8_t *opaque = &rasterizer->opaque[0]; +#else + uint8_t opaque[width]; + memset (opaque, 255, sizeof (opaque)); +#endif + apply_coverage (width, + &dst[((first + pre) * bpp)/8], + rasterizer_src, + opaque, + rasterizer, + first + pre); + } +#define CTX_APPLY_GRAD_C \ + }\ + else\ + {\ + for (int i = 0; i < width; i++)\ + coverage[first + pre + i] = 255;\ + cov_min = ctx_mini (cov_min, first + pre);\ + cov_max = first + pre + width;\ + }\ + }\ + \ + if (next_segment->aa == 0)\ + {\ + coverage[last] += grayend;\ + cov_min = ctx_mini (cov_min, last);\ + cov_max = last;\ + }\ + else\ + {\ + unsigned int u1x1 = ctx_mini (maxx_, ctx_maxi (minx_, ctx_maxi (x1_start, x1_end)));\ + int us = u0x1 / (CTX_RASTERIZER_EDGE_MULTIPLIER*CTX_SUBDIV);\ + int mod = ((((u0x1 / (CTX_RASTERIZER_EDGE_MULTIPLIER*CTX_SUBDIV/256) % 256)^255)) *\ + (CTX_RASTERIZER_EDGE_MULTIPLIER*CTX_SUBDIV/255));\ + int sum = ((u1x1-u0x1+CTX_RASTERIZER_EDGE_MULTIPLIER * CTX_SUBDIV)/255);\ + int recip = (65535) / sum;\ + int a = (65536 * 255) - mod * recip;\ + recip *= CTX_RASTERIZER_EDGE_MULTIPLIER*CTX_SUBDIV;\ +\ + cov_min = ctx_mini (cov_min, us);\ + for (unsigned int u = u0x1; u < u1x1; u+= CTX_RASTERIZER_EDGE_MULTIPLIER*CTX_SUBDIV)\ + {\ + coverage[us ++] += (a>>16);\ + a -= recip;\ + }\ + cov_max = us;\ + }\ + }\ + else if (first == last)\ + {\ + coverage[last]+=(graystart-(grayend^255)); \ + cov_min = ctx_mini (cov_min, first); \ + cov_max = last;\ + }\ + }\ + }\ + if (cov_max>=cov_min)\ + apply_coverage (cov_max-cov_min+1, &dst[(cov_min*bpp)/8], rasterizer_src, \ + &coverage[cov_min], rasterizer, cov_min); + CTX_APPLY_GRAD_C +} -#if CTX_RASTERIZER_SWITCH_DISPATCH - uint32_t *src_pixp; - uint32_t src_pix, si_ga, si_rb, si_ga_full, si_rb_full, si_a; - if (comp != CTX_COV_PATH_FALLBACK && - comp != CTX_COV_PATH_RGBA8_COPY_FRAGMENT && - comp != CTX_COV_PATH_RGBA8_OVER_FRAGMENT) - { - src_pixp = ((uint32_t*)rasterizer->color); - src_pix = src_pixp[0]; - si_ga = ((uint32_t*)rasterizer->color)[1]; - si_rb = ((uint32_t*)rasterizer->color)[2]; - si_ga_full = ((uint32_t*)rasterizer->color)[3]; - si_rb_full = ((uint32_t*)rasterizer->color)[4]; - si_a = src_pix >> 24; - } - else +inline static void +ctx_rasterizer_apply_grads_RGBA8_copy_normal_color (CtxRasterizer *rasterizer, + const int minx, + const int maxx, + uint8_t *coverage, + const int is_winding, + ctx_apply_coverage_fun apply_coverage) +{ + CTX_APPLY_GRAD_A + uint32_t src_pix = ((uint32_t*)rasterizer_src)[0]; + CTX_APPLY_GRAD_B(1, 1) + ctx_span_set_color ((uint32_t*)(&dst[(first+pre) *4]), src_pix, width); + CTX_APPLY_GRAD_C +} + +inline static void +ctx_rasterizer_apply_grads_RGBA8_over_normal_color (CtxRasterizer *rasterizer, + const int minx, + const int maxx, + uint8_t *coverage, + const int is_winding, + ctx_apply_coverage_fun apply_coverage) +{ + CTX_APPLY_GRAD_A + uint32_t si_ga_full, si_rb_full, si_ga, si_a; + si_ga = ((uint32_t*)rasterizer_src)[1]; + si_ga_full = ((uint32_t*)rasterizer_src)[3]; + si_rb_full = ((uint32_t*)rasterizer_src)[4]; + si_a = si_ga >> 16; + CTX_APPLY_GRAD_B(1, 1) + uint32_t* dst_pix = (uint32_t*)(&dst[(first+pre) *4]); + unsigned int count = width; + while (count--) { - src_pix = - si_ga = - si_rb = - si_ga_full = - si_rb_full = - si_a = 0; - src_pixp = &src_pix; + *dst_pix = ctx_over_RGBA8_full_2(*dst_pix, si_ga_full, si_rb_full, si_a); + dst_pix++; } -#endif - - uint8_t *dst = ( (uint8_t *) rasterizer->buf) + - (rasterizer->blit_stride * (scanline / CTX_FULL_AA)); - - coverage -= minx; + CTX_APPLY_GRAD_C +} - const int minx_ = minx * CTX_RASTERIZER_EDGE_MULTIPLIER * CTX_SUBDIV; - const int maxx_ = maxx * CTX_RASTERIZER_EDGE_MULTIPLIER * CTX_SUBDIV; +inline static void +ctx_rasterizer_apply_grads_copy_normal_color (CtxRasterizer *rasterizer, + const int minx, + const int maxx, + uint8_t *coverage, + const int is_winding, + const CtxCovPath comp, + ctx_apply_coverage_fun apply_coverage) +{ + CTX_APPLY_GRAD_A + unsigned int bytes = bpp/8; - int accumulated_x0 = 65538; - int accumulated_x1 = 65536; + CTX_APPLY_GRAD_B(1, 1) - for (int t = 0; t < active_edges -1;t++) + uint8_t* dst_i = (uint8_t*)(&dst[(first+pre) * bytes]); + uint8_t* color = ((uint8_t*)&rasterizer->color_native); + switch (bytes) + { + case 16: + ctx_span_set_color_x4 ((uint32_t*)dst_i, (uint32_t*)color, width); + break; + case 4: + ctx_span_set_color ((uint32_t*)(&dst[(first+pre) *4]), ((uint32_t*)color)[0], width); + break; + case 2: { - 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; - - 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; - - 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; - - if (CTX_UNLIKELY (first < minx)) - { - first = minx; - graystart=0; - } - if (CTX_UNLIKELY (last > maxx)) - { - last = maxx; - grayend=255; - } - graystart = 255-(graystart&0xff); - grayend = (grayend & 0xff); - - if (first < last) - { - int pre = 1; - int post = 1; - - if (abs(delta0) < CTX_RASTERIZER_AA_SLOPE_LIMIT3_FAST_AA) - { - coverage[first] += graystart; - - accumulated_x1 = first; - accumulated_x0 = ctx_mini (accumulated_x0, first); - } - else - { - unsigned int u0 = ctx_mini (maxx_, ctx_maxi (minx_, ctx_mini (x0_start, x0_end))); - unsigned int u1 = ctx_mini (maxx_, ctx_maxi (minx_, ctx_maxi (x0_start, x0_end))); - - int mod = ((u0 / (CTX_RASTERIZER_EDGE_MULTIPLIER*CTX_SUBDIV/256) % 256)^255) * - (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; - int recip = 65536/ sum; - for (unsigned int u = u0; u < u1; u+= CTX_RASTERIZER_EDGE_MULTIPLIER*CTX_SUBDIV) - { - coverage[us + count] += ((u - u0 + mod) * recip)>>16; - count++; - } - pre = us+count-first; - - accumulated_x0 = ctx_mini (accumulated_x0, us); - accumulated_x1 = us + count - 1; - } - - if (accumulated_x1-accumulated_x0>=0) - { - switch (comp) - { -#if CTX_RASTERIZER_SWITCH_DISPATCH - case CTX_COV_PATH_RGBA8_OVER: - { - uint32_t *dst_i = (uint32_t*)&dst[((accumulated_x0) * bpp)/8]; - 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_RGBA8_COPY: - { - uint32_t *dst_i = (uint32_t*)&dst[((accumulated_x0) * bpp)/8]; - 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; - case CTX_COV_PATH_RGB8_COPY: - { - uint8_t *dst_i = (uint8_t*)&dst[((accumulated_x0) * bpp)/8]; - uint8_t *srcp = (uint8_t*)src_pixp; - for (int i = 0; i < accumulated_x1-accumulated_x0+1; i++) - { - for (int c = 0; c < 3; c++) - dst_i[c] = ctx_lerp_u8 (dst_i[c], srcp[c], coverage[accumulated_x0+i]); - dst_i +=3; - } - } - break; -#endif - default: - apply_coverage (rasterizer, - &dst[((accumulated_x0) * bpp)/8], - rasterizer_src, - accumulated_x0, - &coverage[accumulated_x0], - accumulated_x1-accumulated_x0+1); - } - accumulated_x0 = 65538; - accumulated_x1 = 65536; - } + uint16_t val = ((uint16_t*)color)[0]; + while (width--) + { + ((uint16_t*)dst_i)[0] = val; + dst_i+=2; + } + } + break; + default: + while (width--) + { + for (unsigned int b = 0; b < bytes; b++) + *dst_i++ = color[b]; + } + break; - if (abs(delta1) < CTX_RASTERIZER_AA_SLOPE_LIMIT3_FAST_AA) - { - coverage[last] += grayend; - accumulated_x1 = last; - accumulated_x0 = last; - } - else - { - unsigned int u0 = ctx_mini (maxx_, ctx_maxi (minx_, ctx_mini (x1_start, x1_end))); - unsigned int u1 = ctx_mini (maxx_, ctx_maxi (minx_, ctx_maxi (x1_start, x1_end))); + } + CTX_APPLY_GRAD_C +} - int us = u0 / (CTX_RASTERIZER_EDGE_MULTIPLIER*CTX_SUBDIV); - int count = 0; +inline static void +ctx_rasterizer_apply_grads_RGBA8_copy_fragment (CtxRasterizer *rasterizer, + const int minx, + const int maxx, + uint8_t *coverage, + const int is_winding, + ctx_apply_coverage_fun apply_coverage) +{ + CTX_APPLY_GRAD_A + CTX_APPLY_GRAD_B(1, 1) + { + float u0 = 0; float v0 = 0; + float ud = 0; float vd = 0; + float w0 = 1; float wd = 0; + ctx_init_uv (rasterizer, first+pre, scanline/CTX_FULL_AA,&u0, &v0, &w0, &ud, &vd, &wd); + rasterizer->fragment (rasterizer, u0, v0, w0, &dst[(first+pre)*4], + width, ud, vd, wd); + } + CTX_APPLY_GRAD_C +} - int mod = ((((u0 / (CTX_RASTERIZER_EDGE_MULTIPLIER*CTX_SUBDIV/256) % 256)^255) +64) * - (CTX_RASTERIZER_EDGE_MULTIPLIER*CTX_SUBDIV/255)); - int sum = ((u1-u0+CTX_RASTERIZER_EDGE_MULTIPLIER * CTX_SUBDIV * 5/4)/255); +inline static void +ctx_rasterizer_apply_grads_RGBA8_over_fragment (CtxRasterizer *rasterizer, + const int minx, + const int maxx, + uint8_t *coverage, + const int is_winding, + ctx_apply_coverage_fun apply_coverage) +{ + CTX_APPLY_GRAD_A + CTX_APPLY_GRAD_B(1, 1) + ctx_RGBA8_source_over_normal_full_cov_fragment ( + width, + &dst[(first+pre)*4], + NULL, + NULL, + rasterizer, + first + pre, + 1); + CTX_APPLY_GRAD_C +} + +#undef CTX_APPLY_GRAD_A +#undef CTX_APPLY_GRAD_B +#undef CTX_APPLY_GRAD_C - int recip = 65536/ sum; - for (unsigned int u = u0; u < u1; u+= CTX_RASTERIZER_EDGE_MULTIPLIER*CTX_SUBDIV) - { - coverage[us + count] = (((u - u0 + mod)*recip)>>16)^255; - count++; - } - post = last-us+1; - accumulated_x1 = us + count; - accumulated_x0 = us; - } - switch (comp) - { +inline static void +ctx_rasterizer_apply_grads (CtxRasterizer *rasterizer, + const int minx, + const int maxx, + uint8_t *coverage, + const int is_winding, + const CtxCovPath comp, + ctx_apply_coverage_fun apply_coverage) +{ + if (rasterizer->active_edges < 2) return; + switch (comp) + { #if CTX_RASTERIZER_SWITCH_DISPATCH - case CTX_COV_PATH_RGBAF_COPY: - case CTX_COV_PATH_GRAY8_COPY: - case CTX_COV_PATH_RGB8_COPY: - case CTX_COV_PATH_GRAYA8_COPY: - case CTX_COV_PATH_GRAYAF_COPY: - case CTX_COV_PATH_CMYKAF_COPY: - case CTX_COV_PATH_RGB565_COPY: - case CTX_COV_PATH_RGB332_COPY: - case CTX_COV_PATH_CMYK8_COPY: - case CTX_COV_PATH_CMYKA8_COPY: - { - uint8_t* dsts = (uint8_t*)(&dst[(first *bpp)/8]); - uint8_t* dst_i = (uint8_t*)dsts; - uint8_t* color = ((uint8_t*)&rasterizer->color_native); - unsigned int bytes = rasterizer->format->bpp/8; - dst_i+=pre*bytes; - - int scount = (last - post) - (first+pre) + 1; - unsigned int count = scount; - - //for (int i = first + pre; i <= last - post; i++) - if (CTX_LIKELY(scount>0)) - switch (bytes) - { - case 1: -#if 1 - memset (dst_i, color[0], count); -#else - while (count--) - { - dst_i[0] = color[0]; - dst_i++; - } -#endif - break; - case 2: - { - uint16_t val = ((uint16_t*)color)[0]; - while (count--) - { - ((uint16_t*)dst_i)[0] = val; - dst_i+=2; - } - } - break; - case 4: - { - uint32_t val = ((uint32_t*)color)[0]; - while (count--) - { - ((uint32_t*)dst_i)[0] = val; - dst_i+=4; - } - } - break; - case 16: - ctx_span_set_color_x4 ((uint32_t*)dst, (uint32_t*)color, count); - break; - case 3: - while (count--) - { - *dst_i++ = color[0]; - *dst_i++ = color[1]; - *dst_i++ = color[2]; - } - break; - case 5: - while (count--) - { - *dst_i++ = color[0]; - *dst_i++ = color[1]; - *dst_i++ = color[2]; - *dst_i++ = color[3]; - *dst_i++ = color[4]; - } - break; - default: - while (count--) - { - for (unsigned int b = 0; b < bytes; b++) - *dst_i++ = color[b]; - } - break; - } - } - break; - - case CTX_COV_PATH_RGBA8_COPY: - { - uint32_t* dst_pix = (uint32_t*)(&dst[(first *bpp)/8]); - dst_pix+=pre; - ctx_span_set_color (dst_pix, src_pix, last-first-pre-post + 1); - } - break; - - - case CTX_COV_PATH_RGBA8_OVER: - { - uint32_t* dst_pix = (uint32_t*)(&dst[(first *bpp)/8]); - dst_pix+=pre; - int scount = (last - post) - (first + pre) + 1; - if (scount > 0) - { - unsigned int count = scount; - while (count--) - { - *dst_pix = ctx_over_RGBA8_full_2(*dst_pix, si_ga_full, si_rb_full, si_a); - dst_pix++; - } - } - } - break; - case CTX_COV_PATH_RGBA8_COPY_FRAGMENT: - { - int width = last-first-pre-post+1; - if (width>0) - { - float u0 = 0; float v0 = 0; - float ud = 0; float vd = 0; - float w0 = 1; float wd = 0; - ctx_init_uv (rasterizer, first+pre, rasterizer->scanline/CTX_FULL_AA,&u0, &v0, &w0, &ud, &vd, &wd); - rasterizer->fragment (rasterizer, u0, v0, w0, &dst[(first+pre)*bpp/8], - width, ud, vd, wd); - } - } - break; - case CTX_COV_PATH_RGBA8_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)/8], - NULL, - first + pre, - NULL, - width, 1); - } - break; + case CTX_COV_PATH_RGBA8_OVER: + ctx_rasterizer_apply_grads_RGBA8_over_normal_color (rasterizer, minx, maxx, coverage, is_winding, apply_coverage); + break; + case CTX_COV_PATH_RGBA8_COPY: + ctx_rasterizer_apply_grads_RGBA8_copy_normal_color (rasterizer, minx, maxx, coverage, is_winding, apply_coverage); + break; + case CTX_COV_PATH_RGB565_COPY: + case CTX_COV_PATH_RGBAF_COPY: + case CTX_COV_PATH_RGB332_COPY: + case CTX_COV_PATH_GRAY8_COPY: + case CTX_COV_PATH_RGB8_COPY: + case CTX_COV_PATH_GRAYA8_COPY: + case CTX_COV_PATH_GRAYAF_COPY: + case CTX_COV_PATH_CMYKAF_COPY: + case CTX_COV_PATH_CMYK8_COPY: + case CTX_COV_PATH_CMYKA8_COPY: + ctx_rasterizer_apply_grads_copy_normal_color (rasterizer, minx, maxx, coverage, is_winding, comp, apply_coverage); + break; + case CTX_COV_PATH_RGBA8_COPY_FRAGMENT: + ctx_rasterizer_apply_grads_RGBA8_copy_fragment (rasterizer, minx, maxx, coverage, is_winding, apply_coverage); + break; + case CTX_COV_PATH_RGBA8_OVER_FRAGMENT: + ctx_rasterizer_apply_grads_RGBA8_over_fragment (rasterizer, minx, maxx, coverage, is_winding, apply_coverage); + break; #endif - default: - { - int width = last-first-pre-post+1; - if (width > 0) - { -#if static_OPAQUE - uint8_t *opaque = &rasterizer->opaque[0]; -#else - uint8_t opaque[width]; - memset (opaque, 255, sizeof (opaque)); -#endif - apply_coverage (rasterizer, - &dst[((first + pre) * bpp)/8], - rasterizer_src, - first + pre, - opaque, - width); - } - } - } - } - else if (first == last) - { - coverage[last]+=(graystart-(255-grayend)); - - accumulated_x1 = last; - accumulated_x0 = ctx_mini (accumulated_x0, last); - } - } - } + default: + ctx_rasterizer_apply_grads_generic (rasterizer, minx, maxx, coverage, is_winding, apply_coverage); + } +} - if (accumulated_x1-accumulated_x0>=0) - { - switch (comp) - { -#if CTX_RASTERIZER_SWITCH_DISPATCH - case CTX_COV_PATH_RGBA8_OVER: - { - uint32_t *dst_i = (uint32_t*)&dst[((accumulated_x0) * bpp)/8]; - 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_RGBA8_COPY: - { - uint32_t *dst_i = (uint32_t*)&dst[((accumulated_x0) * bpp)/8]; - 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; +static inline void +ctx_rasterizer_reset_soft (CtxRasterizer *rasterizer) +{ +#if CTX_SCANBIN==0 + rasterizer->edge_pos = #endif - default: - apply_coverage (rasterizer, - &dst[((accumulated_x0) * bpp)/8], - rasterizer_src, - accumulated_x0, - &coverage[accumulated_x0], - accumulated_x1-accumulated_x0+1); - } - } + rasterizer->shadow_edge_pos = + rasterizer->scanline = 0; + //rasterizer->comp_op = NULL; // keep comp_op cached + // between rasterizations where rendering attributes are + // nonchanging } -#undef CTX_EDGE static inline void ctx_rasterizer_reset (CtxRasterizer *rasterizer) { - rasterizer->has_shape = + ctx_rasterizer_reset_soft (rasterizer); + rasterizer->first_edge = -1; rasterizer->has_prev = rasterizer->edge_list.count = // ready for new edges +#if CTX_SCANBIN==0 rasterizer->edge_pos = +#endif + rasterizer->shadow_edge_pos = rasterizer->scanline = 0; if (CTX_LIKELY(!rasterizer->preserve)) { @@ -20644,16 +21956,69 @@ ctx_rasterizer_reset (CtxRasterizer *rasterizer) // nonchanging } +#if CTX_SCANBIN==0 +static CTX_INLINE int ctx_compare_edge (const void *ap, int by0) +{ + return ((const CtxSegment *) ap)->y0 - by0; +} + +static CTX_INLINE int ctx_edge_qsort_partition (CtxSegment *A, int low, int high) +{ + int pivot_y0 = A[ (high+low) /2].y0; + int i = low; + int j = high; + while (i <= j) + { + while (ctx_compare_edge (&A[i], pivot_y0) < 0) { i ++; } + while (ctx_compare_edge (&A[j], pivot_y0) > 0) { j --; } + if (i <= j) + { + CtxSegment tmp = A[i]; + A[i++] = A[j]; + A[j--] = tmp; + } + } + return i; +} + +static void ctx_edge_qsortb (CtxSegment *entries, int low, int high) +{ + do { + int p = ctx_edge_qsort_partition (entries, low, high); + if (low < p - 1) + ctx_edge_qsortb (entries, low, p - 1); + if (low >= high) + return; + low = p; + } while (1); +} + +static CTX_INLINE void ctx_edge_qsort (CtxSegment *entries, int low, int high) +{ + do { + int p = ctx_edge_qsort_partition (entries, low, high); + if (low < p - 1) + ctx_edge_qsortb (entries, low, p - 1); + if (low >= high) + return; + low = p; + } while (1); +} +static CTX_INLINE void ctx_sort_edges (CtxRasterizer *rasterizer) +{ + ctx_edge_qsort ((CtxSegment*)& (rasterizer->edge_list.entries[0]), 0, rasterizer->edge_list.count-1); +} +#endif static void -ctx_rasterizer_rasterize_edges2 (CtxRasterizer *rasterizer, const int fill_rule) +ctx_rasterizer_rasterize_edges2 (CtxRasterizer *rasterizer, const int fill_rule, const int allow_direct) { rasterizer->pending_edges = rasterizer->active_edges = 0; - int is_winding = fill_rule == CTX_FILL_RULE_WINDING; + CtxGState *gstate = &rasterizer->state->gstate; + const int is_winding = fill_rule == CTX_FILL_RULE_WINDING; const CtxCovPath comp = rasterizer->comp; - const int real_aa = rasterizer->aa; uint8_t *dst = ((uint8_t *) rasterizer->buf); int scan_start = rasterizer->blit_y * CTX_FULL_AA; int scan_end = scan_start + (rasterizer->blit_height - 1) * CTX_FULL_AA; @@ -20662,200 +22027,263 @@ ctx_rasterizer_rasterize_edges2 (CtxRasterizer *rasterizer, const int fill_rule) 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 bpp = rasterizer->format->bpp; const int blit_stride = rasterizer->blit_stride; - ctx_apply_coverage_fun apply_coverage = rasterizer->apply_coverage; uint8_t *rasterizer_src = rasterizer->color; if (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 (gstate->clip_min_x, minx); + maxx = ctx_mini (gstate->clip_max_x, maxx); minx *= (minx>0); - int pixs = maxx - minx + 1; - uint8_t _coverage[pixs]; + uint8_t _coverage[pixs+16]; // XXX this might hide some valid asan warnings uint8_t *coverage = &_coverage[0]; + ctx_apply_coverage_fun apply_coverage = rasterizer->apply_coverage; rasterizer->scan_min -= (rasterizer->scan_min % CTX_FULL_AA); { if (rasterizer->scan_min > scan_start) { - dst += (rasterizer->blit_stride * (rasterizer->scan_min-scan_start) / CTX_FULL_AA); + dst += (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 )) + if (CTX_UNLIKELY(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; + dst += (blit_stride * (gstate->clip_min_y * CTX_FULL_AA -scan_start) / CTX_FULL_AA); + scan_start = 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((minx >= maxx) || (scan_start > scan_end) || - (scan_start > (rasterizer->blit_y + (rasterizer->blit_height-1)) * CTX_FULL_AA) || + scan_end = ctx_mini (gstate->clip_max_y * CTX_FULL_AA, scan_end); + if (CTX_UNLIKELY((minx >= maxx) | (scan_start > scan_end) | + (scan_start > (rasterizer->blit_y + (rasterizer->blit_height-1)) * CTX_FULL_AA) | (scan_end < (rasterizer->blit_y) * CTX_FULL_AA))) { /* not affecting this rasterizers scanlines */ return; } + rasterizer->scan_aa[1]= + rasterizer->scan_aa[2]= + rasterizer->scan_aa[3]=0; - ctx_edge_qsort ((CtxSegment*)& (rasterizer->edge_list.entries[0]), 0, rasterizer->edge_list.count-1); - rasterizer->scanline = scan_start; +#if CTX_SCANBIN + int ss = scan_start/CTX_FULL_AA; + int se = scan_end/CTX_FULL_AA; + if (ss < 0)ss =0; + if (se >= CTX_MAX_SCANLINES) se = CTX_MAX_SCANLINES-1; - int allow_direct = !(0 -#if CTX_ENABLE_CLIP - || (rasterizer->clip_buffer && (!rasterizer->clip_rectangle)) -#endif -#if CTX_ENABLE_SHADOW_BLUR - || rasterizer->in_shadow + for (int i = ss; i < se; i++) + rasterizer->scan_bin_count[i]=0; + + for (unsigned int i = 0; i < rasterizer->edge_list.count; i++) + { + CtxSegment *segment = & ((CtxSegment*)rasterizer->edge_list.entries)[i]; + int scan = (segment->y0-CTX_FULL_AA+2) / CTX_FULL_AA; + if (scan < ss) scan = ss; + if (scan < se) + rasterizer->scan_bins[scan][rasterizer->scan_bin_count[scan]++]=i; + } +#else + ctx_sort_edges (rasterizer); #endif - ); - for (; rasterizer->scanline <= scan_end;) - { - int aa = ctx_rasterizer_feed_edges_full (rasterizer); + rasterizer->scanline = scan_start; + + while (rasterizer->scanline <= scan_end) + { + int c0 = minx; + int c1 = maxx; + int aa = ctx_rasterizer_feed_edges_full (rasterizer, 0, 0.0f); switch (aa) { - case -1: - rasterizer->scanline += CTX_FULL_AA; - dst += blit_stride; - continue; - case 0: - { /* the scanline transitions does not contain multiple intersections - each aa segment is a linear ramp */ - ctx_rasterizer_increment_edges (rasterizer, CTX_AA_HALFSTEP2); - ctx_rasterizer_feed_edges (rasterizer); - ctx_edge2_insertion_sort ((CtxSegment*)rasterizer->edge_list.entries, rasterizer->edges, rasterizer->active_edges); + case -1: /* no edges */ + rasterizer->scanline += CTX_FULL_AA; + dst += blit_stride; + continue; + case 0: /* the scanline transitions does not contain multiple intersections - each aa segment is a linear ramp */ + case 1: /* level-1 aa is good enough - use same case for less iteration of edges */ + { + rasterizer->scanline += CTX_AA_HALFSTEP2; + ctx_rasterizer_feed_pending_edges (rasterizer); + ctx_rasterizer_sort_active_edges (rasterizer); memset (coverage, 0, pixs); if (allow_direct) { - ctx_rasterizer_generate_coverage_apply2 (rasterizer, minx, maxx, coverage, is_winding, comp, apply_coverage); - ctx_rasterizer_increment_edges (rasterizer, CTX_AA_HALFSTEP); + ctx_rasterizer_apply_grads (rasterizer, minx, maxx, coverage, is_winding, comp, apply_coverage); + rasterizer->scanline += CTX_AA_HALFSTEP; + ctx_rasterizer_increment_edges (rasterizer, CTX_FULL_AA); dst += blit_stride; continue; } - ctx_rasterizer_generate_coverage_set2 (rasterizer, minx, maxx, coverage, is_winding); - ctx_rasterizer_increment_edges (rasterizer, CTX_AA_HALFSTEP); + ctx_rasterizer_generate_coverage_grads (rasterizer, minx, maxx, coverage, is_winding, &c0, &c1); + rasterizer->scanline += CTX_AA_HALFSTEP; + ctx_rasterizer_increment_edges (rasterizer, CTX_FULL_AA); break; } - case 1: - { - ctx_rasterizer_increment_edges (rasterizer, CTX_AA_HALFSTEP2); - ctx_rasterizer_feed_edges (rasterizer); - ctx_edge2_insertion_sort ((CtxSegment*)rasterizer->edge_list.entries, rasterizer->edges, rasterizer->active_edges); - - if (allow_direct) - { /* can generate with direct rendering to target (we're not using shape cache) */ - - ctx_rasterizer_generate_coverage_apply (rasterizer, minx, maxx, is_winding, comp, apply_coverage); - ctx_rasterizer_increment_edges (rasterizer, CTX_AA_HALFSTEP); - - dst += blit_stride; - continue; +#if 1 + case 3: + { /* level of oversampling based on lowest steepness edges */ + const int raa=3; + ctx_rasterizer_increment_edges (rasterizer, -CTX_AA_HALFSTEP2); + memset (coverage, 0, pixs); + const int scanline_increment = 15/raa; + const uint8_t fraction = 255/raa; + + c0 = maxx; + c1 = minx; + for (int i = 1; i <= raa; i++) + { + ctx_rasterizer_sort_active_edges (rasterizer); + ctx_rasterizer_generate_coverage (rasterizer, minx, maxx, coverage, is_winding, raa, fraction, &c0, &c1); + rasterizer->scanline += scanline_increment; + ctx_rasterizer_increment_edges (rasterizer, scanline_increment + CTX_AA_HALFSTEP2 * (i==raa)); + ctx_rasterizer_feed_pending_edges (rasterizer); } - else - { /* cheap fully correct AA, to coverage mask / clipping */ - memset (coverage, 0, pixs); - - ctx_rasterizer_generate_coverage_set (rasterizer, minx, maxx, coverage, is_winding); - ctx_rasterizer_increment_edges (rasterizer, CTX_AA_HALFSTEP); + } + break; + case 5: + { /* level of oversampling based on lowest steepness edges */ + const int raa=5; + ctx_rasterizer_increment_edges (rasterizer, -CTX_AA_HALFSTEP2); + memset (coverage, 0, pixs); + const int scanline_increment = 15/raa; + const uint8_t fraction = 255/raa; + + c0 = maxx; + c1 = minx; + for (int i = 1; i <= raa; i++) + { + ctx_rasterizer_sort_active_edges (rasterizer); + ctx_rasterizer_generate_coverage (rasterizer, minx, maxx, coverage, is_winding, raa, fraction, &c0, &c1); + rasterizer->scanline += scanline_increment; + ctx_rasterizer_increment_edges (rasterizer, scanline_increment + CTX_AA_HALFSTEP2 * (i==raa)); + ctx_rasterizer_feed_pending_edges (rasterizer); + } + } + break; + case 15: + { /* level of oversampling based on lowest steepness edges */ + const int raa=15; + ctx_rasterizer_increment_edges (rasterizer, -CTX_AA_HALFSTEP2); + memset (coverage, 0, pixs); + const int scanline_increment = 15/raa; + const uint8_t fraction = 255/raa; + + c0 = maxx; + c1 = minx; + for (int i = 1; i <= raa; i++) + { + ctx_rasterizer_sort_active_edges (rasterizer); + ctx_rasterizer_generate_coverage (rasterizer, minx, maxx, coverage, is_winding, raa, fraction, &c0, &c1); + rasterizer->scanline += scanline_increment; + ctx_rasterizer_increment_edges (rasterizer, scanline_increment + CTX_AA_HALFSTEP2 * (i==raa)); + ctx_rasterizer_feed_pending_edges (rasterizer); } - break; } + break; +#else default: { /* level of oversampling based on lowest steepness edges */ - if (aa > real_aa) aa = real_aa; - int scanline_increment = 15/aa; + const int raa=aa; + ctx_rasterizer_increment_edges (rasterizer, -CTX_AA_HALFSTEP2); memset (coverage, 0, pixs); - uint8_t fraction = 255/aa; - for (int i = 0; i < CTX_FULL_AA; i+= scanline_increment) + const int scanline_increment = 15/raa; + const uint8_t fraction = 255/raa; + + c0 = maxx; + c1 = minx; + for (int i = 1; i <= raa; i++) { - if (i) ctx_rasterizer_feed_edges (rasterizer); - ctx_edge2_insertion_sort ((CtxSegment*)rasterizer->edge_list.entries, rasterizer->edges, rasterizer->active_edges); - ctx_rasterizer_generate_coverage (rasterizer, minx, maxx, coverage, is_winding, aa, fraction); - ctx_rasterizer_increment_edges (rasterizer, scanline_increment); + ctx_rasterizer_sort_active_edges (rasterizer); + ctx_rasterizer_generate_coverage (rasterizer, minx, maxx, coverage, is_winding, raa, fraction, &c0, &c1); + rasterizer->scanline += scanline_increment; + ctx_rasterizer_increment_edges (rasterizer, scanline_increment + CTX_AA_HALFSTEP2 * (i==raa)); + ctx_rasterizer_feed_pending_edges (rasterizer); } } +#endif } - ctx_coverage_post_process (rasterizer, minx, maxx, coverage - minx, NULL, NULL); - apply_coverage (rasterizer, - &dst[(minx * rasterizer->format->bpp) /8], - rasterizer_src, - minx, - coverage, - pixs); + if (c1 >= c0) + { + ctx_coverage_post_process (rasterizer, c0, c1, coverage - minx, NULL, NULL); + apply_coverage (c1-c0+1, + &dst[(c0 * bpp) /8], + rasterizer_src, + coverage + (c0-minx), + rasterizer, c0); + } dst += blit_stride; } #if CTX_BLENDING_AND_COMPOSITING - 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)) + if (CTX_UNLIKELY((gstate->compositing_mode == CTX_COMPOSITE_SOURCE_OUT) | + (gstate->compositing_mode == CTX_COMPOSITE_SOURCE_IN) | + (gstate->compositing_mode == CTX_COMPOSITE_DESTINATION_IN) | + (gstate->compositing_mode == CTX_COMPOSITE_DESTINATION_ATOP) | + (gstate->compositing_mode == CTX_COMPOSITE_CLEAR))) { /* 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_end = rasterizer->state->gstate.clip_max_y * CTX_FULL_AA; + int gscan_start = gstate->clip_min_y * CTX_FULL_AA; + //int gscan_end = 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 startx = gstate->clip_min_x; + int endx = gstate->clip_max_x; int clipw = endx-startx + 1; uint8_t *dst = ( (uint8_t *) rasterizer->buf); - dst = (uint8_t*)(rasterizer->buf) + rasterizer->blit_stride * (gscan_start / CTX_FULL_AA); + dst = (uint8_t*)(rasterizer->buf) + blit_stride * (gscan_start / CTX_FULL_AA); for (rasterizer->scanline = gscan_start; rasterizer->scanline < scan_start;) { - apply_coverage (rasterizer, + apply_coverage (clipw, &dst[ (startx * rasterizer->format->bpp) /8], - rasterizer_src, 0, nocoverage, clipw); + rasterizer_src, nocoverage, rasterizer, 0); rasterizer->scanline += CTX_FULL_AA; - dst += rasterizer->blit_stride; + dst += blit_stride; } - if (minx < startx) + + if (0)//(minx > startx) & (minxbuf) + rasterizer->blit_stride * (scan_start / CTX_FULL_AA); + dst = (uint8_t*)(rasterizer->buf) + blit_stride * (scan_start / CTX_FULL_AA); for (rasterizer->scanline = scan_start; rasterizer->scanline < scan_end;) { - apply_coverage (rasterizer, + apply_coverage (minx-startx, &dst[ (startx * rasterizer->format->bpp) /8], rasterizer_src, - 0, - nocoverage, minx-startx); + nocoverage, rasterizer, 0); dst += blit_stride; } } if (endx > maxx) { - dst = (uint8_t*)(rasterizer->buf) + rasterizer->blit_stride * (scan_start / CTX_FULL_AA); + dst = (uint8_t*)(rasterizer->buf) + blit_stride * (scan_start / CTX_FULL_AA); for (rasterizer->scanline = scan_start; rasterizer->scanline < scan_end;) { - apply_coverage (rasterizer, + apply_coverage (endx-maxx, &dst[ (maxx * rasterizer->format->bpp) /8], - rasterizer_src, 0, nocoverage, endx-maxx); + rasterizer_src, nocoverage, rasterizer, 0); rasterizer->scanline += CTX_FULL_AA; - dst += rasterizer->blit_stride; + dst += blit_stride; } } -#if 1 - dst = (uint8_t*)(rasterizer->buf) + rasterizer->blit_stride * (scan_end / CTX_FULL_AA); - // XXX this crashes under valgrind/asan - if(0)for (rasterizer->scanline = scan_end; rasterizer->scanline/CTX_FULL_AA < gscan_end-1;) +#if 0 + dst = (uint8_t*)(rasterizer->buf) + blit_stride * (scan_end / CTX_FULL_AA); + for (rasterizer->scanline = scan_end; rasterizer->scanline < gscan_end;) { - apply_coverage (rasterizer, + apply_coverage (clipw-1, &dst[ (startx * rasterizer->format->bpp) /8], rasterizer_src, - 0, - nocoverage, clipw-1); + nocoverage, rasterizer, 0); rasterizer->scanline += CTX_FULL_AA; dst += blit_stride; @@ -20865,6 +22293,121 @@ ctx_rasterizer_rasterize_edges2 (CtxRasterizer *rasterizer, const int fill_rule) #endif } +#if CTX_ENABLE_SHADOW_BLUR +static void +ctx_rasterizer_rasterize_edges3 (CtxRasterizer *rasterizer, const int fill_rule) +{ + rasterizer->pending_edges = + rasterizer->active_edges = 0; + rasterizer->shadow_active_edges = 0; + CtxGState *gstate = &rasterizer->state->gstate; + float blur_radius = rasterizer->feather; + const int is_winding = fill_rule == CTX_FILL_RULE_WINDING; + uint8_t *dst = ((uint8_t *) rasterizer->buf); + + int scan_start = rasterizer->blit_y * CTX_FULL_AA; + int scan_end = scan_start + (rasterizer->blit_height - 1) * 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 bpp = rasterizer->format->bpp; + const int blit_stride = rasterizer->blit_stride; + + uint8_t *rasterizer_src = rasterizer->color; + + if (maxx > blit_max_x - 1) + { maxx = blit_max_x - 1; } + + minx = ctx_maxi (gstate->clip_min_x, minx); + maxx = ctx_mini (gstate->clip_max_x, maxx); + minx *= (minx>0); + + int pixs = maxx - minx + 1; + uint8_t _coverage[pixs+16]; // XXX this might hide some valid asan warnings + uint8_t *coverage = &_coverage[0]; + ctx_apply_coverage_fun apply_coverage = rasterizer->apply_coverage; + + rasterizer->scan_min -= (rasterizer->scan_min % CTX_FULL_AA); + { + if (rasterizer->scan_min > scan_start) + { + dst += (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(gstate->clip_min_y * CTX_FULL_AA > scan_start )) + { + dst += (blit_stride * (gstate->clip_min_y * CTX_FULL_AA -scan_start) / CTX_FULL_AA); + scan_start = gstate->clip_min_y * CTX_FULL_AA; + } + scan_end = ctx_mini (gstate->clip_max_y * CTX_FULL_AA, scan_end); + if (CTX_UNLIKELY((minx >= maxx) | (scan_start > scan_end) | + (scan_start > (rasterizer->blit_y + (rasterizer->blit_height-1)) * CTX_FULL_AA) | + (scan_end < (rasterizer->blit_y) * CTX_FULL_AA))) + { + /* not affecting this rasterizers scanlines */ + return; + } + rasterizer->scan_aa[1]= + rasterizer->scan_aa[2]= + rasterizer->scan_aa[3]=0; + +#if CTX_SCANBIN + int ss = scan_start/CTX_FULL_AA; + int se = scan_end/CTX_FULL_AA; + if (ss < 0)ss =0; + if (se >= CTX_MAX_SCANLINES) se = CTX_MAX_SCANLINES-1; + + for (int i = ss; i < se; i++) + rasterizer->scan_bin_count[i]=0; + + for (unsigned int i = 0; i < rasterizer->edge_list.count; i++) + { + CtxSegment *segment = & ((CtxSegment*)rasterizer->edge_list.entries)[i]; + int scan = (segment->y0-CTX_FULL_AA+2) / CTX_FULL_AA; + if (scan < ss) scan = ss; + if (scan < se) + rasterizer->scan_bins[scan][rasterizer->scan_bin_count[scan]++]=i; + } +#else + ctx_sort_edges (rasterizer); +#endif + + rasterizer->scanline = scan_start; + + while (rasterizer->scanline <= scan_end) + { + int c0 = minx; + int c1 = maxx; + ctx_rasterizer_feed_edges_full (rasterizer, 1, blur_radius); + { + rasterizer->scanline += CTX_AA_HALFSTEP2; + ctx_rasterizer_feed_pending_edges (rasterizer); + + memset (coverage, 0, pixs); + ctx_rasterizer_sort_active_edges (rasterizer); + ctx_rasterizer_generate_sdf (rasterizer, minx, maxx, coverage, is_winding, blur_radius); + rasterizer->scanline += CTX_AA_HALFSTEP; + ctx_rasterizer_increment_edges (rasterizer, CTX_FULL_AA); + } + + { + ctx_coverage_post_process (rasterizer, c0, c1, coverage - minx, NULL, NULL); + apply_coverage (c1-c0+1, + &dst[(c0 * bpp) /8], + rasterizer_src, + coverage + (c0-minx), + rasterizer, c0); + } + dst += blit_stride; + } +} +#endif + #if CTX_INLINE_FILL_RULE void @@ -20877,31 +22420,71 @@ CTX_SIMD_SUFFIX (ctx_rasterizer_rasterize_edges) (CtxRasterizer *rasterizer, con #if CTX_INLINE_FILL_RULE + +// this can shave 1-2% percent off execution time, at the penalty of increased code size void CTX_SIMD_SUFFIX (ctx_rasterizer_rasterize_edges) (CtxRasterizer *rasterizer, const int fill_rule) { - if (fill_rule) - { - ctx_rasterizer_rasterize_edges2 (rasterizer, 1); - } - else - { - ctx_rasterizer_rasterize_edges2 (rasterizer, 0); - } +#if CTX_RASTERIZER_ALLOW_DIRECT + int allow_direct = !(0 +#if CTX_ENABLE_CLIP + | ((rasterizer->clip_buffer!=NULL) & (!rasterizer->clip_rectangle)) +#endif +#if CTX_ENABLE_SHADOW_BLUR + | rasterizer->in_shadow +#endif + ); +#else + const int allow_direct = 0; // temporarily disabled + // we seem to overrrun our scans +#endif + +#if CTX_ENABLE_SHADOW_BLUR + if (rasterizer->in_shadow) + { + if (fill_rule) ctx_rasterizer_rasterize_edges3 (rasterizer, 1); + else ctx_rasterizer_rasterize_edges3 (rasterizer, 0); + return; + } +#endif + +#if 1 + if (allow_direct) + { + if (fill_rule) ctx_rasterizer_rasterize_edges2 (rasterizer, 1, 1); + else ctx_rasterizer_rasterize_edges2 (rasterizer, 0, 1); + } + else + { + if (fill_rule) ctx_rasterizer_rasterize_edges2 (rasterizer, 1, 0); + else ctx_rasterizer_rasterize_edges2 (rasterizer, 0, 0); + } +#else +#endif } #else void CTX_SIMD_SUFFIX (ctx_rasterizer_rasterize_edges) (CtxRasterizer *rasterizer, const int fill_rule) { - ctx_rasterizer_rasterize_edges2 (rasterizer, fill_rule); + int allow_direct = !(0 +#if CTX_ENABLE_CLIP + | ((rasterizer->clip_buffer!=NULL) & (!rasterizer->clip_rectangle)) +#endif + ); +#if CTX_ENABLE_SHADOW_BLUR + if (rasterizer->in_shadow) + ctx_rasterizer_rasterize_edges3 (rasterizer, fill_rule); + else +#endif + ctx_rasterizer_rasterize_edges2 (rasterizer, fill_rule, allow_direct); } #endif -extern CtxPixelFormatInfo *ctx_pixel_formats; +extern const CtxPixelFormatInfo *ctx_pixel_formats; void CTX_SIMD_SUFFIX(ctx_simd_setup)(void); void CTX_SIMD_SUFFIX(ctx_simd_setup)(void) { @@ -20910,10 +22493,10 @@ void CTX_SIMD_SUFFIX(ctx_simd_setup)(void) ctx_rasterizer_rasterize_edges = CTX_SIMD_SUFFIX(ctx_rasterizer_rasterize_edges); #if CTX_FAST_FILL_RECT ctx_composite_fill_rect = CTX_SIMD_SUFFIX(ctx_composite_fill_rect); -#endif #if CTX_FAST_STROKE_RECT ctx_composite_stroke_rect = CTX_SIMD_SUFFIX(ctx_composite_stroke_rect); #endif +#endif } @@ -20922,18 +22505,44 @@ void CTX_SIMD_SUFFIX(ctx_simd_setup)(void) #if CTX_IMPLEMENTATION #if CTX_RASTERIZER +#if CTX_ENABLE_RGB565 -inline static float ctx_fast_hypotf (float x, float y) +void +ctx_RGBA8_to_RGB565_BS (CtxRasterizer *rasterizer, int x, const uint8_t *rgba, void *buf, int count) { - if (x < 0) { x = -x; } - if (y < 0) { y = -y; } - if (x < y) - { return 0.96f * y + 0.4f * x; } - else - { return 0.96f * x + 0.4f * y; } + 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; + } } +void +ctx_RGB565_BS_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, 1); +#if CTX_RGB565_ALPHA + if ((rgba[0]==255) & (rgba[2] == 255) & (rgba[1]==0)) + { rgba[3] = 0; } + else + { rgba[3] = 255; } +#endif + pixel+=1; + rgba +=4; + } +} +#endif static void ctx_rasterizer_gradient_add_stop (CtxRasterizer *rasterizer, float pos, float *rgba) @@ -20960,43 +22569,92 @@ static inline void ctx_rasterizer_update_inner_point (CtxRasterizer *rasterizer, rasterizer->inner_y = y; } -static inline int ctx_rasterizer_add_point (CtxRasterizer *rasterizer, int x1, int y1) +static CTX_INLINE int ctx_rasterizer_add_point (CtxRasterizer *rasterizer, int x1, int y1) { - CtxSegment entry = {CTX_EDGE, {{0,}},0,0}; + CtxSegment entry = {{CTX_EDGE, 0, 0, 0, 0, 0}}; + x1 -= rasterizer->blit_x * CTX_SUBDIV; - entry.data.s16[0]=rasterizer->inner_x; - entry.data.s16[1]=rasterizer->inner_y; + entry.x0=rasterizer->inner_x; + entry.y0=rasterizer->inner_y; - entry.data.s16[2]=x1; - entry.data.s16[3]=y1; + entry.x1=x1; + entry.y1=y1; ctx_rasterizer_update_inner_point (rasterizer, x1, y1); - return ctx_edgelist_add_single (&rasterizer->edge_list, (CtxEntry*)&entry); + int ret = ctx_edgelist_add_single (&rasterizer->edge_list, (CtxEntry*)&entry); + if (CTX_UNLIKELY(rasterizer->has_prev<=0)) + { + CtxSegment *segment = & ((CtxSegment*)rasterizer->edge_list.entries)[rasterizer->edge_list.count-1]; + segment->code = CTX_NEW_EDGE; + rasterizer->has_prev = 1; + rasterizer->first_edge = rasterizer->edge_list.count-1; + } + return ret; } -static void ctx_rasterizer_poly_to_edges (CtxRasterizer *rasterizer) +static inline void ctx_rasterizer_poly_to_edges (CtxRasterizer *rasterizer) { unsigned int count = rasterizer->edge_list.count; - CtxSegment *entry = (CtxSegment*)&rasterizer->edge_list.entries[0]; + CtxSegment *segment = (CtxSegment*)&rasterizer->edge_list.entries[0]; + int skipped = 0; for (unsigned int i = 0; i < count; i++) { - if (entry->data.s16[3] < entry->data.s16[1]) + if (segment[skipped].code == CTX_CLOSE_EDGE) + skipped ++; + else + { + if (segment[skipped].y1 < segment[skipped].y0) { - *entry = ctx_segment_s16 (CTX_EDGE_FLIPPED, - entry->data.s16[2], entry->data.s16[3], - entry->data.s16[0], entry->data.s16[1]); + segment[0] = ctx_segment_s16 (CTX_EDGE_FLIPPED, + segment[skipped].x1, segment[skipped].y1, + segment[skipped].x0, segment[skipped].y0); } - entry++; + else + { + segment[0] = segment[skipped]; + } + segment++; + } } + rasterizer->edge_list.count = count - skipped; } -static inline void ctx_rasterizer_finish_shape (CtxRasterizer *rasterizer) +static inline void +ctx_rasterizer_close_path (CtxRasterizer *rasterizer) { - if (rasterizer->has_shape && rasterizer->has_prev) + int x0 = rasterizer->inner_x; + int y0 = rasterizer->inner_y; + if (rasterizer->first_edge>=0) { - ctx_rasterizer_line_to (rasterizer, rasterizer->first_x, rasterizer->first_y); - rasterizer->has_prev = 0; + CtxSegment *segment = & ((CtxSegment*)rasterizer->edge_list.entries)[rasterizer->first_edge]; + if (segment->code == CTX_NEW_EDGE) + { + CtxSegment entry = {{CTX_EDGE, 0, 0, 0, 0, 0}}; + int x1 = segment->x0; + int y1 = segment->y0; + entry.x0=x0; + entry.y0=y0; + entry.x1=x1; + entry.y1=y1; + // XXX + rasterizer->has_prev = 0; + rasterizer->first_edge = -1; + ctx_edgelist_add_single (&rasterizer->edge_list, (CtxEntry*)&entry); + entry = *segment; + entry.code = CTX_CLOSE_EDGE; + + ctx_edgelist_add_single (&rasterizer->edge_list, (CtxEntry*)&entry); + + ctx_rasterizer_update_inner_point (rasterizer, x1, y1); + + float nx = x1 * (1.0f / CTX_SUBDIV); + float ny = y1 * (1.0f / CTX_FULL_AA); + ctx_device_to_user(rasterizer->backend.ctx, &nx, &ny); + rasterizer->x = nx; + rasterizer->y = ny; + return; + } } } @@ -21011,8 +22669,7 @@ static inline void ctx_rasterizer_move_to (CtxRasterizer *rasterizer, float x, f rasterizer->x = x; rasterizer->y = y; - rasterizer->first_x = x; - rasterizer->first_y = y; + rasterizer->first_edge = rasterizer->edge_list.count - 1; // ? rasterizer->has_prev = -1; _ctx_user_to_device_prepped (rasterizer->state, x,y, &tx, &ty); @@ -21020,50 +22677,23 @@ static inline void ctx_rasterizer_move_to (CtxRasterizer *rasterizer, float x, f ctx_rasterizer_update_inner_point (rasterizer, tx, ty); } -static inline void +static CTX_INLINE void ctx_rasterizer_line_to_fixed (CtxRasterizer *rasterizer, int x, int y) { - rasterizer->has_shape = 1; - // XXX we avoid doing this for the cases where we initially have fixed point - // need a separate call for doing this at end of beziers and arcs - //if (done) - //{ - // rasterizer->y = y*1.0/CTX_FULL_AA; - // rasterizer->x = x*1.0/CTX_SUBDIV; - //} int tx = 0, ty = 0; _ctx_user_to_device_prepped_fixed (rasterizer->state, x, y, &tx, &ty); - tx -= rasterizer->blit_x * CTX_SUBDIV; - ctx_rasterizer_add_point (rasterizer, tx, ty); - - if (CTX_UNLIKELY(rasterizer->has_prev<=0)) - { - CtxSegment *entry = & ((CtxSegment*)rasterizer->edge_list.entries)[rasterizer->edge_list.count-1]; - entry->code = CTX_NEW_EDGE; - rasterizer->has_prev = 1; - } } -static inline void +static CTX_INLINE void ctx_rasterizer_line_to (CtxRasterizer *rasterizer, float x, float y) { int tx = 0, ty = 0; - rasterizer->has_shape = 1; rasterizer->y = y; rasterizer->x = x; _ctx_user_to_device_prepped (rasterizer->state, x, y, &tx, &ty); - tx -= rasterizer->blit_x * CTX_SUBDIV; - ctx_rasterizer_add_point (rasterizer, tx, ty); - - if (CTX_UNLIKELY(rasterizer->has_prev<=0)) - { - CtxSegment *entry = & ((CtxSegment*)rasterizer->edge_list.entries)[rasterizer->edge_list.count-1]; - entry->code = CTX_NEW_EDGE; - rasterizer->has_prev = 1; - } } CTX_INLINE static float @@ -21102,13 +22732,12 @@ ctx_rasterizer_bezier_divide (CtxRasterizer *rasterizer, { float t = (s + e) * 0.5f; float x, y; - float lx, ly, dx, dy; + float dx, dy; ctx_bezier_sample (ox, oy, x0, y0, x1, y1, x2, y2, t, &x, &y); - lx = ctx_lerpf (sx, ex, t); - ly = ctx_lerpf (sy, ey, t); - dx = lx - x; - dy = ly - y; - if (iteration < 6 && (dx*dx+dy*dy) > tolerance) + dx = (sx+ex)/2 - x; + dy = (sy+ey)/2 - y; + + if ((iteration<2) | ((iteration < 6) & (dx*dx+dy*dy > tolerance))) { ctx_rasterizer_bezier_divide (rasterizer, ox, oy, x0, y0, x1, y1, x2, y2, sx, sy, x, y, s, t, iteration + 1, @@ -21127,11 +22756,9 @@ ctx_rasterizer_bezier_divide (CtxRasterizer *rasterizer, CTX_INLINE static int ctx_lerp_fixed (int v0, int v1, int dx) { - return v0 + (((v1-v0) * dx) >> CTX_FIX_SHIFT); + return v0 + (((v1-v0) * dx + ((1<> CTX_FIX_SHIFT); } - - CTX_INLINE static int ctx_bezier_sample_1d_fixed (int x0, int x1, int x2, int x3, int dt) { @@ -21142,48 +22769,57 @@ ctx_bezier_sample_1d_fixed (int x0, int x1, int x2, int x3, int dt) ctx_lerp_fixed (x2, x3, dt), dt), dt); } +typedef struct CtxFixedBezier +{ + int x0; int y0; + int x1; int y1; + int x2; int y2; + int x3; int y3; +} CtxFixedBezier; + CTX_INLINE static void -ctx_bezier_sample_fixed (int x0, int y0, - int x1, int y1, - int x2, int y2, - int x3, int y3, +ctx_bezier_sample_fixed (const CtxFixedBezier *b, int dt, int *x, int *y) { - *x = ctx_bezier_sample_1d_fixed (x0, x1, x2, x3, dt); - *y = ctx_bezier_sample_1d_fixed (y0, y1, y2, y3, dt); + *x = ctx_bezier_sample_1d_fixed (b->x0, b->x1, b->x2, b->x3, dt); + *y = ctx_bezier_sample_1d_fixed (b->y0, b->y1, b->y2, b->y3, dt); } static inline void ctx_rasterizer_bezier_divide_fixed (CtxRasterizer *rasterizer, - int ox, int oy, - int x0, int y0, - int x1, int y1, - int x2, int y2, + const CtxFixedBezier *b, int sx, int sy, int ex, int ey, int s, int e, - int iteration, - int tolerance) + int iteration, long int tolerance) { int t = (s + e) / 2; int x, y; - int lx, ly, dx, dy; - ctx_bezier_sample_fixed (ox, oy, x0, y0, x1, y1, x2, y2, t, &x, &y); + + ctx_bezier_sample_fixed (b, t, &x, &y); + + int dx, dy; +#if 1 + dx = (sx+ex)/2 - x; + dy = (sy+ey)/2 - y; +#else + int lx, ly; lx = ctx_lerp_fixed (sx, ex, t); ly = ctx_lerp_fixed (sy, ey, t); dx = lx - x; dy = ly - y; - if (iteration < 6 && ((dx*dx+dy*dy)) > tolerance) +#endif + + if ((iteration < 2) | ((iteration < 6) & (((long)dx*dx+dy*dy) > tolerance))) { - iteration++; - ctx_rasterizer_bezier_divide_fixed (rasterizer, ox, oy, x0, y0, x1, y1, x2, y2, - sx, sy, x, y, s, t, iteration, - tolerance); + ctx_rasterizer_bezier_divide_fixed (rasterizer, b, + sx, sy, x, y, s, t, iteration+1, tolerance + ); ctx_rasterizer_line_to_fixed (rasterizer, x, y); - ctx_rasterizer_bezier_divide_fixed (rasterizer, ox, oy, x0, y0, x1, y1, x2, y2, - x, y, ex, ey, t, e, iteration, - tolerance); + ctx_rasterizer_bezier_divide_fixed (rasterizer, b, + x, y, ex, ey, t, e, iteration+1, tolerance + ); } } @@ -21193,28 +22829,24 @@ ctx_rasterizer_curve_to (CtxRasterizer *rasterizer, float x1, float y1, float x2, float y2) { -#if CTX_32BIT_SEGMENTS - float tolerance = 0.125f/ctx_matrix_get_scale (&rasterizer->state->gstate.transform); -#else - float tolerance = 0.5f/ctx_matrix_get_scale (&rasterizer->state->gstate.transform); -#endif float ox = rasterizer->state->x; float oy = rasterizer->state->y; - tolerance = tolerance * tolerance; -#if 1 - ctx_rasterizer_bezier_divide_fixed (rasterizer, +#if CTX_RASTERIZER_BEZIER_FIXED_POINT + CtxFixedBezier b = { (int)(ox * CTX_FIX_SCALE), (int)(oy * CTX_FIX_SCALE), (int)(x0 * CTX_FIX_SCALE), (int)(y0 * CTX_FIX_SCALE), - (int)(x1 * CTX_FIX_SCALE), (int)(y1 * CTX_FIX_SCALE), (int)(x2 * CTX_FIX_SCALE), (int)(y2 * CTX_FIX_SCALE), + (int)(x1 * CTX_FIX_SCALE), (int)(y1 * CTX_FIX_SCALE), (int)(x2 * CTX_FIX_SCALE), (int)(y2 * CTX_FIX_SCALE) + }; + ctx_rasterizer_bezier_divide_fixed (rasterizer, &b, (int)(ox * CTX_FIX_SCALE), (int)(oy * CTX_FIX_SCALE), (int)(x2 * CTX_FIX_SCALE), (int)(y2 * CTX_FIX_SCALE), - 0, CTX_FIX_SCALE, 0, (int)(tolerance * CTX_FIX_SCALE * CTX_FIX_SCALE)); + 0, CTX_FIX_SCALE, 0, rasterizer->state->gstate.tolerance_fixed); #else ctx_rasterizer_bezier_divide (rasterizer, ox, oy, x0, y0, x1, y1, x2, y2, ox, oy, x2, y2, - 0.0f, 1.0f, 0, tolerance); + 0.0f, 1.0f, 0, rasterizer->state->gstate.tolerance); #endif ctx_rasterizer_line_to (rasterizer, x2, y2); } @@ -21284,11 +22916,6 @@ ctx_rasterizer_set_texture (CtxRasterizer *rasterizer, if (no < 0 || no >= CTX_MAX_TEXTURES) { no = 0; } if (rasterizer->texture_source->texture[no].data == NULL) { -#if CTX_32BIT_SEGMENTS - // not really, but we want to avoid building/linking fprintf - // for firmwares which are likely to not have this set - //fprintf (stderr, "ctx tex fail %p %s %i\n", rasterizer->texture_source, eid, no); -#endif return; } else @@ -21330,11 +22957,13 @@ ctx_rasterizer_define_texture (CtxRasterizer *rasterizer, */ ctx_rasterizer_set_texture (rasterizer, eid, 0.0f, 0.0f); +#if CTX_ENABLE_CM if (!rasterizer->state->gstate.source_fill.texture.buffer->color_managed) { _ctx_texture_prepare_color_management (rasterizer->state, rasterizer->state->gstate.source_fill.texture.buffer); } +#endif _ctx_texture_unlock (); } @@ -21355,13 +22984,16 @@ ctx_is_transparent (CtxRasterizer *rasterizer, int stroke) return 0; } - +static CTX_INLINE int ctx_perpdot(int ax,int ay,int bx, int by) +{ return (ax*by)-(ay*bx); +} static void ctx_rasterizer_fill (CtxRasterizer *rasterizer) { + CtxGState *gstate = &rasterizer->state->gstate; unsigned int preserved_count = - (rasterizer->preserve&&rasterizer->edge_list.count)? + (rasterizer->preserve&(rasterizer->edge_list.count!=0))? rasterizer->edge_list.count:1; int blit_x = rasterizer->blit_x; int blit_y = rasterizer->blit_y; @@ -21372,30 +23004,35 @@ ctx_rasterizer_fill (CtxRasterizer *rasterizer) 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) ); } + int preserved = 0; + if (rasterizer->preserve) + { memcpy (temp, rasterizer->edge_list.entries, sizeof (CtxSegment)*preserved_count ); + preserved = 1; + } #if CTX_ENABLE_SHADOW_BLUR if (CTX_UNLIKELY(rasterizer->in_shadow)) { for (unsigned 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; + CtxSegment *segment = &((CtxSegment*)rasterizer->edge_list.entries)[i]; + segment->x0 += rasterizer->feather_x * CTX_SUBDIV; + segment->y0 += rasterizer->feather_y * CTX_FULL_AA; + segment->x1 += rasterizer->feather_x * CTX_SUBDIV; + segment->y1 += rasterizer->feather_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; + rasterizer->scan_min += ((rasterizer->feather_y - rasterizer->feather) +1) * CTX_FULL_AA; + rasterizer->scan_max += ((rasterizer->feather_y + rasterizer->feather) +1) * CTX_FULL_AA; + rasterizer->col_min += ((rasterizer->feather_x - rasterizer->feather)+ 1) * CTX_SUBDIV; + rasterizer->col_max += ((rasterizer->feather_x + rasterizer->feather)+ 1) * CTX_SUBDIV; } #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)) + 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))) { } else @@ -21417,22 +23054,22 @@ ctx_rasterizer_fill (CtxRasterizer *rasterizer) 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]) + (!(gstate->clipped != 0)) & + (entry0->x1 == entry1->x1) & + (entry0->y1 == entry3->y1) & + (entry1->y1 == entry2->y1) & + (entry2->x1 == entry3->x1) #if CTX_ENABLE_SHADOW_BLUR - && !rasterizer->in_shadow + & (!rasterizer->in_shadow) #endif ) { - 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); + float x0 = entry3->x1 * (1.0f / CTX_SUBDIV); + float y0 = entry3->y1 * (1.0f / CTX_FULL_AA); + float x1 = entry1->x1 * (1.0f / CTX_SUBDIV); + float y1 = entry1->y1 * (1.0f / CTX_FULL_AA); - if (x1 > x0 && y1 > y0) + if ((x1 > x0) & (y1 > y0)) { ctx_composite_fill_rect (rasterizer, x0, y0, x1, y1, 255); goto done; @@ -21441,51 +23078,32 @@ ctx_rasterizer_fill (CtxRasterizer *rasterizer) } #endif - ctx_rasterizer_finish_shape (rasterizer); + ctx_rasterizer_close_path (rasterizer); ctx_rasterizer_poly_to_edges (rasterizer); - { - ctx_rasterizer_rasterize_edges (rasterizer, rasterizer->state->gstate.fill_rule); - } + ctx_rasterizer_rasterize_edges (rasterizer, gstate->fill_rule); } #if CTX_FAST_FILL_RECT done: #endif - if (CTX_UNLIKELY(rasterizer->preserve)) + if (preserved) { - memcpy (rasterizer->edge_list.entries, temp, sizeof (temp) ); + memcpy (rasterizer->edge_list.entries, temp, sizeof (CtxSegment)*preserved_count ); rasterizer->edge_list.count = preserved_count; } #if CTX_ENABLE_SHADOW_BLUR if (CTX_UNLIKELY(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; + rasterizer->scan_min -= ((rasterizer->feather_y - rasterizer->feather) +1) * CTX_FULL_AA; + rasterizer->scan_max -= ((rasterizer->feather_y + rasterizer->feather) +1) * CTX_FULL_AA; + rasterizer->col_min -= ((rasterizer->feather_x - rasterizer->feather)+ 1) * CTX_SUBDIV; + rasterizer->col_max -= ((rasterizer->feather_x + rasterizer->feather)+ 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) -{ - -} -#endif - - typedef struct _CtxTermGlyph CtxTermGlyph; struct _CtxTermGlyph @@ -21506,8 +23124,8 @@ ctx_rasterizer_find_term_glyph (CtxRasterizer *rasterizer, int col, int row) for (CtxList *l = rasterizer->glyphs; l; l=l->next) { glyph = l->data; - if (glyph->col == col && - glyph->row == row) + if ((glyph->col == col) & + (glyph->row == row)) { return glyph; } @@ -21521,7 +23139,7 @@ ctx_rasterizer_find_term_glyph (CtxRasterizer *rasterizer, int col, int row) } #endif -static int _ctx_glyph (Ctx *ctx, uint32_t unichar, int stroke); +static int _ctx_glyph (Ctx *ctx, int glyph_id, int stroke); static void ctx_rasterizer_glyph (CtxRasterizer *rasterizer, uint32_t unichar, int stroke) { @@ -21532,10 +23150,11 @@ ctx_rasterizer_glyph (CtxRasterizer *rasterizer, uint32_t unichar, int stroke) _ctx_user_to_device (rasterizer->state, &tx, &ty); _ctx_user_to_device (rasterizer->state, &tx2, &ty2); - 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; + 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; #if CTX_TERM #if CTX_BRAILLE_TEXT @@ -21553,8 +23172,8 @@ ctx_rasterizer_glyph (CtxRasterizer *rasterizer, uint32_t unichar, int stroke) _ctx_user_to_device_distance (rasterizer->state, &tx, &font_size); } - if (rasterizer->term_glyphs && !stroke && - fabsf (font_size - ch) < 0.5f) + if ((rasterizer->term_glyphs!=0) & (!stroke) & + (fabsf (font_size - ch) < 0.5f)) { float tx = rasterizer->x; float ty = rasterizer->y; @@ -21593,8 +23212,8 @@ ctx_rasterizer_text (CtxRasterizer *rasterizer, const char *string, int stroke) int ch = (int)ctx_term_get_cell_height (rasterizer->backend.ctx); int cw = (int)ctx_term_get_cell_width (rasterizer->backend.ctx); - if (rasterizer->term_glyphs && !stroke && - fabsf (font_size - ch) < 0.5f) + if ((rasterizer->term_glyphs!=0) & (!stroke) & + (fabsf (font_size - ch) < 0.5f)) { float tx = rasterizer->x; float ty = rasterizer->y; @@ -21624,7 +23243,7 @@ _ctx_font (Ctx *ctx, const char *name); static void ctx_rasterizer_set_font (CtxRasterizer *rasterizer, const char *font_name) { - //_ctx_font (rasterizer->backend.ctx, font_name); + _ctx_font (rasterizer->backend.ctx, font_name); } static void @@ -21641,7 +23260,7 @@ ctx_rasterizer_arc (CtxRasterizer *rasterizer, full_segments = (int)(factor * radius * CTX_PI * 2 / 4.0f); if (full_segments > CTX_RASTERIZER_MAX_CIRCLE_SEGMENTS) { full_segments = CTX_RASTERIZER_MAX_CIRCLE_SEGMENTS; } - if (full_segments < 42) full_segments = 42; + if (full_segments < 24) full_segments = 24; float step = CTX_PI*2.0f/full_segments; int steps; @@ -21702,7 +23321,7 @@ ctx_rasterizer_arc (CtxRasterizer *rasterizer, { float xv = x + ctx_cosf (angle) * radius; float yv = y + ctx_sinf (angle) * radius; - if (first && !rasterizer->has_prev) + if (first & (!rasterizer->has_prev)) { ctx_rasterizer_move_to (rasterizer, xv, yv); } else { ctx_rasterizer_line_to (rasterizer, xv, yv); } @@ -21747,8 +23366,8 @@ ctx_rasterizer_rectangle_reverse (CtxRasterizer *rasterizer, static void ctx_rasterizer_pset (CtxRasterizer *rasterizer, int x, int y, uint8_t cov) { - if (x <= 0 || y < 0 || x >= rasterizer->blit_width || - y >= rasterizer->blit_height) + 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, @@ -21758,7 +23377,7 @@ fg_color); int pitch = rasterizer->format->bpp / 8; uint8_t *dst = ( (uint8_t *) rasterizer->buf) + y * blit_stride + x * pitch; - rasterizer->apply_coverage (rasterizer, dst, rasterizer->color, x, &cov, 1); + rasterizer->apply_coverage (1, dst, rasterizer->color, &cov, rasterizer, x); } @@ -21767,16 +23386,15 @@ ctx_rasterizer_stroke_1px_segment (CtxRasterizer *rasterizer, float x0, float y0, float x1, float y1) { - void (*apply_coverage)(CtxRasterizer *r, uint8_t *dst, uint8_t *src, - int x, uint8_t *coverage, unsigned int count) = - rasterizer->apply_coverage; + ctx_apply_coverage_fun apply_coverage = rasterizer->apply_coverage; uint8_t *rasterizer_src = rasterizer->color; int pitch = rasterizer->format->bpp / 8; int blit_stride = rasterizer->blit_stride; - //x1 += 0.5f; + x1 += 0.5f; + x0 += 0.5f; + y1 += 0.5f; - //x0 += 0.5f; y0 += 0.5f; float dxf = (x1 - x0); @@ -21802,8 +23420,8 @@ ctx_rasterizer_stroke_1px_segment (CtxRasterizer *rasterizer, int i = 0; int sblit_height = blit_height << 16; - for (; i < length && x < 0; ++i, ++x, ty += dy); - for (; i < length && x < blit_width && (ty<0 || (ty>=sblit_height+1)) + for (; (i < length) & (x < 0); ++i, ++x, ty += dy); + for (; (i < length) & (x < blit_width) & ((ty<0) | (ty>=sblit_height+1)) ; ++i, ++x, ty += dy); for (; i < length && x < blit_width && (ty<65536 || (ty>=sblit_height)) @@ -21817,16 +23435,16 @@ ctx_rasterizer_stroke_1px_segment (CtxRasterizer *rasterizer, } { - for (; i < length && x < blit_width && (ty>65536 && (ty65536) & (tybuf) + ((ty>>16)-1) * blit_stride + x * pitch; uint8_t ypos = (ty >> 8) & 0xff; uint8_t rcov=255-ypos; - apply_coverage (rasterizer, dst, rasterizer_src, x, &rcov, 1); + apply_coverage (1, dst, rasterizer_src, &rcov, rasterizer, x); dst += blit_stride; - apply_coverage (rasterizer, dst, rasterizer_src, x, &ypos, 1); + apply_coverage (1, dst, rasterizer_src, &ypos, rasterizer, x); } } @@ -21854,11 +23472,11 @@ ctx_rasterizer_stroke_1px_segment (CtxRasterizer *rasterizer, int sblit_width = blit_width << 16; - for (; i < length && y < 0; ++i, ++y, tx += dx); + for (; (i < length) & (y < 0); ++i, ++y, tx += dx); - for (; i < length && y < blit_height && (tx<0 || (tx>=sblit_width+1)) + for (; (i < length) & (y < blit_height) & ((tx<0) | (tx>=sblit_width+1)) ; ++i, ++y, tx += dx); - for (; i < length && y < blit_height && (tx<65536 || (tx>=sblit_width)) + for (; (i < length) & (y < blit_height) & ((tx<65536) | (tx>=sblit_width)) ; ++i, ++y, tx += dx) { int x = tx>>16; @@ -21868,7 +23486,7 @@ ctx_rasterizer_stroke_1px_segment (CtxRasterizer *rasterizer, } { - for (; i < length && y < blit_height && (tx>65536 && (tx65536) & (tx>16; @@ -21876,7 +23494,7 @@ ctx_rasterizer_stroke_1px_segment (CtxRasterizer *rasterizer, + y * blit_stride + (x-1) * pitch; int xpos = (tx >> 8) & 0xff; uint8_t cov[2]={255-xpos, xpos}; - apply_coverage (rasterizer, dst, rasterizer_src, x, cov, 2); + apply_coverage (2, dst, rasterizer_src, cov, rasterizer, x); } } //for (; i <= length; ++i, ++y, tx += dx) @@ -21905,22 +23523,22 @@ ctx_rasterizer_stroke_1px (CtxRasterizer *rasterizer) int i; for (i = start; i < count; i++) { - CtxSegment *entry = &temp[i]; + CtxSegment *segment = &temp[i]; float x, y; - if (entry->code == CTX_NEW_EDGE) + if (segment->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 / CTX_FULL_AA; + prev_x = segment->x0 * 1.0f / CTX_SUBDIV; + prev_y = segment->y0 * 1.0f / CTX_FULL_AA; started = 1; start = i; } - x = entry->data.s16[2] * 1.0f / CTX_SUBDIV; - y = entry->data.s16[3] * 1.0f / CTX_FULL_AA; + x = segment->x1 * 1.0f / CTX_SUBDIV; + y = segment->y1 * 1.0f / CTX_FULL_AA; ctx_rasterizer_stroke_1px_segment (rasterizer, prev_x, prev_y, x, y); prev_x = x; @@ -21935,6 +23553,8 @@ ctx_rasterizer_stroke_1px (CtxRasterizer *rasterizer) #endif +#define CTX_MIN_STROKE_LEN 0.2f + static void ctx_rasterizer_stroke (CtxRasterizer *rasterizer) { @@ -21949,16 +23569,16 @@ ctx_rasterizer_stroke (CtxRasterizer *rasterizer) if (gstate->source_stroke.type != CTX_SOURCE_INHERIT_FILL) { source_backup = gstate->source_fill; - gstate->source_fill = rasterizer->state->gstate.source_stroke; + gstate->source_fill = gstate->source_stroke; } rasterizer->comp_op = NULL; ctx_composite_setup (rasterizer); #if CTX_STROKE_1PX - if (gstate->line_width * factor <= 0.0f && - gstate->line_width * factor > -10.0f && - rasterizer->format->bpp >= 8) + if ((gstate->line_width * factor <= 0.0f) & + (gstate->line_width * factor > -10.0f) & + (rasterizer->format->bpp >= 8)) { ctx_rasterizer_stroke_1px (rasterizer); if (preserved) @@ -21978,8 +23598,8 @@ ctx_rasterizer_stroke (CtxRasterizer *rasterizer) CtxSegment temp[count]; /* copy of already built up path's poly line */ memcpy (temp, rasterizer->edge_list.entries, sizeof (temp) ); -#if 0 #if CTX_FAST_FILL_RECT +#if CTX_FAST_STROKE_RECT if (rasterizer->edge_list.count == 5) { CtxSegment *entry0 = &((CtxSegment*)rasterizer->edge_list.entries)[0]; @@ -21987,20 +23607,20 @@ ctx_rasterizer_stroke (CtxRasterizer *rasterizer) CtxSegment *entry2 = &((CtxSegment*)rasterizer->edge_list.entries)[2]; CtxSegment *entry3 = &((CtxSegment*)rasterizer->edge_list.entries)[3]; - 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 (!rasterizer->state->gstate.clipped & + (entry0->x1 == entry1->x1) & + (entry0->y1 == entry3->y1) & + (entry1->y1 == entry2->y1) & + (entry2->x1 == entry3->x1) #if CTX_ENABLE_SHADOW_BLUR - && !rasterizer->in_shadow + & !rasterizer->in_shadow #endif ) { - 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; + float x0 = entry3->x1 * 1.0f / CTX_SUBDIV; + float y0 = entry3->y1 * 1.0f / CTX_FULL_AA; + float x1 = entry1->x1 * 1.0f / CTX_SUBDIV; + float y1 = entry1->y1 * 1.0f / CTX_FULL_AA; ctx_composite_stroke_rect (rasterizer, x0, y0, x1, y1, line_width); @@ -22012,20 +23632,9 @@ ctx_rasterizer_stroke (CtxRasterizer *rasterizer) { { - if (line_width < 5.0f) - { -#if 1 - factor *= 0.95f; /* 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.95f; -#endif - } ctx_rasterizer_reset (rasterizer); /* then start afresh with our stroked shape */ CtxMatrix transform_backup = gstate->transform; _ctx_matrix_identity (&gstate->transform); - //gstate->transform_type = 0; _ctx_transform_prime (rasterizer->state); float prev_x = 0.0f; float prev_y = 0.0f; @@ -22033,7 +23642,7 @@ ctx_rasterizer_stroke (CtxRasterizer *rasterizer) float half_width_y = half_width_x; if (CTX_UNLIKELY(line_width <= 0.0f)) - { // makes 0 width be 1px in user-space; hairline + { // makes negative width be 1px in user-space; hairline half_width_x = .5f; half_width_y = .5f; } @@ -22045,96 +23654,88 @@ ctx_rasterizer_stroke (CtxRasterizer *rasterizer) int i; for (i = start; i < count; i++) { - CtxSegment *entry = &temp[i]; + CtxSegment *segment= &temp[i]; float x, y; - if (entry->code == CTX_NEW_EDGE) + if (segment->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 / CTX_FULL_AA; + prev_x = segment->x0 * 1.0f / CTX_SUBDIV; + prev_y = segment->y0 * 1.0f / CTX_FULL_AA; started = 1; start = i; } - x = entry->data.s16[2] * 1.0f / CTX_SUBDIV; - y = entry->data.s16[3] * 1.0f/ CTX_FULL_AA; + x = segment->x1 * 1.0f / CTX_SUBDIV; + y = segment->y1 * 1.0f/ CTX_FULL_AA; float dx = x - prev_x; float dy = y - prev_y; - float length = ctx_fast_hypotf (dx, dy); - if (length>0.001f) + float length = ctx_hypotf (dx, dy); + if ((length>CTX_MIN_STROKE_LEN) | (segment->code == CTX_NEW_EDGE)) { float recip_length = 1.0f/length; dx = dx * recip_length * half_width_x; dy = dy * recip_length * half_width_y; - if (CTX_UNLIKELY(entry->code == CTX_NEW_EDGE)) + if (segment->code == CTX_NEW_EDGE) { - ctx_rasterizer_finish_shape (rasterizer); + ctx_rasterizer_close_path (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; + prev_x = x; + prev_y = y; } end = i-1; foo: for (int i = end; i >= start; i--) { - CtxSegment *entry = &temp[i]; + CtxSegment *segment = &temp[i]; float x, y, dx, dy; - x = entry->data.s16[2] * 1.0f / CTX_SUBDIV; - y = entry->data.s16[3] * 1.0f / CTX_FULL_AA; + x = segment->x1 * 1.0f / CTX_SUBDIV; + y = segment->y1 * 1.0f / CTX_FULL_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)) + float length = ctx_hypotf (dx, dy); + if (length>CTX_MIN_STROKE_LEN) { + float recip_length = 1.0f/length; + 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); + // 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; } - prev_x = x; - prev_y = y; - if (CTX_UNLIKELY(entry->code == CTX_NEW_EDGE)) + if (CTX_UNLIKELY(segment->code == CTX_NEW_EDGE)) { - x = entry->data.s16[0] * 1.0f / CTX_SUBDIV; - y = entry->data.s16[1] * 1.0f / CTX_FULL_AA; + x = segment->x0 * 1.0f / CTX_SUBDIV; + y = segment->y0 * 1.0f / CTX_FULL_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)) + length = ctx_hypotf (dx, dy); + if (CTX_LIKELY(length>CTX_MIN_STROKE_LEN)) { + float recip_length = 1.0f/length; 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); + ctx_rasterizer_close_path (rasterizer); switch (gstate->line_cap) { case CTX_CAP_SQUARE: // XXX: incorrect - if rectangles were in @@ -22145,25 +23746,25 @@ ctx_rasterizer_stroke (CtxRasterizer *rasterizer) int has_prev = 0; for (int i = 0; i < count; i++) { - CtxSegment *entry = &temp[i]; - if (CTX_UNLIKELY(entry->code == CTX_NEW_EDGE)) + CtxSegment *segment = &temp[i]; + if (CTX_UNLIKELY(segment->code == CTX_NEW_EDGE)) { if (has_prev) { ctx_rasterizer_rectangle_reverse (rasterizer, x - half_width_x, y - half_width_y, half_width_x, half_width_y); - ctx_rasterizer_finish_shape (rasterizer); + ctx_rasterizer_close_path (rasterizer); } - x = entry->data.s16[0] * 1.0f / CTX_SUBDIV; - y = entry->data.s16[1] * 1.0f / CTX_FULL_AA; + x = segment->x0 * 1.0f / CTX_SUBDIV; + y = segment->y0 * 1.0f / CTX_FULL_AA; ctx_rasterizer_rectangle_reverse (rasterizer, x - half_width_x, y - half_width_y, half_width_x * 2, half_width_y * 2); - ctx_rasterizer_finish_shape (rasterizer); + ctx_rasterizer_close_path (rasterizer); } - x = entry->data.s16[2] * 1.0f / CTX_SUBDIV; - y = entry->data.s16[3] * 1.0f / CTX_FULL_AA; + x = segment->x1 * 1.0f / CTX_SUBDIV; + y = segment->y1 * 1.0f / CTX_FULL_AA; has_prev = 1; } ctx_rasterizer_rectangle_reverse (rasterizer, x - half_width_x, y - half_width_y, half_width_x * 2, half_width_y * 2); - ctx_rasterizer_finish_shape (rasterizer); + ctx_rasterizer_close_path (rasterizer); } break; case CTX_CAP_NONE: /* nothing to do */ @@ -22174,26 +23775,26 @@ ctx_rasterizer_stroke (CtxRasterizer *rasterizer) int has_prev = 0; for (int i = 0; i < count; i++) { - CtxSegment *entry = &temp[i]; - if (CTX_UNLIKELY(entry->code == CTX_NEW_EDGE)) + CtxSegment *segment = &temp[i]; + if (CTX_UNLIKELY(segment->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); + ctx_rasterizer_close_path (rasterizer); } - x = entry->data.s16[0] * 1.0f / CTX_SUBDIV; - y = entry->data.s16[1] * 1.0f / CTX_FULL_AA; + x = segment->x0 * 1.0f / CTX_SUBDIV; + y = segment->y0 * 1.0f / CTX_FULL_AA; ctx_rasterizer_arc (rasterizer, x, y, half_width_x, CTX_PI*2, 0, 1); - ctx_rasterizer_finish_shape (rasterizer); + ctx_rasterizer_close_path (rasterizer); } - x = entry->data.s16[2] * 1.0f / CTX_SUBDIV; - y = entry->data.s16[3] * 1.0f / CTX_FULL_AA; + x = segment->x1 * 1.0f / CTX_SUBDIV; + y = segment->y1 * 1.0f / CTX_FULL_AA; has_prev = 1; } ctx_rasterizer_move_to (rasterizer, x, y); ctx_rasterizer_arc (rasterizer, x, y, half_width_x, CTX_PI*2, 0, 1); - ctx_rasterizer_finish_shape (rasterizer); + ctx_rasterizer_close_path (rasterizer); break; } } @@ -22207,13 +23808,13 @@ ctx_rasterizer_stroke (CtxRasterizer *rasterizer) 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 / CTX_FULL_AA; - if (CTX_UNLIKELY(entry[1].code == CTX_EDGE)) + CtxSegment *segment = &temp[i]; + x = segment->x1 * 1.0f / CTX_SUBDIV; + y = segment->y1 * 1.0f / CTX_FULL_AA; + if (CTX_UNLIKELY(segment[1].code == CTX_EDGE)) { ctx_rasterizer_arc (rasterizer, x, y, half_width_x, CTX_PI*2, 0, 1); - ctx_rasterizer_finish_shape (rasterizer); + ctx_rasterizer_close_path (rasterizer); } } break; @@ -22222,15 +23823,19 @@ ctx_rasterizer_stroke (CtxRasterizer *rasterizer) CtxFillRule rule_backup = gstate->fill_rule; gstate->fill_rule = CTX_FILL_RULE_WINDING; rasterizer->preserve = 0; // so fill isn't tripped + int aa = rasterizer->aa; + rasterizer->aa = 3 + (aa>5)*2; ctx_rasterizer_fill (rasterizer); + rasterizer->aa = aa; gstate->fill_rule = rule_backup; gstate->transform = transform_backup; - //gstate->transform_type = 0; _ctx_transform_prime (rasterizer->state); } } #if CTX_FAST_FILL_RECT -//done: +#if CTX_FAST_STROKE_RECT + done: +#endif #endif if (preserved) { @@ -22252,23 +23857,25 @@ ctx_rasterizer_stroke (CtxRasterizer *rasterizer) static void ctx_rasterizer_clip_reset (CtxRasterizer *rasterizer) { + CtxGState *gstate = &rasterizer->state->gstate; #if CTX_ENABLE_CLIP if (rasterizer->clip_buffer) ctx_buffer_destroy (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; + gstate->clip_min_x = rasterizer->blit_x; + gstate->clip_min_y = rasterizer->blit_y; - 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; + gstate->clip_max_x = rasterizer->blit_x + rasterizer->blit_width - 1; + gstate->clip_max_y = rasterizer->blit_y + rasterizer->blit_height - 1; } static void ctx_rasterizer_clip_apply (CtxRasterizer *rasterizer, CtxSegment *edges) { - unsigned int count = edges[0].data.u32[0]; + unsigned int count = edges[0].u32[0]; + CtxGState *gstate = &rasterizer->state->gstate; int minx = 5000; int miny = 5000; @@ -22283,19 +23890,19 @@ ctx_rasterizer_clip_apply (CtxRasterizer *rasterizer, for (unsigned int i = 0; i < count; i++) { - CtxSegment *entry = &edges[i+1]; + CtxSegment *segment = &edges[i+1]; float x, y; - if (entry->code == CTX_NEW_EDGE) + if (segment->code == CTX_NEW_EDGE) { - prev_x = entry->data.s16[0] / CTX_SUBDIV; - prev_y = entry->data.s16[1] / CTX_FULL_AA; + prev_x = segment->x0 / CTX_SUBDIV; + prev_y = segment->y0 / CTX_FULL_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 / CTX_FULL_AA; + x = segment->x1 * 1.0f / CTX_SUBDIV; + y = segment->y1 * 1.0f / CTX_FULL_AA; if (x < minx) { minx = (int)x; } if (y < miny) { miny = (int)y; } if (x > maxx) { maxx = (int)x; } @@ -22310,17 +23917,15 @@ ctx_rasterizer_clip_apply (CtxRasterizer *rasterizer, #if CTX_ENABLE_CLIP - if ((rasterizer->clip_rectangle==1 - || !rasterizer->clip_buffer) + if (((rasterizer->clip_rectangle==1) | (!rasterizer->clip_buffer)) ) { if (count == 5) { - if (coords[0][0] == coords[1][0] && - coords[0][1] == coords[4][1] && - coords[0][1] == coords[3][1] && - coords[1][1] == coords[2][1] && - coords[3][0] == coords[4][0] + if ((coords[0][0] == coords[1][0]) & + (coords[0][1] == coords[3][1]) & + (coords[1][1] == coords[2][1]) & + (coords[2][0] == coords[3][0]) ) { #if 0 @@ -22328,14 +23933,14 @@ ctx_rasterizer_clip_apply (CtxRasterizer *rasterizer, maxx-minx+1, maxy-miny+1); #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); + gstate->clip_min_x = + ctx_maxi (minx, gstate->clip_min_x); + gstate->clip_min_y = + ctx_maxi (miny, gstate->clip_min_y); + gstate->clip_max_x = + ctx_mini (maxx, gstate->clip_max_x); + gstate->clip_max_y = + ctx_mini (maxy, gstate->clip_max_y); rasterizer->clip_rectangle = 1; @@ -22347,11 +23952,11 @@ ctx_rasterizer_clip_apply (CtxRasterizer *rasterizer, 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; + for (int y = gstate->clip_min_y; + y <= gstate->clip_max_y; y++) - for (int x = rasterizer->state->gstate.clip_min_x; - x <= rasterizer->state->gstate.clip_max_x; + for (int x = gstate->clip_min_x; + x <= gstate->clip_max_x; x++, i++) { ((uint8_t*)(rasterizer->clip_buffer->data))[i] = 255; @@ -22378,10 +23983,10 @@ ctx_rasterizer_clip_apply (CtxRasterizer *rasterizer, } rasterizer->clip_rectangle = 0; - if ((minx == maxx) || (miny == maxy)) // XXX : reset hack + if ((minx == maxx) | (miny == maxy)) // XXX : reset hack { ctx_rasterizer_clip_reset (rasterizer); - return;//goto done; + return; } int we_made_it = 0; @@ -22416,16 +24021,16 @@ ctx_rasterizer_clip_apply (CtxRasterizer *rasterizer, for (unsigned int i = 0; i < count; i++) { - CtxSegment *entry = &edges[i+1]; + CtxSegment *segment = &edges[i+1]; float x, y; - if (entry->code == CTX_NEW_EDGE) + if (segment->code == CTX_NEW_EDGE) { - prev_x = entry->data.s16[0] * 1.0f / CTX_SUBDIV; - prev_y = entry->data.s16[1] * 1.0f / CTX_FULL_AA; + prev_x = segment->x0 * 1.0f / CTX_SUBDIV; + prev_y = segment->y0 * 1.0f / CTX_FULL_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 / CTX_FULL_AA; + x = segment->x1 * 1.0f / CTX_SUBDIV; + y = segment->y1 * 1.0f / CTX_FULL_AA; ctx_line_to (ctx, x, y); } ctx_gray (ctx, 1.0f); @@ -22461,7 +24066,7 @@ ctx_rasterizer_clip_apply (CtxRasterizer *rasterizer, i=0; /* find upper left */ - for (; i < count && maybe_rect && !next_stage; i++) + for (; (i < count) & maybe_rect & (!next_stage); i++) { uint8_t val = (p_data[i] * data[i])/255; data[i] = val; @@ -22481,7 +24086,7 @@ ctx_rasterizer_clip_apply (CtxRasterizer *rasterizer, next_stage = 0; /* figure out with */ - for (; i < count && !next_stage && maybe_rect; i++) + for (; (i < count) & (!next_stage) & maybe_rect; i++) { int x = i % blit_width; int y = i / blit_width; @@ -22509,7 +24114,7 @@ ctx_rasterizer_clip_apply (CtxRasterizer *rasterizer, next_stage = 0; /* body */ - for (; i < count && maybe_rect && !next_stage; i++) + for (; (i < count) & maybe_rect & (!next_stage); i++) { int x = i % blit_width; uint8_t val = (p_data[i] * data[i])/255; @@ -22528,7 +24133,7 @@ ctx_rasterizer_clip_apply (CtxRasterizer *rasterizer, next_stage = 0; /* foot */ - for (; i < count && maybe_rect && !next_stage; i++) + for (; (i < count) & maybe_rect & (!next_stage); i++) { uint8_t val = (p_data[i] * data[i])/255; data[i] = val; @@ -22552,14 +24157,14 @@ ctx_rasterizer_clip_apply (CtxRasterizer *rasterizer, 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); + gstate->clip_min_x = ctx_maxi (minx, + gstate->clip_min_x); + gstate->clip_min_y = ctx_maxi (miny, + gstate->clip_min_y); + gstate->clip_max_x = ctx_mini (maxx, + gstate->clip_max_x); + gstate->clip_max_y = ctx_mini (maxy, + gstate->clip_max_y); } @@ -22573,7 +24178,7 @@ ctx_rasterizer_clip (CtxRasterizer *rasterizer) //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; + temp[0].u32[0] = count; ctx_state_set_blob (rasterizer->state, SQZ_clip, (uint8_t*)temp, sizeof(temp)); } ctx_rasterizer_clip_apply (rasterizer, temp); @@ -22614,7 +24219,7 @@ ctx_rasterizer_rectangle_reverse (CtxRasterizer *rasterizer, ctx_rasterizer_rel_line_to (rasterizer, 0, -height); ctx_rasterizer_rel_line_to (rasterizer, -width, 0); //ctx_rasterizer_rel_line_to (rasterizer, width/2, 0); - ctx_rasterizer_finish_shape (rasterizer); + ctx_rasterizer_close_path (rasterizer); } static void @@ -22628,9 +24233,7 @@ ctx_rasterizer_rectangle (CtxRasterizer *rasterizer, 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_rasterizer_close_path (rasterizer); } static void @@ -22651,40 +24254,11 @@ ctx_rasterizer_set_pixel (CtxRasterizer *rasterizer, // 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_rectangle (rasterizer, x, y, 1.0f, 1.0f); ctx_rasterizer_fill (rasterizer); #endif } -#if CTX_ENABLE_SHADOW_BLUR -static inline float -ctx_gaussian (float x, float mu, float sigma) -{ - float a = ( x- mu) / sigma; - return ctx_expf (-0.5f * a * a); -} - -static inline void -ctx_compute_gaussian_kernel (int dim, float radius, float *kernel) -{ - 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++) - { - 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_rasterizer_round_rectangle (CtxRasterizer *rasterizer, float x, float y, float width, float height, float corner_radius) { @@ -22695,17 +24269,17 @@ ctx_rasterizer_round_rectangle (CtxRasterizer *rasterizer, float x, float y, flo if (radius > width*0.5f) radius = width/2; if (radius > height*0.5f) radius = height/2; - ctx_rasterizer_finish_shape (rasterizer); + ctx_rasterizer_close_path (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); + ctx_rasterizer_close_path (rasterizer); } static void -ctx_rasterizer_process (Ctx *ctx, CtxCommand *command); +ctx_rasterizer_process (Ctx *ctx, const CtxCommand *command); #if CTX_COMPOSITING_GROUPS static void @@ -22732,6 +24306,7 @@ ctx_rasterizer_start_group (CtxRasterizer *rasterizer) /* add a radius? */ static void ctx_rasterizer_end_group (CtxRasterizer *rasterizer) { + CtxGState *gstate = &rasterizer->state->gstate; CtxEntry restore_command = ctx_void(CTX_RESTORE); CtxEntry save_command = ctx_void(CTX_SAVE); int no = 0; @@ -22743,10 +24318,10 @@ ctx_rasterizer_end_group (CtxRasterizer *rasterizer) Ctx *ctx = rasterizer->backend.ctx; - CtxCompositingMode comp = rasterizer->state->gstate.compositing_mode; - CtxBlend blend = rasterizer->state->gstate.blend_mode; - CtxExtend extend = rasterizer->state->gstate.extend; - float global_alpha = rasterizer->state->gstate.global_alpha_f; + CtxCompositingMode comp = gstate->compositing_mode; + CtxBlend blend = gstate->blend_mode; + CtxExtend extend = gstate->extend; + float global_alpha = gstate->global_alpha_f; // fetch compositing, blending, global alpha ctx_rasterizer_process (ctx, (CtxCommand*)&restore_command); ctx_rasterizer_process (ctx, (CtxCommand*)&save_command); @@ -22830,35 +24405,21 @@ ctx_rasterizer_shadow_stroke (CtxRasterizer *rasterizer) { ctx_f (CTX_COLOR, CTX_RGBA, rgba[0]), ctx_f (CTX_CONT, rgba[1], rgba[2]), - ctx_f (CTX_CONT, rgba[3], 0) + ctx_f (CTX_CONT, rgba[3], 0.0f) }; 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 (ctx, (CtxCommand*)&save_command); + ctx_rasterizer_process (ctx, (CtxCommand*)&set_color_command[0]); + rasterizer->in_shadow = 1; { - 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 (ctx, (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 - } + float factor = ctx_matrix_get_scale (&rasterizer->state->gstate.transform); + rasterizer->feather_x = rasterizer->state->gstate.shadow_offset_x * factor; + rasterizer->feather_y = rasterizer->state->gstate.shadow_offset_y * factor; + rasterizer->feather = rasterizer->state->gstate.shadow_blur * factor; } - //ctx_free (kernel); + rasterizer->preserve = 1; + ctx_rasterizer_stroke (rasterizer); + rasterizer->in_shadow = 0; ctx_rasterizer_process (ctx, (CtxCommand*)&restore_command); } @@ -22886,11 +24447,6 @@ ctx_rasterizer_shadow_text (CtxRasterizer *rasterizer, const char *str) 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 (ctx, (CtxCommand*)&save_command); { @@ -22901,6 +24457,12 @@ ctx_rasterizer_shadow_text (CtxRasterizer *rasterizer, const char *str) ctx_rasterizer_process (ctx, (CtxCommand*)&set_color_command); ctx_rasterizer_process (ctx, (CtxCommand*)&move_to_command); rasterizer->in_shadow=1; + { + float factor = ctx_matrix_get_scale (&rasterizer->state->gstate.transform); + rasterizer->feather_x = rasterizer->state->gstate.shadow_offset_x * factor; + rasterizer->feather_y = rasterizer->state->gstate.shadow_offset_y * factor; + rasterizer->feather = rasterizer->state->gstate.shadow_blur * factor; + } ctx_rasterizer_text (rasterizer, str, 0); rasterizer->in_shadow=0; } @@ -22926,37 +24488,32 @@ ctx_rasterizer_shadow_fill (CtxRasterizer *rasterizer) { ctx_f (CTX_COLOR, CTX_RGBA, rgba[0]), ctx_f (CTX_CONT, rgba[1], rgba[2]), - ctx_f (CTX_CONT, rgba[3], 0) + ctx_f (CTX_CONT, rgba[3], 1.0f) }; 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 (ctx, (CtxCommand*)&save_command); + ctx_rasterizer_process (ctx, (CtxCommand*)&set_color_command); + rasterizer->preserve = 1; + rasterizer->in_shadow = 1; { - 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 (ctx, (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; - } + float factor = ctx_matrix_get_scale (&rasterizer->state->gstate.transform); + if (rasterizer->in_text) + factor /= rasterizer->state->gstate.font_size / CTX_BAKE_FONT_SIZE; + rasterizer->feather_x = rasterizer->state->gstate.shadow_offset_x * factor; + rasterizer->feather_y = rasterizer->state->gstate.shadow_offset_y * factor; + rasterizer->feather = rasterizer->state->gstate.shadow_blur * factor; } + ctx_rasterizer_fill (rasterizer); + ctx_rasterizer_reset_soft (rasterizer); + if (!rasterizer->in_text) + rasterizer->in_shadow = 0; ctx_rasterizer_process (ctx, (CtxCommand*)&restore_command); } #endif static void -ctx_rasterizer_line_dash (CtxRasterizer *rasterizer, unsigned int count, float *dashes) +ctx_rasterizer_line_dash (CtxRasterizer *rasterizer, unsigned int count, const float *dashes) { if (!dashes) { @@ -22975,17 +24532,38 @@ ctx_rasterizer_line_dash (CtxRasterizer *rasterizer, unsigned int count, float * static void -ctx_rasterizer_process (Ctx *ctx, CtxCommand *command) +ctx_rasterizer_process (Ctx *ctx, const CtxCommand *c) { - CtxEntry *entry = &command->entry; - CtxRasterizer *rasterizer = (CtxRasterizer *) ctx->backend; - CtxState *state = rasterizer->state; - CtxCommand *c = (CtxCommand *) entry; - int clear_clip = 0; + const CtxEntry *entry = &c->entry; + CtxRasterizer *rasterizer = (CtxRasterizer *) ctx->backend; + CtxState *state = rasterizer->state; + int clear_clip = 0; - ctx_interpret_style (state, entry, NULL); switch (c->code) { + case CTX_LINE_HEIGHT: + case CTX_WRAP_LEFT: + case CTX_WRAP_RIGHT: + case CTX_LINE_DASH_OFFSET: + case CTX_STROKE_POS: + case CTX_FEATHER: + case CTX_LINE_WIDTH: + case CTX_SHADOW_BLUR: + case CTX_SHADOW_OFFSET_X: + case CTX_SHADOW_OFFSET_Y: + case CTX_LINE_CAP: + case CTX_FILL_RULE: + case CTX_LINE_JOIN: + case CTX_TEXT_ALIGN: + case CTX_TEXT_BASELINE: + case CTX_TEXT_DIRECTION: + case CTX_GLOBAL_ALPHA: + case CTX_FONT_SIZE: + case CTX_MITER_LIMIT: + case CTX_COLOR_SPACE: + case CTX_STROKE_SOURCE: + ctx_interpret_style (state, entry, NULL); + break; #if CTX_ENABLE_SHADOW_BLUR case CTX_SHADOW_COLOR: { @@ -23142,14 +24720,14 @@ ctx_rasterizer_process (Ctx *ctx, CtxCommand *command) rasterizer->comp_op = NULL; } break; + case CTX_CONIC_GRADIENT: case CTX_LINEAR_GRADIENT: - ctx_state_gradient_clear_stops (state); - rasterizer->gradient_cache_valid = 0; - rasterizer->comp_op = NULL; - break; case CTX_RADIAL_GRADIENT: + ctx_interpret_style (state, entry, NULL); ctx_state_gradient_clear_stops (state); +#if CTX_GRADIENT_CACHE rasterizer->gradient_cache_valid = 0; +#endif rasterizer->comp_op = NULL; break; #endif @@ -23160,6 +24738,8 @@ ctx_rasterizer_process (Ctx *ctx, CtxCommand *command) case CTX_COMPOSITING_MODE: case CTX_BLEND_MODE: case CTX_EXTEND: + case CTX_SET_RGBA_U8: + ctx_interpret_style (state, entry, NULL); rasterizer->comp_op = NULL; break; #if CTX_COMPOSITING_GROUPS @@ -23211,8 +24791,7 @@ ctx_rasterizer_process (Ctx *ctx, CtxCommand *command) case CTX_STROKE: if (rasterizer->edge_list.count == 0)break; #if CTX_ENABLE_SHADOW_BLUR - if (state->gstate.shadow_blur > 0.0f && - !rasterizer->in_text) + if ((state->gstate.shadow_blur > 0.0f) & (!rasterizer->in_text)) ctx_rasterizer_shadow_stroke (rasterizer); #endif { @@ -23229,7 +24808,6 @@ ctx_rasterizer_process (Ctx *ctx, CtxCommand *command) int end = 0; CtxMatrix transform_backup = state->gstate.transform; _ctx_matrix_identity (&state->gstate.transform); - //state->gstate.transform_type = 0; _ctx_transform_prime (state); ctx_rasterizer_reset (rasterizer); /* for dashing we create a dashed path to stroke */ @@ -23249,18 +24827,18 @@ ctx_rasterizer_process (Ctx *ctx, CtxCommand *command) 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 / CTX_FULL_AA; + CtxSegment *segment = &temp[0]; + prev_x = segment->x0 * 1.0f / CTX_SUBDIV; + prev_y = segment->y0 * 1.0f / CTX_FULL_AA; ctx_rasterizer_move_to (rasterizer, prev_x, prev_y); is_down = 1; } for (i = start; i < count; i++) { - CtxSegment *entry = &temp[i]; + CtxSegment *segment = &temp[i]; float x, y; - if (entry->code == CTX_NEW_EDGE) + if (segment->code == CTX_NEW_EDGE) { if (started) { @@ -23269,8 +24847,8 @@ ctx_rasterizer_process (Ctx *ctx, CtxCommand *command) dash_lpos = 0.0; goto foo; } - prev_x = entry->data.s16[0] * 1.0f / CTX_SUBDIV; - prev_y = entry->data.s16[1] * 1.0f / CTX_FULL_AA; + prev_x = segment->x0 * 1.0f / CTX_SUBDIV; + prev_y = segment->y0 * 1.0f / CTX_FULL_AA; started = 1; start = i; is_down = 1; @@ -23279,11 +24857,11 @@ ctx_rasterizer_process (Ctx *ctx, CtxCommand *command) again: - x = entry->data.s16[2] * 1.0f / CTX_SUBDIV; - y = entry->data.s16[3] * 1.0f / CTX_FULL_AA; + x = segment->x1 * 1.0f / CTX_SUBDIV; + y = segment->y1 * 1.0f / CTX_FULL_AA; float dx = x - prev_x; float dy = y - prev_y; - float length = ctx_fast_hypotf (dx, dy); + float length = ctx_hypotf (dx, dy); if (dash_lpos + length >= dashes[dash_no] * factor) { @@ -23324,7 +24902,6 @@ ctx_rasterizer_process (Ctx *ctx, CtxCommand *command) start = end+1; } state->gstate.transform = transform_backup; - //state->gstate.transform_type = 0; _ctx_transform_prime (state); } ctx_rasterizer_stroke (rasterizer); @@ -23333,6 +24910,7 @@ ctx_rasterizer_process (Ctx *ctx, CtxCommand *command) break; case CTX_FONT: + ctx_interpret_style (state, entry, NULL); ctx_rasterizer_set_font (rasterizer, ctx_arg_string() ); break; case CTX_TEXT: @@ -23351,10 +24929,6 @@ ctx_rasterizer_process (Ctx *ctx, CtxCommand *command) 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: if (ctx->bail) break; { @@ -23374,14 +24948,19 @@ ctx_rasterizer_process (Ctx *ctx, CtxCommand *command) if (!ctx->bail) { if (rasterizer->edge_list.count == 0)break; + int preserve = rasterizer->preserve; #if CTX_ENABLE_SHADOW_BLUR - if (state->gstate.shadow_blur > 0.0f && - !rasterizer->in_text) + if ((state->gstate.shadow_blur > 0.0f) & (!rasterizer->in_text) & (!rasterizer->in_shadow)) + { ctx_rasterizer_shadow_fill (rasterizer); + } #endif ctx_rasterizer_fill (rasterizer); + if (preserve) + ctx_rasterizer_reset_soft (rasterizer); + else + ctx_rasterizer_reset (rasterizer); } - ctx_rasterizer_reset (rasterizer); break; case CTX_START_FRAME: case CTX_BEGIN_PATH: @@ -23391,11 +24970,27 @@ ctx_rasterizer_process (Ctx *ctx, CtxCommand *command) ctx_rasterizer_clip (rasterizer); break; case CTX_CLOSE_PATH: - ctx_rasterizer_finish_shape (rasterizer); + ctx_rasterizer_close_path (rasterizer); break; case CTX_IMAGE_SMOOTHING: + ctx_interpret_style (state, entry, NULL); rasterizer->comp_op = NULL; break; + case CTX_VIEW_BOX: + { // XXX : this can screw with transforms if one is not careful + float x = ctx_arg_float(0), + y = ctx_arg_float(1), + width = ctx_arg_float(2), + height = ctx_arg_float(2); + float factor = ctx_width (ctx)/width; + float factorh = ctx_height (ctx)/height; + + if (factorh <= factor) factor = factorh; + + ctx_translate (ctx, x, y); + ctx_scale (ctx, factor, factor); + } + break; } ctx_interpret_pos_bare (state, entry, NULL); } @@ -23436,11 +25031,16 @@ CtxAntialias ctx_get_antialias (Ctx *ctx) switch (((CtxRasterizer*)(ctx->backend))->aa) { - case 1: return CTX_ANTIALIAS_NONE; - case 3: return CTX_ANTIALIAS_FAST; - //case 5: return CTX_ANTIALIAS_GOOD; + case 0: + 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 15: + return CTX_ANTIALIAS_FULL; } } @@ -23451,6 +25051,7 @@ static int _ctx_antialias_to_aa (CtxAntialias antialias) case CTX_ANTIALIAS_NONE: return 1; case CTX_ANTIALIAS_FAST: return 3; case CTX_ANTIALIAS_GOOD: return 5; + case CTX_ANTIALIAS_FULL: return 15; default: case CTX_ANTIALIAS_DEFAULT: return CTX_RASTERIZER_AA; } @@ -23479,10 +25080,6 @@ ctx_set_antialias (Ctx *ctx, CtxAntialias antialias) ((CtxRasterizer*)(ctx->backend))->aa = _ctx_antialias_to_aa (antialias); - ((CtxRasterizer*)(ctx->backend))->fast_aa = 0; - if (antialias == CTX_ANTIALIAS_DEFAULT|| - antialias == CTX_ANTIALIAS_FAST) - ((CtxRasterizer*)(ctx->backend))->fast_aa = 1; } CtxRasterizer * @@ -23504,8 +25101,6 @@ ctx_rasterizer_init (CtxRasterizer *rasterizer, Ctx *ctx, Ctx *texture_source, C rasterizer->state = state; rasterizer->texture_source = texture_source?texture_source:ctx; - 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; @@ -23552,7 +25147,7 @@ ctx_rasterizer_reinit (Ctx *ctx, int stride, CtxPixelFormat pixel_format) { - CtxBackend *backend = ctx_get_backend (ctx); + CtxBackend *backend = (CtxBackend*)ctx_get_backend (ctx); CtxRasterizer *rasterizer = (CtxRasterizer*)backend; if (!backend) return; #if 0 @@ -23573,7 +25168,9 @@ ctx_rasterizer_reinit (Ctx *ctx, rasterizer->blit_stride = stride; rasterizer->scan_min = 5000; rasterizer->scan_max = -5000; +#if CTX_GRADIENT_CACHE rasterizer->gradient_cache_valid = 0; +#endif if (pixel_format == CTX_FORMAT_BGRA8) { @@ -23634,7 +25231,7 @@ CtxRasterizer *ctx_rasterizer_new (void *data, int x, int y, int width, int heig #endif -void +static void ctx_state_gradient_clear_stops (CtxState *state) { state->gradient.n_stops = 0; @@ -31613,8 +33210,8 @@ ctx_base642bin (const char *ascii, } -static inline int -ctx_conts_for_entry (CtxEntry *entry) +static CTX_INLINE int +ctx_conts_for_entry (const CtxEntry *entry) { switch (entry->code) { @@ -31641,12 +33238,12 @@ ctx_conts_for_entry (CtxEntry *entry) case CTX_REL_QUAD_TO: case CTX_QUAD_TO: case CTX_LINEAR_GRADIENT: + case CTX_CONIC_GRADIENT: return 1; case CTX_TEXT: case CTX_LINE_DASH: case CTX_COLOR_SPACE: - case CTX_STROKE_TEXT: case CTX_FONT: case CTX_TEXTURE: { @@ -31683,14 +33280,14 @@ ctx_conts_for_entry (CtxEntry *entry) void -ctx_iterator_init (CtxIterator *iterator, +ctx_iterator_init (CtxIterator *iterator, CtxDrawlist *drawlist, - int start_pos, - int flags) + int start_pos, + int flags) { iterator->drawlist = drawlist; iterator->flags = flags; - iterator->bitpack_pos = 0; + iterator->bitpack_pos = iterator->bitpack_length = 0; iterator->pos = start_pos; iterator->end_pos = drawlist->count; @@ -31703,7 +33300,7 @@ int ctx_iterator_pos (CtxIterator *iterator) return iterator->pos; } -static inline CtxEntry *_ctx_iterator_next (CtxIterator *iterator) +static CTX_INLINE CtxEntry *_ctx_iterator_next (CtxIterator *iterator) { int ret = iterator->pos; CtxEntry *entry = &iterator->drawlist->entries[ret]; @@ -31758,9 +33355,9 @@ ctx_iterator_next (CtxIterator *iterator) { CtxEntry *ret; #if CTX_BITPACK - int expand_bitpack = iterator->flags & CTX_ITERATOR_EXPAND_BITPACK; + int expand_bitpack = (iterator->flags & CTX_ITERATOR_EXPAND_BITPACK)!=0; again: - if (CTX_UNLIKELY(expand_bitpack && iterator->bitpack_length)) + if (CTX_UNLIKELY(expand_bitpack & (iterator->bitpack_length!=0))) { ret = &iterator->bitpack_command[iterator->bitpack_pos]; iterator->bitpack_pos += (ctx_conts_for_entry (ret) + 1); @@ -31773,7 +33370,7 @@ ctx_iterator_next (CtxIterator *iterator) #endif ret = _ctx_iterator_next (iterator); #if CTX_BITPACK - if (CTX_UNLIKELY(ret && expand_bitpack)) + if (CTX_UNLIKELY((ret != NULL) & (expand_bitpack))) switch ((CtxCode)(ret->code)) { case CTX_REL_CURVE_TO_REL_LINE_TO: @@ -31783,7 +33380,7 @@ ctx_iterator_next (CtxIterator *iterator) iterator->bitpack_command[2].code = CTX_CONT; iterator->bitpack_command[3].code = CTX_REL_LINE_TO; // 0.0 here is a common optimization - so check for it - if (ret->data.s8[6]== 0 && ret->data.s8[7] == 0) + if ((ret->data.s8[6]== 0) & (ret->data.s8[7] == 0)) { iterator->bitpack_length = 3; } else iterator->bitpack_length = 4; @@ -31844,6 +33441,7 @@ ctx_iterator_next (CtxIterator *iterator) iterator->bitpack_pos = 0; iterator->bitpack_length = 2; goto again; + case CTX_CONIC_GRADIENT: case CTX_LINEAR_GRADIENT: case CTX_QUAD_TO: case CTX_REL_QUAD_TO: @@ -31862,7 +33460,6 @@ ctx_iterator_next (CtxIterator *iterator) case CTX_SOURCE_TRANSFORM: case CTX_ROUND_RECTANGLE: case CTX_TEXT: - case CTX_STROKE_TEXT: case CTX_FONT: case CTX_LINE_DASH: case CTX_FILL: @@ -31904,6 +33501,8 @@ ctx_iterator_next (CtxIterator *iterator) case CTX_RESTORE: case CTX_LINE_WIDTH: case CTX_LINE_DASH_OFFSET: + case CTX_STROKE_POS: + case CTX_FEATHER: case CTX_LINE_HEIGHT: case CTX_WRAP_LEFT: case CTX_WRAP_RIGHT: @@ -32021,7 +33620,7 @@ ctx_drawlist_resize (CtxDrawlist *drawlist, int desired_size) static inline int -ctx_drawlist_add_single (CtxDrawlist *drawlist, CtxEntry *entry) +ctx_drawlist_add_single (CtxDrawlist *drawlist, const CtxEntry *entry) { unsigned int max_size = CTX_MAX_JOURNAL_SIZE; int ret = drawlist->count; @@ -32062,7 +33661,7 @@ ctx_add_single (Ctx *ctx, void *entry) } static inline int -ctx_drawlist_add_entry (CtxDrawlist *drawlist, CtxEntry *entry) +ctx_drawlist_add_entry (CtxDrawlist *drawlist, const CtxEntry *entry) { int length = ctx_conts_for_entry (entry) + 1; int ret = 0; @@ -32428,15 +34027,15 @@ ctx_drawlist_bitpack (CtxDrawlist *drawlist, unsigned int start_pos) to bitpack yet*/ { CtxEntry *entry = &drawlist->entries[i]; - if (entry[0].code == CTX_SET_RGBA_U8 && - entry[1].code == CTX_MOVE_TO && - entry[2].code == CTX_REL_LINE_TO && - entry[3].code == CTX_REL_LINE_TO && - entry[4].code == CTX_REL_LINE_TO && - entry[5].code == CTX_REL_LINE_TO && - entry[6].code == CTX_FILL && - ctx_fabsf (entry[2].data.f[0] - 1.0f) < 0.02f && - ctx_fabsf (entry[3].data.f[1] - 1.0f) < 0.02f) + if ((int)(entry[0].code == CTX_SET_RGBA_U8) & + (entry[1].code == CTX_MOVE_TO) & + (entry[2].code == CTX_REL_LINE_TO) & + (entry[3].code == CTX_REL_LINE_TO) & + (entry[4].code == CTX_REL_LINE_TO) & + (entry[5].code == CTX_REL_LINE_TO) & + (entry[6].code == CTX_FILL) & + (ctx_fabsf (entry[2].data.f[0] - 1.0f) < 0.02f) & + (ctx_fabsf (entry[3].data.f[1] - 1.0f) < 0.02f)) { entry[0].code = CTX_SET_PIXEL; entry[0].data.u16[2] = entry[1].data.f[0]; @@ -32451,9 +34050,9 @@ ctx_drawlist_bitpack (CtxDrawlist *drawlist, unsigned int start_pos) #if 1 else if (entry[0].code == CTX_REL_LINE_TO) { - if (entry[1].code == CTX_REL_LINE_TO && - entry[2].code == CTX_REL_LINE_TO && - entry[3].code == CTX_REL_LINE_TO) + if ((entry[1].code == CTX_REL_LINE_TO) & + (entry[2].code == CTX_REL_LINE_TO) & + (entry[3].code == CTX_REL_LINE_TO)) { float max_dev = find_max_dev (entry, 4); if (max_dev < 114 / CTX_SUBDIV) @@ -32477,9 +34076,9 @@ ctx_drawlist_bitpack (CtxDrawlist *drawlist, unsigned int start_pos) entry[3].code = CTX_NOP; } } - else if (entry[1].code == CTX_REL_LINE_TO && - entry[2].code == CTX_REL_LINE_TO && - entry[3].code == CTX_REL_LINE_TO) + else if ((entry[1].code == CTX_REL_LINE_TO) & + (entry[2].code == CTX_REL_LINE_TO) & + (entry[3].code == CTX_REL_LINE_TO)) { float max_dev = find_max_dev (entry, 4); if (max_dev < 114 / CTX_SUBDIV) @@ -32657,9 +34256,9 @@ ctx_drawlist_compact (CtxDrawlist *drawlist) #endif } -uint8_t *ctx_define_texture_pixel_data (CtxEntry *entry) +uint8_t *ctx_define_texture_pixel_data (const CtxEntry *entry) { - return &entry[2 + 1 + 1 + ctx_conts_for_entry (&entry[2])].data.u8[0]; + return (uint8_t*)&entry[2 + 1 + 1 + ctx_conts_for_entry (&entry[2])].data.u8[0]; } #ifndef __CTX_TRANSFORM @@ -32684,6 +34283,7 @@ ctx_matrix_apply_transform (const CtxMatrix *m, float *x, float *y) static inline int determine_transform_type (const CtxMatrix *m) { + // XXX : does not set 4 - which is perspective if (m->m[2][0] != 0.0f || m->m[2][1] != 0.0f || m->m[2][2] != 1.0f) @@ -32717,6 +34317,10 @@ _ctx_transform_prime (CtxState *state) state->gstate.prepped_transform.m[2][c] = (int)(state->gstate.transform.m[2][c] * TRANSFORM_SCALE); } + state->gstate.tolerance = 0.25f/ctx_matrix_get_scale (&state->gstate.transform); + state->gstate.tolerance *= state->gstate.tolerance; + state->gstate.tolerance_fixed = + (state->gstate.tolerance * CTX_FIX_SCALE * CTX_FIX_SCALE); } static inline void @@ -32769,8 +34373,8 @@ _ctx_user_to_device_prepped_fixed (CtxState *state, int x, int y, int *x_out, in _ctx_user_to_device_prepped_fixed (state, x, y, x_out, y_out); break; case 1: // identity - *x_out = (x * CTX_SUBDIV) / TRANSFORM_SCALE; - *y_out = (y * CTX_FULL_AA) / TRANSFORM_SCALE; + *x_out = (x * CTX_SUBDIV) >> TRANSFORM_SHIFT; + *y_out = (y * CTX_FULL_AA) >> TRANSFORM_SHIFT; break; case 2: // scale/translate _ctx_matrix_apply_transform_scale_translate_fixed (&state->gstate.prepped_transform, x, y, x_out, y_out); @@ -33144,14 +34748,12 @@ ctx_matrix_invert (CtxMatrix *m) #include #endif - - -//#include - #endif #define DESIRED_PERIOD_SIZE 1000 +static pthread_mutex_t ctx_audio_mutex; + int ctx_pcm_bytes_per_frame (CtxPCM format) { switch (format) @@ -33314,7 +34916,9 @@ static void *ctx_alsa_audio_start(Ctx *ctx) if (ctx_pcm_cur_left == 0) { void *old = ctx_pcm_list->data; + pthread_mutex_lock (&ctx_audio_mutex); ctx_list_remove (&ctx_pcm_list, old); + pthread_mutex_unlock (&ctx_audio_mutex); ctx_free (old); ctx_pcm_cur_left = 0; if (ctx_pcm_list) @@ -33351,7 +34955,7 @@ static void *ctx_alsa_audio_start(Ctx *ctx) } #endif -static char MuLawCompressTable[256] = +static const 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, @@ -33443,7 +35047,9 @@ void ctx_ctx_pcm (Ctx *ctx) if (ctx_pcm_cur_left == 0) { void *old = ctx_pcm_list->data; + pthread_mutex_lock (&ctx_audio_mutex); ctx_list_remove (&ctx_pcm_list, old); + pthread_mutex_unlock (&ctx_audio_mutex); ctx_free (old); ctx_pcm_cur_left = 0; if (ctx_pcm_list) @@ -33466,8 +35072,13 @@ void ctx_ctx_pcm (Ctx *ctx) } } +#if CTX_AUDIO_HOST +int ctx_host_audio_init (int hz, CtxPCM format); +#endif + int ctx_pcm_init (Ctx *ctx) { + pthread_mutex_init (&ctx_audio_mutex, NULL); #if 0 if (!strcmp (ctx->backend->name, "mmm") || !strcmp (ctx->backend->name, "mmm-client")) @@ -33487,6 +35098,10 @@ int ctx_pcm_init (Ctx *ctx) } else { +#if CTX_AUDIO_HOST + if (!ctx_host_audio_init (ctx_host_freq, ctx_host_format)) + return -1; +#endif #if CTX_ALSA pthread_t tid; h = alsa_open("default", ctx_host_freq, ctx_pcm_channels (ctx_host_format)); @@ -33542,7 +35157,9 @@ int ctx_pcm_queue (Ctx *ctx, const int8_t *data, int frames) if (ctx_pcm_list == NULL) // otherwise it is another frame at front ctx_pcm_cur_left = scaled_frames; // and current cur_left is valid + pthread_mutex_lock (&ctx_audio_mutex); ctx_list_append (&ctx_pcm_list, packet); + pthread_mutex_unlock (&ctx_audio_mutex); ctx_pcm_queued += scaled_frames; return frames; @@ -33661,14 +35278,14 @@ void ctx_pcm_set_format (Ctx *ctx, CtxPCM format) { } void ctx_pcm_set_sample_rate (Ctx *ctx, int sample_rate) { } int ctx_pcm_get_sample_rate (Ctx *ctx) { return 48000; } CtxPCM ctx_pcm_get_format (Ctx *ctx) { return CTX_S16S; } -int ctx_pcm_queue (Ctx *ctx, const int8_t *data, int frames) { return frames; } +int ctx_pcm_queue (Ctx *ctx, const int8_t *data, int frames) { return frames; } float ctx_pcm_get_queued_length (Ctx *ctx) { return 0.0; } #endif /* Copyright (C) 2020 Øyvind Kolås */ -#if CTX_FORMATTER +#if CTX_FORMATTER || CTX_AUDIO /* returns the maximum string length including terminating \0 */ int ctx_a85enc_len (int input_length) @@ -34521,7 +36138,9 @@ static void ctx_state_set (CtxState *state, uint32_t key, float value) #define CTX_KEYDB_STRING_START (-90000.0f) -#define CTX_KEYDB_STRING_END (CTX_KEYDB_STRING_START + CTX_STRINGPOOL_SIZE) +// XXX : hard-coded as 32kb - breaking above, used to be set from hardcoded +// limits with static stringpool size +#define CTX_KEYDB_STRING_END (CTX_KEYDB_STRING_START + 32000) static int ctx_float_is_string (float val) { @@ -34543,6 +36162,9 @@ static float ctx_string_index_to_float (int index) return CTX_KEYDB_STRING_START + index; } +static char ctx_kv_num[8][32]; +static int ctx_num_idx = 0; + static void *ctx_state_get_blob (CtxState *state, uint32_t key) { float stored = ctx_state_get (state, key); @@ -34553,8 +36175,13 @@ static void *ctx_state_get_blob (CtxState *state, uint32_t key) return &state->stringpool[idx]; } - // format number as string? - return NULL; + if (-stored == 0.0f) return NULL;//""; + + ctx_num_idx ++; + if (ctx_num_idx >=8) ctx_num_idx = 0; + snprintf (&ctx_kv_num[ctx_num_idx][0], 31, "%.6f", stored); + + return ctx_kv_num[ctx_num_idx]; } static const char *ctx_state_get_string (CtxState *state, uint32_t key) @@ -34570,18 +36197,20 @@ static void ctx_state_set_blob (CtxState *state, uint32_t key, uint8_t *data, in { int idx = state->gstate.stringpool_pos; - if (idx + len > CTX_STRINGPOOL_SIZE) + if (idx + len + 1 >= state->stringpool_size - 512) { - ctx_log ("blowing varpool size [%c..]\n", data[0]); + int desired = idx + len + 1 + 1024; + // ctx_log ("blowing varpool size [%c..]\n", data[0]); //fprintf (stderr, "blowing varpool size [%c%c%c..]\n", data[0],data[1], data[1]?data[2]:0); -#if 0 - for (int i = 0; i< CTX_STRINGPOOL_SIZE; i++) + void *copy = ctx_malloc (desired); + if (!copy) return; + if (state->stringpool) { - if (i==0) fprintf (stderr, "\n%i ", i); - else fprintf (stderr, "%c", state->stringpool[i]); + memcpy (copy, state->stringpool, state->gstate.stringpool_pos); + ctx_free (state->stringpool); } -#endif - return; + state->stringpool = copy; + state->stringpool_size = desired; } memcpy (&state->stringpool[idx], data, len); @@ -34929,7 +36558,7 @@ static void ctx_rgb_device_to_user (CtxState *state, float rin, float gin, float } #endif -static void ctx_color_get_drgba (CtxState *state, CtxColor *color, float *out) +static inline void ctx_color_get_drgba (CtxState *state, CtxColor *color, float *out) { if (! (color->valid & CTX_VALID_RGBA_DEVICE) ) { @@ -35080,14 +36709,14 @@ static void ctx_color_get_cmyka_u8 (CtxState *state, CtxColor *color, uint8_t *o #endif #endif -static inline void +static CTX_INLINE void _ctx_color_get_rgba8 (CtxState *state, CtxColor *color, uint8_t *out) { if (! (color->valid & CTX_VALID_RGBA_U8) ) { float rgba[4]; ctx_color_get_drgba (state, color, rgba); - for (int i = 0; i < 4; i ++) + for (unsigned int i = 0; i < 4; i ++) { color->rgba[i] = ctx_float_to_u8 (rgba[i]); } color->valid |= CTX_VALID_RGBA_U8; } @@ -35244,11 +36873,12 @@ void ctx_rgba (Ctx *ctx, float r, float g, float b, float a) else au = (uint8_t)(a * 255); CtxEntry command = ctx_u8 (CTX_SET_RGBA_U8, ru,gu,bu,au, 0, 0, 0, 0); - +#if 0 uint8_t rgba[4]; ctx_color_get_rgba8 (&ctx->state, &ctx->state.gstate.source_fill.color, rgba); if (rgba[0] == ru && rgba[1] == gu && rgba[2] == bu && rgba[3] == au) return; +#endif ctx_process (ctx, &command); #else float components[4]={r,g,b,a}; @@ -35377,7 +37007,7 @@ typedef struct ColorDef { float a; } ColorDef; -static ColorDef _ctx_colors[]={ +static const ColorDef _ctx_colors[]={ {SQZ_black, 0, 0, 0, 1}, {SQZ_red, 1, 0, 0, 1}, {SQZ_green, 0, 1, 0, 1}, @@ -35568,14 +37198,14 @@ int ctx_color (Ctx *ctx, const char *string) void ctx_rgba8 (Ctx *ctx, uint8_t r, uint8_t g, uint8_t b, uint8_t a) { -#if 0 +#if 1 CtxEntry command = ctx_u8 (CTX_SET_RGBA_U8, r, g, b, a, 0, 0, 0, 0); - +#if 0 uint8_t rgba[4]; ctx_color_get_rgba8 (&ctx->state, &ctx->state.gstate.source.color, rgba); if (rgba[0] == r && rgba[1] == g && rgba[2] == b && rgba[3] == a) return; - +#endif ctx_process (ctx, &command); #else ctx_rgba (ctx, r/255.0f, g/255.0f, b/255.0f, a/255.0f); @@ -35636,10 +37266,10 @@ void ctx_rasterizer_colorspace_babl (CtxState *state, } #endif -void ctx_rasterizer_colorspace_icc (CtxState *state, - CtxColorSpace space_slot, - char *icc_data, - int icc_length) +void ctx_rasterizer_colorspace_icc (CtxState *state, + CtxColorSpace space_slot, + const unsigned char *icc_data, + int icc_length) { #if CTX_BABL const char *error = NULL; @@ -35649,7 +37279,7 @@ void ctx_rasterizer_colorspace_icc (CtxState *state, else if (icc_length < 32) { if (icc_data[0] == '0' && icc_data[1] == 'x') - sscanf (icc_data, "%p", &space); + sscanf ((char*)icc_data, "%p", &space); else { char tmp[24]; @@ -35669,7 +37299,7 @@ void ctx_rasterizer_colorspace_icc (CtxState *state, if (!space) { - space = babl_space_from_icc (icc_data, icc_length, BABL_ICC_INTENT_RELATIVE_COLORIMETRIC, &error); + space = babl_space_from_icc ((char*)icc_data, icc_length, BABL_ICC_INTENT_RELATIVE_COLORIMETRIC, &error); } if (space) { @@ -35678,10 +37308,10 @@ void ctx_rasterizer_colorspace_icc (CtxState *state, #endif } -void ctx_colorspace (Ctx *ctx, - CtxColorSpace space_slot, - unsigned char *data, - int data_length) +void ctx_colorspace (Ctx *ctx, + CtxColorSpace space_slot, + const unsigned char *data, + int data_length) { if (data) { @@ -35808,6 +37438,7 @@ static void ctx_buffer_deinit (CtxBuffer *buffer) buffer->data = NULL; buffer->free_func = NULL; buffer->user_data = NULL; +#if CTX_ENABLE_CM if (buffer->color_managed) { if (buffer->color_managed != buffer) @@ -35816,6 +37447,7 @@ static void ctx_buffer_deinit (CtxBuffer *buffer) } buffer->color_managed = NULL; } +#endif } void ctx_buffer_destroy (CtxBuffer *buffer) @@ -35944,6 +37576,7 @@ void _ctx_texture_prepare_color_management (CtxState *state, CtxBuffer *buffer) { +#if CTX_ENABLE_CM // _ctx_texture_lock (); switch (buffer->format->pixel_format) { @@ -35958,8 +37591,8 @@ _ctx_texture_prepare_color_management (CtxState *state, CtxBuffer *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", state->gstate.device_space)), + babl_fish (babl_format_with_space ("Ra'Ga'Ba'A u8", buffer->space), + babl_format_with_space ("Ra'Ga'Ba'A u8", state->gstate.device_space)), buffer->data, color_managed->data, buffer->width * buffer->height ); @@ -35988,6 +37621,7 @@ _ctx_texture_prepare_color_management (CtxState *state, default: buffer->color_managed = buffer; } +#endif // _ctx_texture_unlock (); } @@ -36824,7 +38458,7 @@ const char *ctx_nct_get_event (Ctx *n, int timeoutms, int *x, int *y) case 67: return "pm"; /* have a separate pd ? */ default: { - static char rbuf[100]; + static char rbuf[50]; sprintf (rbuf, "mouse (unhandled state: %i)", buf[3]); return rbuf; } @@ -37157,6 +38791,7 @@ int ctx_count (Ctx *ctx) extern int _ctx_damage_control; +static int _ctx_depth = 0; #if CTX_EVENTS @@ -37241,19 +38876,31 @@ static int is_in_ctx (void) } #endif -#if EMSCRIPTEN -CTX_EXPORT Ctx * -ctx_wasm_get_context (int flags); +#if EMSCRIPTEN +CTX_EXPORT +#endif +#if defined(PICO_BUILD) || CTX_ESP || EMSCRIPTEN +Ctx *ctx_host(void); +#endif static Ctx *ctx_new_ui (int width, int height, const char *backend) { - return ctx_wasm_get_context (64*1024);//CTX_FLAG_HASH_CACHE); -} -#else + static Ctx *ret = NULL; + if (ret) + { + _ctx_depth ++; + return ret; + } +#if defined(PICO_BUILD) || CTX_ESP || EMSCRIPTEN + ret = ctx_host (); +#endif + if (ret) + { + return ret; + } + -static Ctx *ctx_new_ui (int width, int height, const char *backend) -{ #if CTX_TILED if (getenv ("CTX_DAMAGE_CONTROL")) { @@ -37313,7 +38960,6 @@ static Ctx *ctx_new_ui (int width, int height, const char *backend) exit (-1); } - Ctx *ret = NULL; #if CTX_FORMATTER #if CTX_TERMINAL_EVENTS /* we do the query on auto but not on directly set ctx @@ -37397,7 +39043,6 @@ static Ctx *ctx_new_ui (int width, int height, const char *backend) return ret; } #endif -#endif #else void _ctx_texture_unlock (void) { @@ -37460,6 +39105,12 @@ void _ctx_events_init (Ctx *ctx) events->tap_hysteresis = 32; /* XXX: should be ppi dependent */ } +void _ctx_toggle_in_idle_dispatch (Ctx *ctx) +{ + ctx->events.in_idle_dispatch= !ctx->events.in_idle_dispatch; + ctx_reset_has_exited (ctx); +} + void _ctx_idle_iteration (Ctx *ctx) { @@ -37472,8 +39123,8 @@ void _ctx_idle_iteration (Ctx *ctx) if (!ctx->events.idles && !ctx->events.idles_to_add) { +#ifndef __EMSCRIPTEN_PTHREADS__ #ifdef EMSCRIPTEN -#ifdef ASYNCIFY emscripten_sleep (10); #endif #endif @@ -37482,9 +39133,14 @@ void _ctx_idle_iteration (Ctx *ctx) ctx->events.in_idle_dispatch=1; - for (l = ctx->events.idles; l; l = l->next) + CtxList *idles_to_remove = ctx->events.idles_to_remove; + ctx->events.idles_to_remove = NULL; + CtxList *idles = ctx->events.idles; + ctx->events.idles = NULL; + + for (l = idles; l; l = l->next) { - CtxIdleCb *item = l->data; + CtxIdleCb *item = (CtxIdleCb*)l->data; long rem = item->ticks_remaining; if (item->ticks_remaining >= 0) @@ -37496,9 +39152,9 @@ void _ctx_idle_iteration (Ctx *ctx) if (rem < 0) { int to_be_removed = 0; - for (CtxList *l2 = ctx->events.idles_to_remove; l2; l2=l2->next) + for (CtxList *l2 = idles_to_remove; l2; l2=l2->next) { - CtxIdleCb *item2 = l2->data; + CtxIdleCb *item2 = (CtxIdleCb*)l2->data; if (item2 == item) to_be_removed = 1; } @@ -37506,7 +39162,7 @@ void _ctx_idle_iteration (Ctx *ctx) { if (item->cb (ctx, item->idle_data) == 0) { - ctx_list_prepend (&ctx->events.idles_to_remove, item); + ctx_list_prepend (&idles_to_remove, item); } else item->ticks_remaining = item->ticks_full; @@ -37518,9 +39174,9 @@ void _ctx_idle_iteration (Ctx *ctx) else { int to_be_removed = 0; - for (CtxList *l2 = ctx->events.idles_to_remove; l2; l2=l2->next) + for (CtxList *l2 = idles_to_remove; l2; l2=l2->next) { - CtxIdleCb *item2 = l2->data; + CtxIdleCb *item2 = (CtxIdleCb*)l2->data; if (item2 == item) to_be_removed = 1; } @@ -37528,7 +39184,7 @@ void _ctx_idle_iteration (Ctx *ctx) { if (item->cb (ctx, item->idle_data) == 0) { - ctx_list_prepend (&ctx->events.idles_to_remove, item); + ctx_list_prepend (&idles_to_remove, item); } else item->ticks_remaining = item->ticks_full; @@ -37538,23 +39194,26 @@ void _ctx_idle_iteration (Ctx *ctx) while (ctx->events.idles_to_add) { - CtxIdleCb *item = ctx->events.idles_to_add->data; - ctx_list_prepend (&ctx->events.idles, item); + CtxIdleCb *item = (CtxIdleCb*)ctx->events.idles_to_add->data; + ctx_list_prepend (&idles, item); ctx_list_remove (&ctx->events.idles_to_add, item); } - while (ctx->events.idles_to_remove) + while (idles_to_remove) { - CtxIdleCb *item = ctx->events.idles_to_remove->data; - ctx_list_remove (&ctx->events.idles, item); - ctx_list_remove (&ctx->events.idles_to_remove, item); + CtxIdleCb *item = (CtxIdleCb*)idles_to_remove->data; + ctx_list_remove (&idles, item); + ctx_list_remove (&idles_to_remove, item); if (item->destroy_notify) item->destroy_notify (item->destroy_data); } + ctx->events.idles = idles; ctx->events.in_idle_dispatch=0; +#ifndef __EMSCRIPTEN_PTHREADS__ #if EMSCRIPTEN -#ifdef ASYNCIFY +//#ifdef ASYNCIFY emscripten_sleep(1); +//#endif #endif #endif } @@ -37633,7 +39292,7 @@ static void _ctx_bindings_key_press (CtxEvent *event, void *data1, void *data2) { if (events->bindings[i].cb) { - events->bindings[i].cb (event, events->bindings[i].cb_data, NULL); + events->bindings[i].cb (event, events->bindings[i].cb_data, events->bindings[i].command); if (event->stop_propagate) return; handled = 1; @@ -37670,7 +39329,7 @@ void ctx_remove_idle (Ctx *ctx, int handle) for (l = ctx->events.idles; l; l = l->next) { - CtxIdleCb *item = l->data; + CtxIdleCb *item = (CtxIdleCb*)l->data; if (item->id == handle) { ctx_list_prepend (&ctx->events.idles_to_remove, item); @@ -37687,13 +39346,14 @@ void ctx_remove_idle (Ctx *ctx, int handle) ctx_list_remove (&ctx->events.idles_to_remove, item); if (item->destroy_notify) item->destroy_notify (item->destroy_data); + ctx_free (item); } } 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 = ctx_calloc (sizeof (CtxIdleCb), 1); + CtxIdleCb *item = (CtxIdleCb*)ctx_calloc (sizeof (CtxIdleCb), 1); item->cb = idle_cb; item->idle_data = idle_data; item->id = ++ctx->events.idle_id; @@ -37716,7 +39376,7 @@ int ctx_add_timeout (Ctx *ctx, int ms, int (*idle_cb)(Ctx *ctx, void *idle_data) 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 = ctx_calloc (sizeof (CtxIdleCb), 1); + CtxIdleCb *item = (CtxIdleCb*)ctx_calloc (sizeof (CtxIdleCb), 1); item->cb = idle_cb; item->idle_data = idle_data; item->id = ++ctx->events.idle_id; @@ -37912,8 +39572,13 @@ void ctx_listen_full (Ctx *ctx, item->cb[0].finalize_data = finalize_data; item->cb_count = 1; item->types = types; +#if CTX_CURRENT_PATH item->path = ctx_current_path (ctx); item->path_hash = ctx_path_hash (item->path); +#else + item->path = NULL; + item->path_hash = 0; +#endif ctx_get_matrix (ctx, &item->inv_matrix); ctx_matrix_invert (&item->inv_matrix); @@ -37983,7 +39648,7 @@ void ctx_listen (Ctx *ctx, } if (types == CTX_DRAG_MOTION) - types = CTX_DRAG_MOTION | CTX_DRAG_PRESS; + types = (CtxEventType)(CTX_DRAG_MOTION | CTX_DRAG_PRESS); ctx_listen_full (ctx, x, y, width, height, types, cb, data1, data2, NULL, NULL); } @@ -38016,7 +39681,7 @@ void ctx_listen_with_finalize (Ctx *ctx, } if (types == CTX_DRAG_MOTION) - types = CTX_DRAG_MOTION | CTX_DRAG_PRESS; + types = (CtxEventType)(CTX_DRAG_MOTION | CTX_DRAG_PRESS); ctx_listen_full (ctx, x, y, width, height, types, cb, data1, data2, finalize, finalize_data); } @@ -38026,7 +39691,7 @@ static void ctx_report_hit_region (CtxEvent *event, void *data2) { #if CTX_BAREMETAL==0 - const char *id = data; + const char *id = (char*)data; fprintf (stderr, "hit region %s\n", id); #endif @@ -38278,18 +39943,21 @@ void ctx_get_event_fds (Ctx *ctx, int *fd, int *count) } -CtxEvent *ctx_get_event (Ctx *ctx) +static CtxEvent *ctx_get_event2 (Ctx *ctx, int internal) { if (ctx->events.events) { event_copy = *((CtxEvent*)(ctx->events.events->data)); + // XXX : there is leakage of a string here.. + // we could reduce it to a non-growing leak.. by making a copy + // and letting normal free occur.. ctx_list_remove (&ctx->events.events, ctx->events.events->data); return &event_copy; } _ctx_idle_iteration (ctx); #if 1 - if (ctx->events.ctx_get_event_enabled==0) + if (!internal & (ctx->events.ctx_get_event_enabled==0)) { ctx->events.ctx_get_event_enabled = 1; ctx_queue_draw (ctx); @@ -38307,6 +39975,11 @@ CtxEvent *ctx_get_event (Ctx *ctx) return NULL; } +CtxEvent *ctx_get_event (Ctx *ctx) +{ + return ctx_get_event2 (ctx, 0); +} + static int _ctx_emit_cb (Ctx *ctx, CtxList *items, CtxEvent *event, CtxEventType type, float x, float y) { @@ -38992,7 +40665,7 @@ typedef struct CtxKeyMap { const char *shifted; } CtxKeyMap; -static CtxKeyMap intl_key_map[]= +static const CtxKeyMap intl_key_map[]= { {"`","`","~"}, {"1","1","!"}, @@ -39493,7 +41166,7 @@ void ctx_handle_events (Ctx *ctx) #if CTX_VT ctx_clients_handle_events (ctx); #endif - while (ctx_get_event (ctx)){} + while (ctx_get_event2 (ctx, 1)){} } @@ -39552,7 +41225,7 @@ static Mice* mrg_mice_this = &mice; static int mmm_evsource_mice_init () { - unsigned char reset[]={0xff}; + const unsigned char reset[]={0xff}; /* need to detect which event */ mrg_mice_this->prev_state = 0; @@ -40291,7 +41964,7 @@ typedef struct CtxRawKey{ } CtxRawKey; -static CtxRawKey raw_key_map[]= +static const CtxRawKey raw_key_map[]= { {KEY_F1, "F1","F1"}, {KEY_F2, "F2","F2"}, @@ -40652,7 +42325,7 @@ static inline void ctx_svg_arc_to (Ctx *ctx, float rx, float ry, // to offer headroom for multiplexing -#define CTX_REPORT_COL_ROW 0 +#define CTX_REPORT_COL_ROW 1 struct _CtxParser @@ -40675,6 +42348,8 @@ struct float numbers[CTX_PARSER_MAX_ARGS+1]; int n_numbers; int decimal; + int exponent; + int exp; CtxCode command; int expected_args; /* low digits are literal higher values carry special meaning */ @@ -40699,12 +42374,18 @@ struct CtxColorSpace color_space_slot; - void (*exit) (void *exit_data); - void *exit_data; + void (*frame_done) (void *frame_done_data); + void *frame_done_data; int (*set_prop)(void *prop_data, uint32_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; + +#if CTX_REPORT_COL_ROW + char *error; + int error_col; + int error_row; +#endif }; void @@ -40736,8 +42417,8 @@ ctx_parser_init (CtxParser *parser, int (*set_prop)(void *prop_data, uint32_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 + void (*frame_done) (void *frame_done_data), + void *frame_done_data ) { memset (parser, 0, sizeof (CtxParser) ); @@ -40751,8 +42432,8 @@ ctx_parser_init (CtxParser *parser, parser->cursor_y = cursor_y; parser->width = width; parser->height = height; - parser->exit = exit; - parser->exit_data = exit_data; + parser->frame_done = frame_done; + parser->frame_done_data = frame_done_data; parser->color_model = CTX_RGBA; parser->color_stroke = 0; parser->color_components = 4; @@ -40774,15 +42455,15 @@ CtxParser *ctx_parser_new ( int (*set_prop)(void *prop_data, uint32_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) + void (*frame_done) (void *frame_done_data), + void *frame_done_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); + frame_done, frame_done_data); } void ctx_parser_destroy (CtxParser *parser) @@ -40791,143 +42472,19 @@ void ctx_parser_destroy (CtxParser *parser) if (parser->holding) ctx_free (parser->holding); #endif + if (parser->error) + { + fprintf (stderr, "ctx parse error: %s\n", parser->error); + ctx_free (parser->error); + } ctx_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_START_FRAME: - case CTX_END_FRAME: - case CTX_RESTORE: - case CTX_STROKE: - case CTX_FILL: - case CTX_PAINT: - case CTX_DEFINE_FONT: - 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_EXTEND: - case CTX_FONT_SIZE: - case CTX_LINE_JOIN: - case CTX_LINE_CAP: - case CTX_LINE_WIDTH: - case CTX_LINE_DASH_OFFSET: - case CTX_LINE_HEIGHT: - case CTX_WRAP_LEFT: - case CTX_WRAP_RIGHT: - 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_ROUND_RECTANGLE: - return 5; - case CTX_ARC: - case CTX_CURVE_TO: - case CTX_REL_CURVE_TO: - case CTX_RADIAL_GRADIENT: - return 6; - case CTX_ARC_TO: - case CTX_REL_ARC_TO: - return 7; - case CTX_APPLY_TRANSFORM: - case CTX_SOURCE_TRANSFORM: - return 9; - 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; - 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 - return 0; - } -} static int ctx_parser_set_command (CtxParser *parser, CtxCode code) { - if (code < 150 && code >= 32) + if (code <= CTX_LAST_COMMAND && code >= 32) { parser->expected_args = ctx_arguments_for_code (code); parser->n_args = 0; @@ -40964,6 +42521,8 @@ static int ctx_parser_resolve_command (CtxParser *parser, const uint8_t *str) 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 'p': return ctx_parser_set_command (parser, CTX_STROKE_POS); + case 'F': return ctx_parser_set_command (parser, CTX_FEATHER); case 'H': return ctx_parser_set_command (parser, CTX_LINE_HEIGHT); case 'L': return ctx_parser_set_command (parser, CTX_WRAP_LEFT); case 'R': return ctx_parser_set_command (parser, CTX_WRAP_RIGHT); @@ -41002,6 +42561,7 @@ static int ctx_parser_resolve_command (CtxParser *parser, const uint8_t *str) case SQZ_stroke: ret = CTX_STROKE; break; case SQZ_fill: ret = CTX_FILL; break; case SQZ_paint: ret = CTX_PAINT; break; + case SQZ_preserve: ret = CTX_PRESERVE; break; case SQZ_endFrame: ret = CTX_END_FRAME; break; case SQZ_horLineTo: ret = CTX_HOR_LINE_TO; break; case SQZ_rotate: ret = CTX_ROTATE; break; @@ -41053,6 +42613,7 @@ static int ctx_parser_resolve_command (CtxParser *parser, const uint8_t *str) case SQZ_save: ret = CTX_SAVE; break; case SQZ_translate: ret = CTX_TRANSLATE; break; case SQZ_linearGradient: ret = CTX_LINEAR_GRADIENT; break; + case SQZ_conicGradient: ret = CTX_CONIC_GRADIENT; break; case SQZ_relHorLineTo: ret = CTX_REL_HOR_LINE_TO; break; case SQZ_relLineTo: ret = CTX_REL_LINE_TO; break; case SQZ_relMoveTo: ret = CTX_REL_MOVE_TO; break; @@ -41066,7 +42627,6 @@ static int ctx_parser_resolve_command (CtxParser *parser, const uint8_t *str) case SQZ_roundRectangle: ret = CTX_ROUND_RECTANGLE; break; case SQZ_relSmoothTo: ret = CTX_REL_SMOOTH_TO; break; case SQZ_relSmoothqTo: ret = CTX_REL_SMOOTHQ_TO; break; - case SQZ_strokeText: ret = CTX_STROKE_TEXT; break; case SQZ_strokeRect: ret = CTX_STROKE_RECT; break; case SQZ_fillRect: ret = CTX_FILL_RECT; break; case SQZ_relVerLineTo: ret = CTX_REL_VER_LINE_TO; break; @@ -41134,6 +42694,10 @@ static int ctx_parser_resolve_command (CtxParser *parser, const uint8_t *str) return ctx_parser_set_command (parser, CTX_LINE_WIDTH); case SQZ_lineDashOffset: return ctx_parser_set_command (parser, CTX_LINE_DASH_OFFSET); + case SQZ_strokePos: + return ctx_parser_set_command (parser, CTX_STROKE_POS); + case SQZ_feather: + return ctx_parser_set_command (parser, CTX_FEATHER); case SQZ_lineHeight: return ctx_parser_set_command (parser, CTX_LINE_HEIGHT); case SQZ_wrapLeft: @@ -41340,17 +42904,21 @@ static void ctx_parser_dispatch_command (CtxParser *parser) { CtxCode cmd = parser->command; Ctx *ctx = parser->ctx; + if (parser->error) + return; 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", + char *error = (char*)ctx_malloc (256); + sprintf (error, "ctx:%i:%i %c got %i instead of %i args\n", parser->line, parser->col, cmd, parser->n_numbers, parser->expected_args); + parser->error = error; #endif - //return; + return; } #define arg(a) (parser->numbers[a]) @@ -41581,22 +43149,28 @@ static void ctx_parser_dispatch_command (CtxParser *parser) { 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; + float ox = ctx_x (ctx); + float oy = ctx_y (ctx); + float ax = 2 * ox - cx; + float ay = 2 * oy - cy; + parser->pcx = arg(0) + ox; + parser->pcy = arg(1) + oy; + ctx_curve_to (ctx, ax, ay, arg(0) + ox, arg(1) + oy, + arg(2) + ox, arg(3) + oy); } break; case CTX_SMOOTH_TO: { - float ax = 2 * ctx_x (ctx) - parser->pcx; - float ay = 2 * ctx_y (ctx) - parser->pcy; + float cx = parser->pcx; + float cy = parser->pcy; + float ox = ctx_x (ctx); + float oy = ctx_y (ctx); + float ax = 2 * ox - cx; + float ay = 2 * oy - cy; ctx_curve_to (ctx, ax, ay, arg(0), arg(1), arg(2), arg(3) ); parser->pcx = arg(0); - parser->pcx = arg(1); + parser->pcy = arg(1); } break; case CTX_SMOOTHQ_TO: @@ -41608,8 +43182,8 @@ static void ctx_parser_dispatch_command (CtxParser *parser) { float x = ctx_x (ctx); float y = ctx_y (ctx); - parser->pcx = 2 * ctx_x (ctx) - parser->pcx; - parser->pcy = 2 * ctx_y (ctx) - parser->pcy; + parser->pcx = 2 * x - parser->pcx; + parser->pcy = 2 * y - parser->pcy; ctx_quad_to (ctx, parser->pcx, parser->pcy, arg(0) + x, arg(1) + y); } break; @@ -41708,7 +43282,6 @@ static void ctx_parser_dispatch_command (CtxParser *parser) 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) @@ -41724,10 +43297,8 @@ static void ctx_parser_dispatch_command (CtxParser *parser) * 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); } + ctx_text (ctx, c); + if (next_nl) { *next_nl = '\n'; // swap it newline back in @@ -41743,20 +43314,18 @@ static void ctx_parser_dispatch_command (CtxParser *parser) } } } - if (cmd == CTX_STROKE_TEXT) - { parser->command = CTX_STROKE_TEXT; } - else - { parser->command = CTX_TEXT; } + 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); + parser->pcx = ctx_x (ctx); + parser->pcy = ctx_y (ctx); break; case CTX_REL_MOVE_TO: ctx_rel_move_to (ctx, arg(0), arg(1) ); - parser->pcx += arg(0); - parser->pcy += arg(1); + parser->command = CTX_REL_LINE_TO; + parser->pcx = ctx_x (ctx); + parser->pcy = ctx_y (ctx); parser->left_margin = ctx_x (ctx); break; case CTX_LINE_WIDTH: @@ -41765,6 +43334,12 @@ static void ctx_parser_dispatch_command (CtxParser *parser) case CTX_LINE_DASH_OFFSET: ctx_line_dash_offset (ctx, arg(0)); break; + case CTX_STROKE_POS: + ctx_stroke_pos (ctx, arg(0)); + break; + case CTX_FEATHER: + ctx_feather (ctx, arg(0)); + break; case CTX_LINE_HEIGHT: ctx_line_height (ctx, arg(0)); break; @@ -41809,29 +43384,29 @@ static void ctx_parser_dispatch_command (CtxParser *parser) ctx_extend (ctx, (CtxExtend)arg(0)); break; case CTX_FILL_RULE: - ctx_fill_rule (ctx, (CtxFillRule) arg(0) ); + ctx_fill_rule (ctx, (CtxFillRule) arg(0)); break; case CTX_TEXT_ALIGN: - ctx_text_align (ctx, (CtxTextAlign) arg(0) ); + ctx_text_align (ctx, (CtxTextAlign) arg(0)); break; case CTX_TEXT_BASELINE: - ctx_text_baseline (ctx, (CtxTextBaseline) arg(0) ); + ctx_text_baseline (ctx, (CtxTextBaseline) arg(0)); break; case CTX_TEXT_DIRECTION: - ctx_text_direction (ctx, (CtxTextDirection) arg(0) ); + 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) ); + 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_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_rectangle (ctx, arg(0), arg(1), arg(2), arg(3)); ctx_stroke (ctx); break; case CTX_ROUND_RECTANGLE: @@ -41842,7 +43417,11 @@ static void ctx_parser_dispatch_command (CtxParser *parser) ctx_parser_set_size (parser, (int)arg(2), (int)arg(3), 0, 0); break; case CTX_LINEAR_GRADIENT: - ctx_linear_gradient (ctx, arg(0), arg(1), arg(2), arg(3) ); + ctx_linear_gradient (ctx, arg(0), arg(1), arg(2), arg(3)); + break; + case CTX_CONIC_GRADIENT: + // TODO - default arg3 to 1 if unspecified + ctx_conic_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) ); @@ -41866,14 +43445,15 @@ static void ctx_parser_dispatch_command (CtxParser *parser) case CTX_CLOSE_PATH: ctx_close_path (ctx); break; - case CTX_EXIT: - if (parser->exit) - { parser->exit (parser->exit_data); + case CTX_EXIT: // XXX // should be END_FRAME ? + if (parser->frame_done) + { parser->frame_done (parser->frame_done_data); return; } break; case CTX_END_FRAME: //ctx_flush (ctx); // XXX XXX flush only does things inside backends + ctx_end_frame (ctx); break; case CTX_START_FRAME: // XXX is it right to do things here? ctx_start_frame (ctx); @@ -41886,7 +43466,6 @@ static void ctx_parser_dispatch_command (CtxParser *parser) break; } #undef arg -// parser->n_numbers = 0; } static inline void ctx_parser_holding_append (CtxParser *parser, int byte) @@ -41938,6 +43517,8 @@ static void ctx_parser_transform_percent (CtxParser *parser, CtxCode code, int a break; } break; + case CTX_STROKE_POS: + case CTX_FEATHER: case CTX_FONT_SIZE: case CTX_MITER_LIMIT: case CTX_LINE_WIDTH: @@ -42013,6 +43594,8 @@ static void ctx_parser_transform_cell (CtxParser *parser, CtxCode code, int arg_ break; case CTX_MITER_LIMIT: case CTX_FONT_SIZE: + case CTX_STROKE_POS: + case CTX_FEATHER: case CTX_LINE_WIDTH: case CTX_LINE_DASH_OFFSET: { @@ -42046,16 +43629,20 @@ static void ctx_parser_transform_cell (CtxParser *parser, CtxCode code, int arg_ } } -// %h %v %m %M - -static void ctx_parser_number_done (CtxParser *parser) -{ - -} - static void ctx_parser_word_done (CtxParser *parser) { parser->holding[parser->pos]=0; + + if (parser->pos > 1 && (parser->holding[0]=='Z' || + parser->holding[0]=='z')) + { + ctx_close_path (parser->ctx); + memmove (parser->holding, parser->holding+1, parser->pos-1); + parser->pos--; + ctx_parser_word_done (parser); + return; + } + //int old_args = parser->expected_args; int command = ctx_parser_resolve_command (parser, parser->holding); if ((command >= 0 && command < 32) @@ -42096,10 +43683,11 @@ static void ctx_parser_word_done (CtxParser *parser) { ctx_parser_dispatch_command (parser); } + //parser->numbers[0] = 0; } else - { - /* interpret char by char */ + { +#if 0 uint8_t buf[16]=" "; for (int i = 0; parser->pos && parser->holding[i] > ' '; i++) { @@ -42119,6 +43707,8 @@ static void ctx_parser_word_done (CtxParser *parser) ctx_log ("unhandled command '%c'\n", buf[0]); } } +#endif + fprintf (stderr, "unhandled command '%s'\n", parser->holding); } } @@ -42151,6 +43741,22 @@ static void ctx_parser_string_done (CtxParser *parser) ctx_parser_dispatch_command (parser); } } +static inline void ctx_parser_finish_number (CtxParser *parser) +{ + if (parser->state == CTX_PARSER_NEGATIVE_NUMBER) + { parser->numbers[parser->n_numbers] *= -1; } + if (parser->exponent < 0) + { + for (int i = 0; i < parser->exp; i++) + parser->numbers[parser->n_numbers] *= 0.1f; + } + else if (parser->exponent > 0) + { + for (int i = 0; i < parser->exp; i++) + parser->numbers[parser->n_numbers] *= 10.0f; + } + parser->exponent = 0; +} static inline void ctx_parser_feed_byte (CtxParser *parser, char byte) { @@ -42256,6 +43862,7 @@ static inline void ctx_parser_feed_byte (CtxParser *parser, char byte) case '-': parser->state = CTX_PARSER_NEGATIVE_NUMBER; parser->numbers[parser->n_numbers] = 0; + parser->exponent = parser->decimal = 0; break; case '0': case '1': case '2': case '3': case '4': @@ -42263,11 +43870,13 @@ static inline void ctx_parser_feed_byte (CtxParser *parser, char byte) parser->state = CTX_PARSER_NUMBER; parser->numbers[parser->n_numbers] = 0; parser->numbers[parser->n_numbers] += (byte - '0'); + parser->exponent = parser->decimal = 0; break; case '.': parser->state = CTX_PARSER_NUMBER; parser->numbers[parser->n_numbers] = 0; + parser->exponent = 0; parser->decimal = 1; break; default: @@ -42280,6 +43889,7 @@ static inline void ctx_parser_feed_byte (CtxParser *parser, char byte) case CTX_PARSER_NUMBER: case CTX_PARSER_NEGATIVE_NUMBER: { + int do_process = 0; switch (byte) { case 0: case 1: case 2: case 3: case 4: case 5: @@ -42301,28 +43911,49 @@ static inline void ctx_parser_feed_byte (CtxParser *parser, char byte) case '{': case '}': case '=': - if (parser->state == CTX_PARSER_NEGATIVE_NUMBER) - { parser->numbers[parser->n_numbers] *= -1; } + ctx_parser_finish_number (parser); 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; + if (parser->exponent==1) + { + parser->exponent = -1; + } + else + { + ctx_parser_finish_number (parser); + parser->state = CTX_PARSER_NEGATIVE_NUMBER; + if (parser->n_numbers < CTX_PARSER_MAX_ARGS) + parser->n_numbers ++; + parser->numbers[parser->n_numbers] = 0; + parser->exponent = + parser->decimal = 0; + do_process = 1; + } break; case '.': - //if (parser->decimal) // TODO permit .13.32.43 to equivalent to .12 .32 .43 + if (parser->decimal){ + ctx_parser_finish_number (parser); + parser->state = CTX_PARSER_NUMBER; + if (parser->n_numbers < CTX_PARSER_MAX_ARGS) + parser->n_numbers ++; + parser->numbers[parser->n_numbers] = 0; + do_process = 1; + } + parser->exponent = 0; 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) + if (parser->exponent) + { + parser->exp *= 10; + parser->exp += (byte - '0'); + } + else if (parser->decimal) { parser->decimal *= 10; parser->numbers[parser->n_numbers] += (byte - '0') / (1.0f * parser->decimal); @@ -42334,8 +43965,7 @@ static inline void ctx_parser_feed_byte (CtxParser *parser, char byte) } break; case '@': // cells - if (parser->state == CTX_PARSER_NEGATIVE_NUMBER) - { parser->numbers[parser->n_numbers] *= -1; } + ctx_parser_finish_number (parser); { float fval = parser->numbers[parser->n_numbers]; ctx_parser_transform_cell (parser, parser->command, parser->n_numbers, &fval); @@ -42354,8 +43984,7 @@ static inline void ctx_parser_feed_byte (CtxParser *parser, char byte) parser->state = CTX_PARSER_NEUTRAL; break; case '^': // percent of height - if (parser->state == CTX_PARSER_NEGATIVE_NUMBER) - { parser->numbers[parser->n_numbers] *= -1; } + ctx_parser_finish_number (parser); { float fval = parser->numbers[parser->n_numbers]; ctx_parser_transform_percent_height (parser, parser->command, parser->n_numbers, &fval); @@ -42364,8 +43993,7 @@ static inline void ctx_parser_feed_byte (CtxParser *parser, char byte) parser->state = CTX_PARSER_NEUTRAL; break; case '~': // percent of width - if (parser->state == CTX_PARSER_NEGATIVE_NUMBER) - { parser->numbers[parser->n_numbers] *= -1; } + ctx_parser_finish_number (parser); { float fval = parser->numbers[parser->n_numbers]; ctx_parser_transform_percent_width (parser, parser->command, parser->n_numbers, &fval); @@ -42373,19 +44001,28 @@ static inline void ctx_parser_feed_byte (CtxParser *parser, char byte) } parser->state = CTX_PARSER_NEUTRAL; break; + case 'e': + case 'E': + parser->exponent = 1; + parser->exp = 0; + break; default: - if (parser->state == CTX_PARSER_NEGATIVE_NUMBER) - { parser->numbers[parser->n_numbers] *= -1; } + ctx_parser_finish_number (parser); + 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)) + if (do_process || + ((parser->state != CTX_PARSER_NUMBER) && + (parser->state != CTX_PARSER_NEGATIVE_NUMBER))) { - parser->n_numbers ++; - ctx_parser_number_done (parser); + if (!do_process) + { + if (parser->n_numbers < CTX_PARSER_MAX_ARGS) + parser->n_numbers ++; + } if (parser->n_numbers == parser->expected_args || parser->expected_args == CTX_ARG_COLLECT_NUMBERS || @@ -42407,13 +44044,14 @@ static inline void ctx_parser_feed_byte (CtxParser *parser, char byte) default: parser->n_numbers = 0; parser->n_args = 0; + parser->numbers[0] = parser->numbers[tmp1]; break; } parser->expected_args = tmp4; } - if (parser->n_numbers > CTX_PARSER_MAX_ARGS) - { parser->n_numbers = CTX_PARSER_MAX_ARGS; - } + //if (parser->n_numbers > CTX_PARSER_MAX_ARGS) + // { parser->n_numbers = CTX_PARSER_MAX_ARGS; + // } } } break; @@ -42435,6 +44073,7 @@ static inline void ctx_parser_feed_byte (CtxParser *parser, char byte) case '-': parser->state = CTX_PARSER_NEGATIVE_NUMBER; parser->numbers[parser->n_numbers] = 0; + parser->exponent = parser->decimal = 0; break; case '0': case '1': case '2': case '3': case '4': @@ -42442,11 +44081,13 @@ static inline void ctx_parser_feed_byte (CtxParser *parser, char byte) parser->state = CTX_PARSER_NUMBER; parser->numbers[parser->n_numbers] = 0; parser->numbers[parser->n_numbers] += (byte - '0'); + parser->exponent = parser->decimal = 0; break; case '.': parser->state = CTX_PARSER_NUMBER; parser->numbers[parser->n_numbers] = 0; + parser->exponent = 0; parser->decimal = 1; break; default: @@ -42565,8 +44206,9 @@ ctx_parse (Ctx *ctx, const char *string) } CTX_EXPORT void -ctx_parse2 (Ctx *ctx, const char *string, float *scene_elapsed_time, - int *scene_no_p) +ctx_parse_animation (Ctx *ctx, const char *string, + float *scene_elapsed_time, + int *scene_no_p) { float time = *scene_elapsed_time; int scene_no = *scene_no_p; @@ -42576,16 +44218,64 @@ ctx_parse2 (Ctx *ctx, const char *string, float *scene_elapsed_time, int i; -again: +//again: i = 0; - // XXX : this doesn't work when there are [ 's in the text + // XXX : this doesn't work when there are [ or ('s in text int scene_pos = 0; int last_scene = 0; + int scene_start = 0; + int got_duration = 0; + + { + int start = 0; + for (; string[i]; i++) + { + if (!strncmp (&string[i], "newPage", 7)) + { + if (scene_pos == scene_no) + { + if (scene_duration < time) + { + scene_no ++; + (*scene_no_p)++; + *scene_elapsed_time = time = time- scene_duration; + } + else + { + scene_start = start; + } + } + + scene_pos++; + last_scene = scene_pos; + start = i + 7; + scene_duration = 5.0f; + got_duration = 0; + } + + if (!got_duration && !strncmp (&string[i], "duration ", 9)) + { + scene_duration = _ctx_parse_float (&string[i+9], NULL); + got_duration = 1; + } + } + } + i = scene_start; + if (last_scene) + last_scene --; +#if 0 { int in_scene_marker = 0; float duration = -1; + + // go through the string, + // + // post: + // last_scene = highest scene seen + // i = byte offset of start of scene + // scene_duration = duration of current scene for (; string[i]; i++) { char p = string[i]; @@ -42603,7 +44293,7 @@ ctx_parse2 (Ctx *ctx, const char *string, float *scene_elapsed_time, { scene_no ++; (*scene_no_p)++; - *scene_elapsed_time = time = 0; + *scene_elapsed_time = time = time- scene_duration; } else { @@ -42627,12 +44317,14 @@ ctx_parse2 (Ctx *ctx, const char *string, float *scene_elapsed_time, } } } +#endif if (scene_no > last_scene) { scene_no = 0; (*scene_no_p) = 0; - goto again; + return; + //goto again; } if (scene_no == 0 && last_scene==0 && string[i]==0) @@ -42649,7 +44341,7 @@ ctx_parse2 (Ctx *ctx, const char *string, float *scene_elapsed_time, char p = string[i]; if (in_var == 0) { - if (p == '[') + if (!strncmp (&string[i], "newPage", 7)) break; else if (p == '(') { @@ -42730,6 +44422,7 @@ ctx_parse2 (Ctx *ctx, const char *string, float *scene_elapsed_time, val = _ctx_parse_float (eq+1, &ep); keys[n_keys] = key; + if (n_keys < MAX_KEY_FRAMES-1) values[n_keys++] = val; i+=(ep-sp)-1; @@ -42816,7 +44509,7 @@ static inline void _ctx_string_append_byte (CtxString *string, char val) { char *old = string->str; int old_len = string->allocated_length; - string->allocated_length = CTX_MAX (string->allocated_length * 1.5, string->length + 2); + string->allocated_length = CTX_MAX ((int)(string->allocated_length * 1.5f), string->length + 2); string->str = (char*)ctx_realloc (old, old_len, string->allocated_length); } string->str[string->length++] = val; @@ -43240,13 +44933,12 @@ static void ctx_drawlist_backend_destroy (CtxBackend *backend) ctx_free (backend); } -static void ctx_update_current_path (Ctx *ctx, CtxEntry *entry) +static void ctx_update_current_path (Ctx *ctx, const CtxEntry *entry) { #if CTX_CURRENT_PATH switch (entry->code) { case CTX_TEXT: - case CTX_STROKE_TEXT: case CTX_BEGIN_PATH: ctx->current_path.count = 0; break; @@ -43283,8 +44975,9 @@ static void ctx_update_current_path (Ctx *ctx, CtxEntry *entry) } static void -ctx_drawlist_process (Ctx *ctx, CtxEntry *entry) +ctx_drawlist_process (Ctx *ctx, const CtxCommand *command) { + CtxEntry *entry = (CtxEntry*)command; #if CTX_CURRENT_PATH ctx_update_current_path (ctx, entry); #endif @@ -43305,7 +44998,7 @@ static CtxBackend *ctx_drawlist_backend_new (void) // initializing the extra members - which might most // often be a false report - we ass slack since it is // "only" ~ 40 bytes per instance. - backend->process = (void(*)(Ctx *a, CtxCommand *c))ctx_drawlist_process; + backend->process = ctx_drawlist_process; backend->destroy = (void(*)(void *a))ctx_drawlist_backend_destroy; backend->type = CTX_BACKEND_DRAWLIST; return backend; @@ -43470,9 +45163,9 @@ ctx_device_corners_to_user_rect (CtxState *state, } static void -ctx_hasher_process (Ctx *ctx, CtxCommand *command) +ctx_hasher_process (Ctx *ctx, const CtxCommand *command) { - CtxEntry *entry = &command->entry; + const CtxEntry *entry = &command->entry; CtxRasterizer *rasterizer = (CtxRasterizer *) ctx->backend; CtxHasher *hasher = (CtxHasher*) ctx->backend; CtxState *state = rasterizer->state; @@ -43524,39 +45217,13 @@ ctx_hasher_process (Ctx *ctx, CtxCommand *command) // murmur3_32_process(&murmur, (unsigned char*)&color, 4); #endif murmur3_32_process(&murmur, (unsigned char*)&shape_rect, sizeof (CtxIntRectangle)); - _ctx_add_hash (hasher, &shape_rect, murmur3_32_finalize (&murmur)); - ctx_rasterizer_rel_move_to (rasterizer, width, 0); - } - ctx_rasterizer_reset (rasterizer); - break; - case CTX_STROKE_TEXT: { - CtxMurmur murmur; - const char *str = ctx_arg_string(); - memcpy (&murmur, &hasher->murmur_stroke[hasher->source_level], sizeof (CtxMurmur)); - float width = ctx_text_width (rasterizer->backend.ctx, str); - float height = ctx_get_font_size (rasterizer->backend.ctx); - - CtxIntRectangle shape_rect; - - float tx = rasterizer->x; - float ty = rasterizer->y - height * 1.2f; - float tx2 = tx+width; - float ty2 = ty+height * (ctx_str_count_lines (str) + 1.5f); - ctx_device_corners_to_user_rect (rasterizer->state, tx,ty,tx2,ty2, &shape_rect); + float f = rasterizer->state->gstate.global_alpha_f; + murmur3_32_process(&murmur, (uint8_t*)&f, sizeof(float)); + } -#if 0 - uint32_t color; - ctx_color_get_rgba8 (rasterizer->state, &rasterizer->state->gstate.source_stroke.color, (uint8_t*)(&color)); -#endif - murmur3_32_process(&murmur, (unsigned char*)ctx_arg_string(), ctx_strlen (ctx_arg_string())); -#if 1 - murmur3_32_process(&murmur, (unsigned char*)(&rasterizer->state->gstate.transform), sizeof (rasterizer->state->gstate.transform)); - // murmur3_32_process(&murmur, (unsigned char*)&color, 4); -#endif - murmur3_32_process(&murmur, (unsigned char*)&shape_rect, sizeof (CtxIntRectangle)); _ctx_add_hash (hasher, &shape_rect, murmur3_32_finalize (&murmur)); ctx_rasterizer_rel_move_to (rasterizer, width, 0); @@ -43591,6 +45258,14 @@ ctx_hasher_process (Ctx *ctx, CtxCommand *command) murmur3_32_process(&murmur, string, ctx_strlen ((const char*)string)); murmur3_32_process(&murmur, (unsigned char*)(&rasterizer->state->gstate.transform), sizeof (rasterizer->state->gstate.transform)); murmur3_32_process(&murmur, (unsigned char*)&shape_rect, sizeof (CtxIntRectangle)); + + + { + float f = rasterizer->state->gstate.global_alpha_f; + murmur3_32_process(&murmur, (uint8_t*)&f, sizeof(float)); + } + + _ctx_add_hash (hasher, &shape_rect, murmur3_32_finalize (&murmur)); ctx_rasterizer_rel_move_to (rasterizer, width, 0); @@ -43648,6 +45323,10 @@ ctx_hasher_process (Ctx *ctx, CtxCommand *command) int e = rasterizer->state->gstate.extend; murmur3_32_process(&murmur, (uint8_t*)&e, sizeof(int)); } + { + float f = rasterizer->state->gstate.global_alpha_f; + murmur3_32_process(&murmur, (uint8_t*)&f, sizeof(float)); + } { uint32_t color; ctx_color_get_rgba8 (rasterizer->state, &rasterizer->state->gstate.source_fill.color, (uint8_t*)(&color)); @@ -43688,6 +45367,8 @@ ctx_hasher_process (Ctx *ctx, CtxCommand *command) { float f; int i; + f = rasterizer->state->gstate.global_alpha_f; + murmur3_32_process(&murmur, (uint8_t*)&f, sizeof(float)); f = rasterizer->state->gstate.line_width; murmur3_32_process(&murmur, (uint8_t*)&f, sizeof(float)); i = rasterizer->state->gstate.line_cap; @@ -43811,7 +45492,7 @@ ctx_hasher_process (Ctx *ctx, CtxCommand *command) ctx_rasterizer_reset (rasterizer); break; case CTX_CLOSE_PATH: - ctx_rasterizer_finish_shape (rasterizer); + ctx_rasterizer_close_path (rasterizer); break; case CTX_DEFINE_TEXTURE: { @@ -43849,6 +45530,7 @@ ctx_hasher_process (Ctx *ctx, CtxCommand *command) } } break; + case CTX_CONIC_GRADIENT: // XXX: good enough? case CTX_LINEAR_GRADIENT: murmur3_32_init (&hasher->murmur_fill[hasher->source_level]); murmur3_32_process(&hasher->murmur_fill[hasher->source_level], &rasterizer->state->gstate.global_alpha_u8, 1); @@ -43957,6 +45639,7 @@ uint32_t ctx_hasher_get_hash (Ctx *ctx, int col, int row) if (row >= hasher->rows) row = hasher->rows-1; if (col >= hasher->cols) col = hasher->cols-1; + if (hasher->prev_command >= 0) hasher->drawlist->entries[hasher->prev_command].data.u32[1] = 0xffffffff; return hasher->hashes[(row*hasher->cols+col)]; @@ -44267,10 +45950,10 @@ ctx_pdf_apply_transform (Ctx *ctx, float a, float b, float c, float d, float e, } static void -ctx_pdf_process (Ctx *ctx, CtxCommand *c) +ctx_pdf_process (Ctx *ctx, const CtxCommand *c) { CtxPDF *pdf = (void*)ctx->backend; - CtxEntry *entry = (CtxEntry *) &c->entry; + const CtxEntry *entry = (CtxEntry *) &c->entry; CtxState *state = &pdf->state; CtxDrawlist *preserved = NULL; @@ -44645,7 +46328,6 @@ ctx_pdf_process (Ctx *ctx, CtxCommand *c) } break; case CTX_CONT: - case CTX_EDGE: case CTX_DATA: case CTX_DATA_REV: case CTX_END_FRAME: @@ -45187,7 +46869,7 @@ static char *decode_ctx (const char *encoded, int enc_len, const char *prev, int } #endif -#define CTX_START_STRING "U\n" // or " start_frame " +#define CTX_START_STRING ":\n" // or " start_frame " #define CTX_END_STRING "\nX" // or "\ndone" #define CTX_END_STRING2 "\n" @@ -45412,7 +47094,7 @@ Ctx *ctx_new_ctx (int width, int height) backend->end_frame = ctx_ctx_end_frame; backend->type = CTX_BACKEND_CTX; backend->destroy = (void(*)(void *))ctx_ctx_destroy; - backend->process = (void*)ctx_drawlist_process; + backend->process = (void(*)(Ctx *a, const CtxCommand *c))ctx_drawlist_process; backend->consume_events = ctx_ctx_consume_events; ctx_set_backend (ctx, ctxctx); ctx_set_size (ctx, width, height); @@ -45424,6 +47106,10 @@ void ctx_ctx_pcm (Ctx *ctx); #endif +// TODO : tiled renderer should have a work-queue or work-stealing +// right now one core might be working while all +// others are idle + #if CTX_TILED static inline int ctx_tiled_threads_done (CtxTiled *tiled) @@ -45507,6 +47193,7 @@ static void ctx_tiled_end_frame (Ctx *ctx) 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)] = tiled->frame; tiled->tile_affinity[row * CTX_HASH_COLS + col] = 1; dirty_tiles++; } @@ -47641,13 +49328,13 @@ void ctx_sdl_consume_events (Ctx *ctx) CtxTiled *tiled = (void*)backend; CtxSDL *sdl = (void*)backend; SDL_Event event; - int got_events = 0; + //int got_events = 0; ctx_sdl_show_frame (sdl, 0); while (SDL_PollEvent (&event)) { - got_events ++; + //got_events ++; switch (event.type) { case SDL_MOUSEBUTTONDOWN: @@ -47754,7 +49441,7 @@ void ctx_sdl_consume_events (Ctx *ctx) } break; case SDL_QUIT: - ctx_quit (ctx); + ctx_exit (ctx); break; case SDL_DROPFILE: ctx_pointer_drop (ctx, x, y, 0, 0, event.drop.file); @@ -48668,7 +50355,7 @@ ctx_is_half_opaque (CtxRasterizer *rasterizer) } inline static void ctx_term_process (Ctx *ctx, - CtxCommand *command) + const CtxCommand *command) { CtxTerm *term = (void*)ctx->backend; @@ -48739,7 +50426,7 @@ inline static void ctx_term_process (Ctx *ctx, */ ctx_interpret_style (&ctx->state, &command->entry, ctx); ctx_interpret_transforms (&ctx->state, &command->entry, ctx); - ctx_interpret_pos (&ctx->state, &command->entry, ctx); + ctx_interpret_pos_bare (&ctx->state, &command->entry, ctx); /* directly forward */ ctx_process (term->host, &command->entry); @@ -48947,7 +50634,7 @@ struct _CtxTermImg }; inline static void ctx_termimg_process (Ctx *ctx, - CtxCommand *command) + const CtxCommand *command) { CtxTermImg *termimg = (void*)ctx->backend; #if CTX_CURRENT_PATH @@ -49473,6 +51160,14 @@ ctx_cb_extent (Ctx *ctx, float *x0, float *y0, float *x1, float *y1) if (y1) *y1 = ctx_cb_y1 (ctx); } +static void +ctx_cb_start_frame (Ctx *ctx) +{ +#if CTX_EVENTS + ctx_handle_events (ctx); +#endif +} + static void ctx_cb_end_frame (Ctx *ctx) { @@ -49753,6 +51448,13 @@ ctx_cb_end_frame (Ctx *ctx) } if (cb_backend->update_fb) cb_backend->update_fb (ctx, cb_backend->update_fb_user_data); + +} + +static void ctx_cb_destroy (void *data) +{ + // XXX leaking ->fb if it was dynamically allocated + free (data); } Ctx *ctx_new_cb (int width, int height, CtxPixelFormat format, @@ -49768,7 +51470,9 @@ Ctx *ctx_new_cb (int width, int height, CtxPixelFormat format, Ctx *ctx = ctx_new_drawlist (width, height); CtxBackend *backend = (CtxBackend*)ctx_calloc (sizeof (CtxCbBackend), 1); CtxCbBackend *cb_backend = (CtxCbBackend*)backend; + backend->start_frame = ctx_cb_start_frame; backend->end_frame = ctx_cb_end_frame; + backend->destroy = ctx_cb_destroy; cb_backend->format = format; cb_backend->fb = (uint16_t*)scratch_fb; cb_backend->set_pixels = set_pixels; @@ -49792,7 +51496,7 @@ Ctx *ctx_new_cb (int width, int height, CtxPixelFormat format, #if CTX_TFT_ESPI -static void ctx_tft_set_pixels (Ctx *ctx, void *user_data, int x, int y, int w, int h, void *buf) +void ctx_set_pixels (Ctx *ctx, void *user_data, int x, int y, int w, int h, void *buf) { TFT_eSPI *tft = (TFT_eSPI*)user_data; tft->pushRect (x, y, w, h, (uint16_t*)buf); @@ -49805,7 +51509,7 @@ Ctx *ctx_new_tft (TFT_eSPI *tft, { return ctx_new_cb (tft->width(), tft->height(), CTX_FORMAT_RGB565_BYTESWAPPED, - ctx_tft_set_pixels, + ctx_set_pixels, tft, NULL, NULL, @@ -49889,21 +51593,21 @@ int update_fb (Ctx *ctx, void *user_data) if (!canvas.regevents) { - canvas.onmousedown = function (e){ + canvas.onpointerdown = function (e){ var loc = windowToCanvas (canvas, e.clientX, e.clientY); setValue(_pointer_x, loc.x, "float"); setValue(_pointer_y, loc.y, "float"); setValue(_pointer_down, 1, "i32"); e.stopPropagate=1; }; - canvas.onmouseup = function (e){ + canvas.onpointerup = function (e){ var loc = windowToCanvas (canvas, e.clientX, e.clientY); setValue(_pointer_x, loc.x, "float"); setValue(_pointer_y, loc.y, "float"); setValue(_pointer_down, 0, "i32"); e.stopPropagate=1; }; - canvas.onmousemove = function (e){ + canvas.onpointermove = function (e){ var loc = windowToCanvas (canvas, e.clientX, e.clientY); setValue(_pointer_x, loc.x, "float"); setValue(_pointer_y, loc.y, "float"); @@ -49924,10 +51628,8 @@ int update_fb (Ctx *ctx, void *user_data) } ); -#ifdef EMSCRIPTEN -//#ifdef ASYNCIFY - emscripten_sleep(0); -//#endif +#ifndef __EMSCRIPTEN_PTHREADS__ + emscripten_sleep(1); #endif int ret = 0; @@ -49974,7 +51676,8 @@ int update_fb (Ctx *ctx, void *user_data) EMSCRIPTEN_KEEPALIVE uint8_t wasm_scratch[1024*1024*4]; -static void set_pixels (Ctx *ctx, void *user_data, int x0, int y0, int w, int h, void *buf) +CTX_EXPORT +void wctx_set_pixels (Ctx *ctx, void *user_data, int x0, int y0, int w, int h, void *buf) { uint8_t *src = (uint8_t*)buf; int in_w = w; @@ -50005,9 +51708,7 @@ static void set_pixels (Ctx *ctx, void *user_data, int x0, int y0, int w, int h, var h = $3; var canvas = document.getElementById('c'); var context = canvas.getContext('2d'); - var _ctx = _ctx_wasm_get_context(0); // we presume an earlier - // call to have passed - // the memory budget + var _ctx = _ctx_host(); const offset = _get_fb(canvas.width, canvas.height); const imgData = context.createImageData(w,h); @@ -50029,25 +51730,17 @@ static void set_pixels (Ctx *ctx, void *user_data, int x0, int y0, int w, int h, } -#if 0 -int wasm_damage_control = 0; - -CTX_EXPORT -void wasm_set_damage_control(int val) -{ - wasm_damage_control = val; -} -#endif - void ctx_wasm_reset (void) { if (fb) free (fb); fb = NULL; em_ctx = NULL; } -Ctx *ctx_wasm_get_context (int memory_budget) +CTX_EXPORT +Ctx *ctx_host (void) { - + int memory_budget = 64 * 1024; + if (em_ctx) return em_ctx; EM_ASM( {var canvas = document.getElementById('c'); @@ -50075,7 +51768,7 @@ EM_ASM( if (!em_ctx){ em_ctx = ctx_new_cb (width, height, CTX_FORMAT_RGB565_BYTESWAPPED, - set_pixels, + wctx_set_pixels, NULL, update_fb, NULL, @@ -50100,6 +51793,12 @@ EM_ASM( return em_ctx; } +int ctx_host_audio_init (int hz, CtxPCM format) +{ + // NYI, but added to make things link + return 0; +} + #endif static CtxFont ctx_fonts[CTX_MAX_FONTS];// = NULL; @@ -50123,11 +51822,14 @@ static inline int ctx_font_is_monospaced (CtxFont *font) #if CTX_FONT_ENGINE_STB static float -ctx_glyph_width_stb (CtxFont *font, Ctx *ctx, uint32_t unichar); +ctx_glyph_width_stb (CtxFont *font, Ctx *ctx, int glyph_id); 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); +ctx_glyph_stb (CtxFont *font, Ctx *ctx, int glyph_id, int stroke); + +static int +ctx_glyph_stb_find (CtxFont *font, Ctx *ctx, uint32_t unichar); CtxFontEngine ctx_font_engine_stb = { @@ -50138,6 +51840,7 @@ CtxFontEngine ctx_font_engine_stb = ctx_glyph_stb, ctx_glyph_width_stb, ctx_glyph_kern_stb, + ctx_glyph_stb_find, }; @@ -50176,8 +51879,8 @@ ctx_load_font_ttf (const char *name, const void *ttf_contents, int length) ctx_fonts[ctx_font_count].engine = &ctx_font_engine_stb; CtxFont *font = &ctx_fonts[ctx_font_count]; - if (font->engine->glyph_width (font, NULL, 'O') == - font->engine->glyph_width (font, NULL, 'I')) + if (font->engine->glyph_width (font, NULL, font->engine->glyph_lookup(font, NULL, 'O')) == + font->engine->glyph_width (font, NULL, font->engine->glyph_lookup(font, NULL, 'I'))) { font->monospaced = 1; } @@ -50189,6 +51892,7 @@ ctx_load_font_ttf (const char *name, const void *ttf_contents, int length) } #if CTX_FONTS_FROM_FILE + int ctx_load_font_ttf_file (const char *name, const char *path) { @@ -50206,7 +51910,7 @@ ctx_load_font_ttf_file (const char *name, const char *path) #endif static int -ctx_glyph_stb_find (Ctx *ctx, CtxFont *font, uint32_t unichar) +ctx_glyph_stb_find (CtxFont *font, Ctx *ctx, uint32_t unichar) { stbtt_fontinfo *ttf_info = &font->stb.ttf_info; @@ -50236,7 +51940,7 @@ ctx_glyph_stb_find (Ctx *ctx, CtxFont *font, uint32_t unichar) } static float -ctx_glyph_width_stb (CtxFont *font, Ctx *ctx, uint32_t unichar) +ctx_glyph_width_stb (CtxFont *font, Ctx *ctx, int glyph) { stbtt_fontinfo *ttf_info = &font->stb.ttf_info; float font_size = 1.0f; @@ -50244,7 +51948,6 @@ ctx_glyph_width_stb (CtxFont *font, Ctx *ctx, uint32_t unichar) font_size = ctx->state.gstate.font_size; float scale = stbtt_ScaleForPixelHeight (ttf_info, font_size); int advance, lsb; - int glyph = ctx_glyph_stb_find (ctx, font, unichar); #if CTX_EVENTS if (ctx && ctx_backend_type (ctx) == CTX_BACKEND_TERM && ctx_fabsf(3.0f - font_size) < 0.03f) @@ -50263,16 +51966,17 @@ ctx_glyph_kern_stb (CtxFont *font, Ctx *ctx, uint32_t unicharA, 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 glyphA = ctx_glyph_stb_find (ctx, font, unicharA); - int glyphB = ctx_glyph_stb_find (ctx, font, unicharB); - return stbtt_GetGlyphKernAdvance (ttf_info, glyphA, glyphB) * scale; + int glyphA = ctx_glyph_stb_find (font, ctx, unicharA); + int glyphB = ctx_glyph_stb_find (font, ctx, unicharB); + float ret = stbtt_GetGlyphKernAdvance (ttf_info, glyphA, glyphB) * scale; + return ret; } static int -ctx_glyph_stb (CtxFont *font, Ctx *ctx, uint32_t unichar, int stroke) +ctx_glyph_stb (CtxFont *font, Ctx *ctx, int glyph, int stroke) { stbtt_fontinfo *ttf_info = &font->stb.ttf_info; - int glyph = ctx_glyph_stb_find (ctx, font, unichar); + //int glyph = ctx_glyph_stb_find (font, ctx, unichar); if (glyph==0) { return -1; } float font_size = ctx->state.gstate.font_size; @@ -50342,7 +52046,7 @@ ctx_glyph_find_next (CtxFont *font, Ctx *ctx, int offset) return 0; } -static int ctx_glyph_find_ctx (CtxFont *font, Ctx *ctx, uint32_t unichar) +static int ctx_glyph_lookup_ctx (CtxFont *font, Ctx *ctx, uint32_t unichar) { #if CTX_GLYPH_CACHE uint32_t hash = ((((size_t)(font) * 23) ^ unichar) * 17) % @@ -50391,12 +52095,20 @@ static int ctx_glyph_find_ctx (CtxFont *font, Ctx *ctx, uint32_t unichar) return -1; } +static int ctx_glyph_lookup_ctx2 (CtxFont *font, Ctx *ctx, uint32_t unichar) +{ + if (ctx_glyph_lookup_ctx(font,ctx,unichar)>=0) + return unichar; + return -1; +} + static float ctx_glyph_kern_ctx (CtxFont *font, Ctx *ctx, uint32_t unicharA, uint32_t unicharB) { + return 0.0f; // XXX - hack disabling kerning in this backend for now float font_size = ctx->state.gstate.font_size; - int first_kern = ctx_glyph_find_ctx (font, ctx, unicharA); + int first_kern = ctx_glyph_lookup_ctx (font, ctx, unicharA); // XXX ? why doe + 1 make this work if (first_kern < 0) return 0.0; #if CTX_EVENTS @@ -50405,13 +52117,15 @@ ctx_glyph_kern_ctx (CtxFont *font, Ctx *ctx, uint32_t unicharA, uint32_t unichar #endif int length = ctx_font_get_length (font); - for (int i = first_kern + 1; i < length; i++) + for (int i = first_kern+1; i < 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.0f * font_size / CTX_BAKE_FONT_SIZE; } + if (entry->data.u16[1] == unicharB) + { + return entry->data.s32[1] / 255.0f * font_size / CTX_BAKE_FONT_SIZE; + } } if (entry->code == CTX_DEFINE_GLYPH) return 0.0; @@ -50420,7 +52134,7 @@ ctx_glyph_kern_ctx (CtxFont *font, Ctx *ctx, uint32_t unicharA, uint32_t unichar } static float -ctx_glyph_width_ctx (CtxFont *font, Ctx *ctx, uint32_t unichar) +ctx_glyph_width_ctx (CtxFont *font, Ctx *ctx, int unichar) { float font_size = 1.0f; if (ctx) @@ -50428,7 +52142,7 @@ ctx_glyph_width_ctx (CtxFont *font, Ctx *ctx, uint32_t unichar) CtxState *state = &ctx->state; font_size = state->gstate.font_size; } - int start = ctx_glyph_find_ctx (font, ctx, unichar); + int start = ctx_glyph_lookup_ctx (font, ctx, unichar); if (start < 0) { return 0.0; } // XXX : fallback @@ -50451,7 +52165,7 @@ ctx_glyph_width_ctx (CtxFont *font, Ctx *ctx, uint32_t unichar) } static int -ctx_glyph_drawlist (CtxFont *font, Ctx *ctx, CtxDrawlist *drawlist, uint32_t unichar, int stroke) +ctx_glyph_drawlist (CtxFont *font, Ctx *ctx, CtxDrawlist *drawlist, int unichar, int stroke) { CtxState *state = &ctx->state; CtxIterator iterator; @@ -50465,12 +52179,13 @@ ctx_glyph_drawlist (CtxFont *font, Ctx *ctx, CtxDrawlist *drawlist, uint32_t uni if (font->type == 0) #endif { - start = ctx_glyph_find_ctx (font, ctx, unichar); + start = ctx_glyph_lookup_ctx (font, ctx, unichar); if (start < 0) { return -1; } // XXX : fallback glyph } ctx_iterator_init (&iterator, drawlist, start, CTX_ITERATOR_EXPAND_BITPACK); - CtxCommand *command; + CtxCommand *command; + void (*process) (Ctx *ctx, const CtxCommand *entry) = ctx->process; /* XXX : do a binary search instead of a linear search */ while ( (command= ctx_iterator_next (&iterator) ) ) @@ -50489,9 +52204,8 @@ ctx_glyph_drawlist (CtxFont *font, Ctx *ctx, CtxDrawlist *drawlist, uint32_t uni if (ctx->backend && ((CtxRasterizer*)(ctx->backend))->in_shadow) { ctx_rasterizer_shadow_fill ((CtxRasterizer*)ctx->backend); - ((CtxRasterizer*)(ctx->backend))->in_shadow = 1; } - else + #endif #endif ctx_fill (ctx); @@ -50500,9 +52214,9 @@ ctx_glyph_drawlist (CtxFont *font, Ctx *ctx, CtxDrawlist *drawlist, uint32_t uni ctx_restore (ctx); return 0; } - ctx_process (ctx, entry); + process (ctx, (CtxCommand*)entry); } - else if (entry->code == CTX_DEFINE_GLYPH && entry->data.u32[0] == unichar) + else if (entry->code == CTX_DEFINE_GLYPH && entry->data.u32[0] == (unsigned)unichar) { in_glyph = 1; ctx_save (ctx); @@ -50524,9 +52238,8 @@ ctx_glyph_drawlist (CtxFont *font, Ctx *ctx, CtxDrawlist *drawlist, uint32_t uni if (ctx->backend && ((CtxRasterizer*)(ctx->backend))->in_shadow) { ctx_rasterizer_shadow_fill ((CtxRasterizer*)ctx->backend); - ((CtxRasterizer*)(ctx->backend))->in_shadow = 1; } - else + #endif #endif { @@ -50538,7 +52251,7 @@ ctx_glyph_drawlist (CtxFont *font, Ctx *ctx, CtxDrawlist *drawlist, uint32_t uni } static int -ctx_glyph_ctx (CtxFont *font, Ctx *ctx, uint32_t unichar, int stroke) +ctx_glyph_ctx (CtxFont *font, Ctx *ctx, int glyph_id, int stroke) { CtxDrawlist drawlist; drawlist.entries = font->ctx.data; @@ -50546,7 +52259,7 @@ ctx_glyph_ctx (CtxFont *font, Ctx *ctx, uint32_t unichar, int stroke) drawlist.count = length; drawlist.size = length; drawlist.flags = CTX_DRAWLIST_DOESNT_OWN_ENTRIES; - return ctx_glyph_drawlist (font, ctx, &drawlist, unichar, stroke); + return ctx_glyph_drawlist (font, ctx, &drawlist, glyph_id, stroke); } #if 0 @@ -50580,6 +52293,7 @@ static CtxFontEngine ctx_font_engine_ctx = ctx_glyph_ctx, ctx_glyph_width_ctx, ctx_glyph_kern_ctx, + ctx_glyph_lookup_ctx2 }; #endif @@ -50709,6 +52423,9 @@ ctx_glyph_ctx_fs (CtxFont *font, Ctx *ctx, uint32_t unichar, int stroke) int ctx_load_font_ctx_fs (const char *name, const void *data, int length); +static int ctx_glyph_lookup_ctx_fs (CtxFont *font, Ctx *ctx, uint32_t unichar) +{ return unichar; +} static CtxFontEngine ctx_font_engine_ctx_fs = { #if CTX_FONTS_FROM_FILE @@ -50718,6 +52435,7 @@ static CtxFontEngine ctx_font_engine_ctx_fs = ctx_glyph_ctx_fs, ctx_glyph_width_ctx_fs, ctx_glyph_kern_ctx_fs, + ctx_glyph_lookup_ctx_fs }; int @@ -50740,21 +52458,208 @@ ctx_load_font_ctx_fs (const char *name, const void *path, int length) // length #endif +#if CTX_FONT_ENGINE_HARFBUZZ +typedef struct CtxHb { + Ctx *ctx; + float scale; +} CtxHb; + +static int ctx_glyph_lookup_hb (CtxFont *font, Ctx *ctx, uint32_t unichar) +{ + hb_codepoint_t glyph_id; + if (hb_font_get_glyph (font->hb.font, unichar, 0, &glyph_id)) + return glyph_id; + return -1; +} + +static float +ctx_glyph_kern_hb (CtxFont *font, Ctx *ctx, uint32_t unicharA, uint32_t unicharB) +{ + return 0.0; +} + +static float +ctx_glyph_width_hb (CtxFont *font, Ctx *ctx, int glyph_id) +{ + CtxState *state = &ctx->state; + float font_size = state->gstate.font_size; + if (glyph_id < 0) + return 0.0f; + return hb_font_get_glyph_h_advance (font->hb.font, glyph_id) * + font_size * font->hb.scale; +} + +static int +ctx_glyph_hb (CtxFont *font, Ctx *ctx, int glyph_id, int stroke) +{ + CtxState *state = &ctx->state; + float font_size = state->gstate.font_size; + //ctx_save (ctx); + float origin_x = state->x; + float origin_y = state->y; + + if (glyph_id < 0) + return 0; + ctx_current_point (ctx, &origin_x, &origin_y); + ctx_translate (ctx, origin_x, origin_y); + + // XXX : perhaps we can tell harfbuzz about this factor? + CtxHb ctxhb = {ctx, font_size * font->hb.scale}; + //ctx_scale (ctx, font_size*font->hb.scale, font_size*font->hb.scale); + +#if HB_VERSION_MAJOR >= 7 + hb_font_draw_glyph (font->hb.font, glyph_id, font->hb.draw_funcs, &ctxhb); +#else + hb_font_get_glyph_shape (font->hb.font, glyph_id, font->hb.draw_funcs, &ctxhb); +#endif + if (stroke) + ctx_stroke (ctx); + else + ctx_fill (ctx); + + ctx_translate (ctx, -origin_x, -origin_y); + //ctx_restore (ctx); + return 0; +} + +int +ctx_load_font_hb (const char *name, const void *data, int length); + +static CtxFontEngine ctx_font_engine_hb = +{ +#if CTX_FONTS_FROM_FILE + NULL, +#endif + ctx_load_font_hb, + ctx_glyph_hb, + ctx_glyph_width_hb, + ctx_glyph_kern_hb, + ctx_glyph_lookup_hb, +}; + + +static void +ctx_hb_close_path (hb_draw_funcs_t *df, CtxHb *c, + hb_draw_state_t *ds, + void *data) +{ + ctx_close_path (c->ctx); +} + +static void +ctx_hb_move_to (hb_draw_funcs_t *df, CtxHb *c, + hb_draw_state_t *ds, + float to_x, float to_y, + void *data) +{ + ctx_move_to (c->ctx, to_x * c->scale, -to_y * c->scale); +} + +static void +ctx_hb_line_to (hb_draw_funcs_t *df, CtxHb *c, + hb_draw_state_t *ds, + float to_x, float to_y, + void *data) +{ + float scale = c->scale; + ctx_line_to (c->ctx, to_x * scale, -to_y * scale); +} + +static void +ctx_hb_quadratic_to (hb_draw_funcs_t *df, CtxHb *c, + hb_draw_state_t *ds, + float control_x, float control_y, + float to_x, float to_y, + void *data) +{ + float scale = c->scale; + ctx_quad_to (c->ctx, control_x * scale, -control_y * scale, to_x * scale, -to_y * scale); +} + +static void +ctx_hb_cubic_to (hb_draw_funcs_t *df, CtxHb *c, + hb_draw_state_t *ds, + float control1_x, float control1_y, + float control2_x, float control2_y, + float to_x, float to_y, + void *data) +{ + float scale = c->scale; + ctx_curve_to (c->ctx, control1_x * scale, -control1_y * scale, + control2_x * scale, -control2_y * scale, + to_x * scale, -to_y *scale); +} + + + +int +ctx_load_font_hb (const char *name, const void *path, int length) // length is ignored +{ + ctx_font_setup (NULL); + if (ctx_font_count >= CTX_MAX_FONTS) + { return -1; } + CtxFont *font = &ctx_fonts[ctx_font_count]; + + font->type = 4; + font->hb.name = strdup (name); + font->hb.path = ctx_strdup (path); + font->hb.blob = hb_blob_create_from_file(path); + font->hb.face = hb_face_create( + font->hb.blob, 0); + font->hb.font = hb_font_create( + font->hb.face); + font->engine = &ctx_font_engine_hb; + hb_draw_funcs_t *funcs = hb_draw_funcs_create (); +#if HB_VERSION_MAJOR >= 7 + hb_paint_funcs_t *pfuncs = hb_paint_funcs_create (); + font->hb.paint_funcs = pfuncs; + +#if 0 + hb_paint_funcs_set_move_to_func (pfuncs, (hb_draw_move_to_func_t) ctx_hb_move_to, NULL, NULL); + hb_draw_funcs_set_line_to_func (funcs, (hb_draw_line_to_func_t) ctx_hb_line_to, NULL, NULL); + hb_draw_funcs_set_quadratic_to_func (funcs, (hb_draw_quadratic_to_func_t) ctx_hb_quadratic_to, NULL, NULL); + hb_draw_funcs_set_cubic_to_func (funcs, (hb_draw_cubic_to_func_t) ctx_hb_cubic_to, NULL, NULL); + hb_draw_funcs_set_close_path_func (funcs, (hb_draw_close_path_func_t) ctx_hb_close_path, NULL, NULL); +#endif + +#endif + font->hb.draw_funcs = funcs; + + hb_draw_funcs_set_move_to_func (funcs, (hb_draw_move_to_func_t) ctx_hb_move_to, NULL, NULL); + hb_draw_funcs_set_line_to_func (funcs, (hb_draw_line_to_func_t) ctx_hb_line_to, NULL, NULL); + hb_draw_funcs_set_quadratic_to_func (funcs, (hb_draw_quadratic_to_func_t) ctx_hb_quadratic_to, NULL, NULL); + hb_draw_funcs_set_cubic_to_func (funcs, (hb_draw_cubic_to_func_t) ctx_hb_cubic_to, NULL, NULL); + hb_draw_funcs_set_close_path_func (funcs, (hb_draw_close_path_func_t) ctx_hb_close_path, NULL, NULL); + + int x_scale, y_scale; + hb_font_extents_t extents; + hb_font_get_h_extents (font->hb.font, &extents); + + hb_font_get_scale(font->hb.font, &x_scale, &y_scale); + + font->hb.scale = 1.0f / (extents.ascender - extents.descender); + ctx_font_count++; + + return ctx_font_count-1; +} + +#endif + int -_ctx_glyph (Ctx *ctx, uint32_t unichar, int stroke) +_ctx_glyph (Ctx *ctx, int glyph_id, int stroke) { CtxFont *font = &ctx_fonts[ctx->state.gstate.font]; // a begin-path here did not remove stray spikes in terminal #if CTX_ONE_FONT_ENGINE - return ctx_glyph_ctx (font, ctx, unichar, stroke); + return ctx_glyph_ctx (font, ctx, glyph_id, stroke); #else - return font->engine->glyph (font, ctx, unichar, stroke); + return font->engine->glyph (font, ctx, glyph_id, stroke); #endif } int -ctx_glyph (Ctx *ctx, uint32_t unichar, int stroke) +ctx_glyph_id (Ctx *ctx, uint32_t unichar, int stroke) { #if CTX_BACKEND_TEXT CtxEntry commands[3]; // 3 to silence incorrect warning from static analysis @@ -50770,6 +52675,28 @@ ctx_glyph (Ctx *ctx, uint32_t unichar, int stroke) #endif } +int +ctx_glyph_unichar(Ctx *ctx, uint32_t unichar, int stroke) +{ + return ctx_glyph_id(ctx, ctx_glyph_lookup (ctx, unichar), stroke); +} + +int ctx_glyph (Ctx *ctx, uint32_t unichar, int stroke) +{ + return ctx_glyph_unichar(ctx,unichar,stroke); +} + +int +ctx_glyph_lookup (Ctx *ctx, uint32_t unichar) +{ + CtxFont *font = &ctx_fonts[ctx->state.gstate.font]; +#if CTX_ONE_FONT_ENGINE + return ctx_glyph_lookup_ctx2 (font, ctx, unichar); +#else + return font->engine->glyph_lookup (font, ctx, unichar); +#endif +} + float ctx_glyph_width (Ctx *ctx, int unichar) { @@ -50792,6 +52719,162 @@ ctx_glyph_kern (Ctx *ctx, int unicharA, int unicharB) #endif } +static inline int +_ctx_text_substitute_ligatures (Ctx *ctx, CtxFont *font, + uint32_t *unichar, uint32_t next_unichar) +{ + if (ctx_font_is_monospaced (font)) + return 0; + if (*unichar == 'f') + switch (next_unichar) + { + case 'f': if (ctx_glyph_lookup (ctx, 0xfb00)>0) + { + *unichar = 0xfb00; + return 1; + } + break; + case 'i': + if (ctx_glyph_lookup (ctx, 0xfb01) > 0) + { + *unichar = 0xfb01; + return 1; + } + break; + case 'l': + if (ctx_glyph_lookup (ctx, 0xfb02) > 0) + { + *unichar = 0xfb02; + return 1; + } + break; + case 't': + if (ctx_glyph_lookup (ctx, 0xfb05) > 0) + { + *unichar = 0xfb05; + return 1; + } + break; + } + return 0; +} + +typedef struct CtxShape { + uint32_t hash; + const char *utf8; + uint64_t shaping_flags; + CtxGlyph *glyphs; + int count; + float width; +} CtxShape; +/* + * + * return 1 if the glyphs should be freed + */ +#define SHAPE_CACHE_SIZE 4096 + +CtxShape *shape_cache[SHAPE_CACHE_SIZE]; + + +static int +_ctx_shape (Ctx *ctx, + const char *string, + float *width, + CtxGlyph **ret_glyphs, + int *ret_count) +{ + CtxState *state = &ctx->state; + CtxFont *font = &ctx_fonts[state->gstate.font]; + + +#if CTX_FONT_SHAPE_CACHE + uint32_t hash = ctx_strhash (string); + hash ^= (uint32_t)(size_t)(font); + int hpos = hash & (SHAPE_CACHE_SIZE-1); + if (shape_cache[hpos] && !strcmp (shape_cache[hpos]->utf8, string) && shape_cache[hpos]->hash == hash) + { + ret_cached: + if (ret_glyphs) *ret_glyphs = shape_cache[hpos]->glyphs; + if (ret_count) *ret_count= shape_cache[hpos]->count; + if (width) *width = shape_cache[hpos]->width; + return 1; + } +#endif + unsigned int glyph_count = 0; + float x_advance = 0.0; + CtxGlyph *glyphs = NULL; + +#if CTX_FONT_BACKEND_HARFBUZZ + if (font->type == 4) // harfbuzz + { + hb_buffer_t *buf = hb_buffer_create(); + hb_buffer_add_utf8(buf, string, -1, 0, -1); + hb_buffer_guess_segment_properties(buf); + hb_shape(font->hb.font,buf, NULL, 0); + hb_glyph_info_t *glyph_info = hb_buffer_get_glyph_infos(buf, &glyph_count); + hb_glyph_position_t *glyph_pos = hb_buffer_get_glyph_positions(buf, &glyph_count); + glyphs = ctx_glyph_allocate (glyph_count); + for (unsigned int i = 0; i < glyph_count; i++) + { + glyphs[i].index = glyph_info[i].codepoint; + glyphs[i].x = (glyph_pos[i].x_offset + x_advance) * font->hb.scale;; + glyphs[i].y = glyph_pos[i].y_offset * font->hb.scale;; + x_advance += glyph_pos[i].x_advance; + } + x_advance *= font->hb.scale; + hb_buffer_destroy (buf); + } + else +#endif + { + float font_size = state->gstate.font_size; + glyphs = ctx_glyph_allocate (ctx_utf8_strlen (string) * 2); + for (const char *utf8 = string; *utf8; utf8 = ctx_utf8_skip (utf8, 1) ) + { + uint32_t unichar = ctx_utf8_to_unichar (utf8); + uint32_t next = ctx_utf8_to_unichar (ctx_utf8_skip(utf8, 1)); + int skip_kern = 0; + if (_ctx_text_substitute_ligatures (ctx, font, &unichar, next)) + { + utf8 = ctx_utf8_skip (utf8,1); + skip_kern = 1; + } + + glyphs[glyph_count].index = ctx_glyph_lookup (ctx, unichar); + glyphs[glyph_count].x = x_advance; + glyphs[glyph_count].y = 0; + x_advance += ctx_glyph_width(ctx, glyphs[glyph_count].index)/font_size; + + glyph_count++; + if (next &(!skip_kern)) x_advance += ctx_glyph_kern (ctx, unichar, next); + } + } + +#define CTX_CACHE_SHAPE_MAX_STRLEN 8 + +#if CTX_FONT_SHAPE_CACHE + int do_cache = (strlen(string)utf8 = ctx_strdup(string); + shape_cache[hpos]->glyphs = glyphs; + shape_cache[hpos]->count = glyph_count; + shape_cache[hpos]->width = x_advance; + shape_cache[hpos]->hash = hash; + goto ret_cached; + } +#endif + + if (width) *width = x_advance; + if (ret_count) *ret_count = glyph_count; + else ctx_glyph_free (glyphs); + if (ret_glyphs){ *ret_glyphs = glyphs; return 0;} + else return 1; +} + +#if CTX_ONE_FONT_ENGINE float ctx_text_width (Ctx *ctx, const char *string) @@ -50799,12 +52882,34 @@ ctx_text_width (Ctx *ctx, float sum = 0.0; if (!string) return 0.0f; + CtxFont *font = &ctx_fonts[ctx->state.gstate.font]; for (const char *utf8 = string; *utf8; utf8 = ctx_utf8_skip (utf8, 1) ) { - sum += ctx_glyph_width (ctx, ctx_utf8_to_unichar (utf8) ); + uint32_t unichar = ctx_utf8_to_unichar (utf8); + uint32_t next = ctx_utf8_to_unichar (ctx_utf8_skip(utf8, 1)); + int skip_kern = 0; + if (_ctx_text_substitute_ligatures (ctx, font, &unichar, next)) + { + utf8 = ctx_utf8_skip (utf8,1); + skip_kern = 1; + } + sum += ctx_glyph_width (ctx, ctx_glyph_lookup(ctx, unichar)); + if (next &(!skip_kern)) sum += ctx_glyph_kern (ctx, unichar, next); } return sum; } +#else +float +ctx_text_width (Ctx *ctx, + const char *string) +{ + float sum = 0.0; + if (!string) + return 0.0f; + _ctx_shape (ctx, string, &sum, NULL, NULL); + return sum * ctx->state.gstate.font_size; +} +#endif static void _ctx_glyphs (Ctx *ctx, @@ -50812,13 +52917,12 @@ _ctx_glyphs (Ctx *ctx, int n_glyphs, int stroke) { + CtxState *state = &ctx->state; + float font_size = state->gstate.font_size; 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); - } + ctx_move_to (ctx, glyphs[i].x * font_size, glyphs[i].y * font_size); + ctx_glyph_id (ctx, glyphs[i].index, stroke); } } @@ -50826,7 +52930,7 @@ _ctx_glyphs (Ctx *ctx, #define CTX_MAX_WORD_LEN 128 #if 1 -static int ctx_glyph_find (Ctx *ctx, CtxFont *font, uint32_t unichar) +int ctx_glyph_find (Ctx *ctx, CtxFont *font, uint32_t unichar) { int length = ctx_font_get_length (font); for (int i = 0; i < length; i++) @@ -50839,46 +52943,7 @@ static int ctx_glyph_find (Ctx *ctx, CtxFont *font, uint32_t unichar) } #endif -static inline int -_ctx_text_substitute_ligatures (Ctx *ctx, CtxFont *font, - uint32_t *unichar, uint32_t next_unichar) -{ - if (ctx_font_is_monospaced (font)) - return 0; - if (*unichar == 'f') - switch (next_unichar) - { - case 'f': if (ctx_glyph_find (ctx, font, 0xfb00)) - { - *unichar = 0xfb00; - return 1; - } - break; - case 'i': - if (ctx_glyph_find (ctx, font, 0xfb01)) - { - *unichar = 0xfb01; - return 1; - } - break; - case 'l': - if (ctx_glyph_find (ctx, font, 0xfb02)) - { - *unichar = 0xfb02; - return 1; - } - break; - case 't': - if (ctx_glyph_find (ctx, font, 0xfb05)) - { - *unichar = 0xfb05; - return 1; - } - break; - } - return 0; -} - +#if CTX_ONE_FONT_ENGINE static void _ctx_text (Ctx *ctx, const char *string, @@ -50888,6 +52953,7 @@ _ctx_text (Ctx *ctx, char word[CTX_MAX_WORD_LEN]; int word_len = 0; CtxState *state = &ctx->state; + float font_size = state->gstate.font_size; CtxFont *font = &ctx_fonts[state->gstate.font]; float x = ctx->state.x; word[word_len]=0; @@ -50947,21 +53013,7 @@ _ctx_text (Ctx *ctx, { float word_width = 0.0; word[word_len]=0; - - for (const char *bp = &word[0]; *bp; bp = ctx_utf8_skip (bp, 1)) - { - uint32_t unichar = ctx_utf8_to_unichar (bp); - const char *next_utf8 = ctx_utf8_skip (bp, 1); - uint32_t next_unichar = *next_utf8?ctx_utf8_to_unichar (next_utf8):0; - - if (_ctx_text_substitute_ligatures (ctx, font, &unichar, next_unichar)) - bp++; - - float glyph_width = ctx_glyph_width (ctx, unichar); - word_width += glyph_width; - if (next_unichar) - word_width += ctx_glyph_kern (ctx, unichar, next_unichar); - } + word_width = ctx_text_width (ctx, word); if (wrap_left != wrap_right && x + word_width >= wrap_right) @@ -50979,7 +53031,8 @@ _ctx_text (Ctx *ctx, if (_ctx_text_substitute_ligatures (ctx, font, &unichar, next_unichar)) bp++; - float glyph_width = ctx_glyph_width (ctx, unichar); + int glyph_id = ctx_glyph_lookup (ctx, unichar); + float glyph_width = ctx_glyph_width (ctx, glyph_id); if (x + glyph_width >= x1) { y += ctx->state.gstate.font_size * ctx_get_line_height (ctx); @@ -50988,7 +53041,7 @@ _ctx_text (Ctx *ctx, if (visible) { ctx_move_to (ctx, x, y + baseline_offset); - _ctx_glyph (ctx, unichar, stroke); + _ctx_glyph (ctx, glyph_id, stroke); } x += glyph_width; if (next_unichar) @@ -51002,7 +53055,7 @@ _ctx_text (Ctx *ctx, } else if (*utf8 == ' ') { - x += ctx_glyph_width (ctx, ' '); + x += ctx_glyph_width (ctx, ctx_glyph_lookup (ctx, ' ')); } word_len=0; word[word_len]=0; @@ -51024,12 +53077,135 @@ _ctx_text (Ctx *ctx, { ctx_move_to (ctx, x, y); } } +#else +static void +_ctx_text (Ctx *ctx, + const char *string, + int stroke, + int visible) +{ + char word[CTX_MAX_WORD_LEN]; + int word_len = 0; + CtxState *state = &ctx->state; + float font_size = state->gstate.font_size; + float x = ctx->state.x; + word[word_len]=0; + switch ( (int) ctx_state_get (state, SQZ_textAlign) ) + //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, SQZ_textBaseline) ) + { + case CTX_TEXT_BASELINE_HANGING: + /* XXX : crude */ + baseline_offset = ctx->state.gstate.font_size * 0.55f; + break; + case CTX_TEXT_BASELINE_TOP: + /* XXX : crude */ + baseline_offset = ctx->state.gstate.font_size * 0.7f; + break; + case CTX_TEXT_BASELINE_BOTTOM: + baseline_offset = -ctx->state.gstate.font_size * 0.1f; + 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.25f; + break; + } + float x0 = x; + + float wrap_left = ctx_get_wrap_left (ctx); + float wrap_right = ctx_get_wrap_right (ctx); + if (wrap_left != wrap_right) + { + x0 = wrap_left; + } + + if (*string) + for (const char *utf8 = string; utf8 && ( (utf8==string ) || utf8[-1]); utf8 = *utf8?ctx_utf8_skip (utf8, 1):NULL) + { + if (*utf8 == '\n' || + *utf8 == ' ' || + *utf8 == '\0') + { + float word_width = 0.0; + word[word_len]=0; + int n_glyphs = 0; + CtxGlyph *glyphs = NULL; + int cached = _ctx_shape (ctx, word, &word_width, &glyphs, &n_glyphs); + + if (wrap_left != wrap_right && + x + word_width * font_size >= wrap_right) + { + y += ctx->state.gstate.font_size * ctx_get_line_height (ctx); + x = x0; + } + + if (glyphs) + { + if (visible) + { + ctx_save (ctx); + ctx_translate (ctx, x, y + baseline_offset); + ctx_glyphs (ctx, glyphs, n_glyphs); + ctx_restore (ctx); + } + if (!cached) + ctx_glyph_free (glyphs); + } + x += word_width * font_size; + + if (*utf8 == '\n') + { + y += ctx->state.gstate.font_size * ctx_get_line_height (ctx); + x = x0; + } + else if (*utf8 == ' ') + { + x += ctx_glyph_width (ctx, ctx_glyph_lookup (ctx, ' ')); + } + word_len=0; + word[word_len]=0; + } + else + { + int len = ctx_utf8_len (*utf8); + for (int i = 0; i < len; i++) + { + if (word_len + 1 < CTX_MAX_WORD_LEN-1) + word[word_len++]=utf8[i]; + } + } + + } + if (!visible) + { ctx->state.x =x; ctx->state.y=y; } + else + { ctx_move_to (ctx, x, y); } +} +#endif CtxGlyph * ctx_glyph_allocate (int n_glyphs) { return (CtxGlyph *) ctx_malloc (sizeof (CtxGlyph) * n_glyphs); } + void ctx_glyph_free (CtxGlyph *glyphs) { @@ -51075,29 +53251,6 @@ ctx_fill_text (Ctx *ctx, const char *string, ctx_text (ctx, string); } -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 -} - -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 ctx_font_get_vmetrics (Ctx *ctx, CtxFont *font, @@ -51122,6 +53275,8 @@ ctx_font_get_vmetrics (Ctx *ctx, // font_size = ctx->state.gstate.font_size; switch (font->type) { + case 4: + // TODO : implement for harfbuzz #if CTX_FONT_ENGINE_CTX_FS case 3: #endif @@ -51180,6 +53335,9 @@ static const char *ctx_font_get_name (CtxFont *font) #endif #if CTX_FONT_ENGINE_CTX_FS case 3: return font->ctx_fs.name; +#endif +#if CTX_FONT_ENGINE_HARFBUZZ + case 4: return font->hb.name; #endif } return "-"; @@ -51267,7 +53425,7 @@ static int _ctx_resolve_font (const char *name) int namelen = 0; if (strchr (subname, ' ')) { - subname = strchr (subname, ' '); + subname = (char*)strchr (subname, ' '); namelen = subname - name; subname++; } @@ -51287,7 +53445,7 @@ static int _ctx_resolve_font (const char *name) */ if (ret < 0 && strchr (name, ' ')) { - char *subname = strchr (name, ' '); + char *subname = (char*)strchr (name, ' '); for (int i = 0; ret < 0 && i < ctx_font_count; i ++) { const char *font_name = ctx_font_get_name (&ctx_fonts[i]); @@ -51378,6 +53536,8 @@ static void ctx_font_setup (Ctx *ctx) ctx_font_count = 0; + + #if CTX_FONT_ENGINE_CTX_FS if (getenv ("CTX_FONT_LIVE_PATH")) { @@ -51574,7 +53734,16 @@ ctx_print_float (CtxFormatter *formatter, float val) else remainder /= 10; - ctx_print_int (formatter, (int)val); + if (!formatter->longform && ((((int)val))==0) && (remainder)) + { + // + } + else + { + ctx_print_int (formatter, (int)val); + } + + if (remainder) { ctx_formatter_addstr (formatter, ".", 1); @@ -51610,9 +53779,7 @@ static void _ctx_fd_addstr (CtxFormatter *formatter, const char *str, int len) void _ctx_string_addstr (CtxFormatter *formatter, const char *str, int len) { if (!str || len == 0) - { return; - } if (len < 0) len = ctx_strlen (str); ctx_string_append_data ((CtxString*)(formatter->target), str, len); } @@ -51620,16 +53787,13 @@ void _ctx_string_addstr (CtxFormatter *formatter, const char *str, int len) static void _ctx_print_endcmd (CtxFormatter *formatter) { if (formatter->longform) - { - ctx_formatter_addstr (formatter, ");\n", 3); - } + 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); - } + ctx_formatter_addstr (formatter, " ", 2); } const char *_ctx_code_to_name (int code) @@ -51655,7 +53819,6 @@ const char *_ctx_code_to_name (int code) 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; @@ -51665,6 +53828,7 @@ const char *_ctx_code_to_name (int code) case CTX_RECTANGLE: return "rectangle"; break; case CTX_ROUND_RECTANGLE: return "roundRectangle"; break; case CTX_LINEAR_GRADIENT: return "linearGradient"; break; + case CTX_CONIC_GRADIENT: return "conicGradient"; break; case CTX_RADIAL_GRADIENT: return "radialGradient"; break; case CTX_GRADIENT_STOP: return "gradientAddStop"; break; case CTX_VIEW_BOX: return "viewBox"; break; @@ -51719,6 +53883,8 @@ const char *_ctx_code_to_name (int code) case CTX_LINE_CAP: return "lineCap"; break; case CTX_LINE_WIDTH: return "lineWidth"; break; case CTX_LINE_DASH_OFFSET: return "lineDashOffset"; break; + case CTX_STROKE_POS: return "strokePos"; break; + case CTX_FEATHER: return "feather"; break; case CTX_LINE_HEIGHT: return "lineHeight";break; case CTX_WRAP_LEFT: return "wrapLeft"; break; case CTX_WRAP_RIGHT: return "wrapRight"; break; @@ -51758,27 +53924,29 @@ static void _ctx_print_name (CtxFormatter *formatter, int code) 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_EXTEND: name[1]='e'; break; - case CTX_TEXT_ALIGN: name[1]='t'; break; case CTX_TEXT_BASELINE: name[1]='b'; break; + case CTX_LINE_CAP: name[1]='c'; break; case CTX_TEXT_DIRECTION: name[1]='d'; break; + case CTX_EXTEND: name[1]='e'; 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_MITER_LIMIT: name[1]='l'; break; + case CTX_COMPOSITING_MODE: name[1]='m'; break; + case CTX_STROKE_POS: name[1]='p'; break; + case CTX_FEATHER: name[1]='F'; break; + case CTX_FILL_RULE: name[1]='r'; break; + case CTX_SHADOW_BLUR: name[1]='s'; break; + case CTX_TEXT_ALIGN: name[1]='t'; break; case CTX_LINE_WIDTH: name[1]='w'; break; + case CTX_SHADOW_OFFSET_X: name[1]='x'; break; + case CTX_SHADOW_OFFSET_Y: name[1]='y'; break; + case CTX_BLEND_MODE: name[1]='B'; break; + case CTX_SHADOW_COLOR: name[1]='C'; break; case CTX_LINE_DASH_OFFSET: name[1]='D'; break; case CTX_LINE_HEIGHT: name[1]='H'; break; case CTX_WRAP_LEFT: name[1]='L'; break; case CTX_WRAP_RIGHT: name[1]='R'; 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; @@ -51787,7 +53955,7 @@ static void _ctx_print_name (CtxFormatter *formatter, int code) ctx_formatter_addstr (formatter, name, -1); if (formatter->longform) ctx_formatter_addstr (formatter, " (", 2); - else + else if (ctx_arguments_for_code (code)<1 || ctx_arguments_for_code(code)>8) ctx_formatter_addstr (formatter, " ", 1); } } @@ -52092,6 +54260,7 @@ ctx_formatter_process (void *user_data, CtxCommand *c) case CTX_SOURCE_TRANSFORM: ctx_print_entry (formatter, entry, 9); break; + case CTX_CONIC_GRADIENT: case CTX_QUAD_TO: case CTX_RECTANGLE: case CTX_REL_QUAD_TO: @@ -52106,6 +54275,8 @@ ctx_formatter_process (void *user_data, CtxCommand *c) case CTX_ROTATE: case CTX_LINE_WIDTH: case CTX_LINE_DASH_OFFSET: + case CTX_STROKE_POS: + case CTX_FEATHER: case CTX_LINE_HEIGHT: case CTX_WRAP_LEFT: case CTX_WRAP_RIGHT: @@ -52289,14 +54460,44 @@ ctx_formatter_process (void *user_data, CtxCommand *c) break; case CTX_SET_RGBA_U8: if (formatter->longform) - { _ctx_indent (formatter); - ctx_formatter_addstr (formatter, "rgba (", -1); + + if (ctx_arg_u8(3)==255) + { + int components = 3; + if ( + (ctx_arg_u8 (0) == + ctx_arg_u8 (1)) && + (ctx_arg_u8 (1) == + ctx_arg_u8 (2))) + { + components = 1; } - else + + ctx_formatter_addstr (formatter, components==1?"gray":"rgb", -1); + if (formatter->longform) + ctx_formatter_addstr (formatter, " (", -1); + + for (int c = 0; c < components; c++) { - ctx_formatter_addstr (formatter, "rgba (", -1); + if (c) + { + if (formatter->longform) + ctx_formatter_addstr (formatter, ", ", 2); + else + ctx_formatter_addstr (formatter, " ", 1); + } + ctx_print_float (formatter, ctx_u8_to_float (ctx_arg_u8 (c) ) ); } + + } + else + { + + ctx_formatter_addstr (formatter, "rgba", -1); + if (formatter->longform) + ctx_formatter_addstr (formatter, " (", -1); + for (int c = 0; c < 4; c++) { if (c) @@ -52308,6 +54509,7 @@ ctx_formatter_process (void *user_data, CtxCommand *c) } ctx_print_float (formatter, ctx_u8_to_float (ctx_arg_u8 (c) ) ); } + } _ctx_print_endcmd (formatter); break; case CTX_SET_PIXEL: @@ -52360,7 +54562,6 @@ ctx_formatter_process (void *user_data, CtxCommand *c) _ctx_print_endcmd (formatter); break; case CTX_TEXT: - case CTX_STROKE_TEXT: case CTX_FONT: _ctx_print_name (formatter, entry->code); ctx_formatter_addstr (formatter, "\"", 1); @@ -52369,7 +54570,6 @@ ctx_formatter_process (void *user_data, CtxCommand *c) _ctx_print_endcmd (formatter); break; case CTX_CONT: - case CTX_EDGE: case CTX_DATA: case CTX_DATA_REV: case CTX_END_FRAME: @@ -52477,6 +54677,13 @@ ctx_render_string (Ctx *ctx, int longform, int *retlen) #include #include +// SQZ_cx +// SQZ_cy +// SQZ_rx +// SQZ_ry +// SQZ_c +// SQZ_r + CTX_EXPORT int ctx_width (Ctx *ctx) { @@ -52526,7 +54733,7 @@ ctx_current_path_iterator (Ctx *ctx) CtxDrawlist * ctx_current_path (Ctx *ctx) { - CtxDrawlist *drawlist = ctx_calloc (sizeof (CtxDrawlist) + + CtxDrawlist *drawlist = (CtxDrawlist*)ctx_calloc (sizeof (CtxDrawlist) + ctx->current_path.count * 9, 1); drawlist->entries = (CtxEntry*)(&drawlist[1]); drawlist->size = drawlist->count = ctx->current_path.count; @@ -52782,7 +54989,6 @@ static int ctx_eid_valid (Ctx *ctx, const char *eid, int *w, int *h) else if (!ctx_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; @@ -52792,9 +54998,6 @@ static int ctx_eid_valid (Ctx *ctx, const char *eid, int *w, int *h) 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); ctx_list_remove (&ctx->eid_db, eid_info); ctx_list_remove (&to_remove, eid_info); ctx_free (eid_info->eid); @@ -53075,11 +55278,13 @@ ctx_texture_load (Ctx *ctx, const char *path, int *tw, int *th, char *reid) int w, h, components; unsigned char *pixels = NULL; +#if 0 if (path[0] == '/' || !strncmp (path, "file://", 7)) { pixels = stbi_load (path + (path[0]=='/'?0:7), &w, &h, &components, 0); } else +#endif { unsigned char *data = NULL; long length = 0; @@ -53100,11 +55305,9 @@ ctx_texture_load (Ctx *ctx, const char *path, int *tw, int *th, char *reid) case 2: pixel_format = CTX_FORMAT_GRAYA8; break; case 3: pixel_format = CTX_FORMAT_RGB8; break; case 4: pixel_format = CTX_FORMAT_RGBA8; - for (int i = 0; i < w * h; i++) - { - ctx_RGBA8_associate_alpha (&pixels[i * 4]); - } - break; + for (int i = 0; i < w * h; i++) + ctx_RGBA8_associate_alpha (&pixels[i * 4]); + break; } if (tw) *tw = w; if (th) *th = h; @@ -53148,8 +55351,10 @@ ctx_draw_texture_clipped (Ctx *ctx, const char *eid, if (clip_height>0) tex_height = (int)clip_height; ctx_rectangle (ctx, x, y, width, height); ctx_save (ctx); - ctx_texture (ctx, eid, x-(clip_x) * (width/tex_width), y-clip_y * (height -/tex_height)); + + ctx_texture (ctx, eid, 0,0);//x-(clip_x) * (width/tex_width), y-clip_y * (height + ctx_translate (ctx, (x-(clip_x) * (width/tex_width)), (y-clip_y * (height +/tex_height))); ctx_scale (ctx, (width/tex_width), (height/tex_height)); ctx_fill (ctx); ctx_restore (ctx); @@ -53162,14 +55367,129 @@ void ctx_draw_texture (Ctx *ctx, const char *eid, float x, float y, float w, flo ctx_draw_texture_clipped (ctx, eid, x, y, w, h, 0,0,0,0); } +#if CTX_CSS + +typedef struct _CtxSvgCache CtxSvgCache; +struct _CtxSvgCache +{ + uint32_t path_id; + float viewbox[4]; + float width; + float height; + Ctx *drawlist; + int last_used_frame; + CtxSvgCache *next; +}; + +CtxSvgCache *ctx_svg_cache = NULL; + + +static void ctx_draw_svg_clipped (Ctx *ctx, const char *path, float x, float y, float w, float h, float sx, float sy, float swidth, float sheight) +{ + ctx_save (ctx); + +#if 0 + // should match HTML 2d context specs for raster images + if (swidth > 0.1f || sheight > 0.1f) + { + ctx_rectangle (ctx, sx, sy, swidth, sheight); + ctx_clip (ctx); + } +#endif + + int path_id = ctx_strhash (path); + CtxSvgCache *cached = NULL; + + for(cached = ctx_svg_cache; cached; cached = cached->next) + if (cached->path_id == path_id) + break; + + int textureclock = ctx_textureclock (ctx); + + if (!cached) + { + /* evict expired cached svg sprites */ + { + CtxSvgCache *prev = NULL, *iter; + + for (CtxSvgCache *iter = ctx_svg_cache; iter; iter=iter->next) + { + if (textureclock - iter->last_used_frame >= CTX_SVG_FREE_AGE) + { + if (prev) + { + prev->next = iter->next; + } + else + { + ctx_svg_cache = iter->next; + } + + ctx_destroy (iter->drawlist); + ctx_free (iter); + + iter = prev ? prev : ctx_svg_cache; + } + else + { + prev = iter; + } + } + } + + unsigned char *contents = NULL; + long length = 0; + ctx_get_contents (path, &contents, &length); + if (contents) + { + cached = ctx_calloc (sizeof (CtxSvgCache), 1); + cached->path_id = path_id; + cached->next = ctx_svg_cache; + cached->drawlist = ctx_new_drawlist (-1, -1); + Css *css= css_new (cached->drawlist); + float width, height; + css_xml_extent (css, contents, &cached->width, &cached->height, &cached->viewbox[0], &cached->viewbox[1], &cached->viewbox[2], &cached->viewbox[3]); + css_xml_render (css, NULL/*uri*/, NULL /* http(s) fetch cb*/, NULL, NULL, NULL, (char*)contents); + css_destroy (css); + ctx_free (contents); + ctx_svg_cache = cached; + } + } + + if (cached) + { + float factor, + factor_h = h / cached->viewbox[3]; + factor = w / cached->viewbox[2]; + if (factor_h < factor) factor = factor_h; + + ctx_translate (ctx, x, y); + ctx_scale (ctx, factor, factor); + ctx_translate (ctx, -cached->viewbox[0], -cached->viewbox[1]); + ctx_render_ctx (cached->drawlist, ctx); + cached->last_used_frame = textureclock; + } + ctx_restore (ctx); +} +#endif + 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]) +#if CTX_CSS + if (!strcmp (path + strlen(path) - 4, ".svg")) + { + ctx_draw_svg_clipped (ctx, path, x, y, w, h, sx, sy, swidth, sheight); + } + else +#endif { - ctx_draw_texture_clipped (ctx, reteid, x, y, w, h, sx, sy, swidth, 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); + } } } @@ -53188,6 +55508,17 @@ ctx_set_pixel_u8 (Ctx *ctx, uint16_t x, uint16_t y, uint8_t r, uint8_t g, uint8_ ctx_process (ctx, &command); } +void +ctx_conic_gradient (Ctx *ctx, float cx, float cy, float start_angle, float cycles) +{ + CtxEntry command[2]= + { + ctx_f (CTX_CONIC_GRADIENT, cx, cy), + ctx_f (CTX_CONT, start_angle, cycles) + }; + ctx_process (ctx, command); +} + void ctx_linear_gradient (Ctx *ctx, float x0, float y0, float x1, float y1) { @@ -53251,6 +55582,19 @@ void _ctx_set_store_clear (Ctx *ctx) static void ctx_event_free (void *event, void *user_data) { + CtxEvent *e = (CtxEvent*)event; + + if (!e->ctx->events.ctx_get_event_enabled) + { +// XXX : we are leaking a string!!!! +// without this, consuming events +// is broken for clients polling events +// +// we could append them to a list that is freed when starting a new frame? +// + if (e->string) + ctx_free ((char*)e->string); + } ctx_free (event); } @@ -53376,6 +55720,26 @@ void ctx_line_dash_offset (Ctx *ctx, float x) CTX_PROCESS_F1 (CTX_LINE_DASH_OFFSET, x); } +float ctx_get_stroke_pos (Ctx *ctx) +{ + return ctx->state.gstate.stroke_pos; +} + +void ctx_stroke_pos (Ctx *ctx, float x) +{ + CTX_PROCESS_F1 (CTX_STROKE_POS, x); +} + +float ctx_get_feather (Ctx *ctx) +{ + return ctx->state.gstate.feather; +} + +void ctx_feather (Ctx *ctx, float x) +{ + CTX_PROCESS_F1 (CTX_FEATHER, x); +} + void ctx_line_height (Ctx *ctx, float x) { CTX_PROCESS_F1 (CTX_LINE_HEIGHT, x); @@ -53403,7 +55767,7 @@ void ctx_image_smoothing (Ctx *ctx, int enabled) } -void ctx_line_dash (Ctx *ctx, float *dashes, int count) +void ctx_line_dash (Ctx *ctx, const float *dashes, int count) { ctx_process_cmd_str_with_len (ctx, CTX_LINE_DASH, (char*)(dashes), count, 0, count * 4); } @@ -53446,6 +55810,8 @@ void ctx_shadow_offset_y (Ctx *ctx, float x) void ctx_global_alpha (Ctx *ctx, float global_alpha) { + if (global_alpha < 0.0f) global_alpha = 0.0f; + else if (global_alpha > 1.0f) global_alpha = 1.0f; if (ctx->state.gstate.global_alpha_f != global_alpha) CTX_PROCESS_F1 (CTX_GLOBAL_ALPHA, global_alpha); } @@ -53532,7 +55898,7 @@ ctx_get_font (Ctx *ctx) void ctx_line_to (Ctx *ctx, float x, float y) { - if (CTX_UNLIKELY(!ctx->state.has_moved)) + if (ctx->state.has_moved <= 0) { CTX_PROCESS_F (CTX_MOVE_TO, x, y); } else { CTX_PROCESS_F (CTX_LINE_TO, x, y); } @@ -53884,7 +56250,7 @@ ctx_rel_arc_to (Ctx *ctx, float x1, float y1, float x2, float y2, float radius) } void -ctx_exit (Ctx *ctx) +ctx_done_frame (Ctx *ctx) { CTX_PROCESS_VOID (CTX_EXIT); } @@ -53904,9 +56270,9 @@ ctx_end_frame (Ctx *ctx) //////////////////////////////////////// static inline void -ctx_interpret_style (CtxState *state, CtxEntry *entry, void *data) +ctx_interpret_style (CtxState *state, const CtxEntry *entry, void *data) { - CtxCommand *c = (CtxCommand *) entry; + const CtxCommand *c = (CtxCommand *) entry; switch (entry->code) { case CTX_LINE_HEIGHT: @@ -53921,6 +56287,12 @@ ctx_interpret_style (CtxState *state, CtxEntry *entry, void *data) case CTX_LINE_DASH_OFFSET: state->gstate.line_dash_offset = ctx_arg_float (0); break; + case CTX_STROKE_POS: + state->gstate.stroke_pos = ctx_arg_float (0); + break; + case CTX_FEATHER: + state->gstate.feather = ctx_arg_float (0); + break; case CTX_LINE_WIDTH: state->gstate.line_width = ctx_arg_float (0); break; @@ -53975,7 +56347,7 @@ ctx_interpret_style (CtxState *state, CtxEntry *entry, void *data) 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, + (const unsigned char*)c->colorspace.data, c->colorspace.data_len); break; case CTX_IMAGE_SMOOTHING: @@ -54058,6 +56430,23 @@ ctx_interpret_style (CtxState *state, CtxEntry *entry, void *data) //case CTX_TEXTURE: // state->gstate.source.type = CTX_SOURCE_ // break; + case CTX_CONIC_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; + + source->conic_gradient.x = ctx_arg_float (0); + source->conic_gradient.y = ctx_arg_float (1); + source->conic_gradient.start_angle = ctx_arg_float (2); + source->conic_gradient.cycles = ctx_arg_float (3); + source->type = CTX_SOURCE_CONIC_GRADIENT; + source->transform = state->gstate.transform; + ctx_matrix_invert (&source->transform); + } + break; case CTX_LINEAR_GRADIENT: { int is_stroke = (state->source != 0); @@ -54077,12 +56466,12 @@ ctx_interpret_style (CtxState *state, CtxEntry *entry, void *data) 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.0f?1.0f/(end - start):1.0f; + float rdelta = (end-start)!=0.0f?1.0f/(end - start):1.0f; + float rdelta_div_length_recip = rdelta/length; + source->linear_gradient.length = length; + source->linear_gradient.dx_scaled = dx * rdelta_div_length_recip; + source->linear_gradient.dy_scaled = dy * rdelta_div_length_recip; + source->linear_gradient.start_scaled = start * rdelta; source->type = CTX_SOURCE_LINEAR_GRADIENT; source->transform = state->gstate.transform; ctx_matrix_invert (&source->transform); @@ -54118,7 +56507,7 @@ ctx_interpret_style (CtxState *state, CtxEntry *entry, void *data) } static inline void -ctx_interpret_transforms (CtxState *state, CtxEntry *entry, void *data) +ctx_interpret_transforms (CtxState *state, const CtxEntry *entry, void *data) { switch (entry->code) { @@ -54136,24 +56525,20 @@ ctx_interpret_transforms (CtxState *state, CtxEntry *entry, void *data) break; case CTX_IDENTITY: _ctx_matrix_identity (&state->gstate.transform); - //state->gstate.transform_type = 0; _ctx_transform_prime (state); break; case CTX_TRANSLATE: ctx_matrix_translate (&state->gstate.transform, ctx_arg_float (0), ctx_arg_float (1) ); - //state->gstate.transform_type = 0; _ctx_transform_prime (state); break; case CTX_SCALE: ctx_matrix_scale (&state->gstate.transform, ctx_arg_float (0), ctx_arg_float (1) ); - //state->gstate.transform_type = 0; _ctx_transform_prime (state); break; case CTX_ROTATE: ctx_matrix_rotate (&state->gstate.transform, ctx_arg_float (0) ); - //state->gstate.transform_type = 0; _ctx_transform_prime (state); break; case CTX_APPLY_TRANSFORM: @@ -54167,7 +56552,6 @@ ctx_interpret_transforms (CtxState *state, CtxEntry *entry, void *data) ctx_arg_float (8)); _ctx_matrix_multiply (&state->gstate.transform, &state->gstate.transform, &m); // XXX verify order - // state->gstate.transform_type = 0; _ctx_transform_prime (state); } #if 0 @@ -54221,6 +56605,12 @@ ctx_interpret_pos_transform (CtxState *state, CtxEntry *entry, void *data) _ctx_user_to_device (state, &c->linear_gradient.x2, &c->linear_gradient.y2); } break; + case CTX_CONIC_GRADIENT: + if ( ( ( (Ctx *) (data) )->transformation & CTX_TRANSFORMATION_SCREEN_SPACE) ) + { + _ctx_user_to_device (state, &c->conic_gradient.x, &c->conic_gradient.y); + } + break; case CTX_RADIAL_GRADIENT: if ( ( ( (Ctx *) (data) )->transformation & CTX_TRANSFORMATION_SCREEN_SPACE) ) { @@ -54361,7 +56751,7 @@ ctx_interpret_pos_transform (CtxState *state, CtxEntry *entry, void *data) } static inline void -ctx_interpret_pos_bare (CtxState *state, CtxEntry *entry, void *data) +ctx_interpret_pos_bare (CtxState *state, const CtxEntry *entry, void *data) { switch (entry->code) { @@ -54375,41 +56765,83 @@ ctx_interpret_pos_bare (CtxState *state, CtxEntry *entry, void *data) case CTX_STROKE: state->has_moved = 0; break; + case CTX_CLOSE_PATH: + state->x = state->first_x; + state->y = state->first_y; + state->has_moved = -1; + 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; + if (state->has_moved<=0) + { + state->first_x = state->x; + state->first_y = state->y; + 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; + if (state->has_moved<=0) + { + state->first_x = state->x; + state->first_y = state->y; + 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; + if (state->has_moved<=0) + { + state->first_x = state->x; + state->first_y = state->y; + 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); - state->has_moved = 1; + if (state->has_moved<=0) + { + state->first_x = state->x; + state->first_y = state->y; + state->has_moved = 1; + } break; case CTX_REL_MOVE_TO: case CTX_REL_LINE_TO: state->x += ctx_arg_float (0); state->y += ctx_arg_float (1); + + if (state->has_moved<=0) + { + state->first_x = state->x; + state->first_y = state->y; + state->has_moved = 1; + } break; case CTX_REL_CURVE_TO: state->x += ctx_arg_float (4); state->y += ctx_arg_float (5); + if (state->has_moved<=0) + { + state->first_x = state->x; + state->first_y = state->y; + state->has_moved = 1; + } break; case CTX_REL_QUAD_TO: state->x += ctx_arg_float (2); state->y += ctx_arg_float (3); - break; - // XXX missing some smooths + if (state->has_moved<=0) + { + state->first_x = state->x; + state->first_y = state->y; + state->has_moved = 1; + } } } @@ -54439,6 +56871,7 @@ ctx_state_init (CtxState *state) { memset (state, 0, sizeof (CtxState) ); state->gstate.global_alpha_u8 = 255; + state->gstate.global_alpha_u8 = 255; state->gstate.global_alpha_f = 1.0; state->gstate.font_size = 32; // default HTML canvas is 10px sans state->gstate.line_width = 2.0; @@ -54522,7 +56955,8 @@ void ctx_push_backend (Ctx *ctx, ctx->backend_pushed = ctx->backend; ctx->backend = (CtxBackend*)backend; if (ctx->backend->process == NULL) - ctx->backend->process = (void(*)(Ctx*,CtxCommand*))ctx_drawlist_process; + ctx->backend->process = (void(*)(Ctx*,const CtxCommand*))ctx_drawlist_process; + ctx->process = ctx->backend->process; } @@ -54534,6 +56968,7 @@ void ctx_pop_backend (Ctx *ctx) ctx->backend->destroy (ctx->backend); ctx->backend = ctx->backend_pushed; ctx->backend_pushed = NULL; + ctx->process = ctx->backend->process; } void ctx_set_backend (Ctx *ctx, @@ -54543,7 +56978,8 @@ void ctx_set_backend (Ctx *ctx, ctx->backend->destroy (ctx->backend); ctx->backend = (CtxBackend*)backend; if (ctx->backend->process == NULL) - ctx->backend->process = (void(*)(Ctx*,CtxCommand*))ctx_drawlist_process; + ctx->backend->process = (void(*)(Ctx*,const CtxCommand*))ctx_drawlist_process; + ctx->process = ctx->backend->process; } void *ctx_get_backend (Ctx *ctx) @@ -54578,33 +57014,24 @@ ctx_new_drawlist (int width, int height) static Ctx *ctx_new_ui (int width, int height, const char *backend); #endif -#if CTX_PICO -Ctx *ctx_pico_init (void); -#endif -#if CTX_ESP -Ctx *esp_ctx (void); -#endif +/* used by micro-controller backends */ +Ctx *ctx_host (void); CTX_EXPORT Ctx * ctx_new (int width, int height, const char *backend) { -#if CTX_PICO - return ctx_pico_init (); -#endif -#if CTX_ESP - return esp_ctx(); -#endif - + Ctx * ret = NULL; #if CTX_EVENTS if (backend && !ctx_strcmp (backend, "drawlist")) #endif { - return _ctx_new_drawlist (width, height); + ret = _ctx_new_drawlist (width, height); } #if CTX_EVENTS else - return ctx_new_ui (width, height, backend); + ret = ctx_new_ui (width, height, backend); #endif + return ret; } static inline void @@ -54648,11 +57075,43 @@ ctx_destroy (Ctx *ctx) if (!ctx) { return; } + if ((ctx_backend_type(ctx) != CTX_BACKEND_DRAWLIST) && + //(ctx_backend_type(ctx) != CTX_BACKEND_RASTERIZER) && + (ctx_backend_type(ctx) != CTX_BACKEND_HASHER) + + && _ctx_depth) + { + _ctx_depth--; + return; + } + + if (ctx->state.stringpool) + { + ctx_free (ctx->state.stringpool); + ctx->state.stringpool = NULL; + ctx->state.stringpool_size = 0; + } + #if CTX_VT while (ctx_clients (ctx)) ctx_client_remove (ctx, ctx_clients(ctx)->data); #endif +#if 0 +#if CTX_PICO || CTX_ESP + if (ctx == ctx_host ()) + return; +#endif +#endif + + while (ctx->deferred) + { + void *command = ctx->deferred->data; + ctx_list_remove (&ctx->deferred, command); + free (command); + } + + #if CTX_EVENTS ctx_clear_bindings (ctx); #endif @@ -54684,30 +57143,27 @@ ctx_render_ctx (Ctx *ctx, Ctx *d_ctx) { CtxIterator iterator; CtxCommand *command; - ctx->bail = 0; + d_ctx->bail = 0; ctx_iterator_init (&iterator, &ctx->drawlist, 0, - CTX_ITERATOR_EXPAND_BITPACK); - while ( (command = ctx_iterator_next (&iterator) ) ) - { - ctx_process (d_ctx, &command->entry); - } + 0); + void (*process) (Ctx *ctx, const CtxCommand *entry) = d_ctx->process; + while ( (command = (CtxCommand*)_ctx_iterator_next (&iterator) ) ) + process (d_ctx, command); } - void ctx_render_ctx_masked (Ctx *ctx, Ctx *d_ctx, uint32_t mask) { CtxIterator iterator; CtxCommand *command; - ctx_iterator_init (&iterator, &ctx->drawlist, 0, - CTX_ITERATOR_EXPAND_BITPACK); - + ctx_iterator_init (&iterator, &ctx->drawlist, 0, 0); + void (*process) (Ctx *ctx, const CtxCommand *entry) = d_ctx->process; uint32_t active_mask = 0xffffffff; - while ( (command = ctx_iterator_next (&iterator) ) ) + while ( (command = (CtxCommand*)_ctx_iterator_next (&iterator) ) ) { d_ctx->bail = ((active_mask & mask) == 0); - ctx_process (d_ctx, &command->entry); + process (d_ctx, command); switch (command->code) { @@ -54747,30 +57203,24 @@ ctx_render_ctx_textures (Ctx *ctx, Ctx *d_ctx) } } -void ctx_quit (Ctx *ctx) +void ctx_exit (Ctx *ctx) { -#if CTX_VT - while (ctx_clients (ctx)) - ctx_client_remove (ctx, ctx_clients(ctx)->data); -#endif + ctx->exit++; +} -#if CTX_EVENTS - ctx->quit ++; -#endif +int ctx_has_exited (Ctx *ctx) +{ + return (ctx->exit != 0); } -int ctx_has_quit (Ctx *ctx) +void ctx_reset_has_exited (Ctx *ctx) { -#if CTX_EVENTS - return (ctx->quit); -#else - return 1; -#endif + ctx->exit = 0; } int ctx_pixel_format_bits_per_pixel (CtxPixelFormat format) { - CtxPixelFormatInfo *info = ctx_pixel_format_info (format); + const CtxPixelFormatInfo *info = ctx_pixel_format_info (format); if (info) return info->bpp; return -1; @@ -54778,7 +57228,7 @@ int ctx_pixel_format_bits_per_pixel (CtxPixelFormat format) int ctx_pixel_format_get_stride (CtxPixelFormat format, int width) { - CtxPixelFormatInfo *info = ctx_pixel_format_info (format); + const CtxPixelFormatInfo *info = ctx_pixel_format_info (format); if (info) { switch (info->bpp) @@ -54799,7 +57249,7 @@ int ctx_pixel_format_get_stride (CtxPixelFormat format, int width) int ctx_pixel_format_ebpp (CtxPixelFormat format) { - CtxPixelFormatInfo *info = ctx_pixel_format_info (format); + const CtxPixelFormatInfo *info = ctx_pixel_format_info (format); if (info) return info->ebpp; return -1; @@ -54807,7 +57257,7 @@ int ctx_pixel_format_ebpp (CtxPixelFormat format) int ctx_pixel_format_components (CtxPixelFormat format) { - CtxPixelFormatInfo *info = ctx_pixel_format_info (format); + const CtxPixelFormatInfo *info = ctx_pixel_format_info (format); if (info) return info->components; return -1; @@ -54887,8 +57337,8 @@ ctx_string_append_callback (void *contents, size_t size, size_t nmemb, void *use #endif -#ifdef ITK_HAVE_FS -int itk_static_get_contents (const char *path, char **contents, long *length); +#if CSS_HAVE_FS +int css_static_get_contents (const char *path, char **contents, long *length); #endif int @@ -54934,11 +57384,11 @@ ctx_get_contents2 (const char *uri, } if (!strncmp (uri, "file://", 7)) - success = ___ctx_file_get_contents (uri + 7, contents, length, max_len); -#ifdef ITK_HAVE_FS + success = CTX_LOAD_FILE (uri + 7, contents, length, max_len); +#if CSS_HAVE_FS else if (!strncmp (uri, "itk:", 4)) { - success = itk_static_get_contents (uri, (char**)contents, length); + success = css_static_get_contents (uri, (char**)contents, length); } #endif else @@ -54973,7 +57423,7 @@ ctx_get_contents2 (const char *uri, success = 0; } #else - success = ___ctx_file_get_contents (uri, contents, length, max_len); + success = CTX_LOAD_FILE (uri, contents, length, max_len); #endif } ctx_free (temp_uri); @@ -54989,7 +57439,7 @@ ctx_get_contents (const char *uri, return ctx_get_contents2 (uri, contents, length, 1024*1024*1024); } - +#if CTX_MAGIC typedef struct CtxMagicEntry { int is_text; @@ -54999,7 +57449,7 @@ typedef struct CtxMagicEntry { uint8_t magic[16]; } CtxMagicEntry; -static CtxMagicEntry ctx_magics[]={ +static const CtxMagicEntry ctx_magics[]={ {0, "image/bmp", ".bmp", 0, {0}}, {0, "image/png", ".png", 8, {0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a}}, {0, "image/jpeg", ".jpg", 8, {0xff, 0xd8, 0xff, 0xdb, 0xff, 0xd8, 0xff, 0xe0}}, @@ -55015,6 +57465,7 @@ static CtxMagicEntry ctx_magics[]={ {0, "image/exr", ".exr", 4, {0x76, 0x2f, 0x31, 0x01}}, {0, "video/mpeg", ".mpg", 4, {0x00, 0x00, 0x01, 0xba}}, {0, "application/blender", ".blend", 8, {0x42, 0x4c,0x45,0x4e,0x44,0x45,0x52}}, + {0, "application/x-sharedlib", ".elf", 4, {0x7f, 'E','L','F'}}, {0, "image/xcf", ".xcf", 8, {0x67, 0x69,0x6d,0x70,0x20,0x78,0x63,0x66}}, {0, "application/bzip2", ".bz2", 3, {0x42, 0x5a, 0x68}}, {0, "application/gzip", ".gz", 2, {0x1f, 0x8b}}, @@ -55033,6 +57484,7 @@ static CtxMagicEntry ctx_magics[]={ {1, "text/csv", ".csv", 0, {0x0}}, {1, "text/html", ".htm", 0, {0x0}}, {1, "text/html", ".html", 0, {0x0}}, + {1, "image/svg+xml", ".svg", 0, {0x0}}, {1, "text/x-makefile", "makefile", 0, {0x0}}, {1, "application/atom+xml", ".atom", 0, {0x0}}, {1, "application/rdf+xml", ".rdf", 0, {0x0}}, @@ -55041,7 +57493,6 @@ static CtxMagicEntry ctx_magics[]={ {0, "application/octet-stream", ".bin", 0, {0x0}}, {0, "application/x-object", ".o", 0, {0x0}}, {1, "text/utf-8", ".txt", 0, {0xef, 0xbb, 0xbf}}, // utf8 bom - {1, "text/x-sh", ".sh", 0, {0x0}}, {1, "text/x-python", ".py", 0, {0x0}}, {1, "text/x-perl", ".pl", 0, {0x0}}, {1, "text/x-perl", ".pm", 0, {0x0}}, @@ -55058,6 +57509,8 @@ static CtxMagicEntry ctx_magics[]={ {0, "audio/x-wav", ".wav", 4, {0x52, 0x49, 0x46, 0x46}}, {0, "audio/ogg", ".ogg", 4, {0x4f, 0x67, 0x67, 0x53}}, {0, "audio/ogg", ".opus", 0, {0x0}}, + {0, "audio/protracker", ".mod", 0, {0x0}}, + {0, "audio/screamtracker", ".s3m", 0, {0x0}}, {0, "audio/ogg", ".oga", 0, {0x0}}, {0, "audio/mpeg", ".mp1", 0, {0x0}}, {0, "audio/m3u", ".m3u", 0, {0x0}}, @@ -55075,7 +57528,7 @@ int ctx_path_is_dir (const char *path) { struct stat stat_buf; if (!path || path[0]==0) return 0; - lstat (path, &stat_buf); + stat (path, &stat_buf); return S_ISDIR (stat_buf.st_mode); } @@ -55083,7 +57536,7 @@ static int ctx_path_is_exec (const char *path) { struct stat stat_buf; if (!path || path[0]==0) return 0; - lstat (path, &stat_buf); + stat (path, &stat_buf); return stat_buf.st_mode & 0x1; } @@ -55255,6 +57708,7 @@ CtxMediaTypeClass ctx_media_type_class (const char *media_type) } return ret; } +#endif #else int @@ -55321,6 +57775,7 @@ static CtxBackendType __ctx_backend_type (Ctx *ctx) CtxBackend *backend = ctx->backend; if (backend == NULL) return CTX_BACKEND_NONE; + else if (backend->destroy == (void*) ctx_cb_destroy) return CTX_BACKEND_CB; #if CTX_FORMATTER else if (backend->destroy == (void*) ctx_ctx_destroy) return CTX_BACKEND_CTX; #if CTX_HEADLESS @@ -55390,14 +57845,14 @@ int ctx_get_fullscreen (Ctx *ctx) return 0; } -CtxPixelFormatInfo *ctx_pixel_formats = +const CtxPixelFormatInfo *ctx_pixel_formats = #if CTX_COMPOSITE ctx_pixel_formats_generic; #else NULL; #endif -CtxPixelFormatInfo * +const CtxPixelFormatInfo * ctx_pixel_format_info (CtxPixelFormat format) { if (!ctx_pixel_formats) @@ -55425,15 +57880,6 @@ void (*ctx_rasterizer_rasterize_edges) (CtxRasterizer *rasterizer, const int fil void (*ctx_composite_setup) (CtxRasterizer *rasterizer) = ctx_composite_setup_generic; -#if CTX_FAST_STROKE_RECT -void (*ctx_composite_stroke_rect) (CtxRasterizer *rasterizer, - float x0, - float y0, - float x1, - float y1, - float line_width) = - ctx_composite_stroke_rect_generic; -#endif #if CTX_FAST_FILL_RECT void (*ctx_composite_fill_rect) (CtxRasterizer *rasterizer, @@ -55443,6 +57889,15 @@ void (*ctx_composite_fill_rect) (CtxRasterizer *rasterizer, float y1, uint8_t cov) = ctx_composite_fill_rect_generic; +#if CTX_FAST_STROKE_RECT +void (*ctx_composite_stroke_rect) (CtxRasterizer *rasterizer, + float x0, + float y0, + float x1, + float y1, + float line_width) = + ctx_composite_stroke_rect_generic; +#endif #endif #endif @@ -55528,7 +57983,7 @@ typedef struct CtxDeferredCommand { static CtxDeferredCommand *deferred_new (Ctx *ctx, const char *name) { - CtxDeferredCommand *deferred = calloc (sizeof (CtxDeferredCommand), 1); + CtxDeferredCommand *deferred = (CtxDeferredCommand*)calloc (sizeof (CtxDeferredCommand), 1); if (name) deferred->name = ctx_strhash (name); deferred->offset = ctx->drawlist.count; @@ -55582,7 +58037,7 @@ static CtxList *ctx_deferred_commands (Ctx *ctx, const char *name, int *ret_coun int count = 0; for (CtxList *l = ctx->deferred; l; l = l->next) { - CtxDeferredCommand *command = l->data; + CtxDeferredCommand *command = (CtxDeferredCommand*)l->data; if (name) { if (command->name == name_id) @@ -55683,7 +58138,7 @@ void ctx_resolve (Ctx *ctx, const char *name, CtxList *matching = ctx_deferred_commands (ctx, name, &count); while (matching) { - CtxDeferredCommand *command = matching->data; + CtxDeferredCommand *command = (CtxDeferredCommand*)matching->data; float x, y, w = 0, h = 0; if (command->is_rect) @@ -56349,6 +58804,7 @@ struct _VT int cr_on_lf; int cursor_visible; + int scrollbar_visible; int saved_x; int saved_y; uint32_t saved_style; @@ -56626,7 +59082,7 @@ int mic_device = 0; // when non 0 we have an active mic device */ #if 0 -static char MuLawCompressTable[256] = +static const 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, @@ -56683,7 +59139,7 @@ void vt_feed_audio (VT *vt, void *samples, int bytes) int z_result = compress (data, &len, samples, len); if (z_result != Z_OK) { - char buf[256]= "\033_Ao=z;zlib error2\033\\"; + const char *buf = "\033_Ao=z;zlib error2\033\\"; vt_write (vt, buf, strlen(buf)); data = samples; } @@ -56925,7 +59381,7 @@ void vt_audio_task (VT *vt, int click) void terminal_queue_pcm (int16_t sample_left, int16_t sample_right); -static unsigned char vt_bell_audio[] = { +static unsigned char const 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, @@ -57690,7 +60146,7 @@ static unsigned char vt_bell_audio[] = { #endif }; -static short MuLawDecompressTable[256] = +static const short MuLawDecompressTable[256] = { -32124,-31100,-30076,-29052,-28028,-27004,-25980,-24956, -23932,-22908,-21884,-20860,-19836,-18812,-17788,-16764, @@ -57984,7 +60440,7 @@ void vt_audio (VT *vt, const char *command) if (!new_data) { - char buf[256]= "\e_Gf=100;audio decode error\e\\"; + const char *buf= "\e_Gf=100;audio decode error\e\\"; vt_write (vt, buf, strlen(buf)); goto cleanup; } @@ -58099,26 +60555,22 @@ void vt_audio (VT *vt, const char *command) * 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 + * UTF8, cp437 + * vt100 - 101 points on scoresheet + * vt320 - horizontal margins + * BBS/ANSI-art mode + * * realtime audio transmission - * raster sprites (kitty spec) + * raster sprites (sixels, iterm2 and kitty specs) * 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 * */ @@ -58518,7 +60970,9 @@ const char *vt_get_title (VT *vt) return vt->title; } +#if CTX_PTY static void vt_run_command (VT *vt, const char *command, const char *term); +#endif 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); @@ -58629,6 +61083,7 @@ static void vtcmd_reset_to_initial_state (VT *vt, const char *sequence) vt->autowrap = 1; vt->justify = 0; vt->cursor_visible = 1; + vt->scrollbar_visible = 1; vt->charset[0] = 0; vt->charset[1] = 0; vt->charset[2] = 0; @@ -58696,9 +61151,9 @@ void vt_set_line_spacing (VT *vt, float line_spacing) } +#if CTX_PTY static void ctx_clients_signal_child (int signum) { -#if CTX_PTY pid_t pid; int status; if ( (pid = waitpid (-1, &status, WNOHANG) ) != -1) @@ -58716,8 +61171,8 @@ static void ctx_clients_signal_child (int signum) } } } -#endif } +#endif static void vt_init (VT *vt, int width, int height, float font_size, float line_spacing, int id, int can_launch) { @@ -58959,7 +61414,7 @@ void ctx_vt_write (Ctx *ctx, uint8_t byte) } else { - fprintf (stderr, "ctx uart overflow\n"); + //fprintf (stderr, "ctx uart overflow\n"); } } @@ -59028,12 +61483,6 @@ void ctx_dummy_resize (void *serial_obj, int cols, int rows, int px_width, int static void vt_run_argv (VT *vt, char **argv, const char *term) { -#ifdef EMSCRIPTEN - vt->read = em_read; - vt->write = em_write; - vt->waitdata = em_waitdata; - vt->resize = dummy_resize; -#else #if 0 int was_pidone = (getpid () == 1); @@ -59079,7 +61528,6 @@ static void vt_run_argv (VT *vt, char **argv, const char *term) } fcntl(vt->vtpty.pty, F_SETFL, O_NONBLOCK|O_NOCTTY); _ctx_add_listen_fd (vt->vtpty.pty); -#endif } @@ -59191,6 +61639,7 @@ int vt_ch (VT *vt) return vt->ch; } + static int vt_trimlines (VT *vt, int max) { CtxList *chop_point = NULL; @@ -59220,7 +61669,8 @@ static int vt_trimlines (VT *vt, int max) ctx_list_remove (&chop_point, chop_point->data); vt->line_count--; } - if (vt->scrollback_count > vt->scrollback_limit) + + if (vt->scrollback_count > vt->scrollback_limit + 1024) { CtxList *l = vt->scrollback; int no = 0; @@ -60511,7 +62961,7 @@ static void vtcmd_set_alternate_font (VT *vt, const char *sequence) } -static void vt_ctx_exit (void *data) +static void vt_ctx_frame_done (void *data) { VT *vt = data; vt->state = vt_state_neutral; @@ -60651,6 +63101,7 @@ static void vtcmd_set_mode (VT *vt, const char *sequence) vt->cursor_visible = set; break; case 30: // from rxvt - show/hide scrollbar + vt->scrollbar_visible = set; break; case 34: // DECRLM - right to left mode break; @@ -60761,7 +63212,7 @@ static void vtcmd_set_mode (VT *vt, const char *sequence) #endif } - if (!vt->ctxp) + //if (!vt->ctxp) { if (vt->ctxp) @@ -60770,7 +63221,7 @@ static void vtcmd_set_mode (VT *vt, const char *sequence) 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); + (void*)vt_set_prop, (void*)vt_get_prop, vt, vt_ctx_frame_done, vt); } vt->utf8_holding[vt->utf8_pos=0]=0; // XXX : needed? vt->state = vt_state_ctx; @@ -60930,8 +63381,10 @@ static void vtcmd_request_mode (VT *vt, const char *sequence) is_set = (vt->state == vt_state_ctx); break; #endif - case 80:/* DECSDM Sixel scrolling */ case 30: // from rxvt - show/hide scrollbar + is_set = vt->scrollbar_visible; + break; + case 80:/* DECSDM Sixel scrolling */ case 34: // DECRLM - right to left mode case 60: // horizontal cursor coupling case 61: // vertical cursor coupling @@ -61316,7 +63769,7 @@ static void vtcmd_report (VT *vt, const char *sequence) } } -static char *charmap_cp437[]= +static const char *charmap_cp437[]= { " ","☺","☻","♥","♦","♣","♠","•","◘","○","◙","♂","♀","♪","♫","☼", "►","◄","↕","‼","¶","§","▬","↨","↑","↓","→","←","∟","↔","▲","▼", @@ -61337,7 +63790,7 @@ static char *charmap_cp437[]= }; -static char *charmap_graphics[]= +static const char *charmap_graphics[]= { " ","!","\"","#","$","%","&","'","(",")","*","+",",","-",".","/","0", "1","2","3","4","5","6","7","8","9",":",";","<","=",">","?", @@ -61347,7 +63800,7 @@ static char *charmap_graphics[]= "─","⎼","⎽","├","┤","┴","┬","│","≤","≥","π","≠","£","·"," " }; -static char *charmap_uk[]= +static const char *charmap_uk[]= { " ","!","\"","£","$","%","&","'","(",")","*","+",",","-",".","/","0", "1","2","3","4","5","6","7","8","9",":",";","<","=",">","?", @@ -61357,7 +63810,7 @@ static char *charmap_uk[]= "q","r","s","t","u","v","w","x","y","z","{","|","}","~"," " }; -static char *charmap_ascii[]= +static const char *charmap_ascii[]= { " ","!","\"","#","$","%","&","'","(",")","*","+",",","-",".","/","0", "1","2","3","4","5","6","7","8","9",":",";","<","=",">","?", @@ -62008,7 +64461,7 @@ void vt_gfx (VT *vt, const char *command) vt->gfx.data_size); if (z_result != Z_OK) { - char buf[256]= "\033_Go=z;zlib error\033\\"; + const char *buf = "\033_Go=z;zlib error\033\\"; vt_write (vt, buf, strlen (buf) ); goto cleanup; } @@ -62025,7 +64478,7 @@ void vt_gfx (VT *vt, const char *command) 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\\"; + const char *buf= "\033_Gf=100;image decode error\033\\"; vt_write (vt, buf, strlen (buf) ); goto cleanup; } @@ -63211,7 +65664,7 @@ static void vt_state_neutral (VT *vt, int byte) if (vt->charset[vt->shifted_in] != 0 && vt->charset[vt->shifted_in] != 'B') { - char **charmap; + const char **charmap; switch (vt->charset[vt->shifted_in]) { case 'A': @@ -63641,8 +66094,20 @@ void vt_feed_keystring (VT *vt, CtxEvent *event, const char *str) vt_set_local (vt, !vt_get_local (vt) ); return; } - else if (str[0]=='p' && str[1] != 0 && str[2] == ' ') + else if ((!strncmp (str, "control-p", 9)) && (str[9]!=0) && (str[10] == ' ')) + { + str+=8; + goto mice; + } + else if ((!strncmp (str, "shift-p", 7)) && (str[7]!=0) && (str[8] == ' ')) { + str+=6; + goto mice; + } + + else if (str[0]=='p' && str[1] != 0 && str[2] == ' ') + { +mice:{ int cw = vt_cw (vt); int ch = vt_ch (vt); if (!strncmp (str, "pm", 2)) @@ -63724,7 +66189,7 @@ void vt_feed_keystring (VT *vt, CtxEvent *event, const char *str) // queue-draw } return; - } + }} if (vt->scroll_on_input) { @@ -63836,7 +66301,7 @@ const char *ctx_find_shell_command (void) int i; const char *command = NULL; struct stat stat_buf; - static char *alts[][2] = + static const char *alts[][2] = { {"/bin/bash", "/bin/bash"}, {"/usr/bin/bash", "/usr/bin/bash"}, @@ -63859,10 +66324,9 @@ const char *ctx_find_shell_command (void) - -static void vt_run_command (VT *vt, const char *command, const char *term) -{ #if CTX_PTY +void vt_run_command (VT *vt, const char *command, const char *term) +{ #ifdef EMSCRIPTEN printf ("run command %s\n", command); #else @@ -63893,8 +66357,8 @@ static void vt_run_command (VT *vt, const char *command, const char *term) fcntl(vt->vtpty.pty, F_SETFL, O_NONBLOCK|O_NOCTTY); _ctx_add_listen_fd (vt->vtpty.pty); #endif -#endif } +#endif void vt_destroy (VT *vt) @@ -66399,7 +68863,7 @@ static void test_popup (Ctx *ctx, void *data) } #endif -void itk_style_color (Ctx *ctx, const char *name); // only itk fun used in vt +void css_style_color (Ctx *ctx, const char *name); // only itk fun used in vt void vt_use_images (VT *vt, Ctx *ctx) { @@ -66503,13 +68967,13 @@ void vt_draw (VT *vt, Ctx *ctx, double x0, double y0) (vt->rows) * vt->ch); if (vt->reverse_video) { - //itk_style_color (ctx, "terminal-bg-reverse"); + //css_style_color (ctx, "terminal-bg-reverse"); ctx_rgba (ctx, 1.0,1.0,1.0,1.0f); ctx_fill (ctx); } else { - //itk_style_color (ctx, "terminal-bg"); + //css_style_color (ctx, "terminal-bg"); ctx_rgba (ctx,0,0,0,1.0f); ctx_fill (ctx); } @@ -66717,6 +69181,7 @@ void vt_draw (VT *vt, Ctx *ctx, double x0, double y0) } +#if 0 for (int i = 0; i < 4; i++) { if (vt->leds[i]) @@ -66726,6 +69191,8 @@ void vt_draw (VT *vt, Ctx *ctx, double x0, double y0) ctx_fill (ctx); } } +#endif + ctx_restore (ctx); //#define SCROLL_SPEED 0.25; #define SCROLL_SPEED 0.001; @@ -66754,7 +69221,7 @@ void vt_draw (VT *vt, Ctx *ctx, double x0, double y0) } /* scrollbar */ - if (!vt->in_alt_screen) + if (!vt->in_alt_screen && vt->scrollbar_visible) { float disp_lines = vt->rows; float tot_lines = vt->line_count + vt->scrollback_count; @@ -67123,7 +69590,9 @@ void vt_mouse (VT *vt, CtxEvent *event, VtMouseEvent type, int button, int x, in if (buf[0]) { vt_write (vt, buf, strlen (buf) ); +#ifndef PICO_BUILD fsync (vt->vtpty.pty); +#endif } //#endif } @@ -67329,9 +69798,10 @@ CtxClient *ctx_client_new (Ctx *ctx, ctx_list_append (&ctx->events.clients, client); ctx_client_init (ctx, client, x, y, width, height, font_size, flags, user_data, finalize); float line_spacing = 2.0f; - client->vt = vt_new (commandline, width, height, font_size,line_spacing, client->id, (flags & ITK_CLIENT_CAN_LAUNCH)!=0); + client->vt = vt_new (commandline, width, height, font_size,line_spacing, client->id, (flags & CSS_CLIENT_CAN_LAUNCH)!=0); client->vt->client = client; vt_set_ctx (client->vt, ctx); + vt_set_title (client->vt, "ctx - native vectors"); return client; } @@ -67343,9 +69813,10 @@ CtxClient *ctx_client_new_argv (Ctx *ctx, char **argv, int x, int y, int width, ctx_list_append (&ctx->events.clients, client); float line_spacing = 2.0f; - client->vt = vt_new_argv (argv, width, height, font_size,line_spacing, client->id, (flags & ITK_CLIENT_CAN_LAUNCH)!=0); + client->vt = vt_new_argv (argv, width, height, font_size,line_spacing, client->id, (flags & CSS_CLIENT_CAN_LAUNCH)!=0); client->vt->client = client; vt_set_ctx (client->vt, ctx); + vt_set_title (client->vt, "ctx - native vectors"); return client; } @@ -67377,7 +69848,7 @@ CtxClient *ctx_client_new_thread (Ctx *ctx, void (*start_routine)(Ctx *ctx, void client->start_routine = start_routine; thrd_create (&client->tid, launch_client_thread, client); //float line_spacing = 2.0f; - //client->vt = vt_new_thread (start_routine, userdata, width, height, font_size,line_spacing, client->id, (flags & ITK_CLIENT_CAN_LAUNCH)!=0); + //client->vt = vt_new_thread (start_routine, userdata, width, height, font_size,line_spacing, client->id, (flags & CSS_CLIENT_CAN_LAUNCH)!=0); //vt_set_ctx (client->vt, ctx); if (client->vt) client->vt->client = client; @@ -67401,7 +69872,7 @@ static CtxClient *find_active (Ctx *ctx, int x, int y) for (CtxList *l = ctx_clients (ctx); l; l = l->next) { CtxClient *c = l->data; - if ((c->flags & ITK_CLIENT_MAXIMIZED) && c == ctx->events.active_tab) + if ((c->flags & CSS_CLIENT_MAXIMIZED) && c == ctx->events.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) { @@ -67412,7 +69883,7 @@ static CtxClient *find_active (Ctx *ctx, int x, int y) for (CtxList *l = ctx_clients (ctx); l; l = l->next) { CtxClient *c = l->data; - if (!(c->flags & ITK_CLIENT_MAXIMIZED)) + if (!(c->flags & CSS_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) { @@ -67450,7 +69921,7 @@ static void ctx_clients_ensure_layout (Ctx *ctx) if (n_clients == 1) { CtxClient *client = clients->data; - if (client->flags & ITK_CLIENT_MAXIMIZED) + if (client->flags & CSS_CLIENT_MAXIMIZED) { ctx_client_move (ctx, client->id, 0, 0); ctx_client_resize (ctx, client->id, ctx_width (ctx), ctx_height(ctx)); @@ -67462,7 +69933,7 @@ static void ctx_clients_ensure_layout (Ctx *ctx) for (CtxList *l = clients; l; l = l->next) { CtxClient *client = l->data; - if (client->flags & ITK_CLIENT_MAXIMIZED) + if (client->flags & CSS_CLIENT_MAXIMIZED) { ctx_client_move (ctx, client->id, 0, ctx_client_min_y_pos (ctx)); ctx_client_resize (ctx, client->id, ctx_width (ctx), ctx_height(ctx) - @@ -67577,7 +70048,7 @@ void ctx_client_iconify (Ctx *ctx, int id) { CtxClient *client = ctx_client_by_id (ctx, id); if (!client) return; - client->flags |= ITK_CLIENT_ICONIFIED; + client->flags |= CSS_CLIENT_ICONIFIED; ctx_queue_draw (ctx); } @@ -67585,14 +70056,14 @@ int ctx_client_is_iconified (Ctx *ctx, int id) { CtxClient *client = ctx_client_by_id (ctx, id); if (!client) return -1; - return (client->flags & ITK_CLIENT_ICONIFIED) != 0; + return (client->flags & CSS_CLIENT_ICONIFIED) != 0; } void ctx_client_uniconify (Ctx *ctx, int id) { CtxClient *client = ctx_client_by_id (ctx, id); if (!client) return; - client->flags &= ~ITK_CLIENT_ICONIFIED; + client->flags &= ~CSS_CLIENT_ICONIFIED; ctx_queue_draw (ctx); } @@ -67600,9 +70071,9 @@ void ctx_client_maximize (Ctx *ctx, int id) { CtxClient *client = ctx_client_by_id (ctx, id); if (!client) return; - if (!(client->flags & ITK_CLIENT_MAXIMIZED)) + if (!(client->flags & CSS_CLIENT_MAXIMIZED)) { - client->flags |= ITK_CLIENT_MAXIMIZED; + client->flags |= CSS_CLIENT_MAXIMIZED; client->unmaximized_x = client->x; client->unmaximized_y = client->y; client->unmaximized_width = client->width; @@ -67621,16 +70092,16 @@ int ctx_client_is_maximized (Ctx *ctx, int id) { CtxClient *client = ctx_client_by_id (ctx, id); if (!client) return -1; - return (client->flags & ITK_CLIENT_MAXIMIZED) != 0; + return (client->flags & CSS_CLIENT_MAXIMIZED) != 0; } void ctx_client_unmaximize (Ctx *ctx, int id) { CtxClient *client = ctx_client_by_id (ctx, id); if (!client) return; - if ((client->flags & ITK_CLIENT_MAXIMIZED) == 0) + if ((client->flags & CSS_CLIENT_MAXIMIZED) == 0) return; - client->flags &= ~ITK_CLIENT_MAXIMIZED; + client->flags &= ~CSS_CLIENT_MAXIMIZED; ctx_client_resize (ctx, id, client->unmaximized_width, client->unmaximized_height); ctx_client_move (ctx, id, client->unmaximized_x, client->unmaximized_y); ctx->events.active_tab = NULL; @@ -67650,7 +70121,7 @@ void ctx_client_shade (Ctx *ctx, int id) { CtxClient *client = ctx_client_by_id (ctx, id); if (!client) return; - client->flags |= ITK_CLIENT_SHADED; + client->flags |= CSS_CLIENT_SHADED; ctx_queue_draw (ctx); } @@ -67658,14 +70129,14 @@ int ctx_client_is_shaded (Ctx *ctx, int id) { CtxClient *client = ctx_client_by_id (ctx, id); if (!client) return -1; - return (client->flags & ITK_CLIENT_SHADED) != 0; + return (client->flags & CSS_CLIENT_SHADED) != 0; } void ctx_client_unshade (Ctx *ctx, int id) { CtxClient *client = ctx_client_by_id (ctx, id); if (!client) return; - client->flags &= ~ITK_CLIENT_SHADED; + client->flags &= ~CSS_CLIENT_SHADED; ctx_queue_draw (ctx); } @@ -67770,7 +70241,7 @@ int ctx_client_resize (Ctx *ctx, int id, int width, int height) return 0; } -static void ctx_client_titlebar_drag (CtxEvent *event, void *data, void *data2) +void ctx_client_titlebar_drag (CtxEvent *event, void *data, void *data2) { CtxClient *client = data; @@ -67918,7 +70389,7 @@ static void ctx_client_resize_w (CtxEvent *event, void *data, void *data2) event->stop_propagate = 1; } -static void ctx_client_close (CtxEvent *event, void *data, void *data2) +void ctx_client_close (CtxEvent *event, void *data, void *data2) { //Ctx *ctx = event->ctx; CtxClient *client = data; @@ -67944,23 +70415,23 @@ static void ctx_client_draw (Ctx *ctx, CtxClient *client, float x, float y) ctx_translate (ctx, x, y); int width = client->width; int height = client->height; - itk_panel_start (itk, "", 0, 0, width, height); - //itk_seperator (itk); + css_panel_start (itk, "", 0, 0, width, height); + //css_seperator (itk); #if 0 - if (itk_button (itk, "add tab")) + if (css_button (itk, "add tab")) { add_tab (ctx_find_shell_command(), 1); } #endif - //itk_sameline (itk); - on_screen_keyboard = itk_toggle (itk, "on screen keyboard", on_screen_keyboard); - focus_follow_mouse = 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); + //css_sameline (itk); + on_screen_keyboard = css_toggle (itk, "on screen keyboard", on_screen_keyboard); + focus_follow_mouse = css_toggle (itk, "focus follows mouse", focus_follows_mouse); + css_slider_float (itk, "CTX_GREEN", &_ctx_green, 0.0, 1.0, 0.5); + css_ctx_settings (itk); + css_css_settings (itk); + css_panel_end (itk); + css_done (itk); + //css_key_bindings (itk); ctx_restore (ctx); } else @@ -68149,7 +70620,7 @@ void ctx_client_handle_event (Ctx *ctx, CtxEvent *ctx_event, const char *event) #endif else if (!strcmp (event, "shift-control-q") ) { - ctx_quit (ctx); + ctx_exit (ctx); } else if (!strcmp (event, "shift-control-w") ) { @@ -68189,7 +70660,7 @@ static int ctx_clients_dirty_count (Ctx *ctx) return changes; } -static void ctx_client_titlebar_drag_maximized (CtxEvent *event, void *data, void *data2) +void ctx_client_titlebar_drag_maximized (CtxEvent *event, void *data, void *data2) { CtxClient *client = data; Ctx *ctx = event->ctx; @@ -68235,11 +70706,11 @@ void ctx_client_titlebar_draw (Ctx *ctx, CtxClient *client, ctx_rectangle (ctx, x, y - titlebar_height, width, titlebar_height); if (client == ctx->events.active) - itk_style_color (ctx, "titlebar-focused-bg"); + css_style_color (ctx, "titlebar-focused-bg"); else - itk_style_color (ctx, "titlebar-bg"); + css_style_color (ctx, "titlebar-bg"); - if (flag_is_set(client->flags, ITK_CLIENT_MAXIMIZED) || y == titlebar_height) + if (flag_is_set(client->flags, CSS_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); @@ -68253,7 +70724,7 @@ void ctx_client_titlebar_draw (Ctx *ctx, CtxClient *client, //ctx_font_size (ctx, itk->font_size);//titlebar_height);// * 0.85); if (client == ctx->events.active && - (flag_is_set(client->flags, ITK_CLIENT_MAXIMIZED) || y != titlebar_height)) + (flag_is_set(client->flags, CSS_CLIENT_MAXIMIZED) || y != titlebar_height)) #if 1 ctx_rectangle (ctx, x + width - titlebar_height, y - titlebar_height, titlebar_height, @@ -68266,16 +70737,16 @@ void ctx_client_titlebar_draw (Ctx *ctx, CtxClient *client, ctx_begin_path (ctx); ctx_move_to (ctx, x + width - titlebar_height * 0.8, y - titlebar_height * 0.22); if (client == ctx->events.active) - itk_style_color (ctx, "titlebar-focused-close"); + css_style_color (ctx, "titlebar-focused-close"); else - itk_style_color (ctx, "titlebar-close"); + css_style_color (ctx, "titlebar-close"); ctx_text (ctx, "X"); ctx_move_to (ctx, x + width/2, y - titlebar_height * 0.22); if (client == ctx->events.active) - itk_style_color (ctx, "titlebar-focused-fg"); + css_style_color (ctx, "titlebar-focused-fg"); else - itk_style_color (ctx, "titlebar-fg"); + css_style_color (ctx, "titlebar-fg"); ctx_save (ctx); ctx_text_align (ctx, CTX_TEXT_ALIGN_CENTER); @@ -68309,7 +70780,7 @@ int ctx_clients_draw (Ctx *ctx, int layer2) float titlebar_height = _ctx_font_size; int n_clients = ctx_list_length (clients); - if (ctx->events.active && flag_is_set(ctx->events.active->flags, ITK_CLIENT_MAXIMIZED) && n_clients == 1) + if (ctx->events.active && flag_is_set(ctx->events.active->flags, CSS_CLIENT_MAXIMIZED) && n_clients == 1) { ctx_client_draw (ctx, ctx->events.active, 0, 0); return 0; @@ -68317,7 +70788,7 @@ int ctx_clients_draw (Ctx *ctx, int layer2) for (CtxList *l = clients; l; l = l->next) { CtxClient *client = l->data; - if (flag_is_set(client->flags, ITK_CLIENT_MAXIMIZED)) + if (flag_is_set(client->flags, CSS_CLIENT_MAXIMIZED)) { if (client == ctx->events.active_tab) { @@ -68338,18 +70809,18 @@ int ctx_clients_draw (Ctx *ctx, int layer2) if (layer2) { - if (!flag_is_set (client->flags, ITK_CLIENT_LAYER2)) + if (!flag_is_set (client->flags, CSS_CLIENT_LAYER2)) continue; } else { - if (flag_is_set (client->flags, ITK_CLIENT_LAYER2)) + if (flag_is_set (client->flags, CSS_CLIENT_LAYER2)) continue; } - if (vt && !flag_is_set(client->flags, ITK_CLIENT_MAXIMIZED)) + if (vt && !flag_is_set(client->flags, CSS_CLIENT_MAXIMIZED)) { - if (flag_is_set(client->flags, ITK_CLIENT_SHADED)) + if (flag_is_set(client->flags, CSS_CLIENT_SHADED)) { ctx_client_use_images (ctx, client); } @@ -68359,12 +70830,12 @@ int ctx_clients_draw (Ctx *ctx, int layer2) // resize regions if (client == ctx->events.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)) + !flag_is_set(client->flags, CSS_CLIENT_SHADED) && + !flag_is_set(client->flags, CSS_CLIENT_MAXIMIZED) && + flag_is_set(client->flags, CSS_CLIENT_UI_RESIZABLE)) { #if CTX_PTY - itk_style_color (ctx, "titlebar-focused-bg"); + css_style_color (ctx, "titlebar-focused-bg"); #else ctx_rgb(ctx,0.1,0.2,0.3); #endif @@ -68437,7 +70908,7 @@ int ctx_clients_draw (Ctx *ctx, int layer2) } - if (client->flags & ITK_CLIENT_TITLEBAR) + if (client->flags & CSS_CLIENT_TITLEBAR) ctx_client_titlebar_draw (ctx, client, client->x, client->y, client->width, titlebar_height); } } @@ -68470,7 +70941,10 @@ int ctx_clients_need_redraw (Ctx *ctx) ctx_pointer_y (ctx)); if (follow_mouse || ctx_pointer_is_down (ctx, 0) || - ctx_pointer_is_down (ctx, 1) || (ctx->events.active==NULL)) +#if CTX_MAX_DEVICES>1 + ctx_pointer_is_down (ctx, 1) || +#endif + (ctx->events.active==NULL)) { if (client) { @@ -68478,12 +70952,15 @@ int ctx_clients_need_redraw (Ctx *ctx) { ctx->events.active = client; if (follow_mouse == 0 || - (ctx_pointer_is_down (ctx, 0) || - ctx_pointer_is_down (ctx, 1))) + (ctx_pointer_is_down (ctx, 0) +#if CTX_MAX_DEVICES > 1 + || ctx_pointer_is_down (ctx, 1) +#endif + )) { //if (client != clients->data) #if 1 - if ((client->flags & ITK_CLIENT_MAXIMIZED)==0) + if ((client->flags & CSS_CLIENT_MAXIMIZED)==0) { ctx_list_remove (&ctx->events.clients, client); ctx_list_append (&ctx->events.clients, client); @@ -68502,9 +70979,9 @@ int ctx_clients_need_redraw (Ctx *ctx) { if (vt_is_done (client->vt)) { - if ((client->flags & ITK_CLIENT_KEEP_ALIVE)) + if ((client->flags & CSS_CLIENT_KEEP_ALIVE)) { - client->flags |= ITK_CLIENT_FINISHED; + client->flags |= CSS_CLIENT_FINISHED; } else { @@ -68532,7 +71009,7 @@ int ctx_clients_tab_to_id (Ctx *ctx, int tab_no) for (CtxList *l = clients; l; l = l->next) { CtxClient *client = l->data; - if (flag_is_set(client->flags, ITK_CLIENT_MAXIMIZED)) + if (flag_is_set(client->flags, CSS_CLIENT_MAXIMIZED)) { if (no == tab_no) return client->id; @@ -68890,7 +71367,7 @@ static inline void ctx_tvg_memcpy (CtxTinyVG *tvg, void *dst, int pos, int len) } #define CTX_TVG_DEFINE_ACCESOR(nick, type) \ -static inline type ctx_tvg_##nick (CtxTinyVG *tvg)\ +static CTX_INLINE type ctx_tvg_##nick (CtxTinyVG *tvg)\ { type ret;\ ctx_tvg_memcpy (tvg, &ret, tvg->pos, sizeof (type));\ tvg->pos += sizeof(type);\ @@ -68913,7 +71390,7 @@ static inline uint8_t ctx_tvg_u6_u2 (CtxTinyVG *tvg, uint8_t *u2_ret) // XXX if this is inline the ESP32 fails with a compiler error // -static int32_t ctx_tvg_val (CtxTinyVG *tvg) +static CTX_INLINE int32_t ctx_tvg_val (CtxTinyVG *tvg) { switch (tvg->val_type) { @@ -68924,11 +71401,7 @@ static int32_t ctx_tvg_val (CtxTinyVG *tvg) return 0; } -static inline float ctx_tvg_unit (CtxTinyVG *tvg) -{ - uint32_t rv = ctx_tvg_val(tvg); - return rv * tvg->scale_factor; -} +#define ctx_tvg_unit(tvg) (ctx_tvg_val(tvg)*tvg->scale_factor) static inline uint32_t ctx_tvg_var (CtxTinyVG *tvg) { @@ -68944,7 +71417,7 @@ static inline uint32_t ctx_tvg_var (CtxTinyVG *tvg) #define CTX_TVG_MIN(a,b) (((a)<(b))?(a):(b)) #define CTX_TVG_MAX(a,b) (((a)>(b))?(a):(b)) -static void +static inline void ctx_tvg_segment (CtxTinyVG *tvg, int n_commands) { Ctx *ctx = tvg->ctx; @@ -69272,7 +71745,7 @@ static void ctx_tvg_set_style (CtxTinyVG *tvg, CtxTinyVGStyle *style) } } -static void ctx_tvg_path (CtxTinyVG *tvg, int item_count) +static inline void ctx_tvg_path (CtxTinyVG *tvg, int item_count) { int segment_length[item_count]; for (int i = 0; i < item_count; i ++) @@ -69631,6 +72104,11656 @@ int ctx_tinyvg_fd_draw (Ctx *ctx, int fd, int flags) #endif } +#endif +#if 0 +#include "local.conf" +#include "ctx.h" + +#endif + +#if CTX_CSS +//#include "squoze/squoze.h" + +//#include "static.inc" + +//#include "itk.h" // for completeness, itk wants to be built in the ctx + // compilation unit to be influenced by the ctx config + + + +#define CTX_MAX_STYLE_DEPTH CTX_MAX_STATES +#define CTX_MAX_STATE_DEPTH CTX_MAX_STATES + +#if 0 +#define CTX_MAX_FLOATS 16 +#define CTX_MAX_SELECTOR_LENGTH 64 +#define CTX_MAX_CSS_STRINGLEN 512 +#define CTX_MAX_CSS_RULELEN 32 // XXX doesnt have overflow protection +#define CTX_MAX_CSS_RULES 128 + +/* other important maximums */ +#define CTX_MAX_TEXT_LISTEN 256 +#define CTX_XML_INBUF_SIZE 1024 +#else + +#define CTX_MAX_FLOATS 3 +#define CTX_MAX_SELECTOR_LENGTH 64 +#define CTX_MAX_CSS_STRINGLEN 256 +#define CTX_MAX_CSS_RULELEN 32 // XXX doesnt have overflow protection +#define CTX_MAX_CSS_RULES 64 + +/* other important maximums */ +#define CTX_MAX_TEXT_LISTEN 16 +#define CTX_XML_INBUF_SIZE 256 +#endif + +#define PROP(a) (ctx_get_float(mrg->ctx, SQZ_##a)) +#define PROPS(a) (ctx_get_string(mrg->ctx, SQZ_##a)) +#define SET_PROPh(a,v) (ctx_set_float(mrg->ctx, a, v)) +#define SET_PROP(a,v) SET_PROPh(SQZ_##a, v) +#define SET_PROPS(a,v) (ctx_set_string(mrg->ctx, SQZ_##a, v)) +#define SET_PROPSh(a,v) (ctx_set_string(mrg->ctx, a, v)) + +#define SQZ_1 374u +#define SQZ_Aelig 2540544426u +#define SQZ_AElig 2622343083u +#define SQZ_Aring 3473872814u +#define SQZ_Oslash 3911734189u + +/* + * extra hashed strings to be picked up + * + * SQZ_id SQZ_class SQZ_d SQZ_rel SQZ_viewbox + * + */ + +#ifndef TRUE +#define TRUE 1 +#endif +#ifndef FALSE +#define FALSE 0 +#endif + + +/* mrg - MicroRaptor Gui + * Copyright (c) 2014 Øyvind Kolås + * + * 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 . + */ + +#include +#include + +typedef struct _Css Css; + + +void mrg_clear (Css *mrg); + +static void mrg_queue_draw (Css *mrg, CtxIntRectangle *rect) +{ +} + +typedef struct _CtxStyleNode CtxStyleNode; + +#define CTX_STYLE_MAX_CLASSES 8 +#define CTX_STYLE_MAX_PSEUDO 8 + +struct _CtxStyleNode +{ + uint32_t element_hash; + uint32_t classes_hash[CTX_STYLE_MAX_CLASSES]; + uint32_t id_hash; + const char *pseudo[CTX_STYLE_MAX_PSEUDO]; + uint32_t pseudo_hash[CTX_STYLE_MAX_PSEUDO]; + // TODO : to hash pseudos we need to store + // argument (like for nth-child) + int direct_descendant; /* for use in selector chains with > */ + const char *id; +}; + +typedef enum { + CTX_FLOAT_NONE = 0, + CTX_FLOAT_LEFT, + CTX_FLOAT_RIGHT, + CTX_FLOAT_FIXED +} CtxFloat; + + +typedef struct CtxFloatData { + CtxFloat type; + float x; + float y; + float width; + float height; +} CtxFloatData; + +typedef void (*CssNewText) (const char *new_text, void *data); +typedef void (*UiRenderFun) (Css *mrg, void *ui_data); + +typedef struct _Css Css; + +typedef enum { + CTX_DISPLAY_INLINE = 0, + CTX_DISPLAY_BLOCK, + CTX_DISPLAY_LIST_ITEM, + CTX_DISPLAY_NONE, + CTX_DISPLAY_INLINE_BLOCK, + CTX_DISPLAY_FLOW_ROOT, + CTX_DISPLAY_FLEX, + CTX_DISPLAY_GRID, + CTX_DISPLAY_INLINE_FLEX, + CTX_DISPLAY_INLINE_GRID, + CTX_DISPLAY_INLINE_TABLE, + CTX_DISPLAY_RUN_IN, + CTX_DISPLAY_TABLE, + CTX_DISPLAY_TABLE_CAPTION, + CTX_DISPLAY_TABLE_COLUMN_GROUP, + CTX_DISPLAY_TABLE_HEADER_GROUP, + CTX_DISPLAY_TABLE_FOOTER_GROUP, + CTX_DISPLAY_TABLE_ROW_GROUP, + CTX_DISPLAY_TABLE_CELL, + CTX_DISPLAY_TABLE_COLUMN +} CtxDisplay; + +/* matches cairo order */ +typedef enum +{ + CTX_FONT_WEIGHT_NORMAL = 0, + CTX_FONT_WEIGHT_BOLD +} CtxFontWeight; + +/* matches cairo order */ +typedef enum +{ + CTX_FONT_STYLE_NORMAL = 0, + CTX_FONT_STYLE_ITALIC, + CTX_FONT_STYLE_OBLIQUE +} CtxFontStyle; + +typedef enum +{ + CTX_BOX_SIZING_CONTENT_BOX = 0, + CTX_BOX_SIZING_BORDER_BOX +} CtxBoxSizing; + +/* matching nchanterm definitions */ + +typedef enum { + CTX_TEXT_DECORATION_REGULAR = 0, + CTX_TEXT_DECORATION_BOLD = (1 << 0), + CTX_TEXT_DECORATION_DIM = (1 << 1), + CTX_TEXT_DECORATION_UNDERLINE = (1 << 2), + CTX_TEXT_DECORATION_REVERSE = (1 << 3), + CTX_TEXT_DECORATION_OVERLINE = (1 << 4), + CTX_TEXT_DECORATION_LINETHROUGH = (1 << 5), + CTX_TEXT_DECORATION_BLINK = (1 << 6) +} CtxTextDecoration; + +typedef enum { + CTX_POSITION_STATIC = 0, + CTX_POSITION_RELATIVE, + CTX_POSITION_FIXED, + CTX_POSITION_ABSOLUTE +} CtxPosition; + +typedef enum { + CTX_OVERFLOW_VISIBLE = 0, + CTX_OVERFLOW_HIDDEN, + CTX_OVERFLOW_SCROLL, + CTX_OVERFLOW_AUTO +} CtxOverflow; + +typedef enum { + CTX_CLEAR_NONE = 0, + CTX_CLEAR_LEFT, // 1 + CTX_CLEAR_RIGHT, // 2 + CTX_CLEAR_BOTH // 3 ( LEFT | RIGHT ) +} CtxClear; + + +typedef enum { + CTX_WHITE_SPACE_NORMAL = 0, + CTX_WHITE_SPACE_NOWRAP, + CTX_WHITE_SPACE_PRE, + CTX_WHITE_SPACE_PRE_LINE, + CTX_WHITE_SPACE_PRE_WRAP +} CtxWhiteSpace; + +typedef enum { + CTX_VERTICAL_ALIGN_BASELINE = 0, + CTX_VERTICAL_ALIGN_MIDDLE, + CTX_VERTICAL_ALIGN_BOTTOM, + CTX_VERTICAL_ALIGN_TOP, + CTX_VERTICAL_ALIGN_SUB, + CTX_VERTICAL_ALIGN_SUPER +} CtxVerticalAlign; + +typedef enum { + MRG_CURSOR_AUTO = 0, + MRG_CURSOR_ALIAS, + MRG_CURSOR_ALL_SCROLL, + MRG_CURSOR_CELL, + MRG_CURSOR_CONTEXT_MENU, + MRG_CURSOR_COL_RESIZE, + MRG_CURSOR_COPY, + MRG_CURSOR_CROSSHAIR, + MRG_CURSOR_DEFAULT, + MRG_CURSOR_E_RESIZE, + MRG_CURSOR_EW_RESIZE, + MRG_CURSOR_HELP, + MRG_CURSOR_MOVE, + MRG_CURSOR_N_RESIZE, + MRG_CURSOR_NE_RESIZE, + MRG_CURSOR_NESW_RESIZE, + MRG_CURSOR_NS_RESIZE, + MRG_CURSOR_NW_RESIZE, + MRG_CURSOR_NO_DROP, + MRG_CURSOR_NONE, + MRG_CURSOR_NOT_ALLOWED, + MRG_CURSOR_POINTER, + MRG_CURSOR_PROGRESS, + MRG_CURSOR_ROW_RESIZE, + MRG_CURSOR_S_RESIZE, + MRG_CURSOR_SE_RESIZE, + MRG_CURSOR_SW_RESIZE, + MRG_CURSOR_TEXT, + MRG_CURSOR_VERTICAL_TEXT, + MRG_CURSOR_W_RESIZE, + MRG_CURSOR_WAIT, + MRG_CURSOR_ZOOM_IN, + MRG_CURSOR_ZOOM_OUT +} CssCursor; + + +typedef enum { + CTX_UNICODE_BIDI_NORMAL = 0, + CTX_UNICODE_BIDI_EMBED, + CTX_UNICODE_BIDI_BIDI_OVERRIDE +} CtxUnicodeBidi; + +typedef enum { + CTX_VISIBILITY_VISIBLE = 0, + CTX_VISIBILITY_HIDDEN +} CtxVisibility; + +typedef enum { + CTX_LIST_STYLE_OUTSIDE = 0, + CTX_LIST_STYLE_INSIDE +} CtxListStyle; + +/* This style class should be able to grow to contain some color names with + * semantic meaning. + */ +struct _CtxStyle { + /* text-related, we could potentially store *all* variables in keydb + * that would both be slower and more bloatful than tightly packed bits, + * some things currently in keydb should maybe be moved out for + * performance. + */ + float font_size; // used for mrg_em() should be direct + float line_height; + CtxVisibility visibility:1; + CtxFillRule fill_rule:1; + CtxFontStyle font_style:3; + CtxFontWeight font_weight:4; + CtxLineCap stroke_linecap:2; + CtxLineJoin stroke_linejoin:2; + CtxTextAlign text_align:3; + CtxPosition position:2; + CtxBoxSizing box_sizing:1; + CtxVerticalAlign vertical_align:3; + CtxWhiteSpace white_space:3; + CtxUnicodeBidi unicode_bidi:2; + CtxTextDirection direction:2; + CtxListStyle list_style:1; + CtxClear clear:2; + unsigned char fill:1; + CssCursor cursor:6; + CtxTextDecoration text_decoration:7; + unsigned char width_auto:1; + unsigned char margin_left_auto:1; + unsigned char margin_right_auto:1; + unsigned char print_symbols:1; + CtxFloat float_:2; + unsigned char stroke:1; + CtxOverflow overflow:2; + CtxDisplay display:5; + void *id_ptr; + int z_index; +}; + +typedef struct _CtxStyle CtxStyle; + +typedef struct CssState { + CtxStyleNode style_node; + + float original_x; + float original_y; + float block_start_x; + float block_start_y; + float ptly; + float vmarg; + int flow_root; // is + int float_base; + + float (*wrap_edge_left) (Css *mrg, void *data); + float (*wrap_edge_right) (Css *mrg, void *data); + void *wrap_edge_data; + float edge_top; + float edge_left; + float edge_right; + float edge_bottom; + + int skip_lines; /* better with an em offset? */ + int max_lines; /* better with max-y in ems? ? */ + + char *style_id; + CtxStyle style; + + int children; + unsigned int overflowed:1; + unsigned int span_bg_started:1; + + int drawlist_start_offset; +} CssState; + +typedef struct _CssAbsolute CssAbsolute; + +struct _CssAbsolute { + int z_index; + int fixed; + float top; + float left; + float relative_x; + float relative_y; + CtxEntry *entries; + int count; +}; + + +struct _Css { + Ctx *ctx; + Ctx *document_ctx; + Ctx *fixed_ctx; + Ctx *absolute_ctx; + float rem; + float ddpx; + int in_svg; + CtxList *absolutes; + CtxList *stylesheet; + void *css_parse_state; + CtxString *style; + CtxString *style_global; + int quit; + float x; /* in px */ + float y; /* in px */ + float relative_x; + float relative_y; + CtxIntRectangle dirty; + CtxIntRectangle dirty_during_paint; // queued during painting + CssState *state; + CssState states[CTX_MAX_STATE_DEPTH]; + int state_no; + void *backend_data; + int do_clip; + int (*mrg_get_contents) (const char *referer, + const char *input_uri, + char **contents, + long *length, + void *get_contents_data); + void *get_contents_data; + + CtxFloatData float_data[CTX_MAX_FLOATS]; + int floats; + + /** text editing state follows **/ + int text_edited; + int got_edit; + CtxString *edited_str; + char **edited; + + int text_edit_blocked; + CssNewText update_string; + void *update_string_user_data; + + CtxDestroyNotify update_string_destroy_notify; + void *update_string_destroy_data; + + int cursor_pos; + float e_x; + float e_y; + float e_ws; + float e_we; + float e_em; + + CtxEventType text_listen_types[CTX_MAX_TEXT_LISTEN]; + CtxCb text_listen_cb[CTX_MAX_TEXT_LISTEN]; + void *text_listen_data1[CTX_MAX_TEXT_LISTEN]; + void *text_listen_data2[CTX_MAX_TEXT_LISTEN]; + + void (*text_listen_finalize[CTX_MAX_TEXT_LISTEN])(void *listen_data, void *listen_data2, void *finalize_data); + void *text_listen_finalize_data[CTX_MAX_TEXT_LISTEN]; + int text_listen_count; + int text_listen_active; + + int line_level; // nesting level for active-line + // + float line_max_height[CTX_MAX_STATES]; + int line_got_baseline[CTX_MAX_STATES]; // whether the current mrg position of + // baseline is correctly configured + // for relative drawing. + // + // XXX refactor into a bitfield + //////////////////////////////////////////////// + //////////////////////////////////////////////// + //////////////////// end of css //////////////// + + // the following used to be the original Css struct + + int (*ui_fun)(Css *itk, void *data); + void *ui_data; + + // the following should be removed in favor + // of the css|mrg data? + float edge_left; + float edge_top; + float edge_right; + float edge_bottom; + float width; + float height; + + float font_size; + float rel_hmargin; + float rel_vmargin; + float label_width; + + float scale; + + float rel_ver_advance; + float rel_hpad; + float rel_vgap; + float scroll_speed; + + int return_value; // when set to 1, we return the internally held from the + // defining app state when the widget was drawn/intercations + // started. + + float slider_value; // for reporting back slider value + + int active; // 0 not actively editing + // 1 currently in edit-mode of focused widget + // 2 means return edited value + + int active_entry; + int focus_wraparound; + + int focus_no; + int focus_x; + int focus_y; + int focus_width; + char *focus_label; + + char *entry_copy; + int entry_pos; + CssPanel *panel; + char *uri_base; + + CtxList *old_controls; + CtxList *controls; + CtxList *choices; + CtxList *panels; + int hovered_no; + int control_no; + int choice_active; + + int choice_no; // the currenlt active choice if the choice context is visible (or the current control is a choice) + + int popup_x; + int popup_y; + int popup_width; + int popup_height; + + char *active_menu_path; + char *menu_path; + + uint64_t next_flags; + void *next_id; // to pre-empt a control and get it a more unique + // identifier than the numeric pos + int line_no; + int lines_drawn; + int light_mode; + + +//////////////////////////////// + + int in_choices; + + int unresolved_line; + +}; + +float css_panel_scroll (Css *itk); + +static Ctx *mrg_ctx (Css *mrg) +{ + return mrg->ctx; +} + + +/* XXX: stopping sibling grabs should be an addtion to stop propagation, + * this would permit multiple events to co-register, and use that + * to signal each other,.. or perhaps more coordination is needed + */ +void _mrg_clear_text_closures (Css *mrg) +{ + int i; + for (i = 0; i < mrg->text_listen_count; i ++) + { + if (mrg->text_listen_finalize[i]) + mrg->text_listen_finalize[i]( + mrg->text_listen_data1[i], + mrg->text_listen_data2[i], + mrg->text_listen_finalize_data[i]); + } + mrg->text_listen_count = 0; + mrg->text_listen_active = 0; +} + +static CtxList *interns = NULL; + +const char * mrg_intern_string (const char *str) +{ + CtxList *i; + for (i = interns; i; i = i->next) + { + if (!strcmp (i->data, str)) + return i->data; + } + str = strdup (str); + ctx_list_append (&interns, (void*)str); + return str; +} + +int +mrg_get_contents (Css *mrg, + const char *referer, + const char *input_uri, + char **contents, + long *length); + +/* + * Copyright (c) 2002, 2003, Øyvind Kolås + * + * 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. + */ + +#if 0 +void _ctX_bindings_key_down (CtxEvent *event, void *data1, void *data2) +{ + Ctx *ctx = event->ctx; + CtxEvents *mrg = &ctx->events; + int i; + int handled = 0; + + for (i = mrg->n_bindings-1; i>=0; i--) + if (!strcmp (mrg->bindings[i].nick, event->string)) + { + if (mrg->bindings[i].cb) + { + mrg->bindings[i].cb (event, mrg->bindings[i].cb_data, NULL); + if (event->stop_propagate) + return; + handled = 1; + } + } + if (!handled) + for (i = mrg->n_bindings-1; i>=0; i--) + if (!strcmp (mrg->bindings[i].nick, "any")) + { + if (mrg->bindings[i].cb) + { + mrg->bindings[i].cb (event, mrg->bindings[i].cb_data, NULL); + if (event->stop_propagate) + return; + } + } +} +#endif + +#ifndef XMLTOK_H +#define XMLTOK_H + +#include + +typedef struct _Css Css; +typedef struct _CssXml CssXml; + +enum +{ + t_none = 0, + t_whitespace, + t_prolog, + t_dtd, + t_comment, + t_word, + t_tag, + t_closetag, + t_closeemptytag, + t_endtag, + t_att = 10, + t_val, + t_eof, + t_entity, + t_error +}; + +CssXml *xmltok_new (FILE *file_in); +CssXml *xmltok_buf_new (char *membuf); +void xmltok_free (CssXml *t); +int xmltok_lineno (CssXml *t); +int xmltok_get (CssXml *t, char **data, int *pos); + +#endif /*XMLTOK_H */ + +#include +#include +#include +#include + + +/* mrg - MicroRaptor Gui + * Copyright (c) 2014 Øyvind Kolås + * + * 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 . + */ + + +/* mrg - MicroRaptor Gui + * Copyright (c) 2014 Øyvind Kolås + * + * 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 . + */ + +#ifndef _DEFAULT_SOURCE +#define _DEFAULT_SOURCE +#endif + +#include +#include +#include +#include + +struct _CssXml +{ + FILE *file_in; + int state; + CtxString *curdata; + CtxString *curtag; + int c; + int c_held; + + unsigned char *inbuf; + int inbuflen; + int inbufpos; + int line_no; +}; + +enum +{ + s_null = 0, + s_start, + s_tag, + s_tagnamestart, + s_tagname, + s_tagnamedone, + s_intag, + s_attstart, + s_attname, + s_attdone, + s_att, + s_atteq, + s_eqquot, + s_eqvalstart, + s_eqapos, + s_eqaposval, + s_eqaposvaldone, + s_eqval, + s_eqvaldone, + s_eqquotval, + s_eqquotvaldone, + s_tagend, + s_empty, + s_inempty, + s_emptyend, + s_whitespace, + s_whitespacedone, + s_entitystart, + s_entity, + s_entitydone, + s_word, + s_worddone, + s_tagclose, + s_tagclosenamestart, + s_tagclosename, + s_tagclosedone, + s_tagexcl, + s_commentdash1, + s_commentdash2, + s_incomment, + s_commentenddash1, + s_commentenddash2, + s_commentdone, + s_dtd, + s_prolog, + s_prologq, + s_prologdone, + s_eof, + s_error +}; + +char *c_ws = " \n\r\t"; + +enum +{ + c_nil = 0, + c_eat = 1, /* request that another char be used for the next state */ + c_store = 2 /* store the current char in the output buffer */ +}; + + +typedef struct +{ + int state; + char *chars; + unsigned char r_start; + unsigned char r_end; + int next_state; + int resetbuf; + int charhandling; + int return_type; /* if set return current buf, with type set to the type */ +} +state_entry; + +#define max_entries 20 + +static state_entry state_table[s_error][max_entries]; + +static void +a (int state, + char *chars, + unsigned char r_start, + unsigned char r_end, int charhandling, int next_state) +{ + int no = 0; + while (state_table[state][no].state != s_null) + no++; + state_table[state][no].state = state; + state_table[state][no].r_start = r_start; + if (chars) + state_table[state][no].chars = chars; + state_table[state][no].r_end = r_end; + state_table[state][no].charhandling = charhandling; + state_table[state][no].next_state = next_state; +} + +static void +r (int state, int return_type, int next_state) +{ + state_table[state][0].state = state; + state_table[state][0].return_type = return_type; + state_table[state][0].next_state = next_state; +} + +/* *INDENT-OFF* */ + +static void +init_statetable (void) { + static int inited=0; + if(inited) + return; + inited=1; + memset(state_table,0,sizeof(state_table)); + a(s_start, "<", 0,0, c_eat, s_tag); + a(s_start, c_ws, 0,0, c_eat+c_store, s_whitespace); + a(s_start, "&", 0,0, c_eat, s_entitystart); + a(s_start, NULL, 0,255, c_eat+c_store, s_word); + a(s_tag, c_ws, 0,0, c_eat, s_tag); + a(s_tag, "/", 0,0, c_eat, s_tagclose); + a(s_tag, "!", 0,0, c_eat, s_tagexcl); + a(s_tag, "?", 0,0, c_eat, s_prolog); + a(s_tag, NULL, 0,255, c_eat+c_store, s_tagnamestart); + a(s_tagclose, NULL, 0,255, c_eat+c_store, s_tagclosenamestart); + a(s_tagclosenamestart, ">", 0,0, c_eat, s_tagclosedone); + a(s_tagclosenamestart, NULL, 0,255, c_eat+c_store, s_tagclosename); + a(s_tagclosename, ">", 0,0, c_eat, s_tagclosedone); + a(s_tagclosename, NULL, 0,255, c_eat+c_store, s_tagclosename); + r(s_tagclosedone, t_closetag, s_start); + + a(s_whitespace, c_ws, 0,0, c_eat+c_store, s_whitespace); + a(s_whitespace, NULL, 0,255, c_nil, s_whitespacedone); + r(s_whitespacedone, t_whitespace, s_start); + + a(s_entitystart,";", 0,0, c_eat, s_entitydone); + a(s_entitystart,NULL, 0,255, c_eat+c_store, s_entity); + a(s_entity, ";", 0,0, c_eat, s_entitydone); + a(s_entity,NULL, 0,255, c_eat+c_store, s_entity); + r(s_entitydone, t_entity, s_start); + + a(s_word, c_ws, 0,0, c_nil, s_worddone); + a(s_word, "<&", 0,0, c_nil, s_worddone); + a(s_word, NULL, 0,255, c_eat+c_store, s_word); + r(s_worddone, t_word, s_start); + + a(s_tagnamestart,c_ws, 0,0, c_nil, s_tagnamedone); + a(s_tagnamestart, "/>", 0,0, c_nil, s_tagnamedone); + a(s_tagnamestart,NULL, 0,255, c_eat+c_store, s_tagname); + a(s_tagname, c_ws, 0,0, c_nil, s_tagnamedone); + a(s_tagname, "/>", 0,0, c_nil, s_tagnamedone); + a(s_tagname, NULL, 0,255, c_eat+c_store, s_tagname); + r(s_tagnamedone, t_tag, s_intag); + + a(s_intag, c_ws, 0,0, c_eat, s_intag); + a(s_intag, ">", 0,0, c_eat, s_tagend); + a(s_intag, "/", 0,0, c_eat, s_empty); + a(s_intag, NULL, 0,255, c_eat+c_store, s_attstart); + + a(s_attstart, c_ws, 0,0, c_eat, s_attdone); + a(s_attstart, "=/>", 0,0, c_nil, s_attdone); + a(s_attstart, NULL, 0,255, c_eat+c_store, s_attname); + a(s_attname, "=/>", 0,0, c_nil, s_attdone); + a(s_attname, c_ws, 0,0, c_eat, s_attdone); + a(s_attname, NULL, 0,255, c_eat+c_store, s_attname); + r(s_attdone, t_att, s_att); + a(s_att, c_ws, 0,0, c_eat, s_att); + a(s_att, "=", 0,0, c_eat, s_atteq); + a(s_att, NULL, 0,255, c_eat, s_intag); + a(s_atteq, "'", 0,0, c_eat, s_eqapos); + a(s_atteq, "\"", 0,0, c_eat, s_eqquot); + a(s_atteq, c_ws, 0,0, c_eat, s_atteq); + a(s_atteq, NULL, 0,255, c_nil, s_eqval); + + a(s_eqapos, "'", 0,0, c_eat, s_eqaposvaldone); + a(s_eqapos, NULL, 0,255, c_eat+c_store, s_eqaposval); + a(s_eqaposval, "'", 0,0, c_eat, s_eqaposvaldone); + a(s_eqaposval, NULL, 0,255, c_eat+c_store, s_eqaposval); + r(s_eqaposvaldone, t_val, s_intag); + + a(s_eqquot, "\"", 0,0, c_eat, s_eqquotvaldone); + a(s_eqquot, NULL, 0,255, c_eat+c_store, s_eqquotval); + a(s_eqquotval, "\"", 0,0, c_eat, s_eqquotvaldone); + a(s_eqquotval, NULL, 0,255, c_eat+c_store, s_eqquotval); + r(s_eqquotvaldone, t_val, s_intag); + + a(s_eqval, c_ws, 0,0, c_nil, s_eqvaldone); + a(s_eqval, "/>", 0,0, c_nil, s_eqvaldone); + a(s_eqval, NULL, 0,255, c_eat+c_store, s_eqval); + + r(s_eqvaldone, t_val, s_intag); + + r(s_tagend, t_endtag, s_start); + + r(s_empty, t_endtag, s_inempty); + a(s_inempty, ">",0,0, c_eat, s_emptyend); + a(s_inempty, NULL,0,255, c_eat, s_inempty); + r(s_emptyend, t_closeemptytag, s_start); + + a(s_prolog, "?",0,0, c_eat, s_prologq); + a(s_prolog, NULL,0,255, c_eat+c_store, s_prolog); + + a(s_prologq, ">",0,0, c_eat, s_prologdone); + a(s_prologq, NULL,0,255, c_eat+c_store, s_prolog); + r(s_prologdone, t_prolog, s_start); + + a(s_tagexcl, "-",0,0, c_eat, s_commentdash1); + a(s_tagexcl, "D",0,0, c_nil, s_dtd); + a(s_tagexcl, NULL,0,255, c_eat, s_start); + + a(s_commentdash1, "-",0,0, c_eat, s_commentdash2); + a(s_commentdash1, NULL,0,255, c_eat, s_error); + + a(s_commentdash2, "-",0,0, c_eat, s_commentenddash1); + a(s_commentdash2, NULL,0,255, c_eat+c_store, s_incomment); + + a(s_incomment, "-",0,0, c_eat, s_commentenddash1); + a(s_incomment, NULL,0,255, c_eat+c_store, s_incomment); + + a(s_commentenddash1, "-",0,0, c_eat, s_commentenddash2); + a(s_commentenddash1, NULL,0,255, c_eat+c_store, s_incomment); + + a(s_commentenddash2, ">",0,0, c_eat, s_commentdone); + a(s_commentenddash2, NULL,0,255, c_eat+c_store, s_incomment); + + r(s_commentdone, t_comment, s_start); + +} + +/* *INDENT-ON* */ + +static int +is_oneof (char c, char *chars) +{ + while (*chars) + { + if (c == *chars) + return 1; + chars++; + } + return 0; +} + +static int +nextchar (CssXml *t) +{ + int ret; + + if (t->file_in) + { + if (t->inbufpos >= t->inbuflen) + { + t->inbuflen = fread (t->inbuf, 1, CTX_XML_INBUF_SIZE, t->file_in); + t->inbufpos = 0; + if (!t->inbuflen) + return -1; + } + + ret = (int) t->inbuf[t->inbufpos++]; + + if (ret == '\n') + t->line_no++; + } + else + { + if (t->inbufpos >= t->inbuflen) + { + return -1; + } + ret = (int) t->inbuf[t->inbufpos++]; + if (ret == '\n') + t->line_no++; + } + return ret; +} + +int +xmltok_get (CssXml *t, char **data, int *pos) +{ + state_entry *s; + + init_statetable (); + ctx_string_clear (t->curdata); + while (1) + { + if (!t->c_held) + { + t->c = nextchar (t); + if (t->c == -1) + { + if (pos)*pos = t->inbufpos; + return t_eof; + } + t->c_held = 1; + } + if (t->state == s_dtd) + { /* FIXME: should make better code for skipping DTD */ + + /* int angle = 0; */ + int squote = 0; + int dquote = 0; + int abracket = 1; + + /* int sbracket = 0; */ + + ctx_string_append_byte (t->curdata, t->c); + + while (abracket) + { + switch (t->c = nextchar (t)) + { + case -1: + return t_eof; + case '<': + if ((!squote) && (!dquote)) + abracket++; + ctx_string_append_byte (t->curdata, t->c); + break; + case '>': + if ((!squote) && (!dquote)) + abracket--; + if (abracket) + ctx_string_append_byte (t->curdata, t->c); + break; + case '"': + case '\'': + case '[': + case ']': + default: + ctx_string_append_byte (t->curdata, t->c); + break; + } + } + t->c_held = 0; + t->state = s_start; + + if (pos)*pos = t->inbufpos; + return t_dtd; + } + s = &state_table[t->state][0]; + while (s->state) + { + if (s->return_type != t_none) + { + *data = (char *) ctx_string_get (t->curdata); + t->state = s->next_state; + if (s->return_type == t_tag) + ctx_string_set (t->curtag, ctx_string_get (t->curdata)); + if (s->return_type == t_endtag) + *data = (char *) ctx_string_get (t->curtag); + if (s->return_type == t_closeemptytag) + *data = (char *) ctx_string_get (t->curtag); + if (pos) + *pos = t->inbufpos; + return s->return_type; + } + if ((s->chars && is_oneof (t->c, s->chars)) + || ((s->r_start + s->r_end) + && (t->c >= s->r_start && t->c <= s->r_end))) + { + if (s->charhandling & c_store) + { + ctx_string_append_byte (t->curdata, t->c); + } + if (s->charhandling & c_eat) + { + t->c_held = 0; + } + t->state = s->next_state; + break; + } + s++; + } + } + if (pos) + *pos = t->inbufpos; + return t_eof; +} + +CssXml * +xmltok_new (FILE * file_in) +{ + CssXml *ret; + + ret = calloc (1, sizeof (CssXml)); + ret->file_in = file_in; + ret->state = s_start; + ret->curtag = ctx_string_new (""); + ret->curdata = ctx_string_new (""); + ret->inbuf = calloc (1, CTX_XML_INBUF_SIZE); + return ret; +} + +CssXml * +xmltok_buf_new (char *membuf) +{ + CssXml *ret; + + ret = calloc (1, sizeof (CssXml)); + ret->file_in = NULL; + ret->state = s_start; + ret->curtag = ctx_string_new (""); + ret->curdata = ctx_string_new (""); + ret->inbuf = (void*)membuf; + ret->inbuflen = strlen (membuf); + ret->inbufpos = 0; + return ret; +} + +void +xmltok_free (CssXml *t) +{ + ctx_string_free (t->curtag, 1); + ctx_string_free (t->curdata, 1); + + if (t->file_in) + { + /* fclose (t->file_in); */ + free (t->inbuf); + } + free (t); +} + +char *empty_tags[] = { + "img", "IMG", "br", "BR", "hr", "HR", "META", "meta", "link", "LINK", + NULL +}; + +char *endomission_tags[] = { + "li", "LI", "p", "P", "td", "TD", "tr", "TR", NULL +}; + +int +xmltok_lineno (CssXml *t) +{ + return t->line_no; +} + + +void ctx_events_clear (Ctx *ctx) +{ + if (ctx_events_frozen (ctx)) + return; + + ctx_events_clear_items (ctx); + //if (mrg->backend->mrg_clear) + // mrg->backend->mrg_clear (mrg); + + ctx_clear_bindings (ctx); +} + +void mrg_clear (Css *mrg) +{ + ctx_events_clear (mrg->ctx); + _mrg_clear_text_closures (mrg); +} + +void css_set_edge_right (Css *mrg, float val); +void css_set_edge_left (Css *mrg, float val); +void css_set_edge_top (Css *mrg, float val); +void css_set_edge_bottom (Css *mrg, float val); +float mrg_edge_right (Css *mrg); +float mrg_edge_left (Css *mrg); +float mrg_y (Css *mrg); +float mrg_x (Css *mrg); + +float mrg_em (Css *mrg); +void mrg_set_xy (Css *mrg, float x, float y); + +static float _mrg_dynamic_edge_right2 (Css *mrg, CssState *state) +{ + float ret = mrg_edge_right (mrg); + float y = mrg_y (mrg); + float em = mrg_em (mrg); + int i; + + if (mrg->floats) + for (i = state->float_base; i < mrg->floats; i++) + { + CtxFloatData *f = &mrg->float_data[i]; + if (f->type == CTX_FLOAT_RIGHT && + y >= f->y && + y - em < f->y + f->height && + + f->x < ret) + ret = f->x; + } + return ret; +} + +static float _mrg_dynamic_edge_left2 (Css *mrg, CssState *state) +{ + float ret = mrg_edge_left (mrg); + float y = mrg_y (mrg); + float em = mrg_em (mrg); + int i; + + if (mrg->floats) + for (i = state->float_base; i < mrg->floats; i++) + { + CtxFloatData *f = &mrg->float_data[i]; + if (f->type == CTX_FLOAT_LEFT && + y >= f->y && + y - em < f->y + f->height && + f->x + f->width > ret) + ret = f->x + f->width; + } + return ret; +} + +static float _mrg_parent_dynamic_edge_left (Css *mrg) +{ + CssState *state = mrg->state; + if (mrg->state_no) + state = &mrg->states[mrg->state_no-1]; + return _mrg_dynamic_edge_left2 (mrg, state); +} + +static float _mrg_parent_dynamic_edge_right (Css *mrg) +{ + CssState *state = mrg->state; + if (mrg->state_no) + state = &mrg->states[mrg->state_no-1]; + return _mrg_dynamic_edge_right2 (mrg, state); +} + +static float _mrg_dynamic_edge_left (Css *mrg) +{ + if (mrg->state->wrap_edge_left) + return mrg->state->wrap_edge_left (mrg, mrg->state->wrap_edge_data); + return mrg->state->edge_left; +} +float mrg_width (Css *mrg) +{ + if (!mrg) return 640; + return ctx_width (mrg->ctx) / mrg->ddpx; +} + +float mrg_height (Css *mrg) +{ + if (!mrg) return 480; + return ctx_height (mrg->ctx) / mrg->ddpx; +} + +static float _mrg_dynamic_edge_right (Css *mrg) +{ + if (mrg->state->wrap_edge_right) + return mrg->state->wrap_edge_right (mrg, mrg->state->wrap_edge_data); + return mrg->state->edge_right; +} + +static float wrap_edge_left (Css *mrg, void *data) +{ + CssState *state = mrg->state; + return _mrg_dynamic_edge_left2 (mrg, state); +} + +static float wrap_edge_right (Css *mrg, void *data) +{ + CssState *state = mrg->state; + return _mrg_dynamic_edge_right2 (mrg, state); +} + +static void clear_left (Css *mrg) +{ + float y = mrg_y (mrg); + int i; + + if (mrg->floats) + { + for (i = mrg->state->float_base; i < mrg->floats; i++) + { + CtxFloatData *f = &mrg->float_data[i]; + { + if (f->type == CTX_FLOAT_LEFT) + { + if (f->y + f->height > y) + y = f->y + f->height; + } + } + } + } + mrg_set_xy (mrg, mrg_x (mrg), y); +} + +static void clear_right (Css *mrg) +{ + float y = mrg_y (mrg); + int i; + + if (mrg->floats) + { + for (i = mrg->state->float_base; i < mrg->floats; i++) + { + CtxFloatData *f = &mrg->float_data[i]; + { + if (f->type == CTX_FLOAT_RIGHT) + { + if (f->y + f->height > y) + y = f->y + f->height; + } + } + } + } + //fprintf (stderr, "%f}", y); + mrg_set_xy (mrg, mrg_x (mrg), y); +} + +/** + * ctx_style: + * @mrg the mrg-context + * + * Returns the currently + * + */ +CtxStyle *ctx_style (Css *mrg) +{ + return &mrg->state->style; +} + +static void clear_both (Css *mrg) +{ +#if 0 + clear_left (mrg); + clear_right (mrg); +#else + float y = mrg_y (mrg); + int i; + + if (!mrg->state)return; + if (mrg->floats) + { + for (i = mrg->state->float_base; i < mrg->floats; i++) + { + CtxFloatData *f = &mrg->float_data[i]; + { + if (f->y + f->height > y) + y = f->y + f->height; + } + } + } + //y += mrg_em (mrg) * ctx_style(mrg)->line_height; + mrg_set_xy (mrg, mrg_x (mrg), y); +#endif +} + +static float mrg_edge_bottom (Css *mrg) +{ + return mrg->state->edge_bottom; +} + +static float mrg_edge_top (Css *mrg) +{ + return mrg->state->edge_top; +} + +float mrg_edge_left (Css *mrg) +{ + return mrg->state->edge_left; +} + +float mrg_edge_right (Css *mrg) +{ + return mrg->state->edge_right; +} + +float _mrg_dynamic_edge_right (Css *mrg); +float _mrg_dynamic_edge_left (Css *mrg); + +void css_set_edge_top (Css *itk, float edge) +{ + Css *mrg = itk; + itk->state->edge_top = edge; + + // we always set top last, since it causes the + // reset of line handling + // + mrg_set_xy (mrg, _mrg_dynamic_edge_left (mrg) + + ctx_get_float (mrg_ctx(mrg), SQZ_text_indent) + , mrg->state->edge_top);// + mrg_em (mrg)); + + + itk->edge_top = edge; + itk->edge_bottom = itk->height + itk->edge_top; +} + +void css_set_edge_left (Css *itk, float val) +{ + itk->state->edge_left = val; + itk->edge_left = val; + itk->edge_right = itk->width + itk->edge_left; +} + +void css_set_edge_bottom (Css *itk, float edge) +{ + itk->state->edge_bottom = edge; + itk->edge_bottom = edge; + itk->height = itk->edge_bottom - itk->edge_top; +} + +void css_set_edge_right (Css *itk, float edge) +{ + itk->state->edge_right = edge; + itk->edge_right = edge; + itk->width = itk->edge_right - itk->edge_left; +} + +float mrg_rem (Css *mrg) +{ + return mrg->rem; +} + + +void css_start (Css *mrg, const char *class_name, void *id_ptr); +void css_start_with_style (Css *mrg, + const char *style_id, + void *id_ptr, + const char *style); +void css_start_with_stylef (Css *mrg, const char *style_id, void *id_ptr, + const char *format, ...); + +static void ctx_parse_style_id (Css *mrg, + const char *style_id, + CtxStyleNode *node) +{ + const char *p; + char temp[128] = ""; + int temp_l = 0; + if (!style_id) + { + return; // XXX: why does this happen? + } + + memset (node, 0, sizeof (CtxStyleNode)); + + for (p = style_id; ; p++) + { + switch (*p) + { + case '.': + case ':': + case '#': + case 0: + if (temp_l) + { + switch (temp[0]) + { + case '.': + { + int i = 0; + for (i = 0; node->classes_hash[i]; i++); + node->classes_hash[i] = ctx_strhash (&temp[1]); + } + break; + case ':': + { + int i = 0; + for (i = 0; node->pseudo[i]; i++); + node->pseudo[i] = mrg_intern_string (&temp[1]); + for (i = 0; node->pseudo_hash[i]; i++); + node->pseudo_hash[i] = ctx_strhash (&temp[1]); + } + break; + case '#': + node->id = mrg_intern_string (&temp[1]); + node->id_hash = ctx_strhash (&temp[1]); + break; + default: + node->element_hash = ctx_strhash (temp); + break; + } + temp_l = 0; + } + if (*p == 0) + return; + temp[temp_l++] = *p; // XXX: added to make reported fallthrough + temp[temp_l]=0; // not be reported - butexplicit + break; + default: + temp[temp_l++] = *p; + temp[temp_l]=0; + } + } +} + +void _ctx_initial_style (Css *mrg) +{ + CtxStyle *s = ctx_style (mrg); + + /* things not set here, are inherited from the parent style context, + * more properly would be to rig up a fresh context, and copy inherited + * values over, that would permit specifying inherit on any propery. + */ + + s->text_decoration= 0; + s->display = CTX_DISPLAY_INLINE; + s->float_ = CTX_FLOAT_NONE; + s->clear = CTX_CLEAR_NONE; + s->overflow = CTX_OVERFLOW_VISIBLE; + s->position = CTX_POSITION_STATIC; + + SET_PROP(border_top_width, 0); + SET_PROP(border_left_width, 0); + SET_PROP(border_right_width, 0); + SET_PROP(border_bottom_width, 0); + SET_PROP(margin_top, 0); + SET_PROP(margin_left, 0); + SET_PROP(margin_right, 0); + SET_PROP(margin_bottom, 0); + SET_PROP(padding_top, 0); + SET_PROP(padding_left, 0); + SET_PROP(padding_right, 0); + SET_PROP(padding_bottom, 0); + SET_PROP(top, 0); + SET_PROP(left, 0); + SET_PROP(right, 0); + SET_PROP(bottom, 0); + + SET_PROP(min_width, 0); + SET_PROP(max_width, 0); + s->width_auto = 1; + s->margin_left_auto = 0; + s->margin_right_auto = 0; + //SET_PROP(width, 42); + + SET_PROPS(class,""); + SET_PROPS(id,""); + + //ctx_set_float (mrg->ctx, SQZ_stroke_width, 4.0); + + CtxColor *color = ctx_color_new (); + ctx_get_color (mrg->ctx, SQZ_color, color); + + ctx_set_color (mrg->ctx, SQZ_border_top_color, color); + ctx_set_color (mrg->ctx, SQZ_border_bottom_color, color); + ctx_set_color (mrg->ctx, SQZ_border_left_color, color); + ctx_set_color (mrg->ctx, SQZ_border_right_color, color); + +#if 0 + s->stroke_color.red = 1; + s->stroke_color.green = 0; + s->stroke_color.blue = 1; + s->stroke_color.alpha = 1; + s->fill_color.red = 1; + s->fill_color.green = 1; + s->fill_color.blue = 0; + s->fill_color.alpha = 1; +#endif + + ctx_color_set_from_string (mrg->ctx, color, "transparent"); + ctx_set_color (mrg->ctx, SQZ_background_color, color); + ctx_color_free (color); +} + + + +/* mrg - MicroRaptor Gui + * Copyright (c) 2014 Øyvind Kolås + * + * 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 . + */ + +const char * html_css = +"html,address,\n" +"blockquote,\n" +"body,dd, div,\n" +"dl,dt,fieldset, form,\n" +"frame,frameset,\n" +"h1,h2,h3, h4,\n" +"h5,h6,noframes,\n" +"ol,p,ul,center,\n" +"dir,hr,menu,pre{display:block;unicode-bidi:embed}\n" +"h1,h2,h3,h4,h5{page-break-after:avoid}\n" +"li{display:list-item}\n" +"table{display:block;}\n" +//"table{display:table}\n" +//"tr{display:table-row}\n" +"tr{display:block}\n" +"thead{display:table-header-group }\n" +"tbody{display:table-row-group }\n" +"tfoot{display:table-footer-group }\n" +"col{display:table-column}\n" +"img{display:inline-block}\n" +"colgroup{display:table-column-group}\n" +"td,th{display:block-inline}\n" +//"td,th{display:table-cell}\n" +"caption{display:table-caption}\n" +"th{font-weight:bolder;text-align:center}\n" +"caption{text-align:center}\n" +"body{margin:8px;}\n" // was 0.5em +"h1{font-size:2em;margin:.67em 0;}\n" +"h2{font-size:1.5em;margin:.75em 0}\n" +"h3{font-size:1.17em;margin:.83em 0}\n" +"h4,p," +"blockquote,ul," +"fieldset,form," +"ol,dl,dir," +"menu{margin:1.12em 0}\n" +"h5{font-size:.83em;margin: 1.5em 0}\n" +"h6{font-size:.75em;margin: 1.67em 0}\n" +"h1,h2,h3,h4,\n" +"h5,h6,b,\n" +"strong{font-weight:bolder}\n" +"blockquote{margin-left:4em;margin-right:4em}\n" +"i,cite,em,\n" +"var,address{font-style:italic}\n" +"pre,tt,code,\n" +"kbd,samp{font-family:monospace}\n" +"pre{white-space:pre}\n" +"button,textarea,\n" +"input,select{display:inline-block}\n" +"big{font-size:1.17em}\n" +"small,sub,sup{font-size:.83em}\n" +"sub{vertical-align:sub}\n" +"sup{vertical-align:super}\n" +"table{border-spacing:2px;}\n" +"thead,tbody,\n" +"tfoot{vertical-align:middle}\n" +"td,th,tr{vertical-align:inherit}\n" +"s,strike,del{text-decoration:line-through}\n" +"ol,ul,dir," +"menu{padding-left:2.5em}\n" +"dd{margin-left:3em}\n" +"ol{list-style-type:decimal}\n" +"ol ul,ul ol," +"ul ul,ol ol{margin-top: 0;margin-bottom: 0}\n" +"u,ins{text-decoration:underline}\n" +//"br:before{content:\"\\A\";white-space:pre-line}\n" +"center{text-align:center}\n" +":link,:visited{text-decoration:underline}\n" +":focus{outline:thin dotted invert}\n" +".cursor{color:white;background: black;} \n" +"br{display:block;}\n" +"html{font-weight:normal;background-color:white;}\n" +"body{background-color:transparent;color:black;}\n" +"a{color:blue;text-decoration: underline;}\n" +"a:hover{background:black;color:white;text-decoration:underline; }\n" +"head{display:none;}\n" +"title{display:none;}\n" +//"hr{border:1px inset black;margin-bottom: 0.5em;margin-top:0.5em;}\n" +"hr{font-size:1px;border-bottom: 1px solid red;display:block;}\n" + +".scroll{background:blue;}\n" +".wallpaper, .itk{background:#124;color:#f43}\n" +".focused{background:#fff;color:#000}\n" + +" .children{ border:1px solid green;padding-left:3em; }\n" +" .selection{background:white; color: red; }\n" +" :focused {color: yellow; }\n" +" .fnord { color: green; }\n" +" .item { border: 1px solid transparent; color: white; }" +" .item:focused { color: yellow; border: 1px solid blue;}" + /* .item>.text::before { content:"-";display:block;position:absolute;margin-left:-1.2em; } */ + +" .file { border: 1px solid green; margin-left:0;margin-right:0.2em; width: 3em; margin-bottom: 0.2em; height: 3em; display: inline-block; }" + + +//"h1,h2,h3,h4,h5,h6,p,div,b,span,ul,li,ol,dl,dt,dl,propline,button{border-left-color:gray;border-right-color:gray;}\n" + +//"html{font-size:10.0px;color:white;}\n" // from ACID1 - not parsed right + +//// itk defaults stylesheet +"toggle {border: 1px solid green;border: 1px solid red;color:yellow;display:block;}\n" +"button {border: 1px solid green;}\n" +"choice {border: 1px solid brown;display:inline-block;}\n" +".choice_menu_wrap {border: 1px solid red;display:block;position:relative;top:-1.2em;left:4em;height:0;width:0;}\n" +".choice_menu {border: 1px solid red;display:block;position:absolute;width:7em;}\n" +".choice {border: 1px solid brown;display:block;background:black}\n" +".choice:chosen{border: 1px solid brown;display:block;background:red;}\n" +"button:focused{color:yellow;border: 1px solid yellow;background:blue;}\n" +"label {display:inline; color:white;}\n" +"slider {display:inline-block; color:white;}\n" +"propline {display:block;margin-top:0.25em;margin-bottom:0.25em;border:1 px solid transparent;}\n" +"propline:focused {border:1px solid red;background:#faf}\n" +; + + +typedef struct CtxStyleEntry { + char *selector; + CtxStyleNode parsed[CTX_MAX_SELECTOR_LENGTH]; + int sel_len; + char *css; + int specificity; +} CtxStyleEntry; + +static void free_entry (CtxStyleEntry *entry) +{ + free (entry->selector); + free (entry->css); + free (entry); +} + +static int ctx_css_compute_specifity (const char *selector, int priority) +{ + const char *p; + int n_id = 0; + int n_class = 0; + int n_tag = 0; + int n_pseudo = 0; + int in_word = 0; + + for (p = selector; *p; p++) + { + switch (*p) + { + case ' ': + in_word = 0; + break; + case ':': + in_word = 1; + n_pseudo++; + break; + case '.': + in_word = 1; + n_class++; + break; + case '#': + in_word = 1; + n_id++; + break; + default: + if (!in_word) + { + in_word = 1; + n_tag++; + } + } + } + return priority * 100000 + n_pseudo * 10000 + n_id * 1000 + n_class * 100 + n_tag * 10; +} + +static void ctx_css_parse_selector (Css *mrg, const char *selector, CtxStyleEntry *entry) +{ + const char *p = selector; + char section[256]; + int sec_l = 0; + + char type = ' '; + + int direct_descendant = 0; + for (p = selector; ; p++) + { + switch (*p) + { + case '.': case ':': case '#': case ' ': case 0: case '>': + if (sec_l) + { + switch (type) + { + case ' ': + entry->parsed[entry->sel_len].element_hash = ctx_strhash (section); + entry->parsed[entry->sel_len].direct_descendant = direct_descendant; + direct_descendant = 0; + break; + case '#': + entry->parsed[entry->sel_len].id = mrg_intern_string (section); + entry->parsed[entry->sel_len].id_hash = ctx_strhash (section); + entry->parsed[entry->sel_len].direct_descendant = direct_descendant; + direct_descendant = 0; + break; + case '.': + { + int i = 0; + for (i = 0; entry->parsed[entry->sel_len].classes_hash[i]; i++); + entry->parsed[entry->sel_len].classes_hash[i] = ctx_strhash (section); + } + entry->parsed[entry->sel_len].direct_descendant = direct_descendant; + direct_descendant = 0; + break; + case ':': + { + int i = 0; + for (i = 0; entry->parsed[entry->sel_len].pseudo[i]; i++); + entry->parsed[entry->sel_len].pseudo[i] = mrg_intern_string (section); + for (i = 0; entry->parsed[entry->sel_len].pseudo_hash[i]; i++); + entry->parsed[entry->sel_len].pseudo_hash[i] = ctx_strhash (section); + entry->parsed[entry->sel_len].direct_descendant = direct_descendant; + direct_descendant = 0; + } + break; + } + if (*p == ' ' || *p == 0 || *p == '>') + { + entry->sel_len ++; + } + } + type = *p; + if (*p == '>') + { + direct_descendant = 1; + type = ' '; + } + if (*p == 0) + { + +#if 0 + fprintf (stderr, "%s: ", selector); + for (int i = 0; i < entry->sel_len; i++) + { + if (entry->parsed[i].direct_descendant) + fprintf (stderr, "DP "); + fprintf (stderr, "e: %s ", ctx_str_decode (entry->parsed[i].element_hash)); + for (int j = 0; entry->parsed[i].classes_hash[j]; j++) + fprintf (stderr, "c: %s ", ctx_str_decode (entry->parsed[i].classes_hash[j])); + } + fprintf (stderr, "\n"); +#endif + + return; + } + section[(sec_l=0)] = 0; + break; + default: + section[sec_l++] = *p; + section[sec_l] = 0; + break; + } + } + + // not reached +} + +static void ctx_stylesheet_add_selector (Css *mrg, const char *selector, const char *css, int priority) +{ + CtxStyleEntry *entry = calloc (sizeof (CtxStyleEntry), 1); + entry->selector = strdup (selector); + entry->css = strdup (css); + entry->specificity = ctx_css_compute_specifity (selector, priority); + + //fprintf (stderr, "\nsel:%s]\ncss:%s\npri:%i\n", selector, css, priority); + + ctx_css_parse_selector (mrg, selector, entry); + ctx_list_prepend_full (&mrg->stylesheet, entry, (void*)free_entry, NULL); +} + + +#define MAKE_ERROR \ + if (error)\ + {\ + char errbuf[128];\ + sprintf (errbuf, "%i unexpected %c at %i' %c%c%c", __LINE__, *p, (int)(p-css),\ + p[0], p[1], p[2]);\ + *error = strdup (errbuf);\ + } + + +typedef struct _CtxCssParseState CtxCssParseState; + +struct _CtxCssParseState { + int state; + char rule[CTX_MAX_CSS_RULES][CTX_MAX_CSS_RULELEN]; + int rule_no ; + int rule_l[CTX_MAX_CSS_RULES]; + char val[CTX_MAX_CSS_STRINGLEN]; + int val_l; +}; + +/* XXX: this funciton has no buffer bounds checking */ +/* doesnt balance {} () [] and quotes */ + +enum +{ + NEUTRAL = 0, + NEUTRAL_COMMENT, + IN_RULE, + RULE_COMMENT, + IN_ARULE, + ARULE_COMMENT, + IN_IMPORT, + IMPORT_COMMENT, + EXPECT_COLON, + COLON_COMMENT, + EXPECT_VAL, + EXPECT_VAL_COMMENT, + IN_VAL, + VAL_COMMENT, +}; + + +static void _ctx_stylesheet_add (CtxCssParseState *ps, Css *mrg, const char *css, const char *uri_base, + int priority, char **error) +{ + const char *p; + if (!ps) + ps = mrg->css_parse_state = calloc (sizeof (CtxCssParseState), 1); + + if (!css) + return; + + for (p = css; *p; p++) + { + switch (ps->state) + { + case NEUTRAL_COMMENT: + case RULE_COMMENT: + case ARULE_COMMENT: + case IMPORT_COMMENT: + case VAL_COMMENT: + case EXPECT_VAL_COMMENT: + case COLON_COMMENT: if (p[0] == '*' && p[1] == '/') { p++; ps->state--; } break; + case NEUTRAL: + switch (*p) + { + case '/': if (p[1] == '*') { p++; ps->state = NEUTRAL_COMMENT; } break; + case ' ': + case '\n': + case '\r': + case '\t': + case ';': + break; + case '{': + case '}': + MAKE_ERROR; + return; + case '@': + ps->state = IN_ARULE; + ps->rule[ps->rule_no][ps->rule_l[ps->rule_no]++] = *p; + ps->rule[ps->rule_no][ps->rule_l[ps->rule_no]] = 0; + break; + default: + ps->state = IN_RULE; + ps->rule[ps->rule_no][ps->rule_l[ps->rule_no]++] = *p; + ps->rule[ps->rule_no][ps->rule_l[ps->rule_no]] = 0; + break; + } + break; + case IN_ARULE: + switch (*p) + { + case '/': if (p[1] == '*') { p++; ps->state = ARULE_COMMENT; } break; + case '{': + ps->state = IN_VAL; // should be AVAL for media sections... + break; + case '\n': + case '\r': + case '\t': + case ' ': + if (!strcmp (ps->rule[0], "import")) + { + MAKE_ERROR; + } + else + { + ps->state = IN_IMPORT; + } + break; + case ';': + case '}': + MAKE_ERROR; + return; + default: + ps->state = IN_ARULE; + ps->rule[ps->rule_no][ps->rule_l[ps->rule_no]++] = *p; + ps->rule[ps->rule_no][ps->rule_l[ps->rule_no]] = 0; + break; + } + break; + case IN_IMPORT: + switch (*p) + { + int no; + case '/': if (p[1] == '*') { p++; ps->state = IMPORT_COMMENT; } break; + case ';': + while (ps->val_l && ( + ps->val[ps->val_l-1] == ' ' || + ps->val[ps->val_l-1] == '\n' || + ps->val[ps->val_l-1] == '\r' || + ps->val[ps->val_l-1] == '\t')) + ps->val_l--; + while (ps->val_l && ps->val[ps->val_l-1] != ')') + ps->val_l--; + if (ps->val[ps->val_l-1] == ')') + ps->val_l--; + if (ps->val[ps->val_l-1] == '"') + ps->val_l--; + ps->val[ps->val_l]=0; + + if(mrg->mrg_get_contents){ + char *contents = NULL; + long length; + char *uri = ps->val; + + /* really ugly string trimming to get to import uri.. */ + while (uri[0]==' ') uri++; + if (!memcmp (uri, "url", 3)) uri+=3; + if (uri[0]=='(') uri++; + if (uri[0]=='"') uri++; + + /* XXX: should parse out the media part, and check if we should + * really include this file + */ + + mrg_get_contents (mrg, mrg->uri_base, uri, &contents, &length); + if (contents) + { + CtxCssParseState child_parser = {0,}; + _ctx_stylesheet_add (&child_parser, mrg, contents, uri, priority, error); + free (contents); + } + else + { + fprintf (stderr, "404 - %s\n", uri); + } + } + + for (no = 0; no < ps->rule_no+1; no ++) + ps->rule[no][ps->rule_l[no]=0] = 0; + ps->val_l = 0; + ps->val[0] = 0; + ps->rule_no = 0; + ps->state = NEUTRAL; + break; + default: + ps->state = IN_IMPORT; + ps->val[ps->val_l++] = *p; + ps->val[ps->val_l] = 0; + break; + + } + break; + case IN_RULE: + switch (*p) + { + case '/': if (p[1] == '*') { p++; ps->state = RULE_COMMENT; } break; + case '{': + ps->state = IN_VAL; + break; + case '\n': + case '\r': + case '\t': + ps->rule[ps->rule_no][ps->rule_l[ps->rule_no]++] = ' '; + ps->rule[ps->rule_no][ps->rule_l[ps->rule_no]] = 0; + break; + case ',': + ps->state = NEUTRAL; + ps->rule_no++; + break; + case ';': + case '}': + MAKE_ERROR; + return; + default: + ps->state = IN_RULE; + ps->rule[ps->rule_no][ps->rule_l[ps->rule_no]++] = *p; + ps->rule[ps->rule_no][ps->rule_l[ps->rule_no]] = 0; + break; + } + break; + case EXPECT_COLON: + switch (*p) + { + case '/': if (p[1] == '*') { p++; ps->state = COLON_COMMENT; } break; + case ' ': + case '\n': + case '\r': + case '\t': + break; + case ':': + ps->state = EXPECT_VAL; + break; + default: + MAKE_ERROR; + return; + } + break; + case EXPECT_VAL: + switch (*p) + { + case '/': if (p[1] == '*') { p++; ps->state = EXPECT_VAL_COMMENT; } break; + case ' ': + case '\n': + case '\r': + case '\t': + case ';': + break; + case '{': + ps->state = IN_VAL; + break; + default: + MAKE_ERROR; + return; + } + break; + case IN_VAL: + switch (*p) + { + int no; + + case '/': if (p[1] == '*') { p++; ps->state = VAL_COMMENT; } break; + case '}': + while (ps->val_l && ( + ps->val[ps->val_l-1] == ' ' || + ps->val[ps->val_l-1] == '\n' || + ps->val[ps->val_l-1] == '\r' || + ps->val[ps->val_l-1] == '\t')) + ps->val_l--; + ps->val[ps->val_l]=0; + + for (no = 0; no < ps->rule_no+1; no ++) + { + while (ps->rule_l[no] && ( + ps->rule[no][ps->rule_l[no]-1] == ' ' || + ps->rule[no][ps->rule_l[no]-1] == '\n' || + ps->rule[no][ps->rule_l[no]-1] == '\r' || + ps->rule[no][ps->rule_l[no]-1] == '\t')) + ps->rule_l[no]--; + ps->rule[no][ps->rule_l[no]]=0; + + ctx_stylesheet_add_selector (mrg, ps->rule[no], ps->val, priority); + ps->rule[no][ps->rule_l[no]=0] = 0; + } + + ps->val_l = 0; + ps->val[0] = 0; + ps->rule_no = 0; + ps->state = NEUTRAL; + break; + default: + ps->state = IN_VAL; + ps->val[ps->val_l++] = *p; + ps->val[ps->val_l] = 0; + break; + + } + break; + } + } +} + +void ctx_stylesheet_add (Css *mrg, const char *css, const char *uri_base, + int priority, char **error) +{ + CtxCssParseState *ps = mrg->css_parse_state; + _ctx_stylesheet_add (ps, mrg, css, uri_base, priority, error); +} +#define CTX_STYLE_INTERNAL 10 +#define CTX_STYLE_GLOBAL 15 +#define CTX_STYLE_XML 20 +#define CTX_STYLE_APP 20 +#define CTX_STYLE_INLINE 25 +#define CTX_STYLE_CODE 30 + +void css_css_default (Css *mrg) +{ + char *error = NULL; + ctx_stylesheet_add (mrg, html_css, NULL, CTX_STYLE_INTERNAL, &error); + if (error) + { + fprintf (stderr, "Css css parsing error: %s\n", error); + } + + ctx_stylesheet_add (mrg, +"bold{font-weight:bold;}" +"dim*,dim{opacity:0.5;}" +"underline*,underline{text-decoration:underline;}" +"reverse*,selected*,reverse,selected{text-decoration:reverse;}" +"unhandled{color:cyan;}" +"binding:key{background-color:white;color:black;}" +"binding:label{color:cyan;}" + ,NULL, CTX_STYLE_INTERNAL, &error); + + if (error) + { + fprintf (stderr, "mrg css parsing error: %s\n", error); + } +} + +void css_stylesheet_clear (Css *mrg) +{ + if (mrg->stylesheet) + ctx_list_free (&mrg->stylesheet); + css_css_default (mrg); +} + +typedef struct CtxStyleMatch +{ + CtxStyleEntry *entry; + int score; +} CtxStyleMatch; + +static int compare_matches (const void *a, const void *b, void *d) +{ + const CtxStyleMatch *ma = a; + const CtxStyleMatch *mb = b; + return mb->score - ma->score; +} + +static inline int _ctx_nth_match (const char *selector, int child_no) +{ + const char *tmp = selector + 10; + int a = 0; + int b = 0; + + if (!strcmp (tmp, "odd)")) + { + a = 2; b = 1; + } + else if (!strcmp (tmp, "even)")) + { + a = 2; b = 0; + } + else + { + if (ctx_strchr (tmp, 'n')) + { + a = atoi (tmp); + b = atoi (ctx_strchr (tmp, 'n')+1); + } + else + { + b = atoi (tmp); + } + } + + if (!a) + return child_no == b; + else + if (child_no == b || + ((child_no - b > 0) == (a>0))) + return !((child_no - b) % a); + + return 0; +} + +int _ctx_child_no (Css *mrg) +{ + return mrg->states[mrg->state_no-1].children; +} + +static inline int match_nodes (Css *mrg, CtxStyleNode *sel_node, CtxStyleNode *subject) +{ + int j, k; + + if (sel_node->element_hash && + sel_node->element_hash != subject->element_hash) + return 0; + + if (sel_node->id && + sel_node->id != subject->id) + return 0; + + for (j = 0; sel_node->classes_hash[j]; j++) + { + int found = 0; + for (k = 0; subject->classes_hash[k] && !found; k++) + { + if (sel_node->classes_hash[j] == subject->classes_hash[k]) + found = 1; + } + if (!found) + return 0; + } + for (j = 0; sel_node->pseudo[j]; j++) + { + if (ctx_strhash (sel_node->pseudo[j]) == SQZ_first_child) + { + if (!(_ctx_child_no (mrg) == 1)) + return 0; + } + else if (!strncmp (sel_node->pseudo[j], "nth-child(", 10)) + { + if (!_ctx_nth_match (sel_node->pseudo[j], _ctx_child_no (mrg))) + return 0; + } + else + { + int found = 0; + + for (k = 0; subject->pseudo[k] && !found; k++) + { + if (sel_node->pseudo_hash[j] == subject->pseudo_hash[k]) + found = 1; + } + if (!found) + return 0; + } + } + return 1; +} + +static int ctx_selector_vs_ancestry (Css *mrg, + CtxStyleEntry *entry, + CtxStyleNode **ancestry, + int a_depth) +{ + int s = entry->sel_len - 1; + //a_depth = mrg->state_no + 1; +#if 1 + +#if 0 + for (int i = 1; i < a_depth; i++) + { + if (ancestry[i] != &mrg->states[i].style_node) + { + fprintf (stderr, "%i %p!=%p %i \n", i, ancestry[i], + &mrg->states[i].style_node, a_depth); + fprintf (stderr, "%p\n", &mrg->states[mrg->state_no].style_node); + fprintf (stderr, "%p\n", &mrg->states[mrg->state_no+1].style_node); + fprintf (stderr, "%p\n", &mrg->states[mrg->state_no-1].style_node); + } + } +#endif + + if (entry->parsed[s].direct_descendant == 0) + { + /* right most part of selector must match */ + if (a_depth == 1) + { + // XXX it is an oddity how we need to use this instead of mrg->states[0].style_node directly.. + if (!match_nodes (mrg, &entry->parsed[s], ancestry[a_depth-1])) + //if (!match_nodes (mrg, &entry->parsed[s], &mrg->states[0].style_node)) + return 0; + } + else + { + if (!match_nodes (mrg, &entry->parsed[s], &mrg->states[a_depth-1].style_node)) + return 0; + } + + s--; + a_depth--; + } + + if (s < 0 || a_depth < 0) + return 1; +#endif + + while (s >= 0) + { + int ai; + int found_node = 0; + + if (entry->parsed[s].direct_descendant && s > 0) + { // s>0 should always be true when direct_descendant is true + ai = a_depth-1; + { + if (s >0 && ai >0 && match_nodes (mrg, &entry->parsed[s], &mrg->states[ai].style_node) && + match_nodes (mrg, &entry->parsed[s-1], &mrg->states[ai-1].style_node)) + found_node = 1; + } + ai--; + s-=2; + } + else + { + for (ai = a_depth-1; ai >= 0 && !found_node; ai--) + { + if (match_nodes (mrg, &entry->parsed[s], &mrg->states[ai].style_node)) + found_node = 1; + } + s--; + } + if (found_node) + { + a_depth = ai; + } + else + { + return 0; + } + } + + return 1; +} + +static int ctx_css_selector_match (Css *mrg, CtxStyleEntry *entry, CtxStyleNode **ancestry, int a_depth) +{ + if (entry->selector[0] == '*' && + entry->selector[1] == 0) + return entry->specificity; + + if (a_depth == 0) + return 0; + + if (ctx_selector_vs_ancestry (mrg, entry, ancestry, a_depth)) + return entry->specificity; + + return 0; +} + +static char *_ctx_css_compute_style (Css *mrg, CtxStyleNode **ancestry, int a_depth) +{ + CtxList *l; + CtxList *matches = NULL; + int totlen = 2; + char *ret = NULL; + + for (l = mrg->stylesheet; l; l = l->next) + { + CtxStyleEntry *entry = l->data; + int score = ctx_css_selector_match (mrg, entry, ancestry, a_depth); + if (score) + { + CtxStyleMatch *match = malloc (sizeof (CtxStyleMatch)); + match->score = score; + match->entry = entry; + ctx_list_prepend_full (&matches, match, (void*)free, NULL); + totlen += strlen (entry->css) + 1; + } + } + + if (matches) + { + CtxList *l; + char *p; + + p = ret = malloc (totlen); + + ctx_list_sort (&matches, compare_matches, NULL); + for (l = matches; l; l = l->next) + { + CtxStyleMatch *match = l->data; + CtxStyleEntry *entry = match->entry; + strcpy (p, entry->css); + p += strlen (entry->css); + strcpy (p, ";"); + p ++; + } + ctx_list_free (&matches); + } + return ret; +} + +static void _ctx_get_ancestry (Css *mrg, CtxStyleNode **ancestry) +{ + if (mrg->state_no>0) + ancestry[0] = &mrg->states[0].style_node; +} + +char *_ctx_stylesheet_collate_style (Css *mrg) +{ + CtxStyleNode *ancestry[1]; + _ctx_get_ancestry (mrg, ancestry); + char *ret = _ctx_css_compute_style (mrg, ancestry, mrg->state_no + 1); + return ret; +} + +void mrg_set_line_height (Css *mrg, float line_height) +{ + ctx_style (mrg)->line_height = line_height; +} + +float mrg_line_height (Css *mrg) +{ + return ctx_style (mrg)->line_height; +} + +void mrg_set_rem (Css *mrg, float em) +{ + mrg->rem = em; +} + +float mrg_em (Css *mrg) +{ + return mrg->state->style.font_size; +} + +void mrg_set_em (Css *mrg, float em) +{ + mrg->state->style.font_size = em; +} + +void ctx_css_set (Css *mrg, const char *css) +{ + ctx_string_set (mrg->style, css); +} + +void ctx_css_add (Css *mrg, const char *css) +{ + ctx_string_append_str (mrg->style, css); +} + +void css_stylesheet_clear (Css *mrg); +void ctx_stylesheet_add (Css *mrg, const char *css, const char *uri, + int priority, + char **error); + +void ctx_css_set (Css *mrg, const char *css); +void ctx_css_add (Css *mrg, const char *css); + +static inline float mrg_parse_px_x (Css *mrg, const char *str, char **endptr) +{ + float result = 0; + char *end = NULL; +#define PPI 96 + + if (!str) + return 0.0; + + result = _ctx_parse_float (str, &end); + if (endptr) + *endptr=end; + + //if (end[0]=='%v') /// XXX % of viewport; regard less of stacking + if (end[0]=='%') + { + result = result / 100.0 * (mrg_edge_right (mrg) - mrg_edge_left (mrg)); + + if (endptr) + *endptr=end + 1; + } + else if (end[0]=='v' && end[1] == 'h') + { + result = result / 100.0 * (mrg_edge_bottom (mrg) - mrg_edge_top (mrg)); + if (endptr) + *endptr=end + 1; + } + else if (end[0]=='v' && end[1] == 'w') + { + result = result / 100.0 * (mrg_edge_right (mrg) - mrg_edge_left (mrg)); + if (endptr) + *endptr=end + 1; + } + else if (end[0]=='r' && end[1]=='e' && end[2]=='m') + { + result *= mrg_rem (mrg); + if (endptr) + *endptr=end + 3; + } + else if (end[0]=='e' && end[1]=='m') + { + result *= mrg_em (mrg); + if (endptr) + *endptr=end + 2; + } + else if (end[0]=='p' && end[1]=='x') + { + if (endptr) + *endptr=end + 2; + } + else if (end[0]=='p' && end[1]=='t') + { + result = (result / PPI) * 72; + if (endptr) + *endptr=end + 2; + } + else if (end[0]=='p' && end[1]=='c') + { + result = (result / PPI) * 72 / 12; + if (endptr) + *endptr=end + 2; + } + else if (end[0]=='i' && end[1]=='n') + { + result = result / PPI; + if (endptr) + *endptr=end + 2; + } + else if (end[0]=='c' && end[1]=='m') + { + result = (result / PPI) * 2.54; + if (endptr) + *endptr=end + 2; + } + else if (end[0]=='m' && end[1]=='m') + { + result = (result / PPI) * 25.4; + if (endptr) + *endptr=end + 2; + } + return result; +} + +static inline float mrg_parse_px_y (Css *mrg, const char *str, char **endptr) +{ + float result = 0; + char *end = NULL; + if (!str) + return 0.0; + + result = _ctx_parse_float (str, &end); + if (endptr) + *endptr=end; + + if (end[0]=='%') + { + result = result / 100.0 * (mrg_edge_bottom (mrg) - mrg_edge_top (mrg)); + if (endptr) + *endptr=end + 1; + } + else if (end[0]=='v' && end[1] == 'h') + { + result = result / 100.0 * (mrg_edge_bottom (mrg) - mrg_edge_top (mrg)); + if (endptr) + *endptr=end + 1; + } + else if (end[0]=='v' && end[1] == 'w') + { + result = result / 100.0 * (mrg_edge_right (mrg) - mrg_edge_left (mrg)); + if (endptr) + *endptr=end + 1; + } + else if (end[0]=='r' && end[1]=='e' && end[2]=='m') + { + result *= mrg_rem (mrg); + if (endptr) + *endptr=end + 3; + } + else if (end[0]=='e' && end[1]=='m') + { + result *= mrg_em (mrg); + if (endptr) + *endptr=end + 2; + } + else if (end[0]=='p' && end[1]=='x') + { + if (endptr) + *endptr=end + 2; + } + else if (end[0]=='p' && end[1]=='t') + { + result = (result / PPI) * 72; + if (endptr) + *endptr=end + 2; + } + else if (end[0]=='p' && end[1]=='c') + { + result = (result / PPI) * 72 / 12; + if (endptr) + *endptr=end + 2; + } + else if (end[0]=='i' && end[1]=='n') + { + result = result / PPI; + if (endptr) + *endptr=end + 2; + } + else if (end[0]=='c' && end[1]=='m') + { + result = (result / PPI) * 2.54; + if (endptr) + *endptr=end + 2; + } + else if (end[0]=='m' && end[1]=='m') + { + result = (result / PPI) * 25.4; + if (endptr) + *endptr=end + 2; + } + return result; +} + +static inline int mrg_parse_pxs (Css *mrg, const char *str, float *vals) +{ + int n_floats = 0; + char *p = (void*)str; + char *prev = (void *)NULL; + + for (; p && p != prev && *p; ) + { + float val; + prev = p; + val = n_floats%2==1? + mrg_parse_px_x (mrg, p, &p):mrg_parse_px_y (mrg, p, &p); + if (p != prev) + { + vals[n_floats++] = val; + } + } + + return n_floats; +} + + +static inline void ctx_css_handle_property_pass0 (Css *mrg, uint32_t key, + const char *value) +{ + /* pass0 deals with properties that parsing of many other property + * definitions rely on */ + if (key == SQZ_font_size) + { + float parsed; + + if (mrg->state_no) + { + mrg->state_no--; + parsed = mrg_parse_px_y (mrg, value, NULL); + mrg->state_no++; + } + else + { + parsed = mrg_parse_px_y (mrg, value, NULL); + } + mrg_set_em (mrg, parsed); + } + else if (key == SQZ_color) + { + CtxColor *color = ctx_color_new (); + ctx_color_set_from_string (mrg->ctx, color, value); + ctx_set_color (mrg->ctx, SQZ_color, color); + ctx_color_free (color); + } +} + +static void ctx_css_handle_property_pass1 (Css *mrg, uint32_t key, + const char *value) +{ + CtxStyle *s = ctx_style (mrg); + uint32_t val_hash = ctx_strhash (value); + + switch (key) + { + default: + SET_PROPSh(key, value); + break; + case SQZ_right: + case SQZ_bottom: + case SQZ_color: // handled in pass0 + case SQZ_font_size: // handled in pass0 + break; + case SQZ_top: + case SQZ_height: + case SQZ_line_width: + case SQZ_text_indent: + case SQZ_letter_spacing: + case SQZ_word_spacing: + case SQZ_stroke_width: + case SQZ_text_stroke_width: + case SQZ_line_height: + case SQZ_border_top_width: + case SQZ_border_bottom_width: + case SQZ_margin_top: + case SQZ_margin_bottom: + case SQZ_padding_bottom: + case SQZ_padding_top: + case SQZ_min_height: + case SQZ_max_height: + SET_PROPh(key, mrg_parse_px_y (mrg, value, NULL)); + break; + case SQZ_border_right_width: + case SQZ_border_left_width: + case SQZ_left: + case SQZ_tab_size: + case SQZ_min_width: + case SQZ_max_width: + case SQZ_padding_left: + case SQZ_padding_right: + case SQZ_width: // handled in pass1m + SET_PROPh(key, mrg_parse_px_x (mrg, value, NULL)); + break; + case SQZ_margin: + { + float vals[10]; + int n_vals; + n_vals = mrg_parse_pxs (mrg, value, vals); + float res[4] = {0.0f,0.0f,0.0f,0.0f}; + switch (n_vals) + { + case 1: + res[0] = res[1] = res[2] = res[3] = vals[0]; + break; + case 2: + res[0] = vals[0]; res[1] = vals[1]; res[0] = vals[0]; res[1] = vals[1]; + break; + case 3: + res[0] = vals[0]; res[1] = vals[1]; res[2] = vals[2]; res[3] = vals[1]; + break; + case 4: + res[0] = vals[0]; res[1] = vals[1]; res[2] = vals[2]; res[3] = vals[3]; + break; + } + SET_PROP(margin_top, res[0]); + SET_PROP(margin_right, res[1]); + SET_PROP(margin_bottom, res[2]); + SET_PROP(margin_left, res[3]); + } + break; + case SQZ_margin_left: + { + if (val_hash == SQZ_auto) + { + s->margin_left_auto = 1; + } + else + { + SET_PROP(margin_left, mrg_parse_px_x (mrg, value, NULL)); + s->margin_left_auto = 0; + } + } + break; + case SQZ_margin_right: + { + if (val_hash == SQZ_auto) + { + s->margin_right_auto = 1; + } + else + { + SET_PROP(margin_right, mrg_parse_px_x (mrg, value, NULL)); + s->margin_right_auto = 0; + } + } + break; + + case SQZ_padding: + { + float vals[10]; + int n_vals; + n_vals = mrg_parse_pxs (mrg, value, vals); + float res[4] = {0.0f,0.0f,0.0f,0.0f}; + switch (n_vals) + { + case 1: + res[0] = res[1] = res[2] = res[3] = vals[0]; + break; + case 2: + res[0] = vals[0]; res[1] = vals[1]; res[0] = vals[0]; res[1] = vals[1]; + break; + case 3: + res[0] = vals[0]; res[1] = vals[1]; res[2] = vals[2]; res[3] = vals[1]; + break; + case 4: + res[0] = vals[0]; res[1] = vals[1]; res[2] = vals[2]; res[3] = vals[3]; + break; + } + SET_PROP(padding_top, res[0]); + SET_PROP(padding_right, res[1]); + SET_PROP(padding_bottom, res[2]); + SET_PROP(padding_left, res[3]); + } + break; + case SQZ_visibility: + { + if (val_hash == SQZ_visible) s->visibility = CTX_VISIBILITY_VISIBLE; + else if (val_hash == SQZ_hidden) s->visibility = CTX_VISIBILITY_HIDDEN; + else s->visibility = CTX_VISIBILITY_VISIBLE; + } + break; + + case SQZ_border_width: + { + float vals[10]; + int n_vals; + n_vals = mrg_parse_pxs (mrg, value, vals); + float res[4] = {0.0f,0.0f,0.0f,0.0f}; + switch (n_vals) + { + case 1: + res[0] = res[1] = res[2] = res[3] = vals[0]; + break; + case 2: + res[0] = vals[0]; res[1] = vals[1]; res[0] = vals[0]; res[1] = vals[1]; + break; + case 3: + res[0] = vals[0]; res[1] = vals[1]; res[2] = vals[2]; res[3] = vals[1]; + break; + case 4: + res[0] = vals[0]; res[1] = vals[1]; res[2] = vals[2]; res[3] = vals[3]; + break; + } + SET_PROP(border_top_width, res[0]); + SET_PROP(border_right_width, res[1]); + SET_PROP(border_bottom_width, res[2]); + SET_PROP(border_left_width, res[3]); + } + break; + case SQZ_border_color: + { + CtxColor *color = ctx_color_new (); + ctx_color_set_from_string (mrg->ctx, color, value); + ctx_set_color (mrg->ctx, SQZ_border_top_color, color); + ctx_set_color (mrg->ctx, SQZ_border_left_color, color); + ctx_set_color (mrg->ctx, SQZ_border_right_color, color); + ctx_set_color (mrg->ctx, SQZ_border_bottom_color, color); + ctx_color_free (color); + } + break; + case SQZ_border: + { + char word[64]; + int w = 0; + const char *p; + for (p = value; ; p++) + { + switch (*p) + { + case ' ': + case '\n': + case '\r': + case '\t': + case '\0': + if (w) + { + uint32_t word_hash = ctx_strhash (word); + if ((word[0] >= '0' && word[0]<='9') || word[0] == '.') + { + float valf = mrg_parse_px_y (mrg, word, NULL); + SET_PROP(border_top_width, valf); + SET_PROP(border_bottom_width, valf); + SET_PROP(border_right_width, valf); + SET_PROP(border_left_width, valf); + } else if (word_hash == SQZ_solid || + word_hash == SQZ_dotted || + word_hash == SQZ_inset) { + } else { + CtxColor *color = ctx_color_new (); + ctx_color_set_from_string (mrg->ctx, color, word); + ctx_set_color (mrg->ctx, SQZ_border_top_color, color); + ctx_set_color (mrg->ctx, SQZ_border_left_color, color); + ctx_set_color (mrg->ctx, SQZ_border_right_color, color); + ctx_set_color (mrg->ctx, SQZ_border_bottom_color, color); + ctx_color_free (color); + } + word[0]=0; + w=0; + } + break; + default: + word[w++]=*p; + word[w]=0; + break; + } + if (!*p) + break; + } + } + break; + case SQZ_border_bottom: + case SQZ_border_left: + case SQZ_border_right: + case SQZ_border_top: + { + char word[64]; + int w = 0; + const char *p; + for (p = value; ; p++) + { + switch (*p) + { + case ' ': + case '\n': + case '\r': + case '\t': + case '\0': + if (w) + { + uint32_t word_hash = ctx_strhash (word); + if ((word[0] >= '0' && word[0]<='9') || (word[0] == '.')) + { + float valf = mrg_parse_px_x (mrg, word, NULL); + if (key == SQZ_border_bottom) + SET_PROP(border_bottom_width, valf); + else if (key == SQZ_border_left) + SET_PROP(border_left_width, valf); + else if (key == SQZ_border_right) + SET_PROP(border_right_width, valf); + else + SET_PROP(border_top_width, valf); + } else if (word_hash == SQZ_solid || + word_hash == SQZ_dotted || + word_hash == SQZ_inset) { + } else { + CtxColor *color = ctx_color_new (); + ctx_color_set_from_string (mrg->ctx, color, word); + if (key == SQZ_border_bottom) + ctx_set_color (mrg->ctx, SQZ_border_bottom_color, color); + else if (key == SQZ_border_top) + ctx_set_color (mrg->ctx, SQZ_border_top_color, color); + else if (key == SQZ_border_left) + ctx_set_color (mrg->ctx, SQZ_border_left_color, color); + else if (key == SQZ_border_right) + ctx_set_color (mrg->ctx, SQZ_border_right_color, color); + ctx_color_free (color); + } + word[0]=0; + w=0; + } + break; + default: + word[w++]=*p; + word[w]=0; + break; + } + if (!*p) + break; + } + } + break; + + case SQZ_background_color: + case SQZ_background: + { + CtxColor *color = ctx_color_new (); + ctx_color_set_from_string (mrg->ctx, color, value); + ctx_set_color (mrg->ctx, SQZ_background_color, color); + ctx_color_free (color); + } + break; + case SQZ_fill_color: + case SQZ_fill: + { + CtxColor *color = ctx_color_new (); + ctx_color_set_from_string (mrg->ctx, color, value); + ctx_set_color (mrg->ctx, SQZ_fill_color, color); + ctx_color_free (color); + } + break; + case SQZ_stroke_color: + case SQZ_stroke: + { + CtxColor *color = ctx_color_new (); + ctx_color_set_from_string (mrg->ctx, color, value); + ctx_set_color (mrg->ctx, SQZ_stroke_color, color); + ctx_color_free (color); + } + break; + case SQZ_text_stroke_color: + { + CtxColor *color = ctx_color_new (); + ctx_color_set_from_string (mrg->ctx, color, value); + ctx_set_color (mrg->ctx, SQZ_text_stroke_color, color); + ctx_color_free (color); + } + break; + case SQZ_text_stroke: + { + char *col = NULL; + SET_PROP(text_stroke_width, mrg_parse_px_y (mrg, value, &col)); + + if (col) + { + CtxColor *color = ctx_color_new (); + ctx_color_set_from_string (mrg->ctx, color, col + 1); + ctx_set_color (mrg->ctx, SQZ_text_stroke_color, color); + ctx_color_free (color); + } + } + break; + case SQZ_opacity: + { + ctx_global_alpha (mrg->ctx, _ctx_parse_float (value, NULL)); + SET_PROP(opacity, _ctx_parse_float (value, NULL)); + } + break; + case SQZ_z_index: + s->z_index = _ctx_parse_float (value, NULL); + break; + case SQZ_print_symbols: + switch (val_hash) + { + case SQZ_true: + case SQZ_1: + case SQZ_yes: + s->print_symbols = 1; + break; + default: + s->print_symbols = 0; + } + break; + case SQZ_font_weight: + switch (val_hash) + { + case SQZ_bold: + case SQZ_bolder: + s->text_decoration |= CTX_TEXT_DECORATION_BOLD; + s->font_weight = CTX_FONT_WEIGHT_BOLD; + break; + default: + s->text_decoration ^= (s->text_decoration & CTX_TEXT_DECORATION_BOLD); + s->font_weight = CTX_FONT_WEIGHT_NORMAL; + } + #if 0 // XXX + cairo_select_font_face (mrg_ctx (mrg), + s->font_family, + s->font_style, + s->font_weight); + #endif + break; + case SQZ_white_space: + { + switch (val_hash) + { + default: + case SQZ_normal: s->white_space = CTX_WHITE_SPACE_NORMAL; break; + case SQZ_nowrap: s->white_space = CTX_WHITE_SPACE_NOWRAP; break; + case SQZ_pre: s->white_space = CTX_WHITE_SPACE_PRE; break; + case SQZ_pre_line: s->white_space = CTX_WHITE_SPACE_PRE_LINE; break; + case SQZ_pre_wrap: s->white_space = CTX_WHITE_SPACE_PRE_WRAP; break; + } + } + break; + case SQZ_box_sizing: + { + if (val_hash == SQZ_border_box) + { + s->box_sizing = CTX_BOX_SIZING_BORDER_BOX; + s->box_sizing = CTX_BOX_SIZING_CONTENT_BOX; + } + } + break; + case SQZ_float: + switch (val_hash) + { + case SQZ_left: s->float_ = CTX_FLOAT_LEFT; break; + case SQZ_right: s->float_ = CTX_FLOAT_RIGHT; break; + default: s->float_ = CTX_FLOAT_NONE; + } + break; + case SQZ_overflow: + switch(val_hash) + { + case SQZ_visible: s->overflow = CTX_OVERFLOW_VISIBLE; break; + case SQZ_hidden: s->overflow = CTX_OVERFLOW_HIDDEN; break; + case SQZ_scroll: s->overflow = CTX_OVERFLOW_SCROLL; break; + case SQZ_auto: s->overflow = CTX_OVERFLOW_AUTO; break; + default: s->overflow = CTX_OVERFLOW_VISIBLE; break; + } + break; + case SQZ_clear: + switch(val_hash) + { + case SQZ_left: s->clear = CTX_CLEAR_LEFT; break; + case SQZ_right: s->clear = CTX_CLEAR_RIGHT; break; + case SQZ_both: s->clear = CTX_CLEAR_BOTH; break; + default: s->clear = CTX_CLEAR_NONE; break; + } + break; + case SQZ_font_style: + switch(val_hash) + { + case SQZ_italic: s->font_style = CTX_FONT_STYLE_ITALIC; break; + case SQZ_oblique: s->font_style = CTX_FONT_STYLE_OBLIQUE; break; + default: s->font_style = CTX_FONT_STYLE_NORMAL; + #if 0 // XXX + cairo_select_font_face (mrg_ctx (mrg), + s->font_family, + s->font_style, + s->font_weight); + #endif + } + break; + case SQZ_font_family: + { + SET_PROPS(font_family, value); + ctx_font (mrg_ctx (mrg), value); + } + break; + case SQZ_syntax_highlight: + SET_PROPS(syntax_highlight, value); + break; + case SQZ_fill_rule: + switch (val_hash) + { + default: + case SQZ_evenodd: s->fill_rule = CTX_FILL_RULE_EVEN_ODD; break; + case SQZ_nonzero: s->fill_rule = CTX_FILL_RULE_WINDING; break; + } + if (s->fill_rule == CTX_FILL_RULE_EVEN_ODD) + ctx_fill_rule (mrg_ctx (mrg), CTX_FILL_RULE_EVEN_ODD); + else + ctx_fill_rule (mrg_ctx (mrg), CTX_FILL_RULE_WINDING); + break; + case SQZ_stroke_linejoin: + switch (val_hash) + { + case SQZ_miter: s->stroke_linejoin = CTX_JOIN_MITER; break; + case SQZ_round: s->stroke_linejoin = CTX_JOIN_ROUND; break; + case SQZ_bevel: s->stroke_linejoin = CTX_JOIN_BEVEL; break; + default: s->stroke_linejoin = CTX_JOIN_MITER; + } + ctx_line_join (mrg_ctx (mrg), (CtxLineJoin)s->stroke_linejoin); + break; + case SQZ_stroke_linecap: + switch (val_hash) + { + case SQZ_butt: s->stroke_linecap = CTX_CAP_NONE; break; + case SQZ_round: s->stroke_linecap = CTX_CAP_ROUND; break; + case SQZ_square: s->stroke_linecap = CTX_CAP_SQUARE; break; + default: s->stroke_linecap = CTX_CAP_NONE; + } + // XXX : keep track of what we have set - so we at least do not + // keep re-setting it.. + ctx_line_cap (mrg_ctx (mrg), s->stroke_linecap); + break; + case SQZ_vertical_align: + switch (val_hash) + { + case SQZ_middle: s->vertical_align = CTX_VERTICAL_ALIGN_MIDDLE; break; + case SQZ_top: s->vertical_align = CTX_VERTICAL_ALIGN_TOP; break; + case SQZ_sub: s->vertical_align = CTX_VERTICAL_ALIGN_SUB; break; + case SQZ_super: s->vertical_align = CTX_VERTICAL_ALIGN_SUPER; break; + case SQZ_bottom: s->vertical_align = CTX_VERTICAL_ALIGN_BOTTOM; break; + default: s->vertical_align = CTX_VERTICAL_ALIGN_BASELINE; + } + break; + case SQZ_cursor: + switch (val_hash) + { + default: + case SQZ_default: s->cursor = MRG_CURSOR_DEFAULT; break; + case SQZ_auto: s->cursor = MRG_CURSOR_AUTO; break; + case SQZ_alias: s->cursor = MRG_CURSOR_ALIAS; break; + case SQZ_all_scroll: s->cursor = MRG_CURSOR_ALL_SCROLL; break; + case SQZ_cell: s->cursor = MRG_CURSOR_CELL; break; + case SQZ_context_menu: s->cursor = MRG_CURSOR_CONTEXT_MENU; break; + case SQZ_col_resize: s->cursor = MRG_CURSOR_COL_RESIZE; break; + case SQZ_copy: s->cursor = MRG_CURSOR_COPY; break; + case SQZ_crosshair: s->cursor = MRG_CURSOR_CROSSHAIR; break; + case SQZ_e_resize: s->cursor = MRG_CURSOR_E_RESIZE; break; + case SQZ_ew_resize: s->cursor = MRG_CURSOR_EW_RESIZE; break; + case SQZ_help: s->cursor = MRG_CURSOR_HELP; break; + case SQZ_move: s->cursor = MRG_CURSOR_MOVE; break; + case SQZ_n_resize: s->cursor = MRG_CURSOR_N_RESIZE; break; + case SQZ_ne_resize: s->cursor = MRG_CURSOR_NE_RESIZE; break; + case SQZ_nesw_resize: s->cursor = MRG_CURSOR_NESW_RESIZE; break; + case SQZ_ns_resize: s->cursor = MRG_CURSOR_NS_RESIZE; break; + case SQZ_nw_resize: s->cursor = MRG_CURSOR_NW_RESIZE; break; + case SQZ_no_drop: s->cursor = MRG_CURSOR_NO_DROP; break; + case SQZ_none: s->cursor = MRG_CURSOR_NONE; break; + case SQZ_not_allowed: s->cursor = MRG_CURSOR_NOT_ALLOWED; break; + case SQZ_pointer: s->cursor = MRG_CURSOR_POINTER; break; + case SQZ_progress: s->cursor = MRG_CURSOR_PROGRESS; break; + case SQZ_row_resize: s->cursor = MRG_CURSOR_ROW_RESIZE; break; + case SQZ_s_resize: s->cursor = MRG_CURSOR_S_RESIZE; break; + case SQZ_se_resize: s->cursor = MRG_CURSOR_SE_RESIZE; break; + case SQZ_sw_resize: s->cursor = MRG_CURSOR_SW_RESIZE; break; + case SQZ_text: s->cursor = MRG_CURSOR_TEXT; break; + case SQZ_vertical_text: s->cursor = MRG_CURSOR_VERTICAL_TEXT;break; + case SQZ_w_resize: s->cursor = MRG_CURSOR_W_RESIZE; break; + case SQZ_cursor_wait: s->cursor = MRG_CURSOR_WAIT; break; + case SQZ_zoom_in: s->cursor = MRG_CURSOR_ZOOM_IN; break; + case SQZ_zoom_out: s->cursor = MRG_CURSOR_ZOOM_OUT; break; + } + break; + case SQZ_display: + switch (val_hash) + { + case SQZ_none: + case SQZ_hidden: s->display = CTX_DISPLAY_NONE; break; + case SQZ_block: s->display = CTX_DISPLAY_BLOCK; break; + case SQZ_list_item: s->display = CTX_DISPLAY_LIST_ITEM; break; + case SQZ_inline_block: s->display = CTX_DISPLAY_INLINE_BLOCK; break; + case SQZ_flow_root: s->display = CTX_DISPLAY_FLOW_ROOT; break; + default: s->display = CTX_DISPLAY_INLINE; + } + break; + case SQZ_position: + switch (val_hash) + { + case SQZ_relative: s->position = CTX_POSITION_RELATIVE; break; + case SQZ_static: s->position = CTX_POSITION_STATIC; break; + case SQZ_absolute: s->position = CTX_POSITION_ABSOLUTE; break; + case SQZ_fixed: s->position = CTX_POSITION_FIXED; break; + default: s->position = CTX_POSITION_STATIC; + } + break; + case SQZ_direction: + switch (val_hash) + { + case SQZ_rtl: s->direction = CTX_TEXT_DIRECTION_RTL; break; + case SQZ_ltr: s->direction = CTX_TEXT_DIRECTION_LTR; break; + default: s->direction = CTX_TEXT_DIRECTION_LTR; + } + break; + case SQZ_unicode_bidi: + switch (val_hash) + { + case SQZ_normal: s->unicode_bidi = CTX_UNICODE_BIDI_NORMAL; break; + case SQZ_embed: s->unicode_bidi = CTX_UNICODE_BIDI_EMBED; break; + case SQZ_bidi_override: s->unicode_bidi = CTX_UNICODE_BIDI_BIDI_OVERRIDE; break; + default: s->unicode_bidi = CTX_UNICODE_BIDI_NORMAL; break; + } + break; + case SQZ_text_align: + case SQZ_text_anchor: + switch (val_hash) + { + case SQZ_start: s->text_align = CTX_TEXT_ALIGN_START; break; + case SQZ_end: s->text_align = CTX_TEXT_ALIGN_END; break; + case SQZ_left: s->text_align = CTX_TEXT_ALIGN_LEFT; break; + case SQZ_right: s->text_align = CTX_TEXT_ALIGN_RIGHT; break; + case SQZ_justify: s->text_align = CTX_TEXT_ALIGN_JUSTIFY; break; + case SQZ_middle: + case SQZ_center: s->text_align = CTX_TEXT_ALIGN_CENTER; break; + default: s->text_align = CTX_TEXT_ALIGN_LEFT; + } + break; + case SQZ_text_decoration: + switch (val_hash) + { + case SQZ_reverse: s->text_decoration|= CTX_TEXT_DECORATION_REVERSE; break; + case SQZ_underline: s->text_decoration|= CTX_TEXT_DECORATION_UNDERLINE; break; + case SQZ_overline: s->text_decoration|= CTX_TEXT_DECORATION_OVERLINE; break; + case SQZ_linethrough: s->text_decoration|= CTX_TEXT_DECORATION_LINETHROUGH; break; + case SQZ_blink: s->text_decoration|= CTX_TEXT_DECORATION_BLINK; break; + case SQZ_none: + s->text_decoration ^= (s->text_decoration & + (CTX_TEXT_DECORATION_UNDERLINE|CTX_TEXT_DECORATION_REVERSE|CTX_TEXT_DECORATION_OVERLINE|CTX_TEXT_DECORATION_LINETHROUGH|CTX_TEXT_DECORATION_BLINK)); + break; + } + break; + } +} + +static void ctx_css_handle_property_pass1med (Css *mrg, uint32_t key, + const char *value) +{ + CtxStyle *s = ctx_style (mrg); + + if (key == SQZ_width) + { + if (ctx_strhash (value) == SQZ_auto) + { + s->width_auto = 1; + SET_PROP(width, 42); + } + else + { + s->width_auto = 0; + SET_PROP(width, mrg_parse_px_x (mrg, value, NULL)); + + if (s->position == CTX_POSITION_FIXED) // XXX: seems wrong + { + //s->width -= s->border_left_width + s->border_right_width; + } + } + } +} + +enum +{ + MRG_CSS_PROPERTY_PARSER_STATE_NEUTRAL = 0, + MRG_CSS_PROPERTY_PARSER_STATE_IN_NAME, + MRG_CSS_PROPERTY_PARSER_STATE_EXPECT_COLON, + MRG_CSS_PROPERTY_PARSER_STATE_EXPECT_VAL, + MRG_CSS_PROPERTY_PARSER_STATE_IN_VAL +}; + +static void css_parse_properties (Css *mrg, const char *style, + void (*handle_property) (Css *mrg, uint32_t key, + const char *value)) +{ + const char *p; + char name[CTX_MAX_CSS_STRINGLEN] = ""; + char string[CTX_MAX_CSS_STRINGLEN] = ""; + int name_l = 0; + int string_l = 0; + int state = MRG_CSS_PROPERTY_PARSER_STATE_NEUTRAL; + if (!style) + return; + for (p = style; *p; p++) + { + switch (state) + { + case MRG_CSS_PROPERTY_PARSER_STATE_NEUTRAL: + switch (*p) + { + case ' ': + case '\t': + case ';': + case '\n': + case '\r': + break; + default: + name[name_l++]=*p; + name[name_l]=0; + state = MRG_CSS_PROPERTY_PARSER_STATE_IN_NAME; + break; + } + break; + case MRG_CSS_PROPERTY_PARSER_STATE_IN_NAME: + switch (*p) + { + case ':': + state = MRG_CSS_PROPERTY_PARSER_STATE_EXPECT_VAL; + break; + case ' ': + case '\n': + case '\r': + case '\t': + state = MRG_CSS_PROPERTY_PARSER_STATE_EXPECT_COLON; + break; +#if 0 + case '-': + name[name_l++]='_'; + name[name_l]=0; + break; +#endif + default: + name[name_l++]=*p; + name[name_l]=0; + break; + } + break; + case MRG_CSS_PROPERTY_PARSER_STATE_EXPECT_COLON: + switch (*p) + { + case ':': + state = MRG_CSS_PROPERTY_PARSER_STATE_EXPECT_VAL; + break; + default: + break; + } + break; + case MRG_CSS_PROPERTY_PARSER_STATE_EXPECT_VAL: + switch (*p) + { + case ' ': + case '\n': + case '\r': + case '\t': + break; + default: + string[string_l++]=*p; + string[string_l]=0; + state = MRG_CSS_PROPERTY_PARSER_STATE_IN_VAL; + break; + } + break; + case MRG_CSS_PROPERTY_PARSER_STATE_IN_VAL: + switch (*p) + { + case ';': +#if 0 + for (int i = 0; name[i];i++) + if (name[i]=='-')name[i]='_'; +#endif + handle_property (mrg, ctx_strhash (name), string); + state = MRG_CSS_PROPERTY_PARSER_STATE_NEUTRAL; + name_l = 0; + name[0] = 0; + string_l = 0; + string[0] = 0; + break; + default: + string[string_l++]=*p; + string[string_l]=0; + break; + } + break; + } + } + if (name[0]) + { +#if 0 + for (int i = 0; name[i];i++) + if (name[i]=='-') + name[i]='_'; +#endif + handle_property (mrg, ctx_strhash (name), string); + } +} + + +static void ctx_css_handle_property_pass2 (Css *mrg, uint32_t key, + const char *value) +{ + /* this pass contains things that might depend on values + * generated by the previous pass. + */ + CtxStyle *s = ctx_style (mrg); + + if (key == SQZ_right) + { + float width = PROP(width); + float right = mrg_parse_px_x (mrg, value, NULL); + + SET_PROP(right, right); + + if (width == 0) + { + width = 8 * s->font_size; + mrg_queue_draw (mrg, NULL); + } + SET_PROP(right, right); + SET_PROP(left, + (mrg_width(mrg)-right) - width - PROP(border_left_width) - PROP(padding_left) - PROP(padding_right) - PROP(border_right_width) - PROP(margin_right)); + } + else if (key == SQZ_bottom) + { + float height = PROP(height); + + SET_PROP (bottom, mrg_parse_px_y (mrg, value, NULL)); + + if (height == 0) + { + // XXX + height = 2 * s->font_size; + mrg_queue_draw (mrg, NULL); + } + SET_PROP(top, mrg_height(mrg) - PROP(bottom) - height - PROP(padding_top) - PROP(border_top_width) - PROP(padding_bottom) - PROP(border_bottom_width) - PROP(margin_bottom)); + } +} + + +static float deco_width (Css *mrg) +{ + return PROP (padding_left) + PROP(padding_right) + PROP(border_left_width) + PROP(border_right_width); +} + +void css_set_style (Css *mrg, const char *style) +{ + CtxStyle *s; + + css_parse_properties (mrg, style, ctx_css_handle_property_pass0); + css_parse_properties (mrg, style, ctx_css_handle_property_pass1); + css_parse_properties (mrg, style, ctx_css_handle_property_pass1med); + + s = ctx_style (mrg); + + if (!mrg->in_svg) + { + if (s->position == CTX_POSITION_STATIC && + !s->float_) + { + if (s->width_auto && (s->margin_right_auto || s->margin_left_auto)) + { + if (s->margin_left_auto && s->margin_right_auto) + { + s->margin_right_auto = 0; + } + else if (s->margin_left_auto) + s->margin_left_auto = 0; + else + s->margin_right_auto = 0; + } + + if ( s->margin_left_auto && !s->width_auto && !s->margin_right_auto) + { + SET_PROP (margin_left, + (mrg->state->edge_right - mrg->state->edge_left) + - deco_width (mrg) - PROP(margin_right) - PROP(width)); + } + else if ( !s->margin_left_auto && + s->width_auto && + !s->margin_right_auto) + { + SET_PROP (width, + (mrg->state->edge_right - mrg->state->edge_left) + - deco_width (mrg) - PROP(margin_left) - PROP(margin_right)); + } + else if ( !s->margin_left_auto && !s->width_auto && s->margin_right_auto) + { + SET_PROP (margin_right, + (mrg->state->edge_right - mrg->state->edge_left) + - deco_width (mrg) - PROP(margin_left) - PROP(width)); + } + else if ( s->margin_left_auto && !s->width_auto && s->margin_right_auto) + { + float avail_width = (mrg->state->edge_right - mrg->state->edge_left) - deco_width (mrg); + float width = PROP(width); + if (width == 0.0f) + { + float max_width = PROP(max_width); + float min_width = PROP(min_width); + if (max_width != 0.0f) + width = ctx_minf (max_width, avail_width); + else + width = avail_width; // should not happen? + if (min_width != 0.0f) + { + if (width < min_width) width = min_width; + } + + } + + { + float val = (avail_width - width)/2; + SET_PROP (margin_left, val); + SET_PROP (margin_right, val); + } + } + } + + } + + css_parse_properties (mrg, style, ctx_css_handle_property_pass2); +} + +void _css_set_style_properties (Css *mrg, const char *style_properties) +{ + _ctx_initial_style (mrg); + + if (style_properties) + { + css_set_style (mrg, style_properties); + } +} + +void +css_set_stylef (Css *mrg, const char *format, ...) +{ + va_list ap; + size_t needed; + char *buffer; + va_start(ap, format); + needed = vsnprintf(NULL, 0, format, ap) + 1; + buffer = malloc(needed); + va_end (ap); + va_start(ap, format); + vsnprintf(buffer, needed, format, ap); + va_end (ap); + css_set_style (mrg, buffer); + free (buffer); +} + +void ctx_style_defaults (Css *mrg) +{ + Ctx *ctx = mrg->ctx; + float em = 32; + mrg_set_em (mrg, em); + mrg_set_rem (mrg, em); + css_set_edge_left (mrg, 0); + css_set_edge_right (mrg, mrg_width (mrg)); + css_set_edge_bottom (mrg, mrg_height (mrg)); + css_set_edge_top (mrg, 0); + mrg_set_line_height (mrg, 1.2); + + SET_PROP(stroke_width, 1.0f); + { + CtxColor *color = ctx_color_new (); + ctx_color_set_from_string (mrg->ctx, color, "transparent"); + ctx_set_color (ctx, SQZ_stroke_color, color); + ctx_color_free (color); + } + { + CtxColor *color = ctx_color_new (); + ctx_color_set_from_string (mrg->ctx, color, "black"); + ctx_set_color (ctx, SQZ_fill_color, color); + ctx_color_free (color); + } + { + CtxColor *color = ctx_color_new (); + ctx_color_set_from_string (mrg->ctx, color, "green"); + ctx_set_color (ctx, SQZ_color, color); + ctx_color_free (color); + } + + css_stylesheet_clear (mrg); + _ctx_initial_style (mrg); + + if (mrg->style_global->length) + { + ctx_stylesheet_add (mrg, mrg->style_global->str, NULL, CTX_STYLE_GLOBAL, NULL); + } + + if (mrg->style->length) + ctx_stylesheet_add (mrg, mrg->style->str, NULL, CTX_STYLE_GLOBAL, NULL); +} + +void mrg_set_mrg_get_contents (Css *mrg, + int (*mrg_get_contents) (const char *referer, + const char *input_uri, + char **contents, + long *length, + void *get_contents_data), + void *get_contents_data) + +{ + mrg->mrg_get_contents = mrg_get_contents; + mrg->get_contents_data = get_contents_data; +} + +int +mrg_get_contents (Css *mrg, + const char *referer, + const char *input_uri, + char **contents, + long *length) +{ + if (!referer) referer = mrg->uri_base; + if (mrg->mrg_get_contents) + { + int ret; + ret = mrg->mrg_get_contents (referer, input_uri, contents, length, + mrg->get_contents_data); + return ret; + } + else + { + *contents = NULL; + *length = 0; + return -1; + } +} + +int css_print (Css *mrg, const char *string); +static int is_block_item (CtxStyle *style) +{ + return ((style->display == CTX_DISPLAY_BLOCK + ||style->float_ + ||style->display == CTX_DISPLAY_LIST_ITEM + ||style->display == CTX_DISPLAY_FLOW_ROOT + ||style->display == CTX_DISPLAY_INLINE_BLOCK + )); +} + +float mrg_ddpx (Css *mrg) +{ + return mrg->ddpx; +} + +static void +mrg_ctx_set_source_color (Ctx *ctx, CtxColor *color) +{ + float rgba[4]; + ctx_color_get_rgba (ctx_get_state (ctx), color, rgba); + ctx_rgba (ctx, rgba[0], rgba[1], rgba[2], rgba[3]); +} + +static void set_line_height (Ctx *ctx, void *userdata, const char *name, int count, float *x, float *y, float *w, float *h) +{ + float *fptr = (float*)userdata; + *y = fptr[0]; +} + +static void _mrg_nl (Css *mrg) +{ + float ascent, descent; + ctx_font_extents (mrg->ctx, &ascent, &descent, NULL); + mrg->y += mrg->line_max_height[mrg->line_level] * mrg->state->style.line_height; + float val = mrg->line_max_height[mrg->line_level]* ascent; + char name[10]="lin_"; + name[3]=mrg->line_level+2; + + ctx_resolve (mrg->ctx, name, set_line_height, &val); + mrg->line_max_height[mrg->line_level]=0.0f; + mrg->line_got_baseline[mrg->line_level]=0; + + mrg->x = _mrg_dynamic_edge_left(mrg); +#ifdef SNAP + float em = mrg_em (mrg); /* XXX: a global body-line spacing + snap is better grid design */ + mrg->x = ceil (mrg->x / em) * em; + mrg->y = ceil (mrg->y / em) * em; +#endif + + if (mrg->y >= + mrg->state->edge_bottom - PROP(padding_bottom)) + { + mrg->state->overflowed=1; + } +} + +void _mrg_layout_pre (Css *mrg) +{ + CtxStyle *style; + float dynamic_edge_left, dynamic_edge_right; + + + style = ctx_style (mrg); + + mrg->state->original_x = mrg_x (mrg); + mrg->state->original_y = mrg_y (mrg); + + mrg->state->flow_root = (style->display == CTX_DISPLAY_FLOW_ROOT || + style->float_ != CTX_FLOAT_NONE || + style->display == CTX_DISPLAY_INLINE_BLOCK || + style->overflow != CTX_OVERFLOW_VISIBLE); + if (mrg->state->flow_root) + mrg->state->float_base = mrg->floats; + + // newline hacks + if (mrg->state->style_node.element_hash == SQZ_br + || ( mrg->unresolved_line && is_block_item (style)) + ) + { + _mrg_nl (mrg); + } + + if (is_block_item (style)) + { + mrg->line_level++; + mrg->line_max_height[mrg->line_level] = 0.0f; + mrg->line_got_baseline[mrg->line_level]=0; + + + } + mrg->unresolved_line = 0; + + + + if (mrg->state_no) + { + dynamic_edge_right = _mrg_parent_dynamic_edge_right(mrg); + dynamic_edge_left = _mrg_parent_dynamic_edge_left(mrg); + } + else + { + dynamic_edge_right = mrg_edge_right(mrg); + dynamic_edge_left = mrg_edge_left(mrg); + } + + if (style->clear & CTX_CLEAR_RIGHT) + clear_right (mrg); + if (style->clear & CTX_CLEAR_LEFT) + clear_left (mrg); + + // extra box dimensions once, reducing overhead of property storage + // + float padding_left = PROP(padding_left); + float margin_left = PROP(margin_left); + float border_left_width = PROP(border_left_width); + float padding_right = PROP(padding_right); + float margin_right = PROP(margin_right); + float border_right_width = PROP(border_right_width); + float padding_top = PROP(padding_top); + float margin_top = PROP(margin_top); + float border_top_width = PROP(border_top_width); + float padding_bottom = PROP(padding_bottom); + //float margin_bottom = PROP(margin_bottom); + float border_bottom_width = PROP(border_bottom_width); + + float left = PROP(left); + float top = PROP(top); + float width = PROP(width); + float height = PROP(height); + + if (style->display == CTX_DISPLAY_BLOCK || + style->display == CTX_DISPLAY_FLOW_ROOT || + style->display == CTX_DISPLAY_LIST_ITEM) + { + if (padding_left + margin_left + border_left_width != 0) + { + css_set_edge_left (mrg, mrg_edge_left (mrg) + + padding_left + margin_left + border_left_width); + } + if (padding_right + margin_right + border_right_width + != 0) + { + css_set_edge_right (mrg, mrg_edge_right (mrg) - + (padding_right + margin_right + border_right_width)); + } + + + /* collapsing of vertical margins */ + float actual_top = margin_top; + if (actual_top >= mrg->state->vmarg) + actual_top = actual_top - mrg->state->vmarg; + else + actual_top = 0; + + css_set_edge_top (mrg, mrg_y (mrg) + border_top_width + padding_top + actual_top); + + mrg->state->block_start_x = mrg_edge_left (mrg); + mrg->state->block_start_y = mrg_y (mrg); + // mrg->floats = 0; + } + else if (style->display == CTX_DISPLAY_INLINE_BLOCK) + { + + + float left_margin_pad_and_border = padding_left + margin_left + border_left_width; + float right_margin_pad_and_border = padding_right + margin_right + border_right_width; + + if (mrg_x (mrg) + width + left_margin_pad_and_border + right_margin_pad_and_border + >= dynamic_edge_right) + _mrg_nl (mrg); + + if (left_margin_pad_and_border != 0.0f) + { + css_set_edge_left (mrg, mrg_x (mrg) + + padding_left + margin_left + border_left_width); + } + + +#if 0 + if (right_margin_pad_and_border != 0.0f) + { + css_set_edge_right (mrg, mrg_edge_right (mrg) - right_margin_pad_and_border); + } +#else + css_set_edge_right (mrg, mrg_x (mrg) + width); +#endif + + css_set_edge_top (mrg, mrg_y (mrg) + border_top_width);// + actual_top); + + mrg->state->block_start_x = mrg_x (mrg); + mrg->state->block_start_y = mrg_y (mrg); + // mrg->floats = 0; + } + + + /* the list-item is a cheap hack; can be implemented directly + * in later versions of css + */ + if (style->display == CTX_DISPLAY_LIST_ITEM) + { + float x = mrg->x; + + mrg->x -= mrg_em (mrg) * 0.5; + css_print (mrg, "•"); + mrg->x = x; + mrg->line_max_height[mrg->line_level] = 0.0f; + mrg->line_got_baseline[mrg->line_level]=0; + } + + switch (style->position) + { + case CTX_POSITION_RELATIVE: + /* XXX: deal with style->right and style->bottom */ + ctx_translate (mrg_ctx (mrg), left, top); + mrg->relative_x += left; + mrg->relative_y += top; + /* fallthrough */ + + case CTX_POSITION_STATIC: + + if (style->float_ == CTX_FLOAT_RIGHT) + { + if (width == 0.0) + { + width = mrg_edge_right (mrg) - mrg_edge_left (mrg); + } + + width = (width + padding_right + padding_left + border_left_width + border_right_width); + + + if (width + margin_left + margin_right > + mrg_edge_right(mrg)-mrg_edge_left(mrg)) + { + clear_both (mrg); + css_set_edge_left (mrg, mrg_edge_right (mrg) - width); + css_set_edge_right (mrg, mrg_edge_right (mrg) - (margin_right + padding_right + border_right_width)); + + } + else + { + while (dynamic_edge_right - dynamic_edge_left < width + margin_left + margin_right) + { + mrg_set_xy (mrg, mrg_x (mrg), mrg_y (mrg) + 1.0); + dynamic_edge_right = _mrg_parent_dynamic_edge_right(mrg); + dynamic_edge_left = _mrg_parent_dynamic_edge_left(mrg); + } + + css_set_edge_left (mrg, dynamic_edge_right - width); + css_set_edge_right (mrg, dynamic_edge_right - (margin_right + padding_right + border_right_width)); + + } + + css_set_edge_top (mrg, mrg_y (mrg) + (PROP(margin_top))); // - mrg->state->vmarg)); + + mrg->state->block_start_x = mrg_x (mrg); + mrg->state->block_start_y = mrg_y (mrg); + //mrg->floats = 0; + + } else if (style->float_ == CTX_FLOAT_LEFT) + { + float left; + float widt = width; + + if (widt == 0.0) + { + widt = 4 * mrg_em (mrg);//mrg_edge_right (mrg) - mrg_edge_left (mrg); + } + + widt = (widt + padding_right + padding_left + border_left_width + border_right_width); + + if (widt + margin_left + margin_right > + mrg_edge_right(mrg)-mrg_edge_left(mrg)) + { + clear_both (mrg); + left = mrg_edge_left (mrg) + padding_left + border_left_width + margin_left; + } + else + { + while (dynamic_edge_right - dynamic_edge_left < widt + margin_left + margin_right) + { + mrg_set_xy (mrg, mrg_x (mrg), mrg_y (mrg) + 1.0); + dynamic_edge_right = _mrg_parent_dynamic_edge_right(mrg); + dynamic_edge_left = _mrg_parent_dynamic_edge_left(mrg); + } + left = dynamic_edge_left;// + padding_left + border_left_width + margin_left; + } + + css_set_edge_left (mrg, left); + css_set_edge_right (mrg, left + widt + + padding_left /* + border_right_width*/); + css_set_edge_top (mrg, mrg_y (mrg) + (margin_top));// - mrg->state->vmarg)); + //));//- mrg->state->vmarg)); + mrg->state->block_start_x = mrg_x (mrg); + mrg->state->block_start_y = mrg_y (mrg);// + padding_top + border_top_width; + //mrg->floats = 0; + + /* change cursor point after floating something left; if pushed far + * down, the new correct + */ +#if 0 + if(0) + mrg_set_xy (mrg, mrg->state->original_x = left + width + padding_left + border_right_width + padding_right + margin_right + margin_left + border_left_width, + mrg_y (mrg) + padding_top + border_top_width); +#endif + } /* XXX: maybe spot for */ + else if (1) + { + if (width) + css_set_edge_right (mrg, mrg->state->block_start_x + width); + } + break; + case CTX_POSITION_ABSOLUTE: + ctx_get_drawlist (mrg->ctx, &mrg->state->drawlist_start_offset); + mrg->state->drawlist_start_offset--; + { + if (left == 0.0f) // XXX 0.0 should also be a valid value! + left = mrg->x; + if (top == 0.0f) // XXX 0.0 should also be a valid value! + top = mrg->y; + + //mrg->floats = 0; + css_set_edge_left (mrg, left + margin_left + border_left_width + padding_left); + css_set_edge_right (mrg, left + width); + css_set_edge_top (mrg, top + margin_top + border_top_width + padding_top); + mrg->state->block_start_x = mrg_x (mrg); + mrg->state->block_start_y = mrg_y (mrg); + } + break; + case CTX_POSITION_FIXED: + ctx_get_drawlist (mrg->ctx, &mrg->state->drawlist_start_offset); + mrg->state->drawlist_start_offset--; + { + if (!width) + { + width = mrg_edge_right (mrg) - mrg_edge_left (mrg); + } + + ctx_translate (mrg_ctx(mrg), 0, css_panel_scroll (mrg)); + ctx_scale (mrg_ctx(mrg), mrg_ddpx (mrg), mrg_ddpx (mrg)); + //mrg->floats = 0; + + css_set_edge_left (mrg, left + margin_left + border_left_width + padding_left); + css_set_edge_right (mrg, left + margin_left + border_left_width + padding_left + width); + css_set_edge_top (mrg, top + margin_top + border_top_width + padding_top); + mrg->state->block_start_x = mrg_x (mrg); + mrg->state->block_start_y = mrg_y (mrg); + } + break; + } + + if (is_block_item (style)) + { + if (!height) + { + height = mrg_em (mrg) * 4; + } + if (!width) + { + width = mrg_em (mrg) * 4; + } + + if (height /* XXX: if we knew height of dynamic elements + from previous frame, we could use it here */ + && style->overflow == CTX_OVERFLOW_HIDDEN) + { + ctx_rectangle (mrg_ctx(mrg), + mrg->state->block_start_x - padding_left - border_left_width, + mrg->state->block_start_y - mrg_em(mrg) - padding_top - border_top_width, + width + border_right_width + border_left_width + padding_left + padding_right, //mrg_edge_right (mrg) - mrg_edge_left (mrg) + padding_left + padding_right + border_left_width + border_right_width, + height + padding_top + padding_bottom + border_top_width + border_bottom_width); + ctx_clip (mrg_ctx(mrg)); + } + + mrg->state->ptly = 0; + char name[10]="ele_"; + name[3]=mrg->state_no+2; + + CtxColor *background_color = ctx_color_new (); + ctx_get_color (mrg->ctx, SQZ_background_color, background_color); + if (!ctx_color_is_transparent (background_color)) + { + mrg_ctx_set_source_color (mrg->ctx, background_color); + if (is_block_item (style)) + { + ctx_begin_path (mrg->ctx); // XXX : this should not need be here! + ctx_deferred_rectangle (mrg->ctx, name, + mrg->state->block_start_x - padding_left, + mrg->state->block_start_y - padding_top, + width + padding_left + padding_right, + height + padding_top + padding_bottom); + } + else + { + ctx_deferred_rectangle (mrg->ctx, name, + mrg_x (mrg), mrg_y (mrg), + width + padding_left + padding_right, + height + padding_top + padding_bottom); + } + ctx_fill (mrg->ctx); + } + ctx_color_free (background_color); + } +} +void _mrg_layout_post (Css *mrg, CtxFloatRectangle *ret_rect); + +void css_set_style (Css *mrg, const char *style); + +void css_start_with_style (Css *mrg, + const char *style_id, + void *id_ptr, + const char *style) +{ + mrg->states[mrg->state_no].children++; + if (mrg->state_no+1 >= CTX_MAX_STATES) + return; + mrg->state_no++; // XXX bounds check! + mrg->state = &mrg->states[mrg->state_no]; + *mrg->state = mrg->states[mrg->state_no-1]; + mrg->states[mrg->state_no].children = 0; + + mrg->state->style_id = style_id ? strdup (style_id) : NULL; + + ctx_parse_style_id (mrg, mrg->state->style_id, &mrg->state->style_node); + + mrg->state->style.id_ptr = id_ptr; + + ctx_save (mrg_ctx (mrg)); + + _ctx_initial_style (mrg); + + { + char *collated_style = _ctx_stylesheet_collate_style (mrg); + if (collated_style) + { + css_set_style (mrg, collated_style); + free (collated_style); + } + } + if (style) + { + css_set_style (mrg, style); + } + _mrg_layout_pre (mrg); +} + +void +css_start_with_stylef (Css *mrg, const char *style_id, void *id_ptr, + const char *format, ...) +{ + va_list ap; + size_t needed; + char *buffer; + va_start(ap, format); + needed = vsnprintf(NULL, 0, format, ap) + 1; + buffer = malloc(needed); + va_end (ap); + va_start(ap, format); + vsnprintf(buffer, needed, format, ap); + va_end (ap); + css_start_with_style (mrg, style_id, id_ptr, buffer); + free (buffer); +} + +void css_start (Css *mrg, const char *style_id, void *id_ptr) +{ + css_start_with_style (mrg, style_id, id_ptr, NULL); +} + +static int compare_zindex (const void *a, const void *b, void *d) +{ + const CssAbsolute *ma = a; + const CssAbsolute *mb = b; + return mb->z_index- ma->z_index; +} + +void css_end (Css *mrg, CtxFloatRectangle *ret_rect) +{ + _mrg_layout_post (mrg, ret_rect); + if (mrg->state_no == 0) + { + ctx_list_reverse (&mrg->absolutes); + ctx_list_sort (&mrg->absolutes, compare_zindex, NULL); + + /* TODO: handle negative z-index */ + /* TODO: also copy/paste registered interaction points */ + while (mrg->absolutes) + { + CssAbsolute *absolute = mrg->absolutes->data; + ctx_save (mrg->ctx); + ctx_translate (mrg->ctx, absolute->relative_x, absolute->relative_y); + ctx_append_drawlist (mrg->ctx, absolute->entries+1, (absolute->count-1)*9); + ctx_list_remove (&mrg->absolutes, absolute); + free (absolute); + } + } +} + +void mrg_set_line_height (Css *mrg, float line_height); +float mrg_line_height (Css *mrg); + + +typedef struct _ItkCssDef ItkCssDef; + +struct _ItkCssDef { + uint32_t id; + CtxString *str; + ItkCssDef *next; +}; + +static CtxString *css_svg_add_def (ItkCssDef **defs, uint32_t id) +{ + ItkCssDef *iter = *defs; + while (iter) + { + if (iter->id == id) + return iter->str; + iter = iter->next; + } + iter = ctx_calloc (sizeof (ItkCssDef),1); + iter->str = ctx_string_new (""); + iter->id = id; + iter->next = *defs; + *defs = iter; + return iter->str; +} + +static void mrg_path_fill_stroke (Css *mrg, ItkCssDef **defs) +{ + Ctx *ctx = mrg_ctx (mrg); + CtxColor *fill_color = ctx_color_new (); + CtxColor *stroke_color = ctx_color_new (); + + const char *fill = ctx_get_string (mrg->ctx, SQZ_fill); + const char *stroke = ctx_get_string (mrg->ctx, SQZ_stroke); + + + ctx_get_color (ctx, SQZ_stroke_color, stroke_color); + + int has_stroke = (PROP(stroke_width) > 0.001f && + (!ctx_color_is_transparent (stroke_color) + || (stroke != NULL))); + + if (fill && fill[0] == 'u' && strstr(fill, "url(")) + { + char *id = strchr(fill, '#'); + if (id) + { + id ++; + } + if (id[strlen(id)-1]==')') + id[strlen(id)-1]=0; + if (id[strlen(id)-1]=='\'') + id[strlen(id)-1]=0; + if (id[strlen(id)-1]=='"') + id[strlen(id)-1]=0; + CtxString *str = css_svg_add_def (defs, ctx_strhash(id)); + ctx_parse (ctx, str->str); + + if (has_stroke) + ctx_preserve (ctx); + ctx_fill (ctx); + } + else + { + + ctx_get_color (ctx, SQZ_fill_color, fill_color); + + if (!ctx_color_is_transparent (fill_color)) + { + mrg_ctx_set_source_color (ctx, fill_color); + + if (has_stroke) + ctx_preserve (ctx); + ctx_fill (ctx); + } + + } + ctx_color_free (fill_color); + + if (has_stroke) + { + ctx_line_width (ctx, PROP(stroke_width)); + if (stroke && stroke[0] == 'u' && strstr(stroke, "url(")) + { + char *id = strchr(stroke, '#'); + if (id) + { + id ++; + } + if (id[strlen(id)-1]==')') + id[strlen(id)-1]=0; + if (id[strlen(id)-1]=='\'') + id[strlen(id)-1]=0; + if (id[strlen(id)-1]=='"') + id[strlen(id)-1]=0; + CtxString *str = css_svg_add_def (defs, ctx_strhash(id)); + ctx_parse (ctx, str->str); + } + else + { + mrg_ctx_set_source_color (ctx, stroke_color); + } + ctx_stroke (ctx); + } + ctx_color_free (stroke_color); +} + +void _mrg_border_top (Css *mrg, int x, int y, int width, int height) +{ + float border_top_width = PROP(border_top_width); + if (border_top_width < 0.01f) + return; + Ctx *ctx = mrg_ctx (mrg); + CtxColor *color = ctx_color_new (); + ctx_get_color (ctx, SQZ_border_top_color, color); + + if (!ctx_color_is_transparent (color)) + { + ctx_save (ctx); + ctx_begin_path (ctx); + ctx_move_to (ctx, x - PROP(padding_left) - PROP(border_left_width), + y - PROP(padding_top) - border_top_width); + ctx_rel_line_to (ctx, width + PROP(padding_left) + PROP(padding_right) + PROP(border_left_width) + PROP(border_right_width), 0); + ctx_rel_line_to (ctx, -PROP(border_right_width), border_top_width); + ctx_rel_line_to (ctx, - (width + PROP(padding_right) + PROP(padding_left)), 0); + + mrg_ctx_set_source_color (ctx, color); + ctx_fill (ctx); + ctx_restore (ctx); + } + ctx_color_free (color); +} + +void _mrg_border_bottom (Css *mrg, int x, int y, int width, int height) +{ + float border_bottom_width = PROP(border_bottom_width); + if (border_bottom_width < 0.01f) + return; + Ctx *ctx = mrg_ctx (mrg); + CtxColor *color = ctx_color_new (); + ctx_get_color (ctx, SQZ_border_top_color, color); + + if (!ctx_color_is_transparent (color)) + { + ctx_save (ctx); + ctx_begin_path (ctx); + ctx_move_to (ctx, x + width + PROP(padding_right), y + height + PROP(padding_bottom)); + ctx_rel_line_to (ctx, PROP(border_right_width), border_bottom_width); + ctx_rel_line_to (ctx, - (width + PROP(padding_left) + PROP(padding_right) + PROP(border_left_width) + PROP(border_right_width)), 0); + ctx_rel_line_to (ctx, PROP(border_left_width), -border_bottom_width); + + mrg_ctx_set_source_color (ctx, color); + ctx_fill (ctx); + ctx_restore (ctx); + } + + ctx_color_free (color); +} + +void _mrg_border_top_r (Css *mrg, int x, int y, int width, int height) +{ + float border_top_width = PROP(border_top_width); + if (border_top_width < 0.01f) + return; + Ctx *cr = mrg_ctx (mrg); + CtxColor *color = ctx_color_new (); + ctx_get_color (cr, SQZ_border_top_color, color); + + if (!ctx_color_is_transparent (color)) + { + ctx_save (cr); + ctx_begin_path (cr); + ctx_move_to (cr, x, y - PROP(padding_top) - border_top_width); + ctx_rel_line_to (cr, width + PROP(padding_right) + PROP(border_right_width), 0); + ctx_rel_line_to (cr, -PROP(border_right_width), border_top_width); + ctx_rel_line_to (cr, - (width + PROP(padding_right)), 0); + + mrg_ctx_set_source_color (cr, color); + ctx_fill (cr); + ctx_restore (cr); + } + ctx_color_free (color); +} +void _mrg_border_bottom_r (Css *mrg, int x, int y, int width, int height) +{ + float border_bottom_width = PROP(border_bottom_width); + if (border_bottom_width < 0.01f) + return; + Ctx *ctx = mrg_ctx (mrg); + CtxColor *color = ctx_color_new (); + ctx_get_color (ctx, SQZ_border_bottom_color, color); + + if (!ctx_color_is_transparent (color)) + { + ctx_save (ctx); + ctx_begin_path (ctx); + ctx_move_to (ctx, x + width + PROP(padding_right), y + height + PROP(padding_bottom)); + ctx_rel_line_to (ctx, PROP(border_right_width), border_bottom_width); + ctx_rel_line_to (ctx, - (width + PROP(padding_left) + PROP(padding_right) + PROP(border_left_width) + PROP(border_right_width)), 0); + ctx_rel_line_to (ctx, PROP(border_left_width), -border_bottom_width); + + mrg_ctx_set_source_color (ctx, color); + ctx_fill (ctx); + ctx_restore (ctx); + } + + ctx_color_free (color); +} + +void _mrg_border_top_l (Css *mrg, int x, int y, int width, int height) +{ + float border_top_width = PROP(border_top_width); + if (border_top_width < 0.01f) + return; + Ctx *ctx = mrg_ctx (mrg); + CtxColor *color = ctx_color_new (); + ctx_get_color (ctx, SQZ_border_top_color, color); + + if (!ctx_color_is_transparent (color)) + { + ctx_save (ctx); + ctx_begin_path (ctx); + ctx_move_to (ctx, x - PROP(padding_left) - PROP(border_left_width), + y - PROP(padding_top) - border_top_width); + ctx_rel_line_to (ctx, width + PROP(padding_left) + PROP(padding_right) + PROP(border_left_width), 0); + ctx_rel_line_to (ctx, 0, border_top_width); + ctx_rel_line_to (ctx, - (width + PROP(padding_left)), 0); + + mrg_ctx_set_source_color (ctx, color); + ctx_fill (ctx); + ctx_restore (ctx); + } + ctx_color_free (color); +} +void _mrg_border_bottom_l (Css *mrg, int x, int y, int width, int height) +{ + float border_bottom_width = PROP(border_bottom_width); + if (border_bottom_width < 0.01f) + return; + Ctx *ctx = mrg_ctx (mrg); + CtxColor *color = ctx_color_new (); + ctx_get_color (ctx, SQZ_border_bottom_color, color); + + if (!ctx_color_is_transparent (color)) + { + ctx_save (ctx); + ctx_begin_path (ctx); + ctx_move_to (ctx, x + width, y + height + PROP(padding_bottom)); + ctx_rel_line_to (ctx, 0, border_bottom_width); + ctx_rel_line_to (ctx, - (width + PROP(padding_left) + PROP(border_left_width)), 0); + ctx_rel_line_to (ctx, PROP(border_left_width), -border_bottom_width); + + mrg_ctx_set_source_color (ctx, color); + ctx_fill (ctx); + ctx_restore (ctx); + } + + ctx_color_free (color); +} + + +void _mrg_border_top_m (Css *mrg, int x, int y, int width, int height) +{ + float border_top_width = PROP(border_top_width); + if (border_top_width < 0.01f) + return; + Ctx *ctx = mrg_ctx (mrg); + CtxColor *color = ctx_color_new (); + ctx_get_color (ctx, SQZ_border_top_color, color); + + if (!ctx_color_is_transparent (color)) + { + ctx_save (ctx); + ctx_begin_path (ctx); + ctx_move_to (ctx, x, + y - PROP(padding_top) - border_top_width); + ctx_rel_line_to (ctx, width, 0); + ctx_rel_line_to (ctx, 0, border_top_width); + ctx_rel_line_to (ctx, -width, 0); + + mrg_ctx_set_source_color (ctx, color); + ctx_fill (ctx); + ctx_restore (ctx); + } + ctx_color_free (color); +} +void _mrg_border_bottom_m (Css *mrg, int x, int y, int width, int height) +{ + float border_bottom_width = PROP(border_bottom_width); + if ((border_bottom_width) < 0.01f) + return; + Ctx *ctx = mrg_ctx (mrg); + CtxColor *color = ctx_color_new (); + ctx_get_color (ctx, SQZ_border_bottom_color, color); + + if (!ctx_color_is_transparent (color)) + { + ctx_save (ctx); + ctx_begin_path (ctx); + ctx_move_to (ctx, x + width, y + height + PROP(padding_bottom)); + ctx_rel_line_to (ctx, 0, border_bottom_width); + ctx_rel_line_to (ctx, - width, 0); + ctx_rel_line_to (ctx, 0, -border_bottom_width); + + mrg_ctx_set_source_color (ctx, color); + ctx_fill (ctx); + ctx_restore (ctx); + } + + ctx_color_free (color); +} +void _mrg_border_left (Css *mrg, int x, int y, int width, int height) +{ + float border_left_width = PROP(border_left_width); + if (border_left_width < 0.01f) + return; + Ctx *ctx = mrg_ctx (mrg); + CtxColor *color = ctx_color_new (); + ctx_get_color (ctx, SQZ_border_left_color, color); + + if (!ctx_color_is_transparent (color)) + { + ctx_save (ctx); + ctx_begin_path (ctx); + ctx_move_to (ctx, x - PROP(padding_left) - border_left_width, + y - PROP(padding_top) - PROP(border_top_width)); + ctx_rel_line_to (ctx, border_left_width, PROP(border_top_width)); + ctx_rel_line_to (ctx, 0, height + PROP(padding_top) + PROP(padding_bottom) ); + ctx_rel_line_to (ctx, -border_left_width, PROP(border_bottom_width)); + mrg_ctx_set_source_color (ctx, color); + ctx_fill (ctx); + ctx_restore (ctx); + } + + ctx_color_free (color); +} + +void _mrg_border_right (Css *mrg, int x, int y, int width, int height) +{ + float border_right_width = PROP(border_right_width); + if (border_right_width < 0.01f) + return; + Ctx *ctx = mrg_ctx (mrg); + CtxColor *color = ctx_color_new (); + ctx_get_color (ctx, SQZ_border_right_color, color); + + if (!ctx_color_is_transparent (color)) + { + ctx_save (ctx); + ctx_begin_path (ctx); + ctx_move_to (ctx, x + width + PROP(padding_right), y + height + PROP(padding_bottom)); + ctx_rel_line_to (ctx, border_right_width, PROP(border_bottom_width)); + ctx_rel_line_to (ctx, 0, - (height + PROP(padding_top) + PROP(padding_bottom) + PROP(border_top_width) + PROP(border_bottom_width))); + ctx_rel_line_to (ctx, -border_right_width, PROP(border_top_width)); + + mrg_ctx_set_source_color (ctx, color); + ctx_fill (ctx); + ctx_restore (ctx); + } + + ctx_color_free (color); +} + +static void mrg_box (Css *mrg, int x, int y, int width, int height) +{ + _mrg_border_top (mrg, x, y, width, height); + _mrg_border_left (mrg, x, y, width, height); + _mrg_border_right (mrg, x, y, width, height); + _mrg_border_bottom (mrg, x, y, width, height); +} + +#if 0 +static void mrg_box_fill (Css *mrg, CtxStyle *style, float x, float y, float width, float height) +{ + Ctx *ctx = mrg_ctx (mrg); + CtxColor *background_color = ctx_color_new (); + ctx_get_color (ctx, SQZ_background_color, background_color); + if (ctx_color_is_transparent (background_color)) + { + ctx_color_free (background_color); + return; + } + + height = ctx_floorf (y + height) - ctx_floorf(y); + y = ctx_floorf (y); + + ctx_save (ctx); + { + ctx_begin_path (ctx); + ctx_move_to (ctx, x, + y); + ctx_rel_line_to (ctx, 0, height ); + ctx_rel_line_to (ctx, width, 0); + ctx_rel_line_to (ctx, 0, -(height )); + + ctx_fill_rule (ctx, CTX_FILL_RULE_EVEN_ODD); + mrg_ctx_set_source_color (ctx, background_color); + ctx_fill (ctx); + } + ctx_restore (ctx); + ctx_color_free (background_color); +} +#endif + +/* + * each style state level needs to know how far down it has + * painted background,.. on background increment we do all of them.. + * .. floats are problematic - maybe draw them in second layer. + * + */ + + +#if 0 +static void +_mrg_resolve_line_height (Css *mrg, void *data, int last) +{ + //CssState *state = &mrg->states[mrg->state_no]; + //CtxStyle *style = &state->style; + float ascent, descent; + ctx_font_extents (mrg->ctx, &ascent, &descent, NULL); + + float val = mrg->line_max_height[mrg->line_level] * ascent; + + char name[10]="lin_"; + name[3]=mrg->line_level+2; + + ctx_resolve (mrg->ctx, name, set_line_height, &val); + + mrg->line_max_height[mrg->line_level] = 0.0f;//style->font_size;//0.0f; + mrg->line_got_baseline[mrg->line_level]=0; +} +#endif + +/* mrg - MicroRaptor Gui + * Copyright (c) 2014 Øyvind Kolås + * + * 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 . + */ + +/**************/ + +static float measure_word_width (Css *mrg, const char *word) +{ + return ctx_text_width (mrg->ctx, word); +} + +const char * hl_punctuation[] = +{";", ",", "(", ")", "{", "}", NULL}; +const char * hl_operators [] = +{"-", "+", "=", "*", "/", "return", "<", ">", ":", + "if", "else", "break", "case", NULL}; +const char * hl_types[] = +// XXX anything ending in _t ? +{"int", "uint32_t", "uint64_t", "uint8_t", "Ctx", "cairo_t", "Css", "float", "double", + "char", "const", "static", "void", "NULL", + "#include", "#define", NULL}; + +static int is_one_of (const char *word, const char **words) +{ + int i; + for (i = 0; words[i]; i++) + { + if (!strcmp (words[i], word)) + return 1; + } + return 0; +} + +static int is_a_number (const char *word) +{ + int yep = 1; + int i; + for (i = 0; word[i]; i++) + { + if ((word[i] < '0' || word[i] > '9') && word[i] != '.') + yep = 0; + } + return yep; +} + +/* the syntax highlighting is done with static globals; deep in the text + * rendering, this permits the editing code to recognize which string is + * edited and directly work with pointer arithmetic on that instead of + * marked up xml for the highlighting - it limits the syntax highlighting + * context ability + */ +enum { + MRG_HL_NEUTRAL = 0, + MRG_HL_NEXT_NEUTRAL = 1, + MRG_HL_STRING = 2, + MRG_HL_STRING_ESC = 3, + MRG_HL_QSTRING = 4, + MRG_HL_QSTRING_ESC = 5, + MRG_HL_SLASH = 6, + MRG_HL_LINECOMMENT = 7, + MRG_HL_COMMENT = 8, + MRG_HL_COMMENT_STAR = 9, +}; + +static int hl_state_c = MRG_HL_NEUTRAL; + +static void mrg_hl_token (Ctx *cr, const char *word) +{ + switch (hl_state_c) + { + case MRG_HL_NEUTRAL: + if (!strcmp (word, "\"")) + { + hl_state_c = MRG_HL_STRING; + } + else if (!strcmp (word, "'")) + { + hl_state_c = MRG_HL_QSTRING; + } + else if (!strcmp (word, "/")) + { + hl_state_c = MRG_HL_SLASH; + } + break; + case MRG_HL_SLASH: + if (!strcmp (word, "/")) + { + hl_state_c = MRG_HL_LINECOMMENT; + } else if (!strcmp (word, "*")) + { + hl_state_c = MRG_HL_COMMENT; + } else + { + hl_state_c = MRG_HL_NEUTRAL; + } + break; + case MRG_HL_LINECOMMENT: + if (!strcmp (word, "\n")) + { + hl_state_c = MRG_HL_NEXT_NEUTRAL; + } + break; + case MRG_HL_COMMENT: + if (!strcmp (word, "*")) + { + hl_state_c = MRG_HL_COMMENT_STAR; + } + break; + case MRG_HL_COMMENT_STAR: + if (!strcmp (word, "/")) + { + hl_state_c = MRG_HL_NEUTRAL; + } + else + { + hl_state_c = MRG_HL_COMMENT; + } + break; + case MRG_HL_STRING: + if (!strcmp (word, "\"")) + { + hl_state_c = MRG_HL_NEXT_NEUTRAL; + } + else if (!strcmp (word, "\\")) + { + hl_state_c = MRG_HL_STRING_ESC; + } + break; + case MRG_HL_STRING_ESC: + hl_state_c = MRG_HL_STRING; + break; + case MRG_HL_QSTRING: + if (!strcmp (word, "'")) + { + hl_state_c = MRG_HL_NEXT_NEUTRAL; + } + else if (!strcmp (word, "\\")) + { + hl_state_c = MRG_HL_QSTRING_ESC; + } + break; + case MRG_HL_QSTRING_ESC: + hl_state_c = MRG_HL_QSTRING; + break; + case MRG_HL_NEXT_NEUTRAL: + hl_state_c = MRG_HL_NEUTRAL; + break; + } + + switch (hl_state_c) + { + case MRG_HL_NEUTRAL: + if (is_a_number (word)) + ctx_rgb (cr, 0.5, 0.0, 0.0); + else if (is_one_of (word, hl_punctuation)) + ctx_rgb (cr, 0.4, 0.4, 0.4); + else if (is_one_of (word, hl_operators)) + ctx_rgb (cr, 0, 0.5, 0); + else if (is_one_of (word, hl_types)) + ctx_rgb (cr, 0.2, 0.2, 0.5); + else + ctx_rgb (cr, 0, 0, 0); + break; + case MRG_HL_STRING: + case MRG_HL_QSTRING: + ctx_rgb (cr, 1, 0, 0.5); + break; + case MRG_HL_COMMENT: + case MRG_HL_COMMENT_STAR: + case MRG_HL_LINECOMMENT: + ctx_rgb (cr, 0.4, 0.4, 1); + break; + } + + ctx_text (cr, word); +} + +/* hook syntax highlighter in here.. */ +void mrg_hl_text (Ctx *cr, const char *text) +{ + int i; + CtxString *word = ctx_string_new (""); + for (i = 0; i < text[i]; i++) + { + switch (text[i]) + { + case ';': + case '-': + case '\'': + case '>': + case '<': + case '=': + case '+': + case ' ': + case ':': + case '"': + case '*': + case '/': + case '\\': + case '[': + case ']': + case ')': + case ',': + case '(': + if (word->length) + { + mrg_hl_token (cr, word->str); + ctx_string_set (word, ""); + } + ctx_string_append_byte (word, text[i]); + mrg_hl_token (cr, word->str); + ctx_string_set (word, ""); + break; + default: + ctx_rgb (cr, 0,0,0); + ctx_string_append_byte (word, text[i]); + break; + } + } + if (word->length) + mrg_hl_token (cr, word->str); + + ctx_string_free (word, 1); +} + +void ctx_listen (Ctx *ctx, + CtxEventType types, + CtxCb cb, + void* data1, + void* data2); + + + +/* x and y in cairo user units ; returns x advance in user units */ +float mrg_draw_string (Css *mrg, CtxStyle *style, + const char *string, + int utf8_len) +{ + //float x = mrg->x; + float y = mrg->y; + float new_x, old_x; + char *temp_string = NULL; + Ctx *cr = mrg_ctx (mrg); + + ctx_current_point (cr, &old_x, NULL); + + if (utf8_len < 0) + utf8_len = ctx_utf8_strlen (string); + + if (ctx_utf8_strlen (string) != utf8_len) + { + const char *t; + int i; + + temp_string = strdup (string); + for (i = 0, t = temp_string ;i < utf8_len && *t; i++) + { + t += ctx_utf8_len (*t); + } + *(char *)t = 0; + string = temp_string; + } +#if 0 + if (mrg_is_terminal (mrg) && mrg_em (mrg) <= CPX * 4 / mrg->ddpx) + { + const char *t; + int i; + + /* XXX: include transforms */ + int offset; + double u = x , v = y; + cairo_matrix_t matrix; + cairo_get_matrix (mrg_ctx (mrg), &matrix); + cairo_matrix_transform_point (&matrix, &u, &v); + + //u = ctx_floorf(u); + //v = ctx_floorf(v); + + offset = (int)(v/CPX) * ((int)(mrg->width/CPX) * 4) + (int)(u/CPX) * 4; + + old_x = x; + for (i = 0, t = string; *t; i++) + { + if ( v >= 0 && u >= 0 && + (int)u/CPX < (int)(mrg->width/CPX) && + (int)v/CPX < (int)(mrg->height/CPX)) + { + int styleno = offset/4; + memcpy (&mrg->glyphs[offset], t, ctx_utf8_len (*t)); + mrg->styles[styleno] = mrg->state->fg + + mrg->state->bg * 8 + + (mrg->state->style.text_decoration & + (CTX_TEXT_DECORATION_BOLD|CTX_TEXT_DECORATION_DIM|CTX_TEXT_DECORATION_UNDERLINE|CTX_TEXT_DECORATION_REVERSE)) * 64;; + } + t += ctx_utf8_len (*t); + offset += 4; + x += CPX / mrg->ddpx; + } + new_x = x; + } + else +#endif + //if (mrg->in_paint) + { + ctx_font_size (cr, style->font_size); + +#if 0 + if (PROP(text_stroke_width) > 0.01) + { + CtxColor *color = ctx_color_new (); + ctx_get_color (cr, SQZ_text_stroke_color, color); + mrg_ctx_set_source_color (cr, color); + ctx_begin_path (cr); + ctx_move_to (cr, x, y - _mrg_text_shift (mrg)); + ctx_line_width (cr, PROP(text_stroke_width)); + ctx_line_join (cr, CTX_JOIN_ROUND); + ctx_text_stroke (cr, string); + ctx_color_free (color); + } +#endif + + { + CtxColor *color = ctx_color_new (); + ctx_get_color (cr, SQZ_color, color); + mrg_ctx_set_source_color (cr, color); + ctx_color_free (color); + } + //ctx_move_to (cr, x, y - _mrg_text_shift (mrg)); + ctx_current_point (cr, &old_x, NULL); + + /* when syntax highlighting,.. should do it as a coloring + * directly here.. + */ + + const char *syntax_highlight = PROPS(syntax_highlight); + if (!syntax_highlight) syntax_highlight = ""; + + if (syntax_highlight[0] == 0) + ctx_text (cr, string); + else if (!strcmp (syntax_highlight, "C")) + mrg_hl_text (cr, string); + else + ctx_text (cr, string); + + ctx_current_point (cr, &new_x, NULL); + + if (style->text_decoration & CTX_TEXT_DECORATION_UNDERLINE) + { + ctx_rel_move_to (cr, -(new_x-old_x), 0); + ctx_rel_line_to (cr, new_x-old_x, 0); + ctx_stroke (cr); + } + if (style->text_decoration & CTX_TEXT_DECORATION_LINETHROUGH) + { + ctx_move_to (cr, old_x, y - style->font_size / 2); + ctx_line_to (cr, new_x, y - style->font_size / 2); + ctx_stroke (cr); + } + if (style->text_decoration & CTX_TEXT_DECORATION_OVERLINE) + { + ctx_move_to (cr, old_x, y - style->font_size); + ctx_line_to (cr, new_x, y - style->font_size); + ctx_stroke (cr); + } + //ctx_move_to (cr, new_x, y); + } +#if 0 + else + { + ctx_font_size (cr, style->font_size); + new_x = old_x + ctx_text_width (cr, string); + } +#endif + + if (mrg->text_listen_active) + { + float em = mrg_em (mrg); + int no = mrg->text_listen_count-1; + float x, y; + + ctx_current_point (cr, &x, &y); + + ctx_begin_path (cr); + ctx_rectangle (cr, + old_x, y - em, new_x - old_x + 1, em * mrg->state->style.line_height); + ctx_listen (cr, + mrg->text_listen_types[no], + mrg->text_listen_cb[no], + mrg->text_listen_data1[no], + mrg->text_listen_data2[no]); + ctx_begin_path (cr); + ctx_move_to (cr, x, y); + } + + if (temp_string) + free (temp_string); + + return new_x - old_x; +} + +float mrg_addstr (Css *mrg, const char *string, int utf8_length); + +float paint_span_bg_final (Css *mrg, float x, float y, + float width) +{ + CtxStyle *style = ctx_style (mrg); + Ctx *cr = mrg_ctx (mrg); + if (style->display != CTX_DISPLAY_INLINE) + return 0.0; + CtxColor *background_color = ctx_color_new (); + ctx_get_color (cr, SQZ_background_color, background_color); + + if (!ctx_color_is_transparent (background_color)) + { + ctx_save (cr); + ctx_rectangle (cr, x, y + mrg_em (mrg), + width + PROP(padding_right), + mrg_em (mrg) * style->line_height); + mrg_ctx_set_source_color (cr, background_color); + ctx_fill (cr); + ctx_restore (cr); + } + + _mrg_border_top_r (mrg, x, y , width, mrg_em (mrg)); + _mrg_border_bottom_r (mrg, x,y, width, mrg_em (mrg)); + _mrg_border_right (mrg, x, y ,width, mrg_em (mrg)); + + ctx_color_free (background_color); + return PROP(padding_right) + PROP(border_right_width); +} + +float paint_span_bg (Css *mrg, float x, float y, + float width) +{ + CtxStyle *style = ctx_style (mrg); + Ctx *cr = mrg_ctx (mrg); + if (!cr) + return 0.0; + float left_pad = 0.0; + float left_border = 0.0; + if (style->display != CTX_DISPLAY_INLINE) + return 0.0; + + CtxColor *background_color = ctx_color_new (); + ctx_get_color (cr, SQZ_background_color, background_color); + + if (!mrg->state->span_bg_started) + { + left_pad = PROP(padding_left); + left_border = PROP(border_left_width); + mrg->state->span_bg_started = 1; + } + + if (!ctx_color_is_transparent (background_color)) + { + ctx_save (cr); + ctx_rectangle (cr, x + left_border, y, + width + left_pad, + mrg_em (mrg) * style->line_height); + mrg_ctx_set_source_color (cr, background_color); + ctx_fill (cr); + ctx_restore (cr); + } + + if (left_pad || left_border) + { + _mrg_border_left (mrg, x + left_pad + left_border, y, width, mrg_em (mrg)); + _mrg_border_top_l (mrg, x + left_pad + left_border, y, width , mrg_em (mrg)); + _mrg_border_bottom_l (mrg, x + left_pad + left_border, y, width , mrg_em (mrg)); + } + else + { + _mrg_border_top_m (mrg, x, y, width, mrg_em (mrg)); + _mrg_border_bottom_m (mrg, x, y, width, mrg_em (mrg)); + } + + ctx_color_free (background_color); + + return left_pad + left_border; +} + +float +mrg_addstr (Css *mrg, const char *string, int utf8_length) +{ + float x = mrg->x; + float y = mrg->y; + CtxStyle *style = ctx_style (mrg); + float wwidth = measure_word_width (mrg, string); + float left_pad; + left_pad = paint_span_bg (mrg, x, y, wwidth); // TODO avoid doing this for out of bounds + + mrg->line_max_height[mrg->line_level] = + ctx_maxf (mrg->line_max_height[mrg->line_level], + style->font_size); + { + float tx = x; + float ty = y; + ctx_user_to_device (mrg_ctx (mrg), &tx, &ty); + if (ty > ctx_height (mrg->ctx) * 2 || + tx > ctx_width (mrg->ctx)* 2 || + tx < -ctx_width (mrg->ctx) * 2 || + ty < -ctx_height (mrg->ctx) * 2) + { + /* bailing early*/ + } + else + { + + if (mrg->line_got_baseline[mrg->line_level] == 0) + { + ctx_move_to (mrg->ctx, mrg->x, mrg->y); + + char name[10]="lin_"; + name[3]=mrg->line_level+2; + + ctx_deferred_rel_move_to (mrg->ctx, name, 0.0, 0.0);//mrg_em (mrg)); + mrg->line_got_baseline[mrg->line_level] = 1; + } + + if (left_pad != 0.0f) + { + ctx_rel_move_to (mrg->ctx, left_pad, 0.0f); + } + mrg_draw_string (mrg, &mrg->state->style, string, utf8_length); + } + } + + return wwidth + left_pad; +} + +/******** end of core text-drawing primitives **********/ + +#if 0 +void mrg_xy (Css *mrg, float x, float y) +{ + mrg->x = x * mrg_em (mrg); + mrg->y = y * mrg_em (mrg); +} +#endif + +void mrg_set_xy (Css *mrg, float x, float y) +{ + mrg->x = x; + mrg->y = y; + mrg->state->overflowed = 0; +} + +float mrg_x (Css *mrg) +{ + return mrg->x; +} + +float mrg_y (Css *mrg) +{ + return mrg->y; +} + +void mrg_set_wrap_skip_lines (Css *mrg, int skip_lines); +void mrg_set_wrap_max_lines (Css *mrg, int max_lines); + +void mrg_set_wrap_skip_lines (Css *mrg, int skip_lines) +{ + mrg->state->skip_lines = skip_lines; +} + +void mrg_set_wrap_max_lines (Css *mrg, int max_lines) +{ + mrg->state->max_lines = max_lines; +} + +static void _mrg_spaces (Css *mrg, int count) +{ + while (count--) + { + if (mrg->state->style.print_symbols) + mrg->x+=mrg_addstr (mrg, "␣", -1); + else + { + float diff = mrg_addstr (mrg, " ", 1); + +#if 0 + if (mrg_is_terminal (mrg) && mrg_em (mrg) <= CPX * 4 / mrg->ddpx) + { + } + else +#endif + { + if (mrg->state->style.text_decoration & CTX_TEXT_DECORATION_REVERSE) + { + Ctx *cr = mrg_ctx (mrg); + ctx_rectangle (cr, mrg->x + diff*0.1, mrg->y + mrg_em(mrg)*0.2, diff*0.8, -mrg_em (mrg)*1.1); + ctx_rgb (cr, 1,1,1); + ctx_fill (cr); + } + } + mrg->x += diff; + } + } +} + +#define EMIT_NL() \ + do {wraps++; \ + if (wraps >= max_lines)\ + return wraps;\ + if (skip_lines-- <=0)\ + {\ + if (print) { if (gotspace)\ + _mrg_spaces (mrg, 1);\ + if (cursor_start == pos -1 && cursor_start>0 && mrg->text_edited)\ + {\ + css_start (mrg, ".cursor", NULL);\ + _mrg_spaces (mrg, 1);\ + _mrg_nl (mrg);\ + css_end (mrg, NULL);\ + }\ + else\ + _mrg_nl (mrg);\ + } else _mrg_nl (mrg);\ + }\ + if (skip_lines<=0)\ + mrg_set_xy (mrg, _mrg_dynamic_edge_left(mrg), mrg_y (mrg));}while(0) + +#define EMIT_NL2() \ + do {\ + if (skip_lines-- <=0)\ + {\ + if (print) {if (gotspace)\ + _mrg_spaces (mrg, 1);\ + if (cursor_start == *pos -1 && cursor_start>0 && mrg->text_edited)\ + {\ + css_start (mrg, ".cursor", NULL);\ + _mrg_spaces (mrg, 1);\ + _mrg_nl (mrg);\ + css_end (mrg, NULL);\ + }\ + else\ + _mrg_nl (mrg);\ + } else _mrg_nl (mrg);\ + }\ + if (skip_lines<=0)\ + mrg_set_xy (mrg, _mrg_dynamic_edge_left(mrg), mrg_y (mrg));}while(0) + + + +static void mrg_get_edit_state (Css *mrg, + float *x, float *y, float *s, float *e, + float *em_size) +{ + if (x) *x = mrg->e_x; + if (y) *y = mrg->e_y; + if (s) *s = mrg->e_ws; + if (e) *e = mrg->e_we; + if (em_size) *em_size = mrg->e_em; +} + + +static void emit_word (Css *mrg, + int print, + const char *data, + const char *word, + int max_lines, + int skip_lines, + int cursor_start, + int *pos, + int *wraps, + int *wl, + int c, + int gotspace) +{ + float len = ctx_utf8_strlen (word); + float wwidth = measure_word_width (mrg, word); + + if (mrg->x + wwidth >= _mrg_dynamic_edge_right (mrg)) + { + if (mrg->x > mrg_edge_left(mrg) || *wraps != 0) + { + EMIT_NL2(); + } + } + if (mrg->x != mrg_edge_left(mrg) && gotspace) + { + if ((skip_lines<=0)) + { + if (cursor_start == *pos-1 && cursor_start>=0 && mrg->text_edited) + { + if (print) { + css_start (mrg, ".cursor", NULL); + _mrg_spaces (mrg, 1); + css_end (mrg, NULL); + } else { + mrg->x += measure_word_width (mrg, " "); + } + } + else + { + if (print){ + if (mrg->state->style.print_symbols) + { + css_start (mrg, "dim", NULL); + mrg->x += mrg_addstr (mrg, "␣", -1); + css_end (mrg, NULL); + } + else + _mrg_spaces (mrg, 1); + } else { + if (mrg->state->style.print_symbols) + { + mrg->x += measure_word_width (mrg, "␣"); + } + else + { + mrg->x += measure_word_width (mrg, " "); + } + } + } + } + } + if ((skip_lines<=0)) { + if (print){if (cursor_start >= *pos && *pos + len > cursor_start && mrg->text_edited) + { +#if 0 // XXX: there is a bug in mrg_addstr it doesn't respect the length argument + mrg->x += mrg_addstr (mrg, word, cursor_start - *pos); + css_start (mrg, ".cursor", NULL); + mrg->x += mrg_addstr (mrg, mrg_utf8_skip (word, cursor_start - *pos), 1); + css_end (mrg, NULL); + mrg->x += mrg_addstr (mrg, mrg_utf8_skip (word, cursor_start - *pos + 1), len - (cursor_start - *pos) - 1); +#else + + char *dup, *dup2, *dup3; + + dup = strdup (word); + dup2 = strdup (ctx_utf8_skip (dup, cursor_start - *pos)); + dup3 = strdup (ctx_utf8_skip (dup, cursor_start - *pos + 1)); + *((char*)ctx_utf8_skip (dup, cursor_start - *pos)) = 0; + *((char*)ctx_utf8_skip (dup2, 1)) = 0; + + mrg->x += mrg_addstr (mrg, dup, -1); + css_start (mrg, ".cursor", NULL); + mrg->x += mrg_addstr (mrg, dup2, -1); + css_end (mrg, NULL); + mrg->x += mrg_addstr (mrg, dup3, -1); + + free (dup); + free (dup2); + free (dup3); +#endif + } + else + { + mrg->x += mrg_addstr (mrg, word, len); + } + } else { + mrg->x += wwidth; + } + } + *pos += len; + *wl = 0; + +} + +static int css_print_wrap (Css *mrg, + int print, + const char *data, int length, + int max_lines, + int skip_lines, + int cursor_start, + float *retx, + float *rety) +{ +#define MAX_WORDL 1024 + char word[MAX_WORDL+1]=""; + int wl = 0; + int c; + int wraps = 0; + int pos; + int gotspace = 0; + + if (mrg->state->overflowed) + { + return 0; + } + + float space_width = measure_word_width (mrg, " "); + + pos = 0; + + if (max_lines <= 0) + max_lines = 4096; + if (retx) + *retx = -1; + + if (mrg->text_edited && print) + { + mrg->e_x = mrg->x; + mrg->e_y = mrg->y; + mrg->e_ws = mrg_edge_left(mrg); + mrg->e_we = mrg_edge_right(mrg); + mrg->e_em = mrg_em (mrg); + } + + ctx_font_size (mrg_ctx (mrg), ctx_style(mrg)->font_size); + + for (c = 0 ; c < length && data[c] && ! mrg->state->overflowed; c++) + switch (data[c]) + { + case '\n': + if (wl) + { + emit_word (mrg, print, data, word, + max_lines, skip_lines, + cursor_start, + &pos, &wraps, &wl, c, gotspace); + } + pos++; + + if (mrg->state->style.print_symbols && print) + { + css_start (mrg, "dim", NULL); + mrg->x+=mrg_addstr (mrg, "¶", -1);\ + css_end (mrg, NULL); + } + EMIT_NL(); + gotspace = 0; + break; + case '\t': // XXX: this collapses tabs to a single space + case ' ': + if (wl == 0) + { + if (cursor_start == pos-1 && cursor_start>=0 && mrg->text_edited) + { + if (print) + { + css_start (mrg, ".cursor", NULL); + _mrg_spaces (mrg, 1); + css_end (mrg, NULL); + } + else + mrg->x+=mrg_addstr (mrg, " ", -1); + } + else + { + if (mrg->state->style.print_symbols) + { + css_start (mrg, "dim", NULL); + mrg->x+=mrg_addstr (mrg, "␣", -1); + css_end (mrg, NULL); + } + else + { + mrg->x+=mrg_addstr (mrg, " ", -1); + + } + } + } + else + { + emit_word (mrg, print, data, word, + max_lines, skip_lines, + cursor_start, + &pos, &wraps, &wl, c, gotspace); + } + pos++; + + if (retx && *retx < 0 && pos >= cursor_start) + { + float tailwidth; + const char *rest = &word[ctx_utf8_strlen (word) - (pos-cursor_start)]; +#if 0 + if (mrg_is_terminal (mrg)) + tailwidth = (pos-cursor_start -1) * CPX / mrg->ddpx; + else +#endif + tailwidth = measure_word_width (mrg, rest); + *retx = mrg->x - tailwidth; + *rety = mrg->y; + return pos; + } + gotspace = 1; + break; + default: + word[wl++]= data[c]; + wl = ctx_mini (wl, MAX_WORDL-1); + word[wl] = '\0'; + break; + } + if (wl) /* orphaned word for last line. */ + { + emit_word (mrg, print, data, word, + max_lines, skip_lines, + cursor_start, + &pos, &wraps, &wl, c, gotspace); + } + /* cursor at end */ + if (cursor_start == pos && cursor_start>=0 && mrg->text_edited) + { + if (print) + { + if (c && data[c-1]==' ') + mrg->x += space_width; + css_start (mrg, ".cursor", NULL); + _mrg_spaces (mrg, 1); + css_end (mrg, NULL); + } + else + mrg->x += space_width; + } + if (retx && *retx < 0 && pos >= cursor_start) + { + *retx = mrg->x; + *rety = mrg->y; + return pos; + } + return wraps; +} + +int css_print_get_xy (Css *mrg, const char *string, int no, float *x, float *y) +{ + int ret; + if (!string) + return 0; + + if (mrg_edge_left(mrg) != mrg_edge_right(mrg)) + { + float ox, oy; + ox = mrg->x; + oy = mrg->y; + ret = css_print_wrap (mrg, 0, string, strlen (string), mrg->state->max_lines, + mrg->state->skip_lines, no, x, y); + mrg->x = ox; + mrg->y = oy; + return ret; + } + if (y) *y = mrg->y; + if (x) *x = mrg->x + no; // XXX: only correct for nct/monospace + + return 0; +} + +typedef struct _CssGlyph CssGlyph; + +struct _CssGlyph{ + unsigned long index; /* done this way, the remnants of layout; before feeding + * glyphs positions in cairo, similar to how pango would do + * can be reused for computing the caret nav efficiently. + */ + float x; + float y; + int no; +}; + +static int css_print_wrap2 (Css *mrg, + int print, + const char *data, int length, + int max_lines, + int skip_lines, + CtxList **list) +{ + char word[400]=""; + int wl = 0; + int c; + int wraps = 0; + int pos; + int gotspace = 0; + int cursor_start = -1; + + CssGlyph *g = calloc (sizeof (CssGlyph), 1); + g->x = length; + g->y = 42; + g->index = 44; + g->no = 2; + ctx_list_append (list, g); + + if (mrg->state->overflowed) + { + return 0; + } + + pos = 0; + + if (max_lines <= 0) + max_lines = 4096; + + if (mrg->text_edited && print) + { + mrg->e_x = mrg->x; + mrg->e_y = mrg->y; + mrg->e_ws = mrg_edge_left(mrg); + mrg->e_we = mrg_edge_right(mrg); + mrg->e_em = mrg_em (mrg); +#if 0 + if (mrg->scaled_font) + cairo_scaled_font_destroy (mrg->scaled_font); +#endif + ctx_font_size (mrg_ctx (mrg), ctx_style(mrg)->font_size); +#if 0 + mrg->scaled_font = cairo_get_scaled_font (mrg_ctx (mrg)); + cairo_scaled_font_reference (mrg->scaled_font); +#endif + } + + for (c = 0 ; c < length && data[c] && ! mrg->state->overflowed; c++) + switch (data[c]) + { + case '\n': + if (wl) + { + emit_word (mrg, print, data, word, + max_lines, skip_lines, + cursor_start, + &pos, &wraps, &wl, c, gotspace); + } + pos++; + + if (mrg->state->style.print_symbols && print) + { + css_start (mrg, "dim", NULL); + mrg->x+=mrg_addstr (mrg, "¶", -1);\ + css_end (mrg, NULL); + } + EMIT_NL(); + gotspace = 0; + break; + case ' ': + if (wl == 0) + { + if (cursor_start == pos-1 && cursor_start>=0 && mrg->text_edited) + { + if (print) + { + css_start (mrg, ".cursor", NULL); + _mrg_spaces (mrg, 1); + css_end (mrg, NULL); + } + else + mrg->x+=mrg_addstr (mrg, " ", -1); + } + else + { + if (mrg->state->style.print_symbols) + { + css_start (mrg, "dim", NULL); + mrg->x+=mrg_addstr (mrg, "␣", -1); + css_end (mrg, NULL); + } + else + { + mrg->x+=mrg_addstr (mrg, " ", -1); + } + } + } + else + { + emit_word (mrg, print, data, word, + max_lines, skip_lines, + cursor_start, + &pos, &wraps, &wl, c, gotspace); + } + pos++; + +#if 0 + if (retx && *retx < 0 && pos >= cursor_start) + { + float tailwidth; + const char *rest = &word[ctx_utf8_strlen (word) - (pos-cursor_start)]; + if (mrg_is_terminal (mrg)) + tailwidth = (pos-cursor_start -1) * CPX / mrg->ddpx; + else + tailwidth = measure_word_width (mrg, rest); + *retx = mrg->x - tailwidth; + *rety = mrg->y; + return pos; + } +#endif + gotspace = 1; + break; + default: + word[wl++]= data[c]; + word[wl] = '\0'; + break; + } + if (wl) /* orphaned word for last line. */ + { + emit_word (mrg, print, data, word, + max_lines, skip_lines, + cursor_start, + &pos, &wraps, &wl, c, gotspace); + } + /* cursor at end */ + if (cursor_start == pos && cursor_start>=0 && mrg->text_edited) + { + if (print) + { + css_start (mrg, ".cursor", NULL); + _mrg_spaces (mrg, 1); + css_end (mrg, NULL); + } + else + mrg->x += measure_word_width (mrg, " "); + } +#if 0 + if (retx && *retx < 0 && pos >= cursor_start) + { + *retx = mrg->x; + *rety = mrg->y; + return pos; + } +#endif + return wraps; +} + +CtxList *css_print_get_coords (Css *mrg, const char *string) +{ + CtxList *ret = NULL; + if (!string) + return ret; + + if (mrg_edge_left(mrg) != mrg_edge_right(mrg)) + { + float ox, oy; + ox = mrg->x; + oy = mrg->y; + css_print_wrap2 (mrg, 0, string, strlen (string), mrg->state->max_lines, + mrg->state->skip_lines, &ret); + mrg->x = ox; + mrg->y = oy; + return ret; + } + + return ret; +} + +#include + +int css_print (Css *mrg, const char *string) +{ + float ret; + CtxStyle *style = ctx_style (mrg); + mrg->unresolved_line = 1; + +#ifdef SNAP + float em = mrg_em (mrg); /* XXX: a global body-line spacing + snap is better grid design */ + mrg->x = ceil (mrg->x / em) * em; + mrg->y = ceil (mrg->y / em) * em; +#endif + + if (mrg->text_edited) + ctx_string_append_str (mrg->edited_str, string); + + if (style->display == CTX_DISPLAY_NONE) + return 0.0; + + if (!string) + return 0; + + if (mrg_edge_left(mrg) != mrg_edge_right(mrg)) + return css_print_wrap (mrg, 1, string, strlen (string), mrg->state->max_lines, mrg->state->skip_lines, mrg->cursor_pos, NULL, NULL); + + ret = mrg_addstr (mrg, string, ctx_utf8_strlen (string)); + mrg->x += ret; + return ret; +} + +void _mrg_text_prepare (Css *mrg) +{ + hl_state_c = MRG_HL_NEUTRAL; +} + +void _mrg_text_init (Css *mrg) +{ + // XXX: this should be done in a prepre,.. not an init? + // + mrg->state->style.line_height = 1.0; + mrg->state->style.print_symbols = 0; +} + +void mrg_text_listen_done (Css *mrg) +{ + mrg->text_listen_active = 0; +} + +void mrg_text_listen_full (Css *mrg, CtxEventType types, + CtxCb cb, void *data1, void *data2, + void (*finalize)(void *listen_data, void *listen_data2, void *finalize_data), + void *finalize_data) +{ + int no = mrg->text_listen_count; + if (cb == NULL) + { + mrg_text_listen_done (mrg); + return; + } + if (no + 1 >= CTX_MAX_TEXT_LISTEN) + { + fprintf (stderr, "mrg text listen overflow\n"); + return; + } + + mrg->text_listen_types[no] = types; + mrg->text_listen_cb[no] = cb; + mrg->text_listen_data1[no] = data1; + mrg->text_listen_data2[no] = data2; + mrg->text_listen_finalize[no] = finalize; + mrg->text_listen_finalize_data[no] = finalize_data; + mrg->text_listen_count++; + mrg->text_listen_active = 1; +} + +void mrg_text_listen (Css *mrg, CtxEventType types, + CtxCb cb, void *data1, void *data2) +{ + mrg_text_listen_full (mrg, types, cb, data1, data2, NULL, NULL); +} + + +static void cmd_home (CtxEvent *event, void *data1, void *data2) +{ + Css *mrg = data1; + mrg->cursor_pos = 0; + mrg_queue_draw (mrg, NULL); + ctx_event_stop_propagate (event); +} + +static void cmd_end (CtxEvent *event, void *data1, void *data2) +{ + Css *mrg = data1; + mrg->cursor_pos = ctx_utf8_strlen (mrg->edited_str->str); + mrg_queue_draw (mrg, NULL); + ctx_event_stop_propagate (event); +} + +static void cmd_backspace (CtxEvent *event, void *data1, void *data2) +{ + Css *mrg = data1; + char *new; + const char *rest = ctx_utf8_skip (mrg->edited_str->str, mrg->cursor_pos); + const char *mark = ctx_utf8_skip (mrg->edited_str->str, mrg->cursor_pos-1); + + if (mrg->cursor_pos <= 0) + { + mrg->cursor_pos = 0; + } + else + { + new = malloc (strlen (mrg->edited_str->str) + 1); + memcpy (new, mrg->edited_str->str, ((mark - mrg->edited_str->str))); + memcpy (new + ((mark - mrg->edited_str->str)), rest, strlen (rest)); + new [strlen (mrg->edited_str->str)-(rest-mark)] = 0; + mrg->update_string (new, mrg->update_string_user_data); + ctx_string_set (mrg->edited_str, new); + free (new); + mrg->cursor_pos--; + } + mrg_queue_draw (mrg, NULL); + ctx_event_stop_propagate (event); +} + +static void cmd_delete (CtxEvent *event, void *data1, void *data2) +{ + Css *mrg = data1; + char *new; + const char *rest = ctx_utf8_skip (mrg->edited_str->str, mrg->cursor_pos+1); + const char *mark = ctx_utf8_skip (mrg->edited_str->str, mrg->cursor_pos); + + new = malloc (strlen (mrg->edited_str->str) + 1); + memcpy (new, mrg->edited_str->str, ((mark - mrg->edited_str->str))); + memcpy (new + ((mark - mrg->edited_str->str)), rest, strlen (rest)); + new [strlen (mrg->edited_str->str)-(rest-mark)] = 0; + + mrg->update_string (new, mrg->update_string_user_data); + ctx_string_set (mrg->edited_str, new); + free (new); + mrg_queue_draw (mrg, NULL); + ctx_event_stop_propagate (event); +} + +static void cmd_down (CtxEvent *event, void *data1, void *data2) +{ + Css *mrg = data1; + float e_x, e_y, e_s, e_e, e_em; + float cx, cy; + cx = cy = 0; + + mrg_get_edit_state (mrg, &e_x, &e_y, &e_s, &e_e, &e_em); + css_set_edge_left (mrg, e_s - PROP (padding_left)); + css_set_edge_right (mrg, e_e + PROP (padding_right)); + mrg_set_xy (mrg, e_x, e_y); + css_print_get_xy (mrg, mrg->edited_str->str, mrg->cursor_pos, &cx, &cy); + + { + int no; + int best = mrg->cursor_pos; + float best_score = 10000000000.0; + float best_y = cy; + int strl = ctx_utf8_strlen (mrg->edited_str->str); + for (no = mrg->cursor_pos + 1; no < mrg->cursor_pos + 256 && no < strl; no++) + { + float x = 0, y = 0; + float attempt_score = 0.0; + mrg_set_xy (mrg, e_x, e_y); + css_print_get_xy (mrg, mrg->edited_str->str, no, &x, &y); + + if (y > cy && best_y == cy) + best_y = y; + + if (y > cy) + attempt_score = (y - best_y); + else + attempt_score = 1000.0; + + attempt_score += fabs(cx-x) / 10000000.0; + + if (attempt_score <= best_score) + { + best_score = attempt_score; + best = no; + } + } + if (best_y == cy) + { + mrg->cursor_pos = strl; +#if 0 + ctx_key_press (mrg, 0, "down-nudge", 0); +#endif + mrg_queue_draw (mrg, NULL); + return; + } + mrg->cursor_pos = best; + } + + if (mrg->cursor_pos >= ctx_utf8_strlen (mrg->edited_str->str)) + mrg->cursor_pos = ctx_utf8_strlen (mrg->edited_str->str) - 1; + mrg_queue_draw (mrg, NULL); + ctx_event_stop_propagate (event); +} + +static void cmd_up (CtxEvent *event, void *data1, void *data2) +{ + Css *mrg = data1; + float e_x, e_y, e_s, e_e, e_em; + float cx = 0.0f, cy = 0.0f; + mrg_get_edit_state (mrg, &e_x, &e_y, &e_s, &e_e, &e_em); + + css_set_edge_left (mrg, e_s - PROP(padding_left)); + css_set_edge_right (mrg, e_e + PROP(padding_right)); + + mrg_set_xy (mrg, e_x, e_y); + css_print_get_xy (mrg, mrg->edited_str->str, mrg->cursor_pos, &cx, &cy); + + /* XXX: abstract the finding of best cursor pos for x coord to a function */ + { + int no; + int best = mrg->cursor_pos; + float best_y = cy; + float best_score = 1000000000000.0; + for (no = mrg->cursor_pos - 1; no>= mrg->cursor_pos - 256 && no > 0; no--) + { + float x = 0, y = 0; + float attempt_score = 0.0; + mrg_set_xy (mrg, e_x, e_y); + css_print_get_xy (mrg, mrg->edited_str->str, no, &x, &y); + + if (y < cy && best_y == cy) + best_y = y; + + if (y < cy) + attempt_score = (best_y - y); + else + attempt_score = 1000.0; + + attempt_score += fabs(cx-x) / 10000000.0; + + if (attempt_score < best_score) + { + best_score = attempt_score; + best = no; + } + } + mrg->cursor_pos = best; + if (best_y == cy) + { + mrg->cursor_pos = 0; + mrg_queue_draw (mrg, NULL); + ctx_key_press (event->ctx, 0, "up-nudge", 0); + return; // without stop propagate this should permit things registered earlier to fire + } + } + + if (mrg->cursor_pos < 0) + mrg->cursor_pos = 0; + mrg_queue_draw (mrg, NULL); + ctx_event_stop_propagate (event); +} + +int mrg_get_cursor_pos (Css *mrg) +{ + return mrg->cursor_pos; +} + +void mrg_set_cursor_pos (Css *mrg, int pos) +{ + mrg->cursor_pos = pos; + mrg_queue_draw (mrg, NULL); +} + +static void cmd_page_down (CtxEvent *event, void *data1, void *data2) +{ + int i; + for (i = 0; i < 6; i++) + cmd_down (event, data1, data2); + ctx_event_stop_propagate (event); +} + +static void cmd_page_up (CtxEvent *event, void *data1, void *data2) +{ + int i; + for (i = 0; i < 6; i++) + cmd_up (event, data1, data2); + ctx_event_stop_propagate (event); +} + +static void cmd_left (CtxEvent *event, void *data1, void *data2) +{ + Css *mrg = data1; + mrg->cursor_pos--; + if (mrg->cursor_pos < 0) + mrg->cursor_pos = 0; + mrg_queue_draw (mrg, NULL); + ctx_event_stop_propagate (event); +} + +static void cmd_right (CtxEvent *event, void *data1, void *data2) +{ + Css *mrg = data1; + mrg->cursor_pos++; + + /* should mrg have captured the text printed in-between to build its idea + * of what is being edited, thus being able to do its own internal cursor + * positioning with that cache? + */ + + if (mrg->cursor_pos > ctx_utf8_strlen (mrg->edited_str->str)) + mrg->cursor_pos = ctx_utf8_strlen (mrg->edited_str->str); + + mrg_queue_draw (mrg, NULL); + ctx_event_stop_propagate (event); +} + + +/* the added utf8 bits go to edited_str as well, so that successive edits do work out + * + */ + +static void add_utf8 (Css *mrg, const char *string) +{ + char *new; + const char *rest; + /* XXX: this is the code the should be turned into a callback/event + * to digest for the user of the framework, with a reasonable default + * for using it from C with a string + */ + + rest = ctx_utf8_skip (mrg->edited_str->str, mrg->cursor_pos); + + new = malloc (strlen (mrg->edited_str->str) + strlen (string) + 1); + memcpy (new, mrg->edited_str->str, (rest-mrg->edited_str->str)); + memcpy (new + (rest-mrg->edited_str->str), string, strlen (string)); + memcpy (new + (rest-mrg->edited_str->str) + strlen (string), + rest, strlen (rest)); + new [strlen (string) + strlen (mrg->edited_str->str)] = 0; + mrg->update_string (new, mrg->update_string_user_data); + ctx_string_set (mrg->edited_str, new); + free (new); + mrg_queue_draw (mrg, NULL); + mrg->cursor_pos++; +} + +static void cmd_unhandled (CtxEvent *event, void *data1, void *data2) +{ + Css *mrg = data1; + if (!strcmp (event->string, "space")) + { + add_utf8 (mrg, " "); + ctx_event_stop_propagate (event); + } + + if (ctx_utf8_strlen (event->string) != 1) + return; + + add_utf8 (mrg, event->string); + ctx_event_stop_propagate (event); +} + +#if 0 +static void cmd_space (CtxEvent *event, void *data1, void *data2) +{ + if (!ctx_utf8_strlen (event->key_name) == 1) + return 0; + + add_utf8 (event->mrg, " "); + return 1; +} +#endif + +static void cmd_return (CtxEvent *event, void *data1, void *data2) +{ + Css *mrg = data1; + // this check excludes terminal from working + //if (!(ctx_utf8_strlen (event->key_name) == 1)) + // return; + + add_utf8 (mrg, "\n"); + ctx_event_stop_propagate (event); +} + +static void cmd_escape (CtxEvent *event, void *data, void *data2) +{ +#if 0 + mrg_edit_string (event->mrg, NULL, NULL, NULL); +#endif +} + +void mrg_text_edit_bindings (Css *mrg) +{ + ctx_add_key_binding (mrg->ctx, "escape", NULL, "stop editing", cmd_escape, mrg); + ctx_add_key_binding (mrg->ctx, "return", NULL, "add newline", cmd_return, mrg); + ctx_add_key_binding (mrg->ctx, "home", NULL, "cursor to start", cmd_home, mrg); + ctx_add_key_binding (mrg->ctx, "end", NULL, "cursor to end", cmd_end, mrg); + ctx_add_key_binding (mrg->ctx, "left", NULL, "cursor left", cmd_left, mrg); + ctx_add_key_binding (mrg->ctx, "right", NULL, "cursor right", cmd_right, mrg); + ctx_add_key_binding (mrg->ctx, "up", NULL, "cursor up", cmd_up, mrg); + ctx_add_key_binding (mrg->ctx, "down", NULL, "cursor down", cmd_down, mrg); + ctx_add_key_binding (mrg->ctx, "page-up", NULL, "cursor up", cmd_page_up, mrg); + ctx_add_key_binding (mrg->ctx, "page-down", NULL, "cursor down", cmd_page_down, mrg); + ctx_add_key_binding (mrg->ctx, "backspace", NULL, "remove preceding character", cmd_backspace, mrg); + ctx_add_key_binding (mrg->ctx, "delete", NULL, "remove character under cursor", cmd_delete, mrg); + ctx_add_key_binding (mrg->ctx, "any", NULL, "add if key name is 1 char long", cmd_unhandled, mrg); +} + +#if 1 +void mrg_edit_string (Css *mrg, char **string, + void (*update_string)(Css *mrg, + char **string_loc, + const char *new_string, + void *user_data), + void *user_data) +{ + if (mrg->edited == string) + return; + mrg->edited = string; + mrg->update_string = (void*)update_string; + mrg->update_string_user_data = user_data; + if (string) + mrg->cursor_pos = ctx_utf8_strlen (*string); + else + mrg->cursor_pos = 0; + mrg_queue_draw (mrg, NULL); +} +#endif + +void +css_printf (Css *mrg, const char *format, ...) +{ + va_list ap; + size_t needed; + char *buffer; + va_start(ap, format); + needed = vsnprintf(NULL, 0, format, ap) + 1; + buffer = malloc(needed); + va_end (ap); + va_start(ap, format); + vsnprintf(buffer, needed, format, ap); + va_end (ap); + css_print (mrg, buffer); + free (buffer); +} + +void css_set_font_size (Css *mrg, float font_size) +{ + mrg->font_size = font_size; + //css_set_stylef (mrg, "font-size:%fpx;", font_size); +} + +void _mrg_block_edit (Css *mrg) +{ + mrg->text_edit_blocked = 1; +} +void _mrg_unblock_edit (Css *mrg) +{ + mrg->text_edit_blocked = 0; +} + +void mrg_edit_start_full (Css *mrg, + CssNewText update_string, + void *user_data, + CtxDestroyNotify destroy, + void *destroy_data) +{ + if (mrg->update_string_destroy_notify) + { + mrg->update_string_destroy_notify (mrg->update_string_destroy_data); + } + mrg->got_edit = 1; + mrg->text_edited = 1; + mrg->update_string = update_string; + mrg->update_string_user_data = user_data; + mrg->update_string_destroy_notify = destroy; + mrg->update_string_destroy_data = destroy_data; +} + +void mrg_edit_start (Css *mrg, + CssNewText update_string, + void *user_data) +{ + return mrg_edit_start_full (mrg, update_string, user_data, NULL, NULL); +} + +void mrg_edit_end (Css *mrg) +{ + mrg->text_edited = 0; + mrg_text_edit_bindings (mrg); +} + + +#if 0 +static void ctx_css_add_class (Css *mrg, const char *class_name) +{ + int i; + CtxStyleNode *node = &mrg->state->style_node; + for (i = 0; node->classes[i]; i++); + node->classes[i] = mrg_intern_string (class_name); +} + +static void ctx_css_add_pseudo_class (Css *mrg, const char *pseudo_class) +{ + int i; + CtxStyleNode *node = &mrg->state->style_node; + for (i = 0; node->pseudo[i]; i++); + node->pseudo[i] = mrg_intern_string (pseudo_class); +} +#endif + +void _mrg_set_wrap_edge_vfuncs (Css *mrg, + float (*wrap_edge_left) (Css *mrg, void *wrap_edge_data), + float (*wrap_edge_right) (Css *mrg, void *wrap_edge_data), + void *wrap_edge_data) +{ + mrg->state->wrap_edge_left = wrap_edge_left; + mrg->state->wrap_edge_right = wrap_edge_right; + mrg->state->wrap_edge_data = wrap_edge_data; +} + +static void update_rect_geo (Ctx *ctx, void *userdata, const char *name, int count, + float *x, float *y, float *w, float *h) +{ + CtxFloatRectangle *geo = userdata; + *w = geo->width; + *h = geo->height; +} + + +void _mrg_layout_post (Css *mrg, CtxFloatRectangle *ret_rect) +{ + Ctx *ctx = mrg->ctx; + float vmarg = 0; + CtxStyle *style = ctx_style (mrg); + float height = PROP(height); + float width = PROP(width); + + float padding_left = PROP(padding_left); + float margin_left = PROP(margin_left); + float border_left_width = PROP(border_left_width); + float padding_right = PROP(padding_right); + //float margin_right = PROP(margin_right); + //float border_right_width = PROP(border_right_width); + float padding_top = PROP(padding_top); + float margin_top = PROP(margin_top); + float border_top_width = PROP(border_top_width); + float padding_bottom = PROP(padding_bottom); + float margin_bottom = PROP(margin_bottom); + float border_bottom_width = PROP(border_bottom_width); + float left = PROP(left); + float top = PROP(top); + + int returned_dim = 0; + float ascent, descent; + ctx_font_extents (mrg->ctx, &ascent, &descent, NULL); + + if (mrg->state->flow_root) + clear_both (mrg); + + if (is_block_item (style)) + { + if (style->display == CTX_DISPLAY_INLINE_BLOCK) + { + if (height == 0) + height = mrg_y (mrg) - (mrg->state->block_start_y); + + mrg->line_max_height[mrg->line_level-1] = + ctx_maxf (mrg->line_max_height[mrg->line_level-1], + height); + + } + else + { + if (mrg->line_got_baseline[mrg->line_level]) + _mrg_nl (mrg); + } + mrg->line_level--; + //mrg->line_got_baseline [mrg->line_level] = 0; + } + + /* remember data to store about float, XXX: perhaps better to store + * straight into parent state? + */ + if (style->float_) + { + CtxFloatData *float_data = &mrg->float_data[mrg->floats]; + // XXX protect against overflow + mrg->floats++; + + float_data->type = style->float_; + float_data->x = + mrg->state->block_start_x - padding_left - border_left_width - margin_left; + float_data->y = + mrg->state->block_start_y - mrg_em(mrg) - padding_top - border_top_width + - margin_top; + + float_data->width = + mrg_edge_right (mrg) - mrg_edge_left (mrg) + //+ border_left_width + //+ border_right_width +#if 0 + /*+ padding_left +*/ + border_left_width + margin_left + /*+ padding_right +*/ + border_right_width + margin_right +#endif + ; + + float_data->height = + mrg_y (mrg) - (mrg->state->block_start_y) + + margin_bottom + padding_top + padding_bottom + border_top_width + border_bottom_width; + } + + + if (style->display == CTX_DISPLAY_INLINE_BLOCK) + { + CtxFloatRectangle _geo; + CtxFloatRectangle *geo = &_geo; + memset (geo, 0, sizeof (_geo)); + + if (width == 0) + { + width = mrg_x (mrg) - (mrg->state->block_start_x) + padding_right; + } + geo->width = width; + + if (height == 0) + height = mrg_y (mrg) - (mrg->state->block_start_y); + geo->height = height; + + char name[10]="ele_"; + name[3]=mrg->state_no+2; + + ctx_resolve (mrg->ctx, name, update_rect_geo, geo); + mrg_box (mrg, + mrg->state->block_start_x, + mrg->state->block_start_y, + geo->width, + geo->height); + if (ret_rect) + { + ret_rect->x = mrg->state->block_start_x; + ret_rect->y = mrg->state->block_start_y; + ret_rect->width = geo->width; + ret_rect->height = geo->height; + returned_dim = 1; + } + + { + CtxMatrix transform; + ctx_get_matrix (mrg_ctx (mrg), &transform); + float x = ctx_pointer_x (ctx); + float y = ctx_pointer_y (ctx); + ctx_matrix_invert (&transform); + ctx_matrix_apply_transform (&transform, &x, &y); + } + + //mrg_edge_right (mrg) - mrg_edge_left (mrg), mrg_y (mrg) - (mrg->state->block_start_y - mrg_em(mrg))); + + mrg_set_xy (mrg, + mrg_x (mrg) + width, + mrg_y (mrg)); + } + else if (is_block_item (style)) + { + CtxFloatRectangle _geo; + CtxFloatRectangle *geo = &_geo; + memset (geo, 0, sizeof (_geo)); + + geo->width = width; + if (width == 0) + { +#if 0 + if (mrg_y (mrg) == (ctx->state->block_start_y)) + geo->width = mrg_x (mrg) - (ctx->state->block_start_x); + else + geo->width = mrg->state->edge_right - (ctx->state->block_start_x); +#endif + if (style->float_) + { + geo->width = mrg_x (mrg) - (mrg->state->block_start_x); + } + else + { + geo->width = _mrg_dynamic_edge_right(mrg)- + _mrg_dynamic_edge_left (mrg); + } + } + + if (height == 0) + height = mrg_y (mrg) - (mrg->state->block_start_y); + geo->height = height; + + char name[10]="ele_"; + name[3]=mrg->state_no+2; + + + mrg_box (mrg, + mrg->state->block_start_x, + mrg->state->block_start_y, + geo->width, + geo->height); + if (ret_rect) + { + ret_rect->x = mrg->state->block_start_x; + ret_rect->y = mrg->state->block_start_y; + ret_rect->width = geo->width; + ret_rect->height = geo->height; + returned_dim = 1; + } + + geo->width += padding_right + padding_left; + geo->height += padding_top + padding_bottom; + ctx_resolve (mrg->ctx, name, update_rect_geo, geo); + + { + CtxMatrix transform; + ctx_get_matrix (mrg_ctx (mrg), &transform); + float x = ctx_pointer_x (ctx); + float y = ctx_pointer_y (ctx); + ctx_matrix_invert (&transform); + ctx_matrix_apply_transform (&transform, &x, &y); + } + + //mrg_edge_right (mrg) - mrg_edge_left (mrg), mrg_y (mrg) - (mrg->state->block_start_y - mrg_em(mrg))); + + if (!style->float_ && (style->display == CTX_DISPLAY_BLOCK || + style->display == CTX_DISPLAY_FLOW_ROOT || + style->display == CTX_DISPLAY_LIST_ITEM)) + { + vmarg = margin_bottom; + + mrg_set_xy (mrg, + mrg_edge_left (mrg), + mrg_y (mrg) + vmarg + border_bottom_width + padding_bottom); + } + } + else if (style->display == CTX_DISPLAY_INLINE) + { + float x0 = mrg->state->original_x; + float y0 = mrg->state->original_y; + float width = mrg->x - x0; + float height = mrg->y - y0; + + if (ret_rect) + { + ret_rect->x = x0; + ret_rect->y = y0; + ret_rect->width = width; + ret_rect->height = height; + + returned_dim = 1; + } + mrg_box (mrg, x0, y0, width, height); + + mrg->x += paint_span_bg_final (mrg, mrg->x, mrg->y, 0); + } + + + /* restore insert position when having been out-of-context */ + if (style->float_ || + style->position == CTX_POSITION_ABSOLUTE || + style->position == CTX_POSITION_FIXED) + { + mrg_set_xy (mrg, mrg->state->original_x, + mrg->state->original_y); + } + +#if 0 + /* restore state to parent */ + mrg->state_no--; + if (mrg->state_no<0) + mrg->state_no=0; + mrg->state = &mrg->states[mrg->state_no]; +#endif + + if (mrg->state->style_id) + { + free (mrg->state->style_id); + mrg->state->style_id = NULL; + } + /* restore relative shift */ + if (style->position == CTX_POSITION_RELATIVE) + { + //ctx_translate (mrg_ctx (mrg), -left, -top); // not really + // // needed we'll + // // restore.. + mrg->relative_x -= left; + mrg->relative_y -= top; + } + + ctx_restore (mrg_ctx (mrg)); + + if (style->position == CTX_POSITION_ABSOLUTE || + style->position == CTX_POSITION_FIXED) + { + int start_offset = mrg->state->drawlist_start_offset; + int end_offset; + const CtxEntry *entries = ctx_get_drawlist (mrg->ctx, &end_offset); + int count = end_offset - start_offset; + + CssAbsolute *absolute = calloc (sizeof (CssAbsolute) + count * 9, 1); + absolute->z_index = style->z_index; + absolute->top = top; + absolute->left = left; + if (style->position == CTX_POSITION_FIXED) + absolute->fixed = 1; + absolute->relative_x = mrg->relative_x; + absolute->relative_y = mrg->relative_y; + absolute->entries = (CtxEntry*) (absolute + 1); + absolute->count = count; + memcpy (absolute->entries, entries + start_offset, count * 9); + + ctx_list_prepend (&mrg->absolutes, absolute); + + ctx_drawlist_force_count (mrg->ctx, mrg->state->drawlist_start_offset); + } + + + + mrg->state_no--; + if (mrg->state_no < 0) + { + fprintf (stderr, "unbalanced css_start/css_end, enderflow %i\n", mrg->state_no); + mrg->state_no = 0; + } + mrg->state = &mrg->states[mrg->state_no]; + + mrg->state->vmarg = vmarg; + + if (ret_rect && !returned_dim) + fprintf (stderr, "didnt return dim!\n"); + + mrg->unresolved_line = 0; +} + +enum { + HTML_ATT_UNKNOWN = 0, + HTML_ATT_STYLE, + HTML_ATT_CLASS, + HTML_ATT_ID, + HTML_ATT_HREF, + HTML_ATT_REL, + HTML_ATT_SRC +}; + +typedef struct CssEntity { + uint32_t name; + const char *value; +} CssEntity; + +static CssEntity entities[]={ + {SQZ_shy, ""}, // soft hyphen,. should be made use of in wrapping.. + {SQZ_nbsp, " "}, // + {SQZ_lt, "<"}, + {SQZ_gt, ">"}, + {SQZ_trade, "™"}, + {SQZ_copy, "©"}, + {SQZ_middot, "·"}, + {SQZ_bull, "•"}, + {SQZ_Oslash, "Ø"}, + {SQZ_oslash, "ø"}, + {SQZ_hellip, "…"}, + {SQZ_aring, "å"}, + {SQZ_Aring, "Å"}, + {SQZ_aelig, "æ"}, + {SQZ_AElig, "Æ"}, + {SQZ_Aelig, "Æ"}, + {SQZ_laquo, "«"}, + {SQZ_raquo, "»"}, + + /*the above were added as encountered, the rest in anticipation */ + + {SQZ_reg, "®"}, + {SQZ_deg, "°"}, + {SQZ_plusmn, "±"}, + {SQZ_sup2, "²"}, + {SQZ_sup3, "³"}, + {SQZ_sup1, "¹"}, + {SQZ_ordm, "º"}, + {SQZ_para, "¶"}, + {SQZ_cedil, "¸"}, + {SQZ_bull, "·"}, + {SQZ_amp, "&"}, + {SQZ_mdash, "–"}, + {SQZ_apos, "'"}, + {SQZ_quot, "\""}, + {SQZ_iexcl, "¡"}, + {SQZ_cent, "¢"}, + {SQZ_pound, "£"}, + {SQZ_euro, "€"}, + {SQZ_yen, "¥"}, + {SQZ_curren, "¤"}, + {SQZ_sect, "§"}, + {SQZ_phi, "Φ"}, + {SQZ_omega, "Ω"}, + {SQZ_alpha, "α"}, + + /* XXX: incomplete */ + + {0, NULL} +}; + +void +ctx_set (Ctx *ctx, uint32_t key_hash, const char *string, int len); + +static int +mrg_parse_transform (Css *mrg, CtxMatrix *matrix, const char *str_in) +{ + // TODO : parse combined transforms + + const char *str = str_in; + + do { + + if (!strncmp (str, "matrix", 5)) + { + char *s; + int numbers = 0; + double number[12]={0.0,}; + ctx_matrix_identity (matrix); + s = (void*) ctx_strchr (str, '('); + if (!s) + return 0; + s++; + for (; *s && numbers < 11; s++) + { + switch (*s) + { + case '+':case '-':case '.':case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7': case '8': case '9': + number[numbers] = strtod (s, &s); + s--; + numbers++; + } + } + + matrix->m[0][0] = number[0]; + matrix->m[0][1] = number[2]; + matrix->m[0][2] = number[4]; + matrix->m[1][0] = number[1]; + matrix->m[1][1] = number[3]; + matrix->m[1][2] = number[5]; + } + else if (!strncmp (str, "scale", 5)) + { + char *s; + int numbers = 0; + double number[12]={0,0}; + ctx_matrix_identity (matrix); + s = (void*) ctx_strchr (str, '('); + if (!s) + return 0; + s++; + for (; *s; s++) + { + switch (*s) + { + case '+':case '-':case '.':case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7': case '8': case '9': + number[numbers] = strtod (s, &s); + s--; + if (numbers<11) + numbers++; + } + } + if (numbers <= 1) + ctx_matrix_scale (matrix, number[0], number[0]); + else + ctx_matrix_scale (matrix, number[0], number[1]); + } + else if (!strncmp (str, "translate", 5)) + { + char *s; + int numbers = 0; + double number[12]; + ctx_matrix_identity (matrix); + s = (void*) ctx_strchr (str, '('); + if (!s) + return 0; + s++; + for (; *s; s++) + { + switch (*s) + { + case '+':case '-':case '.':case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7': case '8': case '9': + number[numbers] = strtod (s, &s); + s--; + if (numbers < 11) + numbers++; + } + } + ctx_matrix_translate (matrix, number[0], number[1]); + } + else if (!strncmp (str, "rotate", 5)) + { + char *s; + int numbers = 0; + double number[12]; + ctx_matrix_identity (matrix); + s = (void*) ctx_strchr (str, '('); + if (!s) + return 0; + s++; + for (; *s; s++) + { + switch (*s) + { + case '+':case '-':case '.':case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7': case '8': case '9': + number[numbers] = strtod (s, &s); + s--; + if (numbers < 11) + numbers++; + } + } + if (numbers == 3) + { + ctx_matrix_translate (matrix, -number[1], -number[2]); + ctx_matrix_rotate (matrix, number[0] / 360.0 * 2 * M_PI); + ctx_matrix_translate (matrix, number[1], number[2]); + } + else + ctx_matrix_rotate (matrix, number[0] / 360.0 * 2 * M_PI); + } + else + { + //fprintf (stderr, "unhandled transform: %s\n", str); + ctx_matrix_identity (matrix); + return 0; + } + str = strchr (str, ')'); + if (str) { + str++; + while (*str == ' ')str++; + } + } + while (strchr (str, '(')); + return 1; +} + +int +mrg_parse_svg_path (Css *mrg, const char *str) +{ + /* this function is the seed of the ctx parser */ + char command = 'm'; + char *s; + int numbers = 0; + double number[12]; + double pcx, pcy, cx, cy; + + if (!str) + return -1; + + Ctx *ctx = mrg_ctx (mrg); + ctx_parse (ctx, str); + return 0; + //ctx_move_to (ctx, 0, 0); + //ctx_begin_path (ctx); + cx = 0; cy = 0; + pcx = cx; pcy = cy; + + s = (void*)str; +again: + numbers = 0; + + for (; s && *s; s++) + { + switch (*s) + { + case 'z': + case 'Z': + pcx = cx; pcy = cy; + ctx_close_path (ctx); + break; + case 'm': + case 'a': + case 's': + case 'S': + case 'M': + case 'c': + case 'C': + case 'l': + case 'L': + case 'h': + case 'H': + case 'v': + case 'V': + command = *s; + break; + + case '-':case '.':case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7': case '8': case '9': + if (*s == '-') + { + number[numbers] = -strtod (s+1, &s); + s--; + } + else + { + number[numbers] = strtod (s, &s); + s--; + } + if (numbers < 11) + numbers++; + + switch (command) + { + case 'a': + if (numbers == 9) + { + /// XXX: NYI + s++; + goto again; + } + case 'A': + if (numbers == 7) + { + /// XXX: NYI + s++; + goto again; + } + /* fallthrough */ + case 'm': + if (numbers == 2) + { + ctx_rel_move_to (ctx, number[0], number[1]); + cx += number[0]; + cy += number[1]; + pcx = cx; pcy = cy; + s++; + goto again; + } + break; + case 'h': + if (numbers == 1) + { + ctx_rel_line_to (ctx, number[0], 0.0); + cx += number[0]; + pcx = cx; pcy = cy; + s++; + goto again; + } + break; + case 'v': + if (numbers == 1) + { + ctx_rel_line_to (ctx, 0.0, number[0]); + cy += number[0]; + pcx = cx; pcy = cy; + s++; + goto again; + } + break; + case 'l': + if (numbers == 2) + { + ctx_rel_line_to (ctx, number[0], number[1]); + cx += number[0]; + cy += number[1]; + pcx = cx; pcy = cy; + s++; + goto again; + } + break; + case 'c': + if (numbers == 6) + { + ctx_rel_curve_to (ctx, number[0], number[1], + number[2], number[3], + number[4], number[5]); + pcx = cx + number[2]; + pcy = cy + number[3]; + cx += number[4]; + cy += number[5]; + s++; + goto again; + } + break; + case 's': + if (numbers == 4) + { + ctx_curve_to (ctx, 2 * cx - pcx, 2 * cy - pcy, + number[0] + cx, number[1] + cy, + number[2] + cx, number[3] + cy); + pcx = number[0] + cx; + pcy = number[1] + cy; + cx += number[2]; + cy += number[3]; + s++; + goto again; + } + break; + case 'M': + if (numbers == 2) + { + ctx_move_to (ctx, number[0], number[1]); + cx = number[0]; + cy = number[1]; + pcx = cx; pcy = cy; + s++; + goto again; + } + break; + case 'H': + if (numbers == 1) + { + ctx_line_to (ctx, number[0], cy); + cx = number[0]; + pcx = cx; pcy = cy; + s++; + goto again; + } + break; + case 'V': + if (numbers == 1) + { + ctx_line_to (ctx, cx, number[0]); + cy = number[0]; + pcx = cx; pcy = cy; + s++; + goto again; + } + break; + case 'L': + if (numbers == 2) + { + ctx_line_to (ctx, number[0], number[1]); + cx = number[0]; + cy = number[1]; + pcx = cx; pcy = cy; + s++; + goto again; + } + break; + case 'C': + if (numbers == 6) + { + ctx_curve_to (ctx, number[0], number[1], + number[2], number[3], + number[4], number[5]); + pcx = number[2]; + pcy = number[3]; + cx = number[4]; + cy = number[5]; + s++; + goto again; + } + break; + case 'S': + if (numbers == 4) + { + float ax = 2 * cx - pcx; + float ay = 2 * cy - pcy; + ctx_curve_to (ctx, ax, ay, + number[0], number[1], + number[2], number[3]); + pcx = number[0]; + pcy = number[1]; + cx = number[2]; + cy = number[3]; + s++; + goto again; + } + break; + default: + fprintf (stderr, "_%c", *s); + break; + } + break; + default: + break; + } + } + return 0; +} + +static void +mrg_parse_polygon (Css *mrg, const char *str) +{ + Ctx *ctx = mrg_ctx (mrg); + char *s; + int numbers = 0; + int started = 0; + double number[12]; + + if (!str) + return; + //ctx_move_to (ctx, 0, 0); + + s = (void*)str; +again: + numbers = 0; + + for (; *s; s++) + { + switch (*s) + { + case '-':case '.':case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7': case '8': case '9': + number[numbers] = _ctx_parse_float (s, &s); + s--; + if (numbers<11) + numbers++; + + if (numbers == 2) + { + if (started) + ctx_line_to (ctx, number[0], number[1]); + else + { + ctx_move_to (ctx, number[0], number[1]); + started = 1; + } + s++; + goto again; + } + default: + break; + } + } +} + +#if 0 +static void +mrg_parse_ellipse (Css *mrg, const char *str) +{ + Ctx *ctx = mrg_ctx (mrg); + char *s; + int numbers = 0; + int started = 0; + double number[12]; + + if (!str) + return; + //ctx_move_to (ctx, 0, 0); + + s = (void*)str; +again: + numbers = 0; + + for (; *s; s++) + { + switch (*s) + { + case '-':case '.':case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7': case '8': case '9': + number[numbers] = _ctx_parse_float (s, &s); + s--; + if (numbers<11) + numbers++; + + if (numbers == 2) + { + if (started) + ctx_line_to (ctx, number[0], number[1]); + else + { + ctx_move_to (ctx, number[0], number[1]); + started = 1; + } + s++; + goto again; + } + default: + break; + } + } +} +#endif + + +void _mrg_set_wrap_edge_vfuncs (Css *mrg, + float (*wrap_edge_left) (Css *mrg, void *wrap_edge_data), + float (*wrap_edge_right) (Css *mrg, void *wrap_edge_data), + void *wrap_edge_data); + +int mrg_get_contents (Css *mrg, + const char *referer, + const char *input_uri, + char **contents, + long *length); + + +void mrg_text_listen (Css *mrg, CtxEventType types, + CtxCb cb, void *data1, void *data2); + +void mrg_text_listen_full (Css *mrg, CtxEventType types, + CtxCb cb, void *data1, void *data2, + void (*finalize)(void *listen_data, void *listen_data2, void *finalize_data), + void *finalize_data); +void mrg_text_listen_done (Css *mrg); + +char *_mrg_resolve_uri (const char *base_uri, const char *uri); +typedef struct _CssImage CssImage; +struct _CssImage +{ + char *uri; + char *path; + int width; + int height; + CtxBuffer *surface; +}; + +static CtxList *images = NULL; + +static CssImage *_mrg_image (Css *mrg, const char *path) +{ + char *uri = _mrg_resolve_uri (mrg->uri_base, path); + + for (CtxList *l = images; l; l = l->next) + { + CssImage *image = l->data; + if (!strcmp (path, image->uri)) + { + return image; + } + } + + int w = 0, h = 0; +#if 0 + char *p = strchr (uri, ':'); + if (p) + { + if (*p) p++; + if (*p) p++; + if (*p) p++; + } + else p = uri; + ctx_texture_load (mrg->ctx, p, &w, &h, NULL); +#else + ctx_texture_load (mrg->ctx, uri, &w, &h, NULL); +#endif + + if (w) + { + CssImage *image = calloc (sizeof (CssImage), 1); + image->width = w; + image->height = h; + image->uri = strdup (path); + image->path = strdup (uri); + ctx_list_prepend (&images, image); + free (uri); + return _mrg_image (mrg, path); + } + free (uri); + return NULL; +} + +int mrg_query_image (Css *mrg, + const char *path, + int *width, + int *height) +{ + CssImage *image = _mrg_image (mrg, path); + if (image) + { + *width = image->width; + *height = image->height; + return 1; + } + return 0; +} + +void mrg_image (Css *mrg, float x0, float y0, float width, float height, float opacity, const char *path, int *used_width, int *used_height) +{ + CssImage *image = _mrg_image (mrg, path); + if (image) + { + ctx_draw_image (mrg->ctx, image->path, x0, y0, width, height); + } + + +} + +enum +{ + URI_STATE_IN_PROTOCOL = 0, + URI_STATE_IN_HOST, + URI_STATE_IN_PORT, + URI_STATE_E_S1, + URI_STATE_E_S2, + URI_STATE_IN_PATH, + URI_STATE_IN_FRAGMENT, +}; + +int split_uri (char *uri, + char **protocol, + char **host, + char **port, + char **path, + char **fragment) +{ + char *p; + *protocol = + *host = + *port = + *path = + *fragment = NULL; + + if (strstr (uri, "//") || strchr(uri, ':')) + { + int mr = URI_STATE_IN_PROTOCOL; + + if (protocol) + *protocol = uri; + + if (uri[0] == '/' && + uri[1] == '/') + { + mr = URI_STATE_E_S1; + *protocol = NULL; + } + + for (p = uri; *p; p++) + { + switch (mr) + { + case URI_STATE_IN_PROTOCOL: + switch (*p) + { + default: + break; + case ':': + *p = '\0'; + mr = URI_STATE_E_S1; + break; + } + break; + case URI_STATE_E_S1: + switch (*p) + { + default: + mr = URI_STATE_IN_HOST; + if (path) *path = p; + break; + case '/': + mr = URI_STATE_E_S2; + break; + } + break; + case URI_STATE_E_S2: + switch (*p) + { + default: + // XXX ? + break; + case '/': + mr = URI_STATE_IN_HOST; + if (host) *host = p+1; + break; + } + break; + case URI_STATE_IN_HOST: + switch (*p) + { + default: + break; + case ':': + *p = '\0'; + mr = URI_STATE_IN_PORT; + if (port) *port = p+1; + break; + case '/': + *p = '\0'; + mr = URI_STATE_IN_PATH; + if (path) *path = p+1; + break; + } + break; + case URI_STATE_IN_PORT: + switch (*p) + { + default: + break; + case '/': + *p = '\0'; + mr = URI_STATE_IN_PATH; + if (path) *path = p+1; + break; + } + break; + case URI_STATE_IN_PATH: + switch (*p) + { + default: + break; + case '#': + *p = '\0'; + mr = URI_STATE_IN_FRAGMENT; + if (fragment) *fragment = p+1; + break; + } + break; + } + } + } + else + { + + int mr = URI_STATE_IN_HOST; + if (protocol) + *protocol = NULL; + + if (uri[0]=='/') + { + if (host) + *host = NULL; + if (port) + *port = NULL; + *uri = '\0'; + mr = URI_STATE_IN_PATH; + if (path) *path = uri+1; + } + else + { + mr = URI_STATE_IN_PATH; + if (path) *path = uri; + } + + for (p = uri; *p; p++) + { + switch (mr) + { + case URI_STATE_IN_PROTOCOL: + switch (*p) + { + default: + break; + case ':': + *p = '\0'; + mr = URI_STATE_E_S1; + break; + } + break; + case URI_STATE_E_S1: + switch (*p) + { + default: + // XXX ? + break; + case '/': + mr = URI_STATE_E_S2; + break; + } + break; + case URI_STATE_E_S2: + switch (*p) + { + default: + // XXX ? + break; + case '/': + mr = URI_STATE_IN_HOST; + if (host) *host = p+1; + break; + } + break; + case URI_STATE_IN_HOST: + switch (*p) + { + default: + break; + case ':': + *p = '\0'; + mr = URI_STATE_IN_PORT; + if (port) *port = p+1; + break; + case '/': + *p = '\0'; + mr = URI_STATE_IN_PATH; + if (path) *path = p+1; + break; + } + break; + case URI_STATE_IN_PORT: + switch (*p) + { + default: + break; + case '/': + *p = '\0'; + mr = URI_STATE_IN_PATH; + if (path) *path = p+1; + break; + } + break; + case URI_STATE_IN_PATH: + switch (*p) + { + default: + break; + case '#': + *p = '\0'; + mr = URI_STATE_IN_FRAGMENT; + if (fragment) *fragment = p+1; + break; + } + break; + } + } + } + if (*protocol && (*protocol)[0] == 0) + *protocol = NULL; + return 0; +} + +char *_mrg_resolve_uri (const char *base_uri, const char *uri) +{ + char *ret; + char *uri_dup = strdup (uri); + + if (!base_uri) + return uri_dup; + + char *base_dup = strdup (base_uri); + + char *protocol = NULL; + char *host = NULL; + char *port = NULL; + char *path = NULL; + char *fragment = NULL; + char *base_protocol = NULL; + char *base_host = NULL; + char *base_port = NULL; + char *base_path = NULL; + char *base_fragment = NULL; + + //int retlen = 0; + int samehost = 0; + + split_uri (uri_dup, &protocol, &host, &port, &path, &fragment); + split_uri (base_dup, &base_protocol, &base_host, &base_port, &base_path, &base_fragment); + + if (!protocol) + protocol = base_protocol; + if (!host) + { + host = base_host; + port = base_port; + samehost = 1; + } + ret = malloc ( + (path?strlen (path):0) + + (fragment?strlen (fragment):0) + + (host?strlen (host):0) + 640); + if (protocol) + { + if (uri[0] == '/' && uri[1] != '/') + sprintf (ret, "%s://%s%s%s%s", protocol, host, port?":":"", port?port:"", uri); + else if (uri[0] == '.' && uri[1] == '.' && uri[2] == '/' && + uri[3] == '.' && uri[4] == '.') + { + if (strrchr (base_path, '/')) + strrchr (base_path, '/')[1] = 0; + if (base_path[strlen (base_path)-1] == '/') + base_path[strlen (base_path)-1] = 0; + if (strrchr (base_path, '/')) + strrchr (base_path, '/')[1] = 0; + else + base_path[0]=0; + if (strrchr (base_path, '/')) + strrchr (base_path, '/')[1] = 0; + if (base_path[strlen (base_path)-1] == '/') + base_path[strlen (base_path)-1] = 0; + if (strrchr (base_path, '/')) + strrchr (base_path, '/')[1] = 0; + else + base_path[0]=0; + + sprintf (ret, "c%s://%s%s%s/%s%s%s%s", protocol, host, port?":":"", port?port:"", samehost?base_path:"", &path[6], fragment?"#":"", fragment?fragment:""); + } + else if (uri[0] == '.' && uri[1] == '.') + { + if (strrchr (base_path, '/')) + strrchr (base_path, '/')[1] = 0; + if (base_path[strlen (base_path)-1] == '/') + base_path[strlen (base_path)-1] = 0; + if (strrchr (base_path, '/')) + strrchr (base_path, '/')[1] = 0; + else + base_path[0]=0; + + sprintf (ret, "%s://%s%s%s/%s%s%s%s", protocol, host, port?":":"", port?port:"", samehost?base_path:"", &path[3], fragment?"#":"", fragment?fragment:""); + } + else + { + if (strrchr (base_path, '/')) + strrchr (base_path, '/')[1] = 0; + else if (ctx_strchr (base_path, '/')) + ctx_strchr (base_path, '/')[1] = 0; + else + base_path[0] = 0; + + if (host) + sprintf (ret, "%s://%s%s%s/%s%s%s%s", protocol, host, port?":":"", port?port:"", samehost?base_path:"", path, fragment?"#":"", fragment?fragment:""); + else + sprintf (ret, "%s:%s%s%s%s", protocol, samehost?base_path:"", path, fragment?"#":"", fragment?fragment:""); + + } + } + else + { + if (uri[0] == '/') + sprintf (ret, "/%s", path); + else + { + if (strrchr (base_path, '/')) + strrchr (base_path, '/')[1] = 0; + sprintf (ret, "/%s%s", base_path, path); + } + } + + free (uri_dup); + free (base_dup); + return ret; +} + +static float +_ctx_str_get_float (const char *string, int no) +{ + float ret = 0.0f; + int number_no = 0; + const char *s = string; + if (!string) return ret; + + while (*s == ' ')s++; + + while (*s && number_no < no) + { + while ( *s && ((*s >= '0' && *s <= '9') || (*s=='.') || (*s=='-'))) s ++; + number_no ++; + while (*s == ' ')s++; + } + if (*s) + return atof (s); + + return ret; +} + + +void css_xml_render (Css *mrg, + char *uri_base, + void (*link_cb) (CtxEvent *event, void *href, void *link_data), + void *link_data, + void *(finalize)(void *listen_data, void *listen_data2, void *finalize_data), + void *finalize_data, + char *html_) +{ + CssXml *xmltok; + CtxString *svg_text = ctx_string_new (""); + uint32_t tag[CTX_MAX_STATE_DEPTH]; + int pos = 0; + int type = t_none; + static int depth = 0; + int in_style = 0; + int in_defs = 0; + int should_be_empty = 0; + int tagpos = 0; + ItkCssDef *defs = NULL; + + if (mrg->uri_base) + free (mrg->uri_base); + mrg->uri_base = NULL; + if (uri_base) + mrg->uri_base = strdup (uri_base); + + CtxString *style = ctx_string_new (""); + int whitespaces = 0; + uint32_t att = 0; + + +//////////////////////////////////////////////////// + CtxString *str = NULL; + + whitespaces = 0; + att = 0; + pos = 0; +#if 1 + xmltok = xmltok_buf_new (html_); + + ctx_save (mrg->ctx); + while (type != t_eof) + { + char *data = NULL; + type = xmltok_get (xmltok, &data, &pos); + switch (type) + { + case t_word: + case t_whitespace: + if (in_style) + { + ctx_stylesheet_add (mrg, data, uri_base, CTX_STYLE_XML, NULL); + } + break; + case t_att: +#if 0 + for (int i = 0; data[i]; i++) + if (data[i]=='-')data[i]='_'; +#endif + att = ctx_strhash (data); + break; + case t_val: + if (in_defs) + { + ctx_set_string (mrg->ctx, att, data); + } + break; + case t_endtag: + { + int i; + uint32_t data_hash = ctx_strhash (data); + // for (i = 0; data[i]; i++) + // data[i] = tolower (data[i]); + in_style = (data_hash == SQZ_style); + + if (data_hash == SQZ_defs) + { + in_defs = 1; + } + + if (in_defs) + { + + switch (data_hash) + { + case SQZ_stop: + { + const char *offset = ctx_get_string (mrg->ctx, SQZ_offset); + const char *stop_color = ctx_get_string (mrg->ctx, SQZ_stop_color); + const char *stop_opacity = ctx_get_string (mrg->ctx, SQZ_stop_opacity); + + float off = 0.0; + float rgba[4] = {0,0,0,1.0f}; + if (!stop_color) + break; + + if (!strcmp (stop_color, "red")) + { + rgba[0] = 1.0f; + } + else if (!strcmp (stop_color, "gold")) + { + rgba[0] = 1.0f; + rgba[1] = 0.7f; + } + else + { + CtxColor *color = ctx_color_new (); + ctx_color_set_from_string (mrg->ctx, color, stop_color); + ctx_color_get_rgba (ctx_get_state (mrg->ctx), color, rgba); + } + + if (stop_opacity) + { + if (strchr(stop_opacity, '%')) + rgba[3] *= (atof (stop_opacity) / 100.0f); + else + rgba[3] *= (atof (stop_opacity)); + } + + if (offset) + { + if (strchr(offset, '%')) + off = atof (offset) / 100.0f; + else + off = atof (offset); + } + + if (str) + ctx_string_append_printf (str, "addStop %.3f %.3f %.3f %.3f %.3f\n", + off, rgba[0], rgba[1], rgba[2], rgba[3]); + } + break; + case SQZ_radialGradient: + { + const char *id = ctx_get_string (mrg->ctx, SQZ_id); +#define GRAD_PROP_STR(name, def_val) \ + const char *name = def_val;\ + if (ctx_is_set(mrg->ctx, SQZ_##name)) name = PROPS(name); +#define GRAD_PROP_X(name, def_val) \ + float name; const char *str_##name = def_val;\ + if (ctx_is_set(mrg->ctx, SQZ_##name)) str_##name = PROPS(name);\ + name = mrg_parse_px_x (mrg, str_##name, NULL); +#define GRAD_PROP_Y(name, def_val) \ + float name; const char *str_##name = def_val;\ + if (ctx_is_set(mrg->ctx, SQZ_##name)) str_##name = PROPS(name);\ + name = mrg_parse_px_y (mrg, str_##name, NULL); + + + // TODO : gradientUnits='userSpaceOnUse' + // TODO : gradientUnits='objectBoundingBox' (default) + // SQZ_gradientUnits + // SQZ_gradientTransform + // SQZ_spreadMethod = pad, reflect, repeat + // + // SQZ_fy, + // SQZ_fx, + // SQZ_fr, + if (id) + { + GRAD_PROP_STR(gradientUnits, "userSpaceOnUse"); + GRAD_PROP_STR(spreadMethod, "pad"); + const char *transform = ctx_get_string (mrg->ctx, SQZ_gradientTransform); + + GRAD_PROP_X(cx, "50%"); + GRAD_PROP_Y(cy, "50%"); + GRAD_PROP_Y(r, "100%"); + GRAD_PROP_Y(fr, "0%"); + GRAD_PROP_X(fx, "50%"); // XXX should be inherited from cx/cy + GRAD_PROP_Y(fy, "50%"); // not 50% .. + + css_svg_add_def (&defs, ctx_strhash (id)); + + str = css_svg_add_def (&defs, ctx_strhash (id)); + ctx_string_append_printf (str, " radialGradient %f %f %f %f %f %f\n", + cx,cy,r,fx,fy,fr); + + ctx_string_append_printf (str, " rgba "); + if (transform) + { + CtxMatrix matrix; + if (mrg_parse_transform (mrg, &matrix, transform)) + { + ctx_string_append_printf (str, " sourceTransform %f %f %f %f %f %f %f %f %f\n", + matrix.m[0][0], matrix.m[0][1], matrix.m[0][2], + matrix.m[1][0], matrix.m[1][1], matrix.m[1][2], + matrix.m[2][0], matrix.m[2][1], matrix.m[2][2] + ); + } + + } + } + } + break; + case SQZ_linearGradient: + { + const char *id = ctx_get_string (mrg->ctx, SQZ_id); + + if (id) + { + GRAD_PROP_STR(gradientUnits, "userSpaceOnUse"); + GRAD_PROP_STR(spreadMethod, "pad"); + const char *transform = ctx_get_string (mrg->ctx, SQZ_gradientTransform); + GRAD_PROP_X(x1, "0%"); + GRAD_PROP_Y(y1, "0%"); + GRAD_PROP_X(x2, "100%"); + GRAD_PROP_Y(y2, "0%"); + + str = css_svg_add_def (&defs, ctx_strhash (id)); + ctx_string_append_printf (str, " linearGradient %f %f %f %f\n", + x1,y1,x2,y2); + ctx_string_append_printf (str, " rgba "); + if (transform) + { + CtxMatrix matrix; + if (mrg_parse_transform (mrg, &matrix, transform)) + { + ctx_string_append_printf (str, " sourceTransform %f %f %f %f %f %f %f %f %f\n", + matrix.m[0][0], matrix.m[0][1], matrix.m[0][2], + matrix.m[1][0], matrix.m[1][1], matrix.m[1][2], + matrix.m[2][0], matrix.m[2][1], matrix.m[2][2] + ); + } + + } + } + } + break; + } + + } + + if (in_style) + { + if (mrg->css_parse_state) + free (mrg->css_parse_state); + mrg->css_parse_state = NULL; + } + } + break; + case t_tag: + + if (in_defs) + ctx_save (mrg->ctx); + break; + + case t_closetag: + case t_closeemptytag: + { + uint32_t data_hash = ctx_strhash (data); + if (data_hash == SQZ_defs) + in_defs = 0; + if (in_defs) + { + ctx_restore (mrg->ctx); + } + } + break; + default: + break; + } + } + ctx_restore (mrg->ctx); + in_defs = 0; + in_style = 0; + + xmltok_free (xmltok); +#endif + + +//////////////////////////////////////////////////// + + + type = t_none; + whitespaces = 0; + att = 0; + in_style = 0; + xmltok = xmltok_buf_new (html_); + + { + int no = mrg->text_listen_count; + mrg->text_listen_data1[no] = link_data; + mrg->text_listen_data2[no] = html_; + mrg->text_listen_finalize[no] = (void*)finalize; + mrg->text_listen_finalize_data[no] = finalize_data; + mrg->text_listen_count++; + } + + _mrg_set_wrap_edge_vfuncs (mrg, wrap_edge_left, wrap_edge_right, mrg); + mrg->state = &mrg->states[0]; + + + //css_start (mrg, "fjo", NULL); + //ctx_stylesheet_add (mrg, style_sheets->str, uri_base, CTX_STYLE_XML, NULL); + + while (type != t_eof) + { + char *data = NULL; + type = xmltok_get (xmltok, &data, &pos); + + if (type == t_tag || + //type == t_att || + type == t_endtag || + type == t_closeemptytag || + type == t_closetag) + { + int i; + for (i = 0; data[i]; i++) + data[i] = tolower (data[i]); + } + + switch (type) + { + case t_entity: + { + int i; + int dealt_with = 0; + if (data[0]=='#') + { + int c = atoi (&data[1]); + css_printf (mrg, "%c", c); + } + else + { + uint32_t hash = ctx_strhash (data); + for (i = 0; entities[i].name && !dealt_with; i++) + if (hash == entities[i].name) + { + css_print (mrg, entities[i].value); + dealt_with = 1; + } + } + + if (!dealt_with){ + css_start (mrg, "dim", (void*)((size_t)pos)); + css_print (mrg, data); + css_end (mrg, NULL); + } + } + break; + case t_word: + if (in_style) + { + //ctx_stylesheet_add (mrg, data, uri_base, CTX_STYLE_XML, NULL); + } + else + { + if (mrg->in_svg) + { + ctx_string_append_str(svg_text, data); + } + else + css_print (mrg, data); + } + whitespaces = 0; + break; + + case t_whitespace: + if (in_style) + { + //ctx_stylesheet_add (mrg, data, uri_base, CTX_STYLE_XML, NULL); + } + else + { + if (mrg->in_svg) + { + //if (whitespaces == 0) + { + ctx_string_append_str (svg_text, data); + } + whitespaces ++; + } + else + switch (ctx_style (mrg)->white_space) + { + case CTX_WHITE_SPACE_PRE: /* handles as pre-wrap for now */ + case CTX_WHITE_SPACE_PRE_WRAP: + css_print (mrg, data); + break; + case CTX_WHITE_SPACE_PRE_LINE: + switch (*data) + { + case ' ': + whitespaces ++; + if (whitespaces == 1) + css_print (mrg, " "); + break; + case '\n': + whitespaces = 0; + break; + } + break; + case CTX_WHITE_SPACE_NOWRAP: /* XXX: handled like normal, this is bad.. */ + case CTX_WHITE_SPACE_NORMAL: + whitespaces ++; + if (whitespaces == 1) + { + int save = mrg->unresolved_line; + css_print (mrg, " "); + mrg->unresolved_line = save; + } + break; + } + } + break; + case t_tag: + //htmlctx->attributes = 0; + //ctx_save (mrg->ctx); + { + uint32_t data_hash = ctx_strhash (data); + tagpos = pos; + ctx_string_clear (style); + ctx_set_string (mrg->ctx, SQZ_style, ""); + ctx_set_string (mrg->ctx, SQZ_transform, ""); + + if (data_hash == SQZ_html) + { + } + else if (data_hash == SQZ_defs) + { + in_defs = 1; + } + else if (data_hash == SQZ_svg) + { + mrg->in_svg++; + } + } + break; + case t_att: +#if 0 + for (int i = 0; data[i]; i++) + if (data[i]=='-')data[i]='_'; +#endif + att = ctx_strhash (data); + break; + case t_val: + ctx_set_string (mrg->ctx, att, data); + { + uint32_t style_attribute[] ={ + SQZ_fill_rule, + SQZ_font_size, + SQZ_font_family, + SQZ_fill_color, + SQZ_fill, + SQZ_stroke_width, + SQZ_stroke_color, + SQZ_stroke_linecap, + SQZ_stroke_miterlimit, + SQZ_stroke_linejoin, + SQZ_stroke, + // SQZ_viewBox, // SQZ_version + SQZ_color, + SQZ_background_color, + SQZ_background, + SQZ_text_anchor, + 0}; + char *style_attribute_names[] ={ + "fill-rule", + "font-size", + "font-family", + "fill-color", + "fill", + "stroke-width", + "stroke-color", + "stroke-linecap", + "stroke-miterlimit", + "stroke-linejoin", + "stroke", + //"viewBox", + "color", + "background-color", + "background", + "text-anchor", + 0}; + + int j; + for (j = 0; style_attribute[j]; j++) + if (att == style_attribute[j]) + { + ctx_string_append_printf (style, "%s: %s;", + style_attribute_names[j], data); + break; + } + } + break; + case t_endtag: + { + uint32_t data_hash = ctx_strhash (data); +#if 0 + int prev_is_self_closing = 0; + + if (depth) switch (tag[depth-1]) + { + case SQZ_p: + case SQZ_li: + case SQZ_dt: + case SQZ_dd: + case SQZ_option: + case SQZ_thead: + case SQZ_tbody: + case SQZ_head: + case SQZ_tfoot: + case SQZ_colgroup: + case SQZ_th: + prev_is_self_closing = 1; + break; + } + + if (prev_is_self_closing) + { + int is_block = 0; + switch (data_hash) + { + case SQZ_tr: + case SQZ_li: + case SQZ_p: + case SQZ_div: + //case SQZ_td: + is_block = 1; + break; + } + if (is_block) + { + css_end (mrg, NULL); + depth--; + } + + } + +#else + + if (depth && (data_hash == SQZ_tr && tag[depth-1] == SQZ_td)) + { + css_end (mrg, NULL); + depth--; + css_end (mrg, NULL); + depth--; + } + if (depth && (data_hash == SQZ_tr && tag[depth-1] == SQZ_td)) + { + css_end (mrg, NULL); + depth--; + css_end (mrg, NULL); + depth--; + } + else if (depth && ((data_hash == SQZ_dd && tag[depth-1] == SQZ_dt) || + (data_hash == SQZ_li && tag[depth-1] == SQZ_li) || + (data_hash == SQZ_dt && tag[depth-1] == SQZ_dd) || + (data_hash == SQZ_td && tag[depth-1] == SQZ_td) || + (data_hash == SQZ_tr && tag[depth-1] == SQZ_tr) || + (data_hash == SQZ_dd && tag[depth-1] == SQZ_dd) || + (data_hash == SQZ_p && tag[depth-1] == SQZ_p))) + { + css_end (mrg, NULL); + depth--; + } +#endif + + tag[depth] = data_hash; + depth ++; + depth = ctx_mini(depth, CTX_MAX_STATE_DEPTH-1); + + { + char combined[512]=""; + char *klass = (char*)PROPS(class); + /* XXX: spaces in class should be turned into .s making + * it possible to use multiple classes + */ + const char *id = PROPS(id); + + const char *pseudo = ""; + + if (klass) + { + klass = strdup (klass); + if(1){ + int i; + for (i = 0; klass[i]; i++) + if (klass[i] == ' ') + klass[i]='.'; + } + } + + combined[511]=0; + snprintf (combined, 511, "%s%s%s%s%s%s", + data, + klass?".":"", + klass?klass:"", + id?"#":"", + id?id:"", pseudo); + + //fprintf (stderr,"[%s]", combined); + if (klass) + free (klass); + /* collect XML attributes and convert into CSS declarations */ + ctx_string_append_str (style, PROPS(style)); + css_start_with_style (mrg, combined, (void*)((size_t)tagpos), style->str); + } + + if (data_hash == SQZ_g) + { + const char *transform; + if ((transform = PROPS(transform))) + { + CtxMatrix matrix; + if (mrg_parse_transform (mrg, &matrix, transform)) + ctx_apply_matrix (mrg_ctx (mrg), &matrix); + } + } + + else if (data_hash == SQZ_svg) + { + const char *vbox = PROPS(viewbox); + if (vbox && 0) + { + float x = _ctx_str_get_float (vbox, 0); + float y = _ctx_str_get_float (vbox, 1); + float width = _ctx_str_get_float (vbox, 2); + float height = _ctx_str_get_float (vbox, 3); + //fprintf (stderr, "viewBox:%s %f %f %f %f\n", vbox, x, y, width, height); + ctx_view_box (mrg->ctx, x, y, width, height); + } + } + + else if (data_hash == SQZ_polygon) + { + const char *transform; + if ((transform = PROPS(transform))) + { + CtxMatrix matrix; + if (mrg_parse_transform (mrg, &matrix, transform)) + ctx_apply_matrix (mrg_ctx (mrg), &matrix); + } + mrg_parse_polygon (mrg, PROPS(d)); + ctx_close_path (mrg->ctx); + mrg_path_fill_stroke (mrg, &defs); + } + else if (data_hash == SQZ_polyline) + { + const char *transform; + if ((transform = PROPS(transform))) + { + CtxMatrix matrix; + if (mrg_parse_transform (mrg, &matrix, transform)) + ctx_apply_matrix (mrg_ctx (mrg), &matrix); + } + mrg_parse_polygon (mrg, PROPS(d)); + mrg_path_fill_stroke (mrg, &defs); + } + + else if (data_hash == SQZ_path) + { + const char *transform; + ctx_begin_path (mrg_ctx (mrg)); // XXX: eeeek! + if ((transform = PROPS(transform))) + { + CtxMatrix matrix; + if (mrg_parse_transform (mrg, &matrix, transform)) + ctx_apply_matrix (mrg_ctx (mrg), &matrix); + } + mrg_parse_svg_path (mrg, PROPS(d)); + mrg_path_fill_stroke (mrg, &defs); + } + + else if (data_hash == SQZ_line) + { + const char *transform; + if ((transform = PROPS(transform))) + { + CtxMatrix matrix; + if (mrg_parse_transform (mrg, &matrix, transform)) + ctx_apply_matrix (mrg_ctx (mrg), &matrix); + } + // SQZ_x1 + // SQZ_y1 + // SQZ_x2 + // SQZ_y2 + ctx_move_to (mrg->ctx, PROP(x1), PROP(y1)); + ctx_line_to (mrg->ctx, PROP(x2), PROP(y2)); + mrg_path_fill_stroke (mrg, &defs); + } + else if (data_hash == SQZ_ellipse) + { + const char *transform; + if ((transform = PROPS(transform))) + { + CtxMatrix matrix; + if (mrg_parse_transform (mrg, &matrix, transform)) + ctx_apply_matrix (mrg_ctx (mrg), &matrix); + } + ctx_save (mrg->ctx); + ctx_translate (mrg->ctx, PROP(cx), PROP(cy)); + ctx_scale (mrg->ctx, PROP(rx), PROP(ry)); + ctx_arc (mrg->ctx, 0.0f, 0.0f, 1.0f, 0.0f, M_PI*2, 0); + ctx_restore (mrg->ctx); + mrg_path_fill_stroke (mrg, &defs); + } + + else if (data_hash == SQZ_circle) + { + const char *transform; + if ((transform = PROPS(transform))) + { + CtxMatrix matrix; + if (mrg_parse_transform (mrg, &matrix, transform)) + ctx_apply_matrix (mrg_ctx (mrg), &matrix); + } + ctx_arc (mrg->ctx, PROP(cx), PROP(cy), PROP(r), 0.0f, M_PI*2.0f, 0); + mrg_path_fill_stroke (mrg, &defs); + } + + else if (data_hash == SQZ_rect && !in_defs) + { + float width = PROP(width); + float height = PROP(height); + float x = PROP(x); + float y = PROP(y); + float rx = PROP(rx); + + const char *transform; + if ((transform = PROPS(transform))) + { + CtxMatrix matrix; + if (mrg_parse_transform (mrg, &matrix, transform)) + ctx_apply_matrix (mrg_ctx (mrg), &matrix); + } + if (rx > 0.001f) + ctx_round_rectangle (mrg_ctx (mrg), x, y, width, height, rx); + else + ctx_rectangle (mrg_ctx (mrg), x, y, width, height); + mrg_path_fill_stroke (mrg, &defs); + } + + else if (data_hash == SQZ_text) + { + ctx_string_set (svg_text, ""); + } + + if (data_hash == SQZ_a) + { + if (link_cb && ctx_is_set_now (mrg->ctx, SQZ_href)) + mrg_text_listen_full (mrg, CTX_CLICK, link_cb, _mrg_resolve_uri (uri_base, ctx_get_string (mrg->ctx, SQZ_href)), link_data, (void*)free, NULL); //XXX: free is not invoked according to valgrind + } + + else if (data_hash == SQZ_style) + { + in_style = 1; +#if 1 + if (mrg->css_parse_state) + free (mrg->css_parse_state); + mrg->css_parse_state = NULL; +#endif + } + else + in_style = 0; + + should_be_empty = 0; + + if (data_hash == SQZ_link) + { + const char *rel; + if ((rel=PROPS(rel)) && !strcmp (rel, "stylesheet") && ctx_is_set_now (mrg->ctx, SQZ_href)) + { + char *contents; + long length; + mrg_get_contents (mrg, uri_base, ctx_get_string (mrg->ctx, SQZ_href), &contents, &length); + if (contents) + { + ctx_stylesheet_add (mrg, contents, uri_base, CTX_STYLE_XML, NULL); + free (contents); + } + } + } + + if (data_hash == SQZ_img && ctx_is_set_now (mrg->ctx, SQZ_src)) + { + int img_width, img_height; + const char *src = ctx_get_string (mrg->ctx, SQZ_src); + + if (mrg_query_image (mrg, src, &img_width, &img_height)) + { + float width = PROP(width); + float height = PROP(height); + + if (width < 1) + { + width = img_width; + } + if (height < 1) + { + height = img_height *1.0 / img_width * width; + } + + mrg->y += height; + + mrg_image (mrg, + mrg->x, + mrg->y - height, + width, + height, + 1.0f, + src, NULL, NULL); + + mrg->x += width; + } + else + { + css_printf (mrg, "![%s]", src); + } + } +#if 1 + switch (data_hash) + { + case SQZ_link: + case SQZ_meta: + case SQZ_input: + case SQZ_img: + case SQZ_br: + case SQZ_hr: + should_be_empty = 1; + css_end (mrg, NULL); + depth--; + } +#endif + } + break; + + case t_closeemptytag: + case t_closetag: + if (!should_be_empty) + { + uint32_t data_hash = ctx_strhash (data); + if (!strcmp (data, "a")) + { + mrg_text_listen_done (mrg); + } + in_style = 0; + css_end (mrg, NULL); + //ctx_restore (mrg->ctx); + depth--; + + if (data_hash == SQZ_defs) + { + in_defs = 0; + } + else if (data_hash == SQZ_svg) + { + mrg->in_svg--; + } + else if (data_hash == SQZ_text) + { + ctx_move_to (mrg->ctx, PROP(x), PROP(y)); + + //fprintf (stderr, "%f %f\n", PROP(text_align), PROP(text_anchor)); + ctx_font_size (mrg->ctx, PROP(font_size)); + ctx_text (mrg->ctx, svg_text->str); + } + + if (depth<0)depth=0; // XXX +#if 1 + if (tag[depth] != data_hash) + { + if (tag[depth] == SQZ_p) + { + css_end (mrg, NULL); + depth --; + } else + if (depth > 0 && tag[depth-1] == data_hash) + { + css_end (mrg, NULL); + depth --; + } + else if (depth > 1 && tag[depth-2] == data_hash) + { + int i; + for (i = 0; i < 2; i ++) + { + depth --; + css_end (mrg, NULL); + //nctx_restore (mrg->ctx); + } + } +#if 0 + else if (depth > 2 && tag[depth-3] == data_hash) + { + int i; + fprintf (stderr, "%i: fixing close of %s when %s wass open\n", pos, data, tag[depth]); + + for (i = 0; i < 3; i ++) + { + depth --; + css_end (mrg, NULL); + // ctx_restore (mrg->ctx); + } + } + else if (depth > 3 && tag[depth-3] == data_hash) + { + int i; + fprintf (stderr, "%i: fixing close of %s when %s wasss open\n", pos, data, tag[depth]); + + for (i = 0; i < 4; i ++) + { + depth --; + css_end (mrg, NULL); + //ctx_restore (mrg->ctx); + } + } + else if (depth > 4 && tag[depth-5] == data_hash) + { + int i; + fprintf (stderr, "%i: fixing close of %s when %s wassss open\n", pos, data, tag[depth]); + + for (i = 0; i < 5; i ++) + { + depth --; + css_end (mrg, NULL); + } + } +#endif + else + { + if (data_hash == SQZ_table && tag[depth] == SQZ_td) + { + depth--; + css_end (mrg, NULL); + depth--; + css_end (mrg, NULL); + } + else if (data_hash == SQZ_table && tag[depth] == SQZ_tr) + { + depth--; + css_end (mrg, NULL); + } + } + } +#endif + } + break; + } + } + //css_end (mrg, NULL); + + xmltok_free (xmltok); + + if (depth!=0){ + //fprintf (stderr, "xml parsing unbalanced, %i open tags.. \n", depth); + while (depth > 0) + { + //fprintf (stderr, " %s ", ctx_str_decode (tag[depth-1])); + css_end (mrg, NULL); + depth--; + } + //fprintf (stderr, "\n"); + } + + ctx_string_free (style, 1); + ctx_string_free (svg_text, 1); + + if (mrg->absolute_ctx) + { + ctx_render_ctx (mrg->absolute_ctx, mrg->ctx); + ctx_destroy (mrg->absolute_ctx); + } + if (mrg->fixed_ctx) + { + ctx_render_ctx (mrg->fixed_ctx, mrg->ctx); + ctx_destroy (mrg->fixed_ctx); + } + + while (defs) + { + ItkCssDef *temp =defs; + ctx_string_free (temp->str, 1); + defs = temp->next; + ctx_free (temp); + } +} + +int css_xml_extent (Css *mrg, uint8_t *contents, float *width, float *height, float *vb_x, float *vb_y, float *vb_width, float *vb_height) +{ + CssXml *xmltok; + int pos = 0; + int type = t_none; + int in_style = 0; + int in_defs = 0; + int tagpos = 0; + + float c_width = 0.0f; + float c_height = 0.0f; + + int in_svg = 0; +//////////////////////////////////////////////////// + + type = t_none; + unsigned int att = 0; + in_style = 0; + xmltok = xmltok_buf_new ((char*)contents); + + while (type != t_eof) + { + char *data = NULL; + type = xmltok_get (xmltok, &data, &pos); + + if (type == t_tag || + //type == t_att || + type == t_endtag || + type == t_closeemptytag || + type == t_closetag) + { + int i; + for (i = 0; data[i]; i++) + data[i] = tolower (data[i]); + } + + switch (type) + { + case t_tag: + //htmlctx->attributes = 0; + //ctx_save (mrg->ctx); + { + uint32_t data_hash = ctx_strhash (data); + tagpos = pos; + + in_svg = 0; + if (data_hash == SQZ_html) + { + } + else if (data_hash == SQZ_svg) + { + in_svg = 1; + } + } + break; + case t_att: +#if 0 + for (int i = 0; data[i]; i++) + if (data[i]=='-')data[i]='_'; +#endif + att = ctx_strhash (data); + + break; + case t_val: + if (in_svg && att == SQZ_viewBox) + { + if(vb_x) *vb_x = _ctx_str_get_float (data, 0); + if(vb_y) *vb_y = _ctx_str_get_float (data, 1); + if(vb_width) *vb_width = _ctx_str_get_float (data, 2); + if(vb_height) *vb_height = _ctx_str_get_float (data, 3); + } + else if (in_svg && att == SQZ_width) + { + c_width = mrg_parse_px_x (mrg, data, NULL); + if(width) *width = c_width; + } + else if (in_svg && att == SQZ_height) + { + c_height = mrg_parse_px_y (mrg, data, NULL); + if(height) *height = c_height; + } + break; + case t_endtag: + in_svg = 0; + break; + } + } + + xmltok_free (xmltok); + + if (vb_width && (*vb_width == 0.0f)){ + if (vb_x) *vb_x = 0; + *vb_width = c_width; + } + if (vb_height && (*vb_height == 0.0f)){ + if (vb_y) *vb_y = 0; + *vb_height = c_height; + } + if (width && (*width == 0.0f) && vb_width) + { + *width = *vb_width; + } + if (height && (*height == 0.0f) && vb_height) + { + *height = *vb_height; + } + + return 0; +} + +void css_xml_renderf (Css *mrg, + char *uri_base, + void (*link_cb) (CtxEvent *event, void *href, void *link_data), + void *link_data, + char *format, + ...) +{ + va_list ap; + size_t needed; + char *buffer; + va_start(ap, format); + needed = vsnprintf(NULL, 0, format, ap) + 1; + buffer = malloc(needed); + va_end (ap); + va_start(ap, format); + vsnprintf(buffer, needed, format, ap); + va_end (ap); + css_xml_render (mrg, uri_base, link_cb, link_data, NULL, NULL, buffer); + free (buffer); +} +void css_init (Css *mrg, Ctx *ctx, int width, int height); + +void css_print_xml (Css *mrg, const char *xml) +{ + css_xml_render (mrg, NULL, NULL, NULL, NULL, NULL, (char*)xml); +} + +void +css_printf_xml (Css *mrg, const char *format, ...) +{ + va_list ap; + size_t needed; + char *buffer; + va_start(ap, format); + needed = vsnprintf(NULL, 0, format, ap) + 1; + buffer = malloc(needed); + va_end (ap); + va_start(ap, format); + vsnprintf(buffer, needed, format, ap); + va_end (ap); + css_print_xml (mrg, buffer); + free (buffer); +} + +void mrg_set_size (Css *mrg, int width, int height) +{ + if (ctx_width (mrg->ctx) != width || + ctx_height (mrg->ctx) != height) + { + ctx_set_size (mrg->ctx, width, height); + mrg_queue_draw (mrg, NULL); + } +} + +int +_mrg_file_get_contents (const char *path, + char **contents, + long *length) +{ + FILE *file; + long size; + long remaining; + char *buffer; + + file = fopen (path,"rb"); + + if (!file) + return -1; + + if (!strncmp (path, "/proc", 4)) + { + buffer = calloc(2048, 1); + *contents = buffer; + *length = fread (buffer, 1, 2047, file); + buffer[*length] = 0; + return 0; + } + else + { + fseek (file, 0, SEEK_END); + *length = size = remaining = ftell (file); + rewind (file); + buffer = malloc(size + 8); + } + + if (!buffer) + { + fclose(file); + return -1; + } + + remaining -= fread (buffer, 1, remaining, file); + if (remaining) + { + fclose (file); + free (buffer); + return -1; + } + fclose (file); + *contents = buffer; + buffer[size] = 0; + return 0; +} + +#if 0 +static int +_mr_get_contents (const char *referer, + const char *uri, + char **contents, + long *length) +{ + char *uri_dup; + char *protocol = NULL; + char *host = NULL; + char *port = NULL; + char *path = NULL; + char *fragment = NULL; + uint32_t protocol_hash; +#if 0 + if (!strncmp (uri, "mrg:", 4)) + { + return _mrg_internal_get_contents (referer, uri, contents, length); + } +#endif + + uri_dup = strdup (uri); + split_uri (uri_dup, &protocol, &host, &port, &path, &fragment); + protocol_hash = protocol?ctx_strhash (protocol):0; +#if 0 + if (protocol && protocol_hash == SQZ_http) + { + int len; + char *pathdup = malloc (strlen (path) + 2); + pathdup[0] = '/'; + strcpy (&pathdup[1], path); + // fprintf (stderr, "%s %i\n",host, port?atoi(port):80); + char *cont = _mrg_http (NULL, host, port?atoi(port):80, pathdup, &len); + *contents = cont; + *length = len; + //fprintf (stderr, "%s\n", cont); + // + fprintf (stderr, "%i\n", len); + free (uri_dup); + return 0; + } else +#endif + if (protocol && protocol_hash == SQZ_file) + { + char *path2 = malloc (strlen (path) + 2); + int ret; + sprintf (path2, "/%s", path); + ret = ctx_get_contents (path2, (uint8_t**)contents, length); + + free (path2); + free (uri_dup); + fprintf (stderr, "a%i\n", (int)*length); + return ret; + } + else + { + char *c = NULL; + long l = 0; + int ret; + free (uri_dup); + ret = _mrg_file_get_contents (uri, &c, &l); + if (contents) *contents = c; + if (length) *length = l; + return ret; + } + + return -1; +} +#endif + + +typedef struct _CacheEntry { + char *uri; + long length; + char *contents; +} CacheEntry; + +static CtxList *cache = NULL; + + + +/* caching uri fetcher + */ +int +mrg_get_contents_default (const char *referer, + const char *input_uri, + char **contents, + long *length, + void *ignored_user_data) +{ + Css *mrg = ignored_user_data; + char *uri = _mrg_resolve_uri (mrg->uri_base, input_uri); +#if 0 // without caching + int ret = 0; + ctx_get_contents (uri, (uint8_t**)contents, length); + free (uri); + return *contents!=NULL; +#else + /* should resolve before mrg_get_contents */ + //fprintf (stderr, "%s %s\n", uri, input_uri); + + for (CtxList *i = cache; i; i = i->next) + { + CacheEntry *entry = i->data; + if (!strcmp (entry->uri, uri)) + { + if (!entry->contents) + { + *contents = NULL; + if (length) *length = 0; + free (uri); + return -1; + } + + *contents = malloc (entry->length + 1); + memcpy (*contents, entry->contents, entry->length); + (*contents)[entry->length]=0; + free (uri); + if (length) + *length = entry->length; + if (*length) + { + return 0; + } + else + { + return -1; + } + } + } + + { + CacheEntry *entry = calloc (sizeof (CacheEntry), 1); + char *c = NULL; + long l = 0; + + entry->uri = uri; + + ctx_get_contents (uri, (unsigned char**)&c, &l); + + if (c){ + entry->contents = c; + entry->length = l; + } else + { + entry->contents = NULL; + entry->length = 0; + } + ctx_list_prepend (&cache, entry); + +#if MRG_URI_LOG + if (c) + fprintf (stderr, "%li\t%s\n", l, uri); + else + fprintf (stderr, "FAIL\t%s\n", uri); +#endif + } + + int ret = mrg_get_contents_default (referer, uri, contents, length, ignored_user_data); + return ret; +#endif +} + +void css_init (Css *mrg, Ctx *ctx, int width, int height) +{ + //memset (mrg, 0, sizeof (Css)); + mrg->do_clip = 1; + mrg->ctx = mrg->document_ctx = ctx; + _ctx_events_init (mrg->ctx); + mrg->state_no = 0; + mrg->line_level=0; + mrg->line_max_height[0]=0.0f; + mrg->state = &mrg->states[mrg->state_no]; + mrg->state->float_base = 0; + + mrg->floats = 0; +#if 0 + mrg->state->children = 0; + mrg->state->overflowed = 0; + mrg->state->span_bg_started = 0; +#endif + //memset (mrg->state, 0, sizeof (CssState)); + //memset (mrg->states, 0, sizeof (mrg->states)); + /* XXX: is there a better place to set the default text color to black? */ +#if 0 + mrg->state->style.color.red = + mrg->state->style.color.green = + mrg->state->style.color.blue = 0; + mrg->state->style.color.alpha = 1; +#endif + if(1){ + CtxColor *color = ctx_color_new (); + ctx_color_set_rgba (ctx_get_state (mrg->ctx), color, 1, 1, 1, 1); + ctx_set_color (mrg->ctx, SQZ_color, color); + ctx_color_free (color); + } + + mrg->ddpx = 1; + if (getenv ("MRG_DDPX")) + { + mrg->ddpx = strtod (getenv ("MRG_DDPX"), NULL); + } + mrg_set_size (mrg, width, height); + _mrg_text_init (mrg); + + if (!mrg->style) + mrg->style = ctx_string_new (""); + + mrg_set_mrg_get_contents (mrg, mrg_get_contents_default, mrg); + if (mrg->style_global) + ctx_string_free (mrg->style_global, 1); + mrg->style_global = ctx_string_new (""); + + + if(0){ + const char *global_css_uri = "mrg:theme.css"; + + if (getenv ("MRG_CSS")) + global_css_uri = getenv ("MRG_CSS"); + + char *contents; + long length; + mrg_get_contents (mrg, NULL, global_css_uri, &contents, &length); + if (contents) + { + ctx_string_set (mrg->style_global, contents); + free (contents); + } + } + + css_stylesheet_clear (mrg); + _mrg_clear_text_closures (mrg); +} + +#if 0 +Css *mrg_new (Ctx *ctx, int width, int height) +{ + Css *mrg; + + mrg = calloc (sizeof (Css), 1); + mrg->do_clip = 1; + css_init (mrg, ctx, width, height); + ctx_style_defaults (mrg); + +#if 0 + printf ("%f %i %i\n", mrg->state->style.font_size, mrg_width(mrg), mrg_height(mrg)); + printf ("sizeof(Css) %li (was: 1142496)\n", sizeof(Css)); + printf ("sizeof(CssState) %li\n", sizeof(CssState)); + printf ("sizeof(CtxStyle) %li\n", sizeof(CtxStyle)); + printf ("sizeof(CssHtml) %li\n", sizeof(CssHtml)); + printf ("sizeof(CtxCssParseState) %li\n", sizeof(CtxCssParseState)); +#endif + + return mrg; +} +#endif + +void mrg_destroy (Css *mrg) +{ + if (mrg->edited_str) + ctx_string_free (mrg->edited_str, 1); + if (mrg->stylesheet) + ctx_list_free (&mrg->stylesheet); + if (mrg->style_global) + ctx_string_free (mrg->style_global, 1); + if (mrg->style) + ctx_string_free (mrg->style, 1); + if (mrg->css_parse_state) + free (mrg->css_parse_state); + mrg->edited_str = NULL; + free (mrg); +} + +typedef struct _UiChoice UiChoice; +struct _UiChoice +{ + int val; + char *label; +}; + +void css_begin_menu_bar (Css *itk, const char *title) +{ + if (itk->menu_path) + free (itk->menu_path); + itk->menu_path = title?strdup (title):NULL; +} + +void css_begin_menu (Css *itk, const char *title) +{ + char *tmp = malloc (strlen (title) + (itk->menu_path?strlen (itk->menu_path):0) + 2); + sprintf (tmp, "%s/%s", itk->menu_path?itk->menu_path:"", title); + if (itk->menu_path) + free (itk->menu_path); + itk->menu_path = tmp; + if (css_button (itk, title)) + { + if (itk->active_menu_path) free (itk->active_menu_path); + itk->active_menu_path = strdup (itk->menu_path); + }; +} + +void css_menu_item (Css *itk, const char *title) +{ + char *tmp = malloc (strlen (title) + (itk->menu_path?strlen (itk->menu_path):0) + 2); + sprintf (tmp, "%s/%s", itk->menu_path?itk->menu_path:"", title); + //fprintf (stderr, "[%s]\n", tmp); + free (tmp); +} + +void css_end_menu (Css *itk) +{ + if (itk->menu_path) + { + char *split = strrchr (itk->menu_path, '/'); + if (split) *split = 0; + } +} + +void css_end_menu_bar (Css *itk) +{ + css_newline (itk); +} + +static char *css_style=NULL; + +const char *css_style_string (const char *name) +{ + if (!css_style) + return NULL; + char *p = css_style; + static char ret[64]; + int name_len = strlen (name); + while (p && *p) + { + while (*p == ' ')p++; + if (!strncmp (p, name, name_len)) + { + if (p[name_len]==':') + { + for (int i = 2; p[name_len+i] && (p[name_len+i] != ';') + && (p[name_len+i] != '\n'); i++) + { + ret[i-2]=p[name_len+i]; + ret[i-1]=0; + } + return ret; + } + } + else + { + p = strchr (p, '\n'); + if (p) p++; + } + } + return NULL; +} + +float css_style_float (char *name) +{ + const char *str = css_style_string (name); + if (str) + { + return atof (str); + } + return 0.0f; +} + +#if 1 +void css_style_color (Ctx *ctx, const char *name) +{ + const char *str = css_style_string (name); + if (str) + { + while (*str == ' ')str++; + ctx_color (ctx, str); + //ctx_stroke_source (ctx); + //ctx_color (ctx, str); + } + else + { + ctx_rgb (ctx, 0, 0, 0); // XXX : this shows up in thumbnails + //ctx_rgb_stroke (ctx, 1, 0, 1); + } +} +#endif + +static void css_style_color3 (Css *itk, const char *klass, uint32_t attr) +{ + Ctx *ctx = itk->ctx; + CtxStyleNode ancestor; + CtxStyleNode *ancestry[2] = {&ancestor, NULL}; + memset(&ancestor, 0, sizeof (CtxStyleNode)); + ancestry[0]->element_hash = ctx_strhash ("div"); + ancestry[0]->classes_hash[0] = ctx_strhash (klass); + // XXX : fix this casting hack, some stack waste is better then type mismatch? + char *collated = _ctx_css_compute_style (itk, ancestry, 1); + ctx_save (itk->ctx); + css_set_style (itk, collated); + CtxColor *color = ctx_color_new (); + ctx_get_color (ctx, attr, color); + ctx_restore (itk->ctx); + mrg_ctx_set_source_color (ctx, color); + ctx_color_free (color); + free (collated); +} + +void css_style_bg (Css *itk, const char *klass) +{ + css_style_color3 (itk, klass, SQZ_background_color); +} + +void css_style_fg (Css *itk, const char *klass) +{ + css_style_color3 (itk, klass, SQZ_color); +} + +float css_rel_ver_advance (Css *itk) +{ + return itk->rel_ver_advance; +} + +Css *css_new (Ctx *ctx) +{ + Css *itk = calloc (sizeof (Css), 1); + //itk->ctx = ctx; + //itk->panels = NULL; + itk->focus_wraparound = 1; + itk->scale = 1.0; + itk->font_size = getenv("CSS_FONT_SIZE")?atoi(getenv("CSS_FONT_SIZE")):ctx_get_font_size(ctx); + itk->label_width = 0.5; + itk->rel_vmargin = 0.5; + itk->rel_hmargin = 0.5; + itk->rel_ver_advance = 1.2; + itk->menu_path = strdup ("main/foo"); + itk->rel_hpad = 0.3; + itk->rel_vgap = 0.2; + itk->scroll_speed = 1.0/8.0; + itk->light_mode = 1; + ctx_queue_draw (ctx); + if (ctx_backend_type (ctx) == CTX_BACKEND_TERM) + { + itk->scale = 1.0; + itk->font_size = 3; + itk->rel_vgap = 0.0; + itk->rel_ver_advance = 1.0; + itk->rel_hpad = 0.0; + } + itk->width = itk->font_size * 15; + + Css *mrg = (Css*)itk; + css_init (mrg, ctx, ctx_width(ctx), ctx_height(ctx)); + ctx_style_defaults (mrg); + + //printf ("%f %i %i\n", mrg->state->style.font_size, mrg_width(mrg), mrg_height(mrg)); + + return itk; +} + +float css_em (Css *itk) +{ + return itk->font_size * itk->scale; +} + +void css_destroy (Css *itk) +{ + if (itk->menu_path) + free (itk->menu_path); + if (itk->stylesheet) + ctx_list_free (&itk->stylesheet); + if (itk->style_global) + ctx_string_free (itk->style_global, 1); + if (itk->style) + ctx_string_free (itk->style, 1); + if (itk->css_parse_state) + free (itk->css_parse_state); + + + free (itk); +} + +static inline void control_ref (CtxControl *control) +{ + control->ref_count ++; +} + +static inline void control_unref (CtxControl *control) +{ + if (control->ref_count <= 0) + { + CtxControl *w = control; + + if (w->label) + free (w->label); + if (w->fallback) + free (w->fallback); + if (w->entry_value) + free (w->entry_value); + + free (w); + return; + } + control->ref_count--; +} + +void control_finalize (void *control, void *foo, void *bar) +{ + control_unref (control); +} + +void css_reset (Css *itk) +{ + Ctx *ctx = itk->ctx = itk->document_ctx; + ctx_start_frame (ctx); + + if (css_style) + free (css_style); + unsigned char *style = NULL; +#if CTX_FONTS_FROM_FILE + //ctx_get_contents ("/tmp/itk-style", &style, NULL); +#endif + if (style) + { + css_style = (void*)style; + } + else + { + css_style = strdup ( +"\n" +"itk-font-size: 32.0;\n" + +"titlebar-bg: #0007;\n" +"titlebar-fg: #999a;\n" +"titlebar-close: #fff9;\n" +"titlebar-focused-close: #c44;\n" +"titlebar-focused-bg: #333b;\n" +"titlebar-focused-fg: #ffff;\n" +"\n" +"terminal-bg: #000f;\n" +"terminal-bg-reverse: #ddde;\n" +"terminal-active-bg: #000b;\n" +"terminal-active-bg-reverse: #dddb;\n" +"\n" +"itk-bg: rgba(30,40,50, 1.0);\n" +"itk-fg: rgb(225,225,225);\n" +"\n" + ); + + } + + ctx_save (ctx); + ctx_font (ctx, "Regular"); + ctx_font_size (ctx, css_em (itk)); + + itk->next_flags = CSS_FLAG_DEFAULT; + itk->panel = NULL; + + while (itk->old_controls) + { + CtxControl *control = itk->old_controls->data; + control_unref (control); + ctx_list_remove (&itk->old_controls, control); + } + itk->old_controls = itk->controls; + itk->controls = NULL; + while (itk->choices) + { + UiChoice *choice = itk->choices->data; + ctx_list_remove (&itk->choices, choice); + free (choice->label); + free (choice); + } + itk->control_no = 0; + css_init ((Css*)itk, ctx, ctx_width (itk->ctx), ctx_height (itk->ctx)); + //mrg_clear (itk); + ctx_clear_bindings (itk->ctx); + //css_stylesheet_clear ((Css*)itk); + ctx_style_defaults ((Css*)itk); +} + +CssPanel *add_panel (Css *itk, const char *label, float x, float y, float width, float height) +{ + CssPanel *panel; + for (CtxList *l = itk->panels; l; l = l->next) + { + CssPanel *panel = l->data; + if (!strcmp (panel->title, label)) + return panel; + } + panel = calloc (sizeof (CssPanel), 1); + panel->title = strdup (label); + panel->x = x; + panel->y = y; + panel->width = width; + panel->height = height; + ctx_list_prepend (&itk->panels, panel); + + itk->panel = panel; + return panel; +} + +void +css_panels_reset_scroll (Css *itk) +{ + if (!itk || !itk->panels) + return; + for (CtxList *l = itk->panels; l; l = l->next) + { + CssPanel *panel = l->data; + panel->scroll = 0.0; + panel->do_scroll_jump = 1; + } +} + + +/* adds a control - should be done before the drawing of the + * control itself - as this call might draw a highlight in + * the background. + * + * This also allocats a runtime control, used for focus handling + * and ephemreal persistance of possible interaction states - + * useful for accesibility. + */ +CtxControl *css_add_control (Css *itk, + int type, + const char *label, + float x, float y, + float width, float height) +{ + CtxControl *control = calloc (sizeof (CtxControl), 1); + float em = css_em (itk); + control->flags = itk->next_flags; + itk->next_flags = CSS_FLAG_DEFAULT; + control->label = strdup (label); + if (itk->next_id) + { + control->id = itk->next_id; + itk->next_id = NULL; + } + + control->type = type; + control->ref_count=0; + control->x = x; + control->y = y; + control->no = itk->control_no; + itk->control_no++; + control->width = width; + control->height = height; + ctx_list_prepend (&itk->controls, control); + + if (itk->focus_no == control->no && itk->focus_no >= 0) + { + if (itk->y - itk->panel->scroll < em * 2) + { + if (itk->panel->scroll != 0.0f) + { + itk->panel->scroll -= itk->scroll_speed * itk->panel->height * (itk->panel->do_scroll_jump?5:1); + if (itk->panel->scroll<0.0) + itk->panel->scroll=0.0; + ctx_queue_draw (itk->ctx); + } + } + else if (itk->y - itk->panel->scroll + control->height > itk->panel->y + itk->panel->height - em * 2 && control->height < itk->panel->height - em * 2) + { + itk->panel->scroll += itk->scroll_speed * itk->panel->height * (itk->panel->do_scroll_jump?5:1); + + ctx_queue_draw (itk->ctx); + } + else + { + itk->panel->do_scroll_jump = 0; + } + + } + + //ctx_rectangle (itk->ctx, x, y, width, height); + if (itk->focus_no == control->no && + control->type == UI_LABEL) // own-bg + { +#if 1 + //css_style_bg (itk, "focused"); + //ctx_fill (itk->ctx); +#if 0 + ctx_rectangle (itk->ctx, x, y, width, height); + css_style_color (itk->ctx, "itk-fg"); + ctx_line_width (itk->ctx, 2.0f); + ctx_stroke (itk->ctx); +#endif +#endif + } + else + { + if (control->flags & CSS_FLAG_ACTIVE) + if (control->type != UI_LABEL && // no-bg + control->type != UI_BUTTON) // own-bg + { + // css_style_bg (itk, "interactive"); + // ctx_fill (itk->ctx); + } + } + + switch (control->type) + { + case UI_SLIDER: + control->ref_count++; + break; + default: + break; + } + + return control; +} + +#if 0 +static void css_base (Css *itk, const char *label, float x, float y, float width, float height, int focused) +{ + Ctx *ctx = itk->ctx; + if (focused) + { + css_style_color (itk->ctx, "itk-focused-bg"); + + ctx_rectangle (ctx, x, y, width, height); + ctx_fill (ctx); + } +#if 0 + else + css_style_color (itk->ctx, "itk-bg"); + + if (itk->line_no >= itk->lines_drawn) + { + ctx_rectangle (ctx, x, y, width, height); + ctx_fill (ctx); + itk->lines_drawn = itk->line_no -1; + } +#endif +} +#endif + +void css_newline (Css *itk) +{ + itk->y += css_em (itk) * (itk->rel_ver_advance + itk->rel_vgap); + itk->x = itk->edge_left; + itk->line_no++; +} + +void css_seperator (Css *itk) +{ + Css *mrg = (Css*)itk; + css_start (mrg, "hr", NULL); + css_end (mrg, NULL); +} + +void css_label (Css *itk, const char *label) +{ + Css *mrg = (Css*)itk; + //css_start (mrg, "label", NULL); + css_print (mrg, label); + //css_end (mrg, NULL); +} + +void css_labelf (Css *itk, const char *format, ...) +{ + va_list ap; + size_t needed; + char *buffer; + va_start (ap, format); + needed = vsnprintf (NULL, 0, format, ap) + 1; + buffer = malloc (needed); + va_end (ap); + va_start (ap, format); + vsnprintf (buffer, needed, format, ap); + va_end (ap); + css_label (itk, buffer); + free (buffer); +} + +static void titlebar_drag (CtxEvent *event, void *userdata, void *userdata2) +{ + Css *itk = userdata2; + CssPanel *panel = userdata; + +#if 1 + //fprintf (stderr, "%d %f %f\n", event->delta_x, event->delta_y); + panel->x += event->delta_x; + panel->y += event->delta_y; + if (panel->y < 0) panel->y = 0; +#else + panel->x = event->x - panel->width / 2; + panel->y = event->y; +#endif + + event->stop_propagate = 1; + ctx_queue_draw (itk->ctx); +} + +void css_titlebar (Css *itk, const char *label) +{ + Ctx *ctx = itk->ctx; + float em = css_em (itk); + + //CtxControl *control = css_add_control (itk, UI_TITLEBAR, label, itk->x, itk->y, itk->width, em * itk->rel_ver_advance); + + ctx_rectangle (ctx, itk->x, itk->y, itk->width, em * itk->rel_ver_advance); + ctx_listen_with_finalize (ctx, CTX_DRAG, titlebar_drag, itk->panel, itk, NULL, NULL); + + ctx_begin_path (ctx); + itk->line_no = 0; + itk->lines_drawn = 0; + //css_base (itk, label, control->x, control->y, control->width - em * itk->rel_hmargin, em * itk->rel_ver_advance, itk->focus_no == control->no); + css_label (itk, label); + //itk->lines_drawn = 1; + + css_newline (itk); +} + +void css_scroll_start (Css *itk, float height) +{ + CssPanel *panel = itk->panel; + Ctx *ctx = itk->ctx; + ctx_save (ctx); + itk->panel->scroll_start_y = itk->y; + ctx_rectangle (ctx, itk->edge_left - itk->rel_hmargin*css_em(itk), itk->y, panel->width, panel->height - (itk->y - panel->y)); + ctx_clip (ctx); + ctx_begin_path (ctx); + ctx_translate (ctx, 0.0, -panel->scroll); +} + +// applies to next created control +void css_id (Css *itk, void *id) +{ + itk->next_id = id; +} + +// applies to next created control +void css_set_flag (Css *itk, int flag, int on) +{ + if (on) + { + itk->next_flags |= flag; + } + else + { + if (itk->next_flags & flag) + itk->next_flags -= flag; + } +} + +void css_scroll_drag (CtxEvent *event, void *data, void *data2) +{ + Css *itk = data; + CssPanel *panel = data2; + float scrollbar_height = panel->height - (panel->scroll_start_y - panel->y); + float th = scrollbar_height * (scrollbar_height / (panel->max_y-panel->scroll_start_y)); + if (th > scrollbar_height) + { + panel->scroll = 0; + event->stop_propagate = 1; + ctx_queue_draw (itk->ctx); + return; + } + panel->scroll = ((event->y - panel->scroll_start_y - th / 2) / (scrollbar_height-th)) * + (panel->max_y - panel->scroll_start_y - scrollbar_height) + ; + itk->focus_no = -1; + ctx_queue_draw (itk->ctx); + + if (panel->scroll < 0) panel->scroll = 0; + + event->stop_propagate = 1; +} + +void css_scroll_end (Css *itk) +{ + CssPanel *panel = itk->panel; + Ctx *ctx = itk->ctx; + float em = css_em (itk); + ctx_restore (ctx); + itk->panel->max_y = itk->y; + + float scrollbar_height = panel->height - (panel->scroll_start_y - panel->y); + float scrollbar_width = em; + +#if 1 + ctx_begin_path (ctx); + ctx_rectangle (ctx, panel->x + panel->width- scrollbar_width, + panel->scroll_start_y, + scrollbar_width, + scrollbar_height); + ctx_listen (ctx, CTX_DRAG, css_scroll_drag, itk, panel); + css_style_bg (itk, "scroll"); + ctx_fill (ctx); +#endif + + ctx_begin_path (ctx); + float th = scrollbar_height * (scrollbar_height / (panel->max_y-panel->scroll_start_y)); + if (th > scrollbar_height) th = scrollbar_height; + + ctx_rectangle (ctx, panel->x + panel->width- scrollbar_width, + panel->scroll_start_y + + (panel->scroll / (panel->max_y-panel->scroll_start_y)) * ( scrollbar_height ), + scrollbar_width, + th + + ); + + css_style_fg (itk, "scroll"); + ctx_fill (ctx); + + +} + +CssPanel *css_panel_start (Css *itk, const char *title, + int x, int y, int width, int height) +{ + Ctx *ctx = itk->ctx; + CssPanel *panel = add_panel (itk, title, x, y, width, height); + float em = css_em (itk); + css_set_edge_left (itk, panel->x + em * itk->rel_hmargin); + css_set_edge_top (itk, panel->y); + css_set_xy (itk, css_edge_left (itk), panel->y); + //itk->edge_left = itk->x = panel->x + em * itk->rel_hmargin; + //itk->edge_top = itk->y = panel->y; + + if (panel->width != 0) + { + panel->width = width; + panel->height = height; + } + itk->width = panel->width; + itk->height = panel->height; + css_set_wrap_width (itk, panel->width - em); + //css_set_height (itk, panel->height); + + itk->panel = panel; + + css_style_fg (itk, "itk"); + ctx_begin_path (ctx); + ctx_rectangle (ctx, panel->x, panel->y, panel->width, panel->height); + ctx_line_width (ctx, 2); + ctx_stroke (ctx); + + css_style_bg (itk, "wallpaper"); + ctx_rectangle (ctx, panel->x, panel->y, panel->width, panel->height); + ctx_fill (ctx); + + if (title[0]) + css_titlebar (itk, title); + + css_scroll_start (itk, panel->height - (itk->y - panel->y)); + css_start (itk, "div", NULL); + return panel; +} + +void css_panel_resize_drag (CtxEvent *event, void *data, void *data2) +{ + CssPanel *panel = data; + panel->width += event->delta_x; + panel->height += event->delta_y; + ctx_queue_draw (event->ctx); + event->stop_propagate = 1; +} + +void css_panel_end (Css *itk) +{ + Ctx *ctx = itk->ctx; + CssPanel *panel = itk->panel; + float em = css_em (itk); + css_end ((Css*)itk, NULL); + css_scroll_end (itk); + + ctx_rectangle (ctx, panel->x + panel->width - em, + panel->y + panel->height - em, + em, + em); + ctx_listen (ctx, CTX_DRAG, css_panel_resize_drag, panel, itk); + css_style_fg (itk, "wallpaper"); + ctx_begin_path (ctx); + ctx_move_to (ctx, panel->x + panel->width, + panel->y + panel->height); +#if 1 + ctx_rel_line_to (ctx, -em, 0); + ctx_rel_line_to (ctx, em, -em); + ctx_rel_line_to (ctx, 0, em); +#endif + ctx_fill (ctx); + + itk->panel = NULL; +} + +static void css_float_constrain (CtxControl *control, float *val) +{ + float new_val = *val; + if (new_val < control->min) new_val = control->min; + if (new_val > control->max) new_val = control->max; + if (new_val > 0) + { + if (control->step > 0.0) + { + new_val = (int)(new_val / control->step) * control->step; + } + } + else + { + if (control->step > 0.0) + { + new_val = -new_val; + new_val = (int)(new_val / control->step) * control->step; + new_val = -new_val; + } + } + *val = new_val; +} + +#if 0 +static void css_slider_cb_drag (CtxEvent *event, void *userdata, void *userdata2) +{ + Css *itk = userdata2; + CtxControl *control = userdata; + float new_val; + + css_set_focus_no (itk, control->no); + event->stop_propagate = 1; + ctx_queue_draw (event->ctx); + new_val = ((event->x - control->x) / (control->width)) * (control->max-control->min) + control->min; + + css_float_constrain (control, &new_val); + + itk->return_value = 1; + control->value = new_val; + itk->slider_value = new_val; + //if (control->set_val) + // control->set_val (control->val, new_val, control->data); +} +#endif + +float css_slider (Css *itk, const char *label, float value, double min, double max, double step) +{ +#if 0 + Ctx *ctx = itk->ctx; + char buf[100] = ""; + float em = css_em (itk); + + float new_x = itk->x + (itk->label_width) * itk->width; + itk->x = new_x; + + CtxControl *control = css_add_control (itk, UI_SLIDER, label, itk->x, itk->y, itk->width * (1.0 - itk->label_width) - em * 1.5, em * itk->rel_ver_advance); + //control->data = data; + // + control->value = value; + control->min = min; + control->max = max; + control->step = step; + + if (itk->focus_no == control->no) + css_style_color (itk->ctx, "itk-focused-bg"); + else + css_style_color (itk->ctx, "itk-interactive-bg"); + ctx_rectangle (ctx, itk->x, itk->y, control->width, em * itk->rel_ver_advance); + ctx_fill (ctx); + control_ref (control); + ctx_rectangle (ctx, itk->x, itk->y, control->width, em * itk->rel_ver_advance); + ctx_listen_with_finalize (ctx, CTX_DRAG, css_slider_cb_drag, control, itk, control_finalize, NULL); + ctx_begin_path (ctx); + + double fval = value; + + if (step == 1.0) + { + sprintf (buf, "%.0f", fval); + } + else + { + sprintf (buf, "%.3f", fval); + } + css_style_color (itk->ctx, "itk-slider-text"); + ctx_text (ctx, buf); + + float rel_val = ((fval) - min) / (max-min); + css_style_color (itk->ctx, "itk-slider-cursor"); + ctx_rectangle (ctx, itk->x + control->width * rel_val, itk->y, em/8, control->height); + ctx_fill (ctx); + ctx_rectangle (ctx, itk->x, itk->y + em*5/6, control->width, em/8); + ctx_fill (ctx); + + itk->x += (1.0 - itk->label_width) * itk->width; + css_newline (itk); + + if (control->no == itk->focus_no && itk->return_value) + { + itk->return_value = 0; + ctx_queue_draw (ctx); + return itk->slider_value; + } + return value; +#else + Css *mrg = (Css*)itk; + Ctx *ctx = itk->ctx; + + if (itk->focus_no == itk->control_no) + css_start (mrg, "propline:focused", NULL); + else + css_start (mrg, "propline", NULL); + css_label (itk, label); + + CtxFloatRectangle extent; + css_start (mrg, "slider", NULL); + css_printf (mrg, "%f", value); + css_end (mrg, &extent); + CtxControl *control = css_add_control (itk, UI_SLIDER, label, + extent.x, extent.y, extent.width, extent.height); + control->value = value; + control->min = min; + control->max = max; + control->step = step; + css_end (mrg, NULL); + + if (control->no == itk->focus_no && itk->return_value) + { + itk->return_value = 0; + ctx_queue_draw (ctx); + return itk->slider_value; + } + return value; +#endif +} + +void css_slider_float (Css *itk, const char *label, float *val, float min, float max, float step) +{ + *val = css_slider (itk, label, *val, min, max, step); +} + +void css_slider_int (Css *itk, const char *label, int *val, int min, int max, int step) +{ + *val = css_slider (itk, label, *val, min, max, step); +} + +void css_slider_double (Css *itk, const char *label, double *val, double min, double max, double step) +{ + *val = css_slider (itk, label, *val, min, max, step); +} + +void css_slider_uint8 (Css *itk, const char *label, uint8_t *val, uint8_t min, uint8_t max, uint8_t step) +{ + *val = css_slider (itk, label, *val, min, max, step); +} + +void css_slider_uint16 (Css *itk, const char *label, uint16_t *val, uint16_t min, uint16_t max, uint16_t step) +{ + *val = css_slider (itk, label, *val, min, max, step); +} + +void css_slider_uint32 (Css *itk, const char *label, uint32_t *val, uint32_t min, uint32_t max, uint32_t step) +{ + *val = css_slider (itk, label, *val, min, max, step); +} + +void css_slider_int8 (Css *itk, const char *label, int8_t *val, int8_t min, int8_t max, int8_t step) +{ + *val = css_slider (itk, label, *val, min, max, step); +} + +void css_slider_int16 (Css *itk, const char *label, int16_t *val, int16_t min, int16_t max, int16_t step) +{ + *val = css_slider (itk, label, *val, min, max, step); +} + +void css_slider_int32 (Css *itk, const char *label, int32_t *val, int32_t min, int32_t max, int32_t step) +{ + *val = css_slider (itk, label, *val, min, max, step); +} + +CtxControl *css_find_control (Css *itk, int no) +{ + for (CtxList *l = itk->controls; l; l=l->next) + { + CtxControl *control = l->data; + if (control->no == no) + return control; + } + return NULL; +} + + +void css_entry_commit (Css *itk) +{ + if (itk->active_entry<0) + return; + + for (CtxList *l = itk->controls; l; l=l->next) + { + CtxControl *control = l->data; + if (control->no == itk->active_entry) + { + switch (control->type) + { + case UI_ENTRY: + if (itk->entry_copy) + { + //fprintf (stderr, "ec %i %s\n", itk->active_entry, itk->entry_copy); + // strcpy (control->val, itk->entry_copy); + // free (itk->entry_copy); + // itk->entry_copy = NULL; + itk->active = 2; + ctx_queue_draw (itk->ctx); + } + } + return; + } + } +} + +void css_lost_focus (Css *itk) +{ + for (CtxList *l = itk->controls; l; l=l->next) + { + CtxControl *control = l->data; + if (control->no == itk->active_entry) + { + switch (control->type) + { + case UI_ENTRY: + if (itk->active_entry < 0) + return; + if (control->flags & CSS_FLAG_CANCEL_ON_LOST_FOCUS) + { + itk->active_entry = -1; + itk->active = 0; + } + else if (itk->entry_copy) + { + itk->active = 2; + } + ctx_queue_draw (itk->ctx); + break; + default : + itk->active = 0; + itk->choice_active = 0; + ctx_queue_draw (itk->ctx); + break; + } + return; + } + } +} + +void entry_clicked (CtxEvent *event, void *userdata, void *userdata2) +{ + Css *itk = userdata2; + CtxControl *control = userdata; + event->stop_propagate = 1; + + if (itk->active) + { + css_lost_focus (itk); + } + else + { + itk->entry_copy = strdup (control->entry_value); + itk->entry_pos = strlen (itk->entry_copy); + itk->active = 1; + itk->active_entry = control->no; + } + + css_set_focus_no (itk, control->no); + ctx_queue_draw (event->ctx); +} + + +char *css_entry (Css *itk, + const char *label, + const char *fallback, + const char *in_val) +{ + Css *mrg = (Css*)itk; +#if 1 + if (itk->focus_no == itk->control_no) + css_start (mrg, "propline:focused", NULL); + else + css_start (mrg, "propline", NULL); + css_label (itk, label); + + if (itk->active && + itk->entry_copy && itk->focus_no == itk->control_no) + { + int backup = itk->entry_copy[itk->entry_pos]; + char buf[4]="|"; + itk->entry_copy[itk->entry_pos]=0; + css_label (itk, itk->entry_copy); + css_label (itk, buf); + + buf[0]=backup; + if (backup) + { + css_label (itk, buf); + css_label (itk, &itk->entry_copy[itk->entry_pos+1]); + itk->entry_copy[itk->entry_pos] = backup; + } + } + else + { + if (in_val[0]) + { + css_label (itk, in_val); + } + else + { + if (fallback) + { + css_label (itk, fallback); + } + } + } + + CtxFloatRectangle extent; + css_end (mrg, &extent); + CtxControl *control = css_add_control (itk, UI_ENTRY, label, + extent.x, extent.y, extent.width, extent.height); + control->entry_value = strdup (in_val); + if (fallback) + control->fallback = strdup (fallback); + control->ref_count++; +#else + float new_x = itk->x + itk->label_width * itk->width; + + float ewidth = itk->width * (1.0 - itk->label_width); + + if (label[0]) { + css_label (itk, label); + itk->x = new_x; + } + else + { + ewidth = itk->width; + } + CtxControl *control = css_add_control (itk, UI_ENTRY, label, itk->x, itk->y, ewidth, em * itk->rel_ver_advance); + control->entry_value = strdup (in_val); + if (fallback) + control->fallback = strdup (fallback); + control->ref_count++; + + ctx_rectangle (ctx, itk->x, itk->y, itk->width, em * itk->rel_ver_advance); + + if (control->flags & CSS_FLAG_ACTIVE) + { + ctx_listen_with_finalize (ctx, CTX_CLICK, entry_clicked, control, itk, control_finalize, NULL); + control_ref (control); + } + + ctx_begin_path (ctx); + ctx_move_to (ctx, itk->x, itk->y + em); + if (itk->active && + itk->entry_copy && itk->focus_no == control->no) + { + int backup = itk->entry_copy[itk->entry_pos]; + char buf[4]="|"; + itk->entry_copy[itk->entry_pos]=0; + css_style_color (itk->ctx, "itk-interactive"); + ctx_text (ctx, itk->entry_copy); + css_style_color (itk->ctx, "itk-entry-cursor"); + ctx_text (ctx, buf); + + buf[0]=backup; + if (backup) + { + css_style_color (itk->ctx, "itk-interactive"); + ctx_text (ctx, buf); + ctx_text (ctx, &itk->entry_copy[itk->entry_pos+1]); + itk->entry_copy[itk->entry_pos] = backup; + } + } + else + { + if (in_val[0]) + { + if (control->flags & CSS_FLAG_ACTIVE) + css_style_color (itk->ctx, "itk-interactive"); + else + css_style_color (itk->ctx, "itk-fg"); + ctx_text (ctx, in_val); + } + else + { + if (control->fallback) + { + css_style_color (itk->ctx, "itk-entry-fallback"); + ctx_text (ctx, control->fallback); + } + } + } + itk->x += ewidth; + css_newline (itk); +#endif + + if (itk->active == 2 && control->no == itk->active_entry) + { + itk->active = 0; + itk->active_entry = -1; + char *copy = itk->entry_copy; + itk->entry_copy = NULL; + ctx_queue_draw (itk->ctx); // queue a draw - since it is likely the + // response triggers computation + return copy; + } + return NULL; +} + +int css_entry_str_len (Css *itk, const char *label, const char *fallback, char *val, int maxlen) +{ + char *new_val; + if ((new_val = css_entry (itk, label, fallback, val))) + { + if ((int)strlen (new_val) > maxlen -1) + new_val[maxlen-1]=0; + strcpy (val, new_val); + free (new_val); + return 1; + } + return 0; +} + +void toggle_clicked (CtxEvent *event, void *userdata, void *userdata2) +{ + Css *itk = userdata2; + CtxControl *control = userdata; + int *val = control->val; + itk->return_value = 1; // reusing despite name + *val = (*val)?0:1; + event->stop_propagate = 1; + css_set_focus_no (itk, control->no); + ctx_queue_draw (event->ctx); +} + +static void button_clicked (CtxEvent *event, void *userdata, void *userdata2) +{ + Css *itk = userdata2; + CtxControl *control = userdata; + event->stop_propagate = 1; + ctx_queue_draw (event->ctx); + css_set_focus_no (itk, control->no); + itk->return_value = 1; +} + +int css_toggle (Css *itk, const char *label, int input_val) +{ + Ctx *ctx = itk->ctx; + Css *mrg = (Css*)itk; + //float em = css_em (itk); + //float width = ctx_text_width (ctx, label) + em * itk->rel_hpad * 2; + CtxFloatRectangle extent; + + if (itk->focus_no == itk->control_no) + css_start (mrg, "propline:focused", NULL); + else + css_start (mrg, "propline", NULL); + css_start (mrg, "toggle", NULL); + + css_print (mrg, label); + + if (input_val) + { + css_print (mrg, "[x]"); + } + else + { + css_print (mrg, "[ ]"); + } + + css_end (mrg, &extent); + css_end (mrg, NULL); + CtxControl *control = css_add_control (itk, UI_TOGGLE, label, + extent.x, extent.y, extent.width, extent.height); + + control_ref (control); + control->type = UI_TOGGLE; + ctx_rectangle (ctx, extent.x, extent.y, extent.width, extent.height); + ctx_listen_with_finalize (ctx, CTX_CLICK, button_clicked, control, itk, control_finalize, NULL); + ctx_begin_path (ctx); + + if (control->no == itk->focus_no && itk->return_value) + { + itk->return_value = 0; + ctx_queue_draw (ctx); + return !input_val; + } + return input_val; +} + + +int css_radio (Css *itk, const char *label, int set) +{ + Ctx *ctx = itk->ctx; + Css *mrg = (Css*)itk; + //float em = css_em (itk); + //float width = ctx_text_width (ctx, label) + em * itk->rel_hpad * 2; + CtxFloatRectangle extent; + + if (itk->focus_no == itk->control_no) + css_start (mrg, "propline:focused", NULL); + else + css_start (mrg, "propline", NULL); + css_start (mrg, "toggle", NULL); + + css_print (mrg, label); + + if (set) + { + css_print (mrg, "(x)"); + } + else + { + css_print (mrg, "( )"); + } + + css_end (mrg, &extent); + css_end (mrg, NULL); + CtxControl *control = css_add_control (itk, UI_TOGGLE, label, + extent.x, extent.y, extent.width, extent.height); + + control_ref (control); + control->type = UI_TOGGLE; + ctx_rectangle (ctx, extent.x, extent.y, extent.width, extent.height); + ctx_listen_with_finalize (ctx, CTX_CLICK, button_clicked, control, itk, control_finalize, NULL); + ctx_begin_path (ctx); + + if (control->no == itk->focus_no && itk->return_value) + { + itk->return_value = 0; + ctx_queue_draw (ctx); + return !set; + } + return set; +} + +void expander_clicked (CtxEvent *event, void *userdata, void *userdata2) +{ + Css *itk = userdata2; + CtxControl *control = userdata; + int *val = control->val; + *val = (*val)?0:1; + css_set_focus_no (itk, control->no); + ctx_queue_draw (event->ctx); +} + +int css_expander (Css *itk, const char *label, int *val) +{ + Css *mrg = (Css*)itk; + CtxFloatRectangle extent; + if (itk->focus_no == itk->control_no) + css_start (mrg, "propline:focused", NULL); + else + css_start (mrg, "propline", NULL); + + css_labelf (itk, "%s %s", *val?"V":">", label); + + css_end (mrg, &extent); + CtxControl *control = css_add_control (itk, UI_EXPANDER, label, + extent.x, extent.y, extent.width, extent.height); + control->val = val; + + return *val; +} + +int css_button (Css *itk, const char *label) +{ +#if 1 + Ctx *ctx = itk->ctx; + Css *mrg = (Css*)itk; + //float em = css_em (itk); + //float width = ctx_text_width (ctx, label) + em * itk->rel_hpad * 2; + CtxFloatRectangle extent; + + css_start (mrg, itk->focus_no == itk->control_no ? "button:focused" : "button", NULL); + +// css_label (itk, label); + css_print (mrg, label); + + + css_end (mrg, &extent); + CtxControl *control = css_add_control (itk, UI_BUTTON, label, + extent.x, extent.y, extent.width, extent.height); + //itk->x, itk->y, width, em * itk->rel_ver_advance); + + control_ref (control); + control->type = UI_BUTTON; + ctx_rectangle (ctx, extent.x, extent.y, extent.width, extent.height); + ctx_listen_with_finalize (ctx, CTX_CLICK, button_clicked, control, itk, control_finalize, NULL); + ctx_rgb (ctx, 1,1,0); + ctx_fill (ctx); + ctx_begin_path (ctx); + + //css_newline (itk); + if (control->no == itk->focus_no && itk->return_value) + { + itk->return_value = 0; + ctx_queue_draw (ctx); + return 1; + } + return 0; +#else + + Ctx *ctx = itk->ctx; + float em = css_em (itk); + float width = ctx_text_width (ctx, label) + em * itk->rel_hpad * 2; + + + + CtxControl *control = css_add_control (itk, UI_BUTTON, label, + itk->x, itk->y, width, em * itk->rel_ver_advance); + + css_style_color (itk->ctx, "itk-button-shadow"); + ctx_begin_path (ctx); + ctx_round_rectangle (ctx, itk->x + em * 0.1, itk->y + em * 0.1, width, em * itk->rel_ver_advance, em*0.33); + ctx_fill (ctx); + + { + float px = ctx_pointer_x (itk->ctx); + float py = ctx_pointer_y (itk->ctx); + if (px >= control->x && px <= control->x + control->width && + py >= control->y && py <= control->y + control->height) + { + css_style_color (itk->ctx, "itk-button-hover-bg"); + } + else + { + if (itk->focus_no == control->no) + css_style_color (itk->ctx, "itk-button-focused-bg"); + else + css_style_color (itk->ctx, "itk-interactive-bg"); + } + } + + ctx_round_rectangle (ctx, itk->x, itk->y, width, em * itk->rel_ver_advance, em * 0.33); + ctx_fill (ctx); + + + css_style_color (itk->ctx, "itk-button-fg"); + ctx_move_to (ctx, itk->x + em * itk->rel_hpad, itk->y + em * itk->rel_baseline); + ctx_text (ctx, label); + + control_ref (control); + control->type = UI_BUTTON; + ctx_rectangle (ctx, itk->x, itk->y, width, em * itk->rel_ver_advance); + ctx_listen_with_finalize (ctx, CTX_CLICK, button_clicked, control, itk, control_finalize, NULL); + ctx_begin_path (ctx); + +// css_newline (itk); + if (control->no == itk->focus_no && itk->return_value) + { + itk->return_value = 0; + ctx_queue_draw (ctx); + return 1; + } + return 0; + +#endif +} + +static void css_choice_clicked (CtxEvent *event, void *userdata, void *userdata2) +{ + Css *itk = userdata2; + CtxControl *control = userdata; + itk->choice_active = 1; + itk->choice_no = control->value; + css_set_focus_no (itk, control->no); + event->stop_propagate = 1; + ctx_queue_draw (event->ctx); +} + +int css_choice (Css *itk, const char *label, int val) +{ + Ctx *ctx = itk->ctx; + Css *mrg = (Css*)itk; + //float em = css_em (itk); + //float width = ctx_text_width (ctx, label) + em * itk->rel_hpad * 2; + CtxFloatRectangle extent; + + if (itk->focus_no == itk->control_no) + css_start (mrg, "propline:focused", NULL); + else + css_start (mrg, "propline", NULL); + + for (CtxList *l = itk->choices; l; l=l?l->next:NULL) + { + UiChoice *choice = l->data; + if (choice->val == val) + css_printf (mrg, "%s %s", label, choice->label); + } + + //css_end (mrg, NULL); + css_end (mrg, &extent); + CtxControl *control = css_add_control (itk, UI_CHOICE, label, + extent.x, extent.y, extent.width, extent.height); + control->value = val; + control_ref (control); + + + ctx_rectangle (ctx, extent.x, extent.y, extent.width, extent.height); + ctx_listen_with_finalize (ctx, CTX_CLICK, css_choice_clicked, control, itk, control_finalize, NULL); + ctx_begin_path (ctx); + if (itk->focus_no == itk->control_no-1) + { + Css *mrg = (Css*)itk; + if (itk->choice_active) + { + css_start (mrg, "div.choice_menu_wrap", NULL); + css_start (mrg, "div.choice_menu", NULL); + + for (CtxList *l = itk->choices; l; l=l?l->next:NULL) + { + UiChoice *choice = l->data; + if (((int)control->value) == choice->val) + css_start (mrg, "div.choice:chosen", NULL); + else + css_start (mrg, "div.choice", NULL); + css_print (mrg, choice->label); + css_end (mrg, NULL); + } + css_end (mrg, NULL); + css_end (mrg, NULL); + } + if (!itk->choice_active) + { + itk->choice_no = val; + } + else + { + control->value = val; + } + itk->popup_x = control->x; + itk->popup_y = control->y + (itk->panel?-itk->panel->scroll:0); + itk->popup_width = control->width; + itk->popup_height = control->height; + if (itk->return_value) + { + itk->return_value = 0; + ctx_queue_draw (itk->ctx); + return itk->choice_no; + } + } + + return val; +} + +void css_choice_add (Css *itk, int value, const char *label) +{ + UiChoice *choice= calloc (sizeof (UiChoice), 1); + choice->val = value; + choice->label = strdup (label); + ctx_list_append (&itk->choices, choice); +} + + +void css_set_focus_no (Css *itk, int pos) +{ + if (itk->focus_no != pos) + { + itk->focus_no = pos; + + css_lost_focus (itk); + ctx_queue_draw (itk->ctx); + } +} + +CtxControl *css_hovered_control(Css *itk) +{ + float px = ctx_pointer_x (itk->ctx); + float py = ctx_pointer_y (itk->ctx); + for (CtxList *l = itk->controls; l; l=l->next) + { + CtxControl *control = l->data; + if (px >= control->x && px <= control->x + control->width && + py >= control->y && py <= control->y + control->height) + { + return control; + } + } + return NULL; +} + +CtxControl *css_focused_control(Css *itk) +{ + for (CtxList *l = itk->controls; l; l=l->next) + { + CtxControl *control = l->data; + if (control->no == itk->focus_no) + { + return control; + } + } + + + return NULL; +} + +#define CSS_DIRECTION_PREVIOUS -1 +#define CSS_DIRECTION_NEXT 1 +#define CSS_DIRECTION_LEFT 2 +#define CSS_DIRECTION_RIGHT 3 +#define CSS_DIRECTION_UP 4 +#define CSS_DIRECTION_DOWN 5 + +void css_focus (Css *itk, int dir) +{ + css_lost_focus (itk); + if (itk->focus_no < 0) + { + itk->focus_no = 0; + return; + } + + if (dir == CSS_DIRECTION_PREVIOUS || + dir == CSS_DIRECTION_NEXT) + { + itk->focus_no += dir; + + int n_controls = ctx_list_length (itk->controls); + CtxList *iter = ctx_list_nth (itk->controls, n_controls-itk->focus_no-1); + if (iter) + { + CtxControl *control = iter->data; + itk->focus_no = control->no; + } + else + { + if (itk->focus_wraparound) + { + if (itk->focus_no > 1) + itk->focus_no = 0; + else + itk->focus_no = itk->control_no - 1; + } + else + { + if (itk->focus_no <= 1) + itk->focus_no = 0; + else + itk->focus_no = itk->control_no - 1; + } + } + // XXX no control means inifinie loop? + CtxControl *control = + css_focused_control (itk); +#if 1 + if (!control || + !(control->flags & CSS_FLAG_ACTIVE)){ + css_focus (itk, dir); + } +#endif + } + else + { + /* this implements the non-inner element portions of: + * https://drafts.csswg.org/css-nav-1/#find-the-shortest-distance + * + * validity is determined by the centers of the items. + */ + + CtxControl *control = css_focused_control (itk); + CtxControl *best = control; + + if (!best) + best = itk->controls->data; + + float best_dist = 10000000000.0; + { + //float mid_ref_x = control->x + control->width/2; + //float mid_ref_y = control->y + control->height/2; + for (CtxList *iter = itk->controls; iter; iter=iter->next) + { + CtxControl *candidate = iter->data; + //float mid_cand_x = candidate->x + candidate->width/2; + //float mid_cand_y = candidate->y + candidate->height/2; + + int valid = 0; + if (candidate != control) + switch (dir) + { + case CSS_DIRECTION_DOWN: + valid = candidate->y > control->y; + break; + case CSS_DIRECTION_UP: + valid = candidate->y < control->y; + break; + case CSS_DIRECTION_LEFT: + valid = candidate->x < control->x; + break; + case CSS_DIRECTION_RIGHT: + valid = candidate->x > control->x; + break; + } + + if (valid) + { + float cand_coord[2]={0.f,0.f}; + float control_coord[2]={0.f,0.f}; + + float overlap = 0.0f; + float orthogonalSize = 1.0f; + float orthogonalBias = 1.0f; + float orthogonalWeight = 1.0f; + float alignWeight = 5.0f; + + + switch (dir) + { + case CSS_DIRECTION_DOWN: + control_coord[1] = control->y + control->height; + cand_coord[1] = candidate->y; + orthogonalSize = control->width; + orthogonalBias = orthogonalSize / 2.0f; + orthogonalWeight = 2.0f; + + if (candidate->x + candidate->width < control->x) + { +//// ---------- control +/// -- candidate + cand_coord[0] = candidate->x + candidate->width; + control_coord[0] = control->x; + } + else if (candidate->x < control->x && + candidate->x + candidate->width < control->x + control->width) + { +/// ---------- +/// ------ + cand_coord[0] = control->x; + control_coord[0] = control->x; + overlap = (candidate->x+candidate->width) - (control->x); + } + else if (candidate->x > control->x && + candidate->x + candidate->width < control->x + control->width) + { +/// ---------- +/// ------ + cand_coord[0] = candidate->x; + control_coord[0] = candidate->x; + overlap = candidate->width; + } + else if (candidate->x < control->x && + candidate->x + candidate->width > control->x + control->width) + { +/// ---------- +/// -------------- + cand_coord[0] = control->x; + control_coord[0] = control->x; + overlap = control->width; + } + else if (candidate->x > control->x && + candidate->x < control->x + control->width && + candidate->x + candidate->width > control->x + control->width) + { +/// ---------- +/// ---------- + cand_coord[0] = candidate->x; + control_coord[0] = candidate->x; + overlap = control->x + control->width - + candidate->x; + } + else if (candidate->x > control->x + control->width) + { +/// ---------- +/// ------ + cand_coord[0] = candidate->x; + control_coord[0] = control->x + control->width; + } + else + { + cand_coord[0] = candidate->x; + control_coord[0] = candidate->x; + overlap = candidate->width; + } + + break; + case CSS_DIRECTION_UP: + + control_coord[1] = control->y; + cand_coord[1] = candidate->y + candidate->height; + orthogonalSize = control->width; + orthogonalBias = orthogonalSize / 2.0f; + orthogonalWeight = 2.0f; + + if (candidate->x + candidate->width < control->x) + { +//// ---------- control +/// -- candidate + cand_coord[0] = candidate->x + candidate->width; + control_coord[0] = control->x; + } + else if (candidate->x < control->x && + candidate->x + candidate->width < control->x + control->width) + { +/// ---------- +/// ------ + cand_coord[0] = control->x; + control_coord[0] = control->x; + overlap = (candidate->x+candidate->width) - (control->x); + } + else if (candidate->x > control->x && + candidate->x + candidate->width < control->x + control->width) + { +/// ---------- +/// ------ + cand_coord[0] = candidate->x; + control_coord[0] = candidate->x; + overlap = candidate->width; + } + else if (candidate->x < control->x && + candidate->x + candidate->width > control->x + control->width) + { +/// ---------- +/// -------------- + cand_coord[0] = control->x; + control_coord[0] = control->x; + overlap = control->width; + } + else if (candidate->x > control->x && + candidate->x < control->x + control->width && + candidate->x + candidate->width > control->x + control->width) + { +/// ---------- +/// ---------- + cand_coord[0] = candidate->x; + control_coord[0] = candidate->x; + overlap = control->x + control->width - + candidate->x; + } + else if (candidate->x > control->x + control->width) + { +/// ---------- +/// ------ + cand_coord[0] = candidate->x; + control_coord[0] = control->x + control->width; + } + else + { + cand_coord[0] = candidate->x; + control_coord[0] = candidate->x; + overlap = candidate->width; + } + + break; + case CSS_DIRECTION_LEFT: + control_coord[0] = control->x; + cand_coord[0] = candidate->x + candidate->width; + orthogonalSize = control->height; + orthogonalBias = orthogonalSize / 2.0f; + orthogonalWeight = 30.0f; + + if (candidate->y + candidate->height < control->y) + { +//// ---------- control +/// -- candidate + cand_coord[1] = candidate->y + candidate->height; + control_coord[1] = control->y; + } + else if (candidate->y < control->y && + candidate->y + candidate->height < control->y + control->height) + { +/// ---------- +/// ------ + cand_coord[1] = control->y; + control_coord[1] = control->y; + overlap = (candidate->y+candidate->height) - (control->y); + } + else if (candidate->y > control->y && + candidate->y + candidate->height < control->y + control->height) + { +/// ---------- +/// ------ + cand_coord[1] = candidate->y; + control_coord[1] = candidate->y; + overlap = candidate->height; + } + else if (candidate->y < control->y && + candidate->y + candidate->height > control->y + control->height) + { +/// ---------- +/// -------------- + cand_coord[1] = control->y; + control_coord[1] = control->y; + overlap = control->height; + } + else if (candidate->y > control->y && + candidate->y < control->y + control->height && + candidate->y + candidate->height > control->y + control->height) + { +/// ---------- +/// ---------- + cand_coord[1] = candidate->y; + control_coord[1] = candidate->y; + overlap = control->y + control->height - candidate->y; + } + else if (candidate->y > control->y + control->height) + { +/// ---------- +/// ------ + cand_coord[1] = candidate->y; + control_coord[1] = control->y + control->height; + } + else + { + cand_coord[1] = candidate->y; + control_coord[1] = candidate->y; + overlap = candidate->height; + } + + + + + break; + case CSS_DIRECTION_RIGHT: + control_coord[0] = control->x + control->width; + cand_coord[0] = candidate->x; + orthogonalSize = control->height; + orthogonalBias = orthogonalSize / 2.0f; + orthogonalWeight = 30.0f; + + if (candidate->y + candidate->height < control->y) + { +//// ---------- control +/// -- candidate + cand_coord[1] = candidate->y + candidate->height; + control_coord[1] = control->y; + } + else if (candidate->y < control->y && + candidate->y + candidate->height < control->y + control->height) + { +/// ---------- +/// ------ + cand_coord[1] = control->y; + control_coord[1] = control->y; + overlap = (candidate->y+candidate->height) - (control->y); + } + else if (candidate->y > control->y && + candidate->y + candidate->height < control->y + control->height) + { +/// ---------- +/// ------ + cand_coord[1] = candidate->y; + control_coord[1] = candidate->y; + overlap = candidate->height; + } + else if (candidate->y < control->y && + candidate->y + candidate->height > control->y + control->height) + { +/// ---------- +/// -------------- + cand_coord[1] = control->y; + control_coord[1] = control->y; + overlap = control->height; + } + else if (candidate->y > control->y && + candidate->y < control->y + control->height && + candidate->y + candidate->height > control->y + control->height) + { +/// ---------- +/// ---------- + cand_coord[1] = candidate->y; + control_coord[1] = candidate->y; + overlap = control->y + control->height - candidate->y; + } + else if (candidate->y > control->y + control->height) + { +/// ---------- +/// ------ + cand_coord[1] = candidate->y; + control_coord[1] = control->y + control->height; + } + else + { + cand_coord[1] = candidate->y; + control_coord[1] = candidate->y; + overlap = candidate->height; + } + + break; + } + + float displacement = 0.0f; + + switch (dir) + { + case CSS_DIRECTION_DOWN: + case CSS_DIRECTION_UP: + displacement = (fabsf(cand_coord[0]-control_coord[0]) + + orthogonalBias) * orthogonalWeight; + break; + case CSS_DIRECTION_LEFT: + case CSS_DIRECTION_RIGHT: + displacement = (fabsf(cand_coord[1]-control_coord[1]) + + orthogonalBias) * orthogonalWeight; + break; + } + + float alignBias = overlap / orthogonalSize; + float alignment = alignWeight * alignBias; + + float euclidian = hypotf (cand_coord[0]-control_coord[0], + cand_coord[1]-control_coord[1]); + + + float dist = euclidian + displacement - alignment - sqrtf(sqrtf(overlap)); + // here we deviate from the algorithm - giving a smaller bonus to overlap + // to ensure more intermediate small ones miht be chosen + //float dist = euclidian + displacement - alignment - sqrtf(overlap); + if (dist <= best_dist) + { + best_dist = dist; + best = candidate; + } + } + } + } + + css_set_focus_no (itk, best->no); + } +} + +void css_key_tab (CtxEvent *event, void *data, void *data2) +{ + Css *itk = data; + CtxControl *control = css_focused_control (itk); + if (!control) return; + css_focus (itk, CSS_DIRECTION_NEXT); + + event->stop_propagate = 1; + ctx_queue_draw (event->ctx); +} + +void css_key_shift_tab (CtxEvent *event, void *data, void *data2) +{ + Css *itk = data; + CtxControl *control = css_focused_control (itk); + if (!control) return; + css_focus (itk, CSS_DIRECTION_PREVIOUS); + + event->stop_propagate = 1; + ctx_queue_draw (event->ctx); +} + +void css_key_return (CtxEvent *event, void *data, void *data2) +{ + Css *itk = data; + CtxControl *control = css_focused_control (itk); + if (!control) return; + if (control->no == itk->focus_no) + { + switch (control->type) + { + case UI_CHOICE: + { + itk->choice_active = !itk->choice_active; + } + break; + case UI_ENTRY: + { + if (itk->active) + { + css_entry_commit (itk); + } + else + { + itk->entry_copy = strdup (control->entry_value); + itk->entry_pos = strlen (itk->entry_copy); + itk->active = 1; + itk->active_entry = control->no; + } + } + break; + case UI_SLIDER: + if (itk->active) + { + itk->active = 0; + } + else + { + itk->active = 1; + } + break; + case UI_TOGGLE: + itk->return_value=1; + break; + case UI_EXPANDER: + { + int *val = control->val; + *val = !(*val); + } + break; + case UI_RADIO: + case UI_BUTTON: + { + itk->return_value=1; + } + break; + } + } + event->stop_propagate=1; + ctx_queue_draw (event->ctx); +} + +void css_key_left (CtxEvent *event, void *data, void *data2) +{ + Css *itk = data; + CtxControl *control = css_focused_control (itk); + if (!control) return; + + if (itk->active) + { + switch (control->type) + { + case UI_TOGGLE: + case UI_EXPANDER: + case UI_CHOICE: + css_key_return (event, data, data2); + break; + case UI_ENTRY: + itk->entry_pos --; + if (itk->entry_pos < 0) + itk->entry_pos = 0; + break; + case UI_SLIDER: + { + double val = control->value; + val -= control->step; + if (val < control->min) + val = control->min; + + itk->slider_value = control->value = val; + itk->return_value = 1; + } + break; + } + } + else + { + css_focus (itk, CSS_DIRECTION_LEFT); + } + + event->stop_propagate = 1; + ctx_queue_draw (event->ctx); +} + +void css_key_right (CtxEvent *event, void *data, void *data2) +{ + Css *itk = data; + CtxControl *control = css_focused_control (itk); + if (!control) return; + + if (itk->active) + { + switch (control->type) + { + case UI_TOGGLE: + case UI_EXPANDER: + case UI_BUTTON: + case UI_CHOICE: + case UI_RADIO: + // css_key_return (event, data, data2); + break; + case UI_ENTRY: + itk->entry_pos ++; + if (itk->entry_pos > (int)strlen (itk->entry_copy)) + itk->entry_pos = strlen (itk->entry_copy); + break; + case UI_SLIDER: + { + double val = control->value; + val += control->step; + if (val > control->max) val = control->max; + + itk->slider_value = control->value = val; + itk->return_value = 1; + } + break; + } + } + else + { + css_focus (itk, CSS_DIRECTION_RIGHT); + } + + event->stop_propagate = 1; + ctx_queue_draw (event->ctx); +} + +void css_key_up (CtxEvent *event, void *data, void *data2) +{ + Css *itk = data; + CtxControl *control = css_focused_control (itk); + + if (control && control->type == UI_CHOICE && itk->choice_active) + { + int old_val = itk->choice_no; + int prev_val = old_val; + for (CtxList *l = itk->choices; l; l=l?l->next:NULL) + { + UiChoice *choice = l->data; + if (choice->val == old_val) + { + itk->choice_no = prev_val; + itk->return_value = 1; + l=NULL; + } + prev_val = choice->val; + } + } + else if (control) + { + css_focus (itk, CSS_DIRECTION_UP); + } + ctx_queue_draw (event->ctx); + event->stop_propagate = 1; +} + +void css_key_down (CtxEvent *event, void *data, void *data2) +{ + Css *itk = data; + CtxControl *control = css_focused_control (itk); + if (control && control->type == UI_CHOICE && itk->choice_active) + { + { + int old_val = itk->choice_no; + for (CtxList *l = itk->choices; l; l=l?l->next:NULL) + { + UiChoice *choice = l->data; + if (choice->val == old_val) + { + if (l->next) + { + l = l->next; + choice = l->data; + itk->choice_no = choice->val; + itk->return_value = 1; + } + } + } + } + } + else if (control) + { + css_focus (itk, CSS_DIRECTION_DOWN); + } + event->stop_propagate = 1; + ctx_queue_draw (event->ctx); +} + + +void css_key_backspace (CtxEvent *event, void *data, void *data2) +{ + Css *itk = data; + CtxControl *control = css_focused_control (itk); + if (!control) return; + if (!itk->entry_copy) return; + if (!itk->active) return; + + switch (control->type) + { + case UI_ENTRY: + { + if (itk->active && itk->entry_pos > 0) + { + memmove (&itk->entry_copy[itk->entry_pos-1], &itk->entry_copy[itk->entry_pos], + strlen (&itk->entry_copy[itk->entry_pos] )+ 1); + itk->entry_pos --; + } + } + break; + } + event->stop_propagate = 1; + ctx_queue_draw (event->ctx); +} + +void css_key_delete (CtxEvent *event, void *data, void *data2) +{ + Css *itk = data; + CtxControl *control = css_focused_control (itk); + if (!control) return; + if (!itk->entry_copy) return; + if (!itk->active) return; + if ((int)strlen (itk->entry_copy) > itk->entry_pos) + { + css_key_right (event, data, data2); + css_key_backspace (event, data, data2); + } + event->stop_propagate = 1; + ctx_queue_draw (event->ctx); +} + +void css_key_unhandled (CtxEvent *event, void *userdata, void *userdata2) +{ + Css *itk = userdata; + + if (itk->active && itk->entry_copy) + { + const char *str = event->string; + if (!strcmp (str, "space")) + str = " "; + + if (ctx_utf8_strlen (str) == 1) + { + + char *tmp = malloc (strlen (itk->entry_copy) + strlen (str) + 1); + + char *rest = strdup (&itk->entry_copy[itk->entry_pos]); + itk->entry_copy[itk->entry_pos]=0; + + sprintf (tmp, "%s%s%s", itk->entry_copy, str, rest); + free (rest); + itk->entry_pos+=strlen(str); + free (itk->entry_copy); + itk->entry_copy = tmp; + ctx_queue_draw (event->ctx); + } + else + { + printf ("unhandled %s\n", str); + } + } + event->stop_propagate = 1; +} + +void css_key_bindings (Css *itk) +{ + Ctx *ctx = itk->ctx; + ctx_add_key_binding (ctx, "tab", NULL, "focus next", css_key_tab, itk); + ctx_add_key_binding (ctx, "shift-tab", NULL, "focus previous", css_key_shift_tab, itk); + + ctx_add_key_binding (ctx, "up", NULL, "spatial focus up", css_key_up, itk); + ctx_add_key_binding (ctx, "down", NULL, "spatical focus down", css_key_down, itk); + ctx_add_key_binding (ctx, "right", NULL, "spatial focus right", css_key_right, itk); + ctx_add_key_binding (ctx, "left", NULL, "spatial focus left", css_key_left, itk); + + ctx_add_key_binding (ctx, "return", NULL, "enter/edit", css_key_return, itk); + ctx_add_key_binding (ctx, "backspace", NULL, NULL, css_key_backspace, itk); + ctx_add_key_binding (ctx, "delete", NULL, NULL, css_key_delete, itk); + ctx_add_key_binding (ctx, "any", NULL, NULL, css_key_unhandled, itk); +} + +#if 0 +static void css_choice_set (CtxEvent *event, void *data, void *data2) +{ + Css *itk = data; + itk->choice_no = (size_t)(data2); + itk->return_value = 1; + ctx_queue_draw (event->ctx); + event->stop_propagate = 1; +} +#endif + +void ctx_event_block (CtxEvent *event, void *data, void *data2) +{ + Css *itk = data; + itk->choice_active = 0; + event->stop_propagate = 1; + ctx_queue_draw (event->ctx); +} + +void css_done (Css *itk) +{ + Ctx *ctx = itk->ctx; + + CtxControl *control = css_focused_control (itk); +#if 0 + CtxControl *hovered_control = css_hovered_control (itk); + int hovered_no = hovered_control ? hovered_control->no : -1; + + if (itk->hovered_no != hovered_no) + { + itk->hovered_no = hovered_no; + ctx_queue_draw (ctx); + } +#endif + + if (!control){ + ctx_restore (ctx); + return; + } + + ctx_restore (ctx); +} + +int ctx_renderer_is_sdl (Ctx *ctx); +int ctx_renderer_is_fb (Ctx *ctx); + +extern int ctx_show_fps; + +void +css_ctx_settings (Css *itk) +{ +#ifdef CTX_MAX_THREADS + static int ctx_settings = 0; + static int inited = 0; + static int threads; + //static int hash_cache_enabled; + Ctx *ctx = itk->ctx; + + if (!inited){ + if (!ctx_backend_is_tiled (ctx)) + return; + inited = 1; + threads = ctx_get_render_threads (ctx); + //hash_cache_enabled = ctx_get_hash_cache (ctx); + } + if (css_expander (itk, "CTX settings", &ctx_settings)) + { +#if 0 + hash_cache_enabled = css_toggle (itk, "hash cache", hash_cache_enabled); + if (hash_cache_enabled != ctx_get_hash_cache (ctx)){ + ctx_set_hash_cache (ctx, hash_cache_enabled); + } +#endif + +#if CTX_SDL + ctx_show_fps = css_toggle (itk, "fps debug", ctx_show_fps); +#endif + threads = css_slider (itk, "threads", threads, 1, CTX_MAX_THREADS, 1); + if (threads != ctx_get_render_threads (ctx)) + { + ctx_set_render_threads (ctx, threads); + } + } +#endif +} + +void +css_css_settings (Css *itk) +{ + static int css_settings = 0; + if (css_expander (itk, "Css settings", &css_settings)) + { + //itk->focus_wraparound = css_toggle (itk, "focus wraparound", itk->focus_wraparound); + //enable_keybindings = css_toggle (itk, "enable keybindings", enable_keybindings); + //itk->light_mode = css_toggle (itk, "light mode", itk->light_mode); + + itk->scale = css_slider (itk, "global scale", itk->scale, 0.1, 8.0, 0.1); + itk->font_size = css_slider (itk, "font size ", itk->font_size, 3.0, 60.0, 0.25); + + // these will go away with css styling merged. + css_slider_float (itk, "vgap", &itk->rel_vgap, 0.0, 3.0, 0.02); + css_slider_float (itk, "scroll speed", &itk->scroll_speed, 0.0, 1.0, 0.01); + css_slider_float (itk, "ver advance", &itk->rel_ver_advance, 0.1, 4.0, 0.01); + css_slider_float (itk, "hmargin", &itk->rel_hmargin, 0.0, 40.0, 0.1); + css_slider_float (itk, "vmargin", &itk->rel_vmargin, 0.0, 40.0, 0.1); + css_slider_float (itk, "label width", &itk->label_width, 0.0, 40.0, 0.02); + } +} + +void css_key_quit (CtxEvent *event, void *userdata, void *userdata2) +{ + ctx_exit (event->ctx); +} + +int _css_key_bindings_active = 1; + +static int +css_iteration (double time, void *data) +{ + Css *itk = (Css*)data; + Ctx *ctx = itk->ctx; + int ret_val = 1; + + if (1 || ctx_need_redraw (ctx)) + { + css_reset (itk); + if (_css_key_bindings_active) + css_key_bindings (itk); + ctx_add_key_binding (itk->ctx, "control-q", NULL, "Quit", css_key_quit, NULL); + if (itk->ui_fun) + ret_val = itk->ui_fun (itk, itk->ui_data); + + css_done (itk); + ctx_end_frame (ctx); + } + + return ret_val; +} + +#ifdef EMSCRIPTEN +#include +#endif + +void css_run_ui (Css *itk, int (*ui_fun)(Css *itk, void *data), void *ui_data) +{ + itk->ui_fun = ui_fun; + itk->ui_data = ui_data; + int ret_val = 1; + +#ifdef EMSCRIPTEN +#ifdef ASYNCIFY + while (!ctx_has_exited (itk->ctx) && (ret_val == 1)) + { + ret_val = css_iteration (0.0, itk); + } +#else + emscripten_request_animation_frame_loop (css_iteration, itk); + return; +#endif +#else + while (!ctx_has_exited (itk->ctx) && (ret_val == 1)) + { + ret_val = css_iteration (0.0, itk); + } +#endif +} + +void css_main (int (*ui_fun)(Css *itk, void *data), void *ui_data) +{ + Ctx *ctx = ctx_new (-1, -1, NULL); + Css *itk = css_new (ctx); + css_run_ui (itk, ui_fun, ui_data); + css_destroy (itk); + ctx_destroy (ctx); +} + + +float css_x (Css *itk) +{ + return itk->x; +} +float css_y (Css *itk) +{ + return itk->y; +} +void css_set_x (Css *itk, float x) +{ + itk->x = x; +} +void css_set_y (Css *itk, float y) +{ + itk->y = y; +} +void css_set_xy (Css *itk, float x, float y) +{ + itk->x = x; + itk->y = y; +} + + +void css_set_height (Css *itk, float height) +{ + itk->height = height; + itk->edge_bottom = itk->edge_top + height; + css_set_edge_top ((Css*)itk, itk->edge_top); + css_set_edge_bottom ((Css*)itk, itk->edge_bottom); +} + +float css_edge_bottom (Css *itk) +{ + return itk->edge_bottom ; +} +float css_edge_top (Css *itk) +{ + return itk->edge_top ; +} +float css_edge_left (Css *itk) +{ + return itk->edge_left; +} +float css_height (Css *itk) +{ + return itk->height; +} +float css_wrap_width (Css *itk) +{ + return itk->width; // XXX compute from edges:w +} +void css_set_wrap_width (Css *itk, float wwidth) +{ + itk->width = wwidth; + itk->edge_right = itk->edge_left + wwidth; + css_set_edge_right ((Css*)itk, itk->edge_right); + css_set_edge_left ((Css*)itk, itk->edge_left); +} + +float css_edge_right (Css *itk) +{ + return itk->edge_right; +} + +Ctx *css_ctx (Css *itk) +{ + return itk->ctx; +} + +void css_set_scale (Css *itk, float scale) +{ + itk->scale = scale; + ctx_queue_draw (itk->ctx); +} + +float css_scale (Css *itk) +{ + return itk->scale; +} + +int css_focus_no (Css *itk) +{ + return itk->focus_no; +} + +int css_is_editing_entry (Css *itk) +{ + return itk->entry_copy != NULL; +} + +int css_control_no (Css *itk) +{ + return itk->control_no; +} + +float css_panel_scroll (Css *itk) +{ + CssPanel *panel = itk->panel; + if (!panel) + panel = itk->panels?itk->panels->data:NULL; + if (!panel) return 0.0f; + return panel->scroll; +} + +void css_panel_set_scroll (Css *itk, float scroll) +{ + CssPanel *panel = itk->panel; + if (!panel) + panel = itk->panels?itk->panels->data:NULL; + if (!panel) return; + if (scroll < 0.0) scroll = 0.0f; + panel->scroll = scroll; + ctx_queue_draw (itk->ctx); +} + #endif #endif // CTX_IMPLEMENTATION diff --git a/components/ctx/ctx_config.h b/components/ctx/ctx_config.h index 6da8716..6327be4 100644 --- a/components/ctx/ctx_config.h +++ b/components/ctx/ctx_config.h @@ -28,16 +28,19 @@ #define CTX_LIMIT_FORMATS 1 #define CTX_32BIT_SEGMENTS 0 #define CTX_RASTERIZER 1 -#define CTX_RASTERIZER_AA 3 +#define CTX_RASTERIZER_AA 5 #define CTX_ENABLE_RGB565 1 #define CTX_ENABLE_RGB565_BYTESWAPPED 1 #define CTX_COMPOSITING_GROUPS 0 #define CTX_ALWAYS_USE_NEAREST_FOR_SCALE1 1 -#define CTX_EVENTS 0 +#define CTX_EVENTS 1 +#define CTX_TERMINAL_EVENTS 0 #define CTX_THREADS 0 #define CTX_TILED 0 #define CTX_BAREMETAL 1 #define CTX_ONE_FONT_ENGINE 1 +#define CTX_GET_CONTENTS 1 +#define CTX_CSS 1 #define CTX_MAX_SCANLINE_LENGTH 960 #define CTX_MAX_JOURNAL_SIZE (1024*512) @@ -67,7 +70,6 @@ #define CTX_STROKE_1PX 1 -#if defined(CONFIG_FLOW3R_CTX_FLAVOUR_FULL) #define CTX_COMPOSITE_O3 1 #define CTX_RASTERIZER_O2 0 #define CTX_GSTATE_PROTECT 1 @@ -87,63 +89,19 @@ #define STBI_ONLY_GIF #define STBI_ONLY_JPEG -#else -#define CTX_COMPOSITE_O3 1 -#define CTX_RASTERIZER_O2 0 -#define CTX_GSTATE_PROTECT 1 -#define CTX_FORCE_INLINES 1 -#define CTX_FRAGMENT_SPECIALIZE 1 -#define CTX_ENABLE_YUV420 1 -#define CTX_ENABLE_RGBA8 1 -#define CTX_ENABLE_RGB332 1 -#define CTX_ENABLE_RGB8 1 -#define CTX_ENABLE_GRAY8 1 -#define CTX_ENABLE_GRAYA8 1 -#define CTX_ENABLE_GRAY1 1 -#define CTX_ENABLE_GRAY2 1 -#define CTX_ENABLE_GRAY4 1 -#define CTX_STB_IMAGE 1 -#define STBI_ONLY_PNG -#define STBI_ONLY_GIF -#define STBI_ONLY_JPEG - -#endif - - - #define CTX_STATIC_FONT(font) \ ctx_load_font_ctx(ctx_font_##font##_name, \ ctx_font_##font, \ sizeof (ctx_font_##font)) - - -#define CTX_MAX_FONTS 10 +#define CTX_MAX_FONTS 3 #include "Arimo-Regular.h" #define CTX_FONT_0 CTX_STATIC_FONT(Arimo_Regular) -#if defined(CONFIG_FLOW3R_CTX_FLAVOUR_FULL) - -#include "Arimo-Bold.h" -#include "Arimo-Italic.h" -#include "Arimo-BoldItalic.h" -#define CTX_FONT_1 CTX_STATIC_FONT(Arimo_Bold) -#define CTX_FONT_2 CTX_STATIC_FONT(Arimo_Italic) -#define CTX_FONT_3 CTX_STATIC_FONT(Arimo_BoldItalic) - -#include "CampFont1/CampFont1.h" -#include "CampFont2/CampFont2.h" -#include "CampFont3/CampFont3.h" -#define CTX_FONT_4 CTX_STATIC_FONT(CampFont1) -#define CTX_FONT_5 CTX_STATIC_FONT(CampFont2) -#define CTX_FONT_6 CTX_STATIC_FONT(CampFont3) +// there is room for a mono font, but then the partition is full +// +//#include "Comic-Mono.h" +//#define CTX_FONT_1 CTX_STATIC_FONT(Comic_Mono) -#include "MaterialIcons.h" -#define CTX_FONT_7 CTX_STATIC_FONT(MaterialIcons) - -#include "Comic-Mono.h" -#define CTX_FONT_8 CTX_STATIC_FONT(Comic_Mono) - -#endif diff --git a/components/st3m/st3m_gfx.c b/components/st3m/st3m_gfx.c index 13e3197..563d322 100644 --- a/components/st3m/st3m_gfx.c +++ b/components/st3m/st3m_gfx.c @@ -6,9 +6,6 @@ #include "esp_system.h" #include "esp_task.h" #include "esp_timer.h" -#include "freertos/FreeRTOS.h" -#include "freertos/queue.h" -#include "freertos/semphr.h" // clang-format off #include "ctx_config.h" @@ -20,658 +17,17 @@ #include "st3m_counter.h" #include "st3m_version.h" -#define ST3M_GFX_BLIT_TASK 1 +void tildagon_start_frame (Ctx *ctx); +Ctx *tildagon_gfx_ctx (void); +void tildagon_blit_fb (void); +void tildagon_end_frame (Ctx *ctx); -#if CTX_ST3M_FB_INTERNAL_RAM -#undef ST3M_GFX_BLIT_TASK -#define ST3M_GFX_BLIT_TASK 0 -#endif - - -#define ST3M_GFX_DEFAULT_MODE (16 | st3m_gfx_osd) - -static st3m_gfx_mode default_mode = ST3M_GFX_DEFAULT_MODE; - -#if CONFIG_FLOW3R_CTX_FLAVOUR_FULL -EXT_RAM_BSS_ATTR static uint8_t - st3m_fb2[FLOW3R_BSP_DISPLAY_WIDTH * FLOW3R_BSP_DISPLAY_HEIGHT * 4]; -#endif - -#if CTX_ST3M_FB_INTERNAL_RAM -// without EXT_RAM_BSS_ATTR is removed 8bit and 16bit modes go -// faster but it is not possible to enable wifi -static uint16_t st3m_fb[FLOW3R_BSP_DISPLAY_WIDTH * FLOW3R_BSP_DISPLAY_HEIGHT]; -uint8_t st3m_pal[256 * 3]; -#else -EXT_RAM_BSS_ATTR uint8_t st3m_pal[256 * 3]; -EXT_RAM_BSS_ATTR static uint8_t - st3m_fb[FLOW3R_BSP_DISPLAY_WIDTH * FLOW3R_BSP_DISPLAY_HEIGHT * 4]; -#endif - -#if ST3M_GFX_BLIT_TASK -EXT_RAM_BSS_ATTR static uint8_t - st3m_fb_copy[FLOW3R_BSP_DISPLAY_WIDTH * FLOW3R_BSP_DISPLAY_HEIGHT * 2]; -#endif - -EXT_RAM_BSS_ATTR static uint8_t scratch[40 * 1024]; - -// Get a free drawlist ctx to draw into. -// -// ticks_to_wait can be used to limit the time to wait for a free ctx -// descriptor, or portDELAY_MAX can be specified to wait forever. If the timeout -// expires, NULL will be returned. -static Ctx *st3m_gfx_drawctx_free_get(TickType_t ticks_to_wait); - -// Submit a filled ctx descriptor to the rasterization pipeline. -static void st3m_gfx_pipe_put(void); - -static const char *TAG = "st3m-gfx"; - -#define N_DRAWLISTS 3 - -// we keep the OSD buffer the same size as the main framebuffer, -// allowing us to do different combos of which buffer is osd and not - -static st3m_gfx_mode _st3m_gfx_mode = st3m_gfx_default + 1; - -static Ctx *ctx = NULL; -// each frame buffer has an associated rasterizer context -static Ctx *fb_ctx = NULL; -#if CONFIG_FLOW3R_CTX_FLAVOUR_FULL -static Ctx *osd_ctx = NULL; - -#define ST3M_OSD_LOCK_TIMEOUT 500 -SemaphoreHandle_t st3m_osd_lock; -#endif - -SemaphoreHandle_t st3m_fb_lock; -SemaphoreHandle_t st3m_fb_copy_lock; - -typedef struct { - Ctx *user_ctx; - - int osd_y0; - int osd_x0; - int osd_y1; - int osd_x1; - - int blit_x; // upper left pixel in framebuffer coordinates - int blit_y; // - - st3m_gfx_mode mode; - uint8_t *blit_src; -} st3m_gfx_drawlist; -static st3m_gfx_drawlist drawlists[N_DRAWLISTS]; - -#if CONFIG_FLOW3R_CTX_FLAVOUR_FULL -static int _st3m_osd_y1 = - 0; // the corner coordinates of the part of osd that needs to -static int _st3m_osd_x1 = 0; // be composited - more might be composited -static int _st3m_osd_y0 = 0; -static int _st3m_osd_x0 = 0; -#endif +uint8_t st3m_pal[256 * 3]; // XXX - unused static float smoothed_fps = 0.0f; -static QueueHandle_t user_ctx_freeq = NULL; -static QueueHandle_t user_ctx_rastq = NULL; -static QueueHandle_t user_ctx_blitq = NULL; - static st3m_counter_rate_t rast_rate; -static TaskHandle_t graphics_rast_task; -#if ST3M_GFX_BLIT_TASK -static TaskHandle_t graphics_blit_task; -#endif - -static int _st3m_gfx_low_latency = 0; - -static int st3m_gfx_fb_width = 0; -static int st3m_gfx_fb_height = 0; -static int st3m_gfx_blit_x = 0; -static int st3m_gfx_blit_y = 0; -static int st3m_gfx_geom_dirty = 0; - -static inline int st3m_gfx_scale(st3m_gfx_mode mode) { - switch ((int)(mode & st3m_gfx_4x)) { - case st3m_gfx_4x: - return 4; - case st3m_gfx_3x: - return 3; - case st3m_gfx_2x: - return 2; - } - return 1; -} - -/////////////////////////////////////////////////////// - -// get the bits per pixel for a given mode -static inline int _st3m_gfx_bpp(st3m_gfx_mode mode) { - st3m_gfx_mode set_mode = _st3m_gfx_mode ? _st3m_gfx_mode : default_mode; - if (mode == st3m_gfx_default) { - mode = set_mode; - } else if (mode == st3m_gfx_osd) { - if ((st3m_gfx_bpp(set_mode) == 16) || (st3m_gfx_bpp(set_mode) == 8)) - return 32; - else - return 16; - } - int bpp = (mode & 63); - if (bpp >= 2 && bpp < 4) bpp = 2; - if (bpp >= 4 && bpp < 8) bpp = 4; - if (bpp >= 8 && bpp < 16) bpp = 8; - return bpp; -} -int st3m_gfx_bpp(st3m_gfx_mode mode) { return _st3m_gfx_bpp(mode); } - -static Ctx *st3m_gfx_ctx_int(st3m_gfx_mode mode) { - st3m_gfx_mode set_mode = _st3m_gfx_mode ? _st3m_gfx_mode : default_mode; -#if CONFIG_FLOW3R_CTX_FLAVOUR_FULL - if (mode == st3m_gfx_osd) { - if ((_st3m_gfx_bpp(set_mode) == 16) || (st3m_gfx_bpp(set_mode) == 8)) - return osd_ctx; - return osd_ctx; - } -#endif - Ctx *ctx = st3m_gfx_drawctx_free_get(1000); - - if (set_mode & st3m_gfx_direct_ctx) { - if (set_mode & st3m_gfx_smart_redraw) return ctx; - return fb_ctx; - } - - if (!ctx) return NULL; - return ctx; -} - -static void st3m_gfx_viewport_transform(Ctx *ctx, int reset) { - int scale = st3m_gfx_scale(_st3m_gfx_mode ? _st3m_gfx_mode : default_mode); - int32_t offset_x = FLOW3R_BSP_DISPLAY_WIDTH / 2 / scale; - int32_t offset_y = FLOW3R_BSP_DISPLAY_HEIGHT / 2 / scale; - if (reset) - ctx_identity( - ctx); // this might break/need revisiting with tiled rendering - ctx_apply_transform(ctx, 1.0 / scale, 0, offset_x, 0, 1.0 / scale, offset_y, - 0, 0, 1); -} - -static void st3m_gfx_start_frame(Ctx *ctx) { - int scale = st3m_gfx_scale(_st3m_gfx_mode); - if (scale > 1) { - ctx_rectangle(ctx, -120, -120, 240, 240); - ctx_clip(ctx); - } -} - -Ctx *st3m_gfx_ctx(st3m_gfx_mode mode) { - Ctx *ctx = st3m_gfx_ctx_int(mode); -#if CONFIG_FLOW3R_CTX_FLAVOUR_FULL - if (mode == st3m_gfx_osd) { - xSemaphoreTake(st3m_osd_lock, - ST3M_OSD_LOCK_TIMEOUT / portTICK_PERIOD_MS); - } -#endif - ctx_save(ctx); - if (mode != st3m_gfx_osd) st3m_gfx_viewport_transform(ctx, 0); - st3m_gfx_start_frame(ctx); - return ctx; -} - -// Attempt to receive from a queue forever, but log an error if it takes longer -// than two seconds to get something. -static void xQueueReceiveNotifyStarved(QueueHandle_t q, void *dst, - const char *desc) { - uint8_t starved = 0; - for (;;) { - if (xQueueReceive(q, dst, pdMS_TO_TICKS(2000)) == pdTRUE) { - return; - } - if (!starved) { - ESP_LOGI(TAG, "%s", desc); - starved = 1; - } - } -} - -float st3m_gfx_fps(void) { return smoothed_fps; } - -void st3m_gfx_set_palette(uint8_t *pal_in, int count) { - if (count > 256) count = 256; - if (count < 0) count = 0; - for (int i = 0; i < count * 3; i++) st3m_pal[i] = pal_in[i]; -} - -void st3m_gfx_set_default_mode(st3m_gfx_mode mode) { - if ((mode & (1 | 2 | 4 | 8 | 16 | 32)) == mode) { - default_mode &= ~(1 | 2 | 4 | 8 | 16 | 32); - default_mode |= mode; - } else if (mode == st3m_gfx_2x) { - default_mode &= ~st3m_gfx_4x; - default_mode |= st3m_gfx_2x; - } else if (mode == st3m_gfx_3x) { - default_mode &= ~st3m_gfx_4x; - default_mode |= st3m_gfx_3x; - } else if (mode == st3m_gfx_4x) { - default_mode &= ~st3m_gfx_4x; - default_mode |= st3m_gfx_4x; - } else if (mode == st3m_gfx_osd) { - default_mode |= st3m_gfx_osd; - } else if (mode == st3m_gfx_low_latency) { - default_mode |= st3m_gfx_low_latency; - } else if (mode == st3m_gfx_lock) { - default_mode |= st3m_gfx_lock; - } else if (mode == st3m_gfx_direct_ctx) { - default_mode |= st3m_gfx_direct_ctx; - } else - default_mode = mode; - - if (default_mode & st3m_gfx_smart_redraw) { - default_mode &= ~63; - default_mode |= 16; - } - - if (default_mode & st3m_gfx_lock) { - default_mode &= ~st3m_gfx_lock; - _st3m_gfx_mode = default_mode + 1; - st3m_gfx_set_mode(st3m_gfx_default); - default_mode |= st3m_gfx_lock; - } else { - _st3m_gfx_mode = default_mode + 1; - st3m_gfx_set_mode(st3m_gfx_default); - } -} - -static void st3m_gfx_init_palette(st3m_gfx_mode mode) { - switch (mode & 0xf) { - case 1: - for (int i = 0; i < 2; i++) { - st3m_pal[i * 3 + 0] = i * 255; - st3m_pal[i * 3 + 1] = i * 255; - st3m_pal[i * 3 + 2] = i * 255; - } - break; - case 2: - for (int i = 0; i < 4; i++) { - st3m_pal[i * 3 + 0] = (i * 255) / 3; - st3m_pal[i * 3 + 1] = (i * 255) / 3; - st3m_pal[i * 3 + 2] = (i * 255) / 3; - } - break; - case 4: { -#if 0 - // ega palette - int idx = 0; - for (int i = 0; i < 2; i++) - for (int r = 0; r < 2; r++) - for (int g = 0; g < 2; g++) - for (int b = 0; b < 2; b++) { - st3m_pal[idx++] = (r * 127) * (i * 2); - st3m_pal[idx++] = (g * 127) * (i * 2); - st3m_pal[idx++] = (b * 127) * (i * 2); - } -#else - // night-mode - for (int i = 0; i < 16; i++) { - st3m_pal[i * 3 + 0] = (i * 255) / 15; - st3m_pal[i * 3 + 1] = ((i * 255) / 15) / 3; - st3m_pal[i * 3 + 2] = ((i * 255) / 15) / 5; - } - break; -#endif - } break; - case 8: // grayscale - for (int i = 0; i < 256; i++) { - st3m_pal[i * 3 + 0] = i; - st3m_pal[i * 3 + 1] = i; - st3m_pal[i * 3 + 2] = i; - } - break; - case st3m_gfx_rgb332: - for (int i = 0; i < 256; i++) { - st3m_pal[i * 3 + 0] = (((i >> 5) & 7) * 255) / 7; - st3m_pal[i * 3 + 1] = (((i >> 2) & 7) * 255) / 7; - st3m_pal[i * 3 + 2] = - ((((i & 3) << 1) | ((i >> 2) & 1)) * 255) / 7; - } - break; - case st3m_gfx_sepia: - for (int i = 0; i < 256; i++) { - st3m_pal[i * 3 + 0] = i; - st3m_pal[i * 3 + 1] = (i / 255.0) * (i / 255.0) * 255; - st3m_pal[i * 3 + 2] = - (i / 255.0) * (i / 255.0) * (i / 255.0) * 255; - } - break; - case st3m_gfx_cool: - for (int i = 0; i < 256; i++) { - st3m_pal[i * 3 + 0] = - (i / 255.0) * (i / 255.0) * (i / 255.0) * 255; - st3m_pal[i * 3 + 1] = (i / 255.0) * (i / 255.0) * 255; - st3m_pal[i * 3 + 2] = i; - } - break; - } -} - -st3m_gfx_mode st3m_gfx_set_mode(st3m_gfx_mode mode) { - if ((mode == _st3m_gfx_mode) || (0 != (default_mode & st3m_gfx_lock))) { - return (mode ? mode : default_mode); - } - - if (mode == st3m_gfx_default) - mode = default_mode; - else if (mode == st3m_gfx_low_latency) - mode = default_mode | st3m_gfx_low_latency; - else if (mode == st3m_gfx_osd) - mode = default_mode | st3m_gfx_osd; - - _st3m_gfx_mode = (mode == default_mode) ? st3m_gfx_default : mode; - - if (((mode & st3m_gfx_low_latency) != 0) || - ((mode & st3m_gfx_direct_ctx) != 0)) - _st3m_gfx_low_latency = (N_DRAWLISTS - 1); - else - _st3m_gfx_low_latency = 0; - - st3m_gfx_fbconfig(240, 240, 0, 0); - - return mode; -} - -st3m_gfx_mode st3m_gfx_get_mode(void) { - return _st3m_gfx_mode ? _st3m_gfx_mode : default_mode; -} - -uint8_t *st3m_gfx_fb(st3m_gfx_mode mode, int *width, int *height, int *stride) { - st3m_gfx_mode set_mode = _st3m_gfx_mode ? _st3m_gfx_mode : default_mode; - int bpp = _st3m_gfx_bpp(set_mode); - if (mode == st3m_gfx_palette) { - if (stride) *stride = 3; - if (width) *width = 1; - if (height) *height = 256; - return st3m_pal; - } else if (mode == st3m_gfx_default) { - if (stride) *stride = st3m_gfx_fb_width * bpp / 8; - if (width) *width = FLOW3R_BSP_DISPLAY_WIDTH; - if (height) *height = FLOW3R_BSP_DISPLAY_HEIGHT; - return ((uint8_t *)st3m_fb); - } -#if CONFIG_FLOW3R_CTX_FLAVOUR_FULL - else if (mode == st3m_gfx_osd) { - if (stride) *stride = FLOW3R_BSP_DISPLAY_WIDTH * bpp / 8; - if (width) *width = FLOW3R_BSP_DISPLAY_WIDTH; - if (height) *height = FLOW3R_BSP_DISPLAY_HEIGHT; - return st3m_fb2; - } - - int scale = st3m_gfx_scale(set_mode); - if (stride) *stride = FLOW3R_BSP_DISPLAY_WIDTH * bpp / 8; - if (width) *width = FLOW3R_BSP_DISPLAY_WIDTH / scale; - if (height) *height = FLOW3R_BSP_DISPLAY_HEIGHT / scale; -#endif - return (uint8_t *)st3m_fb; -} - -#if CONFIG_FLOW3R_CTX_FLAVOUR_FULL -static void *osd_fb = st3m_fb2; -#endif - -static void st3m_gfx_blit(st3m_gfx_drawlist *drawlist) { - st3m_gfx_mode set_mode = drawlist->mode; - uint8_t *blit_src = drawlist->blit_src; - int bits = _st3m_gfx_bpp(set_mode); - - static st3m_gfx_mode prev_mode; - - - if (set_mode != prev_mode) { - st3m_gfx_init_palette(set_mode); - } - - xSemaphoreTake(st3m_fb_copy_lock, portMAX_DELAY); -#if CONFIG_FLOW3R_CTX_FLAVOUR_FULL - int scale = st3m_gfx_scale(set_mode); - int osd_x0 = drawlist->osd_x0, osd_x1 = drawlist->osd_x1, - osd_y0 = drawlist->osd_y0, osd_y1 = drawlist->osd_y1; - - if ((scale > 1) || ((set_mode & st3m_gfx_osd) && (osd_y0 != osd_y1))) { - if (((set_mode & st3m_gfx_osd) && (osd_y0 != osd_y1))) { - if ((set_mode & 0xf) == st3m_gfx_rgb332) bits = 9; - xSemaphoreTake(st3m_osd_lock, - ST3M_OSD_LOCK_TIMEOUT / portTICK_PERIOD_MS); - flow3r_bsp_display_send_fb_osd(blit_src, bits, scale, osd_fb, - osd_x0, osd_y0, osd_x1, osd_y1); - xSemaphoreGive(st3m_osd_lock); - } else { - if ((set_mode & 0xf) == st3m_gfx_rgb332) bits = 9; - flow3r_bsp_display_send_fb_osd(blit_src, bits, scale, NULL, 0, 0, 0, - 0); - } - } else -#endif - { - if ((set_mode & 0xf) == st3m_gfx_rgb332) bits = 9; - flow3r_bsp_display_send_fb(blit_src, bits); - } - xSemaphoreGive(st3m_fb_copy_lock); - prev_mode = set_mode; -} - -#if ST3M_GFX_BLIT_TASK -static void st3m_gfx_blit_task(void *_arg) { - while (true) { - int desc_no = 0; - xQueueReceiveNotifyStarved(user_ctx_blitq, &desc_no, - "blit task starved (user_ctx)!"); - st3m_gfx_drawlist *drawlist = &drawlists[desc_no]; - - st3m_gfx_blit(drawlist); - xQueueSend(user_ctx_freeq, &desc_no, portMAX_DELAY); - } -} -#endif - -static void st3m_gfx_rast_task(void *_arg) { - (void)_arg; - st3m_gfx_set_mode(st3m_gfx_default); - - int bits = 0; - st3m_gfx_mode prev_set_mode = ST3M_GFX_DEFAULT_MODE - 1; - -#if ST3M_GFX_BLIT_TASK - int direct_blit = 0; -#endif - - while (true) { - int desc_no = 0; - int tc = ctx_textureclock(fb_ctx) + 1; - xQueueReceiveNotifyStarved(user_ctx_rastq, &desc_no, - "rast task starved (user_ctx)!"); - st3m_gfx_drawlist *drawlist = &drawlists[desc_no]; - st3m_gfx_mode set_mode = drawlist->mode; - - xSemaphoreTake(st3m_fb_lock, portMAX_DELAY); - - ctx_set_textureclock(fb_ctx, tc); -#if CONFIG_FLOW3R_CTX_FLAVOUR_FULL - ctx_set_textureclock(osd_ctx, tc); - ctx_set_textureclock(ctx, tc); -#endif - if (st3m_gfx_geom_dirty || (prev_set_mode != set_mode)) { - int was_geom_dirty = (prev_set_mode == set_mode); - bits = _st3m_gfx_bpp(set_mode); - st3m_gfx_geom_dirty = 0; - -#if ST3M_GFX_BLIT_TASK - if ((bits > 16)) - direct_blit = 1; - else - direct_blit = 0; -#endif - - int stride = (bits * st3m_gfx_fb_width) / 8; - switch (bits) { -#if CONFIG_FLOW3R_CTX_FLAVOUR_FULL - case 1: - ctx_rasterizer_reinit(fb_ctx, st3m_fb, 0, 0, - st3m_gfx_fb_width, st3m_gfx_fb_height, - stride, CTX_FORMAT_GRAY1); - break; - case 2: - ctx_rasterizer_reinit(fb_ctx, st3m_fb, 0, 0, - st3m_gfx_fb_width, st3m_gfx_fb_height, - stride, CTX_FORMAT_GRAY2); - break; - case 4: - ctx_rasterizer_reinit(fb_ctx, st3m_fb, 0, 0, - st3m_gfx_fb_width, st3m_gfx_fb_height, - stride, CTX_FORMAT_GRAY4); - break; - case 8: - case 9: - if ((set_mode & 0xf) == 9) - ctx_rasterizer_reinit( - fb_ctx, st3m_fb, 0, 0, st3m_gfx_fb_width, - st3m_gfx_fb_height, stride, CTX_FORMAT_RGB332); - else - ctx_rasterizer_reinit( - fb_ctx, st3m_fb, 0, 0, st3m_gfx_fb_width, - st3m_gfx_fb_height, stride, CTX_FORMAT_GRAY8); - break; -#endif - case 16: - ctx_rasterizer_reinit(fb_ctx, st3m_fb, 0, 0, - st3m_gfx_fb_width, st3m_gfx_fb_height, - stride, - CTX_FORMAT_RGB565_BYTESWAPPED); - break; - -#if CONFIG_FLOW3R_CTX_FLAVOUR_FULL - case 24: - ctx_rasterizer_reinit(fb_ctx, st3m_fb, 0, 0, 240, 240, - 240 * 3, CTX_FORMAT_RGB8); - break; - case 32: - ctx_rasterizer_reinit(fb_ctx, st3m_fb, 0, 0, 240, 240, - 240 * 4, CTX_FORMAT_RGBA8); - break; -#endif - } - if ((set_mode & st3m_gfx_smart_redraw) == 0) { - if (!was_geom_dirty) memset(st3m_fb, 0, sizeof(st3m_fb)); - } -#if CONFIG_FLOW3R_CTX_FLAVOUR_FULL - st3m_gfx_viewport_transform(osd_ctx, 1); - if (!was_geom_dirty) memset(st3m_fb2, 0, sizeof(st3m_fb2)); -#endif - prev_set_mode = set_mode; - } - - if ((set_mode & st3m_gfx_direct_ctx) == 0) { - if ((set_mode & st3m_gfx_smart_redraw)) { - ctx_start_frame(ctx); - ctx_render_ctx(drawlist->user_ctx, ctx); - ctx_end_frame(ctx); - } else { - ctx_save(fb_ctx); - ctx_render_ctx(drawlist->user_ctx, fb_ctx); - ctx_restore(fb_ctx); - } - ctx_drawlist_clear(drawlist->user_ctx); - } -#if ST3M_GFX_BLIT_TASK - if (direct_blit) { -#endif - drawlist->blit_src = st3m_fb; - st3m_gfx_blit(drawlist); - xSemaphoreGive(st3m_fb_lock); - xQueueSend(user_ctx_freeq, &desc_no, portMAX_DELAY); -#if ST3M_GFX_BLIT_TASK - } else { - drawlist->blit_src = st3m_fb_copy; - xSemaphoreTake(st3m_fb_copy_lock, portMAX_DELAY); - int disp_stride = 240 * bits / 8; - if ((st3m_gfx_fb_width == 240) && (drawlist->blit_x == 0)) { - int blit_offset = st3m_gfx_blit_y * 240 * bits / 8; - int overlap = (st3m_gfx_blit_y + 240) - st3m_gfx_fb_height; - - if (overlap > 0) { - // vertical overlap, 2 memcpys - int start_scans = 240 - overlap; - memcpy(st3m_fb_copy, st3m_fb + blit_offset, - start_scans * disp_stride); - memcpy(st3m_fb_copy + start_scans * disp_stride, st3m_fb, - overlap * disp_stride); - } else { // best case - memcpy(st3m_fb_copy, st3m_fb + blit_offset, - 240 * disp_stride); - } - } else { - int fb_stride = st3m_gfx_fb_width * bits / 8; - int scan_offset = drawlist->blit_x * bits / 8; - int scan_overlap = (drawlist->blit_x + 240) - st3m_gfx_fb_width; - if (scan_overlap <= 0) { // only vertical wrap-around - int blit_offset = - (st3m_gfx_blit_y * 240 + drawlist->blit_x) * bits / 8; - int overlap = (st3m_gfx_blit_y + 240) - st3m_gfx_fb_height; - if (overlap <= 0) overlap = 0; - - int start_scans = 240 - overlap; - for (int i = 0; i < start_scans; i++) - memcpy(st3m_fb_copy + i * disp_stride, - st3m_fb + blit_offset + i * fb_stride, - disp_stride); - for (int i = 0; i < overlap; i++) - memcpy(st3m_fb_copy + (i + start_scans) * disp_stride, - st3m_fb + (drawlist->blit_x * bits / 8) + - i * fb_stride, - disp_stride); - } else { // generic case, handles both horizontal and vertical - // wrap-around - int start_bit = 240 - scan_overlap; - - int blit_offset = (st3m_gfx_blit_y)*fb_stride; - int overlap = (st3m_gfx_blit_y + 240) - st3m_gfx_fb_height; - if (overlap <= 0) overlap = 0; - - int start_scans = 240 - overlap; - int start_bytes = start_bit * bits / 8; - int scan_overlap_bytes = scan_overlap * bits / 8; - - for (int i = 0; i < start_scans; i++) - memcpy( - st3m_fb_copy + i * disp_stride, - st3m_fb + blit_offset + i * fb_stride + scan_offset, - start_bytes); - for (int i = 0; i < overlap; i++) - memcpy(st3m_fb_copy + (i + start_scans) * disp_stride, - st3m_fb + scan_offset + i * fb_stride, - start_bytes); - - // second pass over scanlines, filling in second half (which - // is wrapped to start of fb-scans) - for (int i = 0; i < start_scans; i++) - memcpy(st3m_fb_copy + i * disp_stride + start_bytes, - st3m_fb + blit_offset + i * fb_stride, - scan_overlap_bytes); - for (int i = 0; i < overlap; i++) - memcpy(st3m_fb_copy + (i + start_scans) * disp_stride + - start_bytes, - st3m_fb + i * fb_stride, scan_overlap_bytes); - } - } - xSemaphoreGive(st3m_fb_copy_lock); - xSemaphoreGive(st3m_fb_lock); - xQueueSend(user_ctx_blitq, &desc_no, portMAX_DELAY); - } -#endif - - st3m_counter_rate_sample(&rast_rate); - float rate = 1000000.0 / st3m_counter_rate_average(&rast_rate); - smoothed_fps = smoothed_fps * 0.6 + 0.4 * rate; - } -} void st3m_gfx_flow3r_logo(Ctx *ctx, float x, float y, float dim) { static int frameno = 0; @@ -862,9 +218,8 @@ void st3m_gfx_show_textview(st3m_gfx_textview_t *tv) { return; } - Ctx *ctx = st3m_gfx_ctx(st3m_gfx_default); + Ctx *ctx = tildagon_gfx_ctx(); - st3m_gfx_fbconfig(240, 240, 0, 0); ctx_save(ctx); // Draw background. @@ -910,232 +265,19 @@ void st3m_gfx_show_textview(st3m_gfx_textview_t *tv) { ctx_restore(ctx); - st3m_gfx_end_frame(ctx); + tildagon_end_frame(ctx); } -static void set_pixels_ctx(Ctx *ctx, void *user_data, int x, int y, int w, - int h, void *buf) { - uint16_t *src = buf; - for (int scan = y; scan < y + h; scan++) { - uint16_t *dst = (uint16_t *)&st3m_fb[(scan * 240 + x) * 2]; - for (int u = 0; u < w; u++) *(dst++) = *(src++); - } +float st3m_gfx_fps(void) { return smoothed_fps; } +void st3m_gfx_fps_update (void) +{ + st3m_counter_rate_sample(&rast_rate); + float rate = 1000000.0 / st3m_counter_rate_average(&rast_rate); + smoothed_fps = smoothed_fps * 0.6 + 0.4 * rate; } -void st3m_gfx_init(void) { - // Make sure we're not being re-initialized. +void st3m_gfx_init(void) { st3m_counter_rate_init(&rast_rate); - flow3r_bsp_display_init(); - - // Create drawlist ctx queues. - user_ctx_freeq = xQueueCreate(N_DRAWLISTS, sizeof(int)); - assert(user_ctx_freeq != NULL); - user_ctx_rastq = xQueueCreate(1, sizeof(int)); - assert(user_ctx_rastq != NULL); - user_ctx_blitq = xQueueCreate(1, sizeof(int)); - assert(user_ctx_blitq != NULL); - -#if CONFIG_FLOW3R_CTX_FLAVOUR_FULL - st3m_osd_lock = xSemaphoreCreateMutex(); -#endif - st3m_fb_lock = xSemaphoreCreateMutex(); - st3m_fb_copy_lock = xSemaphoreCreateMutex(); - - ctx = ctx_new_cb(FLOW3R_BSP_DISPLAY_WIDTH, FLOW3R_BSP_DISPLAY_HEIGHT, - CTX_FORMAT_RGB565_BYTESWAPPED, set_pixels_ctx, NULL, NULL, - NULL, sizeof(scratch), scratch, - CTX_FLAG_HASH_CACHE | CTX_FLAG_KEEP_DATA); - assert(ctx != NULL); - - // Setup rasterizers for frame buffer formats - fb_ctx = ctx_new_for_framebuffer( - st3m_fb, FLOW3R_BSP_DISPLAY_WIDTH, FLOW3R_BSP_DISPLAY_HEIGHT, - FLOW3R_BSP_DISPLAY_WIDTH * 2, CTX_FORMAT_RGB565_BYTESWAPPED); - assert(fb_ctx != NULL); -#if CONFIG_FLOW3R_CTX_FLAVOUR_FULL - osd_ctx = ctx_new_for_framebuffer( - st3m_fb2, FLOW3R_BSP_DISPLAY_WIDTH, FLOW3R_BSP_DISPLAY_HEIGHT, - FLOW3R_BSP_DISPLAY_WIDTH * 4, CTX_FORMAT_RGBA8); - assert(osd_ctx != NULL); - - st3m_gfx_viewport_transform(osd_ctx, 0); - - ctx_set_texture_source(osd_ctx, fb_ctx); - ctx_set_texture_cache(osd_ctx, fb_ctx); - ctx_set_texture_source(ctx, fb_ctx); - ctx_set_texture_cache(ctx, fb_ctx); - -#endif - - // Setup user_ctx descriptor. - for (int i = 0; i < N_DRAWLISTS; i++) { - drawlists[i].user_ctx = ctx_new_drawlist(FLOW3R_BSP_DISPLAY_WIDTH, - FLOW3R_BSP_DISPLAY_HEIGHT); - assert(drawlists[i].user_ctx != NULL); - ctx_set_texture_cache(drawlists[i].user_ctx, fb_ctx); - - BaseType_t res = xQueueSend(user_ctx_freeq, &i, 0); - assert(res == pdTRUE); - } - - // Start rasterization, scan-out - BaseType_t res = - xTaskCreatePinnedToCore(st3m_gfx_rast_task, "gfx-rast", 8192, NULL, - ESP_TASK_PRIO_MIN + 1, &graphics_rast_task, 0); - assert(res == pdPASS); - -#if ST3M_GFX_BLIT_TASK - res = xTaskCreate(st3m_gfx_blit_task, "gfx-blit", 2048, NULL, - ESP_TASK_PRIO_MIN + 2, &graphics_blit_task); - assert(res == pdPASS); -#endif -} -static int last_descno = 0; -static Ctx *st3m_gfx_drawctx_free_get(TickType_t ticks_to_wait) { - BaseType_t res = xQueueReceive(user_ctx_freeq, &last_descno, ticks_to_wait); - if (res != pdTRUE) return NULL; - - st3m_gfx_drawlist *drawlist = &drawlists[last_descno]; - st3m_gfx_mode set_mode = _st3m_gfx_mode ? _st3m_gfx_mode : default_mode; - drawlist->mode = set_mode; - - if (set_mode & st3m_gfx_direct_ctx) { - while (!uxSemaphoreGetCount(st3m_fb_lock)) vTaskDelay(0); - - if ((set_mode & st3m_gfx_smart_redraw)) { - ctx_start_frame(ctx); - ctx_save(ctx); - return ctx; - } - - return fb_ctx; - } - - return drawlist->user_ctx; -} - -static void st3m_gfx_pipe_put(void) { -#if CONFIG_FLOW3R_CTX_FLAVOUR_FULL - st3m_gfx_drawlist *drawlist = &drawlists[last_descno]; - drawlist->osd_x0 = _st3m_osd_x0; - drawlist->osd_y0 = _st3m_osd_y0; - drawlist->osd_x1 = _st3m_osd_x1; - drawlist->osd_y1 = _st3m_osd_y1; - drawlist->blit_x = st3m_gfx_blit_x; - drawlist->blit_y = st3m_gfx_blit_y; -#endif - xQueueSend(user_ctx_rastq, &last_descno, portMAX_DELAY); -} - -static Ctx *st3m_gfx_ctx_int(st3m_gfx_mode mode); -void st3m_gfx_end_frame(Ctx *ctx) { - ctx_restore(ctx); -#if CONFIG_FLOW3R_CTX_FLAVOUR_FULL - if (ctx == st3m_gfx_ctx_int(st3m_gfx_osd)) { - xSemaphoreGive(st3m_osd_lock); - return; - } -#endif - st3m_gfx_mode set_mode = _st3m_gfx_mode ? _st3m_gfx_mode : default_mode; - if ((set_mode & st3m_gfx_smart_redraw) && (set_mode & st3m_gfx_direct_ctx)) - ctx_end_frame(ctx); - - st3m_gfx_pipe_put(); -} - -uint8_t st3m_gfx_pipe_available(void) { - st3m_gfx_mode set_mode = _st3m_gfx_mode ? _st3m_gfx_mode : default_mode; - if ((set_mode & st3m_gfx_EXPERIMENTAL_think_per_draw) && - (smoothed_fps > 13.0)) - return 1; - return uxQueueMessagesWaiting(user_ctx_freeq) > _st3m_gfx_low_latency; -} - -uint8_t st3m_gfx_pipe_full(void) { - st3m_gfx_mode set_mode = _st3m_gfx_mode ? _st3m_gfx_mode : default_mode; - if ((set_mode & st3m_gfx_EXPERIMENTAL_think_per_draw) && - (smoothed_fps > 13.0)) - return 0; - return uxQueueSpacesAvailable(user_ctx_rastq) == 0; -} - -void st3m_gfx_flush(int timeout_ms) { - ESP_LOGW(TAG, "Pipeline flush/reset requested..."); - - // Drain all workqs and freeqs. - xQueueReset(user_ctx_freeq); - xQueueReset(user_ctx_rastq); - - // Delay, making sure pipeline tasks have returned all used descriptors. One - // second is enough to make sure we've processed everything. - vTaskDelay(timeout_ms / portTICK_PERIOD_MS); - - // And drain again. - xQueueReset(user_ctx_freeq); - -#if CONFIG_FLOW3R_CTX_FLAVOUR_FULL - _st3m_osd_x0 = 0; - _st3m_osd_y0 = 0; - _st3m_osd_x1 = 0; - _st3m_osd_y1 = 0; -#endif - - for (int i = 0; i < N_DRAWLISTS; i++) { - ctx_drawlist_clear(drawlists[i].user_ctx); - BaseType_t res = xQueueSend(user_ctx_freeq, &i, 0); - assert(res == pdTRUE); - } - ESP_LOGW(TAG, "Pipeline flush/reset done."); -} - -#if CONFIG_FLOW3R_CTX_FLAVOUR_FULL -void st3m_gfx_overlay_clip(int x0, int y0, int x1, int y1) { - if (y1 < 0) y1 = 0; - if (y1 > FLOW3R_BSP_DISPLAY_HEIGHT) y1 = FLOW3R_BSP_DISPLAY_HEIGHT; - if (y0 < 0) y0 = 0; - if (y0 > FLOW3R_BSP_DISPLAY_HEIGHT) y0 = FLOW3R_BSP_DISPLAY_HEIGHT; - - if (x1 < 0) x1 = 0; - if (x1 > FLOW3R_BSP_DISPLAY_WIDTH) x1 = FLOW3R_BSP_DISPLAY_WIDTH; - if (x0 < 0) x0 = 0; - if (x0 > FLOW3R_BSP_DISPLAY_WIDTH) x0 = FLOW3R_BSP_DISPLAY_WIDTH; - - if ((x1 < x0) || (y1 < y0)) { - _st3m_osd_x0 = _st3m_osd_y0 = _st3m_osd_x1 = _st3m_osd_y1 = 0; - } else { - _st3m_osd_x0 = x0; - _st3m_osd_y0 = y0; - _st3m_osd_x1 = x1; - _st3m_osd_y1 = y1; - } -} -#endif - -void st3m_gfx_fbconfig(int width, int height, int blit_x, int blit_y) { - if (width <= 0) width = st3m_gfx_fb_width; - if (height <= 0) height = st3m_gfx_fb_height; - st3m_gfx_mode set_mode = _st3m_gfx_mode ? _st3m_gfx_mode : default_mode; - int bits = st3m_gfx_bpp(set_mode); - if (width > CTX_MAX_SCANLINE_LENGTH) width = CTX_MAX_SCANLINE_LENGTH; - if ((width * height * bits) / 8 > (240 * 240 * 4)) - height = 240 * 240 * 4 * 8 / (width * bits); - blit_x %= width; - blit_y %= height; - - if ((st3m_gfx_fb_width != width) || (st3m_gfx_fb_height != height)) { - st3m_gfx_fb_width = width; - st3m_gfx_fb_height = height; - st3m_gfx_geom_dirty++; - } - st3m_gfx_blit_x = blit_x; - st3m_gfx_blit_y = blit_y; -} - -void st3m_gfx_get_fbconfig(int *width, int *height, int *blit_x, int *blit_y) { - if (width) *width = st3m_gfx_fb_width; - if (height) *height = st3m_gfx_fb_height; - if (blit_x) *blit_x = st3m_gfx_blit_x; - if (blit_y) *blit_y = st3m_gfx_blit_y; } diff --git a/components/st3m/st3m_gfx.h b/components/st3m/st3m_gfx.h index faf4883..9cbc7a6 100644 --- a/components/st3m/st3m_gfx.h +++ b/components/st3m/st3m_gfx.h @@ -61,6 +61,8 @@ typedef enum { st3m_gfx_32bpp = 32, } st3m_gfx_mode; +void st3m_gfx_fps_update (void); + // sets the system graphics mode, this is the mode you get to // when calling st3m_gfx_set_mode(st3m_gfx_default); void st3m_gfx_set_default_mode(st3m_gfx_mode mode); diff --git a/drivers/gc9a01/display.c b/drivers/gc9a01/display.c index b3e961f..372900a 100644 --- a/drivers/gc9a01/display.c +++ b/drivers/gc9a01/display.c @@ -27,17 +27,55 @@ static mp_obj_t get_fps() { } static MP_DEFINE_CONST_FUN_OBJ_0(get_fps_obj, get_fps); +#define TILDAGON_DISPLAY_WIDTH 240 +#define TILDAGON_DISPLAY_HEIGHT 240 + +EXT_RAM_BSS_ATTR +static uint8_t tildagon_fb[TILDAGON_DISPLAY_WIDTH * TILDAGON_DISPLAY_HEIGHT * 2]; +static Ctx *tildagon_ctx = NULL; + +Ctx *tildagon_gfx_ctx(void) +{ + if (tildagon_ctx == NULL) + { + tildagon_ctx = ctx_new_for_framebuffer (tildagon_fb, TILDAGON_DISPLAY_WIDTH, TILDAGON_DISPLAY_HEIGHT, TILDAGON_DISPLAY_WIDTH * 2, CTX_FORMAT_RGB565_BYTESWAPPED); + } + return tildagon_ctx; +} + +void tildagon_start_frame(Ctx *ctx) +{ + int32_t offset_x = FLOW3R_BSP_DISPLAY_WIDTH / 2; + int32_t offset_y = FLOW3R_BSP_DISPLAY_HEIGHT / 2; + + ctx_save (ctx); + ctx_identity (ctx); + ctx_apply_transform (ctx, 1.0f, 0.0f, offset_x, 0.0f, 1.0f, offset_y, 0.0f, 0.0f, 1.0f); +} static mp_obj_t get_ctx() { - Ctx *ctx = st3m_gfx_ctx(st3m_gfx_default); + Ctx *ctx = tildagon_gfx_ctx(); + assert (ctx); + tildagon_start_frame (ctx); return mp_ctx_from_ctx(ctx); } static MP_DEFINE_CONST_FUN_OBJ_0(get_ctx_obj, get_ctx); +void tildagon_blit_fb (void) +{ + flow3r_bsp_display_send_fb(tildagon_fb, 16); +} + +void tildagon_end_frame(Ctx *ctx) +{ + ctx_restore (ctx); + tildagon_blit_fb (); + st3m_gfx_fps_update (); +} static mp_obj_t end_frame(mp_obj_t ctx) { mp_ctx_obj_t *self = MP_OBJ_TO_PTR(ctx); - st3m_gfx_end_frame(self->ctx); + tildagon_end_frame (self->ctx); return ctx; } static MP_DEFINE_CONST_FUN_OBJ_1(end_frame_obj, end_frame); diff --git a/drivers/gc9a01/mp_uctx.c b/drivers/gc9a01/mp_uctx.c index 29e5a0d..5b425af 100644 --- a/drivers/gc9a01/mp_uctx.c +++ b/drivers/gc9a01/mp_uctx.c @@ -237,6 +237,7 @@ MP_CTX_COMMON_FUN_0(stroke); MP_CTX_COMMON_FUN_0(paint); MP_CTX_COMMON_FUN_4F(linear_gradient); +MP_CTX_COMMON_FUN_4F(conic_gradient); MP_CTX_COMMON_FUN_6F(radial_gradient); MP_CTX_COMMON_FUN_3F(logo); @@ -852,6 +853,7 @@ static const mp_rom_map_elem_t mp_ctx_locals_dict_table[] = { #endif MP_CTX_METHOD(preserve), MP_CTX_METHOD(linear_gradient), + MP_CTX_METHOD(conic_gradient), MP_CTX_METHOD(radial_gradient), MP_CTX_METHOD(add_stop), MP_CTX_METHOD(line_dash), @@ -1061,6 +1063,52 @@ const mp_obj_module_t mp_module_ctx = { .globals = (mp_obj_dict_t *)&mp_ctx_module_globals, }; + +#include "py/stream.h" +#include "extmod/vfs.h" + +int mp_ctx_vfs_load_file (const char *path, + unsigned char **contents, + long *length, + long max_length) +{ + mp_obj_t filename = mp_obj_new_str(path, strlen(path)); + mp_obj_t open_args[2] = {filename, + MP_OBJ_NEW_QSTR(MP_QSTR_rb)}; + mp_obj_t stat = mp_vfs_stat(filename); + mp_obj_tuple_t *l = MP_OBJ_TO_PTR(stat); + mp_obj_t file = mp_vfs_open(MP_ARRAY_SIZE(open_args), &open_args[0], + (mp_map_t*)&mp_const_empty_map); + const mp_stream_p_t *stream_p = mp_get_stream(file); + if (!stream_p) + { + mp_stream_close (file); + return -1; + } + int errcode = 0; + + long len = mp_obj_get_int(l->items[6]); + if (len > max_length) { + mp_stream_close (file); + return -1; + } + unsigned char *buf = ctx_malloc (len); + if (!buf) + { + mp_stream_close (file); + return -1; + } + mp_stream_rw(file, buf, len, &errcode, MP_STREAM_RW_READ | MP_STREAM_RW_ONCE); + if (errcode != 0) { + mp_raise_OSError(errcode); + } + *contents = buf; + *length = len; + mp_stream_close (file); + return 0; +} + + /* This is a special macro that will make MicroPython aware of this module */ /* clang-format off */ -MP_REGISTER_MODULE(MP_QSTR_ctx, mp_module_ctx); \ No newline at end of file +MP_REGISTER_MODULE(MP_QSTR_ctx, mp_module_ctx);