add text rendering code

This commit is contained in:
Konstantin Käfer
2014-03-21 17:36:29 +01:00
parent a7b9b96d4d
commit ea6b24d02d
13 changed files with 241 additions and 35 deletions

View File

@@ -34,10 +34,12 @@ public:
void removeGlyphs(uint64_t tile_id);
void bind();
private:
std::mutex mtx;
public:
const uint16_t width = 0;
const uint16_t height = 0;
private:
std::mutex mtx;
BinPack<uint16_t> bin;
std::map<std::string, std::map<uint32_t, GlyphValue>> index;
char *const data = nullptr;

View File

@@ -78,8 +78,8 @@ private:
Transform transform;
Texturepool texturepool;
Style style;
Painter painter;
GlyphAtlas glyphAtlas;
Painter painter;
int32_t min_zoom;
int32_t max_zoom;

View File

@@ -0,0 +1,29 @@
#ifndef LLMR_RENDERER_FRAME_HISTORY
#define LLMR_RENDERER_FRAME_HISTORY
#include <deque>
#include <cassert>
#include <cmath>
#include <llmr/platform/platform.hpp>
namespace llmr {
struct FrameSnapshot {
explicit inline FrameSnapshot(float time, float z) : time(time), z(z) {}
float time;
float z;
};
class FrameHistory {
public:
// Record frame history that will be used to calculate fading params
void record(float zoom);
public:
std::deque<FrameSnapshot> history;
};
}
#endif

View File

