<commit_before>/******************************************************************************
* Copyright (c) 2011, Michael P. Gerlek (mpg@flaxen.com)
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following
* conditions are met:
*
*     * Redistributions of source code must retain the above copyright
*       notice, this list of conditions and the following disclaimer.
*     * Redistributions in binary form must reproduce the above copyright
*       notice, this list of conditions and the following disclaimer in
*       the documentation and/or other materials provided
*       with the distribution.
*     * Neither the name of Hobu, Inc. or Flaxen Geo Consulting nor the
*       names of its contributors may be used to endorse or promote
*       products derived from this software without specific prior
*       written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
* OF SUCH DAMAGE.
****************************************************************************/

#include <libpc/drivers/las/Writer.hpp>

#include "LasHeaderWriter.hpp"
#include <libpc/exceptions.hpp>
#include <libpc/Stage.hpp>
#include <libpc/SchemaLayout.hpp>
#include <libpc/PointBuffer.hpp>

#include <iostream>

namespace libpc { namespace drivers { namespace las {

LasWriter::LasWriter(Stage& prevStage, std::ostream& ostream)
    : Writer(prevStage)
    , m_ostream(ostream)
    , m_numPointsWritten(0)
{
    return;
}

const std::string& LasWriter::getName() const
{
    static std::string name("Liblas Writer");
    return name;
}

void LasWriter::writeBegin()
{
    // need to set properties of the header here, based on prev->getHeader() and on the user's preferences
    m_lasHeader.setBounds( getPrevStage().getBounds() );

    const Schema& schema = getPrevStage().getSchema();

    int indexX = schema.getDimensionIndex(Dimension::Field_X, Dimension::Int32);
    int indexY = schema.getDimensionIndex(Dimension::Field_Y, Dimension::Int32);
    int indexZ = schema.getDimensionIndex(Dimension::Field_Z, Dimension::Int32);
    const Dimension& dimX = schema.getDimension(indexX);
    const Dimension& dimY = schema.getDimension(indexY);
    const Dimension& dimZ = schema.getDimension(indexZ);

    m_lasHeader.SetScale(dimX.getNumericScale(), dimY.getNumericScale(), dimZ.getNumericScale());
    m_lasHeader.SetOffset(dimX.getNumericOffset(), dimY.getNumericOffset(), dimZ.getNumericOffset());

    std::cout.setf(std::ios_base::fixed, std::ios_base::floatfield);
    std::cout.precision(2);

    std::cout << "scale: " << dimX.getNumericScale() << dimY.getNumericScale() << dimZ.getNumericScale() << std::endl;
    boost::uint32_t cnt = static_cast<boost::uint32_t>(m_targetNumPointsToWrite);
    m_lasHeader.SetPointRecordsCount(cnt);

    LasHeaderWriter lasHeaderWriter(m_lasHeader, m_ostream);
    lasHeaderWriter.write();

    return;
}

void LasWriter::writeEnd()
{
    m_lasHeader.SetPointRecordsCount(m_numPointsWritten);

    std::streamsize const dataPos = 107;
    m_ostream.seekp(dataPos, std::ios::beg);
    LasHeaderWriter lasHeaderWriter(m_lasHeader, m_ostream);
    Utils::write_n(m_ostream, m_numPointsWritten, sizeof(m_numPointsWritten));

    return;
}

boost::uint32_t LasWriter::writeBuffer(const PointBuffer& PointBuffer)
{
    const SchemaLayout& schemaLayout = PointBuffer.getSchemaLayout();
    const Schema& schema = schemaLayout.getSchema();
    LasHeader::PointFormatId pointFormat = m_lasHeader.getDataFormatId();

    bool hasTimeData = false;
    bool hasColorData = false;
    bool hasWaveData = false;
    switch (pointFormat)
    {
    case LasHeader::ePointFormat0:
        break;
    case LasHeader::ePointFormat1:
        hasTimeData = true;
        break;
    case LasHeader::ePointFormat2:
        hasColorData = true;
        break;
    case LasHeader::ePointFormat3:
        hasTimeData = true;
        hasColorData = true;
        break;
    case LasHeader::ePointFormat4:
        hasTimeData = true;
        hasWaveData = true;
        break;
    case LasHeader::ePointFormat5:
        hasColorData = true;
        hasTimeData = true;
        hasWaveData = true;
        break;
    case LasHeader::ePointFormatUnknown:
        throw not_yet_implemented("Unknown point format encountered");
    }

    const int fieldIndexX = schema.getDimensionIndex(Dimension::Field_X, Dimension::Int32);
    const int fieldIndexY = schema.getDimensionIndex(Dimension::Field_Y, Dimension::Int32);
    const int fieldIndexZ = schema.getDimensionIndex(Dimension::Field_Z, Dimension::Int32);

    const int fieldIndexIntensity = schema.getDimensionIndex(Dimension::Field_Intensity, Dimension::Int16);
    const int fieldIndexReturnNum = schema.getDimensionIndex(Dimension::Field_ReturnNumber, Dimension::Uint8);
    const int fieldIndexNumReturns = schema.getDimensionIndex(Dimension::Field_NumberOfReturns, Dimension::Uint8);
    const int fieldIndexScanDir = schema.getDimensionIndex(Dimension::Field_ScanDirectionFlag, Dimension::Uint8);
    const int fieldIndexFlight = schema.getDimensionIndex(Dimension::Field_EdgeOfFlightLine, Dimension::Uint8);
    const int fieldIndexClassification = schema.getDimensionIndex(Dimension::Field_Classification, Dimension::Uint8);
    const int fieldIndexScanAngle = schema.getDimensionIndex(Dimension::Field_ScanAngleRank, Dimension::Int8);
    const int fieldIndexUserData = schema.getDimensionIndex(Dimension::Field_UserData, Dimension::Uint8);
    const int fieldIndexPointSource = schema.getDimensionIndex(Dimension::Field_PointSourceId, Dimension::Uint16);

    const int fieldIndexTime = (hasTimeData ? schema.getDimensionIndex(Dimension::Field_Time, Dimension::Double) : 0);

    const int fieldIndexRed = (hasColorData ? schema.getDimensionIndex(Dimension::Field_Red, Dimension::Uint16) : 0);
    const int fieldIndexGreen = (hasColorData ? schema.getDimensionIndex(Dimension::Field_Green, Dimension::Uint16) : 0);
    const int fieldIndexBlue = (hasColorData ? schema.getDimensionIndex(Dimension::Field_Blue, Dimension::Uint16) : 0);

    boost::uint32_t numValidPoints = 0;

    boost::uint8_t buf[1024]; // BUG: fixed size

    for (boost::uint32_t pointIndex=0; pointIndex<PointBuffer.getNumPoints(); pointIndex++)
    {

        if (pointFormat == LasHeader::ePointFormat0)
        {
            boost::uint8_t* p = buf;

            const boost::uint32_t x = PointBuffer.getField<boost::uint32_t>(pointIndex, fieldIndexX);
            const boost::uint32_t y = PointBuffer.getField<boost::uint32_t>(pointIndex, fieldIndexY);
            const boost::uint32_t z = PointBuffer.getField<boost::uint32_t>(pointIndex, fieldIndexZ);
            const boost::uint16_t intensity = PointBuffer.getField<boost::uint16_t>(pointIndex, fieldIndexIntensity);

            const boost::uint8_t returnNum = PointBuffer.getField<boost::uint8_t>(pointIndex, fieldIndexReturnNum);
            const boost::uint8_t numReturns = PointBuffer.getField<boost::uint8_t>(pointIndex, fieldIndexNumReturns);
            const boost::uint8_t scanDirFlag = PointBuffer.getField<boost::uint8_t>(pointIndex, fieldIndexScanDir);
            const boost::uint8_t flight = PointBuffer.getField<boost::uint8_t>(pointIndex, fieldIndexFlight);

            const boost::uint8_t bits = returnNum & (numReturns<<3) & (scanDirFlag << 6) && (flight << 7);

            const boost::uint8_t classification = PointBuffer.getField<boost::uint8_t>(pointIndex, fieldIndexClassification);
            const boost::int8_t scanAngleRank = PointBuffer.getField<boost::int8_t>(pointIndex, fieldIndexScanAngle);
            const boost::uint8_t user = PointBuffer.getField<boost::uint8_t>(pointIndex, fieldIndexUserData);
            const boost::uint16_t pointSourceId = PointBuffer.getField<boost::uint16_t>(pointIndex, fieldIndexPointSource);

            Utils::write_field<boost::uint32_t>(p, x);
            Utils::write_field<boost::uint32_t>(p, y);
            Utils::write_field<boost::uint32_t>(p, z);
            Utils::write_field<boost::uint16_t>(p, intensity);
            Utils::write_field<boost::uint8_t>(p, bits);
            Utils::write_field<boost::uint8_t>(p, classification);
            Utils::write_field<boost::int8_t>(p, scanAngleRank);
            Utils::write_field<boost::uint8_t>(p, user);
            Utils::write_field<boost::uint16_t>(p, pointSourceId);

            Utils::write_n(m_ostream, buf, LasHeader::ePointSize0);
        }
        else if (pointFormat == LasHeader::ePointFormat1)
        {
            throw;
            //Utils::write_n(m_ostream, buf, LasHeader::ePointSize1);
        }
        else if (pointFormat == LasHeader::ePointFormat2)
        {
            throw;
            //Utils::write_n(m_ostream, buf, LasHeader::ePointSize2);
        }
        else if (pointFormat == LasHeader::ePointFormat3)
        {
            boost::uint8_t* p = buf;

            const boost::uint32_t x = PointBuffer.getField<boost::uint32_t>(pointIndex, fieldIndexX);
            const boost::uint32_t y = PointBuffer.getField<boost::uint32_t>(pointIndex, fieldIndexY);
            const boost::uint32_t z = PointBuffer.getField<boost::uint32_t>(pointIndex, fieldIndexZ);
            const boost::uint16_t intensity = PointBuffer.getField<boost::uint16_t>(pointIndex, fieldIndexIntensity);

            const boost::uint8_t returnNum = PointBuffer.getField<boost::uint8_t>(pointIndex, fieldIndexReturnNum);
            const boost::uint8_t numReturns = PointBuffer.getField<boost::uint8_t>(pointIndex, fieldIndexNumReturns);
            const boost::uint8_t scanDirFlag = PointBuffer.getField<boost::uint8_t>(pointIndex, fieldIndexScanDir);
            const boost::uint8_t flight = PointBuffer.getField<boost::uint8_t>(pointIndex, fieldIndexFlight);

            const boost::uint8_t bits = returnNum & (numReturns<<3) & (scanDirFlag << 6) && (flight << 7);

            const boost::uint8_t classification = PointBuffer.getField<boost::uint8_t>(pointIndex, fieldIndexClassification);
            const boost::int8_t scanAngleRank = PointBuffer.getField<boost::int8_t>(pointIndex, fieldIndexScanAngle);
            const boost::uint8_t user = PointBuffer.getField<boost::uint8_t>(pointIndex, fieldIndexUserData);
            const boost::uint16_t pointSourceId = PointBuffer.getField<boost::uint16_t>(pointIndex, fieldIndexPointSource);
            const double time = PointBuffer.getField<double>(pointIndex, fieldIndexTime);
            const boost::uint16_t red = PointBuffer.getField<boost::uint16_t>(pointIndex, fieldIndexRed);
            const boost::uint16_t green = PointBuffer.getField<boost::uint16_t>(pointIndex, fieldIndexGreen);
            const boost::uint16_t blue = PointBuffer.getField<boost::uint16_t>(pointIndex, fieldIndexBlue);

            Utils::write_field<boost::uint32_t>(p, x);
            Utils::write_field<boost::uint32_t>(p, y);
            Utils::write_field<boost::uint32_t>(p, z);
            Utils::write_field<boost::uint16_t>(p, intensity);
            Utils::write_field<boost::uint8_t>(p, bits);
            Utils::write_field<boost::uint8_t>(p, classification);
            Utils::write_field<boost::int8_t>(p, scanAngleRank);
            Utils::write_field<boost::uint8_t>(p, user);
            Utils::write_field<boost::uint16_t>(p, pointSourceId);
            Utils::write_field<double>(p, time);
            Utils::write_field<boost::uint16_t>(p, red);
            Utils::write_field<boost::uint16_t>(p, green);
            Utils::write_field<boost::uint16_t>(p, blue);

            Utils::write_n(m_ostream, buf, LasHeader::ePointSize3);
        }
        else
        {
            throw;
        }

        ++numValidPoints;
    }

    //std::vector<boost::uint8_t> const& data = point.GetData();
    //detail::write_n(m_ofs, data.front(), m_header->GetDataRecordLength());

    m_numPointsWritten = m_numPointsWritten+numValidPoints;
    return numValidPoints;
}

} } } // namespaces
<commit_msg>more useful lint :)<commit_after>/******************************************************************************
* Copyright (c) 2011, Michael P. Gerlek (mpg@flaxen.com)
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following
* conditions are met:
*
*     * Redistributions of source code must retain the above copyright
*       notice, this list of conditions and the following disclaimer.
*     * Redistributions in binary form must reproduce the above copyright
*       notice, this list of conditions and the following disclaimer in
*       the documentation and/or other materials provided
*       with the distribution.
*     * Neither the name of Hobu, Inc. or Flaxen Geo Consulting nor the
*       names of its contributors may be used to endorse or promote
*       products derived from this software without specific prior
*       written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
* OF SUCH DAMAGE.
****************************************************************************/

