💿🐜 Antkeeper source code https://antkeeper.com
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

219 lines
5.5 KiB

/*
* Copyright (C) 2021 Christopher J. Howard
*
* This file is part of Antkeeper source code.
*
* Antkeeper source code is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Antkeeper source code is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Antkeeper source code. If not, see <http://www.gnu.org/licenses/>.
*/
#include "type/bitmap-font.hpp"
#include "geom/rect-pack.hpp"
#include <stdexcept>
namespace type {
bitmap_font::bitmap_font(const font_metrics& metrics):
font(metrics)
{}
bitmap_font::bitmap_font()
{}
bool bitmap_font::contains(char32_t code) const
{
return glyphs.count(code) != 0;
}
void bitmap_font::insert(char32_t code, const bitmap_glyph& glyph)
{
glyphs[code] = glyph;
}
void bitmap_font::remove(char32_t code)
{
if (auto it = glyphs.find(code); it != glyphs.end())
glyphs.erase(it);
}
void bitmap_font::clear()
{
glyphs.clear();
}
bool bitmap_font::pack(bool resize)
{
// Returns the smallest power of two that is not smaller than @p x.
auto ceil2 = [](unsigned int x) -> unsigned int
{
if (x <= 1)
return 1;
unsigned int y = 2;
--x;
while (x >>= 1)
y <<= 1;
return y;
};
// Calculate initial size of the font bitmap
unsigned int bitmap_w;
unsigned int bitmap_h;
if (resize)
{
// Find the maximum glyph dimensions
unsigned int max_glyph_w = 0;
unsigned int max_glyph_h = 0;
for (auto it = glyphs.begin(); it != glyphs.end(); ++it)
{
max_glyph_w = std::max(max_glyph_w, it->second.bitmap.get_width());
max_glyph_h = std::max(max_glyph_h, it->second.bitmap.get_height());
}
// Find minimum power of two dimensions that can accommodate maximum glyph dimensions
bitmap_w = ceil2(max_glyph_w);
bitmap_h = ceil2(max_glyph_h);
}
else
{
bitmap_w = bitmap.get_width();
bitmap_h = bitmap.get_height();
}
bool packed = false;
geom::rect_pack<unsigned int> glyph_pack(bitmap_w, bitmap_h);
std::unordered_map<char32_t, const typename geom::rect_pack<unsigned int>::node_type*> glyph_map;
while (!packed)
{
// For each glyph
for (auto it = glyphs.begin(); it != glyphs.end(); ++it)
{
// Attempt to pack glyph bitmap
const auto* node = glyph_pack.pack(it->second.bitmap.get_width(), it->second.bitmap.get_height());
// Abort if packing failed
if (!node)
break;
// Map pack node to glyph character code
glyph_map[it->first] = node;
}
// Check if not all glyphs were packed
if (glyph_map.size() != glyphs.size())
{
if (!resize)
{
// No resize, packing failed
packed = false;
break;
}
// Clear glyph map
glyph_map.clear();
// Clear glyph pack
glyph_pack.clear();
// Resize glyph pack
if (bitmap_w > bitmap_h)
bitmap_h = ceil2(++bitmap_h);
else
bitmap_w = ceil2(++bitmap_w);
glyph_pack.resize(bitmap_w, bitmap_h);
}
else
{
packed = true;
}
}
// Copy glyph bitmaps into font bitmap
if (packed)
{
// Resize font bitmap
bitmap.resize(bitmap_w, bitmap_h);
// For each glyph
for (auto it = glyphs.begin(); it != glyphs.end(); ++it)
{
// Find rect pack node corresponding to the glyph
const auto* node = glyph_map[it->first];
// Copy glyph bitmap data into font bitmap
image& glyph_bitmap = it->second.bitmap;
bitmap.copy(glyph_bitmap, glyph_bitmap.get_width(), glyph_bitmap.get_height(), 0, 0, node->bounds.min.x, node->bounds.min.y);
// Record coordinates of glyph bitmap within font bitmap
it->second.position = {node->bounds.min.x, node->bounds.min.y};
// Clear glyph bitmap data
glyph_bitmap.resize(0, 0);
}
}
return packed;
}
void bitmap_font::unpack(bool resize)
{
for (auto it = glyphs.begin(); it != glyphs.end(); ++it)
{
bitmap_glyph& glyph = it->second;
// Get glyph dimensions
unsigned int glyph_width = static_cast<unsigned int>(glyph.metrics.width + 0.5f);
unsigned int glyph_height = static_cast<unsigned int>(glyph.metrics.height + 0.5f);
// Reformat glyph bitmap if necessary
if (!glyph.bitmap.compatible(bitmap))
glyph.bitmap.format(bitmap.get_component_size(), bitmap.get_channel_count());
// Resize glyph bitmap if necessary
if (glyph.bitmap.get_width() != glyph_width || glyph.bitmap.get_height() != glyph_height)
glyph.bitmap.resize(glyph_width, glyph_height);
// Copy pixel data from font bitmap to glyph bitmap
glyph.bitmap.copy(bitmap, glyph_width, glyph_height, glyph.position.x, glyph.position.y);
}
// Free font bitmap pixel data
if (resize)
{
bitmap.resize(0, 0);
}
}
const glyph_metrics& bitmap_font::get_glyph_metrics(char32_t code) const
{
if (auto it = glyphs.find(code); it != glyphs.end())
return it->second.metrics;
throw std::invalid_argument("Cannot fetch metrics of unknown bitmap glyph");
}
const bitmap_glyph& bitmap_font::get_glyph(char32_t code) const
{
if (auto it = glyphs.find(code); it != glyphs.end())
return it->second;
throw std::invalid_argument("Cannot get unknown bitmap glyph");
}
bitmap_glyph& bitmap_font::get_glyph(char32_t code)
{
if (auto it = glyphs.find(code); it != glyphs.end())
return it->second;
throw std::invalid_argument("Cannot get unknown bitmap glyph");
}
} // namespace type