1 | /* Pango |
2 | * pangoft2-render.c: Rendering routines to FT_Bitmap objects |
3 | * |
4 | * Copyright (C) 2004 Red Hat Software |
5 | * Copyright (C) 2000 Tor Lillqvist |
6 | * |
7 | * This library is free software; you can redistribute it and/or |
8 | * modify it under the terms of the GNU Library General Public |
9 | * License as published by the Free Software Foundation; either |
10 | * version 2 of the License, or (at your option) any later version. |
11 | * |
12 | * This library is distributed in the hope that it will be useful, |
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
15 | * Library General Public License for more details. |
16 | * |
17 | * You should have received a copy of the GNU Library General Public |
18 | * License along with this library; if not, write to the |
19 | * Free Software Foundation, Inc., 59 Temple Place - Suite 330, |
20 | * Boston, MA 02111-1307, USA. |
21 | */ |
22 | |
23 | #include "config.h" |
24 | #include <math.h> |
25 | |
26 | #include "pango-font-private.h" |
27 | #include "pangoft2-private.h" |
28 | #include "pango-impl-utils.h" |
29 | |
30 | /* for compatibility with older freetype versions */ |
31 | #ifndef FT_LOAD_TARGET_MONO |
32 | #define FT_LOAD_TARGET_MONO FT_LOAD_MONOCHROME |
33 | #endif |
34 | |
35 | typedef struct _PangoFT2RendererClass PangoFT2RendererClass; |
36 | |
37 | #define PANGO_FT2_RENDERER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), PANGO_TYPE_FT2_RENDERER, PangoFT2RendererClass)) |
38 | #define PANGO_IS_FT2_RENDERER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), PANGO_TYPE_FT2_RENDERER)) |
39 | #define PANGO_FT2_RENDERER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), PANGO_TYPE_FT2_RENDERER, PangoFT2RendererClass)) |
40 | |
41 | struct _PangoFT2Renderer |
42 | { |
43 | PangoRenderer parent_instance; |
44 | |
45 | FT_Bitmap *bitmap; |
46 | }; |
47 | |
48 | struct _PangoFT2RendererClass |
49 | { |
50 | PangoRendererClass parent_class; |
51 | }; |
52 | |
53 | static void pango_ft2_renderer_draw_glyph (PangoRenderer *renderer, |
54 | PangoFont *font, |
55 | PangoGlyph glyph, |
56 | double x, |
57 | double y); |
58 | static void pango_ft2_renderer_draw_trapezoid (PangoRenderer *renderer, |
59 | PangoRenderPart part, |
60 | double y1, |
61 | double x11, |
62 | double x21, |
63 | double y2, |
64 | double x12, |
65 | double x22); |
66 | |
67 | G_DEFINE_TYPE (PangoFT2Renderer, pango_ft2_renderer, PANGO_TYPE_RENDERER) |
68 | |
69 | static void |
70 | pango_ft2_renderer_init (PangoFT2Renderer *renderer G_GNUC_UNUSED) |
71 | { |
72 | } |
73 | |
74 | static void |
75 | pango_ft2_renderer_class_init (PangoFT2RendererClass *klass) |
76 | { |
77 | PangoRendererClass *renderer_class = PANGO_RENDERER_CLASS (klass); |
78 | |
79 | renderer_class->draw_glyph = pango_ft2_renderer_draw_glyph; |
80 | renderer_class->draw_trapezoid = pango_ft2_renderer_draw_trapezoid; |
81 | } |
82 | |
83 | static void |
84 | pango_ft2_renderer_set_bitmap (PangoFT2Renderer *renderer, |
85 | FT_Bitmap *bitmap) |
86 | { |
87 | renderer->bitmap = bitmap; |
88 | } |
89 | |
90 | typedef struct |
91 | { |
92 | FT_Bitmap bitmap; |
93 | int bitmap_left; |
94 | int bitmap_top; |
95 | } PangoFT2RenderedGlyph; |
96 | |
97 | static void |
98 | pango_ft2_free_rendered_glyph (PangoFT2RenderedGlyph *rendered) |
99 | { |
100 | g_free (mem: rendered->bitmap.buffer); |
101 | g_slice_free (PangoFT2RenderedGlyph, rendered); |
102 | } |
103 | |
104 | static PangoFT2RenderedGlyph * |
105 | pango_ft2_font_render_box_glyph (int width, |
106 | int height, |
107 | int top, |
108 | gboolean invalid) |
109 | { |
110 | PangoFT2RenderedGlyph *box; |
111 | int i, j, offset1, offset2, line_width; |
112 | |
113 | line_width = MAX ((height + 43) / 44, 1); |
114 | if (width < 1 || height < 1) |
115 | line_width = 0; |
116 | |
117 | box = g_slice_new (PangoFT2RenderedGlyph); |
118 | |
119 | box->bitmap_left = 0; |
120 | box->bitmap_top = top; |
121 | |
122 | box->bitmap.pixel_mode = ft_pixel_mode_grays; |
123 | |
124 | box->bitmap.width = width; |
125 | box->bitmap.rows = height; |
126 | box->bitmap.pitch = width; |
127 | |
128 | box->bitmap.buffer = g_malloc0_n (n_blocks: box->bitmap.rows, n_block_bytes: box->bitmap.pitch); |
129 | |
130 | if (G_UNLIKELY (!box->bitmap.buffer)) { |
131 | g_slice_free (PangoFT2RenderedGlyph, box); |
132 | return NULL; |
133 | } |
134 | |
135 | /* draw the box */ |
136 | for (j = 0; j < line_width; j++) |
137 | { |
138 | offset1 = box->bitmap.pitch * (MIN (1 + j, height - 1)); |
139 | offset2 = box->bitmap.pitch * (MAX (box->bitmap.rows - 2 - j, 0)); |
140 | for (i = 1; |
141 | i < (int) box->bitmap.width - 1; |
142 | i++) |
143 | { |
144 | box->bitmap.buffer[offset1 + i] = 0xff; |
145 | box->bitmap.buffer[offset2 + i] = 0xff; |
146 | } |
147 | } |
148 | for (j = 0; j < line_width; j++) |
149 | { |
150 | offset1 = MIN (1 + j, width - 1); |
151 | offset2 = MAX ((int) box->bitmap.width - 2 - j, 0); |
152 | for (i = box->bitmap.pitch; |
153 | i < (int) (box->bitmap.rows - 1) * box->bitmap.pitch; |
154 | i += box->bitmap.pitch) |
155 | { |
156 | box->bitmap.buffer[offset1 + i] = 0xff; |
157 | box->bitmap.buffer[offset2 + i] = 0xff; |
158 | } |
159 | } |
160 | |
161 | if (invalid) |
162 | { |
163 | /* XXX This may scrabble memory. Didn't check close enough */ |
164 | int inc = PANGO_SCALE * MAX (width - line_width, 0) / (height + 1); |
165 | offset1 = PANGO_SCALE; |
166 | offset2 = PANGO_SCALE * MAX (width - line_width - 1, 0) ; |
167 | for (i = box->bitmap.pitch; |
168 | i < (int) (box->bitmap.rows - 1) * box->bitmap.pitch; |
169 | i += box->bitmap.pitch) |
170 | { |
171 | for (j = 0; j < line_width; j++) |
172 | { |
173 | box->bitmap.buffer[PANGO_PIXELS (offset1) + i + j] = 0xff; |
174 | box->bitmap.buffer[PANGO_PIXELS (offset2) + i + j] = 0xff; |
175 | } |
176 | offset1 += inc; |
177 | offset2 -= inc; |
178 | } |
179 | |
180 | } |
181 | |
182 | return box; |
183 | } |
184 | |
185 | static PangoFT2RenderedGlyph * |
186 | pango_ft2_font_render_glyph (PangoFont *font, |
187 | PangoGlyph glyph_index) |
188 | { |
189 | FT_Face face; |
190 | gboolean invalid_input; |
191 | |
192 | invalid_input = glyph_index == PANGO_GLYPH_INVALID_INPUT || (glyph_index & ~PANGO_GLYPH_UNKNOWN_FLAG) > 0x10FFFF; |
193 | |
194 | if (glyph_index & PANGO_GLYPH_UNKNOWN_FLAG) |
195 | { |
196 | PangoFT2RenderedGlyph *box; |
197 | PangoFontMetrics *metrics; |
198 | |
199 | if (!font) |
200 | goto generic_box; |
201 | |
202 | metrics = pango_font_get_metrics (font, NULL); |
203 | if (!metrics) |
204 | goto generic_box; |
205 | |
206 | box = pango_ft2_font_render_box_glyph (PANGO_PIXELS (metrics->approximate_char_width), |
207 | PANGO_PIXELS (metrics->ascent + metrics->descent), |
208 | PANGO_PIXELS (metrics->ascent), |
209 | invalid: invalid_input); |
210 | pango_font_metrics_unref (metrics); |
211 | |
212 | return box; |
213 | } |
214 | |
215 | face = pango_ft2_font_get_face (font); |
216 | |
217 | if (face) |
218 | { |
219 | PangoFT2RenderedGlyph *rendered; |
220 | PangoFT2Font *ft2font = (PangoFT2Font *) font; |
221 | |
222 | rendered = g_slice_new (PangoFT2RenderedGlyph); |
223 | |
224 | /* Draw glyph */ |
225 | FT_Load_Glyph (face, glyph_index, load_flags: ft2font->load_flags); |
226 | FT_Render_Glyph (slot: face->glyph, |
227 | render_mode: (ft2font->load_flags & FT_LOAD_TARGET_MONO ? |
228 | ft_render_mode_mono : ft_render_mode_normal)); |
229 | |
230 | rendered->bitmap = face->glyph->bitmap; |
231 | rendered->bitmap.buffer = g_memdup2 (mem: face->glyph->bitmap.buffer, |
232 | byte_size: face->glyph->bitmap.rows * face->glyph->bitmap.pitch); |
233 | rendered->bitmap_left = face->glyph->bitmap_left; |
234 | rendered->bitmap_top = face->glyph->bitmap_top; |
235 | |
236 | if (G_UNLIKELY (!rendered->bitmap.buffer)) { |
237 | g_slice_free (PangoFT2RenderedGlyph, rendered); |
238 | return NULL; |
239 | } |
240 | |
241 | return rendered; |
242 | } |
243 | else |
244 | { |
245 | generic_box: |
246 | return pango_ft2_font_render_box_glyph (PANGO_UNKNOWN_GLYPH_WIDTH, |
247 | PANGO_UNKNOWN_GLYPH_HEIGHT, |
248 | PANGO_UNKNOWN_GLYPH_HEIGHT, |
249 | invalid: invalid_input); |
250 | } |
251 | } |
252 | |
253 | static void |
254 | pango_ft2_renderer_draw_glyph (PangoRenderer *renderer, |
255 | PangoFont *font, |
256 | PangoGlyph glyph, |
257 | double x, |
258 | double y) |
259 | { |
260 | FT_Bitmap *bitmap = PANGO_FT2_RENDERER (renderer)->bitmap; |
261 | PangoFT2RenderedGlyph *rendered_glyph; |
262 | gboolean add_glyph_to_cache; |
263 | guchar *src, *dest; |
264 | |
265 | int x_start, x_limit; |
266 | int y_start, y_limit; |
267 | int ixoff = floor (x: x + 0.5); |
268 | int iyoff = floor (x: y + 0.5); |
269 | int ix, iy; |
270 | |
271 | if (glyph & PANGO_GLYPH_UNKNOWN_FLAG) |
272 | { |
273 | /* Since we don't draw hexbox for FT2 renderer, |
274 | * unifiy the rendered bitmap in the cache by converting |
275 | * all missing glyphs to either INVALID_INPUT or UNKNOWN_FLAG. |
276 | */ |
277 | |
278 | gunichar wc = glyph & (~PANGO_GLYPH_UNKNOWN_FLAG); |
279 | |
280 | if (G_UNLIKELY (glyph == PANGO_GLYPH_INVALID_INPUT || wc > 0x10FFFF)) |
281 | glyph = PANGO_GLYPH_INVALID_INPUT; |
282 | else |
283 | glyph = PANGO_GLYPH_UNKNOWN_FLAG; |
284 | } |
285 | |
286 | rendered_glyph = _pango_ft2_font_get_cache_glyph_data (font, glyph_index: glyph); |
287 | add_glyph_to_cache = FALSE; |
288 | if (rendered_glyph == NULL) |
289 | { |
290 | rendered_glyph = pango_ft2_font_render_glyph (font, glyph_index: glyph); |
291 | if (rendered_glyph == NULL) |
292 | return; |
293 | add_glyph_to_cache = TRUE; |
294 | } |
295 | |
296 | x_start = MAX (0, - (ixoff + rendered_glyph->bitmap_left)); |
297 | x_limit = MIN ((int) rendered_glyph->bitmap.width, |
298 | (int) (bitmap->width - (ixoff + rendered_glyph->bitmap_left))); |
299 | |
300 | y_start = MAX (0, - (iyoff - rendered_glyph->bitmap_top)); |
301 | y_limit = MIN ((int) rendered_glyph->bitmap.rows, |
302 | (int) (bitmap->rows - (iyoff - rendered_glyph->bitmap_top))); |
303 | |
304 | src = rendered_glyph->bitmap.buffer + |
305 | y_start * rendered_glyph->bitmap.pitch; |
306 | |
307 | dest = bitmap->buffer + |
308 | (y_start + iyoff - rendered_glyph->bitmap_top) * bitmap->pitch + |
309 | x_start + ixoff + rendered_glyph->bitmap_left; |
310 | |
311 | switch (rendered_glyph->bitmap.pixel_mode) |
312 | { |
313 | case ft_pixel_mode_grays: |
314 | src += x_start; |
315 | for (iy = y_start; iy < y_limit; iy++) |
316 | { |
317 | guchar *s = src; |
318 | guchar *d = dest; |
319 | |
320 | for (ix = x_start; ix < x_limit; ix++) |
321 | { |
322 | switch (*s) |
323 | { |
324 | case 0: |
325 | break; |
326 | case 0xff: |
327 | *d = 0xff; |
328 | break; |
329 | default: |
330 | *d = MIN ((gushort) *d + (gushort) *s, 0xff); |
331 | break; |
332 | } |
333 | |
334 | s++; |
335 | d++; |
336 | } |
337 | |
338 | dest += bitmap->pitch; |
339 | src += rendered_glyph->bitmap.pitch; |
340 | } |
341 | break; |
342 | |
343 | case ft_pixel_mode_mono: |
344 | src += x_start / 8; |
345 | for (iy = y_start; iy < y_limit; iy++) |
346 | { |
347 | guchar *s = src; |
348 | guchar *d = dest; |
349 | |
350 | for (ix = x_start; ix < x_limit; ix++) |
351 | { |
352 | if ((*s) & (1 << (7 - (ix % 8)))) |
353 | *d |= 0xff; |
354 | |
355 | if ((ix % 8) == 7) |
356 | s++; |
357 | d++; |
358 | } |
359 | |
360 | dest += bitmap->pitch; |
361 | src += rendered_glyph->bitmap.pitch; |
362 | } |
363 | break; |
364 | |
365 | default: |
366 | g_warning ("pango_ft2_render: " |
367 | "Unrecognized glyph bitmap pixel mode %d\n" , |
368 | rendered_glyph->bitmap.pixel_mode); |
369 | break; |
370 | } |
371 | |
372 | if (add_glyph_to_cache) |
373 | { |
374 | _pango_ft2_font_set_glyph_cache_destroy (font, |
375 | destroy_notify: (GDestroyNotify) pango_ft2_free_rendered_glyph); |
376 | _pango_ft2_font_set_cache_glyph_data (font, |
377 | glyph_index: glyph, cached_glyph: rendered_glyph); |
378 | } |
379 | } |
380 | |
381 | typedef struct { |
382 | double y; |
383 | double x1; |
384 | double x2; |
385 | } Position; |
386 | |
387 | static void |
388 | draw_simple_trap (PangoRenderer *renderer, |
389 | Position *t, |
390 | Position *b) |
391 | { |
392 | FT_Bitmap *bitmap = PANGO_FT2_RENDERER (renderer)->bitmap; |
393 | int iy = floor (x: t->y); |
394 | int x1, x2, x; |
395 | double dy = b->y - t->y; |
396 | guchar *dest; |
397 | |
398 | if (iy < 0 || iy >= (int) bitmap->rows) |
399 | return; |
400 | dest = bitmap->buffer + iy * bitmap->pitch; |
401 | |
402 | if (t->x1 < b->x1) |
403 | x1 = floor (x: t->x1); |
404 | else |
405 | x1 = floor (x: b->x1); |
406 | |
407 | if (t->x2 > b->x2) |
408 | x2 = ceil (x: t->x2); |
409 | else |
410 | x2 = ceil (x: b->x2); |
411 | |
412 | x1 = CLAMP (x1, 0, (int) bitmap->width); |
413 | x2 = CLAMP (x2, 0, (int) bitmap->width); |
414 | |
415 | for (x = x1; x < x2; x++) |
416 | { |
417 | double top_left = MAX (t->x1, x); |
418 | double top_right = MIN (t->x2, x + 1); |
419 | double bottom_left = MAX (b->x1, x); |
420 | double bottom_right = MIN (b->x2, x + 1); |
421 | double c = 0.5 * dy * ((top_right - top_left) + (bottom_right - bottom_left)); |
422 | |
423 | /* When converting to [0,255], we round up. This is intended |
424 | * to prevent the problem of pixels that get divided into |
425 | * multiple slices not being fully black. |
426 | */ |
427 | int ic = c * 256; |
428 | |
429 | dest[x] = MIN (dest[x] + ic, 255); |
430 | } |
431 | } |
432 | |
433 | static void |
434 | interpolate_position (Position *result, |
435 | Position *top, |
436 | Position *bottom, |
437 | double val, |
438 | double val1, |
439 | double val2) |
440 | { |
441 | result->y = (top->y * (val2 - val) + bottom->y * (val - val1)) / (val2 - val1); |
442 | result->x1 = (top->x1 * (val2 - val) + bottom->x1 * (val - val1)) / (val2 - val1); |
443 | result->x2 = (top->x2 * (val2 - val) + bottom->x2 * (val - val1)) / (val2 - val1); |
444 | } |
445 | |
446 | /* This draws a trapezoid with the parallel sides aligned with |
447 | * the X axis. We do this by subdividing the trapezoid vertically |
448 | * into thin slices (themselves trapezoids) where two edge sides are each |
449 | * contained within a single pixel and then rasterizing each |
450 | * slice. There are frequently multiple slices within a single |
451 | * line so we have to accumulate to get the final result. |
452 | */ |
453 | static void |
454 | pango_ft2_renderer_draw_trapezoid (PangoRenderer *renderer, |
455 | PangoRenderPart part G_GNUC_UNUSED, |
456 | double y1, |
457 | double x11, |
458 | double x21, |
459 | double y2, |
460 | double x12, |
461 | double x22) |
462 | { |
463 | Position pos; |
464 | Position t; |
465 | Position b; |
466 | gboolean done = FALSE; |
467 | |
468 | if (y1 == y2) |
469 | return; |
470 | |
471 | pos.y = t.y = y1; |
472 | pos.x1 = t.x1 = x11; |
473 | pos.x2 = t.x2 = x21; |
474 | b.y = y2; |
475 | b.x1 = x12; |
476 | b.x2 = x22; |
477 | |
478 | while (!done) |
479 | { |
480 | Position pos_next; |
481 | double y_next, x1_next, x2_next; |
482 | double ix1, ix2; |
483 | |
484 | /* The algorithm here is written to emphasize simplicity and |
485 | * numerical stability as opposed to speed. |
486 | * |
487 | * While the end result is slicing up the polygon vertically, |
488 | * conceptually we aren't walking in the X direction, rather we |
489 | * are walking along the edges. When we compute crossing of |
490 | * horizontal pixel boundaries, we use the X coordinate as the |
491 | * interpolating variable, when we compute crossing for vertical |
492 | * pixel boundaries, we use the Y coordinate. |
493 | * |
494 | * This allows us to handle almost exactly horizontal edges without |
495 | * running into difficulties. (Almost exactly horizontal edges |
496 | * come up frequently due to inexactness in computing, say, |
497 | * a 90 degree rotation transformation) |
498 | */ |
499 | |
500 | pos_next = b; |
501 | done = TRUE; |
502 | |
503 | /* Check for crossing vertical pixel boundaries */ |
504 | y_next = floor (x: pos.y) + 1; |
505 | if (y_next < pos_next.y) |
506 | { |
507 | interpolate_position (result: &pos_next, top: &t, bottom: &b, |
508 | val: y_next, val1: t.y, val2: b.y); |
509 | pos_next.y = y_next; |
510 | done = FALSE; |
511 | } |
512 | |
513 | /* Check left side for crossing horizontal pixel boundaries */ |
514 | ix1 = floor (x: pos.x1); |
515 | |
516 | if (b.x1 < t.x1) |
517 | { |
518 | if (ix1 == pos.x1) |
519 | x1_next = ix1 - 1; |
520 | else |
521 | x1_next = ix1; |
522 | |
523 | if (x1_next > pos_next.x1) |
524 | { |
525 | interpolate_position (result: &pos_next, top: &t, bottom: &b, |
526 | val: x1_next, val1: t.x1, val2: b.x1); |
527 | pos_next.x1 = x1_next; |
528 | done = FALSE; |
529 | } |
530 | } |
531 | else if (b.x1 > t.x1) |
532 | { |
533 | x1_next = ix1 + 1; |
534 | |
535 | if (x1_next < pos_next.x1) |
536 | { |
537 | interpolate_position (result: &pos_next, top: &t, bottom: &b, |
538 | val: x1_next, val1: t.x1, val2: b.x1); |
539 | pos_next.x1 = x1_next; |
540 | done = FALSE; |
541 | } |
542 | } |
543 | |
544 | /* Check right side for crossing horizontal pixel boundaries */ |
545 | ix2 = floor (x: pos.x2); |
546 | |
547 | if (b.x2 < t.x2) |
548 | { |
549 | if (ix2 == pos.x2) |
550 | x2_next = ix2 - 1; |
551 | else |
552 | x2_next = ix2; |
553 | |
554 | if (x2_next > pos_next.x2) |
555 | { |
556 | interpolate_position (result: &pos_next, top: &t, bottom: &b, |
557 | val: x2_next, val1: t.x2, val2: b.x2); |
558 | pos_next.x2 = x2_next; |
559 | done = FALSE; |
560 | } |
561 | } |
562 | else if (x22 > x21) |
563 | { |
564 | x2_next = ix2 + 1; |
565 | |
566 | if (x2_next < pos_next.x2) |
567 | { |
568 | interpolate_position (result: &pos_next, top: &t, bottom: &b, |
569 | val: x2_next, val1: t.x2, val2: b.x2); |
570 | pos_next.x2 = x2_next; |
571 | done = FALSE; |
572 | } |
573 | } |
574 | |
575 | draw_simple_trap (renderer, t: &pos, b: &pos_next); |
576 | pos = pos_next; |
577 | } |
578 | } |
579 | |
580 | /** |
581 | * pango_ft2_render_layout_subpixel: |
582 | * @bitmap: a FT_Bitmap to render the layout onto |
583 | * @layout: a `PangoLayout` |
584 | * @x: the X position of the left of the layout (in Pango units) |
585 | * @y: the Y position of the top of the layout (in Pango units) |
586 | * |
587 | * Render a `PangoLayout` onto a FreeType2 bitmap, with he |
588 | * location specified in fixed-point Pango units rather than |
589 | * pixels. |
590 | * |
591 | * (Using this will avoid extra inaccuracies from rounding |
592 | * to integer pixels multiple times, even if the final glyph |
593 | * positions are integers.) |
594 | * |
595 | * Since: 1.6 |
596 | */ |
597 | void |
598 | pango_ft2_render_layout_subpixel (FT_Bitmap *bitmap, |
599 | PangoLayout *layout, |
600 | int x, |
601 | int y) |
602 | { |
603 | PangoContext *context; |
604 | PangoFontMap *fontmap; |
605 | PangoRenderer *renderer; |
606 | |
607 | g_return_if_fail (bitmap != NULL); |
608 | g_return_if_fail (PANGO_IS_LAYOUT (layout)); |
609 | |
610 | context = pango_layout_get_context (layout); |
611 | fontmap = pango_context_get_font_map (context); |
612 | renderer = _pango_ft2_font_map_get_renderer (PANGO_FT2_FONT_MAP (fontmap)); |
613 | |
614 | pango_ft2_renderer_set_bitmap (PANGO_FT2_RENDERER (renderer), bitmap); |
615 | |
616 | pango_renderer_draw_layout (renderer, layout, x, y); |
617 | } |
618 | |
619 | /** |
620 | * pango_ft2_render_layout: |
621 | * @bitmap: a FT_Bitmap to render the layout onto |
622 | * @layout: a `PangoLayout` |
623 | * @x: the X position of the left of the layout (in pixels) |
624 | * @y: the Y position of the top of the layout (in pixels) |
625 | * |
626 | * Render a `PangoLayout` onto a FreeType2 bitmap |
627 | */ |
628 | void |
629 | pango_ft2_render_layout (FT_Bitmap *bitmap, |
630 | PangoLayout *layout, |
631 | int x, |
632 | int y) |
633 | { |
634 | pango_ft2_render_layout_subpixel (bitmap, layout, x: x * PANGO_SCALE, y: y * PANGO_SCALE); |
635 | } |
636 | |
637 | /** |
638 | * pango_ft2_render_layout_line_subpixel: |
639 | * @bitmap: a FT_Bitmap to render the line onto |
640 | * @line: a `PangoLayoutLine` |
641 | * @x: the x position of start of string (in Pango units) |
642 | * @y: the y position of baseline (in Pango units) |
643 | * |
644 | * Render a `PangoLayoutLine` onto a FreeType2 bitmap, with he |
645 | * location specified in fixed-point Pango units rather than |
646 | * pixels. |
647 | * |
648 | * (Using this will avoid extra inaccuracies from rounding |
649 | * to integer pixels multiple times, even if the final glyph |
650 | * positions are integers.) |
651 | * |
652 | * Since: 1.6 |
653 | */ |
654 | void |
655 | pango_ft2_render_layout_line_subpixel (FT_Bitmap *bitmap, |
656 | PangoLayoutLine *line, |
657 | int x, |
658 | int y) |
659 | { |
660 | PangoContext *context; |
661 | PangoFontMap *fontmap; |
662 | PangoRenderer *renderer; |
663 | |
664 | g_return_if_fail (bitmap != NULL); |
665 | g_return_if_fail (line != NULL); |
666 | |
667 | context = pango_layout_get_context (layout: line->layout); |
668 | fontmap = pango_context_get_font_map (context); |
669 | renderer = _pango_ft2_font_map_get_renderer (PANGO_FT2_FONT_MAP (fontmap)); |
670 | |
671 | pango_ft2_renderer_set_bitmap (PANGO_FT2_RENDERER (renderer), bitmap); |
672 | |
673 | pango_renderer_draw_layout_line (renderer, line, x, y); |
674 | } |
675 | |
676 | /** |
677 | * pango_ft2_render_layout_line: |
678 | * @bitmap: a FT_Bitmap to render the line onto |
679 | * @line: a `PangoLayoutLine` |
680 | * @x: the x position of start of string (in pixels) |
681 | * @y: the y position of baseline (in pixels) |
682 | * |
683 | * Render a `PangoLayoutLine` onto a FreeType2 bitmap |
684 | */ |
685 | void |
686 | pango_ft2_render_layout_line (FT_Bitmap *bitmap, |
687 | PangoLayoutLine *line, |
688 | int x, |
689 | int y) |
690 | { |
691 | pango_ft2_render_layout_line_subpixel (bitmap, line, x: x * PANGO_SCALE, y: y * PANGO_SCALE); |
692 | } |
693 | |
694 | /** |
695 | * pango_ft2_render_transformed: |
696 | * @bitmap: the FreeType2 bitmap onto which to draw the string |
697 | * @font: the font in which to draw the string |
698 | * @matrix: (nullable): a `PangoMatrix` |
699 | * @glyphs: the glyph string to draw |
700 | * @x: the x position of the start of the string (in Pango |
701 | * units in user space coordinates) |
702 | * @y: the y position of the baseline (in Pango units |
703 | * in user space coordinates) |
704 | * |
705 | * Renders a `PangoGlyphString` onto a FreeType2 bitmap, possibly |
706 | * transforming the layed-out coordinates through a transformation |
707 | * matrix. |
708 | * |
709 | * Note that the transformation matrix for @font is not |
710 | * changed, so to produce correct rendering results, the @font |
711 | * must have been loaded using a `PangoContext` with an identical |
712 | * transformation matrix to that passed in to this function. |
713 | * |
714 | * Since: 1.6 |
715 | */ |
716 | void |
717 | pango_ft2_render_transformed (FT_Bitmap *bitmap, |
718 | const PangoMatrix *matrix, |
719 | PangoFont *font, |
720 | PangoGlyphString *glyphs, |
721 | int x, |
722 | int y) |
723 | { |
724 | PangoFontMap *fontmap; |
725 | PangoRenderer *renderer; |
726 | |
727 | g_return_if_fail (bitmap != NULL); |
728 | g_return_if_fail (glyphs != NULL); |
729 | g_return_if_fail (PANGO_FT2_IS_FONT (font)); |
730 | |
731 | fontmap = PANGO_FC_FONT (font)->fontmap; |
732 | renderer = _pango_ft2_font_map_get_renderer (PANGO_FT2_FONT_MAP (fontmap)); |
733 | |
734 | pango_ft2_renderer_set_bitmap (PANGO_FT2_RENDERER (renderer), bitmap); |
735 | pango_renderer_set_matrix (renderer, matrix); |
736 | |
737 | pango_renderer_draw_glyphs (renderer, font, glyphs, x, y); |
738 | } |
739 | |
740 | /** |
741 | * pango_ft2_render: |
742 | * @bitmap: the FreeType2 bitmap onto which to draw the string |
743 | * @font: the font in which to draw the string |
744 | * @glyphs: the glyph string to draw |
745 | * @x: the x position of the start of the string (in pixels) |
746 | * @y: the y position of the baseline (in pixels) |
747 | * |
748 | * Renders a `PangoGlyphString` onto a FreeType2 bitmap. |
749 | */ |
750 | void |
751 | pango_ft2_render (FT_Bitmap *bitmap, |
752 | PangoFont *font, |
753 | PangoGlyphString *glyphs, |
754 | int x, |
755 | int y) |
756 | { |
757 | pango_ft2_render_transformed (bitmap, NULL, font, glyphs, x: x * PANGO_SCALE, y: y * PANGO_SCALE); |
758 | } |
759 | |
760 | |