nss: upgrade to release 3.73
[LibreOffice.git] / vcl / unx / generic / gdi / cairotextrender.cxx
blobd3fd5f753983be1e10bf4af22613b445956685d5
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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>
27 #include <cairo.h>
28 #include <cairo-ft.h>
30 namespace {
32 typedef struct FT_FaceRec_* FT_Face;
34 class CairoFontsCache
36 public:
37 struct CacheId
39 FT_Face maFace;
40 const FontConfigFontOptions *mpOptions;
41 bool mbEmbolden;
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;
52 private:
53 typedef std::deque< std::pair<void *, CacheId> > LRUFonts;
54 static LRUFonts maLRUFonts;
55 public:
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())
79 return aI->first;
80 return nullptr;
85 namespace
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);
111 return cr;
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 );
124 Point aPos;
125 const GlyphItem* pGlyph;
126 int nStart = 0;
127 while (rLayout.GetNextGlyph(&pGlyph, aPos, nStart))
129 cairo_glyph_t aGlyph;
130 aGlyph.index = pGlyph->glyphId();
131 aGlyph.x = aPos.X();
132 aGlyph.y = aPos.Y();
133 cairo_glyphs.push_back(aGlyph);
135 if (pGlyph->IsVertical())
136 glyph_extrarotation.push_back(1);
137 else
138 glyph_extrarotation.push_back(0);
141 if (cairo_glyphs.empty())
142 return;
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)
148 return;
150 int nRatio = nWidth * 10 / nHeight;
151 if (FreetypeFont::AlmostHorizontalDrainsRenderingPool(nRatio, rFSD))
152 return;
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());
161 if (!cr)
163 SAL_WARN("vcl", "no cairo context for text");
164 return;
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);
180 else
181 cairo_set_font_options(cr, pFontOptions);
184 double nDX, nDY;
185 getSurfaceOffset(nDX, nDY);
186 cairo_translate(cr, nDX, nDY);
188 clipRegion(cr);
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;
197 aId.maFace = aFace;
198 aId.mpOptions = rFont.GetFontOptions();
199 aId.mbEmbolden = rFont.NeedsArtificialBold();
201 cairo_matrix_t m;
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;
206 while (aI != aEnd)
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));
217 if (!font_face)
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);
235 if (nGlyphRotation)
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
254 //like them
255 double xdiff = 0.0;
256 double ydiff = 0.0;
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
286 //draw origin
287 cairo_save (cr);
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);
290 cairo_fill (cr);
291 cairo_restore (cr);
292 #endif
294 aI = aNext;
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();
304 if( !pFontOptions )
305 return;
306 cairo_ft_font_options_substitute(pFontOptions, pPattern);
309 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */