Zepha/src/game/atlas/TextureBuilder.cpp

147 lines
5.1 KiB
C++

#include "TextureAtlas.h"
#include "TextureBuilder.h"
#include "game/atlas/asset/AtlasTexture.h"
vec<u8>& TextureBuilder::Texture::getBytes() {
if (std::holds_alternative<ByteData>(data)) return get<ByteData>(data).data;
const CropData& cropData = get<CropData>(data);
const vec<u8> sourceBytes = cropData.source->getBytes();
const u16 sourceWidth = cropData.source->getSize().x;
vec<u8> newBytes(size.x * size.y * 4);
for (u32 i = 0; i < size.x * size.y * 4; i++) {
newBytes[i] = sourceBytes[(
(cropData.offset.x + ((i / 4) % size.x)) +
(cropData.offset.y + (i / 4 / size.x)) * sourceWidth) * 4 + i % 4];
}
data = ByteData(newBytes);
return get<ByteData>(data).data;
}
void TextureBuilder::Texture::alpha(f32 factor) {
let& data = getBytes();
for (u32 i = 0; i < size.x * size.y; i++)
data[i * 4 + 3] = (data[i * 4 + 3] / 255.f * factor) * 255;
}
void TextureBuilder::Texture::crop(u16vec2 pos, u16vec2 size) {
if (std::holds_alternative<CropData>(data)) {
CropData& crop = get<CropData>(data);
crop.offset += pos;
this->size = size;
}
else {
const let& data = getBytes();
vec<u8> newData(size.x * size.y * 4);
for (u32 i = 0; i < size.x * size.y * 4; i++) newData[i] =
data[((pos.x + i / 4 % size.x) + (pos.y + i / 4 / size.x) * this->size.x) * 4 + i % 4];
this->data = ByteData(newData);
this->size = size;
}
}
void TextureBuilder::Texture::multiply(vec4 scalar) {
let& data = getBytes();
for (u32 i = 0; i < size.x * size.y * 4; i++) data[i] *= scalar[i % 4];
}
void TextureBuilder::Texture::multiply(Texture& texture) {
let& data = getBytes();
let& mul = texture.getBytes();
for (u32 i = 0; i < size.x * size.y * 4; i++) data[i] *= mul[i];
}
void TextureBuilder::Texture::stack(Texture& texture, Texture* mask) {
if (!mask) mask = &texture;
let& data = getBytes();
const let& otherData = texture.getBytes();
const let& maskData = mask->getBytes();
for (u32 i = 0; i < size.x * size.y; i++) {
const f32 factor = maskData[i * 4 + 3] / 255.f;
data[i * 4] = data[i * 4] * (1 - factor) + otherData[i * 4] * factor;
data[i * 4 + 1] = data[i * 4 + 1] * (1 - factor) + otherData[i * 4 + 1] * factor;
data[i * 4 + 2] = data[i * 4 + 2] * (1 - factor) + otherData[i * 4 + 2] * factor;
data[i * 4 + 3] = std::min(static_cast<u16>(data[i * 4 + 3]) + otherData[i * 4 + 3], 255);
}
}
TextureBuilder::Data TextureBuilder::Data::literal(Context& ctx, string str) {
let tex = ctx.atlas.get(str);
return Data({}, tex->getSize(), tex);
}
TextureBuilder::Data TextureBuilder::Data::canvas(u16 w, optional<variant<u16, string>> h, optional<string> fill) {
u16vec2 size = { w, h && std::holds_alternative<u16>(*h) ? std::get<u16>(*h) : w };
vec4 color = Util::hexToColorVec(fill ? *fill : h &&
std::holds_alternative<string>(*h) ? std::get<string>(*h) : "#0000");
return Data(size, color);
}
TextureBuilder::Data TextureBuilder::Data::alpha(f32 factor, Data data) {
data.texture->alpha(factor);
return data;
}
TextureBuilder::Data TextureBuilder::Data::crop(u16 x, u16 y, u16 w, variant<u16, Data> hV, optional<Data> texO) {
u16 h = std::holds_alternative<u16>(hV) ? std::get<u16>(hV) : w;
Data data = std::holds_alternative<Data>(hV) ? std::get<Data>(hV) : *texO;
if (w + x > data.texture->size.x || h + y > data.texture->size.y)
throw std::runtime_error("Invalid crop region.");
data.texture->crop({ x, y }, { w, h });
return data;
}
TextureBuilder::Data TextureBuilder::Data::multiply(variant<string, Data> multiple, Data data) {
if (std::holds_alternative<Data>(multiple)) data.texture->multiply(*get<Data>(multiple).texture.get());
else data.texture->multiply(Util::hexToColorVec(get<string>(multiple)));
return data;
}
TextureBuilder::Data TextureBuilder::Data::stack(Data b, Data s1, optional<Data> s2, optional<Data> s3,
optional<Data> s4, optional<Data> s5, optional<Data> s6, optional<Data> s7, optional<Data> s8) {
vec<Data*> stack;
stack.reserve(8);
stack.emplace_back(&s1);
if (s2) stack.emplace_back(&*s2);
if (s3) stack.emplace_back(&*s3);
if (s4) stack.emplace_back(&*s4);
if (s5) stack.emplace_back(&*s5);
if (s6) stack.emplace_back(&*s6);
if (s7) stack.emplace_back(&*s7);
if (s8) stack.emplace_back(&*s8);
for (const Data* s : stack) {
b.texture->stack(*s->texture.get());
if (s->tintMask) {
if (!b.tintMask) b.tintMask = {{ s->tintMask->first, std::make_unique<Texture>(b.texture->size, vec4 {}) }};
b.tintMask->second->stack(*s->tintMask->second.get(), s->texture.get());
}
}
return b;
}
TextureBuilder::Data TextureBuilder::Data::tint(u32 tint, Data tex, optional<Data> mask) {
if (mask) tex.tintMask = { tint, std::move(mask->texture) };
else tex.tintMask = { tint, make_unique<Texture>(tex.texture->size, vec4(1)) };
return tex;
}
TextureBuilder::TextureBuilder(TextureAtlas& atlas): ctx({ atlas }) {
parser.addFn("_", &Data::literal);
parser.addFn("", &Data::stack);
parser.addFn("canvas", &Data::canvas);
parser.addFn("alpha", &Data::alpha);
parser.addFn("crop", &Data::crop);
parser.addFn("multiply", &Data::multiply);
parser.addFn("tint", &Data::tint);
}
TextureBuilder::Data TextureBuilder::build(const string& str) const {
return parser.parse(str, ctx);
}