1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <unx/cairotextrender.hxx>
22 #include <unx/fc_fontoptions.hxx>
23 #include <unx/freetype_glyphcache.hxx>
24 #include <vcl/svapp.hxx>
25 #include <sallayout.hxx>
32 typedef struct FT_FaceRec_
* FT_Face
;
40 const FontConfigFontOptions
*mpOptions
;
42 bool mbVerticalMetrics
;
43 bool operator ==(const CacheId
& rOther
) const
45 return maFace
== rOther
.maFace
&&
46 mpOptions
== rOther
.mpOptions
&&
47 mbEmbolden
== rOther
.mbEmbolden
&&
48 mbVerticalMetrics
== rOther
.mbVerticalMetrics
;
53 typedef std::deque
< std::pair
<void *, CacheId
> > LRUFonts
;
54 static LRUFonts maLRUFonts
;
56 CairoFontsCache() = delete;
58 static void CacheFont(void *pFont
, const CacheId
&rId
);
59 static void* FindCachedFont(const CacheId
&rId
);
62 CairoFontsCache::LRUFonts
CairoFontsCache::maLRUFonts
;
64 void CairoFontsCache::CacheFont(void *pFont
, const CairoFontsCache::CacheId
&rId
)
66 maLRUFonts
.push_front( std::pair
<void*, CairoFontsCache::CacheId
>(pFont
, rId
) );
67 if (maLRUFonts
.size() > 8)
69 cairo_font_face_destroy(static_cast<cairo_font_face_t
*>(maLRUFonts
.back().first
));
70 maLRUFonts
.pop_back();
74 void* CairoFontsCache::FindCachedFont(const CairoFontsCache::CacheId
&rId
)
76 auto aI
= std::find_if(maLRUFonts
.begin(), maLRUFonts
.end(),
77 [&rId
](const LRUFonts::value_type
& rFont
) { return rFont
.second
== rId
; });
78 if (aI
!= maLRUFonts
.end())
87 bool hasRotation(int nRotation
)
89 return nRotation
!= 0;
92 double toRadian(Degree10 nDegree10th
)
94 return (Degree10(3600) - nDegree10th
).get() * M_PI
/ 1800.0;
97 cairo_t
* syncCairoContext(cairo_t
* cr
)
99 //rhbz#1283420 tdf#117413 bodge to force a read from the underlying surface which has
100 //the side effect of making the mysterious xrender related problem go away
101 cairo_surface_t
*target
= cairo_get_target(cr
);
102 if (cairo_surface_get_type(target
) == CAIRO_SURFACE_TYPE_XLIB
)
104 cairo_surface_t
*throw_away
= cairo_surface_create_similar(target
, cairo_surface_get_content(target
), 1, 1);
105 cairo_t
*force_read_cr
= cairo_create(throw_away
);
106 cairo_set_source_surface(force_read_cr
, target
, 0, 0);
107 cairo_paint(force_read_cr
);
108 cairo_destroy(force_read_cr
);
109 cairo_surface_destroy(throw_away
);
115 void CairoTextRender::DrawTextLayout(const GenericSalLayout
& rLayout
, const SalGraphics
& rGraphics
)
117 const FreetypeFontInstance
& rInstance
= static_cast<FreetypeFontInstance
&>(rLayout
.GetFont());
118 const FreetypeFont
& rFont
= rInstance
.GetFreetypeFont();
120 std::vector
<cairo_glyph_t
> cairo_glyphs
;
121 std::vector
<int> glyph_extrarotation
;
122 cairo_glyphs
.reserve( 256 );
125 const GlyphItem
* pGlyph
;
127 while (rLayout
.GetNextGlyph(&pGlyph
, aPos
, nStart
))
129 cairo_glyph_t aGlyph
;
130 aGlyph
.index
= pGlyph
->glyphId();
133 cairo_glyphs
.push_back(aGlyph
);
135 if (pGlyph
->IsVertical())
136 glyph_extrarotation
.push_back(1);
138 glyph_extrarotation
.push_back(0);
141 if (cairo_glyphs
.empty())
144 const FontSelectPattern
& rFSD
= rInstance
.GetFontSelectPattern();
145 int nHeight
= rFSD
.mnHeight
;
146 int nWidth
= rFSD
.mnWidth
? rFSD
.mnWidth
: nHeight
;
147 if (nWidth
== 0 || nHeight
== 0)
150 int nRatio
= nWidth
* 10 / nHeight
;
151 if (FreetypeFont::AlmostHorizontalDrainsRenderingPool(nRatio
, rFSD
))
155 * It might be ideal to cache surface and cairo context between calls and
156 * only destroy it when the drawable changes, but to do that we need to at
157 * least change the SalFrame etc impls to dtor the SalGraphics *before* the
158 * destruction of the windows they reference
160 cairo_t
*cr
= syncCairoContext(getCairoContext());
163 SAL_WARN("vcl", "no cairo context for text");
167 ImplSVData
* pSVData
= ImplGetSVData();
168 if (const cairo_font_options_t
* pFontOptions
= pSVData
->mpDefInst
->GetCairoFontOptions())
170 const StyleSettings
& rStyleSettings
= Application::GetSettings().GetStyleSettings();
171 if (!rStyleSettings
.GetUseFontAAFromSystem() && !rGraphics
.getAntiAlias())
173 // Disable font AA in case global AA setting is supposed to affect
174 // font rendering (not the default) and AA is disabled.
175 cairo_font_options_t
* pOptions
= cairo_font_options_copy(pFontOptions
);
176 cairo_font_options_set_antialias(pOptions
, CAIRO_ANTIALIAS_NONE
);
177 cairo_set_font_options(cr
, pOptions
);
178 cairo_font_options_destroy(pOptions
);
181 cairo_set_font_options(cr
, pFontOptions
);
185 getSurfaceOffset(nDX
, nDY
);
186 cairo_translate(cr
, nDX
, nDY
);
190 cairo_set_source_rgb(cr
,
191 mnTextColor
.GetRed()/255.0,
192 mnTextColor
.GetGreen()/255.0,
193 mnTextColor
.GetBlue()/255.0);
195 FT_Face aFace
= rFont
.GetFtFace();
196 CairoFontsCache::CacheId aId
;
198 aId
.mpOptions
= rFont
.GetFontOptions();
199 aId
.mbEmbolden
= rFont
.NeedsArtificialBold();
203 std::vector
<int>::const_iterator aEnd
= glyph_extrarotation
.end();
204 std::vector
<int>::const_iterator aStart
= glyph_extrarotation
.begin();
205 std::vector
<int>::const_iterator aI
= aStart
;
208 int nGlyphRotation
= *aI
;
210 std::vector
<int>::const_iterator aNext
= nGlyphRotation
?(aI
+1):std::find_if(aI
+1, aEnd
, hasRotation
);
212 size_t nStartIndex
= std::distance(aStart
, aI
);
213 size_t nLen
= std::distance(aI
, aNext
);
215 aId
.mbVerticalMetrics
= nGlyphRotation
!= 0.0;
216 cairo_font_face_t
* font_face
= static_cast<cairo_font_face_t
*>(CairoFontsCache::FindCachedFont(aId
));
219 const FontConfigFontOptions
*pOptions
= aId
.mpOptions
;
220 FcPattern
*pPattern
= pOptions
->GetPattern();
221 font_face
= cairo_ft_font_face_create_for_pattern(pPattern
);
222 CairoFontsCache::CacheFont(font_face
, aId
);
224 cairo_set_font_face(cr
, font_face
);
226 cairo_set_font_size(cr
, nHeight
);
228 cairo_matrix_init_identity(&m
);
230 if (rLayout
.GetOrientation())
231 cairo_matrix_rotate(&m
, toRadian(rLayout
.GetOrientation()));
233 cairo_matrix_scale(&m
, nWidth
, nHeight
);
237 cairo_matrix_rotate(&m
, toRadian(Degree10(nGlyphRotation
* 900)));
239 cairo_matrix_t em_square
;
240 cairo_matrix_init_identity(&em_square
);
241 cairo_get_matrix(cr
, &em_square
);
243 cairo_matrix_scale(&em_square
, aFace
->units_per_EM
,
244 aFace
->units_per_EM
);
245 cairo_set_matrix(cr
, &em_square
);
247 cairo_font_extents_t font_extents
;
248 cairo_font_extents(cr
, &font_extents
);
250 cairo_matrix_init_identity(&em_square
);
251 cairo_set_matrix(cr
, &em_square
);
253 //gives the same positions as pre-cairo conversion, but I don't
258 // The y is the origin point position, but Cairo will draw
259 // the glyph *above* that point, we need to move it down to
260 // the glyph’s baseline.
261 cairo_text_extents_t aExt
;
262 cairo_glyph_extents(cr
, &cairo_glyphs
[nStartIndex
], nLen
, &aExt
);
263 double nDescender
= std::fmax(aExt
.height
+ aExt
.y_bearing
, 0);
264 ydiff
= (aExt
.x_advance
- nDescender
) / nHeight
;
265 xdiff
= -font_extents
.descent
/nHeight
;
267 cairo_matrix_translate(&m
, xdiff
, ydiff
);
270 if (rFont
.NeedsArtificialItalic())
272 cairo_matrix_t shear
;
273 cairo_matrix_init_identity(&shear
);
274 shear
.xy
= -shear
.xx
* 0x6000L
/ 0x10000L
;
275 cairo_matrix_multiply(&m
, &shear
, &m
);
278 cairo_set_font_matrix(cr
, &m
);
279 cairo_show_glyphs(cr
, &cairo_glyphs
[nStartIndex
], nLen
);
280 if (cairo_status(cr
) != CAIRO_STATUS_SUCCESS
)
282 SAL_WARN("vcl", "rendering text failed with stretch ratio of: " << nRatio
<< ", " << cairo_status_to_string(cairo_status(cr
)));
285 #if OSL_DEBUG_LEVEL > 2
288 cairo_rectangle (cr
, cairo_glyphs
[nStartIndex
].x
, cairo_glyphs
[nStartIndex
].y
, 5, 5);
289 cairo_set_source_rgba (cr
, 1, 0, 0, 0.80);
297 releaseCairoContext(cr
);
300 void FontConfigFontOptions::cairo_font_options_substitute(FcPattern
* pPattern
)
302 ImplSVData
* pSVData
= ImplGetSVData();
303 const cairo_font_options_t
* pFontOptions
= pSVData
->mpDefInst
->GetCairoFontOptions();
306 cairo_ft_font_options_substitute(pFontOptions
, pPattern
);
309 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */