add bounding boxes

This commit is contained in:
jrkb 2023-04-18 13:18:29 +02:00
parent 133b74699e
commit c37609f3f7

View file

@ -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 <GlyphIdentity> & variationText,
BoundingBox & boundingBox,
std::vector <BoundingBox> & 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 <BoundingBox> & boundingBoxes,
std::vector <BufferVertex> & vertices,
std::vector <int32_t> & 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 <int32_t>(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 <GlyphIdentity> & variationText,
std::vector <BufferVertex> & 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 <float>::infinity();
//bb.minY = +std::numeric_limits <float>::infinity();
//bb.maxX = -std::numeric_limits <float>::infinity();
//bb.maxY = -std::numeric_limits <float>::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;