///////////////////////////////////////////////////////////// // Flash Plugin and Player // Copyright (C) 1998,1999 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$"; #endif #define PRINT 0 #define ABS(v) ((v) < 0 ? -(v) : (v)) static void prepareStyles(GraphicDevice *gd, Matrix *matrix, Cxform *cxform, FillStyleDef *f, long n); static void clearStyles(GraphicDevice *gd, FillStyleDef *f, long n); static void drawShape(GraphicDevice *gd, Matrix *matrix1, Cxform *cxform, Shape *shape, ShapeAction shapeAction, void *id,ScanLineFunc scan_line_func); // Constructor Shape::Shape(long id, int level) : Character(ShapeType, id) { defLevel = level; defaultFillStyle.type = f_Solid; defaultFillStyle.color.red = 0; defaultFillStyle.color.green = 0; defaultFillStyle.color.blue = 0; defaultFillStyle.color.alpha = ALPHA_OPAQUE; defaultLineStyle.width = 0; // This is to force a first update lastMat.a = 0; lastMat.d = 0; shape_size += sizeof(Shape); shape_nb ++; file_ptr = NULL; getStyles = 0; getAlpha = 0; } Shape::~Shape() { if (file_ptr) { free(file_ptr); } } void Shape::setBoundingBox(Rect rect) { boundary = rect; } void Shape::getBoundingBox(Rect *bb, DisplayListEntry *e) { *bb = boundary; } int Shape::execute(GraphicDevice *gd, Matrix *matrix, Cxform *cxform) { //printf("TagId = %d\n", getTagId()); //if (getTagId() != 220) return 0; if (cxform) { defaultFillStyle.color = cxform->getColor(gd->getForegroundColor()); } else { defaultFillStyle.color = gd->getForegroundColor(); } defaultFillStyle.color.pixel = gd->allocColor(defaultFillStyle.color); drawShape(gd, matrix, cxform, this, ShapeDraw, NULL, 0); return 0; } void Shape::getRegion(GraphicDevice *gd, Matrix *matrix, void *id, ScanLineFunc scan_line_func) { gd->setClipping(0); drawShape(gd,matrix,0,this,ShapeGetRegion,id,scan_line_func); gd->setClipping(1); } /************************************************************************/ /* create a new path */ static void newPath(ShapeParser *shape, long x, long y) { Path *p; long x1,y1; p=&shape->curPath; x1 = shape->matrix->getX(x, y); y1 = shape->matrix->getY(x, y); p->lastX = x1; p->lastY = y1; p->nb_edges = 0; p->nb_segments = 0; } static void addSegment1(ShapeParser *shape, long x, long y, FillStyleDef *f0, FillStyleDef *f1, LineStyleDef *l) { Path *p; p=&shape->curPath; if (l) { /* a line is defined ... it will be drawn later */ LineSegment *ls; ls = new LineSegment; if (ls != NULL) { ls->l = l; ls->x1 = p->lastX; ls->y1 = p->lastY; ls->x2 = x; ls->y2 = y; ls->first = (p->nb_segments == 0); ls->next = NULL; if (shape->last_line == NULL) { shape->first_line = ls; } else { shape->last_line->next = ls; } shape->last_line = ls; } } /* anti antialiasing not needed if line */ if (!shape->reverse) { shape->gd->addSegment(p->lastX,p->lastY,x,y,f0,f1,l ? 0 : 1); } else { shape->gd->addSegment(p->lastX,p->lastY,x,y,f1,f0,l ? 0 : 1); } p->lastX = x; p->lastY = y; p->nb_segments++; } static void addLine(ShapeParser *shape, long x, long y, FillStyleDef *f0, FillStyleDef *f1, LineStyleDef *l) { long x1,y1; Path *p; p=&shape->curPath; x1 = shape->matrix->getX(x, y); y1 = shape->matrix->getY(x, y); addSegment1(shape,x1,y1,f0,f1,l); p->nb_edges++; } // This is based on Divide and Conquer algorithm. #define BFRAC_BITS 0 #define BFRAC (1 << BFRAC_BITS) static void bezierBuildPoints (ShapeParser *s, int subdivisions, long a1X, long a1Y, long cX, long cY, long a2X, long a2Y) { long c1X,c1Y; long c2X,c2Y; long X,Y; long xmin,ymin,xmax,ymax; if (subdivisions != 0) { /* find the bounding box */ if (a1X < cX) { xmin = a1X; xmax = cX; } else { xmin = cX; xmax = a1X; } if (a2X < xmin) xmin = a2X; if (a2X > xmax) xmax = a2X; if (a1Y < cY) { ymin = a1Y; ymax = cY; } else { ymin = cY; ymax = a1Y; } if (a2Y < ymin) ymin = a2Y; if (a2Y > ymax) ymax = a2Y; if (((xmax - xmin) + (ymax - ymin)) >= (BFRAC*FRAC*2)) { // Control point 1 c1X = (a1X+cX) >> 1; c1Y = (a1Y+cY) >> 1; // Control point 2 c2X = (a2X+cX) >> 1; c2Y = (a2Y+cY) >> 1; // New point X = (c1X+c2X) >> 1; Y = (c1Y+c2Y) >> 1; subdivisions--; bezierBuildPoints(s, subdivisions, a1X, a1Y, c1X, c1Y, X, Y); bezierBuildPoints(s, subdivisions, X, Y, c2X, c2Y, a2X, a2Y); return; } } addSegment1(s, (a2X+(BFRAC/2)) >> BFRAC_BITS, (a2Y+(BFRAC/2)) >> BFRAC_BITS, s->f0, s->f1, s->l); } /* this code is broken, but useful to get something */ static void flushPaths(ShapeParser *s) { LineSegment *ls; LineStyleDef *l; long nx,ny,nn,w; GraphicDevice *gd = s->gd; /* draw the filled polygon */ gd->drawPolygon(); /* draw the lines */ ls = s->first_line; if (ls != NULL) { do { l = ls->l; #if 0 printf("line %d %d %d %d width=%d\n", ls->x1, ls->y1, ls->x2, ls->y2, l->width); #endif /* XXX: this width is false, but it is difficult (and expensive) to have the correct one */ w = ABS((long)(s->matrix->a * l->width)); if (w <= ((3*FRAC)/2)) { w = FRAC; } #ifdef THIN_LINES if (w <= ((3*FRAC)/2)) { // draw the thin lines only in shapeAction == shapeDraw if (gd->scan_line_func == NULL) { gd->setForegroundColor(l->fillstyle.color); gd->drawLine(ls->x1, ls->y1, ls->x2, ls->y2, w); } } else { #else { #endif /* compute the normal vector */ nx = -(ls->y2 - ls->y1); ny = (ls->x2 - ls->x1); /* normalize & width */ nn = 2 * (long) sqrt(nx * nx + ny * ny); #define UL ls->x1 + nx -ny, ls->y1 + ny +nx #define UR ls->x2 + nx +ny, ls->y2 + ny -nx #define LL ls->x1 - nx -ny, ls->y1 - ny +nx #define LR ls->x2 - nx +ny, ls->y2 - ny -nx if (nn > 0) { nx = (nx * w) / nn; ny = (ny * w) / nn; /* top segment */ gd->addSegment(UL, UR, NULL, &l->fillstyle, 1); /* bottom segment */ gd->addSegment(LL, LR, &l->fillstyle, NULL, 1); /* right segment */ gd->addSegment(UR, LR, &l->fillstyle, NULL, 1); /* left segment */ gd->addSegment(UL, LL, NULL, &l->fillstyle, 1); /* draw the line polygon */ gd->drawPolygon(); } } ls = ls->next; } while (ls != NULL); /* delete the line structures */ ls = s->first_line; while (ls != NULL) { LineSegment *ls1; ls1 = ls->next; delete ls; ls = ls1; } /* reset the line pointers */ s->first_line = NULL; s->last_line = NULL; } } static void addBezier(ShapeParser *shape, long ctrlX1, long ctrlY1, long newX1, long newY1, FillStyleDef *f0, FillStyleDef *f1, LineStyleDef *l) { long newX,newY,ctrlX,ctrlY; Path *p; p=&shape->curPath; /* note: we do the matrix multiplication before calculating the bezier points (faster !) */ ctrlX = shape->matrix->getX(ctrlX1, ctrlY1); ctrlY = shape->matrix->getY(ctrlX1, ctrlY1); newX = shape->matrix->getX(newX1, newY1); newY = shape->matrix->getY(newX1, newY1); shape->f0 = f0; shape->f1 = f1; shape->l = l; bezierBuildPoints(shape, 3, p->lastX<lastY<nb_edges++; } /***********************************************************************/ /* bit parser */ static void InitBitParser(struct BitParser *b,U8 *buf) { b->ptr = buf; } static void InitBits(struct BitParser *b) { // Reset the bit position and buffer. b->m_bitPos = 0; b->m_bitBuf = 0; } static inline U8 GetByte(struct BitParser *b) { U8 v; v = *b->ptr++; return v; } static inline U16 GetWord(struct BitParser *b) { U8 *s; U16 v; s = b->ptr; v = s[0] | ((U16) s[1] << 8); b->ptr = s + 2; return v; } static inline U32 GetDWord(struct BitParser *b) { U32 v; U8 * s = b->ptr; v = (U32) s[0] | ((U32) s[1] << 8) | ((U32) s[2] << 16) | ((U32) s [3] << 24); b->ptr = s + 4; return v; } static inline U32 GetBit (struct BitParser *b) { U32 v; S32 m_bitPos = b->m_bitPos; U32 m_bitBuf = b->m_bitBuf; if (m_bitPos == 0) { m_bitBuf = (U32)(*b->ptr++) << 24; m_bitPos = 8; } v = (m_bitBuf >> 31); m_bitPos--; m_bitBuf <<= 1; b->m_bitPos = m_bitPos; b->m_bitBuf = m_bitBuf; return v; } static inline U32 GetBits (struct BitParser *b, int n) { U32 v; S32 m_bitPos = b->m_bitPos; U32 m_bitBuf = b->m_bitBuf; if (n == 0) return 0; while (m_bitPos < n) { m_bitBuf |= (U32)(*b->ptr++) << (24 - m_bitPos); m_bitPos += 8; } v = m_bitBuf >> (32 - n); m_bitBuf <<= n; m_bitPos -= n; b->m_bitPos = m_bitPos; b->m_bitBuf = m_bitBuf; return v; } // Get n bits from the string with sign extension. static inline S32 GetSBits (struct BitParser *b,S32 n) { // Get the number as an unsigned value. S32 v = (S32) GetBits(b,n); // Is the number negative? if (v & (1L << (n - 1))) { // Yes. Extend the sign. v |= -1L << n; } return v; } /************************************************************************/ static void GetMatrix(BitParser *b, Matrix* mat) { InitBits(b); // Scale terms if (GetBit(b)) { int nBits = (int) GetBits(b,5); mat->a = (float)(GetSBits(b,nBits))/(float)0x10000; mat->d = (float)(GetSBits(b,nBits))/(float)0x10000; } else { mat->a = mat->d = 1.0; } // Rotate/skew terms if (GetBit(b)) { int nBits = (int)GetBits(b,5); mat->c = (float)(GetSBits(b,nBits))/(float)0x10000; mat->b = (float)(GetSBits(b,nBits))/(float)0x10000; } else { mat->b = mat->c = 0.0; } // Translate terms int nBits = (int) GetBits(b,5); mat->tx = GetSBits(b,nBits); mat->ty = GetSBits(b,nBits); } static FillStyleDef * ParseFillStyle(ShapeParser *shape, long *n, long getAlpha) { BitParser *b = &shape->bit_parser; FillStyleDef *defs; U16 i = 0; // Get the number of fills. U16 nFills = GetByte(b); // Do we have a larger number? if (nFills == 255) { // Get the larger number. nFills = GetWord(b); } *n = nFills; defs = new FillStyleDef[ nFills ]; if (defs == NULL) return NULL; // Get each of the fill style. for (i = 0; i < nFills; i++) { U16 fillStyle = GetByte(b); defs[i].type = (FillType) fillStyle; if (fillStyle & 0x10) { defs[i].type = (FillType) (fillStyle & 0x12); // Get the gradient matrix. GetMatrix(b,&(defs[i].matrix)); // Get the number of colors. defs[i].gradient.nbGradients = GetByte(b); // Get each of the colors. for (U16 j = 0; j < defs[i].gradient.nbGradients; j++) { defs[i].gradient.ratio[j] = GetByte(b); defs[i].gradient.color[j].red = GetByte(b); defs[i].gradient.color[j].green = GetByte(b); defs[i].gradient.color[j].blue = GetByte(b); if (getAlpha) { defs[i].gradient.color[j].alpha = GetByte(b); } else { defs[i].gradient.color[j].alpha = ALPHA_OPAQUE; } } } else if (fillStyle & 0x40) { defs[i].type = (FillType) (fillStyle & 0x41); // Get the bitmapId defs[i].bitmap = (Bitmap *)shape->dict->getCharacter(GetWord(b)); // Get the bitmap matrix. GetMatrix(b,&(defs[i].matrix)); } else { defs[i].type = (FillType) 0; // A solid color defs[i].color.red = GetByte(b); defs[i].color.green = GetByte(b); defs[i].color.blue = GetByte(b); if (getAlpha) { defs[i].color.alpha = GetByte(b); } else { defs[i].color.alpha = ALPHA_OPAQUE; } } } return defs; } static LineStyleDef * ParseLineStyle(ShapeParser *shape, long *n, long getAlpha) { BitParser *b = &shape->bit_parser; LineStyleDef *defs,*def; FillStyleDef *f; long i; // Get the number of lines. U16 nLines = GetByte(b); // Do we have a larger number? if (nLines == 255) { // Get the larger number. nLines = GetWord(b); } *n = nLines; defs = new LineStyleDef[ nLines ]; if (defs == NULL) return NULL; // Get each of the line styles. for (i = 0; i < nLines; i++) { def=&defs[i]; def->width = GetWord(b); def->color.red = GetByte(b); def->color.green = GetByte(b); def->color.blue = GetByte(b); if (getAlpha) { def->color.alpha = GetByte(b); } else { def->color.alpha = ALPHA_OPAQUE; } f=&def->fillstyle; f->type = f_Solid; f->color = def->color; if (shape->cxform) { f->color = shape->cxform->getColor(f->color); } f->color.pixel = shape->gd->allocColor(f->color); } return defs; } /* 0 = end of shape */ static int ParseShapeRecord(ShapeParser *shape, ShapeRecord *sr, long getAlpha) { BitParser *b = &shape->bit_parser; // Determine if this is an edge. BOOL isEdge = (BOOL) GetBit(b); if (!isEdge) { // Handle a state change U16 flags = (U16) GetBits(b,5); // Are we at the end? if (flags == 0) { // End of shape return 0; } sr->type = shapeNonEdge; sr->flags = (ShapeFlags)flags; // Process a move to. if (flags & flagsMoveTo) { U16 nBits = (U16) GetBits(b,5); sr->x = GetSBits(b,nBits); sr->y = GetSBits(b,nBits); } // Get new fill info. if (flags & flagsFill0) { sr->fillStyle0 = GetBits(b,shape->m_nFillBits); } if (flags & flagsFill1) { sr->fillStyle1 = GetBits(b,shape->m_nFillBits); } // Get new line info if (flags & flagsLine) { sr->lineStyle = GetBits(b,shape->m_nLineBits); } // Check to get a new set of styles for a new shape layer. if (flags & flagsNewStyles) { FillStyleDef *fillDefs; LineStyleDef *lineDefs; long n; // Parse the style. fillDefs = ParseFillStyle(shape, &n, getAlpha); if (fillDefs == NULL) return 0; sr->newFillStyles = fillDefs; sr->nbNewFillStyles = n; lineDefs = ParseLineStyle(shape, &n, getAlpha); if (lineDefs == NULL) return 0; sr->newLineStyles = lineDefs; sr->nbNewLineStyles = n; InitBits(b); // Bug ! // Reset. shape->m_nFillBits = (U16) GetBits(b,4); shape->m_nLineBits = (U16) GetBits(b,4); } //if (flags & flagsEndShape) //printf("\tEnd of shape.\n\n"); return flags & flagsEndShape ? 0 : 1; } else { if (GetBit(b)) { sr->type = shapeLine; // Handle a line U16 nBits = (U16) GetBits(b,4) + 2; // nBits is biased by 2 // Save the deltas if (GetBit(b)) { // Handle a general line. sr->dX = GetSBits(b,nBits); sr->dY = GetSBits(b,nBits); } else { // Handle a vert or horiz line. if (GetBit(b)) { // Vertical line sr->dY = GetSBits(b,nBits); sr->dX = 0; } else { // Horizontal line sr->dX = GetSBits(b,nBits); sr->dY = 0; } } } else { sr->type = shapeCurve; // Handle a curve U16 nBits = (U16) GetBits(b,4) + 2; // nBits is biased by 2 // Get the control sr->ctrlX = GetSBits(b,nBits); sr->ctrlY = GetSBits(b,nBits); // Get the anchor sr->anchorX = GetSBits(b,nBits); sr->anchorY = GetSBits(b,nBits); } return 1; } } static void drawShape(GraphicDevice *gd, Matrix *matrix1, Cxform *cxform, Shape *shape, ShapeAction shapeAction, void *id,ScanLineFunc scan_line_func) { LineStyleDef *l; FillStyleDef *f0; FillStyleDef *f1; ShapeRecord sr1,*sr = &sr1; int firstPoint; long lastX,lastY; LineStyleDef *curLineStyle; long curNbLineStyles; FillStyleDef *curFillStyle; long curNbFillStyles; StyleList *sl; ShapeParser sp1,*sp=&sp1; BitParser *b; Matrix mat,*matrix; mat = (*gd->adjust) * (*matrix1); matrix = &mat; sp->reverse = (mat.a * mat.d) < 0; curLineStyle = NULL; curNbLineStyles = 0; curFillStyle = NULL; curNbFillStyles = 0; sp->style_list = NULL; sp->shape = shape; sp->gd = gd; sp->matrix = matrix; sp->cxform = cxform; sp->dict = shape->dict; if (shapeAction == ShapeGetRegion) { gd->scan_line_func = scan_line_func; gd->scan_line_func_id = id; } else { gd->scan_line_func = NULL; } b = &sp->bit_parser; InitBitParser(b,shape->file_ptr); if (shape->getStyles) { // ShapeWithStyle curFillStyle = ParseFillStyle(sp, &curNbFillStyles, shape->getAlpha); if (curFillStyle == NULL) return; curLineStyle = ParseLineStyle(sp, &curNbLineStyles, shape->getAlpha); if (curLineStyle == NULL) return; sl = new StyleList; if (sl == NULL) return; sl->next = NULL; sl->newFillStyles = curFillStyle; sl->nbNewFillStyles = curNbFillStyles; sl->newLineStyles = curLineStyle; sl->nbNewLineStyles = curNbLineStyles; sp->style_list = sl; if (shapeAction == ShapeDraw) { prepareStyles(gd, matrix, cxform, curFillStyle, curNbFillStyles); } } InitBits(b); sp->m_nFillBits = (U16) GetBits(b,4); sp->m_nLineBits = (U16) GetBits(b,4); l = 0; f0 = 0; f1 = 0; firstPoint = 1; lastX = 0; lastY = 0; sp->curPath.nb_edges = 0; sp->first_line = NULL; sp->last_line = NULL; for(;;) { if (ParseShapeRecord(sp, sr, shape->getAlpha) == 0) break; switch (sr->type) { case shapeNonEdge: if (sr->flags & flagsNewStyles) { curFillStyle = sr->newFillStyles; curNbFillStyles = sr->nbNewFillStyles; curLineStyle = sr->newLineStyles; curNbLineStyles = sr->nbNewLineStyles; sl = new StyleList; sl->next = sp->style_list; sl->newFillStyles = sr->newFillStyles; sl->nbNewFillStyles = sr->nbNewFillStyles; sl->newLineStyles = sr->newLineStyles; sl->nbNewLineStyles = sr->nbNewLineStyles; sp->style_list = sl; if (shapeAction == ShapeDraw) { prepareStyles(gd, matrix, cxform, curFillStyle, curNbFillStyles); } } if (sr->flags & flagsFill0) { if (sr->fillStyle0) { if (curFillStyle) { f0 = &curFillStyle[sr->fillStyle0-1]; } else { f0 = &shape->defaultFillStyle; } } else { f0 = 0; } } if (sr->flags & flagsFill1) { if (sr->fillStyle1) { if (curFillStyle) { f1 = &curFillStyle[sr->fillStyle1-1]; } else { f1 = &shape->defaultFillStyle; } } else { f1 = 0; } } if (sr->flags & flagsLine) { if (sr->lineStyle) { l = &curLineStyle[sr->lineStyle-1]; } else { l = 0; } } if (sr->flags & flagsMoveTo) { if (sp->curPath.nb_edges == 0) { /* if no edges, draw the polygon, then the lines */ flushPaths(sp); } newPath(sp, sr->x, sr->y); firstPoint = 0; lastX = sr->x; lastY = sr->y; #if PRINT printf("---------\nX,Y = %4d,%4d\n", sr->x/20, sr->y/20); #endif } break; case shapeCurve: // Handle Bezier Curves !!! if (firstPoint) { newPath(sp, 0, 0); firstPoint = 0; } { long newX,newY,ctrlX,ctrlY; ctrlX = lastX+sr->ctrlX; ctrlY = lastY+sr->ctrlY; newX = ctrlX+sr->anchorX; newY = ctrlY+sr->anchorY; #if 1 addBezier(sp, ctrlX, ctrlY, newX, newY, f0 , f1, l); #else addLine(sp, newX, newY, f0, f1, l); #endif lastX = newX; lastY = newY; } break; case shapeLine: if (firstPoint) { newPath(sp, 0, 0); firstPoint = 0; } lastX += sr->dX; lastY += sr->dY; addLine(sp, lastX, lastY, f0, f1, l); #if PRINT printf(" X, Y = %4d,%4d\n", lastX/20, lastY/20); #endif break; } } /* XXX: should test if there is something to draw */ flushPaths(sp); /* free the styles */ while (sp->style_list) { StyleList *sl; sl=sp->style_list; sp->style_list = sl->next; if (shapeAction == ShapeDraw) { clearStyles(gd, sl->newFillStyles, sl->nbNewFillStyles); } delete[] sl->newFillStyles; delete[] sl->newLineStyles; delete sl; } } static void prepareStyles(GraphicDevice *gd, Matrix *matrix, Cxform *cxform, FillStyleDef *ftab, long n) { long fs; FillStyleDef *f; for(fs = 0; fs < n; fs++) { f = ftab + fs; switch (f->type) { case f_None: break; case f_Solid: if (cxform) { f->color = cxform->getColor(f->color); } f->color.pixel = gd->allocColor(f->color); break; case f_LinearGradient: case f_RadialGradient: { Matrix mat; int n,r,l; long red, green, blue, alpha; long dRed, dGreen, dBlue, dAlpha; long min,max; Matrix *m; mat = *(matrix) * f->matrix; // Compute inverted matrix f->gradient.imat = mat.invert(); /* renormalize the matrix */ m=&f->gradient.imat; if (f->type == f_LinearGradient) { m->a = m->a * FRAC * (1/128.0) * 65536.0; m->b = m->b * FRAC * (1/128.0) * 65536.0; m->tx = (long) ((m->tx + 16384) * (1/128.0) * 65536.0); } else { m->a = m->a * FRAC * (1/64.0) * 65536.0; m->b = m->b * FRAC * (1/64.0) * 65536.0; m->c = m->c * FRAC * (1/64.0) * 65536.0; m->d = m->d * FRAC * (1/64.0) * 65536.0; m->tx = (long) (m->tx * (1/64.0) * 65536.0); m->ty = (long) (m->ty * (1/64.0) * 65536.0); } // Reset translation in inverted matrix f->gradient.has_alpha = 0; // Build a 256 color ramp f->gradient.ramp = new Color[256]; if (f->gradient.ramp == NULL) { // Invalidate fill style f->type = f_None; continue; } // Store min and max min = f->gradient.ratio[0]; max = f->gradient.ratio[f->gradient.nbGradients-1]; for(r=0; r < f->gradient.nbGradients-1; r++) { Color start,end; l = f->gradient.ratio[r+1]-f->gradient.ratio[r]; if (l == 0) continue; if (cxform) { start = cxform->getColor(f->gradient.color[r]); end = cxform->getColor(f->gradient.color[r+1]); } else { start = f->gradient.color[r]; end = f->gradient.color[r+1]; } if (start.alpha != ALPHA_OPAQUE || end.alpha != ALPHA_OPAQUE) { f->gradient.has_alpha = 1; } dRed = end.red - start.red; dGreen = end.green - start.green; dBlue = end.blue - start.blue; dAlpha = end.alpha - start.alpha; dRed = (dRed<<16)/l; dGreen = (dGreen<<16)/l; dBlue = (dBlue<<16)/l; dAlpha = (dAlpha<<16)/l; red = start.red <<16; green = start.green <<16; blue = start.blue <<16; alpha = start.alpha <<16; for (n=f->gradient.ratio[r]; n<=f->gradient.ratio[r+1]; n++) { f->gradient.ramp[n].red = red>>16; f->gradient.ramp[n].green = green>>16; f->gradient.ramp[n].blue = blue>>16; f->gradient.ramp[n].alpha = alpha>>16; f->gradient.ramp[n].pixel = gd->allocColor(f->gradient.ramp[n]); red += dRed; green += dGreen; blue += dBlue; alpha += dAlpha; } } for(n=0; ngradient.ramp[n] = f->gradient.ramp[min]; } for(n=max; n<256; n++) { f->gradient.ramp[n] = f->gradient.ramp[max]; } } break; case f_TiledBitmap: case f_clippedBitmap: if (f->bitmap) { Matrix *m; f->cmap = gd->getColormap(f->bitmap->colormap, f->bitmap->nbColors, cxform); if (f->cmap == NULL) { /* Get the normal cmap anyway */ f->cmap = f->bitmap->colormap; } f->bitmap_matrix = *(matrix) * f->matrix; f->bitmap_matrix = f->bitmap_matrix.invert(); m=&f->bitmap_matrix; m->a = m->a * FRAC * 65536.0; m->b = m->b * FRAC * 65536.0; m->c = m->c * FRAC * 65536.0; m->d = m->d * FRAC * 65536.0; m->tx = (long) (m->tx * 65536.0); m->ty = (long) (m->ty * 65536.0); f->alpha_table = NULL; if (f->bitmap->alpha_buf && cxform) { unsigned char *alpha_table; int i; alpha_table = (unsigned char *)malloc (256); if (alpha_table != NULL) { for(i=0;i<256;i++) { alpha_table[i] = cxform->getAlpha(i); } } f->alpha_table = alpha_table; } } break; } } } static void clearStyles(GraphicDevice *gd, FillStyleDef *ftab, long n) { long fs; FillStyleDef *f; for(fs = 0; fs < n; fs++) { f = ftab + fs; switch (f->type) { case f_Solid: break; case f_LinearGradient: case f_RadialGradient: if (f->gradient.ramp) { delete f->gradient.ramp; } break; case f_TiledBitmap: case f_clippedBitmap: if (f->bitmap) { if (f->cmap && f->cmap != f->bitmap->colormap) delete f->cmap; if (f->alpha_table) free(f->alpha_table); } break; case f_None: break; } } }