/////////////////////////////////////////////////////////////////////////////// // 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 "Gs/GsContainerNode.h" #define DISABLE_SCOPE_TIMER #include "SrUpdateManager.h" #include "SrCameraMovementRecognizer.h" #include "vectorization/GsSrVectorizeDevice.h" #include "vectorization/GsSrVectorizeView.h" #include "rendering/SrFrameContext.h" #include void OdSrDefaultUpdateStrategy::updateRegion(const OdGsDCRect& region, const OdArray& /*views*/, OdGsDCRectArray& /*outInvalidRects*/) { m_device->fillBackgroundRect(region); } void shrink(OdGsDCRect& rect, long value) { rect.m_min.x += value; rect.m_max.x -= value; rect.m_min.y += value; rect.m_max.y -= value; } bool isNodeContainer(OdGsSrVectorizeView* pView) { int nRoots = pView->numRootDrawables(); for (int nDrawable = 0; nDrawable < nRoots; nDrawable++) { OdGiDrawablePtr pDrawable = pView->rootDrawableAt(nDrawable); if (!pDrawable.isNull()) { OdGsNode* pNode = static_cast(pDrawable->gsNode()); if (pNode && !pNode->isContainer()) return false; } } return true; } bool getExactViewBounds(const OdGsSrVectorizeView* pView, OdGsDCRect& extentsRect); OdGsDCRectArray filterRectByIntersection(const OdGsDCRectArray& baseRects, const OdGsDCRect& intersectedRect) { OdGsDCRectArray res; { res = baseRects; OdGsDCRect* curr = res.begin(); OdGsDCRect* end = res.end(); if (curr < end) { do { *curr &= intersectedRect; if (curr->is_null()) { res.erase(curr); end = res.end(); } else ++curr; } while (curr < end); } } return res; } void OdSrPanUpdateStrategy::updateRegion(const OdGsDCRect& region, const OdArray& views, OdGsDCRectArray& outInvalidRects) { if (views.size() == 0) return; // Hide non-container views (like Paper Space) temporarily for (OdGsSrVectorizeView* pView : views) { if (!isNodeContainer(pView)) { ViewVisibilityState state; state.pView = pView; state.wasVisible = pView->isVisible(); if (state.wasVisible) { pView->hide(); m_hiddenViews.push_back(state); } } } ODA_ASSERT(m_sdx != 0 || m_sdy != 0); OdGsDCRectArray rectsSmall = calculateInvalidRects(region); m_device->movePictureRect(region, m_sdx, m_sdy); for (auto itRect = rectsSmall.begin(); itRect != rectsSmall.end(); ++itRect) { m_device->fillBackgroundRect(*itRect); } outInvalidRects.assignMove(rectsSmall.begin(), rectsSmall.end()); for (OdGsSrVectorizeView* pView : views) { if (isNodeContainer(pView)) { OdGsDCRect viewRect; pView->screenRectNorm(viewRect); if (filterRectByIntersection(outInvalidRects, viewRect).empty()) { // This container view doesn't have invalid rectangles // Hide it temporarily to prevent full redraw ViewVisibilityState state; state.pView = pView; state.wasVisible = pView->isVisible(); if (state.wasVisible) { pView->hide(); m_hiddenViews.push_back(state); } } } } } OdGsDCRectArray OdSrPanUpdateStrategy::calculateInvalidRects(const OdGsDCRect& dcRect) { OdGsDCRectArray result; OdInt32 dx = Od_abs(m_sdx); OdInt32 dy = Od_abs(m_sdy); // Case: No movement at all if (dx == 0 && dy == 0) { return result; // Empty array - no invalid regions } // Case: Movement only along X axis if (dy == 0) { OdGsDCRect invalidRect = dcRect; if (m_sdx > 0) { // Moving right - invalidate left edge invalidRect.m_max.x = dcRect.m_min.x + dx; } else { // Moving left - invalidate right edge invalidRect.m_min.x = dcRect.m_max.x - dx; } result.push_back(invalidRect); return result; } // Case: Movement only along Y axis if (dx == 0) { OdGsDCRect invalidRect = dcRect; if (m_sdy > 0) { // Moving down - invalidate top edge invalidRect.m_max.y = dcRect.m_min.y + dy; } else { // Moving up - invalidate bottom edge invalidRect.m_min.y = dcRect.m_max.y - dy; } result.push_back(invalidRect); return result; } // General case OdGsDCRect horizontalRect; OdGsDCRect verticalRect; OdGsDCRect squareRect; if (m_sdx > 0) { squareRect.m_min.x = verticalRect.m_min.x = dcRect.m_min.x; squareRect.m_max.x = verticalRect.m_max.x = dcRect.m_min.x + dx; horizontalRect.m_min.x = dcRect.m_min.x + dx; horizontalRect.m_max.x = dcRect.m_max.x; } else { squareRect.m_min.x = verticalRect.m_min.x = dcRect.m_max.x - dx; squareRect.m_max.x = verticalRect.m_max.x = dcRect.m_max.x; horizontalRect.m_min.x = dcRect.m_min.x; horizontalRect.m_max.x = dcRect.m_max.x - dx; } if (m_sdy > 0) { squareRect.m_min.y = horizontalRect.m_min.y = dcRect.m_min.y; squareRect.m_max.y = horizontalRect.m_max.y = dcRect.m_min.y + dy; verticalRect.m_min.y = dcRect.m_min.y + dy; verticalRect.m_max.y = dcRect.m_max.y; } else { squareRect.m_min.y = horizontalRect.m_min.y = dcRect.m_max.y - dy; squareRect.m_max.y = horizontalRect.m_max.y = dcRect.m_max.y; verticalRect.m_min.y = dcRect.m_min.y; verticalRect.m_max.y = dcRect.m_max.y - dy; } return { horizontalRect, verticalRect, squareRect }; } void OdSrPanUpdateStrategy::setPanData(const CameraMovementType::PanData& data) { m_sdx = data.pixelsX; m_sdy = data.pixelsY; } void OdSrPanUpdateStrategy::postUpdate(const OdArray& /*views*/) { // Restore visibility for all previously hidden views for (const ViewVisibilityState& state : m_hiddenViews) { if (state.wasVisible) { state.pView->show(); } } // Clear the list for the next update cycle m_hiddenViews.clear(); } OdSrUpdateManager::OdSrUpdateManager(OdGsSrVectorizerDevice* device) : m_device(device), m_defaultStrategy(new OdSrDefaultUpdateStrategy(device)), m_emptyStrategy(new OdSrEmptyUpdateStrategy(device)), m_panStrategy(new OdSrPanUpdateStrategy(device)), m_cameraMovementRecognizer(new OdSrCameraMovementRecognizer(device)) // Initialize other strategies as needed { } /** * Determines if a view needs any update at all. * This combines all factors that might require a view to be updated. * * @param pView The view to check * @return true if the view needs any kind of update */ bool OdSrUpdateManager::viewNeedsUpdate(OdGsSrVectorizeView* pView) const { int viewIdx = getViewIndex(pView); if (viewIdx < 0) return false; // Check if this view is marked as dirty if (m_isDirty[viewIdx]) return true; // Check if camera has moved in this view if (m_viewMovementTypes[viewIdx] != CameraMovementType::None) return true; // Other criteria for determining if a view needs updates can be added here return false; } /** * Initialize the list of dirty views based on current state. * This should be called at the beginning of each update cycle. */ void OdSrUpdateManager::initializeDirtyViews() { for (int i = 0; i < m_device->numViews(); i++) { OdGsSrVectorizeView* pView = static_cast(m_device->viewAt(i)); int pixTol = 0; if (pView->isViewportBorderVisible()) { ODCOLORREF borderColor; int borderWidth; pView->getViewportBorderProperties(borderColor, borderWidth); pixTol = borderWidth; } m_isDirty[i] = !pView->isValidTol(pixTol); } } OdSrUpdateManager::~OdSrUpdateManager() { delete m_defaultStrategy; delete m_panStrategy; delete m_emptyStrategy; delete m_cameraMovementRecognizer; // Delete other owned strategies } void OdSrUpdateManager::updateViews(OdGsDCRect* pRect) { // Step 1: Analyze all views and their movements analyzeViewMovements(); // Step 2: Build non-overlapping rendering regions with associated views OdArray renderingRegions = buildRenderingRegions_old(); // Step 3: Determine optimal strategy for each region for (auto& region : renderingRegions) { bool regionHasInvalidView = false; for (auto pView : region.views) { if (!pView->isValid()) { regionHasInvalidView = true; break; } } if (!regionHasInvalidView) { region.strategy = m_emptyStrategy; continue; } // Determine the optimal strategy for this region determineOptimalStrategy(region); } OdArray strategyViewsMap; // Step 4: Apply strategies to regions and collect invalid rectangles OdGsDCRectArray allInvalidRects = applyStrategies(renderingRegions, strategyViewsMap); // Step 5: Move the collected invalid rects to the device finalizeUpdate(allInvalidRects); m_device->OdGsBaseVectorizeDevice::update(pRect); // Step 6: Call postUpdate on all strategies for (auto& mapEntry : strategyViewsMap) { mapEntry.strategy->postUpdate(mapEntry.views); } m_cameraMovementRecognizer->postUpdate(); for (int idxView = 0; idxView < m_device->numViews(); idxView++) { OdGsSrVectorizeView* pView = static_cast(m_device->viewAt(idxView)); pView->m_isMoved = false; } } OdGsDCRectArray OdSrUpdateManager::applyStrategies(const OdArray renderingRegions, OdArray& strategyViewsMap) { OdGsDCRectArray allInvalidRects; for (auto& region : renderingRegions) { OdGsDCRectArray regionInvalidRects; region.strategy->updateRegion(region.rect, region.views, regionInvalidRects); allInvalidRects.append(regionInvalidRects); // Track which views were processed by which strategy bool strategyFound = false; for (auto& mapEntry : strategyViewsMap) { if (mapEntry.strategy == region.strategy) { // Add any views not already in the list for (auto pView : region.views) { bool viewFound = false; for (auto existingView : mapEntry.views) { if (existingView == pView) { viewFound = true; break; } } if (!viewFound) { mapEntry.views.push_back(pView); } } strategyFound = true; break; } } if (!strategyFound) { OdSrStrategyViewsMap newEntry; newEntry.strategy = region.strategy; newEntry.views = region.views; strategyViewsMap.push_back(newEntry); } } return allInvalidRects; } bool OdSrUpdateManager::checkViewForPanning(OdGsSrVectorizeView* pView) const { OdGsClientViewInfo viewInfo; pView->clientViewInfo(viewInfo); //return !(viewInfo.viewportFlags & OdGsClientViewInfo::kHelperView // for now the optimization doesn't work for paper space and non-rect views // || viewInfo.viewportFlags & OdGsClientViewInfo::kDependentGeometry // || pView->isNonRectClipped()); return true; } bool OdSrUpdateManager::rectsIntersect(const OdGsDCRect& rect1, const OdGsDCRect& rect2) { return !(rect1.m_max.x <= rect2.m_min.x || rect1.m_min.x >= rect2.m_max.x || rect1.m_max.y <= rect2.m_min.y || rect1.m_min.y >= rect2.m_max.y); } OdGsDCRect OdSrUpdateManager::unionRects(const OdGsDCRect& rect1, const OdGsDCRect& rect2) { OdGsDCRect result; result.m_min.x = std::min(rect1.m_min.x, rect2.m_min.x); result.m_min.y = std::min(rect1.m_min.y, rect2.m_min.y); result.m_max.x = std::max(rect1.m_max.x, rect2.m_max.x); result.m_max.y = std::max(rect1.m_max.y, rect2.m_max.y); return result; } void OdSrUpdateManager::mergeOverlappingRects(const OdGsDCRectArray& inputRects, OdGsDCRectArray& outputRects) { if (inputRects.empty()) return; // Start with a copy of the input array OdGsDCRectArray workingRects(inputRects.begin(), inputRects.end()); bool merged = true; // Continue merging until no more overlaps are found while (merged) { merged = false; OdGsDCRectArray newRects; OdArray processed; processed.resize(workingRects.size(), false); for (unsigned int i = 0; i < workingRects.size(); i++) { if (processed[i]) continue; OdGsDCRect currentRect = workingRects[i]; processed[i] = true; for (unsigned int j = i + 1; j < workingRects.size(); j++) { if (processed[j]) continue; // Check if rectangles intersect if (rectsIntersect(currentRect, workingRects[j])) { // Merge overlapping rectangles currentRect = unionRects(currentRect, workingRects[j]); processed[j] = true; merged = true; } } newRects.push_back(currentRect); } workingRects = newRects; } // Copy the result to the output array outputRects.clear(); outputRects.assign(workingRects.begin(), workingRects.end()); } void OdSrUpdateManager::analyzeViewMovements() { m_viewMovementTypes.resize(m_device->numViews(), CameraMovementType::createNone()); m_viewRects.resize(m_device->numViews()); m_isDirty.resize(m_device->numViews(), false); // Initialize dirty view tracking at the beginning of each update cycle initializeDirtyViews(); m_device->initMetrics(); for (int idxView = 0; idxView < m_device->numViews(); idxView++) { OdGsSrVectorizeView* pView = static_cast(m_device->viewAt(idxView)); pView->initMetrics(); pView->updateView(); OdGsDCRect rcRect; pView->screenRectNorm(rcRect); if (pView->isViewportBorderVisible()) { ODCOLORREF borderColor; int borderWidth; pView->getViewportBorderProperties(borderColor, borderWidth); shrink(rcRect, borderWidth); } m_viewRects[idxView] = rcRect; if (checkViewForPanning(pView)) { m_viewMovementTypes[idxView] = m_cameraMovementRecognizer->recognizeMovementType(pView, idxView); } } } bool OdSrUpdateManager::calculateIntersection(const OdGsDCRect& rect1, const OdGsDCRect& rect2, OdGsDCRect& intersection) { if (!rectsIntersect(rect1, rect2)) return false; intersection.m_min.x = std::max(rect1.m_min.x, rect2.m_min.x); intersection.m_min.y = std::max(rect1.m_min.y, rect2.m_min.y); intersection.m_max.x = std::min(rect1.m_max.x, rect2.m_max.x); intersection.m_max.y = std::min(rect1.m_max.y, rect2.m_max.y); return true; } OdArray OdSrUpdateManager::splitRect(const OdGsDCRect& rect, const OdGsDCRect& obstacle) { OdArray result; // Calculate the intersection OdGsDCRect intersection; if (!calculateIntersection(rect, obstacle, intersection)) { // No intersection, return the original rect result.push_back(rect); return result; } // Check if the rectangles are identical or if one contains the other if ((rect.m_min.x == obstacle.m_min.x && rect.m_min.y == obstacle.m_min.y && rect.m_max.x == obstacle.m_max.x && rect.m_max.y == obstacle.m_max.y) || (intersection.m_min.x == rect.m_min.x && intersection.m_min.y == rect.m_min.y && intersection.m_max.x == rect.m_max.x && intersection.m_max.y == rect.m_max.y)) { // Identical rectangles or rect is contained within obstacle - nothing to return return result; } // Top section if (rect.m_min.y < intersection.m_min.y) { OdGsDCRect top; top.m_min.x = rect.m_min.x; top.m_min.y = rect.m_min.y; top.m_max.x = rect.m_max.x; top.m_max.y = intersection.m_min.y; result.push_back(top); } // Bottom section if (rect.m_max.y > intersection.m_max.y) { OdGsDCRect bottom; bottom.m_min.x = rect.m_min.x; bottom.m_min.y = intersection.m_max.y; bottom.m_max.x = rect.m_max.x; bottom.m_max.y = rect.m_max.y; result.push_back(bottom); } // Left section (at the height of the intersection) if (rect.m_min.x < intersection.m_min.x) { OdGsDCRect left; left.m_min.x = rect.m_min.x; left.m_min.y = intersection.m_min.y; left.m_max.x = intersection.m_min.x; left.m_max.y = intersection.m_max.y; result.push_back(left); } // Right section (at the height of the intersection) if (rect.m_max.x > intersection.m_max.x) { OdGsDCRect right; right.m_min.x = intersection.m_max.x; right.m_min.y = intersection.m_min.y; right.m_max.x = rect.m_max.x; right.m_max.y = intersection.m_max.y; result.push_back(right); } return result; } /** * Builds a set of non-overlapping rendering regions based on areas that need updates. * Instead of using view boundaries, this approach focuses on the actual areas of change. * * @return Array of rendering regions that cover all areas requiring updates */ OdArray OdSrUpdateManager::buildRenderingRegions() { OdArray regions; // For each view that needs updating, determine the exact areas of change for (int idxView = 0; idxView < m_device->numViews(); idxView++) { OdGsSrVectorizeView* pView = static_cast(m_device->viewAt(idxView)); if (viewNeedsUpdate(pView)) { // Determine the exact area of change for this view OdGsDCRectArray changeRects; if (m_viewMovementTypes[idxView] == CameraMovementType::PanAlongScreen) { // For panning, these are the areas affected by movement OdGsDCRect viewRect; pView->screenRectNorm(viewRect); m_panStrategy->setPanData(m_viewMovementTypes[idxView].panData()); changeRects = m_panStrategy->calculateInvalidRects(viewRect); } else { // For regular updates, get the content bounds //OdGsDCRect contentBounds = getViewContentExtents(pView); OdGsDCRect viewRect; pView->screenRectNorm(viewRect); changeRects.push_back(viewRect); } // Create a separate region for each changed area for (const auto& rect : changeRects) { OdSrRenderingRegion region; region.rect = rect; // Add all views that intersect with this area to the region for (int j = 0; j < m_device->numViews(); j++) { OdGsSrVectorizeView* otherView = static_cast(m_device->viewAt(j)); OdGsDCRect otherRect; otherView->screenRectNorm(otherRect); if (rectsIntersect(rect, otherRect)) { region.views.push_back(otherView); } } // Strategy will be determined later in determineOptimalStrategy region.strategy = m_defaultStrategy; regions.push_back(region); } } } // If no regions with changes were found, create one empty region for the whole screen if (regions.empty()) { OdSrRenderingRegion emptyRegion; emptyRegion.rect = OdGsDCRect(OdGsDCPoint(0, 0), OdGsDCPoint(m_device->width(), m_device->height())); emptyRegion.strategy = m_emptyStrategy; // Add all views to this region for (int i = 0; i < m_device->numViews(); i++) { emptyRegion.views.push_back(static_cast(m_device->viewAt(i))); } regions.push_back(emptyRegion); } // Resolve any overlapping regions to ensure non-overlapping areas if (regions.size() > 1) { regions = resolveOverlappingRegions(regions); } return regions; } OdArray OdSrUpdateManager::buildRenderingRegions_old() { // Start with individual rectangles for each view OdArray baseRects; for (int idxView = 0; idxView < m_device->numViews(); idxView++) { baseRects.push_back(m_viewRects[idxView]); } // Process all view rectangles to create non-overlapping regions OdArray nonOverlappingRects; for (OdUInt32 i = 0; i < baseRects.size(); i++) { OdGsDCRect current = baseRects[i]; OdArray fragments; fragments.push_back(current); // Split current rectangle against all previously processed rectangles for (OdUInt32 j = 0; j < nonOverlappingRects.size(); j++) { OdArray newFragments; for (OdUInt32 k = 0; k < fragments.size(); k++) { // Split each fragment against the current processed rectangle OdArray splits = splitRect(fragments[k], nonOverlappingRects[j]); newFragments.append(splits); } fragments = newFragments; if (fragments.empty()) break; // All fragments have been eliminated by intersections } // Add remaining fragments to the non-overlapping set nonOverlappingRects.append(fragments); } // Create rendering regions from non-overlapping rectangles OdArray regions; for (const OdGsDCRect& rect : nonOverlappingRects) { OdSrRenderingRegion region; region.rect = rect; region.strategy = m_defaultStrategy; // Initialize with default strategy // Find which views overlap with this region for (int idxView = 0; idxView < m_device->numViews(); idxView++) { if (rectsIntersect(rect, m_viewRects[idxView])) { region.views.push_back( static_cast(m_device->viewAt(idxView))); } } // Only add regions that are covered by at least one view if (!region.views.empty()) { regions.push_back(region); } } return regions; } /** * Resolves overlapping regions by splitting them into non-overlapping parts * * @param inputRegions The original potentially overlapping regions * @return An array of non-overlapping regions */ OdArray OdSrUpdateManager::resolveOverlappingRegions(const OdArray& inputRegions) { if (inputRegions.size() <= 1) { return inputRegions; // No need to resolve if there's only one region } OdArray outputRegions; // Process regions one by one for (unsigned int i = 0; i < inputRegions.size(); i++) { OdSrRenderingRegion currentRegion = inputRegions[i]; OdArray fragments; fragments.push_back(currentRegion.rect); // Split current region against all previously processed regions for (unsigned int j = 0; j < i; j++) { OdArray newFragments; // Process each existing fragment for (unsigned int k = 0; k < fragments.size(); k++) { // Split this fragment against the j-th region OdArray splitResults = splitRect(fragments[k], inputRegions[j].rect); newFragments.append(splitResults); } fragments = newFragments; if (fragments.empty()) { break; // All fragments have been eliminated by intersections } } // Create a new region for each remaining fragment for (const OdGsDCRect& fragment : fragments) { OdSrRenderingRegion newRegion; newRegion.rect = fragment; newRegion.strategy = currentRegion.strategy; // Find all views that intersect with this fragment for (int viewIdx = 0; viewIdx < m_device->numViews(); viewIdx++) { OdGsSrVectorizeView* pView = static_cast(m_device->viewAt(viewIdx)); OdGsDCRect viewRect; pView->screenRectNorm(viewRect); if (rectsIntersect(fragment, viewRect)) { newRegion.views.push_back(pView); } } // Only add regions that contain at least one view if (!newRegion.views.empty()) { outputRegions.push_back(newRegion); } } } // Merge regions with identical view sets and strategies if possible return mergeCompatibleRegions(outputRegions); } /** * Merges regions that have the same set of views and the same strategy * * @param regions The list of regions to merge * @return Optimized list of regions */ OdArray OdSrUpdateManager::mergeCompatibleRegions(const OdArray& regions) { if (regions.size() <= 1) { return regions; } OdArray mergedRegions; OdArray processed; processed.resize(regions.size(), false); for (unsigned int i = 0; i < regions.size(); i++) { if (processed[i]) continue; const OdSrRenderingRegion& baseRegion = regions[i]; OdGsDCRectArray compatibleRects; compatibleRects.push_back(baseRegion.rect); processed[i] = true; // Look for other regions with same views and strategy for (unsigned int j = i + 1; j < regions.size(); j++) { if (processed[j]) continue; const OdSrRenderingRegion& otherRegion = regions[j]; // Check if strategies match if (baseRegion.strategy != otherRegion.strategy) continue; // Check if view sets match if (baseRegion.views.size() != otherRegion.views.size()) continue; bool viewsMatch = true; for (unsigned int v = 0; v < baseRegion.views.size(); v++) { if (baseRegion.views[v] != otherRegion.views[v]) { viewsMatch = false; break; } } if (viewsMatch) { compatibleRects.push_back(otherRegion.rect); processed[j] = true; } } // Create a new merged region if we found compatible regions if (compatibleRects.size() > 1) { OdSrRenderingRegion mergedRegion; mergedRegion.strategy = baseRegion.strategy; mergedRegion.views = baseRegion.views; // Try to merge rectangles that are adjacent or overlapping OdGsDCRectArray optimizedRects; mergeOverlappingRects(compatibleRects, optimizedRects); // If we got a single rectangle, use it if (optimizedRects.size() == 1) { mergedRegion.rect = optimizedRects[0]; mergedRegions.push_back(mergedRegion); } // Otherwise, create separate regions for each rectangle else { for (const OdGsDCRect& rect : optimizedRects) { OdSrRenderingRegion separateRegion = mergedRegion; separateRegion.rect = rect; mergedRegions.push_back(separateRegion); } } } // If no compatible regions, just add the original region else { mergedRegions.push_back(baseRegion); } } return mergedRegions; } bool OdSrUpdateManager::isFullscreenView(OdGsSrVectorizeView* pView) const { OdGsDCRect viewRect; pView->screenRectNorm(viewRect); OdGsDCRect deviceRect(OdGsDCPoint(0, 0), OdGsDCPoint(m_device->width(), m_device->height())); return viewRect.m_min.x == deviceRect.m_min.x && viewRect.m_min.y == deviceRect.m_min.y && viewRect.m_max.x == deviceRect.m_max.x && viewRect.m_max.y == deviceRect.m_max.y; } bool OdSrUpdateManager::determineOptimalStrategy(OdSrRenderingRegion& region) { bool needsPanning = false; bool needsAnyUpdate = false; // Check if any view in this region needs updating for (auto pView : region.views) { int viewIdx = getViewIndex(pView); if (viewIdx >= 0) { // Check if this view needs any update at all if (viewNeedsUpdate(pView)) { needsAnyUpdate = true; // Check specifically for panning needs if (m_viewMovementTypes[viewIdx] == CameraMovementType::PanAlongScreen) { m_panStrategy->setPanData(m_viewMovementTypes[viewIdx].panData()); needsPanning = true; break; // Once we decide to use pan strategy, no need to check further } } } } if (needsPanning) { OdGsDCRectArray invalidRects = m_panStrategy->calculateInvalidRects(region.rect); for (auto pView : region.views) { OdGsDCRect exactViewRect; if (!getExactViewBounds(pView, exactViewRect)) pView->screenRectNorm(exactViewRect); //check if any fullscreen non-container view (paper) has invalid rectangles bool condition1 = !isNodeContainer(pView) && !filterRectByIntersection(invalidRects, exactViewRect).empty() && isFullscreenView(pView); OdGsDCRect viewRect; pView->screenRectNorm(viewRect); // Check if this view is contained within another view bool isContainedViewport = false; for (const auto& pOtherView : region.views) { // Skip comparing with itself if (pView == pOtherView) continue; OdGsClientViewInfo viewInfo; pOtherView->clientViewInfo(viewInfo); bool isHelper = GETBIT(viewInfo.viewportFlags, OdGsClientViewInfo::kHelperView); if (isHelper) continue; OdGsDCRect otherRect; pOtherView->screenRectNorm(otherRect); // Check if this view is completely contained within another view if (viewRect.m_min.x > otherRect.m_min.x && viewRect.m_max.x < otherRect.m_max.x && viewRect.m_min.y > otherRect.m_min.y && viewRect.m_max.y < otherRect.m_max.y) { isContainedViewport = true; break; } } //to prevent unimplemented behavior when we need to move a picture across the paper bool condition2 = isContainedViewport && viewNeedsUpdate(pView) && pView->isMoved(); if (condition1 || condition2) { needsPanning = false; break; } } } // Set the appropriate strategy: // 1. If no views in this region need updating, use Empty strategy // 2. If any view needs panning, use Pan strategy // 3. Otherwise use Default strategy for regular updates if (!needsAnyUpdate) { region.strategy = m_emptyStrategy; } else if (needsPanning) { region.strategy = m_panStrategy; } else { region.strategy = m_defaultStrategy; } return needsPanning; } void OdSrUpdateManager::finalizeUpdate(OdGsDCRectArray& invalidRects) { if (invalidRects.size() > 0) { OdGsDCRectArray optimizedRects; mergeOverlappingRects(invalidRects, optimizedRects); m_device->setValid(true); m_device->m_overlayData.getOverlayData(kGsMainOverlay)->m_invalidRects.assign(optimizedRects.begin(), optimizedRects.end()); } else { m_device->m_overlayData.getOverlayData(kGsMainOverlay)->m_invalidRects.clear(); m_device->invalidate(); } } int OdSrUpdateManager::getViewIndex(OdGsSrVectorizeView* pView) const { for (int i = 0; i < m_device->numViews(); i++) if (m_device->viewAt(i) == pView) return i; return -1; }