mirror of https://github.com/Chlumsky/msdfgen.git
Compare commits
4 Commits
fffbe8758c
...
0ba84ab8af
| Author | SHA1 | Date |
|---|---|---|
|
|
0ba84ab8af | |
|
|
85e8b3d47b | |
|
|
aa9478e9ae | |
|
|
d7ac1e084d |
|
|
@ -26,6 +26,7 @@ output.png
|
|||
render.png
|
||||
CMakeUserPresets.json
|
||||
out/
|
||||
/all-in-one/
|
||||
/build_xcode/
|
||||
/cmake-gen.bat
|
||||
/line-ending-check.bat
|
||||
|
|
|
|||
19
CHANGELOG.md
19
CHANGELOG.md
|
|
@ -1,4 +1,23 @@
|
|||
|
||||
## Version 1.12 (2024-05-18)
|
||||
|
||||
- Added the possibility to specify asymmetrical distance range (`-arange`, `-apxrange`)
|
||||
- Added the ability to export the shape into an SVG file (`-exportsvg`)
|
||||
- Edge coloring no longer colors smooth contours white, which has been found suboptimal
|
||||
- Fixed incorrect scaling of font glyph coordinates. To preserve backwards compatibility, the user has to enable the fix with an explicit additional argument:
|
||||
- `-emnormalize` in standalone, `FONT_SCALING_EM_NORMALIZED` in API for coordinates in ems
|
||||
- `-noemnormalize` in standalone, `FONT_SCALING_NONE` in API for raw integer coordinates
|
||||
- The default (backwards-compatible) behavior will change in a future version; a warning will be displayed if neither option is set
|
||||
- Added two new developer-friendly export image formats: RGBA and FL32
|
||||
- `-size` parameter renamed to `-dimensions` for clarity (old one will be kept for compatibility)
|
||||
- `generate*SDF` functions now combine projection and range into a single argument (`SDFTransformation`)
|
||||
- Conversion of floating point color values to 8-bit integers adjusted to match graphics hardware
|
||||
- Improved edge deconvergence procedure and made sure that calling `Shape::normalize` a second time has no effect
|
||||
- Fixed certain edge cases where Skia geometry preprocessing wouldn't make the geometry fully compliant
|
||||
- The term "perpendicular distance" now used instead of "pseudo-distance" (PSDF instead of PseudoSDF in API)
|
||||
- Fixed a bug in `savePng` where `fclose` could be called on null pointer
|
||||
- Minor code improvements
|
||||
|
||||
## Version 1.11 (2023-11-11)
|
||||
|
||||
- Reworked SVG parser, which now supports multiple paths and other shapes - requires Skia
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2016 - 2024 Viktor Chlumsky
|
||||
Copyright (c) 2014 - 2024 Viktor Chlumsky
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
|
|
|||
|
|
@ -67,7 +67,7 @@ The input can be specified as one of:
|
|||
The complete list of available options can be printed with **-help**.
|
||||
Some of the important ones are:
|
||||
- **-o \<filename\>** – specifies the output file name. The desired format will be deduced from the extension
|
||||
(png, bmp, tif, txt, bin). Otherwise, use -format.
|
||||
(png, bmp, tiff, rgba, fl32, txt, bin). Otherwise, use -format.
|
||||
- **-dimensions \<width\> \<height\>** – specifies the dimensions of the output distance field (in pixels).
|
||||
- **-range \<range\>**, **-pxrange \<range\>** – specifies the width of the range around the shape
|
||||
between the minimum and maximum representable signed distance in shape units or distance field pixels, respectivelly.
|
||||
|
|
@ -107,7 +107,7 @@ in order to generate a distance field. Please note that all classes and function
|
|||
This can be performed automatically using the `edgeColoringSimple` (or other) heuristic, or manually by setting each edge's
|
||||
`color` member. Keep in mind that at least two color channels must be turned on in each edge.
|
||||
- Call `generateSDF`, `generatePSDF`, `generateMSDF`, or `generateMTSDF` to generate a distance field into a floating point
|
||||
`Bitmap` object. This can then be worked with further or saved to a file using `saveBmp`, `savePng`, or `saveTiff`.
|
||||
`Bitmap` object. This can then be worked with further or saved to a file using `saveBmp`, `savePng`, `saveTiff`, etc.
|
||||
- You may also render an image from the distance field using `renderSDF`. Consider calling `simulate8bit`
|
||||
on the distance field beforehand to simulate the standard 8 bits/channel image format.
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,96 @@
|
|||
@echo off
|
||||
setlocal
|
||||
pushd "%~dp0"
|
||||
|
||||
if "%VCVARSALL%"=="" set "VCVARSALL=C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Auxiliary\Build\vcvarsall.bat"
|
||||
if "%VCTOOLSET%"=="" set "VCTOOLSET=VC143"
|
||||
|
||||
if not exist "%VCVARSALL%" (
|
||||
echo vcvarsall.bat not found, set VCVARSALL to its path
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
if "%1"=="" (
|
||||
echo No version specified, using current
|
||||
set "version=current"
|
||||
) else (
|
||||
set "version=%1"
|
||||
)
|
||||
set "builddir=%~dp0\build\release-%version%"
|
||||
|
||||
if exist "%builddir%" (
|
||||
echo Deleting contents of %builddir%
|
||||
rd /s /q "%builddir%"
|
||||
)
|
||||
|
||||
cmake --preset win32 -B "%builddir%\build-win32"
|
||||
cmake --preset win32-omp -B "%builddir%\build-win32-omp"
|
||||
cmake --preset win64 -B "%builddir%\build-win64"
|
||||
cmake --preset win64-omp -B "%builddir%\build-win64-omp"
|
||||
|
||||
cmake --build "%builddir%\build-win32" --config Release
|
||||
cmake --build "%builddir%\build-win32-omp" --config Release
|
||||
cmake --build "%builddir%\build-win64" --config Release
|
||||
cmake --build "%builddir%\build-win64-omp" --config Release
|
||||
|
||||
mkdir "%builddir%\rel-win32\msdfgen"
|
||||
mkdir "%builddir%\rel-win32-omp\msdfgen"
|
||||
mkdir "%builddir%\rel-win64\msdfgen"
|
||||
mkdir "%builddir%\rel-win64-omp\msdfgen"
|
||||
|
||||
copy "%builddir%\build-win32\Release\msdfgen.exe" "%builddir%\rel-win32\msdfgen"
|
||||
copy "%builddir%\build-win32-omp\Release\msdfgen.exe" "%builddir%\rel-win32-omp\msdfgen"
|
||||
copy "%builddir%\build-win64\Release\msdfgen.exe" "%builddir%\rel-win64\msdfgen"
|
||||
copy "%builddir%\build-win64-omp\Release\msdfgen.exe" "%builddir%\rel-win64-omp\msdfgen"
|
||||
|
||||
echo msdfgen.exe -defineshape "{ 1471,0; 1149,0; 1021,333; 435,333; 314,0; 0,0; 571,1466; 884,1466; # }{ 926,580; 724,1124; 526,580; # }" -size 16 16 -autoframe -testrender render.png 1024 1024 > "%builddir%\example.bat"
|
||||
|
||||
copy "%builddir%\example.bat" "%builddir%\rel-win32\msdfgen"
|
||||
copy "%builddir%\example.bat" "%builddir%\rel-win32-omp\msdfgen"
|
||||
copy "%builddir%\example.bat" "%builddir%\rel-win64\msdfgen"
|
||||
copy "%builddir%\example.bat" "%builddir%\rel-win64-omp\msdfgen"
|
||||
|
||||
call "%VCVARSALL%" x64
|
||||
|
||||
set "omp32dll=%VCToolsRedistDir%\x86\Microsoft.%VCTOOLSET%.OPENMP\vcomp140.dll"
|
||||
set "omp64dll=%VCToolsRedistDir%\x64\Microsoft.%VCTOOLSET%.OPENMP\vcomp140.dll"
|
||||
|
||||
if not exist "%omp32dll%" (
|
||||
echo vcomp140.dll [x86] not found, make sure to set VCTOOLSET or update this script
|
||||
exit /b 1
|
||||
)
|
||||
if not exist "%omp64dll%" (
|
||||
echo vcomp140.dll [x64] not found, make sure to set VCTOOLSET or update this script
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
copy "%omp32dll%" "%builddir%\rel-win32-omp\msdfgen"
|
||||
copy "%omp64dll%" "%builddir%\rel-win64-omp\msdfgen"
|
||||
|
||||
if not exist "C:\Program Files\7-Zip\7z.exe" (
|
||||
echo 7-Zip not found, you have to package it manually
|
||||
exit /b 0
|
||||
)
|
||||
|
||||
pushd "%builddir%\rel-win32"
|
||||
"C:\Program Files\7-Zip\7z.exe" a "..\msdfgen-%version%-win32.zip" msdfgen
|
||||
cd msdfgen
|
||||
call example.bat
|
||||
popd
|
||||
pushd "%builddir%\rel-win32-omp"
|
||||
"C:\Program Files\7-Zip\7z.exe" a "..\msdfgen-%version%-win32-openmp.zip" msdfgen
|
||||
cd msdfgen
|
||||
call example.bat
|
||||
popd
|
||||
pushd "%builddir%\rel-win64"
|
||||
"C:\Program Files\7-Zip\7z.exe" a "..\msdfgen-%version%-win64.zip" msdfgen
|
||||
cd msdfgen
|
||||
call example.bat
|
||||
popd
|
||||
pushd "%builddir%\rel-win64-omp"
|
||||
"C:\Program Files\7-Zip\7z.exe" a "..\msdfgen-%version%-win64-openmp.zip" msdfgen
|
||||
cd msdfgen
|
||||
call example.bat
|
||||
popd
|
||||
|
||||
popd
|
||||
|
|
@ -4,6 +4,8 @@
|
|||
#include <cstdlib>
|
||||
#include "arithmetics.hpp"
|
||||
|
||||
#define DECONVERGE_OVERSHOOT 1.11111111111111111 // moves control points slightly more than necessary to account for floating-point errors
|
||||
|
||||
namespace msdfgen {
|
||||
|
||||
Shape::Shape() : inverseYAxis(false) { }
|
||||
|
|
@ -39,13 +41,23 @@ bool Shape::validate() const {
|
|||
return true;
|
||||
}
|
||||
|
||||
static void deconvergeEdge(EdgeHolder &edgeHolder, int param) {
|
||||
static void deconvergeEdge(EdgeHolder &edgeHolder, int param, Vector2 vector) {
|
||||
switch (edgeHolder->type()) {
|
||||
case (int) QuadraticSegment::EDGE_TYPE:
|
||||
edgeHolder = static_cast<const QuadraticSegment *>(&*edgeHolder)->convertToCubic();
|
||||
// fallthrough
|
||||
case (int) CubicSegment::EDGE_TYPE:
|
||||
static_cast<CubicSegment *>(&*edgeHolder)->deconverge(param, MSDFGEN_DECONVERGENCE_FACTOR);
|
||||
{
|
||||
Point2 *p = static_cast<CubicSegment *>(&*edgeHolder)->p;
|
||||
switch (param) {
|
||||
case 0:
|
||||
p[1] += (p[1]-p[0]).length()*vector;
|
||||
break;
|
||||
case 1:
|
||||
p[2] += (p[2]-p[3]).length()*vector;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -59,13 +71,19 @@ void Shape::normalize() {
|
|||
contour->edges.push_back(EdgeHolder(parts[1]));
|
||||
contour->edges.push_back(EdgeHolder(parts[2]));
|
||||
} else {
|
||||
// Push apart convergent edge segments
|
||||
EdgeHolder *prevEdge = &contour->edges.back();
|
||||
for (std::vector<EdgeHolder>::iterator edge = contour->edges.begin(); edge != contour->edges.end(); ++edge) {
|
||||
Vector2 prevDir = (*prevEdge)->direction(1).normalize();
|
||||
Vector2 curDir = (*edge)->direction(0).normalize();
|
||||
if (dotProduct(prevDir, curDir) < MSDFGEN_CORNER_DOT_EPSILON-1) {
|
||||
deconvergeEdge(*prevEdge, 1);
|
||||
deconvergeEdge(*edge, 0);
|
||||
double factor = DECONVERGE_OVERSHOOT*sqrt(1-(MSDFGEN_CORNER_DOT_EPSILON-1)*(MSDFGEN_CORNER_DOT_EPSILON-1))/(MSDFGEN_CORNER_DOT_EPSILON-1);
|
||||
Vector2 axis = factor*(curDir-prevDir).normalize();
|
||||
// Determine curve ordering using third-order derivative (t = 0) of crossProduct((*prevEdge)->point(1-t)-p0, (*edge)->point(t)-p0) where p0 is the corner (*edge)->point(0)
|
||||
if (crossProduct((*prevEdge)->directionChange(1), (*edge)->direction(0))+crossProduct((*edge)->directionChange(0), (*prevEdge)->direction(1)) < 0)
|
||||
axis = -axis;
|
||||
deconvergeEdge(*prevEdge, 1, axis.getOrthogonal(true));
|
||||
deconvergeEdge(*edge, 0, axis.getOrthogonal(false));
|
||||
}
|
||||
prevEdge = &*edge;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,8 +9,6 @@ namespace msdfgen {
|
|||
|
||||
// Threshold of the dot product of adjacent edge directions to be considered convergent.
|
||||
#define MSDFGEN_CORNER_DOT_EPSILON .000001
|
||||
// The proportional amount by which a curve's control point will be adjusted to eliminate convergent corners.
|
||||
#define MSDFGEN_DECONVERGENCE_FACTOR .000001
|
||||
|
||||
/// Vector shape representation.
|
||||
class Shape {
|
||||
|
|
|
|||
|
|
@ -524,18 +524,4 @@ EdgeSegment *QuadraticSegment::convertToCubic() const {
|
|||
return new CubicSegment(p[0], mix(p[0], p[1], 2/3.), mix(p[1], p[2], 1/3.), p[2], color);
|
||||
}
|
||||
|
||||
void CubicSegment::deconverge(int param, double amount) {
|
||||
Vector2 dir = direction(param);
|
||||
Vector2 normal = dir.getOrthonormal();
|
||||
double h = dotProduct(directionChange(param)-dir, normal);
|
||||
switch (param) {
|
||||
case 0:
|
||||
p[1] += amount*(dir+sign(h)*sqrt(fabs(h))*normal);
|
||||
break;
|
||||
case 1:
|
||||
p[2] -= amount*(dir-sign(h)*sqrt(fabs(h))*normal);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -141,8 +141,6 @@ public:
|
|||
void moveEndPoint(Point2 to);
|
||||
void splitInThirds(EdgeSegment *&part0, EdgeSegment *&part1, EdgeSegment *&part2) const;
|
||||
|
||||
void deconverge(int param, double amount);
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,79 @@
|
|||
|
||||
#include "export-svg.h"
|
||||
|
||||
#include <cstdio>
|
||||
#include "edge-segments.h"
|
||||
|
||||
namespace msdfgen {
|
||||
|
||||
static void writeSvgCoord(FILE *f, Point2 coord) {
|
||||
fprintf(f, "%.17g %.17g", coord.x, coord.y);
|
||||
}
|
||||
|
||||
static void writeSvgPathDef(FILE *f, const Shape &shape) {
|
||||
bool beginning = true;
|
||||
for (const Contour &c : shape.contours) {
|
||||
if (c.edges.empty())
|
||||
continue;
|
||||
if (beginning)
|
||||
beginning = false;
|
||||
else
|
||||
fputc(' ', f);
|
||||
fputs("M ", f);
|
||||
writeSvgCoord(f, c.edges[0]->controlPoints()[0]);
|
||||
for (const EdgeHolder &e : c.edges) {
|
||||
const Point2 *cp = e->controlPoints();
|
||||
switch (e->type()) {
|
||||
case (int) LinearSegment::EDGE_TYPE:
|
||||
fputs(" L ", f);
|
||||
writeSvgCoord(f, cp[1]);
|
||||
break;
|
||||
case (int) QuadraticSegment::EDGE_TYPE:
|
||||
fputs(" Q ", f);
|
||||
writeSvgCoord(f, cp[1]);
|
||||
fputc(' ', f);
|
||||
writeSvgCoord(f, cp[2]);
|
||||
break;
|
||||
case (int) CubicSegment::EDGE_TYPE:
|
||||
fputs(" C ", f);
|
||||
writeSvgCoord(f, cp[1]);
|
||||
fputc(' ', f);
|
||||
writeSvgCoord(f, cp[2]);
|
||||
fputc(' ', f);
|
||||
writeSvgCoord(f, cp[3]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
fputs(" Z", f);
|
||||
}
|
||||
}
|
||||
|
||||
bool saveSvgShape(const Shape &shape, const char *filename) {
|
||||
if (FILE *f = fopen(filename, "w")) {
|
||||
fputs("<svg xmlns=\"http://www.w3.org/2000/svg\"><path", f);
|
||||
if (!shape.inverseYAxis)
|
||||
fputs(" transform=\"scale(1 -1)\"", f);
|
||||
fputs(" d=\"", f);
|
||||
writeSvgPathDef(f, shape);
|
||||
fputs("\"/></svg>\n", f);
|
||||
fclose(f);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool saveSvgShape(const Shape &shape, const Shape::Bounds &bounds, const char *filename) {
|
||||
if (FILE *f = fopen(filename, "w")) {
|
||||
fprintf(f, "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"%.17g %.17g %.17g %.17g\"><path", bounds.l, bounds.b, bounds.r-bounds.l, bounds.t-bounds.b);
|
||||
if (!shape.inverseYAxis)
|
||||
fprintf(f, " transform=\"translate(0 %.17g) scale(1 -1)\"", bounds.b+bounds.t);
|
||||
fputs(" d=\"", f);
|
||||
writeSvgPathDef(f, shape);
|
||||
fputs("\"/></svg>\n", f);
|
||||
fclose(f);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "Shape.h"
|
||||
|
||||
namespace msdfgen {
|
||||
|
||||
bool saveSvgShape(const Shape &shape, const char *filename);
|
||||
bool saveSvgShape(const Shape &shape, const Shape::Bounds &bounds, const char *filename);
|
||||
|
||||
}
|
||||
|
|
@ -8,10 +8,12 @@
|
|||
#ifdef MSDFGEN_USE_CPP11
|
||||
#include <cstdint>
|
||||
#else
|
||||
namespace msdfgen {
|
||||
typedef int int32_t;
|
||||
typedef unsigned uint32_t;
|
||||
typedef unsigned short uint16_t;
|
||||
typedef unsigned char uint8_t;
|
||||
}
|
||||
#endif
|
||||
|
||||
#include "pixel-conversion.hpp"
|
||||
|
|
|
|||
|
|
@ -0,0 +1,39 @@
|
|||
|
||||
#include "save-fl32.h"
|
||||
|
||||
#include <cstdio>
|
||||
|
||||
namespace msdfgen {
|
||||
|
||||
// Requires byte reversal for floats on big-endian platform
|
||||
#ifndef __BIG_ENDIAN__
|
||||
|
||||
template <int N>
|
||||
bool saveFl32(const BitmapConstRef<float, N> &bitmap, const char *filename) {
|
||||
if (FILE *f = fopen(filename, "wb")) {
|
||||
byte header[16] = { byte('F'), byte('L'), byte('3'), byte('2') };
|
||||
header[4] = byte(bitmap.height);
|
||||
header[5] = byte(bitmap.height>>8);
|
||||
header[6] = byte(bitmap.height>>16);
|
||||
header[7] = byte(bitmap.height>>24);
|
||||
header[8] = byte(bitmap.width);
|
||||
header[9] = byte(bitmap.width>>8);
|
||||
header[10] = byte(bitmap.width>>16);
|
||||
header[11] = byte(bitmap.width>>24);
|
||||
header[12] = byte(N);
|
||||
fwrite(header, 1, 16, f);
|
||||
fwrite(bitmap.pixels, sizeof(float), N*bitmap.width*bitmap.height, f);
|
||||
fclose(f);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
template bool saveFl32(const BitmapConstRef<float, 1> &bitmap, const char *filename);
|
||||
template bool saveFl32(const BitmapConstRef<float, 2> &bitmap, const char *filename);
|
||||
template bool saveFl32(const BitmapConstRef<float, 3> &bitmap, const char *filename);
|
||||
template bool saveFl32(const BitmapConstRef<float, 4> &bitmap, const char *filename);
|
||||
|
||||
#endif
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "BitmapRef.hpp"
|
||||
|
||||
namespace msdfgen {
|
||||
|
||||
/// Saves the bitmap as an uncompressed floating-point FL32 file, which can be decoded trivially.
|
||||
template <int N>
|
||||
bool saveFl32(const BitmapConstRef<float, N> &bitmap, const char *filename);
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,133 @@
|
|||
|
||||
#include "save-rgba.h"
|
||||
|
||||
#include <cstdio>
|
||||
#include "pixel-conversion.hpp"
|
||||
|
||||
namespace msdfgen {
|
||||
|
||||
class RgbaFileOutput {
|
||||
FILE *file;
|
||||
|
||||
public:
|
||||
RgbaFileOutput(const char *filename, unsigned width, unsigned height) {
|
||||
if ((file = fopen(filename, "wb"))) {
|
||||
byte header[12] = { byte('R'), byte('G'), byte('B'), byte('A') };
|
||||
header[4] = byte(width>>24);
|
||||
header[5] = byte(width>>16);
|
||||
header[6] = byte(width>>8);
|
||||
header[7] = byte(width);
|
||||
header[8] = byte(height>>24);
|
||||
header[9] = byte(height>>16);
|
||||
header[10] = byte(height>>8);
|
||||
header[11] = byte(height);
|
||||
fwrite(header, 1, 12, file);
|
||||
}
|
||||
}
|
||||
|
||||
~RgbaFileOutput() {
|
||||
if (file)
|
||||
fclose(file);
|
||||
}
|
||||
|
||||
void writePixel(const byte rgba[4]) {
|
||||
fwrite(rgba, 1, 4, file);
|
||||
}
|
||||
|
||||
operator FILE *() {
|
||||
return file;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
bool saveRgba(const BitmapConstRef<byte, 1> &bitmap, const char *filename) {
|
||||
RgbaFileOutput output(filename, bitmap.width, bitmap.height);
|
||||
if (output) {
|
||||
byte rgba[4] = { byte(0), byte(0), byte(0), byte(0xff) };
|
||||
for (int y = bitmap.height; y--;) {
|
||||
for (const byte *p = bitmap(0, y), *end = p+bitmap.width; p < end; ++p) {
|
||||
rgba[0] = rgba[1] = rgba[2] = *p;
|
||||
output.writePixel(rgba);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool saveRgba(const BitmapConstRef<byte, 3> &bitmap, const char *filename) {
|
||||
RgbaFileOutput output(filename, bitmap.width, bitmap.height);
|
||||
if (output) {
|
||||
byte rgba[4] = { byte(0), byte(0), byte(0), byte(0xff) };
|
||||
for (int y = bitmap.height; y--;) {
|
||||
for (const byte *p = bitmap(0, y), *end = p+3*bitmap.width; p < end; p += 3) {
|
||||
rgba[0] = p[0], rgba[1] = p[1], rgba[2] = p[2];
|
||||
output.writePixel(rgba);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool saveRgba(const BitmapConstRef<byte, 4> &bitmap, const char *filename) {
|
||||
RgbaFileOutput output(filename, bitmap.width, bitmap.height);
|
||||
if (output) {
|
||||
for (int y = bitmap.height; y--;)
|
||||
fwrite(bitmap(0, y), 1, 4*bitmap.width, output);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool saveRgba(const BitmapConstRef<float, 1> &bitmap, const char *filename) {
|
||||
RgbaFileOutput output(filename, bitmap.width, bitmap.height);
|
||||
if (output) {
|
||||
byte rgba[4] = { byte(0), byte(0), byte(0), byte(0xff) };
|
||||
for (int y = bitmap.height; y--;) {
|
||||
for (const float *p = bitmap(0, y), *end = p+bitmap.width; p < end; ++p) {
|
||||
rgba[0] = rgba[1] = rgba[2] = pixelFloatToByte(*p);
|
||||
output.writePixel(rgba);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool saveRgba(const BitmapConstRef<float, 3> &bitmap, const char *filename) {
|
||||
RgbaFileOutput output(filename, bitmap.width, bitmap.height);
|
||||
if (output) {
|
||||
byte rgba[4] = { byte(0), byte(0), byte(0), byte(0xff) };
|
||||
for (int y = bitmap.height; y--;) {
|
||||
for (const float *p = bitmap(0, y), *end = p+3*bitmap.width; p < end; p += 3) {
|
||||
rgba[0] = pixelFloatToByte(p[0]);
|
||||
rgba[1] = pixelFloatToByte(p[1]);
|
||||
rgba[2] = pixelFloatToByte(p[2]);
|
||||
output.writePixel(rgba);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool saveRgba(const BitmapConstRef<float, 4> &bitmap, const char *filename) {
|
||||
RgbaFileOutput output(filename, bitmap.width, bitmap.height);
|
||||
if (output) {
|
||||
byte rgba[4];
|
||||
for (int y = bitmap.height; y--;) {
|
||||
for (const float *p = bitmap(0, y), *end = p+4*bitmap.width; p < end; p += 4) {
|
||||
rgba[0] = pixelFloatToByte(p[0]);
|
||||
rgba[1] = pixelFloatToByte(p[1]);
|
||||
rgba[2] = pixelFloatToByte(p[2]);
|
||||
rgba[3] = pixelFloatToByte(p[3]);
|
||||
output.writePixel(rgba);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "BitmapRef.hpp"
|
||||
|
||||
namespace msdfgen {
|
||||
|
||||
/// Saves the bitmap as a simple RGBA file, which can be decoded trivially.
|
||||
bool saveRgba(const BitmapConstRef<byte, 1> &bitmap, const char *filename);
|
||||
bool saveRgba(const BitmapConstRef<byte, 3> &bitmap, const char *filename);
|
||||
bool saveRgba(const BitmapConstRef<byte, 4> &bitmap, const char *filename);
|
||||
bool saveRgba(const BitmapConstRef<float, 1> &bitmap, const char *filename);
|
||||
bool saveRgba(const BitmapConstRef<float, 3> &bitmap, const char *filename);
|
||||
bool saveRgba(const BitmapConstRef<float, 4> &bitmap, const char *filename);
|
||||
|
||||
}
|
||||
|
|
@ -8,10 +8,12 @@
|
|||
#ifdef MSDFGEN_USE_CPP11
|
||||
#include <cstdint>
|
||||
#else
|
||||
namespace msdfgen {
|
||||
typedef int int32_t;
|
||||
typedef unsigned uint32_t;
|
||||
typedef unsigned short uint16_t;
|
||||
typedef unsigned char uint8_t;
|
||||
}
|
||||
#endif
|
||||
|
||||
namespace msdfgen {
|
||||
|
|
|
|||
38
main.cpp
38
main.cpp
|
|
@ -30,7 +30,7 @@
|
|||
#define DEFAULT_IMAGE_EXTENSION "png"
|
||||
#define SAVE_DEFAULT_IMAGE_FORMAT savePng
|
||||
#else
|
||||
#define DEFAULT_IMAGE_EXTENSION "tif"
|
||||
#define DEFAULT_IMAGE_EXTENSION "tiff"
|
||||
#define SAVE_DEFAULT_IMAGE_FORMAT saveTiff
|
||||
#endif
|
||||
|
||||
|
|
@ -41,6 +41,8 @@ enum Format {
|
|||
PNG,
|
||||
BMP,
|
||||
TIFF,
|
||||
RGBA,
|
||||
FL32,
|
||||
TEXT,
|
||||
TEXT_FLOAT,
|
||||
BINARY,
|
||||
|
|
@ -49,7 +51,7 @@ enum Format {
|
|||
};
|
||||
|
||||
static bool is8bitFormat(Format format) {
|
||||
return format == PNG || format == BMP || format == TEXT || format == BINARY;
|
||||
return format == PNG || format == BMP || format == RGBA || format == TEXT || format == BINARY;
|
||||
}
|
||||
|
||||
static char toupper(char c) {
|
||||
|
|
@ -263,7 +265,9 @@ static const char *writeOutput(const BitmapConstRef<float, N> &bitmap, const cha
|
|||
return "PNG format is not available in core-only version.";
|
||||
#endif
|
||||
else if (cmpExtension(filename, ".bmp")) format = BMP;
|
||||
else if (cmpExtension(filename, ".tif") || cmpExtension(filename, ".tiff")) format = TIFF;
|
||||
else if (cmpExtension(filename, ".tiff") || cmpExtension(filename, ".tif")) format = TIFF;
|
||||
else if (cmpExtension(filename, ".rgba")) format = RGBA;
|
||||
else if (cmpExtension(filename, ".fl32")) format = FL32;
|
||||
else if (cmpExtension(filename, ".txt")) format = TEXT;
|
||||
else if (cmpExtension(filename, ".bin")) format = BINARY;
|
||||
else
|
||||
|
|
@ -275,6 +279,8 @@ static const char *writeOutput(const BitmapConstRef<float, N> &bitmap, const cha
|
|||
#endif
|
||||
case BMP: return saveBmp(bitmap, filename) ? NULL : "Failed to write output BMP image.";
|
||||
case TIFF: return saveTiff(bitmap, filename) ? NULL : "Failed to write output TIFF image.";
|
||||
case RGBA: return saveRgba(bitmap, filename) ? NULL : "Failed to write output RGBA image.";
|
||||
case FL32: return saveFl32(bitmap, filename) ? NULL : "Failed to write output FL32 image.";
|
||||
case TEXT: case TEXT_FLOAT: {
|
||||
FILE *file = fopen(filename, "w");
|
||||
if (!file) return "Failed to write output text file.";
|
||||
|
|
@ -416,12 +422,14 @@ static const char *const helpText =
|
|||
"\tComputes and prints the distance field's estimated fill error to the standard output.\n"
|
||||
" -exportshape <filename.txt>\n"
|
||||
"\tSaves the shape description into a text file that can be edited and loaded using -shapedesc.\n"
|
||||
" -exportsvg <filename.svg>\n"
|
||||
"\tSaves the shape geometry into a simple SVG file.\n"
|
||||
" -fillrule <nonzero / evenodd / positive / negative>\n"
|
||||
"\tSets the fill rule for the scanline pass. Default is nonzero.\n"
|
||||
#if defined(MSDFGEN_EXTENSIONS) && !defined(MSDFGEN_DISABLE_PNG)
|
||||
" -format <png / bmp / tiff / text / textfloat / bin / binfloat / binfloatbe>\n"
|
||||
" -format <png / bmp / tiff / rgba / fl32 / text / textfloat / bin / binfloat / binfloatbe>\n"
|
||||
#else
|
||||
" -format <bmp / tiff / text / textfloat / bin / binfloat / binfloatbe>\n"
|
||||
" -format <bmp / tiff / rgba / fl32 / text / textfloat / bin / binfloat / binfloatbe>\n"
|
||||
#endif
|
||||
"\tSpecifies the output format of the distance field. Otherwise it is chosen based on output file extension.\n"
|
||||
" -guessorder\n"
|
||||
|
|
@ -548,6 +556,7 @@ int main(int argc, const char *const *argv) {
|
|||
const char *input = NULL;
|
||||
const char *output = "output." DEFAULT_IMAGE_EXTENSION;
|
||||
const char *shapeExport = NULL;
|
||||
const char *svgExport = NULL;
|
||||
const char *testRender = NULL;
|
||||
const char *testRenderMulti = NULL;
|
||||
bool outputSpecified = false;
|
||||
|
|
@ -747,7 +756,9 @@ int main(int argc, const char *const *argv) {
|
|||
fputs("PNG format is not available in core-only version.\n", stderr);
|
||||
#endif
|
||||
else if (ARG_IS("bmp")) SET_FORMAT(BMP, "bmp");
|
||||
else if (ARG_IS("tiff") || ARG_IS("tif")) SET_FORMAT(TIFF, "tif");
|
||||
else if (ARG_IS("tiff") || ARG_IS("tif")) SET_FORMAT(TIFF, "tiff");
|
||||
else if (ARG_IS("rgba")) SET_FORMAT(RGBA, "rgba");
|
||||
else if (ARG_IS("fl32")) SET_FORMAT(FL32, "fl32");
|
||||
else if (ARG_IS("text") || ARG_IS("txt")) SET_FORMAT(TEXT, "txt");
|
||||
else if (ARG_IS("textfloat") || ARG_IS("txtfloat")) SET_FORMAT(TEXT_FLOAT, "txt");
|
||||
else if (ARG_IS("bin") || ARG_IS("binary")) SET_FORMAT(BINARY, "bin");
|
||||
|
|
@ -919,6 +930,10 @@ int main(int argc, const char *const *argv) {
|
|||
shapeExport = argv[argPos++];
|
||||
continue;
|
||||
}
|
||||
ARG_CASE("-exportsvg", 1) {
|
||||
svgExport = argv[argPos++];
|
||||
continue;
|
||||
}
|
||||
ARG_CASE("-testrender", 3) {
|
||||
unsigned w, h;
|
||||
testRender = argv[argPos++];
|
||||
|
|
@ -1041,7 +1056,7 @@ int main(int argc, const char *const *argv) {
|
|||
getGlyphIndex(glyphIndex, guard.font, unicode);
|
||||
if (!loadGlyph(shape, guard.font, glyphIndex, fontCoordinateScaling, &glyphAdvance))
|
||||
ABORT("Failed to load glyph from font file.");
|
||||
if (!fontCoordinateScalingSpecified && (!autoFrame || scaleSpecified || rangeMode == RANGE_UNIT || mode == METRICS || printMetrics || shapeExport)) {
|
||||
if (!fontCoordinateScalingSpecified && (!autoFrame || scaleSpecified || rangeMode == RANGE_UNIT || mode == METRICS || printMetrics || shapeExport || svgExport)) {
|
||||
fputs(
|
||||
"Warning: Using legacy font coordinate conversion for compatibility reasons.\n"
|
||||
" The implicit scaling behavior will likely change in a future version resulting in different output.\n"
|
||||
|
|
@ -1104,7 +1119,7 @@ int main(int argc, const char *const *argv) {
|
|||
|
||||
double avgScale = .5*(scale.x+scale.y);
|
||||
Shape::Bounds bounds = { };
|
||||
if (autoFrame || mode == METRICS || printMetrics || orientation == GUESS)
|
||||
if (autoFrame || mode == METRICS || printMetrics || orientation == GUESS || svgExport)
|
||||
bounds = shape.getBounds();
|
||||
|
||||
if (outputDistanceShift) {
|
||||
|
|
@ -1278,13 +1293,16 @@ int main(int argc, const char *const *argv) {
|
|||
|
||||
// Save output
|
||||
if (shapeExport) {
|
||||
FILE *file = fopen(shapeExport, "w");
|
||||
if (file) {
|
||||
if (FILE *file = fopen(shapeExport, "w")) {
|
||||
writeShapeDescription(file, shape);
|
||||
fclose(file);
|
||||
} else
|
||||
fputs("Failed to write shape export file.\n", stderr);
|
||||
}
|
||||
if (svgExport) {
|
||||
if (!saveSvgShape(shape, bounds, svgExport))
|
||||
fputs("Failed to write shape SVG file.\n", stderr);
|
||||
}
|
||||
const char *error = NULL;
|
||||
switch (mode) {
|
||||
case SINGLE:
|
||||
|
|
|
|||
|
|
@ -36,7 +36,10 @@
|
|||
#include "core/sdf-error-estimation.h"
|
||||
#include "core/save-bmp.h"
|
||||
#include "core/save-tiff.h"
|
||||
#include "core/save-rgba.h"
|
||||
#include "core/save-fl32.h"
|
||||
#include "core/shape-description.h"
|
||||
#include "core/export-svg.h"
|
||||
|
||||
namespace msdfgen {
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"$schema": "https://raw.githubusercontent.com/microsoft/vcpkg-tool/master/docs/vcpkg.schema.json",
|
||||
"name": "msdfgen",
|
||||
"version": "1.11.0",
|
||||
"version": "1.12.0",
|
||||
"default-features": [
|
||||
"extensions",
|
||||
"geometry-preprocessing",
|
||||
|
|
|
|||
Loading…
Reference in New Issue