<commit_before>/*****************************************************************************
  coverageBed.cpp

  (c) 2009 - Aaron Quinlan
  Hall Laboratory
  Department of Biochemistry and Molecular Genetics
  University of Virginia
  aaronquinlan@gmail.com

  Licenced under the GNU General Public License 2.0 license.
******************************************************************************/
#include "lineFileUtilities.h"
#include "coverageBed.h"

// build
BedCoverage::BedCoverage(string &bedAFile, string &bedBFile, bool sameStrand, bool diffStrand,
                         bool writeHistogram, bool bamInput, bool obeySplits,
                         bool eachBase, bool countsOnly) {

    _bedAFile       = bedAFile;
    _bedBFile       = bedBFile;

    _bedA           = new BedFile(bedAFile);
    _bedB           = new BedFile(bedBFile);

    _sameStrand     = sameStrand;
    _diffStrand     = diffStrand;
    _obeySplits     = obeySplits;
    _eachBase       = eachBase;
    _writeHistogram = writeHistogram;
    _bamInput       = bamInput;
    _countsOnly     = countsOnly;

    if (_bamInput == false)
        CollectCoverageBed();
    else
        CollectCoverageBam(_bedA->bedFile);
}

// destroy
BedCoverage::~BedCoverage(void) {
    delete _bedA;
    delete _bedB;
}

void BedCoverage::CollectCoverageBed() {

    // load the "B" bed file into a map so
    // that we can easily compare "A" to it for overlaps
    _bedB->loadBedCovFileIntoMap();

    BED a;
    _bedA->Open();
    // process each entry in A
    while (_bedA->GetNextBed(a)) {
        if (_bedA->_status == BED_VALID) {
            // process the BED entry as a single block
            if (_obeySplits == false)
                _bedB->countHits(a, _sameStrand, _diffStrand, _countsOnly);
            // split the BED into discrete blocksand process each independently.
            else {
                bedVector bedBlocks;
                GetBedBlocks(a, bedBlocks);
                // use countSplitHits to avoid over-counting each split chunk
                // as distinct read coverage.
                _bedB->countSplitHits(bedBlocks, _sameStrand, _diffStrand, _countsOnly);
            }
        }
    }
    _bedA->Close();

    // report the coverage (summary or histogram) for BED B.
    if (_countsOnly == true)
        ReportCounts();
    else
        ReportCoverage();
}

void BedCoverage::CollectCoverageBam(string bamFile) {

    // load the "B" bed file into a map so
    // that we can easily compare "A" to it for overlaps
    _bedB->loadBedCovFileIntoMap();

    // open the BAM file
    BamReader reader;
    if (!reader.Open(bamFile)) {
        cerr << "Failed to open BAM file " << bamFile << endl;
        exit(1);
    }
    // get header & reference information
    string header = reader.GetHeaderText();
    RefVector refs = reader.GetReferenceData();

    // convert each aligned BAM entry to BED
    // and compute coverage on B
    BamAlignment bam;
    while (reader.GetNextAlignment(bam)) {
        if (bam.IsMapped()) {
            // treat the BAM alignment as a single "block"
            if (_obeySplits == false) {
                // construct a new BED entry from the current BAM alignment.
                BED a;

                try
                {
                    a.chrom  = refs.at(bam.RefID).RefName;
                    a.start  = bam.Position;
                    a.end    = bam.GetEndPosition(false, false);
                    a.strand = "+";
                    if (bam.IsReverseStrand()) a.strand = "-";

                    _bedB->countHits(a, _sameStrand, _diffStrand, _countsOnly);
                }
                catch (out_of_range& oor)
                {
                  cerr << bam.Name
                       << " is said to be mapped (0x4 == false), yet the chrom is missing.  Skipping."
                       << endl;
                }
            }
            // split the BAM alignment into discrete blocks and
            // look for overlaps only within each block.
            else {
                // vec to store the discrete BED "blocks" from a
                bedVector bedBlocks;
                // since we are counting coverage, we do want to split blocks when a
                // deletion (D) CIGAR op is encountered (hence the true for the last parm)
                try
                {
                    string chrom  = refs.at(bam.RefID).RefName;
                    GetBamBlocks(bam, chrom, bedBlocks, false, true);
                    // use countSplitHits to avoid over-counting each split chunk
                    // as distinct read coverage.
                    _bedB->countSplitHits(bedBlocks, _sameStrand, _diffStrand, _countsOnly);
                }
                catch (out_of_range& oor)
                {
                  cerr << bam.Name
                       << " is said to be mapped (0x4 == false), yet the chrom is missing.  Skipping."
                       << endl;
                }
            }
        }
    }
    // report the coverage (summary or histogram) for BED B.
    if (_countsOnly == true)
        ReportCounts();
    else
        ReportCoverage();
    // close the BAM file
    reader.Close();
}

void BedCoverage::ReportCounts() {

    // process each chromosome
    masterBedCovMap::const_iterator chromItr = _bedB->bedCovMap.begin();
    masterBedCovMap::const_iterator chromEnd = _bedB->bedCovMap.end();
    for (; chromItr != chromEnd; ++chromItr)
    {
        // for each chrom, process each bin
        binsToBedCovs::const_iterator binItr = chromItr->second.begin();
        binsToBedCovs::const_iterator binEnd = chromItr->second.end();
        for (; binItr != binEnd; ++binItr)
        {
            // for each chrom & bin, compute and report
            // the observed coverage for each feature
            vector<BEDCOV>::const_iterator bedItr = binItr->second.begin();
            vector<BEDCOV>::const_iterator bedEnd = binItr->second.end();
            for (; bedItr != bedEnd; ++bedItr)
            {
                _bedB->reportBedTab(*bedItr);
                printf("%d\n", bedItr->count);
            }
        }
    }
}

void BedCoverage::ReportCoverage() {

    map<unsigned int, unsigned int> allDepthHist;
    unsigned int totalLength = 0;

    // process each chromosome
    masterBedCovMap::const_iterator chromItr = _bedB->bedCovMap.begin();
    masterBedCovMap::const_iterator chromEnd = _bedB->bedCovMap.end();
    for (; chromItr != chromEnd; ++chromItr)
    {
        // for each chrom, process each bin
        binsToBedCovs::const_iterator binItr = chromItr->second.begin();
        binsToBedCovs::const_iterator binEnd = chromItr->second.end();
        for (; binItr != binEnd; ++binItr)
        {
            // for each chrom & bin, compute and report
            // the observed coverage for each feature
            vector<BEDCOV>::const_iterator bedItr = binItr->second.begin();
            vector<BEDCOV>::const_iterator bedEnd = binItr->second.end();
            for (; bedItr != bedEnd; ++bedItr)
            {
                int zeroDepthCount = 0; // number of bases with zero depth
                int depth          = 0; // tracks the depth at the current base

                // the start is either the first base in the feature OR
                // the leftmost position of an overlapping feature. e.g. (s = start):
                // A    ----------
                // B    s    ------------
                int start          = min(bedItr->minOverlapStart, bedItr->start);

                // track the number of bases in the feature covered by
                // 0, 1, 2, ... n features in A
                map<unsigned int, unsigned int> depthHist;
                map<unsigned int, DEPTH>::const_iterator depthItr;

                // compute the coverage observed at each base in the feature marching from start to end.
                for (CHRPOS pos = start+1; pos <= bedItr->end; pos++)
                {
                    // map pointer grabbing the starts and ends observed at this position
                    depthItr = bedItr->depthMap.find(pos);
                    // increment coverage if starts observed at this position.
                    if (depthItr != bedItr->depthMap.end())
                        depth += depthItr->second.starts;
                    // update coverage assuming the current position is within the current B feature
                    if ((pos > bedItr->start) && (pos <= bedItr->end)) {
                        if (depth == 0) zeroDepthCount++;
                        // update our histograms, assuming we are not reporting "per-base" coverage.
                        if (_eachBase == false) {
                            depthHist[depth]++;
                            allDepthHist[depth]++;
                        }
                        else if ((_eachBase == true) && (bedItr->zeroLength == false))
                        {
                            _bedB->reportBedTab(*bedItr);
                            printf("%d\t%d\n", pos-bedItr->start, depth);
                        }
                    }
                    // decrement coverage if ends observed at this position.
                    if (depthItr != bedItr->depthMap.end())
                        depth = depth - depthItr->second.ends;
                }

                // handle the special case where the user wants "per-base" depth
                // but the current feature is length = 0.
                if ((_eachBase == true) && (bedItr->zeroLength == true)) {
                    _bedB->reportBedTab(*bedItr);
                    printf("1\t%d\n",depth);
                }
                // Summarize the coverage for the current interval,
                // assuming the user has not requested "per-base" coverage.
                else if (_eachBase == false)
                {
                    CHRPOS length     = bedItr->end - bedItr->start;
                    if (bedItr->zeroLength == true) {
                        length = 0;
                    }
                    totalLength       += length;
                    int nonZeroBases   = (length - zeroDepthCount);

                    float fractCovered = 0.0;
                    if (bedItr->zeroLength == false) {
                        fractCovered = (float) nonZeroBases / length;
                    }

                    // print a summary of the coverage
                    if (_writeHistogram == false) {
                        _bedB->reportBedTab(*bedItr);
                        printf("%d\t%d\t%d\t%0.7f\n", bedItr->count, nonZeroBases, length, fractCovered);
                    }
                    // HISTOGRAM
                    // report the number of bases with coverage == x
                    else {
                        // produce a histogram when not a zero length feature.
                        if (bedItr->zeroLength == false) {
                            map<unsigned int, unsigned int>::const_iterator histItr = depthHist.begin();
                            map<unsigned int, unsigned int>::const_iterator histEnd = depthHist.end();
                            for (; histItr != histEnd; ++histItr)
                            {
                                float fractAtThisDepth = (float) histItr->second / length;
                                _bedB->reportBedTab(*bedItr);
                                printf("%d\t%d\t%d\t%0.7f\n", histItr->first, histItr->second, length, fractAtThisDepth);
                            }
                        }
                        // special case when it is a zero length feauture.
                        else {
                            _bedB->reportBedTab(*bedItr);
                            printf("%d\t%d\t%d\t%0.7f\n", bedItr->count, 0, 0, 1.0000000);
                        }
                    }
                }
            }
        }
    }
    // report a histogram of coverage among _all_
    // features in B.
    if (_writeHistogram == true) {
        map<unsigned int, unsigned int>::const_iterator histItr = allDepthHist.begin();
        map<unsigned int, unsigned int>::const_iterator histEnd = allDepthHist.end();
        for (; histItr != histEnd; ++histItr) {
            float fractAtThisDepth = (float) histItr->second / totalLength;
            printf("all\t%d\t%d\t%d\t%0.7f\n", histItr->first, histItr->second, totalLength, fractAtThisDepth);
        }
    }
}

