#include "TextComponent.h" #include #include #include extern TextRasterizerCache *rasterizerCache; TextComponent::TextComponent(const std::string &rawText, const int rawX, const int rawY, const unsigned int size, const bool bolded, const unsigned int hexColor, const int passedWindowWidth, const int passedWindowHeight) { windowWidth = passedWindowWidth; windowHeight = passedWindowHeight; //const std::clock_t begin = clock(); text = rawText; //x = rawX; //y = rawY; x = 0; y = 0; fontSize = size; bold = bolded; color = hexColor; // html entity decode sanitize(text); // load font, rasterize, save vertices //rasterize(x, y, windowWidth, windowHeight); // send to video card glGenBuffers(1, &elementBufferObject); //for (unsigned int i = 0; i < glyphVertices.size(); i++) { //int i = 0; //const Glyph &glyph = glyphs[i]; //const std::unique_ptr &glyphVertice = glyphVertices[i]; vertexArrayObjects.push_back(0); vertexBufferObjects.push_back(0); glGenVertexArrays(1, &vertexArrayObjects.back()); glGenBuffers(1, &vertexBufferObjects.back()); glBindVertexArray(vertexArrayObjects.back()); // the data array glBindBuffer(GL_ARRAY_BUFFER, vertexBufferObjects.back()); std::unique_ptr vertices = std::make_unique(36); // upload garbage for now glBufferData(GL_ARRAY_BUFFER, ((3 + 4 + 2) * 4) * sizeof(float), vertices.get(), GL_STATIC_DRAW); //glBufferData(GL_ARRAY_BUFFER, ((3 + 4 + 2) * 4) * sizeof(float), glyphVertice.get(), GL_STATIC_DRAW); // indexes of the array glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, elementBufferObject); glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW); glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, (3 + 4 + 2) * sizeof(float), nullptr); glEnableVertexAttribArray(0); glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, (3 + 4 + 2) * sizeof(float), reinterpret_cast(3 * sizeof(float))); glEnableVertexAttribArray(1); glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, (3 + 4 + 2) * sizeof(float), reinterpret_cast(7 * sizeof(float))); glEnableVertexAttribArray(2); /* textures.push_back(0); glGenTextures(1, &textures.back()); glBindTexture(GL_TEXTURE_2D, textures.back()); */ // upload texture //glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, glyph.textureWidth, glyph.textureHeight, 0, GL_RED, GL_UNSIGNED_BYTE, glyph.textureData.get()); //glGenerateMipmap(GL_TEXTURE_2D); //} //const std::clock_t end = clock(); //std::cout << "buffering text [" << text << "] in: " << std::fixed << ((static_cast(end - begin)) / CLOCKS_PER_SEC) << std::scientific << " seconds" << std::endl; } TextComponent::~TextComponent() { glDeleteBuffers(1, &elementBufferObject); for (unsigned int i = 0; i < vertexArrayObjects.size(); i++) { glDeleteVertexArrays(1, &vertexArrayObjects[i]); glDeleteBuffers(1, &vertexBufferObjects[i]); if (textures.size()>i) { glDeleteTextures(1, &textures[i]); } } } #define posMac(p) (p * (3 + 4 + 2)) // 3 positions + 4 color channels + 2 S&T (texture mapping) inline void setVerticesColor(std::unique_ptr &vertices, int p, unsigned int color) { vertices[static_cast(posMac(p) + 2)] = 0.0f; vertices[static_cast(posMac(p) + 3)] = (static_cast((color >> 24) & 0xFF)) / 255; vertices[static_cast(posMac(p) + 4)] = (static_cast((color >> 16) & 0xFF)) / 255; vertices[static_cast(posMac(p) + 5)] = (static_cast((color >> 8) & 0xFF)) / 255; vertices[static_cast(posMac(p) + 6)] = (static_cast((color >> 0) & 0xFF)) / 255; } void TextComponent::rasterize(const int rawX, const int rawY) { //const std::clock_t begin = clock(); const std::shared_ptr textRasterizer=rasterizerCache->loadFont(fontSize, bold); //unsigned int glyphCount; // we need to know how much width we have between x and windowWidth // and preferrable where the inline starts // my inline start is parent->children adding width from start to me // actually the run of all inline before me, starting at parent x as a minimum //std::cout << "TextComponent::rasterize pre-height: " << (int)height << std::endl; int wrapToX = 0; // windowWidth - rawX; //bool wrapped; //glyphs = textRasterizer->rasterize(text, rawX, windowWidth, wrapToX, width, height, glyphCount, endingX, endingY, wrapped); rasterizationRequest request; request.text = text; request.startX = rawX; // - x if (!boundToPage) { // startX needs to be relative to the parent x. Why? request.startX -= x; } //std::cout << "TextComponent::rasterize - [" << text << "] request.startX: " << request.startX << " x: " << x << " rawX: " << rawX << std::endl; request.availableWidth = availableWidth; request.sourceStartX = rasterStartX; request.sourceStartY = rasterStartY; if (this->win) { request.maxTextureSize = this->win->maxTextureSize & INT_MAX; } // else called from input cstr, no win yet request.noWrap = noWrap; //std::cout << "rasterizing [" << text << "] @" << rawX << " availableWidth: " << availableWidth << " sourceStartX: " << rasterStartX << " noWrap: " << noWrap << std::endl; std::shared_ptr response = textRasterizer->rasterize(request); if (response.get() == nullptr) { // window could be minimized //std::cout << "TextComponent::rasterize - got nullptr from rasterizer" << std::endl; return; } width = response->width; height = response->height; endingX = response->endingX; endingY = response->endingY; // << " wrapTo: " << wrapToX //std::cout << "TextComponent::rasterize done [" << text << "] - start x: " << rawX << " width: " << (int)width << " wrapWidth: " << windowWidth << std::endl; //std::cout << "TextComponent::rasterize post-height: " << (int)height << std::endl; //if (glyphs == nullptr) { // return; //} // did we wrap int startX = rawX; // if we didn't we have a nice little texture starting at rawX potentially up to windowWidth // if we did wrap, then we start at wrapToX (0) and we're a big square texture if (response->wrapped) { //std::cout << "[" << text << "] it's wrapped starting at " << wrapToX << std::endl; startX = wrapToX; } //std::cout << "startX: " << startX << std::endl; glyphVertices.clear(); //for (unsigned int i = 0; i < glyphCount; i++) { //for(std::vector::iterator it=glyphs->begin(); it!=glyphs->end(); ++it) { //const Glyph &glyph = glyphs[i]; //Glyph &glyph=*it; float vx0 = startX; float vy0 = response->y0 + rawY; //std::cout << "glyph x from: " << (int)glyph.x0 << " to " << (int)glyph.x1 << std::endl; float vx1 = startX + (response->x1 - response->x0); float vy1 = response->y1 + rawY; //std::cout << "TextComponent::rasterize - placing at " << (int)vx0 << "," << (int)vy0 << " to " << (int)vx1 << "," << (int)vy1 << std::endl; // convert our local x,y,w,h into actual ogl coords // map vx0 between -1 and 1 //vx0 = ((vx0 / windowWidth) * 2) - 1; //vy0 = ((vy0 / windowHeight) * 2) - 1; pointToViewport(vx0, vy0); pointToViewport(vx1, vy1); //std::cout << "TextComponent::rasterize - placing at GL" << vx0 << "," << vy0 << " to GL" << vx1 << "," << vy1 << std::endl; std::unique_ptr vertices = std::make_unique(36); vertices[posMac(0) + 0] = vx0; vertices[posMac(0) + 1] = vy0; setVerticesColor(vertices, 0, color); vertices[posMac(0) + 7] = response->s0; vertices[posMac(0) + 8] = response->t0; vertices[posMac(1) + 0] = vx0; vertices[posMac(1) + 1] = vy1; setVerticesColor(vertices, 1, color); vertices[posMac(1) + 7] = response->s0; vertices[posMac(1) + 8] = response->t1; vertices[posMac(2) + 0] = vx1; vertices[posMac(2) + 1] = vy1; setVerticesColor(vertices, 2, color); vertices[posMac(2) + 7] = response->s1; vertices[posMac(2) + 8] = response->t1; vertices[posMac(3) + 0] = vx1; vertices[posMac(3) + 1] = vy0; setVerticesColor(vertices, 3, color); vertices[posMac(3) + 7] = response->s1; vertices[posMac(3) + 8] = response->t0; glyphVertices.push_back(std::move(vertices)); //std::cout << "TextComponent::rasterize - glyphVertices count: " << glyphVertices.size() << std::endl; textureWidth = static_cast(response->textureWidth); textureHeight = static_cast(response->textureHeight); textureData = std::move(response->textureData); //} //const std::clock_t end = clock(); //std::cout << "rasterize text [" << text << "] in: " << std::fixed << ((static_cast(end - begin)) / CLOCKS_PER_SEC) << std::scientific << " seconds" << std::endl; verticesDirty = true; response.reset(); } void TextComponent::render() { //std::cout << "TextComponent::render [" << text << "]" << std::endl; GLenum glErr=glGetError(); if(glErr != GL_NO_ERROR) { std::cout << "TextComponent::render - start not ok: " << glErr << std::endl; } if (verticesDirty) { // only called on resize //const std::clock_t begin = clock(); for (unsigned int i = 0; i < vertexBufferObjects.size(); i++) { glBindBuffer(GL_ARRAY_BUFFER, vertexBufferObjects[i]); glBufferData(GL_ARRAY_BUFFER, ((3 + 4 + 2) * 4) * sizeof(float), glyphVertices[i].get(), GL_STATIC_DRAW); } verticesDirty = false; glErr=glGetError(); if(glErr != GL_NO_ERROR) { std::cout << "TextComponent::render - verticesDirty, not ok: " << glErr << std::endl; } //const std::clock_t end = clock(); //std::cout << "undirty TextComponent render [" << text << "] in: " << std::fixed << ((static_cast(end - begin)) / CLOCKS_PER_SEC) << std::scientific << " seconds" << std::endl; } // have to have a texture to draw if (textures.size()) { //std::cout << "has textures" << std::endl; for (unsigned long i = vertexArrayObjects.size(); i > 0; i--) { glBindVertexArray(vertexArrayObjects[i - 1]); // load vertices glBindTexture(GL_TEXTURE_2D, textures[i - 1]); // load texture glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, nullptr); // draw primitives using vertices and texture } glErr=glGetError(); if(glErr != GL_NO_ERROR) { std::cout << "TextComponent::render - texture render, not ok: " << glErr << std::endl; } } else { std::cout << "TextComponent::render - no textures" << std::endl; } } // compatible adapter void TextComponent::resize(const int passedWindowWidth, const int passedWindowHeight) { resize(passedWindowWidth, passedWindowHeight, passedWindowWidth); } // more detailed control void TextComponent::resize(const int passedWindowWidth, const int passedWindowHeight, const int passedAvailableWidth) { GLenum glErr=glGetError(); if(glErr != GL_NO_ERROR) { std::cout << "TextComponent::resize - start not ok: " << glErr << std::endl; } windowWidth = passedWindowWidth; windowHeight = passedWindowHeight; availableWidth = passedAvailableWidth; //std::cout << "TextComponent::resize" << std::endl; //std::cout << "TextComponent::resize - rasterizing at " << (int)x << "x" << (int)y << " size: " << (int)width << "x" << (int)height << std::endl; // update the vertices and any wrapping rasterize(x, y); //std::cout << "TextComponent::resize - rasterized at " << (int)x << "x" << (int)y << " size: " << (int)width << "x" << (int)height << std::endl; // make sure we have glyphs if (!glyphVertices.size()) { std::cout << "TextComponent::resize - no glyphs" << std::endl; return; } // reupload NEW texture to video card /* const Glyph &glyph = glyphs[0]; if (!glyphs) { std::cout << "TextComponent::resize - glyph points no where" << std::endl; return; } */ if (!textures.size()) { textures.push_back(0); glGenTextures(1, &textures.back()); glErr=glGetError(); if(glErr != GL_NO_ERROR) { std::cout << "TextComponent::resize - glGenTextures, not ok: " << glErr << std::endl; } } glBindTexture(GL_TEXTURE_2D, textures.back()); // select texture glErr=glGetError(); if(glErr != GL_NO_ERROR) { std::cout << "TextComponent::resize - glBindTexture, not ok: " << glErr << std::endl; } glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, textureWidth, textureHeight, 0, GL_RED, GL_UNSIGNED_BYTE, textureData.get()); // update texture glErr=glGetError(); if(glErr != GL_NO_ERROR) { std::cout << "TextComponent::resize - glTexImage2D, not ok: " << glErr << std::endl; } glGenerateMipmap(GL_TEXTURE_2D); glErr=glGetError(); if(glErr != GL_NO_ERROR) { std::cout << "TextComponent::render - glGenerateMipmap, not ok: " << glErr << std::endl; } textureData.reset(); // it's uploaded, we're done } void TextComponent::sanitize(std::string &str) { size_t found = 0; // position while ((found = str.find("&", found)) != std::string::npos) { if (str.substr(found, 4) == ">") { str.replace(found, 4, ">"); } found++; } found = 0; while ((found = str.find("\n", found)) != std::string::npos) { str.replace(found, 1, ""); found++; } } std::unique_ptr> TextComponent::size() const { const std::shared_ptr textRasterizer=rasterizerCache->loadFont(fontSize, bold); rasterizationRequest request; request.text = text; // startX needs to be relative to the parent x request.startX = x; request.availableWidth = windowWidth; request.sourceStartX = rasterStartX; request.sourceStartY = rasterStartY; request.noWrap = noWrap; return textRasterizer->size(request); } void TextComponent::updateHighlight() { if (this->textSelected) { color = 0x0000FFFF; } else { color = 0x000000FF; } // update the vertices, colors and any wrapping rasterize(x, y); }