/* This file is part of the Q programming system. The Q programming system is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. The Q programming system 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include #include "ggilib.h" typedef struct { ggi_visual_t vis; void *abuf; unsigned char asz; unsigned short afg, abg; int stride, lines; } visual_t; /* calculate the size of a pixel pack */ static inline int pack_size(ggi_visual_t vis, int n) { int bpp, r; ggi_mode m; if (ggiGetMode(vis, &m)) return -1; bpp = GT_SIZE(m.graphtype); if (n > INT_MAX / bpp) return -1; r = ((bpp*n)%8>0)?1:0; return (bpp*n)/8+r; } /* clear clip area of alpha buffer */ int clear_alpha_buffer(void *_v) { visual_t *v = (visual_t*)_v; int x1, y1, x2, y2; if (ggiGetGCClipping(v->vis, &x1, &y1, &x2, &y2)) return -1; if (v->asz == 1) { unsigned char *abuf = (unsigned char*)v->abuf; int i, y; for (i = y1*v->stride+x1, y = y1; y < y2; y++, i += v->stride) memset(abuf+i, v->abg, x2-x1); } else { unsigned short *abuf = (unsigned short*)v->abuf; int i, j, x, y; for (i = y1*v->stride+x1, y = y1; y < y2; y++, i += v->stride) for (j = i, x = x1; x < x2; x++, j++) abuf[j] = v->abg; } return 0; } /* set alpha values, with clipping */ int set_alpha_box(void *_v, int x, int y, int w, int h, ggi_color *c) { visual_t *v = (visual_t*)_v; int x1, y1, x2, y2; int i, j, k, xx, yy; if (ggiGetGCClipping(v->vis, &x1, &y1, &x2, &y2)) return -1; if (v->asz == 1) { unsigned char *abuf = (unsigned char*)v->abuf; /* FIXME: optimize me! */ for (k = 0, i = y*v->stride+x, yy = y; yy < y+h; yy++, i += v->stride) for (j = i, xx = x; xx < x+w; xx++, j++, k++) if (xx >= x1 && xx < x2 && yy >= y1 && yy < y2) abuf[j] = c[k].a/0x101; } else { unsigned short *abuf = (unsigned short*)v->abuf; /* FIXME: optimize me! */ for (k = 0, i = y*v->stride+x, yy = y; yy < y+h; yy++, i += v->stride) for (j = i, xx = x; xx < x+w; xx++, j++, k++) if (xx >= x1 && xx < x2 && yy >= y1 && yy < y2) abuf[j] = c[k].a; } return 0; } /* same as above, but use current foreground color */ int draw_alpha_box(void *_v, int x, int y, int w, int h) { visual_t *v = (visual_t*)_v; int x1, y1, x2, y2; int i, j, xx, yy; if (ggiGetGCClipping(v->vis, &x1, &y1, &x2, &y2)) return -1; if (v->asz == 1) { unsigned char *abuf = (unsigned char*)v->abuf; /* FIXME: optimize me! */ for (i = y*v->stride+x, yy = y; yy < y+h; yy++, i += v->stride) for (j = i, xx = x; xx < x+w; xx++, j++) if (xx >= x1 && xx < x2 && yy >= y1 && yy < y2) abuf[j] = v->afg; } else { unsigned short *abuf = (unsigned short*)v->abuf; /* FIXME: optimize me! */ for (i = y*v->stride+x, yy = y; yy < y+h; yy++, i += v->stride) for (j = i, xx = x; xx < x+w; xx++, j++) if (xx >= x1 && xx < x2 && yy >= y1 && yy < y2) abuf[j] = v->afg; } return 0; } /* retrieve a rectangle of pixels from a visual, including alpha values */ int get_box(void *_v, int x, int y, int w, int h, ggi_color **c) { visual_t *v = (visual_t*)_v; int n = w*h, bpp, stride, ret; int xc = x, yc = y, wc = w, hc = h; int i, j, k, xx, yy; unsigned char *buf; ggi_mode m; *c = NULL; /* FIXME: we don't support packed non-byte-aligned pixels for now */ if (ggiGetMode(v->vis, &m) || (bpp = GT_SIZE(m.graphtype))%8 != 0) return 0; if (x >= v->stride || y >= v->lines || h <= 0 || w <= 0) return n; if (w > INT_MAX/h) return -1; if (!(buf = malloc(pack_size(v->vis, n)))) return -1; if (n > INT_MAX/sizeof(ggi_color) || !(*c = malloc(n*sizeof(ggi_color)))) { free(buf); return -1; } /* we do our own clipping here, since GGI doesn't handle it :( */ memset(buf, 0, n); memset(*c, 0, n*sizeof(ggi_color)); if (xc < 0) { wc += xc; xc = 0; } if (xc+wc > v->stride) wc = v->stride - xc; if (yc < 0) { hc += yc; yc = 0; } if (yc+hc > v->lines) hc = v->lines - yc; bpp /= 8; stride = w*bpp; if (xc == x && wc == w) { ret = ggiGetBox(v->vis, xc, yc, wc, hc, buf+(yc-y)*stride); } else { /* horizontal clipping, read the pixels line by line */ unsigned char *bufp; int off = (xc-x)*bpp; for (bufp = buf+(yc-y)*stride, yy = yc; yy < yc+hc; yy++, bufp += stride) if ((ret = ggiGetHLine(v->vis, xc, yy, wc, bufp+off))) break; } if (ret) { free(buf); free(*c); return 0; } if (ggiUnpackPixels(v->vis, buf, *c, n)) { free(buf); free(*c); return 0; } free(buf); /* get the alpha values from the alpha buffer if any */ if (v->abuf) { if (v->asz == 1) { unsigned char *abuf = (unsigned char*)v->abuf; /* FIXME: optimize me! */ for (i = y*v->stride+x, k = 0, yy = y; yy < y+h; yy++, i += v->stride) for (j = i, xx = x; xx < x+w; xx++, j++) if (xx >= 0 && xx < v->stride && yy >= 0 && yy < v->lines) (*c)[k++].a = abuf[j]*0x101; else (*c)[k++].a = 0xffff; } else { unsigned short *abuf = (unsigned short*)v->abuf; /* FIXME: optimize me! */ for (i = y*v->stride+x, k = 0, yy = y; yy < y+h; yy++, i += v->stride) for (j = i, xx = x; xx < x+w; xx++, j++) if (xx >= 0 && xx < v->stride && yy >= 0 && yy < v->lines) (*c)[k++].a = abuf[j]; else (*c)[k++].a = 0xffff; } } else { /* default alpha values */ int i; for (i = 0; i < n; i++) (*c)[i].a = 0xffff; } return n; } /* perform alpha blending */ static int blend_box(visual_t *v, int x, int y, int w, int h, ggi_color *c, ggi_color **d) { int i, n = w*h; *d = c; if (!v->abuf) return n; if ((n = get_box(v, x, y, w, h, d)) <= 0 || !(*d)) return n; for (i = 0; i < n; i++) { (*d)[i].r = (((unsigned long)c[i].a)*((unsigned long)c[i].r) + ((unsigned long)0xffff-c[i].a)*((unsigned long)(*d)[i].r))/0xffff; (*d)[i].g = (((unsigned long)c[i].a)*((unsigned long)c[i].g) + ((unsigned long)0xffff-c[i].a)*((unsigned long)(*d)[i].g))/0xffff; (*d)[i].b = (((unsigned long)c[i].a)*((unsigned long)c[i].b) + ((unsigned long)0xffff-c[i].a)*((unsigned long)(*d)[i].b))/0xffff; (*d)[i].a = (0xffffL*((unsigned long)c[i].a) + ((unsigned long)0xffff-c[i].a)*((unsigned long)(*d)[i].a))/0xffff; } return n; } /* same, for single source pixel */ static int blend_box1(visual_t *v, int x, int y, int w, int h, ggi_color *c, ggi_color **d) { int i, n = w*h; *d = NULL; if (!v->abuf || c->a == 0xffff) return n; if ((n = get_box(v, x, y, w, h, d)) <= 0 || !(*d)) return n; for (i = 0; i < n; i++) { (*d)[i].r = (((unsigned long)c->a)*((unsigned long)c->r) + ((unsigned long)0xffff-c->a)*((unsigned long)(*d)[i].r))/0xffff; (*d)[i].g = (((unsigned long)c->a)*((unsigned long)c->g) + ((unsigned long)0xffff-c->a)*((unsigned long)(*d)[i].g))/0xffff; (*d)[i].b = (((unsigned long)c->a)*((unsigned long)c->b) + ((unsigned long)0xffff-c->a)*((unsigned long)(*d)[i].b))/0xffff; (*d)[i].a = (0xffffL*((unsigned long)c->a) + ((unsigned long)0xffff-c->a)*((unsigned long)(*d)[i].a))/0xffff; } return n; } /* store a rectangle of pixels in a visual, with alpha blending */ int put_box(void *_v, int x, int y, int w, int h, ggi_color *c) { visual_t *v = (visual_t*)_v; int n = w*h, ret; unsigned char *buf; ggi_color *d = c; if (h <= 0 || w <= 0) return 0; if (w > INT_MAX/h) return -1; if ((ret = blend_box(v, x, y, w, h, c, &d)) <= 0 || !d) return ret; if (!(buf = malloc(pack_size(v->vis, n)))) return -1; ret = ggiPackColors(v->vis, buf, d, n) || ggiPutBox(v->vis, x, y, w, h, buf); free(buf); if (!ret && v->abuf) ret = set_alpha_box(v, x, y, w, h, d); if (d != c) free(d); if (ret) return 0; else return n; } /* same, with current foreground pixel */ int draw_box(void *_v, int x, int y, int w, int h) { visual_t *v = (visual_t*)_v; int n = w*h, ret; unsigned char *buf; ggi_pixel pix; ggi_color c, *d = NULL; if (h <= 0 || w <= 0) return 0; if (ggiGetGCForeground(v->vis, &pix) || ggiUnmapPixel(v->vis, pix, &c)) return 0; if (v->abuf) c.a = (v->asz==1)?(v->afg*0x101):v->afg; else c.a = 0xffff; if (w > INT_MAX/h) return -1; if ((ret = blend_box1(v, x, y, w, h, &c, &d)) <= 0) return ret; if (d) { if (!(buf = malloc(pack_size(v->vis, n)))) return -1; ret = ggiPackColors(v->vis, buf, d, n) || ggiPutBox(v->vis, x, y, w, h, buf); free(buf); if (!ret && v->abuf) ret = set_alpha_box(v, x, y, w, h, d); free(d); } else { ret = ggiDrawBox(v->vis, x, y, w, h); if (!ret && v->abuf) ret = draw_alpha_box(v, x, y, w, h); } if (ret) return 0; else return n; }