Line data Source code
1 : /*
2 : * GPAC - Multimedia Framework C SDK
3 : *
4 : * Authors: Jean Le Feuvre
5 : * Copyright (c) Telecom ParisTech 2000-2020
6 : * All rights reserved
7 : *
8 : * This file is part of GPAC / Scene Compositor sub-project
9 : *
10 : * GPAC is free software; you can redistribute it and/or modify
11 : * it under the terms of the GNU Lesser General Public License as published by
12 : * the Free Software Foundation; either version 2, or (at your option)
13 : * any later version.
14 : *
15 : * GPAC is distributed in the hope that it will be useful,
16 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 : * GNU Lesser General Public License for more details.
19 : *
20 : * You should have received a copy of the GNU Lesser General Public
21 : * License along with this library; see the file COPYING. If not, write to
22 : * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
23 : *
24 : */
25 :
26 : #include "texturing.h"
27 :
28 : #include <gpac/network.h>
29 : #include <gpac/nodes_mpeg4.h>
30 : #include <gpac/nodes_x3d.h>
31 :
32 : #ifndef GPAC_DISABLE_VRML
33 :
34 : #ifdef __cplusplus
35 : extern "C" {
36 : #endif
37 :
38 : /*for cache texture decode and hash*/
39 : #include <gpac/avparse.h>
40 : #include <gpac/crypt.h>
41 : #include "nodes_stacks.h"
42 :
43 :
44 : typedef struct
45 : {
46 : GF_TextureHandler txh;
47 :
48 : GF_TimeNode time_handle;
49 : Bool fetch_first_frame, first_frame_fetched, is_x3d;
50 : Double start_time;
51 : } MovieTextureStack;
52 :
53 251 : static void movietexture_destroy(GF_Node *node, void *rs, Bool is_destroy)
54 : {
55 251 : if (is_destroy) {
56 251 : MovieTextureStack *st = (MovieTextureStack *) gf_node_get_private(node);
57 251 : gf_sc_texture_destroy(&st->txh);
58 251 : if (st->time_handle.is_registered) gf_sc_unregister_time_node(st->txh.compositor, &st->time_handle);
59 251 : gf_free(st);
60 : }
61 251 : }
62 : static Fixed movietexture_get_speed(MovieTextureStack *stack, M_MovieTexture *mt)
63 : {
64 7812 : return gf_mo_get_speed(stack->txh.stream, mt->speed);
65 : }
66 : static Bool movietexture_get_loop(MovieTextureStack *stack, M_MovieTexture *mt)
67 : {
68 2088 : return gf_mo_get_loop(stack->txh.stream, mt->loop);
69 : }
70 223 : static void movietexture_activate(MovieTextureStack *stack, M_MovieTexture *mt, Double scene_time)
71 : {
72 223 : mt->isActive = 1;
73 223 : gf_node_event_out((GF_Node*)mt, 8/*"isActive"*/);
74 223 : if (!stack->txh.is_open) {
75 216 : scene_time -= mt->startTime;
76 216 : gf_sc_texture_play_from_to(&stack->txh, &mt->url, scene_time, -1, gf_mo_get_loop(stack->txh.stream, mt->loop), 0);
77 7 : } else if (stack->first_frame_fetched) {
78 1 : gf_mo_resume(stack->txh.stream);
79 : }
80 223 : gf_mo_set_speed(stack->txh.stream, mt->speed);
81 223 : }
82 1 : static void movietexture_deactivate(MovieTextureStack *stack, M_MovieTexture *mt)
83 : {
84 1 : mt->isActive = 0;
85 1 : gf_node_event_out((GF_Node*)mt, 8/*"isActive"*/);
86 1 : stack->time_handle.needs_unregister = 1;
87 :
88 1 : if (stack->txh.is_open) {
89 1 : gf_sc_texture_stop_no_unregister(&stack->txh);
90 : }
91 1 : }
92 8379 : static void movietexture_update(GF_TextureHandler *txh)
93 : {
94 8379 : M_MovieTexture *txnode = (M_MovieTexture *) txh->owner;
95 8379 : MovieTextureStack *st = (MovieTextureStack *) gf_node_get_private(txh->owner);
96 :
97 : /*setup texture if needed*/
98 8379 : if (!txh->is_open) return;
99 8347 : if (!txnode->isActive && st->first_frame_fetched) return;
100 :
101 : /*when fetching the first frame disable resync*/
102 8255 : gf_sc_texture_update_frame(txh, 0);
103 :
104 8255 : if (txh->stream_finished) {
105 4176 : if (movietexture_get_loop(st, txnode)) {
106 0 : gf_sc_texture_restart(txh);
107 : }
108 : /*if active deactivate*/
109 2088 : else if (txnode->isActive && (txnode->startTime<txnode->stopTime) && gf_mo_should_deactivate(st->txh.stream) ) {
110 0 : movietexture_deactivate(st, txnode);
111 : }
112 : }
113 : /*first frame is fetched*/
114 8255 : if (!st->first_frame_fetched && (txh->needs_refresh) ) {
115 210 : st->first_frame_fetched = 1;
116 210 : txnode->duration_changed = gf_mo_get_duration(txh->stream);
117 210 : gf_node_event_out(txh->owner, 7/*"duration_changed"*/);
118 : /*stop stream if needed*/
119 210 : if (!txnode->isActive && txh->is_open) {
120 1 : gf_mo_pause(txh->stream);
121 : /*make sure the refresh flag is not cleared*/
122 1 : txh->needs_refresh = 1;
123 1 : gf_sc_invalidate(txh->compositor, NULL);
124 : }
125 : }
126 8255 : if (txh->needs_refresh) {
127 : /*mark all subtrees using this image as dirty*/
128 4202 : gf_node_dirty_parents(txh->owner);
129 : }
130 : }
131 :
132 8348 : static void movietexture_update_time(GF_TimeNode *st)
133 : {
134 : Double time;
135 8348 : M_MovieTexture *mt = (M_MovieTexture *)st->udta;
136 8348 : MovieTextureStack *stack = (MovieTextureStack *)gf_node_get_private(st->udta);
137 :
138 : /*not active, store start time and speed*/
139 8348 : if ( ! mt->isActive) {
140 759 : stack->start_time = mt->startTime;
141 : }
142 8348 : time = gf_node_get_scene_time(st->udta);
143 :
144 16160 : if (time < stack->start_time ||
145 : /*special case if we're getting active AFTER stoptime */
146 8035 : (!mt->isActive && (mt->stopTime > stack->start_time) && (time>=mt->stopTime))
147 : // || (!stack->start_time && !stack->is_x3d && !mt->loop)
148 : ) {
149 : /*opens stream only at first access to fetch first frame*/
150 536 : if (stack->fetch_first_frame) {
151 7 : stack->fetch_first_frame = 0;
152 7 : if (!stack->txh.is_open)
153 7 : gf_sc_texture_play(&stack->txh, &mt->url);
154 : else
155 0 : gf_mo_resume(stack->txh.stream);
156 : }
157 : return;
158 : }
159 :
160 15624 : if (movietexture_get_speed(stack, mt) && mt->isActive) {
161 : /*if stoptime is reached (>startTime) deactivate*/
162 7585 : if ((mt->stopTime > stack->start_time) && (time >= mt->stopTime) ) {
163 1 : movietexture_deactivate(stack, mt);
164 1 : return;
165 : }
166 : }
167 :
168 : /*we're (about to be) active: VRML:
169 : "A time-dependent node is inactive until its startTime is reached. When time now becomes greater than or
170 : equal to startTime, an isActive TRUE event is generated and the time-dependent node becomes active */
171 :
172 7811 : if (! mt->isActive) movietexture_activate(stack, mt, time);
173 7811 : stack->txh.stream_finished = GF_FALSE;
174 7811 : if (!mt->url.count)
175 1919 : stack->txh.stream_finished = GF_TRUE;
176 : }
177 :
178 251 : void compositor_init_movietexture(GF_Compositor *compositor, GF_Node *node)
179 : {
180 : MovieTextureStack *st;
181 251 : GF_SAFEALLOC(st, MovieTextureStack);
182 251 : if (!st) {
183 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_COMPOSE, ("[Compositor] Failed to allocate movie texture stack\n"));
184 : return;
185 : }
186 251 : gf_sc_texture_setup(&st->txh, compositor, node);
187 251 : st->txh.update_texture_fcnt = movietexture_update;
188 251 : st->time_handle.UpdateTimeNode = movietexture_update_time;
189 251 : st->time_handle.udta = node;
190 251 : st->fetch_first_frame = 1;
191 251 : st->txh.flags = 0;
192 251 : if (((M_MovieTexture*)node)->repeatS) st->txh.flags |= GF_SR_TEXTURE_REPEAT_S;
193 251 : if (((M_MovieTexture*)node)->repeatT) st->txh.flags |= GF_SR_TEXTURE_REPEAT_T;
194 :
195 : #ifndef GPAC_DISABLE_X3D
196 251 : st->is_x3d = (gf_node_get_tag(node)==TAG_X3D_MovieTexture) ? 1 : 0;
197 : #endif
198 :
199 251 : gf_node_set_private(node, st);
200 251 : gf_node_set_callback_function(node, movietexture_destroy);
201 :
202 251 : gf_sc_register_time_node(compositor, &st->time_handle);
203 : }
204 :
205 16423 : GF_TextureHandler *mt_get_texture(GF_Node *node)
206 : {
207 16423 : MovieTextureStack *st = (MovieTextureStack *) gf_node_get_private(node);
208 16423 : return &st->txh;
209 : }
210 :
211 206 : void compositor_movietexture_modified(GF_Node *node)
212 : {
213 : M_MovieTexture *mt = (M_MovieTexture *)node;
214 206 : MovieTextureStack *st = (MovieTextureStack *) gf_node_get_private(node);
215 206 : if (!st) return;
216 :
217 : /*if open and changed, stop and play*/
218 206 : if (gf_sc_texture_check_url_change(&st->txh, &mt->url)) {
219 204 : if (st->txh.is_open) gf_sc_texture_stop(&st->txh);
220 204 : if (mt->isActive) gf_sc_texture_play(&st->txh, &mt->url);
221 : }
222 : /*update state if we're active*/
223 2 : else if (mt->isActive) {
224 1 : movietexture_update_time(&st->time_handle);
225 1 : if (!mt->isActive) return;
226 : }
227 : /*reregister if needed*/
228 205 : st->time_handle.needs_unregister = 0;
229 205 : if (!st->time_handle.is_registered) gf_sc_register_time_node(st->txh.compositor, &st->time_handle);
230 : }
231 :
232 107 : static void imagetexture_destroy(GF_Node *node, void *rs, Bool is_destroy)
233 : {
234 107 : if (is_destroy) {
235 107 : GF_TextureHandler *txh = (GF_TextureHandler *) gf_node_get_private(node);
236 :
237 : /*cleanup cache if needed*/
238 107 : if (gf_node_get_tag(node)==TAG_MPEG4_CacheTexture) {
239 : char section[64];
240 : const char *opt, *file;
241 : Bool delete_file = GF_TRUE;
242 : M_CacheTexture *ct = (M_CacheTexture*)node;
243 :
244 : sprintf(section, "@cache=%p", ct);
245 8 : file = gf_opts_get_key(section, "cacheFile");
246 8 : opt = gf_opts_get_key(section, "expireAfterNTP");
247 :
248 8 : if (opt) {
249 : u32 sec, frac, exp;
250 1 : sscanf(opt, "%u", &exp);
251 1 : gf_net_get_ntp(&sec, &frac);
252 1 : if (!exp || (exp>sec)) delete_file=GF_FALSE;
253 : }
254 : if (delete_file) {
255 7 : if (file) gf_file_delete((char*)file);
256 7 : gf_opts_del_section(section);
257 : }
258 :
259 8 : if (txh->data) gf_free(txh->data);
260 8 : txh->data = NULL;
261 : }
262 107 : gf_sc_texture_destroy(txh);
263 107 : gf_free(txh);
264 : }
265 107 : }
266 :
267 7651 : static void imagetexture_update(GF_TextureHandler *txh)
268 : {
269 7651 : if (gf_node_get_tag(txh->owner)!=TAG_MPEG4_CacheTexture) {
270 7057 : MFURL url = ((M_ImageTexture *) txh->owner)->url;
271 :
272 : /*setup texture if needed*/
273 7057 : if (!txh->is_open && url.count) {
274 92 : gf_sc_texture_play(txh, &url);
275 : }
276 7057 : gf_sc_texture_update_frame(txh, 0);
277 :
278 7057 : if (
279 : /*URL is present but not opened - redraw till fetch*/
280 : /* (txh->stream && !txh->tx_io) && */
281 : /*image has been updated*/
282 7057 : txh->needs_refresh) {
283 : /*mark all subtrees using this image as dirty*/
284 99 : gf_node_dirty_parents(txh->owner);
285 99 : gf_sc_invalidate(txh->compositor, NULL);
286 : }
287 : return;
288 : }
289 : /*cache texture case*/
290 : else {
291 594 : M_CacheTexture *ct = (M_CacheTexture *) txh->owner;
292 :
293 : /*decode cacheTexture data */
294 594 : if ((ct->data || ct->image.buffer) && !txh->data) {
295 : #ifndef GPAC_DISABLE_AV_PARSERS
296 : u32 out_size;
297 : GF_Err e;
298 :
299 : /*BT/XMT playback: load to memory*/
300 2 : if (ct->image.buffer) {
301 2 : char *par = (char *) gf_scene_get_service_url( gf_node_get_graph(txh->owner ) );
302 2 : char *src_url = gf_url_concatenate(par, ct->image.buffer);
303 :
304 2 : if (ct->data) gf_free(ct->data);
305 2 : ct->data = NULL;
306 2 : ct->data_len = 0;
307 :
308 2 : e = gf_file_load_data(src_url ? src_url : ct->image.buffer, &ct->data, &ct->data_len);
309 2 : if (e) {
310 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_COMPOSE, ("[Compositor] Failed to load CacheTexture data from file %s: %s\n", src_url ? src_url : ct->image.buffer, gf_error_to_string(e) ) );
311 : }
312 2 : if (ct->image.buffer) gf_free(ct->image.buffer);
313 2 : ct->image.buffer = NULL;
314 2 : if (src_url) gf_free(src_url);
315 : }
316 :
317 : /*BIFS decoded playback*/
318 2 : switch (ct->objectTypeIndication) {
319 2 : case GF_CODECID_JPEG:
320 2 : out_size = 0;
321 2 : e = gf_img_jpeg_dec(ct->data, ct->data_len, &txh->width, &txh->height, &txh->pixelformat, NULL, &out_size, 3);
322 2 : if (e==GF_BUFFER_TOO_SMALL) {
323 : u32 BPP;
324 2 : txh->data = gf_malloc(sizeof(char) * out_size);
325 2 : if (txh->pixelformat==GF_PIXEL_GREYSCALE) BPP = 1;
326 : else BPP = 3;
327 :
328 2 : e = gf_img_jpeg_dec(ct->data, ct->data_len, &txh->width, &txh->height, &txh->pixelformat, txh->data, &out_size, BPP);
329 2 : if (e==GF_OK) {
330 2 : gf_sc_texture_allocate(txh);
331 2 : gf_sc_texture_set_data(txh);
332 2 : txh->needs_refresh = 1;
333 2 : txh->stride = out_size / txh->height;
334 : }
335 : }
336 : break;
337 0 : case GF_CODECID_PNG:
338 0 : out_size = 0;
339 0 : e = gf_img_png_dec(ct->data, ct->data_len, &txh->width, &txh->height, &txh->pixelformat, NULL, &out_size);
340 0 : if (e==GF_BUFFER_TOO_SMALL) {
341 0 : txh->data = gf_malloc(sizeof(char) * out_size);
342 0 : e = gf_img_png_dec(ct->data, ct->data_len, &txh->width, &txh->height, &txh->pixelformat, txh->data, &out_size);
343 0 : if (e==GF_OK) {
344 0 : gf_sc_texture_allocate(txh);
345 0 : gf_sc_texture_set_data(txh);
346 0 : txh->needs_refresh = 1;
347 0 : txh->stride = out_size / txh->height;
348 : }
349 : }
350 : break;
351 : }
352 :
353 : #endif // GPAC_DISABLE_AV_PARSERS
354 :
355 : /*cacheURL is specified, store the image*/
356 2 : if (ct->cacheURL.buffer) {
357 : u32 i;
358 : u8 hash[20];
359 : FILE *cached_texture;
360 : char szExtractName[GF_MAX_PATH], *opt, *src_url;
361 1 : opt = (char *) gf_opts_get_key("core", "cache");
362 1 : if (opt) {
363 : strcpy(szExtractName, opt);
364 : } else {
365 0 : strcpy(szExtractName, gf_get_default_cache_directory());
366 : }
367 : strcat(szExtractName, "/");
368 1 : src_url = (char *) gf_scene_get_service_url( gf_node_get_graph(txh->owner ) );
369 :
370 1 : gf_sha1_csum((u8 *)src_url, (u32) strlen(src_url), hash);
371 21 : for (i=0; i<20; i++) {
372 : char t[3];
373 20 : t[2] = 0;
374 20 : sprintf(t, "%02X", hash[i]);
375 : strcat(szExtractName, t);
376 : }
377 : strcat(szExtractName, "_");
378 :
379 1 : strcat(szExtractName, ct->cacheURL.buffer);
380 1 : cached_texture = gf_fopen(szExtractName, "wb");
381 1 : if (cached_texture) {
382 1 : gf_fwrite(ct->data, ct->data_len, cached_texture);
383 1 : gf_fclose(cached_texture);
384 : }
385 :
386 : /*and write cache info*/
387 1 : if (ct->expirationDate!=0) {
388 : char section[64];
389 : sprintf(section, "@cache=%p", ct);
390 1 : gf_opts_set_key(section, "serviceURL", src_url);
391 1 : gf_opts_set_key(section, "cacheFile", szExtractName);
392 1 : gf_opts_set_key(section, "cacheName", ct->cacheURL.buffer);
393 :
394 1 : if (ct->expirationDate>0) {
395 : char exp[50];
396 : u32 sec, frac;
397 1 : gf_net_get_ntp(&sec, &frac);
398 1 : sec += ct->expirationDate;
399 : sprintf(exp, "%u", sec);
400 1 : gf_opts_set_key(section, "expireAfterNTP", exp);
401 : } else {
402 0 : gf_opts_set_key(section, "expireAfterNTP", "0");
403 : }
404 : }
405 : }
406 :
407 : /*done with image, destroy buffer*/
408 2 : if (ct->data) gf_free(ct->data);
409 2 : ct->data = NULL;
410 2 : ct->data_len = 0;
411 : }
412 : }
413 : }
414 :
415 107 : void compositor_init_imagetexture(GF_Compositor *compositor, GF_Node *node)
416 : {
417 : GF_TextureHandler *txh;
418 107 : GF_SAFEALLOC(txh, GF_TextureHandler);
419 107 : if (!txh) {
420 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_COMPOSE, ("[Compositor] Failed to allocate image texture stack\n"));
421 : return;
422 : }
423 107 : gf_sc_texture_setup(txh, compositor, node);
424 107 : txh->update_texture_fcnt = imagetexture_update;
425 107 : gf_node_set_private(node, txh);
426 107 : gf_node_set_callback_function(node, imagetexture_destroy);
427 107 : txh->flags = 0;
428 :
429 107 : if (gf_node_get_tag(txh->owner)!=TAG_MPEG4_CacheTexture) {
430 99 : if (((M_ImageTexture*)node)->repeatS) txh->flags |= GF_SR_TEXTURE_REPEAT_S;
431 99 : if (((M_ImageTexture*)node)->repeatT) txh->flags |= GF_SR_TEXTURE_REPEAT_T;
432 : } else {
433 : const char *url;
434 : u32 i, count;
435 : M_CacheTexture*ct = (M_CacheTexture*)node;
436 8 : if (!ct->image.buffer) return;
437 :
438 2 : if (ct->repeatS) txh->flags |= GF_SR_TEXTURE_REPEAT_S;
439 2 : if (ct->repeatT) txh->flags |= GF_SR_TEXTURE_REPEAT_T;
440 :
441 : /*locate existing cache*/
442 2 : url = gf_scene_get_service_url( gf_node_get_graph(node) );
443 2 : count = gf_opts_get_section_count();
444 19 : for (i=0; i<count; i++) {
445 : const char *opt;
446 17 : const char *name = gf_opts_get_section_name(i);
447 17 : if (strncmp(name, "@cache=", 7)) continue;
448 0 : opt = gf_opts_get_key(name, "serviceURL");
449 0 : if (!opt || stricmp(opt, url)) continue;
450 0 : opt = gf_opts_get_key(name, "cacheName");
451 0 : if (opt && ct->cacheURL.buffer && !stricmp(opt, ct->cacheURL.buffer)) {
452 0 : opt = gf_opts_get_key(name, "cacheFile");
453 0 : if (opt) gf_file_delete((char*)opt);
454 0 : gf_opts_del_section(name);
455 0 : break;
456 : }
457 : }
458 :
459 : }
460 : }
461 :
462 19730 : GF_TextureHandler *it_get_texture(GF_Node *node)
463 : {
464 19730 : return gf_node_get_private(node);
465 : }
466 14 : void compositor_imagetexture_modified(GF_Node *node)
467 : {
468 : MFURL url;
469 : SFURL sfurl;
470 14 : GF_TextureHandler *txh = (GF_TextureHandler *) gf_node_get_private(node);
471 27 : if (!txh) return;
472 :
473 14 : if (gf_node_get_tag(node)!=TAG_MPEG4_CacheTexture) {
474 14 : url = ((M_ImageTexture *) node)->url;
475 : } else {
476 0 : url.count = 1;
477 0 : sfurl.OD_ID=GF_MEDIA_EXTERNAL_ID;
478 0 : sfurl.url = ((M_CacheTexture *) node)->image.buffer;
479 0 : url.vals = &sfurl;
480 : }
481 :
482 : /*if open and changed, stop and play*/
483 14 : if (txh->is_open) {
484 13 : if (! gf_sc_texture_check_url_change(txh, &url)) return;
485 13 : gf_sc_texture_stop(txh);
486 13 : gf_sc_texture_play(txh, &url);
487 13 : return;
488 : }
489 : /*if not open and changed play*/
490 1 : if (url.count)
491 1 : gf_sc_texture_play(txh, &url);
492 : }
493 :
494 :
495 :
496 : typedef struct
497 : {
498 : GF_TextureHandler txh;
499 : char *pixels;
500 : } PixelTextureStack;
501 :
502 10 : static void pixeltexture_destroy(GF_Node *node, void *rs, Bool is_destroy)
503 : {
504 10 : if (is_destroy) {
505 10 : PixelTextureStack *st = (PixelTextureStack *) gf_node_get_private(node);
506 10 : if (st->pixels) gf_free(st->pixels);
507 10 : gf_sc_texture_destroy(&st->txh);
508 10 : gf_free(st);
509 : }
510 10 : }
511 1145 : static void pixeltexture_update(GF_TextureHandler *txh)
512 : {
513 : u32 pix_format, stride, i;
514 1145 : M_PixelTexture *pt = (M_PixelTexture *) txh->owner;
515 1145 : PixelTextureStack *st = (PixelTextureStack *) gf_node_get_private(txh->owner);
516 :
517 1145 : if (!gf_node_dirty_get(txh->owner)) return;
518 16 : gf_node_dirty_clear(txh->owner, 0);
519 :
520 :
521 : /*pixel texture doesn not use any media object but has data in the content.
522 : However we still use the same texture object, just be careful not to use media funtcions*/
523 16 : txh->transparent = 0;
524 16 : stride = pt->image.width;
525 : /*num_components are as in VRML (1->4) not as in BIFS bitstream (0->3)*/
526 16 : switch (pt->image.numComponents) {
527 : case 1:
528 : pix_format = GF_PIXEL_GREYSCALE;
529 : break;
530 1 : case 2:
531 : pix_format = GF_PIXEL_ALPHAGREY;
532 1 : txh->transparent = 1;
533 1 : stride *= 2;
534 1 : break;
535 1 : case 3:
536 : pix_format = GF_PIXEL_RGB;
537 : txh->transparent = 0;
538 1 : stride *= 3;
539 1 : break;
540 1 : case 4:
541 : pix_format = GF_PIXEL_RGBA;
542 1 : txh->transparent = 1;
543 1 : stride *= 4;
544 1 : break;
545 : default:
546 : return;
547 : }
548 :
549 16 : if (!txh->tx_io) {
550 10 : gf_sc_texture_allocate(txh);
551 10 : if (!txh->tx_io) return;
552 : }
553 :
554 16 : if (st->pixels) gf_free(st->pixels);
555 16 : st->pixels = (char*)gf_malloc(sizeof(char) * stride * pt->image.height);
556 : /*FIXME FOR OPENGL !!*/
557 : #if 0
558 : for (i=0; i<pt->image.height; i++) {
559 : memcpy(st->pixels + i * stride, pt->image.pixels + i * stride, stride);
560 : }
561 : #else
562 : /*revert pixel ordering...*/
563 80 : for (i=0; i<pt->image.height; i++) {
564 64 : memcpy(st->pixels + i * stride, pt->image.pixels + (pt->image.height - 1 - i) * stride, stride);
565 : }
566 : #endif
567 :
568 16 : txh->width = pt->image.width;
569 16 : txh->height = pt->image.height;
570 16 : txh->stride = stride;
571 16 : txh->pixelformat = pix_format;
572 16 : txh->data = st->pixels;
573 :
574 16 : gf_sc_texture_set_data(txh);
575 : }
576 :
577 10 : void compositor_init_pixeltexture(GF_Compositor *compositor, GF_Node *node)
578 : {
579 : PixelTextureStack *st;
580 10 : GF_SAFEALLOC(st, PixelTextureStack);
581 10 : if (!st) {
582 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_COMPOSE, ("[Compositor] Failed to allocate pixel texture stack\n"));
583 : return;
584 : }
585 10 : gf_sc_texture_setup(&st->txh, compositor, node);
586 10 : st->pixels = NULL;
587 10 : st->txh.update_texture_fcnt = pixeltexture_update;
588 :
589 10 : gf_node_set_private(node, st);
590 10 : gf_node_set_callback_function(node, pixeltexture_destroy);
591 10 : st->txh.flags = 0;
592 10 : if (((M_PixelTexture*)node)->repeatS) st->txh.flags |= GF_SR_TEXTURE_REPEAT_S;
593 10 : if (((M_PixelTexture*)node)->repeatT) st->txh.flags |= GF_SR_TEXTURE_REPEAT_T;
594 : }
595 :
596 746 : GF_TextureHandler *pt_get_texture(GF_Node *node)
597 : {
598 746 : PixelTextureStack *st = (PixelTextureStack *) gf_node_get_private(node);
599 746 : return &st->txh;
600 : }
601 :
602 0 : static void matte_update(GF_TextureHandler *txh)
603 : {
604 : /*nothing to do*/
605 0 : }
606 :
607 0 : void compositor_init_mattetexture(GF_Compositor *compositor, GF_Node *node)
608 : {
609 : GF_TextureHandler *txh;
610 0 : GF_SAFEALLOC(txh, GF_TextureHandler);
611 0 : if (!txh) {
612 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_COMPOSE, ("[Compositor] Failed to allocate matte texture stack\n"));
613 : return;
614 : }
615 0 : gf_sc_texture_setup(txh, compositor, node);
616 0 : txh->flags = GF_SR_TEXTURE_MATTE;
617 0 : txh->update_texture_fcnt = matte_update;
618 0 : gf_node_set_private(node, txh);
619 0 : gf_node_set_callback_function(node, imagetexture_destroy);
620 : }
621 :
622 1 : void gf_sc_mo_destroyed(GF_Node *n)
623 : {
624 1 : void *st = gf_node_get_private(n);
625 1 : if (!st) return;
626 :
627 1 : switch (gf_node_get_tag(n)) {
628 0 : case TAG_MPEG4_Background2D:
629 0 : ((Background2DStack *)st)->txh.stream = NULL;
630 0 : break;
631 0 : case TAG_MPEG4_Background:
632 : case TAG_X3D_Background:
633 : #ifndef GPAC_DISABLE_3D
634 0 : ((BackgroundStack *)st)->txh_back.stream = NULL;
635 0 : ((BackgroundStack *)st)->txh_front.stream = NULL;
636 0 : ((BackgroundStack *)st)->txh_left.stream = NULL;
637 0 : ((BackgroundStack *)st)->txh_right.stream = NULL;
638 0 : ((BackgroundStack *)st)->txh_top.stream = NULL;
639 0 : ((BackgroundStack *)st)->txh_bottom.stream = NULL;
640 : #endif
641 0 : break;
642 1 : case TAG_MPEG4_ImageTexture:
643 : case TAG_X3D_ImageTexture:
644 1 : ((GF_TextureHandler *)st)->stream = NULL;
645 1 : break;
646 0 : case TAG_MPEG4_MovieTexture:
647 : case TAG_X3D_MovieTexture:
648 0 : ((MovieTextureStack *)st)->txh.stream = NULL;
649 0 : break;
650 0 : case TAG_MPEG4_MediaSensor:
651 0 : ((MediaSensorStack *)st)->stream = NULL;
652 0 : break;
653 0 : case TAG_MPEG4_MediaControl:
654 0 : ((MediaControlStack *)st)->stream = NULL;
655 0 : break;
656 0 : case TAG_MPEG4_AudioSource:
657 0 : ((AudioSourceStack *)st)->input.stream = NULL;
658 0 : break;
659 0 : case TAG_MPEG4_AudioClip:
660 : case TAG_X3D_AudioClip:
661 0 : ((AudioClipStack *)st)->input.stream = NULL;
662 0 : break;
663 : #ifndef GPAC_DISABLE_SVG
664 0 : case TAG_SVG_video:
665 : case TAG_SVG_image:
666 0 : ((SVG_video_stack *)st)->txh.stream = NULL;
667 0 : break;
668 0 : case TAG_SVG_audio:
669 0 : ((SVG_audio_stack *)st)->input.stream = NULL;
670 0 : break;
671 : #endif
672 : default:
673 : break;
674 : }
675 : }
676 :
677 : #ifdef __cplusplus
678 : }
679 : #endif
680 : #endif /*GPAC_DISABLE_VRML*/
|