Exemple #1
0
            crimild::Bool encode( std::string key, containers::Array< T, U > &a )
            {
                crimild::Size N = a.size();
                encodeArrayBegin( key, N );
                
                a.each( [ this, key ]( T &elem, crimild::Size i ) {
					auto itemKey = beginEncodingArrayElement( key, i );
                    encode( itemKey, elem );
					endEncodingArrayElement( key, i );
                });
                
                encodeArrayEnd( key );

				return true;
            }
Exemple #2
0
void BufferGLTest::mapRangeExplicitFlush() {
    #ifndef MAGNUM_TARGET_GLES2
    if(!Context::current()->isExtensionSupported<Extensions::GL::ARB::map_buffer_range>())
        CORRADE_SKIP(Extensions::GL::ARB::map_buffer_range::string() + std::string(" is not supported"));
    #else
    if(!Context::current()->isExtensionSupported<Extensions::GL::EXT::map_buffer_range>())
        CORRADE_SKIP(Extensions::GL::EXT::map_buffer_range::string() + std::string(" is not supported"));
    #endif

    constexpr char data[] = {2, 7, 5, 13, 25};
    Buffer buffer;
    buffer.setData(data, Buffer::Usage::StaticDraw);

    /* Map, set byte, don't flush and unmap */
    char* contents = reinterpret_cast<char*>(buffer.map(1, 4, Buffer::MapFlag::Write|Buffer::MapFlag::FlushExplicit));
    CORRADE_VERIFY(contents);
    contents[2] = 99;
    CORRADE_VERIFY(buffer.unmap());
    MAGNUM_VERIFY_NO_ERROR();

    /* Unflushed range _might_ not be changed, thus nothing to test */

    /* Map, set byte, flush and unmap */
    contents = reinterpret_cast<char*>(buffer.map(1, 4, Buffer::MapFlag::Write|Buffer::MapFlag::FlushExplicit));
    CORRADE_VERIFY(contents);
    contents[3] = 107;
    buffer.flushMappedRange(3, 1);
    MAGNUM_VERIFY_NO_ERROR();
    CORRADE_VERIFY(buffer.unmap());
    MAGNUM_VERIFY_NO_ERROR();

    /* Flushed range should be changed */
    /** @todo How to verify the contents in ES? */
    #ifndef MAGNUM_TARGET_GLES
    Containers::Array<char> changedContents = buffer.data<char>();
    CORRADE_COMPARE(changedContents.size(), 5);
    CORRADE_COMPARE(changedContents[4], 107);
    #endif
}
void CompressIndicesTest::compressShort() {
    std::size_t indexCount;
    Mesh::IndexType indexType;
    Containers::Array<char> data;
    std::tie(indexCount, indexType, data) = MeshTools::compressIndices(
        std::vector<UnsignedInt>{1, 256, 0, 5});

    CORRADE_COMPARE(indexCount, 4);
    CORRADE_VERIFY(indexType == Mesh::IndexType::UnsignedShort);
    if(!Utility::Endianness::isBigEndian()) {
        CORRADE_COMPARE(std::vector<char>(data.begin(), data.end()),
            (std::vector<char>{ 0x01, 0x00,
                           0x00, 0x01,
                           0x00, 0x00,
                           0x05, 0x00 }));
    } else {
        CORRADE_COMPARE(std::vector<char>(data.begin(), data.end()),
            (std::vector<char>{ 0x00, 0x01,
                           0x01, 0x00,
                           0x00, 0x00,
                           0x00, 0x05 }));
    }
}
Exemple #4
0
void ArrayTest::access() {
    Array a(7);
    for(std::size_t i = 0; i != 7; ++i)
        a[i] = i;

    CORRADE_COMPARE(a.data(), static_cast<int*>(a));
    CORRADE_COMPARE(*(a.begin()+2), 2);
    CORRADE_COMPARE(a[4], 4);
    CORRADE_COMPARE(a.end()-a.begin(), a.size());

    const auto b = Array::from(7, 3, 5, 4);
    CORRADE_COMPARE(b.data(), static_cast<const int*>(b));
    CORRADE_COMPARE(b[2], 5);
}
void BufferGLTest::copy() {
    Buffer buffer1;
    constexpr char data[] = {2, 7, 5, 13, 25};
    buffer1.setData(data, BufferUsage::StaticCopy);

    Buffer buffer2;
    buffer2.setData({nullptr, 5}, BufferUsage::StaticRead);

    Buffer::copy(buffer1, buffer2, 1, 2, 3);
    MAGNUM_VERIFY_NO_ERROR();

    /** @todo How to verify the contents in ES? */
    #ifndef MAGNUM_TARGET_GLES
    const Containers::Array<char> subContents = buffer2.subData<char>(2, 3);
    CORRADE_COMPARE(subContents.size(), 3);
    CORRADE_COMPARE(subContents[0], 7);
    CORRADE_COMPARE(subContents[1], 5);
    CORRADE_COMPARE(subContents[2], 13);
    #endif
}
std::optional<ImageData2D> JpegImporter::doImage2D(UnsignedInt) {
    /* Initialize structures */
    jpeg_decompress_struct file;
    Containers::Array<JSAMPROW> rows;
    Containers::Array<char> data;

    /* Fugly error handling stuff */
    /** @todo Get rid of this crap */
    struct ErrorManager {
        jpeg_error_mgr jpegErrorManager;
        std::jmp_buf setjmpBuffer;
    } errorManager;
    file.err = jpeg_std_error(&errorManager.jpegErrorManager);
    errorManager.jpegErrorManager.error_exit = [](j_common_ptr info) {
        info->err->output_message(info);
        std::longjmp(reinterpret_cast<ErrorManager*>(info->err)->setjmpBuffer, 1);
    };
    if(setjmp(errorManager.setjmpBuffer)) {
        Error() << "Trade::JpegImporter::image2D(): error while reading JPEG file";

        jpeg_destroy_decompress(&file);
        return std::nullopt;
    }

    /* Open file */
    jpeg_create_decompress(&file);
    jpeg_mem_src(&file, _in.begin(), _in.size());

    /* Read file header, start decompression */
    jpeg_read_header(&file, true);
    jpeg_start_decompress(&file);

    /* Image size and type */
    const Vector2i size(file.output_width, file.output_height);
    static_assert(BITS_IN_JSAMPLE == 8, "Only 8-bit JPEG is supported");
    constexpr PixelType type = PixelType::UnsignedByte;

    /* Image format */
    PixelFormat format = {};
    switch(file.out_color_space) {
        case JCS_GRAYSCALE:
            CORRADE_INTERNAL_ASSERT(file.out_color_components == 1);
            #ifdef MAGNUM_TARGET_GLES2
            format = Context::hasCurrent() && Context::current().isExtensionSupported<Extensions::GL::EXT::texture_rg>() ?
                PixelFormat::Red : PixelFormat::Luminance;
            #else
            format = PixelFormat::Red;
            #endif
            break;
        case JCS_RGB:
            CORRADE_INTERNAL_ASSERT(file.out_color_components == 3);
            format = PixelFormat::RGB;
            break;

        /** @todo RGBA (only in libjpeg-turbo and probably ignored) */

        default:
            Error() << "Trade::JpegImporter::image2D(): unsupported color space" << file.out_color_space;
            return std::nullopt;
    }

    /* Initialize data array, align rows to four bytes */
    const std::size_t stride = ((size.x()*file.out_color_components*BITS_IN_JSAMPLE/8 + 3)/4)*4;
    data = Containers::Array<char>{stride*std::size_t(size.y())};

    /* Read image row by row */
    rows = Containers::Array<JSAMPROW>{std::size_t(size.y())};
    for(Int i = 0; i != size.y(); ++i)
        rows[i] = reinterpret_cast<JSAMPROW>(data.data()) + (size.y() - i - 1)*stride;
    while(file.output_scanline < file.output_height)
        jpeg_read_scanlines(&file, rows + file.output_scanline, file.output_height - file.output_scanline);

    /* Cleanup */
    jpeg_finish_decompress(&file);
    jpeg_destroy_decompress(&file);

    /* Always using the default 4-byte alignment */
    return Trade::ImageData2D{format, type, size, std::move(data)};
}
namespace Magnum { namespace Test {

struct BufferGLTest: AbstractOpenGLTester {
    explicit BufferGLTest();

