#include "SVGGradient.h" #include "SVGTransformParser.h" #include #define TMP_STRLEN 0xFF using namespace std; namespace SWF { /* SVGGradient */ SVGGradient::SVGGradient() { spreadMethod = PAD; } void SVGGradient::parse(xmlNodePtr node) { attribs.parseNode(node); const char *tmp = attribs["gradientUnits"]; if(tmp) { if(!strcmp(tmp, "userSpaceOnUse")) { userSpace = true; } else { userSpace = false; } } else { userSpace = false; } parseGradient(); parseSpreadMethod(); parseTransform(); parseStops(node); } void SVGGradient::parseStops(xmlNodePtr parent) { for(xmlNodePtr node = parent->children; node; node = node->next) { if(!xmlStrcmp(node->name, (const xmlChar *)"stop")) parseStop(node); } } void SVGGradient::parseStop(xmlNodePtr node) { SVGGradientStop stop; AttributeParser stopAttribs; stopAttribs.parseNode(node); double offset = stopAttribs.getDouble("offset"); const char *tmp = stopAttribs["stop-color"]; if(tmp) { stop.setColor(tmp); } stop.setAlpha(stopAttribs.getDouble("stop-opacity", 1)); stops[offset] = stop; } void SVGGradient::parseTransform() { const char *tmp = attribs["gradientTransform"]; if(tmp) { TransformParser parser; parser.parse(tmp); transform = parser.getMatrix(); } } void SVGGradient::parseSpreadMethod() { const char *tmp = attribs["spreadMethod"]; if(tmp) { if(!strcmp(tmp, "pad")) { spreadMethod = PAD; } else if(!strcmp(tmp, "reflect")) { spreadMethod = REFLECT; } else if(!strcmp(tmp, "repeat")) { spreadMethod = REPEAT; } else { spreadMethod = PAD; } } } void SVGGradient::writeCommonXML(xmlNodePtr parentNode, Matrix& m, bool hasModes) { char tmp[TMP_STRLEN]; xmlNodePtr node; if(hasModes) { xmlSetProp(parentNode, (const xmlChar *)"interpolationMode", (const xmlChar *)"0"); snprintf(tmp, TMP_STRLEN, "%i", spreadMethod); xmlSetProp(parentNode, (const xmlChar *)"spreadMode", (const xmlChar *)&tmp); } else { xmlSetProp(parentNode, (const xmlChar *)"reserved", (const xmlChar *)"0"); } node = xmlNewChild(parentNode, NULL, (const xmlChar *)"matrix", NULL); node = xmlNewChild(node, NULL, (const xmlChar *)"Transform", NULL); m.setXMLProps(node); node = xmlNewChild(parentNode, NULL, (const xmlChar *)"gradientColors", NULL); for(map::iterator i = stops.begin(); i != stops.end(); i++) { (*i).second.writeXML(node, (*i).first); } } /* SVGLinearGradient */ void SVGLinearGradient::parseGradient() { x1 = attribs.getDouble("x1", 0); y1 = attribs.getDouble("y1", 0); x2 = attribs.getDouble("x2", 1); y2 = attribs.getDouble("y2", 0); } void SVGLinearGradient::writeXML(xmlNodePtr node, Rect& bounds, bool hadModes) { double w = bounds.right - bounds.left; double h = bounds.bottom - bounds.top; Matrix m; if(userSpace) { double lx = x2 - x1; double ly = y2 - y1; double dx = x2 - x1; double dy = y2 - y1; double d = sqrt(dx * dx + dy * dy); double a = atan2(dy, dx); m *= transform; m.translate((x1 + x2) / 2 * 20, (y1 + y2) / 2 * 20); m.rotate(a); m.scale(d * 20 / 32768); } else { double sx, sy; double _x1 = bounds.left + x1 * w; double _y1 = bounds.top + y1 * h; double _x2 = bounds.left + x2 * w; double _y2 = bounds.top + y2 * h; if(x1 != x2) { sx = (_x2 - _x1) / 32768.0; } else { sx = 1; } if(y1 != y2) { sy = (_y2 - _y1) / 32768.0; } else { sy = 1; } double dx = x2 - x1; double dy = y2 - y1; double d = sqrt(dx * dx + dy * dy); double a = atan2(dy, dx); m.translate((_x1 + _x2) / 2, (_y1 + _y2) / 2); m.scale(sx, sy); m.rotate(a); m.scale(d); } xmlNodePtr topNode = xmlNewChild(node, NULL, (const xmlChar *)"LinearGradient", NULL); writeCommonXML(topNode, m, hadModes); } /* SVGRadialGradient */ void SVGRadialGradient::parseGradient() { cx = attribs.getDouble("cx", .5); cy = attribs.getDouble("cy", .5); r = attribs.getDouble("r", .5); if(attribs["fx"] || attribs["fy"]) { hasFocalPoint = true; fx = attribs.getDouble("fx", cx); fy = attribs.getDouble("fy", cy); if(fx == cx && fy == cy) { hasFocalPoint = false; } } else { hasFocalPoint = false; } } void SVGRadialGradient::writeXML(xmlNodePtr node, Rect& bounds, bool hasModes) { Matrix m; double w = bounds.right - bounds.left; double h = bounds.bottom - bounds.top; double shift = 0; if(userSpace) { m *= transform; m.translate(cx * 20, cy * 20); if(hasFocalPoint) { double dx = fx - cx; double dy = fy - cy; m.rotate(atan2(dy, dx)); shift = sqrt(pow(dx, 2) + pow(dy, 2)) / r; } m.scale(r * 20 / 16348.0, r * 20 / 16384.0); } else { double _cx = bounds.left + cx * w; double _cy = bounds.top + cy * h; m.translate(_cx, _cy); m.scale(r * w / 16348.0, r * h / 16384.0); if(hasFocalPoint) { double _fx = bounds.left + fx * w; double _fy = bounds.top + fy * h; double dx = _fx - _cx; double dy = _fy - _cy; m.rotate(atan2(dy, dx)); shift = sqrt(pow(dx, 2) + pow(dy, 2)) / (r * sqrt(pow(w, 2) + pow(h, 2)) / sqrt(2)); } } xmlNodePtr topNode; if(hasFocalPoint) { topNode = xmlNewChild(node, NULL, (const xmlChar *)"ShiftedRadialGradient", NULL); char tmp[TMP_STRLEN]; snprintf(tmp, TMP_STRLEN, "%f", shift); xmlSetProp(topNode, (const xmlChar *)"shift", (const xmlChar *)tmp); } else { topNode = xmlNewChild(node, NULL, (const xmlChar *)"RadialGradient", NULL); } writeCommonXML(topNode, m, hasModes); } /* SVGGradientStop */ void SVGGradientStop::writeXML(xmlNodePtr node, double offset) { char tmp[TMP_STRLEN]; node = xmlNewChild(node, NULL, (const xmlChar *)"GradientItem", NULL); snprintf(tmp, TMP_STRLEN, "%i", (int)(offset * 255)); xmlSetProp(node, (const xmlChar *)"position", (const xmlChar *)&tmp); node = xmlNewChild(node, NULL, (const xmlChar *)"color", NULL); color.writeXML(node); } }