1 /*
2 * Copyright (c) 2007 Ivan Leben
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library in the file COPYING;
16 * if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18 *
19 */
20
21 #include <vg/openvg.h>
22 #include "shContext.h"
23 #include "shPaint.h"
24 #include <stdio.h>
25
26 #define _ITEM_T SHStop
27 #define _ARRAY_T SHStopArray
28 #define _FUNC_T shStopArray
29 #define _COMPARE_T(s1,s2) 0
30 #define _ARRAY_DEFINE
31 #include "shArrayBase.h"
32
33 #define _ITEM_T SHPaint*
34 #define _ARRAY_T SHPaintArray
35 #define _FUNC_T shPaintArray
36 #define _ARRAY_DEFINE
37 #include "shArrayBase.h"
38
39 // We currently do not use gradients which need textures, so disable them to
40 // prevent freeing resources outside the correct OpenGL thread/context.
41 #define SH_NO_PAINT_TEXTURE
42
SHPaint_ctor(SHPaint * p)43 void SHPaint_ctor(SHPaint *p)
44 {
45 int i;
46
47 p->type = VG_PAINT_TYPE_COLOR;
48 CSET(p->color, 0,0,0,1);
49 SH_INITOBJ(SHStopArray, p->instops);
50 SH_INITOBJ(SHStopArray, p->stops);
51 p->premultiplied = VG_FALSE;
52 p->spreadMode = VG_COLOR_RAMP_SPREAD_PAD;
53 p->tilingMode = VG_TILE_FILL;
54 for (i=0; i<4; ++i) p->linearGradient[i] = 0.0f;
55 for (i=0; i<5; ++i) p->radialGradient[i] = 0.0f;
56 p->pattern = VG_INVALID_HANDLE;
57
58 #ifndef SH_NO_PAINT_TEXTURE
59 glGenTextures(1, &p->texture);
60 glBindTexture(GL_TEXTURE_1D, p->texture);
61 glTexImage1D(GL_TEXTURE_1D, 0, GL_RGBA, SH_GRADIENT_TEX_SIZE, 0,
62 GL_RGBA, GL_FLOAT, NULL);
63 #else
64 p->texture = 0;
65 #endif
66 }
67
SHPaint_dtor(SHPaint * p)68 void SHPaint_dtor(SHPaint *p)
69 {
70 SH_DEINITOBJ(SHStopArray, p->instops);
71 SH_DEINITOBJ(SHStopArray, p->stops);
72
73 #ifndef SH_NO_PAINT_TEXTURE
74 if (glIsTexture(p->texture))
75 glDeleteTextures(1, &p->texture);
76 #endif
77 }
78
vgCreatePaint(void)79 VG_API_CALL VGPaint vgCreatePaint(void)
80 {
81 SHPaint *p = NULL;
82 VG_GETCONTEXT(VG_INVALID_HANDLE);
83
84 /* Create new paint object */
85 SH_NEWOBJ(SHPaint, p);
86 VG_RETURN_ERR_IF(!p, VG_OUT_OF_MEMORY_ERROR,
87 VG_INVALID_HANDLE);
88
89 /* Add to resource list */
90 shPaintArrayPushBack(&context->paints, p);
91
92 VG_RETURN((VGPaint)p);
93 }
94
vgDestroyPaint(VGPaint paint)95 VG_API_CALL void vgDestroyPaint(VGPaint paint)
96 {
97 SHint index;
98 VG_GETCONTEXT(VG_NO_RETVAL);
99
100 /* Check if handle valid */
101 index = shPaintArrayFind(&context->paints, (SHPaint*)paint);
102 VG_RETURN_ERR_IF(index == -1, VG_BAD_HANDLE_ERROR, VG_NO_RETVAL);
103
104 /* Delete object and remove resource */
105 SH_DELETEOBJ(SHPaint, (SHPaint*)paint);
106 shPaintArrayRemoveAt(&context->paints, index);
107
108 VG_RETURN(VG_NO_RETVAL);
109 }
110
vgSetPaint(VGPaint paint,VGbitfield paintModes)111 VG_API_CALL void vgSetPaint(VGPaint paint, VGbitfield paintModes)
112 {
113 VG_GETCONTEXT(VG_NO_RETVAL);
114
115 /* Check if handle valid */
116 VG_RETURN_ERR_IF(!shIsValidPaint(context, paint) &&
117 paint != VG_INVALID_HANDLE,
118 VG_BAD_HANDLE_ERROR, VG_NO_RETVAL);
119
120 /* Check for invalid mode */
121 VG_RETURN_ERR_IF(paintModes & ~(VG_STROKE_PATH | VG_FILL_PATH),
122 VG_ILLEGAL_ARGUMENT_ERROR, VG_NO_RETVAL);
123
124 /* Set stroke / fill */
125 if (paintModes & VG_STROKE_PATH)
126 context->strokePaint = (SHPaint*)paint;
127 if (paintModes & VG_FILL_PATH)
128 context->fillPaint = (SHPaint*)paint;
129
130 VG_RETURN(VG_NO_RETVAL);
131 }
132
vgPaintPattern(VGPaint paint,VGImage pattern)133 VG_API_CALL void vgPaintPattern(VGPaint paint, VGImage pattern)
134 {
135 VG_GETCONTEXT(VG_NO_RETVAL);
136
137 /* Check if handle valid */
138 VG_RETURN_ERR_IF(!shIsValidPaint(context, paint),
139 VG_BAD_HANDLE_ERROR, VG_NO_RETVAL);
140
141 /* Check if pattern image valid */
142 VG_RETURN_ERR_IF(!shIsValidImage(context, pattern),
143 VG_BAD_HANDLE_ERROR, VG_NO_RETVAL);
144
145 /* TODO: Check if pattern image is current rendering target */
146
147 /* Set pattern image */
148 ((SHPaint*)paint)->pattern = pattern;
149
150 VG_RETURN(VG_NO_RETVAL);
151 }
152
shUpdateColorRampTexture(SHPaint * p)153 void shUpdateColorRampTexture(SHPaint *p)
154 {
155 #ifndef SH_NO_PAINT_TEXTURE
156 SHint s=0;
157 SHStop *stop1, *stop2;
158 SHfloat rgba[SH_GRADIENT_TEX_COORDSIZE];
159 SHint x1=0, x2=0, dx, x;
160 SHColor dc, c;
161 SHfloat k;
162
163 /* Write first pixel color */
164 stop1 = &p->stops.items[0];
165 CSTORE_RGBA1D_F(stop1->color, rgba, x1);
166
167 /* Walk stops */
168 for (s=1; s<p->stops.size; ++s, x1=x2, stop1=stop2) {
169
170 /* Pick next stop */
171 stop2 = &p->stops.items[s];
172 x2 = (SHint)(stop2->offset * (SH_GRADIENT_TEX_SIZE-1));
173
174 SH_ASSERT(x1 >= 0 && x1 < SH_GRADIENT_TEX_SIZE &&
175 x2 >= 0 && x2 < SH_GRADIENT_TEX_SIZE &&
176 x1 <= x2);
177
178 dx = x2 - x1;
179 CSUBCTO(stop2->color, stop1->color, dc);
180
181 /* Interpolate inbetween */
182 for (x=x1+1; x<=x2; ++x) {
183
184 k = (SHfloat)(x-x1)/dx;
185 CSETC(c, stop1->color);
186 CADDCK(c, dc, k);
187 CSTORE_RGBA1D_F(c, rgba, x);
188 }
189 }
190
191 /* Update texture image */
192 glBindTexture(GL_TEXTURE_1D, p->texture);
193 glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
194 glTexSubImage1D(GL_TEXTURE_1D, 0, 0, SH_GRADIENT_TEX_SIZE,
195 GL_RGBA, GL_FLOAT, rgba);
196 #else
197 printf("ShivaVG: gradients not supported!");
198 #endif
199 }
200
shValidateInputStops(SHPaint * p)201 void shValidateInputStops(SHPaint *p)
202 {
203 SHStop *instop, stop = {0, {0,0,0,0}};
204 SHfloat lastOffset=0.0f;
205 int i;
206
207 shStopArrayClear(&p->stops);
208 shStopArrayReserve(&p->stops, p->instops.size);
209
210 /* Assure input stops are properly defined */
211 for (i=0; i<p->instops.size; ++i) {
212
213 /* Copy stop color */
214 instop = &p->instops.items[i];
215 stop.color = instop->color;
216
217 /* Offset must be in [0,1] */
218 if (instop->offset < 0.0f || instop->offset > 1.0f)
219 continue;
220
221 /* Discard whole sequence if not in ascending order */
222 if (instop->offset < lastOffset)
223 {shStopArrayClear(&p->stops); break;}
224
225 /* Add stop at offset 0 with same color if first not at 0 */
226 if (p->stops.size == 0 && instop->offset != 0.0f) {
227 stop.offset = 0.0f;
228 shStopArrayPushBackP(&p->stops, &stop);}
229
230 /* Add current stop to array */
231 stop.offset = instop->offset;
232 shStopArrayPushBackP(&p->stops, &stop);
233
234 /* Save last offset */
235 lastOffset = instop->offset;
236 }
237
238 /* Add stop at offset 1 with same color if last not at 1 */
239 if (p->stops.size > 0 && lastOffset != 1.0f) {
240 stop.offset = 1.0f;
241 shStopArrayPushBackP(&p->stops, &stop);
242 }
243
244 /* Add 2 default stops if no valid found */
245 if (p->stops.size == 0) {
246 /* First opaque black */
247 stop.offset = 0.0f;
248 CSET(stop.color, 0,0,0,1);
249 shStopArrayPushBackP(&p->stops, &stop);
250 /* Last opaque white */
251 stop.offset = 1.0f;
252 CSET(stop.color, 1,1,1,1);
253 shStopArrayPushBackP(&p->stops, &stop);
254 }
255
256 /* Update texture */
257 shUpdateColorRampTexture(p);
258 }
259
shGenerateStops(SHPaint * p,SHfloat minOffset,SHfloat maxOffset,SHStopArray * outStops)260 void shGenerateStops(SHPaint *p, SHfloat minOffset, SHfloat maxOffset,
261 SHStopArray *outStops)
262 {
263 SHStop *s1,*s2;
264 SHint i1,i2;
265 SHfloat o=0.0f;
266 SHfloat ostep=0.0f;
267 SHint istep=1;
268 SHint istart=0;
269 SHint iend=p->stops.size-1;
270 SHint minDone=0;
271 SHint maxDone=0;
272 SHStop outStop;
273
274 /* Start below zero? */
275 if (minOffset < 0.0f) {
276 if (p->spreadMode == VG_COLOR_RAMP_SPREAD_PAD) {
277 /* Add min offset stop */
278 outStop = p->stops.items[0];
279 outStop.offset = minOffset;
280 shStopArrayPushBackP(outStops, &outStop);
281 /* Add max offset stop and exit */
282 if (maxOffset < 0.0f) {
283 outStop.offset = maxOffset;
284 shStopArrayPushBackP(outStops, &outStop);
285 return; }
286 }else{
287 /* Pad starting offset to nearest factor of 2 */
288 SHint ioff = (SHint)SH_FLOOR(minOffset);
289 o = (SHfloat)(ioff - (ioff & 1));
290 }
291 }
292
293 /* Construct stops until max offset reached */
294 for (i1=istart, i2=istart+istep; maxDone!=1;
295 i1+=istep, i2+=istep, o+=ostep) {
296
297 /* All stops consumed? */
298 if (i1==iend) { switch(p->spreadMode) {
299
300 case VG_COLOR_RAMP_SPREAD_PAD:
301 /* Pick last stop */
302 outStop = p->stops.items[i1];
303 if (!minDone) {
304 /* Add min offset stop with last color */
305 outStop.offset = minOffset;
306 shStopArrayPushBackP(outStops, &outStop); }
307 /* Add max offset stop with last color */
308 outStop.offset = maxOffset;
309 shStopArrayPushBackP(outStops, &outStop);
310 return;
311
312 case VG_COLOR_RAMP_SPREAD_REPEAT:
313 /* Reset iteration */
314 i1=istart; i2=istart+istep;
315 /* Add stop1 if past min offset */
316 if (minDone) {
317 outStop = p->stops.items[0];
318 outStop.offset = o;
319 shStopArrayPushBackP(outStops, &outStop); }
320 break;
321
322 case VG_COLOR_RAMP_SPREAD_REFLECT:
323 /* Reflect iteration direction */
324 istep = -istep;
325 i2 = i1 + istep;
326 iend = (istep==1) ? p->stops.size-1 : 0;
327 break;
328 }
329 }
330
331 /* 2 stops and their offset distance */
332 s1 = &p->stops.items[i1];
333 s2 = &p->stops.items[i2];
334 ostep = s2->offset - s1->offset;
335 ostep = SH_ABS(ostep);
336
337 /* Add stop1 if reached min offset */
338 if (!minDone && o+ostep > minOffset) {
339 minDone = 1;
340 outStop = *s1;
341 outStop.offset = o;
342 shStopArrayPushBackP(outStops, &outStop);
343 }
344
345 /* Mark done if reached max offset */
346 if (o+ostep > maxOffset)
347 maxDone = 1;
348
349 /* Add stop2 if past min offset */
350 if (minDone) {
351 outStop = *s2;
352 outStop.offset = o+ostep;
353 shStopArrayPushBackP(outStops, &outStop);
354 }
355 }
356 }
357
shSetGradientTexGLState(SHPaint * p)358 void shSetGradientTexGLState(SHPaint *p)
359 {
360 #ifndef SH_NO_PAINT_TEXTURE
361 glBindTexture(GL_TEXTURE_1D, p->texture);
362 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
363 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
364
365 switch (p->spreadMode) {
366 case VG_COLOR_RAMP_SPREAD_PAD:
367 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); break;
368 case VG_COLOR_RAMP_SPREAD_REPEAT:
369 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_REPEAT); break;
370 case VG_COLOR_RAMP_SPREAD_REFLECT:
371 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_MIRRORED_REPEAT); break;
372 }
373
374 glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
375 glColor4f(1,1,1,1);
376 #else
377 printf("ShivaVG: gradients not supported!");
378 #endif
379 }
380
shSetPatternTexGLState(SHPaint * p,VGContext * c)381 void shSetPatternTexGLState(SHPaint *p, VGContext *c)
382 {
383 glBindTexture(GL_TEXTURE_2D, ((SHImage*)p->pattern)->texture);
384 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
385 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
386
387 switch(p->tilingMode) {
388 case VG_TILE_FILL:
389 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
390 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
391 glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR,
392 (GLfloat*)&c->tileFillColor);
393 break;
394 case VG_TILE_PAD:
395 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
396 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
397 break;
398 case VG_TILE_REPEAT:
399 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
400 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
401 break;
402 case VG_TILE_REFLECT:
403 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_MIRRORED_REPEAT);
404 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_MIRRORED_REPEAT);
405 break;
406 }
407
408 glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
409 glColor4f(1,1,1,1);
410 }
411
shDrawLinearGradientMesh(SHPaint * p,SHVector2 * min,SHVector2 * max,VGPaintMode mode,GLenum texUnit)412 int shDrawLinearGradientMesh(SHPaint *p, SHVector2 *min, SHVector2 *max,
413 VGPaintMode mode, GLenum texUnit)
414 {
415 SHint i;
416 SHfloat n;
417
418 SHfloat x1 = p->linearGradient[0];
419 SHfloat y1 = p->linearGradient[1];
420 SHfloat x2 = p->linearGradient[2];
421 SHfloat y2 = p->linearGradient[3];
422 SHVector2 c, ux, uy;
423 SHVector2 cc, uux, uuy;
424
425 SHMatrix3x3 *m = 0;
426 SHMatrix3x3 mi;
427 SHint invertible;
428 SHVector2 corners[4];
429 SHfloat minOffset = 0.0f;
430 SHfloat maxOffset = 0.0f;
431 SHfloat left = 0.0f;
432 SHfloat right = 0.0f;
433 SHVector2 l1,r1,l2,r2;
434
435 /* Pick paint transform matrix */
436 SH_GETCONTEXT(0);
437 if (mode == VG_FILL_PATH)
438 m = &context->fillTransform;
439 else if (mode == VG_STROKE_PATH)
440 m = &context->strokeTransform;
441
442 /* Gradient center and unit vectors */
443 SET2(c, x1, y1);
444 SET2(ux, x2-x1, y2-y1);
445 SET2(uy, -ux.y, ux.x);
446 n = NORM2(ux);
447 DIV2(ux, n);
448 NORMALIZE2(uy);
449
450 /* Apply paint-to-user transformation */
451 ADD2V(ux, c); ADD2V(uy, c);
452 TRANSFORM2TO(c, (*m), cc);
453 TRANSFORM2TO(ux, (*m), uux);
454 TRANSFORM2TO(uy, (*m), uuy);
455 SUB2V(ux,c); SUB2V(uy,c);
456 SUB2V(uux,cc); SUB2V(uuy,cc);
457
458 /* Boundbox corners */
459 SET2(corners[0], min->x, min->y);
460 SET2(corners[1], max->x, min->y);
461 SET2(corners[2], max->x, max->y);
462 SET2(corners[3], min->x, max->y);
463
464 /* Find inverse transformation (back to paint space) */
465 invertible = shInvertMatrix(m, &mi);
466 if (!invertible || n==0.0f) {
467
468 /* Fill boundbox with color at offset 1 */
469 SHColor *c = &p->stops.items[p->stops.size-1].color;
470 glColor4fv((GLfloat*)c); glBegin(GL_QUADS);
471 for (i=0; i<4; ++i) glVertex2fv((GLfloat*)&corners[i]);
472 glEnd();
473 return 1;
474 }
475
476 /*--------------------------------------------------------*/
477
478 for (i=0; i<4; ++i) {
479
480 /* Find min/max offset and perpendicular span */
481 SHfloat o, s;
482 TRANSFORM2(corners[i], mi);
483 SUB2V(corners[i], c);
484 o = DOT2(corners[i], ux) / n;
485 s = DOT2(corners[i], uy);
486 if (o < minOffset || i==0) minOffset = o;
487 if (o > maxOffset || i==0) maxOffset = o;
488 if (s < left || i==0) left = s;
489 if (s > right || i==0) right = s;
490 }
491
492 /*---------------------------------------------------------*/
493
494 /* Corners of boundbox in gradient system */
495 SET2V(l1, cc); SET2V(r1, cc);
496 SET2V(l2, cc); SET2V(r2, cc);
497 OFFSET2V(l1, uuy, left); OFFSET2V(l1, uux, minOffset * n);
498 OFFSET2V(r1, uuy, right); OFFSET2V(r1, uux, minOffset * n);
499 OFFSET2V(l2, uuy, left); OFFSET2V(l2, uux, maxOffset * n);
500 OFFSET2V(r2, uuy, right); OFFSET2V(r2, uux, maxOffset * n);
501
502 /* Draw quad using color-ramp texture */
503 glActiveTexture(texUnit);
504 shSetGradientTexGLState(p);
505
506 glEnable(GL_TEXTURE_1D);
507 glBegin(GL_QUAD_STRIP);
508
509 glMultiTexCoord1f(texUnit, minOffset);
510 glVertex2fv((GLfloat*)&r1);
511 glVertex2fv((GLfloat*)&l1);
512
513 glMultiTexCoord1f(texUnit, maxOffset);
514 glVertex2fv((GLfloat*)&r2);
515 glVertex2fv((GLfloat*)&l2);
516
517 glEnd();
518 glDisable(GL_TEXTURE_1D);
519
520 return 1;
521 }
522
shDrawRadialGradientMesh(SHPaint * p,SHVector2 * min,SHVector2 * max,VGPaintMode mode,GLenum texUnit)523 int shDrawRadialGradientMesh(SHPaint *p, SHVector2 *min, SHVector2 *max,
524 VGPaintMode mode, GLenum texUnit)
525 {
526 SHint i, j;
527 float a, n;
528
529 SHfloat cx = p->radialGradient[0];
530 SHfloat cy = p->radialGradient[1];
531 SHfloat fx = p->radialGradient[2];
532 SHfloat fy = p->radialGradient[3];
533 float r = p->radialGradient[4];
534 float fcx, fcy, rr, C;
535
536 SHVector2 ux;
537 SHVector2 uy;
538 SHVector2 c, f;
539 SHVector2 cf;
540
541 SHMatrix3x3 *m = 0;
542 SHMatrix3x3 mi;
543 SHint invertible;
544 SHVector2 corners[4];
545 SHVector2 fcorners[4];
546 SHfloat minOffset=0.0f;
547 SHfloat maxOffset=0.0f;
548
549 SHint maxI=0, maxJ=0;
550 SHfloat maxA=0.0f;
551 SHfloat startA=0.0f;
552
553 int numsteps = 100;
554 float step = 2*PI/numsteps;
555 SHVector2 tmin, tmax;
556 SHVector2 min1, max1, min2, max2;
557
558 /* Pick paint transform matrix */
559 SH_GETCONTEXT(0);
560 if (mode == VG_FILL_PATH)
561 m = &context->fillTransform;
562 else if (mode == VG_STROKE_PATH)
563 m = &context->strokeTransform;
564
565 /* Move focus into circle if outside */
566 SET2(cf, fx,fy);
567 SUB2(cf, cx,cy);
568 n = NORM2(cf);
569 if (n > r) {
570 DIV2(cf, n);
571 fx = cx + 0.995f * r * cf.x;
572 fy = cy + 0.995f * r * cf.y;
573 }
574
575 /* Precalculations */
576 rr = r*r;
577 fcx = fx - cx;
578 fcy = fy - cy;
579 C = fcx*fcx + fcy*fcy - rr;
580
581 /* Apply paint-to-user transformation
582 to focus and unit vectors */
583 SET2(f, fx, fy);
584 SET2(c, cx, cy);
585 SET2(ux, 1, 0);
586 SET2(uy, 0, 1);
587 ADD2(ux, cx, cy);
588 ADD2(uy, cx, cy);
589 TRANSFORM2(f, (*m));
590 TRANSFORM2(c, (*m));
591 TRANSFORM2(ux, (*m));
592 TRANSFORM2(uy, (*m));
593 SUB2V(ux, c); SUB2V(uy, c);
594
595 /* Boundbox corners */
596 SET2(corners[0], min->x, min->y);
597 SET2(corners[1], max->x, min->y);
598 SET2(corners[2], max->x, max->y);
599 SET2(corners[3], min->x, max->y);
600
601 /* Find inverse transformation (back to paint space) */
602 invertible = shInvertMatrix(m, &mi);
603 if (!invertible || r <= 0.0f) {
604
605 /* Fill boundbox with color at offset 1 */
606 SHColor *c = &p->stops.items[p->stops.size-1].color;
607 glColor4fv((GLfloat*)c); glBegin(GL_QUADS);
608 for (i=0; i<4; ++i) glVertex2fv((GLfloat*)&corners[i]);
609 glEnd();
610 return 1;
611 }
612
613 /*--------------------------------------------------------*/
614
615 /* Find min/max offset */
616 for (i=0; i<4; ++i) {
617
618 /* Transform to paint space */
619 SHfloat ax,ay, A,B,D,t, off;
620 TRANSFORM2TO(corners[i], mi, fcorners[i]);
621 SUB2(fcorners[i], fx, fy);
622 n = NORM2(fcorners[i]);
623 if (n == 0.0f) {
624
625 /* Avoid zero-length vectors */
626 off = 0.0f;
627
628 }else{
629
630 /* Distance from focus to circle at corner angle */
631 DIV2(fcorners[i], n);
632 ax = fcorners[i].x;
633 ay = fcorners[i].y;
634 A = ax*ax + ay*ay;
635 B = 2 * (fcx*ax + fcy*ay);
636 D = B*B - 4*A*C;
637 t = (-B + SH_SQRT(D)) / (2*A);
638
639 /* Relative offset of boundbox corner */
640 if (D <= 0.0f) off = 1.0f;
641 else off = n / t;
642 }
643
644 /* Find smallest and largest offset */
645 if (off < minOffset || i==0) minOffset = off;
646 if (off > maxOffset || i==0) maxOffset = off;
647 }
648
649 /* Is transformed focus inside original boundbox? */
650 if (f.x >= min->x && f.x <= max->x &&
651 f.y >= min->y && f.y <= max->y) {
652
653 /* Draw whole circle */
654 minOffset = 0.0f;
655 startA = 0.0f;
656 maxA = 2*PI;
657
658 }else{
659
660 /* Find most distant corner pair */
661 for (i=0; i<3; ++i) {
662 if (ISZERO2(fcorners[i])) continue;
663 for (j=i+1; j<4; ++j) {
664 if (ISZERO2(fcorners[j])) continue;
665 a = ANGLE2N(fcorners[i], fcorners[j]);
666 if (a > maxA || maxA == 0.0f)
667 {maxA=a; maxI=i; maxJ=j;}
668 }}
669
670 /* Pick starting angle */
671 if (CROSS2(fcorners[maxI],fcorners[maxJ]) > 0.0f)
672 startA = shVectorOrientation(&fcorners[maxI]);
673 else startA = shVectorOrientation(&fcorners[maxJ]);
674 }
675
676 /*---------------------------------------------------------*/
677
678 /* TODO: for minOffset we'd actually need to find minimum
679 of the gradient function when X and Y are substitued
680 with a line equation for each bound-box edge. As a
681 workaround we use 0.0f for now. */
682 minOffset = 0.0f;
683 step = PI/50;
684 numsteps = (SHint)SH_CEIL(maxA / step) + 1;
685
686 glActiveTexture(texUnit);
687 shSetGradientTexGLState(p);
688
689 glEnable(GL_TEXTURE_1D);
690 glBegin(GL_QUADS);
691
692 /* Walk the steps and draw gradient mesh */
693 for (i=0, a=startA; i<numsteps; ++i, a+=step) {
694
695 /* Distance from focus to circle border
696 at current angle (gradient space) */
697 float ax = SH_COS(a);
698 float ay = SH_SIN(a);
699 float A = ax*ax + ay*ay;
700 float B = 2 * (fcx*ax + fcy*ay);
701 float D = B*B - 4*A*C;
702 float t = (-B + SH_SQRT(D)) / (2*A);
703 if (D <= 0.0f) t = 0.0f;
704
705 /* Vectors pointing towards minimum and maximum
706 offset at current angle (gradient space) */
707 tmin.x = ax * t * minOffset;
708 tmin.y = ay * t * minOffset;
709 tmax.x = ax * t * maxOffset;
710 tmax.y = ay * t * maxOffset;
711
712 /* Transform back to user space */
713 min2.x = f.x + tmin.x * ux.x + tmin.y * uy.x;
714 min2.y = f.y + tmin.x * ux.y + tmin.y * uy.y;
715 max2.x = f.x + tmax.x * ux.x + tmax.y * uy.x;
716 max2.y = f.y + tmax.x * ux.y + tmax.y * uy.y;
717
718 /* Draw quad */
719 if (i!=0) {
720 glMultiTexCoord1f(texUnit, minOffset);
721 glVertex2fv((GLfloat*)&min1);
722 glVertex2fv((GLfloat*)&min2);
723 glMultiTexCoord1f(texUnit, maxOffset);
724 glVertex2fv((GLfloat*)&max2);
725 glVertex2fv((GLfloat*)&max1);
726 }
727
728 /* Save prev points */
729 min1 = min2;
730 max1 = max2;
731 }
732
733 glEnd();
734 glDisable(GL_TEXTURE_1D);
735
736 return 1;
737 }
738
shDrawPatternMesh(SHPaint * p,SHVector2 * min,SHVector2 * max,VGPaintMode mode,GLenum texUnit)739 int shDrawPatternMesh(SHPaint *p, SHVector2 *min, SHVector2 *max,
740 VGPaintMode mode, GLenum texUnit)
741 {
742 SHMatrix3x3 *m = 0;
743 SHMatrix3x3 mi;
744 SHfloat migl[16];
745 SHint invertible;
746 SHVector2 corners[4];
747 VGfloat sx, sy;
748 SHImage *img;
749 int i;
750
751 /* Pick paint transform matrix */
752 SH_GETCONTEXT(0);
753 if (mode == VG_FILL_PATH)
754 m = &context->fillTransform;
755 else if (mode == VG_STROKE_PATH)
756 m = &context->strokeTransform;
757
758 /* Boundbox corners */
759 SET2(corners[0], min->x, min->y);
760 SET2(corners[1], max->x, min->y);
761 SET2(corners[2], max->x, max->y);
762 SET2(corners[3], min->x, max->y);
763
764 /* Find inverse transformation (back to paint space) */
765 invertible = shInvertMatrix(m, &mi);
766 if (!invertible) {
767
768 /* Fill boundbox with tile fill color */
769 SHColor *c = &context->tileFillColor;
770 glColor4fv((GLfloat*)c); glBegin(GL_QUADS);
771 for (i=0; i<4; ++i) glVertex2fv((GLfloat*)&corners[i]);
772 glEnd();
773 return 1;
774 }
775
776
777 /* Setup texture coordinate transform */
778 img = (SHImage*)p->pattern;
779 sx = 1.0f/(VGfloat)img->texwidth;
780 sy = 1.0f/(VGfloat)img->texheight;
781
782 glActiveTexture(texUnit);
783 shMatrixToGL(&mi, migl);
784 glMatrixMode(GL_TEXTURE);
785 glPushMatrix();
786 glScalef(sx, sy, 1.0f);
787 glMultMatrixf(migl);
788
789
790 /* Draw boundbox with same texture coordinates
791 that will get transformed back to paint space */
792 shSetPatternTexGLState(p, context);
793 glEnable(GL_TEXTURE_2D);
794 glBegin(GL_QUADS);
795
796 for (i=0; i<4; ++i) {
797 glMultiTexCoord2f(texUnit, corners[i].x, corners[i].y);
798 glVertex2fv((GLfloat*)&corners[i]);
799 }
800
801 glEnd();
802 glDisable(GL_TEXTURE_2D);
803 glPopMatrix();
804 glMatrixMode(GL_MODELVIEW);
805 return 1;
806 }
807