1 /*
2  *			GPAC - Multimedia Framework C SDK
3  *
4  *			Authors: Jean Le Feuvre
5  *			Copyright (c) Telecom ParisTech 2000-2012
6  *					All rights reserved
7  *
8  *  This file is part of GPAC / GDIplus 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 #include "gdip_priv.h"
27 
28 
gdip_new_stencil(GF_Raster2D *,GF_StencilType type)29 GF_STENCIL gdip_new_stencil(GF_Raster2D *, GF_StencilType type)
30 {
31 	struct _stencil *sten;
32 
33 	switch (type) {
34 	case GF_STENCIL_SOLID:
35 	case GF_STENCIL_LINEAR_GRADIENT:
36 	case GF_STENCIL_RADIAL_GRADIENT:
37 	case GF_STENCIL_VERTEX_GRADIENT:
38 	case GF_STENCIL_TEXTURE:
39 		break;
40 	default:
41 		return NULL;
42 	}
43 	SAFEALLOC(sten, struct _stencil);
44 	sten->type = type;
45 	sten->alpha = 255;
46 	return (GF_STENCIL) sten;
47 }
48 
49 static
gdip_delete_stencil(GF_STENCIL _this)50 void gdip_delete_stencil(GF_STENCIL _this)
51 {
52 	GPSTEN();
53 	if (_sten->pSolid) GdipDeleteBrush(_sten->pSolid);
54 	if (_sten->pTexture) GdipDeleteBrush(_sten->pTexture);
55 	if (_sten->pLinear) GdipDeleteBrush(_sten->pLinear);
56 	if (_sten->pRadial) GdipDeleteBrush(_sten->pRadial);
57 	if (_sten->circle) GdipDeletePath(_sten->circle);
58 	if (_sten->pMat) GdipDeleteMatrix(_sten->pMat);
59 	if (_sten->pLinearMat) GdipDeleteMatrix(_sten->pLinearMat);
60 	if (_sten->pBitmap) GdipDisposeImage(_sten->pBitmap);
61 	if (_sten->conv_buf) gf_free(_sten->conv_buf);
62 
63 	if (_sten->cols) delete [] _sten->cols;
64 	if (_sten->pos) delete [] _sten->pos;
65 
66 	gf_free(_sten);
67 }
68 static
gdip_stencil_set_matrix(GF_STENCIL _this,GF_Matrix2D * mat)69 GF_Err gdip_stencil_set_matrix(GF_STENCIL _this, GF_Matrix2D *mat)
70 {
71 	GPSTEN();
72 	GPMATRIX();
73 	if (_sten->pMat) GdipDeleteMatrix(_sten->pMat);
74 	_sten->pMat = _mat;
75 	return GF_OK;
76 }
77 
78 static
gdip_set_brush_color(GF_STENCIL _this,GF_Color c)79 GF_Err gdip_set_brush_color(GF_STENCIL _this, GF_Color c)
80 {
81 	GPSTEN();
82 	CHECK_RET(GF_STENCIL_SOLID);
83 	if (!_sten->pSolid)
84 		GdipCreateSolidFill(c, &_sten->pSolid);
85 	else
86 		GdipSetSolidFillColor(_sten->pSolid, c);
87 
88 	return GF_OK;
89 }
90 
91 
92 static
gdip_set_gradient_mode(GF_STENCIL _this,GF_GradientMode mode)93 GF_Err gdip_set_gradient_mode(GF_STENCIL _this, GF_GradientMode mode)
94 {
95 	GPSTEN();
96 	CHECK2_RET(GF_STENCIL_LINEAR_GRADIENT, GF_STENCIL_RADIAL_GRADIENT);
97 	_sten->spread = mode;
98 	_sten->needs_rebuild = GF_TRUE;
99 	return GF_OK;
100 }
101 
102 static
gdip_set_linear_gradient(GF_STENCIL _this,Fixed start_x,Fixed start_y,Fixed end_x,Fixed end_y)103 GF_Err gdip_set_linear_gradient (GF_STENCIL _this, Fixed start_x, Fixed start_y, Fixed end_x, Fixed end_y)
104 {
105 	GPSTEN();
106 	CHECK_RET(GF_STENCIL_LINEAR_GRADIENT);
107 	if (_sten->pLinear) GdipDeleteBrush(_sten->pLinear);
108 
109 	_sten->start.X = FIX2FLT(start_x);
110 	_sten->start.Y = FIX2FLT(start_y);
111 	_sten->end.X = FIX2FLT(end_x);
112 	_sten->end.Y = FIX2FLT(end_y);
113 
114 	GdipCreateLineBrush(&_sten->start, &_sten->end, 0xFF000000, 0xFFFFFFFF, WrapModeTile, &_sten->pLinear);
115 	if (!_sten->pLinearMat) GdipCreateMatrix(&_sten->pLinearMat);
116 	GdipGetLineTransform(_sten->pLinear, _sten->pLinearMat);
117 	_sten->needs_rebuild = GF_TRUE;
118 	return GF_OK;
119 }
120 
gdip_recompute_line_gradient(GF_STENCIL _this)121 void gdip_recompute_line_gradient(GF_STENCIL _this)
122 {
123 	GpPointF start, end;
124 	u32 i, k;
125 	REAL w, h;
126 	GPSTEN();
127 
128 	if (!_sten->needs_rebuild) return;
129 	_sten->needs_rebuild = GF_FALSE;
130 
131 	if (_sten->pLinear) GdipDeleteBrush(_sten->pLinear);
132 	GdipCreateLineBrush(&_sten->start, &_sten->end, 0xFFFF0000, 0xFFFF00FF, WrapModeTile, &_sten->pLinear);
133 	switch (_sten->spread) {
134 	case GF_GRADIENT_MODE_PAD:
135 		break;
136 	case GF_GRADIENT_MODE_SPREAD:
137 		GdipSetLineWrapMode(_sten->pLinear, WrapModeTileFlipXY);
138 		GdipSetLinePresetBlend(_sten->pLinear, (ARGB *) _sten->cols, _sten->pos, _sten->num_pos);
139 		return;
140 	case GF_GRADIENT_MODE_REPEAT:
141 		GdipSetLineWrapMode(_sten->pLinear, WrapModeTile);
142 		GdipSetLinePresetBlend(_sten->pLinear, (ARGB *) _sten->cols, _sten->pos, _sten->num_pos);
143 		return;
144 	}
145 	/*currently gdiplus doesn't support padded mode on gradients, so update the line gradient by
146 	using a line 3 times longer*/
147 	w = _sten->end.X - _sten->start.X;
148 	h = _sten->end.Y - _sten->start.Y;
149 	start.X = _sten->start.X - w;
150 	start.Y = _sten->start.Y - h;
151 	end.X = _sten->end.X + w;
152 	end.Y = _sten->end.Y + h;
153 	GdipCreateLineBrush(&start, &end, 0xFFFF0000, 0xFFFF00FF, WrapModeTile, &_sten->pLinear);
154 	ARGB *cols = new ARGB[_sten->num_pos+2];
155 	REAL *pos = new REAL[_sten->num_pos+2];
156 
157 	k=0;
158 	for (i=0; i<_sten->num_pos; i++) {
159 		cols[i+k] = _sten->cols[i];
160 		pos[i+k] = (1 + _sten->pos[i])/3;
161 
162 		if (!i) {
163 			pos[1] = pos[0];
164 			cols[1] = cols[0];
165 			k=1;
166 			pos[0] = 0;
167 		}
168 	}
169 	pos[_sten->num_pos+1] = 1.0;
170 	cols[_sten->num_pos+1] = cols[_sten->num_pos];
171 
172 	/*since depending on gradient transform the padding is likely to be not big enough, use flipXY to assure that in most
173 	cases the x3 dilatation is enough*/
174 	GdipSetLineWrapMode(_sten->pLinear, WrapModeTileFlipXY);
175 	GdipSetLinePresetBlend(_sten->pLinear, cols, pos, 2+_sten->num_pos);
176 
177 	delete [] cols;
178 	delete [] pos;
179 }
180 
181 
182 /*GDIplus is completely bugged here, we MUST build the gradient in local coord system and apply translation
183 after, otherwise performances are just horrible*/
gdip_recompute_radial_gradient(GF_STENCIL _this)184 void gdip_recompute_radial_gradient(GF_STENCIL _this)
185 {
186 	s32 repeat, k;
187 	u32 i;
188 	GpPointF pt;
189 	GpMatrix *mat;
190 	GPSTEN();
191 
192 
193 	if (!_sten->needs_rebuild) return;
194 	_sten->needs_rebuild = GF_FALSE;
195 
196 
197 	if (_sten->pRadial) {
198 		GdipDeleteBrush(_sten->pRadial);
199 		_sten->pRadial = NULL;
200 	}
201 	if (_sten->pSolid) {
202 		GdipDeleteBrush(_sten->pSolid);
203 		_sten->pSolid = NULL;
204 	}
205 	if (_sten->circle) {
206 		GdipDeletePath(_sten->circle);
207 		_sten->circle = NULL;
208 	}
209 
210 	GdipCreatePath(FillModeAlternate, &_sten->circle);
211 	/*get number of repeats*/
212 	if (_sten->spread == GF_GRADIENT_MODE_PAD) {
213 
214 
215 		GdipAddPathEllipse(_sten->circle, - _sten->radius.X, -_sten->radius.Y,
216 		                   2*_sten->radius.X, 2*_sten->radius.Y);
217 
218 		GdipCreatePathGradientFromPath(_sten->circle, &_sten->pRadial);
219 
220 		ARGB *blends = new ARGB[_sten->num_pos + 1];
221 
222 		/*radial blend pos are from bounds to center in gdiplus*/
223 		blends[0] = _sten->cols[_sten->num_pos - 1];
224 		for (i=0; i<_sten->num_pos; i++) {
225 			blends[i+1] = _sten->cols[_sten->num_pos - i - 1];
226 		}
227 
228 		REAL *pos = new REAL[_sten->num_pos + 1];
229 		pos[0] = 0;
230 		for (i=0; i<_sten->num_pos; i++) {
231 			pos[i+1] = _sten->pos[i];
232 		}
233 
234 		GdipSetPathGradientPresetBlend(_sten->pRadial, blends, pos, _sten->num_pos + 1);
235 		delete [] blends;
236 		delete [] pos;
237 
238 		/*set focal*/
239 		pt = _sten->focal;
240 		pt.X -= _sten->center.X;
241 		pt.Y -= _sten->center.Y;
242 		GdipSetPathGradientCenterPoint(_sten->pRadial, &pt);
243 
244 		/*set transform*/
245 		GdipCreateMatrix(&mat);
246 		GdipTranslateMatrix(mat, _sten->center.X, _sten->center.Y, MatrixOrderAppend);
247 		if (_sten->pMat) GdipMultiplyMatrix(mat, _sten->pMat, MatrixOrderAppend);
248 		GdipSetTextureTransform((GpTexture*)_sten->pRadial, mat);
249 		GdipDeleteMatrix(mat);
250 
251 		/*create back brush*/
252 		GdipCreateSolidFill(_sten->cols[_sten->num_pos - 1], &_sten->pSolid);
253 		GdipResetPath(_sten->circle);
254 		GdipAddPathEllipse(_sten->circle, - _sten->radius.X + _sten->center.X, -_sten->radius.Y + _sten->center.Y,
255 		                   2*_sten->radius.X, 2*_sten->radius.Y);
256 
257 	} else {
258 		repeat = 10;
259 
260 		GdipAddPathEllipse(_sten->circle, - repeat * _sten->radius.X, - repeat*_sten->radius.Y,
261 		                   2*repeat*_sten->radius.X,  2*repeat*_sten->radius.Y);
262 
263 		GdipCreatePathGradientFromPath(_sten->circle, &_sten->pRadial);
264 		GdipDeletePath(_sten->circle);
265 		_sten->circle = NULL;
266 
267 		ARGB *blends = new ARGB[_sten->num_pos*repeat];
268 		REAL *pos = new REAL[_sten->num_pos*repeat];
269 
270 		if (_sten->spread == GF_GRADIENT_MODE_REPEAT) {
271 			for (k=0; k<repeat; k++) {
272 				for (i=0; i<_sten->num_pos; i++) {
273 					blends[k*_sten->num_pos + i] = _sten->cols[_sten->num_pos - i - 1];
274 					pos[k*_sten->num_pos + i] = (k + _sten->pos[i]) / repeat;
275 				}
276 			}
277 		} else {
278 			for (k=0; k<repeat; k++) {
279 				for (i=0; i<_sten->num_pos; i++) {
280 					u32 index = (k%2) ? (_sten->num_pos-i-1) : i;
281 					blends[k*_sten->num_pos + i] = _sten->cols[index];
282 					if (k%2) {
283 						pos[k*_sten->num_pos + i] = (k + (1 - _sten->pos[index]) ) / repeat;
284 					} else {
285 						pos[k*_sten->num_pos + i] = ( k + _sten->pos[i] ) / repeat;
286 					}
287 				}
288 			}
289 		}
290 		GdipSetPathGradientPresetBlend(_sten->pRadial, blends, pos, _sten->num_pos*repeat);
291 		delete [] pos;
292 		delete [] blends;
293 
294 
295 		/*set focal*/
296 		pt = _sten->focal;
297 		pt.X -= (1 - repeat) * (_sten->focal.X - _sten->center.X) + _sten->center.X;
298 		pt.Y -= (1 - repeat) * (_sten->focal.Y - _sten->center.Y) + _sten->center.Y;
299 		GdipSetPathGradientCenterPoint(_sten->pRadial, &pt);
300 
301 		/*set transform*/
302 		GdipCreateMatrix(&mat);
303 		GdipTranslateMatrix(mat, (1 - repeat) * (_sten->focal.X - _sten->center.X) + _sten->center.X,
304 		                    (1 - repeat) * (_sten->focal.Y - _sten->center.Y) + _sten->center.Y,
305 		                    MatrixOrderAppend);
306 		if (_sten->pMat) GdipMultiplyMatrix(mat, _sten->pMat, MatrixOrderAppend);
307 		GdipSetTextureTransform((GpTexture*)_sten->pRadial, mat);
308 		GdipDeleteMatrix(mat);
309 
310 		GdipSetPathGradientWrapMode(_sten->pRadial, WrapModeTileFlipXY);
311 	}
312 }
313 
314 static
gdip_set_radial_gradient(GF_STENCIL _this,Fixed cx,Fixed cy,Fixed fx,Fixed fy,Fixed x_radius,Fixed y_radius)315 GF_Err gdip_set_radial_gradient(GF_STENCIL _this, Fixed cx, Fixed cy, Fixed fx, Fixed fy, Fixed x_radius, Fixed y_radius)
316 {
317 	GPSTEN();
318 	CHECK_RET(GF_STENCIL_RADIAL_GRADIENT);
319 
320 	/*store focal info*/
321 	_sten->radius.X = FIX2FLT(x_radius);
322 	_sten->radius.Y = FIX2FLT(y_radius);
323 	_sten->center.X = FIX2FLT(cx);
324 	_sten->center.Y = FIX2FLT(cy);
325 	_sten->focal.X = FIX2FLT(fx);
326 	_sten->focal.Y = FIX2FLT(fy);
327 	_sten->needs_rebuild = GF_TRUE;
328 	return GF_OK;
329 }
330 
331 static
gdip_set_gradient_interpolation(GF_STENCIL _this,Fixed * pos,GF_Color * col,u32 count)332 GF_Err gdip_set_gradient_interpolation(GF_STENCIL _this, Fixed *pos, GF_Color *col, u32 count)
333 {
334 	u32 i;
335 	GPSTEN();
336 
337 	if (_sten->cols) delete [] _sten->cols;
338 	if (_sten->pos) delete [] _sten->pos;
339 
340 	/*handle padding internally*/
341 	_sten->cols = new ARGB[count];
342 	_sten->pos = new REAL[count];
343 	for (i=0; i<count; i++) _sten->pos[i] = FIX2FLT(pos[i]);
344 	memcpy(_sten->cols, col, sizeof(ARGB)*count);
345 	_sten->num_pos = count;
346 	_sten->needs_rebuild = GF_TRUE;
347 	return GF_OK;
348 }
349 
350 
351 static
gdip_set_vertex_path(GF_STENCIL _this,GF_Path * path)352 GF_Err gdip_set_vertex_path(GF_STENCIL _this, GF_Path *path)
353 {
354 	GPSTEN();
355 	GpPath *p;
356 	CHECK_RET(GF_STENCIL_VERTEX_GRADIENT);
357 	p = gdip_create_path(path);
358 	if (_sten->pRadial) GdipDeleteBrush(_sten->pRadial);
359 	GdipCreatePathGradientFromPath(p, &_sten->pRadial);
360 	GdipDeletePath(p);
361 	return GF_OK;
362 }
363 
364 static
gdip_set_vertex_center(GF_STENCIL _this,Fixed cx,Fixed cy,u32 color)365 GF_Err gdip_set_vertex_center (GF_STENCIL _this, Fixed cx, Fixed cy, u32 color)
366 {
367 	GpStatus ret;
368 	GPSTEN();
369 	CHECK_RET(GF_STENCIL_VERTEX_GRADIENT);
370 
371 	if (!_sten->pRadial) return GF_BAD_PARAM;
372 	_sten->center.X = FIX2FLT(cx);
373 	_sten->center.Y = FIX2FLT(cy);
374 
375 	ret = GdipSetPathGradientCenterPoint(_sten->pRadial, &_sten->center);
376 	ret = GdipSetPathGradientCenterColor(_sten->pRadial, (ARGB) color);
377 	return GF_OK;
378 }
379 
380 static
gdip_set_vertex_colors(GF_STENCIL _this,u32 * colors,u32 nbCol)381 GF_Err gdip_set_vertex_colors (GF_STENCIL _this, u32 *colors, u32 nbCol)
382 {
383 	int col = nbCol;
384 	GPSTEN();
385 	CHECK_RET(GF_STENCIL_VERTEX_GRADIENT);
386 
387 	GpStatus ret;
388 	if (!_sten->pRadial) return GF_BAD_PARAM;
389 	ret = GdipSetPathGradientSurroundColorsWithCount(_sten->pRadial, (ARGB *) colors, &col);
390 	return GF_OK;
391 }
392 
393 
gdip_init_driver_grad(GF_Raster2D * driver)394 void gdip_init_driver_grad(GF_Raster2D *driver)
395 {
396 	driver->stencil_new = gdip_new_stencil;
397 	driver->stencil_delete = gdip_delete_stencil;
398 	driver->stencil_set_matrix = gdip_stencil_set_matrix;
399 	driver->stencil_set_brush_color = gdip_set_brush_color;
400 	driver->stencil_set_gradient_mode = gdip_set_gradient_mode;
401 	driver->stencil_set_linear_gradient = gdip_set_linear_gradient;
402 	driver->stencil_set_radial_gradient = gdip_set_radial_gradient;
403 	driver->stencil_set_gradient_interpolation = gdip_set_gradient_interpolation;
404 	driver->stencil_set_vertex_path = gdip_set_vertex_path;
405 	driver->stencil_set_vertex_center = gdip_set_vertex_center;
406 	driver->stencil_set_vertex_colors = gdip_set_vertex_colors;
407 }
408