<commit_msg>avoid -hist overflow<commit_after>/*****************************************************************************
  coverageBed.cpp

  (c) 2009 - Aaron Quinlan
  Hall Laboratory
  Department of Biochemistry and Molecular Genetics
  University of Virginia
  aaronquinlan@gmail.com

  Licenced under the GNU General Public License 2.0 license.
******************************************************************************/
#include "lineFileUtilities.h"
#include "coverageBed.h"

// build
BedCoverage::BedCoverage(string &bedAFile, string &bedBFile, bool sameStrand, bool diffStrand,
                         bool writeHistogram, bool bamInput, bool obeySplits,
                         bool eachBase, bool countsOnly) {

    _bedAFile       = bedAFile;
    _bedBFile       = bedBFile;

    _bedA           = new BedFile(bedAFile);
    _bedB           = new BedFile(bedBFile);

    _sameStrand     = sameStrand;
    _diffStrand     = diffStrand;
    _obeySplits     = obeySplits;
    _eachBase       = eachBase;
    _writeHistogram = writeHistogram;
    _bamInput       = bamInput;
    _countsOnly     = countsOnly;

    if (_bamInput == false)
        CollectCoverageBed();
    else
        CollectCoverageBam(_bedA->bedFile);
}

// destroy
BedCoverage::~BedCoverage(void) {
    delete _bedA;
    delete _bedB;
}

void BedCoverage::CollectCoverageBed() {

    // load the "B" bed file into a map so
    // that we can easily compare "A" to it for overlaps
    _bedB->loadBedCovFileIntoMap();

    BED a;
    _bedA->Open();
    // process each entry in A
    while (_bedA->GetNextBed(a)) {
        if (_bedA->_status == BED_VALID) {
            // process the BED entry as a single block
            if (_obeySplits == false)
                _bedB->countHits(a, _sameStrand, _diffStrand, _countsOnly);
            // split the BED into discrete blocksand process each independently.
            else {
                bedVector bedBlocks;
                GetBedBlocks(a, bedBlocks);
                // use countSplitHits to avoid over-counting each split chunk
                // as distinct read coverage.
                _bedB->countSplitHits(bedBlocks, _sameStrand, _diffStrand, _countsOnly);
            }
        }
    }
    _bedA->Close();

    // report the coverage (summary or histogram) for BED B.
    if (_countsOnly == true)
        ReportCounts();
    else
        ReportCoverage();
}

void BedCoverage::CollectCoverageBam(string bamFile) {

    // load the "B" bed file into a map so
    // that we can easily compare "A" to it for overlaps
    _bedB->loadBedCovFileIntoMap();

    // open the BAM file
    BamReader reader;
    if (!reader.Open(bamFile)) {
        cerr << "Failed to open BAM file " << bamFile << endl;
        exit(1);
    }
    // get header & reference information
    string header = reader.GetHeaderText();
    RefVector refs = reader.GetReferenceData();

    // convert each aligned BAM entry to BED
    // and compute coverage on B
    BamAlignment bam;
    while (reader.GetNextAlignment(bam)) {
        if (bam.IsMapped()) {
            // treat the BAM alignment as a single "block"
            if (_obeySplits == false) {
                // construct a new BED entry from the current BAM alignment.
                BED a;

                try
                {
                    a.chrom  = refs.at(bam.RefID).RefName;
                    a.start  = bam.Position;
                    a.end    = bam.GetEndPosition(false, false);
                    a.strand = "+";
                    if (bam.IsReverseStrand()) a.strand = "-";

                    _bedB->countHits(a, _sameStrand, _diffStrand, _countsOnly);
                }
                catch (out_of_range& oor)
                {
                  cerr << bam.Name
                       << " is said to be mapped (0x4 == false), yet the chrom is missing.  Skipping."
                       << endl;
                }
            }
            // split the BAM alignment into discrete blocks and
            // look for overlaps only within each block.
            else {
                // vec to store the discrete BED "blocks" from a
                bedVector bedBlocks;
                // since we are counting coverage, we do want to split blocks when a
                // deletion (D) CIGAR op is encountered (hence the true for the last parm)
                try
                {
                    string chrom  = refs.at(bam.RefID).RefName;
                    GetBamBlocks(bam, chrom, bedBlocks, false, true);
                    // use countSplitHits to avoid over-counting each split chunk
                    // as distinct read coverage.
                    _bedB->countSplitHits(bedBlocks, _sameStrand, _diffStrand, _countsOnly);
                }
                catch (out_of_range& oor)
                {
                  cerr << bam.Name
                       << " is said to be mapped (0x4 == false), yet the chrom is missing.  Skipping."
                       << endl;
                }
            }
        }
    }
    // report the coverage (summary or histogram) for BED B.
    if (_countsOnly == true)
        ReportCounts();
    else
        ReportCoverage();
    // close the BAM file
    reader.Close();
}

void BedCoverage::ReportCounts() {

    // process each chromosome
    masterBedCovMap::const_iterator chromItr = _bedB->bedCovMap.begin();
    masterBedCovMap::const_iterator chromEnd = _bedB->bedCovMap.end();
    for (; chromItr != chromEnd; ++chromItr)
    {
        // for each chrom, process each bin
        binsToBedCovs::const_iterator binItr = chromItr->second.begin();
        binsToBedCovs::const_iterator binEnd = chromItr->second.end();
        for (; binItr != binEnd; ++binItr)
        {
            // for each chrom & bin, compute and report
            // the observed coverage for each feature
            vector<BEDCOV>::const_iterator bedItr = binItr->second.begin();
            vector<BEDCOV>::const_iterator bedEnd = binItr->second.end();
            for (; bedItr != bedEnd; ++bedItr)
            {
                _bedB->reportBedTab(*bedItr);
                printf("%d\n", bedItr->count);
            }
        }
    }
}

void BedCoverage::ReportCoverage() {

    map<unsigned long, unsigned long> allDepthHist;
    unsigned long totalLength = 0;

    // process each chromosome
    masterBedCovMap::const_iterator chromItr = _bedB->bedCovMap.begin();
    masterBedCovMap::const_iterator chromEnd = _bedB->bedCovMap.end();
    for (; chromItr != chromEnd; ++chromItr)
    {
        // for each chrom, process each bin
        binsToBedCovs::const_iterator binItr = chromItr->second.begin();
        binsToBedCovs::const_iterator binEnd = chromItr->second.end();
        for (; binItr != binEnd; ++binItr)
        {
            // for each chrom & bin, compute and report
            // the observed coverage for each feature
            vector<BEDCOV>::const_iterator bedItr = binItr->second.begin();
            vector<BEDCOV>::const_iterator bedEnd = binItr->second.end();
            for (; bedItr != bedEnd; ++bedItr)
            {
                int zeroDepthCount = 0; // number of bases with zero depth
                int depth          = 0; // tracks the depth at the current base

                // the start is either the first base in the feature OR
                // the leftmost position of an overlapping feature. e.g. (s = start):
                // A    ----------
                // B    s    ------------
                int start          = min(bedItr->minOverlapStart, bedItr->start);

                // track the number of bases in the feature covered by
                // 0, 1, 2, ... n features in A
                map<unsigned int, unsigned int> depthHist;
                map<unsigned int, DEPTH>::const_iterator depthItr;

                // compute the coverage observed at each base in the feature marching from start to end.
                for (CHRPOS pos = start+1; pos <= bedItr->end; pos++)
                {
                    // map pointer grabbing the starts and ends observed at this position
                    depthItr = bedItr->depthMap.find(pos);
                    // increment coverage if starts observed at this position.
                    if (depthItr != bedItr->depthMap.end())
                        depth += depthItr->second.starts;
                    // update coverage assuming the current position is within the current B feature
                    if ((pos > bedItr->start) && (pos <= bedItr->end)) {
                        if (depth == 0) zeroDepthCount++;
                        // update our histograms, assuming we are not reporting "per-base" coverage.
                        if (_eachBase == false) {
                            depthHist[depth]++;
                            allDepthHist[depth]++;
                        }
                        else if ((_eachBase == true) && (bedItr->zeroLength == false))
                        {
                            _bedB->reportBedTab(*bedItr);
                            printf("%d\t%d\n", pos-bedItr->start, depth);
                        }
                    }
                    // decrement coverage if ends observed at this position.
                    if (depthItr != bedItr->depthMap.end())
                        depth = depth - depthItr->second.ends;
                }

                // handle the special case where the user wants "per-base" depth
                // but the current feature is length = 0.
                if ((_eachBase == true) && (bedItr->zeroLength == true)) {
                    _bedB->reportBedTab(*bedItr);
                    printf("1\t%d\n",depth);
                }
                // Summarize the coverage for the current interval,
                // assuming the user has not requested "per-base" coverage.
                else if (_eachBase == false)
                {
                    CHRPOS length     = bedItr->end - bedItr->start;
                    if (bedItr->zeroLength == true) {
                        length = 0;
                    }
                    totalLength       += length;
                    int nonZeroBases   = (length - zeroDepthCount);

                    float fractCovered = 0.0;
                    if (bedItr->zeroLength == false) {
                        fractCovered = (float) nonZeroBases / length;
                    }

                    // print a summary of the coverage
                    if (_writeHistogram == false) {
                        _bedB->reportBedTab(*bedItr);
                        printf("%d\t%d\t%d\t%0.7f\n", bedItr->count, nonZeroBases, length, fractCovered);
                    }
                    // HISTOGRAM
                    // report the number of bases with coverage == x
                    else {
                        // produce a histogram when not a zero length feature.
                        if (bedItr->zeroLength == false) {
                            map<unsigned int, unsigned int>::const_iterator histItr = depthHist.begin();
                            map<unsigned int, unsigned int>::const_iterator histEnd = depthHist.end();
                            for (; histItr != histEnd; ++histItr)
                            {
                                float fractAtThisDepth = (float) histItr->second / length;
                                _bedB->reportBedTab(*bedItr);
                                printf("%d\t%d\t%d\t%0.7f\n", histItr->first, histItr->second, length, fractAtThisDepth);
                            }
                        }
                        // special case when it is a zero length feauture.
                        else {
                            _bedB->reportBedTab(*bedItr);
                            printf("%d\t%d\t%d\t%0.7f\n", bedItr->count, 0, 0, 1.0000000);
                        }
                    }
                }
            }
        }
    }
    // report a histogram of coverage among _all_
    // features in B.
    if (_writeHistogram == true) {
        map<unsigned long, unsigned long>::const_iterator histItr = allDepthHist.begin();
        map<unsigned long, unsigned long>::const_iterator histEnd = allDepthHist.end();
        for (; histItr != histEnd; ++histItr) {
            float fractAtThisDepth = (float) histItr->second / totalLength;
            printf("all\t%lu\t%lu\t%lu\t%0.7f\n", histItr->first, histItr->second, totalLength, fractAtThisDepth);
        }
    }
}

<|endoftext|>


