<commit_before>// Copyright (C) 2017 Elviss Strazdins
// This file is part of the Ouzel engine.

#include <algorithm>
#include "TextureResource.h"
#include "Renderer.h"
#include "core/Engine.h"
#include "math/MathUtils.h"

namespace ouzel
{
    namespace graphics
    {
        TextureResource::TextureResource()
        {
        }

        TextureResource::~TextureResource()
        {
        }

        bool TextureResource::init(const Size2& newSize,
                                   bool newDynamic,
                                   bool newMipmaps,
                                   bool newRenderTarget,
                                   uint32_t newSampleCount,
                                   bool newDepth,
                                   PixelFormat newPixelFormat)
        {
            std::lock_guard<std::mutex> lock(uploadMutex);

            dynamic = newDynamic;
            mipmaps = newMipmaps;
            renderTarget = newRenderTarget;
            sampleCount = newSampleCount;
            depth = newDepth;
            pixelFormat = newPixelFormat;

            if (!calculateSizes(newSize))
            {
                return false;
            }

            dirty |= DIRTY_DATA | DIRTY_PARAMETERS;

            return true;
        }

        bool TextureResource::initFromBuffer(const std::vector<uint8_t>& newData,
                                             const Size2& newSize,
                                             bool newDynamic,
                                             bool newMipmaps,
                                             PixelFormat newPixelFormat)
        {
            std::lock_guard<std::mutex> lock(uploadMutex);

            dynamic = newDynamic;
            mipmaps = newMipmaps;
            renderTarget = false;
            sampleCount = 1;
            depth = false;
            pixelFormat = newPixelFormat;

            if (!calculateData(newData, newSize))
            {
                return false;
            }

            dirty |= DIRTY_DATA | DIRTY_PARAMETERS;

            return true;
        }

        bool TextureResource::setSize(const Size2& newSize)
        {
            std::lock_guard<std::mutex> lock(uploadMutex);

            if (!dynamic)
            {
                return false;
            }

            if (newSize.v[0] <= 0.0f || newSize.v[1] <= 0.0f)
            {
                return false;
            }

            if (!calculateSizes(newSize))
            {
                return false;
            }

            dirty |= DIRTY_DATA;

            return true;
        }

        bool TextureResource::setData(const std::vector<uint8_t>& newData, const Size2& newSize)
        {
            std::lock_guard<std::mutex> lock(uploadMutex);

            if (!dynamic)
            {
                return false;
            }

            if (newSize.v[0] <= 0.0f || newSize.v[1] <= 0.0f)
            {
                return false;
            }

            if (!calculateData(newData, newSize))
            {
                return false;
            }

            dirty |= DIRTY_DATA;

            return true;
        }

        bool TextureResource::calculateSizes(const Size2& newSize)
        {
            levels.clear();
            size = newSize;

            uint32_t newWidth = static_cast<uint32_t>(newSize.v[0]);
            uint32_t newHeight = static_cast<uint32_t>(newSize.v[1]);

            uint32_t pixelSize = getPixelSize(pixelFormat);
            uint32_t pitch = newWidth * pixelSize;
            levels.push_back({newSize, pitch, std::vector<uint8_t>()});

            if (mipmaps && !renderTarget && (sharedEngine->getRenderer()->isNPOTTexturesSupported() || (isPOT(newWidth) && isPOT(newHeight))))
            {
                uint32_t bufferSize = newWidth * newHeight * pixelSize;

                if (newWidth == 1)
                {
                    bufferSize *= 2;
                }
                if (newHeight == 1)
                {
                    bufferSize *= 2;
                }

                while (newWidth >= 2 && newHeight >= 2)
                {
                    newWidth >>= 1;
                    newHeight >>= 1;

                    Size2 mipMapSize = Size2(static_cast<float>(newWidth), static_cast<float>(newHeight));
                    pitch = newWidth * pixelSize;

                    levels.push_back({mipMapSize, pitch, std::vector<uint8_t>()});
                }

                if (newWidth > newHeight)
                {
                    for (; newWidth >= 2;)
                    {
                        newWidth >>= 1;

                        Size2 mipMapSize = Size2(static_cast<float>(newWidth), static_cast<float>(newHeight));
                        pitch = newWidth * pixelSize;

                        levels.push_back({mipMapSize, pitch, std::vector<uint8_t>()});
                    }
                }
                else
                {
                    for (; newHeight >= 2;)
                    {
                        newHeight >>= 1;

                        Size2 mipMapSize = Size2(static_cast<float>(newWidth), static_cast<float>(newHeight));
                        levels.push_back({mipMapSize, pitch, std::vector<uint8_t>()});
                    }
                }
            }

            return true;
        }

        static void imageRGB8Downsample2x2(uint32_t width, uint32_t height, uint32_t pitch, const uint8_t* src, uint8_t* dst)
        {
            const uint32_t dstWidth  = width / 2;
            const uint32_t dstHeight = height / 2;

            if (dstWidth == 0 ||  dstHeight == 0)
            {
                return;
            }

            for (uint32_t y = 0, ystep = pitch * 2; y < dstHeight; ++y, src += ystep)
            {
                const uint8_t* rgb = src;
                for (uint32_t x = 0; x < dstWidth; ++x, rgb += 6, dst += 3)
                {
                    float pixels = 0.0f;
                    float r = 0, g = 0, b = 0.0;

                    r += powf(rgb[0], 2.2f);
                    g += powf(rgb[1], 2.2f);
                    b += powf(rgb[2], 2.2f);
                    pixels += 1.0f;

                    r += powf(rgb[3], 2.2f);
                    g += powf(rgb[4], 2.2f);
                    b += powf(rgb[5], 2.2f);
                    pixels += 1.0f;

                    r += powf(rgb[pitch + 0], 2.2f);
                    g += powf(rgb[pitch + 1], 2.2f);
                    b += powf(rgb[pitch + 2], 2.2f);
                    pixels += 1.0f;

                    r += powf(rgb[pitch + 3], 2.2f);
                    g += powf(rgb[pitch + 4], 2.2f);
                    b += powf(rgb[pitch + 5], 2.2f);
                    pixels += 1.0f;

                    if (pixels > 0.0f)
                    {
                        r /= pixels;
                        g /= pixels;
                        b /= pixels;
                    }
                    else
                    {
                        r = g = b = 0.0f;
                    }

                    r = powf(r, 1.0f / 2.2f);
                    g = powf(g, 1.0f / 2.2f);
                    b = powf(b, 1.0f / 2.2f);
                    dst[0] = static_cast<uint8_t>(r);
                    dst[1] = static_cast<uint8_t>(g);
                    dst[2] = static_cast<uint8_t>(b);
                }
            }
        }

        static void imageRGBA8Downsample2x2(uint32_t width, uint32_t height, uint32_t pitch, const uint8_t* src, uint8_t* dst)
        {
            const uint32_t dstWidth  = width / 2;
            const uint32_t dstHeight = height / 2;

            if (dstWidth == 0 ||  dstHeight == 0)
            {
                return;
            }

            for (uint32_t y = 0, ystep = pitch * 2; y < dstHeight; ++y, src += ystep)
            {
                const uint8_t* rgba = src;
                for (uint32_t x = 0; x < dstWidth; ++x, rgba += 8, dst += 4)
                {
                    float pixels = 0.0f;
                    float r = 0, g = 0, b = 0, a = 0.0f;

                    if (rgba[3] > 0)
                    {
                        r += powf(rgba[0], 2.2f);
                        g += powf(rgba[1], 2.2f);
                        b += powf(rgba[2], 2.2f);
                        pixels += 1.0f;
                    }
                    a = rgba[3];

                    if (rgba[7] > 0)
                    {
                        r += powf(rgba[4], 2.2f);
                        g += powf(rgba[5], 2.2f);
                        b += powf(rgba[6], 2.2f);
                        pixels += 1.0f;
                    }
                    a += rgba[7];

                    if (rgba[pitch + 3] > 0)
                    {
                        r += powf(rgba[pitch + 0], 2.2f);
                        g += powf(rgba[pitch + 1], 2.2f);
                        b += powf(rgba[pitch + 2], 2.2f);
                        pixels += 1.0f;
                    }
                    a += rgba[pitch + 3];

                    if (rgba[pitch + 7] > 0)
                    {
                        r += powf(rgba[pitch + 4], 2.2f);
                        g += powf(rgba[pitch + 5], 2.2f);
                        b += powf(rgba[pitch + 6], 2.2f);
                        pixels += 1.0f;
                    }
                    a += rgba[pitch + 7];

                    if (pixels > 0.0f)
                    {
                        r /= pixels;
                        g /= pixels;
                        b /= pixels;
                    }
                    else
                    {
                        r = g = b = 0.0f;
                    }

                    r = powf(r, 1.0f / 2.2f);
                    g = powf(g, 1.0f / 2.2f);
                    b = powf(b, 1.0f / 2.2f);
                    a *= 0.25f;
                    dst[0] = static_cast<uint8_t>(r);
                    dst[1] = static_cast<uint8_t>(g);
                    dst[2] = static_cast<uint8_t>(b);
                    dst[3] = static_cast<uint8_t>(a);
                }
            }
        }

        bool TextureResource::calculateData(const std::vector<uint8_t>& newData, const Size2& newSize)
        {
            levels.clear();
            size = newSize;

            uint32_t newWidth = static_cast<uint32_t>(newSize.v[0]);
            uint32_t newHeight = static_cast<uint32_t>(newSize.v[1]);

            uint32_t pixelSize = getPixelSize(pixelFormat);
            uint32_t pitch = newWidth * pixelSize;
            levels.push_back({newSize, pitch, newData});

            if (mipmaps && !renderTarget && (sharedEngine->getRenderer()->isNPOTTexturesSupported() || (isPOT(newWidth) && isPOT(newHeight))))
            {
                uint32_t bufferSize = newWidth * newHeight * pixelSize;

                if (newWidth == 1)
                {
                    bufferSize *= 2;
                }
                if (newHeight == 1)
                {
                    bufferSize *= 2;
                }

                std::vector<uint8_t> mipMapData(bufferSize);
                std::copy(newData.begin(),
                          newData.begin() + static_cast<std::vector<uint8_t>::difference_type>(newWidth * newHeight * pixelSize),
                          mipMapData.begin());

                while (newWidth >= 2 && newHeight >= 2)
                {
                    if (pixelFormat == PixelFormat::RGBA8_UINT)
                    {
                        imageRGBA8Downsample2x2(newWidth, newHeight, pitch, mipMapData.data(), mipMapData.data());
                    }
                    else if (pixelFormat == PixelFormat::RGB8_UINT)
                    {
                        imageRGBA8Downsample2x2(newWidth, 2, pitch, mipMapData.data(), mipMapData.data());
                    }
                    else
                    {
                        // TODO: implement downsampling of other pixel formats
                    }

                    newWidth >>= 1;
                    newHeight >>= 1;

                    Size2 mipMapSize = Size2(static_cast<float>(newWidth), static_cast<float>(newHeight));
                    pitch = newWidth * pixelSize;

                    levels.push_back({mipMapSize, pitch, mipMapData});
                }

                if (newWidth > newHeight) // height is 2
                {
                    for (; newWidth >= 2;)
                    {
                        std::copy(mipMapData.begin(),
                                  mipMapData.begin() + newWidth * pixelSize,
                                  mipMapData.begin() + newWidth * pixelSize);

                        if (pixelFormat == PixelFormat::RGBA8_UINT)
                        {
                            imageRGBA8Downsample2x2(newWidth, 2, pitch, mipMapData.data(), mipMapData.data());
                        }
                        else if (pixelFormat == PixelFormat::RGB8_UINT)
                        {
                            imageRGBA8Downsample2x2(newWidth, 2, pitch, mipMapData.data(), mipMapData.data());
                        }
                        else
                        {
                            // TODO: implement downsampling of other pixel formats
                        }

                        newWidth >>= 1;

                        Size2 mipMapSize = Size2(static_cast<float>(newWidth), static_cast<float>(newHeight));
                        pitch = newWidth * pixelSize;

                        levels.push_back({mipMapSize, pitch, mipMapData});
                    }
                }
                else // width is 2
                {
                    for (; newHeight >= 2;)
                    {
                        for (int32_t i = static_cast<int32_t>(newHeight) - 1; i >= 0; --i)
                        {
                            std::copy(mipMapData.begin() + static_cast<uint32_t>(i * 2) * pixelSize,
                                      mipMapData.begin() + static_cast<uint32_t>(i * 2) * pixelSize + pixelSize,
                                      mipMapData.begin() + static_cast<uint32_t>(i) * pixelSize);

                            std::copy(mipMapData.begin() + static_cast<uint32_t>(i * 2 + 1) * pixelSize,
                                      mipMapData.begin() + static_cast<uint32_t>(i * 2 + 1) * pixelSize + pixelSize,
                                      mipMapData.begin() + static_cast<uint32_t>(i) * pixelSize);
                        }

                        if (pixelFormat == PixelFormat::RGBA8_UINT)
                        {
                            imageRGBA8Downsample2x2(2, newHeight, 8, mipMapData.data(), mipMapData.data());
                        }
                        else if (pixelFormat == PixelFormat::RGB8_UINT)
                        {
                            imageRGB8Downsample2x2(2, newHeight, 8, mipMapData.data(), mipMapData.data());
                        }
                        else
                        {
                            // TODO: implement downsampling of other pixel formats
                        }

                        newHeight >>= 1;

                        Size2 mipMapSize = Size2(static_cast<float>(newWidth), static_cast<float>(newHeight));
                        levels.push_back({mipMapSize, pitch, mipMapData});
                    }
                }
            }

            return true;
        }