#include <libpc/drivers/las/Writer.hpp>

#include "LasHeaderWriter.hpp"
#include <libpc/exceptions.hpp>
#include <libpc/Stage.hpp>
#include <libpc/SchemaLayout.hpp>
#include <libpc/PointBuffer.hpp>

#include <iostream>

namespace libpc { namespace drivers { namespace las {

LasWriter::LasWriter(Stage& prevStage, std::ostream& ostream)
    : Writer(prevStage)
    , m_ostream(ostream)
    , m_numPointsWritten(0)
{
    return;
}

const std::string& LasWriter::getName() const
{
    static std::string name("Liblas Writer");
    return name;
}

void LasWriter::writeBegin()
{
    // need to set properties of the header here, based on prev->getHeader() and on the user's preferences
    m_lasHeader.setBounds( getPrevStage().getBounds() );

    const Schema& schema = getPrevStage().getSchema();

    int indexX = schema.getDimensionIndex(Dimension::Field_X, Dimension::Int32);
    int indexY = schema.getDimensionIndex(Dimension::Field_Y, Dimension::Int32);
    int indexZ = schema.getDimensionIndex(Dimension::Field_Z, Dimension::Int32);
    const Dimension& dimX = schema.getDimension(indexX);
    const Dimension& dimY = schema.getDimension(indexY);
    const Dimension& dimZ = schema.getDimension(indexZ);

    m_lasHeader.SetScale(dimX.getNumericScale(), dimY.getNumericScale(), dimZ.getNumericScale());
    m_lasHeader.SetOffset(dimX.getNumericOffset(), dimY.getNumericOffset(), dimZ.getNumericOffset());

    std::cout.setf(std::ios_base::fixed, std::ios_base::floatfield);
    std::cout.precision(8);

    std::cout << "scale: " << dimX.getNumericScale() <<" " << dimY.getNumericScale() << " " << dimZ.getNumericScale() << std::endl;
    boost::uint32_t cnt = static_cast<boost::uint32_t>(m_targetNumPointsToWrite);
    m_lasHeader.SetPointRecordsCount(cnt);

    LasHeaderWriter lasHeaderWriter(m_lasHeader, m_ostream);
    lasHeaderWriter.write();

    return;
}

void LasWriter::writeEnd()
{
    m_lasHeader.SetPointRecordsCount(m_numPointsWritten);

    std::streamsize const dataPos = 107;
    m_ostream.seekp(dataPos, std::ios::beg);
    LasHeaderWriter lasHeaderWriter(m_lasHeader, m_ostream);
    Utils::write_n(m_ostream, m_numPointsWritten, sizeof(m_numPointsWritten));

    return;
}

boost::uint32_t LasWriter::writeBuffer(const PointBuffer& PointBuffer)
{
    const SchemaLayout& schemaLayout = PointBuffer.getSchemaLayout();
    const Schema& schema = schemaLayout.getSchema();
    LasHeader::PointFormatId pointFormat = m_lasHeader.getDataFormatId();

    bool hasTimeData = false;
    bool hasColorData = false;
    bool hasWaveData = false;
    switch (pointFormat)
    {
    case LasHeader::ePointFormat0:
        break;
    case LasHeader::ePointFormat1:
        hasTimeData = true;
        break;
    case LasHeader::ePointFormat2:
        hasColorData = true;
        break;
    case LasHeader::ePointFormat3:
        hasTimeData = true;
        hasColorData = true;
        break;
    case LasHeader::ePointFormat4:
        hasTimeData = true;
        hasWaveData = true;
        break;
    case LasHeader::ePointFormat5:
        hasColorData = true;
        hasTimeData = true;
        hasWaveData = true;
        break;
    case LasHeader::ePointFormatUnknown:
        throw not_yet_implemented("Unknown point format encountered");
    }

    const int fieldIndexX = schema.getDimensionIndex(Dimension::Field_X, Dimension::Int32);
    const int fieldIndexY = schema.getDimensionIndex(Dimension::Field_Y, Dimension::Int32);
    const int fieldIndexZ = schema.getDimensionIndex(Dimension::Field_Z, Dimension::Int32);

    const int fieldIndexIntensity = schema.getDimensionIndex(Dimension::Field_Intensity, Dimension::Int16);
    const int fieldIndexReturnNum = schema.getDimensionIndex(Dimension::Field_ReturnNumber, Dimension::Uint8);
    const int fieldIndexNumReturns = schema.getDimensionIndex(Dimension::Field_NumberOfReturns, Dimension::Uint8);
    const int fieldIndexScanDir = schema.getDimensionIndex(Dimension::Field_ScanDirectionFlag, Dimension::Uint8);
    const int fieldIndexFlight = schema.getDimensionIndex(Dimension::Field_EdgeOfFlightLine, Dimension::Uint8);
    const int fieldIndexClassification = schema.getDimensionIndex(Dimension::Field_Classification, Dimension::Uint8);
    const int fieldIndexScanAngle = schema.getDimensionIndex(Dimension::Field_ScanAngleRank, Dimension::Int8);
    const int fieldIndexUserData = schema.getDimensionIndex(Dimension::Field_UserData, Dimension::Uint8);
    const int fieldIndexPointSource = schema.getDimensionIndex(Dimension::Field_PointSourceId, Dimension::Uint16);

    const int fieldIndexTime = (hasTimeData ? schema.getDimensionIndex(Dimension::Field_Time, Dimension::Double) : 0);

    const int fieldIndexRed = (hasColorData ? schema.getDimensionIndex(Dimension::Field_Red, Dimension::Uint16) : 0);
    const int fieldIndexGreen = (hasColorData ? schema.getDimensionIndex(Dimension::Field_Green, Dimension::Uint16) : 0);
    const int fieldIndexBlue = (hasColorData ? schema.getDimensionIndex(Dimension::Field_Blue, Dimension::Uint16) : 0);

    boost::uint32_t numValidPoints = 0;

    boost::uint8_t buf[1024]; // BUG: fixed size

    for (boost::uint32_t pointIndex=0; pointIndex<PointBuffer.getNumPoints(); pointIndex++)
    {

        if (pointFormat == LasHeader::ePointFormat0)
        {
            boost::uint8_t* p = buf;

            const boost::uint32_t x = PointBuffer.getField<boost::uint32_t>(pointIndex, fieldIndexX);
            const boost::uint32_t y = PointBuffer.getField<boost::uint32_t>(pointIndex, fieldIndexY);
            const boost::uint32_t z = PointBuffer.getField<boost::uint32_t>(pointIndex, fieldIndexZ);
            const boost::uint16_t intensity = PointBuffer.getField<boost::uint16_t>(pointIndex, fieldIndexIntensity);

            const boost::uint8_t returnNum = PointBuffer.getField<boost::uint8_t>(pointIndex, fieldIndexReturnNum);
            const boost::uint8_t numReturns = PointBuffer.getField<boost::uint8_t>(pointIndex, fieldIndexNumReturns);
            const boost::uint8_t scanDirFlag = PointBuffer.getField<boost::uint8_t>(pointIndex, fieldIndexScanDir);
            const boost::uint8_t flight = PointBuffer.getField<boost::uint8_t>(pointIndex, fieldIndexFlight);

            const boost::uint8_t bits = returnNum & (numReturns<<3) & (scanDirFlag << 6) && (flight << 7);

            const boost::uint8_t classification = PointBuffer.getField<boost::uint8_t>(pointIndex, fieldIndexClassification);
            const boost::int8_t scanAngleRank = PointBuffer.getField<boost::int8_t>(pointIndex, fieldIndexScanAngle);
            const boost::uint8_t user = PointBuffer.getField<boost::uint8_t>(pointIndex, fieldIndexUserData);
            const boost::uint16_t pointSourceId = PointBuffer.getField<boost::uint16_t>(pointIndex, fieldIndexPointSource);

            Utils::write_field<boost::uint32_t>(p, x);
            Utils::write_field<boost::uint32_t>(p, y);
            Utils::write_field<boost::uint32_t>(p, z);
            Utils::write_field<boost::uint16_t>(p, intensity);
            Utils::write_field<boost::uint8_t>(p, bits);
            Utils::write_field<boost::uint8_t>(p, classification);
            Utils::write_field<boost::int8_t>(p, scanAngleRank);
            Utils::write_field<boost::uint8_t>(p, user);
            Utils::write_field<boost::uint16_t>(p, pointSourceId);

            Utils::write_n(m_ostream, buf, LasHeader::ePointSize0);
        }
        else if (pointFormat == LasHeader::ePointFormat1)
        {
            throw;
            //Utils::write_n(m_ostream, buf, LasHeader::ePointSize1);
        }
        else if (pointFormat == LasHeader::ePointFormat2)
        {
            throw;
            //Utils::write_n(m_ostream, buf, LasHeader::ePointSize2);
        }
        else if (pointFormat == LasHeader::ePointFormat3)
        {
            boost::uint8_t* p = buf;

            const boost::uint32_t x = PointBuffer.getField<boost::uint32_t>(pointIndex, fieldIndexX);
            const boost::uint32_t y = PointBuffer.getField<boost::uint32_t>(pointIndex, fieldIndexY);
            const boost::uint32_t z = PointBuffer.getField<boost::uint32_t>(pointIndex, fieldIndexZ);
            const boost::uint16_t intensity = PointBuffer.getField<boost::uint16_t>(pointIndex, fieldIndexIntensity);

            const boost::uint8_t returnNum = PointBuffer.getField<boost::uint8_t>(pointIndex, fieldIndexReturnNum);
            const boost::uint8_t numReturns = PointBuffer.getField<boost::uint8_t>(pointIndex, fieldIndexNumReturns);
            const boost::uint8_t scanDirFlag = PointBuffer.getField<boost::uint8_t>(pointIndex, fieldIndexScanDir);
            const boost::uint8_t flight = PointBuffer.getField<boost::uint8_t>(pointIndex, fieldIndexFlight);

            const boost::uint8_t bits = returnNum & (numReturns<<3) & (scanDirFlag << 6) && (flight << 7);

            const boost::uint8_t classification = PointBuffer.getField<boost::uint8_t>(pointIndex, fieldIndexClassification);
            const boost::int8_t scanAngleRank = PointBuffer.getField<boost::int8_t>(pointIndex, fieldIndexScanAngle);
            const boost::uint8_t user = PointBuffer.getField<boost::uint8_t>(pointIndex, fieldIndexUserData);
            const boost::uint16_t pointSourceId = PointBuffer.getField<boost::uint16_t>(pointIndex, fieldIndexPointSource);
            const double time = PointBuffer.getField<double>(pointIndex, fieldIndexTime);
            const boost::uint16_t red = PointBuffer.getField<boost::uint16_t>(pointIndex, fieldIndexRed);
            const boost::uint16_t green = PointBuffer.getField<boost::uint16_t>(pointIndex, fieldIndexGreen);
            const boost::uint16_t blue = PointBuffer.getField<boost::uint16_t>(pointIndex, fieldIndexBlue);

            Utils::write_field<boost::uint32_t>(p, x);
            Utils::write_field<boost::uint32_t>(p, y);
            Utils::write_field<boost::uint32_t>(p, z);
            Utils::write_field<boost::uint16_t>(p, intensity);
            Utils::write_field<boost::uint8_t>(p, bits);
            Utils::write_field<boost::uint8_t>(p, classification);
            Utils::write_field<boost::int8_t>(p, scanAngleRank);
            Utils::write_field<boost::uint8_t>(p, user);
            Utils::write_field<boost::uint16_t>(p, pointSourceId);
            Utils::write_field<double>(p, time);
            Utils::write_field<boost::uint16_t>(p, red);
            Utils::write_field<boost::uint16_t>(p, green);
            Utils::write_field<boost::uint16_t>(p, blue);

            Utils::write_n(m_ostream, buf, LasHeader::ePointSize3);
        }
        else
        {
            throw;
        }

        ++numValidPoints;
    }

    //std::vector<boost::uint8_t> const& data = point.GetData();
    //detail::write_n(m_ofs, data.front(), m_header->GetDataRecordLength());

    m_numPointsWritten = m_numPointsWritten+numValidPoints;
    return numValidPoints;
}

} } } // namespaces
<|endoftext|>


