Polyline rendering
This commit is contained in:
parent
228d7678bf
commit
7ba4865ffc
|
|
@ -12,6 +12,7 @@
|
|||
#include <blend2d/image.h>
|
||||
|
||||
#include <cstddef>
|
||||
#include <span>
|
||||
|
||||
namespace CesiumVectorData {
|
||||
|
||||
|
|
@ -34,6 +35,11 @@ public:
|
|||
const CesiumGeospatial::CartographicPolygon& polygon,
|
||||
const Color& drawColor);
|
||||
|
||||
void drawPolyline(
|
||||
const std::span<CesiumGeospatial::Cartographic>& points,
|
||||
const Color& drawColor
|
||||
);
|
||||
|
||||
CesiumUtility::IntrusivePointer<CesiumGltf::ImageAsset> finalize();
|
||||
|
||||
private:
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ uint32_t Color::toRgba32() const {
|
|||
}
|
||||
|
||||
VectorRasterizer::VectorRasterizer(
|
||||
const CesiumGeospatial::GlobeRectangle& bounds,
|
||||
const GlobeRectangle& bounds,
|
||||
CesiumUtility::IntrusivePointer<CesiumGltf::ImageAsset>& imageAsset)
|
||||
: _bounds(bounds), _image(), _context(), _imageAsset(imageAsset) {
|
||||
CESIUM_ASSERT(imageAsset->channels == 1 || imageAsset->channels == 4);
|
||||
|
|
@ -36,39 +36,57 @@ VectorRasterizer::VectorRasterizer(
|
|||
this->_imageAsset->channels == 1 ? BL_FORMAT_A8 : BL_FORMAT_PRGB32,
|
||||
reinterpret_cast<void*>(this->_imageAsset->pixelData.data()),
|
||||
(int64_t)this->_imageAsset->width * (int64_t)this->_imageAsset->channels);
|
||||
|
||||
_context.begin(this->_image);
|
||||
// Set up transformation matrix to transform from LLH to pixel coordinates.
|
||||
_context.translate(-bounds.getWest(), -bounds.getSouth());
|
||||
_context.scale(
|
||||
(double)this->_imageAsset->width / bounds.computeWidth(),
|
||||
(double)this->_imageAsset->height / bounds.computeHeight());
|
||||
// We don't want the stroke to be scaled, so set it to perform the scale
|
||||
// before we stroke.
|
||||
_context.setStrokeTransformOrder(BL_STROKE_TRANSFORM_ORDER_BEFORE);
|
||||
// Initialize the image as all transparent.
|
||||
_context.clearAll();
|
||||
}
|
||||
|
||||
void VectorRasterizer::drawPolygon(
|
||||
const CesiumGeospatial::CartographicPolygon& polygon,
|
||||
const CartographicPolygon& polygon,
|
||||
const Color& color) {
|
||||
BLRgba32 style(color.toRgba32());
|
||||
|
||||
const double boundsWidth = this->_bounds.computeWidth();
|
||||
const double boundsHeight = this->_bounds.computeHeight();
|
||||
|
||||
std::vector<BLPoint> points;
|
||||
const std::vector<glm::dvec2>& vertices = polygon.getVertices();
|
||||
for (const uint32_t& idx : polygon.getIndices()) {
|
||||
const glm::dvec2& vertex = vertices[idx];
|
||||
points.emplace_back(
|
||||
(vertex.x - this->_bounds.getWest()) / boundsWidth *
|
||||
this->_image.width(),
|
||||
(vertex.y - this->_bounds.getSouth()) / boundsHeight *
|
||||
this->_image.height());
|
||||
|
||||
// Since glm::dvec2 and BLPoint are both structs containing two doubles in the
|
||||
// same order, we can treat one as the other to avoid copying.
|
||||
CESIUM_ASSERT(sizeof(BLPoint) == sizeof(glm::dvec2));
|
||||
this->_context.fillPolygon(
|
||||
reinterpret_cast<const BLPoint*>(vertices.data()),
|
||||
vertices.size(),
|
||||
style);
|
||||
}
|
||||
|
||||
void VectorRasterizer::drawPolyline(
|
||||
const std::span<Cartographic>& points,
|
||||
const Color& color) {
|
||||
BLRgba32 style(color.toRgba32());
|
||||
|
||||
// Unfortunately Cartographic has an extra component that BLPoint does not, so
|
||||
// we can't use it directly.
|
||||
std::vector<BLPoint> vertices;
|
||||
vertices.reserve(points.size());
|
||||
|
||||
for (Cartographic& vertex : points) {
|
||||
vertices.emplace_back(vertex.longitude, vertex.latitude);
|
||||
}
|
||||
|
||||
this->_context.fillPolygon(points.data(), points.size(), style);
|
||||
this->_context.strokePolyline(vertices.data(), vertices.size(), style);
|
||||
}
|
||||
|
||||
CesiumUtility::IntrusivePointer<CesiumGltf::ImageAsset>
|
||||
VectorRasterizer::finalize() {
|
||||
this->_context.end();
|
||||
|
||||
this->_image.writeToFile("triangle.png");
|
||||
|
||||
if (this->_imageAsset->channels == 4) {
|
||||
// Blend2D writes in BGRA whereas ImageAsset is RGBA.
|
||||
// We need to swap the channels to fix the values.
|
||||
|
|
|
|||
|
|
@ -7,7 +7,10 @@
|
|||
#include <doctest/doctest.h>
|
||||
#include <glm/fwd.hpp>
|
||||
|
||||
#include <chrono>
|
||||
#include <cstddef>
|
||||
#include <iostream>
|
||||
#include <random>
|
||||
|
||||
using namespace CesiumGeospatial;
|
||||
using namespace CesiumVectorData;
|
||||
|
|
@ -15,8 +18,7 @@ using namespace CesiumVectorData;
|
|||
TEST_CASE("VectorRasterizer::rasterize") {
|
||||
GlobeRectangle rect{0.0, 0.0, 1.0, 1.0};
|
||||
|
||||
SUBCASE("Generates a single triangle") {
|
||||
|
||||
SUBCASE("Renders a single triangle") {
|
||||
CesiumUtility::IntrusivePointer<CesiumGltf::ImageAsset> asset;
|
||||
asset.emplace();
|
||||
asset->width = 256;
|
||||
|
|
@ -24,7 +26,8 @@ TEST_CASE("VectorRasterizer::rasterize") {
|
|||
asset->channels = 4;
|
||||
asset->bytesPerChannel = 1;
|
||||
asset->pixelData.resize(
|
||||
(size_t)(asset->width * asset->height * asset->channels * asset->bytesPerChannel),
|
||||
(size_t)(asset->width * asset->height * asset->channels *
|
||||
asset->bytesPerChannel),
|
||||
std::byte{255});
|
||||
|
||||
VectorRasterizer rasterizer(rect, asset);
|
||||
|
|
@ -41,45 +44,39 @@ TEST_CASE("VectorRasterizer::rasterize") {
|
|||
asset->writeTga("triangle.tga");
|
||||
}
|
||||
|
||||
/*SUBCASE("Alpha blends properly") {
|
||||
VectorRasterizer rasterizer(
|
||||
std::vector<CartographicPolygon>{
|
||||
CartographicPolygon(std::vector<glm::dvec2>{
|
||||
glm::dvec2(0.25, 0.25),
|
||||
glm::dvec2(0.5, 0.75),
|
||||
glm::dvec2(0.75, 0.25)}),
|
||||
CartographicPolygon(std::vector<glm::dvec2>{
|
||||
glm::dvec2(0.25, 0.25),
|
||||
glm::dvec2(0.25, 0.75),
|
||||
glm::dvec2(0.9, 0.1)})},
|
||||
std::vector<std::array<std::byte, 4>>{
|
||||
std::array<std::byte, 4>{
|
||||
std::byte{0},
|
||||
std::byte{255},
|
||||
std::byte{255},
|
||||
std::byte{127}},
|
||||
std::array<std::byte, 4>{
|
||||
std::byte{0},
|
||||
std::byte{255},
|
||||
std::byte{0},
|
||||
std::byte{60}}});
|
||||
|
||||
CesiumGltf::ImageAsset asset;
|
||||
asset.width = 256;
|
||||
asset.height = 256;
|
||||
asset.channels = 4;
|
||||
asset.bytesPerChannel = 1;
|
||||
asset.pixelData.resize(
|
||||
(size_t)(asset.width * asset.height * asset.channels *
|
||||
asset.bytesPerChannel),
|
||||
SUBCASE("Renders a polyline") {
|
||||
CesiumUtility::IntrusivePointer<CesiumGltf::ImageAsset> asset;
|
||||
asset.emplace();
|
||||
asset->width = 256;
|
||||
asset->height = 256;
|
||||
asset->channels = 4;
|
||||
asset->bytesPerChannel = 1;
|
||||
asset->pixelData.resize(
|
||||
(size_t)(asset->width * asset->height * asset->channels *
|
||||
asset->bytesPerChannel),
|
||||
std::byte{255});
|
||||
|
||||
rasterizer.rasterize(rect, asset);
|
||||
asset.writeTga("blending.tga");
|
||||
}*/
|
||||
VectorRasterizer rasterizer(rect, asset);
|
||||
|
||||
std::vector<Cartographic> polyline {
|
||||
Cartographic(0.25, 0.25),
|
||||
Cartographic(0.25, 0.5),
|
||||
Cartographic(0.3, 0.7),
|
||||
Cartographic(0.25, 0.8),
|
||||
Cartographic(0.8, 1.0),
|
||||
Cartographic(0.8, 0.9),
|
||||
Cartographic(0.9, 0.9)
|
||||
};
|
||||
|
||||
rasterizer.drawPolyline(
|
||||
polyline,
|
||||
Color{std::byte{0}, std::byte{255}, std::byte{255}, std::byte{255}});
|
||||
rasterizer.finalize();
|
||||
asset->writeTga("polyline.tga");
|
||||
}
|
||||
}
|
||||
|
||||
/*TEST_CASE("VectorRasterizer::rasterize benchmark") {
|
||||
TEST_CASE("VectorRasterizer::rasterize benchmark") {
|
||||
GlobeRectangle rect{0.0, 0.0, 1.0, 1.0};
|
||||
std::chrono::steady_clock clock;
|
||||
std::random_device r;
|
||||
|
|
@ -87,9 +84,20 @@ TEST_CASE("VectorRasterizer::rasterize") {
|
|||
|
||||
std::chrono::microseconds total(0);
|
||||
|
||||
CesiumUtility::IntrusivePointer<CesiumGltf::ImageAsset> asset;
|
||||
asset.emplace();
|
||||
asset->width = 256;
|
||||
asset->height = 256;
|
||||
asset->channels = 4;
|
||||
asset->bytesPerChannel = 1;
|
||||
asset->pixelData.resize(
|
||||
(size_t)(asset->width * asset->height * asset->channels *
|
||||
asset->bytesPerChannel),
|
||||
std::byte{255});
|
||||
|
||||
for (int i = 0; i < 100; i++) {
|
||||
std::vector<CartographicPolygon> polygons;
|
||||
std::vector<std::array<std::byte, 4>> colors;
|
||||
std::vector<Color> colors;
|
||||
std::uniform_real_distribution<double> uniformDist;
|
||||
for (int j = 0; j < 1000; j++) {
|
||||
polygons.emplace_back(std::vector<glm::dvec2>{
|
||||
|
|
@ -97,37 +105,32 @@ TEST_CASE("VectorRasterizer::rasterize") {
|
|||
glm::dvec2(uniformDist(rand), uniformDist(rand)),
|
||||
glm::dvec2(uniformDist(rand), uniformDist(rand)),
|
||||
});
|
||||
colors.emplace_back(std::array<std::byte, 4>{
|
||||
colors.emplace_back(Color{
|
||||
(std::byte)(uniformDist(rand) * 255.0),
|
||||
(std::byte)(uniformDist(rand) * 255.0),
|
||||
(std::byte)(uniformDist(rand) * 255.0),
|
||||
(std::byte)(uniformDist(rand) * 255.0)});
|
||||
}
|
||||
VectorRasterizer rasterizer(polygons, colors);
|
||||
|
||||
CesiumGltf::ImageAsset asset;
|
||||
asset.width = 256;
|
||||
asset.height = 256;
|
||||
asset.channels = 4;
|
||||
asset.bytesPerChannel = 1;
|
||||
asset.pixelData.resize(
|
||||
(size_t)(asset.width * asset.height * asset.channels *
|
||||
asset.bytesPerChannel),
|
||||
std::byte{255});
|
||||
|
||||
std::chrono::steady_clock::time_point start = clock.now();
|
||||
rasterizer.rasterize(rect, asset);
|
||||
|
||||
VectorRasterizer rasterizer(rect, asset);
|
||||
for (size_t j = 0; j < polygons.size(); j++) {
|
||||
rasterizer.drawPolygon(polygons[j], colors[j]);
|
||||
}
|
||||
rasterizer.finalize();
|
||||
|
||||
std::chrono::microseconds time =
|
||||
std::chrono::duration_cast<std::chrono::microseconds>(
|
||||
clock.now() - start);
|
||||
total += time;
|
||||
std::cout << "rasterized 1000 triangles in " << time.count()
|
||||
<< " microseconds\n";
|
||||
asset.writeTga("rand.tga");
|
||||
asset->writeTga("rand.tga");
|
||||
}
|
||||
|
||||
double seconds =
|
||||
std::chrono::duration_cast<std::chrono::duration<double>>(total).count();
|
||||
std::cout << "100 runs in " << seconds << " seconds, avg per run "
|
||||
<< (seconds / 100.0) << " seconds\n";
|
||||
}*/
|
||||
}
|
||||
Loading…
Reference in New Issue