<commit_before>/*
 * DesktopMain.cpp
 *
 * Copyright (C) 2009-17 by RStudio, Inc.
 *
 * Unless you have received this program directly from RStudio pursuant
 * to the terms of a commercial license agreement with RStudio, then
 * this program is licensed to you under the terms of version 3 of the
 * GNU Affero General Public License. This program is distributed WITHOUT
 * ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT,
 * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the
 * AGPL (http://www.gnu.org/licenses/agpl-3.0.txt) for more details.
 *
 */

#include <QtGui>
#include <QPushButton>

#include <boost/bind.hpp>
#include <boost/function.hpp>
#include <boost/scoped_ptr.hpp>

#include <core/Log.hpp>

#include <core/system/FileScanner.hpp>
#include <core/Error.hpp>
#include <core/FileInfo.hpp>
#include <core/FilePath.hpp>
#include <core/SafeConvert.hpp>
#include <core/StringUtils.hpp>
#include <core/system/System.hpp>
#include <core/system/Environment.hpp>
#include <core/r_util/RProjectFile.hpp>
#include <core/r_util/RUserData.hpp>

#include "DesktopApplicationLaunch.hpp"
#include "DesktopSlotBinders.hpp"
#include "DesktopDetectRHome.hpp"
#include "DesktopOptions.hpp"
#include "DesktopUtils.hpp"
#include "DesktopSessionLauncher.hpp"
#include "DesktopProgressActivator.hpp"
#include "DesktopNetworkProxyFactory.hpp"

QProcess* pRSessionProcess;
QString sharedSecret;

using namespace rstudio;
using namespace rstudio::core;
using namespace rstudio::desktop;

namespace {

void initializeSharedSecret()
{
   sharedSecret = QString::number(rand())
                  + QString::number(rand())
                  + QString::number(rand());
   std::string value = sharedSecret.toUtf8().constData();
   core::system::setenv("RS_SHARED_SECRET", value);
}

void initializeWorkingDirectory(int argc,
                                char* argv[],
                                const QString& filename)
{
   // bail if we've already got a working directory as a result of
   // a call to openSessionInNewWindow
   if (!core::system::getenv(kRStudioInitialWorkingDir).empty())
      return;

   // calculate what our initial working directory should be
   std::string workingDir;

   // if there is a filename passed to us then use it's path
   if (filename != QString())
   {
      FilePath filePath(filename.toUtf8().constData());
      if (filePath.exists())
      {
         if (filePath.isDirectory())
            workingDir = filePath.absolutePath();
         else
            workingDir = filePath.parent().absolutePath();
      }
   }

   // do additinal detection if necessary
   if (workingDir.empty())
   {
      // get current path
      FilePath currentPath = FilePath::safeCurrentPath(
                                       core::system::userHomePath());

#if defined(_WIN32) || defined(__APPLE__)

      // detect whether we were launched from the system application menu
      // (e.g. Dock, Program File icon, etc.). we do this by checking
      // whether the executable path is within the current path. if we
      // weren't launched from the system app menu that set the initial
      // wd to the current path

      FilePath exePath;
      Error error = core::system::executablePath(argv[0], &exePath);
      if (!error)
      {
         if (!exePath.isWithin(currentPath))
            workingDir = currentPath.absolutePath();
      }
      else
      {
         LOG_ERROR(error);
      }

#else

      // on linux we take the current working dir if we were launched
      // from within a terminal
      if (core::system::stdoutIsTerminal() &&
         (currentPath != core::system::userHomePath()))
      {
         workingDir = currentPath.absolutePath();
      }

#endif

   }

   // set the working dir if we have one
   if (!workingDir.empty())
      core::system::setenv(kRStudioInitialWorkingDir, workingDir);
}

void setInitialProject(const FilePath& projectFile, QString* pFilename)
{
   core::system::setenv(kRStudioInitialProject, projectFile.absolutePath());
   pFilename->clear();
}

void initializeStartupEnvironment(QString* pFilename)
{
   // if the filename ends with .RData or .rda then this is an
   // environment file. if it ends with .Rproj then it is
   // a project file. we handle both cases by setting an environment
   // var and then resetting the pFilename so it isn't processed
   // using the standard open file logic
   FilePath filePath(pFilename->toUtf8().constData());
   if (filePath.exists())
   {
      std::string ext = filePath.extensionLowerCase();

      // if it is a directory or just an .rdata file then we can see
      // whether there is a project file we can automatically attach to
      if (filePath.isDirectory())
      {
         FilePath projectFile = r_util::projectFromDirectory(filePath);
         if (!projectFile.empty())
         {
            setInitialProject(projectFile, pFilename);
         }
      }
      else if (ext == ".rproj")
      {
         setInitialProject(filePath, pFilename);
      }
      else if (ext == ".rdata" || ext == ".rda")
      {
         core::system::setenv(kRStudioInitialEnvironment, filePath.absolutePath());
         pFilename->clear();
      }

   }
}

QString verifyAndNormalizeFilename(const QString &filename)
{
   if (filename.isNull() || filename.isEmpty())
      return QString();

   QFileInfo fileInfo(filename);
   if (fileInfo.exists())
      return fileInfo.absoluteFilePath();
   else
      return QString();
}

bool isNonProjectFilename(const QString &filename)
{
   if (filename.isNull() || filename.isEmpty())
      return false;

   FilePath filePath(filename.toUtf8().constData());
   return filePath.exists() && filePath.extensionLowerCase() != ".rproj";
}

} // anonymous namespace

int main(int argc, char* argv[])
{
   // enable debug console
   // TODO: better way of selecting a port?
   qputenv("QTWEBENGINE_REMOTE_DEBUGGING", QByteArrayLiteral("59009"));

   core::system::initHook();

   try
   {
      initializeLang();

      // initialize log
      core::system::initializeLog("rdesktop",
                                  core::system::kLogLevelWarning,
                                  desktop::userLogPath());

      // ignore SIGPIPE
      Error error = core::system::ignoreSignal(core::system::SigPipe);
      if (error)
         LOG_ERROR(error);

      // set application attributes
      QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);

#ifndef NDEBUG
      // disable web security for development builds (so we can
      // get access to sourcemaps)
      static std::vector<char*> arguments(argv, argv + argc);
      static char disableWebSecurity[] = "--disable-web-security";
      arguments.push_back(disableWebSecurity);
      argc = (int) arguments.size();
      argv = &arguments[0];
#endif

      boost::scoped_ptr<QApplication> pApp;
      boost::scoped_ptr<ApplicationLaunch> pAppLaunch;
      ApplicationLaunch::init(QString::fromUtf8("RStudio"),
                              argc,
                              argv,
                              &pApp,
                              &pAppLaunch);

      // determine the filename that was passed to us
      QString filename;
#ifdef __APPLE__
      // get filename from OpenFile apple-event (pump to ensure delivery)
      pApp->processEvents();
      filename = verifyAndNormalizeFilename(
                              pAppLaunch->startupOpenFileRequest());
#endif
      // allow all platforms (including OSX) to check the command line.
      // we include OSX because the way Qt handles apple events is to
      // re-route them to the first instance to register for events. in
      // this case (for projects) we use this to initiate a launch
      // of the application with the project filename on the command line
      if (filename.isEmpty())
      {
         // get filename from command line arguments
         if (pApp->arguments().size() > 1)
         {
            QString arg = pApp->arguments().last();
            if (arg != QString::fromUtf8(kRunDiagnosticsOption))
               filename = verifyAndNormalizeFilename(arg);
         }
      }

      // if we have a filename and it is NOT a project file then see
      // if we can open it within an existing instance
      if (isNonProjectFilename(filename))
      {
         if (pAppLaunch->sendMessage(filename))
            return 0;
      }
      else
      {
         // try to register ourselves as a peer for others
         pAppLaunch->attemptToRegisterPeer();
      }

      // init options from command line
      desktop::options().initFromCommandLine(pApp->arguments());

      // reset log if we are in run-diagnostics mode
      if (desktop::options().runDiagnostics())
      {
         desktop::reattachConsoleIfNecessary();
         initializeStderrLog("rdesktop", core::system::kLogLevelWarning);
      }

      initializeSharedSecret();
      initializeWorkingDirectory(argc, argv, filename);
      initializeStartupEnvironment(&filename);

      Options& options = desktop::options();
      if (!prepareEnvironment(options))
         return 1;

      // get install path
      FilePath installPath;
      error = core::system::installPath("..", argv[0], &installPath);
      if (error)
      {
         LOG_ERROR(error);
         return EXIT_FAILURE;
      }

#ifdef _WIN32
      RVersion version = detectRVersion(false);
#endif

      // calculate paths to config file, rsession, and desktop scripts
      FilePath confPath, sessionPath, scriptsPath;
      bool devMode = false;

      // check for debug configuration
      FilePath currentPath = FilePath::safeCurrentPath(installPath);
      if (currentPath.complete("conf/rdesktop-dev.conf").exists())
      {
         confPath = currentPath.complete("conf/rdesktop-dev.conf");
         sessionPath = currentPath.complete("session/rsession");
         scriptsPath = currentPath.complete("desktop");
         devMode = true;
#ifdef _WIN32
         if (version.architecture() == ArchX64)
            sessionPath = installPath.complete("session/x64/rsession");
#endif
      }

      // if there is no conf path then release mode
      if (confPath.empty())
      {
         // default paths (then tweak)
         sessionPath = installPath.complete("bin/rsession");
         scriptsPath = installPath.complete("bin");

         // check for win64 binary on windows
#ifdef _WIN32
         if (version.architecture() == ArchX64)
            sessionPath = installPath.complete("bin/x64/rsession");
#endif

         // check for running in a bundle on OSX
#ifdef __APPLE__
         if (installPath.complete("Info.plist").exists())
         {
            sessionPath = installPath.complete("MacOS/rsession");
            scriptsPath = installPath.complete("MacOS");
         }
#endif
      }
      core::system::fixupExecutablePath(&sessionPath);

      auto* pProxyFactory = new NetworkProxyFactory();
      QNetworkProxyFactory::setApplicationProxyFactory(pProxyFactory);

      // set the scripts path in options
      desktop::options().setScriptsPath(scriptsPath);

      // launch session
      SessionLauncher sessionLauncher(sessionPath, confPath, filename, pAppLaunch.get());
      sessionLauncher.launchFirstSession(installPath, devMode, pApp->arguments());

      ProgressActivator progressActivator;

      int result = pApp->exec();

      options.cleanUpScratchTempDir();

      return result;
   }
   CATCH_UNEXPECTED_EXCEPTION
}

