xref: /haiku/src/servers/app/drawing/Painter/AGGTextRenderer.cpp (revision ef83008e7925bffe29833021a979b9defc0b3625)
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_stroke.h>
13 #include <agg_conv_transform.h>
14 #include <agg_path_storage.h>
15 #include <agg_scanline_boolean_algebra.h>
16 #include <agg_trans_affine.h>
17 
18 #include <math.h>
19 #include <malloc.h>
20 #include <stdio.h>
21 #include <string.h>
22 
23 #define SHOW_GLYPH_BOUNDS 0
24 
25 #include "GlobalSubpixelSettings.h"
26 #include "GlyphLayoutEngine.h"
27 #include "IntRect.h"
28 
29 
AGGTextRenderer(renderer_subpix_type & subpixRenderer,renderer_type & solidRenderer,renderer_bin_type & binRenderer,scanline_unpacked_type & scanline,scanline_unpacked_subpix_type & subpixScanline,rasterizer_subpix_type & subpixRasterizer,scanline_unpacked_masked_type * & maskedScanline,agg::trans_affine & viewTransformation)30 AGGTextRenderer::AGGTextRenderer(renderer_subpix_type& subpixRenderer,
31 		renderer_type& solidRenderer, renderer_bin_type& binRenderer,
32 		scanline_unpacked_type& scanline,
33 		scanline_unpacked_subpix_type& subpixScanline,
34 		rasterizer_subpix_type& subpixRasterizer,
35 		scanline_unpacked_masked_type*& maskedScanline,
36 		agg::trans_affine& viewTransformation)
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 	fMaskedScanline(maskedScanline),
55 
56 	fRasterizer(),
57 
58 	fHinted(true),
59 	fAntialias(true),
60 	fEmbeddedTransformation(),
61 	fViewTransformation(viewTransformation)
62 {
63 	fCurves.approximation_scale(2.0);
64 	fContour.auto_detect_orientation(false);
65 }
66 
67 
~AGGTextRenderer()68 AGGTextRenderer::~AGGTextRenderer()
69 {
70 }
71 
72 
73 void
SetFont(const ServerFont & font)74 AGGTextRenderer::SetFont(const ServerFont& font)
75 {
76 	fFont = font;
77 
78 	// construct an embedded transformation (rotate & shear)
79 	fEmbeddedTransformation.Reset();
80 	fEmbeddedTransformation.ShearBy(B_ORIGIN,
81 		(90.0 - font.Shear()) * M_PI / 180.0, 0.0);
82 	fEmbeddedTransformation.RotateBy(B_ORIGIN,
83 		-font.Rotation() * M_PI / 180.0);
84 
85 	fContour.width(font.FalseBoldWidth() * 2.0);
86 }
87 
88 
89 void
SetHinting(bool hinting)90 AGGTextRenderer::SetHinting(bool hinting)
91 {
92 	fHinted = hinting;
93 }
94 
95 
96 void
SetAntialiasing(bool antialiasing)97 AGGTextRenderer::SetAntialiasing(bool antialiasing)
98 {
99 	if (fAntialias != antialiasing) {
100 		fAntialias = antialiasing;
101 		// NOTE: The fSubpixRasterizer is not used when anti-aliasing is
102 		// disbaled.
103 		if (!fAntialias)
104 			fRasterizer.gamma(agg::gamma_threshold(0.5));
105 		else
106 			fRasterizer.gamma(agg::gamma_power(1.0));
107 	}
108 }
109 
110 
111 typedef agg::conv_transform<FontCacheEntry::CurveConverter, Transformable>
112 	conv_font_trans_type;
113 
114 typedef agg::conv_transform<FontCacheEntry::ContourConverter, Transformable>
115 	conv_font_contour_trans_type;
116 
117 
118 
119 class AGGTextRenderer::StringRenderer {
120 public:
StringRenderer(const IntRect & clippingFrame,bool dryRun,bool subpixelAntiAliased,bool underscore,FontCacheEntry::TransformedOutline & transformedGlyph,FontCacheEntry::TransformedContourOutline & transformedContour,const Transformable & transform,const BPoint & transformOffset,BPoint * nextCharPos,AGGTextRenderer & renderer)121 	StringRenderer(const IntRect& clippingFrame, bool dryRun,
122 			bool subpixelAntiAliased, bool underscore,
123 			FontCacheEntry::TransformedOutline& transformedGlyph,
124 			FontCacheEntry::TransformedContourOutline& transformedContour,
125 			const Transformable& transform,
126 			const BPoint& transformOffset,
127 			BPoint* nextCharPos,
128 			AGGTextRenderer& renderer)
129 		:
130 		fTransform(transform),
131 		fTransformOffset(transformOffset),
132 		fClippingFrame(clippingFrame),
133 		fDryRun(dryRun),
134 		fSubpixelAntiAliased(subpixelAntiAliased),
135 		fVector(false),
136 		fUnderscore(underscore),
137 		fBounds(INT32_MAX, INT32_MAX, INT32_MIN, INT32_MIN),
138 		fNextCharPos(nextCharPos),
139 
140 		fTransformedGlyph(transformedGlyph),
141 		fTransformedContour(transformedContour),
142 
143 		fRenderer(renderer)
144 	{
145 	}
146 
NeedsVector()147 	bool NeedsVector()
148 	{
149 		return !fTransform.IsTranslationOnly();
150 	}
151 
Start()152 	void Start()
153 	{
154 		fRenderer.fRasterizer.reset();
155 		fRenderer.fSubpixRasterizer.reset();
156 	}
157 
Finish(double x,double y)158 	void Finish(double x, double y)
159 	{
160 		if (fVector) {
161 			if (fRenderer.fMaskedScanline != NULL) {
162 				agg::render_scanlines(fRenderer.fRasterizer,
163 					*fRenderer.fMaskedScanline, fRenderer.fSolidRenderer);
164 			} else if (fSubpixelAntiAliased) {
165 				agg::render_scanlines(fRenderer.fSubpixRasterizer,
166 					fRenderer.fSubpixScanline, fRenderer.fSubpixRenderer);
167 			} else {
168 				agg::render_scanlines(fRenderer.fRasterizer,
169 					fRenderer.fScanline, fRenderer.fSolidRenderer);
170 			}
171 		}
172 
173 		if (fUnderscore && !fDryRun) {
174 			agg::path_storage p;
175 			IntRect b = fBounds;
176 			b.bottom = (int)y;
177 			b.OffsetBy(fTransformOffset);
178 			p.move_to(b.left + 0.5, b.bottom + 2.5);
179 			p.line_to(b.right + 0.5, b.bottom + 2.5);
180 			p.close_polygon();
181 			agg::conv_stroke<agg::path_storage> ps(p);
182 			ps.width(fRenderer.fFont.Size() / 12.0f);
183 			if (fRenderer.fMaskedScanline != NULL) {
184 				fRenderer.fRasterizer.add_path(ps);
185 				agg::render_scanlines(fRenderer.fRasterizer,
186 					*fRenderer.fMaskedScanline, fRenderer.fSolidRenderer);
187 			} else if (fSubpixelAntiAliased) {
188 				fRenderer.fSubpixRasterizer.add_path(ps);
189 				agg::render_scanlines(fRenderer.fSubpixRasterizer,
190 					fRenderer.fSubpixScanline, fRenderer.fSubpixRenderer);
191 			} else {
192 				/*
193 				scanline_unpacked_type sl1;
194 				scanline_unpacked_type sl2;
195 
196 				rasterizer_type ras1;
197 				rasterizer_type ras2;
198 
199 				ras1.add_path(ps);
200 				ras2.add_path(fTransformedContour);
201 
202 				agg::render_scanlines(ras1,
203 					sl1, fRenderer.fSolidRenderer);
204 				agg::render_scanlines(ras2,
205 					sl2, fRenderer.fSolidRenderer);
206 
207 				agg::sbool_combine_shapes_aa(agg::sbool_a_minus_b,
208 					ras1, ras2, sl1, sl2, fRenderer.fScanline, fRenderer.fSolidRenderer);
209 				*/
210 				fRenderer.fRasterizer.add_path(ps);
211 				agg::render_scanlines(fRenderer.fRasterizer,
212 					fRenderer.fScanline, fRenderer.fSolidRenderer);
213 			}
214 		}
215 
216 		if (fNextCharPos) {
217 			fNextCharPos->x = x;
218 			fNextCharPos->y = y;
219 			fTransform.Transform(fNextCharPos);
220 		}
221 	}
222 
ConsumeEmptyGlyph(int32 index,uint32 charCode,double x,double y)223 	void ConsumeEmptyGlyph(int32 index, uint32 charCode, double x, double y)
224 	{
225 	}
226 
ConsumeGlyph(int32 index,uint32 charCode,const GlyphCache * glyph,FontCacheEntry * entry,double x,double y,double advanceX,double advanceY)227 	bool ConsumeGlyph(int32 index, uint32 charCode, const GlyphCache* glyph,
228 		FontCacheEntry* entry, double x, double y, double advanceX,
229 			double advanceY)
230 	{
231 		// "glyphBounds" is the bounds of the glyph transformed
232 		// by the x y location of the glyph along the base line,
233 		// it is therefor yet "untransformed" in case there is an
234 		// embedded transformation.
235 		const agg::rect_i& r = glyph->bounds;
236 		IntRect glyphBounds(int32(r.x1 + x), int32(r.y1 + y - 1),
237 			int32(r.x2 + x + 1), int32(r.y2 + y + 1));
238 			// NOTE: "-1"/"+1" converts the glyph bounding box from pixel
239 			// indices to pixel area coordinates
240 
241 		// track bounding box
242 		if (glyphBounds.IsValid())
243 			fBounds = fBounds | glyphBounds;
244 
245 		// render the glyph if this is not a dry run
246 		if (!fDryRun) {
247 			// init the fontmanager's embedded adaptors
248 			// NOTE: The initialization for the "location" of
249 			// the glyph is different depending on whether we
250 			// deal with non-(rotated/sheared) text, in which
251 			// case we have a native FT bitmap. For rotated or
252 			// sheared text, we use AGG vector outlines and
253 			// a transformation pipeline, which will be applied
254 			// _after_ we retrieve the outline, and that's why
255 			// we simply pass x and y, which are untransformed.
256 
257 			// "glyphBounds" is now transformed into screen coords
258 			// in order to stop drawing when we are already outside
259 			// of the clipping frame
260 			if (glyph->data_type != glyph_data_outline) {
261 				// we cannot use the transformation pipeline
262 				double transformedX = x + fTransformOffset.x;
263 				double transformedY = y + fTransformOffset.y;
264 				entry->InitAdaptors(glyph, transformedX, transformedY,
265 					fRenderer.fMonoAdaptor,
266 					fRenderer.fGray8Adaptor,
267 					fRenderer.fPathAdaptor);
268 
269 				glyphBounds.OffsetBy(fTransformOffset);
270 			} else {
271 				entry->InitAdaptors(glyph, x, y,
272 					fRenderer.fMonoAdaptor,
273 					fRenderer.fGray8Adaptor,
274 					fRenderer.fPathAdaptor);
275 
276 				int32 falseBoldWidth = (int32)fRenderer.fContour.width();
277 				if (falseBoldWidth != 0)
278 					glyphBounds.InsetBy(-falseBoldWidth, -falseBoldWidth);
279 				// TODO: not correct! this is later used for clipping,
280 				// but it doesn't get the rect right
281 				glyphBounds = fTransform.TransformBounds(glyphBounds);
282 			}
283 
284 			if (fClippingFrame.Intersects(glyphBounds)) {
285 				switch (glyph->data_type) {
286 					case glyph_data_mono:
287 						agg::render_scanlines(fRenderer.fMonoAdaptor,
288 							fRenderer.fMonoScanline, fRenderer.fBinRenderer);
289 						break;
290 
291 					case glyph_data_gray8:
292 						if (fRenderer.fMaskedScanline != NULL) {
293 							agg::render_scanlines(fRenderer.fGray8Adaptor,
294 								*fRenderer.fMaskedScanline,
295 								fRenderer.fSolidRenderer);
296 						} else {
297 							agg::render_scanlines(fRenderer.fGray8Adaptor,
298 								fRenderer.fGray8Scanline,
299 								fRenderer.fSolidRenderer);
300 						}
301 						break;
302 
303 					case glyph_data_subpix:
304 						// TODO: Handle alpha mask (fRenderer.fMaskedScanline)
305 						agg::render_scanlines(fRenderer.fGray8Adaptor,
306 							fRenderer.fGray8Scanline,
307 							fRenderer.fSubpixRenderer);
308 						break;
309 
310 					case glyph_data_outline: {
311 						fVector = true;
312 						if (fSubpixelAntiAliased) {
313 							if (fRenderer.fContour.width() == 0.0) {
314 								fRenderer.fSubpixRasterizer.add_path(
315 									fTransformedGlyph);
316 							} else {
317 								fRenderer.fSubpixRasterizer.add_path(
318 									fTransformedContour);
319 							}
320 						} else {
321 							if (fRenderer.fContour.width() == 0.0) {
322 								fRenderer.fRasterizer.add_path(
323 									fTransformedGlyph);
324 							} else {
325 								fRenderer.fRasterizer.add_path(
326 									fTransformedContour);
327 							}
328 						}
329 #if SHOW_GLYPH_BOUNDS
330 	agg::path_storage p;
331 	p.move_to(glyphBounds.left + 0.5, glyphBounds.top + 0.5);
332 	p.line_to(glyphBounds.right + 0.5, glyphBounds.top + 0.5);
333 	p.line_to(glyphBounds.right + 0.5, glyphBounds.bottom + 0.5);
334 	p.line_to(glyphBounds.left + 0.5, glyphBounds.bottom + 0.5);
335 	p.close_polygon();
336 	agg::conv_stroke<agg::path_storage> ps(p);
337 	ps.width(1.0);
338 	if (fSubpixelAntiAliased) {
339 		fRenderer.fSubpixRasterizer.add_path(ps);
340 	} else {
341 		fRenderer.fRasterizer.add_path(ps);
342 	}
343 #endif
344 
345 						break;
346 					}
347 					default:
348 						break;
349 				}
350 			}
351 		}
352 		return true;
353 	}
354 
Bounds() const355 	IntRect Bounds() const
356 	{
357 		return fBounds;
358 	}
359 
360 private:
361 	const Transformable& fTransform;
362 	const BPoint&		fTransformOffset;
363 	const IntRect&		fClippingFrame;
364 	bool				fDryRun;
365 	bool				fSubpixelAntiAliased;
366 	bool				fVector;
367 	bool				fUnderscore;
368 	IntRect				fBounds;
369 	BPoint*				fNextCharPos;
370 
371 	FontCacheEntry::TransformedOutline& fTransformedGlyph;
372 	FontCacheEntry::TransformedContourOutline& fTransformedContour;
373 	AGGTextRenderer&	fRenderer;
374 };
375 
376 
377 BRect
RenderString(const char * string,uint32 length,const BPoint & baseLine,const BRect & clippingFrame,bool dryRun,BPoint * nextCharPos,const escapement_delta * delta,FontCacheReference * cacheReference)378 AGGTextRenderer::RenderString(const char* string, uint32 length,
379 	const BPoint& baseLine, const BRect& clippingFrame, bool dryRun,
380 	BPoint* nextCharPos, const escapement_delta* delta,
381 	FontCacheReference* cacheReference)
382 {
383 //printf("RenderString(\"%s\", length: %ld, dry: %d)\n", string, length, dryRun);
384 
385 	Transformable transform(fEmbeddedTransformation);
386 	transform.TranslateBy(baseLine);
387 	transform *= fViewTransformation;
388 
389 	fCurves.approximation_scale(transform.scale());
390 
391 	// use a transformation behind the curves
392 	// (only if glyph->data_type == agg::glyph_data_outline)
393 	// in the pipeline for the rasterizer
394 	FontCacheEntry::TransformedOutline
395 		transformedOutline(fCurves, transform);
396 	FontCacheEntry::TransformedContourOutline
397 		transformedContourOutline(fContour, transform);
398 
399 	// for when we bypass the transformation pipeline
400 	BPoint transformOffset(0.0, 0.0);
401 	transform.Transform(&transformOffset);
402 	IntRect clippingIntFrame(clippingFrame);
403 
404 	bool underscore = fFont.Face() & B_UNDERSCORE_FACE;
405 
406 	StringRenderer renderer(clippingIntFrame, dryRun,
407 		gSubpixelAntialiasing && fAntialias, underscore,
408 		transformedOutline, transformedContourOutline,
409 		transform, transformOffset, nextCharPos, *this);
410 
411 	GlyphLayoutEngine::LayoutGlyphs(renderer, fFont, string, length, INT32_MAX,
412 		delta, fFont.Spacing(), NULL, cacheReference);
413 
414 	return transform.TransformBounds(renderer.Bounds());
415 }
416 
417 
418 BRect
RenderString(const char * string,uint32 length,const BPoint * offsets,const BRect & clippingFrame,bool dryRun,BPoint * nextCharPos,FontCacheReference * cacheReference)419 AGGTextRenderer::RenderString(const char* string, uint32 length,
420 	const BPoint* offsets, const BRect& clippingFrame, bool dryRun,
421 	BPoint* nextCharPos, FontCacheReference* cacheReference)
422 {
423 //printf("RenderString(\"%s\", length: %ld, dry: %d)\n", string, length, dryRun);
424 
425 	Transformable transform(fEmbeddedTransformation);
426 	transform *= fViewTransformation;
427 
428 	fCurves.approximation_scale(transform.scale());
429 
430 	// use a transformation behind the curves
431 	// (only if glyph->data_type == agg::glyph_data_outline)
432 	// in the pipeline for the rasterizer
433 	FontCacheEntry::TransformedOutline
434 		transformedOutline(fCurves, transform);
435 	FontCacheEntry::TransformedContourOutline
436 		transformedContourOutline(fContour, transform);
437 
438 	// for when we bypass the transformation pipeline
439 	BPoint transformOffset(0.0, 0.0);
440 	transform.Transform(&transformOffset);
441 	IntRect clippingIntFrame(clippingFrame);
442 
443 	bool underscore = fFont.Face() & B_UNDERSCORE_FACE;
444 
445 	StringRenderer renderer(clippingIntFrame, dryRun,
446 		gSubpixelAntialiasing && fAntialias, underscore,
447 		transformedOutline, transformedContourOutline,
448 		transform, transformOffset, nextCharPos, *this);
449 
450 	GlyphLayoutEngine::LayoutGlyphs(renderer, fFont, string, length, INT32_MAX,
451 		NULL, fFont.Spacing(), offsets, cacheReference);
452 
453 	return transform.TransformBounds(renderer.Bounds());
454 }
455