From 43821b0cffc5aa419c0218992f06f8962ae54a13 Mon Sep 17 00:00:00 2001 From: Frederico Linhares Date: Wed, 8 May 2024 17:56:29 -0300 Subject: refa Rename graphical engine to BluCat --- src/blucat/character.cpp | 274 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 274 insertions(+) create mode 100644 src/blucat/character.cpp (limited to 'src/blucat/character.cpp') diff --git a/src/blucat/character.cpp b/src/blucat/character.cpp new file mode 100644 index 0000000..c13ffef --- /dev/null +++ b/src/blucat/character.cpp @@ -0,0 +1,274 @@ +/* + * Copyright 2022-2024 Frederico de Oliveira Linhares + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "character.hpp" + +#include "../command.hpp" +#include "../core.hpp" +#include "font.hpp" +#include "image.hpp" +#include "source_buffer.hpp" + +namespace +{ + +struct CharacterBuilder +{ + BluCat::Character *character; + FT_Face face; + uint32_t character_code; + + CharacterBuilder( + BluCat::Character *character, FT_Face face, uint32_t character_code); +}; + +CharacterBuilder::CharacterBuilder( + BluCat::Character *character, FT_Face face, uint32_t character_code): + character{character}, + face{face}, + character_code{character_code} +{ +} + +// TODO: creating one image with one device memory for each character is +// ineficient +void +load_image(void *obj) +{ + auto self = static_cast(obj); + + FT_Error error; + std::vector source_image_raw; + const int num_channels = 4; // all images are converted to RGBA + auto glyph_index{FT_Get_Char_Index(self->face, self->character_code)}; + + error = FT_Load_Glyph(self->face, glyph_index, FT_LOAD_DEFAULT); + if(error) throw CommandError{"failed to load glyph"}; + + error = FT_Render_Glyph(self->face->glyph, FT_RENDER_MODE_NORMAL); + if(error) throw CommandError{"failed to render glyph"}; + + self->character->bearing_x = self->face->glyph->bitmap_left; + self->character->bearing_y = self->face->glyph->bitmap_top; + self->character->advance = (self->face->glyph->advance.x >> 6); + self->character->width = self->face->glyph->bitmap.width; + self->character->height = self->face->glyph->bitmap.rows; + self->character->mip_levels = 1; + + // Character is a white-space. + if(self->character->width <= 0) + { + self->character->image = VK_NULL_HANDLE; + self->character->device_memory = VK_NULL_HANDLE; + + return; + } + + auto image_size{static_cast( + self->face->glyph->bitmap.width * + self->face->glyph->bitmap.rows * num_channels)}; + + { // Create the data for the image buffer + source_image_raw.resize(image_size, 0); + + for(auto y{0}; y < self->face->glyph->bitmap.width; y++) + { + for(auto x{0}; x < self->face->glyph->bitmap.rows; x++) + { + auto image_coord = y * self->face->glyph->bitmap.rows * num_channels + + x * num_channels; + auto glyph_coord = y * self->face->glyph->bitmap.rows + x; + // Red + source_image_raw[image_coord] = 255; + // Green + source_image_raw[image_coord + 1] = 255; + // Blue + source_image_raw[image_coord + 2] = 255; + // Alpha + source_image_raw[image_coord + 3] = + self->face->glyph->bitmap.buffer[glyph_coord]; + } + } + } + + BluCat::SourceBuffer source_image_buffer{ + cg_core.vk_device_with_swapchain, source_image_raw.data(), + image_size}; + + { // Create Vulkan image. + try + { + VkExtent3D vk_extent3d; + vk_extent3d.width = self->face->glyph->bitmap.width; + vk_extent3d.height = self->face->glyph->bitmap.rows; + vk_extent3d.depth = 1; + + BluCat::Image::create( + cg_core.vk_device_with_swapchain, + &self->character->image, + &self->character->device_memory, + VK_FORMAT_R8G8B8A8_UNORM, + vk_extent3d, + self->character->mip_levels, + VK_IMAGE_TILING_OPTIMAL, + VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT); + } + catch(BluCat::Image::Error error) + { + throw CommandError{error.what()}; + } + } + + { // Copy image from buffer into image. + auto queue_family{cg_core.vk_device_with_swapchain-> + get_queue_family_with_presentation()}; + auto queue{queue_family->get_queue()}; + BluCat::CommandPool command_pool{queue_family, 1}; + VkCommandBuffer vk_command_buffer{command_pool.command_buffers[0]}; + + queue.submit_one_time_command(vk_command_buffer, [&](){ + BluCat::Image::move_image_state( + vk_command_buffer, self->character->image, VK_FORMAT_R8G8B8A8_UNORM, + 0, VK_ACCESS_TRANSFER_WRITE_BIT, VK_IMAGE_LAYOUT_UNDEFINED, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_PIPELINE_STAGE_HOST_BIT, + VK_PIPELINE_STAGE_TRANSFER_BIT); + + VkBufferImageCopy image_copy; + image_copy.bufferOffset = 0; + image_copy.bufferRowLength = 0; + image_copy.bufferImageHeight = 0; + image_copy.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + image_copy.imageSubresource.mipLevel = 0; + image_copy.imageSubresource.baseArrayLayer = 0; + image_copy.imageSubresource.layerCount = 1; + image_copy.imageOffset = {0, 0, 0}; + image_copy.imageExtent = { + self->character->width, self->character->height, 1}; + + vkCmdCopyBufferToImage( + vk_command_buffer, source_image_buffer.buffer, self->character->image, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &image_copy); + + BluCat::Image::move_image_state( + vk_command_buffer, self->character->image, VK_FORMAT_R8G8B8A8_UNORM, + VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_SHADER_READ_BIT, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, + VK_PIPELINE_STAGE_TRANSFER_BIT, + VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT); + }); + } +} + +void +unload_image(void *obj) +{ + auto self = static_cast(obj); + + vkDestroyImage( + cg_core.vk_device_with_swapchain->device, self->character->image, nullptr); + vkFreeMemory( + cg_core.vk_device_with_swapchain->device, self->character->device_memory, + nullptr); +} + +const CommandChain loader{ + {&load_image, &unload_image} +}; + +} + +namespace BluCat +{ + +Character::Character(FT_Face face, uint32_t character_code) +{ + CharacterBuilder character_builder(this, face, character_code); + loader.execute(&character_builder); +} + +Character::~Character() +{ + CharacterBuilder character_builder(this, nullptr, 0); + loader.revert(&character_builder); +} + +std::vector +Character::str_to_unicode(const char* str) +{ + std::vector unicode_text; + int text_width{0}; + int text_height{0}; + + { // Reserve memory + int size{0}; + for(auto i{0}; str[i] != '\0'; i++) + if((str[i] & 0b11000000) != 0b10000000) size++; + unicode_text.reserve(size); + } + + for(auto i{0}; str[i] != '\0'; i++) + { + int num_bytes; + uint32_t codepoint{0}; + + if(str[i] >= 0 && str[i] < 127) + { // Normal ASCI character, 1-byte. + num_bytes = 1; + codepoint = str[i]; + } + else if((str[i] & 0xE0) == 0xC0) + { // 2-byte character. + num_bytes = 2; + codepoint += ((str[i] & 0b00011111) << 6); + } + else if((str[i] & 0xF0) == 0xE0) + { // 3-byte character. + num_bytes = 3; + codepoint += ((str[i] & 0b00001111) << 12); + } + else if((str[i] & 0xF8) == 0xF0) + { // 4-byte character. + num_bytes = 4; + codepoint += ((str[i] & 0b00000111) << 18); + } + else + { // FIXME: Add support to 5-byte and 6-byte characters. + } + + switch (num_bytes) + { + case 4: + i++; + codepoint += ((str[i] & 0b00111111) << 12); + case 3: + i++; + codepoint += ((str[i] & 0b00111111) << 6); + case 2: + i++; + codepoint += (str[i] & 0b00111111); + case 1: + default: + break; + } + + unicode_text.push_back(codepoint); + } + + return unicode_text; +} + +} -- cgit v1.2.3