    void construct();
    void constructCopy();
    void constructMove();
    void wrap();

    void label();

    #ifndef MAGNUM_TARGET_GLES2
    void bindBase();
    void bindRange();
    #endif

    void data();
    void map();
    #ifdef CORRADE_TARGET_NACL
    void mapSub();
    #endif
    void mapRange();
    void mapRangeExplicitFlush();
    #ifndef MAGNUM_TARGET_GLES2
    void copy();
    #endif
    void invalidate();
};

BufferGLTest::BufferGLTest() {
    addTests({&BufferGLTest::construct,
              &BufferGLTest::constructCopy,
              &BufferGLTest::constructMove,
              &BufferGLTest::wrap,

              &BufferGLTest::label,

              #ifndef MAGNUM_TARGET_GLES2
              &BufferGLTest::bindBase,
              &BufferGLTest::bindRange,
              #endif

              &BufferGLTest::data,
              &BufferGLTest::map,
              #ifdef CORRADE_TARGET_NACL
              &BufferGLTest::mapSub,
              #endif
              &BufferGLTest::mapRange,
              &BufferGLTest::mapRangeExplicitFlush,
              #ifndef MAGNUM_TARGET_GLES2
              &BufferGLTest::copy,
              #endif
              &BufferGLTest::invalidate});
}

void BufferGLTest::construct() {
    {
        Buffer buffer;

        MAGNUM_VERIFY_NO_ERROR();
        CORRADE_VERIFY(buffer.id() > 0);
        CORRADE_COMPARE(buffer.targetHint(), Buffer::TargetHint::Array);
        CORRADE_COMPARE(buffer.size(), 0);
    }

    MAGNUM_VERIFY_NO_ERROR();
}

void BufferGLTest::constructCopy() {
    CORRADE_VERIFY(!(std::is_constructible<Buffer, const Buffer&>{}));
    CORRADE_VERIFY(!(std::is_assignable<Buffer, const Buffer&>{}));
}

void BufferGLTest::constructMove() {
    Buffer a;
    const Int id = a.id();

    MAGNUM_VERIFY_NO_ERROR();
    CORRADE_VERIFY(id > 0);

    Buffer b(std::move(a));

    CORRADE_COMPARE(a.id(), 0);
    CORRADE_COMPARE(b.id(), id);

    Buffer c;
    const Int cId = c.id();
    c = std::move(b);

    MAGNUM_VERIFY_NO_ERROR();
    CORRADE_VERIFY(cId > 0);
    CORRADE_COMPARE(b.id(), cId);
    CORRADE_COMPARE(c.id(), id);
}

void BufferGLTest::wrap() {
    GLuint id;
    glGenBuffers(1, &id);

    /* Releasing won't delete anything */
    {
        auto buffer = Buffer::wrap(id, ObjectFlag::DeleteOnDestruction);
        CORRADE_COMPARE(buffer.release(), id);
    }

    /* ...so we can wrap it again */
    Buffer::wrap(id);
    glDeleteBuffers(1, &id);
}

void BufferGLTest::label() {
    /* No-Op version is tested in AbstractObjectGLTest */
    if(!Context::current()->isExtensionSupported<Extensions::GL::KHR::debug>() &&
       !Context::current()->isExtensionSupported<Extensions::GL::EXT::debug_label>())
        CORRADE_SKIP("Required extension is not available");

    Buffer buffer;

    CORRADE_COMPARE(buffer.label(), "");
    MAGNUM_VERIFY_NO_ERROR();

    buffer.setLabel("MyBuffer");
    MAGNUM_VERIFY_NO_ERROR();

    CORRADE_COMPARE(buffer.label(), "MyBuffer");
}

#ifndef MAGNUM_TARGET_GLES2
void BufferGLTest::bindBase() {
    #ifndef MAGNUM_TARGET_GLES
    if(!Context::current()->isExtensionSupported<Extensions::GL::ARB::uniform_buffer_object>())
        CORRADE_SKIP(Extensions::GL::ARB::uniform_buffer_object::string() + std::string{" is not supported."});
    #endif

    Buffer buffer;
    buffer.bind(Buffer::Target::Uniform, 15);

    MAGNUM_VERIFY_NO_ERROR();

    Buffer::unbind(Buffer::Target::Uniform, 15);

    MAGNUM_VERIFY_NO_ERROR();

    Buffer::bind(Buffer::Target::Uniform, 7, {&buffer, nullptr, &buffer});

    MAGNUM_VERIFY_NO_ERROR();

    Buffer::unbind(Buffer::Target::Uniform, 7, 3);

    MAGNUM_VERIFY_NO_ERROR();
}

void BufferGLTest::bindRange() {
    #ifndef MAGNUM_TARGET_GLES
    if(!Context::current()->isExtensionSupported<Extensions::GL::ARB::uniform_buffer_object>())
        CORRADE_SKIP(Extensions::GL::ARB::uniform_buffer_object::string() + std::string{" is not supported."});
    #endif

    /* Check that we have correct offset alignment */
    CORRADE_INTERNAL_ASSERT(256 % Buffer::uniformOffsetAlignment() == 0);

    Buffer buffer;
    buffer.setData({nullptr, 1024}, BufferUsage::StaticDraw)
         .bind(Buffer::Target::Uniform, 15, 256, 13);

    MAGNUM_VERIFY_NO_ERROR();

    /** @todo C++14: get rid of std::make_tuple */
    Buffer::bind(Buffer::Target::Uniform, 7, {
        std::make_tuple(&buffer, 256, 13), {},
        std::make_tuple(&buffer, 768, 64)});

    MAGNUM_VERIFY_NO_ERROR();
}
#endif

void BufferGLTest::data() {
    Buffer buffer;

    /* Plain array */
    constexpr Int data[] = {2, 7, 5, 13, 25};
    buffer.setData({data, 5}, BufferUsage::StaticDraw);
    MAGNUM_VERIFY_NO_ERROR();
    CORRADE_COMPARE(buffer.size(), 5*4);

    /* STL vector */
    std::vector<Int> data2{2, 7, 5, 13, 25};
    buffer.setData(data2, BufferUsage::StaticDraw);
    MAGNUM_VERIFY_NO_ERROR();
    CORRADE_COMPARE(buffer.size(), 5*4);

    /* STL array */
    std::array<Int, 5> data3{{2, 7, 5, 13, 25}};
    buffer.setData(data3, BufferUsage::StaticDraw);
    MAGNUM_VERIFY_NO_ERROR();
    CORRADE_COMPARE(buffer.size(), 5*4);

    /** @todo How to verify the contents in ES? */
    #ifndef MAGNUM_TARGET_GLES
    const Containers::Array<Int> contents = buffer.data<Int>();
    MAGNUM_VERIFY_NO_ERROR();
    CORRADE_COMPARE(contents.size(), 5);
    CORRADE_COMPARE(contents[0], 2);
    CORRADE_COMPARE(contents[1], 7);
    CORRADE_COMPARE(contents[2], 5);
    CORRADE_COMPARE(contents[3], 13);
    CORRADE_COMPARE(contents[4], 25);
    #endif

    /* Plain array */
    constexpr Int subData[] = {125, 3, 15};
    buffer.setSubData(4, {subData, 3});
    MAGNUM_VERIFY_NO_ERROR();
    CORRADE_COMPARE(buffer.size(), 5*4);

    /* STL vector */
    std::vector<Int> subData2{125, 3, 15};
    buffer.setSubData(4, subData2);
    MAGNUM_VERIFY_NO_ERROR();
    CORRADE_COMPARE(buffer.size(), 5*4);

    /* STL array */
    std::array<Int, 3> subData3{{125, 3, 15}};
    buffer.setSubData(4, subData3);
    MAGNUM_VERIFY_NO_ERROR();
    CORRADE_COMPARE(buffer.size(), 5*4);

    /** @todo How to verify the contents in ES? */
    #ifndef MAGNUM_TARGET_GLES
    const Containers::Array<Int> subContents = buffer.subData<Int>(4, 3);
    MAGNUM_VERIFY_NO_ERROR();
    CORRADE_COMPARE(subContents.size(), 3);
    CORRADE_COMPARE(subContents[0], 125);
    CORRADE_COMPARE(subContents[1], 3);
    CORRADE_COMPARE(subContents[2], 15);
    #endif
}

void BufferGLTest::map() {
    #ifdef MAGNUM_TARGET_GLES
    if(!Context::current()->isExtensionSupported<Extensions::GL::OES::mapbuffer>())
        CORRADE_SKIP(Extensions::GL::OES::mapbuffer::string() + std::string(" is not supported"));
    #endif
    Buffer buffer;

    constexpr char data[] = {2, 7, 5, 13, 25};
    buffer.setData(data, BufferUsage::StaticDraw);

    #ifndef MAGNUM_TARGET_GLES
    char* contents = buffer.map<char>(Buffer::MapAccess::ReadWrite);
    #else
    char* contents = buffer.map<char>(Buffer::MapAccess::WriteOnly);
    #endif
    MAGNUM_VERIFY_NO_ERROR();

    CORRADE_VERIFY(contents);
    #ifndef MAGNUM_TARGET_GLES2
    CORRADE_COMPARE(contents[2], 5);
    #endif
    contents[3] = 107;

    CORRADE_VERIFY(buffer.unmap());
    MAGNUM_VERIFY_NO_ERROR();

    /** @todo How to verify the contents in ES? */
    #ifndef MAGNUM_TARGET_GLES
    Containers::Array<char> changedContents = buffer.data<char>();
    CORRADE_COMPARE(changedContents.size(), 5);
    CORRADE_COMPARE(changedContents[3], 107);
    #endif
}

#ifdef CORRADE_TARGET_NACL
void BufferGLTest::mapSub() {
    if(!Context::current()->isExtensionSupported<Extensions::GL::CHROMIUM::map_sub>())
        CORRADE_SKIP(Extensions::GL::CHROMIUM::map_sub::string() + std::string(" is not supported"));

    Buffer buffer;

    constexpr char data[] = {2, 7, 5, 13, 25};
    buffer.setData(data, BufferUsage::StaticDraw);

    char* contents = buffer.mapSub<char>(1, 4, Buffer::MapAccess::WriteOnly);
    MAGNUM_VERIFY_NO_ERROR();

    CORRADE_VERIFY(contents);
    contents[3] = 107;

    buffer.unmapSub();
    MAGNUM_VERIFY_NO_ERROR();

    /** @todo How to verify the contents in ES? */
}
#endif

void BufferGLTest::mapRange() {
    #ifndef MAGNUM_TARGET_GLES
    if(!Context::current()->isExtensionSupported<Extensions::GL::ARB::map_buffer_range>())
        CORRADE_SKIP(Extensions::GL::ARB::map_buffer_range::string() + std::string(" is not supported"));
    #elif defined(MAGNUM_TARGET_GLES2)
    if(!Context::current()->isExtensionSupported<Extensions::GL::EXT::map_buffer_range>())
        CORRADE_SKIP(Extensions::GL::EXT::map_buffer_range::string() + std::string(" is not supported"));
    #endif

    constexpr char data[] = {2, 7, 5, 13, 25};
    Buffer buffer;
    buffer.setData(data, BufferUsage::StaticDraw);

    char* contents = buffer.map<char>(1, 4, Buffer::MapFlag::Read|Buffer::MapFlag::Write);
    MAGNUM_VERIFY_NO_ERROR();

    CORRADE_VERIFY(contents);
    CORRADE_COMPARE(contents[2], 13);
    contents[3] = 107;

    CORRADE_VERIFY(buffer.unmap());
    MAGNUM_VERIFY_NO_ERROR();

    /** @todo How to verify the contents in ES? */
    #ifndef MAGNUM_TARGET_GLES
    Containers::Array<char> changedContents = buffer.data<char>();
    CORRADE_COMPARE(changedContents.size(), 5);
    CORRADE_COMPARE(changedContents[4], 107);
    #endif
}

void BufferGLTest::mapRangeExplicitFlush() {
    #ifndef MAGNUM_TARGET_GLES
    if(!Context::current()->isExtensionSupported<Extensions::GL::ARB::map_buffer_range>())
        CORRADE_SKIP(Extensions::GL::ARB::map_buffer_range::string() + std::string(" is not supported"));
    #elif defined(MAGNUM_TARGET_GLES2)
    if(!Context::current()->isExtensionSupported<Extensions::GL::EXT::map_buffer_range>())
        CORRADE_SKIP(Extensions::GL::EXT::map_buffer_range::string() + std::string(" is not supported"));
    #endif

    constexpr char data[] = {2, 7, 5, 13, 25};
    Buffer buffer;
    buffer.setData(data, BufferUsage::StaticDraw);

    /* Map, set byte, don't flush and unmap */
    char* contents = buffer.map<char>(1, 4, Buffer::MapFlag::Write|Buffer::MapFlag::FlushExplicit);
    CORRADE_VERIFY(contents);
    contents[2] = 99;
    CORRADE_VERIFY(buffer.unmap());
    MAGNUM_VERIFY_NO_ERROR();

    /* Unflushed range _might_ not be changed, thus nothing to test */

    /* Map, set byte, flush and unmap */
    contents = buffer.map<char>(1, 4, Buffer::MapFlag::Write|Buffer::MapFlag::FlushExplicit);
    CORRADE_VERIFY(contents);
    contents[3] = 107;
    buffer.flushMappedRange(3, 1);
    MAGNUM_VERIFY_NO_ERROR();
    CORRADE_VERIFY(buffer.unmap());
    MAGNUM_VERIFY_NO_ERROR();

    /* Flushed range should be changed */
    /** @todo How to verify the contents in ES? */
    #ifndef MAGNUM_TARGET_GLES
    Containers::Array<char> changedContents = buffer.data<char>();
    CORRADE_COMPARE(changedContents.size(), 5);
    CORRADE_COMPARE(changedContents[4], 107);
    #endif
}

#ifndef MAGNUM_TARGET_GLES2
void BufferGLTest::copy() {
    Buffer buffer1;
    constexpr char data[] = {2, 7, 5, 13, 25};
    buffer1.setData(data, BufferUsage::StaticCopy);

    Buffer buffer2;
    buffer2.setData({nullptr, 5}, BufferUsage::StaticRead);

    Buffer::copy(buffer1, buffer2, 1, 2, 3);
    MAGNUM_VERIFY_NO_ERROR();

    /** @todo How to verify the contents in ES? */
    #ifndef MAGNUM_TARGET_GLES
    const Containers::Array<char> subContents = buffer2.subData<char>(2, 3);
    CORRADE_COMPARE(subContents.size(), 3);
    CORRADE_COMPARE(subContents[0], 7);
    CORRADE_COMPARE(subContents[1], 5);
    CORRADE_COMPARE(subContents[2], 13);
    #endif
}
#endif

void BufferGLTest::invalidate() {
    Buffer buffer;
    constexpr char data[] = {2, 7, 5, 13, 25};
    buffer.setData(data, BufferUsage::StaticDraw);

    /* Just test that no errors are emitted */

    buffer.invalidateSubData(3, 2);
    MAGNUM_VERIFY_NO_ERROR();

    buffer.invalidateData();
    MAGNUM_VERIFY_NO_ERROR();
}

}}
void BufferGLTest::data() {
    Buffer buffer;

    /* Plain array */
    constexpr Int data[] = {2, 7, 5, 13, 25};
    buffer.setData({data, 5}, BufferUsage::StaticDraw);
    MAGNUM_VERIFY_NO_ERROR();
    CORRADE_COMPARE(buffer.size(), 5*4);

    /* STL vector */
    std::vector<Int> data2{2, 7, 5, 13, 25};
    buffer.setData(data2, BufferUsage::StaticDraw);
    MAGNUM_VERIFY_NO_ERROR();
    CORRADE_COMPARE(buffer.size(), 5*4);

    /* STL array */
    std::array<Int, 5> data3{{2, 7, 5, 13, 25}};
    buffer.setData(data3, BufferUsage::StaticDraw);
    MAGNUM_VERIFY_NO_ERROR();
    CORRADE_COMPARE(buffer.size(), 5*4);

    /** @todo How to verify the contents in ES? */
    #ifndef MAGNUM_TARGET_GLES
    const Containers::Array<Int> contents = buffer.data<Int>();
    MAGNUM_VERIFY_NO_ERROR();
    CORRADE_COMPARE(contents.size(), 5);
    CORRADE_COMPARE(contents[0], 2);
    CORRADE_COMPARE(contents[1], 7);
    CORRADE_COMPARE(contents[2], 5);
    CORRADE_COMPARE(contents[3], 13);
    CORRADE_COMPARE(contents[4], 25);
    #endif

    /* Plain array */
    constexpr Int subData[] = {125, 3, 15};
    buffer.setSubData(4, {subData, 3});
    MAGNUM_VERIFY_NO_ERROR();
    CORRADE_COMPARE(buffer.size(), 5*4);

    /* STL vector */
    std::vector<Int> subData2{125, 3, 15};
    buffer.setSubData(4, subData2);
    MAGNUM_VERIFY_NO_ERROR();
    CORRADE_COMPARE(buffer.size(), 5*4);

    /* STL array */
    std::array<Int, 3> subData3{{125, 3, 15}};
    buffer.setSubData(4, subData3);
    MAGNUM_VERIFY_NO_ERROR();
    CORRADE_COMPARE(buffer.size(), 5*4);

    /** @todo How to verify the contents in ES? */
    #ifndef MAGNUM_TARGET_GLES
    const Containers::Array<Int> subContents = buffer.subData<Int>(4, 3);
    MAGNUM_VERIFY_NO_ERROR();
    CORRADE_COMPARE(subContents.size(), 3);
    CORRADE_COMPARE(subContents[0], 125);
    CORRADE_COMPARE(subContents[1], 3);
    CORRADE_COMPARE(subContents[2], 15);
    #endif
}
Containers::Optional<ImageData2D> JpegImporter::doImage2D(UnsignedInt) {
    /* Initialize structures */
    jpeg_decompress_struct file;
    Containers::Array<char> data;

    /* Fugly error handling stuff */
    /** @todo Get rid of this crap */
    struct ErrorManager {
        jpeg_error_mgr jpegErrorManager;
        std::jmp_buf setjmpBuffer;
        char message[JMSG_LENGTH_MAX]{};
    } errorManager;
    file.err = jpeg_std_error(&errorManager.jpegErrorManager);
    errorManager.jpegErrorManager.error_exit = [](j_common_ptr info) {
        auto& errorManager = *reinterpret_cast<ErrorManager*>(info->err);
        info->err->format_message(info, errorManager.message);
        std::longjmp(errorManager.setjmpBuffer, 1);
    };
    if(setjmp(errorManager.setjmpBuffer)) {
        Error() << "Trade::JpegImporter::image2D(): error:" << errorManager.message;
        jpeg_destroy_decompress(&file);
        return Containers::NullOpt;
    }

    /* Open file */
    jpeg_create_decompress(&file);
    jpeg_mem_src(&file, _in.begin(), _in.size());

    /* Read file header, start decompression. On macOS (Travis, with Xcode 7.3)
       the compilation fails because "no known conversion from 'bool' to
       'boolean' for 2nd argument" (boolean is an enum instead of a typedef to
       int there) so doing the conversion implicitly. */
    jpeg_read_header(&file, boolean(true));
    jpeg_start_decompress(&file);

    /* Image size and type */
    const Vector2i size(file.output_width, file.output_height);
    static_assert(BITS_IN_JSAMPLE == 8, "Only 8-bit JPEG is supported");

    /* Image format */
    PixelFormat format;
    switch(file.out_color_space) {
        case JCS_GRAYSCALE:
            CORRADE_INTERNAL_ASSERT(file.out_color_components == 1);
            format = PixelFormat::R8Unorm;
            break;
        case JCS_RGB:
            CORRADE_INTERNAL_ASSERT(file.out_color_components == 3);
            format = PixelFormat::RGB8Unorm;
            break;

        /** @todo RGBA (only in libjpeg-turbo and probably ignored) */

        default:
            Error() << "Trade::JpegImporter::image2D(): unsupported color space" << file.out_color_space;
            return Containers::NullOpt;
    }

    /* Initialize data array, align rows to four bytes */
    const std::size_t stride = ((size.x()*file.out_color_components*BITS_IN_JSAMPLE/8 + 3)/4)*4;
    data = Containers::Array<char>{stride*std::size_t(size.y())};

    /* Read image row by row */
    while(file.output_scanline < file.output_height) {
        JSAMPROW row = reinterpret_cast<JSAMPROW>(data.data() + (size.y() - file.output_scanline - 1)*stride);
        jpeg_read_scanlines(&file, &row, 1);
    }

    /* Cleanup */
    jpeg_finish_decompress(&file);
    jpeg_destroy_decompress(&file);

    /* Always using the default 4-byte alignment */
    return Trade::ImageData2D{format, size, std::move(data)};
}
Exemple #10
0
namespace Corrade { namespace Containers { namespace Test {

struct ArrayTest: TestSuite::Tester {
    explicit ArrayTest();