<commit_before>/*
 * VapourSynth D2V Plugin
 *
 * Copyright (c) 2012 Derek Buitenhuis
 *
 * This file is part of d2vsource.
 *
 * d2vsource is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * d2vsource is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with d2vsource; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */

#include <iostream>
#include <string>
#include <vector>

extern "C" {
#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
#include <stdio.h>
}

#include "compat.hpp"
#include "d2v.hpp"
#include "decode.hpp"
#include "gop.hpp"

using namespace std;

/*
 * AVIO seek function to handle GOP offsets and multi-file support
 * in libavformat without it knowing about it.
 */
static int64_t file_seek(void *opaque, int64_t offset, int whence)
{
    decodecontext *ctx = (decodecontext *) opaque;

    switch(whence) {
    case SEEK_SET: {
        /*
         * This mutli-file seek is likely very broken, but I don't
         * really care much, since it is only used in avformat_find_stream_info,
         * which does its job fine as-is.
         */
        int64_t real_offset = offset + ctx->orig_file_offset;
        unsigned int i;

        for(i = ctx->orig_file; i < ctx->cur_file; i++)
            real_offset -= ctx->file_sizes[i];

        while(real_offset > ctx->file_sizes[ctx->cur_file] && ctx->cur_file != ctx->files.size() - 1) {
            real_offset -= ctx->file_sizes[ctx->cur_file];
            ctx->cur_file++;
        }

        while(real_offset < 0 && ctx->cur_file) {
            ctx->cur_file--;
            real_offset += ctx->file_sizes[ctx->cur_file];
        }

        fseeko(ctx->files[ctx->cur_file], real_offset, SEEK_SET);

        return offset;
    }
    case AVSEEK_SIZE: {
        /*
         * Return the total filesize of all files combined,
         * adjusted for GOP offset.
         */
        int64_t size = -(ctx->orig_file_offset);
        unsigned int i;

        for(i = ctx->orig_file; i < ctx->file_sizes.size(); i++)
            size += ctx->file_sizes[i];

        return size;
    }
    default:
        /* Shouldn't need to support anything else for our use case. */
        cout << "Unsupported seek!" << endl;
        return -1;
    }
}

/*
 * AVIO packet reading function to handle GOP offsets and multi-file support
 * in libavformat without it knowing about it.
 */
static int read_packet(void *opaque, uint8_t *buf, int size)
{
    decodecontext *ctx = (decodecontext *) opaque;
    int ret;

    /*
     * If we read in less than we got asked for, and we're
     * not on the last file, then start reading seamlessly
     * on the next file.
     */
    ret = fread(buf, 1, size, ctx->files[ctx->cur_file]);
    if (ret < size && ctx->cur_file != ctx->files.size() - 1) {
        ctx->cur_file++;
        fseeko(ctx->files[ctx->cur_file], 0, SEEK_SET);
        fread(buf + ret, 1, size - ret, ctx->files[ctx->cur_file]);
    } else {
        return ret;
    }

    return size;
}

/* Conditionally free all memebers of decodecontext. */
void decodefreep(decodecontext **ctx)
{
    decodecontext *lctx = *ctx;
    unsigned int i;

    if (!lctx)
        return;

    av_freep(&lctx->in);
    av_free_packet(&lctx->inpkt);

    if (lctx->fctx) {
        if (lctx->fctx->pb)
            av_freep(&lctx->fctx->pb);

        avformat_close_input(&lctx->fctx);
    }

    for(i = 0; i < lctx->files.size(); i++)
        fclose(lctx->files[i]);

    lctx->files.clear();
    lctx->file_sizes.clear();

    if (lctx->avctx) {
        avcodec_close(lctx->avctx);
        av_freep(&lctx->avctx);
    }

    delete lctx->fakename;
    delete lctx;

    *ctx = NULL;
}

/* Initialize everything we can with regards to decoding */
decodecontext *decodeinit(d2vcontext *dctx, string& err)
{
    decodecontext *ret;
    int i, av_ret;

    ret = new decodecontext;

    /* Zero the context to aid in conditional freeing later. */
    memset(ret, 0, sizeof(*ret));

    /* Holds our "filename" we pass to libavformat. */
    ret->fakename = new string;

    /* Set our stream index to -1 (uninitialized). */
    ret->stream_index = -1;

    /* Open each file and stash its size. */
    for(i = 0; i < dctx->num_files; i++) {
        FILE *in;
        int64_t size;

        in = fopen(dctx->files[i].c_str(), "rb");
        if (!in) {
            err  = "Cannot open file: ";
            err += dctx->files[i];
            goto fail;
        }

        fseeko(in, 0, SEEK_END);
        size = ftello(in);
        fseeko(in, 0, SEEK_SET);

        ret->file_sizes.push_back(size);
        ret->files.push_back(in);
    }

    /*
     * Register all of our demuxers, parsers, and decoders.
     * Ideally, to create a smaller binary, we only enable the
     * following:
     *
     * Demuxers: mpegvideo, mpegps, mpegts.
     * Parsers: mpegvideo, mpegaudio.
     * Decoders: mpeg1video, mpeg2video.
     */
    avcodec_register_all();
    av_register_all();

    /* Set the correct decoder. */
    if (dctx->mpeg_type == 1) {
        ret->incodec = avcodec_find_decoder(AV_CODEC_ID_MPEG1VIDEO);
    } else if (dctx->mpeg_type == 2) {
        ret->incodec = avcodec_find_decoder(AV_CODEC_ID_MPEG2VIDEO);
    } else {
        err = "Invalid MPEG Type.";
        goto fail;
    }

    /* Allocate the codec's context. */
    ret->avctx = avcodec_alloc_context3(ret->incodec);
    if (!ret->avctx) {
        err = "Cannot allocate AVCodecContext.";
        goto fail;
    }

    /* Set the IDCT algorithm. */
    ret->avctx->idct_algo = dctx->idct_algo;

    /*
     * Enable EMU_EDGE so that we can use buffers that are
     * not padded by 32 pixels.
     */
    ret->avctx->flags |= CODEC_FLAG_EMU_EDGE;

    /* Open it. */
    av_ret = avcodec_open2(ret->avctx, ret->incodec, NULL);
    if (av_ret < 0) {
        err = "Cannot open decoder.";
        goto fail;
    }

    /* Allocate the scratch buffer for our custom AVIO context. */
    ret->in = (uint8_t *) av_malloc(32 * 1024);
    if (!ret->in) {
        err = "Cannot alloc inbuf.";
        goto fail;
    }

    /* We don't want to hear all the info it has. */
    av_log_set_level(AV_LOG_PANIC);

    return ret;

fail:
    decodefreep(&ret);
    return NULL;
}

