GF_Err gf_path_add_subpath(GF_Path *gp, GF_Path *src, GF_Matrix2D *mx) { u32 i; if (!src) return GF_OK; gp->contours = gf_realloc(gp->contours, sizeof(u32) * (gp->n_contours + src->n_contours)); if (!gp->contours) return GF_OUT_OF_MEM; for (i=0; i<src->n_contours; i++) { gp->contours[i+gp->n_contours] = src->contours[i] + gp->n_points; } gp->n_contours += src->n_contours; gp->n_alloc_points += src->n_alloc_points; gp->points = gf_realloc(gp->points, sizeof(GF_Point2D)*gp->n_alloc_points); if (!gp->points) return GF_OUT_OF_MEM; gp->tags = gf_realloc(gp->tags, sizeof(u8)*gp->n_alloc_points); if (!gp->tags) return GF_OUT_OF_MEM; memcpy(gp->points + gp->n_points, src->points, sizeof(GF_Point2D)*src->n_points); if (mx) { for (i=0;i<src->n_points; i++) { gf_mx2d_apply_coords(mx, &gp->points[i+gp->n_points].x, &gp->points[i+gp->n_points].y); } } memcpy(gp->tags + gp->n_points, src->tags, sizeof(u8)*src->n_points); gp->n_points += src->n_points; gf_rect_union(&gp->bbox, &src->bbox); if (!(src->flags & GF_PATH_FLATTENED)) gp->flags &= ~GF_PATH_FLATTENED; if (src->flags & GF_PATH_BBOX_DIRTY) gp->flags |= GF_PATH_BBOX_DIRTY; return GF_OK; }
static void svg_update_bounds(SVG_TextStack *st) { u32 i=0; GF_TextSpan *span; /*finally compute text bounds*/ st->bounds.width = st->bounds.height = 0; st->bounds.x = st->bounds.y = 0; while ( (span = (GF_TextSpan*)gf_list_enum(st->spans, &i)) ) { gf_font_manager_refresh_span_bounds(span); gf_rect_union(&st->bounds, &span->bounds); } }
static void svg_text_area_shift_bounds(SVG_TextStack *st, GF_TraverseState *tr_state) { u32 i=0; GF_TextSpan *span; /*finally compute text bounds*/ st->bounds.width = st->bounds.height = 0; st->bounds.x = st->bounds.y = 0; while ( (span = (GF_TextSpan*)gf_list_enum(st->spans, &i)) ) { u32 j; for (j=0; j<span->nb_glyphs; j++) span->dy[j] += tr_state->base_shift; gf_font_manager_refresh_span_bounds(span); gf_rect_union(&st->bounds, &span->bounds); } }
static void build_text(TextStack *st, M_Text *txt, GF_TraverseState *tr_state) { u32 i, j, int_major, k, styles, count; Fixed fontSize, start_x, start_y, line_spacing, tot_width, tot_height, max_scale, maxExtent; u32 size, trim_size; GF_Font *font; Bool horizontal; GF_TextSpan *trim_tspan = NULL; GF_FontManager *ft_mgr = tr_state->visual->compositor->font_manager; M_FontStyle *fs = (M_FontStyle *)txt->fontStyle; fontSize = FSSIZE; if (fontSize <= 0) { fontSize = INT2FIX(12); if (!tr_state->pixel_metrics) fontSize = gf_divfix(fontSize, tr_state->visual->compositor->output_width); } horizontal = FSHORIZ; start_x = start_y = 0; styles = 0; if (fs && fs->style.buffer) { if (strstr(fs->style.buffer, "BOLD") || strstr(fs->style.buffer, "bold")) styles |= GF_FONT_WEIGHT_BOLD; if (strstr(fs->style.buffer, "ITALIC") || strstr(fs->style.buffer, "italic")) styles |= GF_FONT_ITALIC; if (strstr(fs->style.buffer, "UNDERLINED") || strstr(fs->style.buffer, "underlined")) styles |= GF_FONT_UNDERLINED; } font = gf_font_manager_set_font(ft_mgr, fs ? fs->family.vals : NULL, fs ? fs->family.count : 0, styles); if (!font) return; /*NOTA: we could use integer maths here but we have a risk of overflow with large fonts, so use fixed maths*/ st->ascent = gf_muldiv(fontSize, INT2FIX(font->ascent), INT2FIX(font->em_size)); st->descent = -gf_muldiv(fontSize, INT2FIX(font->descent), INT2FIX(font->em_size)); line_spacing = gf_mulfix(FSSPACE, fontSize); maxExtent = txt->maxExtent; trim_size = 0; if (maxExtent<0) { trim_tspan = gf_font_manager_create_span(ft_mgr, font, "...", fontSize, 0, 0, 0, NULL, 0, styles, (GF_Node*)txt); for (i=0; i<trim_tspan->nb_glyphs; i++) { if (horizontal) { trim_size += trim_tspan->glyphs[i] ? trim_tspan->glyphs[i]->horiz_advance : trim_tspan->font->max_advance_h; } else { trim_size += trim_tspan->glyphs[i] ? trim_tspan->glyphs[i]->vert_advance : trim_tspan->font->max_advance_v; } } } tot_width = tot_height = 0; for (i=0; i < txt->string.count; i++) { GF_TextSpan *tspan; char *str = txt->string.vals[i]; if (!str) continue; tspan = gf_font_manager_create_span(ft_mgr, font, txt->string.vals[i], fontSize, 0, 0, 0, NULL, 0, styles, (GF_Node*)txt); if (!tspan) continue; if (horizontal) tspan->flags |= GF_TEXT_SPAN_HORIZONTAL; size = 0; if (trim_size) { for (j=0; j<tspan->nb_glyphs; j++) { if (horizontal) { size += tspan->glyphs[j] ? tspan->glyphs[j]->horiz_advance : tspan->font->max_advance_h; } else { size += tspan->glyphs[j] ? tspan->glyphs[j]->vert_advance : tspan->font->max_advance_v; } /*word is bigger than allowed extent, rewrite 3 previous chars*/ if ((s32)size*tspan->font_scale >= -maxExtent) { u32 k; u32 nb_chars = (j<2) ? j : 3; for (k=0; k<nb_chars; k++) { u32 idx = nb_chars-k-1; if (horizontal) { size -= tspan->glyphs[j-k] ? tspan->glyphs[j-k]->horiz_advance : tspan->font->max_advance_h; size += trim_tspan->glyphs[idx] ? trim_tspan->glyphs[idx]->horiz_advance : tspan->font->max_advance_h; } else { size -= tspan->glyphs[j-k] ? tspan->glyphs[j-k]->vert_advance : tspan->font->max_advance_v; size += trim_tspan->glyphs[idx] ? trim_tspan->glyphs[idx]->vert_advance : tspan->font->max_advance_v; } tspan->glyphs[j-k] = trim_tspan->glyphs[idx]; } tspan->nb_glyphs = j+1; break; } } } if ((horizontal && !FSLTR) || (!horizontal && !FSTTB)) { for (k=0; k<tspan->nb_glyphs/2; k++) { GF_Glyph *g = tspan->glyphs[k]; tspan->glyphs[k] = tspan->glyphs[tspan->nb_glyphs-1-k]; tspan->glyphs[tspan->nb_glyphs-k-1] = g; } } if (!size) { for (j=0; j<tspan->nb_glyphs; j++) { if (horizontal) { size += tspan->glyphs[j] ? tspan->glyphs[j]->horiz_advance : tspan->font->max_advance_h; } else { size += tspan->glyphs[j] ? tspan->glyphs[j]->vert_advance : tspan->font->max_advance_v; } } } gf_list_add(st->spans, tspan); if (horizontal) { tspan->bounds.width = tspan->font_scale * size; /*apply length*/ if ((txt->length.count>i) && (txt->length.vals[i]>0)) { tspan->x_scale = gf_divfix(txt->length.vals[i], tspan->bounds.width); tspan->bounds.width = txt->length.vals[i]; } if (tot_width < tspan->bounds.width ) tot_width = tspan->bounds.width; } else { tspan->bounds.height = tspan->font_scale * size; /*apply length*/ if ((txt->length.count>i) && (txt->length.vals[i]>0)) { tspan->y_scale = gf_divfix(txt->length.vals[i], tspan->bounds.height); tspan->bounds.height = txt->length.vals[i]; } if (tot_height < tspan->bounds.height) tot_height = tspan->bounds.height; } } if (trim_tspan) gf_font_manager_delete_span(ft_mgr, trim_tspan); max_scale = FIX_ONE; if (horizontal) { if ((maxExtent > 0) && (tot_width>maxExtent)) { max_scale = gf_divfix(maxExtent, tot_width); tot_width = maxExtent; } tot_height = (txt->string.count-1) * line_spacing + (st->ascent + st->descent); st->bounds.height = tot_height; if (!strcmp(FSMINOR, "MIDDLE")) { if (FSTTB) { start_y = tot_height/2; st->bounds.y = start_y; } else { start_y = st->descent + st->ascent - tot_height/2; st->bounds.y = tot_height/2; } } else if (!strcmp(FSMINOR, "BEGIN")) { if (FSTTB) { start_y = st->descent; start_y = 0; st->bounds.y = start_y; } else { st->bounds.y = st->bounds.height; start_y = st->descent + st->ascent; } } else if (!strcmp(FSMINOR, "END")) { if (FSTTB) { start_y = tot_height; st->bounds.y = start_y; } else { start_y = -tot_height + 2*st->descent + st->ascent; st->bounds.y = start_y - (st->descent + st->ascent) + tot_height; } } else { start_y = st->ascent; st->bounds.y = FSTTB ? start_y : (tot_height - st->descent); } } else { if ((maxExtent > 0) && (tot_height>maxExtent) ) { max_scale = gf_divfix(maxExtent, tot_height); tot_height = maxExtent; } tot_width = txt->string.count * line_spacing; st->bounds.width = tot_width; if (!strcmp(FSMINOR, "MIDDLE")) { if (FSLTR) { start_x = -tot_width/2; st->bounds.x = start_x; } else { start_x = tot_width/2 - line_spacing; st->bounds.x = - tot_width + line_spacing; } } else if (!strcmp(FSMINOR, "END")) { if (FSLTR) { start_x = -tot_width; st->bounds.x = start_x; } else { start_x = tot_width-line_spacing; st->bounds.x = 0; } } else { if (FSLTR) { start_x = 0; st->bounds.x = start_x; } else { start_x = -line_spacing; st->bounds.x = -tot_width; } } } /*major-justification*/ if (!strcmp(FSMAJOR, "MIDDLE") ) { int_major = 0; } else if (!strcmp(FSMAJOR, "END") ) { int_major = 1; } else { int_major = 2; } st->bounds.width = st->bounds.height = 0; count = gf_list_count(st->spans); for (i=0; i < count; i++) { GF_TextSpan *span = gf_list_get(st->spans, i); switch (int_major) { /*major-justification MIDDLE*/ case 0: if (horizontal) { start_x = -span->bounds.width/2; } else { //start_y = FSTTB ? span->bounds.height/2 : (-span->bounds.height/2 + space); start_y = span->bounds.height/2; } break; /*major-justification END*/ case 1: if (horizontal) { start_x = (FSLTR) ? -span->bounds.width : 0; } else { //start_y = FSTTB ? span->bounds.height : (-span->bounds.height + space); start_y = FSTTB ? span->bounds.height : 0; } break; /*BEGIN, FIRST or default*/ default: if (horizontal) { start_x = (FSLTR) ? 0 : -span->bounds.width; } else { //start_y = FSTTB ? 0 : space; start_y = FSTTB ? 0 : span->bounds.height; } break; } span->off_x = start_x; span->bounds.x = start_x; if (horizontal) { span->off_y = start_y - st->ascent; span->x_scale = gf_mulfix(span->x_scale, max_scale); span->bounds.y = start_y; } else { span->y_scale = gf_mulfix(span->y_scale, max_scale); span->off_y = start_y - gf_mulfix(st->ascent, span->y_scale); span->bounds.y = start_y; } span->off_x = gf_mulfix(span->off_x, max_scale); span->off_y = gf_mulfix(span->off_y, max_scale); if (horizontal) { start_y += FSTTB ? -line_spacing : line_spacing; span->bounds.height = st->descent + st->ascent; } else { start_x += FSLTR ? line_spacing : -line_spacing; span->bounds.width = line_spacing; } gf_rect_union(&st->bounds, &span->bounds); } }
static void build_shape(FSStack *st, GF_Node *node) { GF_FieldInfo field; MFVec2f *coords; MFInt32 *com, *widthIndex, *lineIndex, *fillIndex, *coordIndex; MFFloat *widths; MFColor *colors; u32 wi, li, fi, ci, command, i, has_ci; FSItem *fill_item, *line_item; Fixed w; SFVec2f cur, pt, ct1={0,0}, ct2, *pts; GF_Rect rc; u32 line_col, fill_col; Bool need_line, need_fill; /*get all fields*/ gf_node_get_field(node, 0, &field); coords = field.far_ptr; gf_node_get_field(node, 1, &field); com = field.far_ptr; gf_node_get_field(node, 2, &field); widths = field.far_ptr; gf_node_get_field(node, 3, &field); colors = field.far_ptr; gf_node_get_field(node, 4, &field); widthIndex = field.far_ptr; gf_node_get_field(node, 5, &field); lineIndex = field.far_ptr; gf_node_get_field(node, 6, &field); fillIndex = field.far_ptr; gf_node_get_field(node, 7, &field); coordIndex = field.far_ptr; wi = li = fi = ci = 0; w = 0; st->max_width = 0; cur.x = cur.y = 0; fill_item = line_item = NULL; need_line = need_fill = 0; cur.x = cur.y = 0; has_ci = coordIndex->count ? 1 : 0; pts = coords->vals; line_col = fill_col = 0; /*implicit commands: 0 1 2 3*/ /* if (widthIndex->count) { w = (widthIndex->vals[wi]==-1) ? 0 : widths->vals[widthIndex->vals[wi]]; if (!w) { need_line = 0; line_item = NULL; } else { need_line = 1; if (st->max_width<w) st->max_width = w; } wi++; } if (lineIndex->count) { if (w) { line_col = SFCOL_MAKE_ARGB(colors->vals[lineIndex->vals[li]]); need_line = 1; } li++; } if (fillIndex->count) { if (fillIndex->vals[fi]==-1) { fill_col = 0; fill_item = NULL; } else { fill_col = SFCOL_MAKE_ARGB(colors->vals[fillIndex->vals[fi]]); need_fill = 1; } fi++; } if (!coordIndex->count) return; cur = coords->vals[coordIndex->vals[ci]]; ci++; */ for (command=0; command<com->count; command++) { switch (com->vals[command]) { /*set line width*/ case 0: if (wi >= widthIndex->count) return; w = (widthIndex->vals[wi]==-1) ? 0 : widths->vals[widthIndex->vals[wi]]; if (!w) line_item = NULL; else { need_line = 1; if (st->max_width<w) st->max_width = w; } wi++; break; /*set line color*/ case 1: if (li > lineIndex->count) return; if (w) { line_col = SFCOL_MAKE_ARGB(colors->vals[lineIndex->vals[li]]); need_line = 1; } li++; break; /*set fill color*/ case 2: if (fi >= fillIndex->count) return; if (fillIndex->vals[fi]==-1) { fill_col = 0; fill_item = NULL; } else { fill_col = SFCOL_MAKE_ARGB(colors->vals[fillIndex->vals[fi]]); need_fill = 1; } fi++; break; /*moveTo*/ case 3: if ((has_ci && ci >= coordIndex->count) || (!has_ci && ci >= coords->count) ) return; if (need_fill) { fill_item = new_fs_item(st, 0, fill_col, 0); need_fill = 0; } if (need_line) { line_item = new_fs_item(st, line_col, 0, w); need_line = 0; } if (has_ci) { pt = pts[coordIndex->vals[ci]]; } else { pt = pts[ci]; } if (fill_item) gf_path_add_move_to(fill_item->path, pt.x, pt.y); if (line_item) gf_path_add_move_to(line_item->path, pt.x, pt.y); ct1 = pt; cur = pt; ci++; break; /*lineTo*/ case 4: if ((has_ci && ci >= coordIndex->count) || (!has_ci && ci >= coords->count) ) return; if (need_fill) { fill_item = new_fs_item(st, 0, fill_col, 0); gf_path_add_move_to(fill_item->path, cur.x, cur.y); need_fill = 0; } if (need_line) { line_item = new_fs_item(st, line_col, 0, w); gf_path_add_move_to(line_item->path, cur.x, cur.y); need_line = 0; } if (has_ci) { pt = pts[coordIndex->vals[ci]]; } else { pt = pts[ci]; } if (fill_item) gf_path_add_line_to(fill_item->path, pt.x, pt.y); if (line_item) gf_path_add_line_to(line_item->path, pt.x, pt.y); cur = pt; ci++; break; /*cubic curveTo*/ case 5: if ((has_ci && ci + 2 >= coordIndex->count) || (!has_ci && ci + 2>= coords->count) ) return; if (need_fill) { fill_item = new_fs_item(st, 0, fill_col, 0); gf_path_add_move_to(fill_item->path, cur.x, cur.y); need_fill = 0; } if (need_line) { line_item = new_fs_item(st, line_col, 0, w); gf_path_add_move_to(line_item->path, cur.x, cur.y); need_line = 0; } if (has_ci) { ct1 = pts[coordIndex->vals[ci]]; ct2 = pts[coordIndex->vals[ci+1]]; pt = pts[coordIndex->vals[ci+2]]; } else { ct1 = pts[ci]; ct2 = pts[ci+1]; pt = pts[ci+2]; } if (fill_item) gf_path_add_cubic_to(fill_item->path, ct1.x, ct1.y, ct2.x, ct2.y, pt.x, pt.y); if (line_item) gf_path_add_cubic_to(line_item->path, ct1.x, ct1.y, ct2.x, ct2.y, pt.x, pt.y); ct1 = ct2; cur = pt; ci += 3; break; /*cubic nextCurveTo*/ case 6: if ((has_ci && ci + 1 >= coordIndex->count) || (!has_ci && ci + 1>= coords->count) ) return; if (need_fill) { fill_item = new_fs_item(st, 0, fill_col, 0); gf_path_add_move_to(fill_item->path, cur.x, cur.y); need_fill = 0; } if (need_line) { line_item = new_fs_item(st, line_col, 0, w); gf_path_add_move_to(line_item->path, cur.x, cur.y); need_line = 0; } ct1.x = 2*cur.x - ct1.x; ct1.y = 2*cur.y - ct1.y; if (has_ci) { ct2 = pts[coordIndex->vals[ci]]; pt = pts[coordIndex->vals[ci+1]]; } else { ct2 = pts[ci]; pt = pts[ci+1]; } if (fill_item) gf_path_add_cubic_to(fill_item->path, ct1.x, ct1.y, ct2.x, ct2.y, pt.x, pt.y); if (line_item) gf_path_add_cubic_to(line_item->path, ct1.x, ct1.y, ct2.x, ct2.y, pt.x, pt.y); ct1 = ct2; cur = pt; ci += 2; break; /*quadratic CurveTo*/ case 7: if ((has_ci && ci + 1 >= coordIndex->count) || (!has_ci && ci + 1>= coords->count) ) return; if (need_fill) { fill_item = new_fs_item(st, 0, fill_col, 0); gf_path_add_move_to(fill_item->path, cur.x, cur.y); need_fill = 0; } if (need_line) { line_item = new_fs_item(st, line_col, 0, w); gf_path_add_move_to(line_item->path, cur.x, cur.y); need_line = 0; } if (has_ci) { ct1 = pts[coordIndex->vals[ci]]; pt = pts[coordIndex->vals[ci+1]]; } else { ct1 = pts[ci]; pt = pts[ci+1]; } if (fill_item) gf_path_add_quadratic_to(fill_item->path, ct1.x, ct1.y, pt.x, pt.y); if (line_item) gf_path_add_quadratic_to(line_item->path, ct1.x, ct1.y, pt.x, pt.y); cur = pt; ci += 2; break; /*quadratic nextCurveTo*/ case 8: if ((has_ci && ci >= coordIndex->count) || (!has_ci && ci >= coords->count) ) return; if (need_fill) { fill_item = new_fs_item(st, 0, fill_col, 0); gf_path_add_move_to(fill_item->path, cur.x, cur.y); need_fill = 0; } if (need_line) { line_item = new_fs_item(st, line_col, 0, w); gf_path_add_move_to(line_item->path, cur.x, cur.y); need_line = 0; } ct1.x = 2*cur.x - ct1.x; ct1.y = 2*cur.y - ct1.y; if (has_ci) { pt = pts[coordIndex->vals[ci]]; } else { pt = pts[ci]; } if (fill_item) gf_path_add_quadratic_to(fill_item->path, ct1.x, ct1.y, pt.x, pt.y); if (line_item) gf_path_add_quadratic_to(line_item->path, ct1.x, ct1.y, pt.x, pt.y); cur = pt; ci += 2; break; /*close*/ case 9: if (fill_item) gf_path_close(fill_item->path); if (line_item) gf_path_close(line_item->path); break; } } /*compute bounds*/ st->bounds.width = st->bounds.height = 0; for (i=0; i<gf_list_count(st->items); i++) { line_item = gf_list_get(st->items, i); gf_path_get_bounds(line_item->path, &rc); gf_rect_union(&st->bounds, &rc); } }
Bool group_cache_compute_stats(GF_Node *node, GroupingNode2D *group, GF_TraverseState *tr_state, DrawableContext *first_child, Bool skip_first_child) { GF_Rect group_bounds; DrawableContext *ctx; u32 nb_segments, nb_objects; u32 alpha_pixels, opaque_pixels, area_world; u32 video_cache_max_size, cache_size, prev_cache_size; u32 i; GF_RectArray ra; /*compute stats*/ nb_objects = 0; nb_segments = 0; alpha_pixels = opaque_pixels = 0; prev_cache_size = group->cached_size; /*reset bounds*/ group_bounds.width = group_bounds.height = 0; video_cache_max_size = tr_state->visual->compositor->video_cache_max_size; /*never cache root node - this should be refined*/ if (gf_node_get_parent(node, 0) == NULL) goto group_reject; if (!group->traverse_time) goto group_reject; ra_init(&ra); ctx = first_child; if (!first_child) ctx = tr_state->visual->context; if (skip_first_child) ctx = ctx->next; /*compute properties for the sub display list*/ while (ctx && ctx->drawable) { //Fixed area; u32 alpha_comp; /*get area and compute alpha/opaque coverage*/ alpha_comp = GF_COL_A(ctx->aspect.fill_color); /*add to group area*/ gf_rect_union(&group_bounds, &ctx->bi->unclip); nb_objects++; /*no alpha*/ if ((alpha_comp==0xFF) /*no transparent texture*/ && (!ctx->aspect.fill_texture || !ctx->aspect.fill_texture->transparent) ) { ra_union_rect(&ra, &ctx->bi->clip); } nb_segments += ctx->drawable->path->n_points; ctx = ctx->next; } if ( /*TEST 1: discard visually empty groups*/ (!group_bounds.width || !group_bounds.height) || /*TEST 2: discard small groups*/ (nb_objects<MIN_OBJECTS_IN_CACHE) || /*TEST 3: low complexity group*/ (nb_segments && (nb_segments<10)) ) { ra_del(&ra); goto group_reject; } ra_refresh(&ra); opaque_pixels = 0; for (i=0; i<ra.count; i++) { opaque_pixels += ra.list[i].width * ra.list[i].height; } ra_del(&ra); /*get coverage in world coords*/ area_world = FIX2INT(group_bounds.width) * FIX2INT(group_bounds.height); /*TEST 4: discard low coverage groups in world coords (plenty of space wasted) we consider that this % of the area is actually drawn - this is of course wrong, we would need to compute each path coverage in local coords then get the ratio */ if (10*opaque_pixels < 7*area_world) goto group_reject; /*the memory size allocated for the cache - cache is drawn in final coordinate system !!*/ group_bounds.width = tr_state->visual->compositor->cache_scale * group_bounds.width / 100; group_bounds.height = tr_state->visual->compositor->cache_scale * group_bounds.height / 100; cache_size = FIX2INT(group_bounds.width) * FIX2INT(group_bounds.height) * 4 /* pixelFormat is ARGB*/; /*TEST 5: cache is less than 10x10 pixels: discard*/ if (cache_size < 400) goto group_reject; /*TEST 6: cache is larger than our allowed memory: discard*/ if (cache_size>=video_cache_max_size) { tr_state->cache_too_small = 1; goto group_reject; } /*compute the delta value for measuring the group importance for later discard (avg_time - Tcache) / (size_cache - drawable_gain) */ group->priority = INT2FIX(nb_objects*1024*group->traverse_time) / cache_size / group->nb_stats_frame; /*OK, group is a good candidate for caching*/ group->nb_objects = nb_objects; group->cached_size = cache_size; /*we're moving from non-cached to cached*/ if (!(group->flags & GROUP_IS_CACHABLE)) { group->flags |= GROUP_IS_CACHABLE; tr_state->visual->compositor->draw_next_frame = 1; /*insert the candidate and then update the list in order*/ group_cache_insert_entry(node, group, tr_state); /*keep track of this cache object for later removal*/ gf_list_add(tr_state->visual->compositor->cached_groups_queue, group); GF_LOG(GF_LOG_DEBUG, GF_LOG_CACHE, ("[CACHE] Turning cache on during stat pass for node %s - %d kb used in all caches\n", gf_node_get_log_name(node), tr_state->visual->compositor->video_cache_current_size )); } /*update memory occupation*/ else { tr_state->visual->compositor->video_cache_current_size -= prev_cache_size; tr_state->visual->compositor->video_cache_current_size += group->cached_size; if (group->cache) group->cache->force_recompute = 1; } return 1; group_reject: group->nb_objects = nb_objects; if ((group->flags & GROUP_IS_CACHABLE) || group->cache) { group->flags &= ~GROUP_IS_CACHABLE; if (group->cache) { group_cache_del(group->cache); group->cache = NULL; group->flags &= ~GROUP_IS_CACHED; } gf_list_del_item(tr_state->visual->compositor->cached_groups, group); tr_state->visual->compositor->video_cache_current_size -= cache_size; } #if 0 GF_LOG(GF_LOG_DEBUG, GF_LOG_CACHE, ("[CACHE] REJECT %s\tObjects: %d\tSlope: %g\tBytes: %d\tTime: %d\n", gf_node_get_log_name(node), group->nb_objects, FIX2FLT(group->priority), group->cached_size, group->traverse_time )); GF_LOG(GF_LOG_DEBUG, GF_LOG_CACHE, ("[CACHE] Status (B): Max: %d\tUsed: %d\tNb Groups: %d\n", tr_state->visual->compositor->video_cache_max_size, tr_state->visual->compositor->video_cache_current_size, gf_list_count(tr_state->visual->compositor->cached_groups) )); #endif return 0; }
Bool group_cache_traverse(GF_Node *node, GroupCache *cache, GF_TraverseState *tr_state, Bool force_recompute, Bool is_mpeg4, Bool auto_fit_vp) { GF_Matrix2D backup; DrawableContext *group_ctx = NULL; GF_ChildNodeItem *l; if (!cache) return 0; /*do we need to recompute the cache*/ if (cache->force_recompute) { force_recompute = 1; cache->force_recompute = 0; } else if (gf_node_dirty_get(node) & GF_SG_CHILD_DIRTY) { force_recompute = 1; } /*we need to redraw the group in an offscreen visual*/ if (force_recompute) { GF_Matrix2D backup; GF_IRect rc1, rc2; u32 type_3d; u32 prev_flags; GF_Rect cache_bounds; GF_SURFACE offscreen_surface, old_surf; GF_Raster2D *r2d = tr_state->visual->compositor->rasterizer; DrawableContext *child_ctx; Fixed temp_x, temp_y, scale_x, scale_y; GF_LOG(GF_LOG_INFO, GF_LOG_COMPOSE, ("[Compositor] Recomputing cache for subtree %s\n", gf_node_get_log_name(node))); /*step 1 : store current state and indicate children should not be cached*/ tr_state->in_group_cache = 1; prev_flags = tr_state->immediate_draw; /*store the current transform matrix, create a new one for group_cache*/ gf_mx2d_copy(backup, tr_state->transform); gf_mx2d_init(tr_state->transform); type_3d = 0; #ifndef GPAC_DISABLE_3D /*force 2D rendering*/ type_3d = tr_state->visual->type_3d; tr_state->visual->type_3d = 0; #endif /*step 2: collect the bounds of all children*/ tr_state->traversing_mode = TRAVERSE_GET_BOUNDS; cache_bounds.width = cache_bounds.height = 0; l = ((GF_ParentNode*)node)->children; while (l) { tr_state->bounds.width = tr_state->bounds.height = 0; gf_node_traverse(l->node, tr_state); l = l->next; gf_rect_union(&cache_bounds, &tr_state->bounds); } tr_state->traversing_mode = TRAVERSE_SORT; if (!cache_bounds.width || !cache_bounds.height) { tr_state->in_group_cache = 0; tr_state->immediate_draw = prev_flags; gf_mx2d_copy(tr_state->transform, backup); #ifndef GPAC_DISABLE_3D tr_state->visual->type_3d = type_3d; #endif return 0; } /*step 3: insert a DrawableContext for this group in the display list*/ if (is_mpeg4) { #ifndef GPAC_DISABLE_VRML group_ctx = drawable_init_context_mpeg4(cache->drawable, tr_state); #endif } else { #ifndef GPAC_DISABLE_SVG group_ctx = drawable_init_context_svg(cache->drawable, tr_state); #endif } if (!group_ctx) return 0; /*step 4: now we have the bounds: allocate the offscreen memory create temp raster visual & attach to buffer override the tr_state->visual->the_surface with the temp raster add translation (shape is not always centered) setup top clipers */ old_surf = tr_state->visual->raster_surface; offscreen_surface = r2d->surface_new(r2d, tr_state->visual->center_coords); /*a new temp raster visual*/ tr_state->visual->raster_surface = offscreen_surface; /*use current surface coordinate scaling to compute the cache*/ #ifdef GF_SR_USE_VIDEO_CACHE scale_x = tr_state->visual->compositor->cache_scale * backup.m[0] / 100; scale_y = tr_state->visual->compositor->cache_scale * backup.m[4] / 100; #else scale_x = backup.m[0]; scale_y = backup.m[4]; #endif if (scale_x<0) scale_x = -scale_x; if (scale_y<0) scale_y = -scale_y; cache->scale = MAX(scale_x, scale_y); tr_state->bounds = cache_bounds; gf_mx2d_add_scale(&tr_state->transform, scale_x, scale_y); gf_mx2d_apply_rect(&tr_state->transform, &cache_bounds); rc1 = gf_rect_pixelize(&cache_bounds); if (rc1.width % 2) rc1.width++; if (rc1.height%2) rc1.height++; /* Initialize the group cache with the scaled pixelized bounds for texture but the original bounds for path*/ group_cache_setup(cache, &tr_state->bounds, &rc1, tr_state->visual->compositor, type_3d); /*attach the buffer to visual*/ r2d->surface_attach_to_buffer(offscreen_surface, cache->txh.data, cache->txh.width, cache->txh.height, 0, cache->txh.stride, cache->txh.pixelformat); /*recompute the bounds with the final scaling used*/ scale_x = gf_divfix(INT2FIX(rc1.width), tr_state->bounds.width); scale_y = gf_divfix(INT2FIX(rc1.height), tr_state->bounds.height); gf_mx2d_init(tr_state->transform); gf_mx2d_add_scale(&tr_state->transform, scale_x, scale_y); cache_bounds = tr_state->bounds; gf_mx2d_apply_rect(&tr_state->transform, &cache_bounds); /*centered the bitmap on the visual*/ temp_x = -cache_bounds.x; temp_y = -cache_bounds.y; if (tr_state->visual->center_coords) { temp_x -= cache_bounds.width/2; temp_y += cache_bounds.height/2; } else { temp_y += cache_bounds.height; } gf_mx2d_add_translation(&tr_state->transform, temp_x, temp_y); /*override top clippers*/ rc1 = tr_state->visual->surf_rect; rc2 = tr_state->visual->top_clipper; tr_state->visual->surf_rect.width = cache->txh.width; tr_state->visual->surf_rect.height = cache->txh.height; if (tr_state->visual->center_coords) { tr_state->visual->surf_rect.y = cache->txh.height/2; tr_state->visual->surf_rect.x = -1 * (s32) cache->txh.width/2; } else { tr_state->visual->surf_rect.y = cache->txh.height; tr_state->visual->surf_rect.x = 0; } tr_state->visual->top_clipper = tr_state->visual->surf_rect; /*step 5: traverse subtree in direct draw mode*/ tr_state->immediate_draw = 1; group_ctx->flags &= ~CTX_NO_ANTIALIAS; l = ((GF_ParentNode*)node)->children; while (l) { gf_node_traverse(l->node, tr_state); l = l->next; } /*step 6: reset all contexts after the current group one*/ child_ctx = group_ctx->next; while (child_ctx && child_ctx->drawable) { drawable_reset_bounds(child_ctx->drawable, tr_state->visual); child_ctx->drawable = NULL; child_ctx = child_ctx->next; } /*and set ourselves as the last context on the main visual*/ tr_state->visual->cur_context = group_ctx; /*restore state and destroy whatever needs to be cleaned*/ gf_mx2d_copy(tr_state->transform, backup); tr_state->in_group_cache = 0; tr_state->immediate_draw = prev_flags; r2d->surface_delete(offscreen_surface); tr_state->visual->raster_surface = old_surf; tr_state->traversing_mode = TRAVERSE_SORT; #ifndef GPAC_DISABLE_3D tr_state->visual->type_3d = type_3d; #endif tr_state->visual->surf_rect = rc1; tr_state->visual->top_clipper = rc2; /*update texture*/ cache->txh.transparent = 1; cache->txh.flags |= GF_SR_TEXTURE_NO_GL_FLIP; gf_sc_texture_set_data(&cache->txh); gf_sc_texture_push_image(&cache->txh, 0, type_3d ? 0 : 1); cache->orig_vp = tr_state->vp_size; } /*just setup the context*/ else { if (is_mpeg4) { #ifndef GPAC_DISABLE_VRML group_ctx = drawable_init_context_mpeg4(cache->drawable, tr_state); #endif } else { #ifndef GPAC_DISABLE_SVG group_ctx = drawable_init_context_svg(cache->drawable, tr_state); #endif } } if (!group_ctx) return 0; group_ctx->flags |= CTX_NO_ANTIALIAS; if (cache->opacity != FIX_ONE) group_ctx->aspect.fill_color = GF_COL_ARGB_FIXED(cache->opacity, FIX_ONE, FIX_ONE, FIX_ONE); else group_ctx->aspect.fill_color = 0; group_ctx->aspect.fill_texture = &cache->txh; if (!cache->opacity) { group_ctx->drawable = NULL; return 0; } if (gf_node_dirty_get(node)) group_ctx->flags |= CTX_TEXTURE_DIRTY; #ifdef CACHE_DEBUG_CENTER gf_mx2d_copy(backup, tr_state->transform); gf_mx2d_init(tr_state->transform); #else gf_mx2d_copy(backup, tr_state->transform); if (auto_fit_vp) { if ((tr_state->vp_size.x != cache->orig_vp.x) || (tr_state->vp_size.y != cache->orig_vp.y)) { GF_Matrix2D m; gf_mx2d_init(m); gf_mx2d_copy(backup, tr_state->transform); gf_mx2d_add_scale(&m, gf_divfix(tr_state->vp_size.x, cache->orig_vp.x), gf_divfix(tr_state->vp_size.y, cache->orig_vp.y) ); gf_mx2d_pre_multiply(&tr_state->transform, &m); } else { auto_fit_vp = 0; } } #endif #ifndef GPAC_DISABLE_3D if (tr_state->visual->type_3d) { if (!cache->drawable->mesh) { cache->drawable->mesh = new_mesh(); mesh_from_path(cache->drawable->mesh, cache->drawable->path); } visual_3d_draw_from_context(group_ctx, tr_state); group_ctx->drawable = NULL; } else #endif drawable_finalize_sort(group_ctx, tr_state, NULL); #ifndef CACHE_DEBUG_CENTER if (auto_fit_vp) #endif { gf_mx2d_copy(tr_state->transform, backup); } return (force_recompute==1); }
void gf_sc_get_nodes_bounds(GF_Node *self, GF_ChildNodeItem *children, GF_TraverseState *tr_state, s32 *child_idx) { u32 i; SFVec2f size; GF_Rect rc; GF_Matrix2D cur_mx; if (tr_state->abort_bounds_traverse) { if (self == tr_state->for_node) { gf_mx2d_pre_multiply(&tr_state->mx_at_node, &tr_state->transform); } tr_state->abort_bounds_traverse=0; gf_sc_get_nodes_bounds(self, children, tr_state, child_idx); tr_state->abort_bounds_traverse=1; return; } if (!children) return; size.x = size.y = -FIX_ONE; #ifndef GPAC_DISABLE_VRML switch (gf_node_get_tag(self)) { case TAG_MPEG4_Layer2D: size = ((M_Layer2D *)self)->size; break; case TAG_MPEG4_Layer3D: size = ((M_Layer3D *)self)->size; break; case TAG_MPEG4_Form: size = ((M_Form *)self)->size; break; } #endif if ((size.x>=0) && (size.y>=0)) { tr_state->bounds = gf_rect_center(size.x, size.y); return; } gf_mx2d_copy(cur_mx, tr_state->transform); rc = gf_rect_center(0,0); i = 0; while (children) { if (child_idx && (i != (u32) *child_idx)) { children = children->next; continue; } gf_mx2d_init(tr_state->transform); tr_state->bounds = gf_rect_center(0,0); /*we hit the target node*/ if (children->node == tr_state->for_node) tr_state->abort_bounds_traverse = 1; gf_node_traverse(children->node, tr_state); if (tr_state->abort_bounds_traverse) { gf_mx2d_add_matrix(&tr_state->mx_at_node, &cur_mx); return; } gf_mx2d_apply_rect(&tr_state->transform, &tr_state->bounds); gf_rect_union(&rc, &tr_state->bounds); children = children->next; if (child_idx) break; } #ifndef GPAC_DISABLE_SVG if (gf_node_get_tag(self)==TAG_SVG_use) { GF_FieldInfo info; if (gf_node_get_attribute_by_tag(self, TAG_XLINK_ATT_href, 0, 0, &info)==GF_OK) { GF_Node *iri = ((XMLRI*)info.far_ptr)->target; if (iri) { gf_mx2d_init(tr_state->transform); tr_state->bounds = gf_rect_center(0,0); /*we hit the target node*/ if (iri == tr_state->for_node) tr_state->abort_bounds_traverse = 1; gf_node_traverse(iri, tr_state); if (tr_state->abort_bounds_traverse) { gf_mx2d_pre_multiply(&tr_state->mx_at_node, &cur_mx); return; } gf_mx2d_apply_rect(&tr_state->transform, &tr_state->bounds); gf_rect_union(&rc, &tr_state->bounds); } } } #endif gf_mx2d_copy(tr_state->transform, cur_mx); if (self != tr_state->for_node) { gf_mx2d_apply_rect(&tr_state->transform, &rc); } tr_state->bounds = rc; }