        bool TextureResource::setFilter(Texture::Filter newFilter)
        {
            std::lock_guard<std::mutex> lock(uploadMutex);

            filter = newFilter;
            dirty |= DIRTY_PARAMETERS;

            return true;
        }

        bool TextureResource::setAddressX(Texture::Address newAddressX)
        {
            std::lock_guard<std::mutex> lock(uploadMutex);

            addressX = newAddressX;
            dirty |= DIRTY_PARAMETERS;

            return true;
        }

        bool TextureResource::setAddressY(Texture::Address newAddressY)
        {
            std::lock_guard<std::mutex> lock(uploadMutex);

            addressY = newAddressY;
            dirty |= DIRTY_PARAMETERS;

            return true;
        }

        bool TextureResource::setMaxAnisotropy(uint32_t newMaxAnisotropy)
        {
            std::lock_guard<std::mutex> lock(uploadMutex);

            maxAnisotropy = newMaxAnisotropy;
            dirty |= DIRTY_PARAMETERS;

            return true;
        }

        void TextureResource::setClearColorBuffer(bool clear)
        {
            std::lock_guard<std::mutex> lock(uploadMutex);

            clearColorBuffer = clear;
            dirty |= DIRTY_PARAMETERS;
        }

        void TextureResource::setClearDepthBuffer(bool clear)
        {
            std::lock_guard<std::mutex> lock(uploadMutex);

            clearColorBuffer = clear;
            dirty |= DIRTY_PARAMETERS;
        }

        void TextureResource::setClearColor(Color color)
        {
            std::lock_guard<std::mutex> lock(uploadMutex);

            clearColor = color;
            dirty |= DIRTY_PARAMETERS;
        }
    } // namespace graphics
} // namespace ouzel
<commit_msg>Reduce RGB resample code<commit_after>// Copyright (C) 2017 Elviss Strazdins
// This file is part of the Ouzel engine.

#include <algorithm>
#include "TextureResource.h"
#include "Renderer.h"
#include "core/Engine.h"
#include "math/MathUtils.h"

namespace ouzel
{
    namespace graphics
    {
        TextureResource::TextureResource()
        {
        }

        TextureResource::~TextureResource()
        {
        }

        bool TextureResource::init(const Size2& newSize,
                                   bool newDynamic,
                                   bool newMipmaps,
                                   bool newRenderTarget,
                                   uint32_t newSampleCount,
                                   bool newDepth,
                                   PixelFormat newPixelFormat)
        {
            std::lock_guard<std::mutex> lock(uploadMutex);

            dynamic = newDynamic;
            mipmaps = newMipmaps;
            renderTarget = newRenderTarget;
            sampleCount = newSampleCount;
            depth = newDepth;
            pixelFormat = newPixelFormat;

            if (!calculateSizes(newSize))
            {
                return false;
            }

            dirty |= DIRTY_DATA | DIRTY_PARAMETERS;

            return true;
        }

        bool TextureResource::initFromBuffer(const std::vector<uint8_t>& newData,
                                             const Size2& newSize,
                                             bool newDynamic,
                                             bool newMipmaps,
                                             PixelFormat newPixelFormat)
        {
            std::lock_guard<std::mutex> lock(uploadMutex);

            dynamic = newDynamic;
            mipmaps = newMipmaps;
            renderTarget = false;
            sampleCount = 1;
            depth = false;
            pixelFormat = newPixelFormat;

            if (!calculateData(newData, newSize))
            {
                return false;
            }

            dirty |= DIRTY_DATA | DIRTY_PARAMETERS;

            return true;
        }

        bool TextureResource::setSize(const Size2& newSize)
        {
            std::lock_guard<std::mutex> lock(uploadMutex);

            if (!dynamic)
            {
                return false;
            }

            if (newSize.v[0] <= 0.0f || newSize.v[1] <= 0.0f)
            {
                return false;
            }

            if (!calculateSizes(newSize))
            {
                return false;
            }

            dirty |= DIRTY_DATA;

            return true;
        }

        bool TextureResource::setData(const std::vector<uint8_t>& newData, const Size2& newSize)
        {
            std::lock_guard<std::mutex> lock(uploadMutex);

            if (!dynamic)
            {
                return false;
            }

            if (newSize.v[0] <= 0.0f || newSize.v[1] <= 0.0f)
            {
                return false;
            }

            if (!calculateData(newData, newSize))
            {
                return false;
            }

            dirty |= DIRTY_DATA;

            return true;
        }

        bool TextureResource::calculateSizes(const Size2& newSize)
        {
            levels.clear();
            size = newSize;

            uint32_t newWidth = static_cast<uint32_t>(newSize.v[0]);
            uint32_t newHeight = static_cast<uint32_t>(newSize.v[1]);

            uint32_t pixelSize = getPixelSize(pixelFormat);
            uint32_t pitch = newWidth * pixelSize;
            levels.push_back({newSize, pitch, std::vector<uint8_t>()});

            if (mipmaps && !renderTarget && (sharedEngine->getRenderer()->isNPOTTexturesSupported() || (isPOT(newWidth) && isPOT(newHeight))))
            {
                uint32_t bufferSize = newWidth * newHeight * pixelSize;

                if (newWidth == 1)
                {
                    bufferSize *= 2;
                }
                if (newHeight == 1)
                {
                    bufferSize *= 2;
                }

                while (newWidth >= 2 && newHeight >= 2)
                {
                    newWidth >>= 1;
                    newHeight >>= 1;

                    Size2 mipMapSize = Size2(static_cast<float>(newWidth), static_cast<float>(newHeight));
                    pitch = newWidth * pixelSize;

                    levels.push_back({mipMapSize, pitch, std::vector<uint8_t>()});
                }

                if (newWidth > newHeight)
                {
                    for (; newWidth >= 2;)
                    {
                        newWidth >>= 1;

                        Size2 mipMapSize = Size2(static_cast<float>(newWidth), static_cast<float>(newHeight));
                        pitch = newWidth * pixelSize;

                        levels.push_back({mipMapSize, pitch, std::vector<uint8_t>()});
                    }
                }
                else
                {
                    for (; newHeight >= 2;)
                    {
                        newHeight >>= 1;

                        Size2 mipMapSize = Size2(static_cast<float>(newWidth), static_cast<float>(newHeight));
                        levels.push_back({mipMapSize, pitch, std::vector<uint8_t>()});
                    }
                }
            }

            return true;
        }

        static void imageRGB8Downsample2x2(uint32_t width, uint32_t height, uint32_t pitch, const uint8_t* src, uint8_t* dst)
        {
            const uint32_t dstWidth  = width / 2;
            const uint32_t dstHeight = height / 2;

            if (dstWidth == 0 ||  dstHeight == 0)
            {
                return;
            }

            for (uint32_t y = 0, ystep = pitch * 2; y < dstHeight; ++y, src += ystep)
            {
                const uint8_t* rgb = src;
                for (uint32_t x = 0; x < dstWidth; ++x, rgb += 6, dst += 3)
                {
                    float r = 0.0f, g = 0.0f, b = 0.0f;

                    r += powf(rgb[0], 2.2f);
                    g += powf(rgb[1], 2.2f);
                    b += powf(rgb[2], 2.2f);

                    r += powf(rgb[3], 2.2f);
                    g += powf(rgb[4], 2.2f);
                    b += powf(rgb[5], 2.2f);

                    r += powf(rgb[pitch + 0], 2.2f);
                    g += powf(rgb[pitch + 1], 2.2f);
                    b += powf(rgb[pitch + 2], 2.2f);

                    r += powf(rgb[pitch + 3], 2.2f);
                    g += powf(rgb[pitch + 4], 2.2f);
                    b += powf(rgb[pitch + 5], 2.2f);

                    r /= 4.0f;
                    g /= 4.0f;
                    b /= 4.0f;

                    r = powf(r, 1.0f / 2.2f);
                    g = powf(g, 1.0f / 2.2f);
                    b = powf(b, 1.0f / 2.2f);
                    dst[0] = static_cast<uint8_t>(r);
                    dst[1] = static_cast<uint8_t>(g);
                    dst[2] = static_cast<uint8_t>(b);
                }
            }
        }

        static void imageRGBA8Downsample2x2(uint32_t width, uint32_t height, uint32_t pitch, const uint8_t* src, uint8_t* dst)
        {
            const uint32_t dstWidth  = width / 2;
            const uint32_t dstHeight = height / 2;

            if (dstWidth == 0 ||  dstHeight == 0)
            {
                return;
            }

            for (uint32_t y = 0, ystep = pitch * 2; y < dstHeight; ++y, src += ystep)
            {
                const uint8_t* rgba = src;
                for (uint32_t x = 0; x < dstWidth; ++x, rgba += 8, dst += 4)
                {
                    float pixels = 0.0f;
                    float r = 0.0f, g = 0.0f, b = 0.0f, a = 0.0f;

                    if (rgba[3] > 0)
                    {
                        r += powf(rgba[0], 2.2f);
                        g += powf(rgba[1], 2.2f);
                        b += powf(rgba[2], 2.2f);
                        pixels += 1.0f;
                    }
                    a = rgba[3];

                    if (rgba[7] > 0)
                    {
                        r += powf(rgba[4], 2.2f);
                        g += powf(rgba[5], 2.2f);
                        b += powf(rgba[6], 2.2f);
                        pixels += 1.0f;
                    }
                    a += rgba[7];

                    if (rgba[pitch + 3] > 0)
                    {
                        r += powf(rgba[pitch + 0], 2.2f);
                        g += powf(rgba[pitch + 1], 2.2f);
                        b += powf(rgba[pitch + 2], 2.2f);
                        pixels += 1.0f;
                    }
                    a += rgba[pitch + 3];

                    if (rgba[pitch + 7] > 0)
                    {
                        r += powf(rgba[pitch + 4], 2.2f);
                        g += powf(rgba[pitch + 5], 2.2f);
                        b += powf(rgba[pitch + 6], 2.2f);
                        pixels += 1.0f;
                    }
                    a += rgba[pitch + 7];

                    if (pixels > 0.0f)
                    {
                        r /= pixels;
                        g /= pixels;
                        b /= pixels;
                    }
                    else
                    {
                        r = g = b = 0.0f;
                    }

                    r = powf(r, 1.0f / 2.2f);
                    g = powf(g, 1.0f / 2.2f);
                    b = powf(b, 1.0f / 2.2f);
                    a *= 0.25f;
                    dst[0] = static_cast<uint8_t>(r);
                    dst[1] = static_cast<uint8_t>(g);
                    dst[2] = static_cast<uint8_t>(b);
                    dst[3] = static_cast<uint8_t>(a);
                }
            }
        }

