1 /*
2  *			GPAC - Multimedia Framework C SDK
3  *
4  *			Authors: Jean Le Feuvre
5  *			Copyright (c) Telecom ParisTech 2000-2019
6  *					All rights reserved
7  *
8  *  This file is part of GPAC / software 2D rasterizer module
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 
27 
28 #include "rast_soft.h"
29 
get_surface_world_matrix(GF_EVGSurface * surf,GF_Matrix2D * mat)30 static void get_surface_world_matrix(GF_EVGSurface *surf, GF_Matrix2D *mat)
31 {
32 	gf_mx2d_init(*mat);
33 	if (surf->center_coords) {
34 		gf_mx2d_add_scale(mat, FIX_ONE, -FIX_ONE);
35 		gf_mx2d_add_translation(mat, INT2FIX(surf->width / 2), INT2FIX(surf->height / 2));
36 	}
37 }
38 
39 GF_EXPORT
gf_evg_surface_new(Bool center_coords)40 GF_EVGSurface *gf_evg_surface_new(Bool center_coords)
41 {
42 	GF_EVGSurface *surf;
43 	GF_SAFEALLOC(surf, GF_EVGSurface);
44 	if (surf) {
45 		surf->center_coords = center_coords;
46 		surf->texture_filter = GF_TEXTURE_FILTER_DEFAULT;
47 		surf->raster = evg_raster_new();
48 		surf->yuv_prof=1;
49 	}
50 	return surf;
51 }
52 
53 EVG_Surface3DExt *evg_init_3d_surface(GF_EVGSurface *surf);
54 
55 GF_EXPORT
gf_evg_surface3d_new()56 GF_EVGSurface *gf_evg_surface3d_new()
57 {
58 	GF_EVGSurface *surf;
59 	GF_SAFEALLOC(surf, GF_EVGSurface);
60 	if (surf) {
61 		surf->texture_filter = GF_TEXTURE_FILTER_DEFAULT;
62 		surf->raster = evg_raster_new();
63 		surf->yuv_prof=1;
64 
65 		surf->ext3d = evg_init_3d_surface(surf);
66 		if (!surf->ext3d) {
67 			gf_free(surf);
68 			return NULL;
69 		}
70 	}
71 	return surf;
72 }
73 
74 GF_EXPORT
gf_evg_surface_set_center_coords(GF_EVGSurface * surf,Bool center_coords)75 void gf_evg_surface_set_center_coords(GF_EVGSurface *surf, Bool center_coords)
76 {
77 	if (surf) surf->center_coords = center_coords;
78 }
79 
80 GF_EXPORT
gf_evg_surface_delete(GF_EVGSurface * surf)81 void gf_evg_surface_delete(GF_EVGSurface *surf)
82 {
83 	if (!surf)
84 		return;
85 	if (surf->stencil_pix_run) gf_free(surf->stencil_pix_run);
86 	surf->stencil_pix_run = NULL;
87 	if (surf->raster) evg_raster_del(surf->raster);
88 	surf->raster = NULL;
89 	if (surf->uv_alpha) gf_free(surf->uv_alpha);
90 
91 	if (surf->ext3d) {
92 		if (surf->ext3d->pix_vals)
93 			gf_free(surf->ext3d->pix_vals);
94 		gf_free(surf->ext3d);
95 	}
96 	gf_free(surf);
97 }
98 
99 GF_EXPORT
gf_evg_surface_set_matrix(GF_EVGSurface * surf,GF_Matrix2D * mat)100 GF_Err gf_evg_surface_set_matrix(GF_EVGSurface *surf, GF_Matrix2D *mat)
101 {
102 	GF_Matrix2D tmp;
103 	if (!surf) return GF_BAD_PARAM;
104 	get_surface_world_matrix(surf, &surf->mat);
105 	surf->is_3d_matrix = GF_FALSE;
106 	if (!mat) return GF_OK;
107 
108 	gf_mx2d_init(tmp);
109 	gf_mx2d_add_matrix(&tmp, mat);
110 	gf_mx2d_add_matrix(&tmp, &surf->mat);
111 	gf_mx2d_copy(surf->mat, tmp);
112 	return GF_OK;
113 }
114 
115 GF_EXPORT
gf_evg_surface_set_matrix_3d(GF_EVGSurface * surf,GF_Matrix * mat)116 GF_Err gf_evg_surface_set_matrix_3d(GF_EVGSurface *surf, GF_Matrix *mat)
117 {
118 	if (!surf) return GF_BAD_PARAM;
119 	get_surface_world_matrix(surf, &surf->mat);
120 	surf->is_3d_matrix = GF_FALSE;
121 	if (!mat) return GF_OK;
122 	gf_mx_copy(surf->mx3d, *mat);
123 	surf->is_3d_matrix = GF_TRUE;
124 	return GF_OK;
125 }
126 
evg_surface_set_components_idx(GF_EVGSurface * surf)127 static void evg_surface_set_components_idx(GF_EVGSurface *surf)
128 {
129 	surf->idx_y1=surf->idx_u=surf->idx_v=0;
130 	surf->idx_a=surf->idx_r=surf->idx_g=surf->idx_b=0;
131 	switch (surf->pixelFormat) {
132 	case GF_PIXEL_YUYV:
133 		surf->idx_y1=0;
134 		surf->idx_u=1;
135 		surf->idx_v=3;
136 		break;
137 	case GF_PIXEL_YVYU:
138 		surf->idx_y1=0;
139 		surf->idx_u=3;
140 		surf->idx_v=1;
141 		break;
142 	case GF_PIXEL_UYVY:
143 		surf->idx_y1=1;
144 		surf->idx_u=0;
145 		surf->idx_v=2;
146 		break;
147 	case GF_PIXEL_VYUY:
148 		surf->idx_y1=1;
149 		surf->idx_u=2;
150 		surf->idx_v=0;
151 		break;
152 	case GF_PIXEL_ALPHAGREY:
153 		surf->idx_a = 0;
154 		surf->idx_g = 1;
155 		break;
156 	case GF_PIXEL_GREYALPHA:
157 		surf->idx_a = 1;
158 		surf->idx_g = 0;
159 		break;
160 	case GF_PIXEL_ARGB:
161 		surf->idx_a=0;
162 		surf->idx_r=1;
163 		surf->idx_g=2;
164 		surf->idx_b=3;
165 		break;
166 	case GF_PIXEL_RGBA:
167 		surf->idx_a=3;
168 		surf->idx_r=0;
169 		surf->idx_g=1;
170 		surf->idx_b=2;
171 		break;
172 	case GF_PIXEL_BGRA:
173 		surf->idx_a=3;
174 		surf->idx_r=2;
175 		surf->idx_g=1;
176 		surf->idx_b=0;
177 		break;
178 	case GF_PIXEL_ABGR:
179 		surf->idx_a=0;
180 		surf->idx_r=3;
181 		surf->idx_g=2;
182 		surf->idx_b=1;
183 		break;
184 	case GF_PIXEL_RGBX:
185 		surf->idx_r=0;
186 		surf->idx_g=1;
187 		surf->idx_b=2;
188 		surf->idx_a=3;
189 		break;
190 	case GF_PIXEL_BGRX:
191 		surf->idx_r=2;
192 		surf->idx_g=1;
193 		surf->idx_b=0;
194 		surf->idx_a=3;
195 		break;
196 	case GF_PIXEL_XRGB:
197 		surf->idx_a=0;
198 		surf->idx_r=1;
199 		surf->idx_g=2;
200 		surf->idx_b=3;
201 		break;
202 	case GF_PIXEL_XBGR:
203 		surf->idx_a=0;
204 		surf->idx_r=3;
205 		surf->idx_g=2;
206 		surf->idx_b=1;
207 		break;
208 	case GF_PIXEL_RGB:
209 		surf->idx_r=0;
210 		surf->idx_g=1;
211 		surf->idx_b=2;
212 		break;
213 	case GF_PIXEL_BGR:
214 		surf->idx_r=2;
215 		surf->idx_g=1;
216 		surf->idx_b=0;
217 		break;
218 	}
219 }
220 
221 GF_Err evg_3d_resize(GF_EVGSurface *surf);
222 
223 
224 GF_EXPORT
gf_evg_surface_attach_to_buffer(GF_EVGSurface * surf,u8 * pixels,u32 width,u32 height,s32 pitch_x,s32 pitch_y,GF_PixelFormat pixelFormat)225 GF_Err gf_evg_surface_attach_to_buffer(GF_EVGSurface *surf, u8 *pixels, u32 width, u32 height, s32 pitch_x, s32 pitch_y, GF_PixelFormat pixelFormat)
226 {
227 	u32 BPP;
228 	Bool size_changed=GF_FALSE;
229 	if (!surf || !pixels) return GF_BAD_PARAM;
230 
231 	surf->is_transparent = GF_FALSE;
232 	surf->not_8bits = GF_FALSE;
233 	switch (pixelFormat) {
234 	case GF_PIXEL_GREYSCALE:
235 		BPP = 1;
236 		break;
237 	case GF_PIXEL_ALPHAGREY:
238 	case GF_PIXEL_GREYALPHA:
239 		BPP = 2;
240 		surf->is_transparent = GF_TRUE;
241 		break;
242 	case GF_PIXEL_RGB_444:
243 	case GF_PIXEL_RGB_555:
244 	case GF_PIXEL_RGB_565:
245 		BPP = 2;
246 		break;
247 	case GF_PIXEL_ARGB:
248 	case GF_PIXEL_BGRA:
249 	case GF_PIXEL_RGBA:
250 	case GF_PIXEL_ABGR:
251 		surf->is_transparent = GF_TRUE;
252 	case GF_PIXEL_RGBX:
253 	case GF_PIXEL_BGRX:
254 	case GF_PIXEL_XRGB:
255 	case GF_PIXEL_XBGR:
256 		BPP = 4;
257 		break;
258 	case GF_PIXEL_BGR:
259 	case GF_PIXEL_RGB:
260 		BPP = 3;
261 		break;
262 	case GF_PIXEL_YUV:
263 	case GF_PIXEL_NV12:
264 	case GF_PIXEL_NV21:
265 	case GF_PIXEL_YUV422:
266 	case GF_PIXEL_YUV444:
267 	case GF_PIXEL_YUYV:
268 	case GF_PIXEL_YVYU:
269 	case GF_PIXEL_UYVY:
270 	case GF_PIXEL_VYUY:
271 		BPP = 1;
272 		break;
273 	case GF_PIXEL_YUV_10:
274 	case GF_PIXEL_NV12_10:
275 	case GF_PIXEL_NV21_10:
276 	case GF_PIXEL_YUV422_10:
277 	case GF_PIXEL_YUV444_10:
278 		BPP = 2;
279 		surf->not_8bits = GF_TRUE;
280 		break;
281 	default:
282 		return GF_NOT_SUPPORTED;
283 	}
284 	if (!pitch_x) pitch_x = BPP;
285 	surf->pitch_x = pitch_x;
286 	surf->pitch_y = pitch_y;
287 	if (!surf->stencil_pix_run || (surf->width != width)) {
288 		u32 run_size = sizeof(u32) * (width+2);
289 		if (surf->not_8bits) run_size *= 2;
290 		if (surf->stencil_pix_run) gf_free(surf->stencil_pix_run);
291 		surf->stencil_pix_run = (u32 *) gf_malloc(run_size);
292 	}
293 	if (surf->width != width) {
294 		surf->width = width;
295 		size_changed = GF_TRUE;
296 	}
297 	if (surf->height != height) {
298 		surf->height = height;
299 		size_changed = GF_TRUE;
300 	}
301 	surf->pixels = (char*)pixels;
302 	surf->pixelFormat = pixelFormat;
303 	surf->BPP = BPP;
304 	evg_surface_set_components_idx(surf);
305 	gf_evg_surface_set_matrix(surf, NULL);
306 	if (surf->ext3d && size_changed)
307 		evg_3d_resize(surf);
308 	return GF_OK;
309 }
310 
311 
312 GF_EXPORT
gf_evg_surface_attach_to_texture(GF_EVGSurface * surf,GF_EVGStencil * sten)313 GF_Err gf_evg_surface_attach_to_texture(GF_EVGSurface *surf, GF_EVGStencil * sten)
314 {
315 	EVG_Texture *tx = (EVG_Texture *) sten;
316 	if (!surf || (tx->type != GF_STENCIL_TEXTURE)) return GF_BAD_PARAM;
317 
318 	return gf_evg_surface_attach_to_buffer(surf, tx->pixels, tx->width, tx->height, 0, tx->stride, tx->pixel_format);
319 }
320 
321 
322 GF_EXPORT
gf_evg_surface_clear(GF_EVGSurface * surf,GF_IRect * rc,u32 color)323 GF_Err gf_evg_surface_clear(GF_EVGSurface *surf, GF_IRect *rc, u32 color)
324 {
325 	GF_IRect clear;
326 	if (!surf) return GF_BAD_PARAM;
327 
328 	if (rc) {
329 		s32 _x, _y;
330 		if (surf->center_coords) {
331 			_x = rc->x + surf->width / 2;
332 			_y = surf->height / 2 - rc->y;
333 		} else {
334 			_x = rc->x;
335 			_y = rc->y - rc->height;
336 		}
337 		/*clip is outside our bounds, ignore*/
338 		if ((_x>=(s32) surf->width) || (_x + rc->width < 0))
339 			return GF_OK;
340 		if ((_y>= (s32) surf->height) || (_y + rc->height < 0))
341 			return GF_OK;
342 
343 		clear.width = (u32) rc->width;
344 		if (_x>=0) {
345 			clear.x = (u32) _x;
346 		} else {
347 			if ( (s32) clear.width + _x < 0) return GF_BAD_PARAM;
348 			clear.width += _x;
349 			clear.x = 0;
350 		}
351 		if (clear.x + clear.width > (s32) surf->width)
352 			clear.width = surf->width - clear.x;
353 
354 		if (!clear.width)
355 			return GF_OK;
356 
357 		clear.height = (u32) rc->height;
358 		if (_y>=0) {
359 			clear.y = _y;
360 		} else {
361 			if ( (s32) clear.height + _y < 0) return GF_BAD_PARAM;
362 			clear.height += _y;
363 			clear.y = 0;
364 		}
365 		if (clear.y + clear.height > (s32) surf->height)
366 			clear.height = surf->height - clear.y;
367 
368 		if (!clear.height)
369 			return GF_OK;
370 	} else {
371 		clear.x = clear.y = 0;
372 		clear.width = surf->width;
373 		clear.height = surf->height;
374 	}
375 
376 	switch (surf->pixelFormat) {
377 	/*RGB formats*/
378 	case GF_PIXEL_GREYSCALE:
379 		return evg_surface_clear_grey(surf, clear, color);
380 	case GF_PIXEL_ALPHAGREY:
381 	case GF_PIXEL_GREYALPHA:
382 		return evg_surface_clear_alphagrey(surf, clear, color);
383 	case GF_PIXEL_ARGB:
384 	case GF_PIXEL_BGRA:
385 	case GF_PIXEL_RGBA:
386 	case GF_PIXEL_ABGR:
387 		return evg_surface_clear_argb(surf, clear, color);
388 
389 	case GF_PIXEL_RGBX:
390 	case GF_PIXEL_BGRX:
391 	case GF_PIXEL_XRGB:
392 	case GF_PIXEL_XBGR:
393 		return evg_surface_clear_rgbx(surf, clear, color);
394 
395 	case GF_PIXEL_RGB:
396 	case GF_PIXEL_BGR:
397 		return evg_surface_clear_rgb(surf, clear, color);
398 	case GF_PIXEL_RGB_444:
399 		return evg_surface_clear_444(surf, clear, color);
400 	case GF_PIXEL_RGB_555:
401 		return evg_surface_clear_555(surf, clear, color);
402 	case GF_PIXEL_RGB_565:
403 		return evg_surface_clear_565(surf, clear, color);
404 
405 	/*YUV formats*/
406 	case GF_PIXEL_YUV:
407 		return evg_surface_clear_yuv420p(surf, clear, color);
408 	case GF_PIXEL_YUV422:
409 		return evg_surface_clear_yuv422p(surf, clear, color);
410 	case GF_PIXEL_NV12:
411 		return evg_surface_clear_nv12(surf, clear, color, GF_FALSE);
412 	case GF_PIXEL_NV21:
413 		return evg_surface_clear_nv12(surf, clear, color, GF_TRUE);
414 	case GF_PIXEL_YUV444:
415 		return evg_surface_clear_yuv444p(surf, clear, color);
416 	case GF_PIXEL_YUYV:
417 	case GF_PIXEL_YVYU:
418 	case GF_PIXEL_UYVY:
419 	case GF_PIXEL_VYUY:
420 		return evg_surface_clear_yuyv(surf, clear, color);
421 	case GF_PIXEL_YUV_10:
422 		return evg_surface_clear_yuv420p_10(surf, clear, color);
423 	case GF_PIXEL_NV12_10:
424 		return evg_surface_clear_nv12_10(surf, clear, color, GF_FALSE);
425 	case GF_PIXEL_NV21_10:
426 		return evg_surface_clear_nv12_10(surf, clear, color, GF_TRUE);
427 	case GF_PIXEL_YUV422_10:
428 		return evg_surface_clear_yuv422p_10(surf, clear, color);
429 	case GF_PIXEL_YUV444_10:
430 		return evg_surface_clear_yuv444p_10(surf, clear, color);
431 	default:
432 		return GF_BAD_PARAM;
433 	}
434 }
435 
436 GF_EXPORT
gf_evg_surface_set_raster_level(GF_EVGSurface * surf,GF_RasterQuality RasterSetting)437 GF_Err gf_evg_surface_set_raster_level(GF_EVGSurface *surf, GF_RasterQuality RasterSetting)
438 {
439 	if (!surf) return GF_BAD_PARAM;
440 	switch (RasterSetting) {
441 	case GF_RASTER_HIGH_QUALITY:
442 		surf->texture_filter = GF_TEXTURE_FILTER_HIGH_QUALITY;
443 		break;
444 	case GF_RASTER_MID:
445 		surf->texture_filter = GF_TEXTURE_FILTER_HIGH_QUALITY;
446 		break;
447 	case GF_RASTER_HIGH_SPEED:
448 	default:
449 		surf->texture_filter = GF_TEXTURE_FILTER_HIGH_SPEED;
450 		break;
451 	}
452 	return GF_OK;
453 }
454 
455 
456 GF_EXPORT
gf_evg_surface_set_clipper(GF_EVGSurface * surf,GF_IRect * rc)457 GF_Err gf_evg_surface_set_clipper(GF_EVGSurface *surf , GF_IRect *rc)
458 {
459 	if (!surf) return GF_BAD_PARAM;
460 	if (rc) {
461 		surf->clipper = *rc;
462 		surf->useClipper = 1;
463 		/*clipper was given in BIFS like coords, we work with bottom-min for rect, (0,0) top-left of surface*/
464 		if (surf->center_coords) {
465 			surf->clipper.x += surf->width / 2;
466 			surf->clipper.y = surf->height / 2 - rc->y;
467 		} else {
468 			surf->clipper.y -= rc->height;
469 		}
470 
471 		if (surf->clipper.x <=0) {
472 			if (surf->clipper.x + (s32) surf->clipper.width < 0) return GF_BAD_PARAM;
473 			surf->clipper.width += surf->clipper.x;
474 			surf->clipper.x = 0;
475 		}
476 		if (surf->clipper.y <=0) {
477 			if (surf->clipper.y + (s32) surf->clipper.height < 0) return GF_BAD_PARAM;
478 			surf->clipper.height += surf->clipper.y;
479 			surf->clipper.y = 0;
480 		}
481 		if (surf->clipper.x + surf->clipper.width > (s32) surf->width) {
482 			surf->clipper.width = surf->width - surf->clipper.x;
483 		}
484 		if (surf->clipper.y + surf->clipper.height > (s32) surf->height) {
485 			surf->clipper.height = surf->height - surf->clipper.y;
486 		}
487 	} else {
488 		surf->useClipper = 0;
489 	}
490 	return GF_OK;
491 }
492 
493 
setup_grey_callback(GF_EVGSurface * surf,Bool for_3d)494 static Bool setup_grey_callback(GF_EVGSurface *surf, Bool for_3d)
495 {
496 	u32 a, uv_alpha_size=0;
497 	Bool use_const = GF_TRUE;
498 
499 	//in 3D mode, we only write one pixel at a time except in YUV modes
500 	if (for_3d) {
501 		a = 1;
502 		use_const = GF_TRUE;
503 	}
504 	else if (surf->sten->type == GF_STENCIL_SOLID) {
505 		surf->fill_col = ((EVG_Brush *)surf->sten)->color;
506 		a = GF_COL_A(surf->fill_col);
507 	} else {
508 		a = 0;
509 		use_const = GF_FALSE;
510 	}
511 
512 	if (use_const && !a && !surf->is_transparent) return GF_FALSE;
513 
514 	surf->is_422 = GF_FALSE;
515 	surf->yuv_type = 0;
516 	surf->swap_uv = GF_FALSE;
517 	surf->yuv_flush_uv = NULL;
518 
519 	if (surf->comp_mode) a=100;
520 	else if (surf->get_alpha) a=100;
521 	surf->fill_single = NULL;
522 	surf->fill_single_a = NULL;
523 
524 	switch (surf->pixelFormat) {
525 	case GF_PIXEL_GREYSCALE:
526 		if (use_const) {
527 			if (a!=0xFF) {
528 				surf->gray_spans = (EVG_Raster_Span_Func) evg_grey_fill_const_a;
529 			} else {
530 				surf->gray_spans = (EVG_Raster_Span_Func) evg_grey_fill_const;
531 			}
532 		} else {
533 			surf->gray_spans = (EVG_Raster_Span_Func) evg_grey_fill_var;
534 		}
535 		if (surf->ext3d) {
536 			surf->fill_single = evg_grey_fill_single;
537 			surf->fill_single_a = evg_grey_fill_single_a;
538 		}
539 		break;
540 	case GF_PIXEL_ALPHAGREY:
541 	case GF_PIXEL_GREYALPHA:
542 		if (use_const) {
543 			if (a!=0xFF) {
544 				surf->gray_spans = (EVG_Raster_Span_Func) evg_alphagrey_fill_const_a;
545 			} else {
546 				surf->gray_spans = (EVG_Raster_Span_Func) evg_alphagrey_fill_const;
547 			}
548 		} else {
549 			surf->gray_spans = (EVG_Raster_Span_Func) evg_alphagrey_fill_var;
550 		}
551 		if (surf->ext3d) {
552 			surf->fill_single = evg_alphagrey_fill_single;
553 			surf->fill_single_a = evg_alphagrey_fill_single_a;
554 		}
555 		break;
556 	case GF_PIXEL_ARGB:
557 	case GF_PIXEL_RGBA:
558 	case GF_PIXEL_BGRA:
559 	case GF_PIXEL_ABGR:
560 		if (use_const) {
561 			if (!a) {
562 				surf->gray_spans = (EVG_Raster_Span_Func) evg_argb_fill_erase;
563 			} else if (a!=0xFF) {
564 				surf->gray_spans = (EVG_Raster_Span_Func) evg_argb_fill_const_a;
565 			} else {
566 				surf->gray_spans = (EVG_Raster_Span_Func) evg_argb_fill_const;
567 			}
568 		} else {
569 			surf->gray_spans = (EVG_Raster_Span_Func) evg_argb_fill_var;
570 		}
571 		if (surf->ext3d) {
572 			surf->fill_single = evg_argb_fill_single;
573 			surf->fill_single_a = evg_argb_fill_single_a;
574 		}
575 		break;
576 
577 	case GF_PIXEL_RGBX:
578 	case GF_PIXEL_BGRX:
579 	case GF_PIXEL_XRGB:
580 	case GF_PIXEL_XBGR:
581 		if (use_const) {
582 			if (a!=0xFF) {
583 				surf->gray_spans = (EVG_Raster_Span_Func) evg_rgbx_fill_const_a;
584 			} else {
585 				surf->gray_spans = (EVG_Raster_Span_Func) evg_rgbx_fill_const;
586 			}
587 		} else {
588 			surf->gray_spans = (EVG_Raster_Span_Func) evg_rgbx_fill_var;
589 		}
590 		if (surf->ext3d) {
591 			surf->fill_single = evg_rgbx_fill_single;
592 			surf->fill_single_a = evg_rgbx_fill_single_a;
593 		}
594 		break;
595 
596 	case GF_PIXEL_RGB:
597 	case GF_PIXEL_BGR:
598 		if (use_const) {
599 			if (a!=0xFF) {
600 				surf->gray_spans = (EVG_Raster_Span_Func) evg_rgb_fill_const_a;
601 			} else {
602 				surf->gray_spans = (EVG_Raster_Span_Func) evg_rgb_fill_const;
603 			}
604 		} else {
605 			surf->gray_spans = (EVG_Raster_Span_Func) evg_rgb_fill_var;
606 		}
607 		if (surf->ext3d) {
608 			surf->fill_single = evg_rgb_fill_single;
609 			surf->fill_single_a = evg_rgb_fill_single_a;
610 		}
611 		break;
612 
613 	case GF_PIXEL_RGB_565:
614 		if (use_const) {
615 			if (a!=0xFF) {
616 				surf->gray_spans = (EVG_Raster_Span_Func) evg_565_fill_const_a;
617 			} else {
618 				surf->gray_spans = (EVG_Raster_Span_Func) evg_565_fill_const;
619 			}
620 		} else {
621 			surf->gray_spans = (EVG_Raster_Span_Func) evg_565_fill_var;
622 			assert(surf->sten->fill_run);
623 		}
624 		if (surf->ext3d) {
625 			surf->fill_single = evg_565_fill_single;
626 			surf->fill_single_a = evg_565_fill_single_a;
627 		}
628 		break;
629 	case GF_PIXEL_RGB_444:
630 		if (use_const) {
631 			if (a!=0xFF) {
632 				surf->gray_spans = (EVG_Raster_Span_Func) evg_444_fill_const_a;
633 			} else {
634 				surf->gray_spans = (EVG_Raster_Span_Func) evg_444_fill_const;
635 			}
636 		} else {
637 			surf->gray_spans = (EVG_Raster_Span_Func) evg_444_fill_var;
638 		}
639 		if (surf->ext3d) {
640 			surf->fill_single = evg_444_fill_single;
641 			surf->fill_single_a = evg_444_fill_single_a;
642 		}
643 		break;
644 	case GF_PIXEL_RGB_555:
645 		if (use_const) {
646 			if (a!=0xFF) {
647 				surf->gray_spans = (EVG_Raster_Span_Func) evg_555_fill_const_a;
648 			} else {
649 				surf->gray_spans = (EVG_Raster_Span_Func) evg_555_fill_const;
650 			}
651 		} else {
652 			surf->gray_spans = (EVG_Raster_Span_Func) evg_555_fill_var;
653 		}
654 		if (surf->ext3d) {
655 			surf->fill_single = evg_555_fill_single;
656 			surf->fill_single_a = evg_555_fill_single_a;
657 		}
658 		break;
659 	case GF_PIXEL_YUV:
660 		surf->yuv_type = EVG_YUV;
661 		if (use_const && !for_3d) {
662 			surf->yuv_flush_uv = evg_yuv420p_flush_uv_const;
663 
664 			if (a!=0xFF) {
665 				surf->gray_spans = (EVG_Raster_Span_Func) evg_yuv420p_fill_const_a;
666 			} else {
667 				surf->gray_spans = (EVG_Raster_Span_Func) evg_yuv420p_fill_const;
668 			}
669 			uv_alpha_size=2*surf->width;
670 		} else {
671 			surf->yuv_flush_uv = evg_yuv420p_flush_uv_var;
672 			surf->gray_spans = (EVG_Raster_Span_Func) evg_yuv420p_fill_var;
673 			uv_alpha_size = 2 * 3*surf->width;
674 			if (for_3d)
675 				surf->sten = &surf->ext3d->yuv_sten;
676 		}
677 		break;
678 	case GF_PIXEL_NV21:
679 		surf->swap_uv = GF_TRUE;
680 	case GF_PIXEL_NV12:
681 		surf->yuv_type = EVG_YUV;
682 
683 		if (surf->swap_uv) {
684 			surf->idx_u = 1;
685 			surf->idx_v = 0;
686 		} else {
687 			surf->idx_u = 0;
688 			surf->idx_v = 1;
689 		}
690 
691 		if (use_const && !for_3d) {
692 			surf->yuv_flush_uv = evg_nv12_flush_uv_const;
693 
694 			if (a!=0xFF) {
695 				surf->gray_spans = (EVG_Raster_Span_Func) evg_yuv420p_fill_const_a;
696 			} else {
697 				surf->gray_spans = (EVG_Raster_Span_Func) evg_yuv420p_fill_const;
698 			}
699 			uv_alpha_size = 2*surf->width;
700 		} else {
701 			surf->yuv_flush_uv = evg_nv12_flush_uv_var;
702 			surf->gray_spans = (EVG_Raster_Span_Func) evg_yuv420p_fill_var;
703 			uv_alpha_size = 2 * 3*surf->width;
704 		}
705 		break;
706 	case GF_PIXEL_YUV422:
707 		surf->yuv_type = EVG_YUV;
708 		surf->is_422 = GF_TRUE;
709 		if (use_const && !for_3d) {
710 			surf->yuv_flush_uv = evg_yuv422p_flush_uv_const;
711 
712 			if (a!=0xFF) {
713 				surf->gray_spans = (EVG_Raster_Span_Func) evg_yuv420p_fill_const_a;
714 			} else {
715 				surf->gray_spans = (EVG_Raster_Span_Func) evg_yuv420p_fill_const;
716 			}
717 			uv_alpha_size = 2*surf->width;
718 		} else {
719 			surf->yuv_flush_uv = evg_yuv422p_flush_uv_var;
720 			surf->gray_spans = (EVG_Raster_Span_Func) evg_yuv420p_fill_var;
721 			uv_alpha_size = 2 * 3*surf->width;
722 		}
723 		break;
724 	case GF_PIXEL_YUV444:
725 		surf->yuv_type = EVG_YUV_444;
726 		if (use_const) {
727 			if (a!=0xFF) {
728 				surf->gray_spans = (EVG_Raster_Span_Func) evg_yuv444p_fill_const_a;
729 			} else {
730 				surf->gray_spans = (EVG_Raster_Span_Func) evg_yuv444p_fill_const;
731 			}
732 		} else {
733 			surf->gray_spans = (EVG_Raster_Span_Func) evg_yuv444p_fill_var;
734 		}
735 		break;
736 	case GF_PIXEL_YUYV:
737 	case GF_PIXEL_YVYU:
738 	case GF_PIXEL_UYVY:
739 	case GF_PIXEL_VYUY:
740 		surf->yuv_type = EVG_YUV;
741 		if (use_const && !for_3d) {
742 			if (a!=0xFF) {
743 				surf->gray_spans = (EVG_Raster_Span_Func) evg_yuyv_fill_const_a;
744 			} else {
745 				surf->gray_spans = (EVG_Raster_Span_Func) evg_yuyv_fill_const;
746 			}
747 			uv_alpha_size = 2*surf->width;
748 		} else {
749 			surf->gray_spans = (EVG_Raster_Span_Func) evg_yuyv_fill_var;
750 			uv_alpha_size = 2 * 3*surf->width;
751 		}
752 		break;
753 
754 	case GF_PIXEL_YUV_10:
755 		surf->yuv_type = EVG_YUV;
756 		if (use_const && !for_3d) {
757 			surf->yuv_flush_uv = evg_yuv420p_10_flush_uv_const;
758 
759 			if (a!=0xFF) {
760 				surf->gray_spans = (EVG_Raster_Span_Func) evg_yuv420p_10_fill_const_a;
761 			} else {
762 				surf->gray_spans = (EVG_Raster_Span_Func) evg_yuv420p_10_fill_const;
763 			}
764 			uv_alpha_size = 4*surf->width;
765 		} else {
766 			surf->yuv_flush_uv = evg_yuv420p_10_flush_uv_var;
767 			surf->gray_spans = (EVG_Raster_Span_Func) evg_yuv420p_10_fill_var;
768 			uv_alpha_size = 4*3*surf->width;
769 		}
770 		break;
771 	case GF_PIXEL_NV21_10:
772 		surf->swap_uv = GF_TRUE;
773 	case GF_PIXEL_NV12_10:
774 		surf->yuv_type = EVG_YUV;
775 
776 		if (surf->swap_uv) {
777 			surf->idx_u = 1;
778 			surf->idx_v = 0;
779 		} else {
780 			surf->idx_u = 0;
781 			surf->idx_v = 1;
782 		}
783 
784 		if (use_const && !for_3d) {
785 			surf->yuv_flush_uv = evg_nv12_10_flush_uv_const;
786 
787 			if (a!=0xFF) {
788 				surf->gray_spans = (EVG_Raster_Span_Func) evg_yuv420p_10_fill_const_a;
789 			} else {
790 				surf->gray_spans = (EVG_Raster_Span_Func) evg_yuv420p_10_fill_const;
791 			}
792 			uv_alpha_size = 4*surf->width;
793 		} else {
794 			surf->yuv_flush_uv = evg_nv12_10_flush_uv_var;
795 			surf->gray_spans = (EVG_Raster_Span_Func) evg_yuv420p_10_fill_var;
796 			uv_alpha_size = 4 * 3*surf->width;
797 		}
798 		break;
799 	case GF_PIXEL_YUV422_10:
800 		surf->yuv_type = EVG_YUV;
801 		surf->is_422 = GF_TRUE;
802 		if (use_const && !for_3d) {
803 			surf->yuv_flush_uv = evg_yuv422p_10_flush_uv_const;
804 
805 			if (a!=0xFF) {
806 				surf->gray_spans = (EVG_Raster_Span_Func) evg_yuv420p_10_fill_const_a;
807 			} else {
808 				surf->gray_spans = (EVG_Raster_Span_Func) evg_yuv420p_10_fill_const;
809 			}
810 			uv_alpha_size = 4*surf->width;
811 		} else {
812 			surf->yuv_flush_uv = evg_yuv422p_10_flush_uv_var;
813 			surf->gray_spans = (EVG_Raster_Span_Func) evg_yuv420p_10_fill_var;
814 			uv_alpha_size = 4 * 3*surf->width;
815 		}
816 		break;
817 	case GF_PIXEL_YUV444_10:
818 		surf->yuv_type = EVG_YUV_444;
819 		if (use_const) {
820 			if (a!=0xFF) {
821 				surf->gray_spans = (EVG_Raster_Span_Func) evg_yuv444p_10_fill_const_a;
822 			} else {
823 				surf->gray_spans = (EVG_Raster_Span_Func) evg_yuv444p_10_fill_const;
824 			}
825 		} else {
826 			surf->gray_spans = (EVG_Raster_Span_Func) evg_yuv444p_10_fill_var;
827 		}
828 		break;
829 	default:
830 		return GF_FALSE;
831 	}
832 
833 	if (uv_alpha_size) {
834 		if (surf->uv_alpha_alloc < uv_alpha_size) {
835 			surf->uv_alpha_alloc = uv_alpha_size;
836 			surf->uv_alpha = gf_realloc(surf->uv_alpha, uv_alpha_size);
837 			memset(surf->uv_alpha, 0, uv_alpha_size);
838 		}
839 		memset(surf->uv_alpha, 0, sizeof(char)*surf->uv_alpha_alloc);
840 	}
841 	if (surf->yuv_type && for_3d)
842 		surf->sten = &surf->ext3d->yuv_sten;
843 
844 	if (use_const && surf->yuv_type) {
845 		u8 y, cb, cr;
846 		gf_evg_rgb_to_yuv(surf, surf->fill_col, &y, &cb, &cr);
847 		if (surf->swap_uv) {
848 			u8 t = cb;
849 			cb = cr;
850 			cr = (u8) t;
851 			surf->swap_uv = GF_FALSE;
852 		}
853 
854 		surf->fill_col = GF_COL_ARGB(a, y, cb, cr);
855 		surf->fill_col_wide = evg_col_to_wide(surf->fill_col);
856 	}
857 
858 	return GF_TRUE;
859 }
860 
861 
862 GF_EXPORT
gf_evg_surface_set_path(GF_EVGSurface * surf,GF_Path * gp)863 GF_Err gf_evg_surface_set_path(GF_EVGSurface *surf, GF_Path *gp)
864 {
865 	if (!surf) return GF_BAD_PARAM;
866 	if (!gp || !gp->n_points) {
867 		surf->ftoutline.n_points = 0;
868 		surf->ftoutline.n_contours = 0;
869 		return GF_OK;
870 	}
871 	gf_path_flatten(gp);
872 	surf->ftoutline.n_points = gp->n_points;
873 	surf->ftoutline.n_contours = gp->n_contours;
874 
875 	surf->ftoutline.tags = gp->tags;
876 	surf->ftoutline.contours = (s32*) gp->contours;
877 
878 	/*store path bounds for gradient/textures*/
879 	gf_path_get_bounds(gp, &surf->path_bounds);
880 	/*invert Y (ft uses min Y)*/
881 	surf->path_bounds.y -= surf->path_bounds.height;
882 
883 	surf->ftoutline.flags = 0;
884 	if (gp->flags & GF_PATH_FILL_ZERO_NONZERO) surf->ftoutline.flags = GF_PATH_FILL_ZERO_NONZERO;
885 
886 	surf->ftoutline.n_points = gp->n_points;
887 	surf->ftoutline.points = gp->points;
888 	surf->mx = &surf->mat;
889 	return GF_OK;
890 }
891 
892 /* static void gray_spans_stub(s32 y, s32 count, EVG_Span *spans, GF_EVGSurface *surf){} */
893 
894 GF_EXPORT
gf_evg_surface_fill(GF_EVGSurface * surf,GF_EVGStencil * sten)895 GF_Err gf_evg_surface_fill(GF_EVGSurface *surf, GF_EVGStencil *sten)
896 {
897 	GF_Err e;
898 	GF_Rect rc;
899 	u32 max_gray;
900 	GF_Matrix2D mat, st_mat;
901 	Bool restore_filter;
902 	if (!surf || !sten) return GF_BAD_PARAM;
903 	if (!surf->ftoutline.n_points) return GF_OK;
904 	surf->sten = sten;
905 
906 	/*setup ft raster calllbacks*/
907 	if (!setup_grey_callback(surf, GF_FALSE)) return GF_OK;
908 
909 	/*	surf->gray_spans = gray_spans_stub; */
910 
911 	/*TODO, check matrix with 3D transform*/
912 
913 	get_surface_world_matrix(surf, &mat);
914 	restore_filter = GF_FALSE;
915 	/*get path frame for texture convertion */
916 	if (sten->type != GF_STENCIL_SOLID) {
917 		rc = surf->path_bounds;
918 		gf_mx2d_apply_rect(&mat, &rc);
919 		rc.x = rc.y = 0;
920 		/*assign target frame and matrix*/
921 		sten->frame = rc;
922 		gf_mx2d_copy(sten->pmat, surf->mat);
923 		gf_mx2d_inverse(&sten->pmat);
924 
925 		gf_mx2d_copy(st_mat, sten->smat);
926 		gf_mx2d_init(sten->smat);
927 
928 		switch (sten->type) {
929 		case GF_STENCIL_TEXTURE:
930 			if ( ((EVG_Texture *)sten)->tx_callback) {
931 
932 			} else {
933 				if (! ((EVG_Texture *)sten)->pixels) return GF_BAD_PARAM;
934 			}
935 
936 			if (((EVG_Texture *)sten)->mod & GF_TEXTURE_FLIP_Y) {
937 				if (!surf->center_coords) gf_mx2d_add_scale(&sten->smat, FIX_ONE, -FIX_ONE);
938 			} else {
939 				if (surf->center_coords) gf_mx2d_add_scale(&sten->smat, FIX_ONE, -FIX_ONE);
940 			}
941 			gf_mx2d_add_matrix(&sten->smat, &st_mat);
942 			gf_mx2d_add_matrix(&sten->smat, &mat);
943 			gf_mx2d_inverse(&sten->smat);
944 
945 			evg_texture_init(sten, surf);
946 			if (((EVG_Texture *)sten)->filter == GF_TEXTURE_FILTER_DEFAULT) {
947 				restore_filter = GF_TRUE;
948 				((EVG_Texture *)sten)->filter = surf->texture_filter;
949 			}
950 
951 			break;
952 		case GF_STENCIL_LINEAR_GRADIENT:
953 		{
954 			EVG_LinearGradient *lin = (EVG_LinearGradient *)sten;
955 			gf_mx2d_add_matrix(&sten->smat, &st_mat);
956 			gf_mx2d_add_matrix(&sten->smat, &mat);
957 			gf_mx2d_inverse(&sten->smat);
958 			/*and finalize matrix in gradient coord system*/
959 			gf_mx2d_add_matrix(&sten->smat, &lin->vecmat);
960 			gf_mx2d_add_scale(&sten->smat, INT2FIX(1<<EVGGRADIENTBITS), INT2FIX(1<<EVGGRADIENTBITS));
961 
962 			/*init*/
963 			evg_gradient_precompute((EVG_BaseGradient *)lin, surf);
964 
965 		}
966 		break;
967 		case GF_STENCIL_RADIAL_GRADIENT:
968 		{
969 			EVG_RadialGradient *rad = (EVG_RadialGradient*)sten;
970 			gf_mx2d_copy(sten->smat, st_mat);
971 			gf_mx2d_add_matrix(&sten->smat, &mat);
972 			gf_mx2d_inverse(&sten->smat);
973 			gf_mx2d_add_translation(&sten->smat, -rad->center.x, -rad->center.y);
974 			gf_mx2d_add_scale(&sten->smat, gf_invfix(rad->radius.x), gf_invfix(rad->radius.y));
975 
976 			rad->d_f.x = gf_divfix(rad->focus.x - rad->center.x, rad->radius.x);
977 			rad->d_f.y = gf_divfix(rad->focus.y - rad->center.y, rad->radius.y);
978 			/*init*/
979 			evg_radial_init(rad);
980 			evg_gradient_precompute((EVG_BaseGradient *)rad, surf);
981 		}
982 		break;
983 		}
984 	}
985 	max_gray = surf->raster->max_gray_spans;
986 	/*force complete line callback for YUV 420/422*/
987 	if (surf->yuv_type==EVG_YUV)
988 		surf->raster->max_gray_spans = 0xFFFFFFFF;
989 
990 	if (surf->useClipper) {
991 		surf->clip_xMin = surf->clipper.x;
992 		surf->clip_yMin = surf->clipper.y;
993 		surf->clip_xMax = (surf->clipper.x + surf->clipper.width);
994 		surf->clip_yMax = (surf->clipper.y + surf->clipper.height);
995 	} else {
996 		surf->clip_xMin = 0;
997 		surf->clip_yMin = 0;
998 		surf->clip_xMax = (surf->width);
999 		surf->clip_yMax = (surf->height);
1000 	}
1001 
1002 	/*and call the raster*/
1003 	if (surf->is_3d_matrix)
1004 		e = evg_raster_render_path_3d(surf);
1005 	else
1006 		e = evg_raster_render(surf);
1007 
1008 	surf->raster->max_gray_spans = max_gray;
1009 
1010 	/*restore stencil matrix*/
1011 	if (sten->type != GF_STENCIL_SOLID) {
1012 		gf_mx2d_copy(sten->smat, st_mat);
1013 		if (restore_filter) ((EVG_Texture *)sten)->filter = GF_TEXTURE_FILTER_DEFAULT;
1014 	}
1015 	surf->sten = 0L;
1016 	return e;
1017 }
1018 
1019 
1020 
gf_evg_surface_set_composite_mode(GF_EVGSurface * surf,GF_EVGCompositeMode comp_mode)1021 void gf_evg_surface_set_composite_mode(GF_EVGSurface *surf, GF_EVGCompositeMode comp_mode)
1022 {
1023 	if (surf) surf->comp_mode = comp_mode;
1024 }
1025 
gf_evg_surface_set_alpha_callback(GF_EVGSurface * surf,u8 (* get_alpha)(void * udta,u8 src_alpha,s32 x,s32 y),void * cbk)1026 void gf_evg_surface_set_alpha_callback(GF_EVGSurface *surf, u8 (*get_alpha)(void *udta, u8 src_alpha, s32 x, s32 y), void *cbk)
1027 {
1028 	if (surf) {
1029 		surf->get_alpha = get_alpha;
1030 		surf->get_alpha_udta = cbk;
1031 	}
1032 }
1033 
gf_evg_surface_set_projection(GF_EVGSurface * surf,GF_Matrix * mx)1034 GF_Err gf_evg_surface_set_projection(GF_EVGSurface *surf, GF_Matrix *mx)
1035 {
1036 	if (!surf || !surf->ext3d) return GF_BAD_PARAM;
1037 	gf_mx_copy(surf->ext3d->proj, *mx);
1038 	return GF_OK;
1039 }
gf_evg_surface_set_modelview(GF_EVGSurface * surf,GF_Matrix * mx)1040 GF_Err gf_evg_surface_set_modelview(GF_EVGSurface *surf, GF_Matrix *mx)
1041 {
1042 	if (!surf || !surf->ext3d) return GF_BAD_PARAM;
1043 	gf_mx_copy(surf->ext3d->modelview, *mx);
1044 	return GF_OK;
1045 }
1046 
1047 GF_Err evg_raster_render3d(GF_EVGSurface *surf, u32 *indices, u32 nb_idx, Float *vertices, u32 nb_vertices, u32 nb_comp, GF_EVGPrimitiveType prim_type);
1048 
gf_evg_surface_draw_array(GF_EVGSurface * surf,u32 * indices,u32 nb_idx,Float * vertices,u32 nb_vertices,u32 nb_comp,GF_EVGPrimitiveType prim_type)1049 GF_Err gf_evg_surface_draw_array(GF_EVGSurface *surf, u32 *indices, u32 nb_idx, Float *vertices, u32 nb_vertices, u32 nb_comp, GF_EVGPrimitiveType prim_type)
1050 {
1051 	GF_Err e;
1052 	u32 max_gray;
1053 	if (!surf || !surf->ext3d) return GF_BAD_PARAM;
1054 
1055 	/*setup ft raster calllbacks*/
1056 	if (!setup_grey_callback(surf, GF_TRUE)) return GF_OK;
1057 
1058 	if (surf->useClipper) {
1059 		surf->clip_xMin = surf->clipper.x;
1060 		surf->clip_yMin = surf->clipper.y;
1061 		surf->clip_xMax = (surf->clipper.x + surf->clipper.width);
1062 		surf->clip_yMax = (surf->clipper.y + surf->clipper.height);
1063 	} else {
1064 		surf->clip_xMin = 0;
1065 		surf->clip_yMin = 0;
1066 		surf->clip_xMax = (surf->width);
1067 		surf->clip_yMax = (surf->height);
1068 	}
1069 	max_gray = surf->raster->max_gray_spans;
1070 	/*force complete line callback for YUV 420/422*/
1071 	if (surf->yuv_type==EVG_YUV)
1072 		surf->raster->max_gray_spans = 0xFFFFFFFF;
1073 
1074 	/*and call the raster*/
1075 	e = evg_raster_render3d(surf, indices, nb_idx, vertices, nb_vertices, nb_comp, prim_type);
1076 	surf->raster->max_gray_spans =  max_gray;
1077 	return e;
1078 }
1079 
1080 GF_Err evg_raster_render3d_path(GF_EVGSurface *surf, GF_Path *path, Float z);
1081 
gf_evg_surface_draw_path(GF_EVGSurface * surf,GF_Path * path,Float z)1082 GF_Err gf_evg_surface_draw_path(GF_EVGSurface *surf, GF_Path *path, Float z)
1083 {
1084 	GF_Err e;
1085 	u32 max_gray;
1086 	if (!surf || !surf->ext3d) return GF_BAD_PARAM;
1087 
1088 	/*setup ft raster calllbacks*/
1089 	if (!setup_grey_callback(surf, GF_TRUE)) return GF_OK;
1090 
1091 	if (surf->useClipper) {
1092 		surf->clip_xMin = surf->clipper.x;
1093 		surf->clip_yMin = surf->clipper.y;
1094 		surf->clip_xMax = (surf->clipper.x + surf->clipper.width);
1095 		surf->clip_yMax = (surf->clipper.y + surf->clipper.height);
1096 	} else {
1097 		surf->clip_xMin = 0;
1098 		surf->clip_yMin = 0;
1099 		surf->clip_xMax = (surf->width);
1100 		surf->clip_yMax = (surf->height);
1101 	}
1102 	max_gray = surf->raster->max_gray_spans;
1103 	/*force complete line callback for YUV 420/422*/
1104 	if (surf->yuv_type==EVG_YUV)
1105 		surf->raster->max_gray_spans = 0xFFFFFFFF;
1106 
1107 	/*and call the raster*/
1108 	e = evg_raster_render3d_path(surf, path, z);
1109 	surf->raster->max_gray_spans =  max_gray;
1110 	return e;
1111 }
1112