GF_EXPORT void gf_sc_texture_update_frame(GF_TextureHandler *txh, Bool disable_resync) { u32 size, ts; /*already refreshed*/ if (txh->needs_refresh) return; if (!txh->stream) { txh->data = NULL; return; } /*should never happen!!*/ if (txh->needs_release) gf_mo_release_data(txh->stream, 0xFFFFFFFF, 0); /*check init flag*/ if (!(gf_mo_get_flags(txh->stream) & GF_MO_IS_INIT)) { /*if we had a texture this means the object has changed - delete texture and force next frame composition (this will take care of OD reuse)*/ if (txh->tx_io) { gf_sc_texture_release(txh); txh->data = NULL; txh->needs_refresh = 1; gf_sc_invalidate(txh->compositor, NULL); return; } if (gf_mo_is_private_media(txh->stream)) { setup_texture_object(txh, 1); gf_node_dirty_set(txh->owner, 0, 0); } } txh->data = gf_mo_fetch_data(txh->stream, !disable_resync, &txh->stream_finished, &ts, &size); /*if no frame or muted don't draw*/ if (!txh->data || !size) { /*TODO - check if this is needed */ if (txh->flags & GF_SR_TEXTURE_PRIVATE_MEDIA) { //txh->needs_refresh = 1; gf_sc_invalidate(txh->compositor, NULL); } return; } /*if setup and same frame return*/ if (txh->tx_io && (txh->stream_finished || (txh->last_frame_time==ts)) ) { gf_mo_release_data(txh->stream, 0xFFFFFFFF, 0); txh->needs_release = 0; return; } txh->needs_release = 1; txh->last_frame_time = ts; if (gf_mo_is_muted(txh->stream)) return; if (!txh->tx_io) { setup_texture_object(txh, 0); } /*try to push texture on graphics but don't complain if failure*/ gf_sc_texture_set_data(txh); txh->needs_refresh = 1; gf_sc_invalidate(txh->compositor, NULL); }
GF_EXPORT void gf_sc_texture_update_frame(GF_TextureHandler *txh, Bool disable_resync) { Bool needs_reload = 0; u32 size, ts; s32 ms_until_pres, ms_until_next; /*already refreshed*/ if (txh->needs_refresh) return; if (!txh->stream) { txh->data = NULL; return; } /*should never happen!!*/ if (txh->needs_release) gf_mo_release_data(txh->stream, 0xFFFFFFFF, 0); /*check init flag*/ if (!(gf_mo_get_flags(txh->stream) & GF_MO_IS_INIT)) { needs_reload = 1; txh->data = NULL; if (txh->tx_io) { gf_sc_texture_release(txh); } } txh->data = gf_mo_fetch_data(txh->stream, !disable_resync, &txh->stream_finished, &ts, &size, &ms_until_pres, &ms_until_next); if (!(gf_mo_get_flags(txh->stream) & GF_MO_IS_INIT)) { needs_reload = 1; } else if (size && txh->size && (size != txh->size)) { needs_reload = 1; } if (needs_reload) { /*if we had a texture this means the object has changed - delete texture and resetup. Do not skip texture update as this may lead to an empty rendering pass (blank frame for this object), especially in DASH*/ if (txh->tx_io) { gf_sc_texture_release(txh); txh->needs_refresh = 1; } if (gf_mo_is_private_media(txh->stream)) { setup_texture_object(txh, 1); gf_node_dirty_set(txh->owner, 0, 0); } } /*if no frame or muted don't draw*/ if (!txh->data || !size) { GF_LOG(GF_LOG_INFO, GF_LOG_CODEC, ("|Visual Texture] No output frame available \n")); /*TODO - check if this is needed */ if (txh->flags & GF_SR_TEXTURE_PRIVATE_MEDIA) { //txh->needs_refresh = 1; gf_sc_invalidate(txh->compositor, NULL); } return; } /*if setup and same frame return*/ if (txh->tx_io && (txh->stream_finished || (txh->last_frame_time==ts)) ) { gf_mo_release_data(txh->stream, 0xFFFFFFFF, 0); txh->needs_release = 0; if (!txh->stream_finished) { if (ms_until_next>0 && (txh->compositor->next_frame_delay > (u32) ms_until_next)) txh->compositor->next_frame_delay = ms_until_next; } return; } txh->needs_release = 1; txh->last_frame_time = ts; txh->size = size; if (txh->raw_memory) { gf_mo_get_raw_image_planes(txh->stream, (u8 **) &txh->data, (u8 **) &txh->pU, (u8 **) &txh->pV); } if (gf_mo_is_muted(txh->stream)) return; if (txh->nb_frames) { s32 push_delay = txh->upload_time / txh->nb_frames; if (push_delay > ms_until_pres) ms_until_pres = 0; else ms_until_pres -= push_delay; } if (txh->compositor->frame_delay < ms_until_pres) txh->compositor->frame_delay = ms_until_pres; txh->compositor->next_frame_delay = 1; if (!txh->tx_io) { setup_texture_object(txh, 0); } /*try to push texture on graphics but don't complain if failure*/ gf_sc_texture_set_data(txh); txh->needs_refresh = 1; gf_sc_invalidate(txh->compositor, NULL); }
static void pixeltexture_update(GF_TextureHandler *txh) { u32 pix_format, stride, i; M_PixelTexture *pt = (M_PixelTexture *) txh->owner; PixelTextureStack *st = (PixelTextureStack *) gf_node_get_private(txh->owner); if (!gf_node_dirty_get(txh->owner)) return; gf_node_dirty_clear(txh->owner, 0); /*pixel texture doesn not use any media object but has data in the content. However we still use the same texture object, just be carefull not to use media funtcions*/ txh->transparent = 0; stride = pt->image.width; /*num_components are as in VRML (1->4) not as in BIFS bitstream (0->3)*/ switch (pt->image.numComponents) { case 1: pix_format = GF_PIXEL_GREYSCALE; break; case 2: pix_format = GF_PIXEL_ALPHAGREY; txh->transparent = 1; stride *= 2; break; case 3: pix_format = GF_PIXEL_RGB_24; txh->transparent = 0; stride *= 3; break; case 4: pix_format = GF_PIXEL_RGBA; txh->transparent = 1; stride *= 4; break; default: return; } if (!txh->tx_io) { gf_sc_texture_allocate(txh); if (!txh->tx_io) return; } if (st->pixels) gf_free(st->pixels); st->pixels = (char*)gf_malloc(sizeof(char) * stride * pt->image.height); /*FIXME FOR OPENGL !!*/ if (0) { for (i=0; i<pt->image.height; i++) { memcpy(st->pixels + i * stride, pt->image.pixels + i * stride, stride); } } /*revert pixel ordering...*/ else { for (i=0; i<pt->image.height; i++) { memcpy(st->pixels + i * stride, pt->image.pixels + (pt->image.height - 1 - i) * stride, stride); } } txh->width = pt->image.width; txh->height = pt->image.height; txh->stride = stride; txh->pixelformat = pix_format; txh->data = st->pixels; gf_sc_texture_set_data(txh); }
static void imagetexture_update(GF_TextureHandler *txh) { if (gf_node_get_tag(txh->owner)!=TAG_MPEG4_CacheTexture) { MFURL url = ((M_ImageTexture *) txh->owner)->url; /*setup texture if needed*/ if (!txh->is_open && url.count) { gf_sc_texture_play(txh, &url); } gf_sc_texture_update_frame(txh, 0); if ( /*URL is present but not opened - redraw till fetch*/ /* (txh->stream && !txh->tx_io) && */ /*image has been updated*/ txh->needs_refresh) { /*mark all subtrees using this image as dirty*/ gf_node_dirty_parents(txh->owner); gf_sc_invalidate(txh->compositor, NULL); } return; } /*cache texture case*/ else { M_CacheTexture *ct = (M_CacheTexture *) txh->owner; /*decode cacheTexture data */ if ((ct->data || ct->image.buffer) && !txh->data) { #ifndef GPAC_DISABLE_AV_PARSERS u32 out_size; GF_Err e; /*BT/XMT playback: load to memory*/ if (ct->image.buffer) { char *par = (char *) gf_scene_get_service_url( gf_node_get_graph(txh->owner ) ); char *src_url = gf_url_concatenate(par, ct->image.buffer); FILE *test = gf_fopen( src_url ? src_url : ct->image.buffer, "rb"); if (test) { fseek(test, 0, SEEK_END); ct->data_len = (u32) gf_ftell(test); ct->data = gf_malloc(sizeof(char)*ct->data_len); fseek(test, 0, SEEK_SET); if (ct->data_len != fread(ct->data, 1, ct->data_len, test)) { GF_LOG(GF_LOG_ERROR, GF_LOG_COMPOSE, ("[Compositor] Failed to load CacheTexture data from file %s: IO err\n", src_url ? src_url : ct->image.buffer ) ); gf_free(ct->data); ct->data = NULL; ct->data_len = 0; } gf_fclose(test); } else { GF_LOG(GF_LOG_ERROR, GF_LOG_COMPOSE, ("[Compositor] Failed to load CacheTexture data from file %s: not found\n", src_url ? src_url : ct->image.buffer ) ); } ct->image.buffer = NULL; if (src_url) gf_free(src_url); } /*BIFS decoded playback*/ switch (ct->objectTypeIndication) { case GPAC_OTI_IMAGE_JPEG: out_size = 0; e = gf_img_jpeg_dec((char *) ct->data, ct->data_len, &txh->width, &txh->height, &txh->pixelformat, NULL, &out_size, 3); if (e==GF_BUFFER_TOO_SMALL) { u32 BPP; txh->data = gf_malloc(sizeof(char) * out_size); if (txh->pixelformat==GF_PIXEL_GREYSCALE) BPP = 1; else BPP = 3; e = gf_img_jpeg_dec((char *) ct->data, ct->data_len, &txh->width, &txh->height, &txh->pixelformat, txh->data, &out_size, BPP); if (e==GF_OK) { gf_sc_texture_allocate(txh); gf_sc_texture_set_data(txh); txh->needs_refresh = 1; txh->stride = out_size / txh->height; } } break; case GPAC_OTI_IMAGE_PNG: out_size = 0; e = gf_img_png_dec((char *) ct->data, ct->data_len, &txh->width, &txh->height, &txh->pixelformat, NULL, &out_size); if (e==GF_BUFFER_TOO_SMALL) { txh->data = gf_malloc(sizeof(char) * out_size); e = gf_img_png_dec((char *) ct->data, ct->data_len, &txh->width, &txh->height, &txh->pixelformat, txh->data, &out_size); if (e==GF_OK) { gf_sc_texture_allocate(txh); gf_sc_texture_set_data(txh); txh->needs_refresh = 1; txh->stride = out_size / txh->height; } } break; } #endif // GPAC_DISABLE_AV_PARSERS /*cacheURL is specified, store the image*/ if (ct->cacheURL.buffer) { u32 i; u8 hash[20]; FILE *cached_texture; char szExtractName[GF_MAX_PATH], section[64], *opt, *src_url; opt = (char *) gf_cfg_get_key(txh->compositor->user->config, "General", "CacheDirectory"); if (opt) { strcpy(szExtractName, opt); } else { opt = gf_get_default_cache_directory(); strcpy(szExtractName, opt); gf_free(opt); } strcat(szExtractName, "/"); src_url = (char *) gf_scene_get_service_url( gf_node_get_graph(txh->owner ) ); gf_sha1_csum((u8 *)src_url, (u32) strlen(src_url), hash); for (i=0; i<20; i++) { char t[3]; t[2] = 0; sprintf(t, "%02X", hash[i]); strcat(szExtractName, t); } strcat(szExtractName, "_"); strcat(szExtractName, ct->cacheURL.buffer); cached_texture = gf_fopen(szExtractName, "wb"); if (cached_texture) { gf_fwrite(ct->data, 1, ct->data_len, cached_texture); gf_fclose(cached_texture); } /*and write cache info*/ if (ct->expirationDate!=0) { sprintf(section, "@cache=%p", ct); gf_cfg_set_key(txh->compositor->user->config, section, "serviceURL", src_url); gf_cfg_set_key(txh->compositor->user->config, section, "cacheFile", szExtractName); gf_cfg_set_key(txh->compositor->user->config, section, "cacheName", ct->cacheURL.buffer); if (ct->expirationDate>0) { char exp[50]; u32 sec, frac; gf_net_get_ntp(&sec, &frac); sec += ct->expirationDate; sprintf(exp, "%u", sec); gf_cfg_set_key(txh->compositor->user->config, section, "expireAfterNTP", exp); } else { gf_cfg_set_key(txh->compositor->user->config, section, "expireAfterNTP", "0"); } } } /*done with image, destroy buffer*/ if (ct->data) gf_free(ct->data); ct->data = NULL; ct->data_len = 0; } } }
GF_EXPORT void gf_sc_texture_update_frame(GF_TextureHandler *txh, Bool disable_resync) { u32 size, ts; /*already refreshed*/ if (txh->needs_refresh) return; if (!txh->stream) { txh->data = NULL; return; } /*should never happen!!*/ if (txh->needs_release) gf_mo_release_data(txh->stream, 0xFFFFFFFF, 0); /*check init flag*/ if (!(gf_mo_get_flags(txh->stream) & GF_MO_IS_INIT)) { /*if we had a texture this means the object has changed - delete texture and resetup. Do not skip texture update as this may lead to an empty rendering pass (blank frame for this object), especially in DASH*/ if (txh->tx_io) { gf_sc_texture_release(txh); txh->data = NULL; txh->needs_refresh = 1; } if (gf_mo_is_private_media(txh->stream)) { setup_texture_object(txh, 1); gf_node_dirty_set(txh->owner, 0, 0); } } txh->data = gf_mo_fetch_data(txh->stream, !disable_resync, &txh->stream_finished, &ts, &size); /*if no frame or muted don't draw*/ if (!txh->data || !size) { GF_LOG(GF_LOG_INFO, GF_LOG_CODEC, ("No output frame available\n")); /*TODO - check if this is needed */ if (txh->flags & GF_SR_TEXTURE_PRIVATE_MEDIA) { //txh->needs_refresh = 1; gf_sc_invalidate(txh->compositor, NULL); } return; } /*if setup and same frame return*/ if (txh->tx_io && (txh->stream_finished || (txh->last_frame_time==ts)) ) { gf_mo_release_data(txh->stream, 0xFFFFFFFF, 0); txh->needs_release = 0; return; } txh->needs_release = 1; txh->last_frame_time = ts; if (gf_mo_is_muted(txh->stream)) return; if (!txh->tx_io) { setup_texture_object(txh, 0); } /*try to push texture on graphics but don't complain if failure*/ gf_sc_texture_set_data(txh); txh->needs_refresh = 1; gf_sc_invalidate(txh->compositor, NULL); }
/* This is a crude draft implementation of filter. The main drawback is that we don't cache any data. We should be able to check for changes in the sub-group or in the filter */ void svg_draw_filter(GF_Node *filter, GF_Node *node, GF_TraverseState *tr_state) { GF_IRect rc1, rc2; #ifndef GPAC_DISABLE_3D u32 type_3d; #endif u32 prev_flags; GF_IRect txrc; Fixed scale_x, scale_y, temp_x, temp_y; DrawableContext *ctx, *child_ctx; GF_SURFACE offscreen_surface, old_surf; GF_Rect bounds, local_bounds, rc; GF_Matrix2D backup; SVGAllAttributes all_atts; GF_FilterStack *st = gf_node_get_private(filter); assert(tr_state->traversing_mode==TRAVERSE_SORT); /*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); gf_node_allow_cyclic_traverse(node); tr_state->traversing_mode = TRAVERSE_GET_BOUNDS; tr_state->bounds.width = tr_state->bounds.height = 0; gf_node_traverse(node, tr_state); local_bounds = bounds = tr_state->bounds; /*compute bounds in final coordinate system - this ensures that the cache has the correct anti aliasing*/ gf_mx2d_apply_rect(&backup, &bounds); txrc = gf_rect_pixelize(&bounds); if (txrc.width%2) txrc.width++; if (txrc.height%2) txrc.height++; bounds = gf_rect_ft(&txrc); tr_state->traversing_mode = TRAVERSE_SORT; gf_mx2d_copy(tr_state->transform, backup); if (!bounds.width || !bounds.height) { return; } /*create a context */ ctx = drawable_init_context_svg(st->drawable, tr_state); if (!ctx) return; /*setup texture */ st->txh.height = txrc.height; st->txh.width = txrc.width; st->txh.stride = txrc.width * 4; st->txh.pixelformat = GF_PIXEL_ARGB; #ifndef GPAC_DISABLE_3D if (tr_state->visual->type_3d) st->txh.pixelformat = GF_PIXEL_RGBA; #endif st->txh.transparent = 1; if (st->txh.stride * st->txh.height > st->alloc_size) { st->alloc_size = st->txh.stride * st->txh.height; st->data = (u8*)gf_realloc(st->data, sizeof(u8) * st->alloc_size); } memset(st->data, 0x0, sizeof(char) * st->txh.stride * st->txh.height); st->txh.data = (char *) st->data; /*setup geometry (rectangle matching the bounds of the object) Warning, we want to center the cached bitmap at the center of the screen (main visual)*/ gf_path_reset(st->drawable->path); gf_path_add_rect_center(st->drawable->path, bounds.x + bounds.width/2, bounds.y - bounds.height/2, bounds.width, bounds.height); old_surf = tr_state->visual->raster_surface; offscreen_surface = tr_state->visual->compositor->rasterizer->surface_new(tr_state->visual->compositor->rasterizer, tr_state->visual->center_coords); tr_state->visual->raster_surface = offscreen_surface; gf_mx2d_copy(backup, tr_state->transform); gf_mx2d_init(tr_state->transform); /*attach the buffer to visual*/ tr_state->visual->compositor->rasterizer->surface_attach_to_buffer(offscreen_surface, st->txh.data, st->txh.width, st->txh.height, 0, st->txh.stride, st->txh.pixelformat); prev_flags = tr_state->immediate_draw; tr_state->immediate_draw = 1; tr_state->traversing_mode = TRAVERSE_SORT; tr_state->in_svg_filter = 1; /*recompute the bounds with the final scaling used*/ scale_x = gf_divfix(bounds.width, local_bounds.width); scale_y = gf_divfix(bounds.height, local_bounds.height); gf_mx2d_init(tr_state->transform); gf_mx2d_add_scale(&tr_state->transform, scale_x, scale_y); rc = local_bounds; gf_mx2d_apply_rect(&tr_state->transform, &rc); /*centered the bitmap on the visual*/ if (tr_state->visual->center_coords) { temp_x = -rc.x - rc.width/2; temp_y = rc.height/2 - rc.y; } else { temp_x = -rc.x; temp_y = rc.height - rc.y; } gf_mx2d_add_translation(&tr_state->transform, temp_x, temp_y); rc1 = tr_state->visual->surf_rect; rc2 = tr_state->visual->top_clipper; tr_state->visual->surf_rect.width = st->txh.width; tr_state->visual->surf_rect.height = st->txh.height; if (tr_state->visual->center_coords) { tr_state->visual->surf_rect.y = st->txh.height/2; tr_state->visual->surf_rect.x = -1 * (s32) st->txh.width/2; } else { tr_state->visual->surf_rect.y = st->txh.height; tr_state->visual->surf_rect.x = 0; } tr_state->visual->top_clipper = tr_state->visual->surf_rect; #ifndef GPAC_DISABLE_3D type_3d = tr_state->visual->type_3d; tr_state->visual->type_3d=0; #endif if (prev_flags) gf_node_allow_cyclic_traverse(node); gf_node_traverse(node, tr_state); child_ctx = 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; } tr_state->visual->cur_context = ctx; /*restore state and destroy whatever needs to be cleaned*/ tr_state->in_svg_filter = 0; tr_state->immediate_draw = prev_flags; tr_state->visual->compositor->rasterizer->surface_delete(offscreen_surface); tr_state->visual->raster_surface = old_surf; tr_state->traversing_mode = TRAVERSE_SORT; tr_state->visual->surf_rect = rc1; tr_state->visual->top_clipper = rc2; #ifndef GPAC_DISABLE_3D tr_state->visual->type_3d = type_3d ; #endif /*update texture*/ st->txh.transparent = 1; st->txh.flags |= GF_SR_TEXTURE_NO_GL_FLIP; gf_sc_texture_set_data(&st->txh); #ifndef GPAC_DISABLE_3D if (tr_state->visual->type_3d) gf_sc_texture_push_image(&st->txh, 0, 0); else #endif gf_sc_texture_push_image(&st->txh, 0, 1); ctx->flags |= CTX_NO_ANTIALIAS; ctx->aspect.fill_color = 0; ctx->aspect.line_color = 0xFF000000; ctx->aspect.fill_texture = &st->txh; ctx->flags |= CTX_TEXTURE_DIRTY; /*get the filter region*/ bounds = local_bounds; gf_svg_flatten_attributes((SVG_Element *)filter, &all_atts); if (!all_atts.filterUnits || (*all_atts.filterUnits==SVG_GRADIENTUNITS_OBJECT)) { Fixed v; v = all_atts.x ? all_atts.x->value : INT2FIX(-10); bounds.x += gf_mulfix(v, bounds.width); v = all_atts.y ? all_atts.y->value : INT2FIX(-10); bounds.y += gf_mulfix(v, bounds.height); v = all_atts.width ? all_atts.width->value : INT2FIX(120); bounds.width = gf_mulfix(v, bounds.width); v = all_atts.height ? all_atts.height->value : INT2FIX(120); bounds.height = gf_mulfix(v, bounds.height); } else { bounds.x = all_atts.x ? all_atts.x->value : 0; bounds.y = all_atts.y ? all_atts.y->value : 0; bounds.width = all_atts.width ? all_atts.x->value : bounds.width; bounds.height = all_atts.x ? all_atts.x->value : 120; } gf_mx2d_apply_rect(&backup, &bounds); svg_filter_apply(filter, &st->txh, &bounds); #ifndef GPAC_DISABLE_3D if (tr_state->visual->type_3d) { if (!st->drawable->mesh) { st->drawable->mesh = new_mesh(); mesh_from_path(st->drawable->mesh, st->drawable->path); } visual_3d_draw_from_context(tr_state->ctx, tr_state); ctx->drawable = NULL; return; } #endif /*we computed the texture in final screen coordinate, so use the identity matrix for the context*/ gf_mx2d_init(tr_state->transform); drawable_finalize_sort(ctx, tr_state, NULL); gf_mx2d_copy(tr_state->transform, backup); }
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); }