        bool TextureResource::calculateData(const std::vector<uint8_t>& newData, const Size2& newSize)
        {
            levels.clear();
            size = newSize;

            uint32_t newWidth = static_cast<uint32_t>(newSize.v[0]);
            uint32_t newHeight = static_cast<uint32_t>(newSize.v[1]);

            uint32_t pixelSize = getPixelSize(pixelFormat);
            uint32_t pitch = newWidth * pixelSize;
            levels.push_back({newSize, pitch, newData});

            if (mipmaps && !renderTarget && (sharedEngine->getRenderer()->isNPOTTexturesSupported() || (isPOT(newWidth) && isPOT(newHeight))))
            {
                uint32_t bufferSize = newWidth * newHeight * pixelSize;

                if (newWidth == 1)
                {
                    bufferSize *= 2;
                }
                if (newHeight == 1)
                {
                    bufferSize *= 2;
                }

                std::vector<uint8_t> mipMapData(bufferSize);
                std::copy(newData.begin(),
                          newData.begin() + static_cast<std::vector<uint8_t>::difference_type>(newWidth * newHeight * pixelSize),
                          mipMapData.begin());

                while (newWidth >= 2 && newHeight >= 2)
                {
                    if (pixelFormat == PixelFormat::RGBA8_UINT)
                    {
                        imageRGBA8Downsample2x2(newWidth, newHeight, pitch, mipMapData.data(), mipMapData.data());
                    }
                    else if (pixelFormat == PixelFormat::RGB8_UINT)
                    {
                        imageRGBA8Downsample2x2(newWidth, 2, pitch, mipMapData.data(), mipMapData.data());
                    }
                    else
                    {
                        // TODO: implement downsampling of other pixel formats
                    }

                    newWidth >>= 1;
                    newHeight >>= 1;

                    Size2 mipMapSize = Size2(static_cast<float>(newWidth), static_cast<float>(newHeight));
                    pitch = newWidth * pixelSize;

                    levels.push_back({mipMapSize, pitch, mipMapData});
                }

                if (newWidth > newHeight) // height is 2
                {
                    for (; newWidth >= 2;)
                    {
                        std::copy(mipMapData.begin(),
                                  mipMapData.begin() + newWidth * pixelSize,
                                  mipMapData.begin() + newWidth * pixelSize);

                        if (pixelFormat == PixelFormat::RGBA8_UINT)
                        {
                            imageRGBA8Downsample2x2(newWidth, 2, pitch, mipMapData.data(), mipMapData.data());
                        }
                        else if (pixelFormat == PixelFormat::RGB8_UINT)
                        {
                            imageRGBA8Downsample2x2(newWidth, 2, pitch, mipMapData.data(), mipMapData.data());
                        }
                        else
                        {
                            // TODO: implement downsampling of other pixel formats
                        }

                        newWidth >>= 1;

                        Size2 mipMapSize = Size2(static_cast<float>(newWidth), static_cast<float>(newHeight));
                        pitch = newWidth * pixelSize;

                        levels.push_back({mipMapSize, pitch, mipMapData});
                    }
                }
                else // width is 2
                {
                    for (; newHeight >= 2;)
                    {
                        for (int32_t i = static_cast<int32_t>(newHeight) - 1; i >= 0; --i)
                        {
                            std::copy(mipMapData.begin() + static_cast<uint32_t>(i * 2) * pixelSize,
                                      mipMapData.begin() + static_cast<uint32_t>(i * 2) * pixelSize + pixelSize,
                                      mipMapData.begin() + static_cast<uint32_t>(i) * pixelSize);

                            std::copy(mipMapData.begin() + static_cast<uint32_t>(i * 2 + 1) * pixelSize,
                                      mipMapData.begin() + static_cast<uint32_t>(i * 2 + 1) * pixelSize + pixelSize,
                                      mipMapData.begin() + static_cast<uint32_t>(i) * pixelSize);
                        }

                        if (pixelFormat == PixelFormat::RGBA8_UINT)
                        {
                            imageRGBA8Downsample2x2(2, newHeight, 8, mipMapData.data(), mipMapData.data());
                        }
                        else if (pixelFormat == PixelFormat::RGB8_UINT)
                        {
                            imageRGB8Downsample2x2(2, newHeight, 8, mipMapData.data(), mipMapData.data());
                        }
                        else
                        {
                            // TODO: implement downsampling of other pixel formats
                        }

                        newHeight >>= 1;

                        Size2 mipMapSize = Size2(static_cast<float>(newWidth), static_cast<float>(newHeight));
                        levels.push_back({mipMapSize, pitch, mipMapData});
                    }
                }
            }

            return true;
        }

        bool TextureResource::setFilter(Texture::Filter newFilter)
        {
            std::lock_guard<std::mutex> lock(uploadMutex);

            filter = newFilter;
            dirty |= DIRTY_PARAMETERS;

            return true;
        }

        bool TextureResource::setAddressX(Texture::Address newAddressX)
        {
            std::lock_guard<std::mutex> lock(uploadMutex);

            addressX = newAddressX;
            dirty |= DIRTY_PARAMETERS;

            return true;
        }

        bool TextureResource::setAddressY(Texture::Address newAddressY)
        {
            std::lock_guard<std::mutex> lock(uploadMutex);

            addressY = newAddressY;
            dirty |= DIRTY_PARAMETERS;

            return true;
        }

        bool TextureResource::setMaxAnisotropy(uint32_t newMaxAnisotropy)
        {
            std::lock_guard<std::mutex> lock(uploadMutex);

            maxAnisotropy = newMaxAnisotropy;
            dirty |= DIRTY_PARAMETERS;

            return true;
        }

        void TextureResource::setClearColorBuffer(bool clear)
        {
            std::lock_guard<std::mutex> lock(uploadMutex);

            clearColorBuffer = clear;
            dirty |= DIRTY_PARAMETERS;
        }

        void TextureResource::setClearDepthBuffer(bool clear)
        {
            std::lock_guard<std::mutex> lock(uploadMutex);

            clearColorBuffer = clear;
            dirty |= DIRTY_PARAMETERS;
        }

        void TextureResource::setClearColor(Color color)
        {
            std::lock_guard<std::mutex> lock(uploadMutex);

            clearColor = color;
            dirty |= DIRTY_PARAMETERS;
        }
    } // namespace graphics
} // namespace ouzel
<|endoftext|>


<commit_before>#include "envoy/api/v2/discovery.pb.h"
#include "envoy/api/v2/rds.pb.h"
#include "envoy/grpc/status.h"
#include "envoy/stats/scope.h"

#include "common/config/protobuf_link_hacks.h"
#include "common/config/resources.h"
#include "common/protobuf/protobuf.h"
#include "common/protobuf/utility.h"

#include "test/common/grpc/grpc_client_integration.h"
#include "test/integration/http_integration.h"
#include "test/integration/utility.h"
#include "test/mocks/server/mocks.h"
#include "test/test_common/network_utility.h"
#include "test/test_common/simulated_time_system.h"
#include "test/test_common/utility.h"

#include "absl/synchronization/notification.h"
#include "gtest/gtest.h"

using testing::AssertionFailure;
using testing::AssertionResult;
using testing::AssertionSuccess;
using testing::IsSubstring;

namespace Envoy {
namespace {

const char Config[] = R"EOF(
admin:
  access_log_path: /dev/null
  address:
    socket_address:
      address: 127.0.0.1
      port_value: 0
static_resources:
  clusters:
  - name: xds_cluster
    type: STATIC
    http2_protocol_options: {}
    hosts:
      socket_address:
        address: 127.0.0.1
        port_value: 0
  - name: my_service
    type: STATIC
    http2_protocol_options: {}
    hosts:
      socket_address:
        address: 127.0.0.1
        port_value: 0
  listeners:
  - name: http
    address:
      socket_address:
        address: 127.0.0.1
        port_value: 0
    filter_chains:
    - filters:
      - name: envoy.http_connection_manager
        config:
          stat_prefix: config_test
          http_filters:
          - name: envoy.router
          codec_type: HTTP2
          rds:
            route_config_name: my_route
            config_source:
              api_config_source:
                api_type: GRPC
                grpc_services:
                  envoy_grpc:
                    cluster_name: xds_cluster
)EOF";

const char RdsConfig[] = R"EOF(
name: my_route
vhds:
  config_source:
    api_config_source:
      api_type: DELTA_GRPC
      grpc_services:
        envoy_grpc:
          cluster_name: xds_cluster
)EOF";

const char RdsConfigWithVhosts[] = R"EOF(
name: my_route
virtual_hosts:
- name: vhost_rds1
  domains: ["vhost.rds.first"]
  routes:
  - match: { prefix: "/rdsone" }
    route: { cluster: my_service }
vhds:
  config_source:
    api_config_source:
      api_type: DELTA_GRPC
      grpc_services:
        envoy_grpc:
          cluster_name: xds_cluster
)EOF";

class VhdsIntegrationTest : public HttpIntegrationTest,
                            public Grpc::GrpcClientIntegrationParamTest {
public:
  VhdsIntegrationTest()
      : HttpIntegrationTest(Http::CodecClient::Type::HTTP2, ipVersion(), realTime(), Config) {}

  void TearDown() override {
    cleanUpXdsConnection();
    test_server_.reset();
    fake_upstreams_.clear();
  }

  std::string virtualHostYaml(const std::string& name, const std::string& domain) {
    return fmt::format(R"EOF(
      name: {}
      domains: [{}]
      routes:
      - match: {{ prefix: "/" }}
        route: {{ cluster: "my_service" }}
    )EOF",
                       name, domain);
  }

  envoy::api::v2::route::VirtualHost buildVirtualHost() {
    return TestUtility::parseYaml<envoy::api::v2::route::VirtualHost>(
        virtualHostYaml("vhost_0", "host"));
  }

  std::vector<envoy::api::v2::route::VirtualHost> buildVirtualHost1() {
    return {TestUtility::parseYaml<envoy::api::v2::route::VirtualHost>(
                virtualHostYaml("vhost_1", "vhost.first")),
            TestUtility::parseYaml<envoy::api::v2::route::VirtualHost>(
                virtualHostYaml("vhost_2", "vhost.second"))};
  }

  envoy::api::v2::route::VirtualHost buildVirtualHost2() {
    return TestUtility::parseYaml<envoy::api::v2::route::VirtualHost>(
        virtualHostYaml("vhost_1", "vhost.first"));
  }

  // Overridden to insert this stuff into the initialize() at the very beginning of
  // HttpIntegrationTest::testRouterRequestAndResponseWithBody().
  void initialize() override {
    // Controls how many fake_upstreams_.emplace_back(new FakeUpstream) will happen in
    // BaseIntegrationTest::createUpstreams() (which is part of initialize()).
    // Make sure this number matches the size of the 'clusters' repeated field in the bootstrap
    // config that you use!
    setUpstreamCount(2);                                  // the CDS cluster
    setUpstreamProtocol(FakeHttpConnection::Type::HTTP2); // CDS uses gRPC uses HTTP2.

    // BaseIntegrationTest::initialize() does many things:
    // 1) It appends to fake_upstreams_ as many as you asked for via setUpstreamCount().
    // 2) It updates your bootstrap config with the ports your fake upstreams are actually listening
    //    on (since you're supposed to leave them as 0).
    // 3) It creates and starts an IntegrationTestServer - the thing that wraps the almost-actual
    //    Envoy used in the tests.
    // 4) Bringing up the server usually entails waiting to ensure that any listeners specified in
    //    the bootstrap config have come up, and registering them in a port map (see lookupPort()).
    //    However, this test needs to defer all of that to later.
    defer_listener_finalization_ = true;
    HttpIntegrationTest::initialize();

    // Now that the upstream has been created, process Envoy's request to discover it.
    // (First, we have to let Envoy establish its connection to the RDS server.)
    AssertionResult result = // xds_connection_ is filled with the new FakeHttpConnection.
        fake_upstreams_[0]->waitForHttpConnection(*dispatcher_, xds_connection_);
    RELEASE_ASSERT(result, result.message());
    result = xds_connection_->waitForNewStream(*dispatcher_, xds_stream_);
    RELEASE_ASSERT(result, result.message());
    xds_stream_->startGrpcStream();
    fake_upstreams_[0]->set_allow_unexpected_disconnects(true);

    EXPECT_TRUE(
        compareDiscoveryRequest(Config::TypeUrl::get().RouteConfiguration, "", {"my_route"}));
    sendDiscoveryResponse<envoy::api::v2::RouteConfiguration>(
        Config::TypeUrl::get().RouteConfiguration, {rdsConfig()}, "1");

    result = xds_connection_->waitForNewStream(*dispatcher_, vhds_stream_, true);
    RELEASE_ASSERT(result, result.message());
    vhds_stream_->startGrpcStream();

    EXPECT_TRUE(
        compareDeltaDiscoveryRequest(Config::TypeUrl::get().VirtualHost, {}, {}, vhds_stream_));
    sendDeltaDiscoveryResponse<envoy::api::v2::route::VirtualHost>({buildVirtualHost()}, {}, "1",
                                                                   vhds_stream_);

    // Wait for our statically specified listener to become ready, and register its port in the
    // test framework's downstream listener port map.
    test_server_->waitUntilListenersReady();
    registerTestServerPorts({"http"});
  }

  void useRdsWithVhosts() { use_rds_with_vhosts = true; }
  const envoy::api::v2::RouteConfiguration rdsConfig() const {
    return TestUtility::parseYaml<envoy::api::v2::RouteConfiguration>(
        use_rds_with_vhosts ? RdsConfigWithVhosts : RdsConfig);
  }

  FakeStreamPtr vhds_stream_;
  bool use_rds_with_vhosts{false};
};

