#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace mbgl { // static MapSnapshotterObserver& MapSnapshotterObserver::nullObserver() { static MapSnapshotterObserver mapSnapshotterObserver; return mapSnapshotterObserver; } class ForwardingRendererObserver final : public RendererObserver { public: explicit ForwardingRendererObserver(RendererObserver& delegate_) : mailbox(std::make_shared(*Scheduler::GetCurrent())), delegate(delegate_, mailbox) {} ~ForwardingRendererObserver() override { mailbox->close(); } void onInvalidate() override { delegate.invoke(&RendererObserver::onInvalidate); } void onResourceError(std::exception_ptr err) override { delegate.invoke(&RendererObserver::onResourceError, err); } void onDidFinishRenderingFrame(RenderMode mode, bool repaintNeeded, bool placementChanged) override { delegate.invoke(&RendererObserver::onDidFinishRenderingFrame, mode, repaintNeeded, placementChanged); } void onStyleImageMissing(const std::string& image, StyleImageMissingCallback cb) override { delegate.invoke(&RendererObserver::onStyleImageMissing, image, std::move(cb)); } private: std::shared_ptr mailbox; ActorRef delegate; }; class SnapshotterRenderer final : public RendererObserver { public: SnapshotterRenderer(Size size, float pixelRatio, const optional& localFontFamily) : frontend(size, pixelRatio, gfx::HeadlessBackend::SwapBehaviour::NoFlush, gfx::ContextMode::Unique, localFontFamily) {} void reset() { hasPendingStillImageRequest = false; frontend.reset(); } void onInvalidate() override { rendererObserver->onInvalidate(); } void onResourceError(std::exception_ptr err) override { hasPendingStillImageRequest = false; rendererObserver->onResourceError(err); } void onDidFinishRenderingFrame(RenderMode mode, bool repaintNeeded, bool placementChanged) override { if (mode == RenderMode::Full && hasPendingStillImageRequest) { stillImage = frontend.readStillImage(); } rendererObserver->onDidFinishRenderingFrame(mode, repaintNeeded, placementChanged); } void setObserver(std::shared_ptr observer) { assert(observer); rendererObserver = std::move(observer); frontend.setObserver(*this); } void update(std::shared_ptr params) { assert(params); hasPendingStillImageRequest = params->stillImageRequest; frontend.update(std::move(params)); } void setSize(Size size) { frontend.setSize(size); } PremultipliedImage takeImage() { assert(stillImage.valid()); return std::move(stillImage); } private: PremultipliedImage stillImage; bool hasPendingStillImageRequest = false; std::shared_ptr rendererObserver; HeadlessFrontend frontend; }; class SnapshotterRendererFrontend final : public RendererFrontend { public: SnapshotterRendererFrontend(Size size, float pixelRatio, optional localFontFamily) : renderer(std::make_unique>( "Snapshotter", size, pixelRatio, std::move(localFontFamily))) {} ~SnapshotterRendererFrontend() = default; void reset() override { renderer->actor().invoke(&SnapshotterRenderer::reset); } void setObserver(RendererObserver& observer) override { renderer->actor().invoke(&SnapshotterRenderer::setObserver, std::make_unique(observer)); } void update(std::shared_ptr parameters) override { updateParameters = std::move(parameters); renderer->actor().invoke(&SnapshotterRenderer::update, updateParameters); } void setSize(Size size) { renderer->actor().invoke(&SnapshotterRenderer::setSize, size); } const TransformState& getTransformState() const { assert(updateParameters); static TransformState defaultTransformState{}; if (updateParameters) return updateParameters->transformState; return defaultTransformState; } PremultipliedImage takeImage() { return renderer->actor().ask(&SnapshotterRenderer::takeImage).get(); } private: std::shared_ptr updateParameters; const std::unique_ptr> renderer; }; class MapSnapshotter::Impl final : public MapObserver { public: Impl(Size size, float pixelRatio, const ResourceOptions& resourceOptions, MapSnapshotterObserver& observer_, optional localFontFamily) : observer(observer_), frontend(size, pixelRatio, std::move(localFontFamily)), map(frontend, *this, MapOptions().withMapMode(MapMode::Static).withSize(size).withPixelRatio(pixelRatio), resourceOptions) {} void setRegion(const LatLngBounds& region) { mbgl::EdgeInsets insets{0, 0, 0, 0}; std::vector latLngs = {region.southwest(), region.northeast()}; map.jumpTo(map.cameraForLatLngs(latLngs, insets)); } void snapshot(MapSnapshotter::Callback callback) { if (!callback) { Log::Error(Event::General, "MapSnapshotter::Callback is not set"); return; } if (renderStillCallback) { callback(std::make_exception_ptr(util::MisuseException("MapSnapshotter is currently rendering an image")), PremultipliedImage(), {}, {}, {}); } renderStillCallback = std::make_unique>( *Scheduler::GetCurrent(), [this, cb = std::move(callback)](std::exception_ptr ptr, PremultipliedImage image, Attributions attributions, PointForFn pfn, LatLngForFn latLonFn) { cb(std::move(ptr), std::move(image), std::move(attributions), std::move(pfn), std::move(latLonFn)); renderStillCallback.reset(); }); map.renderStill([this, actorRef = renderStillCallback->self()](const std::exception_ptr& error) { // Create lambda that captures the current transform state // and can be used to translate for geographic to screen // coordinates assert(map.getCameraOptions().center); PointForFn pointForFn = [center = *map.getCameraOptions().center, transformState = frontend.getTransformState()](const LatLng& latLng) { LatLng unwrappedLatLng = latLng.wrapped(); unwrappedLatLng.unwrapForShortestPath(center); Transform transform{transformState}; return transform.latLngToScreenCoordinate(unwrappedLatLng); }; // Create lambda that captures the current transform state // and can be used to translate for geographic to screen // coordinates LatLngForFn latLngForFn = [transformState = frontend.getTransformState()](const ScreenCoordinate& screenCoordinate) { Transform transform{transformState}; return transform.screenCoordinateToLatLng(screenCoordinate); }; // Collect all source attributions std::vector attributions; for (auto source : map.getStyle().getSources()) { auto attribution = source->getAttribution(); if (attribution) { attributions.push_back(*attribution); } } // Invoke callback actorRef.invoke(&MapSnapshotter::Callback::operator(), error, error ? PremultipliedImage() : frontend.takeImage(), std::move(attributions), std::move(pointForFn), std::move(latLngForFn)); }); } // MapObserver overrides void onDidFailLoadingMap(MapLoadError, const std::string& error) override { observer.onDidFailLoadingStyle(error); } void onDidFinishLoadingStyle() override { observer.onDidFinishLoadingStyle(); } void onStyleImageMissing(const std::string& image) override { observer.onStyleImageMissing(image); } Map& getMap() { return map; } const Map& getMap() const { return map; } SnapshotterRendererFrontend& getRenderer() { return frontend; } void cancel() { renderStillCallback.reset(); } private: std::unique_ptr> renderStillCallback; MapSnapshotterObserver& observer; SnapshotterRendererFrontend frontend; Map map; }; MapSnapshotter::MapSnapshotter(Size size, float pixelRatio, const ResourceOptions& resourceOptions, MapSnapshotterObserver& observer, optional localFontFamily) : impl(std::make_unique( size, pixelRatio, resourceOptions, observer, std::move(localFontFamily))) {} MapSnapshotter::MapSnapshotter(Size size, float pixelRatio, const ResourceOptions& resourceOptions) : MapSnapshotter(size, pixelRatio, resourceOptions, MapSnapshotterObserver::nullObserver()) {} MapSnapshotter::~MapSnapshotter() = default; void MapSnapshotter::setStyleURL(const std::string& styleURL) { impl->getMap().getStyle().loadURL(styleURL); } std::string MapSnapshotter::getStyleURL() const { return impl->getMap().getStyle().getURL(); } void MapSnapshotter::setStyleJSON(const std::string& styleJSON) { impl->getMap().getStyle().loadJSON(styleJSON); } std::string MapSnapshotter::getStyleJSON() const { return impl->getMap().getStyle().getJSON(); } void MapSnapshotter::setSize(const Size& size) { impl->getMap().setSize(size); impl->getRenderer().setSize(size); } Size MapSnapshotter::getSize() const { return impl->getMap().getMapOptions().size(); } void MapSnapshotter::setCameraOptions(const CameraOptions& options) { impl->getMap().jumpTo(options); } CameraOptions MapSnapshotter::getCameraOptions() const { EdgeInsets insets; return impl->getMap().getCameraOptions(insets); } void MapSnapshotter::setRegion(const LatLngBounds& region) { impl->setRegion(region); } LatLngBounds MapSnapshotter::getRegion() const { return impl->getMap().latLngBoundsForCamera(getCameraOptions()); } style::Style& MapSnapshotter::getStyle() { return impl->getMap().getStyle(); } const style::Style& MapSnapshotter::getStyle() const { return impl->getMap().getStyle(); } void MapSnapshotter::snapshot(MapSnapshotter::Callback callback) { impl->snapshot(std::move(callback)); } void MapSnapshotter::cancel() { impl->cancel(); } } // namespace mbgl