    void constructEmpty();
    void constructNullptr();
    void constructDefaultInit();
    void constructValueInit();
    void constructNoInit();
    void constructDirectInit();
    void construct();
    void constructFromExisting();
    void constructZeroSize();
    void constructMove();
    void constructFrom();
    void constructFromChar();

    void boolConversion();
    void pointerConversion();

    void emptyCheck();
    void access();
    void rvalueArrayAccess();
    void rangeBasedFor();

    void slice();
    void sliceToStatic();
    void release();

    void customDeleter();
    void customDeleterType();
};

typedef Containers::Array<int> Array;

ArrayTest::ArrayTest() {
    addTests({&ArrayTest::constructEmpty,
              &ArrayTest::constructNullptr,
              &ArrayTest::constructDefaultInit,
              &ArrayTest::constructValueInit,
              &ArrayTest::constructNoInit,
              &ArrayTest::constructDirectInit,
              &ArrayTest::construct,
              &ArrayTest::constructFromExisting,
              &ArrayTest::constructZeroSize,
              &ArrayTest::constructMove,
              &ArrayTest::constructFrom,
              &ArrayTest::constructFromChar,

              &ArrayTest::boolConversion,
              &ArrayTest::pointerConversion,

              &ArrayTest::emptyCheck,
              &ArrayTest::access,
              &ArrayTest::rvalueArrayAccess,
              &ArrayTest::rangeBasedFor,

              &ArrayTest::slice,
              &ArrayTest::sliceToStatic,
              &ArrayTest::release,

              &ArrayTest::customDeleter,
              &ArrayTest::customDeleterType});
}

void ArrayTest::constructEmpty() {
    const Array a;
    CORRADE_VERIFY(a == nullptr);
    CORRADE_COMPARE(a.size(), 0);

    /* Zero-length should not call new */
    const std::size_t size = 0;
    const Array b(size);
    CORRADE_VERIFY(b == nullptr);
    CORRADE_COMPARE(b.size(), 0);
}

void ArrayTest::constructNullptr() {
    const Array c(nullptr);
    CORRADE_VERIFY(c == nullptr);
    CORRADE_COMPARE(c.size(), 0);

    /* Implicit construction from nullptr should be allowed */
    CORRADE_VERIFY((std::is_convertible<std::nullptr_t, Array>::value));
}

void ArrayTest::construct() {
    const Array a(5);
    CORRADE_VERIFY(a != nullptr);
    CORRADE_COMPARE(a.size(), 5);

    /* Implicit construction from std::size_t is not allowed */
    CORRADE_VERIFY(!(std::is_convertible<std::size_t, Array>::value));
}

void ArrayTest::constructFromExisting() {
    int* a = new int[25];
    Array b{a, 25};
    CORRADE_VERIFY(b == a);
    CORRADE_COMPARE(b.size(), 25);
}

void ArrayTest::constructDefaultInit() {
    const Array a{DefaultInit, 5};
    CORRADE_VERIFY(a);
    CORRADE_COMPARE(a.size(), 5);
}

void ArrayTest::constructValueInit() {
    const Array a{ValueInit, 2};
    CORRADE_VERIFY(a);
    CORRADE_COMPARE(a.size(), 2);
    CORRADE_COMPARE(a[0], 0);
    CORRADE_COMPARE(a[1], 0);
}

namespace {
    struct Foo {
        static int constructorCallCount;
        Foo() { ++constructorCallCount; }
    };

