23 #include "base/integral_types.h"
53 using math::Vector2ui;
64 typedef base::Array2<double> SdfGrid;
70 typedef base::AllocMap<GlyphIndex, SdfGrid> SdfGridMap;
77 struct ImageDataWrapper {
98 struct DeferredUpdate : Texture::SubImage {
101 DeferredUpdate(
const TexturePtr& texture_in,
size_t level_in,
102 const math::Point2ui& offset_in,
const ImagePtr& image_in)
103 : Texture::SubImage(level_in, offset_in, image_in),
120 static const SdfGridMap BuildSdfGridMap(
const Font& font,
123 SdfGridMap grid_map(allocator);
124 for (
auto it = glyph_set.begin(); it != glyph_set.end(); ++it) {
125 const Font::GlyphGrid& glyph_grid = font.GetGlyphGrid(*it);
128 grid_map[*it] = glyph_grid.pixels;
135 static void AddGridsToBinPacker(
const SdfGridMap& grids, BinPacker* packer) {
136 for (SdfGridMap::const_iterator it = grids.begin(); it != grids.end(); ++it) {
137 const SdfGrid& grid = it->second;
138 const uint32
width =
static_cast<uint32
>(grid.GetWidth());
139 const uint32 height =
static_cast<uint32
>(grid.GetHeight());
141 packer->AddRectangle(it->first, Vector2ui(width, height));
146 static size_t ComputeTotalGridArea(
const SdfGridMap& grids) {
148 for (SdfGridMap::const_iterator it = grids.begin(); it != grids.end(); ++it) {
149 const SdfGrid& grid = it->second;
150 area += grid.GetWidth() * grid.GetHeight();
156 static void InsertGrid(
const SdfGrid& grid,
const Point2ui& bottom_left,
157 SdfGrid* composite_grid) {
158 DCHECK_LE(bottom_left[0] + grid.GetWidth(), composite_grid->GetWidth());
159 DCHECK_LE(bottom_left[1] + grid.GetHeight(), composite_grid->GetHeight());
161 const size_t width = grid.GetWidth();
162 const size_t height = grid.GetHeight();
163 for (
size_t y = 0; y < height; ++y) {
164 for (
size_t x = 0; x <
width; ++x) {
165 composite_grid->Set(x + bottom_left[0], y + bottom_left[1],
173 static void NormalizeGrid(
const double scale_factor, SdfGrid* grid) {
174 const size_t width = grid->GetWidth();
175 const size_t height = grid->GetHeight();
177 for (
size_t y = 0; y < height; ++y) {
178 for (
size_t x = 0; x <
width; ++x) {
180 const double d = scale_factor * grid->Get(x, y);
182 grid->Set(x, y, (
math::Clamp(d, -1.0, 1.0) + 1.0) * 0.5);
189 static void NormalizeGridsInMap(
size_t sdf_padding, SdfGridMap* grids) {
190 const float scale_factor =
191 sdf_padding ? 1.0f /
static_cast<float>(sdf_padding) : 1.0f;
192 for (SdfGridMap::iterator it = grids->begin(); it != grids->end(); ++it)
193 NormalizeGrid(scale_factor, &it->second);
199 static const SdfGrid CreatePackedGrid(
200 const SdfGridMap& grids,
const BinPacker&
bin_packer,
201 uint32 width, uint32 height,
size_t sdf_padding) {
204 const double initial_value =
static_cast<double>(width + height);
205 SdfGrid packed_grid(width, height, initial_value);
207 const std::vector<BinPacker::Rectangle>& rects = bin_packer.GetRectangles();
208 const size_t count = rects.size();
209 for (
size_t i = 0; i < count; ++i) {
210 const BinPacker::Rectangle& rect = rects[i];
211 SdfGridMap::const_iterator it =
212 grids.find(static_cast<GlyphIndex>(rect.id));
213 DCHECK(it != grids.end());
214 InsertGrid(it->second, rect.bottom_left, &packed_grid);
217 const float scale_factor =
218 sdf_padding ? 1.0f /
static_cast<float>(sdf_padding) : 1.0f;
219 NormalizeGrid(scale_factor, &packed_grid);
229 static const SdfGrid PackIntoMinimalGrid(
230 const SdfGridMap& grids,
size_t max_image_size,
size_t sdf_padding,
231 BinPacker* bin_packer) {
233 const size_t total_area = ComputeTotalGridArea(grids);
243 const size_t initial_size =
245 uint32 image_width =
static_cast<uint32
>(initial_size);
246 uint32 image_height = image_width;
247 bool double_the_width =
true;
248 while (!bin_packer->Pack(Vector2ui(image_width, image_height))) {
252 if (double_the_width)
256 double_the_width = !double_the_width;
257 if (image_width > max_image_size || image_height > max_image_size)
261 return CreatePackedGrid(grids, *bin_packer, image_width, image_height,
272 *sampler_ptr = sampler;
294 static ImagePtr CreateImage(
size_t width,
size_t height,
297 std::vector<uint8> data_buf(width * height);
303 image->Set(Image::kLuminance,
304 static_cast<uint32>(width), static_cast<uint32>(height),
305 base::DataContainer::CreateAndCopy<uint8>(
306 &data_buf[0], data_buf.size(),
true, allocator));
312 static void StoreGridInImage(
const SdfGrid& grid,
const ImagePtr&
image) {
313 const size_t width = grid.GetWidth();
314 const size_t height = grid.GetHeight();
317 DCHECK(image->GetData().Get());
318 DCHECK_EQ(image->GetDataSize(), width * height);
319 uint8* data = image->GetData()->GetMutableData<uint8>();
323 for (
size_t y = 0; y < height; ++y) {
324 for (
size_t x = 0; x <
width; ++x) {
325 const double d = grid.Get(x, y);
326 data[y * width + x] =
static_cast<uint8
>(d * 255.0);
333 static const Range2f ComputeTextureRectangle(
334 const BinPacker::Rectangle& rect,
const Vector2f& inverse_image_size) {
335 const Point2f min_point(
336 static_cast<float>(rect.bottom_left[0]) * inverse_image_size[0],
337 static_cast<float>(rect.bottom_left[1]) * inverse_image_size[1]);
338 const Vector2f size(static_cast<float>(rect.size[0]) * inverse_image_size[0],
339 static_cast<float>(rect.size[1]) * inverse_image_size[1]);
340 return Range2f::BuildWithSize(min_point, size);
345 static const TexRectMap ComputeTextureRectangleMap(
346 const Image& image,
const BinPacker& bin_packer) {
347 const Vector2f inverse_image_size(
348 1.0f / static_cast<float>(image.GetWidth()),
349 1.0f / static_cast<float>(image.GetHeight()));
352 const std::vector<BinPacker::Rectangle>& rects = bin_packer.GetRectangles();
353 const size_t num_rects = rects.size();
354 TexRectMap texture_rectangle_map(image.GetAllocator());
355 for (
size_t i = 0; i < num_rects; ++i) {
356 const BinPacker::Rectangle& rect = rects[i];
357 texture_rectangle_map[
static_cast<GlyphIndex>(rect.id)] =
358 ComputeTextureRectangle(rect, inverse_image_size);
360 return texture_rectangle_map;
365 static void UpdateImageData(
366 const SdfGridMap& grid_map,
const BinPacker& bin_packer, uint32 image_size,
367 size_t sdf_padding, FontImage::ImageData*
image_data,
369 const SdfGrid packed_grid = CreatePackedGrid(
370 grid_map, bin_packer, image_size, image_size, sdf_padding);
371 DCHECK(packed_grid.GetSize());
374 if (!image_data->texture->HasImage(0U))
375 image_data->texture->SetImage(
376 0U, CreateImage(packed_grid.GetWidth(), packed_grid.GetHeight(),
378 const ImagePtr& image = image_data->texture->GetImage(0U);
381 StoreGridInImage(packed_grid, image);
384 image_data->texture_rectangle_map =
385 ComputeTextureRectangleMap(*image, bin_packer);
393 static void StoreSubImages(
const SdfGridMap& grids,
const BinPacker& bin_packer,
396 base::AllocVector<DeferredUpdate>* updates) {
397 const std::vector<BinPacker::Rectangle>& rects = bin_packer.GetRectangles();
398 const size_t count = rects.size();
399 for (
size_t i = 0; i < count; ++i) {
400 const BinPacker::Rectangle& rect = rects[i];
401 const auto& it = grids.find(static_cast<GlyphIndex>(rect.id));
404 if (it != grids.end()) {
405 const SdfGrid& grid = it->second;
406 ImagePtr image = CreateImage(grid.GetWidth(), grid.GetHeight(), alloc);
407 StoreGridInImage(grid, image);
410 DeferredUpdate(texture, 0U, rect.bottom_left, image));
412 texture->SetSubImage(0U, rect.bottom_left, image);
420 std::set_difference(lhs.cbegin(), lhs.cend(), rhs.cbegin(), rhs.cend(),
421 std::inserter(*diff, diff->end()));
435 : texture(CreateTexture(allocator)),
436 glyph_set(allocator),
437 texture_rectangle_map(allocator) {}
448 : type_(type), font_(font), max_image_size_(max_image_size) {}
457 const ImageData::TexRectMap::const_iterator it =
461 *rectangle = it->second;
462 return !rectangle->IsEmpty();
475 :
FontImage(kStatic, font, max_image_size),
477 InitImageData(font.Get()
478 ? font->GetName() +
"_" +
484 const ImageData& image_data)
485 :
FontImage(kStatic, font, max_image_size),
486 image_data_(image_data) {}
497 const std::string& texture_name,
const GlyphSet& glyph_set) {
500 image_data.texture->SetLabel(texture_name);
503 if (glyph_set.empty() || !font) {
507 image_data.glyph_set = glyph_set;
515 const SdfGridMap grid_map = BuildSdfGridMap(*font, glyph_set, sta);
519 AddGridsToBinPacker(grid_map, &bin_packer);
522 const SdfGrid packed_grid = PackIntoMinimalGrid(
526 if (packed_grid.GetSize()) {
529 CreateImage(packed_grid.GetWidth(), packed_grid.GetHeight(), allocator);
530 image_data.texture->SetImage(0U, image);
531 StoreGridInImage(packed_grid, image);
534 image_data.texture_rectangle_map =
535 ComputeTextureRectangleMap(*image, bin_packer);
550 class DynamicFontImage::Helper :
public Allocatable {
552 Helper() : image_data_wrappers_(*this), deferred_updates_(*this) {}
555 base::AllocVector<ImageDataWrapper>& GetImageDataWrappers() {
556 return image_data_wrappers_;
560 base::AllocVector<DeferredUpdate>& GetDeferredUpdates() {
561 return deferred_updates_;
566 base::AllocVector<ImageDataWrapper> image_data_wrappers_;
569 base::AllocVector<DeferredUpdate> deferred_updates_;
582 helper_(new(GetAllocator()) Helper()),
583 updates_deferred_(false) {}
588 return helper_->GetImageDataWrappers().size();
593 helper_->GetImageDataWrappers();
594 return index < wrappers.size() ? wrappers[index].image_data
595 : base::InvalidReference<ImageData>();
600 helper_->GetImageDataWrappers();
601 return index < wrappers.size() ? wrappers[index].used_area_fraction : 0.f;
605 if (updates_deferred_) {
609 const size_t count = updates.size();
610 for (
size_t i = 0; i < count; ++i) {
611 const DeferredUpdate& di = updates[i];
612 di.texture->SetSubImage(di.level, di.offset, di.image);
624 const GlyphSet& unfiltered_glyph_set) {
633 unfiltered_glyph_set);
634 GetFont()->FilterGlyphs(&glyph_set);
636 if (glyph_set.empty() || (glyph_set.size() == 1 && !(*glyph_set.begin())))
640 GetFont()->CacheSdfGrids(glyph_set);
646 index = FindContainingImageDataIndexPrefiltered(glyph_set);
650 index = FindImageDataThatFits(glyph_set);
656 index = AddImageData(glyph_set);
663 const GlyphSet& unfiltered_glyph_set) {
668 unfiltered_glyph_set);
669 GetFont()->FilterGlyphs(&glyph_set);
670 return glyph_set.size() ? FindContainingImageDataIndexPrefiltered(glyph_set)
674 size_t DynamicFontImage::FindContainingImageDataIndexPrefiltered(
677 helper_->GetImageDataWrappers();
678 const size_t num_wrappers = wrappers.size();
679 for (
size_t i = 0; i < num_wrappers; ++i) {
680 if (
HasAllGlyphs(wrappers[i].image_data, glyph_set))
return i;
681 const auto& candidate = wrappers[i].image_data.glyph_set;
682 if (std::includes(candidate.cbegin(), candidate.cend(), glyph_set.cbegin(),
690 size_t DynamicFontImage::FindImageDataThatFits(
const GlyphSet& glyph_set) {
696 base::AllocVector<ImageDataWrapper>& wrappers =
697 helper_->GetImageDataWrappers();
698 const size_t num_wrappers = wrappers.size();
699 for (
size_t i = 0; i < num_wrappers; ++i) {
700 ImageDataWrapper& wrapper = wrappers[i];
701 ImageData& image_data = wrapper.image_data;
709 SetDifference(glyph_set, image_data.glyph_set, &missing_glyph_set);
710 DCHECK(!missing_glyph_set.empty());
711 SdfGridMap missing_grid_map = BuildSdfGridMap(font, missing_glyph_set, sta);
715 const size_t added_area = ComputeTotalGridArea(missing_grid_map);
716 if (wrapper.packed_area + added_area > max_area)
721 BinPacker test_bin_packer(wrapper.bin_packer);
722 AddGridsToBinPacker(missing_grid_map, &test_bin_packer);
725 if (test_bin_packer.Pack(Vector2ui(image_size, image_size))) {
727 image_data.glyph_set.insert(missing_glyph_set.begin(),
728 missing_glyph_set.end());
731 wrapper.bin_packer = test_bin_packer;
734 NormalizeGridsInMap(font.GetSdfPadding(), &missing_grid_map);
736 if (updates_deferred_) {
737 base::WriteLock lock(&update_lock_);
739 StoreSubImages(missing_grid_map, test_bin_packer, sta,
740 wrapper.image_data.texture,
741 &helper_->GetDeferredUpdates());
743 StoreSubImages(missing_grid_map, test_bin_packer, sta,
744 wrapper.image_data.texture, NULL);
747 wrapper.image_data.texture_rectangle_map = ComputeTextureRectangleMap(
748 *wrapper.image_data.texture->GetImage(0U), test_bin_packer);
751 wrapper.packed_area += added_area;
752 wrapper.used_area_fraction +=
753 static_cast<float>(added_area) / static_cast<float>(max_area);
762 size_t DynamicFontImage::AddImageData(
const GlyphSet& glyph_set) {
770 base::AllocVector<ImageDataWrapper>& wrappers =
771 helper_->GetImageDataWrappers();
772 const size_t index = wrappers.size();
773 wrappers.push_back(ImageDataWrapper(allocator));
774 ImageDataWrapper& wrapper = wrappers[index];
775 ImageData& image_data = wrapper.image_data;
776 const std::string texture_name = font.GetName() +
"_" +
779 image_data.texture->SetLabel(texture_name);
782 const SdfGridMap grid_map = BuildSdfGridMap(font, glyph_set, sta);
785 AddGridsToBinPacker(grid_map, &wrapper.bin_packer);
789 if (wrapper.bin_packer.Pack(Vector2ui(image_size, image_size))) {
790 DCHECK(!wrapper.image_data.texture->HasImage(0U));
791 UpdateImageData(grid_map, wrapper.bin_packer, image_size,
792 font.GetSdfPadding(), &wrapper.image_data, sta);
795 image_data.glyph_set = glyph_set;
798 wrapper.packed_area = ComputeTotalGridArea(grid_map);
799 wrapper.used_area_fraction =
800 static_cast<float>(wrapper.packed_area) /
bool IsInvalidReference(const T &value)
IsInvalidReference() returns true if a passed const reference of type T has an address of InvalidRefe...
kShortTerm is used for objects that are very transient in nature, such as scratch memory used to comp...
size_t GetSdfPadding() const
Returns the padding value used when generating SDF glyphs from the font.
TexRectMap texture_rectangle_map
base::ReferentPtr< Image >::Type ImagePtr
#define ION_DECLARE_SAFE_STATIC_POINTER_WITH_CONSTRUCTOR(type, variable, constructor)
Declare a static non-array pointer and calls a non-default constructor.
~FontImage() override
The destructor is protected because all base::Referent classes must have protected or private destruc...
const size_t kInvalidIndex
kInvalidIndex is a size_t value that is very unlikely to be a valid index.
size_t packed_area
Area (in pixels) of the glyphs already packed into the ImageData.
const ImageData & FindImageData(const GlyphSet &glyph_set) override
Implements this function to find an existing ImageData that already contains all of the glyphs (prese...
#define DCHECK_GT(val1, val2)
size_t FindContainingImageDataIndex(const GlyphSet &glyph_set)
Returns the index of an ImageData instance that contains all of the glyphs (present in the Font) in g...
virtual const AllocatorPtr & GetAllocatorForLifetime(AllocationLifetime lifetime) const
Returns the correct Allocator to use to allocate memory with a specific lifetime. ...
const ImageData & GetImageData(size_t index) const
Returns the indexed ImageData instance, or an invalid reference if the index is out of range...
Range< 2, float > Range2f
float used_area_fraction
Fraction of area used.
~DynamicFontImage() override
T Sqrt(const T &val)
Returns the square root of a value.
SharedPtr< Allocator > AllocatorPtr
A FontImage contains image and texture coordinate information used to render font glyphs...
base::ReferentPtr< Sampler >::Type SamplerPtr
Convenience typedef for shared pointer to a Sampler.
const AllocatorPtr & GetAllocatorForLifetime(AllocationLifetime lifetime) const
Convenience function that returns the Allocator to use to allocate an object with a specific lifetime...
void ProcessDeferredUpdates()
Updates internal texture data with any deferred updates.
T * Get() const
Returns a raw pointer to the instance, which may be NULL.
ImageData(const base::AllocatorPtr &allocator)
FontImage::ImageData functions.
A LockGuard locks a mutex when created, and unlocks it when destroyed.
std::string ValueToString(const T &val)
ValueToString.
base::AllocSet< GlyphIndex > GlyphSet
size_t FindImageDataIndex(const GlyphSet &glyph_set)
This is the same as FindImageData(), but instead returns the index of the ImageData, or kInvalidIndex if unsuccessful.
static bool GetTextureCoords(const ImageData &image_data, GlyphIndex glyph_index, math::Range2f *rectangle)
Convenience function that sets rectangle to the texture coordinate rectangle to use for the indexed g...
size_t GetMaxImageSize()
Returns the maximum image size passed to the constructor.
const T Clamp(const T &val, const T &min_val, const T &max_val)
Clamps a value to lie between a minimum and maximum, inclusive.
float GetImageDataUsedAreaFraction(size_t index) const
Returns the area covered by glyphs in the indexed ImageData, or 0 if the index is out of range...
const Grid & image
The original monochrome image data, as doubles (0 - 1).
~StaticFontImage() override
Copyright 2016 Google Inc.
uint32 NextPowerOf2(uint32 n)
Returns the next power of 2 greater than or equal to n.
#define DCHECK_EQ(val1, val2)
TexturePtr texture
The Texture to add sub-image data to.
#define DCHECK_LE(val1, val2)
FontImage(Type type, const FontPtr &font, size_t max_image_size)
The constructor is passed the type of derived class, the Font to use, and the maximum image size (in ...
const T Square(const T &val)
Squares a value.
void CacheSdfGrids(const GlyphSet &glyph_set)
Makes sure that the GlyphData for each glyph in glyph_set has an SDF grid cached inside the font...
base::AllocMap< GlyphIndex, math::Range2f > TexRectMap
Maps glyph index to a texture coordinate rectangle.
const FontPtr & GetFont()
Returns the Font passed to the constructor.
BinPacker bin_packer
BinPacker used to pack glyphs into the FontImage.
static bool HasAllGlyphs(const ImageData &image_data, const GlyphSet &glyph_set)
Convenience function that returns true if an ImageData instance contains all glyphs in glyph_set...
A ReadLock obtains a read lock, but has a similar interface to a Mutex and can be used with a ReadGua...
const AllocatorPtr & GetAllocator() const
Returns the Allocator that was used for the instance.
StaticFontImage(const FontPtr &font, size_t max_image_size, const GlyphSet &glyph_set)
The constructor sets up the single ImageData instance to contain glyphs for all the requested glyphs...
kLongTerm is used for objects that have persistent lifetimes, such as managers.
const ImageData & FindImageData(const GlyphSet &glyph_sets) override
Implements this function to return the single ImageData instance, whether or not it contains all the ...
FontImage::ImageData image_data
The wrapped ImageData instance.
DynamicFontImage(const FontPtr &font, size_t image_size)
The constructor sets up the DynamicFontImage to use the Font.
Data for each image in the FontImage.
GenericLockGuard< WriteLock > WriteGuard
size_t GetImageDataCount() const
Returns the current count of ImageData instances.
base::ReferentPtr< Texture >::Type TexturePtr
Convenience typedef for shared pointer to a Texture.
static const AllocatorPtr & GetDefaultAllocatorForLifetime(AllocationLifetime lifetime)
Font is a base class for implementation-specific representations of fonts.