/////////////////////////////////////////////////////////////////////////////// // 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 "SrFrameContext.h" #include "vectorization/GsSrVectorizeView.h" OdSrFrameContext::OdSrFrameContext() { m_state.m_color = 0; m_state.m_pDepthBuffer = nullptr; m_state.m_triangleRendererData = new TriangleRendering::OdSrTriangleRendererData(m_state.m_ZClipPlanes); m_state.m_triangleRenderer = new TriangleRendering::OdSrTriangleRenderer(*m_state.m_triangleRendererData, *this); } OdSrFrameContext::~OdSrFrameContext() { delete m_state.m_triangleRenderer; delete m_state.m_triangleRendererData; delete[] m_state.m_pDepthBuffer; //it's created in OdSrFrameContext::initMetrics() as new OdUInt8[...] } void OdSrFrameContext::beginFrame(OdUInt8Array* scanLinesBuffer, OdUInt32 width, OdUInt32 height, OdUInt32 scanLineLength, bool isPerspective) { m_state.m_scanLinesArray = scanLinesBuffer; m_state.m_width = width; m_state.m_height = height; m_state.m_scanLineLength = scanLineLength; m_state.m_scanLinesArray->resize(m_state.m_scanLineLength * m_state.m_height); m_state.m_pScanLines = m_state.m_scanLinesArray->asArrayPtr(); m_state.m_triangleRendererData->setFrameBuffer(m_state.m_width, m_state.m_height, m_state.m_pScanLines, m_state.m_scanLineLength); if (isPerspective) { if (!m_state.m_pDepthBuffer || (m_state.m_width * m_state.m_height) > 0) { delete[] m_state.m_pDepthBuffer; m_state.m_pDepthBuffer = new OdUInt8[m_state.m_width * m_state.m_height]; } m_state.m_triangleRendererData->setDepthBuffer(m_state.m_width, m_state.m_height, m_state.m_pDepthBuffer, m_state.m_scanLineLength); } } void OdSrFrameContext::drawColor(ODCOLORREF color) { m_state.m_color = color; auto* pView = view(); if (pView->isFadingEnabledAndChanged()) { ODCOLORREF fadeColor; OdUInt8 fadingValue; pView->getFadeParams(fadeColor, fadingValue); m_state.m_color = (ODGETALPHA(m_state.m_color) << 24) | ::OdMergeRGBAlpha(fadeColor, m_state.m_color & 0x00FFFFFF, fadingValue); } } void OdSrFrameContext::setColorBuffer(const ODCOLORREF* pColors, OdUInt32 colorsLength) { m_state.m_triangleRendererData->setColorBuffer(pColors, colorsLength); } void OdSrFrameContext::applyFadingToColorBuffer(const ODCOLORREF* pOriginalColors, OdUInt32 colorsLength, ODCOLORREF fadeColor, OdUInt8 fadeValue) { m_state.m_triangleRendererData->applyFadingToColorBuffer(pOriginalColors, colorsLength, fadeColor, fadeValue); } void OdSrFrameContext::drawMultiColorTriangle(const OdGePoint3d* points) { OdUInt32 length; const ODCOLORREF* threeColors = m_state.m_triangleRendererData->triangleColorBuffer(length); auto p0 = m_state.m_transformationHolder.transformToDevice(points[0].convert2d()); auto p1 = m_state.m_transformationHolder.transformToDevice(points[1].convert2d()); auto p2 = m_state.m_transformationHolder.transformToDevice(points[2].convert2d()); draw_multicolor_triangle( (float)p0.x, (float)p0.y, (float)p1.x, (float)p1.y, (float)p2.x, (float)p2.y, threeColors); } void OdSrFrameContext::drawTexturedTriangle(const OdGePoint3d* points) { getRenderer().renderTriangle(points[0], points[1], points[2], m_state.m_transformationHolder); } void OdSrFrameContext::drawTriangle(const OdGePoint3d* points, bool clipBuffer) { auto p0 = m_state.m_transformationHolder.transformToDevice(points[0].convert2d()); auto p1 = m_state.m_transformationHolder.transformToDevice(points[1].convert2d()); auto p2 = m_state.m_transformationHolder.transformToDevice(points[2].convert2d()); if (clipBuffer) { draw_clipping_triangle( int(OdRound(points[0].x)), int(OdRound(points[0].y)), int(OdRound(points[1].x)), int(OdRound(points[1].y)), int(OdRound(points[2].x)), int(OdRound(points[2].y))); } else { draw_triangle( int(OdRound(p0.x)), int(OdRound(p0.y)), int(OdRound(p1.x)), int(OdRound(p1.y)), int(OdRound(p2.x)), int(OdRound(p2.y)), m_state.m_color); } } void OdSrFrameContext::drawCircle(const OdGePoint3d& center, double radius, bool filled) { const OdGeMatrix3d& worldToDevice = m_state.m_transformationHolder.worldToDevice(); OdGePoint3d centerDev = worldToDevice * center; OdGeVector3d xAxis(1.0, 0.0, 0.0); OdGeVector3d startDev = (worldToDevice * xAxis).normalize(); auto circlePointDev = worldToDevice * (center.asVector() + xAxis * radius).asPoint(); double radiusDev = circlePointDev.distanceTo(centerDev); if (filled) { draw_filled_circle((OdInt32)OdRound(centerDev.x), (OdInt32)OdRound(centerDev.y), radiusDev, m_state.m_color); return; } drawCircleAlgorithm((OdInt32)OdRound(centerDev.x), (OdInt32)OdRound(centerDev.y), (OdInt32)OdRound(radiusDev)); } void OdSrFrameContext::draw_general_line(int x1, int y1, int x2, int y2, int lineweight, ODCOLORREF color) { if (lineweight < 2) draw_line(x1, y1, x2, y2, color); else draw_lwLine(x1, y1, x2, y2, lineweight, color); } void OdSrFrameContext::draw_lwLine(int x1, int y1, int x2, int y2, int lineweight, ODCOLORREF color) { OdGeVector2d vDir((double)(x2 - x1), (double)(y2 - y1)); if (!OdZero(vDir.lengthSqrd())) { double half_lineweight = double(lineweight) * 0.5; OdGeVector2d vPerp = vDir.perpVector().normal() * half_lineweight; // Convert to double once, avoid repeated casting double dx1 = double(x1), dy1 = double(y1); double dx2 = double(x2), dy2 = double(y2); // Calculate all 4 corner points in one go int x1_pos = (OdInt32)OdRound(dx1 + vPerp.x); int y1_pos = (OdInt32)OdRound(dy1 + vPerp.y); int x1_neg = (OdInt32)OdRound(dx1 - vPerp.x); int y1_neg = (OdInt32)OdRound(dy1 - vPerp.y); int x2_pos = (OdInt32)OdRound(dx2 + vPerp.x); int y2_pos = (OdInt32)OdRound(dy2 + vPerp.y); int x2_neg = (OdInt32)OdRound(dx2 - vPerp.x); int y2_neg = (OdInt32)OdRound(dy2 - vPerp.y); // Draw the rectangle as 2 triangles draw_triangle(x1_pos, y1_pos, x2_pos, y2_pos, x2_neg, y2_neg, color); draw_triangle(x1_pos, y1_pos, x2_neg, y2_neg, x1_neg, y1_neg, color); // Draw end caps - radius is just half_lineweight, centers are original points draw_filled_circle_precise(dx1, dy1, half_lineweight, color); draw_filled_circle_precise(dx2, dy2, half_lineweight, color); } else draw_filled_circle(x2, y2, double(lineweight) * 0.5, color); } void OdSrFrameContext::drawLineSegment(const OdGePoint3d& worldP1, const OdGePoint3d& worldP2, int lineweightPixels) { const OdGeMatrix3d& worldToDevice = m_state.m_transformationHolder.worldToDevice(); OdGePoint3d devP1 = worldToDevice * worldP1; OdGePoint3d devP2 = worldToDevice * worldP2; draw_general_line( static_cast(OdRound(devP1.x)), static_cast(OdRound(devP1.y)), static_cast(OdRound(devP2.x)), static_cast(OdRound(devP2.y)), lineweightPixels, m_state.m_color); } void OdSrFrameContext::drawLineSegmentEye(const OdGePoint3d& eyeP1, const OdGePoint3d& eyeP2, int lineweightPixels) { const OdGeMatrix3d& eyeToDevice = m_state.m_transformationHolder.eyeToDevice(); OdGePoint3d devP1 = eyeToDevice * eyeP1; OdGePoint3d devP2 = eyeToDevice * eyeP2; draw_general_line( static_cast(OdRound(devP1.x)), static_cast(OdRound(devP1.y)), static_cast(OdRound(devP2.x)), static_cast(OdRound(devP2.y)), lineweightPixels, m_state.m_color); } void OdSrFrameContext::drawPoint(const OdGePoint3d& point, OdDb::LineWeight lineweight) { const OdSrZClipper::ZClipPlanes& planes = m_state.m_ZClipPlanes; if (planes.frontClippingEnabled || planes.backClippingEnabled) { const OdGeMatrix3d& worldToEye = m_state.m_transformationHolder.worldToEye(); OdGePoint3d eyeP = worldToEye * point; if (!OdSrZClipper::clipPointZ(eyeP, planes)) return; } const OdGeMatrix3d& worldToDev = m_state.m_transformationHolder.worldToDevice(); OdGePoint3d screenP = worldToDev * point; int lineweightPixels = view()->lineweightToPixels(lineweight); if (lineweightPixels < 2) draw_point((OdInt32)OdRound(screenP.x), (OdInt32)OdRound(screenP.y), m_state.m_color); else draw_filled_circle((OdInt32)OdRound(screenP.x), (OdInt32)OdRound(screenP.y), double(lineweightPixels) * 0.5, m_state.m_color); } void OdSrFrameContext::drawPolyline(OdUInt32 nPoints, const OdGePoint3d* pPoints, OdDb::LineWeight lineweight, bool closed) { if (nPoints < 2) return; const OdSrZClipper::ZClipPlanes& planes = m_state.m_ZClipPlanes; const bool clippingEnabled = planes.frontClippingEnabled || planes.backClippingEnabled; // If no clipping enabled, use original fast path int lineweightPixels = view()->lineweightToPixels(lineweight); if (!clippingEnabled) { drawPolylineDirect(nPoints, pPoints, lineweightPixels, closed); return; } const OdGeMatrix3d& worldToEye = m_state.m_transformationHolder.worldToEye(); for (OdUInt32 idx = 1; idx < nPoints; ++idx) { OdGePoint3d eyeP1 = worldToEye * pPoints[idx - 1]; OdGePoint3d eyeP2 = worldToEye * pPoints[idx]; OdSrZClipper::ZClipLineResult result = OdSrZClipper::clipLineZEye(eyeP1, eyeP2, planes); if (result != OdSrZClipper::kFullyOutside) drawLineSegmentEye(eyeP1, eyeP2, lineweightPixels); } if (closed) { OdGePoint3d eyeP1 = worldToEye * pPoints[nPoints - 1]; OdGePoint3d eyeP2 = worldToEye * pPoints[0]; OdSrZClipper::ZClipLineResult result = OdSrZClipper::clipLineZEye(eyeP1, eyeP2, planes); if (result != OdSrZClipper::kFullyOutside) drawLineSegmentEye(eyeP1, eyeP2, lineweightPixels); } } void OdSrFrameContext::drawPolylineDirect(OdUInt32 nPoints, const OdGePoint3d* pPoints, int lineweightPixels, bool closed) { const OdGeMatrix3d& worldToDevice = m_state.m_transformationHolder.worldToDevice(); double divider = (worldToDevice[3][0] * pPoints->x + worldToDevice[3][1] * pPoints->y + worldToDevice[3][2] * pPoints->z + worldToDevice[3][3]); if (OdEqual(divider, 1.0, 1.0e-06)) { const OdGePoint3d* pStart = pPoints; for (OdUInt32 idx = 0; idx < nPoints - 1; ++idx, ++pPoints) { OdInt32 x0, y0, x1, y1; m_state.m_transformationHolder.transformTwoPoints3dTo4ints(pPoints, x0, y0, x1, y1); draw_general_line( x0, y0, x1, y1, lineweightPixels, m_state.m_color); } if (closed) { OdGePoint3d p0 = worldToDevice * pStart[nPoints - 1]; OdGePoint3d p1 = worldToDevice * pStart[0]; draw_general_line( static_cast(OdRound(p0.x)), static_cast(OdRound(p0.y)), static_cast(OdRound(p1.x)), static_cast(OdRound(p1.y)), lineweightPixels, m_state.m_color); } } else { for (OdUInt32 idx = 1; idx < nPoints; ++idx) { const OdGePoint3d& curPoint = pPoints[idx - 1]; const OdGePoint3d& nextPoint = pPoints[idx]; OdGePoint3d devP1 = worldToDevice * curPoint; OdGePoint3d devP2 = worldToDevice * nextPoint; draw_general_line( static_cast(OdRound(devP1.x)), static_cast(OdRound(devP1.y)), static_cast(OdRound(devP2.x)), static_cast(OdRound(devP2.y)), lineweightPixels, m_state.m_color); } if (closed) { const OdGePoint3d& curPoint = pPoints[nPoints - 1]; const OdGePoint3d& nextPoint = pPoints[0]; OdGePoint3d devP1 = worldToDevice * curPoint; OdGePoint3d devP2 = worldToDevice * nextPoint; draw_general_line( static_cast(OdRound(devP1.x)), static_cast(OdRound(devP1.y)), static_cast(OdRound(devP2.x)), static_cast(OdRound(devP2.y)), lineweightPixels, m_state.m_color); } } } void OdSrFrameContext::initBuffer(OdUInt8Array* scanLinesBuffer) { m_state.m_scanLinesArray = scanLinesBuffer; } void OdSrFrameContext::setClipRegion(const OdGsDCRect& rect) { m_state.m_clipRegion = rect; m_state.m_clipRegion.m_max.y = m_state.m_height - 1 - m_state.m_clipRegion.m_max.y; m_state.m_clipRegion.m_min.y = m_state.m_height - 1 - m_state.m_clipRegion.m_min.y; if (m_state.m_clipRegion.m_min.x > m_state.m_clipRegion.m_max.x) std::swap(m_state.m_clipRegion.m_min.x, m_state.m_clipRegion.m_max.x); if (m_state.m_clipRegion.m_min.y > m_state.m_clipRegion.m_max.y) std::swap(m_state.m_clipRegion.m_min.y, m_state.m_clipRegion.m_max.y); if (m_state.m_clipRegion.m_min.x < 0) m_state.m_clipRegion.m_min.x = 0; if (m_state.m_clipRegion.m_min.y < 0) m_state.m_clipRegion.m_min.y = 0; if (m_state.m_clipRegion.m_max.x > (int)m_state.m_width - 1) m_state.m_clipRegion.m_max.x = m_state.m_width - 1; if (m_state.m_clipRegion.m_max.y > (int)m_state.m_height - 1) m_state.m_clipRegion.m_max.y = m_state.m_height - 1; m_state.m_triangleRendererData->clipRegion().clipRect = m_state.m_clipRegion; } const OdGsDCRect& OdSrFrameContext::clipRegion() const { return m_state.m_clipRegion; } void OdSrFrameContext::setStencilBufferArea(OdUInt32 nrcWidth, OdUInt32 nrcHeight) { m_state.m_pStencilScanlines.resize(nrcWidth * nrcHeight); m_state.m_stencilWidth = nrcWidth; m_state.m_stencilHeight = nrcHeight; m_state.m_pStencilScanlinesPtr = m_state.m_pStencilScanlines.asArrayPtr(); } void OdSrFrameContext::setStencilBufferOffset(long offsetX, long offsetY) { m_state.m_stencilOffsetX = offsetX; m_state.m_stencilOffsetY = offsetY; } void OdSrFrameContext::resetStencilBuffer() { m_state.m_pStencilScanlines.setPhysicalLength(0); m_state.m_pStencilScanlinesPtr = nullptr; } TriangleRendering::OdSrTriangleRenderer& OdSrFrameContext::getRenderer() { return *m_state.m_triangleRenderer; } void OdSrFrameContext::setTextureData( const OdUInt8* data, OdUInt32 width, OdUInt32 height, OdUInt32 scanLineLength, const OdGiRasterImage::PixelFormatInfo& pixelFormat, const OdSrTextureTransform& transform, const OdSrTextureParams& params) { m_state.m_triangleRendererData->setTextureBuffer(width, height, const_cast(data), scanLineLength); m_state.m_triangleRendererData->setTextureTransform(transform); m_state.m_triangleRendererData->setPixelFormat(pixelFormat); m_state.m_triangleRendererData->setTextureParams(params); } void OdSrFrameContext::resetTexture() { m_state.m_triangleRendererData->resetTextureBuffer(); }