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)}; }
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); }