/* 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 <limits.h>
#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 0;
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;
if (hc <= 0 || wc <= 0) {
free(buf); free(*c);
return 0;
}
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;
}
syntax highlighted by Code2HTML, v. 0.9.1