int decodeframe(int frame_num, d2vcontext *ctx, decodecontext *dctx, AVFrame *out, string& err)
{
    frame f;
    gop g;
    unsigned int i;
    int o, j, av_ret, offset;
    bool next;

    /* Get our frame and the GOP its in. */
    f = ctx->frames[frame_num];
    g = ctx->gops[f.gop];

    /*
     * The offset is how many frames we have to decode from our
     * current position in order to get to the frame we want.
     * The initial offset is obtaiend during the parsing of the
     * D2V file, but it may be more in an open GOP situation,
     * which we handle below.
     */
    offset = f.offset;

    /*
     * If we're in a open GOP situation, then start decoding
     * from the previous GOP (one at most is needed), and adjust
     * out offset accordingly.
     */
    if (!(g.info & GOP_FLAG_CLOSED) && (f.gop - 1 > 0)) {
        int n = frame_num;
        frame t = ctx->frames[n];

        g = ctx->gops[f.gop - 1];

        /*
         * Find the offset of the last frame in the
         * previous GOP and add it to our offset.
         */
        while(t.offset)
            t = ctx->frames[--n];

        t = ctx->frames[--n];

        /*
         * Subtract one from the offset to compensate for
         * libavcodec delay, I think.
         */
        offset += t.offset - 1;
    }

    /*
     * Check if we're decoding linearly, and if the GOP
     * of the current frame and previous frame are either
     * the same, or also linear. If so, we can decode
     * linearly.
     */
    next = (dctx->last_gop == f.gop || dctx->last_gop == f.gop - 1) && dctx->last_frame == frame_num - 1;

    /* Skip GOP initialization if we're decoding linearly. */
    if (!next) {
        /* Free out format and AVIO contexts from the previous seek. */
        if (dctx->fctx) {
            if (dctx->fctx->pb)
                av_freep(&dctx->fctx->pb);

            avformat_close_input(&dctx->fctx);
        }

        /* Seek to our GOP offset and stash the info. */
        fseeko(dctx->files[g.file], g.pos, SEEK_SET);
        dctx->orig_file_offset = g.pos;
        dctx->orig_file        = g.file;
        dctx->cur_file         = g.file;

        /* Allocate format context. */
        dctx->fctx = avformat_alloc_context();
        if (!dctx->fctx) {
            err = "Cannot allocate AVFormatContext.";
            goto dfail;
        }

        /*
         * Find the demuxer for our input type, and also set
         * the "filename" that we pass to libavformat when
         * we open the demuxer with our custom AVIO context.
         */
        if (ctx->stream_type == ELEMENTARY) {
            dctx->fctx->iformat = av_find_input_format("mpegvideo");
            *dctx->fakename      = "fakevideo.m2v";
        } else if (ctx->stream_type == PROGRAM) {
            dctx->fctx->iformat = av_find_input_format("mpeg");
            *dctx->fakename      = "fakevideo.vob";
        } else if (ctx->stream_type == TRANSPORT) {
            dctx->fctx->iformat = av_find_input_format("mpegts");
            *dctx->fakename      = "fakevideo.ts";
        } else {
            err = "Unsupported format.";
            goto dfail;
        }

        /*
         * Initialize out custom AVIO context that libavformat
         * will use instead of a file. It uses our custom packet
         * reading and seeking functions that transparently work
         * with our indexed GOP offsets and multiple files.
         */
        dctx->fctx->pb = avio_alloc_context(dctx->in, 32 * 1024, 0, dctx, read_packet, NULL, file_seek);

        /* Open the demuxer. */
        av_ret = avformat_open_input(&dctx->fctx, (*dctx->fakename).c_str(), NULL, NULL);
        if (av_ret < 0) {
            err = "Cannot open buffer in libavformat.";
            goto dfail;
        }

        /*
         * Flush the buffers of our codec's context so we
         * don't need to re-initialize it.
         */
        avcodec_flush_buffers(dctx->avctx);

        /*
         * Call the abomination function to find out
         * how many streams we have.
         */
        avformat_find_stream_info(dctx->fctx, NULL);

        /* Free and re-initialize any existing packet. */
        av_free_packet(&dctx->inpkt);
        av_init_packet(&dctx->inpkt);
    }

    /* Set our stream index if we need to. */
    if (dctx->stream_index == -1) {
        for(i = 0; i < dctx->fctx->nb_streams; i++) {
            if (dctx->fctx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) {
                dctx->stream_index = (int) i;
                break;
            }
        }
    }

    /*
     * We don't need to read a new packet in if we are decoding
     * linearly, since it's still there from the previous iteration.
     */
    if (!next)
        av_read_frame(dctx->fctx, &dctx->inpkt);

    /* If we're decoding linearly, there is obviously no offset. */
    o = next ? 0 : offset;
    for(j = 0; j <= o; j++) {
        while(dctx->inpkt.stream_index != dctx->stream_index) {
            av_free_packet(&dctx->inpkt);
            av_read_frame(dctx->fctx, &dctx->inpkt);
        }

        /*
         * Loop until we have a whole frame, since there can be
         * multi-packet frames.
         */
        av_ret = 0;
        while(!av_ret) {
            AVPacket orig = dctx->inpkt;

            /*
             * Decoding might not consume out whole packet, so
             * stash the original packet info, loop until it
             * is all consumed, and then restore it, it so
             * we can free it properly.
             */
            while(dctx->inpkt.size > 0) {
                int r = avcodec_decode_video2(dctx->avctx, out, &av_ret, &dctx->inpkt);

                dctx->inpkt.size -= r;
                dctx->inpkt.data += r;
            }

            dctx->inpkt = orig;

            do {
                av_free_packet(&dctx->inpkt);
                av_read_frame(dctx->fctx, &dctx->inpkt);
            } while(dctx->inpkt.stream_index != dctx->stream_index);
        }
    }

    /*
     * Stash the frame number we just decoded, and the GOP it
     * is a part of so we can check if we're decoding linearly
     * later on.
     */
    dctx->last_gop   = f.gop;
    dctx->last_frame = frame_num;

    return 0;

dfail:
    avformat_close_input(&dctx->fctx);
    return -1;
}
<commit_msg>Add support for multi-stream MPEG-TS files<commit_after>/*
 * VapourSynth D2V Plugin
 *
 * Copyright (c) 2012 Derek Buitenhuis
 *
 * This file is part of d2vsource.
 *
 * d2vsource is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * d2vsource is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with d2vsource; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */

#include <iostream>
#include <string>
#include <vector>

extern "C" {
#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
#include <stdio.h>
}

#include "compat.hpp"
#include "d2v.hpp"
#include "decode.hpp"
#include "gop.hpp"

using namespace std;

/*
 * AVIO seek function to handle GOP offsets and multi-file support
 * in libavformat without it knowing about it.
 */
static int64_t file_seek(void *opaque, int64_t offset, int whence)
{
    decodecontext *ctx = (decodecontext *) opaque;

    switch(whence) {
    case SEEK_SET: {
        /*
         * This mutli-file seek is likely very broken, but I don't
         * really care much, since it is only used in avformat_find_stream_info,
         * which does its job fine as-is.
         */
        int64_t real_offset = offset + ctx->orig_file_offset;
        unsigned int i;

        for(i = ctx->orig_file; i < ctx->cur_file; i++)
            real_offset -= ctx->file_sizes[i];

        while(real_offset > ctx->file_sizes[ctx->cur_file] && ctx->cur_file != ctx->files.size() - 1) {
            real_offset -= ctx->file_sizes[ctx->cur_file];
            ctx->cur_file++;
        }

        while(real_offset < 0 && ctx->cur_file) {
            ctx->cur_file--;
            real_offset += ctx->file_sizes[ctx->cur_file];
        }

        fseeko(ctx->files[ctx->cur_file], real_offset, SEEK_SET);

        return offset;
    }
    case AVSEEK_SIZE: {
        /*
         * Return the total filesize of all files combined,
         * adjusted for GOP offset.
         */
        int64_t size = -(ctx->orig_file_offset);
        unsigned int i;

        for(i = ctx->orig_file; i < ctx->file_sizes.size(); i++)
            size += ctx->file_sizes[i];

        return size;
    }
    default:
        /* Shouldn't need to support anything else for our use case. */
        cout << "Unsupported seek!" << endl;
        return -1;
    }
}

/*
 * AVIO packet reading function to handle GOP offsets and multi-file support
 * in libavformat without it knowing about it.
 */
static int read_packet(void *opaque, uint8_t *buf, int size)
{
    decodecontext *ctx = (decodecontext *) opaque;
    int ret;

    /*
     * If we read in less than we got asked for, and we're
     * not on the last file, then start reading seamlessly
     * on the next file.
     */
    ret = fread(buf, 1, size, ctx->files[ctx->cur_file]);
    if (ret < size && ctx->cur_file != ctx->files.size() - 1) {
        ctx->cur_file++;
        fseeko(ctx->files[ctx->cur_file], 0, SEEK_SET);
        fread(buf + ret, 1, size - ret, ctx->files[ctx->cur_file]);
    } else {
        return ret;
    }

    return size;
}

/* Conditionally free all memebers of decodecontext. */
void decodefreep(decodecontext **ctx)
{
    decodecontext *lctx = *ctx;
    unsigned int i;

    if (!lctx)
        return;

    av_freep(&lctx->in);
    av_free_packet(&lctx->inpkt);

    if (lctx->fctx) {
        if (lctx->fctx->pb)
            av_freep(&lctx->fctx->pb);

        avformat_close_input(&lctx->fctx);
    }

    for(i = 0; i < lctx->files.size(); i++)
        fclose(lctx->files[i]);

    lctx->files.clear();
    lctx->file_sizes.clear();

    if (lctx->avctx) {
        avcodec_close(lctx->avctx);
        av_freep(&lctx->avctx);
    }

    delete lctx->fakename;
    delete lctx;

    *ctx = NULL;
}

/* Initialize everything we can with regards to decoding */
decodecontext *decodeinit(d2vcontext *dctx, string& err)
{
    decodecontext *ret;
    int i, av_ret;

    ret = new decodecontext;

    /* Zero the context to aid in conditional freeing later. */
    memset(ret, 0, sizeof(*ret));

    /* Holds our "filename" we pass to libavformat. */
    ret->fakename = new string;

    /* Set our stream index to -1 (uninitialized). */
    ret->stream_index = -1;

    /* Open each file and stash its size. */
    for(i = 0; i < dctx->num_files; i++) {
        FILE *in;
        int64_t size;

        in = fopen(dctx->files[i].c_str(), "rb");
        if (!in) {
            err  = "Cannot open file: ";
            err += dctx->files[i];
            goto fail;
        }

        fseeko(in, 0, SEEK_END);
        size = ftello(in);
        fseeko(in, 0, SEEK_SET);

        ret->file_sizes.push_back(size);
        ret->files.push_back(in);
    }

    /*
     * Register all of our demuxers, parsers, and decoders.
     * Ideally, to create a smaller binary, we only enable the
     * following:
     *
     * Demuxers: mpegvideo, mpegps, mpegts.
     * Parsers: mpegvideo, mpegaudio.
     * Decoders: mpeg1video, mpeg2video.
     */
    avcodec_register_all();
    av_register_all();

    /* Set the correct decoder. */
    if (dctx->mpeg_type == 1) {
        ret->incodec = avcodec_find_decoder(AV_CODEC_ID_MPEG1VIDEO);
    } else if (dctx->mpeg_type == 2) {
        ret->incodec = avcodec_find_decoder(AV_CODEC_ID_MPEG2VIDEO);
    } else {
        err = "Invalid MPEG Type.";
        goto fail;
    }

    /* Allocate the codec's context. */
    ret->avctx = avcodec_alloc_context3(ret->incodec);
    if (!ret->avctx) {
        err = "Cannot allocate AVCodecContext.";
        goto fail;
    }

    /* Set the IDCT algorithm. */
    ret->avctx->idct_algo = dctx->idct_algo;

    /*
     * Enable EMU_EDGE so that we can use buffers that are
     * not padded by 32 pixels.
     */
    ret->avctx->flags |= CODEC_FLAG_EMU_EDGE;

    /* Open it. */
    av_ret = avcodec_open2(ret->avctx, ret->incodec, NULL);
    if (av_ret < 0) {
        err = "Cannot open decoder.";
        goto fail;
    }

    /* Allocate the scratch buffer for our custom AVIO context. */
    ret->in = (uint8_t *) av_malloc(32 * 1024);
    if (!ret->in) {
        err = "Cannot alloc inbuf.";
        goto fail;
    }

    /* We don't want to hear all the info it has. */
    av_log_set_level(AV_LOG_PANIC);

    return ret;

fail:
    decodefreep(&ret);
    return NULL;
}

