BOOL RenderStack::Copy(const RenderStack *Other, RenderRegion *pRegion) { ERROR3IF(Other == NULL || pRegion == NULL, "Illegal NULL params"); // WEBSTER - markn 15/8/97 // This has bee taken out of Webster builds as it can cause fatal access violations with Paper render regions. // It is new path processor code that not's totally bug-free, and as path processors are not used in Webster, // it can be safely taken out. #ifndef WEBSTER // Make sure all attributes are properly popped && deinitialised, so that any used // PathProcessors are correctly removed, etc // (Note: This doesn't deinit the DEFAULT attributes, which we hope will never use // PathProcessors - this should be OK, because the places where this is called from // (at present) always make sure all PathProcessors are properly cleaned up too) CleanUpBeforeDestruct(pRegion); #endif // WEBSTER // Make sure we have an empty stack to work with if (TheStack!=NULL) { // Empty the current stack before copying the new one onto it // For every item in the stack, check to see if it is 'temporary'. If it is, // we can't use it in our stack - we must take a copy. for (UINT32 i=0; i<Top; i++) { // Get rid of all the temp attrs in the stack if (TheStack[i].Temporary) { // Delete this tempory attr. CC_ASSERT_VALID(TheStack[i].pAttrValue); delete TheStack[i].pAttrValue; } } // then get rid of the stack itself CCFree(TheStack); TheStack=NULL; ContextLevel = 0; StackLimit = 0; Top = 0; } // Sanity check ENSURE(TheStack == NULL,"RenderStack::Copy called on a non-empty stack"); #if FALSE // Jason (7/3/97) // Copying the stack like this is rampant. We must render all the attributes across onto // the new stack, so that they are properly stacked and initialised /* // Copy stack variables ContextLevel = Other->ContextLevel; StackLimit = Other->StackLimit; Top = Other->Top; // The other stack may be empty, which will often be the case when merging as it may // not have started rendering yet. if (Other->TheStack == NULL) { // Just to be on the safe side TheStack = NULL; // We're done. return TRUE; } // Allocate the same amount of memory for the stack TheStack = (AttributeRec *) CCMalloc(StackLimit * ITEM_SIZE); if (TheStack == NULL) return FALSE; // Copy the other render region's stack data into this new stack memcpy(TheStack, Other->TheStack, StackLimit * ITEM_SIZE); // For every item in the stack, check to see if it is 'temporary'. If it is, // we can't use it in our stack - we must take a copy. for (UINT32 i = 0; i < Top; i++) { if (TheStack[i].Temporary) { // Get the runtime class info on this object CCRuntimeClass *pCCRuntimeClass = TheStack[i].pAttrValue->GetRuntimeClass(); // Create another object of the same type AttributeValue *pNewAttr = (AttributeValue *) pCCRuntimeClass->CreateObject(); if (pNewAttr == NULL) { // Failed to create object - quit with error, but first ensure that this // stack is limited to the items copied so far. Otherwise the destructor // will attempt to delete objects that belong to the other stack. Top = i; return FALSE; } // Object created ok - get the object to copy its contents across. pNewAttr->SimpleCopy(TheStack[i].pAttrValue); // Put it in the stack TheStack[i].pAttrValue = pNewAttr; } } */ #else if (Other->TheStack == NULL) return(TRUE); UINT32 LastContextLevel = 0; // Copy all attributes across to the new stack by rendering them into the provided render region. // If we aren't "pRegion->TheStack" then we've just lost our paddle, and we're being sucked up the creek... for (UINT32 i = 0; i < Other->Top; i++) { // Make sure we copy context level information across too - if the context level changed, we must // save the context level now. if (LastContextLevel != Other->TheStack[i].ContextLevel) pRegion->SaveContext(); // Get each attribute to render into our render region AttributeValue *pAttrVal = Other->TheStack[i].pAttrValue; // If it's temporary, then replace it with a unique copy for this render region if (Other->TheStack[i].Temporary) { // Get the runtime class info on this object CCRuntimeClass *pCCRuntimeClass = Other->TheStack[i].pAttrValue->GetRuntimeClass(); // Create another object of the same type pAttrVal = (AttributeValue *) pCCRuntimeClass->CreateObject(); if (pAttrVal == NULL) return FALSE; // Object created ok - get the object to copy its contents across. pAttrVal->SimpleCopy(Other->TheStack[i].pAttrValue); } // and render the attribute into this render region pAttrVal->Render(pRegion, Other->TheStack[i].Temporary); } #endif // It worked return TRUE; }
void PathProcessorStrokeVector::ProcessPath(Path *pPath, RenderRegion *pRender, PathShape ShapePath) { PORTNOTETRACE("other","PathProcessorStrokeVector::ProcessPath - do nothing"); #ifndef EXCLUDE_FROM_XARALX ERROR3IF(pPath == NULL || pRender == NULL, "Illegal NULL Params"); // Get the RenderRegion SubRenderContext and see if we're returning part-way through a background render VectorStrokeSubRenderContext *pSubRenderContext = (VectorStrokeSubRenderContext *)pRender->GetSubRenderState(); if (pSubRenderContext != NULL && !IS_A(pSubRenderContext, VectorStrokeSubRenderContext)) { // We can't use the sub render context, because it's not ours! pSubRenderContext = NULL; } // --- If we don't have a valid stroke definition, then get the base class to render // the stroke as a simple flat-filled stroke. StrokeDefinition *pStrokeDef = StrokeComponent::FindStroke(StrokeID); if (pStrokeDef == NULL) { PathProcessorStroke::ProcessPath(pPath, pRender); return; } // --- See if we have to create a new SubRenderContext, or if we can use the one passed in. // We always store all relevant variables in a SubRenderContext object so that we can easily // return control to the RenderRegion without having to copy lots of local values in/out. const BOOL CreateSubRenderContext = (pSubRenderContext == NULL); if (CreateSubRenderContext) { pSubRenderContext = new VectorStrokeSubRenderContext; if (pSubRenderContext == NULL) { pRender->DrawPath(pPath, this, ShapePath); return; } // --- If the provided path is not stroked, then we'll just pass it straight through // We also don't touch it if we're doing EOR rendering, or click hit detection if (!pPath->IsStroked || pRender->DrawingMode != DM_COPYPEN || pRender->IsHitDetect()) { delete pSubRenderContext; pRender->DrawPath(pPath, this, ShapePath); return; } // --- If the quality is set low, or if the current stroke attribute is not our "parent" // attribute (so we're not the "current" stroker) then strokes are just rendered as centrelines // BLOCK { QualityAttribute *pQuality = (QualityAttribute *) pRender->GetCurrentAttribute(ATTR_QUALITY); StrokeTypeAttrValue *pTypeAttr = (StrokeTypeAttrValue *) pRender->GetCurrentAttribute(ATTR_STROKETYPE); if ((pQuality != NULL && pQuality->QualityValue.GetLineQuality() != Quality::FullLine) || (pTypeAttr != NULL && pTypeAttr != GetParentAttr())) { delete pSubRenderContext; pRender->DrawPath(pPath, this, ShapePath); return; } } // --- We don't expect the input path to be stroked AND filled on entry ERROR3IF(pPath->IsFilled, "PathProcessor expected RenderRegion to handle IsFilled case"); // --- Get the current line width & Join Style from the render region // BLOCK { LineWidthAttribute *pWidthAttr = (LineWidthAttribute *) pRender->GetCurrentAttribute(ATTR_LINEWIDTH); if (pWidthAttr != NULL) pSubRenderContext->LineWidth = pWidthAttr->LineWidth; JoinTypeAttribute *pJoinAttr = (JoinTypeAttribute *) pRender->GetCurrentAttribute(ATTR_JOINTYPE); if (pJoinAttr != NULL) pSubRenderContext->JoinStyle = pJoinAttr->JoinType; } } // --- Create a new path to be rendered in place of the provided path // Note that I use a large allocation size so that reallocation need not be done // frequently, which also helps reduce memory fragmentation. Path *pOutput = new Path; if (pOutput == NULL) { if (!pRender->IsSubRenderStateLocked()) pRender->SetSubRenderState(NULL); delete pSubRenderContext; pRender->DrawPath(pPath, this, ShapePath); return; } pOutput->Initialise(128, 128); // --- Find our Variable Width function if (CreateSubRenderContext) { // --- Get the variable line width descriptor from the render region VariableWidthAttrValue *pVarWidthAttr = (VariableWidthAttrValue *) pRender->GetCurrentAttribute(ATTR_VARWIDTH); if (pVarWidthAttr != NULL) pSubRenderContext->pValFunc = pVarWidthAttr->GetWidthFunction(); } ValueFunction *pValFunc = pSubRenderContext->pValFunc; // If we couldn't find a proper value function, then we'll default to constant-width. // We keep a static Constant function around always, because it's thread-safe and because // that saves us the overhead of creating and deleting it on the stack each time we're called static ValueFunctionConstant Constant(1.0); if (pValFunc == NULL) pValFunc = &Constant; // --- Find our brush clipart tree Node *pBrush = pStrokeDef->GetStrokeTree(); ERROR3IF(!IS_A(pBrush, Spread), "Brush does not start with a Spread Node!"); DocRect BrushBounds = ((Spread *)pBrush)->GetBoundingRect(); // Get a PathStroker to map paths onto the destination stroke with PathStrokerVector Stroker(pValFunc, pSubRenderContext->LineWidth, LineCapButt, &BrushBounds); // Work out if this is a repeating stroke, and if so, how often it repeats if (CreateSubRenderContext) { if (pStrokeDef->IsRepeating()) { // The repeat distance is calculated as a number of millipoints per repeat of the // stroke, such that it retains the correct aspect ratio. That is, the ratio of // brush width to height is the same as RepeatDist to LineWidth pSubRenderContext->RepeatDist = (INT32) (Stroker.GetScaleFactor() * (double)BrushBounds.Width()); if (pSubRenderContext->RepeatDist < 1000) // I absolutely refuse to repeat it more than pSubRenderContext->RepeatDist = 1000; // once every 1pt, as this is plenty small enough // Suss the path length out ProcessLength GenerateLength(100); double Length = 0; BOOL ok = GenerateLength.PathLength(pPath, &Length); // Ask the stroke def for its number of brush repeats - 0 means work it out INT32 NumberOfRepeats = pStrokeDef->NumRepeats(); if(NumberOfRepeats == 0 && pSubRenderContext->RepeatDist > 0) { // Work out the optimal number of repeats along the path NumberOfRepeats = (INT32)(floor((Length/pSubRenderContext->RepeatDist) + 0.5)); } // Don't got dividing by zero now... if(NumberOfRepeats <= 0) NumberOfRepeats = 1; // Alter the repeat distance to accomodate this number of repeats pSubRenderContext->RepeatDist = (INT32)(Length / (double)NumberOfRepeats); } // Generate the set of trapezoids for the dest path ProcessPathToTrapezoids GenerateTraps(100); pSubRenderContext->pOutputTraps = new TrapsList(pSubRenderContext->RepeatDist); BOOL Failed = (pSubRenderContext->pOutputTraps == NULL); if (!Failed && !GenerateTraps.Init(pPath, pSubRenderContext->pOutputTraps)) Failed = TRUE; ProcessFlags PFlags(TRUE, FALSE, FALSE); // Flags are: Flatten, !QuantiseLines, !QuantiseAll if (!Failed && !GenerateTraps.Process(PFlags, TrapTravel_Parametric, pSubRenderContext->JoinStyle)) Failed = TRUE; if (Failed) { pRender->DrawPath(pPath, this, ShapePath); if (!pRender->IsSubRenderStateLocked()) pRender->SetSubRenderState(NULL); delete pSubRenderContext; return; } } ERROR3IF(pSubRenderContext->pOutputTraps == NULL || pValFunc == NULL, "Vector stroke SubRenderContext was not properly uninitialised!"); // --- Handle background rendering. We always store all critical information in a SubRenderContext. // If we have to break into rendering because of background rendering, we give the context to the RndRgn // to keep for next time. However, when we finish rendering everything, we need to clean up - we will // assume that we'll make it to the end, and chnage this variable if we get interrupted. BOOL DeleteSubRenderContext = TRUE; // Lock the sub-render context so that nobody "inside" the brush uses it const BOOL SRContextLocked = pRender->IsSubRenderStateLocked(); if (!SRContextLocked) pRender->LockSubRenderState(TRUE); // --- Now "render" the brush clipart tree via our Stroker for ( ; pSubRenderContext->Index < pSubRenderContext->pOutputTraps->GetNumTraps() && DeleteSubRenderContext; pSubRenderContext->Index++) { // Find the trapezoid edge list for this pass TrapEdgeList *pEdgeList = pSubRenderContext->pOutputTraps->GetTrapEdgeList(pSubRenderContext->Index); if (pEdgeList->GetNumEdges() < 2) { ERROR3("No map traps when stroking! Subpath stripped\n"); continue; } // And render away pRender->SaveContext(); Node* pNode = pBrush->FindFirstForUnclippedInkRender(pRender); while (pNode) { // Prepare the stroker to stroke this sub-stroke Stroker.PrepareToStroke(pEdgeList); if (pNode->IsAnAttribute()) { // If we are overriding the fill/transparency with the one applied to the stroke, // then we simply throw away all fill/transparency attributes as we render // (We do this on the fly rather than as we make the brush so that the user can // toggle this mode on and off at any time if they change their mind) BOOL RenderIt = TRUE; if ( (pStrokeDef->OverrideFill() && ((NodeAttribute *)pNode)->IsAColourFill()) || (pStrokeDef->OverrideFill() && ((NodeAttribute *)pNode)->IsAStrokeColour()) || (pStrokeDef->OverrideTrans() && ((NodeAttribute *)pNode)->IsAStrokeTransp()) || (pStrokeDef->OverrideTrans() && ((NodeAttribute *)pNode)->IsATranspFill()) ) { RenderIt = FALSE; } if (RenderIt) { // We must modify all attributes to lie in the destination stroke. // This includes fill/trans geometry endpoints, line widths, and // also possibly modifying transparency levels to allow a flat transparency // to be applied to the whole stroke. AttributeValue *pAttrValue = ((NodeAttribute *)pNode)->GetAttributeValue(); AttributeValue *pNewValue = pAttrValue->MouldIntoStroke(&Stroker, 1.0); //****!!!!TODO - Just above, we have the chance to handle transparency better // - we could at least scale all transparencies by passing a flat scale factor into // the MouldIntoStroke call. if (pNewValue != NULL) pNewValue->Render(pRender, TRUE); // The RndRgn will delete this when it's done with else pNode->Render(pRender); // No change, so render the original attribute } } else if (pNode->IsNodePath()) { // Stroke the trapezoids into the output path pOutput->ClearPath(); if (!Stroker.StrokePath(&((NodePath *)pNode)->InkPath, pOutput)) break; pRender->SetWindingRule(NonZeroWinding); pRender->DrawPath(pOutput, this, ShapePath); } // else // TRACEUSER( "Jason", _T("\nBrush node %s not rendered\n"), pNode->GetRuntimeClass()->m_lpszClassName); pNode = pNode->FindNextForUnclippedInkRender(pRender); } pRender->RestoreContext(); // --- Now check if we should break into rendering for background rendering purposes // If the Sub-render-context is locked, then we're inside a blend or something, and it's too dangerous // for us to store our sub-render state, so we have no choice but to render until we finish. // BLOCK if (!SRContextLocked && IS_A(pRender, GRenderDIB)) { View *pView = pRender->GetRenderView(); if (pView != NULL && !pRender->RenderTreeCanContinue()) { // We have been interrupted by the background redraw system. // We will immediately exit, storing our SubRenderContext away into the // RenderRegion for the next time it calls us (see below). DeleteSubRenderContext = FALSE; // Drop through and let the loop condition handle exit } } } // If we locked the sub-render context, then we must restore it if (!SRContextLocked) pRender->LockSubRenderState(FALSE); // If we have finished rendering everything, then we vape our sub render context. // (We even check if we were interrupted just as we finished the final iteration) if (DeleteSubRenderContext || pSubRenderContext->Index >= pSubRenderContext->pOutputTraps->GetNumTraps()) { if (!SRContextLocked) pRender->SetSubRenderState(NULL); delete pSubRenderContext; pSubRenderContext = NULL; } else { if (!SRContextLocked) pRender->SetSubRenderState(pSubRenderContext); } delete pOutput; pOutput = NULL; #endif }