INSTANTIATE_TEST_CASE_P(IpVersionsClientType, VhdsIntegrationTest, GRPC_CLIENT_INTEGRATION_PARAMS);

// tests a scenario when:
//  - a spontaneous VHDS DiscoveryResponse adds two virtual hosts
//  - the next spontaneous VHDS DiscoveryResponse removes newly added virtual hosts
//  - Upstream makes a request to an (now) unknown domain, which fails
TEST_P(VhdsIntegrationTest, VhdsVirtualHostAddUpdateRemove) {
  // Calls our initialize(), which includes establishing a listener, route, and cluster.
  testRouterHeaderOnlyRequestAndResponse(nullptr, 1);
  cleanupUpstreamAndDownstream();
  codec_client_->waitForDisconnect();

  // A spontaneous VHDS DiscoveryResponse adds two virtual hosts
  sendDeltaDiscoveryResponse<envoy::api::v2::route::VirtualHost>(buildVirtualHost1(), {}, "2",
                                                                 vhds_stream_);
  EXPECT_TRUE(
      compareDeltaDiscoveryRequest(Config::TypeUrl::get().VirtualHost, {}, {}, vhds_stream_));

  testRouterHeaderOnlyRequestAndResponse(nullptr, 1, "/one", "vhost.first");
  cleanupUpstreamAndDownstream();
  codec_client_->waitForDisconnect();
  testRouterHeaderOnlyRequestAndResponse(nullptr, 1, "/two", "vhost.second");
  cleanupUpstreamAndDownstream();
  codec_client_->waitForDisconnect();

  // A spontaneous VHDS DiscoveryResponse removes newly added virtual hosts
  sendDeltaDiscoveryResponse<envoy::api::v2::route::VirtualHost>({}, {"vhost_1", "vhost_2"}, "3",
                                                                 vhds_stream_);
  EXPECT_TRUE(
      compareDeltaDiscoveryRequest(Config::TypeUrl::get().VirtualHost, {}, {}, vhds_stream_));

  // an upstream request to an (now) unknown domain
  codec_client_ = makeHttpConnection(makeClientConnection((lookupPort("http"))));
  Http::TestHeaderMapImpl request_headers{{":method", "GET"},
                                          {":path", "/one"},
                                          {":scheme", "http"},
                                          {":authority", "vhost.first"},
                                          {"x-lyft-user-id", "123"}};
  IntegrationStreamDecoderPtr response = codec_client_->makeHeaderOnlyRequest(request_headers);
  response->waitForHeaders();
  EXPECT_EQ("404", response->headers().Status()->value().getStringView());

  cleanupUpstreamAndDownstream();
}

// tests a scenario when:
//  - an RDS exchange contains a non-empty virtual_hosts array
//  - a spontaneous VHDS DiscoveryResponse adds two virtual hosts
//  - the next spontaneous VHDS DiscoveryResponse removes newly added virtual hosts
//  - Upstream makes a request to an (now) unknown domain, which fails
TEST_P(VhdsIntegrationTest, RdsWithVirtualHostsVhdsVirtualHostAddUpdateRemove) {
  // RDS exchange with a non-empty virtual_hosts field
  useRdsWithVhosts();

  testRouterHeaderOnlyRequestAndResponse(nullptr, 1);
  cleanupUpstreamAndDownstream();
  codec_client_->waitForDisconnect();

  // A spontaneous VHDS DiscoveryResponse adds two virtual hosts
  sendDeltaDiscoveryResponse<envoy::api::v2::route::VirtualHost>(buildVirtualHost1(), {}, "2",
                                                                 vhds_stream_);
  EXPECT_TRUE(
      compareDeltaDiscoveryRequest(Config::TypeUrl::get().VirtualHost, {}, {}, vhds_stream_));

  // verify that rds-based virtual host can be resolved
  testRouterHeaderOnlyRequestAndResponse(nullptr, 1, "/rdsone", "vhost.rds.first");
  cleanupUpstreamAndDownstream();
  codec_client_->waitForDisconnect();
  testRouterHeaderOnlyRequestAndResponse(nullptr, 1, "/one", "vhost.first");
  cleanupUpstreamAndDownstream();
  codec_client_->waitForDisconnect();
  testRouterHeaderOnlyRequestAndResponse(nullptr, 1, "/two", "vhost.second");
  cleanupUpstreamAndDownstream();
  codec_client_->waitForDisconnect();

  // A spontaneous VHDS DiscoveryResponse removes virtual hosts added via vhds
  sendDeltaDiscoveryResponse<envoy::api::v2::route::VirtualHost>({}, {"vhost_1", "vhost_2"}, "3",
                                                                 vhds_stream_);
  EXPECT_TRUE(
      compareDeltaDiscoveryRequest(Config::TypeUrl::get().VirtualHost, {}, {}, vhds_stream_));

  // verify rds-based virtual host is still present
  testRouterHeaderOnlyRequestAndResponse(nullptr, 1, "/rdsone", "vhost.rds.first");
  cleanupUpstreamAndDownstream();
  codec_client_->waitForDisconnect();

  codec_client_ = makeHttpConnection(makeClientConnection((lookupPort("http"))));
  Http::TestHeaderMapImpl request_headers{{":method", "GET"},
                                          {":path", "/one"},
                                          {":scheme", "http"},
                                          {":authority", "vhost.first"},
                                          {"x-lyft-user-id", "123"}};
  IntegrationStreamDecoderPtr response = codec_client_->makeHeaderOnlyRequest(request_headers);
  response->waitForHeaders();
  EXPECT_EQ("404", response->headers().Status()->value().getStringView());

  cleanupUpstreamAndDownstream();
}

} // namespace
} // namespace Envoy
<commit_msg>Fix for an intermittently failing integration test (#6978)<commit_after>#include "envoy/api/v2/discovery.pb.h"
#include "envoy/api/v2/rds.pb.h"
#include "envoy/grpc/status.h"
#include "envoy/stats/scope.h"

#include "common/config/protobuf_link_hacks.h"
#include "common/config/resources.h"
#include "common/protobuf/protobuf.h"
#include "common/protobuf/utility.h"

#include "test/common/grpc/grpc_client_integration.h"
#include "test/integration/http_integration.h"
#include "test/integration/utility.h"
#include "test/mocks/server/mocks.h"
#include "test/test_common/network_utility.h"
#include "test/test_common/simulated_time_system.h"
#include "test/test_common/utility.h"

#include "absl/synchronization/notification.h"
#include "gtest/gtest.h"

using testing::AssertionFailure;
using testing::AssertionResult;
using testing::AssertionSuccess;
using testing::IsSubstring;

namespace Envoy {
namespace {

const char Config[] = R"EOF(
admin:
  access_log_path: /dev/null
  address:
    socket_address:
      address: 127.0.0.1
      port_value: 0
static_resources:
  clusters:
  - name: xds_cluster
    type: STATIC
    http2_protocol_options: {}
    hosts:
      socket_address:
        address: 127.0.0.1
        port_value: 0
  - name: my_service
    type: STATIC
    http2_protocol_options: {}
    hosts:
      socket_address:
        address: 127.0.0.1
        port_value: 0
  listeners:
  - name: http
    address:
      socket_address:
        address: 127.0.0.1
        port_value: 0
    filter_chains:
    - filters:
      - name: envoy.http_connection_manager
        config:
          stat_prefix: config_test
          http_filters:
          - name: envoy.router
          codec_type: HTTP2
          rds:
            route_config_name: my_route
            config_source:
              api_config_source:
                api_type: GRPC
                grpc_services:
                  envoy_grpc:
                    cluster_name: xds_cluster
)EOF";

const char RdsConfig[] = R"EOF(
name: my_route
vhds:
  config_source:
    api_config_source:
      api_type: DELTA_GRPC
      grpc_services:
        envoy_grpc:
          cluster_name: xds_cluster
)EOF";

const char RdsConfigWithVhosts[] = R"EOF(
name: my_route
virtual_hosts:
- name: vhost_rds1
  domains: ["vhost.rds.first"]
  routes:
  - match: { prefix: "/rdsone" }
    route: { cluster: my_service }
vhds:
  config_source:
    api_config_source:
      api_type: DELTA_GRPC
      grpc_services:
        envoy_grpc:
          cluster_name: xds_cluster
)EOF";

class VhdsIntegrationTest : public HttpIntegrationTest,
                            public Grpc::GrpcClientIntegrationParamTest {
public:
  VhdsIntegrationTest()
      : HttpIntegrationTest(Http::CodecClient::Type::HTTP2, ipVersion(), realTime(), Config) {}

  void TearDown() override {
    cleanUpXdsConnection();
    test_server_.reset();
    fake_upstreams_.clear();
  }

  std::string virtualHostYaml(const std::string& name, const std::string& domain) {
    return fmt::format(R"EOF(
      name: {}
      domains: [{}]
      routes:
      - match: {{ prefix: "/" }}
        route: {{ cluster: "my_service" }}
    )EOF",
                       name, domain);
  }

  envoy::api::v2::route::VirtualHost buildVirtualHost() {
    return TestUtility::parseYaml<envoy::api::v2::route::VirtualHost>(
        virtualHostYaml("vhost_0", "host"));
  }

  std::vector<envoy::api::v2::route::VirtualHost> buildVirtualHost1() {
    return {TestUtility::parseYaml<envoy::api::v2::route::VirtualHost>(
                virtualHostYaml("vhost_1", "vhost.first")),
            TestUtility::parseYaml<envoy::api::v2::route::VirtualHost>(
                virtualHostYaml("vhost_2", "vhost.second"))};
  }

  envoy::api::v2::route::VirtualHost buildVirtualHost2() {
    return TestUtility::parseYaml<envoy::api::v2::route::VirtualHost>(
        virtualHostYaml("vhost_1", "vhost.first"));
  }

  // Overridden to insert this stuff into the initialize() at the very beginning of
  // HttpIntegrationTest::testRouterRequestAndResponseWithBody().
  void initialize() override {
    // Controls how many fake_upstreams_.emplace_back(new FakeUpstream) will happen in
    // BaseIntegrationTest::createUpstreams() (which is part of initialize()).
    // Make sure this number matches the size of the 'clusters' repeated field in the bootstrap
    // config that you use!
    setUpstreamCount(2);                                  // the CDS cluster
    setUpstreamProtocol(FakeHttpConnection::Type::HTTP2); // CDS uses gRPC uses HTTP2.

    // BaseIntegrationTest::initialize() does many things:
    // 1) It appends to fake_upstreams_ as many as you asked for via setUpstreamCount().
    // 2) It updates your bootstrap config with the ports your fake upstreams are actually listening
    //    on (since you're supposed to leave them as 0).
    // 3) It creates and starts an IntegrationTestServer - the thing that wraps the almost-actual
    //    Envoy used in the tests.
    // 4) Bringing up the server usually entails waiting to ensure that any listeners specified in
    //    the bootstrap config have come up, and registering them in a port map (see lookupPort()).
    //    However, this test needs to defer all of that to later.
    defer_listener_finalization_ = true;
    HttpIntegrationTest::initialize();

    // Now that the upstream has been created, process Envoy's request to discover it.
    // (First, we have to let Envoy establish its connection to the RDS server.)
    AssertionResult result = // xds_connection_ is filled with the new FakeHttpConnection.
        fake_upstreams_[0]->waitForHttpConnection(*dispatcher_, xds_connection_);
    RELEASE_ASSERT(result, result.message());
    result = xds_connection_->waitForNewStream(*dispatcher_, xds_stream_);
    RELEASE_ASSERT(result, result.message());
    xds_stream_->startGrpcStream();
    fake_upstreams_[0]->set_allow_unexpected_disconnects(true);

    EXPECT_TRUE(
        compareDiscoveryRequest(Config::TypeUrl::get().RouteConfiguration, "", {"my_route"}));
    sendDiscoveryResponse<envoy::api::v2::RouteConfiguration>(
        Config::TypeUrl::get().RouteConfiguration, {rdsConfig()}, "1");

    result = xds_connection_->waitForNewStream(*dispatcher_, vhds_stream_, true);
    RELEASE_ASSERT(result, result.message());
    vhds_stream_->startGrpcStream();

    EXPECT_TRUE(
        compareDeltaDiscoveryRequest(Config::TypeUrl::get().VirtualHost, {}, {}, vhds_stream_));
    sendDeltaDiscoveryResponse<envoy::api::v2::route::VirtualHost>({buildVirtualHost()}, {}, "1",
                                                                   vhds_stream_);
    EXPECT_TRUE(
        compareDeltaDiscoveryRequest(Config::TypeUrl::get().VirtualHost, {}, {}, vhds_stream_));

    // Wait for our statically specified listener to become ready, and register its port in the
    // test framework's downstream listener port map.
    test_server_->waitUntilListenersReady();
    registerTestServerPorts({"http"});
  }

  void useRdsWithVhosts() { use_rds_with_vhosts = true; }
  const envoy::api::v2::RouteConfiguration rdsConfig() const {
    return TestUtility::parseYaml<envoy::api::v2::RouteConfiguration>(
        use_rds_with_vhosts ? RdsConfigWithVhosts : RdsConfig);
  }

  FakeStreamPtr vhds_stream_;
  bool use_rds_with_vhosts{false};
};