int decodeframe(int frame_num, d2vcontext *ctx, decodecontext *dctx, AVFrame *out, string& err)
{
    frame f;
    gop g;
    unsigned int i;
    int o, j, av_ret, offset;
    bool next;

    /* Get our frame and the GOP its in. */
    f = ctx->frames[frame_num];
    g = ctx->gops[f.gop];

    /*
     * The offset is how many frames we have to decode from our
     * current position in order to get to the frame we want.
     * The initial offset is obtaiend during the parsing of the
     * D2V file, but it may be more in an open GOP situation,
     * which we handle below.
     */
    offset = f.offset;

    /*
     * If we're in a open GOP situation, then start decoding
     * from the previous GOP (one at most is needed), and adjust
     * out offset accordingly.
     */
    if (!(g.info & GOP_FLAG_CLOSED) && (f.gop - 1 > 0)) {
        int n = frame_num;
        frame t = ctx->frames[n];

        g = ctx->gops[f.gop - 1];

        /*
         * Find the offset of the last frame in the
         * previous GOP and add it to our offset.
         */
        while(t.offset)
            t = ctx->frames[--n];

        t = ctx->frames[--n];

        /*
         * Subtract one from the offset to compensate for
         * libavcodec delay, I think.
         */
        offset += t.offset - 1;
    }

    /*
     * Check if we're decoding linearly, and if the GOP
     * of the current frame and previous frame are either
     * the same, or also linear. If so, we can decode
     * linearly.
     */
    next = (dctx->last_gop == f.gop || dctx->last_gop == f.gop - 1) && dctx->last_frame == frame_num - 1;

    /* Skip GOP initialization if we're decoding linearly. */
    if (!next) {
        /* Free out format and AVIO contexts from the previous seek. */
        if (dctx->fctx) {
            if (dctx->fctx->pb)
                av_freep(&dctx->fctx->pb);

            avformat_close_input(&dctx->fctx);
        }

        /* Seek to our GOP offset and stash the info. */
        fseeko(dctx->files[g.file], g.pos, SEEK_SET);
        dctx->orig_file_offset = g.pos;
        dctx->orig_file        = g.file;
        dctx->cur_file         = g.file;

        /* Allocate format context. */
        dctx->fctx = avformat_alloc_context();
        if (!dctx->fctx) {
            err = "Cannot allocate AVFormatContext.";
            goto dfail;
        }

        /*
         * Find the demuxer for our input type, and also set
         * the "filename" that we pass to libavformat when
         * we open the demuxer with our custom AVIO context.
         */
        if (ctx->stream_type == ELEMENTARY) {
            dctx->fctx->iformat = av_find_input_format("mpegvideo");
            *dctx->fakename      = "fakevideo.m2v";
        } else if (ctx->stream_type == PROGRAM) {
            dctx->fctx->iformat = av_find_input_format("mpeg");
            *dctx->fakename      = "fakevideo.vob";
        } else if (ctx->stream_type == TRANSPORT) {
            dctx->fctx->iformat = av_find_input_format("mpegts");
            *dctx->fakename      = "fakevideo.ts";
        } else {
            err = "Unsupported format.";
            goto dfail;
        }

        /*
         * Initialize out custom AVIO context that libavformat
         * will use instead of a file. It uses our custom packet
         * reading and seeking functions that transparently work
         * with our indexed GOP offsets and multiple files.
         */
        dctx->fctx->pb = avio_alloc_context(dctx->in, 32 * 1024, 0, dctx, read_packet, NULL, file_seek);

        /* Open the demuxer. */
        av_ret = avformat_open_input(&dctx->fctx, (*dctx->fakename).c_str(), NULL, NULL);
        if (av_ret < 0) {
            err = "Cannot open buffer in libavformat.";
            goto dfail;
        }

        /*
         * Flush the buffers of our codec's context so we
         * don't need to re-initialize it.
         */
        avcodec_flush_buffers(dctx->avctx);

        /*
         * Call the abomination function to find out
         * how many streams we have.
         */
        avformat_find_stream_info(dctx->fctx, NULL);

        /* Free and re-initialize any existing packet. */
        av_free_packet(&dctx->inpkt);
        av_init_packet(&dctx->inpkt);
    }

    /*
     * Set our stream index if we need to.
     * Set it to the stream that matches our MPEG-TS PID if applicable.
     */
    if (dctx->stream_index == -1) {
        if (ctx->ts_pid)
            for(i = 0; i < dctx->fctx->nb_streams; i++)
                if (dctx->fctx->streams[i]->id == ctx->ts_pid)
                    break;
        else
            for(i = 0; i < dctx->fctx->nb_streams; i++)
                if (dctx->fctx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO)
                    break;

        dctx->stream_index = (int) i;
    }

    /*
     * We don't need to read a new packet in if we are decoding
     * linearly, since it's still there from the previous iteration.
     */
    if (!next)
        av_read_frame(dctx->fctx, &dctx->inpkt);

    /* If we're decoding linearly, there is obviously no offset. */
    o = next ? 0 : offset;
    for(j = 0; j <= o; j++) {
        while(dctx->inpkt.stream_index != dctx->stream_index) {
            av_free_packet(&dctx->inpkt);
            av_read_frame(dctx->fctx, &dctx->inpkt);
        }

        /*
         * Loop until we have a whole frame, since there can be
         * multi-packet frames.
         */
        av_ret = 0;
        while(!av_ret) {
            AVPacket orig = dctx->inpkt;

            /*
             * Decoding might not consume out whole packet, so
             * stash the original packet info, loop until it
             * is all consumed, and then restore it, it so
             * we can free it properly.
             */
            while(dctx->inpkt.size > 0) {
                int r = avcodec_decode_video2(dctx->avctx, out, &av_ret, &dctx->inpkt);

                dctx->inpkt.size -= r;
                dctx->inpkt.data += r;
            }

            dctx->inpkt = orig;

            do {
                av_free_packet(&dctx->inpkt);
                av_read_frame(dctx->fctx, &dctx->inpkt);
            } while(dctx->inpkt.stream_index != dctx->stream_index);
        }
    }

    /*
     * Stash the frame number we just decoded, and the GOP it
     * is a part of so we can check if we're decoding linearly
     * later on.
     */
    dctx->last_gop   = f.gop;
    dctx->last_frame = frame_num;

    return 0;

dfail:
    avformat_close_input(&dctx->fctx);
    return -1;
}
<|endoftext|>


<commit_before>#ifndef _options_hpp_INCLUDED
#define _options_hpp_INCLUDED

#ifndef NDEBUG
#define DBG 1
#else
#define DBG 0
#endif
#define OPTIONS \
\
/*     NAME             TYPE, VAL,LO, HI, USAGE */ \
\
OPTION(arena,            int,   3, 0,  3, "1=clause,2=var,3=queue") \
OPTION(binary,          bool,   1, 0,  1, "use binary proof format") \
OPTION(check,           bool, DBG, 0,  1, "save & check original CNF") \
OPTION(clim,             int,  -1, 0,1e9, "conflict limit (-1=none)") \
OPTION(dlim,             int,  -1, 0,1e9, "decision limit (-1=none)") \
OPTION(elim,            bool,   1, 0,  1, "bounded variable elimination") \
OPTION(elimclslim,       int,1000, 0,1e9, "ignore clauses of this size") \
OPTION(elimignore,    double, 0.0, 0,  1, "ignored fraction of variables") \
OPTION(eliminit,         int, 1e3, 0,1e9, "initial conflict limit") \
OPTION(elimint,          int, 1e4, 1,1e9, "initial conflict interval") \
OPTION(elimocclim,       int, 100, 0,1e9, "one sided occurrence limit") \
OPTION(elimroundsinit,   int,   5, 1,1e9, "initial number of rounds") \
OPTION(elimrounds,       int,   2, 1,1e9, "usual number of rounds") \
OPTION(emabumplast,   double,1e-5, 0,  1, "alpha bump last percentage") \
OPTION(emagluefast,   double,3e-2, 0,  1, "alpha fast glue") \
OPTION(emaglueslow,   double,1e-5, 0,  1, "alpha slow glue") \
OPTION(emajump,       double,1e-5, 0,  1, "alpha jump level") \
OPTION(emarestarteff, double,1e-3, 0,  1, "alpha restart effectiveness") \
OPTION(emarestartint, double,1e-5, 0,  1, "alpha actual restart interval") \
OPTION(emasize,       double,1e-5, 0,  1, "alpha learned clause size") \
OPTION(keepglue,         int,   2, 1,1e9, "glue kept learned clauses") \
OPTION(keepsize,         int,   3, 2,1e9, "size kept learned clauses") \
OPTION(leak,            bool,   1, 0,  1, "leak solver memory") \
OPTION(minimize,        bool,   1, 0,  1, "minimize learned clauses") \
OPTION(minimizedepth,    int,1000, 0,1e9, "minimization depth") \
OPTION(prefetch,        bool,   1, 0,  1, "prefetch watches") \
OPTION(profile,          int,   2, 0,  4, "profiling level") \
OPTION(quiet,           bool,   0, 0,  1, "disable all messages") \
OPTION(reduce,          bool,   1, 0,  1, "garbage collect clauses") \
OPTION(reduceglue,      bool,   1, 0,  1, "reduce on glue first") \
OPTION(reduceinc,        int, 300, 1,1e6, "reduce limit increment") \
OPTION(reduceinit,       int,2000, 0,1e6, "initial reduce limit") \
OPTION(restart,         bool,   1, 0,  1, "enable restarting") \
OPTION(restartint,       int,   4, 1,1e9, "restart base interval") \
OPTION(restartmargin, double, 1.1, 0, 10, "restart slow fast margin") \
OPTION(reusetrail,      bool,   1, 0,  1, "enable trail reuse") \
OPTION(shrink,          bool,   1, 0,  1, "shrink learned clause") \
OPTION(shrinkdepth,      int,   2, 0,1e9, "shrinking depth") \
OPTION(shrinkglue,       int,   5, 0,1e9, "glue limit for shrinking") \
OPTION(shrinksize,       int,  20, 0,1e9, "size limit for shrinking") \
OPTION(strengthen,      bool,   1, 0,  1, "strengthen during subsume") \
OPTION(sublast,          int,   5, 0,1e4, "eagerly subsume last") \
OPTION(subsume,         bool,   1, 0,  1, "enable clause subsumption") \
OPTION(subsumeinc,       int, 1e4, 1,1e9, "interval in conflicts") \
OPTION(subsumeinit,      int, 1e4, 0,1e9, "initial subsume limit") \
OPTION(subsumeocclim,    int, 100, 0,1e9, "watch list length limit") \
OPTION(trailbump,       bool,   1, 0,  1, "use trail + bumped") \
OPTION(trailbumplast, double,  40, 0,100, "trail bump last level limit") \
OPTION(trailbumprops, double, 200, 0,1e9, "trail bump propagation limit") \
OPTION(verbose,         bool,   0, 0,  1, "more verbose messages") \
OPTION(witness,         bool,   1, 0,  1, "print witness") \

namespace CaDiCaL {

class Internal;

class Options {

  Internal * internal;

  bool set (   int &, const char *, const char *, const    int, const    int);
  bool set (  bool &, const char *, const char *, const   bool, const   bool);
  bool set (double &, const char *, const char *, const double, const double);

  const char * match (const char *, const char *);

public:

  // Makes options directly accessible, e.g., for instance declares the
  // member 'bool Options.restart' here.  This will give fast and type save
  // access to option values (internally).  In principle one could make all
  // options simply 'double' though, but that requires double conversions
  // during accessing options at run-time and disregard the intended types,
  // e.g., one would need to allow fractional values for actual integer
  // or boolean options.  Keeping the different types makes the output of
  // 'print' and 'usage' also more appealing (since correctly typed values
  // are printed).

#define OPTION(N,T,V,L,H,D) \
  T N;
  OPTIONS
#undef OPTION

  Options (Internal *);

  // This sets the value of an option assuming a 'long' command line
  // argument form.  The argument 'arg' thus should look like
  //
  //  "--<NAME>=<VAL>", "--<NAME>" or "--no-<NAME>"
  //
  // where 'NAME' is one of the option names above.  Returns 'true' if the
  // option was parsed and set correctly.  For boolean values we strictly
  // only allow "true", "false", "0" and "1" as "<VAL>" string.  For 'int'
  // type options we parse "<VAL>" with 'atoi' and force the resulting 'int'
  // value to the 'LO' and 'HI' range and similarly for 'double' type
  // options using 'atof'.  If the string is not a valid 'int' for 'int'
  // options or a 'double' value for 'double' options, then the function
  // returns 'false'.
  //
  bool set (const char * arg);

  // Interface to options using in a certain sense non-type-safe 'double'
  // values even for 'int' and 'bool'.  However, since 'double' can hold a
  // 'bool' and ' with typing, since a 'double' can hold a 'bool' as well an
  // 'int' value precisely, e.g., if the result of 'get' cast down again by
  // the client.
  //
  bool has (const char * name);
  double get (const char * name);
  bool set (const char * name, double);

  void print ();             // print current values in command line form
  static void usage ();      // print usage message for all options
};

};

#endif
<commit_msg>fixed comment<commit_after>#ifndef _options_hpp_INCLUDED
#define _options_hpp_INCLUDED

