Krita Source Code Documentation
Loading...
Searching...
No Matches
KoSvgTextShape_p_glyphs.cpp File Reference
#include "KoSvgTextShape.h"
#include "KoSvgTextShape_p.h"
#include "KisTofuGlyph.h"
#include "KoFontLibraryResourceUtils.h"
#include <FlakeDebug.h>
#include <KoPathShape.h>
#include <kis_global.h>
#include <QPainterPath>
#include <QtMath>
#include <utility>
#include <variant>
#include <ft2build.h>
#include <hb.h>
#include <hb-ft.h>
#include <raqm.h>

Go to the source code of this file.

Classes

class  ColorLayersLoader
 Helper class to load CPAL/COLR v0 color layers, functionally based off the sample code in the freetype docs. More...
 

Functions

static std::pair< QTransform, QTransform > calcOutlineGlyphTransform (const QTransform &ftTF, const raqm_glyph_t &currentGlyph, const CharacterResult &charResult, const bool isHorizontal)
 Calculate the transformation matrices for an outline glyph, taking synthesized italic into account.
 
static QImage convertFromFreeTypeBitmap (FT_GlyphSlotRec *glyphSlot)
 
static QPainterPath convertFromFreeTypeOutline (FT_GlyphSlotRec *glyphSlot)
 
static void emboldenGlyphIfNeeded (const FT_Face ftface, const CharacterResult &charResult, int *x_advance, int *y_advance)
 Embolden a glyph (synthesize bold) if the font does not have native bold.
 
bool faceIsItalic (const FT_Face face)
 
static QString glyphFormatToStr (const FT_Glyph_Format _v)
 

Function Documentation

◆ calcOutlineGlyphTransform()

static std::pair< QTransform, QTransform > calcOutlineGlyphTransform ( const QTransform & ftTF,
const raqm_glyph_t & currentGlyph,
const CharacterResult & charResult,
const bool isHorizontal )
static

Calculate the transformation matrices for an outline glyph, taking synthesized italic into account.

Parameters
ftTFFT unit to 1/72 unit
currentGlyph
charResult
isHorizontal
Returns
std::pair<QTransform, QTransform> {outlineGlyphTf, glyphObliqueTf}

Definition at line 147 of file KoSvgTextShape_p_glyphs.cpp.

151{
152 QTransform outlineGlyphTf = QTransform::fromTranslate(currentGlyph.x_offset, currentGlyph.y_offset);
153 QTransform glyphObliqueTf;
154
155 // Check whether we need to synthesize italic by shearing the glyph:
156 if (charResult.fontStyle != QFont::StyleNormal && !faceIsItalic(currentGlyph.ftface)) {
157 // CSS Fonts Module Level 4, 2.4. Font style: the font-style property:
158 // For `oblique`, "lack of an <angle> represents 14deg".
159 constexpr double SLANT_14DEG = 0.24932800284318069162403993780486;
160 if (isHorizontal) {
161 glyphObliqueTf.shear(SLANT_14DEG, 0);
162 } else {
163 // For vertical mode, CSSWG says:
164 // - Skew around the centre
165 // - Right-side down and left-side up
166 // https://github.com/w3c/csswg-drafts/issues/2869
167 glyphObliqueTf.shear(0, -SLANT_14DEG);
168 }
169 outlineGlyphTf *= glyphObliqueTf;
170 }
171 outlineGlyphTf *= ftTF;
172 return {outlineGlyphTf, glyphObliqueTf};
173}
bool faceIsItalic(const FT_Face face)
QFont::Style fontStyle

References faceIsItalic(), and CharacterResult::fontStyle.

◆ convertFromFreeTypeBitmap()

static QImage convertFromFreeTypeBitmap ( FT_GlyphSlotRec * glyphSlot)
static

Definition at line 732 of file KoSvgTextShape_p_glyphs.cpp.

