app_server: add B_UNDERSCORE_FACE support

There was some confusion (and a TODO indicating it) in ServerFont.cpp,
because the notion of "font face" from the Be API is partially
implemented using different font manager styles (bold, italic, etc),
and partially by keeping flags in a separate variable for drawing
extra things or modifying the drawing (underscore, strikeout, ...).

The implementation did not actually preserve the extra flags, and so the
underscore face attribute was lost.

Implement the actual underlining of the text in AGGTextRenderer. This
implementation is a naive one so far. In particular there are the
following limitations:
1. Line is drawn over the text - no nice gaps for descents. Ideally, the
   line should not touch the letter descents, and leave some space
   around them. I don't know how to retrieve the contour - it appears to
   me this might require bigger refactoring of this code. I have left in
   my experiments commented out in the code.
2. If the text run ends with whitespace, the whitespace is not underlined
   as it should. In particular if another text run is drawn next to it
   and it's expected that the underline is continuous between the two.

Change-Id: I8d78b8e1eceddff0a7d98e5a49659e7b03fd89a0
Reviewed-on: https://review.haiku-os.org/c/haiku/+/3041
Reviewed-by: Kacper Kasper <kacperkasper@gmail.com>
Reviewed-by: Adrien Destugues <pulkomandy@gmail.com>
Tested-by: Commit checker robot <no-reply+buildbot@haiku-os.org>
This commit is contained in:
Kacper Kasper 2020-07-18 20:49:49 +02:00 committed by Adrien Destugues
parent 627fb72d50
commit ef83008e79
4 changed files with 68 additions and 16 deletions

View File

