void XARGenerator::DetermineLinearGradientPoints(const Gradient* pGradient, const Transformation& trans, const RectD& boundings, DocCoord& p1, DocCoord& p2) { if (pGradient->units == Gradient::ObjectBoundingBox) { PointD pLower = boundings.LowerCoord(); PointD pHigher = boundings.HigherCoord(); p1 = DocCoord((INT32)pLower.x, m_docSize.y - (INT32)pLower.y); p2 = DocCoord((INT32)pHigher.x, m_docSize.y - (INT32)pHigher.y); #ifdef SVGDEBUG svgtrace(DBGTRACE_GRADIENTS, "using ObjectBoundingBox %d,%d %d,%d\n", p1.x, p1.y, p2.x, p2.y); #endif } else { // Gradient->units == Gradient::UserSpaceOnUse double fX1 = pGradient->x1; double fY1 = pGradient->y1; double fX2 = pGradient->x2; double fY2 = pGradient->y2; trans.ApplyToCoordinate(fX1, fY1, &fX1, &fY1); trans.ApplyToCoordinate(fX2, fY2, &fX2, &fY2); p1 = DocCoord((INT32)fX1, m_docSize.y - (INT32)fY1); p2 = DocCoord((INT32)fX2, m_docSize.y - (INT32)fY2); #if SVGDEBUG svgtrace(DBGTRACE_GRADIENTS, "using UserSpaceOnUse %d,%d %d,%d\n", p1.x, p1.y, p2.x, p2.y); #endif } }
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; }