www.pudn.com > flash.rar > graphic.cpp
//////////////////////////////////////////////////////////// // Flash Plugin and Player // Copyright (C) 1998 Olivier Debon // // This program 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 // of the License, or (at your option) any later version. // // This program 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. // /////////////////////////////////////////////////////////////// // Author : Olivier Debon// #include "swf.h" #ifdef RCSID static char *rcsid = "$Id: graphic.cc,v 1.5 1999/09/03 15:17:40 ode Exp $"; #endif #include "sqrt.h" #define FULL_AA #define PRINT 0 typedef unsigned short TYPE; static char cmp8[256]; // 8bit colormap static long allocColor15(Color color) { return (color.red >> 3)<<10 | (color.green>>3)<<5 | (color.blue>>3); } #if 0 static long allocColor16_646(Color color) { return (color.red >> 2)<<10 | (color.green>>4)<<6 | (color.blue>>2); } #endif static long allocColor16_565(Color color) { return (color.red >> 3)<<11 | (color.green>>2)<<5 | (color.blue>>3); } static long allocColor24_32(Color color) { return (color.red)<<16 | (color.green)<<8 | color.blue; } static long allocColor8(Color color) { return cmp8[(color.red>>6)<<4 | (color.green>>6)<<2 | (color.blue>>6)]; } // Public GraphicDevice::GraphicDevice(FlashDisplay *fd) { int depth; flashDisplay = fd; bgInitialized = 0; // Reset flash refresh flag flashDisplay->flash_refresh = 0; /* 16 bits, RGB565 */ redMask = 0xF800; greenMask = 0x07E0; blueMask = 0x001F; bpp = 2; depth = 16; /* should be the actual window size */ targetWidth = fd->width; targetHeight = fd->height; bpl = fd->bpl; #if PRINT printf("Target Width = %d\n", targetWidth); printf("Target Height = %d\n", targetHeight); #endif zoom = FRAC; movieWidth = targetWidth; movieHeight = targetHeight; viewPort.xmin = 0; viewPort.xmax = targetWidth-1; viewPort.ymin = 0; viewPort.ymax = targetHeight-1; switch (bpp) { case 1: allocColor = allocColor8; redMask = 0xe0; greenMask = 0x18; blueMask = 0x07; break; case 2: if (depth == 16) { allocColor = allocColor16_565; } else if (depth == 15) { allocColor = allocColor15; } break; case 3: case 4: allocColor = allocColor24_32; break; } canvasBuffer = (unsigned char *) fd->pixels; adjust = new Matrix; foregroundColor.red = 0; foregroundColor.green = 0; foregroundColor.blue = 0; foregroundColor.alpha = ALPHA_OPAQUE; backgroundColor.red = 0; backgroundColor.green = 0; backgroundColor.blue = 0; backgroundColor.alpha = ALPHA_OPAQUE; showMore = 0; setClipping(0); // Reset setClipping(1); /* polygon rasterizer : handle memory errors ! */ height = targetHeight; segs = (Segment **)malloc(height * sizeof(Segment *)); memset(segs, 0, height * sizeof(Segment *)); ymin = height; ymax = -1; seg_pool = (Segment *)malloc(NB_SEGMENT_MAX * sizeof(Segment)); seg_pool_cur = seg_pool; } GraphicDevice::~GraphicDevice() { free(segs); free(seg_pool); if (adjust) { delete adjust; } } ///////////// PLATFORM INDEPENDENT Color * GraphicDevice::getColormap(Color *old, long n, Cxform *cxform) { Color *newCmp; newCmp = new Color[n]; if (newCmp == NULL) return NULL; if (cxform) { for(long i = 0; i < n; i++) { newCmp[i] = cxform->getColor(old[i]); newCmp[i].pixel = allocColor(newCmp[i]); } } else { for(long i = 0; i < n; i++) { newCmp[i].pixel = allocColor(old[i]); } } return newCmp; } ///////////// PLATFORM INDEPENDENT long GraphicDevice::getHeight() { return targetHeight; } ///////////// PLATFORM INDEPENDENT long GraphicDevice::getWidth() { return targetWidth; } ///////////// PLATFORM INDEPENDENT Color GraphicDevice::getForegroundColor() { return foregroundColor; } void GraphicDevice::setForegroundColor(Color color) { foregroundColor = color; } ///////////// PLATFORM INDEPENDENT Color GraphicDevice::getBackgroundColor() { return backgroundColor; } ///////////// PLATFORM INDEPENDENT int GraphicDevice::setBackgroundColor(Color color) { if (bgInitialized == 0) { backgroundColor = color; clearCanvas(); bgInitialized = 1; return 1; } return 0; } ///////////// PLATFORM INDEPENDENT void GraphicDevice::setMovieDimension(long width, long height) { float xAdjust, yAdjust; movieWidth = width; movieHeight = height; xAdjust = (float)targetWidth*zoom/(float)width; yAdjust = (float)targetHeight*zoom/(float)height; if (xAdjust < yAdjust) { adjust->a = xAdjust; adjust->d = xAdjust; adjust->ty = ((targetHeight*zoom) - (long)(height * xAdjust))/2; viewPort.ymin = adjust->ty/zoom; viewPort.ymax = targetHeight-viewPort.ymin-1; } else { adjust->a = yAdjust; adjust->d = yAdjust; adjust->tx = ((targetWidth*zoom) - (long)(width * yAdjust))/2; viewPort.xmin = adjust->tx/zoom; viewPort.xmax = targetWidth-viewPort.xmin-1; } if (viewPort.xmin < 0) viewPort.xmin = 0; if (viewPort.ymin < 0) viewPort.ymin = 0; if (viewPort.xmax >= targetWidth) viewPort.xmax = targetWidth-1; if (viewPort.ymax >= targetHeight) viewPort.ymax = targetHeight-1; } ///////////// PLATFORM INDEPENDENT void GraphicDevice::setMovieZoom(int z) { // incoming val is 20-based... z *= FRAC; z /= 20; if (z <= 0 || z > 100 * FRAC) return; zoom = z; setMovieDimension(movieWidth,movieHeight); } ///////////// PLATFORM INDEPENDENT void GraphicDevice::setMovieOffset(long x, long y) { adjust->tx = -zoom*x; adjust->ty = -zoom*y; } ///////////// PLATFORM INDEPENDENT void GraphicDevice::clearCanvas() { TYPE pixel; TYPE *point,*p; long h, w,n; if (!bgInitialized) return; pixel = allocColor(backgroundColor); // The following allows to test clipping //for(point = (TYPE *)canvasBuffer,n=0; n flash_refresh = 1; flashDisplay->clip_x = clip_rect.xmin; flashDisplay->clip_y = clip_rect.ymin; flashDisplay->clip_width = clip_rect.xmax-clip_rect.xmin; flashDisplay->clip_height = clip_rect.ymax-clip_rect.ymin; } ///////////// PLATFORM INDEPENDENT long GraphicDevice::clip(long &y, long &start, long &end) { long xmin,xend; if (y < clip_rect.ymin || y >= clip_rect.ymax) return 1; if (end <= start) return 1; xmin = clip_rect.xmin * FRAC; xend = clip_rect.xmax * FRAC; if (end <= xmin || start >= xend) return 1; if (start < xmin) start = xmin; if (end > xend) end = xend; return 0; } #define RED_MASK 0xF800 #define GREEN_MASK 0x07E0 #define BLUE_MASK 0x001F typedef unsigned short TYPE; /* alpha = 0 : select c1, alpha = 255 select c2 */ static inline unsigned long mix_alpha(unsigned long c1, unsigned long c2, int alpha) { long r1,r2,r; long g1,g2,g; long b1,b2,b; r1 = c1 & RED_MASK; r2 = c2 & RED_MASK; r = (((r2-r1)*alpha + r1 * 256) >> 8) & RED_MASK; g1 = c1 & GREEN_MASK; g2 = c2 & GREEN_MASK; g = (((g2-g1)*alpha + g1 * 256) >> 8) & GREEN_MASK; b1 = c1 & BLUE_MASK; b2 = c2 & BLUE_MASK; b = (((b2-b1)*alpha + b1 * 256) >> 8) & BLUE_MASK; return (r|g|b); } void GraphicDevice::fillLineAA(FillStyleDef *f, long y, long start, long end) { register long n; TYPE *line; TYPE *point,pixel; unsigned int alpha, start_alpha,end_alpha; if (clip(y,start,end)) return; line = (TYPE *)(canvasBuffer + bpl*y); alpha = f->color.alpha; pixel = f->color.pixel; if (alpha == ALPHA_OPAQUE) { start_alpha = 255 - ((start & (FRAC-1)) << (8-FRAC_BITS)); end_alpha = (end & (FRAC-1)) << (8-FRAC_BITS); start >>= FRAC_BITS; end >>= FRAC_BITS; point = &line[start]; if (start == end) { *point = mix_alpha(*point, pixel, start_alpha + end_alpha - 255); } else { n = end-start; if (start_alpha < 255) { *point = mix_alpha(*point, pixel, start_alpha); point++; n--; } while (n > 0) { *point = pixel; point++; n--; } if (end_alpha > 0) { *point = mix_alpha(*point, pixel, end_alpha); } } } else { start_alpha = 255 - ((start & (FRAC-1)) << (8-FRAC_BITS)); end_alpha = (end & (FRAC-1)) << (8-FRAC_BITS); start >>= FRAC_BITS; end >>= FRAC_BITS; point = &line[start]; if (start == end) { *point = mix_alpha(*point, pixel, ((start_alpha + end_alpha - 255) * alpha) >> 8); } else { n = end-start; if (start_alpha < 255) { *point = mix_alpha(*point, pixel, (start_alpha * alpha) >> 8); point++; n--; } while (n > 0) { *point = mix_alpha(*point, pixel, alpha); point++; n--; } if (end_alpha > 0) { *point = mix_alpha(*point, pixel, (end_alpha * alpha) >> 8); } } } } void GraphicDevice::fillLine(FillStyleDef *f, long y, long start, long end) { register long n; TYPE *line,*point; TYPE pixel; unsigned int alpha; if (clip(y,start,end)) return; start >>= FRAC_BITS; end >>= FRAC_BITS; line = (TYPE *)(canvasBuffer + bpl*y); point = &line[start]; n = end-start; pixel = f->color.pixel; alpha = f->color.alpha; if (alpha == ALPHA_OPAQUE) { while (n--) { *point = pixel; point++; } } else { while (n--) { *point = mix_alpha(*point, pixel, alpha); point++; } } } /* 16 bit assumed... easy to change */ void GraphicDevice::fillLineBitmap(FillStyleDef *f, long y, long start, long end) { int n; long x1,y1,dx,dy; Matrix *m = &f->bitmap_matrix; Bitmap *b = f->bitmap; unsigned char *pixels; unsigned short *p; Color *cmap; long pixbpl; TYPE pixel; int offset; unsigned char *alpha_table; /* safety test) */ if (!b) return; if (clip(y,start,end)) return; start /= FRAC; end /= FRAC; n = end - start; p = (unsigned short *) (this->canvasBuffer + this->bpl*y + start * 2); /* the coordinates in the image are normalized to 16 bits */ x1 = (long) (m->a * start + m->b * y + m->tx); y1 = (long) (m->c * start + m->d * y + m->ty); dx = (long) (m->a); dy = (long) (m->c); pixels = b->pixels; pixbpl = b->bpl; cmap = f->cmap; if (b->alpha_buf == NULL) { while (n) { if (x1 >= 0 && y1 >= 0 && (x1 >> 16) < b->width && (y1 >> 16) < b->height) { pixel = cmap[pixels[(y1 >> 16) * pixbpl + (x1 >> 16)]].pixel; *p = pixel; } x1 += dx; y1 += dy; p++; n--; } } else if (f->alpha_table) { alpha_table = f->alpha_table; while (n) { if (x1 >= 0 && y1 >= 0 && (x1 >> 16) < b->width && (y1 >> 16) < b->height) { offset = (y1 >> 16) * pixbpl + (x1 >> 16); pixel = cmap[pixels[offset]].pixel; *p = mix_alpha(*p, pixel, alpha_table[b->alpha_buf[offset]]); } x1 += dx; y1 += dy; p++; n--; } } else { while (n) { if (x1 >= 0 && y1 >= 0 && (x1 >> 16) < b->width && (y1 >> 16) < b->height) { offset = (y1 >> 16) * pixbpl + (x1 >> 16); pixel = cmap[pixels[offset]].pixel; *p = mix_alpha(*p, pixel, b->alpha_buf[offset]); } x1 += dx; y1 += dy; p++; n--; } } } /* XXX: clipping should be better handled */ void GraphicDevice::fillLineLG(Gradient *grad, long y, long start, long end) { long dr,r,v,r2; register long n; TYPE *line; TYPE *point; Color *cp,*ramp; Matrix *m = &grad->imat; unsigned int start_alpha,end_alpha; if (clip(y,start,end)) return; start_alpha = 255 - ((start & (FRAC-1)) << (8-FRAC_BITS)); end_alpha = (end & (FRAC-1)) << (8-FRAC_BITS); start /= FRAC; end /= FRAC; n = end-start; r = (long) (m->a * start + m->b * y + m->tx); dr = (long) (m->a); ramp = grad->ramp; line = (TYPE *)(canvasBuffer + bpl*y); point = &line[start]; r2 = r + n * dr; if ( ((r | r2) & ~255) == 0 ) { if (!grad->has_alpha) { #ifdef FULL_AA if (start_alpha < 255) { v = r>>16; *point = mix_alpha(*point, (TYPE)ramp[v].pixel, start_alpha); point++; r += dr; n--; } #endif /* FULL_AA */ while (n>0) { v = r>>16; *point = (TYPE)ramp[v].pixel; point++; r += dr; n--; } #ifdef FULL_AA if (end_alpha > 0) { v = r>>16; *point = mix_alpha(*point, (TYPE)ramp[v].pixel, end_alpha); } #endif /* FULL_AA */ } else { while (n--) { v = r>>16; cp = &ramp[v]; *point = mix_alpha(*point, cp->pixel, cp->alpha); point++; r += dr; } } } else { if (!grad->has_alpha) { #ifdef FULL_AA if (start_alpha < 255) { v = r>>16; if (v < 0) v = 0; else if (v > 255) v = 255; *point = mix_alpha(*point, (TYPE)ramp[v].pixel, start_alpha); point++; r += dr; n--; } #endif /* FULL_AA */ while (n>0) { v = r>>16; if (v < 0) v = 0; else if (v > 255) v = 255; *point = (TYPE)ramp[v].pixel; point++; r += dr; n--; } #ifdef FULL_AA if (end_alpha > 0) { v = r>>16; if (v < 0) v = 0; else if (v > 255) v = 255; *point = mix_alpha(*point, (TYPE)ramp[v].pixel, end_alpha); } #endif /* FULL_AA */ } else { while (n--) { v = r>>16; if (v < 0) v = 0; else if (v > 255) v = 255; cp = &ramp[v]; *point = mix_alpha(*point, cp->pixel, cp->alpha); point++; r += dr; } } } } ///////////// PLATFORM INDEPENDENT void GraphicDevice::fillLineRG(Gradient *grad, long y, long start, long end) { long X,dx,r,Y,dy; long dist2; register long n; Color *cp,*ramp; TYPE *line; TYPE *point; Matrix *m = &grad->imat; unsigned int start_alpha,end_alpha; if (clip(y,start,end)) return; start_alpha = 255 - ((start & (FRAC-1)) << (8-FRAC_BITS)); end_alpha = (end & (FRAC-1)) << (8-FRAC_BITS); start /= FRAC; end /= FRAC; n = end-start; X = (long) (m->a * start + m->b * y + m->tx); Y = (long) (m->c * start + m->d * y + m->ty); dx = (long) (m->a); dy = (long) (m->c); ramp = grad->ramp; line = (TYPE *)(canvasBuffer + bpl*y); point = &line[start]; if (!grad->has_alpha) { #ifdef FULL_AA if (start == end) { dist2 = ((X>>16)*(X>>16))+((Y>>16)*(Y>>16)); if ((unsigned long)dist2 >= 65536) { r = 255; } else { r= SQRT[dist2]; } *point = mix_alpha(*point, (TYPE)ramp[r].pixel, start_alpha + end_alpha - 255); } else { if (start_alpha < 255) { dist2 = ((X>>16)*(X>>16))+((Y>>16)*(Y>>16)); if ((unsigned long)dist2 >= 65536) { r = 255; } else { r= SQRT[dist2]; } *point = mix_alpha(*point, (TYPE)ramp[r].pixel, start_alpha); point++; X += dx; Y += dy; n--; } #endif /* FULL_AA */ while (n>0) { dist2 = ((X>>16)*(X>>16))+((Y>>16)*(Y>>16)); if ((unsigned long)dist2 >= 65536) { r = 255; } else { r= SQRT[dist2]; } *point = (TYPE)ramp[r].pixel; point++; X += dx; Y += dy; n--; } #ifdef FULL_AA if (end_alpha > 0) { dist2 = ((X>>16)*(X>>16))+((Y>>16)*(Y>>16)); if ((unsigned long)dist2 >= 65536) { r = 255; } else { r= SQRT[dist2]; } *point = mix_alpha(*point, (TYPE)ramp[r].pixel, end_alpha); } } #endif /* FULL_AA */ } else { while (n--) { dist2 = ((X>>16)*(X>>16))+((Y>>16)*(Y>>16)); if ((unsigned long)dist2 >= 65536) { r = 255; } else { r= SQRT[dist2]; } cp = &ramp[r]; *point = mix_alpha(*point, cp->pixel, cp->alpha); point++; X += dx; Y += dy; } } } void GraphicDevice::drawBox(long x1, long y1, long x2, long y2) { int i; for(i=0;i > FRAC_BITS; y1 = (y1 + (FRAC/2)) >> FRAC_BITS; x2 = (x2 + (FRAC/2)) >> FRAC_BITS; y2 = (y2 + (FRAC/2)) >> FRAC_BITS; #else x1 = (x1) >> FRAC_BITS; y1 = (y1) >> FRAC_BITS; x2 = (x2) >> FRAC_BITS; y2 = (y2) >> FRAC_BITS; #endif if (y1 > y2 || (y1 == y2 && x1 > x2)) { long tmp; tmp=x1; x1=x2; x2=tmp; tmp=y1; y1=y2; y2=tmp; } if (y1 == y2 && (y1 < clip_rect.ymin || y1 > clip_rect.ymax)) return; if (x1 == x2 && (x1 < clip_rect.xmin || x1 > clip_rect.xmax)) return; if (x1 == x2 && y1 == y2) return; // Bad !!! if (y1 < clip_rect.ymin && y1 != y2) { x1 += (x2-x1)*(clip_rect.ymin-y1)/(y2-y1); y1 = clip_rect.ymin; } if (y2 > clip_rect.ymax && y1 != y2) { x2 -= (x2-x1)*(y2-clip_rect.ymax)/(y2-y1); y2 = clip_rect.ymax; } if (x1 < x2) { if (x1 < clip_rect.xmin && x1 != x2) { y1 += (y2-y1)*(clip_rect.xmin-x1)/(x2-x1); x1 = clip_rect.xmin; } if (x2 > clip_rect.xmax && x1 != x2) { y2 -= (y2-y1)*(x2-clip_rect.xmax)/(x2-x1); x2 = clip_rect.xmax; } } if (x1 > x2) { if (x2 < clip_rect.xmin && x2 != x1) { y2 -= (y2-y1)*(clip_rect.xmin-x2)/(x1-x2); x2 = clip_rect.xmin; } if (x1 > clip_rect.xmax && x2 != x1) { y1 += (y2-y1)*(x1-clip_rect.xmax)/(x1-x2); x1 = clip_rect.xmax; } } // Check again if (x1 == x2 && y1 == y2) return; if (x1 < clip_rect.xmin || x2 < clip_rect.xmin) return; if (y1 < clip_rect.ymin || y2 < clip_rect.ymin) return; if (x1 > clip_rect.xmax || x2 > clip_rect.xmax) return; if (y1 > clip_rect.ymax || y2 > clip_rect.ymax) return; sx=bpl >> 1; adr=(y1 * sx + x1); pp = (unsigned short *)canvasBuffer + adr; dx = x2 - x1; dy = y2 - y1; color = allocColor16_565(foregroundColor); alpha = foregroundColor.alpha; if (alpha == ALPHA_OPAQUE) { #define PUTPIXEL() \ { \ *pp=color; \ } #define DRAWLINE(dx,dy,inc_1,inc_2) \ n=dx;\ a=2*dy-dx;\ dy=2*dy;\ dx=2*dx-dy;\ do {\ PUTPIXEL();\ if (a>0) { pp+=(inc_1); a-=dx; }\ else { pp+=(inc_2); a+=dy; }\ } while (--n >= 0); /* fin macro */ if (dx == 0 && dy == 0) { PUTPIXEL(); } else if (dx > 0) { if (dx >= dy) { DRAWLINE(dx, dy, sx + 1, 1); } else { DRAWLINE(dy, dx, sx + 1, sx); } } else { dx = -dx; if (dx >= dy) { DRAWLINE(dx, dy, sx - 1, -1); } else { DRAWLINE(dy, dx, sx - 1, sx); } } #undef DRAWLINE #undef PUTPIXEL } else { #define PUTPIXEL() \ { \ *pp=mix_alpha(*pp,color,alpha); \ } #define DRAWLINE(dx,dy,inc_1,inc_2) \ n=dx;\ a=2*dy-dx;\ dy=2*dy;\ dx=2*dx-dy;\ do {\ PUTPIXEL();\ if (a>0) { pp+=(inc_1); a-=dx; }\ else { pp+=(inc_2); a+=dy; }\ } while (--n >= 0); /* fin macro */ if (dx == 0 && dy == 0) { PUTPIXEL(); } else if (dx > 0) { if (dx >= dy) { DRAWLINE(dx, dy, sx + 1, 1); } else { DRAWLINE(dy, dx, sx + 1, sx); } } else { dx = -dx; if (dx >= dy) { DRAWLINE(dx, dy, sx - 1, -1); } else { DRAWLINE(dy, dx, sx - 1, sx); } } #undef DRAWLINE #undef PUTPIXEL } } /************************************************************************/ /* polygon rasteriser */ static inline Segment *allocSeg(GraphicDevice *gd) { Segment *seg; if ( (gd->seg_pool_cur - gd->seg_pool) >= NB_SEGMENT_MAX ) return NULL; seg = gd->seg_pool_cur++; return seg; } /* add a segment to the current path */ /* XXX: handle reverse here ? */ /* XXX: should not malloc here */ /* XXX: handle y1 == y2 case (different for fill / line) */ void GraphicDevice::addSegment(long x1, long y1, long x2, long y2, FillStyleDef *f0, FillStyleDef *f1, int aa) { Segment *seg,**segs; long dX, X, Y, ymin, ymax, tmp; FillStyleDef *ff; #if 0 if ( ((y1 + (FRAC-1)) & ~(FRAC-1)) == ((y2 + (FRAC-1)) & ~(FRAC-1)) ) { return; } #else if ( y1 == y2 ) { return; } #endif if (y1 < y2) { ymin = y1; ymax = y2; ff = f0; f0 = f1; f1 = ff; } else { ymin = y2; ymax = y1; tmp = x1; x1 = x2; x2 = tmp; } if (ymin>>FRAC_BITS > clip_rect.ymax) { return; } X = x1 << SEGFRAC; dX = ((x2 - x1)< ymax) { //printf("Elimine @ y = %d ymin = %d, ymax = %d\n", Y, ymin, seg->ymax); return; } X += dX * (Y-ymin); Y >>= FRAC_BITS; if (Y >= clip_rect.ymax) { return; } seg = allocSeg(this); if (seg == NULL) { // printf("addSegment: too many segments\n");. return; } seg->next = 0; seg->nextValid = 0; seg->aa = aa; seg->ymax = ymax; seg->x1 = x1; seg->x2 = x2; seg->X = X; seg->dX = dX; seg->fs[0] = f0; seg->fs[1] = f1; if (Y < this->ymin) this->ymin = Y; ymax = (seg->ymax + FRAC - 1) >> FRAC_BITS; if (ymax >= this->height) ymax = this->height-1; if (ymax > this->ymax) this->ymax = ymax; segs = this->segs; if (segs[Y] == 0) { segs[Y] = seg; } else { Segment *s,*prev; prev = 0; for(s = segs[Y]; s; prev = s, s = s->next) { if (s->X > seg->X) { if (prev) { prev->next = seg; seg->next = s; } else { seg->next = segs[Y]; segs[Y] = seg; } break; } } if (s == 0) { prev->next = seg; seg->next = s; } } } static Segment *progressSegments(Segment * curSegs, long y) { Segment *seg,*prev; // Update current segments seg = curSegs; prev = 0; while(seg) { if ((y*FRAC) > seg->ymax) { // Remove this segment, no more valid if (prev) { prev->nextValid = seg->nextValid; } else { curSegs = seg->nextValid; } seg = seg->nextValid; } else { seg->X += seg->dX * FRAC; #if 0 if (prev && prev->X > seg->X) { s = &curSegs; while ((*s)->X < seg->X) s=&(*s)->nextValid; prev->nextValid = seg->nextValid; seg->nextValid = *s; *s = seg; seg = prev->nextValid; } else #endif { prev = seg; seg = seg->nextValid; } } } return curSegs; } static Segment *newSegments(Segment *curSegs, Segment *newSegs) { Segment *s,*seg,*prev; s = curSegs; prev = 0; // Check for new segments for (seg = newSegs; seg; seg=seg->next) { // Place it at the correct position according to X if (curSegs == 0) { curSegs = seg; seg->nextValid = 0; } else { for(; s; prev = s, s = s->nextValid) { if ( s->X > seg->X || ( (s->X == seg->X) && ( (seg->x1 == s->x1 && seg->dX < s->dX) || (seg->x2 == s->x2 && seg->dX > s->dX) ))) { // Insert before s if (prev) { seg->nextValid = s; prev->nextValid = seg; } else { seg->nextValid = curSegs; curSegs = seg; } break; } } // Append at the end if (s == 0) { prev->nextValid = seg; seg->nextValid = 0; } } s = seg; } return curSegs; } #if 0 static void printSeg(Segment *seg) { /* printf("Seg %08x : X = %5d, Ft = %d, Cl = %2x/%2x/%2x, Cr = %2x/%2x/%2x, x1=%5d, x2=%5d, ymin=%5d, ymax=%5d\n", seg, seg->X>>SEGFRAC, seg->right ? seg->right->type: -1, seg->left ? seg->left->color.red : -1, seg->left ? seg->left->color.green : -1, seg->left ? seg->left->color.blue : -1, seg->right ? seg->right->color.red : -1, seg->right ? seg->right->color.green : -1, seg->right ? seg->right->color.blue : -1, seg->x1, seg->x2, seg->ymin, seg->ymax); */ } #endif static void renderScanLine(GraphicDevice *gd, long y, Segment *curSegs) { Segment *seg; long width; int fi = 1; FillStyleDef *f; width = gd->getWidth() * FRAC; if (curSegs && curSegs->fs[0] && curSegs->fs[1] == 0) { fi = 0; } for(seg = curSegs; seg && seg->nextValid; seg = seg->nextValid) { if (seg->nextValid->X <0) continue; if ((seg->X>>SEGFRAC) > width) break; #if 0 if (seg->X > seg->nextValid->X) { printf("error %d %d\n",seg->X,seg->nextValid->X); } #endif f = seg->fs[fi]; if (f) { switch (f->type) { case f_Solid: if (seg->aa) { gd->fillLineAA(f, y, seg->X>>SEGFRAC, seg->nextValid->X>>SEGFRAC); } else { gd->fillLine(f, y, seg->X>>SEGFRAC, seg->nextValid->X>>SEGFRAC); } break; case f_TiledBitmap: case f_clippedBitmap: gd->fillLineBitmap(f, y, seg->X>>SEGFRAC, seg->nextValid->X>>SEGFRAC); break; case f_LinearGradient: gd->fillLineLG(&f->gradient, y, seg->X>>SEGFRAC, seg->nextValid->X>>SEGFRAC); break; case f_RadialGradient: gd->fillLineRG(&f->gradient, y, seg->X>>SEGFRAC, seg->nextValid->X>>SEGFRAC); break; case f_None: break; } } } } /* draw the current path */ void GraphicDevice::drawPolygon(void) { long y; Segment *curSegs,*seg; // no segments ? if (this->ymax == -1) return; // Foreach scanline curSegs = 0; for(y=this->ymin; y <= this->ymax; y++) { // Make X values progess and remove unuseful segments curSegs = progressSegments(curSegs, y); // Add the new segment starting at the y position. curSegs = newSegments(curSegs, this->segs[y]); // Render the scanline if (this->scan_line_func == NULL) { renderScanLine(this, y, curSegs); } else { for(seg = curSegs; seg && seg->nextValid; seg = seg->nextValid) { if (seg->nextValid->X >= seg->X) { this->scan_line_func(this->scan_line_func_id, y, seg->X>>SEGFRAC, seg->nextValid->X>>SEGFRAC); } } } } /* free the segments */ memset(this->segs + this->ymin, 0, (this->ymax - this->ymin + 1) * sizeof(Segment *)); this->ymax = -1; this->ymin = this->height; this->seg_pool_cur = this->seg_pool; } void GraphicDevice::updateClippingRegion(Rect *rect) { if (!clipping) return; transformBoundingBox(&clip_rect, adjust, rect, 1); clip_rect.xmin >>= FRAC_BITS; clip_rect.xmax >>= FRAC_BITS; clip_rect.ymin >>= FRAC_BITS; clip_rect.ymax >>= FRAC_BITS; #if PRINT&1 clip_rect.print(); #endif clip_rect.xmin-=2; clip_rect.ymin-=2; clip_rect.xmax+=2; clip_rect.ymax+=2; if (clip_rect.xmin < viewPort.xmin) clip_rect.xmin = viewPort.xmin; if (clip_rect.xmax < viewPort.xmin) clip_rect.xmax = viewPort.xmin; if (clip_rect.ymin < viewPort.ymin) clip_rect.ymin = viewPort.ymin; if (clip_rect.ymax < viewPort.ymin) clip_rect.ymax = viewPort.ymin; if (clip_rect.xmax > viewPort.xmax) clip_rect.xmax = viewPort.xmax; if (clip_rect.ymax > viewPort.ymax) clip_rect.ymax = viewPort.ymax; if (clip_rect.xmin > viewPort.xmax) clip_rect.xmin = viewPort.xmax; if (clip_rect.ymin > viewPort.ymax) clip_rect.ymin = viewPort.ymax; } void GraphicDevice::setClipping(int value) { clipping = value; if (clipping == 0) { //printf("Clipping Off\n"); // Reset region clip_rect.xmin = viewPort.xmin; clip_rect.xmax = viewPort.xmax; clip_rect.ymin = viewPort.ymin; clip_rect.ymax = viewPort.ymax; } }