aboutsummaryrefslogtreecommitdiff
path: root/src/libui_sdl/libui/windows/drawtext.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/libui_sdl/libui/windows/drawtext.cpp')
-rw-r--r--src/libui_sdl/libui/windows/drawtext.cpp531
1 files changed, 0 insertions, 531 deletions
diff --git a/src/libui_sdl/libui/windows/drawtext.cpp b/src/libui_sdl/libui/windows/drawtext.cpp
deleted file mode 100644
index 05a24f6..0000000
--- a/src/libui_sdl/libui/windows/drawtext.cpp
+++ /dev/null
@@ -1,531 +0,0 @@
-// 22 december 2015
-#include "uipriv_windows.hpp"
-#include "draw.hpp"
-// TODO really migrate
-
-// notes:
-// only available in windows 8 and newer:
-// - character spacing
-// - kerning control
-// - justficiation (how could I possibly be making this up?!)
-// - vertical text (SERIOUSLY?! WHAT THE ACTUAL FUCK, MICROSOFT?!?!?!? DID YOU NOT THINK ABOUT THIS THE FIRST TIME, TRYING TO IMPROVE THE INTERNATIONALIZATION OF WINDOWS 7?!?!?! bonus: some parts of MSDN even say 8.1 and up only!)
-
-struct uiDrawFontFamilies {
- fontCollection *fc;
-};
-
-uiDrawFontFamilies *uiDrawListFontFamilies(void)
-{
- struct uiDrawFontFamilies *ff;
-
- ff = uiNew(struct uiDrawFontFamilies);
- ff->fc = loadFontCollection();
- return ff;
-}
-
-int uiDrawFontFamiliesNumFamilies(uiDrawFontFamilies *ff)
-{
- return ff->fc->fonts->GetFontFamilyCount();
-}
-
-char *uiDrawFontFamiliesFamily(uiDrawFontFamilies *ff, int n)
-{
- IDWriteFontFamily *family;
- WCHAR *wname;
- char *name;
- HRESULT hr;
-
- hr = ff->fc->fonts->GetFontFamily(n, &family);
- if (hr != S_OK)
- logHRESULT(L"error getting font out of collection", hr);
- wname = fontCollectionFamilyName(ff->fc, family);
- name = toUTF8(wname);
- uiFree(wname);
- family->Release();
- return name;
-}
-
-void uiDrawFreeFontFamilies(uiDrawFontFamilies *ff)
-{
- fontCollectionFree(ff->fc);
- uiFree(ff);
-}
-
-struct uiDrawTextFont {
- IDWriteFont *f;
- WCHAR *family; // save for convenience in uiDrawNewTextLayout()
- double size;
-};
-
-uiDrawTextFont *mkTextFont(IDWriteFont *df, BOOL addRef, WCHAR *family, BOOL copyFamily, double size)
-{
- uiDrawTextFont *font;
- WCHAR *copy;
- HRESULT hr;
-
- font = uiNew(uiDrawTextFont);
- font->f = df;
- if (addRef)
- font->f->AddRef();
- if (copyFamily) {
- copy = (WCHAR *) uiAlloc((wcslen(family) + 1) * sizeof (WCHAR), "WCHAR[]");
- wcscpy(copy, family);
- font->family = copy;
- } else
- font->family = family;
- font->size = size;
- return font;
-}
-
-// TODO consider moving these all to dwrite.cpp
-
-// TODO MinGW-w64 is missing this one
-#define DWRITE_FONT_WEIGHT_SEMI_LIGHT (DWRITE_FONT_WEIGHT(350))
-static const struct {
- bool lastOne;
- uiDrawTextWeight uival;
- DWRITE_FONT_WEIGHT dwval;
-} dwriteWeights[] = {
- { false, uiDrawTextWeightThin, DWRITE_FONT_WEIGHT_THIN },
- { false, uiDrawTextWeightUltraLight, DWRITE_FONT_WEIGHT_ULTRA_LIGHT },
- { false, uiDrawTextWeightLight, DWRITE_FONT_WEIGHT_LIGHT },
- { false, uiDrawTextWeightBook, DWRITE_FONT_WEIGHT_SEMI_LIGHT },
- { false, uiDrawTextWeightNormal, DWRITE_FONT_WEIGHT_NORMAL },
- { false, uiDrawTextWeightMedium, DWRITE_FONT_WEIGHT_MEDIUM },
- { false, uiDrawTextWeightSemiBold, DWRITE_FONT_WEIGHT_SEMI_BOLD },
- { false, uiDrawTextWeightBold, DWRITE_FONT_WEIGHT_BOLD },
- { false, uiDrawTextWeightUltraBold, DWRITE_FONT_WEIGHT_ULTRA_BOLD },
- { false, uiDrawTextWeightHeavy, DWRITE_FONT_WEIGHT_HEAVY },
- { true, uiDrawTextWeightUltraHeavy, DWRITE_FONT_WEIGHT_ULTRA_BLACK, },
-};
-
-static const struct {
- bool lastOne;
- uiDrawTextItalic uival;
- DWRITE_FONT_STYLE dwval;
-} dwriteItalics[] = {
- { false, uiDrawTextItalicNormal, DWRITE_FONT_STYLE_NORMAL },
- { false, uiDrawTextItalicOblique, DWRITE_FONT_STYLE_OBLIQUE },
- { true, uiDrawTextItalicItalic, DWRITE_FONT_STYLE_ITALIC },
-};
-
-static const struct {
- bool lastOne;
- uiDrawTextStretch uival;
- DWRITE_FONT_STRETCH dwval;
-} dwriteStretches[] = {
- { false, uiDrawTextStretchUltraCondensed, DWRITE_FONT_STRETCH_ULTRA_CONDENSED },
- { false, uiDrawTextStretchExtraCondensed, DWRITE_FONT_STRETCH_EXTRA_CONDENSED },
- { false, uiDrawTextStretchCondensed, DWRITE_FONT_STRETCH_CONDENSED },
- { false, uiDrawTextStretchSemiCondensed, DWRITE_FONT_STRETCH_SEMI_CONDENSED },
- { false, uiDrawTextStretchNormal, DWRITE_FONT_STRETCH_NORMAL },
- { false, uiDrawTextStretchSemiExpanded, DWRITE_FONT_STRETCH_SEMI_EXPANDED },
- { false, uiDrawTextStretchExpanded, DWRITE_FONT_STRETCH_EXPANDED },
- { false, uiDrawTextStretchExtraExpanded, DWRITE_FONT_STRETCH_EXTRA_EXPANDED },
- { true, uiDrawTextStretchUltraExpanded, DWRITE_FONT_STRETCH_ULTRA_EXPANDED },
-};
-
-void attrToDWriteAttr(struct dwriteAttr *attr)
-{
- bool found;
- int i;
-
- found = false;
- for (i = 0; ; i++) {
- if (dwriteWeights[i].uival == attr->weight) {
- attr->dweight = dwriteWeights[i].dwval;
- found = true;
- break;
- }
- if (dwriteWeights[i].lastOne)
- break;
- }
- if (!found)
- userbug("Invalid text weight %d passed to text function.", attr->weight);
-
- found = false;
- for (i = 0; ; i++) {
- if (dwriteItalics[i].uival == attr->italic) {
- attr->ditalic = dwriteItalics[i].dwval;
- found = true;
- break;
- }
- if (dwriteItalics[i].lastOne)
- break;
- }
- if (!found)
- userbug("Invalid text italic %d passed to text function.", attr->italic);
-
- found = false;
- for (i = 0; ; i++) {
- if (dwriteStretches[i].uival == attr->stretch) {
- attr->dstretch = dwriteStretches[i].dwval;
- found = true;
- break;
- }
- if (dwriteStretches[i].lastOne)
- break;
- }
- if (!found)
- // TODO on other platforms too
- userbug("Invalid text stretch %d passed to text function.", attr->stretch);
-}
-
-void dwriteAttrToAttr(struct dwriteAttr *attr)
-{
- int weight, against, n;
- int curdiff, curindex;
- bool found;
- int i;
-
- // weight is scaled; we need to test to see what's nearest
- weight = (int) (attr->dweight);
- against = (int) (dwriteWeights[0].dwval);
- curdiff = abs(against - weight);
- curindex = 0;
- for (i = 1; ; i++) {
- against = (int) (dwriteWeights[i].dwval);
- n = abs(against - weight);
- if (n < curdiff) {
- curdiff = n;
- curindex = i;
- }
- if (dwriteWeights[i].lastOne)
- break;
- }
- attr->weight = dwriteWeights[i].uival;
-
- // italic and stretch are simple values; we can just do a matching search
- found = false;
- for (i = 0; ; i++) {
- if (dwriteItalics[i].dwval == attr->ditalic) {
- attr->italic = dwriteItalics[i].uival;
- found = true;
- break;
- }
- if (dwriteItalics[i].lastOne)
- break;
- }
- if (!found)
- // these are implbug()s because users shouldn't be able to get here directly; TODO?
- implbug("invalid italic %d passed to dwriteAttrToAttr()", attr->ditalic);
-
- found = false;
- for (i = 0; ; i++) {
- if (dwriteStretches[i].dwval == attr->dstretch) {
- attr->stretch = dwriteStretches[i].uival;
- found = true;
- break;
- }
- if (dwriteStretches[i].lastOne)
- break;
- }
- if (!found)
- implbug("invalid stretch %d passed to dwriteAttrToAttr()", attr->dstretch);
-}
-
-uiDrawTextFont *uiDrawLoadClosestFont(const uiDrawTextFontDescriptor *desc)
-{
- uiDrawTextFont *font;
- IDWriteFontCollection *collection;
- UINT32 index;
- BOOL exists;
- struct dwriteAttr attr;
- IDWriteFontFamily *family;
- WCHAR *wfamily;
- IDWriteFont *match;
- HRESULT hr;
-
- // always get the latest available font information
- hr = dwfactory->GetSystemFontCollection(&collection, TRUE);
- if (hr != S_OK)
- logHRESULT(L"error getting system font collection", hr);
-
- wfamily = toUTF16(desc->Family);
- hr = collection->FindFamilyName(wfamily, &index, &exists);
- if (hr != S_OK)
- logHRESULT(L"error finding font family", hr);
- if (!exists)
- implbug("LONGTERM family not found in uiDrawLoadClosestFont()", hr);
- hr = collection->GetFontFamily(index, &family);
- if (hr != S_OK)
- logHRESULT(L"error loading font family", hr);
-
- attr.weight = desc->Weight;
- attr.italic = desc->Italic;
- attr.stretch = desc->Stretch;
- attrToDWriteAttr(&attr);
- hr = family->GetFirstMatchingFont(
- attr.dweight,
- attr.dstretch,
- attr.ditalic,
- &match);
- if (hr != S_OK)
- logHRESULT(L"error loading font", hr);
-
- font = mkTextFont(match,
- FALSE, // we own the initial reference; no need to add another one
- wfamily, FALSE, // will be freed with font
- desc->Size);
-
- family->Release();
- collection->Release();
-
- return font;
-}
-
-void uiDrawFreeTextFont(uiDrawTextFont *font)
-{
- font->f->Release();
- uiFree(font->family);
- uiFree(font);
-}
-
-uintptr_t uiDrawTextFontHandle(uiDrawTextFont *font)
-{
- return (uintptr_t) (font->f);
-}
-
-void uiDrawTextFontDescribe(uiDrawTextFont *font, uiDrawTextFontDescriptor *desc)
-{
- // TODO
-
- desc->Size = font->size;
-
- // TODO
-}
-
-// text sizes are 1/72 of an inch
-// points in Direct2D are 1/96 of an inch (https://msdn.microsoft.com/en-us/library/windows/desktop/ff684173%28v=vs.85%29.aspx, https://msdn.microsoft.com/en-us/library/windows/desktop/hh447022%28v=vs.85%29.aspx)
-// As for the actual conversion from design units, see:
-// - http://cboard.cprogramming.com/windows-programming/136733-directwrite-font-height-issues.html
-// - https://sourceforge.net/p/vstgui/mailman/message/32483143/
-// - http://xboxforums.create.msdn.com/forums/t/109445.aspx
-// - https://msdn.microsoft.com/en-us/library/dd183564%28v=vs.85%29.aspx
-// - http://www.fontbureau.com/blog/the-em/
-// TODO make points here about how DIPs in DirectWrite == DIPs in Direct2D; if not, figure out what they really are? for the width and layout functions later
-static double scaleUnits(double what, double designUnitsPerEm, double size)
-{
- return (what / designUnitsPerEm) * (size * (96.0 / 72.0));
-}
-
-void uiDrawTextFontGetMetrics(uiDrawTextFont *font, uiDrawTextFontMetrics *metrics)
-{
- DWRITE_FONT_METRICS dm;
-
- font->f->GetMetrics(&dm);
- metrics->Ascent = scaleUnits(dm.ascent, dm.designUnitsPerEm, font->size);
- metrics->Descent = scaleUnits(dm.descent, dm.designUnitsPerEm, font->size);
- // TODO what happens if dm.xxx is negative?
- // TODO remember what this was for
- metrics->Leading = scaleUnits(dm.lineGap, dm.designUnitsPerEm, font->size);
- metrics->UnderlinePos = scaleUnits(dm.underlinePosition, dm.designUnitsPerEm, font->size);
- metrics->UnderlineThickness = scaleUnits(dm.underlineThickness, dm.designUnitsPerEm, font->size);
-}
-
-// some attributes, such as foreground color, can't be applied until after we establish a Direct2D context :/ so we have to prepare all attributes in advance
-// also since there's no way to clear the attributes from a layout en masse (apart from overwriting them all), we'll play it safe by creating a new layout each time
-enum layoutAttrType {
- layoutAttrColor,
-};
-
-struct layoutAttr {
- enum layoutAttrType type;
- int start;
- int end;
- double components[4];
-};
-
-struct uiDrawTextLayout {
- WCHAR *text;
- size_t textlen;
- size_t *graphemes;
- double width;
- IDWriteTextFormat *format;
- std::vector<struct layoutAttr> *attrs;
-};
-
-uiDrawTextLayout *uiDrawNewTextLayout(const char *text, uiDrawTextFont *defaultFont, double width)
-{
- uiDrawTextLayout *layout;
- HRESULT hr;
-
- layout = uiNew(uiDrawTextLayout);
-
- hr = dwfactory->CreateTextFormat(defaultFont->family,
- NULL,
- defaultFont->f->GetWeight(),
- defaultFont->f->GetStyle(),
- defaultFont->f->GetStretch(),
- // typographic points are 1/72 inch; this parameter is 1/96 inch
- // fortunately Microsoft does this too, in https://msdn.microsoft.com/en-us/library/windows/desktop/dd371554%28v=vs.85%29.aspx
- defaultFont->size * (96.0 / 72.0),
- // see http://stackoverflow.com/questions/28397971/idwritefactorycreatetextformat-failing and https://msdn.microsoft.com/en-us/library/windows/desktop/dd368203.aspx
- // TODO use the current locale again?
- L"",
- &(layout->format));
- if (hr != S_OK)
- logHRESULT(L"error creating IDWriteTextFormat", hr);
-
- layout->text = toUTF16(text);
- layout->textlen = wcslen(layout->text);
- layout->graphemes = graphemes(layout->text);
-
- uiDrawTextLayoutSetWidth(layout, width);
-
- layout->attrs = new std::vector<struct layoutAttr>;
-
- return layout;
-}
-
-void uiDrawFreeTextLayout(uiDrawTextLayout *layout)
-{
- delete layout->attrs;
- layout->format->Release();
- uiFree(layout->graphemes);
- uiFree(layout->text);
- uiFree(layout);
-}
-
-static ID2D1SolidColorBrush *mkSolidBrush(ID2D1RenderTarget *rt, double r, double g, double b, double a)
-{
- D2D1_BRUSH_PROPERTIES props;
- D2D1_COLOR_F color;
- ID2D1SolidColorBrush *brush;
- HRESULT hr;
-
- ZeroMemory(&props, sizeof (D2D1_BRUSH_PROPERTIES));
- props.opacity = 1.0;
- // identity matrix
- props.transform._11 = 1;
- props.transform._22 = 1;
- color.r = r;
- color.g = g;
- color.b = b;
- color.a = a;
- hr = rt->CreateSolidColorBrush(
- &color,
- &props,
- &brush);
- if (hr != S_OK)
- logHRESULT(L"error creating solid brush", hr);
- return brush;
-}
-
-IDWriteTextLayout *prepareLayout(uiDrawTextLayout *layout, ID2D1RenderTarget *rt)
-{
- IDWriteTextLayout *dl;
- DWRITE_TEXT_RANGE range;
- IUnknown *unkBrush;
- DWRITE_WORD_WRAPPING wrap;
- FLOAT maxWidth;
- HRESULT hr;
-
- hr = dwfactory->CreateTextLayout(layout->text, layout->textlen,
- layout->format,
- // FLOAT is float, not double, so this should work... TODO
- FLT_MAX, FLT_MAX,
- &dl);
- if (hr != S_OK)
- logHRESULT(L"error creating IDWriteTextLayout", hr);
-
- for (const struct layoutAttr &attr : *(layout->attrs)) {
- range.startPosition = layout->graphemes[attr.start];
- range.length = layout->graphemes[attr.end] - layout->graphemes[attr.start];
- switch (attr.type) {
- case layoutAttrColor:
- if (rt == NULL) // determining extents, not drawing
- break;
- unkBrush = mkSolidBrush(rt,
- attr.components[0],
- attr.components[1],
- attr.components[2],
- attr.components[3]);
- hr = dl->SetDrawingEffect(unkBrush, range);
- unkBrush->Release(); // associated with dl
- break;
- default:
- hr = E_FAIL;
- logHRESULT(L"invalid text attribute type", hr);
- }
- if (hr != S_OK)
- logHRESULT(L"error adding attribute to text layout", hr);
- }
-
- // and set the width
- // this is the only wrapping mode (apart from "no wrap") available prior to Windows 8.1
- wrap = DWRITE_WORD_WRAPPING_WRAP;
- maxWidth = layout->width;
- if (layout->width < 0) {
- wrap = DWRITE_WORD_WRAPPING_NO_WRAP;
- // setting the max width in this case technically isn't needed since the wrap mode will simply ignore the max width, but let's do it just to be safe
- maxWidth = FLT_MAX; // see TODO above
- }
- hr = dl->SetWordWrapping(wrap);
- if (hr != S_OK)
- logHRESULT(L"error setting word wrapping mode", hr);
- hr = dl->SetMaxWidth(maxWidth);
- if (hr != S_OK)
- logHRESULT(L"error setting max layout width", hr);
-
- return dl;
-}
-
-
-void uiDrawTextLayoutSetWidth(uiDrawTextLayout *layout, double width)
-{
- layout->width = width;
-}
-
-// TODO for a single line the height includes the leading; it should not
-void uiDrawTextLayoutExtents(uiDrawTextLayout *layout, double *width, double *height)
-{
- IDWriteTextLayout *dl;
- DWRITE_TEXT_METRICS metrics;
- HRESULT hr;
-
- dl = prepareLayout(layout, NULL);
- hr = dl->GetMetrics(&metrics);
- if (hr != S_OK)
- logHRESULT(L"error getting layout metrics", hr);
- *width = metrics.width;
- // TODO make sure the behavior of this on empty strings is the same on all platforms
- *height = metrics.height;
- dl->Release();
-}
-
-void uiDrawText(uiDrawContext *c, double x, double y, uiDrawTextLayout *layout)
-{
- IDWriteTextLayout *dl;
- D2D1_POINT_2F pt;
- ID2D1Brush *black;
- HRESULT hr;
-
- // TODO document that fully opaque black is the default text color; figure out whether this is upheld in various scenarios on other platforms
- black = mkSolidBrush(c->rt, 0.0, 0.0, 0.0, 1.0);
-
- dl = prepareLayout(layout, c->rt);
- pt.x = x;
- pt.y = y;
- // TODO D2D1_DRAW_TEXT_OPTIONS_NO_SNAP?
- // TODO D2D1_DRAW_TEXT_OPTIONS_CLIP?
- // TODO when setting 8.1 as minimum, D2D1_DRAW_TEXT_OPTIONS_ENABLE_COLOR_FONT?
- c->rt->DrawTextLayout(pt, dl, black, D2D1_DRAW_TEXT_OPTIONS_NONE);
- dl->Release();
-
- black->Release();
-}
-
-void uiDrawTextLayoutSetColor(uiDrawTextLayout *layout, int startChar, int endChar, double r, double g, double b, double a)
-{
- struct layoutAttr attr;
-
- attr.type = layoutAttrColor;
- attr.start = startChar;
- attr.end = endChar;
- attr.components[0] = r;
- attr.components[1] = g;
- attr.components[2] = b;
- attr.components[3] = a;
- layout->attrs->push_back(attr);
-}