#ifdef _WIN32
int WINAPI WinMain(HINSTANCE hInstance,
                   HINSTANCE hPrevInstance,
                   LPSTR lpCmdLine,
                   int nShowCmd)
{
   return main(__argc, __argv);
}
#endif
<commit_msg>disable accessibilty rendering on macos<commit_after>/*
 * DesktopMain.cpp
 *
 * Copyright (C) 2009-17 by RStudio, Inc.
 *
 * Unless you have received this program directly from RStudio pursuant
 * to the terms of a commercial license agreement with RStudio, then
 * this program is licensed to you under the terms of version 3 of the
 * GNU Affero General Public License. This program is distributed WITHOUT
 * ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT,
 * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the
 * AGPL (http://www.gnu.org/licenses/agpl-3.0.txt) for more details.
 *
 */

#include <QtGui>
#include <QPushButton>

#include <boost/bind.hpp>
#include <boost/function.hpp>
#include <boost/scoped_ptr.hpp>

#include <core/Log.hpp>

#include <core/system/FileScanner.hpp>
#include <core/Error.hpp>
#include <core/FileInfo.hpp>
#include <core/FilePath.hpp>
#include <core/SafeConvert.hpp>
#include <core/StringUtils.hpp>
#include <core/system/System.hpp>
#include <core/system/Environment.hpp>
#include <core/r_util/RProjectFile.hpp>
#include <core/r_util/RUserData.hpp>

#include "DesktopApplicationLaunch.hpp"
#include "DesktopSlotBinders.hpp"
#include "DesktopDetectRHome.hpp"
#include "DesktopOptions.hpp"
#include "DesktopUtils.hpp"
#include "DesktopSessionLauncher.hpp"
#include "DesktopProgressActivator.hpp"
#include "DesktopNetworkProxyFactory.hpp"

QProcess* pRSessionProcess;
QString sharedSecret;

using namespace rstudio;
using namespace rstudio::core;
using namespace rstudio::desktop;

namespace {

void initializeSharedSecret()
{
   sharedSecret = QString::number(rand())
                  + QString::number(rand())
                  + QString::number(rand());
   std::string value = sharedSecret.toUtf8().constData();
   core::system::setenv("RS_SHARED_SECRET", value);
}

void initializeWorkingDirectory(int argc,
                                char* argv[],
                                const QString& filename)
{
   // bail if we've already got a working directory as a result of
   // a call to openSessionInNewWindow
   if (!core::system::getenv(kRStudioInitialWorkingDir).empty())
      return;

   // calculate what our initial working directory should be
   std::string workingDir;

   // if there is a filename passed to us then use it's path
   if (filename != QString())
   {
      FilePath filePath(filename.toUtf8().constData());
      if (filePath.exists())
      {
         if (filePath.isDirectory())
            workingDir = filePath.absolutePath();
         else
            workingDir = filePath.parent().absolutePath();
      }
   }

   // do additinal detection if necessary
   if (workingDir.empty())
   {
      // get current path
      FilePath currentPath = FilePath::safeCurrentPath(
                                       core::system::userHomePath());

#if defined(_WIN32) || defined(__APPLE__)

      // detect whether we were launched from the system application menu
      // (e.g. Dock, Program File icon, etc.). we do this by checking
      // whether the executable path is within the current path. if we
      // weren't launched from the system app menu that set the initial
      // wd to the current path

      FilePath exePath;
      Error error = core::system::executablePath(argv[0], &exePath);
      if (!error)
      {
         if (!exePath.isWithin(currentPath))
            workingDir = currentPath.absolutePath();
      }
      else
      {
         LOG_ERROR(error);
      }

#else

      // on linux we take the current working dir if we were launched
      // from within a terminal
      if (core::system::stdoutIsTerminal() &&
         (currentPath != core::system::userHomePath()))
      {
         workingDir = currentPath.absolutePath();
      }

#endif

   }

   // set the working dir if we have one
   if (!workingDir.empty())
      core::system::setenv(kRStudioInitialWorkingDir, workingDir);
}

void setInitialProject(const FilePath& projectFile, QString* pFilename)
{
   core::system::setenv(kRStudioInitialProject, projectFile.absolutePath());
   pFilename->clear();
}

void initializeStartupEnvironment(QString* pFilename)
{
   // if the filename ends with .RData or .rda then this is an
   // environment file. if it ends with .Rproj then it is
   // a project file. we handle both cases by setting an environment
   // var and then resetting the pFilename so it isn't processed
   // using the standard open file logic
   FilePath filePath(pFilename->toUtf8().constData());
   if (filePath.exists())
   {
      std::string ext = filePath.extensionLowerCase();

      // if it is a directory or just an .rdata file then we can see
      // whether there is a project file we can automatically attach to
      if (filePath.isDirectory())
      {
         FilePath projectFile = r_util::projectFromDirectory(filePath);
         if (!projectFile.empty())
         {
            setInitialProject(projectFile, pFilename);
         }
      }
      else if (ext == ".rproj")
      {
         setInitialProject(filePath, pFilename);
      }
      else if (ext == ".rdata" || ext == ".rda")
      {
         core::system::setenv(kRStudioInitialEnvironment, filePath.absolutePath());
         pFilename->clear();
      }

   }
}

QString verifyAndNormalizeFilename(const QString &filename)
{
   if (filename.isNull() || filename.isEmpty())
      return QString();

   QFileInfo fileInfo(filename);
   if (fileInfo.exists())
      return fileInfo.absoluteFilePath();
   else
      return QString();
}

bool isNonProjectFilename(const QString &filename)
{
   if (filename.isNull() || filename.isEmpty())
      return false;

   FilePath filePath(filename.toUtf8().constData());
   return filePath.exists() && filePath.extensionLowerCase() != ".rproj";
}

} // anonymous namespace

int main(int argc, char* argv[])
{
   // enable debug console
   // TODO: better way of selecting a port?
   qputenv("QTWEBENGINE_REMOTE_DEBUGGING", QByteArrayLiteral("59009"));

   core::system::initHook();

   try
   {
      initializeLang();

      // initialize log
      core::system::initializeLog("rdesktop",
                                  core::system::kLogLevelWarning,
                                  desktop::userLogPath());

      // ignore SIGPIPE
      Error error = core::system::ignoreSignal(core::system::SigPipe);
      if (error)
         LOG_ERROR(error);

      // set application attributes
      QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);

      // prepare command line arguments
      static std::vector<char*> arguments(argv, argv + argc);

#ifndef NDEBUG
      // disable web security for development builds (so we can
      // get access to sourcemaps)
      static char disableWebSecurity[] = "--disable-web-security";
      arguments.push_back(disableWebSecurity);
#endif

      // disable chromium renderer accessibility (it causes slowdown
      // when used in conjunction with some applications; see e.g.
      // https://github.com/rstudio/rstudio/issues/1990)
      static char disableRendererAccessibility[] = "--disable-renderer-accessibility";
      arguments.push_back(disableRendererAccessibility);

      // re-assign command line arguments
      argc = (int) arguments.size();
      argv = &arguments[0];

      // prepare application for launch
      boost::scoped_ptr<QApplication> pApp;
      boost::scoped_ptr<ApplicationLaunch> pAppLaunch;
      ApplicationLaunch::init(QString::fromUtf8("RStudio"),
                              argc,
                              argv,
                              &pApp,
                              &pAppLaunch);

      // determine the filename that was passed to us
      QString filename;
#ifdef __APPLE__
      // get filename from OpenFile apple-event (pump to ensure delivery)
      pApp->processEvents();
      filename = verifyAndNormalizeFilename(
                              pAppLaunch->startupOpenFileRequest());
#endif
      // allow all platforms (including OSX) to check the command line.
      // we include OSX because the way Qt handles apple events is to
      // re-route them to the first instance to register for events. in
      // this case (for projects) we use this to initiate a launch
      // of the application with the project filename on the command line
      if (filename.isEmpty())
      {
         // get filename from command line arguments
         if (pApp->arguments().size() > 1)
         {
            QString arg = pApp->arguments().last();
            if (arg != QString::fromUtf8(kRunDiagnosticsOption))
               filename = verifyAndNormalizeFilename(arg);
         }
      }

      // if we have a filename and it is NOT a project file then see
      // if we can open it within an existing instance
      if (isNonProjectFilename(filename))
      {
         if (pAppLaunch->sendMessage(filename))
            return 0;
      }
      else
      {
         // try to register ourselves as a peer for others
         pAppLaunch->attemptToRegisterPeer();
      }

      // init options from command line
      desktop::options().initFromCommandLine(pApp->arguments());

      // reset log if we are in run-diagnostics mode
      if (desktop::options().runDiagnostics())
      {
         desktop::reattachConsoleIfNecessary();
         initializeStderrLog("rdesktop", core::system::kLogLevelWarning);
      }

      initializeSharedSecret();
      initializeWorkingDirectory(argc, argv, filename);
      initializeStartupEnvironment(&filename);

      Options& options = desktop::options();
      if (!prepareEnvironment(options))
         return 1;

      // get install path
      FilePath installPath;
      error = core::system::installPath("..", argv[0], &installPath);
      if (error)
      {
         LOG_ERROR(error);
         return EXIT_FAILURE;
      }

#ifdef _WIN32
      RVersion version = detectRVersion(false);
#endif

      // calculate paths to config file, rsession, and desktop scripts
      FilePath confPath, sessionPath, scriptsPath;
      bool devMode = false;

      // check for debug configuration
      FilePath currentPath = FilePath::safeCurrentPath(installPath);
      if (currentPath.complete("conf/rdesktop-dev.conf").exists())
      {
         confPath = currentPath.complete("conf/rdesktop-dev.conf");
         sessionPath = currentPath.complete("session/rsession");
         scriptsPath = currentPath.complete("desktop");
         devMode = true;
#ifdef _WIN32
         if (version.architecture() == ArchX64)
            sessionPath = installPath.complete("session/x64/rsession");
#endif
      }

      // if there is no conf path then release mode
      if (confPath.empty())
      {
         // default paths (then tweak)
         sessionPath = installPath.complete("bin/rsession");
         scriptsPath = installPath.complete("bin");

         // check for win64 binary on windows
#ifdef _WIN32
         if (version.architecture() == ArchX64)
            sessionPath = installPath.complete("bin/x64/rsession");
#endif

         // check for running in a bundle on OSX
#ifdef __APPLE__
         if (installPath.complete("Info.plist").exists())
         {
            sessionPath = installPath.complete("MacOS/rsession");
            scriptsPath = installPath.complete("MacOS");
         }
#endif
      }
      core::system::fixupExecutablePath(&sessionPath);

      auto* pProxyFactory = new NetworkProxyFactory();
      QNetworkProxyFactory::setApplicationProxyFactory(pProxyFactory);

      // set the scripts path in options
      desktop::options().setScriptsPath(scriptsPath);

      // launch session
      SessionLauncher sessionLauncher(sessionPath, confPath, filename, pAppLaunch.get());
      sessionLauncher.launchFirstSession(installPath, devMode, pApp->arguments());

      ProgressActivator progressActivator;

      int result = pApp->exec();

      options.cleanUpScratchTempDir();

      return result;
   }
   CATCH_UNEXPECTED_EXCEPTION
}

