bool XARGenerator::OutputFillRadialGradient(const Style& style, const Transformation& trans, const RectD& boundings) { bool ok = true; CXaraFileRecord Rec(0); const Gradient* pGradient = style.GetFillGradient(); const GradientStopList& stops = pGradient->stops; if (stops.GetCount() < 1) { #if SVGDEBUG svgtrace(DBGTRACE_GRADIENTS, "gradient '%s' has no stop points, giving up\n", (const char *)pGradient->xmlId.mb_str(wxConvUTF8)); #endif return false; } #if SVGDEBUG svgtrace(DBGTRACE_GRADIENTS, "using radial fill gradient '%s' with %d colours\n", (const char *)pGradient->xmlId.mb_str(wxConvUTF8), stops.GetCount()); svgtrace(DBGTRACE_GRADIENTS, "gradient trans: "); DebugDumpTransformation(DBGTRACE_GRADIENTS, trans); #endif // center, major and minor axis points of gradient DocCoord pC, pMaj, pMin; DetermineRadialGradientPoints(pGradient, trans, boundings, pC, pMaj, pMin); if (stops.GetCount() < 3) { // 1 or 2 stops wxColour col1, col2; double fOpacity1, fOpacity2; if (stops.GetCount() == 1) { GradientStop* pStop1 = stops.GetFirst()->GetData(); col2 = col1 = pStop1->stopColour; fOpacity1 = fOpacity2 = pStop1->stopOpacity; } else { // stops.GetCount() == 2 GradientStop* pStop1 = stops.GetFirst()->GetData(); GradientStop* pStop2 = stops.GetLast()->GetData(); col1 = pStop1->stopColour; col2 = pStop2->stopColour; fOpacity1 = pStop1->stopOpacity; fOpacity2 = pStop2->stopOpacity; } #if SVGDEBUG svgtrace(DBGTRACE_GRADIENTS, "stop points: %d, %d and %d, %d\n", pMaj.x, pMaj.y, pMin.x, pMin.y); #endif UINT32 iRecNo1 = DefineColour(col1); UINT32 iRecNo2 = DefineColour(col2); Rec.Reinit(TAG_ELLIPTICALFILL, TAG_ELLIPTICALFILL_SIZE); ok = Rec.WriteCoord(pC); ok = Rec.WriteCoord(pMaj); ok = Rec.WriteCoord(pMin); ok = Rec.WriteReference(iRecNo1); ok = Rec.WriteReference(iRecNo2); ok = Rec.WriteDOUBLE(0.0); // bias ok = Rec.WriteDOUBLE(0.0); // gain ok = m_pExporter->WriteRecord(&Rec); if (fOpacity1 != 1.0 || fOpacity2 != 1.0) { // the fill has also alpha transparency BYTE bOpacity1 = (BYTE)((1.0-fOpacity1)*255.0); BYTE bOpacity2 = (BYTE)((1.0-fOpacity2)*255.0); Rec.Reinit(TAG_ELLIPTICALTRANSPARENTFILL, TAG_ELLIPTICALTRANSPARENTFILL_SIZE); ok = Rec.WriteCoord(pC); ok = Rec.WriteCoord(pMaj); ok = Rec.WriteCoord(pMin); ok = Rec.WriteBYTE(bOpacity1); ok = Rec.WriteBYTE(bOpacity2); ok = Rec.WriteBYTE(0x01); // mix ok = Rec.WriteDOUBLE(0.0); // bias ok = Rec.WriteDOUBLE(0.0); // gain ok = m_pExporter->WriteRecord(&Rec); } } else { // stops.GetCount() > 2 // XXX ? } return true; }
bool XARGenerator::OutputStyles(const Style& style, const Transformation& trans, const RectD& boundings, UINT32 witch) { bool ok = true; CXaraFileRecord Rec(0); wxString sXmlId = style.GetXmlId(); if (!sXmlId.IsEmpty()) { // XXX how to output object labels in XAR? #if SVGDEBUG svgtrace(DBGTRACE_SHAPES, "object id: %s\n", (const char *)sXmlId.mb_str(wxConvUTF8)); #endif } // XXX TODO to avoid XAR redundancy, we should look // if the styles we are outputting are already the default // in Xara's stack if (witch & STYLE_FILL_COLOUR) { if (style.IsFillColourDefined()) { wxColour col = style.GetFillColour(); if (col.Ok()) { UINT32 iRecNo = DefineColour(col); Rec.Reinit(TAG_FLATFILL, TAG_FLATFILL_SIZE); ok = Rec.WriteReference(iRecNo); ok = m_pExporter->WriteRecord(&Rec); #if SVGDEBUG svgtrace(DBGTRACE_STYLES, "fill colour %d,%d,%d\n", col.Red(), col.Green(), col.Blue()); #endif } else { m_pExporter->WriteZeroSizedRecord(TAG_FLATFILL_NONE); #if SVGDEBUG svgtrace(DBGTRACE_STYLES, "no fill colour\n"); #endif } } else if (!style.IsFillGradientDefined()) { m_pExporter->WriteZeroSizedRecord(TAG_FLATFILL_NONE); #if SVGDEBUG svgtrace(DBGTRACE_STYLES, "no fill colour\n"); #endif } } if (witch & STYLE_FILL_GRADIENT && style.IsFillGradientDefined()) { Gradient* pGradient = style.GetFillGradient(); if (pGradient->type == Gradient::Linear) { OutputFillLinearGradient(style, trans, boundings); } else if (pGradient->type == Gradient::Radial) { OutputFillRadialGradient(style, trans, boundings); } } if (witch & STYLE_FILL_OPACITY && style.IsFillOpacityDefined()) { double opacity = style.GetFillOpacity(); if (opacity < 1.0) { BYTE bOpacity = (BYTE)((1.0-opacity)*255.0); Rec.Reinit(TAG_FLATTRANSPARENTFILL, TAG_FLATTRANSPARENTFILL_SIZE); ok = Rec.WriteBYTE(bOpacity); ok = Rec.WriteBYTE(0x01); ok = m_pExporter->WriteRecord(&Rec); } } if (witch & STYLE_STROKE_COLOUR) { if (style.IsStrokeColourDefined()) { wxColour col = style.GetStrokeColour(); if (col.Ok()) { UINT32 iRecNo = DefineColour(col); Rec.Reinit(TAG_LINECOLOUR, TAG_LINECOLOUR_SIZE); ok = Rec.WriteReference(iRecNo); ok = m_pExporter->WriteRecord(&Rec); #if SVGDEBUG svgtrace(DBGTRACE_STYLES, "stroke colour %d,%d,%d\n", col.Red(), col.Green(), col.Blue()); #endif } else { m_pExporter->WriteZeroSizedRecord(TAG_LINECOLOUR_NONE); #if SVGDEBUG svgtrace(DBGTRACE_STYLES, "no stroke colour\n"); #endif } } else { m_pExporter->WriteZeroSizedRecord(TAG_LINECOLOUR_NONE); #if SVGDEBUG svgtrace(DBGTRACE_STYLES, "no stroke colour\n"); #endif } } if (witch & STYLE_STROKE_WIDTH && style.IsStrokeWidthDefined()) { UINT32 iStrokeWidth = style.GetStrokeWidth(); Rec.Reinit(TAG_LINEWIDTH, TAG_LINEWIDTH_SIZE); ok = Rec.WriteINT32(iStrokeWidth); ok = m_pExporter->WriteRecord(&Rec); } if (witch & STYLE_STROKE_LINEJOIN ) { JointType jt; if (style.IsStrokeLineJoinDefined()) { jt=style.GetStrokeLineJoin(); #if SVGDEBUG switch(jt) { case MitreJoin: svgtrace(DBGTRACE_STYLES, "stroke join mitre\n"); break; case BevelledJoin: svgtrace(DBGTRACE_STYLES, "stroke join bevel\n"); break; case RoundJoin: svgtrace(DBGTRACE_STYLES, "stroke join round\n"); break; } #endif } else { jt=MitreJoin; #if SVGDEBUG svgtrace(DBGTRACE_STYLES, "no stroke specified, using mitre\n"); #endif } Rec.Reinit(TAG_JOINSTYLE, TAG_JOINSTYLE_SIZE); ok = Rec.WriteBYTE(BYTE(jt)); ok = m_pExporter->WriteRecord(&Rec); } if (witch & STYLE_STROKE_LINECAP && style.IsStrokeLineCapDefined()) { LineCapType lct=style.GetStrokeLineCap(); Rec.Reinit(TAG_STARTCAP, TAG_STARTCAP_SIZE); ok = Rec.WriteBYTE(BYTE(lct)); ok = m_pExporter->WriteRecord(&Rec); Rec.Reinit(TAG_ENDCAP, TAG_ENDCAP_SIZE); ok = Rec.WriteBYTE(BYTE(lct)); ok = m_pExporter->WriteRecord(&Rec); #if SVGDEBUG switch(lct) { case LineCapButt: svgtrace(DBGTRACE_STYLES, "stroke cap butt\n"); break; case LineCapRound: svgtrace(DBGTRACE_STYLES, "stroke cap round\n"); break; case LineCapSquare: svgtrace(DBGTRACE_STYLES, "stroke cap square\n"); break; } #endif } if (witch & STYLE_STROKE_OPACITY && style.IsStrokeOpacityDefined()) { double opacity = style.GetStrokeOpacity(); if (opacity < 1.0) { BYTE bOpacity = (BYTE)((1.0-opacity)*255.0); Rec.Reinit(TAG_LINETRANSPARENCY, TAG_LINETRANSPARENCY_SIZE); ok = Rec.WriteBYTE(bOpacity); ok = Rec.WriteBYTE(0x01); // mix ok = m_pExporter->WriteRecord(&Rec); } } if (witch & STYLE_OPACITY && style.IsOpacityDefined()) { double fOpacity = style.GetOpacity(); if (fOpacity < 1.0) { double fFillOpacity = fOpacity; double fStrokeOpacity = fOpacity; if (style.IsFillOpacityDefined()) fFillOpacity *= style.GetFillOpacity(); if (style.IsStrokeOpacityDefined()) fStrokeOpacity *= style.GetStrokeOpacity(); BYTE bFillOpacity = (BYTE)((1.0-fFillOpacity)*255.0); BYTE bStrokeOpacity = (BYTE)((1.0-fStrokeOpacity)*255.0); Rec.Reinit(TAG_FLATTRANSPARENTFILL, TAG_FLATTRANSPARENTFILL_SIZE); ok = Rec.WriteBYTE(bFillOpacity); ok = Rec.WriteBYTE(0x01); // mix ok = m_pExporter->WriteRecord(&Rec); Rec.Reinit(TAG_LINETRANSPARENCY, TAG_LINETRANSPARENCY_SIZE); ok = Rec.WriteBYTE(bStrokeOpacity); ok = Rec.WriteBYTE(0x01); // mix ok = m_pExporter->WriteRecord(&Rec); } } return ok; }
bool XARGenerator::OutputFillLinearGradient(const Style& style, const Transformation& trans, const RectD& boundings) { bool ok = true; CXaraFileRecord Rec(0); const Gradient* pGradient = style.GetFillGradient(); const GradientStopList& stops = pGradient->stops; if (stops.GetCount() < 1) { #if SVGDEBUG svgtrace(DBGTRACE_GRADIENTS, "gradient '%s' has no stop points, giving up\n", (const char *)pGradient->xmlId.mb_str(wxConvUTF8)); #endif return false; } #if SVGDEBUG svgtrace(DBGTRACE_GRADIENTS, "using linear fill gradient '%s' with %d colours\n", (const char *)pGradient->xmlId.mb_str(wxConvUTF8), stops.GetCount()); svgtrace(DBGTRACE_GRADIENTS, "gradient trans: "); DebugDumpTransformation(DBGTRACE_GRADIENTS, trans); #endif // start and end points of gradient DocCoord p1, p2; DetermineLinearGradientPoints(pGradient, trans, boundings, p1, p2); if (pGradient->units == Gradient::ObjectBoundingBox) { // first and last offsets of gradient double fOffsetFirst, fOffsetLast; if (stops.GetCount() == 1) { fOffsetFirst = 0.0; fOffsetLast = 1.0; } else { // stops.GetCount() > 1 GradientStop* pStop1 = stops.GetFirst()->GetData(); GradientStop* pStop2 = stops.GetLast()->GetData(); fOffsetFirst = pStop1->offset; fOffsetLast = pStop2->offset; } #if 0 // determine vector of direction of object gradient double fDX=1.0, fDY=0.0; trans.ApplyToCoordinate(fDX, fDY, &fDX, &fDY); double fMod = sqrt(fDX*fDX+fDY*fDY); fDX /= fMod; fDY /= fMod; #if SVGDEBUG svgtrace(DBGTRACE_GRADIENTS, "direction of gradient: %.2f %.2f\n", fDX, fDY); #endif #endif PointD pLower = boundings.LowerCoord(); PointD pHigher = boundings.HigherCoord(); double fWidth = fabs(pHigher.x - pLower.x); //double fHeight = fabs(pHigher.x - pLower.x); p1.x += (INT32)(fWidth * fOffsetFirst); //p1.y += (INT32)(fHeight * fOffsetFirst); p2.x -= (INT32)(fWidth * (1.0 - fOffsetLast)); //p2.y -= (INT32)(fHeight * (1.0 - fOffsetLast)); #ifdef SVGDEBUG svgtrace(DBGTRACE_GRADIENTS, "new ObjectBoundingBox %d,%d %d,%d\n", p1.x, p1.y, p2.x, p2.y); #endif } if (stops.GetCount() < 3) { // 1 or 2 stops wxColour col1, col2; double fOpacity1, fOpacity2; if (stops.GetCount() == 1) { GradientStop* pStop1 = stops.GetFirst()->GetData(); col2 = col1 = pStop1->stopColour; fOpacity1 = fOpacity2 = pStop1->stopOpacity; } else { // stops.GetCount() == 2 GradientStop* pStop1 = stops.GetFirst()->GetData(); GradientStop* pStop2 = stops.GetLast()->GetData(); col1 = pStop1->stopColour; col2 = pStop2->stopColour; fOpacity1 = pStop1->stopOpacity; fOpacity2 = pStop2->stopOpacity; } #if SVGDEBUG svgtrace(DBGTRACE_GRADIENTS, "stop points: %d, %d and %d, %d\n", p1.x, p1.y, p2.x, p2.y); #endif UINT32 iRecNo1 = DefineColour(col1); UINT32 iRecNo2 = DefineColour(col2); Rec.Reinit(TAG_LINEARFILL, TAG_LINEARFILL_SIZE); ok = Rec.WriteCoord(p1); ok = Rec.WriteCoord(p2); ok = Rec.WriteReference(iRecNo1); ok = Rec.WriteReference(iRecNo2); ok = Rec.WriteDOUBLE(0.0); // bias ok = Rec.WriteDOUBLE(0.0); // gain ok = m_pExporter->WriteRecord(&Rec); if (fOpacity1 != 1.0 || fOpacity2 != 1.0) { // the fill has also alpha transparency BYTE bOpacity1 = (BYTE)((1.0-fOpacity1)*255.0); BYTE bOpacity2 = (BYTE)((1.0-fOpacity2)*255.0); Rec.Reinit(TAG_LINEARTRANSPARENTFILL, TAG_LINEARTRANSPARENTFILL_SIZE); ok = Rec.WriteCoord(p1); ok = Rec.WriteCoord(p2); ok = Rec.WriteBYTE(bOpacity1); ok = Rec.WriteBYTE(bOpacity2); ok = Rec.WriteBYTE(0x01); // mix ok = Rec.WriteDOUBLE(0.0); // bias ok = Rec.WriteDOUBLE(0.0); // gain ok = m_pExporter->WriteRecord(&Rec); } } else { // stops.GetCount() > 2 // we have more than 2 colours size_t size = stops.GetCount(); double* fvOffsets = new double[size]; UINT32* ivRecNo = new UINT32[size]; double* fvOpacity = new double[size]; bool bHaveTransparency = false; for (unsigned int i = 0; i < size; ++i) { GradientStop* pStop = stops.Item(i)->GetData(); ivRecNo[i] = DefineColour(pStop->stopColour); fvOffsets[i] = pStop->offset; fvOpacity[i] = pStop->stopOpacity; if (pStop->stopOpacity < 1.0) bHaveTransparency = true; } Rec.Reinit(TAG_LINEARFILLMULTISTAGE, TAG_LINEARFILLMULTISTAGE_SIZE); ok = Rec.WriteCoord(p1); ok = Rec.WriteCoord(p2); ok = Rec.WriteReference(ivRecNo[0]); ok = Rec.WriteReference(ivRecNo[size-1]); ok = Rec.WriteINT32((INT32)size-2); for (unsigned int i = 1; i < size - 1; ++i) { ok = Rec.WriteDOUBLE(fvOffsets[i]); ok = Rec.WriteReference(ivRecNo[i]); } ok = m_pExporter->WriteRecord(&Rec); if (bHaveTransparency) { if (size == 3) { DocCoord pM; // calculate middle point position pM.x = p1.x + (INT32)((p2.x-p1.x)*fvOffsets[1]); pM.y = p1.y + (INT32)((p2.y-p1.y)*fvOffsets[1]); // hack: avoid aligned points, otherwise the fill will not work if (pM.y == p1.y) { pM.y -= 10; } BYTE bOpacity1 = (BYTE)((1.0-fvOpacity[0])*255.0); BYTE bOpacityM = (BYTE)((1.0-fvOpacity[1])*255.0); BYTE bOpacity2 = (BYTE)((1.0-fvOpacity[2])*255.0); Rec.Reinit(TAG_THREECOLTRANSPARENTFILL, TAG_THREECOLTRANSPARENTFILL_SIZE); ok = Rec.WriteCoord(p1); ok = Rec.WriteCoord(pM); ok = Rec.WriteCoord(p2); ok = Rec.WriteBYTE(bOpacity1); ok = Rec.WriteBYTE(bOpacityM); ok = Rec.WriteBYTE(bOpacity2); ok = Rec.WriteBYTE(0x01); // mix ok = m_pExporter->WriteRecord(&Rec); } else { // size > 3 // XXX the maximum number of transparent positions in XAR files is 4 DocCoord pM1, pM2; // calculate middle point position pM1.x = p1.x + (INT32)((p2.x-p1.x)*fvOffsets[1]); // XXX should get the best offsets pM1.y = p1.y + (INT32)((p2.y-p1.y)*fvOffsets[1]); // not simply the first two pM2.x = p1.x + (INT32)((p2.x-p1.x)*fvOffsets[2]); pM2.y = p1.y + (INT32)((p2.y-p1.y)*fvOffsets[2]); // hack: avoid aligned points, otherwise the fill will not work if (pM1.y == p1.y) { pM1.y -= 10; } if (pM2.y == p1.y) { pM2.y -= 20; } BYTE bOpacity1 = (BYTE)((1.0-fvOpacity[0])*255.0); BYTE bOpacityM1 = (BYTE)((1.0-fvOpacity[1])*255.0); BYTE bOpacityM2 = (BYTE)((1.0-fvOpacity[2])*255.0); BYTE bOpacity2 = (BYTE)((1.0-fvOpacity[size-1])*255.0); Rec.Reinit(TAG_FOURCOLTRANSPARENTFILL, TAG_FOURCOLTRANSPARENTFILL_SIZE); ok = Rec.WriteCoord(p1); ok = Rec.WriteCoord(pM1); ok = Rec.WriteCoord(pM2); //ok = Rec.WriteCoord(p2); // XXX this is wrong ok = Rec.WriteBYTE(bOpacity1); ok = Rec.WriteBYTE(bOpacityM1); ok = Rec.WriteBYTE(bOpacityM2); ok = Rec.WriteBYTE(bOpacity2); ok = Rec.WriteBYTE(0x01); // mix ok = m_pExporter->WriteRecord(&Rec); } } delete[] fvOffsets; delete[] ivRecNo; delete[] fvOpacity; } return ok; }