Exemplo n.º 1
0
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;
}
Exemplo n.º 2
0
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
}