733{
734 KIS_ASSERT(glyphSlot->bitmap.width <= INT32_MAX);
735 KIS_ASSERT(glyphSlot->bitmap.rows <= INT32_MAX);
736 QImage img;
737 const int height = static_cast<int>(glyphSlot->bitmap.rows);
738 const QSize size(static_cast<int>(glyphSlot->bitmap.width), height);
739
740 if (glyphSlot->bitmap.pixel_mode == FT_PIXEL_MODE_MONO) {
741 img = QImage(size, QImage::Format_Mono);
742 uchar *src = glyphSlot->bitmap.buffer;
743 KIS_ASSERT(glyphSlot->bitmap.pitch >= 0);
744 for (int y = 0; y < height; y++) {
745 memcpy(img.scanLine(y), src, static_cast<size_t>(glyphSlot->bitmap.pitch));
746 src += glyphSlot->bitmap.pitch;
747 }
748 } else if (glyphSlot->bitmap.pixel_mode == FT_PIXEL_MODE_GRAY) {
749 img = QImage(size, QImage::Format_Grayscale8);
750 uchar *src = glyphSlot->bitmap.buffer;
751 KIS_ASSERT(glyphSlot->bitmap.pitch >= 0);
752 for (int y = 0; y < height; y++) {
753 memcpy(img.scanLine(y), src, static_cast<size_t>(glyphSlot->bitmap.pitch));
754 src += glyphSlot->bitmap.pitch;
755 }
756 } else if (glyphSlot->bitmap.pixel_mode == FT_PIXEL_MODE_BGRA) {
757 img = QImage(size, QImage::Format_ARGB32_Premultiplied);
758 const uint8_t *src = glyphSlot->bitmap.buffer;
759 for (int y = 0; y < height; y++) {
760 auto *argb = reinterpret_cast<QRgb *>(img.scanLine(y));
761 for (unsigned int x = 0; x < glyphSlot->bitmap.width; x++) {
762 argb[x] = qRgba(src[2], src[1], src[0], src[3]);
763 src += 4;
764 }
765 }
766 }
767
768 return img;
769}
#define KIS_ASSERT(cond)
Definition kis_assert.h:33
unsigned int QRgb
int size(const Forest< T > &forest)
Definition KisForest.h:1232

References KIS_ASSERT, and KoShape::size().

◆ convertFromFreeTypeOutline()

static QPainterPath convertFromFreeTypeOutline ( FT_GlyphSlotRec * glyphSlot)
static

Definition at line 642 of file KoSvgTextShape_p_glyphs.cpp.

643{
644 QPointF cp = QPointF();
645 // convert the outline to a painter path
646 // This is taken from qfontengine_ft.cpp.
647 QPainterPath glyph;
648 glyph.setFillRule(Qt::WindingFill);
649 int i = 0;
650 for (int j = 0; j < glyphSlot->outline.n_contours; ++j) {
651 int last_point = glyphSlot->outline.contours[j];
652 // qDebug() << "contour:" << i << "to" << last_point;
653 QPointF start = QPointF(glyphSlot->outline.points[i].x, glyphSlot->outline.points[i].y);
654 if (!(glyphSlot->outline.tags[i] & 1)) { // start point is not on curve:
655 if (!(glyphSlot->outline.tags[last_point] & 1)) { // end point is not on curve:
656 // qDebug() << " start and end point are not on curve";
657 start = (QPointF(glyphSlot->outline.points[last_point].x, glyphSlot->outline.points[last_point].y) + start) / 2.0;
658 } else {
659 // qDebug() << " end point is on curve, start is not";
660 start = QPointF(glyphSlot->outline.points[last_point].x, glyphSlot->outline.points[last_point].y);
661 }
662 --i; // to use original start point as control point below
663 }
664 start += cp;
665 // qDebug() << " start at" << start;
666 glyph.moveTo(start);
667 std::array<QPointF, 4> curve;
668 curve[0] = start;
669 size_t n = 1;
670 while (i < last_point) {
671 ++i;
672 curve.at(n) = cp + QPointF(glyphSlot->outline.points[i].x, glyphSlot->outline.points[i].y);
673 // qDebug() << " " << i << c[n] << "tag =" <<
674 // (int)g->outline.tags[i]
675 // << ": on curve =" << (bool)(g->outline.tags[i]
676 // & 1);
677 ++n;
678 switch (glyphSlot->outline.tags[i] & 3) {
679 case 2:
680 // cubic bezier element
681 if (n < 4)
682 continue;
683 curve[3] = (curve[3] + curve[2]) / 2;
684 --i;
685 break;
686 case 0:
687 // quadratic bezier element
688 if (n < 3)
689 continue;
690 curve[3] = (curve[1] + curve[2]) / 2;
691 curve[2] = (2 * curve[1] + curve[3]) / 3;
692 curve[1] = (2 * curve[1] + curve[0]) / 3;
693 --i;
694 break;
695 case 1:
696 case 3:
697 if (n == 2) {
698 // qDebug() << " lineTo" << c[1];
699 glyph.lineTo(curve[1]);
700 curve[0] = curve[1];
701 n = 1;
702 continue;
703 } else if (n == 3) {
704 curve[3] = curve[2];
705 curve[2] = (2 * curve[1] + curve[3]) / 3;
706 curve[1] = (2 * curve[1] + curve[0]) / 3;
707 }
708 break;
709 }
710 // qDebug() << " cubicTo" << c[1] << c[2] << c[3];
711 glyph.cubicTo(curve[1], curve[2], curve[3]);
712 curve[0] = curve[3];
713 n = 1;
714 }
715 if (n == 1) {
716 // qDebug() << " closeSubpath";
717 glyph.closeSubpath();
718 } else {
719 curve[3] = start;
720 if (n == 2) {
721 curve[2] = (2 * curve[1] + curve[3]) / 3;
722 curve[1] = (2 * curve[1] + curve[0]) / 3;
723 }
724 // qDebug() << " close cubicTo" << c[1] << c[2] << c[3];
725 glyph.cubicTo(curve[1], curve[2], curve[3]);
726 }
727 ++i;
728 }
729 return glyph;
730}