    int Foo::constructorCallCount = 0;
}

void ArrayTest::constructNoInit() {
    const Containers::Array<Foo> a{NoInit, 5};
    CORRADE_VERIFY(a);
    CORRADE_COMPARE(a.size(), 5);
    CORRADE_COMPARE(Foo::constructorCallCount, 0);

    const Containers::Array<Foo> b{DefaultInit, 7};
    CORRADE_COMPARE(Foo::constructorCallCount, 7);
}

void ArrayTest::constructDirectInit() {
    const Array a{DirectInit, 2, -37};
    CORRADE_VERIFY(a);
    CORRADE_COMPARE(a.size(), 2);
    CORRADE_COMPARE(a[0], -37);
    CORRADE_COMPARE(a[1], -37);
}

void ArrayTest::constructZeroSize() {
    const Array a(0);

    CORRADE_VERIFY(a == nullptr);
    CORRADE_COMPARE(a.size(), 0);
}

void ArrayTest::constructMove() {
    Array a(5);
    CORRADE_VERIFY(a);
    const int* const ptr = a;

    Array b(std::move(a));
    CORRADE_VERIFY(a == nullptr);
    CORRADE_VERIFY(b == ptr);
    CORRADE_COMPARE(a.size(), 0);
    CORRADE_COMPARE(b.size(), 5);

    Array c;
    c = std::move(b);
    CORRADE_VERIFY(b == nullptr);
    CORRADE_VERIFY(c == ptr);
    CORRADE_COMPARE(b.size(), 0);
    CORRADE_COMPARE(c.size(), 5);
}

void ArrayTest::constructFrom() {
    Array a = Array::from(1, 3, 127, -48);
    CORRADE_VERIFY(a);
    CORRADE_COMPARE(a.size(), 4);
    CORRADE_COMPARE(a[0], 1);
    CORRADE_COMPARE(a[1], 3);
    CORRADE_COMPARE(a[2], 127);
    CORRADE_COMPARE(a[3], -48);

    Array b = Array::from();
    CORRADE_VERIFY(!b);
}

void ArrayTest::constructFromChar() {
    /* Verify that this compiles without "narrowing from int to char" errors */
    const auto a = Containers::Array<char>::from(0x11, 0x22, 0x33);
    CORRADE_VERIFY(a);
    CORRADE_COMPARE(a[1], 0x22);
}

void ArrayTest::boolConversion() {
    CORRADE_VERIFY(Array(2));
    CORRADE_VERIFY(!Array());
    CORRADE_VERIFY(!(std::is_convertible<Array, int>::value));
}

void ArrayTest::pointerConversion() {
    Array a(2);
    int* b = a;
    CORRADE_COMPARE(b, a.begin());

    const Array c(3);
    const int* d = c;
    CORRADE_COMPARE(d, c.begin());

    /* Pointer arithmetic */
    const Array e(3);
    const int* f = e + 2;
    CORRADE_COMPARE(f, &e[2]);

    /* Verify that we can't convert rvalues */
    CORRADE_VERIFY((std::is_convertible<Array&, int*>::value));
    CORRADE_VERIFY((std::is_convertible<const Array&, const int*>::value));
    {
        #ifdef CORRADE_GCC47_COMPATIBILITY
        CORRADE_EXPECT_FAIL("Rvalue references for *this are not supported in GCC < 4.8.1.");
        #endif
        CORRADE_VERIFY(!(std::is_convertible<Array, int*>::value));
        CORRADE_VERIFY(!(std::is_convertible<Array&&, int*>::value));
    }

    /* Deleting const&& overload and leaving only const& one will not, in fact,
       disable conversion of const Array&& to pointer, but rather make the
       conversion ambiguous, which is not what we want, as it breaks e.g.
       rvalueArrayAccess() test. */
    {
        CORRADE_EXPECT_FAIL("I don't know how to properly disable conversion of const Array&& to pointer.");
        CORRADE_VERIFY(!(std::is_convertible<const Array, const int*>::value));
        CORRADE_VERIFY(!(std::is_convertible<const Array&&, const int*>::value));
    }
}

void ArrayTest::emptyCheck() {
    Array a;
    CORRADE_VERIFY(!a);
    CORRADE_VERIFY(a.empty());

    Array b(5);
    CORRADE_VERIFY(b);
    CORRADE_VERIFY(!b.empty());
}

void ArrayTest::access() {
    Array a(7);
    for(std::size_t i = 0; i != 7; ++i)
        a[i] = i;

    CORRADE_COMPARE(a.data(), static_cast<int*>(a));
    CORRADE_COMPARE(*(a.begin()+2), 2);
    CORRADE_COMPARE(a[4], 4);
    CORRADE_COMPARE(a.end()-a.begin(), a.size());

    const auto b = Array::from(7, 3, 5, 4);
    CORRADE_COMPARE(b.data(), static_cast<const int*>(b));
    CORRADE_COMPARE(b[2], 5);
}

void ArrayTest::rvalueArrayAccess() {
    CORRADE_COMPARE(Array::from(1, 2, 3, 4)[2], 3);
}

void ArrayTest::rangeBasedFor() {
    Array a(5);
    for(auto& i: a)
        i = 3;

    CORRADE_COMPARE(a[0], 3);
    CORRADE_COMPARE(a[1], 3);
    CORRADE_COMPARE(a[2], 3);
    CORRADE_COMPARE(a[3], 3);
    CORRADE_COMPARE(a[4], 3);
}

void ArrayTest::slice() {
    Array a = Array::from(1, 2, 3, 4, 5);
    const Array ac = Array::from(1, 2, 3, 4, 5);

    ArrayView<int> b = a.slice(1, 4);
    CORRADE_COMPARE(b.size(), 3);
    CORRADE_COMPARE(b[0], 2);
    CORRADE_COMPARE(b[1], 3);
    CORRADE_COMPARE(b[2], 4);

    ArrayView<const int> bc = ac.slice(1, 4);
    CORRADE_COMPARE(bc.size(), 3);
    CORRADE_COMPARE(bc[0], 2);
    CORRADE_COMPARE(bc[1], 3);
    CORRADE_COMPARE(bc[2], 4);

    ArrayView<int> c = a.prefix(3);
    CORRADE_COMPARE(c.size(), 3);
    CORRADE_COMPARE(c[0], 1);
    CORRADE_COMPARE(c[1], 2);
    CORRADE_COMPARE(c[2], 3);

    ArrayView<const int> cc = ac.prefix(3);
    CORRADE_COMPARE(cc.size(), 3);
    CORRADE_COMPARE(cc[0], 1);
    CORRADE_COMPARE(cc[1], 2);
    CORRADE_COMPARE(cc[2], 3);

    ArrayView<int> d = a.suffix(2);
    CORRADE_COMPARE(d.size(), 3);
    CORRADE_COMPARE(d[0], 3);
    CORRADE_COMPARE(d[1], 4);
    CORRADE_COMPARE(d[2], 5);

    ArrayView<const int> dc = ac.suffix(2);
    CORRADE_COMPARE(dc.size(), 3);
    CORRADE_COMPARE(dc[0], 3);
    CORRADE_COMPARE(dc[1], 4);
    CORRADE_COMPARE(dc[2], 5);
}

void ArrayTest::sliceToStatic() {
    Array a = Array::from(1, 2, 3, 4, 5);
    const Array ac = Array::from(1, 2, 3, 4, 5);

    StaticArrayView<3, int> b = a.slice<3>(1);
    CORRADE_COMPARE(b[0], 2);
    CORRADE_COMPARE(b[1], 3);
    CORRADE_COMPARE(b[2], 4);

    StaticArrayView<3, const int> bc = ac.slice<3>(1);
    CORRADE_COMPARE(bc[0], 2);
    CORRADE_COMPARE(bc[1], 3);
    CORRADE_COMPARE(bc[2], 4);
}

void ArrayTest::release() {
    Array a(5);
    int* const data = a;
    int* const released = a.release();
    delete[] released;

    CORRADE_COMPARE(data, released);
    CORRADE_COMPARE(a.begin(), nullptr);
    CORRADE_COMPARE(a.size(), 0);
}

namespace {
    int CustomDeleterDeletedCount = 0;
}

void ArrayTest::customDeleter() {
    int data[25]{};

    {
        Array a{data, 25, [](int*, std::size_t size) { CustomDeleterDeletedCount = size; }};
        CORRADE_VERIFY(a == data);
        CORRADE_COMPARE(a.size(), 25);
        CORRADE_COMPARE(CustomDeleterDeletedCount, 0);
    }

    CORRADE_COMPARE(CustomDeleterDeletedCount, 25);
}

namespace {
    struct CustomDeleter {
        CustomDeleter(int& deletedCountOutput): deletedCount{deletedCountOutput} {}
        void operator()(int*, std::size_t size) { deletedCount = size; }
        int& deletedCount;
    };
}

void ArrayTest::customDeleterType() {
    int data[25]{};
    int deletedCount = 0;

    {
        Containers::Array<int, CustomDeleter> a{data, 25, CustomDeleter{deletedCount}};
        CORRADE_VERIFY(a == data);
        CORRADE_COMPARE(a.size(), 25);
        CORRADE_COMPARE(deletedCount, 0);
    }

    CORRADE_COMPARE(deletedCount, 25);
}

}}}
Exemple #11
0
namespace Magnum { namespace Test {

class BufferGLTest: public AbstractOpenGLTester {
    public:
        explicit BufferGLTest();