#ifndef NDEBUG
#define DBG 1
#else
#define DBG 0
#endif
#define OPTIONS \
\
/*     NAME             TYPE, VAL,LO, HI, USAGE */ \
\
OPTION(arena,            int,   3, 0,  3, "1=clause,2=var,3=queue") \
OPTION(binary,          bool,   1, 0,  1, "use binary proof format") \
OPTION(check,           bool, DBG, 0,  1, "save & check original CNF") \
OPTION(clim,             int,  -1, 0,1e9, "conflict limit (-1=none)") \
OPTION(dlim,             int,  -1, 0,1e9, "decision limit (-1=none)") \
OPTION(elim,            bool,   1, 0,  1, "bounded variable elimination") \
OPTION(elimclslim,       int,1000, 0,1e9, "ignore clauses of this size") \
OPTION(elimignore,    double, 0.0, 0,  1, "ignored fraction of variables") \
OPTION(eliminit,         int, 1e3, 0,1e9, "initial conflict limit") \
OPTION(elimint,          int, 1e4, 1,1e9, "initial conflict interval") \
OPTION(elimocclim,       int, 100, 0,1e9, "one sided occurrence limit") \
OPTION(elimroundsinit,   int,   5, 1,1e9, "initial number of rounds") \
OPTION(elimrounds,       int,   2, 1,1e9, "usual number of rounds") \
OPTION(emabumplast,   double,1e-5, 0,  1, "alpha bump last percentage") \
OPTION(emagluefast,   double,3e-2, 0,  1, "alpha fast glue") \
OPTION(emaglueslow,   double,1e-5, 0,  1, "alpha slow glue") \
OPTION(emajump,       double,1e-5, 0,  1, "alpha jump level") \
OPTION(emarestarteff, double,1e-3, 0,  1, "alpha restart effectiveness") \
OPTION(emarestartint, double,1e-5, 0,  1, "alpha actual restart interval") \
OPTION(emasize,       double,1e-5, 0,  1, "alpha learned clause size") \
OPTION(keepglue,         int,   2, 1,1e9, "glue kept learned clauses") \
OPTION(keepsize,         int,   3, 2,1e9, "size kept learned clauses") \
OPTION(leak,            bool,   1, 0,  1, "leak solver memory") \
OPTION(minimize,        bool,   1, 0,  1, "minimize learned clauses") \
OPTION(minimizedepth,    int,1000, 0,1e9, "minimization depth") \
OPTION(prefetch,        bool,   1, 0,  1, "prefetch watches") \
OPTION(profile,          int,   2, 0,  4, "profiling level") \
OPTION(quiet,           bool,   0, 0,  1, "disable all messages") \
OPTION(reduce,          bool,   1, 0,  1, "garbage collect clauses") \
OPTION(reduceglue,      bool,   1, 0,  1, "reduce on glue first") \
OPTION(reduceinc,        int, 300, 1,1e6, "reduce limit increment") \
OPTION(reduceinit,       int,2000, 0,1e6, "initial reduce limit") \
OPTION(restart,         bool,   1, 0,  1, "enable restarting") \
OPTION(restartint,       int,   4, 1,1e9, "restart base interval") \
OPTION(restartmargin, double, 1.1, 0, 10, "restart slow fast margin") \
OPTION(reusetrail,      bool,   1, 0,  1, "enable trail reuse") \
OPTION(shrink,          bool,   1, 0,  1, "shrink learned clause") \
OPTION(shrinkdepth,      int,   2, 0,1e9, "shrinking depth") \
OPTION(shrinkglue,       int,   5, 0,1e9, "glue limit for shrinking") \
OPTION(shrinksize,       int,  20, 0,1e9, "size limit for shrinking") \
OPTION(strengthen,      bool,   1, 0,  1, "strengthen during subsume") \
OPTION(sublast,          int,   5, 0,1e4, "eagerly subsume last") \
OPTION(subsume,         bool,   1, 0,  1, "enable clause subsumption") \
OPTION(subsumeinc,       int, 1e4, 1,1e9, "interval in conflicts") \
OPTION(subsumeinit,      int, 1e4, 0,1e9, "initial subsume limit") \
OPTION(subsumeocclim,    int, 100, 0,1e9, "watch list length limit") \
OPTION(trailbump,       bool,   1, 0,  1, "use trail + bumped") \
OPTION(trailbumplast, double,  40, 0,100, "trail bump last level limit") \
OPTION(trailbumprops, double, 200, 0,1e9, "trail bump propagation limit") \
OPTION(verbose,         bool,   0, 0,  1, "more verbose messages") \
OPTION(witness,         bool,   1, 0,  1, "print witness") \

namespace CaDiCaL {

class Internal;

class Options {

  Internal * internal;

  bool set (   int &, const char *, const char *, const    int, const    int);
  bool set (  bool &, const char *, const char *, const   bool, const   bool);
  bool set (double &, const char *, const char *, const double, const double);

  const char * match (const char *, const char *);

public:

  // Makes options directly accessible, e.g., for instance declares the
  // member 'bool Options.restart' here.  This will give fast and type save
  // access to option values (internally).  In principle one could make all
  // options simply 'double' though, but that requires double conversions
  // during accessing options at run-time and disregard the intended types,
  // e.g., one would need to allow fractional values for actual integer
  // or boolean options.  Keeping the different types makes the output of
  // 'print' and 'usage' also more appealing (since correctly typed values
  // are printed).

#define OPTION(N,T,V,L,H,D) \
  T N;
  OPTIONS
#undef OPTION

  Options (Internal *);

  // This sets the value of an option assuming a 'long' command line
  // argument form.  The argument 'arg' thus should look like
  //
  //  "--<NAME>=<VAL>", "--<NAME>" or "--no-<NAME>"
  //
  // where 'NAME' is one of the option names above.  Returns 'true' if the
  // option was parsed and set correctly.  For boolean values we strictly
  // only allow "true", "false", "0" and "1" as "<VAL>" string.  For 'int'
  // type options we parse "<VAL>" with 'atoi' and force the resulting 'int'
  // value to the 'LO' and 'HI' range and similarly for 'double' type
  // options using 'atof'.  If the string is not a valid 'int' for 'int'
  // options or a 'double' value for 'double' options, then the function
  // returns 'false'.
  //
  bool set (const char * arg);

  // Interface to options using in a certain sense non-type-safe 'double'
  // values even for 'int' and 'bool'.  However, 'double' can hold a 'bool'
  // as well an 'int' value precisely, e.g., if the result of 'get' is cast
  // down again by the client.  This would only fail for 64 byte 'long'.
  //
  bool has (const char * name);
  double get (const char * name);
  bool set (const char * name, double);

  void print ();             // print current values in command line form
  static void usage ();      // print usage message for all options
};

};

#endif
<|endoftext|>


<commit_before>#include "stdafx.h"
#include "meshinstance.h"
#include "buffers.h"
#include "shaderprogram.h"
#include "perfcounter.h"
#include "renderer.h"

namespace dukat
{
	MeshInstance::MeshInstance(void) : program(nullptr), visible(true)
	{
		uniform_buffers = std::make_unique<GenericBuffer>(1);
		glBindBufferBase(GL_UNIFORM_BUFFER, Renderer::UniformBuffer::MATERIAL, uniform_buffers->buffers[0]);
		glBufferData(GL_UNIFORM_BUFFER, sizeof(Material), &material, GL_STATIC_DRAW);
		// initialize default transform
		transform.update();
	}

	void MeshInstance::set_material(const Material& material)
	{
		this->material = material;
		glBindBufferBase(GL_UNIFORM_BUFFER, Renderer::UniformBuffer::MATERIAL, uniform_buffers->buffers[0]);
		glBufferData(GL_UNIFORM_BUFFER, sizeof(Material), &material, GL_STATIC_DRAW);
	}

	void MeshInstance::set_texture(Texture* texture, int index)
	{
		assert(index >= 0 && index < Renderer::max_texture_units);
		this->texture[index] = texture;
	}

	void MeshInstance::render(Renderer* renderer, const Matrix4& mat)
	{
		renderer->switch_shader(program);

		// combine entity and mesh model matrices
		Matrix4 model = mat * transform.mat_model;
		glUniformMatrix4fv(program->attr(Renderer::uf_model), 1, false, model.m);

		// Bind uniform buffers
		// TODO: use a single buffer for all materials?
		glUniformBlockBinding(program->id, glGetUniformBlockIndex(program->id, Renderer::uf_material), Renderer::UniformBuffer::MATERIAL);
		glBindBufferBase(GL_UNIFORM_BUFFER, Renderer::UniformBuffer::MATERIAL, uniform_buffers->buffers[0]);

		// Bind texture
		for (auto i = 0; i < Renderer::max_texture_units; i++)
		{
			if (texture[i] != nullptr)
			{
				glActiveTexture(GL_TEXTURE0 + i);
				glBindTexture(GL_TEXTURE_2D, texture[i]->id);
				std::stringstream ss;
				ss << "u_tex" << i;
				glUniform1i(program->attr(ss.str()), i);
			}
		}

		mesh->render(program);

		perfc.inc(PerformanceCounter::MESHES);
		perfc.inc(PerformanceCounter::VERTICES, mesh->vertex_count());
	}
}
<commit_msg>Fixed invalid binding of textures in meshinstance<commit_after>#include "stdafx.h"
#include "meshinstance.h"
#include "buffers.h"
#include "shaderprogram.h"
#include "perfcounter.h"
#include "renderer.h"

namespace dukat
{
	MeshInstance::MeshInstance(void) : program(nullptr), visible(true)
	{
		for (auto i = 0; i < Renderer::max_texture_units; i++)
			texture[i] = nullptr;
		uniform_buffers = std::make_unique<GenericBuffer>(1);
		glBindBufferBase(GL_UNIFORM_BUFFER, Renderer::UniformBuffer::MATERIAL, uniform_buffers->buffers[0]);
		glBufferData(GL_UNIFORM_BUFFER, sizeof(Material), &material, GL_STATIC_DRAW);
		// initialize default transform
		transform.update();
	}

	void MeshInstance::set_material(const Material& material)
	{
		this->material = material;
		glBindBufferBase(GL_UNIFORM_BUFFER, Renderer::UniformBuffer::MATERIAL, uniform_buffers->buffers[0]);
		glBufferData(GL_UNIFORM_BUFFER, sizeof(Material), &material, GL_STATIC_DRAW);
	}

	void MeshInstance::set_texture(Texture* texture, int index)
	{
		assert(index >= 0 && index < Renderer::max_texture_units);
		this->texture[index] = texture;
	}

	void MeshInstance::render(Renderer* renderer, const Matrix4& mat)
	{
		renderer->switch_shader(program);

		// combine entity and mesh model matrices
		Matrix4 model = mat * transform.mat_model;
		glUniformMatrix4fv(program->attr(Renderer::uf_model), 1, false, model.m);

		// Bind uniform buffers
		// TODO: use a single buffer for all materials?
		glUniformBlockBinding(program->id, glGetUniformBlockIndex(program->id, Renderer::uf_material), Renderer::UniformBuffer::MATERIAL);
		glBindBufferBase(GL_UNIFORM_BUFFER, Renderer::UniformBuffer::MATERIAL, uniform_buffers->buffers[0]);

		// Bind texture
		std::string id = "u_tex0";
		for (auto i = 0; i < Renderer::max_texture_units; i++)
		{
			if (texture[i] != nullptr)
			{
				glActiveTexture(GL_TEXTURE0 + i);
				glBindTexture(GL_TEXTURE_2D, texture[i]->id);
				id[5] = static_cast<char>(48 + i);
				glUniform1i(program->attr(id), i);
			}
		}

		mesh->render(program);

		perfc.inc(PerformanceCounter::MESHES);
		perfc.inc(PerformanceCounter::VERTICES, mesh->vertex_count());
	}
}
<|endoftext|>


<commit_before>#include <node.h>
#include <node_buffer.h>
#include <jpeglib.h>
#include <cstdlib>
#include <cstring>

#include "common.h"
#include "dynamic_jpeg_stack.h"
#include "jpeg_encoder.h"

using namespace v8;
using namespace node;