@ -192,6 +192,8 @@ ServerFont::operator=(const ServerFont& font)
SetStyle(font.fStyle);
fFace = font.fFace;
return *this;
}
@ -252,7 +254,7 @@ ServerFont::SetStyle(FontStyle* style)
if (style && style != fStyle) {
fStyle.SetTo(style, false);
fFace = fStyle->Face();
fFace = fStyle->PreservedFace(fFace);
fDirection = fStyle->Direction();
}
}
@ -304,10 +306,8 @@ ServerFont::SetFamilyAndStyle(uint32 fontID)
status_t
ServerFont::SetFace(uint16 face)
{
// TODO: This needs further investigation. The face variable is actually
// flags, but some of them are not enforcable at the same time. Also don't
// confuse the Be API "face" with the Freetype face, which is just an
// index in case a single font file exports multiple font faces. The
// Don't confuse the Be API "face" with the Freetype face, which is just
// an index in case a single font file exports multiple font faces. The
// FontStyle class takes care of mapping the font style name to the Be
// API face flags in FontStyle::_TranslateStyleToFace().
@ -319,7 +319,7 @@ ServerFont::SetFace(uint16 face)
style.SetTo(gFontManager->GetStyleByIndex(familyID, i), false);
if (style == NULL)
break;
if (style->Face() == face)
if (style->PreservedFace(face) == face)
break;
else
style = NULL;
@ -331,6 +331,7 @@ ServerFont::SetFace(uint16 face)
if (!style)
return B_ERROR;
fFace = face;
SetStyle(style);
return B_OK;

View File

@ -9,7 +9,10 @@
#include <agg_basics.h>
#include <agg_bounding_rect.h>
#include <agg_conv_segmentator.h>
#include <agg_conv_stroke.h>
#include <agg_conv_transform.h>
#include <agg_path_storage.h>
#include <agg_scanline_boolean_algebra.h>
#include <agg_trans_affine.h>
#include <math.h>
@ -19,11 +22,6 @@
#define SHOW_GLYPH_BOUNDS 0
#if SHOW_GLYPH_BOUNDS
# include <agg_conv_stroke.h>
# include <agg_path_storage.h>
#endif
#include "GlobalSubpixelSettings.h"
#include "GlyphLayoutEngine.h"
#include "IntRect.h"
@ -121,7 +119,7 @@ typedef agg::conv_transform<FontCacheEntry::ContourConverter, Transformable>
class AGGTextRenderer::StringRenderer {
public:
StringRenderer(const IntRect& clippingFrame, bool dryRun,
bool subpixelAntiAliased,
bool subpixelAntiAliased, bool underscore,
FontCacheEntry::TransformedOutline& transformedGlyph,
FontCacheEntry::TransformedContourOutline& transformedContour,
const Transformable& transform,
@ -135,6 +133,7 @@ public:
fDryRun(dryRun),
fSubpixelAntiAliased(subpixelAntiAliased),
fVector(false),
fUnderscore(underscore),
fBounds(INT32_MAX, INT32_MAX, INT32_MIN, INT32_MIN),
fNextCharPos(nextCharPos),
@ -171,6 +170,49 @@ public:
}
}
if (fUnderscore && !fDryRun) {
agg::path_storage p;
IntRect b = fBounds;
b.bottom = (int)y;
b.OffsetBy(fTransformOffset);
p.move_to(b.left + 0.5, b.bottom + 2.5);
p.line_to(b.right + 0.5, b.bottom + 2.5);
p.close_polygon();
agg::conv_stroke<agg::path_storage> ps(p);
ps.width(fRenderer.fFont.Size() / 12.0f);
if (fRenderer.fMaskedScanline != NULL) {
fRenderer.fRasterizer.add_path(ps);
agg::render_scanlines(fRenderer.fRasterizer,
*fRenderer.fMaskedScanline, fRenderer.fSolidRenderer);
} else if (fSubpixelAntiAliased) {
fRenderer.fSubpixRasterizer.add_path(ps);
agg::render_scanlines(fRenderer.fSubpixRasterizer,
fRenderer.fSubpixScanline, fRenderer.fSubpixRenderer);
} else {
/*
scanline_unpacked_type sl1;
scanline_unpacked_type sl2;
rasterizer_type ras1;
rasterizer_type ras2;
ras1.add_path(ps);
ras2.add_path(fTransformedContour);
agg::render_scanlines(ras1,
sl1, fRenderer.fSolidRenderer);
agg::render_scanlines(ras2,
sl2, fRenderer.fSolidRenderer);
agg::sbool_combine_shapes_aa(agg::sbool_a_minus_b,
ras1, ras2, sl1, sl2, fRenderer.fScanline, fRenderer.fSolidRenderer);
*/
fRenderer.fRasterizer.add_path(ps);
agg::render_scanlines(fRenderer.fRasterizer,
fRenderer.fScanline, fRenderer.fSolidRenderer);
}
}
if (fNextCharPos) {
fNextCharPos->x = x;
fNextCharPos->y = y;
@ -322,6 +364,7 @@ private:
bool fDryRun;
bool fSubpixelAntiAliased;
bool fVector;
bool fUnderscore;
IntRect fBounds;
BPoint* fNextCharPos;
@ -358,8 +401,10 @@ AGGTextRenderer::RenderString(const char* string, uint32 length,
transform.Transform(&transformOffset);
IntRect clippingIntFrame(clippingFrame);
bool underscore = fFont.Face() & B_UNDERSCORE_FACE;
StringRenderer renderer(clippingIntFrame, dryRun,
gSubpixelAntialiasing && fAntialias,
gSubpixelAntialiasing && fAntialias, underscore,
transformedOutline, transformedContourOutline,
transform, transformOffset, nextCharPos, *this);
@ -395,8 +440,10 @@ AGGTextRenderer::RenderString(const char* string, uint32 length,
transform.Transform(&transformOffset);
IntRect clippingIntFrame(clippingFrame);
bool underscore = fFont.Face() & B_UNDERSCORE_FACE;
StringRenderer renderer(clippingIntFrame, dryRun,
gSubpixelAntialiasing && fAntialias,
gSubpixelAntialiasing && fAntialias, underscore,
transformedOutline, transformedContourOutline,
transform, transformOffset, nextCharPos, *this);

View File

@ -260,9 +260,12 @@ FontFamily::GetStyleByID(uint16 id) const
FontStyle*
FontFamily::GetStyleMatchingFace(uint16 face) const
{
// TODO: support other faces (strike through, underlined, outlines...)
// Other face flags do not impact the font selection (they are applied
// during drawing)
face &= B_BOLD_FACE | B_ITALIC_FACE | B_REGULAR_FACE | B_CONDENSED_FACE
| B_LIGHT_FACE | B_HEAVY_FACE;
if (face == 0)
face = B_REGULAR_FACE;
int32 count = fStyles.CountItems();
for (int32 i = 0; i < count; i++) {

View File

@ -188,7 +188,8 @@ uint16
FontStyle::PreservedFace(uint16 face) const
{
// TODO: make this better
face &= ~(B_REGULAR_FACE | B_BOLD_FACE | B_ITALIC_FACE);
face &= ~(B_REGULAR_FACE | B_BOLD_FACE | B_ITALIC_FACE | B_CONDENSED_FACE
| B_LIGHT_FACE | B_HEAVY_FACE);
face |= Face();
return face;