diff options
| author | Frederico Linhares <fred@linhares.blue> | 2024-05-08 17:56:29 -0300 | 
|---|---|---|
| committer | Frederico Linhares <fred@linhares.blue> | 2024-05-08 17:56:29 -0300 | 
| commit | 43821b0cffc5aa419c0218992f06f8962ae54a13 (patch) | |
| tree | 97bdbbf710a78e6dcb181d92dd83e98d8b329c6d /src/blucat/character.cpp | |
| parent | 70e156d47346ae3198c623e0af75e5703f894db3 (diff) | |
refa Rename graphical engine to BluCat
Diffstat (limited to 'src/blucat/character.cpp')
| -rw-r--r-- | src/blucat/character.cpp | 274 | 
1 files changed, 274 insertions, 0 deletions
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<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->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<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, 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<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 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<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; +} + +}  | 
