diff --git a/components/ctx/ctx.h b/components/ctx/ctx.h index aff487f9c3c26915ac98f0fa92771d5c381c1ae8..f5e75f7935ee0b338b0501283b4682f3b974a6af 100644 --- a/components/ctx/ctx.h +++ b/components/ctx/ctx.h @@ -1,4 +1,4 @@ -/* ctx git commit: 77eceddd */ +/* ctx git commit: 10d7cba4 */ /* * ctx.h is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -47,6 +47,7 @@ extern "C" { #include <stdint.h> #include <string.h> +#include <strings.h> #include <stdio.h> typedef struct _Ctx Ctx; @@ -85,10 +86,23 @@ typedef struct _CtxEntry CtxEntry; * @ctx: a ctx context. * @count: return location for length of drawlist * + * The returned pointer is only valid as long as no further drawing has been + * done. + * * Returns a read only pointer to the first entry of the contexts drawlist. */ const CtxEntry *ctx_get_drawlist (Ctx *ctx, int *count); +/** + * ctx_drawlist_force_count: + * @ctx: a ctx context + * @count: new count to set, must be lower than the current count. + * + * Shortens the length of the internal drawlist, dropping the last + * items. + */ +void ctx_drawlist_force_count (Ctx *ctx, int count); + /** * ctx_new_for_drawlist: * @@ -425,6 +439,12 @@ void ctx_font (Ctx *ctx, const char *font); */ void ctx_font_family (Ctx *ctx, const char *font_family); +int +ctx_font_extents (Ctx *ctx, + float *ascent, + float *descent, + float *line_gap); + /** * ctx_parse: * @@ -639,32 +659,6 @@ enum _CtxPixelFormat CTX_FORMAT_GRAY2, // 16 // matching flags CTX_FORMAT_YUV420, // 17 CTX_FORMAT_GRAY4=32, // to match flags - CTX_FORMAT_CBRLE_1, // CBRLE constant bitrate runlength encoded 1 bpp - CTX_FORMAT_CBRLE_2, // these formats are used internally with the CBRLE - CTX_FORMAT_CBRLE_3, // flag passed to the cb backend. (should be exteneded - CTX_FORMAT_CBRLE_4, // to also apply to the tiled backends). - CTX_FORMAT_CBRLE_5, // - CTX_FORMAT_CBRLE_6, // When used in other backends they determine automatically - CTX_FORMAT_CBRLE_7, // which specific CBRLE format to use, based on available - CTX_FORMAT_CBRLE_8, // memory budget. - CTX_FORMAT_CBRLE_9, // - CTX_FORMAT_CBRLE_10, // The specific formats are also used for testing and - CTX_FORMAT_CBRLE_11, // improving the visual quality vs performance loss - CTX_FORMAT_CBRLE_12, // when lossy. - CTX_FORMAT_CBRLE_13, // - CTX_FORMAT_CBRLE_14, // - CTX_FORMAT_CBRLE_15, // - CTX_FORMAT_CBRLE_16, // - CTX_FORMAT_CBRLE_17, // - CTX_FORMAT_CBRLE_18, // - CTX_FORMAT_CBRLE_19, // - CTX_FORMAT_CBRLE_20, // - CTX_FORMAT_CBRLE_21, // - CTX_FORMAT_CBRLE_22, // - CTX_FORMAT_CBRLE_23, // - CTX_FORMAT_CBRLE_24, // - CTX_FORMAT_CBRLE_32, // - CTX_FORMAT_CBRLE, // CTX_FORMAT_BGRA8Z, // CTX_FORMAT_RGBA8_SEPARATE_ALPHA, // }; @@ -742,6 +736,25 @@ uint32_t ctx_hasher_get_hash (Ctx *ctx, int col, int row); int ctx_utf8_strlen (const char *s); int ctx_utf8_len (const unsigned char first_byte); +void ctx_deferred_scale (Ctx *ctx, const char *name, float x, float y); +void ctx_deferred_translate (Ctx *ctx, const char *name, float x, float y); +void ctx_deferred_move_to (Ctx *ctx, const char *name, float x, float y); +void ctx_deferred_rel_line_to (Ctx *ctx, const char *name, float x, float y); +void ctx_deferred_rel_move_to (Ctx *ctx, const char *name, float x, float y); +void ctx_deferred_rectangle (Ctx *ctx, const char *name, float x, float y, + float width, float height); +void ctx_resolve (Ctx *ctx, const char *name, + void (*set_dim) (Ctx *ctx, + void *userdata, + const char *name, + int count, + float *x, + float *y, + float *width, + float *height), + void *userdata); + + #ifndef CTX_BABL #ifdef _BABL_H #define CTX_BABL 1 @@ -812,7 +825,6 @@ typedef enum CtxFlags { CTX_FLAG_KEEP_DATA = 1 << 8, // keep existing fb-data instead of doing an initial clear CTX_FLAG_INTRA_UPDATE = 1 << 9, CTX_FLAG_STAY_LOW = 1 << 10, // stay with the color fidelity drop in lowfi - CTX_FLAG_CBRLE = 1 << 11 // use possibly lossy RLE encoded scanlines with constant bitrate } CtxFlags; @@ -1132,6 +1144,13 @@ struct _CtxIntRectangle { int width; int height; }; +typedef struct _CtxFloatRectangle CtxFloatRectangle; +struct _CtxFloatRectangle { + float x; + float y; + float width; + float height; +}; void ctx_quit (Ctx *ctx); int ctx_has_quit (Ctx *ctx); @@ -1372,6 +1391,7 @@ typedef enum CtxBackendType { CTX_BACKEND_CAIRO, CTX_BACKEND_SDL, CTX_BACKEND_DRAWLIST, + CTX_BACKEND_PDF, } CtxBackendType; CtxBackendType ctx_backend_type (Ctx *ctx); @@ -2215,7 +2235,10 @@ void ctx_windowtitle (Ctx *ctx, const char *text); struct _CtxBackend { Ctx *ctx; + void (*process) (Ctx *ctx, CtxCommand *entry); + + /* for interactive/event-handling backends */ void (*start_frame) (Ctx *ctx); void (*end_frame) (Ctx *ctx); @@ -2230,6 +2253,7 @@ struct _CtxBackend void (*destroy) (void *backend); /* the free pointers are abused as the differentiatior between different backends */ CtxFlags flags; + CtxBackendType type; void *user_data; // not used by ctx core }; @@ -2447,6 +2471,7 @@ const char *ctx_path_get_media_type (const char *path); typedef enum { CTX_MEDIA_TYPE_NONE=0, CTX_MEDIA_TYPE_TEXT, + CTX_MEDIA_TYPE_HTML, CTX_MEDIA_TYPE_IMAGE, CTX_MEDIA_TYPE_VIDEO, CTX_MEDIA_TYPE_AUDIO, @@ -2463,6 +2488,8 @@ float ctx_term_get_cell_height (Ctx *ctx); Ctx * ctx_new_pdf (const char *path, float width, float height); void ctx_render_pdf (Ctx *ctx, const char *path); +const char *ctx_str_decode (uint32_t number); +uint32_t ctx_strhash (const char *str); #ifndef CTX_CODEC_CHAR //#define CTX_CODEC_CHAR '\035' @@ -2475,6 +2502,13 @@ void ctx_render_pdf (Ctx *ctx, const char *path); #define assert(a) #endif +void _ctx_write_png (const char *dst_path, int w, int h, int num_chans, void *data); + + +void ctx_vt_write (Ctx *ctx, uint8_t byte); +int ctx_vt_has_data (Ctx *ctx); +int ctx_vt_read (Ctx *ctx); + #ifdef __cplusplus } #endif @@ -5012,6 +5046,7 @@ static inline CtxList *ctx_list_find_custom (CtxList *list, #define CTX_GRADIENT_CACHE 1 #endif + #ifndef CTX_FONTS_FROM_FILE #define CTX_FONTS_FROM_FILE 0 #endif @@ -5036,12 +5071,8 @@ static inline CtxList *ctx_list_find_custom (CtxList *list, #define CTX_CURRENT_PATH 1 #endif -#ifndef CTX_XML -#define CTX_XML 1 -#endif - -#ifndef CTX_CLIENTS -#define CTX_CLIENTS 0 +#ifndef CTX_VT +#define CTX_VT 0 #endif /* when ctx_math is defined, which it is by default, we use ctx' own @@ -5077,7 +5108,7 @@ static inline CtxList *ctx_list_find_custom (CtxList *list, #endif #ifndef CTX_MIN_EDGE_LIST_SIZE -#define CTX_MIN_EDGE_LIST_SIZE 2048*2 +#define CTX_MIN_EDGE_LIST_SIZE 1024*4 #endif #ifndef CTX_RASTERIZER_AA @@ -5102,7 +5133,7 @@ static inline CtxList *ctx_list_find_custom (CtxList *list, // // for desktop-use this should be fully dynamic, possibly // with chained pools, gradients are stored here. -#define CTX_STRINGPOOL_SIZE 1000 // +#define CTX_STRINGPOOL_SIZE 2000 // #endif #ifndef CTX_32BIT_SEGMENTS @@ -5273,6 +5304,10 @@ static inline CtxList *ctx_list_find_custom (CtxList *list, // for a ~15% overall performance hit #endif +#ifndef CTX_RESOLVED_FONTS +#define CTX_RESOLVED_FONTS 8 // how many font-strings to cache the resolution for in a static + // hash-table +#endif /* by including ctx-font-regular.h, or ctx-font-mono.h the * built-in fonts using ctx drawlist encoding is enabled @@ -5428,11 +5463,11 @@ static inline CtxList *ctx_list_find_custom (CtxList *list, #endif #ifndef CTX_GLYPH_CACHE_SIZE -#define CTX_GLYPH_CACHE_SIZE 128 +#define CTX_GLYPH_CACHE_SIZE 256 #endif #ifndef CTX_MAX_STATES -#define CTX_MAX_STATES 10 +#define CTX_MAX_STATES 16 #endif #ifndef CTX_MAX_EDGES @@ -5679,22 +5714,45 @@ static inline CtxList *ctx_list_find_custom (CtxList *list, #define CTX_PDF 0 #endif -#define uncompress tinf_uncompress -#define Z_OK TINF_OK -#define Z_BUF_ERROR TINF_BUF_ERROR -#define Z_DATA_ERROR TINF_DATA_ERROR - -#ifndef CTX_ENABLE_CBRLE -#define CTX_ENABLE_CBRLE 0 +#if CTX_IMAGE_WRITE +#else +#define MINIZ_NO_ARCHIVE_APIS 1 +#define MINIZ_NO_DEFLATE_APIS 1 #endif +//#define MINIZ_NO_ARCHIVE_WRITING_APIS 1 +#define MINIZ_NO_STDIO 1 + + +//#define uncompress tinf_uncompress +//#define Z_OK TINF_OK +//#define Z_BUF_ERROR TINF_BUF_ERROR +//#define Z_DATA_ERROR TINF_DATA_ERROR #ifndef CTX_RAW_KB_EVENTS #define CTX_RAW_KB_EVENTS 1 #endif + #ifndef CTX_BAREMETAL #define CTX_BAREMETAL 0 +#endif + +#if CTX_IMPLEMENTATION +#ifndef SQUOZE_IMPLEMENTATION +#define SQUOZE_IMPLEMENTATION 1 +#define SQUOZE_LIMIT_IMPLEMENTATIONS 1 +#define SQUOZE_IMPLEMENTATION_32_UTF8 1 +#define SQUOZE_USE_INTERN 0 +#endif +#endif + +#ifndef CTX_PTY +#define CTX_PTY 1 +#endif + +#ifndef CTX_STROKE_1PX +#define CTX_STROKE_1PX 1 #endif /* Copyright (C) 2020 Øyvind Kolås <pippin@gimp.org> @@ -5974,7 +6032,7 @@ int _ctx_is_rasterizer (Ctx *ctx); int ctx_color (Ctx *ctx, const char *string); typedef struct _CtxState CtxState; -CtxColor *ctx_color_new (); +CtxColor *ctx_color_new (void); CtxState *ctx_get_state (Ctx *ctx); void ctx_color_get_rgba (CtxState *state, CtxColor *color, float *out); void ctx_color_set_rgba (CtxState *state, CtxColor *color, float r, float g, float b, float a); @@ -6048,203 +6106,200 @@ int ctx_pixel_format_ebpp (CtxPixelFormat format); #endif #ifndef __CTX_CONSTANTS #define __CTX_CONSTANTS -#define CTX_action 971612354u -#define CTX_addStop 3805374936u -#define CTX_alphabetic 2558771929u -#define CTX_aqua 109634u -#define CTX_arc 7298u -#define CTX_arcTo 4138935887u -#define CTX_beginPath 2384638508u -#define CTX_bevel 25538884u -#define CTX_black 23268100u -#define CTX_blend 9317124u -#define CTX_blending 3402343403u -#define CTX_blendMode 3450578624u -#define CTX_blue 371460u -#define CTX_bottom 905225156u -#define CTX_cap 32838u -#define CTX_center 1219785030u -#define CTX_clear 37825286u -#define CTX_clip 1067782u -#define CTX_closePath 3625577848u -#define CTX_cmyk 772934u -#define CTX_cmyka 2870086u -#define CTX_cmykaS 3116500921u -#define CTX_cmykS 934005574u -#define CTX_cmykSpace 2661208064u -#define CTX_color 38757318u -#define CTX_colorSpace 2624117287u -#define CTX_compositingMode 3625102151u -#define CTX_copy 1672134u -#define CTX_currentColor 2944012414u -#define CTX_curveTo 3536162037u -#define CTX_cyan 921158u -#define CTX_darken 950767688u -#define CTX_defineFont 2930064664u -#define CTX_defineGlyph 3698027829u -#define CTX_defineTexture 4201008335u -#define CTX_destinationAtop 3609906960u -#define CTX_destinationIn 2718725020u -#define CTX_destinationOut 3944490553u -#define CTX_destinationOver 2378926016u -#define CTX_deviceCMYK 4096729420u -#define CTX_deviceRGB 3975717407u -#define CTX_difference 2530251746u -#define CTX_done 357320u -#define CTX_drgb 146568u -#define CTX_drgba 2243720u -#define CTX_drgbaS 2541895879u -#define CTX_drgbS 933379208u -#define CTX_drgbSpace 3152489160u -#define CTX_end 9098u -#define CTX_endFrame 3482141353u -#define CTX_endGroup 3639210663u -#define CTX_evenOdd 4065502508u -#define CTX_exit 1330698u -#define CTX_extend 298165770u -#define CTX_fill 811596u -#define CTX_fillRect 2617922007u -#define CTX_fillRule 2727819936u -#define CTX_font 1340364u -#define CTX_fontSize 2516141542u -#define CTX_fuchsia 3356500405u -#define CTX_globalAlpha 3503999095u -#define CTX_glyph 17877774u -#define CTX_gradientAddStop 2707733066u -#define CTX_gray 1641614u -#define CTX_graya 3738766u -#define CTX_grayaS 3152913809u -#define CTX_grayS 934874254u -#define CTX_green 29699214u -#define CTX_hanging 3379012612u -#define CTX_height 1359432016u -#define CTX_horLineTo 2768557894u -#define CTX_hue 11600u -#define CTX_identity 4244560551u -#define CTX_ideographic 4062138887u -#define CTX_imageSmoothing 3391439578u -#define CTX_join 936916u -#define CTX_kerningPair 3655936472u -#define CTX_lab 4184u -#define CTX_laba 69720u -#define CTX_labaS 933302360u -#define CTX_labS 29167704u -#define CTX_lch 16600u -#define CTX_lcha 82136u -#define CTX_lchaS 933314776u -#define CTX_lchS 29180120u -#define CTX_left 1323352u -#define CTX_lighten 2243427702u -#define CTX_lime 354904u -#define CTX_linearGradient 2530643087u -#define CTX_lineCap 4099906770u -#define CTX_lineDash 2275747153u -#define CTX_lineDashOffset 2164798257u -#define CTX_lineHeight 2180215986u -#define CTX_lineJoin 3149521206u -#define CTX_lineTo 3995194545u -#define CTX_lineWidth 4067116285u -#define CTX_lower 38124504u -#define CTX_lower 38124504u -#define CTX_lowerBottom 4236857599u -#define CTX_magenta 2383173845u -#define CTX_maroon 972001370u -#define CTX_maximize 4029307923u -#define CTX_middle 360981082u -#define CTX_miter 38117978u -#define CTX_miterLimit 3784823268u -#define CTX_moveTo 3135948887u -#define CTX_multiply 2379318058u -#define CTX_navy 1683548u -#define CTX_newPage 3875814849u -#define CTX_newPath 2442450175u -#define CTX_newState 3540663677u -#define CTX_none 357340u -#define CTX_nonzero 2230085415u -#define CTX_normal 808293340u -#define CTX_olive 11946782u -#define CTX_paint 42879072u -#define CTX_purple 361796960u -#define CTX_quadTo 3916306495u -#define CTX_radialGradient 3218566169u -#define CTX_raise 11749476u -#define CTX_raise 11749476u -#define CTX_raiseTop 3277017940u -#define CTX_rect 1317220u -#define CTX_rectangle 4111149391u -#define CTX_red 8548u -#define CTX_relArcTo 2940381656u -#define CTX_relCurveTo 3745640049u -#define CTX_relHorLineTo 2661057467u -#define CTX_relLineTo 2437091951u -#define CTX_relMoveTo 2527491593u -#define CTX_relQuadTo 3961311908u -#define CTX_relSmoothqTo 2913202089u -#define CTX_relSmoothTo 3458671695u -#define CTX_relVerLineTo 3868849192u -#define CTX_restore 2936409475u -#define CTX_rgb 4580u -#define CTX_rgba 70116u -#define CTX_rgbaS 933302756u -#define CTX_rgbS 29168100u -#define CTX_rgbSpace 3275074815u -#define CTX_right 42482276u -#define CTX_rotate 377594852u -#define CTX_round 9350116u -#define CTX_roundRectangle 3688082153u -#define CTX_save 372838u -#define CTX_scale 11274470u -#define CTX_screen 950374630u -#define CTX_setFontSize 2794810212u -#define CTX_setLineCap 3062640202u -#define CTX_setLineJoin 3876390174u -#define CTX_setLineWidth 3835759450u -#define CTX_shadowBlur 3119062524u -#define CTX_shadowColor 3795289804u -#define CTX_shadowOffsetX 4134163333u -#define CTX_shadowOffsetY 3519010566u -#define CTX_silver 1219912294u -#define CTX_smoothQuadTo 4024936051u -#define CTX_smoothTo 3997790061u -#define CTX_sourceAtop 3201391080u -#define CTX_sourceIn 3513756343u -#define CTX_sourceOut 4217691207u -#define CTX_sourceOver 4071274055u -#define CTX_sourceTransform 3608891648u -#define CTX_square 373402726u -#define CTX_start 43126054u -#define CTX_startFrame 4232434924u -#define CTX_startGroup 4199711715u -#define CTX_stroke 359634214u -#define CTX_strokeRect 3918462693u -#define CTX_strokeSource 3387288669u -#define CTX_strokeText 4077103477u -#define CTX_teal 788840u -#define CTX_terminate 2614989576u -#define CTX_text 1360232u -#define CTX_textAlign 4087119491u -#define CTX_textBaseline 3671121506u -#define CTX_textDirection 2303324726u -#define CTX_texture 2603404275u -#define CTX_title 11313768u -#define CTX_top 33768u -#define CTX_transform 3717307466u -#define CTX_translate 2746303805u -#define CTX_transparent 3143361910u -#define CTX_unmaximize 3478656422u -#define CTX_userCMYK 4240023559u -#define CTX_userRGB 2839509677u -#define CTX_verLineTo 2881865279u -#define CTX_viewBox 3661895848u -#define CTX_white 11815470u -#define CTX_width 18096750u -#define CTX_winding 3743938776u -#define CTX_wrapLeft 2742686349u -#define CTX_wrapRight 2519274407u -#define CTX_x 48u -#define CTX_xor 37872u -#define CTX_y 50u -#define CTX_yellow 1575772530u +#define SQZ_action 191466746u +#define SQZ_addStop 70177936u +#define SQZ_alphabetic 2838306314u +#define SQZ_aqua 1635086787u +#define SQZ_arc 6517443u +#define SQZ_arcTo 2233202270u +#define SQZ_beginPath 1761844968u +#define SQZ_bevel 426445178u +#define SQZ_black 1346400376u +#define SQZ_blend 262745544u +#define SQZ_blending 3480977226u +#define SQZ_blendMode 1036946672u +#define SQZ_blue 1702194373u +#define SQZ_bottom 297379412u +#define SQZ_cap 7365063u +#define SQZ_center 3333013908u +#define SQZ_clear 730795960u +#define SQZ_clip 1885957319u +#define SQZ_closePath 3976743578u +#define SQZ_cmyk 1803120071u +#define SQZ_cmyka 3062872642u +#define SQZ_cmykaS 1515489614u +#define SQZ_cmykS 2970776438u +#define SQZ_cmykSpace 2158213956u +#define SQZ_color 4113199138u +#define SQZ_colorSpace 2978079760u +#define SQZ_compositingMode 1747631346u +#define SQZ_copy 2037411783u +#define SQZ_currentColor 3045233808u +#define SQZ_curveTo 4214308788u +#define SQZ_cyan 1851881927u +#define SQZ_darken 3637594256u +#define SQZ_defineFont 3455431226u +#define SQZ_defineGlyph 517698760u +#define SQZ_defineTexture 2541406396u +#define SQZ_destinationAtop 1762261548u +#define SQZ_destinationIn 1974899656u +#define SQZ_destinationOut 3450048454u +#define SQZ_destinationOver 3410395560u +#define SQZ_deviceCMYK 475280306u +#define SQZ_deviceRGB 2503222352u +#define SQZ_difference 2667908970u +#define SQZ_done 1701736393u +#define SQZ_drgb 1650946761u +#define SQZ_drgba 2890318204u +#define SQZ_drgbaS 284498328u +#define SQZ_drgbS 2734523996u +#define SQZ_drgbSpace 1678108998u +#define SQZ_end 6581963u +#define SQZ_endFrame 2989111914u +#define SQZ_endGroup 3382848678u +#define SQZ_evenOdd 3958460824u +#define SQZ_exit 1953069259u +#define SQZ_extend 2410480036u +#define SQZ_fill 1819044301u +#define SQZ_fillRect 844863334u +#define SQZ_fillRule 3536928922u +#define SQZ_font 1953394637u +#define SQZ_fontSize 3677341438u +#define SQZ_fuchsia 1388713974u +#define SQZ_globalAlpha 2486696782u +#define SQZ_glyph 4143870566u +#define SQZ_gradientAddStop 4202125454u +#define SQZ_gray 2036429519u +#define SQZ_graya 1057020702u +#define SQZ_grayaS 3143946106u +#define SQZ_grayS 217848836u +#define SQZ_green 2210334888u +#define SQZ_hanging 315820168u +#define SQZ_height 2644040974u +#define SQZ_horLineTo 310288004u +#define SQZ_hue 6649297u +#define SQZ_identity 1295847768u +#define SQZ_ideographic 483722678u +#define SQZ_imageSmoothing 3051511248u +#define SQZ_join 1852403669u +#define SQZ_kerningPair 2107287266u +#define SQZ_lab 6447577u +#define SQZ_laba 1633837529u +#define SQZ_labaS 3945150942u +#define SQZ_labS 1398956505u +#define SQZ_lch 6841305u +#define SQZ_lcha 1634231257u +#define SQZ_lchaS 1426017758u +#define SQZ_lchS 1399350233u +#define SQZ_left 1952867801u +#define SQZ_lighten 3832222246u +#define SQZ_lime 1701669337u +#define SQZ_linearGradient 1237241186u +#define SQZ_lineCap 2995475454u +#define SQZ_lineDash 1187484528u +#define SQZ_lineDashOffset 3122102242u +#define SQZ_lineHeight 1792134122u +#define SQZ_lineJoin 3798665176u +#define SQZ_lineTo 2181648726u +#define SQZ_lineWidth 1866819118u +#define SQZ_lower 3494639400u +#define SQZ_lowerBottom 3353231678u +#define SQZ_magenta 41683490u +#define SQZ_maroon 3330270796u +#define SQZ_maximize 2843333630u +#define SQZ_middle 2338157914u +#define SQZ_miter 2474552592u +#define SQZ_miterLimit 484429810u +#define SQZ_moveTo 2673163660u +#define SQZ_multiply 1455254328u +#define SQZ_navy 2037801437u +#define SQZ_newPage 2996263020u +#define SQZ_newPath 2761203478u +#define SQZ_newState 2752539242u +#define SQZ_none 1701736413u +#define SQZ_nonzero 2128027674u +#define SQZ_normal 1043559938u +#define SQZ_olive 1261573064u +#define SQZ_paint 3703690370u +#define SQZ_purple 245805398u +#define SQZ_quadTo 1586848216u +#define SQZ_radialGradient 2234069350u +#define SQZ_raise 1568723874u +#define SQZ_raiseTop 3049736746u +#define SQZ_rect 1952671205u +#define SQZ_rectangle 979653324u +#define SQZ_red 6579685u +#define SQZ_relArcTo 1740342202u +#define SQZ_relCurveTo 1444075664u +#define SQZ_relHorLineTo 1006817000u +#define SQZ_relLineTo 567984930u +#define SQZ_relMoveTo 70490176u +#define SQZ_relQuadTo 3275675052u +#define SQZ_relSmoothqTo 3903392508u +#define SQZ_relSmoothTo 3177482922u +#define SQZ_relVerLineTo 861219474u +#define SQZ_restore 3389756388u +#define SQZ_rgb 6449125u +#define SQZ_rgba 1633839077u +#define SQZ_rgbaS 1598860234u +#define SQZ_rgbS 1398958053u +#define SQZ_rgbSpace 2863441362u +#define SQZ_right 2455820278u +#define SQZ_rotate 91843306u +#define SQZ_round 2051052438u +#define SQZ_roundRectangle 124772996u +#define SQZ_save 1702257127u +#define SQZ_scale 1608340156u +#define SQZ_screen 1686881180u +#define SQZ_setFontSize 3424593464u +#define SQZ_setLineCap 2943395354u +#define SQZ_setLineJoin 697170840u +#define SQZ_setLineWidth 1869312910u +#define SQZ_shadowBlur 1269325404u +#define SQZ_shadowColor 3671019726u +#define SQZ_shadowOffsetX 1199273834u +#define SQZ_shadowOffsetY 2589386586u +#define SQZ_silver 1756290066u +#define SQZ_smoothQuadTo 241605252u +#define SQZ_smoothTo 509706880u +#define SQZ_sourceAtop 251620176u +#define SQZ_sourceIn 3850864476u +#define SQZ_sourceOut 1431529336u +#define SQZ_sourceOver 2655053588u +#define SQZ_sourceTransform 4288534266u +#define SQZ_square 133625030u +#define SQZ_start 4265770694u +#define SQZ_startFrame 4116075414u +#define SQZ_startGroup 329986940u +#define SQZ_stroke 1713463584u +#define SQZ_strokeRect 98241244u +#define SQZ_strokeSource 711534692u +#define SQZ_strokeText 3684637194u +#define SQZ_teal 1818322409u +#define SQZ_text 1954047465u +#define SQZ_textAlign 3477973996u +#define SQZ_textBaseline 1424973332u +#define SQZ_textDirection 185607982u +#define SQZ_texture 971666452u +#define SQZ_title 946515406u +#define SQZ_top 7368681u +#define SQZ_transform 3862209768u +#define SQZ_translate 3005601842u +#define SQZ_transparent 4231278256u +#define SQZ_unmaximize 3186335298u +#define SQZ_userCMYK 4190017706u +#define SQZ_userRGB 363668428u +#define SQZ_verLineTo 625107550u +#define SQZ_viewBox 3037148798u +#define SQZ_white 3660660630u +#define SQZ_width 303173726u +#define SQZ_winding 2804912752u +#define SQZ_wrapLeft 2241102244u +#define SQZ_wrapRight 1285667914u +#define SQZ_x 241u +#define SQZ_xor 7499761u +#define SQZ_y 243u +#define SQZ_yellow 4030545924u #endif #ifndef __CTX_LIBC_H #define __CTX_LIBC_H @@ -6342,7 +6397,6 @@ static inline char *ctx_strdup (const char *str) #endif -uint32_t ctx_strhash (const char *str); CtxColor *ctx_color_new (void); int ctx_get_int (Ctx *ctx, uint32_t hash); int ctx_get_is_set (Ctx *ctx, uint32_t hash); @@ -6373,15773 +6427,25328 @@ float ctx_pcm_get_queued_length (Ctx *ctx); int ctx_pcm_queue (Ctx *ctx, const int8_t *data, int frames); #endif +/* Copyright (c) 2021-2022 Øyvind Kolås <pippin@gimp.org> -#if CTX_IMPLEMENTATION || CTX_SIMD_BUILD -#ifndef __CTX_CLIENTS_H -#define __CTX_CLIENTS_H - + Fast cache-miss eliminating unicode strings for C. + All features are optional: + optimized 32bit 52bit 62bit and 64bit squoze encodings in UTF5+ and/or UTF-8 + string interning and APIS (for only getting the core squoze reference + implementation) + both utf8, unichar and printf for core APIs + embedding of strings (only for debug/profiling) + reference counting + embedded length -struct _CtxClient { - VT *vt; // or NULL when thread + License to be determined, the core implementation the snippet for + squoze64_utf8 on https://squoz.org/ is ISC licensed - long rev; - CtxList *events; // we could use this queue also for vt - Ctx *ctx; - char *title; - int x; - int y; - int width; - int height; - float opacity; - CtxClientFlags flags; +*/ #if 0 - int shaded; - int iconified; - int maximized; - int resizable; -#endif - int unmaximized_x; - int unmaximized_y; - int unmaximized_width; - int unmaximized_height; - int do_quit; - long drawn_rev; - int id; - int internal; // render a settings window rather than a vt +Minimal usage example: -#if CTX_THREADS - thrd_t tid; // and only split code path in processing? - // -- why? -#endif - void (*start_routine)(Ctx *ctx, void *user_data); - void *user_data; - CtxClientFinalize finalize; - Ctx *sub_ctx; - CtxList *ctx_events; +#define SQUOZE_IMPLEMENTATION +#include "squoze.h" +int main (int argc, char **argv) +{:q + char temp[10]; + Sqz *string = NULL; + + sqz_set (&string, "hello"); + + +} - /* we want to keep variation at the end */ -#if CTX_THREADS - mtx_t mtx; -#endif -#if VT_RECORD - Ctx *recording; #endif -}; +#ifndef SQUOZE_H +#define SQUOZE_H + +#include <stdint.h> +#include <stddef.h> + +// configuration of internal squoze, these +// are values that must be set before both header +// and implementation uses of squoze.h the values only +// impact the string interning implementation and not +// the low-level APIs +#ifndef SQUOZE_INTERN_DIRECT_STRING // when 1 the pointers returned are +#define SQUOZE_INTERN_DIRECT_STRING 1 // directly string pointers + // when 0 the struct of the per entry + // allocation is returned, for integration + // with garbage collectors that scan + // for pointers 0 is preferable. #endif -#if CTX_IMPLEMENTATION|CTX_COMPOSITE +#ifndef SQUOZE_ID_BITS // number of bits to use for interning API +#define SQUOZE_ID_BITS 64 // 32 52 62 or 64 +#endif -#ifndef __CTX_INTERNAL_H -#define __CTX_INTERNAL_H +#ifndef SQUOZE_ID_UTF5 // use UTF5+ as the embed encoding +#define SQUOZE_ID_UTF5 0 // if not set then UTF8 is used +#endif -#if !__COSMOPOLITAN__ -#include <stdlib.h> -#include <stdio.h> -#include <unistd.h> -#include <math.h> +#ifndef SQUOZE_ID_MURMUR // use murmurhash and no embedding +#define SQUOZE_ID_MURMUR 0 // #endif +#ifndef SQUOZE_REF_COUNTING // build the refcounting support, adds +#define SQUOZE_REF_COUNTING 0 // per-interned-string overhead +#endif -#if CTX_BRANCH_HINTS -#define CTX_LIKELY(x) __builtin_expect(!!(x), 1) -#define CTX_UNLIKELY(x) __builtin_expect(!!(x), 0) -#else -#define CTX_LIKELY(x) (x) -#define CTX_UNLIKELY(x) (x) +#ifndef SQUOZE_STORE_LENGTH // store byte-lengths as part of +#define SQUOZE_STORE_LENGTH 1 // per-interned-string data #endif -typedef struct _CtxRasterizer CtxRasterizer; -typedef struct _CtxGState CtxGState; -//typedef struct _CtxState CtxState; +#ifndef SQUOZE_USE_INTERN // enable interning hash-table +#define SQUOZE_USE_INTERN 1 // without this only a single + // core implementation can be built + // +/* XXX - you should not need to tweak anything below here, + * though the tweaks are available for tinkering + * and debugging. + */ +#ifndef SQUOZE_REF_SANITY +#define SQUOZE_REF_SANITY 0 // report consistency errors and use more RAM +#endif -typedef struct _CtxSource CtxSource; +#ifndef SQUOZE_CLOBBER_ON_FREE +#define SQUOZE_CLOBBER_ON_FREE 0 + // clobber strings when freeing, not a full leak report + // but better to always glitch than silently succeding or failing +#endif +#ifndef SQUOZE_INITIAL_POOL_SIZE +#define SQUOZE_INITIAL_POOL_SIZE (1<<8) // initial hash-table capacity +#endif -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); +#ifndef SQUOZE_USE_BUILTIN_CLZ +#define SQUOZE_USE_BUILTIN_CLZ 1 // use builtin for determining highest bit in unicode char +#endif -#define CTX_VALID_RGBA_U8 (1<<0) -#define CTX_VALID_RGBA_DEVICE (1<<1) -#if CTX_ENABLE_CM -#define CTX_VALID_RGBA (1<<2) +#ifndef SQUOZE_UTF8_MANUAL_UNROLL +#define SQUOZE_UTF8_MANUAL_UNROLL 1 // use manually unrolled UTF8 code #endif -#if CTX_ENABLE_CMYK -#define CTX_VALID_CMYKA (1<<3) -#define CTX_VALID_DCMYKA (1<<4) + +#ifndef SQUOZE_LIMIT_IMPLEMENTATIONS +#define SQUOZE_LIMIT_IMPLEMENTATIONS 0 #endif -#define CTX_VALID_GRAYA (1<<5) -#define CTX_VALID_GRAYA_U8 (1<<6) -#define CTX_VALID_LABA ((1<<7) | CTX_VALID_GRAYA) -struct _CtxColor -{ - uint8_t magic; // for colors used in keydb, set to a non valid start of - // string value. - uint8_t rgba[4]; - uint8_t l_u8; - uint8_t original; // the bitmask of the originally set color - uint8_t valid; // bitmask of which members contain valid - // values, gets denser populated as more - // formats are requested from a set color. - float device_red; - float device_green; - float device_blue; - float alpha; - float l; // luminance and gray -#if CTX_ENABLE_LAB // NYI - float a; - float b; +#ifndef SQUOZE_IMPLEMENTATION_32_UTF8 +#define SQUOZE_IMPLEMENTATION_32_UTF8 (!SQUOZE_LIMIT_IMPLEMENTATIONS) +#endif +#ifndef SQUOZE_IMPLEMENTATION_32_UTF5 +#define SQUOZE_IMPLEMENTATION_32_UTF5 (!SQUOZE_LIMIT_IMPLEMENTATIONS) +#endif +#ifndef SQUOZE_IMPLEMENTATION_52_UTF5 +#define SQUOZE_IMPLEMENTATION_52_UTF5 (!SQUOZE_LIMIT_IMPLEMENTATIONS) +#endif +#ifndef SQUOZE_IMPLEMENTATION_62_UTF5 +#define SQUOZE_IMPLEMENTATION_62_UTF5 (!SQUOZE_LIMIT_IMPLEMENTATIONS) +#endif +#ifndef SQUOZE_IMPLEMENTATION_64_UTF8 +#define SQUOZE_IMPLEMENTATION_64_UTF8 (!SQUOZE_LIMIT_IMPLEMENTATIONS) #endif -#if CTX_ENABLE_CMYK - float device_cyan; - float device_magenta; - float device_yellow; - float device_key; - float cyan; - float magenta; - float yellow; - float key; #endif -#if CTX_ENABLE_CM - float red; - float green; - float blue; -#if CTX_BABL - const Babl *space; // gets copied from state when color is declared +#if SQUOZE_USE_INTERN + +#if SQUOZE_ID_BITS==32 +typedef uint32_t sqz_id_t; #else - void *space; // gets copied from state when color is declared, -#endif +typedef uint64_t sqz_id_t; #endif -}; -typedef struct _CtxGradientStop CtxGradientStop; -struct _CtxGradientStop -{ - CtxColor color; - float pos; -}; +typedef struct _Sqz Sqz; /* handle representing a squozed string */ -enum _CtxSourceType -{ - CTX_SOURCE_COLOR = 0, - CTX_SOURCE_TEXTURE, - CTX_SOURCE_LINEAR_GRADIENT, - CTX_SOURCE_RADIAL_GRADIENT, - CTX_SOURCE_INHERIT_FILL -}; +/* create a new string that is the concatenation of a and b + */ +Sqz *sqz_utf8 (const char *str); +const char *sqz_decode (Sqz *squozed, char *temp); -typedef enum _CtxSourceType CtxSourceType; -typedef struct _CtxPixelFormatInfo CtxPixelFormatInfo; +int sqz_length (Sqz *squozed); +sqz_id_t sqz_id (Sqz *squozed); +uint32_t sqz_unichar_at (Sqz *a, int pos); +int sqz_strcmp (Sqz *a, Sqz *b); +inline int sqz_equal (Sqz *a, Sqz *b) { return a == b; } +void sqz_unset (Sqz **a); -struct _CtxBuffer -{ - void *data; - int width; - int height; - int stride; - 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; - void (*free_func) (void *pixels, void *user_data); - void *user_data; -#if CTX_ENABLE_CM -#if CTX_BABL - const Babl *space; -#else - void *space; -#endif -#endif -#if 1 - CtxBuffer *color_managed; /* only valid for one render target, cache - for a specific space - */ -#endif -}; +Sqz *sqz_cat (Sqz *a, Sqz *b); +Sqz *sqz_substring (Sqz *a, int pos, int length); +void sqz_insert (Sqz **a, int pos, Sqz *b); +void sqz_set (Sqz **a, Sqz *b); +void sqz_erase (Sqz **a, int pos, int length); -//void _ctx_user_to_device (CtxState *state, float *x, float *y); -//void _ctx_user_to_device_distance (CtxState *state, float *x, float *y); +#include <stdarg.h> +Sqz *sqz_printf (const char *format, ...); +Sqz *sqz_printf_va_list (const char *format, va_list list); +Sqz *sqz_unichar (uint32_t unichar); +Sqz *sqz_double (double value); +Sqz *sqz_int (int value); + +/* the following is APIs mostly implemented in terms of the above */ + +int sqz_has_prefix (Sqz *a, Sqz *prefix); +int sqz_has_suffix (Sqz *a, Sqz *suffix); + +void sqz_insert_double (Sqz **a, int pos, double value); +void sqz_insert_int (Sqz **a, int pos, int value); + + +void sqz_insert_unichar (Sqz **a, int pos, uint32_t unichar); +void sqz_replace_unichar (Sqz **a, int pos, int length, uint32_t unichar); +void sqz_append_unichar (Sqz **a, uint32_t unichar); +void sqz_append_utf8 (Sqz **a, const char *utf8); +int sqz_has_prefix_utf8 (Sqz *a, const char *utf8); +int sqz_has_suffix_utf8 (Sqz *a, const char *utf8); +void sqz_insert_utf8 (Sqz **a, int pos, const char *utf8); +void sqz_set_utf8 (Sqz **a, const char *utf8); +void sqz_replace_utf8 (Sqz **a, int pos, int length, const char *utf8); +void sqz_set_printf (Sqz **a, const char *format, ...); +void sqz_append_printf (Sqz **a, const char *format, ...); +void sqz_insert_printf (Sqz **a, int pos, const char *format, ...); +void sqz_replace_printf (Sqz **a, int pos, int length, const char *format, ...); +/* increase reference count of string */ +Sqz *sqz_ref (Sqz *squozed); +Sqz *sqz_dup (Sqz *squozed); +/* decrement reference count of string */ +void sqz_unref (Sqz *squozed); +typedef struct _SqzPool SqzPool; /* a pool for grouping allocated strings */ + + +/* create a new string pool, with fallback to another pool - + * or NULL for fallback to default pool, takes a reference on fallback. + */ +SqzPool *sqz_pool_new (SqzPool *fallback); +/* increase reference count of pool + */ +void sqz_pool_ref (SqzPool *pool); -typedef struct _CtxGradient CtxGradient; -struct _CtxGradient -{ - CtxGradientStop stops[CTX_MAX_GRADIENT_STOPS]; - int n_stops; -}; +/* decrease reference point of pool, when matching _new() + _ref() calls + * the pool is destoryed. + */ +void sqz_pool_unref (SqzPool *pool); -struct _CtxSource -{ - int type; - CtxMatrix set_transform; - CtxMatrix transform; - uint32_t pad; - union - { - CtxColor color; - struct - { - uint8_t rgba[4]; // shares data with set color - uint8_t pad; - CtxBuffer *buffer; - } texture; - struct - { - float x0; - float y0; - float x1; - float y1; - float dx; - float dy; - float start; - float end; - float length; - float rdelta; - } linear_gradient; - struct - { - float x0; - float y0; - float r0; - float x1; - float y1; - float r1; - float rdelta; - } radial_gradient; - }; -}; +/* add a string to a squoze pool + */ +Sqz *sqz_pool_add (SqzPool *pool, const char *str); +Sqz *sqz_concat (Sqz *a, Sqz *b); -typedef struct _Ctx16f16Matrix Ctx16f16Matrix; -struct - _Ctx16f16Matrix -{ -#if CTX_32BIT_SEGMENTS - int64_t m[3][3]; // forcing higher precision easily, the extra - // memory cost is minuscle -#else - int32_t m[3][3]; -#endif -}; +/* Report stats on interned strings + */ +void sqz_pool_mem_stats (SqzPool *pool, + size_t *size, + size_t *slack, + size_t *intern_alloc); +/* empty all pools + */ +void sqz_cleanup (void); -struct _CtxGState -{ -#if CTX_32BIT_SEGMENTS - uint32_t keydb_pos; - uint32_t stringpool_pos; -#else - uint16_t keydb_pos; // this limits these - uint16_t stringpool_pos; // #endif - CtxMatrix transform; - Ctx16f16Matrix prepped_transform; - CtxSource source_stroke; - CtxSource source_fill; - float global_alpha_f; - float line_width; - float line_dash_offset; - float miter_limit; - float font_size; -#if CTX_ENABLE_SHADOW_BLUR - float shadow_blur; - float shadow_offset_x; - float shadow_offset_y; +#if SQUOZE_IMPLEMENTATION_32_UTF5 || \ + SQUOZE_IMPLEMENTATION_52_UTF5 || \ + SQUOZE_IMPLEMENTATION_62_UTF5 +#define SQUOZE_USE_UTF5 1 +#else +#define SQUOZE_USE_UTF5 0 #endif - unsigned int transform_type:3; - unsigned int clipped:1; - CtxColorModel color_model:8; - /* bitfield-pack small state-parts */ - CtxLineCap line_cap:2; - CtxLineJoin line_join:2; - CtxFillRule fill_rule:1; - unsigned int image_smoothing:1; - unsigned int font:6; - unsigned int bold:1; - unsigned int italic:1; - uint8_t global_alpha_u8; - int16_t clip_min_x; - int16_t clip_min_y; - int16_t clip_max_x; - int16_t clip_max_y; - int n_dashes; -#if CTX_ENABLE_CM -#if CTX_BABL - const Babl *device_space; - const Babl *texture_space; - const Babl *rgb_space; - const Babl *cmyk_space; +#include <stdio.h> +#include <stdlib.h> +#include <string.h> - const Babl *fish_rgbaf_user_to_device; - const Babl *fish_rgbaf_texture_to_device; - const Babl *fish_rgbaf_device_to_user; +#if SQUOZE_IMPLEMENTATION_32_UTF5 +uint32_t squoze32_utf5 (const char *utf8, size_t len); +const char *squoze32_utf5_decode (uint32_t id, char *dest); +#endif -#else - void *device_space; - void *texture_space; - void *rgb_space; - void *cmyk_space; - void *fish_rgbaf_user_to_device; // dummy padding - void *fish_rgbaf_texture_to_device; // dummy padding - void *fish_rgbaf_device_to_user; // dummy padding +#if SQUOZE_IMPLEMENTATION_32_UTF8 +uint32_t squoze32_utf8 (const char *utf8, size_t len); +const char *squoze32_utf8_decode (uint32_t id, char *dest); #endif + +#if SQUOZE_IMPLEMENTATION_52_UTF5 +uint64_t squoze52_utf5 (const char *utf8, size_t len); +const char *squoze52_utf5_decode (uint64_t id, char *dest); #endif - CtxCompositingMode compositing_mode; // bitfield refs lead to - CtxBlend blend_mode; // non-vectorization - CtxExtend extend; - float dashes[CTX_MAX_DASHES]; // XXX moving dashes - // to state storage,. will - // allow it to be larger, - // free up memory, and - // make save/restore faster -}; +#if SQUOZE_IMPLEMENTATION_62_UTF5 +uint64_t squoze62_utf5 (const char *utf8, size_t len); +const char *squoze62_utf5_decode (uint64_t id, char *dest); +#endif -typedef enum -{ - CTX_TRANSFORMATION_NONE = 0, - CTX_TRANSFORMATION_SCREEN_SPACE = 1, - CTX_TRANSFORMATION_RELATIVE = 2, -#if CTX_BITPACK - CTX_TRANSFORMATION_BITPACK = 4, +#if SQUOZE_IMPLEMENTATION_64_UTF8 +uint64_t squoze64_utf8 (const char *utf8, size_t len); +const char *squoze62_utf8_decode (uint64_t id, char *dest); #endif - CTX_TRANSFORMATION_STORE_CLEAR = 16, -} CtxTransformation; -#define CTX_DRAWLIST_DOESNT_OWN_ENTRIES 64 -#define CTX_DRAWLIST_EDGE_LIST 128 -#define CTX_DRAWLIST_CURRENT_PATH 512 -// BITPACK +#endif -struct _CtxDrawlist -{ - CtxEntry *entries; - unsigned int count; - int size; - uint32_t flags; - int bitpack_pos; // stream is bitpacked up to this offset -}; +#ifdef SQUOZE_IMPLEMENTATION -// the keydb consists of keys set to floating point values, -// that might also be interpreted as integers for enums. -// -// the hash -typedef struct _CtxKeyDbEntry CtxKeyDbEntry; -struct _CtxKeyDbEntry -{ - uint32_t key; - float value; - //union { float f[1]; uint8_t u8[4]; }value; -}; -struct _CtxState +static inline uint32_t MurmurOAAT32 (const char * key, int len) { - int has_moved:1; - int has_clipped:1; - int8_t source; // used for the single-shifting to stroking - // 0 = fill - // 1 = start_stroke - // 2 = in_stroke - // - // if we're at in_stroke at start of a source definition - // we do filling - int16_t gstate_no; + size_t h = 3323198485ul; + for (int i = 0;i < len;i++) { + h ^= key[i]; + h *= 0x5bd1e995; + //h &= 0xffffffff; + h ^= h >> 15; + } + return h; +} - float x; - float y; - int ink_min_x; - int ink_min_y; - int ink_max_x; - int ink_max_y; - CtxGState gstate; - CtxGState gstate_stack[CTX_MAX_STATES];//at end, so can be made dynamic -#if CTX_GRADIENTS - CtxGradient gradient; /* we keep only one gradient, - this goes icky with multiple - restores - it should really be part of - graphics state.. - XXX, with the stringpool gradients - can be stored there. - */ -#endif - CtxKeyDbEntry keydb[CTX_MAX_KEYDB]; - char stringpool[CTX_STRINGPOOL_SIZE]; -}; +static inline uint64_t MurmurOAAT64 ( const char * key, int len) +{ + uint64_t h = 525201411107845655ull; + for (int i = 0;i < len;i++) { + h ^= key[i]; + h *= 0x5bd1e9955bd1e995; + h ^= h >> 47; + } + return h; +} +#if SQUOZE_USE_UTF5 // YYY -typedef struct _CtxFont CtxFont; -typedef struct _CtxFontEngine CtxFontEngine; +// TODO: UTF5+ should operate directly on bits instead of +// going via bytes +static inline void squoze5_encode (const char *input, int inlen, + char *output, int *r_outlen, + int permit_squeezed, + int escape_endzero); +static void squoze_decode_utf5_bytes (int is_utf5, + const unsigned char *input, int inlen, + char *output, int *r_outlen); +static inline size_t squoze5_encode_int (const char *input, int inlen, + int maxlen, int *overflow, + int escape_endzero); -struct _CtxFontEngine -{ -#if CTX_FONTS_FROM_FILE - 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); - float (*glyph_kern) (CtxFont *font, Ctx *ctx, uint32_t unicharA, uint32_t unicharB); -}; -#pragma pack(push,1) -struct _CtxFont +/* this should have the same behavior as the bitwidth and encoding + * specific implementations + */ +static inline uint64_t squoze_encode_id (int squoze_dim, int utf5, const char *stf8, size_t len) { -#if CTX_ONE_FONT_ENGINE==0 - CtxFontEngine *engine; + int length = len; + uint64_t id = 0; +#if SQUOZE_USE_UTF5 + if (utf5) + { + int max_quintets = squoze_dim / 5; + if (length <= max_quintets) + { + int overflow = 0; + id = squoze5_encode_int (stf8, length, max_quintets, &overflow, 1); + if (!overflow) + return id; + } + id = 0; + id = MurmurOAAT32(stf8, length); + id &= ~1; + } + else #endif - union { - struct + const uint8_t *utf8 = (const uint8_t*)stf8; + if (squoze_dim > 32) + squoze_dim = 64; + int bytes_dim = squoze_dim / 8; + + uint8_t first_byte = ((uint8_t*)utf8)[0]; + if (first_byte<128 + && first_byte != 11 + && (length <= bytes_dim)) { - CtxEntry *data; - //uint16_t length; - /* we've got ~110 bytes to fill to cover as - much data as stbtt_fontinfo */ - //int16_t glyph_pos[26]; // for a..z - } ctx; -#if CTX_FONT_ENGINE_CTX_FS - struct + id = utf8[0] * 2 + 1; + for (int i = 1; i < length; i++) + id += ((uint64_t)utf8[i]<<(8*(i))); + } + else if (length <= bytes_dim-1) { - const char *name; - char *path; - } ctx_fs; -#endif -#if CTX_FONT_ENGINE_STB - struct + id = 23; + for (int i = 0; i < length; i++) + id += ((uint64_t)utf8[i]<<(8*(i+1))); + } + else { - const char *name; - stbtt_fontinfo ttf_info; - } stb; -#endif -#if 0 - struct { int start; int end; int gw; int gh; const uint8_t *data;} monobitmap; -#endif - }; -#if CTX_ONE_FONT_ENGINE==0 - uint8_t type:3; // 0 ctx 1 stb 2 monobitmap - uint8_t monospaced:1; + id = MurmurOAAT32(stf8, len); + id &= ~1; // make even - intern marker + } + } + return id; +} + +#ifdef __CTX_H__ // override with ctx variants if included from ctx +#define strdup ctx_strdup +#define strstr ctx_strstr #endif -}; -#pragma pack(pop) -enum _CtxIteratorFlag + +#if SQUOZE_IMPLEMENTATION_32_UTF5 +uint32_t squoze32_utf5 (const char *utf8, size_t len) { - CTX_ITERATOR_FLAT = 0, - CTX_ITERATOR_EXPAND_BITPACK = 2, - CTX_ITERATOR_DEFAULTS = CTX_ITERATOR_EXPAND_BITPACK -}; -typedef enum _CtxIteratorFlag CtxIteratorFlag; + return squoze_encode_id (32, 1, utf8, len); +} +#endif +#if SQUOZE_IMPLEMENTATION_52_UTF5 +uint64_t squoze52_utf5 (const char *utf8, size_t len) +{ + return squoze_encode_id (52, 1, utf8, len); +} +#endif -struct _CtxIterator +#if SQUOZE_IMPLEMENTATION_62_UTF5 +uint64_t squoze62_utf5 (const char *utf8, size_t len) { - int pos; - int first_run; - CtxDrawlist *drawlist; - int end_pos; - int flags; - - int bitpack_pos; - int bitpack_length; // if non 0 bitpack is active - CtxEntry bitpack_command[6]; // the command returned to the - // user if unpacking is needed. -}; - -#if CTX_EVENTS - -// include list implementation - since it already is a header+inline online -// implementation? - -typedef struct CtxItemCb { - CtxEventType types; - CtxCb cb; - void* data1; - void* data2; - - void (*finalize) (void *data1, void *data2, void *finalize_data); - void *finalize_data; - -} CtxItemCb; + return squoze_encode_id (62, 1, utf8, len); +} +#endif +static inline uint64_t squoze_utf8 (size_t bytes_dim, const char *stf8, size_t length) +{ + uint64_t id; + const uint8_t *utf8 = (const uint8_t*)stf8; + uint8_t first_byte = ((uint8_t*)utf8)[0]; + if ( first_byte < 128 + && first_byte != 11 + && (length <= bytes_dim)) + { + switch (length) + { +#if SQUOZE_UTF8_MANUAL_UNROLL + case 0: id = 1; + break; + case 1: id = utf8[0] * 2 + 1; + break; + case 2: id = utf8[0] * 2 + 1 + (utf8[1] << (8*1)); + break; + case 3: id = utf8[0] * 2 + 1 + (utf8[1] << (8*1)) + + (utf8[2] << (8*2)); + break; + case 4: id = utf8[0] * 2 + 1 + (utf8[1] << (8*1)) + + (utf8[2] << (8*2)) + + (utf8[3] << (8*3)); + break; + case 5: id = utf8[0] * 2 + 1 + ((uint64_t)utf8[1] << (8*1)) + + ((uint64_t)utf8[2] << (8*2)) + + ((uint64_t)utf8[3] << (8*3)) + + ((uint64_t)utf8[4] << (8*4)); + break; + case 6: id = utf8[0] * 2 + 1 + ((uint64_t)utf8[1] << (8*1)) + + ((uint64_t)utf8[2] << (8*2)) + + ((uint64_t)utf8[3] << (8*3)) + + ((uint64_t)utf8[4] << (8*4)) + + ((uint64_t)utf8[5] << (8*5)); + break; + case 7: id = utf8[0] * 2 + 1 + ((uint64_t)utf8[1] << (8*1)) + + ((uint64_t)utf8[2] << (8*2)) + + ((uint64_t)utf8[3] << (8*3)) + + ((uint64_t)utf8[4] << (8*4)) + + ((uint64_t)utf8[5] << (8*5)) + + ((uint64_t)utf8[6] << (8*6)); + break; + case 8: id = utf8[0] * 2 + 1 + ((uint64_t)utf8[1] << (8*1)) + + ((uint64_t)utf8[2] << (8*2)) + + ((uint64_t)utf8[3] << (8*3)) + + ((uint64_t)utf8[4] << (8*4)) + + ((uint64_t)utf8[5] << (8*5)) + + ((uint64_t)utf8[6] << (8*6)) + + ((uint64_t)utf8[7] << (8*7)); + break; +#endif + default: + id = utf8[0] * 2 + 1; + for (unsigned int i = 1; i < length; i++) + id += ((uint64_t)utf8[i]<<(8*(i))); + } + return id; + } + else if (length <= bytes_dim-1) + { + switch (length) + { +#if SQUOZE_UTF8_MANUAL_UNROLL + case 0: id = 23; + break; + case 1: id = 23 + (utf8[0] << (8*1)); + break; + case 2: id = 23 + (utf8[0] << (8*1)) + + (utf8[1] << (8*2)); + break; + case 3: id = 23 + (utf8[0] << (8*1)) + + (utf8[1] << (8*2)) + + (utf8[2] << (8*3)); + break; + case 4: id = 23 + ((uint64_t)utf8[0] << (8*1)) + + ((uint64_t)utf8[1] << (8*2)) + + ((uint64_t)utf8[2] << (8*3)) + + ((uint64_t)utf8[3] << (8*4)); + break; + case 5: id = 23 + ((uint64_t)utf8[0] << (8*1)) + + ((uint64_t)utf8[1] << (8*2)) + + ((uint64_t)utf8[2] << (8*3)) + + ((uint64_t)utf8[3] << (8*4)) + + ((uint64_t)utf8[4] << (8*5)); + break; + case 6: id = 23 + ((uint64_t)utf8[0] << (8*1)) + + ((uint64_t)utf8[1] << (8*2)) + + ((uint64_t)utf8[2] << (8*3)) + + ((uint64_t)utf8[3] << (8*4)) + + ((uint64_t)utf8[4] << (8*5)) + + ((uint64_t)utf8[5] << (8*6)); + break; + case 7: id = 23 + ((uint64_t)utf8[0] << (8*1)) + + ((uint64_t)utf8[1] << (8*2)) + + ((uint64_t)utf8[2] << (8*3)) + + ((uint64_t)utf8[3] << (8*4)) + + ((uint64_t)utf8[4] << (8*5)) + + ((uint64_t)utf8[5] << (8*6)) + + ((uint64_t)utf8[6] << (8*7)); + break; +#endif + default: + id = 23; + for (unsigned int i = 0; i < length; i++) + id += ((uint64_t)utf8[i]<<(8*(i+1))); + } + return id; + } -typedef struct CtxItem { - CtxMatrix inv_matrix; /* for event coordinate transforms */ + id = MurmurOAAT32(stf8, length); + id &= ~1; // make even - intern marker + return id; +} - /* bounding box */ - float x0; - float y0; - float x1; - float y1; +#if SQUOZE_IMPLEMENTATION_64_UTF8 +uint64_t squoze64_utf8 (const char *stf8, size_t length) +{ + return squoze_utf8 (8, stf8, length); +} +#endif - void *path; - double path_hash; +#if SQUOZE_IMPLEMENTATION_32_UTF8 +uint32_t squoze32_utf8 (const char *stf8, size_t length) +{ + uint32_t id; + const uint8_t *utf8 = (const uint8_t*)stf8; + size_t bytes_dim = 4; - CtxCursor cursor; /* if 0 then UNSET and no cursor change is requested - */ + uint8_t first_byte = ((uint8_t*)utf8)[0]; + if (first_byte < 128 + && first_byte != 11 + && (length <= bytes_dim)) + { + switch (length) + { +#if SQUOZE_UTF8_MANUAL_UNROLL + case 0: id = 1; + break; + case 1: id = utf8[0] * 2 + 1; + break; + case 2: id = utf8[0] * 2 + 1 + (utf8[1] << (8*1)); + break; + case 3: id = utf8[0] * 2 + 1 + (utf8[1] << (8*1)) + + (utf8[2] << (8*2)); + break; + case 4: id = utf8[0] * 2 + 1 + (utf8[1] << (8*1)) + + (utf8[2] << (8*2)) + + (utf8[3] << (8*3)); + break; +#endif + default: + id = utf8[0] * 2 + 1; + for (unsigned int i = 1; i < length; i++) + id += ((uint32_t)utf8[i]<<(8*(i))); + } + return id; + } + else if (length <= bytes_dim-1) + { + switch (length) + { +#if SQUOZE_UTF8_MANUAL_UNROLL + case 0: id = 23; + break; + case 1: id = 23 + (utf8[0] << (8*1)); + break; + case 2: id = 23 + (utf8[0] << (8*1)) + + (utf8[1] << (8*2)); + break; + case 3: id = 23 + (utf8[0] << (8*1)) + + (utf8[1] << (8*2)) + + (utf8[2] << (8*3)); + break; +#endif + default: + id = 23; + for (unsigned int i = 0; i < length; i++) + id += (utf8[i]<<(8*(i+1))); + } + return id; + } - CtxEventType types; /* all cb's ored together */ - CtxItemCb cb[CTX_MAX_CBS]; - int cb_count; - int ref_count; -} CtxItem; + id = MurmurOAAT32(stf8, length); + id &= ~1; // make even - intern marker + return id; +} +#endif -typedef struct _CtxEvents CtxEvents; -struct _CtxEvents +static const char *squoze_id_decode_r (int squoze_dim, uint64_t hash, char *ret, int retlen, int is_utf5) { - int frozen; - int fullscreen; - CtxList *grabs; /* could split the grabs per device in the same way, - to make dispatch overhead smaller,. probably - not much to win though. */ - CtxEvent drag_event[CTX_MAX_DEVICES]; - CtxList *idles; - CtxList *idles_to_remove; - CtxList *idles_to_add; - CtxList *events; // for ctx_get_event - CtxBinding bindings[CTX_MAX_KEYBINDINGS]; /*< better as list, uses no mem if unused */ - int n_bindings; - CtxItem *prev[CTX_MAX_DEVICES]; - float pointer_x[CTX_MAX_DEVICES]; - float pointer_y[CTX_MAX_DEVICES]; - unsigned char pointer_down[CTX_MAX_DEVICES]; - unsigned int in_idle_dispatch:1; - unsigned int ctx_get_event_enabled:1; - CtxModifierState modifier_state; - int idle_id; - CtxList *items; - CtxItem *last_item; - float tap_hysteresis; -#if CTX_CLIENTS - CtxList *clients; - CtxClient *active; - CtxClient *active_tab; -#endif - int tap_delay_min; - int tap_delay_max; - int tap_delay_hold; -}; +#if SQUOZE_USE_UTF5 + if (is_utf5) + { + int is_utf5 = (hash & 2)!=0; + uint8_t utf5[20]=""; // we newer go really high since there isnt room + // in the integers + uint64_t tmp = hash; + int len = 0; + tmp /= 4; + utf5[len]=0; + while (tmp > 0) + { + utf5[len++] = tmp & 31; + tmp /= 32; + } + utf5[len]=0; + squoze_decode_utf5_bytes (is_utf5, utf5, len, ret, &retlen); + return ret; + } + else #endif + { + if (squoze_dim == 32) + { + if ((hash & 0xff) == 23) + { + memcpy (ret, ((char*)&hash)+1, 3); + ret[3] = 0; + } + else + { + memcpy (ret, &hash, 4); + ((unsigned char*)ret)[0]/=2; + ret[4] = 0; + } + } + else + { + if ((hash & 0xff) == 23) + { + memcpy (ret, ((char*)&hash)+1, 7); + ret[7] = 0; + } + else + { + memcpy (ret, &hash, 8); + ((unsigned char*)ret)[0]/=2; + ret[8] = 0; + } + } + return ret; + } +} -typedef struct _CtxEidInfo +const char *squoze_id_decode (int squoze_dim, uint64_t id, int is_utf5, char *dest); +const char *squoze_id_decode (int squoze_dim, uint64_t id, int is_utf5, char *dest) { - char *eid; - int frame; - int width; - int height; -} CtxEidInfo; + if (id == 0 || ((id & 1) == 0)) {dest[0]=0;return NULL; } + else if (id == 3) { dest[0]=0;return NULL;} + squoze_id_decode_r (squoze_dim, id, dest, 16, is_utf5); + return dest; +} +#if SQUOZE_IMPLEMENTATION_32_UTF5 +const char *squoze32_utf5_decode (uint32_t id, char *dest) +{ + return squoze_id_decode (32, id, 1, dest); +} +#endif -struct _CtxGlyphEntry +#if SQUOZE_IMPLEMENTATION_52_UTF5 +const char *squoze52_utf5_decode (uint64_t id, char *dest) { - uint32_t unichar; - uint16_t offset; - CtxFont *font; -}; -typedef struct _CtxGlyphEntry CtxGlyphEntry; + return squoze_id_decode (52, id, 1, dest); +} +#endif -struct _Ctx +#if SQUOZE_IMPLEMENTATION_62_UTF5 +const char *squoze62_utf5_decode (uint64_t id, char *dest) { - CtxBackend *backend; - CtxDrawlist drawlist; - int transformation; - int width; - int height; - int dirty; - Ctx *texture_cache; - CtxList *eid_db; - CtxState state; /**/ - int frame; /* used for texture lifetime */ - uint32_t bail; - CtxBackend *backend_pushed; - CtxBuffer texture[CTX_MAX_TEXTURES]; -#if CTX_EVENTS - CtxCursor cursor; - int quit; - CtxEvents events; - int mouse_fd; - int mouse_x; - int mouse_y; + return squoze_id_decode (62, id, 1, dest); +} #endif -#if CTX_CURRENT_PATH - CtxDrawlist current_path; // possibly transformed coordinates ! - CtxIterator current_path_iterator; + +#if SQUOZE_IMPLEMENTATION_64_UTF8 +const char *squoze64_utf8_decode (uint64_t id, char *dest) +{ + return squoze_id_decode (64, id, 0, dest); +} #endif -#if CTX_GLYPH_CACHE - CtxGlyphEntry glyph_index_cache[CTX_GLYPH_CACHE_SIZE]; + +#if SQUOZE_IMPLEMENTATION_32_UTF8 +const char *squoze32_utf8_decode (uint32_t id, char *dest) +{ + return squoze_id_decode (32, id, 0, dest); +} #endif - CtxFont *fonts; // a copy to keep it alive with mp's - // garbage collector, the fonts themselves - // are static and shared beyond ctx contexts - -}; +static inline uint32_t +squoze_utf8_to_unichar (const char *input) +{ + const uint8_t *utf8 = (const uint8_t *) input; + uint8_t c = utf8[0]; + if ( (c & 0x80) == 0) + { return c; } + else if ( (c & 0xE0) == 0xC0) + return ( (utf8[0] & 0x1F) << 6) | + (utf8[1] & 0x3F); + else if ( (c & 0xF0) == 0xE0) + return ( (utf8[0] & 0xF) << 12) | + ( (utf8[1] & 0x3F) << 6) | + (utf8[2] & 0x3F); + else if ( (c & 0xF8) == 0xF0) + return ( (utf8[0] & 0x7) << 18) | + ( (utf8[1] & 0x3F) << 12) | + ( (utf8[2] & 0x3F) << 6) | + (utf8[3] & 0x3F); + else if ( (c & 0xFC) == 0xF8) + return ( (utf8[0] & 0x3) << 24) | + ( (utf8[1] & 0x3F) << 18) | + ( (utf8[2] & 0x3F) << 12) | + ( (utf8[3] & 0x3F) << 6) | + (utf8[4] & 0x3F); + else if ( (c & 0xFE) == 0xFC) + return ( (utf8[0] & 0x1) << 30) | + ( (utf8[1] & 0x3F) << 24) | + ( (utf8[2] & 0x3F) << 18) | + ( (utf8[3] & 0x3F) << 12) | + ( (utf8[4] & 0x3F) << 6) | + (utf8[5] & 0x3F); + return 0; +} +static inline int +squoze_unichar_to_utf8 (uint32_t ch, + uint8_t *dest) +{ + /* http://www.cprogramming.com/tutorial/utf8.c */ + /* Basic UTF-8 manipulation routines + by Jeff Bezanson + placed in the public domain Fall 2005 ... */ + if (ch < 0x80) + { + dest[0] = (char) ch; + return 1; + } + if (ch < 0x800) + { + dest[0] = (ch>>6) | 0xC0; + dest[1] = (ch & 0x3F) | 0x80; + return 2; + } + if (ch < 0x10000) + { + dest[0] = (ch>>12) | 0xE0; + dest[1] = ( (ch>>6) & 0x3F) | 0x80; + dest[2] = (ch & 0x3F) | 0x80; + return 3; + } + if (ch < 0x110000) + { + dest[0] = (ch>>18) | 0xF0; + dest[1] = ( (ch>>12) & 0x3F) | 0x80; + dest[2] = ( (ch>>6) & 0x3F) | 0x80; + dest[3] = (ch & 0x3F) | 0x80; + return 4; + } + return 0; +} -static inline void -ctx_process (Ctx *ctx, CtxEntry *entry) +static inline int squoze_utf8_strlen (const char *s) { - ctx->backend->process (ctx, (CtxCommand *) entry); + int count; + if (!s) + { return 0; } + for (count = 0; *s; s++) + if ( (*s & 0xC0) != 0x80) + { count++; } + return count; } -CtxBuffer *ctx_buffer_new (int width, int height, - CtxPixelFormat pixel_format); -void ctx_buffer_destroy (CtxBuffer *buffer); +static inline int +squoze_utf8_len (const unsigned char first_byte) +{ + if ( (first_byte & 0x80) == 0) + { return 1; } + else if ( (first_byte & 0xE0) == 0xC0) + { return 2; } + else if ( (first_byte & 0xF0) == 0xE0) + { return 3; } + else if ( (first_byte & 0xF8) == 0xF0) + { return 4; } + return 1; +} -void -ctx_state_gradient_clear_stops (CtxState *state); -static inline void ctx_interpret_style (CtxState *state, CtxEntry *entry, void *data); -static inline void ctx_interpret_transforms (CtxState *state, CtxEntry *entry, void *data); -static inline void ctx_interpret_pos (CtxState *state, CtxEntry *entry, void *data); -static inline void ctx_interpret_pos_transform (CtxState *state, CtxEntry *entry, void *data); -struct _CtxInternalFsEntry -{ - char *path; - int length; - char *data; +/* data structures and implementation for string interning, potentially + * with both ref-counting and pools of strings. + */ +#if SQUOZE_USE_INTERN + +struct _Sqz { +#if SQUOZE_REF_COUNTING + int32_t ref_count; // set to magic value for ROM strings? + // and store pointer in string data? +#endif +#if SQUOZE_STORE_LENGTH + int32_t length; +#endif + sqz_id_t hash; + char string[]; }; -struct _CtxPixelFormatInfo -{ - CtxPixelFormat pixel_format:8; - uint8_t components; /* number of components */ - uint8_t bpp; /* bits per pixel - for doing offset computations - along with rowstride found elsewhere, if 0 it indicates - 1/8 */ - uint8_t ebpp; /*effective bytes per pixel - for doing offset - computations, for formats that get converted, the - ebpp of the working space applied */ - uint8_t dither_red_blue; - uint8_t dither_green; - CtxPixelFormat composite_format:8; - void (*to_comp) (CtxRasterizer *r, - int x, const void * __restrict__ src, uint8_t * __restrict__ comp, int count); - void (*from_comp) (CtxRasterizer *r, - int x, const uint8_t * __restrict__ comp, void *__restrict__ dst, int count); - void (*apply_coverage) (CtxRasterizer *r, uint8_t * __restrict__ dst, uint8_t * __restrict__ src, int x, uint8_t *coverage, - unsigned int count); - void (*setup) (CtxRasterizer *r); +static inline uint64_t sqz_pool_encode (SqzPool *pool, const char *utf8, size_t len, Sqz **interned_ref); + +struct _SqzPool +{ + int32_t ref_count; + SqzPool *fallback; + Sqz **hashtable; + int count; + int size; + SqzPool *next; }; +static SqzPool global_pool = {0, NULL, NULL, 0, 0, NULL}; -static inline void -_ctx_user_to_device (CtxState *state, float *x, float *y); -static void -_ctx_user_to_device_distance (CtxState *state, float *x, float *y); -static void ctx_state_init (CtxState *state); -static inline void -ctx_interpret_pos_bare (CtxState *state, CtxEntry *entry, void *data); -static inline void -ctx_drawlist_deinit (CtxDrawlist *drawlist); +static SqzPool *sqz_pools = NULL; -//extern CtxPixelFormatInfo *(*ctx_pixel_format_info) (CtxPixelFormat format); -CtxPixelFormatInfo *ctx_pixel_format_info (CtxPixelFormat format); +static int sqz_pool_find (SqzPool *pool, uint64_t hash, int length, const uint8_t *bytes) +{ + if (pool->size == 0) + return -1; + int pos = (hash/2) & (pool->size-1); + if (!pool->hashtable[pos]) + return -1; + while (pool->hashtable[pos]->hash != hash +#if SQUOZE_STORE_LENGTH + || pool->hashtable[pos]->length != length +#endif + || strcmp (pool->hashtable[pos]->string, (char*)bytes) + ) + { + pos++; + pos &= (pool->size-1); + if (!pool->hashtable[pos]) + return -1; + } + return pos; +} +static int sqz_pool_add_entry (SqzPool *pool, Sqz *str) +{ + if (pool->count + 1 >= pool->size / 2) + { + Sqz **old = pool->hashtable; + int old_size = pool->size; + if (old_size == 0) + pool->size = SQUOZE_INITIAL_POOL_SIZE; + else + pool->size *= 2; + pool->hashtable = (Sqz**)calloc (pool->size, sizeof (void*)); + if (old) + { + for (int i = 0; i < old_size; i++) + if (old[i]) + sqz_pool_add_entry (pool, old[i]); + free (old); + } + } + pool->count++; + int pos = (str->hash/2) & (pool->size-1); + while (pool->hashtable[pos]) + { + pos++; + pos &= (pool->size-1); + } + pool->hashtable[pos]=str; + return pos; +} +#if SQUOZE_REF_SANITY +static int sqz_pool_remove (SqzPool *pool, Sqz *squozed, int do_free) +{ + Sqz *str = squozed; + int no = sqz_pool_find (pool, str->hash, strlen (str->string), (uint8_t*)str->string); + if (no < 0) + return 0; + if (do_free) + free (str); +#ifdef assert + assert (pool->hashtable[no] == squozed); +#endif + pool->hashtable[no]=0; + + // check if there is another one to promote now + for (int i = no+1; pool->hashtable[i]; i = (i+1)&(pool->size-1)) + { + if ((pool->hashtable[i]->hash & (pool->size-1)) == (unsigned)no) + { + Sqz *for_upgrade = pool->hashtable[i]; + sqz_pool_remove (pool, for_upgrade, 0); + sqz_pool_add_entry (pool, for_upgrade); + break; + } + } + return 1; +} +#endif -extern void (*ctx_composite_stroke_rect) (CtxRasterizer *rasterizer, - float x0, - float y0, - float x1, - float y1, - float line_width); +static Sqz *sqz_lookup (SqzPool *pool, sqz_id_t id, int length, const uint8_t *bytes) +{ + int pos = sqz_pool_find (pool, id, length, bytes); + if (pos >= 0) + return pool->hashtable[pos]; + if (pool->fallback) + return sqz_lookup (pool->fallback, id, length, bytes); + return NULL; +} -extern void (*ctx_composite_setup) (CtxRasterizer *rasterizer); +void sqz_pool_mem_stats (SqzPool *pool, + size_t *size, + size_t *slack, + size_t *intern_alloc) +{ + if (!pool) pool = &global_pool; + if (size) + { + *size = sizeof (SqzPool) + pool->size * sizeof (void*); + } + if (slack) + { + *slack = (pool->size - pool->count) * sizeof (void*); + } + if (intern_alloc) + { + size_t sum = 0; + for (int i = 0; i < pool->size; i++) + { + if (pool->hashtable[i]) + { + Sqz *squoze = pool->hashtable[i]; + sum += strlen (squoze->string) + 1 + sizeof (Sqz); + } + } + *intern_alloc = sum; + } +} -struct _CtxShapeEntry + // we do 32bit also for 64bit - we want the same predetermined hashes to match +static inline Sqz *_sqz_pool_add (SqzPool *pool, const char *str) { - uint32_t hash; - uint16_t width; - uint16_t height; - int last_frame; // xxx - uint32_t uses; // instrumented for longer keep-alive - uint8_t data[]; -}; + if (!pool) pool = &global_pool; + Sqz *interned = NULL; + uint64_t hash = sqz_pool_encode (pool, str, strlen (str), &interned); -typedef struct _CtxShapeEntry CtxShapeEntry; - -extern void (*ctx_rasterizer_rasterize_edges) (CtxRasterizer *rasterizer, const int fill_rule -#if CTX_SHAPE_CACHE - ,CtxShapeEntry *shape + if (interned) + { +#ifdef assert + assert ((((size_t)interned)&0x1)==0); #endif - ); +#if SQUOZE_DIRECT_STRING + return interned+1; +#else + return interned; +#endif + } + else + return (Sqz*)((size_t)hash); +} +Sqz *sqz_pool_add (SqzPool *pool, const char *str) +{ + return _sqz_pool_add (pool, str); +} +Sqz *sqz_utf8(const char *str) +{ + return _sqz_pool_add (NULL, str); +} -extern void (*ctx_composite_fill_rect) (CtxRasterizer *rasterizer, - float x0, - float y0, - float x1, - float y1, - uint8_t cov); +// encodes utf8 to a squoze id of squoze_dim bits - if interned_ret is provided overflowed ids +// are interned and a new interned squoze is returned. +static uint64_t sqz_pool_encode (SqzPool *pool, const char *utf8, size_t len, Sqz **interned_ref) +{ +#if SQUOZE_ID_BITS==32 && SQUOZE_ID_MURMUR + uint64_t hash = MurmurOAAT32(utf8, len) & ~1; +#elif SQUOZE_ID_BITS==32 && SQUOZE_ID_UTF5 + uint64_t hash = squoze32_utf5 (utf8, len); +#elif SQUOZE_ID_BITS==32 && SQUOZE_ID_UTF8 + uint64_t hash = squoze32_utf8 (utf8, len); +#elif SQUOZE_ID_BITS==62 && SQUOZE_ID_UTF5 + uint64_t hash = squoze62_utf5 (utf8, len); +#elif SQUOZE_ID_BITS==52 && SQUOZE_ID_UTF5 + uint64_t hash = squoze52_utf5 (utf8, len); +#elif SQUOZE_ID_BITS==64 && SQUOZE_ID_UTF8 + uint64_t hash = squoze64_utf8 (utf8, len); +#else + uint64_t hash = squoze_encode_id (SQUOZE_ID_BITS, SQUOZE_ID_UTF5, utf8, len); +#endif + if (!interned_ref) + return hash; + if (pool == NULL) pool = &global_pool; + if ((hash & 1)==0) + { + Sqz *str = sqz_lookup (pool, hash, len, (const uint8_t*)utf8); + if (str) + { +#if SQUOZE_REF_COUNTING + str->ref_count++; +#endif + if (interned_ref) *interned_ref = str + SQUOZE_INTERN_DIRECT_STRING; + return hash; + } -const char *ctx_utf8_skip (const char *s, int utf8_length); -int ctx_utf8_strlen (const char *s); -int -ctx_unichar_to_utf8 (uint32_t ch, - uint8_t *dest); + { + Sqz *entry = (Sqz*)calloc (len + 1 + sizeof(Sqz), 1); + entry->hash = hash; +#if SQUOZE_STORE_LENGTH + entry->length = len; +#endif + strcpy (entry->string, utf8); + if (interned_ref) *interned_ref = entry + SQUOZE_INTERN_DIRECT_STRING; + sqz_pool_add_entry (pool, entry); + } + } + return hash; +} -uint32_t -ctx_utf8_to_unichar (const char *input); +static inline int sqz_is_interned (Sqz *squozed) +{ + return ((((size_t)(squozed))&1) == 0); +} +static inline int sqz_is_embedded (Sqz *squozed) +{ + return !sqz_is_interned (squozed); +} -typedef struct _CtxHasher CtxHasher; +/* returns either the string or temp with the decode + * embedded string decoded + */ +const char *sqz_decode (Sqz *squozed, char *temp) +{ + if (!squozed) return NULL; + if (sqz_is_embedded (squozed)) + { +#if SQUOZE_ID_BITS==32 && SQUOZE_ID_UTF5 + return squoze32_utf5_decode ((size_t)squozed, temp); +#elif SQUOZE_ID_BITS==32 && SQUOZE_ID_UTF8 + return squoze32_utf8_decode ((size_t)squozed, temp); +#elif SQUOZE_ID_BITS==52 && SQUOZE_ID_UTF5 + return squoze52_utf5_decode ((size_t)squozed, temp); +#elif SQUOZE_ID_BITS==62 && SQUOZE_ID_UTF5 + return squoze62_utf5_decode ((size_t)squozed, temp); +#elif SQUOZE_ID_BITS==64 && SQUOZE_ID_UTF8 + return squoze64_utf8_decode ((size_t)squozed, temp); +#else + return squoze_id_decode (SQUOZE_ID_BITS, + ((size_t)squozed), + SQUOZE_ID_UTF5, + temp); +#endif + } + else + { +#if SQUOZE_INTERN_DIRECT_STRING + return (char*)squozed; +#else + return squozed->string; +#endif + } +} -typedef void (*CtxFragment) (CtxRasterizer *rasterizer, float x, float y, float z, void *out, int count, float dx, float dy, float dz); +Sqz *sqz_ref (Sqz *squozed) +{ +#if SQUOZE_REF_COUNTING + if (sqz_is_interned (squozed)) + { + (squozed-SQUOZE_INTERN_DIRECT_STRING)->ref_count ++; + } +#endif + return squozed; +} +Sqz *sqz_dup (Sqz *squozed) +{ + return sqz_ref (squozed); +} -#define CTX_MAX_GAUSSIAN_KERNEL_DIM 512 +void sqz_unref (Sqz *squozed) +{ +#if SQUOZE_REF_COUNTING + if (sqz_is_interned (squozed)) + { +#if SQUOZE_INTERN_DIRECT_STRING + squozed--; +#endif + if (squozed->ref_count <= 0) + { +#if SQUOZE_CLOBBER_ON_FREE + squozed->string[-squozed->ref_count]='#'; +#endif +#if SQUOZE_REF_SANITY + if (squozed->ref_count < 0) + fprintf (stderr, "double unref for \"%s\"\n", squozed->string); + squozed->ref_count--; +#else + SqzPool *pool = &global_pool; + if (sqz_pool_remove (pool, squozed, 1)) + { + return; + } + pool = sqz_pools; + if (pool) + do { + if (sqz_pool_remove (pool, squozed, 1)) + { + return; + } + pool = pool->next; + } while (pool); +#endif + } + else + { + squozed->ref_count--; + } + } +#endif +} +int sqz_has_prefix (Sqz *a, Sqz *prefix) +{ + char tmp_a[16]; + char tmp_prefix[16]; + const char *a_str = sqz_decode (a, tmp_a); + const char *prefix_str = sqz_decode (prefix, tmp_prefix); + return !strncmp (a_str, prefix_str, strlen (prefix_str)); +} +int sqz_has_prefix_utf8 (Sqz *a, const char *utf8) +{ + Sqz *b = sqz_utf8 (utf8); + int ret = sqz_has_prefix (a, b); + sqz_unref (b); + return ret; +} -struct _CtxShapeCache +int sqz_has_suffix_utf8 (Sqz *a, const char *utf8) { - CtxShapeEntry *entries[CTX_SHAPE_CACHE_ENTRIES]; - long size; -}; + Sqz *b = sqz_utf8 (utf8); + int ret = sqz_has_suffix (a, b); + sqz_unref (b); + return ret; +} -typedef struct _CtxShapeCache CtxShapeCache; +int sqz_has_suffix (Sqz *a, Sqz *suffix) +{ + char tmp_a[16]; + const char *a_str = sqz_decode (a, tmp_a); + int a_len = strlen (a_str); + char tmp_suffix[16]; + const char *suffix_str = sqz_decode (suffix, tmp_suffix); + int suffix_len = strlen (suffix_str); + + if (a_len < suffix_len) + return 0; + return strcmp (a_str + a_len - suffix_len, suffix_str); +} -typedef enum { - CTX_COV_PATH_FALLBACK =0, - CTX_COV_PATH_RGBA8_OVER, - CTX_COV_PATH_RGBA8_COPY, - CTX_COV_PATH_RGBA8_COPY_FRAGMENT, - CTX_COV_PATH_RGBA8_OVER_FRAGMENT, - CTX_COV_PATH_GRAYA8_COPY, - CTX_COV_PATH_GRAY1_COPY, - CTX_COV_PATH_GRAY2_COPY, - CTX_COV_PATH_GRAY4_COPY, - CTX_COV_PATH_RGB565_COPY, - CTX_COV_PATH_RGB332_COPY, - CTX_COV_PATH_GRAY8_COPY, - CTX_COV_PATH_RGBAF_COPY, - CTX_COV_PATH_RGB8_COPY, - CTX_COV_PATH_CMYK8_COPY, - CTX_COV_PATH_CMYKA8_COPY, - CTX_COV_PATH_CMYKAF_COPY, - CTX_COV_PATH_GRAYAF_COPY, - CTX_COV_PATH_CBRLE_COPY -} CtxCovPath; -struct _CtxRasterizer +static void _sqz_prepend (Sqz **squoze, Sqz *head) { - CtxBackend backend; - /* these should be initialized and used as the bounds for rendering into the - buffer as well XXX: not yet in use, and when in use will only be - correct for axis aligned clips - proper rasterization of a clipping path - would be yet another refinement on top. - */ + if (!squoze) return; + Sqz *combined = sqz_cat (head, *squoze); + sqz_unref (*squoze); + *squoze=combined; +} +static void _sqz_append (Sqz **squoze, Sqz *tail) +{ + if (!squoze) return; + Sqz *combined = sqz_cat (*squoze, tail); + sqz_unref (*squoze); + *squoze=combined; +} -#define CTX_COMPOSITE_ARGUMENTS CtxRasterizer *rasterizer, uint8_t * __restrict__ dst, uint8_t * __restrict__ src, int x0, uint8_t * __restrict__ coverage, unsigned int count - 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); +Sqz *sqz_substring (Sqz *a, int pos, int length) +{ + int src_length = sqz_length (a); + if (pos > src_length) + return sqz_utf8 (""); + if (pos < 0) + pos = src_length + pos + 1; + char tmp[16]; + const char *src = sqz_decode (a, tmp); + char *end; + int allocated = 0; - unsigned int aa; // level of vertical aa - unsigned int prev_active_edges; - unsigned int active_edges; - unsigned int pending_edges; - unsigned int ending_edges; - unsigned int edge_pos; // where we're at in iterating all edges - unsigned int needs_aa3; // count of how many edges implies antialiasing - unsigned int needs_aa5; // count of how many edges implies antialiasing - unsigned int needs_aa15; // count of how many edges implies antialiasing - unsigned int horizontal_edges; + char *copy; + if (src_length < 256) + { + copy = alloca (strlen (src) + 1); + strcpy (copy, src); + } + else + { + copy = strdup (src); + allocated = 1; + } + char *p = copy; + int i; + for (i = 0; i < pos; i++) + p += squoze_utf8_len (*p); + end = p; + for (i = 0; i < length && *end; i++) + end += squoze_utf8_len (*end); + *end = 0; + + Sqz *ret = sqz_utf8 (p); + if (allocated) + free (copy); + return ret; +} - int scanline; - int scan_min; - int scan_max; - int col_min; - int col_max; +void sqz_erase (Sqz **a, int pos, int length) +{ + if (!a) return; + if (!*a) return; - int inner_x; - int inner_y; + if (length < 1) + return; + if (pos < 0) + { + pos = sqz_length (*a) + pos; + } - float x; - float y; + Sqz *pre = sqz_substring (*a, 0, pos); + Sqz *post = sqz_substring (*a, pos+length, 10000); + sqz_unref (*a); + *a = sqz_cat (pre, post); + sqz_unref (pre); + sqz_unref (post); +} - float first_x; - float first_y; +void sqz_insert (Sqz **a, int pos, Sqz *b) +{ + if (pos == 0) + { + _sqz_prepend (a, b); + return; + } + if (pos == -1) + { + _sqz_append (a, b); + return; + } + if (!a) return; + if (!*a) return; + if (pos < 0) + { + pos = sqz_length (*a) + pos + 1; + } + Sqz *pre = sqz_substring (*a, 0, pos); + Sqz *post = sqz_substring (*a, pos, 10000); + sqz_unref (*a); - uint16_t blit_x; - uint16_t blit_y; - uint16_t blit_width; - uint16_t blit_height; - uint16_t blit_stride; + *a = sqz_cat (pre, b); + _sqz_append (a, post); + sqz_unref (pre); + sqz_unref (post); +} - unsigned int clip_rectangle:1; - unsigned int has_shape:2; - int has_prev:2; - unsigned int preserve:1; -#if CTX_ENABLE_SHADOW_BLUR - unsigned int in_shadow:1; -#endif - unsigned int in_text:1; - unsigned int swap_red_green:1; +void sqz_insert_utf8 (Sqz **a, int pos, const char *utf8) +{ + Sqz *b = sqz_utf8 (utf8); + sqz_insert (a, pos, b); + sqz_unref (b); +} -#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; - Ctx *texture_source; /* normally same as ctx */ - int shadow_y; +Sqz *sqz_unichar (uint32_t unichar) +{ + char temp[5]; + temp[squoze_unichar_to_utf8 (unichar, (uint8_t*)temp)]=0; + return sqz_utf8 (temp); +} - uint8_t color[4*5]; // in compositing format - uint16_t color_native; // - uint16_t color_nativeB[5]; +Sqz *sqz_int (int value) +{ + char temp[40]; + sprintf (temp, "%i", value); + if (strchr (temp, ',')) + *strchr (temp, ',')='.'; + return sqz_utf8 (temp); +} - int edges[CTX_MAX_EDGES]; // integer position in edge array - CtxDrawlist edge_list; +Sqz *sqz_double (double value) +{ + char temp[40]; + sprintf (temp, "%f", value); + if (strchr (temp, ',')) + *strchr (temp, ',')='.'; + return sqz_utf8 (temp); +} -#if CTX_GRADIENTS -#if CTX_GRADIENT_CACHE - int gradient_cache_valid; - uint8_t gradient_cache_u8[CTX_GRADIENT_CACHE_ELEMENTS][4]; - int gradient_cache_elements; -#endif -#endif +void sqz_insert_unichar (Sqz **a, int pos, uint32_t unichar) +{ + Sqz *b = sqz_unichar (unichar); + sqz_insert (a, pos, b); + sqz_unref (b); +} -#if CTX_ENABLE_CLIP - CtxBuffer *clip_buffer; -#endif +void sqz_insert_double (Sqz **a, int pos, double value) +{ + Sqz *b = sqz_double (value); + sqz_insert (a, pos, b); + sqz_unref (b); +} -#if CTX_COMPOSITING_GROUPS - void *saved_buf; // when group redirected - CtxBuffer *group[CTX_GROUP_MAX]; -#endif -#if CTX_ENABLE_SHADOW_BLUR - float kernel[CTX_MAX_GAUSSIAN_KERNEL_DIM]; -#endif +void sqz_insert_int (Sqz **a, int pos, int value) +{ + Sqz *b = sqz_int (value); + sqz_insert (a, pos, b); + sqz_unref (b); +} -#if static_OPAQUE - uint8_t opaque[CTX_MAX_SCANLINE_LENGTH]; -#endif +uint32_t sqz_unichar_at (Sqz *a, int pos) +{ + char tmp[16]; + const char *str = sqz_decode (a, tmp); + const char *p = str; + int i; + if (pos < 0) + { + pos = sqz_length (a) + pos; + } + for (i = 0; i < pos; i++) + p += squoze_utf8_len (*p); + return squoze_utf8_to_unichar (p); +} -#if CTX_SHAPE_CACHE - CtxShapeCache shape_cache; /* needs to be at end of struct, it - is excluded from clearing */ -#endif -}; +void sqz_replace (Sqz **a, int pos, int length, Sqz *b) +{ + sqz_erase (a, pos, length); + sqz_insert (a, pos, b); +} -struct _CtxSHA1 { - uint64_t length; - uint32_t state[5], curlen; - unsigned char buf[64]; -}; -typedef struct _CtxMurmur CtxMurmur; -struct _CtxMurmur { - uint32_t state[2]; -}; +void sqz_replace_unichar (Sqz **a, int pos, int length, uint32_t unichar) +{ + Sqz *b = sqz_unichar (unichar); + sqz_erase (a, pos, length); + sqz_insert (a, pos, b); + sqz_unref (b); +} +void sqz_replace_utf8 (Sqz **a, int pos, int length, const char *utf8) +{ + sqz_erase (a, pos, length); + sqz_insert_utf8 (a, pos, utf8); +} -#pragma pack(push,1) -typedef struct CtxCommandState +void sqz_append_utf8 (Sqz **a, const char *utf8) { - uint16_t pos; - uint32_t active; -} CtxCommandState; -#pragma pack(pop) + sqz_insert_utf8 (a, -1, utf8); +} -struct _CtxHasher +void sqz_append_unichar (Sqz **a, uint32_t unichar) { - CtxRasterizer rasterizer; - int cols; - int rows; - uint32_t hashes[CTX_HASH_COLS*CTX_HASH_ROWS]; - CtxMurmur murmur_fill[CTX_MAX_STATES]; - CtxMurmur murmur_stroke[CTX_MAX_STATES]; - int source_level; - int pos; + sqz_insert_unichar (a, -1, unichar); +} - int prev_command; +#define SQZ_EXPAND_PRINTF \ + va_list ap; \ + size_t needed; \ + char *buffer; \ + va_start (ap, format); \ + needed = vsnprintf (NULL, 0, format, ap) + 1; \ + if (needed < 256) \ + buffer = alloca (needed);\ + else\ + buffer = malloc (needed);\ + va_end (ap);\ + va_start (ap, format);\ + vsnprintf (buffer, needed, format, ap);\ + va_end (ap);\ + Sqz *b = sqz_utf8 (buffer);\ + if (needed >= 256)\ + free (buffer); - CtxDrawlist *drawlist; +Sqz *sqz_printf (const char *format, ...) +{ + SQZ_EXPAND_PRINTF; + return b; +} -}; +void sqz_insert_printf (Sqz **a, int pos, const char *format, ...) +{ + SQZ_EXPAND_PRINTF; + sqz_insert (a, pos, b); + sqz_unref (b); +} -#if CTX_RASTERIZER -void ctx_rasterizer_deinit (CtxRasterizer *rasterizer); -void ctx_rasterizer_destroy (CtxRasterizer *rasterizer); -#endif +void sqz_replace_printf (Sqz **a, int pos, int length, const char *format, ...) +{ + SQZ_EXPAND_PRINTF; + sqz_replace (a, pos, length, b); + sqz_unref (b); +} -enum { - NC_MOUSE_NONE = 0, - NC_MOUSE_PRESS = 1, /* "mouse-pressed", "mouse-released" */ - NC_MOUSE_DRAG = 2, /* + "mouse-drag" (motion with pressed button) */ - NC_MOUSE_ALL = 3 /* + "mouse-motion" (also delivered for release) */ -}; -void _ctx_mouse (Ctx *term, int mode); -void nc_at_exit (void); +void sqz_append_printf (Sqz **a, const char *format, ...) +{ + SQZ_EXPAND_PRINTF; + sqz_insert (a, -1, b); + sqz_unref (b); +} -int ctx_terminal_width (void); -int ctx_terminal_height (void); -int ctx_terminal_cols (void); -int ctx_terminal_rows (void); -extern int ctx_frame_ack; +int sqz_strcmp (Sqz *a, Sqz *b) +{ + if (a == b) return 0; + char tmp_a[16]; + char tmp_b[16]; + return strcmp (sqz_decode (a, tmp_a), sqz_decode (b, tmp_b)); +} -typedef struct _CtxCtx CtxCtx; -struct _CtxCtx +static void _sqz_steal (Sqz **a, Sqz *b) { - CtxBackend backend; - int width; - int height; - int cols; - int rows; - int was_down; -}; + if (*a) + sqz_unref (*a); + *a = b; +} +void sqz_set (Sqz **a, Sqz *b) +{ + if (*a) + sqz_unref (*a); + *a = sqz_ref (b); +} -extern int _ctx_max_threads; -extern int _ctx_enable_hash_cache; -void -ctx_set (Ctx *ctx, uint32_t key_hash, const char *string, int len); -const char * -ctx_get (Ctx *ctx, const char *key); +void sqz_set_utf8 (Sqz **a, const char *str) +{ + _sqz_steal (a, sqz_utf8 (str)); +} -Ctx *ctx_new_ctx (int width, int height); -Ctx *ctx_new_fb (int width, int height); -Ctx *ctx_new_headless (int width, int height); -Ctx *ctx_new_kms (int width, int height); -Ctx *ctx_new_sdl (int width, int height); -Ctx *ctx_new_term (int width, int height); -Ctx *ctx_new_termimg (int width, int height); +void sqz_set_printf (Sqz **a, const char *format, ...) +{ + SQZ_EXPAND_PRINTF; + _sqz_steal (a, b); +} -int ctx_resolve_font (const char *name); +void sqz_unset (Sqz **a) +{ + if (*a == NULL) return; + sqz_unref (*a); + *a = NULL; +} -#if CTX_U8_TO_FLOAT_LUT -extern float ctx_u8_float[256]; -#define ctx_u8_to_float(val_u8) ctx_u8_float[((uint8_t)(val_u8))] -#else -#define ctx_u8_to_float(val_u8) (val_u8/255.0f) +sqz_id_t sqz_id (Sqz *squozed) +{ + if (!squozed) return 0; + if (sqz_is_embedded (squozed)) + return ((size_t)(squozed)); + else + { +#if SQUOZE_INTERN_DIRECT_STRING + squozed--; #endif + return squozed->hash; + } +} -static inline uint8_t ctx_float_to_u8 (float val_f) +int sqz_length (Sqz *squozed) { -#if 1 - union { float f; uint32_t i; } u; - u.f = 32768.0f + val_f * (255.0f / 256.0f); - return (uint8_t)u.i; + char buf[15]; + if (!squozed) return 0; + return squoze_utf8_strlen(sqz_decode (squozed, buf)); +} + +// XXX : not used - remove it, and be unicode native? +int sqz_byte_length (Sqz *squozed) +{ + char buf[15]; + if (!squozed) return 0; +#if 0 + return strlen(sqz_decode (squozed, buf)); #else - return val_f < 0.0f ? 0 : val_f > 1.0f ? 0xff : 0xff * val_f + 0.5f; + if (sqz_is_embedded (squozed)) + { + sqz_decode (squozed, buf); + return strlen (buf); + } + else + { +#if SQUOZE_INTERN_DIRECT_STRING + squozed--; +#endif +#if SQUOZE_STORE_LENGTH + return squozed->length; +#endif + return strlen (squozed->string); + } #endif + return 0; } +Sqz *sqz_cat (Sqz *a, Sqz *b) +{ + char buf_a[16]; + char buf_b[16]; + const char *str_a = sqz_decode (a, buf_a); + const char *str_b = sqz_decode (b, buf_b); + int len_a = strlen (str_a); + int len_b = strlen (str_b); + if (len_a + len_b < 128) + { + char temp[128]; + temp[0]=0; + strcpy (temp, str_a); + if (str_b) + strcpy (&temp[strlen(temp)], str_b); + return sqz_utf8 (temp); + } + else + { + char *temp = malloc (len_a + len_b + 1); + temp[0]=0; + strcpy (temp, str_a); + if (str_b) + strcpy (&temp[strlen(temp)], str_b); + Sqz *ret = sqz_utf8 (temp); + free (temp); + return ret; + } +} -#define CTX_CSS_LUMINANCE_RED 0.3f -#define CTX_CSS_LUMINANCE_GREEN 0.59f -#define CTX_CSS_LUMINANCE_BLUE 0.11f -/* works on both float and uint8_t */ -#define CTX_CSS_RGB_TO_LUMINANCE(rgb) (\ - (rgb[0]) * CTX_CSS_LUMINANCE_RED + \ - (rgb[1]) * CTX_CSS_LUMINANCE_GREEN +\ - (rgb[2]) * CTX_CSS_LUMINANCE_BLUE) +SqzPool *sqz_pool_new (SqzPool *fallback) +{ + SqzPool *pool = (SqzPool*)calloc (sizeof (SqzPool), 1); + pool->fallback = fallback; + pool->next = sqz_pools; + sqz_pools = pool; + if (fallback) + sqz_pool_ref (fallback); + return pool; +} -const char *ctx_nct_get_event (Ctx *n, int timeoutms, int *x, int *y); -const char *ctx_native_get_event (Ctx *n, int timeoutms); -void -ctx_color_get_rgba8 (CtxState *state, CtxColor *color, uint8_t *out); -void ctx_color_get_graya_u8 (CtxState *state, CtxColor *color, uint8_t *out); -float ctx_float_color_rgb_to_gray (CtxState *state, const float *rgb); -void ctx_color_get_graya (CtxState *state, CtxColor *color, float *out); -void ctx_rgb_to_cmyk (float r, float g, float b, - float *c_out, float *m_out, float *y_out, float *k_out); -uint8_t ctx_u8_color_rgb_to_gray (CtxState *state, const uint8_t *rgb); -#if CTX_ENABLE_CMYK -void ctx_color_get_cmyka (CtxState *state, CtxColor *color, float *out); -#endif -static void ctx_color_set_RGBA8 (CtxState *state, CtxColor *color, uint8_t r, uint8_t g, uint8_t b, uint8_t a); -void ctx_color_set_rgba (CtxState *state, CtxColor *color, float r, float g, float b, float a); -static void ctx_color_set_drgba (CtxState *state, CtxColor *color, float r, float g, float b, float a); -void ctx_color_get_cmyka (CtxState *state, CtxColor *color, float *out); -static void ctx_color_set_cmyka (CtxState *state, CtxColor *color, float c, float m, float y, float k, float a); -static void ctx_color_set_dcmyka (CtxState *state, CtxColor *color, float c, float m, float y, float k, float a); -static void ctx_color_set_graya (CtxState *state, CtxColor *color, float gray, float alpha); +void sqz_pool_ref (SqzPool *pool) +{ + if (!pool) return; + pool->ref_count--; +} -int ctx_color_model_get_components (CtxColorModel model); +static void sqz_pool_destroy (SqzPool *pool) +{ +#if 0 + fprintf (stderr, "destorying pool: size:%i count:%i embedded:%i\n", + pool->size, pool->count, pool->count_embedded); +#endif + for (int i = 0; i < pool->size; i++) + { + if (pool->hashtable[i]) + free (pool->hashtable[i]); + pool->hashtable[i] = 0; + } + if (pool->fallback) + sqz_pool_unref (pool->fallback); -static void ctx_state_set (CtxState *state, uint32_t key, float value); + if (pool == sqz_pools) + { + sqz_pools = pool->next; + } + else + { + SqzPool *prev = NULL; + SqzPool *iter = sqz_pools; + while (iter && iter != pool) + { + prev = iter; + iter = iter->next; + } + if (prev) // XXX not needed + prev->next = pool->next; + } + pool->size = 0; + pool->count = 0; + if (pool->hashtable) + free (pool->hashtable); + pool->hashtable = NULL; -static void -ctx_matrix_set (CtxMatrix *matrix, float a, float b, float c, float d, float e, float f, float g, float h, float i); + // XXX report non unreffed items based on config +} +void sqz_pool_unref (SqzPool *pool) +{ + if (!pool) return; + if (pool->ref_count == 0) + { + sqz_pool_destroy (pool); + free (pool); + } + else + { + pool->ref_count--; + } +} -static void ctx_font_setup (); -static float ctx_state_get (CtxState *state, uint32_t hash); +void +sqz_cleanup (void) +{ + sqz_pool_destroy (&global_pool); + // also destory other known pools + // XXX : when debugging report leaked pools +} +#endif -#if CTX_RASTERIZER +// UTF5 implementation -static void -ctx_rasterizer_rel_move_to (CtxRasterizer *rasterizer, float x, float y); -static void -ctx_rasterizer_rel_line_to (CtxRasterizer *rasterizer, float x, float y); +#if SQUOZE_USE_UTF5 -static void -ctx_rasterizer_move_to (CtxRasterizer *rasterizer, float x, float y); -static void -ctx_rasterizer_line_to (CtxRasterizer *rasterizer, float x, float y); -static void -ctx_rasterizer_curve_to (CtxRasterizer *rasterizer, - float x0, float y0, - float x1, float y1, - float x2, float y2); -static void -ctx_rasterizer_rel_curve_to (CtxRasterizer *rasterizer, - float x0, float y0, - float x1, float y1, - float x2, float y2); +// extra value meaning in UTF5 mode +#define SQUOZE_ENTER_SQUEEZE 16 -static void -ctx_rasterizer_reset (CtxRasterizer *rasterizer); -static void -ctx_rasterizer_arc (CtxRasterizer *rasterizer, - float x, - float y, - float radius, - float start_angle, - float end_angle, - int anticlockwise); +// value meanings in squeeze mode +#define SQUOZE_SPACE 0 +#define SQUOZE_DEC_OFFSET_A 27 +#define SQUOZE_INC_OFFSET_A 28 +#define SQUOZE_DEC_OFFSET_B 29 +#define SQUOZE_INC_OFFSET_B 30 +#define SQUOZE_ENTER_UTF5 31 -static void -ctx_rasterizer_quad_to (CtxRasterizer *rasterizer, - float cx, - float cy, - float x, - float y); -static void -ctx_rasterizer_rel_quad_to (CtxRasterizer *rasterizer, - float cx, - float cy, - float x, - float y); +static inline uint32_t squoze_utf8_to_unichar (const char *input); +static inline int squoze_unichar_to_utf8 (uint32_t ch, uint8_t *dest); +static inline int squoze_utf8_len (const unsigned char first_byte); +static inline int squoze_utf8_strlen (const char *s); -static void -ctx_rasterizer_rectangle (CtxRasterizer *rasterizer, - float x, - float y, - float width, - float height); -static void ctx_rasterizer_finish_shape (CtxRasterizer *rasterizer); -static void ctx_rasterizer_clip (CtxRasterizer *rasterizer); -static void -ctx_rasterizer_set_font (CtxRasterizer *rasterizer, const char *font_name); +/* returns the base-offset of the segment this unichar belongs to, + * + * segments are 26 items long and are offset so that 'a'-'z' is + * one segment. + */ +#define SQUOZE_JUMP_STRIDE 26 +#define SQUOZE_JUMP_OFFSET 19 +static inline int squoze_new_offset (uint32_t unichar) +{ + uint32_t ret = unichar - (unichar % SQUOZE_JUMP_STRIDE) + SQUOZE_JUMP_OFFSET; + if (ret > unichar) ret -= SQUOZE_JUMP_STRIDE; + return ret; +} -static void -ctx_rasterizer_gradient_add_stop (CtxRasterizer *rasterizer, float pos, float *rgba); -static void -ctx_rasterizer_set_pixel (CtxRasterizer *rasterizer, - uint16_t x, - uint16_t y, - uint8_t r, - uint8_t g, - uint8_t b, - uint8_t a); -static void -ctx_rasterizer_round_rectangle (CtxRasterizer *rasterizer, float x, float y, float width, float height, float corner_radius); +static inline int squoze_needed_jump (uint32_t off, uint32_t unicha) +{ + int count = 0; + int unichar = unicha; + int offset = off; -#endif + if (unichar == 32) // space is always in range + return 0; -#if CTX_ENABLE_CM // XXX to be moved to ctx.h -void -ctx_set_drgb_space (Ctx *ctx, int device_space); -void -ctx_set_dcmyk_space (Ctx *ctx, int device_space); -void -ctx_rgb_space (Ctx *ctx, int device_space); -void -ctx_set_cmyk_space (Ctx *ctx, int device_space); -#endif + /* TODO: replace this with direct computation of values instead of loop */ + while (unichar < offset) + { + offset -= SQUOZE_JUMP_STRIDE; + count --; + } + if (count) + return count; -#endif + return (unichar - offset) / SQUOZE_JUMP_STRIDE; +} -CtxRasterizer * -ctx_rasterizer_init (CtxRasterizer *rasterizer, Ctx *ctx, Ctx *texture_source, CtxState *state, void *data, int x, int y, int width, int height, int stride, CtxPixelFormat pixel_format, CtxAntialias antialias); -CTX_INLINE static uint8_t ctx_lerp_u8 (uint8_t v0, uint8_t v1, uint8_t dx) +static inline int +squoze_utf5_length (uint32_t unichar) { -#if 0 - return v0 + ((v1-v0) * dx)/255; + if (unichar == 0) + return 1; +#if SQUOZE_USE_BUILTIN_CLZ + return __builtin_clz(unichar)/4+1; #else - return ( ( ( ( (v0) <<8) + (dx) * ( (v1) - (v0) ) ) ) >>8); + int nibbles = 1; + while (unichar) + { + nibbles ++; + unichar /= 16; + } + return nibbles; #endif } -CTX_INLINE static uint32_t ctx_lerp_RGBA8 (const uint32_t v0, const uint32_t v1, const uint8_t dx) -{ -#if 0 - char bv0[4]; - char bv1[4]; - char res[4]; - memcpy (&bv0[0], &v0, 4); - memcpy (&bv1[0], &v1, 4); - for (int c = 0; c < 4; c++) - res [c] = ctx_lerp_u8 (bv0[c], bv1[c], dx); - return ((uint32_t*)(&res[0]))[0]; -#else - const uint32_t cov = dx; - const uint32_t si_ga = (v1 & 0xff00ff00); - const uint32_t si_rb = v1 & 0x00ff00ff; - const uint32_t di_rb = v0 & 0x00ff00ff; - const uint32_t d_rb = si_rb - di_rb; - const uint32_t di_ga = v0 & 0xff00ff00; - const uint32_t d_ga = (si_ga >>8) - (di_ga>>8); - return - (((di_rb + ((0xff00ff + d_rb * cov)>>8)) & 0x00ff00ff)) | - (((di_ga + (0xff00ff + d_ga * cov)) & 0xff00ff00)); +typedef struct EncodeUtf5 { + int is_utf5; + int offset; + int length; + void *write_data; + uint32_t current; +} EncodeUtf5; -#endif +static inline int squoze_compute_cost_utf5 (int offset, int val, int utf5_length, int next_val, int next_utf5_length) +{ + int cost = 0; + cost += utf5_length; + if (next_val) + { + cost += next_utf5_length; + } + return cost; } -CTX_INLINE static void ctx_lerp_RGBA8_split (const uint32_t v0, const uint32_t v1, const uint8_t dx, - uint32_t *dest_ga, uint32_t *dest_rb) +static inline int squoze_compute_cost_squeezed (int offset, int val, int needed_jump, int next_val, int next_utf5_length) { - const uint32_t cov = dx; - const uint32_t si_ga = v1 & 0xff00ff00; - const uint32_t si_rb = v1 & 0x00ff00ff; - const uint32_t di_ga = v0 & 0xff00ff00; - const uint32_t di_rb = v0 & 0x00ff00ff; - const uint32_t d_rb = si_rb - di_rb; - const uint32_t d_ga = (si_ga >>8) - (di_ga >> 8); - *dest_rb = (((di_rb + ((0xff00ff + d_rb * cov)>>8)) & 0x00ff00ff)); - *dest_ga = (((di_ga + (0xff00ff + d_ga * cov)) & 0xff00ff00)); -} - -CTX_INLINE static uint32_t ctx_lerp_RGBA8_merge (uint32_t di_ga, uint32_t di_rb, uint32_t si_ga, uint32_t si_rb, const uint8_t dx) -{ - const uint32_t cov = dx; - const uint32_t d_rb = si_rb - di_rb; - const uint32_t d_ga = (si_ga >> 8) - (di_ga >> 8); - return - (((di_rb + ((0xff00ff + d_rb * cov)>>8)) & 0x00ff00ff)) | - ((di_ga + ((0xff00ff + d_ga * cov) & 0xff00ff00))); -} - -CTX_INLINE static uint32_t ctx_lerp_RGBA8_2 (const uint32_t v0, uint32_t si_ga, uint32_t si_rb, const uint8_t dx) -{ - const uint32_t cov = dx; - const uint32_t di_ga = ( v0 & 0xff00ff00); - const uint32_t di_rb = v0 & 0x00ff00ff; - const uint32_t d_rb = si_rb - di_rb; - const uint32_t d_ga = si_ga - (di_ga>>8); - return - (((di_rb + ((0xff00ff + d_rb * cov)>>8)) & 0x00ff00ff)) | - (((di_ga + (0xff00ff + d_ga * cov)) & 0xff00ff00)); -} + int cost = 0; + if (needed_jump == 0) + { + cost += 1; + } + else if (needed_jump >= -2 && needed_jump <= 2) + { + cost += 2; + offset += SQUOZE_JUMP_STRIDE * needed_jump; + } + else if (needed_jump >= -10 && needed_jump <= 10) + { + cost += 3; + offset += SQUOZE_JUMP_STRIDE * needed_jump; + } + else + { + cost += 100; // very expensive, makes the other choice win + } -CTX_INLINE static float -ctx_lerpf (float v0, float v1, float dx) -{ - return v0 + (v1-v0) * dx; -} + if (next_val) + { + int change_cost = 1 + squoze_utf5_length (next_val); + int no_change_cost = 0; + needed_jump = squoze_needed_jump (offset, next_val); -CTX_INLINE static float -ctx_catmull_rom (float v0, float v1, float v2, float v3, float t) -{ - float ya = v0, yb = v1, yc = v2, yd = v3; - float a3 = 0.5f * (-ya + 3 * yb - 3 * yc + yd); - float a2 = 0.5f * (2 * ya - 5 * yb + 4 * yc - yd); - float a1 = 0.5f * (-ya + yc); - float a0 = yb; - return a3 * t * t * t + - a2 * t * t + - a1 * t + - a0; -} + if (needed_jump == 0) + { + no_change_cost += 1; + } + else if (needed_jump >= -2 && needed_jump <= 2) + { + no_change_cost += 2; + } + else if (needed_jump >= -10 && needed_jump <= 10) + { + no_change_cost += 3; + offset += SQUOZE_JUMP_STRIDE * needed_jump; + } + else + { + no_change_cost = change_cost; + } + if (change_cost < no_change_cost) + cost += change_cost; + else + cost += no_change_cost; + } -CTX_INLINE static float -ctx_catmull_rom_left (float v0, float v1, float v2, float t) -{ - float ya = v0, yb = v1, yc = v2; - float a2 = 0.5f * (ya - 2 * yb + yc); - float a1 = 0.5f * (-3 * ya + 4 * yb - yc); - float a0 = ya; - return a2 * t * t + - a1 * t + - a0; + return cost; } -CTX_INLINE static float -ctx_catmull_rom_right (float v0, float v1, float v2, float t) +static inline void squoze5_encode (const char *input, int inlen, + char *output, int *r_outlen, + int permit_squeezed, + int escape_endzero) { - float ya = v0, yb = v1, yc = v2; - float a2 = 0.5f * (ya - 2 * yb + yc); - float a1 = 0.5f * (-ya + yc); - float a0 = yb; - return a2 * t * t + - a1 * t + - a0; -} - - -#ifndef CTX_MIN -#define CTX_MIN(a,b) (((a)<(b))?(a):(b)) -#endif -#ifndef CTX_MAX -#define CTX_MAX(a,b) (((a)>(b))?(a):(b)) -#endif + int offset = 97;//squoze_new_offset('a'); + int is_utf5 = 1; + int len = 0; -static inline void *ctx_calloc (size_t size, size_t count); + int first_len; + int next_val = squoze_utf8_to_unichar (&input[0]); + int next_utf5_length = squoze_utf5_length (next_val); + for (int i = 0; i < inlen; i+= first_len) + { + int val = next_val; + int utf5_length = next_utf5_length; + int needed_jump = squoze_needed_jump (offset, val); + first_len = squoze_utf8_len (input[i]); + if (i + first_len < inlen) + { + next_val = squoze_utf8_to_unichar (&input[i+first_len]); + next_utf5_length = squoze_utf5_length (next_val); + } -void ctx_screenshot (Ctx *ctx, const char *output_path); + if (is_utf5) + { + int change_cost = squoze_compute_cost_squeezed (offset, val, needed_jump, next_val, next_utf5_length); + int no_change_cost = squoze_compute_cost_utf5 (offset, val, utf5_length, next_val, next_utf5_length); + + if (i != 0) /* ignore cost of initial 'G' */ + change_cost += 1; + if (permit_squeezed && change_cost <= no_change_cost) + { + output[len++] = SQUOZE_ENTER_SQUEEZE; + is_utf5 = 0; + } + } + else + { + int change_cost = 1 + squoze_compute_cost_utf5 (offset, val, utf5_length, next_val, next_utf5_length); + int no_change_cost = squoze_compute_cost_squeezed (offset, val, needed_jump, next_val, next_utf5_length); -CtxSHA1 *ctx_sha1_new (void); -void ctx_sha1_free (CtxSHA1 *sha1); -int ctx_sha1_process(CtxSHA1 *sha1, const unsigned char * msg, unsigned long len); -int ctx_sha1_done(CtxSHA1 * sha1, unsigned char *out); + if (change_cost < no_change_cost) + { + output[len++] = SQUOZE_ENTER_UTF5; + is_utf5 = 1; + } + } -void _ctx_texture_lock (void); -void _ctx_texture_unlock (void); -uint8_t *ctx_define_texture_pixel_data (CtxEntry *entry); -void ctx_buffer_pixels_free (void *pixels, void *userdata); + if (!is_utf5) + { + if (needed_jump) + { + if (needed_jump >= -2 && needed_jump <= 2) + { + switch (needed_jump) + { + case -1: output[len++] = SQUOZE_DEC_OFFSET_B; break; + case 1: output[len++] = SQUOZE_INC_OFFSET_B; break; + case -2: output[len++] = SQUOZE_DEC_OFFSET_A; break; + case 2: output[len++] = SQUOZE_INC_OFFSET_A; break; + } + offset += SQUOZE_JUMP_STRIDE * needed_jump; + } + else if (needed_jump >= -10 && needed_jump <= 10) { + int encoded_val; + if (needed_jump < -2) + encoded_val = 5 - needed_jump; + else + encoded_val = needed_jump - 3; -/*ctx_texture_init: - * return value: eid, as passed in or if NULL generated by hashing pixels and width/height - * XXX this is low-level and not to be used directly use define_texture instead. XXX - */ -const char *ctx_texture_init ( - Ctx *ctx, - const char *eid, - int width, - int height, - int stride, - CtxPixelFormat format, - void *space, - uint8_t *pixels, - void (*freefunc) (void *pixels, void *user_data), - void *user_data); + output[len++] = (encoded_val / 4) + SQUOZE_DEC_OFFSET_A; + output[len++] = (encoded_val % 4) + SQUOZE_DEC_OFFSET_A; -#if CTX_TILED -#if !__COSMOPOLITAN__ -//#include <threads.h> -#endif + offset += SQUOZE_JUMP_STRIDE * needed_jump; + } + else + { +#ifdef assert + assert(0); // should not be reached #endif -typedef struct _CtxTiled CtxTiled; - - -typedef struct _EvSource EvSource; -struct _EvSource -{ - void *priv; /* private storage */ - - /* returns non 0 if there is events waiting */ - int (*has_event) (EvSource *ev_source); - - /* get an event, the returned event should be freed by the caller */ - char *(*get_event) (EvSource *ev_source); - - /* destroy/unref this instance */ - void (*destroy) (EvSource *ev_source); - - /* get the underlying fd, useful for using select on */ - int (*get_fd) (EvSource *ev_source); + output[len++] = SQUOZE_ENTER_UTF5; + is_utf5 = 1; + } + } + } + if (is_utf5) + { + offset = squoze_new_offset (val); + int quintet_no = 0; + uint8_t temp[12]={0,}; - void (*set_coord) (EvSource *ev_source, double x, double y); - /* set_coord is needed to warp relative cursors into normalized range, - * like normal mice/trackpads/nipples - to obey edges and more. - */ + while (val) + { + int oval = val % 16; + int hi = 16; + if (val / 16) + hi = 0; + temp[quintet_no++] = oval + hi; + val /= 16; + } + for (int i = 0; i < quintet_no; i++) + output[len++] = temp[quintet_no-1-i]; + } + else + { + output[len++] = (val == ' ')?SQUOZE_SPACE:val-offset+1; + } + } - /* if this returns non-0 select can be used for non-blocking.. */ -}; + if (escape_endzero && len && output[len-1]==0) + { + if (is_utf5) + output[len++] = 16; + else + output[len++] = SQUOZE_ENTER_UTF5; + } + output[len]=0; + if (r_outlen) + *r_outlen = len; +} -struct _CtxTiled +/* squoze_encode_int: + * @input utf8 input data + * @inlen length of @input in bytes + * @maxlen maximum number of quintets to encode + * @overflow pointer to int that gets set to 1 if we overflow + * @permit_squeezed + * + */ +static inline size_t squoze5_encode_int (const char *input, int inlen, + int maxlen, int *overflow, + int escape_endzero) { - CtxBackend backend; - void (*show_frame) (void *backend, int block); - int width; - int height; - int cols; - int rows; - int was_down; - uint8_t *pixels; - Ctx *ctx_copy; - Ctx *host[CTX_MAX_THREADS]; - CtxAntialias antialias; - int quit; -#if CTX_TILED - //_Atomic - int thread_quit; -#endif - int shown_frame; - int render_frame; - int rendered_frame[CTX_MAX_THREADS]; - int frame; - int min_col; // hasher cols and rows - int min_row; - int max_col; - int max_row; - uint32_t hashes[CTX_HASH_ROWS * CTX_HASH_COLS]; - int8_t tile_affinity[CTX_HASH_ROWS * CTX_HASH_COLS]; // which render thread no is - // responsible for a tile - // - - int pointer_down[3]; + size_t ret = 0; + int offset = 97;//squoze_new_offset('a'); + int is_utf5 = 1; + int len = 0; - CtxCursor shown_cursor; - int vt_active; - EvSource *evsource[4]; - int evsource_count; - uint8_t *fb; -#if CTX_THREADS -#if CTX_TILED - cnd_t cond; - mtx_t mtx; -#endif -#endif -}; + int start_utf5 = 1; + int gotzero = 0; + +#define ADD_QUINTET(q) \ + do { \ + if (len + inlen-i > maxlen) {\ + *overflow = 1;\ + return 0;\ + }\ + ret |= ((size_t)(q))<<(5*len++); gotzero = (q==0);\ + } while (0) + + int first_len; + int next_val = squoze_utf8_to_unichar (&input[0]); + int next_utf5_length = squoze_utf5_length (next_val); + int i = 0; + for (int i = 0; i < inlen; i+= first_len) + { + int val = next_val; + int utf5_length = squoze_utf5_length (val); + int needed_jump = squoze_needed_jump (offset, val); + first_len = squoze_utf8_len (input[i]); + if (i + first_len < inlen) + { + next_val = squoze_utf8_to_unichar (&input[i+first_len]); + next_utf5_length = squoze_utf5_length (next_val); + } + else + { + next_val = 0; + next_utf5_length = 0; + } -static inline Ctx *ctx_backend_get_ctx (void *backend) -{ - CtxBackend *r = (CtxBackend*)backend; - if (r) return r->ctx; - return NULL; -} + if (is_utf5) + { + int change_cost = squoze_compute_cost_squeezed (offset, val, needed_jump, next_val, next_utf5_length); + int no_change_cost = squoze_compute_cost_utf5 (offset, val, utf5_length, next_val, next_utf5_length); + + if (i != 0) /* ignore cost of initial 'G' */ + change_cost += 1; -void -_ctx_texture_prepare_color_management (CtxState *state, - CtxBuffer *buffer); + if (change_cost <= no_change_cost) + { + if (i != 0) + { + ADD_QUINTET(SQUOZE_ENTER_SQUEEZE); + } + else + start_utf5 = 0; -int ctx_is_set (Ctx *ctx, uint32_t hash); + is_utf5 = 0; + } + } + else + { + int change_cost = 1 + squoze_compute_cost_utf5 (offset, val, utf5_length, next_val, next_utf5_length); + int no_change_cost = squoze_compute_cost_squeezed (offset, val, needed_jump, next_val, next_utf5_length); -static Ctx *_ctx_new_drawlist (int width, int height); + if (change_cost < no_change_cost) + { + ADD_QUINTET(SQUOZE_ENTER_UTF5); + is_utf5 = 1; + } + } + if (!is_utf5) + { + if (needed_jump) + { + if (needed_jump >= -2 && needed_jump <= 2) + { + switch (needed_jump) + { + case -1: ADD_QUINTET(SQUOZE_DEC_OFFSET_B); break; + case 1: ADD_QUINTET(SQUOZE_INC_OFFSET_B); break; + case -2: ADD_QUINTET(SQUOZE_DEC_OFFSET_A); break; + case 2: ADD_QUINTET(SQUOZE_INC_OFFSET_A); break; + } + offset += SQUOZE_JUMP_STRIDE * needed_jump; + } + else if (needed_jump >= -10 && needed_jump <= 10) { + int encoded_val; + if (needed_jump < -2) + encoded_val = 5 - needed_jump; + else + encoded_val = needed_jump - 3; -static inline void -_ctx_matrix_apply_transform (const CtxMatrix *m, float *x, float *y) -{ - float x_in = *x; - 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; -} + ADD_QUINTET ((encoded_val/4) + SQUOZE_DEC_OFFSET_A); + ADD_QUINTET ((encoded_val%4) + SQUOZE_DEC_OFFSET_A); + offset += SQUOZE_JUMP_STRIDE * needed_jump; + } + else + { +#ifdef assert + assert(0); // should not be reached +#endif + ADD_QUINTET (SQUOZE_ENTER_UTF5); + is_utf5 = 1; + } + } + } + if (is_utf5) + { + offset = squoze_new_offset (val); + int quintet_no = 0; + uint8_t temp[12]={0,}; -static inline void -_ctx_matrix_multiply (CtxMatrix *result, - const CtxMatrix *t, - const CtxMatrix *s) -{ - CtxMatrix r; + while (val) + { + temp[quintet_no++] = (val&0xf) + (val/16)?0:16; + val /= 16; + } + for (int j = 0; j < quintet_no; j++) + ADD_QUINTET(temp[quintet_no-1-j]); + } + else + { + ADD_QUINTET((val == ' ')?SQUOZE_SPACE:val-offset+1); + } + } - for (unsigned int i = 0; i < 3; i++) +#if 1 + if (escape_endzero && len && gotzero) { - r.m[i][0] = t->m[i][0] * s->m[0][0] - + t->m[i][1] * s->m[1][0] - + t->m[i][2] * s->m[2][0]; - r.m[i][1] = t->m[i][0] * s->m[0][1] - + t->m[i][1] * s->m[1][1] - + t->m[i][2] * s->m[2][1]; - r.m[i][2] = t->m[i][0] * s->m[0][2] - + t->m[i][1] * s->m[1][2] - + t->m[i][2] * s->m[2][2]; + // do a mode-change after 0 to avoid 0 being interpreted + // as end of quintets + ADD_QUINTET(is_utf5?16:SQUOZE_ENTER_UTF5); } - *result = r; -} - -static inline void -_ctx_matrix_identity (CtxMatrix *matrix) -{ - matrix->m[0][0] = 1.0f; - matrix->m[0][1] = 0.0f; - matrix->m[0][2] = 0.0f; - matrix->m[1][0] = 0.0f; - matrix->m[1][1] = 1.0f; - matrix->m[1][2] = 0.0f; - matrix->m[2][0] = 0.0f; - matrix->m[2][1] = 0.0f; - matrix->m[2][2] = 1.0f; -} - -static inline void -_ctx_user_to_device_prepped (CtxState *state, float x, float y, int *out_x, int *out_y); -static inline void -_ctx_user_to_device_prepped_fixed (CtxState *state, int x, int y, int *x_out, int *y_out); - -static int ctx_float_to_string_index (float val); - -void -ctx_render_ctx_masked (Ctx *ctx, Ctx *d_ctx, uint32_t mask); - -static void ctx_state_set_blob (CtxState *state, uint32_t key, uint8_t *data, int len); +#endif +#undef ADD_QUINTET -static inline void -_ctx_transform_prime (CtxState *state); + return (ret<<2) | ((start_utf5*2)|1); +} -void ctx_push_backend (Ctx *ctx, - void *backend); -void ctx_pop_backend (Ctx *ctx); +typedef struct SquozeUtf5Dec { + int is_utf5; + int offset; + void *write_data; + uint32_t current; + void (*append_unichar) (uint32_t unichar, void *write_data); + int jumped_amount; + int jump_mode; +} SquozeUtf5Dec; +typedef struct SquozeUtf5DecDefaultData { + uint8_t *buf; + int length; +} SquozeUtf5DecDefaultData; -static inline float ctx_fmod1f (float val) +static void squoze_decode_utf5_append_unichar_as_utf8 (uint32_t unichar, void *write_data) { - return ctx_fabsf (val - (int)(val)); + SquozeUtf5DecDefaultData *data = (SquozeUtf5DecDefaultData*)write_data; + int length = squoze_unichar_to_utf8 (unichar, &data->buf[data->length]); + data->buf[data->length += length] = 0; } -static inline float ctx_fmodf (float val, float modulus) +static void squoze_decode_jump (SquozeUtf5Dec *dec, uint8_t in) { - return ctx_fmod1f(val/modulus) * modulus; + dec->offset -= SQUOZE_JUMP_STRIDE * dec->jumped_amount; + int jump_len = (dec->jump_mode - SQUOZE_DEC_OFFSET_A) * 4 + + (in - SQUOZE_DEC_OFFSET_A); + if (jump_len > 7) + jump_len = 5 - jump_len; + else + jump_len += 3; + dec->offset += jump_len * SQUOZE_JUMP_STRIDE; + dec->jumped_amount = 0; } -#if EMSCRIPTEN -#define CTX_EXPORT EMSCRIPTEN_KEEPALIVE -#else -#define CTX_EXPORT -#endif - - - -#endif - -#if CTX_EVENTS -#include <sys/select.h> -#endif -/* - * each scanline is encoded as a header + rle data + free space + reversed palette - * - * header: u16_length, u8_colors, u8_0 - * - * The u8_0 padding byte needs to be 0 in the future it might hold higher values, but - * always smaller than colors, this to make it use a value that never naturally occurs - * for u8 associated alpha, making it possible to store either CBRLE or raw pixel - * values in 32bpp scanlines. - * - * The indexed palette is per-scanline, the first eight palette entries are - * fixed as a grayscale. The palette is allocated on the fly and is stored from - * the end of the scanline. As the palette fills up it gets harder and harder - * for new colors to warrant a allocating new entries, gradients only allocate - * colors for their start and end colors. - * - * When we run out of free space the scanline gets recompressed, reducing color - * depth and simplifying pixel data, for 32bpp scanlines this can in the future - * mean fall over to uncompressed raw data. Doing composiing on RLE encoded - * data can on high resolution framebuffers lead to significantly lower memory - * bandwidth and computation required on frames with significant overdraw; to - * fully realise this potential further integration is needed. - * - */ - - - -#if CTX_IMPLEMENTATION || CTX_SIMD_BUILD -#if CTX_ENABLE_CBRLE - - -#define CBRLE_GRADIENT 1 -#define ALLOW_MERGE 1 - -#define PAL_GRAY_PREDEF 0 - -#define GRADIENT_THRESHOLD 1 -#define COLOR_THRESHOLD_START 6 -#define COLOR_THRESHOLD_END (color_budget<=128?32:21) - -#define MAX_GRADIENT_LENGTH (color_budget<=128?63:63) - -// with high numbers, repeated recompressions steal a *lot* of time -// when data is overflowing -// -#define MAX_RECOMPRESS 2 - -// increase to have larger safety margin between pixeldata -// and palette -#define BORDER 1 - - -// -- currently unused -#define MERGE_THRESHOLD 64 //(no<2?50:98) -#define MERGE_CHANCE 42 //(no<2?25:80) - -#define PAL_GRAY_OFFSET (PAL_GRAY_PREDEF * 8) - -enum { - CBRLE_MODE_SET=0, - CBRLE_MODE_OVER=1, - CBRLE_MODE_COPY=2, - CBRLE_MODE_SET_COLOR=3 -}; - - -/// max 32 or 64 colors per scanline -// -// 1 5 2 -// 1 7 8 -// 1 4 4 4 1 1 1 - the last 3bits are lengths 0 means 1 and 1 means 2 for 2 first -static inline int encode_pix (uint8_t *cbrle, int pos, - uint8_t idx, int range, int gradient, - int allow_merge) +static void squoze_decode_utf5 (SquozeUtf5Dec *dec, uint8_t in) { -#if ALLOW_MERGE - if (allow_merge && pos > 3) - { - if (cbrle[pos-1] < 128 && // - cbrle[pos-2] < 128 && // previous must be single - (cbrle[pos-1] >> 4) == 0 && idx <4 && range == 1 && - (cbrle[pos-1]&15) <4) + if (dec->is_utf5) + { + if (in >= 16) + { + if (dec->current) { - int previdx = cbrle[pos-1]&15; - cbrle[pos-1] = (8-1) * 16 + (idx << 2) + previdx; - return 0; + dec->offset = squoze_new_offset (dec->current); + dec->append_unichar (dec->current, dec->write_data); + dec->current = 0; } - } -#endif - - if (range > 64) - fprintf (stderr, "ERROR trying to encode too big range %i!\n", range); - if (idx <= 15 && range < 8 && !gradient) - { - cbrle[pos] = (range-1) * 16 + idx; - return 1; - } - else - { - cbrle[pos] = (range) + 128 + gradient * 64; - cbrle[pos+1] = idx; - return 2; - } -} - - -// pix: pointer to data to decode -// ret_col: where to store 8bit color index -// length_px: number of pixels wide -// gradient: set to 1 if it is a gradient -// -// returns number of bytes in encoded form. -static inline int decode_pix (const uint8_t *pix, - uint8_t *ret_col1, - uint8_t *ret_col2, - int *length_px, - int *gradient) -{ - int encoded_length = 1; - int len = 1; - int col = 0; - int col2 = 0; - if (gradient) - *gradient = 0; - if ((*pix & 0x80) == 0) - { - len = pix[0] >> 4; - len++; - if (len == 8) - { - len = 2; - col = pix[0] & 15; - col2 = col >> 2; - col = col & 3; - if (ret_col2) - *ret_col2 = col2; + } + if (in == SQUOZE_ENTER_SQUEEZE) + { + if (dec->current) + { + dec->offset = squoze_new_offset (dec->current); + dec->append_unichar (dec->current, dec->write_data); + dec->current = 0; + } + dec->is_utf5 = 0; } else { - col = ( pix[0] & 15); - if (ret_col2) - *ret_col2 = col; + dec->current = dec->current * 16 + (in % 16); } } else { - len = pix[0]-128; - if (len > 64) + if (dec->jumped_amount) + { + switch (in) + { + case SQUOZE_DEC_OFFSET_A: + case SQUOZE_DEC_OFFSET_B: + case SQUOZE_INC_OFFSET_A: + case SQUOZE_INC_OFFSET_B: + squoze_decode_jump (dec, in); + break; + default: + dec->append_unichar (dec->offset + (in - 1), dec->write_data); + dec->jumped_amount = 0; + dec->jump_mode = 0; + break; + } + } + else { - len -= 64; - if(gradient) - *gradient = 1; + switch (in) + { + case SQUOZE_ENTER_UTF5: + dec->is_utf5 = 1; + dec->jumped_amount = 0; + dec->jump_mode = 0; + break; + case SQUOZE_SPACE: + dec->append_unichar (' ', dec->write_data); + dec->jumped_amount = 0; + dec->jump_mode = 0; + break; + case SQUOZE_DEC_OFFSET_A: + dec->jumped_amount = -2; + dec->jump_mode = in; + dec->offset += dec->jumped_amount * SQUOZE_JUMP_STRIDE; + break; + case SQUOZE_INC_OFFSET_A: + dec->jumped_amount = 2; + dec->jump_mode = in; + dec->offset += dec->jumped_amount * SQUOZE_JUMP_STRIDE; + break; + case SQUOZE_DEC_OFFSET_B: + dec->jumped_amount = -1; + dec->jump_mode = in; + dec->offset += dec->jumped_amount * SQUOZE_JUMP_STRIDE; + break; + case SQUOZE_INC_OFFSET_B: + dec->jumped_amount = 1; + dec->jump_mode = in; + dec->offset += dec->jumped_amount * SQUOZE_JUMP_STRIDE; + break; + default: + dec->append_unichar (dec->offset + (in - 1), dec->write_data); + dec->jumped_amount = 0; + dec->jump_mode = 0; + } } - col = pix[1]; - encoded_length = 2; - if (ret_col2) - *ret_col2 = col; } - if (length_px) - *length_px = len; - if (ret_col1) - *ret_col1 = col; - return encoded_length; -} - - -static inline int decode_pix_len (const uint8_t *pix, int *length_px) -{ - return decode_pix (pix, NULL, NULL, length_px, NULL); } - -static inline uint32_t color_diff (uint32_t a, uint32_t b) +static void squoze_decode_utf5_bytes (int is_utf5, + const unsigned char *input, int inlen, + char *output, int *r_outlen) { - uint32_t sum_dist = 0; - for (int c = 0; c < 4; c++) - { - sum_dist += ((a&0xff)-(b&0xff))*((a&0xff)-(b&0xff)); - a>>=8; - b>>=8; - } - return sum_dist; + SquozeUtf5DecDefaultData append_data = {(unsigned char*)output, 0}; + SquozeUtf5Dec dec = {is_utf5, + 97,//squoze_new_offset('a'), + &append_data, + 0, + squoze_decode_utf5_append_unichar_as_utf8, + 0, 0 + }; + for (int i = 0; i < inlen; i++) + squoze_decode_utf5 (&dec, input[i]); + if (dec.current) + dec.append_unichar (dec.current, dec.write_data); + if (r_outlen) + *r_outlen = append_data.length; } +#endif -static inline int is_b_good_middle (uint32_t a, uint32_t b, uint32_t c, int color_budget) -{ - uint32_t lerped = ctx_lerp_RGBA8 (a, c, 127); +#endif - if (a == c) return 0; // gradients are more expensive than not - uint32_t sum_dist = color_diff (lerped, b); - return (sum_dist < (GRADIENT_THRESHOLD*GRADIENT_THRESHOLD*3)); -} +#if CTX_IMPLEMENTATION || CTX_SIMD_BUILD -static inline int -ctx_CBRLE_recompress (uint8_t *cbrle, int size, int width, int pos, int level); - -#define GRAYCOL(v) ((v) + (v) * 256 + (v) * 256 * 256 + (unsigned)255*256*256*256) -static const uint32_t hard_pal[8]={ - GRAYCOL(0*255/7), - GRAYCOL(1*255/7), - GRAYCOL(2*255/7), - GRAYCOL(3*255/7), - GRAYCOL(4*255/7), - GRAYCOL(5*255/7), - GRAYCOL(6*255/7), - GRAYCOL(7*255/7) -}; -#include <math.h> -#define pow2(a) ((a)*(a)) -#define ctx_sqrtf sqrtf +#ifndef MINIZ_EXPORT +#define MINIZ_EXPORT +#endif +/* miniz.c 3.0.0 - public domain deflate/inflate, zlib-subset, ZIP reading/writing/appending, PNG writing + See "unlicense" statement at the end of this file. + Rich Geldreich <richgel99@gmail.com>, last updated Oct. 13, 2013 + Implements RFC 1950: http://www.ietf.org/rfc/rfc1950.txt and RFC 1951: http://www.ietf.org/rfc/rfc1951.txt + Most API's defined in miniz.c are optional. For example, to disable the archive related functions just define + MINIZ_NO_ARCHIVE_APIS, or to get rid of all stdio usage define MINIZ_NO_STDIO (see the list below for more macros). -static inline int -ctx_CBRLE_get_color_mask (int gen) -{ - switch (gen) - { - case 0: - case 1: - default: - return 0xffffffff; - case 2: - return 0xfffefefe; - case 3: - return 0xfffcfcfc; - case 4: - return 0xfff8f8f8; - case 5: - return 0xfff0f0f0; - case 6: - return 0xffe0e0e0; - } - return 0xffe0e0e0; -} + * Low-level Deflate/Inflate implementation notes: + Compression: Use the "tdefl" API's. The compressor supports raw, static, and dynamic blocks, lazy or + greedy parsing, match length filtering, RLE-only, and Huffman-only streams. It performs and compresses + approximately as well as zlib. -static inline int -ctx_CBRLE_get_color_idx (uint8_t *cbrle, int size, int color_budget, uint32_t color, int gen) -{ - int found = 0; - int colors = cbrle[2]; - int idx = 0; - uint32_t threshold = - (uint32_t) - ctx_lerpf (COLOR_THRESHOLD_START*COLOR_THRESHOLD_START*3, - COLOR_THRESHOLD_END*COLOR_THRESHOLD_END*3, - //((colors / ( color_budget-1.0f))) ); - ctx_sqrtf((colors / ( color_budget-1.0f))) ); + Decompression: Use the "tinfl" API's. The entire decompressor is implemented as a single function + coroutine: see tinfl_decompress(). It supports decompression into a 32KB (or larger power of 2) wrapping buffer, or into a memory + block large enough to hold the entire file. -#if 0 // reduce color-depth of grays - if (//gen > 0 && - (color&0xf0) == ((color >> 8) & 0xf0) && - (color&0xf0) == ((color >> 16) & 0xf0)) - { - color &= 0xfff0f0f0u; - } -#endif + The low-level tdefl/tinfl API's do not make any use of dynamic memory allocation. - color = color & ctx_CBRLE_get_color_mask (gen+2); + * zlib-style API notes: - uint32_t best_diff = 255*255*3; + miniz.c implements a fairly large subset of zlib. There's enough functionality present for it to be a drop-in + zlib replacement in many apps: + The z_stream struct, optional memory allocation callbacks + deflateInit/deflateInit2/deflate/deflateReset/deflateEnd/deflateBound + inflateInit/inflateInit2/inflate/inflateReset/inflateEnd + compress, compress2, compressBound, uncompress + CRC-32, Adler-32 - Using modern, minimal code size, CPU cache friendly routines. + Supports raw deflate streams or standard zlib streams with adler-32 checking. + Limitations: + The callback API's are not implemented yet. No support for gzip headers or zlib static dictionaries. + I've tried to closely emulate zlib's various flavors of stream flushing and return status codes, but + there are no guarantees that miniz.c pulls this off perfectly. -#if PAL_GRAY_PREDEF // predefined 8 grays - if (!found) - { - uint32_t diff; - int best = -1; - for (;idx < 8; idx++) - if ((diff = color_diff (hard_pal[idx], color)) < best_diff) - { - best_diff = diff; - best = idx; - } - idx = best; - if (best_diff < threshold) - { - found = 1; - } - } -#endif + * PNG writing: See the tdefl_write_image_to_png_file_in_memory() function, originally written by + Alex Evans. Supports 1-4 bytes/pixel images. - if (!found) - { - uint32_t diff; - int best = -1; - for (;idx < colors; idx++) - if ((diff = color_diff (((uint32_t*)(&cbrle[size-4-(idx)*4]))[0], color)) < best_diff) - { - best_diff = diff; - best = idx; - } - if (best !=-1) idx = best + PAL_GRAY_OFFSET; + * ZIP archive API notes: - /* the color diff threshold is dynamic, as - * palette space gets tighter we are less eager to add*/ - if (best_diff > threshold && colors < color_budget-1) // didn't find - store new color - { - idx = colors++; - ((uint32_t*)(&cbrle[size-4-idx*4]))[0] = color; - cbrle[2] = colors; - idx += PAL_GRAY_OFFSET; - } - } + The ZIP archive API's where designed with simplicity and efficiency in mind, with just enough abstraction to + get the job done with minimal fuss. There are simple API's to retrieve file information, read files from + existing archives, create new archives, append new files to existing archives, or clone archive data from + one archive to another. It supports archives located in memory or the heap, on disk (using stdio.h), + or you can specify custom file read/write callbacks. - return idx; -} + - Archive reading: Just call this function to read a single file from a disk archive: -static inline uint32_t -ctx_over_RGBA8 (uint32_t dst, uint32_t src, uint32_t cov); + void *mz_zip_extract_archive_file_to_heap(const char *pZip_filename, const char *pArchive_name, + size_t *pSize, mz_uint zip_flags); -static inline uint32_t -ctx_over_RGBA8_2 (uint32_t dst, uint32_t si_ga, uint32_t si_rb, uint32_t si_a, uint32_t cov); + For more complex cases, use the "mz_zip_reader" functions. Upon opening an archive, the entire central + directory is located and read as-is into memory, and subsequent file access only occurs when reading individual files. + - Archives file scanning: The simple way is to use this function to scan a loaded archive for a specific file: -static inline uint32_t -ctx_CBRLE_idx_to_color (const uint8_t *cbrle, int size, int idx) -{ -#if PAL_GRAY_PREDEF - if (idx < 8) -#if 0 - return (idx * 17 + - idx * 17 * 256 + - idx * 17 * 256 * 256 + - (unsigned)255*256*256*256); -#else - return hard_pal[idx]; -#endif - else -#endif - return ((uint32_t*)(&cbrle[size-4-(idx-PAL_GRAY_OFFSET)*4]))[0]; -} + int mz_zip_reader_locate_file(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags); -static inline int -ctx_CBRLE_compute_color_budget (int width, int size) -{ - int color_budget = 256; - //return color_budget; - switch ((size*8/width)){ - case 1: - case 2: color_budget = 8;break; - case 3: color_budget = 8;break; - case 4: color_budget = 16;break; - case 5: color_budget = 32;break; - case 6: color_budget = 64;break; - case 7: color_budget = 128;break; - case 8: color_budget = 136;break; - case 9: - case 10: color_budget = 196;break; - case 11: - case 12: - case 13: - case 14: - case 15: - case 16: color_budget = 256;break; - break; - case 17:case 18:case 19:case 20:case 21:case 22:case 23: - case 24: - case 32: color_budget = 256;break; - break; - default: - color_budget = ctx_mini(256,size/2/4); - break; - } - color_budget = ctx_maxi(color_budget,ctx_mini(256,size/5/4)); - return color_budget; -} + The locate operation can optionally check file comments too, which (as one example) can be used to identify + multiple versions of the same file in an archive. This function uses a simple linear search through the central + directory, so it's not very fast. -static inline void -ctx_CBRLE_compress (const uint8_t *rgba_in, - uint8_t *cbrle, - int width, - int size, - int skip, - int count, - int mode, - uint8_t *coverage, - int allow_recompress) -{ - const uint8_t *rgba = rgba_in; - int pos; + Alternately, you can iterate through all the files in an archive (using mz_zip_reader_get_num_files()) and + retrieve detailed info on each file by calling mz_zip_reader_file_stat(). - uint32_t prev_val; - int recompress = 0; + - Archive creation: Use the "mz_zip_writer" functions. The ZIP writer immediately writes compressed file data + to disk and builds an exact image of the central directory in memory. The central directory image is written + all at once at the end of the archive file when the archive is finalized. - int repeats; - int colors = 0; + The archive writer can optionally align each file's local header and file data to any power of 2 alignment, + which can be useful when the archive will be read from optical media. Also, the writer supports placing + arbitrary data blobs at the very beginning of ZIP archives. Archives written using either feature are still + readable by any ZIP tool. - uint32_t src_rgba= ((uint32_t*)rgba_in)[0]; - int color_budget = ctx_CBRLE_compute_color_budget (width, size); + - Archive appending: The simple way to add a single file to an archive is to call this function: -#if CBRLE_GRADIENT - int in_gradient = 0; -#endif + mz_bool mz_zip_add_mem_to_archive_file_in_place(const char *pZip_filename, const char *pArchive_name, + const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags); - if (mode == 0) - { -#if 0 - for (int round = 0; round < 1; round++) - for (int i = 1; i < width-1; i++) - { - int g0 = rgba[(i-1)*4+1]; - int g1 = rgba[(i)*4+1]; - int g2 = rgba[(i+1)*4+1]; + The archive will be created if it doesn't already exist, otherwise it'll be appended to. + Note the appending is done in-place and is not an atomic operation, so if something goes wrong + during the operation it's possible the archive could be left without a central directory (although the local + file headers and file data will be fine, so the archive will be recoverable). - if (abs(g0-g1) < abs(g1-g2)) - { - for (int c = 0; c < 4; c++) - { - rgba[i*4+c]=(rgba[(i-1)*4+c]+rgba[i*4+c])/2; - } - } - else if (abs(g0-g1) == abs(g1-g2)) - { - } - else - { - for (int c = 0; c < 4; c++) - { - rgba[i*4+c]=(rgba[(i+1)*4+c]+rgba[(i)*4+c])/2; - } - } - } -#endif - } + For more complex archive modification scenarios: + 1. The safest way is to use a mz_zip_reader to read the existing archive, cloning only those bits you want to + preserve into a new archive using using the mz_zip_writer_add_from_zip_reader() function (which compiles the + compressed file data as-is). When you're done, delete the old archive and rename the newly written archive, and + you're done. This is safe but requires a bunch of temporary disk space or heap memory. - uint8_t trailer[size+2]; - int trailer_size = 0; + 2. Or, you can convert an mz_zip_reader in-place to an mz_zip_writer using mz_zip_writer_init_from_reader(), + append new files as needed, then finalize the archive which will write an updated central directory to the + original archive. (This is basically what mz_zip_add_mem_to_archive_file_in_place() does.) There's a + possibility that the archive's central directory could be lost with this method if anything goes wrong, though. - uint8_t copy[(mode>=1&&mode<=2)?count*2+2:1]; - int copy_size = 0; + - ZIP archive support limitations: + No spanning support. Extraction functions can only handle unencrypted, stored or deflated files. + Requires streams capable of seeking. -#if 1 - if (cbrle[0] == 0 && - cbrle[1] == 0) - { - pos = 4; - for (int i =0; i < width/63;i++) - { - pos = encode_pix (cbrle, pos, 0, 63, 0, 0); - } - ((uint16_t*)cbrle)[0]=pos; - cbrle[2] = 0; - cbrle[3] = 16; - } -#endif + * This is a header file library, like stb_image.c. To get only a header file, either cut and paste the + below header, or create miniz.h, #define MINIZ_HEADER_FILE_ONLY, and then include miniz.c from it. - rgba = rgba_in; + * Important: For best perf. be sure to customize the below macros for your target platform: + #define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 1 + #define MINIZ_LITTLE_ENDIAN 1 + #define MINIZ_HAS_64BIT_REGISTERS 1 - pos = 4; - prev_val = 0xc0ffee; - repeats = 0; - - int trailer_start = 0; + * On platforms using glibc, Be sure to "#define _LARGEFILE64_SOURCE 1" before including miniz.c to ensure miniz + uses the 64-bit variants: fopen64(), stat64(), etc. Otherwise you won't be able to process large files + (i.e. 32-bit stat() fails for me on files > 0x7FFFFFFF bytes). +*/ +#pragma once - int x = 0; - if (skip || width != count) - { - int original_length = ((uint16_t*)cbrle)[0]; - colors = cbrle[2]; - pos = 4; - for (x = 0; x < skip + count;) - { - int repeat = 0; - int codelen = decode_pix_len (&cbrle[pos], &repeat); - if (x + repeat < skip + count && pos < original_length) - { - pos += codelen; - x += repeat; - } - else - { - uint8_t idx; - uint8_t idx2; - int gradient; - decode_pix (&cbrle[pos], &idx, &idx2, &repeat, &gradient); - - trailer_start = pos + codelen; - if (x + repeat == skip + count) - { - trailer_size = original_length - trailer_start; - if (trailer_size > 0) - { - memcpy (&trailer[0], &cbrle[trailer_start], trailer_size); - } - else - { - trailer_size = 0; - } - } - else - { - repeat -= (skip + count - x); +/* Defines to completely disable specific portions of miniz.c: + If all macros here are defined the only functionality remaining will be CRC-32 and adler-32. */ - if (repeat > 0) - { - trailer[0] = repeat + 128 + gradient * 64; - trailer[1] = idx; - } - trailer_size = original_length - trailer_start; - if (trailer_size > 0) - { - memcpy (&trailer[2], &cbrle[pos + codelen], trailer_size); - if (repeat>0)trailer_size += 2; - } - else - { - trailer_size = 0; - } - } - break; - } - } +/* Define MINIZ_NO_STDIO to disable all usage and any functions which rely on stdio for file I/O. */ +/*#define MINIZ_NO_STDIO */ - pos = 4; - rgba = rgba_in; +/* If MINIZ_NO_TIME is specified then the ZIP archive functions will not be able to get the current time, or */ +/* get/set file times, and the C run-time funcs that get/set times won't be called. */ +/* The current downside is the times written to your archives will be from 1979. */ +/*#define MINIZ_NO_TIME */ - for (x = 0; x < skip && pos < original_length;) - { - int repeat = 0; - uint8_t idx; - uint8_t idx2; - int dec_repeat; - int gradient; - //int len = decode_pix_len (&cbrle[pos], &repeat); - int len = decode_pix (&cbrle[pos], &idx, &idx2, &dec_repeat, &gradient); - if (x + dec_repeat < skip) - { - pos += len; - x += dec_repeat; - rgba += 4 * dec_repeat; - } - else - { - repeat = skip - x; +/* Define MINIZ_NO_DEFLATE_APIS to disable all compression API's. */ +/*#define MINIZ_NO_DEFLATE_APIS */ - if (repeat>0) - { - x += repeat; +/* Define MINIZ_NO_INFLATE_APIS to disable all decompression API's. */ +/*#define MINIZ_NO_INFLATE_APIS */ - if (mode==CBRLE_MODE_COPY || - mode==CBRLE_MODE_OVER) - { - int mpos = 0; - if (dec_repeat != repeat) - mpos = encode_pix (copy, 0, idx, dec_repeat-repeat, gradient, 0); - if (original_length) - { - if (trailer_start == 0) trailer_start = original_length; - copy_size = trailer_start - (pos + len); - if (copy_size <0) - fprintf (stderr, "%i: uh? cs:%i %i | %i %i %i\n", __LINE__, - copy_size, (int)sizeof(copy), - trailer_start, pos, len); - if (copy_size<0)copy_size=0; - copy_size = ctx_mini(copy_size, sizeof(copy)); - if (copy_size) - { - memcpy (©[mpos], &cbrle[pos + len], copy_size); - } - copy_size += mpos; - } - else - { - copy_size = 0; - } - } - else - { - rgba += 4 * repeat; - } - gradient = 0; - pos += encode_pix (cbrle, pos, idx, repeat, gradient, 0); - } - else - { - if (mode != CBRLE_MODE_SET) - { - int mpos = 0; - copy_size = trailer_start - (pos + len); - fprintf (stderr, "csb:%i %i\n", copy_size, (int)sizeof(copy)); - memcpy (©[mpos], &cbrle[pos + len], copy_size); - } - } - - break; - } - } - } - else - { - colors = 0; - cbrle[0]=0; // length - cbrle[1]=0; // length - cbrle[2]=0; // pal size - cbrle[3]=0; // alpha - } +/* Define MINIZ_NO_ARCHIVE_APIS to disable all ZIP archive API's. */ +/*#define MINIZ_NO_ARCHIVE_APIS */ -#if CBRLE_GRADIENT - int prev_in_gradient = 0; -#endif +/* Define MINIZ_NO_ARCHIVE_WRITING_APIS to disable all writing related ZIP archive API's. */ +/*#define MINIZ_NO_ARCHIVE_WRITING_APIS */ - if (mode == CBRLE_MODE_OVER) - { - uint32_t si_ga = (src_rgba & 0xff00ff00) >> 8; - uint32_t si_rb = src_rgba & 0x00ff00ff; - uint32_t si_a = si_ga >> 16; +/* Define MINIZ_NO_ZLIB_APIS to remove all ZLIB-style compression/decompression API's. */ +/*#define MINIZ_NO_ZLIB_APIS */ - for (int mpos = 0; mpos < copy_size && x < width && count > 0; ) - { - uint8_t offsetA = 0; - uint8_t offset2 = 0; - int repeat = 0; - int gradient = 0; - mpos += decode_pix (©[mpos], &offsetA, &offset2, &repeat, &gradient); +/* Define MINIZ_NO_ZLIB_COMPATIBLE_NAME to disable zlib names, to prevent conflicts against stock zlib. */ +/*#define MINIZ_NO_ZLIB_COMPATIBLE_NAMES */ - uint32_t color = ctx_CBRLE_idx_to_color (cbrle, size, offsetA); +/* Define MINIZ_NO_MALLOC to disable all calls to malloc, free, and realloc. + Note if MINIZ_NO_MALLOC is defined then the user must always provide custom user alloc/free/realloc + callbacks to the zlib and archive API's, and a few stand-alone helper API's which don't provide custom user + functions (such as tdefl_compress_mem_to_heap() and tinfl_decompress_mem_to_heap()) won't work. */ +/*#define MINIZ_NO_MALLOC */ - repeat = ctx_mini (repeat, count); - if (repeat) - { - do { - int part = 0; - int cov = coverage[0]; - while (coverage[0]==cov && count >0 && repeat > 0 && x < width) - { - x ++; - coverage ++; - part ++; - count --; - repeat --; - } +#ifdef MINIZ_NO_INFLATE_APIS +#define MINIZ_NO_ARCHIVE_APIS +#endif - { - uint32_t composited = ctx_over_RGBA8_2 (color, si_ga, si_rb, si_a, cov); - int idx = ctx_CBRLE_get_color_idx (cbrle, size, - ctx_mini(color_budget, size-pos-colors*4 - 2), // XXX expensive? - composited, recompress); - colors = cbrle[2]; +#ifdef MINIZ_NO_DEFLATE_APIS +#define MINIZ_NO_ARCHIVE_WRITING_APIS +#endif - pos += encode_pix (cbrle, pos, idx, part, gradient, 1); - } - } while (repeat > 0); - } - } - } - else if (mode == CBRLE_MODE_COPY) - { - uint32_t si_ga = (src_rgba & 0xff00ff00) >> 8; - uint32_t si_rb = src_rgba & 0x00ff00ff; - for (int mpos = 0; mpos < copy_size && x < width && count > 0; ) - { - uint8_t offsetA = 0; - uint8_t offset2 = 0; - int repeat = 0; - int gradient = 0; - mpos += decode_pix (©[mpos], &offsetA, &offset2, &repeat, &gradient); +#if defined(__TINYC__) && (defined(__linux) || defined(__linux__)) +/* TODO: Work around "error: include file 'sys\utime.h' when compiling with tcc on Linux */ +#define MINIZ_NO_TIME +#endif - uint32_t color = ctx_CBRLE_idx_to_color (cbrle, size, offsetA); +#include <stddef.h> - repeat = ctx_mini (repeat, count); - if (repeat) - { - colors = cbrle[2]; - do { - int part = 0; - int cov = coverage[0]; - while (coverage[0]==cov && count >0 && repeat > 0 && x < width) - { - x++; - coverage++; - part++; - count --; - repeat--; - } +#if !defined(MINIZ_NO_TIME) && !defined(MINIZ_NO_ARCHIVE_APIS) +#include <time.h> +#endif - { - uint32_t composited; - composited = ctx_lerp_RGBA8_2 (color, si_ga, si_rb, cov); - int idx = ctx_CBRLE_get_color_idx (cbrle, size, ctx_mini(color_budget, size-pos-colors*4 - 8), composited, recompress); - pos += encode_pix (cbrle, pos, idx, part, gradient, 1); - } - } while (repeat > 0); - } - } - } - else - if (mode == CBRLE_MODE_SET_COLOR) - { - int idx = ctx_CBRLE_get_color_idx (cbrle, size, ctx_mini(color_budget, size-pos-colors*4 ), src_rgba, recompress); - while (count > 0) - { - int repeat = ctx_mini (count, 63); - pos += encode_pix (cbrle, pos, idx, repeat, 0, 1); - count -= repeat; - x += repeat; - } - } - else - { +#if defined(_M_IX86) || defined(_M_X64) || defined(__i386__) || defined(__i386) || defined(__i486__) || defined(__i486) || defined(i386) || defined(__ia64__) || defined(__x86_64__) +/* MINIZ_X86_OR_X64_CPU is only used to help set the below macros. */ +#define MINIZ_X86_OR_X64_CPU 1 +#else +#define MINIZ_X86_OR_X64_CPU 0 +#endif - for (; x < width && count--; x++) - { - uint32_t val = ((uint32_t*)rgba)[0] & 0xffffffff; - // NOTE: we could probably drop precision to known lower precision reached here, - // but it requires more global state -#if CBRLE_GRADIENT - uint32_t next_val = 0x23ff00ff; +/* Set MINIZ_LITTLE_ENDIAN only if not set */ +#if !defined(MINIZ_LITTLE_ENDIAN) +#if defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) - if (x + 1 < width && count>1) - next_val = ((uint32_t*)rgba)[1] & 0xffffffffu; +#if (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) +/* Set MINIZ_LITTLE_ENDIAN to 1 if the processor is little endian. */ +#define MINIZ_LITTLE_ENDIAN 1 +#else +#define MINIZ_LITTLE_ENDIAN 0 #endif - if (pos > size - BORDER - trailer_size - colors * 4) - { - ((uint16_t*)cbrle)[0]=pos; - - if (allow_recompress) - do { - pos = ctx_CBRLE_recompress (cbrle, size, width, pos, recompress); - colors = cbrle[2]; - recompress ++; - } - while (pos > size - BORDER - trailer_size - colors * 4 +#else - - && recompress < MAX_RECOMPRESS); - if (recompress >3) color_budget = 0; - if (pos > size - BORDER - trailer_size - colors * 4) - goto done; - } +#if MINIZ_X86_OR_X64_CPU +#define MINIZ_LITTLE_ENDIAN 1 +#else +#define MINIZ_LITTLE_ENDIAN 0 +#endif - if (val != prev_val || repeats>=63) - { - if (repeats) - { +#endif +#endif -#if CBRLE_GRADIENT - if ((( repeats == 1 && in_gradient == 0) || - ( repeats < MAX_GRADIENT_LENGTH && in_gradient == 1)) +/* Using unaligned loads and stores causes errors when using UBSan */ +#if defined(__has_feature) +#if __has_feature(undefined_behavior_sanitizer) +#define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 0 +#endif +#endif - && is_b_good_middle (prev_val, val, next_val, color_budget) - ) - { - in_gradient = 1; - } - else +/* Set MINIZ_USE_UNALIGNED_LOADS_AND_STORES only if not set */ +#if !defined(MINIZ_USE_UNALIGNED_LOADS_AND_STORES) +#if MINIZ_X86_OR_X64_CPU +/* Set MINIZ_USE_UNALIGNED_LOADS_AND_STORES to 1 on CPU's that permit efficient integer loads and stores from unaligned addresses. */ +#define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 0 +#define MINIZ_UNALIGNED_USE_MEMCPY +#else +#define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 0 +#endif #endif - if (repeats) // got incoming pixels - // of color to store - { - int idx = ctx_CBRLE_get_color_idx (cbrle, size, ctx_mini(color_budget, size-pos-colors*4-BORDER ), prev_val, recompress); - colors = cbrle[2]; - pos += encode_pix (cbrle, pos, idx, repeats, - -#if CBRLE_GRADIENT - ((prev_in_gradient==1) && (in_gradient == 1)) +#if defined(_M_X64) || defined(_WIN64) || defined(__MINGW64__) || defined(_LP64) || defined(__LP64__) || defined(__ia64__) || defined(__x86_64__) +/* Set MINIZ_HAS_64BIT_REGISTERS to 1 if operations on 64-bit integers are reasonably fast (and don't involve compiler generated calls to helper functions). */ +#define MINIZ_HAS_64BIT_REGISTERS 1 #else - 0 +#define MINIZ_HAS_64BIT_REGISTERS 0 #endif - - - , 1); -#if CBRLE_GRADIENT - prev_in_gradient = in_gradient; - in_gradient = 0; +#ifdef __cplusplus +extern "C" { #endif - repeats = 0; - } - } - } - repeats++; - prev_val = val; - if (!mode) - rgba +=4; - } - if (repeats && pos < size - colors * 4 - BORDER) - { - int idx = ctx_CBRLE_get_color_idx (cbrle, size, ctx_mini(color_budget, size-pos-colors*4-BORDER ), prev_val, recompress); - colors = cbrle[2]; - - if (repeats && pos + 4 < size - colors * 4 - BORDER) - { - pos += encode_pix (cbrle, pos, idx, repeats, 0, 1); - repeats = 0; - } - } - } +/* ------------------- zlib-style API Definitions. */ - if (trailer_size) - { - for (int i = 0; i < trailer_size;i++) - { +/* For more compatibility with zlib, miniz.c uses unsigned long for some parameters/struct members. Beware: mz_ulong can be either 32 or 64-bits! */ +typedef unsigned long mz_ulong; - if (pos > size - trailer_size - BORDER - colors * 4) - { - ((uint16_t*)cbrle)[0]=pos; - cbrle[2] = colors; +/* mz_free() internally uses the MZ_FREE() macro (which by default calls free() unless you've modified the MZ_MALLOC macro) to release a block allocated from the heap. */ +MINIZ_EXPORT void mz_free(void *p); - if (allow_recompress) - do { - pos = ctx_CBRLE_recompress (cbrle, size, width, pos, recompress); - colors = cbrle[2]; - recompress ++; - } - while (pos > size - trailer_size - BORDER - colors * 4 && recompress < MAX_RECOMPRESS); - if (pos > size - trailer_size - BORDER - colors * 4) - goto done; - } +#define MZ_ADLER32_INIT (1) +/* mz_adler32() returns the initial adler-32 value to use when called with ptr==NULL. */ +MINIZ_EXPORT mz_ulong mz_adler32(mz_ulong adler, const unsigned char *ptr, size_t buf_len); - cbrle[pos++] = trailer[i]; - } - } -done: - ((uint16_t*)cbrle)[0]=pos; - cbrle[2] = colors; - //cbrle[3] = 16; -} +#define MZ_CRC32_INIT (0) +/* mz_crc32() returns the initial CRC-32 value to use when called with ptr==NULL. */ +MINIZ_EXPORT mz_ulong mz_crc32(mz_ulong crc, const unsigned char *ptr, size_t buf_len); -static inline void -_ctx_CBRLE_decompress (const uint8_t *cbrle, uint8_t *rgba8, int width, int size, int skip, int count) +/* Compression strategies. */ +enum { - int x = 0; - int pos = 4; - uint32_t pixA = 0; -#if CBRLE_GRADIENT - uint32_t prev_pix = 0; -#endif - const uint8_t *codepix=cbrle+4; + MZ_DEFAULT_STRATEGY = 0, + MZ_FILTERED = 1, + MZ_HUFFMAN_ONLY = 2, + MZ_RLE = 3, + MZ_FIXED = 4 +}; - int length = ((uint16_t*)cbrle)[0]; - //int colors = cbrle[2]; +/* Method */ +#define MZ_DEFLATED 8 - for (x = 0; x < skip;) - { - int repeat = 0; - if (pos < length) - { - int len = decode_pix_len (&cbrle[pos], &repeat); - if (x + repeat < skip) - { - pos += len; - codepix += len; - x+=repeat; - } - else - break; - } - else - x++; - } - //x=skip; +/* Heap allocation callbacks. +Note that mz_alloc_func parameter types purposely differ from zlib's: items/size is size_t, not unsigned long. */ +typedef void *(*mz_alloc_func)(void *opaque, size_t items, size_t size); +typedef void (*mz_free_func)(void *opaque, void *address); +typedef void *(*mz_realloc_func)(void *opaque, void *address, size_t items, size_t size); - for (; pos < length && x < width; ) - { - uint8_t offsetA = 0; - uint8_t offset2 = 0; - int repeat = 0; - int gradient = 0; - int codelen = decode_pix (codepix, &offsetA, &offset2, &repeat, &gradient); - //fprintf (stderr, "{%i r%i%s}", offsetA, repeat, gradient?"g":""); +/* Compression levels: 0-9 are the standard zlib-style levels, 10 is best possible compression (not zlib compatible, and may be very slow), MZ_DEFAULT_COMPRESSION=MZ_DEFAULT_LEVEL. */ +enum +{ + MZ_NO_COMPRESSION = 0, + MZ_BEST_SPEED = 1, + MZ_BEST_COMPRESSION = 9, + MZ_UBER_COMPRESSION = 10, + MZ_DEFAULT_LEVEL = 6, + MZ_DEFAULT_COMPRESSION = -1 +}; - pixA = ctx_CBRLE_idx_to_color (cbrle, size, offsetA); +#define MZ_VERSION "11.0.0" +#define MZ_VERNUM 0xB000 +#define MZ_VER_MAJOR 11 +#define MZ_VER_MINOR 0 +#define MZ_VER_REVISION 0 +#define MZ_VER_SUBREVISION 0 -#if CBRLE_GRADIENT - if (gradient) - { - for (int i = 0; i < repeat && x < width && count--; i++) - { - int has_head = 1; - int has_tail = 0; - float dt = (i+has_head+0.0f) / (repeat+ has_head + has_tail-1.0f); - ((uint32_t*)(&rgba8[(x++)*4]))[0] = ctx_lerp_RGBA8 (prev_pix, pixA, (uint8_t)(dt*255.0f)); - //((uint32_t*)(&rgba8[(x++)*4]))[0] = (int)(dt * 255) | 0xff000000; - } - } - else -#endif - { - if (offsetA != offset2 && repeat == 2) - { - uint32_t pixB = ctx_CBRLE_idx_to_color (cbrle, size, offset2); - ((uint32_t*)(&rgba8[(x++)*4]))[0] = pixA; - ((uint32_t*)(&rgba8[(x++)*4]))[0] = pixB; - } - else - { - for (int i = 0; i < repeat && x < width && count--; i++) - ((uint32_t*)(&rgba8[(x++)*4]))[0] = pixA; - } - } -#if CBRLE_GRADIENT - prev_pix = pixA; -#endif +#ifndef MINIZ_NO_ZLIB_APIS - codepix += codelen; - pos += codelen; - } - //fprintf (stderr, "\n"); +/* Flush values. For typical usage you only need MZ_NO_FLUSH and MZ_FINISH. The other values are for advanced use (refer to the zlib docs). */ +enum +{ + MZ_NO_FLUSH = 0, + MZ_PARTIAL_FLUSH = 1, + MZ_SYNC_FLUSH = 2, + MZ_FULL_FLUSH = 3, + MZ_FINISH = 4, + MZ_BLOCK = 5 +}; + +/* Return status codes. MZ_PARAM_ERROR is non-standard. */ +enum +{ + MZ_OK = 0, + MZ_STREAM_END = 1, + MZ_NEED_DICT = 2, + MZ_ERRNO = -1, + MZ_STREAM_ERROR = -2, + MZ_DATA_ERROR = -3, + MZ_MEM_ERROR = -4, + MZ_BUF_ERROR = -5, + MZ_VERSION_ERROR = -6, + MZ_PARAM_ERROR = -10000 +}; + +/* Window bits */ +#define MZ_DEFAULT_WINDOW_BITS 15 + +struct mz_internal_state; + +/* Compression/decompression stream struct. */ +typedef struct mz_stream_s +{ + const unsigned char *next_in; /* pointer to next byte to read */ + unsigned int avail_in; /* number of bytes available at next_in */ + mz_ulong total_in; /* total number of bytes consumed so far */ + + unsigned char *next_out; /* pointer to next byte to write */ + unsigned int avail_out; /* number of bytes that can be written to next_out */ + mz_ulong total_out; /* total number of bytes produced so far */ + + char *msg; /* error msg (unused) */ + struct mz_internal_state *state; /* internal state, allocated by zalloc/zfree */ + + mz_alloc_func zalloc; /* optional heap allocation function (defaults to malloc) */ + mz_free_func zfree; /* optional heap free function (defaults to free) */ + void *opaque; /* heap alloc function user pointer */ + + int data_type; /* data_type (unused) */ + mz_ulong adler; /* adler32 of the source or uncompressed data */ + mz_ulong reserved; /* not used */ +} mz_stream; + +typedef mz_stream *mz_streamp; + +/* Returns the version string of miniz.c. */ +MINIZ_EXPORT const char *mz_version(void); + +#ifndef MINIZ_NO_DEFLATE_APIS + +/* mz_deflateInit() initializes a compressor with default options: */ +/* Parameters: */ +/* pStream must point to an initialized mz_stream struct. */ +/* level must be between [MZ_NO_COMPRESSION, MZ_BEST_COMPRESSION]. */ +/* level 1 enables a specially optimized compression function that's been optimized purely for performance, not ratio. */ +/* (This special func. is currently only enabled when MINIZ_USE_UNALIGNED_LOADS_AND_STORES and MINIZ_LITTLE_ENDIAN are defined.) */ +/* Return values: */ +/* MZ_OK on success. */ +/* MZ_STREAM_ERROR if the stream is bogus. */ +/* MZ_PARAM_ERROR if the input parameters are bogus. */ +/* MZ_MEM_ERROR on out of memory. */ +MINIZ_EXPORT int mz_deflateInit(mz_streamp pStream, int level); + +/* mz_deflateInit2() is like mz_deflate(), except with more control: */ +/* Additional parameters: */ +/* method must be MZ_DEFLATED */ +/* window_bits must be MZ_DEFAULT_WINDOW_BITS (to wrap the deflate stream with zlib header/adler-32 footer) or -MZ_DEFAULT_WINDOW_BITS (raw deflate/no header or footer) */ +/* mem_level must be between [1, 9] (it's checked but ignored by miniz.c) */ +MINIZ_EXPORT int mz_deflateInit2(mz_streamp pStream, int level, int method, int window_bits, int mem_level, int strategy); + +/* Quickly resets a compressor without having to reallocate anything. Same as calling mz_deflateEnd() followed by mz_deflateInit()/mz_deflateInit2(). */ +MINIZ_EXPORT int mz_deflateReset(mz_streamp pStream); + +/* mz_deflate() compresses the input to output, consuming as much of the input and producing as much output as possible. */ +/* Parameters: */ +/* pStream is the stream to read from and write to. You must initialize/update the next_in, avail_in, next_out, and avail_out members. */ +/* flush may be MZ_NO_FLUSH, MZ_PARTIAL_FLUSH/MZ_SYNC_FLUSH, MZ_FULL_FLUSH, or MZ_FINISH. */ +/* Return values: */ +/* MZ_OK on success (when flushing, or if more input is needed but not available, and/or there's more output to be written but the output buffer is full). */ +/* MZ_STREAM_END if all input has been consumed and all output bytes have been written. Don't call mz_deflate() on the stream anymore. */ +/* MZ_STREAM_ERROR if the stream is bogus. */ +/* MZ_PARAM_ERROR if one of the parameters is invalid. */ +/* MZ_BUF_ERROR if no forward progress is possible because the input and/or output buffers are empty. (Fill up the input buffer or free up some output space and try again.) */ +MINIZ_EXPORT int mz_deflate(mz_streamp pStream, int flush); + +/* mz_deflateEnd() deinitializes a compressor: */ +/* Return values: */ +/* MZ_OK on success. */ +/* MZ_STREAM_ERROR if the stream is bogus. */ +MINIZ_EXPORT int mz_deflateEnd(mz_streamp pStream); + +/* mz_deflateBound() returns a (very) conservative upper bound on the amount of data that could be generated by deflate(), assuming flush is set to only MZ_NO_FLUSH or MZ_FINISH. */ +MINIZ_EXPORT mz_ulong mz_deflateBound(mz_streamp pStream, mz_ulong source_len); + +/* Single-call compression functions mz_compress() and mz_compress2(): */ +/* Returns MZ_OK on success, or one of the error codes from mz_deflate() on failure. */ +MINIZ_EXPORT int mz_compress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len); +MINIZ_EXPORT int mz_compress2(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len, int level); + +/* mz_compressBound() returns a (very) conservative upper bound on the amount of data that could be generated by calling mz_compress(). */ +MINIZ_EXPORT mz_ulong mz_compressBound(mz_ulong source_len); + +#endif /*#ifndef MINIZ_NO_DEFLATE_APIS*/ + +#ifndef MINIZ_NO_INFLATE_APIS + +/* Initializes a decompressor. */ +MINIZ_EXPORT int mz_inflateInit(mz_streamp pStream); + +/* mz_inflateInit2() is like mz_inflateInit() with an additional option that controls the window size and whether or not the stream has been wrapped with a zlib header/footer: */ +/* window_bits must be MZ_DEFAULT_WINDOW_BITS (to parse zlib header/footer) or -MZ_DEFAULT_WINDOW_BITS (raw deflate). */ +MINIZ_EXPORT int mz_inflateInit2(mz_streamp pStream, int window_bits); + +/* Quickly resets a compressor without having to reallocate anything. Same as calling mz_inflateEnd() followed by mz_inflateInit()/mz_inflateInit2(). */ +MINIZ_EXPORT int mz_inflateReset(mz_streamp pStream); + +/* Decompresses the input stream to the output, consuming only as much of the input as needed, and writing as much to the output as possible. */ +/* Parameters: */ +/* pStream is the stream to read from and write to. You must initialize/update the next_in, avail_in, next_out, and avail_out members. */ +/* flush may be MZ_NO_FLUSH, MZ_SYNC_FLUSH, or MZ_FINISH. */ +/* On the first call, if flush is MZ_FINISH it's assumed the input and output buffers are both sized large enough to decompress the entire stream in a single call (this is slightly faster). */ +/* MZ_FINISH implies that there are no more source bytes available beside what's already in the input buffer, and that the output buffer is large enough to hold the rest of the decompressed data. */ +/* Return values: */ +/* MZ_OK on success. Either more input is needed but not available, and/or there's more output to be written but the output buffer is full. */ +/* MZ_STREAM_END if all needed input has been consumed and all output bytes have been written. For zlib streams, the adler-32 of the decompressed data has also been verified. */ +/* MZ_STREAM_ERROR if the stream is bogus. */ +/* MZ_DATA_ERROR if the deflate stream is invalid. */ +/* MZ_PARAM_ERROR if one of the parameters is invalid. */ +/* MZ_BUF_ERROR if no forward progress is possible because the input buffer is empty but the inflater needs more input to continue, or if the output buffer is not large enough. Call mz_inflate() again */ +/* with more input data, or with more room in the output buffer (except when using single call decompression, described above). */ +MINIZ_EXPORT int mz_inflate(mz_streamp pStream, int flush); + +/* Deinitializes a decompressor. */ +MINIZ_EXPORT int mz_inflateEnd(mz_streamp pStream); + +/* Single-call decompression. */ +/* Returns MZ_OK on success, or one of the error codes from mz_inflate() on failure. */ +MINIZ_EXPORT int mz_uncompress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len); +MINIZ_EXPORT int mz_uncompress2(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong *pSource_len); +#endif /*#ifndef MINIZ_NO_INFLATE_APIS*/ + +/* Returns a string description of the specified error code, or NULL if the error code is invalid. */ +MINIZ_EXPORT const char *mz_error(int err); + +/* Redefine zlib-compatible names to miniz equivalents, so miniz.c can be used as a drop-in replacement for the subset of zlib that miniz.c supports. */ +/* Define MINIZ_NO_ZLIB_COMPATIBLE_NAMES to disable zlib-compatibility if you use zlib in the same project. */ +#ifndef MINIZ_NO_ZLIB_COMPATIBLE_NAMES +typedef unsigned char Byte; +typedef unsigned int uInt; +typedef mz_ulong uLong; +typedef Byte Bytef; +typedef uInt uIntf; +typedef char charf; +typedef int intf; +typedef void *voidpf; +typedef uLong uLongf; +typedef void *voidp; +typedef void *const voidpc; +#define Z_NULL 0 +#define Z_NO_FLUSH MZ_NO_FLUSH +#define Z_PARTIAL_FLUSH MZ_PARTIAL_FLUSH +#define Z_SYNC_FLUSH MZ_SYNC_FLUSH +#define Z_FULL_FLUSH MZ_FULL_FLUSH +#define Z_FINISH MZ_FINISH +#define Z_BLOCK MZ_BLOCK +#define Z_OK MZ_OK +#define Z_STREAM_END MZ_STREAM_END +#define Z_NEED_DICT MZ_NEED_DICT +#define Z_ERRNO MZ_ERRNO +#define Z_STREAM_ERROR MZ_STREAM_ERROR +#define Z_DATA_ERROR MZ_DATA_ERROR +#define Z_MEM_ERROR MZ_MEM_ERROR +#define Z_BUF_ERROR MZ_BUF_ERROR +#define Z_VERSION_ERROR MZ_VERSION_ERROR +#define Z_PARAM_ERROR MZ_PARAM_ERROR +#define Z_NO_COMPRESSION MZ_NO_COMPRESSION +#define Z_BEST_SPEED MZ_BEST_SPEED +#define Z_BEST_COMPRESSION MZ_BEST_COMPRESSION +#define Z_DEFAULT_COMPRESSION MZ_DEFAULT_COMPRESSION +#define Z_DEFAULT_STRATEGY MZ_DEFAULT_STRATEGY +#define Z_FILTERED MZ_FILTERED +#define Z_HUFFMAN_ONLY MZ_HUFFMAN_ONLY +#define Z_RLE MZ_RLE +#define Z_FIXED MZ_FIXED +#define Z_DEFLATED MZ_DEFLATED +#define Z_DEFAULT_WINDOW_BITS MZ_DEFAULT_WINDOW_BITS +#define alloc_func mz_alloc_func +#define free_func mz_free_func +#define internal_state mz_internal_state +#define z_stream mz_stream + +#ifndef MINIZ_NO_DEFLATE_APIS +#define deflateInit mz_deflateInit +#define deflateInit2 mz_deflateInit2 +#define deflateReset mz_deflateReset +#define deflate mz_deflate +#define deflateEnd mz_deflateEnd +#define deflateBound mz_deflateBound +#define compress mz_compress +#define compress2 mz_compress2 +#define compressBound mz_compressBound +#endif /*#ifndef MINIZ_NO_DEFLATE_APIS*/ + +#ifndef MINIZ_NO_INFLATE_APIS +#define inflateInit mz_inflateInit +#define inflateInit2 mz_inflateInit2 +#define inflateReset mz_inflateReset +#define inflate mz_inflate +#define inflateEnd mz_inflateEnd +#define uncompress mz_uncompress +#define uncompress2 mz_uncompress2 +#endif /*#ifndef MINIZ_NO_INFLATE_APIS*/ + +#define crc32 mz_crc32 +#define adler32 mz_adler32 +#define MAX_WBITS 15 +#define MAX_MEM_LEVEL 9 +#define zError mz_error +#define ZLIB_VERSION MZ_VERSION +#define ZLIB_VERNUM MZ_VERNUM +#define ZLIB_VER_MAJOR MZ_VER_MAJOR +#define ZLIB_VER_MINOR MZ_VER_MINOR +#define ZLIB_VER_REVISION MZ_VER_REVISION +#define ZLIB_VER_SUBREVISION MZ_VER_SUBREVISION +#define zlibVersion mz_version +#define zlib_version mz_version() +#endif /* #ifndef MINIZ_NO_ZLIB_COMPATIBLE_NAMES */ + +#endif /* MINIZ_NO_ZLIB_APIS */ - // replicate last value - while (x < width && count--) - ((uint32_t*)(&rgba8[(x++)*4]))[0] = prev_pix; +#ifdef __cplusplus } +#endif -static inline int -ctx_CBRLE_recompress_sort_pal (uint8_t *cbrle, int size, int width, int length, int no) -{ - uint8_t temp[width*4 + 32]; - int colors = cbrle[2]; - //fprintf (stderr, "{%i %i %i}", size, colors, no); - uint32_t mask = ctx_CBRLE_get_color_mask (no); - for (int i = size - colors * 4; i < size-4; i+=4) - { - uint32_t *pix = (uint32_t*)(&cbrle[i]); - pix[i] &= mask; - } - _ctx_CBRLE_decompress (cbrle, temp, width, size, 0, width); - memset(cbrle, 0, size); - ctx_CBRLE_compress (temp, cbrle, width, size, 0, width, CBRLE_MODE_SET, NULL, 0); - return ((uint16_t*)cbrle)[0]; -} -static inline int -ctx_CBRLE_recompress_drop_bits (uint8_t *cbrle, int size, int width, int length, int no) -{ - uint8_t temp[width*4 + 32]; - int colors = cbrle[2]; - //fprintf (stderr, "{%i %i %i}", size, colors, no); - uint32_t mask = ctx_CBRLE_get_color_mask (no); - for (int i = size - colors * 4; i < size-4; i+=4) - { - uint32_t *pix = (uint32_t*)(&cbrle[i]); - pix[i] &= mask; - } - _ctx_CBRLE_decompress (cbrle, temp, width, size, 0, width); - memset(cbrle, 0, size); - ctx_CBRLE_compress (temp, cbrle, width, size, 0, width, CBRLE_MODE_SET, NULL, 0); - return ((uint16_t*)cbrle)[0]; -} +#pragma once +#include <assert.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> -static inline int -ctx_CBRLE_recompress_merge_pairs (uint8_t *cbrle, int size, int width, int length) -{ - uint8_t temp[width*4]; - // drop horizontal resolution - _ctx_CBRLE_decompress (cbrle, temp, width, size, 0, width); - for (int i = 0; i < width-1; i++) - if ((rand()%100<MERGE_CHANCE) - && color_diff ( ((uint32_t*)(&temp[i*4]))[0], - ((uint32_t*)(&temp[(i+1)*4]))[0]) < MERGE_THRESHOLD*MERGE_THRESHOLD*3 - ) - for (int c = 0; c < 4; c++) - temp[i*4+c]=temp[(i+1)*4+c]; - memset(cbrle, 0, size); - ctx_CBRLE_compress (temp, cbrle, width, size, 0, width, CBRLE_MODE_SET, NULL, 0); - return ((uint16_t*)cbrle)[0]; -} +/* ------------------- Types and macros */ +typedef unsigned char mz_uint8; +typedef signed short mz_int16; +typedef unsigned short mz_uint16; +typedef unsigned int mz_uint32; +typedef unsigned int mz_uint; +typedef int64_t mz_int64; +typedef uint64_t mz_uint64; +typedef int mz_bool; -static inline int -ctx_CBRLE_recompress_smoothen (uint8_t *cbrle, int size, int width, int length) -{ - uint8_t temp[width*4]; - _ctx_CBRLE_decompress (cbrle, temp, width, size, 0, width); +#define MZ_FALSE (0) +#define MZ_TRUE (1) -#if 0 - for (int round = 0; round < 2; round++) - for (int i = 1; i < width-1; i++) - { - for (int c = 0; c < 4; c++) - { - temp[i*4+c]=(uint8_t)(temp[(i+1)*4+c]*0.10f+temp[i*4+c]*0.8f+temp[(i-1)*4+c]*0.10f); - } - } +/* Works around MSVC's spammy "warning C4127: conditional expression is constant" message. */ +#ifdef _MSC_VER +#define MZ_MACRO_END while (0, 0) +#else +#define MZ_MACRO_END while (0) #endif -#if 1 - for (int round = 0; round < 2; round++) - for (int i = 1; i < width-1; i++) - { - int g0 = temp[(i-1)*4+1]; - int g1 = temp[(i)*4+1]; - int g2 = temp[(i+1)*4+1]; +#ifdef MINIZ_NO_STDIO +#define MZ_FILE void * +#else +#include <stdio.h> +#define MZ_FILE FILE +#endif /* #ifdef MINIZ_NO_STDIO */ - if (abs(g0-g1) < abs(g1-g2)) - { - for (int c = 0; c < 4; c++) - { - temp[i*4+c]=(temp[(i-1)*4+c]+temp[i*4+c])/2; - } - } - else if (abs(g0-g1) == abs(g1-g2)) - { -#if 0 - for (int c = 0; c < 4; c++) - { - temp[i*4+c]=(temp[(i+1)*4+c]+temp[i*4+c]+temp[(i-1)*4+c])/3; - } -#endif - } - else - { - for (int c = 0; c < 4; c++) - { - temp[i*4+c]=(temp[(i+1)*4+c]+temp[(i)*4+c])/2; - } - } - } +#ifdef MINIZ_NO_TIME +typedef struct mz_dummy_time_t_tag +{ + mz_uint32 m_dummy1; + mz_uint32 m_dummy2; +} mz_dummy_time_t; +#define MZ_TIME_T mz_dummy_time_t #else +#define MZ_TIME_T time_t #endif - memset(cbrle, 0, size); - ctx_CBRLE_compress (temp, cbrle, width, size, 0, width, CBRLE_MODE_SET, NULL, 0); - - return ((uint16_t*)cbrle)[0]; -} +#define MZ_ASSERT(x) assert(x) -static inline int -ctx_CBRLE_recompress (uint8_t *cbrle, int size, int width, int length, int no) -{ - //uint8_t temp[width*4]; - length = ((uint16_t*)cbrle)[0]; +#ifdef MINIZ_NO_MALLOC +#define MZ_MALLOC(x) NULL +#define MZ_FREE(x) (void)x, ((void)0) +#define MZ_REALLOC(p, x) NULL +#else +#define MZ_MALLOC(x) malloc(x) +#define MZ_FREE(x) free(x) +#define MZ_REALLOC(p, x) realloc(p, x) +#endif - //if (no>0) - //fprintf (stderr, "len: %i cols:%i i:%i\n", length, cbrle[2], no); +#define MZ_MAX(a, b) (((a) > (b)) ? (a) : (b)) +#define MZ_MIN(a, b) (((a) < (b)) ? (a) : (b)) +#define MZ_CLEAR_OBJ(obj) memset(&(obj), 0, sizeof(obj)) +#define MZ_CLEAR_ARR(obj) memset((obj), 0, sizeof(obj)) +#define MZ_CLEAR_PTR(obj) memset((obj), 0, sizeof(*obj)) -#if 0 -//ctx_CBRLE_recompress_smoothen (cbrle, size, width, length); - ctx_CBRLE_recompress_smoothen (cbrle, size, width, length); - if (((uint16_t*)cbrle)[0] < length - 8 || no == 0) - { -// fprintf (stderr, "rlen: %i cols:%i i:%i\n", ((uint16_t*)cbrle)[0], -// cbrle[2], no); - return ((uint16_t*)cbrle)[0]; - } +#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN +#define MZ_READ_LE16(p) *((const mz_uint16 *)(p)) +#define MZ_READ_LE32(p) *((const mz_uint32 *)(p)) +#else +#define MZ_READ_LE16(p) ((mz_uint32)(((const mz_uint8 *)(p))[0]) | ((mz_uint32)(((const mz_uint8 *)(p))[1]) << 8U)) +#define MZ_READ_LE32(p) ((mz_uint32)(((const mz_uint8 *)(p))[0]) | ((mz_uint32)(((const mz_uint8 *)(p))[1]) << 8U) | ((mz_uint32)(((const mz_uint8 *)(p))[2]) << 16U) | ((mz_uint32)(((const mz_uint8 *)(p))[3]) << 24U)) #endif -#if 1 - if (no == 0) - { - ctx_CBRLE_recompress_smoothen (cbrle, size, width, length); - if (((uint16_t*)cbrle)[0] < length -8) - return ((uint16_t*)cbrle)[0]; - } + +#define MZ_READ_LE64(p) (((mz_uint64)MZ_READ_LE32(p)) | (((mz_uint64)MZ_READ_LE32((const mz_uint8 *)(p) + sizeof(mz_uint32))) << 32U)) + +#ifdef _MSC_VER +#define MZ_FORCEINLINE __forceinline +#elif defined(__GNUC__) +#define MZ_FORCEINLINE __inline__ __attribute__((__always_inline__)) +#else +#define MZ_FORCEINLINE inline #endif -#if 0 - ctx_CBRLE_recompress_drop_bits (cbrle, size, width, length, no); - if (((uint16_t*)cbrle)[0] < length -8) - return ((uint16_t*)cbrle)[0]; +#ifdef __cplusplus +extern "C" { #endif +extern MINIZ_EXPORT void *miniz_def_alloc_func(void *opaque, size_t items, size_t size); +extern MINIZ_EXPORT void miniz_def_free_func(void *opaque, void *address); +extern MINIZ_EXPORT void *miniz_def_realloc_func(void *opaque, void *address, size_t items, size_t size); -#if 0 - ctx_CBRLE_recompress_merge_pairs (cbrle, size, width, length); - if (((uint16_t*)cbrle)[0] < length - 8 || no < 2) - { - return ((uint16_t*)cbrle)[0]; - } -#endif +#define MZ_UINT16_MAX (0xFFFFU) +#define MZ_UINT32_MAX (0xFFFFFFFFU) - return ((uint16_t*)cbrle)[0]; +#ifdef __cplusplus } +#endif + #pragma once +#ifndef MINIZ_NO_DEFLATE_APIS + +#ifdef __cplusplus +extern "C" { #endif -#endif -#ifndef CTX_DRAWLIST_H -#define CTX_DRAWLIST_H +/* ------------------- Low-level Compression API Definitions */ -static int -ctx_conts_for_entry (CtxEntry *entry); -void -ctx_iterator_init (CtxIterator *iterator, - CtxDrawlist *drawlist, - int start_pos, - int flags); +/* Set TDEFL_LESS_MEMORY to 1 to use less memory (compression will be slightly slower, and raw/dynamic blocks will be output more frequently). */ +#define TDEFL_LESS_MEMORY 0 -int ctx_iterator_pos (CtxIterator *iterator); +/* tdefl_init() compression flags logically OR'd together (low 12 bits contain the max. number of probes per dictionary search): */ +/* TDEFL_DEFAULT_MAX_PROBES: The compressor defaults to 128 dictionary probes per dictionary search. 0=Huffman only, 1=Huffman+LZ (fastest/crap compression), 4095=Huffman+LZ (slowest/best compression). */ +enum +{ + TDEFL_HUFFMAN_ONLY = 0, + TDEFL_DEFAULT_MAX_PROBES = 128, + TDEFL_MAX_PROBES_MASK = 0xFFF +}; -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); -int -ctx_drawlist_insert_entry (CtxDrawlist *drawlist, int pos, CtxEntry *entry); -int -ctx_add_data (Ctx *ctx, void *data, int length); +/* TDEFL_WRITE_ZLIB_HEADER: If set, the compressor outputs a zlib header before the deflate data, and the Adler-32 of the source data at the end. Otherwise, you'll get raw deflate data. */ +/* TDEFL_COMPUTE_ADLER32: Always compute the adler-32 of the input data (even when not writing zlib headers). */ +/* TDEFL_GREEDY_PARSING_FLAG: Set to use faster greedy parsing, instead of more efficient lazy parsing. */ +/* TDEFL_NONDETERMINISTIC_PARSING_FLAG: Enable to decrease the compressor's initialization time to the minimum, but the output may vary from run to run given the same input (depending on the contents of memory). */ +/* TDEFL_RLE_MATCHES: Only look for RLE matches (matches with a distance of 1) */ +/* TDEFL_FILTER_MATCHES: Discards matches <= 5 chars if enabled. */ +/* TDEFL_FORCE_ALL_STATIC_BLOCKS: Disable usage of optimized Huffman tables. */ +/* TDEFL_FORCE_ALL_RAW_BLOCKS: Only use raw (uncompressed) deflate blocks. */ +/* The low 12 bits are reserved to control the max # of hash probes per dictionary lookup (see TDEFL_MAX_PROBES_MASK). */ +enum +{ + TDEFL_WRITE_ZLIB_HEADER = 0x01000, + TDEFL_COMPUTE_ADLER32 = 0x02000, + TDEFL_GREEDY_PARSING_FLAG = 0x04000, + TDEFL_NONDETERMINISTIC_PARSING_FLAG = 0x08000, + TDEFL_RLE_MATCHES = 0x10000, + TDEFL_FILTER_MATCHES = 0x20000, + TDEFL_FORCE_ALL_STATIC_BLOCKS = 0x40000, + TDEFL_FORCE_ALL_RAW_BLOCKS = 0x80000 +}; -int ctx_drawlist_add_u32 (CtxDrawlist *drawlist, CtxCode code, uint32_t u32[2]); -int ctx_drawlist_add_data (CtxDrawlist *drawlist, const void *data, int length); +/* High level compression functions: */ +/* tdefl_compress_mem_to_heap() compresses a block in memory to a heap block allocated via malloc(). */ +/* On entry: */ +/* pSrc_buf, src_buf_len: Pointer and size of source block to compress. */ +/* flags: The max match finder probes (default is 128) logically OR'd against the above flags. Higher probes are slower but improve compression. */ +/* On return: */ +/* Function returns a pointer to the compressed data, or NULL on failure. */ +/* *pOut_len will be set to the compressed data's size, which could be larger than src_buf_len on uncompressible data. */ +/* The caller must free() the returned block when it's no longer needed. */ +MINIZ_EXPORT void *tdefl_compress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags); + +/* tdefl_compress_mem_to_mem() compresses a block in memory to another block in memory. */ +/* Returns 0 on failure. */ +MINIZ_EXPORT size_t tdefl_compress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags); + +/* Compresses an image to a compressed PNG file in memory. */ +/* On entry: */ +/* pImage, w, h, and num_chans describe the image to compress. num_chans may be 1, 2, 3, or 4. */ +/* The image pitch in bytes per scanline will be w*num_chans. The leftmost pixel on the top scanline is stored first in memory. */ +/* level may range from [0,10], use MZ_NO_COMPRESSION, MZ_BEST_SPEED, MZ_BEST_COMPRESSION, etc. or a decent default is MZ_DEFAULT_LEVEL */ +/* If flip is true, the image will be flipped on the Y axis (useful for OpenGL apps). */ +/* On return: */ +/* Function returns a pointer to the compressed data, or NULL on failure. */ +/* *pLen_out will be set to the size of the PNG image file. */ +/* The caller must mz_free() the returned heap block (which will typically be larger than *pLen_out) when it's no longer needed. */ +MINIZ_EXPORT void *tdefl_write_image_to_png_file_in_memory_ex(const void *pImage, int w, int h, int num_chans, size_t *pLen_out, mz_uint level, mz_bool flip); +MINIZ_EXPORT void *tdefl_write_image_to_png_file_in_memory(const void *pImage, int w, int h, int num_chans, size_t *pLen_out); + +/* Output stream interface. The compressor uses this interface to write compressed data. It'll typically be called TDEFL_OUT_BUF_SIZE at a time. */ +typedef mz_bool (*tdefl_put_buf_func_ptr)(const void *pBuf, int len, void *pUser); + +/* tdefl_compress_mem_to_output() compresses a block to an output stream. The above helpers use this function internally. */ +MINIZ_EXPORT mz_bool tdefl_compress_mem_to_output(const void *pBuf, size_t buf_len, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags); -static CtxEntry -ctx_void (CtxCode code); -static inline CtxEntry -ctx_f (CtxCode code, float x, float y); -static CtxEntry -ctx_u32 (CtxCode code, uint32_t x, uint32_t y); -#if 0 -static CtxEntry -ctx_s32 (CtxCode code, int32_t x, int32_t y); -#endif +enum +{ + TDEFL_MAX_HUFF_TABLES = 3, + TDEFL_MAX_HUFF_SYMBOLS_0 = 288, + TDEFL_MAX_HUFF_SYMBOLS_1 = 32, + TDEFL_MAX_HUFF_SYMBOLS_2 = 19, + TDEFL_LZ_DICT_SIZE = 32768, + TDEFL_LZ_DICT_SIZE_MASK = TDEFL_LZ_DICT_SIZE - 1, + TDEFL_MIN_MATCH_LEN = 3, + TDEFL_MAX_MATCH_LEN = 258 +}; -static inline CtxEntry -ctx_s16 (CtxCode code, int x0, int y0, int x1, int y1); -static CtxEntry -ctx_u8 (CtxCode code, - uint8_t a, uint8_t b, uint8_t c, uint8_t d, - uint8_t e, uint8_t f, uint8_t g, uint8_t h); - -#define CTX_PROCESS_VOID(cmd) do {\ - CtxEntry commands[4] = {{cmd,{{0}}}};\ - ctx_process (ctx, &commands[0]);}while(0) \ +/* TDEFL_OUT_BUF_SIZE MUST be large enough to hold a single entire compressed output block (using static/fixed Huffman codes). */ +#if TDEFL_LESS_MEMORY +enum +{ + TDEFL_LZ_CODE_BUF_SIZE = 24 * 1024, + TDEFL_OUT_BUF_SIZE = (TDEFL_LZ_CODE_BUF_SIZE * 13) / 10, + TDEFL_MAX_HUFF_SYMBOLS = 288, + TDEFL_LZ_HASH_BITS = 12, + TDEFL_LEVEL1_HASH_SIZE_MASK = 4095, + TDEFL_LZ_HASH_SHIFT = (TDEFL_LZ_HASH_BITS + 2) / 3, + TDEFL_LZ_HASH_SIZE = 1 << TDEFL_LZ_HASH_BITS +}; +#else +enum +{ + TDEFL_LZ_CODE_BUF_SIZE = 64 * 1024, + TDEFL_OUT_BUF_SIZE = (TDEFL_LZ_CODE_BUF_SIZE * 13) / 10, + TDEFL_MAX_HUFF_SYMBOLS = 288, + TDEFL_LZ_HASH_BITS = 15, + TDEFL_LEVEL1_HASH_SIZE_MASK = 4095, + TDEFL_LZ_HASH_SHIFT = (TDEFL_LZ_HASH_BITS + 2) / 3, + TDEFL_LZ_HASH_SIZE = 1 << TDEFL_LZ_HASH_BITS +}; +#endif -#define CTX_PROCESS_F(cmd,x,y) do {\ - CtxEntry commands[4] = {ctx_f(cmd,x,y),};\ - ctx_process (ctx, &commands[0]);}while(0) \ +/* The low-level tdefl functions below may be used directly if the above helper functions aren't flexible enough. The low-level functions don't make any heap allocations, unlike the above helper functions. */ +typedef enum { + TDEFL_STATUS_BAD_PARAM = -2, + TDEFL_STATUS_PUT_BUF_FAILED = -1, + TDEFL_STATUS_OKAY = 0, + TDEFL_STATUS_DONE = 1 +} tdefl_status; -#define CTX_PROCESS_F1(cmd,x) do {\ - CtxEntry commands[4] = {ctx_f(cmd,x,0),};\ - ctx_process (ctx, &commands[0]);}while(0) \ +/* Must map to MZ_NO_FLUSH, MZ_SYNC_FLUSH, etc. enums */ +typedef enum { + TDEFL_NO_FLUSH = 0, + TDEFL_SYNC_FLUSH = 2, + TDEFL_FULL_FLUSH = 3, + TDEFL_FINISH = 4 +} tdefl_flush; -#define CTX_PROCESS_U32(cmd, x, y) do {\ - CtxEntry commands[4] = {ctx_u32(cmd, x, y)};\ - ctx_process (ctx, &commands[0]);}while(0) +/* tdefl's compression state structure. */ +typedef struct +{ + tdefl_put_buf_func_ptr m_pPut_buf_func; + void *m_pPut_buf_user; + mz_uint m_flags, m_max_probes[2]; + int m_greedy_parsing; + mz_uint m_adler32, m_lookahead_pos, m_lookahead_size, m_dict_size; + mz_uint8 *m_pLZ_code_buf, *m_pLZ_flags, *m_pOutput_buf, *m_pOutput_buf_end; + mz_uint m_num_flags_left, m_total_lz_bytes, m_lz_code_buf_dict_pos, m_bits_in, m_bit_buffer; + mz_uint m_saved_match_dist, m_saved_match_len, m_saved_lit, m_output_flush_ofs, m_output_flush_remaining, m_finished, m_block_index, m_wants_to_finish; + tdefl_status m_prev_return_status; + const void *m_pIn_buf; + void *m_pOut_buf; + size_t *m_pIn_buf_size, *m_pOut_buf_size; + tdefl_flush m_flush; + const mz_uint8 *m_pSrc; + size_t m_src_buf_left, m_out_buf_ofs; + mz_uint8 m_dict[TDEFL_LZ_DICT_SIZE + TDEFL_MAX_MATCH_LEN - 1]; + mz_uint16 m_huff_count[TDEFL_MAX_HUFF_TABLES][TDEFL_MAX_HUFF_SYMBOLS]; + mz_uint16 m_huff_codes[TDEFL_MAX_HUFF_TABLES][TDEFL_MAX_HUFF_SYMBOLS]; + mz_uint8 m_huff_code_sizes[TDEFL_MAX_HUFF_TABLES][TDEFL_MAX_HUFF_SYMBOLS]; + mz_uint8 m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE]; + mz_uint16 m_next[TDEFL_LZ_DICT_SIZE]; + mz_uint16 m_hash[TDEFL_LZ_HASH_SIZE]; + mz_uint8 m_output_buf[TDEFL_OUT_BUF_SIZE]; +} tdefl_compressor; + +/* Initializes the compressor. */ +/* There is no corresponding deinit() function because the tdefl API's do not dynamically allocate memory. */ +/* pBut_buf_func: If NULL, output data will be supplied to the specified callback. In this case, the user should call the tdefl_compress_buffer() API for compression. */ +/* If pBut_buf_func is NULL the user should always call the tdefl_compress() API. */ +/* flags: See the above enums (TDEFL_HUFFMAN_ONLY, TDEFL_WRITE_ZLIB_HEADER, etc.) */ +MINIZ_EXPORT tdefl_status tdefl_init(tdefl_compressor *d, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags); + +/* Compresses a block of data, consuming as much of the specified input buffer as possible, and writing as much compressed data to the specified output buffer as possible. */ +MINIZ_EXPORT tdefl_status tdefl_compress(tdefl_compressor *d, const void *pIn_buf, size_t *pIn_buf_size, void *pOut_buf, size_t *pOut_buf_size, tdefl_flush flush); + +/* tdefl_compress_buffer() is only usable when the tdefl_init() is called with a non-NULL tdefl_put_buf_func_ptr. */ +/* tdefl_compress_buffer() always consumes the entire input buffer. */ +MINIZ_EXPORT tdefl_status tdefl_compress_buffer(tdefl_compressor *d, const void *pIn_buf, size_t in_buf_size, tdefl_flush flush); + +MINIZ_EXPORT tdefl_status tdefl_get_prev_return_status(tdefl_compressor *d); +MINIZ_EXPORT mz_uint32 tdefl_get_adler32(tdefl_compressor *d); + +/* Create tdefl_compress() flags given zlib-style compression parameters. */ +/* level may range from [0,10] (where 10 is absolute max compression, but may be much slower on some files) */ +/* window_bits may be -15 (raw deflate) or 15 (zlib) */ +/* strategy may be either MZ_DEFAULT_STRATEGY, MZ_FILTERED, MZ_HUFFMAN_ONLY, MZ_RLE, or MZ_FIXED */ +MINIZ_EXPORT mz_uint tdefl_create_comp_flags_from_zip_params(int level, int window_bits, int strategy); + +#ifndef MINIZ_NO_MALLOC +/* Allocate the tdefl_compressor structure in C so that */ +/* non-C language bindings to tdefl_ API don't need to worry about */ +/* structure size and allocation mechanism. */ +MINIZ_EXPORT tdefl_compressor *tdefl_compressor_alloc(void); +MINIZ_EXPORT void tdefl_compressor_free(tdefl_compressor *pComp); +#endif -#define CTX_PROCESS_U8(cmd, x) do {\ - CtxEntry commands[4] = {ctx_u8(cmd, x,0,0,0,0,0,0,0)};\ - ctx_process (ctx, &commands[0]);}while(0) +#ifdef __cplusplus +} +#endif +#endif /*#ifndef MINIZ_NO_DEFLATE_APIS*/ + #pragma once -#if CTX_BITPACK_PACKER -static unsigned int -ctx_last_history (CtxDrawlist *drawlist); -#endif +/* ------------------- Low-level Decompression API Definitions */ -#if CTX_BITPACK_PACKER -static void -ctx_drawlist_remove_tiny_curves (CtxDrawlist *drawlist, int start_pos); +#ifndef MINIZ_NO_INFLATE_APIS -static void -ctx_drawlist_bitpack (CtxDrawlist *drawlist, unsigned int start_pos); +#ifdef __cplusplus +extern "C" { #endif +/* Decompression flags used by tinfl_decompress(). */ +/* TINFL_FLAG_PARSE_ZLIB_HEADER: If set, the input has a valid zlib header and ends with an adler32 checksum (it's a valid zlib stream). Otherwise, the input is a raw deflate stream. */ +/* TINFL_FLAG_HAS_MORE_INPUT: If set, there are more input bytes available beyond the end of the supplied input buffer. If clear, the input buffer contains all remaining input. */ +/* TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF: If set, the output buffer is large enough to hold the entire decompressed stream. If clear, the output buffer is at least the size of the dictionary (typically 32KB). */ +/* TINFL_FLAG_COMPUTE_ADLER32: Force adler-32 checksum computation of the decompressed bytes. */ +enum +{ + TINFL_FLAG_PARSE_ZLIB_HEADER = 1, + TINFL_FLAG_HAS_MORE_INPUT = 2, + TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF = 4, + TINFL_FLAG_COMPUTE_ADLER32 = 8 +}; -static void -ctx_process_cmd_str (Ctx *ctx, CtxCode code, const char *string, uint32_t arg0, uint32_t arg1); -static void -ctx_process_cmd_str_float (Ctx *ctx, CtxCode code, const char *string, float arg0, float arg1); -static void -ctx_process_cmd_str_with_len (Ctx *ctx, CtxCode code, const char *string, uint32_t arg0, uint32_t arg1, int len); +/* High level decompression functions: */ +/* tinfl_decompress_mem_to_heap() decompresses a block in memory to a heap block allocated via malloc(). */ +/* On entry: */ +/* pSrc_buf, src_buf_len: Pointer and size of the Deflate or zlib source data to decompress. */ +/* On return: */ +/* Function returns a pointer to the decompressed data, or NULL on failure. */ +/* *pOut_len will be set to the decompressed data's size, which could be larger than src_buf_len on uncompressible data. */ +/* The caller must call mz_free() on the returned block when it's no longer needed. */ +MINIZ_EXPORT void *tinfl_decompress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags); + +/* tinfl_decompress_mem_to_mem() decompresses a block in memory to another block in memory. */ +/* Returns TINFL_DECOMPRESS_MEM_TO_MEM_FAILED on failure, or the number of bytes written on success. */ +#define TINFL_DECOMPRESS_MEM_TO_MEM_FAILED ((size_t)(-1)) +MINIZ_EXPORT size_t tinfl_decompress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags); + +/* tinfl_decompress_mem_to_callback() decompresses a block in memory to an internal 32KB buffer, and a user provided callback function will be called to flush the buffer. */ +/* Returns 1 on success or 0 on failure. */ +typedef int (*tinfl_put_buf_func_ptr)(const void *pBuf, int len, void *pUser); +MINIZ_EXPORT int tinfl_decompress_mem_to_callback(const void *pIn_buf, size_t *pIn_buf_size, tinfl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags); + +struct tinfl_decompressor_tag; +typedef struct tinfl_decompressor_tag tinfl_decompressor; + +#ifndef MINIZ_NO_MALLOC +/* Allocate the tinfl_decompressor structure in C so that */ +/* non-C language bindings to tinfl_ API don't need to worry about */ +/* structure size and allocation mechanism. */ +MINIZ_EXPORT tinfl_decompressor *tinfl_decompressor_alloc(void); +MINIZ_EXPORT void tinfl_decompressor_free(tinfl_decompressor *pDecomp); +#endif + +/* Max size of LZ dictionary. */ +#define TINFL_LZ_DICT_SIZE 32768 + +/* Return status. */ +typedef enum { + /* This flags indicates the inflator needs 1 or more input bytes to make forward progress, but the caller is indicating that no more are available. The compressed data */ + /* is probably corrupted. If you call the inflator again with more bytes it'll try to continue processing the input but this is a BAD sign (either the data is corrupted or you called it incorrectly). */ + /* If you call it again with no input you'll just get TINFL_STATUS_FAILED_CANNOT_MAKE_PROGRESS again. */ + TINFL_STATUS_FAILED_CANNOT_MAKE_PROGRESS = -4, + + /* This flag indicates that one or more of the input parameters was obviously bogus. (You can try calling it again, but if you get this error the calling code is wrong.) */ + TINFL_STATUS_BAD_PARAM = -3, + + /* This flags indicate the inflator is finished but the adler32 check of the uncompressed data didn't match. If you call it again it'll return TINFL_STATUS_DONE. */ + TINFL_STATUS_ADLER32_MISMATCH = -2, + + /* This flags indicate the inflator has somehow failed (bad code, corrupted input, etc.). If you call it again without resetting via tinfl_init() it it'll just keep on returning the same status failure code. */ + TINFL_STATUS_FAILED = -1, + + /* Any status code less than TINFL_STATUS_DONE must indicate a failure. */ + + /* This flag indicates the inflator has returned every byte of uncompressed data that it can, has consumed every byte that it needed, has successfully reached the end of the deflate stream, and */ + /* if zlib headers and adler32 checking enabled that it has successfully checked the uncompressed data's adler32. If you call it again you'll just get TINFL_STATUS_DONE over and over again. */ + TINFL_STATUS_DONE = 0, + + /* This flag indicates the inflator MUST have more input data (even 1 byte) before it can make any more forward progress, or you need to clear the TINFL_FLAG_HAS_MORE_INPUT */ + /* flag on the next call if you don't have any more source data. If the source data was somehow corrupted it's also possible (but unlikely) for the inflator to keep on demanding input to */ + /* proceed, so be sure to properly set the TINFL_FLAG_HAS_MORE_INPUT flag. */ + TINFL_STATUS_NEEDS_MORE_INPUT = 1, + + /* This flag indicates the inflator definitely has 1 or more bytes of uncompressed data available, but it cannot write this data into the output buffer. */ + /* Note if the source compressed data was corrupted it's possible for the inflator to return a lot of uncompressed data to the caller. I've been assuming you know how much uncompressed data to expect */ + /* (either exact or worst case) and will stop calling the inflator and fail after receiving too much. In pure streaming scenarios where you have no idea how many bytes to expect this may not be possible */ + /* so I may need to add some code to address this. */ + TINFL_STATUS_HAS_MORE_OUTPUT = 2 +} tinfl_status; + +/* Initializes the decompressor to its initial state. */ +#define tinfl_init(r) \ + do \ + { \ + (r)->m_state = 0; \ + } \ + MZ_MACRO_END +#define tinfl_get_adler32(r) (r)->m_check_adler32 + +/* Main low-level decompressor coroutine function. This is the only function actually needed for decompression. All the other functions are just high-level helpers for improved usability. */ +/* This is a universal API, i.e. it can be used as a building block to build any desired higher level decompression API. In the limit case, it can be called once per every byte input or output. */ +MINIZ_EXPORT tinfl_status tinfl_decompress(tinfl_decompressor *r, const mz_uint8 *pIn_buf_next, size_t *pIn_buf_size, mz_uint8 *pOut_buf_start, mz_uint8 *pOut_buf_next, size_t *pOut_buf_size, const mz_uint32 decomp_flags); + +/* Internal/private bits follow. */ +enum +{ + TINFL_MAX_HUFF_TABLES = 3, + TINFL_MAX_HUFF_SYMBOLS_0 = 288, + TINFL_MAX_HUFF_SYMBOLS_1 = 32, + TINFL_MAX_HUFF_SYMBOLS_2 = 19, + TINFL_FAST_LOOKUP_BITS = 10, + TINFL_FAST_LOOKUP_SIZE = 1 << TINFL_FAST_LOOKUP_BITS +}; -#pragma pack(push,1) -typedef struct -CtxSegment { -#if CTX_32BIT_SEGMENTS - uint32_t code; +#if MINIZ_HAS_64BIT_REGISTERS +#define TINFL_USE_64BIT_BITBUF 1 #else - uint16_t code; +#define TINFL_USE_64BIT_BITBUF 0 #endif - union { -#if CTX_32BIT_SEGMENTS - int32_t s16[4]; + +#if TINFL_USE_64BIT_BITBUF +typedef mz_uint64 tinfl_bit_buf_t; +#define TINFL_BITBUF_SIZE (64) #else - int16_t s16[4]; +typedef mz_uint32 tinfl_bit_buf_t; +#define TINFL_BITBUF_SIZE (32) +#endif + +struct tinfl_decompressor_tag +{ + mz_uint32 m_state, m_num_bits, m_zhdr0, m_zhdr1, m_z_adler32, m_final, m_type, m_check_adler32, m_dist, m_counter, m_num_extra, m_table_sizes[TINFL_MAX_HUFF_TABLES]; + tinfl_bit_buf_t m_bit_buf; + size_t m_dist_from_out_buf_start; + mz_int16 m_look_up[TINFL_MAX_HUFF_TABLES][TINFL_FAST_LOOKUP_SIZE]; + mz_int16 m_tree_0[TINFL_MAX_HUFF_SYMBOLS_0 * 2]; + mz_int16 m_tree_1[TINFL_MAX_HUFF_SYMBOLS_1 * 2]; + mz_int16 m_tree_2[TINFL_MAX_HUFF_SYMBOLS_2 * 2]; + mz_uint8 m_code_size_0[TINFL_MAX_HUFF_SYMBOLS_0]; + mz_uint8 m_code_size_1[TINFL_MAX_HUFF_SYMBOLS_1]; + mz_uint8 m_code_size_2[TINFL_MAX_HUFF_SYMBOLS_2]; + mz_uint8 m_raw_header[4], m_len_codes[TINFL_MAX_HUFF_SYMBOLS_0 + TINFL_MAX_HUFF_SYMBOLS_1 + 137]; +}; + +#ifdef __cplusplus +} #endif - uint32_t u32[2]; - } data; - int32_t val; - int32_t delta; -} CtxSegment; -#pragma pack(pop) +#endif /*#ifndef MINIZ_NO_INFLATE_APIS*/ + +#pragma once -static inline CtxSegment -ctx_segment_s16 (CtxCode 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; -} +/* ------------------- ZIP archive reading/writing */ -static inline void -ctx_edgelist_resize (CtxDrawlist *drawlist, int desired_size) -{ -#if CTX_DRAWLIST_STATIC - { - static CtxSegment sbuf[CTX_MAX_EDGE_LIST_SIZE]; - drawlist->entries = (CtxEntry*)&sbuf[0]; - drawlist->size = CTX_MAX_EDGE_LIST_SIZE; - } -#else - int new_size = desired_size; - int min_size = CTX_MIN_JOURNAL_SIZE; - int max_size = CTX_MAX_JOURNAL_SIZE; - { - min_size = CTX_MIN_EDGE_LIST_SIZE; - max_size = CTX_MAX_EDGE_LIST_SIZE; - } +#ifndef MINIZ_NO_ARCHIVE_APIS - if (CTX_UNLIKELY(drawlist->size == max_size)) - { return; } - new_size = ctx_maxi (new_size, min_size); - //if (new_size < drawlist->count) - // { new_size = drawlist->count + 4; } - new_size = ctx_mini (new_size, max_size); - if (new_size != drawlist->size) - { - int item_size = item_size = sizeof (CtxSegment); - //fprintf (stderr, "growing drawlist %p %i to %d from %d\n", drawlist, flags, new_size, drawlist->size); - if (drawlist->entries) - { - //printf ("grow %p to %d from %d\n", drawlist, new_size, drawlist->size); - CtxEntry *ne = (CtxEntry *) ctx_malloc (item_size * new_size); - memcpy (ne, drawlist->entries, drawlist->size * item_size ); - ctx_free (drawlist->entries); - drawlist->entries = ne; - //drawlist->entries = (CtxEntry*)ctx_malloc (drawlist->entries, item_size * new_size); - } - else - { - //fprintf (stderr, "allocating for %p %d\n", drawlist, new_size); - drawlist->entries = (CtxEntry *) ctx_malloc (item_size * new_size); - } - drawlist->size = new_size; - } - //fprintf (stderr, "drawlist %p is %d\n", drawlist, drawlist->size); +#ifdef __cplusplus +extern "C" { #endif -} +enum +{ + /* Note: These enums can be reduced as needed to save memory or stack space - they are pretty conservative. */ + MZ_ZIP_MAX_IO_BUF_SIZE = 64 * 1024, + MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE = 512, + MZ_ZIP_MAX_ARCHIVE_FILE_COMMENT_SIZE = 512 +}; -static inline int -ctx_edgelist_add_single (CtxDrawlist *drawlist, CtxEntry *entry) +typedef struct { - int ret = drawlist->count; + /* Central directory file index. */ + mz_uint32 m_file_index; - if (CTX_UNLIKELY(ret >= CTX_MAX_EDGE_LIST_SIZE- 20)) - { - return 0; - } - if (CTX_UNLIKELY(ret + 2 >= drawlist->size)) - { - int new_ = ctx_maxi (drawlist->size * 2, ret + 1024); - new_ = ctx_mini (CTX_MAX_EDGE_LIST_SIZE, new_); - ctx_edgelist_resize (drawlist, new_); - } + /* Byte offset of this entry in the archive's central directory. Note we currently only support up to UINT_MAX or less bytes in the central dir. */ + mz_uint64 m_central_dir_ofs; - ((CtxSegment*)(drawlist->entries))[ret] = *(CtxSegment*)entry; - drawlist->count++; - return ret; -} + /* These fields are copied directly from the zip's central dir. */ + mz_uint16 m_version_made_by; + mz_uint16 m_version_needed; + mz_uint16 m_bit_flag; + mz_uint16 m_method; + /* CRC-32 of uncompressed data. */ + mz_uint32 m_crc32; -#endif + /* File's compressed size. */ + mz_uint64 m_comp_size; + /* File's uncompressed size. Note, I've seen some old archives where directory entries had 512 bytes for their uncompressed sizes, but when you try to unpack them you actually get 0 bytes. */ + mz_uint64 m_uncomp_size; -#if CTX_COMPOSITE + /* Zip internal and external file attributes. */ + mz_uint16 m_internal_attr; + mz_uint32 m_external_attr; -#define CTX_FULL_AA 15 -#define CTX_REFERENCE 0 + /* Entry's local header file offset in bytes. */ + mz_uint64 m_local_header_ofs; + /* Size of comment in bytes. */ + mz_uint32 m_comment_size; -#define CTX_RGBA8_R_SHIFT 0 -#define CTX_RGBA8_G_SHIFT 8 -#define CTX_RGBA8_B_SHIFT 16 -#define CTX_RGBA8_A_SHIFT 24 + /* MZ_TRUE if the entry appears to be a directory. */ + mz_bool m_is_directory; -#define CTX_RGBA8_R_MASK (0xff << CTX_RGBA8_R_SHIFT) -#define CTX_RGBA8_G_MASK (0xff << CTX_RGBA8_G_SHIFT) -#define CTX_RGBA8_B_MASK (0xff << CTX_RGBA8_B_SHIFT) -#define CTX_RGBA8_A_MASK (0xff << CTX_RGBA8_A_SHIFT) + /* MZ_TRUE if the entry uses encryption/strong encryption (which miniz_zip doesn't support) */ + mz_bool m_is_encrypted; -#define CTX_RGBA8_RB_MASK (CTX_RGBA8_R_MASK | CTX_RGBA8_B_MASK) -#define CTX_RGBA8_GA_MASK (CTX_RGBA8_G_MASK | CTX_RGBA8_A_MASK) + /* MZ_TRUE if the file is not encrypted, a patch file, and if it uses a compression method we support. */ + mz_bool m_is_supported; + /* Filename. If string ends in '/' it's a subdirectory entry. */ + /* Guaranteed to be zero terminated, may be truncated to fit. */ + char m_filename[MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE]; + /* Comment field. */ + /* Guaranteed to be zero terminated, may be truncated to fit. */ + char m_comment[MZ_ZIP_MAX_ARCHIVE_FILE_COMMENT_SIZE]; -CTX_INLINE static void -ctx_RGBA8_associate_alpha (uint8_t *u8) -{ -#if 1 - uint32_t val = *((uint32_t*)(u8)); - uint32_t a = u8[3]; - uint32_t g = (((val & CTX_RGBA8_G_MASK) * a) >> 8) & CTX_RGBA8_G_MASK; - uint32_t rb =(((val & CTX_RGBA8_RB_MASK) * a) >> 8) & CTX_RGBA8_RB_MASK; - *((uint32_t*)(u8)) = g|rb|(a << CTX_RGBA8_A_SHIFT); +#ifdef MINIZ_NO_TIME + MZ_TIME_T m_padding; #else - uint32_t a = u8[3]; - u8[0] = (u8[0] * a + 255) >> 8; - u8[1] = (u8[1] * a + 255) >> 8; - u8[2] = (u8[2] * a + 255) >> 8; + MZ_TIME_T m_time; #endif -} +} mz_zip_archive_file_stat; -inline static void -ctx_RGBA8_associate_global_alpha (uint8_t *u8, uint8_t global_alpha) -{ - uint32_t val = *((uint32_t*)(u8)); - uint32_t a = (u8[3] * global_alpha + 255) >> 8; - uint32_t g = (((val & CTX_RGBA8_G_MASK) * a) >> 8) & CTX_RGBA8_G_MASK; - uint32_t rb =(((val & CTX_RGBA8_RB_MASK) * a) >> 8) & CTX_RGBA8_RB_MASK; - *((uint32_t*)(u8)) = g|rb|(a << CTX_RGBA8_A_SHIFT); -} +typedef size_t (*mz_file_read_func)(void *pOpaque, mz_uint64 file_ofs, void *pBuf, size_t n); +typedef size_t (*mz_file_write_func)(void *pOpaque, mz_uint64 file_ofs, const void *pBuf, size_t n); +typedef mz_bool (*mz_file_needs_keepalive)(void *pOpaque); -inline static uint32_t -ctx_RGBA8_associate_global_alpha_u32 (uint32_t val, uint8_t global_alpha) -{ - uint32_t a = ((val>>24) * global_alpha + 255) >> 8; - uint32_t g = (((val & CTX_RGBA8_G_MASK) * a) >> 8) & CTX_RGBA8_G_MASK; - uint32_t rb =(((val & CTX_RGBA8_RB_MASK) * a) >> 8) & CTX_RGBA8_RB_MASK; - return g|rb|(a << CTX_RGBA8_A_SHIFT); -} +struct mz_zip_internal_state_tag; +typedef struct mz_zip_internal_state_tag mz_zip_internal_state; -CTX_INLINE static void -ctx_RGBA8_associate_alpha_probably_opaque (uint8_t *u8) +typedef enum { + MZ_ZIP_MODE_INVALID = 0, + MZ_ZIP_MODE_READING = 1, + MZ_ZIP_MODE_WRITING = 2, + MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED = 3 +} mz_zip_mode; + +typedef enum { + MZ_ZIP_FLAG_CASE_SENSITIVE = 0x0100, + MZ_ZIP_FLAG_IGNORE_PATH = 0x0200, + MZ_ZIP_FLAG_COMPRESSED_DATA = 0x0400, + MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY = 0x0800, + MZ_ZIP_FLAG_VALIDATE_LOCATE_FILE_FLAG = 0x1000, /* if enabled, mz_zip_reader_locate_file() will be called on each file as its validated to ensure the func finds the file in the central dir (intended for testing) */ + MZ_ZIP_FLAG_VALIDATE_HEADERS_ONLY = 0x2000, /* validate the local headers, but don't decompress the entire file and check the crc32 */ + MZ_ZIP_FLAG_WRITE_ZIP64 = 0x4000, /* always use the zip64 file format, instead of the original zip file format with automatic switch to zip64. Use as flags parameter with mz_zip_writer_init*_v2 */ + MZ_ZIP_FLAG_WRITE_ALLOW_READING = 0x8000, + MZ_ZIP_FLAG_ASCII_FILENAME = 0x10000, + /*After adding a compressed file, seek back + to local file header and set the correct sizes*/ + MZ_ZIP_FLAG_WRITE_HEADER_SET_SIZE = 0x20000 +} mz_zip_flags; + +typedef enum { + MZ_ZIP_TYPE_INVALID = 0, + MZ_ZIP_TYPE_USER, + MZ_ZIP_TYPE_MEMORY, + MZ_ZIP_TYPE_HEAP, + MZ_ZIP_TYPE_FILE, + MZ_ZIP_TYPE_CFILE, + MZ_ZIP_TOTAL_TYPES +} mz_zip_type; + +/* miniz error codes. Be sure to update mz_zip_get_error_string() if you add or modify this enum. */ +typedef enum { + MZ_ZIP_NO_ERROR = 0, + MZ_ZIP_UNDEFINED_ERROR, + MZ_ZIP_TOO_MANY_FILES, + MZ_ZIP_FILE_TOO_LARGE, + MZ_ZIP_UNSUPPORTED_METHOD, + MZ_ZIP_UNSUPPORTED_ENCRYPTION, + MZ_ZIP_UNSUPPORTED_FEATURE, + MZ_ZIP_FAILED_FINDING_CENTRAL_DIR, + MZ_ZIP_NOT_AN_ARCHIVE, + MZ_ZIP_INVALID_HEADER_OR_CORRUPTED, + MZ_ZIP_UNSUPPORTED_MULTIDISK, + MZ_ZIP_DECOMPRESSION_FAILED, + MZ_ZIP_COMPRESSION_FAILED, + MZ_ZIP_UNEXPECTED_DECOMPRESSED_SIZE, + MZ_ZIP_CRC_CHECK_FAILED, + MZ_ZIP_UNSUPPORTED_CDIR_SIZE, + MZ_ZIP_ALLOC_FAILED, + MZ_ZIP_FILE_OPEN_FAILED, + MZ_ZIP_FILE_CREATE_FAILED, + MZ_ZIP_FILE_WRITE_FAILED, + MZ_ZIP_FILE_READ_FAILED, + MZ_ZIP_FILE_CLOSE_FAILED, + MZ_ZIP_FILE_SEEK_FAILED, + MZ_ZIP_FILE_STAT_FAILED, + MZ_ZIP_INVALID_PARAMETER, + MZ_ZIP_INVALID_FILENAME, + MZ_ZIP_BUF_TOO_SMALL, + MZ_ZIP_INTERNAL_ERROR, + MZ_ZIP_FILE_NOT_FOUND, + MZ_ZIP_ARCHIVE_TOO_LARGE, + MZ_ZIP_VALIDATION_FAILED, + MZ_ZIP_WRITE_CALLBACK_FAILED, + MZ_ZIP_TOTAL_ERRORS +} mz_zip_error; + +typedef struct { - 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; - } -} + mz_uint64 m_archive_size; + mz_uint64 m_central_directory_file_ofs; -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) + /* We only support up to UINT32_MAX files in zip64 mode. */ + mz_uint32 m_total_files; + mz_zip_mode m_zip_mode; + mz_zip_type m_zip_type; + mz_zip_error m_last_error; + + mz_uint64 m_file_offset_alignment; + + mz_alloc_func m_pAlloc; + mz_free_func m_pFree; + mz_realloc_func m_pRealloc; + void *m_pAlloc_opaque; + + mz_file_read_func m_pRead; + mz_file_write_func m_pWrite; + mz_file_needs_keepalive m_pNeeds_keepalive; + void *m_pIO_opaque; + + mz_zip_internal_state *m_pState; + +} mz_zip_archive; + +typedef struct { -#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 + mz_zip_archive *pZip; + mz_uint flags; + + int status; + + mz_uint64 read_buf_size, read_buf_ofs, read_buf_avail, comp_remaining, out_buf_ofs, cur_file_ofs; + mz_zip_archive_file_stat file_stat; + void *pRead_buf; + void *pWrite_buf; + + size_t out_blk_remain; + + tinfl_decompressor inflator; + +#ifdef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS + mz_uint padding; #else - uint32_t s0_ga, s0_rb, s1_ga, s1_rb; - ctx_lerp_RGBA8_split (isrc00, isrc01, dx, &s0_ga, &s0_rb); - ctx_lerp_RGBA8_split (isrc10, isrc11, dx, &s1_ga, &s1_rb); - return ctx_lerp_RGBA8_merge (s0_ga, s0_rb, s1_ga, s1_rb, dy); + mz_uint file_crc32; #endif -} -#if CTX_GRADIENTS -#if CTX_GRADIENT_CACHE +} mz_zip_reader_extract_iter_state; -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 = ctx_mini (rasterizer->gradient_cache_elements-1, ret); - return ret; -} +/* -------- ZIP reading */ -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)); -} +/* Inits a ZIP archive reader. */ +/* These functions read and validate the archive's central directory. */ +MINIZ_EXPORT mz_bool mz_zip_reader_init(mz_zip_archive *pZip, mz_uint64 size, mz_uint flags); -//static void -//ctx_gradient_cache_reset (void) -//{ -// ctx_gradient_cache_valid = 0; -//} +MINIZ_EXPORT mz_bool mz_zip_reader_init_mem(mz_zip_archive *pZip, const void *pMem, size_t size, mz_uint flags); + +#ifndef MINIZ_NO_STDIO +/* Read a archive from a disk file. */ +/* file_start_ofs is the file offset where the archive actually begins, or 0. */ +/* actual_archive_size is the true total size of the archive, which may be smaller than the file's actual size on disk. If zero the entire file is treated as the archive. */ +MINIZ_EXPORT mz_bool mz_zip_reader_init_file(mz_zip_archive *pZip, const char *pFilename, mz_uint32 flags); +MINIZ_EXPORT mz_bool mz_zip_reader_init_file_v2(mz_zip_archive *pZip, const char *pFilename, mz_uint flags, mz_uint64 file_start_ofs, mz_uint64 archive_size); + +/* Read an archive from an already opened FILE, beginning at the current file position. */ +/* The archive is assumed to be archive_size bytes long. If archive_size is 0, then the entire rest of the file is assumed to contain the archive. */ +/* The FILE will NOT be closed when mz_zip_reader_end() is called. */ +MINIZ_EXPORT mz_bool mz_zip_reader_init_cfile(mz_zip_archive *pZip, MZ_FILE *pFile, mz_uint64 archive_size, mz_uint flags); #endif +/* Ends archive reading, freeing all allocations, and closing the input archive file if mz_zip_reader_init_file() was used. */ +MINIZ_EXPORT mz_bool mz_zip_reader_end(mz_zip_archive *pZip); -CTX_INLINE static void -_ctx_fragment_gradient_1d_RGBA8 (CtxRasterizer *rasterizer, float x, float y, uint8_t *rgba) -{ - float v = x; - uint8_t global_alpha_u8 = rasterizer->state->gstate.global_alpha_u8; - CtxGradient *g = &rasterizer->state->gradient; - if (v < 0) { v = 0; } - if (v > 1) { v = 1; } +/* -------- ZIP reading or writing */ - if (g->n_stops == 0) - { - rgba[0] = rgba[1] = rgba[2] = (int)(v * 255); - rgba[3] = 255; - return; - } - CtxGradientStop *stop = NULL; - CtxGradientStop *next_stop = &g->stops[0]; - CtxColor *color; - for (int s = 0; s < g->n_stops; s++) - { - stop = &g->stops[s]; - next_stop = &g->stops[s+1]; - if (s + 1 >= g->n_stops) { next_stop = NULL; } - if (v >= stop->pos && next_stop && v < next_stop->pos) - { break; } - stop = NULL; - next_stop = NULL; - } - if (stop == NULL && next_stop) - { - color = & (next_stop->color); - } - else if (stop && next_stop == NULL) - { - color = & (stop->color); - } - else if (stop && next_stop) - { - uint8_t stop_rgba[4]; - uint8_t next_rgba[4]; - ctx_color_get_rgba8 (rasterizer->state, & (stop->color), stop_rgba); - ctx_color_get_rgba8 (rasterizer->state, & (next_stop->color), next_rgba); - int dx = (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); } +/* Clears a mz_zip_archive struct to all zeros. */ +/* Important: This must be done before passing the struct to any mz_zip functions. */ +MINIZ_EXPORT void mz_zip_zero_struct(mz_zip_archive *pZip); + +MINIZ_EXPORT mz_zip_mode mz_zip_get_mode(mz_zip_archive *pZip); +MINIZ_EXPORT mz_zip_type mz_zip_get_type(mz_zip_archive *pZip); + +/* Returns the total number of files in the archive. */ +MINIZ_EXPORT mz_uint mz_zip_reader_get_num_files(mz_zip_archive *pZip); + +MINIZ_EXPORT mz_uint64 mz_zip_get_archive_size(mz_zip_archive *pZip); +MINIZ_EXPORT mz_uint64 mz_zip_get_archive_file_start_offset(mz_zip_archive *pZip); +MINIZ_EXPORT MZ_FILE *mz_zip_get_cfile(mz_zip_archive *pZip); + +/* Reads n bytes of raw archive data, starting at file offset file_ofs, to pBuf. */ +MINIZ_EXPORT size_t mz_zip_read_archive_data(mz_zip_archive *pZip, mz_uint64 file_ofs, void *pBuf, size_t n); + +/* All mz_zip funcs set the m_last_error field in the mz_zip_archive struct. These functions retrieve/manipulate this field. */ +/* Note that the m_last_error functionality is not thread safe. */ +MINIZ_EXPORT mz_zip_error mz_zip_set_last_error(mz_zip_archive *pZip, mz_zip_error err_num); +MINIZ_EXPORT mz_zip_error mz_zip_peek_last_error(mz_zip_archive *pZip); +MINIZ_EXPORT mz_zip_error mz_zip_clear_last_error(mz_zip_archive *pZip); +MINIZ_EXPORT mz_zip_error mz_zip_get_last_error(mz_zip_archive *pZip); +MINIZ_EXPORT const char *mz_zip_get_error_string(mz_zip_error mz_err); + +/* MZ_TRUE if the archive file entry is a directory entry. */ +MINIZ_EXPORT mz_bool mz_zip_reader_is_file_a_directory(mz_zip_archive *pZip, mz_uint file_index); + +/* MZ_TRUE if the file is encrypted/strong encrypted. */ +MINIZ_EXPORT mz_bool mz_zip_reader_is_file_encrypted(mz_zip_archive *pZip, mz_uint file_index); + +/* MZ_TRUE if the compression method is supported, and the file is not encrypted, and the file is not a compressed patch file. */ +MINIZ_EXPORT mz_bool mz_zip_reader_is_file_supported(mz_zip_archive *pZip, mz_uint file_index); + +/* Retrieves the filename of an archive file entry. */ +/* Returns the number of bytes written to pFilename, or if filename_buf_size is 0 this function returns the number of bytes needed to fully store the filename. */ +MINIZ_EXPORT mz_uint mz_zip_reader_get_filename(mz_zip_archive *pZip, mz_uint file_index, char *pFilename, mz_uint filename_buf_size); + +/* Attempts to locates a file in the archive's central directory. */ +/* Valid flags: MZ_ZIP_FLAG_CASE_SENSITIVE, MZ_ZIP_FLAG_IGNORE_PATH */ +/* Returns -1 if the file cannot be found. */ +MINIZ_EXPORT int mz_zip_reader_locate_file(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags); +MINIZ_EXPORT mz_bool mz_zip_reader_locate_file_v2(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags, mz_uint32 *file_index); + +/* Returns detailed information about an archive file entry. */ +MINIZ_EXPORT mz_bool mz_zip_reader_file_stat(mz_zip_archive *pZip, mz_uint file_index, mz_zip_archive_file_stat *pStat); + +/* MZ_TRUE if the file is in zip64 format. */ +/* A file is considered zip64 if it contained a zip64 end of central directory marker, or if it contained any zip64 extended file information fields in the central directory. */ +MINIZ_EXPORT mz_bool mz_zip_is_zip64(mz_zip_archive *pZip); + +/* Returns the total central directory size in bytes. */ +/* The current max supported size is <= MZ_UINT32_MAX. */ +MINIZ_EXPORT size_t mz_zip_get_central_dir_size(mz_zip_archive *pZip); + +/* Extracts a archive file to a memory buffer using no memory allocation. */ +/* There must be at least enough room on the stack to store the inflator's state (~34KB or so). */ +MINIZ_EXPORT mz_bool mz_zip_reader_extract_to_mem_no_alloc(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size); +MINIZ_EXPORT mz_bool mz_zip_reader_extract_file_to_mem_no_alloc(mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size); + +/* Extracts a archive file to a memory buffer. */ +MINIZ_EXPORT mz_bool mz_zip_reader_extract_to_mem(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags); +MINIZ_EXPORT mz_bool mz_zip_reader_extract_file_to_mem(mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags); + +/* Extracts a archive file to a dynamically allocated heap buffer. */ +/* The memory will be allocated via the mz_zip_archive's alloc/realloc functions. */ +/* Returns NULL and sets the last error on failure. */ +MINIZ_EXPORT void *mz_zip_reader_extract_to_heap(mz_zip_archive *pZip, mz_uint file_index, size_t *pSize, mz_uint flags); +MINIZ_EXPORT void *mz_zip_reader_extract_file_to_heap(mz_zip_archive *pZip, const char *pFilename, size_t *pSize, mz_uint flags); + +/* Extracts a archive file using a callback function to output the file's data. */ +MINIZ_EXPORT mz_bool mz_zip_reader_extract_to_callback(mz_zip_archive *pZip, mz_uint file_index, mz_file_write_func pCallback, void *pOpaque, mz_uint flags); +MINIZ_EXPORT mz_bool mz_zip_reader_extract_file_to_callback(mz_zip_archive *pZip, const char *pFilename, mz_file_write_func pCallback, void *pOpaque, mz_uint flags); + +/* Extract a file iteratively */ +MINIZ_EXPORT mz_zip_reader_extract_iter_state* mz_zip_reader_extract_iter_new(mz_zip_archive *pZip, mz_uint file_index, mz_uint flags); +MINIZ_EXPORT mz_zip_reader_extract_iter_state* mz_zip_reader_extract_file_iter_new(mz_zip_archive *pZip, const char *pFilename, mz_uint flags); +MINIZ_EXPORT size_t mz_zip_reader_extract_iter_read(mz_zip_reader_extract_iter_state* pState, void* pvBuf, size_t buf_size); +MINIZ_EXPORT mz_bool mz_zip_reader_extract_iter_free(mz_zip_reader_extract_iter_state* pState); + +#ifndef MINIZ_NO_STDIO +/* Extracts a archive file to a disk file and sets its last accessed and modified times. */ +/* This function only extracts files, not archive directory records. */ +MINIZ_EXPORT mz_bool mz_zip_reader_extract_to_file(mz_zip_archive *pZip, mz_uint file_index, const char *pDst_filename, mz_uint flags); +MINIZ_EXPORT mz_bool mz_zip_reader_extract_file_to_file(mz_zip_archive *pZip, const char *pArchive_filename, const char *pDst_filename, mz_uint flags); + +/* Extracts a archive file starting at the current position in the destination FILE stream. */ +MINIZ_EXPORT mz_bool mz_zip_reader_extract_to_cfile(mz_zip_archive *pZip, mz_uint file_index, MZ_FILE *File, mz_uint flags); +MINIZ_EXPORT mz_bool mz_zip_reader_extract_file_to_cfile(mz_zip_archive *pZip, const char *pArchive_filename, MZ_FILE *pFile, mz_uint flags); #endif - rgba[3]=(rgba[3]*global_alpha_u8+255)>>8; - ctx_RGBA8_associate_alpha (rgba); - return; - } - else - { - color = & (g->stops[g->n_stops-1].color); - } - ctx_color_get_rgba8 (rasterizer->state, color, rgba); - if (rasterizer->swap_red_green) - { - uint8_t tmp = rgba[0]; - rgba[0] = rgba[2]; - rgba[2] = tmp; - } - rgba[3]=(rgba[3]*global_alpha_u8+255)>>8; - ctx_RGBA8_associate_alpha (rgba); -} -#if CTX_GRADIENT_CACHE -static void -ctx_gradient_cache_prime (CtxRasterizer *rasterizer); +#if 0 +/* TODO */ + typedef void *mz_zip_streaming_extract_state_ptr; + mz_zip_streaming_extract_state_ptr mz_zip_streaming_extract_begin(mz_zip_archive *pZip, mz_uint file_index, mz_uint flags); + mz_uint64 mz_zip_streaming_extract_get_size(mz_zip_archive *pZip, mz_zip_streaming_extract_state_ptr pState); + mz_uint64 mz_zip_streaming_extract_get_cur_ofs(mz_zip_archive *pZip, mz_zip_streaming_extract_state_ptr pState); + mz_bool mz_zip_streaming_extract_seek(mz_zip_archive *pZip, mz_zip_streaming_extract_state_ptr pState, mz_uint64 new_ofs); + size_t mz_zip_streaming_extract_read(mz_zip_archive *pZip, mz_zip_streaming_extract_state_ptr pState, void *pBuf, size_t buf_size); + mz_bool mz_zip_streaming_extract_end(mz_zip_archive *pZip, mz_zip_streaming_extract_state_ptr pState); #endif -CTX_INLINE static void -ctx_fragment_gradient_1d_RGBA8 (CtxRasterizer *rasterizer, float x, float y, uint8_t *rgba) -{ -#if CTX_GRADIENT_CACHE - *((uint32_t*)rgba) = *((uint32_t*)(&rasterizer->gradient_cache_u8[ctx_grad_index(rasterizer, x)][0])); -#else - _ctx_fragment_gradient_1d_RGBA8 (rasterizer, x, y, rgba); +/* This function compares the archive's local headers, the optional local zip64 extended information block, and the optional descriptor following the compressed data vs. the data in the central directory. */ +/* It also validates that each file can be successfully uncompressed unless the MZ_ZIP_FLAG_VALIDATE_HEADERS_ONLY is specified. */ +MINIZ_EXPORT mz_bool mz_zip_validate_file(mz_zip_archive *pZip, mz_uint file_index, mz_uint flags); + +/* Validates an entire archive by calling mz_zip_validate_file() on each file. */ +MINIZ_EXPORT mz_bool mz_zip_validate_archive(mz_zip_archive *pZip, mz_uint flags); + +/* Misc utils/helpers, valid for ZIP reading or writing */ +MINIZ_EXPORT mz_bool mz_zip_validate_mem_archive(const void *pMem, size_t size, mz_uint flags, mz_zip_error *pErr); +#ifndef MINIZ_NO_STDIO +MINIZ_EXPORT mz_bool mz_zip_validate_file_archive(const char *pFilename, mz_uint flags, mz_zip_error *pErr); #endif -} + +/* Universal end function - calls either mz_zip_reader_end() or mz_zip_writer_end(). */ +MINIZ_EXPORT mz_bool mz_zip_end(mz_zip_archive *pZip); + +/* -------- ZIP writing */ + +#ifndef MINIZ_NO_ARCHIVE_WRITING_APIS + +/* Inits a ZIP archive writer. */ +/*Set pZip->m_pWrite (and pZip->m_pIO_opaque) before calling mz_zip_writer_init or mz_zip_writer_init_v2*/ +/*The output is streamable, i.e. file_ofs in mz_file_write_func always increases only by n*/ +MINIZ_EXPORT mz_bool mz_zip_writer_init(mz_zip_archive *pZip, mz_uint64 existing_size); +MINIZ_EXPORT mz_bool mz_zip_writer_init_v2(mz_zip_archive *pZip, mz_uint64 existing_size, mz_uint flags); + +MINIZ_EXPORT mz_bool mz_zip_writer_init_heap(mz_zip_archive *pZip, size_t size_to_reserve_at_beginning, size_t initial_allocation_size); +MINIZ_EXPORT mz_bool mz_zip_writer_init_heap_v2(mz_zip_archive *pZip, size_t size_to_reserve_at_beginning, size_t initial_allocation_size, mz_uint flags); + +#ifndef MINIZ_NO_STDIO +MINIZ_EXPORT mz_bool mz_zip_writer_init_file(mz_zip_archive *pZip, const char *pFilename, mz_uint64 size_to_reserve_at_beginning); +MINIZ_EXPORT mz_bool mz_zip_writer_init_file_v2(mz_zip_archive *pZip, const char *pFilename, mz_uint64 size_to_reserve_at_beginning, mz_uint flags); +MINIZ_EXPORT mz_bool mz_zip_writer_init_cfile(mz_zip_archive *pZip, MZ_FILE *pFile, mz_uint flags); #endif -CTX_INLINE static void -ctx_u8_associate_alpha (int components, uint8_t *u8) -{ - for (int c = 0; c < components-1; c++) - u8[c] = (u8[c] * u8[components-1] + 255)>>8; -} +/* Converts a ZIP archive reader object into a writer object, to allow efficient in-place file appends to occur on an existing archive. */ +/* For archives opened using mz_zip_reader_init_file, pFilename must be the archive's filename so it can be reopened for writing. If the file can't be reopened, mz_zip_reader_end() will be called. */ +/* For archives opened using mz_zip_reader_init_mem, the memory block must be growable using the realloc callback (which defaults to realloc unless you've overridden it). */ +/* Finally, for archives opened using mz_zip_reader_init, the mz_zip_archive's user provided m_pWrite function cannot be NULL. */ +/* Note: In-place archive modification is not recommended unless you know what you're doing, because if execution stops or something goes wrong before */ +/* the archive is finalized the file's central directory will be hosed. */ +MINIZ_EXPORT mz_bool mz_zip_writer_init_from_reader(mz_zip_archive *pZip, const char *pFilename); +MINIZ_EXPORT mz_bool mz_zip_writer_init_from_reader_v2(mz_zip_archive *pZip, const char *pFilename, mz_uint flags); -#if CTX_GRADIENTS -#if CTX_GRADIENT_CACHE -static void -ctx_gradient_cache_prime (CtxRasterizer *rasterizer) -{ - // XXX : todo make the number of element dynamic depending on length of gradient - // in device coordinates. +/* Adds the contents of a memory buffer to an archive. These functions record the current local time into the archive. */ +/* To add a directory entry, call this method with an archive name ending in a forwardslash with an empty buffer. */ +/* level_and_flags - compression level (0-10, see MZ_BEST_SPEED, MZ_BEST_COMPRESSION, etc.) logically OR'd with zero or more mz_zip_flags, or just set to MZ_DEFAULT_COMPRESSION. */ +MINIZ_EXPORT mz_bool mz_zip_writer_add_mem(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, mz_uint level_and_flags); - if (rasterizer->gradient_cache_valid) - return; - +/* Like mz_zip_writer_add_mem(), except you can specify a file comment field, and optionally supply the function with already compressed data. */ +/* uncomp_size/uncomp_crc32 are only used if the MZ_ZIP_FLAG_COMPRESSED_DATA flag is specified. */ +MINIZ_EXPORT mz_bool mz_zip_writer_add_mem_ex(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, + mz_uint64 uncomp_size, mz_uint32 uncomp_crc32); - { - 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) - { - length = ctx_maxf (source->radial_gradient.r1, source->radial_gradient.r0); - } - // length = CTX_GRADIENT_CACHE_ELEMENTS; - { - float u = length; float v = length; - const CtxMatrix *m = &rasterizer->state->gstate.transform; - //CtxMatrix *transform = &source->transform; - // - // combine with above source transform? - _ctx_matrix_apply_transform (m, &u, &v); - length = ctx_maxf (u, v); - } - - rasterizer->gradient_cache_elements = ctx_mini ((int)length, CTX_GRADIENT_CACHE_ELEMENTS); - } +MINIZ_EXPORT mz_bool mz_zip_writer_add_mem_ex_v2(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, + mz_uint64 uncomp_size, mz_uint32 uncomp_crc32, MZ_TIME_T *last_modified, const char *user_extra_data_local, mz_uint user_extra_data_local_len, + const char *user_extra_data_central, mz_uint user_extra_data_central_len); - for (int u = 0; u < rasterizer->gradient_cache_elements; u++) - { - float v = u / (rasterizer->gradient_cache_elements - 1.0f); - _ctx_fragment_gradient_1d_RGBA8 (rasterizer, v, 0.0f, &rasterizer->gradient_cache_u8[u][0]); - //*((uint32_t*)(&rasterizer->gradient_cache_u8_a[u][0]))= *((uint32_t*)(&rasterizer->gradient_cache_u8[u][0])); - //memcpy(&rasterizer->gradient_cache_u8_a[u][0], &rasterizer->gradient_cache_u8[u][0], 4); - //ctx_RGBA8_associate_alpha (&rasterizer->gradient_cache_u8_a[u][0]); - } - rasterizer->gradient_cache_valid = 1; -} +/* Adds the contents of a file to an archive. This function also records the disk file's modified time into the archive. */ +/* File data is supplied via a read callback function. User mz_zip_writer_add_(c)file to add a file directly.*/ +MINIZ_EXPORT mz_bool mz_zip_writer_add_read_buf_callback(mz_zip_archive *pZip, const char *pArchive_name, mz_file_read_func read_callback, void* callback_opaque, mz_uint64 max_size, + const MZ_TIME_T *pFile_time, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, const char *user_extra_data_local, mz_uint user_extra_data_local_len, + const char *user_extra_data_central, mz_uint user_extra_data_central_len); + + +#ifndef MINIZ_NO_STDIO +/* Adds the contents of a disk file to an archive. This function also records the disk file's modified time into the archive. */ +/* level_and_flags - compression level (0-10, see MZ_BEST_SPEED, MZ_BEST_COMPRESSION, etc.) logically OR'd with zero or more mz_zip_flags, or just set to MZ_DEFAULT_COMPRESSION. */ +MINIZ_EXPORT mz_bool mz_zip_writer_add_file(mz_zip_archive *pZip, const char *pArchive_name, const char *pSrc_filename, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags); + +/* Like mz_zip_writer_add_file(), except the file data is read from the specified FILE stream. */ +MINIZ_EXPORT mz_bool mz_zip_writer_add_cfile(mz_zip_archive *pZip, const char *pArchive_name, MZ_FILE *pSrc_file, mz_uint64 max_size, + const MZ_TIME_T *pFile_time, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, const char *user_extra_data_local, mz_uint user_extra_data_local_len, + const char *user_extra_data_central, mz_uint user_extra_data_central_len); #endif -CTX_INLINE static void -ctx_fragment_gradient_1d_GRAYA8 (CtxRasterizer *rasterizer, float x, float y, uint8_t *rgba) -{ - float v = x; - CtxGradient *g = &rasterizer->state->gradient; - if (v < 0) { v = 0; } - if (v > 1) { v = 1; } - if (g->n_stops == 0) - { - rgba[0] = rgba[1] = rgba[2] = (int)(v * 255); - rgba[1] = 255; - return; - } - CtxGradientStop *stop = NULL; - CtxGradientStop *next_stop = &g->stops[0]; - CtxColor *color; - for (int s = 0; s < g->n_stops; s++) - { - stop = &g->stops[s]; - next_stop = &g->stops[s+1]; - if (s + 1 >= g->n_stops) { next_stop = NULL; } - if (v >= stop->pos && next_stop && v < next_stop->pos) - { break; } - stop = NULL; - next_stop = NULL; - } - if (stop == NULL && next_stop) - { - color = & (next_stop->color); - } - else if (stop && next_stop == NULL) - { - color = & (stop->color); - } - else if (stop && next_stop) - { - uint8_t stop_rgba[4]; - uint8_t next_rgba[4]; - ctx_color_get_graya_u8 (rasterizer->state, & (stop->color), stop_rgba); - ctx_color_get_graya_u8 (rasterizer->state, & (next_stop->color), next_rgba); - int dx = (int)((v - stop->pos) * 255 / (next_stop->pos - stop->pos)); - for (int c = 0; c < 2; c++) - { rgba[c] = ctx_lerp_u8 (stop_rgba[c], next_rgba[c], dx); } - return; - } - else - { - color = & (g->stops[g->n_stops-1].color); - } - ctx_color_get_graya_u8 (rasterizer->state, color, rgba); +/* Adds a file to an archive by fully cloning the data from another archive. */ +/* This function fully clones the source file's compressed data (no recompression), along with its full filename, extra data (it may add or modify the zip64 local header extra data field), and the optional descriptor following the compressed data. */ +MINIZ_EXPORT mz_bool mz_zip_writer_add_from_zip_reader(mz_zip_archive *pZip, mz_zip_archive *pSource_zip, mz_uint src_file_index); + +/* Finalizes the archive by writing the central directory records followed by the end of central directory record. */ +/* After an archive is finalized, the only valid call on the mz_zip_archive struct is mz_zip_writer_end(). */ +/* An archive must be manually finalized by calling this function for it to be valid. */ +MINIZ_EXPORT mz_bool mz_zip_writer_finalize_archive(mz_zip_archive *pZip); + +/* Finalizes a heap archive, returning a pointer to the heap block and its size. */ +/* The heap block will be allocated using the mz_zip_archive's alloc/realloc callbacks. */ +MINIZ_EXPORT mz_bool mz_zip_writer_finalize_heap_archive(mz_zip_archive *pZip, void **ppBuf, size_t *pSize); + +/* Ends archive writing, freeing all allocations, and closing the output file if mz_zip_writer_init_file() was used. */ +/* Note for the archive to be valid, it *must* have been finalized before ending (this function will not do it for you). */ +MINIZ_EXPORT mz_bool mz_zip_writer_end(mz_zip_archive *pZip); + +/* -------- Misc. high-level helper functions: */ + +/* mz_zip_add_mem_to_archive_file_in_place() efficiently (but not atomically) appends a memory blob to a ZIP archive. */ +/* Note this is NOT a fully safe operation. If it crashes or dies in some way your archive can be left in a screwed up state (without a central directory). */ +/* level_and_flags - compression level (0-10, see MZ_BEST_SPEED, MZ_BEST_COMPRESSION, etc.) logically OR'd with zero or more mz_zip_flags, or just set to MZ_DEFAULT_COMPRESSION. */ +/* TODO: Perhaps add an option to leave the existing central dir in place in case the add dies? We could then truncate the file (so the old central dir would be at the end) if something goes wrong. */ +MINIZ_EXPORT mz_bool mz_zip_add_mem_to_archive_file_in_place(const char *pZip_filename, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags); +MINIZ_EXPORT mz_bool mz_zip_add_mem_to_archive_file_in_place_v2(const char *pZip_filename, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, mz_zip_error *pErr); + +#ifndef MINIZ_NO_STDIO +/* Reads a single file from an archive into a heap block. */ +/* If pComment is not NULL, only the file with the specified comment will be extracted. */ +/* Returns NULL on failure. */ +MINIZ_EXPORT void *mz_zip_extract_archive_file_to_heap(const char *pZip_filename, const char *pArchive_name, size_t *pSize, mz_uint flags); +MINIZ_EXPORT void *mz_zip_extract_archive_file_to_heap_v2(const char *pZip_filename, const char *pArchive_name, const char *pComment, size_t *pSize, mz_uint flags, mz_zip_error *pErr); +#endif + +#endif /* #ifndef MINIZ_NO_ARCHIVE_WRITING_APIS */ + +#ifdef __cplusplus } +#endif -CTX_INLINE static void -ctx_fragment_gradient_1d_RGBAF (CtxRasterizer *rasterizer, float v, float y, float *rgba) -{ - float global_alpha = rasterizer->state->gstate.global_alpha_f; - CtxGradient *g = &rasterizer->state->gradient; - if (v < 0) { v = 0; } - if (v > 1) { v = 1; } - if (g->n_stops == 0) - { - rgba[0] = rgba[1] = rgba[2] = v; - rgba[3] = 1.0; - return; - } - CtxGradientStop *stop = NULL; - CtxGradientStop *next_stop = &g->stops[0]; - CtxColor *color; - for (int s = 0; s < g->n_stops; s++) - { - stop = &g->stops[s]; - next_stop = &g->stops[s+1]; - if (s + 1 >= g->n_stops) { next_stop = NULL; } - if (v >= stop->pos && next_stop && v < next_stop->pos) - { break; } - stop = NULL; - next_stop = NULL; - } - if (stop == NULL && next_stop) - { - color = & (next_stop->color); - } - else if (stop && next_stop == NULL) - { - color = & (stop->color); - } - else if (stop && next_stop) - { - float stop_rgba[4]; - float next_rgba[4]; - ctx_color_get_rgba (rasterizer->state, & (stop->color), stop_rgba); - ctx_color_get_rgba (rasterizer->state, & (next_stop->color), next_rgba); - int dx = (int)((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; - return; - } - else - { - color = & (g->stops[g->n_stops-1].color); - } - ctx_color_get_rgba (rasterizer->state, color, rgba); - rgba[3] *= global_alpha; -} -#endif +#endif /* MINIZ_NO_ARCHIVE_APIS */ +#ifndef __CTX_CLIENTS_H +#define __CTX_CLIENTS_H -static void -ctx_fragment_image_RGBA8 (CtxRasterizer *rasterizer, float x, float y, float z, void *out, int count, float dx, float dy, float dw) -{ - uint8_t *rgba = (uint8_t *) out; - CtxSource *g = &rasterizer->state->gstate.source_fill; - CtxBuffer *buffer = g->texture.buffer->color_managed?g->texture.buffer->color_managed:g->texture.buffer; - uint8_t global_alpha_u8 = rasterizer->state->gstate.global_alpha_u8; - 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) - { - *((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; +struct _CtxClient { + VT *vt; // or NULL when thread - 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); - - } - 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); - - } - rgba[3] = (rgba[3] * global_alpha_u8) / 255; - } + long rev; - } - else - { - uint8_t *src = (uint8_t *) buffer->data; - src += v * buffer->stride + u * bpp; - switch (bpp) - { - case 1: - for (int c = 0; c < 3; c++) - { rgba[c] = src[0]; } - rgba[3] = global_alpha_u8; - break; - case 2: - for (int c = 0; c < 3; c++) - { rgba[c] = src[0]; } - rgba[3] = src[1]; - rgba[3] = (rgba[3] * global_alpha_u8) / 255; - break; - case 3: - for (int c = 0; c < 3; c++) - { rgba[c] = src[c]; } - 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; - break; - } + CtxList *events; // we could use this queue also for vt - } - if (rasterizer->swap_red_green) - { - uint8_t tmp = rgba[0]; - rgba[0] = rgba[2]; - rgba[2] = tmp; - } - } - ctx_RGBA8_associate_alpha_probably_opaque (rgba); - rgba += 4; - x += dx; - y += dy; - } -} + Ctx *ctx; + char *title; + int x; + int y; + int width; + int height; + float opacity; + CtxClientFlags flags; +#if 0 + int shaded; + int iconified; + int maximized; + int resizable; +#endif + int unmaximized_x; + int unmaximized_y; + int unmaximized_width; + int unmaximized_height; + int do_quit; + long drawn_rev; + int id; + int internal; // render a settings window rather than a vt -#if CTX_DITHER -static inline int ctx_dither_mask_a (int x, int y, int c, int divisor) -{ - /* https://pippin.gimp.org/a_dither/ */ - return ( ( ( ( (x + c * 67) + y * 236) * 119) & 255 )-127) / divisor; -} +#if CTX_THREADS + thrd_t tid; // and only split code path in processing? + // -- why? +#endif + void (*start_routine)(Ctx *ctx, void *user_data); + void *user_data; + CtxClientFinalize finalize; + Ctx *sub_ctx; + CtxList *ctx_events; -inline static void -ctx_dither_rgba_u8 (uint8_t *rgba, int x, int y, int dither_red_blue, int dither_green) -{ - if (dither_red_blue == 0) - { return; } - for (int c = 0; c < 3; c ++) - { - int val = rgba[c] + ctx_dither_mask_a (x, y, 0, c==1?dither_green:dither_red_blue); - rgba[c] = CTX_CLAMP (val, 0, 255); - } -} -inline static void -ctx_dither_graya_u8 (uint8_t *rgba, int x, int y, int dither_red_blue, int dither_green) -{ - if (dither_red_blue == 0) - { return; } - for (int c = 0; c < 1; c ++) - { - int val = rgba[c] + ctx_dither_mask_a (x, y, 0, dither_red_blue); - rgba[c] = CTX_CLAMP (val, 0, 255); - } -} + /* we want to keep variation at the end */ +#if CTX_THREADS + mtx_t mtx; +#endif +#if VT_RECORD + Ctx *recording; #endif +}; + + -#if 0 -CTX_INLINE static void -ctx_RGBA8_deassociate_alpha (const uint8_t *in, uint8_t *out) -{ - uint32_t val = *((uint32_t*)(in)); - int a = val >> CTX_RGBA8_A_SHIFT; - if (a) - { - if (a ==255) - { - *((uint32_t*)(out)) = val; - } else - { - uint32_t g = (((val & CTX_RGBA8_G_MASK) * 255 / a) >> 8) & CTX_RGBA8_G_MASK; - uint32_t rb =(((val & CTX_RGBA8_RB_MASK) * 255 / a) >> 8) & CTX_RGBA8_RB_MASK; - *((uint32_t*)(out)) = g|rb|(a << CTX_RGBA8_A_SHIFT); - } - } - else - { - *((uint32_t*)(out)) = 0; - } -} #endif -CTX_INLINE static void -ctx_u8_deassociate_alpha (int components, const uint8_t *in, uint8_t *out) -{ - if (in[components-1]) - { - if (in[components-1] != 255) - for (int c = 0; c < components-1; c++) - out[c] = (in[c] * 255) / in[components-1]; - else - for (int c = 0; c < components-1; c++) - out[c] = in[c]; - out[components-1] = in[components-1]; - } - else - { - for (int c = 0; c < components; c++) - out[c] = 0; - } -} +#if CTX_IMPLEMENTATION|CTX_COMPOSITE -CTX_INLINE static void -ctx_float_associate_alpha (int components, float *rgba) -{ - float alpha = rgba[components-1]; - for (int c = 0; c < components-1; c++) - rgba[c] *= alpha; -} +#ifndef __CTX_INTERNAL_H +#define __CTX_INTERNAL_H -CTX_INLINE static void -ctx_float_deassociate_alpha (int components, float *rgba, float *dst) -{ - float ralpha = rgba[components-1]; - if (ralpha != 0.0f) ralpha = 1.0f/ralpha; +#if !__COSMOPOLITAN__ +#include <stdlib.h> +#include <stdio.h> +#include <unistd.h> +#include <math.h> +#endif - for (int c = 0; c < components-1; c++) - dst[c] = (rgba[c] * ralpha); - dst[components-1] = rgba[components-1]; -} -CTX_INLINE static void -ctx_RGBAF_associate_alpha (float *rgba) -{ - ctx_float_associate_alpha (4, rgba); -} +#if CTX_BRANCH_HINTS +#define CTX_LIKELY(x) __builtin_expect(!!(x), 1) +#define CTX_UNLIKELY(x) __builtin_expect(!!(x), 0) +#else +#define CTX_LIKELY(x) (x) +#define CTX_UNLIKELY(x) (x) +#endif -CTX_INLINE static void -ctx_RGBAF_deassociate_alpha (float *rgba, float *dst) -{ - ctx_float_deassociate_alpha (4, rgba, dst); -} +typedef struct _CtxRasterizer CtxRasterizer; +typedef struct _CtxGState CtxGState; +//typedef struct _CtxState CtxState; +typedef struct _CtxSource CtxSource; -static inline void ctx_swap_red_green_u8 (void *data) -{ - uint8_t *rgba = (uint8_t*)data; - uint8_t tmp = rgba[0]; - rgba[0] = rgba[2]; - rgba[2] = tmp; -} -static void -ctx_fragment_swap_red_green_u8 (void *out, int count) +enum _CtxAntialias { - uint8_t *rgba = (uint8_t*)out; - for (int x = 0; x < count; x++) - { - ctx_swap_red_green_u8 (rgba); - rgba += 4; - } -} + 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); -/**** rgb8 ***/ +#define CTX_VALID_RGBA_U8 (1<<0) +#define CTX_VALID_RGBA_DEVICE (1<<1) +#if CTX_ENABLE_CM +#define CTX_VALID_RGBA (1<<2) +#endif +#if CTX_ENABLE_CMYK +#define CTX_VALID_CMYKA (1<<3) +#define CTX_VALID_DCMYKA (1<<4) +#endif +#define CTX_VALID_GRAYA (1<<5) +#define CTX_VALID_GRAYA_U8 (1<<6) +#define CTX_VALID_LABA ((1<<7) | CTX_VALID_GRAYA) -static void -ctx_fragment_image_rgb8_RGBA8_box (CtxRasterizer *rasterizer, - float x, float y, float z, - void *out, int count, float dx, float dy, float dz) +struct _CtxColor { - uint8_t *rgba = (uint8_t *) out; - CtxSource *g = &rasterizer->state->gstate.source_fill; - CtxBuffer *buffer = g->texture.buffer->color_managed?g->texture.buffer->color_managed:g->texture.buffer; - int width = buffer->width; - int height = buffer->height; - uint8_t global_alpha_u8 = rasterizer->state->gstate.global_alpha_u8; - float factor = ctx_matrix_get_scale (&rasterizer->state->gstate.transform); - int dim = (int)((1.0f / factor) / 3); + uint8_t magic; // for colors used in keydb, set to a non valid start of + // string value. + uint8_t rgba[4]; + uint8_t l_u8; + uint8_t original; // the bitmask of the originally set color + uint8_t valid; // bitmask of which members contain valid + // values, gets denser populated as more + // formats are requested from a set color. + float device_red; + float device_green; + float device_blue; + float alpha; + float l; // luminance and gray +#if CTX_ENABLE_LAB // NYI + float a; + float b; +#endif +#if CTX_ENABLE_CMYK + float device_cyan; + float device_magenta; + float device_yellow; + float device_key; + float cyan; + float magenta; + float yellow; + float key; +#endif - int i = 0; +#if CTX_ENABLE_CM + float red; + float green; + float blue; +#if CTX_BABL + const Babl *space; // gets copied from state when color is declared +#else + void *space; // gets copied from state when color is declared, +#endif +#endif +}; - for (; i < count && (x - dim< 0 || y - dim < 0 || x + dim >= height || y + dim >= height); i++) - { - *((uint32_t*)(rgba))=0; - rgba += 4; - x += dx; - y += dy; - } +typedef struct _CtxGradientStop CtxGradientStop; - for (; i < count && !( - x - dim < 0 || y - dim < 0 || - x + dim >= width || - y + dim >= height); i++) - { +struct _CtxGradientStop +{ + CtxColor color; + float pos; +}; - int u = (int)x; - int v = (int)y; - { - int bpp = 3; - rgba[3]=global_alpha_u8; // gets lost - uint64_t sum[4]={0,0,0,0}; - int count = 0; - { - for (int ov = - dim; ov <= dim; ov++) - { - uint8_t *src = (uint8_t *) buffer->data + bpp * ((v+ov) * width + (u - dim)); - for (int ou = - dim; ou <= dim; ou++) - { - for (int c = 0; c < bpp; c++) - sum[c] += src[c]; - count ++; - src += bpp; - } +enum _CtxSourceType +{ + CTX_SOURCE_COLOR = 0, + CTX_SOURCE_TEXTURE, + CTX_SOURCE_LINEAR_GRADIENT, + CTX_SOURCE_RADIAL_GRADIENT, + CTX_SOURCE_INHERIT_FILL +}; - } - } +typedef enum _CtxSourceType CtxSourceType; - int recip = 65536/count; - for (int c = 0; c < bpp; c++) - rgba[c] = sum[c] * recip >> 16; - ctx_RGBA8_associate_alpha_probably_opaque (rgba); - } - rgba += 4; - x += dx; - y += dy; - } +typedef struct _CtxPixelFormatInfo CtxPixelFormatInfo; - for (; i < count; i++) - { - *((uint32_t*)(rgba))= 0; - rgba += 4; - } -} +struct _CtxBuffer +{ + void *data; + int width; + int height; + int stride; + 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; + void (*free_func) (void *pixels, void *user_data); + void *user_data; -#define CTX_DECLARE_SWAP_RED_GREEN_FRAGMENT(frag) \ -static void \ -frag##_swap_red_green (CtxRasterizer *rasterizer,\ - float x, float y, float z,\ - void *out, int count, float dx, float dy, float dz)\ -{\ - frag (rasterizer, x, y, z, out, count, dx, dy, dz);\ - ctx_fragment_swap_red_green_u8 (out, count);\ -} +#if CTX_ENABLE_CM +#if CTX_BABL + const Babl *space; +#else + void *space; +#endif +#endif +#if 1 + CtxBuffer *color_managed; /* only valid for one render target, cache + for a specific space + */ +#endif +}; +//void _ctx_user_to_device (CtxState *state, float *x, float *y); +//void _ctx_user_to_device_distance (CtxState *state, float *x, float *y); -static inline void -ctx_RGBA8_apply_global_alpha_and_associate (CtxRasterizer *rasterizer, - uint8_t *buf, int count) + +typedef struct _CtxGradient CtxGradient; +struct _CtxGradient { - uint8_t global_alpha_u8 = rasterizer->state->gstate.global_alpha_u8; - uint8_t *rgba = (uint8_t *) buf; - if (global_alpha_u8 != 255) + CtxGradientStop stops[CTX_MAX_GRADIENT_STOPS]; + int n_stops; +}; + +struct _CtxSource +{ + int type; + CtxMatrix set_transform; + CtxMatrix transform; + uint32_t pad; + union { - for (int i = 0; i < count; i++) + CtxColor color; + struct { - ctx_RGBA8_associate_global_alpha (rgba, global_alpha_u8); - rgba += 4; - } - } - else - { - for (int i = 0; i < count; i++) + uint8_t rgba[4]; // shares data with set color + uint8_t pad; + CtxBuffer *buffer; + } texture; + struct { - ctx_RGBA8_associate_alpha_probably_opaque (rgba); - rgba += 4; - } - } -} + float x0; + float y0; + float x1; + float y1; + float dx; + float dy; + float start; + float end; + float length; + float rdelta; + } linear_gradient; + struct + { + float x0; + float y0; + float r0; + float x1; + float y1; + float r1; + float rdelta; + } radial_gradient; + }; +}; -#if CTX_FRAGMENT_SPECIALIZE -static void -ctx_fragment_image_rgb8_RGBA8_nearest (CtxRasterizer *rasterizer, - float x, float y, float z, - void *out, int scount, - float dx, float dy, float dz); -static inline void -ctx_fragment_image_rgb8_RGBA8_bi (CtxRasterizer *rasterizer, - float x, float y, float z, - void *out, int scount, - float dx, float dy, float dz) +typedef struct _Ctx16f16Matrix Ctx16f16Matrix; +struct + _Ctx16f16Matrix { - ctx_fragment_image_rgb8_RGBA8_nearest (rasterizer, - x, y, z, - out, scount, - dx, dy, dz); - return; -} +#if CTX_32BIT_SEGMENTS + int64_t m[3][3]; // forcing higher precision easily, the extra + // memory cost is minuscle +#else + int32_t m[3][3]; +#endif +}; -static void -ctx_fragment_image_rgb8_RGBA8_nearest (CtxRasterizer *rasterizer, - float x, float y, float z, - void *out, int scount, - float dx, float dy, float dz) + +struct _CtxGState { - unsigned int 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; - CtxBuffer *buffer = g->texture.buffer->color_managed?g->texture.buffer->color_managed:g->texture.buffer; - const int bwidth = buffer->width; - const int bheight = buffer->height; - unsigned int i = 0; - uint8_t *data = ((uint8_t*)buffer->data); +#if CTX_32BIT_SEGMENTS + uint32_t keydb_pos; + uint32_t stringpool_pos; +#else + uint16_t keydb_pos; // this limits these + uint16_t stringpool_pos; // +#endif - int yi_delta = (int)(dy * 65536); - int xi_delta = (int)(dx * 65536); - int zi_delta = (int)(dz * 65536); - int32_t yi = (int)(y * 65536); - int32_t xi = (int)(x * 65536); - int32_t zi = (int)(z * 65536); - { - int32_t u1 = xi + xi_delta* (count-1); - int32_t v1 = yi + yi_delta* (count-1); - int32_t z1 = zi + zi_delta* (count-1); - uint32_t *edst = ((uint32_t*)out)+(count-1); - 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) - { - *edst-- = 0; - count --; - u1 -= xi_delta; - v1 -= yi_delta; - z1 -= zi_delta; - } - else break; - } - } + CtxMatrix transform; + Ctx16f16Matrix prepped_transform; + CtxSource source_stroke; + CtxSource source_fill; + float global_alpha_f; - for (i= 0; i < count; i ++) - { - 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) - { - *((uint32_t*)(rgba))= 0; - } - else - break; - xi += xi_delta; - yi += yi_delta; - zi += zi_delta; - rgba += 4; - } + float line_width; + float line_dash_offset; + float miter_limit; + float font_size; +#if CTX_ENABLE_SHADOW_BLUR + float shadow_blur; + float shadow_offset_x; + float shadow_offset_y; +#endif + unsigned int transform_type:3; + unsigned int clipped:1; + CtxColorModel color_model:8; + /* bitfield-pack small state-parts */ + CtxLineCap line_cap:2; + CtxLineJoin line_join:2; + CtxFillRule fill_rule:1; + unsigned int image_smoothing:1; + unsigned int font:6; + unsigned int bold:1; + unsigned int italic:1; - while (i < count) - { - float z_recip = (zi!=0) * (1.0f/zi); - int u = (int)(xi * z_recip); - int v = (int)(yi * z_recip); - 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); - xi += xi_delta; - yi += yi_delta; - zi += zi_delta; - rgba += 4; - i++; - } -} + uint8_t global_alpha_u8; + int16_t clip_min_x; + int16_t clip_min_y; + int16_t clip_max_x; + int16_t clip_max_y; + int n_dashes; +#if CTX_ENABLE_CM +#if CTX_BABL + const Babl *device_space; + const Babl *texture_space; + const Babl *rgb_space; + const Babl *cmyk_space; + const Babl *fish_rgbaf_user_to_device; + const Babl *fish_rgbaf_texture_to_device; + const Babl *fish_rgbaf_device_to_user; -CTX_DECLARE_SWAP_RED_GREEN_FRAGMENT(ctx_fragment_image_rgb8_RGBA8_box) -CTX_DECLARE_SWAP_RED_GREEN_FRAGMENT(ctx_fragment_image_rgb8_RGBA8_bi) -CTX_DECLARE_SWAP_RED_GREEN_FRAGMENT(ctx_fragment_image_rgb8_RGBA8_nearest) +#else + void *device_space; + void *texture_space; + void *rgb_space; + void *cmyk_space; + void *fish_rgbaf_user_to_device; // dummy padding + void *fish_rgbaf_texture_to_device; // dummy padding + void *fish_rgbaf_device_to_user; // dummy padding +#endif +#endif + CtxCompositingMode compositing_mode; // bitfield refs lead to + CtxBlend blend_mode; // non-vectorization + CtxExtend extend; + float dashes[CTX_MAX_DASHES]; // XXX moving dashes + // to state storage,. will + // allow it to be larger, + // free up memory, and + // make save/restore faster +}; -static inline void -ctx_fragment_image_rgb8_RGBA8 (CtxRasterizer *rasterizer, - float x, - float y, - float z, - void *out, int count, float dx, float dy, float dz) +typedef enum { - if (rasterizer->swap_red_green) - { - if (rasterizer->state->gstate.image_smoothing) - { - float factor = ctx_matrix_get_scale (&rasterizer->state->gstate.transform); - 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) - ctx_fragment_image_rgb8_RGBA8_nearest_swap_red_green (rasterizer,x,y,z, - out,count,dx,dy,dz); - #endif - else - ctx_fragment_image_rgb8_RGBA8_bi_swap_red_green (rasterizer,x,y,z, - out,count, dx, dy, dz); - } - else - { - ctx_fragment_image_rgb8_RGBA8_nearest_swap_red_green (rasterizer,x,y,z, - out,count,dx,dy,dz); - } - } - else - { - if (rasterizer->state->gstate.image_smoothing) - { - float factor = ctx_matrix_get_scale (&rasterizer->state->gstate.transform); - if (factor <= 0.50f) - 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) - ctx_fragment_image_rgb8_RGBA8_nearest (rasterizer, x, y, z, out, count, dx, dy, dz); - #endif - else - ctx_fragment_image_rgb8_RGBA8_bi (rasterizer,x,y,z,out,count,dx,dy,dz); - } - else - { - ctx_fragment_image_rgb8_RGBA8_nearest (rasterizer,x,y,z,out, - count,dx,dy, dz); - } - } -} - + CTX_TRANSFORMATION_NONE = 0, + CTX_TRANSFORMATION_SCREEN_SPACE = 1, + CTX_TRANSFORMATION_RELATIVE = 2, +#if CTX_BITPACK + CTX_TRANSFORMATION_BITPACK = 4, +#endif + CTX_TRANSFORMATION_STORE_CLEAR = 16, +} CtxTransformation; -/************** rgba8 */ +#define CTX_DRAWLIST_DOESNT_OWN_ENTRIES 64 +#define CTX_DRAWLIST_EDGE_LIST 128 +#define CTX_DRAWLIST_CURRENT_PATH 512 +// BITPACK -static void -ctx_fragment_image_rgba8_RGBA8_box (CtxRasterizer *rasterizer, - float x, float y, float z, - void *out, int count, float dx, float dy, float dz) +struct _CtxDrawlist { - uint8_t *rgba = (uint8_t *) out; - CtxSource *g = &rasterizer->state->gstate.source_fill; - CtxBuffer *buffer = g->texture.buffer->color_managed?g->texture.buffer->color_managed:g->texture.buffer; - int width = buffer->width; - int height = buffer->height; - uint8_t global_alpha_u8 = rasterizer->state->gstate.global_alpha_u8; - float factor = ctx_matrix_get_scale (&rasterizer->state->gstate.transform); - int dim = (int)((1.0f / factor) / 3); - - int i = 0; - - for (; i < count && (x - dim< 0 || y - dim < 0 || x + dim >= height || y + dim >= height); i++) - { - *((uint32_t*)(rgba))=0; - rgba += 4; - x += dx; - y += dy; - } - - for (; i < count && !( - x - dim < 0 || y - dim < 0 || - x + dim >= width || - y + dim >= height); i++) - { + CtxEntry *entries; + unsigned int count; + int size; + uint32_t flags; + int bitpack_pos; // stream is bitpacked up to this offset +}; - int u = (int)x; - int v = (int)y; - { - int bpp = 4; - uint64_t sum[4]={0,0,0,0}; - int count = 0; +// the keydb consists of keys set to floating point values, +// that might also be interpreted as integers for enums. +// +// the hash +typedef struct _CtxKeyDbEntry CtxKeyDbEntry; +struct _CtxKeyDbEntry +{ + uint32_t key; + float value; + //union { float f[1]; uint8_t u8[4]; }value; +}; - { - for (int ov = - dim; ov <= dim; ov++) - { - uint8_t *src = (uint8_t *) buffer->data + bpp * ((v+ov) * width + (u - dim)); - for (int ou = - dim; ou <= dim; ou++) - { - for (int c = 0; c < bpp; c++) - sum[c] += src[c]; - count ++; - src += bpp; - } +struct _CtxState +{ + unsigned int has_moved:1; + unsigned int has_clipped:1; + int8_t source; // used for the single-shifting to stroking + // 0 = fill + // 1 = start_stroke + // 2 = in_stroke + // + // if we're at in_stroke at start of a source definition + // we do filling + int16_t gstate_no; - } - } + float x; + float y; + int ink_min_x; + int ink_min_y; + int ink_max_x; + int ink_max_y; + CtxGState gstate; + CtxGState gstate_stack[CTX_MAX_STATES];//at end, so can be made dynamic +#if CTX_GRADIENTS + CtxGradient gradient; /* we keep only one gradient, + this goes icky with multiple + restores - it should really be part of + graphics state.. + XXX, with the stringpool gradients + can be stored there. + */ +#endif + CtxKeyDbEntry keydb[CTX_MAX_KEYDB]; + char stringpool[CTX_STRINGPOOL_SIZE]; +}; - int recip = 65536/count; - 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); - } - rgba += 4; - x += dx; - y += dy; - } +typedef struct _CtxFont CtxFont; +typedef struct _CtxFontEngine CtxFontEngine; - for (; i < count; i++) - { - *((uint32_t*)(rgba))= 0; - rgba += 4; - } -#if CTX_DITHER -//ctx_dither_rgba_u8 (rgba, x, y, rasterizer->format->dither_red_blue, -// rasterizer->format->dither_green); +struct _CtxFontEngine +{ +#if CTX_FONTS_FROM_FILE + 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); + float (*glyph_kern) (CtxFont *font, Ctx *ctx, uint32_t unicharA, uint32_t unicharB); +}; -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) +#pragma pack(push,1) +struct _CtxFont { - 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_ONE_FONT_ENGINE==0 + CtxFontEngine *engine; #endif - 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))) - { - for (unsigned i = 0 ; i < count; i++) - *dst++ = 0; - return; - } - -#if 1 - int pre = ctx_mini(ctx_maxi(-u,0), count); - memset (dst, 0, pre); - dst +=pre; - count-=pre; - src+=pre; - u+=pre; -#else - while (count && !(u >= 0)) + union { - *dst++ = 0; - src ++; - u++; - count--; - } + struct + { + CtxEntry *data; + //uint16_t length; + /* we've got ~110 bytes to fill to cover as + much data as stbtt_fontinfo */ + //int16_t glyph_pos[26]; // for a..z + } ctx; +#if CTX_FONT_ENGINE_CTX_FS + struct + { + const char *name; + char *path; + } ctx_fs; +#endif +#if CTX_FONT_ENGINE_STB + struct + { + const char *name; + stbtt_fontinfo ttf_info; + } stb; +#endif +#if 0 + struct { int start; int end; int gw; int gh; const uint8_t *data;} monobitmap; #endif + }; +#if CTX_ONE_FONT_ENGINE==0 + uint8_t type:3; // 0 ctx 1 stb 2 monobitmap + uint8_t monospaced:1; +#endif +}; +#pragma pack(pop) - int limit = ctx_mini (count, bwidth - u); - if (limit>0) - { - memcpy (dst, src, limit * 4); - dst += limit; - } - memset (dst, 0, count - limit); +enum _CtxIteratorFlag +{ + CTX_ITERATOR_FLAT = 0, + CTX_ITERATOR_EXPAND_BITPACK = 2, + CTX_ITERATOR_DEFAULTS = CTX_ITERATOR_EXPAND_BITPACK +}; +typedef enum _CtxIteratorFlag CtxIteratorFlag; -//ctx_RGBA8_apply_global_alpha_and_associate (rasterizer, (uint8_t*)out, count); -} -static void -ctx_fragment_image_rgba8_RGBA8_nearest_copy_repeat (CtxRasterizer *rasterizer, - float x, float y, float z, - void *out, int count, float dx, float dy, float dz) +struct _CtxIterator { - 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; - int bwidth = buffer->width; - int bheight = buffer->height; - int u = (int)x; - int v = (int)y; - if (v < 0) v += bheight * 8192; - if (u < 0) u += bwidth * 8192; - v %= bheight; - u %= bwidth; + int pos; + int first_run; + CtxDrawlist *drawlist; + int end_pos; + int flags; - uint32_t *src = ((uint32_t*)buffer->data) + bwidth * v; + int bitpack_pos; + int bitpack_length; // if non 0 bitpack is active + CtxEntry bitpack_command[6]; // the command returned to the + // user if unpacking is needed. +}; - while (count) - { - int chunk = ctx_mini (bwidth - u, count); - memcpy (dst, src + u, chunk * 4); - dst += chunk; - count -= chunk; - u = (u + chunk) % bwidth; - } -} +#if CTX_EVENTS -static inline int -_ctx_coords_restrict (CtxExtend extend, - int *u, int *v, - int bwidth, int bheight) -{ - switch (extend) - { - case CTX_EXTEND_REPEAT: - if(u) - { - while (*u < 0) *u += bwidth * 4096; // XXX need better way to do this - *u %= bwidth; - } - if(v) - { - while (*v < 0) *v += bheight * 4096; - *v %= bheight; - } - return 1; - case CTX_EXTEND_REFLECT: - if (u) - { - while (*u < 0) *u += bwidth * 4096; // XXX need better way to do this - *u %= (bwidth*2); +// include list implementation - since it already is a header+inline online +// implementation? - *u = (*u>=bwidth) * (bwidth*2 - *u) + - (*u<bwidth) * *u; - } +typedef struct CtxItemCb { + CtxEventType types; + CtxCb cb; + void* data1; + void* data2; - if (v) - { - while (*v < 0) *v += bheight * 4096; - *v %= (bheight*2); - *v = (*v>=bheight) * (bheight*2 - *v) + - (*v<bheight) * *v; - } + void (*finalize) (void *data1, void *data2, void *finalize_data); + void *finalize_data; - return 1; - case CTX_EXTEND_PAD: - if (u)*u = ctx_mini (ctx_maxi (*u, 0), bwidth-1); - if (v)*v = ctx_mini (ctx_maxi (*v, 0), bheight-1); - return 1; - case CTX_EXTEND_NONE: - if (u) - { - //*u %= bwidth; - if (*u < 0 || *u >= bwidth) return 0; - } - if (v) - { - //*v %= bheight; - if (*v < 0 || *v >= bheight) return 0; - } - return 1; - } - return 0; -} +} CtxItemCb; -static void -ctx_fragment_image_rgba8_RGBA8_nearest_affine (CtxRasterizer *rasterizer, - float x, float y, float z, - 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 *rgba = (uint8_t *) out; - CtxSource *g = &rasterizer->state->gstate.source_fill; - CtxBuffer *buffer = g->texture.buffer->color_managed?g->texture.buffer->color_managed:g->texture.buffer; - 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); - switch (extend){ - case 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; - } +typedef struct CtxItem { + CtxMatrix inv_matrix; /* for event coordinate transforms */ - 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; - } + /* bounding box */ + float x0; + float y0; + float x1; + float y1; - 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++; - } - } - break; - default: - 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; - } -} + void *path; + double path_hash; + CtxCursor cursor; /* if 0 then UNSET and no cursor change is requested + */ + CtxEventType types; /* all cb's ored together */ + CtxItemCb cb[CTX_MAX_CBS]; + int cb_count; + int ref_count; +} CtxItem; -static void -ctx_fragment_image_rgba8_RGBA8_nearest_scale (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 = NULL; - CtxExtend extend = rasterizer->state->gstate.extend; - uint32_t *src = NULL; - buffer = g->texture.buffer->color_managed?g->texture.buffer->color_managed:g->texture.buffer; - int ideltax = (int)(dx * 65536); - uint32_t *dst = (uint32_t*)out; - int bwidth = buffer->width; - int bheight = buffer->height; - int bbheight = bheight << 16; - int bbwidth = bwidth << 16; -// x += 0.5f; -// y += 0.5f; - src = (uint32_t*)buffer->data; - //if (!src){ fprintf (stderr, "eeek bailing in nearest fragment\n"); return;}; +typedef struct _CtxEvents CtxEvents; +struct _CtxEvents +{ + int frozen; + int fullscreen; + CtxList *grabs; /* could split the grabs per device in the same way, + to make dispatch overhead smaller,. probably + not much to win though. */ + CtxEvent drag_event[CTX_MAX_DEVICES]; + CtxList *idles; + CtxList *idles_to_remove; + CtxList *idles_to_add; + CtxList *events; // for ctx_get_event + CtxBinding bindings[CTX_MAX_KEYBINDINGS]; /*< better as list, uses no mem if unused */ + int n_bindings; + CtxItem *prev[CTX_MAX_DEVICES]; + float pointer_x[CTX_MAX_DEVICES]; + float pointer_y[CTX_MAX_DEVICES]; + unsigned char pointer_down[CTX_MAX_DEVICES]; + unsigned int in_idle_dispatch:1; + unsigned int ctx_get_event_enabled:1; + CtxModifierState modifier_state; + int idle_id; + CtxList *items; + CtxItem *last_item; + float tap_hysteresis; +#if CTX_VT + CtxList *clients; + CtxClient *active; + CtxClient *active_tab; +#endif + int tap_delay_min; + int tap_delay_max; + int tap_delay_hold; +}; +#endif - { - unsigned int i = 0; - int32_t ix = (int)(x * 65536); - int32_t iy = (int)(y * 65536); +typedef struct _CtxEidInfo +{ + char *eid; + int frame; + int width; + int height; +} CtxEidInfo; - if (extend == CTX_EXTEND_NONE) - { - int32_t u1 = ix + ideltax * (count-1); - int32_t v1 = iy; - uint32_t *edst = ((uint32_t*)out)+count - 1; - for (; i < count; ) - { - if (u1 <0 || v1 < 0 || u1 >= bbwidth || v1 >= bbheight) - { - *edst-- = 0; - count --; - u1 -= ideltax; - } - else break; - } - for (i = 0; i < count; i ++) - { - if (ix < 0 || iy < 0 || ix >= bbwidth || iy >= bbheight) - { - *dst++ = 0; - x += dx; - ix += ideltax; - } - else break; - } +struct _CtxGlyphEntry +{ + uint32_t unichar; + uint16_t offset; + CtxFont *font; +}; +typedef struct _CtxGlyphEntry CtxGlyphEntry; - 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; - } - } - else - { +struct _Ctx +{ + CtxBackend *backend; + CtxDrawlist drawlist; + int transformation; + int width; + int height; + int dirty; + Ctx *texture_cache; + CtxList *deferred; + CtxList *eid_db; + CtxState state; /**/ + int frame; /* used for texture lifetime */ + uint32_t bail; + CtxBackend *backend_pushed; + CtxBuffer texture[CTX_MAX_TEXTURES]; +#if CTX_EVENTS + CtxCursor cursor; + int quit; + CtxEvents events; + int mouse_fd; + int mouse_x; + int mouse_y; +#endif +#if CTX_CURRENT_PATH + CtxDrawlist current_path; // possibly transformed coordinates ! + CtxIterator current_path_iterator; +#endif +#if CTX_GLYPH_CACHE + CtxGlyphEntry glyph_index_cache[CTX_GLYPH_CACHE_SIZE]; +#endif + CtxFont *fonts; // a copy to keep it alive with mp's + // garbage collector, the fonts themselves + // are static and shared beyond ctx contexts + - int v = iy >> 16; - int u = ix >> 16; - _ctx_coords_restrict (extend, &u, &v, bwidth, bheight); - int o = (v)*bwidth; - for (; i < count; i ++) - { - u = ix >> 16; - _ctx_coords_restrict (extend, &u, &v, bwidth, bheight); - *dst++ = src[o + (u)]; - ix += ideltax; - } - } - } -// ctx_RGBA8_apply_global_alpha_and_associate (rasterizer, (uint8_t*)out, count); -} +}; -static void -ctx_fragment_image_rgba8_RGBA8_nearest_generic (CtxRasterizer *rasterizer, - float x, float y, float z, - void *out, int scount, float dx, float dy, float dz) +static inline void +ctx_process (Ctx *ctx, CtxEntry *entry) { - unsigned int 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; - CtxBuffer *buffer = g->texture.buffer->color_managed?g->texture.buffer->color_managed:g->texture.buffer; - 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); - int zi_delta = (int)(dz * 65536); - int32_t yi = (int)(y * 65536); - int32_t xi = (int)(x * 65536); - int32_t zi = (int)(z * 65536); - switch (extend){ - case CTX_EXTEND_NONE: - { + ctx->backend->process (ctx, (CtxCommand *) entry); +} - int32_t u1 = xi + xi_delta* (count-1); - int32_t v1 = yi + yi_delta* (count-1); - int32_t z1 = zi + zi_delta* (count-1); - uint32_t *edst = ((uint32_t*)out)+(count-1); - 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) - { - *edst-- = 0; - count --; - u1 -= xi_delta; - v1 -= yi_delta; - z1 -= zi_delta; - } - else break; - } +CtxBuffer *ctx_buffer_new (int width, int height, + CtxPixelFormat pixel_format); +void ctx_buffer_destroy (CtxBuffer *buffer); - for (i= 0; i < count; i ++) - { - 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) - { - *((uint32_t*)(rgba))= 0; - } - else - break; - xi += xi_delta; - yi += yi_delta; - zi += zi_delta; - rgba += 4; - } +void +ctx_state_gradient_clear_stops (CtxState *state); - 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; - zi += zi_delta; - rgba += 4; - i++; - } - } - break; - default: - 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; - zi += zi_delta; - rgba += 4; - i++; - } - break; - } -} +static inline void ctx_interpret_style (CtxState *state, CtxEntry *entry, void *data); +static inline void ctx_interpret_transforms (CtxState *state, CtxEntry *entry, void *data); +static inline void ctx_interpret_pos (CtxState *state, CtxEntry *entry, void *data); +static inline void ctx_interpret_pos_transform (CtxState *state, CtxEntry *entry, void *data); -static void -ctx_fragment_image_rgba8_RGBA8_nearest (CtxRasterizer *rasterizer, - float x, float y, float z, - void *out, int icount, float dx, float dy, float dz) +struct _CtxInternalFsEntry { - unsigned int count = icount; - CtxExtend extend = rasterizer->state->gstate.extend; - if (z == 1.0f && dz == 0.0f) // this also catches other constant z! - { - 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); - } - else - { - ctx_fragment_image_rgba8_RGBA8_nearest_generic (rasterizer, x, y, z, out, count, dx, dy, dz); - } -} - + char *path; + int length; + char *data; +}; -static inline void -ctx_fragment_image_rgba8_RGBA8_bi_scale (CtxRasterizer *rasterizer, - float x, float y, float z, - void *out, int scount, float dx, float dy, float dz) +struct _CtxPixelFormatInfo { - 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; - 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 + CtxPixelFormat pixel_format:8; + uint8_t components; /* number of components */ + uint8_t bpp; /* bits per pixel - for doing offset computations + along with rowstride found elsewhere, if 0 it indicates + 1/8 */ + uint8_t ebpp; /*effective bytes per pixel - for doing offset + computations, for formats that get converted, the + ebpp of the working space applied */ + uint8_t dither_red_blue; + uint8_t dither_green; + CtxPixelFormat composite_format:8; - int32_t yi = (int)(y * 65536); - int32_t xi = (int)(x * 65536); + void (*to_comp) (CtxRasterizer *r, + int x, const void * __restrict__ src, uint8_t * __restrict__ comp, int count); + void (*from_comp) (CtxRasterizer *r, + int x, const uint8_t * __restrict__ comp, void *__restrict__ dst, int count); + void (*apply_coverage) (CtxRasterizer *r, uint8_t * __restrict__ dst, uint8_t * __restrict__ src, int x, uint8_t *coverage, + unsigned int count); + void (*setup) (CtxRasterizer *r); +}; - 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; - } - } +static inline void +_ctx_user_to_device (CtxState *state, float *x, float *y); +static void +_ctx_user_to_device_distance (CtxState *state, float *x, float *y); +static void ctx_state_init (CtxState *state); +static inline void +ctx_interpret_pos_bare (CtxState *state, CtxEntry *entry, void *data); +static inline void +ctx_drawlist_deinit (CtxDrawlist *drawlist); - - int v = yi >> 16; +//extern CtxPixelFormatInfo *(*ctx_pixel_format_info) (CtxPixelFormat format); +CtxPixelFormatInfo *ctx_pixel_format_info (CtxPixelFormat format); - int dv = (yi >> 8) & 0xff; - int u = xi >> 16; +extern void (*ctx_composite_stroke_rect) (CtxRasterizer *rasterizer, + float x0, + float y0, + float x1, + float y1, + float line_width); - int v1 = v+1; +extern void (*ctx_composite_setup) (CtxRasterizer *rasterizer); - _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 = ((uint32_t*)buffer->data) + bwidth * v1; +struct _CtxShapeEntry +{ + uint32_t hash; + uint16_t width; + uint16_t height; + int last_frame; // xxx + uint32_t uses; // instrumented for longer keep-alive + uint8_t data[]; +}; - if (!extend && v1 > bheight-1) ndata = data; +typedef struct _CtxShapeEntry CtxShapeEntry; - 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; +extern void (*ctx_rasterizer_rasterize_edges) (CtxRasterizer *rasterizer, const int fill_rule +#if CTX_SHAPE_CACHE + ,CtxShapeEntry *shape +#endif + ); - 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_associate_global_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 (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 - { - 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; - rgba += 4; - u = xi >> 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_associate_global_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 (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 - { - 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; - rgba += 4; - u = xi >> 16; - } - } - } -} -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 global_alpha_u8 = rasterizer->state->gstate.global_alpha_u8; - uint8_t *rgba = (uint8_t *) out; - CtxSource *g = &rasterizer->state->gstate.source_fill; - CtxBuffer *buffer = g->texture.buffer->color_managed?g->texture.buffer->color_managed:g->texture.buffer; - 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); +extern void (*ctx_composite_fill_rect) (CtxRasterizer *rasterizer, + float x0, + float y0, + float x1, + float y1, + uint8_t cov); - 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; - } +const char *ctx_utf8_skip (const char *s, int utf8_length); +int ctx_utf8_strlen (const char *s); +int +ctx_unichar_to_utf8 (uint32_t ch, + uint8_t *dest); - 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 +ctx_utf8_to_unichar (const char *input); - 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 (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; +typedef struct _CtxHasher CtxHasher; - _ctx_coords_restrict (extend, &u, &v, bwidth, bheight); - _ctx_coords_restrict (extend, &u1, &v1, bwidth, bheight); +typedef void (*CtxFragment) (CtxRasterizer *rasterizer, float x, float y, float z, void *out, int count, float dx, float dy, float dz); - 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_RGBA8_associate_global_alpha_u32 ( - ctx_bi_RGBA8 (*src00,*src01,*src10,*src11, du,dv), global_alpha_u8); - xi += xi_delta; - yi += yi_delta; - rgba += 4; +#define CTX_MAX_GAUSSIAN_KERNEL_DIM 512 - i++; - } -} -static inline void -ctx_fragment_image_rgba8_RGBA8_bi_generic (CtxRasterizer *rasterizer, - float x, float y, float z, - void *out, int scount, - float dx, float dy, float dz) +struct _CtxShapeCache { - 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; - CtxBuffer *buffer = g->texture.buffer->color_managed?g->texture.buffer->color_managed:g->texture.buffer; - 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); + CtxShapeEntry *entries[CTX_SHAPE_CACHE_ENTRIES]; + long size; +}; - int yi_delta = (int)(dy * 65536); - int xi_delta = (int)(dx * 65536); - int zi_delta = (int)(dz * 65536); - int32_t yi = (int)(y * 65536); - int32_t xi = (int)(x * 65536); - int32_t zi = (int)(z * 65536); - if (extend == CTX_EXTEND_NONE) { - int32_t u1 = xi + xi_delta* (count-1); - int32_t v1 = yi + yi_delta* (count-1); - int32_t z1 = zi + zi_delta* (count-1); - uint32_t *edst = ((uint32_t*)out)+(count-1); - 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) - { - *edst-- = 0; - count --; - u1 -= xi_delta; - v1 -= yi_delta; - z1 -= zi_delta; - } - else break; - } +typedef struct _CtxShapeCache CtxShapeCache; - for (i= 0; i < count; i ++) - { - 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) - { - *((uint32_t*)(rgba))= 0; - } - else - break; - xi += xi_delta; - yi += yi_delta; - zi += zi_delta; - rgba += 4; - } - } +typedef enum { + CTX_COV_PATH_FALLBACK =0, + CTX_COV_PATH_RGBA8_OVER, + CTX_COV_PATH_RGBA8_COPY, + CTX_COV_PATH_RGBA8_COPY_FRAGMENT, + CTX_COV_PATH_RGBA8_OVER_FRAGMENT, + CTX_COV_PATH_GRAYA8_COPY, + CTX_COV_PATH_GRAY1_COPY, + CTX_COV_PATH_GRAY2_COPY, + CTX_COV_PATH_GRAY4_COPY, + CTX_COV_PATH_RGB565_COPY, + CTX_COV_PATH_RGB332_COPY, + CTX_COV_PATH_GRAY8_COPY, + CTX_COV_PATH_RGBAF_COPY, + CTX_COV_PATH_RGB8_COPY, + CTX_COV_PATH_CMYK8_COPY, + CTX_COV_PATH_CMYKA8_COPY, + CTX_COV_PATH_CMYKAF_COPY, + CTX_COV_PATH_GRAYAF_COPY +} CtxCovPath; - uint32_t *src00=data; - uint32_t *src01=data; - uint32_t *src10=data; - uint32_t *src11=data; +struct _CtxRasterizer +{ + CtxBackend backend; + /* these should be initialized and used as the bounds for rendering into the + buffer as well XXX: not yet in use, and when in use will only be + correct for axis aligned clips - proper rasterization of a clipping path + would be yet another refinement on top. + */ - 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); +#define CTX_COMPOSITE_ARGUMENTS CtxRasterizer *rasterizer, uint8_t * __restrict__ dst, uint8_t * __restrict__ src, int x0, uint8_t * __restrict__ coverage, unsigned int count + 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); - 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_RGBA8_associate_global_alpha_u32 ( - ctx_bi_RGBA8 (*src00,*src01,*src10,*src11, du,dv), global_alpha_u8); - xi += xi_delta; - yi += yi_delta; - zi += zi_delta; - rgba += 4; + unsigned int aa; // level of vertical aa + unsigned int prev_active_edges; + unsigned int active_edges; + unsigned int pending_edges; + unsigned int ending_edges; + unsigned int edge_pos; // where we're at in iterating all edges + unsigned int needs_aa3; // count of how many edges implies antialiasing + unsigned int needs_aa5; // count of how many edges implies antialiasing + unsigned int needs_aa15; // count of how many edges implies antialiasing + unsigned int horizontal_edges; - i++; - } -} + int scanline; + int scan_min; + int scan_max; + int col_min; + int col_max; + int inner_x; + int inner_y; -static void -ctx_fragment_image_rgba8_RGBA8_bi (CtxRasterizer *rasterizer, - float x, float y, float z, - 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) - { - ctx_fragment_image_rgba8_RGBA8_bi_scale (rasterizer, x, y, z, out, count, dx, dy, dz); - } - 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 - { - ctx_fragment_image_rgba8_RGBA8_bi_generic (rasterizer, x, y, z, out, count, dx, dy, dz); - } -} -#endif + float x; + float y; -#define ctx_clampi(val,min,max) \ - ctx_mini (ctx_maxi ((val), (min)), (max)) + float first_x; + float first_y; -static inline uint32_t ctx_yuv_to_rgba32 (uint8_t y, uint8_t u, uint8_t v) -{ - int cy = ((y - 16) * 76309) >> 16; - int cr = (v - 128); - int cb = (u - 128); - int red = cy + ((cr * 104597) >> 16); - int green = cy - ((cb * 25674 + cr * 53278) >> 16); - int blue = cy + ((cb * 132201) >> 16); - return ctx_clampi (red, 0, 255) | - (ctx_clampi (green, 0, 255) << 8) | - (ctx_clampi (blue, 0, 255) << 16) | - (0xff << 24); -} - -static void -ctx_fragment_image_yuv420_RGBA8_nearest (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; - CtxBuffer *buffer = g->texture.buffer; - if (buffer->color_managed) - buffer = buffer->color_managed; - uint8_t *src = (uint8_t *) buffer->data; - int bwidth = buffer->width; - int bheight = buffer->height; - int bwidth_div_2 = bwidth/2; - int bheight_div_2 = bheight/2; - x += 0.5f; - y += 0.5f; - - if (!src) - return; - - { - int i = 0; - - float u1 = x + dx * (count-1); - float v1 = y + dy * (count-1); - uint32_t *edst = ((uint32_t*)out)+count - 1; - for (; i < count; ) - { - if (u1 <0 || v1 < 0 || u1 >= bwidth || v1 >= bheight) - { - *edst-- = 0; - count --; - u1 -= dx; - v1 -= dy; - } - else break; - } - - for (; i < count; i ++) - { - int u = (int)x; - int v = (int)y; - if ((u < 0 || v < 0 || u >= bwidth || v >= bheight)) - { - *((uint32_t*)(rgba))= 0; - } - else - { - break; - } - x += dx; - y += dy; - rgba += 4; - } - - uint32_t u_offset = bheight * bwidth; - uint32_t v_offset = u_offset + bheight_div_2 * bwidth_div_2; - - if (rasterizer->swap_red_green) - { - v_offset = bheight * bwidth; - u_offset = v_offset + bheight_div_2 * bwidth_div_2; - } + uint16_t blit_x; + uint16_t blit_y; + uint16_t blit_width; + uint16_t blit_height; + uint16_t blit_stride; - // XXX this is incorrect- but fixes some bug! - int ix = 65536;//x * 65536; - int iy = (int)(y * 65536); + unsigned int clip_rectangle:1; + unsigned int has_shape:2; + int has_prev:2; + unsigned int preserve:1; +#if CTX_ENABLE_SHADOW_BLUR + unsigned int in_shadow:1; +#endif + unsigned int in_text:1; + unsigned int swap_red_green:1; - int ideltax = (int)(dx * 65536); - int ideltay = (int)(dy * 65536); +#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; + Ctx *texture_source; /* normally same as ctx */ + int shadow_y; - if (ideltay == 0) - { - int u = ix >> 16; - int v = iy >> 16; + uint8_t color[4*5]; // in compositing format + uint16_t color_native; // + uint16_t color_nativeB[5]; - uint32_t y = v * bwidth; - uint32_t uv = (v / 2) * bwidth_div_2; + int edges[CTX_MAX_EDGES]; // integer position in edge array + CtxDrawlist edge_list; - if (v >= 0 && v < bheight) - while (i < count)// && u >= 0 && u+1 < bwidth) - { - *((uint32_t*)(rgba))= ctx_yuv_to_rgba32 (src[y+u], - src[u_offset+uv+u/2], src[v_offset+uv+u/2]); -#if 0 -#if CTX_DITHER - ctx_dither_rgba_u8 (rgba, x, y, rasterizer->format->dither_red_blue, - rasterizer->format->dither_green); +#if CTX_GRADIENTS +#if CTX_GRADIENT_CACHE + int gradient_cache_valid; + uint8_t gradient_cache_u8[CTX_GRADIENT_CACHE_ELEMENTS][4]; + int gradient_cache_elements; #endif #endif - ix += ideltax; - rgba += 4; - u = ix >> 16; - i++; - } - } - else - { - int u = ix >> 16; - int v = iy >> 16; +#if CTX_ENABLE_CLIP + CtxBuffer *clip_buffer; +#endif - while (i < count)// && u >= 0 && v >= 0 && u < bwidth && v < bheight) - { - uint32_t y = v * bwidth + u; - uint32_t uv = (v / 2) * bwidth_div_2 + (u / 2); +#if CTX_COMPOSITING_GROUPS + void *saved_buf; // when group redirected + CtxBuffer *group[CTX_GROUP_MAX]; +#endif +#if CTX_ENABLE_SHADOW_BLUR + float kernel[CTX_MAX_GAUSSIAN_KERNEL_DIM]; +#endif - *((uint32_t*)(rgba))= ctx_yuv_to_rgba32 (src[y], - src[u_offset+uv], src[v_offset+uv]); -#if 0 -#if CTX_DITHER - ctx_dither_rgba_u8 (rgba, x+i, y, rasterizer->format->dither_red_blue, - rasterizer->format->dither_green); +#if static_OPAQUE + uint8_t opaque[CTX_MAX_SCANLINE_LENGTH]; #endif + +#if CTX_SHAPE_CACHE + CtxShapeCache shape_cache; /* needs to be at end of struct, it + is excluded from clearing */ #endif +}; - ix += ideltax; - iy += ideltay; - rgba += 4; - u = ix >> 16; - v = iy >> 16; - i++; - } - } +struct _CtxSHA1 { + uint64_t length; + uint32_t state[5], curlen; + unsigned char buf[64]; +}; +typedef struct _CtxMurmur CtxMurmur; +struct _CtxMurmur { + uint32_t state[2]; +}; - for (; i < count; i++) - { - *((uint32_t*)(rgba))= 0; - rgba += 4; - } - } - if (rasterizer->state->gstate.global_alpha_u8 != 255) - ctx_RGBA8_apply_global_alpha_and_associate (rasterizer, (uint8_t*)out, count); -} +#pragma pack(push,1) +typedef struct CtxCommandState +{ + uint16_t pos; + uint32_t active; +} CtxCommandState; +#pragma pack(pop) -#if CTX_FRAGMENT_SPECIALIZE +struct _CtxHasher +{ + CtxRasterizer rasterizer; + int cols; + int rows; + uint32_t hashes[CTX_HASH_COLS*CTX_HASH_ROWS]; + CtxMurmur murmur_fill[CTX_MAX_STATES]; + CtxMurmur murmur_stroke[CTX_MAX_STATES]; + int source_level; + int pos; -CTX_DECLARE_SWAP_RED_GREEN_FRAGMENT(ctx_fragment_image_rgba8_RGBA8_box) -CTX_DECLARE_SWAP_RED_GREEN_FRAGMENT(ctx_fragment_image_rgba8_RGBA8_bi) -CTX_DECLARE_SWAP_RED_GREEN_FRAGMENT(ctx_fragment_image_rgba8_RGBA8_nearest) + int prev_command; -CTX_DECLARE_SWAP_RED_GREEN_FRAGMENT(ctx_fragment_image_rgba8_RGBA8_nearest_copy) -CTX_DECLARE_SWAP_RED_GREEN_FRAGMENT(ctx_fragment_image_rgba8_RGBA8_nearest_copy_repeat) -CTX_DECLARE_SWAP_RED_GREEN_FRAGMENT(ctx_fragment_image_rgba8_RGBA8_nearest_scale) -CTX_DECLARE_SWAP_RED_GREEN_FRAGMENT(ctx_fragment_image_rgba8_RGBA8_nearest_affine) -CTX_DECLARE_SWAP_RED_GREEN_FRAGMENT(ctx_fragment_image_rgba8_RGBA8_nearest_generic) + CtxDrawlist *drawlist; -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) +}; -static inline void -ctx_fragment_image_rgba8_RGBA8 (CtxRasterizer *rasterizer, - float x, float y, float z, - void *out, int count, float dx, float dy, float dz) -{ - if (rasterizer->state->gstate.image_smoothing) - { - float factor = ctx_matrix_get_scale (&rasterizer->state->gstate.transform); - if (factor <= 0.50f) - { - if (rasterizer->swap_red_green) - ctx_fragment_image_rgba8_RGBA8_box_swap_red_green (rasterizer, x, y, z, out, count, dx, dy, dz); - else - 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) - { - // XXX: also verify translate == 0 for this fast path to be valid - if (rasterizer->swap_red_green) - ctx_fragment_image_rgba8_RGBA8_nearest_swap_red_green (rasterizer, x, y, z, out, count, dx, dy, dz); - else - ctx_fragment_image_rgba8_RGBA8_nearest (rasterizer, x, y, z, out, count, dx, dy, dz); - } -#endif - else - { - if (rasterizer->swap_red_green) - ctx_fragment_image_rgba8_RGBA8_bi_swap_red_green (rasterizer, x, y, z, out, count, dx, dy, dz); - else - ctx_fragment_image_rgba8_RGBA8_bi (rasterizer, x, y, z, out, count, dx, dy, dz); - } - } - else - { - if (rasterizer->swap_red_green) - ctx_fragment_image_rgba8_RGBA8_nearest_swap_red_green (rasterizer, x, y, z, out, count, dx, dy, dz); - else - ctx_fragment_image_rgba8_RGBA8_nearest (rasterizer, x, y, z, out, count, dx, dy, dz); - } - //ctx_fragment_swap_red_green_u8 (out, count); -#if 0 -#if CTX_DITHER - uint8_t *rgba = (uint8_t*)out; - ctx_dither_rgba_u8 (rgba, x, y, rasterizer->format->dither_red_blue, - rasterizer->format->dither_green); -#endif -#endif -} +#if CTX_RASTERIZER +void ctx_rasterizer_deinit (CtxRasterizer *rasterizer); +void ctx_rasterizer_destroy (CtxRasterizer *rasterizer); #endif -static void -ctx_fragment_image_gray1_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; - CtxBuffer *buffer = g->texture.buffer; - for (int i = 0; i < count; i ++) - { - int u = (int)x; - int v = (int)y; - if ( u < 0 || v < 0 || - u >= buffer->width || - v >= buffer->height) - { - rgba[0] = rgba[1] = rgba[2] = rgba[3] = 0; - } - else - { - uint8_t *src = (uint8_t *) buffer->data; - src += v * buffer->stride + u / 8; - if (*src & (1<< (u & 7) ) ) - { - rgba[0] = rgba[1] = rgba[2] = rgba[3] = 0; - } - else - { - for (int c = 0; c < 4; c++) - { rgba[c] = 255; - }//g->texture.rgba[c]; - //} - } - } +enum { + NC_MOUSE_NONE = 0, + NC_MOUSE_PRESS = 1, /* "mouse-pressed", "mouse-released" */ + NC_MOUSE_DRAG = 2, /* + "mouse-drag" (motion with pressed button) */ + NC_MOUSE_ALL = 3 /* + "mouse-motion" (also delivered for release) */ +}; +void _ctx_mouse (Ctx *term, int mode); +void nc_at_exit (void); - rgba += 4; - x += dx; - y += dy; - } -} +int ctx_terminal_width (void); +int ctx_terminal_height (void); +int ctx_terminal_cols (void); +int ctx_terminal_rows (void); +extern int ctx_frame_ack; -#if CTX_GRADIENTS -static void -ctx_fragment_radial_gradient_RGBA8 (CtxRasterizer *rasterizer, float x, float y, float z, void *out, int count, float dx, float dy, float dz) +typedef struct _CtxCtx CtxCtx; +struct _CtxCtx { - uint8_t *rgba = (uint8_t *) out; - CtxSource *g = &rasterizer->state->gstate.source_fill; -#if CTX_DITHER - int scan = rasterizer->scanline / CTX_FULL_AA; - int ox = (int)x; -#endif - for (int i = 0; i < count; i ++) - { - float v = (ctx_hypotf_fast (g->radial_gradient.x0 - x, g->radial_gradient.y0 - y) - - g->radial_gradient.r0) * (g->radial_gradient.rdelta); -#if CTX_GRADIENT_CACHE - uint32_t *rgbap = (uint32_t*)&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, rasterizer->format->dither_red_blue, - rasterizer->format->dither_green); -#endif - rgba += 4; - x += dx; - y += dy; - } -} + CtxBackend backend; + int width; + int height; + int cols; + int rows; + int was_down; +}; -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) -{ -#if 0 - uint8_t *rgba = (uint8_t *) out; - CtxSource *g = &rasterizer->state->gstate.source_fill; - for (int i = 0; i < count; i ++) - { - float v = ( ( (g->linear_gradient.dx * x + g->linear_gradient.dy * y) / - g->linear_gradient.length) - - g->linear_gradient.start) * (g->linear_gradient.rdelta); -#if CTX_GRADIENT_CACHE - uint32_t*rgbap = ((uint32_t*)(&ctx_gradient_cache_u8[ctx_grad_index(v)][0])); - *((uint32_t*)rgba) = *rgbap; -#else - _ctx_fragment_gradient_1d_RGBA8 (rasterizer, v, 1.0, rgba); -#endif -#if CTX_DITHER - ctx_dither_rgba_u8 (rgba, x+i, y, rasterizer->format->dither_red_blue, - rasterizer->format->dither_green); -#endif - rgba += 4; - x += dx; - y += dy; - } -#else - uint8_t *rgba = (uint8_t *) out; - CtxSource *g = &rasterizer->state->gstate.source_fill; - float u0 = x; float v0 = y; - float ud = dx; float vd = dy; - float linear_gradient_rdelta = g->linear_gradient.rdelta; - float linear_gradient_length = g->linear_gradient.length; - float linear_gradient_length_recip = 1.0f/linear_gradient_length; - float linear_gradient_dx = g->linear_gradient.dx *linear_gradient_length_recip * linear_gradient_rdelta; - float linear_gradient_dy = g->linear_gradient.dy *linear_gradient_length_recip * linear_gradient_rdelta; - float linear_gradient_start = g->linear_gradient.start * linear_gradient_rdelta; +extern int _ctx_max_threads; +extern int _ctx_enable_hash_cache; +void +ctx_set (Ctx *ctx, uint32_t key_hash, const char *string, int len); +const char * +ctx_get (Ctx *ctx, const char *key); -#if CTX_DITHER - int dither_red_blue = rasterizer->format->dither_red_blue; - int dither_green = rasterizer->format->dither_green; - int scan = rasterizer->scanline / CTX_FULL_AA; - int ox = (int)x; -#endif +Ctx *ctx_new_ctx (int width, int height); +Ctx *ctx_new_fb (int width, int height); +Ctx *ctx_new_headless (int width, int height); +Ctx *ctx_new_kms (int width, int height); +Ctx *ctx_new_sdl (int width, int height); +Ctx *ctx_new_term (int width, int height); +Ctx *ctx_new_termimg (int width, int height); - u0 *= linear_gradient_dx; - v0 *= linear_gradient_dy; - ud *= linear_gradient_dx; - vd *= linear_gradient_dy; +int ctx_resolve_font (const char *name); -#if CTX_GRADIENT_CACHE - int vv = (int)(((u0 + v0) - linear_gradient_start) * (rasterizer->gradient_cache_elements-1) * 256); - int ud_plus_vd = (int)((ud + vd) * (rasterizer->gradient_cache_elements-1) * 256); +#if CTX_U8_TO_FLOAT_LUT +extern float ctx_u8_float[256]; +#define ctx_u8_to_float(val_u8) ctx_u8_float[((uint8_t)(val_u8))] #else - float vv = ((u0 + v0) - linear_gradient_start); - float ud_plus_vd = (ud + vd); +#define ctx_u8_to_float(val_u8) (val_u8/255.0f) #endif - 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; +static inline uint8_t ctx_float_to_u8 (float val_f) +{ +#if 1 + union { float f; uint32_t i; } u; + u.f = 32768.0f + val_f * (255.0f / 256.0f); + return (uint8_t)u.i; #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; - } + return val_f < 0.0f ? 0 : val_f > 1.0f ? 0xff : 0xff * val_f + 0.5f; #endif } + +#define CTX_CSS_LUMINANCE_RED 0.3f +#define CTX_CSS_LUMINANCE_GREEN 0.59f +#define CTX_CSS_LUMINANCE_BLUE 0.11f + +/* works on both float and uint8_t */ +#define CTX_CSS_RGB_TO_LUMINANCE(rgb) (\ + (rgb[0]) * CTX_CSS_LUMINANCE_RED + \ + (rgb[1]) * CTX_CSS_LUMINANCE_GREEN +\ + (rgb[2]) * CTX_CSS_LUMINANCE_BLUE) + +const char *ctx_nct_get_event (Ctx *n, int timeoutms, int *x, int *y); +const char *ctx_native_get_event (Ctx *n, int timeoutms); +void +ctx_color_get_rgba8 (CtxState *state, CtxColor *color, uint8_t *out); +void ctx_color_get_graya_u8 (CtxState *state, CtxColor *color, uint8_t *out); +float ctx_float_color_rgb_to_gray (CtxState *state, const float *rgb); +void ctx_color_get_graya (CtxState *state, CtxColor *color, float *out); +void ctx_rgb_to_cmyk (float r, float g, float b, + float *c_out, float *m_out, float *y_out, float *k_out); +uint8_t ctx_u8_color_rgb_to_gray (CtxState *state, const uint8_t *rgb); +#if CTX_ENABLE_CMYK +void ctx_color_get_cmyka (CtxState *state, CtxColor *color, float *out); #endif +static void ctx_color_set_RGBA8 (CtxState *state, CtxColor *color, uint8_t r, uint8_t g, uint8_t b, uint8_t a); +void ctx_color_set_rgba (CtxState *state, CtxColor *color, float r, float g, float b, float a); +static void ctx_color_set_drgba (CtxState *state, CtxColor *color, float r, float g, float b, float a); +void ctx_color_get_cmyka (CtxState *state, CtxColor *color, float *out); +static void ctx_color_set_cmyka (CtxState *state, CtxColor *color, float c, float m, float y, float k, float a); +static void ctx_color_set_dcmyka (CtxState *state, CtxColor *color, float c, float m, float y, float k, float a); +static void ctx_color_set_graya (CtxState *state, CtxColor *color, float gray, float alpha); -static void -ctx_fragment_color_RGBA8 (CtxRasterizer *rasterizer, float x, float y, float z, void *out, int count, float dx, float dy, float dz) -{ - uint8_t *rgba_out = (uint8_t *) out; - CtxSource *g = &rasterizer->state->gstate.source_fill; - ctx_color_get_rgba8 (rasterizer->state, &g->color, rgba_out); - ctx_RGBA8_associate_alpha (rgba_out); - if (rasterizer->swap_red_green) - { - int tmp = rgba_out[0]; - rgba_out[0] = rgba_out[2]; - rgba_out[2] = tmp; - } - for (int i = 1; i < count; i++, rgba_out+=4) - memcpy (rgba_out + count * 4, rgba_out, 4); -} -#if CTX_ENABLE_FLOAT +int ctx_color_model_get_components (CtxColorModel model); + +static void ctx_state_set (CtxState *state, uint32_t key, float value); -#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) -{ - float *rgba = (float *) out; - CtxSource *g = &rasterizer->state->gstate.source_fill; - for (int i = 0; i < count; i++) - { - float v = ( ( (g->linear_gradient.dx * x + g->linear_gradient.dy * y) / - g->linear_gradient.length) - - g->linear_gradient.start) * (g->linear_gradient.rdelta); - ctx_fragment_gradient_1d_RGBAF (rasterizer, v, 1.0f, rgba); - x += dx; - y += dy; - rgba += 4; - } -} +ctx_matrix_set (CtxMatrix *matrix, float a, float b, float c, float d, float e, float f, float g, float h, float i); -static void -ctx_fragment_radial_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; - for (int i = 0; i < count; i++) - { - float v = ctx_hypotf (g->radial_gradient.x0 - x, g->radial_gradient.y0 - y); - v = (v - g->radial_gradient.r0) * (g->radial_gradient.rdelta); - ctx_fragment_gradient_1d_RGBAF (rasterizer, v, 0.0f, rgba); - x+=dx; - y+=dy; - rgba +=4; - } -} -#endif +static void ctx_font_setup (Ctx *ctx); +static float ctx_state_get (CtxState *state, uint32_t hash); -static void -ctx_fragment_color_RGBAF (CtxRasterizer *rasterizer, float x, float y, float z, void *out, int count, float dx, float dy, float dz) -{ - float *rgba = (float *) out; - float in[4]; - CtxSource *g = &rasterizer->state->gstate.source_fill; - ctx_color_get_rgba (rasterizer->state, &g->color, in); - for (int c = 0; c < 3; c++) - in[c] *= in[3]; - while (count--) - { - for (int c = 0; c < 4; c++) - rgba[c] = in[c]; - rgba += 4; - } -} +#if CTX_RASTERIZER +static void +ctx_rasterizer_rel_move_to (CtxRasterizer *rasterizer, float x, float y); +static void +ctx_rasterizer_rel_line_to (CtxRasterizer *rasterizer, float x, float y); -static void ctx_fragment_image_RGBAF (CtxRasterizer *rasterizer, float x, float y, float z, void *out, int count, float dx, float dy, float dz) -{ - float *outf = (float *) out; - 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; - switch (buffer->format->bpp) - { -#if CTX_FRAGMENT_SPECIALIZE - case 1: ctx_fragment_image_gray1_RGBA8 (rasterizer, x, y, z, rgba, count, dx, dy, dz); break; - case 24: ctx_fragment_image_rgb8_RGBA8 (rasterizer, x, y, z, rgba, count, dx, dy, dz); break; - case 32: ctx_fragment_image_rgba8_RGBA8 (rasterizer, x, y, z, rgba, count, dx, dy, dz); break; -#endif - default: ctx_fragment_image_RGBA8 (rasterizer, x, y, z, rgba, count, dx, dy, dz); break; - } - for (int c = 0; c < 4 * count; c ++) { outf[c] = ctx_u8_to_float (rgba[c]); } -} +static void +ctx_rasterizer_move_to (CtxRasterizer *rasterizer, float x, float y); +static void +ctx_rasterizer_line_to (CtxRasterizer *rasterizer, float x, float y); +static void +ctx_rasterizer_curve_to (CtxRasterizer *rasterizer, + float x0, float y0, + float x1, float y1, + float x2, float y2); +static void +ctx_rasterizer_rel_curve_to (CtxRasterizer *rasterizer, + float x0, float y0, + float x1, float y1, + float x2, float y2); -static CtxFragment ctx_rasterizer_get_fragment_RGBAF (CtxRasterizer *rasterizer) -{ - CtxGState *gstate = &rasterizer->state->gstate; - switch (gstate->source_fill.type) - { - case CTX_SOURCE_TEXTURE: return ctx_fragment_image_RGBAF; - case CTX_SOURCE_COLOR: return ctx_fragment_color_RGBAF; -#if CTX_GRADIENTS - case CTX_SOURCE_LINEAR_GRADIENT: return ctx_fragment_linear_gradient_RGBAF; - case CTX_SOURCE_RADIAL_GRADIENT: return ctx_fragment_radial_gradient_RGBAF; -#endif - } - return ctx_fragment_color_RGBAF; -} -#endif +static void +ctx_rasterizer_reset (CtxRasterizer *rasterizer); +static void +ctx_rasterizer_arc (CtxRasterizer *rasterizer, + float x, + float y, + float radius, + float start_angle, + float end_angle, + int anticlockwise); +static void +ctx_rasterizer_quad_to (CtxRasterizer *rasterizer, + float cx, + float cy, + float x, + float y); -static inline int -ctx_matrix_no_perspective (CtxMatrix *matrix) -{ - if (fabsf(matrix->m[2][0]) >0.001f) return 0; - if (fabsf(matrix->m[2][1]) >0.001f) return 0; - if (fabsf(matrix->m[2][2] - 1.0f)>0.001f) return 0; - return 1; -} +static void +ctx_rasterizer_rel_quad_to (CtxRasterizer *rasterizer, + float cx, + float cy, + float x, + float y); -/* for multiples of 90 degree rotations, we return no rotation */ -static inline int -ctx_matrix_no_skew_or_rotate (CtxMatrix *matrix) -{ - if (fabsf(matrix->m[0][1]) >0.001f) return 0; - if (fabsf(matrix->m[1][0]) >0.001f) return 0; - return ctx_matrix_no_perspective (matrix); -} +static void +ctx_rasterizer_rectangle (CtxRasterizer *rasterizer, + float x, + float y, + float width, + float height); +static void ctx_rasterizer_finish_shape (CtxRasterizer *rasterizer); +static void ctx_rasterizer_clip (CtxRasterizer *rasterizer); +static void +ctx_rasterizer_set_font (CtxRasterizer *rasterizer, const char *font_name); -static CtxFragment ctx_rasterizer_get_fragment_RGBA8 (CtxRasterizer *rasterizer) -{ - CtxGState *gstate = &rasterizer->state->gstate; - CtxSource *g = &rasterizer->state->gstate.source_fill; - switch (gstate->source_fill.type) - { - case CTX_SOURCE_TEXTURE: - { - CtxBuffer *buffer = g->texture.buffer; - if (buffer) - buffer = buffer->color_managed?buffer->color_managed:buffer; - if (!buffer || !buffer->format) - return ctx_fragment_color_RGBA8; +static void +ctx_rasterizer_gradient_add_stop (CtxRasterizer *rasterizer, float pos, float *rgba); +static void +ctx_rasterizer_set_pixel (CtxRasterizer *rasterizer, + uint16_t x, + uint16_t y, + uint8_t r, + uint8_t g, + uint8_t b, + uint8_t a); +static void +ctx_rasterizer_round_rectangle (CtxRasterizer *rasterizer, float x, float y, float width, float height, float corner_radius); - if (buffer->format->pixel_format == CTX_FORMAT_YUV420) - { - return ctx_fragment_image_yuv420_RGBA8_nearest; - } - else -#if CTX_FRAGMENT_SPECIALIZE - switch (buffer->format->bpp) - { - case 1: return ctx_fragment_image_gray1_RGBA8; -#if 1 - case 24: - { - if (gstate->image_smoothing) - { - float factor = ctx_matrix_get_scale (&gstate->transform); - //fprintf (stderr, "{%.3f}", factor); - if (factor < 0.5f) - { - if (rasterizer->swap_red_green) - return ctx_fragment_image_rgb8_RGBA8_box_swap_red_green; - return ctx_fragment_image_rgb8_RGBA8_box; - } -#if CTX_ALWAYS_USE_NEAREST_FOR_SCALE1 - else if (factor > 0.99f && factor < 1.01f) - { - if (rasterizer->swap_red_green) - return ctx_fragment_image_rgb8_RGBA8_nearest_swap_red_green; - return ctx_fragment_image_rgb8_RGBA8_nearest; - } #endif - else - { - if (rasterizer->swap_red_green) - return ctx_fragment_image_rgb8_RGBA8_bi_swap_red_green; - return ctx_fragment_image_rgb8_RGBA8_bi; - } - } - else - { - if (rasterizer->swap_red_green) - return ctx_fragment_image_rgb8_RGBA8_nearest_swap_red_green; - return ctx_fragment_image_rgb8_RGBA8_nearest; - } - } - break; + +#if CTX_ENABLE_CM // XXX to be moved to ctx.h +void +ctx_set_drgb_space (Ctx *ctx, int device_space); +void +ctx_set_dcmyk_space (Ctx *ctx, int device_space); +void +ctx_rgb_space (Ctx *ctx, int device_space); +void +ctx_set_cmyk_space (Ctx *ctx, int device_space); #endif - case 32: - { - CtxMatrix *transform = &gstate->source_fill.transform; - CtxExtend extend = rasterizer->state->gstate.extend; - if (gstate->image_smoothing) - { - float factor = ctx_matrix_get_scale (&gstate->transform); - //fprintf (stderr, "[%.3f]", factor); - if (factor < 0.5f) - { - if (rasterizer->swap_red_green) - return ctx_fragment_image_rgba8_RGBA8_box_swap_red_green; - return ctx_fragment_image_rgba8_RGBA8_box; - } -#if CTX_ALWAYS_USE_NEAREST_FOR_SCALE1 - else if (factor > 0.99f && factor < 1.01f && extend == CTX_EXTEND_NONE) - { - if (rasterizer->swap_red_green) - return ctx_fragment_image_rgba8_RGBA8_nearest_copy_swap_red_green; - return ctx_fragment_image_rgba8_RGBA8_nearest_copy; - } + #endif - else - { - if (rasterizer->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 (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 ctx_fragment_image_rgba8_RGBA8_bi_affine_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 (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 ctx_fragment_image_rgba8_RGBA8_bi_affine; - } - return ctx_fragment_image_rgba8_RGBA8_bi_generic; - } - } - else - { - if (rasterizer->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) - { - return ctx_fragment_image_rgba8_RGBA8_nearest_copy_swap_red_green; - 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_nearest_scale_swap_red_green; - } - return ctx_fragment_image_rgba8_RGBA8_nearest_affine_swap_red_green; - } - return ctx_fragment_image_rgba8_RGBA8_nearest_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) - { - 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_nearest_scale; - } - return ctx_fragment_image_rgba8_RGBA8_nearest_affine; - } - return ctx_fragment_image_rgba8_RGBA8_nearest_generic; - } - } - default: return ctx_fragment_image_RGBA8; - } +CtxRasterizer * +ctx_rasterizer_init (CtxRasterizer *rasterizer, Ctx *ctx, Ctx *texture_source, CtxState *state, void *data, int x, int y, int width, int height, int stride, CtxPixelFormat pixel_format, CtxAntialias antialias); + +CTX_INLINE static uint8_t ctx_lerp_u8 (uint8_t v0, uint8_t v1, uint8_t dx) +{ +#if 0 + return v0 + ((v1-v0) * dx)/255; #else - return ctx_fragment_image_RGBA8; + return ( ( ( ( (v0) <<8) + (dx) * ( (v1) - (v0) ) ) ) >>8); #endif - } +} + +CTX_INLINE static uint32_t ctx_lerp_RGBA8 (const uint32_t v0, const uint32_t v1, const uint8_t dx) +{ +#if 0 + char bv0[4]; + char bv1[4]; + char res[4]; + memcpy (&bv0[0], &v0, 4); + memcpy (&bv1[0], &v1, 4); + for (int c = 0; c < 4; c++) + res [c] = ctx_lerp_u8 (bv0[c], bv1[c], dx); + return ((uint32_t*)(&res[0]))[0]; +#else + const uint32_t cov = dx; + const uint32_t si_ga = (v1 & 0xff00ff00); + const uint32_t si_rb = v1 & 0x00ff00ff; + const uint32_t di_rb = v0 & 0x00ff00ff; + const uint32_t d_rb = si_rb - di_rb; + const uint32_t di_ga = v0 & 0xff00ff00; + const uint32_t d_ga = (si_ga >>8) - (di_ga>>8); + return + (((di_rb + ((0xff00ff + d_rb * cov)>>8)) & 0x00ff00ff)) | + (((di_ga + (0xff00ff + d_ga * cov)) & 0xff00ff00)); - case CTX_SOURCE_COLOR: return ctx_fragment_color_RGBA8; -#if CTX_GRADIENTS - case CTX_SOURCE_LINEAR_GRADIENT: return ctx_fragment_linear_gradient_RGBA8; - case CTX_SOURCE_RADIAL_GRADIENT: return ctx_fragment_radial_gradient_RGBA8; #endif - } - return ctx_fragment_color_RGBA8; } -static inline void -ctx_init_uv (CtxRasterizer *rasterizer, - int x0, - int y0, - float *u0, float *v0, float *w0, float *ud, float *vd, float *wd) - //float *u0, float *v0, float *w0, float *ud, float *vd, float *wd) +CTX_INLINE static void ctx_lerp_RGBA8_split (const uint32_t v0, const uint32_t v1, const uint8_t dx, + uint32_t *dest_ga, uint32_t *dest_rb) { - CtxMatrix *transform = &rasterizer->state->gstate.source_fill.transform; - *u0 = transform->m[0][0] * (x0 + 0.0f) + - transform->m[0][1] * (y0 + 0.0f) + - transform->m[0][2]; - *v0 = transform->m[1][0] * (x0 + 0.0f) + - transform->m[1][1] * (y0 + 0.0f) + - transform->m[1][2]; - *w0 = transform->m[2][0] * (x0 + 0.0f) + - transform->m[2][1] * (y0 + 0.0f) + - transform->m[2][2]; - *ud = transform->m[0][0]; - *vd = transform->m[1][0]; - *wd = transform->m[2][0]; + const uint32_t cov = dx; + const uint32_t si_ga = v1 & 0xff00ff00; + const uint32_t si_rb = v1 & 0x00ff00ff; + const uint32_t di_ga = v0 & 0xff00ff00; + const uint32_t di_rb = v0 & 0x00ff00ff; + const uint32_t d_rb = si_rb - di_rb; + const uint32_t d_ga = (si_ga >>8) - (di_ga >> 8); + *dest_rb = (((di_rb + ((0xff00ff + d_rb * cov)>>8)) & 0x00ff00ff)); + *dest_ga = (((di_ga + (0xff00ff + d_ga * cov)) & 0xff00ff00)); } -static inline void -ctx_u8_copy_normal (int components, CTX_COMPOSITE_ARGUMENTS) +CTX_INLINE static uint32_t ctx_lerp_RGBA8_merge (uint32_t di_ga, uint32_t di_rb, uint32_t si_ga, uint32_t si_rb, const uint8_t dx) { - if (CTX_UNLIKELY(rasterizer->fragment)) - { - 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); - while (count--) - { - uint8_t cov = *coverage; - if (CTX_UNLIKELY(cov == 0)) - { - u0+=ud; - v0+=vd; - } - else - { - rasterizer->fragment (rasterizer, u0, v0, w0, src, 1, ud, vd, wd); - u0+=ud; - v0+=vd; - if (cov == 255) - { - for (int c = 0; c < components; c++) - dst[c] = src[c]; - } - else - { - uint8_t rcov = 255 - cov; - for (int c = 0; c < components; c++) - { dst[c] = (src[c]*cov + dst[c]*rcov)/255; } - } - } - dst += components; - coverage ++; - } - return; - } + const uint32_t cov = dx; + const uint32_t d_rb = si_rb - di_rb; + const uint32_t d_ga = (si_ga >> 8) - (di_ga >> 8); + return + (((di_rb + ((0xff00ff + d_rb * cov)>>8)) & 0x00ff00ff)) | + ((di_ga + ((0xff00ff + d_ga * cov) & 0xff00ff00))); +} - while (count--) - { - uint8_t cov = *coverage; - uint8_t rcov = 255-cov; - for (int c = 0; c < components; c++) - { dst[c] = (src[c]*cov+dst[c]*rcov)/255; } - dst += components; - coverage ++; - } +CTX_INLINE static uint32_t ctx_lerp_RGBA8_2 (const uint32_t v0, uint32_t si_ga, uint32_t si_rb, const uint8_t dx) +{ + const uint32_t cov = dx; + const uint32_t di_ga = ( v0 & 0xff00ff00); + const uint32_t di_rb = v0 & 0x00ff00ff; + const uint32_t d_rb = si_rb - di_rb; + const uint32_t d_ga = si_ga - (di_ga>>8); + return + (((di_rb + ((0xff00ff + d_rb * cov)>>8)) & 0x00ff00ff)) | + (((di_ga + (0xff00ff + d_ga * cov)) & 0xff00ff00)); } -static void -ctx_u8_clear_normal (int components, CTX_COMPOSITE_ARGUMENTS) +CTX_INLINE static float +ctx_lerpf (float v0, float v1, float dx) { - while (count--) - { - uint8_t cov = *coverage; - for (int c = 0; c < components; c++) - { dst[c] = (dst[c] * (256-cov)) >> 8; } - coverage ++; - dst += components; - } + return v0 + (v1-v0) * dx; } -typedef enum { - CTX_PORTER_DUFF_0, - CTX_PORTER_DUFF_1, - CTX_PORTER_DUFF_ALPHA, - CTX_PORTER_DUFF_1_MINUS_ALPHA, -} CtxPorterDuffFactor; +CTX_INLINE static float +ctx_catmull_rom (float v0, float v1, float v2, float v3, float t) +{ + float ya = v0, yb = v1, yc = v2, yd = v3; + float a3 = 0.5f * (-ya + 3 * yb - 3 * yc + yd); + float a2 = 0.5f * (2 * ya - 5 * yb + 4 * yc - yd); + float a1 = 0.5f * (-ya + yc); + float a0 = yb; + return a3 * t * t * t + + a2 * t * t + + a1 * t + + a0; +} -#define \ -ctx_porter_duff_factors(mode, foo, bar)\ -{\ - switch (mode)\ - {\ - case CTX_COMPOSITE_SOURCE_ATOP:\ - f_s = CTX_PORTER_DUFF_ALPHA;\ - f_d = CTX_PORTER_DUFF_1_MINUS_ALPHA;\ - break;\ - case CTX_COMPOSITE_DESTINATION_ATOP:\ - f_s = CTX_PORTER_DUFF_1_MINUS_ALPHA;\ - f_d = CTX_PORTER_DUFF_ALPHA;\ - break;\ - case CTX_COMPOSITE_DESTINATION_IN:\ - f_s = CTX_PORTER_DUFF_0;\ - f_d = CTX_PORTER_DUFF_ALPHA;\ - break;\ - case CTX_COMPOSITE_DESTINATION:\ - f_s = CTX_PORTER_DUFF_0;\ - f_d = CTX_PORTER_DUFF_1;\ - break;\ - case CTX_COMPOSITE_SOURCE_OVER:\ - f_s = CTX_PORTER_DUFF_1;\ - f_d = CTX_PORTER_DUFF_1_MINUS_ALPHA;\ - break;\ - case CTX_COMPOSITE_DESTINATION_OVER:\ - f_s = CTX_PORTER_DUFF_1_MINUS_ALPHA;\ - f_d = CTX_PORTER_DUFF_1;\ - break;\ - case CTX_COMPOSITE_XOR:\ - f_s = CTX_PORTER_DUFF_1_MINUS_ALPHA;\ - f_d = CTX_PORTER_DUFF_1_MINUS_ALPHA;\ - break;\ - case CTX_COMPOSITE_DESTINATION_OUT:\ - f_s = CTX_PORTER_DUFF_0;\ - f_d = CTX_PORTER_DUFF_1_MINUS_ALPHA;\ - break;\ - case CTX_COMPOSITE_SOURCE_OUT:\ - f_s = CTX_PORTER_DUFF_1_MINUS_ALPHA;\ - f_d = CTX_PORTER_DUFF_0;\ - break;\ - case CTX_COMPOSITE_SOURCE_IN:\ - f_s = CTX_PORTER_DUFF_ALPHA;\ - f_d = CTX_PORTER_DUFF_0;\ - break;\ - case CTX_COMPOSITE_COPY:\ - f_s = CTX_PORTER_DUFF_1;\ - f_d = CTX_PORTER_DUFF_1_MINUS_ALPHA;\ - break;\ - default:\ - case CTX_COMPOSITE_CLEAR:\ - f_s = CTX_PORTER_DUFF_0;\ - f_d = CTX_PORTER_DUFF_0;\ - break;\ - }\ -} - -static inline void -ctx_u8_source_over_normal_color (int components, - CtxRasterizer *rasterizer, - uint8_t * __restrict__ dst, - uint8_t * __restrict__ src, - int x0, - uint8_t * __restrict__ coverage, - int count) +CTX_INLINE static float +ctx_catmull_rom_left (float v0, float v1, float v2, float t) { - uint8_t tsrc[5]; - *((uint32_t*)tsrc) = *((uint32_t*)src); - - while (count--) - { - uint8_t cov = *coverage++; - for (int c = 0; c < components; c++) - dst[c] = ((((tsrc[c] * cov)) + (dst[c] * (((((255+(tsrc[components-1] * cov))>>8))^255 ))))>>8); - dst+=components; - } + float ya = v0, yb = v1, yc = v2; + float a2 = 0.5f * (ya - 2 * yb + yc); + float a1 = 0.5f * (-3 * ya + 4 * yb - yc); + float a0 = ya; + return a2 * t * t + + a1 * t + + a0; } -static inline void -ctx_u8_source_copy_normal_color (int components, CTX_COMPOSITE_ARGUMENTS) +CTX_INLINE static float +ctx_catmull_rom_right (float v0, float v1, float v2, float t) { - while (count--) - { - for (int c = 0; c < components; c++) - dst[c] = ctx_lerp_u8(dst[c],src[c],coverage[0]); - coverage ++; - dst+=components; - } + float ya = v0, yb = v1, yc = v2; + float a2 = 0.5f * (ya - 2 * yb + yc); + float a1 = 0.5f * (-ya + yc); + float a0 = yb; + return a2 * t * t + + a1 * t + + a0; } -static inline void -ctx_RGBA8_source_over_normal_buf (CTX_COMPOSITE_ARGUMENTS, uint8_t *tsrc) + +#ifndef CTX_MIN +#define CTX_MIN(a,b) (((a)<(b))?(a):(b)) +#endif +#ifndef CTX_MAX +#define CTX_MAX(a,b) (((a)>(b))?(a):(b)) +#endif + +static inline void *ctx_calloc (size_t size, size_t count); + +void ctx_screenshot (Ctx *ctx, const char *output_path); + + +CtxSHA1 *ctx_sha1_new (void); +void ctx_sha1_free (CtxSHA1 *sha1); +int ctx_sha1_process(CtxSHA1 *sha1, const unsigned char * msg, unsigned long len); +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); +void ctx_buffer_pixels_free (void *pixels, void *userdata); + +/*ctx_texture_init: + * return value: eid, as passed in or if NULL generated by hashing pixels and width/height + * XXX this is low-level and not to be used directly use define_texture instead. XXX + */ +const char *ctx_texture_init ( + Ctx *ctx, + const char *eid, + int width, + int height, + int stride, + CtxPixelFormat format, + void *space, + uint8_t *pixels, + void (*freefunc) (void *pixels, void *user_data), + void *user_data); + +#if CTX_TILED +#if !__COSMOPOLITAN__ +//#include <threads.h> +#endif +#endif +typedef struct _CtxTiled CtxTiled; + + +typedef struct _EvSource EvSource; +struct _EvSource { - while (count--) - { - uint32_t si_ga = ((*((uint32_t*)tsrc)) & 0xff00ff00) >> 8; - uint32_t si_rb = (*((uint32_t*)tsrc)) & 0x00ff00ff; -// uint32_t di_ga = ((*((uint32_t*)dst)) & 0xff00ff00) >> 8; -// uint32_t di_rb = (*((uint32_t*)dst)) & 0x00ff00ff; - uint32_t si_a = si_ga >> 16; - uint32_t cov = *coverage; - uint32_t racov = (255-((255+si_a*cov)>>8)); - *((uint32_t*)(dst)) = + void *priv; /* private storage */ - (((si_rb*cov+0xff00ff+(((*((uint32_t*)(dst)))&0x00ff00ff)*racov))>>8)&0x00ff00ff)| - ((si_ga*cov+0xff00ff+((((*((uint32_t*)(dst)))&0xff00ff00)>>8)*racov))&0xff00ff00); + /* returns non 0 if there is events waiting */ + int (*has_event) (EvSource *ev_source); - coverage ++; - tsrc += 4; - dst += 4; - } -} + /* get an event, the returned event should be freed by the caller */ + char *(*get_event) (EvSource *ev_source); -static inline void -ctx_RGBA8_source_over_normal_full_cov_buf (CTX_COMPOSITE_ARGUMENTS, uint8_t *tsrc) + /* destroy/unref this instance */ + void (*destroy) (EvSource *ev_source); + + /* get the underlying fd, useful for using select on */ + int (*get_fd) (EvSource *ev_source); + + + void (*set_coord) (EvSource *ev_source, double x, double y); + /* set_coord is needed to warp relative cursors into normalized range, + * like normal mice/trackpads/nipples - to obey edges and more. + */ + + /* if this returns non-0 select can be used for non-blocking.. */ +}; + +struct _CtxTiled { - uint32_t *ttsrc = (uint32_t*)tsrc; - uint32_t *ddst = (uint32_t*)dst; - while (count--) - { - uint32_t si_ga = ((*ttsrc) & 0xff00ff00) >> 8; - uint32_t si_rb = (*ttsrc++) & 0x00ff00ff; - uint32_t si_a = si_ga >> 16; - uint32_t racov = si_a^255; - *(ddst) = - (((si_rb*255+0xff00ff+(((*ddst)&0x00ff00ff)*racov))>>8)&0x00ff00ff)| - ((si_ga*255+0xff00ff+((((*ddst)&0xff00ff00)>>8)*racov))&0xff00ff00); - ddst++; - } -} + CtxBackend backend; + void (*show_frame) (void *backend, int block); + int width; + int height; + int cols; + int rows; + int was_down; + uint8_t *pixels; + Ctx *ctx_copy; + Ctx *host[CTX_MAX_THREADS]; + CtxAntialias antialias; + int quit; +#if CTX_TILED + //_Atomic + int thread_quit; +#endif + int shown_frame; + int render_frame; + int rendered_frame[CTX_MAX_THREADS]; + int frame; + int min_col; // hasher cols and rows + int min_row; + int max_col; + int max_row; + uint32_t hashes[CTX_HASH_ROWS * CTX_HASH_COLS]; + int8_t tile_affinity[CTX_HASH_ROWS * CTX_HASH_COLS]; // which render thread no is + // responsible for a tile + // -static inline void -ctx_RGBA8_source_copy_normal_buf (CTX_COMPOSITE_ARGUMENTS, uint8_t *__restrict__ tsrc) + int pointer_down[3]; + + CtxCursor shown_cursor; + int vt_active; + EvSource *evsource[4]; + int evsource_count; + uint8_t *fb; +#if CTX_THREADS +#if CTX_TILED + cnd_t cond; + mtx_t mtx; +#endif +#endif +}; + +static inline Ctx *ctx_backend_get_ctx (void *backend) { - uint32_t *ttsrc = (uint32_t*)tsrc; - uint32_t *ddst = (uint32_t*)dst; - while (count--) - { - *ddst=ctx_lerp_RGBA8 (*ddst, *(ttsrc++), *(coverage++)); - ddst++; - } + CtxBackend *r = (CtxBackend*)backend; + if (r) return r->ctx; + return NULL; } +void +_ctx_texture_prepare_color_management (CtxState *state, + CtxBuffer *buffer); + +int ctx_is_set (Ctx *ctx, uint32_t hash); + +static Ctx *_ctx_new_drawlist (int width, int height); + + static inline void -ctx_RGBA8_source_over_normal_fragment (CTX_COMPOSITE_ARGUMENTS) +_ctx_matrix_apply_transform (const CtxMatrix *m, float *x, float *y) { - 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); - 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]); + float x_in = *x; + 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; } + + static inline void -ctx_RGBA8_source_over_normal_full_cov_fragment (CTX_COMPOSITE_ARGUMENTS, int scanlines) +_ctx_matrix_multiply (CtxMatrix *result, + const CtxMatrix *t, + const CtxMatrix *s) { - CtxMatrix *transform = &rasterizer->state->gstate.source_fill.transform; - int scan = rasterizer->scanline /CTX_FULL_AA; + CtxMatrix r; - if (CTX_LIKELY(ctx_matrix_no_perspective (transform))) - { - float u0, v0, ud, vd, w0, wd; - 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]); - u0 -= vd; - v0 += ud; - dst += rasterizer->blit_stride; - } - } - else + for (unsigned int i = 0; i < 3; i++) { - 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]); - dst += rasterizer->blit_stride; - } + r.m[i][0] = t->m[i][0] * s->m[0][0] + + t->m[i][1] * s->m[1][0] + + t->m[i][2] * s->m[2][0]; + r.m[i][1] = t->m[i][0] * s->m[0][1] + + t->m[i][1] * s->m[1][1] + + t->m[i][2] * s->m[2][1]; + r.m[i][2] = t->m[i][0] * s->m[0][2] + + t->m[i][1] * s->m[1][2] + + t->m[i][2] * s->m[2][2]; } + *result = r; } static inline void -ctx_RGBA8_source_copy_normal_fragment (CTX_COMPOSITE_ARGUMENTS) +_ctx_matrix_identity (CtxMatrix *matrix) { - 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); - 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]); + matrix->m[0][0] = 1.0f; + matrix->m[0][1] = 0.0f; + matrix->m[0][2] = 0.0f; + matrix->m[1][0] = 0.0f; + matrix->m[1][1] = 1.0f; + matrix->m[1][2] = 0.0f; + matrix->m[2][0] = 0.0f; + matrix->m[2][1] = 0.0f; + matrix->m[2][2] = 1.0f; } +static inline void +_ctx_user_to_device_prepped (CtxState *state, float x, float y, int *out_x, int *out_y); +static inline void +_ctx_user_to_device_prepped_fixed (CtxState *state, int x, int y, int *x_out, int *y_out); -static void -ctx_RGBA8_source_over_normal_color (CTX_COMPOSITE_ARGUMENTS) -{ -#if CTX_REFERENCE - ctx_u8_source_over_normal_color (4, rasterizer, dst, src, x0, coverage, count); -#else - uint32_t si_ga = ((uint32_t*)rasterizer->color)[1]; - uint32_t si_rb = ((uint32_t*)rasterizer->color)[2]; - uint32_t si_a = si_ga >> 16; +static int ctx_float_to_string_index (float val); - while (count--) - { - uint32_t cov = *coverage++; - uint32_t rcov = (((255+si_a * cov)>>8))^255; - uint32_t di = *((uint32_t*)dst); - uint32_t di_ga = ((di & 0xff00ff00) >> 8); - uint32_t di_rb = (di & 0x00ff00ff); - *((uint32_t*)(dst)) = - (((si_rb * cov + 0xff00ff + di_rb * rcov) & 0xff00ff00) >> 8) | - ((si_ga * cov + 0xff00ff + di_ga * rcov) & 0xff00ff00); - dst+=4; - } -#endif -} +void +ctx_render_ctx_masked (Ctx *ctx, Ctx *d_ctx, uint32_t mask); -static void -ctx_RGBA8_source_copy_normal_color (CTX_COMPOSITE_ARGUMENTS) -{ -#if CTX_REFERENCE - ctx_u8_source_copy_normal_color (4, rasterizer, dst, src, x0, coverage, count); -#else - uint32_t si_ga = ((uint32_t*)rasterizer->color)[1]; - uint32_t si_rb = ((uint32_t*)rasterizer->color)[2]; +static void ctx_state_set_blob (CtxState *state, uint32_t key, uint8_t *data, int len); - while (count--) - { - uint32_t cov = *coverage++; - uint32_t di = *((uint32_t*)dst); - uint32_t di_ga = (di & 0xff00ff00); - uint32_t di_rb = (di & 0x00ff00ff); - uint32_t d_rb = si_rb - di_rb; - uint32_t d_ga = si_ga - (di_ga>>8); +static inline void +_ctx_transform_prime (CtxState *state); - *((uint32_t*)(dst)) = +void ctx_push_backend (Ctx *ctx, + void *backend); +void ctx_pop_backend (Ctx *ctx); - (((di_rb + ((d_rb * cov)>>8)) & 0x00ff00ff)) | - ((di_ga + ((d_ga * cov) & 0xff00ff00))); - dst +=4; - } -#endif -} -static void -ctx_RGBA8_clear_normal (CTX_COMPOSITE_ARGUMENTS) +static inline float ctx_fmod1f (float val) { - ctx_u8_clear_normal (4, rasterizer, dst, src, x0, coverage, count); + return ctx_fabsf (val - (int)(val)); } -static void -ctx_u8_blend_normal (int components, uint8_t * __restrict__ dst, uint8_t *src, uint8_t *blended, int count) +static inline float ctx_fmodf (float val, float modulus) { - for (int j = 0; j < count; j++) - { - switch (components) - { - case 3: - ((uint8_t*)(blended))[2] = ((uint8_t*)(src))[2]; - *((uint16_t*)(blended)) = *((uint16_t*)(src)); - break; - case 2: - *((uint16_t*)(blended)) = *((uint16_t*)(src)); - break; - case 5: - *((uint32_t*)(blended)) = *((uint32_t*)(src)); - ((uint8_t*)(blended))[4] = ((uint8_t*)(src))[4]; - break; - case 4: - *((uint32_t*)(blended)) = *((uint32_t*)(src)); - break; - default: - { - for (int i = 0; i<components;i++) - blended[i] = src[i]; - } - break; - } - blended+=components; - src+=components; - } + return ctx_fmod1f(val/modulus) * modulus; } -/* branchless 8bit add that maxes out at 255 */ -static inline uint8_t ctx_sadd8(uint8_t a, uint8_t b) -{ - uint16_t s = (uint16_t)a+b; - return -(s>>8) | (uint8_t)s; -} +#if EMSCRIPTEN +#define CTX_EXPORT EMSCRIPTEN_KEEPALIVE +#else +#define CTX_EXPORT +#endif -#if CTX_BLENDING_AND_COMPOSITING -#define ctx_u8_blend_define(name, CODE) \ -static inline void \ -ctx_u8_blend_##name (int components, uint8_t * __restrict__ dst, uint8_t *src, uint8_t *blended, int count)\ -{\ - for (int j = 0; j < count; j++) { \ - uint8_t *s=src; uint8_t b[components];\ - ctx_u8_deassociate_alpha (components, dst, b);\ - CODE;\ - blended[components-1] = src[components-1];\ - ctx_u8_associate_alpha (components, blended);\ - src += components;\ - dst += components;\ - blended += components;\ - }\ -} -#define ctx_u8_blend_define_seperable(name, CODE) \ - ctx_u8_blend_define(name, for (int c = 0; c < components-1; c++) { CODE ;}) \ +#endif -ctx_u8_blend_define_seperable(multiply, blended[c] = (b[c] * s[c])/255;) -ctx_u8_blend_define_seperable(screen, blended[c] = s[c] + b[c] - (s[c] * b[c])/255;) -ctx_u8_blend_define_seperable(overlay, blended[c] = b[c] < 127 ? (s[c] * b[c])/255 : - s[c] + b[c] - (s[c] * b[c])/255;) -ctx_u8_blend_define_seperable(darken, blended[c] = ctx_mini (b[c], s[c])) -ctx_u8_blend_define_seperable(lighten, blended[c] = ctx_maxi (b[c], s[c])) -ctx_u8_blend_define_seperable(color_dodge, blended[c] = b[c] == 0 ? 0 : - s[c] == 255 ? 255 : ctx_mini(255, (255 * b[c]) / (255-s[c]))) -ctx_u8_blend_define_seperable(color_burn, blended[c] = b[c] == 1 ? 1 : - s[c] == 0 ? 0 : 255 - ctx_mini(255, (255*(255 - b[c])) / s[c])) -ctx_u8_blend_define_seperable(hard_light, blended[c] = s[c] < 127 ? (b[c] * s[c])/255 : - b[c] + s[c] - (b[c] * s[c])/255;) -ctx_u8_blend_define_seperable(difference, blended[c] = (b[c] - s[c])) -ctx_u8_blend_define_seperable(divide, blended[c] = s[c]?(255 * b[c]) / s[c]:0) -ctx_u8_blend_define_seperable(addition, blended[c] = ctx_sadd8 (s[c], b[c])) -ctx_u8_blend_define_seperable(subtract, blended[c] = ctx_maxi(0, s[c]-b[c])) -ctx_u8_blend_define_seperable(exclusion, blended[c] = b[c] + s[c] - 2 * (b[c] * s[c]/255)) -ctx_u8_blend_define_seperable(soft_light, - if (s[c] <= 255/2) - { - blended[c] = b[c] - (255 - 2 * s[c]) * b[c] * (255 - b[c]) / (255 * 255); - } - else - { - int d; - if (b[c] <= 255/4) - d = (((16 * b[c] - 12 * 255)/255 * b[c] + 4 * 255) * b[c])/255; - else - d = (int)(ctx_sqrtf(b[c]/255.0f) * 255.4f); - blended[c] = (b[c] + (2 * s[c] - 255) * (d - b[c]))/255; - } -) +#if CTX_EVENTS +#include <sys/select.h> +#endif +#ifndef CTX_DRAWLIST_H +#define CTX_DRAWLIST_H -static int ctx_int_get_max (int components, int *c) -{ - int max = 0; - for (int i = 0; i < components - 1; i ++) - { - if (c[i] > max) max = c[i]; - } - return max; -} +static int +ctx_conts_for_entry (CtxEntry *entry); +void +ctx_iterator_init (CtxIterator *iterator, + CtxDrawlist *drawlist, + int start_pos, + int flags); -static int ctx_int_get_min (int components, int *c) -{ - int min = 400; - for (int i = 0; i < components - 1; i ++) - { - if (c[i] < min) min = c[i]; - } - return min; -} +int ctx_iterator_pos (CtxIterator *iterator); -static int ctx_int_get_lum (int components, int *c) -{ - switch (components) - { - case 3: - case 4: - return (int)(CTX_CSS_RGB_TO_LUMINANCE(c)); - case 1: - case 2: - return c[0]; - break; - default: - { - int sum = 0; - for (int i = 0; i < components - 1; i ++) - { - sum += c[i]; - } - return sum / (components - 1); - } - break; - } -} +static 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); +int +ctx_drawlist_insert_entry (CtxDrawlist *drawlist, int pos, CtxEntry *entry); +int +ctx_add_data (Ctx *ctx, void *data, int length); -static int ctx_u8_get_lum (int components, uint8_t *c) -{ - switch (components) - { - case 3: - case 4: - return (int)(CTX_CSS_RGB_TO_LUMINANCE(c)); - case 1: - case 2: - return c[0]; - break; - default: - { - int sum = 0; - for (int i = 0; i < components - 1; i ++) - { - sum += c[i]; - } - return sum / (components - 1); - } - break; - } -} -static int ctx_u8_get_sat (int components, uint8_t *c) -{ - switch (components) - { - case 3: - case 4: - { int r = c[0]; - int g = c[1]; - int b = c[2]; - return ctx_maxi(r, ctx_maxi(g,b)) - ctx_mini(r,ctx_mini(g,b)); - } - break; - case 1: - case 2: - return 0.0; - break; - default: - { - int min = 1000; - int max = -1000; - for (int i = 0; i < components - 1; i ++) - { - if (c[i] < min) min = c[i]; - if (c[i] > max) max = c[i]; - } - return max-min; - } - break; - } -} +int ctx_drawlist_add_u32 (CtxDrawlist *drawlist, CtxCode code, uint32_t u32[2]); +int ctx_drawlist_add_data (CtxDrawlist *drawlist, const void *data, int length); -static void ctx_u8_set_lum (int components, uint8_t *c, uint8_t lum) -{ - int d = lum - ctx_u8_get_lum (components, c); - int tc[components]; - for (int i = 0; i < components - 1; i++) - { - tc[i] = c[i] + d; - } +static CtxEntry +ctx_void (CtxCode code); +static inline CtxEntry +ctx_f (CtxCode code, float x, float y); +static CtxEntry +ctx_u32 (CtxCode code, uint32_t x, uint32_t y); +#if 0 +static CtxEntry +ctx_s32 (CtxCode code, int32_t x, int32_t y); +#endif - int l = ctx_int_get_lum (components, tc); - int n = ctx_int_get_min (components, tc); - int x = ctx_int_get_max (components, tc); +static inline CtxEntry +ctx_s16 (CtxCode code, int x0, int y0, int x1, int y1); +static CtxEntry +ctx_u8 (CtxCode code, + uint8_t a, uint8_t b, uint8_t c, uint8_t d, + uint8_t e, uint8_t f, uint8_t g, uint8_t h); - if (n < 0 && l!=n) - { - for (int i = 0; i < components - 1; i++) - tc[i] = l + (((tc[i] - l) * l) / (l-n)); - } +#define CTX_PROCESS_VOID(cmd) do {\ + CtxEntry commands[4] = {{cmd,{{0}}}};\ + ctx_process (ctx, &commands[0]);}while(0) \ - if (x > 255 && x!=l) - { - for (int i = 0; i < components - 1; i++) - tc[i] = l + (((tc[i] - l) * (255 - l)) / (x-l)); - } - for (int i = 0; i < components - 1; i++) - c[i] = tc[i]; -} +#define CTX_PROCESS_F(cmd,x,y) do {\ + CtxEntry commands[4] = {ctx_f(cmd,x,y),};\ + ctx_process (ctx, &commands[0]);}while(0) \ -static void ctx_u8_set_sat (int components, uint8_t *c, uint8_t sat) -{ - int max = 0, mid = 1, min = 2; - - if (c[min] > c[mid]){int t = min; min = mid; mid = t;} - if (c[mid] > c[max]){int t = mid; mid = max; max = t;} - if (c[min] > c[mid]){int t = min; min = mid; mid = t;} +#define CTX_PROCESS_F1(cmd,x) do {\ + CtxEntry commands[4] = {ctx_f(cmd,x,0),};\ + ctx_process (ctx, &commands[0]);}while(0) \ - if (c[max] > c[min]) - { - c[mid] = ((c[mid]-c[min]) * sat) / (c[max] - c[min]); - c[max] = sat; - } - else - { - c[mid] = c[max] = 0; - } - c[min] = 0; -} +#define CTX_PROCESS_U32(cmd, x, y) do {\ + CtxEntry commands[4] = {ctx_u32(cmd, x, y)};\ + ctx_process (ctx, &commands[0]);}while(0) -ctx_u8_blend_define(color, - for (int i = 0; i < components; i++) - blended[i] = s[i]; - ctx_u8_set_lum(components, blended, ctx_u8_get_lum (components, s)); -) +#define CTX_PROCESS_U8(cmd, x) do {\ + CtxEntry commands[4] = {ctx_u8(cmd, x,0,0,0,0,0,0,0)};\ + ctx_process (ctx, &commands[0]);}while(0) -ctx_u8_blend_define(hue, - int in_sat = ctx_u8_get_sat(components, b); - int in_lum = ctx_u8_get_lum(components, b); - for (int i = 0; i < components; i++) - blended[i] = s[i]; - ctx_u8_set_sat(components, blended, in_sat); - ctx_u8_set_lum(components, blended, in_lum); -) -ctx_u8_blend_define(saturation, - int in_sat = ctx_u8_get_sat(components, s); - int in_lum = ctx_u8_get_lum(components, b); - for (int i = 0; i < components; i++) - blended[i] = b[i]; - ctx_u8_set_sat(components, blended, in_sat); - ctx_u8_set_lum(components, blended, in_lum); -) +#if CTX_BITPACK_PACKER +static unsigned int +ctx_last_history (CtxDrawlist *drawlist); +#endif -ctx_u8_blend_define(luminosity, - int in_lum = ctx_u8_get_lum(components, s); - for (int i = 0; i < components; i++) - blended[i] = b[i]; - ctx_u8_set_lum(components, blended, in_lum); -) +#if CTX_BITPACK_PACKER +static void +ctx_drawlist_remove_tiny_curves (CtxDrawlist *drawlist, int start_pos); + +static void +ctx_drawlist_bitpack (CtxDrawlist *drawlist, unsigned int start_pos); #endif -CTX_INLINE static void -ctx_u8_blend (int components, CtxBlend blend, uint8_t * __restrict__ dst, uint8_t *src, uint8_t *blended, int count) +static void +ctx_process_cmd_str (Ctx *ctx, CtxCode code, const char *string, uint32_t arg0, uint32_t arg1); +static void +ctx_process_cmd_str_float (Ctx *ctx, CtxCode code, const char *string, float arg0, float arg1); +static void +ctx_process_cmd_str_with_len (Ctx *ctx, CtxCode code, const char *string, uint32_t arg0, uint32_t arg1, int len); + +#pragma pack(push,1) +typedef struct +CtxSegment { +#if CTX_32BIT_SEGMENTS + uint32_t code; +#else + uint16_t code; +#endif + union { +#if CTX_32BIT_SEGMENTS + int32_t s16[4]; +#else + int16_t s16[4]; +#endif + uint32_t u32[2]; + } data; + int32_t val; + int32_t delta; +} CtxSegment; +#pragma pack(pop) + + + +static inline CtxSegment +ctx_segment_s16 (CtxCode code, int x0, int y0, int x1, int y1) { -#if CTX_BLENDING_AND_COMPOSITING - switch (blend) - { - case CTX_BLEND_NORMAL: ctx_u8_blend_normal (components, dst, src, blended, count); break; - case CTX_BLEND_MULTIPLY: ctx_u8_blend_multiply (components, dst, src, blended, count); break; - case CTX_BLEND_SCREEN: ctx_u8_blend_screen (components, dst, src, blended, count); break; - case CTX_BLEND_OVERLAY: ctx_u8_blend_overlay (components, dst, src, blended, count); break; - case CTX_BLEND_DARKEN: ctx_u8_blend_darken (components, dst, src, blended, count); break; - case CTX_BLEND_LIGHTEN: ctx_u8_blend_lighten (components, dst, src, blended, count); break; - case CTX_BLEND_COLOR_DODGE: ctx_u8_blend_color_dodge (components, dst, src, blended, count); break; - case CTX_BLEND_COLOR_BURN: ctx_u8_blend_color_burn (components, dst, src, blended, count); break; - case CTX_BLEND_HARD_LIGHT: ctx_u8_blend_hard_light (components, dst, src, blended, count); break; - case CTX_BLEND_SOFT_LIGHT: ctx_u8_blend_soft_light (components, dst, src, blended, count); break; - case CTX_BLEND_DIFFERENCE: ctx_u8_blend_difference (components, dst, src, blended, count); break; - case CTX_BLEND_EXCLUSION: ctx_u8_blend_exclusion (components, dst, src, blended, count); break; - case CTX_BLEND_COLOR: ctx_u8_blend_color (components, dst, src, blended, count); break; - case CTX_BLEND_HUE: ctx_u8_blend_hue (components, dst, src, blended, count); break; - case CTX_BLEND_SATURATION: ctx_u8_blend_saturation (components, dst, src, blended, count); break; - case CTX_BLEND_LUMINOSITY: ctx_u8_blend_luminosity (components, dst, src, blended, count); break; - case CTX_BLEND_ADDITION: ctx_u8_blend_addition (components, dst, src, blended, count); break; - case CTX_BLEND_DIVIDE: ctx_u8_blend_divide (components, dst, src, blended, count); break; - case CTX_BLEND_SUBTRACT: ctx_u8_blend_subtract (components, dst, src, blended, count); break; - } + 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; +} + +static inline void +ctx_edgelist_resize (CtxDrawlist *drawlist, int desired_size) +{ +#if CTX_DRAWLIST_STATIC + { + static CtxSegment sbuf[CTX_MAX_EDGE_LIST_SIZE]; + drawlist->entries = (CtxEntry*)&sbuf[0]; + drawlist->size = CTX_MAX_EDGE_LIST_SIZE; + } #else - switch (blend) - { - default: ctx_u8_blend_normal (components, dst, src, blended, count); break; - } + int new_size = desired_size; + int min_size = CTX_MIN_JOURNAL_SIZE; + int max_size = CTX_MAX_JOURNAL_SIZE; + { + min_size = CTX_MIN_EDGE_LIST_SIZE; + max_size = CTX_MAX_EDGE_LIST_SIZE; + } + if (CTX_UNLIKELY(drawlist->size == max_size)) + { return; } + new_size = ctx_maxi (new_size, min_size); + //if (new_size < drawlist->count) + // { new_size = drawlist->count + 4; } + new_size = ctx_mini (new_size, max_size); + if (new_size != drawlist->size) + { + int item_size = item_size = sizeof (CtxSegment); + //fprintf (stderr, "growing drawlist %p %i to %d from %d\n", drawlist, flags, new_size, drawlist->size); + if (drawlist->entries) + { + //printf ("grow %p to %d from %d\n", drawlist, new_size, drawlist->size); + CtxEntry *ne = (CtxEntry *) ctx_malloc (item_size * new_size); + memcpy (ne, drawlist->entries, drawlist->size * item_size ); + ctx_free (drawlist->entries); + drawlist->entries = ne; + //drawlist->entries = (CtxEntry*)ctx_malloc (drawlist->entries, item_size * new_size); + } + else + { + //fprintf (stderr, "allocating for %p %d\n", drawlist, new_size); + drawlist->entries = (CtxEntry *) ctx_malloc (item_size * new_size); + } + drawlist->size = new_size; + } + //fprintf (stderr, "drawlist %p is %d\n", drawlist, drawlist->size); #endif } -CTX_INLINE static void -__ctx_u8_porter_duff (CtxRasterizer *rasterizer, - int components, - uint8_t * dst, - uint8_t * src, - int x0, - uint8_t * __restrict__ coverage, - int count, - CtxCompositingMode compositing_mode, - CtxFragment fragment, - CtxBlend blend) + +static inline int +ctx_edgelist_add_single (CtxDrawlist *drawlist, CtxEntry *entry) { - CtxPorterDuffFactor f_s, f_d; - ctx_porter_duff_factors (compositing_mode, &f_s, &f_d); - CtxGState *gstate = &rasterizer->state->gstate; - uint8_t global_alpha_u8 = gstate->global_alpha_u8; - uint8_t tsrc[components * count]; - int src_step = 0; + int ret = drawlist->count; - if (gstate->source_fill.type == CTX_SOURCE_COLOR) - { - src = &tsrc[0]; - memcpy (src, rasterizer->color, 4); - if (blend != CTX_BLEND_NORMAL) - ctx_u8_blend (components, blend, dst, src, src, 1); - } - else - { - float u0 = 0; float v0 = 0; - float ud = 0; float vd = 0; - float w0 = 1; float wd = 0; - src = &tsrc[0]; + if (CTX_UNLIKELY(ret >= CTX_MAX_EDGE_LIST_SIZE- 20)) + { + return 0; + } + if (CTX_UNLIKELY(ret + 2 >= drawlist->size)) + { + int new_ = ctx_maxi (drawlist->size * 2, ret + 1024); + new_ = ctx_mini (CTX_MAX_EDGE_LIST_SIZE, new_); + ctx_edgelist_resize (drawlist, new_); + } - ctx_init_uv (rasterizer, x0, rasterizer->scanline/CTX_FULL_AA, &u0, &v0, &w0, &ud, &vd, &wd); - fragment (rasterizer, u0, v0, w0, src, count, ud, vd, wd); - if (blend != CTX_BLEND_NORMAL) - ctx_u8_blend (components, blend, dst, src, src, count); - src_step = components; - } + ((CtxSegment*)(drawlist->entries))[ret] = *(CtxSegment*)entry; + drawlist->count++; + return ret; +} - while (count--) - { - uint32_t cov = *coverage; - if (CTX_UNLIKELY(global_alpha_u8 != 255)) - cov = (cov * global_alpha_u8 + 255) >> 8; +#endif - uint8_t csrc[components]; - for (int c = 0; c < components; c++) - csrc[c] = (src[c] * cov + 255) >> 8; - for (int c = 0; c < components; c++) - { - uint32_t res = 0; +#if CTX_COMPOSITE + +#define CTX_FULL_AA 15 +#define CTX_REFERENCE 0 + + +#define CTX_RGBA8_R_SHIFT 0 +#define CTX_RGBA8_G_SHIFT 8 +#define CTX_RGBA8_B_SHIFT 16 +#define CTX_RGBA8_A_SHIFT 24 + +#define CTX_RGBA8_R_MASK (0xff << CTX_RGBA8_R_SHIFT) +#define CTX_RGBA8_G_MASK (0xff << CTX_RGBA8_G_SHIFT) +#define CTX_RGBA8_B_MASK (0xff << CTX_RGBA8_B_SHIFT) +#define CTX_RGBA8_A_MASK (0xff << CTX_RGBA8_A_SHIFT) + +#define CTX_RGBA8_RB_MASK (CTX_RGBA8_R_MASK | CTX_RGBA8_B_MASK) +#define CTX_RGBA8_GA_MASK (CTX_RGBA8_G_MASK | CTX_RGBA8_A_MASK) + + + +CTX_INLINE static void +ctx_RGBA8_associate_alpha (uint8_t *u8) +{ #if 1 - switch (f_s) - { - case CTX_PORTER_DUFF_0: break; - case CTX_PORTER_DUFF_1: res += (csrc[c] ); break; - case CTX_PORTER_DUFF_ALPHA: res += (csrc[c] * dst[components-1] + 255) >> 8; break; - case CTX_PORTER_DUFF_1_MINUS_ALPHA: res += (csrc[c] * (256-dst[components-1])) >> 8; break; - } - switch (f_d) - { - case CTX_PORTER_DUFF_0: break; - case CTX_PORTER_DUFF_1: res += dst[c]; break; - case CTX_PORTER_DUFF_ALPHA: res += (dst[c] * csrc[components-1] + 255) >> 8; break; - case CTX_PORTER_DUFF_1_MINUS_ALPHA: res += (dst[c] * (256-csrc[components-1])) >> 8; break; - } + uint32_t val = *((uint32_t*)(u8)); + uint32_t a = u8[3]; + uint32_t g = (((val & CTX_RGBA8_G_MASK) * a) >> 8) & CTX_RGBA8_G_MASK; + uint32_t rb =(((val & CTX_RGBA8_RB_MASK) * a) >> 8) & CTX_RGBA8_RB_MASK; + *((uint32_t*)(u8)) = g|rb|(a << CTX_RGBA8_A_SHIFT); #else - switch (f_s) - { - case CTX_PORTER_DUFF_0: break; - case CTX_PORTER_DUFF_1: res += (csrc[c] ); break; - case CTX_PORTER_DUFF_ALPHA: res += (csrc[c] * dst[components-1])/255; break; - case CTX_PORTER_DUFF_1_MINUS_ALPHA: res += (csrc[c] * (255-dst[components-1]))/255; break; - } - switch (f_d) - { - case CTX_PORTER_DUFF_0: break; - case CTX_PORTER_DUFF_1: res += dst[c]; break; - case CTX_PORTER_DUFF_ALPHA: res += (dst[c] * csrc[components-1])/255; break; - case CTX_PORTER_DUFF_1_MINUS_ALPHA: res += (dst[c] * (255-csrc[components-1]))/255; break; - } + uint32_t a = u8[3]; + u8[0] = (u8[0] * a + 255) >> 8; + u8[1] = (u8[1] * a + 255) >> 8; + u8[2] = (u8[2] * a + 255) >> 8; #endif - dst[c] = res; - } - coverage ++; - src+=src_step; - dst+=components; - } +} + +inline static void +ctx_RGBA8_associate_global_alpha (uint8_t *u8, uint8_t global_alpha) +{ + uint32_t val = *((uint32_t*)(u8)); + uint32_t a = (u8[3] * global_alpha + 255) >> 8; + uint32_t g = (((val & CTX_RGBA8_G_MASK) * a) >> 8) & CTX_RGBA8_G_MASK; + uint32_t rb =(((val & CTX_RGBA8_RB_MASK) * a) >> 8) & CTX_RGBA8_RB_MASK; + *((uint32_t*)(u8)) = g|rb|(a << CTX_RGBA8_A_SHIFT); +} + +inline static uint32_t +ctx_RGBA8_associate_global_alpha_u32 (uint32_t val, uint8_t global_alpha) +{ + uint32_t a = ((val>>24) * global_alpha + 255) >> 8; + uint32_t g = (((val & CTX_RGBA8_G_MASK) * a) >> 8) & CTX_RGBA8_G_MASK; + uint32_t rb =(((val & CTX_RGBA8_RB_MASK) * a) >> 8) & CTX_RGBA8_RB_MASK; + return g|rb|(a << CTX_RGBA8_A_SHIFT); } CTX_INLINE static void -_ctx_u8_porter_duff (CtxRasterizer *rasterizer, - int components, - uint8_t * dst, - uint8_t * __restrict__ src, - int x0, - uint8_t * coverage, - int count, - CtxCompositingMode compositing_mode, - CtxFragment fragment, - CtxBlend blend) +ctx_RGBA8_associate_alpha_probably_opaque (uint8_t *u8) { - __ctx_u8_porter_duff (rasterizer, components, dst, src, x0, coverage, count, compositing_mode, fragment, blend); + 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; + } } -#define _ctx_u8_porter_duffs(comp_format, components, source, fragment, blend) \ - switch (rasterizer->state->gstate.compositing_mode) \ - { \ - case CTX_COMPOSITE_SOURCE_ATOP: \ - __ctx_u8_porter_duff (rasterizer, components, dst, src, x0, coverage, count, \ - CTX_COMPOSITE_SOURCE_ATOP, fragment, blend);\ - break;\ - case CTX_COMPOSITE_DESTINATION_ATOP:\ - __ctx_u8_porter_duff (rasterizer, components, dst, src, x0, coverage, count,\ - CTX_COMPOSITE_DESTINATION_ATOP, fragment, blend);\ - break;\ - case CTX_COMPOSITE_DESTINATION_IN:\ - __ctx_u8_porter_duff (rasterizer, components, dst, src, x0, coverage, count,\ - CTX_COMPOSITE_DESTINATION_IN, fragment, blend);\ - break;\ - case CTX_COMPOSITE_DESTINATION:\ - __ctx_u8_porter_duff (rasterizer, components, dst, src, x0, coverage, count,\ - CTX_COMPOSITE_DESTINATION, fragment, blend);\ - break;\ - case CTX_COMPOSITE_SOURCE_OVER:\ - __ctx_u8_porter_duff (rasterizer, components, dst, src, x0, coverage, count,\ - CTX_COMPOSITE_SOURCE_OVER, fragment, blend);\ - break;\ - case CTX_COMPOSITE_DESTINATION_OVER:\ - __ctx_u8_porter_duff (rasterizer, components, dst, src, x0, coverage, count,\ - CTX_COMPOSITE_DESTINATION_OVER, fragment, blend);\ - break;\ - case CTX_COMPOSITE_XOR:\ - __ctx_u8_porter_duff (rasterizer, components, dst, src, x0, coverage, count,\ - CTX_COMPOSITE_XOR, fragment, blend);\ - break;\ - case CTX_COMPOSITE_DESTINATION_OUT:\ - __ctx_u8_porter_duff (rasterizer, components, dst, src, x0, coverage, count,\ - CTX_COMPOSITE_DESTINATION_OUT, fragment, blend);\ - break;\ - case CTX_COMPOSITE_SOURCE_OUT:\ - __ctx_u8_porter_duff (rasterizer, components, dst, src, x0, coverage, count,\ - CTX_COMPOSITE_SOURCE_OUT, fragment, blend);\ - break;\ - case CTX_COMPOSITE_SOURCE_IN:\ - __ctx_u8_porter_duff (rasterizer, components, dst, src, x0, coverage, count,\ - CTX_COMPOSITE_SOURCE_IN, fragment, blend);\ - break;\ - case CTX_COMPOSITE_COPY:\ - __ctx_u8_porter_duff (rasterizer, components, dst, src, x0, coverage, count,\ - CTX_COMPOSITE_COPY, fragment, blend);\ - break;\ - case CTX_COMPOSITE_CLEAR:\ - __ctx_u8_porter_duff (rasterizer, components, dst, src, x0, coverage, count,\ - CTX_COMPOSITE_CLEAR, fragment, blend);\ - break;\ - } - -/* generating one function per compositing_mode would be slightly more efficient, - * but on embedded targets leads to slightly more code bloat, - * here we trade off a slight amount of performance - */ -#define ctx_u8_porter_duff(comp_format, components, source, fragment, blend) \ -static void \ -ctx_##comp_format##_porter_duff_##source (CTX_COMPOSITE_ARGUMENTS) \ -{ \ - _ctx_u8_porter_duffs(comp_format, components, source, fragment, blend);\ -} - -ctx_u8_porter_duff(RGBA8, 4,generic, rasterizer->fragment, rasterizer->state->gstate.blend_mode) -//ctx_u8_porter_duff(comp_name, components,color_##blend_name, NULL, blend_mode) - - -#if CTX_INLINED_NORMAL_RGBA8 - -ctx_u8_porter_duff(RGBA8, 4,color, rasterizer->fragment, rasterizer->state->gstate.blend_mode) - -#if CTX_GRADIENTS -ctx_u8_porter_duff(RGBA8, 4,linear_gradient, ctx_fragment_linear_gradient_RGBA8, rasterizer->state->gstate.blend_mode) -ctx_u8_porter_duff(RGBA8, 4,radial_gradient, ctx_fragment_radial_gradient_RGBA8, rasterizer->state->gstate.blend_mode) +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) +{ +#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 -ctx_u8_porter_duff(RGBA8, 4,image, ctx_fragment_image_RGBA8, rasterizer->state->gstate.blend_mode) +#else + uint32_t s0_ga, s0_rb, s1_ga, s1_rb; + ctx_lerp_RGBA8_split (isrc00, isrc01, dx, &s0_ga, &s0_rb); + ctx_lerp_RGBA8_split (isrc10, isrc11, dx, &s1_ga, &s1_rb); + return ctx_lerp_RGBA8_merge (s0_ga, s0_rb, s1_ga, s1_rb, dy); #endif - - -static inline void -ctx_RGBA8_nop (CTX_COMPOSITE_ARGUMENTS) -{ } +#if CTX_GRADIENTS +#if CTX_GRADIENT_CACHE -static inline void -ctx_setup_native_color (CtxRasterizer *rasterizer) +inline static int ctx_grad_index (CtxRasterizer *rasterizer, float v) { - if (rasterizer->state->gstate.source_fill.type == CTX_SOURCE_COLOR) - { - rasterizer->format->from_comp (rasterizer, 0, - &rasterizer->color[0], - &rasterizer->color_native, - 1); - } + int ret = (int)(v * (rasterizer->gradient_cache_elements - 1) + 0.5f); + ret = ctx_maxi (0, ret); + ret = ctx_mini (rasterizer->gradient_cache_elements-1, ret); + return ret; } -static void -ctx_setup_apply_coverage (CtxRasterizer *rasterizer) +inline static int ctx_grad_index_i (CtxRasterizer *rasterizer, int v) { - rasterizer->apply_coverage = rasterizer->format->apply_coverage ? - rasterizer->format->apply_coverage : - rasterizer->comp_op; + v = v >> 8; + return ctx_maxi (0, ctx_mini (rasterizer->gradient_cache_elements-1, v)); } -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; +//static void +//ctx_gradient_cache_reset (void) +//{ +// ctx_gradient_cache_valid = 0; +//} +#endif - int blend_mode = gstate->blend_mode; - int compositing_mode = gstate->compositing_mode; - if (gstate->source_fill.type == CTX_SOURCE_COLOR) - { - ctx_fragment_color_RGBA8 (rasterizer, 0,0, 1,rasterizer->color, 1, 0,0,0); - if (gstate->global_alpha_u8 != 255) - { - for (int c = 0; c < 4; c ++) - rasterizer->color[c] = (rasterizer->color[c] * gstate->global_alpha_u8 + 255)>>8; - } - uint32_t src_pix = ((uint32_t*)rasterizer->color)[0]; - uint32_t si_ga = (src_pix & 0xff00ff00) >> 8; - uint32_t si_rb = src_pix & 0x00ff00ff; - uint32_t si_ga_full = si_ga * 255; - uint32_t si_rb_full = si_rb * 255; -// uint32_t si_a = si_ga >> 16; +CTX_INLINE static void +_ctx_fragment_gradient_1d_RGBA8 (CtxRasterizer *rasterizer, float x, float y, uint8_t *rgba) +{ + float v = x; + uint8_t global_alpha_u8 = rasterizer->state->gstate.global_alpha_u8; + CtxGradient *g = &rasterizer->state->gradient; + if (v < 0) { v = 0; } + if (v > 1) { v = 1; } - ((uint32_t*)rasterizer->color)[1] = si_ga; - ((uint32_t*)rasterizer->color)[2] = si_rb; - ((uint32_t*)rasterizer->color)[3] = si_ga_full; - ((uint32_t*)rasterizer->color)[4] = si_rb_full; + if (g->n_stops == 0) + { + rgba[0] = rgba[1] = rgba[2] = (int)(v * 255); + rgba[3] = 255; + return; } - -#if CTX_INLINED_NORMAL_RGBA8 - if (gstate->compositing_mode == CTX_COMPOSITE_CLEAR) - rasterizer->comp_op = ctx_RGBA8_clear_normal; - else - switch (gstate->blend_mode) + CtxGradientStop *stop = NULL; + CtxGradientStop *next_stop = &g->stops[0]; + CtxColor *color; + for (int s = 0; s < g->n_stops; s++) { - case CTX_BLEND_NORMAL: - if (gstate->compositing_mode == CTX_COMPOSITE_COPY) - { - rasterizer->comp_op = ctx_RGBA8_copy_normal; - if (gstate->source_fill.type == CTX_SOURCE_COLOR) - rasterizer->comp = CTX_COV_PATH_RGBA8_COPY; - - } - else if (gstate->global_alpha_u8 == 0) - { - rasterizer->comp_op = ctx_RGBA8_nop; - } - else - switch (gstate->source_fill.type) - { - case CTX_SOURCE_COLOR: - if (gstate->compositing_mode == CTX_COMPOSITE_SOURCE_OVER) - { - rasterizer->comp_op = ctx_RGBA8_source_over_normal_color; - if ( ((float*)rasterizer->color)[3] >= 0.999f) - rasterizer->comp = CTX_COV_PATH_RGBA8_COPY; - } - else - { - rasterizer->comp_op = ctx_RGBAF_porter_duff_color_normal; - } - break; -#if CTX_GRADIENTS - case CTX_SOURCE_LINEAR_GRADIENT: - rasterizer->comp_op = ctx_RGBA8_porter_duff_linear_gradient_normal; - break; - case CTX_SOURCE_RADIAL_GRADIENT: - rasterizer->comp_op = ctx_RGBA8_porter_duff_radial_gradient_normal; - break; -#endif - case CTX_SOURCE_TEXTURE: - rasterizer->comp_op = ctx_RGBA8_porter_duff_image_normal; - break; - default: - rasterizer->comp_op = ctx_RGBA8_porter_duff_generic_normal; - break; - } - break; - default: - switch (gstate->source_fill.type) - { - case CTX_SOURCE_COLOR: - rasterizer->comp_op = ctx_RGBA8_porter_duff_color; - break; -#if CTX_GRADIENTS - case CTX_SOURCE_LINEAR_GRADIENT: - rasterizer->comp_op = ctx_RGBA8_porter_duff_linear_gradient; - break; - case CTX_SOURCE_RADIAL_GRADIENT: - rasterizer->comp_op = ctx_RGBA8_porter_duff_radial_gradient; - break; -#endif - case CTX_SOURCE_TEXTURE: - rasterizer->comp_op = ctx_RGBA8_porter_duff_image; - break; - default: - rasterizer->comp_op = ctx_RGBA8_porter_duff_generic; - break; - } - break; + stop = &g->stops[s]; + next_stop = &g->stops[s+1]; + if (s + 1 >= g->n_stops) { next_stop = NULL; } + if (v >= stop->pos && next_stop && v < next_stop->pos) + { break; } + stop = NULL; + next_stop = NULL; } - -#else - - if (gstate->source_fill.type == CTX_SOURCE_COLOR) + if (stop == NULL && next_stop) { - - if (blend_mode == CTX_BLEND_NORMAL) - { - if(compositing_mode == CTX_COMPOSITE_COPY) - { - rasterizer->comp_op = ctx_RGBA8_source_copy_normal_color; - rasterizer->comp = CTX_COV_PATH_RGBA8_COPY; - } - else if (compositing_mode == CTX_COMPOSITE_SOURCE_OVER) - { - if (rasterizer->color[components-1] == 255) - { - rasterizer->comp_op = ctx_RGBA8_source_copy_normal_color; - rasterizer->comp = CTX_COV_PATH_RGBA8_COPY; - } - else - { - rasterizer->comp_op = ctx_RGBA8_source_over_normal_color; - rasterizer->comp = CTX_COV_PATH_RGBA8_OVER; - } - } - } - else if (compositing_mode == CTX_COMPOSITE_CLEAR) - { - rasterizer->comp_op = ctx_RGBA8_clear_normal; - } - } - else if (blend_mode == CTX_BLEND_NORMAL) - { - if(compositing_mode == CTX_COMPOSITE_SOURCE_OVER) + color = & (next_stop->color); + } + else if (stop && next_stop == NULL) { - rasterizer->comp_op = ctx_RGBA8_source_over_normal_fragment; - rasterizer->comp = CTX_COV_PATH_RGBA8_OVER_FRAGMENT; + color = & (stop->color); } - else if (compositing_mode == CTX_COMPOSITE_COPY) + else if (stop && next_stop) { - rasterizer->comp_op = ctx_RGBA8_source_copy_normal_fragment; - rasterizer->comp = CTX_COV_PATH_RGBA8_COPY_FRAGMENT; + uint8_t stop_rgba[4]; + uint8_t next_rgba[4]; + ctx_color_get_rgba8 (rasterizer->state, & (stop->color), stop_rgba); + ctx_color_get_rgba8 (rasterizer->state, & (next_stop->color), next_rgba); + int dx = (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; + ctx_RGBA8_associate_alpha (rgba); + return; } + else + { + color = & (g->stops[g->n_stops-1].color); + } + ctx_color_get_rgba8 (rasterizer->state, color, rgba); + if (rasterizer->swap_red_green) + { + uint8_t tmp = rgba[0]; + rgba[0] = rgba[2]; + rgba[2] = tmp; } -#endif - ctx_setup_apply_coverage (rasterizer); -} - - -static inline void -ctx_setup_RGB (CtxRasterizer *rasterizer) -{ - ctx_setup_RGBA8 (rasterizer); - ctx_setup_native_color (rasterizer); - - rasterizer->comp = CTX_COV_PATH_FALLBACK; + rgba[3]=(rgba[3]*global_alpha_u8+255)>>8; + ctx_RGBA8_associate_alpha (rgba); } - -#if CTX_ENABLE_CBRLE +#if CTX_GRADIENT_CACHE static void -ctx_setup_CBRLE (CtxRasterizer *rasterizer) -{ - ctx_setup_RGBA8 (rasterizer); - //ctx_setup_native_color (rasterizer); - -#if 0 - if (rasterizer->comp == CTX_COV_PATH_RGBA8_COPY) - rasterizer->comp = CTX_COV_PATH_CBRLE_COPY; - else -#endif - rasterizer->comp = CTX_COV_PATH_FALLBACK; -} +ctx_gradient_cache_prime (CtxRasterizer *rasterizer); #endif -#if CTX_ENABLE_RGB332 -static void -ctx_setup_RGB332 (CtxRasterizer *rasterizer) +CTX_INLINE static void +ctx_fragment_gradient_1d_RGBA8 (CtxRasterizer *rasterizer, float x, float y, uint8_t *rgba) { - ctx_setup_RGBA8 (rasterizer); - ctx_setup_native_color (rasterizer); - - if (rasterizer->comp == CTX_COV_PATH_RGBA8_COPY) - rasterizer->comp = CTX_COV_PATH_RGB332_COPY; - else - rasterizer->comp = CTX_COV_PATH_FALLBACK; -} +#if CTX_GRADIENT_CACHE + *((uint32_t*)rgba) = *((uint32_t*)(&rasterizer->gradient_cache_u8[ctx_grad_index(rasterizer, x)][0])); +#else + _ctx_fragment_gradient_1d_RGBA8 (rasterizer, x, y, rgba); #endif - -#if CTX_ENABLE_RGB565 -static void -ctx_setup_RGB565 (CtxRasterizer *rasterizer) -{ - ctx_setup_RGBA8 (rasterizer); - ctx_setup_native_color (rasterizer); - - if (rasterizer->comp == CTX_COV_PATH_RGBA8_COPY) - rasterizer->comp = CTX_COV_PATH_RGB565_COPY; - else - rasterizer->comp = CTX_COV_PATH_FALLBACK; } #endif -#if CTX_ENABLE_RGB8 -static void -ctx_setup_RGB8 (CtxRasterizer *rasterizer) +CTX_INLINE static void +ctx_u8_associate_alpha (int components, uint8_t *u8) { - ctx_setup_RGBA8 (rasterizer); - ctx_setup_native_color (rasterizer); - - if (rasterizer->comp == CTX_COV_PATH_RGBA8_COPY) - rasterizer->comp = CTX_COV_PATH_RGB8_COPY; - else - rasterizer->comp = CTX_COV_PATH_FALLBACK; + for (int c = 0; c < components-1; c++) + u8[c] = (u8[c] * u8[components-1] + 255)>>8; } -#endif +#if CTX_GRADIENTS +#if CTX_GRADIENT_CACHE static void -ctx_composite_convert (CTX_COMPOSITE_ARGUMENTS) -{ - uint8_t pixels[count * rasterizer->format->ebpp]; - rasterizer->format->to_comp (rasterizer, x0, dst, &pixels[0], count); - rasterizer->comp_op (rasterizer, &pixels[0], rasterizer->color, x0, coverage, count); - rasterizer->format->from_comp (rasterizer, x0, &pixels[0], dst, count); -} - -#if CTX_ENABLE_FLOAT -static inline void -ctx_float_copy_normal (int components, CTX_COMPOSITE_ARGUMENTS) +ctx_gradient_cache_prime (CtxRasterizer *rasterizer) { - float *dstf = (float*)dst; - float *srcf = (float*)src; - 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); + // XXX : todo make the number of element dynamic depending on length of gradient + // in device coordinates. - while (count--) - { - uint8_t cov = *coverage; - float covf = ctx_u8_to_float (cov); - for (int c = 0; c < components; c++) - dstf[c] = dstf[c]*(1.0f-covf) + srcf[c]*covf; - dstf += components; - coverage ++; - } -} + if (rasterizer->gradient_cache_valid) + return; + -static inline void -ctx_float_clear_normal (int components, CTX_COMPOSITE_ARGUMENTS) -{ - float *dstf = (float*)dst; - while (count--) { -#if 0 - uint8_t cov = *coverage; - if (cov == 0) - { - } - else if (cov == 255) + CtxSource *source = &rasterizer->state->gstate.source_fill; + float length = 100; + if (source->type == CTX_SOURCE_LINEAR_GRADIENT) { -#endif - switch (components) - { - case 2: - ((uint64_t*)(dst))[0] = 0; - break; - case 4: - ((uint64_t*)(dst))[0] = 0; - ((uint64_t*)(dst))[1] = 0; - break; - default: - for (int c = 0; c < components; c++) - dstf[c] = 0.0f; - } -#if 0 + length = source->linear_gradient.length; } else + if (source->type == CTX_SOURCE_RADIAL_GRADIENT) { - float ralpha = 1.0f - ctx_u8_to_float (cov); - for (int c = 0; c < components; c++) - { dstf[c] = (dstf[c] * ralpha); } + length = ctx_maxf (source->radial_gradient.r1, source->radial_gradient.r0); } - coverage ++; -#endif - dstf += components; - } -} - - -static inline void -ctx_float_source_over_normal_color (int components, CTX_COMPOSITE_ARGUMENTS) -{ - float *dstf = (float*)dst; - float *srcf = (float*)src; - while (count--) + // length = CTX_GRADIENT_CACHE_ELEMENTS; { - uint8_t cov = *coverage; - float fcov = ctx_u8_to_float (cov); - float ralpha = 1.0f - fcov * srcf[components-1]; - for (int c = 0; c < components; c++) - dstf[c] = srcf[c]*fcov + dstf[c] * ralpha; - coverage ++; - dstf+= components; + float u = length; float v = length; + const CtxMatrix *m = &rasterizer->state->gstate.transform; + //CtxMatrix *transform = &source->transform; + // + // combine with above source transform? + _ctx_matrix_apply_transform (m, &u, &v); + length = ctx_maxf (u, v); + } + + rasterizer->gradient_cache_elements = ctx_mini ((int)length, CTX_GRADIENT_CACHE_ELEMENTS); } -} - -static inline void -ctx_float_source_copy_normal_color (int components, CTX_COMPOSITE_ARGUMENTS) -{ - float *dstf = (float*)dst; - float *srcf = (float*)src; - while (count--) + for (int u = 0; u < rasterizer->gradient_cache_elements; u++) { - uint8_t cov = *coverage; - float fcov = ctx_u8_to_float (cov); - float ralpha = 1.0f - fcov; - for (int c = 0; c < components; c++) - dstf[c] = (srcf[c]*fcov + dstf[c] * ralpha); - coverage ++; - dstf+= components; + float v = u / (rasterizer->gradient_cache_elements - 1.0f); + _ctx_fragment_gradient_1d_RGBA8 (rasterizer, v, 0.0f, &rasterizer->gradient_cache_u8[u][0]); + //*((uint32_t*)(&rasterizer->gradient_cache_u8_a[u][0]))= *((uint32_t*)(&rasterizer->gradient_cache_u8[u][0])); + //memcpy(&rasterizer->gradient_cache_u8_a[u][0], &rasterizer->gradient_cache_u8[u][0], 4); + //ctx_RGBA8_associate_alpha (&rasterizer->gradient_cache_u8_a[u][0]); } + rasterizer->gradient_cache_valid = 1; } +#endif -inline static void -ctx_float_blend_normal (int components, float *dst, float *src, float *blended) -{ - float a = src[components-1]; - for (int c = 0; c < components - 1; c++) - blended[c] = src[c] * a; - blended[components-1]=a; +CTX_INLINE static void +ctx_fragment_gradient_1d_GRAYA8 (CtxRasterizer *rasterizer, float x, float y, uint8_t *rgba) +{ + float v = x; + CtxGradient *g = &rasterizer->state->gradient; + if (v < 0) { v = 0; } + if (v > 1) { v = 1; } + if (g->n_stops == 0) + { + rgba[0] = rgba[1] = rgba[2] = (int)(v * 255); + rgba[1] = 255; + return; + } + CtxGradientStop *stop = NULL; + CtxGradientStop *next_stop = &g->stops[0]; + CtxColor *color; + for (int s = 0; s < g->n_stops; s++) + { + stop = &g->stops[s]; + next_stop = &g->stops[s+1]; + if (s + 1 >= g->n_stops) { next_stop = NULL; } + if (v >= stop->pos && next_stop && v < next_stop->pos) + { break; } + stop = NULL; + next_stop = NULL; + } + if (stop == NULL && next_stop) + { + color = & (next_stop->color); + } + else if (stop && next_stop == NULL) + { + color = & (stop->color); + } + else if (stop && next_stop) + { + uint8_t stop_rgba[4]; + uint8_t next_rgba[4]; + ctx_color_get_graya_u8 (rasterizer->state, & (stop->color), stop_rgba); + ctx_color_get_graya_u8 (rasterizer->state, & (next_stop->color), next_rgba); + int dx = (int)((v - stop->pos) * 255 / (next_stop->pos - stop->pos)); + for (int c = 0; c < 2; c++) + { rgba[c] = ctx_lerp_u8 (stop_rgba[c], next_rgba[c], dx); } + return; + } + else + { + color = & (g->stops[g->n_stops-1].color); + } + ctx_color_get_graya_u8 (rasterizer->state, color, rgba); } -static float ctx_float_get_max (int components, float *c) +CTX_INLINE static void +ctx_fragment_gradient_1d_RGBAF (CtxRasterizer *rasterizer, float v, float y, float *rgba) { - float max = -1000.0f; - for (int i = 0; i < components - 1; i ++) - { - if (c[i] > max) max = c[i]; - } - return max; + float global_alpha = rasterizer->state->gstate.global_alpha_f; + CtxGradient *g = &rasterizer->state->gradient; + if (v < 0) { v = 0; } + if (v > 1) { v = 1; } + if (g->n_stops == 0) + { + rgba[0] = rgba[1] = rgba[2] = v; + rgba[3] = 1.0; + return; + } + CtxGradientStop *stop = NULL; + CtxGradientStop *next_stop = &g->stops[0]; + CtxColor *color; + for (int s = 0; s < g->n_stops; s++) + { + stop = &g->stops[s]; + next_stop = &g->stops[s+1]; + if (s + 1 >= g->n_stops) { next_stop = NULL; } + if (v >= stop->pos && next_stop && v < next_stop->pos) + { break; } + stop = NULL; + next_stop = NULL; + } + if (stop == NULL && next_stop) + { + color = & (next_stop->color); + } + else if (stop && next_stop == NULL) + { + color = & (stop->color); + } + else if (stop && next_stop) + { + float stop_rgba[4]; + float next_rgba[4]; + ctx_color_get_rgba (rasterizer->state, & (stop->color), stop_rgba); + ctx_color_get_rgba (rasterizer->state, & (next_stop->color), next_rgba); + int dx = (int)((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; + return; + } + else + { + color = & (g->stops[g->n_stops-1].color); + } + ctx_color_get_rgba (rasterizer->state, color, rgba); + rgba[3] *= global_alpha; } +#endif -static float ctx_float_get_min (int components, float *c) +static void +ctx_fragment_image_RGBA8 (CtxRasterizer *rasterizer, float x, float y, float z, void *out, int count, float dx, float dy, float dw) { - float min = 400.0; - for (int i = 0; i < components - 1; i ++) + uint8_t *rgba = (uint8_t *) out; + CtxSource *g = &rasterizer->state->gstate.source_fill; + CtxBuffer *buffer = g->texture.buffer->color_managed?g->texture.buffer->color_managed:g->texture.buffer; + uint8_t global_alpha_u8 = rasterizer->state->gstate.global_alpha_u8; + + for (int i = 0; i < count; i ++) { - if (c[i] < min) min = c[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) + { + *((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); + + } + 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); + + } + rgba[3] = (rgba[3] * global_alpha_u8) / 255; + } + + } + else + { + uint8_t *src = (uint8_t *) buffer->data; + src += v * buffer->stride + u * bpp; + switch (bpp) + { + case 1: + for (int c = 0; c < 3; c++) + { rgba[c] = src[0]; } + rgba[3] = global_alpha_u8; + break; + case 2: + for (int c = 0; c < 3; c++) + { rgba[c] = src[0]; } + rgba[3] = src[1]; + rgba[3] = (rgba[3] * global_alpha_u8) / 255; + break; + case 3: + for (int c = 0; c < 3; c++) + { rgba[c] = src[c]; } + 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; + break; + } + + } + if (rasterizer->swap_red_green) + { + uint8_t tmp = rgba[0]; + rgba[0] = rgba[2]; + rgba[2] = tmp; + } + } + ctx_RGBA8_associate_alpha_probably_opaque (rgba); + rgba += 4; + x += dx; + y += dy; } - return min; } -static float ctx_float_get_lum (int components, float *c) +#if CTX_DITHER +static inline int ctx_dither_mask_a (int x, int y, int c, int divisor) { - switch (components) - { - case 3: - case 4: - return CTX_CSS_RGB_TO_LUMINANCE(c); - case 1: - case 2: - return c[0]; - break; - default: - { - float sum = 0; - for (int i = 0; i < components - 1; i ++) - { - sum += c[i]; - } - return sum / (components - 1); - } - } + /* https://pippin.gimp.org/a_dither/ */ + return ( ( ( ( (x + c * 67) + y * 236) * 119) & 255 )-127) / divisor; } -static float ctx_float_get_sat (int components, float *c) +inline static void +ctx_dither_rgba_u8 (uint8_t *rgba, int x, int y, int dither_red_blue, int dither_green) { - switch (components) - { - case 3: - case 4: - { float r = c[0]; - float g = c[1]; - float b = c[2]; - return ctx_maxf(r, ctx_maxf(g,b)) - ctx_minf(r,ctx_minf(g,b)); - } - break; - case 1: - case 2: return 0.0; - break; - default: - { - float min = 1000; - float max = -1000; - for (int i = 0; i < components - 1; i ++) - { - if (c[i] < min) min = c[i]; - if (c[i] > max) max = c[i]; - } - return max-min; - } - } + if (dither_red_blue == 0) + { return; } + for (int c = 0; c < 3; c ++) + { + int val = rgba[c] + ctx_dither_mask_a (x, y, 0, c==1?dither_green:dither_red_blue); + rgba[c] = CTX_CLAMP (val, 0, 255); + } } -static void ctx_float_set_lum (int components, float *c, float lum) +inline static void +ctx_dither_graya_u8 (uint8_t *rgba, int x, int y, int dither_red_blue, int dither_green) { - float d = lum - ctx_float_get_lum (components, c); - float tc[components]; - for (int i = 0; i < components - 1; i++) - { - tc[i] = c[i] + d; - } + if (dither_red_blue == 0) + { return; } + for (int c = 0; c < 1; c ++) + { + int val = rgba[c] + ctx_dither_mask_a (x, y, 0, dither_red_blue); + rgba[c] = CTX_CLAMP (val, 0, 255); + } +} +#endif - float l = ctx_float_get_lum (components, tc); - float n = ctx_float_get_min (components, tc); - float x = ctx_float_get_max (components, tc); +#if 0 +CTX_INLINE static void +ctx_RGBA8_deassociate_alpha (const uint8_t *in, uint8_t *out) +{ + uint32_t val = *((uint32_t*)(in)); + int a = val >> CTX_RGBA8_A_SHIFT; + if (a) + { + if (a ==255) + { + *((uint32_t*)(out)) = val; + } else + { + uint32_t g = (((val & CTX_RGBA8_G_MASK) * 255 / a) >> 8) & CTX_RGBA8_G_MASK; + uint32_t rb =(((val & CTX_RGBA8_RB_MASK) * 255 / a) >> 8) & CTX_RGBA8_RB_MASK; + *((uint32_t*)(out)) = g|rb|(a << CTX_RGBA8_A_SHIFT); + } + } + else + { + *((uint32_t*)(out)) = 0; + } +} +#endif - if (n < 0.0f && l != n) +CTX_INLINE static void +ctx_u8_deassociate_alpha (int components, const uint8_t *in, uint8_t *out) +{ + if (in[components-1]) { - for (int i = 0; i < components - 1; i++) - tc[i] = l + (((tc[i] - l) * l) / (l-n)); + if (in[components-1] != 255) + for (int c = 0; c < components-1; c++) + out[c] = (in[c] * 255) / in[components-1]; + else + for (int c = 0; c < components-1; c++) + out[c] = in[c]; + out[components-1] = in[components-1]; } - - if (x > 1.0f && x != l) + else { - for (int i = 0; i < components - 1; i++) - tc[i] = l + (((tc[i] - l) * (1.0f - l)) / (x-l)); + for (int c = 0; c < components; c++) + out[c] = 0; } - for (int i = 0; i < components - 1; i++) - c[i] = tc[i]; } -static void ctx_float_set_sat (int components, float *c, float sat) +CTX_INLINE static void +ctx_float_associate_alpha (int components, float *rgba) { - int max = 0, mid = 1, min = 2; - - if (c[min] > c[mid]){int t = min; min = mid; mid = t;} - if (c[mid] > c[max]){int t = mid; mid = max; max = t;} - if (c[min] > c[mid]){int t = min; min = mid; mid = t;} + float alpha = rgba[components-1]; + for (int c = 0; c < components-1; c++) + rgba[c] *= alpha; +} - if (c[max] > c[min]) - { - c[mid] = ((c[mid]-c[min]) * sat) / (c[max] - c[min]); - c[max] = sat; - } - else - { - c[mid] = c[max] = 0.0f; - } - c[min] = 0.0f; +CTX_INLINE static void +ctx_float_deassociate_alpha (int components, float *rgba, float *dst) +{ + float ralpha = rgba[components-1]; + if (ralpha != 0.0f) ralpha = 1.0f/ralpha; + for (int c = 0; c < components-1; c++) + dst[c] = (rgba[c] * ralpha); + dst[components-1] = rgba[components-1]; } -#define ctx_float_blend_define(name, CODE) \ -static inline void \ -ctx_float_blend_##name (int components, float * __restrict__ dst, float *src, float *blended)\ -{\ - float *s = src; float b[components];\ - ctx_float_deassociate_alpha (components, dst, b);\ - CODE;\ - blended[components-1] = s[components-1];\ - ctx_float_associate_alpha (components, blended);\ +CTX_INLINE static void +ctx_RGBAF_associate_alpha (float *rgba) +{ + ctx_float_associate_alpha (4, rgba); } -#define ctx_float_blend_define_seperable(name, CODE) \ - ctx_float_blend_define(name, for (int c = 0; c < components-1; c++) { CODE ;}) \ +CTX_INLINE static void +ctx_RGBAF_deassociate_alpha (float *rgba, float *dst) +{ + ctx_float_deassociate_alpha (4, rgba, dst); +} -ctx_float_blend_define_seperable(multiply, blended[c] = (b[c] * s[c]);) -ctx_float_blend_define_seperable(screen, blended[c] = b[c] + s[c] - (b[c] * s[c]);) -ctx_float_blend_define_seperable(overlay, blended[c] = b[c] < 0.5f ? (s[c] * b[c]) : - s[c] + b[c] - (s[c] * b[c]);) -ctx_float_blend_define_seperable(darken, blended[c] = ctx_minf (b[c], s[c])) -ctx_float_blend_define_seperable(lighten, blended[c] = ctx_maxf (b[c], s[c])) -ctx_float_blend_define_seperable(color_dodge, blended[c] = (b[c] == 0.0f) ? 0.0f : - s[c] == 1.0f ? 1.0f : ctx_minf(1.0f, (b[c]) / (1.0f-s[c]))) -ctx_float_blend_define_seperable(color_burn, blended[c] = (b[c] == 1.0f) ? 1.0f : - s[c] == 0.0f ? 0.0f : 1.0f - ctx_minf(1.0f, ((1.0f - b[c])) / s[c])) -ctx_float_blend_define_seperable(hard_light, blended[c] = s[c] < 0.f ? (b[c] * s[c]) : - b[c] + s[c] - (b[c] * s[c]);) -ctx_float_blend_define_seperable(difference, blended[c] = (b[c] - s[c])) -ctx_float_blend_define_seperable(divide, blended[c] = s[c]?(b[c]) / s[c]:0.0f) -ctx_float_blend_define_seperable(addition, blended[c] = s[c]+b[c]) -ctx_float_blend_define_seperable(subtract, blended[c] = s[c]-b[c]) +static inline void ctx_swap_red_green_u8 (void *data) +{ + uint8_t *rgba = (uint8_t*)data; + uint8_t tmp = rgba[0]; + rgba[0] = rgba[2]; + rgba[2] = tmp; +} -ctx_float_blend_define_seperable(exclusion, blended[c] = b[c] + s[c] - 2.0f * b[c] * s[c]) -ctx_float_blend_define_seperable(soft_light, - if (s[c] <= 0.5f) - { - blended[c] = b[c] - (1.0f - 2.0f * s[c]) * b[c] * (1.0f - b[c]); - } - else +static void +ctx_fragment_swap_red_green_u8 (void *out, int count) +{ + uint8_t *rgba = (uint8_t*)out; + for (int x = 0; x < count; x++) { - int d; - if (b[c] <= 255/4) - d = (((16 * b[c] - 12.0f) * b[c] + 4.0f) * b[c]); - else - d = ctx_sqrtf(b[c]); - blended[c] = (b[c] + (2.0f * s[c] - 1.0f) * (d - b[c])); + ctx_swap_red_green_u8 (rgba); + rgba += 4; } -) +} +/**** rgb8 ***/ -ctx_float_blend_define(color, - for (int i = 0; i < components; i++) - blended[i] = s[i]; - ctx_float_set_lum(components, blended, ctx_float_get_lum (components, s)); -) +static void +ctx_fragment_image_rgb8_RGBA8_box (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; + CtxBuffer *buffer = g->texture.buffer->color_managed?g->texture.buffer->color_managed:g->texture.buffer; + int width = buffer->width; + int height = buffer->height; + uint8_t global_alpha_u8 = rasterizer->state->gstate.global_alpha_u8; + float factor = ctx_matrix_get_scale (&rasterizer->state->gstate.transform); + int dim = (int)((1.0f / factor) / 3); -ctx_float_blend_define(hue, - float in_sat = ctx_float_get_sat(components, b); - float in_lum = ctx_float_get_lum(components, b); - for (int i = 0; i < components; i++) - blended[i] = s[i]; - ctx_float_set_sat(components, blended, in_sat); - ctx_float_set_lum(components, blended, in_lum); -) + int i = 0; -ctx_float_blend_define(saturation, - float in_sat = ctx_float_get_sat(components, s); - float in_lum = ctx_float_get_lum(components, b); - for (int i = 0; i < components; i++) - blended[i] = b[i]; - ctx_float_set_sat(components, blended, in_sat); - ctx_float_set_lum(components, blended, in_lum); -) + for (; i < count && (x - dim< 0 || y - dim < 0 || x + dim >= height || y + dim >= height); i++) + { + *((uint32_t*)(rgba))=0; + rgba += 4; + x += dx; + y += dy; + } -ctx_float_blend_define(luminosity, - float in_lum = ctx_float_get_lum(components, s); - for (int i = 0; i < components; i++) - blended[i] = b[i]; - ctx_float_set_lum(components, blended, in_lum); -) + for (; i < count && !( + x - dim < 0 || y - dim < 0 || + x + dim >= width || + y + dim >= height); i++) + { -inline static void -ctx_float_blend (int components, CtxBlend blend, float * __restrict__ dst, float *src, float *blended) -{ - switch (blend) + int u = (int)x; + int v = (int)y; + { + int bpp = 3; + rgba[3]=global_alpha_u8; // gets lost + uint64_t sum[4]={0,0,0,0}; + int count = 0; + + { + for (int ov = - dim; ov <= dim; ov++) + { + uint8_t *src = (uint8_t *) buffer->data + bpp * ((v+ov) * width + (u - dim)); + for (int ou = - dim; ou <= dim; ou++) + { + for (int c = 0; c < bpp; c++) + sum[c] += src[c]; + count ++; + src += bpp; + } + + } + } + + int recip = 65536/count; + for (int c = 0; c < bpp; c++) + rgba[c] = sum[c] * recip >> 16; + ctx_RGBA8_associate_alpha_probably_opaque (rgba); + } + rgba += 4; + x += dx; + y += dy; + } + + for (; i < count; i++) { - case CTX_BLEND_NORMAL: ctx_float_blend_normal (components, dst, src, blended); break; - case CTX_BLEND_MULTIPLY: ctx_float_blend_multiply (components, dst, src, blended); break; - case CTX_BLEND_SCREEN: ctx_float_blend_screen (components, dst, src, blended); break; - case CTX_BLEND_OVERLAY: ctx_float_blend_overlay (components, dst, src, blended); break; - case CTX_BLEND_DARKEN: ctx_float_blend_darken (components, dst, src, blended); break; - case CTX_BLEND_LIGHTEN: ctx_float_blend_lighten (components, dst, src, blended); break; - case CTX_BLEND_COLOR_DODGE: ctx_float_blend_color_dodge (components, dst, src, blended); break; - case CTX_BLEND_COLOR_BURN: ctx_float_blend_color_burn (components, dst, src, blended); break; - case CTX_BLEND_HARD_LIGHT: ctx_float_blend_hard_light (components, dst, src, blended); break; - case CTX_BLEND_SOFT_LIGHT: ctx_float_blend_soft_light (components, dst, src, blended); break; - case CTX_BLEND_DIFFERENCE: ctx_float_blend_difference (components, dst, src, blended); break; - case CTX_BLEND_EXCLUSION: ctx_float_blend_exclusion (components, dst, src, blended); break; - case CTX_BLEND_COLOR: ctx_float_blend_color (components, dst, src, blended); break; - case CTX_BLEND_HUE: ctx_float_blend_hue (components, dst, src, blended); break; - case CTX_BLEND_SATURATION: ctx_float_blend_saturation (components, dst, src, blended); break; - case CTX_BLEND_LUMINOSITY: ctx_float_blend_luminosity (components, dst, src, blended); break; - case CTX_BLEND_ADDITION: ctx_float_blend_addition (components, dst, src, blended); break; - case CTX_BLEND_SUBTRACT: ctx_float_blend_subtract (components, dst, src, blended); break; - case CTX_BLEND_DIVIDE: ctx_float_blend_divide (components, dst, src, blended); break; + *((uint32_t*)(rgba))= 0; + rgba += 4; } } -/* this is the grunt working function, when inlined code-path elimination makes - * it produce efficient code. - */ -CTX_INLINE static void -ctx_float_porter_duff (CtxRasterizer *rasterizer, - int components, - uint8_t * __restrict__ dst, - uint8_t * __restrict__ src, - int x0, - uint8_t * __restrict__ coverage, - int count, - CtxCompositingMode compositing_mode, - CtxFragment fragment, - CtxBlend blend) -{ - float *dstf = (float*)dst; +#define CTX_DECLARE_SWAP_RED_GREEN_FRAGMENT(frag) \ +static void \ +frag##_swap_red_green (CtxRasterizer *rasterizer,\ + float x, float y, float z,\ + void *out, int count, float dx, float dy, float dz)\ +{\ + frag (rasterizer, x, y, z, out, count, dx, dy, dz);\ + ctx_fragment_swap_red_green_u8 (out, count);\ +} - CtxPorterDuffFactor f_s, f_d; - ctx_porter_duff_factors (compositing_mode, &f_s, &f_d); + + +static inline void +ctx_RGBA8_apply_global_alpha_and_associate (CtxRasterizer *rasterizer, + uint8_t *buf, int count) +{ uint8_t global_alpha_u8 = rasterizer->state->gstate.global_alpha_u8; - float global_alpha_f = rasterizer->state->gstate.global_alpha_f; - - if (rasterizer->state->gstate.source_fill.type == CTX_SOURCE_COLOR) + uint8_t *rgba = (uint8_t *) buf; + if (global_alpha_u8 != 255) { - float tsrc[components]; - - while (count--) + for (int i = 0; i < count; i++) { - uint8_t cov = *coverage; -#if 1 - if ( - CTX_UNLIKELY((compositing_mode == CTX_COMPOSITE_DESTINATION_OVER && dst[components-1] == 1.0f)|| - (cov == 0 && (compositing_mode == CTX_COMPOSITE_SOURCE_OVER || - compositing_mode == CTX_COMPOSITE_XOR || - compositing_mode == CTX_COMPOSITE_DESTINATION_OUT || - compositing_mode == CTX_COMPOSITE_SOURCE_ATOP - )))) - { - coverage ++; - dstf+=components; - continue; - } -#endif - memcpy (tsrc, rasterizer->color, sizeof(tsrc)); - - if (blend != CTX_BLEND_NORMAL) - ctx_float_blend (components, blend, dstf, tsrc, tsrc); - float covf = ctx_u8_to_float (cov); - - if (global_alpha_u8 != 255) - covf = covf * global_alpha_f; - - if (covf != 1.0f) - { - for (int c = 0; c < components; c++) - tsrc[c] *= covf; - } - - for (int c = 0; c < components; c++) - { - float res; - /* these switches and this whole function is written to be - * inlined when compiled when the enum values passed in are - * constants. - */ - switch (f_s) - { - case CTX_PORTER_DUFF_0: res = 0.0f; break; - case CTX_PORTER_DUFF_1: res = (tsrc[c]); break; - case CTX_PORTER_DUFF_ALPHA: res = (tsrc[c] * dstf[components-1]); break; - case CTX_PORTER_DUFF_1_MINUS_ALPHA: res = (tsrc[c] * (1.0f-dstf[components-1])); break; - } - switch (f_d) - { - case CTX_PORTER_DUFF_0: dstf[c] = res; break; - case CTX_PORTER_DUFF_1: dstf[c] = res + (dstf[c]); break; - case CTX_PORTER_DUFF_ALPHA: dstf[c] = res + (dstf[c] * tsrc[components-1]); break; - case CTX_PORTER_DUFF_1_MINUS_ALPHA: dstf[c] = res + (dstf[c] * (1.0f-tsrc[components-1])); break; - } - } - coverage ++; - dstf +=components; + ctx_RGBA8_associate_global_alpha (rgba, global_alpha_u8); + rgba += 4; } } else { - float tsrc[components]; - float u0 = 0; float v0 = 0; - float ud = 0; float vd = 0; - float w0 = 1; float wd = 0; - for (int c = 0; c < components; c++) tsrc[c] = 0.0f; - ctx_init_uv (rasterizer, x0, rasterizer->scanline/CTX_FULL_AA, &u0, &v0, &w0, &ud, &vd, &wd); - - while (count--) + for (int i = 0; i < count; i++) { - uint8_t cov = *coverage; -#if 1 - if ( - CTX_UNLIKELY((compositing_mode == CTX_COMPOSITE_DESTINATION_OVER && dst[components-1] == 1.0f)|| - (cov == 0 && (compositing_mode == CTX_COMPOSITE_SOURCE_OVER || - compositing_mode == CTX_COMPOSITE_XOR || - compositing_mode == CTX_COMPOSITE_DESTINATION_OUT || - compositing_mode == CTX_COMPOSITE_SOURCE_ATOP - )))) - { - u0 += ud; - v0 += vd; - coverage ++; - dstf+=components; - continue; - } -#endif + ctx_RGBA8_associate_alpha_probably_opaque (rgba); + rgba += 4; + } + } +} - fragment (rasterizer, u0, v0, w0, tsrc, 1, ud, vd, wd); - if (blend != CTX_BLEND_NORMAL) - ctx_float_blend (components, blend, dstf, tsrc, tsrc); - u0 += ud; - v0 += vd; - float covf = ctx_u8_to_float (cov); +#if CTX_FRAGMENT_SPECIALIZE - if (global_alpha_u8 != 255) - covf = covf * global_alpha_f; +static void +ctx_fragment_image_rgb8_RGBA8_nearest (CtxRasterizer *rasterizer, + float x, float y, float z, + void *out, int scount, + float dx, float dy, float dz); +static inline void +ctx_fragment_image_rgb8_RGBA8_bi (CtxRasterizer *rasterizer, + float x, float y, float z, + void *out, int scount, + float dx, float dy, float dz) +{ + ctx_fragment_image_rgb8_RGBA8_nearest (rasterizer, + x, y, z, + out, scount, + dx, dy, dz); + return; +} - if (covf != 1.0f) - { - for (int c = 0; c < components; c++) - tsrc[c] *= covf; - } +static void +ctx_fragment_image_rgb8_RGBA8_nearest (CtxRasterizer *rasterizer, + float x, float y, float z, + 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 *rgba = (uint8_t *) out; + CtxSource *g = &rasterizer->state->gstate.source_fill; + CtxBuffer *buffer = g->texture.buffer->color_managed?g->texture.buffer->color_managed:g->texture.buffer; + const int bwidth = buffer->width; + const int bheight = buffer->height; + unsigned int i = 0; + uint8_t *data = ((uint8_t*)buffer->data); - for (int c = 0; c < components; c++) + int yi_delta = (int)(dy * 65536); + int xi_delta = (int)(dx * 65536); + int zi_delta = (int)(dz * 65536); + int32_t yi = (int)(y * 65536); + int32_t xi = (int)(x * 65536); + int32_t zi = (int)(z * 65536); + { + int32_t u1 = xi + xi_delta* (count-1); + int32_t v1 = yi + yi_delta* (count-1); + int32_t z1 = zi + zi_delta* (count-1); + uint32_t *edst = ((uint32_t*)out)+(count-1); + 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) { - float res; - /* these switches and this whole function is written to be - * inlined when compiled when the enum values passed in are - * constants. - */ - switch (f_s) - { - case CTX_PORTER_DUFF_0: res = 0.0f; break; - case CTX_PORTER_DUFF_1: res = (tsrc[c]); break; - case CTX_PORTER_DUFF_ALPHA: res = (tsrc[c] * dstf[components-1]); break; - case CTX_PORTER_DUFF_1_MINUS_ALPHA: res = (tsrc[c] * (1.0f-dstf[components-1])); break; - } - switch (f_d) - { - case CTX_PORTER_DUFF_0: dstf[c] = res; break; - case CTX_PORTER_DUFF_1: dstf[c] = res + (dstf[c]); break; - case CTX_PORTER_DUFF_ALPHA: dstf[c] = res + (dstf[c] * tsrc[components-1]); break; - case CTX_PORTER_DUFF_1_MINUS_ALPHA: dstf[c] = res + (dstf[c] * (1.0f-tsrc[components-1])); break; - } + *edst-- = 0; + count --; + u1 -= xi_delta; + v1 -= yi_delta; + z1 -= zi_delta; } - coverage ++; - dstf +=components; + else break; } } -} - -/* generating one function per compositing_mode would be slightly more efficient, - * but on embedded targets leads to slightly more code bloat, - * here we trade off a slight amount of performance - */ -#define ctx_float_porter_duff(compformat, components, source, fragment, blend) \ -static void \ -ctx_##compformat##_porter_duff_##source (CTX_COMPOSITE_ARGUMENTS) \ -{ \ - switch (rasterizer->state->gstate.compositing_mode) \ - { \ - case CTX_COMPOSITE_SOURCE_ATOP: \ - ctx_float_porter_duff (rasterizer, components, dst, src, x0, coverage, count, \ - CTX_COMPOSITE_SOURCE_ATOP, fragment, blend);\ - break;\ - case CTX_COMPOSITE_DESTINATION_ATOP:\ - ctx_float_porter_duff (rasterizer, components, dst, src, x0, coverage, count,\ - CTX_COMPOSITE_DESTINATION_ATOP, fragment, blend);\ - break;\ - case CTX_COMPOSITE_DESTINATION_IN:\ - ctx_float_porter_duff (rasterizer, components, dst, src, x0, coverage, count,\ - CTX_COMPOSITE_DESTINATION_IN, fragment, blend);\ - break;\ - case CTX_COMPOSITE_DESTINATION:\ - ctx_float_porter_duff (rasterizer, components, dst, src, x0, coverage, count,\ - CTX_COMPOSITE_DESTINATION, fragment, blend);\ - break;\ - case CTX_COMPOSITE_SOURCE_OVER:\ - ctx_float_porter_duff (rasterizer, components, dst, src, x0, coverage, count,\ - CTX_COMPOSITE_SOURCE_OVER, fragment, blend);\ - break;\ - case CTX_COMPOSITE_DESTINATION_OVER:\ - ctx_float_porter_duff (rasterizer, components, dst, src, x0, coverage, count,\ - CTX_COMPOSITE_DESTINATION_OVER, fragment, blend);\ - break;\ - case CTX_COMPOSITE_XOR:\ - ctx_float_porter_duff (rasterizer, components, dst, src, x0, coverage, count,\ - CTX_COMPOSITE_XOR, fragment, blend);\ - break;\ - case CTX_COMPOSITE_DESTINATION_OUT:\ - ctx_float_porter_duff (rasterizer, components, dst, src, x0, coverage, count,\ - CTX_COMPOSITE_DESTINATION_OUT, fragment, blend);\ - break;\ - case CTX_COMPOSITE_SOURCE_OUT:\ - ctx_float_porter_duff (rasterizer, components, dst, src, x0, coverage, count,\ - CTX_COMPOSITE_SOURCE_OUT, fragment, blend);\ - break;\ - case CTX_COMPOSITE_SOURCE_IN:\ - ctx_float_porter_duff (rasterizer, components, dst, src, x0, coverage, count,\ - CTX_COMPOSITE_SOURCE_IN, fragment, blend);\ - break;\ - case CTX_COMPOSITE_COPY:\ - ctx_float_porter_duff (rasterizer, components, dst, src, x0, coverage, count,\ - CTX_COMPOSITE_COPY, fragment, blend);\ - break;\ - case CTX_COMPOSITE_CLEAR:\ - ctx_float_porter_duff (rasterizer, components, dst, src, x0, coverage, count,\ - CTX_COMPOSITE_CLEAR, fragment, blend);\ - break;\ - }\ -} -#endif - -#if CTX_ENABLE_RGBAF -ctx_float_porter_duff(RGBAF, 4,color, rasterizer->fragment, rasterizer->state->gstate.blend_mode) -ctx_float_porter_duff(RGBAF, 4,generic, rasterizer->fragment, rasterizer->state->gstate.blend_mode) + for (i= 0; i < count; i ++) + { + 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) + { + *((uint32_t*)(rgba))= 0; + } + else + break; + xi += xi_delta; + yi += yi_delta; + zi += zi_delta; + rgba += 4; + } -#if CTX_INLINED_NORMAL -#if CTX_GRADIENTS -ctx_float_porter_duff(RGBAF, 4,linear_gradient, ctx_fragment_linear_gradient_RGBAF, rasterizer->state->gstate.blend_mode) -ctx_float_porter_duff(RGBAF, 4,radial_gradient, ctx_fragment_radial_gradient_RGBAF, rasterizer->state->gstate.blend_mode) -#endif -ctx_float_porter_duff(RGBAF, 4,image, ctx_fragment_image_RGBAF, rasterizer->state->gstate.blend_mode) + while (i < count) + { + float z_recip = (zi!=0) * (1.0f/zi); + int u = (int)(xi * z_recip); + int v = (int)(yi * z_recip); + 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); + xi += xi_delta; + yi += yi_delta; + zi += zi_delta; + rgba += 4; + i++; + } +} -#if CTX_GRADIENTS -#define ctx_float_porter_duff_blend(comp_name, components, blend_mode, blend_name)\ -ctx_float_porter_duff(comp_name, components,color_##blend_name, rasterizer->fragment, blend_mode)\ -ctx_float_porter_duff(comp_name, components,generic_##blend_name, rasterizer->fragment, blend_mode)\ -ctx_float_porter_duff(comp_name, components,linear_gradient_##blend_name, ctx_fragment_linear_gradient_RGBA8, blend_mode)\ -ctx_float_porter_duff(comp_name, components,radial_gradient_##blend_name, ctx_fragment_radial_gradient_RGBA8, blend_mode)\ -ctx_float_porter_duff(comp_name, components,image_##blend_name, ctx_fragment_image_RGBAF, blend_mode) -#else -#define ctx_float_porter_duff_blend(comp_name, components, blend_mode, blend_name)\ -ctx_float_porter_duff(comp_name, components,color_##blend_name, rasterizer->fragment, blend_mode)\ -ctx_float_porter_duff(comp_name, components,generic_##blend_name, rasterizer->fragment, blend_mode)\ -ctx_float_porter_duff(comp_name, components,image_##blend_name, ctx_fragment_image_RGBAF, blend_mode) -#endif -ctx_float_porter_duff_blend(RGBAF, 4, CTX_BLEND_NORMAL, normal) +CTX_DECLARE_SWAP_RED_GREEN_FRAGMENT(ctx_fragment_image_rgb8_RGBA8_box) +CTX_DECLARE_SWAP_RED_GREEN_FRAGMENT(ctx_fragment_image_rgb8_RGBA8_bi) +CTX_DECLARE_SWAP_RED_GREEN_FRAGMENT(ctx_fragment_image_rgb8_RGBA8_nearest) -static void -ctx_RGBAF_copy_normal (CTX_COMPOSITE_ARGUMENTS) +static inline void +ctx_fragment_image_rgb8_RGBA8 (CtxRasterizer *rasterizer, + float x, + float y, + float z, + void *out, int count, float dx, float dy, float dz) { - ctx_float_copy_normal (4, rasterizer, dst, src, x0, coverage, count); + if (rasterizer->swap_red_green) + { + if (rasterizer->state->gstate.image_smoothing) + { + float factor = ctx_matrix_get_scale (&rasterizer->state->gstate.transform); + 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) + ctx_fragment_image_rgb8_RGBA8_nearest_swap_red_green (rasterizer,x,y,z, + out,count,dx,dy,dz); + #endif + else + ctx_fragment_image_rgb8_RGBA8_bi_swap_red_green (rasterizer,x,y,z, + out,count, dx, dy, dz); + } + else + { + ctx_fragment_image_rgb8_RGBA8_nearest_swap_red_green (rasterizer,x,y,z, + out,count,dx,dy,dz); + } + } + else + { + if (rasterizer->state->gstate.image_smoothing) + { + float factor = ctx_matrix_get_scale (&rasterizer->state->gstate.transform); + if (factor <= 0.50f) + 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) + ctx_fragment_image_rgb8_RGBA8_nearest (rasterizer, x, y, z, out, count, dx, dy, dz); + #endif + else + ctx_fragment_image_rgb8_RGBA8_bi (rasterizer,x,y,z,out,count,dx,dy,dz); + } + else + { + ctx_fragment_image_rgb8_RGBA8_nearest (rasterizer,x,y,z,out, + count,dx,dy, dz); + } + } } -static void -ctx_RGBAF_clear_normal (CTX_COMPOSITE_ARGUMENTS) -{ - ctx_float_clear_normal (4, rasterizer, dst, src, x0, coverage, count); -} -#if 1 -static void -ctx_RGBAF_source_over_normal_color (CTX_COMPOSITE_ARGUMENTS) -{ - ctx_float_source_over_normal_color (4, rasterizer, dst, rasterizer->color, x0, coverage, count); -} -#endif -#endif +/************** rgba8 */ static void -ctx_setup_RGBAF (CtxRasterizer *rasterizer) +ctx_fragment_image_rgba8_RGBA8_box (CtxRasterizer *rasterizer, + float x, float y, float z, + void *out, int count, float dx, float dy, float dz) { - CtxGState *gstate = &rasterizer->state->gstate; - int components = 4; - rasterizer->fragment = ctx_rasterizer_get_fragment_RGBAF (rasterizer); - rasterizer->comp = CTX_COV_PATH_FALLBACK; -#if 1 - if (gstate->source_fill.type == CTX_SOURCE_COLOR) - { - rasterizer->comp_op = ctx_RGBAF_porter_duff_color; - ctx_fragment_color_RGBAF (rasterizer, 0,0,1, rasterizer->color, 1, 0,0,0); - if (gstate->global_alpha_u8 != 255) - for (int c = 0; c < components; c ++) - ((float*)rasterizer->color)[c] *= gstate->global_alpha_f; + uint8_t *rgba = (uint8_t *) out; + CtxSource *g = &rasterizer->state->gstate.source_fill; + CtxBuffer *buffer = g->texture.buffer->color_managed?g->texture.buffer->color_managed:g->texture.buffer; + int width = buffer->width; + int height = buffer->height; + uint8_t global_alpha_u8 = rasterizer->state->gstate.global_alpha_u8; + float factor = ctx_matrix_get_scale (&rasterizer->state->gstate.transform); + int dim = (int)((1.0f / factor) / 3); - if (rasterizer->format->from_comp) - rasterizer->format->from_comp (rasterizer, 0, - &rasterizer->color[0], - &rasterizer->color_native, - 1); - } - else -#endif + int i = 0; + + for (; i < count && (x - dim< 0 || y - dim < 0 || x + dim >= height || y + dim >= height); i++) { - rasterizer->comp_op = ctx_RGBAF_porter_duff_generic; + *((uint32_t*)(rgba))=0; + rgba += 4; + x += dx; + y += dy; } -#if CTX_INLINED_NORMAL - if (gstate->compositing_mode == CTX_COMPOSITE_CLEAR) - rasterizer->comp_op = ctx_RGBAF_clear_normal; - else - switch (gstate->blend_mode) + for (; i < count && !( + x - dim < 0 || y - dim < 0 || + x + dim >= width || + y + dim >= height); i++) + { + + int u = (int)x; + int v = (int)y; { - case CTX_BLEND_NORMAL: - if (gstate->compositing_mode == CTX_COMPOSITE_COPY) - { - rasterizer->comp_op = ctx_RGBAF_copy_normal; - if (gstate->source_fill.type == CTX_SOURCE_COLOR) - rasterizer->comp = CTX_COV_PATH_RGBAF_COPY; + int bpp = 4; + uint64_t sum[4]={0,0,0,0}; + int count = 0; - } - else if (gstate->global_alpha_u8 == 0) - { - rasterizer->comp_op = ctx_RGBA8_nop; - } - else - switch (gstate->source_fill.type) - { - case CTX_SOURCE_COLOR: - if (gstate->compositing_mode == CTX_COMPOSITE_SOURCE_OVER) - { - rasterizer->comp_op = ctx_RGBAF_source_over_normal_color; - if ( ((float*)rasterizer->color)[3] >= 0.999f) - rasterizer->comp = CTX_COV_PATH_RGBAF_COPY; - } - else + { + for (int ov = - dim; ov <= dim; ov++) { - rasterizer->comp_op = ctx_RGBAF_porter_duff_color_normal; + uint8_t *src = (uint8_t *) buffer->data + bpp * ((v+ov) * width + (u - dim)); + for (int ou = - dim; ou <= dim; ou++) + { + for (int c = 0; c < bpp; c++) + sum[c] += src[c]; + count ++; + src += bpp; + } + } - break; -#if CTX_GRADIENTS - case CTX_SOURCE_LINEAR_GRADIENT: - rasterizer->comp_op = ctx_RGBAF_porter_duff_linear_gradient_normal; - break; - case CTX_SOURCE_RADIAL_GRADIENT: - rasterizer->comp_op = ctx_RGBAF_porter_duff_radial_gradient_normal; - break; -#endif - case CTX_SOURCE_TEXTURE: - rasterizer->comp_op = ctx_RGBAF_porter_duff_image_normal; - break; - default: - rasterizer->comp_op = ctx_RGBAF_porter_duff_generic_normal; - break; - } - break; - default: - switch (gstate->source_fill.type) - { - case CTX_SOURCE_COLOR: - rasterizer->comp_op = ctx_RGBAF_porter_duff_color; - break; -#if CTX_GRADIENTS - case CTX_SOURCE_LINEAR_GRADIENT: - rasterizer->comp_op = ctx_RGBAF_porter_duff_linear_gradient; - break; - case CTX_SOURCE_RADIAL_GRADIENT: - rasterizer->comp_op = ctx_RGBAF_porter_duff_radial_gradient; - break; -#endif - case CTX_SOURCE_TEXTURE: - rasterizer->comp_op = ctx_RGBAF_porter_duff_image; - break; - default: - rasterizer->comp_op = ctx_RGBAF_porter_duff_generic; - break; - } - break; + } + + int recip = 65536/count; + 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); } + rgba += 4; + x += dx; + y += dy; + } + + + for (; i < count; i++) + { + *((uint32_t*)(rgba))= 0; + rgba += 4; + } +#if CTX_DITHER +//ctx_dither_rgba_u8 (rgba, x, y, rasterizer->format->dither_red_blue, +// rasterizer->format->dither_green); #endif - ctx_setup_apply_coverage (rasterizer); } -#endif -#if CTX_ENABLE_GRAYAF -#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_image_rgba8_RGBA8_nearest_copy (CtxRasterizer *rasterizer, + float x, float y, float z, + void *out, int scount, float dx, float dy, float dz) { - float rgba[4]; + unsigned int count = scount; CtxSource *g = &rasterizer->state->gstate.source_fill; - for (int i = 0 ; i < count; i++) + 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; +#endif + 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))) { - 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]; - out = ((float*)(out)) + 2; - x += dx; - y += dy; + for (unsigned i = 0 ; i < count; i++) + *dst++ = 0; + return; } -} -static void -ctx_fragment_radial_gradient_GRAYAF (CtxRasterizer *rasterizer, float x, float y, float z, void *out, int count, float dx, float dy, float dz) -{ - float rgba[4]; - CtxSource *g = &rasterizer->state->gstate.source_fill; - for (int i = 0; i < count; i ++) +#if 1 + int pre = ctx_mini(ctx_maxi(-u,0), count); + memset (dst, 0, pre); + dst +=pre; + count-=pre; + src+=pre; + u+=pre; +#else + while (count && !(u >= 0)) { - float v = 0.0f; - if ((g->radial_gradient.r1-g->radial_gradient.r0) > 0.0f) - { - v = ctx_hypotf (g->radial_gradient.x0 - x, g->radial_gradient.y0 - y); - v = (v - g->radial_gradient.r0) / (g->radial_gradient.rdelta); - } - ctx_fragment_gradient_1d_RGBAF (rasterizer, v, 0.0, rgba); - ((float*)out)[0] = ctx_float_color_rgb_to_gray (rasterizer->state, rgba); - ((float*)out)[1] = rgba[3]; - out = ((float*)(out)) + 2; - x += dx; - y += dy; + *dst++ = 0; + src ++; + u++; + count--; } -} #endif -static void -ctx_fragment_color_GRAYAF (CtxRasterizer *rasterizer, float x, float y, float z, void *out, int count, float dx, float dy, float dz) -{ - CtxSource *g = &rasterizer->state->gstate.source_fill; - for (int i = 0; i < count; i++) + int limit = ctx_mini (count, bwidth - u); + if (limit>0) { - ctx_color_get_graya (rasterizer->state, &g->color, (float*)out); - out = ((float*)(out)) + 2; - x += dx; - y += dy; + memcpy (dst, src, limit * 4); + dst += limit; } + memset (dst, 0, count - limit); + +//ctx_RGBA8_apply_global_alpha_and_associate (rasterizer, (uint8_t*)out, count); } -static void ctx_fragment_image_GRAYAF (CtxRasterizer *rasterizer, float x, float y, float z, void *out, int count, float dx, float dy, float dz) +static void +ctx_fragment_image_rgba8_RGBA8_nearest_copy_repeat (CtxRasterizer *rasterizer, + float x, float y, float z, + void *out, int count, float dx, float dy, float dz) { - 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; - switch (buffer->format->bpp) - { -#if CTX_FRAGMENT_SPECIALIZE - case 1: ctx_fragment_image_gray1_RGBA8 (rasterizer, x, y, z, rgba, count, dx, dy, dz); break; - case 24: ctx_fragment_image_rgb8_RGBA8 (rasterizer, x, y, z, rgba, count, dx, dy, dz); break; - case 32: ctx_fragment_image_rgba8_RGBA8 (rasterizer, x, y, z, rgba, count, dx, dy, dz); break; -#endif - default: ctx_fragment_image_RGBA8 (rasterizer, x, y, z, rgba, count, dx, dy, dz); break; - } - for (int c = 0; c < 2 * count; c ++) { - rgbaf[c] = ctx_u8_to_float (rgba[c]); - ((float*)out)[0] = ctx_float_color_rgb_to_gray (rasterizer->state, rgbaf); - ((float*)out)[1] = rgbaf[3]; - out = ((float*)out) + 2; + CtxBuffer *buffer = + g->texture.buffer->color_managed?g->texture.buffer->color_managed:g->texture.buffer; + uint32_t *dst = (uint32_t*)out; + int bwidth = buffer->width; + int bheight = buffer->height; + int u = (int)x; + int v = (int)y; + if (v < 0) v += bheight * 8192; + if (u < 0) u += bwidth * 8192; + v %= bheight; + u %= bwidth; + + uint32_t *src = ((uint32_t*)buffer->data) + bwidth * v; + + while (count) + { + int chunk = ctx_mini (bwidth - u, count); + memcpy (dst, src + u, chunk * 4); + dst += chunk; + count -= chunk; + u = (u + chunk) % bwidth; } } -static CtxFragment ctx_rasterizer_get_fragment_GRAYAF (CtxRasterizer *rasterizer) +static inline int +_ctx_coords_restrict (CtxExtend extend, + int *u, int *v, + int bwidth, int bheight) { - CtxGState *gstate = &rasterizer->state->gstate; - switch (gstate->source_fill.type) - { - case CTX_SOURCE_TEXTURE: return ctx_fragment_image_GRAYAF; - case CTX_SOURCE_COLOR: return ctx_fragment_color_GRAYAF; -#if CTX_GRADIENTS - case CTX_SOURCE_LINEAR_GRADIENT: return ctx_fragment_linear_gradient_GRAYAF; - case CTX_SOURCE_RADIAL_GRADIENT: return ctx_fragment_radial_gradient_GRAYAF; -#endif - } - return ctx_fragment_color_GRAYAF; -} + switch (extend) + { + case CTX_EXTEND_REPEAT: + if(u) + { + while (*u < 0) *u += bwidth * 4096; // XXX need better way to do this + *u %= bwidth; + } + if(v) + { + while (*v < 0) *v += bheight * 4096; + *v %= bheight; + } + return 1; + case CTX_EXTEND_REFLECT: + if (u) + { + while (*u < 0) *u += bwidth * 4096; // XXX need better way to do this + *u %= (bwidth*2); -ctx_float_porter_duff(GRAYAF, 2,color, rasterizer->fragment, rasterizer->state->gstate.blend_mode) -ctx_float_porter_duff(GRAYAF, 2,generic, rasterizer->fragment, rasterizer->state->gstate.blend_mode) + *u = (*u>=bwidth) * (bwidth*2 - *u) + + (*u<bwidth) * *u; + } -#if CTX_INLINED_NORMAL -ctx_float_porter_duff(GRAYAF, 2,color_normal, rasterizer->fragment, CTX_BLEND_NORMAL) -ctx_float_porter_duff(GRAYAF, 2,generic_normal, rasterizer->fragment, CTX_BLEND_NORMAL) + if (v) + { + while (*v < 0) *v += bheight * 4096; + *v %= (bheight*2); + *v = (*v>=bheight) * (bheight*2 - *v) + + (*v<bheight) * *v; + } -static void -ctx_GRAYAF_copy_normal (CTX_COMPOSITE_ARGUMENTS) -{ - ctx_float_copy_normal (2, rasterizer, dst, src, x0, coverage, count); + return 1; + case CTX_EXTEND_PAD: + if (u)*u = ctx_mini (ctx_maxi (*u, 0), bwidth-1); + if (v)*v = ctx_mini (ctx_maxi (*v, 0), bheight-1); + return 1; + case CTX_EXTEND_NONE: + if (u) + { + //*u %= bwidth; + if (*u < 0 || *u >= bwidth) return 0; + } + if (v) + { + //*v %= bheight; + if (*v < 0 || *v >= bheight) return 0; + } + return 1; + } + return 0; } static void -ctx_GRAYAF_clear_normal (CTX_COMPOSITE_ARGUMENTS) +ctx_fragment_image_rgba8_RGBA8_nearest_affine (CtxRasterizer *rasterizer, + float x, float y, float z, + void *out, int scount, float dx, float dy, float dz) { - ctx_float_clear_normal (2, rasterizer, dst, src, x0, coverage, count); -} + unsigned int 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; + CtxBuffer *buffer = g->texture.buffer->color_managed?g->texture.buffer->color_managed:g->texture.buffer; + 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); -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); -} -#endif + int yi_delta = (int)(dy * 65536); + int xi_delta = (int)(dx * 65536); + int32_t yi = (int)(y * 65536); + int32_t xi = (int)(x * 65536); + switch (extend){ + case CTX_EXTEND_NONE: + { -static void -ctx_setup_GRAYAF (CtxRasterizer *rasterizer) -{ - CtxGState *gstate = &rasterizer->state->gstate; - int components = 2; - rasterizer->fragment = ctx_rasterizer_get_fragment_GRAYAF (rasterizer); - rasterizer->comp = CTX_COV_PATH_FALLBACK; - if (gstate->source_fill.type == CTX_SOURCE_COLOR) + 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; ) { - rasterizer->comp_op = ctx_GRAYAF_porter_duff_color; - ctx_color_get_rgba (rasterizer->state, &gstate->source_fill.color, (float*)rasterizer->color); - if (gstate->global_alpha_u8 != 255) - for (int c = 0; c < components; c ++) - ((float*)rasterizer->color)[c] *= gstate->global_alpha_f; - - if (rasterizer->format->from_comp) - rasterizer->format->from_comp (rasterizer, 0, - &rasterizer->color[0], - &rasterizer->color_native, - 1); + 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; } - else - { - rasterizer->comp_op = ctx_GRAYAF_porter_duff_generic; - } -#if CTX_INLINED_NORMAL - if (gstate->compositing_mode == CTX_COMPOSITE_CLEAR) - rasterizer->comp_op = ctx_GRAYAF_clear_normal; - else - switch (gstate->blend_mode) + 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) { - case CTX_BLEND_NORMAL: - if (gstate->compositing_mode == CTX_COMPOSITE_COPY) - { - rasterizer->comp_op = ctx_GRAYAF_copy_normal; - } - else if (gstate->global_alpha_u8 == 0) - rasterizer->comp_op = ctx_RGBA8_nop; - else - switch (gstate->source_fill.type) - { - case CTX_SOURCE_COLOR: - if (gstate->compositing_mode == CTX_COMPOSITE_SOURCE_OVER) - { - if (((float*)rasterizer->color)[components-1] == 0.0f) - rasterizer->comp_op = ctx_RGBA8_nop; -#if 1 - else //if (((float*)rasterizer->color)[components-1] == 0.0f) - rasterizer->comp_op = ctx_GRAYAF_source_copy_normal_color; -#endif - //else - // rasterizer->comp_op = ctx_GRAYAF_porter_duff_color_normal; - } - else - { - rasterizer->comp_op = ctx_GRAYAF_porter_duff_color_normal; - } - break; - default: - rasterizer->comp_op = ctx_GRAYAF_porter_duff_generic_normal; - break; - } - break; - default: - switch (gstate->source_fill.type) - { - case CTX_SOURCE_COLOR: - rasterizer->comp_op = ctx_GRAYAF_porter_duff_color; - break; - default: - rasterizer->comp_op = ctx_GRAYAF_porter_duff_generic; - break; - } - break; + *((uint32_t*)(rgba))= 0; } -#endif - ctx_setup_apply_coverage (rasterizer); -} - -#endif -#if CTX_ENABLE_GRAYF - -static void -ctx_composite_GRAYF (CTX_COMPOSITE_ARGUMENTS) -{ - float *dstf = (float*)dst; + else + break; + xi += xi_delta; + yi += yi_delta; + rgba += 4; + } - float temp[count*2]; - for (unsigned int i = 0; i < count; i++) + while (i < count) { - temp[i*2] = dstf[i]; - temp[i*2+1] = 1.0f; + 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++; } - rasterizer->comp_op (rasterizer, (uint8_t*)temp, rasterizer->color, x0, coverage, count); - for (unsigned int i = 0; i < count; i++) - { - dstf[i] = temp[i*2]; + } + break; + default: + 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; } } -#endif -#if CTX_ENABLE_BGRA8 - -inline static void -ctx_swap_red_green (uint8_t *rgba) -{ - uint32_t *buf = (uint32_t *) rgba; - uint32_t orig = *buf; - uint32_t green_alpha = (orig & 0xff00ff00); - uint32_t red_blue = (orig & 0x00ff00ff); - uint32_t red = red_blue << 16; - uint32_t blue = red_blue >> 16; - *buf = green_alpha | red | blue; -} -static void -ctx_BGRA8_to_RGBA8 (CtxRasterizer *rasterizer, int x, const void *buf, uint8_t *rgba, int count) -{ - uint32_t *srci = (uint32_t *) buf; - uint32_t *dsti = (uint32_t *) rgba; - while (count--) - { - uint32_t val = *srci++; - ctx_swap_red_green ( (uint8_t *) &val); - *dsti++ = val; - } -} static void -ctx_RGBA8_to_BGRA8 (CtxRasterizer *rasterizer, int x, const uint8_t *rgba, void *buf, int count) +ctx_fragment_image_rgba8_RGBA8_nearest_scale (CtxRasterizer *rasterizer, + float x, float y, float z, + void *out, int scount, float dx, float dy, float dz) { - ctx_BGRA8_to_RGBA8 (rasterizer, x, rgba, (uint8_t *) buf, count); -} + unsigned int count = scount; + CtxSource *g = &rasterizer->state->gstate.source_fill; + CtxBuffer *buffer = NULL; + CtxExtend extend = rasterizer->state->gstate.extend; + uint32_t *src = NULL; + buffer = g->texture.buffer->color_managed?g->texture.buffer->color_managed:g->texture.buffer; + int ideltax = (int)(dx * 65536); + uint32_t *dst = (uint32_t*)out; + int bwidth = buffer->width; + int bheight = buffer->height; + int bbheight = bheight << 16; + int bbwidth = bwidth << 16; +// x += 0.5f; +// y += 0.5f; -static void -ctx_composite_BGRA8 (CTX_COMPOSITE_ARGUMENTS) -{ - // for better performance, this could be done without a pre/post conversion, - // by swapping R and B of source instead... as long as it is a color instead - // of gradient or image - // - // - uint8_t pixels[count * 4]; - ctx_BGRA8_to_RGBA8 (rasterizer, x0, dst, &pixels[0], count); - rasterizer->comp_op (rasterizer, &pixels[0], rasterizer->color, x0, coverage, count); - ctx_BGRA8_to_RGBA8 (rasterizer, x0, &pixels[0], dst, count); -} + src = (uint32_t*)buffer->data; + //if (!src){ fprintf (stderr, "eeek bailing in nearest fragment\n"); return;}; + { + unsigned int i = 0; + int32_t ix = (int)(x * 65536); + int32_t iy = (int)(y * 65536); -#endif -static inline void -ctx_composite_direct (CTX_COMPOSITE_ARGUMENTS) -{ - // for better performance, this could be done without a pre/post conversion, - // by swapping R and B of source instead... as long as it is a color instead - // of gradient or image - // - // - rasterizer->comp_op (rasterizer, dst, rasterizer->color, x0, coverage, count); -} + if (extend == CTX_EXTEND_NONE) + { + int32_t u1 = ix + ideltax * (count-1); + int32_t v1 = iy; + uint32_t *edst = ((uint32_t*)out)+count - 1; + for (; i < count; ) + { + if (u1 <0 || v1 < 0 || u1 >= bbwidth || v1 >= bbheight) + { + *edst-- = 0; + count --; + u1 -= ideltax; + } + else break; + } -#if CTX_ENABLE_CMYKAF + for (i = 0; i < count; i ++) + { + if (ix < 0 || iy < 0 || ix >= bbwidth || iy >= bbheight) + { + *dst++ = 0; + x += dx; + ix += ideltax; + } + else break; + } -static void -ctx_fragment_other_CMYKAF (CtxRasterizer *rasterizer, float x, float y, float z, void *out, int count, float dx, float dy, float dz) -{ - float *cmyka = (float*)out; - float _rgba[4 * count]; - float *rgba = &_rgba[0]; - CtxGState *gstate = &rasterizer->state->gstate; - switch (gstate->source_fill.type) + 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; + } + } + else { - case CTX_SOURCE_TEXTURE: - ctx_fragment_image_RGBAF (rasterizer, x, y, z, rgba, count, dx, dy, dz); - break; - case CTX_SOURCE_COLOR: - ctx_fragment_color_RGBAF (rasterizer, x, y, z, rgba, count, dx, dy, dz); - break; -#if CTX_GRADIENTS - case CTX_SOURCE_LINEAR_GRADIENT: - ctx_fragment_linear_gradient_RGBAF (rasterizer, x, y, z, rgba, count, dx, dy, dz); - break; - case CTX_SOURCE_RADIAL_GRADIENT: - ctx_fragment_radial_gradient_RGBAF (rasterizer, x, y, z, rgba, count, dx, dy, dz); - break; -#endif - default: - rgba[0]=rgba[1]=rgba[2]=rgba[3]=0.0f; - break; + + int v = iy >> 16; + int u = ix >> 16; + _ctx_coords_restrict (extend, &u, &v, bwidth, bheight); + int o = (v)*bwidth; + for (; i < count; i ++) + { + u = ix >> 16; + _ctx_coords_restrict (extend, &u, &v, bwidth, bheight); + *dst++ = src[o + (u)]; + ix += ideltax; + } } - for (int i = 0; i < count; i++) - { - cmyka[4]=rgba[3]; - ctx_rgb_to_cmyk (rgba[0], rgba[1], rgba[2], &cmyka[0], &cmyka[1], &cmyka[2], &cmyka[3]); - cmyka += 5; - rgba += 4; } +// ctx_RGBA8_apply_global_alpha_and_associate (rasterizer, (uint8_t*)out, count); } static void -ctx_fragment_color_CMYKAF (CtxRasterizer *rasterizer, float x, float y, float z, void *out, int count, float dx, float dy, float dz) +ctx_fragment_image_rgba8_RGBA8_nearest_generic (CtxRasterizer *rasterizer, + float x, float y, float z, + void *out, int scount, float dx, float dy, float dz) { - CtxGState *gstate = &rasterizer->state->gstate; - float *cmyka = (float*)out; - float cmyka_in[5]; - ctx_color_get_cmyka (rasterizer->state, &gstate->source_fill.color, cmyka_in); - for (int i = 0; i < count; i++) + unsigned int 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; + CtxBuffer *buffer = g->texture.buffer->color_managed?g->texture.buffer->color_managed:g->texture.buffer; + 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); + int zi_delta = (int)(dz * 65536); + int32_t yi = (int)(y * 65536); + int32_t xi = (int)(x * 65536); + int32_t zi = (int)(z * 65536); + switch (extend){ + case CTX_EXTEND_NONE: + { + + int32_t u1 = xi + xi_delta* (count-1); + int32_t v1 = yi + yi_delta* (count-1); + int32_t z1 = zi + zi_delta* (count-1); + uint32_t *edst = ((uint32_t*)out)+(count-1); + 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) + { + *edst-- = 0; + count --; + u1 -= xi_delta; + v1 -= yi_delta; + z1 -= zi_delta; + } + else break; + } + + for (i= 0; i < count; i ++) { - for (int c = 0; c < 4; c ++) + 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) { - cmyka[c] = (1.0f - cmyka_in[c]); + *((uint32_t*)(rgba))= 0; } - cmyka[4] = cmyka_in[4]; - cmyka += 5; + else + break; + xi += xi_delta; + yi += yi_delta; + zi += zi_delta; + rgba += 4; } -} -static CtxFragment ctx_rasterizer_get_fragment_CMYKAF (CtxRasterizer *rasterizer) -{ - CtxGState *gstate = &rasterizer->state->gstate; - switch (gstate->source_fill.type) + 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; + zi += zi_delta; + rgba += 4; + i++; + } + } + break; + default: + while (i < count) { - case CTX_SOURCE_COLOR: - return ctx_fragment_color_CMYKAF; + 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; + zi += zi_delta; + rgba += 4; + i++; } - return ctx_fragment_other_CMYKAF; + break; + } } -ctx_float_porter_duff (CMYKAF, 5,color, rasterizer->fragment, rasterizer->state->gstate.blend_mode) -ctx_float_porter_duff (CMYKAF, 5,generic, rasterizer->fragment, rasterizer->state->gstate.blend_mode) - -#if CTX_INLINED_NORMAL -ctx_float_porter_duff (CMYKAF, 5,color_normal, rasterizer->fragment, CTX_BLEND_NORMAL) -ctx_float_porter_duff (CMYKAF, 5,generic_normal, rasterizer->fragment, CTX_BLEND_NORMAL) - static void -ctx_CMYKAF_copy_normal (CTX_COMPOSITE_ARGUMENTS) +ctx_fragment_image_rgba8_RGBA8_nearest (CtxRasterizer *rasterizer, + float x, float y, float z, + void *out, int icount, float dx, float dy, float dz) { - ctx_float_copy_normal (5, rasterizer, dst, src, x0, coverage, count); + unsigned int count = icount; + CtxExtend extend = rasterizer->state->gstate.extend; + if (z == 1.0f && dz == 0.0f) // this also catches other constant z! + { + 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); + } + else + { + ctx_fragment_image_rgba8_RGBA8_nearest_generic (rasterizer, x, y, z, out, count, dx, dy, dz); + } } -static void -ctx_CMYKAF_clear_normal (CTX_COMPOSITE_ARGUMENTS) -{ - ctx_float_clear_normal (5, rasterizer, dst, src, x0, coverage, count); -} -static void -ctx_CMYKAF_source_copy_normal_color (CTX_COMPOSITE_ARGUMENTS) +static inline void +ctx_fragment_image_rgba8_RGBA8_bi_scale (CtxRasterizer *rasterizer, + float x, float y, float z, + void *out, int scount, float dx, float dy, float dz) { - ctx_float_source_copy_normal_color (5, rasterizer, dst, rasterizer->color, x0, coverage, count); -} -#endif + 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; + const int bwidth = buffer->width; + const int bheight = buffer->height; + unsigned int i = 0; -static void -ctx_setup_CMYKAF (CtxRasterizer *rasterizer) -{ - CtxGState *gstate = &rasterizer->state->gstate; - int components = 5; - rasterizer->fragment = ctx_rasterizer_get_fragment_CMYKAF (rasterizer); - rasterizer->comp = CTX_COV_PATH_FALLBACK; - if (gstate->source_fill.type == CTX_SOURCE_COLOR) + if (!extend) { - rasterizer->comp_op = ctx_CMYKAF_porter_duff_color; - rasterizer->comp_op = ctx_CMYKAF_porter_duff_generic; - ctx_color_get_cmyka (rasterizer->state, &gstate->source_fill.color, (float*)rasterizer->color); - if (gstate->global_alpha_u8 != 255) - ((float*)rasterizer->color)[components-1] *= gstate->global_alpha_f; + if (!(y >= 0 && y < bheight)) + { + uint32_t *dst = (uint32_t*)rgba; + for (i = 0 ; i < count; i++) + *dst++ = 0; + return; + } + } - if (rasterizer->format->from_comp) - rasterizer->format->from_comp (rasterizer, 0, - &rasterizer->color[0], - &rasterizer->color_native, - 1); + //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 - { - rasterizer->comp_op = ctx_CMYKAF_porter_duff_generic; + else break; } - -#if CTX_INLINED_NORMAL - if (gstate->compositing_mode == CTX_COMPOSITE_CLEAR) - rasterizer->comp_op = ctx_CMYKAF_clear_normal; - else - switch (gstate->blend_mode) + for (i= 0; i < count; i ++) { - case CTX_BLEND_NORMAL: - if (gstate->compositing_mode == CTX_COMPOSITE_COPY) - { - rasterizer->comp_op = ctx_CMYKAF_copy_normal; - } - else if (gstate->global_alpha_u8 == 0) - rasterizer->comp_op = ctx_RGBA8_nop; - else - switch (gstate->source_fill.type) - { - case CTX_SOURCE_COLOR: - if (gstate->compositing_mode == CTX_COMPOSITE_SOURCE_OVER) - { - if (((float*)rasterizer->color)[components-1] == 0.0f) - rasterizer->comp_op = ctx_RGBA8_nop; - else if (((float*)rasterizer->color)[components-1] == 1.0f) - { - rasterizer->comp_op = ctx_CMYKAF_source_copy_normal_color; - rasterizer->comp = CTX_COV_PATH_CMYKAF_COPY; - } - else - rasterizer->comp_op = ctx_CMYKAF_porter_duff_color_normal; - } - else - { - rasterizer->comp_op = ctx_CMYKAF_porter_duff_color_normal; - } - break; - default: - rasterizer->comp_op = ctx_CMYKAF_porter_duff_generic_normal; - break; - } - break; - default: - switch (gstate->source_fill.type) - { - case CTX_SOURCE_COLOR: - rasterizer->comp_op = ctx_CMYKAF_porter_duff_color; - break; - default: - rasterizer->comp_op = ctx_CMYKAF_porter_duff_generic; - break; - } + int u = xi >> 16; + if ( u < 0 || u >= bwidth-1) + { + *((uint32_t*)(rgba))= 0; + xi += xi_delta; + rgba += 4; + } + else break; } -#else - - 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_CMYKAF_COPY; - } - else if (gstate->compositing_mode == CTX_COMPOSITE_SOURCE_OVER && - rasterizer->color[components-1] == 255) - { - rasterizer->comp = CTX_COV_PATH_CMYKAF_COPY; - } } -#endif - ctx_setup_apply_coverage (rasterizer); -} -static void -ctx_setup_CMYKA8 (CtxRasterizer *rasterizer) -{ - ctx_setup_CMYKAF (rasterizer); + + int v = yi >> 16; - if (rasterizer->comp == CTX_COV_PATH_CMYKAF_COPY) - rasterizer->comp = CTX_COV_PATH_CMYKA8_COPY; -} -static void -ctx_setup_CMYK8 (CtxRasterizer *rasterizer) -{ - ctx_setup_CMYKAF (rasterizer); - if (rasterizer->comp == CTX_COV_PATH_CMYKAF_COPY) - rasterizer->comp = CTX_COV_PATH_CMYK8_COPY; -} + int dv = (yi >> 8) & 0xff; -#endif -#if CTX_ENABLE_CMYKA8 + int u = xi >> 16; -static void -ctx_CMYKA8_to_CMYKAF (CtxRasterizer *rasterizer, uint8_t *src, float *dst, int count) -{ - for (int i = 0; i < count; i ++) + 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 = ((uint32_t*)buffer->data) + bwidth * v1; + + if (!extend && v1 > bheight-1) ndata = data; + + if (extend) + { + if (xi_delta == 65536) { - for (int c = 0; c < 4; c ++) - { dst[c] = ctx_u8_to_float ( (255-src[c]) ); } - dst[4] = ctx_u8_to_float (src[4]); - for (int c = 0; c < 4; c++) - { dst[c] *= dst[4]; } - src += 5; - dst += 5; + 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_associate_global_alpha_u32 ( + ctx_lerp_RGBA8_merge (s0_ga, s0_rb, s1_ga, s1_rb, du), global_alpha_u8); + rgba += 4; + u++; + src0 ++; + src1 ++; + } } -} -static void -ctx_CMYKAF_to_CMYKA8 (CtxRasterizer *rasterizer, float *src, uint8_t *dst, int count) -{ - for (int i = 0; i < count; i ++) + else { - int a = ctx_float_to_u8 (src[4]); - if (a != 0 && a != 255) + uint32_t s0_ga = 0, s1_ga = 0, s0_rb = 0, s1_rb = 0; + int prev_u = -1000; + for (; (i < count); i++) { - float recip = 1.0f/src[4]; - for (int c = 0; c < 4; c++) + if (CTX_LIKELY(prev_u == u)) { - dst[c] = ctx_float_to_u8 (1.0f - src[c] * recip); } + 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 + { + 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; + rgba += 4; + u = xi >> 16; + _ctx_coords_restrict (extend, &u, NULL, bwidth, bheight); } - else - { - for (int c = 0; c < 4; c++) - dst[c] = 255 - ctx_float_to_u8 (src[c]); - } - dst[4]=a; - - src += 5; - dst += 5; } -} - -static void -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); - ctx_CMYKAF_to_CMYKA8 (rasterizer, &pixels[0], dst, count); -} - -#endif -#if CTX_ENABLE_CMYK8 - -static void -ctx_CMYK8_to_CMYKAF (CtxRasterizer *rasterizer, uint8_t *src, float *dst, int count) -{ - for (int i = 0; i < count; i ++) + + } + else + { + if (xi_delta == 65536) { - dst[0] = ctx_u8_to_float (255-src[0]); - dst[1] = ctx_u8_to_float (255-src[1]); - dst[2] = ctx_u8_to_float (255-src[2]); - dst[3] = ctx_u8_to_float (255-src[3]); - dst[4] = 1.0f; - src += 4; - dst += 5; + 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_associate_global_alpha_u32 ( + ctx_lerp_RGBA8_merge (s0_ga, s0_rb, s1_ga, s1_rb, du), global_alpha_u8); + rgba += 4; + u++; + src0 ++; + src1 ++; + } } -} -static void -ctx_CMYKAF_to_CMYK8 (CtxRasterizer *rasterizer, float *src, uint8_t *dst, int count) -{ - for (int i = 0; i < count; i ++) + else { - float c = src[0]; - float m = src[1]; - float y = src[2]; - float k = src[3]; - float a = src[4]; - if (a != 0.0f && a != 1.0f) + 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)) { - float recip = 1.0f/a; - c *= recip; - m *= recip; - y *= recip; - k *= recip; } - c = 1.0f - c; - m = 1.0f - m; - y = 1.0f - y; - k = 1.0f - k; - dst[0] = ctx_float_to_u8 (c); - dst[1] = ctx_float_to_u8 (m); - dst[2] = ctx_float_to_u8 (y); - dst[3] = ctx_float_to_u8 (k); - src += 5; - dst += 4; + 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 + { + 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; + rgba += 4; + u = xi >> 16; + } } + } } -static void -ctx_composite_CMYK8 (CTX_COMPOSITE_ARGUMENTS) + +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) { - float pixels[count * 5]; - ctx_CMYK8_to_CMYKAF (rasterizer, dst, &pixels[0], count); - rasterizer->comp_op (rasterizer, (uint8_t *) &pixels[0], src, x0, coverage, count); - ctx_CMYKAF_to_CMYK8 (rasterizer, &pixels[0], dst, count); -} -#endif + 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; + CtxBuffer *buffer = g->texture.buffer->color_managed?g->texture.buffer->color_managed:g->texture.buffer; + 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); -#if CTX_ENABLE_RGB8 + int yi_delta = (int)(dy * 65536); + int xi_delta = (int)(dx * 65536); + int32_t yi = (int)(y * 65536); + int32_t xi = (int)(x * 65536); -inline static void -ctx_RGB8_to_RGBA8 (CtxRasterizer *rasterizer, int x, const void *buf, uint8_t *rgba, int count) -{ - const uint8_t *pixel = (const uint8_t *) buf; - while (count--) + 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; ) { - rgba[0] = pixel[0]; - rgba[1] = pixel[1]; - rgba[2] = pixel[2]; - rgba[3] = 255; - pixel+=3; - rgba +=4; + 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; } -} -inline static void -ctx_RGBA8_to_RGB8 (CtxRasterizer *rasterizer, int x, const uint8_t *rgba, void *buf, int count) -{ - uint8_t *pixel = (uint8_t *) buf; - while (count--) + 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) { - pixel[0] = rgba[0]; - pixel[1] = rgba[1]; - pixel[2] = rgba[2]; - pixel+=3; - rgba +=4; + *((uint32_t*)(rgba))= 0; } -} + else + break; + xi += xi_delta; + yi += yi_delta; + rgba += 4; + } + } -#endif -#if CTX_ENABLE_GRAY1 + uint32_t *src00=data; + uint32_t *src01=data; + uint32_t *src10=data; + uint32_t *src11=data; -#if CTX_NATIVE_GRAYA8 -inline static void -ctx_GRAY1_to_GRAYA8 (CtxRasterizer *rasterizer, int x, const void *buf, uint8_t *graya, int count) -{ - const uint8_t *pixel = (uint8_t *) buf; - while (count--) + while (i < count) + { + int du = xi >> 8; + 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 { - int bitno = x&7; - if (bitno == 0 && count >= 7) - { - if (*pixel == 0) - { - for (int i = 0; i < 8; i++) - { - *graya++ = 0; *graya++ = 255; - } - x+=8; count-=7; pixel++; - continue; - } - else if (*pixel == 0xff) - { - for (int i = 0; i < 8 * 2; i++) - { - *graya++ = 255; - } - x+=8; count-=7; pixel++; - continue; - } - } - *graya++ = 255 * ((*pixel) & (1<<bitno)); - *graya++ = 255; - pixel+= (bitno ==7); - x++; - } -} + int u1 = u + 1; + int v1 = v + 1; -inline static void -ctx_GRAYA8_to_GRAY1 (CtxRasterizer *rasterizer, int x, const uint8_t *rgba, void *buf, int count) -{ - uint8_t *pixel = (uint8_t *) buf; - while (count--) + _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 { - int gray = rgba[0]; - int bitno = x&7; - if (gray >= 128) - *pixel |= (1<<bitno); - else - *pixel &= (~ (1<<bitno)); - pixel+= (bitno==7); - x++; - rgba +=2; + 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); + xi += xi_delta; + yi += yi_delta; + rgba += 4; + + i++; + } } -#else -inline static void -ctx_GRAY1_to_RGBA8 (CtxRasterizer *rasterizer, int x, const void *buf, uint8_t *rgba, int count) +static inline void +ctx_fragment_image_rgba8_RGBA8_bi_generic (CtxRasterizer *rasterizer, + float x, float y, float z, + void *out, int scount, + float dx, float dy, float dz) { - const uint8_t *pixel = (uint8_t *) buf; - uint32_t *dst = (uint32_t*)rgba; - while (count--) - { - int bitno = x&7; + 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; + CtxBuffer *buffer = g->texture.buffer->color_managed?g->texture.buffer->color_managed:g->texture.buffer; + 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); - if ((bitno) == 0 && count >=7) + int yi_delta = (int)(dy * 65536); + int xi_delta = (int)(dx * 65536); + int zi_delta = (int)(dz * 65536); + int32_t yi = (int)(y * 65536); + int32_t xi = (int)(x * 65536); + int32_t zi = (int)(z * 65536); + if (extend == CTX_EXTEND_NONE) { + int32_t u1 = xi + xi_delta* (count-1); + int32_t v1 = yi + yi_delta* (count-1); + int32_t z1 = zi + zi_delta* (count-1); + uint32_t *edst = ((uint32_t*)out)+(count-1); + 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) { - /* special case some bit patterns when decoding */ - if (*pixel == 0) - { - *dst++ = 0xff000000; - *dst++ = 0xff000000; - *dst++ = 0xff000000; - *dst++ = 0xff000000; - *dst++ = 0xff000000; - *dst++ = 0xff000000; - *dst++ = 0xff000000; - *dst++ = 0xff000000; - x+=8; count-=7; pixel++; - continue; - } - else if (*pixel == 0xff) - { - *dst++ = 0xffffffff; - *dst++ = 0xffffffff; - *dst++ = 0xffffffff; - *dst++ = 0xffffffff; - *dst++ = 0xffffffff; - *dst++ = 0xffffffff; - *dst++ = 0xffffffff; - *dst++ = 0xffffffff; - x+=8; count-=7; pixel++; - continue; - } - else if (*pixel == 0x0f) - { - *dst++ = 0xff000000; - *dst++ = 0xff000000; - *dst++ = 0xff000000; - *dst++ = 0xff000000; - *dst++ = 0xffffffff; - *dst++ = 0xffffffff; - *dst++ = 0xffffffff; - *dst++ = 0xffffffff; - x+=8; count-=7; pixel++; - continue; - } - else if (*pixel == 0xfc) - { - *dst++ = 0xffffffff; - *dst++ = 0xffffffff; - *dst++ = 0xffffffff; - *dst++ = 0xffffffff; - *dst++ = 0xffffffff; - *dst++ = 0xffffffff; - *dst++ = 0xff000000; - *dst++ = 0xff000000; - x+=8; count-=7; pixel++; - continue; - } - else if (*pixel == 0x3f) - { - *dst++ = 0xff000000; - *dst++ = 0xff000000; - *dst++ = 0xffffffff; - *dst++ = 0xffffffff; - *dst++ = 0xffffffff; - *dst++ = 0xffffffff; - *dst++ = 0xffffffff; - *dst++ = 0xffffffff; - x+=8; count-=7; pixel++; - continue; - } + *edst-- = 0; + count --; + u1 -= xi_delta; + v1 -= yi_delta; + z1 -= zi_delta; } - *dst++=0xff000000 + 0x00ffffff * ((*pixel & (1<< bitno ) )!=0); - pixel += (bitno ==7); - x++; + else break; } -} -inline static void -ctx_RGBA8_to_GRAY1 (CtxRasterizer *rasterizer, int x, const uint8_t *rgba, void *buf, int count) -{ - uint8_t *pixel = (uint8_t *) buf; - while (count--) + for (i= 0; i < count; i ++) + { + 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) { - int gray = ctx_u8_color_rgb_to_gray (rasterizer->state, rgba); - int bitno = x&7; - //gray += ctx_dither_mask_a (x, rasterizer->scanline/aa, 0, 127); - if (gray >= 128) - *pixel |= (1<< bitno); - else - *pixel &= (~ (1<< bitno)); - pixel+= (bitno ==7); - x++; - rgba +=4; + *((uint32_t*)(rgba))= 0; } -} -#endif + else + break; + xi += xi_delta; + yi += yi_delta; + zi += zi_delta; + rgba += 4; + } + } -#endif -#if CTX_ENABLE_GRAY2 + uint32_t *src00=data; + uint32_t *src01=data; + uint32_t *src10=data; + uint32_t *src11=data; -#if CTX_NATIVE_GRAYA8 -inline static void -ctx_GRAY2_to_GRAYA8 (CtxRasterizer *rasterizer, int x, const void *buf, uint8_t *rgba, int count) -{ - const uint8_t *pixel = (uint8_t *) buf; - while (count--) + 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 { - uint8_t val = (((*pixel) >> ( (x&3) <<1)) & 3) * 85; - rgba[0] = val; - rgba[1] = 255; - if ( (x&3) ==3) - { pixel+=1; } - x++; - rgba +=2; - } -} + int u1 = u + 1; + int v1 = v + 1; -inline static void -ctx_GRAYA8_to_GRAY2 (CtxRasterizer *rasterizer, int x, const uint8_t *rgba, void *buf, int count) -{ - uint8_t *pixel = (uint8_t *) buf; - while (count--) + _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 { - int val = rgba[0]; - val = ctx_sadd8 (val, 40) >> 6; - *pixel = (*pixel & (~ (3 << ( (x&3) <<1) ) )) - | ( (val << ( (x&3) <<1) ) ); - if ( (x&3) ==3) - { pixel+=1; } - x++; - rgba +=2; + 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); + xi += xi_delta; + yi += yi_delta; + zi += zi_delta; + rgba += 4; + + i++; + } } -#else -inline static void -ctx_GRAY2_to_RGBA8 (CtxRasterizer *rasterizer, int x, const void *buf, uint8_t *rgba, int count) + +static void +ctx_fragment_image_rgba8_RGBA8_bi (CtxRasterizer *rasterizer, + float x, float y, float z, + void *out, int icount, float dx, float dy, float dz) { - const uint8_t *pixel = (uint8_t *) buf; - uint32_t *dst = (uint32_t*)rgba; - while (count--) - { - int bitno = x & 3; - if ((bitno) == 0 && count >=3) - { - /* special case some bit patterns when decoding */ - if (*pixel == 0) - { - *dst++ = 0xff000000; - *dst++ = 0xff000000; - *dst++ = 0xff000000; - *dst++ = 0xff000000; - x+=4; count-=3; pixel++; - continue; - } - else if (*pixel == 0xff) - { - *dst++ = 0xffffffff; - *dst++ = 0xffffffff; - *dst++ = 0xffffffff; - *dst++ = 0xffffffff; - x+=4; count-=3; pixel++; - continue; - } - else if (*pixel == 0x55) - { - *dst++ = 0xff555555; - *dst++ = 0xff555555; - *dst++ = 0xff555555; - *dst++ = 0xff555555; - x+=4; count-=3; pixel++; - continue; - } - else if (*pixel == 0xaa) - { - *dst++ = 0xffaaaaaa; - *dst++ = 0xffaaaaaa; - *dst++ = 0xffaaaaaa; - *dst++ = 0xffaaaaaa; - x+=4; count-=3; pixel++; - continue; - } - else if (*pixel == 0x0f) - { - *dst++ = 0xff000000; - *dst++ = 0xff000000; - *dst++ = 0xffffffff; - *dst++ = 0xffffffff; - x+=4; count-=3; pixel++; - continue; - } - else if (*pixel == 0xfc) - { - *dst++ = 0xffffffff; - *dst++ = 0xffffffff; - *dst++ = 0xffffffff; - *dst++ = 0xff000000; - x+=4; count-=3; pixel++; - continue; - } - else if (*pixel == 0x3f) - { - *dst++ = 0xff000000; - *dst++ = 0xffffffff; - *dst++ = 0xffffffff; - *dst++ = 0xffffffff; - x+=4; count-=3; pixel++; - continue; - } - } - { - uint8_t val = (((*pixel) >> ( (bitno) <<1)) & 3) * 85; - *dst = val + val * 256u + val * 256u * 256u + 255u * 256u * 256u * 256u; - if (bitno==3) - { pixel+=1; } - x++; - dst++; - } - } -} - -inline static void -ctx_RGBA8_to_GRAY2 (CtxRasterizer *rasterizer, int x, const uint8_t *rgba, void *buf, int count) -{ - uint8_t *pixel = (uint8_t *) buf; - while (count--) - { - int val = ctx_u8_color_rgb_to_gray (rasterizer->state, rgba); - val >>= 6; - *pixel = (*pixel & (~ (3 << ((x&3) <<1) ) )) - | ( (val << ((x&3) <<1) ) ); - if ( (x&3) ==3) - { pixel+=1; } - x++; - rgba +=4; - } + unsigned int count = icount; + 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) + ctx_fragment_image_rgba8_RGBA8_bi_affine (rasterizer, x, y, z, out, count, dx, dy, dz); + else + { + ctx_fragment_image_rgba8_RGBA8_bi_generic (rasterizer, x, y, z, out, count, dx, dy, dz); + } } #endif -#endif -#if CTX_ENABLE_GRAY4 +#define ctx_clampi(val,min,max) \ + ctx_mini (ctx_maxi ((val), (min)), (max)) -#if CTX_NATIVE_GRAYA8 -inline static void -ctx_GRAY4_to_GRAYA8 (CtxRasterizer *rasterizer, int x, const void *buf, uint8_t *rgba, int count) +static inline uint32_t ctx_yuv_to_rgba32 (uint8_t y, uint8_t u, uint8_t v) { - const uint8_t *pixel = (uint8_t *) buf; - while (count--) - { - int val = (*pixel & (15 << ( (x & 1) <<2) ) ) >> ( (x&1) <<2); - val <<= 4; - rgba[0] = val; - rgba[1] = 255; - if ( (x&1) ==1) - { pixel+=1; } - x++; - rgba +=2; - } + int cy = ((y - 16) * 76309) >> 16; + int cr = (v - 128); + int cb = (u - 128); + int red = cy + ((cr * 104597) >> 16); + int green = cy - ((cb * 25674 + cr * 53278) >> 16); + int blue = cy + ((cb * 132201) >> 16); + return ctx_clampi (red, 0, 255) | + (ctx_clampi (green, 0, 255) << 8) | + (ctx_clampi (blue, 0, 255) << 16) | + (0xff << 24); } -inline static void -ctx_GRAYA8_to_GRAY4 (CtxRasterizer *rasterizer, int x, const uint8_t *rgba, void *buf, int count) +static void +ctx_fragment_image_yuv420_RGBA8_nearest (CtxRasterizer *rasterizer, + float x, float y, float z, + void *out, int count, float dx, float dy, float dz) { - uint8_t *pixel = (uint8_t *) buf; - while (count--) + uint8_t *rgba = (uint8_t *) out; + CtxSource *g = &rasterizer->state->gstate.source_fill; + CtxBuffer *buffer = g->texture.buffer; + if (buffer->color_managed) + buffer = buffer->color_managed; + uint8_t *src = (uint8_t *) buffer->data; + int bwidth = buffer->width; + int bheight = buffer->height; + int bwidth_div_2 = bwidth/2; + int bheight_div_2 = bheight/2; + x += 0.5f; + y += 0.5f; + + if (!src) + return; + + { + int i = 0; + + float u1 = x + dx * (count-1); + float v1 = y + dy * (count-1); + uint32_t *edst = ((uint32_t*)out)+count - 1; + for (; i < count; ) { - int val = rgba[0]; - val >>= 4; - *pixel = *pixel & (~ (15 << ( (x&1) <<2) ) ); - *pixel = *pixel | ( (val << ( (x&1) <<2) ) ); - if ( (x&1) ==1) - { pixel+=1; } - x++; - rgba +=2; + if (u1 <0 || v1 < 0 || u1 >= bwidth || v1 >= bheight) + { + *edst-- = 0; + count --; + u1 -= dx; + v1 -= dy; + } + else break; } -} -#else -inline static void -ctx_GRAY4_to_RGBA8 (CtxRasterizer *rasterizer, int x, const void *buf, uint8_t *rgba, int count) -{ - const uint8_t *pixel = (uint8_t *) buf; - while (count--) + + for (; i < count; i ++) { - int val = (*pixel & (15 << ( (x & 1) <<2) ) ) >> ( (x&1) <<2); - val <<= 4; - rgba[0] = val; - rgba[1] = val; - rgba[2] = val; - rgba[3] = 255; - if ( (x&1) ==1) - { pixel+=1; } - x++; - rgba +=4; + int u = (int)x; + int v = (int)y; + if ((u < 0 || v < 0 || u >= bwidth || v >= bheight)) + { + *((uint32_t*)(rgba))= 0; + } + else + { + break; + } + x += dx; + y += dy; + rgba += 4; } -} -inline static void -ctx_RGBA8_to_GRAY4 (CtxRasterizer *rasterizer, int x, const uint8_t *rgba, void *buf, int count) -{ - uint8_t *pixel = (uint8_t *) buf; - while (count--) + uint32_t u_offset = bheight * bwidth; + uint32_t v_offset = u_offset + bheight_div_2 * bwidth_div_2; + + if (rasterizer->swap_red_green) { - int val = ctx_u8_color_rgb_to_gray (rasterizer->state, rgba); - val >>= 4; - *pixel = *pixel & (~ (15 << ( (x&1) <<2) ) ); - *pixel = *pixel | ( (val << ( (x&1) <<2) ) ); - if ( (x&1) ==1) - { pixel+=1; } - x++; - rgba +=4; + v_offset = bheight * bwidth; + u_offset = v_offset + bheight_div_2 * bwidth_div_2; } -} + + // XXX this is incorrect- but fixes some bug! + int ix = 65536;//x * 65536; + int iy = (int)(y * 65536); + + int ideltax = (int)(dx * 65536); + int ideltay = (int)(dy * 65536); + + if (ideltay == 0) + { + int u = ix >> 16; + int v = iy >> 16; + + uint32_t y = v * bwidth; + uint32_t uv = (v / 2) * bwidth_div_2; + + if (v >= 0 && v < bheight) + while (i < count)// && u >= 0 && u+1 < bwidth) + { + *((uint32_t*)(rgba))= ctx_yuv_to_rgba32 (src[y+u], + src[u_offset+uv+u/2], src[v_offset+uv+u/2]); +#if 0 +#if CTX_DITHER + ctx_dither_rgba_u8 (rgba, x, y, rasterizer->format->dither_red_blue, + rasterizer->format->dither_green); #endif +#endif + + ix += ideltax; + rgba += 4; + u = ix >> 16; + i++; + } + } + else + { + int u = ix >> 16; + int v = iy >> 16; + + while (i < count)// && u >= 0 && v >= 0 && u < bwidth && v < bheight) + { + uint32_t y = v * bwidth + u; + uint32_t uv = (v / 2) * bwidth_div_2 + (u / 2); + *((uint32_t*)(rgba))= ctx_yuv_to_rgba32 (src[y], + src[u_offset+uv], src[v_offset+uv]); +#if 0 +#if CTX_DITHER + ctx_dither_rgba_u8 (rgba, x+i, y, rasterizer->format->dither_red_blue, + rasterizer->format->dither_green); +#endif #endif -#if CTX_ENABLE_GRAY8 -#if CTX_NATIVE_GRAYA8 -inline static void -ctx_GRAY8_to_GRAYA8 (CtxRasterizer *rasterizer, int x, const void *buf, uint8_t *rgba, int count) -{ - const uint8_t *pixel = (uint8_t *) buf; - while (count--) + ix += ideltax; + iy += ideltay; + rgba += 4; + u = ix >> 16; + v = iy >> 16; + i++; + } + } + + for (; i < count; i++) { - rgba[0] = pixel[0]; - rgba[1] = 255; - pixel+=1; - rgba +=2; + *((uint32_t*)(rgba))= 0; + rgba += 4; } + } + + if (rasterizer->state->gstate.global_alpha_u8 != 255) + ctx_RGBA8_apply_global_alpha_and_associate (rasterizer, (uint8_t*)out, count); } -inline static void -ctx_GRAYA8_to_GRAY8 (CtxRasterizer *rasterizer, int x, const uint8_t *rgba, void *buf, int count) +#if CTX_FRAGMENT_SPECIALIZE + +CTX_DECLARE_SWAP_RED_GREEN_FRAGMENT(ctx_fragment_image_rgba8_RGBA8_box) +CTX_DECLARE_SWAP_RED_GREEN_FRAGMENT(ctx_fragment_image_rgba8_RGBA8_bi) +CTX_DECLARE_SWAP_RED_GREEN_FRAGMENT(ctx_fragment_image_rgba8_RGBA8_nearest) + +CTX_DECLARE_SWAP_RED_GREEN_FRAGMENT(ctx_fragment_image_rgba8_RGBA8_nearest_copy) +CTX_DECLARE_SWAP_RED_GREEN_FRAGMENT(ctx_fragment_image_rgba8_RGBA8_nearest_copy_repeat) +CTX_DECLARE_SWAP_RED_GREEN_FRAGMENT(ctx_fragment_image_rgba8_RGBA8_nearest_scale) +CTX_DECLARE_SWAP_RED_GREEN_FRAGMENT(ctx_fragment_image_rgba8_RGBA8_nearest_affine) +CTX_DECLARE_SWAP_RED_GREEN_FRAGMENT(ctx_fragment_image_rgba8_RGBA8_nearest_generic) + +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) + +static inline void +ctx_fragment_image_rgba8_RGBA8 (CtxRasterizer *rasterizer, + float x, float y, float z, + void *out, int count, float dx, float dy, float dz) { - uint8_t *pixel = (uint8_t *) buf; - while (count--) + if (rasterizer->state->gstate.image_smoothing) + { + float factor = ctx_matrix_get_scale (&rasterizer->state->gstate.transform); + if (factor <= 0.50f) { - pixel[0] = rgba[0]; - pixel+=1; - rgba +=2; + if (rasterizer->swap_red_green) + ctx_fragment_image_rgba8_RGBA8_box_swap_red_green (rasterizer, x, y, z, out, count, dx, dy, dz); + else + ctx_fragment_image_rgba8_RGBA8_box (rasterizer, x, y, z, out, count, dx, dy, dz); } -} -#else -inline static void -ctx_GRAY8_to_RGBA8 (CtxRasterizer *rasterizer, int x, const void *buf, uint8_t *rgba, int count) -{ - const uint8_t *pixel = (uint8_t *) buf; - while (count--) +#if CTX_ALWAYS_USE_NEAREST_FOR_SCALE1 + else if (factor > 0.99f && factor < 1.01f) { - rgba[0] = pixel[0]; - rgba[1] = pixel[0]; - rgba[2] = pixel[0]; - rgba[3] = 255; - pixel+=1; - rgba +=4; + // XXX: also verify translate == 0 for this fast path to be valid + if (rasterizer->swap_red_green) + ctx_fragment_image_rgba8_RGBA8_nearest_swap_red_green (rasterizer, x, y, z, out, count, dx, dy, dz); + else + ctx_fragment_image_rgba8_RGBA8_nearest (rasterizer, x, y, z, out, count, dx, dy, dz); } -} - -inline static void -ctx_RGBA8_to_GRAY8 (CtxRasterizer *rasterizer, int x, const uint8_t *rgba, void *buf, int count) -{ - uint8_t *pixel = (uint8_t *) buf; - for (int i = 0; i < count; i ++) +#endif + else { - pixel[i] = ctx_u8_color_rgb_to_gray (rasterizer->state, rgba + i * 4); + if (rasterizer->swap_red_green) + ctx_fragment_image_rgba8_RGBA8_bi_swap_red_green (rasterizer, x, y, z, out, count, dx, dy, dz); + else + ctx_fragment_image_rgba8_RGBA8_bi (rasterizer, x, y, z, out, count, dx, dy, dz); } -} + } + else + { + if (rasterizer->swap_red_green) + ctx_fragment_image_rgba8_RGBA8_nearest_swap_red_green (rasterizer, x, y, z, out, count, dx, dy, dz); + else + ctx_fragment_image_rgba8_RGBA8_nearest (rasterizer, x, y, z, out, count, dx, dy, dz); + } + //ctx_fragment_swap_red_green_u8 (out, count); +#if 0 +#if CTX_DITHER + uint8_t *rgba = (uint8_t*)out; + ctx_dither_rgba_u8 (rgba, x, y, rasterizer->format->dither_red_blue, + rasterizer->format->dither_green); #endif - #endif -#if CTX_ENABLE_GRAYA8 +} +#endif -inline static void -ctx_GRAYA8_to_RGBA8 (CtxRasterizer *rasterizer, int x, const void *buf, uint8_t *rgba, int count) +static void +ctx_fragment_image_gray1_RGBA8 (CtxRasterizer *rasterizer, float x, float y, float z, void *out, int count, float dx, float dy, float dz) { - const uint8_t *pixel = (const uint8_t *) buf; - while (count--) + uint8_t *rgba = (uint8_t *) out; + CtxSource *g = &rasterizer->state->gstate.source_fill; + CtxBuffer *buffer = g->texture.buffer; + for (int i = 0; i < count; i ++) + { + int u = (int)x; + int v = (int)y; + if ( u < 0 || v < 0 || + u >= buffer->width || + v >= buffer->height) { - rgba[0] = pixel[0]; - rgba[1] = pixel[0]; - rgba[2] = pixel[0]; - rgba[3] = pixel[1]; - pixel+=2; - rgba +=4; + rgba[0] = rgba[1] = rgba[2] = rgba[3] = 0; } -} - -inline static void -ctx_RGBA8_to_GRAYA8 (CtxRasterizer *rasterizer, int x, const uint8_t *rgba, void *buf, int count) -{ - uint8_t *pixel = (uint8_t *) buf; - while (count--) + else { - pixel[0] = ctx_u8_color_rgb_to_gray (rasterizer->state, rgba); - pixel[1] = rgba[3]; - pixel+=2; - rgba +=4; + uint8_t *src = (uint8_t *) buffer->data; + src += v * buffer->stride + u / 8; + if (*src & (1<< (u & 7) ) ) + { + rgba[0] = rgba[1] = rgba[2] = rgba[3] = 0; + } + else + { + for (int c = 0; c < 4; c++) + { rgba[c] = 255; + }//g->texture.rgba[c]; + //} + } } -} -#if CTX_NATIVE_GRAYA8 -CTX_INLINE static void ctx_rgba_to_graya_u8 (CtxState *state, uint8_t *in, uint8_t *out) -{ - out[0] = ctx_u8_color_rgb_to_gray (state, in); - out[1] = in[3]; + rgba += 4; + x += dx; + y += dy; + } } #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_radial_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; - uint8_t *dst = (uint8_t*)out; #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); + for (int i = 0; i < count; i ++) { - uint8_t rgba[4]; - ctx_fragment_gradient_1d_RGBA8 (rasterizer, v, 1.0f, rgba); - ctx_rgba_to_graya_u8 (rasterizer->state, rgba, dst); - - } - + float v = (ctx_hypotf_fast (g->radial_gradient.x0 - x, g->radial_gradient.y0 - y) - + g->radial_gradient.r0) * (g->radial_gradient.rdelta); +#if CTX_GRADIENT_CACHE + uint32_t *rgbap = (uint32_t*)&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_graya_u8 ((uint8_t*)dst, ox + i, scan, rasterizer->format->dither_red_blue, - rasterizer->format->dither_green); + ctx_dither_rgba_u8 (rgba, ox+i, scan, rasterizer->format->dither_red_blue, + rasterizer->format->dither_green); #endif - dst += 2; - x += dx; - y += dy; + rgba += 4; + x += dx; + y += dy; } } static void -ctx_fragment_radial_gradient_GRAYA8 (CtxRasterizer *rasterizer, float x, float y, float z, void *out, int count, float dx, float dy, float dz) +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 *dst = (uint8_t*)out; -#if CTX_DITHER - int scan = rasterizer->scanline / CTX_FULL_AA; - int ox = (int)x; -#endif - - for (int i = 0; i < count;i ++) - { +#if 0 + uint8_t *rgba = (uint8_t *) out; CtxSource *g = &rasterizer->state->gstate.source_fill; - float v = (ctx_hypotf (g->radial_gradient.x0 - x, g->radial_gradient.y0 - y) - - g->radial_gradient.r0) * (g->radial_gradient.rdelta); + for (int i = 0; i < count; i ++) { - uint8_t rgba[4]; - ctx_fragment_gradient_1d_RGBA8 (rasterizer, v, 1.0, rgba); - ctx_rgba_to_graya_u8 (rasterizer->state, rgba, dst); - } -#if CTX_DITHER - ctx_dither_graya_u8 ((uint8_t*)dst, ox+i, scan, rasterizer->format->dither_red_blue, - rasterizer->format->dither_green); + float v = ( ( (g->linear_gradient.dx * x + g->linear_gradient.dy * y) / + g->linear_gradient.length) - + g->linear_gradient.start) * (g->linear_gradient.rdelta); +#if CTX_GRADIENT_CACHE + uint32_t*rgbap = ((uint32_t*)(&ctx_gradient_cache_u8[ctx_grad_index(v)][0])); + *((uint32_t*)rgba) = *rgbap; +#else + _ctx_fragment_gradient_1d_RGBA8 (rasterizer, v, 1.0, rgba); #endif - dst += 2; - x += dx; - y += dy; - } -} +#if CTX_DITHER + ctx_dither_rgba_u8 (rgba, x+i, y, rasterizer->format->dither_red_blue, + rasterizer->format->dither_green); #endif - -static void -ctx_fragment_color_GRAYA8 (CtxRasterizer *rasterizer, float x, float y, float z, void *out, int count, float dx, float dy, float dz) -{ - CtxSource *g = &rasterizer->state->gstate.source_fill; - uint16_t *dst = (uint16_t*)out; - uint16_t pix; - ctx_color_get_graya_u8 (rasterizer->state, &g->color, (uint8_t*)&pix); - for (int i = 0; i <count; i++) - { - dst[i]=pix; + rgba += 4; + x += dx; + y += dy; } -} +#else + uint8_t *rgba = (uint8_t *) out; -static void ctx_fragment_image_GRAYA8 (CtxRasterizer *rasterizer, float x, float y, float z, void *out, int count, float dx, float dy, float dz) -{ - 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; - switch (buffer->format->bpp) - { -#if CTX_FRAGMENT_SPECIALIZE - case 1: ctx_fragment_image_gray1_RGBA8 (rasterizer, x, y, z, rgba, count, dx, dy, dz); break; - case 24: ctx_fragment_image_rgb8_RGBA8 (rasterizer, x, y, z, rgba, count, dx, dy, dz); break; - case 32: ctx_fragment_image_rgba8_RGBA8 (rasterizer, x, y, z, rgba, count, dx, dy, dz); break; -#endif - default: ctx_fragment_image_RGBA8 (rasterizer, x, y, z, rgba, count, dx, dy, dz); break; - } - for (int i = 0; i < count; i++) - ctx_rgba_to_graya_u8 (rasterizer->state, &rgba[i*4], &((uint8_t*)out)[i*2]); -} + 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; -static CtxFragment ctx_rasterizer_get_fragment_GRAYA8 (CtxRasterizer *rasterizer) -{ - CtxGState *gstate = &rasterizer->state->gstate; - switch (gstate->source_fill.type) - { - case CTX_SOURCE_TEXTURE: return ctx_fragment_image_GRAYA8; - case CTX_SOURCE_COLOR: return ctx_fragment_color_GRAYA8; -#if CTX_GRADIENTS - case CTX_SOURCE_LINEAR_GRADIENT: return ctx_fragment_linear_gradient_GRAYA8; - case CTX_SOURCE_RADIAL_GRADIENT: return ctx_fragment_radial_gradient_GRAYA8; +#if CTX_DITHER + int dither_red_blue = rasterizer->format->dither_red_blue; + int dither_green = rasterizer->format->dither_green; + int scan = rasterizer->scanline / CTX_FULL_AA; + int ox = (int)x; #endif - } - return ctx_fragment_color_GRAYA8; -} - -ctx_u8_porter_duff(GRAYA8, 2,generic, rasterizer->fragment, rasterizer->state->gstate.blend_mode) - -#if CTX_INLINED_NORMAL -ctx_u8_porter_duff(GRAYA8, 2,generic_normal, rasterizer->fragment, CTX_BLEND_NORMAL) -static void -ctx_GRAYA8_copy_normal (CTX_COMPOSITE_ARGUMENTS) -{ - ctx_u8_copy_normal (2, rasterizer, dst, src, x0, coverage, count); -} - -static void -ctx_GRAYA8_clear_normal (CTX_COMPOSITE_ARGUMENTS) -{ - ctx_u8_clear_normal (2, rasterizer, dst, src, x0, coverage, count); -} + u0 *= linear_gradient_dx; + v0 *= linear_gradient_dy; + ud *= linear_gradient_dx; + vd *= linear_gradient_dy; -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); +#if CTX_GRADIENT_CACHE + int vv = (int)(((u0 + v0) - linear_gradient_start) * (rasterizer->gradient_cache_elements-1) * 256); + int ud_plus_vd = (int)((ud + vd) * (rasterizer->gradient_cache_elements-1) * 256); #else - uint8_t tsrc[5]; - *((uint32_t*)tsrc) = *((uint32_t*)src); + float vv = ((u0 + v0) - linear_gradient_start); + float ud_plus_vd = (ud + vd); +#endif - while (count--) + for (int i = 0; i < count ; i++) { - uint32_t cov = *coverage++; - uint32_t common =(((((255+(tsrc[1] * cov))>>8))^255 )); - dst[0] = ((((tsrc[0] * cov)) + (dst[0] * common ))>>8); - dst[1] = ((((tsrc[1] * cov)) + (dst[1] * common ))>>8); - dst+=2; - } +#if CTX_GRADIENT_CACHE + uint32_t*rgbap = ((uint32_t*)(&rasterizer->gradient_cache_u8[ctx_grad_index_i (rasterizer, vv)][0])); + *((uint32_t*)rgba) = *rgbap; +#else + _ctx_fragment_gradient_1d_RGBA8 (rasterizer, vv, 1.0, rgba); #endif -} - -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); -} +#if CTX_DITHER + ctx_dither_rgba_u8 (rgba, ox+i, scan, dither_red_blue, dither_green); #endif - -inline static int -ctx_is_opaque_color (CtxRasterizer *rasterizer) -{ - CtxGState *gstate = &rasterizer->state->gstate; - if (gstate->global_alpha_u8 != 255) - return 0; - if (gstate->source_fill.type == CTX_SOURCE_COLOR) - { - uint8_t ga[2]; - ctx_color_get_graya_u8 (rasterizer->state, &gstate->source_fill.color, ga); - return ga[1] == 255; + rgba+= 4; + vv += ud_plus_vd; } - return 0; -} - -static void -ctx_setup_GRAYA8 (CtxRasterizer *rasterizer) -{ - CtxGState *gstate = &rasterizer->state->gstate; - int components = 2; - rasterizer->fragment = ctx_rasterizer_get_fragment_GRAYA8 (rasterizer); - rasterizer->comp_op = ctx_GRAYA8_porter_duff_generic; - rasterizer->comp = CTX_COV_PATH_FALLBACK; - if (gstate->source_fill.type == CTX_SOURCE_COLOR) - { - ctx_fragment_color_GRAYA8 (rasterizer, 0,0, 1,rasterizer->color, 1, 0,0,0); - if (gstate->global_alpha_u8 != 255) - for (int c = 0; c < components; c ++) - rasterizer->color[c] = (rasterizer->color[c] * gstate->global_alpha_u8)/255; - - if (rasterizer->format->from_comp) - rasterizer->format->from_comp (rasterizer, 0, - &rasterizer->color[0], - &rasterizer->color_native, - 1); - } - -#if CTX_INLINED_NORMAL - if (gstate->compositing_mode == CTX_COMPOSITE_CLEAR) - rasterizer->comp_op = ctx_GRAYA8_clear_normal; - else - switch (gstate->blend_mode) - { - case CTX_BLEND_NORMAL: - if (gstate->compositing_mode == CTX_COMPOSITE_COPY) - { - rasterizer->comp_op = ctx_GRAYA8_copy_normal; - rasterizer->comp = CTX_COV_PATH_GRAYA8_COPY; - } - else if (gstate->global_alpha_u8 == 0) - rasterizer->comp_op = ctx_RGBA8_nop; - else - switch (gstate->source_fill.type) - { - case CTX_SOURCE_COLOR: - if (gstate->compositing_mode == CTX_COMPOSITE_SOURCE_OVER) - { - if (rasterizer->color[components-1] == 0) - rasterizer->comp_op = ctx_RGBA8_nop; - else if (rasterizer->color[components-1] == 255) - { - rasterizer->comp_op = ctx_GRAYA8_source_copy_normal_color; - rasterizer->comp = CTX_COV_PATH_GRAYA8_COPY; - } - else - rasterizer->comp_op = ctx_GRAYA8_source_over_normal_color; - } - else - { - rasterizer->comp_op = ctx_GRAYA8_porter_duff_generic_normal; - } - break; - default: - rasterizer->comp_op = ctx_GRAYA8_porter_duff_generic_normal; - break; - } - break; - default: - rasterizer->comp_op = ctx_GRAYA8_porter_duff_generic; - break; - } -#else - 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) - { - rasterizer->comp = CTX_COV_PATH_GRAYA8_COPY; - } - } #endif - ctx_setup_apply_coverage (rasterizer); } -#if CTX_ENABLE_GRAY4 -static void -ctx_setup_GRAY4 (CtxRasterizer *rasterizer) -{ - ctx_setup_GRAYA8 (rasterizer); - if (rasterizer->comp == CTX_COV_PATH_GRAYA8_COPY) - rasterizer->comp = CTX_COV_PATH_GRAY4_COPY; - else - rasterizer->comp = CTX_COV_PATH_FALLBACK; -} #endif -#if CTX_ENABLE_GRAY2 static void -ctx_setup_GRAY2 (CtxRasterizer *rasterizer) +ctx_fragment_color_RGBA8 (CtxRasterizer *rasterizer, float x, float y, float z, void *out, int count, float dx, float dy, float dz) { - ctx_setup_GRAYA8 (rasterizer); - if (rasterizer->comp == CTX_COV_PATH_GRAYA8_COPY) - rasterizer->comp = CTX_COV_PATH_GRAY2_COPY; - else - rasterizer->comp = CTX_COV_PATH_FALLBACK; + uint8_t *rgba_out = (uint8_t *) out; + CtxSource *g = &rasterizer->state->gstate.source_fill; + ctx_color_get_rgba8 (rasterizer->state, &g->color, rgba_out); + ctx_RGBA8_associate_alpha (rgba_out); + if (rasterizer->swap_red_green) + { + int tmp = rgba_out[0]; + rgba_out[0] = rgba_out[2]; + rgba_out[2] = tmp; + } + for (int i = 1; i < count; i++, rgba_out+=4) + memcpy (rgba_out + count * 4, rgba_out, 4); } -#endif +#if CTX_ENABLE_FLOAT -#if CTX_ENABLE_GRAY1 +#if CTX_GRADIENTS static void -ctx_setup_GRAY1 (CtxRasterizer *rasterizer) +ctx_fragment_linear_gradient_RGBAF (CtxRasterizer *rasterizer, float x, float y, float z, void *out, int count, float dx, float dy, float dz) { - ctx_setup_GRAYA8 (rasterizer); - if (rasterizer->comp == CTX_COV_PATH_GRAYA8_COPY) - rasterizer->comp = CTX_COV_PATH_GRAY1_COPY; - else - rasterizer->comp = CTX_COV_PATH_FALLBACK; + float *rgba = (float *) out; + CtxSource *g = &rasterizer->state->gstate.source_fill; + for (int i = 0; i < count; i++) + { + float v = ( ( (g->linear_gradient.dx * x + g->linear_gradient.dy * y) / + g->linear_gradient.length) - + g->linear_gradient.start) * (g->linear_gradient.rdelta); + ctx_fragment_gradient_1d_RGBAF (rasterizer, v, 1.0f, rgba); + x += dx; + y += dy; + rgba += 4; + } } -#endif static void -ctx_setup_GRAY8 (CtxRasterizer *rasterizer) +ctx_fragment_radial_gradient_RGBAF (CtxRasterizer *rasterizer, float x, float y, float z, void *out, int count, float dx, float dy, float dz) { - ctx_setup_GRAYA8 (rasterizer); - if (rasterizer->comp == CTX_COV_PATH_GRAYA8_COPY) - rasterizer->comp = CTX_COV_PATH_GRAY8_COPY; - else - rasterizer->comp = CTX_COV_PATH_FALLBACK; + float *rgba = (float *) out; + CtxSource *g = &rasterizer->state->gstate.source_fill; + for (int i = 0; i < count; i++) + { + float v = ctx_hypotf (g->radial_gradient.x0 - x, g->radial_gradient.y0 - y); + v = (v - g->radial_gradient.r0) * (g->radial_gradient.rdelta); + ctx_fragment_gradient_1d_RGBAF (rasterizer, v, 0.0f, rgba); + x+=dx; + y+=dy; + rgba +=4; + } } - -#endif - #endif -inline static void -ctx_332_unpack (uint8_t pixel, - uint8_t *red, - uint8_t *green, - uint8_t *blue) -{ - *green = (((pixel >> 2) & 7)*255)/7; - *red = (((pixel >> 5) & 7)*255)/7; - *blue = ((((pixel & 3) << 1) | ((pixel >> 2) & 1))*255)/7; -} - -static inline uint8_t -ctx_332_pack (uint8_t red, - uint8_t green, - uint8_t blue) -{ - return ((ctx_sadd8(red,15) >> 5) << 5) - |((ctx_sadd8(green,15) >> 5) << 2) - |(ctx_sadd8(blue,15) >> 6); -} -#if CTX_ENABLE_RGB332 -static inline uint8_t -ctx_888_to_332 (uint32_t in) +static void +ctx_fragment_color_RGBAF (CtxRasterizer *rasterizer, float x, float y, float z, void *out, int count, float dx, float dy, float dz) { - uint8_t *rgb=(uint8_t*)(&in); - return ctx_332_pack (rgb[0],rgb[1],rgb[2]); + float *rgba = (float *) out; + float in[4]; + CtxSource *g = &rasterizer->state->gstate.source_fill; + ctx_color_get_rgba (rasterizer->state, &g->color, in); + for (int c = 0; c < 3; c++) + in[c] *= in[3]; + while (count--) + { + for (int c = 0; c < 4; c++) + rgba[c] = in[c]; + rgba += 4; + } } -static inline uint32_t -ctx_332_to_888 (uint8_t in) -{ - uint32_t ret = 0; - uint8_t *rgba=(uint8_t*)&ret; - ctx_332_unpack (in, - &rgba[0], - &rgba[1], - &rgba[2]); - rgba[3] = 255; - return ret; -} -static inline void -ctx_RGB332_to_RGBA8 (CtxRasterizer *rasterizer, int x, const void *buf, uint8_t *rgba, int count) +static void ctx_fragment_image_RGBAF (CtxRasterizer *rasterizer, float x, float y, float z, void *out, int count, float dx, float dy, float dz) { - const uint8_t *pixel = (uint8_t *) buf; - while (count--) + float *outf = (float *) out; + 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; + switch (buffer->format->bpp) { - ctx_332_unpack (*pixel, &rgba[0], &rgba[1], &rgba[2]); -#if CTX_RGB332_ALPHA - if (rgba[0]==255 && rgba[2] == 255 && rgba[1]==0) - { rgba[3] = 0; } - else +#if CTX_FRAGMENT_SPECIALIZE + case 1: ctx_fragment_image_gray1_RGBA8 (rasterizer, x, y, z, rgba, count, dx, dy, dz); break; + case 24: ctx_fragment_image_rgb8_RGBA8 (rasterizer, x, y, z, rgba, count, dx, dy, dz); break; + case 32: ctx_fragment_image_rgba8_RGBA8 (rasterizer, x, y, z, rgba, count, dx, dy, dz); break; #endif - { rgba[3] = 255; } - pixel+=1; - rgba +=4; + default: ctx_fragment_image_RGBA8 (rasterizer, x, y, z, rgba, count, dx, dy, dz); break; } + for (int c = 0; c < 4 * count; c ++) { outf[c] = ctx_u8_to_float (rgba[c]); } } -static inline void -ctx_RGBA8_to_RGB332 (CtxRasterizer *rasterizer, int x, const uint8_t *rgba, void *buf, int count) +static CtxFragment ctx_rasterizer_get_fragment_RGBAF (CtxRasterizer *rasterizer) { - uint8_t *pixel = (uint8_t *) buf; - while (count--) + CtxGState *gstate = &rasterizer->state->gstate; + switch (gstate->source_fill.type) { -#if CTX_RGB332_ALPHA - if (rgba[3]==0) - { pixel[0] = ctx_332_pack (255, 0, 255); } - else + case CTX_SOURCE_TEXTURE: return ctx_fragment_image_RGBAF; + case CTX_SOURCE_COLOR: return ctx_fragment_color_RGBAF; +#if CTX_GRADIENTS + case CTX_SOURCE_LINEAR_GRADIENT: return ctx_fragment_linear_gradient_RGBAF; + case CTX_SOURCE_RADIAL_GRADIENT: return ctx_fragment_radial_gradient_RGBAF; #endif - { pixel[0] = ctx_332_pack (rgba[0], rgba[1], rgba[2]); } - pixel+=1; - rgba +=4; - } -} - -static void -ctx_composite_RGB332 (CTX_COMPOSITE_ARGUMENTS) -{ -#if 1 - if (CTX_LIKELY(rasterizer->comp_op == ctx_RGBA8_source_over_normal_color)) - { - uint32_t si_ga = ((uint32_t*)rasterizer->color)[1]; - uint32_t si_rb = ((uint32_t*)rasterizer->color)[2]; - uint32_t si_a = si_ga >> 16; - - while (count--) - { - uint32_t cov = *coverage++; - uint32_t rcov = (((255+si_a * cov)>>8))^255; - uint32_t di = ctx_332_to_888 (*((uint8_t*)dst)); - uint32_t di_ga = ((di & 0xff00ff00) >> 8); - uint32_t di_rb = (di & 0x00ff00ff); - *((uint8_t*)(dst)) = - ctx_888_to_332((((si_rb * cov + 0xff00ff + di_rb * rcov) & 0xff00ff00) >> 8) | - ((si_ga * cov + 0xff00ff + di_ga * rcov) & 0xff00ff00)); - dst+=1; } - return; - } -#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); - ctx_RGBA8_to_RGB332 (rasterizer, x0, &pixels[0], dst, count); + return ctx_fragment_color_RGBAF; } - -#endif -static inline uint16_t -ctx_565_pack (uint8_t red, - uint8_t green, - uint8_t blue, - const int byteswap) -{ -#if 0 - // is this extra precision warranted? - // for 332 it gives more pure white.. - // it might be the case also for generic 565 - red = ctx_sadd8 (red, 4); - green = ctx_sadd8 (green, 3); - blue = ctx_sadd8 (blue, 4); #endif - uint32_t c = (red >> 3) << 11; - c |= (green >> 2) << 5; - c |= blue >> 3; - if (byteswap) - { return (c>>8) | (c<<8); } /* swap bytes */ - return c; -} - - // ARGB - -#if CTX_ENABLE_CBRLE - -static inline void -ctx_RGBA8_to_CBRLE (CtxRasterizer *rasterizer, int u, const uint8_t *rgba, void *buf, int count) +static inline int +ctx_matrix_no_perspective (CtxMatrix *matrix) { - uint8_t *pixel = (uint8_t *) buf; - int width = rasterizer->blit_width; - int format_bits = rasterizer->format->bpp; - pixel-= (u *format_bits)/8; - int size = (width * format_bits)/8; - - ctx_CBRLE_compress (&rgba[-u*4], pixel, width, size, u, count, CBRLE_MODE_SET, NULL, 1); + if (fabsf(matrix->m[2][0]) >0.001f) return 0; + if (fabsf(matrix->m[2][1]) >0.001f) return 0; + if (fabsf(matrix->m[2][2] - 1.0f)>0.001f) return 0; + return 1; } -static inline void -ctx_CBRLE_to_RGBA8 (CtxRasterizer *rasterizer, int u, const void *buf, uint8_t *rgba, int count) +/* for multiples of 90 degree rotations, we return no rotation */ +static inline int +ctx_matrix_no_skew_or_rotate (CtxMatrix *matrix) { - const uint8_t *pixel = (uint8_t *) buf; - int width = rasterizer->blit_width; - uint8_t temp[width * 4]; - int format_bits = rasterizer->format->bpp; - pixel-= (u *format_bits)/8; - int size = (width * format_bits)/8; - - _ctx_CBRLE_decompress (pixel, &temp[0], width, size, u, ctx_mini(count, width-u));//count); - - uint32_t *src = (uint32_t*)&temp[4*u]; - while (count--) - { - ((uint32_t*)rgba)[0] = *src++; - rgba += 4; - } + if (fabsf(matrix->m[0][1]) >0.001f) return 0; + if (fabsf(matrix->m[1][0]) >0.001f) return 0; + return ctx_matrix_no_perspective (matrix); } -static void -ctx_composite_CBRLE (CTX_COMPOSITE_ARGUMENTS) +static CtxFragment ctx_rasterizer_get_fragment_RGBA8 (CtxRasterizer *rasterizer) { - uint8_t *pixel = (uint8_t*)dst; - int format_bits = rasterizer->format->bpp; - pixel-= (x0 *format_bits)/8; - -#if 0 - if (pixel[0] || pixel[1]) - { - - if (CTX_LIKELY(rasterizer->comp_op == ctx_RGBA8_source_over_normal_color)) - { - int width = rasterizer->blit_width; - int size = (width * format_bits)/8; - ctx_CBRLE_compress (rasterizer->color, pixel, width, size, x0, count, - CBRLE_MODE_OVER, coverage, 1); - return; - } - else if (CTX_LIKELY(rasterizer->comp_op == ctx_RGBA8_source_copy_normal_color) && - getenv("CTX_CBRLE_COPY")) - { - int width = rasterizer->blit_width; - int size = (width * format_bits)/8; - ctx_CBRLE_compress (rasterizer->color, pixel, width, size, x0, count, - CBRLE_MODE_COPY, coverage, 1); - return; - } - } - -#endif - - uint8_t pixels[count * 4]; - ctx_CBRLE_to_RGBA8 (rasterizer, x0, dst, &pixels[0], count); - rasterizer->comp_op (rasterizer, &pixels[0], rasterizer->color, x0, coverage, count); - ctx_RGBA8_to_CBRLE (rasterizer, x0, &pixels[0], dst, count); -} - -#endif - - - + CtxGState *gstate = &rasterizer->state->gstate; + CtxSource *g = &rasterizer->state->gstate.source_fill; + switch (gstate->source_fill.type) + { + case CTX_SOURCE_TEXTURE: + { + CtxBuffer *buffer = g->texture.buffer; + if (buffer) + buffer = buffer->color_managed?buffer->color_managed:buffer; + if (!buffer || !buffer->format) + return ctx_fragment_color_RGBA8; + if (buffer->format->pixel_format == CTX_FORMAT_YUV420) + { + return ctx_fragment_image_yuv420_RGBA8_nearest; + } + else +#if CTX_FRAGMENT_SPECIALIZE + switch (buffer->format->bpp) + { + case 1: return ctx_fragment_image_gray1_RGBA8; +#if 1 + case 24: + { + if (gstate->image_smoothing) + { + float factor = ctx_matrix_get_scale (&gstate->transform); + //fprintf (stderr, "{%.3f}", factor); + if (factor < 0.5f) + { + if (rasterizer->swap_red_green) + return ctx_fragment_image_rgb8_RGBA8_box_swap_red_green; + return ctx_fragment_image_rgb8_RGBA8_box; + } +#if CTX_ALWAYS_USE_NEAREST_FOR_SCALE1 + else if (factor > 0.99f && factor < 1.01f) + { + if (rasterizer->swap_red_green) + return ctx_fragment_image_rgb8_RGBA8_nearest_swap_red_green; + return ctx_fragment_image_rgb8_RGBA8_nearest; + } +#endif + else + { + if (rasterizer->swap_red_green) + return ctx_fragment_image_rgb8_RGBA8_bi_swap_red_green; + return ctx_fragment_image_rgb8_RGBA8_bi; + } + } + else + { + if (rasterizer->swap_red_green) + return ctx_fragment_image_rgb8_RGBA8_nearest_swap_red_green; + return ctx_fragment_image_rgb8_RGBA8_nearest; + } + } + break; +#endif + case 32: + { + CtxMatrix *transform = &gstate->source_fill.transform; + CtxExtend extend = rasterizer->state->gstate.extend; + if (gstate->image_smoothing) + { + float factor = ctx_matrix_get_scale (&gstate->transform); + //fprintf (stderr, "[%.3f]", factor); + if (factor < 0.5f) + { + if (rasterizer->swap_red_green) + return ctx_fragment_image_rgba8_RGBA8_box_swap_red_green; + return ctx_fragment_image_rgba8_RGBA8_box; + } +#if CTX_ALWAYS_USE_NEAREST_FOR_SCALE1 + else if (factor > 0.99f && factor < 1.01f && extend == CTX_EXTEND_NONE) + { + if (rasterizer->swap_red_green) + return ctx_fragment_image_rgba8_RGBA8_nearest_copy_swap_red_green; + return ctx_fragment_image_rgba8_RGBA8_nearest_copy; + } +#endif + else + { + if (rasterizer->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 (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 ctx_fragment_image_rgba8_RGBA8_bi_affine_swap_red_green; + } + return ctx_fragment_image_rgba8_RGBA8_bi_generic_swap_red_green; + } - - - - - -#if CTX_ENABLE_RGB565 | CTX_ENABLE_RGB565_BYTESWAPPED - -static inline void -ctx_565_unpack (const uint16_t pixel, - uint8_t *red, - uint8_t *green, - uint8_t *blue, - const int byteswap) -{ - uint16_t byteswapped; - if (byteswap) - { byteswapped = (pixel>>8) | (pixel<<8); } - else - { byteswapped = pixel; } - uint8_t b = (byteswapped & 31) <<3; - uint8_t g = ( (byteswapped>>5) & 63) <<2; - uint8_t r = ( (byteswapped>>11) & 31) <<3; - -#if 0 - *blue = (b > 248) * 255 + (b <= 248) * b; - *green = (g > 248) * 255 + (g <= 248) * g; - *red = (r > 248) * 255 + (r <= 248) * r; + 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 (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 ctx_fragment_image_rgba8_RGBA8_bi_affine; + } + return ctx_fragment_image_rgba8_RGBA8_bi_generic; + } + } + else + { + if (rasterizer->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) + { + return ctx_fragment_image_rgba8_RGBA8_nearest_copy_swap_red_green; + 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_nearest_scale_swap_red_green; + } + return ctx_fragment_image_rgba8_RGBA8_nearest_affine_swap_red_green; + } + return ctx_fragment_image_rgba8_RGBA8_nearest_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) + { + 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_nearest_scale; + } + return ctx_fragment_image_rgba8_RGBA8_nearest_affine; + } + return ctx_fragment_image_rgba8_RGBA8_nearest_generic; + } + } + default: return ctx_fragment_image_RGBA8; + } #else - *blue = b; - *green = g; - *red = r; + return ctx_fragment_image_RGBA8; #endif -} + } -static inline uint32_t -ctx_565_unpack_32 (const uint16_t pixel, - const int byteswap) -{ - uint16_t byteswapped; - if (byteswap) - { byteswapped = (pixel>>8) | (pixel<<8); } - else - { byteswapped = pixel; } - uint32_t b = (byteswapped & 31) <<3; - uint32_t g = ( (byteswapped>>5) & 63) <<2; - uint32_t r = ( (byteswapped>>11) & 31) <<3; -#if 0 - b = (b > 248) * 255 + (b <= 248) * b; - g = (g > 248) * 255 + (g <= 248) * g; - r = (r > 248) * 255 + (r <= 248) * r; + case CTX_SOURCE_COLOR: return ctx_fragment_color_RGBA8; +#if CTX_GRADIENTS + case CTX_SOURCE_LINEAR_GRADIENT: return ctx_fragment_linear_gradient_RGBA8; + case CTX_SOURCE_RADIAL_GRADIENT: return ctx_fragment_radial_gradient_RGBA8; #endif - - return r + (g << 8) + (b << 16) + (0xff << 24); + } + return ctx_fragment_color_RGBA8; } +static inline void +ctx_init_uv (CtxRasterizer *rasterizer, + int x0, + int y0, + float *u0, float *v0, float *w0, float *ud, float *vd, float *wd) + //float *u0, float *v0, float *w0, float *ud, float *vd, float *wd) +{ + CtxMatrix *transform = &rasterizer->state->gstate.source_fill.transform; + *u0 = transform->m[0][0] * (x0 + 0.0f) + + transform->m[0][1] * (y0 + 0.0f) + + transform->m[0][2]; + *v0 = transform->m[1][0] * (x0 + 0.0f) + + transform->m[1][1] * (y0 + 0.0f) + + transform->m[1][2]; + *w0 = transform->m[2][0] * (x0 + 0.0f) + + transform->m[2][1] * (y0 + 0.0f) + + transform->m[2][2]; + *ud = transform->m[0][0]; + *vd = transform->m[1][0]; + *wd = transform->m[2][0]; +} -static inline uint16_t -ctx_888_to_565 (uint32_t in, int byteswap) +static inline void +ctx_u8_copy_normal (int components, CTX_COMPOSITE_ARGUMENTS) { - uint8_t *rgb=(uint8_t*)(&in); - return ctx_565_pack (rgb[0],rgb[1],rgb[2], byteswap); + if (CTX_UNLIKELY(rasterizer->fragment)) + { + 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); + while (count--) + { + uint8_t cov = *coverage; + if (CTX_UNLIKELY(cov == 0)) + { + u0+=ud; + v0+=vd; + } + else + { + rasterizer->fragment (rasterizer, u0, v0, w0, src, 1, ud, vd, wd); + u0+=ud; + v0+=vd; + if (cov == 255) + { + for (int c = 0; c < components; c++) + dst[c] = src[c]; + } + else + { + uint8_t rcov = 255 - cov; + for (int c = 0; c < components; c++) + { dst[c] = (src[c]*cov + dst[c]*rcov)/255; } + } + } + dst += components; + coverage ++; + } + return; + } + + while (count--) + { + uint8_t cov = *coverage; + uint8_t rcov = 255-cov; + for (int c = 0; c < components; c++) + { dst[c] = (src[c]*cov+dst[c]*rcov)/255; } + dst += components; + coverage ++; + } } -static inline uint32_t -ctx_565_to_888 (uint16_t in, int byteswap) +static void +ctx_u8_clear_normal (int components, CTX_COMPOSITE_ARGUMENTS) { - uint32_t ret = 0; - uint8_t *rgba=(uint8_t*)&ret; - ctx_565_unpack (in, - &rgba[0], - &rgba[1], - &rgba[2], - byteswap); - //rgba[3]=255; - return ret; + while (count--) + { + uint8_t cov = *coverage; + for (int c = 0; c < components; c++) + { dst[c] = (dst[c] * (256-cov)) >> 8; } + coverage ++; + dst += components; + } } -#endif -#if CTX_ENABLE_RGB565 +typedef enum { + CTX_PORTER_DUFF_0, + CTX_PORTER_DUFF_1, + CTX_PORTER_DUFF_ALPHA, + CTX_PORTER_DUFF_1_MINUS_ALPHA, +} CtxPorterDuffFactor; +#define \ +ctx_porter_duff_factors(mode, foo, bar)\ +{\ + switch (mode)\ + {\ + case CTX_COMPOSITE_SOURCE_ATOP:\ + f_s = CTX_PORTER_DUFF_ALPHA;\ + f_d = CTX_PORTER_DUFF_1_MINUS_ALPHA;\ + break;\ + case CTX_COMPOSITE_DESTINATION_ATOP:\ + f_s = CTX_PORTER_DUFF_1_MINUS_ALPHA;\ + f_d = CTX_PORTER_DUFF_ALPHA;\ + break;\ + case CTX_COMPOSITE_DESTINATION_IN:\ + f_s = CTX_PORTER_DUFF_0;\ + f_d = CTX_PORTER_DUFF_ALPHA;\ + break;\ + case CTX_COMPOSITE_DESTINATION:\ + f_s = CTX_PORTER_DUFF_0;\ + f_d = CTX_PORTER_DUFF_1;\ + break;\ + case CTX_COMPOSITE_SOURCE_OVER:\ + f_s = CTX_PORTER_DUFF_1;\ + f_d = CTX_PORTER_DUFF_1_MINUS_ALPHA;\ + break;\ + case CTX_COMPOSITE_DESTINATION_OVER:\ + f_s = CTX_PORTER_DUFF_1_MINUS_ALPHA;\ + f_d = CTX_PORTER_DUFF_1;\ + break;\ + case CTX_COMPOSITE_XOR:\ + f_s = CTX_PORTER_DUFF_1_MINUS_ALPHA;\ + f_d = CTX_PORTER_DUFF_1_MINUS_ALPHA;\ + break;\ + case CTX_COMPOSITE_DESTINATION_OUT:\ + f_s = CTX_PORTER_DUFF_0;\ + f_d = CTX_PORTER_DUFF_1_MINUS_ALPHA;\ + break;\ + case CTX_COMPOSITE_SOURCE_OUT:\ + f_s = CTX_PORTER_DUFF_1_MINUS_ALPHA;\ + f_d = CTX_PORTER_DUFF_0;\ + break;\ + case CTX_COMPOSITE_SOURCE_IN:\ + f_s = CTX_PORTER_DUFF_ALPHA;\ + f_d = CTX_PORTER_DUFF_0;\ + break;\ + case CTX_COMPOSITE_COPY:\ + f_s = CTX_PORTER_DUFF_1;\ + f_d = CTX_PORTER_DUFF_1_MINUS_ALPHA;\ + break;\ + default:\ + case CTX_COMPOSITE_CLEAR:\ + f_s = CTX_PORTER_DUFF_0;\ + f_d = CTX_PORTER_DUFF_0;\ + break;\ + }\ +} static inline void -ctx_RGB565_to_RGBA8 (CtxRasterizer *rasterizer, int x, const void *buf, uint8_t *rgba, int count) +ctx_u8_source_over_normal_color (int components, + CtxRasterizer *rasterizer, + uint8_t * __restrict__ dst, + uint8_t * __restrict__ src, + int x0, + uint8_t * __restrict__ coverage, + int count) { - const uint16_t *pixel = (uint16_t *) buf; + uint8_t tsrc[5]; + *((uint32_t*)tsrc) = *((uint32_t*)src); + while (count--) - { - // 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) - { rgba[3] = 0; } -#endif - pixel+=1; - rgba +=4; - } + { + uint8_t cov = *coverage++; + for (int c = 0; c < components; c++) + dst[c] = ((((tsrc[c] * cov)) + (dst[c] * (((((255+(tsrc[components-1] * cov))>>8))^255 ))))>>8); + dst+=components; + } } static inline void -ctx_RGBA8_to_RGB565 (CtxRasterizer *rasterizer, int x, const uint8_t *rgba, void *buf, int count) +ctx_u8_source_copy_normal_color (int components, CTX_COMPOSITE_ARGUMENTS) { - uint16_t *pixel = (uint16_t *) buf; while (count--) - { -#if CTX_RGB565_ALPHA - if (rgba[3]==0) - { pixel[0] = ctx_565_pack (255, 0, 255, 0); } - else -#endif - { pixel[0] = ctx_565_pack (rgba[0], rgba[1], rgba[2], 0); } - pixel+=1; - rgba +=4; - } + { + for (int c = 0; c < components; c++) + dst[c] = ctx_lerp_u8(dst[c],src[c],coverage[0]); + coverage ++; + dst+=components; + } } -static void -ctx_RGBA8_source_over_normal_color (CTX_COMPOSITE_ARGUMENTS); -static void -ctx_RGBA8_source_copy_normal_color (CTX_COMPOSITE_ARGUMENTS); - -static void -ctx_composite_RGB565 (CTX_COMPOSITE_ARGUMENTS) +static inline void +ctx_RGBA8_source_over_normal_buf (CTX_COMPOSITE_ARGUMENTS, uint8_t *tsrc) { -#if 1 - if (CTX_LIKELY(rasterizer->comp_op == ctx_RGBA8_source_over_normal_color)) + while (count--) { - uint32_t si_ga = ((uint32_t*)rasterizer->color)[1]; - uint32_t si_rb = ((uint32_t*)rasterizer->color)[2]; - uint32_t si_a = si_ga >> 16; + uint32_t si_ga = ((*((uint32_t*)tsrc)) & 0xff00ff00) >> 8; + uint32_t si_rb = (*((uint32_t*)tsrc)) & 0x00ff00ff; +// uint32_t di_ga = ((*((uint32_t*)dst)) & 0xff00ff00) >> 8; +// uint32_t di_rb = (*((uint32_t*)dst)) & 0x00ff00ff; + uint32_t si_a = si_ga >> 16; + uint32_t cov = *coverage; + uint32_t racov = (255-((255+si_a*cov)>>8)); + *((uint32_t*)(dst)) = - while (count--) - { - uint32_t cov = *coverage++; - uint32_t rcov = (((255+si_a * cov)>>8))^255; - uint32_t di = ctx_565_to_888 (*((uint16_t*)dst), 0); - uint32_t di_ga = ((di & 0xff00ff00) >> 8); - uint32_t di_rb = (di & 0x00ff00ff); - *((uint16_t*)(dst)) = - ctx_888_to_565((((si_rb * cov + 0xff00ff + di_rb * rcov) & 0xff00ff00) >> 8) | - ((si_ga * cov + 0xff00ff + di_ga * rcov) & 0xff00ff00), 0); - dst+=2; - } - return; - } -#endif + (((si_rb*cov+0xff00ff+(((*((uint32_t*)(dst)))&0x00ff00ff)*racov))>>8)&0x00ff00ff)| + ((si_ga*cov+0xff00ff+((((*((uint32_t*)(dst)))&0xff00ff00)>>8)*racov))&0xff00ff00); - uint8_t pixels[count * 4]; - ctx_RGB565_to_RGBA8 (rasterizer, x0, dst, &pixels[0], count); - rasterizer->comp_op (rasterizer, &pixels[0], rasterizer->color, x0, coverage, count); - ctx_RGBA8_to_RGB565 (rasterizer, x0, &pixels[0], dst, count); + coverage ++; + tsrc += 4; + dst += 4; + } } -#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) +ctx_RGBA8_source_over_normal_full_cov_buf (CTX_COMPOSITE_ARGUMENTS, uint8_t *tsrc) { - const uint16_t *pixel = (uint16_t *) buf; + uint32_t *ttsrc = (uint32_t*)tsrc; + uint32_t *ddst = (uint32_t*)dst; 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; - } + { + uint32_t si_ga = ((*ttsrc) & 0xff00ff00) >> 8; + uint32_t si_rb = (*ttsrc++) & 0x00ff00ff; + uint32_t si_a = si_ga >> 16; + uint32_t racov = si_a^255; + *(ddst) = + (((si_rb*255+0xff00ff+(((*ddst)&0x00ff00ff)*racov))>>8)&0x00ff00ff)| + ((si_ga*255+0xff00ff+((((*ddst)&0xff00ff00)>>8)*racov))&0xff00ff00); + ddst++; + } } static inline void -ctx_RGBA8_to_RGB565_BS (CtxRasterizer *rasterizer, int x, const uint8_t *rgba, void *buf, int count) +ctx_RGBA8_source_copy_normal_buf (CTX_COMPOSITE_ARGUMENTS, uint8_t *__restrict__ tsrc) { - uint16_t *pixel = (uint16_t *) buf; + uint32_t *ttsrc = (uint32_t*)tsrc; + uint32_t *ddst = (uint32_t*)dst; 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; - } -} + { + *ddst=ctx_lerp_RGBA8 (*ddst, *(ttsrc++), *(coverage++)); + ddst++; + } +} -static void -ctx_composite_RGB565_BS (CTX_COMPOSITE_ARGUMENTS) +static inline void +ctx_RGBA8_source_over_normal_fragment (CTX_COMPOSITE_ARGUMENTS) { -#if 1 - if (CTX_LIKELY(rasterizer->comp_op == ctx_RGBA8_source_over_normal_color)) - { - uint32_t si_ga = ((uint32_t*)rasterizer->color)[1]; - uint32_t si_rb = ((uint32_t*)rasterizer->color)[2]; - uint32_t si_a = si_ga >> 16; + 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); + 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]); +} - while (count--) +static inline void +ctx_RGBA8_source_over_normal_full_cov_fragment (CTX_COMPOSITE_ARGUMENTS, int scanlines) +{ + CtxMatrix *transform = &rasterizer->state->gstate.source_fill.transform; + int scan = rasterizer->scanline /CTX_FULL_AA; + + if (CTX_LIKELY(ctx_matrix_no_perspective (transform))) + { + float u0, v0, ud, vd, w0, wd; + ctx_init_uv (rasterizer, x0, scan, &u0, &v0, &w0, &ud, &vd, &wd); + for (int y = 0; y < scanlines; y++) { - uint32_t cov = *coverage++; - uint32_t rcov = (((255+si_a * cov)>>8))^255; - uint32_t di = ctx_565_to_888 (*((uint16_t*)dst), 1); - uint32_t di_ga = ((di & 0xff00ff00) >> 8); - uint32_t di_rb = (di & 0x00ff00ff); - *((uint16_t*)(dst)) = - ctx_888_to_565((((si_rb * cov + 0xff00ff + di_rb * rcov) & 0xff00ff00) >> 8) | - ((si_ga * cov + 0xff00ff + di_ga * rcov) & 0xff00ff00), 1); - dst+=2; + 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]); + u0 -= vd; + v0 += ud; + dst += rasterizer->blit_stride; + } + } + else + { + 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]); + dst += rasterizer->blit_stride; } - return; } -#endif - - uint8_t pixels[count * 4]; - ctx_RGB565_BS_to_RGBA8 (rasterizer, x0, dst, &pixels[0], count); - rasterizer->comp_op (rasterizer, &pixels[0], rasterizer->color, x0, coverage, count); - ctx_RGBA8_to_RGB565_BS (rasterizer, x0, &pixels[0], dst, count); } -#endif - -static inline uint32_t -ctx_over_RGBA8 (uint32_t dst, uint32_t src, uint32_t cov) +static inline void +ctx_RGBA8_source_copy_normal_fragment (CTX_COMPOSITE_ARGUMENTS) { - uint32_t si_ga = (src & 0xff00ff00) >> 8; - uint32_t si_rb = src & 0x00ff00ff; - uint32_t si_a = si_ga >> 16; - uint32_t rcov = ((255+si_a * cov)>>8)^255; - uint32_t di_ga = ( dst & 0xff00ff00) >> 8; - uint32_t di_rb = dst & 0x00ff00ff; - return - ((((si_rb * cov) + 0xff00ff + (di_rb * rcov)) & 0xff00ff00) >> 8) | - (((si_ga * cov) + 0xff00ff + (di_ga * rcov)) & 0xff00ff00); + 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); + 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]); } -static inline uint32_t -ctx_over_RGBA8_full (uint32_t dst, uint32_t src) +static void +ctx_RGBA8_source_over_normal_color (CTX_COMPOSITE_ARGUMENTS) { - uint32_t si_ga = (src & 0xff00ff00) >> 8; - uint32_t si_rb = src & 0x00ff00ff; +#if CTX_REFERENCE + ctx_u8_source_over_normal_color (4, rasterizer, dst, src, x0, coverage, count); +#else + uint32_t si_ga = ((uint32_t*)rasterizer->color)[1]; + uint32_t si_rb = ((uint32_t*)rasterizer->color)[2]; uint32_t si_a = si_ga >> 16; - uint32_t rcov = si_a^255; - uint32_t di_ga = (dst & 0xff00ff00) >> 8; - uint32_t di_rb = dst & 0x00ff00ff; - return - ((((si_rb * 255) + 0xff00ff + (di_rb * rcov)) & 0xff00ff00) >> 8) | - (((si_ga * 255) + 0xff00ff + (di_ga * rcov)) & 0xff00ff00); -} -static inline uint32_t -ctx_over_RGBA8_2 (uint32_t dst, uint32_t si_ga, uint32_t si_rb, uint32_t si_a, uint32_t cov) -{ - uint32_t rcov = ((si_a * cov)/255)^255; - uint32_t di_ga = (dst & 0xff00ff00) >> 8; - uint32_t di_rb = dst & 0x00ff00ff; - return - ((((si_rb * cov) + 0xff00ff + (di_rb * rcov)) & 0xff00ff00) >> 8) | - (((si_ga * cov) + 0xff00ff + (di_ga * rcov)) & 0xff00ff00); + while (count--) + { + uint32_t cov = *coverage++; + uint32_t rcov = (((255+si_a * cov)>>8))^255; + uint32_t di = *((uint32_t*)dst); + uint32_t di_ga = ((di & 0xff00ff00) >> 8); + uint32_t di_rb = (di & 0x00ff00ff); + *((uint32_t*)(dst)) = + (((si_rb * cov + 0xff00ff + di_rb * rcov) & 0xff00ff00) >> 8) | + ((si_ga * cov + 0xff00ff + di_ga * rcov) & 0xff00ff00); + dst+=4; + } +#endif } -static inline uint32_t -ctx_over_RGBA8_full_2 (uint32_t dst, uint32_t si_ga_full, uint32_t si_rb_full, uint32_t si_a) +static void +ctx_RGBA8_source_copy_normal_color (CTX_COMPOSITE_ARGUMENTS) { - uint32_t rcov = si_a^255; - uint32_t di_ga = ( dst & 0xff00ff00) >> 8; - uint32_t di_rb = dst & 0x00ff00ff; - return - ((((si_rb_full) + (di_rb * rcov)) & 0xff00ff00) >> 8) | - (((si_ga_full) + (di_ga * rcov)) & 0xff00ff00); -} +#if CTX_REFERENCE + ctx_u8_source_copy_normal_color (4, rasterizer, dst, src, x0, coverage, count); +#else + uint32_t si_ga = ((uint32_t*)rasterizer->color)[1]; + uint32_t si_rb = ((uint32_t*)rasterizer->color)[2]; -static inline void ctx_span_set_color (uint32_t *dst_pix, uint32_t val, int count) -{ - if (count>0) - while(count--) - *dst_pix++=val; -} + while (count--) + { + uint32_t cov = *coverage++; + uint32_t di = *((uint32_t*)dst); + uint32_t di_ga = (di & 0xff00ff00); + uint32_t di_rb = (di & 0x00ff00ff); -static inline void ctx_span_set_colorb (uint32_t *dst_pix, uint32_t val, int count) -{ - while(count--) - *dst_pix++=val; + uint32_t d_rb = si_rb - di_rb; + uint32_t d_ga = si_ga - (di_ga>>8); + + *((uint32_t*)(dst)) = + + (((di_rb + ((d_rb * cov)>>8)) & 0x00ff00ff)) | + ((di_ga + ((d_ga * cov) & 0xff00ff00))); + dst +=4; + } +#endif } -static inline void ctx_span_set_colorbu (uint32_t *dst_pix, uint32_t val, unsigned int count) +static void +ctx_RGBA8_clear_normal (CTX_COMPOSITE_ARGUMENTS) { - while(count--) - *dst_pix++=val; + ctx_u8_clear_normal (4, rasterizer, dst, src, x0, coverage, count); } -static inline void ctx_span_set_color_x4 (uint32_t *dst_pix, uint32_t *val, int count) +static void +ctx_u8_blend_normal (int components, uint8_t * __restrict__ dst, uint8_t *src, uint8_t *blended, int count) { - if (count>0) - while(count--) + for (int j = 0; j < count; j++) { - *dst_pix++=val[0]; - *dst_pix++=val[1]; - *dst_pix++=val[2]; - *dst_pix++=val[3]; + switch (components) + { + case 3: + ((uint8_t*)(blended))[2] = ((uint8_t*)(src))[2]; + *((uint16_t*)(blended)) = *((uint16_t*)(src)); + break; + case 2: + *((uint16_t*)(blended)) = *((uint16_t*)(src)); + break; + case 5: + *((uint32_t*)(blended)) = *((uint32_t*)(src)); + ((uint8_t*)(blended))[4] = ((uint8_t*)(src))[4]; + break; + case 4: + *((uint32_t*)(blended)) = *((uint32_t*)(src)); + break; + default: + { + for (int i = 0; i<components;i++) + blended[i] = src[i]; + } + break; + } + blended+=components; + src+=components; } } -#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) +/* branchless 8bit add that maxes out at 255 */ +static inline uint8_t ctx_sadd8(uint8_t a, uint8_t b) { - 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 */ + uint16_t s = (uint16_t)a+b; + return -(s>>8) | (uint8_t)s; +} - //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]; +#if CTX_BLENDING_AND_COMPOSITING - 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); +#define ctx_u8_blend_define(name, CODE) \ +static inline void \ +ctx_u8_blend_##name (int components, uint8_t * __restrict__ dst, uint8_t *src, uint8_t *blended, int count)\ +{\ + for (int j = 0; j < count; j++) { \ + uint8_t *s=src; uint8_t b[components];\ + ctx_u8_deassociate_alpha (components, dst, b);\ + CODE;\ + blended[components-1] = src[components-1];\ + ctx_u8_associate_alpha (components, blended);\ + src += components;\ + dst += components;\ + blended += components;\ + }\ +} - int iter = 0; +#define ctx_u8_blend_define_seperable(name, CODE) \ + ctx_u8_blend_define(name, for (int c = 0; c < components-1; c++) { CODE ;}) \ - int loaded_v = -1; - int top = iter % 2; +ctx_u8_blend_define_seperable(multiply, blended[c] = (b[c] * s[c])/255;) +ctx_u8_blend_define_seperable(screen, blended[c] = s[c] + b[c] - (s[c] * b[c])/255;) +ctx_u8_blend_define_seperable(overlay, blended[c] = b[c] < 127 ? (s[c] * b[c])/255 : + s[c] + b[c] - (s[c] * b[c])/255;) +ctx_u8_blend_define_seperable(darken, blended[c] = ctx_mini (b[c], s[c])) +ctx_u8_blend_define_seperable(lighten, blended[c] = ctx_maxi (b[c], s[c])) +ctx_u8_blend_define_seperable(color_dodge, blended[c] = b[c] == 0 ? 0 : + s[c] == 255 ? 255 : ctx_mini(255, (255 * b[c]) / (255-s[c]))) +ctx_u8_blend_define_seperable(color_burn, blended[c] = b[c] == 1 ? 1 : + s[c] == 0 ? 0 : 255 - ctx_mini(255, (255*(255 - b[c])) / s[c])) +ctx_u8_blend_define_seperable(hard_light, blended[c] = s[c] < 127 ? (b[c] * s[c])/255 : + b[c] + s[c] - (b[c] * s[c])/255;) +ctx_u8_blend_define_seperable(difference, blended[c] = (b[c] - s[c])) +ctx_u8_blend_define_seperable(divide, blended[c] = s[c]?(255 * b[c]) / s[c]:0) +ctx_u8_blend_define_seperable(addition, blended[c] = ctx_sadd8 (s[c], b[c])) +ctx_u8_blend_define_seperable(subtract, blended[c] = ctx_maxi(0, s[c]-b[c])) +ctx_u8_blend_define_seperable(exclusion, blended[c] = b[c] + s[c] - 2 * (b[c] * s[c]/255)) +ctx_u8_blend_define_seperable(soft_light, + if (s[c] <= 255/2) + { + blended[c] = b[c] - (255 - 2 * s[c]) * b[c] * (255 - b[c]) / (255 * 255); + } + else + { + int d; + if (b[c] <= 255/4) + d = (((16 * b[c] - 12 * 255)/255 * b[c] + 4 * 255) * b[c])/255; + else + d = (int)(ctx_sqrtf(b[c]/255.0f) * 255.4f); + blended[c] = (b[c] + (2 * s[c] - 255) * (d - b[c]))/255; + } +) +static int ctx_int_get_max (int components, int *c) +{ + int max = 0; + for (int i = 0; i < components - 1; i ++) + { + if (c[i] > max) max = c[i]; + } + return max; +} - { // 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; - } - } +static int ctx_int_get_min (int components, int *c) +{ + int min = 400; + for (int i = 0; i < components - 1; i ++) + { + if (c[i] < min) min = c[i]; + } + return min; +} - { // 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) +static int ctx_int_get_lum (int components, int *c) +{ + switch (components) + { + case 3: + case 4: + return (int)(CTX_CSS_RGB_TO_LUMINANCE(c)); + case 1: + case 2: + return c[0]; + break; + default: { - 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); - } + int sum = 0; + for (int i = 0; i < components - 1; i ++) + { + sum += c[i]; + } + return sum / (components - 1); + } + break; + } +} - ctx_lerp_RGBA8_split (*src0, *src1, ui>>8, &rb_row[top][xa], &rb_row[top][xa+1]); - ui += ui_delta; - } - iter++; - top = iter % 2; +static int ctx_u8_get_lum (int components, uint8_t *c) +{ + switch (components) + { + case 3: + case 4: + return (int)(CTX_CSS_RGB_TO_LUMINANCE(c)); + case 1: + case 2: + return c[0]; + break; + default: + { + int sum = 0; + for (int i = 0; i < components - 1; i ++) + { + sum += c[i]; + } + return sum / (components - 1); } - + break; + } +} +static int ctx_u8_get_sat (int components, uint8_t *c) +{ + switch (components) + { + case 3: + case 4: + { int r = c[0]; + int g = c[1]; + int b = c[2]; + return ctx_maxi(r, ctx_maxi(g,b)) - ctx_mini(r,ctx_mini(g,b)); + } + break; + case 1: + case 2: + return 0.0; + break; + default: { - uint32_t*dst_i = copy?(uint32_t*)dst:(uint32_t*)tsrc; - int ntop = !top; - for (unsigned int xa = 0; xa < width * 2; xa+=2) + int min = 1000; + int max = -1000; + for (int i = 0; i < components - 1; i ++) { - *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 (c[i] < min) min = c[i]; + if (c[i] > max) max = c[i]; } - if (!copy) - ctx_RGBA8_source_over_normal_full_cov_buf (rasterizer, - dst, NULL, x0, NULL, width, &tsrc[0]); + return max-min; } - row_u -= vi_delta; - row_v += ui_delta; - dst += blit_stride; - } + break; } } -static inline void ctx_RGBA8_image_rgba8_RGBA8_bi_affine_fill_rect (CtxRasterizer *rasterizer, int x0, int y0, int x1, int y1, int copy) +static void ctx_u8_set_lum (int components, uint8_t *c, uint8_t lum) { - 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; + int d = lum - ctx_u8_get_lum (components, c); + int tc[components]; + for (int i = 0; i < components - 1; i++) + { + tc[i] = c[i] + d; + } - 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; + int l = ctx_int_get_lum (components, tc); + int n = ctx_int_get_min (components, tc); + int x = ctx_int_get_max (components, tc); - unsigned int width = x1-x0+1; - unsigned int height = y1-y0+1; + if (n < 0 && l!=n) + { + for (int i = 0; i < components - 1; i++) + tc[i] = l + (((tc[i] - l) * l) / (l-n)); + } - CtxSource *g = &rasterizer->state->gstate.source_fill; - CtxBuffer *buffer = g->texture.buffer->color_managed?g->texture.buffer->color_managed:g->texture.buffer; + if (x > 255 && x!=l) + { + for (int i = 0; i < components - 1; i++) + tc[i] = l + (((tc[i] - l) * (255 - l)) / (x-l)); + } + for (int i = 0; i < components - 1; i++) + c[i] = tc[i]; +} - int 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]; +static void ctx_u8_set_sat (int components, uint8_t *c, uint8_t sat) +{ + int max = 0, mid = 1, min = 2; + + if (c[min] > c[mid]){int t = min; min = mid; mid = t;} + if (c[mid] > c[max]){int t = mid; mid = max; max = t;} + if (c[min] > c[mid]){int t = min; min = mid; mid = t;} - 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); + if (c[max] > c[min]) + { + c[mid] = ((c[mid]-c[min]) * sat) / (c[max] - c[min]); + c[max] = sat; + } + else + { + c[mid] = c[max] = 0; + } + c[min] = 0; +} - int iter = 0; +ctx_u8_blend_define(color, + for (int i = 0; i < components; i++) + blended[i] = s[i]; + ctx_u8_set_lum(components, blended, ctx_u8_get_lum (components, s)); +) - int loaded_v = -1; - int top = iter % 2; +ctx_u8_blend_define(hue, + int in_sat = ctx_u8_get_sat(components, b); + int in_lum = ctx_u8_get_lum(components, b); + for (int i = 0; i < components; i++) + blended[i] = s[i]; + ctx_u8_set_sat(components, blended, in_sat); + ctx_u8_set_lum(components, blended, in_lum); +) +ctx_u8_blend_define(saturation, + int in_sat = ctx_u8_get_sat(components, s); + int in_lum = ctx_u8_get_lum(components, b); + for (int i = 0; i < components; i++) + blended[i] = b[i]; + ctx_u8_set_sat(components, blended, in_sat); + ctx_u8_set_lum(components, blended, in_lum); +) - { // 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; - } - } +ctx_u8_blend_define(luminosity, + int in_lum = ctx_u8_get_lum(components, s); + for (int i = 0; i < components; i++) + blended[i] = b[i]; + ctx_u8_set_lum(components, blended, in_lum); +) +#endif - for (unsigned int y = 0; y < height; y++) +CTX_INLINE static void +ctx_u8_blend (int components, CtxBlend blend, uint8_t * __restrict__ dst, uint8_t *src, uint8_t *blended, int count) +{ +#if CTX_BLENDING_AND_COMPOSITING + switch (blend) { - 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; + case CTX_BLEND_NORMAL: ctx_u8_blend_normal (components, dst, src, blended, count); break; + case CTX_BLEND_MULTIPLY: ctx_u8_blend_multiply (components, dst, src, blended, count); break; + case CTX_BLEND_SCREEN: ctx_u8_blend_screen (components, dst, src, blended, count); break; + case CTX_BLEND_OVERLAY: ctx_u8_blend_overlay (components, dst, src, blended, count); break; + case CTX_BLEND_DARKEN: ctx_u8_blend_darken (components, dst, src, blended, count); break; + case CTX_BLEND_LIGHTEN: ctx_u8_blend_lighten (components, dst, src, blended, count); break; + case CTX_BLEND_COLOR_DODGE: ctx_u8_blend_color_dodge (components, dst, src, blended, count); break; + case CTX_BLEND_COLOR_BURN: ctx_u8_blend_color_burn (components, dst, src, blended, count); break; + case CTX_BLEND_HARD_LIGHT: ctx_u8_blend_hard_light (components, dst, src, blended, count); break; + case CTX_BLEND_SOFT_LIGHT: ctx_u8_blend_soft_light (components, dst, src, blended, count); break; + case CTX_BLEND_DIFFERENCE: ctx_u8_blend_difference (components, dst, src, blended, count); break; + case CTX_BLEND_EXCLUSION: ctx_u8_blend_exclusion (components, dst, src, blended, count); break; + case CTX_BLEND_COLOR: ctx_u8_blend_color (components, dst, src, blended, count); break; + case CTX_BLEND_HUE: ctx_u8_blend_hue (components, dst, src, blended, count); break; + case CTX_BLEND_SATURATION: ctx_u8_blend_saturation (components, dst, src, blended, count); break; + case CTX_BLEND_LUMINOSITY: ctx_u8_blend_luminosity (components, dst, src, blended, count); break; + case CTX_BLEND_ADDITION: ctx_u8_blend_addition (components, dst, src, blended, count); break; + case CTX_BLEND_DIVIDE: ctx_u8_blend_divide (components, dst, src, blended, count); break; + case CTX_BLEND_SUBTRACT: ctx_u8_blend_subtract (components, dst, src, blended, count); break; + } +#else + switch (blend) + { + default: ctx_u8_blend_normal (components, dst, src, blended, count); break; } + +#endif } -#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) +CTX_INLINE static void +__ctx_u8_porter_duff (CtxRasterizer *rasterizer, + int components, + uint8_t * dst, + uint8_t * src, + int x0, + uint8_t * __restrict__ coverage, + int count, + CtxCompositingMode compositing_mode, + CtxFragment fragment, + CtxBlend blend) { - 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); - - uint32_t *dst = ( (uint32_t *) rasterizer->buf); - int blit_stride = rasterizer->blit_stride/4; - dst += (y0 - rasterizer->blit_y) * blit_stride; - dst += (x0); - - 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; + CtxPorterDuffFactor f_s, f_d; + ctx_porter_duff_factors (compositing_mode, &f_s, &f_d); + CtxGState *gstate = &rasterizer->state->gstate; + uint8_t global_alpha_u8 = gstate->global_alpha_u8; + uint8_t tsrc[components * count]; + int src_step = 0; - 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; - int u = x0;// + 0.5f; - int v = y0;// + 0.5f; + if (gstate->source_fill.type == CTX_SOURCE_COLOR) + { + src = &tsrc[0]; + memcpy (src, rasterizer->color, 4); + if (blend != CTX_BLEND_NORMAL) + ctx_u8_blend (components, blend, dst, src, src, 1); + } + else + { + float u0 = 0; float v0 = 0; + float ud = 0; float vd = 0; + float w0 = 1; float wd = 0; + src = &tsrc[0]; - uint32_t *src = ((uint32_t*)buffer->data) + bwidth * v + u; + ctx_init_uv (rasterizer, x0, rasterizer->scanline/CTX_FULL_AA, &u0, &v0, &w0, &ud, &vd, &wd); + fragment (rasterizer, u0, v0, w0, src, count, ud, vd, wd); + if (blend != CTX_BLEND_NORMAL) + ctx_u8_blend (components, blend, dst, src, src, count); + src_step = components; + } - int pre = ctx_mini(ctx_maxi(-u,0), width); + while (count--) + { + uint32_t cov = *coverage; - width-=pre; - u+=pre; + if (CTX_UNLIKELY(global_alpha_u8 != 255)) + cov = (cov * global_alpha_u8 + 255) >> 8; - int core = ctx_mini (width, bwidth - u); + uint8_t csrc[components]; + for (int c = 0; c < components; c++) + csrc[c] = (src[c] * cov + 255) >> 8; - if (copy) - { - if (core>0) + for (int c = 0; c < components; c++) { - uint32_t *t_dst = dst; - for (unsigned int y = 0; y < height; y++) + uint32_t res = 0; +#if 1 + switch (f_s) { - if (CTX_LIKELY((v >= 0 && v < bheight))) - { - memcpy (t_dst, src + pre, core * 4); - } - v++; - src += bwidth; - t_dst += blit_stride; + case CTX_PORTER_DUFF_0: break; + case CTX_PORTER_DUFF_1: res += (csrc[c] ); break; + case CTX_PORTER_DUFF_ALPHA: res += (csrc[c] * dst[components-1] + 255) >> 8; break; + case CTX_PORTER_DUFF_1_MINUS_ALPHA: res += (csrc[c] * (256-dst[components-1])) >> 8; break; } - } - } - else - { - if (core>0) - { - uint32_t *t_dst = dst; - for (unsigned int y = 0; y < height; y++) + switch (f_d) { - 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); - } - v++; - src += bwidth; - t_dst += blit_stride; + case CTX_PORTER_DUFF_0: break; + case CTX_PORTER_DUFF_1: res += dst[c]; break; + case CTX_PORTER_DUFF_ALPHA: res += (dst[c] * csrc[components-1] + 255) >> 8; break; + case CTX_PORTER_DUFF_1_MINUS_ALPHA: res += (dst[c] * (256-csrc[components-1])) >> 8; break; + } +#else + switch (f_s) + { + case CTX_PORTER_DUFF_0: break; + case CTX_PORTER_DUFF_1: res += (csrc[c] ); break; + case CTX_PORTER_DUFF_ALPHA: res += (csrc[c] * dst[components-1])/255; break; + case CTX_PORTER_DUFF_1_MINUS_ALPHA: res += (csrc[c] * (255-dst[components-1]))/255; break; + } + switch (f_d) + { + case CTX_PORTER_DUFF_0: break; + case CTX_PORTER_DUFF_1: res += dst[c]; break; + case CTX_PORTER_DUFF_ALPHA: res += (dst[c] * csrc[components-1])/255; break; + case CTX_PORTER_DUFF_1_MINUS_ALPHA: res += (dst[c] * (255-csrc[components-1]))/255; break; } +#endif + dst[c] = res; } + coverage ++; + src+=src_step; + dst+=components; } } + +CTX_INLINE static void +_ctx_u8_porter_duff (CtxRasterizer *rasterizer, + int components, + uint8_t * dst, + uint8_t * __restrict__ src, + int x0, + uint8_t * coverage, + int count, + CtxCompositingMode compositing_mode, + CtxFragment fragment, + CtxBlend blend) +{ + __ctx_u8_porter_duff (rasterizer, components, dst, src, x0, coverage, count, compositing_mode, fragment, blend); +} + +#define _ctx_u8_porter_duffs(comp_format, components, source, fragment, blend) \ + switch (rasterizer->state->gstate.compositing_mode) \ + { \ + case CTX_COMPOSITE_SOURCE_ATOP: \ + __ctx_u8_porter_duff (rasterizer, components, dst, src, x0, coverage, count, \ + CTX_COMPOSITE_SOURCE_ATOP, fragment, blend);\ + break;\ + case CTX_COMPOSITE_DESTINATION_ATOP:\ + __ctx_u8_porter_duff (rasterizer, components, dst, src, x0, coverage, count,\ + CTX_COMPOSITE_DESTINATION_ATOP, fragment, blend);\ + break;\ + case CTX_COMPOSITE_DESTINATION_IN:\ + __ctx_u8_porter_duff (rasterizer, components, dst, src, x0, coverage, count,\ + CTX_COMPOSITE_DESTINATION_IN, fragment, blend);\ + break;\ + case CTX_COMPOSITE_DESTINATION:\ + __ctx_u8_porter_duff (rasterizer, components, dst, src, x0, coverage, count,\ + CTX_COMPOSITE_DESTINATION, fragment, blend);\ + break;\ + case CTX_COMPOSITE_SOURCE_OVER:\ + __ctx_u8_porter_duff (rasterizer, components, dst, src, x0, coverage, count,\ + CTX_COMPOSITE_SOURCE_OVER, fragment, blend);\ + break;\ + case CTX_COMPOSITE_DESTINATION_OVER:\ + __ctx_u8_porter_duff (rasterizer, components, dst, src, x0, coverage, count,\ + CTX_COMPOSITE_DESTINATION_OVER, fragment, blend);\ + break;\ + case CTX_COMPOSITE_XOR:\ + __ctx_u8_porter_duff (rasterizer, components, dst, src, x0, coverage, count,\ + CTX_COMPOSITE_XOR, fragment, blend);\ + break;\ + case CTX_COMPOSITE_DESTINATION_OUT:\ + __ctx_u8_porter_duff (rasterizer, components, dst, src, x0, coverage, count,\ + CTX_COMPOSITE_DESTINATION_OUT, fragment, blend);\ + break;\ + case CTX_COMPOSITE_SOURCE_OUT:\ + __ctx_u8_porter_duff (rasterizer, components, dst, src, x0, coverage, count,\ + CTX_COMPOSITE_SOURCE_OUT, fragment, blend);\ + break;\ + case CTX_COMPOSITE_SOURCE_IN:\ + __ctx_u8_porter_duff (rasterizer, components, dst, src, x0, coverage, count,\ + CTX_COMPOSITE_SOURCE_IN, fragment, blend);\ + break;\ + case CTX_COMPOSITE_COPY:\ + __ctx_u8_porter_duff (rasterizer, components, dst, src, x0, coverage, count,\ + CTX_COMPOSITE_COPY, fragment, blend);\ + break;\ + case CTX_COMPOSITE_CLEAR:\ + __ctx_u8_porter_duff (rasterizer, components, dst, src, x0, coverage, count,\ + CTX_COMPOSITE_CLEAR, fragment, blend);\ + break;\ + } + +/* generating one function per compositing_mode would be slightly more efficient, + * but on embedded targets leads to slightly more code bloat, + * here we trade off a slight amount of performance + */ +#define ctx_u8_porter_duff(comp_format, components, source, fragment, blend) \ +static void \ +ctx_##comp_format##_porter_duff_##source (CTX_COMPOSITE_ARGUMENTS) \ +{ \ + _ctx_u8_porter_duffs(comp_format, components, source, fragment, blend);\ +} + +ctx_u8_porter_duff(RGBA8, 4,generic, rasterizer->fragment, rasterizer->state->gstate.blend_mode) +//ctx_u8_porter_duff(comp_name, components,color_##blend_name, NULL, blend_mode) + + +#if CTX_INLINED_NORMAL_RGBA8 + +ctx_u8_porter_duff(RGBA8, 4,color, rasterizer->fragment, rasterizer->state->gstate.blend_mode) + +#if CTX_GRADIENTS +ctx_u8_porter_duff(RGBA8, 4,linear_gradient, ctx_fragment_linear_gradient_RGBA8, rasterizer->state->gstate.blend_mode) +ctx_u8_porter_duff(RGBA8, 4,radial_gradient, ctx_fragment_radial_gradient_RGBA8, rasterizer->state->gstate.blend_mode) +#endif +ctx_u8_porter_duff(RGBA8, 4,image, ctx_fragment_image_RGBA8, rasterizer->state->gstate.blend_mode) #endif -static void -ctx_composite_fill_rect_aligned (CtxRasterizer *rasterizer, - int x0, - int y0, - int x1, - int y1, - uint8_t cov) +static inline void +ctx_RGBA8_nop (CTX_COMPOSITE_ARGUMENTS) { - int blit_x = rasterizer->blit_x; - int blit_y = rasterizer->blit_y; - int blit_width = rasterizer->blit_width; - int blit_height = rasterizer->blit_height; - int blit_stride = rasterizer->blit_stride; +} - x0 = ctx_maxi (x0, blit_x); - x1 = ctx_mini (x1, blit_x + blit_width - 1); - 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; - // - if (CTX_UNLIKELY (width <=0 || height <= 0)) - return; +static inline void +ctx_setup_native_color (CtxRasterizer *rasterizer) +{ + if (rasterizer->state->gstate.source_fill.type == CTX_SOURCE_COLOR) + { + rasterizer->format->from_comp (rasterizer, 0, + &rasterizer->color[0], + &rasterizer->color_native, + 1); + } +} - CtxCovPath comp = rasterizer->comp; - uint8_t *dst; +static void +ctx_setup_apply_coverage (CtxRasterizer *rasterizer) +{ + rasterizer->apply_coverage = rasterizer->format->apply_coverage ? + rasterizer->format->apply_coverage : + rasterizer->comp_op; +} - // this could be done here, but is not used - // by a couple of the cases -#define INIT_ENV do {\ - rasterizer->scanline = y0 * CTX_FULL_AA; \ - dst = ( (uint8_t *) rasterizer->buf); \ - dst += (y0 - blit_y) * blit_stride; \ - dst += (x0 * rasterizer->format->bpp)/8;}while(0); +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; -//if (CTX_UNLIKELY(width <=0 || height <= 0)) -// return; - if (cov == 255) - { - switch (comp) - { - case CTX_COV_PATH_RGBA8_COPY: + int blend_mode = gstate->blend_mode; + int compositing_mode = gstate->compositing_mode; + + if (gstate->source_fill.type == CTX_SOURCE_COLOR) { - uint32_t color; - memcpy (&color, (uint32_t*)rasterizer->color, sizeof (color)); - INIT_ENV; - if (CTX_UNLIKELY(width == 1)) - { - for (unsigned int y = y0; y <= (unsigned)y1; y++) - { - uint32_t *dst_i = (uint32_t*)&dst[0]; - *dst_i = color; - dst += blit_stride; - } - } - else + ctx_fragment_color_RGBA8 (rasterizer, 0,0, 1,rasterizer->color, 1, 0,0,0); + if (gstate->global_alpha_u8 != 255) { - 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; - } + for (int c = 0; c < 4; c ++) + rasterizer->color[c] = (rasterizer->color[c] * gstate->global_alpha_u8 + 255)>>8; } - return; - } - case CTX_COV_PATH_RGBAF_COPY: - case CTX_COV_PATH_GRAY8_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_RGB8_COPY: - case CTX_COV_PATH_CMYK8_COPY: - case CTX_COV_PATH_CMYKA8_COPY: + uint32_t src_pix = ((uint32_t*)rasterizer->color)[0]; + uint32_t si_ga = (src_pix & 0xff00ff00) >> 8; + uint32_t si_rb = src_pix & 0x00ff00ff; + uint32_t si_ga_full = si_ga * 255; + uint32_t si_rb_full = si_rb * 255; +// uint32_t si_a = si_ga >> 16; + + ((uint32_t*)rasterizer->color)[1] = si_ga; + ((uint32_t*)rasterizer->color)[2] = si_rb; + ((uint32_t*)rasterizer->color)[3] = si_ga_full; + ((uint32_t*)rasterizer->color)[4] = si_rb_full; + } + +#if CTX_INLINED_NORMAL_RGBA8 + if (gstate->compositing_mode == CTX_COMPOSITE_CLEAR) + rasterizer->comp_op = ctx_RGBA8_clear_normal; + else + switch (gstate->blend_mode) { - uint8_t *color = (uint8_t*)&rasterizer->color_native; - unsigned int bytes = rasterizer->format->bpp/8; - INIT_ENV; + case CTX_BLEND_NORMAL: + if (gstate->compositing_mode == CTX_COMPOSITE_COPY) + { + rasterizer->comp_op = ctx_RGBA8_copy_normal; + if (gstate->source_fill.type == CTX_SOURCE_COLOR) + rasterizer->comp = CTX_COV_PATH_RGBA8_COPY; - switch (bytes) - { - case 1: - { - uint8_t col = *color; - if (width == 1) - for (unsigned int y = y0; y <= (unsigned)y1; y++) - { - *dst = col; - dst += blit_stride; - } - else - for (unsigned int y = y0; y <= (unsigned)y1; y++) - { -#if 0 - uint8_t *dst_i = (uint8_t*)&dst[0]; - for (int x = 0; x < width; x++) *dst_i++ = col; -#else - memset (dst, col, width); -#endif - dst += blit_stride; - } - } - break; - case 2: - { - uint16_t val = ((uint16_t*)color)[0]; - for (unsigned int y = y0; y <= (unsigned)y1; y++) - { - uint16_t *dst_i = (uint16_t*)&dst[0]; - for (int x = 0; x < width; x++) - *dst_i++ = val; - dst += blit_stride; - } - } - break; - case 3: - for (unsigned int y = y0; y <= (unsigned)y1; y++) - { - uint8_t *dst_i = (uint8_t*)&dst[0]; - for (int x = 0; x < width; x++) - for (unsigned int b = 0; b < 3; b++) *dst_i++ = color[b]; - dst += blit_stride; - } - break; - case 4: - { - uint32_t val = ((uint32_t*)color)[0]; - if (width == 1) - for (unsigned int y = y0; y <= (unsigned)y1; y++) + } + else if (gstate->global_alpha_u8 == 0) + { + rasterizer->comp_op = ctx_RGBA8_nop; + } + else + switch (gstate->source_fill.type) + { + case CTX_SOURCE_COLOR: + if (gstate->compositing_mode == CTX_COMPOSITE_SOURCE_OVER) { - *((uint32_t*)&dst[0]) = val; - dst += blit_stride; + rasterizer->comp_op = ctx_RGBA8_source_over_normal_color; + if ( ((float*)rasterizer->color)[3] >= 0.999f) + rasterizer->comp = CTX_COV_PATH_RGBA8_COPY; } else - for (unsigned int y = y0; y <= (unsigned)y1; y++) { - //uint32_t *dst_i = (uint32_t*)&dst[0]; - ctx_span_set_colorbu ((uint32_t*)&dst[0], val, width); - dst += blit_stride; + rasterizer->comp_op = ctx_RGBAF_porter_duff_color_normal; } - } - break; - case 5: - for (unsigned int y = y0; y <= (unsigned)y1; y++) - { - uint8_t *dst_i = (uint8_t*)&dst[0]; - for (int x = 0; x < width; x++) - for (unsigned int b = 0; b < 5; b++) *dst_i++ = color[b]; - dst += blit_stride; - } - break; - case 16: - for (unsigned int y = y0; y <= (unsigned)y1; y++) - { - uint8_t *dst_i = (uint8_t*)&dst[0]; - for (int x = 0; x < width; x++)for (unsigned int b = 0; b < 16; b++) *dst_i++ = color[b]; - dst += blit_stride; - } - break; - default: - for (unsigned int y = y0; y <= (unsigned)y1; y++) - { - uint8_t *dst_i = (uint8_t*)&dst[0]; - for (int x = 0; x < width; x++) - for (unsigned int b = 0; b < bytes; b++) - *dst_i++ = color[b]; - dst += blit_stride; - } - } - return; - } - case CTX_COV_PATH_RGBA8_OVER: - { - uint32_t si_ga_full = ((uint32_t*)rasterizer->color)[3]; - uint32_t si_rb_full = ((uint32_t*)rasterizer->color)[4]; - uint32_t si_a = rasterizer->color[3]; - INIT_ENV; - - if (CTX_UNLIKELY(width == 1)) - { - for (unsigned int y = y0; y <= (unsigned)y1; y++) - { - ((uint32_t*)(dst))[0] = ctx_over_RGBA8_full_2 ( - ((uint32_t*)(dst))[0], si_ga_full, si_rb_full, si_a); - dst += blit_stride; + break; +#if CTX_GRADIENTS + case CTX_SOURCE_LINEAR_GRADIENT: + rasterizer->comp_op = ctx_RGBA8_porter_duff_linear_gradient_normal; + break; + case CTX_SOURCE_RADIAL_GRADIENT: + rasterizer->comp_op = ctx_RGBA8_porter_duff_radial_gradient_normal; + break; +#endif + case CTX_SOURCE_TEXTURE: + rasterizer->comp_op = ctx_RGBA8_porter_duff_image_normal; + break; + default: + rasterizer->comp_op = ctx_RGBA8_porter_duff_generic_normal; + break; } - } - else - { - for (unsigned int y = y0; y <= (unsigned)y1; y++) + break; + default: + switch (gstate->source_fill.type) { - uint32_t *dst_i = (uint32_t*)&dst[0]; - for (int i = 0; i < width; i++) - { - dst_i[i] = ctx_over_RGBA8_full_2 (dst_i[i], si_ga_full, si_rb_full, si_a); - } - dst += blit_stride; + case CTX_SOURCE_COLOR: + rasterizer->comp_op = ctx_RGBA8_porter_duff_color; + break; +#if CTX_GRADIENTS + case CTX_SOURCE_LINEAR_GRADIENT: + rasterizer->comp_op = ctx_RGBA8_porter_duff_linear_gradient; + break; + case CTX_SOURCE_RADIAL_GRADIENT: + rasterizer->comp_op = ctx_RGBA8_porter_duff_radial_gradient; + break; +#endif + case CTX_SOURCE_TEXTURE: + rasterizer->comp_op = ctx_RGBA8_porter_duff_image; + break; + default: + rasterizer->comp_op = ctx_RGBA8_porter_duff_generic; + break; } - } - return; + break; } - case CTX_COV_PATH_RGBA8_COPY_FRAGMENT: - { - CtxFragment fragment = rasterizer->fragment; - CtxMatrix *transform = &rasterizer->state->gstate.source_fill.transform; - CtxExtend extend = rasterizer->state->gstate.extend; - INIT_ENV; - if (fragment == ctx_fragment_image_rgba8_RGBA8_bi_scale) - { - ctx_RGBA8_image_rgba8_RGBA8_bi_scaled_fill_rect (rasterizer, x0, y0, x1, -y1, 1); - 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, 1); - return; - } +#else - if (CTX_LIKELY(ctx_matrix_no_perspective (transform))) - { - int scan = rasterizer->scanline/CTX_FULL_AA; - float u0, v0, ud, vd, w0, wd; - ctx_init_uv (rasterizer, x0, scan, &u0, &v0, &w0, &ud, &vd, &wd); - for (unsigned int y = y0; y <= (unsigned)y1; y++) - { - fragment (rasterizer, u0, v0, w0, &dst[0], width, ud, vd, wd); - u0 -= vd; - v0 += ud; - dst += blit_stride; - } - } - else - { - int scan = rasterizer->scanline/CTX_FULL_AA; - for (unsigned int y = y0; y <= (unsigned)y1; y++) - { - float u0, v0, ud, vd, w0, wd; - ctx_init_uv (rasterizer, x0, scan + y-y0, &u0, &v0, &w0, &ud, &vd, &wd); - fragment (rasterizer, u0, v0, w0, &dst[0], width, ud, vd, wd); - dst += blit_stride; - } - } - return; - } - case CTX_COV_PATH_RGBA8_OVER_FRAGMENT: + if (gstate->source_fill.type == CTX_SOURCE_COLOR) { - CtxFragment fragment = rasterizer->fragment; - CtxExtend extend = rasterizer->state->gstate.extend; -#if 0 - if (fragment == ctx_fragment_image_rgba8_RGBA8_nearest_copy) - { - ctx_RGBA8_image_rgba8_RGBA8_nearest_fill_rect_copy (rasterizer, x0, y0, x1, y1, 0); - return; - } - else -#endif - 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; - } - INIT_ENV; - ctx_RGBA8_source_over_normal_full_cov_fragment (rasterizer, - &dst[0], NULL, x0, NULL, width, y1-y0+1); - return; - } - break; - default: - break; - } - } - else - { - switch (comp) - { - case CTX_COV_PATH_RGBA8_COPY: - { - uint32_t color; - memcpy (&color, (uint32_t*)rasterizer->color, sizeof (color)); - INIT_ENV; + if (blend_mode == CTX_BLEND_NORMAL) { - for (unsigned int y = y0; y <= (unsigned)y1; y++) + if(compositing_mode == CTX_COMPOSITE_COPY) { - 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; + rasterizer->comp_op = ctx_RGBA8_source_copy_normal_color; + rasterizer->comp = CTX_COV_PATH_RGBA8_COPY; } - return; - } - } - case CTX_COV_PATH_RGBAF_COPY: - { - float *color = ((float*)rasterizer->color); - float covf = cov / 255.0f; - INIT_ENV; - { - for (unsigned int y = y0; y <= (unsigned)y1; y++) + else if (compositing_mode == CTX_COMPOSITE_SOURCE_OVER) { - float *dst_f = (float*)&dst[0]; - for (unsigned int i = 0; i < (unsigned)width; i++) + if (rasterizer->color[components-1] == 255) { - for (unsigned int c = 0; c < 4; c++) - dst_f[i*4+c] = ctx_lerpf (dst_f[i*4+c], color[c], covf); + rasterizer->comp_op = ctx_RGBA8_source_copy_normal_color; + rasterizer->comp = CTX_COV_PATH_RGBA8_COPY; } - dst += blit_stride; - } - return; - } - } - case CTX_COV_PATH_RGBA8_OVER: - { - uint32_t color; - memcpy (&color, (uint32_t*)rasterizer->color, sizeof (color)); - INIT_ENV; - if (width == 1) - { - for (unsigned int y = y0; y <= (unsigned)y1; y++) - { - uint32_t *dst_i = (uint32_t*)&dst[0]; - *dst_i = ctx_over_RGBA8 (*dst_i, color, cov); - dst += blit_stride; - } - } - else - { - 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++) + else { - dst_i[i] = ctx_over_RGBA8 (dst_i[i], color, cov); + rasterizer->comp_op = ctx_RGBA8_source_over_normal_color; + rasterizer->comp = CTX_COV_PATH_RGBA8_OVER; } - dst += blit_stride; } } - return; - } - break; - default: - break; - } + else if (compositing_mode == CTX_COMPOSITE_CLEAR) + { + rasterizer->comp_op = ctx_RGBA8_clear_normal; + } } - - INIT_ENV; -#undef INIT_ENV - - - /* fallback */ + else if (blend_mode == CTX_BLEND_NORMAL) { - uint8_t coverage[width]; - memset (coverage, cov, sizeof (coverage) ); - for (unsigned int y = y0; y <= (unsigned)y1; y++) + if(compositing_mode == CTX_COMPOSITE_SOURCE_OVER) { - rasterizer->apply_coverage (rasterizer, &dst[0], rasterizer->color, x0, coverage, width); - rasterizer->scanline += CTX_FULL_AA; - dst += blit_stride; + rasterizer->comp_op = ctx_RGBA8_source_over_normal_fragment; + rasterizer->comp = CTX_COV_PATH_RGBA8_OVER_FRAGMENT; + } + else if (compositing_mode == CTX_COMPOSITE_COPY) + { + rasterizer->comp_op = ctx_RGBA8_source_copy_normal_fragment; + rasterizer->comp = CTX_COV_PATH_RGBA8_COPY_FRAGMENT; } } +#endif + ctx_setup_apply_coverage (rasterizer); } -void -CTX_SIMD_SUFFIX (ctx_composite_fill_rect) (CtxRasterizer *rasterizer, - float x0, - float y0, - float x1, - float y1, - uint8_t cov); -void -CTX_SIMD_SUFFIX (ctx_composite_fill_rect) (CtxRasterizer *rasterizer, - float x0, - float y0, - float x1, - float y1, - uint8_t cov) +static inline void +ctx_setup_RGB (CtxRasterizer *rasterizer) { - 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)) - { - /* best-case scenario axis aligned rectangle */ - ctx_composite_fill_rect_aligned (rasterizer, (int)x0, (int)y0, (int)(x1-1), (int)(y1-1), 255); - return; - } - - int blit_x = rasterizer->blit_x; - int blit_y = rasterizer->blit_y; - int blit_stride = rasterizer->blit_stride; - int blit_width = rasterizer->blit_width; - int blit_height = rasterizer->blit_height; - - x0 = ctx_maxf (x0, blit_x); - y0 = ctx_maxf (y0, blit_y); - x1 = ctx_minf (x1, blit_x + blit_width); - y1 = ctx_minf (y1, blit_y + blit_height); + ctx_setup_RGBA8 (rasterizer); + ctx_setup_native_color (rasterizer); - 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); + rasterizer->comp = CTX_COV_PATH_FALLBACK; +} - x0 = ctx_floorf (x0); - y0 = ctx_floorf (y0); - x1 = ctx_floorf (x1+7/8.0f); - y1 = ctx_floorf (y1+15/15.0f); - int has_top = (top < 255); - int has_bottom = (bottom <255); - 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; +#if CTX_ENABLE_RGB332 +static void +ctx_setup_RGB332 (CtxRasterizer *rasterizer) +{ + ctx_setup_RGBA8 (rasterizer); + ctx_setup_native_color (rasterizer); - int width = (int)(x1 - x0); + if (rasterizer->comp == CTX_COV_PATH_RGBA8_COPY) + rasterizer->comp = CTX_COV_PATH_RGB332_COPY; + else + rasterizer->comp = CTX_COV_PATH_FALLBACK; +} +#endif - if ((width >0)) - { - uint8_t *dst = ( (uint8_t *) rasterizer->buf); - uint8_t coverage[width+2]; - uint32_t x0i = (int)x0+has_left; - uint32_t x1i = (int)x1-has_right; - uint32_t y0i = (int)y0+has_top; - uint32_t y1i = (int)y1-has_bottom; - dst += (((int)y0) - blit_y) * blit_stride; - dst += ((int)x0) * rasterizer->format->bpp/8; +#if CTX_ENABLE_RGB565 +static void +ctx_setup_RGB565 (CtxRasterizer *rasterizer) +{ + ctx_setup_RGBA8 (rasterizer); + ctx_setup_native_color (rasterizer); - if (has_top) - { - int i = 0; - if (has_left) - { - coverage[i++] = (top * left + 255) >> 8; - } - for (unsigned int x = x0i; x < x1i; x++) - coverage[i++] = top; - if (has_right) - coverage[i++]= (top * right + 255) >> 8; + if (rasterizer->comp == CTX_COV_PATH_RGBA8_COPY) + rasterizer->comp = CTX_COV_PATH_RGB565_COPY; + else + rasterizer->comp = CTX_COV_PATH_FALLBACK; +} +#endif - rasterizer->apply_coverage (rasterizer, dst, rasterizer->color, (int)x0, coverage, width); - dst += blit_stride; - } +#if CTX_ENABLE_RGB8 +static void +ctx_setup_RGB8 (CtxRasterizer *rasterizer) +{ + ctx_setup_RGBA8 (rasterizer); + ctx_setup_native_color (rasterizer); - if (y1-y0-has_top-has_bottom > 0) - { - if (has_left) - ctx_composite_fill_rect_aligned (rasterizer, (int)x0, y0i, - (int)x0, y1i-1, left); - if (has_right) - ctx_composite_fill_rect_aligned (rasterizer, (int)x1-1, y0i, - (int)x1-1, y1i-1, right); + if (rasterizer->comp == CTX_COV_PATH_RGBA8_COPY) + rasterizer->comp = CTX_COV_PATH_RGB8_COPY; + else + rasterizer->comp = CTX_COV_PATH_FALLBACK; +} +#endif - if (width - has_left - has_right > 0) - ctx_composite_fill_rect_aligned (rasterizer, x0i,y0i, - x1i-1,y1i-1,255); +static void +ctx_composite_convert (CTX_COMPOSITE_ARGUMENTS) +{ + uint8_t pixels[count * rasterizer->format->ebpp]; + rasterizer->format->to_comp (rasterizer, x0, dst, &pixels[0], count); + rasterizer->comp_op (rasterizer, &pixels[0], rasterizer->color, x0, coverage, count); + rasterizer->format->from_comp (rasterizer, x0, &pixels[0], dst, count); +} - dst += blit_stride * (y1i-y0i); +#if CTX_ENABLE_FLOAT +static inline void +ctx_float_copy_normal (int components, CTX_COMPOSITE_ARGUMENTS) +{ + float *dstf = (float*)dst; + float *srcf = (float*)src; + float u0 = 0; float v0 = 0; + float ud = 0; float vd = 0; + float w0 = 1; float wd = 0; + + ctx_init_uv (rasterizer, x0, rasterizer->scanline/CTX_FULL_AA, &u0, &v0, &w0, &ud, &vd, &wd); + + while (count--) + { + uint8_t cov = *coverage; + float covf = ctx_u8_to_float (cov); + for (int c = 0; c < components; c++) + dstf[c] = dstf[c]*(1.0f-covf) + srcf[c]*covf; + dstf += components; + coverage ++; } - if (has_bottom) - { - int i = 0; - if (has_left) - coverage[i++] = (bottom * left + 255) >> 8; - for (unsigned int x = x0i; x < x1i; x++) - coverage[i++] = bottom; - coverage[i++]= (bottom * right + 255) >> 8; +} - rasterizer->apply_coverage (rasterizer,dst, rasterizer->color, (int)x0, coverage, width); +static inline void +ctx_float_clear_normal (int components, CTX_COMPOSITE_ARGUMENTS) +{ + float *dstf = (float*)dst; + while (count--) + { +#if 0 + uint8_t cov = *coverage; + if (cov == 0) + { + } + else if (cov == 255) + { +#endif + switch (components) + { + case 2: + ((uint64_t*)(dst))[0] = 0; + break; + case 4: + ((uint64_t*)(dst))[0] = 0; + ((uint64_t*)(dst))[1] = 0; + break; + default: + for (int c = 0; c < components; c++) + dstf[c] = 0.0f; + } +#if 0 + } + else + { + float ralpha = 1.0f - ctx_u8_to_float (cov); + for (int c = 0; c < components; c++) + { dstf[c] = (dstf[c] * ralpha); } } + coverage ++; +#endif + dstf += components; } } -void -CTX_SIMD_SUFFIX(ctx_composite_stroke_rect) (CtxRasterizer *rasterizer, - float x0, - float y0, - float x1, - float y1, - float line_width); -void -CTX_SIMD_SUFFIX(ctx_composite_stroke_rect) (CtxRasterizer *rasterizer, - float x0, - float y0, - float x1, - float y1, - float line_width) +static inline void +ctx_float_source_over_normal_color (int components, CTX_COMPOSITE_ARGUMENTS) { - float lwmod = ctx_fmod1f (line_width); - int lw = (int)ctx_floorf (line_width + 0.5f); - int is_compat_even = (lw % 2 == 0) && (lwmod < 0.1f); // only even linewidths implemented properly - int is_compat_odd = (lw % 2 == 1) && (lwmod < 0.1f); // only even linewidths implemented properly + float *dstf = (float*)dst; + float *srcf = (float*)src; + while (count--) + { + uint8_t cov = *coverage; + float fcov = ctx_u8_to_float (cov); + float ralpha = 1.0f - fcov * srcf[components-1]; + for (int c = 0; c < components; c++) + dstf[c] = srcf[c]*fcov + dstf[c] * ralpha; + coverage ++; + dstf+= components; + } +} - float off_x = 0; - float off_y = 0; +static inline void +ctx_float_source_copy_normal_color (int components, CTX_COMPOSITE_ARGUMENTS) +{ + float *dstf = (float*)dst; + float *srcf = (float*)src; - if (is_compat_odd) + while (count--) + { + uint8_t cov = *coverage; + float fcov = ctx_u8_to_float (cov); + float ralpha = 1.0f - fcov; + for (int c = 0; c < components; c++) + dstf[c] = (srcf[c]*fcov + dstf[c] * ralpha); + coverage ++; + dstf+= components; + } +} + +inline static void +ctx_float_blend_normal (int components, float *dst, float *src, float *blended) +{ + float a = src[components-1]; + for (int c = 0; c < components - 1; c++) + blended[c] = src[c] * a; + blended[components-1]=a; +} + +static float ctx_float_get_max (int components, float *c) +{ + float max = -1000.0f; + for (int i = 0; i < components - 1; i ++) + { + if (c[i] > max) max = c[i]; + } + return max; +} + +static float ctx_float_get_min (int components, float *c) +{ + float min = 400.0; + for (int i = 0; i < components - 1; i ++) + { + if (c[i] < min) min = c[i]; + } + return min; +} + +static float ctx_float_get_lum (int components, float *c) +{ + switch (components) + { + case 3: + case 4: + return CTX_CSS_RGB_TO_LUMINANCE(c); + case 1: + case 2: + return c[0]; + break; + default: + { + float sum = 0; + for (int i = 0; i < components - 1; i ++) + { + sum += c[i]; + } + return sum / (components - 1); + } + } +} + +static float ctx_float_get_sat (int components, float *c) +{ + switch (components) + { + case 3: + case 4: + { float r = c[0]; + float g = c[1]; + float b = c[2]; + return ctx_maxf(r, ctx_maxf(g,b)) - ctx_minf(r,ctx_minf(g,b)); + } + break; + case 1: + case 2: return 0.0; + break; + default: + { + float min = 1000; + float max = -1000; + for (int i = 0; i < components - 1; i ++) + { + if (c[i] < min) min = c[i]; + if (c[i] > max) max = c[i]; + } + return max-min; + } + } +} + +static void ctx_float_set_lum (int components, float *c, float lum) +{ + float d = lum - ctx_float_get_lum (components, c); + float tc[components]; + for (int i = 0; i < components - 1; i++) + { + tc[i] = c[i] + d; + } + + float l = ctx_float_get_lum (components, tc); + float n = ctx_float_get_min (components, tc); + float x = ctx_float_get_max (components, tc); + + 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) + { + for (int i = 0; i < components - 1; i++) + tc[i] = l + (((tc[i] - l) * (1.0f - l)) / (x-l)); + } + for (int i = 0; i < components - 1; i++) + c[i] = tc[i]; +} + +static void ctx_float_set_sat (int components, float *c, float sat) +{ + int max = 0, mid = 1, min = 2; + + if (c[min] > c[mid]){int t = min; min = mid; mid = t;} + if (c[mid] > c[max]){int t = mid; mid = max; max = t;} + if (c[min] > c[mid]){int t = min; min = mid; mid = t;} + + if (c[max] > c[min]) + { + c[mid] = ((c[mid]-c[min]) * sat) / (c[max] - c[min]); + c[max] = sat; + } + else + { + c[mid] = c[max] = 0.0f; + } + c[min] = 0.0f; + +} + +#define ctx_float_blend_define(name, CODE) \ +static inline void \ +ctx_float_blend_##name (int components, float * __restrict__ dst, float *src, float *blended)\ +{\ + float *s = src; float b[components];\ + ctx_float_deassociate_alpha (components, dst, b);\ + CODE;\ + blended[components-1] = s[components-1];\ + ctx_float_associate_alpha (components, blended);\ +} + +#define ctx_float_blend_define_seperable(name, CODE) \ + ctx_float_blend_define(name, for (int c = 0; c < components-1; c++) { CODE ;}) \ + +ctx_float_blend_define_seperable(multiply, blended[c] = (b[c] * s[c]);) +ctx_float_blend_define_seperable(screen, blended[c] = b[c] + s[c] - (b[c] * s[c]);) +ctx_float_blend_define_seperable(overlay, blended[c] = b[c] < 0.5f ? (s[c] * b[c]) : + s[c] + b[c] - (s[c] * b[c]);) +ctx_float_blend_define_seperable(darken, blended[c] = ctx_minf (b[c], s[c])) +ctx_float_blend_define_seperable(lighten, blended[c] = ctx_maxf (b[c], s[c])) +ctx_float_blend_define_seperable(color_dodge, blended[c] = (b[c] == 0.0f) ? 0.0f : + s[c] == 1.0f ? 1.0f : ctx_minf(1.0f, (b[c]) / (1.0f-s[c]))) +ctx_float_blend_define_seperable(color_burn, blended[c] = (b[c] == 1.0f) ? 1.0f : + s[c] == 0.0f ? 0.0f : 1.0f - ctx_minf(1.0f, ((1.0f - b[c])) / s[c])) +ctx_float_blend_define_seperable(hard_light, blended[c] = s[c] < 0.f ? (b[c] * s[c]) : + b[c] + s[c] - (b[c] * s[c]);) +ctx_float_blend_define_seperable(difference, blended[c] = (b[c] - s[c])) + +ctx_float_blend_define_seperable(divide, blended[c] = s[c]?(b[c]) / s[c]:0.0f) +ctx_float_blend_define_seperable(addition, blended[c] = s[c]+b[c]) +ctx_float_blend_define_seperable(subtract, blended[c] = s[c]-b[c]) + +ctx_float_blend_define_seperable(exclusion, blended[c] = b[c] + s[c] - 2.0f * b[c] * s[c]) +ctx_float_blend_define_seperable(soft_light, + if (s[c] <= 0.5f) + { + blended[c] = b[c] - (1.0f - 2.0f * s[c]) * b[c] * (1.0f - b[c]); + } + else + { + int d; + if (b[c] <= 255/4) + d = (((16 * b[c] - 12.0f) * b[c] + 4.0f) * b[c]); + else + d = ctx_sqrtf(b[c]); + blended[c] = (b[c] + (2.0f * s[c] - 1.0f) * (d - b[c])); + } +) + + +ctx_float_blend_define(color, + for (int i = 0; i < components; i++) + blended[i] = s[i]; + ctx_float_set_lum(components, blended, ctx_float_get_lum (components, s)); +) + +ctx_float_blend_define(hue, + float in_sat = ctx_float_get_sat(components, b); + float in_lum = ctx_float_get_lum(components, b); + for (int i = 0; i < components; i++) + blended[i] = s[i]; + ctx_float_set_sat(components, blended, in_sat); + ctx_float_set_lum(components, blended, in_lum); +) + +ctx_float_blend_define(saturation, + float in_sat = ctx_float_get_sat(components, s); + float in_lum = ctx_float_get_lum(components, b); + for (int i = 0; i < components; i++) + blended[i] = b[i]; + ctx_float_set_sat(components, blended, in_sat); + ctx_float_set_lum(components, blended, in_lum); +) + +ctx_float_blend_define(luminosity, + float in_lum = ctx_float_get_lum(components, s); + for (int i = 0; i < components; i++) + blended[i] = b[i]; + ctx_float_set_lum(components, blended, in_lum); +) + +inline static void +ctx_float_blend (int components, CtxBlend blend, float * __restrict__ dst, float *src, float *blended) +{ + switch (blend) + { + case CTX_BLEND_NORMAL: ctx_float_blend_normal (components, dst, src, blended); break; + case CTX_BLEND_MULTIPLY: ctx_float_blend_multiply (components, dst, src, blended); break; + case CTX_BLEND_SCREEN: ctx_float_blend_screen (components, dst, src, blended); break; + case CTX_BLEND_OVERLAY: ctx_float_blend_overlay (components, dst, src, blended); break; + case CTX_BLEND_DARKEN: ctx_float_blend_darken (components, dst, src, blended); break; + case CTX_BLEND_LIGHTEN: ctx_float_blend_lighten (components, dst, src, blended); break; + case CTX_BLEND_COLOR_DODGE: ctx_float_blend_color_dodge (components, dst, src, blended); break; + case CTX_BLEND_COLOR_BURN: ctx_float_blend_color_burn (components, dst, src, blended); break; + case CTX_BLEND_HARD_LIGHT: ctx_float_blend_hard_light (components, dst, src, blended); break; + case CTX_BLEND_SOFT_LIGHT: ctx_float_blend_soft_light (components, dst, src, blended); break; + case CTX_BLEND_DIFFERENCE: ctx_float_blend_difference (components, dst, src, blended); break; + case CTX_BLEND_EXCLUSION: ctx_float_blend_exclusion (components, dst, src, blended); break; + case CTX_BLEND_COLOR: ctx_float_blend_color (components, dst, src, blended); break; + case CTX_BLEND_HUE: ctx_float_blend_hue (components, dst, src, blended); break; + case CTX_BLEND_SATURATION: ctx_float_blend_saturation (components, dst, src, blended); break; + case CTX_BLEND_LUMINOSITY: ctx_float_blend_luminosity (components, dst, src, blended); break; + case CTX_BLEND_ADDITION: ctx_float_blend_addition (components, dst, src, blended); break; + case CTX_BLEND_SUBTRACT: ctx_float_blend_subtract (components, dst, src, blended); break; + case CTX_BLEND_DIVIDE: ctx_float_blend_divide (components, dst, src, blended); break; + } +} + +/* this is the grunt working function, when inlined code-path elimination makes + * it produce efficient code. + */ +CTX_INLINE static void +ctx_float_porter_duff (CtxRasterizer *rasterizer, + int components, + uint8_t * __restrict__ dst, + uint8_t * __restrict__ src, + int x0, + uint8_t * __restrict__ coverage, + int count, + CtxCompositingMode compositing_mode, + CtxFragment fragment, + CtxBlend blend) +{ + float *dstf = (float*)dst; + + CtxPorterDuffFactor f_s, f_d; + ctx_porter_duff_factors (compositing_mode, &f_s, &f_d); + uint8_t global_alpha_u8 = rasterizer->state->gstate.global_alpha_u8; + float global_alpha_f = rasterizer->state->gstate.global_alpha_f; + + if (rasterizer->state->gstate.source_fill.type == CTX_SOURCE_COLOR) + { + float tsrc[components]; + + while (count--) + { + uint8_t cov = *coverage; +#if 1 + if ( + CTX_UNLIKELY((compositing_mode == CTX_COMPOSITE_DESTINATION_OVER && dst[components-1] == 1.0f)|| + (cov == 0 && (compositing_mode == CTX_COMPOSITE_SOURCE_OVER || + compositing_mode == CTX_COMPOSITE_XOR || + compositing_mode == CTX_COMPOSITE_DESTINATION_OUT || + compositing_mode == CTX_COMPOSITE_SOURCE_ATOP + )))) { - off_x = 0.5f; - off_y = (CTX_FULL_AA/2)*1.0f / (CTX_FULL_AA); + coverage ++; + dstf+=components; + continue; } +#endif + memcpy (tsrc, rasterizer->color, sizeof(tsrc)); - if((is_compat_odd || is_compat_even) && + if (blend != CTX_BLEND_NORMAL) + ctx_float_blend (components, blend, dstf, tsrc, tsrc); + float covf = ctx_u8_to_float (cov); - ((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))) + if (global_alpha_u8 != 255) + covf = covf * global_alpha_f; + if (covf != 1.0f) + { + for (int c = 0; c < components; c++) + tsrc[c] *= covf; + } + for (int c = 0; c < components; c++) { - int bw = lw/2+1; - int bwb = lw/2; + float res; + /* these switches and this whole function is written to be + * inlined when compiled when the enum values passed in are + * constants. + */ + switch (f_s) + { + case CTX_PORTER_DUFF_0: res = 0.0f; break; + case CTX_PORTER_DUFF_1: res = (tsrc[c]); break; + case CTX_PORTER_DUFF_ALPHA: res = (tsrc[c] * dstf[components-1]); break; + case CTX_PORTER_DUFF_1_MINUS_ALPHA: res = (tsrc[c] * (1.0f-dstf[components-1])); break; + } + switch (f_d) + { + case CTX_PORTER_DUFF_0: dstf[c] = res; break; + case CTX_PORTER_DUFF_1: dstf[c] = res + (dstf[c]); break; + case CTX_PORTER_DUFF_ALPHA: dstf[c] = res + (dstf[c] * tsrc[components-1]); break; + case CTX_PORTER_DUFF_1_MINUS_ALPHA: dstf[c] = res + (dstf[c] * (1.0f-tsrc[components-1])); break; + } + } + coverage ++; + dstf +=components; + } + } + else + { + float tsrc[components]; + float u0 = 0; float v0 = 0; + float ud = 0; float vd = 0; + float w0 = 1; float wd = 0; + for (int c = 0; c < components; c++) tsrc[c] = 0.0f; + ctx_init_uv (rasterizer, x0, rasterizer->scanline/CTX_FULL_AA, &u0, &v0, &w0, &ud, &vd, &wd); - if (is_compat_even) + while (count--) + { + uint8_t cov = *coverage; +#if 1 + if ( + CTX_UNLIKELY((compositing_mode == CTX_COMPOSITE_DESTINATION_OVER && dst[components-1] == 1.0f)|| + (cov == 0 && (compositing_mode == CTX_COMPOSITE_SOURCE_OVER || + compositing_mode == CTX_COMPOSITE_XOR || + compositing_mode == CTX_COMPOSITE_DESTINATION_OUT || + compositing_mode == CTX_COMPOSITE_SOURCE_ATOP + )))) + { + u0 += ud; + v0 += vd; + coverage ++; + dstf+=components; + continue; + } +#endif + + fragment (rasterizer, u0, v0, w0, tsrc, 1, ud, vd, wd); + if (blend != CTX_BLEND_NORMAL) + ctx_float_blend (components, blend, dstf, tsrc, tsrc); + u0 += ud; + v0 += vd; + float covf = ctx_u8_to_float (cov); + + if (global_alpha_u8 != 255) + covf = covf * global_alpha_f; + + if (covf != 1.0f) + { + for (int c = 0; c < components; c++) + tsrc[c] *= covf; + } + + for (int c = 0; c < components; c++) + { + float res; + /* these switches and this whole function is written to be + * inlined when compiled when the enum values passed in are + * constants. + */ + switch (f_s) { - bw = lw/2; + case CTX_PORTER_DUFF_0: res = 0.0f; break; + case CTX_PORTER_DUFF_1: res = (tsrc[c]); break; + case CTX_PORTER_DUFF_ALPHA: res = (tsrc[c] * dstf[components-1]); break; + case CTX_PORTER_DUFF_1_MINUS_ALPHA: res = (tsrc[c] * (1.0f-dstf[components-1])); break; } - /* top */ - ctx_composite_fill_rect_aligned (rasterizer, - (int)x0-bwb, (int)y0-bwb, - (int)x1+bw-1, (int)y0+bw-1, 255); - /* bottom */ - ctx_composite_fill_rect_aligned (rasterizer, - (int)x0-bwb, (int)y1-bwb, - (int)x1-bwb-1, (int)y1+bw-1, 255); + switch (f_d) + { + case CTX_PORTER_DUFF_0: dstf[c] = res; break; + case CTX_PORTER_DUFF_1: dstf[c] = res + (dstf[c]); break; + case CTX_PORTER_DUFF_ALPHA: dstf[c] = res + (dstf[c] * tsrc[components-1]); break; + case CTX_PORTER_DUFF_1_MINUS_ALPHA: dstf[c] = res + (dstf[c] * (1.0f-tsrc[components-1])); break; + } + } + coverage ++; + dstf +=components; + } + } +} + +/* generating one function per compositing_mode would be slightly more efficient, + * but on embedded targets leads to slightly more code bloat, + * here we trade off a slight amount of performance + */ +#define ctx_float_porter_duff(compformat, components, source, fragment, blend) \ +static void \ +ctx_##compformat##_porter_duff_##source (CTX_COMPOSITE_ARGUMENTS) \ +{ \ + switch (rasterizer->state->gstate.compositing_mode) \ + { \ + case CTX_COMPOSITE_SOURCE_ATOP: \ + ctx_float_porter_duff (rasterizer, components, dst, src, x0, coverage, count, \ + CTX_COMPOSITE_SOURCE_ATOP, fragment, blend);\ + break;\ + case CTX_COMPOSITE_DESTINATION_ATOP:\ + ctx_float_porter_duff (rasterizer, components, dst, src, x0, coverage, count,\ + CTX_COMPOSITE_DESTINATION_ATOP, fragment, blend);\ + break;\ + case CTX_COMPOSITE_DESTINATION_IN:\ + ctx_float_porter_duff (rasterizer, components, dst, src, x0, coverage, count,\ + CTX_COMPOSITE_DESTINATION_IN, fragment, blend);\ + break;\ + case CTX_COMPOSITE_DESTINATION:\ + ctx_float_porter_duff (rasterizer, components, dst, src, x0, coverage, count,\ + CTX_COMPOSITE_DESTINATION, fragment, blend);\ + break;\ + case CTX_COMPOSITE_SOURCE_OVER:\ + ctx_float_porter_duff (rasterizer, components, dst, src, x0, coverage, count,\ + CTX_COMPOSITE_SOURCE_OVER, fragment, blend);\ + break;\ + case CTX_COMPOSITE_DESTINATION_OVER:\ + ctx_float_porter_duff (rasterizer, components, dst, src, x0, coverage, count,\ + CTX_COMPOSITE_DESTINATION_OVER, fragment, blend);\ + break;\ + case CTX_COMPOSITE_XOR:\ + ctx_float_porter_duff (rasterizer, components, dst, src, x0, coverage, count,\ + CTX_COMPOSITE_XOR, fragment, blend);\ + break;\ + case CTX_COMPOSITE_DESTINATION_OUT:\ + ctx_float_porter_duff (rasterizer, components, dst, src, x0, coverage, count,\ + CTX_COMPOSITE_DESTINATION_OUT, fragment, blend);\ + break;\ + case CTX_COMPOSITE_SOURCE_OUT:\ + ctx_float_porter_duff (rasterizer, components, dst, src, x0, coverage, count,\ + CTX_COMPOSITE_SOURCE_OUT, fragment, blend);\ + break;\ + case CTX_COMPOSITE_SOURCE_IN:\ + ctx_float_porter_duff (rasterizer, components, dst, src, x0, coverage, count,\ + CTX_COMPOSITE_SOURCE_IN, fragment, blend);\ + break;\ + case CTX_COMPOSITE_COPY:\ + ctx_float_porter_duff (rasterizer, components, dst, src, x0, coverage, count,\ + CTX_COMPOSITE_COPY, fragment, blend);\ + break;\ + case CTX_COMPOSITE_CLEAR:\ + ctx_float_porter_duff (rasterizer, components, dst, src, x0, coverage, count,\ + CTX_COMPOSITE_CLEAR, fragment, blend);\ + break;\ + }\ +} +#endif + +#if CTX_ENABLE_RGBAF + +ctx_float_porter_duff(RGBAF, 4,color, rasterizer->fragment, rasterizer->state->gstate.blend_mode) +ctx_float_porter_duff(RGBAF, 4,generic, rasterizer->fragment, rasterizer->state->gstate.blend_mode) + +#if CTX_INLINED_NORMAL +#if CTX_GRADIENTS +ctx_float_porter_duff(RGBAF, 4,linear_gradient, ctx_fragment_linear_gradient_RGBAF, rasterizer->state->gstate.blend_mode) +ctx_float_porter_duff(RGBAF, 4,radial_gradient, ctx_fragment_radial_gradient_RGBAF, rasterizer->state->gstate.blend_mode) +#endif +ctx_float_porter_duff(RGBAF, 4,image, ctx_fragment_image_RGBAF, rasterizer->state->gstate.blend_mode) + + +#if CTX_GRADIENTS +#define ctx_float_porter_duff_blend(comp_name, components, blend_mode, blend_name)\ +ctx_float_porter_duff(comp_name, components,color_##blend_name, rasterizer->fragment, blend_mode)\ +ctx_float_porter_duff(comp_name, components,generic_##blend_name, rasterizer->fragment, blend_mode)\ +ctx_float_porter_duff(comp_name, components,linear_gradient_##blend_name, ctx_fragment_linear_gradient_RGBA8, blend_mode)\ +ctx_float_porter_duff(comp_name, components,radial_gradient_##blend_name, ctx_fragment_radial_gradient_RGBA8, blend_mode)\ +ctx_float_porter_duff(comp_name, components,image_##blend_name, ctx_fragment_image_RGBAF, blend_mode) +#else +#define ctx_float_porter_duff_blend(comp_name, components, blend_mode, blend_name)\ +ctx_float_porter_duff(comp_name, components,color_##blend_name, rasterizer->fragment, blend_mode)\ +ctx_float_porter_duff(comp_name, components,generic_##blend_name, rasterizer->fragment, blend_mode)\ +ctx_float_porter_duff(comp_name, components,image_##blend_name, ctx_fragment_image_RGBAF, blend_mode) +#endif + +ctx_float_porter_duff_blend(RGBAF, 4, CTX_BLEND_NORMAL, normal) + + +static void +ctx_RGBAF_copy_normal (CTX_COMPOSITE_ARGUMENTS) +{ + ctx_float_copy_normal (4, rasterizer, dst, src, x0, coverage, count); +} + +static void +ctx_RGBAF_clear_normal (CTX_COMPOSITE_ARGUMENTS) +{ + ctx_float_clear_normal (4, rasterizer, dst, src, x0, coverage, count); +} + +#if 1 +static void +ctx_RGBAF_source_over_normal_color (CTX_COMPOSITE_ARGUMENTS) +{ + ctx_float_source_over_normal_color (4, rasterizer, dst, rasterizer->color, x0, coverage, count); +} +#endif +#endif + +static void +ctx_setup_RGBAF (CtxRasterizer *rasterizer) +{ + CtxGState *gstate = &rasterizer->state->gstate; + int components = 4; + rasterizer->fragment = ctx_rasterizer_get_fragment_RGBAF (rasterizer); + rasterizer->comp = CTX_COV_PATH_FALLBACK; +#if 1 + if (gstate->source_fill.type == CTX_SOURCE_COLOR) + { + rasterizer->comp_op = ctx_RGBAF_porter_duff_color; + ctx_fragment_color_RGBAF (rasterizer, 0,0,1, rasterizer->color, 1, 0,0,0); + if (gstate->global_alpha_u8 != 255) + for (int c = 0; c < components; c ++) + ((float*)rasterizer->color)[c] *= gstate->global_alpha_f; + + if (rasterizer->format->from_comp) + rasterizer->format->from_comp (rasterizer, 0, + &rasterizer->color[0], + &rasterizer->color_native, + 1); + } + else +#endif + { + rasterizer->comp_op = ctx_RGBAF_porter_duff_generic; + } + +#if CTX_INLINED_NORMAL + if (gstate->compositing_mode == CTX_COMPOSITE_CLEAR) + rasterizer->comp_op = ctx_RGBAF_clear_normal; + else + switch (gstate->blend_mode) + { + case CTX_BLEND_NORMAL: + if (gstate->compositing_mode == CTX_COMPOSITE_COPY) + { + rasterizer->comp_op = ctx_RGBAF_copy_normal; + if (gstate->source_fill.type == CTX_SOURCE_COLOR) + rasterizer->comp = CTX_COV_PATH_RGBAF_COPY; + + } + else if (gstate->global_alpha_u8 == 0) + { + rasterizer->comp_op = ctx_RGBA8_nop; + } + else + switch (gstate->source_fill.type) + { + case CTX_SOURCE_COLOR: + if (gstate->compositing_mode == CTX_COMPOSITE_SOURCE_OVER) + { + rasterizer->comp_op = ctx_RGBAF_source_over_normal_color; + if ( ((float*)rasterizer->color)[3] >= 0.999f) + rasterizer->comp = CTX_COV_PATH_RGBAF_COPY; + } + else + { + rasterizer->comp_op = ctx_RGBAF_porter_duff_color_normal; + } + break; +#if CTX_GRADIENTS + case CTX_SOURCE_LINEAR_GRADIENT: + rasterizer->comp_op = ctx_RGBAF_porter_duff_linear_gradient_normal; + break; + case CTX_SOURCE_RADIAL_GRADIENT: + rasterizer->comp_op = ctx_RGBAF_porter_duff_radial_gradient_normal; + break; +#endif + case CTX_SOURCE_TEXTURE: + rasterizer->comp_op = ctx_RGBAF_porter_duff_image_normal; + break; + default: + rasterizer->comp_op = ctx_RGBAF_porter_duff_generic_normal; + break; + } + break; + default: + switch (gstate->source_fill.type) + { + case CTX_SOURCE_COLOR: + rasterizer->comp_op = ctx_RGBAF_porter_duff_color; + break; +#if CTX_GRADIENTS + case CTX_SOURCE_LINEAR_GRADIENT: + rasterizer->comp_op = ctx_RGBAF_porter_duff_linear_gradient; + break; + case CTX_SOURCE_RADIAL_GRADIENT: + rasterizer->comp_op = ctx_RGBAF_porter_duff_radial_gradient; + break; +#endif + case CTX_SOURCE_TEXTURE: + rasterizer->comp_op = ctx_RGBAF_porter_duff_image; + break; + default: + rasterizer->comp_op = ctx_RGBAF_porter_duff_generic; + break; + } + break; + } +#endif + ctx_setup_apply_coverage (rasterizer); +} + +#endif +#if CTX_ENABLE_GRAYAF + +#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) +{ + float rgba[4]; + CtxSource *g = &rasterizer->state->gstate.source_fill; + for (int i = 0 ; i < count; i++) + { + float v = ( ( (g->linear_gradient.dx * x + g->linear_gradient.dy * y) / + g->linear_gradient.length) - + g->linear_gradient.start) * (g->linear_gradient.rdelta); + ctx_fragment_gradient_1d_RGBAF (rasterizer, v, 1.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; + } +} + +static void +ctx_fragment_radial_gradient_GRAYAF (CtxRasterizer *rasterizer, float x, float y, float z, void *out, int count, float dx, float dy, float dz) +{ + float rgba[4]; + CtxSource *g = &rasterizer->state->gstate.source_fill; + for (int i = 0; i < count; i ++) + { + float v = 0.0f; + if ((g->radial_gradient.r1-g->radial_gradient.r0) > 0.0f) + { + v = ctx_hypotf (g->radial_gradient.x0 - x, g->radial_gradient.y0 - y); + v = (v - g->radial_gradient.r0) / (g->radial_gradient.rdelta); + } + ctx_fragment_gradient_1d_RGBAF (rasterizer, v, 0.0, rgba); + ((float*)out)[0] = ctx_float_color_rgb_to_gray (rasterizer->state, rgba); + ((float*)out)[1] = rgba[3]; + out = ((float*)(out)) + 2; + x += dx; + y += dy; + } +} +#endif + +static void +ctx_fragment_color_GRAYAF (CtxRasterizer *rasterizer, float x, float y, float z, void *out, int count, float dx, float dy, float dz) +{ + CtxSource *g = &rasterizer->state->gstate.source_fill; + for (int i = 0; i < count; i++) + { + ctx_color_get_graya (rasterizer->state, &g->color, (float*)out); + out = ((float*)(out)) + 2; + x += dx; + y += dy; + } +} + +static void ctx_fragment_image_GRAYAF (CtxRasterizer *rasterizer, float x, float y, float z, void *out, int count, float dx, float dy, float dz) +{ + 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; + switch (buffer->format->bpp) + { +#if CTX_FRAGMENT_SPECIALIZE + case 1: ctx_fragment_image_gray1_RGBA8 (rasterizer, x, y, z, rgba, count, dx, dy, dz); break; + case 24: ctx_fragment_image_rgb8_RGBA8 (rasterizer, x, y, z, rgba, count, dx, dy, dz); break; + case 32: ctx_fragment_image_rgba8_RGBA8 (rasterizer, x, y, z, rgba, count, dx, dy, dz); break; +#endif + default: ctx_fragment_image_RGBA8 (rasterizer, x, y, z, rgba, count, dx, dy, dz); break; + } + for (int c = 0; c < 2 * count; c ++) { + rgbaf[c] = ctx_u8_to_float (rgba[c]); + ((float*)out)[0] = ctx_float_color_rgb_to_gray (rasterizer->state, rgbaf); + ((float*)out)[1] = rgbaf[3]; + out = ((float*)out) + 2; + } +} + +static CtxFragment ctx_rasterizer_get_fragment_GRAYAF (CtxRasterizer *rasterizer) +{ + CtxGState *gstate = &rasterizer->state->gstate; + switch (gstate->source_fill.type) + { + case CTX_SOURCE_TEXTURE: return ctx_fragment_image_GRAYAF; + case CTX_SOURCE_COLOR: return ctx_fragment_color_GRAYAF; +#if CTX_GRADIENTS + case CTX_SOURCE_LINEAR_GRADIENT: return ctx_fragment_linear_gradient_GRAYAF; + case CTX_SOURCE_RADIAL_GRADIENT: return ctx_fragment_radial_gradient_GRAYAF; +#endif + } + return ctx_fragment_color_GRAYAF; +} + +ctx_float_porter_duff(GRAYAF, 2,color, rasterizer->fragment, rasterizer->state->gstate.blend_mode) +ctx_float_porter_duff(GRAYAF, 2,generic, rasterizer->fragment, rasterizer->state->gstate.blend_mode) + +#if CTX_INLINED_NORMAL +ctx_float_porter_duff(GRAYAF, 2,color_normal, rasterizer->fragment, CTX_BLEND_NORMAL) +ctx_float_porter_duff(GRAYAF, 2,generic_normal, rasterizer->fragment, CTX_BLEND_NORMAL) + +static void +ctx_GRAYAF_copy_normal (CTX_COMPOSITE_ARGUMENTS) +{ + ctx_float_copy_normal (2, rasterizer, dst, src, x0, coverage, count); +} + +static void +ctx_GRAYAF_clear_normal (CTX_COMPOSITE_ARGUMENTS) +{ + ctx_float_clear_normal (2, rasterizer, dst, src, x0, coverage, count); +} + +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); +} +#endif + +static void +ctx_setup_GRAYAF (CtxRasterizer *rasterizer) +{ + CtxGState *gstate = &rasterizer->state->gstate; + int components = 2; + rasterizer->fragment = ctx_rasterizer_get_fragment_GRAYAF (rasterizer); + rasterizer->comp = CTX_COV_PATH_FALLBACK; + if (gstate->source_fill.type == CTX_SOURCE_COLOR) + { + rasterizer->comp_op = ctx_GRAYAF_porter_duff_color; + ctx_color_get_rgba (rasterizer->state, &gstate->source_fill.color, (float*)rasterizer->color); + if (gstate->global_alpha_u8 != 255) + for (int c = 0; c < components; c ++) + ((float*)rasterizer->color)[c] *= gstate->global_alpha_f; + + if (rasterizer->format->from_comp) + rasterizer->format->from_comp (rasterizer, 0, + &rasterizer->color[0], + &rasterizer->color_native, + 1); + } + else + { + rasterizer->comp_op = ctx_GRAYAF_porter_duff_generic; + } + +#if CTX_INLINED_NORMAL + if (gstate->compositing_mode == CTX_COMPOSITE_CLEAR) + rasterizer->comp_op = ctx_GRAYAF_clear_normal; + else + switch (gstate->blend_mode) + { + case CTX_BLEND_NORMAL: + if (gstate->compositing_mode == CTX_COMPOSITE_COPY) + { + rasterizer->comp_op = ctx_GRAYAF_copy_normal; + } + else if (gstate->global_alpha_u8 == 0) + rasterizer->comp_op = ctx_RGBA8_nop; + else + switch (gstate->source_fill.type) + { + case CTX_SOURCE_COLOR: + if (gstate->compositing_mode == CTX_COMPOSITE_SOURCE_OVER) + { + if (((float*)rasterizer->color)[components-1] == 0.0f) + rasterizer->comp_op = ctx_RGBA8_nop; +#if 1 + else //if (((float*)rasterizer->color)[components-1] == 0.0f) + rasterizer->comp_op = ctx_GRAYAF_source_copy_normal_color; +#endif + //else + // rasterizer->comp_op = ctx_GRAYAF_porter_duff_color_normal; + } + else + { + rasterizer->comp_op = ctx_GRAYAF_porter_duff_color_normal; + } + break; + default: + rasterizer->comp_op = ctx_GRAYAF_porter_duff_generic_normal; + break; + } + break; + default: + switch (gstate->source_fill.type) + { + case CTX_SOURCE_COLOR: + rasterizer->comp_op = ctx_GRAYAF_porter_duff_color; + break; + default: + rasterizer->comp_op = ctx_GRAYAF_porter_duff_generic; + break; + } + break; + } +#endif + ctx_setup_apply_coverage (rasterizer); +} + +#endif +#if CTX_ENABLE_GRAYF + +static void +ctx_composite_GRAYF (CTX_COMPOSITE_ARGUMENTS) +{ + float *dstf = (float*)dst; + + float temp[count*2]; + for (unsigned int i = 0; i < count; i++) + { + temp[i*2] = dstf[i]; + temp[i*2+1] = 1.0f; + } + rasterizer->comp_op (rasterizer, (uint8_t*)temp, rasterizer->color, x0, coverage, count); + for (unsigned int i = 0; i < count; i++) + { + dstf[i] = temp[i*2]; + } +} + +#endif +#if CTX_ENABLE_BGRA8 + +inline static void +ctx_swap_red_green (uint8_t *rgba) +{ + uint32_t *buf = (uint32_t *) rgba; + uint32_t orig = *buf; + uint32_t green_alpha = (orig & 0xff00ff00); + uint32_t red_blue = (orig & 0x00ff00ff); + uint32_t red = red_blue << 16; + uint32_t blue = red_blue >> 16; + *buf = green_alpha | red | blue; +} + +static void +ctx_BGRA8_to_RGBA8 (CtxRasterizer *rasterizer, int x, const void *buf, uint8_t *rgba, int count) +{ + uint32_t *srci = (uint32_t *) buf; + uint32_t *dsti = (uint32_t *) rgba; + while (count--) + { + uint32_t val = *srci++; + ctx_swap_red_green ( (uint8_t *) &val); + *dsti++ = val; + } +} + +static void +ctx_RGBA8_to_BGRA8 (CtxRasterizer *rasterizer, int x, const uint8_t *rgba, void *buf, int count) +{ + ctx_BGRA8_to_RGBA8 (rasterizer, x, rgba, (uint8_t *) buf, count); +} + +static void +ctx_composite_BGRA8 (CTX_COMPOSITE_ARGUMENTS) +{ + // for better performance, this could be done without a pre/post conversion, + // by swapping R and B of source instead... as long as it is a color instead + // of gradient or image + // + // + uint8_t pixels[count * 4]; + ctx_BGRA8_to_RGBA8 (rasterizer, x0, dst, &pixels[0], count); + rasterizer->comp_op (rasterizer, &pixels[0], rasterizer->color, x0, coverage, count); + ctx_BGRA8_to_RGBA8 (rasterizer, x0, &pixels[0], dst, count); +} + + +#endif +static inline void +ctx_composite_direct (CTX_COMPOSITE_ARGUMENTS) +{ + // for better performance, this could be done without a pre/post conversion, + // by swapping R and B of source instead... as long as it is a color instead + // of gradient or image + // + // + rasterizer->comp_op (rasterizer, dst, rasterizer->color, x0, coverage, count); +} + +#if CTX_ENABLE_CMYKAF + +static void +ctx_fragment_other_CMYKAF (CtxRasterizer *rasterizer, float x, float y, float z, void *out, int count, float dx, float dy, float dz) +{ + float *cmyka = (float*)out; + float _rgba[4 * count]; + float *rgba = &_rgba[0]; + CtxGState *gstate = &rasterizer->state->gstate; + switch (gstate->source_fill.type) + { + case CTX_SOURCE_TEXTURE: + ctx_fragment_image_RGBAF (rasterizer, x, y, z, rgba, count, dx, dy, dz); + break; + case CTX_SOURCE_COLOR: + ctx_fragment_color_RGBAF (rasterizer, x, y, z, rgba, count, dx, dy, dz); + break; +#if CTX_GRADIENTS + case CTX_SOURCE_LINEAR_GRADIENT: + ctx_fragment_linear_gradient_RGBAF (rasterizer, x, y, z, rgba, count, dx, dy, dz); + break; + case CTX_SOURCE_RADIAL_GRADIENT: + ctx_fragment_radial_gradient_RGBAF (rasterizer, x, y, z, rgba, count, dx, dy, dz); + break; +#endif + default: + rgba[0]=rgba[1]=rgba[2]=rgba[3]=0.0f; + break; + } + for (int i = 0; i < count; i++) + { + cmyka[4]=rgba[3]; + ctx_rgb_to_cmyk (rgba[0], rgba[1], rgba[2], &cmyka[0], &cmyka[1], &cmyka[2], &cmyka[3]); + cmyka += 5; + rgba += 4; + } +} + +static void +ctx_fragment_color_CMYKAF (CtxRasterizer *rasterizer, float x, float y, float z, void *out, int count, float dx, float dy, float dz) +{ + CtxGState *gstate = &rasterizer->state->gstate; + float *cmyka = (float*)out; + float cmyka_in[5]; + ctx_color_get_cmyka (rasterizer->state, &gstate->source_fill.color, cmyka_in); + for (int i = 0; i < count; i++) + { + for (int c = 0; c < 4; c ++) + { + cmyka[c] = (1.0f - cmyka_in[c]); + } + cmyka[4] = cmyka_in[4]; + cmyka += 5; + } +} + +static CtxFragment ctx_rasterizer_get_fragment_CMYKAF (CtxRasterizer *rasterizer) +{ + CtxGState *gstate = &rasterizer->state->gstate; + switch (gstate->source_fill.type) + { + case CTX_SOURCE_COLOR: + return ctx_fragment_color_CMYKAF; + } + return ctx_fragment_other_CMYKAF; +} + +ctx_float_porter_duff (CMYKAF, 5,color, rasterizer->fragment, rasterizer->state->gstate.blend_mode) +ctx_float_porter_duff (CMYKAF, 5,generic, rasterizer->fragment, rasterizer->state->gstate.blend_mode) + +#if CTX_INLINED_NORMAL +ctx_float_porter_duff (CMYKAF, 5,color_normal, rasterizer->fragment, CTX_BLEND_NORMAL) +ctx_float_porter_duff (CMYKAF, 5,generic_normal, rasterizer->fragment, CTX_BLEND_NORMAL) + +static void +ctx_CMYKAF_copy_normal (CTX_COMPOSITE_ARGUMENTS) +{ + ctx_float_copy_normal (5, rasterizer, dst, src, x0, coverage, count); +} + +static void +ctx_CMYKAF_clear_normal (CTX_COMPOSITE_ARGUMENTS) +{ + ctx_float_clear_normal (5, rasterizer, dst, src, x0, coverage, count); +} + +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); +} +#endif + +static void +ctx_setup_CMYKAF (CtxRasterizer *rasterizer) +{ + CtxGState *gstate = &rasterizer->state->gstate; + int components = 5; + rasterizer->fragment = ctx_rasterizer_get_fragment_CMYKAF (rasterizer); + rasterizer->comp = CTX_COV_PATH_FALLBACK; + if (gstate->source_fill.type == CTX_SOURCE_COLOR) + { + rasterizer->comp_op = ctx_CMYKAF_porter_duff_color; + rasterizer->comp_op = ctx_CMYKAF_porter_duff_generic; + ctx_color_get_cmyka (rasterizer->state, &gstate->source_fill.color, (float*)rasterizer->color); + if (gstate->global_alpha_u8 != 255) + ((float*)rasterizer->color)[components-1] *= gstate->global_alpha_f; + + if (rasterizer->format->from_comp) + rasterizer->format->from_comp (rasterizer, 0, + &rasterizer->color[0], + &rasterizer->color_native, + 1); + } + else + { + rasterizer->comp_op = ctx_CMYKAF_porter_duff_generic; + } + +#if CTX_INLINED_NORMAL + if (gstate->compositing_mode == CTX_COMPOSITE_CLEAR) + rasterizer->comp_op = ctx_CMYKAF_clear_normal; + else + switch (gstate->blend_mode) + { + case CTX_BLEND_NORMAL: + if (gstate->compositing_mode == CTX_COMPOSITE_COPY) + { + rasterizer->comp_op = ctx_CMYKAF_copy_normal; + } + else if (gstate->global_alpha_u8 == 0) + rasterizer->comp_op = ctx_RGBA8_nop; + else + switch (gstate->source_fill.type) + { + case CTX_SOURCE_COLOR: + if (gstate->compositing_mode == CTX_COMPOSITE_SOURCE_OVER) + { + if (((float*)rasterizer->color)[components-1] == 0.0f) + rasterizer->comp_op = ctx_RGBA8_nop; + else if (((float*)rasterizer->color)[components-1] == 1.0f) + { + rasterizer->comp_op = ctx_CMYKAF_source_copy_normal_color; + rasterizer->comp = CTX_COV_PATH_CMYKAF_COPY; + } + else + rasterizer->comp_op = ctx_CMYKAF_porter_duff_color_normal; + } + else + { + rasterizer->comp_op = ctx_CMYKAF_porter_duff_color_normal; + } + break; + default: + rasterizer->comp_op = ctx_CMYKAF_porter_duff_generic_normal; + break; + } + break; + default: + switch (gstate->source_fill.type) + { + case CTX_SOURCE_COLOR: + rasterizer->comp_op = ctx_CMYKAF_porter_duff_color; + break; + default: + rasterizer->comp_op = ctx_CMYKAF_porter_duff_generic; + break; + } + break; + } +#else + + 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_CMYKAF_COPY; + } + else if (gstate->compositing_mode == CTX_COMPOSITE_SOURCE_OVER && + rasterizer->color[components-1] == 255) + { + rasterizer->comp = CTX_COV_PATH_CMYKAF_COPY; + } + } +#endif + ctx_setup_apply_coverage (rasterizer); +} + +static void +ctx_setup_CMYKA8 (CtxRasterizer *rasterizer) +{ + ctx_setup_CMYKAF (rasterizer); + + if (rasterizer->comp == CTX_COV_PATH_CMYKAF_COPY) + rasterizer->comp = CTX_COV_PATH_CMYKA8_COPY; +} + +static void +ctx_setup_CMYK8 (CtxRasterizer *rasterizer) +{ + ctx_setup_CMYKAF (rasterizer); + if (rasterizer->comp == CTX_COV_PATH_CMYKAF_COPY) + rasterizer->comp = CTX_COV_PATH_CMYK8_COPY; +} + +#endif +#if CTX_ENABLE_CMYKA8 + +static void +ctx_CMYKA8_to_CMYKAF (CtxRasterizer *rasterizer, uint8_t *src, float *dst, int count) +{ + for (int i = 0; i < count; i ++) + { + for (int c = 0; c < 4; c ++) + { dst[c] = ctx_u8_to_float ( (255-src[c]) ); } + dst[4] = ctx_u8_to_float (src[4]); + for (int c = 0; c < 4; c++) + { dst[c] *= dst[4]; } + src += 5; + dst += 5; + } +} +static void +ctx_CMYKAF_to_CMYKA8 (CtxRasterizer *rasterizer, float *src, uint8_t *dst, int count) +{ + for (int i = 0; i < count; i ++) + { + int a = ctx_float_to_u8 (src[4]); + if (a != 0 && a != 255) + { + float recip = 1.0f/src[4]; + for (int c = 0; c < 4; c++) + { + dst[c] = ctx_float_to_u8 (1.0f - src[c] * recip); + } + } + else + { + for (int c = 0; c < 4; c++) + dst[c] = 255 - ctx_float_to_u8 (src[c]); + } + dst[4]=a; + + src += 5; + dst += 5; + } +} + +static void +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); + ctx_CMYKAF_to_CMYKA8 (rasterizer, &pixels[0], dst, count); +} + +#endif +#if CTX_ENABLE_CMYK8 + +static void +ctx_CMYK8_to_CMYKAF (CtxRasterizer *rasterizer, uint8_t *src, float *dst, int count) +{ + for (int i = 0; i < count; i ++) + { + dst[0] = ctx_u8_to_float (255-src[0]); + dst[1] = ctx_u8_to_float (255-src[1]); + dst[2] = ctx_u8_to_float (255-src[2]); + dst[3] = ctx_u8_to_float (255-src[3]); + dst[4] = 1.0f; + src += 4; + dst += 5; + } +} +static void +ctx_CMYKAF_to_CMYK8 (CtxRasterizer *rasterizer, float *src, uint8_t *dst, int count) +{ + for (int i = 0; i < count; i ++) + { + float c = src[0]; + float m = src[1]; + float y = src[2]; + float k = src[3]; + float a = src[4]; + if (a != 0.0f && a != 1.0f) + { + float recip = 1.0f/a; + c *= recip; + m *= recip; + y *= recip; + k *= recip; + } + c = 1.0f - c; + m = 1.0f - m; + y = 1.0f - y; + k = 1.0f - k; + dst[0] = ctx_float_to_u8 (c); + dst[1] = ctx_float_to_u8 (m); + dst[2] = ctx_float_to_u8 (y); + dst[3] = ctx_float_to_u8 (k); + src += 5; + dst += 4; + } +} + +static void +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); + ctx_CMYKAF_to_CMYK8 (rasterizer, &pixels[0], dst, count); +} +#endif + +#if CTX_ENABLE_RGB8 + +inline static void +ctx_RGB8_to_RGBA8 (CtxRasterizer *rasterizer, int x, const void *buf, uint8_t *rgba, int count) +{ + const uint8_t *pixel = (const uint8_t *) buf; + while (count--) + { + rgba[0] = pixel[0]; + rgba[1] = pixel[1]; + rgba[2] = pixel[2]; + rgba[3] = 255; + pixel+=3; + rgba +=4; + } +} + +inline static void +ctx_RGBA8_to_RGB8 (CtxRasterizer *rasterizer, int x, const uint8_t *rgba, void *buf, int count) +{ + uint8_t *pixel = (uint8_t *) buf; + while (count--) + { + pixel[0] = rgba[0]; + pixel[1] = rgba[1]; + pixel[2] = rgba[2]; + pixel+=3; + rgba +=4; + } +} + +#endif +#if CTX_ENABLE_GRAY1 + +#if CTX_NATIVE_GRAYA8 +inline static void +ctx_GRAY1_to_GRAYA8 (CtxRasterizer *rasterizer, int x, const void *buf, uint8_t *graya, int count) +{ + const uint8_t *pixel = (uint8_t *) buf; + while (count--) + { + int bitno = x&7; + if (bitno == 0 && count >= 7) + { + if (*pixel == 0) + { + for (int i = 0; i < 8; i++) + { + *graya++ = 0; *graya++ = 255; + } + x+=8; count-=7; pixel++; + continue; + } + else if (*pixel == 0xff) + { + for (int i = 0; i < 8 * 2; i++) + { + *graya++ = 255; + } + x+=8; count-=7; pixel++; + continue; + } + } + *graya++ = 255 * ((*pixel) & (1<<bitno)); + *graya++ = 255; + pixel+= (bitno ==7); + x++; + } +} + +inline static void +ctx_GRAYA8_to_GRAY1 (CtxRasterizer *rasterizer, int x, const uint8_t *rgba, void *buf, int count) +{ + uint8_t *pixel = (uint8_t *) buf; + while (count--) + { + int gray = rgba[0]; + int bitno = x&7; + if (gray >= 128) + *pixel |= (1<<bitno); + else + *pixel &= (~ (1<<bitno)); + pixel+= (bitno==7); + x++; + rgba +=2; + } +} + +#else + +inline static void +ctx_GRAY1_to_RGBA8 (CtxRasterizer *rasterizer, int x, const void *buf, uint8_t *rgba, int count) +{ + const uint8_t *pixel = (uint8_t *) buf; + uint32_t *dst = (uint32_t*)rgba; + while (count--) + { + int bitno = x&7; + + if ((bitno) == 0 && count >=7) + { + /* special case some bit patterns when decoding */ + if (*pixel == 0) + { + *dst++ = 0xff000000; + *dst++ = 0xff000000; + *dst++ = 0xff000000; + *dst++ = 0xff000000; + *dst++ = 0xff000000; + *dst++ = 0xff000000; + *dst++ = 0xff000000; + *dst++ = 0xff000000; + x+=8; count-=7; pixel++; + continue; + } + else if (*pixel == 0xff) + { + *dst++ = 0xffffffff; + *dst++ = 0xffffffff; + *dst++ = 0xffffffff; + *dst++ = 0xffffffff; + *dst++ = 0xffffffff; + *dst++ = 0xffffffff; + *dst++ = 0xffffffff; + *dst++ = 0xffffffff; + x+=8; count-=7; pixel++; + continue; + } + else if (*pixel == 0x0f) + { + *dst++ = 0xff000000; + *dst++ = 0xff000000; + *dst++ = 0xff000000; + *dst++ = 0xff000000; + *dst++ = 0xffffffff; + *dst++ = 0xffffffff; + *dst++ = 0xffffffff; + *dst++ = 0xffffffff; + x+=8; count-=7; pixel++; + continue; + } + else if (*pixel == 0xfc) + { + *dst++ = 0xffffffff; + *dst++ = 0xffffffff; + *dst++ = 0xffffffff; + *dst++ = 0xffffffff; + *dst++ = 0xffffffff; + *dst++ = 0xffffffff; + *dst++ = 0xff000000; + *dst++ = 0xff000000; + x+=8; count-=7; pixel++; + continue; + } + else if (*pixel == 0x3f) + { + *dst++ = 0xff000000; + *dst++ = 0xff000000; + *dst++ = 0xffffffff; + *dst++ = 0xffffffff; + *dst++ = 0xffffffff; + *dst++ = 0xffffffff; + *dst++ = 0xffffffff; + *dst++ = 0xffffffff; + x+=8; count-=7; pixel++; + continue; + } + } + *dst++=0xff000000 + 0x00ffffff * ((*pixel & (1<< bitno ) )!=0); + pixel += (bitno ==7); + x++; + } +} + +inline static void +ctx_RGBA8_to_GRAY1 (CtxRasterizer *rasterizer, int x, const uint8_t *rgba, void *buf, int count) +{ + uint8_t *pixel = (uint8_t *) buf; + while (count--) + { + int gray = ctx_u8_color_rgb_to_gray (rasterizer->state, rgba); + int bitno = x&7; + //gray += ctx_dither_mask_a (x, rasterizer->scanline/aa, 0, 127); + if (gray >= 128) + *pixel |= (1<< bitno); + else + *pixel &= (~ (1<< bitno)); + pixel+= (bitno ==7); + x++; + rgba +=4; + } +} +#endif + +#endif +#if CTX_ENABLE_GRAY2 + +#if CTX_NATIVE_GRAYA8 +inline static void +ctx_GRAY2_to_GRAYA8 (CtxRasterizer *rasterizer, int x, const void *buf, uint8_t *rgba, int count) +{ + const uint8_t *pixel = (uint8_t *) buf; + while (count--) + { + uint8_t val = (((*pixel) >> ( (x&3) <<1)) & 3) * 85; + rgba[0] = val; + rgba[1] = 255; + if ( (x&3) ==3) + { pixel+=1; } + x++; + rgba +=2; + } +} + +inline static void +ctx_GRAYA8_to_GRAY2 (CtxRasterizer *rasterizer, int x, const uint8_t *rgba, void *buf, int count) +{ + uint8_t *pixel = (uint8_t *) buf; + while (count--) + { + int val = rgba[0]; + val = ctx_sadd8 (val, 40) >> 6; + *pixel = (*pixel & (~ (3 << ( (x&3) <<1) ) )) + | ( (val << ( (x&3) <<1) ) ); + if ( (x&3) ==3) + { pixel+=1; } + x++; + rgba +=2; + } +} +#else + +inline static void +ctx_GRAY2_to_RGBA8 (CtxRasterizer *rasterizer, int x, const void *buf, uint8_t *rgba, int count) +{ + const uint8_t *pixel = (uint8_t *) buf; + uint32_t *dst = (uint32_t*)rgba; + while (count--) + { + int bitno = x & 3; + if ((bitno) == 0 && count >=3) + { + /* special case some bit patterns when decoding */ + if (*pixel == 0) + { + *dst++ = 0xff000000; + *dst++ = 0xff000000; + *dst++ = 0xff000000; + *dst++ = 0xff000000; + x+=4; count-=3; pixel++; + continue; + } + else if (*pixel == 0xff) + { + *dst++ = 0xffffffff; + *dst++ = 0xffffffff; + *dst++ = 0xffffffff; + *dst++ = 0xffffffff; + x+=4; count-=3; pixel++; + continue; + } + else if (*pixel == 0x55) + { + *dst++ = 0xff555555; + *dst++ = 0xff555555; + *dst++ = 0xff555555; + *dst++ = 0xff555555; + x+=4; count-=3; pixel++; + continue; + } + else if (*pixel == 0xaa) + { + *dst++ = 0xffaaaaaa; + *dst++ = 0xffaaaaaa; + *dst++ = 0xffaaaaaa; + *dst++ = 0xffaaaaaa; + x+=4; count-=3; pixel++; + continue; + } + else if (*pixel == 0x0f) + { + *dst++ = 0xff000000; + *dst++ = 0xff000000; + *dst++ = 0xffffffff; + *dst++ = 0xffffffff; + x+=4; count-=3; pixel++; + continue; + } + else if (*pixel == 0xfc) + { + *dst++ = 0xffffffff; + *dst++ = 0xffffffff; + *dst++ = 0xffffffff; + *dst++ = 0xff000000; + x+=4; count-=3; pixel++; + continue; + } + else if (*pixel == 0x3f) + { + *dst++ = 0xff000000; + *dst++ = 0xffffffff; + *dst++ = 0xffffffff; + *dst++ = 0xffffffff; + x+=4; count-=3; pixel++; + continue; + } + } + { + uint8_t val = (((*pixel) >> ( (bitno) <<1)) & 3) * 85; + *dst = val + val * 256u + val * 256u * 256u + 255u * 256u * 256u * 256u; + if (bitno==3) + { pixel+=1; } + x++; + dst++; + } + } +} + +inline static void +ctx_RGBA8_to_GRAY2 (CtxRasterizer *rasterizer, int x, const uint8_t *rgba, void *buf, int count) +{ + uint8_t *pixel = (uint8_t *) buf; + while (count--) + { + int val = ctx_u8_color_rgb_to_gray (rasterizer->state, rgba); + val >>= 6; + *pixel = (*pixel & (~ (3 << ((x&3) <<1) ) )) + | ( (val << ((x&3) <<1) ) ); + if ( (x&3) ==3) + { pixel+=1; } + x++; + rgba +=4; + } +} +#endif + +#endif +#if CTX_ENABLE_GRAY4 + +#if CTX_NATIVE_GRAYA8 +inline static void +ctx_GRAY4_to_GRAYA8 (CtxRasterizer *rasterizer, int x, const void *buf, uint8_t *rgba, int count) +{ + const uint8_t *pixel = (uint8_t *) buf; + while (count--) + { + int val = (*pixel & (15 << ( (x & 1) <<2) ) ) >> ( (x&1) <<2); + val <<= 4; + rgba[0] = val; + rgba[1] = 255; + if ( (x&1) ==1) + { pixel+=1; } + x++; + rgba +=2; + } +} + +inline static void +ctx_GRAYA8_to_GRAY4 (CtxRasterizer *rasterizer, int x, const uint8_t *rgba, void *buf, int count) +{ + uint8_t *pixel = (uint8_t *) buf; + while (count--) + { + int val = rgba[0]; + val >>= 4; + *pixel = *pixel & (~ (15 << ( (x&1) <<2) ) ); + *pixel = *pixel | ( (val << ( (x&1) <<2) ) ); + if ( (x&1) ==1) + { pixel+=1; } + x++; + rgba +=2; + } +} +#else +inline static void +ctx_GRAY4_to_RGBA8 (CtxRasterizer *rasterizer, int x, const void *buf, uint8_t *rgba, int count) +{ + const uint8_t *pixel = (uint8_t *) buf; + while (count--) + { + int val = (*pixel & (15 << ( (x & 1) <<2) ) ) >> ( (x&1) <<2); + val <<= 4; + rgba[0] = val; + rgba[1] = val; + rgba[2] = val; + rgba[3] = 255; + if ( (x&1) ==1) + { pixel+=1; } + x++; + rgba +=4; + } +} + +inline static void +ctx_RGBA8_to_GRAY4 (CtxRasterizer *rasterizer, int x, const uint8_t *rgba, void *buf, int count) +{ + uint8_t *pixel = (uint8_t *) buf; + while (count--) + { + int val = ctx_u8_color_rgb_to_gray (rasterizer->state, rgba); + val >>= 4; + *pixel = *pixel & (~ (15 << ( (x&1) <<2) ) ); + *pixel = *pixel | ( (val << ( (x&1) <<2) ) ); + if ( (x&1) ==1) + { pixel+=1; } + x++; + rgba +=4; + } +} +#endif + +#endif +#if CTX_ENABLE_GRAY8 + +#if CTX_NATIVE_GRAYA8 +inline static void +ctx_GRAY8_to_GRAYA8 (CtxRasterizer *rasterizer, int x, const void *buf, uint8_t *rgba, int count) +{ + const uint8_t *pixel = (uint8_t *) buf; + while (count--) + { + rgba[0] = pixel[0]; + rgba[1] = 255; + pixel+=1; + rgba +=2; + } +} + +inline static void +ctx_GRAYA8_to_GRAY8 (CtxRasterizer *rasterizer, int x, const uint8_t *rgba, void *buf, int count) +{ + uint8_t *pixel = (uint8_t *) buf; + while (count--) + { + pixel[0] = rgba[0]; + pixel+=1; + rgba +=2; + } +} +#else +inline static void +ctx_GRAY8_to_RGBA8 (CtxRasterizer *rasterizer, int x, const void *buf, uint8_t *rgba, int count) +{ + const uint8_t *pixel = (uint8_t *) buf; + while (count--) + { + rgba[0] = pixel[0]; + rgba[1] = pixel[0]; + rgba[2] = pixel[0]; + rgba[3] = 255; + pixel+=1; + rgba +=4; + } +} + +inline static void +ctx_RGBA8_to_GRAY8 (CtxRasterizer *rasterizer, int x, const uint8_t *rgba, void *buf, int count) +{ + uint8_t *pixel = (uint8_t *) buf; + for (int i = 0; i < count; i ++) + { + pixel[i] = ctx_u8_color_rgb_to_gray (rasterizer->state, rgba + i * 4); + } +} +#endif + +#endif +#if CTX_ENABLE_GRAYA8 + +inline static void +ctx_GRAYA8_to_RGBA8 (CtxRasterizer *rasterizer, int x, const void *buf, uint8_t *rgba, int count) +{ + const uint8_t *pixel = (const uint8_t *) buf; + while (count--) + { + rgba[0] = pixel[0]; + rgba[1] = pixel[0]; + rgba[2] = pixel[0]; + rgba[3] = pixel[1]; + pixel+=2; + rgba +=4; + } +} + +inline static void +ctx_RGBA8_to_GRAYA8 (CtxRasterizer *rasterizer, int x, const uint8_t *rgba, void *buf, int count) +{ + uint8_t *pixel = (uint8_t *) buf; + while (count--) + { + pixel[0] = ctx_u8_color_rgb_to_gray (rasterizer->state, rgba); + pixel[1] = rgba[3]; + pixel+=2; + rgba +=4; + } +} + +#if CTX_NATIVE_GRAYA8 +CTX_INLINE static void ctx_rgba_to_graya_u8 (CtxState *state, uint8_t *in, uint8_t *out) +{ + out[0] = ctx_u8_color_rgb_to_gray (state, in); + out[1] = in[3]; +} + +#if CTX_GRADIENTS +static void +ctx_fragment_linear_gradient_GRAYA8 (CtxRasterizer *rasterizer, float x, float y, float z, void *out, int count, float dx, float dy, float dz) +{ + CtxSource *g = &rasterizer->state->gstate.source_fill; + uint8_t *dst = (uint8_t*)out; +#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_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; + } +} + +static void +ctx_fragment_radial_gradient_GRAYA8 (CtxRasterizer *rasterizer, float x, float y, float z, void *out, int count, float dx, float dy, float dz) +{ + uint8_t *dst = (uint8_t*)out; +#if CTX_DITHER + int scan = rasterizer->scanline / CTX_FULL_AA; + int ox = (int)x; +#endif + + for (int i = 0; i < count;i ++) + { + CtxSource *g = &rasterizer->state->gstate.source_fill; + float v = (ctx_hypotf (g->radial_gradient.x0 - x, g->radial_gradient.y0 - y) - + g->radial_gradient.r0) * (g->radial_gradient.rdelta); + { + uint8_t rgba[4]; + ctx_fragment_gradient_1d_RGBA8 (rasterizer, v, 1.0, rgba); + ctx_rgba_to_graya_u8 (rasterizer->state, rgba, dst); + } +#if CTX_DITHER + ctx_dither_graya_u8 ((uint8_t*)dst, ox+i, scan, rasterizer->format->dither_red_blue, + rasterizer->format->dither_green); +#endif + dst += 2; + x += dx; + y += dy; + } +} +#endif + +static void +ctx_fragment_color_GRAYA8 (CtxRasterizer *rasterizer, float x, float y, float z, void *out, int count, float dx, float dy, float dz) +{ + CtxSource *g = &rasterizer->state->gstate.source_fill; + uint16_t *dst = (uint16_t*)out; + uint16_t pix; + ctx_color_get_graya_u8 (rasterizer->state, &g->color, (uint8_t*)&pix); + for (int i = 0; i <count; i++) + { + dst[i]=pix; + } +} + +static void ctx_fragment_image_GRAYA8 (CtxRasterizer *rasterizer, float x, float y, float z, void *out, int count, float dx, float dy, float dz) +{ + 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; + switch (buffer->format->bpp) + { +#if CTX_FRAGMENT_SPECIALIZE + case 1: ctx_fragment_image_gray1_RGBA8 (rasterizer, x, y, z, rgba, count, dx, dy, dz); break; + case 24: ctx_fragment_image_rgb8_RGBA8 (rasterizer, x, y, z, rgba, count, dx, dy, dz); break; + case 32: ctx_fragment_image_rgba8_RGBA8 (rasterizer, x, y, z, rgba, count, dx, dy, dz); break; +#endif + default: ctx_fragment_image_RGBA8 (rasterizer, x, y, z, rgba, count, dx, dy, dz); break; + } + for (int i = 0; i < count; i++) + ctx_rgba_to_graya_u8 (rasterizer->state, &rgba[i*4], &((uint8_t*)out)[i*2]); +} + +static CtxFragment ctx_rasterizer_get_fragment_GRAYA8 (CtxRasterizer *rasterizer) +{ + CtxGState *gstate = &rasterizer->state->gstate; + switch (gstate->source_fill.type) + { + case CTX_SOURCE_TEXTURE: return ctx_fragment_image_GRAYA8; + case CTX_SOURCE_COLOR: return ctx_fragment_color_GRAYA8; +#if CTX_GRADIENTS + case CTX_SOURCE_LINEAR_GRADIENT: return ctx_fragment_linear_gradient_GRAYA8; + case CTX_SOURCE_RADIAL_GRADIENT: return ctx_fragment_radial_gradient_GRAYA8; +#endif + } + return ctx_fragment_color_GRAYA8; +} + +ctx_u8_porter_duff(GRAYA8, 2,generic, rasterizer->fragment, rasterizer->state->gstate.blend_mode) + +#if CTX_INLINED_NORMAL +ctx_u8_porter_duff(GRAYA8, 2,generic_normal, rasterizer->fragment, CTX_BLEND_NORMAL) + +static void +ctx_GRAYA8_copy_normal (CTX_COMPOSITE_ARGUMENTS) +{ + ctx_u8_copy_normal (2, rasterizer, dst, src, x0, coverage, count); +} + +static void +ctx_GRAYA8_clear_normal (CTX_COMPOSITE_ARGUMENTS) +{ + ctx_u8_clear_normal (2, rasterizer, dst, src, x0, coverage, count); +} + +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); +#else + uint8_t tsrc[5]; + *((uint32_t*)tsrc) = *((uint32_t*)src); + + while (count--) + { + uint32_t cov = *coverage++; + uint32_t common =(((((255+(tsrc[1] * cov))>>8))^255 )); + dst[0] = ((((tsrc[0] * cov)) + (dst[0] * common ))>>8); + dst[1] = ((((tsrc[1] * cov)) + (dst[1] * common ))>>8); + dst+=2; + } +#endif +} + +static void +ctx_GRAYA8_source_copy_normal_color (CTX_COMPOSITE_ARGUMENTS) +{ + ctx_u8_source_copy_normal_color (2, rasterizer, dst, rasterizer->color, x0, coverage, count); +} +#endif + +inline static int +ctx_is_opaque_color (CtxRasterizer *rasterizer) +{ + CtxGState *gstate = &rasterizer->state->gstate; + if (gstate->global_alpha_u8 != 255) + return 0; + if (gstate->source_fill.type == CTX_SOURCE_COLOR) + { + uint8_t ga[2]; + ctx_color_get_graya_u8 (rasterizer->state, &gstate->source_fill.color, ga); + return ga[1] == 255; + } + return 0; +} + +static void +ctx_setup_GRAYA8 (CtxRasterizer *rasterizer) +{ + CtxGState *gstate = &rasterizer->state->gstate; + int components = 2; + rasterizer->fragment = ctx_rasterizer_get_fragment_GRAYA8 (rasterizer); + rasterizer->comp_op = ctx_GRAYA8_porter_duff_generic; + rasterizer->comp = CTX_COV_PATH_FALLBACK; + if (gstate->source_fill.type == CTX_SOURCE_COLOR) + { + ctx_fragment_color_GRAYA8 (rasterizer, 0,0, 1,rasterizer->color, 1, 0,0,0); + if (gstate->global_alpha_u8 != 255) + for (int c = 0; c < components; c ++) + rasterizer->color[c] = (rasterizer->color[c] * gstate->global_alpha_u8)/255; + + if (rasterizer->format->from_comp) + rasterizer->format->from_comp (rasterizer, 0, + &rasterizer->color[0], + &rasterizer->color_native, + 1); + } + +#if CTX_INLINED_NORMAL + if (gstate->compositing_mode == CTX_COMPOSITE_CLEAR) + rasterizer->comp_op = ctx_GRAYA8_clear_normal; + else + switch (gstate->blend_mode) + { + case CTX_BLEND_NORMAL: + if (gstate->compositing_mode == CTX_COMPOSITE_COPY) + { + rasterizer->comp_op = ctx_GRAYA8_copy_normal; + rasterizer->comp = CTX_COV_PATH_GRAYA8_COPY; + } + else if (gstate->global_alpha_u8 == 0) + rasterizer->comp_op = ctx_RGBA8_nop; + else + switch (gstate->source_fill.type) + { + case CTX_SOURCE_COLOR: + if (gstate->compositing_mode == CTX_COMPOSITE_SOURCE_OVER) + { + if (rasterizer->color[components-1] == 0) + rasterizer->comp_op = ctx_RGBA8_nop; + else if (rasterizer->color[components-1] == 255) + { + rasterizer->comp_op = ctx_GRAYA8_source_copy_normal_color; + rasterizer->comp = CTX_COV_PATH_GRAYA8_COPY; + } + else + rasterizer->comp_op = ctx_GRAYA8_source_over_normal_color; + } + else + { + rasterizer->comp_op = ctx_GRAYA8_porter_duff_generic_normal; + } + break; + default: + rasterizer->comp_op = ctx_GRAYA8_porter_duff_generic_normal; + break; + } + break; + default: + rasterizer->comp_op = ctx_GRAYA8_porter_duff_generic; + break; + } +#else + 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) + { + rasterizer->comp = CTX_COV_PATH_GRAYA8_COPY; + } + } +#endif + ctx_setup_apply_coverage (rasterizer); +} + +#if CTX_ENABLE_GRAY4 +static void +ctx_setup_GRAY4 (CtxRasterizer *rasterizer) +{ + ctx_setup_GRAYA8 (rasterizer); + if (rasterizer->comp == CTX_COV_PATH_GRAYA8_COPY) + rasterizer->comp = CTX_COV_PATH_GRAY4_COPY; + else + rasterizer->comp = CTX_COV_PATH_FALLBACK; +} +#endif + +#if CTX_ENABLE_GRAY2 +static void +ctx_setup_GRAY2 (CtxRasterizer *rasterizer) +{ + ctx_setup_GRAYA8 (rasterizer); + if (rasterizer->comp == CTX_COV_PATH_GRAYA8_COPY) + rasterizer->comp = CTX_COV_PATH_GRAY2_COPY; + else + rasterizer->comp = CTX_COV_PATH_FALLBACK; +} +#endif + +#if CTX_ENABLE_GRAY1 +static void +ctx_setup_GRAY1 (CtxRasterizer *rasterizer) +{ + ctx_setup_GRAYA8 (rasterizer); + if (rasterizer->comp == CTX_COV_PATH_GRAYA8_COPY) + rasterizer->comp = CTX_COV_PATH_GRAY1_COPY; + else + rasterizer->comp = CTX_COV_PATH_FALLBACK; +} +#endif + +static void +ctx_setup_GRAY8 (CtxRasterizer *rasterizer) +{ + ctx_setup_GRAYA8 (rasterizer); + if (rasterizer->comp == CTX_COV_PATH_GRAYA8_COPY) + rasterizer->comp = CTX_COV_PATH_GRAY8_COPY; + else + rasterizer->comp = CTX_COV_PATH_FALLBACK; +} + +#endif + +#endif + +inline static void +ctx_332_unpack (uint8_t pixel, + uint8_t *red, + uint8_t *green, + uint8_t *blue) +{ + *green = (((pixel >> 2) & 7)*255)/7; + *red = (((pixel >> 5) & 7)*255)/7; + *blue = ((((pixel & 3) << 1) | ((pixel >> 2) & 1))*255)/7; +} + +static inline uint8_t +ctx_332_pack (uint8_t red, + uint8_t green, + uint8_t blue) +{ + return ((ctx_sadd8(red,15) >> 5) << 5) + |((ctx_sadd8(green,15) >> 5) << 2) + |(ctx_sadd8(blue,15) >> 6); +} +#if CTX_ENABLE_RGB332 + +static inline uint8_t +ctx_888_to_332 (uint32_t in) +{ + uint8_t *rgb=(uint8_t*)(&in); + return ctx_332_pack (rgb[0],rgb[1],rgb[2]); +} + +static inline uint32_t +ctx_332_to_888 (uint8_t in) +{ + uint32_t ret = 0; + uint8_t *rgba=(uint8_t*)&ret; + ctx_332_unpack (in, + &rgba[0], + &rgba[1], + &rgba[2]); + rgba[3] = 255; + return ret; +} + +static inline void +ctx_RGB332_to_RGBA8 (CtxRasterizer *rasterizer, int x, const void *buf, uint8_t *rgba, int count) +{ + const uint8_t *pixel = (uint8_t *) buf; + while (count--) + { + ctx_332_unpack (*pixel, &rgba[0], &rgba[1], &rgba[2]); +#if CTX_RGB332_ALPHA + if (rgba[0]==255 && rgba[2] == 255 && rgba[1]==0) + { rgba[3] = 0; } + else +#endif + { rgba[3] = 255; } + pixel+=1; + rgba +=4; + } +} + +static inline void +ctx_RGBA8_to_RGB332 (CtxRasterizer *rasterizer, int x, const uint8_t *rgba, void *buf, int count) +{ + uint8_t *pixel = (uint8_t *) buf; + while (count--) + { +#if CTX_RGB332_ALPHA + if (rgba[3]==0) + { pixel[0] = ctx_332_pack (255, 0, 255); } + else +#endif + { pixel[0] = ctx_332_pack (rgba[0], rgba[1], rgba[2]); } + pixel+=1; + rgba +=4; + } +} + +static void +ctx_composite_RGB332 (CTX_COMPOSITE_ARGUMENTS) +{ +#if 1 + if (CTX_LIKELY(rasterizer->comp_op == ctx_RGBA8_source_over_normal_color)) + { + uint32_t si_ga = ((uint32_t*)rasterizer->color)[1]; + uint32_t si_rb = ((uint32_t*)rasterizer->color)[2]; + uint32_t si_a = si_ga >> 16; + + while (count--) + { + uint32_t cov = *coverage++; + uint32_t rcov = (((255+si_a * cov)>>8))^255; + uint32_t di = ctx_332_to_888 (*((uint8_t*)dst)); + uint32_t di_ga = ((di & 0xff00ff00) >> 8); + uint32_t di_rb = (di & 0x00ff00ff); + *((uint8_t*)(dst)) = + ctx_888_to_332((((si_rb * cov + 0xff00ff + di_rb * rcov) & 0xff00ff00) >> 8) | + ((si_ga * cov + 0xff00ff + di_ga * rcov) & 0xff00ff00)); + dst+=1; + } + return; + } +#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); + ctx_RGBA8_to_RGB332 (rasterizer, x0, &pixels[0], dst, count); +} + +#endif +static inline uint16_t +ctx_565_pack (uint8_t red, + uint8_t green, + uint8_t blue, + const int byteswap) +{ +#if 0 + // is this extra precision warranted? + // for 332 it gives more pure white.. + // it might be the case also for generic 565 + red = ctx_sadd8 (red, 4); + green = ctx_sadd8 (green, 3); + blue = ctx_sadd8 (blue, 4); +#endif + + uint32_t c = (red >> 3) << 11; + c |= (green >> 2) << 5; + c |= blue >> 3; + if (byteswap) + { return (c>>8) | (c<<8); } /* swap bytes */ + return c; +} + +#if CTX_ENABLE_RGB565 | CTX_ENABLE_RGB565_BYTESWAPPED + +static inline void +ctx_565_unpack (const uint16_t pixel, + uint8_t *red, + uint8_t *green, + uint8_t *blue, + const int byteswap) +{ + uint16_t byteswapped; + if (byteswap) + { byteswapped = (pixel>>8) | (pixel<<8); } + else + { byteswapped = pixel; } + uint8_t b = (byteswapped & 31) <<3; + uint8_t g = ( (byteswapped>>5) & 63) <<2; + uint8_t r = ( (byteswapped>>11) & 31) <<3; + +#if 0 + *blue = (b > 248) * 255 + (b <= 248) * b; + *green = (g > 248) * 255 + (g <= 248) * g; + *red = (r > 248) * 255 + (r <= 248) * r; +#else + *blue = b; + *green = g; + *red = r; +#endif +} + +static inline uint32_t +ctx_565_unpack_32 (const uint16_t pixel, + const int byteswap) +{ + uint16_t byteswapped; + if (byteswap) + { byteswapped = (pixel>>8) | (pixel<<8); } + else + { byteswapped = pixel; } + uint32_t b = (byteswapped & 31) <<3; + uint32_t g = ( (byteswapped>>5) & 63) <<2; + uint32_t r = ( (byteswapped>>11) & 31) <<3; +#if 0 + b = (b > 248) * 255 + (b <= 248) * b; + g = (g > 248) * 255 + (g <= 248) * g; + r = (r > 248) * 255 + (r <= 248) * r; +#endif + + return r + (g << 8) + (b << 16) + (0xff << 24); +} + + +static inline uint16_t +ctx_888_to_565 (uint32_t in, int byteswap) +{ + uint8_t *rgb=(uint8_t*)(&in); + return ctx_565_pack (rgb[0],rgb[1],rgb[2], byteswap); +} + +static inline uint32_t +ctx_565_to_888 (uint16_t in, int byteswap) +{ + uint32_t ret = 0; + uint8_t *rgba=(uint8_t*)&ret; + ctx_565_unpack (in, + &rgba[0], + &rgba[1], + &rgba[2], + byteswap); + //rgba[3]=255; + return ret; +} + +#endif +#if CTX_ENABLE_RGB565 + + +static inline void +ctx_RGB565_to_RGBA8 (CtxRasterizer *rasterizer, int x, const void *buf, uint8_t *rgba, int count) +{ + const uint16_t *pixel = (uint16_t *) buf; + while (count--) + { + // 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) + { rgba[3] = 0; } +#endif + pixel+=1; + rgba +=4; + } +} + +static inline void +ctx_RGBA8_to_RGB565 (CtxRasterizer *rasterizer, int x, const uint8_t *rgba, void *buf, int count) +{ + uint16_t *pixel = (uint16_t *) buf; + while (count--) + { +#if CTX_RGB565_ALPHA + if (rgba[3]==0) + { pixel[0] = ctx_565_pack (255, 0, 255, 0); } + else +#endif + { pixel[0] = ctx_565_pack (rgba[0], rgba[1], rgba[2], 0); } + pixel+=1; + rgba +=4; + } +} + +static void +ctx_RGBA8_source_over_normal_color (CTX_COMPOSITE_ARGUMENTS); +static void +ctx_RGBA8_source_copy_normal_color (CTX_COMPOSITE_ARGUMENTS); + +static void +ctx_composite_RGB565 (CTX_COMPOSITE_ARGUMENTS) +{ +#if 1 + if (CTX_LIKELY(rasterizer->comp_op == ctx_RGBA8_source_over_normal_color)) + { + uint32_t si_ga = ((uint32_t*)rasterizer->color)[1]; + uint32_t si_rb = ((uint32_t*)rasterizer->color)[2]; + uint32_t si_a = si_ga >> 16; + + while (count--) + { + uint32_t cov = *coverage++; + uint32_t rcov = (((255+si_a * cov)>>8))^255; + uint32_t di = ctx_565_to_888 (*((uint16_t*)dst), 0); + uint32_t di_ga = ((di & 0xff00ff00) >> 8); + uint32_t di_rb = (di & 0x00ff00ff); + *((uint16_t*)(dst)) = + ctx_888_to_565((((si_rb * cov + 0xff00ff + di_rb * rcov) & 0xff00ff00) >> 8) | + ((si_ga * cov + 0xff00ff + di_ga * rcov) & 0xff00ff00), 0); + dst+=2; + } + return; + } +#endif + + uint8_t pixels[count * 4]; + ctx_RGB565_to_RGBA8 (rasterizer, x0, dst, &pixels[0], count); + rasterizer->comp_op (rasterizer, &pixels[0], rasterizer->color, x0, coverage, count); + ctx_RGBA8_to_RGB565 (rasterizer, x0, &pixels[0], dst, count); +} +#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; + } +} + +static void +ctx_composite_RGB565_BS (CTX_COMPOSITE_ARGUMENTS) +{ +#if 1 + if (CTX_LIKELY(rasterizer->comp_op == ctx_RGBA8_source_over_normal_color)) + { + uint32_t si_ga = ((uint32_t*)rasterizer->color)[1]; + uint32_t si_rb = ((uint32_t*)rasterizer->color)[2]; + uint32_t si_a = si_ga >> 16; + + while (count--) + { + uint32_t cov = *coverage++; + uint32_t rcov = (((255+si_a * cov)>>8))^255; + uint32_t di = ctx_565_to_888 (*((uint16_t*)dst), 1); + uint32_t di_ga = ((di & 0xff00ff00) >> 8); + uint32_t di_rb = (di & 0x00ff00ff); + *((uint16_t*)(dst)) = + ctx_888_to_565((((si_rb * cov + 0xff00ff + di_rb * rcov) & 0xff00ff00) >> 8) | + ((si_ga * cov + 0xff00ff + di_ga * rcov) & 0xff00ff00), 1); + dst+=2; + } + return; + } +#endif + + uint8_t pixels[count * 4]; + ctx_RGB565_BS_to_RGBA8 (rasterizer, x0, dst, &pixels[0], count); + rasterizer->comp_op (rasterizer, &pixels[0], rasterizer->color, x0, coverage, count); + ctx_RGBA8_to_RGB565_BS (rasterizer, x0, &pixels[0], dst, count); +} +#endif + + +static inline uint32_t +ctx_over_RGBA8 (uint32_t dst, uint32_t src, uint32_t cov) +{ + uint32_t si_ga = (src & 0xff00ff00) >> 8; + uint32_t si_rb = src & 0x00ff00ff; + uint32_t si_a = si_ga >> 16; + uint32_t rcov = ((255+si_a * cov)>>8)^255; + uint32_t di_ga = ( dst & 0xff00ff00) >> 8; + uint32_t di_rb = dst & 0x00ff00ff; + return + ((((si_rb * cov) + 0xff00ff + (di_rb * rcov)) & 0xff00ff00) >> 8) | + (((si_ga * cov) + 0xff00ff + (di_ga * rcov)) & 0xff00ff00); +} + + +static inline uint32_t +ctx_over_RGBA8_full (uint32_t dst, uint32_t src) +{ + uint32_t si_ga = (src & 0xff00ff00) >> 8; + uint32_t si_rb = src & 0x00ff00ff; + uint32_t si_a = si_ga >> 16; + uint32_t rcov = si_a^255; + uint32_t di_ga = (dst & 0xff00ff00) >> 8; + uint32_t di_rb = dst & 0x00ff00ff; + return + ((((si_rb * 255) + 0xff00ff + (di_rb * rcov)) & 0xff00ff00) >> 8) | + (((si_ga * 255) + 0xff00ff + (di_ga * rcov)) & 0xff00ff00); +} + +static inline uint32_t +ctx_over_RGBA8_2 (uint32_t dst, uint32_t si_ga, uint32_t si_rb, uint32_t si_a, uint32_t cov) +{ + uint32_t rcov = ((si_a * cov)/255)^255; + uint32_t di_ga = (dst & 0xff00ff00) >> 8; + uint32_t di_rb = dst & 0x00ff00ff; + return + ((((si_rb * cov) + 0xff00ff + (di_rb * rcov)) & 0xff00ff00) >> 8) | + (((si_ga * cov) + 0xff00ff + (di_ga * rcov)) & 0xff00ff00); +} + +static inline uint32_t +ctx_over_RGBA8_full_2 (uint32_t dst, uint32_t si_ga_full, uint32_t si_rb_full, uint32_t si_a) +{ + uint32_t rcov = si_a^255; + uint32_t di_ga = ( dst & 0xff00ff00) >> 8; + uint32_t di_rb = dst & 0x00ff00ff; + return + ((((si_rb_full) + (di_rb * rcov)) & 0xff00ff00) >> 8) | + (((si_ga_full) + (di_ga * rcov)) & 0xff00ff00); +} + +static inline void ctx_span_set_color (uint32_t *dst_pix, uint32_t val, int count) +{ + if (count>0) + while(count--) + *dst_pix++=val; +} + +static inline void ctx_span_set_colorb (uint32_t *dst_pix, uint32_t val, int count) +{ + while(count--) + *dst_pix++=val; +} + +static inline void ctx_span_set_colorbu (uint32_t *dst_pix, uint32_t val, unsigned int count) +{ + while(count--) + *dst_pix++=val; +} + +static inline void ctx_span_set_color_x4 (uint32_t *dst_pix, uint32_t *val, int count) +{ + if (count>0) + while(count--) + { + *dst_pix++=val[0]; + *dst_pix++=val[1]; + *dst_pix++=val[2]; + *dst_pix++=val[3]; + } +} + +#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 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) +{ + 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); + + uint32_t *dst = ( (uint32_t *) rasterizer->buf); + int blit_stride = rasterizer->blit_stride/4; + dst += (y0 - rasterizer->blit_y) * blit_stride; + dst += (x0); + + 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; + int bwidth = buffer->width; + int bheight = buffer->height; + int u = x0;// + 0.5f; + int v = y0;// + 0.5f; + + uint32_t *src = ((uint32_t*)buffer->data) + bwidth * v + u; + + int pre = ctx_mini(ctx_maxi(-u,0), width); + + width-=pre; + u+=pre; + + int core = ctx_mini (width, bwidth - u); + + if (copy) + { + if (core>0) + { + uint32_t *t_dst = dst; + for (unsigned int y = 0; y < height; y++) + { + if (CTX_LIKELY((v >= 0 && v < bheight))) + { + memcpy (t_dst, src + pre, 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++) + { + 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); + } + v++; + src += bwidth; + t_dst += blit_stride; + } + } + } +} +#endif + + +static void +ctx_composite_fill_rect_aligned (CtxRasterizer *rasterizer, + int x0, + int y0, + int x1, + int y1, + uint8_t cov) +{ + int blit_x = rasterizer->blit_x; + int blit_y = rasterizer->blit_y; + int blit_width = rasterizer->blit_width; + int blit_height = rasterizer->blit_height; + int blit_stride = rasterizer->blit_stride; + + x0 = ctx_maxi (x0, blit_x); + x1 = ctx_mini (x1, blit_x + blit_width - 1); + 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; + // + if (CTX_UNLIKELY (width <=0 || height <= 0)) + return; + + CtxCovPath comp = rasterizer->comp; + uint8_t *dst; + + // this could be done here, but is not used + // by a couple of the cases +#define INIT_ENV do {\ + rasterizer->scanline = y0 * CTX_FULL_AA; \ + dst = ( (uint8_t *) rasterizer->buf); \ + 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)); + INIT_ENV; + if (CTX_UNLIKELY(width == 1)) + { + for (unsigned int y = y0; y <= (unsigned)y1; y++) + { + uint32_t *dst_i = (uint32_t*)&dst[0]; + *dst_i = color; + dst += blit_stride; + } + } + else + { + 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; + } + } + return; + } + case CTX_COV_PATH_RGBAF_COPY: + case CTX_COV_PATH_GRAY8_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_RGB8_COPY: + case CTX_COV_PATH_CMYK8_COPY: + case CTX_COV_PATH_CMYKA8_COPY: + { + uint8_t *color = (uint8_t*)&rasterizer->color_native; + unsigned int bytes = rasterizer->format->bpp/8; + INIT_ENV; + + switch (bytes) + { + case 1: + { + uint8_t col = *color; + if (width == 1) + for (unsigned int y = y0; y <= (unsigned)y1; y++) + { + *dst = col; + dst += blit_stride; + } + else + for (unsigned int y = y0; y <= (unsigned)y1; y++) + { +#if 0 + uint8_t *dst_i = (uint8_t*)&dst[0]; + for (int x = 0; x < width; x++) *dst_i++ = col; +#else + memset (dst, col, width); +#endif + dst += blit_stride; + } + } + break; + case 2: + { + uint16_t val = ((uint16_t*)color)[0]; + for (unsigned int y = y0; y <= (unsigned)y1; y++) + { + uint16_t *dst_i = (uint16_t*)&dst[0]; + for (int x = 0; x < width; x++) + *dst_i++ = val; + dst += blit_stride; + } + } + break; + case 3: + for (unsigned int y = y0; y <= (unsigned)y1; y++) + { + uint8_t *dst_i = (uint8_t*)&dst[0]; + for (int x = 0; x < width; x++) + for (unsigned int b = 0; b < 3; b++) *dst_i++ = color[b]; + dst += blit_stride; + } + break; + case 4: + { + uint32_t val = ((uint32_t*)color)[0]; + if (width == 1) + for (unsigned int y = y0; y <= (unsigned)y1; y++) + { + *((uint32_t*)&dst[0]) = val; + dst += blit_stride; + } + else + for (unsigned int y = y0; y <= (unsigned)y1; y++) + { + //uint32_t *dst_i = (uint32_t*)&dst[0]; + ctx_span_set_colorbu ((uint32_t*)&dst[0], val, width); + dst += blit_stride; + } + } + break; + case 5: + for (unsigned int y = y0; y <= (unsigned)y1; y++) + { + uint8_t *dst_i = (uint8_t*)&dst[0]; + for (int x = 0; x < width; x++) + for (unsigned int b = 0; b < 5; b++) *dst_i++ = color[b]; + dst += blit_stride; + } + break; + case 16: + for (unsigned int y = y0; y <= (unsigned)y1; y++) + { + uint8_t *dst_i = (uint8_t*)&dst[0]; + for (int x = 0; x < width; x++)for (unsigned int b = 0; b < 16; b++) *dst_i++ = color[b]; + dst += blit_stride; + } + break; + default: + for (unsigned int y = y0; y <= (unsigned)y1; y++) + { + uint8_t *dst_i = (uint8_t*)&dst[0]; + for (int x = 0; x < width; x++) + for (unsigned int b = 0; b < bytes; b++) + *dst_i++ = color[b]; + dst += blit_stride; + } + } + return; + } + case CTX_COV_PATH_RGBA8_OVER: + { + uint32_t si_ga_full = ((uint32_t*)rasterizer->color)[3]; + uint32_t si_rb_full = ((uint32_t*)rasterizer->color)[4]; + uint32_t si_a = rasterizer->color[3]; + INIT_ENV; + + if (CTX_UNLIKELY(width == 1)) + { + for (unsigned int y = y0; y <= (unsigned)y1; y++) + { + ((uint32_t*)(dst))[0] = ctx_over_RGBA8_full_2 ( + ((uint32_t*)(dst))[0], si_ga_full, si_rb_full, si_a); + dst += blit_stride; + } + } + else + { + for (unsigned int y = y0; y <= (unsigned)y1; y++) + { + uint32_t *dst_i = (uint32_t*)&dst[0]; + for (int i = 0; i < width; i++) + { + dst_i[i] = ctx_over_RGBA8_full_2 (dst_i[i], si_ga_full, si_rb_full, si_a); + } + dst += blit_stride; + } + } + return; + } + case CTX_COV_PATH_RGBA8_COPY_FRAGMENT: + { + CtxFragment fragment = rasterizer->fragment; + CtxMatrix *transform = &rasterizer->state->gstate.source_fill.transform; + CtxExtend extend = rasterizer->state->gstate.extend; + INIT_ENV; + + if (fragment == ctx_fragment_image_rgba8_RGBA8_bi_scale) + { + ctx_RGBA8_image_rgba8_RGBA8_bi_scaled_fill_rect (rasterizer, x0, y0, x1, +y1, 1); + 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, 1); + return; + } + + if (CTX_LIKELY(ctx_matrix_no_perspective (transform))) + { + int scan = rasterizer->scanline/CTX_FULL_AA; + float u0, v0, ud, vd, w0, wd; + ctx_init_uv (rasterizer, x0, scan, &u0, &v0, &w0, &ud, &vd, &wd); + for (unsigned int y = y0; y <= (unsigned)y1; y++) + { + fragment (rasterizer, u0, v0, w0, &dst[0], width, ud, vd, wd); + u0 -= vd; + v0 += ud; + dst += blit_stride; + } + } + else + { + int scan = rasterizer->scanline/CTX_FULL_AA; + for (unsigned int y = y0; y <= (unsigned)y1; y++) + { + float u0, v0, ud, vd, w0, wd; + ctx_init_uv (rasterizer, x0, scan + y-y0, &u0, &v0, &w0, &ud, &vd, &wd); + fragment (rasterizer, u0, v0, w0, &dst[0], width, ud, vd, wd); + dst += blit_stride; + } + } + return; + } + case CTX_COV_PATH_RGBA8_OVER_FRAGMENT: + { + CtxFragment fragment = rasterizer->fragment; + CtxExtend extend = rasterizer->state->gstate.extend; +#if 0 + if (fragment == ctx_fragment_image_rgba8_RGBA8_nearest_copy) + { + ctx_RGBA8_image_rgba8_RGBA8_nearest_fill_rect_copy (rasterizer, x0, y0, x1, y1, 0); + return; + } + else +#endif + 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; + } + + INIT_ENV; + ctx_RGBA8_source_over_normal_full_cov_fragment (rasterizer, + &dst[0], NULL, x0, NULL, width, y1-y0+1); + return; + } + break; + default: + break; + } + } + else + { + switch (comp) + { + case CTX_COV_PATH_RGBA8_COPY: + { + uint32_t color; + memcpy (&color, (uint32_t*)rasterizer->color, sizeof (color)); + 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; + } + } + case CTX_COV_PATH_RGBAF_COPY: + { + float *color = ((float*)rasterizer->color); + float covf = cov / 255.0f; + INIT_ENV; + { + for (unsigned int y = y0; y <= (unsigned)y1; y++) + { + float *dst_f = (float*)&dst[0]; + for (unsigned int i = 0; i < (unsigned)width; i++) + { + for (unsigned int c = 0; c < 4; c++) + dst_f[i*4+c] = ctx_lerpf (dst_f[i*4+c], color[c], covf); + } + dst += blit_stride; + } + return; + } + } + case CTX_COV_PATH_RGBA8_OVER: + { + uint32_t color; + memcpy (&color, (uint32_t*)rasterizer->color, sizeof (color)); + INIT_ENV; + if (width == 1) + { + for (unsigned int y = y0; y <= (unsigned)y1; y++) + { + uint32_t *dst_i = (uint32_t*)&dst[0]; + *dst_i = ctx_over_RGBA8 (*dst_i, color, cov); + dst += blit_stride; + } + } + else + { + 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_over_RGBA8 (dst_i[i], color, cov); + } + dst += blit_stride; + } + } + return; + } + break; + default: + break; + } + } + + INIT_ENV; +#undef INIT_ENV + + + /* fallback */ + { + 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) = + rasterizer->apply_coverage; + + for (unsigned int y = y0; y <= (unsigned)y1; y++) + { + apply_coverage (rasterizer, &dst[0], rasterizer_src, x0, coverage, width); + rasterizer->scanline += CTX_FULL_AA; + dst += blit_stride; + } + } +} + +void +CTX_SIMD_SUFFIX (ctx_composite_fill_rect) (CtxRasterizer *rasterizer, + float x0, + float y0, + float x1, + float y1, + uint8_t cov); + +void +CTX_SIMD_SUFFIX (ctx_composite_fill_rect) (CtxRasterizer *rasterizer, + float x0, + float y0, + float x1, + 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)) + { + /* best-case scenario axis aligned rectangle */ + ctx_composite_fill_rect_aligned (rasterizer, (int)x0, (int)y0, (int)(x1-1), (int)(y1-1), 255); + return; + } + + int blit_x = rasterizer->blit_x; + int blit_y = rasterizer->blit_y; + int blit_stride = rasterizer->blit_stride; + 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) = + rasterizer->apply_coverage; + + x0 = ctx_maxf (x0, blit_x); + y0 = ctx_maxf (y0, blit_y); + x1 = ctx_minf (x1, blit_x + blit_width); + y1 = ctx_minf (y1, blit_y + blit_height); + + uint8_t left = (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); + + x0 = ctx_floorf (x0); + y0 = ctx_floorf (y0); + x1 = ctx_floorf (x1+7/8.0f); + y1 = ctx_floorf (y1+15/15.0f); + + int has_top = (top < 255); + int has_bottom = (bottom <255); + 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; + + int width = (int)(x1 - x0); + + if ((width >0)) + { + uint8_t *dst = ( (uint8_t *) rasterizer->buf); + uint8_t coverage[width+2]; + uint32_t x0i = (int)x0+has_left; + uint32_t x1i = (int)x1-has_right; + uint32_t y0i = (int)y0+has_top; + uint32_t y1i = (int)y1-has_bottom; + dst += (((int)y0) - blit_y) * blit_stride; + dst += ((int)x0) * rasterizer->format->bpp/8; + + if (has_top) + { + int i = 0; + if (has_left) + { + coverage[i++] = (top * left + 255) >> 8; + } + for (unsigned int x = x0i; x < x1i; x++) + coverage[i++] = top; + if (has_right) + coverage[i++]= (top * right + 255) >> 8; + + apply_coverage (rasterizer, dst, rasterizer_src, (int)x0, coverage, width); + dst += blit_stride; + } + + if (y1-y0-has_top-has_bottom > 0) + { + if (has_left) + ctx_composite_fill_rect_aligned (rasterizer, (int)x0, y0i, + (int)x0, y1i-1, left); + if (has_right) + ctx_composite_fill_rect_aligned (rasterizer, (int)x1-1, y0i, + (int)x1-1, y1i-1, right); + + if (width - has_left - has_right > 0) + ctx_composite_fill_rect_aligned (rasterizer, x0i,y0i, + x1i-1,y1i-1,255); + + dst += blit_stride * (y1i-y0i); + } + if (has_bottom) + { + int i = 0; + if (has_left) + coverage[i++] = (bottom * left + 255) >> 8; + for (unsigned int x = x0i; x < x1i; x++) + coverage[i++] = bottom; + coverage[i++]= (bottom * right + 255) >> 8; + + apply_coverage (rasterizer,dst, rasterizer_src, (int)x0, coverage, width); + } + } +} + +void +CTX_SIMD_SUFFIX(ctx_composite_stroke_rect) (CtxRasterizer *rasterizer, + float x0, + float y0, + float x1, + float y1, + float line_width); + +void +CTX_SIMD_SUFFIX(ctx_composite_stroke_rect) (CtxRasterizer *rasterizer, + float x0, + float y0, + float x1, + float y1, + float line_width) +{ + float lwmod = ctx_fmod1f (line_width); + int lw = (int)ctx_floorf (line_width + 0.5f); + int is_compat_even = (lw % 2 == 0) && (lwmod < 0.1f); // only even linewidths implemented properly + int is_compat_odd = (lw % 2 == 1) && (lwmod < 0.1f); // only even linewidths implemented properly + + float off_x = 0; + float off_y = 0; + + if (is_compat_odd) + { + off_x = 0.5f; + off_y = (CTX_FULL_AA/2)*1.0f / (CTX_FULL_AA); + } + + 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 bw = lw/2+1; + int bwb = lw/2; + + if (is_compat_even) + { + bw = lw/2; + } + /* top */ + ctx_composite_fill_rect_aligned (rasterizer, + (int)x0-bwb, (int)y0-bwb, + (int)x1+bw-1, (int)y0+bw-1, 255); + /* bottom */ + ctx_composite_fill_rect_aligned (rasterizer, + (int)x0-bwb, (int)y1-bwb, + (int)x1-bwb-1, (int)y1+bw-1, 255); + + /* left */ + ctx_composite_fill_rect_aligned (rasterizer, + (int)x0-bwb, (int)y0+1, + (int)x0+bw-1, (int)y1-bwb, 255); + /* right */ + ctx_composite_fill_rect_aligned (rasterizer, + (int)x1-bwb, (int)y0+1, + (int)x1+bw-1, (int)y1+bw-1, 255); + } + else + { + float hw = line_width/2; + + + /* top */ + ctx_composite_fill_rect (rasterizer, + x0+hw, y0-hw, + x1-hw, y0+hw, 255); + /* bottom */ + ctx_composite_fill_rect (rasterizer, + x0+hw, y1-hw, + x1-hw, y1+hw, 255); + + /* left */ + ctx_composite_fill_rect (rasterizer, + x0-hw, y0+hw, + x0+hw, y1-hw, 255); + /* right */ + + ctx_composite_fill_rect (rasterizer, + x1-hw, y0+hw, + x1+hw, y1-hw, 255); + + /* corners */ + + ctx_composite_fill_rect (rasterizer, + x0-hw, y0-hw, + x0+hw, y0+hw, 255); + ctx_composite_fill_rect (rasterizer, + x1-hw, y1-hw, + x1+hw, y1+hw, 255); + ctx_composite_fill_rect (rasterizer, + x1-hw, y0-hw, + x1+hw, y0+hw, 255); + ctx_composite_fill_rect (rasterizer, + x0-hw, y1-hw, + x0+hw, y1+hw, 255); + } +} + + +#endif + +static void +CTX_SIMD_SUFFIX (ctx_composite_setup) (CtxRasterizer *rasterizer) +{ + if (CTX_UNLIKELY (rasterizer->comp_op==NULL)) + { +#if CTX_GRADIENTS +#if CTX_GRADIENT_CACHE + switch (rasterizer->state->gstate.source_fill.type) + { + case CTX_SOURCE_LINEAR_GRADIENT: + case CTX_SOURCE_RADIAL_GRADIENT: + ctx_gradient_cache_prime (rasterizer); + break; + case CTX_SOURCE_TEXTURE: + + _ctx_matrix_multiply (&rasterizer->state->gstate.source_fill.transform, + &rasterizer->state->gstate.source_fill.set_transform, + &rasterizer->state->gstate.transform); +#if 0 + rasterizer->state->gstate.source_fill.transform_inv = + rasterizer->state->gstate.source_fill.transform; +#endif + ctx_matrix_invert (&rasterizer->state->gstate.source_fill.transform); + +#if 0 + 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 + break; + } +#endif +#endif + } + rasterizer->format->setup (rasterizer); + +} + + +CtxPixelFormatInfo CTX_SIMD_SUFFIX(ctx_pixel_formats)[]= +{ +#if CTX_ENABLE_RGBA8 + { + CTX_FORMAT_RGBA8, 4, 32, 4, 0, 0, CTX_FORMAT_RGBA8, + NULL, NULL, NULL, ctx_setup_RGBA8 + }, +#endif +#if CTX_ENABLE_BGRA8 + { + CTX_FORMAT_BGRA8, 4, 32, 4, 0, 0, CTX_FORMAT_RGBA8, + ctx_BGRA8_to_RGBA8, ctx_RGBA8_to_BGRA8, ctx_composite_BGRA8, ctx_setup_RGBA8, + }, +#endif +#if CTX_ENABLE_GRAYF + { + CTX_FORMAT_GRAYF, 1, 32, 4 * 2, 0, 0, CTX_FORMAT_GRAYAF, + NULL, NULL, ctx_composite_GRAYF, ctx_setup_GRAYAF, + }, +#endif +#if CTX_ENABLE_GRAYAF + { + CTX_FORMAT_GRAYAF, 2, 64, 4 * 2, 0, 0, CTX_FORMAT_GRAYAF, + NULL, NULL, NULL, ctx_setup_GRAYAF, + }, +#endif +#if CTX_ENABLE_RGBAF + { + CTX_FORMAT_RGBAF, 4, 128, 4 * 4, 0, 0, CTX_FORMAT_RGBAF, + NULL, NULL, NULL, ctx_setup_RGBAF, + }, +#endif +#if CTX_ENABLE_RGB8 + { + CTX_FORMAT_RGB8, 3, 24, 4, 0, 0, CTX_FORMAT_RGBA8, + ctx_RGB8_to_RGBA8, ctx_RGBA8_to_RGB8, ctx_composite_convert, ctx_setup_RGB8, + }, +#endif +#if CTX_ENABLE_GRAY1 + { +#if CTX_NATIVE_GRAYA8 + CTX_FORMAT_GRAY1, 1, 1, 2, 1, 1, CTX_FORMAT_GRAYA8, + ctx_GRAY1_to_GRAYA8, ctx_GRAYA8_to_GRAY1, ctx_composite_convert, ctx_setup_GRAY1, +#else + CTX_FORMAT_GRAY1, 1, 1, 4, 1, 1, CTX_FORMAT_RGBA8, + ctx_GRAY1_to_RGBA8, ctx_RGBA8_to_GRAY1, ctx_composite_convert, ctx_setup_RGB, +#endif + }, +#endif +#if CTX_ENABLE_GRAY2 + { +#if CTX_NATIVE_GRAYA8 + CTX_FORMAT_GRAY2, 1, 2, 2, 4, 4, CTX_FORMAT_GRAYA8, + ctx_GRAY2_to_GRAYA8, ctx_GRAYA8_to_GRAY2, ctx_composite_convert, ctx_setup_GRAY2, +#else + CTX_FORMAT_GRAY2, 1, 2, 4, 4, 4, CTX_FORMAT_RGBA8, + ctx_GRAY2_to_RGBA8, ctx_RGBA8_to_GRAY2, ctx_composite_convert, ctx_setup_RGB, +#endif + }, +#endif +#if CTX_ENABLE_GRAY4 + { +#if CTX_NATIVE_GRAYA8 + CTX_FORMAT_GRAY4, 1, 4, 2, 16, 16, CTX_FORMAT_GRAYA8, + ctx_GRAY4_to_GRAYA8, ctx_GRAYA8_to_GRAY4, ctx_composite_convert, ctx_setup_GRAY4, +#else + CTX_FORMAT_GRAY4, 1, 4, 4, 16, 16, CTX_FORMAT_GRAYA8, + ctx_GRAY4_to_RGBA8, ctx_RGBA8_to_GRAY4, ctx_composite_convert, ctx_setup_RGB, +#endif + }, +#endif +#if CTX_ENABLE_GRAY8 + { +#if CTX_NATIVE_GRAYA8 + CTX_FORMAT_GRAY8, 1, 8, 2, 0, 0, CTX_FORMAT_GRAYA8, + ctx_GRAY8_to_GRAYA8, ctx_GRAYA8_to_GRAY8, ctx_composite_convert, ctx_setup_GRAY8, +#else + CTX_FORMAT_GRAY8, 1, 8, 4, 0, 0, CTX_FORMAT_RGBA8, + ctx_GRAY8_to_RGBA8, ctx_RGBA8_to_GRAY8, ctx_composite_convert, ctx_setup_RGB, +#endif + }, +#endif +#if CTX_ENABLE_GRAYA8 + { +#if CTX_NATIVE_GRAYA8 + CTX_FORMAT_GRAYA8, 2, 16, 2, 0, 0, CTX_FORMAT_GRAYA8, + ctx_GRAYA8_to_RGBA8, ctx_RGBA8_to_GRAYA8, NULL, ctx_setup_GRAYA8, +#else + CTX_FORMAT_GRAYA8, 2, 16, 4, 0, 0, CTX_FORMAT_RGBA8, + ctx_GRAYA8_to_RGBA8, ctx_RGBA8_to_GRAYA8, ctx_composite_convert, ctx_setup_RGB, +#endif + }, +#endif +#if CTX_ENABLE_RGB332 + { + CTX_FORMAT_RGB332, 3, 8, 4, 12, 12, CTX_FORMAT_RGBA8, + ctx_RGB332_to_RGBA8, ctx_RGBA8_to_RGB332, + ctx_composite_RGB332, ctx_setup_RGB332, + }, +#endif +#if CTX_ENABLE_RGB565 + { + CTX_FORMAT_RGB565, 3, 16, 4, 16, 32, CTX_FORMAT_RGBA8, + ctx_RGB565_to_RGBA8, ctx_RGBA8_to_RGB565, + ctx_composite_RGB565, ctx_setup_RGB565, + }, +#endif +#if CTX_ENABLE_RGB565_BYTESWAPPED + { + CTX_FORMAT_RGB565_BYTESWAPPED, 3, 16, 4, 16, 32, CTX_FORMAT_RGBA8, + ctx_RGB565_BS_to_RGBA8, + ctx_RGBA8_to_RGB565_BS, + ctx_composite_RGB565_BS, ctx_setup_RGB565, + }, +#endif +#if CTX_ENABLE_CMYKAF + { + CTX_FORMAT_CMYKAF, 5, 160, 4 * 5, 0, 0, CTX_FORMAT_CMYKAF, + NULL, NULL, NULL, ctx_setup_CMYKAF, + }, +#endif +#if CTX_ENABLE_CMYKA8 + { + CTX_FORMAT_CMYKA8, 5, 40, 4 * 5, 0, 0, CTX_FORMAT_CMYKAF, + NULL, NULL, ctx_composite_CMYKA8, ctx_setup_CMYKA8, + }, +#endif +#if CTX_ENABLE_CMYK8 + { + CTX_FORMAT_CMYK8, 5, 32, 4 * 5, 0, 0, CTX_FORMAT_CMYKAF, + NULL, NULL, ctx_composite_CMYK8, ctx_setup_CMYK8, + }, +#endif +#if CTX_ENABLE_YUV420 + { + CTX_FORMAT_YUV420, 1, 8, 4, 0, 0, CTX_FORMAT_RGBA8, + NULL, NULL, ctx_composite_convert, ctx_setup_RGB, + }, +#endif + { + CTX_FORMAT_NONE + } +}; + + + + +#endif // CTX_COMPOSITE + +#endif // CTX_IMPLEMENTATION + +#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) + +CTX_INLINE static int ctx_compare_edges (const void *ap, const void *bp) +{ + const CtxSegment *a = (const CtxSegment *) ap; + const CtxSegment *b = (const CtxSegment *) bp; + return a->data.s16[1] - b->data.s16[1]; +} + +CTX_INLINE static int ctx_edge_qsort_partition (CtxSegment *A, int low, int high) +{ + CtxSegment pivot = A[ (high+low) /2]; + int i = low; + int j = high; + while (i <= j) + { + while (ctx_compare_edges (&A[i], &pivot) < 0) { i ++; } + while (ctx_compare_edges (&pivot, &A[j]) < 0) { j --; } + if (i <= j) + { + 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); } +} + +static inline void ctx_rasterizer_sort_edges (CtxRasterizer *rasterizer) +{ + ctx_edge_qsort ((CtxSegment*)& (rasterizer->edge_list.entries[0]), 0, rasterizer->edge_list.count-1); +} + +static inline void ctx_rasterizer_discard_edges (CtxRasterizer *rasterizer) +{ + int scanline = rasterizer->scanline; + int next_scanline = scanline + CTX_FULL_AA; + int limit3 = CTX_RASTERIZER_AA_SLOPE_LIMIT3_FAST_AA; + CtxSegment *segments = &((CtxSegment*)(rasterizer->edge_list.entries))[0]; + int *edges = rasterizer->edges; + for (unsigned int i = 0; i < rasterizer->active_edges; i++) + { + CtxSegment *segment = segments + edges[i]; + int edge_end = segment->data.s16[3]-1; + if (edge_end < scanline) + { + + int dx_dy = abs(segment->delta); + rasterizer->needs_aa3 -= (dx_dy > limit3); + rasterizer->needs_aa5 -= (dx_dy > CTX_RASTERIZER_AA_SLOPE_LIMIT5); + rasterizer->needs_aa15 -= (dx_dy > CTX_RASTERIZER_AA_SLOPE_LIMIT15); + rasterizer->edges[i] = rasterizer->edges[rasterizer->active_edges-1]; + rasterizer->active_edges--; + i--; + } + else if (edge_end < next_scanline) + rasterizer->ending_edges++; + } +#if 0 + // perhaps we should - but for 99% of the cases we do not need to, so we skip it + for (int i = 0; i < rasterizer->pending_edges; i++) + { + int edge_end = ((CtxSegment*)(rasterizer->edge_list.entries))[rasterizer->edges[CTX_MAX_EDGES-1-i]].data.s16[3]-1; + if (edge_end < scanline + CTX_FULL_AA) + rasterizer->ending_edges++; + } +#endif +} + +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; + unsigned int pending_base = CTX_MAX_EDGES-pending_edges; + for (unsigned int i = 0; i < active_edges; i++) + { + CtxSegment *segment = segments + rasterizer->edges[i]; + segment->val += segment->delta * count; + } + for (unsigned int i = 0; i < pending_edges; i++) + { + CtxSegment *segment = segments + rasterizer->edges[pending_base+i]; + segment->val += segment->delta * count; + } +} + +/* 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) +{ + for(unsigned int i=1; i<count; i++) + { + int temp = entries[i]; + int j = i-1; + while (j >= 0 && segments[temp].val - segments[entries[j]].val < 0) + { + entries[j+1] = entries[j]; + j--; + } + entries[j+1] = temp; + } +} + +inline static int ctx_edge2_compare2 (CtxSegment *segments, int a, int b) +{ + CtxSegment *seg_a = &segments[a]; + CtxSegment *seg_b = &segments[b]; + int minval_a = ctx_mini (seg_a->val - seg_a->delta * CTX_AA_HALFSTEP2, seg_a->val + seg_a->delta * CTX_AA_HALFSTEP); + int minval_b = ctx_mini (seg_b->val - seg_b->delta * CTX_AA_HALFSTEP2, seg_b->val + seg_b->delta * CTX_AA_HALFSTEP); + return minval_a - minval_b; +} + +inline static void ctx_edge2_insertion_sort2 (CtxSegment *segments, int *entries, unsigned int count) +{ + for(unsigned int i=1; i<count; i++) + { + int temp = entries[i]; + int j = i-1; + while (j >= 0 && ctx_edge2_compare2 (segments, temp, entries[j]) < 0) + { + entries[j+1] = entries[j]; + j--; + } + entries[j+1] = temp; + } +} + +inline static void ctx_rasterizer_feed_edges (CtxRasterizer *rasterizer, int apply2_sort) +{ + int miny; + CtxSegment *__restrict__ entries = (CtxSegment*)&rasterizer->edge_list.entries[0]; + int *edges = rasterizer->edges; + unsigned int pending_edges = rasterizer->pending_edges; + rasterizer->horizontal_edges = 0; + rasterizer->ending_edges = 0; + for (unsigned int i = 0; i < pending_edges; i++) + { + if (entries[rasterizer->edges[CTX_MAX_EDGES-1-i]].data.s16[1] - 1 <= rasterizer->scanline && + rasterizer->active_edges < CTX_MAX_EDGES-2) + { + unsigned int no = rasterizer->active_edges; + rasterizer->active_edges++; + edges[no] = edges[CTX_MAX_EDGES-1-i]; + edges[CTX_MAX_EDGES-1-i] = + edges[CTX_MAX_EDGES-1-pending_edges + 1]; + pending_edges--; + i--; + } + } + int limit3 = CTX_RASTERIZER_AA_SLOPE_LIMIT3_FAST_AA; + int scanline = rasterizer->scanline; + int next_scanline = scanline + CTX_FULL_AA; + unsigned int edge_pos = rasterizer->edge_pos; + unsigned int edge_count = rasterizer->edge_list.count; + while ((edge_pos < edge_count && + (miny=entries[edge_pos].data.s16[1]-1) <= next_scanline)) + { + if (rasterizer->active_edges < CTX_MAX_EDGES-2 && + entries[edge_pos].data.s16[3]-1 /* (maxy) */ >= scanline) + { + int dy = (entries[edge_pos].data.s16[3] - 1 - miny); + if (dy) + { + int yd = scanline - miny; + unsigned int no = rasterizer->active_edges; + rasterizer->active_edges++; + unsigned int index = edges[no] = edge_pos; + int x0 = entries[index].data.s16[0]; + int x1 = entries[index].data.s16[2]; + int dx_dy = CTX_RASTERIZER_EDGE_MULTIPLIER * (x1 - x0) / dy; + entries[index].delta = dx_dy; + entries[index].val = x0 * CTX_RASTERIZER_EDGE_MULTIPLIER + + (yd * dx_dy); + + { + int abs_dx_dy = abs(dx_dy); + rasterizer->needs_aa3 += (abs_dx_dy > limit3); + rasterizer->needs_aa5 += (abs_dx_dy > CTX_RASTERIZER_AA_SLOPE_LIMIT5); + rasterizer->needs_aa15 += (abs_dx_dy > CTX_RASTERIZER_AA_SLOPE_LIMIT15); + } + + 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[no]; + pending_edges++; + rasterizer->active_edges--; + } + } + else + rasterizer->horizontal_edges ++; + } + edge_pos++; + } + rasterizer->pending_edges = pending_edges; + rasterizer->edge_pos = edge_pos; + ctx_rasterizer_discard_edges (rasterizer); + if (apply2_sort) + ctx_edge2_insertion_sort2 ((CtxSegment*)rasterizer->edge_list.entries, rasterizer->edges, rasterizer->active_edges); + else + ctx_edge2_insertion_sort ((CtxSegment*)rasterizer->edge_list.entries, rasterizer->edges, rasterizer->active_edges); +} +#undef CTX_CMPSWP + +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; + { + 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; + } + } +#endif + +#if CTX_ENABLE_CLIP + if (CTX_UNLIKELY(rasterizer->clip_buffer && !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; + uint8_t *clip_line = &((uint8_t*)(rasterizer->clip_buffer->data))[rasterizer->blit_width*y]; + // XXX SIMD candidate + 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; +#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 (is_winding)\ + parity = parity + -1+2*(segment->code == CTX_EDGE_FLIPPED);\ + else\ + parity = 1-parity; \ + } + + +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) +{ + 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; + } + + graystart = fraction- (graystart&0xff)/aa_factor; + grayend = (grayend & 0xff) / aa_factor; + + if (first < last) + { + coverage[first] += graystart; + for (int x = first + 1; x < last; x++) + coverage[x] += fraction; + coverage[last] += grayend; + } + else if (first == last) + coverage[first] += (graystart-fraction+grayend); + } + } +} + +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; + } + + graystart = (graystart&0xff) ^ 255; + grayend = (grayend & 0xff); + + coverage[first] += graystart; + coverage[last] += grayend; + if (first + 1< last) + memset(&coverage[first+1], 255, last-(first+1)); + } + } +} + + +inline static void +ctx_rasterizer_generate_coverage_apply (CtxRasterizer *rasterizer, + int minx, + int maxx, + uint8_t* __restrict__ coverage, + int is_winding, + CtxCovPath comp) +{ + CtxSegment *entries = (CtxSegment*)(&rasterizer->edge_list.entries[0]); + void (*apply_coverage)(CtxRasterizer *r, uint8_t *dst, uint8_t *src, + int x, uint8_t *coverage, unsigned int count) = + rasterizer->apply_coverage; + 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; + } + else + { + src_pix = + si_ga = + si_rb = + si_ga_full = + si_rb_full = + si_a = 0; + src_pixp = &src_pix; + } +#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; + + 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; + + if (CTX_UNLIKELY(first < minx)) + { + graystart = 0; + first = minx; + } + if (CTX_UNLIKELY(last > maxx)) + { + last = maxx; + grayend=255; + } + graystart = (graystart&0xff) ^ 255; + + grayend = (grayend & 0xff); + + 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; + } + + 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); + + 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); +#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]; + 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++; + } + + for (unsigned int i = 0; i < count && count>2; count-=2, x+=2, dstp++) + *dstp = val_x2; + + 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; +#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++; + } + + 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 + +#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<<bitno); + dstp += (bitno == 7); + x++; + } + + for (unsigned int i = 0; i < count && count>8; count-=8) + { + *dstp = 255; + dstp++; + x+=8; + } + + for (unsigned int i = 0; i < count; i++) + { + int bitno = x & 7; + *dstp |= (1<<bitno); + dstp += (bitno == 7); + x++; + } + } + else + { + unsigned int x = first + 1; + for (unsigned int i = 0; i < count && x & 7; count--) + { + int bitno = x & 7; + *dstp &= ~(1<<bitno); + dstp += (bitno == 7); + x++; + } + + for (unsigned int i = 0; i < count && count>8; count-=8) + { + *dstp = 0; + dstp++; + x+=8; + } + + for (unsigned int i = 0; i < count; i++) + { + int bitno = x & 7; + *dstp &= ~(1<<bitno); + dstp += (bitno == 7); + x++; + } + + } + } + break; +#endif + + case CTX_COV_PATH_RGBA8_OVER: + { + uint32_t* dst_pix = (uint32_t*)(&dst[(first *bpp)/8]); + *dst_pix = ctx_over_RGBA8_2(*dst_pix, si_ga, si_rb, si_a, graystart); + dst_pix++; + for (unsigned int i = first + 1; i < (unsigned)last; i++) + { + *dst_pix = ctx_over_RGBA8_full_2(*dst_pix, si_ga_full, si_rb_full, si_a); + dst_pix++; + } + } + break; + case CTX_COV_PATH_RGBA8_COPY_FRAGMENT: + { + float u0 = 0; float v0 = 0; + float ud = 0; float vd = 0; + float w0 = 1; float wd = 0; + uint8_t gs = graystart; + ctx_RGBA8_source_copy_normal_fragment (rasterizer, &dst[(first * bpp)/8], NULL, first, &gs, 1); + ctx_init_uv (rasterizer, first+1, scanline/CTX_FULL_AA,&u0, &v0, &w0, &ud, &vd, &wd); + rasterizer->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)); +#endif + opaque[0] = graystart; + apply_coverage (rasterizer, + &dst[(first * bpp)/8], + rasterizer_src, first, opaque, last-first); + +#if static_OPAQUE + opaque[0] = 255; +#endif + } + } + accumulated = grayend; + } + else if (first == last) + { + accumulated = (graystart-(grayend^255)); + } + accumulator_x = last; + } + } + + if (accumulated) + { + 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); + } + } +} + +inline static int ctx_rasterizer_is_simple (CtxRasterizer *rasterizer) +{ + if (rasterizer->fast_aa == 0 || + rasterizer->ending_edges || + rasterizer->pending_edges) + return 0; + int *edges = rasterizer->edges; + CtxSegment *segments = &((CtxSegment*)(rasterizer->edge_list.entries))[0]; + + int active_edges = rasterizer->active_edges; + for (int t = 0; t < active_edges -1;t++) + { + CtxSegment *segment0 = segments + edges[t]; + CtxSegment *segment1 = segments + edges[t+1]; + const int delta0 = segment0->delta; + const int delta1 = segment1->delta; + const int x0 = segment0->val; + const int x1 = segment1->val; + int x0_end = x0 + delta0 * CTX_AA_HALFSTEP; + int x1_end = x1 + delta1 * CTX_AA_HALFSTEP; + int x0_start = x0 - delta0 * CTX_AA_HALFSTEP2; + int x1_start = x1 - delta1 * CTX_AA_HALFSTEP2; + if (x1_end < x0_end || + x1_start < x0_end || + x1_end < x0_start + ) + return 0; + } + return 1; +} + + +inline static void +ctx_rasterizer_generate_coverage_set2 (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; + + const int minx_ = minx * CTX_RASTERIZER_EDGE_MULTIPLIER * CTX_SUBDIV; + const int maxx_ = maxx * CTX_RASTERIZER_EDGE_MULTIPLIER * CTX_SUBDIV; + + for (int t = 0; t < active_edges -1;t++) + { + CtxSegment *segment = &entries[edges[t]]; + UPDATE_PARITY; + + 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 = (graystart&0xff) ^ 255; + 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; + } + 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 us = u0 / (CTX_RASTERIZER_EDGE_MULTIPLIER*CTX_SUBDIV); + int count = 0; + + 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 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-1)-first+1; + } + + if (abs(delta1) < CTX_RASTERIZER_AA_SLOPE_LIMIT3_FAST_AA) + { + coverage[last] += grayend; + } + 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))); + + 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) * + (CTX_RASTERIZER_EDGE_MULTIPLIER*CTX_SUBDIV/255)); + int sum = ((u1-u0+CTX_RASTERIZER_EDGE_MULTIPLIER * CTX_SUBDIV * 5/4)/255); + 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; + } + for (int i = first + pre; i <= last - post; i++) + coverage[i] = 255; + } + else if (first == last) + { + coverage[last]+=(graystart-(grayend^255)); + } + } + } +} + + +inline static void +ctx_rasterizer_generate_coverage_apply2 (CtxRasterizer *rasterizer, + int minx, + int maxx, + uint8_t *coverage, + int is_winding, + CtxCovPath comp) +{ + CtxSegment *entries = (CtxSegment*)(&rasterizer->edge_list.entries[0]); + int *edges = rasterizer->edges; + int scanline = rasterizer->scanline; + const int bpp = rasterizer->format->bpp; + int active_edges = rasterizer->active_edges; + void (*apply_coverage)(CtxRasterizer *r, uint8_t *dst, uint8_t *src, + int x, uint8_t *coverage, unsigned int count) = + rasterizer->apply_coverage; + uint8_t *rasterizer_src = rasterizer->color; + 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; + } + else + { + src_pix = + si_ga = + si_rb = + si_ga_full = + si_rb_full = + si_a = 0; + src_pixp = &src_pix; + } +#endif + + 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 accumulated_x0 = 65538; + int accumulated_x1 = 65536; + + 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; + 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-1)-first+1; + + 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; + } + + 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))); + + 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) * + (CTX_RASTERIZER_EDGE_MULTIPLIER*CTX_SUBDIV/255)); + int sum = ((u1-u0+CTX_RASTERIZER_EDGE_MULTIPLIER * CTX_SUBDIV * 5/4)/255); + + 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) + { +#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; +#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); + } + } + } + + 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; +#endif + default: + apply_coverage (rasterizer, + &dst[((accumulated_x0) * bpp)/8], + rasterizer_src, + accumulated_x0, + &coverage[accumulated_x0], + accumulated_x1-accumulated_x0+1); + } + } +} + +#undef CTX_EDGE_Y0 +#undef CTX_EDGE + +static inline void +ctx_rasterizer_reset (CtxRasterizer *rasterizer) +{ + rasterizer->has_shape = + rasterizer->has_prev = + rasterizer->edge_list.count = // ready for new edges + rasterizer->edge_pos = + rasterizer->scanline = 0; + if (CTX_LIKELY(!rasterizer->preserve)) + { + rasterizer->scan_min = + rasterizer->col_min = 50000000; + rasterizer->scan_max = + rasterizer->col_max = -50000000; + } + //rasterizer->comp_op = NULL; // keep comp_op cached + // between rasterizations where rendering attributes are + // nonchanging +} + +static void +ctx_rasterizer_rasterize_edges2 (CtxRasterizer *rasterizer, const int fill_rule +#if CTX_SHAPE_CACHE + ,CtxShapeEntry *shape +#endif + ) +{ + rasterizer->pending_edges = + rasterizer->active_edges = 0; + //rasterizer->scanline = 0; + 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; + const int blit_width = rasterizer->blit_width; + const int blit_max_x = rasterizer->blit_x + blit_width; + int minx = rasterizer->col_min / CTX_SUBDIV - rasterizer->blit_x; + int maxx = (rasterizer->col_max + CTX_SUBDIV-1) / CTX_SUBDIV - + rasterizer->blit_x; + const int blit_stride = rasterizer->blit_stride; + uint8_t real_fraction = 255/real_aa; + void (*apply_coverage)(CtxRasterizer *r, uint8_t *dst, uint8_t *src, + int x, uint8_t *coverage, unsigned int count) = + rasterizer->apply_coverage; + uint8_t *rasterizer_src = rasterizer->color; + + rasterizer->prev_active_edges = -1; + if ( +#if CTX_SHAPE_CACHE + !shape && +#endif + maxx > blit_max_x - 1) + { maxx = blit_max_x - 1; } + + minx = ctx_maxi (rasterizer->state->gstate.clip_min_x, minx); + maxx = ctx_mini (rasterizer->state->gstate.clip_max_x, maxx); + minx = ctx_maxi (0, minx); // redundant? + if (CTX_UNLIKELY (minx >= maxx)) + { + return; + } +#if CTX_SHAPE_CACHE + uint8_t _coverage[shape?2:maxx-minx+1]; +#else + uint8_t _coverage[maxx-minx+1]; +#endif + uint8_t *coverage = &_coverage[0]; + + int coverage_size; + + rasterizer->scan_min -= (rasterizer->scan_min % CTX_FULL_AA); +#if CTX_SHAPE_CACHE + if (shape) + { + coverage_size = shape->width; + coverage = &shape->data[0]; + scan_start = rasterizer->scan_min; + scan_end = rasterizer->scan_max; + } + else +#endif + { + coverage_size = sizeof (_coverage); + if (rasterizer->scan_min > scan_start) + { + dst += (rasterizer->blit_stride * (rasterizer->scan_min-scan_start) / CTX_FULL_AA); + scan_start = rasterizer->scan_min; + } + scan_end = ctx_mini (rasterizer->scan_max, scan_end); + } + + if (CTX_UNLIKELY(rasterizer->state->gstate.clip_min_y * CTX_FULL_AA > scan_start )) + { + dst += (rasterizer->blit_stride * (rasterizer->state->gstate.clip_min_y * CTX_FULL_AA -scan_start) / CTX_FULL_AA); + scan_start = rasterizer->state->gstate.clip_min_y * CTX_FULL_AA; + } + scan_end = ctx_mini (rasterizer->state->gstate.clip_max_y * CTX_FULL_AA, scan_end); + if (CTX_UNLIKELY(scan_start > scan_end || + (scan_start > (rasterizer->blit_y + (rasterizer->blit_height-1)) * CTX_FULL_AA) || + (scan_end < (rasterizer->blit_y) * CTX_FULL_AA))) + { + /* not affecting this rasterizers scanlines */ + return; + } + + rasterizer->horizontal_edges = + rasterizer->needs_aa3 = + rasterizer->needs_aa5 = + rasterizer->needs_aa15 = 0; + + ctx_rasterizer_sort_edges (rasterizer); + rasterizer->scanline = scan_start; + ctx_rasterizer_feed_edges (rasterizer, 0); + + int avoid_direct = (0 +#if CTX_ENABLE_CLIP + || rasterizer->clip_buffer +#endif +#if CTX_ENABLE_SHADOW_BLUR + || rasterizer->in_shadow +#endif +#if CTX_SHAPE_CACHE + || shape != NULL +#endif + ); + + for (; rasterizer->scanline <= scan_end;) + { + + if (rasterizer->active_edges == 0 && rasterizer->pending_edges == 0) + { /* no edges */ + ctx_rasterizer_feed_edges (rasterizer, 0); + ctx_rasterizer_increment_edges (rasterizer, CTX_FULL_AA); + dst += blit_stride; +#if CTX_SHAPE_CACHE + if (shape) + { + memset (coverage, 0, coverage_size); + coverage += shape->width; + } +#endif + rasterizer->prev_active_edges = rasterizer->active_edges; + continue; + } + else if (real_aa != 1 && ( (rasterizer->horizontal_edges!=0) + || (rasterizer->active_edges != rasterizer->prev_active_edges) + || (rasterizer->active_edges + rasterizer->pending_edges == rasterizer->ending_edges) + )) + { /* needs full AA */ + int increment = CTX_FULL_AA/real_aa; + memset (coverage, 0, coverage_size); + for (int i = 0; i < real_aa; i++) + { + ctx_rasterizer_feed_edges (rasterizer, 0); + ctx_rasterizer_generate_coverage (rasterizer, minx, maxx, coverage, is_winding, real_aa, real_fraction); + ctx_rasterizer_increment_edges (rasterizer, increment); + } + } + else if (rasterizer->needs_aa3 == 0) + { + if (! avoid_direct) + { /* can generate with direct rendering to target (we're not using shape cache) */ + ctx_rasterizer_increment_edges (rasterizer, CTX_AA_HALFSTEP2); + ctx_rasterizer_feed_edges (rasterizer, 0); + + ctx_rasterizer_generate_coverage_apply (rasterizer, minx, maxx, coverage, is_winding, comp); + ctx_rasterizer_increment_edges (rasterizer, CTX_AA_HALFSTEP); + + dst += blit_stride; + rasterizer->prev_active_edges = rasterizer->active_edges; + continue; + } + else + { /* cheap fully correct AA, to coverage mask / clipping */ + ctx_rasterizer_increment_edges (rasterizer, CTX_AA_HALFSTEP2); + ctx_rasterizer_feed_edges (rasterizer, 0); + + memset (coverage, 0, coverage_size); + ctx_rasterizer_generate_coverage_set (rasterizer, minx, maxx, coverage, is_winding); + ctx_rasterizer_increment_edges (rasterizer, CTX_AA_HALFSTEP); + } + } + else if (ctx_rasterizer_is_simple (rasterizer)) + { /* 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, 1); + memset (coverage, 0, coverage_size); + if (!avoid_direct) + { + ctx_rasterizer_generate_coverage_apply2 (rasterizer, minx, maxx, coverage, is_winding, + comp); + ctx_rasterizer_increment_edges (rasterizer, CTX_AA_HALFSTEP); + + dst += blit_stride; + rasterizer->prev_active_edges = rasterizer->active_edges; + continue; + } + ctx_rasterizer_generate_coverage_set2 (rasterizer, minx, maxx, coverage, is_winding); + ctx_rasterizer_increment_edges (rasterizer, CTX_AA_HALFSTEP); + if (real_aa == 1) + { + for (int x = minx; x <= maxx; x ++) + coverage[x] = coverage[x] > 127?255:0; + } + } + else + { /* determine level of oversampling based on lowest steepness edges */ + int aa = 3; + if (rasterizer->needs_aa5 && real_aa >=5) + { + aa = 5; + if (rasterizer->needs_aa15 && real_aa >=15) + aa = 15; + } + int scanline_increment = 15/aa; + + memset (coverage, 0, coverage_size); + uint8_t fraction = 255/aa; + for (int i = 0; i < CTX_FULL_AA; i+= scanline_increment) + { + ctx_rasterizer_feed_edges (rasterizer, 0); + ctx_rasterizer_generate_coverage (rasterizer, minx, maxx, coverage, is_winding, aa, fraction); + ctx_rasterizer_increment_edges (rasterizer, scanline_increment); + } + } + + ctx_coverage_post_process (rasterizer, minx, maxx, coverage - minx, NULL, NULL); +#if CTX_SHAPE_CACHE + if (shape == NULL) +#endif + { + apply_coverage (rasterizer, + &dst[(minx * rasterizer->format->bpp) /8], + rasterizer_src, + minx, + coverage, + maxx-minx+ 1); + } +#if CTX_SHAPE_CACHE + else + { + coverage += shape->width; + } +#endif + dst += blit_stride; + rasterizer->prev_active_edges = rasterizer->active_edges; + } + + 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)) + { + /* fill in the rest of the blitrect when compositing mode permits it */ + uint8_t nocoverage[rasterizer->blit_width]; + //int gscan_start = rasterizer->state->gstate.clip_min_y * CTX_FULL_AA; + int gscan_start = rasterizer->state->gstate.clip_min_y * CTX_FULL_AA; + int gscan_end = rasterizer->state->gstate.clip_max_y * CTX_FULL_AA; + memset (nocoverage, 0, sizeof(nocoverage)); + int startx = rasterizer->state->gstate.clip_min_x; + int endx = rasterizer->state->gstate.clip_max_x; + int clipw = endx-startx + 1; + uint8_t *dst = ( (uint8_t *) rasterizer->buf); + + dst = (uint8_t*)(rasterizer->buf) + rasterizer->blit_stride * (gscan_start / CTX_FULL_AA); + for (rasterizer->scanline = gscan_start; rasterizer->scanline < scan_start;) + { + apply_coverage (rasterizer, + &dst[ (startx * rasterizer->format->bpp) /8], + rasterizer_src, 0, nocoverage, clipw); + rasterizer->scanline += CTX_FULL_AA; + dst += rasterizer->blit_stride; + } + if (minx < startx) + { + dst = (uint8_t*)(rasterizer->buf) + rasterizer->blit_stride * (scan_start / CTX_FULL_AA); + for (rasterizer->scanline = scan_start; rasterizer->scanline < scan_end;) + { + apply_coverage (rasterizer, + &dst[ (startx * rasterizer->format->bpp) /8], + rasterizer_src, + 0, + nocoverage, minx-startx); + dst += blit_stride; + } + } + + if (endx > maxx) + { + dst = (uint8_t*)(rasterizer->buf) + rasterizer->blit_stride * (scan_start / CTX_FULL_AA); + for (rasterizer->scanline = scan_start; rasterizer->scanline < scan_end;) + { + apply_coverage (rasterizer, + &dst[ (maxx * rasterizer->format->bpp) /8], + rasterizer_src, 0, nocoverage, endx-maxx); + + rasterizer->scanline += CTX_FULL_AA; + dst += rasterizer->blit_stride; + } + } +#if 1 + dst = (uint8_t*)(rasterizer->buf) + rasterizer->blit_stride * (scan_end / CTX_FULL_AA); + // XXX this crashes under valgrind/asan + if(0)for (rasterizer->scanline = scan_end; rasterizer->scanline/CTX_FULL_AA < gscan_end-1;) + { + apply_coverage (rasterizer, + &dst[ (startx * rasterizer->format->bpp) /8], + rasterizer_src, + 0, + nocoverage, clipw-1); + + rasterizer->scanline += CTX_FULL_AA; + dst += blit_stride; + } +#endif + } +} + + +#if CTX_INLINE_FILL_RULE +void +CTX_SIMD_SUFFIX (ctx_rasterizer_rasterize_edges) (CtxRasterizer *rasterizer, const int fill_rule +#if CTX_SHAPE_CACHE + ,CtxShapeEntry *shape +#endif + ); +#else + +void +CTX_SIMD_SUFFIX (ctx_rasterizer_rasterize_edges) (CtxRasterizer *rasterizer, const int fill_rule +#if CTX_SHAPE_CACHE + ,CtxShapeEntry *shape +#endif + ); +#endif + + +#if CTX_INLINE_FILL_RULE +void +CTX_SIMD_SUFFIX (ctx_rasterizer_rasterize_edges) (CtxRasterizer *rasterizer, const int fill_rule +#if CTX_SHAPE_CACHE + ,CtxShapeEntry *shape +#endif + ) +{ + if (fill_rule) + { + ctx_rasterizer_rasterize_edges2 (rasterizer, 1 +#if CTX_SHAPE_CACHE + ,shape +#endif + ); + } + else + { + ctx_rasterizer_rasterize_edges2 (rasterizer, 0 +#if CTX_SHAPE_CACHE + ,shape +#endif + ); + } +} +#else + +void +CTX_SIMD_SUFFIX (ctx_rasterizer_rasterize_edges) (CtxRasterizer *rasterizer, const int fill_rule +#if CTX_SHAPE_CACHE + ,CtxShapeEntry *shape +#endif + ) +{ + ctx_rasterizer_rasterize_edges2 (rasterizer, fill_rule +#if CTX_SHAPE_CACHE + ,shape +#endif + ); +} + +#endif + + + +extern CtxPixelFormatInfo *ctx_pixel_formats; +void CTX_SIMD_SUFFIX(ctx_simd_setup)(void); +void CTX_SIMD_SUFFIX(ctx_simd_setup)(void) +{ + ctx_pixel_formats = CTX_SIMD_SUFFIX(ctx_pixel_formats); + ctx_composite_setup = CTX_SIMD_SUFFIX(ctx_composite_setup); + 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); + ctx_composite_stroke_rect = CTX_SIMD_SUFFIX(ctx_composite_stroke_rect); +#endif +} + + +#endif +#endif +#if CTX_IMPLEMENTATION +#if CTX_RASTERIZER + + +inline static float ctx_fast_hypotf (float x, float y) +{ + 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; } +} + + + +static void +ctx_rasterizer_gradient_add_stop (CtxRasterizer *rasterizer, float pos, float *rgba) +{ + /* FIXME XXX we only have one gradient, but might need separate gradients + * for fill/stroke ! + * + */ + CtxGradient *gradient = &rasterizer->state->gradient; + CtxGradientStop *stop = &gradient->stops[gradient->n_stops]; + stop->pos = pos; + ctx_color_set_rgba (rasterizer->state, & (stop->color), rgba[0], rgba[1], rgba[2], rgba[3]); + if (gradient->n_stops < CTX_MAX_GRADIENT_STOPS-1) //we'll keep overwriting the last when out of stops + { gradient->n_stops++; } +} + +static inline void ctx_rasterizer_update_inner_point (CtxRasterizer *rasterizer, int x, int y) +{ + rasterizer->scan_min = ctx_mini (y, rasterizer->scan_min); + rasterizer->scan_max = ctx_maxi (y, rasterizer->scan_max); + rasterizer->col_min = ctx_mini (x, rasterizer->col_min); + rasterizer->col_max = ctx_maxi (x, rasterizer->col_max); + rasterizer->inner_x = x; + rasterizer->inner_y = y; +} + +static inline int ctx_rasterizer_add_point (CtxRasterizer *rasterizer, int x1, int y1) +{ + CtxSegment entry = {CTX_EDGE, {{0,}},0,0}; + + entry.data.s16[0]=rasterizer->inner_x; + entry.data.s16[1]=rasterizer->inner_y; + + entry.data.s16[2]=x1; + entry.data.s16[3]=y1; + + ctx_rasterizer_update_inner_point (rasterizer, x1, y1); + + return ctx_edgelist_add_single (&rasterizer->edge_list, (CtxEntry*)&entry); +} + +#if 0 +#define CTX_SHAPE_CACHE_PRIME1 7853 +#define CTX_SHAPE_CACHE_PRIME2 4129 +#define CTX_SHAPE_CACHE_PRIME3 3371 +#define CTX_SHAPE_CACHE_PRIME4 4221 +#else +#define CTX_SHAPE_CACHE_PRIME1 283 +#define CTX_SHAPE_CACHE_PRIME2 599 +#define CTX_SHAPE_CACHE_PRIME3 101 +#define CTX_SHAPE_CACHE_PRIME4 661 +#endif + + +#if CTX_SHAPE_CACHE +float ctx_shape_cache_rate = 0.0; +int _ctx_shape_cache_enabled = CTX_SHAPE_CACHE_DEFAULT; + +//static CtxShapeCache ctx_cache = {{NULL,}, 0}; + +static long ctx_shape_cache_hits = 0; +static long ctx_shape_cache_misses = 0; + + +/* this returns the buffer to use for rendering, it always + succeeds.. + */ +static inline CtxShapeEntry *ctx_shape_entry_find (CtxRasterizer *rasterizer, uint32_t hash, int width, int height) +{ + /* use both some high and some low bits */ + int entry_no = ( (hash >> 10) ^ (hash & 1023) ) % CTX_SHAPE_CACHE_ENTRIES; + { + static int i = 0; + i++; + if (i>256) + { + if (ctx_shape_cache_hits+ctx_shape_cache_misses) + { + ctx_shape_cache_rate = + 0.5f * ctx_shape_cache_rate + + 0.5f * (ctx_shape_cache_hits * 100.0f / (ctx_shape_cache_hits+ctx_shape_cache_misses)); + } + i = 0; + ctx_shape_cache_hits = 0; + ctx_shape_cache_misses = 0; + } + } +// XXX : this 1 one is needed to silence a false positive: +// ==90718== Invalid write of size 1 +// ==90718== at 0x1189EF: ctx_rasterizer_generate_coverage (ctx.h:4786) +// ==90718== by 0x118E57: ctx_rasterizer_rasterize_edges (ctx.h:4907) +// + int size = sizeof (CtxShapeEntry) + width * height + 1; + + CtxShapeEntry *entry = rasterizer->shape_cache.entries[entry_no]; + if (entry) + { + int old_size = sizeof (CtxShapeEntry) + entry->width + entry->height + 1; + if (entry->hash == hash && + entry->width == width && + entry->height == height) + { + if (entry->uses < 1<<30) + { entry->uses++; } + ctx_shape_cache_hits ++; + return entry; + } + + if (old_size >= size) + { + rasterizer->shape_cache.size -= old_size; + rasterizer->shape_cache.size += (old_size-size); // slack/leaked + } + else + { + rasterizer->shape_cache.entries[entry_no] = NULL; + rasterizer->shape_cache.size -= entry->width * entry->height; + rasterizer->shape_cache.size -= sizeof (CtxShapeEntry); + ctx_free (entry); + entry = NULL; + } + } + + if (!entry) + entry = rasterizer->shape_cache.entries[entry_no] = (CtxShapeEntry *) ctx_calloc (size, 1); + + rasterizer->shape_cache.size += size; + + ctx_shape_cache_misses ++; + entry->hash = hash; + entry->width = width; + entry->height = height; + entry->uses = 0; + return entry; +} + +#endif + +static uint32_t ctx_rasterizer_poly_to_edges (CtxRasterizer *rasterizer) +{ +#if CTX_SHAPE_CACHE + int x = 0; + int y = 0; +#endif + unsigned int count = rasterizer->edge_list.count; + if (CTX_UNLIKELY (count == 0)) + return 0; + CtxSegment *entry = (CtxSegment*)&rasterizer->edge_list.entries[0]; +#if CTX_SHAPE_CACHE +#if 1 + int ox = entry->data.s16[2]; + int oy = entry->data.s16[3]; +#endif + uint32_t hash = rasterizer->edge_list.count; + hash = (ox & CTX_SUBDIV); + hash *= CTX_SHAPE_CACHE_PRIME1; + hash += (oy & CTX_SUBDIV); +#endif + //CtxSegment *entry = &(((CtxSegment*)(rasterizer->edge_list.entries)))[0]; + for (unsigned int i = 0; i < count; i++) + { +#if CTX_SHAPE_CACHE + x = entry->data.s16[2]; + y = entry->data.s16[3]; + int dx = x-ox; + int dy = y-oy; + ox = x; + oy = y; + hash *= CTX_SHAPE_CACHE_PRIME3; + hash += dx; + hash *= CTX_SHAPE_CACHE_PRIME4; + hash += dy; +#endif +#if 1 + if (entry->data.s16[3] < entry->data.s16[1]) + { + *entry = ctx_segment_s16 (CTX_EDGE_FLIPPED, + entry->data.s16[2], entry->data.s16[3], + entry->data.s16[0], entry->data.s16[1]); + } +#endif + entry++; + } +#if CTX_SHAPE_CACHE + return hash; +#else + return 0; +#endif +} + +static inline void ctx_rasterizer_finish_shape (CtxRasterizer *rasterizer) +{ + if (rasterizer->has_shape && rasterizer->has_prev) + { + ctx_rasterizer_line_to (rasterizer, rasterizer->first_x, rasterizer->first_y); + rasterizer->has_prev = 0; + } +} + +//#define MIN_Y -100 +//#define MAX_Y 3800 +//#define MIN_X -100 +//#define MAX_X 3600*10 + +static inline void ctx_rasterizer_move_to (CtxRasterizer *rasterizer, float x, float y) +{ + int tx = 0, ty = 0; + + rasterizer->x = x; + rasterizer->y = y; + rasterizer->first_x = x; + rasterizer->first_y = y; + rasterizer->has_prev = -1; + _ctx_user_to_device_prepped (rasterizer->state, x,y, &tx, &ty); + + tx -= rasterizer->blit_x * CTX_SUBDIV; + ctx_rasterizer_update_inner_point (rasterizer, tx, ty); +} + +static 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 +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 +ctx_bezier_sample_1d (float x0, float x1, float x2, float x3, float dt) +{ + return ctx_lerpf ( + ctx_lerpf (ctx_lerpf (x0, x1, dt), + ctx_lerpf (x1, x2, dt), dt), + ctx_lerpf (ctx_lerpf (x1, x2, dt), + ctx_lerpf (x2, x3, dt), dt), dt); +} + +CTX_INLINE static void +ctx_bezier_sample (float x0, float y0, + float x1, float y1, + float x2, float y2, + float x3, float y3, + float dt, float *x, float *y) +{ + *x = ctx_bezier_sample_1d (x0, x1, x2, x3, dt); + *y = ctx_bezier_sample_1d (y0, y1, y2, y3, dt); +} + +static inline void +ctx_rasterizer_bezier_divide (CtxRasterizer *rasterizer, + float ox, float oy, + float x0, float y0, + float x1, float y1, + float x2, float y2, + float sx, float sy, + float ex, float ey, + float s, + float e, + int iteration, + float tolerance) +{ + float t = (s + e) * 0.5f; + float x, y; + float lx, ly, 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) + { + ctx_rasterizer_bezier_divide (rasterizer, ox, oy, x0, y0, x1, y1, x2, y2, + sx, sy, x, y, s, t, iteration + 1, + tolerance); + ctx_rasterizer_line_to (rasterizer, x, y); + ctx_rasterizer_bezier_divide (rasterizer, ox, oy, x0, y0, x1, y1, x2, y2, + x, y, ex, ey, t, e, iteration + 1, + tolerance); + } +} + +#define CTX_FIX_SCALE 1024 +#define CTX_FIX_SHIFT 10 + + +CTX_INLINE static int +ctx_lerp_fixed (int v0, int v1, int dx) +{ + return v0 + (((v1-v0) * dx) >> CTX_FIX_SHIFT); +} + + + +CTX_INLINE static int +ctx_bezier_sample_1d_fixed (int x0, int x1, int x2, int x3, int dt) +{ + return ctx_lerp_fixed ( + ctx_lerp_fixed (ctx_lerp_fixed (x0, x1, dt), + ctx_lerp_fixed (x1, x2, dt), dt), + ctx_lerp_fixed (ctx_lerp_fixed (x1, x2, dt), + ctx_lerp_fixed (x2, x3, dt), dt), dt); +} + +CTX_INLINE static void +ctx_bezier_sample_fixed (int x0, int y0, + int x1, int y1, + int x2, int y2, + int x3, int y3, + 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); +} + +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, + int sx, int sy, + int ex, int ey, + int s, + int e, + int iteration, + 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); + 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) + { + 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_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); + } +} + +static void +ctx_rasterizer_curve_to (CtxRasterizer *rasterizer, + float x0, float y0, + 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){ + +#if CTX_AVOID_CLIPPED_SUBDIVISION + // XXX need sporting to fixed point + float maxx = ctx_maxf (x1,x2); + maxx = ctx_maxf (maxx, ox); + maxx = ctx_maxf (maxx, x0); + float maxy = ctx_maxf (y1,y2); + maxy = ctx_maxf (maxy, oy); + maxy = ctx_maxf (maxy, y0); + float minx = ctx_minf (x1,x2); + minx = ctx_minf (minx, ox); + minx = ctx_minf (minx, x0); + float miny = ctx_minf (y1,y2); + miny = ctx_minf (miny, oy); + miny = ctx_minf (miny, y0); + + float coords[4][2]={{minx,miny}, + {maxx,miny}, + {maxx,maxy}, + {minx,maxy}}; + for (int i = 0; i < 4; i++) + { + _ctx_user_to_device (rasterizer->state, &coords[i][0], &coords[i][1]); + } + minx = maxx = coords[0][0]; + miny = maxy = coords[0][1]; + for (int i = 1; i < 4; i++) + { + minx = ctx_minf (minx, coords[i][0]); + miny = ctx_minf (miny, coords[i][1]); + maxx = ctx_maxf (minx, coords[i][0]); + maxy = ctx_maxf (miny, coords[i][1]); + } + + if( (maxx-minx) + (maxy-miny) < 0.66f || + (minx > rasterizer->blit_x + rasterizer->blit_width) || + (miny > rasterizer->blit_y + rasterizer->blit_height) || + (maxx < rasterizer->blit_x) || + (maxy < rasterizer->blit_y) ) + { + } + else +#endif + { +#if 1 + ctx_rasterizer_bezier_divide_fixed (rasterizer, + (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)(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)); +#else + ctx_rasterizer_bezier_divide (rasterizer, + ox, oy, x0, y0, + x1, y1, x2, y2, + ox, oy, x2, y2, + 0.0f, 1.0f, 0, tolerance); +#endif + } + } + ctx_rasterizer_line_to (rasterizer, x2, y2); +} + +static void +ctx_rasterizer_rel_move_to (CtxRasterizer *rasterizer, float x, float y) +{ + //if (CTX_UNLIKELY(x == 0.f && y == 0.f)) + //{ return; } + x += rasterizer->x; + y += rasterizer->y; + ctx_rasterizer_move_to (rasterizer, x, y); +} + +static void +ctx_rasterizer_rel_line_to (CtxRasterizer *rasterizer, float x, float y) +{ + //if (CTX_UNLIKELY(x== 0.f && y==0.f)) + // { return; } + x += rasterizer->x; + y += rasterizer->y; + ctx_rasterizer_line_to (rasterizer, x, y); +} + +static void +ctx_rasterizer_rel_curve_to (CtxRasterizer *rasterizer, + float x0, float y0, float x1, float y1, float x2, float y2) +{ + x0 += rasterizer->x; + y0 += rasterizer->y; + x1 += rasterizer->x; + y1 += rasterizer->y; + x2 += rasterizer->x; + y2 += rasterizer->y; + ctx_rasterizer_curve_to (rasterizer, x0, y0, x1, y1, x2, y2); +} + + +static int +ctx_rasterizer_find_texture (CtxRasterizer *rasterizer, + const char *eid) +{ + int no; + for (no = 0; no < CTX_MAX_TEXTURES; no++) + { + if (rasterizer->texture_source->texture[no].data && + rasterizer->texture_source->texture[no].eid && + !strcmp (rasterizer->texture_source->texture[no].eid, eid)) + return no; + } + return -1; +} + +static void +ctx_rasterizer_set_texture (CtxRasterizer *rasterizer, + const char *eid, + float x, + float y) +{ + int is_stroke = (rasterizer->state->source != 0); + CtxSource *source = is_stroke && (rasterizer->state->gstate.source_stroke.type != CTX_SOURCE_INHERIT_FILL)? + &rasterizer->state->gstate.source_stroke: + &rasterizer->state->gstate.source_fill; + rasterizer->state->source = 0; + + int no = ctx_rasterizer_find_texture (rasterizer, eid); + if (no < 0 || no >= CTX_MAX_TEXTURES) { no = 0; } + if (rasterizer->texture_source->texture[no].data == NULL) + { +#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 + { + rasterizer->texture_source->texture[no].frame = rasterizer->texture_source->frame; + } + source->type = CTX_SOURCE_TEXTURE; + source->texture.buffer = &rasterizer->texture_source->texture[no]; + ctx_matrix_identity (&source->set_transform); + ctx_matrix_translate (&source->set_transform, x, y); +} + + +static void +ctx_rasterizer_define_texture (CtxRasterizer *rasterizer, + const char *eid, + int width, + int height, + int format, + char unsigned *data) +{ + _ctx_texture_lock (); // we're using the same texture_source from all threads, keeping allocaitons down + // need synchronizing (it could be better to do a pre-pass) + ctx_texture_init (rasterizer->texture_source, + eid, + width, + height, + ctx_pixel_format_get_stride ((CtxPixelFormat)format, width), + (CtxPixelFormat)format, +#if CTX_ENABLE_CM + (void*)rasterizer->state->gstate.texture_space, +#else + NULL, +#endif + data, + ctx_buffer_pixels_free, (void*)23); + /* when userdata for ctx_buffer_pixels_free is 23, texture_init dups the data on + * use + */ + + ctx_rasterizer_set_texture (rasterizer, eid, 0.0f, 0.0f); + if (!rasterizer->state->gstate.source_fill.texture.buffer->color_managed) + { + _ctx_texture_prepare_color_management (rasterizer->state, + rasterizer->state->gstate.source_fill.texture.buffer); + } + _ctx_texture_unlock (); +} + + +inline static int +ctx_is_transparent (CtxRasterizer *rasterizer, int stroke) +{ + CtxGState *gstate = &rasterizer->state->gstate; + if (gstate->global_alpha_u8 == 0) + return 1; + if (gstate->source_fill.type == CTX_SOURCE_COLOR) + { + uint8_t ga[2]; + ctx_color_get_graya_u8 (rasterizer->state, &gstate->source_fill.color, ga); + if (ga[1] == 0) + return 1; + } + return 0; +} + + + +static void +ctx_rasterizer_fill (CtxRasterizer *rasterizer) +{ + unsigned int preserved_count = + (rasterizer->preserve&&rasterizer->edge_list.count)? + rasterizer->edge_list.count:1; + int blit_x = rasterizer->blit_x; + int blit_y = rasterizer->blit_y; + int blit_width = rasterizer->blit_width; + int blit_height = rasterizer->blit_height; +#if CTX_SHAPE_CACHE + int blit_stride = rasterizer->blit_stride; +#endif + + CtxSegment temp[preserved_count]; /* copy of already built up path's poly line + XXX - by building a large enough path + the stack can be smashed! + */ + if (CTX_UNLIKELY(rasterizer->preserve)) + { memcpy (temp, rasterizer->edge_list.entries, sizeof (temp) ); } + +#if CTX_ENABLE_SHADOW_BLUR + if (CTX_UNLIKELY(rasterizer->in_shadow)) + { + 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; + } + rasterizer->scan_min += rasterizer->shadow_y * CTX_FULL_AA; + rasterizer->scan_max += rasterizer->shadow_y * CTX_FULL_AA; + rasterizer->col_min += (rasterizer->shadow_x - rasterizer->state->gstate.shadow_blur * 3 + 1) * CTX_SUBDIV; + rasterizer->col_max += (rasterizer->shadow_x + rasterizer->state->gstate.shadow_blur * 3 + 1) * CTX_SUBDIV; + } +#endif + + if (CTX_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 + { + ctx_composite_setup (rasterizer); + + rasterizer->state->ink_min_x = ctx_mini (rasterizer->state->ink_min_x, rasterizer->col_min / CTX_SUBDIV); + rasterizer->state->ink_max_x = ctx_maxi (rasterizer->state->ink_min_x, rasterizer->col_max / CTX_SUBDIV); + rasterizer->state->ink_min_y = ctx_mini (rasterizer->state->ink_min_y, rasterizer->scan_min / CTX_FULL_AA); + rasterizer->state->ink_max_y = ctx_maxi (rasterizer->state->ink_max_y, rasterizer->scan_max / CTX_FULL_AA); + +#if CTX_FAST_FILL_RECT + if (rasterizer->edge_list.count == 5) + { + CtxSegment *entry0 = &(((CtxSegment*)(rasterizer->edge_list.entries)))[0]; + CtxSegment *entry1 = &(((CtxSegment*)(rasterizer->edge_list.entries)))[1]; + CtxSegment *entry2 = &(((CtxSegment*)(rasterizer->edge_list.entries)))[2]; + CtxSegment *entry3 = &(((CtxSegment*)(rasterizer->edge_list.entries)))[3]; + + + if ( + (!(rasterizer->state->gstate.clipped != 0)) & + (entry0->data.s16[2] == entry1->data.s16[2]) & + (entry0->data.s16[3] == entry3->data.s16[3]) & + (entry1->data.s16[3] == entry2->data.s16[3]) & + (entry2->data.s16[2] == entry3->data.s16[2]) +#if CTX_ENABLE_SHADOW_BLUR + && !rasterizer->in_shadow +#endif + ) + { + 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); + + if (x1 > x0 && y1 > y0) + { + ctx_composite_fill_rect (rasterizer, x0, y0, x1, y1, 255); + goto done; + } + } + } +#endif + + ctx_rasterizer_finish_shape (rasterizer); + + uint32_t hash = ctx_rasterizer_poly_to_edges (rasterizer); + if (hash){}; + +#if CTX_SHAPE_CACHE + void (*apply_coverage)(CtxRasterizer *r, uint8_t *dst, uint8_t *src, + int x, uint8_t *coverage, unsigned int count) = + rasterizer->apply_coverage; + uint8_t *rasterizer_src = rasterizer->color; + int width = (rasterizer->col_max + (CTX_SUBDIV-1) ) / CTX_SUBDIV - rasterizer->col_min/CTX_SUBDIV + 1; + int height = (rasterizer->scan_max + (CTX_FULL_AA-1) ) / CTX_FULL_AA - rasterizer->scan_min / CTX_FULL_AA + 1; + if (width * height < CTX_SHAPE_CACHE_DIM && width >=1 && height >= 1 + && width < CTX_SHAPE_CACHE_MAX_DIM + && height < CTX_SHAPE_CACHE_MAX_DIM +#if CTX_ENABLE_SHADOW_BLUR + && !rasterizer->in_shadow +#endif + ) + { + int scan_min = rasterizer->scan_min; + int col_min = rasterizer->col_min; + scan_min -= (scan_min % CTX_FULL_AA); + int y0 = scan_min / CTX_FULL_AA; + int y1 = y0 + height; + int x0 = col_min / CTX_SUBDIV; + int ymin = y0; + int x1 = x0 + width; + int clip_x_min = blit_x; + int clip_x_max = blit_x + blit_width - 1; + int clip_y_min = blit_y; + int clip_y_max = blit_y + blit_height - 1; + + int dont_cache = 0; + if (CTX_UNLIKELY(x1 >= clip_x_max)) + { x1 = clip_x_max; + dont_cache = 1; + } + int xo = 0; + if (CTX_UNLIKELY(x0 < clip_x_min)) + { + xo = clip_x_min - x0; + x0 = clip_x_min; + dont_cache = 1; + } + if (CTX_UNLIKELY(y0 < clip_y_min || y1 >= clip_y_max)) + dont_cache = 1; + if (dont_cache || !_ctx_shape_cache_enabled) + { + rasterizer->scanline = scan_min; + ctx_rasterizer_rasterize_edges (rasterizer, rasterizer->state->gstate.fill_rule +#if CTX_SHAPE_CACHE + , NULL +#endif + ); + } + else + { + rasterizer->scanline = scan_min; + CtxShapeEntry *shape = ctx_shape_entry_find (rasterizer, hash, width, height); + + if (shape->uses == 0) + { + CtxBuffer *buffer_backup = rasterizer->clip_buffer; + rasterizer->clip_buffer = NULL; + ctx_rasterizer_rasterize_edges (rasterizer, rasterizer->state->gstate.fill_rule, shape); + rasterizer->clip_buffer = buffer_backup; + } + + int ewidth = x1 - x0; + if (ewidth>0) + { + rasterizer->scanline = scan_min; + int bpp = rasterizer->format->bpp; + if (rasterizer->clip_buffer && !rasterizer->clip_rectangle) + { + uint8_t composite[ewidth]; + uint8_t *clip_data = (uint8_t*)rasterizer->clip_buffer->data; + int shape_width = shape->width; + for (int y = y0; y < y1; y++) + { + if ( (y >= clip_y_min) && (y <= clip_y_max) ) + { + for (int x = 0; x < ewidth; x++) + { + int val = shape->data[shape_width * (int)(y-ymin) + xo + x]; + // XXX : not valid for 1bit clip buffers + val = (val*(clip_data) [ + ((y-blit_y) * blit_width) + x0 + x])/255; + composite[x] = val; + } + apply_coverage (rasterizer, + ( (uint8_t *) rasterizer->buf) + (y-blit_y) * blit_stride + ((int) (x0) * bpp)/8, + rasterizer_src, + x0, // is 0 + composite, + ewidth ); + } + rasterizer->scanline += CTX_FULL_AA; + } + } + else + { + for (int y = y0; y < y1; y++) + { + if (CTX_LIKELY((y >= clip_y_min) && (y <= clip_y_max) )) + { + apply_coverage (rasterizer, + ( (uint8_t *) rasterizer->buf) + (y-blit_y) * blit_stride + (int) ((x0) * bpp)/8, rasterizer_src, + x0, + &shape->data[shape->width * (int) (y-ymin) + xo], + ewidth ); + } + rasterizer->scanline += CTX_FULL_AA; + } + } + } + } + } + else +#endif + { + + ctx_rasterizer_rasterize_edges (rasterizer, rasterizer->state->gstate.fill_rule +#if CTX_SHAPE_CACHE + , NULL +#endif + ); + } + } +#if CTX_FAST_FILL_RECT +done: +#endif + if (CTX_UNLIKELY(rasterizer->preserve)) + { + memcpy (rasterizer->edge_list.entries, temp, sizeof (temp) ); + 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; + } +#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 +{ + uint32_t unichar; + int col; + int row; + uint8_t rgba_bg[4]; + uint8_t rgba_fg[4]; +}; + +static int _ctx_glyph (Ctx *ctx, uint32_t unichar, int stroke); +static void +ctx_rasterizer_glyph (CtxRasterizer *rasterizer, uint32_t unichar, int stroke) +{ + float tx = rasterizer->state->x; + float ty = rasterizer->state->y - rasterizer->state->gstate.font_size; + float tx2 = rasterizer->state->x + rasterizer->state->gstate.font_size; + float ty2 = rasterizer->state->y + rasterizer->state->gstate.font_size; + _ctx_user_to_device (rasterizer->state, &tx, &ty); + _ctx_user_to_device (rasterizer->state, &tx2, &ty2); + + 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 + float font_size = 0; + int ch = 1; + int cw = 1; + + if (rasterizer->term_glyphs) + { + float tx = 0; + font_size = rasterizer->state->gstate.font_size; + + ch = (int)ctx_term_get_cell_height (rasterizer->backend.ctx); + cw = (int)ctx_term_get_cell_width (rasterizer->backend.ctx); + + _ctx_user_to_device_distance (rasterizer->state, &tx, &font_size); + } + if (rasterizer->term_glyphs && !stroke && + fabsf (font_size - ch) < 0.5f) + { + float tx = rasterizer->x; + float ty = rasterizer->y; + _ctx_user_to_device (rasterizer->state, &tx, &ty); + int col = (int)(tx / cw + 1); + int row = (int)(ty / ch + 1); + CtxTermGlyph *glyph = ctx_calloc (sizeof (CtxTermGlyph), 1); + ctx_list_append (&rasterizer->glyphs, glyph); + glyph->unichar = unichar; + glyph->col = col; + glyph->row = row; + ctx_color_get_rgba8 (rasterizer->state, &rasterizer->state->gstate.source_fill.color, + &glyph->rgba_fg[0]); + } + else +#endif +#endif + _ctx_glyph (rasterizer->backend.ctx, unichar, stroke); +} + +static void +_ctx_text (Ctx *ctx, + const char *string, + int stroke, + int visible); +static void +ctx_rasterizer_text (CtxRasterizer *rasterizer, const char *string, int stroke) +{ +#if CTX_TERM +#if CTX_BRAILLE_TEXT + float font_size = 0; + if (rasterizer->term_glyphs) + { + float tx = 0; + font_size = rasterizer->state->gstate.font_size; + _ctx_user_to_device_distance (rasterizer->state, &tx, &font_size); + } + 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) + { + float tx = rasterizer->x; + float ty = rasterizer->y; + _ctx_user_to_device (rasterizer->state, &tx, &ty); + int col = (int)(tx / cw + 1); + int row = (int)(ty / ch + 1); + + for (int i = 0; string[i]; i++, col++) + { + CtxTermGlyph *glyph = ctx_calloc (sizeof (CtxTermGlyph), 1); + ctx_list_prepend (&rasterizer->glyphs, glyph); + glyph->unichar = string[i]; + glyph->col = col; + glyph->row = row; + ctx_color_get_rgba8 (rasterizer->state, &rasterizer->state->gstate.source_fill.color, + glyph->rgba_fg); + } + } + else +#endif +#endif + { + _ctx_text (rasterizer->backend.ctx, string, stroke, 1); + } +} + +void +_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); +} + +static void +ctx_rasterizer_arc (CtxRasterizer *rasterizer, + float x, + float y, + float radius, + float start_angle, + float end_angle, + int anticlockwise) +{ + float factor = ctx_matrix_get_scale (&rasterizer->state->gstate.transform); + int full_segments = CTX_RASTERIZER_MAX_CIRCLE_SEGMENTS; + 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; + float step = CTX_PI*2.0f/full_segments; + int steps; + + if (end_angle < -30.0f) + end_angle = -30.0f; + if (start_angle < -30.0f) + start_angle = -30.0f; + if (end_angle > 30.0f) + end_angle = 30.0f; + if (start_angle > 30.0f) + start_angle = 30.0f; + + if (radius <= 0.0001f) + return; + + if (end_angle == start_angle) + // XXX also detect arcs fully outside render view + { + if (rasterizer->has_prev!=0) + ctx_rasterizer_line_to (rasterizer, x + ctx_cosf (end_angle) * radius, + y + ctx_sinf (end_angle) * radius); + else + ctx_rasterizer_move_to (rasterizer, x + ctx_cosf (end_angle) * radius, + y + ctx_sinf (end_angle) * radius); + return; + } +#if 1 + if ( (!anticlockwise && fabsf((end_angle - start_angle) - CTX_PI*2) < 0.01f) || + ( (anticlockwise && fabsf((start_angle - end_angle) - CTX_PI*2) < 0.01f ) ) + || (anticlockwise && fabsf((end_angle - start_angle) - CTX_PI*2) < 0.01f) || (!anticlockwise && fabsf((start_angle - end_angle) - CTX_PI*2) < 0.01f ) ) + { + steps = full_segments - 1; + } + else +#endif + { + if (anticlockwise) + steps = (int)((start_angle - end_angle) / (CTX_PI*2) * full_segments); + else + steps = (int)((end_angle - start_angle) / (CTX_PI*2) * full_segments); + // if (steps > full_segments) + // steps = full_segments; + } + + if (anticlockwise) { step = step * -1; } + int first = 1; + if (steps == 0 /* || steps==full_segments -1 || (anticlockwise && steps == full_segments) */) + { + float xv = x + ctx_cosf (start_angle) * radius; + float yv = y + ctx_sinf (start_angle) * radius; + if (!rasterizer->has_prev) + { ctx_rasterizer_move_to (rasterizer, xv, yv); } + first = 0; + } + else + { + for (float angle = start_angle, i = 0; i < steps; angle += step, i++) + { + float xv = x + ctx_cosf (angle) * radius; + float yv = y + ctx_sinf (angle) * radius; + if (first && !rasterizer->has_prev) + { ctx_rasterizer_move_to (rasterizer, xv, yv); } + else + { ctx_rasterizer_line_to (rasterizer, xv, yv); } + first = 0; + } + } + ctx_rasterizer_line_to (rasterizer, x + ctx_cosf (end_angle) * radius, + y + ctx_sinf (end_angle) * radius); +} + +static void +ctx_rasterizer_quad_to (CtxRasterizer *rasterizer, + float cx, + float cy, + float x, + float y) +{ + ctx_rasterizer_curve_to (rasterizer, + (cx * 2 + rasterizer->x) / 3.0f, (cy * 2 + rasterizer->y) / 3.0f, + (cx * 2 + x) / 3.0f, (cy * 2 + y) / 3.0f, + x, y); +} + +static void +ctx_rasterizer_rel_quad_to (CtxRasterizer *rasterizer, + float cx, float cy, + float x, float y) +{ + ctx_rasterizer_quad_to (rasterizer, cx + rasterizer->x, cy + rasterizer->y, + x + rasterizer->x, y + rasterizer->y); +} + +static void +ctx_rasterizer_rectangle_reverse (CtxRasterizer *rasterizer, + float x, + float y, + float width, + float height); + +#if CTX_STROKE_1PX + +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) + { return; } + uint8_t fg_color[4]; + ctx_color_get_rgba8 (rasterizer->state, &rasterizer->state->gstate.source_fill.color, +fg_color); + + int blit_stride = rasterizer->blit_stride; + 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); +} + + +static inline void +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; + uint8_t *rasterizer_src = rasterizer->color; + int pitch = rasterizer->format->bpp / 8; + int blit_stride = rasterizer->blit_stride; + + x1 += 0.5f; + y1 += 0.5f; + x0 += 0.5f; + y0 += 0.5f; + + float dxf = (x1 - x0); + float dyf = (y1 - y0); + int tx = (x0)* 65536; + int ty = (y0)* 65536; + + int blit_width = rasterizer->blit_width; + int blit_height = rasterizer->blit_height; + + if (dxf*dxf>dyf*dyf) + { + int length = abs((int)dxf); + int dy = (dyf * 65536)/(length); + int x = tx >> 16; + + if (dxf < 0.0f) + { + ty = (y1)* 65536; + x = (x1); + dy *= -1; + } + 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)) + ; ++i, ++x, ty += dy); + + for (; i < length && x < blit_width && (ty<65536 || (ty>=sblit_height)) + ; ++i, ++x, ty += dy) + { + int y = ty>>16; + int ypos = (ty >> 8) & 0xff; + + ctx_rasterizer_pset (rasterizer, x, y-1, 255-ypos); + ctx_rasterizer_pset (rasterizer, x, y, ypos); + } + + { + for (; i < length && x < blit_width && (ty>65536 && (ty<sblit_height)) + ; ++i, ++x, ty += dy) + { + uint8_t *dst = ( (uint8_t *) rasterizer->buf) + + ((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); + dst += blit_stride; + apply_coverage (rasterizer, dst, rasterizer_src, x, &ypos, 1); + } + } + + //for (; i < length; ++i, ++x, ty += dy) + { + int y = ty>>16; + int ypos = (ty >> 8) & 0xff; + ctx_rasterizer_pset (rasterizer, x, y-1, 255-ypos); + ctx_rasterizer_pset (rasterizer, x, y, ypos); + } + + } + else + { + int length = abs((int)dyf); + int dx = (dxf * 65536)/(length); + int y = ty >> 16; + + if (dyf < 0.0f) + { + tx = (x1)* 65536; + y = (y1); + dx *= -1; + } + int i = 0; + + int sblit_width = blit_width << 16; + + for (; i < length && y < 0; ++i, ++y, tx += dx); + + 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)) + ; ++i, ++y, tx += dx) + { + int x = tx>>16; + int xpos = (tx >> 8) & 0xff; + ctx_rasterizer_pset (rasterizer, x-1, y, 255-xpos); + ctx_rasterizer_pset (rasterizer, x, y, xpos); + } + + { + for (; i < length && y < blit_height && (tx>65536 && (tx<sblit_width)) + ; ++i, ++y, tx += dx) + { + int x = tx>>16; + uint8_t *dst = ( (uint8_t *) rasterizer->buf) + + 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); + } + } + //for (; i <= length; ++i, ++y, tx += dx) + { // better do one too many than one too little + int x = tx>>16; + int xpos = (tx >> 8) & 0xff; + ctx_rasterizer_pset (rasterizer, x-1, y, 255-xpos); + ctx_rasterizer_pset (rasterizer, x, y, xpos); + } + } +} + +static inline void +ctx_rasterizer_stroke_1px (CtxRasterizer *rasterizer) +{ + int count = rasterizer->edge_list.count; + CtxSegment *temp = (CtxSegment*)rasterizer->edge_list.entries; + float prev_x = 0.0f; + float prev_y = 0.0f; + int start = 0; + int end = 0; + + while (start < count) + { + int started = 0; + int i; + for (i = start; i < count; i++) + { + CtxSegment *entry = &temp[i]; + float x, y; + if (entry->code == CTX_NEW_EDGE) + { + if (started) + { + end = i - 1; + goto foo; + } + prev_x = entry->data.s16[0] * 1.0f / CTX_SUBDIV; + prev_y = entry->data.s16[1] * 1.0f / 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; + + ctx_rasterizer_stroke_1px_segment (rasterizer, prev_x, prev_y, x, y); + prev_x = x; + prev_y = y; + } + end = i-1; +foo: + start = end+1; + } + ctx_rasterizer_reset (rasterizer); +} + +#endif + +static void +ctx_rasterizer_stroke (CtxRasterizer *rasterizer) +{ + CtxGState *gstate = &rasterizer->state->gstate; + CtxSource source_backup; + int count = rasterizer->edge_list.count; + if (count == 0) + return; + int preserved = rasterizer->preserve; + float factor = ctx_matrix_get_scale (&gstate->transform); + float line_width = gstate->line_width * factor; + if (gstate->source_stroke.type != CTX_SOURCE_INHERIT_FILL) + { + source_backup = gstate->source_fill; + gstate->source_fill = rasterizer->state->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) + + ||( gstate->line_width * factor >= 0.99f + && gstate->line_width * factor <= 1.01f + && gstate->n_dashes == 0 + ) + ) + { + ctx_rasterizer_stroke_1px (rasterizer); + if (preserved) + { + rasterizer->preserve = 0; + } + else + { + rasterizer->edge_list.count = 0; + } + if (gstate->source_stroke.type != CTX_SOURCE_INHERIT_FILL) + gstate->source_fill = source_backup; + + return; + } +#endif + + CtxSegment temp[count]; /* copy of already built up path's poly line */ + memcpy (temp, rasterizer->edge_list.entries, sizeof (temp) ); + +#if CTX_FAST_FILL_RECT + if (rasterizer->edge_list.count == 5) + { + CtxSegment *entry0 = &((CtxSegment*)rasterizer->edge_list.entries)[0]; + CtxSegment *entry1 = &((CtxSegment*)rasterizer->edge_list.entries)[1]; + CtxSegment *entry2 = &((CtxSegment*)rasterizer->edge_list.entries)[2]; + CtxSegment *entry3 = &((CtxSegment*)rasterizer->edge_list.entries)[3]; + + if (!rasterizer->state->gstate.clipped && + (entry0->data.s16[2] == entry1->data.s16[2]) && + (entry0->data.s16[3] == entry3->data.s16[3]) && + (entry1->data.s16[3] == entry2->data.s16[3]) && + (entry2->data.s16[2] == entry3->data.s16[2]) +#if CTX_ENABLE_SHADOW_BLUR + && !rasterizer->in_shadow +#endif + ) + { + + float 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; + + ctx_composite_stroke_rect (rasterizer, x0, y0, x1, y1, line_width); + + goto done; + } + } +#endif + + { + { + if (line_width < 5.0f) + { + factor *= 0.89f; /* this hack adjustment makes sharp 1px and 2px strokewidths + // end up sharp without erronious AA; we seem to be off by + // one somewhere else, causing the need for this + // */ + line_width *= 0.89f; + } + 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; + float half_width_x = line_width/2; + float half_width_y = half_width_x; + + if (CTX_UNLIKELY(line_width <= 0.0f)) + { // makes 0 width be 1px in user-space; hairline + half_width_x = .5f; + half_width_y = .5f; + } + int start = 0; + int end = 0; + while (start < count) + { + int started = 0; + int i; + for (i = start; i < count; i++) + { + CtxSegment *entry = &temp[i]; + float x, y; + if (entry->code == CTX_NEW_EDGE) + { + if (CTX_LIKELY(started)) + { + end = i - 1; + goto foo; + } + prev_x = entry->data.s16[0] / CTX_SUBDIV; + prev_y = entry->data.s16[1] / CTX_FULL_AA; + started = 1; + start = i; + } + x = entry->data.s16[2] / CTX_SUBDIV; + y = entry->data.s16[3] / 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 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)) + { + ctx_rasterizer_finish_shape (rasterizer); + ctx_rasterizer_move_to (rasterizer, prev_x+dy, prev_y-dx); + } + ctx_rasterizer_line_to (rasterizer, prev_x-dy, prev_y+dx); + + // we need to know the slope of the other side + + // XXX possible miter line-to + //ctx_rasterizer_line_to (rasterizer, prev_x-dy+4, prev_y+dx+10); + //ctx_rasterizer_line_to (rasterizer, prev_x-dy+8, prev_y+dx+0); + + ctx_rasterizer_line_to (rasterizer, x-dy, y+dx); + } + prev_x = x; + prev_y = y; + } + end = i-1; +foo: + for (int i = end; i >= start; i--) + { + CtxSegment *entry = &temp[i]; + float x, y, dx, dy; + x = entry->data.s16[2] * 1.0f / CTX_SUBDIV; + y = entry->data.s16[3] * 1.0f / 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)) + { + ctx_rasterizer_line_to (rasterizer, prev_x-dy, prev_y+dx); + // XXX possible miter line-to + // ctx_rasterizer_line_to (rasterizer, prev_x-dy+10, prev_y+dx+10); + ctx_rasterizer_line_to (rasterizer, x-dy, y+dx); + } + prev_x = x; + prev_y = y; + if (CTX_UNLIKELY(entry->code == CTX_NEW_EDGE)) + { + x = entry->data.s16[0] * 1.0f / CTX_SUBDIV; + y = entry->data.s16[1] * 1.0f / 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)) + { + dx = dx * recip_length * half_width_x; + dy = dy * recip_length * half_width_y; + ctx_rasterizer_line_to (rasterizer, prev_x-dy, prev_y+dx); + ctx_rasterizer_line_to (rasterizer, x-dy, y+dx); + } + } + if ( (prev_x != x) && (prev_y != y) ) + { + prev_x = x; + prev_y = y; + } + } + start = end+1; + } + ctx_rasterizer_finish_shape (rasterizer); + switch (gstate->line_cap) + { + case CTX_CAP_SQUARE: // XXX: incorrect - if rectangles were in + // reverse order - rotation would be off + // better implement correct here + { + float x = 0, y = 0; + int has_prev = 0; + for (int i = 0; i < count; i++) + { + CtxSegment *entry = &temp[i]; + if (CTX_UNLIKELY(entry->code == CTX_NEW_EDGE)) + { + if (has_prev) + { + ctx_rasterizer_rectangle_reverse (rasterizer, x - half_width_x, y - half_width_y, half_width_x, half_width_y); + ctx_rasterizer_finish_shape (rasterizer); + } + x = entry->data.s16[0] * 1.0f / CTX_SUBDIV; + y = entry->data.s16[1] * 1.0f / 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); + } + x = entry->data.s16[2] * 1.0f / CTX_SUBDIV; + y = entry->data.s16[3] * 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); + } + break; + case CTX_CAP_NONE: /* nothing to do */ + break; + case CTX_CAP_ROUND: + { + float x = 0, y = 0; + int has_prev = 0; + for (int i = 0; i < count; i++) + { + CtxSegment *entry = &temp[i]; + if (CTX_UNLIKELY(entry->code == CTX_NEW_EDGE)) + { + if (has_prev) + { + ctx_rasterizer_arc (rasterizer, x, y, half_width_x, CTX_PI*3, 0, 1); + ctx_rasterizer_finish_shape (rasterizer); + } + x = entry->data.s16[0] * 1.0f / CTX_SUBDIV; + y = entry->data.s16[1] * 1.0f / CTX_FULL_AA; + ctx_rasterizer_arc (rasterizer, x, y, half_width_x, CTX_PI*2, 0, 1); + ctx_rasterizer_finish_shape (rasterizer); + } + x = entry->data.s16[2] * 1.0f / CTX_SUBDIV; + y = entry->data.s16[3] * 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); + break; + } + } + switch (gstate->line_join) + { + case CTX_JOIN_BEVEL: + case CTX_JOIN_MITER: + break; + case CTX_JOIN_ROUND: + { + float x = 0, y = 0; + for (int i = 0; i < count-1; i++) + { + CtxSegment *entry = &temp[i]; + x = entry->data.s16[2] * 1.0f / CTX_SUBDIV; + y = entry->data.s16[3] * 1.0f / CTX_FULL_AA; + if (CTX_UNLIKELY(entry[1].code == CTX_EDGE)) + { + ctx_rasterizer_arc (rasterizer, x, y, half_width_x, CTX_PI*2, 0, 1); + ctx_rasterizer_finish_shape (rasterizer); + } + } + break; + } + } + CtxFillRule rule_backup = gstate->fill_rule; + gstate->fill_rule = CTX_FILL_RULE_WINDING; + rasterizer->preserve = 0; // so fill isn't tripped + ctx_rasterizer_fill (rasterizer); + gstate->fill_rule = rule_backup; + gstate->transform = transform_backup; + //gstate->transform_type = 0; + _ctx_transform_prime (rasterizer->state); + } + } +#if CTX_FAST_FILL_RECT +done: +#endif + if (preserved) + { + memcpy (rasterizer->edge_list.entries, temp, sizeof (temp) ); + rasterizer->edge_list.count = count; + rasterizer->preserve = 0; + } + if (gstate->source_stroke.type != CTX_SOURCE_INHERIT_FILL) + gstate->source_fill = source_backup; +} + +#if CTX_1BIT_CLIP +#define CTX_CLIP_FORMAT CTX_FORMAT_GRAY1 +#else +#define CTX_CLIP_FORMAT CTX_FORMAT_GRAY8 +#endif + + +static void +ctx_rasterizer_clip_reset (CtxRasterizer *rasterizer) +{ +#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; + + rasterizer->state->gstate.clip_max_x = rasterizer->blit_x + rasterizer->blit_width - 1; + rasterizer->state->gstate.clip_max_y = rasterizer->blit_y + rasterizer->blit_height - 1; +} + +static void +ctx_rasterizer_clip_apply (CtxRasterizer *rasterizer, + CtxSegment *edges) +{ + unsigned int count = edges[0].data.u32[0]; + + int minx = 5000; + int miny = 5000; + int maxx = -5000; + int maxy = -5000; + int prev_x = 0; + int prev_y = 0; + int blit_width = rasterizer->blit_width; + int blit_height = rasterizer->blit_height; + + float coords[6][2]; + + for (unsigned int i = 0; i < count; i++) + { + CtxSegment *entry = &edges[i+1]; + float x, y; + if (entry->code == CTX_NEW_EDGE) + { + prev_x = entry->data.s16[0] / CTX_SUBDIV; + prev_y = entry->data.s16[1] / 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; + if (x < minx) { minx = (int)x; } + if (y < miny) { miny = (int)y; } + if (x > maxx) { maxx = (int)x; } + if (y > maxy) { maxy = (int)y; } + + if (i < 6) + { + coords[i][0] = x; + coords[i][1] = y; + } + } + +#if CTX_ENABLE_CLIP + + 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 0 + printf ("%d,%d %dx%d\n", minx, miny, + 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); + + rasterizer->clip_rectangle = 1; + +#if 0 + if (!rasterizer->clip_buffer) + rasterizer->clip_buffer = ctx_buffer_new (blit_width, + blit_height, + CTX_CLIP_FORMAT); + + memset (rasterizer->clip_buffer->data, 0, blit_width * blit_height); + int i = 0; + for (int y = rasterizer->state->gstate.clip_min_y; + y <= rasterizer->state->gstate.clip_max_y; + y++) + for (int x = rasterizer->state->gstate.clip_min_x; + x <= rasterizer->state->gstate.clip_max_x; + x++, i++) + { + ((uint8_t*)(rasterizer->clip_buffer->data))[i] = 255; + } +#endif + + return; + } +#if 0 + else + { + printf ("%d,%d %dx%d 0,0:%.2f 0,1:%.2f 1,0:%.2f 11:%.2f 20:%.2f 21:%2.f 30:%.2f 31:%.2f 40:%.2f 41:%.2f\n", minx, miny, + maxx-minx+1, maxy-miny+1 + + ,coords[0][0] , coords[0][1] + ,coords[1][0] , coords[1][1] + ,coords[2][0] , coords[2][1] + ,coords[3][0] , coords[3][1] + ,coords[4][0] , coords[4][1] + ); + } +#endif + } + } + rasterizer->clip_rectangle = 0; + + if ((minx == maxx) || (miny == maxy)) // XXX : reset hack + { + ctx_rasterizer_clip_reset (rasterizer); + return;//goto done; + } + + int we_made_it = 0; + CtxBuffer *clip_buffer; + + if (!rasterizer->clip_buffer) + { + rasterizer->clip_buffer = ctx_buffer_new (blit_width, + blit_height, + CTX_CLIP_FORMAT); + clip_buffer = rasterizer->clip_buffer; + we_made_it = 1; + if (CTX_CLIP_FORMAT == CTX_FORMAT_GRAY1) + memset (rasterizer->clip_buffer->data, 0, blit_width * blit_height/8); + else + memset (rasterizer->clip_buffer->data, 0, blit_width * blit_height); + } + else + { + clip_buffer = ctx_buffer_new (blit_width, blit_height, + CTX_CLIP_FORMAT); + } + + { + + float prev_x = 0; + float prev_y = 0; + + Ctx *ctx = ctx_new_for_framebuffer (clip_buffer->data, blit_width, blit_height, + blit_width, + CTX_CLIP_FORMAT); + + for (unsigned int i = 0; i < count; i++) + { + CtxSegment *entry = &edges[i+1]; + float x, y; + if (entry->code == CTX_NEW_EDGE) + { + prev_x = entry->data.s16[0] * 1.0f / CTX_SUBDIV; + prev_y = entry->data.s16[1] * 1.0f / 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; + ctx_line_to (ctx, x, y); + } + ctx_gray (ctx, 1.0f); + ctx_fill (ctx); + ctx_destroy (ctx); + } + + int maybe_rect = 1; + rasterizer->clip_rectangle = 0; + + if (CTX_CLIP_FORMAT == CTX_FORMAT_GRAY1) + { + unsigned int count = blit_width * blit_height / 8; + for (unsigned int i = 0; i < count; i++) + { + ((uint8_t*)rasterizer->clip_buffer->data)[i] = + (((uint8_t*)rasterizer->clip_buffer->data)[i] & + ((uint8_t*)clip_buffer->data)[i]); + } + } + else + { + int count = blit_width * blit_height; + + + int i; + int x0 = 0; + int y0 = 0; + int width = -1; + int next_stage = 0; + uint8_t *p_data = (uint8_t*)rasterizer->clip_buffer->data; + uint8_t *data = (uint8_t*)clip_buffer->data; + + i=0; + /* find upper left */ + for (; i < count && maybe_rect && !next_stage; i++) + { + uint8_t val = (p_data[i] * data[i])/255; + data[i] = val; + switch (val) + { + case 255: + x0 = i % blit_width; + y0 = i / blit_width; + next_stage = 1; + break; + case 0: break; + default: + maybe_rect = 0; + break; + } + } + + next_stage = 0; + /* figure out with */ + for (; i < count && !next_stage && maybe_rect; i++) + { + int x = i % blit_width; + int y = i / blit_width; + uint8_t val = (p_data[i] * data[i])/255; + data[i] = val; + + if (y == y0) + { + switch (val) + { + case 255: + width = x - x0 + 1; + break; + case 0: + next_stage = 1; + break; + default: + maybe_rect = 0; + break; + } + if (x % blit_width == blit_width - 1) next_stage = 1; + } + else next_stage = 1; + } + + next_stage = 0; + /* body */ + for (; i < count && maybe_rect && !next_stage; i++) + { + int x = i % blit_width; + uint8_t val = (p_data[i] * data[i])/255; + data[i] = val; + + if (x < x0) + { + if (val != 0){ maybe_rect = 0; next_stage = 1; } + } else if (x < x0 + width) + { + if (val != 255){ if (val != 0) maybe_rect = 0; next_stage = 1; } + } else { + if (val != 0){ maybe_rect = 0; next_stage = 1; } + } + } + + next_stage = 0; + /* foot */ + for (; i < count && maybe_rect && !next_stage; i++) + { + uint8_t val = (p_data[i] * data[i])/255; + data[i] = val; + + if (val != 0){ maybe_rect = 0; next_stage = 1; } + } + + + for (; i < count; i++) + { + uint8_t val = (p_data[i] * data[i])/255; + data[i] = val; + } + + if (maybe_rect) + rasterizer->clip_rectangle = 1; + } + if (!we_made_it) + ctx_buffer_destroy (clip_buffer); +#else + if (coords[0][0]){}; +#endif + + rasterizer->state->gstate.clip_min_x = ctx_maxi (minx, + rasterizer->state->gstate.clip_min_x); + rasterizer->state->gstate.clip_min_y = ctx_maxi (miny, + rasterizer->state->gstate.clip_min_y); + rasterizer->state->gstate.clip_max_x = ctx_mini (maxx, + rasterizer->state->gstate.clip_max_x); + rasterizer->state->gstate.clip_max_y = ctx_mini (maxy, + rasterizer->state->gstate.clip_max_y); +} + + +static void +ctx_rasterizer_clip (CtxRasterizer *rasterizer) +{ + int count = rasterizer->edge_list.count; + CtxSegment temp[count+1]; /* copy of already built up path's poly line */ + rasterizer->state->has_clipped=1; + rasterizer->state->gstate.clipped=1; + //if (rasterizer->preserve) + { memcpy (temp + 1, rasterizer->edge_list.entries, sizeof (temp) - sizeof (temp[0])); + temp[0].code = CTX_NOP; + temp[0].data.u32[0] = count; + ctx_state_set_blob (rasterizer->state, SQZ_clip, (uint8_t*)temp, sizeof(temp)); + } + ctx_rasterizer_clip_apply (rasterizer, temp); + ctx_rasterizer_reset (rasterizer); + if (rasterizer->preserve) + { + memcpy (rasterizer->edge_list.entries, temp + 1, sizeof (temp) - sizeof(temp[0])); + rasterizer->edge_list.count = count; + rasterizer->preserve = 0; + } +} + + +#if 0 +static void +ctx_rasterizer_load_image (CtxRasterizer *rasterizer, + const char *path, + float x, + float y) +{ + // decode PNG, put it in image is slot 1, + // magic width height stride format data + ctx_buffer_load_png (&rasterizer->backend.ctx->texture[0], path); + ctx_rasterizer_set_texture (rasterizer, 0, x, y); +} +#endif + +static void +ctx_rasterizer_rectangle_reverse (CtxRasterizer *rasterizer, + float x, + float y, + float width, + float height) +{ + ctx_rasterizer_move_to (rasterizer, x, y); + 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, 0); + //ctx_rasterizer_rel_line_to (rasterizer, width/2, 0); + ctx_rasterizer_finish_shape (rasterizer); +} + +static void +ctx_rasterizer_rectangle (CtxRasterizer *rasterizer, + float x, + float y, + float width, + float height) +{ + ctx_rasterizer_move_to (rasterizer, x, y); + ctx_rasterizer_rel_line_to (rasterizer, width, 0); + ctx_rasterizer_rel_line_to (rasterizer, 0, height); + ctx_rasterizer_rel_line_to (rasterizer, -width, 0); + ctx_rasterizer_rel_line_to (rasterizer, 0, -height); + //ctx_rasterizer_rel_line_to (rasterizer, width/2, 0); + ctx_rasterizer_finish_shape (rasterizer); +} + +static void +ctx_rasterizer_set_pixel (CtxRasterizer *rasterizer, + uint16_t x, + uint16_t y, + uint8_t r, + uint8_t g, + uint8_t b, + uint8_t a) +{ + rasterizer->state->gstate.source_fill.type = CTX_SOURCE_COLOR; + ctx_color_set_RGBA8 (rasterizer->state, &rasterizer->state->gstate.source_fill.color, r, g, b, a); + rasterizer->comp_op = NULL; +#if 0 + // XXX : doesn't take transforms into account - and has + // received less testing than code paths part of protocol, + // using rectangle properly will trigger the fillrect fastpath + ctx_rasterizer_pset (rasterizer, x, y, 255); +#else + ctx_rasterizer_rectangle (rasterizer, x, y, 1.0, 1.0); + ctx_rasterizer_fill (rasterizer); +#endif +} + +#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) +{ + float aspect = 1.0f; + float radius = corner_radius / aspect; + float degrees = CTX_PI / 180.0f; + + if (radius > width*0.5f) radius = width/2; + if (radius > height*0.5f) radius = height/2; + + ctx_rasterizer_finish_shape (rasterizer); + ctx_rasterizer_arc (rasterizer, x + width - radius, y + radius, radius, -90 * degrees, 0 * degrees, 0); + ctx_rasterizer_arc (rasterizer, x + width - radius, y + height - radius, radius, 0 * degrees, 90 * degrees, 0); + ctx_rasterizer_arc (rasterizer, x + radius, y + height - radius, radius, 90 * degrees, 180 * degrees, 0); + ctx_rasterizer_arc (rasterizer, x + radius, y + radius, radius, 180 * degrees, 270 * degrees, 0); + + ctx_rasterizer_finish_shape (rasterizer); +} + +static void +ctx_rasterizer_process (Ctx *ctx, CtxCommand *command); + +#if CTX_COMPOSITING_GROUPS +static void +ctx_rasterizer_start_group (CtxRasterizer *rasterizer) /* add a radius? */ +{ + CtxEntry save_command = ctx_void(CTX_SAVE); + // allocate buffer, and set it as temporary target + int no; + if (rasterizer->group[0] == NULL) // first group + { + rasterizer->saved_buf = rasterizer->buf; + } + for (no = 0; rasterizer->group[no] && no < CTX_GROUP_MAX; no++); + + if (no >= CTX_GROUP_MAX) + return; + rasterizer->group[no] = ctx_buffer_new (rasterizer->blit_width, + rasterizer->blit_height, + rasterizer->format->composite_format); + rasterizer->buf = rasterizer->group[no]->data; + ctx_rasterizer_process (rasterizer->backend.ctx, (CtxCommand*)&save_command); +} + +static void +ctx_rasterizer_end_group (CtxRasterizer *rasterizer) +{ + CtxEntry restore_command = ctx_void(CTX_RESTORE); + CtxEntry save_command = ctx_void(CTX_SAVE); + int no = 0; + for (no = 0; rasterizer->group[no] && no < CTX_GROUP_MAX; no++); + no--; + + if (no < 0) + return; + + 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; + // fetch compositing, blending, global alpha + ctx_rasterizer_process (ctx, (CtxCommand*)&restore_command); + ctx_rasterizer_process (ctx, (CtxCommand*)&save_command); + CtxEntry set_state[4]= + { + ctx_u32 (CTX_COMPOSITING_MODE, comp, 0), + ctx_u32 (CTX_BLEND_MODE, blend, 0), + ctx_u32 (CTX_EXTEND, extend, 0), + ctx_f (CTX_GLOBAL_ALPHA, global_alpha, 0.0) + }; + ctx_rasterizer_process (ctx, (CtxCommand*)&set_state[0]); + ctx_rasterizer_process (ctx, (CtxCommand*)&set_state[1]); + ctx_rasterizer_process (ctx, (CtxCommand*)&set_state[2]); + ctx_rasterizer_process (ctx, (CtxCommand*)&set_state[3]); + if (no == 0) + { + rasterizer->buf = rasterizer->saved_buf; + } + else + { + rasterizer->buf = rasterizer->group[no-1]->data; + } + // XXX use texture_source ? + ctx_texture_init (ctx, ".ctx-group", + rasterizer->blit_width, + rasterizer->blit_height, + + rasterizer->blit_width * rasterizer->format->bpp/8, + rasterizer->format->pixel_format, + NULL, // space + (uint8_t*)rasterizer->group[no]->data, + NULL, NULL); + { + const char *eid = ".ctx-group"; + int eid_len = ctx_strlen (eid); + + CtxEntry commands[4] = + { + ctx_f (CTX_TEXTURE, rasterizer->blit_x, rasterizer->blit_y), + ctx_u32 (CTX_DATA, eid_len, eid_len/9+1), + ctx_u32 (CTX_CONT, 0,0), + ctx_u32 (CTX_CONT, 0,0) + }; + memcpy( (char *) &commands[2].data.u8[0], eid, eid_len); + ( (char *) (&commands[2].data.u8[0]) ) [eid_len]=0; + + ctx_rasterizer_process (ctx, (CtxCommand*)commands); + } + { + CtxEntry commands[2]= + { + ctx_f (CTX_RECTANGLE, rasterizer->blit_x, rasterizer->blit_y), + ctx_f (CTX_CONT, rasterizer->blit_width, rasterizer->blit_height) + }; + ctx_rasterizer_process (ctx, (CtxCommand*)commands); + } + { + CtxEntry commands[1] = { ctx_void (CTX_FILL) }; + ctx_rasterizer_process (ctx, (CtxCommand*)commands); + } + //ctx_texture_release (rasterizer->backend.ctx, ".ctx-group"); + ctx_buffer_destroy (rasterizer->group[no]); + rasterizer->group[no] = 0; + ctx_rasterizer_process (ctx, (CtxCommand*)&restore_command); +} +#endif + +#if CTX_ENABLE_SHADOW_BLUR +static void +ctx_rasterizer_shadow_stroke (CtxRasterizer *rasterizer) +{ + CtxColor color; + CtxEntry save_command = ctx_void(CTX_SAVE); + Ctx *ctx = rasterizer->backend.ctx; + + float rgba[4] = {0, 0, 0, 1.0}; + if (ctx_get_color (rasterizer->backend.ctx, SQZ_shadowColor, &color) == 0) + ctx_color_get_rgba (rasterizer->state, &color, rgba); + + CtxEntry set_color_command [3]= + { + ctx_f (CTX_COLOR, CTX_RGBA, rgba[0]), + ctx_f (CTX_CONT, rgba[1], rgba[2]), + ctx_f (CTX_CONT, rgba[3], 0) + }; + CtxEntry restore_command = ctx_void(CTX_RESTORE); + float radius = rasterizer->state->gstate.shadow_blur; + int dim = 2 * radius + 1; + if (dim > CTX_MAX_GAUSSIAN_KERNEL_DIM) + dim = CTX_MAX_GAUSSIAN_KERNEL_DIM; + ctx_compute_gaussian_kernel (dim, radius, rasterizer->kernel); + ctx_rasterizer_process (ctx, (CtxCommand*)&save_command); + { + int i = 0; + for (int v = 0; v < dim; v += 1, i++) + { + float dy = rasterizer->state->gstate.shadow_offset_y + v - dim/2; + set_color_command[2].data.f[0] = rasterizer->kernel[i] * rgba[3]; + ctx_rasterizer_process (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 + } + } + //ctx_free (kernel); + ctx_rasterizer_process (ctx, (CtxCommand*)&restore_command); +} + +static void +ctx_rasterizer_shadow_text (CtxRasterizer *rasterizer, const char *str) +{ + float x = rasterizer->state->x; + float y = rasterizer->state->y; + CtxColor color; + CtxEntry save_command = ctx_void(CTX_SAVE); + Ctx *ctx = rasterizer->backend.ctx; + + float rgba[4] = {0, 0, 0, 1.0}; + if (ctx_get_color (rasterizer->backend.ctx, SQZ_shadowColor, &color) == 0) + ctx_color_get_rgba (rasterizer->state, &color, rgba); + + CtxEntry set_color_command [3]= + { + ctx_f (CTX_COLOR, CTX_RGBA, rgba[0]), + ctx_f (CTX_CONT, rgba[1], rgba[2]), + ctx_f (CTX_CONT, rgba[3], 0) + }; + CtxEntry move_to_command [1]= + { + ctx_f (CTX_MOVE_TO, x, y), + }; + CtxEntry restore_command = ctx_void(CTX_RESTORE); + float radius = rasterizer->state->gstate.shadow_blur; + int dim = 2 * radius + 1; + if (dim > CTX_MAX_GAUSSIAN_KERNEL_DIM) + dim = CTX_MAX_GAUSSIAN_KERNEL_DIM; + ctx_compute_gaussian_kernel (dim, radius, rasterizer->kernel); + ctx_rasterizer_process (ctx, (CtxCommand*)&save_command); + + { + { + move_to_command[0].data.f[0] = x; + move_to_command[0].data.f[1] = y; + set_color_command[2].data.f[0] = rgba[3]; + ctx_rasterizer_process (ctx, (CtxCommand*)&set_color_command); + ctx_rasterizer_process (ctx, (CtxCommand*)&move_to_command); + rasterizer->in_shadow=1; + ctx_rasterizer_text (rasterizer, str, 0); + rasterizer->in_shadow=0; + } + } + ctx_rasterizer_process (ctx, (CtxCommand*)&restore_command); + move_to_command[0].data.f[0] = x; + move_to_command[0].data.f[1] = y; + ctx_rasterizer_process (ctx, (CtxCommand*)&move_to_command); +} + +static void +ctx_rasterizer_shadow_fill (CtxRasterizer *rasterizer) +{ + CtxColor color; + Ctx *ctx = rasterizer->backend.ctx; + CtxEntry save_command = ctx_void(CTX_SAVE); + + float rgba[4] = {0, 0, 0, 1.0}; + if (ctx_get_color (rasterizer->backend.ctx, SQZ_shadowColor, &color) == 0) + ctx_color_get_rgba (rasterizer->state, &color, rgba); + + CtxEntry set_color_command [3]= + { + ctx_f (CTX_COLOR, CTX_RGBA, rgba[0]), + ctx_f (CTX_CONT, rgba[1], rgba[2]), + ctx_f (CTX_CONT, rgba[3], 0) + }; + CtxEntry restore_command = ctx_void(CTX_RESTORE); + float radius = rasterizer->state->gstate.shadow_blur; + int dim = 2 * radius + 1; + if (dim > CTX_MAX_GAUSSIAN_KERNEL_DIM) + dim = CTX_MAX_GAUSSIAN_KERNEL_DIM; + ctx_compute_gaussian_kernel (dim, radius, rasterizer->kernel); + ctx_rasterizer_process (ctx, (CtxCommand*)&save_command); + + { + 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; + } + } + ctx_rasterizer_process (ctx, (CtxCommand*)&restore_command); +} +#endif + +static void +ctx_rasterizer_line_dash (CtxRasterizer *rasterizer, unsigned int count, float *dashes) +{ + if (!dashes) + { + rasterizer->state->gstate.n_dashes = 0; + return; + } + count = CTX_MIN(count, CTX_MAX_DASHES); + rasterizer->state->gstate.n_dashes = count; + memcpy(&rasterizer->state->gstate.dashes[0], dashes, count * sizeof(float)); + for (unsigned int i = 0; i < count; i ++) + { + if (rasterizer->state->gstate.dashes[i] < 0.0001f) + rasterizer->state->gstate.dashes[i] = 0.0001f; // hang protection + } +} + + +static void +ctx_rasterizer_process (Ctx *ctx, CtxCommand *command) +{ + CtxEntry *entry = &command->entry; + CtxRasterizer *rasterizer = (CtxRasterizer *) ctx->backend; + CtxState *state = rasterizer->state; + CtxCommand *c = (CtxCommand *) entry; + int clear_clip = 0; + + ctx_interpret_style (state, entry, NULL); + switch (c->code) + { +#if CTX_ENABLE_SHADOW_BLUR + case CTX_SHADOW_COLOR: + { + CtxColor col; + CtxColor *color = &col; + //state->gstate.source_fill.type = CTX_SOURCE_COLOR; + switch ((int)c->rgba.model) + { + case CTX_RGB: + ctx_color_set_rgba (state, color, c->rgba.r, c->rgba.g, c->rgba.b, 1.0f); + break; + case CTX_RGBA: + //ctx_color_set_rgba (state, color, c->rgba.r, c->rgba.g, c->rgba.b, c->rgba.a); + ctx_color_set_rgba (state, color, c->rgba.r, c->rgba.g, c->rgba.b, c->rgba.a); + break; + case CTX_DRGBA: + ctx_color_set_drgba (state, color, c->rgba.r, c->rgba.g, c->rgba.b, c->rgba.a); + break; +#if CTX_ENABLE_CMYK + case CTX_CMYKA: + ctx_color_set_cmyka (state, color, c->cmyka.c, c->cmyka.m, c->cmyka.y, c->cmyka.k, c->cmyka.a); + break; + case CTX_CMYK: + ctx_color_set_cmyka (state, color, c->cmyka.c, c->cmyka.m, c->cmyka.y, c->cmyka.k, 1.0f); + break; + case CTX_DCMYKA: + ctx_color_set_dcmyka (state, color, c->cmyka.c, c->cmyka.m, c->cmyka.y, c->cmyka.k, c->cmyka.a); + break; + case CTX_DCMYK: + ctx_color_set_dcmyka (state, color, c->cmyka.c, c->cmyka.m, c->cmyka.y, c->cmyka.k, 1.0f); + break; +#endif + case CTX_GRAYA: + ctx_color_set_graya (state, color, c->graya.g, c->graya.a); + break; + case CTX_GRAY: + ctx_color_set_graya (state, color, c->graya.g, 1.0f); + break; + } + ctx_set_color (rasterizer->backend.ctx, SQZ_shadowColor, color); + } + break; +#endif + case CTX_LINE_DASH: + if (c->line_dash.count) + { + ctx_rasterizer_line_dash (rasterizer, c->line_dash.count, c->line_dash.data); + } + else + ctx_rasterizer_line_dash (rasterizer, 0, NULL); + break; + + + case CTX_LINE_TO: + if (ctx->bail) break; + ctx_rasterizer_line_to (rasterizer, c->c.x0, c->c.y0); + break; + case CTX_REL_LINE_TO: + if (ctx->bail) break; + ctx_rasterizer_rel_line_to (rasterizer, c->c.x0, c->c.y0); + break; + case CTX_MOVE_TO: + if (ctx->bail) break; + ctx_rasterizer_move_to (rasterizer, c->c.x0, c->c.y0); + break; + case CTX_REL_MOVE_TO: + if (ctx->bail) break; + ctx_rasterizer_rel_move_to (rasterizer, c->c.x0, c->c.y0); + break; + case CTX_CURVE_TO: + if (ctx->bail) break; + ctx_rasterizer_curve_to (rasterizer, c->c.x0, c->c.y0, + c->c.x1, c->c.y1, + c->c.x2, c->c.y2); + break; + case CTX_REL_CURVE_TO: + if (ctx->bail) break; + ctx_rasterizer_rel_curve_to (rasterizer, c->c.x0, c->c.y0, + c->c.x1, c->c.y1, + c->c.x2, c->c.y2); + break; + case CTX_QUAD_TO: + if (ctx->bail) break; + ctx_rasterizer_quad_to (rasterizer, c->c.x0, c->c.y0, c->c.x1, c->c.y1); + break; + case CTX_REL_QUAD_TO: + if (ctx->bail) break; + ctx_rasterizer_rel_quad_to (rasterizer, c->c.x0, c->c.y0, c->c.x1, c->c.y1); + break; + case CTX_ARC: + if (ctx->bail) break; + ctx_rasterizer_arc (rasterizer, c->arc.x, c->arc.y, c->arc.radius, c->arc.angle1, c->arc.angle2, (int)c->arc.direction); + break; + case CTX_RECTANGLE: + if (ctx->bail) break; + ctx_rasterizer_rectangle (rasterizer, c->rectangle.x, c->rectangle.y, + c->rectangle.width, c->rectangle.height); + break; + case CTX_ROUND_RECTANGLE: + if (ctx->bail) break; + ctx_rasterizer_round_rectangle (rasterizer, c->rectangle.x, c->rectangle.y, + c->rectangle.width, c->rectangle.height, + c->rectangle.radius); + break; + case CTX_SET_PIXEL: + ctx_rasterizer_set_pixel (rasterizer, c->set_pixel.x, c->set_pixel.y, + c->set_pixel.rgba[0], + c->set_pixel.rgba[1], + c->set_pixel.rgba[2], + c->set_pixel.rgba[3]); + break; + case CTX_DEFINE_TEXTURE: + { + uint8_t *pixel_data = ctx_define_texture_pixel_data (entry); + ctx_rasterizer_define_texture (rasterizer, c->define_texture.eid, + c->define_texture.width, c->define_texture.height, + c->define_texture.format, + pixel_data); + rasterizer->comp_op = NULL; + rasterizer->fragment = NULL; + } + break; + case CTX_TEXTURE: + ctx_rasterizer_set_texture (rasterizer, c->texture.eid, + c->texture.x, c->texture.y); + rasterizer->comp_op = NULL; + rasterizer->fragment = NULL; + break; + case CTX_SOURCE_TRANSFORM: + ctx_matrix_set (&state->gstate.source_fill.set_transform, + ctx_arg_float (0), ctx_arg_float (1), + ctx_arg_float (2), ctx_arg_float (3), + ctx_arg_float (4), ctx_arg_float (5), + ctx_arg_float (6), ctx_arg_float (7), + ctx_arg_float (8)); + rasterizer->comp_op = NULL; + break; +#if 0 + case CTX_LOAD_IMAGE: + ctx_rasterizer_load_image (rasterizer, ctx_arg_string(), + ctx_arg_float (0), ctx_arg_float (1) ); + break; +#endif +#if CTX_GRADIENTS + case CTX_GRADIENT_STOP: + { + float rgba[4]= {ctx_u8_to_float (ctx_arg_u8 (4) ), + ctx_u8_to_float (ctx_arg_u8 (4+1) ), + ctx_u8_to_float (ctx_arg_u8 (4+2) ), + ctx_u8_to_float (ctx_arg_u8 (4+3) ) + }; + ctx_rasterizer_gradient_add_stop (rasterizer, + ctx_arg_float (0), rgba); + rasterizer->comp_op = NULL; + } + break; + case CTX_LINEAR_GRADIENT: + ctx_state_gradient_clear_stops (state); + rasterizer->gradient_cache_valid = 0; + rasterizer->comp_op = NULL; + break; + case CTX_RADIAL_GRADIENT: + ctx_state_gradient_clear_stops (state); + rasterizer->gradient_cache_valid = 0; + rasterizer->comp_op = NULL; + break; +#endif + case CTX_PRESERVE: + rasterizer->preserve = 1; + break; + case CTX_COLOR: + case CTX_COMPOSITING_MODE: + case CTX_BLEND_MODE: + case CTX_EXTEND: + rasterizer->comp_op = NULL; + break; +#if CTX_COMPOSITING_GROUPS + case CTX_START_GROUP: + ctx_rasterizer_start_group (rasterizer); + break; + case CTX_END_GROUP: + ctx_rasterizer_end_group (rasterizer); + break; +#endif + + case CTX_RESTORE: + for (unsigned int i = state->gstate_no?state->gstate_stack[state->gstate_no-1].keydb_pos:0; + i < state->gstate.keydb_pos; i++) + { + if (state->keydb[i].key == SQZ_clip) + { + clear_clip = 1; + } + } + /* FALLTHROUGH */ + case CTX_ROTATE: + case CTX_SCALE: + case CTX_APPLY_TRANSFORM: + case CTX_TRANSLATE: + case CTX_IDENTITY: + /* FALLTHROUGH */ + case CTX_SAVE: + rasterizer->comp_op = NULL; + ctx_interpret_transforms (state, entry, NULL); + if (clear_clip) + { + ctx_rasterizer_clip_reset (rasterizer); + for (unsigned int i = state->gstate_no?state->gstate_stack[state->gstate_no-1].keydb_pos:0; + i < state->gstate.keydb_pos; i++) + { + if (state->keydb[i].key == SQZ_clip) + { + int idx = ctx_float_to_string_index (state->keydb[i].value); + if (idx >=0) + { + CtxSegment *edges = (CtxSegment*)&state->stringpool[idx]; + ctx_rasterizer_clip_apply (rasterizer, edges); + } + } + } + } + break; + case CTX_STROKE: + if (rasterizer->edge_list.count == 0)break; +#if CTX_ENABLE_SHADOW_BLUR + if (state->gstate.shadow_blur > 0.0f && + !rasterizer->in_text) + ctx_rasterizer_shadow_stroke (rasterizer); +#endif + { + int count = rasterizer->edge_list.count; + if (state->gstate.n_dashes) + { + int n_dashes = state->gstate.n_dashes; + float *dashes = state->gstate.dashes; + float factor = ctx_matrix_get_scale (&state->gstate.transform); + + CtxSegment temp[count]; /* copy of already built up path's poly line */ + memcpy (temp, rasterizer->edge_list.entries, sizeof (temp)); + int start = 0; + int end = 0; + CtxMatrix transform_backup = state->gstate.transform; + _ctx_matrix_identity (&state->gstate.transform); + //state->gstate.transform_type = 0; + _ctx_transform_prime (state); + ctx_rasterizer_reset (rasterizer); /* for dashing we create + a dashed path to stroke */ + float prev_x = 0.0f; + float prev_y = 0.0f; + //float pos = 0.0; - /* left */ - ctx_composite_fill_rect_aligned (rasterizer, - (int)x0-bwb, (int)y0+1, - (int)x0+bw-1, (int)y1-bwb, 255); - /* right */ - ctx_composite_fill_rect_aligned (rasterizer, - (int)x1-bwb, (int)y0+1, - (int)x1+bw-1, (int)y1+bw-1, 255); - } - else - { - float hw = line_width/2; + int dash_no = 0.0; + float dash_lpos = state->gstate.line_dash_offset * factor; + int is_down = 0; + while (start < count) + { + int started = 0; + int i; + is_down = 0; - /* top */ - ctx_composite_fill_rect (rasterizer, - x0+hw, y0-hw, - x1-hw, y0+hw, 255); - /* bottom */ - ctx_composite_fill_rect (rasterizer, - x0+hw, y1-hw, - x1-hw, y1+hw, 255); + 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; + ctx_rasterizer_move_to (rasterizer, prev_x, prev_y); + is_down = 1; + } - /* left */ - ctx_composite_fill_rect (rasterizer, - x0-hw, y0+hw, - x0+hw, y1-hw, 255); - /* right */ + for (i = start; i < count; i++) + { + CtxSegment *entry = &temp[i]; + float x, y; + if (entry->code == CTX_NEW_EDGE) + { + if (started) + { + end = i - 1; + dash_no = 0; + dash_lpos = 0.0; + goto foo; + } + prev_x = entry->data.s16[0] * 1.0f / CTX_SUBDIV; + prev_y = entry->data.s16[1] * 1.0f / CTX_FULL_AA; + started = 1; + start = i; + is_down = 1; + ctx_rasterizer_move_to (rasterizer, prev_x, prev_y); + } - ctx_composite_fill_rect (rasterizer, - x1-hw, y0+hw, - x1+hw, y1-hw, 255); +again: - /* corners */ + x = entry->data.s16[2] * 1.0f / CTX_SUBDIV; + y = entry->data.s16[3] * 1.0f / CTX_FULL_AA; + float dx = x - prev_x; + float dy = y - prev_y; + float length = ctx_fast_hypotf (dx, dy); - ctx_composite_fill_rect (rasterizer, - x0-hw, y0-hw, - x0+hw, y0+hw, 255); - ctx_composite_fill_rect (rasterizer, - x1-hw, y1-hw, - x1+hw, y1+hw, 255); - ctx_composite_fill_rect (rasterizer, - x1-hw, y0-hw, - x1+hw, y0+hw, 255); - ctx_composite_fill_rect (rasterizer, - x0-hw, y1-hw, - x0+hw, y1+hw, 255); - } -} + if (dash_lpos + length >= dashes[dash_no] * factor) + { + float p = (dashes[dash_no] * factor - dash_lpos) / length; + float splitx = x * p + (1.0f - p) * prev_x; + float splity = y * p + (1.0f - p) * prev_y; + if (is_down) + { + ctx_rasterizer_line_to (rasterizer, splitx, splity); + is_down = 0; + } + else + { + ctx_rasterizer_move_to (rasterizer, splitx, splity); + is_down = 1; + } + prev_x = splitx; + prev_y = splity; + dash_no++; + dash_lpos=0; + if (dash_no >= n_dashes) dash_no = 0; + goto again; + } + else + { + //pos += length; + dash_lpos += length; + { + if (is_down) + ctx_rasterizer_line_to (rasterizer, x, y); + } + } + prev_x = x; + prev_y = y; + } + end = i-1; +foo: + start = end+1; + } + state->gstate.transform = transform_backup; + //state->gstate.transform_type = 0; + _ctx_transform_prime (state); + } + ctx_rasterizer_stroke (rasterizer); + } + ctx_rasterizer_reset (rasterizer); + break; + case CTX_FONT: + ctx_rasterizer_set_font (rasterizer, ctx_arg_string() ); + break; + case CTX_TEXT: + if (ctx->bail) + { + _ctx_text (rasterizer->backend.ctx, ctx_arg_string(), 0, 0); + break; + } + rasterizer->in_text++; +#if CTX_ENABLE_SHADOW_BLUR + if (state->gstate.shadow_blur > 0.0) + ctx_rasterizer_shadow_text (rasterizer, ctx_arg_string ()); +#endif + ctx_rasterizer_text (rasterizer, ctx_arg_string(), 0); + rasterizer->in_text--; + ctx_rasterizer_reset (rasterizer); + break; + case CTX_STROKE_TEXT: + ctx_rasterizer_text (rasterizer, ctx_arg_string(), 1); + ctx_rasterizer_reset (rasterizer); + break; + case CTX_GLYPH: + if (ctx->bail) break; + { + uint32_t unichar = entry[0].data.u32[0]; + uint32_t stroke = unichar & ((uint32_t)1<<31); + if (stroke) unichar -= stroke; + ctx_rasterizer_glyph (rasterizer, entry[0].data.u32[0], stroke); + } + break; + case CTX_PAINT: + // XXX simplify this with a special case + ctx_rasterizer_rectangle (rasterizer, -1000.0, -1000.0, 11000, 11000); + ctx_rasterizer_fill (rasterizer); + ctx_rasterizer_reset (rasterizer); + break; + case CTX_FILL: + if (!ctx->bail) + { + if (rasterizer->edge_list.count == 0)break; +#if CTX_ENABLE_SHADOW_BLUR + if (state->gstate.shadow_blur > 0.0f && + !rasterizer->in_text) + ctx_rasterizer_shadow_fill (rasterizer); #endif + ctx_rasterizer_fill (rasterizer); + } + ctx_rasterizer_reset (rasterizer); + break; + case CTX_START_FRAME: + case CTX_BEGIN_PATH: + ctx_rasterizer_reset (rasterizer); + break; + case CTX_CLIP: + ctx_rasterizer_clip (rasterizer); + break; + case CTX_CLOSE_PATH: + ctx_rasterizer_finish_shape (rasterizer); + break; + case CTX_IMAGE_SMOOTHING: + rasterizer->comp_op = NULL; + break; + } + ctx_interpret_pos_bare (state, entry, NULL); +} -static void -CTX_SIMD_SUFFIX (ctx_composite_setup) (CtxRasterizer *rasterizer) + +//static CtxFont *ctx_fonts; +void +ctx_rasterizer_deinit (CtxRasterizer *rasterizer) { - if (CTX_UNLIKELY (rasterizer->comp_op==NULL)) - { -#if CTX_GRADIENTS -#if CTX_GRADIENT_CACHE - switch (rasterizer->state->gstate.source_fill.type) + //rasterizer->fonts = ctx_fonts; + ctx_drawlist_deinit (&rasterizer->edge_list); +#if CTX_ENABLE_CLIP + if (rasterizer->clip_buffer) { - case CTX_SOURCE_LINEAR_GRADIENT: - case CTX_SOURCE_RADIAL_GRADIENT: - ctx_gradient_cache_prime (rasterizer); - break; - case CTX_SOURCE_TEXTURE: - - _ctx_matrix_multiply (&rasterizer->state->gstate.source_fill.transform, - &rasterizer->state->gstate.source_fill.set_transform, - &rasterizer->state->gstate.transform); -#if 0 - rasterizer->state->gstate.source_fill.transform_inv = - rasterizer->state->gstate.source_fill.transform; -#endif - ctx_matrix_invert (&rasterizer->state->gstate.source_fill.transform); - -#if 0 - 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 - break; + ctx_buffer_destroy (rasterizer->clip_buffer); + rasterizer->clip_buffer = NULL; } #endif +#if CTX_SHAPE_CACHE + for (int i = 0; i < CTX_SHAPE_CACHE_ENTRIES; i ++) + if (rasterizer->shape_cache.entries[i]) + { + ctx_free (rasterizer->shape_cache.entries[i]); + rasterizer->shape_cache.entries[i] = NULL; + } #endif - } - rasterizer->format->setup (rasterizer); - } +void +ctx_rasterizer_destroy (CtxRasterizer *rasterizer) +{ + ctx_rasterizer_deinit (rasterizer); + ctx_free (rasterizer); +} -CtxPixelFormatInfo CTX_SIMD_SUFFIX(ctx_pixel_formats)[]= +CtxAntialias ctx_get_antialias (Ctx *ctx) { -#if CTX_ENABLE_RGBA8 - { - CTX_FORMAT_RGBA8, 4, 32, 4, 0, 0, CTX_FORMAT_RGBA8, - NULL, NULL, NULL, ctx_setup_RGBA8 - }, -#endif -#if CTX_ENABLE_BGRA8 - { - CTX_FORMAT_BGRA8, 4, 32, 4, 0, 0, CTX_FORMAT_RGBA8, - ctx_BGRA8_to_RGBA8, ctx_RGBA8_to_BGRA8, ctx_composite_BGRA8, ctx_setup_RGBA8, - }, -#endif -#if CTX_ENABLE_GRAYF - { - CTX_FORMAT_GRAYF, 1, 32, 4 * 2, 0, 0, CTX_FORMAT_GRAYAF, - NULL, NULL, ctx_composite_GRAYF, ctx_setup_GRAYAF, - }, -#endif -#if CTX_ENABLE_GRAYAF - { - CTX_FORMAT_GRAYAF, 2, 64, 4 * 2, 0, 0, CTX_FORMAT_GRAYAF, - NULL, NULL, NULL, ctx_setup_GRAYAF, - }, -#endif -#if CTX_ENABLE_RGBAF - { - CTX_FORMAT_RGBAF, 4, 128, 4 * 4, 0, 0, CTX_FORMAT_RGBAF, - NULL, NULL, NULL, ctx_setup_RGBAF, - }, -#endif -#if CTX_ENABLE_RGB8 - { - CTX_FORMAT_RGB8, 3, 24, 4, 0, 0, CTX_FORMAT_RGBA8, - ctx_RGB8_to_RGBA8, ctx_RGBA8_to_RGB8, ctx_composite_convert, ctx_setup_RGB8, - }, -#endif -#if CTX_ENABLE_GRAY1 - { -#if CTX_NATIVE_GRAYA8 - CTX_FORMAT_GRAY1, 1, 1, 2, 1, 1, CTX_FORMAT_GRAYA8, - ctx_GRAY1_to_GRAYA8, ctx_GRAYA8_to_GRAY1, ctx_composite_convert, ctx_setup_GRAY1, -#else - CTX_FORMAT_GRAY1, 1, 1, 4, 1, 1, CTX_FORMAT_RGBA8, - ctx_GRAY1_to_RGBA8, ctx_RGBA8_to_GRAY1, ctx_composite_convert, ctx_setup_RGB, -#endif - }, -#endif -#if CTX_ENABLE_GRAY2 - { -#if CTX_NATIVE_GRAYA8 - CTX_FORMAT_GRAY2, 1, 2, 2, 4, 4, CTX_FORMAT_GRAYA8, - ctx_GRAY2_to_GRAYA8, ctx_GRAYA8_to_GRAY2, ctx_composite_convert, ctx_setup_GRAY2, -#else - CTX_FORMAT_GRAY2, 1, 2, 4, 4, 4, CTX_FORMAT_RGBA8, - ctx_GRAY2_to_RGBA8, ctx_RGBA8_to_GRAY2, ctx_composite_convert, ctx_setup_RGB, -#endif - }, -#endif -#if CTX_ENABLE_GRAY4 - { -#if CTX_NATIVE_GRAYA8 - CTX_FORMAT_GRAY4, 1, 4, 2, 16, 16, CTX_FORMAT_GRAYA8, - ctx_GRAY4_to_GRAYA8, ctx_GRAYA8_to_GRAY4, ctx_composite_convert, ctx_setup_GRAY4, -#else - CTX_FORMAT_GRAY4, 1, 4, 4, 16, 16, CTX_FORMAT_GRAYA8, - ctx_GRAY4_to_RGBA8, ctx_RGBA8_to_GRAY4, ctx_composite_convert, ctx_setup_RGB, -#endif - }, -#endif -#if CTX_ENABLE_GRAY8 - { -#if CTX_NATIVE_GRAYA8 - CTX_FORMAT_GRAY8, 1, 8, 2, 0, 0, CTX_FORMAT_GRAYA8, - ctx_GRAY8_to_GRAYA8, ctx_GRAYA8_to_GRAY8, ctx_composite_convert, ctx_setup_GRAY8, -#else - CTX_FORMAT_GRAY8, 1, 8, 4, 0, 0, CTX_FORMAT_RGBA8, - ctx_GRAY8_to_RGBA8, ctx_RGBA8_to_GRAY8, ctx_composite_convert, ctx_setup_RGB, -#endif - }, -#endif -#if CTX_ENABLE_GRAYA8 - { -#if CTX_NATIVE_GRAYA8 - CTX_FORMAT_GRAYA8, 2, 16, 2, 0, 0, CTX_FORMAT_GRAYA8, - ctx_GRAYA8_to_RGBA8, ctx_RGBA8_to_GRAYA8, NULL, ctx_setup_GRAYA8, -#else - CTX_FORMAT_GRAYA8, 2, 16, 4, 0, 0, CTX_FORMAT_RGBA8, - ctx_GRAYA8_to_RGBA8, ctx_RGBA8_to_GRAYA8, ctx_composite_convert, ctx_setup_RGB, -#endif - }, -#endif -#if CTX_ENABLE_RGB332 +#if CTX_EVENTS + if (ctx_backend_is_tiled (ctx)) { - CTX_FORMAT_RGB332, 3, 8, 4, 12, 12, CTX_FORMAT_RGBA8, - ctx_RGB332_to_RGBA8, ctx_RGBA8_to_RGB332, - ctx_composite_RGB332, ctx_setup_RGB332, - }, + CtxTiled *fb = (CtxTiled*)(ctx->backend); + return fb->antialias; + } #endif -#if CTX_ENABLE_CBRLE - { - CTX_FORMAT_CBRLE_1, 4, 1, 4, 12, 12, CTX_FORMAT_RGBA8, - ctx_CBRLE_to_RGBA8, ctx_RGBA8_to_CBRLE, - ctx_composite_CBRLE, ctx_setup_CBRLE, - }, - { - CTX_FORMAT_CBRLE_3, 4, 3, 4, 12, 12, CTX_FORMAT_RGBA8, - ctx_CBRLE_to_RGBA8, ctx_RGBA8_to_CBRLE, - ctx_composite_CBRLE, ctx_setup_CBRLE, - }, - { - CTX_FORMAT_CBRLE_2, 4, 2, 4, 12, 12, CTX_FORMAT_RGBA8, - ctx_CBRLE_to_RGBA8, ctx_RGBA8_to_CBRLE, - ctx_composite_CBRLE, ctx_setup_CBRLE, - }, - { - CTX_FORMAT_CBRLE_4, 4, 4, 4, 12, 12, CTX_FORMAT_RGBA8, - ctx_CBRLE_to_RGBA8, ctx_RGBA8_to_CBRLE, - ctx_composite_CBRLE, ctx_setup_CBRLE, - }, - { - CTX_FORMAT_CBRLE_5, 4, 5, 4, 12, 12, CTX_FORMAT_RGBA8, - ctx_CBRLE_to_RGBA8, ctx_RGBA8_to_CBRLE, - ctx_composite_CBRLE, ctx_setup_CBRLE, - }, - { - CTX_FORMAT_CBRLE_6, 4, 6, 4, 12, 12, CTX_FORMAT_RGBA8, - ctx_CBRLE_to_RGBA8, ctx_RGBA8_to_CBRLE, - ctx_composite_CBRLE, ctx_setup_CBRLE, - }, - { - CTX_FORMAT_CBRLE_7, 4, 7, 4, 12, 12, CTX_FORMAT_RGBA8, - ctx_CBRLE_to_RGBA8, ctx_RGBA8_to_CBRLE, - ctx_composite_CBRLE, ctx_setup_CBRLE, - }, - { - CTX_FORMAT_CBRLE_8, 4, 8, 4, 12, 12, CTX_FORMAT_RGBA8, - ctx_CBRLE_to_RGBA8, ctx_RGBA8_to_CBRLE, - ctx_composite_CBRLE, ctx_setup_CBRLE, - }, - { - CTX_FORMAT_CBRLE_9, 4, 9, 4, 12, 12, CTX_FORMAT_RGBA8, - ctx_CBRLE_to_RGBA8, ctx_RGBA8_to_CBRLE, - ctx_composite_CBRLE, ctx_setup_CBRLE, - }, - { - CTX_FORMAT_CBRLE_10, 4, 10, 4, 12, 12, CTX_FORMAT_RGBA8, - ctx_CBRLE_to_RGBA8, ctx_RGBA8_to_CBRLE, - ctx_composite_CBRLE, ctx_setup_CBRLE, - }, - { - CTX_FORMAT_CBRLE_11, 4, 11, 4, 12, 12, CTX_FORMAT_RGBA8, - ctx_CBRLE_to_RGBA8, ctx_RGBA8_to_CBRLE, - ctx_composite_CBRLE, ctx_setup_CBRLE, - }, - { - CTX_FORMAT_CBRLE_12, 4, 12, 4, 12, 12, CTX_FORMAT_RGBA8, - ctx_CBRLE_to_RGBA8, ctx_RGBA8_to_CBRLE, - ctx_composite_CBRLE, ctx_setup_CBRLE, - }, - { - CTX_FORMAT_CBRLE_13, 4, 13, 4, 12, 12, CTX_FORMAT_RGBA8, - ctx_CBRLE_to_RGBA8, ctx_RGBA8_to_CBRLE, - ctx_composite_CBRLE, ctx_setup_CBRLE, - }, - { - CTX_FORMAT_CBRLE_14, 4, 14, 4, 12, 12, CTX_FORMAT_RGBA8, - ctx_CBRLE_to_RGBA8, ctx_RGBA8_to_CBRLE, - ctx_composite_CBRLE, ctx_setup_CBRLE, - }, - { - CTX_FORMAT_CBRLE_15, 4, 15, 4, 12, 12, CTX_FORMAT_RGBA8, - ctx_CBRLE_to_RGBA8, ctx_RGBA8_to_CBRLE, - ctx_composite_CBRLE, ctx_setup_CBRLE, - }, - { - CTX_FORMAT_CBRLE_16, 4, 16, 4, 12, 12, CTX_FORMAT_RGBA8, - ctx_CBRLE_to_RGBA8, ctx_RGBA8_to_CBRLE, - ctx_composite_CBRLE, ctx_setup_CBRLE, - }, + if (ctx_backend_type (ctx) != CTX_BACKEND_RASTERIZER) return CTX_ANTIALIAS_DEFAULT; + switch (((CtxRasterizer*)(ctx->backend))->aa) { - CTX_FORMAT_CBRLE_17, 4, 17, 4, 12, 12, CTX_FORMAT_RGBA8, - ctx_CBRLE_to_RGBA8, ctx_RGBA8_to_CBRLE, - ctx_composite_CBRLE, ctx_setup_CBRLE, - }, - { - CTX_FORMAT_CBRLE_18, 4, 18, 4, 12, 12, CTX_FORMAT_RGBA8, - ctx_CBRLE_to_RGBA8, ctx_RGBA8_to_CBRLE, - ctx_composite_CBRLE, ctx_setup_CBRLE, - }, - { - CTX_FORMAT_CBRLE_19, 4, 19, 4, 12, 12, CTX_FORMAT_RGBA8, - ctx_CBRLE_to_RGBA8, ctx_RGBA8_to_CBRLE, - ctx_composite_CBRLE, ctx_setup_CBRLE, - }, - { - CTX_FORMAT_CBRLE_20, 4, 20, 4, 12, 12, CTX_FORMAT_RGBA8, - ctx_CBRLE_to_RGBA8, ctx_RGBA8_to_CBRLE, - ctx_composite_CBRLE, ctx_setup_CBRLE, - }, - { - CTX_FORMAT_CBRLE_21, 4, 21, 4, 12, 12, CTX_FORMAT_RGBA8, - ctx_CBRLE_to_RGBA8, ctx_RGBA8_to_CBRLE, - ctx_composite_CBRLE, ctx_setup_CBRLE, - }, - { - CTX_FORMAT_CBRLE_22, 4, 22, 4, 12, 12, CTX_FORMAT_RGBA8, - ctx_CBRLE_to_RGBA8, ctx_RGBA8_to_CBRLE, - ctx_composite_CBRLE, ctx_setup_CBRLE, - }, + 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; + } +} +static int _ctx_antialias_to_aa (CtxAntialias antialias) +{ + switch (antialias) { - CTX_FORMAT_CBRLE_23, 4, 23, 4, 12, 12, CTX_FORMAT_RGBA8, - ctx_CBRLE_to_RGBA8, ctx_RGBA8_to_CBRLE, - ctx_composite_CBRLE, ctx_setup_CBRLE, - }, - + case CTX_ANTIALIAS_NONE: return 1; + case CTX_ANTIALIAS_FAST: return 3; + case CTX_ANTIALIAS_GOOD: return 5; + default: + case CTX_ANTIALIAS_DEFAULT: return CTX_RASTERIZER_AA; + } +} +void +ctx_set_antialias (Ctx *ctx, CtxAntialias antialias) +{ +#if CTX_TERMINAL_EVENTS + if (ctx_backend_is_tiled (ctx)) { - CTX_FORMAT_CBRLE_24, 4, 24, 4, 12, 12, CTX_FORMAT_RGBA8, - ctx_CBRLE_to_RGBA8, ctx_RGBA8_to_CBRLE, - ctx_composite_CBRLE, ctx_setup_CBRLE, - }, - { - CTX_FORMAT_CBRLE_32, 4, 32, 4, 12, 12, CTX_FORMAT_RGBA8, - ctx_CBRLE_to_RGBA8, ctx_RGBA8_to_CBRLE, - ctx_composite_CBRLE, ctx_setup_CBRLE, - }, - { - CTX_FORMAT_CBRLE, 4, 32, 4, 12, 12, CTX_FORMAT_RGBA8, - ctx_CBRLE_to_RGBA8, ctx_RGBA8_to_CBRLE, - ctx_composite_CBRLE, ctx_setup_CBRLE, - }, -#endif -#if CTX_ENABLE_RGB565 - { - CTX_FORMAT_RGB565, 3, 16, 4, 16, 32, CTX_FORMAT_RGBA8, - ctx_RGB565_to_RGBA8, ctx_RGBA8_to_RGB565, - ctx_composite_RGB565, ctx_setup_RGB565, - }, -#endif -#if CTX_ENABLE_RGB565_BYTESWAPPED - { - CTX_FORMAT_RGB565_BYTESWAPPED, 3, 16, 4, 16, 32, CTX_FORMAT_RGBA8, - ctx_RGB565_BS_to_RGBA8, - ctx_RGBA8_to_RGB565_BS, - ctx_composite_RGB565_BS, ctx_setup_RGB565, - }, -#endif -#if CTX_ENABLE_CMYKAF - { - CTX_FORMAT_CMYKAF, 5, 160, 4 * 5, 0, 0, CTX_FORMAT_CMYKAF, - NULL, NULL, NULL, ctx_setup_CMYKAF, - }, + CtxTiled *fb = (CtxTiled*)(ctx->backend); + fb->antialias = antialias; +#if CTX_THREADS + for (int i = 0; i < _ctx_max_threads; i++) +#else + int i = 0; #endif -#if CTX_ENABLE_CMYKA8 - { - CTX_FORMAT_CMYKA8, 5, 40, 4 * 5, 0, 0, CTX_FORMAT_CMYKAF, - NULL, NULL, ctx_composite_CMYKA8, ctx_setup_CMYKA8, - }, + { + ctx_set_antialias (fb->host[i], antialias); + } + return; + } #endif -#if CTX_ENABLE_CMYK8 - { - CTX_FORMAT_CMYK8, 5, 32, 4 * 5, 0, 0, CTX_FORMAT_CMYKAF, - NULL, NULL, ctx_composite_CMYK8, ctx_setup_CMYK8, - }, + if (ctx_backend_type (ctx) != CTX_BACKEND_RASTERIZER) return; + + ((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 * +ctx_rasterizer_init (CtxRasterizer *rasterizer, Ctx *ctx, Ctx *texture_source, CtxState *state, void *data, int x, int y, int width, int height, int stride, CtxPixelFormat pixel_format, CtxAntialias antialias) +{ +#if CTX_ENABLE_CLIP + if (rasterizer->clip_buffer) + ctx_buffer_destroy (rasterizer->clip_buffer); #endif -#if CTX_ENABLE_YUV420 - { - CTX_FORMAT_YUV420, 1, 8, 4, 0, 0, CTX_FORMAT_RGBA8, - NULL, NULL, ctx_composite_convert, ctx_setup_RGB, - }, + if (rasterizer->edge_list.size) + ctx_drawlist_deinit (&rasterizer->edge_list); +#if CTX_SHAPE_CACHE + memset (rasterizer, 0, sizeof (CtxRasterizer) - sizeof (CtxShapeCache)); +#else + memset (rasterizer, 0, sizeof (CtxRasterizer)); #endif - { - CTX_FORMAT_NONE - } -}; - + CtxBackend *backend = (CtxBackend*)rasterizer; + backend->type = CTX_BACKEND_RASTERIZER; + backend->process = ctx_rasterizer_process; + backend->destroy = (CtxDestroyNotify)ctx_rasterizer_destroy; + backend->ctx = ctx; + rasterizer->edge_list.flags |= CTX_DRAWLIST_EDGE_LIST; + 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; + rasterizer->blit_y = y; + rasterizer->blit_width = width; + rasterizer->blit_height = height; + rasterizer->state->gstate.clip_min_x = x; + rasterizer->state->gstate.clip_min_y = y; + rasterizer->state->gstate.clip_max_x = x + width - 1; + rasterizer->state->gstate.clip_max_y = y + height - 1; + rasterizer->blit_stride = stride; + rasterizer->scan_min = 5000; + rasterizer->scan_max = -5000; + if (pixel_format == CTX_FORMAT_BGRA8) + { + pixel_format = CTX_FORMAT_RGBA8; + rasterizer->swap_red_green = 1; + } -#endif // CTX_COMPOSITE + rasterizer->format = ctx_pixel_format_info (pixel_format); -#endif // CTX_IMPLEMENTATION +#if CTX_GRADIENTS +#if CTX_GRADIENT_CACHE + rasterizer->gradient_cache_elements = CTX_GRADIENT_CACHE_ELEMENTS; + rasterizer->gradient_cache_valid = 0; +#endif +#endif -#if CTX_IMPLEMENTATION || CTX_SIMD_BUILD -#if CTX_COMPOSITE +#if static_OPAQUE + memset (rasterizer->opaque, 255, sizeof (rasterizer->opaque)); +#endif -#define CTX_AA_HALFSTEP2 (CTX_FULL_AA/2) -#define CTX_AA_HALFSTEP ((CTX_FULL_AA/2)+1) + return rasterizer; +} -CTX_INLINE static int ctx_compare_edges (const void *ap, const void *bp) +Ctx * +ctx_new_for_buffer (CtxBuffer *buffer) { - const CtxSegment *a = (const CtxSegment *) ap; - const CtxSegment *b = (const CtxSegment *) bp; - return a->data.s16[1] - b->data.s16[1]; + Ctx *ctx = _ctx_new_drawlist (buffer->width, buffer->height); + ctx_set_backend (ctx, + ctx_rasterizer_init ( (CtxRasterizer *) ctx_malloc (sizeof (CtxRasterizer) ), + ctx, NULL, &ctx->state, + buffer->data, 0, 0, buffer->width, buffer->height, + buffer->stride, buffer->format->pixel_format, + CTX_ANTIALIAS_DEFAULT)); + return ctx; } -CTX_INLINE static int ctx_edge_qsort_partition (CtxSegment *A, int low, int high) +Ctx * +ctx_new_for_framebuffer (void *data, int width, int height, + int stride, + CtxPixelFormat pixel_format) { - 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; + Ctx *ctx = _ctx_new_drawlist (width, height); + CtxRasterizer *r = ctx_rasterizer_init ( (CtxRasterizer *) ctx_calloc (sizeof (CtxRasterizer), 1), + ctx, NULL, &ctx->state, data, 0, 0, width, height, + stride, pixel_format, CTX_ANTIALIAS_DEFAULT); + ctx_set_backend (ctx, r); + if (pixel_format == CTX_FORMAT_GRAY1) // XXX we get some bugs without it.. + { // something is going amiss with offsets + ctx_set_antialias (ctx, CTX_ANTIALIAS_NONE); + } + return ctx; } -static inline void ctx_edge_qsort (CtxSegment *entries, int low, int high) +// ctx_new_for_stream (FILE *stream); + +#if 0 +CtxRasterizer *ctx_rasterizer_new (void *data, int x, int y, int width, int height, + int stride, CtxPixelFormat pixel_format) { - 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); } + CtxState *state = (CtxState *) ctx_malloc (sizeof (CtxState) ); + CtxRasterizer *rasterizer = (CtxRasterizer *) ctx_malloc (sizeof (CtxBackend) ); + ctx_rasterizer_init (rasterizer, state, data, x, y, width, height, + stride, pixel_format, CTX_ANTIALIAS_DEFAULT); } +#endif -static inline void ctx_rasterizer_sort_edges (CtxRasterizer *rasterizer) +#else + +#endif + + +void +ctx_state_gradient_clear_stops (CtxState *state) { - ctx_edge_qsort ((CtxSegment*)& (rasterizer->edge_list.entries[0]), 0, rasterizer->edge_list.count-1); + state->gradient.n_stops = 0; } -static inline void ctx_rasterizer_discard_edges (CtxRasterizer *rasterizer) + +/**** end of engine ****/ +//#include "miniz.h" +/************************************************************************** + * + * Copyright 2013-2014 RAD Game Tools and Valve Software + * Copyright 2010-2014 Rich Geldreich and Tenacious Software LLC + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + **************************************************************************/ + + + +typedef unsigned char mz_validate_uint16[sizeof(mz_uint16) == 2 ? 1 : -1]; +typedef unsigned char mz_validate_uint32[sizeof(mz_uint32) == 4 ? 1 : -1]; +typedef unsigned char mz_validate_uint64[sizeof(mz_uint64) == 8 ? 1 : -1]; + +#ifdef __cplusplus +extern "C" { +#endif + +/* ------------------- zlib-style API's */ + +mz_ulong mz_adler32(mz_ulong adler, const unsigned char *ptr, size_t buf_len) { - int scanline = rasterizer->scanline; - int next_scanline = scanline + CTX_FULL_AA; - int limit3 = CTX_RASTERIZER_AA_SLOPE_LIMIT3_FAST_AA; - CtxSegment *segments = &((CtxSegment*)(rasterizer->edge_list.entries))[0]; - int *edges = rasterizer->edges; - for (unsigned int i = 0; i < rasterizer->active_edges; i++) + mz_uint32 i, s1 = (mz_uint32)(adler & 0xffff), s2 = (mz_uint32)(adler >> 16); + size_t block_len = buf_len % 5552; + if (!ptr) + return MZ_ADLER32_INIT; + while (buf_len) { - CtxSegment *segment = segments + edges[i]; - int edge_end = segment->data.s16[3]-1; - if (edge_end < scanline) + for (i = 0; i + 7 < block_len; i += 8, ptr += 8) { - - int dx_dy = abs(segment->delta); - rasterizer->needs_aa3 -= (dx_dy > limit3); - rasterizer->needs_aa5 -= (dx_dy > CTX_RASTERIZER_AA_SLOPE_LIMIT5); - rasterizer->needs_aa15 -= (dx_dy > CTX_RASTERIZER_AA_SLOPE_LIMIT15); - rasterizer->edges[i] = rasterizer->edges[rasterizer->active_edges-1]; - rasterizer->active_edges--; - i--; + s1 += ptr[0], s2 += s1; + s1 += ptr[1], s2 += s1; + s1 += ptr[2], s2 += s1; + s1 += ptr[3], s2 += s1; + s1 += ptr[4], s2 += s1; + s1 += ptr[5], s2 += s1; + s1 += ptr[6], s2 += s1; + s1 += ptr[7], s2 += s1; } - else if (edge_end < next_scanline) - rasterizer->ending_edges++; - } -#if 0 - // perhaps we should - but for 99% of the cases we do not need to, so we skip it - for (int i = 0; i < rasterizer->pending_edges; i++) - { - int edge_end = ((CtxSegment*)(rasterizer->edge_list.entries))[rasterizer->edges[CTX_MAX_EDGES-1-i]].data.s16[3]-1; - if (edge_end < scanline + CTX_FULL_AA) - rasterizer->ending_edges++; + for (; i < block_len; ++i) + s1 += *ptr++, s2 += s1; + s1 %= 65521U, s2 %= 65521U; + buf_len -= block_len; + block_len = 5552; } -#endif + return (s2 << 16) + s1; } -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; - unsigned int pending_base = CTX_MAX_EDGES-pending_edges; - for (unsigned int i = 0; i < active_edges; i++) +/* Karl Malbrain's compact CRC-32. See "A compact CCITT crc16 and crc32 C implementation that balances processor cache usage against speed": http://www.geocities.com/malbrain/ */ +#if 0 + mz_ulong mz_crc32(mz_ulong crc, const mz_uint8 *ptr, size_t buf_len) + { + static const mz_uint32 s_crc32[16] = { 0, 0x1db71064, 0x3b6e20c8, 0x26d930ac, 0x76dc4190, 0x6b6b51f4, 0x4db26158, 0x5005713c, + 0xedb88320, 0xf00f9344, 0xd6d6a3e8, 0xcb61b38c, 0x9b64c2b0, 0x86d3d2d4, 0xa00ae278, 0xbdbdf21c }; + mz_uint32 crcu32 = (mz_uint32)crc; + if (!ptr) + return MZ_CRC32_INIT; + crcu32 = ~crcu32; + while (buf_len--) + { + mz_uint8 b = *ptr++; + crcu32 = (crcu32 >> 4) ^ s_crc32[(crcu32 & 0xF) ^ (b & 0xF)]; + crcu32 = (crcu32 >> 4) ^ s_crc32[(crcu32 & 0xF) ^ (b >> 4)]; + } + return ~crcu32; + } +#elif defined(USE_EXTERNAL_MZCRC) +/* If USE_EXTERNAL_CRC is defined, an external module will export the + * mz_crc32() symbol for us to use, e.g. an SSE-accelerated version. + * Depending on the impl, it may be necessary to ~ the input/output crc values. + */ +mz_ulong mz_crc32(mz_ulong crc, const mz_uint8 *ptr, size_t buf_len); +#else +/* Faster, but larger CPU cache footprint. + */ +mz_ulong mz_crc32(mz_ulong crc, const mz_uint8 *ptr, size_t buf_len) +{ + static const mz_uint32 s_crc_table[256] = + { + 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, 0xE963A535, + 0x9E6495A3, 0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD, + 0xE7B82D07, 0x90BF1D91, 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, 0x1ADAD47D, + 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, + 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5, 0x3B6E20C8, 0x4C69105E, 0xD56041E4, + 0xA2677172, 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, 0x35B5A8FA, 0x42B2986C, + 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59, 0x26D930AC, + 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F, + 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, 0x2F6F7C87, 0x58684C11, 0xC1611DAB, + 0xB6662D3D, 0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, + 0x9FBFE4A5, 0xE8B8D433, 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, + 0x086D3D2D, 0x91646C97, 0xE6635C01, 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, + 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, + 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65, 0x4DB26158, 0x3AB551CE, + 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB, 0x4369E96A, + 0x346ED9FC, 0xAD678846, 0xDA60B8D0, 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, + 0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, + 0xCE61E49F, 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81, + 0xB7BD5C3B, 0xC0BA6CAD, 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, 0xEAD54739, + 0x9DD277AF, 0x04DB2615, 0x73DC1683, 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, + 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1, 0xF00F9344, 0x8708A3D2, 0x1E01F268, + 0x6906C2FE, 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7, 0xFED41B76, 0x89D32BE0, + 0x10DA7A5A, 0x67DD4ACC, 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, 0xD6D6A3E8, + 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B, + 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, + 0x4669BE79, 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, 0xCC0C7795, 0xBB0B4703, + 0x220216B9, 0x5505262F, 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, + 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D, 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, + 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713, 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, + 0x0CB61B38, 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, 0x86D3D2D4, 0xF1D4E242, + 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777, 0x88085AE6, + 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45, + 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, 0xA7672661, 0xD06016F7, 0x4969474D, + 0x3E6E77DB, 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, + 0x47B2CF7F, 0x30B5FFE9, 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, + 0xCDD70693, 0x54DE5729, 0x23D967BF, 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, + 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D + }; + + mz_uint32 crc32 = (mz_uint32)crc ^ 0xFFFFFFFF; + const mz_uint8 *pByte_buf = (const mz_uint8 *)ptr; + + while (buf_len >= 4) { - CtxSegment *segment = segments + rasterizer->edges[i]; - segment->val += segment->delta * count; + crc32 = (crc32 >> 8) ^ s_crc_table[(crc32 ^ pByte_buf[0]) & 0xFF]; + crc32 = (crc32 >> 8) ^ s_crc_table[(crc32 ^ pByte_buf[1]) & 0xFF]; + crc32 = (crc32 >> 8) ^ s_crc_table[(crc32 ^ pByte_buf[2]) & 0xFF]; + crc32 = (crc32 >> 8) ^ s_crc_table[(crc32 ^ pByte_buf[3]) & 0xFF]; + pByte_buf += 4; + buf_len -= 4; } - for (unsigned int i = 0; i < pending_edges; i++) + + while (buf_len) { - CtxSegment *segment = segments + rasterizer->edges[pending_base+i]; - segment->val += segment->delta * count; + crc32 = (crc32 >> 8) ^ s_crc_table[(crc32 ^ pByte_buf[0]) & 0xFF]; + ++pByte_buf; + --buf_len; } + + return ~crc32; } +#endif -/* 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) +void mz_free(void *p) { - for(unsigned int i=1; i<count; i++) - { - int temp = entries[i]; - int j = i-1; - while (j >= 0 && segments[temp].val - segments[entries[j]].val < 0) - { - entries[j+1] = entries[j]; - j--; - } - entries[j+1] = temp; - } + MZ_FREE(p); } -inline static int ctx_edge2_compare2 (CtxSegment *segments, int a, int b) +MINIZ_EXPORT void *miniz_def_alloc_func(void *opaque, size_t items, size_t size) { - CtxSegment *seg_a = &segments[a]; - CtxSegment *seg_b = &segments[b]; - int minval_a = ctx_mini (seg_a->val - seg_a->delta * CTX_AA_HALFSTEP2, seg_a->val + seg_a->delta * CTX_AA_HALFSTEP); - int minval_b = ctx_mini (seg_b->val - seg_b->delta * CTX_AA_HALFSTEP2, seg_b->val + seg_b->delta * CTX_AA_HALFSTEP); - return minval_a - minval_b; + (void)opaque, (void)items, (void)size; + return MZ_MALLOC(items * size); } - -inline static void ctx_edge2_insertion_sort2 (CtxSegment *segments, int *entries, unsigned int count) +MINIZ_EXPORT void miniz_def_free_func(void *opaque, void *address) { - for(unsigned int i=1; i<count; i++) - { - int temp = entries[i]; - int j = i-1; - while (j >= 0 && ctx_edge2_compare2 (segments, temp, entries[j]) < 0) - { - entries[j+1] = entries[j]; - j--; - } - entries[j+1] = temp; - } + (void)opaque, (void)address; + MZ_FREE(address); +} +MINIZ_EXPORT void *miniz_def_realloc_func(void *opaque, void *address, size_t items, size_t size) +{ + (void)opaque, (void)address, (void)items, (void)size; + return MZ_REALLOC(address, items * size); } -inline static void ctx_rasterizer_feed_edges (CtxRasterizer *rasterizer, int apply2_sort) +const char *mz_version(void) { - int miny; - CtxSegment *__restrict__ entries = (CtxSegment*)&rasterizer->edge_list.entries[0]; - int *edges = rasterizer->edges; - unsigned int pending_edges = rasterizer->pending_edges; - rasterizer->horizontal_edges = 0; - rasterizer->ending_edges = 0; - for (unsigned int i = 0; i < pending_edges; i++) - { - if (entries[rasterizer->edges[CTX_MAX_EDGES-1-i]].data.s16[1] - 1 <= rasterizer->scanline && - rasterizer->active_edges < CTX_MAX_EDGES-2) - { - unsigned int no = rasterizer->active_edges; - rasterizer->active_edges++; - edges[no] = edges[CTX_MAX_EDGES-1-i]; - edges[CTX_MAX_EDGES-1-i] = - edges[CTX_MAX_EDGES-1-pending_edges + 1]; - pending_edges--; - i--; - } - } - int limit3 = CTX_RASTERIZER_AA_SLOPE_LIMIT3_FAST_AA; - int scanline = rasterizer->scanline; - int next_scanline = scanline + CTX_FULL_AA; - unsigned int edge_pos = rasterizer->edge_pos; - unsigned int edge_count = rasterizer->edge_list.count; - while ((edge_pos < edge_count && - (miny=entries[edge_pos].data.s16[1]-1) <= next_scanline)) - { - if (rasterizer->active_edges < CTX_MAX_EDGES-2 && - entries[edge_pos].data.s16[3]-1 /* (maxy) */ >= scanline) - { - int dy = (entries[edge_pos].data.s16[3] - 1 - miny); - if (dy) - { - int yd = scanline - miny; - unsigned int no = rasterizer->active_edges; - rasterizer->active_edges++; - unsigned int index = edges[no] = edge_pos; - int x0 = entries[index].data.s16[0]; - int x1 = entries[index].data.s16[2]; - int dx_dy = CTX_RASTERIZER_EDGE_MULTIPLIER * (x1 - x0) / dy; - entries[index].delta = dx_dy; - entries[index].val = x0 * CTX_RASTERIZER_EDGE_MULTIPLIER + - (yd * dx_dy); + return MZ_VERSION; +} - { - int abs_dx_dy = abs(dx_dy); - rasterizer->needs_aa3 += (abs_dx_dy > limit3); - rasterizer->needs_aa5 += (abs_dx_dy > CTX_RASTERIZER_AA_SLOPE_LIMIT5); - rasterizer->needs_aa15 += (abs_dx_dy > CTX_RASTERIZER_AA_SLOPE_LIMIT15); - } +#ifndef MINIZ_NO_ZLIB_APIS - 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[no]; - pending_edges++; - rasterizer->active_edges--; - } - } - else - rasterizer->horizontal_edges ++; - } - edge_pos++; - } - rasterizer->pending_edges = pending_edges; - rasterizer->edge_pos = edge_pos; - ctx_rasterizer_discard_edges (rasterizer); - if (apply2_sort) - ctx_edge2_insertion_sort2 ((CtxSegment*)rasterizer->edge_list.entries, rasterizer->edges, rasterizer->active_edges); - else - ctx_edge2_insertion_sort ((CtxSegment*)rasterizer->edge_list.entries, rasterizer->edges, rasterizer->active_edges); +#ifndef MINIZ_NO_DEFLATE_APIS + +int mz_deflateInit(mz_streamp pStream, int level) +{ + return mz_deflateInit2(pStream, level, MZ_DEFLATED, MZ_DEFAULT_WINDOW_BITS, 9, MZ_DEFAULT_STRATEGY); } -#undef CTX_CMPSWP -static inline void ctx_coverage_post_process (CtxRasterizer *rasterizer, unsigned int minx, unsigned int maxx, uint8_t *coverage, int *first_col, int *last_col) +int mz_deflateInit2(mz_streamp pStream, int level, int method, int window_bits, int mem_level, int strategy) { -#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; - { - 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; - } - } -#endif + tdefl_compressor *pComp; + mz_uint comp_flags = TDEFL_COMPUTE_ADLER32 | tdefl_create_comp_flags_from_zip_params(level, window_bits, strategy); -#if CTX_ENABLE_CLIP - if (CTX_UNLIKELY(rasterizer->clip_buffer && !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; - uint8_t *clip_line = &((uint8_t*)(rasterizer->clip_buffer->data))[rasterizer->blit_width*y]; - // XXX SIMD candidate - for (unsigned int x = minx; x <= maxx; x ++) + if (!pStream) + return MZ_STREAM_ERROR; + if ((method != MZ_DEFLATED) || ((mem_level < 1) || (mem_level > 9)) || ((window_bits != MZ_DEFAULT_WINDOW_BITS) && (-window_bits != MZ_DEFAULT_WINDOW_BITS))) + return MZ_PARAM_ERROR; + + pStream->data_type = 0; + pStream->adler = MZ_ADLER32_INIT; + pStream->msg = NULL; + pStream->reserved = 0; + pStream->total_in = 0; + pStream->total_out = 0; + if (!pStream->zalloc) + pStream->zalloc = miniz_def_alloc_func; + if (!pStream->zfree) + pStream->zfree = miniz_def_free_func; + + pComp = (tdefl_compressor *)pStream->zalloc(pStream->opaque, 1, sizeof(tdefl_compressor)); + if (!pComp) + return MZ_MEM_ERROR; + + pStream->state = (struct mz_internal_state *)pComp; + + if (tdefl_init(pComp, NULL, NULL, comp_flags) != TDEFL_STATUS_OKAY) { -#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; -#endif + mz_deflateEnd(pStream); + return MZ_PARAM_ERROR; } - } -#endif + + return MZ_OK; } -#define CTX_EDGE(no) entries[edges[no]] -#define CTX_EDGE_YMIN (segment->data.s16[1]-1) +int mz_deflateReset(mz_streamp pStream) +{ + if ((!pStream) || (!pStream->state) || (!pStream->zalloc) || (!pStream->zfree)) + return MZ_STREAM_ERROR; + pStream->total_in = pStream->total_out = 0; + tdefl_init((tdefl_compressor *)pStream->state, NULL, NULL, ((tdefl_compressor *)pStream->state)->m_flags); + return MZ_OK; +} -#define UPDATE_PARITY \ - if (CTX_LIKELY(scanline!=CTX_EDGE_YMIN))\ - { \ - if (is_winding)\ - parity = parity + -1+2*(segment->code == CTX_EDGE_FLIPPED);\ - else\ - parity = 1-parity; \ - } +int mz_deflate(mz_streamp pStream, int flush) +{ + size_t in_bytes, out_bytes; + mz_ulong orig_total_in, orig_total_out; + int mz_status = MZ_OK; + if ((!pStream) || (!pStream->state) || (flush < 0) || (flush > MZ_FINISH) || (!pStream->next_out)) + return MZ_STREAM_ERROR; + if (!pStream->avail_out) + return MZ_BUF_ERROR; -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) -{ - 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 (flush == MZ_PARTIAL_FLUSH) + flush = MZ_SYNC_FLUSH; - 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 (((tdefl_compressor *)pStream->state)->m_prev_return_status == TDEFL_STATUS_DONE) + return (flush == MZ_FINISH) ? MZ_STREAM_END : MZ_BUF_ERROR; - if (CTX_UNLIKELY (first < minx)) - { - first = minx; - graystart=0; - } - if (CTX_UNLIKELY (last > maxx)) - { - last = maxx; - grayend=255; - } + orig_total_in = pStream->total_in; + orig_total_out = pStream->total_out; + for (;;) + { + tdefl_status defl_status; + in_bytes = pStream->avail_in; + out_bytes = pStream->avail_out; - graystart = fraction- (graystart&0xff)/aa_factor; - grayend = (grayend & 0xff) / aa_factor; + defl_status = tdefl_compress((tdefl_compressor *)pStream->state, pStream->next_in, &in_bytes, pStream->next_out, &out_bytes, (tdefl_flush)flush); + pStream->next_in += (mz_uint)in_bytes; + pStream->avail_in -= (mz_uint)in_bytes; + pStream->total_in += (mz_uint)in_bytes; + pStream->adler = tdefl_get_adler32((tdefl_compressor *)pStream->state); - if (first < last) - { - coverage[first] += graystart; - for (int x = first + 1; x < last; x++) - coverage[x] += fraction; - coverage[last] += grayend; - } - else if (first == last) - coverage[first] += (graystart-fraction+grayend); + pStream->next_out += (mz_uint)out_bytes; + pStream->avail_out -= (mz_uint)out_bytes; + pStream->total_out += (mz_uint)out_bytes; + + if (defl_status < 0) + { + mz_status = MZ_STREAM_ERROR; + break; } - } + else if (defl_status == TDEFL_STATUS_DONE) + { + mz_status = MZ_STREAM_END; + break; + } + else if (!pStream->avail_out) + break; + else if ((!pStream->avail_in) && (flush != MZ_FINISH)) + { + if ((flush) || (pStream->total_in != orig_total_in) || (pStream->total_out != orig_total_out)) + break; + return MZ_BUF_ERROR; /* Can't make forward progress without some input. + */ + } + } + return mz_status; } -inline static void -ctx_rasterizer_generate_coverage_set (CtxRasterizer *rasterizer, - int minx, - int maxx, - uint8_t *coverage, - int is_winding) +int mz_deflateEnd(mz_streamp pStream) { - 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++) + if (!pStream) + return MZ_STREAM_ERROR; + if (pStream->state) { - 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; + pStream->zfree(pStream->opaque, pStream->state); + pStream->state = NULL; + } + return MZ_OK; +} - if (CTX_UNLIKELY (first < minx)) - { - first = minx; - graystart=0; - } - if (CTX_UNLIKELY (last > maxx)) - { - last = maxx; - grayend=255; - } +mz_ulong mz_deflateBound(mz_streamp pStream, mz_ulong source_len) +{ + (void)pStream; + /* This is really over conservative. (And lame, but it's actually pretty tricky to compute a true upper bound given the way tdefl's blocking works.) */ + return MZ_MAX(128 + (source_len * 110) / 100, 128 + source_len + ((source_len / (31 * 1024)) + 1) * 5); +} - graystart = (graystart&0xff) ^ 255; - grayend = (grayend & 0xff); +int mz_compress2(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len, int level) +{ + int status; + mz_stream stream; + memset(&stream, 0, sizeof(stream)); - coverage[first] += graystart; - coverage[last] += grayend; - if (first + 1< last) - memset(&coverage[first+1], 255, last-(first+1)); - } - } -} + /* In case mz_ulong is 64-bits (argh I hate longs). */ + if ((mz_uint64)(source_len | *pDest_len) > 0xFFFFFFFFU) + return MZ_PARAM_ERROR; + stream.next_in = pSource; + stream.avail_in = (mz_uint32)source_len; + stream.next_out = pDest; + stream.avail_out = (mz_uint32)*pDest_len; -inline static void -ctx_rasterizer_generate_coverage_apply (CtxRasterizer *rasterizer, - int minx, - int maxx, - uint8_t* __restrict__ coverage, - int is_winding, - CtxCovPath comp) -{ - CtxSegment *entries = (CtxSegment*)(&rasterizer->edge_list.entries[0]); - 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; - } - else - { - src_pix = - si_ga = - si_rb = - si_ga_full = - si_rb_full = - si_a = 0; - src_pixp = &src_pix; - } -#endif + status = mz_deflateInit(&stream, level); + if (status != MZ_OK) + return status; - 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++) + status = mz_deflate(&stream, MZ_FINISH); + if (status != MZ_STREAM_END) { - CtxSegment *segment = &entries[edges[t]]; - UPDATE_PARITY; + mz_deflateEnd(&stream); + return (status == MZ_OK) ? MZ_BUF_ERROR : status; + } - if (parity) + *pDest_len = stream.total_out; + return mz_deflateEnd(&stream); +} + +int mz_compress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len) +{ + return mz_compress2(pDest, pDest_len, pSource, source_len, MZ_DEFAULT_COMPRESSION); +} + +mz_ulong mz_compressBound(mz_ulong source_len) +{ + return mz_deflateBound(NULL, source_len); +} + +#endif /*#ifndef MINIZ_NO_DEFLATE_APIS*/ + +#ifndef MINIZ_NO_INFLATE_APIS + +typedef struct +{ + tinfl_decompressor m_decomp; + mz_uint m_dict_ofs, m_dict_avail, m_first_call, m_has_flushed; + int m_window_bits; + mz_uint8 m_dict[TINFL_LZ_DICT_SIZE]; + tinfl_status m_last_status; +} inflate_state; + +int mz_inflateInit2(mz_streamp pStream, int window_bits) +{ + inflate_state *pDecomp; + if (!pStream) + return MZ_STREAM_ERROR; + if ((window_bits != MZ_DEFAULT_WINDOW_BITS) && (-window_bits != MZ_DEFAULT_WINDOW_BITS)) + return MZ_PARAM_ERROR; + + pStream->data_type = 0; + pStream->adler = 0; + pStream->msg = NULL; + pStream->total_in = 0; + pStream->total_out = 0; + pStream->reserved = 0; + if (!pStream->zalloc) + pStream->zalloc = miniz_def_alloc_func; + if (!pStream->zfree) + pStream->zfree = miniz_def_free_func; + + pDecomp = (inflate_state *)pStream->zalloc(pStream->opaque, 1, sizeof(inflate_state)); + if (!pDecomp) + return MZ_MEM_ERROR; + + pStream->state = (struct mz_internal_state *)pDecomp; + + tinfl_init(&pDecomp->m_decomp); + pDecomp->m_dict_ofs = 0; + pDecomp->m_dict_avail = 0; + pDecomp->m_last_status = TINFL_STATUS_NEEDS_MORE_INPUT; + pDecomp->m_first_call = 1; + pDecomp->m_has_flushed = 0; + pDecomp->m_window_bits = window_bits; + + return MZ_OK; +} + +int mz_inflateInit(mz_streamp pStream) +{ + return mz_inflateInit2(pStream, MZ_DEFAULT_WINDOW_BITS); +} + +int mz_inflateReset(mz_streamp pStream) +{ + inflate_state *pDecomp; + if (!pStream) + return MZ_STREAM_ERROR; + + pStream->data_type = 0; + pStream->adler = 0; + pStream->msg = NULL; + pStream->total_in = 0; + pStream->total_out = 0; + pStream->reserved = 0; + + pDecomp = (inflate_state *)pStream->state; + + tinfl_init(&pDecomp->m_decomp); + pDecomp->m_dict_ofs = 0; + pDecomp->m_dict_avail = 0; + pDecomp->m_last_status = TINFL_STATUS_NEEDS_MORE_INPUT; + pDecomp->m_first_call = 1; + pDecomp->m_has_flushed = 0; + /* pDecomp->m_window_bits = window_bits */; + + return MZ_OK; +} + +int mz_inflate(mz_streamp pStream, int flush) +{ + inflate_state *pState; + mz_uint n, first_call, decomp_flags = TINFL_FLAG_COMPUTE_ADLER32; + size_t in_bytes, out_bytes, orig_avail_in; + tinfl_status status; + + if ((!pStream) || (!pStream->state)) + return MZ_STREAM_ERROR; + if (flush == MZ_PARTIAL_FLUSH) + flush = MZ_SYNC_FLUSH; + if ((flush) && (flush != MZ_SYNC_FLUSH) && (flush != MZ_FINISH)) + return MZ_STREAM_ERROR; + + pState = (inflate_state *)pStream->state; + if (pState->m_window_bits > 0) + decomp_flags |= TINFL_FLAG_PARSE_ZLIB_HEADER; + orig_avail_in = pStream->avail_in; + + first_call = pState->m_first_call; + pState->m_first_call = 0; + if (pState->m_last_status < 0) + return MZ_DATA_ERROR; + + if (pState->m_has_flushed && (flush != MZ_FINISH)) + return MZ_STREAM_ERROR; + pState->m_has_flushed |= (flush == MZ_FINISH); + + if ((flush == MZ_FINISH) && (first_call)) + { + /* MZ_FINISH on the first call implies that the input and output buffers are large enough to hold the entire compressed/decompressed file. */ + decomp_flags |= TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF; + in_bytes = pStream->avail_in; + out_bytes = pStream->avail_out; + status = tinfl_decompress(&pState->m_decomp, pStream->next_in, &in_bytes, pStream->next_out, pStream->next_out, &out_bytes, decomp_flags); + pState->m_last_status = status; + pStream->next_in += (mz_uint)in_bytes; + pStream->avail_in -= (mz_uint)in_bytes; + pStream->total_in += (mz_uint)in_bytes; + pStream->adler = tinfl_get_adler32(&pState->m_decomp); + pStream->next_out += (mz_uint)out_bytes; + pStream->avail_out -= (mz_uint)out_bytes; + pStream->total_out += (mz_uint)out_bytes; + + if (status < 0) + return MZ_DATA_ERROR; + else if (status != TINFL_STATUS_DONE) { - CtxSegment *next_segment = &entries[edges[t+1]]; - const int x0 = segment->val; - const int x1 = next_segment->val; + pState->m_last_status = TINFL_STATUS_FAILED; + return MZ_BUF_ERROR; + } + return MZ_STREAM_END; + } + /* flush != MZ_FINISH then we must assume there's more input. */ + if (flush != MZ_FINISH) + decomp_flags |= TINFL_FLAG_HAS_MORE_INPUT; - 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; + if (pState->m_dict_avail) + { + n = MZ_MIN(pState->m_dict_avail, pStream->avail_out); + memcpy(pStream->next_out, pState->m_dict + pState->m_dict_ofs, n); + pStream->next_out += n; + pStream->avail_out -= n; + pStream->total_out += n; + pState->m_dict_avail -= n; + pState->m_dict_ofs = (pState->m_dict_ofs + n) & (TINFL_LZ_DICT_SIZE - 1); + return ((pState->m_last_status == TINFL_STATUS_DONE) && (!pState->m_dict_avail)) ? MZ_STREAM_END : MZ_OK; + } - if (CTX_UNLIKELY(first < minx)) - { - graystart = 0; - first = minx; - } - if (CTX_UNLIKELY(last > maxx)) - { - last = maxx; - grayend=255; - } - graystart = (graystart&0xff) ^ 255; + for (;;) + { + in_bytes = pStream->avail_in; + out_bytes = TINFL_LZ_DICT_SIZE - pState->m_dict_ofs; + + status = tinfl_decompress(&pState->m_decomp, pStream->next_in, &in_bytes, pState->m_dict, pState->m_dict + pState->m_dict_ofs, &out_bytes, decomp_flags); + pState->m_last_status = status; + + pStream->next_in += (mz_uint)in_bytes; + pStream->avail_in -= (mz_uint)in_bytes; + pStream->total_in += (mz_uint)in_bytes; + pStream->adler = tinfl_get_adler32(&pState->m_decomp); + + pState->m_dict_avail = (mz_uint)out_bytes; + + n = MZ_MIN(pState->m_dict_avail, pStream->avail_out); + memcpy(pStream->next_out, pState->m_dict + pState->m_dict_ofs, n); + pStream->next_out += n; + pStream->avail_out -= n; + pStream->total_out += n; + pState->m_dict_avail -= n; + pState->m_dict_ofs = (pState->m_dict_ofs + n) & (TINFL_LZ_DICT_SIZE - 1); + + if (status < 0) + return MZ_DATA_ERROR; /* Stream is corrupted (there could be some uncompressed data left in the output dictionary - oh well). */ + else if ((status == TINFL_STATUS_NEEDS_MORE_INPUT) && (!orig_avail_in)) + return MZ_BUF_ERROR; /* Signal caller that we can't make forward progress without supplying more input or by setting flush to MZ_FINISH. */ + else if (flush == MZ_FINISH) + { + /* The output buffer MUST be large to hold the remaining uncompressed data when flush==MZ_FINISH. */ + if (status == TINFL_STATUS_DONE) + return pState->m_dict_avail ? MZ_BUF_ERROR : MZ_STREAM_END; + /* status here must be TINFL_STATUS_HAS_MORE_OUTPUT, which means there's at least 1 more byte on the way. If there's no more room left in the output buffer then something is wrong. */ + else if (!pStream->avail_out) + return MZ_BUF_ERROR; + } + else if ((status == TINFL_STATUS_DONE) || (!pStream->avail_in) || (!pStream->avail_out) || (pState->m_dict_avail)) + break; + } - grayend = (grayend & 0xff); + return ((status == TINFL_STATUS_DONE) && (!pState->m_dict_avail)) ? MZ_STREAM_END : MZ_OK; +} - 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: - rasterizer->apply_coverage (rasterizer, (uint8_t*)dst_pix, rasterizer->color, accumulator_x, &accumulated, 1); - } - } - accumulated = 0; - } +int mz_inflateEnd(mz_streamp pStream) +{ + if (!pStream) + return MZ_STREAM_ERROR; + if (pStream->state) + { + pStream->zfree(pStream->opaque, pStream->state); + pStream->state = NULL; + } + return MZ_OK; +} +int mz_uncompress2(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong *pSource_len) +{ + mz_stream stream; + int status; + memset(&stream, 0, sizeof(stream)); - 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); + /* In case mz_ulong is 64-bits (argh I hate longs). */ + if ((mz_uint64)(*pSource_len | *pDest_len) > 0xFFFFFFFFU) + return MZ_PARAM_ERROR; - dst_pix++; - ctx_span_set_colorb (dst_pix, src_pix, last - first - 1); - } - break; -#if CTX_ENABLE_CBRLE - case CTX_COV_PATH_CBRLE_COPY: - { - uint8_t* dsts = (uint8_t*)(&dst[(first *bpp)/8]); - uint8_t startcov = graystart; - rasterizer->apply_coverage (rasterizer, (uint8_t*)dsts, rasterizer->color, first, &startcov, 1); + stream.next_in = pSource; + stream.avail_in = (mz_uint32)*pSource_len; + stream.next_out = pDest; + stream.avail_out = (mz_uint32)*pDest_len; - if (last-(first+1) > 0) - ctx_CBRLE_compress (rasterizer->color, - dst, - rasterizer->blit_width, - rasterizer->blit_stride, - first+1, last-(first+1), - CBRLE_MODE_SET_COLOR, NULL, 1); + status = mz_inflateInit(&stream); + if (status != MZ_OK) + return status; - } - break; -#endif - 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; - rasterizer->apply_coverage (rasterizer, (uint8_t*)dsts, rasterizer->color, 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; + status = mz_inflate(&stream, MZ_FINISH); + *pSource_len = *pSource_len - stream.avail_in; + if (status != MZ_STREAM_END) + { + mz_inflateEnd(&stream); + return ((status == MZ_BUF_ERROR) && (!stream.avail_in)) ? MZ_DATA_ERROR : status; + } + *pDest_len = stream.total_out; - unsigned int count = last-(first+1);// (last - post) - (first+pre) + 1; + return mz_inflateEnd(&stream); +} - //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); -#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]; - 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; +int mz_uncompress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len) +{ + return mz_uncompress2(pDest, pDest_len, pSource, &source_len); +} -#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; - rasterizer->apply_coverage (rasterizer, (uint8_t*)dstp, rasterizer->color, first, &startcov, 1); - dstp = (uint8_t*)(&dst[((first+1)*bpp)/8]); - unsigned int count = last - first - 1; - int val = srcp[0]/17; +#endif /*#ifndef MINIZ_NO_INFLATE_APIS*/ - 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++; - } +const char *mz_error(int err) +{ + static struct + { + int m_err; + const char *m_pDesc; + } s_error_descs[] = + { + { MZ_OK, "" }, { MZ_STREAM_END, "stream end" }, { MZ_NEED_DICT, "need dictionary" }, { MZ_ERRNO, "file error" }, { MZ_STREAM_ERROR, "stream error" }, { MZ_DATA_ERROR, "data error" }, { MZ_MEM_ERROR, "out of memory" }, { MZ_BUF_ERROR, "buf error" }, { MZ_VERSION_ERROR, "version error" }, { MZ_PARAM_ERROR, "parameter error" } + }; + mz_uint i; + for (i = 0; i < sizeof(s_error_descs) / sizeof(s_error_descs[0]); ++i) + if (s_error_descs[i].m_err == err) + return s_error_descs[i].m_pDesc; + return NULL; +} - for (unsigned int i = 0; i < count && count>2; count-=2, x+=2, dstp++) - *dstp = val_x2; +#endif /*MINIZ_NO_ZLIB_APIS */ - 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; +#ifdef __cplusplus +} #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; - rasterizer->apply_coverage (rasterizer, (uint8_t*)dstp, rasterizer->color, first, &startcov, 1); - dstp = (uint8_t*)(&dst[((first+1)*bpp)/8]); - unsigned int count = last - first - 1; - int val = srcp[0]/85; +/* + This is free and unencumbered software released into the public domain. + + Anyone is free to copy, modify, publish, use, compile, sell, or + distribute this software, either in source code form or as a compiled + binary, for any purpose, commercial or non-commercial, and by any + means. + + In jurisdictions that recognize copyright laws, the author or authors + of this software dedicate any and all copyright interest in the + software to the public domain. We make this dedication for the benefit + of the public at large and to the detriment of our heirs and + successors. We intend this dedication to be an overt act of + relinquishment in perpetuity of all present and future rights to this + software under copyright law. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR + OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + OTHER DEALINGS IN THE SOFTWARE. + + For more information, please refer to <http://unlicense.org/> +*/ +/************************************************************************** + * + * Copyright 2013-2014 RAD Game Tools and Valve Software + * Copyright 2010-2014 Rich Geldreich and Tenacious Software LLC + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + **************************************************************************/ - 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++; - } - 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; +#ifndef MINIZ_NO_DEFLATE_APIS + +#ifdef __cplusplus +extern "C" { #endif -#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; - rasterizer->apply_coverage (rasterizer, (uint8_t*)dstp, rasterizer->color, 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<<bitno); - dstp += (bitno == 7); - x++; - } +/* ------------------- Low-level Compression (independent from all decompression API's) */ - for (unsigned int i = 0; i < count && count>8; count-=8) - { - *dstp = 255; - dstp++; - x+=8; - } +/* Purposely making these tables static for faster init and thread safety. */ +static const mz_uint16 s_tdefl_len_sym[256] = + { + 257, 258, 259, 260, 261, 262, 263, 264, 265, 265, 266, 266, 267, 267, 268, 268, 269, 269, 269, 269, 270, 270, 270, 270, 271, 271, 271, 271, 272, 272, 272, 272, + 273, 273, 273, 273, 273, 273, 273, 273, 274, 274, 274, 274, 274, 274, 274, 274, 275, 275, 275, 275, 275, 275, 275, 275, 276, 276, 276, 276, 276, 276, 276, 276, + 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, + 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, + 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, + 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, + 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, + 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 285 + }; - for (unsigned int i = 0; i < count; i++) - { - int bitno = x & 7; - *dstp |= (1<<bitno); - dstp += (bitno == 7); - x++; - } - } - else - { - unsigned int x = first + 1; - for (unsigned int i = 0; i < count && x & 7; count--) - { - int bitno = x & 7; - *dstp &= ~(1<<bitno); - dstp += (bitno == 7); - x++; - } +static const mz_uint8 s_tdefl_len_extra[256] = + { + 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0 + }; - for (unsigned int i = 0; i < count && count>8; count-=8) - { - *dstp = 0; - dstp++; - x+=8; - } +static const mz_uint8 s_tdefl_small_dist_sym[512] = + { + 0, 1, 2, 3, 4, 4, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17 + }; - for (unsigned int i = 0; i < count; i++) - { - int bitno = x & 7; - *dstp &= ~(1<<bitno); - dstp += (bitno == 7); - x++; - } +static const mz_uint8 s_tdefl_small_dist_extra[512] = + { + 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7 + }; - } - } - break; -#endif +static const mz_uint8 s_tdefl_large_dist_sym[128] = + { + 0, 0, 18, 19, 20, 20, 21, 21, 22, 22, 22, 22, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 25, 25, 25, 25, 25, 25, 25, 25, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, + 28, 28, 28, 28, 28, 28, 28, 28, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29 + }; - case CTX_COV_PATH_RGBA8_OVER: +static const mz_uint8 s_tdefl_large_dist_extra[128] = + { + 0, 0, 8, 8, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13 + }; + +/* Radix sorts tdefl_sym_freq[] array by 16-bit key m_key. Returns ptr to sorted values. */ +typedef struct +{ + mz_uint16 m_key, m_sym_index; +} tdefl_sym_freq; +static tdefl_sym_freq *tdefl_radix_sort_syms(mz_uint num_syms, tdefl_sym_freq *pSyms0, tdefl_sym_freq *pSyms1) +{ + mz_uint32 total_passes = 2, pass_shift, pass, i, hist[256 * 2]; + tdefl_sym_freq *pCur_syms = pSyms0, *pNew_syms = pSyms1; + MZ_CLEAR_ARR(hist); + for (i = 0; i < num_syms; i++) + { + mz_uint freq = pSyms0[i].m_key; + hist[freq & 0xFF]++; + hist[256 + ((freq >> 8) & 0xFF)]++; + } + while ((total_passes > 1) && (num_syms == hist[(total_passes - 1) * 256])) + total_passes--; + for (pass_shift = 0, pass = 0; pass < total_passes; pass++, pass_shift += 8) + { + const mz_uint32 *pHist = &hist[pass << 8]; + mz_uint offsets[256], cur_ofs = 0; + for (i = 0; i < 256; i++) + { + offsets[i] = cur_ofs; + cur_ofs += pHist[i]; + } + for (i = 0; i < num_syms; i++) + pNew_syms[offsets[(pCur_syms[i].m_key >> pass_shift) & 0xFF]++] = pCur_syms[i]; + { + tdefl_sym_freq *t = pCur_syms; + pCur_syms = pNew_syms; + pNew_syms = t; + } + } + return pCur_syms; +} + +/* tdefl_calculate_minimum_redundancy() originally written by: Alistair Moffat, alistair@cs.mu.oz.au, Jyrki Katajainen, jyrki@diku.dk, November 1996. */ +static void tdefl_calculate_minimum_redundancy(tdefl_sym_freq *A, int n) +{ + int root, leaf, next, avbl, used, dpth; + if (n == 0) + return; + else if (n == 1) + { + A[0].m_key = 1; + return; + } + A[0].m_key += A[1].m_key; + root = 0; + leaf = 2; + for (next = 1; next < n - 1; next++) + { + if (leaf >= n || A[root].m_key < A[leaf].m_key) + { + A[next].m_key = A[root].m_key; + A[root++].m_key = (mz_uint16)next; + } + else + A[next].m_key = A[leaf++].m_key; + if (leaf >= n || (root < next && A[root].m_key < A[leaf].m_key)) + { + A[next].m_key = (mz_uint16)(A[next].m_key + A[root].m_key); + A[root++].m_key = (mz_uint16)next; + } + else + A[next].m_key = (mz_uint16)(A[next].m_key + A[leaf++].m_key); + } + A[n - 2].m_key = 0; + for (next = n - 3; next >= 0; next--) + A[next].m_key = A[A[next].m_key].m_key + 1; + avbl = 1; + used = dpth = 0; + root = n - 2; + next = n - 1; + while (avbl > 0) + { + while (root >= 0 && (int)A[root].m_key == dpth) + { + used++; + root--; + } + while (avbl > used) + { + A[next--].m_key = (mz_uint16)(dpth); + avbl--; + } + avbl = 2 * used; + dpth++; + used = 0; + } +} + +/* Limits canonical Huffman code table's max code size. */ +enum +{ + TDEFL_MAX_SUPPORTED_HUFF_CODESIZE = 32 +}; +static void tdefl_huffman_enforce_max_code_size(int *pNum_codes, int code_list_len, int max_code_size) +{ + int i; + mz_uint32 total = 0; + if (code_list_len <= 1) + return; + for (i = max_code_size + 1; i <= TDEFL_MAX_SUPPORTED_HUFF_CODESIZE; i++) + pNum_codes[max_code_size] += pNum_codes[i]; + for (i = max_code_size; i > 0; i--) + total += (((mz_uint32)pNum_codes[i]) << (max_code_size - i)); + while (total != (1UL << max_code_size)) + { + pNum_codes[max_code_size]--; + for (i = max_code_size - 1; i > 0; i--) + if (pNum_codes[i]) { - uint32_t* dst_pix = (uint32_t*)(&dst[(first *bpp)/8]); - *dst_pix = ctx_over_RGBA8_2(*dst_pix, si_ga, si_rb, si_a, graystart); - dst_pix++; - for (unsigned int i = first + 1; i < (unsigned)last; i++) - { - *dst_pix = ctx_over_RGBA8_full_2(*dst_pix, si_ga_full, si_rb_full, si_a); - dst_pix++; - } + pNum_codes[i]--; + pNum_codes[i + 1] += 2; + break; } - break; - case CTX_COV_PATH_RGBA8_COPY_FRAGMENT: + total--; + } +} + +static void tdefl_optimize_huffman_table(tdefl_compressor *d, int table_num, int table_len, int code_size_limit, int static_table) +{ + int i, j, l, num_codes[1 + TDEFL_MAX_SUPPORTED_HUFF_CODESIZE]; + mz_uint next_code[TDEFL_MAX_SUPPORTED_HUFF_CODESIZE + 1]; + MZ_CLEAR_ARR(num_codes); + if (static_table) + { + for (i = 0; i < table_len; i++) + num_codes[d->m_huff_code_sizes[table_num][i]]++; + } + else + { + tdefl_sym_freq syms0[TDEFL_MAX_HUFF_SYMBOLS], syms1[TDEFL_MAX_HUFF_SYMBOLS], *pSyms; + int num_used_syms = 0; + const mz_uint16 *pSym_count = &d->m_huff_count[table_num][0]; + for (i = 0; i < table_len; i++) + if (pSym_count[i]) { - float u0 = 0; float v0 = 0; - float ud = 0; float vd = 0; - float w0 = 1; float wd = 0; - uint8_t gs = graystart; - ctx_RGBA8_source_copy_normal_fragment (rasterizer, &dst[(first * bpp)/8], NULL, first, &gs, 1); - ctx_init_uv (rasterizer, first+1, scanline/CTX_FULL_AA,&u0, &v0, &w0, &ud, &vd, &wd); - rasterizer->fragment (rasterizer, u0, v0, w0, &dst[((first+1)*bpp)/8], last-first-1, ud, vd, wd); + syms0[num_used_syms].m_key = (mz_uint16)pSym_count[i]; + syms0[num_used_syms++].m_sym_index = (mz_uint16)i; } + + pSyms = tdefl_radix_sort_syms(num_used_syms, syms0, syms1); + tdefl_calculate_minimum_redundancy(pSyms, num_used_syms); + + for (i = 0; i < num_used_syms; i++) + num_codes[pSyms[i].m_key]++; + + tdefl_huffman_enforce_max_code_size(num_codes, num_used_syms, code_size_limit); + + MZ_CLEAR_ARR(d->m_huff_code_sizes[table_num]); + MZ_CLEAR_ARR(d->m_huff_codes[table_num]); + for (i = 1, j = num_used_syms; i <= code_size_limit; i++) + for (l = num_codes[i]; l > 0; l--) + d->m_huff_code_sizes[table_num][pSyms[--j].m_sym_index] = (mz_uint8)(i); + } + + next_code[1] = 0; + for (j = 0, i = 2; i <= code_size_limit; i++) + next_code[i] = j = ((j + num_codes[i - 1]) << 1); + + for (i = 0; i < table_len; i++) + { + mz_uint rev_code = 0, code, code_size; + if ((code_size = d->m_huff_code_sizes[table_num][i]) == 0) + continue; + code = next_code[code_size]++; + for (l = code_size; l > 0; l--, code >>= 1) + rev_code = (rev_code << 1) | (code & 1); + d->m_huff_codes[table_num][i] = (mz_uint16)rev_code; + } +} + +#define TDEFL_PUT_BITS(b, l) \ + do \ + { \ + mz_uint bits = b; \ + mz_uint len = l; \ + MZ_ASSERT(bits <= ((1U << len) - 1U)); \ + d->m_bit_buffer |= (bits << d->m_bits_in); \ + d->m_bits_in += len; \ + while (d->m_bits_in >= 8) \ + { \ + if (d->m_pOutput_buf < d->m_pOutput_buf_end) \ + *d->m_pOutput_buf++ = (mz_uint8)(d->m_bit_buffer); \ + d->m_bit_buffer >>= 8; \ + d->m_bits_in -= 8; \ + } \ + } \ + MZ_MACRO_END + +#define TDEFL_RLE_PREV_CODE_SIZE() \ + { \ + if (rle_repeat_count) \ + { \ + if (rle_repeat_count < 3) \ + { \ + d->m_huff_count[2][prev_code_size] = (mz_uint16)(d->m_huff_count[2][prev_code_size] + rle_repeat_count); \ + while (rle_repeat_count--) \ + packed_code_sizes[num_packed_code_sizes++] = prev_code_size; \ + } \ + else \ + { \ + d->m_huff_count[2][16] = (mz_uint16)(d->m_huff_count[2][16] + 1); \ + packed_code_sizes[num_packed_code_sizes++] = 16; \ + packed_code_sizes[num_packed_code_sizes++] = (mz_uint8)(rle_repeat_count - 3); \ + } \ + rle_repeat_count = 0; \ + } \ + } + +#define TDEFL_RLE_ZERO_CODE_SIZE() \ + { \ + if (rle_z_count) \ + { \ + if (rle_z_count < 3) \ + { \ + d->m_huff_count[2][0] = (mz_uint16)(d->m_huff_count[2][0] + rle_z_count); \ + while (rle_z_count--) \ + packed_code_sizes[num_packed_code_sizes++] = 0; \ + } \ + else if (rle_z_count <= 10) \ + { \ + d->m_huff_count[2][17] = (mz_uint16)(d->m_huff_count[2][17] + 1); \ + packed_code_sizes[num_packed_code_sizes++] = 17; \ + packed_code_sizes[num_packed_code_sizes++] = (mz_uint8)(rle_z_count - 3); \ + } \ + else \ + { \ + d->m_huff_count[2][18] = (mz_uint16)(d->m_huff_count[2][18] + 1); \ + packed_code_sizes[num_packed_code_sizes++] = 18; \ + packed_code_sizes[num_packed_code_sizes++] = (mz_uint8)(rle_z_count - 11); \ + } \ + rle_z_count = 0; \ + } \ + } + +static mz_uint8 s_tdefl_packed_code_size_syms_swizzle[] = { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 }; + +static void tdefl_start_dynamic_block(tdefl_compressor *d) +{ + int num_lit_codes, num_dist_codes, num_bit_lengths; + mz_uint i, total_code_sizes_to_pack, num_packed_code_sizes, rle_z_count, rle_repeat_count, packed_code_sizes_index; + mz_uint8 code_sizes_to_pack[TDEFL_MAX_HUFF_SYMBOLS_0 + TDEFL_MAX_HUFF_SYMBOLS_1], packed_code_sizes[TDEFL_MAX_HUFF_SYMBOLS_0 + TDEFL_MAX_HUFF_SYMBOLS_1], prev_code_size = 0xFF; + + d->m_huff_count[0][256] = 1; + + tdefl_optimize_huffman_table(d, 0, TDEFL_MAX_HUFF_SYMBOLS_0, 15, MZ_FALSE); + tdefl_optimize_huffman_table(d, 1, TDEFL_MAX_HUFF_SYMBOLS_1, 15, MZ_FALSE); + + for (num_lit_codes = 286; num_lit_codes > 257; num_lit_codes--) + if (d->m_huff_code_sizes[0][num_lit_codes - 1]) break; - case CTX_COV_PATH_RGBA8_OVER_FRAGMENT: + for (num_dist_codes = 30; num_dist_codes > 1; num_dist_codes--) + if (d->m_huff_code_sizes[1][num_dist_codes - 1]) + break; + + memcpy(code_sizes_to_pack, &d->m_huff_code_sizes[0][0], num_lit_codes); + memcpy(code_sizes_to_pack + num_lit_codes, &d->m_huff_code_sizes[1][0], num_dist_codes); + total_code_sizes_to_pack = num_lit_codes + num_dist_codes; + num_packed_code_sizes = 0; + rle_z_count = 0; + rle_repeat_count = 0; + + memset(&d->m_huff_count[2][0], 0, sizeof(d->m_huff_count[2][0]) * TDEFL_MAX_HUFF_SYMBOLS_2); + for (i = 0; i < total_code_sizes_to_pack; i++) + { + mz_uint8 code_size = code_sizes_to_pack[i]; + if (!code_size) + { + TDEFL_RLE_PREV_CODE_SIZE(); + if (++rle_z_count == 138) { - 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); + TDEFL_RLE_ZERO_CODE_SIZE(); } - break; -#endif - default: + } + else + { + TDEFL_RLE_ZERO_CODE_SIZE(); + if (code_size != prev_code_size) { -#if static_OPAQUE - uint8_t *opaque = &rasterizer->opaque[0]; -#else - uint8_t opaque[last-first]; - memset (opaque, 255, sizeof (opaque)); -#endif - opaque[0] = graystart; - rasterizer->apply_coverage (rasterizer, - &dst[(first * bpp)/8], - rasterizer->color, first, opaque, last-first); - -#if static_OPAQUE - opaque[0] = 255; -#endif + TDEFL_RLE_PREV_CODE_SIZE(); + d->m_huff_count[2][code_size] = (mz_uint16)(d->m_huff_count[2][code_size] + 1); + packed_code_sizes[num_packed_code_sizes++] = code_size; } + else if (++rle_repeat_count == 6) + { + TDEFL_RLE_PREV_CODE_SIZE(); } - accumulated = grayend; - } - else if (first == last) - { - accumulated = (graystart-(grayend^255)); - } - accumulator_x = last; } - } + prev_code_size = code_size; + } + if (rle_repeat_count) + { + TDEFL_RLE_PREV_CODE_SIZE(); + } + else + { + TDEFL_RLE_ZERO_CODE_SIZE(); + } - if (accumulated) - { - 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: - rasterizer->apply_coverage (rasterizer, (uint8_t*)dst_pix, rasterizer->color, accumulator_x, &accumulated, 1); - } - } -} + tdefl_optimize_huffman_table(d, 2, TDEFL_MAX_HUFF_SYMBOLS_2, 7, MZ_FALSE); -inline static int ctx_rasterizer_is_simple (CtxRasterizer *rasterizer) -{ - if (rasterizer->fast_aa == 0 || - rasterizer->ending_edges || - rasterizer->pending_edges) - return 0; - int *edges = rasterizer->edges; - CtxSegment *segments = &((CtxSegment*)(rasterizer->edge_list.entries))[0]; + TDEFL_PUT_BITS(2, 2); - int active_edges = rasterizer->active_edges; - for (int t = 0; t < active_edges -1;t++) - { - CtxSegment *segment0 = segments + edges[t]; - CtxSegment *segment1 = segments + edges[t+1]; - const int delta0 = segment0->delta; - const int delta1 = segment1->delta; - const int x0 = segment0->val; - const int x1 = segment1->val; - int x0_end = x0 + delta0 * CTX_AA_HALFSTEP; - int x1_end = x1 + delta1 * CTX_AA_HALFSTEP; - int x0_start = x0 - delta0 * CTX_AA_HALFSTEP2; - int x1_start = x1 - delta1 * CTX_AA_HALFSTEP2; - if (x1_end < x0_end || - x1_start < x0_end || - x1_end < x0_start - ) - return 0; - } - return 1; -} + TDEFL_PUT_BITS(num_lit_codes - 257, 5); + TDEFL_PUT_BITS(num_dist_codes - 1, 5); + for (num_bit_lengths = 18; num_bit_lengths >= 0; num_bit_lengths--) + if (d->m_huff_code_sizes[2][s_tdefl_packed_code_size_syms_swizzle[num_bit_lengths]]) + break; + num_bit_lengths = MZ_MAX(4, (num_bit_lengths + 1)); + TDEFL_PUT_BITS(num_bit_lengths - 4, 4); + for (i = 0; (int)i < num_bit_lengths; i++) + TDEFL_PUT_BITS(d->m_huff_code_sizes[2][s_tdefl_packed_code_size_syms_swizzle[i]], 3); -inline static void -ctx_rasterizer_generate_coverage_set2 (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; + for (packed_code_sizes_index = 0; packed_code_sizes_index < num_packed_code_sizes;) + { + mz_uint code = packed_code_sizes[packed_code_sizes_index++]; + MZ_ASSERT(code < TDEFL_MAX_HUFF_SYMBOLS_2); + TDEFL_PUT_BITS(d->m_huff_codes[2][code], d->m_huff_code_sizes[2][code]); + if (code >= 16) + TDEFL_PUT_BITS(packed_code_sizes[packed_code_sizes_index++], "\02\03\07"[code - 16]); + } +} - coverage -= minx; +static void tdefl_start_static_block(tdefl_compressor *d) +{ + mz_uint i; + mz_uint8 *p = &d->m_huff_code_sizes[0][0]; - const int minx_ = minx * CTX_RASTERIZER_EDGE_MULTIPLIER * CTX_SUBDIV; - const int maxx_ = maxx * CTX_RASTERIZER_EDGE_MULTIPLIER * CTX_SUBDIV; + for (i = 0; i <= 143; ++i) + *p++ = 8; + for (; i <= 255; ++i) + *p++ = 9; + for (; i <= 279; ++i) + *p++ = 7; + for (; i <= 287; ++i) + *p++ = 8; - for (int t = 0; t < active_edges -1;t++) + memset(d->m_huff_code_sizes[1], 5, 32); + + tdefl_optimize_huffman_table(d, 0, 288, 15, MZ_TRUE); + tdefl_optimize_huffman_table(d, 1, 32, 15, MZ_TRUE); + + TDEFL_PUT_BITS(1, 2); +} + +static const mz_uint mz_bitmasks[17] = { 0x0000, 0x0001, 0x0003, 0x0007, 0x000F, 0x001F, 0x003F, 0x007F, 0x00FF, 0x01FF, 0x03FF, 0x07FF, 0x0FFF, 0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF }; + +#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN && MINIZ_HAS_64BIT_REGISTERS +static mz_bool tdefl_compress_lz_codes(tdefl_compressor *d) +{ + mz_uint flags; + mz_uint8 *pLZ_codes; + mz_uint8 *pOutput_buf = d->m_pOutput_buf; + mz_uint8 *pLZ_code_buf_end = d->m_pLZ_code_buf; + mz_uint64 bit_buffer = d->m_bit_buffer; + mz_uint bits_in = d->m_bits_in; + +#define TDEFL_PUT_BITS_FAST(b, l) \ + { \ + bit_buffer |= (((mz_uint64)(b)) << bits_in); \ + bits_in += (l); \ + } + + flags = 1; + for (pLZ_codes = d->m_lz_code_buf; pLZ_codes < pLZ_code_buf_end; flags >>= 1) { - CtxSegment *segment = &entries[edges[t]]; - UPDATE_PARITY; + if (flags == 1) + flags = *pLZ_codes++ | 0x100; - if (parity) + if (flags & 1) { - 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; + mz_uint s0, s1, n0, n1, sym, num_extra_bits; + mz_uint match_len = pLZ_codes[0]; + match_dist = (pLZ_codes[1] | (pLZ_codes[2] << 8)); + pLZ_codes += 3; - 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; + MZ_ASSERT(d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]); + TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][s_tdefl_len_sym[match_len]], d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]); + TDEFL_PUT_BITS_FAST(match_len & mz_bitmasks[s_tdefl_len_extra[match_len]], s_tdefl_len_extra[match_len]); - 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); + /* This sequence coaxes MSVC into using cmov's vs. jmp's. */ + s0 = s_tdefl_small_dist_sym[match_dist & 511]; + n0 = s_tdefl_small_dist_extra[match_dist & 511]; + s1 = s_tdefl_large_dist_sym[match_dist >> 8]; + n1 = s_tdefl_large_dist_extra[match_dist >> 8]; + sym = (match_dist < 512) ? s0 : s1; + num_extra_bits = (match_dist < 512) ? n0 : n1; - if (first < last) - { - int pre = 1; - int post = 1; + MZ_ASSERT(d->m_huff_code_sizes[1][sym]); + TDEFL_PUT_BITS_FAST(d->m_huff_codes[1][sym], d->m_huff_code_sizes[1][sym]); + TDEFL_PUT_BITS_FAST(match_dist & mz_bitmasks[num_extra_bits], num_extra_bits); + } + else + { + mz_uint lit = *pLZ_codes++; + MZ_ASSERT(d->m_huff_code_sizes[0][lit]); + TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][lit], d->m_huff_code_sizes[0][lit]); - if (abs(delta0) < CTX_RASTERIZER_AA_SLOPE_LIMIT3_FAST_AA) + if (((flags & 2) == 0) && (pLZ_codes < pLZ_code_buf_end)) { - coverage[first] += graystart; + flags >>= 1; + lit = *pLZ_codes++; + MZ_ASSERT(d->m_huff_code_sizes[0][lit]); + TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][lit], d->m_huff_code_sizes[0][lit]); + + if (((flags & 2) == 0) && (pLZ_codes < pLZ_code_buf_end)) + { + flags >>= 1; + lit = *pLZ_codes++; + MZ_ASSERT(d->m_huff_code_sizes[0][lit]); + TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][lit], d->m_huff_code_sizes[0][lit]); + } } - 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 us = u0 / (CTX_RASTERIZER_EDGE_MULTIPLIER*CTX_SUBDIV); - int count = 0; + if (pOutput_buf >= d->m_pOutput_buf_end) + return MZ_FALSE; - 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); + memcpy(pOutput_buf, &bit_buffer, sizeof(mz_uint64)); + pOutput_buf += (bits_in >> 3); + bit_buffer >>= (bits_in & ~7); + bits_in &= 7; + } - 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-1)-first+1; - } - - if (abs(delta1) < CTX_RASTERIZER_AA_SLOPE_LIMIT3_FAST_AA) +#undef TDEFL_PUT_BITS_FAST + + d->m_pOutput_buf = pOutput_buf; + d->m_bits_in = 0; + d->m_bit_buffer = 0; + + while (bits_in) + { + mz_uint32 n = MZ_MIN(bits_in, 16); + TDEFL_PUT_BITS((mz_uint)bit_buffer & mz_bitmasks[n], n); + bit_buffer >>= n; + bits_in -= n; + } + + TDEFL_PUT_BITS(d->m_huff_codes[0][256], d->m_huff_code_sizes[0][256]); + + return (d->m_pOutput_buf < d->m_pOutput_buf_end); +} +#else +static mz_bool tdefl_compress_lz_codes(tdefl_compressor *d) +{ + mz_uint flags; + mz_uint8 *pLZ_codes; + + flags = 1; + for (pLZ_codes = d->m_lz_code_buf; pLZ_codes < d->m_pLZ_code_buf; flags >>= 1) + { + if (flags == 1) + flags = *pLZ_codes++ | 0x100; + if (flags & 1) + { + mz_uint sym, num_extra_bits; + mz_uint match_len = pLZ_codes[0], match_dist = (pLZ_codes[1] | (pLZ_codes[2] << 8)); + pLZ_codes += 3; + + MZ_ASSERT(d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]); + TDEFL_PUT_BITS(d->m_huff_codes[0][s_tdefl_len_sym[match_len]], d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]); + TDEFL_PUT_BITS(match_len & mz_bitmasks[s_tdefl_len_extra[match_len]], s_tdefl_len_extra[match_len]); + + if (match_dist < 512) { - coverage[last] += grayend; + sym = s_tdefl_small_dist_sym[match_dist]; + num_extra_bits = s_tdefl_small_dist_extra[match_dist]; } 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))); - - 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) * - (CTX_RASTERIZER_EDGE_MULTIPLIER*CTX_SUBDIV/255)); - int sum = ((u1-u0+CTX_RASTERIZER_EDGE_MULTIPLIER * CTX_SUBDIV * 5/4)/255); - 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; + sym = s_tdefl_large_dist_sym[match_dist >> 8]; + num_extra_bits = s_tdefl_large_dist_extra[match_dist >> 8]; } - for (int i = first + pre; i <= last - post; i++) - coverage[i] = 255; - } - else if (first == last) - { - coverage[last]+=(graystart-(grayend^255)); - } + MZ_ASSERT(d->m_huff_code_sizes[1][sym]); + TDEFL_PUT_BITS(d->m_huff_codes[1][sym], d->m_huff_code_sizes[1][sym]); + TDEFL_PUT_BITS(match_dist & mz_bitmasks[num_extra_bits], num_extra_bits); } - } -} + else + { + mz_uint lit = *pLZ_codes++; + MZ_ASSERT(d->m_huff_code_sizes[0][lit]); + TDEFL_PUT_BITS(d->m_huff_codes[0][lit], d->m_huff_code_sizes[0][lit]); + } + } + TDEFL_PUT_BITS(d->m_huff_codes[0][256], d->m_huff_code_sizes[0][256]); -inline static void -ctx_rasterizer_generate_coverage_apply2 (CtxRasterizer *rasterizer, - int minx, - int maxx, - uint8_t *coverage, - int is_winding, - CtxCovPath comp) + return (d->m_pOutput_buf < d->m_pOutput_buf_end); +} +#endif /* MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN && MINIZ_HAS_64BIT_REGISTERS */ + +static mz_bool tdefl_compress_block(tdefl_compressor *d, mz_bool static_block) { - CtxSegment *entries = (CtxSegment*)(&rasterizer->edge_list.entries[0]); - int *edges = rasterizer->edges; - int scanline = rasterizer->scanline; - const int bpp = rasterizer->format->bpp; - int active_edges = rasterizer->active_edges; - int parity = 0; + if (static_block) + tdefl_start_static_block(d); + else + tdefl_start_dynamic_block(d); + return tdefl_compress_lz_codes(d); +} -#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 - { - src_pix = - si_ga = - si_rb = - si_ga_full = - si_rb_full = - si_a = 0; - src_pixp = &src_pix; - } -#endif +static const mz_uint s_tdefl_num_probes[11]; - uint8_t *dst = ( (uint8_t *) rasterizer->buf) + - (rasterizer->blit_stride * (scanline / CTX_FULL_AA)); +static int tdefl_flush_block(tdefl_compressor *d, int flush) +{ + mz_uint saved_bit_buf, saved_bits_in; + mz_uint8 *pSaved_output_buf; + mz_bool comp_block_succeeded = MZ_FALSE; + int n, use_raw_block = ((d->m_flags & TDEFL_FORCE_ALL_RAW_BLOCKS) != 0) && (d->m_lookahead_pos - d->m_lz_code_buf_dict_pos) <= d->m_dict_size; + mz_uint8 *pOutput_buf_start = ((d->m_pPut_buf_func == NULL) && ((*d->m_pOut_buf_size - d->m_out_buf_ofs) >= TDEFL_OUT_BUF_SIZE)) ? ((mz_uint8 *)d->m_pOut_buf + d->m_out_buf_ofs) : d->m_output_buf; - coverage -= minx; + d->m_pOutput_buf = pOutput_buf_start; + d->m_pOutput_buf_end = d->m_pOutput_buf + TDEFL_OUT_BUF_SIZE - 16; - const int minx_ = minx * CTX_RASTERIZER_EDGE_MULTIPLIER * CTX_SUBDIV; - const int maxx_ = maxx * CTX_RASTERIZER_EDGE_MULTIPLIER * CTX_SUBDIV; + MZ_ASSERT(!d->m_output_flush_remaining); + d->m_output_flush_ofs = 0; + d->m_output_flush_remaining = 0; - int accumulated_x0 = 65538; - int accumulated_x1 = 65536; + *d->m_pLZ_flags = (mz_uint8)(*d->m_pLZ_flags >> d->m_num_flags_left); + d->m_pLZ_code_buf -= (d->m_num_flags_left == 8); - for (int t = 0; t < active_edges -1;t++) + if ((d->m_flags & TDEFL_WRITE_ZLIB_HEADER) && (!d->m_block_index)) { - CtxSegment *segment = &entries[edges[t]]; - UPDATE_PARITY; + const mz_uint8 cmf = 0x78; + mz_uint8 flg, flevel = 3; + mz_uint header, i, n = sizeof(s_tdefl_num_probes) / sizeof(mz_uint); - 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; + /* Determine compression level by reversing the process in tdefl_create_comp_flags_from_zip_params() */ + for (i = 0; i < n; i++) + if (s_tdefl_num_probes[i] == (d->m_flags & 0xFFF)) break; - int x0_start = x0 - delta0 * CTX_AA_HALFSTEP2; - int x1_start = x1 - delta1 * CTX_AA_HALFSTEP2; - int x0_end = x0 + delta0 * CTX_AA_HALFSTEP; - int x1_end = x1 + delta1 * CTX_AA_HALFSTEP; + if (i < 2) + flevel = 0; + else if (i < 6) + flevel = 1; + else if (i == 6) + flevel = 2; - 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; + header = cmf << 8 | (flevel << 6); + header += 31 - (header % 31); + flg = header & 0xFF; - 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); + TDEFL_PUT_BITS(cmf, 8); + TDEFL_PUT_BITS(flg, 8); + } - if (first < last) - { - int pre = 1; - int post = 1; + TDEFL_PUT_BITS(flush == TDEFL_FINISH, 1); - if (abs(delta0) < CTX_RASTERIZER_AA_SLOPE_LIMIT3_FAST_AA) - { - coverage[first] += graystart; + pSaved_output_buf = d->m_pOutput_buf; + saved_bit_buf = d->m_bit_buffer; + saved_bits_in = d->m_bits_in; - 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))); + if (!use_raw_block) + comp_block_succeeded = tdefl_compress_block(d, (d->m_flags & TDEFL_FORCE_ALL_STATIC_BLOCKS) || (d->m_total_lz_bytes < 48)); - 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); + /* If the block gets expanded, forget the current contents of the output buffer and send a raw block instead. */ + if (((use_raw_block) || ((d->m_total_lz_bytes) && ((d->m_pOutput_buf - pSaved_output_buf + 1U) >= d->m_total_lz_bytes))) && + ((d->m_lookahead_pos - d->m_lz_code_buf_dict_pos) <= d->m_dict_size)) + { + mz_uint i; + d->m_pOutput_buf = pSaved_output_buf; + d->m_bit_buffer = saved_bit_buf, d->m_bits_in = saved_bits_in; + TDEFL_PUT_BITS(0, 2); + if (d->m_bits_in) + { + TDEFL_PUT_BITS(0, 8 - d->m_bits_in); + } + for (i = 2; i; --i, d->m_total_lz_bytes ^= 0xFFFF) + { + TDEFL_PUT_BITS(d->m_total_lz_bytes & 0xFFFF, 16); + } + for (i = 0; i < d->m_total_lz_bytes; ++i) + { + TDEFL_PUT_BITS(d->m_dict[(d->m_lz_code_buf_dict_pos + i) & TDEFL_LZ_DICT_SIZE_MASK], 8); + } + } + /* Check for the extremely unlikely (if not impossible) case of the compressed block not fitting into the output buffer when using dynamic codes. */ + else if (!comp_block_succeeded) + { + d->m_pOutput_buf = pSaved_output_buf; + d->m_bit_buffer = saved_bit_buf, d->m_bits_in = saved_bits_in; + tdefl_compress_block(d, MZ_TRUE); + } - 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) + if (flush) + { + if (flush == TDEFL_FINISH) + { + if (d->m_bits_in) { - coverage[us + count] += ((u - u0 + mod) * recip)>>16; - count++; + TDEFL_PUT_BITS(0, 8 - d->m_bits_in); } - pre = (us+count-1)-first+1; - - 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: + if (d->m_flags & TDEFL_WRITE_ZLIB_HEADER) + { + mz_uint i, a = d->m_adler32; + for (i = 0; i < 4; i++) { - 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; - } + TDEFL_PUT_BITS((a >> 24) & 0xFF, 8); + a <<= 8; } - break; -#endif - default: - rasterizer->apply_coverage (rasterizer, - &dst[((accumulated_x0) * bpp)/8], - rasterizer->color, - accumulated_x0, - &coverage[accumulated_x0], - accumulated_x1-accumulated_x0+1); - } - accumulated_x0 = 65538; - accumulated_x1 = 65536; - } - - if (abs(delta1) < CTX_RASTERIZER_AA_SLOPE_LIMIT3_FAST_AA) - { - coverage[last] += grayend; - accumulated_x1 = last; - accumulated_x0 = last; - } - else - { - 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) * - (CTX_RASTERIZER_EDGE_MULTIPLIER*CTX_SUBDIV/255)); - int sum = ((u1-u0+CTX_RASTERIZER_EDGE_MULTIPLIER * CTX_SUBDIV * 5/4)/255); - - int recip = 65536/ sum; - for (unsigned int u = u0; u < u1; u+= CTX_RASTERIZER_EDGE_MULTIPLIER*CTX_SUBDIV) + } + } + else + { + mz_uint i, z = 0; + TDEFL_PUT_BITS(0, 3); + if (d->m_bits_in) { - coverage[us + count] = (((u - u0 + mod)*recip)>>16)^255; - count++; + TDEFL_PUT_BITS(0, 8 - d->m_bits_in); } - post = last-us+1; - - accumulated_x1 = us + count; - accumulated_x0 = us; - } - 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: + for (i = 2; i; --i, z ^= 0xFFFF) { - 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; + TDEFL_PUT_BITS(z & 0xFFFF, 16); + } + } + } - int scount = (last - post) - (first+pre) + 1; - unsigned int count = scount; + MZ_ASSERT(d->m_pOutput_buf < d->m_pOutput_buf_end); - //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; -#if CTX_ENABLE_CBRLE - case CTX_COV_PATH_CBRLE_COPY: - { - int count = (last - post) - (first+pre) + 1; - if (count>0) - ctx_CBRLE_compress (rasterizer->color, - dst, - rasterizer->blit_width, - rasterizer->blit_stride, - pre, count, - CBRLE_MODE_SET_COLOR, NULL, 1); + memset(&d->m_huff_count[0][0], 0, sizeof(d->m_huff_count[0][0]) * TDEFL_MAX_HUFF_SYMBOLS_0); + memset(&d->m_huff_count[1][0], 0, sizeof(d->m_huff_count[1][0]) * TDEFL_MAX_HUFF_SYMBOLS_1); - } - break; -#endif + d->m_pLZ_code_buf = d->m_lz_code_buf + 1; + d->m_pLZ_flags = d->m_lz_code_buf; + d->m_num_flags_left = 8; + d->m_lz_code_buf_dict_pos += d->m_total_lz_bytes; + d->m_total_lz_bytes = 0; + d->m_block_index++; - case CTX_COV_PATH_RGBA8_COPY: + if ((n = (int)(d->m_pOutput_buf - pOutput_buf_start)) != 0) + { + if (d->m_pPut_buf_func) + { + *d->m_pIn_buf_size = d->m_pSrc - (const mz_uint8 *)d->m_pIn_buf; + if (!(*d->m_pPut_buf_func)(d->m_output_buf, n, d->m_pPut_buf_user)) + return (d->m_prev_return_status = TDEFL_STATUS_PUT_BUF_FAILED); + } + else if (pOutput_buf_start == d->m_output_buf) + { + int bytes_to_copy = (int)MZ_MIN((size_t)n, (size_t)(*d->m_pOut_buf_size - d->m_out_buf_ofs)); + memcpy((mz_uint8 *)d->m_pOut_buf + d->m_out_buf_ofs, d->m_output_buf, bytes_to_copy); + d->m_out_buf_ofs += bytes_to_copy; + if ((n -= bytes_to_copy) != 0) { - 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); + d->m_output_flush_ofs = bytes_to_copy; + d->m_output_flush_remaining = n; } - break; + } + else + { + d->m_out_buf_ofs += n; + } + } + return d->m_output_flush_remaining; +} - 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); - } - } +#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES +#ifdef MINIZ_UNALIGNED_USE_MEMCPY +static mz_uint16 TDEFL_READ_UNALIGNED_WORD(const mz_uint8* p) +{ + mz_uint16 ret; + memcpy(&ret, p, sizeof(mz_uint16)); + return ret; +} +static mz_uint16 TDEFL_READ_UNALIGNED_WORD2(const mz_uint16* p) +{ + mz_uint16 ret; + memcpy(&ret, p, sizeof(mz_uint16)); + return ret; +} +#else +#define TDEFL_READ_UNALIGNED_WORD(p) *(const mz_uint16 *)(p) +#define TDEFL_READ_UNALIGNED_WORD2(p) *(const mz_uint16 *)(p) +#endif +static MZ_FORCEINLINE void tdefl_find_match(tdefl_compressor *d, mz_uint lookahead_pos, mz_uint max_dist, mz_uint max_match_len, mz_uint *pMatch_dist, mz_uint *pMatch_len) +{ + mz_uint dist, pos = lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK, match_len = *pMatch_len, probe_pos = pos, next_probe_pos, probe_len; + mz_uint num_probes_left = d->m_max_probes[match_len >= 32]; + const mz_uint16 *s = (const mz_uint16 *)(d->m_dict + pos), *p, *q; + mz_uint16 c01 = TDEFL_READ_UNALIGNED_WORD(&d->m_dict[pos + match_len - 1]), s01 = TDEFL_READ_UNALIGNED_WORD2(s); + MZ_ASSERT(max_match_len <= TDEFL_MAX_MATCH_LEN); + if (max_match_len <= match_len) + return; + for (;;) + { + for (;;) + { + if (--num_probes_left == 0) + return; +#define TDEFL_PROBE \ + next_probe_pos = d->m_next[probe_pos]; \ + if ((!next_probe_pos) || ((dist = (mz_uint16)(lookahead_pos - next_probe_pos)) > max_dist)) \ + return; \ + probe_pos = next_probe_pos & TDEFL_LZ_DICT_SIZE_MASK; \ + if (TDEFL_READ_UNALIGNED_WORD(&d->m_dict[probe_pos + match_len - 1]) == c01) \ + break; + TDEFL_PROBE; + TDEFL_PROBE; + TDEFL_PROBE; + } + if (!dist) 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); - } + q = (const mz_uint16 *)(d->m_dict + probe_pos); + if (TDEFL_READ_UNALIGNED_WORD2(q) != s01) + continue; + p = s; + probe_len = 32; + do + { + } while ((TDEFL_READ_UNALIGNED_WORD2(++p) == TDEFL_READ_UNALIGNED_WORD2(++q)) && (TDEFL_READ_UNALIGNED_WORD2(++p) == TDEFL_READ_UNALIGNED_WORD2(++q)) && + (TDEFL_READ_UNALIGNED_WORD2(++p) == TDEFL_READ_UNALIGNED_WORD2(++q)) && (TDEFL_READ_UNALIGNED_WORD2(++p) == TDEFL_READ_UNALIGNED_WORD2(++q)) && (--probe_len > 0)); + if (!probe_len) + { + *pMatch_dist = dist; + *pMatch_len = MZ_MIN(max_match_len, (mz_uint)TDEFL_MAX_MATCH_LEN); break; -#endif - default: - { - int width = last-first-pre-post+1; - if (width > 0) - { -#if static_OPAQUE - uint8_t *opaque = &rasterizer->opaque[0]; + } + else if ((probe_len = ((mz_uint)(p - s) * 2) + (mz_uint)(*(const mz_uint8 *)p == *(const mz_uint8 *)q)) > match_len) + { + *pMatch_dist = dist; + if ((*pMatch_len = match_len = MZ_MIN(max_match_len, probe_len)) == max_match_len) + break; + c01 = TDEFL_READ_UNALIGNED_WORD(&d->m_dict[pos + match_len - 1]); + } + } +} #else - uint8_t opaque[width]; - memset (opaque, 255, sizeof (opaque)); -#endif - rasterizer->apply_coverage (rasterizer, - &dst[((first + pre) * bpp)/8], - rasterizer->color, - first + pre, - opaque, - width); - } - } - } - } - else if (first == last) - { - coverage[last]+=(graystart-(255-grayend)); +static MZ_FORCEINLINE void tdefl_find_match(tdefl_compressor *d, mz_uint lookahead_pos, mz_uint max_dist, mz_uint max_match_len, mz_uint *pMatch_dist, mz_uint *pMatch_len) +{ + mz_uint dist, pos = lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK, match_len = *pMatch_len, probe_pos = pos, next_probe_pos, probe_len; + mz_uint num_probes_left = d->m_max_probes[match_len >= 32]; + const mz_uint8 *s = d->m_dict + pos, *p, *q; + mz_uint8 c0 = d->m_dict[pos + match_len], c1 = d->m_dict[pos + match_len - 1]; + MZ_ASSERT(max_match_len <= TDEFL_MAX_MATCH_LEN); + if (max_match_len <= match_len) + return; + for (;;) + { + for (;;) + { + if (--num_probes_left == 0) + return; +#define TDEFL_PROBE \ + next_probe_pos = d->m_next[probe_pos]; \ + if ((!next_probe_pos) || ((dist = (mz_uint16)(lookahead_pos - next_probe_pos)) > max_dist)) \ + return; \ + probe_pos = next_probe_pos & TDEFL_LZ_DICT_SIZE_MASK; \ + if ((d->m_dict[probe_pos + match_len] == c0) && (d->m_dict[probe_pos + match_len - 1] == c1)) \ + break; + TDEFL_PROBE; + TDEFL_PROBE; + TDEFL_PROBE; + } + if (!dist) + break; + p = s; + q = d->m_dict + probe_pos; + for (probe_len = 0; probe_len < max_match_len; probe_len++) + if (*p++ != *q++) + break; + if (probe_len > match_len) + { + *pMatch_dist = dist; + if ((*pMatch_len = match_len = probe_len) == max_match_len) + return; + c0 = d->m_dict[pos + match_len]; + c1 = d->m_dict[pos + match_len - 1]; + } + } +} +#endif /* #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES */ - accumulated_x1 = last; - accumulated_x0 = ctx_mini (accumulated_x0, last); - } +#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN +#ifdef MINIZ_UNALIGNED_USE_MEMCPY +static mz_uint32 TDEFL_READ_UNALIGNED_WORD32(const mz_uint8* p) +{ + mz_uint32 ret; + memcpy(&ret, p, sizeof(mz_uint32)); + return ret; +} +#else +#define TDEFL_READ_UNALIGNED_WORD32(p) *(const mz_uint32 *)(p) +#endif +static mz_bool tdefl_compress_fast(tdefl_compressor *d) +{ + /* Faster, minimally featured LZRW1-style match+parse loop with better register utilization. Intended for applications where raw throughput is valued more highly than ratio. */ + mz_uint lookahead_pos = d->m_lookahead_pos, lookahead_size = d->m_lookahead_size, dict_size = d->m_dict_size, total_lz_bytes = d->m_total_lz_bytes, num_flags_left = d->m_num_flags_left; + mz_uint8 *pLZ_code_buf = d->m_pLZ_code_buf, *pLZ_flags = d->m_pLZ_flags; + mz_uint cur_pos = lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK; + + while ((d->m_src_buf_left) || ((d->m_flush) && (lookahead_size))) + { + const mz_uint TDEFL_COMP_FAST_LOOKAHEAD_SIZE = 4096; + mz_uint dst_pos = (lookahead_pos + lookahead_size) & TDEFL_LZ_DICT_SIZE_MASK; + mz_uint num_bytes_to_process = (mz_uint)MZ_MIN(d->m_src_buf_left, TDEFL_COMP_FAST_LOOKAHEAD_SIZE - lookahead_size); + d->m_src_buf_left -= num_bytes_to_process; + lookahead_size += num_bytes_to_process; + + while (num_bytes_to_process) + { + mz_uint32 n = MZ_MIN(TDEFL_LZ_DICT_SIZE - dst_pos, num_bytes_to_process); + memcpy(d->m_dict + dst_pos, d->m_pSrc, n); + if (dst_pos < (TDEFL_MAX_MATCH_LEN - 1)) + memcpy(d->m_dict + TDEFL_LZ_DICT_SIZE + dst_pos, d->m_pSrc, MZ_MIN(n, (TDEFL_MAX_MATCH_LEN - 1) - dst_pos)); + d->m_pSrc += n; + dst_pos = (dst_pos + n) & TDEFL_LZ_DICT_SIZE_MASK; + num_bytes_to_process -= n; } - } - if (accumulated_x1-accumulated_x0>=0) - { - switch (comp) - { -#if CTX_RASTERIZER_SWITCH_DISPATCH - case CTX_COV_PATH_RGBA8_OVER: + dict_size = MZ_MIN(TDEFL_LZ_DICT_SIZE - lookahead_size, dict_size); + if ((!d->m_flush) && (lookahead_size < TDEFL_COMP_FAST_LOOKAHEAD_SIZE)) + break; + + while (lookahead_size >= 4) + { + mz_uint cur_match_dist, cur_match_len = 1; + mz_uint8 *pCur_dict = d->m_dict + cur_pos; + mz_uint first_trigram = TDEFL_READ_UNALIGNED_WORD32(pCur_dict) & 0xFFFFFF; + mz_uint hash = (first_trigram ^ (first_trigram >> (24 - (TDEFL_LZ_HASH_BITS - 8)))) & TDEFL_LEVEL1_HASH_SIZE_MASK; + mz_uint probe_pos = d->m_hash[hash]; + d->m_hash[hash] = (mz_uint16)lookahead_pos; + + if (((cur_match_dist = (mz_uint16)(lookahead_pos - probe_pos)) <= dict_size) && ((TDEFL_READ_UNALIGNED_WORD32(d->m_dict + (probe_pos &= TDEFL_LZ_DICT_SIZE_MASK)) & 0xFFFFFF) == first_trigram)) + { + const mz_uint16 *p = (const mz_uint16 *)pCur_dict; + const mz_uint16 *q = (const mz_uint16 *)(d->m_dict + probe_pos); + mz_uint32 probe_len = 32; + do { - 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: + } while ((TDEFL_READ_UNALIGNED_WORD2(++p) == TDEFL_READ_UNALIGNED_WORD2(++q)) && (TDEFL_READ_UNALIGNED_WORD2(++p) == TDEFL_READ_UNALIGNED_WORD2(++q)) && + (TDEFL_READ_UNALIGNED_WORD2(++p) == TDEFL_READ_UNALIGNED_WORD2(++q)) && (TDEFL_READ_UNALIGNED_WORD2(++p) == TDEFL_READ_UNALIGNED_WORD2(++q)) && (--probe_len > 0)); + cur_match_len = ((mz_uint)(p - (const mz_uint16 *)pCur_dict) * 2) + (mz_uint)(*(const mz_uint8 *)p == *(const mz_uint8 *)q); + if (!probe_len) + cur_match_len = cur_match_dist ? TDEFL_MAX_MATCH_LEN : 0; + + if ((cur_match_len < TDEFL_MIN_MATCH_LEN) || ((cur_match_len == TDEFL_MIN_MATCH_LEN) && (cur_match_dist >= 8U * 1024U))) { - 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++; - } + cur_match_len = 1; + *pLZ_code_buf++ = (mz_uint8)first_trigram; + *pLZ_flags = (mz_uint8)(*pLZ_flags >> 1); + d->m_huff_count[0][(mz_uint8)first_trigram]++; } - break; -#endif - default: - rasterizer->apply_coverage (rasterizer, - &dst[((accumulated_x0) * bpp)/8], - rasterizer->color, - accumulated_x0, - &coverage[accumulated_x0], - accumulated_x1-accumulated_x0+1); - } - } -} + else + { + mz_uint32 s0, s1; + cur_match_len = MZ_MIN(cur_match_len, lookahead_size); -#undef CTX_EDGE_Y0 -#undef CTX_EDGE + MZ_ASSERT((cur_match_len >= TDEFL_MIN_MATCH_LEN) && (cur_match_dist >= 1) && (cur_match_dist <= TDEFL_LZ_DICT_SIZE)); -static inline void -ctx_rasterizer_reset (CtxRasterizer *rasterizer) -{ - rasterizer->has_shape = - rasterizer->has_prev = - rasterizer->edge_list.count = // ready for new edges - rasterizer->edge_pos = - rasterizer->scanline = 0; - if (CTX_LIKELY(!rasterizer->preserve)) - { - rasterizer->scan_min = - rasterizer->col_min = 50000000; - rasterizer->scan_max = - rasterizer->col_max = -50000000; - } - //rasterizer->comp_op = NULL; // keep comp_op cached - // between rasterizations where rendering attributes are - // nonchanging -} + cur_match_dist--; -static void -ctx_rasterizer_rasterize_edges2 (CtxRasterizer *rasterizer, const int fill_rule -#if CTX_SHAPE_CACHE - ,CtxShapeEntry *shape + pLZ_code_buf[0] = (mz_uint8)(cur_match_len - TDEFL_MIN_MATCH_LEN); +#ifdef MINIZ_UNALIGNED_USE_MEMCPY + memcpy(&pLZ_code_buf[1], &cur_match_dist, sizeof(cur_match_dist)); +#else + *(mz_uint16 *)(&pLZ_code_buf[1]) = (mz_uint16)cur_match_dist; #endif - ) -{ - rasterizer->pending_edges = - rasterizer->active_edges = 0; - //rasterizer->scanline = 0; - 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; - const int blit_width = rasterizer->blit_width; - const int blit_max_x = rasterizer->blit_x + blit_width; - int minx = rasterizer->col_min / CTX_SUBDIV - rasterizer->blit_x; - int maxx = (rasterizer->col_max + CTX_SUBDIV-1) / CTX_SUBDIV - - rasterizer->blit_x; - const int blit_stride = rasterizer->blit_stride; - uint8_t real_fraction = 255/real_aa; + pLZ_code_buf += 3; + *pLZ_flags = (mz_uint8)((*pLZ_flags >> 1) | 0x80); - rasterizer->prev_active_edges = -1; - if ( -#if CTX_SHAPE_CACHE - !shape && -#endif - maxx > blit_max_x - 1) - { maxx = blit_max_x - 1; } + s0 = s_tdefl_small_dist_sym[cur_match_dist & 511]; + s1 = s_tdefl_large_dist_sym[cur_match_dist >> 8]; + d->m_huff_count[1][(cur_match_dist < 512) ? s0 : s1]++; - minx = ctx_maxi (rasterizer->state->gstate.clip_min_x, minx); - maxx = ctx_mini (rasterizer->state->gstate.clip_max_x, maxx); - minx = ctx_maxi (0, minx); // redundant? - if (CTX_UNLIKELY (minx >= maxx)) - { - return; + d->m_huff_count[0][s_tdefl_len_sym[cur_match_len - TDEFL_MIN_MATCH_LEN]]++; + } + } + else + { + *pLZ_code_buf++ = (mz_uint8)first_trigram; + *pLZ_flags = (mz_uint8)(*pLZ_flags >> 1); + d->m_huff_count[0][(mz_uint8)first_trigram]++; + } + + if (--num_flags_left == 0) + { + num_flags_left = 8; + pLZ_flags = pLZ_code_buf++; + } + + total_lz_bytes += cur_match_len; + lookahead_pos += cur_match_len; + dict_size = MZ_MIN(dict_size + cur_match_len, (mz_uint)TDEFL_LZ_DICT_SIZE); + cur_pos = (cur_pos + cur_match_len) & TDEFL_LZ_DICT_SIZE_MASK; + MZ_ASSERT(lookahead_size >= cur_match_len); + lookahead_size -= cur_match_len; + + if (pLZ_code_buf > &d->m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE - 8]) + { + int n; + d->m_lookahead_pos = lookahead_pos; + d->m_lookahead_size = lookahead_size; + d->m_dict_size = dict_size; + d->m_total_lz_bytes = total_lz_bytes; + d->m_pLZ_code_buf = pLZ_code_buf; + d->m_pLZ_flags = pLZ_flags; + d->m_num_flags_left = num_flags_left; + if ((n = tdefl_flush_block(d, 0)) != 0) + return (n < 0) ? MZ_FALSE : MZ_TRUE; + total_lz_bytes = d->m_total_lz_bytes; + pLZ_code_buf = d->m_pLZ_code_buf; + pLZ_flags = d->m_pLZ_flags; + num_flags_left = d->m_num_flags_left; + } + } + + while (lookahead_size) + { + mz_uint8 lit = d->m_dict[cur_pos]; + + total_lz_bytes++; + *pLZ_code_buf++ = lit; + *pLZ_flags = (mz_uint8)(*pLZ_flags >> 1); + if (--num_flags_left == 0) + { + num_flags_left = 8; + pLZ_flags = pLZ_code_buf++; + } + + d->m_huff_count[0][lit]++; + + lookahead_pos++; + dict_size = MZ_MIN(dict_size + 1, (mz_uint)TDEFL_LZ_DICT_SIZE); + cur_pos = (cur_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK; + lookahead_size--; + + if (pLZ_code_buf > &d->m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE - 8]) + { + int n; + d->m_lookahead_pos = lookahead_pos; + d->m_lookahead_size = lookahead_size; + d->m_dict_size = dict_size; + d->m_total_lz_bytes = total_lz_bytes; + d->m_pLZ_code_buf = pLZ_code_buf; + d->m_pLZ_flags = pLZ_flags; + d->m_num_flags_left = num_flags_left; + if ((n = tdefl_flush_block(d, 0)) != 0) + return (n < 0) ? MZ_FALSE : MZ_TRUE; + total_lz_bytes = d->m_total_lz_bytes; + pLZ_code_buf = d->m_pLZ_code_buf; + pLZ_flags = d->m_pLZ_flags; + num_flags_left = d->m_num_flags_left; + } + } } -#if CTX_SHAPE_CACHE - uint8_t _coverage[shape?2:maxx-minx+1]; -#else - uint8_t _coverage[maxx-minx+1]; -#endif - uint8_t *coverage = &_coverage[0]; - int coverage_size; + d->m_lookahead_pos = lookahead_pos; + d->m_lookahead_size = lookahead_size; + d->m_dict_size = dict_size; + d->m_total_lz_bytes = total_lz_bytes; + d->m_pLZ_code_buf = pLZ_code_buf; + d->m_pLZ_flags = pLZ_flags; + d->m_num_flags_left = num_flags_left; + return MZ_TRUE; +} +#endif /* MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN */ - rasterizer->scan_min -= (rasterizer->scan_min % CTX_FULL_AA); -#if CTX_SHAPE_CACHE - if (shape) +static MZ_FORCEINLINE void tdefl_record_literal(tdefl_compressor *d, mz_uint8 lit) +{ + d->m_total_lz_bytes++; + *d->m_pLZ_code_buf++ = lit; + *d->m_pLZ_flags = (mz_uint8)(*d->m_pLZ_flags >> 1); + if (--d->m_num_flags_left == 0) { - coverage_size = shape->width; - coverage = &shape->data[0]; - scan_start = rasterizer->scan_min; - scan_end = rasterizer->scan_max; + d->m_num_flags_left = 8; + d->m_pLZ_flags = d->m_pLZ_code_buf++; } - else -#endif - { - coverage_size = sizeof (_coverage); - if (rasterizer->scan_min > scan_start) - { - dst += (rasterizer->blit_stride * (rasterizer->scan_min-scan_start) / CTX_FULL_AA); - scan_start = rasterizer->scan_min; - } - scan_end = ctx_mini (rasterizer->scan_max, scan_end); - } + d->m_huff_count[0][lit]++; +} - if (CTX_UNLIKELY(rasterizer->state->gstate.clip_min_y * CTX_FULL_AA > scan_start )) - { - dst += (rasterizer->blit_stride * (rasterizer->state->gstate.clip_min_y * CTX_FULL_AA -scan_start) / CTX_FULL_AA); - scan_start = rasterizer->state->gstate.clip_min_y * CTX_FULL_AA; - } - scan_end = ctx_mini (rasterizer->state->gstate.clip_max_y * CTX_FULL_AA, scan_end); - if (CTX_UNLIKELY(scan_start > scan_end || - (scan_start > (rasterizer->blit_y + (rasterizer->blit_height-1)) * CTX_FULL_AA) || - (scan_end < (rasterizer->blit_y) * CTX_FULL_AA))) - { - /* not affecting this rasterizers scanlines */ - return; - } +static MZ_FORCEINLINE void tdefl_record_match(tdefl_compressor *d, mz_uint match_len, mz_uint match_dist) +{ + mz_uint32 s0, s1; - rasterizer->horizontal_edges = - rasterizer->needs_aa3 = - rasterizer->needs_aa5 = - rasterizer->needs_aa15 = 0; + MZ_ASSERT((match_len >= TDEFL_MIN_MATCH_LEN) && (match_dist >= 1) && (match_dist <= TDEFL_LZ_DICT_SIZE)); - ctx_rasterizer_sort_edges (rasterizer); - rasterizer->scanline = scan_start; - ctx_rasterizer_feed_edges (rasterizer, 0); + d->m_total_lz_bytes += match_len; - int avoid_direct = (0 -#if CTX_ENABLE_CLIP - || rasterizer->clip_buffer -#endif -#if CTX_ENABLE_SHADOW_BLUR - || rasterizer->in_shadow -#endif -#if CTX_SHAPE_CACHE - || shape != NULL -#endif - ); + d->m_pLZ_code_buf[0] = (mz_uint8)(match_len - TDEFL_MIN_MATCH_LEN); - for (; rasterizer->scanline <= scan_end;) - { + match_dist -= 1; + d->m_pLZ_code_buf[1] = (mz_uint8)(match_dist & 0xFF); + d->m_pLZ_code_buf[2] = (mz_uint8)(match_dist >> 8); + d->m_pLZ_code_buf += 3; - if (rasterizer->active_edges == 0 && rasterizer->pending_edges == 0) - { /* no edges */ - ctx_rasterizer_feed_edges (rasterizer, 0); - ctx_rasterizer_increment_edges (rasterizer, CTX_FULL_AA); - dst += blit_stride; -#if CTX_SHAPE_CACHE - if (shape) - { - memset (coverage, 0, coverage_size); - coverage += shape->width; - } -#endif - rasterizer->prev_active_edges = rasterizer->active_edges; - continue; + *d->m_pLZ_flags = (mz_uint8)((*d->m_pLZ_flags >> 1) | 0x80); + if (--d->m_num_flags_left == 0) + { + d->m_num_flags_left = 8; + d->m_pLZ_flags = d->m_pLZ_code_buf++; } - else if (real_aa != 1 && ( (rasterizer->horizontal_edges!=0) - || (rasterizer->active_edges != rasterizer->prev_active_edges) - || (rasterizer->active_edges + rasterizer->pending_edges == rasterizer->ending_edges) - )) - { /* needs full AA */ - int increment = CTX_FULL_AA/real_aa; - memset (coverage, 0, coverage_size); - for (int i = 0; i < real_aa; i++) + + s0 = s_tdefl_small_dist_sym[match_dist & 511]; + s1 = s_tdefl_large_dist_sym[(match_dist >> 8) & 127]; + d->m_huff_count[1][(match_dist < 512) ? s0 : s1]++; + d->m_huff_count[0][s_tdefl_len_sym[match_len - TDEFL_MIN_MATCH_LEN]]++; +} + +static mz_bool tdefl_compress_normal(tdefl_compressor *d) +{ + const mz_uint8 *pSrc = d->m_pSrc; + size_t src_buf_left = d->m_src_buf_left; + tdefl_flush flush = d->m_flush; + + while ((src_buf_left) || ((flush) && (d->m_lookahead_size))) + { + mz_uint len_to_move, cur_match_dist, cur_match_len, cur_pos; + /* Update dictionary and hash chains. Keeps the lookahead size equal to TDEFL_MAX_MATCH_LEN. */ + if ((d->m_lookahead_size + d->m_dict_size) >= (TDEFL_MIN_MATCH_LEN - 1)) { - ctx_rasterizer_feed_edges (rasterizer, 0); - ctx_rasterizer_generate_coverage (rasterizer, minx, maxx, coverage, is_winding, real_aa, real_fraction); - ctx_rasterizer_increment_edges (rasterizer, increment); + mz_uint dst_pos = (d->m_lookahead_pos + d->m_lookahead_size) & TDEFL_LZ_DICT_SIZE_MASK, ins_pos = d->m_lookahead_pos + d->m_lookahead_size - 2; + mz_uint hash = (d->m_dict[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] << TDEFL_LZ_HASH_SHIFT) ^ d->m_dict[(ins_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK]; + mz_uint num_bytes_to_process = (mz_uint)MZ_MIN(src_buf_left, TDEFL_MAX_MATCH_LEN - d->m_lookahead_size); + const mz_uint8 *pSrc_end = pSrc ? pSrc + num_bytes_to_process : NULL; + src_buf_left -= num_bytes_to_process; + d->m_lookahead_size += num_bytes_to_process; + while (pSrc != pSrc_end) + { + mz_uint8 c = *pSrc++; + d->m_dict[dst_pos] = c; + if (dst_pos < (TDEFL_MAX_MATCH_LEN - 1)) + d->m_dict[TDEFL_LZ_DICT_SIZE + dst_pos] = c; + hash = ((hash << TDEFL_LZ_HASH_SHIFT) ^ c) & (TDEFL_LZ_HASH_SIZE - 1); + d->m_next[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] = d->m_hash[hash]; + d->m_hash[hash] = (mz_uint16)(ins_pos); + dst_pos = (dst_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK; + ins_pos++; + } } - } - else if (rasterizer->needs_aa3 == 0) - { - if (! avoid_direct) - { /* can generate with direct rendering to target (we're not using shape cache) */ - ctx_rasterizer_increment_edges (rasterizer, CTX_AA_HALFSTEP2); - ctx_rasterizer_feed_edges (rasterizer, 0); + else + { + while ((src_buf_left) && (d->m_lookahead_size < TDEFL_MAX_MATCH_LEN)) + { + mz_uint8 c = *pSrc++; + mz_uint dst_pos = (d->m_lookahead_pos + d->m_lookahead_size) & TDEFL_LZ_DICT_SIZE_MASK; + src_buf_left--; + d->m_dict[dst_pos] = c; + if (dst_pos < (TDEFL_MAX_MATCH_LEN - 1)) + d->m_dict[TDEFL_LZ_DICT_SIZE + dst_pos] = c; + if ((++d->m_lookahead_size + d->m_dict_size) >= TDEFL_MIN_MATCH_LEN) + { + mz_uint ins_pos = d->m_lookahead_pos + (d->m_lookahead_size - 1) - 2; + mz_uint hash = ((d->m_dict[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] << (TDEFL_LZ_HASH_SHIFT * 2)) ^ (d->m_dict[(ins_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK] << TDEFL_LZ_HASH_SHIFT) ^ c) & (TDEFL_LZ_HASH_SIZE - 1); + d->m_next[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] = d->m_hash[hash]; + d->m_hash[hash] = (mz_uint16)(ins_pos); + } + } + } + d->m_dict_size = MZ_MIN(TDEFL_LZ_DICT_SIZE - d->m_lookahead_size, d->m_dict_size); + if ((!flush) && (d->m_lookahead_size < TDEFL_MAX_MATCH_LEN)) + break; - ctx_rasterizer_generate_coverage_apply (rasterizer, minx, maxx, coverage, is_winding, comp); - ctx_rasterizer_increment_edges (rasterizer, CTX_AA_HALFSTEP); + /* Simple lazy/greedy parsing state machine. */ + len_to_move = 1; + cur_match_dist = 0; + cur_match_len = d->m_saved_match_len ? d->m_saved_match_len : (TDEFL_MIN_MATCH_LEN - 1); + cur_pos = d->m_lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK; + if (d->m_flags & (TDEFL_RLE_MATCHES | TDEFL_FORCE_ALL_RAW_BLOCKS)) + { + if ((d->m_dict_size) && (!(d->m_flags & TDEFL_FORCE_ALL_RAW_BLOCKS))) + { + mz_uint8 c = d->m_dict[(cur_pos - 1) & TDEFL_LZ_DICT_SIZE_MASK]; + cur_match_len = 0; + while (cur_match_len < d->m_lookahead_size) + { + if (d->m_dict[cur_pos + cur_match_len] != c) + break; + cur_match_len++; + } + if (cur_match_len < TDEFL_MIN_MATCH_LEN) + cur_match_len = 0; + else + cur_match_dist = 1; + } + } + else + { + tdefl_find_match(d, d->m_lookahead_pos, d->m_dict_size, d->m_lookahead_size, &cur_match_dist, &cur_match_len); + } + if (((cur_match_len == TDEFL_MIN_MATCH_LEN) && (cur_match_dist >= 8U * 1024U)) || (cur_pos == cur_match_dist) || ((d->m_flags & TDEFL_FILTER_MATCHES) && (cur_match_len <= 5))) + { + cur_match_dist = cur_match_len = 0; + } + if (d->m_saved_match_len) + { + if (cur_match_len > d->m_saved_match_len) + { + tdefl_record_literal(d, (mz_uint8)d->m_saved_lit); + if (cur_match_len >= 128) + { + tdefl_record_match(d, cur_match_len, cur_match_dist); + d->m_saved_match_len = 0; + len_to_move = cur_match_len; + } + else + { + d->m_saved_lit = d->m_dict[cur_pos]; + d->m_saved_match_dist = cur_match_dist; + d->m_saved_match_len = cur_match_len; + } + } + else + { + tdefl_record_match(d, d->m_saved_match_len, d->m_saved_match_dist); + len_to_move = d->m_saved_match_len - 1; + d->m_saved_match_len = 0; + } + } + else if (!cur_match_dist) + tdefl_record_literal(d, d->m_dict[MZ_MIN(cur_pos, sizeof(d->m_dict) - 1)]); + else if ((d->m_greedy_parsing) || (d->m_flags & TDEFL_RLE_MATCHES) || (cur_match_len >= 128)) + { + tdefl_record_match(d, cur_match_len, cur_match_dist); + len_to_move = cur_match_len; + } + else + { + d->m_saved_lit = d->m_dict[MZ_MIN(cur_pos, sizeof(d->m_dict) - 1)]; + d->m_saved_match_dist = cur_match_dist; + d->m_saved_match_len = cur_match_len; + } + /* Move the lookahead forward by len_to_move bytes. */ + d->m_lookahead_pos += len_to_move; + MZ_ASSERT(d->m_lookahead_size >= len_to_move); + d->m_lookahead_size -= len_to_move; + d->m_dict_size = MZ_MIN(d->m_dict_size + len_to_move, (mz_uint)TDEFL_LZ_DICT_SIZE); + /* Check if it's time to flush the current LZ codes to the internal output buffer. */ + if ((d->m_pLZ_code_buf > &d->m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE - 8]) || + ((d->m_total_lz_bytes > 31 * 1024) && (((((mz_uint)(d->m_pLZ_code_buf - d->m_lz_code_buf) * 115) >> 7) >= d->m_total_lz_bytes) || (d->m_flags & TDEFL_FORCE_ALL_RAW_BLOCKS)))) + { + int n; + d->m_pSrc = pSrc; + d->m_src_buf_left = src_buf_left; + if ((n = tdefl_flush_block(d, 0)) != 0) + return (n < 0) ? MZ_FALSE : MZ_TRUE; + } + } - dst += blit_stride; - rasterizer->prev_active_edges = rasterizer->active_edges; - continue; - } - else - { /* cheap fully correct AA, to coverage mask / clipping */ - ctx_rasterizer_increment_edges (rasterizer, CTX_AA_HALFSTEP2); - ctx_rasterizer_feed_edges (rasterizer, 0); + d->m_pSrc = pSrc; + d->m_src_buf_left = src_buf_left; + return MZ_TRUE; +} - memset (coverage, 0, coverage_size); - ctx_rasterizer_generate_coverage_set (rasterizer, minx, maxx, coverage, is_winding); - ctx_rasterizer_increment_edges (rasterizer, CTX_AA_HALFSTEP); - } +static tdefl_status tdefl_flush_output_buffer(tdefl_compressor *d) +{ + if (d->m_pIn_buf_size) + { + *d->m_pIn_buf_size = d->m_pSrc - (const mz_uint8 *)d->m_pIn_buf; } - else if (ctx_rasterizer_is_simple (rasterizer)) - { /* 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, 1); - memset (coverage, 0, coverage_size); - if (!avoid_direct) - { - ctx_rasterizer_generate_coverage_apply2 (rasterizer, minx, maxx, coverage, is_winding, - comp); - ctx_rasterizer_increment_edges (rasterizer, CTX_AA_HALFSTEP); - dst += blit_stride; - rasterizer->prev_active_edges = rasterizer->active_edges; - continue; - } - ctx_rasterizer_generate_coverage_set2 (rasterizer, minx, maxx, coverage, is_winding); - ctx_rasterizer_increment_edges (rasterizer, CTX_AA_HALFSTEP); - if (real_aa == 1) - { - for (int x = minx; x <= maxx; x ++) - coverage[x] = coverage[x] > 127?255:0; - } + if (d->m_pOut_buf_size) + { + size_t n = MZ_MIN(*d->m_pOut_buf_size - d->m_out_buf_ofs, d->m_output_flush_remaining); + memcpy((mz_uint8 *)d->m_pOut_buf + d->m_out_buf_ofs, d->m_output_buf + d->m_output_flush_ofs, n); + d->m_output_flush_ofs += (mz_uint)n; + d->m_output_flush_remaining -= (mz_uint)n; + d->m_out_buf_ofs += n; + + *d->m_pOut_buf_size = d->m_out_buf_ofs; } - else - { /* determine level of oversampling based on lowest steepness edges */ - int aa = 3; - if (rasterizer->needs_aa5 && real_aa >=5) - { - aa = 5; - if (rasterizer->needs_aa15 && real_aa >=15) - aa = 15; - } - int scanline_increment = 15/aa; - memset (coverage, 0, coverage_size); - uint8_t fraction = 255/aa; - for (int i = 0; i < CTX_FULL_AA; i+= scanline_increment) - { - ctx_rasterizer_feed_edges (rasterizer, 0); - ctx_rasterizer_generate_coverage (rasterizer, minx, maxx, coverage, is_winding, aa, fraction); - ctx_rasterizer_increment_edges (rasterizer, scanline_increment); - } + return (d->m_finished && !d->m_output_flush_remaining) ? TDEFL_STATUS_DONE : TDEFL_STATUS_OKAY; +} + +tdefl_status tdefl_compress(tdefl_compressor *d, const void *pIn_buf, size_t *pIn_buf_size, void *pOut_buf, size_t *pOut_buf_size, tdefl_flush flush) +{ + if (!d) + { + if (pIn_buf_size) + *pIn_buf_size = 0; + if (pOut_buf_size) + *pOut_buf_size = 0; + return TDEFL_STATUS_BAD_PARAM; } - ctx_coverage_post_process (rasterizer, minx, maxx, coverage - minx, NULL, NULL); -#if CTX_SHAPE_CACHE - if (shape == NULL) -#endif - { - rasterizer->apply_coverage (rasterizer, - &dst[(minx * rasterizer->format->bpp) /8], - rasterizer->color, - minx, - coverage, - maxx-minx+ 1); - } -#if CTX_SHAPE_CACHE - else - { - coverage += shape->width; - } -#endif - dst += blit_stride; - rasterizer->prev_active_edges = rasterizer->active_edges; + d->m_pIn_buf = pIn_buf; + d->m_pIn_buf_size = pIn_buf_size; + d->m_pOut_buf = pOut_buf; + d->m_pOut_buf_size = pOut_buf_size; + d->m_pSrc = (const mz_uint8 *)(pIn_buf); + d->m_src_buf_left = pIn_buf_size ? *pIn_buf_size : 0; + d->m_out_buf_ofs = 0; + d->m_flush = flush; + + if (((d->m_pPut_buf_func != NULL) == ((pOut_buf != NULL) || (pOut_buf_size != NULL))) || (d->m_prev_return_status != TDEFL_STATUS_OKAY) || + (d->m_wants_to_finish && (flush != TDEFL_FINISH)) || (pIn_buf_size && *pIn_buf_size && !pIn_buf) || (pOut_buf_size && *pOut_buf_size && !pOut_buf)) + { + if (pIn_buf_size) + *pIn_buf_size = 0; + if (pOut_buf_size) + *pOut_buf_size = 0; + return (d->m_prev_return_status = TDEFL_STATUS_BAD_PARAM); } + d->m_wants_to_finish |= (flush == TDEFL_FINISH); - 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)) - { - /* fill in the rest of the blitrect when compositing mode permits it */ - uint8_t nocoverage[rasterizer->blit_width]; - //int gscan_start = rasterizer->state->gstate.clip_min_y * CTX_FULL_AA; - int gscan_start = rasterizer->state->gstate.clip_min_y * CTX_FULL_AA; - int gscan_end = rasterizer->state->gstate.clip_max_y * CTX_FULL_AA; - memset (nocoverage, 0, sizeof(nocoverage)); - int startx = rasterizer->state->gstate.clip_min_x; - int endx = rasterizer->state->gstate.clip_max_x; - int clipw = endx-startx + 1; - uint8_t *dst = ( (uint8_t *) rasterizer->buf); + if ((d->m_output_flush_remaining) || (d->m_finished)) + return (d->m_prev_return_status = tdefl_flush_output_buffer(d)); - dst = (uint8_t*)(rasterizer->buf) + rasterizer->blit_stride * (gscan_start / CTX_FULL_AA); - for (rasterizer->scanline = gscan_start; rasterizer->scanline < scan_start;) - { - rasterizer->apply_coverage (rasterizer, - &dst[ (startx * rasterizer->format->bpp) /8], - rasterizer->color, - 0, - nocoverage, clipw); - rasterizer->scanline += CTX_FULL_AA; - dst += rasterizer->blit_stride; - } - if (minx < startx) - { - dst = (uint8_t*)(rasterizer->buf) + rasterizer->blit_stride * (scan_start / CTX_FULL_AA); - for (rasterizer->scanline = scan_start; rasterizer->scanline < scan_end;) - { - rasterizer->apply_coverage (rasterizer, - &dst[ (startx * rasterizer->format->bpp) /8], - rasterizer->color, - 0, - nocoverage, minx-startx); - dst += blit_stride; - } - } +#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN + if (((d->m_flags & TDEFL_MAX_PROBES_MASK) == 1) && + ((d->m_flags & TDEFL_GREEDY_PARSING_FLAG) != 0) && + ((d->m_flags & (TDEFL_FILTER_MATCHES | TDEFL_FORCE_ALL_RAW_BLOCKS | TDEFL_RLE_MATCHES)) == 0)) + { + if (!tdefl_compress_fast(d)) + return d->m_prev_return_status; + } + else +#endif /* #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN */ + { + if (!tdefl_compress_normal(d)) + return d->m_prev_return_status; + } - if (endx > maxx) - { - dst = (uint8_t*)(rasterizer->buf) + rasterizer->blit_stride * (scan_start / CTX_FULL_AA); - for (rasterizer->scanline = scan_start; rasterizer->scanline < scan_end;) - { - rasterizer->apply_coverage (rasterizer, - &dst[ (maxx * rasterizer->format->bpp) /8], - rasterizer->color, - 0, - nocoverage, endx-maxx); + if ((d->m_flags & (TDEFL_WRITE_ZLIB_HEADER | TDEFL_COMPUTE_ADLER32)) && (pIn_buf)) + d->m_adler32 = (mz_uint32)mz_adler32(d->m_adler32, (const mz_uint8 *)pIn_buf, d->m_pSrc - (const mz_uint8 *)pIn_buf); - rasterizer->scanline += CTX_FULL_AA; - dst += rasterizer->blit_stride; - } - } -#if 1 - dst = (uint8_t*)(rasterizer->buf) + rasterizer->blit_stride * (scan_end / CTX_FULL_AA); - // XXX this crashes under valgrind/asan - if(0)for (rasterizer->scanline = scan_end; rasterizer->scanline/CTX_FULL_AA < gscan_end-1;) - { - rasterizer->apply_coverage (rasterizer, - &dst[ (startx * rasterizer->format->bpp) /8], - rasterizer->color, - 0, - nocoverage, clipw-1); + if ((flush) && (!d->m_lookahead_size) && (!d->m_src_buf_left) && (!d->m_output_flush_remaining)) + { + if (tdefl_flush_block(d, flush) < 0) + return d->m_prev_return_status; + d->m_finished = (flush == TDEFL_FINISH); + if (flush == TDEFL_FULL_FLUSH) + { + MZ_CLEAR_ARR(d->m_hash); + MZ_CLEAR_ARR(d->m_next); + d->m_dict_size = 0; + } + } - rasterizer->scanline += CTX_FULL_AA; - dst += blit_stride; - } -#endif - } + return (d->m_prev_return_status = tdefl_flush_output_buffer(d)); } +tdefl_status tdefl_compress_buffer(tdefl_compressor *d, const void *pIn_buf, size_t in_buf_size, tdefl_flush flush) +{ + MZ_ASSERT(d->m_pPut_buf_func); + return tdefl_compress(d, pIn_buf, &in_buf_size, NULL, NULL, flush); +} -#if CTX_INLINE_FILL_RULE -void -CTX_SIMD_SUFFIX (ctx_rasterizer_rasterize_edges) (CtxRasterizer *rasterizer, const int fill_rule -#if CTX_SHAPE_CACHE - ,CtxShapeEntry *shape -#endif - ); -#else - -void -CTX_SIMD_SUFFIX (ctx_rasterizer_rasterize_edges) (CtxRasterizer *rasterizer, const int fill_rule -#if CTX_SHAPE_CACHE - ,CtxShapeEntry *shape -#endif - ); -#endif +tdefl_status tdefl_init(tdefl_compressor *d, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags) +{ + d->m_pPut_buf_func = pPut_buf_func; + d->m_pPut_buf_user = pPut_buf_user; + d->m_flags = (mz_uint)(flags); + d->m_max_probes[0] = 1 + ((flags & 0xFFF) + 2) / 3; + d->m_greedy_parsing = (flags & TDEFL_GREEDY_PARSING_FLAG) != 0; + d->m_max_probes[1] = 1 + (((flags & 0xFFF) >> 2) + 2) / 3; + if (!(flags & TDEFL_NONDETERMINISTIC_PARSING_FLAG)) + MZ_CLEAR_ARR(d->m_hash); + d->m_lookahead_pos = d->m_lookahead_size = d->m_dict_size = d->m_total_lz_bytes = d->m_lz_code_buf_dict_pos = d->m_bits_in = 0; + d->m_output_flush_ofs = d->m_output_flush_remaining = d->m_finished = d->m_block_index = d->m_bit_buffer = d->m_wants_to_finish = 0; + d->m_pLZ_code_buf = d->m_lz_code_buf + 1; + d->m_pLZ_flags = d->m_lz_code_buf; + *d->m_pLZ_flags = 0; + d->m_num_flags_left = 8; + d->m_pOutput_buf = d->m_output_buf; + d->m_pOutput_buf_end = d->m_output_buf; + d->m_prev_return_status = TDEFL_STATUS_OKAY; + d->m_saved_match_dist = d->m_saved_match_len = d->m_saved_lit = 0; + d->m_adler32 = 1; + d->m_pIn_buf = NULL; + d->m_pOut_buf = NULL; + d->m_pIn_buf_size = NULL; + d->m_pOut_buf_size = NULL; + d->m_flush = TDEFL_NO_FLUSH; + d->m_pSrc = NULL; + d->m_src_buf_left = 0; + d->m_out_buf_ofs = 0; + if (!(flags & TDEFL_NONDETERMINISTIC_PARSING_FLAG)) + MZ_CLEAR_ARR(d->m_dict); + memset(&d->m_huff_count[0][0], 0, sizeof(d->m_huff_count[0][0]) * TDEFL_MAX_HUFF_SYMBOLS_0); + memset(&d->m_huff_count[1][0], 0, sizeof(d->m_huff_count[1][0]) * TDEFL_MAX_HUFF_SYMBOLS_1); + return TDEFL_STATUS_OKAY; +} +tdefl_status tdefl_get_prev_return_status(tdefl_compressor *d) +{ + return d->m_prev_return_status; +} -#if CTX_INLINE_FILL_RULE -void -CTX_SIMD_SUFFIX (ctx_rasterizer_rasterize_edges) (CtxRasterizer *rasterizer, const int fill_rule -#if CTX_SHAPE_CACHE - ,CtxShapeEntry *shape -#endif - ) +mz_uint32 tdefl_get_adler32(tdefl_compressor *d) { - if (fill_rule) - { - ctx_rasterizer_rasterize_edges2 (rasterizer, 1 -#if CTX_SHAPE_CACHE - ,shape -#endif - ); - } - else - { - ctx_rasterizer_rasterize_edges2 (rasterizer, 0 -#if CTX_SHAPE_CACHE - ,shape -#endif - ); - } + return d->m_adler32; } -#else -void -CTX_SIMD_SUFFIX (ctx_rasterizer_rasterize_edges) (CtxRasterizer *rasterizer, const int fill_rule -#if CTX_SHAPE_CACHE - ,CtxShapeEntry *shape -#endif - ) +mz_bool tdefl_compress_mem_to_output(const void *pBuf, size_t buf_len, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags) { - ctx_rasterizer_rasterize_edges2 (rasterizer, fill_rule -#if CTX_SHAPE_CACHE - ,shape -#endif - ); + tdefl_compressor *pComp; + mz_bool succeeded; + if (((buf_len) && (!pBuf)) || (!pPut_buf_func)) + return MZ_FALSE; + pComp = (tdefl_compressor *)MZ_MALLOC(sizeof(tdefl_compressor)); + if (!pComp) + return MZ_FALSE; + succeeded = (tdefl_init(pComp, pPut_buf_func, pPut_buf_user, flags) == TDEFL_STATUS_OKAY); + succeeded = succeeded && (tdefl_compress_buffer(pComp, pBuf, buf_len, TDEFL_FINISH) == TDEFL_STATUS_DONE); + MZ_FREE(pComp); + return succeeded; } -#endif +typedef struct +{ + size_t m_size, m_capacity; + mz_uint8 *m_pBuf; + mz_bool m_expandable; +} tdefl_output_buffer; +static mz_bool tdefl_output_buffer_putter(const void *pBuf, int len, void *pUser) +{ + tdefl_output_buffer *p = (tdefl_output_buffer *)pUser; + size_t new_size = p->m_size + len; + if (new_size > p->m_capacity) + { + size_t new_capacity = p->m_capacity; + mz_uint8 *pNew_buf; + if (!p->m_expandable) + return MZ_FALSE; + do + { + new_capacity = MZ_MAX(128U, new_capacity << 1U); + } while (new_size > new_capacity); + pNew_buf = (mz_uint8 *)MZ_REALLOC(p->m_pBuf, new_capacity); + if (!pNew_buf) + return MZ_FALSE; + p->m_pBuf = pNew_buf; + p->m_capacity = new_capacity; + } + memcpy((mz_uint8 *)p->m_pBuf + p->m_size, pBuf, len); + p->m_size = new_size; + return MZ_TRUE; +} +void *tdefl_compress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags) +{ + tdefl_output_buffer out_buf; + MZ_CLEAR_OBJ(out_buf); + if (!pOut_len) + return MZ_FALSE; + else + *pOut_len = 0; + out_buf.m_expandable = MZ_TRUE; + if (!tdefl_compress_mem_to_output(pSrc_buf, src_buf_len, tdefl_output_buffer_putter, &out_buf, flags)) + return NULL; + *pOut_len = out_buf.m_size; + return out_buf.m_pBuf; +} -extern CtxPixelFormatInfo *ctx_pixel_formats; -void CTX_SIMD_SUFFIX(ctx_simd_setup)(void); -void CTX_SIMD_SUFFIX(ctx_simd_setup)(void) +size_t tdefl_compress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags) { - ctx_pixel_formats = CTX_SIMD_SUFFIX(ctx_pixel_formats); - ctx_composite_setup = CTX_SIMD_SUFFIX(ctx_composite_setup); - 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); - ctx_composite_stroke_rect = CTX_SIMD_SUFFIX(ctx_composite_stroke_rect); + tdefl_output_buffer out_buf; + MZ_CLEAR_OBJ(out_buf); + if (!pOut_buf) + return 0; + out_buf.m_pBuf = (mz_uint8 *)pOut_buf; + out_buf.m_capacity = out_buf_len; + if (!tdefl_compress_mem_to_output(pSrc_buf, src_buf_len, tdefl_output_buffer_putter, &out_buf, flags)) + return 0; + return out_buf.m_size; +} + +static const mz_uint s_tdefl_num_probes[11] = { 0, 1, 6, 32, 16, 32, 128, 256, 512, 768, 1500 }; + +/* level may actually range from [0,10] (10 is a "hidden" max level, where we want a bit more compression and it's fine if throughput to fall off a cliff on some files). */ +mz_uint tdefl_create_comp_flags_from_zip_params(int level, int window_bits, int strategy) +{ + mz_uint comp_flags = s_tdefl_num_probes[(level >= 0) ? MZ_MIN(10, level) : MZ_DEFAULT_LEVEL] | ((level <= 3) ? TDEFL_GREEDY_PARSING_FLAG : 0); + if (window_bits > 0) + comp_flags |= TDEFL_WRITE_ZLIB_HEADER; + + if (!level) + comp_flags |= TDEFL_FORCE_ALL_RAW_BLOCKS; + else if (strategy == MZ_FILTERED) + comp_flags |= TDEFL_FILTER_MATCHES; + else if (strategy == MZ_HUFFMAN_ONLY) + comp_flags &= ~TDEFL_MAX_PROBES_MASK; + else if (strategy == MZ_FIXED) + comp_flags |= TDEFL_FORCE_ALL_STATIC_BLOCKS; + else if (strategy == MZ_RLE) + comp_flags |= TDEFL_RLE_MATCHES; + + return comp_flags; +} + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable : 4204) /* nonstandard extension used : non-constant aggregate initializer (also supported by GNU C and C99, so no big deal) */ +#endif + +/* Simple PNG writer function by Alex Evans, 2011. Released into the public domain: https://gist.github.com/908299, more context at + http://altdevblogaday.org/2011/04/06/a-smaller-jpg-encoder/. + This is actually a modification of Alex's original code so PNG files generated by this function pass pngcheck. */ +void *tdefl_write_image_to_png_file_in_memory_ex(const void *pImage, int w, int h, int num_chans, size_t *pLen_out, mz_uint level, mz_bool flip) +{ + /* Using a local copy of this array here in case MINIZ_NO_ZLIB_APIS was defined. */ + static const mz_uint s_tdefl_png_num_probes[11] = { 0, 1, 6, 32, 16, 32, 128, 256, 512, 768, 1500 }; + tdefl_compressor *pComp = (tdefl_compressor *)MZ_MALLOC(sizeof(tdefl_compressor)); + tdefl_output_buffer out_buf; + int i, bpl = w * num_chans, y, z; + mz_uint32 c; + *pLen_out = 0; + if (!pComp) + return NULL; + MZ_CLEAR_OBJ(out_buf); + out_buf.m_expandable = MZ_TRUE; + out_buf.m_capacity = 57 + MZ_MAX(64, (1 + bpl) * h); + if (NULL == (out_buf.m_pBuf = (mz_uint8 *)MZ_MALLOC(out_buf.m_capacity))) + { + MZ_FREE(pComp); + return NULL; + } + /* write dummy header */ + for (z = 41; z; --z) + tdefl_output_buffer_putter(&z, 1, &out_buf); + /* compress image data */ + tdefl_init(pComp, tdefl_output_buffer_putter, &out_buf, s_tdefl_png_num_probes[MZ_MIN(10, level)] | TDEFL_WRITE_ZLIB_HEADER); + for (y = 0; y < h; ++y) + { + tdefl_compress_buffer(pComp, &z, 1, TDEFL_NO_FLUSH); + tdefl_compress_buffer(pComp, (mz_uint8 *)pImage + (flip ? (h - 1 - y) : y) * bpl, bpl, TDEFL_NO_FLUSH); + } + if (tdefl_compress_buffer(pComp, NULL, 0, TDEFL_FINISH) != TDEFL_STATUS_DONE) + { + MZ_FREE(pComp); + MZ_FREE(out_buf.m_pBuf); + return NULL; + } + /* write real header */ + *pLen_out = out_buf.m_size - 41; + { + static const mz_uint8 chans[] = { 0x00, 0x00, 0x04, 0x02, 0x06 }; + mz_uint8 pnghdr[41] = { 0x89, 0x50, 0x4e, 0x47, 0x0d, + 0x0a, 0x1a, 0x0a, 0x00, 0x00, + 0x00, 0x0d, 0x49, 0x48, 0x44, + 0x52, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x08, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x49, 0x44, 0x41, + 0x54 }; + pnghdr[18] = (mz_uint8)(w >> 8); + pnghdr[19] = (mz_uint8)w; + pnghdr[22] = (mz_uint8)(h >> 8); + pnghdr[23] = (mz_uint8)h; + pnghdr[25] = chans[num_chans]; + pnghdr[33] = (mz_uint8)(*pLen_out >> 24); + pnghdr[34] = (mz_uint8)(*pLen_out >> 16); + pnghdr[35] = (mz_uint8)(*pLen_out >> 8); + pnghdr[36] = (mz_uint8)*pLen_out; + c = (mz_uint32)mz_crc32(MZ_CRC32_INIT, pnghdr + 12, 17); + for (i = 0; i < 4; ++i, c <<= 8) + ((mz_uint8 *)(pnghdr + 29))[i] = (mz_uint8)(c >> 24); + memcpy(out_buf.m_pBuf, pnghdr, 41); + } + /* write footer (IDAT CRC-32, followed by IEND chunk) */ + if (!tdefl_output_buffer_putter("\0\0\0\0\0\0\0\0\x49\x45\x4e\x44\xae\x42\x60\x82", 16, &out_buf)) + { + *pLen_out = 0; + MZ_FREE(pComp); + MZ_FREE(out_buf.m_pBuf); + return NULL; + } + c = (mz_uint32)mz_crc32(MZ_CRC32_INIT, out_buf.m_pBuf + 41 - 4, *pLen_out + 4); + for (i = 0; i < 4; ++i, c <<= 8) + (out_buf.m_pBuf + out_buf.m_size - 16)[i] = (mz_uint8)(c >> 24); + /* compute final size of file, grab compressed data buffer and return */ + *pLen_out += 57; + MZ_FREE(pComp); + return out_buf.m_pBuf; +} +void *tdefl_write_image_to_png_file_in_memory(const void *pImage, int w, int h, int num_chans, size_t *pLen_out) +{ + /* Level 6 corresponds to TDEFL_DEFAULT_MAX_PROBES or MZ_DEFAULT_LEVEL (but we can't depend on MZ_DEFAULT_LEVEL being available in case the zlib API's where #defined out) */ + return tdefl_write_image_to_png_file_in_memory_ex(pImage, w, h, num_chans, pLen_out, 6, MZ_FALSE); +} + +#ifndef MINIZ_NO_MALLOC +/* Allocate the tdefl_compressor and tinfl_decompressor structures in C so that */ +/* non-C language bindings to tdefL_ and tinfl_ API don't need to worry about */ +/* structure size and allocation mechanism. */ +tdefl_compressor *tdefl_compressor_alloc(void) +{ + return (tdefl_compressor *)MZ_MALLOC(sizeof(tdefl_compressor)); +} + +void tdefl_compressor_free(tdefl_compressor *pComp) +{ + MZ_FREE(pComp); +} +#endif + +#ifdef _MSC_VER +#pragma warning(pop) #endif + +#ifdef __cplusplus } +#endif +#endif /*#ifndef MINIZ_NO_DEFLATE_APIS*/ + /************************************************************************** + * + * Copyright 2013-2014 RAD Game Tools and Valve Software + * Copyright 2010-2014 Rich Geldreich and Tenacious Software LLC + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + **************************************************************************/ -#endif -#endif -#if CTX_IMPLEMENTATION -#if CTX_RASTERIZER -inline static float ctx_fast_hypotf (float x, float y) -{ - 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; } -} +#ifndef MINIZ_NO_INFLATE_APIS +#ifdef __cplusplus +extern "C" { +#endif +/* ------------------- Low-level Decompression (completely independent from all compression API's) */ -static void -ctx_rasterizer_gradient_add_stop (CtxRasterizer *rasterizer, float pos, float *rgba) -{ - /* FIXME XXX we only have one gradient, but might need separate gradients - * for fill/stroke ! - * - */ - CtxGradient *gradient = &rasterizer->state->gradient; - CtxGradientStop *stop = &gradient->stops[gradient->n_stops]; - stop->pos = pos; - ctx_color_set_rgba (rasterizer->state, & (stop->color), rgba[0], rgba[1], rgba[2], rgba[3]); - if (gradient->n_stops < CTX_MAX_GRADIENT_STOPS-1) //we'll keep overwriting the last when out of stops - { gradient->n_stops++; } -} +#define TINFL_MEMCPY(d, s, l) memcpy(d, s, l) +#define TINFL_MEMSET(p, c, l) memset(p, c, l) -static inline void ctx_rasterizer_update_inner_point (CtxRasterizer *rasterizer, int x, int y) -{ - rasterizer->scan_min = ctx_mini (y, rasterizer->scan_min); - rasterizer->scan_max = ctx_maxi (y, rasterizer->scan_max); - rasterizer->col_min = ctx_mini (x, rasterizer->col_min); - rasterizer->col_max = ctx_maxi (x, rasterizer->col_max); - rasterizer->inner_x = x; - rasterizer->inner_y = y; +#define TINFL_CR_BEGIN \ + switch (r->m_state) \ + { \ + case 0: +#define TINFL_CR_RETURN(state_index, result) \ + do \ + { \ + status = result; \ + r->m_state = state_index; \ + goto common_exit; \ + case state_index:; \ + } \ + MZ_MACRO_END +#define TINFL_CR_RETURN_FOREVER(state_index, result) \ + do \ + { \ + for (;;) \ + { \ + TINFL_CR_RETURN(state_index, result); \ + } \ + } \ + MZ_MACRO_END +#define TINFL_CR_FINISH } + +#define TINFL_GET_BYTE(state_index, c) \ + do \ + { \ + while (pIn_buf_cur >= pIn_buf_end) \ + { \ + TINFL_CR_RETURN(state_index, (decomp_flags & TINFL_FLAG_HAS_MORE_INPUT) ? TINFL_STATUS_NEEDS_MORE_INPUT : TINFL_STATUS_FAILED_CANNOT_MAKE_PROGRESS); \ + } \ + c = *pIn_buf_cur++; \ + } \ + MZ_MACRO_END + +#define TINFL_NEED_BITS(state_index, n) \ + do \ + { \ + mz_uint c; \ + TINFL_GET_BYTE(state_index, c); \ + bit_buf |= (((tinfl_bit_buf_t)c) << num_bits); \ + num_bits += 8; \ + } while (num_bits < (mz_uint)(n)) +#define TINFL_SKIP_BITS(state_index, n) \ + do \ + { \ + if (num_bits < (mz_uint)(n)) \ + { \ + TINFL_NEED_BITS(state_index, n); \ + } \ + bit_buf >>= (n); \ + num_bits -= (n); \ + } \ + MZ_MACRO_END +#define TINFL_GET_BITS(state_index, b, n) \ + do \ + { \ + if (num_bits < (mz_uint)(n)) \ + { \ + TINFL_NEED_BITS(state_index, n); \ + } \ + b = bit_buf & ((1 << (n)) - 1); \ + bit_buf >>= (n); \ + num_bits -= (n); \ + } \ + MZ_MACRO_END + +/* TINFL_HUFF_BITBUF_FILL() is only used rarely, when the number of bytes remaining in the input buffer falls below 2. */ +/* It reads just enough bytes from the input stream that are needed to decode the next Huffman code (and absolutely no more). It works by trying to fully decode a */ +/* Huffman code by using whatever bits are currently present in the bit buffer. If this fails, it reads another byte, and tries again until it succeeds or until the */ +/* bit buffer contains >=15 bits (deflate's max. Huffman code size). */ +#define TINFL_HUFF_BITBUF_FILL(state_index, pLookUp, pTree) \ + do \ + { \ + temp = pLookUp[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]; \ + if (temp >= 0) \ + { \ + code_len = temp >> 9; \ + if ((code_len) && (num_bits >= code_len)) \ + break; \ + } \ + else if (num_bits > TINFL_FAST_LOOKUP_BITS) \ + { \ + code_len = TINFL_FAST_LOOKUP_BITS; \ + do \ + { \ + temp = pTree[~temp + ((bit_buf >> code_len++) & 1)]; \ + } while ((temp < 0) && (num_bits >= (code_len + 1))); \ + if (temp >= 0) \ + break; \ + } \ + TINFL_GET_BYTE(state_index, c); \ + bit_buf |= (((tinfl_bit_buf_t)c) << num_bits); \ + num_bits += 8; \ + } while (num_bits < 15); + +/* TINFL_HUFF_DECODE() decodes the next Huffman coded symbol. It's more complex than you would initially expect because the zlib API expects the decompressor to never read */ +/* beyond the final byte of the deflate stream. (In other words, when this macro wants to read another byte from the input, it REALLY needs another byte in order to fully */ +/* decode the next Huffman code.) Handling this properly is particularly important on raw deflate (non-zlib) streams, which aren't followed by a byte aligned adler-32. */ +/* The slow path is only executed at the very end of the input buffer. */ +/* v1.16: The original macro handled the case at the very end of the passed-in input buffer, but we also need to handle the case where the user passes in 1+zillion bytes */ +/* following the deflate data and our non-conservative read-ahead path won't kick in here on this code. This is much trickier. */ +#define TINFL_HUFF_DECODE(state_index, sym, pLookUp, pTree) \ + do \ + { \ + int temp; \ + mz_uint code_len, c; \ + if (num_bits < 15) \ + { \ + if ((pIn_buf_end - pIn_buf_cur) < 2) \ + { \ + TINFL_HUFF_BITBUF_FILL(state_index, pLookUp, pTree); \ + } \ + else \ + { \ + bit_buf |= (((tinfl_bit_buf_t)pIn_buf_cur[0]) << num_bits) | (((tinfl_bit_buf_t)pIn_buf_cur[1]) << (num_bits + 8)); \ + pIn_buf_cur += 2; \ + num_bits += 16; \ + } \ + } \ + if ((temp = pLookUp[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= 0) \ + code_len = temp >> 9, temp &= 511; \ + else \ + { \ + code_len = TINFL_FAST_LOOKUP_BITS; \ + do \ + { \ + temp = pTree[~temp + ((bit_buf >> code_len++) & 1)]; \ + } while (temp < 0); \ + } \ + sym = temp; \ + bit_buf >>= code_len; \ + num_bits -= code_len; \ + } \ + MZ_MACRO_END + +static void tinfl_clear_tree(tinfl_decompressor *r) +{ + if (r->m_type == 0) + MZ_CLEAR_ARR(r->m_tree_0); + else if (r->m_type == 1) + MZ_CLEAR_ARR(r->m_tree_1); + else + MZ_CLEAR_ARR(r->m_tree_2); } -static inline int ctx_rasterizer_add_point (CtxRasterizer *rasterizer, int x1, int y1) +tinfl_status tinfl_decompress(tinfl_decompressor *r, const mz_uint8 *pIn_buf_next, size_t *pIn_buf_size, mz_uint8 *pOut_buf_start, mz_uint8 *pOut_buf_next, size_t *pOut_buf_size, const mz_uint32 decomp_flags) { - CtxSegment entry = {CTX_EDGE, {{0,}},0,0}; + static const mz_uint16 s_length_base[31] = { 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0 }; + static const mz_uint8 s_length_extra[31] = { 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 0, 0 }; + static const mz_uint16 s_dist_base[32] = { 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577, 0, 0 }; + static const mz_uint8 s_dist_extra[32] = { 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13 }; + static const mz_uint8 s_length_dezigzag[19] = { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 }; + static const mz_uint16 s_min_table_sizes[3] = { 257, 1, 4 }; - entry.data.s16[0]=rasterizer->inner_x; - entry.data.s16[1]=rasterizer->inner_y; + mz_int16 *pTrees[3]; + mz_uint8 *pCode_sizes[3]; - entry.data.s16[2]=x1; - entry.data.s16[3]=y1; + tinfl_status status = TINFL_STATUS_FAILED; + mz_uint32 num_bits, dist, counter, num_extra; + tinfl_bit_buf_t bit_buf; + const mz_uint8 *pIn_buf_cur = pIn_buf_next, *const pIn_buf_end = pIn_buf_next + *pIn_buf_size; + mz_uint8 *pOut_buf_cur = pOut_buf_next, *const pOut_buf_end = pOut_buf_next ? pOut_buf_next + *pOut_buf_size : NULL; + size_t out_buf_size_mask = (decomp_flags & TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF) ? (size_t)-1 : ((pOut_buf_next - pOut_buf_start) + *pOut_buf_size) - 1, dist_from_out_buf_start; - ctx_rasterizer_update_inner_point (rasterizer, x1, y1); + /* Ensure the output buffer's size is a power of 2, unless the output buffer is large enough to hold the entire output file (in which case it doesn't matter). */ + if (((out_buf_size_mask + 1) & out_buf_size_mask) || (pOut_buf_next < pOut_buf_start)) + { + *pIn_buf_size = *pOut_buf_size = 0; + return TINFL_STATUS_BAD_PARAM; + } - return ctx_edgelist_add_single (&rasterizer->edge_list, (CtxEntry*)&entry); -} + pTrees[0] = r->m_tree_0; + pTrees[1] = r->m_tree_1; + pTrees[2] = r->m_tree_2; + pCode_sizes[0] = r->m_code_size_0; + pCode_sizes[1] = r->m_code_size_1; + pCode_sizes[2] = r->m_code_size_2; -#if 0 -#define CTX_SHAPE_CACHE_PRIME1 7853 -#define CTX_SHAPE_CACHE_PRIME2 4129 -#define CTX_SHAPE_CACHE_PRIME3 3371 -#define CTX_SHAPE_CACHE_PRIME4 4221 + num_bits = r->m_num_bits; + bit_buf = r->m_bit_buf; + dist = r->m_dist; + counter = r->m_counter; + num_extra = r->m_num_extra; + dist_from_out_buf_start = r->m_dist_from_out_buf_start; + TINFL_CR_BEGIN + + bit_buf = num_bits = dist = counter = num_extra = r->m_zhdr0 = r->m_zhdr1 = 0; + r->m_z_adler32 = r->m_check_adler32 = 1; + if (decomp_flags & TINFL_FLAG_PARSE_ZLIB_HEADER) + { + TINFL_GET_BYTE(1, r->m_zhdr0); + TINFL_GET_BYTE(2, r->m_zhdr1); + counter = (((r->m_zhdr0 * 256 + r->m_zhdr1) % 31 != 0) || (r->m_zhdr1 & 32) || ((r->m_zhdr0 & 15) != 8)); + if (!(decomp_flags & TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF)) + counter |= (((1U << (8U + (r->m_zhdr0 >> 4))) > 32768U) || ((out_buf_size_mask + 1) < (size_t)(1U << (8U + (r->m_zhdr0 >> 4))))); + if (counter) + { + TINFL_CR_RETURN_FOREVER(36, TINFL_STATUS_FAILED); + } + } + + do + { + TINFL_GET_BITS(3, r->m_final, 3); + r->m_type = r->m_final >> 1; + if (r->m_type == 0) + { + TINFL_SKIP_BITS(5, num_bits & 7); + for (counter = 0; counter < 4; ++counter) + { + if (num_bits) + TINFL_GET_BITS(6, r->m_raw_header[counter], 8); + else + TINFL_GET_BYTE(7, r->m_raw_header[counter]); + } + if ((counter = (r->m_raw_header[0] | (r->m_raw_header[1] << 8))) != (mz_uint)(0xFFFF ^ (r->m_raw_header[2] | (r->m_raw_header[3] << 8)))) + { + TINFL_CR_RETURN_FOREVER(39, TINFL_STATUS_FAILED); + } + while ((counter) && (num_bits)) + { + TINFL_GET_BITS(51, dist, 8); + while (pOut_buf_cur >= pOut_buf_end) + { + TINFL_CR_RETURN(52, TINFL_STATUS_HAS_MORE_OUTPUT); + } + *pOut_buf_cur++ = (mz_uint8)dist; + counter--; + } + while (counter) + { + size_t n; + while (pOut_buf_cur >= pOut_buf_end) + { + TINFL_CR_RETURN(9, TINFL_STATUS_HAS_MORE_OUTPUT); + } + while (pIn_buf_cur >= pIn_buf_end) + { + TINFL_CR_RETURN(38, (decomp_flags & TINFL_FLAG_HAS_MORE_INPUT) ? TINFL_STATUS_NEEDS_MORE_INPUT : TINFL_STATUS_FAILED_CANNOT_MAKE_PROGRESS); + } + n = MZ_MIN(MZ_MIN((size_t)(pOut_buf_end - pOut_buf_cur), (size_t)(pIn_buf_end - pIn_buf_cur)), counter); + TINFL_MEMCPY(pOut_buf_cur, pIn_buf_cur, n); + pIn_buf_cur += n; + pOut_buf_cur += n; + counter -= (mz_uint)n; + } + } + else if (r->m_type == 3) + { + TINFL_CR_RETURN_FOREVER(10, TINFL_STATUS_FAILED); + } + else + { + if (r->m_type == 1) + { + mz_uint8 *p = r->m_code_size_0; + mz_uint i; + r->m_table_sizes[0] = 288; + r->m_table_sizes[1] = 32; + TINFL_MEMSET(r->m_code_size_1, 5, 32); + for (i = 0; i <= 143; ++i) + *p++ = 8; + for (; i <= 255; ++i) + *p++ = 9; + for (; i <= 279; ++i) + *p++ = 7; + for (; i <= 287; ++i) + *p++ = 8; + } + else + { + for (counter = 0; counter < 3; counter++) + { + TINFL_GET_BITS(11, r->m_table_sizes[counter], "\05\05\04"[counter]); + r->m_table_sizes[counter] += s_min_table_sizes[counter]; + } + MZ_CLEAR_ARR(r->m_code_size_2); + for (counter = 0; counter < r->m_table_sizes[2]; counter++) + { + mz_uint s; + TINFL_GET_BITS(14, s, 3); + r->m_code_size_2[s_length_dezigzag[counter]] = (mz_uint8)s; + } + r->m_table_sizes[2] = 19; + } + for (; (int)r->m_type >= 0; r->m_type--) + { + int tree_next, tree_cur; + mz_int16 *pLookUp; + mz_int16 *pTree; + mz_uint8 *pCode_size; + mz_uint i, j, used_syms, total, sym_index, next_code[17], total_syms[16]; + pLookUp = r->m_look_up[r->m_type]; + pTree = pTrees[r->m_type]; + pCode_size = pCode_sizes[r->m_type]; + MZ_CLEAR_ARR(total_syms); + TINFL_MEMSET(pLookUp, 0, sizeof(r->m_look_up[0])); + tinfl_clear_tree(r); + for (i = 0; i < r->m_table_sizes[r->m_type]; ++i) + total_syms[pCode_size[i]]++; + used_syms = 0, total = 0; + next_code[0] = next_code[1] = 0; + for (i = 1; i <= 15; ++i) + { + used_syms += total_syms[i]; + next_code[i + 1] = (total = ((total + total_syms[i]) << 1)); + } + if ((65536 != total) && (used_syms > 1)) + { + TINFL_CR_RETURN_FOREVER(35, TINFL_STATUS_FAILED); + } + for (tree_next = -1, sym_index = 0; sym_index < r->m_table_sizes[r->m_type]; ++sym_index) + { + mz_uint rev_code = 0, l, cur_code, code_size = pCode_size[sym_index]; + if (!code_size) + continue; + cur_code = next_code[code_size]++; + for (l = code_size; l > 0; l--, cur_code >>= 1) + rev_code = (rev_code << 1) | (cur_code & 1); + if (code_size <= TINFL_FAST_LOOKUP_BITS) + { + mz_int16 k = (mz_int16)((code_size << 9) | sym_index); + while (rev_code < TINFL_FAST_LOOKUP_SIZE) + { + pLookUp[rev_code] = k; + rev_code += (1 << code_size); + } + continue; + } + if (0 == (tree_cur = pLookUp[rev_code & (TINFL_FAST_LOOKUP_SIZE - 1)])) + { + pLookUp[rev_code & (TINFL_FAST_LOOKUP_SIZE - 1)] = (mz_int16)tree_next; + tree_cur = tree_next; + tree_next -= 2; + } + rev_code >>= (TINFL_FAST_LOOKUP_BITS - 1); + for (j = code_size; j > (TINFL_FAST_LOOKUP_BITS + 1); j--) + { + tree_cur -= ((rev_code >>= 1) & 1); + if (!pTree[-tree_cur - 1]) + { + pTree[-tree_cur - 1] = (mz_int16)tree_next; + tree_cur = tree_next; + tree_next -= 2; + } + else + tree_cur = pTree[-tree_cur - 1]; + } + tree_cur -= ((rev_code >>= 1) & 1); + pTree[-tree_cur - 1] = (mz_int16)sym_index; + } + if (r->m_type == 2) + { + for (counter = 0; counter < (r->m_table_sizes[0] + r->m_table_sizes[1]);) + { + mz_uint s; + TINFL_HUFF_DECODE(16, dist, r->m_look_up[2], r->m_tree_2); + if (dist < 16) + { + r->m_len_codes[counter++] = (mz_uint8)dist; + continue; + } + if ((dist == 16) && (!counter)) + { + TINFL_CR_RETURN_FOREVER(17, TINFL_STATUS_FAILED); + } + num_extra = "\02\03\07"[dist - 16]; + TINFL_GET_BITS(18, s, num_extra); + s += "\03\03\013"[dist - 16]; + TINFL_MEMSET(r->m_len_codes + counter, (dist == 16) ? r->m_len_codes[counter - 1] : 0, s); + counter += s; + } + if ((r->m_table_sizes[0] + r->m_table_sizes[1]) != counter) + { + TINFL_CR_RETURN_FOREVER(21, TINFL_STATUS_FAILED); + } + TINFL_MEMCPY(r->m_code_size_0, r->m_len_codes, r->m_table_sizes[0]); + TINFL_MEMCPY(r->m_code_size_1, r->m_len_codes + r->m_table_sizes[0], r->m_table_sizes[1]); + } + } + for (;;) + { + mz_uint8 *pSrc; + for (;;) + { + if (((pIn_buf_end - pIn_buf_cur) < 4) || ((pOut_buf_end - pOut_buf_cur) < 2)) + { + TINFL_HUFF_DECODE(23, counter, r->m_look_up[0], r->m_tree_0); + if (counter >= 256) + break; + while (pOut_buf_cur >= pOut_buf_end) + { + TINFL_CR_RETURN(24, TINFL_STATUS_HAS_MORE_OUTPUT); + } + *pOut_buf_cur++ = (mz_uint8)counter; + } + else + { + int sym2; + mz_uint code_len; +#if TINFL_USE_64BIT_BITBUF + if (num_bits < 30) + { + bit_buf |= (((tinfl_bit_buf_t)MZ_READ_LE32(pIn_buf_cur)) << num_bits); + pIn_buf_cur += 4; + num_bits += 32; + } #else -#define CTX_SHAPE_CACHE_PRIME1 283 -#define CTX_SHAPE_CACHE_PRIME2 599 -#define CTX_SHAPE_CACHE_PRIME3 101 -#define CTX_SHAPE_CACHE_PRIME4 661 + if (num_bits < 15) + { + bit_buf |= (((tinfl_bit_buf_t)MZ_READ_LE16(pIn_buf_cur)) << num_bits); + pIn_buf_cur += 2; + num_bits += 16; + } +#endif + if ((sym2 = r->m_look_up[0][bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= 0) + code_len = sym2 >> 9; + else + { + code_len = TINFL_FAST_LOOKUP_BITS; + do + { + sym2 = r->m_tree_0[~sym2 + ((bit_buf >> code_len++) & 1)]; + } while (sym2 < 0); + } + counter = sym2; + bit_buf >>= code_len; + num_bits -= code_len; + if (counter & 256) + break; + +#if !TINFL_USE_64BIT_BITBUF + if (num_bits < 15) + { + bit_buf |= (((tinfl_bit_buf_t)MZ_READ_LE16(pIn_buf_cur)) << num_bits); + pIn_buf_cur += 2; + num_bits += 16; + } #endif + if ((sym2 = r->m_look_up[0][bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= 0) + code_len = sym2 >> 9; + else + { + code_len = TINFL_FAST_LOOKUP_BITS; + do + { + sym2 = r->m_tree_0[~sym2 + ((bit_buf >> code_len++) & 1)]; + } while (sym2 < 0); + } + bit_buf >>= code_len; + num_bits -= code_len; + pOut_buf_cur[0] = (mz_uint8)counter; + if (sym2 & 256) + { + pOut_buf_cur++; + counter = sym2; + break; + } + pOut_buf_cur[1] = (mz_uint8)sym2; + pOut_buf_cur += 2; + } + } + if ((counter &= 511) == 256) + break; -#if CTX_SHAPE_CACHE -float ctx_shape_cache_rate = 0.0; -int _ctx_shape_cache_enabled = CTX_SHAPE_CACHE_DEFAULT; + num_extra = s_length_extra[counter - 257]; + counter = s_length_base[counter - 257]; + if (num_extra) + { + mz_uint extra_bits; + TINFL_GET_BITS(25, extra_bits, num_extra); + counter += extra_bits; + } -//static CtxShapeCache ctx_cache = {{NULL,}, 0}; + TINFL_HUFF_DECODE(26, dist, r->m_look_up[1], r->m_tree_1); + num_extra = s_dist_extra[dist]; + dist = s_dist_base[dist]; + if (num_extra) + { + mz_uint extra_bits; + TINFL_GET_BITS(27, extra_bits, num_extra); + dist += extra_bits; + } -static long ctx_shape_cache_hits = 0; -static long ctx_shape_cache_misses = 0; + dist_from_out_buf_start = pOut_buf_cur - pOut_buf_start; + if ((dist == 0 || dist > dist_from_out_buf_start || dist_from_out_buf_start == 0) && (decomp_flags & TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF)) + { + TINFL_CR_RETURN_FOREVER(37, TINFL_STATUS_FAILED); + } + pSrc = pOut_buf_start + ((dist_from_out_buf_start - dist) & out_buf_size_mask); -/* this returns the buffer to use for rendering, it always - succeeds.. - */ -static inline CtxShapeEntry *ctx_shape_entry_find (CtxRasterizer *rasterizer, uint32_t hash, int width, int height) -{ - /* use both some high and some low bits */ - int entry_no = ( (hash >> 10) ^ (hash & 1023) ) % CTX_SHAPE_CACHE_ENTRIES; - { - static int i = 0; - i++; - if (i>256) - { - if (ctx_shape_cache_hits+ctx_shape_cache_misses) - { - ctx_shape_cache_rate = - 0.5f * ctx_shape_cache_rate + - 0.5f * (ctx_shape_cache_hits * 100.0f / (ctx_shape_cache_hits+ctx_shape_cache_misses)); + if ((MZ_MAX(pOut_buf_cur, pSrc) + counter) > pOut_buf_end) + { + while (counter--) + { + while (pOut_buf_cur >= pOut_buf_end) + { + TINFL_CR_RETURN(53, TINFL_STATUS_HAS_MORE_OUTPUT); + } + *pOut_buf_cur++ = pOut_buf_start[(dist_from_out_buf_start++ - dist) & out_buf_size_mask]; + } + continue; + } +#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES + else if ((counter >= 9) && (counter <= dist)) + { + const mz_uint8 *pSrc_end = pSrc + (counter & ~7); + do + { +#ifdef MINIZ_UNALIGNED_USE_MEMCPY + memcpy(pOut_buf_cur, pSrc, sizeof(mz_uint32)*2); +#else + ((mz_uint32 *)pOut_buf_cur)[0] = ((const mz_uint32 *)pSrc)[0]; + ((mz_uint32 *)pOut_buf_cur)[1] = ((const mz_uint32 *)pSrc)[1]; +#endif + pOut_buf_cur += 8; + } while ((pSrc += 8) < pSrc_end); + if ((counter &= 7) < 3) + { + if (counter) + { + pOut_buf_cur[0] = pSrc[0]; + if (counter > 1) + pOut_buf_cur[1] = pSrc[1]; + pOut_buf_cur += counter; + } + continue; + } + } +#endif + while(counter>2) + { + pOut_buf_cur[0] = pSrc[0]; + pOut_buf_cur[1] = pSrc[1]; + pOut_buf_cur[2] = pSrc[2]; + pOut_buf_cur += 3; + pSrc += 3; + counter -= 3; + } + if (counter > 0) + { + pOut_buf_cur[0] = pSrc[0]; + if (counter > 1) + pOut_buf_cur[1] = pSrc[1]; + pOut_buf_cur += counter; + } + } } - i = 0; - ctx_shape_cache_hits = 0; - ctx_shape_cache_misses = 0; - } - } -// XXX : this 1 one is needed to silence a false positive: -// ==90718== Invalid write of size 1 -// ==90718== at 0x1189EF: ctx_rasterizer_generate_coverage (ctx.h:4786) -// ==90718== by 0x118E57: ctx_rasterizer_rasterize_edges (ctx.h:4907) -// - int size = sizeof (CtxShapeEntry) + width * height + 1; + } while (!(r->m_final & 1)); - CtxShapeEntry *entry = rasterizer->shape_cache.entries[entry_no]; - if (entry) + /* Ensure byte alignment and put back any bytes from the bitbuf if we've looked ahead too far on gzip, or other Deflate streams followed by arbitrary data. */ + /* I'm being super conservative here. A number of simplifications can be made to the byte alignment part, and the Adler32 check shouldn't ever need to worry about reading from the bitbuf now. */ + TINFL_SKIP_BITS(32, num_bits & 7); + while ((pIn_buf_cur > pIn_buf_next) && (num_bits >= 8)) { - int old_size = sizeof (CtxShapeEntry) + entry->width + entry->height + 1; - if (entry->hash == hash && - entry->width == width && - entry->height == height) + --pIn_buf_cur; + num_bits -= 8; + } + bit_buf &= ~(~(tinfl_bit_buf_t)0 << num_bits); + MZ_ASSERT(!num_bits); /* if this assert fires then we've read beyond the end of non-deflate/zlib streams with following data (such as gzip streams). */ + + if (decomp_flags & TINFL_FLAG_PARSE_ZLIB_HEADER) + { + for (counter = 0; counter < 4; ++counter) { - if (entry->uses < 1<<30) - { entry->uses++; } - ctx_shape_cache_hits ++; - return entry; + mz_uint s; + if (num_bits) + TINFL_GET_BITS(41, s, 8); + else + TINFL_GET_BYTE(42, s); + r->m_z_adler32 = (r->m_z_adler32 << 8) | s; } - - if (old_size >= size) - { - rasterizer->shape_cache.size -= old_size; - rasterizer->shape_cache.size += (old_size-size); // slack/leaked - } - else - { - rasterizer->shape_cache.entries[entry_no] = NULL; - rasterizer->shape_cache.size -= entry->width * entry->height; - rasterizer->shape_cache.size -= sizeof (CtxShapeEntry); - ctx_free (entry); - entry = NULL; - } } + TINFL_CR_RETURN_FOREVER(34, TINFL_STATUS_DONE); - if (!entry) - entry = rasterizer->shape_cache.entries[entry_no] = (CtxShapeEntry *) ctx_calloc (size, 1); - - rasterizer->shape_cache.size += size; + TINFL_CR_FINISH - ctx_shape_cache_misses ++; - entry->hash = hash; - entry->width = width; - entry->height = height; - entry->uses = 0; - return entry; +common_exit: + /* As long as we aren't telling the caller that we NEED more input to make forward progress: */ + /* Put back any bytes from the bitbuf in case we've looked ahead too far on gzip, or other Deflate streams followed by arbitrary data. */ + /* We need to be very careful here to NOT push back any bytes we definitely know we need to make forward progress, though, or we'll lock the caller up into an inf loop. */ + if ((status != TINFL_STATUS_NEEDS_MORE_INPUT) && (status != TINFL_STATUS_FAILED_CANNOT_MAKE_PROGRESS)) + { + while ((pIn_buf_cur > pIn_buf_next) && (num_bits >= 8)) + { + --pIn_buf_cur; + num_bits -= 8; + } + } + r->m_num_bits = num_bits; + r->m_bit_buf = bit_buf & ~(~(tinfl_bit_buf_t)0 << num_bits); + r->m_dist = dist; + r->m_counter = counter; + r->m_num_extra = num_extra; + r->m_dist_from_out_buf_start = dist_from_out_buf_start; + *pIn_buf_size = pIn_buf_cur - pIn_buf_next; + *pOut_buf_size = pOut_buf_cur - pOut_buf_next; + if ((decomp_flags & (TINFL_FLAG_PARSE_ZLIB_HEADER | TINFL_FLAG_COMPUTE_ADLER32)) && (status >= 0)) + { + const mz_uint8 *ptr = pOut_buf_next; + size_t buf_len = *pOut_buf_size; + mz_uint32 i, s1 = r->m_check_adler32 & 0xffff, s2 = r->m_check_adler32 >> 16; + size_t block_len = buf_len % 5552; + while (buf_len) + { + for (i = 0; i + 7 < block_len; i += 8, ptr += 8) + { + s1 += ptr[0], s2 += s1; + s1 += ptr[1], s2 += s1; + s1 += ptr[2], s2 += s1; + s1 += ptr[3], s2 += s1; + s1 += ptr[4], s2 += s1; + s1 += ptr[5], s2 += s1; + s1 += ptr[6], s2 += s1; + s1 += ptr[7], s2 += s1; + } + for (; i < block_len; ++i) + s1 += *ptr++, s2 += s1; + s1 %= 65521U, s2 %= 65521U; + buf_len -= block_len; + block_len = 5552; + } + r->m_check_adler32 = (s2 << 16) + s1; + if ((status == TINFL_STATUS_DONE) && (decomp_flags & TINFL_FLAG_PARSE_ZLIB_HEADER) && (r->m_check_adler32 != r->m_z_adler32)) + status = TINFL_STATUS_ADLER32_MISMATCH; + } + return status; } -#endif - -static uint32_t ctx_rasterizer_poly_to_edges (CtxRasterizer *rasterizer) +/* Higher level helper functions. */ +void *tinfl_decompress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags) { -#if CTX_SHAPE_CACHE - int x = 0; - int y = 0; -#endif - unsigned int count = rasterizer->edge_list.count; - if (CTX_UNLIKELY (count == 0)) - return 0; - CtxSegment *entry = (CtxSegment*)&rasterizer->edge_list.entries[0]; -#if CTX_SHAPE_CACHE -#if 1 - int ox = entry->data.s16[2]; - int oy = entry->data.s16[3]; -#endif - uint32_t hash = rasterizer->edge_list.count; - hash = (ox & CTX_SUBDIV); - hash *= CTX_SHAPE_CACHE_PRIME1; - hash += (oy & CTX_SUBDIV); -#endif - //CtxSegment *entry = &(((CtxSegment*)(rasterizer->edge_list.entries)))[0]; - for (unsigned int i = 0; i < count; i++) + tinfl_decompressor decomp; + void *pBuf = NULL, *pNew_buf; + size_t src_buf_ofs = 0, out_buf_capacity = 0; + *pOut_len = 0; + tinfl_init(&decomp); + for (;;) { -#if CTX_SHAPE_CACHE - x = entry->data.s16[2]; - y = entry->data.s16[3]; - int dx = x-ox; - int dy = y-oy; - ox = x; - oy = y; - hash *= CTX_SHAPE_CACHE_PRIME3; - hash += dx; - hash *= CTX_SHAPE_CACHE_PRIME4; - hash += dy; -#endif -#if 1 - if (entry->data.s16[3] < entry->data.s16[1]) + size_t src_buf_size = src_buf_len - src_buf_ofs, dst_buf_size = out_buf_capacity - *pOut_len, new_out_buf_capacity; + tinfl_status status = tinfl_decompress(&decomp, (const mz_uint8 *)pSrc_buf + src_buf_ofs, &src_buf_size, (mz_uint8 *)pBuf, pBuf ? (mz_uint8 *)pBuf + *pOut_len : NULL, &dst_buf_size, + (flags & ~TINFL_FLAG_HAS_MORE_INPUT) | TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF); + if ((status < 0) || (status == TINFL_STATUS_NEEDS_MORE_INPUT)) { - *entry = ctx_segment_s16 (CTX_EDGE_FLIPPED, - entry->data.s16[2], entry->data.s16[3], - entry->data.s16[0], entry->data.s16[1]); + MZ_FREE(pBuf); + *pOut_len = 0; + return NULL; } -#endif - entry++; + src_buf_ofs += src_buf_size; + *pOut_len += dst_buf_size; + if (status == TINFL_STATUS_DONE) + break; + new_out_buf_capacity = out_buf_capacity * 2; + if (new_out_buf_capacity < 128) + new_out_buf_capacity = 128; + pNew_buf = MZ_REALLOC(pBuf, new_out_buf_capacity); + if (!pNew_buf) + { + MZ_FREE(pBuf); + *pOut_len = 0; + return NULL; + } + pBuf = pNew_buf; + out_buf_capacity = new_out_buf_capacity; } -#if CTX_SHAPE_CACHE - return hash; -#else - return 0; -#endif + return pBuf; } -static inline void ctx_rasterizer_finish_shape (CtxRasterizer *rasterizer) +size_t tinfl_decompress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags) { - if (rasterizer->has_shape && rasterizer->has_prev) + tinfl_decompressor decomp; + tinfl_status status; + tinfl_init(&decomp); + status = tinfl_decompress(&decomp, (const mz_uint8 *)pSrc_buf, &src_buf_len, (mz_uint8 *)pOut_buf, (mz_uint8 *)pOut_buf, &out_buf_len, (flags & ~TINFL_FLAG_HAS_MORE_INPUT) | TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF); + return (status != TINFL_STATUS_DONE) ? TINFL_DECOMPRESS_MEM_TO_MEM_FAILED : out_buf_len; +} + +int tinfl_decompress_mem_to_callback(const void *pIn_buf, size_t *pIn_buf_size, tinfl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags) +{ + int result = 0; + tinfl_decompressor decomp; + mz_uint8 *pDict = (mz_uint8 *)MZ_MALLOC(TINFL_LZ_DICT_SIZE); + size_t in_buf_ofs = 0, dict_ofs = 0; + if (!pDict) + return TINFL_STATUS_FAILED; + memset(pDict,0,TINFL_LZ_DICT_SIZE); + tinfl_init(&decomp); + for (;;) { - ctx_rasterizer_line_to (rasterizer, rasterizer->first_x, rasterizer->first_y); - rasterizer->has_prev = 0; + size_t in_buf_size = *pIn_buf_size - in_buf_ofs, dst_buf_size = TINFL_LZ_DICT_SIZE - dict_ofs; + tinfl_status status = tinfl_decompress(&decomp, (const mz_uint8 *)pIn_buf + in_buf_ofs, &in_buf_size, pDict, pDict + dict_ofs, &dst_buf_size, + (flags & ~(TINFL_FLAG_HAS_MORE_INPUT | TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF))); + in_buf_ofs += in_buf_size; + if ((dst_buf_size) && (!(*pPut_buf_func)(pDict + dict_ofs, (int)dst_buf_size, pPut_buf_user))) + break; + if (status != TINFL_STATUS_HAS_MORE_OUTPUT) + { + result = (status == TINFL_STATUS_DONE); + break; + } + dict_ofs = (dict_ofs + dst_buf_size) & (TINFL_LZ_DICT_SIZE - 1); } + MZ_FREE(pDict); + *pIn_buf_size = in_buf_ofs; + return result; } -//#define MIN_Y -100 -//#define MAX_Y 3800 -//#define MIN_X -100 -//#define MAX_X 3600*10 - -static inline void ctx_rasterizer_move_to (CtxRasterizer *rasterizer, float x, float y) +#ifndef MINIZ_NO_MALLOC +tinfl_decompressor *tinfl_decompressor_alloc(void) { - int tx = 0, ty = 0; + tinfl_decompressor *pDecomp = (tinfl_decompressor *)MZ_MALLOC(sizeof(tinfl_decompressor)); + if (pDecomp) + tinfl_init(pDecomp); + return pDecomp; +} - rasterizer->x = x; - rasterizer->y = y; - rasterizer->first_x = x; - rasterizer->first_y = y; - rasterizer->has_prev = -1; - _ctx_user_to_device_prepped (rasterizer->state, x,y, &tx, &ty); +void tinfl_decompressor_free(tinfl_decompressor *pDecomp) +{ + MZ_FREE(pDecomp); +} +#endif - tx -= rasterizer->blit_x * CTX_SUBDIV; - ctx_rasterizer_update_inner_point (rasterizer, tx, ty); +#ifdef __cplusplus } +#endif -static 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; +#endif /*#ifndef MINIZ_NO_INFLATE_APIS*/ + /************************************************************************** + * + * Copyright 2013-2014 RAD Game Tools and Valve Software + * Copyright 2010-2014 Rich Geldreich and Tenacious Software LLC + * Copyright 2016 Martin Raiber + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + **************************************************************************/ - 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; - } -} +#ifndef MINIZ_NO_ARCHIVE_APIS -static 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; +#ifdef __cplusplus +extern "C" { +#endif - _ctx_user_to_device_prepped (rasterizer->state, x, y, &tx, &ty); - tx -= rasterizer->blit_x * CTX_SUBDIV; +/* ------------------- .ZIP archive reading */ - ctx_rasterizer_add_point (rasterizer, tx, ty); +#ifdef MINIZ_NO_STDIO +#define MZ_FILE void * +#else +#include <sys/stat.h> - 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; - } -} +#if defined(_MSC_VER) || defined(__MINGW64__) -CTX_INLINE static float -ctx_bezier_sample_1d (float x0, float x1, float x2, float x3, float dt) +#define WIN32_LEAN_AND_MEAN +#include <windows.h> + +static WCHAR* mz_utf8z_to_widechar(const char* str) { - return ctx_lerpf ( - ctx_lerpf (ctx_lerpf (x0, x1, dt), - ctx_lerpf (x1, x2, dt), dt), - ctx_lerpf (ctx_lerpf (x1, x2, dt), - ctx_lerpf (x2, x3, dt), dt), dt); + int reqChars = MultiByteToWideChar(CP_UTF8, 0, str, -1, NULL, 0); + WCHAR* wStr = (WCHAR*)malloc(reqChars * sizeof(WCHAR)); + MultiByteToWideChar(CP_UTF8, 0, str, -1, wStr, sizeof(WCHAR) * reqChars); + return wStr; } -CTX_INLINE static void -ctx_bezier_sample (float x0, float y0, - float x1, float y1, - float x2, float y2, - float x3, float y3, - float dt, float *x, float *y) +static FILE *mz_fopen(const char *pFilename, const char *pMode) { - *x = ctx_bezier_sample_1d (x0, x1, x2, x3, dt); - *y = ctx_bezier_sample_1d (y0, y1, y2, y3, dt); + WCHAR* wFilename = mz_utf8z_to_widechar(pFilename); + WCHAR* wMode = mz_utf8z_to_widechar(pMode); + FILE* pFile = NULL; + errno_t err = _wfopen_s(&pFile, wFilename, wMode); + free(wFilename); + free(wMode); + return err ? NULL : pFile; } -static inline void -ctx_rasterizer_bezier_divide (CtxRasterizer *rasterizer, - float ox, float oy, - float x0, float y0, - float x1, float y1, - float x2, float y2, - float sx, float sy, - float ex, float ey, - float s, - float e, - int iteration, - float tolerance) +static FILE *mz_freopen(const char *pPath, const char *pMode, FILE *pStream) { - float t = (s + e) * 0.5f; - float x, y; - float lx, ly, 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) - { - ctx_rasterizer_bezier_divide (rasterizer, ox, oy, x0, y0, x1, y1, x2, y2, - sx, sy, x, y, s, t, iteration + 1, - tolerance); - ctx_rasterizer_line_to (rasterizer, x, y); - ctx_rasterizer_bezier_divide (rasterizer, ox, oy, x0, y0, x1, y1, x2, y2, - x, y, ex, ey, t, e, iteration + 1, - tolerance); - } + WCHAR* wPath = mz_utf8z_to_widechar(pPath); + WCHAR* wMode = mz_utf8z_to_widechar(pMode); + FILE* pFile = NULL; + errno_t err = _wfreopen_s(&pFile, wPath, wMode, pStream); + free(wPath); + free(wMode); + return err ? NULL : pFile; } -#define CTX_FIX_SCALE 1024 -#define CTX_FIX_SHIFT 10 - - -CTX_INLINE static int -ctx_lerp_fixed (int v0, int v1, int dx) +static int mz_stat64(const char *path, struct __stat64 *buffer) { - return v0 + (((v1-v0) * dx) >> CTX_FIX_SHIFT); + WCHAR* wPath = mz_utf8z_to_widechar(path); + int res = _wstat64(wPath, buffer); + free(wPath); + return res; } +#ifndef MINIZ_NO_TIME +#include <sys/utime.h> +#endif +#define MZ_FOPEN mz_fopen +#define MZ_FCLOSE fclose +#define MZ_FREAD fread +#define MZ_FWRITE fwrite +#define MZ_FTELL64 _ftelli64 +#define MZ_FSEEK64 _fseeki64 +#define MZ_FILE_STAT_STRUCT _stat64 +#define MZ_FILE_STAT mz_stat64 +#define MZ_FFLUSH fflush +#define MZ_FREOPEN mz_freopen +#define MZ_DELETE_FILE remove + +#elif defined(__MINGW32__) || defined(__WATCOMC__) +#ifndef MINIZ_NO_TIME +#include <sys/utime.h> +#endif +#define MZ_FOPEN(f, m) fopen(f, m) +#define MZ_FCLOSE fclose +#define MZ_FREAD fread +#define MZ_FWRITE fwrite +#define MZ_FTELL64 _ftelli64 +#define MZ_FSEEK64 _fseeki64 +#define MZ_FILE_STAT_STRUCT stat +#define MZ_FILE_STAT stat +#define MZ_FFLUSH fflush +#define MZ_FREOPEN(f, m, s) freopen(f, m, s) +#define MZ_DELETE_FILE remove + +#elif defined(__TINYC__) +#ifndef MINIZ_NO_TIME +#include <sys/utime.h> +#endif +#define MZ_FOPEN(f, m) fopen(f, m) +#define MZ_FCLOSE fclose +#define MZ_FREAD fread +#define MZ_FWRITE fwrite +#define MZ_FTELL64 ftell +#define MZ_FSEEK64 fseek +#define MZ_FILE_STAT_STRUCT stat +#define MZ_FILE_STAT stat +#define MZ_FFLUSH fflush +#define MZ_FREOPEN(f, m, s) freopen(f, m, s) +#define MZ_DELETE_FILE remove + +#elif defined(__USE_LARGEFILE64) /* gcc, clang */ +#ifndef MINIZ_NO_TIME +#include <utime.h> +#endif +#define MZ_FOPEN(f, m) fopen64(f, m) +#define MZ_FCLOSE fclose +#define MZ_FREAD fread +#define MZ_FWRITE fwrite +#define MZ_FTELL64 ftello64 +#define MZ_FSEEK64 fseeko64 +#define MZ_FILE_STAT_STRUCT stat64 +#define MZ_FILE_STAT stat64 +#define MZ_FFLUSH fflush +#define MZ_FREOPEN(p, m, s) freopen64(p, m, s) +#define MZ_DELETE_FILE remove + +#elif defined(__APPLE__) || defined(__FreeBSD__) +#ifndef MINIZ_NO_TIME +#include <utime.h> +#endif +#define MZ_FOPEN(f, m) fopen(f, m) +#define MZ_FCLOSE fclose +#define MZ_FREAD fread +#define MZ_FWRITE fwrite +#define MZ_FTELL64 ftello +#define MZ_FSEEK64 fseeko +#define MZ_FILE_STAT_STRUCT stat +#define MZ_FILE_STAT stat +#define MZ_FFLUSH fflush +#define MZ_FREOPEN(p, m, s) freopen(p, m, s) +#define MZ_DELETE_FILE remove + +#else +//#pragma message("Using fopen, ftello, fseeko, stat() etc. path for file I/O - this path may not support large files.") +#ifndef MINIZ_NO_TIME +#include <utime.h> +#endif +#define MZ_FOPEN(f, m) fopen(f, m) +#define MZ_FCLOSE fclose +#define MZ_FREAD fread +#define MZ_FWRITE fwrite +#ifdef __STRICT_ANSI__ +#define MZ_FTELL64 ftell +#define MZ_FSEEK64 fseek +#else +#define MZ_FTELL64 ftello +#define MZ_FSEEK64 fseeko +#endif +#define MZ_FILE_STAT_STRUCT stat +#define MZ_FILE_STAT stat +#define MZ_FFLUSH fflush +#define MZ_FREOPEN(f, m, s) freopen(f, m, s) +#define MZ_DELETE_FILE remove +#endif /* #ifdef _MSC_VER */ +#endif /* #ifdef MINIZ_NO_STDIO */ +#define MZ_TOLOWER(c) ((((c) >= 'A') && ((c) <= 'Z')) ? ((c) - 'A' + 'a') : (c)) -CTX_INLINE static int -ctx_bezier_sample_1d_fixed (int x0, int x1, int x2, int x3, int dt) +/* Various ZIP archive enums. To completely avoid cross platform compiler alignment and platform endian issues, miniz.c doesn't use structs for any of this stuff. */ +enum { - return ctx_lerp_fixed ( - ctx_lerp_fixed (ctx_lerp_fixed (x0, x1, dt), - ctx_lerp_fixed (x1, x2, dt), dt), - ctx_lerp_fixed (ctx_lerp_fixed (x1, x2, dt), - ctx_lerp_fixed (x2, x3, dt), dt), dt); -} + /* ZIP archive identifiers and record sizes */ + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG = 0x06054b50, + MZ_ZIP_CENTRAL_DIR_HEADER_SIG = 0x02014b50, + MZ_ZIP_LOCAL_DIR_HEADER_SIG = 0x04034b50, + MZ_ZIP_LOCAL_DIR_HEADER_SIZE = 30, + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE = 46, + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE = 22, + + /* ZIP64 archive identifier and record sizes */ + MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIG = 0x06064b50, + MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIG = 0x07064b50, + MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE = 56, + MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE = 20, + MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID = 0x0001, + MZ_ZIP_DATA_DESCRIPTOR_ID = 0x08074b50, + MZ_ZIP_DATA_DESCRIPTER_SIZE64 = 24, + MZ_ZIP_DATA_DESCRIPTER_SIZE32 = 16, + + /* Central directory header record offsets */ + MZ_ZIP_CDH_SIG_OFS = 0, + MZ_ZIP_CDH_VERSION_MADE_BY_OFS = 4, + MZ_ZIP_CDH_VERSION_NEEDED_OFS = 6, + MZ_ZIP_CDH_BIT_FLAG_OFS = 8, + MZ_ZIP_CDH_METHOD_OFS = 10, + MZ_ZIP_CDH_FILE_TIME_OFS = 12, + MZ_ZIP_CDH_FILE_DATE_OFS = 14, + MZ_ZIP_CDH_CRC32_OFS = 16, + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS = 20, + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS = 24, + MZ_ZIP_CDH_FILENAME_LEN_OFS = 28, + MZ_ZIP_CDH_EXTRA_LEN_OFS = 30, + MZ_ZIP_CDH_COMMENT_LEN_OFS = 32, + MZ_ZIP_CDH_DISK_START_OFS = 34, + MZ_ZIP_CDH_INTERNAL_ATTR_OFS = 36, + MZ_ZIP_CDH_EXTERNAL_ATTR_OFS = 38, + MZ_ZIP_CDH_LOCAL_HEADER_OFS = 42, + + /* Local directory header offsets */ + MZ_ZIP_LDH_SIG_OFS = 0, + MZ_ZIP_LDH_VERSION_NEEDED_OFS = 4, + MZ_ZIP_LDH_BIT_FLAG_OFS = 6, + MZ_ZIP_LDH_METHOD_OFS = 8, + MZ_ZIP_LDH_FILE_TIME_OFS = 10, + MZ_ZIP_LDH_FILE_DATE_OFS = 12, + MZ_ZIP_LDH_CRC32_OFS = 14, + MZ_ZIP_LDH_COMPRESSED_SIZE_OFS = 18, + MZ_ZIP_LDH_DECOMPRESSED_SIZE_OFS = 22, + MZ_ZIP_LDH_FILENAME_LEN_OFS = 26, + MZ_ZIP_LDH_EXTRA_LEN_OFS = 28, + MZ_ZIP_LDH_BIT_FLAG_HAS_LOCATOR = 1 << 3, + + /* End of central directory offsets */ + MZ_ZIP_ECDH_SIG_OFS = 0, + MZ_ZIP_ECDH_NUM_THIS_DISK_OFS = 4, + MZ_ZIP_ECDH_NUM_DISK_CDIR_OFS = 6, + MZ_ZIP_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS = 8, + MZ_ZIP_ECDH_CDIR_TOTAL_ENTRIES_OFS = 10, + MZ_ZIP_ECDH_CDIR_SIZE_OFS = 12, + MZ_ZIP_ECDH_CDIR_OFS_OFS = 16, + MZ_ZIP_ECDH_COMMENT_SIZE_OFS = 20, + + /* ZIP64 End of central directory locator offsets */ + MZ_ZIP64_ECDL_SIG_OFS = 0, /* 4 bytes */ + MZ_ZIP64_ECDL_NUM_DISK_CDIR_OFS = 4, /* 4 bytes */ + MZ_ZIP64_ECDL_REL_OFS_TO_ZIP64_ECDR_OFS = 8, /* 8 bytes */ + MZ_ZIP64_ECDL_TOTAL_NUMBER_OF_DISKS_OFS = 16, /* 4 bytes */ + + /* ZIP64 End of central directory header offsets */ + MZ_ZIP64_ECDH_SIG_OFS = 0, /* 4 bytes */ + MZ_ZIP64_ECDH_SIZE_OF_RECORD_OFS = 4, /* 8 bytes */ + MZ_ZIP64_ECDH_VERSION_MADE_BY_OFS = 12, /* 2 bytes */ + MZ_ZIP64_ECDH_VERSION_NEEDED_OFS = 14, /* 2 bytes */ + MZ_ZIP64_ECDH_NUM_THIS_DISK_OFS = 16, /* 4 bytes */ + MZ_ZIP64_ECDH_NUM_DISK_CDIR_OFS = 20, /* 4 bytes */ + MZ_ZIP64_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS = 24, /* 8 bytes */ + MZ_ZIP64_ECDH_CDIR_TOTAL_ENTRIES_OFS = 32, /* 8 bytes */ + MZ_ZIP64_ECDH_CDIR_SIZE_OFS = 40, /* 8 bytes */ + MZ_ZIP64_ECDH_CDIR_OFS_OFS = 48, /* 8 bytes */ + MZ_ZIP_VERSION_MADE_BY_DOS_FILESYSTEM_ID = 0, + MZ_ZIP_DOS_DIR_ATTRIBUTE_BITFLAG = 0x10, + MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_IS_ENCRYPTED = 1, + MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_COMPRESSED_PATCH_FLAG = 32, + MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_USES_STRONG_ENCRYPTION = 64, + MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_LOCAL_DIR_IS_MASKED = 8192, + MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_UTF8 = 1 << 11 +}; -CTX_INLINE static void -ctx_bezier_sample_fixed (int x0, int y0, - int x1, int y1, - int x2, int y2, - int x3, int y3, - int dt, int *x, int *y) +typedef struct { - *x = ctx_bezier_sample_1d_fixed (x0, x1, x2, x3, dt); - *y = ctx_bezier_sample_1d_fixed (y0, y1, y2, y3, dt); -} + void *m_p; + size_t m_size, m_capacity; + mz_uint m_element_size; +} mz_zip_array; -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, - int sx, int sy, - int ex, int ey, - int s, - int e, - int iteration, - int tolerance) +struct mz_zip_internal_state_tag { - 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); - 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) - { - ctx_rasterizer_bezier_divide_fixed (rasterizer, ox, oy, x0, y0, x1, y1, x2, y2, - 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 + 1, - tolerance); - } -} + mz_zip_array m_central_dir; + mz_zip_array m_central_dir_offsets; + mz_zip_array m_sorted_central_dir_offsets; -static void -ctx_rasterizer_curve_to (CtxRasterizer *rasterizer, - float x0, float y0, - float x1, float y1, - float x2, float y2) + /* The flags passed in when the archive is initially opened. */ + mz_uint32 m_init_flags; + + /* MZ_TRUE if the archive has a zip64 end of central directory headers, etc. */ + mz_bool m_zip64; + + /* MZ_TRUE if we found zip64 extended info in the central directory (m_zip64 will also be slammed to true too, even if we didn't find a zip64 end of central dir header, etc.) */ + mz_bool m_zip64_has_extended_info_fields; + + /* These fields are used by the file, FILE, memory, and memory/heap read/write helpers. */ + MZ_FILE *m_pFile; + mz_uint64 m_file_archive_start_ofs; + + void *m_pMem; + size_t m_mem_size; + size_t m_mem_capacity; +}; + +#define MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(array_ptr, element_size) (array_ptr)->m_element_size = element_size + +#if defined(DEBUG) || defined(_DEBUG) +static MZ_FORCEINLINE mz_uint mz_zip_array_range_check(const mz_zip_array *pArray, mz_uint index) { -#if CTX_32BIT_SEGMENTS - float tolerance = 0.125f/ctx_matrix_get_scale (&rasterizer->state->gstate.transform); + MZ_ASSERT(index < pArray->m_size); + return index; +} +#define MZ_ZIP_ARRAY_ELEMENT(array_ptr, element_type, index) ((element_type *)((array_ptr)->m_p))[mz_zip_array_range_check(array_ptr, index)] #else - float tolerance = 0.5f/ctx_matrix_get_scale (&rasterizer->state->gstate.transform); +#define MZ_ZIP_ARRAY_ELEMENT(array_ptr, element_type, index) ((element_type *)((array_ptr)->m_p))[index] #endif - float ox = rasterizer->state->x; - float oy = rasterizer->state->y; - tolerance = tolerance * tolerance; - - if(1){ +static MZ_FORCEINLINE void mz_zip_array_init(mz_zip_array *pArray, mz_uint32 element_size) +{ + memset(pArray, 0, sizeof(mz_zip_array)); + pArray->m_element_size = element_size; +} -#if CTX_AVOID_CLIPPED_SUBDIVISION - // XXX need sporting to fixed point - float maxx = ctx_maxf (x1,x2); - maxx = ctx_maxf (maxx, ox); - maxx = ctx_maxf (maxx, x0); - float maxy = ctx_maxf (y1,y2); - maxy = ctx_maxf (maxy, oy); - maxy = ctx_maxf (maxy, y0); - float minx = ctx_minf (x1,x2); - minx = ctx_minf (minx, ox); - minx = ctx_minf (minx, x0); - float miny = ctx_minf (y1,y2); - miny = ctx_minf (miny, oy); - miny = ctx_minf (miny, y0); - - float coords[4][2]={{minx,miny}, - {maxx,miny}, - {maxx,maxy}, - {minx,maxy}}; - for (int i = 0; i < 4; i++) - { - _ctx_user_to_device (rasterizer->state, &coords[i][0], &coords[i][1]); - } - minx = maxx = coords[0][0]; - miny = maxy = coords[0][1]; - for (int i = 1; i < 4; i++) - { - minx = ctx_minf (minx, coords[i][0]); - miny = ctx_minf (miny, coords[i][1]); - maxx = ctx_maxf (minx, coords[i][0]); - maxy = ctx_maxf (miny, coords[i][1]); - } +static MZ_FORCEINLINE void mz_zip_array_clear(mz_zip_archive *pZip, mz_zip_array *pArray) +{ + pZip->m_pFree(pZip->m_pAlloc_opaque, pArray->m_p); + memset(pArray, 0, sizeof(mz_zip_array)); +} - if( (maxx-minx) + (maxy-miny) < 0.66f || - (minx > rasterizer->blit_x + rasterizer->blit_width) || - (miny > rasterizer->blit_y + rasterizer->blit_height) || - (maxx < rasterizer->blit_x) || - (maxy < rasterizer->blit_y) ) +static mz_bool mz_zip_array_ensure_capacity(mz_zip_archive *pZip, mz_zip_array *pArray, size_t min_new_capacity, mz_uint growing) +{ + void *pNew_p; + size_t new_capacity = min_new_capacity; + MZ_ASSERT(pArray->m_element_size); + if (pArray->m_capacity >= min_new_capacity) + return MZ_TRUE; + if (growing) { + new_capacity = MZ_MAX(1, pArray->m_capacity); + while (new_capacity < min_new_capacity) + new_capacity *= 2; } - else -#endif + if (NULL == (pNew_p = pZip->m_pRealloc(pZip->m_pAlloc_opaque, pArray->m_p, pArray->m_element_size, new_capacity))) + return MZ_FALSE; + pArray->m_p = pNew_p; + pArray->m_capacity = new_capacity; + return MZ_TRUE; +} + +static MZ_FORCEINLINE mz_bool mz_zip_array_reserve(mz_zip_archive *pZip, mz_zip_array *pArray, size_t new_capacity, mz_uint growing) +{ + if (new_capacity > pArray->m_capacity) { -#if 1 - ctx_rasterizer_bezier_divide_fixed (rasterizer, - (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)(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)); -#else - ctx_rasterizer_bezier_divide (rasterizer, - ox, oy, x0, y0, - x1, y1, x2, y2, - ox, oy, x2, y2, - 0.0f, 1.0f, 0, tolerance); -#endif + if (!mz_zip_array_ensure_capacity(pZip, pArray, new_capacity, growing)) + return MZ_FALSE; } - } - ctx_rasterizer_line_to (rasterizer, x2, y2); + return MZ_TRUE; } -static void -ctx_rasterizer_rel_move_to (CtxRasterizer *rasterizer, float x, float y) +static MZ_FORCEINLINE mz_bool mz_zip_array_resize(mz_zip_archive *pZip, mz_zip_array *pArray, size_t new_size, mz_uint growing) { - //if (CTX_UNLIKELY(x == 0.f && y == 0.f)) - //{ return; } - x += rasterizer->x; - y += rasterizer->y; - ctx_rasterizer_move_to (rasterizer, x, y); + if (new_size > pArray->m_capacity) + { + if (!mz_zip_array_ensure_capacity(pZip, pArray, new_size, growing)) + return MZ_FALSE; + } + pArray->m_size = new_size; + return MZ_TRUE; } -static void -ctx_rasterizer_rel_line_to (CtxRasterizer *rasterizer, float x, float y) +static MZ_FORCEINLINE mz_bool mz_zip_array_ensure_room(mz_zip_archive *pZip, mz_zip_array *pArray, size_t n) { - //if (CTX_UNLIKELY(x== 0.f && y==0.f)) - // { return; } - x += rasterizer->x; - y += rasterizer->y; - ctx_rasterizer_line_to (rasterizer, x, y); + return mz_zip_array_reserve(pZip, pArray, pArray->m_size + n, MZ_TRUE); } -static void -ctx_rasterizer_rel_curve_to (CtxRasterizer *rasterizer, - float x0, float y0, float x1, float y1, float x2, float y2) +static MZ_FORCEINLINE mz_bool mz_zip_array_push_back(mz_zip_archive *pZip, mz_zip_array *pArray, const void *pElements, size_t n) { - x0 += rasterizer->x; - y0 += rasterizer->y; - x1 += rasterizer->x; - y1 += rasterizer->y; - x2 += rasterizer->x; - y2 += rasterizer->y; - ctx_rasterizer_curve_to (rasterizer, x0, y0, x1, y1, x2, y2); + size_t orig_size = pArray->m_size; + if (!mz_zip_array_resize(pZip, pArray, orig_size + n, MZ_TRUE)) + return MZ_FALSE; + if (n > 0) + memcpy((mz_uint8 *)pArray->m_p + orig_size * pArray->m_element_size, pElements, n * pArray->m_element_size); + return MZ_TRUE; } - -static int -ctx_rasterizer_find_texture (CtxRasterizer *rasterizer, - const char *eid) +#ifndef MINIZ_NO_TIME +static MZ_TIME_T mz_zip_dos_to_time_t(int dos_time, int dos_date) { - int no; - for (no = 0; no < CTX_MAX_TEXTURES; no++) - { - if (rasterizer->texture_source->texture[no].data && - rasterizer->texture_source->texture[no].eid && - !strcmp (rasterizer->texture_source->texture[no].eid, eid)) - return no; - } - return -1; + struct tm tm; + memset(&tm, 0, sizeof(tm)); + tm.tm_isdst = -1; + tm.tm_year = ((dos_date >> 9) & 127) + 1980 - 1900; + tm.tm_mon = ((dos_date >> 5) & 15) - 1; + tm.tm_mday = dos_date & 31; + tm.tm_hour = (dos_time >> 11) & 31; + tm.tm_min = (dos_time >> 5) & 63; + tm.tm_sec = (dos_time << 1) & 62; + return mktime(&tm); } -static void -ctx_rasterizer_set_texture (CtxRasterizer *rasterizer, - const char *eid, - float x, - float y) +#ifndef MINIZ_NO_ARCHIVE_WRITING_APIS +static void mz_zip_time_t_to_dos_time(MZ_TIME_T time, mz_uint16 *pDOS_time, mz_uint16 *pDOS_date) { - int is_stroke = (rasterizer->state->source != 0); - CtxSource *source = is_stroke && (rasterizer->state->gstate.source_stroke.type != CTX_SOURCE_INHERIT_FILL)? - &rasterizer->state->gstate.source_stroke: - &rasterizer->state->gstate.source_fill; - rasterizer->state->source = 0; - - int no = ctx_rasterizer_find_texture (rasterizer, eid); - if (no < 0 || no >= CTX_MAX_TEXTURES) { no = 0; } - if (rasterizer->texture_source->texture[no].data == NULL) +#ifdef _MSC_VER + struct tm tm_struct; + struct tm *tm = &tm_struct; + errno_t err = localtime_s(tm, &time); + if (err) { -#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; + *pDOS_date = 0; + *pDOS_time = 0; + return; } - else - { - rasterizer->texture_source->texture[no].frame = rasterizer->texture_source->frame; - } - source->type = CTX_SOURCE_TEXTURE; - source->texture.buffer = &rasterizer->texture_source->texture[no]; - ctx_matrix_identity (&source->set_transform); - ctx_matrix_translate (&source->set_transform, x, y); +#else + struct tm *tm = localtime(&time); +#endif /* #ifdef _MSC_VER */ + + *pDOS_time = (mz_uint16)(((tm->tm_hour) << 11) + ((tm->tm_min) << 5) + ((tm->tm_sec) >> 1)); + *pDOS_date = (mz_uint16)(((tm->tm_year + 1900 - 1980) << 9) + ((tm->tm_mon + 1) << 5) + tm->tm_mday); } +#endif /* MINIZ_NO_ARCHIVE_WRITING_APIS */ +#ifndef MINIZ_NO_STDIO +#ifndef MINIZ_NO_ARCHIVE_WRITING_APIS +static mz_bool mz_zip_get_file_modified_time(const char *pFilename, MZ_TIME_T *pTime) +{ + struct MZ_FILE_STAT_STRUCT file_stat; -static void -ctx_rasterizer_define_texture (CtxRasterizer *rasterizer, - const char *eid, - int width, - int height, - int format, - char unsigned *data) + /* On Linux with x86 glibc, this call will fail on large files (I think >= 0x80000000 bytes) unless you compiled with _LARGEFILE64_SOURCE. Argh. */ + if (MZ_FILE_STAT(pFilename, &file_stat) != 0) + return MZ_FALSE; + + *pTime = file_stat.st_mtime; + + return MZ_TRUE; +} +#endif /* #ifndef MINIZ_NO_ARCHIVE_WRITING_APIS*/ + +static mz_bool mz_zip_set_file_times(const char *pFilename, MZ_TIME_T access_time, MZ_TIME_T modified_time) { - _ctx_texture_lock (); // we're using the same texture_source from all threads, keeping allocaitons down - // need synchronizing (it could be better to do a pre-pass) - ctx_texture_init (rasterizer->texture_source, - eid, - width, - height, - ctx_pixel_format_get_stride ((CtxPixelFormat)format, width), - (CtxPixelFormat)format, -#if CTX_ENABLE_CM - (void*)rasterizer->state->gstate.texture_space, -#else - NULL, -#endif - data, - ctx_buffer_pixels_free, (void*)23); - /* when userdata for ctx_buffer_pixels_free is 23, texture_init dups the data on - * use - */ + struct utimbuf t; - ctx_rasterizer_set_texture (rasterizer, eid, 0.0f, 0.0f); - if (!rasterizer->state->gstate.source_fill.texture.buffer->color_managed) - { - _ctx_texture_prepare_color_management (rasterizer->state, - rasterizer->state->gstate.source_fill.texture.buffer); - } - _ctx_texture_unlock (); + memset(&t, 0, sizeof(t)); + t.actime = access_time; + t.modtime = modified_time; + + return !utime(pFilename, &t); } +#endif /* #ifndef MINIZ_NO_STDIO */ +#endif /* #ifndef MINIZ_NO_TIME */ +static MZ_FORCEINLINE mz_bool mz_zip_set_error(mz_zip_archive *pZip, mz_zip_error err_num) +{ + if (pZip) + pZip->m_last_error = err_num; + return MZ_FALSE; +} -inline static int -ctx_is_transparent (CtxRasterizer *rasterizer, int stroke) +static mz_bool mz_zip_reader_init_internal(mz_zip_archive *pZip, mz_uint flags) { - CtxGState *gstate = &rasterizer->state->gstate; - if (gstate->global_alpha_u8 == 0) - return 1; - if (gstate->source_fill.type == CTX_SOURCE_COLOR) - { - uint8_t ga[2]; - ctx_color_get_graya_u8 (rasterizer->state, &gstate->source_fill.color, ga); - if (ga[1] == 0) - return 1; - } - return 0; + (void)flags; + if ((!pZip) || (pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_INVALID)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + if (!pZip->m_pAlloc) + pZip->m_pAlloc = miniz_def_alloc_func; + if (!pZip->m_pFree) + pZip->m_pFree = miniz_def_free_func; + if (!pZip->m_pRealloc) + pZip->m_pRealloc = miniz_def_realloc_func; + + pZip->m_archive_size = 0; + pZip->m_central_directory_file_ofs = 0; + pZip->m_total_files = 0; + pZip->m_last_error = MZ_ZIP_NO_ERROR; + + if (NULL == (pZip->m_pState = (mz_zip_internal_state *)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, sizeof(mz_zip_internal_state)))) + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + + memset(pZip->m_pState, 0, sizeof(mz_zip_internal_state)); + MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir, sizeof(mz_uint8)); + MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir_offsets, sizeof(mz_uint32)); + MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_sorted_central_dir_offsets, sizeof(mz_uint32)); + pZip->m_pState->m_init_flags = flags; + pZip->m_pState->m_zip64 = MZ_FALSE; + pZip->m_pState->m_zip64_has_extended_info_fields = MZ_FALSE; + + pZip->m_zip_mode = MZ_ZIP_MODE_READING; + + return MZ_TRUE; } +static MZ_FORCEINLINE mz_bool mz_zip_reader_filename_less(const mz_zip_array *pCentral_dir_array, const mz_zip_array *pCentral_dir_offsets, mz_uint l_index, mz_uint r_index) +{ + const mz_uint8 *pL = &MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_array, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_offsets, mz_uint32, l_index)), *pE; + const mz_uint8 *pR = &MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_array, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_offsets, mz_uint32, r_index)); + mz_uint l_len = MZ_READ_LE16(pL + MZ_ZIP_CDH_FILENAME_LEN_OFS), r_len = MZ_READ_LE16(pR + MZ_ZIP_CDH_FILENAME_LEN_OFS); + mz_uint8 l = 0, r = 0; + pL += MZ_ZIP_CENTRAL_DIR_HEADER_SIZE; + pR += MZ_ZIP_CENTRAL_DIR_HEADER_SIZE; + pE = pL + MZ_MIN(l_len, r_len); + while (pL < pE) + { + if ((l = MZ_TOLOWER(*pL)) != (r = MZ_TOLOWER(*pR))) + break; + pL++; + pR++; + } + return (pL == pE) ? (l_len < r_len) : (l < r); +} +#define MZ_SWAP_UINT32(a, b) \ + do \ + { \ + mz_uint32 t = a; \ + a = b; \ + b = t; \ + } \ + MZ_MACRO_END -static void -ctx_rasterizer_fill (CtxRasterizer *rasterizer) +/* Heap sort of lowercased filenames, used to help accelerate plain central directory searches by mz_zip_reader_locate_file(). (Could also use qsort(), but it could allocate memory.) */ +static void mz_zip_reader_sort_central_dir_offsets_by_filename(mz_zip_archive *pZip) { - unsigned int preserved_count = - (rasterizer->preserve&&rasterizer->edge_list.count)? - rasterizer->edge_list.count:1; - int blit_x = rasterizer->blit_x; - int blit_y = rasterizer->blit_y; - int blit_width = rasterizer->blit_width; - int blit_height = rasterizer->blit_height; -#if CTX_SHAPE_CACHE - int blit_stride = rasterizer->blit_stride; -#endif + mz_zip_internal_state *pState = pZip->m_pState; + const mz_zip_array *pCentral_dir_offsets = &pState->m_central_dir_offsets; + const mz_zip_array *pCentral_dir = &pState->m_central_dir; + mz_uint32 *pIndices; + mz_uint32 start, end; + const mz_uint32 size = pZip->m_total_files; - CtxSegment temp[preserved_count]; /* copy of already built up path's poly line - XXX - by building a large enough path - the stack can be smashed! - */ - if (CTX_UNLIKELY(rasterizer->preserve)) - { memcpy (temp, rasterizer->edge_list.entries, sizeof (temp) ); } + if (size <= 1U) + return; -#if CTX_ENABLE_SHADOW_BLUR - if (CTX_UNLIKELY(rasterizer->in_shadow)) - { - for (unsigned int i = 0; i < rasterizer->edge_list.count; i++) + pIndices = &MZ_ZIP_ARRAY_ELEMENT(&pState->m_sorted_central_dir_offsets, mz_uint32, 0); + + start = (size - 2U) >> 1U; + for (;;) { - 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; + mz_uint64 child, root = start; + for (;;) + { + if ((child = (root << 1U) + 1U) >= size) + break; + child += (((child + 1U) < size) && (mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, pIndices[child], pIndices[child + 1U]))); + if (!mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, pIndices[root], pIndices[child])) + break; + MZ_SWAP_UINT32(pIndices[root], pIndices[child]); + root = child; + } + if (!start) + break; + start--; } - rasterizer->scan_min += rasterizer->shadow_y * CTX_FULL_AA; - rasterizer->scan_max += rasterizer->shadow_y * CTX_FULL_AA; - rasterizer->col_min += (rasterizer->shadow_x - rasterizer->state->gstate.shadow_blur * 3 + 1) * CTX_SUBDIV; - rasterizer->col_max += (rasterizer->shadow_x + rasterizer->state->gstate.shadow_blur * 3 + 1) * CTX_SUBDIV; - } -#endif - if (CTX_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)) + end = size - 1; + while (end > 0) { + mz_uint64 child, root = 0; + MZ_SWAP_UINT32(pIndices[end], pIndices[0]); + for (;;) + { + if ((child = (root << 1U) + 1U) >= end) + break; + child += (((child + 1U) < end) && mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, pIndices[child], pIndices[child + 1U])); + if (!mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, pIndices[root], pIndices[child])) + break; + MZ_SWAP_UINT32(pIndices[root], pIndices[child]); + root = child; + } + end--; } - else - { - ctx_composite_setup (rasterizer); +} - rasterizer->state->ink_min_x = ctx_mini (rasterizer->state->ink_min_x, rasterizer->col_min / CTX_SUBDIV); - rasterizer->state->ink_max_x = ctx_maxi (rasterizer->state->ink_min_x, rasterizer->col_max / CTX_SUBDIV); - rasterizer->state->ink_min_y = ctx_mini (rasterizer->state->ink_min_y, rasterizer->scan_min / CTX_FULL_AA); - rasterizer->state->ink_max_y = ctx_maxi (rasterizer->state->ink_max_y, rasterizer->scan_max / CTX_FULL_AA); +static mz_bool mz_zip_reader_locate_header_sig(mz_zip_archive *pZip, mz_uint32 record_sig, mz_uint32 record_size, mz_int64 *pOfs) +{ + mz_int64 cur_file_ofs; + mz_uint32 buf_u32[4096 / sizeof(mz_uint32)]; + mz_uint8 *pBuf = (mz_uint8 *)buf_u32; -#if CTX_FAST_FILL_RECT - if (rasterizer->edge_list.count == 5) + /* Basic sanity checks - reject files which are too small */ + if (pZip->m_archive_size < record_size) + return MZ_FALSE; + + /* Find the record by scanning the file from the end towards the beginning. */ + cur_file_ofs = MZ_MAX((mz_int64)pZip->m_archive_size - (mz_int64)sizeof(buf_u32), 0); + for (;;) { - CtxSegment *entry0 = &(((CtxSegment*)(rasterizer->edge_list.entries)))[0]; - CtxSegment *entry1 = &(((CtxSegment*)(rasterizer->edge_list.entries)))[1]; - CtxSegment *entry2 = &(((CtxSegment*)(rasterizer->edge_list.entries)))[2]; - CtxSegment *entry3 = &(((CtxSegment*)(rasterizer->edge_list.entries)))[3]; + int i, n = (int)MZ_MIN(sizeof(buf_u32), pZip->m_archive_size - cur_file_ofs); + if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pBuf, n) != (mz_uint)n) + return MZ_FALSE; - if ( - (!(rasterizer->state->gstate.clipped != 0)) & - (entry0->data.s16[2] == entry1->data.s16[2]) & - (entry0->data.s16[3] == entry3->data.s16[3]) & - (entry1->data.s16[3] == entry2->data.s16[3]) & - (entry2->data.s16[2] == entry3->data.s16[2]) -#if CTX_ENABLE_SHADOW_BLUR - && !rasterizer->in_shadow -#endif - ) - { - 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); + for (i = n - 4; i >= 0; --i) + { + mz_uint s = MZ_READ_LE32(pBuf + i); + if (s == record_sig) + { + if ((pZip->m_archive_size - (cur_file_ofs + i)) >= record_size) + break; + } + } - if (x1 > x0 && y1 > y0) - { - ctx_composite_fill_rect (rasterizer, x0, y0, x1, y1, 255); - goto done; - } - } + if (i >= 0) + { + cur_file_ofs += i; + break; + } + + /* Give up if we've searched the entire file, or we've gone back "too far" (~64kb) */ + if ((!cur_file_ofs) || ((pZip->m_archive_size - cur_file_ofs) >= (MZ_UINT16_MAX + record_size))) + return MZ_FALSE; + + cur_file_ofs = MZ_MAX(cur_file_ofs - (sizeof(buf_u32) - 3), 0); } -#endif - ctx_rasterizer_finish_shape (rasterizer); + *pOfs = cur_file_ofs; + return MZ_TRUE; +} - uint32_t hash = ctx_rasterizer_poly_to_edges (rasterizer); - if (hash){}; +static mz_bool mz_zip_reader_read_central_dir(mz_zip_archive *pZip, mz_uint flags) +{ + mz_uint cdir_size = 0, cdir_entries_on_this_disk = 0, num_this_disk = 0, cdir_disk_index = 0; + mz_uint64 cdir_ofs = 0; + mz_int64 cur_file_ofs = 0; + const mz_uint8 *p; -#if CTX_SHAPE_CACHE - int width = (rasterizer->col_max + (CTX_SUBDIV-1) ) / CTX_SUBDIV - rasterizer->col_min/CTX_SUBDIV + 1; - int height = (rasterizer->scan_max + (CTX_FULL_AA-1) ) / CTX_FULL_AA - rasterizer->scan_min / CTX_FULL_AA + 1; - if (width * height < CTX_SHAPE_CACHE_DIM && width >=1 && height >= 1 - && width < CTX_SHAPE_CACHE_MAX_DIM - && height < CTX_SHAPE_CACHE_MAX_DIM -#if CTX_ENABLE_SHADOW_BLUR - && !rasterizer->in_shadow -#endif - ) - { - int scan_min = rasterizer->scan_min; - int col_min = rasterizer->col_min; - scan_min -= (scan_min % CTX_FULL_AA); - int y0 = scan_min / CTX_FULL_AA; - int y1 = y0 + height; - int x0 = col_min / CTX_SUBDIV; - int ymin = y0; - int x1 = x0 + width; - int clip_x_min = blit_x; - int clip_x_max = blit_x + blit_width - 1; - int clip_y_min = blit_y; - int clip_y_max = blit_y + blit_height - 1; + mz_uint32 buf_u32[4096 / sizeof(mz_uint32)]; + mz_uint8 *pBuf = (mz_uint8 *)buf_u32; + mz_bool sort_central_dir = ((flags & MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY) == 0); + mz_uint32 zip64_end_of_central_dir_locator_u32[(MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; + mz_uint8 *pZip64_locator = (mz_uint8 *)zip64_end_of_central_dir_locator_u32; - int dont_cache = 0; - if (CTX_UNLIKELY(x1 >= clip_x_max)) - { x1 = clip_x_max; - dont_cache = 1; - } - int xo = 0; - if (CTX_UNLIKELY(x0 < clip_x_min)) - { - xo = clip_x_min - x0; - x0 = clip_x_min; - dont_cache = 1; - } - if (CTX_UNLIKELY(y0 < clip_y_min || y1 >= clip_y_max)) - dont_cache = 1; - if (dont_cache || !_ctx_shape_cache_enabled) + mz_uint32 zip64_end_of_central_dir_header_u32[(MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; + mz_uint8 *pZip64_end_of_central_dir = (mz_uint8 *)zip64_end_of_central_dir_header_u32; + + mz_uint64 zip64_end_of_central_dir_ofs = 0; + + /* Basic sanity checks - reject files which are too small, and check the first 4 bytes of the file to make sure a local header is there. */ + if (pZip->m_archive_size < MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) + return mz_zip_set_error(pZip, MZ_ZIP_NOT_AN_ARCHIVE); + + if (!mz_zip_reader_locate_header_sig(pZip, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE, &cur_file_ofs)) + return mz_zip_set_error(pZip, MZ_ZIP_FAILED_FINDING_CENTRAL_DIR); + + /* Read and verify the end of central directory record. */ + if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pBuf, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) != MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + + if (MZ_READ_LE32(pBuf + MZ_ZIP_ECDH_SIG_OFS) != MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG) + return mz_zip_set_error(pZip, MZ_ZIP_NOT_AN_ARCHIVE); + + if (cur_file_ofs >= (MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE + MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE)) + { + if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs - MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE, pZip64_locator, MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE) == MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE) { - rasterizer->scanline = scan_min; - ctx_rasterizer_rasterize_edges (rasterizer, rasterizer->state->gstate.fill_rule -#if CTX_SHAPE_CACHE - , NULL -#endif - ); + if (MZ_READ_LE32(pZip64_locator + MZ_ZIP64_ECDL_SIG_OFS) == MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIG) + { + zip64_end_of_central_dir_ofs = MZ_READ_LE64(pZip64_locator + MZ_ZIP64_ECDL_REL_OFS_TO_ZIP64_ECDR_OFS); + if (zip64_end_of_central_dir_ofs > (pZip->m_archive_size - MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE)) + return mz_zip_set_error(pZip, MZ_ZIP_NOT_AN_ARCHIVE); + + if (pZip->m_pRead(pZip->m_pIO_opaque, zip64_end_of_central_dir_ofs, pZip64_end_of_central_dir, MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE) == MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE) + { + if (MZ_READ_LE32(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_SIG_OFS) == MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIG) + { + pZip->m_pState->m_zip64 = MZ_TRUE; + } + } + } } - else + } + + pZip->m_total_files = MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_CDIR_TOTAL_ENTRIES_OFS); + cdir_entries_on_this_disk = MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS); + num_this_disk = MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_NUM_THIS_DISK_OFS); + cdir_disk_index = MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_NUM_DISK_CDIR_OFS); + cdir_size = MZ_READ_LE32(pBuf + MZ_ZIP_ECDH_CDIR_SIZE_OFS); + cdir_ofs = MZ_READ_LE32(pBuf + MZ_ZIP_ECDH_CDIR_OFS_OFS); + + if (pZip->m_pState->m_zip64) + { + mz_uint32 zip64_total_num_of_disks = MZ_READ_LE32(pZip64_locator + MZ_ZIP64_ECDL_TOTAL_NUMBER_OF_DISKS_OFS); + mz_uint64 zip64_cdir_total_entries = MZ_READ_LE64(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_CDIR_TOTAL_ENTRIES_OFS); + mz_uint64 zip64_cdir_total_entries_on_this_disk = MZ_READ_LE64(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS); + mz_uint64 zip64_size_of_end_of_central_dir_record = MZ_READ_LE64(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_SIZE_OF_RECORD_OFS); + mz_uint64 zip64_size_of_central_directory = MZ_READ_LE64(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_CDIR_SIZE_OFS); + + if (zip64_size_of_end_of_central_dir_record < (MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE - 12)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + if (zip64_total_num_of_disks != 1U) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_MULTIDISK); + + /* Check for miniz's practical limits */ + if (zip64_cdir_total_entries > MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); + + pZip->m_total_files = (mz_uint32)zip64_cdir_total_entries; + + if (zip64_cdir_total_entries_on_this_disk > MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); + + cdir_entries_on_this_disk = (mz_uint32)zip64_cdir_total_entries_on_this_disk; + + /* Check for miniz's current practical limits (sorry, this should be enough for millions of files) */ + if (zip64_size_of_central_directory > MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_CDIR_SIZE); + + cdir_size = (mz_uint32)zip64_size_of_central_directory; + + num_this_disk = MZ_READ_LE32(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_NUM_THIS_DISK_OFS); + + cdir_disk_index = MZ_READ_LE32(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_NUM_DISK_CDIR_OFS); + + cdir_ofs = MZ_READ_LE64(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_CDIR_OFS_OFS); + } + + if (pZip->m_total_files != cdir_entries_on_this_disk) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_MULTIDISK); + + if (((num_this_disk | cdir_disk_index) != 0) && ((num_this_disk != 1) || (cdir_disk_index != 1))) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_MULTIDISK); + + if (cdir_size < pZip->m_total_files * MZ_ZIP_CENTRAL_DIR_HEADER_SIZE) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + if ((cdir_ofs + (mz_uint64)cdir_size) > pZip->m_archive_size) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + pZip->m_central_directory_file_ofs = cdir_ofs; + + if (pZip->m_total_files) + { + mz_uint i, n; + /* Read the entire central directory into a heap block, and allocate another heap block to hold the unsorted central dir file record offsets, and possibly another to hold the sorted indices. */ + if ((!mz_zip_array_resize(pZip, &pZip->m_pState->m_central_dir, cdir_size, MZ_FALSE)) || + (!mz_zip_array_resize(pZip, &pZip->m_pState->m_central_dir_offsets, pZip->m_total_files, MZ_FALSE))) + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + + if (sort_central_dir) { - rasterizer->scanline = scan_min; - CtxShapeEntry *shape = ctx_shape_entry_find (rasterizer, hash, width, height); + if (!mz_zip_array_resize(pZip, &pZip->m_pState->m_sorted_central_dir_offsets, pZip->m_total_files, MZ_FALSE)) + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + } - if (shape->uses == 0) - { - CtxBuffer *buffer_backup = rasterizer->clip_buffer; - rasterizer->clip_buffer = NULL; - ctx_rasterizer_rasterize_edges (rasterizer, rasterizer->state->gstate.fill_rule, shape); - rasterizer->clip_buffer = buffer_backup; - } + if (pZip->m_pRead(pZip->m_pIO_opaque, cdir_ofs, pZip->m_pState->m_central_dir.m_p, cdir_size) != cdir_size) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); - int ewidth = x1 - x0; - if (ewidth>0) + /* Now create an index into the central directory file records, do some basic sanity checking on each record */ + p = (const mz_uint8 *)pZip->m_pState->m_central_dir.m_p; + for (n = cdir_size, i = 0; i < pZip->m_total_files; ++i) { - rasterizer->scanline = scan_min; - int bpp = rasterizer->format->bpp; - if (rasterizer->clip_buffer && !rasterizer->clip_rectangle) - { - uint8_t composite[ewidth]; - uint8_t *clip_data = (uint8_t*)rasterizer->clip_buffer->data; - int shape_width = shape->width; - for (int y = y0; y < y1; y++) + mz_uint total_header_size, disk_index, bit_flags, filename_size, ext_data_size; + mz_uint64 comp_size, decomp_size, local_header_ofs; + + if ((n < MZ_ZIP_CENTRAL_DIR_HEADER_SIZE) || (MZ_READ_LE32(p) != MZ_ZIP_CENTRAL_DIR_HEADER_SIG)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, mz_uint32, i) = (mz_uint32)(p - (const mz_uint8 *)pZip->m_pState->m_central_dir.m_p); + + if (sort_central_dir) + MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_sorted_central_dir_offsets, mz_uint32, i) = i; + + comp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS); + decomp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS); + local_header_ofs = MZ_READ_LE32(p + MZ_ZIP_CDH_LOCAL_HEADER_OFS); + filename_size = MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS); + ext_data_size = MZ_READ_LE16(p + MZ_ZIP_CDH_EXTRA_LEN_OFS); + + if ((!pZip->m_pState->m_zip64_has_extended_info_fields) && + (ext_data_size) && + (MZ_MAX(MZ_MAX(comp_size, decomp_size), local_header_ofs) == MZ_UINT32_MAX)) { - if ( (y >= clip_y_min) && (y <= clip_y_max) ) + /* Attempt to find zip64 extended information field in the entry's extra data */ + mz_uint32 extra_size_remaining = ext_data_size; + + if (extra_size_remaining) { - for (int x = 0; x < ewidth; x++) + const mz_uint8 *pExtra_data; + void* buf = NULL; + + if (MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + filename_size + ext_data_size > n) + { + buf = MZ_MALLOC(ext_data_size); + if(buf==NULL) + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + + if (pZip->m_pRead(pZip->m_pIO_opaque, cdir_ofs + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + filename_size, buf, ext_data_size) != ext_data_size) + { + MZ_FREE(buf); + return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + } + + pExtra_data = (mz_uint8*)buf; + } + else + { + pExtra_data = p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + filename_size; + } + + do { - int val = shape->data[shape_width * (int)(y-ymin) + xo + x]; - // XXX : not valid for 1bit clip buffers - val = (val*(clip_data) [ - ((y-blit_y) * blit_width) + x0 + x])/255; - composite[x] = val; - } - rasterizer->apply_coverage (rasterizer, - ( (uint8_t *) rasterizer->buf) + (y-blit_y) * blit_stride + ((int) (x0) * bpp)/8, - rasterizer->color, - x0, // is 0 - composite, - ewidth ); - } - rasterizer->scanline += CTX_FULL_AA; + mz_uint32 field_id; + mz_uint32 field_data_size; + + if (extra_size_remaining < (sizeof(mz_uint16) * 2)) + { + MZ_FREE(buf); + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + } + + field_id = MZ_READ_LE16(pExtra_data); + field_data_size = MZ_READ_LE16(pExtra_data + sizeof(mz_uint16)); + + if ((field_data_size + sizeof(mz_uint16) * 2) > extra_size_remaining) + { + MZ_FREE(buf); + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + } + + if (field_id == MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID) + { + /* Ok, the archive didn't have any zip64 headers but it uses a zip64 extended information field so mark it as zip64 anyway (this can occur with infozip's zip util when it reads compresses files from stdin). */ + pZip->m_pState->m_zip64 = MZ_TRUE; + pZip->m_pState->m_zip64_has_extended_info_fields = MZ_TRUE; + break; + } + + pExtra_data += sizeof(mz_uint16) * 2 + field_data_size; + extra_size_remaining = extra_size_remaining - sizeof(mz_uint16) * 2 - field_data_size; + } while (extra_size_remaining); + + MZ_FREE(buf); + } } - } - else - { - for (int y = y0; y < y1; y++) + + /* I've seen archives that aren't marked as zip64 that uses zip64 ext data, argh */ + if ((comp_size != MZ_UINT32_MAX) && (decomp_size != MZ_UINT32_MAX)) { - if (CTX_LIKELY((y >= clip_y_min) && (y <= clip_y_max) )) + if (((!MZ_READ_LE32(p + MZ_ZIP_CDH_METHOD_OFS)) && (decomp_size != comp_size)) || (decomp_size && !comp_size)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + } + + disk_index = MZ_READ_LE16(p + MZ_ZIP_CDH_DISK_START_OFS); + if ((disk_index == MZ_UINT16_MAX) || ((disk_index != num_this_disk) && (disk_index != 1))) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_MULTIDISK); + + if (comp_size != MZ_UINT32_MAX) + { + if (((mz_uint64)MZ_READ_LE32(p + MZ_ZIP_CDH_LOCAL_HEADER_OFS) + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + comp_size) > pZip->m_archive_size) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + } + + bit_flags = MZ_READ_LE16(p + MZ_ZIP_CDH_BIT_FLAG_OFS); + if (bit_flags & MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_LOCAL_DIR_IS_MASKED) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_ENCRYPTION); + + if ((total_header_size = MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS) + MZ_READ_LE16(p + MZ_ZIP_CDH_EXTRA_LEN_OFS) + MZ_READ_LE16(p + MZ_ZIP_CDH_COMMENT_LEN_OFS)) > n) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + n -= total_header_size; + p += total_header_size; + } + } + + if (sort_central_dir) + mz_zip_reader_sort_central_dir_offsets_by_filename(pZip); + + return MZ_TRUE; +} + +void mz_zip_zero_struct(mz_zip_archive *pZip) +{ + if (pZip) + MZ_CLEAR_PTR(pZip); +} + +static mz_bool mz_zip_reader_end_internal(mz_zip_archive *pZip, mz_bool set_last_error) +{ + mz_bool status = MZ_TRUE; + + if (!pZip) + return MZ_FALSE; + + if ((!pZip->m_pState) || (!pZip->m_pAlloc) || (!pZip->m_pFree) || (pZip->m_zip_mode != MZ_ZIP_MODE_READING)) + { + if (set_last_error) + pZip->m_last_error = MZ_ZIP_INVALID_PARAMETER; + + return MZ_FALSE; + } + + if (pZip->m_pState) + { + mz_zip_internal_state *pState = pZip->m_pState; + pZip->m_pState = NULL; + + mz_zip_array_clear(pZip, &pState->m_central_dir); + mz_zip_array_clear(pZip, &pState->m_central_dir_offsets); + mz_zip_array_clear(pZip, &pState->m_sorted_central_dir_offsets); + +#ifndef MINIZ_NO_STDIO + if (pState->m_pFile) + { + if (pZip->m_zip_type == MZ_ZIP_TYPE_FILE) + { + if (MZ_FCLOSE(pState->m_pFile) == EOF) { - rasterizer->apply_coverage (rasterizer, - ( (uint8_t *) rasterizer->buf) + (y-blit_y) * blit_stride + (int) ((x0) * bpp)/8, rasterizer->color, - x0, - &shape->data[shape->width * (int) (y-ymin) + xo], - ewidth ); + if (set_last_error) + pZip->m_last_error = MZ_ZIP_FILE_CLOSE_FAILED; + status = MZ_FALSE; } - rasterizer->scanline += CTX_FULL_AA; } - } - } + pState->m_pFile = NULL; } - } - else -#endif +#endif /* #ifndef MINIZ_NO_STDIO */ + + pZip->m_pFree(pZip->m_pAlloc_opaque, pState); + } + pZip->m_zip_mode = MZ_ZIP_MODE_INVALID; + + return status; +} + +mz_bool mz_zip_reader_end(mz_zip_archive *pZip) +{ + return mz_zip_reader_end_internal(pZip, MZ_TRUE); +} +mz_bool mz_zip_reader_init(mz_zip_archive *pZip, mz_uint64 size, mz_uint flags) +{ + if ((!pZip) || (!pZip->m_pRead)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + if (!mz_zip_reader_init_internal(pZip, flags)) + return MZ_FALSE; + + pZip->m_zip_type = MZ_ZIP_TYPE_USER; + pZip->m_archive_size = size; + + if (!mz_zip_reader_read_central_dir(pZip, flags)) { - - ctx_rasterizer_rasterize_edges (rasterizer, rasterizer->state->gstate.fill_rule -#if CTX_SHAPE_CACHE - , NULL -#endif - ); + mz_zip_reader_end_internal(pZip, MZ_FALSE); + return MZ_FALSE; } - } -#if CTX_FAST_FILL_RECT -done: + + return MZ_TRUE; +} + +static size_t mz_zip_mem_read_func(void *pOpaque, mz_uint64 file_ofs, void *pBuf, size_t n) +{ + mz_zip_archive *pZip = (mz_zip_archive *)pOpaque; + size_t s = (file_ofs >= pZip->m_archive_size) ? 0 : (size_t)MZ_MIN(pZip->m_archive_size - file_ofs, n); + memcpy(pBuf, (const mz_uint8 *)pZip->m_pState->m_pMem + file_ofs, s); + return s; +} + +mz_bool mz_zip_reader_init_mem(mz_zip_archive *pZip, const void *pMem, size_t size, mz_uint flags) +{ + if (!pMem) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + if (size < MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) + return mz_zip_set_error(pZip, MZ_ZIP_NOT_AN_ARCHIVE); + + if (!mz_zip_reader_init_internal(pZip, flags)) + return MZ_FALSE; + + pZip->m_zip_type = MZ_ZIP_TYPE_MEMORY; + pZip->m_archive_size = size; + pZip->m_pRead = mz_zip_mem_read_func; + pZip->m_pIO_opaque = pZip; + pZip->m_pNeeds_keepalive = NULL; + +#ifdef __cplusplus + pZip->m_pState->m_pMem = const_cast<void *>(pMem); +#else + pZip->m_pState->m_pMem = (void *)pMem; #endif - if (CTX_UNLIKELY(rasterizer->preserve)) + + pZip->m_pState->m_mem_size = size; + + if (!mz_zip_reader_read_central_dir(pZip, flags)) { - memcpy (rasterizer->edge_list.entries, temp, sizeof (temp) ); - rasterizer->edge_list.count = preserved_count; + mz_zip_reader_end_internal(pZip, MZ_FALSE); + return MZ_FALSE; } -#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; - } -#endif - rasterizer->preserve = 0; + + return MZ_TRUE; } -#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) +#ifndef MINIZ_NO_STDIO +static size_t mz_zip_file_read_func(void *pOpaque, mz_uint64 file_ofs, void *pBuf, size_t n) { + mz_zip_archive *pZip = (mz_zip_archive *)pOpaque; + mz_int64 cur_ofs = MZ_FTELL64(pZip->m_pState->m_pFile); -} -#endif + file_ofs += pZip->m_pState->m_file_archive_start_ofs; + if (((mz_int64)file_ofs < 0) || (((cur_ofs != (mz_int64)file_ofs)) && (MZ_FSEEK64(pZip->m_pState->m_pFile, (mz_int64)file_ofs, SEEK_SET)))) + return 0; -typedef struct _CtxTermGlyph CtxTermGlyph; + return MZ_FREAD(pBuf, 1, n, pZip->m_pState->m_pFile); +} -struct _CtxTermGlyph +mz_bool mz_zip_reader_init_file(mz_zip_archive *pZip, const char *pFilename, mz_uint32 flags) { - uint32_t unichar; - int col; - int row; - uint8_t rgba_bg[4]; - uint8_t rgba_fg[4]; -}; + return mz_zip_reader_init_file_v2(pZip, pFilename, flags, 0, 0); +} -static int _ctx_glyph (Ctx *ctx, uint32_t unichar, int stroke); -static void -ctx_rasterizer_glyph (CtxRasterizer *rasterizer, uint32_t unichar, int stroke) +mz_bool mz_zip_reader_init_file_v2(mz_zip_archive *pZip, const char *pFilename, mz_uint flags, mz_uint64 file_start_ofs, mz_uint64 archive_size) { - float tx = rasterizer->state->x; - float ty = rasterizer->state->y - rasterizer->state->gstate.font_size; - float tx2 = rasterizer->state->x + rasterizer->state->gstate.font_size; - float ty2 = rasterizer->state->y + rasterizer->state->gstate.font_size; - _ctx_user_to_device (rasterizer->state, &tx, &ty); - _ctx_user_to_device (rasterizer->state, &tx2, &ty2); + mz_uint64 file_size; + MZ_FILE *pFile; - 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 ((!pZip) || (!pFilename) || ((archive_size) && (archive_size < MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE))) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); -#if CTX_BRAILLE_TEXT - float font_size = 0; - int ch = 1; - int cw = 1; + pFile = MZ_FOPEN(pFilename, "rb"); + if (!pFile) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_OPEN_FAILED); - if (rasterizer->term_glyphs) - { - float tx = 0; - font_size = rasterizer->state->gstate.font_size; + file_size = archive_size; + if (!file_size) + { + if (MZ_FSEEK64(pFile, 0, SEEK_END)) + { + MZ_FCLOSE(pFile); + return mz_zip_set_error(pZip, MZ_ZIP_FILE_SEEK_FAILED); + } - ch = (int)ctx_term_get_cell_height (rasterizer->backend.ctx); - cw = (int)ctx_term_get_cell_width (rasterizer->backend.ctx); + file_size = MZ_FTELL64(pFile); + } - _ctx_user_to_device_distance (rasterizer->state, &tx, &font_size); - } - if (rasterizer->term_glyphs && !stroke && - fabsf (font_size - ch) < 0.5f) - { - float tx = rasterizer->x; - float ty = rasterizer->y; - _ctx_user_to_device (rasterizer->state, &tx, &ty); - int col = (int)(tx / cw + 1); - int row = (int)(ty / ch + 1); - CtxTermGlyph *glyph = ctx_calloc (sizeof (CtxTermGlyph), 1); - ctx_list_append (&rasterizer->glyphs, glyph); - glyph->unichar = unichar; - glyph->col = col; - glyph->row = row; - ctx_color_get_rgba8 (rasterizer->state, &rasterizer->state->gstate.source_fill.color, - &glyph->rgba_fg[0]); - } - else -#endif - _ctx_glyph (rasterizer->backend.ctx, unichar, stroke); + /* TODO: Better sanity check archive_size and the # of actual remaining bytes */ + + if (file_size < MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) + { + MZ_FCLOSE(pFile); + return mz_zip_set_error(pZip, MZ_ZIP_NOT_AN_ARCHIVE); + } + + if (!mz_zip_reader_init_internal(pZip, flags)) + { + MZ_FCLOSE(pFile); + return MZ_FALSE; + } + + pZip->m_zip_type = MZ_ZIP_TYPE_FILE; + pZip->m_pRead = mz_zip_file_read_func; + pZip->m_pIO_opaque = pZip; + pZip->m_pState->m_pFile = pFile; + pZip->m_archive_size = file_size; + pZip->m_pState->m_file_archive_start_ofs = file_start_ofs; + + if (!mz_zip_reader_read_central_dir(pZip, flags)) + { + mz_zip_reader_end_internal(pZip, MZ_FALSE); + return MZ_FALSE; + } + + return MZ_TRUE; } -static void -_ctx_text (Ctx *ctx, - const char *string, - int stroke, - int visible); -static void -ctx_rasterizer_text (CtxRasterizer *rasterizer, const char *string, int stroke) +mz_bool mz_zip_reader_init_cfile(mz_zip_archive *pZip, MZ_FILE *pFile, mz_uint64 archive_size, mz_uint flags) { -#if CTX_BRAILLE_TEXT - float font_size = 0; - if (rasterizer->term_glyphs) - { - float tx = 0; - font_size = rasterizer->state->gstate.font_size; - _ctx_user_to_device_distance (rasterizer->state, &tx, &font_size); - } - int ch = (int)ctx_term_get_cell_height (rasterizer->backend.ctx); - int cw = (int)ctx_term_get_cell_width (rasterizer->backend.ctx); + mz_uint64 cur_file_ofs; - if (rasterizer->term_glyphs && !stroke && - fabsf (font_size - ch) < 0.5f) - { - float tx = rasterizer->x; - float ty = rasterizer->y; - _ctx_user_to_device (rasterizer->state, &tx, &ty); - int col = (int)(tx / cw + 1); - int row = (int)(ty / ch + 1); + if ((!pZip) || (!pFile)) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_OPEN_FAILED); - for (int i = 0; string[i]; i++, col++) + cur_file_ofs = MZ_FTELL64(pFile); + + if (!archive_size) { - CtxTermGlyph *glyph = ctx_calloc (sizeof (CtxTermGlyph), 1); - ctx_list_prepend (&rasterizer->glyphs, glyph); - glyph->unichar = string[i]; - glyph->col = col; - glyph->row = row; - ctx_color_get_rgba8 (rasterizer->state, &rasterizer->state->gstate.source_fill.color, - glyph->rgba_fg); + if (MZ_FSEEK64(pFile, 0, SEEK_END)) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_SEEK_FAILED); + + archive_size = MZ_FTELL64(pFile) - cur_file_ofs; + + if (archive_size < MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) + return mz_zip_set_error(pZip, MZ_ZIP_NOT_AN_ARCHIVE); } - } - else -#endif - { - _ctx_text (rasterizer->backend.ctx, string, stroke, 1); - } + + if (!mz_zip_reader_init_internal(pZip, flags)) + return MZ_FALSE; + + pZip->m_zip_type = MZ_ZIP_TYPE_CFILE; + pZip->m_pRead = mz_zip_file_read_func; + + pZip->m_pIO_opaque = pZip; + pZip->m_pState->m_pFile = pFile; + pZip->m_archive_size = archive_size; + pZip->m_pState->m_file_archive_start_ofs = cur_file_ofs; + + if (!mz_zip_reader_read_central_dir(pZip, flags)) + { + mz_zip_reader_end_internal(pZip, MZ_FALSE); + return MZ_FALSE; + } + + return MZ_TRUE; } -void -_ctx_font (Ctx *ctx, const char *name); -static void -ctx_rasterizer_set_font (CtxRasterizer *rasterizer, const char *font_name) +#endif /* #ifndef MINIZ_NO_STDIO */ + +static MZ_FORCEINLINE const mz_uint8 *mz_zip_get_cdh(mz_zip_archive *pZip, mz_uint file_index) { - //_ctx_font (rasterizer->backend.ctx, font_name); + if ((!pZip) || (!pZip->m_pState) || (file_index >= pZip->m_total_files)) + return NULL; + return &MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, mz_uint32, file_index)); } -static void -ctx_rasterizer_arc (CtxRasterizer *rasterizer, - float x, - float y, - float radius, - float start_angle, - float end_angle, - int anticlockwise) +mz_bool mz_zip_reader_is_file_encrypted(mz_zip_archive *pZip, mz_uint file_index) { - float factor = ctx_matrix_get_scale (&rasterizer->state->gstate.transform); - int full_segments = CTX_RASTERIZER_MAX_CIRCLE_SEGMENTS; - 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; - float step = CTX_PI*2.0f/full_segments; - int steps; + mz_uint m_bit_flag; + const mz_uint8 *p = mz_zip_get_cdh(pZip, file_index); + if (!p) + { + mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + return MZ_FALSE; + } - if (end_angle < -30.0f) - end_angle = -30.0f; - if (start_angle < -30.0f) - start_angle = -30.0f; - if (end_angle > 30.0f) - end_angle = 30.0f; - if (start_angle > 30.0f) - start_angle = 30.0f; + m_bit_flag = MZ_READ_LE16(p + MZ_ZIP_CDH_BIT_FLAG_OFS); + return (m_bit_flag & (MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_IS_ENCRYPTED | MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_USES_STRONG_ENCRYPTION)) != 0; +} - if (radius <= 0.0001f) - return; +mz_bool mz_zip_reader_is_file_supported(mz_zip_archive *pZip, mz_uint file_index) +{ + mz_uint bit_flag; + mz_uint method; - if (end_angle == start_angle) - // XXX also detect arcs fully outside render view + const mz_uint8 *p = mz_zip_get_cdh(pZip, file_index); + if (!p) { - if (rasterizer->has_prev!=0) - ctx_rasterizer_line_to (rasterizer, x + ctx_cosf (end_angle) * radius, - y + ctx_sinf (end_angle) * radius); - else - ctx_rasterizer_move_to (rasterizer, x + ctx_cosf (end_angle) * radius, - y + ctx_sinf (end_angle) * radius); - return; + mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + return MZ_FALSE; } -#if 1 - if ( (!anticlockwise && fabsf((end_angle - start_angle) - CTX_PI*2) < 0.01f) || - ( (anticlockwise && fabsf((start_angle - end_angle) - CTX_PI*2) < 0.01f ) ) - || (anticlockwise && fabsf((end_angle - start_angle) - CTX_PI*2) < 0.01f) || (!anticlockwise && fabsf((start_angle - end_angle) - CTX_PI*2) < 0.01f ) ) + + method = MZ_READ_LE16(p + MZ_ZIP_CDH_METHOD_OFS); + bit_flag = MZ_READ_LE16(p + MZ_ZIP_CDH_BIT_FLAG_OFS); + + if ((method != 0) && (method != MZ_DEFLATED)) { - steps = full_segments - 1; + mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_METHOD); + return MZ_FALSE; } - else -#endif + + if (bit_flag & (MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_IS_ENCRYPTED | MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_USES_STRONG_ENCRYPTION)) { - if (anticlockwise) - steps = (int)((start_angle - end_angle) / (CTX_PI*2) * full_segments); - else - steps = (int)((end_angle - start_angle) / (CTX_PI*2) * full_segments); - // if (steps > full_segments) - // steps = full_segments; + mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_ENCRYPTION); + return MZ_FALSE; } - if (anticlockwise) { step = step * -1; } - int first = 1; - if (steps == 0 /* || steps==full_segments -1 || (anticlockwise && steps == full_segments) */) + if (bit_flag & MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_COMPRESSED_PATCH_FLAG) { - float xv = x + ctx_cosf (start_angle) * radius; - float yv = y + ctx_sinf (start_angle) * radius; - if (!rasterizer->has_prev) - { ctx_rasterizer_move_to (rasterizer, xv, yv); } - first = 0; + mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_FEATURE); + return MZ_FALSE; } - else + + return MZ_TRUE; +} + +mz_bool mz_zip_reader_is_file_a_directory(mz_zip_archive *pZip, mz_uint file_index) +{ + mz_uint filename_len, attribute_mapping_id, external_attr; + const mz_uint8 *p = mz_zip_get_cdh(pZip, file_index); + if (!p) { - for (float angle = start_angle, i = 0; i < steps; angle += step, i++) + mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + return MZ_FALSE; + } + + filename_len = MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS); + if (filename_len) + { + if (*(p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + filename_len - 1) == '/') + return MZ_TRUE; + } + + /* Bugfix: This code was also checking if the internal attribute was non-zero, which wasn't correct. */ + /* Most/all zip writers (hopefully) set DOS file/directory attributes in the low 16-bits, so check for the DOS directory flag and ignore the source OS ID in the created by field. */ + /* FIXME: Remove this check? Is it necessary - we already check the filename. */ + attribute_mapping_id = MZ_READ_LE16(p + MZ_ZIP_CDH_VERSION_MADE_BY_OFS) >> 8; + (void)attribute_mapping_id; + + external_attr = MZ_READ_LE32(p + MZ_ZIP_CDH_EXTERNAL_ATTR_OFS); + if ((external_attr & MZ_ZIP_DOS_DIR_ATTRIBUTE_BITFLAG) != 0) + { + return MZ_TRUE; + } + + return MZ_FALSE; +} + +static mz_bool mz_zip_file_stat_internal(mz_zip_archive *pZip, mz_uint file_index, const mz_uint8 *pCentral_dir_header, mz_zip_archive_file_stat *pStat, mz_bool *pFound_zip64_extra_data) +{ + mz_uint n; + const mz_uint8 *p = pCentral_dir_header; + + if (pFound_zip64_extra_data) + *pFound_zip64_extra_data = MZ_FALSE; + + if ((!p) || (!pStat)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + /* Extract fields from the central directory record. */ + pStat->m_file_index = file_index; + pStat->m_central_dir_ofs = MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, mz_uint32, file_index); + pStat->m_version_made_by = MZ_READ_LE16(p + MZ_ZIP_CDH_VERSION_MADE_BY_OFS); + pStat->m_version_needed = MZ_READ_LE16(p + MZ_ZIP_CDH_VERSION_NEEDED_OFS); + pStat->m_bit_flag = MZ_READ_LE16(p + MZ_ZIP_CDH_BIT_FLAG_OFS); + pStat->m_method = MZ_READ_LE16(p + MZ_ZIP_CDH_METHOD_OFS); +#ifndef MINIZ_NO_TIME + pStat->m_time = mz_zip_dos_to_time_t(MZ_READ_LE16(p + MZ_ZIP_CDH_FILE_TIME_OFS), MZ_READ_LE16(p + MZ_ZIP_CDH_FILE_DATE_OFS)); +#endif + pStat->m_crc32 = MZ_READ_LE32(p + MZ_ZIP_CDH_CRC32_OFS); + pStat->m_comp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS); + pStat->m_uncomp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS); + pStat->m_internal_attr = MZ_READ_LE16(p + MZ_ZIP_CDH_INTERNAL_ATTR_OFS); + pStat->m_external_attr = MZ_READ_LE32(p + MZ_ZIP_CDH_EXTERNAL_ATTR_OFS); + pStat->m_local_header_ofs = MZ_READ_LE32(p + MZ_ZIP_CDH_LOCAL_HEADER_OFS); + + /* Copy as much of the filename and comment as possible. */ + n = MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS); + n = MZ_MIN(n, MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE - 1); + memcpy(pStat->m_filename, p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE, n); + pStat->m_filename[n] = '\0'; + + n = MZ_READ_LE16(p + MZ_ZIP_CDH_COMMENT_LEN_OFS); + n = MZ_MIN(n, MZ_ZIP_MAX_ARCHIVE_FILE_COMMENT_SIZE - 1); + pStat->m_comment_size = n; + memcpy(pStat->m_comment, p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS) + MZ_READ_LE16(p + MZ_ZIP_CDH_EXTRA_LEN_OFS), n); + pStat->m_comment[n] = '\0'; + + /* Set some flags for convienance */ + pStat->m_is_directory = mz_zip_reader_is_file_a_directory(pZip, file_index); + pStat->m_is_encrypted = mz_zip_reader_is_file_encrypted(pZip, file_index); + pStat->m_is_supported = mz_zip_reader_is_file_supported(pZip, file_index); + + /* See if we need to read any zip64 extended information fields. */ + /* Confusingly, these zip64 fields can be present even on non-zip64 archives (Debian zip on a huge files from stdin piped to stdout creates them). */ + if (MZ_MAX(MZ_MAX(pStat->m_comp_size, pStat->m_uncomp_size), pStat->m_local_header_ofs) == MZ_UINT32_MAX) + { + /* Attempt to find zip64 extended information field in the entry's extra data */ + mz_uint32 extra_size_remaining = MZ_READ_LE16(p + MZ_ZIP_CDH_EXTRA_LEN_OFS); + + if (extra_size_remaining) { - float xv = x + ctx_cosf (angle) * radius; - float yv = y + ctx_sinf (angle) * radius; - if (first && !rasterizer->has_prev) - { ctx_rasterizer_move_to (rasterizer, xv, yv); } - else - { ctx_rasterizer_line_to (rasterizer, xv, yv); } - first = 0; + const mz_uint8 *pExtra_data = p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS); + + do + { + mz_uint32 field_id; + mz_uint32 field_data_size; + + if (extra_size_remaining < (sizeof(mz_uint16) * 2)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + field_id = MZ_READ_LE16(pExtra_data); + field_data_size = MZ_READ_LE16(pExtra_data + sizeof(mz_uint16)); + + if ((field_data_size + sizeof(mz_uint16) * 2) > extra_size_remaining) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + if (field_id == MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID) + { + const mz_uint8 *pField_data = pExtra_data + sizeof(mz_uint16) * 2; + mz_uint32 field_data_remaining = field_data_size; + + if (pFound_zip64_extra_data) + *pFound_zip64_extra_data = MZ_TRUE; + + if (pStat->m_uncomp_size == MZ_UINT32_MAX) + { + if (field_data_remaining < sizeof(mz_uint64)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + pStat->m_uncomp_size = MZ_READ_LE64(pField_data); + pField_data += sizeof(mz_uint64); + field_data_remaining -= sizeof(mz_uint64); + } + + if (pStat->m_comp_size == MZ_UINT32_MAX) + { + if (field_data_remaining < sizeof(mz_uint64)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + pStat->m_comp_size = MZ_READ_LE64(pField_data); + pField_data += sizeof(mz_uint64); + field_data_remaining -= sizeof(mz_uint64); + } + + if (pStat->m_local_header_ofs == MZ_UINT32_MAX) + { + if (field_data_remaining < sizeof(mz_uint64)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + pStat->m_local_header_ofs = MZ_READ_LE64(pField_data); + pField_data += sizeof(mz_uint64); + field_data_remaining -= sizeof(mz_uint64); + } + + break; + } + + pExtra_data += sizeof(mz_uint16) * 2 + field_data_size; + extra_size_remaining = extra_size_remaining - sizeof(mz_uint16) * 2 - field_data_size; + } while (extra_size_remaining); } } - ctx_rasterizer_line_to (rasterizer, x + ctx_cosf (end_angle) * radius, - y + ctx_sinf (end_angle) * radius); + + return MZ_TRUE; } -static void -ctx_rasterizer_quad_to (CtxRasterizer *rasterizer, - float cx, - float cy, - float x, - float y) +static MZ_FORCEINLINE mz_bool mz_zip_string_equal(const char *pA, const char *pB, mz_uint len, mz_uint flags) { - ctx_rasterizer_curve_to (rasterizer, - (cx * 2 + rasterizer->x) / 3.0f, (cy * 2 + rasterizer->y) / 3.0f, - (cx * 2 + x) / 3.0f, (cy * 2 + y) / 3.0f, - x, y); + mz_uint i; + if (flags & MZ_ZIP_FLAG_CASE_SENSITIVE) + return 0 == memcmp(pA, pB, len); + for (i = 0; i < len; ++i) + if (MZ_TOLOWER(pA[i]) != MZ_TOLOWER(pB[i])) + return MZ_FALSE; + return MZ_TRUE; } -static void -ctx_rasterizer_rel_quad_to (CtxRasterizer *rasterizer, - float cx, float cy, - float x, float y) +static MZ_FORCEINLINE int mz_zip_filename_compare(const mz_zip_array *pCentral_dir_array, const mz_zip_array *pCentral_dir_offsets, mz_uint l_index, const char *pR, mz_uint r_len) { - ctx_rasterizer_quad_to (rasterizer, cx + rasterizer->x, cy + rasterizer->y, - x + rasterizer->x, y + rasterizer->y); + const mz_uint8 *pL = &MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_array, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_offsets, mz_uint32, l_index)), *pE; + mz_uint l_len = MZ_READ_LE16(pL + MZ_ZIP_CDH_FILENAME_LEN_OFS); + mz_uint8 l = 0, r = 0; + pL += MZ_ZIP_CENTRAL_DIR_HEADER_SIZE; + pE = pL + MZ_MIN(l_len, r_len); + while (pL < pE) + { + if ((l = MZ_TOLOWER(*pL)) != (r = MZ_TOLOWER(*pR))) + break; + pL++; + pR++; + } + return (pL == pE) ? (int)(l_len - r_len) : (l - r); } -static void -ctx_rasterizer_rectangle_reverse (CtxRasterizer *rasterizer, - float x, - float y, - float width, - float height); +static mz_bool mz_zip_locate_file_binary_search(mz_zip_archive *pZip, const char *pFilename, mz_uint32 *pIndex) +{ + mz_zip_internal_state *pState = pZip->m_pState; + const mz_zip_array *pCentral_dir_offsets = &pState->m_central_dir_offsets; + const mz_zip_array *pCentral_dir = &pState->m_central_dir; + mz_uint32 *pIndices = &MZ_ZIP_ARRAY_ELEMENT(&pState->m_sorted_central_dir_offsets, mz_uint32, 0); + const mz_uint32 size = pZip->m_total_files; + const mz_uint filename_len = (mz_uint)strlen(pFilename); -static void -ctx_rasterizer_stroke (CtxRasterizer *rasterizer) + if (pIndex) + *pIndex = 0; + + if (size) + { + /* yes I could use uint32_t's, but then we would have to add some special case checks in the loop, argh, and */ + /* honestly the major expense here on 32-bit CPU's will still be the filename compare */ + mz_int64 l = 0, h = (mz_int64)size - 1; + + while (l <= h) + { + mz_int64 m = l + ((h - l) >> 1); + mz_uint32 file_index = pIndices[(mz_uint32)m]; + + int comp = mz_zip_filename_compare(pCentral_dir, pCentral_dir_offsets, file_index, pFilename, filename_len); + if (!comp) + { + if (pIndex) + *pIndex = file_index; + return MZ_TRUE; + } + else if (comp < 0) + l = m + 1; + else + h = m - 1; + } + } + + return mz_zip_set_error(pZip, MZ_ZIP_FILE_NOT_FOUND); +} + +int mz_zip_reader_locate_file(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags) { - CtxGState *gstate = &rasterizer->state->gstate; - CtxSource source_backup; - int count = rasterizer->edge_list.count; - if (count == 0) - return; - if (gstate->source_stroke.type != CTX_SOURCE_INHERIT_FILL) - { - source_backup = gstate->source_fill; - gstate->source_fill = rasterizer->state->gstate.source_stroke; - } - int preserved = rasterizer->preserve; - float factor = ctx_matrix_get_scale (&gstate->transform); - float line_width = gstate->line_width * factor; + mz_uint32 index; + if (!mz_zip_reader_locate_file_v2(pZip, pName, pComment, flags, &index)) + return -1; + else + return (int)index; +} - rasterizer->comp_op = NULL; - ctx_composite_setup (rasterizer); +mz_bool mz_zip_reader_locate_file_v2(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags, mz_uint32 *pIndex) +{ + mz_uint file_index; + size_t name_len, comment_len; - CtxSegment temp[count]; /* copy of already built up path's poly line */ - memcpy (temp, rasterizer->edge_list.entries, sizeof (temp) ); + if (pIndex) + *pIndex = 0; -#if CTX_FAST_FILL_RECT - if (rasterizer->edge_list.count == 5) + if ((!pZip) || (!pZip->m_pState) || (!pName)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + /* See if we can use a binary search */ + if (((pZip->m_pState->m_init_flags & MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY) == 0) && + (pZip->m_zip_mode == MZ_ZIP_MODE_READING) && + ((flags & (MZ_ZIP_FLAG_IGNORE_PATH | MZ_ZIP_FLAG_CASE_SENSITIVE)) == 0) && (!pComment) && (pZip->m_pState->m_sorted_central_dir_offsets.m_size)) { - CtxSegment *entry0 = &((CtxSegment*)rasterizer->edge_list.entries)[0]; - CtxSegment *entry1 = &((CtxSegment*)rasterizer->edge_list.entries)[1]; - CtxSegment *entry2 = &((CtxSegment*)rasterizer->edge_list.entries)[2]; - CtxSegment *entry3 = &((CtxSegment*)rasterizer->edge_list.entries)[3]; + return mz_zip_locate_file_binary_search(pZip, pName, pIndex); + } - if (!rasterizer->state->gstate.clipped && - (entry0->data.s16[2] == entry1->data.s16[2]) && - (entry0->data.s16[3] == entry3->data.s16[3]) && - (entry1->data.s16[3] == entry2->data.s16[3]) && - (entry2->data.s16[2] == entry3->data.s16[2]) -#if CTX_ENABLE_SHADOW_BLUR - && !rasterizer->in_shadow -#endif - ) - { + /* Locate the entry by scanning the entire central directory */ + name_len = strlen(pName); + if (name_len > MZ_UINT16_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); - 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; + comment_len = pComment ? strlen(pComment) : 0; + if (comment_len > MZ_UINT16_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); - ctx_composite_stroke_rect (rasterizer, x0, y0, x1, y1, line_width); + for (file_index = 0; file_index < pZip->m_total_files; file_index++) + { + const mz_uint8 *pHeader = &MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, mz_uint32, file_index)); + mz_uint filename_len = MZ_READ_LE16(pHeader + MZ_ZIP_CDH_FILENAME_LEN_OFS); + const char *pFilename = (const char *)pHeader + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE; + if (filename_len < name_len) + continue; + if (comment_len) + { + mz_uint file_extra_len = MZ_READ_LE16(pHeader + MZ_ZIP_CDH_EXTRA_LEN_OFS), file_comment_len = MZ_READ_LE16(pHeader + MZ_ZIP_CDH_COMMENT_LEN_OFS); + const char *pFile_comment = pFilename + filename_len + file_extra_len; + if ((file_comment_len != comment_len) || (!mz_zip_string_equal(pComment, pFile_comment, file_comment_len, flags))) + continue; + } + if ((flags & MZ_ZIP_FLAG_IGNORE_PATH) && (filename_len)) + { + int ofs = filename_len - 1; + do + { + if ((pFilename[ofs] == '/') || (pFilename[ofs] == '\\') || (pFilename[ofs] == ':')) + break; + } while (--ofs >= 0); + ofs++; + pFilename += ofs; + filename_len -= ofs; + } + if ((filename_len == name_len) && (mz_zip_string_equal(pName, pFilename, filename_len, flags))) + { + if (pIndex) + *pIndex = file_index; + return MZ_TRUE; + } + } - goto done; + return mz_zip_set_error(pZip, MZ_ZIP_FILE_NOT_FOUND); +} + +static +mz_bool mz_zip_reader_extract_to_mem_no_alloc1(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size, const mz_zip_archive_file_stat *st) +{ + int status = TINFL_STATUS_DONE; + mz_uint64 needed_size, cur_file_ofs, comp_remaining, out_buf_ofs = 0, read_buf_size, read_buf_ofs = 0, read_buf_avail; + mz_zip_archive_file_stat file_stat; + void *pRead_buf; + mz_uint32 local_header_u32[(MZ_ZIP_LOCAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; + mz_uint8 *pLocal_header = (mz_uint8 *)local_header_u32; + tinfl_decompressor inflator; + if ((!pZip) || (!pZip->m_pState) || ((buf_size) && (!pBuf)) || ((user_read_buf_size) && (!pUser_read_buf)) || (!pZip->m_pRead)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); - } - } + if (st) { + file_stat = *st; + } else + if (!mz_zip_reader_file_stat(pZip, file_index, &file_stat)) + return MZ_FALSE; + + /* A directory or zero length file */ + if ((file_stat.m_is_directory) || (!file_stat.m_comp_size)) + return MZ_TRUE; + + /* Encryption and patch files are not supported. */ + if (file_stat.m_bit_flag & (MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_IS_ENCRYPTED | MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_USES_STRONG_ENCRYPTION | MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_COMPRESSED_PATCH_FLAG)) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_ENCRYPTION); + + /* This function only supports decompressing stored and deflate. */ + if ((!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) && (file_stat.m_method != 0) && (file_stat.m_method != MZ_DEFLATED)) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_METHOD); + + /* Ensure supplied output buffer is large enough. */ + needed_size = (flags & MZ_ZIP_FLAG_COMPRESSED_DATA) ? file_stat.m_comp_size : file_stat.m_uncomp_size; + if (buf_size < needed_size) + return mz_zip_set_error(pZip, MZ_ZIP_BUF_TOO_SMALL); + + /* Read and parse the local directory entry. */ + cur_file_ofs = file_stat.m_local_header_ofs; + if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + + if (MZ_READ_LE32(pLocal_header) != MZ_ZIP_LOCAL_DIR_HEADER_SIG) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + cur_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_FILENAME_LEN_OFS) + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_EXTRA_LEN_OFS); + if ((cur_file_ofs + file_stat.m_comp_size) > pZip->m_archive_size) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + if ((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) || (!file_stat.m_method)) + { + /* The file is stored or the caller has requested the compressed data. */ + if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pBuf, (size_t)needed_size) != needed_size) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + +#ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS + if ((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) == 0) + { + if (mz_crc32(MZ_CRC32_INIT, (const mz_uint8 *)pBuf, (size_t)file_stat.m_uncomp_size) != file_stat.m_crc32) + return mz_zip_set_error(pZip, MZ_ZIP_CRC_CHECK_FAILED); + } #endif - + + return MZ_TRUE; + } + + /* Decompress the file either directly from memory or from a file input buffer. */ + tinfl_init(&inflator); + + if (pZip->m_pState->m_pMem) { + /* Read directly from the archive in memory. */ + pRead_buf = (mz_uint8 *)pZip->m_pState->m_pMem + cur_file_ofs; + read_buf_size = read_buf_avail = file_stat.m_comp_size; + comp_remaining = 0; + } + else if (pUser_read_buf) { - if (line_width < 5.0f) - { - factor *= 0.89f; /* this hack adjustment makes sharp 1px and 2px strokewidths - // end up sharp without erronious AA; we seem to be off by - // one somewhere else, causing the need for this - // */ - line_width *= 0.89f; - } - 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; - float half_width_x = line_width/2; - float half_width_y = half_width_x; + /* Use a user provided read buffer. */ + if (!user_read_buf_size) + return MZ_FALSE; + pRead_buf = (mz_uint8 *)pUser_read_buf; + read_buf_size = user_read_buf_size; + read_buf_avail = 0; + comp_remaining = file_stat.m_comp_size; + } + else + { + /* Temporarily allocate a read buffer. */ + read_buf_size = MZ_MIN(file_stat.m_comp_size, (mz_uint64)MZ_ZIP_MAX_IO_BUF_SIZE); + if (((sizeof(size_t) == sizeof(mz_uint32))) && (read_buf_size > 0x7FFFFFFF)) + return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); - if (CTX_UNLIKELY(line_width <= 0.0f)) - { // makes 0 width be hairline - half_width_x = .5f; - half_width_y = .5f; - } - int start = 0; - int end = 0; - while (start < count) + if (NULL == (pRead_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)read_buf_size))) + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + + read_buf_avail = 0; + comp_remaining = file_stat.m_comp_size; + } + + do + { + /* The size_t cast here should be OK because we've verified that the output buffer is >= file_stat.m_uncomp_size above */ + size_t in_buf_size, out_buf_size = (size_t)(file_stat.m_uncomp_size - out_buf_ofs); + if ((!read_buf_avail) && (!pZip->m_pState->m_pMem)) { - int started = 0; - int i; - for (i = start; i < count; i++) + read_buf_avail = MZ_MIN(read_buf_size, comp_remaining); + if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pRead_buf, (size_t)read_buf_avail) != read_buf_avail) { - CtxSegment *entry = &temp[i]; - float x, y; - if (entry->code == CTX_NEW_EDGE) - { - if (CTX_LIKELY(started)) - { - end = i - 1; - goto foo; - } - prev_x = entry->data.s16[0] / CTX_SUBDIV; - prev_y = entry->data.s16[1] / CTX_FULL_AA; - started = 1; - start = i; - } - x = entry->data.s16[2] / CTX_SUBDIV; - y = entry->data.s16[3] / 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 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)) - { - ctx_rasterizer_finish_shape (rasterizer); - ctx_rasterizer_move_to (rasterizer, prev_x+dy, prev_y-dx); - } - ctx_rasterizer_line_to (rasterizer, prev_x-dy, prev_y+dx); - - // we need to know the slope of the other side + status = TINFL_STATUS_FAILED; + mz_zip_set_error(pZip, MZ_ZIP_DECOMPRESSION_FAILED); + break; + } + cur_file_ofs += read_buf_avail; + comp_remaining -= read_buf_avail; + read_buf_ofs = 0; + } + in_buf_size = (size_t)read_buf_avail; + status = tinfl_decompress(&inflator, (mz_uint8 *)pRead_buf + read_buf_ofs, &in_buf_size, (mz_uint8 *)pBuf, (mz_uint8 *)pBuf + out_buf_ofs, &out_buf_size, TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF | (comp_remaining ? TINFL_FLAG_HAS_MORE_INPUT : 0)); + read_buf_avail -= in_buf_size; + read_buf_ofs += in_buf_size; + out_buf_ofs += out_buf_size; + } while (status == TINFL_STATUS_NEEDS_MORE_INPUT); + + if (status == TINFL_STATUS_DONE) + { + /* Make sure the entire file was decompressed, and check its CRC. */ + if (out_buf_ofs != file_stat.m_uncomp_size) + { + mz_zip_set_error(pZip, MZ_ZIP_UNEXPECTED_DECOMPRESSED_SIZE); + status = TINFL_STATUS_FAILED; + } +#ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS + else if (mz_crc32(MZ_CRC32_INIT, (const mz_uint8 *)pBuf, (size_t)file_stat.m_uncomp_size) != file_stat.m_crc32) + { + mz_zip_set_error(pZip, MZ_ZIP_CRC_CHECK_FAILED); + status = TINFL_STATUS_FAILED; + } +#endif + } + + if ((!pZip->m_pState->m_pMem) && (!pUser_read_buf)) + pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); + + return status == TINFL_STATUS_DONE; +} + +mz_bool mz_zip_reader_extract_to_mem_no_alloc(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size) +{ + return mz_zip_reader_extract_to_mem_no_alloc1(pZip, file_index, pBuf, buf_size, flags, pUser_read_buf, user_read_buf_size, NULL); +} + +mz_bool mz_zip_reader_extract_file_to_mem_no_alloc(mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size) +{ + mz_uint32 file_index; + if (!mz_zip_reader_locate_file_v2(pZip, pFilename, NULL, flags, &file_index)) + return MZ_FALSE; + return mz_zip_reader_extract_to_mem_no_alloc1(pZip, file_index, pBuf, buf_size, flags, pUser_read_buf, user_read_buf_size, NULL); +} + +mz_bool mz_zip_reader_extract_to_mem(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags) +{ + return mz_zip_reader_extract_to_mem_no_alloc1(pZip, file_index, pBuf, buf_size, flags, NULL, 0, NULL); +} + +mz_bool mz_zip_reader_extract_file_to_mem(mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags) +{ + return mz_zip_reader_extract_file_to_mem_no_alloc(pZip, pFilename, pBuf, buf_size, flags, NULL, 0); +} + +void *mz_zip_reader_extract_to_heap(mz_zip_archive *pZip, mz_uint file_index, size_t *pSize, mz_uint flags) +{ + mz_zip_archive_file_stat file_stat; + mz_uint64 alloc_size; + void *pBuf; + + if (pSize) + *pSize = 0; + + if (!mz_zip_reader_file_stat(pZip, file_index, &file_stat)) + return NULL; + + alloc_size = (flags & MZ_ZIP_FLAG_COMPRESSED_DATA) ? file_stat.m_comp_size : file_stat.m_uncomp_size; + if (((sizeof(size_t) == sizeof(mz_uint32))) && (alloc_size > 0x7FFFFFFF)) + { + mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); + return NULL; + } + + if (NULL == (pBuf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)alloc_size))) + { + mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + return NULL; + } + + if (!mz_zip_reader_extract_to_mem_no_alloc1(pZip, file_index, pBuf, (size_t)alloc_size, flags, NULL, 0, &file_stat)) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); + return NULL; + } + + if (pSize) + *pSize = (size_t)alloc_size; + return pBuf; +} + +void *mz_zip_reader_extract_file_to_heap(mz_zip_archive *pZip, const char *pFilename, size_t *pSize, mz_uint flags) +{ + mz_uint32 file_index; + if (!mz_zip_reader_locate_file_v2(pZip, pFilename, NULL, flags, &file_index)) + { + if (pSize) + *pSize = 0; + return MZ_FALSE; + } + return mz_zip_reader_extract_to_heap(pZip, file_index, pSize, flags); +} + +mz_bool mz_zip_reader_extract_to_callback(mz_zip_archive *pZip, mz_uint file_index, mz_file_write_func pCallback, void *pOpaque, mz_uint flags) +{ + int status = TINFL_STATUS_DONE; +#ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS + mz_uint file_crc32 = MZ_CRC32_INIT; +#endif + mz_uint64 read_buf_size, read_buf_ofs = 0, read_buf_avail, comp_remaining, out_buf_ofs = 0, cur_file_ofs; + mz_zip_archive_file_stat file_stat; + void *pRead_buf = NULL; + void *pWrite_buf = NULL; + mz_uint32 local_header_u32[(MZ_ZIP_LOCAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; + mz_uint8 *pLocal_header = (mz_uint8 *)local_header_u32; - // 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); + if ((!pZip) || (!pZip->m_pState) || (!pCallback) || (!pZip->m_pRead)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); - ctx_rasterizer_line_to (rasterizer, x-dy, y+dx); - } - prev_x = x; - prev_y = y; + if (!mz_zip_reader_file_stat(pZip, file_index, &file_stat)) + return MZ_FALSE; + + /* A directory or zero length file */ + if ((file_stat.m_is_directory) || (!file_stat.m_comp_size)) + return MZ_TRUE; + + /* Encryption and patch files are not supported. */ + if (file_stat.m_bit_flag & (MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_IS_ENCRYPTED | MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_USES_STRONG_ENCRYPTION | MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_COMPRESSED_PATCH_FLAG)) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_ENCRYPTION); + + /* This function only supports decompressing stored and deflate. */ + if ((!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) && (file_stat.m_method != 0) && (file_stat.m_method != MZ_DEFLATED)) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_METHOD); + + /* Read and do some minimal validation of the local directory entry (this doesn't crack the zip64 stuff, which we already have from the central dir) */ + cur_file_ofs = file_stat.m_local_header_ofs; + if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + + if (MZ_READ_LE32(pLocal_header) != MZ_ZIP_LOCAL_DIR_HEADER_SIG) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + cur_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_FILENAME_LEN_OFS) + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_EXTRA_LEN_OFS); + if ((cur_file_ofs + file_stat.m_comp_size) > pZip->m_archive_size) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + /* Decompress the file either directly from memory or from a file input buffer. */ + if (pZip->m_pState->m_pMem) + { + pRead_buf = (mz_uint8 *)pZip->m_pState->m_pMem + cur_file_ofs; + read_buf_size = read_buf_avail = file_stat.m_comp_size; + comp_remaining = 0; + } + else + { + read_buf_size = MZ_MIN(file_stat.m_comp_size, (mz_uint64)MZ_ZIP_MAX_IO_BUF_SIZE); + if (NULL == (pRead_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)read_buf_size))) + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + + read_buf_avail = 0; + comp_remaining = file_stat.m_comp_size; + } + + if ((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) || (!file_stat.m_method)) + { + /* The file is stored or the caller has requested the compressed data. */ + if (pZip->m_pState->m_pMem) + { + if (((sizeof(size_t) == sizeof(mz_uint32))) && (file_stat.m_comp_size > MZ_UINT32_MAX)) + return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); + + if (pCallback(pOpaque, out_buf_ofs, pRead_buf, (size_t)file_stat.m_comp_size) != file_stat.m_comp_size) + { + mz_zip_set_error(pZip, MZ_ZIP_WRITE_CALLBACK_FAILED); + status = TINFL_STATUS_FAILED; } - end = i-1; -foo: - for (int i = end; i >= start; i--) + else if (!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) { - CtxSegment *entry = &temp[i]; - float x, y, dx, dy; - x = entry->data.s16[2] * 1.0f / CTX_SUBDIV; - y = entry->data.s16[3] * 1.0f / 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)) +#ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS + file_crc32 = (mz_uint32)mz_crc32(file_crc32, (const mz_uint8 *)pRead_buf, (size_t)file_stat.m_comp_size); +#endif + } + + cur_file_ofs += file_stat.m_comp_size; + out_buf_ofs += file_stat.m_comp_size; + comp_remaining = 0; + } + else + { + while (comp_remaining) + { + read_buf_avail = MZ_MIN(read_buf_size, comp_remaining); + if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pRead_buf, (size_t)read_buf_avail) != read_buf_avail) { - 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); + mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + status = TINFL_STATUS_FAILED; + break; } - prev_x = x; - prev_y = y; - if (CTX_UNLIKELY(entry->code == CTX_NEW_EDGE)) + +#ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS + if (!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) { - x = entry->data.s16[0] * 1.0f / CTX_SUBDIV; - y = entry->data.s16[1] * 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)) - { - 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); - } + file_crc32 = (mz_uint32)mz_crc32(file_crc32, (const mz_uint8 *)pRead_buf, (size_t)read_buf_avail); } - if ( (prev_x != x) && (prev_y != y) ) +#endif + + if (pCallback(pOpaque, out_buf_ofs, pRead_buf, (size_t)read_buf_avail) != read_buf_avail) { - prev_x = x; - prev_y = y; + mz_zip_set_error(pZip, MZ_ZIP_WRITE_CALLBACK_FAILED); + status = TINFL_STATUS_FAILED; + break; } + + cur_file_ofs += read_buf_avail; + out_buf_ofs += read_buf_avail; + comp_remaining -= read_buf_avail; } - start = end+1; } - ctx_rasterizer_finish_shape (rasterizer); - switch (gstate->line_cap) + } + else + { + tinfl_decompressor inflator; + tinfl_init(&inflator); + + if (NULL == (pWrite_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, TINFL_LZ_DICT_SIZE))) { - case CTX_CAP_SQUARE: // XXX: incorrect - if rectangles were in - // reverse order - rotation would be off - // better implement correct here + mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + status = TINFL_STATUS_FAILED; + } + else + { + do { - float x = 0, y = 0; - int has_prev = 0; - for (int i = 0; i < count; i++) + mz_uint8 *pWrite_buf_cur = (mz_uint8 *)pWrite_buf + (out_buf_ofs & (TINFL_LZ_DICT_SIZE - 1)); + size_t in_buf_size, out_buf_size = TINFL_LZ_DICT_SIZE - (out_buf_ofs & (TINFL_LZ_DICT_SIZE - 1)); + if ((!read_buf_avail) && (!pZip->m_pState->m_pMem)) { - CtxSegment *entry = &temp[i]; - if (CTX_UNLIKELY(entry->code == CTX_NEW_EDGE)) + read_buf_avail = MZ_MIN(read_buf_size, comp_remaining); + if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pRead_buf, (size_t)read_buf_avail) != read_buf_avail) { - 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); - } - x = entry->data.s16[0] * 1.0f / CTX_SUBDIV; - y = entry->data.s16[1] * 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); + mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + status = TINFL_STATUS_FAILED; + break; } - x = entry->data.s16[2] * 1.0f / CTX_SUBDIV; - y = entry->data.s16[3] * 1.0f / CTX_FULL_AA; - has_prev = 1; + cur_file_ofs += read_buf_avail; + comp_remaining -= read_buf_avail; + read_buf_ofs = 0; } - 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); - } - break; - case CTX_CAP_NONE: /* nothing to do */ - break; - case CTX_CAP_ROUND: - { - float x = 0, y = 0; - int has_prev = 0; - for (int i = 0; i < count; i++) + + in_buf_size = (size_t)read_buf_avail; + status = tinfl_decompress(&inflator, (const mz_uint8 *)pRead_buf + read_buf_ofs, &in_buf_size, (mz_uint8 *)pWrite_buf, pWrite_buf_cur, &out_buf_size, comp_remaining ? TINFL_FLAG_HAS_MORE_INPUT : 0); + read_buf_avail -= in_buf_size; + read_buf_ofs += in_buf_size; + + if (out_buf_size) { - CtxSegment *entry = &temp[i]; - if (CTX_UNLIKELY(entry->code == CTX_NEW_EDGE)) + if (pCallback(pOpaque, out_buf_ofs, pWrite_buf_cur, out_buf_size) != out_buf_size) { - if (has_prev) - { - ctx_rasterizer_arc (rasterizer, x, y, half_width_x, CTX_PI*3, 0, 1); - ctx_rasterizer_finish_shape (rasterizer); - } - x = entry->data.s16[0] * 1.0f / CTX_SUBDIV; - y = entry->data.s16[1] * 1.0f / CTX_FULL_AA; - ctx_rasterizer_arc (rasterizer, x, y, half_width_x, CTX_PI*2, 0, 1); - ctx_rasterizer_finish_shape (rasterizer); + mz_zip_set_error(pZip, MZ_ZIP_WRITE_CALLBACK_FAILED); + status = TINFL_STATUS_FAILED; + break; + } + +#ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS + file_crc32 = (mz_uint32)mz_crc32(file_crc32, pWrite_buf_cur, out_buf_size); +#endif + if ((out_buf_ofs += out_buf_size) > file_stat.m_uncomp_size) + { + mz_zip_set_error(pZip, MZ_ZIP_DECOMPRESSION_FAILED); + status = TINFL_STATUS_FAILED; + break; } - x = entry->data.s16[2] * 1.0f / CTX_SUBDIV; - y = entry->data.s16[3] * 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); - break; + } while ((status == TINFL_STATUS_NEEDS_MORE_INPUT) || (status == TINFL_STATUS_HAS_MORE_OUTPUT)); + } + } + + if ((status == TINFL_STATUS_DONE) && (!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA))) + { + /* Make sure the entire file was decompressed, and check its CRC. */ + if (out_buf_ofs != file_stat.m_uncomp_size) + { + mz_zip_set_error(pZip, MZ_ZIP_UNEXPECTED_DECOMPRESSED_SIZE); + status = TINFL_STATUS_FAILED; + } +#ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS + else if (file_crc32 != file_stat.m_crc32) + { + mz_zip_set_error(pZip, MZ_ZIP_DECOMPRESSION_FAILED); + status = TINFL_STATUS_FAILED; + } +#endif + } + + if (!pZip->m_pState->m_pMem) + pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); + + if (pWrite_buf) + pZip->m_pFree(pZip->m_pAlloc_opaque, pWrite_buf); + + return status == TINFL_STATUS_DONE; +} + +mz_bool mz_zip_reader_extract_file_to_callback(mz_zip_archive *pZip, const char *pFilename, mz_file_write_func pCallback, void *pOpaque, mz_uint flags) +{ + mz_uint32 file_index; + if (!mz_zip_reader_locate_file_v2(pZip, pFilename, NULL, flags, &file_index)) + return MZ_FALSE; + + return mz_zip_reader_extract_to_callback(pZip, file_index, pCallback, pOpaque, flags); +} + +mz_zip_reader_extract_iter_state* mz_zip_reader_extract_iter_new(mz_zip_archive *pZip, mz_uint file_index, mz_uint flags) +{ + mz_zip_reader_extract_iter_state *pState; + mz_uint32 local_header_u32[(MZ_ZIP_LOCAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; + mz_uint8 *pLocal_header = (mz_uint8 *)local_header_u32; + + /* Argument sanity check */ + if ((!pZip) || (!pZip->m_pState)) + return NULL; + + /* Allocate an iterator status structure */ + pState = (mz_zip_reader_extract_iter_state*)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, sizeof(mz_zip_reader_extract_iter_state)); + if (!pState) + { + mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + return NULL; + } + + /* Fetch file details */ + if (!mz_zip_reader_file_stat(pZip, file_index, &pState->file_stat)) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pState); + return NULL; + } + + /* Encryption and patch files are not supported. */ + if (pState->file_stat.m_bit_flag & (MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_IS_ENCRYPTED | MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_USES_STRONG_ENCRYPTION | MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_COMPRESSED_PATCH_FLAG)) + { + mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_ENCRYPTION); + pZip->m_pFree(pZip->m_pAlloc_opaque, pState); + return NULL; + } + + /* This function only supports decompressing stored and deflate. */ + if ((!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) && (pState->file_stat.m_method != 0) && (pState->file_stat.m_method != MZ_DEFLATED)) + { + mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_METHOD); + pZip->m_pFree(pZip->m_pAlloc_opaque, pState); + return NULL; + } + + /* Init state - save args */ + pState->pZip = pZip; + pState->flags = flags; + + /* Init state - reset variables to defaults */ + pState->status = TINFL_STATUS_DONE; +#ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS + pState->file_crc32 = MZ_CRC32_INIT; +#endif + pState->read_buf_ofs = 0; + pState->out_buf_ofs = 0; + pState->pRead_buf = NULL; + pState->pWrite_buf = NULL; + pState->out_blk_remain = 0; + + /* Read and parse the local directory entry. */ + pState->cur_file_ofs = pState->file_stat.m_local_header_ofs; + if (pZip->m_pRead(pZip->m_pIO_opaque, pState->cur_file_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE) + { + mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + pZip->m_pFree(pZip->m_pAlloc_opaque, pState); + return NULL; + } + + if (MZ_READ_LE32(pLocal_header) != MZ_ZIP_LOCAL_DIR_HEADER_SIG) + { + mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + pZip->m_pFree(pZip->m_pAlloc_opaque, pState); + return NULL; + } + + pState->cur_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_FILENAME_LEN_OFS) + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_EXTRA_LEN_OFS); + if ((pState->cur_file_ofs + pState->file_stat.m_comp_size) > pZip->m_archive_size) + { + mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + pZip->m_pFree(pZip->m_pAlloc_opaque, pState); + return NULL; + } + + /* Decompress the file either directly from memory or from a file input buffer. */ + if (pZip->m_pState->m_pMem) + { + pState->pRead_buf = (mz_uint8 *)pZip->m_pState->m_pMem + pState->cur_file_ofs; + pState->read_buf_size = pState->read_buf_avail = pState->file_stat.m_comp_size; + pState->comp_remaining = pState->file_stat.m_comp_size; + } + else + { + if (!((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) || (!pState->file_stat.m_method))) + { + /* Decompression required, therefore intermediate read buffer required */ + pState->read_buf_size = MZ_MIN(pState->file_stat.m_comp_size, (mz_uint64)MZ_ZIP_MAX_IO_BUF_SIZE); + if (NULL == (pState->pRead_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)pState->read_buf_size))) + { + mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + pZip->m_pFree(pZip->m_pAlloc_opaque, pState); + return NULL; } } - switch (gstate->line_join) + else { - case CTX_JOIN_BEVEL: - case CTX_JOIN_MITER: - break; - case CTX_JOIN_ROUND: + /* Decompression not required - we will be reading directly into user buffer, no temp buf required */ + pState->read_buf_size = 0; + } + pState->read_buf_avail = 0; + pState->comp_remaining = pState->file_stat.m_comp_size; + } + + if (!((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) || (!pState->file_stat.m_method))) + { + /* Decompression required, init decompressor */ + tinfl_init( &pState->inflator ); + + /* Allocate write buffer */ + if (NULL == (pState->pWrite_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, TINFL_LZ_DICT_SIZE))) + { + mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + if (pState->pRead_buf) + pZip->m_pFree(pZip->m_pAlloc_opaque, pState->pRead_buf); + pZip->m_pFree(pZip->m_pAlloc_opaque, pState); + return NULL; + } + } + + return pState; +} + +mz_zip_reader_extract_iter_state* mz_zip_reader_extract_file_iter_new(mz_zip_archive *pZip, const char *pFilename, mz_uint flags) +{ + mz_uint32 file_index; + + /* Locate file index by name */ + if (!mz_zip_reader_locate_file_v2(pZip, pFilename, NULL, flags, &file_index)) + return NULL; + + /* Construct iterator */ + return mz_zip_reader_extract_iter_new(pZip, file_index, flags); +} + +size_t mz_zip_reader_extract_iter_read(mz_zip_reader_extract_iter_state* pState, void* pvBuf, size_t buf_size) +{ + size_t copied_to_caller = 0; + + /* Argument sanity check */ + if ((!pState) || (!pState->pZip) || (!pState->pZip->m_pState) || (!pvBuf)) + return 0; + + if ((pState->flags & MZ_ZIP_FLAG_COMPRESSED_DATA) || (!pState->file_stat.m_method)) + { + /* The file is stored or the caller has requested the compressed data, calc amount to return. */ + copied_to_caller = (size_t)MZ_MIN( buf_size, pState->comp_remaining ); + + /* Zip is in memory....or requires reading from a file? */ + if (pState->pZip->m_pState->m_pMem) + { + /* Copy data to caller's buffer */ + memcpy( pvBuf, pState->pRead_buf, copied_to_caller ); + pState->pRead_buf = ((mz_uint8*)pState->pRead_buf) + copied_to_caller; + } + else + { + /* Read directly into caller's buffer */ + if (pState->pZip->m_pRead(pState->pZip->m_pIO_opaque, pState->cur_file_ofs, pvBuf, copied_to_caller) != copied_to_caller) { - float x = 0, y = 0; - for (int i = 0; i < count-1; i++) + /* Failed to read all that was asked for, flag failure and alert user */ + mz_zip_set_error(pState->pZip, MZ_ZIP_FILE_READ_FAILED); + pState->status = TINFL_STATUS_FAILED; + copied_to_caller = 0; + } + } + +#ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS + /* Compute CRC if not returning compressed data only */ + if (!(pState->flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) + pState->file_crc32 = (mz_uint32)mz_crc32(pState->file_crc32, (const mz_uint8 *)pvBuf, copied_to_caller); +#endif + + /* Advance offsets, dec counters */ + pState->cur_file_ofs += copied_to_caller; + pState->out_buf_ofs += copied_to_caller; + pState->comp_remaining -= copied_to_caller; + } + else + { + do + { + /* Calc ptr to write buffer - given current output pos and block size */ + mz_uint8 *pWrite_buf_cur = (mz_uint8 *)pState->pWrite_buf + (pState->out_buf_ofs & (TINFL_LZ_DICT_SIZE - 1)); + + /* Calc max output size - given current output pos and block size */ + size_t in_buf_size, out_buf_size = TINFL_LZ_DICT_SIZE - (pState->out_buf_ofs & (TINFL_LZ_DICT_SIZE - 1)); + + if (!pState->out_blk_remain) + { + /* Read more data from file if none available (and reading from file) */ + if ((!pState->read_buf_avail) && (!pState->pZip->m_pState->m_pMem)) { - 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)) + /* Calc read size */ + pState->read_buf_avail = MZ_MIN(pState->read_buf_size, pState->comp_remaining); + if (pState->pZip->m_pRead(pState->pZip->m_pIO_opaque, pState->cur_file_ofs, pState->pRead_buf, (size_t)pState->read_buf_avail) != pState->read_buf_avail) { - ctx_rasterizer_arc (rasterizer, x, y, half_width_x, CTX_PI*2, 0, 1); - ctx_rasterizer_finish_shape (rasterizer); + mz_zip_set_error(pState->pZip, MZ_ZIP_FILE_READ_FAILED); + pState->status = TINFL_STATUS_FAILED; + break; } + + /* Advance offsets, dec counters */ + pState->cur_file_ofs += pState->read_buf_avail; + pState->comp_remaining -= pState->read_buf_avail; + pState->read_buf_ofs = 0; } - break; + + /* Perform decompression */ + in_buf_size = (size_t)pState->read_buf_avail; + pState->status = tinfl_decompress(&pState->inflator, (const mz_uint8 *)pState->pRead_buf + pState->read_buf_ofs, &in_buf_size, (mz_uint8 *)pState->pWrite_buf, pWrite_buf_cur, &out_buf_size, pState->comp_remaining ? TINFL_FLAG_HAS_MORE_INPUT : 0); + pState->read_buf_avail -= in_buf_size; + pState->read_buf_ofs += in_buf_size; + + /* Update current output block size remaining */ + pState->out_blk_remain = out_buf_size; } - } - CtxFillRule rule_backup = gstate->fill_rule; - gstate->fill_rule = CTX_FILL_RULE_WINDING; - rasterizer->preserve = 0; // so fill isn't tripped - ctx_rasterizer_fill (rasterizer); - gstate->fill_rule = rule_backup; - gstate->transform = transform_backup; - //gstate->transform_type = 0; - _ctx_transform_prime (rasterizer->state); - } - } -#if CTX_FAST_FILL_RECT -done: + + if (pState->out_blk_remain) + { + /* Calc amount to return. */ + size_t to_copy = MZ_MIN( (buf_size - copied_to_caller), pState->out_blk_remain ); + + /* Copy data to caller's buffer */ + memcpy( (mz_uint8*)pvBuf + copied_to_caller, pWrite_buf_cur, to_copy ); + +#ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS + /* Perform CRC */ + pState->file_crc32 = (mz_uint32)mz_crc32(pState->file_crc32, pWrite_buf_cur, to_copy); #endif - if (preserved) - { - memcpy (rasterizer->edge_list.entries, temp, sizeof (temp) ); - rasterizer->edge_list.count = count; - rasterizer->preserve = 0; + + /* Decrement data consumed from block */ + pState->out_blk_remain -= to_copy; + + /* Inc output offset, while performing sanity check */ + if ((pState->out_buf_ofs += to_copy) > pState->file_stat.m_uncomp_size) + { + mz_zip_set_error(pState->pZip, MZ_ZIP_DECOMPRESSION_FAILED); + pState->status = TINFL_STATUS_FAILED; + break; + } + + /* Increment counter of data copied to caller */ + copied_to_caller += to_copy; + } + } while ( (copied_to_caller < buf_size) && ((pState->status == TINFL_STATUS_NEEDS_MORE_INPUT) || (pState->status == TINFL_STATUS_HAS_MORE_OUTPUT)) ); } - if (gstate->source_stroke.type != CTX_SOURCE_INHERIT_FILL) - gstate->source_fill = source_backup; + + /* Return how many bytes were copied into user buffer */ + return copied_to_caller; } -#if CTX_1BIT_CLIP -#define CTX_CLIP_FORMAT CTX_FORMAT_GRAY1 -#else -#define CTX_CLIP_FORMAT CTX_FORMAT_GRAY8 +mz_bool mz_zip_reader_extract_iter_free(mz_zip_reader_extract_iter_state* pState) +{ + int status; + + /* Argument sanity check */ + if ((!pState) || (!pState->pZip) || (!pState->pZip->m_pState)) + return MZ_FALSE; + + /* Was decompression completed and requested? */ + if ((pState->status == TINFL_STATUS_DONE) && (!(pState->flags & MZ_ZIP_FLAG_COMPRESSED_DATA))) + { + /* Make sure the entire file was decompressed, and check its CRC. */ + if (pState->out_buf_ofs != pState->file_stat.m_uncomp_size) + { + mz_zip_set_error(pState->pZip, MZ_ZIP_UNEXPECTED_DECOMPRESSED_SIZE); + pState->status = TINFL_STATUS_FAILED; + } +#ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS + else if (pState->file_crc32 != pState->file_stat.m_crc32) + { + mz_zip_set_error(pState->pZip, MZ_ZIP_DECOMPRESSION_FAILED); + pState->status = TINFL_STATUS_FAILED; + } #endif + } + /* Free buffers */ + if (!pState->pZip->m_pState->m_pMem) + pState->pZip->m_pFree(pState->pZip->m_pAlloc_opaque, pState->pRead_buf); + if (pState->pWrite_buf) + pState->pZip->m_pFree(pState->pZip->m_pAlloc_opaque, pState->pWrite_buf); -static void -ctx_rasterizer_clip_reset (CtxRasterizer *rasterizer) + /* Save status */ + status = pState->status; + + /* Free context */ + pState->pZip->m_pFree(pState->pZip->m_pAlloc_opaque, pState); + + return status == TINFL_STATUS_DONE; +} + +#ifndef MINIZ_NO_STDIO +static size_t mz_zip_file_write_callback(void *pOpaque, mz_uint64 ofs, const void *pBuf, size_t n) { -#if CTX_ENABLE_CLIP - if (rasterizer->clip_buffer) - ctx_buffer_destroy (rasterizer->clip_buffer); - rasterizer->clip_buffer = NULL; + (void)ofs; + + return MZ_FWRITE(pBuf, 1, n, (MZ_FILE *)pOpaque); +} + +mz_bool mz_zip_reader_extract_to_file(mz_zip_archive *pZip, mz_uint file_index, const char *pDst_filename, mz_uint flags) +{ + mz_bool status; + mz_zip_archive_file_stat file_stat; + MZ_FILE *pFile; + + if (!mz_zip_reader_file_stat(pZip, file_index, &file_stat)) + return MZ_FALSE; + + if ((file_stat.m_is_directory) || (!file_stat.m_is_supported)) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_FEATURE); + + pFile = MZ_FOPEN(pDst_filename, "wb"); + if (!pFile) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_OPEN_FAILED); + + status = mz_zip_reader_extract_to_callback(pZip, file_index, mz_zip_file_write_callback, pFile, flags); + + if (MZ_FCLOSE(pFile) == EOF) + { + if (status) + mz_zip_set_error(pZip, MZ_ZIP_FILE_CLOSE_FAILED); + + status = MZ_FALSE; + } + +#if !defined(MINIZ_NO_TIME) && !defined(MINIZ_NO_STDIO) + if (status) + mz_zip_set_file_times(pDst_filename, file_stat.m_time, file_stat.m_time); #endif - rasterizer->state->gstate.clip_min_x = rasterizer->blit_x; - rasterizer->state->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; + return status; } -static void -ctx_rasterizer_clip_apply (CtxRasterizer *rasterizer, - CtxSegment *edges) +mz_bool mz_zip_reader_extract_file_to_file(mz_zip_archive *pZip, const char *pArchive_filename, const char *pDst_filename, mz_uint flags) { - unsigned int count = edges[0].data.u32[0]; + mz_uint32 file_index; + if (!mz_zip_reader_locate_file_v2(pZip, pArchive_filename, NULL, flags, &file_index)) + return MZ_FALSE; - int minx = 5000; - int miny = 5000; - int maxx = -5000; - int maxy = -5000; - int prev_x = 0; - int prev_y = 0; - int blit_width = rasterizer->blit_width; - int blit_height = rasterizer->blit_height; + return mz_zip_reader_extract_to_file(pZip, file_index, pDst_filename, flags); +} - float coords[6][2]; +mz_bool mz_zip_reader_extract_to_cfile(mz_zip_archive *pZip, mz_uint file_index, MZ_FILE *pFile, mz_uint flags) +{ + mz_zip_archive_file_stat file_stat; - for (unsigned int i = 0; i < count; i++) + if (!mz_zip_reader_file_stat(pZip, file_index, &file_stat)) + return MZ_FALSE; + + if ((file_stat.m_is_directory) || (!file_stat.m_is_supported)) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_FEATURE); + + return mz_zip_reader_extract_to_callback(pZip, file_index, mz_zip_file_write_callback, pFile, flags); +} + +mz_bool mz_zip_reader_extract_file_to_cfile(mz_zip_archive *pZip, const char *pArchive_filename, MZ_FILE *pFile, mz_uint flags) +{ + mz_uint32 file_index; + if (!mz_zip_reader_locate_file_v2(pZip, pArchive_filename, NULL, flags, &file_index)) + return MZ_FALSE; + + return mz_zip_reader_extract_to_cfile(pZip, file_index, pFile, flags); +} +#endif /* #ifndef MINIZ_NO_STDIO */ + +static size_t mz_zip_compute_crc32_callback(void *pOpaque, mz_uint64 file_ofs, const void *pBuf, size_t n) +{ + mz_uint32 *p = (mz_uint32 *)pOpaque; + (void)file_ofs; + *p = (mz_uint32)mz_crc32(*p, (const mz_uint8 *)pBuf, n); + return n; +} + +mz_bool mz_zip_validate_file(mz_zip_archive *pZip, mz_uint file_index, mz_uint flags) +{ + mz_zip_archive_file_stat file_stat; + mz_zip_internal_state *pState; + const mz_uint8 *pCentral_dir_header; + mz_bool found_zip64_ext_data_in_cdir = MZ_FALSE; + mz_bool found_zip64_ext_data_in_ldir = MZ_FALSE; + mz_uint32 local_header_u32[(MZ_ZIP_LOCAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; + mz_uint8 *pLocal_header = (mz_uint8 *)local_header_u32; + mz_uint64 local_header_ofs = 0; + mz_uint32 local_header_filename_len, local_header_extra_len, local_header_crc32; + mz_uint64 local_header_comp_size, local_header_uncomp_size; + mz_uint32 uncomp_crc32 = MZ_CRC32_INIT; + mz_bool has_data_descriptor; + mz_uint32 local_header_bit_flags; + + mz_zip_array file_data_array; + mz_zip_array_init(&file_data_array, 1); + + if ((!pZip) || (!pZip->m_pState) || (!pZip->m_pAlloc) || (!pZip->m_pFree) || (!pZip->m_pRead)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + if (file_index > pZip->m_total_files) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + pState = pZip->m_pState; + + pCentral_dir_header = mz_zip_get_cdh(pZip, file_index); + + if (!mz_zip_file_stat_internal(pZip, file_index, pCentral_dir_header, &file_stat, &found_zip64_ext_data_in_cdir)) + return MZ_FALSE; + + /* A directory or zero length file */ + if ((file_stat.m_is_directory) || (!file_stat.m_uncomp_size)) + return MZ_TRUE; + + /* Encryption and patch files are not supported. */ + if (file_stat.m_is_encrypted) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_ENCRYPTION); + + /* This function only supports stored and deflate. */ + if ((file_stat.m_method != 0) && (file_stat.m_method != MZ_DEFLATED)) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_METHOD); + + if (!file_stat.m_is_supported) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_FEATURE); + + /* Read and parse the local directory entry. */ + local_header_ofs = file_stat.m_local_header_ofs; + if (pZip->m_pRead(pZip->m_pIO_opaque, local_header_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + + if (MZ_READ_LE32(pLocal_header) != MZ_ZIP_LOCAL_DIR_HEADER_SIG) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + local_header_filename_len = MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_FILENAME_LEN_OFS); + local_header_extra_len = MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_EXTRA_LEN_OFS); + local_header_comp_size = MZ_READ_LE32(pLocal_header + MZ_ZIP_LDH_COMPRESSED_SIZE_OFS); + local_header_uncomp_size = MZ_READ_LE32(pLocal_header + MZ_ZIP_LDH_DECOMPRESSED_SIZE_OFS); + local_header_crc32 = MZ_READ_LE32(pLocal_header + MZ_ZIP_LDH_CRC32_OFS); + local_header_bit_flags = MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_BIT_FLAG_OFS); + has_data_descriptor = (local_header_bit_flags & 8) != 0; + + if (local_header_filename_len != strlen(file_stat.m_filename)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + if ((local_header_ofs + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + local_header_filename_len + local_header_extra_len + file_stat.m_comp_size) > pZip->m_archive_size) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + if (!mz_zip_array_resize(pZip, &file_data_array, MZ_MAX(local_header_filename_len, local_header_extra_len), MZ_FALSE)) { - CtxSegment *entry = &edges[i+1]; - float x, y; - if (entry->code == CTX_NEW_EDGE) + mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + goto handle_failure; + } + + if (local_header_filename_len) + { + if (pZip->m_pRead(pZip->m_pIO_opaque, local_header_ofs + MZ_ZIP_LOCAL_DIR_HEADER_SIZE, file_data_array.m_p, local_header_filename_len) != local_header_filename_len) { - prev_x = entry->data.s16[0] / CTX_SUBDIV; - prev_y = entry->data.s16[1] / 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; } + mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + goto handle_failure; } - x = entry->data.s16[2] * 1.0f / CTX_SUBDIV; - y = entry->data.s16[3] * 1.0f / CTX_FULL_AA; - if (x < minx) { minx = (int)x; } - if (y < miny) { miny = (int)y; } - if (x > maxx) { maxx = (int)x; } - if (y > maxy) { maxy = (int)y; } - if (i < 6) - { - coords[i][0] = x; - coords[i][1] = y; - } + /* I've seen 1 archive that had the same pathname, but used backslashes in the local dir and forward slashes in the central dir. Do we care about this? For now, this case will fail validation. */ + if (memcmp(file_stat.m_filename, file_data_array.m_p, local_header_filename_len) != 0) + { + mz_zip_set_error(pZip, MZ_ZIP_VALIDATION_FAILED); + goto handle_failure; + } } -#if CTX_ENABLE_CLIP - - if ((rasterizer->clip_rectangle==1 - || !rasterizer->clip_buffer) - ) - { - if (count == 5) + if ((local_header_extra_len) && ((local_header_comp_size == MZ_UINT32_MAX) || (local_header_uncomp_size == MZ_UINT32_MAX))) { - 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 0 - printf ("%d,%d %dx%d\n", minx, miny, - maxx-minx+1, maxy-miny+1); -#endif + mz_uint32 extra_size_remaining = local_header_extra_len; + const mz_uint8 *pExtra_data = (const mz_uint8 *)file_data_array.m_p; - 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); + if (pZip->m_pRead(pZip->m_pIO_opaque, local_header_ofs + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + local_header_filename_len, file_data_array.m_p, local_header_extra_len) != local_header_extra_len) + { + mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + goto handle_failure; + } - rasterizer->clip_rectangle = 1; + do + { + mz_uint32 field_id, field_data_size, field_total_size; -#if 0 - if (!rasterizer->clip_buffer) - rasterizer->clip_buffer = ctx_buffer_new (blit_width, - blit_height, - CTX_CLIP_FORMAT); + if (extra_size_remaining < (sizeof(mz_uint16) * 2)) + { + mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + goto handle_failure; + } - memset (rasterizer->clip_buffer->data, 0, blit_width * blit_height); - int i = 0; - for (int y = rasterizer->state->gstate.clip_min_y; - y <= rasterizer->state->gstate.clip_max_y; - y++) - for (int x = rasterizer->state->gstate.clip_min_x; - x <= rasterizer->state->gstate.clip_max_x; - x++, i++) - { - ((uint8_t*)(rasterizer->clip_buffer->data))[i] = 255; - } -#endif + field_id = MZ_READ_LE16(pExtra_data); + field_data_size = MZ_READ_LE16(pExtra_data + sizeof(mz_uint16)); + field_total_size = field_data_size + sizeof(mz_uint16) * 2; - return; - } -#if 0 - else - { - printf ("%d,%d %dx%d 0,0:%.2f 0,1:%.2f 1,0:%.2f 11:%.2f 20:%.2f 21:%2.f 30:%.2f 31:%.2f 40:%.2f 41:%.2f\n", minx, miny, - maxx-minx+1, maxy-miny+1 - - ,coords[0][0] , coords[0][1] - ,coords[1][0] , coords[1][1] - ,coords[2][0] , coords[2][1] - ,coords[3][0] , coords[3][1] - ,coords[4][0] , coords[4][1] - ); - } -#endif + if (field_total_size > extra_size_remaining) + { + mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + goto handle_failure; + } + + if (field_id == MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID) + { + const mz_uint8 *pSrc_field_data = pExtra_data + sizeof(mz_uint32); + + if (field_data_size < sizeof(mz_uint64) * 2) + { + mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + goto handle_failure; + } + + local_header_uncomp_size = MZ_READ_LE64(pSrc_field_data); + local_header_comp_size = MZ_READ_LE64(pSrc_field_data + sizeof(mz_uint64)); + + found_zip64_ext_data_in_ldir = MZ_TRUE; + break; + } + + pExtra_data += field_total_size; + extra_size_remaining -= field_total_size; + } while (extra_size_remaining); } - } - rasterizer->clip_rectangle = 0; - if ((minx == maxx) || (miny == maxy)) // XXX : reset hack - { - ctx_rasterizer_clip_reset (rasterizer); - return;//goto done; - } + /* TODO: parse local header extra data when local_header_comp_size is 0xFFFFFFFF! (big_descriptor.zip) */ + /* I've seen zips in the wild with the data descriptor bit set, but proper local header values and bogus data descriptors */ + if ((has_data_descriptor) && (!local_header_comp_size) && (!local_header_crc32)) + { + mz_uint8 descriptor_buf[32]; + mz_bool has_id; + const mz_uint8 *pSrc; + mz_uint32 file_crc32; + mz_uint64 comp_size = 0, uncomp_size = 0; - int we_made_it = 0; - CtxBuffer *clip_buffer; + mz_uint32 num_descriptor_uint32s = ((pState->m_zip64) || (found_zip64_ext_data_in_ldir)) ? 6 : 4; - if (!rasterizer->clip_buffer) - { - rasterizer->clip_buffer = ctx_buffer_new (blit_width, - blit_height, - CTX_CLIP_FORMAT); - clip_buffer = rasterizer->clip_buffer; - we_made_it = 1; - if (CTX_CLIP_FORMAT == CTX_FORMAT_GRAY1) - memset (rasterizer->clip_buffer->data, 0, blit_width * blit_height/8); - else - memset (rasterizer->clip_buffer->data, 0, blit_width * blit_height); - } - else - { - clip_buffer = ctx_buffer_new (blit_width, blit_height, - CTX_CLIP_FORMAT); - } + if (pZip->m_pRead(pZip->m_pIO_opaque, local_header_ofs + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + local_header_filename_len + local_header_extra_len + file_stat.m_comp_size, descriptor_buf, sizeof(mz_uint32) * num_descriptor_uint32s) != (sizeof(mz_uint32) * num_descriptor_uint32s)) + { + mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + goto handle_failure; + } - { + has_id = (MZ_READ_LE32(descriptor_buf) == MZ_ZIP_DATA_DESCRIPTOR_ID); + pSrc = has_id ? (descriptor_buf + sizeof(mz_uint32)) : descriptor_buf; - float prev_x = 0; - float prev_y = 0; + file_crc32 = MZ_READ_LE32(pSrc); - Ctx *ctx = ctx_new_for_framebuffer (clip_buffer->data, blit_width, blit_height, - blit_width, - CTX_CLIP_FORMAT); + if ((pState->m_zip64) || (found_zip64_ext_data_in_ldir)) + { + comp_size = MZ_READ_LE64(pSrc + sizeof(mz_uint32)); + uncomp_size = MZ_READ_LE64(pSrc + sizeof(mz_uint32) + sizeof(mz_uint64)); + } + else + { + comp_size = MZ_READ_LE32(pSrc + sizeof(mz_uint32)); + uncomp_size = MZ_READ_LE32(pSrc + sizeof(mz_uint32) + sizeof(mz_uint32)); + } - for (unsigned int i = 0; i < count; i++) + if ((file_crc32 != file_stat.m_crc32) || (comp_size != file_stat.m_comp_size) || (uncomp_size != file_stat.m_uncomp_size)) + { + mz_zip_set_error(pZip, MZ_ZIP_VALIDATION_FAILED); + goto handle_failure; + } + } + else { - CtxSegment *entry = &edges[i+1]; - float x, y; - if (entry->code == CTX_NEW_EDGE) + if ((local_header_crc32 != file_stat.m_crc32) || (local_header_comp_size != file_stat.m_comp_size) || (local_header_uncomp_size != file_stat.m_uncomp_size)) { - prev_x = entry->data.s16[0] * 1.0f / CTX_SUBDIV; - prev_y = entry->data.s16[1] * 1.0f / CTX_FULL_AA; - ctx_move_to (ctx, prev_x, prev_y); + mz_zip_set_error(pZip, MZ_ZIP_VALIDATION_FAILED); + goto handle_failure; } - x = entry->data.s16[2] * 1.0f / CTX_SUBDIV; - y = entry->data.s16[3] * 1.0f / CTX_FULL_AA; - ctx_line_to (ctx, x, y); } - ctx_gray (ctx, 1.0f); - ctx_fill (ctx); - ctx_destroy (ctx); - } - int maybe_rect = 1; - rasterizer->clip_rectangle = 0; + mz_zip_array_clear(pZip, &file_data_array); - if (CTX_CLIP_FORMAT == CTX_FORMAT_GRAY1) - { - unsigned int count = blit_width * blit_height / 8; - for (unsigned int i = 0; i < count; i++) + if ((flags & MZ_ZIP_FLAG_VALIDATE_HEADERS_ONLY) == 0) { - ((uint8_t*)rasterizer->clip_buffer->data)[i] = - (((uint8_t*)rasterizer->clip_buffer->data)[i] & - ((uint8_t*)clip_buffer->data)[i]); + if (!mz_zip_reader_extract_to_callback(pZip, file_index, mz_zip_compute_crc32_callback, &uncomp_crc32, 0)) + return MZ_FALSE; + + /* 1 more check to be sure, although the extract checks too. */ + if (uncomp_crc32 != file_stat.m_crc32) + { + mz_zip_set_error(pZip, MZ_ZIP_VALIDATION_FAILED); + return MZ_FALSE; + } } - } - else - { - int count = blit_width * blit_height; + return MZ_TRUE; - int i; - int x0 = 0; - int y0 = 0; - int width = -1; - int next_stage = 0; - uint8_t *p_data = (uint8_t*)rasterizer->clip_buffer->data; - uint8_t *data = (uint8_t*)clip_buffer->data; +handle_failure: + mz_zip_array_clear(pZip, &file_data_array); + return MZ_FALSE; +} - i=0; - /* find upper left */ - for (; i < count && maybe_rect && !next_stage; i++) +mz_bool mz_zip_validate_archive(mz_zip_archive *pZip, mz_uint flags) +{ + mz_zip_internal_state *pState; + mz_uint32 i; + + if ((!pZip) || (!pZip->m_pState) || (!pZip->m_pAlloc) || (!pZip->m_pFree) || (!pZip->m_pRead)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + pState = pZip->m_pState; + + /* Basic sanity checks */ + if (!pState->m_zip64) { - uint8_t val = (p_data[i] * data[i])/255; - data[i] = val; - switch (val) - { - case 255: - x0 = i % blit_width; - y0 = i / blit_width; - next_stage = 1; - break; - case 0: break; - default: - maybe_rect = 0; - break; - } - } + if (pZip->m_total_files > MZ_UINT16_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); - next_stage = 0; - /* figure out with */ - for (; i < count && !next_stage && maybe_rect; i++) + if (pZip->m_archive_size > MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); + } + else { - int x = i % blit_width; - int y = i / blit_width; - uint8_t val = (p_data[i] * data[i])/255; - data[i] = val; + if (pState->m_central_dir.m_size >= MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); + } - if (y == y0) - { - switch (val) + for (i = 0; i < pZip->m_total_files; i++) + { + if (MZ_ZIP_FLAG_VALIDATE_LOCATE_FILE_FLAG & flags) { - case 255: - width = x - x0 + 1; - break; - case 0: - next_stage = 1; - break; - default: - maybe_rect = 0; - break; + mz_uint32 found_index; + mz_zip_archive_file_stat stat; + + if (!mz_zip_reader_file_stat(pZip, i, &stat)) + return MZ_FALSE; + + if (!mz_zip_reader_locate_file_v2(pZip, stat.m_filename, NULL, 0, &found_index)) + return MZ_FALSE; + + /* This check can fail if there are duplicate filenames in the archive (which we don't check for when writing - that's up to the user) */ + if (found_index != i) + return mz_zip_set_error(pZip, MZ_ZIP_VALIDATION_FAILED); } - if (x % blit_width == blit_width - 1) next_stage = 1; - } - else next_stage = 1; + + if (!mz_zip_validate_file(pZip, i, flags)) + return MZ_FALSE; } - next_stage = 0; - /* body */ - for (; i < count && maybe_rect && !next_stage; i++) + return MZ_TRUE; +} + +mz_bool mz_zip_validate_mem_archive(const void *pMem, size_t size, mz_uint flags, mz_zip_error *pErr) +{ + mz_bool success = MZ_TRUE; + mz_zip_archive zip; + mz_zip_error actual_err = MZ_ZIP_NO_ERROR; + + if ((!pMem) || (!size)) { - int x = i % blit_width; - uint8_t val = (p_data[i] * data[i])/255; - data[i] = val; + if (pErr) + *pErr = MZ_ZIP_INVALID_PARAMETER; + return MZ_FALSE; + } - if (x < x0) - { - if (val != 0){ maybe_rect = 0; next_stage = 1; } - } else if (x < x0 + width) - { - if (val != 255){ if (val != 0) maybe_rect = 0; next_stage = 1; } - } else { - if (val != 0){ maybe_rect = 0; next_stage = 1; } - } + mz_zip_zero_struct(&zip); + + if (!mz_zip_reader_init_mem(&zip, pMem, size, flags)) + { + if (pErr) + *pErr = zip.m_last_error; + return MZ_FALSE; } - next_stage = 0; - /* foot */ - for (; i < count && maybe_rect && !next_stage; i++) + if (!mz_zip_validate_archive(&zip, flags)) { - uint8_t val = (p_data[i] * data[i])/255; - data[i] = val; + actual_err = zip.m_last_error; + success = MZ_FALSE; + } - if (val != 0){ maybe_rect = 0; next_stage = 1; } + if (!mz_zip_reader_end_internal(&zip, success)) + { + if (!actual_err) + actual_err = zip.m_last_error; + success = MZ_FALSE; } + if (pErr) + *pErr = actual_err; - for (; i < count; i++) + return success; +} + +#ifndef MINIZ_NO_STDIO +mz_bool mz_zip_validate_file_archive(const char *pFilename, mz_uint flags, mz_zip_error *pErr) +{ + mz_bool success = MZ_TRUE; + mz_zip_archive zip; + mz_zip_error actual_err = MZ_ZIP_NO_ERROR; + + if (!pFilename) { - uint8_t val = (p_data[i] * data[i])/255; - data[i] = val; + if (pErr) + *pErr = MZ_ZIP_INVALID_PARAMETER; + return MZ_FALSE; } - if (maybe_rect) - rasterizer->clip_rectangle = 1; - } - if (!we_made_it) - ctx_buffer_destroy (clip_buffer); -#else - if (coords[0][0]){}; -#endif - - rasterizer->state->gstate.clip_min_x = ctx_maxi (minx, - rasterizer->state->gstate.clip_min_x); - rasterizer->state->gstate.clip_min_y = ctx_maxi (miny, - rasterizer->state->gstate.clip_min_y); - rasterizer->state->gstate.clip_max_x = ctx_mini (maxx, - rasterizer->state->gstate.clip_max_x); - rasterizer->state->gstate.clip_max_y = ctx_mini (maxy, - rasterizer->state->gstate.clip_max_y); -} + mz_zip_zero_struct(&zip); + if (!mz_zip_reader_init_file_v2(&zip, pFilename, flags, 0, 0)) + { + if (pErr) + *pErr = zip.m_last_error; + return MZ_FALSE; + } -static void -ctx_rasterizer_clip (CtxRasterizer *rasterizer) -{ - int count = rasterizer->edge_list.count; - CtxSegment temp[count+1]; /* copy of already built up path's poly line */ - rasterizer->state->has_clipped=1; - rasterizer->state->gstate.clipped=1; - //if (rasterizer->preserve) - { memcpy (temp + 1, rasterizer->edge_list.entries, sizeof (temp) - sizeof (temp[0])); - temp[0].code = CTX_NOP; - temp[0].data.u32[0] = count; - ctx_state_set_blob (rasterizer->state, CTX_clip, (uint8_t*)temp, sizeof(temp)); + if (!mz_zip_validate_archive(&zip, flags)) + { + actual_err = zip.m_last_error; + success = MZ_FALSE; } - ctx_rasterizer_clip_apply (rasterizer, temp); - ctx_rasterizer_reset (rasterizer); - if (rasterizer->preserve) + + if (!mz_zip_reader_end_internal(&zip, success)) { - memcpy (rasterizer->edge_list.entries, temp + 1, sizeof (temp) - sizeof(temp[0])); - rasterizer->edge_list.count = count; - rasterizer->preserve = 0; + if (!actual_err) + actual_err = zip.m_last_error; + success = MZ_FALSE; } + + if (pErr) + *pErr = actual_err; + + return success; } +#endif /* #ifndef MINIZ_NO_STDIO */ +/* ------------------- .ZIP archive writing */ -#if 0 -static void -ctx_rasterizer_load_image (CtxRasterizer *rasterizer, - const char *path, - float x, - float y) +#ifndef MINIZ_NO_ARCHIVE_WRITING_APIS + +static MZ_FORCEINLINE void mz_write_le16(mz_uint8 *p, mz_uint16 v) { - // decode PNG, put it in image is slot 1, - // magic width height stride format data - ctx_buffer_load_png (&rasterizer->backend.ctx->texture[0], path); - ctx_rasterizer_set_texture (rasterizer, 0, x, y); + p[0] = (mz_uint8)v; + p[1] = (mz_uint8)(v >> 8); +} +static MZ_FORCEINLINE void mz_write_le32(mz_uint8 *p, mz_uint32 v) +{ + p[0] = (mz_uint8)v; + p[1] = (mz_uint8)(v >> 8); + p[2] = (mz_uint8)(v >> 16); + p[3] = (mz_uint8)(v >> 24); +} +static MZ_FORCEINLINE void mz_write_le64(mz_uint8 *p, mz_uint64 v) +{ + mz_write_le32(p, (mz_uint32)v); + mz_write_le32(p + sizeof(mz_uint32), (mz_uint32)(v >> 32)); } -#endif -static void -ctx_rasterizer_rectangle_reverse (CtxRasterizer *rasterizer, - float x, - float y, - float width, - float height) +#define MZ_WRITE_LE16(p, v) mz_write_le16((mz_uint8 *)(p), (mz_uint16)(v)) +#define MZ_WRITE_LE32(p, v) mz_write_le32((mz_uint8 *)(p), (mz_uint32)(v)) +#define MZ_WRITE_LE64(p, v) mz_write_le64((mz_uint8 *)(p), (mz_uint64)(v)) + +static size_t mz_zip_heap_write_func(void *pOpaque, mz_uint64 file_ofs, const void *pBuf, size_t n) { - ctx_rasterizer_move_to (rasterizer, x, y); - 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, 0); - //ctx_rasterizer_rel_line_to (rasterizer, width/2, 0); - ctx_rasterizer_finish_shape (rasterizer); + mz_zip_archive *pZip = (mz_zip_archive *)pOpaque; + mz_zip_internal_state *pState = pZip->m_pState; + mz_uint64 new_size = MZ_MAX(file_ofs + n, pState->m_mem_size); + + if (!n) + return 0; + + /* An allocation this big is likely to just fail on 32-bit systems, so don't even go there. */ + if ((sizeof(size_t) == sizeof(mz_uint32)) && (new_size > 0x7FFFFFFF)) + { + mz_zip_set_error(pZip, MZ_ZIP_FILE_TOO_LARGE); + return 0; + } + + if (new_size > pState->m_mem_capacity) + { + void *pNew_block; + size_t new_capacity = MZ_MAX(64, pState->m_mem_capacity); + + while (new_capacity < new_size) + new_capacity *= 2; + + if (NULL == (pNew_block = pZip->m_pRealloc(pZip->m_pAlloc_opaque, pState->m_pMem, 1, new_capacity))) + { + mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + return 0; + } + + pState->m_pMem = pNew_block; + pState->m_mem_capacity = new_capacity; + } + memcpy((mz_uint8 *)pState->m_pMem + file_ofs, pBuf, n); + pState->m_mem_size = (size_t)new_size; + return n; } -static void -ctx_rasterizer_rectangle (CtxRasterizer *rasterizer, - float x, - float y, - float width, - float height) +static mz_bool mz_zip_writer_end_internal(mz_zip_archive *pZip, mz_bool set_last_error) { - ctx_rasterizer_move_to (rasterizer, x, y); - ctx_rasterizer_rel_line_to (rasterizer, width, 0); - ctx_rasterizer_rel_line_to (rasterizer, 0, height); - ctx_rasterizer_rel_line_to (rasterizer, -width, 0); - ctx_rasterizer_rel_line_to (rasterizer, 0, -height); - //ctx_rasterizer_rel_line_to (rasterizer, width/2, 0); - ctx_rasterizer_finish_shape (rasterizer); + mz_zip_internal_state *pState; + mz_bool status = MZ_TRUE; + + if ((!pZip) || (!pZip->m_pState) || (!pZip->m_pAlloc) || (!pZip->m_pFree) || ((pZip->m_zip_mode != MZ_ZIP_MODE_WRITING) && (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED))) + { + if (set_last_error) + mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + return MZ_FALSE; + } + + pState = pZip->m_pState; + pZip->m_pState = NULL; + mz_zip_array_clear(pZip, &pState->m_central_dir); + mz_zip_array_clear(pZip, &pState->m_central_dir_offsets); + mz_zip_array_clear(pZip, &pState->m_sorted_central_dir_offsets); + +#ifndef MINIZ_NO_STDIO + if (pState->m_pFile) + { + if (pZip->m_zip_type == MZ_ZIP_TYPE_FILE) + { + if (MZ_FCLOSE(pState->m_pFile) == EOF) + { + if (set_last_error) + mz_zip_set_error(pZip, MZ_ZIP_FILE_CLOSE_FAILED); + status = MZ_FALSE; + } + } + + pState->m_pFile = NULL; + } +#endif /* #ifndef MINIZ_NO_STDIO */ + + if ((pZip->m_pWrite == mz_zip_heap_write_func) && (pState->m_pMem)) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pState->m_pMem); + pState->m_pMem = NULL; + } + + pZip->m_pFree(pZip->m_pAlloc_opaque, pState); + pZip->m_zip_mode = MZ_ZIP_MODE_INVALID; + return status; } -static void -ctx_rasterizer_set_pixel (CtxRasterizer *rasterizer, - uint16_t x, - uint16_t y, - uint8_t r, - uint8_t g, - uint8_t b, - uint8_t a) +mz_bool mz_zip_writer_init_v2(mz_zip_archive *pZip, mz_uint64 existing_size, mz_uint flags) { - rasterizer->state->gstate.source_fill.type = CTX_SOURCE_COLOR; - ctx_color_set_RGBA8 (rasterizer->state, &rasterizer->state->gstate.source_fill.color, r, g, b, a); - rasterizer->comp_op = NULL; -#if 0 - // XXX : doesn't take transforms into account - and has - // received less testing than code paths part of protocol, - // using rectangle properly will trigger the fillrect fastpath - ctx_rasterizer_pset (rasterizer, x, y, 255); -#else - ctx_rasterizer_rectangle (rasterizer, x, y, 1.0, 1.0); - ctx_rasterizer_fill (rasterizer); -#endif + mz_bool zip64 = (flags & MZ_ZIP_FLAG_WRITE_ZIP64) != 0; + + if ((!pZip) || (pZip->m_pState) || (!pZip->m_pWrite) || (pZip->m_zip_mode != MZ_ZIP_MODE_INVALID)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + if (flags & MZ_ZIP_FLAG_WRITE_ALLOW_READING) + { + if (!pZip->m_pRead) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + } + + if (pZip->m_file_offset_alignment) + { + /* Ensure user specified file offset alignment is a power of 2. */ + if (pZip->m_file_offset_alignment & (pZip->m_file_offset_alignment - 1)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + } + + if (!pZip->m_pAlloc) + pZip->m_pAlloc = miniz_def_alloc_func; + if (!pZip->m_pFree) + pZip->m_pFree = miniz_def_free_func; + if (!pZip->m_pRealloc) + pZip->m_pRealloc = miniz_def_realloc_func; + + pZip->m_archive_size = existing_size; + pZip->m_central_directory_file_ofs = 0; + pZip->m_total_files = 0; + + if (NULL == (pZip->m_pState = (mz_zip_internal_state *)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, sizeof(mz_zip_internal_state)))) + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + + memset(pZip->m_pState, 0, sizeof(mz_zip_internal_state)); + + MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir, sizeof(mz_uint8)); + MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir_offsets, sizeof(mz_uint32)); + MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_sorted_central_dir_offsets, sizeof(mz_uint32)); + + pZip->m_pState->m_zip64 = zip64; + pZip->m_pState->m_zip64_has_extended_info_fields = zip64; + + pZip->m_zip_type = MZ_ZIP_TYPE_USER; + pZip->m_zip_mode = MZ_ZIP_MODE_WRITING; + + return MZ_TRUE; } -#if CTX_ENABLE_SHADOW_BLUR -static inline float -ctx_gaussian (float x, float mu, float sigma) +mz_bool mz_zip_writer_init(mz_zip_archive *pZip, mz_uint64 existing_size) { - float a = ( x- mu) / sigma; - return ctx_expf (-0.5f * a * a); + return mz_zip_writer_init_v2(pZip, existing_size, 0); } -static inline void -ctx_compute_gaussian_kernel (int dim, float radius, float *kernel) +mz_bool mz_zip_writer_init_heap_v2(mz_zip_archive *pZip, size_t size_to_reserve_at_beginning, size_t initial_allocation_size, mz_uint flags) { - 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++) + pZip->m_pWrite = mz_zip_heap_write_func; + pZip->m_pNeeds_keepalive = NULL; + + if (flags & MZ_ZIP_FLAG_WRITE_ALLOW_READING) + pZip->m_pRead = mz_zip_mem_read_func; + + pZip->m_pIO_opaque = pZip; + + if (!mz_zip_writer_init_v2(pZip, size_to_reserve_at_beginning, flags)) + return MZ_FALSE; + + pZip->m_zip_type = MZ_ZIP_TYPE_HEAP; + + if (0 != (initial_allocation_size = MZ_MAX(initial_allocation_size, size_to_reserve_at_beginning))) { - float val = //ctx_gaussian (row, radius, sigma) * - ctx_gaussian (col, radius, sigma); - kernel[i] = val; - sum += val; + if (NULL == (pZip->m_pState->m_pMem = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, initial_allocation_size))) + { + mz_zip_writer_end_internal(pZip, MZ_FALSE); + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + } + pZip->m_pState->m_mem_capacity = initial_allocation_size; } - i = 0; - //for (int row = 0; row < dim; row ++) - for (int col = 0; col < dim; col ++, i++) - kernel[i] /= sum; + + return MZ_TRUE; } -#endif -static void -ctx_rasterizer_round_rectangle (CtxRasterizer *rasterizer, float x, float y, float width, float height, float corner_radius) +mz_bool mz_zip_writer_init_heap(mz_zip_archive *pZip, size_t size_to_reserve_at_beginning, size_t initial_allocation_size) { - float aspect = 1.0f; - float radius = corner_radius / aspect; - float degrees = CTX_PI / 180.0f; + return mz_zip_writer_init_heap_v2(pZip, size_to_reserve_at_beginning, initial_allocation_size, 0); +} - if (radius > width*0.5f) radius = width/2; - if (radius > height*0.5f) radius = height/2; +#ifndef MINIZ_NO_STDIO +static size_t mz_zip_file_write_func(void *pOpaque, mz_uint64 file_ofs, const void *pBuf, size_t n) +{ + mz_zip_archive *pZip = (mz_zip_archive *)pOpaque; + mz_int64 cur_ofs = MZ_FTELL64(pZip->m_pState->m_pFile); - ctx_rasterizer_finish_shape (rasterizer); - ctx_rasterizer_arc (rasterizer, x + width - radius, y + radius, radius, -90 * degrees, 0 * degrees, 0); - ctx_rasterizer_arc (rasterizer, x + width - radius, y + height - radius, radius, 0 * degrees, 90 * degrees, 0); - ctx_rasterizer_arc (rasterizer, x + radius, y + height - radius, radius, 90 * degrees, 180 * degrees, 0); - ctx_rasterizer_arc (rasterizer, x + radius, y + radius, radius, 180 * degrees, 270 * degrees, 0); + file_ofs += pZip->m_pState->m_file_archive_start_ofs; - ctx_rasterizer_finish_shape (rasterizer); + if (((mz_int64)file_ofs < 0) || (((cur_ofs != (mz_int64)file_ofs)) && (MZ_FSEEK64(pZip->m_pState->m_pFile, (mz_int64)file_ofs, SEEK_SET)))) + { + mz_zip_set_error(pZip, MZ_ZIP_FILE_SEEK_FAILED); + return 0; + } + + return MZ_FWRITE(pBuf, 1, n, pZip->m_pState->m_pFile); } -static void -ctx_rasterizer_process (Ctx *ctx, CtxCommand *command); +mz_bool mz_zip_writer_init_file(mz_zip_archive *pZip, const char *pFilename, mz_uint64 size_to_reserve_at_beginning) +{ + return mz_zip_writer_init_file_v2(pZip, pFilename, size_to_reserve_at_beginning, 0); +} -#if CTX_COMPOSITING_GROUPS -static void -ctx_rasterizer_start_group (CtxRasterizer *rasterizer) /* add a radius? */ +mz_bool mz_zip_writer_init_file_v2(mz_zip_archive *pZip, const char *pFilename, mz_uint64 size_to_reserve_at_beginning, mz_uint flags) { - CtxEntry save_command = ctx_void(CTX_SAVE); - // allocate buffer, and set it as temporary target - int no; - if (rasterizer->group[0] == NULL) // first group - { - rasterizer->saved_buf = rasterizer->buf; - } - for (no = 0; rasterizer->group[no] && no < CTX_GROUP_MAX; no++); + MZ_FILE *pFile; - if (no >= CTX_GROUP_MAX) - return; - rasterizer->group[no] = ctx_buffer_new (rasterizer->blit_width, - rasterizer->blit_height, - rasterizer->format->composite_format); - rasterizer->buf = rasterizer->group[no]->data; - ctx_rasterizer_process (rasterizer->backend.ctx, (CtxCommand*)&save_command); + pZip->m_pWrite = mz_zip_file_write_func; + pZip->m_pNeeds_keepalive = NULL; + + if (flags & MZ_ZIP_FLAG_WRITE_ALLOW_READING) + pZip->m_pRead = mz_zip_file_read_func; + + pZip->m_pIO_opaque = pZip; + + if (!mz_zip_writer_init_v2(pZip, size_to_reserve_at_beginning, flags)) + return MZ_FALSE; + + if (NULL == (pFile = MZ_FOPEN(pFilename, (flags & MZ_ZIP_FLAG_WRITE_ALLOW_READING) ? "w+b" : "wb"))) + { + mz_zip_writer_end(pZip); + return mz_zip_set_error(pZip, MZ_ZIP_FILE_OPEN_FAILED); + } + + pZip->m_pState->m_pFile = pFile; + pZip->m_zip_type = MZ_ZIP_TYPE_FILE; + + if (size_to_reserve_at_beginning) + { + mz_uint64 cur_ofs = 0; + char buf[4096]; + + MZ_CLEAR_ARR(buf); + + do + { + size_t n = (size_t)MZ_MIN(sizeof(buf), size_to_reserve_at_beginning); + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_ofs, buf, n) != n) + { + mz_zip_writer_end(pZip); + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + } + cur_ofs += n; + size_to_reserve_at_beginning -= n; + } while (size_to_reserve_at_beginning); + } + + return MZ_TRUE; } -static void -ctx_rasterizer_end_group (CtxRasterizer *rasterizer) +mz_bool mz_zip_writer_init_cfile(mz_zip_archive *pZip, MZ_FILE *pFile, mz_uint flags) { - CtxEntry restore_command = ctx_void(CTX_RESTORE); - CtxEntry save_command = ctx_void(CTX_SAVE); - int no = 0; - for (no = 0; rasterizer->group[no] && no < CTX_GROUP_MAX; no++); - no--; + pZip->m_pWrite = mz_zip_file_write_func; + pZip->m_pNeeds_keepalive = NULL; - if (no < 0) - return; + if (flags & MZ_ZIP_FLAG_WRITE_ALLOW_READING) + pZip->m_pRead = mz_zip_file_read_func; - Ctx *ctx = rasterizer->backend.ctx; + pZip->m_pIO_opaque = pZip; - 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; - // fetch compositing, blending, global alpha - ctx_rasterizer_process (ctx, (CtxCommand*)&restore_command); - ctx_rasterizer_process (ctx, (CtxCommand*)&save_command); - CtxEntry set_state[4]= - { - ctx_u32 (CTX_COMPOSITING_MODE, comp, 0), - ctx_u32 (CTX_BLEND_MODE, blend, 0), - ctx_u32 (CTX_EXTEND, extend, 0), - ctx_f (CTX_GLOBAL_ALPHA, global_alpha, 0.0) - }; - ctx_rasterizer_process (ctx, (CtxCommand*)&set_state[0]); - ctx_rasterizer_process (ctx, (CtxCommand*)&set_state[1]); - ctx_rasterizer_process (ctx, (CtxCommand*)&set_state[2]); - ctx_rasterizer_process (ctx, (CtxCommand*)&set_state[3]); - if (no == 0) - { - rasterizer->buf = rasterizer->saved_buf; - } - else - { - rasterizer->buf = rasterizer->group[no-1]->data; - } - // XXX use texture_source ? - ctx_texture_init (ctx, ".ctx-group", - rasterizer->blit_width, - rasterizer->blit_height, - - rasterizer->blit_width * rasterizer->format->bpp/8, - rasterizer->format->pixel_format, - NULL, // space - (uint8_t*)rasterizer->group[no]->data, - NULL, NULL); - { - const char *eid = ".ctx-group"; - int eid_len = ctx_strlen (eid); + if (!mz_zip_writer_init_v2(pZip, 0, flags)) + return MZ_FALSE; - CtxEntry commands[4] = - { - ctx_f (CTX_TEXTURE, rasterizer->blit_x, rasterizer->blit_y), - ctx_u32 (CTX_DATA, eid_len, eid_len/9+1), - ctx_u32 (CTX_CONT, 0,0), - ctx_u32 (CTX_CONT, 0,0) - }; - memcpy( (char *) &commands[2].data.u8[0], eid, eid_len); - ( (char *) (&commands[2].data.u8[0]) ) [eid_len]=0; + pZip->m_pState->m_pFile = pFile; + pZip->m_pState->m_file_archive_start_ofs = MZ_FTELL64(pZip->m_pState->m_pFile); + pZip->m_zip_type = MZ_ZIP_TYPE_CFILE; - ctx_rasterizer_process (ctx, (CtxCommand*)commands); - } - { - CtxEntry commands[2]= - { - ctx_f (CTX_RECTANGLE, rasterizer->blit_x, rasterizer->blit_y), - ctx_f (CTX_CONT, rasterizer->blit_width, rasterizer->blit_height) - }; - ctx_rasterizer_process (ctx, (CtxCommand*)commands); - } - { - CtxEntry commands[1] = { ctx_void (CTX_FILL) }; - ctx_rasterizer_process (ctx, (CtxCommand*)commands); - } - //ctx_texture_release (rasterizer->backend.ctx, ".ctx-group"); - ctx_buffer_destroy (rasterizer->group[no]); - rasterizer->group[no] = 0; - ctx_rasterizer_process (ctx, (CtxCommand*)&restore_command); + return MZ_TRUE; } -#endif +#endif /* #ifndef MINIZ_NO_STDIO */ -#if CTX_ENABLE_SHADOW_BLUR -static void -ctx_rasterizer_shadow_stroke (CtxRasterizer *rasterizer) +mz_bool mz_zip_writer_init_from_reader_v2(mz_zip_archive *pZip, const char *pFilename, mz_uint flags) { - CtxColor color; - CtxEntry save_command = ctx_void(CTX_SAVE); - Ctx *ctx = rasterizer->backend.ctx; + mz_zip_internal_state *pState; - float rgba[4] = {0, 0, 0, 1.0}; - if (ctx_get_color (rasterizer->backend.ctx, CTX_shadowColor, &color) == 0) - ctx_color_get_rgba (rasterizer->state, &color, rgba); + if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_READING)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); - CtxEntry set_color_command [3]= - { - ctx_f (CTX_COLOR, CTX_RGBA, rgba[0]), - ctx_f (CTX_CONT, rgba[1], rgba[2]), - ctx_f (CTX_CONT, rgba[3], 0) - }; - CtxEntry restore_command = ctx_void(CTX_RESTORE); - float radius = rasterizer->state->gstate.shadow_blur; - int dim = 2 * radius + 1; - if (dim > CTX_MAX_GAUSSIAN_KERNEL_DIM) - dim = CTX_MAX_GAUSSIAN_KERNEL_DIM; - ctx_compute_gaussian_kernel (dim, radius, rasterizer->kernel); - ctx_rasterizer_process (ctx, (CtxCommand*)&save_command); - { - int i = 0; - for (int v = 0; v < dim; v += 1, i++) - { - float dy = rasterizer->state->gstate.shadow_offset_y + v - dim/2; - set_color_command[2].data.f[0] = rasterizer->kernel[i] * rgba[3]; - ctx_rasterizer_process (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 - } - } - //ctx_free (kernel); - ctx_rasterizer_process (ctx, (CtxCommand*)&restore_command); + if (flags & MZ_ZIP_FLAG_WRITE_ZIP64) + { + /* We don't support converting a non-zip64 file to zip64 - this seems like more trouble than it's worth. (What about the existing 32-bit data descriptors that could follow the compressed data?) */ + if (!pZip->m_pState->m_zip64) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + } + + /* No sense in trying to write to an archive that's already at the support max size */ + if (pZip->m_pState->m_zip64) + { + if (pZip->m_total_files == MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); + } + else + { + if (pZip->m_total_files == MZ_UINT16_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); + + if ((pZip->m_archive_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + MZ_ZIP_LOCAL_DIR_HEADER_SIZE) > MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_TOO_LARGE); + } + + pState = pZip->m_pState; + + if (pState->m_pFile) + { +#ifdef MINIZ_NO_STDIO + (void)pFilename; + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); +#else + if (pZip->m_pIO_opaque != pZip) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + if (pZip->m_zip_type == MZ_ZIP_TYPE_FILE) + { + if (!pFilename) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + /* Archive is being read from stdio and was originally opened only for reading. Try to reopen as writable. */ + if (NULL == (pState->m_pFile = MZ_FREOPEN(pFilename, "r+b", pState->m_pFile))) + { + /* The mz_zip_archive is now in a bogus state because pState->m_pFile is NULL, so just close it. */ + mz_zip_reader_end_internal(pZip, MZ_FALSE); + return mz_zip_set_error(pZip, MZ_ZIP_FILE_OPEN_FAILED); + } + } + + pZip->m_pWrite = mz_zip_file_write_func; + pZip->m_pNeeds_keepalive = NULL; +#endif /* #ifdef MINIZ_NO_STDIO */ + } + else if (pState->m_pMem) + { + /* Archive lives in a memory block. Assume it's from the heap that we can resize using the realloc callback. */ + if (pZip->m_pIO_opaque != pZip) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + pState->m_mem_capacity = pState->m_mem_size; + pZip->m_pWrite = mz_zip_heap_write_func; + pZip->m_pNeeds_keepalive = NULL; + } + /* Archive is being read via a user provided read function - make sure the user has specified a write function too. */ + else if (!pZip->m_pWrite) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + /* Start writing new files at the archive's current central directory location. */ + /* TODO: We could add a flag that lets the user start writing immediately AFTER the existing central dir - this would be safer. */ + pZip->m_archive_size = pZip->m_central_directory_file_ofs; + pZip->m_central_directory_file_ofs = 0; + + /* Clear the sorted central dir offsets, they aren't useful or maintained now. */ + /* Even though we're now in write mode, files can still be extracted and verified, but file locates will be slow. */ + /* TODO: We could easily maintain the sorted central directory offsets. */ + mz_zip_array_clear(pZip, &pZip->m_pState->m_sorted_central_dir_offsets); + + pZip->m_zip_mode = MZ_ZIP_MODE_WRITING; + + return MZ_TRUE; } -static void -ctx_rasterizer_shadow_text (CtxRasterizer *rasterizer, const char *str) +mz_bool mz_zip_writer_init_from_reader(mz_zip_archive *pZip, const char *pFilename) { - float x = rasterizer->state->x; - float y = rasterizer->state->y; - CtxColor color; - CtxEntry save_command = ctx_void(CTX_SAVE); - Ctx *ctx = rasterizer->backend.ctx; + return mz_zip_writer_init_from_reader_v2(pZip, pFilename, 0); +} - float rgba[4] = {0, 0, 0, 1.0}; - if (ctx_get_color (rasterizer->backend.ctx, CTX_shadowColor, &color) == 0) - ctx_color_get_rgba (rasterizer->state, &color, rgba); +/* TODO: pArchive_name is a terrible name here! */ +mz_bool mz_zip_writer_add_mem(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, mz_uint level_and_flags) +{ + return mz_zip_writer_add_mem_ex(pZip, pArchive_name, pBuf, buf_size, NULL, 0, level_and_flags, 0, 0); +} - CtxEntry set_color_command [3]= - { - ctx_f (CTX_COLOR, CTX_RGBA, rgba[0]), - ctx_f (CTX_CONT, rgba[1], rgba[2]), - ctx_f (CTX_CONT, rgba[3], 0) - }; - CtxEntry move_to_command [1]= - { - ctx_f (CTX_MOVE_TO, x, y), - }; - CtxEntry restore_command = ctx_void(CTX_RESTORE); - float radius = rasterizer->state->gstate.shadow_blur; - int dim = 2 * radius + 1; - if (dim > CTX_MAX_GAUSSIAN_KERNEL_DIM) - dim = CTX_MAX_GAUSSIAN_KERNEL_DIM; - ctx_compute_gaussian_kernel (dim, radius, rasterizer->kernel); - ctx_rasterizer_process (ctx, (CtxCommand*)&save_command); +typedef struct +{ + mz_zip_archive *m_pZip; + mz_uint64 m_cur_archive_file_ofs; + mz_uint64 m_comp_size; +} mz_zip_writer_add_state; - { - { - move_to_command[0].data.f[0] = x; - move_to_command[0].data.f[1] = y; - set_color_command[2].data.f[0] = rgba[3]; - ctx_rasterizer_process (ctx, (CtxCommand*)&set_color_command); - ctx_rasterizer_process (ctx, (CtxCommand*)&move_to_command); - rasterizer->in_shadow=1; - ctx_rasterizer_text (rasterizer, str, 0); - rasterizer->in_shadow=0; - } - } - ctx_rasterizer_process (ctx, (CtxCommand*)&restore_command); - move_to_command[0].data.f[0] = x; - move_to_command[0].data.f[1] = y; - ctx_rasterizer_process (ctx, (CtxCommand*)&move_to_command); +static mz_bool mz_zip_writer_add_put_buf_callback(const void *pBuf, int len, void *pUser) +{ + mz_zip_writer_add_state *pState = (mz_zip_writer_add_state *)pUser; + if ((int)pState->m_pZip->m_pWrite(pState->m_pZip->m_pIO_opaque, pState->m_cur_archive_file_ofs, pBuf, len) != len) + return MZ_FALSE; + + pState->m_cur_archive_file_ofs += len; + pState->m_comp_size += len; + return MZ_TRUE; } -static void -ctx_rasterizer_shadow_fill (CtxRasterizer *rasterizer) +#define MZ_ZIP64_MAX_LOCAL_EXTRA_FIELD_SIZE (sizeof(mz_uint16) * 2 + sizeof(mz_uint64) * 2) +#define MZ_ZIP64_MAX_CENTRAL_EXTRA_FIELD_SIZE (sizeof(mz_uint16) * 2 + sizeof(mz_uint64) * 3) +static mz_uint32 mz_zip_writer_create_zip64_extra_data(mz_uint8 *pBuf, mz_uint64 *pUncomp_size, mz_uint64 *pComp_size, mz_uint64 *pLocal_header_ofs) { - CtxColor color; - Ctx *ctx = rasterizer->backend.ctx; - CtxEntry save_command = ctx_void(CTX_SAVE); + mz_uint8 *pDst = pBuf; + mz_uint32 field_size = 0; - float rgba[4] = {0, 0, 0, 1.0}; - if (ctx_get_color (rasterizer->backend.ctx, CTX_shadowColor, &color) == 0) - ctx_color_get_rgba (rasterizer->state, &color, rgba); + MZ_WRITE_LE16(pDst + 0, MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID); + MZ_WRITE_LE16(pDst + 2, 0); + pDst += sizeof(mz_uint16) * 2; - CtxEntry set_color_command [3]= - { - ctx_f (CTX_COLOR, CTX_RGBA, rgba[0]), - ctx_f (CTX_CONT, rgba[1], rgba[2]), - ctx_f (CTX_CONT, rgba[3], 0) - }; - CtxEntry restore_command = ctx_void(CTX_RESTORE); - float radius = rasterizer->state->gstate.shadow_blur; - int dim = 2 * radius + 1; - if (dim > CTX_MAX_GAUSSIAN_KERNEL_DIM) - dim = CTX_MAX_GAUSSIAN_KERNEL_DIM; - ctx_compute_gaussian_kernel (dim, radius, rasterizer->kernel); - ctx_rasterizer_process (ctx, (CtxCommand*)&save_command); + if (pUncomp_size) + { + MZ_WRITE_LE64(pDst, *pUncomp_size); + pDst += sizeof(mz_uint64); + field_size += sizeof(mz_uint64); + } - { - 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; - } - } - ctx_rasterizer_process (ctx, (CtxCommand*)&restore_command); + if (pComp_size) + { + MZ_WRITE_LE64(pDst, *pComp_size); + pDst += sizeof(mz_uint64); + field_size += sizeof(mz_uint64); + } + + if (pLocal_header_ofs) + { + MZ_WRITE_LE64(pDst, *pLocal_header_ofs); + pDst += sizeof(mz_uint64); + field_size += sizeof(mz_uint64); + } + + MZ_WRITE_LE16(pBuf + 2, field_size); + + return (mz_uint32)(pDst - pBuf); } -#endif -static void -ctx_rasterizer_line_dash (CtxRasterizer *rasterizer, unsigned int count, float *dashes) +static mz_bool mz_zip_writer_create_local_dir_header(mz_zip_archive *pZip, mz_uint8 *pDst, mz_uint16 filename_size, mz_uint16 extra_size, mz_uint64 uncomp_size, mz_uint64 comp_size, mz_uint32 uncomp_crc32, mz_uint16 method, mz_uint16 bit_flags, mz_uint16 dos_time, mz_uint16 dos_date) { - if (!dashes) - { - rasterizer->state->gstate.n_dashes = 0; - return; - } - count = CTX_MIN(count, CTX_MAX_DASHES); - rasterizer->state->gstate.n_dashes = count; - memcpy(&rasterizer->state->gstate.dashes[0], dashes, count * sizeof(float)); - for (unsigned int i = 0; i < count; i ++) - { - if (rasterizer->state->gstate.dashes[i] < 0.0001f) - rasterizer->state->gstate.dashes[i] = 0.0001f; // hang protection - } + (void)pZip; + memset(pDst, 0, MZ_ZIP_LOCAL_DIR_HEADER_SIZE); + MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_SIG_OFS, MZ_ZIP_LOCAL_DIR_HEADER_SIG); + MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_VERSION_NEEDED_OFS, method ? 20 : 0); + MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_BIT_FLAG_OFS, bit_flags); + MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_METHOD_OFS, method); + MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_FILE_TIME_OFS, dos_time); + MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_FILE_DATE_OFS, dos_date); + MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_CRC32_OFS, uncomp_crc32); + MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_COMPRESSED_SIZE_OFS, MZ_MIN(comp_size, MZ_UINT32_MAX)); + MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_DECOMPRESSED_SIZE_OFS, MZ_MIN(uncomp_size, MZ_UINT32_MAX)); + MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_FILENAME_LEN_OFS, filename_size); + MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_EXTRA_LEN_OFS, extra_size); + return MZ_TRUE; } +static mz_bool mz_zip_writer_create_central_dir_header(mz_zip_archive *pZip, mz_uint8 *pDst, + mz_uint16 filename_size, mz_uint16 extra_size, mz_uint16 comment_size, + mz_uint64 uncomp_size, mz_uint64 comp_size, mz_uint32 uncomp_crc32, + mz_uint16 method, mz_uint16 bit_flags, mz_uint16 dos_time, mz_uint16 dos_date, + mz_uint64 local_header_ofs, mz_uint32 ext_attributes) +{ + (void)pZip; + memset(pDst, 0, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE); + MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_SIG_OFS, MZ_ZIP_CENTRAL_DIR_HEADER_SIG); + MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_VERSION_NEEDED_OFS, method ? 20 : 0); + MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_BIT_FLAG_OFS, bit_flags); + MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_METHOD_OFS, method); + MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_FILE_TIME_OFS, dos_time); + MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_FILE_DATE_OFS, dos_date); + MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_CRC32_OFS, uncomp_crc32); + MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS, MZ_MIN(comp_size, MZ_UINT32_MAX)); + MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS, MZ_MIN(uncomp_size, MZ_UINT32_MAX)); + MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_FILENAME_LEN_OFS, filename_size); + MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_EXTRA_LEN_OFS, extra_size); + MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_COMMENT_LEN_OFS, comment_size); + MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_EXTERNAL_ATTR_OFS, ext_attributes); + MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_LOCAL_HEADER_OFS, MZ_MIN(local_header_ofs, MZ_UINT32_MAX)); + return MZ_TRUE; +} -static void -ctx_rasterizer_process (Ctx *ctx, CtxCommand *command) +static mz_bool mz_zip_writer_add_to_central_dir(mz_zip_archive *pZip, const char *pFilename, mz_uint16 filename_size, + const void *pExtra, mz_uint16 extra_size, const void *pComment, mz_uint16 comment_size, + mz_uint64 uncomp_size, mz_uint64 comp_size, mz_uint32 uncomp_crc32, + mz_uint16 method, mz_uint16 bit_flags, mz_uint16 dos_time, mz_uint16 dos_date, + mz_uint64 local_header_ofs, mz_uint32 ext_attributes, + const char *user_extra_data, mz_uint user_extra_data_len) { - CtxEntry *entry = &command->entry; - CtxRasterizer *rasterizer = (CtxRasterizer *) ctx->backend; - CtxState *state = rasterizer->state; - CtxCommand *c = (CtxCommand *) entry; - int clear_clip = 0; + mz_zip_internal_state *pState = pZip->m_pState; + mz_uint32 central_dir_ofs = (mz_uint32)pState->m_central_dir.m_size; + size_t orig_central_dir_size = pState->m_central_dir.m_size; + mz_uint8 central_dir_header[MZ_ZIP_CENTRAL_DIR_HEADER_SIZE]; - ctx_interpret_style (state, entry, NULL); - switch (c->code) + if (!pZip->m_pState->m_zip64) { -#if CTX_ENABLE_SHADOW_BLUR - case CTX_SHADOW_COLOR: - { - CtxColor col; - CtxColor *color = &col; - //state->gstate.source_fill.type = CTX_SOURCE_COLOR; - switch ((int)c->rgba.model) - { - case CTX_RGB: - ctx_color_set_rgba (state, color, c->rgba.r, c->rgba.g, c->rgba.b, 1.0f); - break; - case CTX_RGBA: - //ctx_color_set_rgba (state, color, c->rgba.r, c->rgba.g, c->rgba.b, c->rgba.a); - ctx_color_set_rgba (state, color, c->rgba.r, c->rgba.g, c->rgba.b, c->rgba.a); - break; - case CTX_DRGBA: - ctx_color_set_drgba (state, color, c->rgba.r, c->rgba.g, c->rgba.b, c->rgba.a); - break; -#if CTX_ENABLE_CMYK - case CTX_CMYKA: - ctx_color_set_cmyka (state, color, c->cmyka.c, c->cmyka.m, c->cmyka.y, c->cmyka.k, c->cmyka.a); - break; - case CTX_CMYK: - ctx_color_set_cmyka (state, color, c->cmyka.c, c->cmyka.m, c->cmyka.y, c->cmyka.k, 1.0f); - break; - case CTX_DCMYKA: - ctx_color_set_dcmyka (state, color, c->cmyka.c, c->cmyka.m, c->cmyka.y, c->cmyka.k, c->cmyka.a); - break; - case CTX_DCMYK: - ctx_color_set_dcmyka (state, color, c->cmyka.c, c->cmyka.m, c->cmyka.y, c->cmyka.k, 1.0f); - break; -#endif - case CTX_GRAYA: - ctx_color_set_graya (state, color, c->graya.g, c->graya.a); - break; - case CTX_GRAY: - ctx_color_set_graya (state, color, c->graya.g, 1.0f); - break; - } - ctx_set_color (rasterizer->backend.ctx, CTX_shadowColor, color); - } - break; -#endif - case CTX_LINE_DASH: - if (c->line_dash.count) - { - ctx_rasterizer_line_dash (rasterizer, c->line_dash.count, c->line_dash.data); - } - else - ctx_rasterizer_line_dash (rasterizer, 0, NULL); - break; + if (local_header_ofs > 0xFFFFFFFF) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_TOO_LARGE); + } + /* miniz doesn't support central dirs >= MZ_UINT32_MAX bytes yet */ + if (((mz_uint64)pState->m_central_dir.m_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + filename_size + extra_size + user_extra_data_len + comment_size) >= MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_CDIR_SIZE); - case CTX_LINE_TO: - if (ctx->bail) break; - ctx_rasterizer_line_to (rasterizer, c->c.x0, c->c.y0); - break; - case CTX_REL_LINE_TO: - if (ctx->bail) break; - ctx_rasterizer_rel_line_to (rasterizer, c->c.x0, c->c.y0); - break; - case CTX_MOVE_TO: - if (ctx->bail) break; - ctx_rasterizer_move_to (rasterizer, c->c.x0, c->c.y0); - break; - case CTX_REL_MOVE_TO: - if (ctx->bail) break; - ctx_rasterizer_rel_move_to (rasterizer, c->c.x0, c->c.y0); - break; - case CTX_CURVE_TO: - if (ctx->bail) break; - ctx_rasterizer_curve_to (rasterizer, c->c.x0, c->c.y0, - c->c.x1, c->c.y1, - c->c.x2, c->c.y2); - break; - case CTX_REL_CURVE_TO: - if (ctx->bail) break; - ctx_rasterizer_rel_curve_to (rasterizer, c->c.x0, c->c.y0, - c->c.x1, c->c.y1, - c->c.x2, c->c.y2); - break; - case CTX_QUAD_TO: - if (ctx->bail) break; - ctx_rasterizer_quad_to (rasterizer, c->c.x0, c->c.y0, c->c.x1, c->c.y1); - break; - case CTX_REL_QUAD_TO: - if (ctx->bail) break; - ctx_rasterizer_rel_quad_to (rasterizer, c->c.x0, c->c.y0, c->c.x1, c->c.y1); - break; - case CTX_ARC: - if (ctx->bail) break; - ctx_rasterizer_arc (rasterizer, c->arc.x, c->arc.y, c->arc.radius, c->arc.angle1, c->arc.angle2, (int)c->arc.direction); - break; - case CTX_RECTANGLE: - if (ctx->bail) break; - ctx_rasterizer_rectangle (rasterizer, c->rectangle.x, c->rectangle.y, - c->rectangle.width, c->rectangle.height); - break; - case CTX_ROUND_RECTANGLE: - if (ctx->bail) break; - ctx_rasterizer_round_rectangle (rasterizer, c->rectangle.x, c->rectangle.y, - c->rectangle.width, c->rectangle.height, - c->rectangle.radius); - break; - case CTX_SET_PIXEL: - ctx_rasterizer_set_pixel (rasterizer, c->set_pixel.x, c->set_pixel.y, - c->set_pixel.rgba[0], - c->set_pixel.rgba[1], - c->set_pixel.rgba[2], - c->set_pixel.rgba[3]); - break; - case CTX_DEFINE_TEXTURE: + if (!mz_zip_writer_create_central_dir_header(pZip, central_dir_header, filename_size, (mz_uint16)(extra_size + user_extra_data_len), comment_size, uncomp_size, comp_size, uncomp_crc32, method, bit_flags, dos_time, dos_date, local_header_ofs, ext_attributes)) + return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); + + if ((!mz_zip_array_push_back(pZip, &pState->m_central_dir, central_dir_header, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE)) || + (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pFilename, filename_size)) || + (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pExtra, extra_size)) || + (!mz_zip_array_push_back(pZip, &pState->m_central_dir, user_extra_data, user_extra_data_len)) || + (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pComment, comment_size)) || + (!mz_zip_array_push_back(pZip, &pState->m_central_dir_offsets, ¢ral_dir_ofs, 1))) + { + /* Try to resize the central directory array back into its original state. */ + mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE); + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + } + + return MZ_TRUE; +} + +static mz_bool mz_zip_writer_validate_archive_name(const char *pArchive_name) +{ + /* Basic ZIP archive filename validity checks: Valid filenames cannot start with a forward slash, cannot contain a drive letter, and cannot use DOS-style backward slashes. */ + if (*pArchive_name == '/') + return MZ_FALSE; + + /* Making sure the name does not contain drive letters or DOS style backward slashes is the responsibility of the program using miniz*/ + + return MZ_TRUE; +} + +static mz_uint mz_zip_writer_compute_padding_needed_for_file_alignment(mz_zip_archive *pZip) +{ + mz_uint32 n; + if (!pZip->m_file_offset_alignment) + return 0; + n = (mz_uint32)(pZip->m_archive_size & (pZip->m_file_offset_alignment - 1)); + return (mz_uint)((pZip->m_file_offset_alignment - n) & (pZip->m_file_offset_alignment - 1)); +} + +static mz_bool mz_zip_writer_write_zeros(mz_zip_archive *pZip, mz_uint64 cur_file_ofs, mz_uint32 n) +{ + char buf[4096]; + memset(buf, 0, MZ_MIN(sizeof(buf), n)); + while (n) + { + mz_uint32 s = MZ_MIN(sizeof(buf), n); + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_file_ofs, buf, s) != s) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + + cur_file_ofs += s; + n -= s; + } + return MZ_TRUE; +} + +mz_bool mz_zip_writer_add_mem_ex(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, + mz_uint64 uncomp_size, mz_uint32 uncomp_crc32) +{ + return mz_zip_writer_add_mem_ex_v2(pZip, pArchive_name, pBuf, buf_size, pComment, comment_size, level_and_flags, uncomp_size, uncomp_crc32, NULL, NULL, 0, NULL, 0); +} + +mz_bool mz_zip_writer_add_mem_ex_v2(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, + mz_uint level_and_flags, mz_uint64 uncomp_size, mz_uint32 uncomp_crc32, MZ_TIME_T *last_modified, + const char *user_extra_data, mz_uint user_extra_data_len, const char *user_extra_data_central, mz_uint user_extra_data_central_len) +{ + mz_uint16 method = 0, dos_time = 0, dos_date = 0; + mz_uint level, ext_attributes = 0, num_alignment_padding_bytes; + mz_uint64 local_dir_header_ofs = pZip->m_archive_size, cur_archive_file_ofs = pZip->m_archive_size, comp_size = 0; + size_t archive_name_size; + mz_uint8 local_dir_header[MZ_ZIP_LOCAL_DIR_HEADER_SIZE]; + tdefl_compressor *pComp = NULL; + mz_bool store_data_uncompressed; + mz_zip_internal_state *pState; + mz_uint8 *pExtra_data = NULL; + mz_uint32 extra_size = 0; + mz_uint8 extra_data[MZ_ZIP64_MAX_CENTRAL_EXTRA_FIELD_SIZE]; + mz_uint16 bit_flags = 0; + + if ((int)level_and_flags < 0) + level_and_flags = MZ_DEFAULT_LEVEL; + + if (uncomp_size || (buf_size && !(level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA))) + bit_flags |= MZ_ZIP_LDH_BIT_FLAG_HAS_LOCATOR; + + if (!(level_and_flags & MZ_ZIP_FLAG_ASCII_FILENAME)) + bit_flags |= MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_UTF8; + + level = level_and_flags & 0xF; + store_data_uncompressed = ((!level) || (level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA)); + + if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING) || ((buf_size) && (!pBuf)) || (!pArchive_name) || ((comment_size) && (!pComment)) || (level > MZ_UBER_COMPRESSION)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + pState = pZip->m_pState; + + if (pState->m_zip64) + { + if (pZip->m_total_files == MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); + } + else + { + if (pZip->m_total_files == MZ_UINT16_MAX) { - uint8_t *pixel_data = ctx_define_texture_pixel_data (entry); - ctx_rasterizer_define_texture (rasterizer, c->define_texture.eid, - c->define_texture.width, c->define_texture.height, - c->define_texture.format, - pixel_data); - rasterizer->comp_op = NULL; - rasterizer->fragment = NULL; + pState->m_zip64 = MZ_TRUE; + /*return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); */ } - break; - case CTX_TEXTURE: - ctx_rasterizer_set_texture (rasterizer, c->texture.eid, - c->texture.x, c->texture.y); - rasterizer->comp_op = NULL; - rasterizer->fragment = NULL; - break; - case CTX_SOURCE_TRANSFORM: - ctx_matrix_set (&state->gstate.source_fill.set_transform, - ctx_arg_float (0), ctx_arg_float (1), - ctx_arg_float (2), ctx_arg_float (3), - ctx_arg_float (4), ctx_arg_float (5), - ctx_arg_float (6), ctx_arg_float (7), - ctx_arg_float (8)); - rasterizer->comp_op = NULL; - break; -#if 0 - case CTX_LOAD_IMAGE: - ctx_rasterizer_load_image (rasterizer, ctx_arg_string(), - ctx_arg_float (0), ctx_arg_float (1) ); - break; -#endif -#if CTX_GRADIENTS - case CTX_GRADIENT_STOP: + if (((mz_uint64)buf_size > 0xFFFFFFFF) || (uncomp_size > 0xFFFFFFFF)) { - float rgba[4]= {ctx_u8_to_float (ctx_arg_u8 (4) ), - ctx_u8_to_float (ctx_arg_u8 (4+1) ), - ctx_u8_to_float (ctx_arg_u8 (4+2) ), - ctx_u8_to_float (ctx_arg_u8 (4+3) ) - }; - ctx_rasterizer_gradient_add_stop (rasterizer, - ctx_arg_float (0), rgba); - rasterizer->comp_op = NULL; + pState->m_zip64 = MZ_TRUE; + /*return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); */ } - break; - 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_state_gradient_clear_stops (state); - rasterizer->gradient_cache_valid = 0; - rasterizer->comp_op = NULL; - break; -#endif - case CTX_PRESERVE: - rasterizer->preserve = 1; - break; - case CTX_COLOR: - case CTX_COMPOSITING_MODE: - case CTX_BLEND_MODE: - case CTX_EXTEND: - rasterizer->comp_op = NULL; - break; -#if CTX_COMPOSITING_GROUPS - case CTX_START_GROUP: - ctx_rasterizer_start_group (rasterizer); - break; - case CTX_END_GROUP: - ctx_rasterizer_end_group (rasterizer); - break; -#endif + } - case CTX_RESTORE: - for (unsigned int i = state->gstate_no?state->gstate_stack[state->gstate_no-1].keydb_pos:0; - i < state->gstate.keydb_pos; i++) - { - if (state->keydb[i].key == CTX_clip) - { - clear_clip = 1; - } - } - /* FALLTHROUGH */ - case CTX_ROTATE: - case CTX_SCALE: - case CTX_APPLY_TRANSFORM: - case CTX_TRANSLATE: - case CTX_IDENTITY: - /* FALLTHROUGH */ - case CTX_SAVE: - rasterizer->comp_op = NULL; - ctx_interpret_transforms (state, entry, NULL); - if (clear_clip) - { - ctx_rasterizer_clip_reset (rasterizer); - for (unsigned int i = state->gstate_no?state->gstate_stack[state->gstate_no-1].keydb_pos:0; - i < state->gstate.keydb_pos; i++) + if ((!(level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) && (uncomp_size)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + if (!mz_zip_writer_validate_archive_name(pArchive_name)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_FILENAME); + +#ifndef MINIZ_NO_TIME + if (last_modified != NULL) + { + mz_zip_time_t_to_dos_time(*last_modified, &dos_time, &dos_date); + } + else + { + MZ_TIME_T cur_time; + time(&cur_time); + mz_zip_time_t_to_dos_time(cur_time, &dos_time, &dos_date); + } +#endif /* #ifndef MINIZ_NO_TIME */ + + if (!(level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) + { + uncomp_crc32 = (mz_uint32)mz_crc32(MZ_CRC32_INIT, (const mz_uint8 *)pBuf, buf_size); + uncomp_size = buf_size; + if (uncomp_size <= 3) + { + level = 0; + store_data_uncompressed = MZ_TRUE; + } + } + + archive_name_size = strlen(pArchive_name); + if (archive_name_size > MZ_UINT16_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_FILENAME); + + num_alignment_padding_bytes = mz_zip_writer_compute_padding_needed_for_file_alignment(pZip); + + /* miniz doesn't support central dirs >= MZ_UINT32_MAX bytes yet */ + if (((mz_uint64)pState->m_central_dir.m_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + archive_name_size + MZ_ZIP64_MAX_CENTRAL_EXTRA_FIELD_SIZE + comment_size) >= MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_CDIR_SIZE); + + if (!pState->m_zip64) + { + /* Bail early if the archive would obviously become too large */ + if ((pZip->m_archive_size + num_alignment_padding_bytes + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + archive_name_size + + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + archive_name_size + comment_size + user_extra_data_len + + pState->m_central_dir.m_size + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE + user_extra_data_central_len + + MZ_ZIP_DATA_DESCRIPTER_SIZE32) > 0xFFFFFFFF) { - if (state->keydb[i].key == CTX_clip) - { - int idx = ctx_float_to_string_index (state->keydb[i].value); - if (idx >=0) - { - CtxSegment *edges = (CtxSegment*)&state->stringpool[idx]; - ctx_rasterizer_clip_apply (rasterizer, edges); - } - } + pState->m_zip64 = MZ_TRUE; + /*return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); */ } - } - break; - 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) - ctx_rasterizer_shadow_stroke (rasterizer); -#endif - { - int count = rasterizer->edge_list.count; - if (state->gstate.n_dashes) - { - int n_dashes = state->gstate.n_dashes; - float *dashes = state->gstate.dashes; - float factor = ctx_matrix_get_scale (&state->gstate.transform); + } - CtxSegment temp[count]; /* copy of already built up path's poly line */ - memcpy (temp, rasterizer->edge_list.entries, sizeof (temp)); - int start = 0; - int end = 0; - CtxMatrix transform_backup = state->gstate.transform; - _ctx_matrix_identity (&state->gstate.transform); - //state->gstate.transform_type = 0; - _ctx_transform_prime (state); - ctx_rasterizer_reset (rasterizer); /* for dashing we create - a dashed path to stroke */ - float prev_x = 0.0f; - float prev_y = 0.0f; - //float pos = 0.0; + if ((archive_name_size) && (pArchive_name[archive_name_size - 1] == '/')) + { + /* Set DOS Subdirectory attribute bit. */ + ext_attributes |= MZ_ZIP_DOS_DIR_ATTRIBUTE_BITFLAG; - int dash_no = 0.0; - float dash_lpos = state->gstate.line_dash_offset * factor; - int is_down = 0; + /* Subdirectories cannot contain data. */ + if ((buf_size) || (uncomp_size)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + } - while (start < count) - { - int started = 0; - int i; - is_down = 0; + /* Try to do any allocations before writing to the archive, so if an allocation fails the file remains unmodified. (A good idea if we're doing an in-place modification.) */ + if ((!mz_zip_array_ensure_room(pZip, &pState->m_central_dir, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + archive_name_size + comment_size + (pState->m_zip64 ? MZ_ZIP64_MAX_CENTRAL_EXTRA_FIELD_SIZE : 0))) || (!mz_zip_array_ensure_room(pZip, &pState->m_central_dir_offsets, 1))) + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); - 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; - ctx_rasterizer_move_to (rasterizer, prev_x, prev_y); - is_down = 1; - } + if ((!store_data_uncompressed) && (buf_size)) + { + if (NULL == (pComp = (tdefl_compressor *)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, sizeof(tdefl_compressor)))) + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + } - for (i = start; i < count; i++) - { - CtxSegment *entry = &temp[i]; - float x, y; - if (entry->code == CTX_NEW_EDGE) - { - if (started) - { - end = i - 1; - dash_no = 0; - dash_lpos = 0.0; - goto foo; - } - prev_x = entry->data.s16[0] * 1.0f / CTX_SUBDIV; - prev_y = entry->data.s16[1] * 1.0f / CTX_FULL_AA; - started = 1; - start = i; - is_down = 1; - ctx_rasterizer_move_to (rasterizer, prev_x, prev_y); - } + if (!mz_zip_writer_write_zeros(pZip, cur_archive_file_ofs, num_alignment_padding_bytes)) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); + return MZ_FALSE; + } -again: + local_dir_header_ofs += num_alignment_padding_bytes; + if (pZip->m_file_offset_alignment) + { + MZ_ASSERT((local_dir_header_ofs & (pZip->m_file_offset_alignment - 1)) == 0); + } + cur_archive_file_ofs += num_alignment_padding_bytes; - x = entry->data.s16[2] * 1.0f / CTX_SUBDIV; - y = entry->data.s16[3] * 1.0f / CTX_FULL_AA; - float dx = x - prev_x; - float dy = y - prev_y; - float length = ctx_fast_hypotf (dx, dy); + MZ_CLEAR_ARR(local_dir_header); - if (dash_lpos + length >= dashes[dash_no] * factor) - { - float p = (dashes[dash_no] * factor - dash_lpos) / length; - float splitx = x * p + (1.0f - p) * prev_x; - float splity = y * p + (1.0f - p) * prev_y; - if (is_down) - { - ctx_rasterizer_line_to (rasterizer, splitx, splity); - is_down = 0; - } - else - { - ctx_rasterizer_move_to (rasterizer, splitx, splity); - is_down = 1; - } - prev_x = splitx; - prev_y = splity; - dash_no++; - dash_lpos=0; - if (dash_no >= n_dashes) dash_no = 0; - goto again; - } - else - { - //pos += length; - dash_lpos += length; - { - if (is_down) - ctx_rasterizer_line_to (rasterizer, x, y); - } - } - prev_x = x; - prev_y = y; - } - end = i-1; -foo: - start = end+1; + if (!store_data_uncompressed || (level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) + { + method = MZ_DEFLATED; + } + + if (pState->m_zip64) + { + if (uncomp_size >= MZ_UINT32_MAX || local_dir_header_ofs >= MZ_UINT32_MAX) + { + pExtra_data = extra_data; + extra_size = mz_zip_writer_create_zip64_extra_data(extra_data, (uncomp_size >= MZ_UINT32_MAX) ? &uncomp_size : NULL, + (uncomp_size >= MZ_UINT32_MAX) ? &comp_size : NULL, (local_dir_header_ofs >= MZ_UINT32_MAX) ? &local_dir_header_ofs : NULL); } - state->gstate.transform = transform_backup; - //state->gstate.transform_type = 0; - _ctx_transform_prime (state); + + if (!mz_zip_writer_create_local_dir_header(pZip, local_dir_header, (mz_uint16)archive_name_size, (mz_uint16)(extra_size + user_extra_data_len), 0, 0, 0, method, bit_flags, dos_time, dos_date)) + return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); + + if (pZip->m_pWrite(pZip->m_pIO_opaque, local_dir_header_ofs, local_dir_header, sizeof(local_dir_header)) != sizeof(local_dir_header)) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + + cur_archive_file_ofs += sizeof(local_dir_header); + + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pArchive_name, archive_name_size) != archive_name_size) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); } - ctx_rasterizer_stroke (rasterizer); + cur_archive_file_ofs += archive_name_size; + + if (pExtra_data != NULL) + { + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, extra_data, extra_size) != extra_size) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + + cur_archive_file_ofs += extra_size; } - ctx_rasterizer_reset (rasterizer); + } + else + { + if ((comp_size > MZ_UINT32_MAX) || (cur_archive_file_ofs > MZ_UINT32_MAX)) + return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); + if (!mz_zip_writer_create_local_dir_header(pZip, local_dir_header, (mz_uint16)archive_name_size, (mz_uint16)user_extra_data_len, 0, 0, 0, method, bit_flags, dos_time, dos_date)) + return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); - break; - case CTX_FONT: - ctx_rasterizer_set_font (rasterizer, ctx_arg_string() ); - break; - case CTX_TEXT: - if (ctx->bail) + if (pZip->m_pWrite(pZip->m_pIO_opaque, local_dir_header_ofs, local_dir_header, sizeof(local_dir_header)) != sizeof(local_dir_header)) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + + cur_archive_file_ofs += sizeof(local_dir_header); + + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pArchive_name, archive_name_size) != archive_name_size) { - _ctx_text (rasterizer->backend.ctx, ctx_arg_string(), 0, 0); - break; + pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); } + cur_archive_file_ofs += archive_name_size; + } - rasterizer->in_text++; -#if CTX_ENABLE_SHADOW_BLUR - if (state->gstate.shadow_blur > 0.0) - ctx_rasterizer_shadow_text (rasterizer, ctx_arg_string ()); -#endif - ctx_rasterizer_text (rasterizer, ctx_arg_string(), 0); - rasterizer->in_text--; - ctx_rasterizer_reset (rasterizer); - break; - case CTX_STROKE_TEXT: - ctx_rasterizer_text (rasterizer, ctx_arg_string(), 1); - ctx_rasterizer_reset (rasterizer); - break; - case CTX_GLYPH: - if (ctx->bail) break; + if (user_extra_data_len > 0) + { + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, user_extra_data, user_extra_data_len) != user_extra_data_len) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + + cur_archive_file_ofs += user_extra_data_len; + } + + if (store_data_uncompressed) + { + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pBuf, buf_size) != buf_size) { - uint32_t unichar = entry[0].data.u32[0]; - uint32_t stroke = unichar & ((uint32_t)1<<31); - if (stroke) unichar -= stroke; - ctx_rasterizer_glyph (rasterizer, entry[0].data.u32[0], stroke); + pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); } - break; - case CTX_PAINT: - // XXX simplify this with a special case - ctx_rasterizer_rectangle (rasterizer, -1000.0, -1000.0, 11000, 11000); - ctx_rasterizer_fill (rasterizer); - ctx_rasterizer_reset (rasterizer); - break; - case CTX_FILL: - if (!ctx->bail) + + cur_archive_file_ofs += buf_size; + comp_size = buf_size; + } + else if (buf_size) + { + mz_zip_writer_add_state state; + + state.m_pZip = pZip; + state.m_cur_archive_file_ofs = cur_archive_file_ofs; + state.m_comp_size = 0; + + if ((tdefl_init(pComp, mz_zip_writer_add_put_buf_callback, &state, tdefl_create_comp_flags_from_zip_params(level, -15, MZ_DEFAULT_STRATEGY)) != TDEFL_STATUS_OKAY) || + (tdefl_compress_buffer(pComp, pBuf, buf_size, TDEFL_FINISH) != TDEFL_STATUS_DONE)) { - if (rasterizer->edge_list.count == 0)break; -#if CTX_ENABLE_SHADOW_BLUR - if (state->gstate.shadow_blur > 0.0f && - !rasterizer->in_text) - ctx_rasterizer_shadow_fill (rasterizer); -#endif - ctx_rasterizer_fill (rasterizer); + pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); + return mz_zip_set_error(pZip, MZ_ZIP_COMPRESSION_FAILED); } - ctx_rasterizer_reset (rasterizer); - break; - case CTX_START_FRAME: - case CTX_BEGIN_PATH: - ctx_rasterizer_reset (rasterizer); - break; - case CTX_CLIP: - ctx_rasterizer_clip (rasterizer); - break; - case CTX_CLOSE_PATH: - ctx_rasterizer_finish_shape (rasterizer); - break; - case CTX_IMAGE_SMOOTHING: - rasterizer->comp_op = NULL; - break; + + comp_size = state.m_comp_size; + cur_archive_file_ofs = state.m_cur_archive_file_ofs; } - ctx_interpret_pos_bare (state, entry, NULL); -} + pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); + pComp = NULL; -//static CtxFont *ctx_fonts; -void -ctx_rasterizer_deinit (CtxRasterizer *rasterizer) -{ - //rasterizer->fonts = ctx_fonts; - ctx_drawlist_deinit (&rasterizer->edge_list); -#if CTX_ENABLE_CLIP - if (rasterizer->clip_buffer) - { - ctx_buffer_destroy (rasterizer->clip_buffer); - rasterizer->clip_buffer = NULL; - } -#endif -#if CTX_SHAPE_CACHE - for (int i = 0; i < CTX_SHAPE_CACHE_ENTRIES; i ++) - if (rasterizer->shape_cache.entries[i]) + if (uncomp_size) { - ctx_free (rasterizer->shape_cache.entries[i]); - rasterizer->shape_cache.entries[i] = NULL; + mz_uint8 local_dir_footer[MZ_ZIP_DATA_DESCRIPTER_SIZE64]; + mz_uint32 local_dir_footer_size = MZ_ZIP_DATA_DESCRIPTER_SIZE32; + + MZ_ASSERT(bit_flags & MZ_ZIP_LDH_BIT_FLAG_HAS_LOCATOR); + + MZ_WRITE_LE32(local_dir_footer + 0, MZ_ZIP_DATA_DESCRIPTOR_ID); + MZ_WRITE_LE32(local_dir_footer + 4, uncomp_crc32); + if (pExtra_data == NULL) + { + if (comp_size > MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); + + MZ_WRITE_LE32(local_dir_footer + 8, comp_size); + MZ_WRITE_LE32(local_dir_footer + 12, uncomp_size); + } + else + { + MZ_WRITE_LE64(local_dir_footer + 8, comp_size); + MZ_WRITE_LE64(local_dir_footer + 16, uncomp_size); + local_dir_footer_size = MZ_ZIP_DATA_DESCRIPTER_SIZE64; + } + + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, local_dir_footer, local_dir_footer_size) != local_dir_footer_size) + return MZ_FALSE; + + cur_archive_file_ofs += local_dir_footer_size; } -#endif -} -void -ctx_rasterizer_destroy (CtxRasterizer *rasterizer) -{ - ctx_rasterizer_deinit (rasterizer); - ctx_free (rasterizer); -} + if (pExtra_data != NULL) + { + extra_size = mz_zip_writer_create_zip64_extra_data(extra_data, (uncomp_size >= MZ_UINT32_MAX) ? &uncomp_size : NULL, + (uncomp_size >= MZ_UINT32_MAX) ? &comp_size : NULL, (local_dir_header_ofs >= MZ_UINT32_MAX) ? &local_dir_header_ofs : NULL); + } -CtxAntialias ctx_get_antialias (Ctx *ctx) -{ -#if CTX_EVENTS - if (ctx_backend_is_tiled (ctx)) - { - CtxTiled *fb = (CtxTiled*)(ctx->backend); - return fb->antialias; - } -#endif - if (ctx_backend_type (ctx) != CTX_BACKEND_RASTERIZER) return CTX_ANTIALIAS_DEFAULT; + if (!mz_zip_writer_add_to_central_dir(pZip, pArchive_name, (mz_uint16)archive_name_size, pExtra_data, (mz_uint16)extra_size, pComment, + comment_size, uncomp_size, comp_size, uncomp_crc32, method, bit_flags, dos_time, dos_date, local_dir_header_ofs, ext_attributes, + user_extra_data_central, user_extra_data_central_len)) + return MZ_FALSE; - switch (((CtxRasterizer*)(ctx->backend))->aa) - { - case 1: return CTX_ANTIALIAS_NONE; - case 3: return CTX_ANTIALIAS_FAST; - //case 5: return CTX_ANTIALIAS_GOOD; - default: - case 15: return CTX_ANTIALIAS_DEFAULT; - } -} + pZip->m_total_files++; + pZip->m_archive_size = cur_archive_file_ofs; -static int _ctx_antialias_to_aa (CtxAntialias antialias) -{ - switch (antialias) - { - case CTX_ANTIALIAS_NONE: return 1; - case CTX_ANTIALIAS_FAST: return 3; - case CTX_ANTIALIAS_GOOD: return 5; - default: - case CTX_ANTIALIAS_DEFAULT: return CTX_RASTERIZER_AA; - } + return MZ_TRUE; } -void -ctx_set_antialias (Ctx *ctx, CtxAntialias antialias) +mz_bool mz_zip_writer_add_read_buf_callback(mz_zip_archive *pZip, const char *pArchive_name, mz_file_read_func read_callback, void* callback_opaque, mz_uint64 max_size, const MZ_TIME_T *pFile_time, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, + const char *user_extra_data, mz_uint user_extra_data_len, const char *user_extra_data_central, mz_uint user_extra_data_central_len) { -#if CTX_TERMINAL_EVENTS - if (ctx_backend_is_tiled (ctx)) - { - CtxTiled *fb = (CtxTiled*)(ctx->backend); - fb->antialias = antialias; -#if CTX_THREADS - for (int i = 0; i < _ctx_max_threads; i++) -#else - int i = 0; -#endif - { - ctx_set_antialias (fb->host[i], antialias); - } - return; - } -#endif - if (ctx_backend_type (ctx) != CTX_BACKEND_RASTERIZER) return; + mz_uint16 gen_flags; + mz_uint uncomp_crc32 = MZ_CRC32_INIT, level, num_alignment_padding_bytes; + mz_uint16 method = 0, dos_time = 0, dos_date = 0, ext_attributes = 0; + mz_uint64 local_dir_header_ofs, cur_archive_file_ofs = pZip->m_archive_size, uncomp_size = 0, comp_size = 0; + size_t archive_name_size; + mz_uint8 local_dir_header[MZ_ZIP_LOCAL_DIR_HEADER_SIZE]; + mz_uint8 *pExtra_data = NULL; + mz_uint32 extra_size = 0; + mz_uint8 extra_data[MZ_ZIP64_MAX_CENTRAL_EXTRA_FIELD_SIZE]; + mz_zip_internal_state *pState; + mz_uint64 file_ofs = 0, cur_archive_header_file_ofs; - ((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; -} + if ((int)level_and_flags < 0) + level_and_flags = MZ_DEFAULT_LEVEL; + level = level_and_flags & 0xF; -CtxRasterizer * -ctx_rasterizer_init (CtxRasterizer *rasterizer, Ctx *ctx, Ctx *texture_source, CtxState *state, void *data, int x, int y, int width, int height, int stride, CtxPixelFormat pixel_format, CtxAntialias antialias) -{ -#if CTX_ENABLE_CLIP - if (rasterizer->clip_buffer) - ctx_buffer_destroy (rasterizer->clip_buffer); -#endif - if (rasterizer->edge_list.size) - ctx_drawlist_deinit (&rasterizer->edge_list); -#if CTX_SHAPE_CACHE - memset (rasterizer, 0, sizeof (CtxRasterizer) - sizeof (CtxShapeCache)); -#else - memset (rasterizer, 0, sizeof (CtxRasterizer)); -#endif - CtxBackend *backend = (CtxBackend*)rasterizer; - backend->process = ctx_rasterizer_process; - backend->destroy = (CtxDestroyNotify)ctx_rasterizer_destroy; - backend->ctx = ctx; - rasterizer->edge_list.flags |= CTX_DRAWLIST_EDGE_LIST; - rasterizer->state = state; - rasterizer->texture_source = texture_source?texture_source:ctx; + gen_flags = (level_and_flags & MZ_ZIP_FLAG_WRITE_HEADER_SET_SIZE) ? 0 : MZ_ZIP_LDH_BIT_FLAG_HAS_LOCATOR; - rasterizer->aa = _ctx_antialias_to_aa (antialias); - rasterizer->fast_aa = (antialias == CTX_ANTIALIAS_DEFAULT||antialias == CTX_ANTIALIAS_FAST); - ctx_state_init (rasterizer->state); - rasterizer->buf = data; - rasterizer->blit_x = x; - rasterizer->blit_y = y; - rasterizer->blit_width = width; - rasterizer->blit_height = height; - rasterizer->state->gstate.clip_min_x = x; - rasterizer->state->gstate.clip_min_y = y; - rasterizer->state->gstate.clip_max_x = x + width - 1; - rasterizer->state->gstate.clip_max_y = y + height - 1; - rasterizer->blit_stride = stride; - rasterizer->scan_min = 5000; - rasterizer->scan_max = -5000; + if (!(level_and_flags & MZ_ZIP_FLAG_ASCII_FILENAME)) + gen_flags |= MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_UTF8; - if (pixel_format == CTX_FORMAT_BGRA8) - { - pixel_format = CTX_FORMAT_RGBA8; - rasterizer->swap_red_green = 1; - } + /* Sanity checks */ + if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING) || (!pArchive_name) || ((comment_size) && (!pComment)) || (level > MZ_UBER_COMPRESSION)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); - rasterizer->format = ctx_pixel_format_info (pixel_format); + pState = pZip->m_pState; -#if CTX_GRADIENTS -#if CTX_GRADIENT_CACHE - rasterizer->gradient_cache_elements = CTX_GRADIENT_CACHE_ELEMENTS; - rasterizer->gradient_cache_valid = 0; -#endif -#endif + if ((!pState->m_zip64) && (max_size > MZ_UINT32_MAX)) + { + /* Source file is too large for non-zip64 */ + /*return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); */ + pState->m_zip64 = MZ_TRUE; + } -#if static_OPAQUE - memset (rasterizer->opaque, 255, sizeof (rasterizer->opaque)); -#endif + /* We could support this, but why? */ + if (level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); - return rasterizer; -} + if (!mz_zip_writer_validate_archive_name(pArchive_name)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_FILENAME); -Ctx * -ctx_new_for_buffer (CtxBuffer *buffer) -{ - Ctx *ctx = _ctx_new_drawlist (buffer->width, buffer->height); - ctx_set_backend (ctx, - ctx_rasterizer_init ( (CtxRasterizer *) ctx_malloc (sizeof (CtxRasterizer) ), - ctx, NULL, &ctx->state, - buffer->data, 0, 0, buffer->width, buffer->height, - buffer->stride, buffer->format->pixel_format, - CTX_ANTIALIAS_DEFAULT)); - return ctx; -} + if (pState->m_zip64) + { + if (pZip->m_total_files == MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); + } + else + { + if (pZip->m_total_files == MZ_UINT16_MAX) + { + pState->m_zip64 = MZ_TRUE; + /*return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); */ + } + } -Ctx * -ctx_new_for_framebuffer (void *data, int width, int height, - int stride, - CtxPixelFormat pixel_format) -{ - Ctx *ctx = _ctx_new_drawlist (width, height); - CtxRasterizer *r = ctx_rasterizer_init ( (CtxRasterizer *) ctx_calloc (sizeof (CtxRasterizer), 1), - ctx, NULL, &ctx->state, data, 0, 0, width, height, - stride, pixel_format, CTX_ANTIALIAS_DEFAULT); - ctx_set_backend (ctx, r); - if (pixel_format == CTX_FORMAT_GRAY1) // XXX we get some bugs without it.. - { // something is going amiss with offsets - ctx_set_antialias (ctx, CTX_ANTIALIAS_NONE); - } - return ctx; -} + archive_name_size = strlen(pArchive_name); + if (archive_name_size > MZ_UINT16_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_FILENAME); -// ctx_new_for_stream (FILE *stream); + num_alignment_padding_bytes = mz_zip_writer_compute_padding_needed_for_file_alignment(pZip); -#if 0 -CtxRasterizer *ctx_rasterizer_new (void *data, int x, int y, int width, int height, - int stride, CtxPixelFormat pixel_format) -{ - CtxState *state = (CtxState *) ctx_malloc (sizeof (CtxState) ); - CtxRasterizer *rasterizer = (CtxRasterizer *) ctx_malloc (sizeof (CtxBackend) ); - ctx_rasterizer_init (rasterizer, state, data, x, y, width, height, - stride, pixel_format, CTX_ANTIALIAS_DEFAULT); -} -#endif + /* miniz doesn't support central dirs >= MZ_UINT32_MAX bytes yet */ + if (((mz_uint64)pState->m_central_dir.m_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + archive_name_size + MZ_ZIP64_MAX_CENTRAL_EXTRA_FIELD_SIZE + comment_size) >= MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_CDIR_SIZE); -#else + if (!pState->m_zip64) + { + /* Bail early if the archive would obviously become too large */ + if ((pZip->m_archive_size + num_alignment_padding_bytes + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + archive_name_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + + archive_name_size + comment_size + user_extra_data_len + pState->m_central_dir.m_size + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE + 1024 + + MZ_ZIP_DATA_DESCRIPTER_SIZE32 + user_extra_data_central_len) > 0xFFFFFFFF) + { + pState->m_zip64 = MZ_TRUE; + /*return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); */ + } + } +#ifndef MINIZ_NO_TIME + if (pFile_time) + { + mz_zip_time_t_to_dos_time(*pFile_time, &dos_time, &dos_date); + } #endif + if (max_size <= 3) + level = 0; -void -ctx_state_gradient_clear_stops (CtxState *state) -{ - state->gradient.n_stops = 0; -} + if (!mz_zip_writer_write_zeros(pZip, cur_archive_file_ofs, num_alignment_padding_bytes)) + { + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + } + cur_archive_file_ofs += num_alignment_padding_bytes; + local_dir_header_ofs = cur_archive_file_ofs; -/**** end of engine ****/ -/* atty - audio interface and driver for terminals - * Copyright (C) 2020 Øyvind Kolås <pippin@gimp.org> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. If not, see <http://www.gnu.org/licenses/>. - */ + if (pZip->m_file_offset_alignment) + { + MZ_ASSERT((cur_archive_file_ofs & (pZip->m_file_offset_alignment - 1)) == 0); + } -static const char *base64_map="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; -static void bin2base64_group (const unsigned char *in, int remaining, char *out) -{ - unsigned char digit[4] = {0,0,64,64}; - int i; - digit[0] = in[0] >> 2; - digit[1] = ((in[0] & 0x03) << 4) | ((in[1] & 0xf0) >> 4); - if (remaining > 1) + if (max_size && level) { - digit[2] = ((in[1] & 0x0f) << 2) | ((in[2] & 0xc0) >> 6); - if (remaining > 2) - digit[3] = ((in[2] & 0x3f)); + method = MZ_DEFLATED; } - for (i = 0; i < 4; i++) - out[i] = base64_map[digit[i]]; -} -void -ctx_bin2base64 (const void *bin, - size_t bin_length, - char *ascii) -{ - /* this allocation is a hack to ensure we always produce the same result, - * regardless of padding data accidentally taken into account. - */ - unsigned char *bin2 = (unsigned char*)ctx_calloc (bin_length + 4, 1); - unsigned const char *p = bin2; - unsigned int i; - if (bin_length > 128 * 1024 * 1024) return; - memcpy (bin2, bin, (size_t)bin_length); - for (i=0; i*3 < bin_length; i++) - { - int remaining = bin_length - i*3; - bin2base64_group (&p[i*3], remaining, &ascii[i*4]); - } - ctx_free (bin2); - ascii[i*4]=0; -} + MZ_CLEAR_ARR(local_dir_header); + if (pState->m_zip64) + { + if (max_size >= MZ_UINT32_MAX || local_dir_header_ofs >= MZ_UINT32_MAX) + { + pExtra_data = extra_data; + if (level_and_flags & MZ_ZIP_FLAG_WRITE_HEADER_SET_SIZE) + extra_size = mz_zip_writer_create_zip64_extra_data(extra_data, (max_size >= MZ_UINT32_MAX) ? &uncomp_size : NULL, + (max_size >= MZ_UINT32_MAX) ? &comp_size : NULL, + (local_dir_header_ofs >= MZ_UINT32_MAX) ? &local_dir_header_ofs : NULL); + else + extra_size = mz_zip_writer_create_zip64_extra_data(extra_data, NULL, + NULL, + (local_dir_header_ofs >= MZ_UINT32_MAX) ? &local_dir_header_ofs : NULL); + } -static unsigned char base64_revmap[255]; -static void base64_revmap_init (void) -{ - static int done = 0; - if (done) - return; + if (!mz_zip_writer_create_local_dir_header(pZip, local_dir_header, (mz_uint16)archive_name_size, (mz_uint16)(extra_size + user_extra_data_len), 0, 0, 0, method, gen_flags, dos_time, dos_date)) + return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); - for (int i = 0; i < 255; i ++) - base64_revmap[i]=255; - for (int i = 0; i < 64; i ++) - base64_revmap[((const unsigned char*)base64_map)[i]]=i; - /* include variants used in URI encodings for decoder, - * even if that is not how we encode - */ - base64_revmap['-']=62; - base64_revmap['_']=63; - base64_revmap['+']=62; - base64_revmap['/']=63; + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, local_dir_header, sizeof(local_dir_header)) != sizeof(local_dir_header)) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); - done = 1; -} + cur_archive_file_ofs += sizeof(local_dir_header); + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pArchive_name, archive_name_size) != archive_name_size) + { + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + } -int -ctx_base642bin (const char *ascii, - int *length, - unsigned char *bin) -{ - int i; - int charno = 0; - int outputno = 0; - int carry = 0; - base64_revmap_init (); - for (i = 0; ascii[i]; i++) + cur_archive_file_ofs += archive_name_size; + + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, extra_data, extra_size) != extra_size) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + + cur_archive_file_ofs += extra_size; + } + else { - int bits = base64_revmap[((const unsigned char*)ascii)[i]]; - if (length && outputno > *length) + if ((comp_size > MZ_UINT32_MAX) || (cur_archive_file_ofs > MZ_UINT32_MAX)) + return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); + if (!mz_zip_writer_create_local_dir_header(pZip, local_dir_header, (mz_uint16)archive_name_size, (mz_uint16)user_extra_data_len, 0, 0, 0, method, gen_flags, dos_time, dos_date)) + return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); + + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, local_dir_header, sizeof(local_dir_header)) != sizeof(local_dir_header)) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + + cur_archive_file_ofs += sizeof(local_dir_header); + + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pArchive_name, archive_name_size) != archive_name_size) { - *length = -1; - return -1; + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); } - if (bits != 255) + + cur_archive_file_ofs += archive_name_size; + } + + if (user_extra_data_len > 0) + { + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, user_extra_data, user_extra_data_len) != user_extra_data_len) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + + cur_archive_file_ofs += user_extra_data_len; + } + + if (max_size) + { + void *pRead_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, MZ_ZIP_MAX_IO_BUF_SIZE); + if (!pRead_buf) { - switch (charno % 4) + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + } + + if (!level) + { + while (1) { - case 0: - carry = bits; - break; - case 1: - bin[outputno] = (carry << 2) | (bits >> 4); - outputno++; - carry = bits & 15; - break; - case 2: - bin[outputno] = (carry << 4) | (bits >> 2); - outputno++; - carry = bits & 3; - break; - case 3: - bin[outputno] = (carry << 6) | bits; - outputno++; - carry = 0; - break; + size_t n = read_callback(callback_opaque, file_ofs, pRead_buf, MZ_ZIP_MAX_IO_BUF_SIZE); + if (n == 0) + break; + + if ((n > MZ_ZIP_MAX_IO_BUF_SIZE) || (file_ofs + n > max_size)) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); + return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + } + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pRead_buf, n) != n) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + } + file_ofs += n; + uncomp_crc32 = (mz_uint32)mz_crc32(uncomp_crc32, (const mz_uint8 *)pRead_buf, n); + cur_archive_file_ofs += n; } - charno++; + uncomp_size = file_ofs; + comp_size = uncomp_size; + } + else + { + mz_bool result = MZ_FALSE; + mz_zip_writer_add_state state; + tdefl_compressor *pComp = (tdefl_compressor *)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, sizeof(tdefl_compressor)); + if (!pComp) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + } + + state.m_pZip = pZip; + state.m_cur_archive_file_ofs = cur_archive_file_ofs; + state.m_comp_size = 0; + + if (tdefl_init(pComp, mz_zip_writer_add_put_buf_callback, &state, tdefl_create_comp_flags_from_zip_params(level, -15, MZ_DEFAULT_STRATEGY)) != TDEFL_STATUS_OKAY) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); + pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); + return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); + } + + for (;;) + { + tdefl_status status; + tdefl_flush flush = TDEFL_NO_FLUSH; + + size_t n = read_callback(callback_opaque, file_ofs, pRead_buf, MZ_ZIP_MAX_IO_BUF_SIZE); + if ((n > MZ_ZIP_MAX_IO_BUF_SIZE) || (file_ofs + n > max_size)) + { + mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + break; + } + + file_ofs += n; + uncomp_crc32 = (mz_uint32)mz_crc32(uncomp_crc32, (const mz_uint8 *)pRead_buf, n); + + if (pZip->m_pNeeds_keepalive != NULL && pZip->m_pNeeds_keepalive(pZip->m_pIO_opaque)) + flush = TDEFL_FULL_FLUSH; + + if (n == 0) + flush = TDEFL_FINISH; + + status = tdefl_compress_buffer(pComp, pRead_buf, n, flush); + if (status == TDEFL_STATUS_DONE) + { + result = MZ_TRUE; + break; + } + else if (status != TDEFL_STATUS_OKAY) + { + mz_zip_set_error(pZip, MZ_ZIP_COMPRESSION_FAILED); + break; + } + } + + pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); + + if (!result) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); + return MZ_FALSE; + } + + uncomp_size = file_ofs; + comp_size = state.m_comp_size; + cur_archive_file_ofs = state.m_cur_archive_file_ofs; } + + pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); } - bin[outputno]=0; - if (length) - *length= outputno; - return outputno; -} -#ifndef SQUOZE_H -#define SQUOZE_H -#include <stdio.h> -#include <stdlib.h> -#include <stdint.h> -#include <string.h> -#include <assert.h> + if (!(level_and_flags & MZ_ZIP_FLAG_WRITE_HEADER_SET_SIZE)) + { + mz_uint8 local_dir_footer[MZ_ZIP_DATA_DESCRIPTER_SIZE64]; + mz_uint32 local_dir_footer_size = MZ_ZIP_DATA_DESCRIPTER_SIZE32; + + MZ_WRITE_LE32(local_dir_footer + 0, MZ_ZIP_DATA_DESCRIPTOR_ID); + MZ_WRITE_LE32(local_dir_footer + 4, uncomp_crc32); + if (pExtra_data == NULL) + { + if (comp_size > MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); + + MZ_WRITE_LE32(local_dir_footer + 8, comp_size); + MZ_WRITE_LE32(local_dir_footer + 12, uncomp_size); + } + else + { + MZ_WRITE_LE64(local_dir_footer + 8, comp_size); + MZ_WRITE_LE64(local_dir_footer + 16, uncomp_size); + local_dir_footer_size = MZ_ZIP_DATA_DESCRIPTER_SIZE64; + } -uint32_t squoze6 (const char *utf8); -uint64_t squoze10 (const char *utf8); -uint64_t squoze12 (const char *utf8); -const char *squoze6_decode (uint32_t hash); -const char *squoze10_decode (uint64_t hash); -const char *squoze12_decode (uint64_t hash); + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, local_dir_footer, local_dir_footer_size) != local_dir_footer_size) + return MZ_FALSE; -//#define SQUOZE_NO_INTERNING // this disables the interning - providing only a hash (and decode for non-overflowed hashes) + cur_archive_file_ofs += local_dir_footer_size; + } -#define SQUOZE_ENTER_SQUEEZE 16 + if (level_and_flags & MZ_ZIP_FLAG_WRITE_HEADER_SET_SIZE) + { + if (pExtra_data != NULL) + { + extra_size = mz_zip_writer_create_zip64_extra_data(extra_data, (max_size >= MZ_UINT32_MAX) ? &uncomp_size : NULL, + (max_size >= MZ_UINT32_MAX) ? &comp_size : NULL, (local_dir_header_ofs >= MZ_UINT32_MAX) ? &local_dir_header_ofs : NULL); + } -#define SQUOZE_SPACE 0 -#define SQUOZE_DEC_OFFSET_A 27 -#define SQUOZE_INC_OFFSET_A 28 -#define SQUOZE_DEC_OFFSET_B 29 -#define SQUOZE_INC_OFFSET_B 30 -#define SQUOZE_ENTER_UTF5 31 + if (!mz_zip_writer_create_local_dir_header(pZip, local_dir_header, + (mz_uint16)archive_name_size, (mz_uint16)(extra_size + user_extra_data_len), + (max_size >= MZ_UINT32_MAX) ? MZ_UINT32_MAX : uncomp_size, + (max_size >= MZ_UINT32_MAX) ? MZ_UINT32_MAX : comp_size, + uncomp_crc32, method, gen_flags, dos_time, dos_date)) + return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR); + + cur_archive_header_file_ofs = local_dir_header_ofs; + + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_header_file_ofs, local_dir_header, sizeof(local_dir_header)) != sizeof(local_dir_header)) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + + if (pExtra_data != NULL) + { + cur_archive_header_file_ofs += sizeof(local_dir_header); + + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_header_file_ofs, pArchive_name, archive_name_size) != archive_name_size) + { + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + } + + cur_archive_header_file_ofs += archive_name_size; + + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_header_file_ofs, extra_data, extra_size) != extra_size) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + + cur_archive_header_file_ofs += extra_size; + } + } + + if (pExtra_data != NULL) + { + extra_size = mz_zip_writer_create_zip64_extra_data(extra_data, (uncomp_size >= MZ_UINT32_MAX) ? &uncomp_size : NULL, + (uncomp_size >= MZ_UINT32_MAX) ? &comp_size : NULL, (local_dir_header_ofs >= MZ_UINT32_MAX) ? &local_dir_header_ofs : NULL); + } + + if (!mz_zip_writer_add_to_central_dir(pZip, pArchive_name, (mz_uint16)archive_name_size, pExtra_data, (mz_uint16)extra_size, pComment, comment_size, + uncomp_size, comp_size, uncomp_crc32, method, gen_flags, dos_time, dos_date, local_dir_header_ofs, ext_attributes, + user_extra_data_central, user_extra_data_central_len)) + return MZ_FALSE; + + pZip->m_total_files++; + pZip->m_archive_size = cur_archive_file_ofs; + + return MZ_TRUE; +} -#define SQUOZE_JUMP_STRIDE 26 -#define SQUOZE_JUMP_OFFSET 19 +#ifndef MINIZ_NO_STDIO -static inline uint32_t squoze_utf8_to_unichar (const char *input); -static inline int squoze_unichar_to_utf8 (uint32_t ch, uint8_t *dest); -static inline int squoze_utf8_len (const unsigned char first_byte); +static size_t mz_file_read_func_stdio(void *pOpaque, mz_uint64 file_ofs, void *pBuf, size_t n) +{ + MZ_FILE *pSrc_file = (MZ_FILE *)pOpaque; + mz_int64 cur_ofs = MZ_FTELL64(pSrc_file); + if (((mz_int64)file_ofs < 0) || (((cur_ofs != (mz_int64)file_ofs)) && (MZ_FSEEK64(pSrc_file, (mz_int64)file_ofs, SEEK_SET)))) + return 0; -/* returns the base-offset of the segment this unichar belongs to, - * - * segments are 26 items long and are offset so that the 'a'-'z' is - * one segment. - */ -static inline int squoze_new_offset (uint32_t unichar) + return MZ_FREAD(pBuf, 1, n, pSrc_file); +} + +mz_bool mz_zip_writer_add_cfile(mz_zip_archive *pZip, const char *pArchive_name, MZ_FILE *pSrc_file, mz_uint64 max_size, const MZ_TIME_T *pFile_time, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, + const char *user_extra_data, mz_uint user_extra_data_len, const char *user_extra_data_central, mz_uint user_extra_data_central_len) { - uint32_t ret = unichar - (unichar % SQUOZE_JUMP_STRIDE) + SQUOZE_JUMP_OFFSET; - if (ret > unichar) ret -= SQUOZE_JUMP_STRIDE; - return ret; + return mz_zip_writer_add_read_buf_callback(pZip, pArchive_name, mz_file_read_func_stdio, pSrc_file, max_size, pFile_time, pComment, comment_size, level_and_flags, + user_extra_data, user_extra_data_len, user_extra_data_central, user_extra_data_central_len); } -static int squoze_needed_jump (uint32_t off, uint32_t unicha) +mz_bool mz_zip_writer_add_file(mz_zip_archive *pZip, const char *pArchive_name, const char *pSrc_filename, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags) { - int count = 0; - int unichar = unicha; - int offset = off; + MZ_FILE *pSrc_file = NULL; + mz_uint64 uncomp_size = 0; + MZ_TIME_T file_modified_time; + MZ_TIME_T *pFile_time = NULL; + mz_bool status; - if (unichar == 32) // space is always in range - return 0; + memset(&file_modified_time, 0, sizeof(file_modified_time)); - /* TODO: replace this with direct computation of values instead of loops */ +#if !defined(MINIZ_NO_TIME) && !defined(MINIZ_NO_STDIO) + pFile_time = &file_modified_time; + if (!mz_zip_get_file_modified_time(pSrc_filename, &file_modified_time)) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_STAT_FAILED); +#endif - while (unichar < offset) - { - offset -= SQUOZE_JUMP_STRIDE; - count ++; - } - if (count) - { - return -count; - } - while (unichar - offset >= SQUOZE_JUMP_STRIDE) - { - offset += SQUOZE_JUMP_STRIDE; - count ++; - } - return count; -} + pSrc_file = MZ_FOPEN(pSrc_filename, "rb"); + if (!pSrc_file) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_OPEN_FAILED); -static inline int -squoze_utf5_length (uint32_t unichar) -{ - int octets = 0; - if (unichar == 0) - return 1; - while (unichar) - { - octets ++; - unichar /= 16; - } - return octets; -} + MZ_FSEEK64(pSrc_file, 0, SEEK_END); + uncomp_size = MZ_FTELL64(pSrc_file); + MZ_FSEEK64(pSrc_file, 0, SEEK_SET); -typedef struct EncodeUtf5 { - int is_utf5; - int offset; - int length; - void *write_data; - uint32_t current; -} EncodeUtf5; + status = mz_zip_writer_add_cfile(pZip, pArchive_name, pSrc_file, uncomp_size, pFile_time, pComment, comment_size, level_and_flags, NULL, 0, NULL, 0); -static inline uint64_t -squoze_overflow_mask_for_dim (int squoze_dim) -{ - return ((uint64_t)1<<(squoze_dim * 5 + 1)); + MZ_FCLOSE(pSrc_file); + + return status; } +#endif /* #ifndef MINIZ_NO_STDIO */ -static int squoze_compute_cost_utf5 (int offset, int val, int next_val) +static mz_bool mz_zip_writer_update_zip64_extension_block(mz_zip_array *pNew_ext, mz_zip_archive *pZip, const mz_uint8 *pExt, mz_uint32 ext_len, mz_uint64 *pComp_size, mz_uint64 *pUncomp_size, mz_uint64 *pLocal_header_ofs, mz_uint32 *pDisk_start) { - int cost = 0; - cost += squoze_utf5_length (val); - if (next_val) - { - int no_change_cost = squoze_utf5_length (next_val); -#if 0 // not hit in test-corpus, it is easier to specify and - // port the hash consistently without it - offset = squoze_new_offset (val); - int change_cost = 1; - int needed_jump = squoze_needed_jump (offset, next_val); + /* + 64 should be enough for any new zip64 data */ + if (!mz_zip_array_reserve(pZip, pNew_ext, ext_len + 64, MZ_FALSE)) + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); - if (needed_jump == 0) - { - change_cost += 1; - } else if (needed_jump >= -2 && needed_jump <= 2) - { - change_cost += 2; - } - else if (needed_jump >= -10 && needed_jump <= -10) + mz_zip_array_resize(pZip, pNew_ext, 0, MZ_FALSE); + + if ((pUncomp_size) || (pComp_size) || (pLocal_header_ofs) || (pDisk_start)) { - change_cost += 3; + mz_uint8 new_ext_block[64]; + mz_uint8 *pDst = new_ext_block; + mz_write_le16(pDst, MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID); + mz_write_le16(pDst + sizeof(mz_uint16), 0); + pDst += sizeof(mz_uint16) * 2; + + if (pUncomp_size) + { + mz_write_le64(pDst, *pUncomp_size); + pDst += sizeof(mz_uint64); + } + + if (pComp_size) + { + mz_write_le64(pDst, *pComp_size); + pDst += sizeof(mz_uint64); + } + + if (pLocal_header_ofs) + { + mz_write_le64(pDst, *pLocal_header_ofs); + pDst += sizeof(mz_uint64); + } + + if (pDisk_start) + { + mz_write_le32(pDst, *pDisk_start); + pDst += sizeof(mz_uint32); + } + + mz_write_le16(new_ext_block + sizeof(mz_uint16), (mz_uint16)((pDst - new_ext_block) - sizeof(mz_uint16) * 2)); + + if (!mz_zip_array_push_back(pZip, pNew_ext, new_ext_block, pDst - new_ext_block)) + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); } - else + + if ((pExt) && (ext_len)) { - change_cost += 100; + mz_uint32 extra_size_remaining = ext_len; + const mz_uint8 *pExtra_data = pExt; + + do + { + mz_uint32 field_id, field_data_size, field_total_size; + + if (extra_size_remaining < (sizeof(mz_uint16) * 2)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + field_id = MZ_READ_LE16(pExtra_data); + field_data_size = MZ_READ_LE16(pExtra_data + sizeof(mz_uint16)); + field_total_size = field_data_size + sizeof(mz_uint16) * 2; + + if (field_total_size > extra_size_remaining) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + if (field_id != MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID) + { + if (!mz_zip_array_push_back(pZip, pNew_ext, pExtra_data, field_total_size)) + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + } + + pExtra_data += field_total_size; + extra_size_remaining -= field_total_size; + } while (extra_size_remaining); } - if (change_cost < no_change_cost) + return MZ_TRUE; +} + +/* TODO: This func is now pretty freakin complex due to zip64, split it up? */ +mz_bool mz_zip_writer_add_from_zip_reader(mz_zip_archive *pZip, mz_zip_archive *pSource_zip, mz_uint src_file_index) +{ + mz_uint n, bit_flags, num_alignment_padding_bytes, src_central_dir_following_data_size; + mz_uint64 src_archive_bytes_remaining, local_dir_header_ofs; + mz_uint64 cur_src_file_ofs, cur_dst_file_ofs; + mz_uint32 local_header_u32[(MZ_ZIP_LOCAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; + mz_uint8 *pLocal_header = (mz_uint8 *)local_header_u32; + mz_uint8 new_central_header[MZ_ZIP_CENTRAL_DIR_HEADER_SIZE]; + size_t orig_central_dir_size; + mz_zip_internal_state *pState; + void *pBuf; + const mz_uint8 *pSrc_central_header; + mz_zip_archive_file_stat src_file_stat; + mz_uint32 src_filename_len, src_comment_len, src_ext_len; + mz_uint32 local_header_filename_size, local_header_extra_len; + mz_uint64 local_header_comp_size, local_header_uncomp_size; + mz_bool found_zip64_ext_data_in_ldir = MZ_FALSE; + + /* Sanity checks */ + if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING) || (!pSource_zip->m_pRead)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + pState = pZip->m_pState; + + /* Don't support copying files from zip64 archives to non-zip64, even though in some cases this is possible */ + if ((pSource_zip->m_pState->m_zip64) && (!pZip->m_pState->m_zip64)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + /* Get pointer to the source central dir header and crack it */ + if (NULL == (pSrc_central_header = mz_zip_get_cdh(pSource_zip, src_file_index))) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + if (MZ_READ_LE32(pSrc_central_header + MZ_ZIP_CDH_SIG_OFS) != MZ_ZIP_CENTRAL_DIR_HEADER_SIG) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + src_filename_len = MZ_READ_LE16(pSrc_central_header + MZ_ZIP_CDH_FILENAME_LEN_OFS); + src_comment_len = MZ_READ_LE16(pSrc_central_header + MZ_ZIP_CDH_COMMENT_LEN_OFS); + src_ext_len = MZ_READ_LE16(pSrc_central_header + MZ_ZIP_CDH_EXTRA_LEN_OFS); + src_central_dir_following_data_size = src_filename_len + src_ext_len + src_comment_len; + + /* TODO: We don't support central dir's >= MZ_UINT32_MAX bytes right now (+32 fudge factor in case we need to add more extra data) */ + if ((pState->m_central_dir.m_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + src_central_dir_following_data_size + 32) >= MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_CDIR_SIZE); + + num_alignment_padding_bytes = mz_zip_writer_compute_padding_needed_for_file_alignment(pZip); + + if (!pState->m_zip64) { - cost += change_cost; + if (pZip->m_total_files == MZ_UINT16_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); } else -#endif { - cost += no_change_cost; + /* TODO: Our zip64 support still has some 32-bit limits that may not be worth fixing. */ + if (pZip->m_total_files == MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); } - } + if (!mz_zip_file_stat_internal(pSource_zip, src_file_index, pSrc_central_header, &src_file_stat, NULL)) + return MZ_FALSE; + cur_src_file_ofs = src_file_stat.m_local_header_ofs; + cur_dst_file_ofs = pZip->m_archive_size; + /* Read the source archive's local dir header */ + if (pSource_zip->m_pRead(pSource_zip->m_pIO_opaque, cur_src_file_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); - return cost; -} + if (MZ_READ_LE32(pLocal_header) != MZ_ZIP_LOCAL_DIR_HEADER_SIG) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); -static int squoze_compute_cost_squeezed (int offset, int val, int next_val) -{ - int needed_jump = squoze_needed_jump (offset, val); - int cost = 0; - if (needed_jump == 0) - { - cost += 1; - } - else if (needed_jump >= -2 && needed_jump <= 2) - { - cost += 2; - offset += SQUOZE_JUMP_STRIDE * needed_jump; - } - else if (needed_jump >= -10 && needed_jump <= 10) - { - cost += 3; - offset += SQUOZE_JUMP_STRIDE * needed_jump; - } - else - { - cost += 100; // very expensive, makes the other choice win - } + cur_src_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE; - if (next_val) - { - int change_cost = 1 + squoze_utf5_length (next_val); - int no_change_cost = 0; - needed_jump = squoze_needed_jump (offset, next_val); + /* Compute the total size we need to copy (filename+extra data+compressed data) */ + local_header_filename_size = MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_FILENAME_LEN_OFS); + local_header_extra_len = MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_EXTRA_LEN_OFS); + local_header_comp_size = MZ_READ_LE32(pLocal_header + MZ_ZIP_LDH_COMPRESSED_SIZE_OFS); + local_header_uncomp_size = MZ_READ_LE32(pLocal_header + MZ_ZIP_LDH_DECOMPRESSED_SIZE_OFS); + src_archive_bytes_remaining = local_header_filename_size + local_header_extra_len + src_file_stat.m_comp_size; - if (needed_jump == 0) - { - no_change_cost += 1; - } - else if (needed_jump >= -2 && needed_jump <= 2) + /* Try to find a zip64 extended information field */ + if ((local_header_extra_len) && ((local_header_comp_size == MZ_UINT32_MAX) || (local_header_uncomp_size == MZ_UINT32_MAX))) { - no_change_cost += 2; + mz_zip_array file_data_array; + const mz_uint8 *pExtra_data; + mz_uint32 extra_size_remaining = local_header_extra_len; + + mz_zip_array_init(&file_data_array, 1); + if (!mz_zip_array_resize(pZip, &file_data_array, local_header_extra_len, MZ_FALSE)) + { + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + } + + if (pSource_zip->m_pRead(pSource_zip->m_pIO_opaque, src_file_stat.m_local_header_ofs + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + local_header_filename_size, file_data_array.m_p, local_header_extra_len) != local_header_extra_len) + { + mz_zip_array_clear(pZip, &file_data_array); + return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + } + + pExtra_data = (const mz_uint8 *)file_data_array.m_p; + + do + { + mz_uint32 field_id, field_data_size, field_total_size; + + if (extra_size_remaining < (sizeof(mz_uint16) * 2)) + { + mz_zip_array_clear(pZip, &file_data_array); + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + } + + field_id = MZ_READ_LE16(pExtra_data); + field_data_size = MZ_READ_LE16(pExtra_data + sizeof(mz_uint16)); + field_total_size = field_data_size + sizeof(mz_uint16) * 2; + + if (field_total_size > extra_size_remaining) + { + mz_zip_array_clear(pZip, &file_data_array); + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + } + + if (field_id == MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID) + { + const mz_uint8 *pSrc_field_data = pExtra_data + sizeof(mz_uint32); + + if (field_data_size < sizeof(mz_uint64) * 2) + { + mz_zip_array_clear(pZip, &file_data_array); + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + } + + local_header_uncomp_size = MZ_READ_LE64(pSrc_field_data); + local_header_comp_size = MZ_READ_LE64(pSrc_field_data + sizeof(mz_uint64)); /* may be 0 if there's a descriptor */ + + found_zip64_ext_data_in_ldir = MZ_TRUE; + break; + } + + pExtra_data += field_total_size; + extra_size_remaining -= field_total_size; + } while (extra_size_remaining); + + mz_zip_array_clear(pZip, &file_data_array); } - else if (needed_jump >= -10 && needed_jump <= 10) + + if (!pState->m_zip64) { - no_change_cost += 3; - offset += SQUOZE_JUMP_STRIDE * needed_jump; + /* Try to detect if the new archive will most likely wind up too big and bail early (+(sizeof(mz_uint32) * 4) is for the optional descriptor which could be present, +64 is a fudge factor). */ + /* We also check when the archive is finalized so this doesn't need to be perfect. */ + mz_uint64 approx_new_archive_size = cur_dst_file_ofs + num_alignment_padding_bytes + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + src_archive_bytes_remaining + (sizeof(mz_uint32) * 4) + + pState->m_central_dir.m_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + src_central_dir_following_data_size + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE + 64; + + if (approx_new_archive_size >= MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); } - else + + /* Write dest archive padding */ + if (!mz_zip_writer_write_zeros(pZip, cur_dst_file_ofs, num_alignment_padding_bytes)) + return MZ_FALSE; + + cur_dst_file_ofs += num_alignment_padding_bytes; + + local_dir_header_ofs = cur_dst_file_ofs; + if (pZip->m_file_offset_alignment) { - no_change_cost = change_cost; + MZ_ASSERT((local_dir_header_ofs & (pZip->m_file_offset_alignment - 1)) == 0); } - if (change_cost < no_change_cost) - cost += change_cost; - else - cost += no_change_cost; - } - - return cost; -} + /* The original zip's local header+ext block doesn't change, even with zip64, so we can just copy it over to the dest zip */ + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_dst_file_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); -static void squoze5_encode (const char *input, int inlen, - char *output, int *r_outlen, - int permit_squeezed, - int escape_endzero) -{ - int offset = squoze_new_offset('a'); - int is_utf5 = 1; - int len = 0; + cur_dst_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE; - for (int i = 0; i < inlen; i+= squoze_utf8_len (input[i])) - { - int val = squoze_utf8_to_unichar (&input[i]); - int next_val = 0; - int first_len = squoze_utf8_len (input[i]); - if (i + first_len < inlen) - next_val = squoze_utf8_to_unichar (&input[i+first_len]); + /* Copy over the source archive bytes to the dest archive, also ensure we have enough buf space to handle optional data descriptor */ + if (NULL == (pBuf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)MZ_MAX(32U, MZ_MIN((mz_uint64)MZ_ZIP_MAX_IO_BUF_SIZE, src_archive_bytes_remaining))))) + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); - if (is_utf5) + while (src_archive_bytes_remaining) { - int change_cost = squoze_compute_cost_squeezed (offset, val, next_val); - int no_change_cost = squoze_compute_cost_utf5 (offset, val, next_val); - - if (i != 0) /* ignore cost of initial 'G' */ - change_cost += 1; + n = (mz_uint)MZ_MIN((mz_uint64)MZ_ZIP_MAX_IO_BUF_SIZE, src_archive_bytes_remaining); + if (pSource_zip->m_pRead(pSource_zip->m_pIO_opaque, cur_src_file_ofs, pBuf, n) != n) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); + return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + } + cur_src_file_ofs += n; - if (permit_squeezed && change_cost <= no_change_cost) - { - output[len++] = SQUOZE_ENTER_SQUEEZE; - is_utf5 = 0; - } + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_dst_file_ofs, pBuf, n) != n) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + } + cur_dst_file_ofs += n; + + src_archive_bytes_remaining -= n; } - else + + /* Now deal with the optional data descriptor */ + bit_flags = MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_BIT_FLAG_OFS); + if (bit_flags & 8) { - int change_cost = 1 + squoze_compute_cost_utf5 (offset, val, next_val); - int no_change_cost = squoze_compute_cost_squeezed (offset, val, next_val); + /* Copy data descriptor */ + if ((pSource_zip->m_pState->m_zip64) || (found_zip64_ext_data_in_ldir)) + { + /* src is zip64, dest must be zip64 */ - if (change_cost < no_change_cost) - { - output[len++] = SQUOZE_ENTER_UTF5; - is_utf5 = 1; - } + /* name uint32_t's */ + /* id 1 (optional in zip64?) */ + /* crc 1 */ + /* comp_size 2 */ + /* uncomp_size 2 */ + if (pSource_zip->m_pRead(pSource_zip->m_pIO_opaque, cur_src_file_ofs, pBuf, (sizeof(mz_uint32) * 6)) != (sizeof(mz_uint32) * 6)) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); + return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + } + + n = sizeof(mz_uint32) * ((MZ_READ_LE32(pBuf) == MZ_ZIP_DATA_DESCRIPTOR_ID) ? 6 : 5); + } + else + { + /* src is NOT zip64 */ + mz_bool has_id; + + if (pSource_zip->m_pRead(pSource_zip->m_pIO_opaque, cur_src_file_ofs, pBuf, sizeof(mz_uint32) * 4) != sizeof(mz_uint32) * 4) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); + return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + } + + has_id = (MZ_READ_LE32(pBuf) == MZ_ZIP_DATA_DESCRIPTOR_ID); + + if (pZip->m_pState->m_zip64) + { + /* dest is zip64, so upgrade the data descriptor */ + const mz_uint8 *pSrc_descriptor = (const mz_uint8 *)pBuf + (has_id ? sizeof(mz_uint32) : 0); + const mz_uint32 src_crc32 = MZ_READ_LE32(pSrc_descriptor); + const mz_uint64 src_comp_size = MZ_READ_LE32(pSrc_descriptor + sizeof(mz_uint32)); + const mz_uint64 src_uncomp_size = MZ_READ_LE32(pSrc_descriptor + 2*sizeof(mz_uint32)); + + mz_write_le32((mz_uint8 *)pBuf, MZ_ZIP_DATA_DESCRIPTOR_ID); + mz_write_le32((mz_uint8 *)pBuf + sizeof(mz_uint32) * 1, src_crc32); + mz_write_le64((mz_uint8 *)pBuf + sizeof(mz_uint32) * 2, src_comp_size); + mz_write_le64((mz_uint8 *)pBuf + sizeof(mz_uint32) * 4, src_uncomp_size); + + n = sizeof(mz_uint32) * 6; + } + else + { + /* dest is NOT zip64, just copy it as-is */ + n = sizeof(mz_uint32) * (has_id ? 4 : 3); + } + } + + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_dst_file_ofs, pBuf, n) != n) + { + pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); + } + + cur_src_file_ofs += n; + cur_dst_file_ofs += n; } + pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); - if (!is_utf5) + /* Finally, add the new central dir header */ + orig_central_dir_size = pState->m_central_dir.m_size; + + memcpy(new_central_header, pSrc_central_header, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE); + + if (pState->m_zip64) { - int needed_jump = squoze_needed_jump (offset, val); - if (needed_jump) - { - if (needed_jump >= -2 && needed_jump <= 2) + /* This is the painful part: We need to write a new central dir header + ext block with updated zip64 fields, and ensure the old fields (if any) are not included. */ + const mz_uint8 *pSrc_ext = pSrc_central_header + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + src_filename_len; + mz_zip_array new_ext_block; + + mz_zip_array_init(&new_ext_block, sizeof(mz_uint8)); + + MZ_WRITE_LE32(new_central_header + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS, MZ_UINT32_MAX); + MZ_WRITE_LE32(new_central_header + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS, MZ_UINT32_MAX); + MZ_WRITE_LE32(new_central_header + MZ_ZIP_CDH_LOCAL_HEADER_OFS, MZ_UINT32_MAX); + + if (!mz_zip_writer_update_zip64_extension_block(&new_ext_block, pZip, pSrc_ext, src_ext_len, &src_file_stat.m_comp_size, &src_file_stat.m_uncomp_size, &local_dir_header_ofs, NULL)) { - switch (needed_jump) - { - case -1: output[len++] = SQUOZE_DEC_OFFSET_B; break; - case 1: output[len++] = SQUOZE_INC_OFFSET_B; break; - case -2: output[len++] = SQUOZE_DEC_OFFSET_A; break; - case 2: output[len++] = SQUOZE_INC_OFFSET_A; break; - } - offset += SQUOZE_JUMP_STRIDE * needed_jump; + mz_zip_array_clear(pZip, &new_ext_block); + return MZ_FALSE; } - else if (needed_jump >= -10 && needed_jump <= 10) { - int encoded_val; - if (needed_jump < -2) - encoded_val = 5 - needed_jump; - else - encoded_val = needed_jump - 3; - output[len++] = (encoded_val / 4) + SQUOZE_DEC_OFFSET_A; - output[len++] = (encoded_val % 4) + SQUOZE_DEC_OFFSET_A; + MZ_WRITE_LE16(new_central_header + MZ_ZIP_CDH_EXTRA_LEN_OFS, new_ext_block.m_size); - offset += SQUOZE_JUMP_STRIDE * needed_jump; + if (!mz_zip_array_push_back(pZip, &pState->m_central_dir, new_central_header, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE)) + { + mz_zip_array_clear(pZip, &new_ext_block); + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); } - else + + if (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pSrc_central_header + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE, src_filename_len)) { - assert(0); // should not be reached - output[len++] = SQUOZE_ENTER_UTF5; - is_utf5 = 1; + mz_zip_array_clear(pZip, &new_ext_block); + mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE); + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + } + + if (!mz_zip_array_push_back(pZip, &pState->m_central_dir, new_ext_block.m_p, new_ext_block.m_size)) + { + mz_zip_array_clear(pZip, &new_ext_block); + mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE); + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + } + + if (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pSrc_central_header + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + src_filename_len + src_ext_len, src_comment_len)) + { + mz_zip_array_clear(pZip, &new_ext_block); + mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE); + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + } + + mz_zip_array_clear(pZip, &new_ext_block); + } + else + { + /* sanity checks */ + if (cur_dst_file_ofs > MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); + + if (local_dir_header_ofs >= MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); + + MZ_WRITE_LE32(new_central_header + MZ_ZIP_CDH_LOCAL_HEADER_OFS, local_dir_header_ofs); + + if (!mz_zip_array_push_back(pZip, &pState->m_central_dir, new_central_header, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE)) + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + + if (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pSrc_central_header + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE, src_central_dir_following_data_size)) + { + mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE); + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); } - } } - if (is_utf5) + /* This shouldn't trigger unless we screwed up during the initial sanity checks */ + if (pState->m_central_dir.m_size >= MZ_UINT32_MAX) { - int octets = 0; - offset = squoze_new_offset (val); - while (val) - { - int oval = val % 16; - int hi = 16; - if (val / 16) hi = 0; - output[len+ (octets++)] = oval + hi; - val /= 16; - } - for (int j = 0; j < octets/2; j++) // mirror in-place - { // TODO refactor to be single pass - int tmp = output[len+j]; - output[len+j] = output[len+octets-1-j]; - output[len+octets-1-j] = tmp; - } - len += octets; + /* TODO: Support central dirs >= 32-bits in size */ + mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE); + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_CDIR_SIZE); } - else + + n = (mz_uint32)orig_central_dir_size; + if (!mz_zip_array_push_back(pZip, &pState->m_central_dir_offsets, &n, 1)) { - if (val == ' ') - { - output[len++] = SQUOZE_SPACE; - } - else - { - output[len++] = val-offset+1; - } + mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE); + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); } - } - if (escape_endzero && len && output[len-1]==0) - { - if (is_utf5) - output[len++] = 16; - else - output[len++] = SQUOZE_ENTER_UTF5; - } - output[len]=0; - if (r_outlen) - *r_outlen = len; + pZip->m_total_files++; + pZip->m_archive_size = cur_dst_file_ofs; + + return MZ_TRUE; } -static inline uint64_t _squoze (int squoze_dim, const char *utf8) +mz_bool mz_zip_writer_finalize_archive(mz_zip_archive *pZip) { - char encoded[4096]=""; - int encoded_len=0; - squoze5_encode (utf8, strlen (utf8), encoded, &encoded_len, 1, 1); - uint64_t hash = 0; - int utf5 = (encoded[0] != SQUOZE_ENTER_SQUEEZE); - uint64_t multiplier = ((squoze_dim == 6) ? 0x25bd1e975 - : 0x98173415bd1e975); - - uint64_t overflowed_mask = squoze_overflow_mask_for_dim (squoze_dim); - uint64_t all_bits = overflowed_mask - 1; + mz_zip_internal_state *pState; + mz_uint64 central_dir_ofs, central_dir_size; + mz_uint8 hdr[256]; - int rshift = (squoze_dim == 6) ? 8 : 16; + if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + pState = pZip->m_pState; - if (encoded_len - (!utf5) <= squoze_dim) - { - for (int i = !utf5; i < encoded_len; i++) + if (pState->m_zip64) { - uint64_t val = encoded[i]; - hash = hash | (val << (5*(i-(!utf5)))); + if ((mz_uint64)pState->m_central_dir.m_size >= MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); } - hash <<= 1; // make room for the bit that encodes utf5 or squeeze - } - else - { - for (int i = 0; i < encoded_len; i++) + else { - uint64_t val = encoded[i]; - hash = hash ^ val; - hash = hash * multiplier; - hash = hash & all_bits; - hash = hash ^ ((hash >> rshift)); + if ((pZip->m_total_files > MZ_UINT16_MAX) || ((pZip->m_archive_size + pState->m_central_dir.m_size + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) > MZ_UINT32_MAX)) + return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); } - hash |= overflowed_mask; - } - return hash | utf5; -} -typedef struct _CashInterned CashInterned; + central_dir_ofs = 0; + central_dir_size = 0; + if (pZip->m_total_files) + { + /* Write central directory */ + central_dir_ofs = pZip->m_archive_size; + central_dir_size = pState->m_central_dir.m_size; + pZip->m_central_directory_file_ofs = central_dir_ofs; + if (pZip->m_pWrite(pZip->m_pIO_opaque, central_dir_ofs, pState->m_central_dir.m_p, (size_t)central_dir_size) != central_dir_size) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); -struct _CashInterned { - uint64_t hash; - char *string; -}; + pZip->m_archive_size += central_dir_size; + } -static CashInterned *interned = NULL; -static int n_interned = 0; -static int s_interned = 0; + if (pState->m_zip64) + { + /* Write zip64 end of central directory header */ + mz_uint64 rel_ofs_to_zip64_ecdr = pZip->m_archive_size; -static int squoze_interned_find (uint64_t hash) -{ -#if 1 - int min = 0; - int max = n_interned - 1; - if (max <= 0) - return 0; - do - { - int pos = (min + max)/2; - if (interned[pos].hash == hash) - return pos; - else if (min == max - 1) - return max; - else if (interned[pos].hash < hash) - min = pos; - else - max = pos; - } while (min != max); - return max; -#else - for (int i = 0; i < n_interned; i++) - if (interned[i].hash > hash) - return i; - return 0; -#endif -} + MZ_CLEAR_ARR(hdr); + MZ_WRITE_LE32(hdr + MZ_ZIP64_ECDH_SIG_OFS, MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIG); + MZ_WRITE_LE64(hdr + MZ_ZIP64_ECDH_SIZE_OF_RECORD_OFS, MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE - sizeof(mz_uint32) - sizeof(mz_uint64)); + MZ_WRITE_LE16(hdr + MZ_ZIP64_ECDH_VERSION_MADE_BY_OFS, 0x031E); /* TODO: always Unix */ + MZ_WRITE_LE16(hdr + MZ_ZIP64_ECDH_VERSION_NEEDED_OFS, 0x002D); + MZ_WRITE_LE64(hdr + MZ_ZIP64_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS, pZip->m_total_files); + MZ_WRITE_LE64(hdr + MZ_ZIP64_ECDH_CDIR_TOTAL_ENTRIES_OFS, pZip->m_total_files); + MZ_WRITE_LE64(hdr + MZ_ZIP64_ECDH_CDIR_SIZE_OFS, central_dir_size); + MZ_WRITE_LE64(hdr + MZ_ZIP64_ECDH_CDIR_OFS_OFS, central_dir_ofs); + if (pZip->m_pWrite(pZip->m_pIO_opaque, pZip->m_archive_size, hdr, MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE) != MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); -#ifdef __CTX_H__ -#define strdup ctx_strdup -#define strstr ctx_strstr -#endif + pZip->m_archive_size += MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE; -static inline uint64_t squoze (int squoze_dim, const char *utf8) -{ - uint64_t hash = _squoze (squoze_dim, utf8); -#ifdef SQUOZE_NO_INTERNING - return hash; -#endif - uint64_t overflowed_mask = squoze_overflow_mask_for_dim (squoze_dim); - if (hash & overflowed_mask) - { - int pos = squoze_interned_find (hash); - if (interned && interned[pos].hash == hash) - return hash; + /* Write zip64 end of central directory locator */ + MZ_CLEAR_ARR(hdr); + MZ_WRITE_LE32(hdr + MZ_ZIP64_ECDL_SIG_OFS, MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIG); + MZ_WRITE_LE64(hdr + MZ_ZIP64_ECDL_REL_OFS_TO_ZIP64_ECDR_OFS, rel_ofs_to_zip64_ecdr); + MZ_WRITE_LE32(hdr + MZ_ZIP64_ECDL_TOTAL_NUMBER_OF_DISKS_OFS, 1); + if (pZip->m_pWrite(pZip->m_pIO_opaque, pZip->m_archive_size, hdr, MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE) != MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); - if (n_interned + 1 >= s_interned) - { - s_interned = (s_interned + 128)*2; - interned = (CashInterned*)realloc (interned, s_interned * sizeof (CashInterned)); + pZip->m_archive_size += MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE; } - n_interned++; -#if 1 - if (n_interned-pos) - memmove (&interned[pos+1], &interned[pos], (n_interned-pos) * sizeof (CashInterned)); - // the memmove is the expensive part of testing for collisions - // insertions should be cheaper! at least looking up strings - // is cheap -#else - pos = n_interned-1; -#endif - { - CashInterned *entry = &interned[pos]; - entry->hash = hash; - entry->string = strdup (utf8); - } + /* Write end of central directory record */ + MZ_CLEAR_ARR(hdr); + MZ_WRITE_LE32(hdr + MZ_ZIP_ECDH_SIG_OFS, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG); + MZ_WRITE_LE16(hdr + MZ_ZIP_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS, MZ_MIN(MZ_UINT16_MAX, pZip->m_total_files)); + MZ_WRITE_LE16(hdr + MZ_ZIP_ECDH_CDIR_TOTAL_ENTRIES_OFS, MZ_MIN(MZ_UINT16_MAX, pZip->m_total_files)); + MZ_WRITE_LE32(hdr + MZ_ZIP_ECDH_CDIR_SIZE_OFS, MZ_MIN(MZ_UINT32_MAX, central_dir_size)); + MZ_WRITE_LE32(hdr + MZ_ZIP_ECDH_CDIR_OFS_OFS, MZ_MIN(MZ_UINT32_MAX, central_dir_ofs)); - } - return hash; -} + if (pZip->m_pWrite(pZip->m_pIO_opaque, pZip->m_archive_size, hdr, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) != MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); -uint32_t squoze6 (const char *utf8) -{ - return squoze (6, utf8); -} +#ifndef MINIZ_NO_STDIO + if ((pState->m_pFile) && (MZ_FFLUSH(pState->m_pFile) == EOF)) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_CLOSE_FAILED); +#endif /* #ifndef MINIZ_NO_STDIO */ -uint64_t squoze10 (const char *utf8) -{ - return squoze (10, utf8); + pZip->m_archive_size += MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE; + + pZip->m_zip_mode = MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED; + return MZ_TRUE; } -uint64_t squoze12 (const char *utf8) +mz_bool mz_zip_writer_finalize_heap_archive(mz_zip_archive *pZip, void **ppBuf, size_t *pSize) { - return squoze (12, utf8); -} + if ((!ppBuf) || (!pSize)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); -uint32_t ctx_strhash(const char *str) { - return squoze (6, str); -} + *ppBuf = NULL; + *pSize = 0; -typedef struct CashUtf5Dec { - int is_utf5; - int offset; - void *write_data; - uint32_t current; - void (*append_unichar) (uint32_t unichar, void *write_data); - int jumped_amount; - int jump_mode; -} CashUtf5Dec; + if ((!pZip) || (!pZip->m_pState)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); -typedef struct CashUtf5DecDefaultData { - uint8_t *buf; - int length; -} CashUtf5DecDefaultData; + if (pZip->m_pWrite != mz_zip_heap_write_func) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); -static void squoze_decode_utf5_append_unichar_as_utf8 (uint32_t unichar, void *write_data) + if (!mz_zip_writer_finalize_archive(pZip)) + return MZ_FALSE; + + *ppBuf = pZip->m_pState->m_pMem; + *pSize = pZip->m_pState->m_mem_size; + pZip->m_pState->m_pMem = NULL; + pZip->m_pState->m_mem_size = pZip->m_pState->m_mem_capacity = 0; + + return MZ_TRUE; +} + +mz_bool mz_zip_writer_end(mz_zip_archive *pZip) { - CashUtf5DecDefaultData *data = (CashUtf5DecDefaultData*)write_data; - int length = squoze_unichar_to_utf8 (unichar, &data->buf[data->length]); - data->buf[data->length += length] = 0; + return mz_zip_writer_end_internal(pZip, MZ_TRUE); } -static void squoze_decode_jump (CashUtf5Dec *dec, uint8_t in) +#ifndef MINIZ_NO_STDIO +mz_bool mz_zip_add_mem_to_archive_file_in_place(const char *pZip_filename, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags) { - dec->offset -= SQUOZE_JUMP_STRIDE * dec->jumped_amount; - int jump_len = (dec->jump_mode - SQUOZE_DEC_OFFSET_A) * 4 + - (in - SQUOZE_DEC_OFFSET_A); - if (jump_len > 7) - jump_len = 5 - jump_len; - else - jump_len += 3; - dec->offset += jump_len * SQUOZE_JUMP_STRIDE; - dec->jumped_amount = 0; + return mz_zip_add_mem_to_archive_file_in_place_v2(pZip_filename, pArchive_name, pBuf, buf_size, pComment, comment_size, level_and_flags, NULL); } -static void squoze_decode_utf5 (CashUtf5Dec *dec, uint8_t in) +mz_bool mz_zip_add_mem_to_archive_file_in_place_v2(const char *pZip_filename, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, mz_zip_error *pErr) { - if (dec->is_utf5) - { - if (in >= 16) + mz_bool status, created_new_archive = MZ_FALSE; + mz_zip_archive zip_archive; + struct MZ_FILE_STAT_STRUCT file_stat; + mz_zip_error actual_err = MZ_ZIP_NO_ERROR; + + mz_zip_zero_struct(&zip_archive); + if ((int)level_and_flags < 0) + level_and_flags = MZ_DEFAULT_LEVEL; + + if ((!pZip_filename) || (!pArchive_name) || ((buf_size) && (!pBuf)) || ((comment_size) && (!pComment)) || ((level_and_flags & 0xF) > MZ_UBER_COMPRESSION)) { - if (dec->current) - { - dec->offset = squoze_new_offset (dec->current); - dec->append_unichar (dec->current, dec->write_data); - dec->current = 0; - } + if (pErr) + *pErr = MZ_ZIP_INVALID_PARAMETER; + return MZ_FALSE; } - if (in == SQUOZE_ENTER_SQUEEZE) + + if (!mz_zip_writer_validate_archive_name(pArchive_name)) { - if (dec->current) - { - dec->offset = squoze_new_offset (dec->current); - dec->append_unichar (dec->current, dec->write_data); - dec->current = 0; - } - dec->is_utf5 = 0; + if (pErr) + *pErr = MZ_ZIP_INVALID_FILENAME; + return MZ_FALSE; + } + + /* Important: The regular non-64 bit version of stat() can fail here if the file is very large, which could cause the archive to be overwritten. */ + /* So be sure to compile with _LARGEFILE64_SOURCE 1 */ + if (MZ_FILE_STAT(pZip_filename, &file_stat) != 0) + { + /* Create a new archive. */ + if (!mz_zip_writer_init_file_v2(&zip_archive, pZip_filename, 0, level_and_flags)) + { + if (pErr) + *pErr = zip_archive.m_last_error; + return MZ_FALSE; + } + + created_new_archive = MZ_TRUE; } else { - dec->current = dec->current * 16 + (in % 16); + /* Append to an existing archive. */ + if (!mz_zip_reader_init_file_v2(&zip_archive, pZip_filename, level_and_flags | MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY, 0, 0)) + { + if (pErr) + *pErr = zip_archive.m_last_error; + return MZ_FALSE; + } + + if (!mz_zip_writer_init_from_reader_v2(&zip_archive, pZip_filename, level_and_flags)) + { + if (pErr) + *pErr = zip_archive.m_last_error; + + mz_zip_reader_end_internal(&zip_archive, MZ_FALSE); + + return MZ_FALSE; + } } - } - else - { - if (dec->jumped_amount) + + status = mz_zip_writer_add_mem_ex(&zip_archive, pArchive_name, pBuf, buf_size, pComment, comment_size, level_and_flags, 0, 0); + actual_err = zip_archive.m_last_error; + + /* Always finalize, even if adding failed for some reason, so we have a valid central directory. (This may not always succeed, but we can try.) */ + if (!mz_zip_writer_finalize_archive(&zip_archive)) { - switch (in) - { - case SQUOZE_DEC_OFFSET_A: - case SQUOZE_DEC_OFFSET_B: - case SQUOZE_INC_OFFSET_A: - case SQUOZE_INC_OFFSET_B: - squoze_decode_jump (dec, in); - break; - default: - dec->append_unichar (dec->offset + (in - 1), dec->write_data); - dec->jumped_amount = 0; - dec->jump_mode = 0; - break; - } + if (!actual_err) + actual_err = zip_archive.m_last_error; + + status = MZ_FALSE; } - else + + if (!mz_zip_writer_end_internal(&zip_archive, status)) { - switch (in) - { - case SQUOZE_ENTER_UTF5: - dec->is_utf5 = 1; - dec->jumped_amount = 0; - dec->jump_mode = 0; - break; - case SQUOZE_SPACE: - dec->append_unichar (' ', dec->write_data); - dec->jumped_amount = 0; - dec->jump_mode = 0; - break; - case SQUOZE_DEC_OFFSET_A: - dec->jumped_amount = -2; - dec->jump_mode = in; - dec->offset += dec->jumped_amount * SQUOZE_JUMP_STRIDE; - break; - case SQUOZE_INC_OFFSET_A: - dec->jumped_amount = 2; - dec->jump_mode = in; - dec->offset += dec->jumped_amount * SQUOZE_JUMP_STRIDE; - break; - case SQUOZE_DEC_OFFSET_B: - dec->jumped_amount = -1; - dec->jump_mode = in; - dec->offset += dec->jumped_amount * SQUOZE_JUMP_STRIDE; - break; - case SQUOZE_INC_OFFSET_B: - dec->jumped_amount = 1; - dec->jump_mode = in; - dec->offset += dec->jumped_amount * SQUOZE_JUMP_STRIDE; - break; - default: - dec->append_unichar (dec->offset + (in - 1), dec->write_data); - dec->jumped_amount = 0; - dec->jump_mode = 0; - } + if (!actual_err) + actual_err = zip_archive.m_last_error; + + status = MZ_FALSE; } - } + + if ((!status) && (created_new_archive)) + { + /* It's a new archive and something went wrong, so just delete it. */ + int ignoredStatus = MZ_DELETE_FILE(pZip_filename); + (void)ignoredStatus; + } + + if (pErr) + *pErr = actual_err; + + return status; +} + +void *mz_zip_extract_archive_file_to_heap_v2(const char *pZip_filename, const char *pArchive_name, const char *pComment, size_t *pSize, mz_uint flags, mz_zip_error *pErr) +{ + mz_uint32 file_index; + mz_zip_archive zip_archive; + void *p = NULL; + + if (pSize) + *pSize = 0; + + if ((!pZip_filename) || (!pArchive_name)) + { + if (pErr) + *pErr = MZ_ZIP_INVALID_PARAMETER; + + return NULL; + } + + mz_zip_zero_struct(&zip_archive); + if (!mz_zip_reader_init_file_v2(&zip_archive, pZip_filename, flags | MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY, 0, 0)) + { + if (pErr) + *pErr = zip_archive.m_last_error; + + return NULL; + } + + if (mz_zip_reader_locate_file_v2(&zip_archive, pArchive_name, pComment, flags, &file_index)) + { + p = mz_zip_reader_extract_to_heap(&zip_archive, file_index, pSize, flags); + } + + mz_zip_reader_end_internal(&zip_archive, p != NULL); + + if (pErr) + *pErr = zip_archive.m_last_error; + + return p; +} + +void *mz_zip_extract_archive_file_to_heap(const char *pZip_filename, const char *pArchive_name, size_t *pSize, mz_uint flags) +{ + return mz_zip_extract_archive_file_to_heap_v2(pZip_filename, pArchive_name, NULL, pSize, flags, NULL); +} + +#endif /* #ifndef MINIZ_NO_STDIO */ + +#endif /* #ifndef MINIZ_NO_ARCHIVE_WRITING_APIS */ + +/* ------------------- Misc utils */ + +mz_zip_mode mz_zip_get_mode(mz_zip_archive *pZip) +{ + return pZip ? pZip->m_zip_mode : MZ_ZIP_MODE_INVALID; +} + +mz_zip_type mz_zip_get_type(mz_zip_archive *pZip) +{ + return pZip ? pZip->m_zip_type : MZ_ZIP_TYPE_INVALID; +} + +mz_zip_error mz_zip_set_last_error(mz_zip_archive *pZip, mz_zip_error err_num) +{ + mz_zip_error prev_err; + + if (!pZip) + return MZ_ZIP_INVALID_PARAMETER; + + prev_err = pZip->m_last_error; + + pZip->m_last_error = err_num; + return prev_err; } -static void squoze_decode_utf5_bytes (int is_utf5, - const unsigned char *input, int inlen, - char *output, int *r_outlen) +mz_zip_error mz_zip_peek_last_error(mz_zip_archive *pZip) { - CashUtf5DecDefaultData append_data = {(unsigned char*)output, 0}; - CashUtf5Dec dec = {is_utf5, - squoze_new_offset('a'), - &append_data, - 0, - squoze_decode_utf5_append_unichar_as_utf8, - 0, 0 - }; - for (int i = 0; i < inlen; i++) - squoze_decode_utf5 (&dec, input[i]); - if (dec.current) - dec.append_unichar (dec.current, dec.write_data); - if (r_outlen) - *r_outlen = append_data.length; + if (!pZip) + return MZ_ZIP_INVALID_PARAMETER; + + return pZip->m_last_error; } -static const char *squoze_decode_r (int squoze_dim, uint64_t hash, char *ret, int retlen) +mz_zip_error mz_zip_clear_last_error(mz_zip_archive *pZip) { - uint64_t overflowed_mask = ((uint64_t)1<<(squoze_dim * 5 + 1)); + return mz_zip_set_last_error(pZip, MZ_ZIP_NO_ERROR); +} - if (hash & overflowed_mask) - { -#if 0 - for (int i = 0; i < n_interned; i++) +mz_zip_error mz_zip_get_last_error(mz_zip_archive *pZip) +{ + mz_zip_error prev_err; + + if (!pZip) + return MZ_ZIP_INVALID_PARAMETER; + + prev_err = pZip->m_last_error; + + pZip->m_last_error = MZ_ZIP_NO_ERROR; + return prev_err; +} + +const char *mz_zip_get_error_string(mz_zip_error mz_err) +{ + switch (mz_err) { - CashInterned *entry = &interned[i]; - if (entry->hash == hash) - return entry->string; + case MZ_ZIP_NO_ERROR: + return "no error"; + case MZ_ZIP_UNDEFINED_ERROR: + return "undefined error"; + case MZ_ZIP_TOO_MANY_FILES: + return "too many files"; + case MZ_ZIP_FILE_TOO_LARGE: + return "file too large"; + case MZ_ZIP_UNSUPPORTED_METHOD: + return "unsupported method"; + case MZ_ZIP_UNSUPPORTED_ENCRYPTION: + return "unsupported encryption"; + case MZ_ZIP_UNSUPPORTED_FEATURE: + return "unsupported feature"; + case MZ_ZIP_FAILED_FINDING_CENTRAL_DIR: + return "failed finding central directory"; + case MZ_ZIP_NOT_AN_ARCHIVE: + return "not a ZIP archive"; + case MZ_ZIP_INVALID_HEADER_OR_CORRUPTED: + return "invalid header or archive is corrupted"; + case MZ_ZIP_UNSUPPORTED_MULTIDISK: + return "unsupported multidisk archive"; + case MZ_ZIP_DECOMPRESSION_FAILED: + return "decompression failed or archive is corrupted"; + case MZ_ZIP_COMPRESSION_FAILED: + return "compression failed"; + case MZ_ZIP_UNEXPECTED_DECOMPRESSED_SIZE: + return "unexpected decompressed size"; + case MZ_ZIP_CRC_CHECK_FAILED: + return "CRC-32 check failed"; + case MZ_ZIP_UNSUPPORTED_CDIR_SIZE: + return "unsupported central directory size"; + case MZ_ZIP_ALLOC_FAILED: + return "allocation failed"; + case MZ_ZIP_FILE_OPEN_FAILED: + return "file open failed"; + case MZ_ZIP_FILE_CREATE_FAILED: + return "file create failed"; + case MZ_ZIP_FILE_WRITE_FAILED: + return "file write failed"; + case MZ_ZIP_FILE_READ_FAILED: + return "file read failed"; + case MZ_ZIP_FILE_CLOSE_FAILED: + return "file close failed"; + case MZ_ZIP_FILE_SEEK_FAILED: + return "file seek failed"; + case MZ_ZIP_FILE_STAT_FAILED: + return "file stat failed"; + case MZ_ZIP_INVALID_PARAMETER: + return "invalid parameter"; + case MZ_ZIP_INVALID_FILENAME: + return "invalid filename"; + case MZ_ZIP_BUF_TOO_SMALL: + return "buffer too small"; + case MZ_ZIP_INTERNAL_ERROR: + return "internal error"; + case MZ_ZIP_FILE_NOT_FOUND: + return "file not found"; + case MZ_ZIP_ARCHIVE_TOO_LARGE: + return "archive is too large"; + case MZ_ZIP_VALIDATION_FAILED: + return "validation failed"; + case MZ_ZIP_WRITE_CALLBACK_FAILED: + return "write calledback failed"; + case MZ_ZIP_TOTAL_ERRORS: + return "total errors"; + default: + break; } -#else - int pos = squoze_interned_find (hash); - if (!interned || (interned[pos].hash!=hash)) - return NULL; - return interned[pos].string; -#endif - return NULL; - } - uint8_t utf5[140]=""; // we newer go really high since there isnt room - // in the integers - uint64_t tmp = hash & (overflowed_mask-1); - int len = 0; - int is_utf5 = tmp & 1; - tmp /= 2; - int in_utf5 = is_utf5; - while (tmp > 0) - { - uint64_t remnant = tmp % 32; - uint64_t val = remnant; + return "unknown error"; +} - if ( in_utf5 && val == SQUOZE_ENTER_SQUEEZE) in_utf5 = 0; - else if (!in_utf5 && val == SQUOZE_ENTER_UTF5) in_utf5 = 1; +/* Note: Just because the archive is not zip64 doesn't necessarily mean it doesn't have Zip64 extended information extra field, argh. */ +mz_bool mz_zip_is_zip64(mz_zip_archive *pZip) +{ + if ((!pZip) || (!pZip->m_pState)) + return MZ_FALSE; - utf5[len++] = val; - tmp -= remnant; - tmp /= 32; - } - utf5[len]=0; - squoze_decode_utf5_bytes (is_utf5, utf5, len, ret, &retlen); - //ret[retlen]=0; - return ret; + return pZip->m_pState->m_zip64; } -/* copy the value as soon as possible, some mitigation is in place - * for more than one value in use and cross-thread interactions. - */ -static const char *squoze_decode (int squoze_dim, uint64_t hash) +size_t mz_zip_get_central_dir_size(mz_zip_archive *pZip) { -#if CTX_THREADS -#define THREAD __thread // use thread local storage - static THREAD int no = 0; - static THREAD char ret[3][256]; - no ++; - if (no > 7) no = 0; - return squoze_decode_r (squoze_dim, hash, ret[no], 256); -#undef THREAD -#else - static char ret[64]; - return squoze_decode_r (squoze_dim, hash, ret, 256); -#endif + if ((!pZip) || (!pZip->m_pState)) + return 0; + + return pZip->m_pState->m_central_dir.m_size; } -const char *squoze6_decode (uint32_t hash) +mz_uint mz_zip_reader_get_num_files(mz_zip_archive *pZip) { - return squoze_decode (6, hash); + return pZip ? pZip->m_total_files : 0; } -const char *squoze10_decode (uint64_t hash) +mz_uint64 mz_zip_get_archive_size(mz_zip_archive *pZip) { - return squoze_decode (10, hash); + if (!pZip) + return 0; + return pZip->m_archive_size; } -const char *squoze12_decode (uint64_t hash) +mz_uint64 mz_zip_get_archive_file_start_offset(mz_zip_archive *pZip) { - return squoze_decode (12, hash); + if ((!pZip) || (!pZip->m_pState)) + return 0; + return pZip->m_pState->m_file_archive_start_ofs; } -static inline uint32_t -squoze_utf8_to_unichar (const char *input) +MZ_FILE *mz_zip_get_cfile(mz_zip_archive *pZip) { - const uint8_t *utf8 = (const uint8_t *) input; - uint8_t c = utf8[0]; - if ( (c & 0x80) == 0) - { return c; } - else if ( (c & 0xE0) == 0xC0) - return ( (utf8[0] & 0x1F) << 6) | - (utf8[1] & 0x3F); - else if ( (c & 0xF0) == 0xE0) - return ( (utf8[0] & 0xF) << 12) | - ( (utf8[1] & 0x3F) << 6) | - (utf8[2] & 0x3F); - else if ( (c & 0xF8) == 0xF0) - return ( (utf8[0] & 0x7) << 18) | - ( (utf8[1] & 0x3F) << 12) | - ( (utf8[2] & 0x3F) << 6) | - (utf8[3] & 0x3F); - else if ( (c & 0xFC) == 0xF8) - return ( (utf8[0] & 0x3) << 24) | - ( (utf8[1] & 0x3F) << 18) | - ( (utf8[2] & 0x3F) << 12) | - ( (utf8[3] & 0x3F) << 6) | - (utf8[4] & 0x3F); - else if ( (c & 0xFE) == 0xFC) - return ( (utf8[0] & 0x1) << 30) | - ( (utf8[1] & 0x3F) << 24) | - ( (utf8[2] & 0x3F) << 18) | - ( (utf8[3] & 0x3F) << 12) | - ( (utf8[4] & 0x3F) << 6) | - (utf8[5] & 0x3F); - return 0; + if ((!pZip) || (!pZip->m_pState)) + return 0; + return pZip->m_pState->m_pFile; } -static inline int -squoze_unichar_to_utf8 (uint32_t ch, - uint8_t *dest) + +size_t mz_zip_read_archive_data(mz_zip_archive *pZip, mz_uint64 file_ofs, void *pBuf, size_t n) { - /* http://www.cprogramming.com/tutorial/utf8.c */ - /* Basic UTF-8 manipulation routines - by Jeff Bezanson - placed in the public domain Fall 2005 ... */ - if (ch < 0x80) - { - dest[0] = (char) ch; - return 1; - } - if (ch < 0x800) + if ((!pZip) || (!pZip->m_pState) || (!pBuf) || (!pZip->m_pRead)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + + return pZip->m_pRead(pZip->m_pIO_opaque, file_ofs, pBuf, n); +} + +mz_uint mz_zip_reader_get_filename(mz_zip_archive *pZip, mz_uint file_index, char *pFilename, mz_uint filename_buf_size) +{ + mz_uint n; + const mz_uint8 *p = mz_zip_get_cdh(pZip, file_index); + if (!p) { - dest[0] = (ch>>6) | 0xC0; - dest[1] = (ch & 0x3F) | 0x80; - return 2; + if (filename_buf_size) + pFilename[0] = '\0'; + mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); + return 0; } - if (ch < 0x10000) + n = MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS); + if (filename_buf_size) { - dest[0] = (ch>>12) | 0xE0; - dest[1] = ( (ch>>6) & 0x3F) | 0x80; - dest[2] = (ch & 0x3F) | 0x80; - return 3; + n = MZ_MIN(n, filename_buf_size - 1); + memcpy(pFilename, p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE, n); + pFilename[n] = '\0'; } - if (ch < 0x110000) + return n + 1; +} + +mz_bool mz_zip_reader_file_stat(mz_zip_archive *pZip, mz_uint file_index, mz_zip_archive_file_stat *pStat) +{ + return mz_zip_file_stat_internal(pZip, file_index, mz_zip_get_cdh(pZip, file_index), pStat, NULL); +} + +mz_bool mz_zip_end(mz_zip_archive *pZip) +{ + if (!pZip) + return MZ_FALSE; + + if (pZip->m_zip_mode == MZ_ZIP_MODE_READING) + return mz_zip_reader_end(pZip); +#ifndef MINIZ_NO_ARCHIVE_WRITING_APIS + else if ((pZip->m_zip_mode == MZ_ZIP_MODE_WRITING) || (pZip->m_zip_mode == MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED)) + return mz_zip_writer_end(pZip); +#endif + + return MZ_FALSE; +} + +#ifdef __cplusplus +} +#endif + +#endif /*#ifndef MINIZ_NO_ARCHIVE_APIS*/ +/* atty - audio interface and driver for terminals + * Copyright (C) 2020 Øyvind Kolås <pippin@gimp.org> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see <http://www.gnu.org/licenses/>. + */ + +static const char *base64_map="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; +static void bin2base64_group (const unsigned char *in, int remaining, char *out) +{ + unsigned char digit[4] = {0,0,64,64}; + int i; + digit[0] = in[0] >> 2; + digit[1] = ((in[0] & 0x03) << 4) | ((in[1] & 0xf0) >> 4); + if (remaining > 1) { - dest[0] = (ch>>18) | 0xF0; - dest[1] = ( (ch>>12) & 0x3F) | 0x80; - dest[2] = ( (ch>>6) & 0x3F) | 0x80; - dest[3] = (ch & 0x3F) | 0x80; - return 4; + digit[2] = ((in[1] & 0x0f) << 2) | ((in[2] & 0xc0) >> 6); + if (remaining > 2) + digit[3] = ((in[2] & 0x3f)); } - return 0; + for (i = 0; i < 4; i++) + out[i] = base64_map[digit[i]]; } -static inline int -squoze_utf8_len (const unsigned char first_byte) +void +ctx_bin2base64 (const void *bin, + size_t bin_length, + char *ascii) { - if ( (first_byte & 0x80) == 0) - { return 1; } /* ASCII */ - else if ( (first_byte & 0xE0) == 0xC0) - { return 2; } - else if ( (first_byte & 0xF0) == 0xE0) - { return 3; } - else if ( (first_byte & 0xF8) == 0xF0) - { return 4; } - return 1; + /* this allocation is a hack to ensure we always produce the same result, + * regardless of padding data accidentally taken into account. + */ + unsigned char *bin2 = (unsigned char*)ctx_calloc (bin_length + 4, 1); + unsigned const char *p = bin2; + unsigned int i; + if (bin_length > 128 * 1024 * 1024) return; + memcpy (bin2, bin, (size_t)bin_length); + for (i=0; i*3 < bin_length; i++) + { + int remaining = bin_length - i*3; + bin2base64_group (&p[i*3], remaining, &ascii[i*4]); + } + ctx_free (bin2); + ascii[i*4]=0; } -#endif +static unsigned char base64_revmap[255]; +static void base64_revmap_init (void) +{ + static int done = 0; + if (done) + return; + + for (int i = 0; i < 255; i ++) + base64_revmap[i]=255; + for (int i = 0; i < 64; i ++) + base64_revmap[((const unsigned char*)base64_map)[i]]=i; + /* include variants used in URI encodings for decoder, + * even if that is not how we encode + */ + base64_revmap['-']=62; + base64_revmap['_']=63; + base64_revmap['+']=62; + base64_revmap['/']=63; + + done = 1; +} + + +int +ctx_base642bin (const char *ascii, + int *length, + unsigned char *bin) +{ + int i; + int charno = 0; + int outputno = 0; + int carry = 0; + base64_revmap_init (); + for (i = 0; ascii[i]; i++) + { + int bits = base64_revmap[((const unsigned char*)ascii)[i]]; + if (length && outputno > *length) + { + *length = -1; + return -1; + } + if (bits != 255) + { + switch (charno % 4) + { + case 0: + carry = bits; + break; + case 1: + bin[outputno] = (carry << 2) | (bits >> 4); + outputno++; + carry = bits & 15; + break; + case 2: + bin[outputno] = (carry << 4) | (bits >> 2); + outputno++; + carry = bits & 3; + break; + case 3: + bin[outputno] = (carry << 6) | bits; + outputno++; + carry = 0; + break; + } + charno++; + } + } + bin[outputno]=0; + if (length) + *length= outputno; + return outputno; +} static inline int @@ -22289,7 +31898,7 @@ ctx_iterator_next (CtxIterator *iterator) #if CTX_BITPACK int expand_bitpack = iterator->flags & CTX_ITERATOR_EXPAND_BITPACK; again: - if (CTX_UNLIKELY(iterator->bitpack_length)) + if (CTX_UNLIKELY(expand_bitpack && iterator->bitpack_length)) { ret = &iterator->bitpack_command[iterator->bitpack_pos]; iterator->bitpack_pos += (ctx_conts_for_entry (ret) + 1); @@ -22690,6 +32299,14 @@ const CtxEntry *ctx_get_drawlist (Ctx *ctx, int *count) return ctx->drawlist.entries; } +void ctx_drawlist_force_count (Ctx *ctx, int count) +{ + if ((int)ctx->drawlist.count < count) + return; + ctx->drawlist.count = count; +} + + int ctx_add_data (Ctx *ctx, void *data, int length) { @@ -23800,13 +33417,13 @@ static void *ctx_alsa_audio_start(Ctx *ctx) if (c > 0) { int i; + uint16_t left = 0, right = 0; for (i = 0; i < c && ctx_pcm_cur_left; i ++) { if (ctx_pcm_list && ctx_pcm_cur_left) // XXX this line can be removed { uint32_t *packet_sizep = (ctx_pcm_list->data); uint32_t packet_size = *packet_sizep; - uint16_t left = 0, right = 0; if (is_float) { @@ -23847,6 +33464,13 @@ static void *ctx_alsa_audio_start(Ctx *ctx) } } } + for (;i < c; i ++) + { + /* slight click protection in case we were not left at dc */ + data[i * 2 + 0] = (left *= 0.5f); + data[i * 2 + 1] = (right *= 0.5f); + } + c = snd_pcm_writei(h, data, c); if (c < 0) @@ -23975,7 +33599,7 @@ void ctx_ctx_pcm (Ctx *ctx) int encoded_len = ctx_a85enc (data, encoded, i); fprintf (stdout, "\033_Af=%i;", i); fwrite (encoded, 1, encoded_len, stdout); - fwrite ("\e\\", 1, 2, stdout); + fwrite ("\033\\", 1, 2, stdout); fflush (stdout); } } @@ -24815,7 +34439,7 @@ static int ctx_ydec (const char *tmp_src, char *dst, int count) dst[out_len++] = o; break; case '\n': - case '\e': + case '\033': case '\r': case '\0': break; @@ -25011,12 +34635,12 @@ static float ctx_state_get (CtxState *state, uint32_t hash) static void ctx_state_set (CtxState *state, uint32_t key, float value) { - if (key != CTX_newState) + if (key != SQZ_newState) { if (ctx_state_get (state, key) == value) { return; } for (int i = state->gstate.keydb_pos-1; - i >= 0 && state->keydb[i].key != CTX_newState; + i >= 0 && state->keydb[i].key != SQZ_newState; i--) { if (state->keydb[i].key == key) @@ -25134,11 +34758,14 @@ static void ctx_state_set_string (CtxState *state, uint32_t key, const char *str static int ctx_state_get_color (CtxState *state, uint32_t key, CtxColor *color) { CtxColor *stored = (CtxColor*)ctx_state_get_blob (state, key); + CtxColor copy; if (stored) { - if (stored->magic == 127) + // we make a copy to ensure alignment + memcpy (©, stored, sizeof (CtxColor)); + if (copy.magic == 127) { - *color = *stored; + *color = copy; return 0; } } @@ -25888,50 +35515,28 @@ typedef struct ColorDef { float a; } ColorDef; -#if 0 -#define CTX_silver CTX_STRH('s','i','l','v','e','r',0,0,0,0,0,0,0,0) -#define CTX_fuchsia CTX_STRH('f','u','c','h','s','i','a',0,0,0,0,0,0,0) -#define CTX_gray CTX_STRH('g','r','a','y',0,0,0,0,0,0,0,0,0,0) -#define CTX_yellow CTX_STRH('y','e','l','l','o','w',0,0,0,0,0,0,0,0) -#define CTX_white CTX_STRH('w','h','i','t','e',0,0,0,0,0,0,0,0,0) -#define CTX_maroon CTX_STRH('m','a','r','o','o','n',0,0,0,0,0,0,0,0) -#define CTX_magenta CTX_STRH('m','a','g','e','n','t','a',0,0,0,0,0,0,0) -#define CTX_blue CTX_STRH('b','l','u','e',0,0,0,0,0,0,0,0,0,0) -#define CTX_green CTX_STRH('g','r','e','e','n',0,0,0,0,0,0,0,0,0) -#define CTX_red CTX_STRH('r','e','d',0,0,0,0,0,0,0,0,0,0,0) -#define CTX_purple CTX_STRH('p','u','r','p','l','e',0,0,0,0,0,0,0,0) -#define CTX_olive CTX_STRH('o','l','i','v','e',0,0,0,0,0,0,0,0,0) -#define CTX_teal CTX_STRH('t','e','a','l',0,0,0,0,0,0,0,0,0,0) -#define CTX_black CTX_STRH('b','l','a','c','k',0,0,0,0,0,0,0,0,0) -#define CTX_cyan CTX_STRH('c','y','a','n',0,0,0,0,0,0,0,0,0,0) -#define CTX_navy CTX_STRH('n','a','v','y',0,0,0,0,0,0,0,0,0,0) -#define CTX_lime CTX_STRH('l','i','m','e',0,0,0,0,0,0,0,0,0,0) -#define CTX_aqua CTX_STRH('a','q','u','a',0,0,0,0,0,0,0,0,0,0) -#define CTX_transparent CTX_STRH('t','r','a','n','s','p','a','r','e','n','t',0,0,0) -#endif - static ColorDef _ctx_colors[]={ - {CTX_black, 0, 0, 0, 1}, - {CTX_red, 1, 0, 0, 1}, - {CTX_green, 0, 1, 0, 1}, - {CTX_yellow, 1, 1, 0, 1}, - {CTX_blue, 0, 0, 1, 1}, - {CTX_fuchsia, 1, 0, 1, 1}, - {CTX_cyan, 0, 1, 1, 1}, - {CTX_white, 1, 1, 1, 1}, - {CTX_silver, 0.75294f, 0.75294f, 0.75294f, 1}, - {CTX_gray, 0.50196f, 0.50196f, 0.50196f, 1}, - {CTX_magenta, 0.50196f, 0, 0.50196f, 1}, - {CTX_maroon, 0.50196f, 0, 0, 1}, - {CTX_purple, 0.50196f, 0, 0.50196f, 1}, - {CTX_green, 0, 0.50196f, 0, 1}, - {CTX_lime, 0, 1, 0, 1}, - {CTX_olive, 0.50196f, 0.50196f, 0, 1}, - {CTX_navy, 0, 0, 0.50196f, 1}, - {CTX_teal, 0, 0.50196f, 0.50196f, 1}, - {CTX_aqua, 0, 1, 1, 1}, - {CTX_transparent, 0, 0, 0, 0}, - {CTX_none, 0, 0, 0, 0}, + {SQZ_black, 0, 0, 0, 1}, + {SQZ_red, 1, 0, 0, 1}, + {SQZ_green, 0, 1, 0, 1}, + {SQZ_yellow, 1, 1, 0, 1}, + {SQZ_blue, 0, 0, 1, 1}, + {SQZ_fuchsia, 1, 0, 1, 1}, + {SQZ_cyan, 0, 1, 1, 1}, + {SQZ_white, 1, 1, 1, 1}, + {SQZ_silver, 0.75294f, 0.75294f, 0.75294f, 1}, + {SQZ_gray, 0.50196f, 0.50196f, 0.50196f, 1}, + {SQZ_magenta, 0.50196f, 0, 0.50196f, 1}, + {SQZ_maroon, 0.50196f, 0, 0, 1}, + {SQZ_purple, 0.50196f, 0, 0.50196f, 1}, + {SQZ_green, 0, 0.50196f, 0, 1}, + {SQZ_lime, 0, 1, 0, 1}, + {SQZ_olive, 0.50196f, 0.50196f, 0, 1}, + {SQZ_navy, 0, 0, 0.50196f, 1}, + {SQZ_teal, 0, 0.50196f, 0.50196f, 1}, + {SQZ_aqua, 0, 1, 1, 1}, + {SQZ_transparent, 0, 0, 0, 0}, + {SQZ_none, 0, 0, 0, 0}, }; static int xdigit_value(const char xdigit) @@ -26056,11 +35661,12 @@ int ctx_color_set_from_string (Ctx *ctx, CtxColor *color, const char *string) // return 0; //rgba[0], rgba[1], rgba[2], rgba[3]); - if (hash == CTX_currentColor) + if (hash == SQZ_currentColor) { float rgba[4]; CtxColor ccolor; - ctx_get_color (ctx, CTX_color, &ccolor); + memset (&ccolor, 0, sizeof (CtxColor)); + ctx_get_color (ctx, SQZ_color, &ccolor); ctx_color_get_rgba (&(ctx->state), &ccolor, rgba); ctx_color_set_rgba (&(ctx->state), color, rgba[0], rgba[1], rgba[2], rgba[3]); return 0; @@ -26636,11 +36242,13 @@ ctx_utf8_to_unichar (const char *input) #if CTX_TERMINAL_EVENTS #if !__COSMOPOLITAN__ -#include <termios.h> #include <fcntl.h> +#if CTX_PTY +#include <termios.h> #include <sys/ioctl.h> #endif +#endif #if 0 int ctx_terminal_width (void) @@ -26656,7 +36264,7 @@ int ctx_terminal_width (void) raw.c_cc[VMIN] = 1; raw.c_cc[VTIME] = 0; /* 1 byte, no timer */ if (tcsetattr (STDIN_FILENO, TCSAFLUSH, &raw) < 0) return 0; - fprintf (stderr, "\e[14t"); + fprintf (stderr, "\033[14t"); //tcflush(STDIN_FILENO, 1); #if __COSMOPOLITAN__ /// XXX ? @@ -26706,7 +36314,7 @@ int ctx_terminal_height (void) raw.c_cc[VMIN] = 1; raw.c_cc[VTIME] = 0; /* 1 byte, no timer */ if (tcsetattr (STDIN_FILENO, TCSAFLUSH, &raw) < 0) return 0; - fprintf (stderr, "\e[14t"); + fprintf (stderr, "\033[14t"); //tcflush(STDIN_FILENO, 1); #if !__COSMOPOLITAN__ tcdrain(STDIN_FILENO); @@ -26744,36 +36352,52 @@ int ctx_terminal_height (void) int ctx_terminal_width (void) { +#if CTX_PTY struct winsize ws; if (ioctl(0,TIOCGWINSZ,&ws)!=0) return 640; return ws.ws_xpixel; +#else + return 240; +#endif } int ctx_terminal_height (void) { +#if CTX_PTY struct winsize ws; if (ioctl(0,TIOCGWINSZ,&ws)!=0) return 450; return ws.ws_ypixel; +#else + return 240; +#endif } #endif int ctx_terminal_cols (void) { +#if CTX_PTY struct winsize ws; if (ioctl(0,TIOCGWINSZ,&ws)!=0) return 80; return ws.ws_col; +#else + return 40; +#endif } int ctx_terminal_rows (void) { +#if CTX_PTY struct winsize ws; if (ioctl(0,TIOCGWINSZ,&ws)!=0) return 25; return ws.ws_row; +#else + return 16; +#endif } @@ -26792,7 +36416,9 @@ int ctx_terminal_rows (void) /*************************** input handling *************************/ #if !__COSMOPOLITAN__ +#if CTX_PTY #include <termios.h> +#endif #include <errno.h> #include <signal.h> #endif @@ -26804,7 +36430,9 @@ int ctx_terminal_rows (void) #endif static int size_changed = 0; /* XXX: global state */ +#if CTX_PTY static int ctx_term_signal_installed = 0; /* XXX: global state */ +#endif static const char *mouse_modes[]= {TERMINAL_MOUSE_OFF, @@ -26988,16 +36616,19 @@ static const NcKeyCode keycodes[]={ {"ok", "", "\033[0n"}, {NULL, } }; - +#if CTX_PTY static struct termios orig_attr; /* in order to restore at exit */ static int nc_is_raw = 0; static int atexit_registered = 0; +#endif static int mouse_mode = NC_MOUSE_NONE; static void _nc_noraw (void) { +#if CTX_PTY if (nc_is_raw && tcsetattr (STDIN_FILENO, TCSAFLUSH, &orig_attr) != -1) nc_is_raw = 0; +#endif } void @@ -27006,10 +36637,10 @@ nc_at_exit (void) printf (TERMINAL_MOUSE_OFF); printf (XTERM_ALTSCREEN_OFF); _nc_noraw(); - fprintf (stdout, "\e[?25h"); + fprintf (stdout, "\033[?25h"); //if (ctx_native_events) - fprintf (stdout, "\e[?201l"); - fprintf (stdout, "\e[?1049l"); + fprintf (stdout, "\033[?201l"); + fprintf (stdout, "\033[?1049l"); } static const char *mouse_get_event_int (Ctx *n, int *x, int *y) @@ -27126,7 +36757,7 @@ static int mouse_has_event (Ctx *n) return retval != 0; } - +#if CTX_PTY static int _nc_raw (void) { struct termios raw; @@ -27153,6 +36784,7 @@ static int _nc_raw (void) #endif return 0; } +#endif static int match_keycode (const char *buf, int length, const NcKeyCode **ret) { @@ -27204,13 +36836,11 @@ int ctx_nct_has_event (Ctx *n, int delay_ms) const char *ctx_nct_get_event (Ctx *n, int timeoutms, int *x, int *y) { - unsigned char buf[20]; - int length; - - if (x) *x = -1; if (y) *y = -1; - +#if CTX_PTY + unsigned char buf[20]; + int length; if (!ctx_term_signal_installed) { _nc_raw (); @@ -27372,6 +37002,9 @@ const char *ctx_nct_get_event (Ctx *n, int timeoutms, int *x, int *y) else return "key read eek"; return "fail"; +#else + return "NYI."; +#endif } void ctx_nct_consume_events (Ctx *ctx) @@ -27457,6 +37090,7 @@ void ctx_nct_consume_events (Ctx *ctx) const char *ctx_native_get_event (Ctx *n, int timeoutms) { +#if CTX_PTY static unsigned char buf[256]; int length; @@ -27501,7 +37135,7 @@ const char *ctx_native_get_event (Ctx *n, int timeoutms) if (read (STDIN_FILENO, &buf[length], 1) != -1) { buf[length+1] = 0; - if (!strcmp ((char*)buf, "\e[0n")) + if (!strcmp ((char*)buf, "\033[0n")) { ctx_frame_ack = 1; return NULL; @@ -27514,6 +37148,7 @@ const char *ctx_native_get_event (Ctx *n, int timeoutms) } got_event = ctx_nct_has_event (n, 5); } +#endif return NULL; } @@ -27553,8 +37188,20 @@ void _ctx_mouse (Ctx *term, int mode) #define usecs(time) ((uint64_t)(time.tv_sec - start_time.tv_sec) * 1000000 + time. tv_usec) #if !__COSMOPOLITAN__ -static struct timeval start_time; +#if CTX_PTY==0 +#include "pico/stdlib.h" +#include "hardware/timer.h" +static uint64_t pico_get_time(void) { + // Reading low latches the high value + uint32_t lo = timer_hw->timelr; + uint32_t hi = timer_hw->timehr; + return ((uint64_t) hi << 32u) | lo; +} +static uint64_t start_time; +#else +static struct timeval start_time; +#endif static void _ctx_init_ticks (void) { @@ -27562,15 +37209,24 @@ _ctx_init_ticks (void) if (done) return; done = 1; +#if CTX_PTY==0 + start_time = pico_get_time(); +#else gettimeofday (&start_time, NULL); +#endif } static inline unsigned long _ctx_ticks (void) { +#if CTX_PTY==0 + uint64_t measure_time = pico_get_time(); + return measure_time - start_time; +#else struct timeval measure_time; gettimeofday (&measure_time, NULL); return usecs (measure_time) - usecs (start_time); +#endif } CTX_EXPORT unsigned long @@ -27654,8 +37310,10 @@ void ctx_list_backends(void) #if CTX_FB fprintf (stderr, " fb"); #endif -#if CTX_TERMINAL_EVENTS +#if CTX_TERM fprintf (stderr, " term"); +#endif +#if CTX_TERMIMG fprintf (stderr, " termimg"); #endif fprintf (stderr, "\n"); @@ -27671,6 +37329,7 @@ static uint32_t ctx_ms (Ctx *ctx) static int is_in_ctx (void) { +#if CTX_PTY char buf[1024]; struct termios orig_attr; struct termios raw; @@ -27682,7 +37341,7 @@ static int is_in_ctx (void) raw.c_cc[VMIN] = 1; raw.c_cc[VTIME] = 0; /* 1 byte, no timer */ if (tcsetattr (STDIN_FILENO, TCSAFLUSH, &raw) < 0) return 0; - fprintf (stderr, "\e[?200$p"); + fprintf (stderr, "\033[?200$p"); //tcflush(STDIN_FILENO, 1); #if !__COSMOPOLITAN__ tcdrain(STDIN_FILENO); @@ -27713,11 +37372,12 @@ static int is_in_ctx (void) { return 1; } +#endif return 0; } #endif -#if CTX_WASM_ORIG // Renamed from upstream, as we don't want this code to be compiled in, even though we're using Emscripten. +#if EMSCRIPTEN CTX_EXPORT Ctx * ctx_wasm_get_context (int flags); @@ -27790,6 +37450,7 @@ static Ctx *ctx_new_ui (int width, int height, const char *backend) } Ctx *ret = NULL; +#if CTX_FORMATTER #if CTX_TERMINAL_EVENTS /* we do the query on auto but not on directly set ctx * @@ -27804,6 +37465,7 @@ static Ctx *ctx_new_ui (int width, int height, const char *backend) } } #endif +#endif #if CTX_TERMINAL_EVENTS #if CTX_HEADLESS @@ -27843,17 +37505,21 @@ static Ctx *ctx_new_ui (int width, int height, const char *backend) #if CTX_TERMINAL_EVENTS #if CTX_RASTERIZER // braille in terminal +#if CTX_TERM if (!ret) { if ((backend==NULL) || (!ctx_strcmp (backend, "term"))) ret = ctx_new_term (width, height); } +#endif +#if CTX_TERMIMG if (!ret) { if ((backend==NULL) || (!ctx_strcmp (backend, "termimg"))) ret = ctx_new_termimg (width, height); } #endif +#endif #endif if (!ret) { @@ -28454,7 +38120,7 @@ void ctx_listen (Ctx *ctx, if (types == CTX_DRAG_MOTION) types = CTX_DRAG_MOTION | CTX_DRAG_PRESS; - return ctx_listen_full (ctx, x, y, width, height, types, cb, data1, data2, NULL, NULL); + ctx_listen_full (ctx, x, y, width, height, types, cb, data1, data2, NULL, NULL); } void ctx_listen_with_finalize (Ctx *ctx, @@ -28487,7 +38153,7 @@ void ctx_listen_with_finalize (Ctx *ctx, if (types == CTX_DRAG_MOTION) types = CTX_DRAG_MOTION | CTX_DRAG_PRESS; - return ctx_listen_full (ctx, x, y, width, height, types, cb, data1, data2, finalize, finalize_data); + ctx_listen_full (ctx, x, y, width, height, types, cb, data1, data2, finalize, finalize_data); } @@ -28517,9 +38183,9 @@ void ctx_add_hit_region (Ctx *ctx, const char *id) height = ey2 - ey1; } - return ctx_listen_full (ctx, x, y, width, height, - CTX_POINTER, ctx_report_hit_region, - id_copy, NULL, (void*)ctx_free, NULL); + ctx_listen_full (ctx, x, y, width, height, + CTX_POINTER, ctx_report_hit_region, + id_copy, NULL, (void*)ctx_free, NULL); } typedef struct _CtxGrab CtxGrab; @@ -28655,17 +38321,18 @@ CtxItem *_ctx_detect (Ctx *ctx, float x, float y, CtxEventType type) return NULL; } +static CtxEvent event_copy; + static int _ctx_emit_cb_item (Ctx *ctx, CtxItem *item, CtxEvent *event, CtxEventType type, float x, float y) { - static CtxEvent s_event; CtxEvent transformed_event; int i; if (!event) { - event = &s_event; + event = &event_copy; event->type = type; event->x = x; event->y = y; @@ -28746,9 +38413,9 @@ void ctx_get_event_fds (Ctx *ctx, int *fd, int *count) *count = 0; } + CtxEvent *ctx_get_event (Ctx *ctx) { - static CtxEvent event_copy; if (ctx->events.events) { event_copy = *((CtxEvent*)(ctx->events.events->data)); @@ -29358,6 +39025,7 @@ static const char *ctx_keycode_to_keyname (CtxModifierState modifier_state, temp[0]=keycode-65+'A'; else temp[0]=keycode-65+'a'; + temp[1]=0; } else if (keycode >= 112 && keycode <= 123) { @@ -29440,6 +39108,7 @@ static const char *ctx_keycode_to_keyname (CtxModifierState modifier_state, if (keycode >= 48 && keycode <=66) { temp[0]=keycode-48+'0'; + temp[1]=0; } else { @@ -29851,6 +39520,14 @@ int ctx_get_render_threads (Ctx *ctx) { return _ctx_max_threads; } +#else +void ctx_set_render_threads (Ctx *ctx, int n_threads) +{ +} +int ctx_get_render_threads (Ctx *ctx) +{ + return 1; +} #endif void ctx_set_hash_cache (Ctx *ctx, int enable_hash_cache) { @@ -29864,7 +39541,7 @@ int ctx_get_hash_cache (Ctx *ctx) int ctx_need_redraw (Ctx *ctx) { return (ctx->dirty != 0) -#if CTX_CLIENTS +#if CTX_VT || ctx_clients_need_redraw (ctx) #endif ; @@ -29903,9 +39580,14 @@ void _ctx_remove_listen_fd (int fd) #ifdef EMSCRIPTEN extern int em_in_len; #endif +#if CTX_VT +extern int ctx_dummy_in_len; +#endif int ctx_input_pending (Ctx *ctx, int timeout) { + int retval = 0; +#if CTX_PTY struct timeval tv; fd_set fdset; FD_ZERO (&fdset); @@ -29924,21 +39606,27 @@ int ctx_input_pending (Ctx *ctx, int timeout) tv.tv_usec = timeout; tv.tv_sec = timeout / 1000000; tv.tv_usec = timeout % 1000000; - int retval = select (_ctx_listen_max_fd + 1, &fdset, NULL, NULL, &tv); + retval = select (_ctx_listen_max_fd + 1, &fdset, NULL, NULL, &tv); if (retval == -1) { +#if CTX_BAREMETAL==0 perror ("select"); +#endif return 0; } +#endif #ifdef EMSCRIPTEN retval += em_in_len; +#endif +#if CTX_VT + retval += ctx_dummy_in_len; #endif return retval; } void ctx_handle_events (Ctx *ctx) { -#if CTX_CLIENTS +#if CTX_VT ctx_clients_handle_events (ctx); #endif while (ctx_get_event (ctx)){} @@ -29967,10 +39655,10 @@ static void ctx_events_deinit (Ctx *ctx) #if CTX_TERMINAL_EVENTS - -static int mice_has_event (); -static char *mice_get_event (); -static void mice_destroy (); +#if CTX_PTY +static int mice_has_event (void); +static char *mice_get_event (void); +static void mice_destroy (void); static int mice_get_fd (EvSource *ev_source); static void mice_set_coord (EvSource *ev_source, double x, double y); @@ -30019,13 +39707,13 @@ static int mmm_evsource_mice_init () return 0; } -static void mice_destroy () +static void mice_destroy (void) { if (mrg_mice_this->fd != -1) close (mrg_mice_this->fd); } -static int mice_has_event () +static int mice_has_event (void) { struct timeval tv; int retval; @@ -30043,7 +39731,7 @@ static int mice_has_event () return 0; } -static char *mice_get_event () +static char *mice_get_event (void) { const char *ret = "pm"; double relx, rely; @@ -30191,6 +39879,7 @@ static inline EvSource *evsource_mice_new (void) } return NULL; } +#endif static int evsource_kb_term_has_event (void); static char *evsource_kb_term_get_event (void); @@ -30207,10 +39896,13 @@ static EvSource ctx_ev_src_kb_term = { NULL }; +#if CTX_PTY static struct termios orig_attr; +#endif static void real_evsource_kb_term_destroy (int sign) { +#if CTX_PTY static int done = 0; if (sign == 0) @@ -30235,6 +39927,7 @@ static void real_evsource_kb_term_destroy (int sign) } tcsetattr (STDIN_FILENO, TCSAFLUSH, &orig_attr); //fprintf (stderr, "evsource kb destroy\n"); +#endif } static void evsource_kb_term_destroy (int sign) @@ -30244,6 +39937,7 @@ static void evsource_kb_term_destroy (int sign) static int evsource_kb_term_init () { +#if CTX_PTY // ioctl(STDIN_FILENO, KDSKBMODE, K_RAW); //atexit ((void*) real_evsource_kb_term_destroy); signal (SIGSEGV, (void*) real_evsource_kb_term_destroy); @@ -30267,19 +39961,20 @@ static int evsource_kb_term_init () raw.c_cc[VMIN] = 1; raw.c_cc[VTIME] = 0; /* 1 byte, no timer */ if (tcsetattr (STDIN_FILENO, TCSAFLUSH, &raw) < 0) return 0; // XXX? return other value? - +#endif return 0; } static int evsource_kb_term_has_event (void) { + int retval = 0; +#if CTX_PTY struct timeval tv; - int retval; - fd_set rfds; FD_ZERO (&rfds); FD_SET(STDIN_FILENO, &rfds); tv.tv_sec = 0; tv.tv_usec = 0; retval = select (STDIN_FILENO+1, &rfds, NULL, NULL, &tv); +#endif return retval == 1; } @@ -30293,93 +39988,93 @@ typedef struct MmmKeyCode { char sequence[10]; /* terminal sequence */ } MmmKeyCode; static const MmmKeyCode ufb_keycodes[]={ - {"up", "\e[A"}, - {"down", "\e[B"}, - {"right", "\e[C"}, - {"left", "\e[D"}, - - {"shift-up", "\e[1;2A"}, - {"shift-down", "\e[1;2B"}, - {"shift-right", "\e[1;2C"}, - {"shift-left", "\e[1;2D"}, - - {"alt-up", "\e[1;3A"}, - {"alt-down", "\e[1;3B"}, - {"alt-right", "\e[1;3C"}, - {"alt-left", "\e[1;3D"}, - {"alt-shift-up", "\e[1;4A"}, - {"alt-shift-down", "\e[1;4B"}, - {"alt-shift-right", "\e[1;4C"}, - {"alt-shift-left", "\e[1;4D"}, - - {"control-up", "\e[1;5A"}, - {"control-down", "\e[1;5B"}, - {"control-right", "\e[1;5C"}, - {"control-left", "\e[1;5D"}, + {"up", "\033[A"}, + {"down", "\033[B"}, + {"right", "\033[C"}, + {"left", "\033[D"}, + + {"shift-up", "\033[1;2A"}, + {"shift-down", "\033[1;2B"}, + {"shift-right", "\033[1;2C"}, + {"shift-left", "\033[1;2D"}, + + {"alt-up", "\033[1;3A"}, + {"alt-down", "\033[1;3B"}, + {"alt-right", "\033[1;3C"}, + {"alt-left", "\033[1;3D"}, + {"alt-shift-up", "\033[1;4A"}, + {"alt-shift-down", "\033[1;4B"}, + {"alt-shift-right", "\033[1;4C"}, + {"alt-shift-left", "\033[1;4D"}, + + {"control-up", "\033[1;5A"}, + {"control-down", "\033[1;5B"}, + {"control-right", "\033[1;5C"}, + {"control-left", "\033[1;5D"}, /* putty */ - {"control-up", "\eOA"}, - {"control-down", "\eOB"}, - {"control-right", "\eOC"}, - {"control-left", "\eOD"}, - - {"control-shift-up", "\e[1;6A"}, - {"control-shift-down", "\e[1;6B"}, - {"control-shift-right", "\e[1;6C"}, - {"control-shift-left", "\e[1;6D"}, - - {"control-up", "\eOa"}, - {"control-down", "\eOb"}, - {"control-right", "\eOc"}, - {"control-left", "\eOd"}, - - {"shift-up", "\e[a"}, - {"shift-down", "\e[b"}, - {"shift-right", "\e[c"}, - {"shift-left", "\e[d"}, - - {"insert", "\e[2~"}, - {"delete", "\e[3~"}, - {"page-up", "\e[5~"}, - {"page-down", "\e[6~"}, - {"home", "\eOH"}, - {"end", "\eOF"}, - {"home", "\e[H"}, - {"end", "\e[F"}, - {"control-delete", "\e[3;5~"}, - {"shift-delete", "\e[3;2~"}, - {"control-shift-delete","\e[3;6~"}, - - {"F1", "\e[25~"}, - {"F2", "\e[26~"}, - {"F3", "\e[27~"}, - {"F4", "\e[26~"}, - - - {"F1", "\e[11~"}, - {"F2", "\e[12~"}, - {"F3", "\e[13~"}, - {"F4", "\e[14~"}, - {"F1", "\eOP"}, - {"F2", "\eOQ"}, - {"F3", "\eOR"}, - {"F4", "\eOS"}, - {"F5", "\e[15~"}, - {"F6", "\e[16~"}, - {"F7", "\e[17~"}, - {"F8", "\e[18~"}, - {"F9", "\e[19~"}, - {"F9", "\e[20~"}, - {"F10", "\e[21~"}, - {"F11", "\e[22~"}, - {"F12", "\e[23~"}, + {"control-up", "\033OA"}, + {"control-down", "\033OB"}, + {"control-right", "\033OC"}, + {"control-left", "\033OD"}, + + {"control-shift-up", "\033[1;6A"}, + {"control-shift-down", "\033[1;6B"}, + {"control-shift-right", "\033[1;6C"}, + {"control-shift-left", "\033[1;6D"}, + + {"control-up", "\033Oa"}, + {"control-down", "\033Ob"}, + {"control-right", "\033Oc"}, + {"control-left", "\033Od"}, + + {"shift-up", "\033[a"}, + {"shift-down", "\033[b"}, + {"shift-right", "\033[c"}, + {"shift-left", "\033[d"}, + + {"insert", "\033[2~"}, + {"delete", "\033[3~"}, + {"page-up", "\033[5~"}, + {"page-down", "\033[6~"}, + {"home", "\033OH"}, + {"end", "\033OF"}, + {"home", "\033[H"}, + {"end", "\033[F"}, + {"control-delete", "\033[3;5~"}, + {"shift-delete", "\033[3;2~"}, + {"control-shift-delete","\033[3;6~"}, + + {"F1", "\033[25~"}, + {"F2", "\033[26~"}, + {"F3", "\033[27~"}, + {"F4", "\033[26~"}, + + + {"F1", "\033[11~"}, + {"F2", "\033[12~"}, + {"F3", "\033[13~"}, + {"F4", "\033[14~"}, + {"F1", "\033OP"}, + {"F2", "\033OQ"}, + {"F3", "\033OR"}, + {"F4", "\033OS"}, + {"F5", "\033[15~"}, + {"F6", "\033[16~"}, + {"F7", "\033[17~"}, + {"F8", "\033[18~"}, + {"F9", "\033[19~"}, + {"F9", "\033[20~"}, + {"F10", "\033[21~"}, + {"F11", "\033[22~"}, + {"F12", "\033[23~"}, {"tab", {9, '\0'}}, {"shift-tab", {27, 9, '\0'}}, // also generated by alt-tab in linux console {"alt-space", {27, ' ', '\0'}}, - {"shift-tab", "\e[Z"}, + {"shift-tab", "\033[Z"}, {"backspace", {127, '\0'}}, {"space", " "}, - {"\e", "\e"}, + {"\033", "\033"}, {"return", {10,0}}, {"return", {13,0}}, /* this section could be autogenerated by code */ @@ -30408,61 +40103,61 @@ static const MmmKeyCode ufb_keycodes[]={ {"control-x", {24,0}}, {"control-y", {25,0}}, {"control-z", {26,0}}, - {"alt-`", "\e`"}, - {"alt-0", "\e0"}, - {"alt-1", "\e1"}, - {"alt-2", "\e2"}, - {"alt-3", "\e3"}, - {"alt-4", "\e4"}, - {"alt-5", "\e5"}, - {"alt-6", "\e6"}, - {"alt-7", "\e7"}, /* backspace? */ - {"alt-8", "\e8"}, - {"alt-9", "\e9"}, - {"alt-+", "\e+"}, - {"alt--", "\e-"}, - {"alt-/", "\e/"}, - {"alt-a", "\ea"}, - {"alt-b", "\eb"}, - {"alt-c", "\ec"}, - {"alt-d", "\ed"}, - {"alt-e", "\ee"}, - {"alt-f", "\ef"}, - {"alt-g", "\eg"}, - {"alt-h", "\eh"}, /* backspace? */ - {"alt-i", "\ei"}, - {"alt-j", "\ej"}, - {"alt-k", "\ek"}, - {"alt-l", "\el"}, - {"alt-n", "\em"}, - {"alt-n", "\en"}, - {"alt-o", "\eo"}, - {"alt-p", "\ep"}, - {"alt-q", "\eq"}, - {"alt-r", "\er"}, - {"alt-s", "\es"}, - {"alt-t", "\et"}, - {"alt-u", "\eu"}, - {"alt-v", "\ev"}, - {"alt-w", "\ew"}, - {"alt-x", "\ex"}, - {"alt-y", "\ey"}, - {"alt-z", "\ez"}, + {"alt-`", "\033`"}, + {"alt-0", "\0330"}, + {"alt-1", "\0331"}, + {"alt-2", "\0332"}, + {"alt-3", "\0333"}, + {"alt-4", "\0334"}, + {"alt-5", "\0335"}, + {"alt-6", "\0336"}, + {"alt-7", "\0337"}, /* backspace? */ + {"alt-8", "\0338"}, + {"alt-9", "\0339"}, + {"alt-+", "\033+"}, + {"alt--", "\033-"}, + {"alt-/", "\033/"}, + {"alt-a", "\033a"}, + {"alt-b", "\033b"}, + {"alt-c", "\033c"}, + {"alt-d", "\033d"}, + {"alt-e", "\033e"}, + {"alt-f", "\033f"}, + {"alt-g", "\033g"}, + {"alt-h", "\033h"}, /* backspace? */ + {"alt-i", "\033i"}, + {"alt-j", "\033j"}, + {"alt-k", "\033k"}, + {"alt-l", "\033l"}, + {"alt-n", "\033m"}, + {"alt-n", "\033n"}, + {"alt-o", "\033o"}, + {"alt-p", "\033p"}, + {"alt-q", "\033q"}, + {"alt-r", "\033r"}, + {"alt-s", "\033s"}, + {"alt-t", "\033t"}, + {"alt-u", "\033u"}, + {"alt-v", "\033v"}, + {"alt-w", "\033w"}, + {"alt-x", "\033x"}, + {"alt-y", "\033y"}, + {"alt-z", "\033z"}, /* Linux Console */ - {"home", "\e[1~"}, - {"end", "\e[4~"}, - {"F1", "\e[[A"}, - {"F2", "\e[[B"}, - {"F3", "\e[[C"}, - {"F4", "\e[[D"}, - {"F5", "\e[[E"}, - {"F6", "\e[[F"}, - {"F7", "\e[[G"}, - {"F8", "\e[[H"}, - {"F9", "\e[[I"}, - {"F10", "\e[[J"}, - {"F11", "\e[[K"}, - {"F12", "\e[[L"}, + {"home", "\033[1~"}, + {"end", "\033[4~"}, + {"F1", "\033[[A"}, + {"F2", "\033[[B"}, + {"F3", "\033[[C"}, + {"F4", "\033[[D"}, + {"F5", "\033[[E"}, + {"F6", "\033[[F"}, + {"F7", "\033[[G"}, + {"F8", "\033[[H"}, + {"F9", "\033[[I"}, + {"F10", "\033[[J"}, + {"F11", "\033[[K"}, + {"F12", "\033[[L"}, {NULL, } }; static int fb_keyboard_match_keycode (const char *buf, int length, const MmmKeyCode **ret) @@ -30470,7 +40165,7 @@ static int fb_keyboard_match_keycode (const char *buf, int length, const MmmKeyC int i; int matches = 0; - if (!strncmp (buf, "\e[M", MIN(length,3))) + if (!strncmp (buf, "\033[M", MIN(length,3))) { if (length >= 6) return 9001; @@ -30942,7 +40637,7 @@ int ctx_in_fill (Ctx *ctx, float x, float y) return 1; return 0; #else - return 1 + return 1; #endif } return 0; @@ -30988,7 +40683,7 @@ int ctx_in_stroke (Ctx *ctx, float x, float y) return 1; return 0; #else - return 1 + return 1; #endif } return 0; @@ -31436,271 +41131,271 @@ static int ctx_parser_resolve_command (CtxParser *parser, const uint8_t *str) /* first a list of mappings to one_char hashes, handled in a * separate fast path switch without hashing */ - case CTX_arcTo: ret = CTX_ARC_TO; break; - case CTX_arc: ret = CTX_ARC; break; - case CTX_curveTo: ret = CTX_CURVE_TO; break; - case CTX_restore: ret = CTX_RESTORE; break; - case CTX_stroke: ret = CTX_STROKE; break; - case CTX_fill: ret = CTX_FILL; break; - case CTX_paint: ret = CTX_PAINT; break; - case CTX_endFrame: ret = CTX_END_FRAME; break; - case CTX_horLineTo: ret = CTX_HOR_LINE_TO; break; - case CTX_rotate: ret = CTX_ROTATE; break; - case CTX_color: ret = CTX_COLOR; break; - case CTX_lineTo: ret = CTX_LINE_TO; break; - case CTX_moveTo: ret = CTX_MOVE_TO; break; - case CTX_scale: ret = CTX_SCALE; break; - case CTX_newPage: ret = CTX_NEW_PAGE; break; - case CTX_quadTo: ret = CTX_QUAD_TO; break; - case CTX_viewBox: ret = CTX_VIEW_BOX; break; - case CTX_smoothTo: ret = CTX_SMOOTH_TO; break; - case CTX_smoothQuadTo: ret = CTX_SMOOTHQ_TO; break; - case CTX_clear: ret = CTX_COMPOSITE_CLEAR; break; - case CTX_copy: ret = CTX_COMPOSITE_COPY; break; - case CTX_destinationOver: ret = CTX_COMPOSITE_DESTINATION_OVER; break; - case CTX_destinationIn: ret = CTX_COMPOSITE_DESTINATION_IN; break; - case CTX_destinationOut: ret = CTX_COMPOSITE_DESTINATION_OUT; break; - case CTX_sourceOver: ret = CTX_COMPOSITE_SOURCE_OVER; break; - case CTX_sourceAtop: ret = CTX_COMPOSITE_SOURCE_ATOP; break; - case CTX_destinationAtop: ret = CTX_COMPOSITE_DESTINATION_ATOP; break; - case CTX_sourceOut: ret = CTX_COMPOSITE_SOURCE_OUT; break; - case CTX_sourceIn: ret = CTX_COMPOSITE_SOURCE_IN; break; - case CTX_xor: ret = CTX_COMPOSITE_XOR; break; - case CTX_darken: ret = CTX_BLEND_DARKEN; break; - case CTX_lighten: ret = CTX_BLEND_LIGHTEN; break; - //case CTX_color: ret = CTX_BLEND_COLOR; break; + case SQZ_arcTo: ret = CTX_ARC_TO; break; + case SQZ_arc: ret = CTX_ARC; break; + case SQZ_curveTo: ret = CTX_CURVE_TO; break; + case SQZ_restore: ret = CTX_RESTORE; break; + case SQZ_stroke: ret = CTX_STROKE; break; + case SQZ_fill: ret = CTX_FILL; break; + case SQZ_paint: ret = CTX_PAINT; 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; + case SQZ_color: ret = CTX_COLOR; break; + case SQZ_lineTo: ret = CTX_LINE_TO; break; + case SQZ_moveTo: ret = CTX_MOVE_TO; break; + case SQZ_scale: ret = CTX_SCALE; break; + case SQZ_newPage: ret = CTX_NEW_PAGE; break; + case SQZ_quadTo: ret = CTX_QUAD_TO; break; + case SQZ_viewBox: ret = CTX_VIEW_BOX; break; + case SQZ_smoothTo: ret = CTX_SMOOTH_TO; break; + case SQZ_smoothQuadTo: ret = CTX_SMOOTHQ_TO; break; + case SQZ_clear: ret = CTX_COMPOSITE_CLEAR; break; + case SQZ_copy: ret = CTX_COMPOSITE_COPY; break; + case SQZ_destinationOver: ret = CTX_COMPOSITE_DESTINATION_OVER; break; + case SQZ_destinationIn: ret = CTX_COMPOSITE_DESTINATION_IN; break; + case SQZ_destinationOut: ret = CTX_COMPOSITE_DESTINATION_OUT; break; + case SQZ_sourceOver: ret = CTX_COMPOSITE_SOURCE_OVER; break; + case SQZ_sourceAtop: ret = CTX_COMPOSITE_SOURCE_ATOP; break; + case SQZ_destinationAtop: ret = CTX_COMPOSITE_DESTINATION_ATOP; break; + case SQZ_sourceOut: ret = CTX_COMPOSITE_SOURCE_OUT; break; + case SQZ_sourceIn: ret = CTX_COMPOSITE_SOURCE_IN; break; + case SQZ_xor: ret = CTX_COMPOSITE_XOR; break; + case SQZ_darken: ret = CTX_BLEND_DARKEN; break; + case SQZ_lighten: ret = CTX_BLEND_LIGHTEN; break; + //case SQZ_color: ret = CTX_BLEND_COLOR; break; // // XXX check that he special casing for color works // it is the first collision and it is due to our own // color, not w3c for now unique use of it // - case CTX_hue: ret = CTX_BLEND_HUE; break; - case CTX_multiply: ret = CTX_BLEND_MULTIPLY; break; - case CTX_normal: ret = CTX_BLEND_NORMAL;break; - case CTX_screen: ret = CTX_BLEND_SCREEN;break; - case CTX_difference: ret = CTX_BLEND_DIFFERENCE; break; - case CTX_startFrame: ret = CTX_START_FRAME; break; - case CTX_verLineTo: ret = CTX_VER_LINE_TO; break; - case CTX_exit: - case CTX_done: ret = CTX_EXIT; break; - case CTX_closePath: ret = CTX_CLOSE_PATH; break; - case CTX_beginPath: - case CTX_newPath: ret = CTX_BEGIN_PATH; break; - case CTX_relArcTo: ret = CTX_REL_ARC_TO; break; - case CTX_clip: ret = CTX_CLIP; break; - case CTX_relCurveTo: ret = CTX_REL_CURVE_TO; break; - case CTX_startGroup: ret = CTX_START_GROUP; break; - case CTX_endGroup: ret = CTX_END_GROUP; break; - case CTX_save: ret = CTX_SAVE; break; - case CTX_translate: ret = CTX_TRANSLATE; break; - case CTX_linearGradient: ret = CTX_LINEAR_GRADIENT; break; - case CTX_relHorLineTo: ret = CTX_REL_HOR_LINE_TO; break; - case CTX_relLineTo: ret = CTX_REL_LINE_TO; break; - case CTX_relMoveTo: ret = CTX_REL_MOVE_TO; break; - case CTX_font: ret = CTX_FONT; break; - case CTX_radialGradient:ret = CTX_RADIAL_GRADIENT; break; - case CTX_gradientAddStop: - case CTX_addStop: ret = CTX_GRADIENT_STOP; break; - case CTX_relQuadTo: ret = CTX_REL_QUAD_TO; break; - case CTX_rectangle: - case CTX_rect: ret = CTX_RECTANGLE; break; - case CTX_roundRectangle: ret = CTX_ROUND_RECTANGLE; break; - case CTX_relSmoothTo: ret = CTX_REL_SMOOTH_TO; break; - case CTX_relSmoothqTo: ret = CTX_REL_SMOOTHQ_TO; break; - case CTX_strokeText: ret = CTX_STROKE_TEXT; break; - case CTX_strokeRect: ret = CTX_STROKE_RECT; break; - case CTX_fillRect: ret = CTX_FILL_RECT; break; - case CTX_relVerLineTo: ret = CTX_REL_VER_LINE_TO; break; - case CTX_text: ret = CTX_TEXT; break; - case CTX_identity: ret = CTX_IDENTITY; break; - case CTX_transform: ret = CTX_APPLY_TRANSFORM; break; - case CTX_sourceTransform: ret = CTX_SOURCE_TRANSFORM; break; - case CTX_texture: ret = CTX_TEXTURE; break; - case CTX_defineTexture: ret = CTX_DEFINE_TEXTURE; break; + case SQZ_hue: ret = CTX_BLEND_HUE; break; + case SQZ_multiply: ret = CTX_BLEND_MULTIPLY; break; + case SQZ_normal: ret = CTX_BLEND_NORMAL;break; + case SQZ_screen: ret = CTX_BLEND_SCREEN;break; + case SQZ_difference: ret = CTX_BLEND_DIFFERENCE; break; + case SQZ_startFrame: ret = CTX_START_FRAME; break; + case SQZ_verLineTo: ret = CTX_VER_LINE_TO; break; + case SQZ_exit: + case SQZ_done: ret = CTX_EXIT; break; + case SQZ_closePath: ret = CTX_CLOSE_PATH; break; + case SQZ_beginPath: + case SQZ_newPath: ret = CTX_BEGIN_PATH; break; + case SQZ_relArcTo: ret = CTX_REL_ARC_TO; break; + case SQZ_clip: ret = CTX_CLIP; break; + case SQZ_relCurveTo: ret = CTX_REL_CURVE_TO; break; + case SQZ_startGroup: ret = CTX_START_GROUP; break; + case SQZ_endGroup: ret = CTX_END_GROUP; break; + case SQZ_save: ret = CTX_SAVE; break; + case SQZ_translate: ret = CTX_TRANSLATE; break; + case SQZ_linearGradient: ret = CTX_LINEAR_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; + case SQZ_font: ret = CTX_FONT; break; + case SQZ_radialGradient:ret = CTX_RADIAL_GRADIENT; break; + case SQZ_gradientAddStop: + case SQZ_addStop: ret = CTX_GRADIENT_STOP; break; + case SQZ_relQuadTo: ret = CTX_REL_QUAD_TO; break; + case SQZ_rectangle: + case SQZ_rect: ret = CTX_RECTANGLE; break; + 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; + case SQZ_text: ret = CTX_TEXT; break; + case SQZ_identity: ret = CTX_IDENTITY; break; + case SQZ_transform: ret = CTX_APPLY_TRANSFORM; break; + case SQZ_sourceTransform: ret = CTX_SOURCE_TRANSFORM; break; + case SQZ_texture: ret = CTX_TEXTURE; break; + case SQZ_defineTexture: ret = CTX_DEFINE_TEXTURE; break; #if 0 - case CTX_rgbSpace: + case SQZ_rgbSpace: return ctx_parser_set_command (parser, CTX_SET_RGB_SPACE); - case CTX_cmykSpace: + case SQZ_cmykSpace: return ctx_parser_set_command (parser, CTX_SET_CMYK_SPACE); - case CTX_drgbSpace: + case SQZ_drgbSpace: return ctx_parser_set_command (parser, CTX_SET_DRGB_SPACE); #endif - case CTX_defineFont: + case SQZ_defineFont: return ctx_parser_set_command (parser, CTX_DEFINE_FONT); - case CTX_defineGlyph: + case SQZ_defineGlyph: return ctx_parser_set_command (parser, CTX_DEFINE_GLYPH); - case CTX_kerningPair: + case SQZ_kerningPair: return ctx_parser_set_command (parser, CTX_KERNING_PAIR); - case CTX_colorSpace: + case SQZ_colorSpace: return ctx_parser_set_command (parser, CTX_COLOR_SPACE); - case CTX_fillRule: + case SQZ_fillRule: return ctx_parser_set_command (parser, CTX_FILL_RULE); - case CTX_fontSize: - case CTX_setFontSize: + case SQZ_fontSize: + case SQZ_setFontSize: return ctx_parser_set_command (parser, CTX_FONT_SIZE); - case CTX_compositingMode: + case SQZ_compositingMode: return ctx_parser_set_command (parser, CTX_COMPOSITING_MODE); - case CTX_extend: + case SQZ_extend: return ctx_parser_set_command (parser, CTX_EXTEND); - case CTX_blend: - case CTX_blending: - case CTX_blendMode: + case SQZ_blend: + case SQZ_blending: + case SQZ_blendMode: return ctx_parser_set_command (parser, CTX_BLEND_MODE); - case CTX_miterLimit: + case SQZ_miterLimit: return ctx_parser_set_command (parser, CTX_MITER_LIMIT); - case CTX_textAlign: + case SQZ_textAlign: return ctx_parser_set_command (parser, CTX_TEXT_ALIGN); - case CTX_textBaseline: + case SQZ_textBaseline: return ctx_parser_set_command (parser, CTX_TEXT_BASELINE); - case CTX_textDirection: + case SQZ_textDirection: return ctx_parser_set_command (parser, CTX_TEXT_DIRECTION); - case CTX_join: - case CTX_lineJoin: - case CTX_setLineJoin: + case SQZ_join: + case SQZ_lineJoin: + case SQZ_setLineJoin: return ctx_parser_set_command (parser, CTX_LINE_JOIN); - case CTX_glyph: + case SQZ_glyph: return ctx_parser_set_command (parser, CTX_GLYPH); - case CTX_cap: - case CTX_lineCap: - case CTX_setLineCap: + case SQZ_cap: + case SQZ_lineCap: + case SQZ_setLineCap: return ctx_parser_set_command (parser, CTX_LINE_CAP); - case CTX_lineDash: + case SQZ_lineDash: return ctx_parser_set_command (parser, CTX_LINE_DASH); - case CTX_lineWidth: - case CTX_setLineWidth: + case SQZ_lineWidth: + case SQZ_setLineWidth: return ctx_parser_set_command (parser, CTX_LINE_WIDTH); - case CTX_lineDashOffset: + case SQZ_lineDashOffset: return ctx_parser_set_command (parser, CTX_LINE_DASH_OFFSET); - case CTX_lineHeight: + case SQZ_lineHeight: return ctx_parser_set_command (parser, CTX_LINE_HEIGHT); - case CTX_wrapLeft: + case SQZ_wrapLeft: return ctx_parser_set_command (parser, CTX_WRAP_LEFT); - case CTX_wrapRight: + case SQZ_wrapRight: return ctx_parser_set_command (parser, CTX_WRAP_RIGHT); - case CTX_imageSmoothing: + case SQZ_imageSmoothing: return ctx_parser_set_command (parser, CTX_IMAGE_SMOOTHING); - case CTX_shadowColor: + case SQZ_shadowColor: return ctx_parser_set_command (parser, CTX_SHADOW_COLOR); - case CTX_shadowBlur: + case SQZ_shadowBlur: return ctx_parser_set_command (parser, CTX_SHADOW_BLUR); - case CTX_shadowOffsetX: + case SQZ_shadowOffsetX: return ctx_parser_set_command (parser, CTX_SHADOW_OFFSET_X); - case CTX_shadowOffsetY: + case SQZ_shadowOffsetY: return ctx_parser_set_command (parser, CTX_SHADOW_OFFSET_Y); - case CTX_globalAlpha: + case SQZ_globalAlpha: return ctx_parser_set_command (parser, CTX_GLOBAL_ALPHA); - case CTX_strokeSource: + case SQZ_strokeSource: return ctx_parser_set_command (parser, CTX_STROKE_SOURCE); /* strings are handled directly here, * instead of in the one-char handler, using return instead of break */ - case CTX_gray: + case SQZ_gray: ctx_parser_set_color_model (parser, CTX_GRAY, 0); return ctx_parser_set_command (parser, CTX_COLOR); - case CTX_graya: + case SQZ_graya: ctx_parser_set_color_model (parser, CTX_GRAYA, 0); return ctx_parser_set_command (parser, CTX_COLOR); - case CTX_rgb: + case SQZ_rgb: ctx_parser_set_color_model (parser, CTX_RGB, 0); return ctx_parser_set_command (parser, CTX_COLOR); - case CTX_drgb: + case SQZ_drgb: ctx_parser_set_color_model (parser, CTX_DRGB, 0); return ctx_parser_set_command (parser, CTX_COLOR); - case CTX_rgba: + case SQZ_rgba: ctx_parser_set_color_model (parser, CTX_RGBA, 0); return ctx_parser_set_command (parser, CTX_COLOR); - case CTX_drgba: + case SQZ_drgba: ctx_parser_set_color_model (parser, CTX_DRGBA, 0); return ctx_parser_set_command (parser, CTX_COLOR); - case CTX_cmyk: + case SQZ_cmyk: ctx_parser_set_color_model (parser, CTX_CMYK, 0); return ctx_parser_set_command (parser, CTX_COLOR); - case CTX_cmyka: + case SQZ_cmyka: ctx_parser_set_color_model (parser, CTX_CMYKA, 0); return ctx_parser_set_command (parser, CTX_COLOR); - case CTX_lab: + case SQZ_lab: ctx_parser_set_color_model (parser, CTX_LAB, 0); return ctx_parser_set_command (parser, CTX_COLOR); - case CTX_laba: + case SQZ_laba: ctx_parser_set_color_model (parser, CTX_LABA, 0); return ctx_parser_set_command (parser, CTX_COLOR); - case CTX_lch: + case SQZ_lch: ctx_parser_set_color_model (parser, CTX_LCH, 0); return ctx_parser_set_command (parser, CTX_COLOR); - case CTX_lcha: + case SQZ_lcha: ctx_parser_set_color_model (parser, CTX_LCHA, 0); return ctx_parser_set_command (parser, CTX_COLOR); /* and a full repeat of the above, with S for Stroke suffix */ - case CTX_grayS: + case SQZ_grayS: ctx_parser_set_color_model (parser, CTX_GRAY, 1); return ctx_parser_set_command (parser, CTX_COLOR); - case CTX_grayaS: + case SQZ_grayaS: ctx_parser_set_color_model (parser, CTX_GRAYA, 1); return ctx_parser_set_command (parser, CTX_COLOR); - case CTX_rgbS: + case SQZ_rgbS: ctx_parser_set_color_model (parser, CTX_RGB, 1); return ctx_parser_set_command (parser, CTX_COLOR); - case CTX_drgbS: + case SQZ_drgbS: ctx_parser_set_color_model (parser, CTX_DRGB, 1); return ctx_parser_set_command (parser, CTX_COLOR); - case CTX_rgbaS: + case SQZ_rgbaS: ctx_parser_set_color_model (parser, CTX_RGBA, 1); return ctx_parser_set_command (parser, CTX_COLOR); - case CTX_drgbaS: + case SQZ_drgbaS: ctx_parser_set_color_model (parser, CTX_DRGBA, 1); return ctx_parser_set_command (parser, CTX_COLOR); - case CTX_cmykS: + case SQZ_cmykS: ctx_parser_set_color_model (parser, CTX_CMYK, 1); return ctx_parser_set_command (parser, CTX_COLOR); - case CTX_cmykaS: + case SQZ_cmykaS: ctx_parser_set_color_model (parser, CTX_CMYKA, 1); return ctx_parser_set_command (parser, CTX_COLOR); - case CTX_labS: + case SQZ_labS: ctx_parser_set_color_model (parser, CTX_LAB, 1); return ctx_parser_set_command (parser, CTX_COLOR); - case CTX_labaS: + case SQZ_labaS: ctx_parser_set_color_model (parser, CTX_LABA, 1); return ctx_parser_set_command (parser, CTX_COLOR); - case CTX_lchS: + case SQZ_lchS: ctx_parser_set_color_model (parser, CTX_LCH, 1); return ctx_parser_set_command (parser, CTX_COLOR); - case CTX_lchaS: + case SQZ_lchaS: ctx_parser_set_color_model (parser, CTX_LCHA, 1); return ctx_parser_set_command (parser, CTX_COLOR); /* words that correspond to low integer constants */ - case CTX_nonzero: return CTX_FILL_RULE_WINDING; - case CTX_winding: return CTX_FILL_RULE_WINDING; - case CTX_evenOdd: return CTX_FILL_RULE_EVEN_ODD; - case CTX_bevel: return CTX_JOIN_BEVEL; - case CTX_round: return CTX_JOIN_ROUND; - case CTX_miter: return CTX_JOIN_MITER; - case CTX_none: return CTX_CAP_NONE; - case CTX_square: return CTX_CAP_SQUARE; - case CTX_start: return CTX_TEXT_ALIGN_START; - case CTX_end: return CTX_TEXT_ALIGN_END; - case CTX_left: return CTX_TEXT_ALIGN_LEFT; - case CTX_right: return CTX_TEXT_ALIGN_RIGHT; - case CTX_center: return CTX_TEXT_ALIGN_CENTER; - case CTX_top: return CTX_TEXT_BASELINE_TOP; - case CTX_bottom : return CTX_TEXT_BASELINE_BOTTOM; - case CTX_middle: return CTX_TEXT_BASELINE_MIDDLE; - case CTX_alphabetic: return CTX_TEXT_BASELINE_ALPHABETIC; - case CTX_hanging: return CTX_TEXT_BASELINE_HANGING; - case CTX_ideographic: return CTX_TEXT_BASELINE_IDEOGRAPHIC; - - case CTX_userRGB: return CTX_COLOR_SPACE_USER_RGB; - case CTX_deviceRGB: return CTX_COLOR_SPACE_DEVICE_RGB; - case CTX_userCMYK: return CTX_COLOR_SPACE_USER_CMYK; - case CTX_deviceCMYK: return CTX_COLOR_SPACE_DEVICE_CMYK; + case SQZ_nonzero: return CTX_FILL_RULE_WINDING; + case SQZ_winding: return CTX_FILL_RULE_WINDING; + case SQZ_evenOdd: return CTX_FILL_RULE_EVEN_ODD; + case SQZ_bevel: return CTX_JOIN_BEVEL; + case SQZ_round: return CTX_JOIN_ROUND; + case SQZ_miter: return CTX_JOIN_MITER; + case SQZ_none: return CTX_CAP_NONE; + case SQZ_square: return CTX_CAP_SQUARE; + case SQZ_start: return CTX_TEXT_ALIGN_START; + case SQZ_end: return CTX_TEXT_ALIGN_END; + case SQZ_left: return CTX_TEXT_ALIGN_LEFT; + case SQZ_right: return CTX_TEXT_ALIGN_RIGHT; + case SQZ_center: return CTX_TEXT_ALIGN_CENTER; + case SQZ_top: return CTX_TEXT_BASELINE_TOP; + case SQZ_bottom : return CTX_TEXT_BASELINE_BOTTOM; + case SQZ_middle: return CTX_TEXT_BASELINE_MIDDLE; + case SQZ_alphabetic: return CTX_TEXT_BASELINE_ALPHABETIC; + case SQZ_hanging: return CTX_TEXT_BASELINE_HANGING; + case SQZ_ideographic: return CTX_TEXT_BASELINE_IDEOGRAPHIC; + + case SQZ_userRGB: return CTX_COLOR_SPACE_USER_RGB; + case SQZ_deviceRGB: return CTX_COLOR_SPACE_DEVICE_RGB; + case SQZ_userCMYK: return CTX_COLOR_SPACE_USER_CMYK; + case SQZ_deviceCMYK: return CTX_COLOR_SPACE_DEVICE_CMYK; #undef STR #undef LOWER default: @@ -32607,7 +42302,10 @@ static inline void ctx_parser_feed_byte (CtxParser *parser, char byte) } #endif - if (CTX_LIKELY(parser->state == CTX_PARSER_STRING_YENC)) + switch (parser->state) + { + + case CTX_PARSER_STRING_YENC: { if (CTX_UNLIKELY((parser->prev_byte == '=') && (byte == 'y'))) { @@ -32632,7 +42330,8 @@ static inline void ctx_parser_feed_byte (CtxParser *parser, char byte) parser->prev_byte = byte; return; } - else if (parser->state == CTX_PARSER_STRING_A85) + + case CTX_PARSER_STRING_A85: { /* since these are our largest bulk transfers, minimize * overhead for this case. */ @@ -32650,8 +42349,8 @@ static inline void ctx_parser_feed_byte (CtxParser *parser, char byte) } return; } - switch (parser->state) - { + + case CTX_PARSER_NEUTRAL: switch (byte) { @@ -32895,6 +42594,7 @@ static inline void ctx_parser_feed_byte (CtxParser *parser, char byte) ctx_parser_word_done (parser); } break; +#if 0 case CTX_PARSER_STRING_A85: if (CTX_LIKELY(byte!='~')) { @@ -32909,6 +42609,7 @@ static inline void ctx_parser_feed_byte (CtxParser *parser, char byte) ctx_parser_string_done (parser); } break; +#endif case CTX_PARSER_STRING_APOS: switch (byte) { @@ -33734,9 +43435,15 @@ ctx_drawlist_process (Ctx *ctx, CtxEntry *entry) static CtxBackend *ctx_drawlist_backend_new (void) { - CtxBackend *backend = (CtxBackend*)ctx_calloc (sizeof (CtxBackend), 1); + CtxBackend *backend = (CtxBackend*)ctx_calloc (sizeof (CtxCtx), 1); + // the sizeof(CtxCtx) should actually be sizeof(CtxBackend) + // but static analysis complains about event code + // 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->destroy = (void(*)(void *a))ctx_drawlist_backend_destroy; + backend->type = CTX_BACKEND_DRAWLIST; return backend; } @@ -33922,7 +43629,7 @@ ctx_hasher_process (Ctx *ctx, CtxCommand *command) 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); - switch ((int)ctx_state_get (rasterizer->state, CTX_textAlign)) + switch ((int)ctx_state_get (rasterizer->state, SQZ_textAlign)) { case CTX_TEXT_ALIGN_LEFT: case CTX_TEXT_ALIGN_START: @@ -34319,6 +44026,7 @@ ctx_hasher_init (CtxRasterizer *rasterizer, Ctx *ctx, CtxState *state, int width CtxHasher *hasher = (CtxHasher*)rasterizer; ctx_memset (rasterizer, 0, sizeof (CtxHasher) ); CtxBackend *backend = (CtxBackend*)hasher; + backend->type = CTX_BACKEND_HASHER; backend->ctx = ctx; backend->process = ctx_hasher_process; backend->destroy = (CtxDestroyNotify)ctx_rasterizer_destroy; @@ -34847,6 +44555,7 @@ ctx_pdf_process (Ctx *ctx, CtxCommand *c) break; case CTX_COLOR: + { int space = ((int) ctx_arg_float (0)) & 511; switch (space) // XXX remove 511 after stroke source is complete { @@ -34887,6 +44596,7 @@ ctx_pdf_process (Ctx *ctx, CtxCommand *c) ctx_pdf_print("G\n"); break; } + } break; case CTX_SET_RGBA_U8: @@ -35147,6 +44857,7 @@ ctx_new_pdf (const char *path, float width, float height) pdf->width = width; pdf->height = height; + backend->type = CTX_BACKEND_PDF; backend->destroy = (void*)ctx_pdf_destroy; backend->process = ctx_pdf_process; backend->ctx = ctx; @@ -35423,6 +45134,7 @@ ctx_cairo_process (Ctx *ctx, CtxCommand *c) break; case CTX_COLOR: + { int space = ((int) ctx_arg_float (0)) & 511; switch (space) // XXX remove 511 after stroke source is complete { @@ -35446,6 +45158,7 @@ ctx_cairo_process (Ctx *ctx, CtxCommand *c) cairo_set_source_rgba (cr, c->graya.g, c->graya.g, c->graya.g, 1.0f); break; } + } break; #if 0 @@ -35681,6 +45394,7 @@ ctx_new_for_cairo (cairo_t *cr) Ctx *ctx = _ctx_new_drawlist (640, 480); CtxCairo *ctx_cairo = ctx_calloc(sizeof(CtxCairo),1); CtxBackend *backend = (CtxBackend*)ctx_cairo; + backend->type = CTX_BACKEND_CAIRO; backend->destroy = (void*)ctx_cairo_destroy; backend->process = ctx_cairo_process; backend->ctx = ctx; @@ -35692,6 +45406,8 @@ ctx_new_for_cairo (cairo_t *cr) #endif #if CTX_TERMINAL_EVENTS +int ctx_frame_ack = -1; +#if CTX_FORMATTER #if 0 static int ctx_find_largest_matching_substring @@ -35992,7 +45708,6 @@ static char *decode_ctx (const char *encoded, int enc_len, const char *prev, int #define CTX_END_STRING "\nX" // or "\ndone" #define CTX_END_STRING2 "\n" -int ctx_frame_ack = -1; static char *prev_frame_contents = NULL; static int prev_frame_len = 0; @@ -36007,8 +45722,8 @@ static void ctx_ctx_end_frame (Ctx *ctx) #endif if (ctx_native_events) - fprintf (stdout, "\e[?201h"); - fprintf (stdout, "\e[H\e[?25l\e[?200h"); + fprintf (stdout, "\033[?201h"); + fprintf (stdout, "\033[H\033[?25l\033[?200h"); #if 1 fprintf (stdout, CTX_START_STRING); ctx_render_stream (ctxctx->backend.ctx, stdout, 0); @@ -36065,7 +45780,7 @@ static void ctx_ctx_end_frame (Ctx *ctx) #endif #if CTX_SYNC_FRAMES - fprintf (stdout, "\e[5n"); + fprintf (stdout, "\033[5n"); fflush (stdout); ctx_frame_ack = 0; @@ -36142,7 +45857,7 @@ void ctx_ctx_consume_events (Ctx *ctx) ctx_incoming_message (ctx, event + strlen ("message"), 0); } else if (!strcmp (event, "size-changed")) { - fprintf (stdout, "\e[H\e[2J\e[?25l"); + fprintf (stdout, "\033[H\033[2J\033[?25l"); ctxctx->cols = ctx_terminal_cols (); ctxctx->rows = ctx_terminal_rows (); @@ -36182,10 +45897,10 @@ Ctx *ctx_new_ctx (int width, int height) Ctx *ctx = _ctx_new_drawlist (width, height); CtxCtx *ctxctx = (CtxCtx*)ctx_calloc (sizeof (CtxCtx), 1); CtxBackend *backend = (CtxBackend*)ctxctx; - fprintf (stdout, "\e[?1049h"); + fprintf (stdout, "\033[?1049h"); fflush (stdout); - //fprintf (stderr, "\e[H"); - //fprintf (stderr, "\e[2J"); + //fprintf (stderr, "\033[H"); + //fprintf (stderr, "\033[2J"); ctx_native_events = 1; if (width <= 0 || height <= 0) { @@ -36207,6 +45922,7 @@ Ctx *ctx_new_ctx (int width, int height) if (!ctx_native_events) _ctx_mouse (ctx, NC_MOUSE_DRAG); 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->consume_events = ctx_ctx_consume_events; @@ -36218,6 +45934,7 @@ Ctx *ctx_new_ctx (int width, int height) void ctx_ctx_pcm (Ctx *ctx); +#endif #endif #if CTX_TILED @@ -36238,9 +45955,9 @@ int _ctx_damage_control = 0; void ctx_tiled_destroy (CtxTiled *tiled) { tiled->quit = 1; - mtx_lock (&tiled->mtx); - cnd_broadcast (&tiled->cond); - mtx_unlock (&tiled->mtx); + //mtx_lock (&tiled->mtx); + //cnd_broadcast (&tiled->cond); + //mtx_unlock (&tiled->mtx); while (tiled->thread_quit < _ctx_max_threads) usleep (1000); @@ -36266,7 +45983,7 @@ static long sdl_icc_length = 0; static void ctx_tiled_end_frame (Ctx *ctx) { CtxTiled *tiled = (CtxTiled*)ctx->backend; - mtx_lock (&tiled->mtx); + //mtx_lock (&tiled->mtx); if (tiled->shown_frame == tiled->render_frame) { int dirty_tiles = 0; @@ -36359,13 +46076,13 @@ static void ctx_tiled_end_frame (Ctx *ctx) ctx_render_ctx (tiled->ctx_copy, host); } #endif - cnd_broadcast (&tiled->cond); + //cnd_broadcast (&tiled->cond); } else { fprintf (stderr, "{drip}"); } - mtx_unlock (&tiled->mtx); + //mtx_unlock (&tiled->mtx); ctx_drawlist_clear (ctx); } @@ -36379,9 +46096,9 @@ void ctx_tiled_render_fun (void **data) { Ctx *host = tiled->host[no]; - mtx_lock (&tiled->mtx); - cnd_wait(&tiled->cond, &tiled->mtx); - mtx_unlock (&tiled->mtx); + //mtx_lock (&tiled->mtx); + //cnd_wait(&tiled->cond, &tiled->mtx); + //mtx_unlock (&tiled->mtx); if (tiled->render_frame != tiled->rendered_frame[no]) { @@ -36412,14 +46129,11 @@ void ctx_tiled_render_fun (void **data) #endif int swap_red_green = rasterizer->swap_red_green; int compressed_scanlines = 0; -#if CTX_ENABLE_CBRLE - // compressed_scanlines = 1; -#endif ctx_rasterizer_init (rasterizer, host, tiled->backend.ctx, &host->state, &tiled->pixels[tiled->width * 4 * y0 + x0 * 4], 0, 0, width, height, - tiled->width*4, compressed_scanlines?CTX_FORMAT_CBRLE_32:CTX_FORMAT_BGRA8, + tiled->width*4, CTX_FORMAT_BGRA8, tiled->antialias); ((CtxRasterizer*)(host->backend))->swap_red_green = swap_red_green; if (sdl_icc_length) @@ -36428,18 +46142,6 @@ void ctx_tiled_render_fun (void **data) ctx_translate (host, -x0, -y0); ctx_render_ctx_masked (tiled->ctx_copy, host, active_mask); -#if CTX_ENABLE_CBRLE - if (compressed_scanlines) - for (int i = 0; i < height; i ++) - { - uint8_t temp[width*4]; - _ctx_CBRLE_decompress (&tiled->pixels[tiled->width * 4 * (y0+i) + x0 * 4], - temp, - width, - width * 4, 0, width); - memcpy (&tiled->pixels[tiled->width * 4 * (y0+i) + x0 * 4], temp, sizeof (temp)); - } -#endif } } tiled->rendered_frame[no] = tiled->render_frame; @@ -36575,64 +46277,64 @@ static void ctx_tiled_undraw_cursor (CtxTiled *tiled) } } -static inline void ctx_tiled_draw_cursor (CtxTiled *tiled) -{ - int cursor_x = (int)ctx_pointer_x (tiled->backend.ctx); - int cursor_y = (int)ctx_pointer_y (tiled->backend.ctx); - int cursor_size = ctx_height (tiled->backend.ctx) / 28; - CtxCursor cursor_shape = tiled->backend.ctx->cursor; - int no = 0; - - if (cursor_x == ctx_tiled_cursor_drawn_x && - cursor_y == ctx_tiled_cursor_drawn_y && - cursor_shape == ctx_tiled_cursor_drawn_shape) - ctx_tiled_cursor_same_pos ++; - else - ctx_tiled_cursor_same_pos = 0; - - if (ctx_tiled_cursor_same_pos >= CTX_FB_HIDE_CURSOR_FRAMES) - { - if (ctx_tiled_cursor_drawn) - ctx_tiled_undraw_cursor (tiled); - return; - } - - /* no need to flicker when stationary, motion flicker can also be removed - * by combining the previous and next position masks when a motion has - * occured.. - */ - if (ctx_tiled_cursor_same_pos && ctx_tiled_cursor_drawn) - return; - - ctx_tiled_undraw_cursor (tiled); - - no = 0; - - int startx = -cursor_size; - int starty = -cursor_size; - - if (cursor_shape == CTX_CURSOR_ARROW) - startx = starty = 0; - - for (int y = starty; y < cursor_size; y++) - for (int x = startx; x < cursor_size; x++, no+=4) - { - if (x + cursor_x < tiled->width && y + cursor_y < tiled->height) - { - if (ctx_is_in_cursor (x, y, cursor_size, cursor_shape)) - { - int o = ((cursor_y + y) * tiled->width + (cursor_x + x)) * 4; - tiled->fb[o+0]^=0x88; - tiled->fb[o+1]^=0x88; - tiled->fb[o+2]^=0x88; - } - } - } - ctx_tiled_cursor_drawn = 1; - ctx_tiled_cursor_drawn_x = cursor_x; - ctx_tiled_cursor_drawn_y = cursor_y; - ctx_tiled_cursor_drawn_shape = cursor_shape; -} +//static inline void ctx_tiled_draw_cursor (CtxTiled *tiled) +//{ +// int cursor_x = (int)ctx_pointer_x (tiled->backend.ctx); +// int cursor_y = (int)ctx_pointer_y (tiled->backend.ctx); +// int cursor_size = ctx_height (tiled->backend.ctx) / 28; +// CtxCursor cursor_shape = tiled->backend.ctx->cursor; +// int no = 0; +// +// if (cursor_x == ctx_tiled_cursor_drawn_x && +// cursor_y == ctx_tiled_cursor_drawn_y && +// cursor_shape == ctx_tiled_cursor_drawn_shape) +// ctx_tiled_cursor_same_pos ++; +// else +// ctx_tiled_cursor_same_pos = 0; +// +// if (ctx_tiled_cursor_same_pos >= CTX_FB_HIDE_CURSOR_FRAMES) +// { +// if (ctx_tiled_cursor_drawn) +// ctx_tiled_undraw_cursor (tiled); +// return; +// } +// +// /* no need to flicker when stationary, motion flicker can also be removed +// * by combining the previous and next position masks when a motion has +// * occured.. +// */ +// if (ctx_tiled_cursor_same_pos && ctx_tiled_cursor_drawn) +// return; +// +// ctx_tiled_undraw_cursor (tiled); +// +// no = 0; +// +// int startx = -cursor_size; +// int starty = -cursor_size; +// +// if (cursor_shape == CTX_CURSOR_ARROW) +// startx = starty = 0; +// +// for (int y = starty; y < cursor_size; y++) +// for (int x = startx; x < cursor_size; x++, no+=4) +// { +// if (x + cursor_x < tiled->width && y + cursor_y < tiled->height) +// { +// if (ctx_is_in_cursor (x, y, cursor_size, cursor_shape)) +// { +// int o = ((cursor_y + y) * tiled->width + (cursor_x + x)) * 4; +// tiled->fb[o+0]^=0x88; +// tiled->fb[o+1]^=0x88; +// tiled->fb[o+2]^=0x88; +// } +// } +// } +// ctx_tiled_cursor_drawn = 1; +// ctx_tiled_cursor_drawn_x = cursor_x; +// ctx_tiled_cursor_drawn_y = cursor_y; +// ctx_tiled_cursor_drawn_shape = cursor_shape; +//} #endif @@ -36667,8 +46369,10 @@ static char *ctx_headless_get_clipboard (Ctx *ctx) static inline int ctx_headless_get_mice_fd (Ctx *ctx) { +#if CTX_PTY //CtxHeadless *fb = (void*)ctx->backend; return _ctx_mice_fd; +#endif } typedef struct _CtxHeadless CtxHeadless; @@ -36844,6 +46548,7 @@ Ctx *ctx_new_headless (int width, int height) // not to be done for headless, we want sRGB thumbs - at least not device specific // perhaps rec2020 or similar? + backend->type = CTX_BACKEND_HEADLESS; backend->ctx = _ctx_new_drawlist (width, height); backend->end_frame = ctx_tiled_end_frame; backend->process = (void*)ctx_drawlist_process; @@ -36910,7 +46615,9 @@ Ctx *ctx_new_headless (int width, int height) #if !__COSMOPOLITAN__ #include <fcntl.h> +#if CTX_PTY #include <sys/ioctl.h> +#endif #include <signal.h> #endif @@ -36923,8 +46630,10 @@ static int ctx_fb_single_buffer = 0; // used with the framebuffer this static int ctx_fb_get_mice_fd (Ctx *ctx) { +#if CTX_PTY //CtxFb *fb = (void*)ctx->backend; return _ctx_mice_fd; +#endif } static void ctx_fb_get_event_fds (Ctx *ctx, int *fd, int *count) @@ -37437,6 +47146,7 @@ Ctx *ctx_new_fb (int width, int height) ctx_get_contents ("file:///tmp/ctx.icc", &sdl_icc, &sdl_icc_length); #endif + backend->type = CTX_BACKEND_FB; backend->ctx = _ctx_new_drawlist (width, height); tiled->ctx_copy = _ctx_new_drawlist (width, height); tiled->width = width; @@ -37509,7 +47219,10 @@ Ctx *ctx_new_fb (int width, int height) tiled->evsource[tiled->evsource_count++] = kb; kb->priv = fb; } - EvSource *mice = evsource_mice_new (); + EvSource *mice = NULL; +#if CTX_PTY + mice = evsource_mice_new (); +#endif if (mice) { tiled->evsource[tiled->evsource_count++] = mice; @@ -37554,7 +47267,9 @@ Ctx *ctx_new_fb (int width, int height) #if !__COSMOPOLITAN__ #include <fcntl.h> +#if CTX_PTY #include <sys/ioctl.h> +#endif #include <signal.h> #endif @@ -38031,8 +47746,10 @@ static void vt_switch_cb (int sig) static int ctx_kms_get_mice_fd (Ctx *ctx) { +#if CTX_PTY //CtxKMS *fb = (void*)ctx->backend; return _ctx_mice_fd; +#endif } Ctx *ctx_new_kms (int width, int height) @@ -38065,6 +47782,7 @@ Ctx *ctx_new_kms (int width, int height) ctx_get_contents ("file:///tmp/ctx.icc", &sdl_icc, &sdl_icc_length); #endif + backend->type = CTX_BACKEND_KMS; backend->ctx = _ctx_new_drawlist (width, height); tiled->ctx_copy = _ctx_new_drawlist (width, height); @@ -38131,7 +47849,10 @@ Ctx *ctx_new_kms (int width, int height) tiled->evsource[tiled->evsource_count++] = kb; kb->priv = fb; } - EvSource *mice = evsource_mice_new (); + EvSource *mice = NULL; +#if CTX_PTY + mice = evsource_mice_new (); +#endif if (mice) { tiled->evsource[tiled->evsource_count++] = mice; @@ -38675,6 +48396,7 @@ Ctx *ctx_new_sdl (int width, int height) SDL_EnableScreenSaver (); SDL_GL_SetSwapInterval (1); + backend->type = CTX_BACKEND_SDL; backend->ctx = _ctx_new_drawlist (width, height); tiled->ctx_copy = _ctx_new_drawlist (width, height); tiled->width = width; @@ -38741,7 +48463,7 @@ Ctx *ctx_new_sdl (int width, int height) #endif } #endif - +#if CTX_TERM #if CTX_TERMINAL_EVENTS #if !__COSMOPOLITAN__ @@ -38839,7 +48561,7 @@ static void ctx_term_set_fg (int red, int green, int blue) _ctx_curfg=lc; if (_ctx_term256 == 0) { - fprintf(stderr, "\e[38;2;%i;%i;%im", red,green,blue); + fprintf(stderr, "\033[38;2;%i;%i;%im", red,green,blue); } else { @@ -38855,10 +48577,10 @@ static void ctx_term_set_fg (int red, int green, int blue) if (((int)(r/1.66)== (int)(g/1.66)) && ((int)(g/1.66) == ((int)(b/1.66)))) { - fprintf(stderr,"\e[38;5;%im", 16 + 216 + gray); + fprintf(stderr,"\033[38;5;%im", 16 + 216 + gray); } else - fprintf(stderr,"\e[38;5;%im", 16 + r * 6 * 6 + g * 6 + b); + fprintf(stderr,"\033[38;5;%im", 16 + r * 6 * 6 + g * 6 + b); } } @@ -38870,7 +48592,7 @@ static void ctx_term_set_bg(int red, int green, int blue) _ctx_curbg=lc; if (_ctx_term256 == 0) { - fprintf(stderr,"\e[48;2;%i;%i;%im", red,green,blue); + fprintf(stderr,"\033[48;2;%i;%i;%im", red,green,blue); } else { @@ -38886,10 +48608,10 @@ static void ctx_term_set_bg(int red, int green, int blue) if (((int)(r/1.66)== (int)(g/1.66)) && ((int)(g/1.66) == ((int)(b/1.66)))) { - fprintf(stderr,"\e[48;5;%im", 16 + 216 + gray); + fprintf(stderr,"\033[48;5;%im", 16 + 216 + gray); } else - fprintf(stderr,"\e[48;5;%im", 16 + r * 6 * 6 + g * 6 + b); + fprintf(stderr,"\033[48;5;%im", 16 + r * 6 * 6 + g * 6 + b); } } @@ -38898,9 +48620,9 @@ static int _ctx_term_force_full = 0; void ctx_term_scanout (CtxTerm *term) { int row = 1; - fprintf (stderr,"\e[H"); -// printf ("\e[?25l"); - fprintf (stderr, "\e[0m"); + fprintf (stderr,"\033[H"); +// printf ("\033[?25l"); + fprintf (stderr, "\033[0m"); int cur_fg[3]={-1,-1,-1}; int cur_bg[3]={-1,-1,-1}; @@ -38941,7 +48663,7 @@ void ctx_term_scanout (CtxTerm *term) // TODO: accumulate succesive such to be ignored items, // and compress them into one, making us compress largely // reused screens well - fprintf (stderr, "\e[C"); + fprintf (stderr, "\033[C"); } strcpy (cell->prev_utf8, cell->utf8); memcpy (cell->prev_fg, cell->fg, 3); @@ -38951,8 +48673,8 @@ void ctx_term_scanout (CtxTerm *term) fprintf (stderr, "\n\r"); row ++; } - fprintf (stderr, "\e[0m"); - //printf ("\e[?25h"); + fprintf (stderr, "\033[0m"); + //printf ("\033[?25h"); // } @@ -39599,10 +49321,10 @@ inline static void ctx_term_end_frame (Ctx *ctx) } #endif - printf ("\e[H"); - printf ("\e[0m"); + printf ("\033[H"); + printf ("\033[0m"); ctx_term_scanout (term); - printf ("\e[0m"); + printf ("\033[0m"); fflush (NULL); #if CTX_BRAILLE_TEXT while (rasterizer->glyphs) @@ -39617,7 +49339,7 @@ void ctx_term_destroy (CtxTerm *term) ctx_free (term->lines->data); ctx_list_remove (&term->lines, term->lines->data); } - printf ("\e[?25h"); // cursor on + printf ("\033[?25h"); // cursor on nc_at_exit (); ctx_free (term->pixels); ctx_destroy (term->host); @@ -39669,8 +49391,8 @@ Ctx *ctx_new_term (int width, int height) if (mode && strcmp (mode, "0") && strcmp (mode, "no")) _ctx_term_force_full = 1; - fprintf (stderr, "\e[?1049h"); - fprintf (stderr, "\e[?25l"); // cursor off + fprintf (stderr, "\033[?1049h"); + fprintf (stderr, "\033[?25l"); // cursor off int maxwidth = ctx_terminal_cols () * ctx_term_cw; int maxheight = (ctx_terminal_rows ()) * ctx_term_ch; @@ -39682,6 +49404,7 @@ Ctx *ctx_new_term (int width, int height) if (width > maxwidth) width = maxwidth; if (height > maxheight) height = maxheight; backend->ctx = ctx; + backend->type = CTX_BACKEND_TERM; term->width = width; term->height = height; @@ -39711,7 +49434,8 @@ Ctx *ctx_new_term (int width, int height) } #endif - +#endif +#if CTX_TERMIMG #if CTX_TERMINAL_EVENTS #if !__COSMOPOLITAN__ @@ -39759,24 +49483,24 @@ inline static void ctx_termimg_end_frame (Ctx *ctx) int i = 0; - printf ("\e[H"); - printf ("\e_Gf=24,s=%i,v=%i,t=d,a=T,m=1;\e\\", width, height); + printf ("\033[H"); + printf ("\033_Gf=24,s=%i,v=%i,t=d,a=T,m=1;\033\\", width, height); while (i < encoded_len) { if (i + 4096 < encoded_len) { - printf ("\e_Gm=1;"); + printf ("\033_Gm=1;"); } else { - printf ("\e_Gm=0;"); + printf ("\033_Gm=0;"); } for (int n = 0; n < 4000 && i < encoded_len; n++) { printf ("%c", encoded[i]); i++; } - printf ("\e\\"); + printf ("\033\\"); } ctx_free (encoded); @@ -39790,7 +49514,7 @@ void ctx_termimg_destroy (CtxTermImg *termimg) ctx_free (termimg->lines->data); ctx_list_remove (&termimg->lines, termimg->lines->data); } - printf ("\e[?25h"); // cursor on + printf ("\033[?25h"); // cursor on nc_at_exit (); ctx_free (termimg->pixels); ctx_destroy (termimg->host); @@ -39802,8 +49526,8 @@ Ctx *ctx_new_termimg (int width, int height) { Ctx *ctx = _ctx_new_drawlist (width, height); #if CTX_RASTERIZER - fprintf (stdout, "\e[?1049h"); - fprintf (stdout, "\e[?25l"); // cursor off + fprintf (stdout, "\033[?1049h"); + fprintf (stdout, "\033[?25l"); // cursor off CtxTermImg *termimg = (CtxTermImg*)ctx_calloc (sizeof (CtxTermImg), 1); CtxBackend *backend = (void*)termimg; @@ -39831,6 +49555,7 @@ Ctx *ctx_new_termimg (int width, int height) _ctx_mouse (ctx, NC_MOUSE_DRAG); ctx_set_backend (ctx, termimg); + backend->type = CTX_BACKEND_TERMIMG; backend->ctx = ctx; backend->process = ctx_termimg_process; backend->end_frame = ctx_termimg_end_frame; @@ -39845,6 +49570,7 @@ Ctx *ctx_new_termimg (int width, int height) } #endif +#endif typedef struct CtxCbBackend @@ -39983,88 +49709,6 @@ static int ctx_render_cb (CtxCbBackend *backend_cb, } fb = backend_cb->fb; -#if CTX_ENABLE_CBRLE - if (flags & CTX_FLAG_CBRLE) - { - int bitdepth = memory_budget * 8 / height / width; - int zformat; - if (bitdepth < 1) - { - fprintf (stderr, "not enough memory for 1bit cbrle\n"); - return 1; - } - switch (bitdepth) - { - case 1: zformat = CTX_FORMAT_CBRLE_1; break; - case 2: zformat = CTX_FORMAT_CBRLE_2; break; - case 3: zformat = CTX_FORMAT_CBRLE_3; break; - case 4: zformat = CTX_FORMAT_CBRLE_4; break; - case 5: zformat = CTX_FORMAT_CBRLE_5; break; - case 6: zformat = CTX_FORMAT_CBRLE_6; break; - case 7: zformat = CTX_FORMAT_CBRLE_7; break; - case 8: zformat = CTX_FORMAT_CBRLE_8; break; - case 9: zformat = CTX_FORMAT_CBRLE_9; break; - case 10: zformat = CTX_FORMAT_CBRLE_10; break; - case 11: zformat = CTX_FORMAT_CBRLE_11; break; - case 12: zformat = CTX_FORMAT_CBRLE_12; break; - case 13: zformat = CTX_FORMAT_CBRLE_13; break; - case 14: zformat = CTX_FORMAT_CBRLE_14; break; - case 15: zformat = CTX_FORMAT_CBRLE_15; break; - case 16: zformat = CTX_FORMAT_CBRLE_16; break; - case 17: zformat = CTX_FORMAT_CBRLE_17; break; - case 18: zformat = CTX_FORMAT_CBRLE_18; break; - case 19: zformat = CTX_FORMAT_CBRLE_19; break; - case 20: zformat = CTX_FORMAT_CBRLE_20; break; - case 21: zformat = CTX_FORMAT_CBRLE_21; break; - case 22: zformat = CTX_FORMAT_CBRLE_22; break; - case 23: zformat = CTX_FORMAT_CBRLE_23; break; - default: - case 24: zformat = CTX_FORMAT_CBRLE_24; break; - } - //zformat = CTX_FORMAT_GRAY4; - //bitdepth = 4; - int stride = (width * bitdepth + 7) / 8; - - memset(fb, 0, stride * height); - CtxRasterizer *r = ctx_rasterizer_init ((CtxRasterizer*)&backend_cb->rasterizer, - ctx, NULL, &ctx->state, fb, x0, y0, width, height, - stride, zformat, CTX_ANTIALIAS_DEFAULT); - ((CtxBackend*)r)->destroy = (CtxDestroyNotify)ctx_rasterizer_deinit; - ctx_push_backend (ctx, r); - - //ctx_scale (ctx, 1.0f/scale_factor, 1.0f/scale_factor); - ctx_translate (ctx, -1.0f * x0, -1.0f * y0); - if (active_mask) - ctx_render_ctx_masked (ctx, ctx, active_mask); - else - ctx_render_ctx (ctx, ctx); - - for (int y = 0; y < height;y++) - { - uint8_t rgba8[width*4]; - uint16_t rgb565[width]; - - _ctx_CBRLE_decompress ((uint8_t*)&fb[stride * y/2], - rgba8, width, stride, 0, width); - - for (int i = 0; i < width; i++) - { - rgb565[i] = ctx_888_to_565 ( ((uint32_t*)&rgba8[i*4])[0], 1); - } - - backend_cb->set_pixels (ctx, backend_cb->set_pixels_user_data, - //x0, y0, width, render_height, (uint16_t*)scaled, - //width * render_height * bpp); - x0,y0+y,width,1,(void*)rgb565, - stride);// * height); - } - - ctx_pop_backend (ctx); - - return abort; - } -#endif - if (flags & CTX_FLAG_LOWFI) { int scale_factor = 1; @@ -40644,7 +50288,7 @@ Ctx *ctx_new_tft (TFT_eSPI *tft, } #endif -#ifdef CTX_WASM_ORIG // Renamed from upstream, as we don't want this code to be compiled in, even though we're using Emscripten. +#ifdef EMSCRIPTEN #include "emscripten.h" #include <unistd.h> @@ -40931,6 +50575,11 @@ EM_ASM( static CtxFont ctx_fonts[CTX_MAX_FONTS];// = NULL; static int ctx_font_count = 0; +typedef struct CtxResolvedFont {uint32_t sqstr; int font_no;} CtxResolvedFont; + +#if CTX_RESOLVED_FONTS!=0 +static CtxResolvedFont ctx_resolved_fonts[CTX_RESOLVED_FONTS]; +#endif static void ctx_font_setup (Ctx *ctx); @@ -41199,7 +50848,7 @@ static int ctx_glyph_find_ctx (CtxFont *font, Ctx *ctx, uint32_t unichar) } } #else - int start = 0; + int start = -1; int end = ctx_font_get_length (font); int max_iter = 10; uint32_t start_glyph = ctx_glyph_find_next (font, ctx, start); @@ -41747,7 +51396,7 @@ _ctx_text (Ctx *ctx, CtxFont *font = &ctx_fonts[state->gstate.font]; float x = ctx->state.x; word[word_len]=0; - switch ( (int) ctx_state_get (state, CTX_textAlign) ) + switch ( (int) ctx_state_get (state, SQZ_textAlign) ) //switch (state->gstate.text_align) { case CTX_TEXT_ALIGN_START: @@ -41763,7 +51412,7 @@ _ctx_text (Ctx *ctx, } float y = ctx->state.y; float baseline_offset = 0.0f; - switch ( (int) ctx_state_get (state, CTX_textBaseline) ) + switch ( (int) ctx_state_get (state, SQZ_textBaseline) ) { case CTX_TEXT_BASELINE_HANGING: /* XXX : crude */ @@ -41875,6 +51524,8 @@ _ctx_text (Ctx *ctx, } if (!visible) + { ctx->state.x =x; ctx->state.y=y; } + else { ctx_move_to (ctx, x, y); } } @@ -41951,6 +51602,75 @@ ctx_stroke_text (Ctx *ctx, const char *string, ctx_text_stroke (ctx, string); } + +int +ctx_font_get_vmetrics (Ctx *ctx, + CtxFont *font, + float *ascent, + float *descent, + float *linegap); + +int +ctx_font_get_vmetrics (Ctx *ctx, + CtxFont *font, + float *ascent, + float *descent, + float *linegap) +{ +#if CTX_ONE_FONT_ENGINE + if (ascent) *ascent=0.8f; + if (descent)*descent=0.2f; + if (linegap)*linegap=1.2f; +#else + //float font_size = 1.0f; + //if (ctx) + // font_size = ctx->state.gstate.font_size; + switch (font->type) + { +#if CTX_FONT_ENGINE_CTX_FS + case 3: +#endif + case 0: + if (ascent) *ascent=0.8f; + if (descent)*descent=0.2f; + if (linegap)*linegap=1.2f; + return 0; +#if CTX_FONT_ENGINE_STB + case 1: + case 2: + { + int aval,dval,lgval; + float font_size = 16.0f; + if (ctx) + font_size = ctx->state.gstate.font_size; + stbtt_GetFontVMetrics(&font->stb.ttf_info, &aval, &dval, &lgval); + float scale = stbtt_ScaleForPixelHeight (&font->stb.ttf_info, font_size); + if (ascent) *ascent= (aval * scale) / font_size; + if (descent)*descent= (dval * scale) / font_size; + if (linegap)*linegap= (lgval * scale) / font_size; + } + + return 0; +#endif + } +#endif + return 0; +} + +int +ctx_font_extents (Ctx *ctx, + float *ascent, + float *descent, + float *line_gap) +{ + CtxFont *font = &ctx_fonts[ctx->state.gstate.font]; + return ctx_font_get_vmetrics (ctx, + font, + ascent, + descent, + line_gap); +} + static const char *ctx_font_get_name (CtxFont *font) { #if CTX_ONE_FONT_ENGINE @@ -41973,20 +51693,37 @@ static const char *ctx_font_get_name (CtxFont *font) static int _ctx_resolve_font (const char *name) { + int ret = -1; +#if CTX_RESOLVED_FONTS!=0 + uint32_t sqstr = ctx_strhash (name); + int pos = sqstr % CTX_RESOLVED_FONTS; + int tries = 0; + while (ctx_resolved_fonts[pos].sqstr && tries < CTX_RESOLVED_FONTS) + { + if (ctx_resolved_fonts[pos].sqstr == sqstr) + return ctx_resolved_fonts[pos].font_no; + pos++; + pos %= CTX_RESOLVED_FONTS; + tries++; + } +#endif + char temp[ctx_strlen (name)+1]; /* first we look for exact */ - for (int i = 0; i < ctx_font_count; i ++) + for (int i = 0; ret < 0 && i < ctx_font_count; i ++) { if (!ctx_strcmp (ctx_font_get_name (&ctx_fonts[i]), name) ) - { return i; } + { ret = i; } } /* ... and substring matches for passed in string */ - for (int i = 0; i < ctx_font_count; i ++) + for (int i = 0; ret < 0 && i < ctx_font_count; i ++) { if (ctx_strstr (ctx_font_get_name (&ctx_fonts[i]), name) ) - { return i; } + { ret = i; } } + if (ret < 0) + { /* then we normalize some names */ if (!strncmp (name, "Helvetica", 9)) { @@ -42013,12 +51750,13 @@ static int _ctx_resolve_font (const char *name) { name = "Courier"; } + } /* and attempt substring matching with mangled named * permitting matches with length and two first chars * to be valid */ - { + if (ret < 0 ) { char *subname = (char*)name; int namelen = 0; if (strchr (subname, ' ')) @@ -42027,32 +51765,38 @@ static int _ctx_resolve_font (const char *name) namelen = subname - name; subname++; } - for (int i = 0; i < ctx_font_count; i ++) + for (int i = 0; ret < 0 && i < ctx_font_count; i ++) { const char *font_name = ctx_font_get_name (&ctx_fonts[i]); if (font_name[0]==name[0] && font_name[1]==name[1] && font_name[namelen] == name[namelen] && (namelen == 0 || ctx_strstr (font_name, subname) )) - return i; + ret = i; } } /* then we look for a match of the substring after the first * space */ - if (strchr (name, ' ')) + if (ret < 0 && strchr (name, ' ')) { char *subname = strchr (name, ' '); - for (int i = 0; i < ctx_font_count; i ++) + for (int i = 0; ret < 0 && i < ctx_font_count; i ++) { const char *font_name = ctx_font_get_name (&ctx_fonts[i]); if (ctx_strstr (font_name, subname) ) - { return i; } + { ret = i; } } } - - return -1; +#if CTX_RESOLVED_FONTS!=0 + if (ret >=0 && ctx_resolved_fonts[pos].sqstr == 0) + { + ctx_resolved_fonts[pos].sqstr = sqstr; + ctx_resolved_fonts[pos].font_no = ret; + } +#endif + return ret; } const char *ctx_get_font_name (Ctx *ctx, int no) @@ -42078,7 +51822,6 @@ int ctx_resolve_font (const char *name) } - #if !( defined(CTX_FONT_0) ||\ defined(CTX_FONT_1) ||\ defined(CTX_FONT_2) ||\ @@ -42864,10 +52607,10 @@ ctx_formatter_process (void *user_data, CtxCommand *c) _ctx_print_name (formatter, entry->code); switch (c->set.key_hash) { - case CTX_x: ctx_formatter_addstrf (formatter, " 'x' "); break; - case CTX_y: ctx_formatter_addstrf (formatter, " 'y' "); break; - case CTX_width: ctx_formatter_addstrf (formatter, " width "); break; - case CTX_height: ctx_formatter_addstrf (formatter, " height "); break; + case SQZ_x: ctx_formatter_addstrf (formatter, " 'x' "); break; + case SQZ_y: ctx_formatter_addstrf (formatter, " 'y' "); break; + case SQZ_width: ctx_formatter_addstrf (formatter, " width "); break; + case SQZ_height: ctx_formatter_addstrf (formatter, " height "); break; default: ctx_formatter_addstrf (formatter, " %d ", c->set.key_hash); } @@ -43254,8 +52997,9 @@ ctx_current_path (Ctx *ctx) drawlist->entries = (CtxEntry*)(&drawlist[1]); drawlist->size = drawlist->count = ctx->current_path.count; drawlist->flags = CTX_DRAWLIST_DOESNT_OWN_ENTRIES; - memcpy (drawlist->entries, ctx->current_path.entries, - drawlist->count * 9); + if (drawlist->count) + memcpy (drawlist->entries, ctx->current_path.entries, + drawlist->count * 9); return drawlist; } @@ -43354,7 +53098,7 @@ ctx_gstate_push (CtxState *state) { return; } state->gstate_stack[state->gstate_no] = state->gstate; state->gstate_no++; - ctx_state_set (state, CTX_newState, 0.0f); + ctx_state_set (state, SQZ_newState, 0.0f); state->has_clipped=0; } @@ -43451,14 +53195,14 @@ ctx_get_image_data (Ctx *ctx, int sx, int sy, int sw, int sh, void ctx_screenshot (Ctx *ctx, const char *output_path) { -#ifdef INCLUDE_STB_IMAGE_WRITE_H +#if CTX_IMAGE_WRITE uint32_t width = ctx_width (ctx); uint32_t height = ctx_height (ctx); uint8_t *buf = ctx_malloc (width * height * 4); ctx_get_image_data (ctx, 0, 0, width, height, CTX_FORMAT_RGBA8, width *4, buf); - stbi_write_png (output_path, width, height, 4, buf, width * 4); + _ctx_write_png (output_path, width, height, 4, buf); ctx_free (buf); #endif } @@ -43517,10 +53261,10 @@ static int ctx_eid_valid (Ctx *ctx, const char *eid, int *w, int *h) //FILE *f = fopen ("/tmp/l", "a"); //fprintf (f, "%i client removing %s\n", getpid(), eid_info->eid); //fclose (f); - ctx_free (eid_info->eid); - ctx_free (eid_info); ctx_list_remove (&ctx->eid_db, eid_info); ctx_list_remove (&to_remove, eid_info); + ctx_free (eid_info->eid); + ctx_free (eid_info); } return ret; } @@ -43540,10 +53284,10 @@ void ctx_drop_eid (Ctx *ctx, const char *eid) while (to_remove) { CtxEidInfo *eid_info = (CtxEidInfo*)to_remove->data; - ctx_free (eid_info->eid); - ctx_free (eid_info); ctx_list_remove (&ctx->eid_db, eid_info); ctx_list_remove (&to_remove, eid_info); + ctx_free (eid_info->eid); + ctx_free (eid_info); } for (int i = 0; i < CTX_MAX_TEXTURES; i++) @@ -43781,7 +53525,7 @@ ctx_texture_load (Ctx *ctx, const char *path, int *tw, int *th, char *reid) return; } -#ifdef STBI_INCLUDE_STB_IMAGE_H +#if CTX_STB_IMAGE CtxPixelFormat pixel_format = CTX_FORMAT_RGBA8; int w, h, components; unsigned char *pixels = NULL; @@ -44231,10 +53975,8 @@ ctx_font_family (Ctx *ctx, const char *name) { #if CTX_BACKEND_TEXT ctx_process_cmd_str (ctx, CTX_FONT, name, 0, 0); - _ctx_font (ctx, name); -#else - _ctx_font (ctx, name); #endif + _ctx_font (ctx, name); } void @@ -44353,26 +54095,26 @@ CtxExtend ctx_get_extend (Ctx *ctx) CtxTextAlign ctx_get_text_align (Ctx *ctx) { - return (CtxTextAlign)ctx_state_get (&ctx->state, CTX_textAlign); + return (CtxTextAlign)ctx_state_get (&ctx->state, SQZ_textAlign); } float ctx_get_wrap_left (Ctx *ctx) { - return ctx_state_get (&ctx->state, CTX_wrapLeft); + return ctx_state_get (&ctx->state, SQZ_wrapLeft); } float ctx_get_wrap_right (Ctx *ctx) { - return ctx_state_get (&ctx->state, CTX_wrapRight); + return ctx_state_get (&ctx->state, SQZ_wrapRight); } float ctx_get_line_height (Ctx *ctx) { - return ctx_state_get (&ctx->state, CTX_lineHeight); + return ctx_state_get (&ctx->state, SQZ_lineHeight); } CtxTextBaseline ctx_get_text_baseline (Ctx *ctx) { - return (CtxTextBaseline)ctx_state_get (&ctx->state, CTX_textBaseline); + return (CtxTextBaseline)ctx_state_get (&ctx->state, SQZ_textBaseline); } CtxLineCap ctx_get_line_cap (Ctx *ctx) @@ -44630,13 +54372,13 @@ ctx_interpret_style (CtxState *state, CtxEntry *entry, void *data) switch (entry->code) { case CTX_LINE_HEIGHT: - ctx_state_set (state, CTX_lineHeight, ctx_arg_float (0) ); + ctx_state_set (state, SQZ_lineHeight, ctx_arg_float (0) ); break; case CTX_WRAP_LEFT: - ctx_state_set (state, CTX_wrapLeft, ctx_arg_float (0) ); + ctx_state_set (state, SQZ_wrapLeft, ctx_arg_float (0) ); break; case CTX_WRAP_RIGHT: - ctx_state_set (state, CTX_wrapRight, ctx_arg_float (0) ); + ctx_state_set (state, SQZ_wrapRight, ctx_arg_float (0) ); break; case CTX_LINE_DASH_OFFSET: state->gstate.line_dash_offset = ctx_arg_float (0); @@ -44674,13 +54416,13 @@ ctx_interpret_style (CtxState *state, CtxEntry *entry, void *data) state->gstate.extend = (CtxExtend) ctx_arg_u32 (0); break; case CTX_TEXT_ALIGN: - ctx_state_set (state, CTX_textAlign, ctx_arg_u8 (0) ); + ctx_state_set (state, SQZ_textAlign, ctx_arg_u8 (0) ); break; case CTX_TEXT_BASELINE: - ctx_state_set (state, CTX_textBaseline, ctx_arg_u8 (0) ); + ctx_state_set (state, SQZ_textBaseline, ctx_arg_u8 (0) ); break; case CTX_TEXT_DIRECTION: - ctx_state_set (state, CTX_textDirection, ctx_arg_u8 (0) ); + ctx_state_set (state, SQZ_textDirection, ctx_arg_u8 (0) ); break; case CTX_GLOBAL_ALPHA: state->gstate.global_alpha_u8 = ctx_float_to_u8 (ctx_arg_float (0) ); @@ -45159,10 +54901,10 @@ ctx_state_init (CtxState *state) state->gstate.image_smoothing = 1; state->gstate.source_stroke.type = CTX_SOURCE_INHERIT_FILL; ctx_color_set_graya (state, &state->gstate.source_fill.color, 1.0f, 1.0f); - ctx_state_set (state, CTX_lineHeight, 1.0f); + ctx_state_set (state, SQZ_lineHeight, 1.0f); #if CTX_TEXT_WRAP - ctx_state_set (state, CTX_wrapLeft, 0.0f); - ctx_state_set (state, CTX_wrapRight, 0.0f); + ctx_state_set (state, SQZ_wrapLeft, 0.0f); + ctx_state_set (state, SQZ_wrapRight, 0.0f); #endif state->ink_min_x = 8192; @@ -45303,9 +55045,17 @@ ctx_new_drawlist (int width, int height) static Ctx *ctx_new_ui (int width, int height, const char *backend); #endif +#if CTX_PTY==0 +Ctx *ctx_pico_init (void); +#endif + CTX_EXPORT Ctx * ctx_new (int width, int height, const char *backend) { +#if CTX_PTY==0 + return ctx_pico_init (); +#endif + #if CTX_EVENTS if (backend && !ctx_strcmp (backend, "drawlist")) #endif @@ -45359,7 +55109,7 @@ ctx_destroy (Ctx *ctx) if (!ctx) { return; } -#if CTX_CLIENTS +#if CTX_VT while (ctx_clients (ctx)) ctx_client_remove (ctx, ctx_clients(ctx)->data); #endif @@ -45460,7 +55210,7 @@ ctx_render_ctx_textures (Ctx *ctx, Ctx *d_ctx) void ctx_quit (Ctx *ctx) { -#if CTX_CLIENTS +#if CTX_VT while (ctx_clients (ctx)) ctx_client_remove (ctx, ctx_clients(ctx)->data); #endif @@ -45597,6 +55347,10 @@ 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); +#endif + int ctx_get_contents2 (const char *uri, unsigned char **contents, @@ -45614,7 +55368,11 @@ ctx_get_contents2 (const char *uri, } if (strchr (uri, '#')) + { + if (temp_uri == NULL) + uri = temp_uri = strdup (uri); strchr (uri, '#')[0]=0; + } for (CtxList *l = registered_contents; l; l = l->next) { @@ -45637,6 +55395,12 @@ 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 + else if (!strncmp (uri, "itk:", 4)) + { + success = itk_static_get_contents (uri, (char**)contents, length); + } +#endif else { #if CTX_CURL @@ -45952,6 +55716,18 @@ CtxMediaTypeClass ctx_media_type_class (const char *media_type) return ret; } +#else +int +ctx_get_contents (const char *uri, + unsigned char **contents, + long *length) +{ + *contents = NULL; + *length = -1; + return -1; +//ctx_get_contents2 (uri, contents, length, 1024*1024*1024); +} + #endif @@ -45998,20 +55774,24 @@ float ctx_y (Ctx *ctx) return y; } -CtxBackendType ctx_backend_type (Ctx *ctx) +static CtxBackendType __ctx_backend_type (Ctx *ctx) { + if (!ctx) + return CTX_BACKEND_NONE; CtxBackend *backend = ctx->backend; if (backend == NULL) return CTX_BACKEND_NONE; -#if CTX_TERMINAL_EVENTS +#if CTX_FORMATTER else if (backend->destroy == (void*) ctx_ctx_destroy) return CTX_BACKEND_CTX; #if CTX_HEADLESS else if (backend->destroy == (void*) ctx_headless_destroy) return CTX_BACKEND_HEADLESS; #endif #endif #if CTX_TERMINAL_EVENTS +#if CTX_TERM else if (backend->destroy == (void*) ctx_term_destroy) return CTX_BACKEND_TERM; #endif +#endif #if CTX_RASTERIZER else if (backend->process == (void*) ctx_hasher_process) return CTX_BACKEND_HASHER; #endif @@ -46027,1372 +55807,408 @@ CtxBackendType ctx_backend_type (Ctx *ctx) #if CTX_SDL else if (backend->destroy == (void*) ctx_sdl_destroy) return CTX_BACKEND_SDL; #endif -#if CTX_CAIRO - else if (backend->destroy == (void*) ctx_cairo_destroy) return CTX_BACKEND_CAIRO; -#endif -#if CTX_TERMINAL_EVENTS - else if (backend->destroy == (void*) ctx_termimg_destroy) return CTX_BACKEND_TERMIMG; -#endif - return CTX_BACKEND_NONE; -} - - -void ctx_set_fullscreen (Ctx *ctx, int val) -{ -#if CTX_SDL - if (ctx_backend_type (ctx) == CTX_BACKEND_SDL) - ctx_sdl_set_fullscreen (ctx, val); -#endif -} - -int ctx_get_fullscreen (Ctx *ctx) -{ -#if CTX_SDL - if (ctx_backend_type (ctx) == CTX_BACKEND_SDL) - return ctx_sdl_get_fullscreen (ctx); -#endif - return 0; -} - -CtxPixelFormatInfo *ctx_pixel_formats = -#if CTX_COMPOSITE -ctx_pixel_formats_generic; -#else -NULL; -#endif - -CtxPixelFormatInfo * -ctx_pixel_format_info (CtxPixelFormat format) -{ - if (!ctx_pixel_formats) - { - assert (0); - return NULL; - } - for (unsigned int i = 0; ctx_pixel_formats[i].pixel_format; i++) - { - if (ctx_pixel_formats[i].pixel_format == format) - { - return &ctx_pixel_formats[i]; - } - } - assert (0); - return NULL; -} - - -#if CTX_RASTERIZER - - -void (*ctx_rasterizer_rasterize_edges) (CtxRasterizer *rasterizer, const int fill_rule -#if CTX_SHAPE_CACHE - ,CtxShapeEntry *shape -#endif - ) = - ctx_rasterizer_rasterize_edges_generic; - -void (*ctx_composite_setup) (CtxRasterizer *rasterizer) = - ctx_composite_setup_generic; -#if CTX_FAST_FILL_RECT -void (*ctx_composite_stroke_rect) (CtxRasterizer *rasterizer, - float x0, - float y0, - float x1, - float y1, - float line_width) = - ctx_composite_stroke_rect_generic; - -void (*ctx_composite_fill_rect) (CtxRasterizer *rasterizer, - float x0, - float y0, - float x1, - float y1, - uint8_t cov) = - ctx_composite_fill_rect_generic; -#endif - -#endif - - -CTX_EXPORT void -ctx_logo (Ctx *ctx, float x, float y, float dim) -{ - //float width = ctx_width (ctx); - //float height = ctx_height (ctx); - ctx_save (ctx); - ctx_translate (ctx, x, y);// - //width/2, height/2); - - //if (width < height) height = width; - - ctx_scale (ctx, dim, dim); - ctx_translate (ctx, -0.5f, -0.5f); - ctx_begin_path (ctx); - ctx_rgba(ctx,1,1,1,0.4f); - ctx_move_to(ctx,0.43956786f,0.90788066f); - ctx_rel_curve_to(ctx,0.0195929f,0.0102943f,0.0716181f,0.0218038f,0.10361884f,-0.0167646f); - ctx_line_to (ctx,0.93768705f,0.37887837f); - ctx_rel_curve_to (ctx, 0.019925f,-0.0342044f,-0.00963f,-0.0544608f,-0.0308834f,-0.0508084f); - ctx_rel_curve_to (ctx,-0.17965502f,0.0285588f,-0.35466092f,-0.055125f,-0.45096394f,-0.21253089f); - ctx_rel_curve_to (ctx, -0.0176003f,-0.02988716f, -0.0594422f,-0.01560777f,-0.0594422f,0.0139473f); - ctx_rel_curve_to (ctx, 0, 0.0591101f,0.003321f,0.49845135f,0.001991f, 0.70699722f); - ctx_rel_curve_to (ctx, 0.00039042f, 0.0283487f,0.0157362f,0.0529866f,0.0408456f,0.070733f); - ctx_fill (ctx); - - ctx_move_to (ctx, 0.39772584f,0.91850721f); - ctx_rel_line_to (ctx, -0.0664159f, 0); - ctx_rel_curve_to (ctx, -0.15408489f,0, -0.27894675f,-0.12486192f, -0.27894675f,-0.2789468f); - ctx_rel_curve_to (ctx, 0,-0.15408489f, 0.12486186f,-0.27861466f, 0.27894675f,-0.27894675f); - ctx_rel_line_to (ctx, 0.18585599f,0.0000662f); - ctx_rel_curve_to (ctx, 0.0111839f,0.00017138f, 0.0158287f,0.001542f, 0.0263337f,0.0134822f); - ctx_rel_curve_to (ctx, 0.11733258f,0.14373102f, 0.3018009f,0.36870115f, 0.3942639f,0.49195316f); - ctx_rel_curve_to (ctx, 0.0185394f,0.0332794f, -0.0106225f,0.0505515f, -0.0228143f,0.0505207f); - - ctx_linear_gradient (ctx, 0.0525f, 0, 0.9905f, 0); - ctx_gradient_add_stop (ctx, 0.0f, 1.0f, 1.0f, 0.66f, 1.0f); - ctx_gradient_add_stop (ctx, 0.2f, 1.0f, 0.66f, 0.0f, 1.0f); - ctx_gradient_add_stop (ctx, 0.5f, 1.0f, 0.0f, 0.0f, 1.0f); - ctx_gradient_add_stop (ctx, 1.0f, 0.4f, 0.0f, 0.53f, 1.0f); - ctx_fill (ctx); - - - - ctx_linear_gradient(ctx, 0.697f, 0.17f, 0.4318f, 0.884f); - ctx_gradient_add_stop (ctx, 0, 0.26f, 0.26f, 1, 1.0f); - ctx_gradient_add_stop (ctx, 0.3f, 0, 1, 1, 0.4f); - ctx_gradient_add_stop (ctx, 1.0f, 0, 1, 0.26f,1.0f); - - ctx_move_to(ctx,0.43956786f,0.90788066f); - ctx_rel_curve_to(ctx,0.0195929f,0.0102943f,0.0716181f,0.0218038f,0.10361884f,-0.0167646f); - ctx_line_to (ctx,0.93768705f,0.37887837f); - ctx_rel_curve_to (ctx, 0.019925f,-0.0342044f,-0.00963f,-0.0544608f,-0.0308834f,-0.0508084f); - ctx_rel_curve_to (ctx,-0.17965502f,0.0285588f,-0.35466092f,-0.055125f,-0.45096394f,-0.21253089f); - ctx_rel_curve_to (ctx, -0.0176003f,-0.02988716f, -0.0594422f,-0.01560777f,-0.0594422f,0.0139473f); - ctx_rel_curve_to (ctx, 0, 0.0591101f,0.003321f,0.49845135f,0.001991f, 0.70699722f); - ctx_rel_curve_to (ctx, 0.00039042f, 0.0283487f,0.0157362f,0.0529866f,0.0408456f,0.070733f); - ctx_fill (ctx); - - ctx_restore (ctx); -} - -void -ctx_CBRLE_decompress (const uint8_t *cbrle, uint8_t *rgba8, int width, int size); -void -ctx_CBRLE_decompress (const uint8_t *cbrle, uint8_t *rgba8, int width, int size) -{ -#if CTX_ENABLE_CBRLE - _ctx_CBRLE_decompress (cbrle, rgba8, width, size, 0, width); -#endif -} - - -void -ctx_clip_extents (Ctx *ctx, float *x0, float *y0, - float *x1, float *y1) -{ - CtxGState *gstate = &ctx->state.gstate; - if(x0)*x0 = gstate->clip_min_x; - if(y0)*y0 = gstate->clip_min_y; - if(x1)*x1 = gstate->clip_max_x; - if(y1)*y1 = gstate->clip_max_y; -} -/* - * tinf - tiny inflate library (inflate, gzip, zlib) - * - * Copyright (c) 2003-2019 Joergen Ibsen - * - * This software is provided 'as-is', without any express or implied - * warranty. In no event will the authors be held liable for any damages - * arising from the use of this software. - * - * Permission is granted to anyone to use this software for any purpose, - * including commercial applications, and to alter it and redistribute it - * freely, subject to the following restrictions: - * - * 1. The origin of this software must not be misrepresented; you must - * not claim that you wrote the original software. If you use this - * software in a product, an acknowledgment in the product - * documentation would be appreciated but is not required. - * - * 2. Altered source versions must be plainly marked as such, and must - * not be misrepresented as being the original software. - * - * 3. This notice may not be removed or altered from any source - * distribution. - */ - -#ifndef TINF_H_INCLUDED -#define TINF_H_INCLUDED - -#ifdef __cplusplus -extern "C" { -#endif - -#define TINF_VER_MAJOR 1 /**< Major version number */ -#define TINF_VER_MINOR 2 /**< Minor version number */ -#define TINF_VER_PATCH 1 /**< Patch version number */ -#define TINF_VER_STRING "1.2.1" /**< Version number as a string */ - -#ifndef TINFCC -# ifdef __WATCOMC__ -# define TINFCC __cdecl -# else -# define TINFCC -# endif -#endif - -/** - * Status codes returned. - * - * @see tinf_uncompress, tinf_gzip_uncompress, tinf_zlib_uncompress - */ -typedef enum { - TINF_OK = 0, /**< Success */ - TINF_DATA_ERROR = -3, /**< Input error */ - TINF_BUF_ERROR = -5 /**< Not enough room for output */ -} tinf_error_code; - -/** - * Initialize global data used by tinf. - * - * @deprecated No longer required, may be removed in a future version. - */ -void TINFCC tinf_init(void); - -/** - * Decompress `sourceLen` bytes of deflate data from `source` to `dest`. - * - * The variable `destLen` points to must contain the size of `dest` on entry, - * and will be set to the size of the decompressed data on success. - * - * Reads at most `sourceLen` bytes from `source`. - * Writes at most `*destLen` bytes to `dest`. - * - * @param dest pointer to where to place decompressed data - * @param destLen pointer to variable containing size of `dest` - * @param source pointer to compressed data - * @param sourceLen size of compressed data - * @return `TINF_OK` on success, error code on error - */ -int TINFCC tinf_uncompress(void *dest, unsigned int *destLen, - const void *source, unsigned int sourceLen); - -/** - * Decompress `sourceLen` bytes of gzip data from `source` to `dest`. - * - * The variable `destLen` points to must contain the size of `dest` on entry, - * and will be set to the size of the decompressed data on success. - * - * Reads at most `sourceLen` bytes from `source`. - * Writes at most `*destLen` bytes to `dest`. - * - * @param dest pointer to where to place decompressed data - * @param destLen pointer to variable containing size of `dest` - * @param source pointer to compressed data - * @param sourceLen size of compressed data - * @return `TINF_OK` on success, error code on error - */ -int TINFCC tinf_gzip_uncompress(void *dest, unsigned int *destLen, - const void *source, unsigned int sourceLen); - -/** - * Decompress `sourceLen` bytes of zlib data from `source` to `dest`. - * - * The variable `destLen` points to must contain the size of `dest` on entry, - * and will be set to the size of the decompressed data on success. - * - * Reads at most `sourceLen` bytes from `source`. - * Writes at most `*destLen` bytes to `dest`. - * - * @param dest pointer to where to place decompressed data - * @param destLen pointer to variable containing size of `dest` - * @param source pointer to compressed data - * @param sourceLen size of compressed data - * @return `TINF_OK` on success, error code on error - */ -int TINFCC tinf_zlib_uncompress(void *dest, unsigned int *destLen, - const void *source, unsigned int sourceLen); - -/** - * Compute Adler-32 checksum of `length` bytes starting at `data`. - * - * @param data pointer to data - * @param length size of data - * @return Adler-32 checksum - */ -unsigned int TINFCC tinf_adler32(const void *data, unsigned int length); - -/** - * Compute CRC32 checksum of `length` bytes starting at `data`. - * - * @param data pointer to data - * @param length size of data - * @return CRC32 checksum - */ -unsigned int TINFCC tinf_crc32(const void *data, unsigned int length); - -#ifdef __cplusplus -} /* extern "C" */ -#endif - -#endif /* TINF_H_INCLUDED */ -/* - * Adler-32 checksum - * - * Copyright (c) 2003-2019 Joergen Ibsen - * - * This software is provided 'as-is', without any express or implied - * warranty. In no event will the authors be held liable for any damages - * arising from the use of this software. - * - * Permission is granted to anyone to use this software for any purpose, - * including commercial applications, and to alter it and redistribute it - * freely, subject to the following restrictions: - * - * 1. The origin of this software must not be misrepresented; you must - * not claim that you wrote the original software. If you use this - * software in a product, an acknowledgment in the product - * documentation would be appreciated but is not required. - * - * 2. Altered source versions must be plainly marked as such, and must - * not be misrepresented as being the original software. - * - * 3. This notice may not be removed or altered from any source - * distribution. - */ - -/* - * Adler-32 algorithm taken from the zlib source, which is - * Copyright (C) 1995-1998 Jean-loup Gailly and Mark Adler - */ - -//#include "tinf.h" - -#define A32_BASE 65521 -#define A32_NMAX 5552 - -unsigned int tinf_adler32(const void *data, unsigned int length) -{ - const unsigned char *buf = (const unsigned char *) data; - - unsigned int s1 = 1; - unsigned int s2 = 0; - - while (length > 0) { - int k = length < A32_NMAX ? length : A32_NMAX; - int i; - - for (i = k / 16; i; --i, buf += 16) { - s1 += buf[0]; - s2 += s1; - s1 += buf[1]; - s2 += s1; - s1 += buf[2]; - s2 += s1; - s1 += buf[3]; - s2 += s1; - s1 += buf[4]; - s2 += s1; - s1 += buf[5]; - s2 += s1; - s1 += buf[6]; - s2 += s1; - s1 += buf[7]; - s2 += s1; - - s1 += buf[8]; - s2 += s1; - s1 += buf[9]; - s2 += s1; - s1 += buf[10]; - s2 += s1; - s1 += buf[11]; - s2 += s1; - s1 += buf[12]; - s2 += s1; - s1 += buf[13]; - s2 += s1; - s1 += buf[14]; - s2 += s1; - s1 += buf[15]; - s2 += s1; - } - - for (i = k % 16; i; --i) { - s1 += *buf++; - s2 += s1; - } - - s1 %= A32_BASE; - s2 %= A32_BASE; - - length -= k; - } - - return (s2 << 16) | s1; -} -/* - * CRC32 checksum - * - * Copyright (c) 1998-2019 Joergen Ibsen - * - * This software is provided 'as-is', without any express or implied - * warranty. In no event will the authors be held liable for any damages - * arising from the use of this software. - * - * Permission is granted to anyone to use this software for any purpose, - * including commercial applications, and to alter it and redistribute it - * freely, subject to the following restrictions: - * - * 1. The origin of this software must not be misrepresented; you must - * not claim that you wrote the original software. If you use this - * software in a product, an acknowledgment in the product - * documentation would be appreciated but is not required. - * - * 2. Altered source versions must be plainly marked as such, and must - * not be misrepresented as being the original software. - * - * 3. This notice may not be removed or altered from any source - * distribution. - */ - -/* - * CRC32 algorithm taken from the zlib source, which is - * Copyright (C) 1995-1998 Jean-loup Gailly and Mark Adler - */ - -//#include "tinf.h" - -static const unsigned int tinf_crc32tab[16] = { - 0x00000000, 0x1DB71064, 0x3B6E20C8, 0x26D930AC, 0x76DC4190, - 0x6B6B51F4, 0x4DB26158, 0x5005713C, 0xEDB88320, 0xF00F9344, - 0xD6D6A3E8, 0xCB61B38C, 0x9B64C2B0, 0x86D3D2D4, 0xA00AE278, - 0xBDBDF21C -}; - -unsigned int tinf_crc32(const void *data, unsigned int length) -{ - const unsigned char *buf = (const unsigned char *) data; - unsigned int crc = 0xFFFFFFFF; - unsigned int i; - - if (length == 0) { - return 0; - } - - for (i = 0; i < length; ++i) { - crc ^= buf[i]; - crc = tinf_crc32tab[crc & 0x0F] ^ (crc >> 4); - crc = tinf_crc32tab[crc & 0x0F] ^ (crc >> 4); - } - - return crc ^ 0xFFFFFFFF; -} -/* - * tinflate - tiny inflate - * - * Copyright (c) 2003-2019 Joergen Ibsen - * - * This software is provided 'as-is', without any express or implied - * warranty. In no event will the authors be held liable for any damages - * arising from the use of this software. - * - * Permission is granted to anyone to use this software for any purpose, - * including commercial applications, and to alter it and redistribute it - * freely, subject to the following restrictions: - * - * 1. The origin of this software must not be misrepresented; you must - * not claim that you wrote the original software. If you use this - * software in a product, an acknowledgment in the product - * documentation would be appreciated but is not required. - * - * 2. Altered source versions must be plainly marked as such, and must - * not be misrepresented as being the original software. - * - * 3. This notice may not be removed or altered from any source - * distribution. - */ - -//#include "tinf.h" - -#include <assert.h> -#include <limits.h> - -#if defined(UINT_MAX) && (UINT_MAX) < 0xFFFFFFFFUL -# error "tinf requires unsigned int to be at least 32-bit" -#endif - -/* -- Internal data structures -- */ - -struct tinf_tree { - unsigned short counts[16]; /* Number of codes with a given length */ - unsigned short symbols[288]; /* Symbols sorted by code */ - int max_sym; -}; - -struct tinf_data { - const unsigned char *source; - const unsigned char *source_end; - unsigned int tag; - int bitcount; - int overflow; - - unsigned char *dest_start; - unsigned char *dest; - unsigned char *dest_end; - - struct tinf_tree ltree; /* Literal/length tree */ - struct tinf_tree dtree; /* Distance tree */ -}; - -/* -- Utility functions -- */ - -static inline unsigned int read_le16(const unsigned char *p) -{ - return ((unsigned int) p[0]) - | ((unsigned int) p[1] << 8); -} - -/* Build fixed Huffman trees */ -static void tinf_build_fixed_trees(struct tinf_tree *lt, struct tinf_tree *dt) -{ - int i; - - /* Build fixed literal/length tree */ - for (i = 0; i < 16; ++i) { - lt->counts[i] = 0; - } - - lt->counts[7] = 24; - lt->counts[8] = 152; - lt->counts[9] = 112; - - for (i = 0; i < 24; ++i) { - lt->symbols[i] = 256 + i; - } - for (i = 0; i < 144; ++i) { - lt->symbols[24 + i] = i; - } - for (i = 0; i < 8; ++i) { - lt->symbols[24 + 144 + i] = 280 + i; - } - for (i = 0; i < 112; ++i) { - lt->symbols[24 + 144 + 8 + i] = 144 + i; - } - - lt->max_sym = 285; - - /* Build fixed distance tree */ - for (i = 0; i < 16; ++i) { - dt->counts[i] = 0; - } - - dt->counts[5] = 32; - - for (i = 0; i < 32; ++i) { - dt->symbols[i] = i; - } - - dt->max_sym = 29; -} - -/* Given an array of code lengths, build a tree */ -static int tinf_build_tree(struct tinf_tree *t, const unsigned char *lengths, - unsigned int num) -{ - unsigned short offs[16]; - unsigned int i, num_codes, available; - - assert(num <= 288); - - for (i = 0; i < 16; ++i) { - t->counts[i] = 0; - } - - t->max_sym = -1; - - /* Count number of codes for each non-zero length */ - for (i = 0; i < num; ++i) { - assert(lengths[i] <= 15); - - if (lengths[i]) { - t->max_sym = i; - t->counts[lengths[i]]++; - } - } - - /* Compute offset table for distribution sort */ - for (available = 1, num_codes = 0, i = 0; i < 16; ++i) { - unsigned int used = t->counts[i]; - - /* Check length contains no more codes than available */ - if (used > available) { - return TINF_DATA_ERROR; - } - available = 2 * (available - used); - - offs[i] = num_codes; - num_codes += used; - } - - /* - * Check all codes were used, or for the special case of only one - * code that it has length 1 - */ - if ((num_codes > 1 && available > 0) - || (num_codes == 1 && t->counts[1] != 1)) { - return TINF_DATA_ERROR; - } - - /* Fill in symbols sorted by code */ - for (i = 0; i < num; ++i) { - if (lengths[i]) { - t->symbols[offs[lengths[i]]++] = i; - } - } - - /* - * For the special case of only one code (which will be 0) add a - * code 1 which results in a symbol that is too large - */ - if (num_codes == 1) { - t->counts[1] = 2; - t->symbols[1] = t->max_sym + 1; - } - - return TINF_OK; -} - -/* -- Decode functions -- */ - -static void tinf_refill(struct tinf_data *d, int num) -{ - assert(num >= 0 && num <= 32); - - /* Read bytes until at least num bits available */ - while (d->bitcount < num) { - if (d->source != d->source_end) { - d->tag |= (unsigned int) *d->source++ << d->bitcount; - } - else { - d->overflow = 1; - } - d->bitcount += 8; - } - - assert(d->bitcount <= 32); -} - -static unsigned int tinf_getbits_no_refill(struct tinf_data *d, int num) -{ - unsigned int bits; - - assert(num >= 0 && num <= d->bitcount); - - /* Get bits from tag */ - bits = d->tag & ((1UL << num) - 1); - - /* Remove bits from tag */ - d->tag >>= num; - d->bitcount -= num; - - return bits; -} - -/* Get num bits from source stream */ -static unsigned int tinf_getbits(struct tinf_data *d, int num) -{ - tinf_refill(d, num); - return tinf_getbits_no_refill(d, num); -} - -/* Read a num bit value from stream and add base */ -static unsigned int tinf_getbits_base(struct tinf_data *d, int num, int base) -{ - return base + (num ? tinf_getbits(d, num) : 0); -} - -/* Given a data stream and a tree, decode a symbol */ -static int tinf_decode_symbol(struct tinf_data *d, const struct tinf_tree *t) -{ - int base = 0, offs = 0; - int len; - - /* - * Get more bits while code index is above number of codes - * - * Rather than the actual code, we are computing the position of the - * code in the sorted order of codes, which is the index of the - * corresponding symbol. - * - * Conceptually, for each code length (level in the tree), there are - * counts[len] leaves on the left and internal nodes on the right. - * The index we have decoded so far is base + offs, and if that - * falls within the leaves we are done. Otherwise we adjust the range - * of offs and add one more bit to it. - */ - for (len = 1; ; ++len) { - offs = 2 * offs + tinf_getbits(d, 1); - - assert(len <= 15); - - if (offs < t->counts[len]) { - break; - } - - base += t->counts[len]; - offs -= t->counts[len]; - } - - assert(base + offs >= 0 && base + offs < 288); - - return t->symbols[base + offs]; +#if CTX_CAIRO + else if (backend->destroy == (void*) ctx_cairo_destroy) return CTX_BACKEND_CAIRO; +#endif +#if CTX_TERMIMG + else if (backend->destroy == (void*) ctx_termimg_destroy) return CTX_BACKEND_TERMIMG; +#endif + return CTX_BACKEND_NONE; } -/* Given a data stream, decode dynamic trees from it */ -static int tinf_decode_trees(struct tinf_data *d, struct tinf_tree *lt, - struct tinf_tree *dt) +CtxBackendType ctx_backend_type (Ctx *ctx) { - unsigned char lengths[288 + 32]; - - /* Special ordering of code length codes */ - static const unsigned char clcidx[19] = { - 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, - 11, 4, 12, 3, 13, 2, 14, 1, 15 - }; - - unsigned int hlit, hdist, hclen; - unsigned int i, num, length; - int res; - - /* Get 5 bits HLIT (257-286) */ - hlit = tinf_getbits_base(d, 5, 257); - - /* Get 5 bits HDIST (1-32) */ - hdist = tinf_getbits_base(d, 5, 1); - - /* Get 4 bits HCLEN (4-19) */ - hclen = tinf_getbits_base(d, 4, 4); - - /* - * The RFC limits the range of HLIT to 286, but lists HDIST as range - * 1-32, even though distance codes 30 and 31 have no meaning. While - * we could allow the full range of HLIT and HDIST to make it possible - * to decode the fixed trees with this function, we consider it an - * error here. - * - * See also: https://github.com/madler/zlib/issues/82 - */ - if (hlit > 286 || hdist > 30) { - return TINF_DATA_ERROR; - } - - for (i = 0; i < 19; ++i) { - lengths[i] = 0; - } - - /* Read code lengths for code length alphabet */ - for (i = 0; i < hclen; ++i) { - /* Get 3 bits code length (0-7) */ - unsigned int clen = tinf_getbits(d, 3); - - lengths[clcidx[i]] = clen; - } - - /* Build code length tree (in literal/length tree to save space) */ - res = tinf_build_tree(lt, lengths, 19); - - if (res != TINF_OK) { - return res; - } - - /* Check code length tree is not empty */ - if (lt->max_sym == -1) { - return TINF_DATA_ERROR; - } - - /* Decode code lengths for the dynamic trees */ - for (num = 0; num < hlit + hdist; ) { - int sym = tinf_decode_symbol(d, lt); - - if (sym > lt->max_sym) { - return TINF_DATA_ERROR; - } - - switch (sym) { - case 16: - /* Copy previous code length 3-6 times (read 2 bits) */ - if (num == 0) { - return TINF_DATA_ERROR; - } - sym = lengths[num - 1]; - length = tinf_getbits_base(d, 2, 3); - break; - case 17: - /* Repeat code length 0 for 3-10 times (read 3 bits) */ - sym = 0; - length = tinf_getbits_base(d, 3, 3); - break; - case 18: - /* Repeat code length 0 for 11-138 times (read 7 bits) */ - sym = 0; - length = tinf_getbits_base(d, 7, 11); - break; - default: - /* Values 0-15 represent the actual code lengths */ - length = 1; - break; - } - - if (length > hlit + hdist - num) { - return TINF_DATA_ERROR; - } - - while (length--) { - lengths[num++] = sym; - } - } - - /* Check EOB symbol is present */ - if (lengths[256] == 0) { - return TINF_DATA_ERROR; - } - - /* Build dynamic trees */ - res = tinf_build_tree(lt, lengths, hlit); - - if (res != TINF_OK) { - return res; - } - - res = tinf_build_tree(dt, lengths + hlit, hdist); + CtxBackend *backend = ctx->backend; + CtxBackendType internal = backend->type; - if (res != TINF_OK) { - return res; - } + if (!internal) + { + CtxBackendType computed = __ctx_backend_type (ctx); + backend->type = computed; + //fprintf (stderr, "did a caching set of %i\n", computed); + return computed; + } - return TINF_OK; + return internal; } -/* -- Block inflate functions -- */ -/* Given a stream and two trees, inflate a block of data */ -static int tinf_inflate_block_data(struct tinf_data *d, struct tinf_tree *lt, - struct tinf_tree *dt) +void ctx_set_fullscreen (Ctx *ctx, int val) { - /* Extra bits and base tables for length codes */ - static const unsigned char length_bits[30] = { - 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, - 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, - 4, 4, 4, 4, 5, 5, 5, 5, 0, 127 - }; +#if CTX_SDL + if (ctx_backend_type (ctx) == CTX_BACKEND_SDL) + ctx_sdl_set_fullscreen (ctx, val); +#endif +} - static const unsigned short length_base[30] = { - 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, - 15, 17, 19, 23, 27, 31, 35, 43, 51, 59, - 67, 83, 99, 115, 131, 163, 195, 227, 258, 0 - }; +int ctx_get_fullscreen (Ctx *ctx) +{ +#if CTX_SDL + if (ctx_backend_type (ctx) == CTX_BACKEND_SDL) + return ctx_sdl_get_fullscreen (ctx); +#endif + return 0; +} - /* Extra bits and base tables for distance codes */ - static const unsigned char dist_bits[30] = { - 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, - 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, - 9, 9, 10, 10, 11, 11, 12, 12, 13, 13 - }; +CtxPixelFormatInfo *ctx_pixel_formats = +#if CTX_COMPOSITE +ctx_pixel_formats_generic; +#else +NULL; +#endif - static const unsigned short dist_base[30] = { - 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, - 33, 49, 65, 97, 129, 193, 257, 385, 513, 769, - 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577 - }; +CtxPixelFormatInfo * +ctx_pixel_format_info (CtxPixelFormat format) +{ + if (!ctx_pixel_formats) + { + assert (0); + return NULL; + } + for (unsigned int i = 0; ctx_pixel_formats[i].pixel_format; i++) + { + if (ctx_pixel_formats[i].pixel_format == format) + { + return &ctx_pixel_formats[i]; + } + } + assert (0); + return NULL; +} - for (;;) { - int sym = tinf_decode_symbol(d, lt); - /* Check for overflow in bit reader */ - if (d->overflow) { - return TINF_DATA_ERROR; - } +#if CTX_RASTERIZER - if (sym < 256) { - if (d->dest == d->dest_end) { - return TINF_BUF_ERROR; - } - *d->dest++ = sym; - } - else { - int length, dist, offs; - int i; - /* Check for end of block */ - if (sym == 256) { - return TINF_OK; - } +void (*ctx_rasterizer_rasterize_edges) (CtxRasterizer *rasterizer, const int fill_rule +#if CTX_SHAPE_CACHE + ,CtxShapeEntry *shape +#endif + ) = + ctx_rasterizer_rasterize_edges_generic; - /* Check sym is within range and distance tree is not empty */ - if (sym > lt->max_sym || sym - 257 > 28 || dt->max_sym == -1) { - return TINF_DATA_ERROR; - } +void (*ctx_composite_setup) (CtxRasterizer *rasterizer) = + ctx_composite_setup_generic; +#if CTX_FAST_FILL_RECT +void (*ctx_composite_stroke_rect) (CtxRasterizer *rasterizer, + float x0, + float y0, + float x1, + float y1, + float line_width) = + ctx_composite_stroke_rect_generic; - sym -= 257; +void (*ctx_composite_fill_rect) (CtxRasterizer *rasterizer, + float x0, + float y0, + float x1, + float y1, + uint8_t cov) = + ctx_composite_fill_rect_generic; +#endif - /* Possibly get more bits from length code */ - length = tinf_getbits_base(d, length_bits[sym], - length_base[sym]); +#endif - dist = tinf_decode_symbol(d, dt); - /* Check dist is within range */ - if (dist > dt->max_sym || dist > 29) { - return TINF_DATA_ERROR; - } +CTX_EXPORT void +ctx_logo (Ctx *ctx, float x, float y, float dim) +{ + //float width = ctx_width (ctx); + //float height = ctx_height (ctx); + ctx_save (ctx); + ctx_translate (ctx, x, y);// + //width/2, height/2); - /* Possibly get more bits from distance code */ - offs = tinf_getbits_base(d, dist_bits[dist], - dist_base[dist]); + //if (width < height) height = width; + + ctx_scale (ctx, dim, dim); + ctx_translate (ctx, -0.5f, -0.5f); + ctx_begin_path (ctx); + ctx_rgba(ctx,1,1,1,0.4f); + ctx_move_to(ctx,0.43956786f,0.90788066f); + ctx_rel_curve_to(ctx,0.0195929f,0.0102943f,0.0716181f,0.0218038f,0.10361884f,-0.0167646f); + ctx_line_to (ctx,0.93768705f,0.37887837f); + ctx_rel_curve_to (ctx, 0.019925f,-0.0342044f,-0.00963f,-0.0544608f,-0.0308834f,-0.0508084f); + ctx_rel_curve_to (ctx,-0.17965502f,0.0285588f,-0.35466092f,-0.055125f,-0.45096394f,-0.21253089f); + ctx_rel_curve_to (ctx, -0.0176003f,-0.02988716f, -0.0594422f,-0.01560777f,-0.0594422f,0.0139473f); + ctx_rel_curve_to (ctx, 0, 0.0591101f,0.003321f,0.49845135f,0.001991f, 0.70699722f); + ctx_rel_curve_to (ctx, 0.00039042f, 0.0283487f,0.0157362f,0.0529866f,0.0408456f,0.070733f); + ctx_fill (ctx); - if (offs > d->dest - d->dest_start) { - return TINF_DATA_ERROR; - } + ctx_move_to (ctx, 0.39772584f,0.91850721f); + ctx_rel_line_to (ctx, -0.0664159f, 0); + ctx_rel_curve_to (ctx, -0.15408489f,0, -0.27894675f,-0.12486192f, -0.27894675f,-0.2789468f); + ctx_rel_curve_to (ctx, 0,-0.15408489f, 0.12486186f,-0.27861466f, 0.27894675f,-0.27894675f); + ctx_rel_line_to (ctx, 0.18585599f,0.0000662f); + ctx_rel_curve_to (ctx, 0.0111839f,0.00017138f, 0.0158287f,0.001542f, 0.0263337f,0.0134822f); + ctx_rel_curve_to (ctx, 0.11733258f,0.14373102f, 0.3018009f,0.36870115f, 0.3942639f,0.49195316f); + ctx_rel_curve_to (ctx, 0.0185394f,0.0332794f, -0.0106225f,0.0505515f, -0.0228143f,0.0505207f); - if (d->dest_end - d->dest < length) { - return TINF_BUF_ERROR; - } + ctx_linear_gradient (ctx, 0.0525f, 0, 0.9905f, 0); + ctx_gradient_add_stop (ctx, 0.0f, 1.0f, 1.0f, 0.66f, 1.0f); + ctx_gradient_add_stop (ctx, 0.2f, 1.0f, 0.66f, 0.0f, 1.0f); + ctx_gradient_add_stop (ctx, 0.5f, 1.0f, 0.0f, 0.0f, 1.0f); + ctx_gradient_add_stop (ctx, 1.0f, 0.4f, 0.0f, 0.53f, 1.0f); + ctx_fill (ctx); + - /* Copy match */ - for (i = 0; i < length; ++i) { - d->dest[i] = d->dest[i - offs]; - } - d->dest += length; - } - } + ctx_linear_gradient(ctx, 0.697f, 0.17f, 0.4318f, 0.884f); + ctx_gradient_add_stop (ctx, 0, 0.26f, 0.26f, 1, 1.0f); + ctx_gradient_add_stop (ctx, 0.3f, 0, 1, 1, 0.4f); + ctx_gradient_add_stop (ctx, 1.0f, 0, 1, 0.26f,1.0f); + + ctx_move_to(ctx,0.43956786f,0.90788066f); + ctx_rel_curve_to(ctx,0.0195929f,0.0102943f,0.0716181f,0.0218038f,0.10361884f,-0.0167646f); + ctx_line_to (ctx,0.93768705f,0.37887837f); + ctx_rel_curve_to (ctx, 0.019925f,-0.0342044f,-0.00963f,-0.0544608f,-0.0308834f,-0.0508084f); + ctx_rel_curve_to (ctx,-0.17965502f,0.0285588f,-0.35466092f,-0.055125f,-0.45096394f,-0.21253089f); + ctx_rel_curve_to (ctx, -0.0176003f,-0.02988716f, -0.0594422f,-0.01560777f,-0.0594422f,0.0139473f); + ctx_rel_curve_to (ctx, 0, 0.0591101f,0.003321f,0.49845135f,0.001991f, 0.70699722f); + ctx_rel_curve_to (ctx, 0.00039042f, 0.0283487f,0.0157362f,0.0529866f,0.0408456f,0.070733f); + ctx_fill (ctx); + + ctx_restore (ctx); } -/* Inflate an uncompressed block of data */ -static int tinf_inflate_uncompressed_block(struct tinf_data *d) +void +ctx_clip_extents (Ctx *ctx, float *x0, float *y0, + float *x1, float *y1) { - unsigned int length, invlength; - - if (d->source_end - d->source < 4) { - return TINF_DATA_ERROR; - } - - /* Get length */ - length = read_le16(d->source); - - /* Get one's complement of length */ - invlength = read_le16(d->source + 2); - - /* Check length */ - if (length != (~invlength & 0x0000FFFF)) { - return TINF_DATA_ERROR; - } - - d->source += 4; - - if (d->source_end - d->source < length) { - return TINF_DATA_ERROR; - } - - if (d->dest_end - d->dest < length) { - return TINF_BUF_ERROR; - } - - /* Copy block */ - while (length--) { - *d->dest++ = *d->source++; - } + CtxGState *gstate = &ctx->state.gstate; + if(x0)*x0 = gstate->clip_min_x; + if(y0)*y0 = gstate->clip_min_y; + if(x1)*x1 = gstate->clip_max_x; + if(y1)*y1 = gstate->clip_max_y; +} - /* Make sure we start next block on a byte boundary */ - d->tag = 0; - d->bitcount = 0; +typedef struct CtxDeferredCommand { + uint32_t name; + int offset; + int is_rect; +} CtxDeferredCommand; - return TINF_OK; +static CtxDeferredCommand *deferred_new (Ctx *ctx, const char *name) +{ + CtxDeferredCommand *deferred = calloc (sizeof (CtxDeferredCommand), 1); + if (name) + deferred->name = ctx_strhash (name); + deferred->offset = ctx->drawlist.count; + ctx_list_prepend (&ctx->deferred, deferred); + return deferred; } -/* Inflate a block of data compressed with fixed Huffman trees */ -static int tinf_inflate_fixed_block(struct tinf_data *d) +void ctx_deferred_move_to (Ctx *ctx, const char *name, float x, float y) { - /* Build fixed Huffman trees */ - tinf_build_fixed_trees(&d->ltree, &d->dtree); - - /* Decode block using fixed trees */ - return tinf_inflate_block_data(d, &d->ltree, &d->dtree); + deferred_new (ctx, name); + ctx_move_to (ctx, x, y); } -/* Inflate a block of data compressed with dynamic Huffman trees */ -static int tinf_inflate_dynamic_block(struct tinf_data *d) +void ctx_deferred_rel_move_to (Ctx *ctx, const char *name, float x, float y) { - /* Decode trees from stream */ - int res = tinf_decode_trees(d, &d->ltree, &d->dtree); - - if (res != TINF_OK) { - return res; - } - - /* Decode block using decoded trees */ - return tinf_inflate_block_data(d, &d->ltree, &d->dtree); + deferred_new (ctx, name); + ctx_rel_move_to (ctx, x, y); } -/* -- Public functions -- */ - -/* Initialize global (static) data */ -void tinf_init(void) +void ctx_deferred_rel_line_to (Ctx *ctx, const char *name, float x, float y) { - return; + deferred_new (ctx, name); + ctx_rel_line_to (ctx, x, y); } -/* Inflate stream from source to dest */ -int tinf_uncompress(void *dest, unsigned int *destLen, - const void *source, unsigned int sourceLen) +void ctx_deferred_scale (Ctx *ctx, const char *name, float x, float y) { - struct tinf_data d; - int bfinal; - - /* Initialise data */ - d.source = (const unsigned char *) source; - d.source_end = d.source + sourceLen; - d.tag = 0; - d.bitcount = 0; - d.overflow = 0; - - d.dest = (unsigned char *) dest; - d.dest_start = d.dest; - d.dest_end = d.dest + *destLen; - - do { - unsigned int btype; - int res; - - /* Read final block flag */ - bfinal = tinf_getbits(&d, 1); - - /* Read block type (2 bits) */ - btype = tinf_getbits(&d, 2); - - /* Decompress block */ - switch (btype) { - case 0: - /* Decompress uncompressed block */ - res = tinf_inflate_uncompressed_block(&d); - break; - case 1: - /* Decompress block with fixed Huffman trees */ - res = tinf_inflate_fixed_block(&d); - break; - case 2: - /* Decompress block with dynamic Huffman trees */ - res = tinf_inflate_dynamic_block(&d); - break; - default: - res = TINF_DATA_ERROR; - break; - } - - if (res != TINF_OK) { - return res; - } - } while (!bfinal); - - /* Check for overflow in bit reader */ - if (d.overflow) { - return TINF_DATA_ERROR; - } - - *destLen = d.dest - d.dest_start; - - return TINF_OK; + deferred_new (ctx, name); + ctx_scale (ctx, x, y); } -/* clang -g -O1 -fsanitize=fuzzer,address -DTINF_FUZZING tinflate.c */ -#if defined(TINF_FUZZING) -#include <limits.h> -#include <stddef.h> -#include <stdint.h> -#include <stdlib.h> -#include <string.h> - -unsigned char depacked[64 * 1024]; - -extern int -LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) +void ctx_deferred_translate (Ctx *ctx, const char *name, float x, float y) { - if (size > UINT_MAX / 2) { return 0; } - unsigned int destLen = sizeof(depacked); - tinf_uncompress(depacked, &destLen, data, size); - return 0; + deferred_new (ctx, name); + ctx_translate (ctx, x, y); } -#endif -/* - * tinfgzip - tiny gzip decompressor - * - * Copyright (c) 2003-2019 Joergen Ibsen - * - * This software is provided 'as-is', without any express or implied - * warranty. In no event will the authors be held liable for any damages - * arising from the use of this software. - * - * Permission is granted to anyone to use this software for any purpose, - * including commercial applications, and to alter it and redistribute it - * freely, subject to the following restrictions: - * - * 1. The origin of this software must not be misrepresented; you must - * not claim that you wrote the original software. If you use this - * software in a product, an acknowledgment in the product - * documentation would be appreciated but is not required. - * - * 2. Altered source versions must be plainly marked as such, and must - * not be misrepresented as being the original software. - * - * 3. This notice may not be removed or altered from any source - * distribution. - */ -//#include "tinf.h" - -typedef enum { - FTEXT = 1, - FHCRC = 2, - FEXTRA = 4, - FNAME = 8, - FCOMMENT = 16 -} tinf_gzip_flag; - -#if 0 -static inline unsigned int read_le16(const unsigned char *p) +void ctx_deferred_rectangle (Ctx *ctx, const char *name, + float x, float y, + float width, float height) { - return ((unsigned int) p[0]) - | ((unsigned int) p[1] << 8); + CtxDeferredCommand *deferred = deferred_new (ctx, name); + deferred->is_rect = 1; + ctx_rectangle (ctx, x, y, width, height); } -#endif -static inline unsigned int read_le32(const unsigned char *p) +static CtxList *ctx_deferred_commands (Ctx *ctx, const char *name, int *ret_count) { - return ((unsigned int) p[0]) - | ((unsigned int) p[1] << 8) - | ((unsigned int) p[2] << 16) - | ((unsigned int) p[3] << 24); + CtxList *matching = NULL; + uint32_t name_id = ctx_strhash (name); + int count = 0; + for (CtxList *l = ctx->deferred; l; l = l->next) + { + CtxDeferredCommand *command = l->data; + if (name) + { + if (command->name == name_id) + { + ctx_list_prepend (&matching, command); + count ++; + } + } + else + { + if (command->name == 0) + { + ctx_list_prepend (&matching, command); + count ++; + } + } + } + if (ret_count) + *ret_count = count; + return matching; } -int tinf_gzip_uncompress(void *dest, unsigned int *destLen, - const void *source, unsigned int sourceLen) +#if 0 +void ctx_resolve_rel_line_to (Ctx *ctx, const char *name, + void (*set_dim) (void *userdata, + const char *name, + int count, + float *x, + float *y), + void *userdata) { - const unsigned char *src = (const unsigned char *) source; - unsigned char *dst = (unsigned char *) dest; - const unsigned char *start; - unsigned int dlen, crc32; - int res; - unsigned char flg; - - /* -- Check header -- */ - - /* Check room for at least 10 byte header and 8 byte trailer */ - if (sourceLen < 18) { - return TINF_DATA_ERROR; - } - - /* Check id bytes */ - if (src[0] != 0x1F || src[1] != 0x8B) { - return TINF_DATA_ERROR; - } - - /* Check method is deflate */ - if (src[2] != 8) { - return TINF_DATA_ERROR; - } - - /* Get flag byte */ - flg = src[3]; - - /* Check that reserved bits are zero */ - if (flg & 0xE0) { - return TINF_DATA_ERROR; - } - - /* -- Find start of compressed data -- */ - - /* Skip base header of 10 bytes */ - start = src + 10; - - /* Skip extra data if present */ - if (flg & FEXTRA) { - unsigned int xlen = read_le16(start); - - if (xlen > sourceLen - 12) { - return TINF_DATA_ERROR; - } - - start += xlen + 2; - } - - /* Skip file name if present */ - if (flg & FNAME) { - do { - if (start - src >= sourceLen) { - return TINF_DATA_ERROR; - } - } while (*start++); - } - - /* Skip file comment if present */ - if (flg & FCOMMENT) { - do { - if (start - src >= sourceLen) { - return TINF_DATA_ERROR; - } - } while (*start++); - } - - /* Check header crc if present */ - if (flg & FHCRC) { - unsigned int hcrc; - - if (start - src > sourceLen - 2) { - return TINF_DATA_ERROR; - } - - hcrc = read_le16(start); - - if (hcrc != (tinf_crc32(src, start - src) & 0x0000FFFF)) { - return TINF_DATA_ERROR; - } - - start += 2; - } - - /* -- Get decompressed length -- */ + int count = 0; + CtxList *matching = ctx_deferred_commands (ctx, name, &count); + while (matching) + { + CtxDeferredCommand *command = matching->data; - dlen = read_le32(&src[sourceLen - 4]); + float x = ((CtxCommand*)&ctx->drawlist.entries[command->offset])->rel_line_to.x; + float y = ((CtxCommand*)&ctx->drawlist.entries[command->offset])->rel_line_to.y; - if (dlen > *destLen) { - return TINF_BUF_ERROR; - } + set_dim (userdata, name, count, &x, &y); - /* -- Get CRC32 checksum of original data -- */ + ((CtxCommand*)&ctx->drawlist.entries[command->offset])->rel_line_to.x = x; + ((CtxCommand*)&ctx->drawlist.entries[command->offset])->rel_line_to.y = y; - crc32 = read_le32(&src[sourceLen - 8]); + ctx_list_remove (&ctx->deferred, command); + ctx_list_remove (&matching, command); + free (command); + } +} - /* -- Decompress data -- */ +void ctx_resolve_rectangle (Ctx *ctx, const char *name, + void (*set_dim) (void *userdata, + const char *name, + int count, + float *x, + float *y, + float *width, + float *height), + void *userdata) +{ + int count = 0; + CtxList *matching = ctx_deferred_commands (ctx, name, &count); + while (matching) + { + CtxDeferredCommand *command = matching->data; - if ((src + sourceLen) - start < 8) { - return TINF_DATA_ERROR; - } + float x = ((CtxCommand*)&ctx->drawlist.entries[command->offset])->rectangle.x; + float y = ((CtxCommand*)&ctx->drawlist.entries[command->offset])->rectangle.y; + float w = ((CtxCommand*)&ctx->drawlist.entries[command->offset])->rectangle.width; + float h = ((CtxCommand*)&ctx->drawlist.entries[command->offset])->rectangle.height; - res = tinf_uncompress(dst, destLen, start, - (src + sourceLen) - start - 8); + set_dim (userdata, name, count, &x, &y, &w, &h); - if (res != TINF_OK) { - return TINF_DATA_ERROR; - } + ((CtxCommand*)&ctx->drawlist.entries[command->offset])->rectangle.x = x; + ((CtxCommand*)&ctx->drawlist.entries[command->offset])->rectangle.y = y; + ((CtxCommand*)&ctx->drawlist.entries[command->offset])->rectangle.width = w; + ((CtxCommand*)&ctx->drawlist.entries[command->offset])->rectangle.height = h; + ctx_list_remove (&ctx->deferred, command); + ctx_list_remove (&matching, command); + free (command); + } +} +#endif - if (*destLen != dlen) { - return TINF_DATA_ERROR; - } +void ctx_resolve (Ctx *ctx, const char *name, + void (*resolve) (Ctx *ctx, + void *userdata, + const char *name, + int count, + float *x, + float *y, + float *width, // ignored + float *height),// for non-rect + void *userdata) +{ + int count = 0; + CtxList *matching = ctx_deferred_commands (ctx, name, &count); + while (matching) + { + CtxDeferredCommand *command = matching->data; - /* -- Check CRC32 checksum -- */ + float x, y, w = 0, h = 0; + if (command->is_rect) + { + x = ((CtxCommand*)&ctx->drawlist.entries[command->offset])->rectangle.x; + y = ((CtxCommand*)&ctx->drawlist.entries[command->offset])->rectangle.y; + w = ((CtxCommand*)&ctx->drawlist.entries[command->offset])->rectangle.width; + h = ((CtxCommand*)&ctx->drawlist.entries[command->offset])->rectangle.height; + } + else + { + x = ((CtxCommand*)&ctx->drawlist.entries[command->offset])->rel_line_to.x; + y = ((CtxCommand*)&ctx->drawlist.entries[command->offset])->rel_line_to.y; + } - if (crc32 != tinf_crc32(dst, dlen)) { - return TINF_DATA_ERROR; - } + resolve (ctx, userdata, name, count, &x, &y, &w, &h); - return TINF_OK; + if (command->is_rect) + { + ((CtxCommand*)&ctx->drawlist.entries[command->offset])->rectangle.x = x; + ((CtxCommand*)&ctx->drawlist.entries[command->offset])->rectangle.y = y; + ((CtxCommand*)&ctx->drawlist.entries[command->offset])->rectangle.width = w; + ((CtxCommand*)&ctx->drawlist.entries[command->offset])->rectangle.height = h; + } + else + { + ((CtxCommand*)&ctx->drawlist.entries[command->offset])->rel_line_to.x = x; + ((CtxCommand*)&ctx->drawlist.entries[command->offset])->rel_line_to.y = y; + } + ctx_list_remove (&ctx->deferred, command); + ctx_list_remove (&matching, command); + free (command); + } } -/* - * tinfzlib - tiny zlib decompressor - * - * Copyright (c) 2003-2019 Joergen Ibsen - * - * This software is provided 'as-is', without any express or implied - * warranty. In no event will the authors be held liable for any damages - * arising from the use of this software. - * - * Permission is granted to anyone to use this software for any purpose, - * including commercial applications, and to alter it and redistribute it - * freely, subject to the following restrictions: - * - * 1. The origin of this software must not be misrepresented; you must - * not claim that you wrote the original software. If you use this - * software in a product, an acknowledgment in the product - * documentation would be appreciated but is not required. - * - * 2. Altered source versions must be plainly marked as such, and must - * not be misrepresented as being the original software. - * - * 3. This notice may not be removed or altered from any source - * distribution. - */ - -//#include "tinf.h" -static unsigned int read_be32(const unsigned char *p) +void _ctx_write_png (const char *dst_path, int w, int h, int num_chans, void *data) { - return ((unsigned int) p[0] << 24) - | ((unsigned int) p[1] << 16) - | ((unsigned int) p[2] << 8) - | ((unsigned int) p[3]); +#if CTX_IMAGE_WRITE + size_t len = 0; + char *buf = tdefl_write_image_to_png_file_in_memory (data, w, h, num_chans, &len); + if (buf) + { + FILE *f = fopen (dst_path, "w"); + fwrite (buf, len, 1, f); + fclose (f); + mz_free (buf); + } +#endif } -int tinf_zlib_uncompress(void *dest, unsigned int *destLen, - const void *source, unsigned int sourceLen) +const char * +ctx_str_decode (uint32_t number) { - const unsigned char *src = (const unsigned char *) source; - unsigned char *dst = (unsigned char *) dest; - unsigned int a32; - int res; - unsigned char cmf, flg; - - /* -- Check header -- */ - - /* Check room for at least 2 byte header and 4 byte trailer */ - if (sourceLen < 6) { - return TINF_DATA_ERROR; - } - - /* Get header bytes */ - cmf = src[0]; - flg = src[1]; - - /* Check checksum */ - if ((256 * cmf + flg) % 31) { - return TINF_DATA_ERROR; - } - - /* Check method is deflate */ - if ((cmf & 0x0F) != 8) { - return TINF_DATA_ERROR; - } - - /* Check window size is valid */ - if ((cmf >> 4) > 7) { - return TINF_DATA_ERROR; - } - - /* Check there is no preset dictionary */ - if (flg & 0x20) { - return TINF_DATA_ERROR; - } - - /* -- Get Adler-32 checksum of original data -- */ - - a32 = read_be32(&src[sourceLen - 4]); - - /* -- Decompress data -- */ - - res = tinf_uncompress(dst, destLen, src + 2, sourceLen - 6); - - if (res != TINF_OK) { - return TINF_DATA_ERROR; - } + static char temp[16]; + return squoze32_utf8_decode (number, temp); +} - /* -- Check Adler-32 checksum -- */ +uint32_t ctx_strhash(const char *str) +{ + return squoze32_utf8 (str, strlen (str)); +} - if (a32 != tinf_adler32(dst, *destLen)) { - return TINF_DATA_ERROR; - } - return TINF_OK; -} #ifndef MRG_UTF8_H #define MRG_UTF8_H @@ -47471,7 +56287,7 @@ utf8_decode(uint32_t* state, uint32_t* codep, uint32_t byte) { } #endif -#if CTX_CLIENTS +#if CTX_VT /* mrg - MicroRaptor Gui * Copyright (c) 2014 Øyvind Kolås <pippin@hodefoting.com> @@ -47854,6 +56670,7 @@ typedef struct GfxState struct _VT { VtPty vtpty; + int empty_count; int id; unsigned char buf[BUFSIZ]; // need one per vt int keyrepeat; @@ -48071,7 +56888,12 @@ void vt_paste (VT *vt, const char *str); //void vt_feed_byte (VT *vt, int byte); //)#define DEFAULT_SCROLLBACK (1<<16) + +#if CTX_PTY #define DEFAULT_SCROLLBACK (1<<13) +#else +#define DEFAULT_SCROLLBACK (2) +#endif #define DEFAULT_ROWS 24 #define DEFAULT_COLS 80 @@ -48158,11 +56980,12 @@ static void vt_resize (VT *vt, int cols, int rows, int px_width, int px_height) */ -#ifndef EMSCRIPTEN -#undef uncompress -#include <zlib.h> -#endif +//#ifndef EMSCRIPTEN +//#undef uncompress +//#include <zlib.h> +//#endif +#if CTX_AUDIO #if CTX_SDL #include <SDL.h> @@ -49534,11 +58357,7 @@ void vt_audio (VT *vt, const char *command) { case 'z': { -#ifndef EMSCRIPTEN - unsigned long -#else - unsigned int -#endif + unsigned long int actual_uncompressed_size = audio->frames * audio->bits/8 * audio->channels + 512; unsigned char *data2 = ctx_malloc (actual_uncompressed_size); /* if a buf size is set (rather compression, but @@ -49697,6 +58516,9 @@ void vt_audio (VT *vt, const char *command) audio->data_size=0; } #endif +#endif +#if CTX_VT + /* DEC terminals/xterm family terminal with ANSI, utf8, vector graphics and * audio. * @@ -49728,6 +58550,7 @@ void vt_audio (VT *vt, const char *command) * */ +int ctx_dummy_in_len = 0; #if CTX_TERMINAL_EVENTS #include <sys/stat.h> @@ -49745,8 +58568,10 @@ void vt_audio (VT *vt, const char *command) #include <fcntl.h> #include <sys/stat.h> #include <sys/time.h> +#if CTX_PTY #include <sys/ioctl.h> #include <termios.h> +#endif #include "ctx.h" @@ -49810,7 +58635,9 @@ static void vt_state_sixel (VT *vt, int byte); static void vt_state_esc_sequence (VT *vt, int byte); static void vt_state_esc_foo (VT *vt, int byte); static void vt_state_swallow (VT *vt, int byte); +#if CTX_PARSER static void vt_state_ctx (VT *vt, int byte); +#endif static void vt_state_vt52 (VT *vt, int byte); #if 0 @@ -49998,6 +58825,7 @@ static Image *image_add (int width, void vtpty_resize (void *data, int cols, int rows, int px_width, int px_height) { +#if CTX_PTY VtPty *vtpty = data; struct winsize ws; ws.ws_row = rows; @@ -50005,6 +58833,7 @@ void vtpty_resize (void *data, int cols, int rows, int px_width, int px_height) ws.ws_xpixel = px_width; ws.ws_ypixel = px_height; ioctl (vtpty->pty, TIOCSWINSZ, &ws); +#endif } ssize_t vtpty_write (void *data, const void *buf, size_t count) @@ -50103,7 +58932,6 @@ static int vt_margin_right (VT *vt) static void vtcmd_reset_to_initial_state (VT *vt, const char *sequence); int vt_set_prop (VT *vt, uint32_t key_hash, const char *val); -uint32_t ctx_strhash (const char *utf8); static void vt_set_title (VT *vt, const char *new_title) { @@ -50299,6 +59127,7 @@ void vt_set_line_spacing (VT *vt, float line_spacing) static void ctx_clients_signal_child (int signum) { +#if CTX_PTY pid_t pid; int status; if ( (pid = waitpid (-1, &status, WNOHANG) ) != -1) @@ -50316,6 +59145,7 @@ static void ctx_clients_signal_child (int signum) } } } +#endif } static void vt_init (VT *vt, int width, int height, float font_size, float line_spacing, int id, int can_launch) @@ -50363,6 +59193,7 @@ static void vt_init (VT *vt, int width, int height, float font_size, float line_ vt->bg_color[2] = 0; } +#if CTX_PTY static pid_t vt_forkpty (int *amaster, char *aname, @@ -50414,6 +59245,7 @@ vt_forkpty (int *amaster, *amaster = master; return pid; } +#endif static void ctx_child_prepare_env (int was_pidone, const char *term) @@ -50515,18 +59347,94 @@ ssize_t em_read (void *serial_obj, void *buf, size_t count) } return 0; } - int em_waitdata (void *serial_obj, int timeout) + +int em_waitdata (void *serial_obj, int timeout) { return em_in_len; } - void em_resize (void *serial_obj, int cols, int rows, int px_width, int px_height) +#endif + +#define CTX_VT_INBUFSIZE 128 +#define CTX_VT_OUTBUFSIZE 128 + +static char ctx_dummy_inbuf[CTX_VT_INBUFSIZE]=""; +static char ctx_dummy_outbuf[CTX_VT_OUTBUFSIZE]=""; +static int ctx_dummy_in_pos = 0; +static int ctx_dummy_in_read_pos = 0; +static int ctx_dummy_out_len = 0; +static int ctx_dummy_out_pos = 0; +static int ctx_dummy_out_read_pos = 0; + +void ctx_vt_write (Ctx *ctx, uint8_t byte) +{ + if (ctx_dummy_in_len < CTX_VT_INBUFSIZE) + { + ctx_dummy_inbuf[ctx_dummy_in_pos++] = byte; + ctx_dummy_in_len++; + if (ctx_dummy_in_pos >= CTX_VT_INBUFSIZE)ctx_dummy_in_pos = 0; + } + else + { + fprintf (stderr, "ctx uart overflow\n"); + } +} + +int ctx_vt_has_data (Ctx *ctx) +{ + return ctx_dummy_out_len; +} + +int ctx_vt_read (Ctx *ctx) { + int ret = -1; + if (ctx_dummy_out_len) + { + ret = ctx_dummy_outbuf[ctx_dummy_out_read_pos++]; + --ctx_dummy_out_len; + if (ctx_dummy_out_read_pos>=CTX_VT_OUTBUFSIZE)ctx_dummy_out_read_pos = 0; + } + return ret; } -#endif +static ssize_t ctx_dummy_write (void *s, const void *buf, size_t count) +{ + const char *src = (const char*)buf; + unsigned int i; + for (i = 0; i < count && ctx_dummy_out_len < CTX_VT_OUTBUFSIZE; i ++) + { + ctx_dummy_outbuf[ctx_dummy_out_pos++] = src[i]; + ctx_dummy_out_len++; + if (ctx_dummy_out_pos >= CTX_VT_OUTBUFSIZE)ctx_dummy_out_pos = 0; + } + if (ctx_dummy_out_len >= CTX_VT_OUTBUFSIZE) + printf ("ctx_dummy_outbuf overflow\n"); + + return i; +} +static ssize_t ctx_dummy_read (void *serial_obj, void *buf, size_t count) +{ + char *dst = (char*)buf; + if (ctx_dummy_in_len) + { + *dst = ctx_dummy_inbuf[ctx_dummy_in_read_pos++]; + --ctx_dummy_in_len; + if (ctx_dummy_in_read_pos>=CTX_VT_INBUFSIZE)ctx_dummy_in_read_pos = 0; + return 1; + } + return 0; +} + +static int ctx_dummy_waitdata (void *serial_obj, int timeout) +{ + return ctx_dummy_in_len; +} + +void ctx_dummy_resize (void *serial_obj, int cols, int rows, int px_width, int px_height) +{ +} static void vt_run_argv (VT *vt, char **argv, const char *term) { @@ -50534,24 +59442,39 @@ static void vt_run_argv (VT *vt, char **argv, const char *term) vt->read = em_read; vt->write = em_write; vt->waitdata = em_waitdata; - vt->resize = em_resize; - - printf ("aaa?\n"); + vt->resize = dummy_resize; #else - struct winsize ws; - //signal (SIGCHLD,signal_child); + #if 0 int was_pidone = (getpid () == 1); #else int was_pidone = 0; // do no special treatment, all child processes belong // to root #endif + +#if CTX_PTY==1 + if (!argv) +#endif + { + vt->read = ctx_dummy_read; + vt->write = ctx_dummy_write; + vt->waitdata = ctx_dummy_waitdata; + vt->resize = ctx_dummy_resize; + return; + } + + +#if CTX_PTY + + struct winsize ws; + //signal (SIGCHLD,signal_child); signal (SIGINT,SIG_DFL); ws.ws_row = vt->rows; ws.ws_col = vt->cols; ws.ws_xpixel = ws.ws_col * vt->cw; ws.ws_ypixel = ws.ws_row * vt->ch; vt->vtpty.pid = vt_forkpty (&vt->vtpty.pty, NULL, NULL, &ws); +#endif if (vt->vtpty.pid == 0) { ctx_child_prepare_env (was_pidone, term); @@ -50576,7 +59499,7 @@ VT *vt_new_argv (char **argv, int width, int height, float font_size, float line vt_init (vt, width, height, font_size, line_spacing, id, can_launch); vt_set_font_size (vt, font_size); vt_set_line_spacing (vt, line_spacing); - if (argv) + //if (argv) { vt_run_argv (vt, argv, NULL); } @@ -50649,6 +59572,8 @@ static char *string_chop_head (char *orig) /* return pointer to reset after arg VT *vt_new (const char *command, int width, int height, float font_size, float line_spacing, int id, int can_launch) { + if (!command) + return vt_new_argv (NULL, width, height, font_size, line_spacing, id, can_launch); char *cargv[32]; int cargc; char *rest, *copy; @@ -50705,7 +59630,7 @@ 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 + 1024) + if (vt->scrollback_count > vt->scrollback_limit) { CtxList *l = vt->scrollback; int no = 0; @@ -50829,6 +59754,7 @@ void vt_set_term_size (VT *vt, int icols, int irows) if (vt->rows == irows && vt->cols == icols) return; +#if CTX_PARSER if (vt->state == vt_state_ctx) { // we should queue a pending resize instead, @@ -50836,6 +59762,7 @@ void vt_set_term_size (VT *vt, int icols, int irows) // rendered frame is discarded? return; } +#endif if(1)vt_rewrap (vt, icols); @@ -50871,8 +59798,10 @@ void vt_set_term_size (VT *vt, int icols, int irows) _vt_move_to (vt, vt->cursor_y, vt->cursor_x); ctx_client_rev_inc (vt->client); VT_info ("resize %i %i", irows, icols); +#if CTX_PARSER if (vt->ctxp) ctx_parser_destroy (vt->ctxp); +#endif vt->ctxp = NULL; } @@ -51079,7 +60008,7 @@ static void vtcmd_set_left_and_right_margins (VT *vt, const char *sequence) static inline int parse_int (const char *arg, int def_val) { - if (!isdigit (arg[1]) || strlen (arg) == 2) + if (!((arg[1]>='0' && arg[1]<='9')) || strlen (arg) == 2) { return def_val; } return atoi (arg+1); } @@ -52035,22 +60964,6 @@ static void vt_ctx_exit (void *data) //ctx_parser_destroy (vt->ctxp); //vt->ctxp = NULL; } -#if 0 -#define CTX_x CTX_STRH('x',0,0,0,0,0,0,0,0,0,0,0,0,0) -#define CTX_y CTX_STRH('y',0,0,0,0,0,0,0,0,0,0,0,0,0) -#define CTX_lower_bottom CTX_STRH('l','o','w','e','r','-','b','o','t','t','o','m',0,0) -#define CTX_lower CTX_STRH('l','o','w','e','r',0,0,0,0,0,0,0,0,0) -#define CTX_raise CTX_STRH('r','a','i','s','e',0,0,0,0,0,0,0,0,0) -#define CTX_raise_top CTX_STRH('r','a','i','s','e','-','t','o','p',0,0,0,0,0) -#define CTX_terminate CTX_STRH('t','e','r','m','i','n','a','t','e',0,0,0,0,0) -#define CTX_maximize CTX_STRH('m','a','x','i','m','i','z','e',0,0,0,0,0,0) -#define CTX_unmaximize CTX_STRH('u','n','m','a','x','i','m','i','z','e',0,0,0,0) -#define CTX_width CTX_STRH('w','i','d','t','h',0,0,0,0,0,0,0,0,0) -#define CTX_title CTX_STRH('t','i','t','l','e',0,0,0,0,0,0,0,0,0) -#define CTX_action CTX_STRH('a','c','t','i','o','n',0,0,0,0,0,0,0,0) -#define CTX_height CTX_STRH('h','e','i','g','h','t',0,0,0,0,0,0,0,0) -#endif - static int vt_get_prop (VT *vt, const char *key, const char **val, int *len) { @@ -52063,19 +60976,19 @@ static int vt_get_prop (VT *vt, const char *key, const char **val, int *len) return 0; switch (key_hash) { - case CTX_title: + case SQZ_title: sprintf (str, "setkey %s %s\n", key, client->title); break; - case CTX_x: + case SQZ_x: sprintf (str, "setkey %s %i\n", key, client->x); break; - case CTX_y: + case SQZ_y: sprintf (str, "setkey %s %i\n", key, client->y); break; - case CTX_width: + case SQZ_width: sprintf (str, "setkey %s %i\n", key, client->width); break; - case CTX_height: + case SQZ_height: sprintf (str, "setkey %s %i\n", key, client->width); break; default: @@ -52238,7 +61151,8 @@ qagain: case 201:/*MODE;;ctx-events;on;off;*/ vt->ctx_events = set; break; - + +#if CTX_PARSER case 200:/*MODE;;ctx vector graphics mode;on;;*/ if (set) { @@ -52256,6 +61170,10 @@ qagain: vt->current_line->frame = ctx_string_new (""); #endif } + + if (!vt->ctxp) + { + if (vt->ctxp) ctx_parser_destroy (vt->ctxp); @@ -52263,10 +61181,12 @@ qagain: vt->cols * vt->cw, vt->rows * vt->ch, vt->cw, vt->ch, vt->cursor_x, vt->cursor_y, (void*)vt_set_prop, (void*)vt_get_prop, vt, vt_ctx_exit, vt); + } vt->utf8_holding[vt->utf8_pos=0]=0; // XXX : needed? vt->state = vt_state_ctx; } break; +#endif default: VT_warning ("unhandled CSI ? %i%s", qval, set?"h":"l"); return; @@ -52415,9 +61335,11 @@ static void vtcmd_request_mode (VT *vt, const char *sequence) is_set = vt->in_alt_screen; break; break; +#if CTX_PARSER case 200:/*ctx protocol;On;;*/ is_set = (vt->state == vt_state_ctx); break; +#endif case 80:/* DECSDM Sixel scrolling */ case 30: // from rxvt - show/hide scrollbar case 34: // DECRLM - right to left mode @@ -52483,7 +61405,7 @@ static void vtcmd_request_mode (VT *vt, const char *sequence) static void vtcmd_set_t (VT *vt, const char *sequence) { - /* \e[21y is request title - allows inserting keychars */ + /* \033[21y is request title - allows inserting keychars */ if (!strcmp (sequence, "[1t")) { ctx_client_unshade (vt->root_ctx, vt->id); } else if (!strcmp (sequence, "[2t")) { ctx_client_shade (vt->root_ctx, vt->id); } else if (!strncmp (sequence, "[3;", 3)) { @@ -52768,7 +61690,7 @@ static void vtcmd_report (VT *vt, const char *sequence) sprintf (buf, "\033[?21n"); // locked } #if 0 - {"[6n", 0, }, /* id:DSR cursor position report, yields a reply <tt>\e[Pl;PcR</tt> */ + {"[6n", 0, }, /* id:DSR cursor position report, yields a reply <tt>\033[Pl;PcR</tt> */ #endif else if (!strcmp (sequence, "[6n") ) // DSR cursor position report { @@ -52777,7 +61699,7 @@ static void vtcmd_report (VT *vt, const char *sequence) else if (!strcmp (sequence, "[?6n") ) // DECXPR extended cursor position report { #if 0 - {"[?6n", 0, }, /* id:DEXCPR extended cursor position report, yields a reply <tt>\e[Pl;PcR</tt> */ + {"[?6n", 0, }, /* id:DEXCPR extended cursor position report, yields a reply <tt>\033[Pl;PcR</tt> */ #endif sprintf (buf, "\033[?%i;%i;1R", vt->cursor_y - (vt->origin? (vt->margin_top - 1) :0), (int) vt->cursor_x - (vt->origin? (VT_MARGIN_LEFT-1) :0) ); } @@ -53154,7 +62076,9 @@ static void vt_state_apc_audio (VT *vt, int byte) { if ( (byte < 32) && ( (byte < 8) || (byte > 13) ) ) { +#if CTX_AUDIO vt_audio (vt, vt->argument_buf); +#endif vt->state = ( (byte == 27) ? vt_state_swallow : vt_state_neutral); } else @@ -53252,7 +62176,9 @@ static int _vt_handle_control (VT *vt, int byte) } return 1; case '\a': /* BELl */ +#if CTX_AUDIO vt_bell (vt); +#endif return 1; case '\b': /* BS */ _vt_backspace (vt); @@ -53484,11 +62410,7 @@ void vt_gfx (VT *vt, const char *command) unsigned char *data2 = ctx_malloc (vt->gfx.buf_size + 1); /* if a buf size is set (rather compression, but * this works first..) then */ -#ifndef EMSCRIPTEN - unsigned long -#else - unsigned int -#endif + unsigned long int actual_uncompressed_size = vt->gfx.buf_size; int z_result = uncompress (data2, &actual_uncompressed_size, vt->gfx.data, @@ -53504,7 +62426,7 @@ void vt_gfx (VT *vt, const char *command) vt->gfx.data_size = actual_uncompressed_size; vt->gfx.compression = 0; } -#ifdef STBI_INCLUDE_STB_IMAGE_H +#if CTX_STB_IMAGE if (vt->gfx.format == 100) { int channels; @@ -53925,80 +62847,12 @@ static void vt_sixels (VT *vt, const char *sixels) ctx_client_rev_inc (vt->client); } -static inline void vt_ctx_unrled (VT *vt, char byte) +#if CTX_PARSER +static void vt_state_ctx (VT *vt, int byte) { -#if CTX_VT_USE_FRAMEDIFF - ctx_string_append_byte (vt->current_line->frame, byte); -#endif ctx_parser_feed_byte (vt->ctxp, byte); } - -static void vt_state_ctx (VT *vt, int byte) -{ -#if 0 - //fprintf (stderr, "%c", byte); - if (CTX_UNLIKELY(byte == CTX_CODEC_CHAR)) - { - if (CTX_UNLIKELY(vt->in_prev_match)) - { - char *prev = vt->current_line->prev; - int prev_length = vt->current_line->prev_length; - int start = atoi (vt->reference); - int len = 0; - if (strchr (vt->reference, ' ')) - len = atoi (strchr (vt->reference, ' ')+1); - - //fprintf (stderr, "%i-%i:", start, len); - - if (start < 0) start = 0; - if (start >= (prev_length))start = prev_length-1; - if (len + start > prev_length) - len = prev_length - start; - - //fprintf (stderr, "%i-%i\n", start, len); - - if (CTX_UNLIKELY (start == 0 && len == 0)) - { - vt_ctx_unrled (vt, CTX_CODEC_CHAR); - } - else - { - if (prev) - for (int i = 0; i < len && start + i < prev_length; i++) - { - vt_ctx_unrled (vt, prev[start + i]); - } - } - vt->ref_len = 0; - vt->reference[0]=0; - vt->in_prev_match = 0; - } - else - { - vt->reference[0]=0; - vt->ref_len = 0; - vt->in_prev_match = 1; - } - } - else - { - if (CTX_UNLIKELY(vt->in_prev_match)) - { - if (vt->ref_len < 15) - { - vt->reference[vt->ref_len++]=byte; - vt->reference[vt->ref_len]=0; - } - } - else - { - vt_ctx_unrled (vt, byte); - } - } -#else - vt_ctx_unrled (vt, byte); #endif -} static int vt_decoder_feed (VT *vt, int byte) { @@ -54271,7 +63125,7 @@ static void vt_state_osc (VT *vt, int byte) case 1: case 2: #if 0 - {"]0;New_title\e\", 0, , }, /* id: set window title */ " + {"]0;New_title\033\", 0, , }, /* id: set window title */ " #endif vt_set_title (vt, vt->argument_buf + 3); break; @@ -54342,7 +63196,7 @@ static void vt_state_osc (VT *vt, int byte) #if 0 {"]1337;key=value:base64data\b\", 0, vtcmd_erase_in_line, VT100}, /* args:keyvalue id: iterm2 graphics */ " #endif -#ifdef STBI_INCLUDE_STB_IMAGE_H +#if CTX_STB_IMAGE case 1337: if (!strncmp (&vt->argument_buf[6], "File=", 5) ) { @@ -54640,9 +63494,9 @@ static void vt_state_apc_generic (VT *vt, int byte) } #if 0 - {"_G..\e\", 0, vtcmd_delete_n_chars, VT102}, /* ref:none id: <a href='https://sw.kovidgoyal.net/kitty/graphics-protocol.html'>kitty graphics</a> */ " - {"_A..\e\", 0, vtcmd_delete_n_chars, VT102}, /* id: <a href='https://github.com/hodefoting/atty/'>atty</a> audio input/output */ " - {"_C..\e\", 0, vtcmd_delete_n_chars, VT102}, /* id: run command */ " + {"_G..\033\", 0, vtcmd_delete_n_chars, VT102}, /* ref:none id: <a href='https://sw.kovidgoyal.net/kitty/graphics-protocol.html'>kitty graphics</a> */ " + {"_A..\033\", 0, vtcmd_delete_n_chars, VT102}, /* id: <a href='https://github.com/hodefoting/atty/'>atty</a> audio input/output */ " + {"_C..\033\", 0, vtcmd_delete_n_chars, VT102}, /* id: run command */ " #endif static void vt_state_apc (VT *vt, int byte) { @@ -54717,7 +63571,7 @@ static void vt_state_esc (VT *vt, int byte) break; #if 0 - {"Psixel_data\e\", 0, , }, /* id: sixels */ " + {"Psixel_data\033\", 0, , }, /* id: sixels */ " #endif case 'P': @@ -54843,10 +63697,17 @@ int vt_poll (VT *vt, int timeout) int first = 1; while (remaining_chars > 0 && vt_waitdata (vt, first?0:1000*5) && - ( ticks - start_ticks < timeout || vt->state == vt_state_ctx)) + ( ticks - start_ticks < timeout +#if CTX_PARSER + || vt->state == vt_state_ctx +#endif + + )) { first = 0; +#if CTX_AUDIO vt_audio_task (vt, 0); +#endif if (vt->in_smooth_scroll) { remaining_chars = 1; @@ -54864,21 +63725,30 @@ int vt_poll (VT *vt, int timeout) // XXX allow state to break out in ctx mode on flush got_data+=len; remaining_chars -= len; +#if CTX_PARSER if (vt->state == vt_state_ctx) { if (remaining_chars < read_size) { remaining_chars = read_size * 2; } } +#endif ticks = ctx_ticks (); } if (got_data < 0) { - if (kill (vt->vtpty.pid, 0) != 0) + vt->empty_count ++; + if (vt->empty_count > 256) + { + if (kill (vt->vtpty.pid, 0) != 0) { vt->vtpty.done = 1; } + vt->empty_count = 1; + } } + else + vt->empty_count = 0; return got_data; } @@ -55345,6 +64215,7 @@ void vt_paste (VT *vt, const char *str) const char *ctx_find_shell_command (void) { +#if CTX_PTY #ifdef EMSCRIPTEN return NULL; #else @@ -55389,6 +64260,9 @@ const char *ctx_find_shell_command (void) } return command; #endif +#else + return NULL; +#endif } @@ -55396,6 +64270,7 @@ const char *ctx_find_shell_command (void) static void vt_run_command (VT *vt, const char *command, const char *term) { +#if CTX_PTY #ifdef EMSCRIPTEN printf ("run command %s\n", command); #else @@ -55426,6 +64301,7 @@ 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 } @@ -55442,8 +64318,10 @@ void vt_destroy (VT *vt) vt_line_free (vt->scrollback->data, 1); ctx_list_remove (&vt->scrollback, vt->scrollback->data); } +#if CTX_PARSER if (vt->ctxp) ctx_parser_destroy (vt->ctxp); +#endif //if (vt->ctx) // { ctx_destroy (vt->ctx); } free (vt->argument_buf); @@ -57708,7 +66586,10 @@ void ctx_client_mouse_event (CtxEvent *event, void *data, void *data2) (event->type == CTX_DRAG_MOTION || event->type == CTX_DRAG_PRESS || event->type == CTX_DRAG_RELEASE)) - return scrollbar_drag (event, data, data2); + { + scrollbar_drag (event, vt, data2); + return; + } switch (event->type) { case CTX_MOTION: @@ -57790,7 +66671,9 @@ void vt_mouse_event (CtxEvent *event, void *data, void *data2) (event->type == CTX_DRAG_MOTION || event->type == CTX_DRAG_PRESS || event->type == CTX_DRAG_RELEASE)) - return scrollbar_drag (event, data, data2); + { + scrollbar_drag (event, vt, data2);return; + } switch (event->type) { case CTX_MOTION: @@ -58023,7 +66906,7 @@ void vt_draw (VT *vt, Ctx *ctx, double x0, double y0) //if (vt->scroll || full) { ctx_begin_path (ctx); -#if 1 +#if CTX_PTY ctx_rectangle (ctx, 0, 0, vt->width, //(vt->cols) * vt->cw, (vt->rows) * vt->ch); if (vt->reverse_video) @@ -58037,6 +66920,9 @@ void vt_draw (VT *vt, Ctx *ctx, double x0, double y0) //ctx_rgba (ctx,0,0,0,1.0f); ctx_fill (ctx); } +#else + //ctx_rgba (ctx,0,0,0,1.0f); + ctx_fill (ctx); #endif if (vt->scroll != 0.0f) ctx_translate (ctx, 0.0, vt->ch * vt->scroll); @@ -58405,9 +67291,10 @@ void vt_set_local (VT *vt, int local) vt->local_editing = local; } +#if CTX_PTY static unsigned long prev_press_time = 0; static int short_count = 0; - +#endif void terminal_set_primary (const char *text) { @@ -58417,7 +67304,9 @@ void terminal_set_primary (const char *text) } void terminal_long_tap (Ctx *ctx, VT *vt); +#if CTX_PTY static int long_tap_cb_id = 0; +#endif static int single_tap (Ctx *ctx, void *data) { #if 0 // XXX @@ -58430,6 +67319,7 @@ static int single_tap (Ctx *ctx, void *data) void vt_mouse (VT *vt, CtxEvent *event, VtMouseEvent type, int button, int x, int y, int px_x, int px_y) { +#if CTX_PTY char buf[64]=""; int button_state = 0; ctx_client_rev_inc (vt->client); @@ -58644,8 +67534,9 @@ void vt_mouse (VT *vt, CtxEvent *event, VtMouseEvent type, int button, int x, in if (buf[0]) { vt_write (vt, buf, strlen (buf) ); - fflush (NULL); + fsync (vt->vtpty.pty); } +#endif } pid_t vt_get_pid (VT *vt) @@ -58658,6 +67549,7 @@ void vt_set_ctx (VT *vt, Ctx *ctx) vt->root_ctx = ctx; } +#endif #endif #endif @@ -58665,7 +67557,7 @@ float ctx_target_fps = 100.0; /* this might end up being the resolution of our idle callback firing */ -#if CTX_CLIENTS +#if CTX_VT #ifndef _DEFAULT_SOURCE @@ -58681,7 +67573,7 @@ float ctx_target_fps = 100.0; /* this might end up being the resolution of our #include <string.h> #include <sys/types.h> #include <sys/wait.h> -#include <sys/ioctl.h> +//#include <sys/ioctl.h> #include <signal.h> #include <math.h> #include <sys/time.h> @@ -58723,10 +67615,11 @@ const char *ctx_client_get_title (Ctx *ctx, int id) int vt_set_prop (VT *vt, uint32_t key_hash, const char *val) { +#if CTX_VT #if 1 switch (key_hash) { - case CTX_title: + case SQZ_title: { CtxClient *client = vt_get_client (vt); if (client) @@ -58765,24 +67658,25 @@ int vt_set_prop (VT *vt, uint32_t key_hash, const char *val) switch (key_hash) { - case CTX_title: ctx_client_set_title (ct->id, val); break; - case CTX_x: client->x = fval; break; - case CTX_y: client->y = fval; break; - case CTX_width: ctx_client_resize (ct->id, fval, client->height); break; - case CTX_height: ctx_client_resize (ct->id, client->width, fval); break; - case CTX_action: + case SQZ_title: ctx_client_set_title (ct->id, val); break; + case SQZ_x: client->x = fval; break; + case SQZ_y: client->y = fval; break; + case SQZ_width: ctx_client_resize (ct->id, fval, client->height); break; + case SQZ_height: ctx_client_resize (ct->id, client->width, fval); break; + case SQZ_action: switch (val_hash) { - case CTX_maximize: ctx_client_maximize (client); break; - case CTX_unmaximize: ctx_client_unmaximize (client); break; - case CTX_lower: ctx_client_lower (client); break; - case CTX_lowerBottom: ctx_client_lower_bottom (client); break; - case CTX_raise: ctx_client_raise (client); break; - case CTX_raiseTop: ctx_client_raise_top (client); break; + case SQZ_maximize: ctx_client_maximize (client); break; + case SQZ_unmaximize: ctx_client_unmaximize (client); break; + case SQZ_lower: ctx_client_lower (client); break; + case SQZ_lowerBottom: ctx_client_lower_bottom (client); break; + case SQZ_raise: ctx_client_raise (client); break; + case SQZ_raiseTop: ctx_client_raise_top (client); break; } break; } ct->rev++; +#endif #endif return 0; } @@ -58795,12 +67689,14 @@ void ctx_client_maximize (Ctx *ctx, int id); CtxClient *vt_get_client (VT *vt) { +#if CTX_VT for (CtxList *l = ctx_clients (vt->root_ctx); l; l =l->next) { CtxClient *client = l->data; if (client->vt == vt) return client; } +#endif return NULL; } @@ -58865,6 +67761,7 @@ CtxClient *ctx_client_new_argv (Ctx *ctx, char **argv, int x, int y, int width, } #ifndef EMSCRIPTEN +#if 0 static void *launch_client_thread (void *data) { CtxClient *client = data; @@ -58898,6 +67795,7 @@ CtxClient *ctx_client_new_thread (Ctx *ctx, void (*start_routine)(Ctx *ctx, void return client; } #endif +#endif #if CTX_SHAPE_CACHE extern float ctx_shape_cache_rate; @@ -59740,8 +68638,8 @@ float ctx_client_max_y_pos (Ctx *ctx) void ctx_client_titlebar_draw (Ctx *ctx, CtxClient *client, float x, float y, float width, float titlebar_height) { -#if 0 - ctx_move_to (ctx, x, y + height * 0.8); +#if CTX_PTY==0 + ctx_move_to (ctx, x, y + titlebar_height * 0.8); if (client == ctx->events.active) ctx_rgba (ctx, 1, 1,0.4, 1.0); else @@ -59879,7 +68777,11 @@ int ctx_clients_draw (Ctx *ctx, int layer2) !flag_is_set(client->flags, ITK_CLIENT_MAXIMIZED) && flag_is_set(client->flags, ITK_CLIENT_UI_RESIZABLE)) { +#if CTX_PTY itk_style_color (ctx, "titlebar-focused-bg"); +#else + ctx_rgb(ctx,0.1,0.2,0.3); +#endif ctx_rectangle (ctx, client->x, @@ -60059,12 +68961,12 @@ CtxList *ctx_clients (Ctx *ctx) return ctx?ctx->events.clients:NULL; } -#endif /* CTX_CLIENTS */ +#endif /* CTX_VT */ int ctx_clients_handle_events (Ctx *ctx) { //int n_clients = ctx_list_length (clients); -#if CTX_CLIENTS +#if CTX_VT int pending_data = 0; long time_start = ctx_ticks (); int sleep_time = 1000000/ctx_target_fps; @@ -60102,11 +69004,13 @@ done: } else { +#if CTX_AUDIO for (CtxList *l = clients; l; l = l->next) { CtxClient *client = l->data; vt_audio_task (client->vt, 0); } +#endif } //int got_events = 0; @@ -60163,13 +69067,13 @@ long ctx_client_rev (CtxClient *client) void ctx_client_feed_keystring (CtxClient *client, CtxEvent *event, const char *str) { -#if CTX_CLIENTS +#if CTX_VT if (!client || !client->vt) return; vt_feed_keystring (client->vt, event, str); #endif } -#if CTX_CLIENTS +#if CTX_VT int ctx_client_id (CtxClient *client) { return client?client->id:-1; @@ -60407,10 +69311,10 @@ static inline type ctx_tvg_##nick (CtxTinyVG *tvg)\ return ret;\ } -CTX_TVG_DEFINE_ACCESOR(u8, uint8_t); -CTX_TVG_DEFINE_ACCESOR(u16, uint16_t); -CTX_TVG_DEFINE_ACCESOR(u32, uint32_t); -CTX_TVG_DEFINE_ACCESOR(float, float); +CTX_TVG_DEFINE_ACCESOR(u8, uint8_t) +CTX_TVG_DEFINE_ACCESOR(u16, uint16_t) +CTX_TVG_DEFINE_ACCESOR(u32, uint32_t) +CTX_TVG_DEFINE_ACCESOR(float, float) #undef CTX_TVG_DEFINE_ACCESSOR diff --git a/sim/fakes/synth.py b/sim/fakes/bl00mbox.py similarity index 100% rename from sim/fakes/synth.py rename to sim/fakes/bl00mbox.py