INSTANTIATE_TEST_CASE_P(IpVersionsClientType, VhdsIntegrationTest, GRPC_CLIENT_INTEGRATION_PARAMS);

// tests a scenario when:
//  - a spontaneous VHDS DiscoveryResponse adds two virtual hosts
//  - the next spontaneous VHDS DiscoveryResponse removes newly added virtual hosts
//  - Upstream makes a request to an (now) unknown domain, which fails
TEST_P(VhdsIntegrationTest, VhdsVirtualHostAddUpdateRemove) {
  // Calls our initialize(), which includes establishing a listener, route, and cluster.
  testRouterHeaderOnlyRequestAndResponse(nullptr, 1);
  cleanupUpstreamAndDownstream();
  codec_client_->waitForDisconnect();

  // A spontaneous VHDS DiscoveryResponse adds two virtual hosts
  sendDeltaDiscoveryResponse<envoy::api::v2::route::VirtualHost>(buildVirtualHost1(), {}, "2",
                                                                 vhds_stream_);
  EXPECT_TRUE(
      compareDeltaDiscoveryRequest(Config::TypeUrl::get().VirtualHost, {}, {}, vhds_stream_));

  testRouterHeaderOnlyRequestAndResponse(nullptr, 1, "/one", "vhost.first");
  cleanupUpstreamAndDownstream();
  codec_client_->waitForDisconnect();
  testRouterHeaderOnlyRequestAndResponse(nullptr, 1, "/two", "vhost.second");
  cleanupUpstreamAndDownstream();
  codec_client_->waitForDisconnect();

  // A spontaneous VHDS DiscoveryResponse removes newly added virtual hosts
  sendDeltaDiscoveryResponse<envoy::api::v2::route::VirtualHost>({}, {"vhost_1", "vhost_2"}, "3",
                                                                 vhds_stream_);
  EXPECT_TRUE(
      compareDeltaDiscoveryRequest(Config::TypeUrl::get().VirtualHost, {}, {}, vhds_stream_));

  // an upstream request to an (now) unknown domain
  codec_client_ = makeHttpConnection(makeClientConnection((lookupPort("http"))));
  Http::TestHeaderMapImpl request_headers{{":method", "GET"},
                                          {":path", "/one"},
                                          {":scheme", "http"},
                                          {":authority", "vhost.first"},
                                          {"x-lyft-user-id", "123"}};
  IntegrationStreamDecoderPtr response = codec_client_->makeHeaderOnlyRequest(request_headers);
  response->waitForHeaders();
  EXPECT_EQ("404", response->headers().Status()->value().getStringView());

  cleanupUpstreamAndDownstream();
}

// tests a scenario when:
//  - an RDS exchange contains a non-empty virtual_hosts array
//  - a spontaneous VHDS DiscoveryResponse adds two virtual hosts
//  - the next spontaneous VHDS DiscoveryResponse removes newly added virtual hosts
//  - Upstream makes a request to an (now) unknown domain, which fails
TEST_P(VhdsIntegrationTest, RdsWithVirtualHostsVhdsVirtualHostAddUpdateRemove) {
  // RDS exchange with a non-empty virtual_hosts field
  useRdsWithVhosts();

  testRouterHeaderOnlyRequestAndResponse(nullptr, 1);
  cleanupUpstreamAndDownstream();
  codec_client_->waitForDisconnect();

  // A spontaneous VHDS DiscoveryResponse adds two virtual hosts
  sendDeltaDiscoveryResponse<envoy::api::v2::route::VirtualHost>(buildVirtualHost1(), {}, "2",
                                                                 vhds_stream_);
  EXPECT_TRUE(
      compareDeltaDiscoveryRequest(Config::TypeUrl::get().VirtualHost, {}, {}, vhds_stream_));

  // verify that rds-based virtual host can be resolved
  testRouterHeaderOnlyRequestAndResponse(nullptr, 1, "/rdsone", "vhost.rds.first");
  cleanupUpstreamAndDownstream();
  codec_client_->waitForDisconnect();
  testRouterHeaderOnlyRequestAndResponse(nullptr, 1, "/one", "vhost.first");
  cleanupUpstreamAndDownstream();
  codec_client_->waitForDisconnect();
  testRouterHeaderOnlyRequestAndResponse(nullptr, 1, "/two", "vhost.second");
  cleanupUpstreamAndDownstream();
  codec_client_->waitForDisconnect();

  // A spontaneous VHDS DiscoveryResponse removes virtual hosts added via vhds
  sendDeltaDiscoveryResponse<envoy::api::v2::route::VirtualHost>({}, {"vhost_1", "vhost_2"}, "3",
                                                                 vhds_stream_);
  EXPECT_TRUE(
      compareDeltaDiscoveryRequest(Config::TypeUrl::get().VirtualHost, {}, {}, vhds_stream_));

  // verify rds-based virtual host is still present
  testRouterHeaderOnlyRequestAndResponse(nullptr, 1, "/rdsone", "vhost.rds.first");
  cleanupUpstreamAndDownstream();
  codec_client_->waitForDisconnect();

  codec_client_ = makeHttpConnection(makeClientConnection((lookupPort("http"))));
  Http::TestHeaderMapImpl request_headers{{":method", "GET"},
                                          {":path", "/one"},
                                          {":scheme", "http"},
                                          {":authority", "vhost.first"},
                                          {"x-lyft-user-id", "123"}};
  IntegrationStreamDecoderPtr response = codec_client_->makeHeaderOnlyRequest(request_headers);
  response->waitForHeaders();
  EXPECT_EQ("404", response->headers().Status()->value().getStringView());

  cleanupUpstreamAndDownstream();
}

} // namespace
} // namespace Envoy
<|endoftext|>


