mirror of
https://github.com/kovidgoyal/kitty
synced 2026-07-19 15:04:50 +02:00
Basic color emoji rendering working
Still need to downscale color bitmaps to fit into cells
This commit is contained in:
@@ -24,6 +24,7 @@ in vec3 underline_pos;
|
|||||||
in vec3 strike_pos;
|
in vec3 strike_pos;
|
||||||
in vec3 foreground;
|
in vec3 foreground;
|
||||||
in vec3 decoration_fg;
|
in vec3 decoration_fg;
|
||||||
|
in float colored_sprite;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
out vec4 final_color;
|
out vec4 final_color;
|
||||||
@@ -51,13 +52,15 @@ vec4 alpha_blend_premul(vec3 over, float over_alpha, vec3 under, float under_alp
|
|||||||
|
|
||||||
#ifdef NEEDS_FOREGROUND
|
#ifdef NEEDS_FOREGROUND
|
||||||
vec4 calculate_foreground() {
|
vec4 calculate_foreground() {
|
||||||
float text_alpha = texture(sprites, sprite_pos).r;
|
vec4 text_fg = texture(sprites, sprite_pos);
|
||||||
float underline_alpha = texture(sprites, underline_pos).r;
|
vec3 fg = mix(foreground, text_fg.rgb, colored_sprite);
|
||||||
float strike_alpha = texture(sprites, strike_pos).r;
|
float text_alpha = text_fg.a;
|
||||||
|
float underline_alpha = texture(sprites, underline_pos).a;
|
||||||
|
float strike_alpha = texture(sprites, strike_pos).a;
|
||||||
// Since strike and text are the same color, we simply add the alpha values
|
// Since strike and text are the same color, we simply add the alpha values
|
||||||
float combined_alpha = min(text_alpha + strike_alpha, 1.0f);
|
float combined_alpha = min(text_alpha + strike_alpha, 1.0f);
|
||||||
// Underline color might be different, so alpha blend
|
// Underline color might be different, so alpha blend
|
||||||
return alpha_blend(decoration_fg, underline_alpha, foreground, combined_alpha);
|
return alpha_blend(decoration_fg, underline_alpha, fg, combined_alpha);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|||||||
@@ -52,12 +52,14 @@ out vec3 underline_pos;
|
|||||||
out vec3 strike_pos;
|
out vec3 strike_pos;
|
||||||
out vec3 foreground;
|
out vec3 foreground;
|
||||||
out vec3 decoration_fg;
|
out vec3 decoration_fg;
|
||||||
|
out float colored_sprite;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
// Utility functions {{{
|
// Utility functions {{{
|
||||||
const uint BYTE_MASK = uint(0xFF);
|
const uint BYTE_MASK = uint(0xFF);
|
||||||
const uint SHORT_MASK = uint(0xFFFF);
|
const uint Z_MASK = uint(0xFFF);
|
||||||
|
const uint COLOR_MASK = uint(0x4000);
|
||||||
const uint ZERO = uint(0);
|
const uint ZERO = uint(0);
|
||||||
const uint ONE = uint(1);
|
const uint ONE = uint(1);
|
||||||
const uint TWO = uint(2);
|
const uint TWO = uint(2);
|
||||||
@@ -150,7 +152,8 @@ void main() {
|
|||||||
#ifdef NEEDS_FOREGROUND
|
#ifdef NEEDS_FOREGROUND
|
||||||
|
|
||||||
// The character sprite being rendered
|
// The character sprite being rendered
|
||||||
sprite_pos = to_sprite_pos(pos, sprite_coords.x, sprite_coords.y, sprite_coords.z & SHORT_MASK);
|
sprite_pos = to_sprite_pos(pos, sprite_coords.x, sprite_coords.y, sprite_coords.z & Z_MASK);
|
||||||
|
colored_sprite = float((sprite_coords.z & COLOR_MASK) >> 14);
|
||||||
|
|
||||||
// Foreground
|
// Foreground
|
||||||
uint resolved_fg = resolve_color(colors[fg_index], default_colors[fg_index]);
|
uint resolved_fg = resolve_color(colors[fg_index], default_colors[fg_index]);
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
|
|
||||||
#include "fonts.h"
|
#include "fonts.h"
|
||||||
#include "state.h"
|
#include "state.h"
|
||||||
|
#include "emoji.h"
|
||||||
|
|
||||||
#define MISSING_GLYPH 4
|
#define MISSING_GLYPH 4
|
||||||
#define MAX_NUM_EXTRA_GLYPHS 8
|
#define MAX_NUM_EXTRA_GLYPHS 8
|
||||||
@@ -17,7 +18,6 @@ send_sprite_to_gpu_func current_send_sprite_to_gpu = NULL;
|
|||||||
static PyObject *python_send_to_gpu_impl = NULL;
|
static PyObject *python_send_to_gpu_impl = NULL;
|
||||||
extern PyTypeObject Line_Type;
|
extern PyTypeObject Line_Type;
|
||||||
|
|
||||||
typedef struct SpritePosition SpritePosition;
|
|
||||||
typedef struct SpecialGlyphCache SpecialGlyphCache;
|
typedef struct SpecialGlyphCache SpecialGlyphCache;
|
||||||
enum {NO_FONT=-3, MISSING_FONT=-2, BLANK_FONT=-1, BOX_FONT=0};
|
enum {NO_FONT=-3, MISSING_FONT=-2, BLANK_FONT=-1, BOX_FONT=0};
|
||||||
|
|
||||||
@@ -26,15 +26,17 @@ typedef struct {
|
|||||||
glyph_index data[MAX_NUM_EXTRA_GLYPHS];
|
glyph_index data[MAX_NUM_EXTRA_GLYPHS];
|
||||||
} ExtraGlyphs;
|
} ExtraGlyphs;
|
||||||
|
|
||||||
|
typedef struct SpritePosition SpritePosition;
|
||||||
struct SpritePosition {
|
struct SpritePosition {
|
||||||
SpritePosition *next;
|
SpritePosition *next;
|
||||||
bool filled, rendered;
|
bool filled, rendered, colored;
|
||||||
sprite_index x, y, z;
|
sprite_index x, y, z;
|
||||||
uint8_t ligature_index;
|
uint8_t ligature_index;
|
||||||
glyph_index glyph;
|
glyph_index glyph;
|
||||||
ExtraGlyphs extra_glyphs;
|
ExtraGlyphs extra_glyphs;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
struct SpecialGlyphCache {
|
struct SpecialGlyphCache {
|
||||||
SpecialGlyphCache *next;
|
SpecialGlyphCache *next;
|
||||||
glyph_index glyph;
|
glyph_index glyph;
|
||||||
@@ -93,7 +95,7 @@ sprite_map_set_error(int error) {
|
|||||||
void
|
void
|
||||||
sprite_tracker_set_limits(size_t max_texture_size, size_t max_array_len) {
|
sprite_tracker_set_limits(size_t max_texture_size, size_t max_array_len) {
|
||||||
sprite_tracker.max_texture_size = max_texture_size;
|
sprite_tracker.max_texture_size = max_texture_size;
|
||||||
sprite_tracker.max_array_len = max_array_len;
|
sprite_tracker.max_array_len = MIN(0xfff, max_array_len);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void
|
static inline void
|
||||||
@@ -143,6 +145,7 @@ sprite_position_for(Font *font, glyph_index glyph, ExtraGlyphs *extra_glyphs, ui
|
|||||||
s->ligature_index = ligature_index;
|
s->ligature_index = ligature_index;
|
||||||
s->filled = true;
|
s->filled = true;
|
||||||
s->rendered = false;
|
s->rendered = false;
|
||||||
|
s->colored = false;
|
||||||
s->x = sprite_tracker.x; s->y = sprite_tracker.y; s->z = sprite_tracker.z;
|
s->x = sprite_tracker.x; s->y = sprite_tracker.y; s->z = sprite_tracker.z;
|
||||||
do_increment(error);
|
do_increment(error);
|
||||||
return s;
|
return s;
|
||||||
@@ -195,7 +198,7 @@ free_maps(Font *font) {
|
|||||||
|
|
||||||
void
|
void
|
||||||
clear_sprite_map(Font *font) {
|
clear_sprite_map(Font *font) {
|
||||||
#define CLEAR(s) s->filled = false; s->rendered = false; s->glyph = 0; memset(&s->extra_glyphs, 0, sizeof(ExtraGlyphs)); s->x = 0; s->y = 0; s->z = 0; s->ligature_index = 0;
|
#define CLEAR(s) s->filled = false; s->rendered = false; s->colored = false; s->glyph = 0; memset(&s->extra_glyphs, 0, sizeof(ExtraGlyphs)); s->x = 0; s->y = 0; s->z = 0; s->ligature_index = 0;
|
||||||
SpritePosition *s;
|
SpritePosition *s;
|
||||||
for (size_t i = 0; i < sizeof(font->sprite_map)/sizeof(font->sprite_map[0]); i++) {
|
for (size_t i = 0; i < sizeof(font->sprite_map)/sizeof(font->sprite_map[0]); i++) {
|
||||||
s = font->sprite_map + i;
|
s = font->sprite_map + i;
|
||||||
@@ -437,9 +440,7 @@ render_alpha_mask(uint8_t *alpha_mask, pixel* dest, Region *src_rect, Region *de
|
|||||||
for(size_t sc = src_rect->left, dc = dest_rect->left; sc < src_rect->right && dc < dest_rect->right; sc++, dc++) {
|
for(size_t sc = src_rect->left, dc = dest_rect->left; sc < src_rect->right && dc < dest_rect->right; sc++, dc++) {
|
||||||
pixel val = d[dc];
|
pixel val = d[dc];
|
||||||
uint8_t alpha = s[sc];
|
uint8_t alpha = s[sc];
|
||||||
#define C(shift) ((MIN(0xff, alpha + ((val >> shift) & 0xff))) << shift)
|
d[dc] = 0xffffff00 | MIN(0xff, alpha + (val & 0xff));
|
||||||
d[dc] = C(24) | C(16) | C(8) | MIN(0xff, alpha + (val & 0xff));
|
|
||||||
#undef C
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -458,6 +459,7 @@ render_box_cell(Cell *cell) {
|
|||||||
set_sprite(cell, sp->x, sp->y, sp->z);
|
set_sprite(cell, sp->x, sp->y, sp->z);
|
||||||
if (sp->rendered) return;
|
if (sp->rendered) return;
|
||||||
sp->rendered = true;
|
sp->rendered = true;
|
||||||
|
sp->colored = false;
|
||||||
PyObject *ret = PyObject_CallFunction(box_drawing_function, "I", cell->ch);
|
PyObject *ret = PyObject_CallFunction(box_drawing_function, "I", cell->ch);
|
||||||
if (ret == NULL) { PyErr_Print(); return; }
|
if (ret == NULL) { PyErr_Print(); return; }
|
||||||
uint8_t *alpha_mask = PyLong_AsVoidPtr(PyTuple_GET_ITEM(ret, 0));
|
uint8_t *alpha_mask = PyLong_AsVoidPtr(PyTuple_GET_ITEM(ret, 0));
|
||||||
@@ -493,6 +495,7 @@ load_hb_buffer(Cell *first_cell, index_type num_cells) {
|
|||||||
static inline void
|
static inline void
|
||||||
set_cell_sprite(Cell *cell, SpritePosition *sp) {
|
set_cell_sprite(Cell *cell, SpritePosition *sp) {
|
||||||
cell->sprite_x = sp->x; cell->sprite_y = sp->y; cell->sprite_z = sp->z;
|
cell->sprite_x = sp->x; cell->sprite_y = sp->y; cell->sprite_z = sp->z;
|
||||||
|
if (sp->colored) cell->sprite_z |= 0x4000;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline pixel*
|
static inline pixel*
|
||||||
@@ -518,10 +521,12 @@ render_group(unsigned int num_cells, unsigned int num_glyphs, Cell *cells, hb_gl
|
|||||||
}
|
}
|
||||||
|
|
||||||
clear_canvas();
|
clear_canvas();
|
||||||
render_glyphs_in_cells(font->face, font->bold, font->italic, info, positions, num_glyphs, canvas, cell_width, cell_height, num_cells, baseline);
|
bool was_colored = is_emoji(cells->ch);
|
||||||
|
render_glyphs_in_cells(font->face, font->bold, font->italic, info, positions, num_glyphs, canvas, cell_width, cell_height, num_cells, baseline, &was_colored);
|
||||||
|
|
||||||
for (unsigned int i = 0; i < num_cells; i++) {
|
for (unsigned int i = 0; i < num_cells; i++) {
|
||||||
sprite_position[i]->rendered = true;
|
sprite_position[i]->rendered = true;
|
||||||
|
sprite_position[i]->colored = was_colored;
|
||||||
set_cell_sprite(cells + i, sprite_position[i]);
|
set_cell_sprite(cells + i, sprite_position[i]);
|
||||||
pixel *buf = num_cells == 1 ? canvas : extract_cell_from_canvas(i, num_cells);
|
pixel *buf = num_cells == 1 ? canvas : extract_cell_from_canvas(i, num_cells);
|
||||||
current_send_sprite_to_gpu(sprite_position[i]->x, sprite_position[i]->y, sprite_position[i]->z, buf);
|
current_send_sprite_to_gpu(sprite_position[i]->x, sprite_position[i]->y, sprite_position[i]->z, buf);
|
||||||
@@ -882,13 +887,6 @@ concat_cells(PyObject UNUSED *self, PyObject *args) {
|
|||||||
rgba[1] = (src[i] >> 16) & 0xff;
|
rgba[1] = (src[i] >> 16) & 0xff;
|
||||||
rgba[2] = (src[i] >> 8) & 0xff;
|
rgba[2] = (src[i] >> 8) & 0xff;
|
||||||
rgba[3] = src[i] & 0xff;
|
rgba[3] = src[i] & 0xff;
|
||||||
// pre-multiplied, so de-multiply
|
|
||||||
if (rgba[3]) {
|
|
||||||
float f = 255.f / (float)rgba[3];
|
|
||||||
rgba[0] = MAX(255.f, ((float)rgba[0] * f));
|
|
||||||
rgba[1] = MAX(255.f, ((float)rgba[1] * f));
|
|
||||||
rgba[2] = MAX(255.f, ((float)rgba[2] * f));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
uint8_t *src = (uint8_t*)s + cell_width * r;
|
uint8_t *src = (uint8_t*)s + cell_width * r;
|
||||||
|
|||||||
@@ -13,13 +13,12 @@
|
|||||||
#pragma GCC diagnostic pop
|
#pragma GCC diagnostic pop
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
unsigned int glyph_id_for_codepoint(PyObject *, char_type);
|
unsigned int glyph_id_for_codepoint(PyObject *, char_type);
|
||||||
hb_font_t* harfbuzz_font_for_face(PyObject*);
|
hb_font_t* harfbuzz_font_for_face(PyObject*);
|
||||||
bool set_size_for_face(PyObject*, unsigned int, bool);
|
bool set_size_for_face(PyObject*, unsigned int, bool);
|
||||||
void cell_metrics(PyObject*, unsigned int*, unsigned int*, unsigned int*, unsigned int*, unsigned int*);
|
void cell_metrics(PyObject*, unsigned int*, unsigned int*, unsigned int*, unsigned int*, unsigned int*);
|
||||||
void sprite_tracker_current_layout(unsigned int *x, unsigned int *y, unsigned int *z);
|
void sprite_tracker_current_layout(unsigned int *x, unsigned int *y, unsigned int *z);
|
||||||
bool render_glyphs_in_cells(PyObject *f, bool bold, bool italic, hb_glyph_info_t *info, hb_glyph_position_t *positions, unsigned int num_glyphs, pixel *canvas, unsigned int cell_width, unsigned int cell_height, unsigned int num_cells, unsigned int baseline);
|
bool render_glyphs_in_cells(PyObject *f, bool bold, bool italic, hb_glyph_info_t *info, hb_glyph_position_t *positions, unsigned int num_glyphs, pixel *canvas, unsigned int cell_width, unsigned int cell_height, unsigned int num_cells, unsigned int baseline, bool *was_colored);
|
||||||
void render_alpha_mask(uint8_t *alpha_mask, pixel* dest, Region *src_rect, Region *dest_rect, size_t src_stride, size_t dest_stride);
|
void render_alpha_mask(uint8_t *alpha_mask, pixel* dest, Region *src_rect, Region *dest_rect, size_t src_stride, size_t dest_stride);
|
||||||
void render_line(Line *line);
|
void render_line(Line *line);
|
||||||
void sprite_tracker_set_limits(size_t max_texture_size, size_t max_array_len);
|
void sprite_tracker_set_limits(size_t max_texture_size, size_t max_array_len);
|
||||||
|
|||||||
@@ -11,8 +11,9 @@ from kitty.config import defaults
|
|||||||
from kitty.constants import is_macos
|
from kitty.constants import is_macos
|
||||||
from kitty.fast_data_types import (
|
from kitty.fast_data_types import (
|
||||||
Screen, change_wcwidth, get_fallback_font, send_prerendered_sprites,
|
Screen, change_wcwidth, get_fallback_font, send_prerendered_sprites,
|
||||||
set_font, set_font_size, set_logical_dpi, set_send_sprite_to_gpu,
|
set_font, set_font_size, set_logical_dpi, set_options,
|
||||||
sprite_map_set_limits, test_render_line, test_shape
|
set_send_sprite_to_gpu, sprite_map_set_limits, test_render_line,
|
||||||
|
test_shape
|
||||||
)
|
)
|
||||||
from kitty.fonts.box_drawing import render_box_char, render_missing_glyph
|
from kitty.fonts.box_drawing import render_box_char, render_missing_glyph
|
||||||
|
|
||||||
@@ -169,6 +170,7 @@ def render_box_drawing(codepoint):
|
|||||||
def setup_for_testing(family='monospace', size=11.0, dpi=96.0):
|
def setup_for_testing(family='monospace', size=11.0, dpi=96.0):
|
||||||
from kitty.utils import get_logical_dpi
|
from kitty.utils import get_logical_dpi
|
||||||
opts = defaults._replace(font_family=family)
|
opts = defaults._replace(font_family=family)
|
||||||
|
set_options(opts)
|
||||||
sprites = {}
|
sprites = {}
|
||||||
|
|
||||||
def send_to_gpu(x, y, z, data):
|
def send_to_gpu(x, y, z, data):
|
||||||
@@ -195,7 +197,9 @@ def render_string(text, family='monospace', size=11.0, dpi=96.0):
|
|||||||
cells = []
|
cells = []
|
||||||
found_content = False
|
found_content = False
|
||||||
for i in reversed(range(s.columns)):
|
for i in reversed(range(s.columns)):
|
||||||
sp = line.sprite_at(i)
|
sp = list(line.sprite_at(i))
|
||||||
|
sp[2] &= 0xfff
|
||||||
|
sp = tuple(sp)
|
||||||
if sp == (0, 0, 0) and not found_content:
|
if sp == (0, 0, 0) and not found_content:
|
||||||
continue
|
continue
|
||||||
found_content = True
|
found_content = True
|
||||||
@@ -260,5 +264,5 @@ def showcase():
|
|||||||
f = 'monospace' if is_macos else 'Liberation Mono'
|
f = 'monospace' if is_macos else 'Liberation Mono'
|
||||||
test_render_string('He\u0347\u0305llo\u0337, w\u0302or\u0306l\u0354d!', family=f)
|
test_render_string('He\u0347\u0305llo\u0337, w\u0302or\u0306l\u0354d!', family=f)
|
||||||
test_render_string('你好,世界', family=f)
|
test_render_string('你好,世界', family=f)
|
||||||
test_render_string('|\U0001F601|\U0001F64f|\U0001F63a|', family=f)
|
test_render_string('|😁|🙏|😺|', family=f)
|
||||||
test_render_string('A=>>B!=C', family='Fira Code')
|
test_render_string('A=>>B!=C', family='Fira Code')
|
||||||
|
|||||||
@@ -117,7 +117,7 @@ set_font_size(Face *self, FT_F26Dot6 char_width, FT_F26Dot6 char_height, FT_UInt
|
|||||||
}
|
}
|
||||||
if (strike_index > -1) {
|
if (strike_index > -1) {
|
||||||
error = FT_Select_Size(self->face, strike_index);
|
error = FT_Select_Size(self->face, strike_index);
|
||||||
if (error) { set_freetype_error("Failed to set char size for non-scaleable font, with error:", error); return false; }
|
if (error) { set_freetype_error("Failed to set char size for non-scalable font, with error:", error); return false; }
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -316,6 +316,7 @@ typedef struct {
|
|||||||
unsigned char* buf;
|
unsigned char* buf;
|
||||||
size_t start_x, width, stride;
|
size_t start_x, width, stride;
|
||||||
size_t rows;
|
size_t rows;
|
||||||
|
FT_Pixel_Mode pixel_mode;
|
||||||
} ProcessedBitmap;
|
} ProcessedBitmap;
|
||||||
|
|
||||||
|
|
||||||
@@ -336,7 +337,6 @@ trim_borders(ProcessedBitmap *ans, size_t extra) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
static inline bool
|
static inline bool
|
||||||
render_bitmap(Face *self, int glyph_id, ProcessedBitmap *ans, unsigned int cell_width, unsigned int num_cells, bool bold, bool italic, bool rescale) {
|
render_bitmap(Face *self, int glyph_id, ProcessedBitmap *ans, unsigned int cell_width, unsigned int num_cells, bool bold, bool italic, bool rescale) {
|
||||||
if (!load_glyph(self, glyph_id, FT_LOAD_RENDER)) return false;
|
if (!load_glyph(self, glyph_id, FT_LOAD_RENDER)) return false;
|
||||||
@@ -346,6 +346,7 @@ render_bitmap(Face *self, int glyph_id, ProcessedBitmap *ans, unsigned int cell_
|
|||||||
ans->start_x = 0; ans->width = bitmap->width;
|
ans->start_x = 0; ans->width = bitmap->width;
|
||||||
ans->stride = bitmap->pitch < 0 ? -bitmap->pitch : bitmap->pitch;
|
ans->stride = bitmap->pitch < 0 ? -bitmap->pitch : bitmap->pitch;
|
||||||
ans->rows = bitmap->rows;
|
ans->rows = bitmap->rows;
|
||||||
|
ans->pixel_mode = bitmap->pixel_mode;
|
||||||
if (ans->width > max_width) {
|
if (ans->width > max_width) {
|
||||||
size_t extra = bitmap->width - max_width;
|
size_t extra = bitmap->width - max_width;
|
||||||
if (italic && extra < cell_width / 2) {
|
if (italic && extra < cell_width / 2) {
|
||||||
@@ -362,6 +363,49 @@ render_bitmap(Face *self, int glyph_id, ProcessedBitmap *ans, unsigned int cell_
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static inline bool
|
||||||
|
render_color_bitmap(Face *self, int glyph_id, ProcessedBitmap *ans, unsigned int cell_width, unsigned int num_cells, bool bold, bool italic) {
|
||||||
|
unsigned short best = 0, diff = USHRT_MAX;
|
||||||
|
for (short i = 0; i < self->face->num_fixed_sizes; i++) {
|
||||||
|
unsigned short w = self->face->available_sizes[i].width;
|
||||||
|
unsigned short d = w > (unsigned short)cell_width ? w - (unsigned short)cell_width : (unsigned short)cell_width - w;
|
||||||
|
if (d < diff) {
|
||||||
|
diff = d;
|
||||||
|
best = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
FT_Error error = FT_Select_Size(self->face, best);
|
||||||
|
if (error) { set_freetype_error("Failed to set char size for non-scalable font, with error:", error); return false; }
|
||||||
|
if (!load_glyph(self, glyph_id, FT_LOAD_COLOR)) return false;
|
||||||
|
FT_Bitmap *bitmap = &self->face->glyph->bitmap;
|
||||||
|
if (bitmap->pixel_mode != FT_PIXEL_MODE_BGRA) return render_bitmap(self, glyph_id, ans, cell_width, num_cells, bold, italic, true);
|
||||||
|
ans->buf = bitmap->buffer;
|
||||||
|
ans->start_x = 0; ans->width = bitmap->width;
|
||||||
|
ans->stride = bitmap->pitch < 0 ? -bitmap->pitch : bitmap->pitch;
|
||||||
|
ans->rows = bitmap->rows;
|
||||||
|
ans->pixel_mode = bitmap->pixel_mode;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
copy_color_bitmap(uint8_t *src, pixel* dest, Region *src_rect, Region *dest_rect, size_t src_stride, size_t dest_stride) {
|
||||||
|
for (size_t sr = src_rect->top, dr = dest_rect->top; sr < src_rect->bottom && dr < dest_rect->bottom; sr++, dr++) {
|
||||||
|
pixel *d = dest + dest_stride * dr;
|
||||||
|
uint8_t *s = src + src_stride * sr;
|
||||||
|
for(size_t sc = src_rect->left, dc = dest_rect->left; sc < src_rect->right && dc < dest_rect->right; sc++, dc++) {
|
||||||
|
uint8_t *bgra = s + 4 * sc;
|
||||||
|
// TODO: Convert from sRGB to linear color space?
|
||||||
|
if (bgra[3]) {
|
||||||
|
#define C(idx, shift) ( (uint8_t)(((float)bgra[idx] / (float)bgra[3]) * 255) << shift)
|
||||||
|
d[dc] = C(2, 24) | C(1, 16) | C(0, 8) | bgra[3];
|
||||||
|
#undef C
|
||||||
|
} else d[dc] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static inline void
|
static inline void
|
||||||
place_bitmap_in_canvas(pixel *cell, ProcessedBitmap *bm, size_t cell_width, size_t cell_height, float x_offset, float y_offset, FT_Glyph_Metrics *metrics, size_t baseline) {
|
place_bitmap_in_canvas(pixel *cell, ProcessedBitmap *bm, size_t cell_width, size_t cell_height, float x_offset, float y_offset, FT_Glyph_Metrics *metrics, size_t baseline) {
|
||||||
// We want the glyph to be positioned inside the cell based on the bearingX
|
// We want the glyph to be positioned inside the cell based on the bearingX
|
||||||
@@ -392,7 +436,9 @@ place_bitmap_in_canvas(pixel *cell, ProcessedBitmap *bm, size_t cell_width, size
|
|||||||
|
|
||||||
/* printf("x_offset: %f bearing_x: %f y_offset: %f bearing_y: %f src_start_row: %zu src_start_column: %zu dest_start_row: %zu dest_start_column: %zu bm_width: %lu bitmap_rows: %lu\n", x_offset, bearing_x, y_offset, bearing_y, src_start_row, src_start_column, dest_start_row, dest_start_column, bm->width, bm->rows); */
|
/* printf("x_offset: %f bearing_x: %f y_offset: %f bearing_y: %f src_start_row: %zu src_start_column: %zu dest_start_row: %zu dest_start_column: %zu bm_width: %lu bitmap_rows: %lu\n", x_offset, bearing_x, y_offset, bearing_y, src_start_row, src_start_column, dest_start_row, dest_start_column, bm->width, bm->rows); */
|
||||||
|
|
||||||
render_alpha_mask(bm->buf, cell, &src, &dest, bm->stride, cell_width);
|
if (bm->pixel_mode == FT_PIXEL_MODE_BGRA) {
|
||||||
|
copy_color_bitmap(bm->buf, cell, &src, &dest, bm->stride, cell_width);
|
||||||
|
} else render_alpha_mask(bm->buf, cell, &src, &dest, bm->stride, cell_width);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void
|
static inline void
|
||||||
@@ -406,13 +452,18 @@ right_shift_canvas(pixel *canvas, size_t width, size_t height, size_t amt) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
render_glyphs_in_cells(PyObject *f, bool bold, bool italic, hb_glyph_info_t *info, hb_glyph_position_t *positions, unsigned int num_glyphs, pixel *canvas, unsigned int cell_width, unsigned int cell_height, unsigned int num_cells, unsigned int baseline) {
|
render_glyphs_in_cells(PyObject *f, bool bold, bool italic, hb_glyph_info_t *info, hb_glyph_position_t *positions, unsigned int num_glyphs, pixel *canvas, unsigned int cell_width, unsigned int cell_height, unsigned int num_cells, unsigned int baseline, bool *was_colored) {
|
||||||
Face *self = (Face*)f;
|
Face *self = (Face*)f;
|
||||||
|
bool is_emoji = *was_colored; *was_colored = is_emoji && self->face->num_fixed_sizes > 0 && self->has_color;
|
||||||
float x = 0.f, y = 0.f, x_offset = 0.f;
|
float x = 0.f, y = 0.f, x_offset = 0.f;
|
||||||
ProcessedBitmap bm;
|
ProcessedBitmap bm;
|
||||||
unsigned int canvas_width = cell_width * num_cells;
|
unsigned int canvas_width = cell_width * num_cells;
|
||||||
for (unsigned int i = 0; i < num_glyphs; i++) {
|
for (unsigned int i = 0; i < num_glyphs; i++) {
|
||||||
if (!render_bitmap(self, info[i].codepoint, &bm, cell_width, num_cells, bold, italic, true)) return false;
|
if (*was_colored) {
|
||||||
|
if (!render_color_bitmap(self, info[i].codepoint, &bm, cell_width, num_cells, bold, italic)) return false;
|
||||||
|
} else {
|
||||||
|
if (!render_bitmap(self, info[i].codepoint, &bm, cell_width, num_cells, bold, italic, true)) return false;
|
||||||
|
}
|
||||||
x_offset = x + (float)positions[i].x_offset / 64.0f;
|
x_offset = x + (float)positions[i].x_offset / 64.0f;
|
||||||
y = (float)positions[i].y_offset / 64.0f;
|
y = (float)positions[i].y_offset / 64.0f;
|
||||||
if (self->face->glyph->metrics.width > 0 && bm.width > 0) place_bitmap_in_canvas(canvas, &bm, canvas_width, cell_height, x_offset, y, &self->face->glyph->metrics, baseline);
|
if (self->face->glyph->metrics.width > 0 && bm.width > 0) place_bitmap_in_canvas(canvas, &bm, canvas_width, cell_height, x_offset, y, &self->face->glyph->metrics, baseline);
|
||||||
|
|||||||
@@ -297,8 +297,8 @@ PYWRAP1(handle_for_window_id) {
|
|||||||
|
|
||||||
PYWRAP1(set_options) {
|
PYWRAP1(set_options) {
|
||||||
PyObject *ret, *opts;
|
PyObject *ret, *opts;
|
||||||
int is_wayland, debug_gl = 0;
|
int is_wayland = 0, debug_gl = 0;
|
||||||
PA("Op|p", &opts, &is_wayland, &debug_gl);
|
PA("O|pp", &opts, &is_wayland, &debug_gl);
|
||||||
global_state.is_wayland = is_wayland ? true : false;
|
global_state.is_wayland = is_wayland ? true : false;
|
||||||
global_state.debug_gl = debug_gl ? true : false;
|
global_state.debug_gl = debug_gl ? true : false;
|
||||||
#define GA(name) ret = PyObject_GetAttrString(opts, #name); if (ret == NULL) return NULL;
|
#define GA(name) ret = PyObject_GetAttrString(opts, #name); if (ret == NULL) return NULL;
|
||||||
|
|||||||
Reference in New Issue
Block a user