#ifdef _WIN32
int WINAPI WinMain(HINSTANCE hInstance,
                   HINSTANCE hPrevInstance,
                   LPSTR lpCmdLine,
                   int nShowCmd)
{
   return main(__argc, __argv);
}
#endif
<|endoftext|>


<commit_before><commit_msg>INTEGRATION: CWS changefileheader (1.13.16); FILE MERGED 2008/04/01 12:27:25 thb 1.13.16.2: #i85898# Stripping all external header guards 2008/03/31 12:22:45 rt 1.13.16.1: #i87441# Change license header to LPGL v3.<commit_after><|endoftext|>


<commit_before>#include "QtApplicationMachineAutomationEventFilter.h"
#include "QtMachineAutomationEvent.h"

using namespace sequanto::automation;

QtApplicationMachineAutomationEventFilter::QtApplicationMachineAutomationEventFilter ( QObject * _parent )
   : QObject(_parent)
{
}

QByteArray ToByteArray ( QWidget * _widget )
{
   return QByteArray ( (const char*) &_widget, sizeof(QWidget*) );
}

bool QtApplicationMachineAutomationEventFilter::eventFilter ( QObject * _object, QEvent * _event )
{
   if ( _event->type() == QtMachineAutomationEvent::ID )
   {
      QtMachineAutomationEvent * event = dynamic_cast<QtMachineAutomationEvent*>(_event);
      event->received();

      if ( event->command() == QtMachineAutomationEvent::DESKTOP0 )
      {
         event->done ( ToByteArray(QApplication::desktop()) );
      }
      else
      {
         QWidget * widget = event->widget();
         if ( widget != NULL )
         {
            switch ( event->command() )
            {
            case QtMachineAutomationEvent::NAME:
#ifdef QT_NO_ACCESSIBILITY
               event->done ( widget->objectName() );
#else
               if ( widget->accessibleName().isEmpty() )
               {
                  event->done ( widget->objectName() );
               }
               else
               {
                  event->done ( widget->accessibleName() );
               }
#endif
               break;

            case QtMachineAutomationEvent::ROLE:
               if ( widget->isWindow() )
               {
                  event->done ( "window" );
               }
               else if ( qobject_cast<QCheckBox*>(widget) != NULL )
               {
                  event->done ( "checkbox" );
               }
               else if ( qobject_cast<QRadioButton*>(widget) != NULL )
               {
                  event->done ( "radiobutton" );
               }
               else if ( qobject_cast<QAbstractButton*>(widget) != NULL )
               {
                  event->done ( "button" );
               }
               else if ( qobject_cast<QAbstractSlider*>(widget) != NULL )
               {
                  event->done ( "slider" );
               }
               else if ( qobject_cast<QComboBox*>(widget) != NULL )
               {
                  event->done ( "combobox" );
               }
               else if ( qobject_cast<QSpinBox*>(widget) != NULL )
               {
                  event->done ( "spinbox" );
               }
               else if ( qobject_cast<QCalendarWidget*>(widget) != NULL )
               {
                  event->done ( "calendar" );
               }
               else if ( qobject_cast<QDialog*>(widget) != NULL )
               {
                  event->done ( "dialog" );
               }
               else if ( qobject_cast<QDialogButtonBox*>(widget) != NULL ||
                  qobject_cast<QGroupBox*>(widget) != NULL )
               {
                  event->done ( "group" );
               }
               else if ( qobject_cast<QLabel*>(widget) != NULL )
               {
                  event->done ( "label" );
               }
               else if ( qobject_cast<QSplitter*>(widget) != NULL )
               {
                  event->done ( "splitter" );
               }
               else if ( qobject_cast<QLineEdit*>(widget) != NULL )
               {
                  event->done ( "text" );
               }
               else if ( qobject_cast<QMdiSubWindow*>(widget) != NULL )
               {
                  event->done ( "window" );
               }
               else if ( qobject_cast<QMenu*>(widget) != NULL )
               {
                  event->done ( "menu" );
               }
               else if ( qobject_cast<QMenuBar*>(widget) != NULL )
               {
                  event->done ( "menubar" );
               }
               else if ( qobject_cast<QProgressBar*>(widget) != NULL )
               {
                  event->done ( "progress" );
               }
               else if ( qobject_cast<QStatusBar*>(widget) != NULL )
               {
                  event->done ( "statusbar" );
               }
               else if ( qobject_cast<QTabBar*>(widget) != NULL )
               {
                  event->done ( "tabs" );
               }
               else if ( qobject_cast<QTabWidget*>(widget) != NULL )
               {
                  event->done ( "tabs" );
               }
               else if ( qobject_cast<QToolBar*>(widget) != NULL )
               {
                  event->done ( "toolbar" );
               }
               /*
               else if ( qobject_cast<*>(widget) != NULL )
               {
               event->done ( "" );
               }
               */
               else
               {
                  event->done ( widget->metaObject()->className() );
               }
               break;

            case QtMachineAutomationEvent::TEXT:
               if ( widget->isWindow() )
               {
                  event->done ( widget->windowTitle() );
               }
               else if ( qobject_cast<QAbstractButton*>(widget) != NULL )
               {
                  event->done( qobject_cast<QAbstractButton*>(widget)->text() );
               }
               else if ( qobject_cast<QAbstractSlider*>(widget) != NULL )
               {
                  event->done ( QString(qobject_cast<QAbstractSlider*>(widget)->value()) );
               }
               else if ( qobject_cast<QComboBox*>(widget) != NULL )
               {
                  event->done ( qobject_cast<QComboBox*>(widget)->currentText() );
               }
               else if ( qobject_cast<QSpinBox*>(widget) != NULL )
               {
                  event->done ( QString(qobject_cast<QSpinBox*>(widget)->value()) );
               }
               else if ( qobject_cast<QCalendarWidget*>(widget) != NULL )
               {
                  event->done ( qobject_cast<QCalendarWidget*>(widget)->selectedDate().toString() );
               }
               else if ( qobject_cast<QDialog*>(widget) != NULL )
               {
                  event->done( qobject_cast<QDialog*>(widget)->windowTitle() );
               }
               else if ( qobject_cast<QGroupBox*>(widget) != NULL )
               {
                  event->done ( qobject_cast<QGroupBox*>(widget)->title() );
               }
               else if ( qobject_cast<QLabel*>(widget) != NULL )
               {
                  event->done ( qobject_cast<QLabel*>(widget)->text() );
               }
               else if ( qobject_cast<QSplitter*>(widget) != NULL )
               {
                  event->done ( QString(qobject_cast<QSplitter*>(widget)->orientation()) );
               }
               else if ( qobject_cast<QLineEdit*>(widget) != NULL )
               {
                  event->done ( qobject_cast<QLineEdit*>(widget)->text() );
               }
               else if ( qobject_cast<QMdiSubWindow*>(widget) != NULL )
               {
                  event->done( qobject_cast<QMdiSubWindow*>(widget)->windowTitle() );
               }
               else if ( qobject_cast<QMenu*>(widget) != NULL )
               {
                  event->done ( qobject_cast<QMenu*>(widget)->title() );
               }
               else if ( qobject_cast<QProgressBar*>(widget) != NULL )
               {
                  QProgressBar * progressBar = qobject_cast<QProgressBar*>(widget);
                  event->done ( progressBar->text() + progressBar->value() );
               }
               else if ( qobject_cast<QStatusBar*>(widget) != NULL )
               {
                  event->done ( qobject_cast<QStatusBar*>(widget)->currentMessage() );
               }
               else if ( qobject_cast<QTabBar*>(widget) != NULL )
               {
                  event->done ( QString(qobject_cast<QTabBar*>(widget)->count()) );
               }
               else if ( qobject_cast<QTabWidget*>(widget) != NULL )
               {
                  event->done ( QString(qobject_cast<QTabWidget*>(widget)->count()) );
               }
               else
               {
                  event->done ( "" );
               }
               break;

            case QtMachineAutomationEvent::X:
            case QtMachineAutomationEvent::Y:
               {
                  QPoint pos = widget->mapToGlobal(QPoint(0,0));
                  /*
                  QWidget * window = widget->window();
                  QPoint windowTopLeft ( window->geometry().topLeft() );
                  pos -= windowTopLeft;
                  */

                  if ( event->command() == QtMachineAutomationEvent::X )
                  {
                     event->done ( pos.x() );
                  }
                  else
                  {
                     event->done ( pos.y() );
                  }
               }
               break;

            case QtMachineAutomationEvent::WIDTH:
               event->done ( widget->width() );
               break;

            case QtMachineAutomationEvent::HEIGHT:
               event->done ( widget->height() );
               break;

            case QtMachineAutomationEvent::CHILD:
            case QtMachineAutomationEvent::CHILDREN:
               {
                  QWidgetList list;
                  if ( widget == QApplication::desktop() )
                  {
                     QWidgetList topLevel ( QApplication::topLevelWidgets() );
                     for ( QWidgetList::const_iterator it = topLevel.constBegin(); it != topLevel.constEnd(); it++ )
                     {
                         if ( (*it)->isVisible() )
                         {
                             list.append ( *it );
                         }
                     }
                  }
                  else
                  {
                     for ( QObjectList::const_iterator it = widget->children().constBegin(); it != widget->children().constEnd(); it++ )
                     {
                        if ( (*it)->isWidgetType() )
                        {
                           QWidget * childWidget = qobject_cast<QWidget*>(*it);
                           if ( childWidget->isVisible() )
                           {
                               list.append ( childWidget );
                           }
                        }
                     }
                  }
                  if ( event->command() == QtMachineAutomationEvent::CHILDREN)
                  {
                     event->done(list.count());
                  }
                  else
                  {
                     int index = event->index();
                     for ( int i = 0; i < list.count(); i++ )
                     {
                        if ( i == index )
                        {
                           event->done ( ToByteArray(list.at(i)) );
                           index = -1;
                        }
                     }
                     if ( index == -1 )
                     {
                        event->done ( QVariant() );
                     }
                  }
                  break;
               }
            case QtMachineAutomationEvent::ACTIONS:
               event->done ( widget->actions().length() );
               break;

            case QtMachineAutomationEvent::ACTION_NAME:
               if ( event->index() < widget->actions().length() )
               {
                  event->done ( widget->actions().at(event->index())->text());
               }
               else
               {
                  event->done ( QVariant() );
               }
               break;

            case QtMachineAutomationEvent::ACTION_DO:
               if ( event->index() < widget->actions().length() )
               {
                  widget->actions().at(event->index())->trigger();
               }
               event->done ( QVariant() );
               break;

            default:
                event->done ( QVariant() );
                break;
            }
         }
         else
         {
            event->done ( QVariant() );
         }
      }
      return true;
   }
   else if ( _event->type() == QtMachineAutomationMouseEvent::ID )
   {
      QtMachineAutomationMouseEvent * event = dynamic_cast<QtMachineAutomationMouseEvent*>(_event);
      QWidget * receiver = QApplication::widgetAt(event->position());
      if ( receiver != NULL )
      {
         QPoint widgetPos = receiver->mapFromGlobal ( event->position() );

         QApplication::postEvent ( receiver, new QMouseEvent( QEvent::MouseButtonPress, widgetPos, event->position(), event->button(), event->button(), Qt::NoModifier ) );
         QApplication::postEvent ( receiver, new QMouseEvent( QEvent::MouseButtonRelease, widgetPos, event->position(), event->button(), event->button(), Qt::NoModifier ) );
      }
      return true;
   }
   return QObject::eventFilter(_object, _event );
}

