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 728 of file KoSvgTextShape_p_glyphs.cpp.

729{
730 KIS_ASSERT(glyphSlot->bitmap.width <= INT32_MAX);
731 KIS_ASSERT(glyphSlot->bitmap.rows <= INT32_MAX);
732 QImage img;
733 const int height = static_cast<int>(glyphSlot->bitmap.rows);
734 const QSize size(static_cast<int>(glyphSlot->bitmap.width), height);
735
736 if (glyphSlot->bitmap.pixel_mode == FT_PIXEL_MODE_MONO) {
737 img = QImage(size, QImage::Format_Mono);
738 uchar *src = glyphSlot->bitmap.buffer;
739 KIS_ASSERT(glyphSlot->bitmap.pitch >= 0);
740 for (int y = 0; y < height; y++) {
741 memcpy(img.scanLine(y), src, static_cast<size_t>(glyphSlot->bitmap.pitch));
742 src += glyphSlot->bitmap.pitch;
743 }
744 } else if (glyphSlot->bitmap.pixel_mode == FT_PIXEL_MODE_GRAY) {
745 img = QImage(size, QImage::Format_Grayscale8);
746 uchar *src = glyphSlot->bitmap.buffer;
747 KIS_ASSERT(glyphSlot->bitmap.pitch >= 0);
748 for (int y = 0; y < height; y++) {
749 memcpy(img.scanLine(y), src, static_cast<size_t>(glyphSlot->bitmap.pitch));
750 src += glyphSlot->bitmap.pitch;
751 }
752 } else if (glyphSlot->bitmap.pixel_mode == FT_PIXEL_MODE_BGRA) {
753 img = QImage(size, QImage::Format_ARGB32_Premultiplied);
754 const uint8_t *src = glyphSlot->bitmap.buffer;
755 for (int y = 0; y < height; y++) {
756 auto *argb = reinterpret_cast<QRgb *>(img.scanLine(y));
757 for (unsigned int x = 0; x < glyphSlot->bitmap.width; x++) {
758 argb[x] = qRgba(src[2], src[1], src[0], src[3]);
759 src += 4;
760 }
761 }
762 }
763
764 return img;
765}
#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 638 of file KoSvgTextShape_p_glyphs.cpp.

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

◆ 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.