void
DynamicJpegStack::Initialize(v8::Handle<v8::Object> target)
{
    HandleScope scope;

    Local<FunctionTemplate> t = FunctionTemplate::New(New);
    t->InstanceTemplate()->SetInternalFieldCount(1);
    NODE_SET_PROTOTYPE_METHOD(t, "encode", JpegEncode);
    NODE_SET_PROTOTYPE_METHOD(t, "push", Push);
    NODE_SET_PROTOTYPE_METHOD(t, "reset", Reset);
    NODE_SET_PROTOTYPE_METHOD(t, "setBackground", SetBackground);
    NODE_SET_PROTOTYPE_METHOD(t, "setQuality", SetQuality);
    NODE_SET_PROTOTYPE_METHOD(t, "dimensions", Dimensions);
    target->Set(String::NewSymbol("DynamicJpegStack"), t->GetFunction());
}

DynamicJpegStack::DynamicJpegStack(buffer_type bbuf_type) :
    quality(60), buf_type(bbuf_type),
    dyn_rect(-1, -1, 0, 0),
    bg_width(0), bg_height(0), data(NULL) {}

DynamicJpegStack::~DynamicJpegStack()
{
    free(data);
}

void
DynamicJpegStack::UpdateOptimalDimension(int x, int y, int w, int h)
{
    if (dyn_rect.x == -1 || x < dyn_rect.x)
        dyn_rect.x = x;
    if (dyn_rect.y == -1 || y < dyn_rect.y)
        dyn_rect.y = y;

    if (dyn_rect.w == 0)
        dyn_rect.w = w;
    if (dyn_rect.h == 0)
        dyn_rect.h = h;

    int ww = w - (dyn_rect.w - (x - dyn_rect.x));
    if (ww > 0)
        dyn_rect.w += ww;

    int hh = h - (dyn_rect.h - (y - dyn_rect.y));
    if (hh > 0)
        dyn_rect.h += hh;
}

Handle<Value>
DynamicJpegStack::JpegEncode()
{
    HandleScope scope;

    try {
        JpegEncoder jpeg_encoder(data, bg_width, bg_height, quality, BUF_RGB);
        jpeg_encoder.setRect(Rect(dyn_rect.x, dyn_rect.y, dyn_rect.w, dyn_rect.h));
        jpeg_encoder.encode();
        return scope.Close(
            Encode(jpeg_encoder.get_jpeg(), jpeg_encoder.get_jpeg_len(), BINARY)
        );
    }
    catch (const char *err) {
        return VException(err);
    }
}

void
DynamicJpegStack::Push(unsigned char *data_buf, int x, int y, int w, int h)
{
    UpdateOptimalDimension(x, y, w, h);

    int start = y*bg_width*3 + x*3;

    if (buf_type == BUF_RGB) {
        for (int i = 0; i < h; i++) {
            unsigned char *datap = &data[start + i*bg_width*3];
            for (int j = 0; j < w; j++) {
                *datap++ = *data_buf++;
                *datap++ = *data_buf++;
                *datap++ = *data_buf++;
            }
        }
    }
    else if (buf_type == BUF_BGR) {
        for (int i = 0; i < h; i++) {
            unsigned char *datap = &data[start + i*bg_width*3];
            for (int j = 0; j < w; j++) {
                *datap++ = *(data_buf+2);
                *datap++ = *(data_buf+1);
                *datap++ = *data_buf;
                data_buf+=3;
            }
        }
    }
    else if (buf_type == BUF_RGBA) {
        for (int i = 0; i < h; i++) {
            unsigned char *datap = &data[start + i*bg_width*3];
            for (int j = 0; j < w; j++) {
                *datap++ = *data_buf++;
                *datap++ = *data_buf++;
                *datap++ = *data_buf++;
                data_buf++;
            }
        }
    }
    else if (buf_type == BUF_BGRA) {
        for (int i = 0; i < h; i++) {
            unsigned char *datap = &data[start + i*bg_width*3];
            for (int j = 0; j < w; j++) {
                *datap++ = *(data_buf+2);
                *datap++ = *(data_buf+1);
                *datap++ = *data_buf;
                data_buf += 4;
            }
        }
    }
}

void
DynamicJpegStack::SetBackground(unsigned char *data_buf, int w, int h)
{
    if (data) {
        free(data);
        data = NULL;
    }

    if (buf_type == BUF_RGB) {
        data = (unsigned char *)malloc(sizeof(*data)*w*h*3);
        if (!data) throw "malloc failed in DynamicJpegStack::SetBackground";
        memcpy(data, data_buf, w*h*3);
    }
    else if (buf_type == BUF_BGR) {
        data = bgr_to_rgb(data_buf, w*h*3);
        if (!data) throw "malloc failed in DynamicJpegStack::SetBackground";
    }
    else if (buf_type == BUF_RGBA) {
        data = rgba_to_rgb(data_buf, w*h*4);
        if (!data) throw "malloc failed in DynamicJpegStack::SetBackground";
    }
    else if (buf_type == BUF_BGRA) {
        data = bgra_to_rgb(data_buf, w*h*4);
        if (!data) throw "malloc failed in DynamicJpegStack::SetBackground";
    }
    bg_width = w;
    bg_height = h;
}

void
DynamicJpegStack::SetQuality(int q)
{
    quality = q;
}

void
DynamicJpegStack::Reset()
{
    dyn_rect = Rect(-1, -1, 0, 0);
}

Handle<Value>
DynamicJpegStack::Dimensions()
{
    HandleScope scope;

    Local<Object> dim = Object::New();
    dim->Set(String::NewSymbol("x"), Integer::New(dyn_rect.x));
    dim->Set(String::NewSymbol("y"), Integer::New(dyn_rect.y));
    dim->Set(String::NewSymbol("width"), Integer::New(dyn_rect.w));
    dim->Set(String::NewSymbol("height"), Integer::New(dyn_rect.h));

    return scope.Close(dim);
}

Handle<Value>
DynamicJpegStack::New(const Arguments &args)
{
    HandleScope scope;

    if (args.Length() > 1)
        return VException("One argument max - buffer type.");

    buffer_type buf_type = BUF_RGB;
    if (args.Length() == 1) {
        if (!args[0]->IsString())
            return VException("First argument must be a string. Either 'rgb', 'bgr', 'rgba' or 'bgra'.");

        String::AsciiValue bt(args[0]->ToString());
        if (!(str_eq(*bt, "rgb") || str_eq(*bt, "bgr") ||
            str_eq(*bt, "rgba") || str_eq(*bt, "bgra")))
        {
            return VException("Buffer type must be 'rgb', 'bgr', 'rgba' or 'bgra'.");
        }

        if (str_eq(*bt, "rgb"))
            buf_type = BUF_RGB;
        else if (str_eq(*bt, "bgr"))
            buf_type = BUF_BGR;
        else if (str_eq(*bt, "rgba"))
            buf_type = BUF_RGBA;
        else if (str_eq(*bt, "bgra"))
            buf_type = BUF_BGRA;
        else
            return VException("Buffer type wasn't 'rgb', 'bgr', 'rgba' or 'bgra'.");
    }

    DynamicJpegStack *jpeg = new DynamicJpegStack(buf_type);
    jpeg->Wrap(args.This());
    return args.This();
}

Handle<Value>
DynamicJpegStack::JpegEncode(const Arguments &args)
{
    HandleScope scope;
    DynamicJpegStack *jpeg = ObjectWrap::Unwrap<DynamicJpegStack>(args.This());
    return jpeg->JpegEncode();
}

Handle<Value>
DynamicJpegStack::Push(const Arguments &args)
{
    HandleScope scope;

    if (args.Length() != 5)
        return VException("Five arguments required - buffer, x, y, width, height.");

    if (!Buffer::HasInstance(args[0]))
        return VException("First argument must be Buffer.");
    if (!args[1]->IsInt32())
        return VException("Second argument must be integer x.");
    if (!args[2]->IsInt32())
        return VException("Third argument must be integer y.");
    if (!args[3]->IsInt32())
        return VException("Fourth argument must be integer w.");
    if (!args[4]->IsInt32())
        return VException("Fifth argument must be integer h.");

    DynamicJpegStack *jpeg = ObjectWrap::Unwrap<DynamicJpegStack>(args.This());

    if (!jpeg->data)
        return VException("No background has been set, use setBackground or setSolidBackground to set.");

    Buffer *data_buf = ObjectWrap::Unwrap<Buffer>(args[0]->ToObject());
    int x = args[1]->Int32Value();
    int y = args[2]->Int32Value();
    int w = args[3]->Int32Value();
    int h = args[4]->Int32Value();

    if (x < 0)
        return VException("Coordinate x smaller than 0.");
    if (y < 0)
        return VException("Coordinate y smaller than 0.");
    if (w < 0)
        return VException("Width smaller than 0.");
    if (h < 0)
        return VException("Height smaller than 0.");
    if (x >= jpeg->bg_width)
        return VException("Coordinate x exceeds DynamicJpegStack's background dimensions.");
    if (y >= jpeg->bg_height)
        return VException("Coordinate y exceeds DynamicJpegStack's background dimensions.");
    if (x+w > jpeg->bg_width)
        return VException("Pushed fragment exceeds DynamicJpegStack's width.");
    if (y+h > jpeg->bg_height)
        return VException("Pushed fragment exceeds DynamicJpegStack's height.");

    jpeg->Push((unsigned char *)data_buf->data(), x, y, w, h);

    return Undefined();
}

Handle<Value>
DynamicJpegStack::SetBackground(const Arguments &args)
{
    HandleScope scope;

    if (args.Length() != 3)
        return VException("Four arguments required - buffer, width, height");
    if (!Buffer::HasInstance(args[0]))
        return VException("First argument must be Buffer.");
    if (!args[1]->IsInt32())
        return VException("Second argument must be integer width.");
    if (!args[2]->IsInt32())
        return VException("Third argument must be integer height.");

    DynamicJpegStack *jpeg = ObjectWrap::Unwrap<DynamicJpegStack>(args.This());
    Buffer *data_buf = ObjectWrap::Unwrap<Buffer>(args[0]->ToObject());
    int w = args[1]->Int32Value();
    int h = args[2]->Int32Value();

    if (w < 0)
        return VException("Coordinate x smaller than 0.");
    if (h < 0)
        return VException("Coordinate y smaller than 0.");

    try {
        jpeg->SetBackground((unsigned char *)data_buf->data(), w, h);
    }
    catch (const char *err) {
        return VException(err);
    }

    return Undefined();
}

Handle<Value>
DynamicJpegStack::Reset(const Arguments &args)
{
    HandleScope scope;

    DynamicJpegStack *jpeg = ObjectWrap::Unwrap<DynamicJpegStack>(args.This());
    jpeg->Reset();
    return Undefined();
}

Handle<Value>
DynamicJpegStack::Dimensions(const Arguments &args)
{
    HandleScope scope;

    DynamicJpegStack *jpeg = ObjectWrap::Unwrap<DynamicJpegStack>(args.This());
    return scope.Close(jpeg->Dimensions());
}

Handle<Value>
DynamicJpegStack::SetQuality(const Arguments &args)
{
    HandleScope scope;

    if (args.Length() != 1)
        return VException("One argument required - quality");

    if (!args[0]->IsInt32())
        return VException("First argument must be integer quality");

    int q = args[0]->Int32Value();

    if (q < 0)
        return VException("Quality must be greater or equal to 0.");
    if (q > 100)
        return VException("Quality must be less than or equal to 100.");

    DynamicJpegStack *jpeg = ObjectWrap::Unwrap<DynamicJpegStack>(args.This());
    jpeg->SetQuality(q);

    return Undefined();
}

<commit_msg>make this a switch/case instead of if/else if/else if/..., too<commit_after>#include <node.h>
#include <node_buffer.h>
#include <jpeglib.h>
#include <cstdlib>
#include <cstring>

#include "common.h"
#include "dynamic_jpeg_stack.h"
#include "jpeg_encoder.h"

using namespace v8;
using namespace node;