◆ emboldenGlyphIfNeeded()

static void emboldenGlyphIfNeeded ( const FT_Face ftface,
const CharacterResult & charResult,
int * x_advance,
int * y_advance )
static

Embolden a glyph (synthesize bold) if the font does not have native bold.

Parameters
ftface
charResult
x_advancePointer to the X advance to be adjusted if needed.
y_advancePointer to the Y advance to be adjusted if needed.

Definition at line 62 of file KoSvgTextShape_p_glyphs.cpp.

63{
64 constexpr int WEIGHT_SEMIBOLD = 600;
65 if (charResult.fontWeight >= WEIGHT_SEMIBOLD) {
66 // Simplest check: Bold fonts don't need to be embolden.
67 if (ftface->style_flags & FT_STYLE_FLAG_BOLD) {
68 return;
69 }
70
71 // Variable fnots also don't need to be embolden.
72 if (FT_HAS_MULTIPLE_MASTERS(ftface)) {
73 return;
74 }
75
76 // Some heavy weight classes don't cause FT_STYLE_FLAG_BOLD to be set,
77 // so we have to check the OS/2 and STAT table for its weight class to be sure.
78 hb_font_t_sp hbFont(hb_ft_font_create_referenced(ftface));
79 if (hb_style_get_value(hbFont.data(), HB_STYLE_TAG_WEIGHT) >= WEIGHT_SEMIBOLD) {
80 return;
81 }
82
83 // This code is somewhat inspired by Firefox.
84 FT_Pos strength =
85 FT_MulFix(ftface->units_per_EM, ftface->size->metrics.y_scale) / 48;
86
87 if (ftface->glyph->format == FT_GLYPH_FORMAT_BITMAP) {
88 // This is similar to what FT_GlyphSlot_Embolden does.
89
90 // Round down to full pixel.
91 strength &= ~63;
92 if (strength == 0) {
93 // ... but it has to be at least one pixel.
94 strength = 64;
95 }
96
97 FT_GlyphSlot_Own_Bitmap(ftface->glyph);
98
99 // Embolden less vertically than horizontally. Especially if
100 // strength is only 1px, don't embolden vertically at all.
101 // Otherwise it makes the glyph way too heavy, especially for
102 // CJK glyphs in small sizes.
103 const FT_Pos strengthY = strength - 64;
104 FT_Bitmap_Embolden(ftface->glyph->library, &ftface->glyph->bitmap, strength, strengthY);
105
106 if (x_advance && *x_advance != 0) {
107 *x_advance += strength;
108 }
109 if (y_advance && *y_advance != 0) {
110 *y_advance -= strengthY;
111 }
112 } else {
113 FT_Outline_Embolden(&ftface->glyph->outline, strength);
114
115 if (x_advance && *x_advance != 0) {
116 *x_advance += strength;
117 }
118 if (y_advance && *y_advance != 0) {
119 *y_advance -= strength;
120 }
121 }
122 }
123}

References KisLibraryResourcePointer< T, P >::data(), and CharacterResult::fontWeight.

◆ faceIsItalic()

bool faceIsItalic ( const FT_Face face)

Definition at line 125 of file KoSvgTextShape_p_glyphs.cpp.

125 {
126 hb_font_t_sp hbFont(hb_ft_font_create_referenced(face));
127 bool isItalic = hb_style_get_value(hbFont.data(), HB_STYLE_TAG_ITALIC) > 0;
128 bool isOblique = false;
129
130 if (FT_HAS_MULTIPLE_MASTERS(face)) {
131 isOblique = hb_style_get_value(hbFont.data(), HB_STYLE_TAG_SLANT_ANGLE) != 0;
132 }
133
134 return isItalic || isOblique;
135}

References KisLibraryResourcePointer< T, P >::data().

◆ glyphFormatToStr()

static QString glyphFormatToStr ( const FT_Glyph_Format _v)
static

Definition at line 41 of file KoSvgTextShape_p_glyphs.cpp.

42{
43 const unsigned int v = _v;
44 QString s;
45 s += QChar((v >> 24) & 0xFF);
46 s += QChar((v >> 16) & 0xFF);
47 s += QChar((v >> 8) & 0xFF);
48 s += QChar((v >> 0) & 0xFF);
49 return s;
50}
qreal v

References v.