@@ -6,6 +6,7 @@
#include <llmr/geometry/vertex_buffer.hpp>
#include <llmr/util/mat4.hpp>
#include <llmr/util/noncopyable.hpp>
#include <llmr/renderer/frame_history.hpp>
#include <llmr/shader/plain_shader.hpp>
#include <llmr/shader/outline_shader.hpp>
@@ -30,7 +31,7 @@ class TextBucket;
class Painter : private util::noncopyable {
public:
Painter(Transform& transform, Settings& settings, Style& style);
Painter(Transform& transform, Settings& settings, Style& style, GlyphAtlas& glyphAtlas);
void setup();
void clear();
@@ -68,8 +69,9 @@ private:
Transform& transform;
Settings& settings;
Style& style;
GlyphAtlas& glyphAtlas;
FrameHistory frameHistory;
uint32_t gl_program = 0;
float gl_lineWidth = 0;

View File

@@ -15,15 +15,15 @@ public:
void setColor(const std::array<float, 4> &color);
void setBuffer(float buffer);
void setGamma(float gamma);
void setExmatrix(const std::array<float, 16> &exmatrix);
void setExtrudeMatrix(const std::array<float, 16> &exmatrix);
void setAngle(float angle);
void setZoom(float zoom);
void setFlip(float flip);
void setFadedist(float fadedist);
void setMinfadezoom(float minfadezoom);
void setMaxfadezoom(float maxfadezoom);
void setFadezoom(float fadezoom);
void setTexsize(const std::array<float, 2> &texsize);
void setFadeDist(float fadedist);
void setMinFadeZoom(float minfadezoom);
void setMaxFadeZoom(float maxfadezoom);
void setFadeZoom(float fadezoom);
void setTextureSize(const std::array<float, 2> &texsize);
private:
int32_t a_pos = -1;

View File

@@ -113,11 +113,19 @@ struct FillProperties {
};
struct TextClass {
Color color = {{ 1, 1, 1, 1 }};
FunctionProperty<bool> hidden;
Color color = {{ 0, 0, 0, 1 }};
Color halo = {{ 1, 1, 1, 0.75 }};
FunctionProperty<float> haloRadius = 0.25f;
FunctionProperty<float> size = 12.0f;
};
struct TextProperties {
Color color = {{ 1, 1, 1, 1 }};
bool hidden = false;
Color color = {{ 0, 0, 0, 1 }};
Color halo = {{ 1, 1, 1, 0.75 }};
float haloRadius = 0.25f;
float size = 12.0f;
};
struct BackgroundClass {

View File

@@ -15,8 +15,8 @@ Map::Map(Settings& settings)
transform(),
texturepool(),
style(),
painter(transform, settings, style),
glyphAtlas(1024, 1024),
painter(transform, settings, style, glyphAtlas),
min_zoom(0),
max_zoom((use_raster ? kTileRasterMaxZoom : kTileVectorMaxZoom)) {
}
@@ -443,8 +443,6 @@ bool Map::render() {
transform.updateAnimations();
}
glyphAtlas.bind();
if (*style.sprite->raster && !style.sprite->raster->textured) {
style.sprite->raster->setTexturepool(&texturepool);
}

View File

@@ -0,0 +1,16 @@
#include <llmr/renderer/frame_history.hpp>
using namespace llmr;
// Record frame history that will be used to calculate fading params
void FrameHistory::record(float zoom) {
// first frame ever
if (!history.size()) {
history.emplace_back(FrameSnapshot{0.0f, zoom});
history.emplace_back(FrameSnapshot{0.0f, zoom});
}
if (history.size() == 2 || history.back().z != zoom) {
history.emplace_back(FrameSnapshot{static_cast<float>(platform::time() * 1000), zoom});
}
}

View File

@@ -22,10 +22,11 @@ using namespace llmr;
#define BUFFER_OFFSET(i) ((char *)nullptr + (i))
Painter::Painter(Transform& transform, Settings& settings, Style& style)
Painter::Painter(Transform& transform, Settings& settings, Style& style, GlyphAtlas& glyphAtlas)
: transform(transform),
settings(settings),
style(style) {
style(style),
glyphAtlas(glyphAtlas) {
}
void Painter::setup() {
@@ -152,6 +153,8 @@ void Painter::render(const Tile& tile) {
return;
}
frameHistory.record(transform.getZoom());
matrix = tile.matrix;
glStencilFunc(GL_EQUAL, tile.clip_id, 0xFF);
@@ -477,9 +480,116 @@ void Painter::renderPoint(PointBucket& bucket, const std::string& layer_name, co
}
void Painter::renderText(TextBucket& bucket, const std::string& layer_name, const Tile::ID& id) {
// noop
// Abort early.
if (pass == Opaque) return;
if (bucket.empty()) return;
const TextProperties& properties = style.computed.texts[layer_name];
if (properties.hidden) return;
mat4 exMatrix;
matrix::copy(exMatrix, projMatrix);
if (bucket.geom_desc.path == TextPathType::Curve) {
matrix::rotate_z(exMatrix, exMatrix, transform.getAngle());
}
// var rotate = layerStyle.rotate || 0;
float rotate = 0.0f;
if (rotate != 0.0f) {
matrix::rotate_z(exMatrix, exMatrix, rotate);
}
// If layerStyle.size > bucket.info.fontSize then labels may collide
float fontSize = fmin(properties.size, bucket.geom_desc.font_size);
matrix::scale(exMatrix, exMatrix, fontSize / 24.0f, fontSize / 24.0f, 1.0f);
useProgram(textShader->program);
textShader->setMatrix(matrix);
textShader->setExtrudeMatrix(exMatrix);
glyphAtlas.bind();
textShader->setTextureSize({{ static_cast<float>(glyphAtlas.width), static_cast<float>(glyphAtlas.height) }});
// bucket.geometry.glyphVertex.bind(gl);
// var ubyte = gl.UNSIGNED_BYTE;
// gl.vertexAttribPointer(shader.a_pos, 2, gl.SHORT, false, 16, 0);
// gl.vertexAttribPointer(shader.a_offset, 2, gl.SHORT, false, 16, 4);
// gl.vertexAttribPointer(shader.a_tex, 2, ubyte, false, 16, 8);
// gl.vertexAttribPointer(shader.a_labelminzoom, 1, ubyte, false, 16, 10);
// gl.vertexAttribPointer(shader.a_minzoom, 1, ubyte, false, 16, 11);
// gl.vertexAttribPointer(shader.a_maxzoom, 1, ubyte, false, 16, 12);
// gl.vertexAttribPointer(shader.a_angle, 1, ubyte, false, 16, 13);
// gl.vertexAttribPointer(shader.a_rangeend, 1, ubyte, false, 16, 14);
// gl.vertexAttribPointer(shader.a_rangestart, 1, ubyte, false, 16, 15);
textShader->setGamma(2.5f / fontSize / transform.pixelRatio);
// Convert the -pi..pi to an int8 range.
float angle = round((transform.getAngle() + rotate) / M_PI * 128);
// adjust min/max zooms for variable font sies
float zoomAdjust = log(fontSize / bucket.geom_desc.font_size) / log(2);
textShader->setAngle((int32_t)(angle + 256) % 256);
textShader->setFlip(bucket.geom_desc.path == TextPathType::Curve ? 1 : 0);
textShader->setZoom((transform.getZoom() - zoomAdjust) * 10); // current zoom level
// Label fading
const float duration = 300.0f;
const float currentTime = platform::time() * 1000;
std::deque<FrameSnapshot> &history = frameHistory.history;
// Remove frames until only one is outside the duration, or until there are only three
while (history.size() > 3 && history[1].time + duration < currentTime) {
history.pop_front();
}
if (history[1].time + duration < currentTime) {
history[0].z = history[1].z;
}
size_t frameLen = history.size();
assert("there should never be less than three frames in the history" && frameLen >= 3);
// Find the range of zoom levels we want to fade between
float startingZ = history.front().z;
const FrameSnapshot lastFrame = history.back();
float endingZ = lastFrame.z;
float lowZ = fmin(startingZ, endingZ);
float highZ = fmax(startingZ, endingZ);
// Calculate the speed of zooming, and how far it would zoom in terms of zoom levels in one duration
float zoomDiff = endingZ - history[1].z,
timeDiff = lastFrame.time - history[1].time;
if (timeDiff > duration) timeDiff = 1;
float fadedist = zoomDiff / (timeDiff / duration);
if (isnan(fadedist)) fprintf(stderr, "fadedist should never be NaN");
// At end of a zoom when the zoom stops changing continue pretending to zoom at that speed
// bump is how much farther it would have been if it had continued zooming at the same rate
float bump = (currentTime - lastFrame.time) / duration * fadedist;
textShader->setFadeDist(fadedist * 10);
textShader->setMinFadeZoom(floor(lowZ * 10));
textShader->setMaxFadeZoom(floor(highZ * 10));
textShader->setFadeZoom((transform.getZoom() + bump) * 10);
// We're drawing in the translucent pass which is bottom-to-top, so we need
// to draw the halo first.
if (properties.halo[3] > 0.0f) {
textShader->setColor(properties.halo);
textShader->setBuffer(properties.haloRadius);
bucket.drawGlyphs(*textShader);
}
// Then, we draw the text over the halo
textShader->setColor(properties.color);
textShader->setBuffer((256.0f - 64.0f) / 256.0f);
bucket.drawGlyphs(*textShader);
}
void Painter::renderDebug(const TileData::Ptr& tile_data) {

View File

@@ -150,5 +150,13 @@ void TextBucket::render(Painter &painter, const std::string &layer_name,
bool TextBucket::empty() const { return triangleGroups.empty(); }
void TextBucket::drawGlyphs(TextShader &shader) {
// noop
char *vertex_index = BUFFER_OFFSET(vertex_start * vertexBuffer->itemSize);
char *elements_index =
BUFFER_OFFSET(triangle_elements_start * triangleElementsBuffer->itemSize);
for (triangle_group_type &group : triangleGroups) {
group.array.bind(shader, *vertexBuffer, *triangleElementsBuffer, vertex_index);
glDrawElements(GL_TRIANGLES, group.elements_length * 3, GL_UNSIGNED_SHORT, elements_index);
vertex_index += group.vertex_length * vertexBuffer->itemSize;
elements_index += group.elements_length * triangleElementsBuffer->itemSize;
}
}

View File

@@ -65,6 +65,8 @@ TextShader::TextShader()
}
void TextShader::bind(char *offset) {
fprintf(stderr, "bind textshader\n");
glEnableVertexAttribArray(a_pos);
glVertexAttribPointer(a_pos, 2, GL_SHORT, false, 16, offset + 0);
@@ -123,7 +125,7 @@ void TextShader::setGamma(float new_gamma) {
}
}
void TextShader::setExmatrix(const std::array<float, 16> &new_exmatrix) {
void TextShader::setExtrudeMatrix(const std::array<float, 16> &new_exmatrix) {
if (exmatrix != new_exmatrix) {
glUniformMatrix4fv(u_exmatrix, 1, GL_FALSE, new_exmatrix.data());
exmatrix = new_exmatrix;
@@ -151,35 +153,35 @@ void TextShader::setFlip(float new_flip) {
}
}
void TextShader::setFadedist(float new_fadedist) {
void TextShader::setFadeDist(float new_fadedist) {
if (fadedist != new_fadedist) {
glUniform1f(u_fadedist, new_fadedist);
fadedist = new_fadedist;
}
}
void TextShader::setMinfadezoom(float new_minfadezoom) {
void TextShader::setMinFadeZoom(float new_minfadezoom) {
if (minfadezoom != new_minfadezoom) {
glUniform1f(u_minfadezoom, new_minfadezoom);
minfadezoom = new_minfadezoom;
}
}
void TextShader::setMaxfadezoom(float new_maxfadezoom) {
void TextShader::setMaxFadeZoom(float new_maxfadezoom) {
if (maxfadezoom != new_maxfadezoom) {
glUniform1f(u_maxfadezoom, new_maxfadezoom);
maxfadezoom = new_maxfadezoom;
}
}
void TextShader::setFadezoom(float new_fadezoom) {
void TextShader::setFadeZoom(float new_fadezoom) {
if (fadezoom != new_fadezoom) {
glUniform1f(u_fadezoom, new_fadezoom);
fadezoom = new_fadezoom;
}
}
void TextShader::setTexsize(const std::array<float, 2> &new_texsize) {
void TextShader::setTextureSize(const std::array<float, 2> &new_texsize) {
if (texsize != new_texsize) {
glUniform2fv(u_texsize, 1, new_texsize.data());
texsize = new_texsize;

View File

@@ -83,7 +83,11 @@ void Style::cascade(float z) {
// TODO: This should be restricted to point styles that have actual
// values so as to not override with default values.
llmr::TextProperties& text = computed.texts[layer_name];
text.hidden = layer.hidden(z);
text.color = layer.color;
text.size = layer.size(z);
text.halo = layer.halo;
text.haloRadius = layer.haloRadius(z);
}
// Cascade background

View File

@@ -268,24 +268,35 @@ std::string StyleParser::parseString(JSVal value) {
}
Color StyleParser::parseColor(JSVal value) {
if (!value.IsString()) {
if (value.IsArray()) {
// [ r, g, b, a] array
if (value.Size() != 4) {
throw Style::exception("color array must have four elements");
}
JSVal r = value[(rapidjson::SizeType)0], g = value[1], b = value[2], a = value[3];
if (!r.IsNumber() || !g.IsNumber() || !b.IsNumber() || !a.IsNumber()) {
throw Style::exception("color values must be numbers");
}
return {{static_cast<float>(r.GetDouble() / 255),
static_cast<float>(g.GetDouble() / 255),
static_cast<float>(b.GetDouble() / 255),
static_cast<float>(a.GetDouble())}};
} else if (!value.IsString()) {
throw Style::exception("color value must be a string");
}
const std::string str { value.GetString(), value.GetStringLength() };
const std::string str{value.GetString(), value.GetStringLength()};
auto it = constants.find(str);
if (it != constants.end()) {
return parseColor(*it->second);
} else {
CSSColorParser::Color css_color = CSSColorParser::parse(str);
return {{
(float)css_color.r / 255,
(float)css_color.g / 255,
(float)css_color.b / 255,
css_color.a
}
};
return {{(float)css_color.r / 255, (float)css_color.g / 255,
(float)css_color.b / 255, css_color.a}};
}
}
@@ -477,10 +488,26 @@ PointClass StyleParser::parsePointClass(JSVal value) {
TextClass StyleParser::parseTextClass(JSVal value) {
TextClass klass;
if (value.HasMember("hidden")) {
klass.hidden = parseBoolFunction(value["hidden"]);
}
if (value.HasMember("color")) {
klass.color = parseColor(value["color"]);
}
if (value.HasMember("stroke")) {
klass.halo = parseColor(value["stroke"]);
}
if (value.HasMember("strokeWidth")) {
klass.haloRadius = parseFloatFunction(value["strokeWidth"]);
}
if (value.HasMember("size")) {
klass.size = parseFloatFunction(value["size"]);
}
return klass;
}