[core] Custom layers
This commit is contained in:
@@ -27,6 +27,7 @@ class Transform;
|
||||
class PointAnnotation;
|
||||
class ShapeAnnotation;
|
||||
struct CameraOptions;
|
||||
class StyleLayer;
|
||||
|
||||
namespace util {
|
||||
template <class T> class Thread;
|
||||
@@ -162,6 +163,10 @@ public:
|
||||
AnnotationIDs getPointAnnotationsInBounds(const LatLngBounds&);
|
||||
LatLngBounds getBoundsForAnnotations(const AnnotationIDs&);
|
||||
|
||||
// Style API
|
||||
void addLayer(std::unique_ptr<StyleLayer>);
|
||||
void addLayer(std::unique_ptr<StyleLayer>, const std::string& before);
|
||||
|
||||
// Memory
|
||||
void setSourceTileCacheSize(size_t);
|
||||
void onLowMemory();
|
||||
|
||||
19
src/mbgl/layer/custom_layer.cpp
Normal file
19
src/mbgl/layer/custom_layer.cpp
Normal file
@@ -0,0 +1,19 @@
|
||||
#include <mbgl/layer/custom_layer.hpp>
|
||||
#include <mbgl/renderer/bucket.hpp>
|
||||
|
||||
namespace mbgl {
|
||||
|
||||
CustomLayer::CustomLayer(const std::string& id_) {
|
||||
id = id_;
|
||||
}
|
||||
|
||||
bool CustomLayer::recalculate(const StyleCalculationParameters&) {
|
||||
passes = RenderPass::Translucent;
|
||||
return false;
|
||||
}
|
||||
|
||||
std::unique_ptr<Bucket> CustomLayer::createBucket(StyleBucketParameters&) const {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
} // namespace mbgl
|
||||
49
src/mbgl/layer/custom_layer.hpp
Normal file
49
src/mbgl/layer/custom_layer.hpp
Normal file
@@ -0,0 +1,49 @@
|
||||
#ifndef MBGL_CUSTOM_LAYER
|
||||
#define MBGL_CUSTOM_LAYER
|
||||
|
||||
#include <mbgl/style/style_layer.hpp>
|
||||
|
||||
namespace mbgl {
|
||||
|
||||
class StyleRenderParameters;
|
||||
|
||||
class CustomLayer : public StyleLayer {
|
||||
public:
|
||||
CustomLayer(const std::string& id);
|
||||
CustomLayer(const CustomLayer&) = default;
|
||||
|
||||
/**
|
||||
* Initialize any GL state needed by the custom layer. This method is called once, from the
|
||||
* rendering thread, at a point when the GL context is active but before rendering for the
|
||||
* first time.
|
||||
*
|
||||
* Resources that are acquired in this method must be released in the subclass's destructor.
|
||||
* However, note that in some situations the destructor may be called without initialize()
|
||||
* having been called. The destructor should be prepared for this case, e.g. by checking if
|
||||
* the resources were actually acquired before releasing them.
|
||||
*/
|
||||
virtual void initialize() = 0;
|
||||
|
||||
/**
|
||||
* Render the layer. This method is called once per frame. The implementation should not make
|
||||
* any assumptions about the GL state (other than that the correct context is active). It may
|
||||
* make changes to the state, and is not required to reset values such as the depth mask, stencil
|
||||
* mask, and corresponding test flags to their original values.
|
||||
*
|
||||
* The input StyleRenderParameters object has a `TransformState state` member that can be queried
|
||||
* for information about the current map view.
|
||||
*/
|
||||
virtual void render(const StyleRenderParameters&) const = 0;
|
||||
|
||||
void parseLayout(const JSVal&) final {}
|
||||
void parsePaints(const JSVal&) final {}
|
||||
|
||||
void cascade(const StyleCascadeParameters&) final {}
|
||||
bool recalculate(const StyleCalculationParameters&) final;
|
||||
|
||||
std::unique_ptr<Bucket> createBucket(StyleBucketParameters&) const final;
|
||||
};
|
||||
|
||||
} // namespace mbgl
|
||||
|
||||
#endif
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <mbgl/map/map_data.hpp>
|
||||
#include <mbgl/annotation/point_annotation.hpp>
|
||||
#include <mbgl/annotation/shape_annotation.hpp>
|
||||
#include <mbgl/style/style_layer.hpp>
|
||||
|
||||
#include <mbgl/util/projection.hpp>
|
||||
#include <mbgl/util/thread.hpp>
|
||||
@@ -415,6 +416,16 @@ AnnotationIDs Map::getPointAnnotationsInBounds(const LatLngBounds& bounds) {
|
||||
LatLngBounds Map::getBoundsForAnnotations(const AnnotationIDs& annotations) {
|
||||
return data->getAnnotationManager()->getBoundsForAnnotations(annotations);
|
||||
}
|
||||
|
||||
#pragma mark - Style API
|
||||
|
||||
void Map::addLayer(std::unique_ptr<StyleLayer> layer) {
|
||||
context->invoke(&MapContext::addLayer, std::move(layer), mapbox::util::optional<std::string>());
|
||||
}
|
||||
|
||||
void Map::addLayer(std::unique_ptr<StyleLayer> layer, const std::string& before) {
|
||||
context->invoke(&MapContext::addLayer, std::move(layer), before);
|
||||
}
|
||||
|
||||
#pragma mark - Toggles
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include <mbgl/storage/response.hpp>
|
||||
|
||||
#include <mbgl/style/style.hpp>
|
||||
#include <mbgl/style/style_layer.hpp>
|
||||
|
||||
#include <mbgl/sprite/sprite_atlas.hpp>
|
||||
#include <mbgl/sprite/sprite_store.hpp>
|
||||
@@ -277,6 +278,10 @@ double MapContext::getTopOffsetPixelsForAnnotationIcon(const std::string& name)
|
||||
return data.getAnnotationManager()->getTopOffsetPixelsForIcon(name);
|
||||
}
|
||||
|
||||
void MapContext::addLayer(std::unique_ptr<StyleLayer> layer, mapbox::util::optional<std::string> after) {
|
||||
style->addLayer(std::move(layer), after);
|
||||
}
|
||||
|
||||
void MapContext::setSourceTileCacheSize(size_t size) {
|
||||
assert(util::ThreadContext::currentlyOn(util::ThreadType::Map));
|
||||
if (size != sourceCacheSize) {
|
||||
|
||||
@@ -11,6 +11,8 @@
|
||||
#include <mbgl/util/gl_object_store.hpp>
|
||||
#include <mbgl/util/ptr.hpp>
|
||||
|
||||
#include <mapbox/optional.hpp>
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace mbgl {
|
||||
@@ -48,9 +50,14 @@ public:
|
||||
|
||||
bool isLoaded() const;
|
||||
|
||||
// Annotations
|
||||
void addAnnotationIcon(const std::string&, std::shared_ptr<const SpriteImage>);
|
||||
double getTopOffsetPixelsForAnnotationIcon(const std::string&);
|
||||
void updateAnnotations();
|
||||
|
||||
// Style API
|
||||
void addLayer(std::unique_ptr<StyleLayer>,
|
||||
const mapbox::util::optional<std::string> before);
|
||||
|
||||
void setSourceTileCacheSize(size_t size);
|
||||
void onLowMemory();
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include <mbgl/style/style_layer.hpp>
|
||||
#include <mbgl/style/style_bucket_parameters.hpp>
|
||||
#include <mbgl/layer/background_layer.hpp>
|
||||
#include <mbgl/layer/custom_layer.hpp>
|
||||
#include <mbgl/layer/symbol_layer.hpp>
|
||||
#include <mbgl/sprite/sprite_atlas.hpp>
|
||||
#include <mbgl/geometry/glyph_atlas.hpp>
|
||||
@@ -109,8 +110,8 @@ void TileWorker::parseLayer(const StyleLayer* layer, const GeometryTile& geometr
|
||||
if (state == TileData::State::obsolete)
|
||||
return;
|
||||
|
||||
// Background is a special case.
|
||||
if (layer->is<BackgroundLayer>())
|
||||
// Background and custom layers are special cases.
|
||||
if (layer->is<BackgroundLayer>() || layer->is<CustomLayer>())
|
||||
return;
|
||||
|
||||
// Skip this bucket if we are to not render this
|
||||
|
||||
@@ -10,8 +10,10 @@
|
||||
|
||||
#include <mbgl/style/style.hpp>
|
||||
#include <mbgl/style/style_layer.hpp>
|
||||
#include <mbgl/style/style_render_parameters.hpp>
|
||||
|
||||
#include <mbgl/layer/background_layer.hpp>
|
||||
#include <mbgl/layer/custom_layer.hpp>
|
||||
|
||||
#include <mbgl/sprite/sprite_atlas.hpp>
|
||||
#include <mbgl/geometry/line_atlas.hpp>
|
||||
@@ -217,9 +219,11 @@ void Painter::renderPass(RenderPass pass_,
|
||||
|
||||
for (; it != end; ++it, i += increment) {
|
||||
currentLayer = i;
|
||||
const auto& item = *it;
|
||||
|
||||
if (!item.layer.hasRenderPass(pass))
|
||||
const auto& item = *it;
|
||||
const StyleLayer& layer = item.layer;
|
||||
|
||||
if (!layer.hasRenderPass(pass))
|
||||
continue;
|
||||
|
||||
if (pass == RenderPass::Translucent) {
|
||||
@@ -233,13 +237,17 @@ void Painter::renderPass(RenderPass pass_,
|
||||
config.stencilMask = 0x0;
|
||||
config.viewport = { 0, 0, frame.framebufferSize[0], frame.framebufferSize[1] };
|
||||
|
||||
if (item.bucket && item.tile) {
|
||||
MBGL_DEBUG_GROUP(item.layer.id + " - " + std::string(item.tile->id));
|
||||
prepareTile(*item.tile);
|
||||
item.bucket->render(*this, item.layer, item.tile->id, item.tile->matrix);
|
||||
} else {
|
||||
if (layer.is<BackgroundLayer>()) {
|
||||
MBGL_DEBUG_GROUP("background");
|
||||
renderBackground(*item.layer.template as<BackgroundLayer>());
|
||||
renderBackground(*layer.as<BackgroundLayer>());
|
||||
} else if (layer.is<CustomLayer>()) {
|
||||
MBGL_DEBUG_GROUP(layer.id + " - custom");
|
||||
layer.as<CustomLayer>()->render(StyleRenderParameters(state));
|
||||
config.setDirty();
|
||||
} else {
|
||||
MBGL_DEBUG_GROUP(layer.id + " - " + std::string(item.tile->id));
|
||||
prepareTile(*item.tile);
|
||||
item.bucket->render(*this, layer, item.tile->id, item.tile->matrix);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include <mbgl/map/tile.hpp>
|
||||
#include <mbgl/map/transform_state.hpp>
|
||||
#include <mbgl/layer/symbol_layer.hpp>
|
||||
#include <mbgl/layer/custom_layer.hpp>
|
||||
#include <mbgl/sprite/sprite_store.hpp>
|
||||
#include <mbgl/sprite/sprite_atlas.hpp>
|
||||
#include <mbgl/style/style_layer.hpp>
|
||||
@@ -100,18 +101,18 @@ StyleLayer* Style::getLayer(const std::string& id) const {
|
||||
return it != layers.end() ? it->get() : nullptr;
|
||||
}
|
||||
|
||||
void Style::addLayer(std::unique_ptr<StyleLayer> layer) {
|
||||
void Style::addLayer(std::unique_ptr<StyleLayer> layer, mapbox::util::optional<std::string> before) {
|
||||
if (SymbolLayer* symbolLayer = layer->as<SymbolLayer>()) {
|
||||
if (!symbolLayer->spriteAtlas) {
|
||||
symbolLayer->spriteAtlas = spriteAtlas.get();
|
||||
}
|
||||
}
|
||||
|
||||
layers.emplace_back(std::move(layer));
|
||||
}
|
||||
if (CustomLayer* customLayer = layer->as<CustomLayer>()) {
|
||||
customLayer->initialize();
|
||||
}
|
||||
|
||||
void Style::addLayer(std::unique_ptr<StyleLayer> layer, const std::string& before) {
|
||||
layers.emplace(findLayer(before), std::move(layer));
|
||||
layers.emplace(before ? findLayer(*before) : layers.end(), std::move(layer));
|
||||
}
|
||||
|
||||
void Style::removeLayer(const std::string& id) {
|
||||
@@ -249,6 +250,11 @@ RenderData Style::getRenderData() const {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (layer->is<CustomLayer>()) {
|
||||
result.order.emplace_back(*layer);
|
||||
continue;
|
||||
}
|
||||
|
||||
Source* source = getSource(layer->source);
|
||||
if (!source) {
|
||||
Log::Warning(Event::Render, "can't find source for layer '%s'", layer->id.c_str());
|
||||
|
||||
@@ -11,6 +11,8 @@
|
||||
#include <mbgl/util/chrono.hpp>
|
||||
#include <mbgl/util/worker.hpp>
|
||||
|
||||
#include <mapbox/optional.hpp>
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
@@ -88,8 +90,8 @@ public:
|
||||
|
||||
std::vector<std::unique_ptr<StyleLayer>> getLayers() const;
|
||||
StyleLayer* getLayer(const std::string& id) const;
|
||||
void addLayer(std::unique_ptr<StyleLayer>);
|
||||
void addLayer(std::unique_ptr<StyleLayer>, const std::string& beforeLayerID);
|
||||
void addLayer(std::unique_ptr<StyleLayer>,
|
||||
mapbox::util::optional<std::string> beforeLayerID = {});
|
||||
void removeLayer(const std::string& layerID);
|
||||
|
||||
RenderData getRenderData() const;
|
||||
|
||||
18
src/mbgl/style/style_render_parameters.hpp
Normal file
18
src/mbgl/style/style_render_parameters.hpp
Normal file
@@ -0,0 +1,18 @@
|
||||
#ifndef STYLE_RENDER_PARAMETERS
|
||||
#define STYLE_RENDER_PARAMETERS
|
||||
|
||||
namespace mbgl {
|
||||
|
||||
class TransformState;
|
||||
|
||||
class StyleRenderParameters {
|
||||
public:
|
||||
StyleRenderParameters(const TransformState& state_)
|
||||
: state(state_) {}
|
||||
|
||||
const TransformState& state;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -14,41 +14,20 @@
|
||||
|
||||
using namespace mbgl;
|
||||
|
||||
namespace {
|
||||
|
||||
std::string getFileSourceRoot() {
|
||||
#ifdef MBGL_ASSET_ZIP
|
||||
return "test/fixtures/annotations/assets.zip";
|
||||
#else
|
||||
return "";
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
std::shared_ptr<SpriteImage> defaultMarker() {
|
||||
PremultipliedImage image = decodeImage(util::read_file("test/fixtures/sprites/default_marker.png"));
|
||||
return std::make_shared<SpriteImage>(image.width, image.height, 1.0, std::string(reinterpret_cast<char*>(image.data.get()), image.size()));
|
||||
}
|
||||
|
||||
PremultipliedImage render(Map& map) {
|
||||
std::promise<PremultipliedImage> promise;
|
||||
map.renderStill([&](std::exception_ptr, PremultipliedImage&& image) {
|
||||
promise.set_value(std::move(image));
|
||||
});
|
||||
return promise.get_future().get();
|
||||
}
|
||||
|
||||
void checkRendering(Map& map, const char * name) {
|
||||
PremultipliedImage actual = render(map);
|
||||
test::checkImage(std::string("test/fixtures/annotations/") + name + "/",
|
||||
actual, 0.0002, 0.1);
|
||||
test::render(map), 0.0002, 0.1);
|
||||
}
|
||||
|
||||
TEST(Annotations, PointAnnotation) {
|
||||
auto display = std::make_shared<mbgl::HeadlessDisplay>();
|
||||
HeadlessView view(display, 1);
|
||||
DefaultFileSource fileSource(nullptr, getFileSourceRoot());
|
||||
DefaultFileSource fileSource(nullptr, test::getFileSourceRoot());
|
||||
|
||||
Map map(view, fileSource, MapMode::Still);
|
||||
map.setStyleJSON(util::read_file("test/fixtures/api/empty.json"), "");
|
||||
@@ -61,7 +40,7 @@ TEST(Annotations, PointAnnotation) {
|
||||
TEST(Annotations, LineAnnotation) {
|
||||
auto display = std::make_shared<mbgl::HeadlessDisplay>();
|
||||
HeadlessView view(display, 1);
|
||||
DefaultFileSource fileSource(nullptr, getFileSourceRoot());
|
||||
DefaultFileSource fileSource(nullptr, test::getFileSourceRoot());
|
||||
|
||||
Map map(view, fileSource, MapMode::Still);
|
||||
map.setStyleJSON(util::read_file("test/fixtures/api/empty.json"), "");
|
||||
@@ -80,7 +59,7 @@ TEST(Annotations, LineAnnotation) {
|
||||
TEST(Annotations, FillAnnotation) {
|
||||
auto display = std::make_shared<mbgl::HeadlessDisplay>();
|
||||
HeadlessView view(display, 1);
|
||||
DefaultFileSource fileSource(nullptr, getFileSourceRoot());
|
||||
DefaultFileSource fileSource(nullptr, test::getFileSourceRoot());
|
||||
|
||||
Map map(view, fileSource, MapMode::Still);
|
||||
map.setStyleJSON(util::read_file("test/fixtures/api/empty.json"), "");
|
||||
@@ -98,7 +77,7 @@ TEST(Annotations, FillAnnotation) {
|
||||
TEST(Annotations, StyleSourcedShapeAnnotation) {
|
||||
auto display = std::make_shared<mbgl::HeadlessDisplay>();
|
||||
HeadlessView view(display, 1);
|
||||
DefaultFileSource fileSource(nullptr, getFileSourceRoot());
|
||||
DefaultFileSource fileSource(nullptr, test::getFileSourceRoot());
|
||||
|
||||
Map map(view, fileSource, MapMode::Still);
|
||||
map.setStyleJSON(util::read_file("test/fixtures/api/annotation.json"), "");
|
||||
@@ -113,14 +92,14 @@ TEST(Annotations, StyleSourcedShapeAnnotation) {
|
||||
TEST(Annotations, AddMultiple) {
|
||||
auto display = std::make_shared<mbgl::HeadlessDisplay>();
|
||||
HeadlessView view(display, 1);
|
||||
DefaultFileSource fileSource(nullptr, getFileSourceRoot());
|
||||
DefaultFileSource fileSource(nullptr, test::getFileSourceRoot());
|
||||
|
||||
Map map(view, fileSource, MapMode::Still);
|
||||
map.setStyleJSON(util::read_file("test/fixtures/api/empty.json"), "");
|
||||
map.addAnnotationIcon("default_marker", defaultMarker());
|
||||
map.addPointAnnotation(PointAnnotation({ 0, -10 }, "default_marker"));
|
||||
|
||||
render(map);
|
||||
test::render(map);
|
||||
|
||||
map.addPointAnnotation(PointAnnotation({ 0, 10 }, "default_marker"));
|
||||
|
||||
@@ -130,12 +109,12 @@ TEST(Annotations, AddMultiple) {
|
||||
TEST(Annotations, NonImmediateAdd) {
|
||||
auto display = std::make_shared<mbgl::HeadlessDisplay>();
|
||||
HeadlessView view(display, 1);
|
||||
DefaultFileSource fileSource(nullptr, getFileSourceRoot());
|
||||
DefaultFileSource fileSource(nullptr, test::getFileSourceRoot());
|
||||
|
||||
Map map(view, fileSource, MapMode::Still);
|
||||
map.setStyleJSON(util::read_file("test/fixtures/api/empty.json"), "");
|
||||
|
||||
render(map);
|
||||
test::render(map);
|
||||
|
||||
AnnotationSegments segments = {{ {{ { 0, 0 }, { 0, 45 }, { 45, 45 }, { 45, 0 } }} }};
|
||||
|
||||
@@ -150,14 +129,14 @@ TEST(Annotations, NonImmediateAdd) {
|
||||
TEST(Annotations, RemovePoint) {
|
||||
auto display = std::make_shared<mbgl::HeadlessDisplay>();
|
||||
HeadlessView view(display, 1);
|
||||
DefaultFileSource fileSource(nullptr, getFileSourceRoot());
|
||||
DefaultFileSource fileSource(nullptr, test::getFileSourceRoot());
|
||||
|
||||
Map map(view, fileSource, MapMode::Still);
|
||||
map.setStyleJSON(util::read_file("test/fixtures/api/empty.json"), "");
|
||||
map.addAnnotationIcon("default_marker", defaultMarker());
|
||||
uint32_t point = map.addPointAnnotation(PointAnnotation({ 0, 0 }, "default_marker"));
|
||||
|
||||
render(map);
|
||||
test::render(map);
|
||||
|
||||
map.removeAnnotation(point);
|
||||
|
||||
@@ -167,7 +146,7 @@ TEST(Annotations, RemovePoint) {
|
||||
TEST(Annotations, RemoveShape) {
|
||||
auto display = std::make_shared<mbgl::HeadlessDisplay>();
|
||||
HeadlessView view(display, 1);
|
||||
DefaultFileSource fileSource(nullptr, getFileSourceRoot());
|
||||
DefaultFileSource fileSource(nullptr, test::getFileSourceRoot());
|
||||
|
||||
AnnotationSegments segments = {{ {{ { 0, 0 }, { 45, 45 } }} }};
|
||||
|
||||
@@ -179,7 +158,7 @@ TEST(Annotations, RemoveShape) {
|
||||
map.setStyleJSON(util::read_file("test/fixtures/api/empty.json"), "");
|
||||
uint32_t shape = map.addShapeAnnotation(ShapeAnnotation(segments, properties));
|
||||
|
||||
render(map);
|
||||
test::render(map);
|
||||
|
||||
map.removeAnnotation(shape);
|
||||
|
||||
@@ -189,26 +168,26 @@ TEST(Annotations, RemoveShape) {
|
||||
TEST(Annotations, ImmediateRemoveShape) {
|
||||
auto display = std::make_shared<mbgl::HeadlessDisplay>();
|
||||
HeadlessView view(display, 1);
|
||||
DefaultFileSource fileSource(nullptr, getFileSourceRoot());
|
||||
DefaultFileSource fileSource(nullptr, test::getFileSourceRoot());
|
||||
Map map(view, fileSource, MapMode::Still);
|
||||
|
||||
map.removeAnnotation(map.addShapeAnnotation(ShapeAnnotation({}, {})));
|
||||
map.setStyleJSON(util::read_file("test/fixtures/api/empty.json"), "");
|
||||
|
||||
render(map);
|
||||
test::render(map);
|
||||
}
|
||||
|
||||
TEST(Annotations, SwitchStyle) {
|
||||
auto display = std::make_shared<mbgl::HeadlessDisplay>();
|
||||
HeadlessView view(display, 1);
|
||||
DefaultFileSource fileSource(nullptr, getFileSourceRoot());
|
||||
DefaultFileSource fileSource(nullptr, test::getFileSourceRoot());
|
||||
|
||||
Map map(view, fileSource, MapMode::Still);
|
||||
map.setStyleJSON(util::read_file("test/fixtures/api/empty.json"), "");
|
||||
map.addAnnotationIcon("default_marker", defaultMarker());
|
||||
map.addPointAnnotation(PointAnnotation({ 0, 0 }, "default_marker"));
|
||||
|
||||
render(map);
|
||||
test::render(map);
|
||||
|
||||
map.setStyleJSON(util::read_file("test/fixtures/api/empty.json"), "");
|
||||
|
||||
|
||||
83
test/api/custom_layer.cpp
Normal file
83
test/api/custom_layer.cpp
Normal file
@@ -0,0 +1,83 @@
|
||||
#include "../fixtures/util.hpp"
|
||||
|
||||
#include <mbgl/map/map.hpp>
|
||||
#include <mbgl/platform/default/headless_display.hpp>
|
||||
#include <mbgl/platform/default/headless_view.hpp>
|
||||
#include <mbgl/storage/default_file_source.hpp>
|
||||
#include <mbgl/layer/custom_layer.hpp>
|
||||
#include <mbgl/util/io.hpp>
|
||||
#include <mbgl/util/mat4.hpp>
|
||||
|
||||
using namespace mbgl;
|
||||
|
||||
static const GLchar * vertexShaderSource = "attribute vec2 a_pos; void main() { gl_Position = vec4(a_pos, 0, 1); }";
|
||||
static const GLchar * fragmentShaderSource = "void main() { gl_FragColor = vec4(0, 1, 0, 1); }";
|
||||
|
||||
class TestLayer : public CustomLayer {
|
||||
public:
|
||||
TestLayer() : CustomLayer("custom") {}
|
||||
TestLayer(const TestLayer& copy) : CustomLayer(copy) {} // Don't copy GL resources
|
||||
|
||||
~TestLayer() {
|
||||
if (program) {
|
||||
MBGL_CHECK_ERROR(glDeleteBuffers(1, &buffer));
|
||||
MBGL_CHECK_ERROR(glDetachShader(program, vertexShader));
|
||||
MBGL_CHECK_ERROR(glDetachShader(program, fragmentShader));
|
||||
MBGL_CHECK_ERROR(glDeleteShader(vertexShader));
|
||||
MBGL_CHECK_ERROR(glDeleteShader(fragmentShader));
|
||||
MBGL_CHECK_ERROR(glDeleteProgram(program));
|
||||
}
|
||||
}
|
||||
|
||||
void initialize() override {
|
||||
program = MBGL_CHECK_ERROR(glCreateProgram());
|
||||
vertexShader = MBGL_CHECK_ERROR(glCreateShader(GL_VERTEX_SHADER));
|
||||
fragmentShader = MBGL_CHECK_ERROR(glCreateShader(GL_FRAGMENT_SHADER));
|
||||
|
||||
MBGL_CHECK_ERROR(glShaderSource(vertexShader, 1, &vertexShaderSource, nullptr));
|
||||
MBGL_CHECK_ERROR(glCompileShader(vertexShader));
|
||||
MBGL_CHECK_ERROR(glAttachShader(program, vertexShader));
|
||||
MBGL_CHECK_ERROR(glShaderSource(fragmentShader, 1, &fragmentShaderSource, nullptr));
|
||||
MBGL_CHECK_ERROR(glCompileShader(fragmentShader));
|
||||
MBGL_CHECK_ERROR(glAttachShader(program, fragmentShader));
|
||||
MBGL_CHECK_ERROR(glLinkProgram(program));
|
||||
a_pos = glGetAttribLocation(program, "a_pos");
|
||||
|
||||
GLfloat background[] = { -1,-1, 1,-1, -1,1, 1,1 };
|
||||
MBGL_CHECK_ERROR(glGenBuffers(1, &buffer));
|
||||
MBGL_CHECK_ERROR(glBindBuffer(GL_ARRAY_BUFFER, buffer));
|
||||
MBGL_CHECK_ERROR(glBufferData(GL_ARRAY_BUFFER, 8 * sizeof(GLfloat), background, GL_STATIC_DRAW));
|
||||
}
|
||||
|
||||
void render(const StyleRenderParameters&) const override {
|
||||
MBGL_CHECK_ERROR(glUseProgram(program));
|
||||
MBGL_CHECK_ERROR(glBindBuffer(GL_ARRAY_BUFFER, buffer));
|
||||
MBGL_CHECK_ERROR(glEnableVertexAttribArray(a_pos));
|
||||
MBGL_CHECK_ERROR(glVertexAttribPointer(a_pos, 2, GL_FLOAT, GL_FALSE, 0, NULL));
|
||||
MBGL_CHECK_ERROR(glDisable(GL_STENCIL_TEST));
|
||||
MBGL_CHECK_ERROR(glDisable(GL_DEPTH_TEST));
|
||||
MBGL_CHECK_ERROR(glDrawArrays(GL_TRIANGLE_STRIP, 0, 4));
|
||||
}
|
||||
|
||||
std::unique_ptr<StyleLayer> clone() const override {
|
||||
return std::make_unique<TestLayer>(*this);
|
||||
}
|
||||
|
||||
GLuint program = 0;
|
||||
GLuint vertexShader = 0;
|
||||
GLuint fragmentShader = 0;
|
||||
GLuint buffer = 0;
|
||||
GLuint a_pos = 0;
|
||||
};
|
||||
|
||||
TEST(CustomLayer, Basic) {
|
||||
auto display = std::make_shared<mbgl::HeadlessDisplay>();
|
||||
HeadlessView view(display, 1);
|
||||
DefaultFileSource fileSource(nullptr, test::getFileSourceRoot());
|
||||
|
||||
Map map(view, fileSource, MapMode::Still);
|
||||
map.setStyleJSON(util::read_file("test/fixtures/api/empty.json"), "");
|
||||
map.addLayer(std::make_unique<TestLayer>());
|
||||
|
||||
test::checkImage("test/fixtures/custom_layer/basic", test::render(map));
|
||||
}
|
||||
BIN
test/fixtures/custom_layer/basic/expected.png
vendored
Normal file
BIN
test/fixtures/custom_layer/basic/expected.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.6 KiB |
18
test/fixtures/util.cpp
vendored
18
test/fixtures/util.cpp
vendored
@@ -1,5 +1,6 @@
|
||||
#include "util.hpp"
|
||||
|
||||
#include <mbgl/map/map.hpp>
|
||||
#include <mbgl/platform/log.hpp>
|
||||
#include <mbgl/util/image.hpp>
|
||||
#include <mbgl/util/io.hpp>
|
||||
@@ -12,12 +13,21 @@
|
||||
#pragma GCC diagnostic pop
|
||||
|
||||
#include <csignal>
|
||||
#include <future>
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
namespace mbgl {
|
||||
namespace test {
|
||||
|
||||
std::string getFileSourceRoot() {
|
||||
#ifdef MBGL_ASSET_ZIP
|
||||
return "test/fixtures/annotations/assets.zip";
|
||||
#else
|
||||
return "";
|
||||
#endif
|
||||
}
|
||||
|
||||
pid_t startServer(const char *executable) {
|
||||
int pipefd[2];
|
||||
|
||||
@@ -85,6 +95,14 @@ uint64_t crc64(const std::string& str) {
|
||||
return crc64(str.data(), str.size());
|
||||
}
|
||||
|
||||
PremultipliedImage render(Map& map) {
|
||||
std::promise<PremultipliedImage> promise;
|
||||
map.renderStill([&](std::exception_ptr, PremultipliedImage&& image) {
|
||||
promise.set_value(std::move(image));
|
||||
});
|
||||
return promise.get_future().get();
|
||||
}
|
||||
|
||||
void checkImage(const std::string& base,
|
||||
const PremultipliedImage& actual,
|
||||
double imageThreshold,
|
||||
|
||||
7
test/fixtures/util.hpp
vendored
7
test/fixtures/util.hpp
vendored
@@ -16,13 +16,20 @@
|
||||
} name;
|
||||
|
||||
namespace mbgl {
|
||||
|
||||
class Map;
|
||||
|
||||
namespace test {
|
||||
|
||||
std::string getFileSourceRoot();
|
||||
|
||||
pid_t startServer(const char *executable);
|
||||
void stopServer(pid_t pid);
|
||||
|
||||
uint64_t crc64(const char*, size_t);
|
||||
uint64_t crc64(const std::string&);
|
||||
|
||||
PremultipliedImage render(Map&);
|
||||
|
||||
void checkImage(const std::string& base,
|
||||
const PremultipliedImage& actual,
|
||||
|
||||
@@ -43,7 +43,7 @@
|
||||
'api/api_misuse.cpp',
|
||||
'api/repeated_render.cpp',
|
||||
'api/set_style.cpp',
|
||||
|
||||
'api/custom_layer.cpp',
|
||||
|
||||
'miscellaneous/async_task.cpp',
|
||||
'miscellaneous/clip_ids.cpp',
|
||||
|
||||
Reference in New Issue
Block a user