diff --git a/src/gpufont/font.hpp b/src/gpufont/font.hpp index a0bed77..2a6287b 100644 --- a/src/gpufont/font.hpp +++ b/src/gpufont/font.hpp @@ -194,6 +194,36 @@ class Font { float r, g, b, a; }; + // + // p0----------p1 + // | | + // | /\ | + // | / \ | + // | /____\ | + // | / \ | + // | | + // p3----------p2 + // + // also has bufferIndex & uv coords + struct BoundingBox { + glm::vec4 p0; + glm::vec4 p1; + glm::vec4 p2; + glm::vec4 p3; + float u0; + float u1; + float v0; + float v1; + int32_t bufferIndex; + + void multiply(const glm::mat4 & mat){ + p0 = mat * p0; + p1 = mat * p1; + p2 = mat * p2; + p3 = mat * p3; + } + }; + /// A structure to model a given axis of a variable font. struct FontVariationAxisParameters { /// The name of the variation axis. @@ -873,6 +903,148 @@ class Font { } + float getLineHeight(float fontSize_px = 1){ + float out = ((float)face->height * fontSize_px) / (float)face->units_per_EM * worldSize; + return out; + } + + float getAscender(float fontSize_px = 1){ + float out = ((float)face->ascender * fontSize_px) / (float)face->units_per_EM * worldSize; + return out; + } + + float getDescender(float fontSize_px = 1){ + float out = ((float)face->descender * fontSize_px) / (float)face->units_per_EM * worldSize; + return out; + } + + void collectBoundingBoxes(const std::vector & variationText, + BoundingBox & boundingBox, + std::vector & boundingBoxes, + float & advanceY, + const bool vFlip = false, + const float fontSize_px = 42){ + + float advanceX = 0; + + FT_UInt previous = 0; + float minX = FLT_MAX; + float minY = FLT_MAX; + float maxX = -FLT_MAX; + float maxY = -FLT_MAX; + for(const GlyphIdentity & glyphIdentity : variationText){ + if(glyphIdentity.charcode == '\0'){ + break; + } + float letterFontSize_px = fontSize_px; // can be individual + const uint32_t & charcode = glyphIdentity.charcode; + + if(charcode == '\r'){ + continue; + } + + if(charcode == '\n' || charcode == 'n'){ + advanceX = 0; + advanceY += getLineHeight(fontSize_px); + if(!vFlip){ + advanceY *= -1; + } + if(hinting){ + advanceY = std::round(advanceY); + } + continue; + } + + auto glyphIt = glyphs.find(glyphIdentity); + Glyph & glyph = (glyphIt == glyphs.end()) ? glyphs.begin()->second : glyphIt->second; // in case we have no glyph, draw first one? + // NOTE: should we do this? + + if(previous != 0 && glyph.index != 0){ + FT_Vector kerning; + FT_Error error = FT_Get_Kerning(face, previous, glyph.index, kerningMode, &kerning); + if(!error){ + advanceX += ((float)kerning.x * fontSize_px) / emSize * worldSize; + } + } + + // Do not emit quad for empty glyphs (whitespace). + if(glyph.curveCount){ + FT_Pos d = (FT_Pos)(emSize * dilation); + + float u0 = (float)(glyph.bearingX - d) / emSize; + float v0 = (float)(glyph.bearingY - glyph.height - d) / emSize; + float u1 = (float)(glyph.bearingX + glyph.width + d) / emSize; + float v1 = (float)(glyph.bearingY + d) / emSize; + + float x0 = advanceX + u0 * worldSize * letterFontSize_px; + float y0 = v0 * worldSize * letterFontSize_px; + float x1 = advanceX + u1 * worldSize * letterFontSize_px; + float y1 = v1 * worldSize * letterFontSize_px; + + if(vFlip){ + float _v = v0; + v0 = v1; + v1 = _v; + + y0 = -v0 * worldSize * letterFontSize_px; + y1 = -v1 * worldSize * letterFontSize_px; + } + + y0 += advanceY; + y1 += advanceY; + minX = min(x0, min(x1, minX)); + maxX = max(x0, max(x1, maxX)); + minY = min(y0, min(y1, minY)); + maxY = max(y0, max(y1, maxY)); + + boundingBoxes.push_back({ + glm::vec4(x0, y0, 0, 1), + glm::vec4(x1, y0, 0, 1), + glm::vec4(x1, y1, 0, 1), + glm::vec4(x0, y1, 0, 1), + u0, u1, v0, v1, + glyph.bufferIndex + }); + } + + advanceX += ((float)glyph.advance * letterFontSize_px) / emSize * worldSize; + previous = glyph.index; + } + boundingBox.p0 = glm::vec4(minX, minY, 0, 1); + boundingBox.p1 = glm::vec4(maxX, minY, 0, 1); + boundingBox.p2 = glm::vec4(maxX, maxY, 0, 1); + boundingBox.p3 = glm::vec4(minX, maxY, 0, 1); + advanceY += getLineHeight(fontSize_px); + if(!vFlip){ + advanceY *= -1; + } + if(hinting){ + advanceY = std::round(advanceY); + } + } + + void collectVerticesAndIndices(const ofNode & node, + std::vector & boundingBoxes, + std::vector & vertices, + std::vector & indices, + const glm::vec4 & color = glm::vec4(1), + const float fontSize_px = 42){ + + for(BoundingBox & bb : boundingBoxes){ + + bb.multiply(node.getGlobalTransformMatrix()); + + int32_t base = static_cast (vertices.size()); + vertices.push_back(BufferVertex{bb.p0.x, bb.p0.y, bb.u0, bb.v0, bb.bufferIndex, color.r, color.g, color.b, color.a}); + vertices.push_back(BufferVertex{bb.p1.x, bb.p1.y, bb.u1, bb.v0, bb.bufferIndex, color.r, color.g, color.b, color.a}); + vertices.push_back(BufferVertex{bb.p2.x, bb.p2.y, bb.u1, bb.v1, bb.bufferIndex, color.r, color.g, color.b, color.a}); + vertices.push_back(BufferVertex{bb.p3.x, bb.p3.y, bb.u0, bb.v1, bb.bufferIndex, color.r, color.g, color.b, color.a}); + + indices.insert(indices.end(), {base, base + 1, base + 2, base + 2, base + 3, base}); + + } + } + void collectVerticesAndIndices(const ofNode & node, const std::vector & variationText, std::vector & vertices, @@ -975,80 +1147,6 @@ class Font { glBindVertexArray(0); } - struct BoundingBox { - float minX, minY, maxX, maxY; - }; - - BoundingBox measure(float x, float y, const std::string & text){ - //BoundingBox bb; - cerr << "ofxGPUFont::Font::BoundingBox not implemented" << endl; - //bb.minX = +std::numeric_limits ::infinity(); - //bb.minY = +std::numeric_limits ::infinity(); - //bb.maxX = -std::numeric_limits ::infinity(); - //bb.maxY = -std::numeric_limits ::infinity(); - - //float originalX = x; - - //FT_UInt previous = 0; - //for(const char * textIt = text.c_str(); *textIt != '\0';){ - //uint32_t charcode = decodeCharcodes(&textIt); - - //if(charcode == '\r'){ - //continue; - //} - - //if(charcode == '\n'){ - //x = originalX; - //y -= (float)face->height / (float)face->units_per_EM * worldSize; - //if(hinting){ - //y = std::round(y); - //} - //continue; - //} - - //auto glyphIt = glyphs.find(charcode); - //Glyph & glyph = (glyphIt == glyphs.end()) ? glyphs[0] : glyphIt->second; - - //if(previous != 0 && glyph.index != 0){ - //FT_Vector kerning; - //FT_Error error = FT_Get_Kerning(face, previous, glyph.index, kerningMode, &kerning); - //if(!error){ - //x += (float)kerning.x / emSize * worldSize; - //} - //} - - //// Note: Do not apply dilation here, we want to calculate exact bounds. - //float u0 = (float)(glyph.bearingX) / emSize; - //float v0 = (float)(glyph.bearingY - glyph.height) / emSize; - //float u1 = (float)(glyph.bearingX + glyph.width) / emSize; - //float v1 = (float)(glyph.bearingY) / emSize; - - //float x0 = x + u0 * worldSize; - //float y0 = y + v0 * worldSize; - //float x1 = x + u1 * worldSize; - //float y1 = y + v1 * worldSize; - - //if(x0 < bb.minX){ - //bb.minX = x0; - //} - //if(y0 < bb.minY){ - //bb.minY = y0; - //} - //if(x1 > bb.maxX){ - //bb.maxX = x1; - //} - //if(y1 > bb.maxY){ - //bb.maxY = y1; - //} - - //x += (float)glyph.advance / emSize * worldSize; - //previous = glyph.index; - //} - - //return bb; - return BoundingBox(); - } - private: FT_Face face;