        void construct();
        void data();
        void map();
        #ifdef MAGNUM_TARGET_GLES2
        void mapSub();
        #endif
        void mapRange();
        void mapRangeExplicitFlush();
        #ifndef MAGNUM_TARGET_GLES2
        void copy();
        #endif
        #ifndef MAGNUM_TARGET_GLES2
        void invalidate();
        #endif
};

BufferGLTest::BufferGLTest() {
    addTests({&BufferGLTest::construct,
              &BufferGLTest::data,
              &BufferGLTest::map,
              #ifdef MAGNUM_TARGET_GLES2
              &BufferGLTest::mapSub,
              #endif
              &BufferGLTest::mapRange,
              &BufferGLTest::mapRangeExplicitFlush,
              #ifndef MAGNUM_TARGET_GLES2
              &BufferGLTest::copy,
              #endif
              #ifndef MAGNUM_TARGET_GLES
              &BufferGLTest::invalidate
              #endif
              });
}

void BufferGLTest::construct() {
    Buffer buffer;
    MAGNUM_VERIFY_NO_ERROR();

    CORRADE_COMPARE(buffer.targetHint(), Buffer::Target::Array);

    CORRADE_COMPARE(buffer.size(), 0);
    MAGNUM_VERIFY_NO_ERROR();
}

void BufferGLTest::data() {
    Buffer buffer;

    /* Plain array */
    constexpr Int data[] = {2, 7, 5, 13, 25};
    buffer.setData({data, 5}, Buffer::Usage::StaticDraw);
    MAGNUM_VERIFY_NO_ERROR();
    CORRADE_COMPARE(buffer.size(), 5*4);

    /* STL vector */
    std::vector<Int> data2{2, 7, 5, 13, 25};
    buffer.setData(data2, Buffer::Usage::StaticDraw);
    MAGNUM_VERIFY_NO_ERROR();
    CORRADE_COMPARE(buffer.size(), 5*4);

    /* STL array */
    std::array<Int, 5> data3{{2, 7, 5, 13, 25}};
    buffer.setData(data3, Buffer::Usage::StaticDraw);
    MAGNUM_VERIFY_NO_ERROR();
    CORRADE_COMPARE(buffer.size(), 5*4);

    /** @todo How to verify the contents in ES? */
    #ifndef MAGNUM_TARGET_GLES
    const Containers::Array<Int> contents = buffer.data<Int>();
    MAGNUM_VERIFY_NO_ERROR();
    CORRADE_COMPARE(contents.size(), 5);
    CORRADE_COMPARE(contents[0], 2);
    CORRADE_COMPARE(contents[1], 7);
    CORRADE_COMPARE(contents[2], 5);
    CORRADE_COMPARE(contents[3], 13);
    CORRADE_COMPARE(contents[4], 25);
    #endif

    /* Plain array */
    constexpr Int subData[] = {125, 3, 15};
    buffer.setSubData(4, {subData, 3});
    MAGNUM_VERIFY_NO_ERROR();
    CORRADE_COMPARE(buffer.size(), 5*4);

    /* STL vector */
    std::vector<Int> subData2{125, 3, 15};
    buffer.setSubData(4, subData2);
    MAGNUM_VERIFY_NO_ERROR();
    CORRADE_COMPARE(buffer.size(), 5*4);

    /* STL array */
    std::array<Int, 3> subData3{{125, 3, 15}};
    buffer.setSubData(4, subData3);
    MAGNUM_VERIFY_NO_ERROR();
    CORRADE_COMPARE(buffer.size(), 5*4);

    /** @todo How to verify the contents in ES? */
    #ifndef MAGNUM_TARGET_GLES
    const Containers::Array<Int> subContents = buffer.subData<Int>(4, 3);
    MAGNUM_VERIFY_NO_ERROR();
    CORRADE_COMPARE(subContents.size(), 3);
    CORRADE_COMPARE(subContents[0], 125);
    CORRADE_COMPARE(subContents[1], 3);
    CORRADE_COMPARE(subContents[2], 15);
    #endif
}

#ifndef MAGNUM_TARGET_GLES3
void BufferGLTest::map() {
    #ifdef MAGNUM_TARGET_GLES2
    if(!Context::current()->isExtensionSupported<Extensions::GL::OES::mapbuffer>())
        CORRADE_SKIP(Extensions::GL::OES::mapbuffer::string() + std::string(" is not supported"));
    #endif
    Buffer buffer;

    constexpr char data[] = {2, 7, 5, 13, 25};
    buffer.setData(data, Buffer::Usage::StaticDraw);

    #ifndef MAGNUM_TARGET_GLES2
    char* contents = reinterpret_cast<char*>(buffer.map(Buffer::MapAccess::ReadWrite));
    #else
    char* contents = reinterpret_cast<char*>(buffer.map(Buffer::MapAccess::WriteOnly));
    #endif
    MAGNUM_VERIFY_NO_ERROR();

    CORRADE_VERIFY(contents);
    #ifndef MAGNUM_TARGET_GLES2
    CORRADE_COMPARE(contents[2], 5);
    #endif
    contents[3] = 107;

    CORRADE_VERIFY(buffer.unmap());
    MAGNUM_VERIFY_NO_ERROR();

    /** @todo How to verify the contents in ES? */
    #ifndef MAGNUM_TARGET_GLES
    Containers::Array<char> changedContents = buffer.data<char>();
    CORRADE_COMPARE(changedContents.size(), 5);
    CORRADE_COMPARE(changedContents[3], 107);
    #endif
}
#endif

#ifdef MAGNUM_TARGET_GLES2
void BufferGLTest::mapSub() {
    if(!Context::current()->isExtensionSupported<Extensions::GL::CHROMIUM::map_sub>())
        CORRADE_SKIP(Extensions::GL::CHROMIUM::map_sub::string() + std::string(" is not supported"));

    Buffer buffer;

    constexpr char data[] = {2, 7, 5, 13, 25};
    buffer.setData(data, Buffer::Usage::StaticDraw);

    char* contents = reinterpret_cast<char*>(buffer.mapSub(1, 4, Buffer::MapAccess::WriteOnly));
    MAGNUM_VERIFY_NO_ERROR();

    CORRADE_VERIFY(contents);
    contents[3] = 107;

    buffer.unmapSub();
    MAGNUM_VERIFY_NO_ERROR();

    /** @todo How to verify the contents in ES? */
}
#endif

void BufferGLTest::mapRange() {
    #ifndef MAGNUM_TARGET_GLES2
    if(!Context::current()->isExtensionSupported<Extensions::GL::ARB::map_buffer_range>())
        CORRADE_SKIP(Extensions::GL::ARB::map_buffer_range::string() + std::string(" is not supported"));
    #else
    if(!Context::current()->isExtensionSupported<Extensions::GL::EXT::map_buffer_range>())
        CORRADE_SKIP(Extensions::GL::EXT::map_buffer_range::string() + std::string(" is not supported"));
    #endif

    constexpr char data[] = {2, 7, 5, 13, 25};
    Buffer buffer;
    buffer.setData(data, Buffer::Usage::StaticDraw);

    char* contents = reinterpret_cast<char*>(buffer.map(1, 4, Buffer::MapFlag::Read|Buffer::MapFlag::Write));
    MAGNUM_VERIFY_NO_ERROR();

    CORRADE_VERIFY(contents);
    CORRADE_COMPARE(contents[2], 13);
    contents[3] = 107;

    CORRADE_VERIFY(buffer.unmap());
    MAGNUM_VERIFY_NO_ERROR();

    /** @todo How to verify the contents in ES? */
    #ifndef MAGNUM_TARGET_GLES
    Containers::Array<char> changedContents = buffer.data<char>();
    CORRADE_COMPARE(changedContents.size(), 5);
    CORRADE_COMPARE(changedContents[4], 107);
    #endif
}

void BufferGLTest::mapRangeExplicitFlush() {
    #ifndef MAGNUM_TARGET_GLES2
    if(!Context::current()->isExtensionSupported<Extensions::GL::ARB::map_buffer_range>())
        CORRADE_SKIP(Extensions::GL::ARB::map_buffer_range::string() + std::string(" is not supported"));
    #else
    if(!Context::current()->isExtensionSupported<Extensions::GL::EXT::map_buffer_range>())
        CORRADE_SKIP(Extensions::GL::EXT::map_buffer_range::string() + std::string(" is not supported"));
    #endif

    constexpr char data[] = {2, 7, 5, 13, 25};
    Buffer buffer;
    buffer.setData(data, Buffer::Usage::StaticDraw);

    /* Map, set byte, don't flush and unmap */
    char* contents = reinterpret_cast<char*>(buffer.map(1, 4, Buffer::MapFlag::Write|Buffer::MapFlag::FlushExplicit));
    CORRADE_VERIFY(contents);
    contents[2] = 99;
    CORRADE_VERIFY(buffer.unmap());
    MAGNUM_VERIFY_NO_ERROR();

    /* Unflushed range _might_ not be changed, thus nothing to test */

    /* Map, set byte, flush and unmap */
    contents = reinterpret_cast<char*>(buffer.map(1, 4, Buffer::MapFlag::Write|Buffer::MapFlag::FlushExplicit));
    CORRADE_VERIFY(contents);
    contents[3] = 107;
    buffer.flushMappedRange(3, 1);
    MAGNUM_VERIFY_NO_ERROR();
    CORRADE_VERIFY(buffer.unmap());
    MAGNUM_VERIFY_NO_ERROR();

    /* Flushed range should be changed */
    /** @todo How to verify the contents in ES? */
    #ifndef MAGNUM_TARGET_GLES
    Containers::Array<char> changedContents = buffer.data<char>();
    CORRADE_COMPARE(changedContents.size(), 5);
    CORRADE_COMPARE(changedContents[4], 107);
    #endif
}

#ifndef MAGNUM_TARGET_GLES2
void BufferGLTest::copy() {
    Buffer buffer1;
    constexpr char data[] = {2, 7, 5, 13, 25};
    buffer1.setData(data, Buffer::Usage::StaticDraw);

    Buffer buffer2;
    buffer2.setData({nullptr, 5}, Buffer::Usage::StaticDraw);

    Buffer::copy(buffer1, buffer2, 1, 2, 3);
    MAGNUM_VERIFY_NO_ERROR();

    /** @todo How to verify the contents in ES? */
    #ifndef MAGNUM_TARGET_GLES
    const Containers::Array<char> subContents = buffer2.subData<char>(2, 3);
    CORRADE_COMPARE(subContents.size(), 3);
    CORRADE_COMPARE(subContents[0], 7);
    CORRADE_COMPARE(subContents[1], 5);
    CORRADE_COMPARE(subContents[2], 13);
    #endif
}
#endif

#ifndef MAGNUM_TARGET_GLES
void BufferGLTest::invalidate() {
    if(!Context::current()->isExtensionSupported<Extensions::GL::ARB::invalidate_subdata>())
        CORRADE_SKIP(Extensions::GL::ARB::invalidate_subdata::string() + std::string(" is not supported"));

    Buffer buffer;
    constexpr char data[] = {2, 7, 5, 13, 25};
    buffer.setData(data, Buffer::Usage::StaticDraw);

    /* Just test that no errors are emitted */

    buffer.invalidateSubData(3, 2);
    MAGNUM_VERIFY_NO_ERROR();

    buffer.invalidateData();
    MAGNUM_VERIFY_NO_ERROR();
}
#endif

}}