QtApplicationMachineAutomationEventFilter::~QtApplicationMachineAutomationEventFilter ()
{
}
<commit_msg>Problem, we did not call event->done(...) for CHILD events when the child index was not found.<commit_after>#include "QtApplicationMachineAutomationEventFilter.h"
#include "QtMachineAutomationEvent.h"

using namespace sequanto::automation;

QtApplicationMachineAutomationEventFilter::QtApplicationMachineAutomationEventFilter ( QObject * _parent )
   : QObject(_parent)
{
}

QByteArray ToByteArray ( QWidget * _widget )
{
   return QByteArray ( (const char*) &_widget, sizeof(QWidget*) );
}

bool QtApplicationMachineAutomationEventFilter::eventFilter ( QObject * _object, QEvent * _event )
{
   if ( _event->type() == QtMachineAutomationEvent::ID )
   {
      QtMachineAutomationEvent * event = dynamic_cast<QtMachineAutomationEvent*>(_event);
      event->received();

      if ( event->command() == QtMachineAutomationEvent::DESKTOP0 )
      {
         event->done ( ToByteArray(QApplication::desktop()) );
      }
      else
      {
         QWidget * widget = event->widget();
         if ( widget != NULL )
         {
            switch ( event->command() )
            {
            case QtMachineAutomationEvent::NAME:
#ifdef QT_NO_ACCESSIBILITY
               event->done ( widget->objectName() );
#else
               if ( widget->accessibleName().isEmpty() )
               {
                  event->done ( widget->objectName() );
               }
               else
               {
                  event->done ( widget->accessibleName() );
               }
#endif
               break;

            case QtMachineAutomationEvent::ROLE:
               if ( widget->isWindow() )
               {
                  event->done ( "window" );
               }
               else if ( qobject_cast<QCheckBox*>(widget) != NULL )
               {
                  event->done ( "checkbox" );
               }
               else if ( qobject_cast<QRadioButton*>(widget) != NULL )
               {
                  event->done ( "radiobutton" );
               }
               else if ( qobject_cast<QAbstractButton*>(widget) != NULL )
               {
                  event->done ( "button" );
               }
               else if ( qobject_cast<QAbstractSlider*>(widget) != NULL )
               {
                  event->done ( "slider" );
               }
               else if ( qobject_cast<QComboBox*>(widget) != NULL )
               {
                  event->done ( "combobox" );
               }
               else if ( qobject_cast<QSpinBox*>(widget) != NULL )
               {
                  event->done ( "spinbox" );
               }
               else if ( qobject_cast<QCalendarWidget*>(widget) != NULL )
               {
                  event->done ( "calendar" );
               }
               else if ( qobject_cast<QDialog*>(widget) != NULL )
               {
                  event->done ( "dialog" );
               }
               else if ( qobject_cast<QDialogButtonBox*>(widget) != NULL ||
                  qobject_cast<QGroupBox*>(widget) != NULL )
               {
                  event->done ( "group" );
               }
               else if ( qobject_cast<QLabel*>(widget) != NULL )
               {
                  event->done ( "label" );
               }
               else if ( qobject_cast<QSplitter*>(widget) != NULL )
               {
                  event->done ( "splitter" );
               }
               else if ( qobject_cast<QLineEdit*>(widget) != NULL )
               {
                  event->done ( "text" );
               }
               else if ( qobject_cast<QMdiSubWindow*>(widget) != NULL )
               {
                  event->done ( "window" );
               }
               else if ( qobject_cast<QMenu*>(widget) != NULL )
               {
                  event->done ( "menu" );
               }
               else if ( qobject_cast<QMenuBar*>(widget) != NULL )
               {
                  event->done ( "menubar" );
               }
               else if ( qobject_cast<QProgressBar*>(widget) != NULL )
               {
                  event->done ( "progress" );
               }
               else if ( qobject_cast<QStatusBar*>(widget) != NULL )
               {
                  event->done ( "statusbar" );
               }
               else if ( qobject_cast<QTabBar*>(widget) != NULL )
               {
                  event->done ( "tabs" );
               }
               else if ( qobject_cast<QTabWidget*>(widget) != NULL )
               {
                  event->done ( "tabs" );
               }
               else if ( qobject_cast<QToolBar*>(widget) != NULL )
               {
                  event->done ( "toolbar" );
               }
               /*
               else if ( qobject_cast<*>(widget) != NULL )
               {
               event->done ( "" );
               }
               */
               else
               {
                  event->done ( widget->metaObject()->className() );
               }
               break;

            case QtMachineAutomationEvent::TEXT:
               if ( widget->isWindow() )
               {
                  event->done ( widget->windowTitle() );
               }
               else if ( qobject_cast<QAbstractButton*>(widget) != NULL )
               {
                  event->done( qobject_cast<QAbstractButton*>(widget)->text() );
               }
               else if ( qobject_cast<QAbstractSlider*>(widget) != NULL )
               {
                  event->done ( QString(qobject_cast<QAbstractSlider*>(widget)->value()) );
               }
               else if ( qobject_cast<QComboBox*>(widget) != NULL )
               {
                  event->done ( qobject_cast<QComboBox*>(widget)->currentText() );
               }
               else if ( qobject_cast<QSpinBox*>(widget) != NULL )
               {
                  event->done ( QString(qobject_cast<QSpinBox*>(widget)->value()) );
               }
               else if ( qobject_cast<QCalendarWidget*>(widget) != NULL )
               {
                  event->done ( qobject_cast<QCalendarWidget*>(widget)->selectedDate().toString() );
               }
               else if ( qobject_cast<QDialog*>(widget) != NULL )
               {
                  event->done( qobject_cast<QDialog*>(widget)->windowTitle() );
               }
               else if ( qobject_cast<QGroupBox*>(widget) != NULL )
               {
                  event->done ( qobject_cast<QGroupBox*>(widget)->title() );
               }
               else if ( qobject_cast<QLabel*>(widget) != NULL )
               {
                  event->done ( qobject_cast<QLabel*>(widget)->text() );
               }
               else if ( qobject_cast<QSplitter*>(widget) != NULL )
               {
                  event->done ( QString(qobject_cast<QSplitter*>(widget)->orientation()) );
               }
               else if ( qobject_cast<QLineEdit*>(widget) != NULL )
               {
                  event->done ( qobject_cast<QLineEdit*>(widget)->text() );
               }
               else if ( qobject_cast<QMdiSubWindow*>(widget) != NULL )
               {
                  event->done( qobject_cast<QMdiSubWindow*>(widget)->windowTitle() );
               }
               else if ( qobject_cast<QMenu*>(widget) != NULL )
               {
                  event->done ( qobject_cast<QMenu*>(widget)->title() );
               }
               else if ( qobject_cast<QProgressBar*>(widget) != NULL )
               {
                  QProgressBar * progressBar = qobject_cast<QProgressBar*>(widget);
                  event->done ( progressBar->text() + progressBar->value() );
               }
               else if ( qobject_cast<QStatusBar*>(widget) != NULL )
               {
                  event->done ( qobject_cast<QStatusBar*>(widget)->currentMessage() );
               }
               else if ( qobject_cast<QTabBar*>(widget) != NULL )
               {
                  event->done ( QString(qobject_cast<QTabBar*>(widget)->count()) );
               }
               else if ( qobject_cast<QTabWidget*>(widget) != NULL )
               {
                  event->done ( QString(qobject_cast<QTabWidget*>(widget)->count()) );
               }
               else
               {
                  event->done ( "" );
               }
               break;

            case QtMachineAutomationEvent::X:
            case QtMachineAutomationEvent::Y:
               {
                  QPoint pos = widget->mapToGlobal(QPoint(0,0));
                  /*
                  QWidget * window = widget->window();
                  QPoint windowTopLeft ( window->geometry().topLeft() );
                  pos -= windowTopLeft;
                  */

                  if ( event->command() == QtMachineAutomationEvent::X )
                  {
                     event->done ( pos.x() );
                  }
                  else
                  {
                     event->done ( pos.y() );
                  }
               }
               break;

            case QtMachineAutomationEvent::WIDTH:
               event->done ( widget->width() );
               break;

            case QtMachineAutomationEvent::HEIGHT:
               event->done ( widget->height() );
               break;

            case QtMachineAutomationEvent::CHILD:
            case QtMachineAutomationEvent::CHILDREN:
               {
                  QWidgetList list;
                  if ( widget == QApplication::desktop() )
                  {
                     QWidgetList topLevel ( QApplication::topLevelWidgets() );
                     for ( QWidgetList::const_iterator it = topLevel.constBegin(); it != topLevel.constEnd(); it++ )
                     {
                         if ( (*it)->isVisible() )
                         {
                             list.append ( *it );
                         }
                     }
                  }
                  else
                  {
                     for ( QObjectList::const_iterator it = widget->children().constBegin(); it != widget->children().constEnd(); it++ )
                     {
                        if ( (*it)->isWidgetType() )
                        {
                           QWidget * childWidget = qobject_cast<QWidget*>(*it);
                           if ( childWidget->isVisible() )
                           {
                               list.append ( childWidget );
                           }
                        }
                     }
                  }
                  if ( event->command() == QtMachineAutomationEvent::CHILDREN)
                  {
                     event->done(list.count());
                  }
                  else
                  {
                     int index = event->index();
                     for ( int i = 0; i < list.count(); i++ )
                     {
                        if ( i == index )
                        {
                           event->done ( ToByteArray(list.at(i)) );
                           index = -1;
                        }
                     }
                     if ( index != -1 )
                     {
                        event->done ( QVariant() );
                     }
                  }
                  break;
               }
            case QtMachineAutomationEvent::ACTIONS:
               event->done ( widget->actions().length() );
               break;

            case QtMachineAutomationEvent::ACTION_NAME:
               if ( event->index() < widget->actions().length() )
               {
                  event->done ( widget->actions().at(event->index())->text());
               }
               else
               {
                  event->done ( QVariant() );
               }
               break;

            case QtMachineAutomationEvent::ACTION_DO:
               if ( event->index() < widget->actions().length() )
               {
                  widget->actions().at(event->index())->trigger();
               }
               event->done ( QVariant() );
               break;

            default:
                event->done ( QVariant() );
                break;
            }
         }
         else
         {
            event->done ( QVariant() );
         }
      }
      return true;
   }
   else if ( _event->type() == QtMachineAutomationMouseEvent::ID )
   {
      QtMachineAutomationMouseEvent * event = dynamic_cast<QtMachineAutomationMouseEvent*>(_event);
      QWidget * receiver = QApplication::widgetAt(event->position());
      if ( receiver != NULL )
      {
         QPoint widgetPos = receiver->mapFromGlobal ( event->position() );

         QApplication::postEvent ( receiver, new QMouseEvent( QEvent::MouseButtonPress, widgetPos, event->position(), event->button(), event->button(), Qt::NoModifier ) );
         QApplication::postEvent ( receiver, new QMouseEvent( QEvent::MouseButtonRelease, widgetPos, event->position(), event->button(), event->button(), Qt::NoModifier ) );
      }
      return true;
   }
   return QObject::eventFilter(_object, _event );
}

