/////////////////////////////////////////////////////////////////////////////// // Copyright (C) 2002-2025, Open Design Alliance (the "Alliance"). // All rights reserved. // // This software and its documentation and related materials are owned by // the Alliance. The software may only be incorporated into application // programs owned by members of the Alliance, subject to a signed // Membership Agreement and Supplemental Software License Agreement with the // Alliance. The structure and organization of this software are the valuable // trade secrets of the Alliance and its suppliers. The software is also // protected by copyright law and international treaty provisions. Application // programs incorporating this software must include the following statement // with their copyright notices: // // This application incorporates Open Design Alliance software pursuant to a license // agreement with Open Design Alliance. // Open Design Alliance Copyright (C) 2002-2025 by Open Design Alliance. // All rights reserved. // // By use of this software, its documentation or related materials, you // acknowledge and accept the above terms. /////////////////////////////////////////////////////////////////////////////// #include "OdaCommon.h" #include "GsSrVectorizeView.h" #include "GsSrVectorizeDevice.h" #include "GsSrViewHelper.h" #include "GsNrcGpcConverter.cpp" //external file from ExRender #include "SrConveyorPipelineManager.h" #include "data/metafile/SrMetafile.h" #include "data/metafile/records/SrTTFTextRecords.h" #include "rendering/SrFrameContext.h" #include #include OdGsSrVectorizeView::OdGsSrVectorizeView() { m_pGeometry = new OdGiSrGeometry; m_pipelineManager = new OdSrConveyorPipelineManager(); m_pContext = new OdSrFrameContext(); } OdGsSrVectorizeView::~OdGsSrVectorizeView() { delete m_pContext; delete m_pipelineManager; delete m_pGeometry; } void OdGsSrVectorizeView::setEdgeRects(const OdGsDCRectArray& rects) { m_edgeRects.clear(); m_edgeRects.assign(rects.begin(), rects.end()); } OdGsDCRectArray& OdGsSrVectorizeView::edgeRects() { return m_edgeRects; } void OdGsSrVectorizeView::rasterImageDc(const OdGePoint3d& origin, const OdGeVector3d& u, const OdGeVector3d& v, const OdGiRasterImage* pImg, // image object const OdGePoint2d* uvBoundary, // may not be null OdUInt32 numBoundPts, bool transparency, double brightness, double contrast, double fade) { //m_modelToOutput = m_pModelToEyeProc->eyeToOutputTransform() * m_pModelToEyeProc->modelToEyeTransform(); m_pGeometry->setModelToEye(&m_modelToOutput); OdGiBaseVectorizer::rasterImageDc(origin, u, v, pImg, uvBoundary, numBoundPts, transparency, brightness, contrast, fade); } bool OdGsSrVectorizeView::isFadingEnabledAndChanged() const { bool enabled = isFaded(); if (m_fadingEnabled != enabled) { m_fadingEnabled = enabled; m_fadingValue = fadingIntensity(); } else if (enabled) { OdUInt32 currentFading = fadingIntensity(); if (m_fadingValue != currentFading) return false; // intensity unchanged m_fadingValue = currentFading; } else return false; // both false, do nothing return true; } void OdGsSrVectorizeView::getFadeParams(ODCOLORREF& fadeColor, OdUInt8& fadingValue) const { fadingValue = (OdUInt8)(255 - (int)(2.55 * fadingIntensity())); fadeColor = const_cast(this)->device()->getPaletteBackground(); } void OdGsSrVectorizeView::updateScreen() { m_pContext->setCurrentView(this); OdGsSrBaseView::updateScreen(); } #ifdef OD_SFTRND_OLE #include "ExOwnerDrawDc.cpp" #endif void OdGsSrVectorizeView::ownerDrawDc(const OdGePoint3d& origin, const OdGeVector3d& u, const OdGeVector3d& v, const OdGiSelfGdiDrawable* pDrawable, bool bDcAligned, bool bAllowClipping) { #ifndef OD_SFTRND_OLE OdGsSrBaseView::ownerDrawDc(origin, u, v, pDrawable, bDcAligned, bAllowClipping); #else ::exOwnerDrawDc(origin, u, v, pDrawable, (getRenderMode() == OdGsView::k2DOptimized) && bDcAligned, bAllowClipping, *this, GETBIT(m_flags, kModelCache), GETBIT(m_flags, kModelCache)); #endif } void OdGsSrVectorizeView::text(const OdGePoint3d& position, const OdGeVector3d& normal, const OdGeVector3d& direction, const OdChar* msg, OdInt32 length, bool raw, const OdGiTextStyle* pStyle) { if (pStyle->isShxFont() || view().mode() != OdGsView::k2DOptimized || giContext().quickTextMode() || !OdZero(subEntityTraits().thickness()) || device()->m_isBitmap) { OdGsBaseVectorizer::text(position, normal, direction, msg, length, raw, pStyle); return; } OdSrTTFTextBegin* pEndTTFMarker = new OdSrTTFTextBegin(); OdGePoint3d extMin, extMax; OdGeVector3d u, v; OdGiTextStyle prepStyle; ::odgiCalculateTextBasis(u, v, normal, direction, pStyle->textSize(), pStyle->xScale(), pStyle->obliquingAngle(), pStyle->isBackward(), pStyle->isUpsideDown()); ::odgiPrepareTextStyle(pStyle, prepStyle); OdGiExtAccum::textExtents(OdGiBaseVectorizer::drawContext(), prepStyle, msg, (int)length, raw ? kOdGiRawText : 0, extMin, extMax); OdGeVector3d uVector = u * (extMax.x - extMin.x); OdGeVector3d vVector = v * (extMax.y - extMin.y); OdGePoint3d minPos = position + (u * extMin.x + v * extMin.y); pEndTTFMarker->m_textHeightVector = (!pStyle->isVertical()) ? vVector : uVector; pEndTTFMarker->m_textHeightVector.transformBy(m_pModelToEyeProc->modelToWorldTransform()); m_pGeometry->addRecord(pEndTTFMarker); m_pGeometry->addRecord(new OdSrTTFTextJumper()); // Add first jumper // rectangle to replace small texts OdGePoint3d poly[4] = { minPos, minPos + vVector, minPos + vVector + uVector, minPos + uVector }; OdGiFillType fillMode = subEntityTraits().fillType(); if (fillMode != kOdGiFillAlways) { subEntityTraits().setFillType(kOdGiFillAlways); onTraitsModified(); } OdGsBaseVectorizer::polygon(4, poly); if (fillMode != kOdGiFillAlways) { subEntityTraits().setFillType(fillMode); onTraitsModified(); } pEndTTFMarker->m_pJumper = new OdSrTTFTextJumper(); // Second jumper after the polygon m_pGeometry->addRecord(pEndTTFMarker->m_pJumper); OdGsBaseVectorizer::text(position, normal, direction, msg, length, raw, pStyle); // Search last known record to skip text rendering up to it OdSrRecord* pSearchEnd = pEndTTFMarker; while (pSearchEnd->m_pNext) pSearchEnd = pSearchEnd->m_pNext; pEndTTFMarker->m_pMarker = pSearchEnd; } void OdGsSrVectorizeView::getViewportClipRect(OdGsDCRect& pRect) { OdGePoint2d lowerLeft, upperRight; OdGsDCRect vRect2; pRect = device()->outputRect(); getViewport(lowerLeft, upperRight); getViewport(vRect2); long nWidth = Od_abs(pRect.m_max.x - pRect.m_min.x); long nHeight = Od_abs(pRect.m_min.y - pRect.m_max.y); bool bNegatedY = pRect.m_max.y < pRect.m_min.y; pRect.m_min.x = OdRoundToLong(lowerLeft.x * nWidth); if (bNegatedY) pRect.m_min.y = nHeight - OdRoundToLong(lowerLeft.y * nHeight); else pRect.m_min.y = OdRoundToLong(lowerLeft.y * nHeight); pRect.m_max.x = OdRoundToLong(upperRight.x * nWidth); if (bNegatedY) pRect.m_max.y = nHeight - OdRoundToLong(upperRight.y * nHeight); else pRect.m_max.y = OdRoundToLong(upperRight.y * nHeight); if (pRect.m_min.x > pRect.m_max.x) std::swap(pRect.m_min.x, pRect.m_max.x); if (pRect.m_min.y > pRect.m_max.y) std::swap(pRect.m_min.y, pRect.m_max.y); if (vRect2.m_min.x > vRect2.m_max.x) std::swap(vRect2.m_min.x, vRect2.m_max.x); if (vRect2.m_min.y > vRect2.m_max.y) std::swap(vRect2.m_min.y, vRect2.m_max.y); pRect.intersectWith(vRect2, false); std::swap(pRect.m_min.y, pRect.m_max.y); } void OdGsSrVectorizeView::loadViewport() { m_pContext->setClipRegion(device()->outputRect()); if (device()->numViews() > 1) { OdGsDCRect rcRect; getViewportClipRect(rcRect); m_pContext->setClipRegion(rcRect); // initialize NRC viewport clipping if (isNonRectClipped()) { rcRect = m_pContext->clipRegion(); long intOffsetX = rcRect.m_min.x, intOffsetY = rcRect.m_min.y; OdUInt32 intWidth = 0, intHeight = 0; if (rcRect.m_max.x > rcRect.m_min.x && rcRect.m_max.y > rcRect.m_min.y) { intWidth = OdUInt32(rcRect.m_max.x - rcRect.m_min.x); intHeight = OdUInt32(rcRect.m_max.y - rcRect.m_min.y); } if (intWidth == 0 || intHeight == 0) { m_pContext->m_state.m_pStencilScanlines.setPhysicalLength(0); } else { intWidth++; intHeight++; if (m_pContext->m_state.m_stencilWidth != intWidth || m_pContext->m_state.m_stencilHeight != intHeight) { m_pContext->m_state.m_pStencilScanlines.resize(intWidth * intHeight); m_pContext->m_state.m_stencilWidth = intWidth; m_pContext->m_state.m_stencilHeight = intHeight; } m_pContext->m_state.m_stencilOffsetX = intOffsetX; m_pContext->m_state.m_stencilOffsetY = intOffsetY; } } } if (!m_pContext->m_state.m_pStencilScanlines.empty()) { // render clipping contour ::memset(m_pContext->m_state.m_pStencilScanlines.asArrayPtr(), 0, m_pContext->m_state.m_pStencilScanlines.size() * sizeof(OdUInt8)); m_pContext->m_state.m_pStencilScanlinesPtr = m_pContext->m_state.m_pStencilScanlines.asArrayPtr(); OdGePoint2dArray clipPoints; OdIntArray ptCounts; viewportClipRegion(ptCounts, clipPoints); gpc_polygon poly = OdGsNrcGpcConverter::getNrcClipAsGpcPolygon(ptCounts.size(), ptCounts.getPtr(), clipPoints); OdGsNrcGpcConverter::getGpcPolygonAsNrcClip(&poly, ptCounts, clipPoints); ::gpc_free_polygon(&poly); int numVerts; OdGePoint3dArray newVerts(clipPoints.size()); OdGiFillType prevType = subEntityTraits().fillType(); OdGsView::RenderMode prevMode = m_pGeometry->renderMode(); m_pGeometry->setRenderMode(k2DOptimized); if (prevType != kOdGiFillAlways) subEntityTraits().setFillType(kOdGiFillAlways); m_pGeometry->m_drawClipBuffer = true; for (int j = 0, i = 0; j < (int)ptCounts.size(); j++) { numVerts = ptCounts[j]; newVerts.resize(numVerts); int i0 = i; for (; i < i0 + numVerts; i++) newVerts[i - i0].set(clipPoints[i].x, clipPoints[i].y, 1.0); //.transformBy(pixels2DxCoords); m_pGeometry->polygonOut(numVerts, newVerts.asArrayPtr()); m_pGeometry->m_drawClipBuffer = false; } if (prevType != kOdGiFillAlways) subEntityTraits().setFillType(prevType); m_pGeometry->setRenderMode(prevMode); } else { m_pContext->resetStencilBuffer(); } if (m_pContext->m_state.m_pStencilScanlinesPtr) { m_pContext->m_state.m_triangleRendererData->clipRegion().width = m_pContext->m_state.m_stencilWidth; m_pContext->m_state.m_triangleRendererData->clipRegion().height = m_pContext->m_state.m_stencilHeight; m_pContext->m_state.m_triangleRendererData->clipRegion().offsetX = m_pContext->m_state.m_stencilOffsetX; m_pContext->m_state.m_triangleRendererData->clipRegion().offsetY = m_pContext->m_state.m_stencilOffsetY; m_pContext->m_state.m_triangleRendererData->clipRegion().clipMask = m_pContext->m_state.m_pStencilScanlines.getPtr(); } else m_pContext->m_state.m_triangleRendererData->clipRegion().resetMask(); } void OdGsSrVectorizeView::drawViewportFrame() { m_pipelineManager->processOnce(); // for now it's for HLR only OdGsSrBaseView::drawViewportFrame(); } void OdGsSrVectorizeView::initMetrics() { m_pContext->initBuffer(&device()->frameBuffer()); } bool OdGsSrVectorizeView::isViewModified() { return false; //for now not supported } void OdGsSrVectorizeView::updateView() { m_pContext->beginFrame( &device()->frameBuffer(), device()->width(), device()->height(), device()->scanLineSize(), OdGsBaseVectorizeView::isPerspective()); m_pContext->m_state.m_transformationHolder.setMatrixWorldToDevice(worldToDeviceMatrix()); m_pContext->m_state.m_transformationHolder.setMatrixWorldToEye(worldToEyeMatrix()); m_pContext->m_state.m_transformationHolder.setMatrixEyeToDevice(eyeToScreenMatrix()); m_pContext->setClipRegion(device()->outputRect()); m_drawTransparencyView = device()->get_DrawTransparency(); double frontClipValue = frontClip(); bool frontClipEnabled = isFrontClipped(); m_pContext->m_state.m_ZClipPlanes.backDistance = backClip(); m_pContext->m_state.m_ZClipPlanes.backClippingEnabled = isBackClipped(); if (view().isPerspective()) { if (frontClipEnabled) frontClipValue = odmin(frontClipValue, focalLength() * 0.9); else frontClipValue = focalLength() * 0.9; frontClipEnabled = true; } m_pContext->m_state.m_ZClipPlanes.frontDistance = frontClipValue; m_pContext->m_state.m_ZClipPlanes.frontClippingEnabled = frontClipEnabled; } OdGsSrVectorizerDevice* OdGsSrVectorizeView::device() { return static_cast(OdGsBaseVectorizeView::device()); } OdRxObjectPtr OdGsSrVectorizeView::newGsMetafile() { OdSmartPtr pRes = OdRxObjectImpl::createObject(); return pRes; } void OdGsSrVectorizeView::beginViewVectorization() { OdGsBaseVectorizer::beginViewVectorization(); m_pipelineManager->setupPipeline(this); bool hlrEnabled = device()->get_EnableSoftwareHLR(); m_pipelineManager->configure() .enableHLR((m_pGeometry->renderMode() == kHiddenLine) && hlrEnabled); m_pipelineManager->finalOutput() .setDestGeometry(*m_pGeometry); setDrawContextFlags(kSpatialFilterSimplText | kEmbranchmentSimplText, false); m_pGeometry->setDeviation(&m_pModelToEyeProc->eyeDeviation()); m_pGeometry->setDrawContext(OdGiBaseVectorizer::drawContext()); setEyeToOutputTransform(getEyeToWorldTransform()); auto modelToEye = getModelToEyeTransform(); auto modelToEye2 = getModelToEyeTransform(); } bool OdGsSrVectorizeView::enabledSoftwareHLR() { return device()->get_EnableSoftwareHLR(); } void OdGsSrVectorizeView::endViewVectorization() { if (!isOutputSuppressed()) { m_pipelineManager->process(); // for now it's for HLR only } m_pGeometry->stopPacking(); OdGsBaseVectorizer::endViewVectorization(); } ODCOLORREF OdGsSrVectorizeView::getColorRef(const OdCmEntityColor& col, const OdCmTransparency transparency) { ODCOLORREF baseColor = col.isByACI() || col.isByDgnIndex() ? paletteColor(col.colorIndex()) : ODTOCOLORREF(col); if (!m_drawTransparencyView) return baseColor | 0xFF000000; return (((ODCOLORREF)transparency.alpha()) << 24) | (baseColor & 0x00FFFFFF); } void OdGsSrVectorizeView::onTraitsModified() { OdGsBaseVectorizer::onTraitsModified(); if (isOutputSuppressed()) return; m_pGeometry->onTraitsModified(); } void OdGsSrVectorizeView::beginMetafile(OdRxObject* pMetafile) { m_pGeometry->beginMetafile(pMetafile, this); } void OdGsSrVectorizeView::endMetafile(OdRxObject* pMetafile) { m_pGeometry->endMetafile(pMetafile); } void OdGsSrVectorizeView::playMetafile(const OdRxObject* pMetafile) { OdSrMetafile* meta = static_cast(const_cast(pMetafile)); meta->play( *m_pContext); } void OdGsSrVectorizeView::dolly(double xDolly, double yDolly, double zDolly) { m_isMoved = true; OdGsSrBaseView::dolly(xDolly, yDolly, zDolly); } void OdGsSrVectorizeView::roll(double rollAngle) { m_isMoved = true; OdGsSrBaseView::roll(rollAngle); } void OdGsSrVectorizeView::orbit(double xOrbit, double yOrbit) { m_isMoved = true; OdGsSrBaseView::orbit(xOrbit, yOrbit); } void OdGsSrVectorizeView::zoom(double zoomFactor) { m_isMoved = true; OdGsSrBaseView::zoom(zoomFactor); } void OdGsSrVectorizeView::pan(double xPan, double yPan) { m_isMoved = true; OdGsSrBaseView::pan(xPan, yPan); } void OdGsSrVectorizeView::setView(const OdGePoint3d& position, const OdGePoint3d& target, const OdGeVector3d& upVector, double fieldWidth, double fieldHeight, Projection projection) { m_isMoved = true; OdGsSrBaseView::setView(position, target, upVector, fieldWidth, fieldHeight, projection); } void OdGsSrVectorizeView::zoomWindow(const OdGePoint2d& lowerLeft, const OdGePoint2d& upperRight) { m_isMoved = true; OdGsSrBaseView::zoomWindow(lowerLeft, upperRight); } void OdGsSrVectorizeView::zoomExtents(const OdGePoint3d& minPt, const OdGePoint3d& maxPt) { m_isMoved = true; OdGsSrBaseView::zoomExtents(minPt, maxPt); } void OdGsSrVectorizeView::setBackClip(double distance) { m_isMoved = true; OdGsSrBaseView::setBackClip(distance); } void OdGsSrVectorizeView::setFrontClip(double distance) { m_isMoved = true; OdGsSrBaseView::setFrontClip(distance); } bool OdGsSrVectorizeView::isMoved() const { return m_isMoved; } // copypaste from OdGsBaseVectorizeDevice::GsDeviceOverlayDataContainer::containsInvalidRects // except the use of tolerance bool OdGsSrVectorizerDevice::containsInvalidRectsTol(OdGsSrVectorizeView& view, OdInt32 pixTolerance) { bool bIfHas = false; OdUInt32 activeOverlays = m_overlayData.activeOverlays(); bool bHasInvalidRects = false; OdGsDCRect vprc; for (GsDeviceOverlayDataContainer::Iterator oIt = m_overlayData.newIterator(activeOverlays); !oIt.done(); oIt.next()) { const GsDeviceOverlayData* pData = oIt.overlayData(); unsigned n = pData->m_invalidRects.size(); if (n) { if (!bHasInvalidRects) view.screenRectNorm(vprc), bHasInvalidRects = true; OdGsDCRect invrc; unsigned i = 0; do { invrc = pData->m_invalidRects.getAt(i); invrc &= vprc; OdInt32 xDist = abs(invrc.m_max.x - invrc.m_min.x); OdInt32 yDist = abs(invrc.m_max.y - invrc.m_min.y); if (!(xDist <= pixTolerance || yDist <= pixTolerance)) return true; ++i; } while (i < n); } else if (m_overlayData.isOverlayInvalid(oIt.overlayId()) && view.isViewportOnScreen()) return true; } return (bIfHas) ? !bHasInvalidRects : false; } // copypaste from OdGsBaseVectorizeView::isValid // except the use of tolerance bool OdGsSrVectorizeView::isValidTol(OdInt32 pixTolerance) const { if (isInvalid() || isCheckValid()) return false; if (baseDevice()->invalid()) return false; OdGsSrVectorizeView* thisView = const_cast(this); if (cachedDrawables()) { if (m_drawables.size() > OdUInt32(getNCachedDrawables(*thisView))) return false; for (unsigned i = 0; i < m_drawables.size(); ++i) { DrawableHolder& holder = const_cast(m_drawables[i]); OdGsBaseModel* pModel = static_cast(holder.m_pGsModel.get()); if (pModel) { if (pModel->invalidVp(localViewportId(pModel))) { setCheckValid(true); // optimize next call of isValid() return false; } OdGsNode* pRootNode = (const_cast(this))->getRootNode(holder); if (pRootNode) { if (pRootNode->invalidVp()) { setCheckValid(true); // optimize next call of isValid() return false; } } } } } if (m_overlayData.hasInvalidRects(m_overlayData.activeOverlays())) return false; // Check does view intersects with any invalid rectangle if (thisView->device()->containsInvalidRectsTol(*thisView, pixTolerance)) return false; return true; }