<commit_before>/**
*  Copyright (C) 2015 3D Repo Ltd
*
*  This program is free software: you can redistribute it and/or modify
*  it under the terms of the GNU Affero General Public License as
*  published by the Free Software Foundation, either version 3 of the
*  License, or (at your option) any later version.
*
*  This program 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 Affero General Public License for more details.
*
*  You should have received a copy of the GNU Affero General Public License
*  along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include <cstdlib>
#include <gtest/gtest.h>
#include <repo/repo_controller.h>
#include <repo/error_codes.h>
#include "../repo_test_database_info.h"
#include "../repo_test_fileservice_info.h"
#include "../repo_test_utils.h"

using namespace repo;

static std::shared_ptr<RepoController> getController()
{
	static std::shared_ptr<RepoController> controller =
		std::make_shared<RepoController>();

	return controller;
}

TEST(RepoControllerTest, CommitScene) {
	auto controller = getController();
	auto token = initController(controller.get());
	//Try to commit a scene without setting db/project name
	uint8_t errCode;
	auto scene = controller->loadSceneFromFile(getDataPath(simpleModel), errCode);
	EXPECT_EQ(REPOERR_OK, errCode);
	EXPECT_EQ(REPOERR_UNKNOWN_ERR, controller->commitScene(token, scene));
	EXPECT_FALSE(scene->isRevisioned());

	//Trying to commit a scene with empty db and project name should also fail
	scene->setDatabaseAndProjectName("", "");
	EXPECT_EQ(REPOERR_UNKNOWN_ERR, controller->commitScene(token, scene));
	EXPECT_FALSE(scene->isRevisioned());

	scene->setDatabaseAndProjectName("balh", "");
	EXPECT_EQ(REPOERR_UNKNOWN_ERR, controller->commitScene(token, scene));
	EXPECT_FALSE(scene->isRevisioned());

	scene->setDatabaseAndProjectName("", "blah");
	EXPECT_EQ(REPOERR_UNKNOWN_ERR, controller->commitScene(token, scene));
	EXPECT_FALSE(scene->isRevisioned());

	//Setting the db name and project name should allow commit successfully
	scene->setDatabaseAndProjectName("commitSceneTest", "commitCube");
	EXPECT_EQ(REPOERR_OK, controller->commitScene(initController(controller.get()), scene));
	EXPECT_TRUE(scene->isRevisioned());
	EXPECT_TRUE(projectExists("commitSceneTest", "commitCube"));
	EXPECT_EQ(scene->getOwner(), REPO_GTEST_DBUSER);

	auto scene2 = controller->loadSceneFromFile(getDataPath(simpleModel), errCode);
	std::string owner = "dog";
	EXPECT_EQ(errCode, 0);
	scene2->setDatabaseAndProjectName("commitSceneTest", "commitCube2");
	EXPECT_EQ(REPOERR_OK, controller->commitScene(initController(controller.get()), scene2, owner));
	EXPECT_TRUE(scene2->isRevisioned());
	EXPECT_TRUE(projectExists("commitSceneTest", "commitCube2"));
	EXPECT_EQ(scene2->getOwner(), owner);

	//null pointer checks
	EXPECT_EQ(REPOERR_UNKNOWN_ERR, controller->commitScene(token, nullptr));
	EXPECT_EQ(REPOERR_UNKNOWN_ERR, controller->commitScene(nullptr, scene));
}

TEST(RepoControllerTest, LoadSceneFromFile) {
	auto controller = getController();
	auto defaultG = core::model::RepoScene::GraphType::DEFAULT;
	auto optG = core::model::RepoScene::GraphType::OPTIMIZED;

	//standard import
	uint8_t errCode;
	auto scene = controller->loadSceneFromFile(getDataPath(simpleModel), errCode);
	ASSERT_TRUE(scene);
	EXPECT_EQ(errCode, 0);
	ASSERT_TRUE(scene->getRoot(defaultG));
	ASSERT_TRUE(scene->getRoot(optG));
	EXPECT_FALSE(scene->isMissingTexture());
	EXPECT_FALSE(scene->isRevisioned());
	EXPECT_TRUE(dynamic_cast<core::model::TransformationNode*>(scene->getRoot(defaultG))->isIdentity());

	//Import the scene with no transformation reduction
	auto sceneNoReduction = controller->loadSceneFromFile(getDataPath(simpleModel), errCode, repo::manipulator::modelconvertor::ModelImportConfig(false));
	EXPECT_EQ(errCode, 0);
	EXPECT_TRUE(sceneNoReduction);
	EXPECT_TRUE(sceneNoReduction->getRoot(defaultG));
	EXPECT_TRUE(sceneNoReduction->getRoot(optG));
	EXPECT_FALSE(sceneNoReduction->isMissingTexture());
	//There should be more transformations in the non-reduced scene than the standard scene
	EXPECT_TRUE(sceneNoReduction->getAllTransformations(defaultG).size()
			> scene->getAllTransformations(defaultG).size());

	//Import the scene with root trans rotated
	auto sceneRotated = controller->loadSceneFromFile(getDataPath(simpleModel), errCode, repo::manipulator::modelconvertor::ModelImportConfig(true, true, true));
	EXPECT_EQ(errCode, 0);
	EXPECT_TRUE(sceneRotated);
	ASSERT_TRUE(sceneRotated->getRoot(defaultG));
	EXPECT_TRUE(sceneRotated->getRoot(optG));
	EXPECT_FALSE(sceneRotated->isMissingTexture());
	//The root transformation should not be an identity
	core::model::TransformationNode *rootTrans = dynamic_cast<core::model::TransformationNode*>(sceneRotated->getRoot(defaultG));
	EXPECT_FALSE(rootTrans->isIdentity());

	//Import the scene with non existant file
	auto sceneNoFile = controller->loadSceneFromFile("thisFileDoesntExist.obj", errCode);
	EXPECT_EQ(errCode, REPOERR_MODEL_FILE_READ);
	EXPECT_FALSE(sceneNoFile);

	//Import the scene with bad Extension
	auto sceneBadExt = controller->loadSceneFromFile(getDataPath(badExtensionFile), errCode);
	EXPECT_EQ(errCode, REPOERR_FILE_TYPE_NOT_SUPPORTED);
	EXPECT_FALSE(sceneBadExt);

	//Import the scene with texture but not found
	auto sceneNoTex = controller->loadSceneFromFile(getDataPath(texturedModel), errCode);
	EXPECT_EQ(errCode, 0);
	EXPECT_TRUE(sceneNoTex);
	EXPECT_TRUE(sceneNoTex->getRoot(defaultG));
	EXPECT_TRUE(sceneNoTex->getRoot(optG));
	EXPECT_TRUE(sceneNoTex->isMissingTexture());

	//Import the scene with texture but not found
	auto sceneTex = controller->loadSceneFromFile(getDataPath(texturedModel2), errCode);
	EXPECT_EQ(errCode, 0);
	EXPECT_TRUE(sceneTex);
	EXPECT_TRUE(sceneTex->getRoot(defaultG));
	EXPECT_TRUE(sceneTex->getRoot(optG));
	EXPECT_FALSE(sceneTex->isMissingTexture());

	//FIXME: need to test with change of config, but this is probably not trival.
}<commit_msg>ISSUE #558 update test to the new behaviour<commit_after>/**
*  Copyright (C) 2015 3D Repo Ltd
*
*  This program is free software: you can redistribute it and/or modify
*  it under the terms of the GNU Affero General Public License as
*  published by the Free Software Foundation, either version 3 of the
*  License, or (at your option) any later version.
*
*  This program 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 Affero General Public License for more details.
*
*  You should have received a copy of the GNU Affero General Public License
*  along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include <cstdlib>
#include <gtest/gtest.h>
#include <repo/repo_controller.h>
#include <repo/error_codes.h>
#include "../repo_test_database_info.h"
#include "../repo_test_fileservice_info.h"
#include "../repo_test_utils.h"

using namespace repo;

static std::shared_ptr<RepoController> getController()
{
	static std::shared_ptr<RepoController> controller =
		std::make_shared<RepoController>();

	return controller;
}

TEST(RepoControllerTest, CommitScene) {
	auto controller = getController();
	auto token = initController(controller.get());
	//Try to commit a scene without setting db/project name
	uint8_t errCode;
	auto scene = controller->loadSceneFromFile(getDataPath(simpleModel), errCode);
	EXPECT_EQ(REPOERR_OK, errCode);
	EXPECT_EQ(REPOERR_UNKNOWN_ERR, controller->commitScene(token, scene));
	EXPECT_FALSE(scene->isRevisioned());

	//Trying to commit a scene with empty db and project name should also fail
	scene->setDatabaseAndProjectName("", "");
	EXPECT_EQ(REPOERR_UNKNOWN_ERR, controller->commitScene(token, scene));
	EXPECT_FALSE(scene->isRevisioned());

	scene->setDatabaseAndProjectName("balh", "");
	EXPECT_EQ(REPOERR_UNKNOWN_ERR, controller->commitScene(token, scene));
	EXPECT_FALSE(scene->isRevisioned());

	scene->setDatabaseAndProjectName("", "blah");
	EXPECT_EQ(REPOERR_UNKNOWN_ERR, controller->commitScene(token, scene));
	EXPECT_FALSE(scene->isRevisioned());

	//Setting the db name and project name should allow commit successfully
	scene->setDatabaseAndProjectName("commitSceneTest", "commitCube");
	EXPECT_EQ(REPOERR_OK, controller->commitScene(initController(controller.get()), scene));
	EXPECT_TRUE(scene->isRevisioned());
	EXPECT_TRUE(projectExists("commitSceneTest", "commitCube"));
	EXPECT_EQ(scene->getOwner(), "ANONYMOUS USER");

	auto scene2 = controller->loadSceneFromFile(getDataPath(simpleModel), errCode);
	std::string owner = "dog";
	EXPECT_EQ(errCode, 0);
	scene2->setDatabaseAndProjectName("commitSceneTest", "commitCube2");
	EXPECT_EQ(REPOERR_OK, controller->commitScene(initController(controller.get()), scene2, owner));
	EXPECT_TRUE(scene2->isRevisioned());
	EXPECT_TRUE(projectExists("commitSceneTest", "commitCube2"));
	EXPECT_EQ(scene2->getOwner(), owner);

	//null pointer checks
	EXPECT_EQ(REPOERR_UNKNOWN_ERR, controller->commitScene(token, nullptr));
	EXPECT_EQ(REPOERR_UNKNOWN_ERR, controller->commitScene(nullptr, scene));
}

TEST(RepoControllerTest, LoadSceneFromFile) {
	auto controller = getController();
	auto defaultG = core::model::RepoScene::GraphType::DEFAULT;
	auto optG = core::model::RepoScene::GraphType::OPTIMIZED;

	//standard import
	uint8_t errCode;
	auto scene = controller->loadSceneFromFile(getDataPath(simpleModel), errCode);
	ASSERT_TRUE(scene);
	EXPECT_EQ(errCode, 0);
	ASSERT_TRUE(scene->getRoot(defaultG));
	ASSERT_TRUE(scene->getRoot(optG));
	EXPECT_FALSE(scene->isMissingTexture());
	EXPECT_FALSE(scene->isRevisioned());
	EXPECT_TRUE(dynamic_cast<core::model::TransformationNode*>(scene->getRoot(defaultG))->isIdentity());

	//Import the scene with no transformation reduction
	auto sceneNoReduction = controller->loadSceneFromFile(getDataPath(simpleModel), errCode, repo::manipulator::modelconvertor::ModelImportConfig(false));
	EXPECT_EQ(errCode, 0);
	EXPECT_TRUE(sceneNoReduction);
	EXPECT_TRUE(sceneNoReduction->getRoot(defaultG));
	EXPECT_TRUE(sceneNoReduction->getRoot(optG));
	EXPECT_FALSE(sceneNoReduction->isMissingTexture());
	//There should be more transformations in the non-reduced scene than the standard scene
	EXPECT_TRUE(sceneNoReduction->getAllTransformations(defaultG).size()
			> scene->getAllTransformations(defaultG).size());

	//Import the scene with root trans rotated
	auto sceneRotated = controller->loadSceneFromFile(getDataPath(simpleModel), errCode, repo::manipulator::modelconvertor::ModelImportConfig(true, true, true));
	EXPECT_EQ(errCode, 0);
	EXPECT_TRUE(sceneRotated);
	ASSERT_TRUE(sceneRotated->getRoot(defaultG));
	EXPECT_TRUE(sceneRotated->getRoot(optG));
	EXPECT_FALSE(sceneRotated->isMissingTexture());
	//The root transformation should not be an identity
	core::model::TransformationNode *rootTrans = dynamic_cast<core::model::TransformationNode*>(sceneRotated->getRoot(defaultG));
	EXPECT_FALSE(rootTrans->isIdentity());

	//Import the scene with non existant file
	auto sceneNoFile = controller->loadSceneFromFile("thisFileDoesntExist.obj", errCode);
	EXPECT_EQ(errCode, REPOERR_MODEL_FILE_READ);
	EXPECT_FALSE(sceneNoFile);

	//Import the scene with bad Extension
	auto sceneBadExt = controller->loadSceneFromFile(getDataPath(badExtensionFile), errCode);
	EXPECT_EQ(errCode, REPOERR_FILE_TYPE_NOT_SUPPORTED);
	EXPECT_FALSE(sceneBadExt);

	//Import the scene with texture but not found
	auto sceneNoTex = controller->loadSceneFromFile(getDataPath(texturedModel), errCode);
	EXPECT_EQ(errCode, 0);
	EXPECT_TRUE(sceneNoTex);
	EXPECT_TRUE(sceneNoTex->getRoot(defaultG));
	EXPECT_TRUE(sceneNoTex->getRoot(optG));
	EXPECT_TRUE(sceneNoTex->isMissingTexture());

	//Import the scene with texture but not found
	auto sceneTex = controller->loadSceneFromFile(getDataPath(texturedModel2), errCode);
	EXPECT_EQ(errCode, 0);
	EXPECT_TRUE(sceneTex);
	EXPECT_TRUE(sceneTex->getRoot(defaultG));
	EXPECT_TRUE(sceneTex->getRoot(optG));
	EXPECT_FALSE(sceneTex->isMissingTexture());

	//FIXME: need to test with change of config, but this is probably not trival.
}<|endoftext|>


<commit_before>/****************************************************************************
 *
 *   Copyright (c) 2017 PX4 Development Team. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. 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.
 * 3. Neither the name PX4 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.
 *
 ****************************************************************************/

/**
 * @file camera_feedback.cpp
 *
 * Online and offline geotagging from camera feedback
 *
 * @author Mohammed Kabir <kabir@uasys.io>
 */

#include "camera_feedback.hpp"

namespace camera_feedback
{
CameraFeedback	*g_camera_feedback;
}

CameraFeedback::CameraFeedback() :
	_task_should_exit(false),
	_main_task(-1),
	_trigger_sub(-1),
	_lpos_sub(-1),
	_gpos_sub(-1),
	_att_sub(-1),
	_capture_pub(nullptr),
	_camera_feedback_mode(CAMERA_FEEDBACK_MODE_NONE)
{

	// Parameters
	_p_feedback = param_find("CAM_FBACK_MODE");

	param_get(_p_feedback, &_camera_feedback_mode);

}

CameraFeedback::~CameraFeedback()
{

	if (_main_task != -1) {

		/* task wakes up every 100ms or so at the longest */
		_task_should_exit = true;

		/* wait for a second for the task to quit at our request */
		unsigned i = 0;

		do {
			/* wait 20ms */
			usleep(20000);

			/* if we have given up, kill it */
			if (++i > 50) {
				px4_task_delete(_main_task);
				break;
			}
		} while (_main_task != -1);
	}

	camera_feedback::g_camera_feedback = nullptr;
}

int
CameraFeedback::start()
{

	ASSERT(_main_task == -1);

	/* start the task */
	_main_task = px4_task_spawn_cmd("camera_feedback",
					SCHED_DEFAULT,
					SCHED_PRIORITY_DEFAULT + 15,
					1600,
					(px4_main_t)&CameraFeedback::task_main_trampoline,
					nullptr);

	if (_main_task < 0) {
		warn("task start failed");
		return -errno;
	}

	return OK;

}

void
CameraFeedback::stop()
{
	if (camera_feedback::g_camera_feedback != nullptr) {
		delete (camera_feedback::g_camera_feedback);
	}
}

