xref: /haiku/src/servers/app/drawing/Painter/AGGTextRenderer.cpp (revision 820dca4df6c7bf955c46e8f6521b9408f50b2900)
1 /*
2  * Copyright 2005-2009, Stephan Aßmus <superstippi@gmx.de>.
3  * Copyright 2008, Andrej Spielmann <andrej.spielmann@seh.ox.ac.uk>.
4  * All rights reserved. Distributed under the terms of the MIT License.
5  */
6 
7 #include "AGGTextRenderer.h"
8 
9 #include <agg_basics.h>
10 #include <agg_bounding_rect.h>
11 #include <agg_conv_segmentator.h>
12 #include <agg_conv_transform.h>
13 #include <agg_trans_affine.h>
14 
15 #include <math.h>
16 #include <malloc.h>
17 #include <stdio.h>
18 #include <string.h>
19 
20 #define SHOW_GLYPH_BOUNDS 0
21 
22 #if SHOW_GLYPH_BOUNDS
23 #	include <agg_conv_stroke.h>
24 #	include <agg_path_storage.h>
25 #endif
26 
27 #include "GlobalSubpixelSettings.h"
28 #include "GlyphLayoutEngine.h"
29 #include "IntRect.h"
30 
31 
32 AGGTextRenderer::AGGTextRenderer(renderer_subpix_type& subpixRenderer,
33 		renderer_type& solidRenderer, renderer_bin_type& binRenderer,
34 		scanline_unpacked_type& scanline,
35 		scanline_unpacked_subpix_type& subpixScanline,
36 		rasterizer_subpix_type& subpixRasterizer)
37 	:
38 	fPathAdaptor(),
39 	fGray8Adaptor(),
40 	fGray8Scanline(),
41 	fMonoAdaptor(),
42 	fMonoScanline(),
43 	fSubpixAdaptor(),
44 
45 	fCurves(fPathAdaptor),
46 	fContour(fCurves),
47 
48 	fSolidRenderer(solidRenderer),
49 	fBinRenderer(binRenderer),
50 	fSubpixRenderer(subpixRenderer),
51 	fScanline(scanline),
52 	fSubpixScanline(subpixScanline),
53 	fSubpixRasterizer(subpixRasterizer),
54 	fRasterizer(),
55 
56 	fHinted(true),
57 	fAntialias(true),
58 	fKerning(true),
59 	fEmbeddedTransformation()
60 {
61 	fCurves.approximation_scale(2.0);
62 	fContour.auto_detect_orientation(false);
63 }
64 
65 
66 AGGTextRenderer::~AGGTextRenderer()
67 {
68 }
69 
70 
71 void
72 AGGTextRenderer::SetFont(const ServerFont& font)
73 {
74 	fFont = font;
75 
76 	// construct an embedded transformation (rotate & shear)
77 	fEmbeddedTransformation.Reset();
78 	fEmbeddedTransformation.ShearBy(B_ORIGIN,
79 		(90.0 - font.Shear()) * M_PI / 180.0, 0.0);
80 	fEmbeddedTransformation.RotateBy(B_ORIGIN,
81 		-font.Rotation() * M_PI / 180.0);
82 
83 	fContour.width(font.FalseBoldWidth() * 2.0);
84 }
85 
86 
87 void
88 AGGTextRenderer::SetHinting(bool hinting)
89 {
90 	fHinted = hinting;
91 }
92 
93 
94 void
95 AGGTextRenderer::SetAntialiasing(bool antialiasing)
96 {
97 	if (fAntialias != antialiasing) {
98 		fAntialias = antialiasing;
99 		// NOTE: The fSubpixRasterizer is not used when anti-aliasing is
100 		// disbaled.
101 		if (!fAntialias)
102 			fRasterizer.gamma(agg::gamma_threshold(0.5));
103 		else
104 			fRasterizer.gamma(agg::gamma_power(1.0));
105 	}
106 }
107 
108 
109 typedef agg::conv_transform<FontCacheEntry::CurveConverter, Transformable>
110 	conv_font_trans_type;
111 
112 typedef agg::conv_transform<FontCacheEntry::ContourConverter, Transformable>
113 	conv_font_contour_trans_type;
114 
115 
116 
117 class AGGTextRenderer::StringRenderer {
118 public:
119 	StringRenderer(const IntRect& clippingFrame, bool dryRun,
120 			bool subpixelAntiAliased,
121 			FontCacheEntry::TransformedOutline& transformedGlyph,
122 			FontCacheEntry::TransformedContourOutline& transformedContour,
123 			const Transformable& transform,
124 			const BPoint& transformOffset,
125 			BPoint* nextCharPos,
126 			AGGTextRenderer& renderer)
127 		:
128 		fTransform(transform),
129 		fTransformOffset(transformOffset),
130 		fClippingFrame(clippingFrame),
131 		fDryRun(dryRun),
132 		fSubpixelAntiAliased(subpixelAntiAliased),
133 		fVector(false),
134 		fBounds(INT32_MAX, INT32_MAX, INT32_MIN, INT32_MIN),
135 		fNextCharPos(nextCharPos),
136 
137 		fTransformedGlyph(transformedGlyph),
138 		fTransformedContour(transformedContour),
139 
140 		fRenderer(renderer)
141 	{
142 	}
143 
144 	void Start()
145 	{
146 		fRenderer.fRasterizer.reset();
147 		fRenderer.fSubpixRasterizer.reset();
148 	}
149 
150 	void Finish(double x, double y)
151 	{
152 		if (fVector) {
153 			if (fSubpixelAntiAliased) {
154 				agg::render_scanlines(fRenderer.fSubpixRasterizer,
155 					fRenderer.fSubpixScanline, fRenderer.fSubpixRenderer);
156 			} else {
157 				agg::render_scanlines(fRenderer.fRasterizer,
158 					fRenderer.fScanline, fRenderer.fSolidRenderer);
159 			}
160 		}
161 
162 		if (fNextCharPos) {
163 			fNextCharPos->x = x;
164 			fNextCharPos->y = y;
165 			fTransform.Transform(fNextCharPos);
166 		}
167 	}
168 
169 	void ConsumeEmptyGlyph(int32 index, uint32 charCode, double x, double y)
170 	{
171 	}
172 
173 	bool ConsumeGlyph(int32 index, uint32 charCode, const GlyphCache* glyph,
174 		FontCacheEntry* entry, double x, double y)
175 	{
176 		// "glyphBounds" is the bounds of the glyph transformed
177 		// by the x y location of the glyph along the base line,
178 		// it is therefor yet "untransformed" in case there is an
179 		// embedded transformation.
180 		const agg::rect_i& r = glyph->bounds;
181 		IntRect glyphBounds(int32(r.x1 + x), int32(r.y1 + y - 1),
182 			int32(r.x2 + x + 1), int32(r.y2 + y + 1));
183 			// NOTE: "-1"/"+1" converts the glyph bounding box from pixel
184 			// indices to pixel area coordinates
185 
186 		// track bounding box
187 		if (glyphBounds.IsValid())
188 			fBounds = fBounds | glyphBounds;
189 
190 		// render the glyph if this is not a dry run
191 		if (!fDryRun) {
192 			// init the fontmanager's embedded adaptors
193 			// NOTE: The initialization for the "location" of
194 			// the glyph is different depending on whether we
195 			// deal with non-(rotated/sheared) text, in which
196 			// case we have a native FT bitmap. For rotated or
197 			// sheared text, we use AGG vector outlines and
198 			// a transformation pipeline, which will be applied
199 			// _after_ we retrieve the outline, and that's why
200 			// we simply pass x and y, which are untransformed.
201 
202 			// "glyphBounds" is now transformed into screen coords
203 			// in order to stop drawing when we are already outside
204 			// of the clipping frame
205 			if (glyph->data_type != glyph_data_outline) {
206 				// we cannot use the transformation pipeline
207 				double transformedX = x + fTransformOffset.x;
208 				double transformedY = y + fTransformOffset.y;
209 				entry->InitAdaptors(glyph, transformedX, transformedY,
210 					fRenderer.fMonoAdaptor,
211 					fRenderer.fGray8Adaptor,
212 					fRenderer.fPathAdaptor);
213 
214 				glyphBounds.OffsetBy(fTransformOffset);
215 			} else {
216 				entry->InitAdaptors(glyph, x, y,
217 					fRenderer.fMonoAdaptor,
218 					fRenderer.fGray8Adaptor,
219 					fRenderer.fPathAdaptor);
220 
221 				int32 falseBoldWidth = (int32)fRenderer.fContour.width();
222 				if (falseBoldWidth != 0)
223 					glyphBounds.InsetBy(-falseBoldWidth, -falseBoldWidth);
224 				// TODO: not correct! this is later used for clipping,
225 				// but it doesn't get the rect right
226 				glyphBounds = fTransform.TransformBounds(glyphBounds);
227 			}
228 
229 			if (fClippingFrame.Intersects(glyphBounds)) {
230 				switch (glyph->data_type) {
231 					case glyph_data_mono:
232 						agg::render_scanlines(fRenderer.fMonoAdaptor,
233 							fRenderer.fMonoScanline, fRenderer.fBinRenderer);
234 						break;
235 
236 					case glyph_data_gray8:
237 						agg::render_scanlines(fRenderer.fGray8Adaptor,
238 							fRenderer.fGray8Scanline, fRenderer.fSolidRenderer);
239 						break;
240 
241 					case glyph_data_subpix:
242 						agg::render_scanlines(fRenderer.fGray8Adaptor,
243 							fRenderer.fGray8Scanline,
244 							fRenderer.fSubpixRenderer);
245 						break;
246 
247 					case glyph_data_outline: {
248 						fVector = true;
249 						if (fSubpixelAntiAliased) {
250 							if (fRenderer.fContour.width() == 0.0) {
251 								fRenderer.fSubpixRasterizer.add_path(
252 									fTransformedGlyph);
253 							} else {
254 								fRenderer.fSubpixRasterizer.add_path(
255 									fTransformedContour);
256 							}
257 						} else {
258 							if (fRenderer.fContour.width() == 0.0) {
259 								fRenderer.fRasterizer.add_path(
260 									fTransformedGlyph);
261 							} else {
262 								fRenderer.fRasterizer.add_path(
263 									fTransformedContour);
264 							}
265 						}
266 #if SHOW_GLYPH_BOUNDS
267 	agg::path_storage p;
268 	p.move_to(glyphBounds.left + 0.5, glyphBounds.top + 0.5);
269 	p.line_to(glyphBounds.right + 0.5, glyphBounds.top + 0.5);
270 	p.line_to(glyphBounds.right + 0.5, glyphBounds.bottom + 0.5);
271 	p.line_to(glyphBounds.left + 0.5, glyphBounds.bottom + 0.5);
272 	p.close_polygon();
273 	agg::conv_stroke<agg::path_storage> ps(p);
274 	ps.width(1.0);
275 	if (fSubpixelAntiAliased) {
276 		fRenderer.fSubpixRasterizer.add_path(ps);
277 	} else {
278 		fRenderer.fRasterizer.add_path(ps);
279 	}
280 #endif
281 
282 						break;
283 					}
284 					default:
285 						break;
286 				}
287 			}
288 		}
289 		return true;
290 	}
291 
292 	IntRect Bounds() const
293 	{
294 		return fBounds;
295 	}
296 
297 private:
298  	const Transformable& fTransform;
299 	const BPoint&		fTransformOffset;
300 	const IntRect&		fClippingFrame;
301 	bool				fDryRun;
302 	bool				fSubpixelAntiAliased;
303 	bool				fVector;
304 	IntRect				fBounds;
305 	BPoint*				fNextCharPos;
306 
307 	FontCacheEntry::TransformedOutline& fTransformedGlyph;
308 	FontCacheEntry::TransformedContourOutline& fTransformedContour;
309 	AGGTextRenderer&	fRenderer;
310 };
311 
312 
313 BRect
314 AGGTextRenderer::RenderString(const char* string, uint32 length,
315 	const BPoint& baseLine, const BRect& clippingFrame, bool dryRun,
316 	BPoint* nextCharPos, const escapement_delta* delta,
317 	FontCacheReference* cacheReference)
318 {
319 //printf("RenderString(\"%s\", length: %ld, dry: %d)\n", string, length, dryRun);
320 
321 	Transformable transform(fEmbeddedTransformation);
322 	transform.TranslateBy(baseLine);
323 
324 	fCurves.approximation_scale(transform.scale());
325 
326 	// use a transformation behind the curves
327 	// (only if glyph->data_type == agg::glyph_data_outline)
328 	// in the pipeline for the rasterizer
329 	FontCacheEntry::TransformedOutline
330 		transformedOutline(fCurves, transform);
331 	FontCacheEntry::TransformedContourOutline
332 		transformedContourOutline(fContour, transform);
333 
334 	// for when we bypass the transformation pipeline
335 	BPoint transformOffset(0.0, 0.0);
336 	transform.Transform(&transformOffset);
337 
338 	StringRenderer renderer(clippingFrame, dryRun,
339 		gSubpixelAntialiasing && fAntialias,
340 		transformedOutline, transformedContourOutline,
341 		transform, transformOffset, nextCharPos, *this);
342 
343 	GlyphLayoutEngine::LayoutGlyphs(renderer, fFont, string, length, delta,
344 		fKerning, B_BITMAP_SPACING, NULL, cacheReference);
345 
346 	return transform.TransformBounds(renderer.Bounds());
347 }
348 
349 
350 BRect
351 AGGTextRenderer::RenderString(const char* string, uint32 length,
352 	const BPoint* offsets, const BRect& clippingFrame, bool dryRun,
353 	BPoint* nextCharPos, FontCacheReference* cacheReference)
354 {
355 //printf("RenderString(\"%s\", length: %ld, dry: %d)\n", string, length, dryRun);
356 
357 	Transformable transform(fEmbeddedTransformation);
358 
359 	fCurves.approximation_scale(transform.scale());
360 
361 	// use a transformation behind the curves
362 	// (only if glyph->data_type == agg::glyph_data_outline)
363 	// in the pipeline for the rasterizer
364 	FontCacheEntry::TransformedOutline
365 		transformedOutline(fCurves, transform);
366 	FontCacheEntry::TransformedContourOutline
367 		transformedContourOutline(fContour, transform);
368 
369 	// for when we bypass the transformation pipeline
370 	BPoint transformOffset(0.0, 0.0);
371 	transform.Transform(&transformOffset);
372 
373 	StringRenderer renderer(clippingFrame, dryRun,
374 		gSubpixelAntialiasing && fAntialias,
375 		transformedOutline, transformedContourOutline,
376 		transform, transformOffset, nextCharPos, *this);
377 
378 	GlyphLayoutEngine::LayoutGlyphs(renderer, fFont, string, length, NULL,
379 		fKerning, B_BITMAP_SPACING, offsets, cacheReference);
380 
381 	return transform.TransformBounds(renderer.Bounds());
382 }
383