void
DynamicJpegStack::Initialize(v8::Handle<v8::Object> target)
{
    HandleScope scope;

    Local<FunctionTemplate> t = FunctionTemplate::New(New);
    t->InstanceTemplate()->SetInternalFieldCount(1);
    NODE_SET_PROTOTYPE_METHOD(t, "encode", JpegEncode);
    NODE_SET_PROTOTYPE_METHOD(t, "push", Push);
    NODE_SET_PROTOTYPE_METHOD(t, "reset", Reset);
    NODE_SET_PROTOTYPE_METHOD(t, "setBackground", SetBackground);
    NODE_SET_PROTOTYPE_METHOD(t, "setQuality", SetQuality);
    NODE_SET_PROTOTYPE_METHOD(t, "dimensions", Dimensions);
    target->Set(String::NewSymbol("DynamicJpegStack"), t->GetFunction());
}

DynamicJpegStack::DynamicJpegStack(buffer_type bbuf_type) :
    quality(60), buf_type(bbuf_type),
    dyn_rect(-1, -1, 0, 0),
    bg_width(0), bg_height(0), data(NULL) {}

DynamicJpegStack::~DynamicJpegStack()
{
    free(data);
}

void
DynamicJpegStack::UpdateOptimalDimension(int x, int y, int w, int h)
{
    if (dyn_rect.x == -1 || x < dyn_rect.x)
        dyn_rect.x = x;
    if (dyn_rect.y == -1 || y < dyn_rect.y)
        dyn_rect.y = y;

    if (dyn_rect.w == 0)
        dyn_rect.w = w;
    if (dyn_rect.h == 0)
        dyn_rect.h = h;

    int ww = w - (dyn_rect.w - (x - dyn_rect.x));
    if (ww > 0)
        dyn_rect.w += ww;

    int hh = h - (dyn_rect.h - (y - dyn_rect.y));
    if (hh > 0)
        dyn_rect.h += hh;
}

Handle<Value>
DynamicJpegStack::JpegEncode()
{
    HandleScope scope;

    try {
        JpegEncoder jpeg_encoder(data, bg_width, bg_height, quality, BUF_RGB);
        jpeg_encoder.setRect(Rect(dyn_rect.x, dyn_rect.y, dyn_rect.w, dyn_rect.h));
        jpeg_encoder.encode();
        return scope.Close(
            Encode(jpeg_encoder.get_jpeg(), jpeg_encoder.get_jpeg_len(), BINARY)
        );
    }
    catch (const char *err) {
        return VException(err);
    }
}

void
DynamicJpegStack::Push(unsigned char *data_buf, int x, int y, int w, int h)
{
    UpdateOptimalDimension(x, y, w, h);

    int start = y*bg_width*3 + x*3;

    switch (buf_type) {
    case BUF_RGB:
        for (int i = 0; i < h; i++) {
            unsigned char *datap = &data[start + i*bg_width*3];
            for (int j = 0; j < w; j++) {
                *datap++ = *data_buf++;
                *datap++ = *data_buf++;
                *datap++ = *data_buf++;
            }
        }
        break;

    case BUF_BGR:
        for (int i = 0; i < h; i++) {
            unsigned char *datap = &data[start + i*bg_width*3];
            for (int j = 0; j < w; j++) {
                *datap++ = *(data_buf+2);
                *datap++ = *(data_buf+1);
                *datap++ = *data_buf;
                data_buf+=3;
            }
        }
        break;

    case BUF_RGBA:
        for (int i = 0; i < h; i++) {
            unsigned char *datap = &data[start + i*bg_width*3];
            for (int j = 0; j < w; j++) {
                *datap++ = *data_buf++;
                *datap++ = *data_buf++;
                *datap++ = *data_buf++;
                data_buf++;
            }
        }
        break;

    case BUF_BGRA:
        for (int i = 0; i < h; i++) {
            unsigned char *datap = &data[start + i*bg_width*3];
            for (int j = 0; j < w; j++) {
                *datap++ = *(data_buf+2);
                *datap++ = *(data_buf+1);
                *datap++ = *data_buf;
                data_buf += 4;
            }
        }
        break;

    default:
        throw "Unexpected buf_type in DynamicJpegStack::Push";
    }
}

void
DynamicJpegStack::SetBackground(unsigned char *data_buf, int w, int h)
{
    if (data) {
        free(data);
        data = NULL;
    }

    switch (buf_type) {
    case BUF_RGB:
        data = (unsigned char *)malloc(sizeof(*data)*w*h*3);
        if (!data) throw "malloc failed in DynamicJpegStack::SetBackground";
        memcpy(data, data_buf, w*h*3);
        break;

    case BUF_BGR:
        data = bgr_to_rgb(data_buf, w*h*3);
        if (!data) throw "malloc failed in DynamicJpegStack::SetBackground";
        break;

    case BUF_RGBA:
        data = rgba_to_rgb(data_buf, w*h*4);
        if (!data) throw "malloc failed in DynamicJpegStack::SetBackground";
        break;

    case BUF_BGRA:
        data = bgra_to_rgb(data_buf, w*h*4);
        if (!data) throw "malloc failed in DynamicJpegStack::SetBackground";
        break;

    default:
        throw "Unexpected buf_type in DynamicJpegStack::SetBackground";
    }
    bg_width = w;
    bg_height = h;
}

void
DynamicJpegStack::SetQuality(int q)
{
    quality = q;
}

void
DynamicJpegStack::Reset()
{
    dyn_rect = Rect(-1, -1, 0, 0);
}

Handle<Value>
DynamicJpegStack::Dimensions()
{
    HandleScope scope;

    Local<Object> dim = Object::New();
    dim->Set(String::NewSymbol("x"), Integer::New(dyn_rect.x));
    dim->Set(String::NewSymbol("y"), Integer::New(dyn_rect.y));
    dim->Set(String::NewSymbol("width"), Integer::New(dyn_rect.w));
    dim->Set(String::NewSymbol("height"), Integer::New(dyn_rect.h));

    return scope.Close(dim);
}

Handle<Value>
DynamicJpegStack::New(const Arguments &args)
{
    HandleScope scope;

    if (args.Length() > 1)
        return VException("One argument max - buffer type.");

    buffer_type buf_type = BUF_RGB;
    if (args.Length() == 1) {
        if (!args[0]->IsString())
            return VException("First argument must be a string. Either 'rgb', 'bgr', 'rgba' or 'bgra'.");

        String::AsciiValue bt(args[0]->ToString());
        if (!(str_eq(*bt, "rgb") || str_eq(*bt, "bgr") ||
            str_eq(*bt, "rgba") || str_eq(*bt, "bgra")))
        {
            return VException("Buffer type must be 'rgb', 'bgr', 'rgba' or 'bgra'.");
        }

        if (str_eq(*bt, "rgb"))
            buf_type = BUF_RGB;
        else if (str_eq(*bt, "bgr"))
            buf_type = BUF_BGR;
        else if (str_eq(*bt, "rgba"))
            buf_type = BUF_RGBA;
        else if (str_eq(*bt, "bgra"))
            buf_type = BUF_BGRA;
        else
            return VException("Buffer type wasn't 'rgb', 'bgr', 'rgba' or 'bgra'.");
    }

    DynamicJpegStack *jpeg = new DynamicJpegStack(buf_type);
    jpeg->Wrap(args.This());
    return args.This();
}

Handle<Value>
DynamicJpegStack::JpegEncode(const Arguments &args)
{
    HandleScope scope;
    DynamicJpegStack *jpeg = ObjectWrap::Unwrap<DynamicJpegStack>(args.This());
    return jpeg->JpegEncode();
}

Handle<Value>
DynamicJpegStack::Push(const Arguments &args)
{
    HandleScope scope;

    if (args.Length() != 5)
        return VException("Five arguments required - buffer, x, y, width, height.");

    if (!Buffer::HasInstance(args[0]))
        return VException("First argument must be Buffer.");
    if (!args[1]->IsInt32())
        return VException("Second argument must be integer x.");
    if (!args[2]->IsInt32())
        return VException("Third argument must be integer y.");
    if (!args[3]->IsInt32())
        return VException("Fourth argument must be integer w.");
    if (!args[4]->IsInt32())
        return VException("Fifth argument must be integer h.");

    DynamicJpegStack *jpeg = ObjectWrap::Unwrap<DynamicJpegStack>(args.This());

    if (!jpeg->data)
        return VException("No background has been set, use setBackground or setSolidBackground to set.");

    Buffer *data_buf = ObjectWrap::Unwrap<Buffer>(args[0]->ToObject());
    int x = args[1]->Int32Value();
    int y = args[2]->Int32Value();
    int w = args[3]->Int32Value();
    int h = args[4]->Int32Value();

    if (x < 0)
        return VException("Coordinate x smaller than 0.");
    if (y < 0)
        return VException("Coordinate y smaller than 0.");
    if (w < 0)
        return VException("Width smaller than 0.");
    if (h < 0)
        return VException("Height smaller than 0.");
    if (x >= jpeg->bg_width)
        return VException("Coordinate x exceeds DynamicJpegStack's background dimensions.");
    if (y >= jpeg->bg_height)
        return VException("Coordinate y exceeds DynamicJpegStack's background dimensions.");
    if (x+w > jpeg->bg_width)
        return VException("Pushed fragment exceeds DynamicJpegStack's width.");
    if (y+h > jpeg->bg_height)
        return VException("Pushed fragment exceeds DynamicJpegStack's height.");

    jpeg->Push((unsigned char *)data_buf->data(), x, y, w, h);

    return Undefined();
}

Handle<Value>
DynamicJpegStack::SetBackground(const Arguments &args)
{
    HandleScope scope;

    if (args.Length() != 3)
        return VException("Four arguments required - buffer, width, height");
    if (!Buffer::HasInstance(args[0]))
        return VException("First argument must be Buffer.");
    if (!args[1]->IsInt32())
        return VException("Second argument must be integer width.");
    if (!args[2]->IsInt32())
        return VException("Third argument must be integer height.");

    DynamicJpegStack *jpeg = ObjectWrap::Unwrap<DynamicJpegStack>(args.This());
    Buffer *data_buf = ObjectWrap::Unwrap<Buffer>(args[0]->ToObject());
    int w = args[1]->Int32Value();
    int h = args[2]->Int32Value();

    if (w < 0)
        return VException("Coordinate x smaller than 0.");
    if (h < 0)
        return VException("Coordinate y smaller than 0.");

    try {
        jpeg->SetBackground((unsigned char *)data_buf->data(), w, h);
    }
    catch (const char *err) {
        return VException(err);
    }

    return Undefined();
}

Handle<Value>
DynamicJpegStack::Reset(const Arguments &args)
{
    HandleScope scope;

    DynamicJpegStack *jpeg = ObjectWrap::Unwrap<DynamicJpegStack>(args.This());
    jpeg->Reset();
    return Undefined();
}

Handle<Value>
DynamicJpegStack::Dimensions(const Arguments &args)
{
    HandleScope scope;

    DynamicJpegStack *jpeg = ObjectWrap::Unwrap<DynamicJpegStack>(args.This());
    return scope.Close(jpeg->Dimensions());
}

Handle<Value>
DynamicJpegStack::SetQuality(const Arguments &args)
{
    HandleScope scope;

    if (args.Length() != 1)
        return VException("One argument required - quality");

    if (!args[0]->IsInt32())
        return VException("First argument must be integer quality");

    int q = args[0]->Int32Value();

    if (q < 0)
        return VException("Quality must be greater or equal to 0.");
    if (q > 100)
        return VException("Quality must be less than or equal to 100.");

    DynamicJpegStack *jpeg = ObjectWrap::Unwrap<DynamicJpegStack>(args.This());
    jpeg->SetQuality(q);

    return Undefined();
}

<|endoftext|>