Add texture creation from bytes

This commit is contained in:
Andrew Cassidy 2021-03-29 19:38:37 -07:00
parent 67751e87c7
commit c70907ced2
3 changed files with 44 additions and 47 deletions

View File

@ -20,10 +20,13 @@
#pragma once
#include <array>
#include <climits>
#include <cstdint>
#include <cstring>
#include <memory>
#include <stdexcept>
#include <tuple>
#include <type_traits>
#include "Block.h"
#include "Color.h"
@ -44,12 +47,14 @@ class Texture {
*/
virtual size_t Size() const noexcept = 0;
virtual const uint8_t *Data() const noexcept = 0;
virtual uint8_t *Data() noexcept = 0;
protected:
Texture(int width, int height) : _width(width), _height(height) {
if (width <= 0) throw std::invalid_argument("Texture width must be greater than 0");
if (height <= 0) throw std::invalid_argument("Texture height must be greater than 0");
}
virtual uint8_t *DataMutable() = 0;
int _width;
int _height;
@ -142,8 +147,10 @@ class RawTexture : public Texture {
}
};
virtual const uint8_t *Data() const noexcept override { return reinterpret_cast<const uint8_t *>(_pixels); }
virtual uint8_t *Data() noexcept override { return reinterpret_cast<uint8_t *>(_pixels); }
protected:
virtual uint8_t *DataMutable() override { return reinterpret_cast<uint8_t *>(_pixels); }
Color *_pixels;
};
@ -211,8 +218,10 @@ template <typename B> class BlockTexture : public Texture {
virtual size_t Size() const noexcept override { return (size_t)(BlocksX() * BlocksY()) * sizeof(B); }
virtual const uint8_t *Data() const noexcept override { return reinterpret_cast<const uint8_t *>(_blocks); }
virtual uint8_t *Data() noexcept override { return reinterpret_cast<uint8_t *>(_blocks); }
protected:
virtual uint8_t *DataMutable() override { return reinterpret_cast<uint8_t *>(_blocks); }
B *_blocks;
};

View File

