{"id":13531952,"url":"https://github.com/Chlumsky/msdfgen","last_synced_at":"2025-04-01T20:30:45.713Z","repository":{"id":37432235,"uuid":"56971819","full_name":"Chlumsky/msdfgen","owner":"Chlumsky","description":"Multi-channel signed distance field generator","archived":false,"fork":false,"pushed_at":"2025-03-09T10:31:15.000Z","size":2531,"stargazers_count":4183,"open_issues_count":2,"forks_count":438,"subscribers_count":100,"default_branch":"master","last_synced_at":"2025-03-27T00:04:24.125Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Chlumsky.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"github":"Chlumsky"}},"created_at":"2016-04-24T13:03:47.000Z","updated_at":"2025-03-26T03:46:37.000Z","dependencies_parsed_at":"2024-01-05T13:49:50.132Z","dependency_job_id":"44ce19df-9f6c-45c7-8d26-bab3d815153c","html_url":"https://github.com/Chlumsky/msdfgen","commit_stats":null,"previous_names":[],"tags_count":13,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Chlumsky%2Fmsdfgen","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Chlumsky%2Fmsdfgen/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Chlumsky%2Fmsdfgen/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Chlumsky%2Fmsdfgen/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Chlumsky","download_url":"https://codeload.github.com/Chlumsky/msdfgen/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":246709923,"owners_count":20821297,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-08-01T07:01:07.160Z","updated_at":"2025-04-01T20:30:40.703Z","avatar_url":"https://github.com/Chlumsky.png","language":"C++","funding_links":["https://github.com/sponsors/Chlumsky"],"categories":["ComputerGraphics \u0026\u0026 Shadingv","Basic MSDF usage","C++","Libraries","3rd party libraries"],"sub_categories":["Google Analytics","C++","Fork of https://github.com/FrostAtom/awesome_wotlk/"],"readme":"# Multi-channel signed distance field generator\n\nThis is a utility for generating signed distance fields from vector shapes and font glyphs,\nwhich serve as a texture representation that can be used in real-time graphics to efficiently reproduce said shapes.\nAlthough it can also be used to generate conventional signed distance fields best known from\n[this Valve paper](https://steamcdn-a.akamaihd.net/apps/valve/2007/SIGGRAPH2007_AlphaTestedMagnification.pdf)\nand perpendicular distance fields, its primary purpose is to generate multi-channel distance fields,\nusing a method I have developed. Unlike monochrome distance fields, they have the ability\nto reproduce sharp corners almost perfectly by utilizing all three color channels.\n\nThe following comparison demonstrates the improvement in image quality.\n\n![demo-msdf16](https://user-images.githubusercontent.com/18639794/106391899-e37ebe80-63ef-11eb-988b-4764004bb196.png)\n![demo-sdf16](https://user-images.githubusercontent.com/18639794/106391905-e679af00-63ef-11eb-96c3-993176330911.png)\n![demo-sdf32](https://user-images.githubusercontent.com/18639794/106391906-e7aadc00-63ef-11eb-8f84-d402d0dd9174.png)\n\n- To learn more about this method, you can read my [Master's thesis](https://github.com/Chlumsky/msdfgen/files/3050967/thesis.pdf).\n- Check out my [MSDF-Atlas-Gen](https://github.com/Chlumsky/msdf-atlas-gen) if you want to generate entire font atlases for text rendering.\n- See what's new in the [changelog](CHANGELOG.md).\n\n## Getting started\n\nThe project can be used either as a library or as a console program. It is divided into two parts, **[core](core)**\nand **[extensions](ext)**. The core module has no dependencies and only uses bare C++. It contains all\nkey data structures and algorithms, which can be accessed through the [msdfgen.h](msdfgen.h) header.\nExtensions contain utilities for loading fonts and SVG files, as well as saving PNG images.\nThose are exposed by the [msdfgen-ext.h](msdfgen-ext.h) header. This module uses\n[FreeType](https://freetype.org/),\n[TinyXML2](https://www.grinninglizard.com/tinyxml2/),\n[libpng](http://www.libpng.org/pub/png/libpng.html),\nand (optionally) [Skia](https://skia.org/).\n\nAdditionally, there is the [main.cpp](main.cpp), which wraps the functionality into\na comprehensive standalone console program. To start using the program immediately,\nthere is a Windows binary available for download in the [\"Releases\" section](https://github.com/Chlumsky/msdfgen/releases).\nTo use the project as a library, you may install it via the [vcpkg](https://vcpkg.io) package manager as\n```\nvcpkg install msdfgen\n```\nOr, to build the project from source, you may use the included [CMake script](CMakeLists.txt).\nIn its default configuration, it requires [vcpkg](https://vcpkg.io) as the provider for third-party library dependencies.\nIf you set the environment variable `VCPKG_ROOT` to the vcpkg directory,\nthe CMake configuration will take care of fetching all required packages from vcpkg.\n\n## Console commands\n\nThe standalone program is executed as\n```\nmsdfgen.exe \u003cmode\u003e \u003cinput\u003e \u003coptions\u003e\n```\nwhere only the input specification is required.\n\nMode can be one of:\n - **sdf** \u0026ndash; generates a conventional monochrome (true) signed distance field.\n - **psdf** \u0026ndash; generates a monochrome signed perpendicular distance field.\n - **msdf** (default) \u0026ndash; generates a multi-channel signed distance field using my new method.\n - **mtsdf** \u0026ndash; generates a combined multi-channel and true signed distance field in the alpha channel.\n\nThe input can be specified as one of:\n - **-font \\\u003cfilename.ttf\\\u003e \\\u003ccharacter code\\\u003e** \u0026ndash; to load a glyph from a font file.\n   Character code can be expressed as either a decimal (63) or hexadecimal (0x3F) Unicode value, or an ASCII character\n   in single quotes ('?').\n - **-svg \\\u003cfilename.svg\\\u003e** \u0026ndash; to load an SVG file. Note that only the last vector path in the file will be used.\n - **-shapedesc \\\u003cfilename.txt\\\u003e**, -defineshape \\\u003cdefinition\\\u003e, -stdin \u0026ndash; to load a text description of the shape\n   from either a file, the next argument, or the standard input, respectively. Its syntax is documented further down.\n\nThe complete list of available options can be printed with **-help**.\nSome of the important ones are:\n - **-o \\\u003cfilename\\\u003e** \u0026ndash; specifies the output file name. The desired format will be deduced from the extension\n   (png, bmp, tiff, rgba, fl32, txt, bin). Otherwise, use -format.\n - **-dimensions \\\u003cwidth\\\u003e \\\u003cheight\\\u003e** \u0026ndash; specifies the dimensions of the output distance field (in pixels).\n - **-range \\\u003crange\\\u003e**, **-pxrange \\\u003crange\\\u003e** \u0026ndash; specifies the width of the range around the shape\n   between the minimum and maximum representable signed distance in shape units or distance field pixels, respectivelly.\n - **-scale \\\u003cscale\\\u003e** \u0026ndash; sets the scale used to convert shape units to distance field pixels.\n - **-translate \\\u003cx\\\u003e \\\u003cy\\\u003e** \u0026ndash; sets the translation of the shape in shape units. Otherwise the origin (0, 0)\n   lies in the bottom left corner.\n - **-autoframe** \u0026ndash; automatically frames the shape to fit the distance field. If the output must be precisely aligned,\n   you should manually position it using -translate and -scale instead.\n - **-angle \\\u003cangle\\\u003e** \u0026ndash; specifies the maximum angle to be considered a corner.\n   Can be expressed in radians (3.0) or degrees with D at the end (171.9D).\n - **-testrender \\\u003cfilename.png\\\u003e \\\u003cwidth\\\u003e \\\u003cheight\\\u003e** - tests the generated distance field by using it to render an image\n   of the original shape into a PNG file with the specified dimensions. Alternatively, -testrendermulti renders\n   an image without combining the color channels, and may give you an insight in how the multi-channel distance field works.\n - **-exportshape \\\u003cfilename.txt\\\u003e** - saves the text description of the shape with edge coloring to the specified file.\n   This can be later edited and used as input through -shapedesc.\n - **-printmetrics** \u0026ndash; prints some useful information about the shape's layout.\n\nFor example,\n```\nmsdfgen.exe msdf -font C:\\Windows\\Fonts\\arialbd.ttf 'M' -o msdf.png -dimensions 32 32 -pxrange 4 -autoframe -testrender render.png 1024 1024\n```\n\nwill take the glyph capital M from the Arial Bold typeface, generate a 32\u0026times;32 multi-channel distance field\nwith a 4 pixels wide distance range, store it into msdf.png, and create a test render of the glyph as render.png.\n\n**Note:** Do not use `-autoframe` to generate character maps! It is intended as a quick preview only.\n\n## Library API\n\nIf you choose to use this utility inside your own program, there are a few simple steps you need to perform\nin order to generate a distance field. Please note that all classes and functions are in the `msdfgen` namespace.\n\n - Acquire a `Shape` object. You can either load it via `loadGlyph` or `loadSvgShape`, or construct it manually.\n   It consists of closed contours, which in turn consist of edges. An edge is represented by a `LinearEdge`, `QuadraticEdge`,\n   or `CubicEdge`. You can construct them from two endpoints and 0 to 2 Bézier control points.\n - Normalize the shape using its `normalize` method and assign colors to edges if you need a multi-channel SDF.\n   This can be performed automatically using the `edgeColoringSimple` (or other) heuristic, or manually by setting each edge's\n   `color` member. Keep in mind that at least two color channels must be turned on in each edge.\n - Call `generateSDF`, `generatePSDF`, `generateMSDF`, or `generateMTSDF` to generate a distance field into a floating point\n   `Bitmap` object. This can then be worked with further or saved to a file using `saveBmp`, `savePng`, `saveTiff`, etc.\n - You may also render an image from the distance field using `renderSDF`. Consider calling `simulate8bit`\n   on the distance field beforehand to simulate the standard 8 bits/channel image format.\n\nExample:\n```c++\n#include \u003cmsdfgen.h\u003e\n#include \u003cmsdfgen-ext.h\u003e\n\nusing namespace msdfgen;\n\nint main() {\n    if (FreetypeHandle *ft = initializeFreetype()) {\n        if (FontHandle *font = loadFont(ft, \"C:\\\\Windows\\\\Fonts\\\\arialbd.ttf\")) {\n            Shape shape;\n            if (loadGlyph(shape, font, 'A', FONT_SCALING_EM_NORMALIZED)) {\n                shape.normalize();\n                //                      max. angle\n                edgeColoringSimple(shape, 3.0);\n                //          output width, height\n                Bitmap\u003cfloat, 3\u003e msdf(32, 32);\n                //                            scale, translation (in em's)\n                SDFTransformation t(Projection(32.0, Vector2(0.125, 0.125)), Range(0.125));\n                generateMSDF(msdf, shape, t);\n                savePng(msdf, \"output.png\");\n            }\n            destroyFont(font);\n        }\n        deinitializeFreetype(ft);\n    }\n    return 0;\n}\n```\n\n## Using a multi-channel distance field\n\nUsing a multi-channel distance field generated by this program is similarly simple to how a monochrome distance field is used.\nThe only additional operation is computing the **median** of the three channels inside the fragment shader,\nright after sampling the distance field. This signed distance value can then be used the same way as usual.\n\nThe following is an example GLSL fragment shader with anti-aliasing:\n\n```glsl\nin vec2 texCoord;\nout vec4 color;\nuniform sampler2D msdf;\nuniform vec4 bgColor;\nuniform vec4 fgColor;\n\nfloat median(float r, float g, float b) {\n    return max(min(r, g), min(max(r, g), b));\n}\n\nvoid main() {\n    vec3 msd = texture(msdf, texCoord).rgb;\n    float sd = median(msd.r, msd.g, msd.b);\n    float screenPxDistance = screenPxRange()*(sd - 0.5);\n    float opacity = clamp(screenPxDistance + 0.5, 0.0, 1.0);\n    color = mix(bgColor, fgColor, opacity);\n}\n```\n\nHere, `screenPxRange()` represents the distance field range in output screen pixels. For example, if the pixel range was set to 2\nwhen generating a 32x32 distance field, and it is used to draw a quad that is 72x72 pixels on the screen,\nit should return 4.5 (because 72/32 * 2 = 4.5).\n**For 2D rendering, this can generally be replaced by a precomputed uniform value.**\n\nFor rendering in a **3D perspective only**, where the texture scale varies across the screen,\nyou may want to implement this function with fragment derivatives in the following way.\nI would suggest precomputing `unitRange` as a uniform variable instead of `pxRange` for better performance.\n\n```glsl\nuniform float pxRange; // set to distance field's pixel range\n\nfloat screenPxRange() {\n    vec2 unitRange = vec2(pxRange)/vec2(textureSize(msdf, 0));\n    vec2 screenTexSize = vec2(1.0)/fwidth(texCoord);\n    return max(0.5*dot(unitRange, screenTexSize), 1.0);\n}\n```\n\n`screenPxRange()` must never be lower than 1. If it is lower than 2, there is a high probability that the anti-aliasing will fail\nand you may want to re-generate your distance field with a wider range.\n\n## Shape description syntax\n\nThe text shape description has the following syntax.\n - Each closed contour is enclosed by braces: `{ \u003ccontour 1\u003e } { \u003ccontour 2\u003e }`\n - Each point (and control point) is written as two real numbers separated by a comma.\n - Points in a contour are separated with semicolons.\n - The last point of each contour must be equal to the first, or the symbol `#` can be used, which represents the first point.\n - There can be an edge segment specification between any two points, also separated by semicolons.\n   This can include the edge's color (`c`, `m`, `y` or `w`) and/or one or two Bézier curve control points inside parentheses.\n\nFor example,\n```\n{ -1, -1; m; -1, +1; y; +1, +1; m; +1, -1; y; # }\n```\nwould represent a square with magenta and yellow edges,\n```\n{ 0, 1; (+1.6, -0.8; -1.6, -0.8); # }\n```\nis a teardrop shape formed by a single cubic Bézier curve.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FChlumsky%2Fmsdfgen","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FChlumsky%2Fmsdfgen","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FChlumsky%2Fmsdfgen/lists"}