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 
movietexture_destroy(GF_Node * node,void * rs,Bool is_destroy)53 static void movietexture_destroy(GF_Node *node, void *rs, Bool is_destroy)
54 {
55 	if (is_destroy) {
56 		MovieTextureStack *st = (MovieTextureStack *) gf_node_get_private(node);
57 		gf_sc_texture_destroy(&st->txh);
58 		if (st->time_handle.is_registered) gf_sc_unregister_time_node(st->txh.compositor, &st->time_handle);
59 		gf_free(st);
60 	}
61 }
movietexture_get_speed(MovieTextureStack * stack,M_MovieTexture * mt)62 static Fixed movietexture_get_speed(MovieTextureStack *stack, M_MovieTexture *mt)
63 {
64 	return gf_mo_get_speed(stack->txh.stream, mt->speed);
65 }
movietexture_get_loop(MovieTextureStack * stack,M_MovieTexture * mt)66 static Bool movietexture_get_loop(MovieTextureStack *stack, M_MovieTexture *mt)
67 {
68 	return gf_mo_get_loop(stack->txh.stream, mt->loop);
69 }
movietexture_activate(MovieTextureStack * stack,M_MovieTexture * mt,Double scene_time)70 static void movietexture_activate(MovieTextureStack *stack, M_MovieTexture *mt, Double scene_time)
71 {
72 	mt->isActive = 1;
73 	gf_node_event_out((GF_Node*)mt, 8/*"isActive"*/);
74 	if (!stack->txh.is_open) {
75 		scene_time -= mt->startTime;
76 		gf_sc_texture_play_from_to(&stack->txh, &mt->url, scene_time, -1, gf_mo_get_loop(stack->txh.stream, mt->loop), 0);
77 	} else if (stack->first_frame_fetched) {
78 		gf_mo_resume(stack->txh.stream);
79 	}
80 	gf_mo_set_speed(stack->txh.stream, mt->speed);
81 }
movietexture_deactivate(MovieTextureStack * stack,M_MovieTexture * mt)82 static void movietexture_deactivate(MovieTextureStack *stack, M_MovieTexture *mt)
83 {
84 	mt->isActive = 0;
85 	gf_node_event_out((GF_Node*)mt, 8/*"isActive"*/);
86 	stack->time_handle.needs_unregister = 1;
87 
88 	if (stack->txh.is_open) {
89 		gf_sc_texture_stop_no_unregister(&stack->txh);
90 	}
91 }
movietexture_update(GF_TextureHandler * txh)92 static void movietexture_update(GF_TextureHandler *txh)
93 {
94 	M_MovieTexture *txnode = (M_MovieTexture *) txh->owner;
95 	MovieTextureStack *st = (MovieTextureStack *) gf_node_get_private(txh->owner);
96 
97 	/*setup texture if needed*/
98 	if (!txh->is_open) return;
99 	if (!txnode->isActive && st->first_frame_fetched) return;
100 
101 	/*when fetching the first frame disable resync*/
102 	gf_sc_texture_update_frame(txh, 0);
103 
104 	if (txh->stream_finished) {
105 		if (movietexture_get_loop(st, txnode)) {
106 			gf_sc_texture_restart(txh);
107 		}
108 		/*if active deactivate*/
109 		else if (txnode->isActive && gf_mo_should_deactivate(st->txh.stream) ) {
110 			movietexture_deactivate(st, txnode);
111 		}
112 	}
113 	/*first frame is fetched*/
114 	if (!st->first_frame_fetched && (txh->needs_refresh) ) {
115 		st->first_frame_fetched = 1;
116 		txnode->duration_changed = gf_mo_get_duration(txh->stream);
117 		gf_node_event_out(txh->owner, 7/*"duration_changed"*/);
118 		/*stop stream if needed*/
119 		if (!txnode->isActive && txh->is_open) {
120 			gf_mo_pause(txh->stream);
121 			/*make sure the refresh flag is not cleared*/
122 			txh->needs_refresh = 1;
123 			gf_sc_invalidate(txh->compositor, NULL);
124 		}
125 	}
126 	if (txh->needs_refresh) {
127 		/*mark all subtrees using this image as dirty*/
128 		gf_node_dirty_parents(txh->owner);
129 	}
130 }
131 
movietexture_update_time(GF_TimeNode * st)132 static void movietexture_update_time(GF_TimeNode *st)
133 {
134 	Double time;
135 	M_MovieTexture *mt = (M_MovieTexture *)st->udta;
136 	MovieTextureStack *stack = (MovieTextureStack *)gf_node_get_private(st->udta);
137 
138 	/*not active, store start time and speed*/
139 	if ( ! mt->isActive) {
140 		stack->start_time = mt->startTime;
141 	}
142 	time = gf_node_get_scene_time(st->udta);
143 
144 	if (time < stack->start_time ||
145 	        /*special case if we're getting active AFTER stoptime */
146 	        (!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 		if (stack->fetch_first_frame) {
151 			stack->fetch_first_frame = 0;
152 			if (!stack->txh.is_open)
153 				gf_sc_texture_play(&stack->txh, &mt->url);
154 			else
155 				gf_mo_resume(stack->txh.stream);
156 		}
157 		return;
158 	}
159 
160 	if (movietexture_get_speed(stack, mt) && mt->isActive) {
161 		/*if stoptime is reached (>startTime) deactivate*/
162 		if ((mt->stopTime > stack->start_time) && (time >= mt->stopTime) ) {
163 			movietexture_deactivate(stack, mt);
164 			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 	if (! mt->isActive) movietexture_activate(stack, mt, time);
173 	stack->txh.stream_finished = GF_FALSE;
174 	if (!mt->url.count)
175 		stack->txh.stream_finished = GF_TRUE;
176 }
177 
compositor_init_movietexture(GF_Compositor * compositor,GF_Node * node)178 void compositor_init_movietexture(GF_Compositor *compositor, GF_Node *node)
179 {
180 	MovieTextureStack *st;
181 	GF_SAFEALLOC(st, MovieTextureStack);
182 	if (!st) {
183 		GF_LOG(GF_LOG_ERROR, GF_LOG_COMPOSE, ("[Compositor] Failed to allocate movie texture stack\n"));
184 		return;
185 	}
186 	gf_sc_texture_setup(&st->txh, compositor, node);
187 	st->txh.update_texture_fcnt = movietexture_update;
188 	st->time_handle.UpdateTimeNode = movietexture_update_time;
189 	st->time_handle.udta = node;
190 	st->fetch_first_frame = 1;
191 	st->txh.flags = 0;
192 	if (((M_MovieTexture*)node)->repeatS) st->txh.flags |= GF_SR_TEXTURE_REPEAT_S;
193 	if (((M_MovieTexture*)node)->repeatT) st->txh.flags |= GF_SR_TEXTURE_REPEAT_T;
194 
195 #ifndef GPAC_DISABLE_X3D
196 	st->is_x3d = (gf_node_get_tag(node)==TAG_X3D_MovieTexture) ? 1 : 0;
197 #endif
198 
199 	gf_node_set_private(node, st);
200 	gf_node_set_callback_function(node, movietexture_destroy);
201 
202 	gf_sc_register_time_node(compositor, &st->time_handle);
203 }
204 
mt_get_texture(GF_Node * node)205 GF_TextureHandler *mt_get_texture(GF_Node *node)
206 {
207 	MovieTextureStack *st = (MovieTextureStack *) gf_node_get_private(node);
208 	return &st->txh;
209 }
210 
compositor_movietexture_modified(GF_Node * node)211 void compositor_movietexture_modified(GF_Node *node)
212 {
213 	M_MovieTexture *mt = (M_MovieTexture *)node;
214 	MovieTextureStack *st = (MovieTextureStack *) gf_node_get_private(node);
215 	if (!st) return;
216 
217 	/*if open and changed, stop and play*/
218 	if (gf_sc_texture_check_url_change(&st->txh, &mt->url)) {
219 		if (st->txh.is_open) gf_sc_texture_stop(&st->txh);
220 		if (mt->isActive) gf_sc_texture_play(&st->txh, &mt->url);
221 	}
222 	/*update state if we're active*/
223 	else if (mt->isActive) {
224 		movietexture_update_time(&st->time_handle);
225 		if (!mt->isActive) return;
226 	}
227 	/*reregister if needed*/
228 	st->time_handle.needs_unregister = 0;
229 	if (!st->time_handle.is_registered) gf_sc_register_time_node(st->txh.compositor, &st->time_handle);
230 }
231 
imagetexture_destroy(GF_Node * node,void * rs,Bool is_destroy)232 static void imagetexture_destroy(GF_Node *node, void *rs, Bool is_destroy)
233 {
234 	if (is_destroy) {
235 		GF_TextureHandler *txh = (GF_TextureHandler *) gf_node_get_private(node);
236 
237 		/*cleanup cache if needed*/
238 		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 			file = gf_opts_get_key(section, "cacheFile");
246 			opt = gf_opts_get_key(section, "expireAfterNTP");
247 
248 			if (opt) {
249 				u32 sec, frac, exp;
250 				sscanf(opt, "%u", &exp);
251 				gf_net_get_ntp(&sec, &frac);
252 				if (!exp || (exp>sec)) delete_file=GF_FALSE;
253 			}
254 			if (delete_file) {
255 				if (file) gf_file_delete((char*)file);
256 				gf_opts_del_section(section);
257 			}
258 
259 			if (txh->data) gf_free(txh->data);
260 			txh->data = NULL;
261 		}
262 		gf_sc_texture_destroy(txh);
263 		gf_free(txh);
264 	}
265 }
266 
imagetexture_update(GF_TextureHandler * txh)267 static void imagetexture_update(GF_TextureHandler *txh)
268 {
269 	if (gf_node_get_tag(txh->owner)!=TAG_MPEG4_CacheTexture) {
270 		MFURL url = ((M_ImageTexture *) txh->owner)->url;
271 
272 		/*setup texture if needed*/
273 		if (!txh->is_open && url.count) {
274 			gf_sc_texture_play(txh, &url);
275 		}
276 		gf_sc_texture_update_frame(txh, 0);
277 
278 		if (
279 		    /*URL is present but not opened - redraw till fetch*/
280 		    /* (txh->stream && !txh->tx_io) && */
281 		    /*image has been updated*/
282 		    txh->needs_refresh) {
283 			/*mark all subtrees using this image as dirty*/
284 			gf_node_dirty_parents(txh->owner);
285 			gf_sc_invalidate(txh->compositor, NULL);
286 		}
287 		return;
288 	}
289 	/*cache texture case*/
290 	else {
291 		M_CacheTexture *ct = (M_CacheTexture *) txh->owner;
292 
293 		/*decode cacheTexture data */
294 		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 			if (ct->image.buffer) {
301 				char *par = (char *) gf_scene_get_service_url( gf_node_get_graph(txh->owner ) );
302 				char *src_url = gf_url_concatenate(par, ct->image.buffer);
303 
304 				if (ct->data) gf_free(ct->data);
305 				ct->data = NULL;
306 				ct->data_len = 0;
307 
308 				e = gf_file_load_data(src_url ? src_url : ct->image.buffer, &ct->data, &ct->data_len);
309 				if (e) {
310 					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 				if (ct->image.buffer) gf_free(ct->image.buffer);
313 				ct->image.buffer = NULL;
314 				if (src_url) gf_free(src_url);
315 			}
316 
317 			/*BIFS decoded playback*/
318 			switch (ct->objectTypeIndication) {
319 			case GF_CODECID_JPEG:
320 				out_size = 0;
321 				e = gf_img_jpeg_dec(ct->data, ct->data_len, &txh->width, &txh->height, &txh->pixelformat, NULL, &out_size, 3);
322 				if (e==GF_BUFFER_TOO_SMALL) {
323 					u32 BPP;
324 					txh->data = gf_malloc(sizeof(char) * out_size);
325 					if (txh->pixelformat==GF_PIXEL_GREYSCALE) BPP = 1;
326 					else BPP = 3;
327 
328 					e = gf_img_jpeg_dec(ct->data, ct->data_len, &txh->width, &txh->height, &txh->pixelformat, txh->data, &out_size, BPP);
329 					if (e==GF_OK) {
330 						gf_sc_texture_allocate(txh);
331 						gf_sc_texture_set_data(txh);
332 						txh->needs_refresh = 1;
333 						txh->stride = out_size / txh->height;
334 					}
335 				}
336 				break;
337 			case GF_CODECID_PNG:
338 				out_size = 0;
339 				e = gf_img_png_dec(ct->data, ct->data_len, &txh->width, &txh->height, &txh->pixelformat, NULL, &out_size);
340 				if (e==GF_BUFFER_TOO_SMALL) {
341 					txh->data = gf_malloc(sizeof(char) * out_size);
342 					e = gf_img_png_dec(ct->data, ct->data_len, &txh->width, &txh->height, &txh->pixelformat, txh->data, &out_size);
343 					if (e==GF_OK) {
344 						gf_sc_texture_allocate(txh);
345 						gf_sc_texture_set_data(txh);
346 						txh->needs_refresh = 1;
347 						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 			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 				opt = (char *) gf_opts_get_key("core", "cache");
362 				if (opt) {
363 					strcpy(szExtractName, opt);
364 				} else {
365 					strcpy(szExtractName, gf_get_default_cache_directory());
366 				}
367 				strcat(szExtractName, "/");
368 				src_url = (char *) gf_scene_get_service_url( gf_node_get_graph(txh->owner ) );
369 
370 				gf_sha1_csum((u8 *)src_url, (u32) strlen(src_url), hash);
371 				for (i=0; i<20; i++) {
372 					char t[3];
373 					t[2] = 0;
374 					sprintf(t, "%02X", hash[i]);
375 					strcat(szExtractName, t);
376 				}
377 				strcat(szExtractName, "_");
378 
379 				strcat(szExtractName, ct->cacheURL.buffer);
380 				cached_texture = gf_fopen(szExtractName, "wb");
381 				if (cached_texture) {
382 					gf_fwrite(ct->data, ct->data_len, cached_texture);
383 					gf_fclose(cached_texture);
384 				}
385 
386 				/*and write cache info*/
387 				if (ct->expirationDate!=0) {
388 					char section[64];
389 					sprintf(section, "@cache=%p", ct);
390 					gf_opts_set_key(section, "serviceURL", src_url);
391 					gf_opts_set_key(section, "cacheFile", szExtractName);
392 					gf_opts_set_key(section, "cacheName", ct->cacheURL.buffer);
393 
394 					if (ct->expirationDate>0) {
395 						char exp[50];
396 						u32 sec, frac;
397 						gf_net_get_ntp(&sec, &frac);
398 						sec += ct->expirationDate;
399 						sprintf(exp, "%u", sec);
400 						gf_opts_set_key(section, "expireAfterNTP", exp);
401 					} else {
402 						gf_opts_set_key(section, "expireAfterNTP", "0");
403 					}
404 				}
405 			}
406 
407 			/*done with image, destroy buffer*/
408 			if (ct->data) gf_free(ct->data);
409 			ct->data = NULL;
410 			ct->data_len = 0;
411 		}
412 	}
413 }
414 
compositor_init_imagetexture(GF_Compositor * compositor,GF_Node * node)415 void compositor_init_imagetexture(GF_Compositor *compositor, GF_Node *node)
416 {
417 	GF_TextureHandler *txh;
418 	GF_SAFEALLOC(txh, GF_TextureHandler);
419 	if (!txh) {
420 		GF_LOG(GF_LOG_ERROR, GF_LOG_COMPOSE, ("[Compositor] Failed to allocate image texture stack\n"));
421 		return;
422 	}
423 	gf_sc_texture_setup(txh, compositor, node);
424 	txh->update_texture_fcnt = imagetexture_update;
425 	gf_node_set_private(node, txh);
426 	gf_node_set_callback_function(node, imagetexture_destroy);
427 	txh->flags = 0;
428 
429 	if (gf_node_get_tag(txh->owner)!=TAG_MPEG4_CacheTexture) {
430 		if (((M_ImageTexture*)node)->repeatS) txh->flags |= GF_SR_TEXTURE_REPEAT_S;
431 		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 		if (!ct->image.buffer) return;
437 
438 		if (ct->repeatS) txh->flags |= GF_SR_TEXTURE_REPEAT_S;
439 		if (ct->repeatT) txh->flags |= GF_SR_TEXTURE_REPEAT_T;
440 
441 		/*locate existing cache*/
442 		url = gf_scene_get_service_url( gf_node_get_graph(node) );
443 		count = gf_opts_get_section_count();
444 		for (i=0; i<count; i++) {
445 			const char *opt;
446 			const char *name = gf_opts_get_section_name(i);
447 			if (strncmp(name, "@cache=", 7)) continue;
448 			opt = gf_opts_get_key(name, "serviceURL");
449 			if (!opt || stricmp(opt, url)) continue;
450 			opt = gf_opts_get_key(name, "cacheName");
451 			if (opt && ct->cacheURL.buffer && !stricmp(opt, ct->cacheURL.buffer)) {
452 				opt = gf_opts_get_key(name, "cacheFile");
453 				if (opt) gf_file_delete((char*)opt);
454 				gf_opts_del_section(name);
455 				break;
456 			}
457 		}
458 
459 	}
460 }
461 
it_get_texture(GF_Node * node)462 GF_TextureHandler *it_get_texture(GF_Node *node)
463 {
464 	return gf_node_get_private(node);
465 }
compositor_imagetexture_modified(GF_Node * node)466 void compositor_imagetexture_modified(GF_Node *node)
467 {
468 	MFURL url;
469 	SFURL sfurl;
470 	GF_TextureHandler *txh = (GF_TextureHandler *) gf_node_get_private(node);
471 	if (!txh) return;
472 
473 	if (gf_node_get_tag(node)!=TAG_MPEG4_CacheTexture) {
474 		url = ((M_ImageTexture *) node)->url;
475 	} else {
476 		url.count = 1;
477 		sfurl.OD_ID=GF_MEDIA_EXTERNAL_ID;
478 		sfurl.url = ((M_CacheTexture *) node)->image.buffer;
479 		url.vals = &sfurl;
480 	}
481 
482 	/*if open and changed, stop and play*/
483 	if (txh->is_open) {
484 		if (! gf_sc_texture_check_url_change(txh, &url)) return;
485 		gf_sc_texture_stop(txh);
486 		gf_sc_texture_play(txh, &url);
487 		return;
488 	}
489 	/*if not open and changed play*/
490 	if (url.count)
491 		gf_sc_texture_play(txh, &url);
492 }
493 
494 
495 
496 typedef struct
497 {
498 	GF_TextureHandler txh;
499 	char *pixels;
500 } PixelTextureStack;
501 
pixeltexture_destroy(GF_Node * node,void * rs,Bool is_destroy)502 static void pixeltexture_destroy(GF_Node *node, void *rs, Bool is_destroy)
503 {
504 	if (is_destroy) {
505 		PixelTextureStack *st = (PixelTextureStack *) gf_node_get_private(node);
506 		if (st->pixels) gf_free(st->pixels);
507 		gf_sc_texture_destroy(&st->txh);
508 		gf_free(st);
509 	}
510 }
pixeltexture_update(GF_TextureHandler * txh)511 static void pixeltexture_update(GF_TextureHandler *txh)
512 {
513 	u32 pix_format, stride, i;
514 	M_PixelTexture *pt = (M_PixelTexture *) txh->owner;
515 	PixelTextureStack *st = (PixelTextureStack *) gf_node_get_private(txh->owner);
516 
517 	if (!gf_node_dirty_get(txh->owner)) return;
518 	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 carefull not to use media funtcions*/
523 	txh->transparent = 0;
524 	stride = pt->image.width;
525 	/*num_components are as in VRML (1->4) not as in BIFS bitstream (0->3)*/
526 	switch (pt->image.numComponents) {
527 	case 1:
528 		pix_format = GF_PIXEL_GREYSCALE;
529 		break;
530 	case 2:
531 		pix_format = GF_PIXEL_ALPHAGREY;
532 		txh->transparent = 1;
533 		stride *= 2;
534 		break;
535 	case 3:
536 		pix_format = GF_PIXEL_RGB;
537 		txh->transparent = 0;
538 		stride *= 3;
539 		break;
540 	case 4:
541 		pix_format = GF_PIXEL_RGBA;
542 		txh->transparent = 1;
543 		stride *= 4;
544 		break;
545 	default:
546 		return;
547 	}
548 
549 	if (!txh->tx_io) {
550 		gf_sc_texture_allocate(txh);
551 		if (!txh->tx_io) return;
552 	}
553 
554 	if (st->pixels) gf_free(st->pixels);
555 	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 	for (i=0; i<pt->image.height; i++) {
564 		memcpy(st->pixels + i * stride, pt->image.pixels + (pt->image.height - 1 - i) * stride, stride);
565 	}
566 #endif
567 
568 	txh->width = pt->image.width;
569 	txh->height = pt->image.height;
570 	txh->stride = stride;
571 	txh->pixelformat = pix_format;
572 	txh->data = st->pixels;
573 
574 	gf_sc_texture_set_data(txh);
575 }
576 
compositor_init_pixeltexture(GF_Compositor * compositor,GF_Node * node)577 void compositor_init_pixeltexture(GF_Compositor *compositor, GF_Node *node)
578 {
579 	PixelTextureStack *st;
580 	GF_SAFEALLOC(st, PixelTextureStack);
581 	if (!st) {
582 		GF_LOG(GF_LOG_ERROR, GF_LOG_COMPOSE, ("[Compositor] Failed to allocate pixel texture stack\n"));
583 		return;
584 	}
585 	gf_sc_texture_setup(&st->txh, compositor, node);
586 	st->pixels = NULL;
587 	st->txh.update_texture_fcnt = pixeltexture_update;
588 
589 	gf_node_set_private(node, st);
590 	gf_node_set_callback_function(node, pixeltexture_destroy);
591 	st->txh.flags = 0;
592 	if (((M_PixelTexture*)node)->repeatS) st->txh.flags |= GF_SR_TEXTURE_REPEAT_S;
593 	if (((M_PixelTexture*)node)->repeatT) st->txh.flags |= GF_SR_TEXTURE_REPEAT_T;
594 }
595 
pt_get_texture(GF_Node * node)596 GF_TextureHandler *pt_get_texture(GF_Node *node)
597 {
598 	PixelTextureStack *st = (PixelTextureStack *) gf_node_get_private(node);
599 	return &st->txh;
600 }
601 
matte_update(GF_TextureHandler * txh)602 static void matte_update(GF_TextureHandler *txh)
603 {
604 	/*nothing to do*/
605 }
606 
compositor_init_mattetexture(GF_Compositor * compositor,GF_Node * node)607 void compositor_init_mattetexture(GF_Compositor *compositor, GF_Node *node)
608 {
609 	GF_TextureHandler *txh;
610 	GF_SAFEALLOC(txh, GF_TextureHandler);
611 	if (!txh) {
612 		GF_LOG(GF_LOG_ERROR, GF_LOG_COMPOSE, ("[Compositor] Failed to allocate matte texture stack\n"));
613 		return;
614 	}
615 	gf_sc_texture_setup(txh, compositor, node);
616 	txh->flags = GF_SR_TEXTURE_MATTE;
617 	txh->update_texture_fcnt = matte_update;
618 	gf_node_set_private(node, txh);
619 	gf_node_set_callback_function(node, imagetexture_destroy);
620 }
621 
gf_sc_mo_destroyed(GF_Node * n)622 void gf_sc_mo_destroyed(GF_Node *n)
623 {
624 	void *st = gf_node_get_private(n);
625 	if (!st) return;
626 
627 	switch (gf_node_get_tag(n)) {
628 	case TAG_MPEG4_Background2D:
629 		((Background2DStack *)st)->txh.stream = NULL;
630 		break;
631 	case TAG_MPEG4_Background:
632 	case TAG_X3D_Background:
633 #ifndef GPAC_DISABLE_3D
634 		((BackgroundStack *)st)->txh_back.stream = NULL;
635 		((BackgroundStack *)st)->txh_front.stream = NULL;
636 		((BackgroundStack *)st)->txh_left.stream = NULL;
637 		((BackgroundStack *)st)->txh_right.stream = NULL;
638 		((BackgroundStack *)st)->txh_top.stream = NULL;
639 		((BackgroundStack *)st)->txh_bottom.stream = NULL;
640 #endif
641 		break;
642 	case TAG_MPEG4_ImageTexture:
643 	case TAG_X3D_ImageTexture:
644 		((GF_TextureHandler *)st)->stream = NULL;
645 		break;
646 	case TAG_MPEG4_MovieTexture:
647 	case TAG_X3D_MovieTexture:
648 		((MovieTextureStack *)st)->txh.stream = NULL;
649 		break;
650 	case TAG_MPEG4_MediaSensor:
651 		((MediaSensorStack *)st)->stream = NULL;
652 		break;
653 	case TAG_MPEG4_MediaControl:
654 		((MediaControlStack *)st)->stream = NULL;
655 		break;
656 	case TAG_MPEG4_AudioSource:
657 		((AudioSourceStack *)st)->input.stream = NULL;
658 		break;
659 	case TAG_MPEG4_AudioClip:
660 	case TAG_X3D_AudioClip:
661 		((AudioClipStack *)st)->input.stream = NULL;
662 		break;
663 #ifndef GPAC_DISABLE_SVG
664 	case TAG_SVG_video:
665 	case TAG_SVG_image:
666 		((SVG_video_stack *)st)->txh.stream = NULL;
667 		break;
668 	case TAG_SVG_audio:
669 		((SVG_audio_stack *)st)->input.stream = NULL;
670 		break;
671 #endif
672 	default:
673 		break;
674 	}
675 }
676 
677 #ifdef __cplusplus
678 }
679 #endif
680 #endif /*GPAC_DISABLE_VRML*/
681