QtApplicationMachineAutomationEventFilter::~QtApplicationMachineAutomationEventFilter ()
{
}
<|endoftext|>


<commit_before>// Copyright (c) the JPEG XL Project Authors. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

#include "lib/jxl/dec_patch_dictionary.h"

#include <stdint.h>
#include <stdlib.h>
#include <sys/types.h>

#include <algorithm>
#include <random>
#include <string>
#include <tuple>
#include <utility>
#include <vector>

#include "lib/jxl/ans_params.h"
#include "lib/jxl/base/compiler_specific.h"
#include "lib/jxl/base/override.h"
#include "lib/jxl/base/status.h"
#include "lib/jxl/blending.h"
#include "lib/jxl/chroma_from_luma.h"
#include "lib/jxl/color_management.h"
#include "lib/jxl/common.h"
#include "lib/jxl/dec_ans.h"
#include "lib/jxl/dec_frame.h"
#include "lib/jxl/entropy_coder.h"
#include "lib/jxl/frame_header.h"
#include "lib/jxl/image.h"
#include "lib/jxl/image_bundle.h"
#include "lib/jxl/image_ops.h"
#include "lib/jxl/patch_dictionary_internal.h"

namespace jxl {

constexpr size_t kMaxPatches = 1 << 24;

Status PatchDictionary::Decode(BitReader* br, size_t xsize, size_t ysize,
                               bool* uses_extra_channels) {
  positions_.clear();
  std::vector<uint8_t> context_map;
  ANSCode code;
  JXL_RETURN_IF_ERROR(
      DecodeHistograms(br, kNumPatchDictionaryContexts, &code, &context_map));
  ANSSymbolReader decoder(&code, br);

  auto read_num = [&](size_t context) {
    size_t r = decoder.ReadHybridUint(context, br, context_map);
    return r;
  };

  size_t num_ref_patch = read_num(kNumRefPatchContext);
  // TODO(veluca): does this make sense?
  if (num_ref_patch > kMaxPatches) {
    return JXL_FAILURE("Too many patches in dictionary");
  }

  size_t total_patches = 0;
  size_t next_size = 1;

  for (size_t id = 0; id < num_ref_patch; id++) {
    PatchReferencePosition ref_pos;
    ref_pos.ref = read_num(kReferenceFrameContext);
    if (ref_pos.ref >= kMaxNumReferenceFrames ||
        shared_->reference_frames[ref_pos.ref].frame->xsize() == 0) {
      return JXL_FAILURE("Invalid reference frame ID");
    }
    if (!shared_->reference_frames[ref_pos.ref].ib_is_in_xyb) {
      return JXL_FAILURE(
          "Patches cannot use frames saved post color transforms");
    }
    const ImageBundle& ib = *shared_->reference_frames[ref_pos.ref].frame;
    ref_pos.x0 = read_num(kPatchReferencePositionContext);
    ref_pos.y0 = read_num(kPatchReferencePositionContext);
    ref_pos.xsize = read_num(kPatchSizeContext) + 1;
    ref_pos.ysize = read_num(kPatchSizeContext) + 1;
    if (ref_pos.x0 + ref_pos.xsize > ib.xsize()) {
      return JXL_FAILURE("Invalid position specified in reference frame");
    }
    if (ref_pos.y0 + ref_pos.ysize > ib.ysize()) {
      return JXL_FAILURE("Invalid position specified in reference frame");
    }
    size_t id_count = read_num(kPatchCountContext) + 1;
    total_patches += id_count;
    if (total_patches > kMaxPatches) {
      return JXL_FAILURE("Too many patches in dictionary");
    }
    if (next_size < total_patches) {
      next_size *= 2;
      next_size = std::min<size_t>(next_size, kMaxPatches);
    }
    positions_.reserve(next_size);
    for (size_t i = 0; i < id_count; i++) {
      PatchPosition pos;
      pos.ref_pos = ref_pos;
      if (i == 0) {
        pos.x = read_num(kPatchPositionContext);
        pos.y = read_num(kPatchPositionContext);
      } else {
        pos.x =
            positions_.back().x + UnpackSigned(read_num(kPatchOffsetContext));
        pos.y =
            positions_.back().y + UnpackSigned(read_num(kPatchOffsetContext));
      }
      if (pos.x + ref_pos.xsize > xsize) {
        return JXL_FAILURE("Invalid patch x: at %zu + %zu > %zu", pos.x,
                           ref_pos.xsize, xsize);
      }
      if (pos.y + ref_pos.ysize > ysize) {
        return JXL_FAILURE("Invalid patch y: at %zu + %zu > %zu", pos.y,
                           ref_pos.ysize, ysize);
      }
      for (size_t i = 0; i < shared_->metadata->m.extra_channel_info.size() + 1;
           i++) {
        uint32_t blend_mode = read_num(kPatchBlendModeContext);
        if (blend_mode >= uint32_t(PatchBlendMode::kNumBlendModes)) {
          return JXL_FAILURE("Invalid patch blend mode: %u", blend_mode);
        }
        PatchBlending info;
        info.mode = static_cast<PatchBlendMode>(blend_mode);
        if (UsesAlpha(info.mode)) {
          *uses_extra_channels = true;
        }
        if (info.mode != PatchBlendMode::kNone && i > 0) {
          *uses_extra_channels = true;
        }
        if (UsesAlpha(info.mode) &&
            shared_->metadata->m.extra_channel_info.size() > 1) {
          info.alpha_channel = read_num(kPatchAlphaChannelContext);
          if (info.alpha_channel >=
              shared_->metadata->m.extra_channel_info.size()) {
            return JXL_FAILURE(
                "Invalid alpha channel for blending: %u out of %u\n",
                info.alpha_channel,
                (uint32_t)shared_->metadata->m.extra_channel_info.size());
          }
        } else {
          info.alpha_channel = 0;
        }
        if (UsesClamp(info.mode)) {
          info.clamp = read_num(kPatchClampContext);
        } else {
          info.clamp = false;
        }
        pos.blending.push_back(info);
      }
      positions_.push_back(std::move(pos));
    }
  }
  positions_.shrink_to_fit();

  if (!decoder.CheckANSFinalState()) {
    return JXL_FAILURE("ANS checksum failure.");
  }
  if (!HasAny()) {
    return JXL_FAILURE("Decoded patch dictionary but got none");
  }

  ComputePatchCache();
  return true;
}

int PatchDictionary::GetReferences() const {
  int result = 0;
  for (size_t i = 0; i < positions_.size(); ++i) {
    result |= (1 << static_cast<int>(positions_[i].ref_pos.ref));
  }
  return result;
}

void PatchDictionary::ComputePatchCache() {
  patch_starts_.clear();
  sorted_patches_.clear();
  if (positions_.empty()) return;
  std::vector<std::pair<size_t, size_t>> sorted_patches_y;
  for (size_t i = 0; i < positions_.size(); i++) {
    const PatchPosition& pos = positions_[i];
    for (size_t y = pos.y; y < pos.y + pos.ref_pos.ysize; y++) {
      sorted_patches_y.emplace_back(y, i);
    }
  }
  // The relative order of patches that affect the same pixels is preserved.
  // This is important for patches that have a blend mode different from kAdd.
  std::sort(sorted_patches_y.begin(), sorted_patches_y.end());
  patch_starts_.resize(sorted_patches_y.back().first + 2,
                       sorted_patches_y.size());
  sorted_patches_.resize(sorted_patches_y.size());
  for (size_t i = 0; i < sorted_patches_y.size(); i++) {
    sorted_patches_[i] = sorted_patches_y[i].second;
    patch_starts_[sorted_patches_y[i].first] =
        std::min(patch_starts_[sorted_patches_y[i].first], i);
  }
  for (size_t i = patch_starts_.size() - 1; i > 0; i--) {
    patch_starts_[i - 1] = std::min(patch_starts_[i], patch_starts_[i - 1]);
  }
}

Status PatchDictionary::AddTo(Image3F* opsin, const Rect& opsin_rect,
                              float* const* extra_channels,
                              const Rect& image_rect) const {
  JXL_CHECK(SameSize(opsin_rect, image_rect));
  size_t num_ec = shared_->metadata->m.num_extra_channels;
  std::vector<const float*> fg_ptrs(3 + num_ec);
  std::vector<float*> bg_ptrs(3 + num_ec);
  for (size_t y = image_rect.y0(); y < image_rect.y0() + image_rect.ysize();
       y++) {
    if (y + 1 >= patch_starts_.size()) continue;
    for (size_t id = patch_starts_[y]; id < patch_starts_[y + 1]; id++) {
      const PatchPosition& pos = positions_[sorted_patches_[id]];
      size_t by = pos.y;
      size_t bx = pos.x;
      size_t xsize = pos.ref_pos.xsize;
      JXL_DASSERT(y >= by);
      JXL_DASSERT(y < by + pos.ref_pos.ysize);
      size_t iy = y - by;
      size_t ref = pos.ref_pos.ref;
      if (bx >= image_rect.x0() + image_rect.xsize()) continue;
      if (bx + xsize < image_rect.x0()) continue;
      size_t x0 = std::max(bx, image_rect.x0());
      size_t x1 = std::min(bx + xsize, image_rect.x0() + image_rect.xsize());
      for (size_t c = 0; c < 3; c++) {
        fg_ptrs[c] =
            shared_->reference_frames[ref].frame->color()->ConstPlaneRow(
                c, pos.ref_pos.y0 + iy) +
            pos.ref_pos.x0 + x0 - bx;
        bg_ptrs[c] = opsin_rect.PlaneRow(opsin, c, y - image_rect.y0()) + x0 -
                     image_rect.x0();
      }
      for (size_t i = 0; i < num_ec; i++) {
        fg_ptrs[3 + i] =
            shared_->reference_frames[ref].frame->extra_channels()[i].ConstRow(
                pos.ref_pos.y0 + iy) +
            pos.ref_pos.x0 + x0 - bx;
        bg_ptrs[3 + i] = extra_channels[i] + x0 - image_rect.x0();
      }
      JXL_RETURN_IF_ERROR(
          PerformBlending(bg_ptrs.data(), fg_ptrs.data(), bg_ptrs.data(),
                          x1 - x0, pos.blending[0], pos.blending.data() + 1,
                          shared_->metadata->m.extra_channel_info));
    }
  }
  return true;
}

}  // namespace jxl
<commit_msg>Avoid allocations when no patches are present.<commit_after>// Copyright (c) the JPEG XL Project Authors. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

