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