blob: 451a0758d8869479f4c4e9fcf129d681bcf19243 [file] [log] [blame]
// Copyright 2019 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/ui/lib/escher/debug/debug_font.h"
#include <lib/syslog/cpp/macros.h>
#include "src/ui/lib/escher/renderer/batch_gpu_uploader.h"
#include "src/ui/lib/escher/types/color.h"
#include "src/ui/lib/escher/util/alloca.h"
#include "src/ui/lib/escher/util/image_utils.h"
#include "src/ui/lib/escher/vk/image.h"
namespace escher {
std::unique_ptr<DebugFont> DebugFont::New(BatchGpuUploader* uploader, ImageFactory* factory) {
auto bytes = DebugFont::GetFontPixels();
auto image = image_utils::NewRgbaImage(factory, uploader, kGlyphWidth, kGlyphHeight * kNumGlyphs,
bytes.get(), vk::ImageLayout::eTransferSrcOptimal);
return std::unique_ptr<DebugFont>(new DebugFont(std::move(image)));
}
DebugFont::DebugFont(ImagePtr image) : image_(std::move(image)) { FX_DCHECK(image_); }
void DebugFont::Blit(CommandBuffer* cb, const std::string& text, const ImagePtr& target,
vk::Offset2D offset, int32_t scale) {
cb->KeepAlive(target);
vk::ImageBlit* regions = ESCHER_ALLOCA(vk::ImageBlit, text.length());
const int32_t dst_top = offset.y;
const int32_t dst_bottom = offset.y + kGlyphHeight * scale;
uint32_t region_count = 0;
for (size_t i = 0; i < text.length(); ++i) {
const uint32_t glyph_index = static_cast<uint32_t>(text[i]);
if (glyph_index >= kNumGlyphs) {
continue;
}
const int32_t src_top = glyph_index * kGlyphHeight;
const int32_t src_bottom = src_top + kGlyphHeight;
const int32_t dst_left = offset.x + region_count * kGlyphWidth * scale;
const int32_t dst_right = dst_left + kGlyphWidth * scale;
auto& region = regions[region_count];
region.srcSubresource.aspectMask = vk::ImageAspectFlagBits::eColor;
region.srcSubresource.mipLevel = 0;
region.srcSubresource.baseArrayLayer = 0;
region.srcSubresource.layerCount = 1;
region.dstSubresource = region.srcSubresource;
region.srcOffsets[0] = vk::Offset3D(0, src_top, 0);
region.srcOffsets[1] = vk::Offset3D(kGlyphWidth, src_bottom, 1);
region.dstOffsets[0] = vk::Offset3D(dst_left, dst_top, 0);
region.dstOffsets[1] = vk::Offset3D(dst_right, dst_bottom, 1);
++region_count;
}
if (region_count > 0) {
cb->vk().blitImage(image_->vk(), vk::ImageLayout::eTransferSrcOptimal, target->vk(),
vk::ImageLayout::eTransferDstOptimal, region_count, regions,
vk::Filter::eNearest);
}
}
std::unique_ptr<uint8_t[]> DebugFont::GetFontPixels() {
// Fill all glyph_bits with placeholder; we will replace some of these for the
// glyphs that we actually have bit patterns for.
uint32_t glyph_bits[kNumGlyphs];
for (size_t i = 0; i < kNumGlyphs; ++i) {
// placeholder (black square)
// 11111
// 11111
// 11111
// 11111
// 11111
// == 11111 11111 11111 11111 11111
// == 1 1111 1111 1111 1111 1111 1111
// == 0x1ffffff
glyph_bits[i] = 0x1ffffff;
}
// TODO(fxbug.dev/7297): glyphs for ASCII 0x0 - 0x1F
// 'space'
// .....
// .....
// .....
// .....
// .....
glyph_bits[32] = 0x0;
// '!'
// ..1..
// ..1..
// ..1..
// .....
// ..1..
// == 00100 00100 00100 00000 00100
// == 0100 0010 0001 0000 0000 0100
glyph_bits[int32_t{'!'}] = 0x421004;
// '"'
// .1.1.
// .1.1.
// .....
// .....
// .....
// == 01010 01010 00000 00000 00000
// == 1010 0101 0000 0000 0000 0000
glyph_bits[int32_t{'"'}] = 0xa50000;
// '#'
// .1.1.
// 11111
// .1.1.
// 11111
// .1.1.
// == 01010 11111 01010 11111 01010
// == 1010 1111 1010 1011 1110 1010
glyph_bits[int32_t{'#'}] = 0xafabea;
// TODO(fxbug.dev/7297): glyphs for $%&\()*+
// '-'
// .....
// .....
// 11111
// .....
// .....
// == 00000 00000 11111 00000 00000
// == 0111 1100 0000 0000
glyph_bits[int32_t{'-'}] = 0x7c00;
// '.'
// .....
// .....
// .....
// .....
// ..1..
// == 00000 00000 00000 00000 00100
glyph_bits[int32_t{'.'}] = 0x4;
// '0'
// 11111
// 1...1
// 1...1
// 1...1
// 11111
// == 11111 10001 10001 10001 11111
// == 0001 1111 1000 1100 0110 0011 1111
glyph_bits[int32_t{'0'}] = 0x1f8c63f;
// '1'
// ..1..
// ..1..
// ..1..
// ..1..
// ..1..
// == 00100 00100 00100 00100 00100
// == 0100 0010 0001 0000 1000 0100
glyph_bits[int32_t{'1'}] = 0x421084;
// '2'
// 11111
// ....1
// 11111
// 1....
// 11111
// == 11111 00001 11111 10000 11111
// == 0011 1111 0000 1111 1110 0001 1111
glyph_bits[int32_t{'2'}] = 0x3f0fe1f;
// '3'
// 11111
// ....1
// ..111
// ....1
// 11111
// == 11111 00001 00111 00001 11111
// == 0001 1111 0000 1001 1100 0011 1111
glyph_bits[int32_t{'3'}] = 0x1f09c3f;
// '4'
// 1...1
// 1...1
// 11111
// ....1
// ....1
// == 10001 10001 11111 00001 00001
// == 0001 0001 1000 1111 1100 0010 0001
glyph_bits[int32_t{'4'}] = 0x118fc21;
// '5'
// 11111
// 1....
// 11111
// ....1
// 11111
// == 11111 10000 11111 00001 11111
// == 0001 1111 1000 0111 1100 0011 1111
glyph_bits[int32_t{'5'}] = 0x1f87c3f;
// '6'
// 11111
// 1....
// 11111
// 1...1
// 11111
// == 11111 10000 11111 10001 11111
// == 0001 1111 1000 0111 1110 0011 1111
glyph_bits[int32_t{'6'}] = 0x1f87e3f;
// '7'
// 11111
// ....1
// ....1
// ....1
// ....1
// == 11111 00001 00001 00001 00001
// == 0001 1111 0000 1000 0100 0010 0001
glyph_bits[int32_t{'7'}] = 0x1f08421;
// '8'
// 11111
// 1...1
// 11111
// 1...1
// 11111
// == 11111 10001 11111 10001 11111
// == 0001 1111 1000 1111 1110 0011 1111
glyph_bits[int32_t{'8'}] = 0x1f8fe3f;
// '9'
// 11111
// 1...1
// 11111
// ....1
// 11111
// == 11111 10001 11111 00001 11111
// == 0001 1111 1000 1111 1100 0011 1111
glyph_bits[int32_t{'9'}] = 0x1f8fc3f;
// TODO(fxbug.dev/7297): glyphs for ASCII 0x3A - 0x40 and 0x44 - 0x7F
// ‘A’
// ..1..
// .1.1.
// 11111
// 1...1
// 1...1
// == 00100 01010 11111 10001 10001
glyph_bits[int32_t{'A'}] = 0x457E31;
// ‘B’
// 1111.
// 1...1
// 1111.
// 1...1
// 1111.
// == 11110 10001 11110 10001 11110
glyph_bits[int32_t{'B'}] = 0x1E8FA3E;
// ‘C’
// .1111
// 1....
// 1....
// 1....
// .1111
// == 11111 10000 10000 10000 11111
glyph_bits[int32_t{'C'}] = 0x1F8421F;
// ‘D’
// 1111.
// 1...1
// 1...1
// 1...1
// 1111.
// == 11110 10001 10001 10001 11110
glyph_bits[int32_t{'D'}] = 0x1E8C63E;
// ‘E’
// 11111
// 1....
// 1111.
// 1....
// 11111
// == 11111 10000 11110 10000 11111
glyph_bits[int32_t{'E'}] = 0x1F87A1F;
// ‘F’
// 11111
// 1....
// 1111.
// 1....
// 1....
// == 11111 10000 11110 10000 10000
glyph_bits[int32_t{'F'}] = 0x1F87A10;
// TODO(fxbug.dev/7297): glyphs for ASCII 0x47 - 0x48
// ‘I’
// 11111
// ..1..
// ..1..
// ..1..
// 11111
// == 11111 00100 00100 00100 11111
glyph_bits[int32_t{'I'}] = 0x1F2109F;
// TODO(fxbug.dev/7297): glyphs for ASCII 0x4A - 0x4C
// ‘M’
// .1.1.
// 11.11
// 1.1.1
// 1.1.1
// 1...1
// == 01010 11011 10101 10101 10001
glyph_bits[int32_t{'M'}] = 0xADD6B1;
// ‘N’
// 11..1
// 11..1
// 1.1.1
// 1..11
// 1..11
// == 11001 11001 10101 10011 10011
glyph_bits[int32_t{'N'}] = 0x19CD673;
// ‘O’
// .111.
// 1...1
// 1...1
// 1...1
// .111.
// == 01110 10001 10001 10001 01110
glyph_bits[int32_t{'O'}] = 0xE8C62E;
// // TODO(fxbug.dev/7297): glyphs for ASCII 0x50 - 0x51
// ‘R'
// 1111.
// 1...1
// 1111.
// 1.1..
// 1..11
// == 11110 10001 11110 10100 10011
glyph_bits[int32_t{'R'}] = 0x1E8FA93;
// ‘S’
// .1111
// 1....
// .111.
// ....1
// 1111.
// == 01111 10000 01110 00001 11110
glyph_bits[int32_t{'S'}] = 0xF8383E;
// ‘T’
// 11111
// ..1..
// ..1..
// ..1..
// ..1..
// == 11111 00100 00100 00100 00100
glyph_bits[int32_t{'T'}] = 0x1F21084;
// TODO(fxbug.dev/7297): glyphs for ASCII 0x55 - 0x7F
// Process the bits that describe each glyph, turning them into black
// and white pixels.
constexpr uint32_t kInnerWidth = kGlyphWidth - 2 * kGlyphPadding;
constexpr uint32_t kInnerHeight = kGlyphHeight - 2 * kGlyphPadding;
static_assert(kInnerWidth == 5, "unpadded glyph width must be 5.");
static_assert(kInnerHeight == 5, "unpadded glyph height must be 5.");
constexpr uint32_t kBytesPerPixel = 4;
constexpr uint32_t kBytesPerRow = kGlyphWidth * kBytesPerPixel;
constexpr uint32_t kBytesPerGlyph = kBytesPerRow * (kGlyphHeight);
auto output = std::make_unique<uint8_t[]>(kNumGlyphs * kBytesPerGlyph);
// Avoid endianness problems by always specifying in byte order.
constexpr ColorRgba kBlack(0, 0, 0, 0xff);
constexpr ColorRgba kWhite(0xff, 0xff, 0xff, 0xff);
for (size_t i = 0; i < kNumGlyphs; ++i) {
ColorRgba* glyph = reinterpret_cast<ColorRgba*>(output.get() + i * kBytesPerGlyph);
// Fill the entire glyph with white, including the padding.
// We could just fill the padding, but the performance improvement
// would be negligible (this is only done once).
for (size_t x = 0; x < kGlyphWidth; ++x) {
for (size_t y = 0; y < kGlyphHeight; ++y) {
glyph[x + y * kGlyphWidth] = kWhite;
}
}
// Fill in the pixels of the glyph itself (not the padding).
constexpr uint32_t kMaxGlyphShift = kInnerWidth * kInnerHeight - 1;
for (size_t x = 0; x < kInnerWidth; ++x) {
for (size_t y = 0; y < kInnerHeight; ++y) {
size_t shift = kMaxGlyphShift - (y * kInnerWidth + x);
ColorRgba color = ((glyph_bits[i] >> shift) & 1) ? kBlack : kWhite;
glyph[(x + kGlyphPadding) + (y + kGlyphPadding) * kGlyphWidth] = color;
}
}
}
return output;
}
} // namespace escher