summaryrefslogtreecommitdiff
path: root/src/vk/character.cpp
diff options
context:
space:
mode:
authorFrederico Linhares <fred@linhares.blue>2023-04-20 16:17:49 -0300
committerFrederico Linhares <fred@linhares.blue>2023-04-20 16:17:49 -0300
commit66cb556fb6f87d195aacf8a25ffafb86d524da19 (patch)
tree358b69272bd46190092e4297f85642033b493d0a /src/vk/character.cpp
parent63748c1035d3fb39ad7b6ab1f6ad1f829ed85758 (diff)
feat Create text rendering system
Diffstat (limited to 'src/vk/character.cpp')
-rw-r--r--src/vk/character.cpp265
1 files changed, 265 insertions, 0 deletions
diff --git a/src/vk/character.cpp b/src/vk/character.cpp
new file mode 100644
index 0000000..04ddb0c
--- /dev/null
+++ b/src/vk/character.cpp
@@ -0,0 +1,265 @@
+/*
+ * Copyright 2022-2023 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
+{
+ VK::Character *character;
+ FT_Face face;
+ uint32_t character_code;
+
+ CharacterBuilder(
+ VK::Character *character, FT_Face face, uint32_t character_code);
+};
+
+CharacterBuilder::CharacterBuilder(
+ VK::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<CharacterBuilder*>(obj);
+
+ FT_Error error;
+ std::vector<uint8_t> 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->width = self->face->glyph->bitmap.width;
+ self->character->height = self->face->glyph->bitmap.rows;
+ self->character->mip_levels = 1;
+ self->character->bearing_x = self->face->glyph->bitmap_left;
+ self->character->bearing_y = self->face->glyph->bitmap_top;
+ self->character->advance = self->face->glyph->bitmap.width; // self->face->glyph->advance.x;
+
+ auto image_size{static_cast<size_t>(
+ 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);
+
+ 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] = 0;
+ // Green
+ source_image_raw[image_coord + 1] = 0;
+ // Blue
+ source_image_raw[image_coord + 2] = 0;
+ // Alpha
+ source_image_raw[image_coord + 3] =
+ self->face->glyph->bitmap.buffer[glyph_coord];
+ }
+ }
+ }
+
+ VK::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;
+
+ VK::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(VK::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()};
+ VK::CommandPool command_pool{queue_family, 1};
+ VkCommandBuffer vk_command_buffer{command_pool.command_buffers[0]};
+
+ queue.submit_one_time_command(vk_command_buffer, [&](){
+ VK::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);
+
+ VK::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<CharacterBuilder*>(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 VK
+{
+
+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<uint32_t>
+Character::str_to_unicode(const char* str)
+{
+ std::vector<uint32_t> 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;
+}
+
+}