2 * Copyright (c) 2007 Ivan Leben
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.
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.
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
21 #include <vg/openvg.h>
22 #include "shContext.h"
26 #define _ITEM_T SHStop
27 #define _ARRAY_T SHStopArray
28 #define _FUNC_T shStopArray
29 #define _COMPARE_T(s1,s2) 0
31 #include "shArrayBase.h"
33 #define _ITEM_T SHPaint*
34 #define _ARRAY_T SHPaintArray
35 #define _FUNC_T shPaintArray
37 #include "shArrayBase.h"
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
43 void SHPaint_ctor(SHPaint *p)
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;
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);
68 void SHPaint_dtor(SHPaint *p)
70 SH_DEINITOBJ(SHStopArray, p->instops);
71 SH_DEINITOBJ(SHStopArray, p->stops);
73 #ifndef SH_NO_PAINT_TEXTURE
74 if (glIsTexture(p->texture))
75 glDeleteTextures(1, &p->texture);
79 VG_API_CALL VGPaint vgCreatePaint(void)
82 VG_GETCONTEXT(VG_INVALID_HANDLE);
84 /* Create new paint object */
85 SH_NEWOBJ(SHPaint, p);
86 VG_RETURN_ERR_IF(!p, VG_OUT_OF_MEMORY_ERROR,
89 /* Add to resource list */
90 shPaintArrayPushBack(&context->paints, p);
92 VG_RETURN((VGPaint)p);
95 VG_API_CALL void vgDestroyPaint(VGPaint paint)
98 VG_GETCONTEXT(VG_NO_RETVAL);
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);
104 /* Delete object and remove resource */
105 SH_DELETEOBJ(SHPaint, (SHPaint*)paint);
106 shPaintArrayRemoveAt(&context->paints, index);
108 VG_RETURN(VG_NO_RETVAL);
111 VG_API_CALL void vgSetPaint(VGPaint paint, VGbitfield paintModes)
113 VG_GETCONTEXT(VG_NO_RETVAL);
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);
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);
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;
130 VG_RETURN(VG_NO_RETVAL);
133 VG_API_CALL void vgPaintPattern(VGPaint paint, VGImage pattern)
135 VG_GETCONTEXT(VG_NO_RETVAL);
137 /* Check if handle valid */
138 VG_RETURN_ERR_IF(!shIsValidPaint(context, paint),
139 VG_BAD_HANDLE_ERROR, VG_NO_RETVAL);
141 /* Check if pattern image valid */
142 VG_RETURN_ERR_IF(!shIsValidImage(context, pattern),
143 VG_BAD_HANDLE_ERROR, VG_NO_RETVAL);
145 /* TODO: Check if pattern image is current rendering target */
147 /* Set pattern image */
148 ((SHPaint*)paint)->pattern = pattern;
150 VG_RETURN(VG_NO_RETVAL);
153 void shUpdateColorRampTexture(SHPaint *p)
155 #ifndef SH_NO_PAINT_TEXTURE
157 SHStop *stop1, *stop2;
158 SHfloat rgba[SH_GRADIENT_TEX_COORDSIZE];
159 SHint x1=0, x2=0, dx, x;
163 /* Write first pixel color */
164 stop1 = &p->stops.items[0];
165 CSTORE_RGBA1D_F(stop1->color, rgba, x1);
168 for (s=1; s<p->stops.size; ++s, x1=x2, stop1=stop2) {
171 stop2 = &p->stops.items[s];
172 x2 = (SHint)(stop2->offset * (SH_GRADIENT_TEX_SIZE-1));
174 SH_ASSERT(x1 >= 0 && x1 < SH_GRADIENT_TEX_SIZE &&
175 x2 >= 0 && x2 < SH_GRADIENT_TEX_SIZE &&
179 CSUBCTO(stop2->color, stop1->color, dc);
181 /* Interpolate inbetween */
182 for (x=x1+1; x<=x2; ++x) {
184 k = (SHfloat)(x-x1)/dx;
185 CSETC(c, stop1->color);
187 CSTORE_RGBA1D_F(c, rgba, x);
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);
197 printf("ShivaVG: gradients not supported!");
201 void shValidateInputStops(SHPaint *p)
203 SHStop *instop, stop = {0, {0,0,0,0}};
204 SHfloat lastOffset=0.0f;
207 shStopArrayClear(&p->stops);
208 shStopArrayReserve(&p->stops, p->instops.size);
210 /* Assure input stops are properly defined */
211 for (i=0; i<p->instops.size; ++i) {
213 /* Copy stop color */
214 instop = &p->instops.items[i];
215 stop.color = instop->color;
217 /* Offset must be in [0,1] */
218 if (instop->offset < 0.0f || instop->offset > 1.0f)
221 /* Discard whole sequence if not in ascending order */
222 if (instop->offset < lastOffset)
223 {shStopArrayClear(&p->stops); break;}
225 /* Add stop at offset 0 with same color if first not at 0 */
226 if (p->stops.size == 0 && instop->offset != 0.0f) {
228 shStopArrayPushBackP(&p->stops, &stop);}
230 /* Add current stop to array */
231 stop.offset = instop->offset;
232 shStopArrayPushBackP(&p->stops, &stop);
234 /* Save last offset */
235 lastOffset = instop->offset;
238 /* Add stop at offset 1 with same color if last not at 1 */
239 if (p->stops.size > 0 && lastOffset != 1.0f) {
241 shStopArrayPushBackP(&p->stops, &stop);
244 /* Add 2 default stops if no valid found */
245 if (p->stops.size == 0) {
246 /* First opaque black */
248 CSET(stop.color, 0,0,0,1);
249 shStopArrayPushBackP(&p->stops, &stop);
250 /* Last opaque white */
252 CSET(stop.color, 1,1,1,1);
253 shStopArrayPushBackP(&p->stops, &stop);
257 shUpdateColorRampTexture(p);
260 void shGenerateStops(SHPaint *p, SHfloat minOffset, SHfloat maxOffset,
261 SHStopArray *outStops)
269 SHint iend=p->stops.size-1;
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);
287 /* Pad starting offset to nearest factor of 2 */
288 SHint ioff = (SHint)SH_FLOOR(minOffset);
289 o = (SHfloat)(ioff - (ioff & 1));
293 /* Construct stops until max offset reached */
294 for (i1=istart, i2=istart+istep; maxDone!=1;
295 i1+=istep, i2+=istep, o+=ostep) {
297 /* All stops consumed? */
298 if (i1==iend) { switch(p->spreadMode) {
300 case VG_COLOR_RAMP_SPREAD_PAD:
302 outStop = p->stops.items[i1];
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);
312 case VG_COLOR_RAMP_SPREAD_REPEAT:
313 /* Reset iteration */
314 i1=istart; i2=istart+istep;
315 /* Add stop1 if past min offset */
317 outStop = p->stops.items[0];
319 shStopArrayPushBackP(outStops, &outStop); }
322 case VG_COLOR_RAMP_SPREAD_REFLECT:
323 /* Reflect iteration direction */
326 iend = (istep==1) ? p->stops.size-1 : 0;
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);
337 /* Add stop1 if reached min offset */
338 if (!minDone && o+ostep > minOffset) {
342 shStopArrayPushBackP(outStops, &outStop);
345 /* Mark done if reached max offset */
346 if (o+ostep > maxOffset)
349 /* Add stop2 if past min offset */
352 outStop.offset = o+ostep;
353 shStopArrayPushBackP(outStops, &outStop);
358 void shSetGradientTexGLState(SHPaint *p)
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);
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;
374 glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
377 printf("ShivaVG: gradients not supported!");
381 void shSetPatternTexGLState(SHPaint *p, VGContext *c)
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);
387 switch(p->tilingMode) {
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);
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);
399 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
400 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
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);
408 glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
412 int shDrawLinearGradientMesh(SHPaint *p, SHVector2 *min, SHVector2 *max,
413 VGPaintMode mode, GLenum texUnit)
418 SHfloat x1 = p->linearGradient[0];
419 SHfloat y1 = p->linearGradient[1];
420 SHfloat x2 = p->linearGradient[2];
421 SHfloat y2 = p->linearGradient[3];
423 SHVector2 cc, uux, uuy;
428 SHVector2 corners[4];
429 SHfloat minOffset = 0.0f;
430 SHfloat maxOffset = 0.0f;
432 SHfloat right = 0.0f;
433 SHVector2 l1,r1,l2,r2;
435 /* Pick paint transform matrix */
437 if (mode == VG_FILL_PATH)
438 m = &context->fillTransform;
439 else if (mode == VG_STROKE_PATH)
440 m = &context->strokeTransform;
442 /* Gradient center and unit vectors */
444 SET2(ux, x2-x1, y2-y1);
445 SET2(uy, -ux.y, ux.x);
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);
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);
464 /* Find inverse transformation (back to paint space) */
465 invertible = shInvertMatrix(m, &mi);
466 if (!invertible || n==0.0f) {
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]);
476 /*--------------------------------------------------------*/
478 for (i=0; i<4; ++i) {
480 /* Find min/max offset and perpendicular span */
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;
492 /*---------------------------------------------------------*/
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);
502 /* Draw quad using color-ramp texture */
503 glActiveTexture(texUnit);
504 shSetGradientTexGLState(p);
506 glEnable(GL_TEXTURE_1D);
507 glBegin(GL_QUAD_STRIP);
509 glMultiTexCoord1f(texUnit, minOffset);
510 glVertex2fv((GLfloat*)&r1);
511 glVertex2fv((GLfloat*)&l1);
513 glMultiTexCoord1f(texUnit, maxOffset);
514 glVertex2fv((GLfloat*)&r2);
515 glVertex2fv((GLfloat*)&l2);
518 glDisable(GL_TEXTURE_1D);
523 int shDrawRadialGradientMesh(SHPaint *p, SHVector2 *min, SHVector2 *max,
524 VGPaintMode mode, GLenum texUnit)
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;
544 SHVector2 corners[4];
545 SHVector2 fcorners[4];
546 SHfloat minOffset=0.0f;
547 SHfloat maxOffset=0.0f;
549 SHint maxI=0, maxJ=0;
554 float step = 2*PI/numsteps;
555 SHVector2 tmin, tmax;
556 SHVector2 min1, max1, min2, max2;
558 /* Pick paint transform matrix */
560 if (mode == VG_FILL_PATH)
561 m = &context->fillTransform;
562 else if (mode == VG_STROKE_PATH)
563 m = &context->strokeTransform;
565 /* Move focus into circle if outside */
571 fx = cx + 0.995f * r * cf.x;
572 fy = cy + 0.995f * r * cf.y;
575 /* Precalculations */
579 C = fcx*fcx + fcy*fcy - rr;
581 /* Apply paint-to-user transformation
582 to focus and unit vectors */
591 TRANSFORM2(ux, (*m));
592 TRANSFORM2(uy, (*m));
593 SUB2V(ux, c); SUB2V(uy, c);
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);
601 /* Find inverse transformation (back to paint space) */
602 invertible = shInvertMatrix(m, &mi);
603 if (!invertible || r <= 0.0f) {
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]);
613 /*--------------------------------------------------------*/
615 /* Find min/max offset */
616 for (i=0; i<4; ++i) {
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]);
625 /* Avoid zero-length vectors */
630 /* Distance from focus to circle at corner angle */
631 DIV2(fcorners[i], n);
635 B = 2 * (fcx*ax + fcy*ay);
637 t = (-B + SH_SQRT(D)) / (2*A);
639 /* Relative offset of boundbox corner */
640 if (D <= 0.0f) off = 1.0f;
644 /* Find smallest and largest offset */
645 if (off < minOffset || i==0) minOffset = off;
646 if (off > maxOffset || i==0) maxOffset = off;
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) {
653 /* Draw whole circle */
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;}
670 /* Pick starting angle */
671 if (CROSS2(fcorners[maxI],fcorners[maxJ]) > 0.0f)
672 startA = shVectorOrientation(&fcorners[maxI]);
673 else startA = shVectorOrientation(&fcorners[maxJ]);
676 /*---------------------------------------------------------*/
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. */
684 numsteps = (SHint)SH_CEIL(maxA / step) + 1;
686 glActiveTexture(texUnit);
687 shSetGradientTexGLState(p);
689 glEnable(GL_TEXTURE_1D);
692 /* Walk the steps and draw gradient mesh */
693 for (i=0, a=startA; i<numsteps; ++i, a+=step) {
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;
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;
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;
720 glMultiTexCoord1f(texUnit, minOffset);
721 glVertex2fv((GLfloat*)&min1);
722 glVertex2fv((GLfloat*)&min2);
723 glMultiTexCoord1f(texUnit, maxOffset);
724 glVertex2fv((GLfloat*)&max2);
725 glVertex2fv((GLfloat*)&max1);
728 /* Save prev points */
734 glDisable(GL_TEXTURE_1D);
739 int shDrawPatternMesh(SHPaint *p, SHVector2 *min, SHVector2 *max,
740 VGPaintMode mode, GLenum texUnit)
746 SHVector2 corners[4];
751 /* Pick paint transform matrix */
753 if (mode == VG_FILL_PATH)
754 m = &context->fillTransform;
755 else if (mode == VG_STROKE_PATH)
756 m = &context->strokeTransform;
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);
764 /* Find inverse transformation (back to paint space) */
765 invertible = shInvertMatrix(m, &mi);
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]);
777 /* Setup texture coordinate transform */
778 img = (SHImage*)p->pattern;
779 sx = 1.0f/(VGfloat)img->texwidth;
780 sy = 1.0f/(VGfloat)img->texheight;
782 glActiveTexture(texUnit);
783 shMatrixToGL(&mi, migl);
784 glMatrixMode(GL_TEXTURE);
786 glScalef(sx, sy, 1.0f);
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);
796 for (i=0; i<4; ++i) {
797 glMultiTexCoord2f(texUnit, corners[i].x, corners[i].y);
798 glVertex2fv((GLfloat*)&corners[i]);
802 glDisable(GL_TEXTURE_2D);
804 glMatrixMode(GL_MODELVIEW);