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