Mercurial > octave
comparison libinterp/corefcn/ft-text-renderer.cc @ 22331:b81b08cc4c83
maint: Indent namespaces in more files.
author | John W. Eaton <jwe@octave.org> |
---|---|
date | Wed, 17 Aug 2016 11:43:27 -0400 |
parents | 71dd9d5a5ecd |
children | 34ce5be04942 |
comparison
equal
deleted
inserted
replaced
22330:53e246fd8124 | 22331:b81b08cc4c83 |
---|---|
87 // Forward declaration | 87 // Forward declaration |
88 static void ft_face_destroyed (void *object); | 88 static void ft_face_destroyed (void *object); |
89 | 89 |
90 namespace octave | 90 namespace octave |
91 { | 91 { |
92 | |
93 class | 92 class |
94 ft_manager | 93 ft_manager |
95 { | 94 { |
96 public: | 95 public: |
97 static bool instance_ok (void) | 96 static bool instance_ok (void) |
322 octave::ft_manager::font_destroyed (reinterpret_cast<FT_Face> (object)); | 321 octave::ft_manager::font_destroyed (reinterpret_cast<FT_Face> (object)); |
323 } | 322 } |
324 | 323 |
325 namespace octave | 324 namespace octave |
326 { | 325 { |
327 | 326 class |
328 class | 327 OCTINTERP_API |
329 OCTINTERP_API | 328 ft_text_renderer : public base_text_renderer |
330 ft_text_renderer : public base_text_renderer | 329 { |
331 { | 330 public: |
332 public: | 331 |
333 | 332 enum |
334 enum | 333 { |
335 { | 334 MODE_BBOX = 0, |
336 MODE_BBOX = 0, | 335 MODE_RENDER = 1 |
337 MODE_RENDER = 1 | 336 }; |
338 }; | 337 |
339 | 338 enum |
340 enum | 339 { |
341 { | 340 ROTATION_0 = 0, |
342 ROTATION_0 = 0, | 341 ROTATION_90 = 1, |
343 ROTATION_90 = 1, | 342 ROTATION_180 = 2, |
344 ROTATION_180 = 2, | 343 ROTATION_270 = 3 |
345 ROTATION_270 = 3 | 344 }; |
346 }; | 345 |
347 | 346 public: |
348 public: | 347 |
349 | 348 ft_text_renderer (void) |
350 ft_text_renderer (void) | 349 : base_text_renderer (), font (), bbox (1, 4, 0.0), halign (0), |
351 : base_text_renderer (), font (), bbox (1, 4, 0.0), halign (0), | |
352 xoffset (0), line_yoffset (0), yoffset (0), mode (MODE_BBOX), | 350 xoffset (0), line_yoffset (0), yoffset (0), mode (MODE_BBOX), |
353 color (dim_vector (1, 3), 0) | 351 color (dim_vector (1, 3), 0) |
354 { } | 352 { } |
355 | 353 |
356 ~ft_text_renderer (void) { } | 354 ~ft_text_renderer (void) { } |
357 | 355 |
358 void visit (text_element_string& e); | 356 void visit (text_element_string& e); |
359 | 357 |
360 void visit (text_element_list& e); | 358 void visit (text_element_list& e); |
361 | 359 |
362 void visit (text_element_subscript& e); | 360 void visit (text_element_subscript& e); |
363 | 361 |
364 void visit (text_element_superscript& e); | 362 void visit (text_element_superscript& e); |
365 | 363 |
366 void visit (text_element_color& e); | 364 void visit (text_element_color& e); |
367 | 365 |
368 void visit (text_element_fontsize& e); | 366 void visit (text_element_fontsize& e); |
369 | 367 |
370 void visit (text_element_fontname& e); | 368 void visit (text_element_fontname& e); |
371 | 369 |
372 void visit (text_element_fontstyle& e); | 370 void visit (text_element_fontstyle& e); |
373 | 371 |
374 void visit (text_element_symbol& e); | 372 void visit (text_element_symbol& e); |
375 | 373 |
376 void visit (text_element_combined& e); | 374 void visit (text_element_combined& e); |
377 | 375 |
378 void reset (void); | 376 void reset (void); |
379 | 377 |
380 uint8NDArray get_pixels (void) const { return pixels; } | 378 uint8NDArray get_pixels (void) const { return pixels; } |
381 | 379 |
382 Matrix get_boundingbox (void) const { return bbox; } | 380 Matrix get_boundingbox (void) const { return bbox; } |
383 | 381 |
384 uint8NDArray render (text_element *elt, Matrix& box, | 382 uint8NDArray render (text_element *elt, Matrix& box, |
385 int rotation = ROTATION_0); | 383 int rotation = ROTATION_0); |
386 | 384 |
387 Matrix get_extent (text_element *elt, double rotation = 0.0); | 385 Matrix get_extent (text_element *elt, double rotation = 0.0); |
388 Matrix get_extent (const std::string& txt, double rotation, | 386 Matrix get_extent (const std::string& txt, double rotation, |
389 const caseless_str& interpreter); | 387 const caseless_str& interpreter); |
390 | 388 |
391 void set_font (const std::string& name, const std::string& weight, | 389 void set_font (const std::string& name, const std::string& weight, |
392 const std::string& angle, double size); | 390 const std::string& angle, double size); |
393 | 391 |
394 void set_color (const Matrix& c); | 392 void set_color (const Matrix& c); |
395 | 393 |
396 void set_mode (int m); | 394 void set_mode (int m); |
397 | 395 |
398 void text_to_pixels (const std::string& txt, | 396 void text_to_pixels (const std::string& txt, |
399 uint8NDArray& pxls, Matrix& bbox, | 397 uint8NDArray& pxls, Matrix& bbox, |
400 int halign, int valign, double rotation, | 398 int halign, int valign, double rotation, |
401 const caseless_str& interpreter, | 399 const caseless_str& interpreter, |
402 bool handle_rotation); | 400 bool handle_rotation); |
403 | 401 |
404 private: | 402 private: |
405 | 403 |
406 int rotation_to_mode (double rotation) const; | 404 int rotation_to_mode (double rotation) const; |
407 | 405 |
408 // No copying! | 406 // No copying! |
409 | 407 |
410 ft_text_renderer (const ft_text_renderer&); | 408 ft_text_renderer (const ft_text_renderer&); |
411 | 409 |
412 ft_text_renderer& operator = (const ft_text_renderer&); | 410 ft_text_renderer& operator = (const ft_text_renderer&); |
413 | 411 |
414 // Class to hold information about fonts and a strong | 412 // Class to hold information about fonts and a strong |
415 // reference to the font objects loaded by FreeType. | 413 // reference to the font objects loaded by FreeType. |
416 | 414 |
417 class ft_font : public text_renderer::font | 415 class ft_font : public text_renderer::font |
418 { | 416 { |
417 public: | |
418 | |
419 ft_font (void) | |
420 : text_renderer::font (), face (0) { } | |
421 | |
422 ft_font (const std::string& nm, const std::string& wt, | |
423 const std::string& ang, double sz, FT_Face f = 0) | |
424 : text_renderer::font (nm, wt, ang, sz), face (f) | |
425 { } | |
426 | |
427 ft_font (const ft_font& ft); | |
428 | |
429 ~ft_font (void) | |
430 { | |
431 if (face) | |
432 FT_Done_Face (face); | |
433 } | |
434 | |
435 ft_font& operator = (const ft_font& ft); | |
436 | |
437 bool is_valid (void) const { return get_face (); } | |
438 | |
439 FT_Face get_face (void) const; | |
440 | |
441 private: | |
442 | |
443 mutable FT_Face face; | |
444 }; | |
445 | |
446 void push_new_line (void); | |
447 | |
448 void update_line_bbox (void); | |
449 | |
450 void compute_bbox (void); | |
451 | |
452 int compute_line_xoffset (const Matrix& lb) const; | |
453 | |
454 FT_UInt process_character (FT_ULong code, FT_UInt previous = 0); | |
455 | |
419 public: | 456 public: |
420 | 457 |
421 ft_font (void) | 458 void text_to_strlist (const std::string& txt, |
422 : text_renderer::font (), face (0) { } | 459 std::list<text_renderer::string>& lst, Matrix& bbox, |
423 | 460 int halign, int valign, double rotation, |
424 ft_font (const std::string& nm, const std::string& wt, | 461 const caseless_str& interp); |
425 const std::string& ang, double sz, FT_Face f = 0) | |
426 : text_renderer::font (nm, wt, ang, sz), face (f) | |
427 { } | |
428 | |
429 ft_font (const ft_font& ft); | |
430 | |
431 ~ft_font (void) | |
432 { | |
433 if (face) | |
434 FT_Done_Face (face); | |
435 } | |
436 | |
437 ft_font& operator = (const ft_font& ft); | |
438 | |
439 bool is_valid (void) const { return get_face (); } | |
440 | |
441 FT_Face get_face (void) const; | |
442 | 462 |
443 private: | 463 private: |
444 | 464 |
445 mutable FT_Face face; | 465 // The current font used by the renderer. |
466 ft_font font; | |
467 | |
468 // Used to stored the bounding box corresponding to the rendered text. | |
469 // The bounding box has the form [x, y, w, h] where x and y represent the | |
470 // coordinates of the bottom left corner relative to the anchor point of | |
471 // the text (== start of text on the baseline). Due to font descent or | |
472 // multiple lines, the value y is usually negative. | |
473 Matrix bbox; | |
474 | |
475 // Used to stored the rendered text. It's a 3D matrix with size MxNx4 | |
476 // where M and N are the width and height of the bounding box. | |
477 uint8NDArray pixels; | |
478 | |
479 // Used to store the bounding box of each line. This is used to layout | |
480 // multiline text properly. | |
481 std::list<Matrix> line_bbox; | |
482 | |
483 // The current horizontal alignment. This is used to align multi-line text. | |
484 int halign; | |
485 | |
486 // The X offset for the next glyph. | |
487 int xoffset; | |
488 | |
489 // The Y offset of the baseline for the current line. | |
490 int line_yoffset; | |
491 | |
492 // The Y offset of the baseline for the next glyph. The offset is relative | |
493 // to line_yoffset. The total Y offset is computed with: | |
494 // line_yoffset + yoffset. | |
495 int yoffset; | |
496 | |
497 // The current mode of the rendering process (box computing or rendering). | |
498 int mode; | |
499 | |
500 // The base color of the rendered text. | |
501 uint8NDArray color; | |
502 | |
503 // A list of parsed strings to be used for printing. | |
504 std::list<text_renderer::string> strlist; | |
505 | |
506 // The X offset of the baseline for the current line. | |
507 int line_xoffset; | |
508 | |
446 }; | 509 }; |
447 | 510 |
448 void push_new_line (void); | 511 void |
449 | 512 ft_text_renderer::set_font (const std::string& name, const std::string& weight, |
450 void update_line_bbox (void); | 513 const std::string& angle, double size) |
451 | 514 { |
452 void compute_bbox (void); | 515 // FIXME: take "fontunits" into account |
453 | 516 |
454 int compute_line_xoffset (const Matrix& lb) const; | 517 font = ft_font (name, weight, angle, size, 0); |
455 | 518 } |
456 FT_UInt process_character (FT_ULong code, FT_UInt previous = 0); | 519 |
457 | 520 void |
458 public: | 521 ft_text_renderer::push_new_line (void) |
459 | 522 { |
460 void text_to_strlist (const std::string& txt, | 523 switch (mode) |
461 std::list<text_renderer::string>& lst, Matrix& bbox, | 524 { |
462 int halign, int valign, double rotation, | 525 case MODE_BBOX: |
463 const caseless_str& interp); | |
464 | |
465 private: | |
466 | |
467 // The current font used by the renderer. | |
468 ft_font font; | |
469 | |
470 // Used to stored the bounding box corresponding to the rendered text. | |
471 // The bounding box has the form [x, y, w, h] where x and y represent the | |
472 // coordinates of the bottom left corner relative to the anchor point of | |
473 // the text (== start of text on the baseline). Due to font descent or | |
474 // multiple lines, the value y is usually negative. | |
475 Matrix bbox; | |
476 | |
477 // Used to stored the rendered text. It's a 3D matrix with size MxNx4 | |
478 // where M and N are the width and height of the bounding box. | |
479 uint8NDArray pixels; | |
480 | |
481 // Used to store the bounding box of each line. This is used to layout | |
482 // multiline text properly. | |
483 std::list<Matrix> line_bbox; | |
484 | |
485 // The current horizontal alignment. This is used to align multi-line text. | |
486 int halign; | |
487 | |
488 // The X offset for the next glyph. | |
489 int xoffset; | |
490 | |
491 // The Y offset of the baseline for the current line. | |
492 int line_yoffset; | |
493 | |
494 // The Y offset of the baseline for the next glyph. The offset is relative | |
495 // to line_yoffset. The total Y offset is computed with: | |
496 // line_yoffset + yoffset. | |
497 int yoffset; | |
498 | |
499 // The current mode of the rendering process (box computing or rendering). | |
500 int mode; | |
501 | |
502 // The base color of the rendered text. | |
503 uint8NDArray color; | |
504 | |
505 // A list of parsed strings to be used for printing. | |
506 std::list<text_renderer::string> strlist; | |
507 | |
508 // The X offset of the baseline for the current line. | |
509 int line_xoffset; | |
510 | |
511 }; | |
512 | |
513 void | |
514 ft_text_renderer::set_font (const std::string& name, const std::string& weight, | |
515 const std::string& angle, double size) | |
516 { | |
517 // FIXME: take "fontunits" into account | |
518 | |
519 font = ft_font (name, weight, angle, size, 0); | |
520 } | |
521 | |
522 void | |
523 ft_text_renderer::push_new_line (void) | |
524 { | |
525 switch (mode) | |
526 { | |
527 case MODE_BBOX: | |
528 { | |
529 // Create a new bbox entry based on the current font. | |
530 | |
531 FT_Face face = font.get_face (); | |
532 | |
533 if (face) | |
534 { | |
535 int asc = face->size->metrics.ascender >> 6; | |
536 int desc = face->size->metrics.descender >> 6; | |
537 int h = face->size->metrics.height >> 6; | |
538 | |
539 Matrix bb (1, 5, 0.0); | |
540 | |
541 bb(1) = desc; | |
542 bb(3) = asc - desc; | |
543 bb(4) = h; | |
544 | |
545 line_bbox.push_back (bb); | |
546 | |
547 xoffset = yoffset = 0; | |
548 } | |
549 } | |
550 break; | |
551 | |
552 case MODE_RENDER: | |
553 { | |
554 // Move to the next line bbox, adjust xoffset based on alignment | |
555 // and yoffset based on the old and new line bbox. | |
556 | |
557 Matrix old_bbox = line_bbox.front (); | |
558 line_bbox.pop_front (); | |
559 Matrix new_bbox = line_bbox.front (); | |
560 | |
561 xoffset = line_xoffset = compute_line_xoffset (new_bbox); | |
562 line_yoffset += (old_bbox(1) - (new_bbox(1) + new_bbox(3))); | |
563 yoffset = 0; | |
564 } | |
565 break; | |
566 } | |
567 } | |
568 | |
569 int | |
570 ft_text_renderer::compute_line_xoffset (const Matrix& lb) const | |
571 { | |
572 if (! bbox.is_empty ()) | |
573 { | |
574 switch (halign) | |
575 { | 526 { |
576 case 0: | 527 // Create a new bbox entry based on the current font. |
577 return 0; | 528 |
578 case 1: | 529 FT_Face face = font.get_face (); |
579 return (bbox(2) - lb(2)) / 2; | 530 |
580 case 2: | 531 if (face) |
581 return (bbox(2) - lb(2)); | |
582 } | |
583 } | |
584 | |
585 return 0; | |
586 } | |
587 | |
588 void | |
589 ft_text_renderer::compute_bbox (void) | |
590 { | |
591 // Stack the various line bbox together and compute the final | |
592 // bounding box for the entire text string. | |
593 | |
594 bbox = Matrix (); | |
595 | |
596 switch (line_bbox.size ()) | |
597 { | |
598 case 0: | |
599 break; | |
600 | |
601 case 1: | |
602 bbox = line_bbox.front ().extract (0, 0, 0, 3); | |
603 break; | |
604 | |
605 default: | |
606 for (std::list<Matrix>::const_iterator it = line_bbox.begin (); | |
607 it != line_bbox.end (); ++it) | |
608 { | |
609 if (bbox.is_empty ()) | |
610 bbox = it->extract (0, 0, 0, 3); | |
611 else | |
612 { | 532 { |
613 bbox(1) -= (*it)(3); | 533 int asc = face->size->metrics.ascender >> 6; |
614 bbox(3) += (*it)(3); | 534 int desc = face->size->metrics.descender >> 6; |
615 bbox(2) = octave::math::max (bbox(2), (*it)(2)); | 535 int h = face->size->metrics.height >> 6; |
536 | |
537 Matrix bb (1, 5, 0.0); | |
538 | |
539 bb(1) = desc; | |
540 bb(3) = asc - desc; | |
541 bb(4) = h; | |
542 | |
543 line_bbox.push_back (bb); | |
544 | |
545 xoffset = yoffset = 0; | |
616 } | 546 } |
617 } | 547 } |
618 break; | 548 break; |
619 } | 549 |
620 } | 550 case MODE_RENDER: |
621 | |
622 void | |
623 ft_text_renderer::update_line_bbox (void) | |
624 { | |
625 // Called after a font change, when in MODE_BBOX mode, to update the | |
626 // current line bbox with the new font metrics. This also includes the | |
627 // current yoffset, that is the offset of the current glyph's baseline | |
628 // the line's baseline. | |
629 | |
630 if (mode == MODE_BBOX) | |
631 { | |
632 int asc = font.get_face ()->size->metrics.ascender >> 6; | |
633 int desc = font.get_face ()->size->metrics.descender >> 6; | |
634 | |
635 Matrix& bb = line_bbox.back (); | |
636 | |
637 if ((yoffset + desc) < bb(1)) | |
638 { | 551 { |
639 // The new font goes below the bottom of the current bbox. | 552 // Move to the next line bbox, adjust xoffset based on alignment |
640 | 553 // and yoffset based on the old and new line bbox. |
641 int delta = bb(1) - (yoffset + desc); | 554 |
642 | 555 Matrix old_bbox = line_bbox.front (); |
643 bb(1) -= delta; | 556 line_bbox.pop_front (); |
644 bb(3) += delta; | 557 Matrix new_bbox = line_bbox.front (); |
645 } | 558 |
646 | 559 xoffset = line_xoffset = compute_line_xoffset (new_bbox); |
647 if ((yoffset + asc) > (bb(1) + bb(3))) | 560 line_yoffset += (old_bbox(1) - (new_bbox(1) + new_bbox(3))); |
648 { | |
649 // The new font goes above the top of the current bbox. | |
650 | |
651 int delta = (yoffset + asc) - (bb(1) + bb(3)); | |
652 | |
653 bb(3) += delta; | |
654 } | |
655 } | |
656 } | |
657 | |
658 void | |
659 ft_text_renderer::set_mode (int m) | |
660 { | |
661 mode = m; | |
662 | |
663 switch (mode) | |
664 { | |
665 case MODE_BBOX: | |
666 xoffset = line_yoffset = yoffset = 0; | |
667 bbox = Matrix (1, 4, 0.0); | |
668 line_bbox.clear (); | |
669 push_new_line (); | |
670 break; | |
671 | |
672 case MODE_RENDER: | |
673 if (bbox.numel () != 4) | |
674 { | |
675 ::warning ("ft_text_renderer: invalid bounding box, cannot render"); | |
676 | |
677 xoffset = line_yoffset = yoffset = 0; | |
678 pixels = uint8NDArray (); | |
679 } | |
680 else | |
681 { | |
682 dim_vector d (4, octave_idx_type (bbox(2)), | |
683 octave_idx_type (bbox(3))); | |
684 pixels = uint8NDArray (d, static_cast<uint8_t> (0)); | |
685 xoffset = compute_line_xoffset (line_bbox.front ()); | |
686 line_yoffset = -bbox(1)-1; | |
687 yoffset = 0; | 561 yoffset = 0; |
688 } | 562 } |
689 break; | 563 break; |
690 | 564 } |
691 default: | 565 } |
692 error ("ft_text_renderer: invalid mode '%d'", mode); | 566 |
693 break; | 567 int |
694 } | 568 ft_text_renderer::compute_line_xoffset (const Matrix& lb) const |
695 } | 569 { |
696 | 570 if (! bbox.is_empty ()) |
697 FT_UInt | 571 { |
698 ft_text_renderer::process_character (FT_ULong code, FT_UInt previous) | 572 switch (halign) |
699 { | 573 { |
700 FT_Face face = font.get_face (); | 574 case 0: |
701 FT_UInt glyph_index = 0; | 575 return 0; |
702 | 576 case 1: |
703 if (face) | 577 return (bbox(2) - lb(2)) / 2; |
704 { | 578 case 2: |
705 glyph_index = FT_Get_Char_Index (face, code); | 579 return (bbox(2) - lb(2)); |
706 | 580 } |
707 if (code != '\n' | 581 } |
708 && (! glyph_index | 582 |
709 || FT_Load_Glyph (face, glyph_index, FT_LOAD_DEFAULT))) | 583 return 0; |
710 { | 584 } |
711 glyph_index = 0; | 585 |
712 warn_missing_glyph (code); | 586 void |
713 } | 587 ft_text_renderer::compute_bbox (void) |
714 else | 588 { |
715 { | 589 // Stack the various line bbox together and compute the final |
716 switch (mode) | 590 // bounding box for the entire text string. |
591 | |
592 bbox = Matrix (); | |
593 | |
594 switch (line_bbox.size ()) | |
595 { | |
596 case 0: | |
597 break; | |
598 | |
599 case 1: | |
600 bbox = line_bbox.front ().extract (0, 0, 0, 3); | |
601 break; | |
602 | |
603 default: | |
604 for (std::list<Matrix>::const_iterator it = line_bbox.begin (); | |
605 it != line_bbox.end (); ++it) | |
606 { | |
607 if (bbox.is_empty ()) | |
608 bbox = it->extract (0, 0, 0, 3); | |
609 else | |
610 { | |
611 bbox(1) -= (*it)(3); | |
612 bbox(3) += (*it)(3); | |
613 bbox(2) = octave::math::max (bbox(2), (*it)(2)); | |
614 } | |
615 } | |
616 break; | |
617 } | |
618 } | |
619 | |
620 void | |
621 ft_text_renderer::update_line_bbox (void) | |
622 { | |
623 // Called after a font change, when in MODE_BBOX mode, to update the | |
624 // current line bbox with the new font metrics. This also includes the | |
625 // current yoffset, that is the offset of the current glyph's baseline | |
626 // the line's baseline. | |
627 | |
628 if (mode == MODE_BBOX) | |
629 { | |
630 int asc = font.get_face ()->size->metrics.ascender >> 6; | |
631 int desc = font.get_face ()->size->metrics.descender >> 6; | |
632 | |
633 Matrix& bb = line_bbox.back (); | |
634 | |
635 if ((yoffset + desc) < bb(1)) | |
636 { | |
637 // The new font goes below the bottom of the current bbox. | |
638 | |
639 int delta = bb(1) - (yoffset + desc); | |
640 | |
641 bb(1) -= delta; | |
642 bb(3) += delta; | |
643 } | |
644 | |
645 if ((yoffset + asc) > (bb(1) + bb(3))) | |
646 { | |
647 // The new font goes above the top of the current bbox. | |
648 | |
649 int delta = (yoffset + asc) - (bb(1) + bb(3)); | |
650 | |
651 bb(3) += delta; | |
652 } | |
653 } | |
654 } | |
655 | |
656 void | |
657 ft_text_renderer::set_mode (int m) | |
658 { | |
659 mode = m; | |
660 | |
661 switch (mode) | |
662 { | |
663 case MODE_BBOX: | |
664 xoffset = line_yoffset = yoffset = 0; | |
665 bbox = Matrix (1, 4, 0.0); | |
666 line_bbox.clear (); | |
667 push_new_line (); | |
668 break; | |
669 | |
670 case MODE_RENDER: | |
671 if (bbox.numel () != 4) | |
672 { | |
673 ::warning ("ft_text_renderer: invalid bounding box, cannot render"); | |
674 | |
675 xoffset = line_yoffset = yoffset = 0; | |
676 pixels = uint8NDArray (); | |
677 } | |
678 else | |
679 { | |
680 dim_vector d (4, octave_idx_type (bbox(2)), | |
681 octave_idx_type (bbox(3))); | |
682 pixels = uint8NDArray (d, static_cast<uint8_t> (0)); | |
683 xoffset = compute_line_xoffset (line_bbox.front ()); | |
684 line_yoffset = -bbox(1)-1; | |
685 yoffset = 0; | |
686 } | |
687 break; | |
688 | |
689 default: | |
690 error ("ft_text_renderer: invalid mode '%d'", mode); | |
691 break; | |
692 } | |
693 } | |
694 | |
695 FT_UInt | |
696 ft_text_renderer::process_character (FT_ULong code, FT_UInt previous) | |
697 { | |
698 FT_Face face = font.get_face (); | |
699 FT_UInt glyph_index = 0; | |
700 | |
701 if (face) | |
702 { | |
703 glyph_index = FT_Get_Char_Index (face, code); | |
704 | |
705 if (code != '\n' | |
706 && (! glyph_index | |
707 || FT_Load_Glyph (face, glyph_index, FT_LOAD_DEFAULT))) | |
708 { | |
709 glyph_index = 0; | |
710 warn_missing_glyph (code); | |
711 } | |
712 else | |
713 { | |
714 switch (mode) | |
715 { | |
716 case MODE_RENDER: | |
717 if (code == '\n') | |
718 { | |
719 glyph_index = FT_Get_Char_Index (face, ' '); | |
720 if (! glyph_index | |
721 || FT_Load_Glyph (face, glyph_index, FT_LOAD_DEFAULT)) | |
722 { | |
723 glyph_index = 0; | |
724 warn_missing_glyph (' '); | |
725 } | |
726 else | |
727 push_new_line (); | |
728 } | |
729 else if (FT_Render_Glyph (face->glyph, FT_RENDER_MODE_NORMAL)) | |
730 { | |
731 glyph_index = 0; | |
732 warn_glyph_render (code); | |
733 } | |
734 else | |
735 { | |
736 FT_Bitmap& bitmap = face->glyph->bitmap; | |
737 int x0, y0; | |
738 | |
739 if (previous) | |
740 { | |
741 FT_Vector delta; | |
742 | |
743 FT_Get_Kerning (face, previous, glyph_index, | |
744 FT_KERNING_DEFAULT, &delta); | |
745 xoffset += (delta.x >> 6); | |
746 } | |
747 | |
748 x0 = xoffset + face->glyph->bitmap_left; | |
749 y0 = line_yoffset + yoffset + face->glyph->bitmap_top; | |
750 | |
751 // 'w' seems to have a negative -1 | |
752 // face->glyph->bitmap_left, this is so we don't | |
753 // index out of bound, and assumes we've allocated | |
754 // the right amount of horizontal space in the bbox. | |
755 if (x0 < 0) | |
756 x0 = 0; | |
757 | |
758 for (int r = 0; static_cast<unsigned int> (r) < bitmap.rows; r++) | |
759 for (int c = 0; static_cast<unsigned int> (c) < bitmap.width; c++) | |
760 { | |
761 unsigned char pix = bitmap.buffer[r*bitmap.width+c]; | |
762 if (x0+c < 0 || x0+c >= pixels.dim2 () | |
763 || y0-r < 0 || y0-r >= pixels.dim3 ()) | |
764 { | |
765 //::warning ("ft_text_renderer: pixel out of bound (char=%d, (x,y)=(%d,%d), (w,h)=(%d,%d)", | |
766 // str[i], x0+c, y0-r, pixels.dim2 (), pixels.dim3 ()); | |
767 } | |
768 else if (pixels(3, x0+c, y0-r).value () == 0) | |
769 { | |
770 pixels(0, x0+c, y0-r) = color(0); | |
771 pixels(1, x0+c, y0-r) = color(1); | |
772 pixels(2, x0+c, y0-r) = color(2); | |
773 pixels(3, x0+c, y0-r) = pix; | |
774 } | |
775 } | |
776 | |
777 xoffset += (face->glyph->advance.x >> 6); | |
778 } | |
779 break; | |
780 | |
781 case MODE_BBOX: | |
782 if (code == '\n') | |
783 { | |
784 glyph_index = FT_Get_Char_Index (face, ' '); | |
785 if (! glyph_index | |
786 || FT_Load_Glyph (face, glyph_index, FT_LOAD_DEFAULT)) | |
787 { | |
788 glyph_index = 0; | |
789 warn_missing_glyph (' '); | |
790 } | |
791 else | |
792 push_new_line (); | |
793 } | |
794 else | |
795 { | |
796 Matrix& bb = line_bbox.back (); | |
797 | |
798 // If we have a previous glyph, use kerning information. | |
799 // This usually means moving a bit backward before adding | |
800 // the next glyph. That is, "delta.x" is usually < 0. | |
801 if (previous) | |
802 { | |
803 FT_Vector delta; | |
804 | |
805 FT_Get_Kerning (face, previous, glyph_index, | |
806 FT_KERNING_DEFAULT, &delta); | |
807 | |
808 xoffset += (delta.x >> 6); | |
809 } | |
810 | |
811 // Extend current X offset box by the width of the current | |
812 // glyph. Then extend the line bounding box if necessary. | |
813 | |
814 xoffset += (face->glyph->advance.x >> 6); | |
815 bb(2) = octave::math::max (bb(2), xoffset); | |
816 } | |
817 break; | |
818 } | |
819 } | |
820 } | |
821 | |
822 return glyph_index; | |
823 } | |
824 | |
825 void | |
826 ft_text_renderer::text_to_strlist (const std::string& txt, | |
827 std::list<text_renderer::string>& lst, | |
828 Matrix& box, | |
829 int ha, int va, double rot, | |
830 const caseless_str& interp) | |
831 { | |
832 uint8NDArray pxls; | |
833 | |
834 // First run text_to_pixels which will also build the string list | |
835 | |
836 text_to_pixels (txt, pxls, box, ha, va, rot, interp, false); | |
837 | |
838 lst = strlist; | |
839 } | |
840 | |
841 void | |
842 ft_text_renderer::visit (text_element_string& e) | |
843 { | |
844 if (font.is_valid ()) | |
845 { | |
846 FT_UInt glyph_index, previous = 0; | |
847 | |
848 std::string str = e.string_value (); | |
849 size_t n = str.length (); | |
850 size_t curr = 0; | |
851 size_t idx = 0; | |
852 mbstate_t ps; | |
853 memset (&ps, 0, sizeof (ps)); // Initialize state to 0. | |
854 wchar_t wc; | |
855 | |
856 text_renderer::string fs (str, font, xoffset, yoffset); | |
857 | |
858 while (n > 0) | |
859 { | |
860 size_t r = std::mbrtowc (&wc, str.data () + curr, n, &ps); | |
861 | |
862 if (r > 0 | |
863 && r != static_cast<size_t> (-1) | |
864 && r != static_cast<size_t> (-2)) | |
865 { | |
866 n -= r; | |
867 curr += r; | |
868 | |
869 if (wc == L'\n') | |
870 { | |
871 // Finish previous string in srtlist before processing | |
872 // the newline character | |
873 fs.set_y (line_yoffset + yoffset); | |
874 fs.set_color (color); | |
875 std::string s = str.substr (idx, curr - idx - 1); | |
876 if (! s.empty ()) | |
877 { | |
878 fs.set_string (s); | |
879 strlist.push_back (fs); | |
880 } | |
881 } | |
882 | |
883 glyph_index = process_character (wc, previous); | |
884 | |
885 if (wc == L'\n') | |
886 { | |
887 previous = 0; | |
888 // Start a new string in strlist | |
889 idx = curr; | |
890 fs = text_renderer::string (str.substr (idx), font, | |
891 line_xoffset, yoffset); | |
892 | |
893 } | |
894 else | |
895 previous = glyph_index; | |
896 } | |
897 else | |
898 { | |
899 if (r != 0) | |
900 ::warning ("ft_text_renderer: failed to decode string `%s' with " | |
901 "locale `%s'", str.c_str (), | |
902 std::setlocale (LC_CTYPE, 0)); | |
903 break; | |
904 } | |
905 } | |
906 | |
907 if (! fs.get_string ().empty ()) | |
908 { | |
909 fs.set_y (line_yoffset + yoffset); | |
910 fs.set_color (color); | |
911 strlist.push_back (fs); | |
912 } | |
913 } | |
914 } | |
915 | |
916 void | |
917 ft_text_renderer::visit (text_element_list& e) | |
918 { | |
919 // Save and restore (after processing the list) the current font and color. | |
920 | |
921 ft_font saved_font (font); | |
922 uint8NDArray saved_color (color); | |
923 | |
924 text_processor::visit (e); | |
925 | |
926 font = saved_font; | |
927 color = saved_color; | |
928 } | |
929 | |
930 void | |
931 ft_text_renderer::visit (text_element_subscript& e) | |
932 { | |
933 ft_font saved_font (font); | |
934 int saved_line_yoffset = line_yoffset; | |
935 int saved_yoffset = yoffset; | |
936 | |
937 set_font (font.get_name (), font.get_weight (), font.get_angle (), | |
938 font.get_size () - 2); | |
939 | |
940 if (font.is_valid ()) | |
941 { | |
942 int h = font.get_face ()->size->metrics.height >> 6; | |
943 | |
944 // Shifting the baseline by 2/3 the font height seems to produce | |
945 // decent result. | |
946 yoffset -= (h * 2) / 3; | |
947 | |
948 if (mode == MODE_BBOX) | |
949 update_line_bbox (); | |
950 } | |
951 | |
952 text_processor::visit (e); | |
953 | |
954 font = saved_font; | |
955 // If line_yoffset changed, this means we moved to a new line; hence yoffset | |
956 // cannot be restored, because the saved value is not relevant anymore. | |
957 if (line_yoffset == saved_line_yoffset) | |
958 yoffset = saved_yoffset; | |
959 } | |
960 | |
961 void | |
962 ft_text_renderer::visit (text_element_superscript& e) | |
963 { | |
964 ft_font saved_font (font); | |
965 int saved_line_yoffset = line_yoffset; | |
966 int saved_yoffset = yoffset; | |
967 | |
968 set_font (font.get_name (), font.get_weight (), font.get_angle (), | |
969 font.get_size () - 2); | |
970 | |
971 if (saved_font.is_valid ()) | |
972 { | |
973 int s_asc = saved_font.get_face ()->size->metrics.ascender >> 6; | |
974 | |
975 // Shifting the baseline by 2/3 base font ascender seems to produce | |
976 // decent result. | |
977 yoffset += (s_asc * 2) / 3; | |
978 | |
979 if (mode == MODE_BBOX) | |
980 update_line_bbox (); | |
981 } | |
982 | |
983 text_processor::visit (e); | |
984 | |
985 font = saved_font; | |
986 // If line_yoffset changed, this means we moved to a new line; hence yoffset | |
987 // cannot be restored, because the saved value is not relevant anymore. | |
988 if (line_yoffset == saved_line_yoffset) | |
989 yoffset = saved_yoffset; | |
990 } | |
991 | |
992 void | |
993 ft_text_renderer::visit (text_element_color& e) | |
994 { | |
995 if (mode == MODE_RENDER) | |
996 set_color (e.get_color ()); | |
997 } | |
998 | |
999 void | |
1000 ft_text_renderer::visit (text_element_fontsize& e) | |
1001 { | |
1002 double sz = e.get_fontsize (); | |
1003 | |
1004 // FIXME: Matlab documentation says that the font size is expressed | |
1005 // in the text object FontUnit. | |
1006 | |
1007 set_font (font.get_name (), font.get_weight (), font.get_angle (), sz); | |
1008 | |
1009 if (mode == MODE_BBOX) | |
1010 update_line_bbox (); | |
1011 } | |
1012 | |
1013 void | |
1014 ft_text_renderer::visit (text_element_fontname& e) | |
1015 { | |
1016 set_font (e.get_fontname (), font.get_weight (), font.get_angle (), | |
1017 font.get_size ()); | |
1018 | |
1019 if (mode == MODE_BBOX) | |
1020 update_line_bbox (); | |
1021 } | |
1022 | |
1023 void | |
1024 ft_text_renderer::visit (text_element_fontstyle& e) | |
1025 { | |
1026 switch (e.get_fontstyle ()) | |
1027 { | |
1028 case text_element_fontstyle::normal: | |
1029 set_font (font.get_name (), "normal", "normal", font.get_size ()); | |
1030 break; | |
1031 | |
1032 case text_element_fontstyle::bold: | |
1033 set_font (font.get_name (), "bold", "normal", font.get_size ()); | |
1034 break; | |
1035 | |
1036 case text_element_fontstyle::italic: | |
1037 set_font (font.get_name (), "normal", "italic", font.get_size ()); | |
1038 break; | |
1039 | |
1040 case text_element_fontstyle::oblique: | |
1041 set_font (font.get_name (), "normal", "oblique", font.get_size ()); | |
1042 break; | |
1043 } | |
1044 | |
1045 if (mode == MODE_BBOX) | |
1046 update_line_bbox (); | |
1047 } | |
1048 | |
1049 void | |
1050 ft_text_renderer::visit (text_element_symbol& e) | |
1051 { | |
1052 uint32_t code = e.get_symbol_code (); | |
1053 | |
1054 text_renderer::string fs (std::string ("-"), font, xoffset, yoffset); | |
1055 | |
1056 if (code != text_element_symbol::invalid_code && font.is_valid ()) | |
1057 { | |
1058 process_character (code); | |
1059 fs.set_code (code); | |
1060 } | |
1061 else if (font.is_valid ()) | |
1062 ::warning ("ignoring unknown symbol: %d", e.get_symbol ()); | |
1063 | |
1064 if (fs.get_code ()) | |
1065 { | |
1066 fs.set_y (line_yoffset + yoffset); | |
1067 fs.set_color (color); | |
1068 strlist.push_back (fs); | |
1069 } | |
1070 } | |
1071 | |
1072 void | |
1073 ft_text_renderer::visit (text_element_combined& e) | |
1074 { | |
1075 int saved_xoffset = xoffset; | |
1076 int max_xoffset = xoffset; | |
1077 | |
1078 for (text_element_combined::iterator it = e.begin (); it != e.end (); ++it) | |
1079 { | |
1080 xoffset = saved_xoffset; | |
1081 (*it)->accept (*this); | |
1082 max_xoffset = octave::math::max (xoffset, max_xoffset); | |
1083 } | |
1084 | |
1085 xoffset = max_xoffset; | |
1086 } | |
1087 | |
1088 void | |
1089 ft_text_renderer::reset (void) | |
1090 { | |
1091 set_mode (MODE_BBOX); | |
1092 set_color (Matrix (1, 3, 0.0)); | |
1093 } | |
1094 | |
1095 void | |
1096 ft_text_renderer::set_color (const Matrix& c) | |
1097 { | |
1098 if (c.numel () == 3) | |
1099 { | |
1100 color(0) = static_cast<uint8_t> (c(0)*255); | |
1101 color(1) = static_cast<uint8_t> (c(1)*255); | |
1102 color(2) = static_cast<uint8_t> (c(2)*255); | |
1103 } | |
1104 else | |
1105 ::warning ("ft_text_renderer::set_color: invalid color"); | |
1106 } | |
1107 | |
1108 uint8NDArray | |
1109 ft_text_renderer::render (text_element *elt, Matrix& box, int rotation) | |
1110 { | |
1111 set_mode (MODE_BBOX); | |
1112 elt->accept (*this); | |
1113 compute_bbox (); | |
1114 box = bbox; | |
1115 | |
1116 set_mode (MODE_RENDER); | |
1117 // Clear the list of parsed strings | |
1118 strlist.clear (); | |
1119 | |
1120 if (pixels.numel () > 0) | |
1121 { | |
1122 elt->accept (*this); | |
1123 | |
1124 switch (rotation) | |
1125 { | |
1126 case ROTATION_0: | |
1127 break; | |
1128 | |
1129 case ROTATION_90: | |
717 { | 1130 { |
718 case MODE_RENDER: | 1131 Array<octave_idx_type> perm (dim_vector (3, 1)); |
719 if (code == '\n') | 1132 perm(0) = 0; |
720 { | 1133 perm(1) = 2; |
721 glyph_index = FT_Get_Char_Index (face, ' '); | 1134 perm(2) = 1; |
722 if (! glyph_index | 1135 pixels = pixels.permute (perm); |
723 || FT_Load_Glyph (face, glyph_index, FT_LOAD_DEFAULT)) | 1136 |
724 { | 1137 Array<idx_vector> idx (dim_vector (3, 1)); |
725 glyph_index = 0; | 1138 idx(0) = idx_vector (':'); |
726 warn_missing_glyph (' '); | 1139 idx(1) = idx_vector (pixels.dim2 ()-1, -1, -1); |
727 } | 1140 idx(2) = idx_vector (':'); |
728 else | 1141 pixels = uint8NDArray (pixels.index (idx)); |
729 push_new_line (); | |
730 } | |
731 else if (FT_Render_Glyph (face->glyph, FT_RENDER_MODE_NORMAL)) | |
732 { | |
733 glyph_index = 0; | |
734 warn_glyph_render (code); | |
735 } | |
736 else | |
737 { | |
738 FT_Bitmap& bitmap = face->glyph->bitmap; | |
739 int x0, y0; | |
740 | |
741 if (previous) | |
742 { | |
743 FT_Vector delta; | |
744 | |
745 FT_Get_Kerning (face, previous, glyph_index, | |
746 FT_KERNING_DEFAULT, &delta); | |
747 xoffset += (delta.x >> 6); | |
748 } | |
749 | |
750 x0 = xoffset + face->glyph->bitmap_left; | |
751 y0 = line_yoffset + yoffset + face->glyph->bitmap_top; | |
752 | |
753 // 'w' seems to have a negative -1 | |
754 // face->glyph->bitmap_left, this is so we don't | |
755 // index out of bound, and assumes we've allocated | |
756 // the right amount of horizontal space in the bbox. | |
757 if (x0 < 0) | |
758 x0 = 0; | |
759 | |
760 for (int r = 0; static_cast<unsigned int> (r) < bitmap.rows; r++) | |
761 for (int c = 0; static_cast<unsigned int> (c) < bitmap.width; c++) | |
762 { | |
763 unsigned char pix = bitmap.buffer[r*bitmap.width+c]; | |
764 if (x0+c < 0 || x0+c >= pixels.dim2 () | |
765 || y0-r < 0 || y0-r >= pixels.dim3 ()) | |
766 { | |
767 //::warning ("ft_text_renderer: pixel out of bound (char=%d, (x,y)=(%d,%d), (w,h)=(%d,%d)", | |
768 // str[i], x0+c, y0-r, pixels.dim2 (), pixels.dim3 ()); | |
769 } | |
770 else if (pixels(3, x0+c, y0-r).value () == 0) | |
771 { | |
772 pixels(0, x0+c, y0-r) = color(0); | |
773 pixels(1, x0+c, y0-r) = color(1); | |
774 pixels(2, x0+c, y0-r) = color(2); | |
775 pixels(3, x0+c, y0-r) = pix; | |
776 } | |
777 } | |
778 | |
779 xoffset += (face->glyph->advance.x >> 6); | |
780 } | |
781 break; | |
782 | |
783 case MODE_BBOX: | |
784 if (code == '\n') | |
785 { | |
786 glyph_index = FT_Get_Char_Index (face, ' '); | |
787 if (! glyph_index | |
788 || FT_Load_Glyph (face, glyph_index, FT_LOAD_DEFAULT)) | |
789 { | |
790 glyph_index = 0; | |
791 warn_missing_glyph (' '); | |
792 } | |
793 else | |
794 push_new_line (); | |
795 } | |
796 else | |
797 { | |
798 Matrix& bb = line_bbox.back (); | |
799 | |
800 // If we have a previous glyph, use kerning information. | |
801 // This usually means moving a bit backward before adding | |
802 // the next glyph. That is, "delta.x" is usually < 0. | |
803 if (previous) | |
804 { | |
805 FT_Vector delta; | |
806 | |
807 FT_Get_Kerning (face, previous, glyph_index, | |
808 FT_KERNING_DEFAULT, &delta); | |
809 | |
810 xoffset += (delta.x >> 6); | |
811 } | |
812 | |
813 // Extend current X offset box by the width of the current | |
814 // glyph. Then extend the line bounding box if necessary. | |
815 | |
816 xoffset += (face->glyph->advance.x >> 6); | |
817 bb(2) = octave::math::max (bb(2), xoffset); | |
818 } | |
819 break; | |
820 } | 1142 } |
821 } | 1143 break; |
822 } | 1144 |
823 | 1145 case ROTATION_180: |
824 return glyph_index; | |
825 } | |
826 | |
827 void | |
828 ft_text_renderer::text_to_strlist (const std::string& txt, | |
829 std::list<text_renderer::string>& lst, | |
830 Matrix& box, | |
831 int ha, int va, double rot, | |
832 const caseless_str& interp) | |
833 { | |
834 uint8NDArray pxls; | |
835 | |
836 // First run text_to_pixels which will also build the string list | |
837 | |
838 text_to_pixels (txt, pxls, box, ha, va, rot, interp, false); | |
839 | |
840 lst = strlist; | |
841 } | |
842 | |
843 void | |
844 ft_text_renderer::visit (text_element_string& e) | |
845 { | |
846 if (font.is_valid ()) | |
847 { | |
848 FT_UInt glyph_index, previous = 0; | |
849 | |
850 std::string str = e.string_value (); | |
851 size_t n = str.length (); | |
852 size_t curr = 0; | |
853 size_t idx = 0; | |
854 mbstate_t ps; | |
855 memset (&ps, 0, sizeof (ps)); // Initialize state to 0. | |
856 wchar_t wc; | |
857 | |
858 text_renderer::string fs (str, font, xoffset, yoffset); | |
859 | |
860 while (n > 0) | |
861 { | |
862 size_t r = std::mbrtowc (&wc, str.data () + curr, n, &ps); | |
863 | |
864 if (r > 0 | |
865 && r != static_cast<size_t> (-1) | |
866 && r != static_cast<size_t> (-2)) | |
867 { | 1146 { |
868 n -= r; | 1147 Array<idx_vector> idx (dim_vector (3, 1)); |
869 curr += r; | 1148 idx(0) = idx_vector (':'); |
870 | 1149 idx(1) = idx_vector (pixels.dim2 ()-1, -1, -1); |
871 if (wc == L'\n') | 1150 idx(2) = idx_vector (pixels.dim3 ()-1, -1, -1); |
872 { | 1151 pixels = uint8NDArray (pixels.index (idx)); |
873 // Finish previous string in srtlist before processing | |
874 // the newline character | |
875 fs.set_y (line_yoffset + yoffset); | |
876 fs.set_color (color); | |
877 std::string s = str.substr (idx, curr - idx - 1); | |
878 if (! s.empty ()) | |
879 { | |
880 fs.set_string (s); | |
881 strlist.push_back (fs); | |
882 } | |
883 } | |
884 | |
885 glyph_index = process_character (wc, previous); | |
886 | |
887 if (wc == L'\n') | |
888 { | |
889 previous = 0; | |
890 // Start a new string in strlist | |
891 idx = curr; | |
892 fs = text_renderer::string (str.substr (idx), font, | |
893 line_xoffset, yoffset); | |
894 | |
895 } | |
896 else | |
897 previous = glyph_index; | |
898 } | 1152 } |
899 else | 1153 break; |
1154 | |
1155 case ROTATION_270: | |
900 { | 1156 { |
901 if (r != 0) | 1157 Array<octave_idx_type> perm (dim_vector (3, 1)); |
902 ::warning ("ft_text_renderer: failed to decode string `%s' with " | 1158 perm(0) = 0; |
903 "locale `%s'", str.c_str (), | 1159 perm(1) = 2; |
904 std::setlocale (LC_CTYPE, 0)); | 1160 perm(2) = 1; |
905 break; | 1161 pixels = pixels.permute (perm); |
1162 | |
1163 Array<idx_vector> idx (dim_vector (3, 1)); | |
1164 idx(0) = idx_vector (':'); | |
1165 idx(1) = idx_vector (':'); | |
1166 idx(2) = idx_vector (pixels.dim3 ()-1, -1, -1); | |
1167 pixels = uint8NDArray (pixels.index (idx)); | |
906 } | 1168 } |
907 } | 1169 break; |
908 | 1170 } |
909 if (! fs.get_string ().empty ()) | 1171 } |
910 { | 1172 |
911 fs.set_y (line_yoffset + yoffset); | 1173 return pixels; |
912 fs.set_color (color); | 1174 } |
913 strlist.push_back (fs); | 1175 |
914 } | 1176 // Note: |
915 } | 1177 // x-extent accurately measures width of glyphs. |
916 } | 1178 // y-extent is overly large because it is measured from baseline-to-baseline. |
917 | 1179 // Calling routines, such as ylabel, may need to account for this mismatch. |
918 void | 1180 |
919 ft_text_renderer::visit (text_element_list& e) | 1181 Matrix |
920 { | 1182 ft_text_renderer::get_extent (text_element *elt, double rotation) |
921 // Save and restore (after processing the list) the current font and color. | 1183 { |
922 | 1184 set_mode (MODE_BBOX); |
923 ft_font saved_font (font); | 1185 elt->accept (*this); |
924 uint8NDArray saved_color (color); | 1186 compute_bbox (); |
925 | 1187 |
926 text_processor::visit (e); | 1188 Matrix extent (1, 2, 0.0); |
927 | 1189 |
928 font = saved_font; | 1190 switch (rotation_to_mode (rotation)) |
929 color = saved_color; | 1191 { |
930 } | 1192 case ROTATION_0: |
931 | 1193 case ROTATION_180: |
932 void | 1194 extent(0) = bbox(2); |
933 ft_text_renderer::visit (text_element_subscript& e) | 1195 extent(1) = bbox(3); |
934 { | 1196 break; |
935 ft_font saved_font (font); | 1197 |
936 int saved_line_yoffset = line_yoffset; | 1198 case ROTATION_90: |
937 int saved_yoffset = yoffset; | 1199 case ROTATION_270: |
938 | 1200 extent(0) = bbox(3); |
939 set_font (font.get_name (), font.get_weight (), font.get_angle (), | 1201 extent(1) = bbox(2); |
940 font.get_size () - 2); | 1202 } |
941 | 1203 |
942 if (font.is_valid ()) | 1204 return extent; |
943 { | 1205 } |
944 int h = font.get_face ()->size->metrics.height >> 6; | 1206 |
945 | 1207 Matrix |
946 // Shifting the baseline by 2/3 the font height seems to produce | 1208 ft_text_renderer::get_extent (const std::string& txt, double rotation, |
947 // decent result. | 1209 const caseless_str& interpreter) |
948 yoffset -= (h * 2) / 3; | 1210 { |
949 | 1211 text_element *elt = text_parser::parse (txt, interpreter); |
950 if (mode == MODE_BBOX) | 1212 Matrix extent = get_extent (elt, rotation); |
951 update_line_bbox (); | 1213 delete elt; |
952 } | 1214 |
953 | 1215 return extent; |
954 text_processor::visit (e); | 1216 } |
955 | 1217 |
956 font = saved_font; | 1218 int |
957 // If line_yoffset changed, this means we moved to a new line; hence yoffset | 1219 ft_text_renderer::rotation_to_mode (double rotation) const |
958 // cannot be restored, because the saved value is not relevant anymore. | 1220 { |
959 if (line_yoffset == saved_line_yoffset) | 1221 // Clip rotation to range [0, 360] |
960 yoffset = saved_yoffset; | 1222 while (rotation < 0) |
961 } | 1223 rotation += 360.0; |
962 | 1224 while (rotation > 360.0) |
963 void | 1225 rotation -= 360.0; |
964 ft_text_renderer::visit (text_element_superscript& e) | 1226 |
965 { | 1227 if (rotation == 0.0) |
966 ft_font saved_font (font); | 1228 return ROTATION_0; |
967 int saved_line_yoffset = line_yoffset; | 1229 else if (rotation == 90.0) |
968 int saved_yoffset = yoffset; | 1230 return ROTATION_90; |
969 | 1231 else if (rotation == 180.0) |
970 set_font (font.get_name (), font.get_weight (), font.get_angle (), | 1232 return ROTATION_180; |
971 font.get_size () - 2); | 1233 else if (rotation == 270.0) |
972 | 1234 return ROTATION_270; |
973 if (saved_font.is_valid ()) | 1235 else |
974 { | 1236 return ROTATION_0; |
975 int s_asc = saved_font.get_face ()->size->metrics.ascender >> 6; | 1237 } |
976 | 1238 |
977 // Shifting the baseline by 2/3 base font ascender seems to produce | 1239 void |
978 // decent result. | 1240 ft_text_renderer::text_to_pixels (const std::string& txt, |
979 yoffset += (s_asc * 2) / 3; | 1241 uint8NDArray& pxls, Matrix& box, |
980 | 1242 int _halign, int valign, double rotation, |
981 if (mode == MODE_BBOX) | 1243 const caseless_str& interpreter, |
982 update_line_bbox (); | 1244 bool handle_rotation) |
983 } | 1245 { |
984 | 1246 int rot_mode = rotation_to_mode (rotation); |
985 text_processor::visit (e); | 1247 |
986 | 1248 halign = _halign; |
987 font = saved_font; | 1249 |
988 // If line_yoffset changed, this means we moved to a new line; hence yoffset | 1250 text_element *elt = text_parser::parse (txt, interpreter); |
989 // cannot be restored, because the saved value is not relevant anymore. | 1251 pxls = render (elt, box, rot_mode); |
990 if (line_yoffset == saved_line_yoffset) | 1252 delete elt; |
991 yoffset = saved_yoffset; | 1253 |
992 } | 1254 if (pxls.is_empty ()) |
993 | 1255 return; // nothing to render |
994 void | 1256 |
995 ft_text_renderer::visit (text_element_color& e) | 1257 switch (halign) |
996 { | 1258 { |
997 if (mode == MODE_RENDER) | 1259 case 1: |
998 set_color (e.get_color ()); | 1260 box(0) = -box(2)/2; |
999 } | 1261 break; |
1000 | 1262 |
1001 void | 1263 case 2: |
1002 ft_text_renderer::visit (text_element_fontsize& e) | 1264 box(0) = -box(2); |
1003 { | 1265 break; |
1004 double sz = e.get_fontsize (); | 1266 |
1005 | 1267 default: |
1006 // FIXME: Matlab documentation says that the font size is expressed | 1268 box(0) = 0; |
1007 // in the text object FontUnit. | 1269 break; |
1008 | 1270 } |
1009 set_font (font.get_name (), font.get_weight (), font.get_angle (), sz); | 1271 |
1010 | 1272 switch (valign) |
1011 if (mode == MODE_BBOX) | 1273 { |
1012 update_line_bbox (); | 1274 case 1: |
1013 } | 1275 box(1) = -box(3)/2; |
1014 | 1276 break; |
1015 void | 1277 |
1016 ft_text_renderer::visit (text_element_fontname& e) | 1278 case 2: |
1017 { | 1279 box(1) = -box(3); |
1018 set_font (e.get_fontname (), font.get_weight (), font.get_angle (), | 1280 break; |
1019 font.get_size ()); | 1281 |
1020 | 1282 case 3: |
1021 if (mode == MODE_BBOX) | 1283 break; |
1022 update_line_bbox (); | 1284 |
1023 } | 1285 case 4: |
1024 | 1286 box(1) = -box(3)-box(1); |
1025 void | 1287 break; |
1026 ft_text_renderer::visit (text_element_fontstyle& e) | 1288 |
1027 { | 1289 default: |
1028 switch (e.get_fontstyle ()) | 1290 box(1) = 0; |
1029 { | 1291 break; |
1030 case text_element_fontstyle::normal: | 1292 } |
1031 set_font (font.get_name (), "normal", "normal", font.get_size ()); | 1293 |
1032 break; | 1294 if (handle_rotation) |
1033 | 1295 { |
1034 case text_element_fontstyle::bold: | 1296 switch (rot_mode) |
1035 set_font (font.get_name (), "bold", "normal", font.get_size ()); | 1297 { |
1036 break; | 1298 case ROTATION_90: |
1037 | 1299 std::swap (box(0), box(1)); |
1038 case text_element_fontstyle::italic: | 1300 std::swap (box(2), box(3)); |
1039 set_font (font.get_name (), "normal", "italic", font.get_size ()); | 1301 box(0) = -box(0)-box(2); |
1040 break; | 1302 break; |
1041 | 1303 |
1042 case text_element_fontstyle::oblique: | 1304 case ROTATION_180: |
1043 set_font (font.get_name (), "normal", "oblique", font.get_size ()); | 1305 box(0) = -box(0)-box(2); |
1044 break; | 1306 box(1) = -box(1)-box(3); |
1045 } | 1307 break; |
1046 | 1308 |
1047 if (mode == MODE_BBOX) | 1309 case ROTATION_270: |
1048 update_line_bbox (); | 1310 std::swap (box(0), box(1)); |
1049 } | 1311 std::swap (box(2), box(3)); |
1050 | 1312 box(1) = -box(1)-box(3); |
1051 void | 1313 break; |
1052 ft_text_renderer::visit (text_element_symbol& e) | 1314 } |
1053 { | 1315 } |
1054 uint32_t code = e.get_symbol_code (); | 1316 } |
1055 | 1317 |
1056 text_renderer::string fs (std::string ("-"), font, xoffset, yoffset); | 1318 ft_text_renderer::ft_font::ft_font (const ft_font& ft) |
1057 | 1319 : text_renderer::font (ft), face (0) |
1058 if (code != text_element_symbol::invalid_code && font.is_valid ()) | 1320 { |
1059 { | |
1060 process_character (code); | |
1061 fs.set_code (code); | |
1062 } | |
1063 else if (font.is_valid ()) | |
1064 ::warning ("ignoring unknown symbol: %d", e.get_symbol ()); | |
1065 | |
1066 if (fs.get_code ()) | |
1067 { | |
1068 fs.set_y (line_yoffset + yoffset); | |
1069 fs.set_color (color); | |
1070 strlist.push_back (fs); | |
1071 } | |
1072 } | |
1073 | |
1074 void | |
1075 ft_text_renderer::visit (text_element_combined& e) | |
1076 { | |
1077 int saved_xoffset = xoffset; | |
1078 int max_xoffset = xoffset; | |
1079 | |
1080 for (text_element_combined::iterator it = e.begin (); it != e.end (); ++it) | |
1081 { | |
1082 xoffset = saved_xoffset; | |
1083 (*it)->accept (*this); | |
1084 max_xoffset = octave::math::max (xoffset, max_xoffset); | |
1085 } | |
1086 | |
1087 xoffset = max_xoffset; | |
1088 } | |
1089 | |
1090 void | |
1091 ft_text_renderer::reset (void) | |
1092 { | |
1093 set_mode (MODE_BBOX); | |
1094 set_color (Matrix (1, 3, 0.0)); | |
1095 } | |
1096 | |
1097 void | |
1098 ft_text_renderer::set_color (const Matrix& c) | |
1099 { | |
1100 if (c.numel () == 3) | |
1101 { | |
1102 color(0) = static_cast<uint8_t> (c(0)*255); | |
1103 color(1) = static_cast<uint8_t> (c(1)*255); | |
1104 color(2) = static_cast<uint8_t> (c(2)*255); | |
1105 } | |
1106 else | |
1107 ::warning ("ft_text_renderer::set_color: invalid color"); | |
1108 } | |
1109 | |
1110 uint8NDArray | |
1111 ft_text_renderer::render (text_element *elt, Matrix& box, int rotation) | |
1112 { | |
1113 set_mode (MODE_BBOX); | |
1114 elt->accept (*this); | |
1115 compute_bbox (); | |
1116 box = bbox; | |
1117 | |
1118 set_mode (MODE_RENDER); | |
1119 // Clear the list of parsed strings | |
1120 strlist.clear (); | |
1121 | |
1122 if (pixels.numel () > 0) | |
1123 { | |
1124 elt->accept (*this); | |
1125 | |
1126 switch (rotation) | |
1127 { | |
1128 case ROTATION_0: | |
1129 break; | |
1130 | |
1131 case ROTATION_90: | |
1132 { | |
1133 Array<octave_idx_type> perm (dim_vector (3, 1)); | |
1134 perm(0) = 0; | |
1135 perm(1) = 2; | |
1136 perm(2) = 1; | |
1137 pixels = pixels.permute (perm); | |
1138 | |
1139 Array<idx_vector> idx (dim_vector (3, 1)); | |
1140 idx(0) = idx_vector (':'); | |
1141 idx(1) = idx_vector (pixels.dim2 ()-1, -1, -1); | |
1142 idx(2) = idx_vector (':'); | |
1143 pixels = uint8NDArray (pixels.index (idx)); | |
1144 } | |
1145 break; | |
1146 | |
1147 case ROTATION_180: | |
1148 { | |
1149 Array<idx_vector> idx (dim_vector (3, 1)); | |
1150 idx(0) = idx_vector (':'); | |
1151 idx(1) = idx_vector (pixels.dim2 ()-1, -1, -1); | |
1152 idx(2) = idx_vector (pixels.dim3 ()-1, -1, -1); | |
1153 pixels = uint8NDArray (pixels.index (idx)); | |
1154 } | |
1155 break; | |
1156 | |
1157 case ROTATION_270: | |
1158 { | |
1159 Array<octave_idx_type> perm (dim_vector (3, 1)); | |
1160 perm(0) = 0; | |
1161 perm(1) = 2; | |
1162 perm(2) = 1; | |
1163 pixels = pixels.permute (perm); | |
1164 | |
1165 Array<idx_vector> idx (dim_vector (3, 1)); | |
1166 idx(0) = idx_vector (':'); | |
1167 idx(1) = idx_vector (':'); | |
1168 idx(2) = idx_vector (pixels.dim3 ()-1, -1, -1); | |
1169 pixels = uint8NDArray (pixels.index (idx)); | |
1170 } | |
1171 break; | |
1172 } | |
1173 } | |
1174 | |
1175 return pixels; | |
1176 } | |
1177 | |
1178 // Note: | |
1179 // x-extent accurately measures width of glyphs. | |
1180 // y-extent is overly large because it is measured from baseline-to-baseline. | |
1181 // Calling routines, such as ylabel, may need to account for this mismatch. | |
1182 | |
1183 Matrix | |
1184 ft_text_renderer::get_extent (text_element *elt, double rotation) | |
1185 { | |
1186 set_mode (MODE_BBOX); | |
1187 elt->accept (*this); | |
1188 compute_bbox (); | |
1189 | |
1190 Matrix extent (1, 2, 0.0); | |
1191 | |
1192 switch (rotation_to_mode (rotation)) | |
1193 { | |
1194 case ROTATION_0: | |
1195 case ROTATION_180: | |
1196 extent(0) = bbox(2); | |
1197 extent(1) = bbox(3); | |
1198 break; | |
1199 | |
1200 case ROTATION_90: | |
1201 case ROTATION_270: | |
1202 extent(0) = bbox(3); | |
1203 extent(1) = bbox(2); | |
1204 } | |
1205 | |
1206 return extent; | |
1207 } | |
1208 | |
1209 Matrix | |
1210 ft_text_renderer::get_extent (const std::string& txt, double rotation, | |
1211 const caseless_str& interpreter) | |
1212 { | |
1213 text_element *elt = text_parser::parse (txt, interpreter); | |
1214 Matrix extent = get_extent (elt, rotation); | |
1215 delete elt; | |
1216 | |
1217 return extent; | |
1218 } | |
1219 | |
1220 int | |
1221 ft_text_renderer::rotation_to_mode (double rotation) const | |
1222 { | |
1223 // Clip rotation to range [0, 360] | |
1224 while (rotation < 0) | |
1225 rotation += 360.0; | |
1226 while (rotation > 360.0) | |
1227 rotation -= 360.0; | |
1228 | |
1229 if (rotation == 0.0) | |
1230 return ROTATION_0; | |
1231 else if (rotation == 90.0) | |
1232 return ROTATION_90; | |
1233 else if (rotation == 180.0) | |
1234 return ROTATION_180; | |
1235 else if (rotation == 270.0) | |
1236 return ROTATION_270; | |
1237 else | |
1238 return ROTATION_0; | |
1239 } | |
1240 | |
1241 void | |
1242 ft_text_renderer::text_to_pixels (const std::string& txt, | |
1243 uint8NDArray& pxls, Matrix& box, | |
1244 int _halign, int valign, double rotation, | |
1245 const caseless_str& interpreter, | |
1246 bool handle_rotation) | |
1247 { | |
1248 int rot_mode = rotation_to_mode (rotation); | |
1249 | |
1250 halign = _halign; | |
1251 | |
1252 text_element *elt = text_parser::parse (txt, interpreter); | |
1253 pxls = render (elt, box, rot_mode); | |
1254 delete elt; | |
1255 | |
1256 if (pxls.is_empty ()) | |
1257 return; // nothing to render | |
1258 | |
1259 switch (halign) | |
1260 { | |
1261 case 1: | |
1262 box(0) = -box(2)/2; | |
1263 break; | |
1264 | |
1265 case 2: | |
1266 box(0) = -box(2); | |
1267 break; | |
1268 | |
1269 default: | |
1270 box(0) = 0; | |
1271 break; | |
1272 } | |
1273 | |
1274 switch (valign) | |
1275 { | |
1276 case 1: | |
1277 box(1) = -box(3)/2; | |
1278 break; | |
1279 | |
1280 case 2: | |
1281 box(1) = -box(3); | |
1282 break; | |
1283 | |
1284 case 3: | |
1285 break; | |
1286 | |
1287 case 4: | |
1288 box(1) = -box(3)-box(1); | |
1289 break; | |
1290 | |
1291 default: | |
1292 box(1) = 0; | |
1293 break; | |
1294 } | |
1295 | |
1296 if (handle_rotation) | |
1297 { | |
1298 switch (rot_mode) | |
1299 { | |
1300 case ROTATION_90: | |
1301 std::swap (box(0), box(1)); | |
1302 std::swap (box(2), box(3)); | |
1303 box(0) = -box(0)-box(2); | |
1304 break; | |
1305 | |
1306 case ROTATION_180: | |
1307 box(0) = -box(0)-box(2); | |
1308 box(1) = -box(1)-box(3); | |
1309 break; | |
1310 | |
1311 case ROTATION_270: | |
1312 std::swap (box(0), box(1)); | |
1313 std::swap (box(2), box(3)); | |
1314 box(1) = -box(1)-box(3); | |
1315 break; | |
1316 } | |
1317 } | |
1318 } | |
1319 | |
1320 ft_text_renderer::ft_font::ft_font (const ft_font& ft) | |
1321 : text_renderer::font (ft), face (0) | |
1322 { | |
1323 #if defined (HAVE_FT_REFERENCE_FACE) | 1321 #if defined (HAVE_FT_REFERENCE_FACE) |
1324 FT_Face ft_face = ft.get_face (); | 1322 FT_Face ft_face = ft.get_face (); |
1325 | 1323 |
1326 if (ft_face && FT_Reference_Face (ft_face) == 0) | 1324 if (ft_face && FT_Reference_Face (ft_face) == 0) |
1327 face = ft_face; | 1325 face = ft_face; |
1328 #endif | 1326 #endif |
1329 } | 1327 } |
1330 | 1328 |
1331 ft_text_renderer::ft_font& | 1329 ft_text_renderer::ft_font& |
1332 ft_text_renderer::ft_font::operator = (const ft_font& ft) | 1330 ft_text_renderer::ft_font::operator = (const ft_font& ft) |
1333 { | 1331 { |
1334 if (&ft != this) | 1332 if (&ft != this) |
1335 { | 1333 { |
1336 text_renderer::font::operator = (ft); | 1334 text_renderer::font::operator = (ft); |
1337 | 1335 |
1338 if (face) | 1336 if (face) |
1339 { | 1337 { |
1340 FT_Done_Face (face); | 1338 FT_Done_Face (face); |
1341 face = 0; | 1339 face = 0; |
1342 } | 1340 } |
1343 | 1341 |
1344 #if defined (HAVE_FT_REFERENCE_FACE) | 1342 #if defined (HAVE_FT_REFERENCE_FACE) |
1345 FT_Face ft_face = ft.get_face (); | 1343 FT_Face ft_face = ft.get_face (); |
1346 | 1344 |
1347 if (ft_face && FT_Reference_Face (ft_face) == 0) | 1345 if (ft_face && FT_Reference_Face (ft_face) == 0) |
1348 face = ft_face; | 1346 face = ft_face; |
1349 #endif | 1347 #endif |
1350 } | 1348 } |
1351 | 1349 |
1352 return *this; | 1350 return *this; |
1353 } | 1351 } |
1354 | 1352 |
1355 FT_Face | 1353 FT_Face |
1356 ft_text_renderer::ft_font::get_face (void) const | 1354 ft_text_renderer::ft_font::get_face (void) const |
1357 { | 1355 { |
1358 if (! face && ! name.empty ()) | 1356 if (! face && ! name.empty ()) |
1359 { | 1357 { |
1360 face = ft_manager::get_font (name, weight, angle, size); | 1358 face = ft_manager::get_font (name, weight, angle, size); |
1361 | 1359 |
1362 if (face) | 1360 if (face) |
1363 { | 1361 { |
1364 if (FT_Set_Char_Size (face, 0, size*64, 0, 0)) | 1362 if (FT_Set_Char_Size (face, 0, size*64, 0, 0)) |
1365 ::warning ("ft_text_renderer: unable to set font size to %g", size); | 1363 ::warning ("ft_text_renderer: unable to set font size to %g", size); |
1366 } | 1364 } |
1367 else | 1365 else |
1368 ::warning ("ft_text_renderer: unable to load appropriate font"); | 1366 ::warning ("ft_text_renderer: unable to load appropriate font"); |
1369 } | 1367 } |
1370 | 1368 |
1371 return face; | 1369 return face; |
1372 } | 1370 } |
1373 | |
1374 } | 1371 } |
1375 | 1372 |
1376 #endif | 1373 #endif |
1377 | 1374 |
1378 namespace octave | 1375 namespace octave |