std::optional<ImageData2D> StbImageImporter::doImage2D(UnsignedInt) {
    Vector2i size;
    Int components;

    stbi_uc* const data = stbi_load_from_memory(_in, _in.size(), &size.x(), &size.y(), &components, 0);
    if(!data) {
        Error() << "Trade::StbImageImporter::image2D(): cannot open the image:" << stbi_failure_reason();
        return std::nullopt;
    }

    ColorFormat format;
    switch(components) {
        case 1:
            #ifndef MAGNUM_TARGET_GLES2
            format = ColorFormat::Red;
            #else
            format = Context::current() && Context::current()->isExtensionSupported<Extensions::GL::EXT::texture_rg>() ?
                ColorFormat::Red : ColorFormat::Luminance;
            #endif
            break;
        case 2:
            #ifndef MAGNUM_TARGET_GLES2
            format = ColorFormat::RG;
            #else
            format = Context::current() && Context::current()->isExtensionSupported<Extensions::GL::EXT::texture_rg>() ?
                ColorFormat::RG : ColorFormat::LuminanceAlpha;
            #endif
            break;

        case 3: format = ColorFormat::RGB; break;
        case 4: format = ColorFormat::RGBA; break;
        default: CORRADE_ASSERT_UNREACHABLE();
    }

    /* Copy the data with reversed row order to a new[]-allocated array so we
       can delete[] it later (the original data with must be deleted with free()) */
    unsigned char* const imageData = new unsigned char[size.product()*components];
    for(Int y = 0; y != size.y(); ++y) {
        const Int stride = size.x()*components;
        std::copy(data + y*stride, data + (y + 1)*stride, imageData + (size.y() - y - 1)*stride);
    }
    stbi_image_free(data);

    return Trade::ImageData2D(format, ColorType::UnsignedByte, size, imageData);
}
Containers::Optional<ImageData2D> DevIlImageImporter::doImage2D(UnsignedInt) {
    ILuint imgID = 0;
    ilGenImages(1, &imgID);
    ilBindImage(imgID);

    /* So we can use the shorter if(!ilFoo()) */
    static_assert(!IL_FALSE, "IL_FALSE doesn't have a zero value");

    if(!ilLoadL(IL_TYPE_UNKNOWN, _in, _in.size())) {
        /* iluGetString() returns empty string for 0x512, which is even more
           useless than just returning the error ID */
        Error() << "Trade::DevIlImageImporter::image2D(): cannot open the image:" << ilGetError();
        return Containers::NullOpt;
    }

    const Vector2i size{ilGetInteger(IL_IMAGE_WIDTH), ilGetInteger(IL_IMAGE_HEIGHT)};

    Int components;
    bool rgbaNeeded = false;
    PixelFormat format;
    switch(ilGetInteger(IL_IMAGE_FORMAT)) {
        /* Grayscale */
        case IL_LUMINANCE:
            format = PixelFormat::R8Unorm;
            components = 1;
            break;

        /* Grayscale + alpha */
        case IL_LUMINANCE_ALPHA:
            format = PixelFormat::RG8Unorm;
            components = 2;
            break;

        /* BGR */
        case IL_BGR:
            rgbaNeeded = true;
            format = PixelFormat::RGB8Unorm;
            components = 3;
            break;

        /* BGRA */
        case IL_BGRA:
            rgbaNeeded = true;
            format = PixelFormat::RGBA8Unorm;
            components = 4;
            break;

        /* RGB */
        case IL_RGB:
            format = PixelFormat::RGB8Unorm;
            components = 3;
            break;

        /* RGBA */
        case IL_RGBA:
            format = PixelFormat::RGBA8Unorm;
            components = 4;
            break;

        /* No idea, convert to RGBA */
        default:
            format = PixelFormat::RGBA8Unorm;
            components = 4;
            rgbaNeeded = true;
    }

    /* If the format isn't one we recognize, convert to RGB(A) */
    if(rgbaNeeded && !ilConvertImage(components == 3 ? IL_RGB : IL_RGBA, IL_UNSIGNED_BYTE)) {
        /* iluGetString() returns empty string for 0x512, which is even
           more useless than just returning the error ID */
        Error() << "Trade::DevIlImageImporter::image2D(): cannot convert image:" << ilGetError();
        return Containers::NullOpt;
    }

    /* Flip the image to match OpenGL's conventions */
    /** @todo use our own routine to avoid linking to ILU */
    ILinfo ImageInfo;
    iluGetImageInfo(&ImageInfo);
    if(ImageInfo.Origin == IL_ORIGIN_UPPER_LEFT)
        iluFlipImage();

    /* Copy the data into array that is owned by us and not by IL */
    Containers::Array<char> imageData{std::size_t(size.product()*components)};
    std::copy_n(reinterpret_cast<char*>(ilGetData()), imageData.size(), imageData.begin());

    /* Release the texture back to DevIL */
    ilDeleteImages(1, &imgID);

    /* Adjust pixel storage if row size is not four byte aligned */
    PixelStorage storage;
    if((size.x()*components)%4 != 0)
        storage.setAlignment(1);

    return Trade::ImageData2D{storage, format, size, std::move(imageData)};
}
Beispiel #3
0
std::optional<ImageData2D> TgaImporter::doImage2D(UnsignedInt) {
    /* Check if the file is long enough */
    if(_in.size() < std::streamoff(sizeof(TgaHeader))) {
        Error() << "Trade::TgaImporter::image2D(): the file is too short:" << _in.size() << "bytes";
        return std::nullopt;
    }

    const TgaHeader& header = *reinterpret_cast<const TgaHeader*>(_in.data());

    /* Size in machine endian */
    const Vector2i size{Utility::Endianness::littleEndian(header.width),
                        Utility::Endianness::littleEndian(header.height)};

    /* Image format */
    PixelFormat format;
    if(header.colorMapType != 0) {
        Error() << "Trade::TgaImporter::image2D(): paletted files are not supported";
        return std::nullopt;
    }

    /* Color */
    if(header.imageType == 2) {
        switch(header.bpp) {
            case 24:
                format = PixelFormat::RGB;
                break;
            case 32:
                format = PixelFormat::RGBA;
                break;
            default:
                Error() << "Trade::TgaImporter::image2D(): unsupported color bits-per-pixel:" << header.bpp;
                return std::nullopt;
        }

    /* Grayscale */
    } else if(header.imageType == 3) {
        #if defined(MAGNUM_TARGET_GLES2) && !defined(MAGNUM_TARGET_WEBGL)
        format = Context::hasCurrent() && Context::current().isExtensionSupported<Extensions::GL::EXT::texture_rg>() ?
            PixelFormat::Red : PixelFormat::Luminance;
        #elif !(defined(MAGNUM_TARGET_WEBGL) && defined(MAGNUM_TARGET_GLES2))
        format = PixelFormat::Red;
        #else
        format = PixelFormat::Luminance;
        #endif
        if(header.bpp != 8) {
            Error() << "Trade::TgaImporter::image2D(): unsupported grayscale bits-per-pixel:" << header.bpp;
            return std::nullopt;
        }

    /* Compressed files */
    } else {
        Error() << "Trade::TgaImporter::image2D(): unsupported (compressed?) image type:" << header.imageType;
        return std::nullopt;
    }

    Containers::Array<char> data{std::size_t(size.product())*header.bpp/8};
    std::copy_n(_in + sizeof(TgaHeader), data.size(), data.begin());

    /* Adjust pixel storage if row size is not four byte aligned */
    PixelStorage storage;
    if((size.x()*header.bpp/8)%4 != 0)
        storage.setAlignment(1);

    if(format == PixelFormat::RGB) {
        auto pixels = reinterpret_cast<Math::Vector3<UnsignedByte>*>(data.data());
        std::transform(pixels, pixels + size.product(), pixels,
            [](Math::Vector3<UnsignedByte> pixel) { return Math::swizzle<'b', 'g', 'r'>(pixel); });
    } else if(format == PixelFormat::RGBA) {
        auto pixels = reinterpret_cast<Math::Vector4<UnsignedByte>*>(data.data());
        std::transform(pixels, pixels + size.product(), pixels,
            [](Math::Vector4<UnsignedByte> pixel) { return Math::swizzle<'b', 'g', 'r', 'a'>(pixel); });
    }

    return ImageData2D{storage, format, PixelType::UnsignedByte, size, std::move(data)};
}
std::optional<ImageData2D> JpegImporter::doImage2D(UnsignedInt) {
    /* Initialize structures */
    jpeg_decompress_struct file;
    JSAMPARRAY rows = nullptr;
    unsigned char* data = nullptr;

    /* 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);
        delete[] rows;
        delete[] data;
        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 const ImageType type = ImageType::UnsignedByte;

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

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

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

    /* Initialize data array */
    data = new unsigned char[size.product()*file.out_color_components*BITS_IN_JSAMPLE/8];

    /* Read image row by row */
    rows = new JSAMPROW[size.y()];
    const Int stride = size.x()*file.out_color_components*BITS_IN_JSAMPLE/8;
    for(Int i = 0; i != size.y(); ++i)
        rows[i] = 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);
    delete[] rows;
    rows = nullptr;

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

    return Trade::ImageData2D(format, type, size, data);
}