/* Copyright (C) 2002 Nikolas Zimmermann This file is part of the KDE project This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License aint with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ksvgiconpainter.h" #define ART_END2 10 const double deg2rad = 0.017453292519943295769; // pi/180 class KSVGIconPainterHelper { public: KSVGIconPainterHelper(int width, int height, KSVGIconPainter *painter) { m_painter = painter; m_clipSVP = 0; m_fillColor = Qt::black; m_useFill = true; m_useStroke = false; m_useFillGradient = false; m_useStrokeGradient = false; m_worldMatrix = new TQWMatrix(); // Create new image with alpha support m_image = new TQImage(width, height, 32); m_image->setAlphaBuffer(true); m_strokeWidth = 1.0; m_strokeMiterLimit = 4; m_dashOffset = 0; m_dashes = ""; m_opacity = 0xff; m_fillOpacity = 0xff; m_strokeOpacity = 0xff; m_fillRule = "nonzero"; m_width = width; m_height = height; m_rowstride = m_width * 4; // Make internal libart rendering buffer transparent m_buffer = art_new(art_u8, m_rowstride * m_height); memset(m_buffer, 0, m_rowstride * m_height); m_tempBuffer = 0; } ~KSVGIconPainterHelper() { if(m_clipSVP) art_svp_free(m_clipSVP); art_free(m_buffer); delete m_image; delete m_worldMatrix; for(TQMap::Iterator it = m_linearGradientMap.begin(); it != m_linearGradientMap.end(); ++it) { if (!it.data()) continue; delete [] it.data()->stops; delete it.data(); } for(TQMap::Iterator it = m_radialGradientMap.begin(); it != m_radialGradientMap.end(); ++it) { if (!it.data()) continue; delete [] it.data()->stops; delete it.data(); } } ArtVpath *allocVPath(int number) { return art_new(ArtVpath, number); } ArtBpath *allocBPath(int number) { return art_new(ArtBpath, number); } void ensureSpace(TQMemArray &vec, int index) { if(vec.size() == (unsigned int) index) vec.resize(index + 1); } void createBuffer() { m_tempBuffer = art_new(art_u8, m_rowstride * m_height); memset(m_tempBuffer, 0, m_rowstride * m_height); // Swap buffers, so we work with the new one internally... art_u8 *temp = m_buffer; m_buffer = m_tempBuffer; m_tempBuffer = temp; } void mixBuffer(int opacity) { art_u8 *srcPixel = m_buffer; art_u8 *dstPixel = m_tempBuffer; for(int y = 0; y < m_height; y++) { for(int x = 0; x < m_width; x++) { art_u8 r, g, b, a; a = srcPixel[4 * x + 3]; if(a) { r = srcPixel[4 * x]; g = srcPixel[4 * x + 1]; b = srcPixel[4 * x + 2]; int temp = a * opacity + 0x80; a = (temp + (temp >> 8)) >> 8; art_rgba_run_alpha(dstPixel + 4 * x, r, g, b, a, 1); } } srcPixel += m_rowstride; dstPixel += m_rowstride; } // Re-swap again... art_u8 *temp = m_buffer; m_buffer = m_tempBuffer; m_tempBuffer = temp; art_free(m_tempBuffer); m_tempBuffer = 0; } TQ_UINT32 toArtColor(const TQColor &color) { // Convert in a libart suitable form TQString tempName = color.name(); const char *str = tempName.latin1(); int result = 0; for(int i = 1; str[i]; i++) { int hexval; if(str[i] >= '0' && str[i] <= '9') hexval = str[i] - '0'; else if (str[i] >= 'A' && str[i] <= 'F') hexval = str[i] - 'A' + 10; else if (str[i] >= 'a' && str[i] <= 'f') hexval = str[i] - 'a' + 10; else break; result = (result << 4) + hexval; } return result; } void drawSVP(ArtSVP *svp, TQ_UINT32 rgb, int opacity) { if(!svp) return; ArtRender *render = art_render_new(0, 0, m_width, m_height, m_buffer, m_rowstride, 3, 8, ART_ALPHA_SEPARATE, 0); art_render_svp(render, svp); art_render_mask_solid(render, (opacity << 8) + opacity + (opacity >> 7)); ArtPixMaxDepth color[3]; color[0] = ART_PIX_MAX_FROM_8(rgb >> 16); color[1] = ART_PIX_MAX_FROM_8((rgb >> 8) & 0xff); color[2] = ART_PIX_MAX_FROM_8(rgb & 0xff); art_render_image_solid(render, color); art_render_invoke(render); } void drawBPath(ArtBpath *bpath) { double affine[6]; affine[0] = m_worldMatrix->m11(); affine[1] = m_worldMatrix->m12(); affine[2] = m_worldMatrix->m21(); affine[3] = m_worldMatrix->m22(); affine[4] = m_worldMatrix->dx(); affine[5] = m_worldMatrix->dy(); ArtBpath *temp = art_bpath_affine_transform(bpath, affine); ArtVpath *vec = art_bez_path_to_vec(temp, 0.25); art_free(temp); drawPathInternal(vec, affine); } void drawVPath(ArtVpath *vec) { double affine[6]; affine[0] = m_worldMatrix->m11(); affine[1] = m_worldMatrix->m12(); affine[2] = m_worldMatrix->m21(); affine[3] = m_worldMatrix->m22(); affine[4] = m_worldMatrix->dx(); affine[5] = m_worldMatrix->dy(); ArtVpath *temp = art_vpath_affine_transform(vec, affine); art_free(vec); vec = temp; drawPathInternal(vec, affine); } void drawPathInternal(ArtVpath *vec, double *affine) { ArtSVP *svp; ArtSVP *fillSVP = 0, *strokeSVP = 0; TQ_UINT32 fillColor = 0, strokeColor = 0; // Filling { int index = -1; TQValueVector toCorrect; while(vec[++index].code != ART_END) { if(vec[index].code == ART_END2) { vec[index].code = ART_LINETO; toCorrect.push_back(index); } } fillColor = toArtColor(m_fillColor); ArtSvpWriter *swr; ArtSVP *temp; temp = art_svp_from_vpath(vec); if(m_fillRule == "evenodd") swr = art_svp_writer_rewind_new(ART_WIND_RULE_ODDEVEN); else swr = art_svp_writer_rewind_new(ART_WIND_RULE_NONZERO); art_svp_intersector(temp, swr); svp = art_svp_writer_rewind_reap(swr); fillSVP = svp; art_svp_free(temp); TQValueVector::iterator it; for(it = toCorrect.begin(); it != toCorrect.end(); ++it) vec[(*it)].code = (ArtPathcode)ART_END2; } // There seems to be a problem when stroke width is zero, this is a quick // fix (Rob). if(m_strokeWidth <= 0) m_useStroke = m_useStrokeGradient = false; // Stroking if(m_useStroke || m_useStrokeGradient) { strokeColor = toArtColor(m_strokeColor); double ratio = art_affine_expansion(affine); double strokeWidth = m_strokeWidth * ratio; ArtPathStrokeJoinType joinStyle = ART_PATH_STROKE_JOIN_MITER; ArtPathStrokeCapType capStyle = ART_PATH_STROKE_CAP_BUTT; if(m_joinStyle == "miter") joinStyle = ART_PATH_STROKE_JOIN_MITER; else if(m_joinStyle == "round") joinStyle = ART_PATH_STROKE_JOIN_ROUND; else if(m_joinStyle == "bevel") joinStyle = ART_PATH_STROKE_JOIN_BEVEL; if(m_capStyle == "butt") capStyle = ART_PATH_STROKE_CAP_BUTT; else if(m_capStyle == "round") capStyle = ART_PATH_STROKE_CAP_ROUND; else if(m_capStyle == "square") capStyle = ART_PATH_STROKE_CAP_SQUARE; if(m_dashes.length() > 0) { TQRegExp reg("[, ]"); TQStringList dashList = TQStringList::split(reg, m_dashes); double *dashes = new double[dashList.count()]; for(unsigned int i = 0; i < dashList.count(); i++) dashes[i] = m_painter->toPixel(dashList[i], true); ArtVpathDash dash; dash.offset = m_dashOffset; dash.n_dash = dashList.count(); dash.dash = dashes; ArtVpath *vec2 = art_vpath_dash(vec, &dash); art_free(vec); delete[] dashes; vec = vec2; } svp = art_svp_vpath_stroke(vec, joinStyle, capStyle, strokeWidth, m_strokeMiterLimit, 0.25); strokeSVP = svp; } // Apply opacity int fillOpacity = static_cast(m_fillOpacity); int strokeOpacity = static_cast(m_strokeOpacity); int opacity = static_cast(m_opacity); // Needed hack, to support both transparent // paths and transparent gradients if(fillOpacity == strokeOpacity && fillOpacity == opacity && !m_useFillGradient && !m_useStrokeGradient) opacity = 255; if(fillOpacity != 255) { int temp = fillOpacity * opacity + 0x80; fillOpacity = (temp + (temp >> 8)) >> 8; } if(strokeOpacity != 255) { int temp = strokeOpacity * opacity + 0x80; strokeOpacity = (temp + (temp >> 8)) >> 8; } // Create temporary buffer if necessary bool tempDone = false; if(m_opacity != 0xff) { tempDone = true; createBuffer(); } // Apply Gradients on fill/stroke if(m_useFillGradient) applyGradient(fillSVP, true); else if(m_useFill) drawSVP(fillSVP, fillColor, fillOpacity); if(m_useStrokeGradient) applyGradient(strokeSVP, false); else if(m_useStroke) drawSVP(strokeSVP, strokeColor, strokeOpacity); // Mix in temporary buffer, if possible if(tempDone) mixBuffer(opacity); if(m_clipSVP) { art_svp_free(m_clipSVP); m_clipSVP = 0; } if(fillSVP) art_svp_free(fillSVP); if(strokeSVP) art_svp_free(strokeSVP); // Reset opacity values m_opacity = 255.0; m_fillOpacity = 255.0; m_strokeOpacity = 255.0; art_free(vec); } void applyLinearGradient(ArtSVP *svp, const TQString &ref) { ArtGradientLinear *linear = m_linearGradientMap[ref]; if(linear) { TQDomElement element = m_linearGradientElementMap[linear]; double x1, y1, x2, y2; if(element.hasAttribute("x1")) x1 = m_painter->toPixel(element.attribute("x1"), true); else x1 = 0; if(element.hasAttribute("y1")) y1 = m_painter->toPixel(element.attribute("y1"), false); else y1 = 0; if(element.hasAttribute("x2")) x2 = m_painter->toPixel(element.attribute("x2"), true); else x2 = 100; if(element.hasAttribute("y2")) y2 = m_painter->toPixel(element.attribute("y2"), false); else y2 = 0; // Adjust to gradientTransform TQWMatrix m = m_painter->parseTransform(element.attribute("gradientTransform")); m.map(x1, y1, &x1, &y1); m.map(x2, y2, &x2, &y2); double x1n = x1 * m_worldMatrix->m11() + y1 * m_worldMatrix->m21() + m_worldMatrix->dx(); double y1n = x1 * m_worldMatrix->m12() + y1 * m_worldMatrix->m22() + m_worldMatrix->dy(); double x2n = x2 * m_worldMatrix->m11() + y2 * m_worldMatrix->m21() + m_worldMatrix->dx(); double y2n = x2 * m_worldMatrix->m12() + y2 * m_worldMatrix->m22() + m_worldMatrix->dy(); double dx = x2n - x1n; double dy = y2n - y1n; double scale = 1.0 / (dx * dx + dy * dy); linear->a = dx * scale; linear->b = dy * scale; linear->c = -(x1n * linear->a + y1n * linear->b); ArtRender *render = art_render_new(0, 0, m_width, m_height, m_buffer, m_rowstride, 3, 8, ART_ALPHA_SEPARATE, 0); art_render_svp(render, svp); art_render_gradient_linear(render, linear, ART_FILTER_HYPER); art_render_invoke(render); } } void applyRadialGradient(ArtSVP *svp, const TQString &ref) { ArtGradientRadial *radial = m_radialGradientMap[ref]; if(radial) { TQDomElement element = m_radialGradientElementMap[radial]; double cx, cy, r, fx, fy; if(element.hasAttribute("cx")) cx = m_painter->toPixel(element.attribute("cx"), true); else cx = 50; if(element.hasAttribute("cy")) cy = m_painter->toPixel(element.attribute("cy"), false); else cy = 50; if(element.hasAttribute("r")) r = m_painter->toPixel(element.attribute("r"), true); else r = 50; if(element.hasAttribute("fx")) fx = m_painter->toPixel(element.attribute("fx"), false); else fx = cx; if(element.hasAttribute("fy")) fy = m_painter->toPixel(element.attribute("fy"), false); else fy = cy; radial->affine[0] = m_worldMatrix->m11(); radial->affine[1] = m_worldMatrix->m12(); radial->affine[2] = m_worldMatrix->m21(); radial->affine[3] = m_worldMatrix->m22(); radial->affine[4] = m_worldMatrix->dx(); radial->affine[5] = m_worldMatrix->dy(); radial->fx = (fx - cx) / r; radial->fy = (fy - cy) / r; double aff1[6], aff2[6], gradTransform[6]; // Respect gradientTransform TQWMatrix m = m_painter->parseTransform(element.attribute("gradientTransform")); gradTransform[0] = m.m11(); gradTransform[1] = m.m12(); gradTransform[2] = m.m21(); gradTransform[3] = m.m22(); gradTransform[4] = m.dx(); gradTransform[5] = m.dy(); art_affine_scale(aff1, r, r); art_affine_translate(aff2, cx, cy); art_affine_multiply(aff1, aff1, aff2); art_affine_multiply(aff1, aff1, gradTransform); art_affine_multiply(aff1, aff1, radial->affine); art_affine_invert(radial->affine, aff1); ArtRender *render = art_render_new(0, 0, m_width, m_height, m_buffer, m_rowstride, 3, 8, ART_ALPHA_SEPARATE, 0); art_render_svp(render, svp); art_render_gradient_radial(render, radial, ART_FILTER_HYPER); art_render_invoke(render); } } void applyGradient(ArtSVP *svp, const TQString &ref) { ArtGradientLinear *linear = m_linearGradientMap[ref]; if(linear) { TQDomElement element = m_linearGradientElementMap[linear]; if(!element.hasAttribute("xlink:href")) { applyLinearGradient(svp, ref); return; } else { ArtGradientLinear *linear = m_linearGradientMap[element.attribute("xlink:href").mid(1)]; TQDomElement newElement = m_linearGradientElementMap[linear]; // Saved 'old' attributes TQDict refattrs; refattrs.setAutoDelete(true); for(unsigned int i = 0; i < newElement.attributes().length(); ++i) refattrs.insert(newElement.attributes().item(i).nodeName(), new TQString(newElement.attributes().item(i).nodeValue())); // Copy attributes if(!newElement.isNull()) { TQDomNamedNodeMap attr = element.attributes(); for(unsigned int i = 0; i < attr.length(); i++) { TQString name = attr.item(i).nodeName(); if(name != "xlink:href" && name != "id") newElement.setAttribute(name, attr.item(i).nodeValue()); } } applyGradient(svp, element.attribute("xlink:href").mid(1)); // Restore attributes TQDictIterator itr(refattrs); for(; itr.current(); ++itr) newElement.setAttribute(itr.currentKey(), *(itr.current())); return; } } ArtGradientRadial *radial = m_radialGradientMap[ref]; if(radial) { TQDomElement element = m_radialGradientElementMap[radial]; if(!element.hasAttribute("xlink:href")) { applyRadialGradient(svp, ref); return; } else { ArtGradientRadial *radial = m_radialGradientMap[element.attribute("xlink:href").mid(1)]; TQDomElement newElement = m_radialGradientElementMap[radial]; // Saved 'old' attributes TQDict refattrs; refattrs.setAutoDelete(true); for(unsigned int i = 0; i < newElement.attributes().length(); ++i) refattrs.insert(newElement.attributes().item(i).nodeName(), new TQString(newElement.attributes().item(i).nodeValue())); // Copy attributes if(!newElement.isNull()) { TQDomNamedNodeMap attr = element.attributes(); for(unsigned int i = 0; i < attr.length(); i++) { TQString name = attr.item(i).nodeName(); if(name != "xlink:href" && name != "id") newElement.setAttribute(name, attr.item(i).nodeValue()); } } applyGradient(svp, element.attribute("xlink:href").mid(1)); // Restore attributes TQDictIterator itr(refattrs); for(; itr.current(); ++itr) newElement.setAttribute(itr.currentKey(), *(itr.current())); return; } } } void applyGradient(ArtSVP *svp, bool fill) { TQString ref; if(fill) { m_useFillGradient = false; ref = m_fillGradientReference; } else { m_useStrokeGradient = false; ref = m_strokeGradientReference; } applyGradient(svp, ref); } void blit() { unsigned char *line = m_buffer; for(int y = 0; y < m_height; y++) { TQRgb *sl = reinterpret_cast(m_image->scanLine(y)); for(int x = 0; x < m_width; x++) sl[x] = tqRgba(line[x * 4], line[x * 4 + 1], line[x * 4 + 2], line[x * 4 + 3]); line += m_rowstride; } } void calculateArc(bool relative, TQMemArray &vec, int &index, double &curx, double &cury, double angle, double x, double y, double r1, double r2, bool largeArcFlag, bool sweepFlag) { double sin_th, cos_th; double a00, a01, a10, a11; double x0, y0, x1, y1, xc, yc; double d, sfactor, sfactor_sq; double th0, th1, th_arc; int i, n_segs; sin_th = sin(angle * (M_PI / 180.0)); cos_th = cos(angle * (M_PI / 180.0)); double dx; if(!relative) dx = (curx - x) / 2.0; else dx = -x / 2.0; double dy; if(!relative) dy = (cury - y) / 2.0; else dy = -y / 2.0; double _x1 = cos_th * dx + sin_th * dy; double _y1 = -sin_th * dx + cos_th * dy; double Pr1 = r1 * r1; double Pr2 = r2 * r2; double Px = _x1 * _x1; double Py = _y1 * _y1; // Spec : check if radii are large enough double check = Px / Pr1 + Py / Pr2; if(check > 1) { r1 = r1 * sqrt(check); r2 = r2 * sqrt(check); } a00 = cos_th / r1; a01 = sin_th / r1; a10 = -sin_th / r2; a11 = cos_th / r2; x0 = a00 * curx + a01 * cury; y0 = a10 * curx + a11 * cury; if(!relative) x1 = a00 * x + a01 * y; else x1 = a00 * (curx + x) + a01 * (cury + y); if(!relative) y1 = a10 * x + a11 * y; else y1 = a10 * (curx + x) + a11 * (cury + y); /* (x0, y0) is current point in transformed coordinate space. (x1, y1) is new point in transformed coordinate space. The arc fits a unit-radius circle in this space. */ d = (x1 - x0) * (x1 - x0) + (y1 - y0) * (y1 - y0); sfactor_sq = 1.0 / d - 0.25; if(sfactor_sq < 0) sfactor_sq = 0; sfactor = sqrt(sfactor_sq); if(sweepFlag == largeArcFlag) sfactor = -sfactor; xc = 0.5 * (x0 + x1) - sfactor * (y1 - y0); yc = 0.5 * (y0 + y1) + sfactor * (x1 - x0); /* (xc, yc) is center of the circle. */ th0 = atan2(y0 - yc, x0 - xc); th1 = atan2(y1 - yc, x1 - xc); th_arc = th1 - th0; if(th_arc < 0 && sweepFlag) th_arc += 2 * M_PI; else if(th_arc > 0 && !sweepFlag) th_arc -= 2 * M_PI; n_segs = (int) (int) ceil(fabs(th_arc / (M_PI * 0.5 + 0.001))); for(i = 0; i < n_segs; i++) { index++; ensureSpace(vec, index); { double sin_th, cos_th; double a00, a01, a10, a11; double x1, y1, x2, y2, x3, y3; double t; double th_half; double _th0 = th0 + i * th_arc / n_segs; double _th1 = th0 + (i + 1) * th_arc / n_segs; sin_th = sin(angle * (M_PI / 180.0)); cos_th = cos(angle * (M_PI / 180.0)); /* inverse transform compared with rsvg_path_arc */ a00 = cos_th * r1; a01 = -sin_th * r2; a10 = sin_th * r1; a11 = cos_th * r2; th_half = 0.5 * (_th1 - _th0); t = (8.0 / 3.0) * sin(th_half * 0.5) * sin(th_half * 0.5) / sin(th_half); x1 = xc + cos(_th0) - t * sin(_th0); y1 = yc + sin(_th0) + t * cos(_th0); x3 = xc + cos(_th1); y3 = yc + sin(_th1); x2 = x3 + t * sin(_th1); y2 = y3 - t * cos(_th1); ensureSpace(vec, index); vec[index].code = ART_CURVETO; vec[index].x1 = a00 * x1 + a01 * y1; vec[index].y1 = a10 * x1 + a11 * y1; vec[index].x2 = a00 * x2 + a01 * y2; vec[index].y2 = a10 * x2 + a11 * y2; vec[index].x3 = a00 * x3 + a01 * y3; vec[index].y3 = a10 * x3 + a11 * y3; } } if(!relative) curx = x; else curx += x; if(!relative) cury = y; else cury += y; } // For any docs, see the libart library static void art_vpath_render_bez(ArtVpath **p_vpath, int *pn, int *pn_max, double x0, double y0, double x1, double y1, double x2, double y2, double x3, double y3, double flatness) { double x3_0, y3_0, z3_0_dot, z1_dot, z2_dot; double z1_perp, z2_perp, max_perp_sq; double x_m, y_m, xa1, ya1, xa2, ya2, xb1, yb1, xb2, yb2; x3_0 = x3 - x0; y3_0 = y3 - y0; z3_0_dot = x3_0 * x3_0 + y3_0 * y3_0; if (z3_0_dot < 0.001) goto nosubdivide; max_perp_sq = flatness * flatness * z3_0_dot; z1_perp = (y1 - y0) * x3_0 - (x1 - x0) * y3_0; if (z1_perp * z1_perp > max_perp_sq) goto subdivide; z2_perp = (y3 - y2) * x3_0 - (x3 - x2) * y3_0; if (z2_perp * z2_perp > max_perp_sq) goto subdivide; z1_dot = (x1 - x0) * x3_0 + (y1 - y0) * y3_0; if (z1_dot < 0 && z1_dot * z1_dot > max_perp_sq) goto subdivide; z2_dot = (x3 - x2) * x3_0 + (y3 - y2) * y3_0; if (z2_dot < 0 && z2_dot * z2_dot > max_perp_sq) goto subdivide; if (z1_dot + z1_dot > z3_0_dot) goto subdivide; if (z2_dot + z2_dot > z3_0_dot) goto subdivide; nosubdivide: art_vpath_add_point (p_vpath, pn, pn_max, ART_LINETO, x3, y3); return; subdivide: xa1 = (x0 + x1) * 0.5; ya1 = (y0 + y1) * 0.5; xa2 = (x0 + 2 * x1 + x2) * 0.25; ya2 = (y0 + 2 * y1 + y2) * 0.25; xb1 = (x1 + 2 * x2 + x3) * 0.25; yb1 = (y1 + 2 * y2 + y3) * 0.25; xb2 = (x2 + x3) * 0.5; yb2 = (y2 + y3) * 0.5; x_m = (xa2 + xb1) * 0.5; y_m = (ya2 + yb1) * 0.5; art_vpath_render_bez (p_vpath, pn, pn_max, x0, y0, xa1, ya1, xa2, ya2, x_m, y_m, flatness); art_vpath_render_bez (p_vpath, pn, pn_max, x_m, y_m, xb1, yb1, xb2, yb2, x3, y3, flatness); } ArtVpath *art_bez_path_to_vec(const ArtBpath *bez, double flatness) { ArtVpath *vec; int vec_n, vec_n_max; int bez_index; double x, y; vec_n = 0; vec_n_max = (1 << 4); vec = art_new (ArtVpath, vec_n_max); x = 0; y = 0; bez_index = 0; do { if(vec_n >= vec_n_max) art_expand (vec, ArtVpath, vec_n_max); switch (bez[bez_index].code) { case ART_MOVETO_OPEN: case ART_MOVETO: case ART_LINETO: x = bez[bez_index].x3; y = bez[bez_index].y3; vec[vec_n].code = bez[bez_index].code; vec[vec_n].x = x; vec[vec_n].y = y; vec_n++; break; case ART_END: vec[vec_n].code = ART_END; vec[vec_n].x = 0; vec[vec_n].y = 0; vec_n++; break; case ART_END2: vec[vec_n].code = (ArtPathcode)ART_END2; vec[vec_n].x = bez[bez_index].x3; vec[vec_n].y = bez[bez_index].y3; vec_n++; break; case ART_CURVETO: art_vpath_render_bez (&vec, &vec_n, &vec_n_max, x, y, bez[bez_index].x1, bez[bez_index].y1, bez[bez_index].x2, bez[bez_index].y2, bez[bez_index].x3, bez[bez_index].y3, flatness); x = bez[bez_index].x3; y = bez[bez_index].y3; break; } } while (bez[bez_index++].code != ART_END); return vec; } static void art_rgb_affine_run(int *p_x0, int *p_x1, int y, int src_width, int src_height, const double affine[6]) { int x0, x1; double z; double x_intercept; int xi; x0 = *p_x0; x1 = *p_x1; if (affine[0] > 1e-6) { z = affine[2] * (y + 0.5) + affine[4]; x_intercept = -z / affine[0]; xi = (int) (int) ceil (x_intercept + 1e-6 - 0.5); if (xi > x0) x0 = xi; x_intercept = (-z + src_width) / affine[0]; xi = (int) ceil (x_intercept - 1e-6 - 0.5); if (xi < x1) x1 = xi; } else if (affine[0] < -1e-6) { z = affine[2] * (y + 0.5) + affine[4]; x_intercept = (-z + src_width) / affine[0]; xi = (int) ceil (x_intercept + 1e-6 - 0.5); if (xi > x0) x0 = xi; x_intercept = -z / affine[0]; xi = (int) ceil (x_intercept - 1e-6 - 0.5); if (xi < x1) x1 = xi; } else { z = affine[2] * (y + 0.5) + affine[4]; if (z < 0 || z >= src_width) { *p_x1 = *p_x0; return; } } if (affine[1] > 1e-6) { z = affine[3] * (y + 0.5) + affine[5]; x_intercept = -z / affine[1]; xi = (int) ceil (x_intercept + 1e-6 - 0.5); if (xi > x0) x0 = xi; x_intercept = (-z + src_height) / affine[1]; xi = (int) ceil (x_intercept - 1e-6 - 0.5); if (xi < x1) x1 = xi; } else if (affine[1] < -1e-6) { z = affine[3] * (y + 0.5) + affine[5]; x_intercept = (-z + src_height) / affine[1]; xi = (int) ceil (x_intercept + 1e-6 - 0.5); if (xi > x0) x0 = xi; x_intercept = -z / affine[1]; xi = (int) ceil (x_intercept - 1e-6 - 0.5); if (xi < x1) x1 = xi; } else { z = affine[3] * (y + 0.5) + affine[5]; if (z < 0 || z >= src_height) { *p_x1 = *p_x0; return; } } *p_x0 = x0; *p_x1 = x1; } // Slightly modified version to support RGBA buffers, copied from gnome-print static void art_rgba_rgba_affine(art_u8 *dst, int x0, int y0, int x1, int y1, int dst_rowstride, const art_u8 *src, int src_width, int src_height, int src_rowstride, const double affine[6]) { int x, y; double inv[6]; art_u8 *dst_p, *dst_linestart; const art_u8 *src_p; ArtPoint pt, src_pt; int src_x, src_y; int alpha; art_u8 bg_r, bg_g, bg_b, bg_a, cr, cg, cb; art_u8 fg_r, fg_g, fg_b; int tmp; int run_x0, run_x1; dst_linestart = dst; art_affine_invert (inv, affine); for (y = y0; y < y1; y++) { pt.y = y + 0.5; run_x0 = x0; run_x1 = x1; art_rgb_affine_run (&run_x0, &run_x1, y, src_width, src_height, inv); dst_p = dst_linestart + (run_x0 - x0) * 4; for (x = run_x0; x < run_x1; x++) { pt.x = x + 0.5; art_affine_point (&src_pt, &pt, inv); src_x = (int) floor (src_pt.x); src_y = (int) floor (src_pt.y); src_p = src + (src_y * src_rowstride) + src_x * 4; if (src_x >= 0 && src_x < src_width && src_y >= 0 && src_y < src_height) { alpha = src_p[3]; if (alpha) { if (alpha == 255) { dst_p[0] = src_p[0]; dst_p[1] = src_p[1]; dst_p[2] = src_p[2]; dst_p[3] = 255; } else { bg_r = dst_p[0]; bg_g = dst_p[1]; bg_b = dst_p[2]; bg_a = dst_p[3]; cr = (bg_r * bg_a + 0x80) >> 8; cg = (bg_g * bg_g + 0x80) >> 8; cb = (bg_b * bg_b + 0x80) >> 8; tmp = (src_p[0] - bg_r) * alpha; fg_r = bg_r + ((tmp + (tmp >> 8) + 0x80) >> 8); tmp = (src_p[1] - bg_g) * alpha; fg_g = bg_g + ((tmp + (tmp >> 8) + 0x80) >> 8); tmp = (src_p[2] - bg_b) * alpha; fg_b = bg_b + ((tmp + (tmp >> 8) + 0x80) >> 8); dst_p[0] = fg_r; dst_p[1] = fg_g; dst_p[2] = fg_b; dst_p[3] = bg_a + (((255 - bg_a) * alpha + 0x80) >> 8); } } } else { dst_p[0] = 255; dst_p[1] = 0; dst_p[2] = 0; dst_p[3] = 255;} dst_p += 4; } dst_linestart += dst_rowstride; } } private: friend class KSVGIconPainter; ArtSVP *m_clipSVP; TQImage *m_image; TQWMatrix *m_worldMatrix; TQString m_fillRule; TQString m_joinStyle; TQString m_capStyle; int m_strokeMiterLimit; TQString m_dashes; unsigned short m_dashOffset; TQColor m_fillColor; TQColor m_strokeColor; art_u8 *m_buffer; art_u8 *m_tempBuffer; int m_width; int m_height; int m_rowstride; double m_opacity; double m_fillOpacity; double m_strokeOpacity; bool m_useFill; bool m_useStroke; bool m_useFillGradient; bool m_useStrokeGradient; TQString m_fillGradientReference; TQString m_strokeGradientReference; TQMap m_linearGradientMap; TQMap m_linearGradientElementMap; TQMap m_radialGradientMap; TQMap m_radialGradientElementMap; KSVGIconPainter *m_painter; double m_strokeWidth; }; struct KSVGIconPainter::Private { KSVGIconPainterHelper *helper; int drawWidth; int drawHeight; }; KSVGIconPainter::KSVGIconPainter(int width, int height) : d(new Private()) { d->helper = new KSVGIconPainterHelper(width, height, this); d->drawWidth = width; d->drawHeight = height; } KSVGIconPainter::~KSVGIconPainter() { delete d->helper; delete d; } void KSVGIconPainter::setDrawWidth(int dwidth) { d->drawWidth = dwidth; } void KSVGIconPainter::setDrawHeight(int dheight) { d->drawHeight = dheight; } void KSVGIconPainter::finish() { d->helper->blit(); } TQImage *KSVGIconPainter::image() { return new TQImage(*d->helper->m_image); } TQWMatrix *KSVGIconPainter::worldMatrix() { return d->helper->m_worldMatrix; } void KSVGIconPainter::setWorldMatrix(TQWMatrix *matrix) { if(d->helper->m_worldMatrix) delete d->helper->m_worldMatrix; d->helper->m_worldMatrix = matrix; } void KSVGIconPainter::setStrokeWidth(double width) { d->helper->m_strokeWidth = width; } void KSVGIconPainter::setStrokeMiterLimit(const TQString &miter) { d->helper->m_strokeMiterLimit = miter.toInt(); } void KSVGIconPainter::setStrokeDashOffset(const TQString &dashOffset) { d->helper->m_dashOffset = dashOffset.toUInt(); } void KSVGIconPainter::setStrokeDashArray(const TQString &dashes) { d->helper->m_dashes = dashes; } void KSVGIconPainter::setCapStyle(const TQString &cap) { d->helper->m_capStyle = cap; } void KSVGIconPainter::setJoinStyle(const TQString &join) { d->helper->m_joinStyle = join; } void KSVGIconPainter::setStrokeColor(const TQString &stroke) { if(stroke.startsWith("url")) { d->helper->m_useStroke = false; d->helper->m_useStrokeGradient = true; TQString url = stroke; unsigned int start = url.find("#") + 1; unsigned int end = url.findRev(")"); d->helper->m_strokeGradientReference = url.mid(start, end - start); } else { d->helper->m_strokeColor = parseColor(stroke); d->helper->m_useStrokeGradient = false; d->helper->m_strokeGradientReference = TQString::null; if(stroke.stripWhiteSpace().lower() != "none") setUseStroke(true); else setUseStroke(false); } } void KSVGIconPainter::setFillColor(const TQString &fill) { if(fill.startsWith("url")) { d->helper->m_useFill = false; d->helper->m_useFillGradient = true; TQString url = fill; unsigned int start = url.find("#") + 1; unsigned int end = url.findRev(")"); d->helper->m_fillGradientReference = url.mid(start, end - start); } else { d->helper->m_fillColor = parseColor(fill); d->helper->m_useFillGradient = false; d->helper->m_fillGradientReference = TQString::null; if(fill.stripWhiteSpace().lower() != "none") setUseFill(true); else setUseFill(false); } } void KSVGIconPainter::setFillRule(const TQString &fillRule) { d->helper->m_fillRule = fillRule; } TQ_UINT32 KSVGIconPainter::parseOpacity(const TQString &data) { int opacity = 255; if(!data.isEmpty()) { double temp; if(data.contains("%")) { TQString tempString = data.left(data.length() - 1); temp = double(255 * tempString.toDouble()) / 100.0; } else temp = data.toDouble(); opacity = (int) floor(temp * 255 + 0.5); } return opacity; } void KSVGIconPainter::setFillOpacity(const TQString &fillOpacity) { d->helper->m_fillOpacity = parseOpacity(fillOpacity); } void KSVGIconPainter::setStrokeOpacity(const TQString &strokeOpacity) { d->helper->m_strokeOpacity = parseOpacity(strokeOpacity); } void KSVGIconPainter::setOpacity(const TQString &opacity) { d->helper->m_opacity = parseOpacity(opacity); } void KSVGIconPainter::setUseFill(bool fill) { d->helper->m_useFill = fill; } void KSVGIconPainter::setUseStroke(bool stroke) { d->helper->m_useStroke = stroke; } void KSVGIconPainter::setClippingRect(int x, int y, int w, int h) { ArtVpath *vec = d->helper->allocVPath(6); vec[0].code = ART_MOVETO; vec[0].x = x; vec[0].y = y; vec[1].code = ART_LINETO; vec[1].x = x; vec[1].y = y + h; vec[2].code = ART_LINETO; vec[2].x = x + w; vec[2].y = y + h; vec[3].code = ART_LINETO; vec[3].x = x + w; vec[3].y = y; vec[4].code = ART_LINETO; vec[4].x = x; vec[4].y = y; vec[5].code = ART_END; if(d->helper->m_clipSVP) art_svp_free(d->helper->m_clipSVP); d->helper->m_clipSVP = art_svp_from_vpath(vec); art_free(vec); } void KSVGIconPainter::drawRectangle(double x, double y, double w, double h, double rx, double ry) { if((int) rx != 0 && (int) ry != 0) { ArtVpath *res; ArtBpath *vec = d->helper->allocBPath(10); int i = 0; if(rx > w / 2) rx = w / 2; if(ry > h / 2) ry = h / 2; vec[i].code = ART_MOVETO_OPEN; vec[i].x3 = x + rx; vec[i].y3 = y; i++; vec[i].code = ART_CURVETO; vec[i].x1 = x + rx * (1 - 0.552); vec[i].y1 = y; vec[i].x2 = x; vec[i].y2 = y + ry * (1 - 0.552); vec[i].x3 = x; vec[i].y3 = y + ry; i++; if(ry < h / 2) { vec[i].code = ART_LINETO; vec[i].x3 = x; vec[i].y3 = y + h - ry; i++; } vec[i].code = ART_CURVETO; vec[i].x1 = x; vec[i].y1 = y + h - ry * (1 - 0.552); vec[i].x2 = x + rx * (1 - 0.552); vec[i].y2 = y + h; vec[i].x3 = x + rx; vec[i].y3 = y + h; i++; if(rx < w / 2) { vec[i].code = ART_LINETO; vec[i].x3 = x + w - rx; vec[i].y3 = y + h; i++; } vec[i].code = ART_CURVETO; vec[i].x1 = x + w - rx * (1 - 0.552); vec[i].y1 = y + h; vec[i].x2 = x + w; vec[i].y2 = y + h - ry * (1 - 0.552); vec[i].x3 = x + w; vec[i].y3 = y + h - ry; i++; if(ry < h / 2) { vec[i].code = ART_LINETO; vec[i].x3 = x + w; vec[i].y3 = y + ry; i++; } vec[i].code = ART_CURVETO; vec[i].x1 = x + w; vec[i].y1 = y + ry * (1 - 0.552); vec[i].x2 = x + w - rx * (1 - 0.552); vec[i].y2 = y; vec[i].x3 = x + w - rx; vec[i].y3 = y; i++; if(rx < w / 2) { vec[i].code = ART_LINETO; vec[i].x3 = x + rx; vec[i].y3 = y; i++; } vec[i].code = ART_END; res = d->helper->art_bez_path_to_vec(vec, 0.25); art_free(vec); d->helper->drawVPath(res); } else { ArtVpath *vec = d->helper->allocVPath(6); vec[0].code = ART_MOVETO; vec[0].x = x; vec[0].y = y; vec[1].code = ART_LINETO; vec[1].x = x; vec[1].y = y + h; vec[2].code = ART_LINETO; vec[2].x = x + w; vec[2].y = y + h; vec[3].code = ART_LINETO; vec[3].x = x + w; vec[3].y = y; vec[4].code = ART_LINETO; vec[4].x = x; vec[4].y = y; vec[5].code = ART_END; d->helper->drawVPath(vec); } } void KSVGIconPainter::drawEllipse(double cx, double cy, double rx, double ry) { ArtBpath *temp; temp = d->helper->allocBPath(6); double x1, y1, x2, y2, x3, y3; double len = 0.55228474983079356; double cos4[] = {1.0, 0.0, -1.0, 0.0, 1.0}; double sin4[] = {0.0, 1.0, 0.0, -1.0, 0.0}; int i = 0; temp[i].code = ART_MOVETO; temp[i].x3 = cx + rx; temp[i].y3 = cy; i++; while(i < 5) { x1 = cos4[i-1] + len * cos4[i]; y1 = sin4[i-1] + len * sin4[i]; x2 = cos4[i] + len * cos4[i-1]; y2 = sin4[i] + len * sin4[i-1]; x3 = cos4[i]; y3 = sin4[i]; temp[i].code = ART_CURVETO; temp[i].x1 = cx + x1 * rx; temp[i].y1 = cy + y1 * ry; temp[i].x2 = cx + x2 * rx; temp[i].y2 = cy + y2 * ry; temp[i].x3 = cx + x3 * rx; temp[i].y3 = cy + y3 * ry; i++; } temp[i].code = ART_END; d->helper->drawBPath(temp); art_free(temp); } void KSVGIconPainter::drawLine(double x1, double y1, double x2, double y2) { ArtVpath *vec; vec = d->helper->allocVPath(3); vec[0].code = ART_MOVETO_OPEN; vec[0].x = x1; vec[0].y = y1; vec[1].code = ART_LINETO; vec[1].x = x2; vec[1].y = y2; vec[2].code = ART_END; d->helper->drawVPath(vec); } void KSVGIconPainter::drawPolyline(TQPointArray polyArray, int points) { if(polyArray.point(0).x() == -1 || polyArray.point(0).y() == -1) return; ArtVpath *polyline; if(points == -1) points = polyArray.count(); polyline = d->helper->allocVPath(3 + points); polyline[0].code = ART_MOVETO; polyline[0].x = polyArray.point(0).x(); polyline[0].y = polyArray.point(0).y(); int index; for(index = 1; index < points; index++) { TQPoint point = polyArray.point(index); polyline[index].code = ART_LINETO; polyline[index].x = point.x(); polyline[index].y = point.y(); } if(d->helper->m_useFill) // if the polyline must be filled, inform libart that it should not be closed. { polyline[index].code = (ArtPathcode)ART_END2; polyline[index].x = polyArray.point(0).x(); polyline[index++].y = polyArray.point(0).y(); } polyline[index].code = ART_END; d->helper->drawVPath(polyline); } void KSVGIconPainter::drawPolygon(TQPointArray polyArray) { ArtVpath *polygon; polygon = d->helper->allocVPath(3 + polyArray.count()); polygon[0].code = ART_MOVETO; polygon[0].x = polyArray.point(0).x(); polygon[0].y = polyArray.point(0).y(); unsigned int index; for(index = 1; index < polyArray.count(); index++) { TQPoint point = polyArray.point(index); polygon[index].code = ART_LINETO; polygon[index].x = point.x(); polygon[index].y = point.y(); } polygon[index].code = ART_LINETO; polygon[index].x = polyArray.point(0).x(); polygon[index].y = polyArray.point(0).y(); index++; polygon[index].code = ART_END; d->helper->drawVPath(polygon); } // Path parsing tool // parses the coord into number and forwards to the next token static const char *getCoord(const char *ptr, double &number) { int integer, exponent; double decimal, frac; int sign, expsign; exponent = 0; integer = 0; frac = 1.0; decimal = 0; sign = 1; expsign = 1; // read the sign if(*ptr == '+') ptr++; else if(*ptr == '-') { ptr++; sign = -1; } // read the integer part while(*ptr != '\0' && *ptr >= '0' && *ptr <= '9') integer = (integer * 10) + *(ptr++) - '0'; if(*ptr == '.') // read the decimals { ptr++; while(*ptr != '\0' && *ptr >= '0' && *ptr <= '9') decimal += (*(ptr++) - '0') * (frac *= 0.1); } if(*ptr == 'e' || *ptr == 'E') // read the exponent part { ptr++; // read the sign of the exponent if(*ptr == '+') ptr++; else if(*ptr == '-') { ptr++; expsign = -1; } exponent = 0; while(*ptr != '\0' && *ptr >= '0' && *ptr <= '9') { exponent *= 10; exponent += *ptr - '0'; ptr++; } } number = integer + decimal; number *= sign * pow(10.0, expsign * exponent); // skip the following space if(*ptr == ' ') ptr++; return ptr; } void KSVGIconPainter::drawPath(const TQString &data, bool filled) { if (!data.isEmpty()) { TQString value = data; TQMemArray vec; int index = -1; double curx = 0.0, cury = 0.0, contrlx = 0.0, contrly = 0.0, xc, yc; unsigned int lastCommand = 0; TQString _d = value.replace(",", " "); _d = _d.simplifyWhiteSpace(); const char *ptr = _d.latin1(); const char *end = _d.latin1() + _d.length() + 1; double tox, toy, x1, y1, x2, y2, rx, ry, angle; bool largeArc, sweep; char command = *(ptr++); while(ptr < end) { if(*ptr == ' ') ptr++; switch(command) { case 'm': ptr = getCoord(ptr, tox); ptr = getCoord(ptr, toy); if(index != -1 && lastCommand != 'z') { // Find last subpath int find = -1; for(int i = index; i >= 0; i--) { if(vec[i].code == ART_MOVETO_OPEN || vec[i].code == ART_MOVETO) { find = i; break; } } index++; if(vec.size() == (unsigned int) index) vec.resize(index + 1); vec[index].code = (ArtPathcode)ART_END2; vec[index].x3 = vec[find].x3; vec[index].y3 = vec[find].y3; } curx += tox; cury += toy; index++; d->helper->ensureSpace(vec, index); vec[index].code = (index == 0) ? ART_MOVETO : ART_MOVETO_OPEN; vec[index].x3 = curx; vec[index].y3 = cury; lastCommand = 'm'; break; case 'M': ptr = getCoord(ptr, tox); ptr = getCoord(ptr, toy); if(index != -1 && lastCommand != 'z') { // Find last subpath int find = -1; for(int i = index; i >= 0; i--) { if(vec[i].code == ART_MOVETO_OPEN || vec[i].code == ART_MOVETO) { find = i; break; } } index++; if(vec.size() == (unsigned int) index) vec.resize(index + 1); vec[index].code = (ArtPathcode)ART_END2; vec[index].x3 = vec[find].x3; vec[index].y3 = vec[find].y3; } curx = tox; cury = toy; index++; d->helper->ensureSpace(vec, index); vec[index].code = (index == 0) ? ART_MOVETO : ART_MOVETO_OPEN; vec[index].x3 = curx; vec[index].y3 = cury; lastCommand = 'M'; break; case 'l': ptr = getCoord(ptr, tox); ptr = getCoord(ptr, toy); index++; d->helper->ensureSpace(vec, index); vec[index].code = ART_LINETO; vec[index].x3 = curx + tox; vec[index].y3 = cury + toy; curx += tox; cury += toy; lastCommand = 'l'; break; case 'L': ptr = getCoord(ptr, tox); ptr = getCoord(ptr, toy); index++; d->helper->ensureSpace(vec, index); vec[index].code = ART_LINETO; vec[index].x3 = tox; vec[index].y3 = toy; curx = tox; cury = toy; lastCommand = 'L'; break; case 'h': ptr = getCoord(ptr, tox); index++; curx += tox; d->helper->ensureSpace(vec, index); vec[index].code = ART_LINETO; vec[index].x3 = curx; vec[index].y3 = cury; lastCommand = 'h'; break; case 'H': ptr = getCoord(ptr, tox); index++; curx = tox; d->helper->ensureSpace(vec, index); vec[index].code = ART_LINETO; vec[index].x3 = curx; vec[index].y3 = cury; lastCommand = 'H'; break; case 'v': ptr = getCoord(ptr, toy); index++; cury += toy; d->helper->ensureSpace(vec, index); vec[index].code = ART_LINETO; vec[index].x3 = curx; vec[index].y3 = cury; lastCommand = 'v'; break; case 'V': ptr = getCoord(ptr, toy); index++; cury = toy; d->helper->ensureSpace(vec, index); vec[index].code = ART_LINETO; vec[index].x3 = curx; vec[index].y3 = cury; lastCommand = 'V'; break; case 'c': ptr = getCoord(ptr, x1); ptr = getCoord(ptr, y1); ptr = getCoord(ptr, x2); ptr = getCoord(ptr, y2); ptr = getCoord(ptr, tox); ptr = getCoord(ptr, toy); index++; d->helper->ensureSpace(vec, index); vec[index].code = ART_CURVETO; vec[index].x1 = curx + x1; vec[index].y1 = cury + y1; vec[index].x2 = curx + x2; vec[index].y2 = cury + y2; vec[index].x3 = curx + tox; vec[index].y3 = cury + toy; curx += tox; cury += toy; contrlx = vec[index].x2; contrly = vec[index].y2; lastCommand = 'c'; break; case 'C': ptr = getCoord(ptr, x1); ptr = getCoord(ptr, y1); ptr = getCoord(ptr, x2); ptr = getCoord(ptr, y2); ptr = getCoord(ptr, tox); ptr = getCoord(ptr, toy); index++; d->helper->ensureSpace(vec, index); vec[index].code = ART_CURVETO; vec[index].x1 = x1; vec[index].y1 = y1; vec[index].x2 = x2; vec[index].y2 = y2; vec[index].x3 = tox; vec[index].y3 = toy; curx = vec[index].x3; cury = vec[index].y3; contrlx = vec[index].x2; contrly = vec[index].y2; lastCommand = 'C'; break; case 's': ptr = getCoord(ptr, x2); ptr = getCoord(ptr, y2); ptr = getCoord(ptr, tox); ptr = getCoord(ptr, toy); index++; d->helper->ensureSpace(vec, index); vec[index].code = ART_CURVETO; vec[index].x1 = 2 * curx - contrlx; vec[index].y1 = 2 * cury - contrly; vec[index].x2 = curx + x2; vec[index].y2 = cury + y2; vec[index].x3 = curx + tox; vec[index].y3 = cury + toy; curx += tox; cury += toy; contrlx = vec[index].x2; contrly = vec[index].y2; lastCommand = 's'; break; case 'S': ptr = getCoord(ptr, x2); ptr = getCoord(ptr, y2); ptr = getCoord(ptr, tox); ptr = getCoord(ptr, toy); index++; d->helper->ensureSpace(vec, index); vec[index].code = ART_CURVETO; vec[index].x1 = 2 * curx - contrlx; vec[index].y1 = 2 * cury - contrly; vec[index].x2 = x2; vec[index].y2 = y2; vec[index].x3 = tox; vec[index].y3 = toy; curx = vec[index].x3; cury = vec[index].y3; contrlx = vec[index].x2; contrly = vec[index].y2; lastCommand = 'S'; break; case 'q': ptr = getCoord(ptr, x1); ptr = getCoord(ptr, y1); ptr = getCoord(ptr, tox); ptr = getCoord(ptr, toy); index++; d->helper->ensureSpace(vec, index); vec[index].code = ART_CURVETO; vec[index].x1 = (curx + 2 * (x1 + curx)) * (1.0 / 3.0); vec[index].y1 = (cury + 2 * (y1 + cury)) * (1.0 / 3.0); vec[index].x2 = ((curx + tox) + 2 * (x1 + curx)) * (1.0 / 3.0); vec[index].y2 = ((cury + toy) + 2 * (y1 + cury)) * (1.0 / 3.0); vec[index].x3 = curx + tox; vec[index].y3 = cury + toy; contrlx = curx + x1; contrly = cury + y1; curx += tox; cury += toy; lastCommand = 'q'; break; case 'Q': ptr = getCoord(ptr, x1); ptr = getCoord(ptr, y1); ptr = getCoord(ptr, tox); ptr = getCoord(ptr, toy); index++; d->helper->ensureSpace(vec, index); // TODO : if this fails make it more like QuadraticRel vec[index].code = ART_CURVETO; vec[index].x1 = (curx + 2 * x1) * (1.0 / 3.0); vec[index].y1 = (cury + 2 * y1) * (1.0 / 3.0); vec[index].x2 = (tox + 2 * x1) * (1.0 / 3.0); vec[index].y2 = (toy + 2 * y1) * (1.0 / 3.0); vec[index].x3 = tox; vec[index].y3 = toy; curx = vec[index].x3; cury = vec[index].y3; contrlx = vec[index].x2; contrly = vec[index].y2; lastCommand = 'Q'; break; case 't': ptr = getCoord(ptr, tox); ptr = getCoord(ptr, toy); xc = 2 * curx - contrlx; yc = 2 * cury - contrly; index++; d->helper->ensureSpace(vec, index); vec[index].code = ART_CURVETO; vec[index].x1 = (curx + 2 * xc) * (1.0 / 3.0); vec[index].y1 = (cury + 2 * yc) * (1.0 / 3.0); vec[index].x2 = ((curx + tox) + 2 * xc) * (1.0 / 3.0); vec[index].y2 = ((cury + toy) + 2 * yc) * (1.0 / 3.0); vec[index].x3 = curx + tox; vec[index].y3 = cury + toy; curx += tox; cury += toy; contrlx = xc; contrly = yc; lastCommand = 't'; break; case 'T': ptr = getCoord(ptr, tox); ptr = getCoord(ptr, toy); xc = 2 * curx - contrlx; yc = 2 * cury - contrly; index++; d->helper->ensureSpace(vec, index); vec[index].code = ART_CURVETO; vec[index].x1 = (curx + 2 * xc) * (1.0 / 3.0); vec[index].y1 = (cury + 2 * yc) * (1.0 / 3.0); vec[index].x2 = (tox + 2 * xc) * (1.0 / 3.0); vec[index].y2 = (toy + 2 * yc) * (1.0 / 3.0); vec[index].x3 = tox; vec[index].y3 = toy; curx = tox; cury = toy; contrlx = xc; contrly = yc; lastCommand = 'T'; break; case 'z': case 'Z': int find; find = -1; for(int i = index; i >= 0; i--) { if(vec[i].code == ART_MOVETO_OPEN || vec[i].code == ART_MOVETO) { find = i; break; } } if(find != -1) { if(vec[find].x3 != curx || vec[find].y3 != cury) { index++; d->helper->ensureSpace(vec, index); vec[index].code = ART_LINETO; vec[index].x3 = vec[find].x3; vec[index].y3 = vec[find].y3; } } // reset for next (sub)path curx = vec[find].x3; cury = vec[find].y3; lastCommand = 'z'; break; case 'a': ptr = getCoord(ptr, rx); ptr = getCoord(ptr, ry); ptr = getCoord(ptr, angle); ptr = getCoord(ptr, tox); largeArc = tox == 1; ptr = getCoord(ptr, tox); sweep = tox == 1; ptr = getCoord(ptr, tox); ptr = getCoord(ptr, toy); // Spec: radii are nonnegative numbers rx = fabs(rx); ry = fabs(ry); d->helper->calculateArc(true, vec, index, curx, cury, angle, tox, toy, rx, ry, largeArc, sweep); lastCommand = 'a'; break; case 'A': ptr = getCoord(ptr, rx); ptr = getCoord(ptr, ry); ptr = getCoord(ptr, angle); ptr = getCoord(ptr, tox); largeArc = tox == 1; ptr = getCoord(ptr, tox); sweep = tox == 1; ptr = getCoord(ptr, tox); ptr = getCoord(ptr, toy); // Spec: radii are nonnegative numbers rx = fabs(rx); ry = fabs(ry); d->helper->calculateArc(false, vec, index, curx, cury, angle, tox, toy, rx, ry, largeArc, sweep); lastCommand = 'A'; break; } if(*ptr == '+' || *ptr == '-' || *ptr == '.' || (*ptr >= '0' && *ptr <= '9')) { // there are still coords in this command if(command == 'M') { command = 'L'; } else if(command == 'm') { command = 'l'; } } else { command = *(ptr++); } // Detect reflection points if(lastCommand != 'C' && lastCommand != 'c' && lastCommand != 'S' && lastCommand != 's' && lastCommand != 'Q' && lastCommand != 'q' && lastCommand != 'T' && lastCommand != 't') { contrlx = curx; contrly = cury; } } // Find last subpath int find = -1; for(int i = index; i >= 0; i--) { if(vec[i].code == ART_MOVETO_OPEN || vec[i].code == ART_MOVETO) { find = i; break; } } // Fix a problem where the .svg file used doubles as values... (sofico.svg) if(curx != vec[find].x3 && cury != vec[find].y3) { if((int) curx == (int) vec[find].x3 && (int) cury == (int) vec[find].y3) { index++; if(vec.size() == (unsigned int) index) vec.resize(index + 1); vec[index].code = ART_LINETO; vec[index].x3 = vec[find].x3; vec[index].y3 = vec[find].y3; curx = vec[find].x3; cury = vec[find].y3; } } // Handle filled paths that are not closed explicitly if(filled) { if((int) curx != (int) vec[find].x3 || (int) cury != (int) vec[find].y3) { index++; if(vec.size() == (unsigned int) index) vec.resize(index + 1); vec[index].code = (ArtPathcode)ART_END2; vec[index].x3 = vec[find].x3; vec[index].y3 = vec[find].y3; curx = vec[find].x3; cury = vec[find].y3; } } // Close index++; if(vec.size() == (unsigned int) index) vec.resize(index + 1); vec[index].code = ART_END; // There are pure-moveto paths which reference paint servers *bah* // Do NOT render them bool render = false; for(int i = index; i >= 0; i--) { if(vec[i].code != ART_MOVETO_OPEN && vec[i].code != ART_MOVETO && !(vec[i].code >= ART_END)) { render = true; break; } } if(render) d->helper->drawBPath(vec.data()); } } void KSVGIconPainter::drawImage(double x, double y, TQImage &image) { if(image.depth() != 32) image = image.convertDepth(32); double affine[6]; affine[0] = d->helper->m_worldMatrix->m11(); affine[1] = d->helper->m_worldMatrix->m12(); affine[2] = d->helper->m_worldMatrix->m21(); affine[3] = d->helper->m_worldMatrix->m22(); // use the world matrix to convert the coordinates d->helper->m_worldMatrix->map(x, y, &affine[4], &affine[5]); d->helper->art_rgba_rgba_affine(d->helper->m_buffer, 0, 0, d->helper->m_width, d->helper->m_height, d->helper->m_rowstride, image.bits(), image.width(), image.height(), image.width() * 4, affine); } TQColor KSVGIconPainter::parseColor(const TQString ¶m) { if(param.stripWhiteSpace().startsWith("#")) { TQColor color; color.setNamedColor(param.stripWhiteSpace()); return color; } else if(param.stripWhiteSpace().startsWith("rgb(")) { TQString parse = param.stripWhiteSpace(); TQStringList colors = TQStringList::split(',', parse); TQString r = colors[0].right((colors[0].length() - 4)); TQString g = colors[1]; TQString b = colors[2].left((colors[2].length() - 1)); if(r.contains("%")) { r = r.left(r.length() - 1); r = TQString::number(int((double(255 * r.toDouble()) / 100.0))); } if(g.contains("%")) { g = g.left(g.length() - 1); g = TQString::number(int((double(255 * g.toDouble()) / 100.0))); } if(b.contains("%")) { b = b.left(b.length() - 1); b = TQString::number(int((double(255 * b.toDouble()) / 100.0))); } return TQColor(r.toInt(), g.toInt(), b.toInt()); } else { TQString rgbColor = param.stripWhiteSpace(); if(rgbColor == "aliceblue") return TQColor(240, 248, 255); else if(rgbColor == "antiquewhite") return TQColor(250, 235, 215); else if(rgbColor == "aqua") return TQColor(0, 255, 255); else if(rgbColor == "aquamarine") return TQColor(127, 255, 212); else if(rgbColor == "azure") return TQColor(240, 255, 255); else if(rgbColor == "beige") return TQColor(245, 245, 220); else if(rgbColor == "bisque") return TQColor(255, 228, 196); else if(rgbColor == "black") return TQColor(0, 0, 0); else if(rgbColor == "blanchedalmond") return TQColor(255, 235, 205); else if(rgbColor == "blue") return TQColor(0, 0, 255); else if(rgbColor == "blueviolet") return TQColor(138, 43, 226); else if(rgbColor == "brown") return TQColor(165, 42, 42); else if(rgbColor == "burlywood") return TQColor(222, 184, 135); else if(rgbColor == "cadetblue") return TQColor(95, 158, 160); else if(rgbColor == "chartreuse") return TQColor(127, 255, 0); else if(rgbColor == "chocolate") return TQColor(210, 105, 30); else if(rgbColor == "coral") return TQColor(255, 127, 80); else if(rgbColor == "cornflowerblue") return TQColor(100, 149, 237); else if(rgbColor == "cornsilk") return TQColor(255, 248, 220); else if(rgbColor == "crimson") return TQColor(220, 20, 60); else if(rgbColor == "cyan") return TQColor(0, 255, 255); else if(rgbColor == "darkblue") return TQColor(0, 0, 139); else if(rgbColor == "darkcyan") return TQColor(0, 139, 139); else if(rgbColor == "darkgoldenrod") return TQColor(184, 134, 11); else if(rgbColor == "darkgray") return TQColor(169, 169, 169); else if(rgbColor == "darkgrey") return TQColor(169, 169, 169); else if(rgbColor == "darkgreen") return TQColor(0, 100, 0); else if(rgbColor == "darkkhaki") return TQColor(189, 183, 107); else if(rgbColor == "darkmagenta") return TQColor(139, 0, 139); else if(rgbColor == "darkolivegreen") return TQColor(85, 107, 47); else if(rgbColor == "darkorange") return TQColor(255, 140, 0); else if(rgbColor == "darkorchid") return TQColor(153, 50, 204); else if(rgbColor == "darkred") return TQColor(139, 0, 0); else if(rgbColor == "darksalmon") return TQColor(233, 150, 122); else if(rgbColor == "darkseagreen") return TQColor(143, 188, 143); else if(rgbColor == "darkslateblue") return TQColor(72, 61, 139); else if(rgbColor == "darkslategray") return TQColor(47, 79, 79); else if(rgbColor == "darkslategrey") return TQColor(47, 79, 79); else if(rgbColor == "darkturquoise") return TQColor(0, 206, 209); else if(rgbColor == "darkviolet") return TQColor(148, 0, 211); else if(rgbColor == "deeppink") return TQColor(255, 20, 147); else if(rgbColor == "deepskyblue") return TQColor(0, 191, 255); else if(rgbColor == "dimgray") return TQColor(105, 105, 105); else if(rgbColor == "dimgrey") return TQColor(105, 105, 105); else if(rgbColor == "dodgerblue") return TQColor(30, 144, 255); else if(rgbColor == "firebrick") return TQColor(178, 34, 34); else if(rgbColor == "floralwhite") return TQColor(255, 250, 240); else if(rgbColor == "forestgreen") return TQColor(34, 139, 34); else if(rgbColor == "fuchsia") return TQColor(255, 0, 255); else if(rgbColor == "gainsboro") return TQColor(220, 220, 220); else if(rgbColor == "ghostwhite") return TQColor(248, 248, 255); else if(rgbColor == "gold") return TQColor(255, 215, 0); else if(rgbColor == "goldenrod") return TQColor(218, 165, 32); else if(rgbColor == "gray") return TQColor(128, 128, 128); else if(rgbColor == "grey") return TQColor(128, 128, 128); else if(rgbColor == "green") return TQColor(0, 128, 0); else if(rgbColor == "greenyellow") return TQColor(173, 255, 47); else if(rgbColor == "honeydew") return TQColor(240, 255, 240); else if(rgbColor == "hotpink") return TQColor(255, 105, 180); else if(rgbColor == "indianred") return TQColor(205, 92, 92); else if(rgbColor == "indigo") return TQColor(75, 0, 130); else if(rgbColor == "ivory") return TQColor(255, 255, 240); else if(rgbColor == "khaki") return TQColor(240, 230, 140); else if(rgbColor == "lavender") return TQColor(230, 230, 250); else if(rgbColor == "lavenderblush") return TQColor(255, 240, 245); else if(rgbColor == "lawngreen") return TQColor(124, 252, 0); else if(rgbColor == "lemonchiffon") return TQColor(255, 250, 205); else if(rgbColor == "lightblue") return TQColor(173, 216, 230); else if(rgbColor == "lightcoral") return TQColor(240, 128, 128); else if(rgbColor == "lightcyan") return TQColor(224, 255, 255); else if(rgbColor == "lightgoldenrodyellow") return TQColor(250, 250, 210); else if(rgbColor == "lightgray") return TQColor(211, 211, 211); else if(rgbColor == "lightgrey") return TQColor(211, 211, 211); else if(rgbColor == "lightgreen") return TQColor(144, 238, 144); else if(rgbColor == "lightpink") return TQColor(255, 182, 193); else if(rgbColor == "lightsalmon") return TQColor(255, 160, 122); else if(rgbColor == "lightseagreen") return TQColor(32, 178, 170); else if(rgbColor == "lightskyblue") return TQColor(135, 206, 250); else if(rgbColor == "lightslategray") return TQColor(119, 136, 153); else if(rgbColor == "lightslategrey") return TQColor(119, 136, 153); else if(rgbColor == "lightsteelblue") return TQColor(176, 196, 222); else if(rgbColor == "lightyellow") return TQColor(255, 255, 224); else if(rgbColor == "lime") return TQColor(0, 255, 0); else if(rgbColor == "limegreen") return TQColor(50, 205, 50); else if(rgbColor == "linen") return TQColor(250, 240, 230); else if(rgbColor == "magenta") return TQColor(255, 0, 255); else if(rgbColor == "maroon") return TQColor(128, 0, 0); else if(rgbColor == "mediumaquamarine") return TQColor(102, 205, 170); else if(rgbColor == "mediumblue") return TQColor(0, 0, 205); else if(rgbColor == "mediumorchid") return TQColor(186, 85, 211); else if(rgbColor == "mediumpurple") return TQColor(147, 112, 219); else if(rgbColor == "mediumseagreen") return TQColor(60, 179, 113); else if(rgbColor == "mediumslateblue") return TQColor(123, 104, 238); else if(rgbColor == "mediumspringgreen") return TQColor(0, 250, 154); else if(rgbColor == "mediumturquoise") return TQColor(72, 209, 204); else if(rgbColor == "mediumvioletred") return TQColor(199, 21, 133); else if(rgbColor == "midnightblue") return TQColor(25, 25, 112); else if(rgbColor == "mintcream") return TQColor(245, 255, 250); else if(rgbColor == "mistyrose") return TQColor(255, 228, 225); else if(rgbColor == "moccasin") return TQColor(255, 228, 181); else if(rgbColor == "navajowhite") return TQColor(255, 222, 173); else if(rgbColor == "navy") return TQColor(0, 0, 128); else if(rgbColor == "oldlace") return TQColor(253, 245, 230); else if(rgbColor == "olive") return TQColor(128, 128, 0); else if(rgbColor == "olivedrab") return TQColor(107, 142, 35); else if(rgbColor == "orange") return TQColor(255, 165, 0); else if(rgbColor == "orangered") return TQColor(255, 69, 0); else if(rgbColor == "orchid") return TQColor(218, 112, 214); else if(rgbColor == "palegoldenrod") return TQColor(238, 232, 170); else if(rgbColor == "palegreen") return TQColor(152, 251, 152); else if(rgbColor == "paleturquoise") return TQColor(175, 238, 238); else if(rgbColor == "palevioletred") return TQColor(219, 112, 147); else if(rgbColor == "papayawhip") return TQColor(255, 239, 213); else if(rgbColor == "peachpuff") return TQColor(255, 218, 185); else if(rgbColor == "peru") return TQColor(205, 133, 63); else if(rgbColor == "pink") return TQColor(255, 192, 203); else if(rgbColor == "plum") return TQColor(221, 160, 221); else if(rgbColor == "powderblue") return TQColor(176, 224, 230); else if(rgbColor == "purple") return TQColor(128, 0, 128); else if(rgbColor == "red") return TQColor(255, 0, 0); else if(rgbColor == "rosybrown") return TQColor(188, 143, 143); else if(rgbColor == "royalblue") return TQColor(65, 105, 225); else if(rgbColor == "saddlebrown") return TQColor(139, 69, 19); else if(rgbColor == "salmon") return TQColor(250, 128, 114); else if(rgbColor == "sandybrown") return TQColor(244, 164, 96); else if(rgbColor == "seagreen") return TQColor(46, 139, 87); else if(rgbColor == "seashell") return TQColor(255, 245, 238); else if(rgbColor == "sienna") return TQColor(160, 82, 45); else if(rgbColor == "silver") return TQColor(192, 192, 192); else if(rgbColor == "skyblue") return TQColor(135, 206, 235); else if(rgbColor == "slateblue") return TQColor(106, 90, 205); else if(rgbColor == "slategray") return TQColor(112, 128, 144); else if(rgbColor == "slategrey") return TQColor(112, 128, 144); else if(rgbColor == "snow") return TQColor(255, 250, 250); else if(rgbColor == "springgreen") return TQColor(0, 255, 127); else if(rgbColor == "steelblue") return TQColor(70, 130, 180); else if(rgbColor == "tan") return TQColor(210, 180, 140); else if(rgbColor == "teal") return TQColor(0, 128, 128); else if(rgbColor == "thistle") return TQColor(216, 191, 216); else if(rgbColor == "tomato") return TQColor(255, 99, 71); else if(rgbColor == "turquoise") return TQColor(64, 224, 208); else if(rgbColor == "violet") return TQColor(238, 130, 238); else if(rgbColor == "wheat") return TQColor(245, 222, 179); else if(rgbColor == "white") return TQColor(255, 255, 255); else if(rgbColor == "whitesmoke") return TQColor(245, 245, 245); else if(rgbColor == "yellow") return TQColor(255, 255, 0); else if(rgbColor == "yellowgreen") return TQColor(154, 205, 50); } return TQColor(); } double KSVGIconPainter::dpi() { return 90.0; // TODO: make modal? } double KSVGIconPainter::toPixel(const TQString &s, bool hmode) { if(s.isEmpty()) return 0.0; TQString check = s; double ret = 0.0; double value = 0; const char *start = check.latin1(); const char *end = getCoord(start, value); if(uint(end - start) < check.length()) { if(check.endsWith("px")) ret = value; else if(check.endsWith("cm")) ret = (value / 2.54) * dpi(); else if(check.endsWith("pc")) ret = (value / 6.0) * dpi(); else if(check.endsWith("mm")) ret = (value / 25.4) * dpi(); else if(check.endsWith("in")) ret = value * dpi(); else if(check.endsWith("pt")) ret = (value / 72.0) * dpi(); else if(check.endsWith("%")) { ret = value / 100.0; if(hmode) ret *= d->drawWidth; else ret *= d->drawHeight; } else if(check.endsWith("em")) { ret = value * 10.0; // TODO make this depend on actual font size } } else ret = value; return ret; } ArtGradientLinear *KSVGIconPainter::linearGradient(const TQString &id) { return d->helper->m_linearGradientMap[id]; } void KSVGIconPainter::addLinearGradient(const TQString &id, ArtGradientLinear *gradient) { d->helper->m_linearGradientMap.insert(id, gradient); } TQDomElement KSVGIconPainter::linearGradientElement(ArtGradientLinear *linear) { return d->helper->m_linearGradientElementMap[linear]; } void KSVGIconPainter::addLinearGradientElement(ArtGradientLinear *gradient, TQDomElement element) { d->helper->m_linearGradientElementMap.insert(gradient, element); } ArtGradientRadial *KSVGIconPainter::radialGradient(const TQString &id) { return d->helper->m_radialGradientMap[id]; } void KSVGIconPainter::addRadialGradient(const TQString &id, ArtGradientRadial *gradient) { d->helper->m_radialGradientMap.insert(id, gradient); } TQDomElement KSVGIconPainter::radialGradientElement(ArtGradientRadial *radial) { return d->helper->m_radialGradientElementMap[radial]; } void KSVGIconPainter::addRadialGradientElement(ArtGradientRadial *gradient, TQDomElement element) { d->helper->m_radialGradientElementMap.insert(gradient, element); } TQ_UINT32 KSVGIconPainter::toArtColor(const TQColor &color) { return d->helper->toArtColor(color); } TQWMatrix KSVGIconPainter::parseTransform(const TQString &transform) { TQWMatrix result; // Split string for handling 1 transform statement at a time TQStringList subtransforms = TQStringList::split(')', transform); TQStringList::ConstIterator it = subtransforms.begin(); TQStringList::ConstIterator end = subtransforms.end(); for(; it != end; ++it) { TQStringList subtransform = TQStringList::split('(', (*it)); subtransform[0] = subtransform[0].stripWhiteSpace().lower(); subtransform[1] = subtransform[1].simplifyWhiteSpace(); TQRegExp reg("([-]?\\d*\\.?\\d+(?:e[-]?\\d+)?)"); int pos = 0; TQStringList params; while(pos >= 0) { pos = reg.search(subtransform[1], pos); if(pos != -1) { params += reg.cap(1); pos += reg.matchedLength(); } } if(subtransform[0].startsWith(";") || subtransform[0].startsWith(",")) subtransform[0] = subtransform[0].right(subtransform[0].length() - 1); if(subtransform[0] == "rotate") { if(params.count() == 3) { double x = params[1].toDouble(); double y = params[2].toDouble(); result.translate(x, y); result.rotate(params[0].toDouble()); result.translate(-x, -y); } else result.rotate(params[0].toDouble()); } else if(subtransform[0] == "translate") { if(params.count() == 2) result.translate(params[0].toDouble(), params[1].toDouble()); else // Spec : if only one param given, assume 2nd param to be 0 result.translate(params[0].toDouble() , 0); } else if(subtransform[0] == "scale") { if(params.count() == 2) result.scale(params[0].toDouble(), params[1].toDouble()); else // Spec : if only one param given, assume uniform scaling result.scale(params[0].toDouble(), params[0].toDouble()); } else if(subtransform[0] == "skewx") result.shear(tan(params[0].toDouble() * deg2rad), 0.0F); else if(subtransform[0] == "skewy") result.shear(tan(params[0].toDouble() * deg2rad), 0.0F); else if(subtransform[0] == "skewy") result.shear(0.0F, tan(params[0].toDouble() * deg2rad)); else if(subtransform[0] == "matrix") { if(params.count() >= 6) { result.setMatrix(params[0].toDouble(), params[1].toDouble(), params[2].toDouble(), params[3].toDouble(), params[4].toDouble(), params[5].toDouble()); } } } return result; }