void
CameraFeedback::task_main()
{

	// We only support trigger feedback for now
	// This will later be extended to support hardware feedback from the camera.
	if (_camera_feedback_mode != CAMERA_FEEDBACK_MODE_TRIGGER) {
		return;
	}

	// Polling sources
	_trigger_sub = orb_subscribe(ORB_ID(camera_trigger));
	struct camera_trigger_s trig = {};

	px4_pollfd_struct_t fds[1] = {};
	fds[0].fd = _trigger_sub;
	fds[0].events = POLLIN;

	// Geotagging subscriptions
	_lpos_sub = orb_subscribe(ORB_ID(vehicle_local_position));
	_gpos_sub = orb_subscribe(ORB_ID(vehicle_global_position));
	_att_sub = orb_subscribe(ORB_ID(vehicle_attitude));
	struct vehicle_local_position_s lpos = {};
	struct vehicle_global_position_s gpos = {};
	struct vehicle_attitude_s att = {};

	bool updated = false;

	while (!_task_should_exit) {

		/* wait for up to 20ms for data */
		int pret = px4_poll(&fds[0], (sizeof(fds) / sizeof(fds[0])), 20);

		if (pret < 0) {
			PX4_WARN("poll error %d, %d", pret, errno);
			continue;
		}

		/* trigger subscription updated */
		if (fds[0].revents & POLLIN) {

			orb_copy(ORB_ID(camera_trigger), _trigger_sub, &trig);

			/* update geotagging subscriptions */
			orb_check(_gpos_sub, &updated);

			if (updated) {
				orb_copy(ORB_ID(vehicle_global_position), _gpos_sub, &gpos);
			}

			orb_check(_lpos_sub, &updated);

			if (updated) {
				orb_copy(ORB_ID(vehicle_local_position), _lpos_sub, &lpos);
			}

			orb_check(_att_sub, &updated);

			if (updated) {
				orb_copy(ORB_ID(vehicle_attitude), _att_sub, &att);
			}

			if (gpos.timestamp == 0 ||
			    lpos.timestamp == 0 ||
			    att.timestamp == 0 ||
			    !lpos.xy_valid) {
				// reject until we have valid data
				continue;
			}

			struct camera_capture_s capture = {};

			// Fill timestamps
			capture.timestamp = trig.timestamp;

			capture.timestamp_utc = trig.timestamp_utc;

			// Fill image sequence
			capture.seq = trig.seq;

			// Fill position data
			capture.lat = gpos.lat;

			capture.lon = gpos.lon;

			capture.alt = gpos.alt;

			capture.ground_distance = lpos.dist_bottom_valid ? lpos.dist_bottom : -1.0f;

			// Fill attitude data
			// TODO : this needs to be rotated by camera orientation or set to gimbal orientation when available
			capture.q[0] = att.q[0];

			capture.q[1] = att.q[1];

			capture.q[2] = att.q[2];

			capture.q[3] = att.q[3];

			// Indicate that no capture feedback from camera is available
			capture.result = -1;

			int instance_id;

			orb_publish_auto(ORB_ID(camera_capture), &_capture_pub, &capture, &instance_id, ORB_PRIO_DEFAULT);

		}

	}

	PX4_INFO("Exiting.");
	_main_task = -1;

}

void
CameraFeedback::task_main_trampoline(int argc, char *argv[])
{
	camera_feedback::g_camera_feedback->task_main();
}

static int usage()
{
	PX4_INFO("usage: camera_feedback {start|stop}\n");
	return 1;
}

extern "C" __EXPORT int camera_feedback_main(int argc, char *argv[]);

int camera_feedback_main(int argc, char *argv[])
{
	if (argc < 2) {
		return usage();
	}

	if (!strcmp(argv[1], "start")) {

		if (camera_feedback::g_camera_feedback != nullptr) {
			PX4_WARN("already running");
			return 0;
		}

		camera_feedback::g_camera_feedback = new CameraFeedback();

		if (camera_feedback::g_camera_feedback == nullptr) {
			PX4_WARN("alloc failed");
			return 1;
		}

		camera_feedback::g_camera_feedback->start();
		return 0;
	}

	if (camera_feedback::g_camera_feedback == nullptr) {
		PX4_WARN("not running");
		return 1;

	} else if (!strcmp(argv[1], "stop")) {
		camera_feedback::g_camera_feedback->stop();

	} else {
		return usage();
	}

	return 0;
}<commit_msg>camera_feedback : only use global position subscription to fill geotagging packet<commit_after>/****************************************************************************
 *
 *   Copyright (c) 2017 PX4 Development Team. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. 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.
 * 3. Neither the name PX4 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.
 *
 ****************************************************************************/

/**
 * @file camera_feedback.cpp
 *
 * Online and offline geotagging from camera feedback
 *
 * @author Mohammed Kabir <kabir@uasys.io>
 */

#include "camera_feedback.hpp"

namespace camera_feedback
{
CameraFeedback	*g_camera_feedback;
}

CameraFeedback::CameraFeedback() :
	_task_should_exit(false),
	_main_task(-1),
	_trigger_sub(-1),
	_gpos_sub(-1),
	_att_sub(-1),
	_capture_pub(nullptr),
	_camera_feedback_mode(CAMERA_FEEDBACK_MODE_NONE)
{

	// Parameters
	_p_feedback = param_find("CAM_FBACK_MODE");

	param_get(_p_feedback, &_camera_feedback_mode);

}

CameraFeedback::~CameraFeedback()
{

	if (_main_task != -1) {

		/* task wakes up every 100ms or so at the longest */
		_task_should_exit = true;

		/* wait for a second for the task to quit at our request */
		unsigned i = 0;

		do {
			/* wait 20ms */
			usleep(20000);

			/* if we have given up, kill it */
			if (++i > 50) {
				px4_task_delete(_main_task);
				break;
			}
		} while (_main_task != -1);
	}

	camera_feedback::g_camera_feedback = nullptr;
}

int
CameraFeedback::start()
{

	ASSERT(_main_task == -1);

	/* start the task */
	_main_task = px4_task_spawn_cmd("camera_feedback",
					SCHED_DEFAULT,
					SCHED_PRIORITY_DEFAULT + 15,
					1600,
					(px4_main_t)&CameraFeedback::task_main_trampoline,
					nullptr);

	if (_main_task < 0) {
		warn("task start failed");
		return -errno;
	}

	return OK;

}

void
CameraFeedback::stop()
{
	if (camera_feedback::g_camera_feedback != nullptr) {
		delete (camera_feedback::g_camera_feedback);
	}
}

void
CameraFeedback::task_main()
{

	// We only support trigger feedback for now
	// This will later be extended to support hardware feedback from the camera.
	if (_camera_feedback_mode != CAMERA_FEEDBACK_MODE_TRIGGER) {
		return;
	}

	// Polling sources
	_trigger_sub = orb_subscribe(ORB_ID(camera_trigger));
	struct camera_trigger_s trig = {};

	px4_pollfd_struct_t fds[1] = {};
	fds[0].fd = _trigger_sub;
	fds[0].events = POLLIN;

	// Geotagging subscriptions
	_gpos_sub = orb_subscribe(ORB_ID(vehicle_global_position));
	_att_sub = orb_subscribe(ORB_ID(vehicle_attitude));
	struct vehicle_global_position_s gpos = {};
	struct vehicle_attitude_s att = {};

	bool updated = false;

	while (!_task_should_exit) {

		/* wait for up to 20ms for data */
		int pret = px4_poll(&fds[0], (sizeof(fds) / sizeof(fds[0])), 20);

		if (pret < 0) {
			PX4_WARN("poll error %d, %d", pret, errno);
			continue;
		}

		/* trigger subscription updated */
		if (fds[0].revents & POLLIN) {

			orb_copy(ORB_ID(camera_trigger), _trigger_sub, &trig);

			/* update geotagging subscriptions */
			orb_check(_gpos_sub, &updated);

			if (updated) {
				orb_copy(ORB_ID(vehicle_global_position), _gpos_sub, &gpos);
			}

			orb_check(_att_sub, &updated);

			if (updated) {
				orb_copy(ORB_ID(vehicle_attitude), _att_sub, &att);
			}

			if (trig.timestamp == 0 ||
			    gpos.timestamp == 0 ||
			    att.timestamp == 0) {
				// reject until we have valid data
				continue;
			}

			struct camera_capture_s capture = {};

			// Fill timestamps
			capture.timestamp = trig.timestamp;

			capture.timestamp_utc = trig.timestamp_utc;

			// Fill image sequence
			capture.seq = trig.seq;

			// Fill position data
			capture.lat = gpos.lat;

			capture.lon = gpos.lon;

			capture.alt = gpos.alt;

			capture.ground_distance = gpos.terrain_alt_valid ? (gpos.alt - gpos.terrain_alt) : -1.0f;

			// Fill attitude data
			// TODO : this needs to be rotated by camera orientation or set to gimbal orientation when available
			capture.q[0] = att.q[0];

			capture.q[1] = att.q[1];

			capture.q[2] = att.q[2];

			capture.q[3] = att.q[3];

			// Indicate that no capture feedback from camera is available
			capture.result = -1;

			int instance_id;

			orb_publish_auto(ORB_ID(camera_capture), &_capture_pub, &capture, &instance_id, ORB_PRIO_DEFAULT);

		}

	}

	PX4_INFO("Exiting.");
	_main_task = -1;

}

void
CameraFeedback::task_main_trampoline(int argc, char *argv[])
{
	camera_feedback::g_camera_feedback->task_main();
}

static int usage()
{
	PX4_INFO("usage: camera_feedback {start|stop}\n");
	return 1;
}

extern "C" __EXPORT int camera_feedback_main(int argc, char *argv[]);

int camera_feedback_main(int argc, char *argv[])
{
	if (argc < 2) {
		return usage();
	}

	if (!strcmp(argv[1], "start")) {

		if (camera_feedback::g_camera_feedback != nullptr) {
			PX4_WARN("already running");
			return 0;
		}

		camera_feedback::g_camera_feedback = new CameraFeedback();

		if (camera_feedback::g_camera_feedback == nullptr) {
			PX4_WARN("alloc failed");
			return 1;
		}

		camera_feedback::g_camera_feedback->start();
		return 0;
	}

	if (camera_feedback::g_camera_feedback == nullptr) {
		PX4_WARN("not running");
		return 1;

	} else if (!strcmp(argv[1], "stop")) {
		camera_feedback::g_camera_feedback->stop();

	} else {
		return usage();
	}

	return 0;
}<|endoftext|>


<commit_before>#ifndef EBITEN_GRAPHICS_DETAIL_OPENGL_DEVICE_HPP
#define EBITEN_GRAPHICS_DETAIL_OPENGL_DEVICE_HPP

#include "ebiten/graphics/detail/opengl/graphics_context.hpp"
#include "ebiten/graphics/detail/opengl/opengl_initializer.hpp"
#include "ebiten/graphics/detail/opengl/texture_factory.hpp"
#include "ebiten/graphics/detail/opengl/texture_pointer.hpp"
#include "ebiten/graphics/native_view.hpp"
#include "ebiten/noncopyable.hpp"
#include "ebiten/platform.hpp"

#ifdef EBITEN_MACOSX
# include <OpenGL/gl.h>
#endif
#ifdef EBITEN_IOS
# import <GLKit/GLKit.h>
#endif

#include <cassert>
#include <functional>
#include <memory>

