| /* | |
| * GPAC - Multimedia Framework C SDK | |
| * | |
| * Authors: Jean Le Feuvre | |
| * Copyright (c) Telecom ParisTech 2000-2012 | |
| * All rights reserved | |
| * | |
| * This file is part of GPAC / GDIplus rasterizer module | |
| * | |
| * GPAC is free software; you can redistribute it and/or modify | |
| * it under the terms of the GNU Lesser General Public License as published by | |
| * the Free Software Foundation; either version 2, or (at your option) | |
| * any later version. | |
| * | |
| * GPAC is distributed in the hope that it will be useful, | |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| * GNU Lesser General Public License for more details. | |
| * | |
| * You should have received a copy of the GNU Lesser General Public | |
| * License along with this library; see the file COPYING. If not, write to | |
| * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. | |
| * | |
| */ | |
| #include "gdip_priv.h" | |
| GF_STENCIL gdip_new_stencil(GF_Raster2D *, GF_StencilType type) | |
| { | |
| struct _stencil *sten; | |
| switch (type) { | |
| case GF_STENCIL_SOLID: | |
| case GF_STENCIL_LINEAR_GRADIENT: | |
| case GF_STENCIL_RADIAL_GRADIENT: | |
| case GF_STENCIL_VERTEX_GRADIENT: | |
| case GF_STENCIL_TEXTURE: | |
| break; | |
| default: | |
| return NULL; | |
| } | |
| SAFEALLOC(sten, struct _stencil); | |
| sten->type = type; | |
| sten->alpha = 255; | |
| return (GF_STENCIL) sten; | |
| } | |
| static | |
| void gdip_delete_stencil(GF_STENCIL _this) | |
| { | |
| GPSTEN(); | |
| if (_sten->pSolid) GdipDeleteBrush(_sten->pSolid); | |
| if (_sten->pTexture) GdipDeleteBrush(_sten->pTexture); | |
| if (_sten->pLinear) GdipDeleteBrush(_sten->pLinear); | |
| if (_sten->pRadial) GdipDeleteBrush(_sten->pRadial); | |
| if (_sten->circle) GdipDeletePath(_sten->circle); | |
| if (_sten->pMat) GdipDeleteMatrix(_sten->pMat); | |
| if (_sten->pLinearMat) GdipDeleteMatrix(_sten->pLinearMat); | |
| if (_sten->pBitmap) GdipDisposeImage(_sten->pBitmap); | |
| if (_sten->conv_buf) gf_free(_sten->conv_buf); | |
| if (_sten->cols) delete [] _sten->cols; | |
| if (_sten->pos) delete [] _sten->pos; | |
| gf_free(_sten); | |
| } | |
| static | |
| GF_Err gdip_stencil_set_matrix(GF_STENCIL _this, GF_Matrix2D *mat) | |
| { | |
| GPSTEN(); | |
| GPMATRIX(); | |
| if (_sten->pMat) GdipDeleteMatrix(_sten->pMat); | |
| _sten->pMat = _mat; | |
| return GF_OK; | |
| } | |
| static | |
| GF_Err gdip_set_brush_color(GF_STENCIL _this, GF_Color c) | |
| { | |
| GPSTEN(); | |
| CHECK_RET(GF_STENCIL_SOLID); | |
| if (!_sten->pSolid) | |
| GdipCreateSolidFill(c, &_sten->pSolid); | |
| else | |
| GdipSetSolidFillColor(_sten->pSolid, c); | |
| return GF_OK; | |
| } | |
| static | |
| GF_Err gdip_set_gradient_mode(GF_STENCIL _this, GF_GradientMode mode) | |
| { | |
| GPSTEN(); | |
| CHECK2_RET(GF_STENCIL_LINEAR_GRADIENT, GF_STENCIL_RADIAL_GRADIENT); | |
| _sten->spread = mode; | |
| _sten->needs_rebuild = GF_TRUE; | |
| return GF_OK; | |
| } | |
| static | |
| GF_Err gdip_set_linear_gradient (GF_STENCIL _this, Fixed start_x, Fixed start_y, Fixed end_x, Fixed end_y) | |
| { | |
| GPSTEN(); | |
| CHECK_RET(GF_STENCIL_LINEAR_GRADIENT); | |
| if (_sten->pLinear) GdipDeleteBrush(_sten->pLinear); | |
| _sten->start.X = FIX2FLT(start_x); | |
| _sten->start.Y = FIX2FLT(start_y); | |
| _sten->end.X = FIX2FLT(end_x); | |
| _sten->end.Y = FIX2FLT(end_y); | |
| GdipCreateLineBrush(&_sten->start, &_sten->end, 0xFF000000, 0xFFFFFFFF, WrapModeTile, &_sten->pLinear); | |
| if (!_sten->pLinearMat) GdipCreateMatrix(&_sten->pLinearMat); | |
| GdipGetLineTransform(_sten->pLinear, _sten->pLinearMat); | |
| _sten->needs_rebuild = GF_TRUE; | |
| return GF_OK; | |
| } | |
| void gdip_recompute_line_gradient(GF_STENCIL _this) | |
| { | |
| GpPointF start, end; | |
| u32 i, k; | |
| REAL w, h; | |
| GPSTEN(); | |
| if (!_sten->needs_rebuild) return; | |
| _sten->needs_rebuild = GF_FALSE; | |
| if (_sten->pLinear) GdipDeleteBrush(_sten->pLinear); | |
| GdipCreateLineBrush(&_sten->start, &_sten->end, 0xFFFF0000, 0xFFFF00FF, WrapModeTile, &_sten->pLinear); | |
| switch (_sten->spread) { | |
| case GF_GRADIENT_MODE_PAD: | |
| break; | |
| case GF_GRADIENT_MODE_SPREAD: | |
| GdipSetLineWrapMode(_sten->pLinear, WrapModeTileFlipXY); | |
| GdipSetLinePresetBlend(_sten->pLinear, (ARGB *) _sten->cols, _sten->pos, _sten->num_pos); | |
| return; | |
| case GF_GRADIENT_MODE_REPEAT: | |
| GdipSetLineWrapMode(_sten->pLinear, WrapModeTile); | |
| GdipSetLinePresetBlend(_sten->pLinear, (ARGB *) _sten->cols, _sten->pos, _sten->num_pos); | |
| return; | |
| } | |
| /*currently gdiplus doesn't support padded mode on gradients, so update the line gradient by | |
| using a line 3 times longer*/ | |
| w = _sten->end.X - _sten->start.X; | |
| h = _sten->end.Y - _sten->start.Y; | |
| start.X = _sten->start.X - w; | |
| start.Y = _sten->start.Y - h; | |
| end.X = _sten->end.X + w; | |
| end.Y = _sten->end.Y + h; | |
| GdipCreateLineBrush(&start, &end, 0xFFFF0000, 0xFFFF00FF, WrapModeTile, &_sten->pLinear); | |
| ARGB *cols = new ARGB[_sten->num_pos+2]; | |
| REAL *pos = new REAL[_sten->num_pos+2]; | |
| k=0; | |
| for (i=0; i<_sten->num_pos; i++) { | |
| cols[i+k] = _sten->cols[i]; | |
| pos[i+k] = (1 + _sten->pos[i])/3; | |
| if (!i) { | |
| pos[1] = pos[0]; | |
| cols[1] = cols[0]; | |
| k=1; | |
| pos[0] = 0; | |
| } | |
| } | |
| pos[_sten->num_pos+1] = 1.0; | |
| cols[_sten->num_pos+1] = cols[_sten->num_pos]; | |
| /*since depending on gradient transform the padding is likely to be not big enough, use flipXY to assure that in most | |
| cases the x3 dilatation is enough*/ | |
| GdipSetLineWrapMode(_sten->pLinear, WrapModeTileFlipXY); | |
| GdipSetLinePresetBlend(_sten->pLinear, cols, pos, 2+_sten->num_pos); | |
| delete [] cols; | |
| delete [] pos; | |
| } | |
| /*GDIplus is completely bugged here, we MUST build the gradient in local coord system and apply translation | |
| after, otherwise performances are just horrible*/ | |
| void gdip_recompute_radial_gradient(GF_STENCIL _this) | |
| { | |
| s32 repeat, k; | |
| u32 i; | |
| GpPointF pt; | |
| GpMatrix *mat; | |
| GPSTEN(); | |
| if (!_sten->needs_rebuild) return; | |
| _sten->needs_rebuild = GF_FALSE; | |
| if (_sten->pRadial) { | |
| GdipDeleteBrush(_sten->pRadial); | |
| _sten->pRadial = NULL; | |
| } | |
| if (_sten->pSolid) { | |
| GdipDeleteBrush(_sten->pSolid); | |
| _sten->pSolid = NULL; | |
| } | |
| if (_sten->circle) { | |
| GdipDeletePath(_sten->circle); | |
| _sten->circle = NULL; | |
| } | |
| GdipCreatePath(FillModeAlternate, &_sten->circle); | |
| /*get number of repeats*/ | |
| if (_sten->spread == GF_GRADIENT_MODE_PAD) { | |
| GdipAddPathEllipse(_sten->circle, - _sten->radius.X, -_sten->radius.Y, | |
| 2*_sten->radius.X, 2*_sten->radius.Y); | |
| GdipCreatePathGradientFromPath(_sten->circle, &_sten->pRadial); | |
| ARGB *blends = new ARGB[_sten->num_pos + 1]; | |
| /*radial blend pos are from bounds to center in gdiplus*/ | |
| blends[0] = _sten->cols[_sten->num_pos - 1]; | |
| for (i=0; i<_sten->num_pos; i++) { | |
| blends[i+1] = _sten->cols[_sten->num_pos - i - 1]; | |
| } | |
| REAL *pos = new REAL[_sten->num_pos + 1]; | |
| pos[0] = 0; | |
| for (i=0; i<_sten->num_pos; i++) { | |
| pos[i+1] = _sten->pos[i]; | |
| } | |
| GdipSetPathGradientPresetBlend(_sten->pRadial, blends, pos, _sten->num_pos + 1); | |
| delete [] blends; | |
| delete [] pos; | |
| /*set focal*/ | |
| pt = _sten->focal; | |
| pt.X -= _sten->center.X; | |
| pt.Y -= _sten->center.Y; | |
| GdipSetPathGradientCenterPoint(_sten->pRadial, &pt); | |
| /*set transform*/ | |
| GdipCreateMatrix(&mat); | |
| GdipTranslateMatrix(mat, _sten->center.X, _sten->center.Y, MatrixOrderAppend); | |
| if (_sten->pMat) GdipMultiplyMatrix(mat, _sten->pMat, MatrixOrderAppend); | |
| GdipSetTextureTransform((GpTexture*)_sten->pRadial, mat); | |
| GdipDeleteMatrix(mat); | |
| /*create back brush*/ | |
| GdipCreateSolidFill(_sten->cols[_sten->num_pos - 1], &_sten->pSolid); | |
| GdipResetPath(_sten->circle); | |
| GdipAddPathEllipse(_sten->circle, - _sten->radius.X + _sten->center.X, -_sten->radius.Y + _sten->center.Y, | |
| 2*_sten->radius.X, 2*_sten->radius.Y); | |
| } else { | |
| repeat = 10; | |
| GdipAddPathEllipse(_sten->circle, - repeat * _sten->radius.X, - repeat*_sten->radius.Y, | |
| 2*repeat*_sten->radius.X, 2*repeat*_sten->radius.Y); | |
| GdipCreatePathGradientFromPath(_sten->circle, &_sten->pRadial); | |
| GdipDeletePath(_sten->circle); | |
| _sten->circle = NULL; | |
| ARGB *blends = new ARGB[_sten->num_pos*repeat]; | |
| REAL *pos = new REAL[_sten->num_pos*repeat]; | |
| if (_sten->spread == GF_GRADIENT_MODE_REPEAT) { | |
| for (k=0; k<repeat; k++) { | |
| for (i=0; i<_sten->num_pos; i++) { | |
| blends[k*_sten->num_pos + i] = _sten->cols[_sten->num_pos - i - 1]; | |
| pos[k*_sten->num_pos + i] = (k + _sten->pos[i]) / repeat; | |
| } | |
| } | |
| } else { | |
| for (k=0; k<repeat; k++) { | |
| for (i=0; i<_sten->num_pos; i++) { | |
| u32 index = (k%2) ? (_sten->num_pos-i-1) : i; | |
| blends[k*_sten->num_pos + i] = _sten->cols[index]; | |
| if (k%2) { | |
| pos[k*_sten->num_pos + i] = (k + (1 - _sten->pos[index]) ) / repeat; | |
| } else { | |
| pos[k*_sten->num_pos + i] = ( k + _sten->pos[i] ) / repeat; | |
| } | |
| } | |
| } | |
| } | |
| GdipSetPathGradientPresetBlend(_sten->pRadial, blends, pos, _sten->num_pos*repeat); | |
| delete [] pos; | |
| delete [] blends; | |
| /*set focal*/ | |
| pt = _sten->focal; | |
| pt.X -= (1 - repeat) * (_sten->focal.X - _sten->center.X) + _sten->center.X; | |
| pt.Y -= (1 - repeat) * (_sten->focal.Y - _sten->center.Y) + _sten->center.Y; | |
| GdipSetPathGradientCenterPoint(_sten->pRadial, &pt); | |
| /*set transform*/ | |
| GdipCreateMatrix(&mat); | |
| GdipTranslateMatrix(mat, (1 - repeat) * (_sten->focal.X - _sten->center.X) + _sten->center.X, | |
| (1 - repeat) * (_sten->focal.Y - _sten->center.Y) + _sten->center.Y, | |
| MatrixOrderAppend); | |
| if (_sten->pMat) GdipMultiplyMatrix(mat, _sten->pMat, MatrixOrderAppend); | |
| GdipSetTextureTransform((GpTexture*)_sten->pRadial, mat); | |
| GdipDeleteMatrix(mat); | |
| GdipSetPathGradientWrapMode(_sten->pRadial, WrapModeTileFlipXY); | |
| } | |
| } | |
| static | |
| GF_Err gdip_set_radial_gradient(GF_STENCIL _this, Fixed cx, Fixed cy, Fixed fx, Fixed fy, Fixed x_radius, Fixed y_radius) | |
| { | |
| GPSTEN(); | |
| CHECK_RET(GF_STENCIL_RADIAL_GRADIENT); | |
| /*store focal info*/ | |
| _sten->radius.X = FIX2FLT(x_radius); | |
| _sten->radius.Y = FIX2FLT(y_radius); | |
| _sten->center.X = FIX2FLT(cx); | |
| _sten->center.Y = FIX2FLT(cy); | |
| _sten->focal.X = FIX2FLT(fx); | |
| _sten->focal.Y = FIX2FLT(fy); | |
| _sten->needs_rebuild = GF_TRUE; | |
| return GF_OK; | |
| } | |
| static | |
| GF_Err gdip_set_gradient_interpolation(GF_STENCIL _this, Fixed *pos, GF_Color *col, u32 count) | |
| { | |
| u32 i; | |
| GPSTEN(); | |
| if (_sten->cols) delete [] _sten->cols; | |
| if (_sten->pos) delete [] _sten->pos; | |
| /*handle padding internally*/ | |
| _sten->cols = new ARGB[count]; | |
| _sten->pos = new REAL[count]; | |
| for (i=0; i<count; i++) _sten->pos[i] = FIX2FLT(pos[i]); | |
| memcpy(_sten->cols, col, sizeof(ARGB)*count); | |
| _sten->num_pos = count; | |
| _sten->needs_rebuild = GF_TRUE; | |
| return GF_OK; | |
| } | |
| static | |
| GF_Err gdip_set_vertex_path(GF_STENCIL _this, GF_Path *path) | |
| { | |
| GPSTEN(); | |
| GpPath *p; | |
| CHECK_RET(GF_STENCIL_VERTEX_GRADIENT); | |
| p = gdip_create_path(path); | |
| if (_sten->pRadial) GdipDeleteBrush(_sten->pRadial); | |
| GdipCreatePathGradientFromPath(p, &_sten->pRadial); | |
| GdipDeletePath(p); | |
| return GF_OK; | |
| } | |
| static | |
| GF_Err gdip_set_vertex_center (GF_STENCIL _this, Fixed cx, Fixed cy, u32 color) | |
| { | |
| GpStatus ret; | |
| GPSTEN(); | |
| CHECK_RET(GF_STENCIL_VERTEX_GRADIENT); | |
| if (!_sten->pRadial) return GF_BAD_PARAM; | |
| _sten->center.X = FIX2FLT(cx); | |
| _sten->center.Y = FIX2FLT(cy); | |
| ret = GdipSetPathGradientCenterPoint(_sten->pRadial, &_sten->center); | |
| ret = GdipSetPathGradientCenterColor(_sten->pRadial, (ARGB) color); | |
| return GF_OK; | |
| } | |
| static | |
| GF_Err gdip_set_vertex_colors (GF_STENCIL _this, u32 *colors, u32 nbCol) | |
| { | |
| int col = nbCol; | |
| GPSTEN(); | |
| CHECK_RET(GF_STENCIL_VERTEX_GRADIENT); | |
| GpStatus ret; | |
| if (!_sten->pRadial) return GF_BAD_PARAM; | |
| ret = GdipSetPathGradientSurroundColorsWithCount(_sten->pRadial, (ARGB *) colors, &col); | |
| return GF_OK; | |
| } | |
| void gdip_init_driver_grad(GF_Raster2D *driver) | |
| { | |
| driver->stencil_new = gdip_new_stencil; | |
| driver->stencil_delete = gdip_delete_stencil; | |
| driver->stencil_set_matrix = gdip_stencil_set_matrix; | |
| driver->stencil_set_brush_color = gdip_set_brush_color; | |
| driver->stencil_set_gradient_mode = gdip_set_gradient_mode; | |
| driver->stencil_set_linear_gradient = gdip_set_linear_gradient; | |
| driver->stencil_set_radial_gradient = gdip_set_radial_gradient; | |
| driver->stencil_set_gradient_interpolation = gdip_set_gradient_interpolation; | |
| driver->stencil_set_vertex_path = gdip_set_vertex_path; | |
| driver->stencil_set_vertex_center = gdip_set_vertex_center; | |
| driver->stencil_set_vertex_colors = gdip_set_vertex_colors; | |
| } |