#include "lib/jxl/dec_patch_dictionary.h"

#include <stdint.h>
#include <stdlib.h>
#include <sys/types.h>

#include <algorithm>
#include <random>
#include <string>
#include <tuple>
#include <utility>
#include <vector>

#include "lib/jxl/ans_params.h"
#include "lib/jxl/base/compiler_specific.h"
#include "lib/jxl/base/override.h"
#include "lib/jxl/base/status.h"
#include "lib/jxl/blending.h"
#include "lib/jxl/chroma_from_luma.h"
#include "lib/jxl/color_management.h"
#include "lib/jxl/common.h"
#include "lib/jxl/dec_ans.h"
#include "lib/jxl/dec_frame.h"
#include "lib/jxl/entropy_coder.h"
#include "lib/jxl/frame_header.h"
#include "lib/jxl/image.h"
#include "lib/jxl/image_bundle.h"
#include "lib/jxl/image_ops.h"
#include "lib/jxl/patch_dictionary_internal.h"

namespace jxl {

constexpr size_t kMaxPatches = 1 << 24;

Status PatchDictionary::Decode(BitReader* br, size_t xsize, size_t ysize,
                               bool* uses_extra_channels) {
  positions_.clear();
  std::vector<uint8_t> context_map;
  ANSCode code;
  JXL_RETURN_IF_ERROR(
      DecodeHistograms(br, kNumPatchDictionaryContexts, &code, &context_map));
  ANSSymbolReader decoder(&code, br);

  auto read_num = [&](size_t context) {
    size_t r = decoder.ReadHybridUint(context, br, context_map);
    return r;
  };

  size_t num_ref_patch = read_num(kNumRefPatchContext);
  // TODO(veluca): does this make sense?
  if (num_ref_patch > kMaxPatches) {
    return JXL_FAILURE("Too many patches in dictionary");
  }

  size_t total_patches = 0;
  size_t next_size = 1;

  for (size_t id = 0; id < num_ref_patch; id++) {
    PatchReferencePosition ref_pos;
    ref_pos.ref = read_num(kReferenceFrameContext);
    if (ref_pos.ref >= kMaxNumReferenceFrames ||
        shared_->reference_frames[ref_pos.ref].frame->xsize() == 0) {
      return JXL_FAILURE("Invalid reference frame ID");
    }
    if (!shared_->reference_frames[ref_pos.ref].ib_is_in_xyb) {
      return JXL_FAILURE(
          "Patches cannot use frames saved post color transforms");
    }
    const ImageBundle& ib = *shared_->reference_frames[ref_pos.ref].frame;
    ref_pos.x0 = read_num(kPatchReferencePositionContext);
    ref_pos.y0 = read_num(kPatchReferencePositionContext);
    ref_pos.xsize = read_num(kPatchSizeContext) + 1;
    ref_pos.ysize = read_num(kPatchSizeContext) + 1;
    if (ref_pos.x0 + ref_pos.xsize > ib.xsize()) {
      return JXL_FAILURE("Invalid position specified in reference frame");
    }
    if (ref_pos.y0 + ref_pos.ysize > ib.ysize()) {
      return JXL_FAILURE("Invalid position specified in reference frame");
    }
    size_t id_count = read_num(kPatchCountContext) + 1;
    total_patches += id_count;
    if (total_patches > kMaxPatches) {
      return JXL_FAILURE("Too many patches in dictionary");
    }
    if (next_size < total_patches) {
      next_size *= 2;
      next_size = std::min<size_t>(next_size, kMaxPatches);
    }
    positions_.reserve(next_size);
    for (size_t i = 0; i < id_count; i++) {
      PatchPosition pos;
      pos.ref_pos = ref_pos;
      if (i == 0) {
        pos.x = read_num(kPatchPositionContext);
        pos.y = read_num(kPatchPositionContext);
      } else {
        pos.x =
            positions_.back().x + UnpackSigned(read_num(kPatchOffsetContext));
        pos.y =
            positions_.back().y + UnpackSigned(read_num(kPatchOffsetContext));
      }
      if (pos.x + ref_pos.xsize > xsize) {
        return JXL_FAILURE("Invalid patch x: at %zu + %zu > %zu", pos.x,
                           ref_pos.xsize, xsize);
      }
      if (pos.y + ref_pos.ysize > ysize) {
        return JXL_FAILURE("Invalid patch y: at %zu + %zu > %zu", pos.y,
                           ref_pos.ysize, ysize);
      }
      for (size_t i = 0; i < shared_->metadata->m.extra_channel_info.size() + 1;
           i++) {
        uint32_t blend_mode = read_num(kPatchBlendModeContext);
        if (blend_mode >= uint32_t(PatchBlendMode::kNumBlendModes)) {
          return JXL_FAILURE("Invalid patch blend mode: %u", blend_mode);
        }
        PatchBlending info;
        info.mode = static_cast<PatchBlendMode>(blend_mode);
        if (UsesAlpha(info.mode)) {
          *uses_extra_channels = true;
        }
        if (info.mode != PatchBlendMode::kNone && i > 0) {
          *uses_extra_channels = true;
        }
        if (UsesAlpha(info.mode) &&
            shared_->metadata->m.extra_channel_info.size() > 1) {
          info.alpha_channel = read_num(kPatchAlphaChannelContext);
          if (info.alpha_channel >=
              shared_->metadata->m.extra_channel_info.size()) {
            return JXL_FAILURE(
                "Invalid alpha channel for blending: %u out of %u\n",
                info.alpha_channel,
                (uint32_t)shared_->metadata->m.extra_channel_info.size());
          }
        } else {
          info.alpha_channel = 0;
        }
        if (UsesClamp(info.mode)) {
          info.clamp = read_num(kPatchClampContext);
        } else {
          info.clamp = false;
        }
        pos.blending.push_back(info);
      }
      positions_.push_back(std::move(pos));
    }
  }
  positions_.shrink_to_fit();

  if (!decoder.CheckANSFinalState()) {
    return JXL_FAILURE("ANS checksum failure.");
  }
  if (!HasAny()) {
    return JXL_FAILURE("Decoded patch dictionary but got none");
  }

  ComputePatchCache();
  return true;
}

int PatchDictionary::GetReferences() const {
  int result = 0;
  for (size_t i = 0; i < positions_.size(); ++i) {
    result |= (1 << static_cast<int>(positions_[i].ref_pos.ref));
  }
  return result;
}

void PatchDictionary::ComputePatchCache() {
  patch_starts_.clear();
  sorted_patches_.clear();
  if (positions_.empty()) return;
  std::vector<std::pair<size_t, size_t>> sorted_patches_y;
  for (size_t i = 0; i < positions_.size(); i++) {
    const PatchPosition& pos = positions_[i];
    for (size_t y = pos.y; y < pos.y + pos.ref_pos.ysize; y++) {
      sorted_patches_y.emplace_back(y, i);
    }
  }
  // The relative order of patches that affect the same pixels is preserved.
  // This is important for patches that have a blend mode different from kAdd.
  std::sort(sorted_patches_y.begin(), sorted_patches_y.end());
  patch_starts_.resize(sorted_patches_y.back().first + 2,
                       sorted_patches_y.size());
  sorted_patches_.resize(sorted_patches_y.size());
  for (size_t i = 0; i < sorted_patches_y.size(); i++) {
    sorted_patches_[i] = sorted_patches_y[i].second;
    patch_starts_[sorted_patches_y[i].first] =
        std::min(patch_starts_[sorted_patches_y[i].first], i);
  }
  for (size_t i = patch_starts_.size() - 1; i > 0; i--) {
    patch_starts_[i - 1] = std::min(patch_starts_[i], patch_starts_[i - 1]);
  }
}

Status PatchDictionary::AddTo(Image3F* opsin, const Rect& opsin_rect,
                              float* const* extra_channels,
                              const Rect& image_rect) const {
  JXL_CHECK(SameSize(opsin_rect, image_rect));
  if (patch_starts_.empty()) return true;
  size_t num_ec = shared_->metadata->m.num_extra_channels;
  std::vector<const float*> fg_ptrs(3 + num_ec);
  std::vector<float*> bg_ptrs(3 + num_ec);
  for (size_t y = image_rect.y0(); y < image_rect.y0() + image_rect.ysize();
       y++) {
    if (y + 1 >= patch_starts_.size()) continue;
    for (size_t id = patch_starts_[y]; id < patch_starts_[y + 1]; id++) {
      const PatchPosition& pos = positions_[sorted_patches_[id]];
      size_t by = pos.y;
      size_t bx = pos.x;
      size_t xsize = pos.ref_pos.xsize;
      JXL_DASSERT(y >= by);
      JXL_DASSERT(y < by + pos.ref_pos.ysize);
      size_t iy = y - by;
      size_t ref = pos.ref_pos.ref;
      if (bx >= image_rect.x0() + image_rect.xsize()) continue;
      if (bx + xsize < image_rect.x0()) continue;
      size_t x0 = std::max(bx, image_rect.x0());
      size_t x1 = std::min(bx + xsize, image_rect.x0() + image_rect.xsize());
      for (size_t c = 0; c < 3; c++) {
        fg_ptrs[c] =
            shared_->reference_frames[ref].frame->color()->ConstPlaneRow(
                c, pos.ref_pos.y0 + iy) +
            pos.ref_pos.x0 + x0 - bx;
        bg_ptrs[c] = opsin_rect.PlaneRow(opsin, c, y - image_rect.y0()) + x0 -
                     image_rect.x0();
      }
      for (size_t i = 0; i < num_ec; i++) {
        fg_ptrs[3 + i] =
            shared_->reference_frames[ref].frame->extra_channels()[i].ConstRow(
                pos.ref_pos.y0 + iy) +
            pos.ref_pos.x0 + x0 - bx;
        bg_ptrs[3 + i] = extra_channels[i] + x0 - image_rect.x0();
      }
      JXL_RETURN_IF_ERROR(
          PerformBlending(bg_ptrs.data(), fg_ptrs.data(), bg_ptrs.data(),
                          x1 - x0, pos.blending[0], pos.blending.data() + 1,
                          shared_->metadata->m.extra_channel_info));
    }
  }
  return true;
}

}  // namespace jxl
<|endoftext|>