LCOV - code coverage report
Current view: top level - src/playback/qml/MuseScore/Playback - notationregionsbeingprocessedmodel.cpp Coverage Total Hit
Test: filtered_coverage.info Lines: 0.0 % 315 0
Test Date: 2026-11-05 04:07:02

            Line data    Source code
       1              : /*
       2              :  * SPDX-License-Identifier: GPL-3.0-only
       3              :  * MuseScore-Studio-CLA-applies
       4              :  *
       5              :  * MuseScore Studio
       6              :  * Music Composition & Notation
       7              :  *
       8              :  * Copyright (C) 2025 MuseScore Limited and others
       9              :  *
      10              :  * This program is free software: you can redistribute it and/or modify
      11              :  * it under the terms of the GNU General Public License version 3 as
      12              :  * published by the Free Software Foundation.
      13              :  *
      14              :  * This program is distributed in the hope that it will be useful,
      15              :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      16              :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      17              :  * GNU General Public License for more details.
      18              :  *
      19              :  * You should have received a copy of the GNU General Public License
      20              :  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
      21              :  */
      22              : 
      23              : #include "notationregionsbeingprocessedmodel.h"
      24              : 
      25              : #include <QTransform>
      26              : #include <QVariant>
      27              : 
      28              : #include "engraving/dom/measure.h"
      29              : #include "engraving/dom/score.h"
      30              : #include "engraving/dom/segment.h"
      31              : #include "engraving/dom/system.h"
      32              : 
      33              : #include "notation/imasternotation.h"
      34              : #include "notation/inotation.h"
      35              : #include "notation/inotationparts.h" // IWYU pragma: keep
      36              : #include "notation/inotationplayback.h" // IWYU pragma: keep
      37              : 
      38              : using namespace muse::audio;
      39              : using namespace mu::playback;
      40              : using namespace mu::engraving;
      41              : using namespace mu::notation;
      42              : 
      43            0 : static const Segment* findSegmentFrom(const Score* score, const System* system,
      44              :                                       const int tickFrom, const staff_idx_t staffIdx)
      45              : {
      46            0 :     const Segment* segment = score->tick2leftSegment(Fraction::fromTicks(tickFrom), /*MM*/ true, SegmentType::ChordRest);
      47            0 :     if (!segment || segment->system() != system) {
      48            0 :         return nullptr;
      49              :     }
      50              : 
      51            0 :     const track_idx_t trackIdx = staff2track(staffIdx);
      52            0 :     const EngravingItem* item = segment->element(trackIdx);
      53            0 :     if (!item) {
      54            0 :         return nullptr;
      55              :     }
      56              : 
      57            0 :     if (!item->isRest()) {
      58            0 :         return segment;
      59              :     }
      60              : 
      61            0 :     while (segment && item && item->isRest()) {
      62            0 :         segment = segment->next1(SegmentType::ChordRest);
      63            0 :         item = segment ? segment->element(trackIdx) : nullptr;
      64              :     }
      65              : 
      66            0 :     return segment;
      67              : }
      68              : 
      69            0 : static const Segment* findSegmentTo(const Score* score, const System* system, const Segment* segmentFrom,
      70              :                                     const int tickTo, const staff_idx_t staffIdx)
      71              : {
      72            0 :     const Segment* segment = score->tick2leftSegment(Fraction::fromTicks(tickTo), /*MM*/ true, SegmentType::ChordRest);
      73            0 :     if (!segment || segment->system() != system) {
      74            0 :         return nullptr;
      75              :     }
      76              : 
      77            0 :     if (segment == segmentFrom) {
      78            0 :         return segment;
      79              :     }
      80              : 
      81            0 :     const track_idx_t trackIdx = staff2track(staffIdx);
      82            0 :     const EngravingItem* item = segment->element(trackIdx);
      83            0 :     if (!item) {
      84            0 :         return nullptr;
      85              :     }
      86              : 
      87            0 :     if (!item->isRest()) {
      88            0 :         return segment;
      89              :     }
      90              : 
      91            0 :     while (segment && item && item->isRest()) {
      92            0 :         segment = segment->prev1(SegmentType::ChordRest);
      93            0 :         item = segment ? segment->element(trackIdx) : nullptr;
      94              :     }
      95              : 
      96            0 :     return segment;
      97              : }
      98              : 
      99            0 : NotationRegionsBeingProcessedModel::NotationRegionsBeingProcessedModel(QObject* parent)
     100            0 :     : QAbstractListModel(parent), muse::Contextable(muse::iocCtxForQmlObject(this))
     101              : {
     102            0 : }
     103              : 
     104            0 : QVariant NotationRegionsBeingProcessedModel::data(const QModelIndex& index, int role) const
     105              : {
     106            0 :     if (!index.isValid() || index.row() >= rowCount()) {
     107            0 :         return QVariant();
     108              :     }
     109              : 
     110            0 :     const RegionInfo& region = m_regions.at(index.row());
     111              : 
     112            0 :     switch (role) {
     113            0 :     case RectRole: return region.viewRect;
     114            0 :     case ProgressRole: {
     115            0 :         auto it = m_tracksBeingProcessed.find(region.trackId);
     116            0 :         return it != m_tracksBeingProcessed.end() ? it->second.progress : 0;
     117              :     }
     118              :     }
     119              : 
     120            0 :     return QVariant();
     121              : }
     122              : 
     123            0 : int NotationRegionsBeingProcessedModel::rowCount(const QModelIndex&) const
     124              : {
     125            0 :     return m_regions.size();
     126              : }
     127              : 
     128            0 : QHash<int, QByteArray> NotationRegionsBeingProcessedModel::roleNames() const
     129              : {
     130              :     static const QHash<int, QByteArray> roles {
     131            0 :         { RectRole, "rect" },
     132            0 :         { ProgressRole, "progress" },
     133            0 :     };
     134              : 
     135            0 :     return roles;
     136            0 : }
     137              : 
     138            0 : void NotationRegionsBeingProcessedModel::load()
     139              : {
     140            0 :     clear();
     141            0 :     projectSession()->masterNotationChanged().onNotify(this, [this]() {
     142            0 :         clear();
     143            0 :     });
     144              : 
     145            0 :     onOnlineSoundsChanged();
     146            0 :     playbackController()->onlineSoundsChanged().onNotify(this, [this]() {
     147            0 :         onOnlineSoundsChanged();
     148            0 :     });
     149              : 
     150            0 :     listenViewModeChanges();
     151              : 
     152            0 :     projectSession()->currentNotationChanged().onNotify(this, [this]() {
     153            0 :         listenViewModeChanges();
     154            0 :         setRegions(calculateRegions(m_tracksBeingProcessed));
     155            0 :     });
     156              : 
     157            0 :     playbackController()->isPlayingChanged().onReceive(this, [this](bool) {
     158            0 :         onIsPlayingChanged();
     159            0 :     });
     160              : 
     161            0 :     configuration()->onlineSoundsShowProgressBarModeChanged().onNotify(this, [this]() {
     162            0 :         initShouldShowRegions();
     163            0 :         setRegions(calculateRegions(m_tracksBeingProcessed));
     164            0 :     });
     165            0 : }
     166              : 
     167            0 : QVariant NotationRegionsBeingProcessedModel::notationViewMatrix() const
     168              : {
     169            0 :     return m_notationViewMatrix;
     170              : }
     171              : 
     172            0 : bool NotationRegionsBeingProcessedModel::isEmpty() const
     173              : {
     174            0 :     return m_regions.empty();
     175              : }
     176              : 
     177            0 : QColor NotationRegionsBeingProcessedModel::progressBackgroundColor() const
     178              : {
     179            0 :     return notationConfiguration()->selectionColor(VOICES); // "all voices" color
     180              : }
     181              : 
     182            0 : QColor NotationRegionsBeingProcessedModel::progressTextColor() const
     183              : {
     184            0 :     return notationConfiguration()->notationColor();
     185              : }
     186              : 
     187            0 : void NotationRegionsBeingProcessedModel::setNotationViewMatrix(const QVariant& matrix)
     188              : {
     189            0 :     if (m_notationViewMatrix == matrix) {
     190            0 :         return;
     191              :     }
     192              : 
     193            0 :     m_notationViewMatrix = matrix.value<QTransform>();
     194            0 :     emit notationViewMatrixChanged();
     195              : 
     196            0 :     onViewMatrixChanged();
     197              : }
     198              : 
     199            0 : bool NotationRegionsBeingProcessedModel::isPlaying() const
     200              : {
     201            0 :     return playbackController()->isPlaying();
     202              : }
     203              : 
     204            0 : void NotationRegionsBeingProcessedModel::clear()
     205              : {
     206            0 :     m_onlineSounds.clear();
     207            0 :     m_tracksBeingProcessed.clear();
     208            0 :     initShouldShowRegions();
     209            0 :     setRegions({});
     210            0 : }
     211              : 
     212            0 : void NotationRegionsBeingProcessedModel::setRegions(const QList<RegionInfo>& regions)
     213              : {
     214            0 :     if (m_regions == regions) {
     215            0 :         return;
     216              :     }
     217              : 
     218            0 :     const bool wasEmpty = m_regions.empty();
     219            0 :     beginResetModel();
     220            0 :     m_regions = regions;
     221            0 :     endResetModel();
     222              : 
     223            0 :     if (wasEmpty != m_regions.empty()) {
     224            0 :         emit isEmptyChanged();
     225              :     }
     226              : }
     227              : 
     228            0 : void NotationRegionsBeingProcessedModel::onOnlineSoundsChanged()
     229              : {
     230            0 :     const std::map<TrackId, AudioResourceMeta>& newSounds = playbackController()->onlineSounds();
     231              : 
     232            0 :     for (const auto& pair : newSounds) {
     233            0 :         auto it = m_onlineSounds.find(pair.first);
     234            0 :         if (it == m_onlineSounds.end() || it->second != pair.second) {
     235            0 :             startListeningToProgress(pair.first);
     236              :         }
     237              :     }
     238              : 
     239            0 :     for (const auto& pair : m_onlineSounds) {
     240            0 :         if (!muse::contains(newSounds, pair.first)) {
     241            0 :             stopListeningToProgress(pair.first);
     242              :         }
     243              :     }
     244              : 
     245            0 :     m_onlineSounds = newSounds;
     246            0 : }
     247              : 
     248            0 : void NotationRegionsBeingProcessedModel::onIsPlayingChanged()
     249              : {
     250            0 :     const OnlineSoundsShowProgressBarMode mode = configuration()->onlineSoundsShowProgressBarMode();
     251            0 :     if (mode != OnlineSoundsShowProgressBarMode::DuringPlayback) {
     252            0 :         return;
     253              :     }
     254              : 
     255            0 :     if (!isPlaying()) {
     256            0 :         m_shouldShowRegions = !m_tracksBeingProcessed.empty();
     257            0 :         return;
     258              :     }
     259              : 
     260            0 :     m_shouldShowRegions = true;
     261            0 :     setRegions(calculateRegions(m_tracksBeingProcessed));
     262              : }
     263              : 
     264            0 : void NotationRegionsBeingProcessedModel::listenViewModeChanges()
     265              : {
     266            0 :     INotationPtr notation = projectSession()->currentNotation();
     267            0 :     if (!notation) {
     268            0 :         return;
     269              :     }
     270              : 
     271            0 :     notation->viewModeChanged().onNotify(this, [this]() {
     272            0 :         setRegions(calculateRegions(m_tracksBeingProcessed));
     273            0 :     }, Asyncable::Mode::SetReplace /* FIXME */);
     274            0 : }
     275              : 
     276            0 : void NotationRegionsBeingProcessedModel::startListeningToProgress(const TrackId trackId)
     277              : {
     278            0 :     const IPlaybackController::InstrumentTrackIdMap& instrumentTrackIdMap = playbackController()->instrumentTrackIdMap();
     279            0 :     const InstrumentTrackId instrumentTrackId = muse::key(instrumentTrackIdMap, trackId);
     280            0 :     if (!instrumentTrackId.isValid()) {
     281            0 :         return;
     282              :     }
     283              : 
     284            0 :     playback()->inputProcessingProgress(trackId)
     285            0 :     .onResolve(this, [this, instrumentTrackId](InputProcessingProgress inputProgress) {
     286            0 :         if (inputProgress.isStarted) {
     287            0 :             onProgressStarted(instrumentTrackId);
     288              :         }
     289              : 
     290            0 :         inputProgress.processedChannel.onReceive(this, [this, instrumentTrackId]
     291              :                                                  (const InputProcessingProgress::StatusInfo& status,
     292              :                                                   const InputProcessingProgress::ChunkInfoList& chunks,
     293              :                                                   const InputProcessingProgress::ProgressInfo& progress)
     294              :         {
     295            0 :             switch (status.status) {
     296            0 :                 case InputProcessingProgress::Undefined:
     297            0 :                     break;
     298            0 :                 case InputProcessingProgress::Started: {
     299            0 :                     onProgressStarted(instrumentTrackId);
     300            0 :                 } break;
     301            0 :                 case InputProcessingProgress::Processing: {
     302            0 :                     if (!chunks.empty()) {
     303            0 :                         onChunksReceived(instrumentTrackId, chunks);
     304              :                     }
     305            0 :                     onProgressChanged(instrumentTrackId, progress.current);
     306            0 :                 } break;
     307            0 :                 case InputProcessingProgress::Finished: {
     308            0 :                     onProgressFinished(instrumentTrackId);
     309            0 :                 } break;
     310              :             }
     311            0 :         });
     312            0 :     });
     313            0 : }
     314              : 
     315            0 : void NotationRegionsBeingProcessedModel::stopListeningToProgress(const muse::audio::TrackId trackId)
     316              : {
     317            0 :     const IPlaybackController::InstrumentTrackIdMap& instrumentTrackIdMap = playbackController()->instrumentTrackIdMap();
     318            0 :     const InstrumentTrackId instrumentTrackId = muse::key(instrumentTrackIdMap, trackId);
     319            0 :     if (!instrumentTrackId.isValid()) {
     320            0 :         return;
     321              :     }
     322              : 
     323            0 :     onProgressFinished(instrumentTrackId);
     324            0 : }
     325              : 
     326            0 : void NotationRegionsBeingProcessedModel::onProgressStarted(const InstrumentTrackId& instrumentTrackId)
     327              : {
     328            0 :     if (!muse::contains(m_tracksBeingProcessed, instrumentTrackId)) {
     329            0 :         m_tracksBeingProcessed[instrumentTrackId] = TrackInfo();
     330              :     }
     331            0 : }
     332              : 
     333            0 : void NotationRegionsBeingProcessedModel::onChunksReceived(const InstrumentTrackId& instrumentTrackId, const ChunkInfoList& chunks)
     334              : {
     335            0 :     const notation::IMasterNotationPtr master = projectSession()->masterNotation();
     336            0 :     if (!master) {
     337            0 :         return;
     338              :     }
     339              : 
     340            0 :     auto it = m_tracksBeingProcessed.find(instrumentTrackId);
     341            0 :     if (it == m_tracksBeingProcessed.end()) {
     342            0 :         return;
     343              :     }
     344              : 
     345            0 :     TrackInfo& info = it->second;
     346            0 :     info.ranges.clear();
     347              : 
     348            0 :     bool shouldUpdate = false;
     349              : 
     350            0 :     for (const InputProcessingProgress::ChunkInfo& chunk : chunks) {
     351            0 :         TickRange range;
     352            0 :         range.tickFrom = master->playback()->secToTick(chunk.start);
     353            0 :         range.tickTo = master->playback()->secToTick(chunk.end);
     354              : 
     355            0 :         if (!muse::contains(info.ranges, range)) {
     356            0 :             info.ranges.push_back(range);
     357            0 :             shouldUpdate = true;
     358              :         }
     359              :     }
     360              : 
     361            0 :     if (!shouldUpdate) {
     362            0 :         return;
     363              :     }
     364              : 
     365            0 :     QList<RegionInfo> newRegions = m_regions;
     366            0 :     newRegions.removeIf([&instrumentTrackId](const RegionInfo& r) {
     367            0 :         return r.trackId == instrumentTrackId;
     368              :     });
     369              : 
     370            0 :     newRegions.append(calculateRegions({ { instrumentTrackId, info } }));
     371            0 :     setRegions(newRegions);
     372            0 : }
     373              : 
     374            0 : void NotationRegionsBeingProcessedModel::onProgressChanged(const InstrumentTrackId& instrumentTrackId, int progress)
     375              : {
     376            0 :     auto trackIt = m_tracksBeingProcessed.find(instrumentTrackId);
     377            0 :     if (trackIt == m_tracksBeingProcessed.end()) {
     378            0 :         return;
     379              :     }
     380              : 
     381            0 :     if (trackIt->second.progress == progress) {
     382            0 :         return;
     383              :     }
     384              : 
     385            0 :     trackIt->second.progress = progress;
     386              : 
     387            0 :     for (int i = 0; i < m_regions.size(); ++i) {
     388            0 :         const RegionInfo& region = m_regions.at(i);
     389            0 :         if (region.trackId != instrumentTrackId) {
     390            0 :             continue;
     391              :         }
     392              : 
     393            0 :         QModelIndex modelIdx = index(i);
     394            0 :         emit dataChanged(modelIdx, modelIdx, { ProgressRole });
     395              :     }
     396              : }
     397              : 
     398            0 : void NotationRegionsBeingProcessedModel::onProgressFinished(const InstrumentTrackId& instrumentTrackId)
     399              : {
     400            0 :     muse::remove(m_tracksBeingProcessed, instrumentTrackId);
     401              : 
     402            0 :     const QList<RegionInfo> regionsCopy = m_regions;
     403            0 :     const bool wasEmpty = m_regions.empty();
     404              : 
     405            0 :     for (const RegionInfo& region : regionsCopy) {
     406            0 :         if (region.trackId != instrumentTrackId) {
     407            0 :             continue;
     408              :         }
     409              : 
     410            0 :         const int idx = m_regions.indexOf(region);
     411            0 :         if (idx < 0) {
     412            0 :             continue;
     413              :         }
     414              : 
     415            0 :         beginRemoveRows(QModelIndex(), idx, idx);
     416            0 :         m_regions.removeAt(idx);
     417            0 :         endRemoveRows();
     418              :     }
     419              : 
     420            0 :     if (wasEmpty != m_regions.empty()) {
     421            0 :         initShouldShowRegions();
     422            0 :         emit isEmptyChanged();
     423              :     }
     424            0 : }
     425              : 
     426            0 : void NotationRegionsBeingProcessedModel::onViewMatrixChanged()
     427              : {
     428            0 :     if (m_regions.empty()) {
     429            0 :         return;
     430              :     }
     431              : 
     432            0 :     for (RegionInfo& region : m_regions) {
     433            0 :         region.viewRect = m_notationViewMatrix.mapRect(region.logicRect);
     434              :     }
     435              : 
     436            0 :     emit dataChanged(index(0), index(m_regions.size() - 1), { RectRole });
     437              : }
     438              : 
     439            0 : void NotationRegionsBeingProcessedModel::initShouldShowRegions()
     440              : {
     441            0 :     switch (configuration()->onlineSoundsShowProgressBarMode()) {
     442            0 :     case OnlineSoundsShowProgressBarMode::Always:
     443            0 :         m_shouldShowRegions = true;
     444            0 :         break;
     445            0 :     case OnlineSoundsShowProgressBarMode::Never:
     446            0 :         m_shouldShowRegions = false;
     447            0 :         break;
     448            0 :     case OnlineSoundsShowProgressBarMode::DuringPlayback:
     449            0 :         m_shouldShowRegions = isPlaying();
     450            0 :         break;
     451              :     }
     452            0 : }
     453              : 
     454            0 : QList<NotationRegionsBeingProcessedModel::RegionInfo> NotationRegionsBeingProcessedModel::calculateRegions(
     455              :     const TracksBeingProcessed& tracks) const
     456              : {
     457            0 :     if (!m_shouldShowRegions || tracks.empty()) {
     458            0 :         return {};
     459              :     }
     460              : 
     461            0 :     const notation::INotationPtr notation = projectSession()->currentNotation();
     462            0 :     if (!notation) {
     463            0 :         return {};
     464              :     }
     465              : 
     466            0 :     QList<RegionInfo> regions;
     467              : 
     468            0 :     for (const auto& pair : tracks) {
     469            0 :         const Part* part = notation->parts()->part(pair.first.partId);
     470            0 :         if (!part) {
     471            0 :             continue;
     472              :         }
     473              : 
     474            0 :         const std::vector<QRectF> newRects = calculateRects(part, pair.second.ranges);
     475            0 :         regions.reserve(regions.size() + newRects.size());
     476              : 
     477            0 :         for (const QRectF& rect : newRects) {
     478            0 :             RegionInfo region;
     479            0 :             region.trackId = pair.first;
     480            0 :             region.logicRect = rect;
     481            0 :             region.viewRect = m_notationViewMatrix.mapRect(rect);
     482            0 :             regions.push_back(region);
     483            0 :         }
     484            0 :     }
     485              : 
     486            0 :     return regions;
     487            0 : }
     488              : 
     489            0 : std::vector<QRectF> NotationRegionsBeingProcessedModel::calculateRects(const Part* part, const std::vector<TickRange>& ranges) const
     490              : {
     491            0 :     std::vector<QRectF> result;
     492              : 
     493            0 :     for (const System* system : part->score()->systems()) {
     494            0 :         if (system->measures().empty() || system->staves().empty()) {
     495            0 :             continue;
     496              :         }
     497              : 
     498            0 :         std::vector<QRectF> systemRects = calculateRects(part, system, ranges);
     499            0 :         result.insert(result.end(), std::make_move_iterator(systemRects.begin()),
     500              :                       std::make_move_iterator(systemRects.end()));
     501            0 :     }
     502              : 
     503            0 :     return result;
     504            0 : }
     505              : 
     506            0 : std::vector<QRectF> NotationRegionsBeingProcessedModel::calculateRects(const Part* part, const System* system,
     507              :                                                                        const std::vector<TickRange>& ranges) const
     508              : {
     509            0 :     const staff_idx_t staffIdx = system->firstVisibleSysStaffOfPart(part);
     510            0 :     const SysStaff* sysStaff = system->staff(staffIdx);
     511            0 :     if (!sysStaff) {
     512            0 :         return {};
     513              :     }
     514              : 
     515            0 :     const Score* score = system->score();
     516            0 :     const Measure* lastMeasure = score->lastMeasureMM();
     517            0 :     const double lastMeasureEndX = lastMeasure ? lastMeasure->canvasPos().x() + lastMeasure->width() : 0.0;
     518              : 
     519            0 :     std::vector<QRectF> result;
     520              : 
     521            0 :     for (const TickRange& range : ranges) {
     522            0 :         if (system->last()->tick().ticks() < range.tickFrom) {
     523            0 :             continue;
     524              :         }
     525              : 
     526            0 :         if (system->first()->tick().ticks() > range.tickTo) {
     527            0 :             continue;
     528              :         }
     529              : 
     530            0 :         const muse::PointF systemPos = system->canvasPos();
     531            0 :         QRectF logicRect = sysStaff->bbox().translated(systemPos).toQRectF();
     532              : 
     533            0 :         const Segment* segmentFrom = findSegmentFrom(score, system, range.tickFrom, staffIdx);
     534            0 :         if (segmentFrom) {
     535            0 :             const double segmentFromStartX = segmentFrom->canvasPos().x();
     536            0 :             const double startX = std::max(segmentFromStartX, logicRect.x());
     537            0 :             logicRect.setLeft(startX);
     538              :         } else {
     539            0 :             const double firstNoteRestX = systemPos.x() + system->firstNoteRestSegmentX();
     540            0 :             logicRect.setLeft(firstNoteRestX);
     541              :         }
     542              : 
     543            0 :         const Segment* segmentTo = findSegmentTo(score, system, segmentFrom, range.tickTo, staffIdx);
     544            0 :         if (segmentTo) {
     545            0 :             const double segmentToEndX = segmentTo->canvasPos().x() + segmentTo->width();
     546            0 :             const double endX = std::min(systemPos.x() + system->width(), segmentToEndX);
     547            0 :             logicRect.setRight(endX);
     548            0 :         } else if (lastMeasure) {
     549            0 :             const double width = std::min(logicRect.width(), lastMeasureEndX - logicRect.x());
     550            0 :             logicRect.setWidth(width);
     551              :         }
     552              : 
     553              :         // Merge with overlapping rects
     554            0 :         for (auto it = result.begin(); it != result.end();) {
     555            0 :             if (logicRect.intersects(*it)) {
     556            0 :                 logicRect = logicRect.united(*it);
     557            0 :                 it = result.erase(it);
     558              :             } else {
     559            0 :                 ++it;
     560              :             }
     561              :         }
     562              : 
     563            0 :         result.push_back(logicRect);
     564              :     }
     565              : 
     566            0 :     return result;
     567            0 : }
        

Generated by: LCOV version 2.0-1