#ifdef HAVE_CONFIG_H # include "config.h" #endif #include #include #include #include "ector_private.h" #include "ector_software_private.h" #include "ector_blend_private.h" static void _blend_color_argb(int count, const SW_FT_Span *spans, void *user_data) { Span_Data *data = (Span_Data *)(user_data); // multiply the color with mul_col if any uint color = ECTOR_MUL4_SYM(data->color, data->mul_col); Eina_Bool solid_source = ((color >> 24) == 255); // move to the offset location uint *buffer = data->raster_buffer.buffer + (data->raster_buffer.width * data->offy + data->offx); if (solid_source) { while (count--) { uint *target = buffer + (data->raster_buffer.width * spans->y + spans->x); if (spans->coverage == 255) { _ector_memfill(target, color, spans->len); } else { uint c = ECTOR_MUL_256(color, spans->coverage); int ialpha = 255 - spans->coverage; for (int i = 0; i < spans->len; ++i) target[i] = c + ECTOR_MUL_256(target[i], ialpha); } ++spans; } return; } while (count--) { uint *target = buffer + (data->raster_buffer.width * spans->y + spans->x); uint c = ECTOR_MUL_256(color, spans->coverage); int ialpha = (~c) >> 24; for (int i = 0; i < spans->len; ++i) target[i] = c + ECTOR_MUL_256(target[i], ialpha); ++spans; } } int buffer_size = 2048; typedef void (*src_fetch) (unsigned int *buffer, Span_Data *data, int y, int x, int length); static void _blend_gradient(int count, const SW_FT_Span *spans, void *user_data) { Span_Data *data = (Span_Data *)(user_data); src_fetch fetchfunc = NULL; if(data->type == LinearGradient) fetchfunc = &fetch_linear_gradient; if(data->type == RadialGradient) fetchfunc = &fetch_radial_gradient; unsigned int buffer[buffer_size]; // move to the offset location unsigned int *destbuffer = data->raster_buffer.buffer + (data->raster_buffer.width * data->offy + data->offx); while (count--) { unsigned int *target = destbuffer + (data->raster_buffer.width * spans->y + spans->x); int length = spans->len; while (length) { int l = MIN(length, buffer_size); if (fetchfunc) fetchfunc(buffer, data, spans->y, spans->x, l); if (data->mul_col == 0xffffffff) _ector_comp_func_source_over(target, buffer, l, spans->coverage); // TODO use proper composition func else _ector_comp_func_source_over_mul_c(target, buffer, data->mul_col, l, spans->coverage); target += l; length -= l; } ++spans; } } /*! \internal spans must be sorted on y */ static const SW_FT_Span *_intersect_spans_rect(const Eina_Rectangle *clip, const SW_FT_Span *spans, const SW_FT_Span *end, SW_FT_Span **out_spans, int available) { SW_FT_Span *out = *out_spans; const short minx = clip->x; const short miny = clip->y; const short maxx = minx + clip->w - 1; const short maxy = miny + clip->h - 1; while (available && spans < end ) { if (spans->y > maxy) { spans = end;// update spans so that we can breakout break; } if (spans->y < miny || spans->x > maxx || spans->x + spans->len <= minx) { ++spans; continue; } if (spans->x < minx) { out->len = MIN(spans->len - (minx - spans->x), maxx - minx + 1); out->x = minx; } else { out->x = spans->x; out->len = MIN(spans->len, (maxx - spans->x + 1)); } if (out->len != 0) { out->y = spans->y; out->coverage = spans->coverage; ++out; } ++spans; --available; } *out_spans = out; return spans; } static inline int _div_255(int x) { return (x + (x>>8) + 0x80) >> 8; } static const SW_FT_Span *_intersect_spans_region(const Shape_Rle_Data *clip, int *currentClip, const SW_FT_Span *spans, const SW_FT_Span *end, SW_FT_Span **out_spans, int available) { SW_FT_Span *out = *out_spans; const SW_FT_Span *clipSpans = clip->spans + *currentClip; const SW_FT_Span *clipEnd = clip->spans + clip->size; while (available && spans < end ) { if (clipSpans >= clipEnd) { spans = end; break; } if (clipSpans->y > spans->y) { ++spans; continue; } if (spans->y != clipSpans->y) { ++clipSpans; continue; } //assert(spans->y == clipSpans->y); int sx1 = spans->x; int sx2 = sx1 + spans->len; int cx1 = clipSpans->x; int cx2 = cx1 + clipSpans->len; if (cx1 < sx1 && cx2 < sx1) { ++clipSpans; continue; } else if (sx1 < cx1 && sx2 < cx1) { ++spans; continue; } int x = MAX(sx1, cx1); int len = MIN(sx2, cx2) - x; if (len) { out->x = MAX(sx1, cx1); out->len = MIN(sx2, cx2) - out->x; out->y = spans->y; out->coverage = _div_255(spans->coverage * clipSpans->coverage); ++out; --available; } if (sx2 < cx2) { ++spans; } else { ++clipSpans; } } *out_spans = out; *currentClip = clipSpans - clip->spans; return spans; } static void _span_fill_clipRect(int span_count, const SW_FT_Span *spans, void *user_data) { const int NSPANS = 256; int clip_count, i; SW_FT_Span cspans[NSPANS]; Span_Data *fill_data = (Span_Data *) user_data; Clip_Data clip = fill_data->clip; clip_count = eina_array_count(clip.clips); for (i = 0; i < clip_count ; i ++) { Eina_Rectangle *rect = (Eina_Rectangle *)eina_array_data_get(clip.clips, i); Eina_Rectangle tmpRect; // invert transform the offset tmpRect.x = rect->x - fill_data->offx; tmpRect.y = rect->y - fill_data->offy; tmpRect.w = rect->w; tmpRect.h = rect->h; const SW_FT_Span *end = spans + span_count; while (spans < end) { SW_FT_Span *clipped = cspans; spans = _intersect_spans_rect(&tmpRect,spans, end, &clipped, NSPANS); if (clipped - cspans) fill_data->unclipped_blend(clipped - cspans, cspans, fill_data); } } } static void _span_fill_clipPath(int span_count, const SW_FT_Span *spans, void *user_data) { const int NSPANS = 256; int current_clip = 0; SW_FT_Span cspans[NSPANS]; Span_Data *fill_data = (Span_Data *) user_data; Clip_Data clip = fill_data->clip; //TODO take clip path offset into account. const SW_FT_Span *end = spans + span_count; while (spans < end) { SW_FT_Span *clipped = cspans; spans = _intersect_spans_region(clip.path, ¤t_clip, spans, end, &clipped, NSPANS); if (clipped - cspans) fill_data->unclipped_blend(clipped - cspans, cspans, fill_data); } } static void _adjust_span_fill_methods(Span_Data *spdata) { switch(spdata->type) { case None: spdata->unclipped_blend = 0; break; case Solid: spdata->unclipped_blend = &_blend_color_argb; break; case LinearGradient: case RadialGradient: spdata->unclipped_blend = &_blend_gradient; break; case Image: spdata->unclipped_blend = 0;//&_blend_image; break; } // setup clipping if (!spdata->unclipped_blend) { spdata->blend = 0; } else if (!spdata->clip.enabled) { spdata->blend = spdata->unclipped_blend; } else if (spdata->clip.has_rect_clip) { spdata->blend = &_span_fill_clipRect; } else { spdata->blend = &_span_fill_clipPath; } } void ector_software_rasterizer_init(Software_Rasterizer *rasterizer) { // initialize the rasterizer and stroker unsigned char* renderPool = (unsigned char*) malloc(1024 * 100); sw_ft_grays_raster.raster_new(&rasterizer->raster); sw_ft_grays_raster.raster_reset(rasterizer->raster, renderPool, 1024*100); SW_FT_Stroker_New(&rasterizer->stroker); SW_FT_Stroker_Set(rasterizer->stroker, 1<<6,SW_FT_STROKER_LINECAP_BUTT,SW_FT_STROKER_LINEJOIN_MITER,0); //initialize the span data. rasterizer->fill_data.raster_buffer.buffer = NULL; rasterizer->fill_data.clip.enabled = EINA_FALSE; rasterizer->fill_data.unclipped_blend = 0; rasterizer->fill_data.blend = 0; } void ector_software_rasterizer_done(Software_Rasterizer *rasterizer) { sw_ft_grays_raster.raster_done(rasterizer->raster); SW_FT_Stroker_Done(rasterizer->stroker); //TODO free the pool memory } void ector_software_rasterizer_stroke_set(Software_Rasterizer *rasterizer, double width, Efl_Gfx_Cap cap_style, Efl_Gfx_Join join_style) { SW_FT_Stroker_LineCap cap; SW_FT_Stroker_LineJoin join; switch (cap_style) { case EFL_GFX_CAP_SQUARE: cap = SW_FT_STROKER_LINECAP_SQUARE; break; case EFL_GFX_CAP_ROUND: cap = SW_FT_STROKER_LINECAP_ROUND; break; default: cap = SW_FT_STROKER_LINECAP_BUTT; break; } switch (join_style) { case EFL_GFX_JOIN_BEVEL: join = SW_FT_STROKER_LINEJOIN_BEVEL; break; case EFL_GFX_JOIN_ROUND: join = SW_FT_STROKER_LINEJOIN_ROUND; break; default: join = SW_FT_STROKER_LINEJOIN_MITER; break; } int stroke_width = (int)(width * 64); SW_FT_Stroker_Set(rasterizer->stroker, stroke_width, cap, join, 0); } static void _rle_generation_cb( int count, const SW_FT_Span* spans,void *user) { Shape_Rle_Data *rle = (Shape_Rle_Data *) user; int newsize = rle->size + count; // allocate enough memory for new spans // alloc is required to prevent free and reallocation // when the rle needs to be regenerated because of attribute change. if(rle->alloc < newsize) { rle->spans = (SW_FT_Span *) realloc(rle->spans, newsize * sizeof(SW_FT_Span)); rle->alloc = newsize; } // copy the new spans to the allocated memory SW_FT_Span *lastspan = (rle->spans + rle->size); memcpy(lastspan,spans, count * sizeof(SW_FT_Span)); // update the size rle->size = newsize; } Shape_Rle_Data * ector_software_rasterizer_generate_rle_data(Software_Rasterizer *rasterizer, SW_FT_Outline *outline) { Shape_Rle_Data *rle_data = (Shape_Rle_Data *) calloc(1, sizeof(Shape_Rle_Data)); SW_FT_Raster_Params params; params.flags = SW_FT_RASTER_FLAG_DIRECT | SW_FT_RASTER_FLAG_AA ; params.gray_spans = &_rle_generation_cb; params.user = rle_data; params.source = outline; sw_ft_grays_raster.raster_render(rasterizer->raster, ¶ms); return rle_data; } Shape_Rle_Data * ector_software_rasterizer_generate_stroke_rle_data(Software_Rasterizer *rasterizer, SW_FT_Outline *outline, Eina_Bool closePath) { uint points,contors; SW_FT_Stroker_ParseOutline(rasterizer->stroker, outline, !closePath); SW_FT_Stroker_GetCounts(rasterizer->stroker,&points, &contors); SW_FT_Outline strokeOutline = {0}; strokeOutline.points = (SW_FT_Vector *) calloc(points, sizeof(SW_FT_Vector)); strokeOutline.tags = (char *) calloc(points, sizeof(char)); strokeOutline.contours = (short *) calloc(contors, sizeof(short)); SW_FT_Stroker_Export(rasterizer->stroker, &strokeOutline); Shape_Rle_Data *rle_data = ector_software_rasterizer_generate_rle_data(rasterizer, &strokeOutline); // cleanup the outline data. free(strokeOutline.points); free(strokeOutline.tags); free(strokeOutline.contours); return rle_data; } void ector_software_rasterizer_destroy_rle_data(Shape_Rle_Data *rle) { if (rle) { if (rle->spans) free(rle->spans); free(rle); } } static void _setup_span_fill_matrix(Software_Rasterizer *rasterizer) { if (rasterizer->transform) { eina_matrix3_inverse(rasterizer->transform, &rasterizer->fill_data.inv); } else { eina_matrix3_identity(&rasterizer->fill_data.inv); eina_matrix3_identity(&rasterizer->fill_data.inv); } } void ector_software_rasterizer_transform_set(Software_Rasterizer *rasterizer, Eina_Matrix3 *t) { rasterizer->transform = t; } void ector_software_rasterizer_clip_rect_set(Software_Rasterizer *rasterizer, Eina_Array *clips) { if (clips) { rasterizer->fill_data.clip.clips = clips; rasterizer->fill_data.clip.has_rect_clip = EINA_TRUE; rasterizer->fill_data.clip.enabled = EINA_TRUE; } else { rasterizer->fill_data.clip.clips = NULL; rasterizer->fill_data.clip.has_rect_clip = EINA_FALSE; rasterizer->fill_data.clip.enabled = EINA_FALSE; } } void ector_software_rasterizer_clip_shape_set(Software_Rasterizer *rasterizer, Shape_Rle_Data *clip) { rasterizer->fill_data.clip.path = clip; rasterizer->fill_data.clip.has_path_clip = EINA_TRUE; rasterizer->fill_data.clip.enabled = EINA_TRUE; } void ector_software_rasterizer_color_set(Software_Rasterizer *rasterizer, int r, int g, int b, int a) { rasterizer->fill_data.color = ECTOR_ARGB_JOIN(a, r, g, b); rasterizer->fill_data.type = Solid; } void ector_software_rasterizer_linear_gradient_set(Software_Rasterizer *rasterizer, Ector_Renderer_Software_Gradient_Data *linear) { rasterizer->fill_data.gradient = linear; rasterizer->fill_data.type = LinearGradient; } void ector_software_rasterizer_radial_gradient_set(Software_Rasterizer *rasterizer, Ector_Renderer_Software_Gradient_Data *radial) { rasterizer->fill_data.gradient = radial; rasterizer->fill_data.type = RadialGradient; } void ector_software_rasterizer_draw_rle_data(Software_Rasterizer *rasterizer, int x, int y, uint mul_col, Ector_Rop op, Shape_Rle_Data* rle) { // check for NULL rle data if (!rle) return; rasterizer->fill_data.offx = x; rasterizer->fill_data.offy = y; rasterizer->fill_data.mul_col = mul_col; rasterizer->fill_data.op = op; _setup_span_fill_matrix(rasterizer); _adjust_span_fill_methods(&rasterizer->fill_data); if(rasterizer->fill_data.blend) rasterizer->fill_data.blend(rle->size, rle->spans, &rasterizer->fill_data); }