diff --git a/src/Atlas.cpp b/src/Atlas.cpp new file mode 100644 index 0000000..f5490de --- /dev/null +++ b/src/Atlas.cpp @@ -0,0 +1,87 @@ +#include "Atlas.h" +#include "BitmapRef.hpp" + +void ofxMsdfgen::Atlas::setup(string _fontPath){ + fontPath = _fontPath; + if(generate()){ + ofLogNotice("ofxMsdfgen::Atlas::setup()") << "generated Atlas"; + }else{ + ofLogError("ofxMsdfgen::Atlas::setup()") << "whoops, could not generate Atlas"; + } +} + +void ofxMsdfgen::Atlas::setup(string _fontPath, + AtlasSettings _settings){ + settings = _settings; + setup(_fontPath); +} + +bool ofxMsdfgen::Atlas::generate(){ + bool success = false; + // Initialize instance of FreeType library + if(msdfgen::FreetypeHandle * ft = msdfgen::initializeFreetype()){ + // Load font file + if(msdfgen::FontHandle * font = msdfgen::loadFont(ft, fontPath.c_str())){ + // Storage for glyph geometry and their coordinates in the atlas + std::vector glyphs; + // FontGeometry is a helper class that loads a set of glyphs from a single font. + // It can also be used to get additional font metrics, kerning information, etc. + msdf_atlas::FontGeometry fontGeometry(&glyphs); + // Load a set of character glyphs: + // The second argument can be ignored unless you mix different font sizes in one atlas. + // In the last argument, you can specify a charset other than ASCII. + // To load specific glyph indices, use loadGlyphs instead. + fontGeometry.loadCharset(font, 1.0, msdf_atlas::Charset::ASCII); + // Apply MSDF edge coloring. See edge-coloring.h for other coloring strategies. + for(msdf_atlas::GlyphGeometry & glyph : glyphs){ + glyph.edgeColoring(&msdfgen::edgeColoringInkTrap, + settings.maxCornerAngle, + 0); + } + // TightAtlasPacker class computes the layout of the atlas. + msdf_atlas::TightAtlasPacker packer; + // Set atlas parameters: + // setDimensions or setDimensionsConstraint to find the best value + packer.setDimensionsConstraint(msdf_atlas::TightAtlasPacker::DimensionsConstraint::SQUARE); + // setScale for a fixed size or setMinimumScale to use the largest that fits + packer.setMinimumScale(settings.minimumScale); + // setPixelRange or setUnitRange + packer.setPixelRange(settings.pixelRange); + packer.setMiterLimit(settings.miterLimit); + // Compute atlas layout - pack glyphs + packer.pack(glyphs.data(), glyphs.size()); + // Get final atlas dimensions + int width = 0, height = 0; + packer.getDimensions(width, height); + // The ImmediateAtlasGenerator class facilitates the generation of the atlas bitmap. + msdf_atlas::ImmediateAtlasGenerator < + float, // pixel type of buffer for individual glyphs depends on generator function + 3, // number of atlas color channels + & msdf_atlas::msdfGenerator, // function to generate bitmaps for individual glyphs + msdf_atlas::BitmapAtlasStorage // class that stores the atlas bitmap + // For example, a custom atlas storage class that stores it in VRAM can be used. + > generator(width, height); + // GeneratorAttributes can be modified to change the generator's default settings. + msdf_atlas::GeneratorAttributes attributes; + generator.setAttributes(attributes); + generator.setThreadCount(settings.threadCount); + // Generate atlas bitmap + generator.generate(glyphs.data(), glyphs.size()); + // The atlas bitmap can now be retrieved via atlasStorage as a BitmapConstRef. + // The glyphs array (or fontGeometry) contains positioning data for typesetting text. + msdfgen::Bitmap bitmap(generator.atlasStorage()); + ofxMsdfgen::toOfImage(bitmap, atlasImage); + + // Cleanup + msdfgen::destroyFont(font); + + success = true; // FIXME: always turns true, why do we have this + } + msdfgen::deinitializeFreetype(ft); + } + return success; +} + +const ofImage & ofxMsdfgen::Atlas::getAtlasImage(){ + return atlasImage; +} diff --git a/src/Atlas.h b/src/Atlas.h new file mode 100644 index 0000000..5c81267 --- /dev/null +++ b/src/Atlas.h @@ -0,0 +1,26 @@ +#pragma once + +#include "ofMain.h" +#include +#include "conversion.h" + +namespace ofxMsdfgen { +struct AtlasSettings { + float maxCornerAngle = 3.0; + float minimumScale = 24.0; + float pixelRange = 2.0; + float miterLimit = 1.0; + int threadCount = 4; +}; +class Atlas { + public: + void setup(string _fontPath); + void setup(string _fontPath, AtlasSettings _settings); + bool generate(); + const ofImage & getAtlasImage(); + AtlasSettings settings; + private: + string fontPath; + ofImage atlasImage; +}; +} diff --git a/src/conversion.cpp b/src/conversion.cpp new file mode 100644 index 0000000..b9f021f --- /dev/null +++ b/src/conversion.cpp @@ -0,0 +1,60 @@ +#include "conversion.h" + +void ofxMsdfgen::toOfImage(msdfgen::Bitmap bitmap, + ofFloatImage & image){ + image.allocate(bitmap.width(), bitmap.height(), OF_IMAGE_COLOR); + memcpy(image.getPixels().getData(), static_cast (bitmap), bitmap.width() * bitmap.height() * 3 * sizeof(float)); + image.update(); // float is slow here +} + +void ofxMsdfgen::toOfImage(const msdfgen::Bitmap bitmap, + ofImage & image){ + image.allocate(bitmap.width(), bitmap.height(), OF_IMAGE_COLOR); + ofPixels & pixels = image.getPixels(); + int w = bitmap.width(); + int h = bitmap.height(); + for(int y = 0; y < h; y++){ + for(int x = 0; x < w; x++){ + const float * rgb = bitmap(x, y); + int index = 3 * (x + y * w); + pixels.getData()[index] = (unsigned char)std::clamp(rgb[0] * 256.0, + 0.0, 255.0); + pixels.getData()[index + 1] = (unsigned char)std::clamp(rgb[1] * 256.0, + 0.0, 255.0); + pixels.getData()[index + 2] = (unsigned char)std::clamp(rgb[2] * 256.0, + 0.0, 255.0); + } + } + + image.update(); +} + +void ofxMsdfgen::toOfImage(const msdfgen::Bitmap bitmap, + ofImage & image){ + image.allocate(bitmap.width(), bitmap.height(), OF_IMAGE_COLOR); + memcpy(image.getPixels().getData(), bitmap.getPixels(), bitmap.width() * bitmap.height() * 3 * sizeof(byte)); + + image.update(); +} + +ofImage ofxMsdfgen::toOfImage(const msdfgen::Bitmap bitmap){ + ofImage image; + image.allocate(bitmap.width(), bitmap.height(), OF_IMAGE_COLOR); + ofPixels & pixels = image.getPixels(); + int w = bitmap.width(); + int h = bitmap.height(); + for(int y = 0; y < h; y++){ + for(int x = 0; x < w; x++){ + const float * rgb = bitmap(x, y); + int index = 3 * (x + y * w); + pixels.getData()[index] = (unsigned char)std::clamp(rgb[0] * 256.0, + 0.0, 255.0); + pixels.getData()[index + 1] = (unsigned char)std::clamp(rgb[1] * 256.0, + 0.0, 255.0); + pixels.getData()[index + 2] = (unsigned char)std::clamp(rgb[2] * 256.0, + 0.0, 255.0); + } + } + image.update(); + return image; +} diff --git a/src/conversion.h b/src/conversion.h new file mode 100644 index 0000000..4710cb0 --- /dev/null +++ b/src/conversion.h @@ -0,0 +1,16 @@ +#pragma once + +#include "ofMain.h" +#include "BitmapAtlasStorage.h" +#include "GlyphGeometry.h" +#include + +namespace ofxMsdfgen { +void toOfImage(msdfgen::Bitmap bitmap, + ofFloatImage & image); +void toOfImage(const msdfgen::Bitmap bitmap, + ofImage & image); +void toOfImage(const msdfgen::Bitmap bitmap, + ofImage & image); +ofImage toOfImage(const msdfgen::Bitmap ); +} diff --git a/src/ofxMsdfgen.cpp b/src/ofxMsdfgen.cpp index 8a06889..a894fce 100644 --- a/src/ofxMsdfgen.cpp +++ b/src/ofxMsdfgen.cpp @@ -1,31 +1,80 @@ #include "ofxMsdfgen.h" +#include "BitmapRef.hpp" +#include "ofGraphicsConstants.h" #include "ofPixels.h" -void ofxMsdfgen::toOfImage(const msdfgen::Bitmap bitmap, - ofFloatImage & image){ - image.allocate(bitmap.width(), bitmap.height(), OF_IMAGE_COLOR); - memcpy(image.getPixels().getData(), bitmap.getPixels(), bitmap.width() * bitmap.height() * 3 * sizeof(float)); - image.update(); // float is slow here -} - -void ofxMsdfgen::toOfImage(const msdfgen::Bitmap bitmap, - ofImage & image){ - image.allocate(bitmap.width(), bitmap.height(), OF_IMAGE_COLOR); - ofPixels & pixels = image.getPixels(); - int w = bitmap.width(); - int h = bitmap.height(); - for(int y = 0; y < h; y++){ - for(int x = 0; x < w; x++){ - const float * rgb = bitmap(x, y); - int index = 3 * (x + y * w); - pixels.getData()[index] = (unsigned char)std::clamp(rgb[0] * 256.0, - 0.0, 255.0); - pixels.getData()[index + 1] = (unsigned char)std::clamp(rgb[1] * 256.0, - 0.0, 255.0); - pixels.getData()[index + 2] = (unsigned char)std::clamp(rgb[2] * 256.0, - 0.0, 255.0); +bool ofxMsdfgen::generateAtlas(const char * fontFilename){ + bool success = false; + // Initialize instance of FreeType library + if(msdfgen::FreetypeHandle * ft = msdfgen::initializeFreetype()){ + // Load font file + if(msdfgen::FontHandle * font = msdfgen::loadFont(ft, fontFilename)){ + // Storage for glyph geometry and their coordinates in the atlas + std::vector glyphs; + // FontGeometry is a helper class that loads a set of glyphs from a single font. + // It can also be used to get additional font metrics, kerning information, etc. + msdf_atlas::FontGeometry fontGeometry(&glyphs); + // Load a set of character glyphs: + // The second argument can be ignored unless you mix different font sizes in one atlas. + // In the last argument, you can specify a charset other than ASCII. + // To load specific glyph indices, use loadGlyphs instead. + fontGeometry.loadCharset(font, 1.0, msdf_atlas::Charset::ASCII); + // Apply MSDF edge coloring. See edge-coloring.h for other coloring strategies. + const double maxCornerAngle = 3.0; + for(msdf_atlas::GlyphGeometry & glyph : glyphs){ + glyph.edgeColoring(&msdfgen::edgeColoringInkTrap, maxCornerAngle, 0); + } + // TightAtlasPacker class computes the layout of the atlas. + msdf_atlas::TightAtlasPacker packer; + // Set atlas parameters: + // setDimensions or setDimensionsConstraint to find the best value + packer.setDimensionsConstraint(msdf_atlas::TightAtlasPacker::DimensionsConstraint::SQUARE); + // setScale for a fixed size or setMinimumScale to use the largest that fits + packer.setMinimumScale(24.0); + // setPixelRange or setUnitRange + packer.setPixelRange(2.0); + packer.setMiterLimit(1.0); + // Compute atlas layout - pack glyphs + packer.pack(glyphs.data(), glyphs.size()); + // Get final atlas dimensions + int width = 0, height = 0; + packer.getDimensions(width, height); + // The ImmediateAtlasGenerator class facilitates the generation of the atlas bitmap. + msdf_atlas::ImmediateAtlasGenerator < + float, // pixel type of buffer for individual glyphs depends on generator function + 3, // number of atlas color channels + & msdf_atlas::msdfGenerator, // function to generate bitmaps for individual glyphs + msdf_atlas::BitmapAtlasStorage // class that stores the atlas bitmap + // For example, a custom atlas storage class that stores it in VRAM can be used. + > generator(width, height); + // GeneratorAttributes can be modified to change the generator's default settings. + msdf_atlas::GeneratorAttributes attributes; + generator.setAttributes(attributes); + generator.setThreadCount(12); + // Generate atlas bitmap + generator.generate(glyphs.data(), glyphs.size()); + // The atlas bitmap can now be retrieved via atlasStorage as a BitmapConstRef. + // The glyphs array (or fontGeometry) contains positioning data for typesetting text. + success = submitAtlasBitmapAndLayout((msdfgen::BitmapConstRef )generator.atlasStorage(), + glyphs, + width, + height); + // Cleanup + msdfgen::destroyFont(font); } + msdfgen::deinitializeFreetype(ft); } - - image.update(); + return success; +} + +bool ofxMsdfgen::submitAtlasBitmapAndLayout(const msdfgen::BitmapConstRef & storage, + const std::vector & glyphs, + const int & width, + const int & height){ + const msdfgen::Bitmap bitmap(storage); + ofImage image; + toOfImage(bitmap, image); + image.save("atlas.png"); + + return true; } diff --git a/src/ofxMsdfgen.h b/src/ofxMsdfgen.h index c1bba67..fcb9fab 100644 --- a/src/ofxMsdfgen.h +++ b/src/ofxMsdfgen.h @@ -1,11 +1,19 @@ #pragma once +#include "BitmapAtlasStorage.h" +#include "GlyphGeometry.h" #include "ofMain.h" #include +#include "Atlas.h" +#include "conversion.h" namespace ofxMsdfgen { -void toOfImage(const msdfgen::Bitmap bitmap, - ofFloatImage & image); -void toOfImage(const msdfgen::Bitmap bitmap, - ofImage & image); +bool generateAtlas(const char * fontFilename); + +// doesn't really do anything yet +// just saves the atlas as a png +bool submitAtlasBitmapAndLayout(const msdfgen::BitmapConstRef & storage, + const std::vector & glyphs, + const int & width, + const int & height); }