namespace ebiten {
namespace graphics {
namespace detail {

class device : private noncopyable {
private:
  std::size_t screen_width_;
  std::size_t screen_height_;
  std::size_t const screen_scale_;
  std::function<bool(texture_factory&)> update_func_;
  std::function<void(graphics_context&, texture&)> draw_func_;
  texture_factory texture_factory_;
  graphics_context graphics_context_;
  texture_pointer offscreen_texture_;
  opengl_initializer opengl_initializer_;
public:
  device(std::size_t screen_width,
         std::size_t screen_height,
         std::size_t screen_scale,
         native_view native_view,
         std::function<bool(texture_factory&)> const& update_func,
         std::function<void(graphics_context&, texture&)> const& draw_func)
    : screen_width_(screen_width),
      screen_height_(screen_height),
      screen_scale_(screen_scale),
      update_func_(update_func),
      draw_func_(draw_func),
      texture_factory_(),
      graphics_context_(screen_width,
                        screen_height,
                        screen_scale,
                        this->texture_factory_),
      opengl_initializer_(native_view) {
    assert(0 < this->screen_width_);
    assert(0 < this->screen_height_);
    assert(0 < this->screen_scale_);
    assert(this->screen_width_  <= 4096);
    assert(this->screen_height_ <= 4096);
    assert(this->screen_scale_  <= 4);
    assert(this->update_func_);
    assert(this->draw_func_);
    this->opengl_initializer_.initialize(std::bind(&device::update, this));
  }
  ~device() {
    // TODO: implement
  }
  void
  set_screen_size(std::size_t screen_width,
                  std::size_t screen_height) {
    assert(0 < screen_width);
    assert(0 < screen_height);
    assert(screen_width  <= 4096);
    assert(screen_height <= 4096);
    this->screen_width_  = screen_width;
    this->screen_height_ = screen_height;
    this->graphics_context_.set_screen_size(screen_width, screen_height);
    if (!this->offscreen_texture_) {
      return;
    }
    // TODO: How about the OpenGL context?
    // TODO: Should a texture include its framebuffer?
    this->graphics_context_.delete_framebuffer(*this->offscreen_texture_);
    this->offscreen_texture_ = nullptr;
  }
private:
  /*
   * NOTICE:
   *   The OpenGL functions should be called only in this method 'update'.
   *   Is that better to add an argument to this method?
   */
  bool
  update() {
    try {
      if (!this->offscreen_texture_) {
        this->offscreen_texture_ = this->texture_factory_.create(this->screen_width_,
                                                                 this->screen_height_);
      }
      assert(static_cast<bool>(this->offscreen_texture_));
      bool const terminated = this->update_func_(this->texture_factory_);
      detail::graphics_context& g = this->graphics_context_;
      ::glEnable(GL_TEXTURE_2D);
      ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
      ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
      g.set_offscreen(*this->offscreen_texture_);
      g.clear();
      this->draw_func_(g, *this->offscreen_texture_);
      g.flush();
      ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
      ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
      g.reset_offscreen();
      g.clear();
      geometry_matrix geom_mat;
      geom_mat.set_a(this->screen_scale_);
      geom_mat.set_d(this->screen_scale_);
      g.draw_texture(*this->offscreen_texture_,
                     0, 0, this->screen_width_, this->screen_height_,
                     geom_mat, color_matrix::identity());
      g.flush();
      return terminated;
    } catch (std::runtime_error const& e) {
      // TODO: Logging
      std::cerr << e.what() << std::endl;
      std::abort();
    }
    return true;
  }
};

}
}
}

#endif
<commit_msg>Destroy the offscreen texture in the OpenGL context<commit_after>#ifndef EBITEN_GRAPHICS_DETAIL_OPENGL_DEVICE_HPP
#define EBITEN_GRAPHICS_DETAIL_OPENGL_DEVICE_HPP

#include "ebiten/graphics/detail/opengl/graphics_context.hpp"
#include "ebiten/graphics/detail/opengl/opengl_initializer.hpp"
#include "ebiten/graphics/detail/opengl/texture_factory.hpp"
#include "ebiten/graphics/detail/opengl/texture_pointer.hpp"
#include "ebiten/graphics/native_view.hpp"
#include "ebiten/noncopyable.hpp"
#include "ebiten/platform.hpp"

#ifdef EBITEN_MACOSX
# include <OpenGL/gl.h>
#endif
#ifdef EBITEN_IOS
# import <GLKit/GLKit.h>
#endif

#include <cassert>
#include <functional>
#include <memory>

namespace ebiten {
namespace graphics {
namespace detail {

class device : private noncopyable {
private:
  std::size_t screen_width_;
  std::size_t screen_height_;
  std::size_t const screen_scale_;
  std::function<bool(texture_factory&)> update_func_;
  std::function<void(graphics_context&, texture&)> draw_func_;
  texture_factory texture_factory_;
  graphics_context graphics_context_;
  texture_pointer offscreen_texture_;
  opengl_initializer opengl_initializer_;
  bool to_destory_offscreen_texture_;
public:
  device(std::size_t screen_width,
         std::size_t screen_height,
         std::size_t screen_scale,
         native_view native_view,
         std::function<bool(texture_factory&)> const& update_func,
         std::function<void(graphics_context&, texture&)> const& draw_func)
    : screen_width_(screen_width),
      screen_height_(screen_height),
      screen_scale_(screen_scale),
      update_func_(update_func),
      draw_func_(draw_func),
      texture_factory_(),
      graphics_context_(screen_width,
                        screen_height,
                        screen_scale,
                        this->texture_factory_),
      opengl_initializer_(native_view),
      to_destory_offscreen_texture_(false) {
    assert(0 < this->screen_width_);
    assert(0 < this->screen_height_);
    assert(0 < this->screen_scale_);
    assert(this->screen_width_  <= 4096);
    assert(this->screen_height_ <= 4096);
    assert(this->screen_scale_  <= 4);
    assert(this->update_func_);
    assert(this->draw_func_);
    this->opengl_initializer_.initialize(std::bind(&device::update, this));
  }
  ~device() {
    // TODO: implement
  }
  void
  set_screen_size(std::size_t screen_width,
                  std::size_t screen_height) {
    assert(0 < screen_width);
    assert(0 < screen_height);
    assert(screen_width  <= 4096);
    assert(screen_height <= 4096);
    this->screen_width_  = screen_width;
    this->screen_height_ = screen_height;
    this->graphics_context_.set_screen_size(screen_width, screen_height);
    this->to_destory_offscreen_texture_ = true;
  }
private:
  /*
   * NOTICE:
   *   The OpenGL functions should be called only in this method 'update'.
   *   Is that better to add an argument to this method?
   */
  bool
  update() {
    try {
      if (this->to_destory_offscreen_texture_) {
        if (this->offscreen_texture_) {
          // TODO: Should a texture include its framebuffer?
          this->graphics_context_.delete_framebuffer(*this->offscreen_texture_);
          this->offscreen_texture_ = nullptr;
        }
        this->to_destory_offscreen_texture_ = false;
      }
      if (!this->offscreen_texture_) {
        this->offscreen_texture_ = this->texture_factory_.create(this->screen_width_,
                                                                 this->screen_height_);
      }
      assert(static_cast<bool>(this->offscreen_texture_));
      bool const terminated = this->update_func_(this->texture_factory_);
      detail::graphics_context& g = this->graphics_context_;
      ::glEnable(GL_TEXTURE_2D);
      ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
      ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
      g.set_offscreen(*this->offscreen_texture_);
      g.clear();
      this->draw_func_(g, *this->offscreen_texture_);
      g.flush();
      ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
      ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
      g.reset_offscreen();
      g.clear();
      geometry_matrix geom_mat;
      geom_mat.set_a(this->screen_scale_);
      geom_mat.set_d(this->screen_scale_);
      g.draw_texture(*this->offscreen_texture_,
                     0, 0, this->screen_width_, this->screen_height_,
                     geom_mat, color_matrix::identity());
      g.flush();
      return terminated;
    } catch (std::runtime_error const& e) {
      // TODO: Logging
      std::cerr << e.what() << std::endl;
      std::abort();
    }
    return true;
  }
};

}
}
}

#endif
<|endoftext|>


<commit_before>#pragma once

#include <sstream>
#include <string>
#include <chrono>
#include <tuple>

//#include "printPattern.hpp"
//#include "printPercentage.hpp"
//#include "printTime.hpp"
#include "Pattern.hpp"
#include "Time.hpp"
#include "Percentage.hpp"
#include "Label.hpp"

/**
 * @brief A set of fancy progress bars.
 */
namespace ElegantProgressbars{

/**
 * @brief Writes a fancy progressbar with minimal input
 *
 * Takes the total number of elements to process and creates a nice progressbar
 * from that. This is intended to be called in a loop / recursion / MPI thread
 * / pthread / etc. It should be called each time one of the nTotal elements is
 * done processing.
 * Operation can be fine-tuned to write time with high precision and different
 * length of the progressbar through template arguments.
 *
 * @param nTotal the total number of elements to process. If multiple values
 *               are supplied in different calls, the function will try to use
 *               the highest of those values.
 * @param current (optional) the element you are currently at. This can be used
 *               to overwrite the default behaviour (which is to assume that an
 *               element was successfully processed each time this function is
 *               called)
 * @param highPrecision (template, optional) if set to true, time will be
 *               displayed with additional milliseconds
 * @param length (template, optional) used to change the length of the printed
 *               progressbar
 *
 */
template<bool highPrecision = false, unsigned length = 50>
inline std::string fancyProgressBar(
    unsigned const nTotal,
    unsigned const current = 0
    ){

  using namespace std::chrono;
  typedef duration<long long int, std::milli> milliseconds;

  static unsigned maxNTotal = 0;
  static unsigned part = 0;
  static unsigned tic  = 0;
  static time_point<steady_clock> startTime;
  if(part==0){ startTime = steady_clock::now(); } // get the starting time on the very first call

  std::stringstream ss;
  auto const now = steady_clock::now();

  maxNTotal = std::max(maxNTotal, nTotal);
  part = current ? current : ++part;

  //limit the update intervall (not faster than every 35ms. This would be madness.)
  duration<float> const timeSpent = now - startTime;
  if(timeSpent.count() > 0.035f*tic || part == maxNTotal){
    ++tic;
    auto const percentage  = static_cast<float>(part) / static_cast<float>(maxNTotal);
    std::string s;
    unsigned h;

    ss << "\r";
    std::tie(s,h) = Label::print();
    ss << s;
    std::tie(s,h) = Pattern<length>::print(part, maxNTotal, percentage);
    ss << s;
    std::tie(s,h) = Percentage::print(part, maxNTotal, percentage);
    ss << s;
    std::tie(s,h) = Time<highPrecision>::print(part, maxNTotal, percentage);
    ss << s;
    if(part==maxNTotal) ss << std::endl;
    ss << std::flush;
  }

  return ss.str();
}

}
<commit_msg>Adopted @brief to autobrief option<commit_after>#pragma once

#include <sstream>
#include <string>
#include <chrono>
#include <tuple>

//#include "printPattern.hpp"
//#include "printPercentage.hpp"
//#include "printTime.hpp"
#include "Pattern.hpp"
#include "Time.hpp"
#include "Percentage.hpp"
#include "Label.hpp"

/**
 * A set of fancy progress bars.
 *
 */
namespace ElegantProgressbars{

/**
 * Writes a fancy progressbar with minimal input
 *
 * Takes the total number of elements to process and creates a nice progressbar
 * from that. This is intended to be called in a loop / recursion / MPI thread
 * / pthread / etc. It should be called each time one of the nTotal elements is
 * done processing.
 * Operation can be fine-tuned to write time with high precision and different
 * length of the progressbar through template arguments.
 *
 * @param nTotal the total number of elements to process. If multiple values
 *               are supplied in different calls, the function will try to use
 *               the highest of those values.
 * @param current (optional) the element you are currently at. This can be used
 *               to overwrite the default behaviour (which is to assume that an
 *               element was successfully processed each time this function is
 *               called)
 * @param highPrecision (template, optional) if set to true, time will be
 *               displayed with additional milliseconds
 * @param length (template, optional) used to change the length of the printed
 *               progressbar
 *
 */
template<bool highPrecision = false, unsigned length = 50>
inline std::string fancyProgressBar(
    unsigned const nTotal,
    unsigned const current = 0
    ){

  using namespace std::chrono;
  typedef duration<long long int, std::milli> milliseconds;

  static unsigned maxNTotal = 0;
  static unsigned part = 0;
  static unsigned tic  = 0;
  static time_point<steady_clock> startTime;
  if(part==0){ startTime = steady_clock::now(); } // get the starting time on the very first call

  std::stringstream ss;
  auto const now = steady_clock::now();

  maxNTotal = std::max(maxNTotal, nTotal);
  part = current ? current : ++part;

  //limit the update intervall (not faster than every 35ms. This would be madness.)
  duration<float> const timeSpent = now - startTime;
  if(timeSpent.count() > 0.035f*tic || part == maxNTotal){
    ++tic;
    auto const percentage  = static_cast<float>(part) / static_cast<float>(maxNTotal);
    std::string s;
    unsigned h;

    ss << "\r";
    std::tie(s,h) = Label::print();
    ss << s;
    std::tie(s,h) = Pattern<length>::print(part, maxNTotal, percentage);
    ss << s;
    std::tie(s,h) = Percentage::print(part, maxNTotal, percentage);
    ss << s;
    std::tie(s,h) = Time<highPrecision>::print(part, maxNTotal, percentage);
    ss << s;
    if(part==maxNTotal) ss << std::endl;
    ss << std::flush;
  }

  return ss.str();
}

}
<|endoftext|>