@ -83,48 +83,6 @@ namespace quicktex::bindings {
void InitS3TC(py::module_ &m);
/*py::bytes EncodeImage(const BlockEncoder &self, py::bytes decoded, unsigned image_width, unsigned image_height) {
if (image_width % self.BlockWidth() != 0) throw std::invalid_argument("Width is not an even multiple of block_width");
if (image_height % self.BlockHeight() != 0) throw std::invalid_argument("Height is not an even multiple of block_height");
if (image_width == 0 || image_height == 0) throw std::invalid_argument("Image has zero size");
size_t size = image_width * image_height;
size_t block_size = (size / (self.BlockHeight() * self.BlockWidth())) * self.BlockSize();
size_t color_size = size * sizeof(Color);
std::string encoded_str = std::string(block_size, 0);
std::string decoded_str = (std::string)decoded; // decoded data is copied here, unfortunately
if (decoded_str.size() != color_size) throw std::invalid_argument("Incompatible data: image width and height do not match the size of the decoded image");
self.EncodeImage(reinterpret_cast<uint8_t *>(encoded_str.data()), reinterpret_cast<Color *>(decoded_str.data()), image_width, image_height);
auto bytes = py::bytes(encoded_str); // encoded data is copied here, unfortunately
return bytes;
}
py::bytes DecodeImage(const BlockDecoder &self, py::bytes encoded, unsigned image_width, unsigned image_height) {
if (image_width % self.WidthInBlocks() != 0) throw std::invalid_argument("Width is not an even multiple of block_width");
if (image_height % self.HeightInBlocks() != 0) throw std::invalid_argument("Height is not an even multiple of block_height");
if (image_width == 0 || image_height == 0) throw std::invalid_argument("Image has zero size");
size_t size = image_width * image_height;
size_t block_size = (size / (self.HeightInBlocks() * self.WidthInBlocks())) * self.BlockSize();
size_t color_size = size * sizeof(Color);
std::string encoded_str = (std::string)encoded; // encoded data is copied here, unfortunately
std::string decoded_str = std::string(color_size, 0);
if (encoded_str.size() != block_size) throw std::invalid_argument("Incompatible data: image width and height do not match the size of the encoded image");
self.DecodeImage(reinterpret_cast<uint8_t *>(encoded_str.data()), reinterpret_cast<Color *>(decoded_str.data()), image_width, image_height);
auto bytes = py::bytes(decoded_str); // decoded data is copied here, unfortunately
return bytes;
}*/
PYBIND11_MODULE(_quicktex, m) {
m.doc() = "More Stuff";
@ -137,13 +95,18 @@ PYBIND11_MODULE(_quicktex, m) {
texture.def_property_readonly("width", &Texture::Width);
texture.def_property_readonly("height", &Texture::Height);
// texture.def_buffer([](Texture &t) { return py::buffer_info(t.Data(), t.Size()); });
texture.def("to_bytes", [](const Texture &t) { return py::bytes(reinterpret_cast<const char *>(t.Data()), t.Size()); });
py::class_<RawTexture> raw_texture(m, "RawTexture", texture);
raw_texture.def(py::init<int, int>(), "width"_a, "height"_a);
raw_texture.def("get_pixel", &RawTexture::GetPixel);
raw_texture.def("set_pixel", &RawTexture::SetPixel);
InitS3TC(m);
raw_texture.def_static("from_bytes", &BufferToTexture<RawTexture>, "data"_a, "width"_a, "height"_a);
// InitS3TC(m);
}
} // namespace quicktex::bindings

View File

@ -21,6 +21,8 @@
#include <pybind11/pybind11.h>
#include <cstdint>
#include <cstring>
#include <memory>
#include <stdexcept>
@ -34,9 +36,30 @@ template <> struct type_caster<quicktex::Color>;
namespace py = pybind11;
namespace quicktex::bindings {
using namespace pybind11::literals;
template <typename T> T BufferToTexture(py::buffer buf, int width, int height) {
static_assert(std::is_base_of<Texture, T>::value);
static_assert(std::is_constructible<T, int, int>::value);
auto info = buf.request(false);
auto output = T(width, height);//std::make_shared<T>(width, height);
auto dst_size = output.Size();
if (info.format != py::format_descriptor<uint8_t>::format()) throw std::runtime_error("Incompatible format in python buffer: expected a byte array.");
if (info.size < (ssize_t)dst_size) std::runtime_error("Incompatible format in python buffer: Input data is smaller than texture size.");
if (info.ndim == 1) {
if (info.shape[0] < (ssize_t)dst_size) throw std::runtime_error("Incompatible format in python buffer: 1-D buffer has incorrect length.");
if (info.strides[0] != 1) throw std::runtime_error("Incompatible format in python buffer: 1-D buffer is not contiguous.");
} else {
throw std::runtime_error("Incompatible format in python buffer: Incorrect number of dimensions.");
}
std::memcpy(output.Data(), info.ptr, dst_size);
return output;
}
template <typename B> py::class_<BlockTexture<B>> BindBlockTexture(py::module_& m, const char* name) {
using BTex = BlockTexture<B>;
@ -48,6 +71,8 @@ template <typename B> py::class_<BlockTexture<B>> BindBlockTexture(py::module_&
block_texture.def("get_block", &BTex::GetBlock, "x"_a, "y"_a);
block_texture.def("set_block", &BTex::SetBlock, "x"_a, "y"_a, "block"_a);
block_texture.def_static("from_bytes", &BufferToTexture<BTex>, "data"_a, "width"_a, "height"_a);
block_texture.def_property_readonly("blocks_x", &BTex::BlocksX);
block_texture.def_property_readonly("blocks_y", &BTex::BlocksY);
}